From ce5191db904b5aaf9cce8b29f40f2d5d5e149774 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Sat, 13 Dec 2025 01:07:03 +0300 Subject: [PATCH 1/7] Update tests/__data__ --- tests/__data__/expected/playlist_test/af.m3u | 3 +++ tests/__data__/input/playlist_test/results.js | 7 ++++++- tests/__data__/input/playlist_test/streams/af.m3u | 3 +++ tests/__data__/input/playlist_test/streams/ag.m3u | 2 +- 4 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 tests/__data__/expected/playlist_test/af.m3u create mode 100644 tests/__data__/input/playlist_test/streams/af.m3u diff --git a/tests/__data__/expected/playlist_test/af.m3u b/tests/__data__/expected/playlist_test/af.m3u new file mode 100644 index 0000000000..1f35239dd3 --- /dev/null +++ b/tests/__data__/expected/playlist_test/af.m3u @@ -0,0 +1,3 @@ +#EXTM3U +#EXTINF:-1 tvg-id="BaharTV.af@SD",Bahar TV (720p) [Not 24/7] +https://59d39900ebfb8.streamlock.net/bahartv/bahartv/playlist.m3u8 \ No newline at end of file diff --git a/tests/__data__/input/playlist_test/results.js b/tests/__data__/input/playlist_test/results.js index 58ecd5e05e..73858db2bc 100644 --- a/tests/__data__/input/playlist_test/results.js +++ b/tests/__data__/input/playlist_test/results.js @@ -10,5 +10,10 @@ module.exports = { url: 'https://tego-cdn2a.sibercdn.com/Live_TV-ABSTV-10/tracks-v3a1/rewind-7200.m3u8?token=e5f61e7be8363eb781b4bdfe591bf917dd529c1a-SjY3NzRTbDZQNnFQVkZaNkZja2RxV3JKc1VBa05zQkdMNStJakRGV0VTTzNrOEVGVUlIQmxta1NLV0o3bzdVdQ-1736094545-1736008145', http: { referrer: '', 'user-agent': '' }, status: { ok: false, code: 'HTTP_FORBIDDEN', message: 'HTTP 403 Forbidden' } - } + }, + 'https://59d39900ebfb8.streamlock.net/bahartv/bahartv/playlist.m3u8': { + url: 'https://59d39900ebfb8.streamlock.net/bahartv/bahartv/playlist.m3u8', + http: { referrer: '', 'user-agent': '' }, + status: { ok: false, code: 'HTTP_404_NOT_FOUND', message: 'HTTP 404 Not Found' } + } } diff --git a/tests/__data__/input/playlist_test/streams/af.m3u b/tests/__data__/input/playlist_test/streams/af.m3u new file mode 100644 index 0000000000..1f35239dd3 --- /dev/null +++ b/tests/__data__/input/playlist_test/streams/af.m3u @@ -0,0 +1,3 @@ +#EXTM3U +#EXTINF:-1 tvg-id="BaharTV.af@SD",Bahar TV (720p) [Not 24/7] +https://59d39900ebfb8.streamlock.net/bahartv/bahartv/playlist.m3u8 \ No newline at end of file diff --git a/tests/__data__/input/playlist_test/streams/ag.m3u b/tests/__data__/input/playlist_test/streams/ag.m3u index 71345f8cc4..e3f4015a91 100644 --- a/tests/__data__/input/playlist_test/streams/ag.m3u +++ b/tests/__data__/input/playlist_test/streams/ag.m3u @@ -1,5 +1,5 @@ #EXTM3U #EXTINF:-1 tvg-id="ABSTV.ag",ABS TV https://tego-cdn2a.sibercdn.com/Live_TV-ABSTV-10/tracks-v3a1/rewind-7200.m3u8?token=e5f61e7be8363eb781b4bdfe591bf917dd529c1a-SjY3NzRTbDZQNnFQVkZaNkZja2RxV3JKc1VBa05zQkdMNStJakRGV0VTTzNrOEVGVUlIQmxta1NLV0o3bzdVdQ-1736094545-1736008145 -#EXTINF:-1 tvg-id="ABSTV.ag@HD",ABS TV (1080p) [Not 24/7] +#EXTINF:-1 tvg-id="ABSTV.ag@HD",ABS TV (1080p) [Geo-blocked] https://query-streamlink.herokuapp.com/iptv-query?streaming-ip=https://www.twitch.tv/absliveantigua3 From 7f7862ba962f35bb2b4e3d59c65b6ee54889a366 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Sat, 13 Dec 2025 01:23:58 +0300 Subject: [PATCH 2/7] Update test.ts --- scripts/commands/playlist/test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/commands/playlist/test.ts b/scripts/commands/playlist/test.ts index d428aca167..e677429861 100644 --- a/scripts/commands/playlist/test.ts +++ b/scripts/commands/playlist/test.ts @@ -124,6 +124,7 @@ function drawTable() { { name: '', alignment: 'center', minLen: 3, maxLen: 3 }, { name: 'tvg-id', alignment: 'left', color: 'green', minLen: 25, maxLen: 25 }, { name: 'url', alignment: 'left', color: 'green', minLen: 100, maxLen: 100 }, + { name: 'label', alignment: 'left', color: 'yellow', minLen: 15, maxLen: 15 }, { name: 'status', alignment: 'left', minLen: 25, maxLen: 25 } ] }) @@ -136,6 +137,7 @@ function drawTable() { '': index, 'tvg-id': truncate(tvgId, 25), url: truncate(stream.url, 100), + label: stream.label, status } table.append(row) @@ -153,7 +155,7 @@ async function removeBrokenLinks() { let streams: Collection = new Collection(streamsGrouped.get(filepath)) streams = streams.filter((stream: Stream) => - !stream.statusCode ? true : !errorStatusCodes.includes(stream.statusCode) + !stream.statusCode || stream.label ? true : !errorStatusCodes.includes(stream.statusCode) ) const playlist = new Playlist(streams, { public: false }) From bdf23a698e7ac0b30e539dc964d063940646990d Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Sat, 13 Dec 2025 01:29:38 +0300 Subject: [PATCH 3/7] Update CONTRIBUTING.md --- CONTRIBUTING.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 92f4f851a6..9037125f6e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -77,17 +77,17 @@ This command will run an automatic check of all links in the playlist and displa npm run playlist:test streams/fr.m3u streams/fr.m3u -┌─────┬───────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────────────────────┬───────────────────────────┐ -│ │ tvg-id │ url │ status │ -├─────┼───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────────────────┤ -│ 0 │ 6ter.fr │ https://origin-caf900c010ea8046.live.6cloud.fr/out/v1/29c7a579af3348b48230f76cd75699a5/dash_short... │ LOADING... │ -│ 1 │ 20MinutesTV.fr │ https://lives.digiteka.com/stream/86d3e867-a272-496b-8412-f59aa0104771/index.m3u8 │ FFMPEG_STREAMS_NOT_FOUND │ -│ 2 │ │ https://video1.getstreamhosting.com:1936/8420/8420/playlist.m3u8 │ OK │ -│ 3 │ ADNTVPlus.fr │ https://samsunguk-adn-samsung-fre-qfrlc.amagi.tv/playlist/samsunguk-adn-samsung-fre/playlist.m3u8 │ HTTP_FORBIDDEN │ -│ 4 │ Africa24.fr │ https://edge12.vedge.infomaniak.com/livecast/ik:africa24/manifest.m3u8 │ OK │ -│ 5 │ Africa24English.fr │ https://edge17.vedge.infomaniak.com/livecast/ik:africa24sport/manifest.m3u8 │ OK │ -│ 6 │ AfricanewsEnglish.fr │ https://37c774660687468c821a51190046facf.mediatailor.us-east-1.amazonaws.com/v1/master/04fd913bb2... │ HTTP_GATEWAY_TIMEOUT │ -│ 7 │ AlpedHuezTV.fr │ https://edge.vedge.infomaniak.com/livecast/ik:adhtv/chunklist.m3u8 │ HTTP_NOT_FOUND │ +┌─────┬───────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────────────────────┬────────────────┬───────────────────────────┐ +│ │ tvg-id │ url │ label │ status │ +├─────┼───────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────┼────────────────┼───────────────────────────┤ +│ 0 │ 6ter.fr │ https://origin-caf900c010ea8046.live.6cloud.fr/out/v1/29c7a579af3348b48230f76cd75699a5/dash_short... │ │ LOADING... │ +│ 1 │ 20MinutesTV.fr │ https://lives.digiteka.com/stream/86d3e867-a272-496b-8412-f59aa0104771/index.m3u8 │ │ FFMPEG_STREAMS_NOT_FOUND │ +│ 2 │ │ https://video1.getstreamhosting.com:1936/8420/8420/playlist.m3u8 │ │ OK │ +│ 3 │ ADNTVPlus.fr │ https://samsunguk-adn-samsung-fre-qfrlc.amagi.tv/playlist/samsunguk-adn-samsung-fre/playlist.m3u8 │ Geo-blocked │ HTTP_FORBIDDEN │ +│ 4 │ Africa24.fr │ https://edge12.vedge.infomaniak.com/livecast/ik:africa24/manifest.m3u8 │ │ OK │ +│ 5 │ Africa24English.fr │ https://edge17.vedge.infomaniak.com/livecast/ik:africa24sport/manifest.m3u8 │ │ OK │ +│ 6 │ AfricanewsEnglish.fr │ https://37c774660687468c821a51190046facf.mediatailor.us-east-1.amazonaws.com/v1/master/04fd913bb2... │ │ HTTP_GATEWAY_TIMEOUT │ +│ 7 │ AlpedHuezTV.fr │ https://edge.vedge.infomaniak.com/livecast/ik:adhtv/chunklist.m3u8 │ Not 24/7 │ HTTP_NOT_FOUND │ ``` Also, if you add the `--fix` option to the command, the script will automatically remove all broken streams it finds from your local copy of playlists: From b100d93124c14ae771592094cfa44fbce733efd8 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Sat, 13 Dec 2025 01:35:09 +0300 Subject: [PATCH 4/7] Update ag.m3u --- tests/__data__/input/playlist_test/streams/ag.m3u | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/__data__/input/playlist_test/streams/ag.m3u b/tests/__data__/input/playlist_test/streams/ag.m3u index e3f4015a91..0ec3908d50 100644 --- a/tests/__data__/input/playlist_test/streams/ag.m3u +++ b/tests/__data__/input/playlist_test/streams/ag.m3u @@ -1,5 +1,5 @@ #EXTM3U #EXTINF:-1 tvg-id="ABSTV.ag",ABS TV https://tego-cdn2a.sibercdn.com/Live_TV-ABSTV-10/tracks-v3a1/rewind-7200.m3u8?token=e5f61e7be8363eb781b4bdfe591bf917dd529c1a-SjY3NzRTbDZQNnFQVkZaNkZja2RxV3JKc1VBa05zQkdMNStJakRGV0VTTzNrOEVGVUlIQmxta1NLV0o3bzdVdQ-1736094545-1736008145 -#EXTINF:-1 tvg-id="ABSTV.ag@HD",ABS TV (1080p) [Geo-blocked] +#EXTINF:-1 tvg-id="ABSTV.ag@HD",ABS TV (1080p) https://query-streamlink.herokuapp.com/iptv-query?streaming-ip=https://www.twitch.tv/absliveantigua3 From 33d4969ed2122f94b8468cd6f628fa17fab0b4cc Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Sat, 13 Dec 2025 02:12:30 +0300 Subject: [PATCH 5/7] Update test.ts --- scripts/commands/playlist/test.ts | 43 +++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/scripts/commands/playlist/test.ts b/scripts/commands/playlist/test.ts index e677429861..dbba11c571 100644 --- a/scripts/commands/playlist/test.ts +++ b/scripts/commands/playlist/test.ts @@ -21,7 +21,15 @@ const results: { [key: string]: string } = {} let interval: string | number | NodeJS.Timeout | undefined let streams = new Collection() let isLiveUpdateEnabled = true -const errorStatusCodes = ['ENOTFOUND', 'HTTP_404_NOT_FOUND', 'HTTP_404_UNKONWN_ERROR'] +const errorStatusCodes = [ + 'ENOTFOUND', + 'ENETUNREACH', + 'ECONNREFUSED', + 'HTTP_404_NOT_FOUND', + 'HTTP_404_UNKONWN_ERROR', + 'HTTP_403_FORBIDDEN', + 'HTTP_401_UNAUTHORIZED' +] program .argument('[filepath...]', 'Path to file to test') @@ -98,17 +106,12 @@ async function runTest(stream: Stream) { stream.statusCode = result.status.code - let status = '' - if (result.status.ok) status = chalk.green('OK') - else if (errorStatusCodes.includes(result.status.code)) { - status = chalk.red(result.status.code) + if (stream.statusCode === 'OK') return + if (errorStatusCodes.includes(stream.statusCode) && !stream.label) { errors++ } else { - status = chalk.yellow(result.status.code) warnings++ } - - results[key] = status } function drawTable() { @@ -130,7 +133,6 @@ function drawTable() { }) streams.forEach((stream: Stream, index: number) => { const key = stream.getUniqKey() - const status = results[key] || chalk.gray('PENDING') const tvgId = stream.getTvgId() const row = { @@ -138,7 +140,7 @@ function drawTable() { 'tvg-id': truncate(tvgId, 25), url: truncate(stream.url, 100), label: stream.label, - status + status: getStatus(stream) } table.append(row) }) @@ -154,9 +156,7 @@ async function removeBrokenLinks() { for (const filepath of streamsGrouped.keys()) { let streams: Collection = new Collection(streamsGrouped.get(filepath)) - streams = streams.filter((stream: Stream) => - !stream.statusCode || stream.label ? true : !errorStatusCodes.includes(stream.statusCode) - ) + streams = streams.filter((stream: Stream) => !isBroken(stream)) const playlist = new Playlist(streams, { public: false }) await rootStorage.save(filepath, playlist.toString()) @@ -198,3 +198,20 @@ async function isOffline() { }) }).catch(() => {}) } + +function getStatus(stream: Stream): string { + if (!stream.statusCode) return chalk.gray('PENDING') + if (stream.statusCode === 'OK') return chalk.green(stream.statusCode) + if (errorStatusCodes.includes(stream.statusCode) && !stream.label) + return chalk.red(stream.statusCode) + + return chalk.yellow(stream.statusCode) +} + +function isBroken(stream: Stream): boolean { + if (!stream.statusCode) return false + if (stream.label) return false + if (!errorStatusCodes.includes(stream.statusCode)) return false + + return true +} From 1ab5de27690cf1b21888e546c3ffdc95db9f1bf2 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Sun, 14 Dec 2025 01:11:09 +0300 Subject: [PATCH 6/7] Update test.ts --- scripts/commands/playlist/test.ts | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/scripts/commands/playlist/test.ts b/scripts/commands/playlist/test.ts index dbba11c571..3c79f03afd 100644 --- a/scripts/commands/playlist/test.ts +++ b/scripts/commands/playlist/test.ts @@ -127,20 +127,24 @@ function drawTable() { { name: '', alignment: 'center', minLen: 3, maxLen: 3 }, { name: 'tvg-id', alignment: 'left', color: 'green', minLen: 25, maxLen: 25 }, { name: 'url', alignment: 'left', color: 'green', minLen: 100, maxLen: 100 }, - { name: 'label', alignment: 'left', color: 'yellow', minLen: 15, maxLen: 15 }, + { name: 'label', alignment: 'left', color: 'yellow', minLen: 13, maxLen: 13 }, { name: 'status', alignment: 'left', minLen: 25, maxLen: 25 } ] }) + streams.forEach((stream: Stream, index: number) => { - const key = stream.getUniqKey() - const tvgId = stream.getTvgId() + const tvgId = truncate(stream.getTvgId(), 25) + const url = truncate(stream.url, 100) + const color = getColor(stream) + const label = stream.label || '' + const status = stream.statusCode || 'PENDING' const row = { '': index, - 'tvg-id': truncate(tvgId, 25), - url: truncate(stream.url, 100), - label: stream.label, - status: getStatus(stream) + 'tvg-id': chalk[color](tvgId), + url: chalk[color](url), + label: chalk[color](label), + status: chalk[color](status) } table.append(row) }) @@ -199,13 +203,12 @@ async function isOffline() { }).catch(() => {}) } -function getStatus(stream: Stream): string { - if (!stream.statusCode) return chalk.gray('PENDING') - if (stream.statusCode === 'OK') return chalk.green(stream.statusCode) - if (errorStatusCodes.includes(stream.statusCode) && !stream.label) - return chalk.red(stream.statusCode) +function getColor(stream: Stream): string { + if (!stream.statusCode) return 'gray' + if (stream.statusCode === 'OK') return 'green' + if (errorStatusCodes.includes(stream.statusCode) && !stream.label) return 'red' - return chalk.yellow(stream.statusCode) + return 'yellow' } function isBroken(stream: Stream): boolean { From bbdbc92138fa638940df253be3bdfb4b5164ad26 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Sun, 14 Dec 2025 01:18:49 +0300 Subject: [PATCH 7/7] Update test.ts --- scripts/commands/playlist/test.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/scripts/commands/playlist/test.ts b/scripts/commands/playlist/test.ts index 3c79f03afd..0464ea645c 100644 --- a/scripts/commands/playlist/test.ts +++ b/scripts/commands/playlist/test.ts @@ -17,7 +17,6 @@ const LIVE_UPDATE_MAX_STREAMS = 100 let errors = 0 let warnings = 0 -const results: { [key: string]: string } = {} let interval: string | number | NodeJS.Timeout | undefined let streams = new Collection() let isLiveUpdateEnabled = true @@ -99,11 +98,8 @@ async function main() { main() async function runTest(stream: Stream) { - const key = stream.getUniqKey() - results[key] = chalk.white('LOADING...') - + stream.statusCode = 'LOADING...' const result: StreamTesterResult = await tester.test(stream) - stream.statusCode = result.status.code if (stream.statusCode === 'OK') return @@ -205,6 +201,7 @@ async function isOffline() { function getColor(stream: Stream): string { if (!stream.statusCode) return 'gray' + if (stream.statusCode === 'LOADING...') return 'white' if (stream.statusCode === 'OK') return 'green' if (errorStatusCodes.includes(stream.statusCode) && !stream.label) return 'red'