diff --git a/.changes/deps-epg-parser-0-5-0.md b/.changes/deps-epg-parser-0-5-0.md new file mode 100644 index 000000000..0eb47360f --- /dev/null +++ b/.changes/deps-epg-parser-0-5-0.md @@ -0,0 +1,6 @@ +--- +type: internal +area: deps +--- + +The web backend now uses the current structured XMLTV parser output, covered by a route-level contract test for future PWA EPG support. diff --git a/apps/web-backend/src/app/web-backend-app.spec.ts b/apps/web-backend/src/app/web-backend-app.spec.ts index 9c90818dd..bd271f1b4 100644 --- a/apps/web-backend/src/app/web-backend-app.spec.ts +++ b/apps/web-backend/src/app/web-backend-app.spec.ts @@ -210,6 +210,87 @@ https://stream.example/news.m3u8`); ); }); + it('parses XMLTV metadata into the current EPG shape', async () => { + const httpClient = new StubHttpClient(); + httpClient.queueResponse(` + + + News + + https://example.test/news + + + Morning News + Headlines + + +`); + + await withServer( + createWebBackendApp({ + clientOrigins: ['http://localhost:4200'], + httpClient, + resolveHostname: resolvePublicHost, + }), + async (baseUrl) => { + const targetId = await registerProviderTarget( + baseUrl, + 'https://provider.example/guide.xml' + ); + const response = await fetch( + `${baseUrl}/parse-xml?targetId=${targetId}`, + { headers: { Origin: 'http://localhost:4200' } } + ); + const body = (await response.json()) as Record; + + expect(response.status).toBe(200); + expect(body).toMatchObject({ + sourceInfoName: 'Fixture Guide', + channels: [ + { + id: 'news', + displayName: [{ lang: 'en', value: 'News' }], + icon: [ + { + src: 'https://example.test/news.png', + width: '100', + height: '50', + }, + ], + url: [ + { + system: 'homepage', + value: 'https://example.test/news', + }, + ], + }, + ], + programs: [ + { + channel: 'news', + start: '2026-08-11T01:00:00.000Z', + stop: '2026-08-11T02:00:00.000Z', + title: [{ lang: 'en', value: 'Morning News' }], + desc: [{ lang: 'en', value: 'Headlines' }], + icon: [ + { + src: 'https://example.test/program.png', + }, + ], + }, + ], + }); + expect(httpClient.requests).toEqual([ + { + headers: undefined, + params: undefined, + url: 'https://provider.example/guide.xml', + }, + ]); + } + ); + }); + it('extracts KODIPROP ClearKey DRM on the /parse URL-import path', async () => { const httpClient = new StubHttpClient(); const kid = '00112233445566778899aabbccddeeff'; diff --git a/package.json b/package.json index 36a19de49..58520fff8 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "drizzle-orm": "0.45.2", "electron-conf": "1.3.0", "electron-updater": "6.8.9", - "epg-parser": "^0.1.6", + "epg-parser": "^0.5.0", "fix-path": "5.0.0", "hls.js": "1.6.16", "iptv-playlist-parser": "github:4gray/iptv-playlist-parser#v0.15.2-iptvnator.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2919bbc5f..a5aa38c1f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -122,8 +122,8 @@ importers: specifier: 6.8.9 version: 6.8.9 epg-parser: - specifier: ^0.1.6 - version: 0.1.6 + specifier: ^0.5.0 + version: 0.5.0 fix-path: specifier: 5.0.0 version: 5.0.0 @@ -6049,6 +6049,9 @@ packages: date-fns@4.4.0: resolution: {integrity: sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==} + dayjs@1.11.21: + resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==} + debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -6487,8 +6490,8 @@ packages: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} - epg-parser@0.1.6: - resolution: {integrity: sha512-g6AxKOvs0E4bTGPdIUh8/FDKdrVjbf4DVK0jIFuChDt7wBRJmMVyqbLeS8NApf6M2wpCRLBpIenXOCS88w0Rqw==} + epg-parser@0.5.0: + resolution: {integrity: sha512-NK9vSev/KkCVcKilJd35PmB9tP4+tN45dhcsSxHagVoUTINh8KLZ1yvgtoPDVYr0IaSPJXIIIZWcu1JPjS5HMw==} err-code@2.0.3: resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} @@ -8091,6 +8094,9 @@ packages: lodash.escaperegexp@4.1.2: resolution: {integrity: sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==} + lodash.groupby@4.6.0: + resolution: {integrity: sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==} + lodash.isequal@4.5.0: resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. @@ -17976,6 +17982,8 @@ snapshots: date-fns@4.4.0: {} + dayjs@1.11.21: {} + debug@2.6.9: dependencies: ms: 2.0.0 @@ -18311,8 +18319,10 @@ snapshots: environment@1.1.0: {} - epg-parser@0.1.6: + epg-parser@0.5.0: dependencies: + dayjs: 1.11.21 + lodash.groupby: 4.6.0 xml-js: 1.6.11 err-code@2.0.3: {} @@ -20552,6 +20562,8 @@ snapshots: lodash.escaperegexp@4.1.2: {} + lodash.groupby@4.6.0: {} + lodash.isequal@4.5.0: {} lodash.memoize@4.1.2: {} @@ -24671,7 +24683,7 @@ snapshots: xml-js@1.6.11: dependencies: - sax: 1.6.0 + sax: 1.6.1 xml-name-validator@5.0.0: {}