From 811ae7cea48ad51469c0e8bbfddfb137bc251ba3 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Thu, 4 Dec 2025 18:30:45 +0300 Subject: [PATCH 01/11] Create .prettierrc --- .prettierrc | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000000..1c187c69b2 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "singleQuote": true, + "semi": false, + "printWidth": 100, + "trailingComma": "none", + "arrowParens": "avoid" +} \ No newline at end of file From a9e0398414c03c965a55e53fd292724b933a1bea Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Thu, 4 Dec 2025 18:31:33 +0300 Subject: [PATCH 02/11] Update package.json --- package.json | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 0358af08f8..7453c15715 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,6 @@ "act:format": "gh act workflow_dispatch -W .github/workflows/format.yml", "act:update": "gh act workflow_dispatch -W .github/workflows/update.yml", "api:load": "tsx scripts/commands/api/load.ts", - "api:generate": "tsx scripts/commands/api/generate.ts", - "api:deploy": "npx gh-pages-clean && npx gh-pages -a -m \"Deploy to iptv-org/api\" -d .api -r https://$GITHUB_TOKEN@github.com/iptv-org/api.git", "playlist:format": "tsx scripts/commands/playlist/format.ts", "playlist:update": "tsx scripts/commands/playlist/update.ts", "playlist:generate": "tsx scripts/commands/playlist/generate.ts", @@ -14,13 +12,12 @@ "playlist:lint": "npx m3u-linter -c m3u-linter.json", "playlist:test": "tsx scripts/commands/playlist/test.ts", "playlist:edit": "tsx scripts/commands/playlist/edit.ts", - "playlist:deploy": "npx gh-pages-clean && npx gh-pages -m \"Deploy to GitHub Pages\" -d .gh-pages -r https://$GITHUB_TOKEN@github.com/iptv-org/iptv.git", + "playlist:export": "tsx scripts/commands/playlist/export.ts", "readme:update": "tsx scripts/commands/readme/update.ts", "report:create": "tsx scripts/commands/report/create.ts", "check": "npm run playlist:lint && npm run playlist:validate", "format": "npm run playlist:format", "update": "npm run playlist:generate && npm run api:generate && npm run readme:update", - "deploy": "npm run playlist:deploy && npm run api:deploy", "lint": "npx eslint \"scripts/**/*.{ts,js}\" \"tests/**/*.{ts,js}\"", "test": "jest --runInBand", "postinstall": "npm run api:load" @@ -45,7 +42,7 @@ "@freearhey/search-js": "^0.1.2", "@freearhey/storage-js": "^0.1.0", "@inquirer/prompts": "^7.8.0", - "@iptv-org/sdk": "^1.0.2", + "@iptv-org/sdk": "^1.1.3", "@octokit/core": "^7.0.3", "@octokit/plugin-paginate-rest": "^13.1.1", "@octokit/plugin-rest-endpoint-methods": "^16.0.0", From 007921693187b267c25b4f7f728643e7cf2d10b4 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Thu, 4 Dec 2025 18:31:35 +0300 Subject: [PATCH 03/11] Update package-lock.json --- package-lock.json | 87 ++++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/package-lock.json b/package-lock.json index a5f14a99b4..d68fefc967 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@freearhey/search-js": "^0.1.2", "@freearhey/storage-js": "^0.1.0", "@inquirer/prompts": "^7.8.0", - "@iptv-org/sdk": "^1.0.2", + "@iptv-org/sdk": "^1.1.3", "@octokit/core": "^7.0.3", "@octokit/plugin-paginate-rest": "^13.1.1", "@octokit/plugin-rest-endpoint-methods": "^16.0.0", @@ -1501,18 +1501,35 @@ } }, "node_modules/@iptv-org/sdk": { - "version": "1.0.2", - "resolved": "https://npm.pkg.github.com/download/@iptv-org/sdk/1.0.2/131e5145ff68ffb5e213eb02d26d43db91a8a5a3", - "integrity": "sha512-Lpq+5vko9HkqOMDaXpeLBzKVgJikuKax7dJgBjr+XIgxRgMB1gucCjbvTNCR5AHhnORWvX+LFaodzy06jKukOA==", - "license": "UNLICENSED", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@iptv-org/sdk/-/sdk-1.1.3.tgz", + "integrity": "sha512-e2IQWPVpNdMJnCkJulnBpiU2Hn5hpaSKNTxo4bvOI4uMRRXR7R8hWbm9jtJnf5LYQ6xdN0xT52EjI0zi9dh5yg==", "dependencies": { - "@freearhey/core": "^0.14.3", + "@freearhey/core": "^0.15.1", "@freearhey/search-js": "^0.2.0", "@ntlab/sfetch": "^1.2.0", "axios": "^1.11.0", "dayjs": "^1.11.18" } }, + "node_modules/@iptv-org/sdk/node_modules/@freearhey/core": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@freearhey/core/-/core-0.15.1.tgz", + "integrity": "sha512-wYquasgTNGsLGDXPjHlPMvwfuoG9kgy9sEWsEc/i4Z2T2uKCfTboHYola9Ykh9X1lGlECypq8lL0IBAiOhKE/w==", + "dependencies": { + "@types/lodash": "^4.14.198", + "@types/pako": "^2.0.3", + "consola": "^3.4.2", + "dayjs": "^1.11.13", + "glob": "^11.0.1", + "lodash": "^4.17.21", + "natural-orderby": "^5.0.0", + "normalize-url": "^8.1.0", + "object-treeify": "^2.1.1", + "pako": "^2.1.0", + "timer-node": "^5.0.9" + } + }, "node_modules/@iptv-org/sdk/node_modules/@freearhey/search-js": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@freearhey/search-js/-/search-js-0.2.0.tgz", @@ -1528,7 +1545,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", - "license": "MIT", "engines": { "node": "20 || >=22" } @@ -1537,7 +1553,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", - "license": "MIT", "dependencies": { "@isaacs/balanced-match": "^4.0.1" }, @@ -1653,10 +1668,9 @@ } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "license": "MIT", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -2007,10 +2021,9 @@ } }, "node_modules/@jest/reporters/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "license": "ISC", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -4829,14 +4842,13 @@ } }, "node_modules/glob": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", - "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", - "license": "ISC", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", - "minimatch": "^10.0.3", + "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" @@ -4863,10 +4875,9 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", - "license": "ISC", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", "dependencies": { "@isaacs/brace-expansion": "^5.0.0" }, @@ -5467,10 +5478,9 @@ } }, "node_modules/jest-config/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "license": "ISC", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -5936,10 +5946,9 @@ } }, "node_modules/jest-runtime/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "license": "ISC", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -6218,9 +6227,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dependencies": { "argparse": "^2.0.1" }, @@ -7707,9 +7716,9 @@ } }, "node_modules/validator": { - "version": "13.15.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.0.tgz", - "integrity": "sha512-36B2ryl4+oL5QxZ3AzD0t5SsMNGvTtQHpjgFO5tbNxfXbMFkY822ktCDe1MnlqV3301QQI9SLHDNJokDI+Z9pA==", + "version": "13.15.23", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.23.tgz", + "integrity": "sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw==", "engines": { "node": ">= 0.10" } From 963e4a88c4aa8956f6ed0387c13b7a7ed7681aa1 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Thu, 4 Dec 2025 18:31:54 +0300 Subject: [PATCH 04/11] Update tests/__data__ --- .../.api/streams.json | 0 tests/__data__/expected/playlist_format/in.m3u | 4 +++- tests/__data__/expected/playlist_format/nl.m3u | 6 +----- .../.gh-pages/categories/undefined.m3u | 6 +----- .../.gh-pages/countries/undefined.m3u | 6 +----- .../playlist_generate/.gh-pages/index.category.m3u | 6 +----- .../playlist_generate/.gh-pages/index.country.m3u | 6 +----- .../playlist_generate/.gh-pages/index.language.m3u | 6 +----- .../expected/playlist_generate/.gh-pages/index.m3u | 6 +----- .../.gh-pages/languages/undefined.m3u | 6 +----- .../playlist_generate/.gh-pages/raw/unsorted.m3u | 6 +----- .../playlist_generate/.gh-pages/sources/unsorted.m3u | 6 +----- tests/__data__/expected/playlist_update/fr.m3u | 4 +++- tests/__data__/expected/playlist_update/uk.m3u | 6 ------ tests/__data__/expected/playlist_update/us.m3u | 11 +++-------- .../input/{api_generate => playlist_export}/ad.m3u | 0 .../input/{api_generate => playlist_export}/ca.m3u | 0 .../input/{api_generate => playlist_export}/in.m3u | 0 .../input/{api_generate => playlist_export}/uk.m3u | 0 .../{api_generate => playlist_export}/unsorted.m3u | 0 20 files changed, 19 insertions(+), 66 deletions(-) rename tests/__data__/expected/{api_generate => playlist_export}/.api/streams.json (100%) rename tests/__data__/input/{api_generate => playlist_export}/ad.m3u (100%) rename tests/__data__/input/{api_generate => playlist_export}/ca.m3u (100%) rename tests/__data__/input/{api_generate => playlist_export}/in.m3u (100%) rename tests/__data__/input/{api_generate => playlist_export}/uk.m3u (100%) rename tests/__data__/input/{api_generate => playlist_export}/unsorted.m3u (100%) diff --git a/tests/__data__/expected/api_generate/.api/streams.json b/tests/__data__/expected/playlist_export/.api/streams.json similarity index 100% rename from tests/__data__/expected/api_generate/.api/streams.json rename to tests/__data__/expected/playlist_export/.api/streams.json diff --git a/tests/__data__/expected/playlist_format/in.m3u b/tests/__data__/expected/playlist_format/in.m3u index 84637bfe5c..f733f449a4 100644 --- a/tests/__data__/expected/playlist_format/in.m3u +++ b/tests/__data__/expected/playlist_format/in.m3u @@ -1,3 +1,5 @@ #EXTM3U -#EXTINF:-1 tvg-id="" http-referrer="http://test.com" http-user-agent="Mozilla/5.0",Manorama News -2 [U3] (480p) [Geo-blocked] [Not 24/7] +#EXTINF:-1 tvg-id="",Manorama News -2 [U3] (480p) [Geo-blocked] [Not 24/7] +#EXTVLCOPT:http-referrer=http://test.com +#EXTVLCOPT:http-user-agent=Mozilla/5.0 https://ythls.onrender.com/channel/UCP0uG-mcMImgKnJz-VjJZmQ.m3u8 diff --git a/tests/__data__/expected/playlist_format/nl.m3u b/tests/__data__/expected/playlist_format/nl.m3u index bf18bfa6f1..b9d1f9e4cf 100644 --- a/tests/__data__/expected/playlist_format/nl.m3u +++ b/tests/__data__/expected/playlist_format/nl.m3u @@ -3,13 +3,9 @@ http://resolver.streaming.api.nos.nl/livestream?url=/live/npo/tvlive/npo1/npo1.isml/.m3u8 #EXTINF:-1 tvg-id="NPO2.nl@SD",NPO 2 (342p) http://resolver.streaming.api.nos.nl/livestream?url=/live/npo/tvlive/npo2/npo2.isml/.m3u8 -#EXTINF:-1 tvg-id="NPO2.nl@SD" http-referrer="http://imn.iq" http-user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",NPO 2 (302p) [Geo-blocked] +#EXTINF:-1 tvg-id="NPO2.nl@SD",NPO 2 (302p) [Geo-blocked] #EXTVLCOPT:http-referrer=http://imn.iq #EXTVLCOPT:http-user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 -#KODIPROP:inputstream=inputstream.adaptive -#KODIPROP:inputstream.adaptive.manifest_type=mpd -#KODIPROP:inputstream.adaptive.license_type=com.widevine.alpha -#KODIPROP:inputstream.adaptive.license_key=https://drm.ors.at/acquire-license/widevine?BrandGuid=13f2e056-53fe-4469-ba6d-999970dbe549&userToken=v9ZVSksv4S7rT55o10dmYNRa4asye3z05eWCFxD%2FFYIlTJEpuf6tF8asPcyQOFq0h5opS%2B6WoMxnshWkihpHq5qrdrBEZ69piE94J9Feh385snGOqK3PYO7tLLjxmsCAe%2B9%2BNnurSSO5RCAIRsL125nSj1eOR%2F1GSKOgGH80HK2FDLiePxPkeaAxuWzacNBB%2FqnIGGxfe3GlmN65cU9F8WEpKFDlaxW%2Fv3ZSLAp3%2BZEq1aZXJ6Oz%2Fi0diD0EybH7|Content-Type=application/octet-stream|R{SSM}| http://stream.tvtap.net:8081/live/nl-npo2.stream/playlist.m3u8?|Referer="https://referer.xyz/"|User-Agent="Mozilla/5.0+(iPhone;+CPU+iPhone+OS+17_7+like+Mac+OS+X)+AppleWebKit/605.1.15+(KHTML,+like+Gecko)+Version/18.0+Mobile/15E148+Safari/604.1"|Origin="https://origin.xyz" #EXTINF:-1 tvg-id="NPO2.nl@SD",NPO 2 [Geo-blocked] http://resolver.streaming.api.nos.nl/livestream?url=/live/npo/tvlive/npo2/npo22.isml/.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/categories/undefined.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/categories/undefined.m3u index 5373b79516..cf6e335107 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/categories/undefined.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/categories/undefined.m3u @@ -1,13 +1,9 @@ #EXTM3U #EXTINF:-1 tvg-id="5AABTV.ca" tvg-logo="" group-title="Undefined",5AAB TV http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8 -#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined" http-referrer="http://imn.iq" http-user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",Andorra TV (720p) [Not 24/7] +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Andorra TV (720p) [Not 24/7] #EXTVLCOPT:http-referrer=http://imn.iq #EXTVLCOPT:http-user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 -#KODIPROP:inputstream=inputstream.adaptive -#KODIPROP:inputstream.adaptive.manifest_type=mpd -#KODIPROP:inputstream.adaptive.license_type=com.widevine.alpha -#KODIPROP:inputstream.adaptive.license_key=https://drm.ors.at/acquire-license/widevine?BrandGuid=13f2e056-53fe-4469-ba6d-999970dbe549&userToken=v9ZVSksv4S7rT55o10dmYNRa4asye3z05eWCFxD%2FFYIlTJEpuf6tF8asPcyQOFq0h5opS%2B6WoMxnshWkihpHq5qrdrBEZ69piE94J9Feh385snGOqK3PYO7tLLjxmsCAe%2B9%2BNnurSSO5RCAIRsL125nSj1eOR%2F1GSKOgGH80HK2FDLiePxPkeaAxuWzacNBB%2FqnIGGxfe3GlmN65cU9F8WEpKFDlaxW%2Fv3ZSLAp3%2BZEq1aZXJ6Oz%2Fi0diD0EybH7|Content-Type=application/octet-stream|R{SSM}| http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8|Referer="https://referer.xyz/"|User-Agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1"|Origin="https://origin.xyz" #EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="https://i.imgur.com/BnhTn8i.png" group-title="Undefined",ATV https://iptv-all.lanesh4d0w.repl.co/andorra/atv diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/countries/undefined.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/countries/undefined.m3u index dd4df2992c..dd09fd6ad1 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/countries/undefined.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/countries/undefined.m3u @@ -1,11 +1,7 @@ #EXTM3U -#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined" http-referrer="http://imn.iq" http-user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",Andorra TV (720p) [Not 24/7] +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Andorra TV (720p) [Not 24/7] #EXTVLCOPT:http-referrer=http://imn.iq #EXTVLCOPT:http-user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 -#KODIPROP:inputstream=inputstream.adaptive -#KODIPROP:inputstream.adaptive.manifest_type=mpd -#KODIPROP:inputstream.adaptive.license_type=com.widevine.alpha -#KODIPROP:inputstream.adaptive.license_key=https://drm.ors.at/acquire-license/widevine?BrandGuid=13f2e056-53fe-4469-ba6d-999970dbe549&userToken=v9ZVSksv4S7rT55o10dmYNRa4asye3z05eWCFxD%2FFYIlTJEpuf6tF8asPcyQOFq0h5opS%2B6WoMxnshWkihpHq5qrdrBEZ69piE94J9Feh385snGOqK3PYO7tLLjxmsCAe%2B9%2BNnurSSO5RCAIRsL125nSj1eOR%2F1GSKOgGH80HK2FDLiePxPkeaAxuWzacNBB%2FqnIGGxfe3GlmN65cU9F8WEpKFDlaxW%2Fv3ZSLAp3%2BZEq1aZXJ6Oz%2Fi0diD0EybH7|Content-Type=application/octet-stream|R{SSM}| http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8|Referer="https://referer.xyz/"|User-Agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1"|Origin="https://origin.xyz" #EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="https://i.imgur.com/CnhTn8i.png" group-title="Undefined",ATV HD https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/index.category.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/index.category.m3u index 125eb49e67..6ba5454679 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/index.category.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/index.category.m3u @@ -11,13 +11,9 @@ http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 #EXTINF:-1 tvg-id="5AABTV.ca" tvg-logo="" group-title="Undefined",5AAB TV http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8 -#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined" http-referrer="http://imn.iq" http-user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",Andorra TV (720p) [Not 24/7] +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Andorra TV (720p) [Not 24/7] #EXTVLCOPT:http-referrer=http://imn.iq #EXTVLCOPT:http-user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 -#KODIPROP:inputstream=inputstream.adaptive -#KODIPROP:inputstream.adaptive.manifest_type=mpd -#KODIPROP:inputstream.adaptive.license_type=com.widevine.alpha -#KODIPROP:inputstream.adaptive.license_key=https://drm.ors.at/acquire-license/widevine?BrandGuid=13f2e056-53fe-4469-ba6d-999970dbe549&userToken=v9ZVSksv4S7rT55o10dmYNRa4asye3z05eWCFxD%2FFYIlTJEpuf6tF8asPcyQOFq0h5opS%2B6WoMxnshWkihpHq5qrdrBEZ69piE94J9Feh385snGOqK3PYO7tLLjxmsCAe%2B9%2BNnurSSO5RCAIRsL125nSj1eOR%2F1GSKOgGH80HK2FDLiePxPkeaAxuWzacNBB%2FqnIGGxfe3GlmN65cU9F8WEpKFDlaxW%2Fv3ZSLAp3%2BZEq1aZXJ6Oz%2Fi0diD0EybH7|Content-Type=application/octet-stream|R{SSM}| http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8|Referer="https://referer.xyz/"|User-Agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1"|Origin="https://origin.xyz" #EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="https://i.imgur.com/BnhTn8i.png" group-title="Undefined",ATV https://iptv-all.lanesh4d0w.repl.co/andorra/atv diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/index.country.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/index.country.m3u index 8d8394b09d..73aca46c1c 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/index.country.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/index.country.m3u @@ -15,13 +15,9 @@ http://146.59.85.40:89/dunaworld/index.m3u8 http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.m3u8 #EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="International",ЭлТР (480p) [Not 24/7] http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 -#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined" http-referrer="http://imn.iq" http-user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",Andorra TV (720p) [Not 24/7] +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Andorra TV (720p) [Not 24/7] #EXTVLCOPT:http-referrer=http://imn.iq #EXTVLCOPT:http-user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 -#KODIPROP:inputstream=inputstream.adaptive -#KODIPROP:inputstream.adaptive.manifest_type=mpd -#KODIPROP:inputstream.adaptive.license_type=com.widevine.alpha -#KODIPROP:inputstream.adaptive.license_key=https://drm.ors.at/acquire-license/widevine?BrandGuid=13f2e056-53fe-4469-ba6d-999970dbe549&userToken=v9ZVSksv4S7rT55o10dmYNRa4asye3z05eWCFxD%2FFYIlTJEpuf6tF8asPcyQOFq0h5opS%2B6WoMxnshWkihpHq5qrdrBEZ69piE94J9Feh385snGOqK3PYO7tLLjxmsCAe%2B9%2BNnurSSO5RCAIRsL125nSj1eOR%2F1GSKOgGH80HK2FDLiePxPkeaAxuWzacNBB%2FqnIGGxfe3GlmN65cU9F8WEpKFDlaxW%2Fv3ZSLAp3%2BZEq1aZXJ6Oz%2Fi0diD0EybH7|Content-Type=application/octet-stream|R{SSM}| http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8|Referer="https://referer.xyz/"|User-Agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1"|Origin="https://origin.xyz" #EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="https://i.imgur.com/CnhTn8i.png" group-title="Undefined",ATV HD https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/index.language.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/index.language.m3u index 626893e3e9..a5f6343690 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/index.language.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/index.language.m3u @@ -7,13 +7,9 @@ http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.m3u8 #EXTINF:-1 tvg-id="5AABTV.ca" tvg-logo="" group-title="Undefined",5AAB TV http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8 -#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined" http-referrer="http://imn.iq" http-user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",Andorra TV (720p) [Not 24/7] +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Andorra TV (720p) [Not 24/7] #EXTVLCOPT:http-referrer=http://imn.iq #EXTVLCOPT:http-user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 -#KODIPROP:inputstream=inputstream.adaptive -#KODIPROP:inputstream.adaptive.manifest_type=mpd -#KODIPROP:inputstream.adaptive.license_type=com.widevine.alpha -#KODIPROP:inputstream.adaptive.license_key=https://drm.ors.at/acquire-license/widevine?BrandGuid=13f2e056-53fe-4469-ba6d-999970dbe549&userToken=v9ZVSksv4S7rT55o10dmYNRa4asye3z05eWCFxD%2FFYIlTJEpuf6tF8asPcyQOFq0h5opS%2B6WoMxnshWkihpHq5qrdrBEZ69piE94J9Feh385snGOqK3PYO7tLLjxmsCAe%2B9%2BNnurSSO5RCAIRsL125nSj1eOR%2F1GSKOgGH80HK2FDLiePxPkeaAxuWzacNBB%2FqnIGGxfe3GlmN65cU9F8WEpKFDlaxW%2Fv3ZSLAp3%2BZEq1aZXJ6Oz%2Fi0diD0EybH7|Content-Type=application/octet-stream|R{SSM}| http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8|Referer="https://referer.xyz/"|User-Agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1"|Origin="https://origin.xyz" #EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="https://i.imgur.com/CnhTn8i.png" group-title="Undefined",ATV HD https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/index.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/index.m3u index 255edf9a86..b5c6838c82 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/index.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/index.m3u @@ -1,13 +1,9 @@ #EXTM3U #EXTINF:-1 tvg-id="5AABTV.ca" tvg-logo="" group-title="Undefined",5AAB TV http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8 -#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined" http-referrer="http://imn.iq" http-user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",Andorra TV (720p) [Not 24/7] +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Andorra TV (720p) [Not 24/7] #EXTVLCOPT:http-referrer=http://imn.iq #EXTVLCOPT:http-user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 -#KODIPROP:inputstream=inputstream.adaptive -#KODIPROP:inputstream.adaptive.manifest_type=mpd -#KODIPROP:inputstream.adaptive.license_type=com.widevine.alpha -#KODIPROP:inputstream.adaptive.license_key=https://drm.ors.at/acquire-license/widevine?BrandGuid=13f2e056-53fe-4469-ba6d-999970dbe549&userToken=v9ZVSksv4S7rT55o10dmYNRa4asye3z05eWCFxD%2FFYIlTJEpuf6tF8asPcyQOFq0h5opS%2B6WoMxnshWkihpHq5qrdrBEZ69piE94J9Feh385snGOqK3PYO7tLLjxmsCAe%2B9%2BNnurSSO5RCAIRsL125nSj1eOR%2F1GSKOgGH80HK2FDLiePxPkeaAxuWzacNBB%2FqnIGGxfe3GlmN65cU9F8WEpKFDlaxW%2Fv3ZSLAp3%2BZEq1aZXJ6Oz%2Fi0diD0EybH7|Content-Type=application/octet-stream|R{SSM}| http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8|Referer="https://referer.xyz/"|User-Agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1"|Origin="https://origin.xyz" #EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="https://i.imgur.com/BnhTn8i.png" group-title="Undefined",ATV https://iptv-all.lanesh4d0w.repl.co/andorra/atv diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/languages/undefined.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/languages/undefined.m3u index 1bca3048d0..cbfd187b6d 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/languages/undefined.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/languages/undefined.m3u @@ -1,13 +1,9 @@ #EXTM3U #EXTINF:-1 tvg-id="5AABTV.ca" tvg-logo="" group-title="Undefined",5AAB TV http://158.69.124.9:1935/5aabtv/5aabtv/playlist.m3u8 -#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined" http-referrer="http://imn.iq" http-user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",Andorra TV (720p) [Not 24/7] +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Andorra TV (720p) [Not 24/7] #EXTVLCOPT:http-referrer=http://imn.iq #EXTVLCOPT:http-user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 -#KODIPROP:inputstream=inputstream.adaptive -#KODIPROP:inputstream.adaptive.manifest_type=mpd -#KODIPROP:inputstream.adaptive.license_type=com.widevine.alpha -#KODIPROP:inputstream.adaptive.license_key=https://drm.ors.at/acquire-license/widevine?BrandGuid=13f2e056-53fe-4469-ba6d-999970dbe549&userToken=v9ZVSksv4S7rT55o10dmYNRa4asye3z05eWCFxD%2FFYIlTJEpuf6tF8asPcyQOFq0h5opS%2B6WoMxnshWkihpHq5qrdrBEZ69piE94J9Feh385snGOqK3PYO7tLLjxmsCAe%2B9%2BNnurSSO5RCAIRsL125nSj1eOR%2F1GSKOgGH80HK2FDLiePxPkeaAxuWzacNBB%2FqnIGGxfe3GlmN65cU9F8WEpKFDlaxW%2Fv3ZSLAp3%2BZEq1aZXJ6Oz%2Fi0diD0EybH7|Content-Type=application/octet-stream|R{SSM}| http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8|Referer="https://referer.xyz/"|User-Agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1"|Origin="https://origin.xyz" #EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="https://i.imgur.com/CnhTn8i.png" group-title="Undefined",ATV HD https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/raw/unsorted.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/raw/unsorted.m3u index 530c218fa6..a42eaba5cc 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/raw/unsorted.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/raw/unsorted.m3u @@ -3,13 +3,9 @@ http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.m3u8 #EXTINF:-1 tvg-id="VisitXTV.nl" tvg-logo="https://i.imgur.com/RJ9wbNF.jpg" group-title="XXX",Visit-X TV https://stream.visit-x.tv/vxtv/ngrp:live_all/30fps.m3u8 -#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined" http-referrer="http://imn.iq" http-user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",Andorra TV (720p) [Not 24/7] +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Andorra TV (720p) [Not 24/7] #EXTVLCOPT:http-referrer=http://imn.iq #EXTVLCOPT:http-user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 -#KODIPROP:inputstream=inputstream.adaptive -#KODIPROP:inputstream.adaptive.manifest_type=mpd -#KODIPROP:inputstream.adaptive.license_type=com.widevine.alpha -#KODIPROP:inputstream.adaptive.license_key=https://drm.ors.at/acquire-license/widevine?BrandGuid=13f2e056-53fe-4469-ba6d-999970dbe549&userToken=v9ZVSksv4S7rT55o10dmYNRa4asye3z05eWCFxD%2FFYIlTJEpuf6tF8asPcyQOFq0h5opS%2B6WoMxnshWkihpHq5qrdrBEZ69piE94J9Feh385snGOqK3PYO7tLLjxmsCAe%2B9%2BNnurSSO5RCAIRsL125nSj1eOR%2F1GSKOgGH80HK2FDLiePxPkeaAxuWzacNBB%2FqnIGGxfe3GlmN65cU9F8WEpKFDlaxW%2Fv3ZSLAp3%2BZEq1aZXJ6Oz%2Fi0diD0EybH7|Content-Type=application/octet-stream|R{SSM}| http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8|Referer="https://referer.xyz/"|User-Agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1"|Origin="https://origin.xyz" #EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Undefined",Duna World (576i) http://146.59.85.40:89/dunaworld/index.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/sources/unsorted.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/sources/unsorted.m3u index 52eb85ee4e..88d7c786f2 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/sources/unsorted.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/sources/unsorted.m3u @@ -1,11 +1,7 @@ #EXTM3U -#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined" http-referrer="http://imn.iq" http-user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",Andorra TV (720p) [Not 24/7] +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Andorra TV (720p) [Not 24/7] #EXTVLCOPT:http-referrer=http://imn.iq #EXTVLCOPT:http-user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 -#KODIPROP:inputstream=inputstream.adaptive -#KODIPROP:inputstream.adaptive.manifest_type=mpd -#KODIPROP:inputstream.adaptive.license_type=com.widevine.alpha -#KODIPROP:inputstream.adaptive.license_key=https://drm.ors.at/acquire-license/widevine?BrandGuid=13f2e056-53fe-4469-ba6d-999970dbe549&userToken=v9ZVSksv4S7rT55o10dmYNRa4asye3z05eWCFxD%2FFYIlTJEpuf6tF8asPcyQOFq0h5opS%2B6WoMxnshWkihpHq5qrdrBEZ69piE94J9Feh385snGOqK3PYO7tLLjxmsCAe%2B9%2BNnurSSO5RCAIRsL125nSj1eOR%2F1GSKOgGH80HK2FDLiePxPkeaAxuWzacNBB%2FqnIGGxfe3GlmN65cU9F8WEpKFDlaxW%2Fv3ZSLAp3%2BZEq1aZXJ6Oz%2Fi0diD0EybH7|Content-Type=application/octet-stream|R{SSM}| http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8|Referer="https://referer.xyz/"|User-Agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1"|Origin="https://origin.xyz" #EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="Undefined",Duna World (576i) http://146.59.85.40:89/dunaworld/index.m3u8 diff --git a/tests/__data__/expected/playlist_update/fr.m3u b/tests/__data__/expected/playlist_update/fr.m3u index fe6e40fbce..08556c744c 100644 --- a/tests/__data__/expected/playlist_update/fr.m3u +++ b/tests/__data__/expected/playlist_update/fr.m3u @@ -1,3 +1,5 @@ #EXTM3U -#EXTINF:-1 tvg-id="TFX.fr@SD" http-referrer="https://pkpakiplay.xyz/" http-user-agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",TFX +#EXTINF:-1 tvg-id="TFX.fr@SD",TFX +#EXTVLCOPT:http-referrer=https://pkpakiplay.xyz/ +#EXTVLCOPT:http-user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 17_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1 https://stitcher-ipv4.pluto.tv/v1/stitch/embed/hls/channel/64c109a4798def0008a6e03e/master.m3u8?advertisingId={PSID}&appVersion=unknown&deviceDNT={TARGETOPT}&deviceId={PSID}&deviceLat=0&deviceLon=0&deviceMake=samsung&deviceModel=samsung&deviceType=samsung-tvplus&deviceVersion=unknown&embedPartner=samsung-tvplus&profileFloor=&profileLimit=&samsung_app_domain={APP_DOMAIN}&samsung_app_name={APP_NAME}&us_privacy=1YNY diff --git a/tests/__data__/expected/playlist_update/uk.m3u b/tests/__data__/expected/playlist_update/uk.m3u index ab04109e58..2333877803 100644 --- a/tests/__data__/expected/playlist_update/uk.m3u +++ b/tests/__data__/expected/playlist_update/uk.m3u @@ -4,10 +4,4 @@ http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 #EXTINF:-1 tvg-id="BBCNews.uk",BBC News HD (480p) [Geo-blocked] http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/playlist.m3u8 #EXTINF:-1 tvg-id="BeanoTV.uk@SD",Beano TV -#EXTVLCOPT:http-referrer=http://imn.iq -#EXTVLCOPT:http-user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 -#KODIPROP:inputstream=inputstream.adaptive -#KODIPROP:inputstream.adaptive.manifest_type=mpd -#KODIPROP:inputstream.adaptive.license_type=com.widevine.alpha -#KODIPROP:inputstream.adaptive.license_key=https://drm.ors.at/acquire-license/widevine?BrandGuid=13f2e056-53fe-4469-ba6d-999970dbe549&userToken=v9ZVSksv4S7rT55o10dmYNRa4asye3z05eWCFxD%2FFYIlTJEpuf6tF8asPcyQOFq0h5opS%2B6WoMxnshWkihpHq5qrdrBEZ69piE94J9Feh385snGOqK3PYO7tLLjxmsCAe%2B9%2BNnurSSO5RCAIRsL125nSj1eOR%2F1GSKOgGH80HK2FDLiePxPkeaAxuWzacNBB%2FqnIGGxfe3GlmN65cU9F8WEpKFDlaxW%2Fv3ZSLAp3%2BZEq1aZXJ6Oz%2Fi0diD0EybH7|Content-Type=application/octet-stream|R{SSM}| https://a5b4bacecd47433dad06d3189fc7422e.mediatailor.us-east-1.amazonaws.com/v1/manifest/04fd913bb278d8775298c26fdca9d9841f37601f/RakutenTV-eu_BeanoTV/b1f233d5-847c-437d-aa4f-f73e67a85323/2.m3u8|Referer="https://referer.xyz/"|User-Agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1"|Origin="https://origin.xyz" diff --git a/tests/__data__/expected/playlist_update/us.m3u b/tests/__data__/expected/playlist_update/us.m3u index 8f57ccc7bf..1b78e84f6c 100644 --- a/tests/__data__/expected/playlist_update/us.m3u +++ b/tests/__data__/expected/playlist_update/us.m3u @@ -1,11 +1,6 @@ #EXTM3U -#EXTINF:-1 tvg-id="BBCAmerica.us@East" http-user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36 Edge/12.246",BBC America East (720p) -#EXTVLCOPT:http-referrer=http://imn.iq -#EXTVLCOPT:http-user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 -#KODIPROP:inputstream=inputstream.adaptive -#KODIPROP:inputstream.adaptive.manifest_type=mpd -#KODIPROP:inputstream.adaptive.license_type=com.widevine.alpha -#KODIPROP:inputstream.adaptive.license_key=https://drm.ors.at/acquire-license/widevine?BrandGuid=13f2e056-53fe-4469-ba6d-999970dbe549&userToken=v9ZVSksv4S7rT55o10dmYNRa4asye3z05eWCFxD%2FFYIlTJEpuf6tF8asPcyQOFq0h5opS%2B6WoMxnshWkihpHq5qrdrBEZ69piE94J9Feh385snGOqK3PYO7tLLjxmsCAe%2B9%2BNnurSSO5RCAIRsL125nSj1eOR%2F1GSKOgGH80HK2FDLiePxPkeaAxuWzacNBB%2FqnIGGxfe3GlmN65cU9F8WEpKFDlaxW%2Fv3ZSLAp3%2BZEq1aZXJ6Oz%2Fi0diD0EybH7|Content-Type=application/octet-stream|R{SSM}| -https://xui-backend.energeek.cl/live/9/playlist.m3u8?username=ZZDemoIPTVGH&password=mdo96EuqMkTR|Referer="https://referer.xyz/"|User-Agent="Mozilla/5.0 (iPhone; CPU iPhone OS 17_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1"|Origin="https://origin.xyz" +#EXTINF:-1 tvg-id="BBCAmerica.us@East",BBC America East (720p) +#EXTVLCOPT:http-user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36 Edge/12.246 +https://servilive.com:3126/live/tele2000live.m3u8 #EXTINF:-1 tvg-id="FastTV.us@SD",Fast TV https://3fa797d5.wurl.com/manifest/f36d25e7e52f1ba8d7e56eb859c636563214f541/T05PX01vdG9yVHJlbmRGYXN0VFZfSExT/b5e5e0e2-12b3-4312-93c9-c0a7c50b41ca/4.m3u8 diff --git a/tests/__data__/input/api_generate/ad.m3u b/tests/__data__/input/playlist_export/ad.m3u similarity index 100% rename from tests/__data__/input/api_generate/ad.m3u rename to tests/__data__/input/playlist_export/ad.m3u diff --git a/tests/__data__/input/api_generate/ca.m3u b/tests/__data__/input/playlist_export/ca.m3u similarity index 100% rename from tests/__data__/input/api_generate/ca.m3u rename to tests/__data__/input/playlist_export/ca.m3u diff --git a/tests/__data__/input/api_generate/in.m3u b/tests/__data__/input/playlist_export/in.m3u similarity index 100% rename from tests/__data__/input/api_generate/in.m3u rename to tests/__data__/input/playlist_export/in.m3u diff --git a/tests/__data__/input/api_generate/uk.m3u b/tests/__data__/input/playlist_export/uk.m3u similarity index 100% rename from tests/__data__/input/api_generate/uk.m3u rename to tests/__data__/input/playlist_export/uk.m3u diff --git a/tests/__data__/input/api_generate/unsorted.m3u b/tests/__data__/input/playlist_export/unsorted.m3u similarity index 100% rename from tests/__data__/input/api_generate/unsorted.m3u rename to tests/__data__/input/playlist_export/unsorted.m3u From a27ba0c7d0c6e2a0ca97f66c1867327199e5bc45 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Thu, 4 Dec 2025 18:32:06 +0300 Subject: [PATCH 05/11] Update tests --- .../{api/generate.test.ts => playlist/export.test.ts} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename tests/commands/{api/generate.test.ts => playlist/export.test.ts} (70%) diff --git a/tests/commands/api/generate.test.ts b/tests/commands/playlist/export.test.ts similarity index 70% rename from tests/commands/api/generate.test.ts rename to tests/commands/playlist/export.test.ts index d32a2cfc27..43f4c4b9e2 100644 --- a/tests/commands/api/generate.test.ts +++ b/tests/commands/playlist/export.test.ts @@ -3,20 +3,20 @@ import { execSync } from 'child_process' import fs from 'fs-extra' const ENV_VAR = - 'cross-env DATA_DIR=tests/__data__/input/data STREAMS_DIR=tests/__data__/input/api_generate API_DIR=tests/__data__/output/.api' + 'cross-env DATA_DIR=tests/__data__/input/data STREAMS_DIR=tests/__data__/input/playlist_export API_DIR=tests/__data__/output/.api' beforeEach(() => { fs.emptyDirSync('tests/__data__/output') }) -describe('api:generate', () => { +describe('playlist:export', () => { it('can create streams.json', () => { - const cmd = `${ENV_VAR} npm run api:generate` + const cmd = `${ENV_VAR} npm run playlist:export` const stdout = execSync(cmd, { encoding: 'utf8' }) if (process.env.DEBUG === 'true') console.log(cmd, stdout) expect(content('tests/__data__/output/.api/streams.json')).toMatchObject( - content('tests/__data__/expected/api_generate/.api/streams.json') + content('tests/__data__/expected/playlist_export/.api/streams.json') ) }) }) From 20d3ecc07408217147cfa83767f5592334566d71 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Thu, 4 Dec 2025 18:32:25 +0300 Subject: [PATCH 06/11] Update scripts --- .../{api/generate.ts => playlist/export.ts} | 0 scripts/commands/playlist/test.ts | 8 +-- scripts/commands/playlist/update.ts | 25 ++++---- scripts/commands/playlist/validate.ts | 10 +-- scripts/commands/report/create.ts | 12 ++-- scripts/core/issueParser.ts | 18 +++--- scripts/core/playlistParser.ts | 4 +- scripts/generators/languagesGenerator.ts | 4 +- scripts/models/stream.ts | 64 ++++++------------- 9 files changed, 60 insertions(+), 85 deletions(-) rename scripts/commands/{api/generate.ts => playlist/export.ts} (100%) diff --git a/scripts/commands/api/generate.ts b/scripts/commands/playlist/export.ts similarity index 100% rename from scripts/commands/api/generate.ts rename to scripts/commands/playlist/export.ts diff --git a/scripts/commands/playlist/test.ts b/scripts/commands/playlist/test.ts index 737b2dbea7..9446e8c4ad 100644 --- a/scripts/commands/playlist/test.ts +++ b/scripts/commands/playlist/test.ts @@ -1,5 +1,5 @@ import { PlaylistParser, StreamTester, CliTable } from '../../core' -import type { TestResult } from '../../core/streamTester' +import type { StreamTesterResult } from '../../core/streamTester' import { ROOT_DIR, STREAMS_DIR } from '../../constants' import { Logger, Collection } from '@freearhey/core' import { program, OptionValues } from 'commander' @@ -92,10 +92,10 @@ async function runTest(stream: Stream) { const key = stream.getUniqKey() results[key] = chalk.white('LOADING...') - const result: TestResult = await tester.test(stream) + const result: StreamTesterResult = await tester.test(stream) let status = '' - const errorStatusCodes = ['ENOTFOUND', 'HTTP_404_NOT_FOUND'] + const errorStatusCodes = ['ENOTFOUND', 'HTTP_404_NOT_FOUND', 'HTTP_404_UNKONWN_ERROR'] if (result.status.ok) status = chalk.green('OK') else if (errorStatusCodes.includes(result.status.code)) { status = chalk.red(result.status.code) @@ -144,7 +144,7 @@ function drawTable() { } } -function onFinish(error: Error) { +function onFinish(error: Error | null | undefined) { clearInterval(interval) if (error) { diff --git a/scripts/commands/playlist/update.ts b/scripts/commands/playlist/update.ts index 600b23ac34..1c5f17ee33 100644 --- a/scripts/commands/playlist/update.ts +++ b/scripts/commands/playlist/update.ts @@ -71,9 +71,9 @@ async function removeStreams({ requests.forEach((issue: Issue) => { const data = issue.data - if (data.missing('streamUrl')) return + if (data.missing('stream_url')) return - const streamUrls = data.getString('streamUrl') || '' + const streamUrls = data.getString('stream_url') || '' let changed = false streamUrls @@ -104,14 +104,14 @@ async function editStreams({ requests.forEach((issue: Issue) => { const data = issue.data - if (data.missing('streamUrl')) return + if (data.missing('stream_url')) return const stream: Stream = streams.first( - (_stream: Stream) => _stream.url === data.getString('streamUrl') + (_stream: Stream) => _stream.url === data.getString('stream_url') ) if (!stream) return - const streamId = data.getString('streamId') || '' + const streamId = data.getString('stream_id') || '' const [channelId, feedId] = streamId.split('@') if (channelId) { @@ -138,12 +138,12 @@ async function addStreams({ ) requests.forEach((issue: Issue) => { const data = issue.data - if (data.missing('streamId') || data.missing('streamUrl')) return - if (streams.includes((_stream: Stream) => _stream.url === data.getString('streamUrl'))) return - const streamUrl = data.getString('streamUrl') || '' + if (data.missing('stream_id') || data.missing('stream_url')) return + if (streams.includes((_stream: Stream) => _stream.url === data.getString('stream_url'))) return + const streamUrl = data.getString('stream_url') || '' if (!isURI(streamUrl)) return - const streamId = data.getString('streamId') || '' + const streamId = data.getString('stream_id') || '' const [channelId, feedId] = streamId.split('@') const channel: sdk.Models.Channel | undefined = apiData.channelsKeyById.get(channelId) @@ -151,9 +151,8 @@ async function addStreams({ const label = data.getString('label') || '' const quality = data.getString('quality') || null - const httpUserAgent = data.getString('httpUserAgent') || null - const httpReferrer = data.getString('httpReferrer') || null - const directives = data.getArray('directives') || [] + const httpUserAgent = data.getString('http_user_agent') || null + const httpReferrer = data.getString('http_referrer') || null const stream = new Stream({ channel: channelId, @@ -166,7 +165,7 @@ async function addStreams({ }) stream.label = label - stream.setDirectives(directives).updateTitle().updateFilepath() + stream.updateTitle().updateFilepath() streams.add(stream) processedIssues.add(issue.number) diff --git a/scripts/commands/playlist/validate.ts b/scripts/commands/playlist/validate.ts index 36ec037e23..c05964f562 100644 --- a/scripts/commands/playlist/validate.ts +++ b/scripts/commands/playlist/validate.ts @@ -31,8 +31,8 @@ async function main() { const streams = await parser.parse(files) logger.info(`found ${streams.count()} streams`) - let errors = new Collection() - let warnings = new Collection() + const errors = new Collection() + const warnings = new Collection() const streamsGroupedByFilepath = streams.groupBy((stream: Stream) => stream.getFilepath()) for (const filepath of streamsGroupedByFilepath.keys()) { const streams = streamsGroupedByFilepath.get(filepath) @@ -97,8 +97,10 @@ async function main() { console.log(` ${chalk.gray(position)}${status}${logItem.message}`) }) - errors = errors.concat(log.filter((logItem: LogItem) => logItem.type === 'error')) - warnings = warnings.concat(log.filter((logItem: LogItem) => logItem.type === 'warning')) + log.forEach((logItem: LogItem) => { + if (logItem.type === 'error') errors.add(logItem) + else if (logItem.type === 'warning') warnings.add(logItem) + }) } } diff --git a/scripts/commands/report/create.ts b/scripts/commands/report/create.ts index 52986637fd..93488471d3 100644 --- a/scripts/commands/report/create.ts +++ b/scripts/commands/report/create.ts @@ -47,7 +47,7 @@ async function main() { issue.labels.find((label: string) => label === 'streams:remove') ) removeRequests.forEach((issue: Issue) => { - const streamUrls = issue.data.getArray('streamUrl') || [] + const streamUrls = issue.data.getArray('stream_url') || [] if (!streamUrls.length) { const result = { @@ -82,8 +82,8 @@ async function main() { const addRequests = issues.filter(issue => issue.labels.includes('streams:add')) const addRequestsBuffer = new Dictionary() addRequests.forEach((issue: Issue) => { - const streamId = issue.data.getString('streamId') || '' - const streamUrl = issue.data.getString('streamUrl') || '' + const streamId = issue.data.getString('stream_id') || '' + const streamUrl = issue.data.getString('stream_url') || '' const [channelId] = streamId.split('@') const result = { @@ -114,8 +114,8 @@ async function main() { issue.labels.find((label: string) => label === 'streams:edit') ) editRequests.forEach((issue: Issue) => { - const streamId = issue.data.getString('streamId') || '' - const streamUrl = issue.data.getString('streamUrl') || '' + const streamId = issue.data.getString('stream_id') || '' + const streamUrl = issue.data.getString('stream_url') || '' const [channelId] = streamId.split('@') const result = { @@ -140,7 +140,7 @@ async function main() { ) const channelSearchRequestsBuffer = new Dictionary() channelSearchRequests.forEach((issue: Issue) => { - const streamId = issue.data.getString('streamId') || issue.data.getString('channelId') || '' + const streamId = issue.data.getString('stream_id') || issue.data.getString('channel_id') || '' const [channelId, feedId] = streamId.split('@') const result = { diff --git a/scripts/core/issueParser.ts b/scripts/core/issueParser.ts index ad488e6c5d..54ebd694b1 100644 --- a/scripts/core/issueParser.ts +++ b/scripts/core/issueParser.ts @@ -3,20 +3,18 @@ import { IssueData } from './issueData' import { Issue } from '../models' const FIELDS = new Dictionary({ - 'Stream ID': 'streamId', - 'Channel ID': 'channelId', - 'Feed ID': 'feedId', - 'Stream URL': 'streamUrl', - 'New Stream URL': 'newStreamUrl', + 'Stream ID': 'stream_id', + 'Channel ID': 'channel_id', + 'Feed ID': 'feed_id', + 'Stream URL': 'stream_url', Label: 'label', Quality: 'quality', - 'HTTP User-Agent': 'httpUserAgent', - 'HTTP User Agent': 'httpUserAgent', - 'HTTP Referrer': 'httpReferrer', + 'HTTP User-Agent': 'http_user_agent', + 'HTTP User Agent': 'http_user_agent', + 'HTTP Referrer': 'http_referrer', 'What happened to the stream?': 'reason', Reason: 'reason', - Notes: 'notes', - Directives: 'directives' + Notes: 'notes' }) export class IssueParser { diff --git a/scripts/core/playlistParser.ts b/scripts/core/playlistParser.ts index 08b2542ff9..e1d4a84562 100644 --- a/scripts/core/playlistParser.ts +++ b/scripts/core/playlistParser.ts @@ -20,7 +20,9 @@ export class PlaylistParser { for (const filepath of files) { if (!this.storage.existsSync(filepath)) continue const _parsed: Collection = await this.parseFile(filepath) - parsed.concat(_parsed) + _parsed.forEach((item: Stream) => { + parsed.add(item) + }) } return parsed diff --git a/scripts/generators/languagesGenerator.ts b/scripts/generators/languagesGenerator.ts index 28fce5241a..3e7de3f0d7 100644 --- a/scripts/generators/languagesGenerator.ts +++ b/scripts/generators/languagesGenerator.ts @@ -25,7 +25,9 @@ export class LanguagesGenerator implements Generator { const languages = new Collection() streams.forEach((stream: Stream) => { - languages.concat(stream.getLanguages()) + stream.getLanguages().forEach((language: sdk.Models.Language) => { + languages.add(language) + }) }) languages diff --git a/scripts/models/stream.ts b/scripts/models/stream.ts index c050930b4d..002a6cc2e1 100644 --- a/scripts/models/stream.ts +++ b/scripts/models/stream.ts @@ -7,7 +7,6 @@ import { data } from '../api' import path from 'node:path' export class Stream extends sdk.Models.Stream { - directives: Collection filepath?: string line?: number groupTitle: string = 'Undefined' @@ -19,18 +18,14 @@ export class Stream extends sdk.Models.Stream { const data = { label: issueData.getString('label'), quality: issueData.getString('quality'), - httpUserAgent: issueData.getString('httpUserAgent'), - httpReferrer: issueData.getString('httpReferrer'), - newStreamUrl: issueData.getString('newStreamUrl'), - directives: issueData.getArray('directives') + httpUserAgent: issueData.getString('http_user_agent'), + httpReferrer: issueData.getString('http_referrer') } if (data.label !== undefined) this.label = data.label if (data.quality !== undefined) this.quality = data.quality if (data.httpUserAgent !== undefined) this.user_agent = data.httpUserAgent if (data.httpReferrer !== undefined) this.referrer = data.httpReferrer - if (data.newStreamUrl !== undefined) this.url = data.newStreamUrl - if (data.directives !== undefined) this.setDirectives(data.directives) return this } @@ -54,24 +49,6 @@ export class Stream extends sdk.Models.Stream { return { title, label, quality } } - function parseDirectives(string: string): Collection { - const directives = new Collection() - - if (!string) return directives - - const supportedDirectives = ['#EXTVLCOPT', '#KODIPROP'] - const lines = string.split('\r\n') - const regex = new RegExp(`^${supportedDirectives.join('|')}`, 'i') - - lines.forEach((line: string) => { - if (regex.test(line)) { - directives.add(line.trim()) - } - }) - - return directives - } - if (!data.name) throw new Error('"name" property is required') if (!data.url) throw new Error('"url" property is required') @@ -91,7 +68,6 @@ export class Stream extends sdk.Models.Stream { stream.tvgId = data.tvg.id stream.line = data.line stream.label = label || null - stream.directives = parseDirectives(data.raw) return stream } @@ -235,7 +211,9 @@ export class Stream extends sdk.Models.Stream { .intersects(new Collection(region.countries)) .isNotEmpty() ) - regions.concat(relatedRegions) + relatedRegions.forEach(region => { + regions.add(region) + }) break } case 'country': { @@ -246,7 +224,9 @@ export class Stream extends sdk.Models.Stream { (code: string) => code === country.code ) ) - regions.concat(countryRegions) + countryRegions.forEach(region => { + regions.add(region) + }) break } case 'subdivision': { @@ -257,7 +237,9 @@ export class Stream extends sdk.Models.Stream { (code: string) => code === subdivision.country ) ) - regions.concat(subdivisionRegions) + subdivisionRegions.forEach(region => { + regions.add(region) + }) break } case 'city': { @@ -268,7 +250,9 @@ export class Stream extends sdk.Models.Stream { (code: string) => code === city.country ) ) - regions.concat(cityRegions) + cityRegions.forEach(region => { + regions.add(region) + }) break } } @@ -306,14 +290,6 @@ export class Stream extends sdk.Models.Stream { return !!found } - setDirectives(directives: string[]): this { - this.directives = new Collection(directives).filter((directive: string) => - /^(#KODIPROP|#EXTVLCOPT)/.test(directive) - ) - - return this - } - updateTvgId(): this { if (!this.channel) return this if (this.feed) { @@ -418,20 +394,16 @@ export class Stream extends sdk.Models.Stream { output += ` tvg-logo="${this.getTvgLogo()}" group-title="${this.groupTitle}"` } + output += `,${this.getFullTitle()}` + if (this.referrer) { - output += ` http-referrer="${this.referrer}"` + output += `\r\n#EXTVLCOPT:http-referrer=${this.referrer}` } if (this.user_agent) { - output += ` http-user-agent="${this.user_agent}"` + output += `\r\n#EXTVLCOPT:http-user-agent=${this.user_agent}` } - output += `,${this.getFullTitle()}` - - this.directives.forEach((prop: string) => { - output += `\r\n${prop}` - }) - output += `\r\n${this.url}` return output From 26c2b1a90a4808adeba6944422ed2e53da5efde1 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Thu, 4 Dec 2025 18:32:46 +0300 Subject: [PATCH 07/11] Update issue templates --- .github/ISSUE_TEMPLATE/1_streams_add.yml | 7 ------- .github/ISSUE_TEMPLATE/2_streams_edit.yml | 14 -------------- 2 files changed, 21 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/1_streams_add.yml b/.github/ISSUE_TEMPLATE/1_streams_add.yml index 6c54d31493..5e70e2667b 100644 --- a/.github/ISSUE_TEMPLATE/1_streams_add.yml +++ b/.github/ISSUE_TEMPLATE/1_streams_add.yml @@ -60,13 +60,6 @@ body: label: HTTP Referrer placeholder: 'https://example.com/' - - type: textarea - id: directives - attributes: - label: Directives - description: 'List of directives telling players how to play the stream. Supported `#KODIPROP` and `#EXTVLCOPT`.' - placeholder: '#KODIPROP:inputstream=inputstream.adaptive' - - type: textarea id: notes attributes: diff --git a/.github/ISSUE_TEMPLATE/2_streams_edit.yml b/.github/ISSUE_TEMPLATE/2_streams_edit.yml index 7cd0b8a9f0..88905f24b7 100644 --- a/.github/ISSUE_TEMPLATE/2_streams_edit.yml +++ b/.github/ISSUE_TEMPLATE/2_streams_edit.yml @@ -18,13 +18,6 @@ body: value: | What exactly needs to be changed? To delete an existing value without replacement use the `~` symbol. - - type: input - id: new_stream_url - attributes: - label: New Stream URL - description: New link to the stream - placeholder: 'https://servilive.com:3126/live/tele2000live.m3u8' - - type: input id: stream_id attributes: @@ -72,13 +65,6 @@ body: label: HTTP Referrer placeholder: 'https://example.com/' - - type: textarea - id: directives - attributes: - label: Directives - description: 'List of directives telling players how to play the stream. Supported `#KODIPROP` and `#EXTVLCOPT`.' - placeholder: '#KODIPROP:inputstream=inputstream.adaptive' - - type: textarea id: notes attributes: From c11c54f0bf759779fec4b4549d3c36002ffcda21 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Thu, 4 Dec 2025 18:32:59 +0300 Subject: [PATCH 08/11] Update workflows --- .github/workflows/check.yml | 10 +--------- .github/workflows/format.yml | 9 +-------- .github/workflows/update.yml | 11 ++--------- 3 files changed, 4 insertions(+), 26 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 9e2463d2a3..22b6d2368e 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -6,8 +6,6 @@ on: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true -permissions: - packages: read jobs: check: runs-on: ubuntu-latest @@ -18,7 +16,7 @@ jobs: run: | git fetch origin master:master ANY_CHANGED=false - ALL_CHANGED_FILES=$(git diff --name-only master -- streams/ | tr '\n' ' ') + ALL_CHANGED_FILES=$(git diff --diff-filter=ACMRT --name-only master -- streams/ | tr '\n' ' ') if [ -n "${ALL_CHANGED_FILES}" ]; then ANY_CHANGED=true fi @@ -29,12 +27,6 @@ jobs: with: node-version: 22 cache: 'npm' - - name: Setup .npmrc for GitHub Packages - if: steps.files.outputs.any_changed == 'true' - run: | - echo "//npm.pkg.github.com/:_authToken=${{ secrets.GITHUB_TOKEN }}" >> .npmrc - echo "@iptv-org:registry=https://npm.pkg.github.com/" >> .npmrc - echo "always-auth=true" >> .npmrc - name: Install dependencies if: steps.files.outputs.any_changed == 'true' run: npm install diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index cbd85a76cf..c6ecfbde8d 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -3,8 +3,6 @@ on: workflow_dispatch: # schedule: # - cron: "0 12 * * *" -permissions: - packages: read jobs: main: runs-on: ubuntu-latest @@ -24,11 +22,6 @@ jobs: with: node-version: 22 cache: 'npm' - - name: Setup .npmrc for GitHub Packages - run: | - echo "//npm.pkg.github.com/:_authToken=${{ secrets.GITHUB_TOKEN }}" >> .npmrc - echo "@iptv-org:registry=https://npm.pkg.github.com/" >> .npmrc - echo "always-auth=true" >> .npmrc - name: Install dependencies run: npm install - name: Format internal playlists @@ -41,7 +34,7 @@ jobs: id: files_after run: | ANY_CHANGED=false - ALL_CHANGED_FILES=$(git diff --name-only master -- streams/ | tr '\n' ' ') + ALL_CHANGED_FILES=$(git diff --diff-filter=ACMRT --name-only master -- streams/ | tr '\n' ' ') if [ -n "${ALL_CHANGED_FILES}" ]; then ANY_CHANGED=true fi diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml index 48f7e2903d..580b75d1ef 100644 --- a/.github/workflows/update.yml +++ b/.github/workflows/update.yml @@ -3,8 +3,6 @@ on: workflow_dispatch: schedule: - cron: '0 0 * * *' -permissions: - packages: read jobs: main: runs-on: ubuntu-latest @@ -24,11 +22,6 @@ jobs: with: node-version: 22 cache: 'npm' - - name: Setup .npmrc for GitHub Packages - run: | - echo "//npm.pkg.github.com/:_authToken=${{ secrets.GITHUB_TOKEN }}" >> .npmrc - echo "@iptv-org:registry=https://npm.pkg.github.com/" >> .npmrc - echo "always-auth=true" >> .npmrc - name: Install dependencies run: npm install - name: Update internal playlists @@ -40,8 +33,8 @@ jobs: npm run playlist:validate - name: Generate public playlists run: npm run playlist:generate - - name: Generate .api/streams.json - run: npm run api:generate + - name: Create .api/streams.json + run: npm run playlist:export - name: Update readme run: npm run readme:update - name: Setup git From 79855b267d2d4689fe823766921a4f9e5ff0352c Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Thu, 4 Dec 2025 18:38:56 +0300 Subject: [PATCH 09/11] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 24bb67ca03..50ece87cbe 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Links to other useful IPTV-related resources can be found in the [iptv-org/aweso ## Discussions -If you need help finding a channel, have a question or idea, welcome to the [Discussions](https://github.com/orgs/iptv-org/discussions). +If you have a question or idea, welcome to the [Discussions](https://github.com/orgs/iptv-org/discussions). ## FAQ From c0157e6c9f846261858718477876fc63593815ae Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Thu, 4 Dec 2025 18:38:59 +0300 Subject: [PATCH 10/11] Update package.json --- package.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/package.json b/package.json index 7453c15715..a3104adb14 100644 --- a/package.json +++ b/package.json @@ -15,9 +15,6 @@ "playlist:export": "tsx scripts/commands/playlist/export.ts", "readme:update": "tsx scripts/commands/readme/update.ts", "report:create": "tsx scripts/commands/report/create.ts", - "check": "npm run playlist:lint && npm run playlist:validate", - "format": "npm run playlist:format", - "update": "npm run playlist:generate && npm run api:generate && npm run readme:update", "lint": "npx eslint \"scripts/**/*.{ts,js}\" \"tests/**/*.{ts,js}\"", "test": "jest --runInBand", "postinstall": "npm run api:load" From d6ba152182a61b495b42175f278878903b6833cd Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Thu, 4 Dec 2025 18:39:05 +0300 Subject: [PATCH 11/11] Update CONTRIBUTING.md --- CONTRIBUTING.md | 35 ++++++----------------------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 701e41e4d1..6f5ec2cee4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,9 +20,10 @@ Regardless of which option you choose, before posting your request please do the - Make sure the link you want to add works stably. To check this, open it in one of the players (for example, [VLC player](https://www.videolan.org/vlc/index.html)) and watch the broadcast for at least a minute (some test streams are interrupted after 15-30 seconds). - Make sure the link is not already in the playlist. This can be done by [searching](https://github.com/search?q=repo%3Aiptv-org%2Fiptv+http%3A%2F%2Fexample.com&type=code) the repository. +- Make sure the link does not lead to the Xtream Codes server. [Why don't you accept links to Xtream Codes server?](FAQ.md#why-dont-you-accept-links-to-xtream-codes-server) +- Make sure that the link leads directly to the broadcast, without unnecessary redirects. - Find the ID of the channel you want on [iptv-org.github.io](https://iptv-org.github.io/). If your desired channel is not on the list you can leave a request to add it [here](https://github.com/iptv-org/database/issues/new/choose). - Make sure the channel is not blocklisted. It can also be done through [iptv-org.github.io](https://iptv-org.github.io/). -- The link does not lead to the Xtream Codes server. [Why don't you accept links to Xtream Codes server?](FAQ.md#why-dont-you-accept-links-to-xtream-codes-server) - If you know that the broadcast only works in certain countries or it is periodically interrupted, do not forget to indicate this in the request. A requests without a valid stream ID or working link to the stream will be closed immediately. @@ -129,33 +130,15 @@ Example: https://example.com/playlist.m3u8 ``` -Also, if necessary, you can specify custom [HTTP User-Agent](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent) and [HTTP Referrer](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer) through additional attributes: +Also, if necessary, you can specify custom [HTTP User-Agent](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent) and [HTTP Referrer](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer) through `#EXTVLCOPT` directive: ```xml -#EXTINF:-1 tvg-id="ExampleTV.us" http-referrer="http://example.com/" http-user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64)",Example TV -http://example.com/stream.m3u8 -``` - -or use player-specific directives: - -_VLC_ - -```xml -#EXTINF:-1 tvg-id="ExampleTV.us@VLC",Example TV +#EXTINF:-1 tvg-id="ExampleTV.us",Example TV #EXTVLCOPT:http-referrer=http://example.com/ #EXTVLCOPT:http-user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) http://example.com/stream.m3u8 ``` -_Kodi_ - -```xml -#EXTINF:-1 tvg-id="ExampleTV.us@Kodi",Example TV -#KODIPROP:inputstream=inputstream.adaptive -#KODIPROP:inputstream.adaptive.stream_headers=Referer=http://example.com/&User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) -http://example.com/stream.m3u8 -``` - ## Project Structure - `.github/` @@ -185,8 +168,6 @@ To run scripts use the `npm run ` command. - `act:format`: allows to test the [format](https://github.com/iptv-org/iptv/blob/master/.github/workflows/update.yml) workflow locally. Depends on [nektos/gh-act](https://github.com/nektos/gh-act). - `act:update`: allows to test the [update](https://github.com/iptv-org/iptv/blob/master/.github/workflows/update.yml) workflow locally. Depends on [nektos/gh-act](https://github.com/nektos/gh-act). - `api:load`: downloads the latest channel and stream data from the [iptv-org/api](https://github.com/iptv-org/api). -- `api:generate`: generates a JSON file with all streams for the [iptv-org/api](https://github.com/iptv-org/api) repository. -- `api:deploy`: allows to manually upload a JSON file created via `api:generate` to the [iptv-org/api](https://github.com/iptv-org/api) repository. To run the script you must provide your [personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) with write access to the repository. - `playlist:format`: formats internal playlists. The process includes [URL normalization](https://en.wikipedia.org/wiki/URI_normalization), duplicate removal, removing invalid id's and sorting links by channel name, quality, and label. - `playlist:update`: triggers an update of internal playlists. The process involves processing approved requests from issues. - `playlist:generate`: generates all public playlists. @@ -194,13 +175,9 @@ To run scripts use the `npm run ` command. - `playlist:lint`: сhecks internal playlists for syntax errors. - `playlist:test`: tests links in internal playlists. - `playlist:edit`: utility for quick streams mapping. -- `playlist:deploy`: allows to manually publish all generated via `playlist:generate` playlists. To run the script you must provide your [personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) with write access to the repository. +- `playlist:export`: creates a JSON file with all streams for the [iptv-org/api](https://github.com/iptv-org/api) repository. - `readme:update`: updates the list of playlists in [README.md](README.md). - `report:create`: creates a report on current issues. -- `check`: (shorthand) sequentially runs the `playlist:lint` and `playlist:validate` scripts. -- `format`: (shorthand) runs the `playlist:format` script. -- `update`: (shorthand) sequentially runs the `playlist:generate`, `api:generate` and `readme:update` scripts. -- `deploy`: (shorthand) sequentially runs the `playlist:deploy` and `api:deploy` scripts. - `lint`: сhecks the scripts for syntax errors. - `test`: runs a test of all the scripts described above. @@ -212,4 +189,4 @@ Each workflow includes its own set of scripts that can be run either manually or - `check`: sequentially runs the `api:load`, `playlist:check` and `playlist:validate` scripts when a new pull request appears, and blocks the merge if it detects an error in it. - `format`: sequentially runs `api:load`, `playlist:format`, `playlist:lint` and `playlist:validate` scripts. -- `update`: every day at 0:00 UTC sequentially runs `api:load`, `playlist:update`, `playlist:lint`, `playlist:validate`, `playlist:generate`, `api:generate` and `readme:update` scripts and deploys the output files if successful. +- `update`: every day at 0:00 UTC sequentially runs `api:load`, `playlist:update`, `playlist:lint`, `playlist:validate`, `playlist:generate`, `playlist:export` and `readme:update` scripts and deploys the output files if successful.