From 90d065d22340c2e738a0b4d4f95c47648e5f4f68 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 16 Apr 2025 20:19:05 +0300 Subject: [PATCH 01/24] Update dependencies --- package-lock.json | 532 +++++++++++++++++++++++++++++++++++++++++----- package.json | 5 +- yarn.lock | 189 ++++++++++++++-- 3 files changed, 652 insertions(+), 74 deletions(-) diff --git a/package-lock.json b/package-lock.json index a9d29de735..4df46535d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,8 @@ "@eslint/eslintrc": "^3.3.0", "@eslint/js": "^9.21.0", "@freearhey/core": "^0.7.0", + "@freearhey/search-js": "^0.1.2", + "@inquirer/prompts": "^7.4.1", "@octokit/core": "^6.1.4", "@octokit/plugin-paginate-rest": "^11.4.3", "@octokit/plugin-rest-endpoint-methods": "^7.1.3", @@ -36,8 +38,8 @@ "lodash": "^4.17.21", "m3u-linter": "^0.4.2", "markdown-include": "^0.4.3", + "node-cleanup": "^2.1.2", "numeral": "^2.0.6", - "transliteration": "^2.3.5", "ts-jest": "^29.2.5", "tsx": "^4.6.2", "valid-url": "^1.0.9" @@ -1093,6 +1095,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@freearhey/search-js": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@freearhey/search-js/-/search-js-0.1.2.tgz", + "integrity": "sha512-F2o+xpGCXOK4OsZfKEHfXNNkAZmny2eBnPOp+P0iyV20ja7gJGfTFaEc6okcuEo6OB6P7LnSxTvISkoArFtlfg==", + "dependencies": { + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1149,13 +1162,36 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@inquirer/confirm": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.7.tgz", - "integrity": "sha512-Xrfbrw9eSiHb+GsesO8TQIeHSMTP0xyvTCeeYevgZ4sKW+iz9w/47bgfG9b0niQm+xaLY2EWPBINUPldLwvYiw==", + "node_modules/@inquirer/checkbox": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.5.tgz", + "integrity": "sha512-swPczVU+at65xa5uPfNP9u3qx/alNwiaykiI/ExpsmMSQW55trmZcwhYWzw/7fj+n6Q8z1eENvR7vFfq9oPSAQ==", "dependencies": { - "@inquirer/core": "^10.1.8", - "@inquirer/type": "^3.0.5" + "@inquirer/core": "^10.1.10", + "@inquirer/figures": "^1.0.11", + "@inquirer/type": "^3.0.6", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.9.tgz", + "integrity": "sha512-NgQCnHqFTjF7Ys2fsqK2WtnA8X1kHyInyG+nMIuHowVTIgIuS10T4AznI/PvbqSpJqjCUqNBlKGh1v3bwLFL4w==", + "dependencies": { + "@inquirer/core": "^10.1.10", + "@inquirer/type": "^3.0.6" }, "engines": { "node": ">=18" @@ -1170,12 +1206,12 @@ } }, "node_modules/@inquirer/core": { - "version": "10.1.8", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.8.tgz", - "integrity": "sha512-HpAqR8y715zPpM9e/9Q+N88bnGwqqL8ePgZ0SMv/s3673JLMv3bIkoivGmjPqXlEgisUksSXibweQccUwEx4qQ==", + "version": "10.1.10", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.10.tgz", + "integrity": "sha512-roDaKeY1PYY0aCqhRmXihrHjoSW2A00pV3Ke5fTpMCkzcGF64R8e0lw3dK+eLEHwS4vB5RnW1wuQmvzoRul8Mw==", "dependencies": { "@inquirer/figures": "^1.0.11", - "@inquirer/type": "^3.0.5", + "@inquirer/type": "^3.0.6", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", @@ -1219,6 +1255,48 @@ "node": ">=8" } }, + "node_modules/@inquirer/editor": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.10.tgz", + "integrity": "sha512-5GVWJ+qeI6BzR6TIInLP9SXhWCEcvgFQYmcRG6d6RIlhFjM5TyG18paTGBgRYyEouvCmzeco47x9zX9tQEofkw==", + "dependencies": { + "@inquirer/core": "^10.1.10", + "@inquirer/type": "^3.0.6", + "external-editor": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.12.tgz", + "integrity": "sha512-jV8QoZE1fC0vPe6TnsOfig+qwu7Iza1pkXoUJ3SroRagrt2hxiL+RbM432YAihNR7m7XnU0HWl/WQ35RIGmXHw==", + "dependencies": { + "@inquirer/core": "^10.1.10", + "@inquirer/type": "^3.0.6", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, "node_modules/@inquirer/figures": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.11.tgz", @@ -1227,10 +1305,165 @@ "node": ">=18" } }, + "node_modules/@inquirer/input": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.9.tgz", + "integrity": "sha512-mshNG24Ij5KqsQtOZMgj5TwEjIf+F2HOESk6bjMwGWgcH5UBe8UoljwzNFHqdMbGYbgAf6v2wU/X9CAdKJzgOA==", + "dependencies": { + "@inquirer/core": "^10.1.10", + "@inquirer/type": "^3.0.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.12.tgz", + "integrity": "sha512-7HRFHxbPCA4e4jMxTQglHJwP+v/kpFsCf2szzfBHy98Wlc3L08HL76UDiA87TOdX5fwj2HMOLWqRWv9Pnn+Z5Q==", + "dependencies": { + "@inquirer/core": "^10.1.10", + "@inquirer/type": "^3.0.6" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.12.tgz", + "integrity": "sha512-FlOB0zvuELPEbnBYiPaOdJIaDzb2PmJ7ghi/SVwIHDDSQ2K4opGBkF+5kXOg6ucrtSUQdLhVVY5tycH0j0l+0g==", + "dependencies": { + "@inquirer/core": "^10.1.10", + "@inquirer/type": "^3.0.6", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.4.1.tgz", + "integrity": "sha512-UlmM5FVOZF0gpoe1PT/jN4vk8JmpIWBlMvTL8M+hlvPmzN89K6z03+IFmyeu/oFCenwdwHDr2gky7nIGSEVvlA==", + "dependencies": { + "@inquirer/checkbox": "^4.1.5", + "@inquirer/confirm": "^5.1.9", + "@inquirer/editor": "^4.2.10", + "@inquirer/expand": "^4.0.12", + "@inquirer/input": "^4.1.9", + "@inquirer/number": "^3.0.12", + "@inquirer/password": "^4.0.12", + "@inquirer/rawlist": "^4.0.12", + "@inquirer/search": "^3.0.12", + "@inquirer/select": "^4.1.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.0.12.tgz", + "integrity": "sha512-wNPJZy8Oc7RyGISPxp9/MpTOqX8lr0r+lCCWm7hQra+MDtYRgINv1hxw7R+vKP71Bu/3LszabxOodfV/uTfsaA==", + "dependencies": { + "@inquirer/core": "^10.1.10", + "@inquirer/type": "^3.0.6", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.12.tgz", + "integrity": "sha512-H/kDJA3kNlnNIjB8YsaXoQI0Qccgf0Na14K1h8ExWhNmUg2E941dyFPrZeugihEa9AZNW5NdsD/NcvUME83OPQ==", + "dependencies": { + "@inquirer/core": "^10.1.10", + "@inquirer/figures": "^1.0.11", + "@inquirer/type": "^3.0.6", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.1.1.tgz", + "integrity": "sha512-IUXzzTKVdiVNMA+2yUvPxWsSgOG4kfX93jOM4Zb5FgujeInotv5SPIJVeXQ+fO4xu7tW8VowFhdG5JRmmCyQ1Q==", + "dependencies": { + "@inquirer/core": "^10.1.10", + "@inquirer/figures": "^1.0.11", + "@inquirer/type": "^3.0.6", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, "node_modules/@inquirer/type": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.5.tgz", - "integrity": "sha512-ZJpeIYYueOz/i/ONzrfof8g89kNdO2hjGuvULROo3O8rlB2CRtSseE5KeirnyE4t/thAn/EwvS/vuQeJCn+NZg==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.6.tgz", + "integrity": "sha512-/mKVCtVpyBu3IDarv0G+59KC4stsD5mDsGpYh+GKs1NZT88Jh52+cuoA1AtLk2Q0r/quNl+1cSUyLRHBFeD0XA==", "engines": { "node": ">=18" }, @@ -2862,6 +3095,11 @@ "node": ">=10" } }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -3447,6 +3685,19 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/fast-content-type-parse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", @@ -3948,6 +4199,17 @@ "node": ">=10.17.0" } }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -5409,6 +5671,11 @@ "node": ">=18" } }, + "node_modules/node-cleanup": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", + "integrity": "sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==" + }, "node_modules/node-gzip": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/node-gzip/-/node-gzip-1.1.2.tgz", @@ -5514,6 +5781,14 @@ "node": ">= 0.8.0" } }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/outvariant": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", @@ -6069,6 +6344,11 @@ "run-script-os": "index.js" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", @@ -6443,6 +6723,17 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -6474,21 +6765,6 @@ "node": ">=6" } }, - "node_modules/transliteration": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/transliteration/-/transliteration-2.3.5.tgz", - "integrity": "sha512-HAGI4Lq4Q9dZ3Utu2phaWgtm3vB6PkLUFqWAScg/UW+1eZ/Tg6Exo4oC0/3VUol/w4BlefLhUUSVBr/9/ZGQOw==", - "dependencies": { - "yargs": "^17.5.1" - }, - "bin": { - "slugify": "dist/bin/slugify", - "transliterate": "dist/bin/transliterate" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/ts-api-utils": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", @@ -7491,6 +7767,14 @@ } } }, + "@freearhey/search-js": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@freearhey/search-js/-/search-js-0.1.2.tgz", + "integrity": "sha512-F2o+xpGCXOK4OsZfKEHfXNNkAZmny2eBnPOp+P0iyV20ja7gJGfTFaEc6okcuEo6OB6P7LnSxTvISkoArFtlfg==", + "requires": { + "lodash": "^4.17.21" + } + }, "@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -7522,22 +7806,34 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==" }, - "@inquirer/confirm": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.7.tgz", - "integrity": "sha512-Xrfbrw9eSiHb+GsesO8TQIeHSMTP0xyvTCeeYevgZ4sKW+iz9w/47bgfG9b0niQm+xaLY2EWPBINUPldLwvYiw==", + "@inquirer/checkbox": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.5.tgz", + "integrity": "sha512-swPczVU+at65xa5uPfNP9u3qx/alNwiaykiI/ExpsmMSQW55trmZcwhYWzw/7fj+n6Q8z1eENvR7vFfq9oPSAQ==", "requires": { - "@inquirer/core": "^10.1.8", - "@inquirer/type": "^3.0.5" + "@inquirer/core": "^10.1.10", + "@inquirer/figures": "^1.0.11", + "@inquirer/type": "^3.0.6", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + } + }, + "@inquirer/confirm": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.9.tgz", + "integrity": "sha512-NgQCnHqFTjF7Ys2fsqK2WtnA8X1kHyInyG+nMIuHowVTIgIuS10T4AznI/PvbqSpJqjCUqNBlKGh1v3bwLFL4w==", + "requires": { + "@inquirer/core": "^10.1.10", + "@inquirer/type": "^3.0.6" } }, "@inquirer/core": { - "version": "10.1.8", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.8.tgz", - "integrity": "sha512-HpAqR8y715zPpM9e/9Q+N88bnGwqqL8ePgZ0SMv/s3673JLMv3bIkoivGmjPqXlEgisUksSXibweQccUwEx4qQ==", + "version": "10.1.10", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.10.tgz", + "integrity": "sha512-roDaKeY1PYY0aCqhRmXihrHjoSW2A00pV3Ke5fTpMCkzcGF64R8e0lw3dK+eLEHwS4vB5RnW1wuQmvzoRul8Mw==", "requires": { "@inquirer/figures": "^1.0.11", - "@inquirer/type": "^3.0.5", + "@inquirer/type": "^3.0.6", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", @@ -7563,15 +7859,113 @@ } } }, + "@inquirer/editor": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.10.tgz", + "integrity": "sha512-5GVWJ+qeI6BzR6TIInLP9SXhWCEcvgFQYmcRG6d6RIlhFjM5TyG18paTGBgRYyEouvCmzeco47x9zX9tQEofkw==", + "requires": { + "@inquirer/core": "^10.1.10", + "@inquirer/type": "^3.0.6", + "external-editor": "^3.1.0" + } + }, + "@inquirer/expand": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.12.tgz", + "integrity": "sha512-jV8QoZE1fC0vPe6TnsOfig+qwu7Iza1pkXoUJ3SroRagrt2hxiL+RbM432YAihNR7m7XnU0HWl/WQ35RIGmXHw==", + "requires": { + "@inquirer/core": "^10.1.10", + "@inquirer/type": "^3.0.6", + "yoctocolors-cjs": "^2.1.2" + } + }, "@inquirer/figures": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.11.tgz", "integrity": "sha512-eOg92lvrn/aRUqbxRyvpEWnrvRuTYRifixHkYVpJiygTgVSBIHDqLh0SrMQXkafvULg3ck11V7xvR+zcgvpHFw==" }, + "@inquirer/input": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.9.tgz", + "integrity": "sha512-mshNG24Ij5KqsQtOZMgj5TwEjIf+F2HOESk6bjMwGWgcH5UBe8UoljwzNFHqdMbGYbgAf6v2wU/X9CAdKJzgOA==", + "requires": { + "@inquirer/core": "^10.1.10", + "@inquirer/type": "^3.0.6" + } + }, + "@inquirer/number": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.12.tgz", + "integrity": "sha512-7HRFHxbPCA4e4jMxTQglHJwP+v/kpFsCf2szzfBHy98Wlc3L08HL76UDiA87TOdX5fwj2HMOLWqRWv9Pnn+Z5Q==", + "requires": { + "@inquirer/core": "^10.1.10", + "@inquirer/type": "^3.0.6" + } + }, + "@inquirer/password": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.12.tgz", + "integrity": "sha512-FlOB0zvuELPEbnBYiPaOdJIaDzb2PmJ7ghi/SVwIHDDSQ2K4opGBkF+5kXOg6ucrtSUQdLhVVY5tycH0j0l+0g==", + "requires": { + "@inquirer/core": "^10.1.10", + "@inquirer/type": "^3.0.6", + "ansi-escapes": "^4.3.2" + } + }, + "@inquirer/prompts": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.4.1.tgz", + "integrity": "sha512-UlmM5FVOZF0gpoe1PT/jN4vk8JmpIWBlMvTL8M+hlvPmzN89K6z03+IFmyeu/oFCenwdwHDr2gky7nIGSEVvlA==", + "requires": { + "@inquirer/checkbox": "^4.1.5", + "@inquirer/confirm": "^5.1.9", + "@inquirer/editor": "^4.2.10", + "@inquirer/expand": "^4.0.12", + "@inquirer/input": "^4.1.9", + "@inquirer/number": "^3.0.12", + "@inquirer/password": "^4.0.12", + "@inquirer/rawlist": "^4.0.12", + "@inquirer/search": "^3.0.12", + "@inquirer/select": "^4.1.1" + } + }, + "@inquirer/rawlist": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.0.12.tgz", + "integrity": "sha512-wNPJZy8Oc7RyGISPxp9/MpTOqX8lr0r+lCCWm7hQra+MDtYRgINv1hxw7R+vKP71Bu/3LszabxOodfV/uTfsaA==", + "requires": { + "@inquirer/core": "^10.1.10", + "@inquirer/type": "^3.0.6", + "yoctocolors-cjs": "^2.1.2" + } + }, + "@inquirer/search": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.12.tgz", + "integrity": "sha512-H/kDJA3kNlnNIjB8YsaXoQI0Qccgf0Na14K1h8ExWhNmUg2E941dyFPrZeugihEa9AZNW5NdsD/NcvUME83OPQ==", + "requires": { + "@inquirer/core": "^10.1.10", + "@inquirer/figures": "^1.0.11", + "@inquirer/type": "^3.0.6", + "yoctocolors-cjs": "^2.1.2" + } + }, + "@inquirer/select": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.1.1.tgz", + "integrity": "sha512-IUXzzTKVdiVNMA+2yUvPxWsSgOG4kfX93jOM4Zb5FgujeInotv5SPIJVeXQ+fO4xu7tW8VowFhdG5JRmmCyQ1Q==", + "requires": { + "@inquirer/core": "^10.1.10", + "@inquirer/figures": "^1.0.11", + "@inquirer/type": "^3.0.6", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + } + }, "@inquirer/type": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.5.tgz", - "integrity": "sha512-ZJpeIYYueOz/i/ONzrfof8g89kNdO2hjGuvULROo3O8rlB2CRtSseE5KeirnyE4t/thAn/EwvS/vuQeJCn+NZg==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.6.tgz", + "integrity": "sha512-/mKVCtVpyBu3IDarv0G+59KC4stsD5mDsGpYh+GKs1NZT88Jh52+cuoA1AtLk2Q0r/quNl+1cSUyLRHBFeD0XA==", "requires": {} }, "@isaacs/cliui": { @@ -8832,6 +9226,11 @@ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "peer": true }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, "ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -9245,6 +9644,16 @@ "jest-util": "^29.7.0" } }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, "fast-content-type-parse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", @@ -9603,6 +10012,14 @@ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "peer": true }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, "ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -10689,6 +11106,11 @@ "resolved": "https://registry.npmjs.org/natural-orderby/-/natural-orderby-3.0.2.tgz", "integrity": "sha512-x7ZdOwBxZCEm9MM7+eQCjkrNLrW3rkBKNHVr78zbtqnMGVNlnDi6C/eUEYgxHNrcbu0ymvjzcwIL/6H1iHri9g==" }, + "node-cleanup": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", + "integrity": "sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==" + }, "node-gzip": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/node-gzip/-/node-gzip-1.1.2.tgz", @@ -10767,6 +11189,11 @@ "word-wrap": "^1.2.5" } }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==" + }, "outvariant": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", @@ -11138,6 +11565,11 @@ "resolved": "https://registry.npmjs.org/run-script-os/-/run-script-os-1.1.6.tgz", "integrity": "sha512-ql6P2LzhBTTDfzKts+Qo4H94VUKpxKDFz6QxxwaUZN0mwvi7L3lpOI7BqPCq7lgDh3XLl0dpeXwfcVIitlrYrw==" }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", @@ -11414,6 +11846,14 @@ } } }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, "tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -11439,14 +11879,6 @@ "url-parse": "^1.5.3" } }, - "transliteration": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/transliteration/-/transliteration-2.3.5.tgz", - "integrity": "sha512-HAGI4Lq4Q9dZ3Utu2phaWgtm3vB6PkLUFqWAScg/UW+1eZ/Tg6Exo4oC0/3VUol/w4BlefLhUUSVBr/9/ZGQOw==", - "requires": { - "yargs": "^17.5.1" - } - }, "ts-api-utils": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", diff --git a/package.json b/package.json index d3aec1ea83..6533852a23 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "playlist:validate": "tsx scripts/commands/playlist/validate.ts", "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", "readme:update": "tsx scripts/commands/readme/update.ts", "report:create": "tsx scripts/commands/report/create.ts", @@ -40,6 +41,8 @@ "@eslint/eslintrc": "^3.3.0", "@eslint/js": "^9.21.0", "@freearhey/core": "^0.7.0", + "@freearhey/search-js": "^0.1.2", + "@inquirer/prompts": "^7.4.1", "@octokit/core": "^6.1.4", "@octokit/plugin-paginate-rest": "^11.4.3", "@octokit/plugin-rest-endpoint-methods": "^7.1.3", @@ -65,8 +68,8 @@ "lodash": "^4.17.21", "m3u-linter": "^0.4.2", "markdown-include": "^0.4.3", + "node-cleanup": "^2.1.2", "numeral": "^2.0.6", - "transliteration": "^2.3.5", "ts-jest": "^29.2.5", "tsx": "^4.6.2", "valid-url": "^1.0.9" diff --git a/yarn.lock b/yarn.lock index 82088c20d6..94e14d2f5d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -385,6 +385,13 @@ run-script-os "^1.1.6" signale "^1.4.0" +"@freearhey/search-js@^0.1.2": + version "0.1.2" + resolved "https://registry.npmjs.org/@freearhey/search-js/-/search-js-0.1.2.tgz" + integrity sha512-F2o+xpGCXOK4OsZfKEHfXNNkAZmny2eBnPOp+P0iyV20ja7gJGfTFaEc6okcuEo6OB6P7LnSxTvISkoArFtlfg== + dependencies: + lodash "^4.17.21" + "@humanfs/core@^0.19.1": version "0.19.1" resolved "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz" @@ -413,21 +420,32 @@ resolved "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz" integrity sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ== -"@inquirer/confirm@^5.0.0": - version "5.1.7" - resolved "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.7.tgz" - integrity sha512-Xrfbrw9eSiHb+GsesO8TQIeHSMTP0xyvTCeeYevgZ4sKW+iz9w/47bgfG9b0niQm+xaLY2EWPBINUPldLwvYiw== +"@inquirer/checkbox@^4.1.5": + version "4.1.5" + resolved "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.5.tgz" + integrity sha512-swPczVU+at65xa5uPfNP9u3qx/alNwiaykiI/ExpsmMSQW55trmZcwhYWzw/7fj+n6Q8z1eENvR7vFfq9oPSAQ== dependencies: - "@inquirer/core" "^10.1.8" - "@inquirer/type" "^3.0.5" + "@inquirer/core" "^10.1.10" + "@inquirer/figures" "^1.0.11" + "@inquirer/type" "^3.0.6" + ansi-escapes "^4.3.2" + yoctocolors-cjs "^2.1.2" -"@inquirer/core@^10.1.8": - version "10.1.8" - resolved "https://registry.npmjs.org/@inquirer/core/-/core-10.1.8.tgz" - integrity sha512-HpAqR8y715zPpM9e/9Q+N88bnGwqqL8ePgZ0SMv/s3673JLMv3bIkoivGmjPqXlEgisUksSXibweQccUwEx4qQ== +"@inquirer/confirm@^5.0.0", "@inquirer/confirm@^5.1.9": + version "5.1.9" + resolved "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.9.tgz" + integrity sha512-NgQCnHqFTjF7Ys2fsqK2WtnA8X1kHyInyG+nMIuHowVTIgIuS10T4AznI/PvbqSpJqjCUqNBlKGh1v3bwLFL4w== + dependencies: + "@inquirer/core" "^10.1.10" + "@inquirer/type" "^3.0.6" + +"@inquirer/core@^10.1.10": + version "10.1.10" + resolved "https://registry.npmjs.org/@inquirer/core/-/core-10.1.10.tgz" + integrity sha512-roDaKeY1PYY0aCqhRmXihrHjoSW2A00pV3Ke5fTpMCkzcGF64R8e0lw3dK+eLEHwS4vB5RnW1wuQmvzoRul8Mw== dependencies: "@inquirer/figures" "^1.0.11" - "@inquirer/type" "^3.0.5" + "@inquirer/type" "^3.0.6" ansi-escapes "^4.3.2" cli-width "^4.1.0" mute-stream "^2.0.0" @@ -435,15 +453,104 @@ wrap-ansi "^6.2.0" yoctocolors-cjs "^2.1.2" +"@inquirer/editor@^4.2.10": + version "4.2.10" + resolved "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.10.tgz" + integrity sha512-5GVWJ+qeI6BzR6TIInLP9SXhWCEcvgFQYmcRG6d6RIlhFjM5TyG18paTGBgRYyEouvCmzeco47x9zX9tQEofkw== + dependencies: + "@inquirer/core" "^10.1.10" + "@inquirer/type" "^3.0.6" + external-editor "^3.1.0" + +"@inquirer/expand@^4.0.12": + version "4.0.12" + resolved "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.12.tgz" + integrity sha512-jV8QoZE1fC0vPe6TnsOfig+qwu7Iza1pkXoUJ3SroRagrt2hxiL+RbM432YAihNR7m7XnU0HWl/WQ35RIGmXHw== + dependencies: + "@inquirer/core" "^10.1.10" + "@inquirer/type" "^3.0.6" + yoctocolors-cjs "^2.1.2" + "@inquirer/figures@^1.0.11": version "1.0.11" resolved "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.11.tgz" integrity sha512-eOg92lvrn/aRUqbxRyvpEWnrvRuTYRifixHkYVpJiygTgVSBIHDqLh0SrMQXkafvULg3ck11V7xvR+zcgvpHFw== -"@inquirer/type@^3.0.5": - version "3.0.5" - resolved "https://registry.npmjs.org/@inquirer/type/-/type-3.0.5.tgz" - integrity sha512-ZJpeIYYueOz/i/ONzrfof8g89kNdO2hjGuvULROo3O8rlB2CRtSseE5KeirnyE4t/thAn/EwvS/vuQeJCn+NZg== +"@inquirer/input@^4.1.9": + version "4.1.9" + resolved "https://registry.npmjs.org/@inquirer/input/-/input-4.1.9.tgz" + integrity sha512-mshNG24Ij5KqsQtOZMgj5TwEjIf+F2HOESk6bjMwGWgcH5UBe8UoljwzNFHqdMbGYbgAf6v2wU/X9CAdKJzgOA== + dependencies: + "@inquirer/core" "^10.1.10" + "@inquirer/type" "^3.0.6" + +"@inquirer/number@^3.0.12": + version "3.0.12" + resolved "https://registry.npmjs.org/@inquirer/number/-/number-3.0.12.tgz" + integrity sha512-7HRFHxbPCA4e4jMxTQglHJwP+v/kpFsCf2szzfBHy98Wlc3L08HL76UDiA87TOdX5fwj2HMOLWqRWv9Pnn+Z5Q== + dependencies: + "@inquirer/core" "^10.1.10" + "@inquirer/type" "^3.0.6" + +"@inquirer/password@^4.0.12": + version "4.0.12" + resolved "https://registry.npmjs.org/@inquirer/password/-/password-4.0.12.tgz" + integrity sha512-FlOB0zvuELPEbnBYiPaOdJIaDzb2PmJ7ghi/SVwIHDDSQ2K4opGBkF+5kXOg6ucrtSUQdLhVVY5tycH0j0l+0g== + dependencies: + "@inquirer/core" "^10.1.10" + "@inquirer/type" "^3.0.6" + ansi-escapes "^4.3.2" + +"@inquirer/prompts@^7.4.1": + version "7.4.1" + resolved "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.4.1.tgz" + integrity sha512-UlmM5FVOZF0gpoe1PT/jN4vk8JmpIWBlMvTL8M+hlvPmzN89K6z03+IFmyeu/oFCenwdwHDr2gky7nIGSEVvlA== + dependencies: + "@inquirer/checkbox" "^4.1.5" + "@inquirer/confirm" "^5.1.9" + "@inquirer/editor" "^4.2.10" + "@inquirer/expand" "^4.0.12" + "@inquirer/input" "^4.1.9" + "@inquirer/number" "^3.0.12" + "@inquirer/password" "^4.0.12" + "@inquirer/rawlist" "^4.0.12" + "@inquirer/search" "^3.0.12" + "@inquirer/select" "^4.1.1" + +"@inquirer/rawlist@^4.0.12": + version "4.0.12" + resolved "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.0.12.tgz" + integrity sha512-wNPJZy8Oc7RyGISPxp9/MpTOqX8lr0r+lCCWm7hQra+MDtYRgINv1hxw7R+vKP71Bu/3LszabxOodfV/uTfsaA== + dependencies: + "@inquirer/core" "^10.1.10" + "@inquirer/type" "^3.0.6" + yoctocolors-cjs "^2.1.2" + +"@inquirer/search@^3.0.12": + version "3.0.12" + resolved "https://registry.npmjs.org/@inquirer/search/-/search-3.0.12.tgz" + integrity sha512-H/kDJA3kNlnNIjB8YsaXoQI0Qccgf0Na14K1h8ExWhNmUg2E941dyFPrZeugihEa9AZNW5NdsD/NcvUME83OPQ== + dependencies: + "@inquirer/core" "^10.1.10" + "@inquirer/figures" "^1.0.11" + "@inquirer/type" "^3.0.6" + yoctocolors-cjs "^2.1.2" + +"@inquirer/select@^4.1.1": + version "4.1.1" + resolved "https://registry.npmjs.org/@inquirer/select/-/select-4.1.1.tgz" + integrity sha512-IUXzzTKVdiVNMA+2yUvPxWsSgOG4kfX93jOM4Zb5FgujeInotv5SPIJVeXQ+fO4xu7tW8VowFhdG5JRmmCyQ1Q== + dependencies: + "@inquirer/core" "^10.1.10" + "@inquirer/figures" "^1.0.11" + "@inquirer/type" "^3.0.6" + ansi-escapes "^4.3.2" + yoctocolors-cjs "^2.1.2" + +"@inquirer/type@^3.0.6": + version "3.0.6" + resolved "https://registry.npmjs.org/@inquirer/type/-/type-3.0.6.tgz" + integrity sha512-/mKVCtVpyBu3IDarv0G+59KC4stsD5mDsGpYh+GKs1NZT88Jh52+cuoA1AtLk2Q0r/quNl+1cSUyLRHBFeD0XA== "@isaacs/cliui@^8.0.2": version "8.0.2" @@ -1401,6 +1508,11 @@ char-regex@^1.0.2: resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + ci-info@^3.2.0: version "3.9.0" resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz" @@ -1802,6 +1914,15 @@ expect@^29.0.0, expect@^29.7.0: jest-message-util "^29.7.0" jest-util "^29.7.0" +external-editor@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + fast-content-type-parse@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz" @@ -2120,6 +2241,13 @@ human-signals@^2.1.0: resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + ignore@^5.2.0, ignore@^5.3.1: version "5.3.2" resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz" @@ -3068,6 +3196,11 @@ natural-orderby@^3.0.2: resolved "https://registry.npmjs.org/natural-orderby/-/natural-orderby-3.0.2.tgz" integrity sha512-x7ZdOwBxZCEm9MM7+eQCjkrNLrW3rkBKNHVr78zbtqnMGVNlnDi6C/eUEYgxHNrcbu0ymvjzcwIL/6H1iHri9g== +node-cleanup@^2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz" + integrity sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw== + node-gzip@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/node-gzip/-/node-gzip-1.1.2.tgz" @@ -3141,6 +3274,11 @@ optionator@^0.9.3: type-check "^0.4.0" word-wrap "^1.2.5" +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + outvariant@^1.4.0, outvariant@^1.4.3: version "1.4.3" resolved "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz" @@ -3441,6 +3579,11 @@ run-script-os@^1.1.6: resolved "https://registry.npmjs.org/run-script-os/-/run-script-os-1.1.6.tgz" integrity sha512-ql6P2LzhBTTDfzKts+Qo4H94VUKpxKDFz6QxxwaUZN0mwvi7L3lpOI7BqPCq7lgDh3XLl0dpeXwfcVIitlrYrw== +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + semver@^6.3.0: version "6.3.1" resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" @@ -3680,6 +3823,13 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + tmpl@1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" @@ -3702,13 +3852,6 @@ tough-cookie@^4.1.4: universalify "^0.2.0" url-parse "^1.5.3" -transliteration@^2.3.5: - version "2.3.5" - resolved "https://registry.npmjs.org/transliteration/-/transliteration-2.3.5.tgz" - integrity sha512-HAGI4Lq4Q9dZ3Utu2phaWgtm3vB6PkLUFqWAScg/UW+1eZ/Tg6Exo4oC0/3VUol/w4BlefLhUUSVBr/9/ZGQOw== - dependencies: - yargs "^17.5.1" - ts-api-utils@^1.3.0: version "1.4.3" resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz" @@ -3911,7 +4054,7 @@ yargs-parser@^21.1.1: resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== -yargs@^17.3.1, yargs@^17.5.1, yargs@^17.7.2: +yargs@^17.3.1, yargs@^17.7.2: version "17.7.2" resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== From 8225e59a0ac653c35655413c40f00078a8d61f60 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 16 Apr 2025 20:19:19 +0300 Subject: [PATCH 02/24] Update tests/__data__ --- tests/__data__/input/data/guides.json | 1 + tests/__data__/input/data/streams.json | 1 + tests/__data__/input/playlist_edit/playlist.m3u | 5 +++++ 3 files changed, 7 insertions(+) create mode 100644 tests/__data__/input/data/guides.json create mode 100644 tests/__data__/input/data/streams.json create mode 100644 tests/__data__/input/playlist_edit/playlist.m3u diff --git a/tests/__data__/input/data/guides.json b/tests/__data__/input/data/guides.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/tests/__data__/input/data/guides.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/tests/__data__/input/data/streams.json b/tests/__data__/input/data/streams.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/tests/__data__/input/data/streams.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/tests/__data__/input/playlist_edit/playlist.m3u b/tests/__data__/input/playlist_edit/playlist.m3u new file mode 100644 index 0000000000..2ebf1ffa7a --- /dev/null +++ b/tests/__data__/input/playlist_edit/playlist.m3u @@ -0,0 +1,5 @@ +#EXTM3U +#EXTINF:-1 tvg-id="HewadTV.af",Hewad TV (720p) [Not 24/7] +http://51.210.199.58/hls/stream.m3u8 +#EXTINF:-1 tvg-id="",Télévision française 1 (480p) +https://live.relentlessinnovations.net:1936/imantv/imantv/playlist.m3u8 \ No newline at end of file From 2d44218887ddee930933faec87761af88a486b4c Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 16 Apr 2025 20:19:31 +0300 Subject: [PATCH 03/24] Update tests --- tests/commands/playlist/edit.test.ts | 43 ++++++++++++++++++++++++ tests/commands/playlist/generate.test.ts | 4 ++- tests/commands/playlist/validate.test.ts | 5 ++- 3 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 tests/commands/playlist/edit.test.ts diff --git a/tests/commands/playlist/edit.test.ts b/tests/commands/playlist/edit.test.ts new file mode 100644 index 0000000000..4651e5ef88 --- /dev/null +++ b/tests/commands/playlist/edit.test.ts @@ -0,0 +1,43 @@ +import { execSync } from 'child_process' +import fs from 'fs-extra' +import os from 'os' + +type ExecError = { + status: number + stdout: string +} + +let ENV_VAR = 'DATA_DIR=tests/__data__/input/data' +if (os.platform() === 'win32') { + ENV_VAR = 'SET "DATA_DIR=tests/__data__/input/data" &&' +} + +beforeEach(() => { + fs.emptyDirSync('tests/__data__/output') + fs.copySync( + 'tests/__data__/input/playlist_edit/playlist.m3u', + 'tests/__data__/output/playlist.m3u' + ) +}) + +describe('playlist:edit', () => { + it('shows list of options for a streams', () => { + const cmd = `${ENV_VAR} npm run playlist:edit --- tests/__data__/output/playlist.m3u` + try { + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + checkStdout(stdout) + } catch (error) { + // NOTE: for Windows only + if (process.env.DEBUG === 'true') console.log(cmd, error) + checkStdout((error as ExecError).stdout) + } + }) +}) + +function checkStdout(stdout: string) { + expect(stdout).toContain('TF1.fr (TF1, Télévision française 1)') + expect(stdout).toContain('Type...') + expect(stdout).toContain('Skip') + expect(stdout).toContain("File 'tests/__data__/output/playlist.m3u' successfully saved") +} diff --git a/tests/commands/playlist/generate.test.ts b/tests/commands/playlist/generate.test.ts index 54c4e61e0b..389ee022ee 100644 --- a/tests/commands/playlist/generate.test.ts +++ b/tests/commands/playlist/generate.test.ts @@ -7,11 +7,13 @@ beforeEach(() => { }) it('can generate playlists and logs', () => { - execSync( + const stdout = execSync( 'STREAMS_DIR=tests/__data__/input/playlist_generate DATA_DIR=tests/__data__/input/data PUBLIC_DIR=tests/__data__/output/.gh-pages LOGS_DIR=tests/__data__/output/logs npm run playlist:generate', { encoding: 'utf8' } ) + if (process.env.DEBUG === 'true') console.log(stdout) + const playlists = glob .sync('tests/__data__/expected/playlist_generate/.gh-pages/**/*.m3u') .map((file: string) => file.replace('tests/__data__/expected/playlist_generate/', '')) diff --git a/tests/commands/playlist/validate.test.ts b/tests/commands/playlist/validate.test.ts index 38dd104ee4..b2a43324e4 100644 --- a/tests/commands/playlist/validate.test.ts +++ b/tests/commands/playlist/validate.test.ts @@ -7,14 +7,16 @@ type ExecError = { it('show an error if channel id in the blocklist', () => { try { - execSync( + const stdout = execSync( 'DATA_DIR=tests/__data__/input/data STREAMS_DIR=tests/__data__/input/playlist_validate npm run playlist:validate -- us_blocked.m3u', { encoding: 'utf8' } ) + if (process.env.DEBUG === 'true') console.log(stdout) process.exit(1) } catch (error) { + if (process.env.DEBUG === 'true') console.log((error as ExecError).stdout) expect((error as ExecError).status).toBe(1) expect((error as ExecError).stdout).toContain(`us_blocked.m3u 2 error "FoxSports2.us" is on the blocklist due to claims of copyright holders (https://github.com/iptv-org/iptv/issues/0002) @@ -31,6 +33,7 @@ it('show a warning if channel has wrong id', () => { encoding: 'utf8' } ) + if (process.env.DEBUG === 'true') console.log(stdout) expect(stdout).toContain( 'wrong_id.m3u\n 2 warning "qib22lAq1L.us" is not in the database\n\n1 problems (0 errors, 1 warnings)\n' From 1cd6a31854c65bab40ebb8bdf4d76374bed3122f Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 16 Apr 2025 20:53:58 +0300 Subject: [PATCH 04/24] Update template.md --- .readme/template.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.readme/template.md b/.readme/template.md index 5dd4d1cf87..84a8f70133 100644 --- a/.readme/template.md +++ b/.readme/template.md @@ -93,12 +93,12 @@ Same thing, but split up into separate files: ### Grouped by region +Playlists in which channels are grouped by the region for which they are broadcasted. +
Expand
-Playlists in which channels are grouped by the region for which they are broadcasted. - ``` https://iptv-org.github.io/iptv/index.region.m3u ``` From d39af32f189a112f1aead8bd21083c89550b7ac4 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 16 Apr 2025 20:54:01 +0300 Subject: [PATCH 05/24] Update CONTRIBUTING.md --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 556ed05c7b..022381c639 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -176,6 +176,7 @@ To run scripts use the `npm run ` command. - `playlist:validate`: сhecks ids and links in internal playlists for errors. - `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. - `readme:update`: updates the list of playlists in [README.md](README.md). - `report:create`: creates a report on current issues. From d42b102cdfb2db19fec869d393815066919fe65e Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 16 Apr 2025 20:54:55 +0300 Subject: [PATCH 06/24] Update scripts --- scripts/commands/api/generate.ts | 27 ++-- scripts/commands/api/load.ts | 35 ++--- scripts/commands/playlist/edit.ts | 208 ++++++++++++++++++++++++++ scripts/commands/playlist/format.ts | 29 ++-- scripts/commands/playlist/generate.ts | 100 +++---------- scripts/commands/playlist/test.ts | 22 ++- scripts/commands/playlist/update.ts | 98 +++++------- scripts/commands/playlist/validate.ts | 59 ++++---- scripts/commands/report/create.ts | 53 ++++--- scripts/core/apiClient.ts | 57 +------ scripts/core/dataLoader.ts | 100 +++++++++++++ scripts/core/dataProcessor.ts | 110 ++++++++++++++ scripts/core/index.ts | 18 ++- scripts/core/playlistParser.ts | 13 +- scripts/models/blocked.ts | 17 --- scripts/models/blocklistRecord.ts | 15 ++ scripts/models/category.ts | 12 +- scripts/models/channel.ts | 145 ++++++++++++++---- scripts/models/country.ts | 50 +++++-- scripts/models/feed.ts | 86 +++++++---- scripts/models/guide.ts | 54 +++++++ scripts/models/index.ts | 17 ++- scripts/models/language.ts | 23 ++- scripts/models/region.ts | 53 +++++-- scripts/models/stream.ts | 121 ++++++++------- scripts/models/subdivision.ts | 33 ++-- scripts/models/timezone.ts | 4 +- scripts/types/blocklistRecord.d.ts | 5 + scripts/types/category.d.ts | 9 ++ scripts/types/channel.d.ts | 52 +++++++ scripts/types/country.d.ts | 20 +++ scripts/types/dataLoader.d.ts | 19 +++ scripts/types/dataProcessor.d.ts | 27 ++++ scripts/types/feed.d.ts | 12 ++ scripts/types/guide.d.ts | 17 +++ scripts/types/language.d.ts | 9 ++ scripts/types/region.d.ts | 13 ++ scripts/types/stream.d.ts | 10 ++ scripts/types/subdivision.d.ts | 12 ++ 39 files changed, 1256 insertions(+), 508 deletions(-) create mode 100644 scripts/commands/playlist/edit.ts create mode 100644 scripts/core/dataLoader.ts create mode 100644 scripts/core/dataProcessor.ts delete mode 100644 scripts/models/blocked.ts create mode 100644 scripts/models/blocklistRecord.ts create mode 100644 scripts/models/guide.ts create mode 100644 scripts/types/blocklistRecord.d.ts create mode 100644 scripts/types/category.d.ts create mode 100644 scripts/types/channel.d.ts create mode 100644 scripts/types/country.d.ts create mode 100644 scripts/types/dataLoader.d.ts create mode 100644 scripts/types/dataProcessor.d.ts create mode 100644 scripts/types/feed.d.ts create mode 100644 scripts/types/guide.d.ts create mode 100644 scripts/types/language.d.ts create mode 100644 scripts/types/region.d.ts create mode 100644 scripts/types/stream.d.ts create mode 100644 scripts/types/subdivision.d.ts diff --git a/scripts/commands/api/generate.ts b/scripts/commands/api/generate.ts index 670fa091d8..f264260f9b 100644 --- a/scripts/commands/api/generate.ts +++ b/scripts/commands/api/generate.ts @@ -1,30 +1,25 @@ -import { Logger, Storage, Collection } from '@freearhey/core' +import { DataLoader, DataProcessor, PlaylistParser } from '../../core' +import type { DataProcessorData } from '../../types/dataProcessor' import { API_DIR, STREAMS_DIR, DATA_DIR } from '../../constants' -import { PlaylistParser } from '../../core' -import { Stream, Channel, Feed } from '../../models' -import { uniqueId } from 'lodash' +import type { DataLoaderData } from '../../types/dataLoader' +import { Logger, Storage } from '@freearhey/core' +import { Stream } from '../../models' async function main() { const logger = new Logger() - logger.info('loading api data...') + logger.info('loading data from api...') + const processor = new DataProcessor() const dataStorage = new Storage(DATA_DIR) - const channelsData = await dataStorage.json('channels.json') - const channels = new Collection(channelsData).map(data => new Channel(data)) - const channelsGroupedById = channels.keyBy((channel: Channel) => channel.id) - const feedsData = await dataStorage.json('feeds.json') - const feeds = new Collection(feedsData).map(data => - new Feed(data).withChannel(channelsGroupedById) - ) - const feedsGroupedByChannelId = feeds.groupBy((feed: Feed) => - feed.channel ? feed.channel.id : uniqueId() - ) + const dataLoader = new DataLoader({ storage: dataStorage }) + const data: DataLoaderData = await dataLoader.load() + const { channelsKeyById, feedsGroupedByChannelId }: DataProcessorData = processor.process(data) logger.info('loading streams...') const streamsStorage = new Storage(STREAMS_DIR) const parser = new PlaylistParser({ storage: streamsStorage, - channelsGroupedById, + channelsKeyById, feedsGroupedByChannelId }) const files = await streamsStorage.list('**/*.m3u') diff --git a/scripts/commands/api/load.ts b/scripts/commands/api/load.ts index fbb1fea437..3fdc70043c 100644 --- a/scripts/commands/api/load.ts +++ b/scripts/commands/api/load.ts @@ -1,23 +1,24 @@ -import { Logger } from '@freearhey/core' -import { ApiClient } from '../../core' +import { DATA_DIR } from '../../constants' +import { Storage } from '@freearhey/core' +import { DataLoader } from '../../core' async function main() { - const logger = new Logger() - const client = new ApiClient({ logger }) + const storage = new Storage(DATA_DIR) + const loader = new DataLoader({ storage }) - const requests = [ - client.download('blocklist.json'), - client.download('categories.json'), - client.download('channels.json'), - client.download('countries.json'), - client.download('languages.json'), - client.download('regions.json'), - client.download('subdivisions.json'), - client.download('feeds.json'), - client.download('timezones.json') - ] - - await Promise.all(requests) + await Promise.all([ + loader.download('blocklist.json'), + loader.download('categories.json'), + loader.download('channels.json'), + loader.download('countries.json'), + loader.download('languages.json'), + loader.download('regions.json'), + loader.download('subdivisions.json'), + loader.download('feeds.json'), + loader.download('timezones.json'), + loader.download('guides.json'), + loader.download('streams.json') + ]) } main() diff --git a/scripts/commands/playlist/edit.ts b/scripts/commands/playlist/edit.ts new file mode 100644 index 0000000000..c57a62d1e1 --- /dev/null +++ b/scripts/commands/playlist/edit.ts @@ -0,0 +1,208 @@ +import { Storage, Collection, Logger, Dictionary } from '@freearhey/core' +import { DataLoader, DataProcessor, PlaylistParser } from '../../core' +import { Channel, Feed, Playlist, Stream } from '../../models' +import type { ChannelSearchableData } from '../../types/channel' +import { DataProcessorData } from '../../types/dataProcessor' +import { DataLoaderData } from '../../types/dataLoader' +import { select, input } from '@inquirer/prompts' +import { DATA_DIR } from '../../constants' +import nodeCleanup from 'node-cleanup' +import sjs from '@freearhey/search-js' +import { Command } from 'commander' +import readline from 'readline' + +type ChoiceValue = { type: string; value?: Feed | Channel } +type Choice = { name: string; short?: string; value: ChoiceValue; default?: boolean } + +if (process.platform === 'win32') { + readline + .createInterface({ + input: process.stdin, + output: process.stdout + }) + .on('SIGINT', function () { + process.emit('SIGINT') + }) +} + +const program = new Command() + +program.argument('', 'Path to *.channels.xml file to edit').parse(process.argv) + +const filepath = program.args[0] +const logger = new Logger() +const storage = new Storage() +let parsedStreams = new Collection() + +main(filepath) +nodeCleanup(() => { + save(filepath) +}) + +export default async function main(filepath: string) { + if (!(await storage.exists(filepath))) { + throw new Error(`File "${filepath}" does not exists`) + } + + logger.info('loading data from api...') + const processor = new DataProcessor() + const dataStorage = new Storage(DATA_DIR) + const loader = new DataLoader({ storage: dataStorage }) + const data: DataLoaderData = await loader.load() + const { channels, channelsKeyById, feedsGroupedByChannelId }: DataProcessorData = + processor.process(data) + + logger.info('loading streams...') + const parser = new PlaylistParser({ storage, feedsGroupedByChannelId, channelsKeyById }) + parsedStreams = await parser.parseFile(filepath) + const streamsWithoutId = parsedStreams.filter((stream: Stream) => !stream.id) + + logger.info( + `found ${parsedStreams.count()} streams (including ${streamsWithoutId.count()} without ID)` + ) + + logger.info('creating search index...') + const items = channels.map((channel: Channel) => channel.getSearchable()).all() + const searchIndex = sjs.createIndex(items, { + searchable: ['name', 'altNames', 'guideNames', 'streamNames', 'feedFullNames'] + }) + + logger.info('starting...\n') + + for (const stream of streamsWithoutId.all()) { + try { + stream.id = await selectChannel(stream, searchIndex, feedsGroupedByChannelId, channelsKeyById) + } catch (err) { + logger.info(err.message) + break + } + } + + streamsWithoutId.forEach((stream: Stream) => { + if (stream.id === '-') { + stream.id = '' + } + }) +} + +async function selectChannel( + stream: Stream, + searchIndex, + feedsGroupedByChannelId: Dictionary, + channelsKeyById: Dictionary +): Promise { + const query = escapeRegex(stream.getName()) + const similarChannels = searchIndex + .search(query) + .map((item: ChannelSearchableData) => channelsKeyById.get(item.id)) + + const url = stream.url.length > 50 ? stream.url.slice(0, 50) + '...' : stream.url + + const selected: ChoiceValue = await select({ + message: `Select channel ID for "${stream.name}" (${url}):`, + choices: getChannelChoises(new Collection(similarChannels)), + pageSize: 10 + }) + + switch (selected.type) { + case 'skip': + return '-' + case 'type': { + const typedChannelId = await input({ message: ' Channel ID:' }) + if (!typedChannelId) return '' + const selectedFeedId = await selectFeed(typedChannelId, feedsGroupedByChannelId) + if (selectedFeedId === '-') return typedChannelId + return [typedChannelId, selectedFeedId].join('@') + } + case 'channel': { + const selectedChannel = selected.value + if (!selectedChannel) return '' + const selectedFeedId = await selectFeed(selectedChannel.id, feedsGroupedByChannelId) + if (selectedFeedId === '-') return selectedChannel.id + return [selectedChannel.id, selectedFeedId].join('@') + } + } + + return '' +} + +async function selectFeed(channelId: string, feedsGroupedByChannelId: Dictionary): Promise { + const channelFeeds = new Collection(feedsGroupedByChannelId.get(channelId)) || new Collection() + const choices = getFeedChoises(channelFeeds) + + const selected: ChoiceValue = await select({ + message: `Select feed ID for "${channelId}":`, + choices, + pageSize: 10 + }) + + switch (selected.type) { + case 'skip': + return '-' + case 'type': + return await input({ message: ' Feed ID:', default: 'SD' }) + case 'feed': + const selectedFeed = selected.value + if (!selectedFeed) return '' + return selectedFeed.id + } + + return '' +} + +function getChannelChoises(channels: Collection): Choice[] { + const choises: Choice[] = [] + + channels.forEach((channel: Channel) => { + const names = new Collection([channel.name, ...channel.altNames.all()]).uniq().join(', ') + + choises.push({ + value: { + type: 'channel', + value: channel + }, + name: `${channel.id} (${names})`, + short: `${channel.id}` + }) + }) + + choises.push({ name: 'Type...', value: { type: 'type' } }) + choises.push({ name: 'Skip', value: { type: 'skip' } }) + + return choises +} + +function getFeedChoises(feeds: Collection): Choice[] { + const choises: Choice[] = [] + + feeds.forEach((feed: Feed) => { + let name = `${feed.id} (${feed.name})` + if (feed.isMain) name += ' [main]' + + choises.push({ + value: { + type: 'feed', + value: feed + }, + default: feed.isMain, + name, + short: feed.id + }) + }) + + choises.push({ name: 'Type...', value: { type: 'type' } }) + choises.push({ name: 'Skip', value: { type: 'skip' } }) + + return choises +} + +function save(filepath: string) { + if (!storage.existsSync(filepath)) return + const playlist = new Playlist(parsedStreams) + storage.saveSync(filepath, playlist.toString()) + logger.info(`\nFile '${filepath}' successfully saved`) +} + +function escapeRegex(string: string) { + return string.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&') +} diff --git a/scripts/commands/playlist/format.ts b/scripts/commands/playlist/format.ts index 6ac14cb625..43868b73e3 100644 --- a/scripts/commands/playlist/format.ts +++ b/scripts/commands/playlist/format.ts @@ -1,33 +1,28 @@ -import { Logger, Storage, Collection } from '@freearhey/core' +import { Logger, Storage } from '@freearhey/core' import { STREAMS_DIR, DATA_DIR } from '../../constants' -import { PlaylistParser } from '../../core' -import { Stream, Playlist, Channel, Feed } from '../../models' +import { DataLoader, DataProcessor, PlaylistParser } from '../../core' +import { Stream, Playlist } from '../../models' import { program } from 'commander' -import { uniqueId } from 'lodash' +import { DataLoaderData } from '../../types/dataLoader' +import { DataProcessorData } from '../../types/dataProcessor' program.argument('[filepath]', 'Path to file to validate').parse(process.argv) async function main() { - const streamsStorage = new Storage(STREAMS_DIR) const logger = new Logger() logger.info('loading data from api...') + const processor = new DataProcessor() const dataStorage = new Storage(DATA_DIR) - const channelsData = await dataStorage.json('channels.json') - const channels = new Collection(channelsData).map(data => new Channel(data)) - const channelsGroupedById = channels.keyBy((channel: Channel) => channel.id) - const feedsData = await dataStorage.json('feeds.json') - const feeds = new Collection(feedsData).map(data => - new Feed(data).withChannel(channelsGroupedById) - ) - const feedsGroupedByChannelId = feeds.groupBy(feed => - feed.channel ? feed.channel.id : uniqueId() - ) + const loader = new DataLoader({ storage: dataStorage }) + const data: DataLoaderData = await loader.load() + const { channelsKeyById, feedsGroupedByChannelId }: DataProcessorData = processor.process(data) logger.info('loading streams...') + const streamsStorage = new Storage(STREAMS_DIR) const parser = new PlaylistParser({ storage: streamsStorage, - channelsGroupedById, + channelsKeyById, feedsGroupedByChannelId }) const files = program.args.length ? program.args : await streamsStorage.list('**/*.m3u') @@ -46,7 +41,7 @@ async function main() { logger.info('removing wrong id...') streams = streams.map((stream: Stream) => { - if (!stream.channel || channelsGroupedById.missing(stream.channel.id)) { + if (!stream.channel || channelsKeyById.missing(stream.channel.id)) { stream.id = '' } diff --git a/scripts/commands/playlist/generate.ts b/scripts/commands/playlist/generate.ts index 7acbbba4b1..c323d5395c 100644 --- a/scripts/commands/playlist/generate.ts +++ b/scripts/commands/playlist/generate.ts @@ -1,16 +1,6 @@ -import { Logger, Storage, Collection } from '@freearhey/core' -import { PlaylistParser } from '../../core' -import { - Stream, - Category, - Channel, - Language, - Country, - Region, - Subdivision, - Feed, - Timezone -} from '../../models' +import { Logger, Storage } from '@freearhey/core' +import { PlaylistParser, DataProcessor, DataLoader } from '../../core' +import { Stream } from '../../models' import { uniqueId } from 'lodash' import { CategoriesGenerator, @@ -24,86 +14,36 @@ import { IndexRegionGenerator } from '../../generators' import { DATA_DIR, LOGS_DIR, STREAMS_DIR } from '../../constants' +import type { DataProcessorData } from '../../types/dataProcessor' +import type { DataLoaderData } from '../../types/dataLoader' async function main() { const logger = new Logger() - const dataStorage = new Storage(DATA_DIR) const generatorsLogger = new Logger({ stream: await new Storage(LOGS_DIR).createStream(`generators.log`) }) logger.info('loading data from api...') - const categoriesData = await dataStorage.json('categories.json') - const countriesData = await dataStorage.json('countries.json') - const languagesData = await dataStorage.json('languages.json') - const regionsData = await dataStorage.json('regions.json') - const subdivisionsData = await dataStorage.json('subdivisions.json') - const timezonesData = await dataStorage.json('timezones.json') - const channelsData = await dataStorage.json('channels.json') - const feedsData = await dataStorage.json('feeds.json') - - logger.info('preparing data...') - const subdivisions = new Collection(subdivisionsData).map(data => new Subdivision(data)) - const subdivisionsGroupedByCode = subdivisions.keyBy( - (subdivision: Subdivision) => subdivision.code - ) - const subdivisionsGroupedByCountryCode = subdivisions.groupBy( - (subdivision: Subdivision) => subdivision.countryCode - ) - let regions = new Collection(regionsData).map(data => - new Region(data).withSubdivisions(subdivisions) - ) - const regionsGroupedByCode = regions.keyBy((region: Region) => region.code) - const categories = new Collection(categoriesData).map(data => new Category(data)) - const categoriesGroupedById = categories.keyBy((category: Category) => category.id) - const languages = new Collection(languagesData).map(data => new Language(data)) - const languagesGroupedByCode = languages.keyBy((language: Language) => language.code) - const countries = new Collection(countriesData).map(data => - new Country(data) - .withRegions(regions) - .withLanguage(languagesGroupedByCode) - .withSubdivisions(subdivisionsGroupedByCountryCode) - ) - const countriesGroupedByCode = countries.keyBy((country: Country) => country.code) - regions = regions.map((region: Region) => region.withCountries(countriesGroupedByCode)) - - const timezones = new Collection(timezonesData).map(data => - new Timezone(data).withCountries(countriesGroupedByCode) - ) - const timezonesGroupedById = timezones.keyBy((timezone: Timezone) => timezone.id) - - const channels = new Collection(channelsData).map(data => - new Channel(data) - .withCategories(categoriesGroupedById) - .withCountry(countriesGroupedByCode) - .withSubdivision(subdivisionsGroupedByCode) - ) - const channelsGroupedById = channels.keyBy((channel: Channel) => channel.id) - const feeds = new Collection(feedsData).map(data => - new Feed(data) - .withChannel(channelsGroupedById) - .withLanguages(languagesGroupedByCode) - .withTimezones(timezonesGroupedById) - .withBroadcastCountries( - countriesGroupedByCode, - regionsGroupedByCode, - subdivisionsGroupedByCode - ) - .withBroadcastRegions(regions) - .withBroadcastSubdivisions(subdivisionsGroupedByCode) - ) - const feedsGroupedByChannelId = feeds.groupBy((feed: Feed) => - feed.channel ? feed.channel.id : uniqueId() - ) + const processor = new DataProcessor() + const dataStorage = new Storage(DATA_DIR) + const loader = new DataLoader({ storage: dataStorage }) + const data: DataLoaderData = await loader.load() + const { + categories, + countries, + regions, + channelsKeyById, + feedsGroupedByChannelId + }: DataProcessorData = processor.process(data) logger.info('loading streams...') - const storage = new Storage(STREAMS_DIR) + const streamsStorage = new Storage(STREAMS_DIR) const parser = new PlaylistParser({ - storage, - channelsGroupedById, + storage: streamsStorage, + channelsKeyById, feedsGroupedByChannelId }) - const files = await storage.list('**/*.m3u') + const files = await streamsStorage.list('**/*.m3u') let streams = await parser.parse(files) const totalStreams = streams.count() streams = streams.uniqBy((stream: Stream) => diff --git a/scripts/commands/playlist/test.ts b/scripts/commands/playlist/test.ts index f32f2e0c2c..cc6f1dc148 100644 --- a/scripts/commands/playlist/test.ts +++ b/scripts/commands/playlist/test.ts @@ -1,13 +1,15 @@ import { Logger, Storage, Collection } from '@freearhey/core' import { ROOT_DIR, STREAMS_DIR, DATA_DIR } from '../../constants' -import { PlaylistParser, StreamTester, CliTable } from '../../core' -import { Stream, Feed, Channel } from '../../models' +import { PlaylistParser, StreamTester, CliTable, DataProcessor, DataLoader } from '../../core' +import { Stream } from '../../models' import { program } from 'commander' import { eachLimit } from 'async-es' import commandExists from 'command-exists' import chalk from 'chalk' import os from 'node:os' import dns from 'node:dns' +import type { DataLoaderData } from '../../types/dataLoader' +import type { DataProcessorData } from '../../types/dataProcessor' const cpus = os.cpus() @@ -54,22 +56,18 @@ async function main() { return } - logger.info('loading channels from api...') + logger.info('loading data from api...') + const processor = new DataProcessor() const dataStorage = new Storage(DATA_DIR) - const channelsData = await dataStorage.json('channels.json') - const channels = new Collection(channelsData).map(data => new Channel(data)) - const channelsGroupedById = channels.keyBy((channel: Channel) => channel.id) - const feedsData = await dataStorage.json('feeds.json') - const feeds = new Collection(feedsData).map(data => - new Feed(data).withChannel(channelsGroupedById) - ) - const feedsGroupedByChannelId = feeds.groupBy(feed => feed.channel) + const loader = new DataLoader({ storage: dataStorage }) + const data: DataLoaderData = await loader.load() + const { channelsKeyById, feedsGroupedByChannelId }: DataProcessorData = processor.process(data) logger.info('loading streams...') const rootStorage = new Storage(ROOT_DIR) const parser = new PlaylistParser({ storage: rootStorage, - channelsGroupedById, + channelsKeyById, feedsGroupedByChannelId }) const files = program.args.length ? program.args : await rootStorage.list(`${STREAMS_DIR}/*.m3u`) diff --git a/scripts/commands/playlist/update.ts b/scripts/commands/playlist/update.ts index b2ac5b8143..55551b67d0 100644 --- a/scripts/commands/playlist/update.ts +++ b/scripts/commands/playlist/update.ts @@ -1,38 +1,33 @@ +import { DataLoader, DataProcessor, IssueLoader, PlaylistParser } from '../../core' import { Logger, Storage, Collection, Dictionary } from '@freearhey/core' +import type { DataProcessorData } from '../../types/dataProcessor' +import { Stream, Playlist, Channel, Issue } from '../../models' +import type { DataLoaderData } from '../../types/dataLoader' import { DATA_DIR, STREAMS_DIR } from '../../constants' -import { IssueLoader, PlaylistParser } from '../../core' -import { Stream, Playlist, Channel, Feed, Issue } from '../../models' import validUrl from 'valid-url' -import { uniqueId } from 'lodash' let processedIssues = new Collection() async function main() { const logger = new Logger({ disabled: true }) - const loader = new IssueLoader() + const issueLoader = new IssueLoader() logger.info('loading issues...') - const issues = await loader.load() + const issues = await issueLoader.load() - logger.info('loading channels from api...') + logger.info('loading data from api...') + const processor = new DataProcessor() const dataStorage = new Storage(DATA_DIR) - const channelsData = await dataStorage.json('channels.json') - const channels = new Collection(channelsData).map(data => new Channel(data)) - const channelsGroupedById = channels.keyBy((channel: Channel) => channel.id) - const feedsData = await dataStorage.json('feeds.json') - const feeds = new Collection(feedsData).map(data => - new Feed(data).withChannel(channelsGroupedById) - ) - const feedsGroupedByChannelId = feeds.groupBy((feed: Feed) => - feed.channel ? feed.channel.id : uniqueId() - ) + const dataLoader = new DataLoader({ storage: dataStorage }) + const data: DataLoaderData = await dataLoader.load() + const { channelsKeyById, feedsGroupedByChannelId }: DataProcessorData = processor.process(data) logger.info('loading streams...') const streamsStorage = new Storage(STREAMS_DIR) const parser = new PlaylistParser({ storage: streamsStorage, feedsGroupedByChannelId, - channelsGroupedById + channelsKeyById }) const files = await streamsStorage.list('**/*.m3u') const streams = await parser.parse(files) @@ -44,7 +39,7 @@ async function main() { await editStreams({ streams, issues, - channelsGroupedById, + channelsKeyById, feedsGroupedByChannelId }) @@ -52,7 +47,7 @@ async function main() { await addStreams({ streams, issues, - channelsGroupedById, + channelsKeyById, feedsGroupedByChannelId }) @@ -101,12 +96,12 @@ async function removeStreams({ streams, issues }: { streams: Collection; issues: async function editStreams({ streams, issues, - channelsGroupedById, + channelsKeyById, feedsGroupedByChannelId }: { streams: Collection issues: Collection - channelsGroupedById: Dictionary + channelsKeyById: Dictionary feedsGroupedByChannelId: Dictionary }) { const requests = issues.filter( @@ -129,7 +124,7 @@ async function editStreams({ stream .setChannelId(channelId) .setFeedId(feedId) - .withChannel(channelsGroupedById) + .withChannel(channelsKeyById) .withFeed(feedsGroupedByChannelId) .updateId() .updateName() @@ -143,8 +138,8 @@ async function editStreams({ if (data.has('label')) stream.setLabel(label) if (data.has('quality')) stream.setQuality(quality) - if (data.has('httpUserAgent')) stream.setHttpUserAgent(httpUserAgent) - if (data.has('httpReferrer')) stream.setHttpReferrer(httpReferrer) + if (data.has('httpUserAgent')) stream.setUserAgent(httpUserAgent) + if (data.has('httpReferrer')) stream.setReferrer(httpReferrer) processedIssues.add(issue.number) }) @@ -153,12 +148,12 @@ async function editStreams({ async function addStreams({ streams, issues, - channelsGroupedById, + channelsKeyById, feedsGroupedByChannelId }: { streams: Collection issues: Collection - channelsGroupedById: Dictionary + channelsKeyById: Dictionary feedsGroupedByChannelId: Dictionary }) { const requests = issues.filter( @@ -168,51 +163,32 @@ async function addStreams({ const data = issue.data if (data.missing('streamId') || data.missing('streamUrl')) return if (streams.includes((_stream: Stream) => _stream.url === data.getString('streamUrl'))) return - const stringUrl = data.getString('streamUrl') || '' - if (!isUri(stringUrl)) return + const streamUrl = data.getString('streamUrl') || '' + if (!isUri(streamUrl)) return const streamId = data.getString('streamId') || '' - const [channelId] = streamId.split('@') + const [channelId, feedId] = streamId.split('@') - const channel: Channel = channelsGroupedById.get(channelId) + const channel: Channel = channelsKeyById.get(channelId) if (!channel) return - const label = data.getString('label') || '' - const quality = data.getString('quality') || '' - const httpUserAgent = data.getString('httpUserAgent') || '' - const httpReferrer = data.getString('httpReferrer') || '' + const label = data.getString('label') || null + const quality = data.getString('quality') || null + const httpUserAgent = data.getString('httpUserAgent') || null + const httpReferrer = data.getString('httpReferrer') || null const stream = new Stream({ - tvg: { - id: streamId, - name: '', - url: '', - logo: '', - rec: '', - shift: '' - }, + channel: channelId, + feed: feedId, name: data.getString('channelName') || channel.name, - url: stringUrl, - group: { - title: '' - }, - http: { - 'user-agent': httpUserAgent, - referrer: httpReferrer - }, - line: -1, - raw: '', - timeshift: '', - catchup: { - type: '', - source: '', - days: '' - } + url: streamUrl, + user_agent: httpUserAgent, + referrer: httpReferrer, + quality, + label }) - .withChannel(channelsGroupedById) + .withChannel(channelsKeyById) .withFeed(feedsGroupedByChannelId) - .setLabel(label) - .setQuality(quality) .updateName() .updateFilepath() diff --git a/scripts/commands/playlist/validate.ts b/scripts/commands/playlist/validate.ts index 6296b5651f..57bca4f6fb 100644 --- a/scripts/commands/playlist/validate.ts +++ b/scripts/commands/playlist/validate.ts @@ -1,10 +1,11 @@ import { Logger, Storage, Collection, Dictionary } from '@freearhey/core' -import { PlaylistParser } from '../../core' -import { Channel, Stream, Blocked, Feed } from '../../models' +import { DataLoader, DataProcessor, PlaylistParser } from '../../core' +import { DataProcessorData } from '../../types/dataProcessor' +import { DATA_DIR, STREAMS_DIR } from '../../constants' +import { DataLoaderData } from '../../types/dataLoader' +import { BlocklistRecord, Stream } from '../../models' import { program } from 'commander' import chalk from 'chalk' -import { uniqueId } from 'lodash' -import { DATA_DIR, STREAMS_DIR } from '../../constants' program.argument('[filepath]', 'Path to file to validate').parse(process.argv) @@ -18,26 +19,21 @@ async function main() { const logger = new Logger() logger.info('loading data from api...') + const processor = new DataProcessor() const dataStorage = new Storage(DATA_DIR) - const channelsData = await dataStorage.json('channels.json') - const channels = new Collection(channelsData).map(data => new Channel(data)) - const channelsGroupedById = channels.keyBy((channel: Channel) => channel.id) - const feedsData = await dataStorage.json('feeds.json') - const feeds = new Collection(feedsData).map(data => - new Feed(data).withChannel(channelsGroupedById) - ) - const feedsGroupedByChannelId = feeds.groupBy((feed: Feed) => - feed.channel ? feed.channel.id : uniqueId() - ) - const blocklistContent = await dataStorage.json('blocklist.json') - const blocklist = new Collection(blocklistContent).map(data => new Blocked(data)) - const blocklistGroupedByChannelId = blocklist.keyBy((blocked: Blocked) => blocked.channelId) + const loader = new DataLoader({ storage: dataStorage }) + const data: DataLoaderData = await loader.load() + const { + channelsKeyById, + feedsGroupedByChannelId, + blocklistRecordsGroupedByChannelId + }: DataProcessorData = processor.process(data) logger.info('loading streams...') const streamsStorage = new Storage(STREAMS_DIR) const parser = new PlaylistParser({ storage: streamsStorage, - channelsGroupedById, + channelsKeyById, feedsGroupedByChannelId }) const files = program.args.length ? program.args : await streamsStorage.list('**/*.m3u') @@ -55,11 +51,11 @@ async function main() { const buffer = new Dictionary() streams.forEach((stream: Stream) => { if (stream.channelId) { - const channel = channelsGroupedById.get(stream.channelId) + const channel = channelsKeyById.get(stream.channelId) if (!channel) { log.add({ type: 'warning', - line: stream.line, + line: stream.getLine(), message: `"${stream.id}" is not in the database` }) } @@ -69,29 +65,32 @@ async function main() { if (duplicate) { log.add({ type: 'warning', - line: stream.line, + line: stream.getLine(), message: `"${stream.url}" is already on the playlist` }) } else { buffer.set(stream.url, true) } - const blocked = stream.channel ? blocklistGroupedByChannelId.get(stream.channel.id) : false - if (blocked) { - if (blocked.reason === 'dmca') { + const blocklistRecords = stream.channel + ? new Collection(blocklistRecordsGroupedByChannelId.get(stream.channel.id)) + : new Collection() + + blocklistRecords.forEach((blocklistRecord: BlocklistRecord) => { + if (blocklistRecord.reason === 'dmca') { log.add({ type: 'error', - line: stream.line, - message: `"${blocked.channelId}" is on the blocklist due to claims of copyright holders (${blocked.ref})` + line: stream.getLine(), + message: `"${blocklistRecord.channelId}" is on the blocklist due to claims of copyright holders (${blocklistRecord.ref})` }) - } else if (blocked.reason === 'nsfw') { + } else if (blocklistRecord.reason === 'nsfw') { log.add({ type: 'error', - line: stream.line, - message: `"${blocked.channelId}" is on the blocklist due to NSFW content (${blocked.ref})` + line: stream.getLine(), + message: `"${blocklistRecord.channelId}" is on the blocklist due to NSFW content (${blocklistRecord.ref})` }) } - } + }) }) if (log.notEmpty()) { diff --git a/scripts/commands/report/create.ts b/scripts/commands/report/create.ts index 7584105d11..4ad272de34 100644 --- a/scripts/commands/report/create.ts +++ b/scripts/commands/report/create.ts @@ -1,44 +1,41 @@ +import { DataLoader, DataProcessor, IssueLoader, PlaylistParser } from '../../core' import { Logger, Storage, Collection, Dictionary } from '@freearhey/core' +import { DataProcessorData } from '../../types/dataProcessor' import { DATA_DIR, STREAMS_DIR } from '../../constants' -import { IssueLoader, PlaylistParser } from '../../core' -import { Blocked, Channel, Issue, Stream, Feed } from '../../models' -import { uniqueId } from 'lodash' +import { DataLoaderData } from '../../types/dataLoader' +import { Issue, Stream } from '../../models' async function main() { const logger = new Logger() - const loader = new IssueLoader() + const issueLoader = new IssueLoader() let report = new Collection() logger.info('loading issues...') - const issues = await loader.load() + const issues = await issueLoader.load() logger.info('loading data from api...') + const processor = new DataProcessor() const dataStorage = new Storage(DATA_DIR) - const channelsData = await dataStorage.json('channels.json') - const channels = new Collection(channelsData).map(data => new Channel(data)) - const channelsGroupedById = channels.keyBy((channel: Channel) => channel.id) - const feedsData = await dataStorage.json('feeds.json') - const feeds = new Collection(feedsData).map(data => - new Feed(data).withChannel(channelsGroupedById) - ) - const feedsGroupedByChannelId = feeds.groupBy((feed: Feed) => - feed.channel ? feed.channel.id : uniqueId() - ) - const blocklistContent = await dataStorage.json('blocklist.json') - const blocklist = new Collection(blocklistContent).map(data => new Blocked(data)) - const blocklistGroupedByChannelId = blocklist.keyBy((blocked: Blocked) => blocked.channelId) + const dataLoader = new DataLoader({ storage: dataStorage }) + const data: DataLoaderData = await dataLoader.load() + const { + channelsKeyById, + feedsGroupedByChannelId, + blocklistRecordsGroupedByChannelId + }: DataProcessorData = processor.process(data) logger.info('loading streams...') const streamsStorage = new Storage(STREAMS_DIR) const parser = new PlaylistParser({ storage: streamsStorage, - channelsGroupedById, + channelsKeyById, feedsGroupedByChannelId }) const files = await streamsStorage.list('**/*.m3u') const streams = await parser.parse(files) const streamsGroupedByUrl = streams.groupBy((stream: Stream) => stream.url) const streamsGroupedByChannelId = streams.groupBy((stream: Stream) => stream.channelId) + const streamsGroupedById = streams.groupBy((stream: Stream) => stream.getId()) logger.info('checking broken streams reports...') const brokenStreamReports = issues.filter(issue => @@ -94,8 +91,8 @@ async function main() { if (!channelId) result.status = 'missing_id' else if (!streamUrl) result.status = 'missing_link' - else if (blocklistGroupedByChannelId.has(channelId)) result.status = 'blocked' - else if (channelsGroupedById.missing(channelId)) result.status = 'wrong_id' + else if (blocklistRecordsGroupedByChannelId.has(channelId)) result.status = 'blocked' + else if (channelsKeyById.missing(channelId)) result.status = 'wrong_id' else if (streamsGroupedByUrl.has(streamUrl)) result.status = 'on_playlist' else if (addRequestsBuffer.has(streamUrl)) result.status = 'duplicate' else result.status = 'pending' @@ -124,7 +121,7 @@ async function main() { if (!streamUrl) result.status = 'missing_link' else if (streamsGroupedByUrl.missing(streamUrl)) result.status = 'invalid_link' - else if (channelId && channelsGroupedById.missing(channelId)) result.status = 'invalid_id' + else if (channelId && channelsKeyById.missing(channelId)) result.status = 'invalid_id' report.add(result) }) @@ -147,16 +144,16 @@ async function main() { } if (!channelId) result.status = 'missing_id' - else if (channelsGroupedById.missing(channelId)) result.status = 'invalid_id' - else if (channelSearchRequestsBuffer.has(channelId)) result.status = 'duplicate' - else if (blocklistGroupedByChannelId.has(channelId)) result.status = 'blocked' - else if (streamsGroupedByChannelId.has(channelId)) result.status = 'fulfilled' + else if (channelsKeyById.missing(channelId)) result.status = 'invalid_id' + else if (channelSearchRequestsBuffer.has(streamId)) result.status = 'duplicate' + else if (blocklistRecordsGroupedByChannelId.has(channelId)) result.status = 'blocked' + else if (streamsGroupedById.has(streamId)) result.status = 'fulfilled' else { - const channelData = channelsGroupedById.get(channelId) + const channelData = channelsKeyById.get(channelId) if (channelData.length && channelData[0].closed) result.status = 'closed' } - channelSearchRequestsBuffer.set(channelId, true) + channelSearchRequestsBuffer.set(streamId, true) report.add(result) }) diff --git a/scripts/core/apiClient.ts b/scripts/core/apiClient.ts index 3b62919082..e4815a81aa 100644 --- a/scripts/core/apiClient.ts +++ b/scripts/core/apiClient.ts @@ -1,59 +1,16 @@ -import { Logger, Storage } from '@freearhey/core' -import axios, { AxiosInstance, AxiosResponse, AxiosProgressEvent } from 'axios' -import cliProgress, { MultiBar } from 'cli-progress' -import numeral from 'numeral' +import axios, { AxiosInstance, AxiosResponse, AxiosRequestConfig } from 'axios' export class ApiClient { - progressBar: MultiBar - client: AxiosInstance - storage: Storage - logger: Logger + instance: AxiosInstance - constructor({ logger }: { logger: Logger }) { - this.logger = logger - this.client = axios.create({ + constructor() { + this.instance = axios.create({ + baseURL: 'https://iptv-org.github.io/api', responseType: 'stream' }) - this.storage = new Storage() - this.progressBar = new cliProgress.MultiBar({ - stopOnComplete: true, - hideCursor: true, - forceRedraw: true, - barsize: 36, - format(options, params, payload) { - const filename = payload.filename.padEnd(18, ' ') - const barsize = options.barsize || 40 - const percent = (params.progress * 100).toFixed(2) - const speed = payload.speed ? numeral(payload.speed).format('0.0 b') + '/s' : 'N/A' - const total = numeral(params.total).format('0.0 b') - const completeSize = Math.round(params.progress * barsize) - const incompleteSize = barsize - completeSize - const bar = - options.barCompleteString && options.barIncompleteString - ? options.barCompleteString.substr(0, completeSize) + - options.barGlue + - options.barIncompleteString.substr(0, incompleteSize) - : '-'.repeat(barsize) - - return `${filename} [${bar}] ${percent}% | ETA: ${params.eta}s | ${total} | ${speed}` - } - }) } - async download(filename: string) { - const stream = await this.storage.createStream(`temp/data/${filename}`) - - const bar = this.progressBar.create(0, 0, { filename }) - - this.client - .get(`https://iptv-org.github.io/api/${filename}`, { - onDownloadProgress({ total, loaded, rate }: AxiosProgressEvent) { - if (total) bar.setTotal(total) - bar.update(loaded, { speed: rate }) - } - }) - .then((response: AxiosResponse) => { - response.data.pipe(stream) - }) + get(url: string, options: AxiosRequestConfig): Promise { + return this.instance.get(url, options) } } diff --git a/scripts/core/dataLoader.ts b/scripts/core/dataLoader.ts new file mode 100644 index 0000000000..2379edc9ee --- /dev/null +++ b/scripts/core/dataLoader.ts @@ -0,0 +1,100 @@ +import { ApiClient } from './apiClient' +import { Storage } from '@freearhey/core' +import cliProgress, { MultiBar } from 'cli-progress' +import numeral from 'numeral' +import type { DataLoaderProps, DataLoaderData } from '../types/dataLoader' + +export class DataLoader { + client: ApiClient + storage: Storage + progressBar: MultiBar + + constructor(props: DataLoaderProps) { + this.client = new ApiClient() + this.storage = props.storage + this.progressBar = new cliProgress.MultiBar({ + stopOnComplete: true, + hideCursor: true, + forceRedraw: true, + barsize: 36, + format(options, params, payload) { + const filename = payload.filename.padEnd(18, ' ') + const barsize = options.barsize || 40 + const percent = (params.progress * 100).toFixed(2) + const speed = payload.speed ? numeral(payload.speed).format('0.0 b') + '/s' : 'N/A' + const total = numeral(params.total).format('0.0 b') + const completeSize = Math.round(params.progress * barsize) + const incompleteSize = barsize - completeSize + const bar = + options.barCompleteString && options.barIncompleteString + ? options.barCompleteString.substr(0, completeSize) + + options.barGlue + + options.barIncompleteString.substr(0, incompleteSize) + : '-'.repeat(barsize) + + return `${filename} [${bar}] ${percent}% | ETA: ${params.eta}s | ${total} | ${speed}` + } + }) + } + + async load(): Promise { + const [ + countries, + regions, + subdivisions, + languages, + categories, + blocklist, + channels, + feeds, + timezones, + guides, + streams + ] = await Promise.all([ + this.storage.json('countries.json'), + this.storage.json('regions.json'), + this.storage.json('subdivisions.json'), + this.storage.json('languages.json'), + this.storage.json('categories.json'), + this.storage.json('blocklist.json'), + this.storage.json('channels.json'), + this.storage.json('feeds.json'), + this.storage.json('timezones.json'), + this.storage.json('guides.json'), + this.storage.json('streams.json') + ]) + + return { + countries, + regions, + subdivisions, + languages, + categories, + blocklist, + channels, + feeds, + timezones, + guides, + streams + } + } + + async download(filename: string) { + if (!this.storage || !this.progressBar) return + + const stream = await this.storage.createStream(filename) + const progressBar = this.progressBar.create(0, 0, { filename }) + + this.client + .get(filename, { + responseType: 'stream', + onDownloadProgress({ total, loaded, rate }) { + if (total) progressBar.setTotal(total) + progressBar.update(loaded, { speed: rate }) + } + }) + .then(response => { + response.data.pipe(stream) + }) + } +} diff --git a/scripts/core/dataProcessor.ts b/scripts/core/dataProcessor.ts new file mode 100644 index 0000000000..3290fe5b30 --- /dev/null +++ b/scripts/core/dataProcessor.ts @@ -0,0 +1,110 @@ +import { DataLoaderData } from '../types/dataLoader' +import { Collection } from '@freearhey/core' +import { + BlocklistRecord, + Subdivision, + Category, + Language, + Timezone, + Channel, + Country, + Region, + Stream, + Guide, + Feed +} from '../models' + +export class DataProcessor { + constructor() {} + + process(data: DataLoaderData) { + const categories = new Collection(data.categories).map(data => new Category(data)) + const categoriesKeyById = categories.keyBy((category: Category) => category.id) + + const subdivisions = new Collection(data.subdivisions).map(data => new Subdivision(data)) + const subdivisionsKeyByCode = subdivisions.keyBy((subdivision: Subdivision) => subdivision.code) + const subdivisionsGroupedByCountryCode = subdivisions.groupBy( + (subdivision: Subdivision) => subdivision.countryCode + ) + + let regions = new Collection(data.regions).map(data => new Region(data)) + const regionsKeyByCode = regions.keyBy((region: Region) => region.code) + + const blocklistRecords = new Collection(data.blocklist).map(data => new BlocklistRecord(data)) + const blocklistRecordsGroupedByChannelId = blocklistRecords.groupBy( + (blocklistRecord: BlocklistRecord) => blocklistRecord.channelId + ) + + const streams = new Collection(data.streams).map(data => new Stream(data)) + const streamsGroupedById = streams.groupBy((stream: Stream) => stream.getId()) + + const guides = new Collection(data.guides).map(data => new Guide(data)) + const guidesGroupedByStreamId = guides.groupBy((guide: Guide) => guide.getStreamId()) + + const languages = new Collection(data.languages).map(data => new Language(data)) + const languagesKeyByCode = languages.keyBy((language: Language) => language.code) + + const countries = new Collection(data.countries).map(data => + new Country(data) + .withRegions(regions) + .withLanguage(languagesKeyByCode) + .withSubdivisions(subdivisionsGroupedByCountryCode) + ) + const countriesKeyByCode = countries.keyBy((country: Country) => country.code) + + regions = regions.map((region: Region) => region.withCountries(countriesKeyByCode)) + + const timezones = new Collection(data.timezones).map(data => + new Timezone(data).withCountries(countriesKeyByCode) + ) + const timezonesKeyById = timezones.keyBy((timezone: Timezone) => timezone.id) + + let channels = new Collection(data.channels).map(data => + new Channel(data) + .withCategories(categoriesKeyById) + .withCountry(countriesKeyByCode) + .withSubdivision(subdivisionsKeyByCode) + .withCategories(categoriesKeyById) + ) + const channelsKeyById = channels.keyBy((channel: Channel) => channel.id) + + let feeds = new Collection(data.feeds).map(data => + new Feed(data) + .withChannel(channelsKeyById) + .withLanguages(languagesKeyByCode) + .withTimezones(timezonesKeyById) + .withBroadcastCountries(countriesKeyByCode, regionsKeyByCode, subdivisionsKeyByCode) + .withBroadcastRegions(regions) + .withBroadcastSubdivisions(subdivisionsKeyByCode) + ) + const feedsGroupedByChannelId = feeds.groupBy((feed: Feed) => feed.channelId) + + channels = channels.map((channel: Channel) => channel.withFeeds(feedsGroupedByChannelId)) + + return { + blocklistRecordsGroupedByChannelId, + subdivisionsGroupedByCountryCode, + feedsGroupedByChannelId, + guidesGroupedByStreamId, + subdivisionsKeyByCode, + countriesKeyByCode, + languagesKeyByCode, + streamsGroupedById, + categoriesKeyById, + timezonesKeyById, + regionsKeyByCode, + blocklistRecords, + channelsKeyById, + subdivisions, + categories, + countries, + languages, + timezones, + channels, + regions, + streams, + guides, + feeds + } + } +} diff --git a/scripts/core/index.ts b/scripts/core/index.ts index cdac07f58e..d322373100 100644 --- a/scripts/core/index.ts +++ b/scripts/core/index.ts @@ -1,11 +1,13 @@ -export * from './playlistParser' -export * from './numberParser' -export * from './logParser' -export * from './markdown' +export * from './apiClient' +export * from './cliTable' +export * from './dataProcessor' +export * from './dataLoader' +export * from './htmlTable' +export * from './issueData' export * from './issueLoader' export * from './issueParser' -export * from './htmlTable' -export * from './apiClient' -export * from './issueData' +export * from './logParser' +export * from './markdown' +export * from './numberParser' +export * from './playlistParser' export * from './streamTester' -export * from './cliTable' diff --git a/scripts/core/playlistParser.ts b/scripts/core/playlistParser.ts index b28876663e..7d388dd209 100644 --- a/scripts/core/playlistParser.ts +++ b/scripts/core/playlistParser.ts @@ -5,18 +5,18 @@ import { Stream } from '../models' type PlaylistPareserProps = { storage: Storage feedsGroupedByChannelId: Dictionary - channelsGroupedById: Dictionary + channelsKeyById: Dictionary } export class PlaylistParser { storage: Storage feedsGroupedByChannelId: Dictionary - channelsGroupedById: Dictionary + channelsKeyById: Dictionary - constructor({ storage, feedsGroupedByChannelId, channelsGroupedById }: PlaylistPareserProps) { + constructor({ storage, feedsGroupedByChannelId, channelsKeyById }: PlaylistPareserProps) { this.storage = storage this.feedsGroupedByChannelId = feedsGroupedByChannelId - this.channelsGroupedById = channelsGroupedById + this.channelsKeyById = channelsKeyById } async parse(files: string[]): Promise { @@ -35,9 +35,10 @@ export class PlaylistParser { const parsed: parser.Playlist = parser.parse(content) const streams = new Collection(parsed.items).map((data: parser.PlaylistItem) => { - const stream = new Stream(data) + const stream = new Stream() + .fromPlaylistItem(data) .withFeed(this.feedsGroupedByChannelId) - .withChannel(this.channelsGroupedById) + .withChannel(this.channelsKeyById) .setFilepath(filepath) return stream diff --git a/scripts/models/blocked.ts b/scripts/models/blocked.ts deleted file mode 100644 index 29041278b1..0000000000 --- a/scripts/models/blocked.ts +++ /dev/null @@ -1,17 +0,0 @@ -type BlockedProps = { - channel: string - reason: string - ref: string -} - -export class Blocked { - channelId: string - reason: string - ref: string - - constructor(data: BlockedProps) { - this.channelId = data.channel - this.reason = data.reason - this.ref = data.ref - } -} diff --git a/scripts/models/blocklistRecord.ts b/scripts/models/blocklistRecord.ts new file mode 100644 index 0000000000..632a1d4ddc --- /dev/null +++ b/scripts/models/blocklistRecord.ts @@ -0,0 +1,15 @@ +import type { BlocklistRecordData } from '../types/blocklistRecord' + +export class BlocklistRecord { + channelId: string + reason: string + ref: string + + constructor(data?: BlocklistRecordData) { + if (!data) return + + this.channelId = data.channel + this.reason = data.reason + this.ref = data.ref + } +} diff --git a/scripts/models/category.ts b/scripts/models/category.ts index 17ff9af122..5b228a86d0 100644 --- a/scripts/models/category.ts +++ b/scripts/models/category.ts @@ -1,7 +1,4 @@ -type CategoryData = { - id: string - name: string -} +import type { CategoryData, CategorySerializedData } from '../types/category' export class Category { id: string @@ -11,4 +8,11 @@ export class Category { this.id = data.id this.name = data.name } + + serialize(): CategorySerializedData { + return { + id: this.id, + name: this.name + } + } } diff --git a/scripts/models/channel.ts b/scripts/models/channel.ts index 1d4c5cf8d6..cdc09af0ad 100644 --- a/scripts/models/channel.ts +++ b/scripts/models/channel.ts @@ -1,23 +1,6 @@ import { Collection, Dictionary } from '@freearhey/core' -import { Category, Country, Subdivision } from './index' - -type ChannelData = { - id: string - name: string - alt_names: string[] - network: string - owners: Collection - country: string - subdivision: string - city: string - categories: Collection - is_nsfw: boolean - launched: string - closed: string - replaced_by: string - website: string - logo: string -} +import { Category, Country, Feed, Guide, Stream, Subdivision } from './index' +import type { ChannelData, ChannelSearchableData, ChannelSerializedData } from '../types/channel' export class Channel { id: string @@ -31,15 +14,18 @@ export class Channel { subdivision?: Subdivision cityName?: string categoryIds: Collection - categories?: Collection + categories: Collection = new Collection() isNSFW: boolean launched?: string closed?: string replacedBy?: string website?: string logo: string + feeds?: Collection + + constructor(data?: ChannelData) { + if (!data) return - constructor(data: ChannelData) { this.id = data.id this.name = data.name this.altNames = new Collection(data.alt_names) @@ -57,28 +43,34 @@ export class Channel { this.logo = data.logo } - withSubdivision(subdivisionsGroupedByCode: Dictionary): this { + withSubdivision(subdivisionsKeyByCode: Dictionary): this { if (!this.subdivisionCode) return this - this.subdivision = subdivisionsGroupedByCode.get(this.subdivisionCode) + this.subdivision = subdivisionsKeyByCode.get(this.subdivisionCode) return this } - withCountry(countriesGroupedByCode: Dictionary): this { - this.country = countriesGroupedByCode.get(this.countryCode) + withCountry(countriesKeyByCode: Dictionary): this { + this.country = countriesKeyByCode.get(this.countryCode) return this } - withCategories(groupedCategories: Dictionary): this { + withCategories(categoriesKeyById: Dictionary): this { this.categories = this.categoryIds - .map((id: string) => groupedCategories.get(id)) + .map((id: string) => categoriesKeyById.get(id)) .filter(Boolean) return this } + withFeeds(feedsGroupedByChannelId: Dictionary): this { + this.feeds = new Collection(feedsGroupedByChannelId.get(this.id)) + + return this + } + getCountry(): Country | undefined { return this.country } @@ -102,7 +94,106 @@ export class Channel { ) } + getFeeds(): Collection { + if (!this.feeds) return new Collection() + + return this.feeds + } + + getGuides(): Collection { + let guides = new Collection() + + this.getFeeds().forEach((feed: Feed) => { + guides = guides.concat(feed.getGuides()) + }) + + return guides + } + + getGuideNames(): Collection { + return this.getGuides() + .map((guide: Guide) => guide.siteName) + .uniq() + } + + getStreams(): Collection { + let streams = new Collection() + + this.getFeeds().forEach((feed: Feed) => { + streams = streams.concat(feed.getStreams()) + }) + + return streams + } + + getStreamNames(): Collection { + return this.getStreams() + .map((stream: Stream) => stream.getName()) + .uniq() + } + + getFeedFullNames(): Collection { + return this.getFeeds() + .map((feed: Feed) => feed.getFullName()) + .uniq() + } + isSFW(): boolean { return this.isNSFW === false } + + getSearchable(): ChannelSearchableData { + return { + id: this.id, + name: this.name, + altNames: this.altNames.all(), + guideNames: this.getGuideNames().all(), + streamNames: this.getStreamNames().all(), + feedFullNames: this.getFeedFullNames().all() + } + } + + serialize(): ChannelSerializedData { + return { + id: this.id, + name: this.name, + altNames: this.altNames.all(), + network: this.network, + owners: this.owners.all(), + countryCode: this.countryCode, + country: this.country ? this.country.serialize() : undefined, + subdivisionCode: this.subdivisionCode, + subdivision: this.subdivision ? this.subdivision.serialize() : undefined, + cityName: this.cityName, + categoryIds: this.categoryIds.all(), + categories: this.categories.map((category: Category) => category.serialize()).all(), + isNSFW: this.isNSFW, + launched: this.launched, + closed: this.closed, + replacedBy: this.replacedBy, + website: this.website, + logo: this.logo + } + } + + deserialize(data: ChannelSerializedData): this { + this.id = data.id + this.name = data.name + this.altNames = new Collection(data.altNames) + this.network = data.network + this.owners = new Collection(data.owners) + this.countryCode = data.countryCode + this.country = data.country ? new Country().deserialize(data.country) : undefined + this.subdivisionCode = data.subdivisionCode + this.cityName = data.cityName + this.categoryIds = new Collection(data.categoryIds) + this.isNSFW = data.isNSFW + this.launched = data.launched + this.closed = data.closed + this.replacedBy = data.replacedBy + this.website = data.website + this.logo = data.logo + + return this + } } diff --git a/scripts/models/country.ts b/scripts/models/country.ts index ac822a2350..780c4413f1 100644 --- a/scripts/models/country.ts +++ b/scripts/models/country.ts @@ -1,12 +1,8 @@ import { Collection, Dictionary } from '@freearhey/core' -import { Region, Language } from '.' - -type CountryData = { - code: string - name: string - lang: string - flag: string -} +import { Region, Language, Subdivision } from '.' +import type { CountryData, CountrySerializedData } from '../types/country' +import { SubdivisionSerializedData } from '../types/subdivision' +import { RegionSerializedData } from '../types/region' export class Country { code: string @@ -17,7 +13,9 @@ export class Country { subdivisions?: Collection regions?: Collection - constructor(data: CountryData) { + constructor(data?: CountryData) { + if (!data) return + this.code = data.code this.name = data.name this.flag = data.flag @@ -38,8 +36,8 @@ export class Country { return this } - withLanguage(languagesGroupedByCode: Dictionary): this { - this.language = languagesGroupedByCode.get(this.languageCode) + withLanguage(languagesKeyByCode: Dictionary): this { + this.language = languagesKeyByCode.get(this.languageCode) return this } @@ -55,4 +53,34 @@ export class Country { getSubdivisions(): Collection { return this.subdivisions || new Collection() } + + serialize(): CountrySerializedData { + return { + code: this.code, + name: this.name, + flag: this.flag, + languageCode: this.languageCode, + language: this.language ? this.language.serialize() : null, + subdivisions: this.subdivisions + ? this.subdivisions.map((subdivision: Subdivision) => subdivision.serialize()).all() + : [], + regions: this.regions ? this.regions.map((region: Region) => region.serialize()).all() : [] + } + } + + deserialize(data: CountrySerializedData): this { + this.code = data.code + this.name = data.name + this.flag = data.flag + this.languageCode = data.languageCode + this.language = data.language ? new Language().deserialize(data.language) : undefined + this.subdivisions = new Collection(data.subdivisions).map((data: SubdivisionSerializedData) => + new Subdivision().deserialize(data) + ) + this.regions = new Collection(data.regions).map((data: RegionSerializedData) => + new Region().deserialize(data) + ) + + return this + } } diff --git a/scripts/models/feed.ts b/scripts/models/feed.ts index 03e34762bc..03ae31184e 100644 --- a/scripts/models/feed.ts +++ b/scripts/models/feed.ts @@ -1,16 +1,6 @@ import { Collection, Dictionary } from '@freearhey/core' import { Country, Language, Region, Channel, Subdivision } from './index' - -type FeedData = { - channel: string - id: string - name: string - is_main: boolean - broadcast_area: Collection - languages: Collection - timezones: Collection - video_format: string -} +import type { FeedData } from '../types/feed' export class Feed { channelId: string @@ -30,6 +20,8 @@ export class Feed { timezoneIds: Collection timezones?: Collection videoFormat: string + guides?: Collection + streams?: Collection constructor(data: FeedData) { this.channelId = data.channel @@ -61,40 +53,58 @@ export class Feed { }) } - withChannel(channelsGroupedById: Dictionary): this { - this.channel = channelsGroupedById.get(this.channelId) + withChannel(channelsKeyById: Dictionary): this { + this.channel = channelsKeyById.get(this.channelId) return this } - withLanguages(languagesGroupedByCode: Dictionary): this { + withStreams(streamsGroupedById: Dictionary): this { + this.streams = new Collection(streamsGroupedById.get(`${this.channelId}@${this.id}`)) + + if (this.isMain) { + this.streams = this.streams.concat(new Collection(streamsGroupedById.get(this.channelId))) + } + + return this + } + + withGuides(guidesGroupedByStreamId: Dictionary): this { + this.guides = new Collection(guidesGroupedByStreamId.get(`${this.channelId}@${this.id}`)) + + if (this.isMain) { + this.guides = this.guides.concat(new Collection(guidesGroupedByStreamId.get(this.channelId))) + } + + return this + } + + withLanguages(languagesKeyByCode: Dictionary): this { this.languages = this.languageCodes - .map((code: string) => languagesGroupedByCode.get(code)) + .map((code: string) => languagesKeyByCode.get(code)) .filter(Boolean) return this } - withTimezones(timezonesGroupedById: Dictionary): this { - this.timezones = this.timezoneIds - .map((id: string) => timezonesGroupedById.get(id)) - .filter(Boolean) + withTimezones(timezonesKeyById: Dictionary): this { + this.timezones = this.timezoneIds.map((id: string) => timezonesKeyById.get(id)).filter(Boolean) return this } - withBroadcastSubdivisions(subdivisionsGroupedByCode: Dictionary): this { + withBroadcastSubdivisions(subdivisionsKeyByCode: Dictionary): this { this.broadcastSubdivisions = this.broadcastSubdivisionCodes.map((code: string) => - subdivisionsGroupedByCode.get(code) + subdivisionsKeyByCode.get(code) ) return this } withBroadcastCountries( - countriesGroupedByCode: Dictionary, - regionsGroupedByCode: Dictionary, - subdivisionsGroupedByCode: Dictionary + countriesKeyByCode: Dictionary, + regionsKeyByCode: Dictionary, + subdivisionsKeyByCode: Dictionary ): this { let broadcastCountries = new Collection() @@ -104,22 +114,22 @@ export class Feed { } this.broadcastCountryCodes.forEach((code: string) => { - broadcastCountries.add(countriesGroupedByCode.get(code)) + broadcastCountries.add(countriesKeyByCode.get(code)) }) this.broadcastRegionCodes.forEach((code: string) => { - const region: Region = regionsGroupedByCode.get(code) + const region: Region = regionsKeyByCode.get(code) if (region) { region.countryCodes.forEach((countryCode: string) => { - broadcastCountries.add(countriesGroupedByCode.get(countryCode)) + broadcastCountries.add(countriesKeyByCode.get(countryCode)) }) } }) this.broadcastSubdivisionCodes.forEach((code: string) => { - const subdivision: Subdivision = subdivisionsGroupedByCode.get(code) + const subdivision: Subdivision = subdivisionsKeyByCode.get(code) if (subdivision) { - broadcastCountries.add(countriesGroupedByCode.get(subdivision.countryCode)) + broadcastCountries.add(countriesKeyByCode.get(subdivision.countryCode)) } }) @@ -197,4 +207,22 @@ export class Feed { return this.getBroadcastRegions().includes((_region: Region) => _region.code === region.code) } + + getGuides(): Collection { + if (!this.guides) return new Collection() + + return this.guides + } + + getStreams(): Collection { + if (!this.streams) return new Collection() + + return this.streams + } + + getFullName(): string { + if (!this.channel) return '' + + return `${this.channel.name} ${this.name}` + } } diff --git a/scripts/models/guide.ts b/scripts/models/guide.ts new file mode 100644 index 0000000000..3bc849d86e --- /dev/null +++ b/scripts/models/guide.ts @@ -0,0 +1,54 @@ +import type { GuideData, GuideSerializedData } from '../types/guide' + +export class Guide { + channelId?: string + feedId?: string + siteDomain: string + siteId: string + siteName: string + languageCode: string + + constructor(data?: GuideData) { + if (!data) return + + this.channelId = data.channel + this.feedId = data.feed + this.siteDomain = data.site + this.siteId = data.site_id + this.siteName = data.site_name + this.languageCode = data.lang + } + + getUUID(): string { + return this.getStreamId() + this.siteId + } + + getStreamId(): string | undefined { + if (!this.channelId) return undefined + if (!this.feedId) return this.channelId + + return `${this.channelId}@${this.feedId}` + } + + serialize(): GuideSerializedData { + return { + channelId: this.channelId, + feedId: this.feedId, + siteDomain: this.siteDomain, + siteId: this.siteId, + siteName: this.siteName, + languageCode: this.languageCode + } + } + + deserialize(data: GuideSerializedData): this { + this.channelId = data.channelId + this.feedId = data.feedId + this.siteDomain = data.siteDomain + this.siteId = data.siteId + this.siteName = data.siteName + this.languageCode = data.languageCode + + return this + } +} diff --git a/scripts/models/index.ts b/scripts/models/index.ts index 83a9380ed8..db4d6f5fa8 100644 --- a/scripts/models/index.ts +++ b/scripts/models/index.ts @@ -1,13 +1,14 @@ -export * from './issue' -export * from './playlist' -export * from './blocked' -export * from './stream' +export * from './blocklistRecord' +export * from './broadcastArea' export * from './category' export * from './channel' -export * from './language' export * from './country' -export * from './region' -export * from './subdivision' export * from './feed' -export * from './broadcastArea' +export * from './guide' +export * from './issue' +export * from './language' +export * from './playlist' +export * from './region' +export * from './stream' +export * from './subdivision' export * from './timezone' diff --git a/scripts/models/language.ts b/scripts/models/language.ts index aeda5e6c22..1e6df829bc 100644 --- a/scripts/models/language.ts +++ b/scripts/models/language.ts @@ -1,14 +1,27 @@ -type LanguageData = { - code: string - name: string -} +import type { LanguageData, LanguageSerializedData } from '../types/language' export class Language { code: string name: string - constructor(data: LanguageData) { + constructor(data?: LanguageData) { + if (!data) return + this.code = data.code this.name = data.name } + + serialize(): LanguageSerializedData { + return { + code: this.code, + name: this.name + } + } + + deserialize(data: LanguageSerializedData): this { + this.code = data.code + this.name = data.name + + return this + } } diff --git a/scripts/models/region.ts b/scripts/models/region.ts index 928b48f06f..ace44bc52f 100644 --- a/scripts/models/region.ts +++ b/scripts/models/region.ts @@ -1,27 +1,26 @@ import { Collection, Dictionary } from '@freearhey/core' -import { Subdivision } from '.' - -type RegionData = { - code: string - name: string - countries: string[] -} +import { Country, Subdivision } from '.' +import type { RegionData, RegionSerializedData } from '../types/region' +import { CountrySerializedData } from '../types/country' +import { SubdivisionSerializedData } from '../types/subdivision' export class Region { code: string name: string countryCodes: Collection - countries?: Collection - subdivisions?: Collection + countries: Collection = new Collection() + subdivisions: Collection = new Collection() + + constructor(data?: RegionData) { + if (!data) return - constructor(data: RegionData) { this.code = data.code this.name = data.name this.countryCodes = new Collection(data.countries) } - withCountries(countriesGroupedByCode: Dictionary): this { - this.countries = this.countryCodes.map((code: string) => countriesGroupedByCode.get(code)) + withCountries(countriesKeyByCode: Dictionary): this { + this.countries = this.countryCodes.map((code: string) => countriesKeyByCode.get(code)) return this } @@ -35,11 +34,11 @@ export class Region { } getSubdivisions(): Collection { - return this.subdivisions || new Collection() + return this.subdivisions } getCountries(): Collection { - return this.countries || new Collection() + return this.countries } includesCountryCode(code: string): boolean { @@ -49,4 +48,30 @@ export class Region { isWorldwide(): boolean { return this.code === 'INT' } + + serialize(): RegionSerializedData { + return { + code: this.code, + name: this.name, + countryCodes: this.countryCodes.all(), + countries: this.countries.map((country: Country) => country.serialize()).all(), + subdivisions: this.subdivisions + .map((subdivision: Subdivision) => subdivision.serialize()) + .all() + } + } + + deserialize(data: RegionSerializedData): this { + this.code = data.code + this.name = data.name + this.countryCodes = new Collection(data.countryCodes) + this.countries = new Collection(data.countries).map((data: CountrySerializedData) => + new Country().deserialize(data) + ) + this.subdivisions = new Collection(data.subdivisions).map((data: SubdivisionSerializedData) => + new Subdivision().deserialize(data) + ) + + return this + } } diff --git a/scripts/models/stream.ts b/scripts/models/stream.ts index bd01845f63..2de399efc7 100644 --- a/scripts/models/stream.ts +++ b/scripts/models/stream.ts @@ -1,26 +1,45 @@ -import { URL, Collection, Dictionary } from '@freearhey/core' import { Feed, Channel, Category, Region, Subdivision, Country, Language } from './index' +import { URL, Collection, Dictionary } from '@freearhey/core' +import type { StreamData } from '../types/stream' import parser from 'iptv-playlist-parser' export class Stream { - name: string + name?: string url: string id?: string - groupTitle: string channelId?: string channel?: Channel feedId?: string feed?: Feed filepath?: string - line: number + line?: number label?: string verticalResolution?: number isInterlaced?: boolean - httpReferrer?: string - httpUserAgent?: string + referrer?: string + userAgent?: string + groupTitle: string = 'Undefined' removed: boolean = false - constructor(data: parser.PlaylistItem) { + constructor(data?: StreamData) { + if (!data) return + + const id = data.channel && data.feed ? [data.channel, data.feed].join('@') : data.channel + const { verticalResolution, isInterlaced } = parseQuality(data.quality) + + this.id = id || undefined + this.channelId = data.channel || undefined + this.feedId = data.feed || undefined + this.name = data.name || undefined + this.url = data.url + this.referrer = data.referrer || undefined + this.userAgent = data.user_agent || undefined + this.verticalResolution = verticalResolution || undefined + this.isInterlaced = isInterlaced || undefined + this.label = data.label || undefined + } + + fromPlaylistItem(data: parser.PlaylistItem): this { if (!data.name) throw new Error('"name" property is required') if (!data.url) throw new Error('"url" property is required') @@ -37,15 +56,16 @@ export class Stream { this.verticalResolution = verticalResolution || undefined this.isInterlaced = isInterlaced || undefined this.url = data.url - this.httpReferrer = data.http.referrer || undefined - this.httpUserAgent = data.http['user-agent'] || undefined - this.groupTitle = 'Undefined' + this.referrer = data.http.referrer || undefined + this.userAgent = data.http['user-agent'] || undefined + + return this } - withChannel(channelsGroupedById: Dictionary): this { + withChannel(channelsKeyById: Dictionary): this { if (!this.channelId) return this - this.channel = channelsGroupedById.get(this.channelId) + this.channel = channelsKeyById.get(this.channelId) return this } @@ -93,18 +113,22 @@ export class Stream { return this } - setHttpUserAgent(httpUserAgent: string): this { - this.httpUserAgent = httpUserAgent + setUserAgent(userAgent: string): this { + this.userAgent = userAgent return this } - setHttpReferrer(httpReferrer: string): this { - this.httpReferrer = httpReferrer + setReferrer(referrer: string): this { + this.referrer = referrer return this } + getLine(): number { + return this.line || -1 + } + setFilepath(filepath: string): this { this.filepath = filepath @@ -133,12 +157,12 @@ export class Stream { return this.filepath || '' } - getHttpReferrer(): string { - return this.httpReferrer || '' + getReferrer(): string { + return this.referrer || '' } - getHttpUserAgent(): string { - return this.httpUserAgent || '' + getUserAgent(): string { + return this.userAgent || '' } getQuality(): string { @@ -198,14 +222,6 @@ export class Stream { return Object.assign(Object.create(Object.getPrototypeOf(this)), this) } - hasName(): boolean { - return !!this.name - } - - noName(): boolean { - return !this.name - } - hasChannel() { return !!this.channel } @@ -281,8 +297,12 @@ export class Stream { return this?.channel?.logo || '' } + getName(): string { + return this.name || '' + } + getTitle(): string { - let title = `${this.name}` + let title = `${this.getName()}` if (this.getQuality()) { title += ` (${this.getQuality()})` @@ -303,30 +323,13 @@ export class Stream { return this.id || '' } - data() { - return { - id: this.id, - channel: this.channel, - feed: this.feed, - filepath: this.filepath, - label: this.label, - name: this.name, - verticalResolution: this.verticalResolution, - isInterlaced: this.isInterlaced, - url: this.url, - httpReferrer: this.httpReferrer, - httpUserAgent: this.httpUserAgent, - line: this.line - } - } - toJSON() { return { channel: this.channelId || null, feed: this.feedId || null, url: this.url, - referrer: this.httpReferrer || null, - user_agent: this.httpUserAgent || null, + referrer: this.referrer || null, + user_agent: this.userAgent || null, quality: this.getQuality() || null } } @@ -338,22 +341,22 @@ export class Stream { output += ` tvg-logo="${this.getLogo()}" group-title="${this.groupTitle}"` } - if (this.httpReferrer) { - output += ` http-referrer="${this.httpReferrer}"` + if (this.referrer) { + output += ` http-referrer="${this.referrer}"` } - if (this.httpUserAgent) { - output += ` http-user-agent="${this.httpUserAgent}"` + if (this.userAgent) { + output += ` http-user-agent="${this.userAgent}"` } output += `,${this.getTitle()}` - if (this.httpReferrer) { - output += `\n#EXTVLCOPT:http-referrer=${this.httpReferrer}` + if (this.referrer) { + output += `\n#EXTVLCOPT:http-referrer=${this.referrer}` } - if (this.httpUserAgent) { - output += `\n#EXTVLCOPT:http-user-agent=${this.httpUserAgent}` + if (this.userAgent) { + output += `\n#EXTVLCOPT:http-user-agent=${this.userAgent}` } output += `\n${this.url}` @@ -379,7 +382,11 @@ function escapeRegExp(text) { return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&') } -function parseQuality(quality: string): { verticalResolution: number; isInterlaced: boolean } { +function parseQuality(quality: string | null): { + verticalResolution: number | null + isInterlaced: boolean | null +} { + if (!quality) return { verticalResolution: null, isInterlaced: null } let [, verticalResolutionString] = quality.match(/^(\d+)/) || [null, undefined] const isInterlaced = /i$/i.test(quality) let verticalResolution = 0 diff --git a/scripts/models/subdivision.ts b/scripts/models/subdivision.ts index d6795fea33..b43d1c88d7 100644 --- a/scripts/models/subdivision.ts +++ b/scripts/models/subdivision.ts @@ -1,26 +1,41 @@ +import { SubdivisionData, SubdivisionSerializedData } from '../types/subdivision' import { Dictionary } from '@freearhey/core' import { Country } from '.' -type SubdivisionData = { - code: string - name: string - country: string -} - export class Subdivision { code: string name: string countryCode: string country?: Country - constructor(data: SubdivisionData) { + constructor(data?: SubdivisionData) { + if (!data) return + this.code = data.code this.name = data.name this.countryCode = data.country } - withCountry(countriesGroupedByCode: Dictionary): this { - this.country = countriesGroupedByCode.get(this.countryCode) + withCountry(countriesKeyByCode: Dictionary): this { + this.country = countriesKeyByCode.get(this.countryCode) + + return this + } + + serialize(): SubdivisionSerializedData { + return { + code: this.code, + name: this.name, + countryCode: this.code, + country: this.country ? this.country.serialize() : undefined + } + } + + deserialize(data: SubdivisionSerializedData): this { + this.code = data.code + this.name = data.name + this.countryCode = data.countryCode + this.country = data.country ? new Country().deserialize(data.country) : undefined return this } diff --git a/scripts/models/timezone.ts b/scripts/models/timezone.ts index b519f0e06b..e4071138fe 100644 --- a/scripts/models/timezone.ts +++ b/scripts/models/timezone.ts @@ -18,8 +18,8 @@ export class Timezone { this.countryCodes = new Collection(data.countries) } - withCountries(countriesGroupedByCode: Dictionary): this { - this.countries = this.countryCodes.map((code: string) => countriesGroupedByCode.get(code)) + withCountries(countriesKeyByCode: Dictionary): this { + this.countries = this.countryCodes.map((code: string) => countriesKeyByCode.get(code)) return this } diff --git a/scripts/types/blocklistRecord.d.ts b/scripts/types/blocklistRecord.d.ts new file mode 100644 index 0000000000..4b1d9e7dc8 --- /dev/null +++ b/scripts/types/blocklistRecord.d.ts @@ -0,0 +1,5 @@ +export type BlocklistRecordData = { + channel: string + reason: string + ref: string +} diff --git a/scripts/types/category.d.ts b/scripts/types/category.d.ts new file mode 100644 index 0000000000..e78d6c62ed --- /dev/null +++ b/scripts/types/category.d.ts @@ -0,0 +1,9 @@ +export type CategorySerializedData = { + id: string + name: string +} + +export type CategoryData = { + id: string + name: string +} diff --git a/scripts/types/channel.d.ts b/scripts/types/channel.d.ts new file mode 100644 index 0000000000..1f9d031cb1 --- /dev/null +++ b/scripts/types/channel.d.ts @@ -0,0 +1,52 @@ +import { Collection } from '@freearhey/core' +import type { CountrySerializedData } from './country' +import type { SubdivisionSerializedData } from './subdivision' +import type { CategorySerializedData } from './category' + +export type ChannelSerializedData = { + id: string + name: string + altNames: string[] + network?: string + owners: string[] + countryCode: string + country?: CountrySerializedData + subdivisionCode?: string + subdivision?: SubdivisionSerializedData + cityName?: string + categoryIds: string[] + categories?: CategorySerializedData[] + isNSFW: boolean + launched?: string + closed?: string + replacedBy?: string + website?: string + logo: string +} + +export type ChannelData = { + id: string + name: string + alt_names: string[] + network: string + owners: Collection + country: string + subdivision: string + city: string + categories: Collection + is_nsfw: boolean + launched: string + closed: string + replaced_by: string + website: string + logo: string +} + +export type ChannelSearchableData = { + id: string + name: string + altNames: string[] + guideNames: string[] + streamNames: string[] + feedFullNames: string[] +} diff --git a/scripts/types/country.d.ts b/scripts/types/country.d.ts new file mode 100644 index 0000000000..9554d4c68c --- /dev/null +++ b/scripts/types/country.d.ts @@ -0,0 +1,20 @@ +import type { LanguageSerializedData } from './language' +import type { SubdivisionSerializedData } from './subdivision' +import type { RegionSerializedData } from './region' + +export type CountrySerializedData = { + code: string + name: string + flag: string + languageCode: string + language: LanguageSerializedData | null + subdivisions: SubdivisionSerializedData[] + regions: RegionSerializedData[] +} + +export type CountryData = { + code: string + name: string + lang: string + flag: string +} diff --git a/scripts/types/dataLoader.d.ts b/scripts/types/dataLoader.d.ts new file mode 100644 index 0000000000..05742ff9d1 --- /dev/null +++ b/scripts/types/dataLoader.d.ts @@ -0,0 +1,19 @@ +import { Storage } from '@freearhey/core' + +export type DataLoaderProps = { + storage: Storage +} + +export type DataLoaderData = { + countries: object | object[] + regions: object | object[] + subdivisions: object | object[] + languages: object | object[] + categories: object | object[] + blocklist: object | object[] + channels: object | object[] + feeds: object | object[] + timezones: object | object[] + guides: object | object[] + streams: object | object[] +} diff --git a/scripts/types/dataProcessor.d.ts b/scripts/types/dataProcessor.d.ts new file mode 100644 index 0000000000..1005ff5b23 --- /dev/null +++ b/scripts/types/dataProcessor.d.ts @@ -0,0 +1,27 @@ +import { Collection, Dictionary } from '@freearhey/core' + +export type DataProcessorData = { + blocklistRecordsGroupedByChannelId: Dictionary + subdivisionsGroupedByCountryCode: Dictionary + feedsGroupedByChannelId: Dictionary + guidesGroupedByStreamId: Dictionary + subdivisionsKeyByCode: Dictionary + countriesKeyByCode: Dictionary + languagesKeyByCode: Dictionary + streamsGroupedById: Dictionary + categoriesKeyById: Dictionary + timezonesKeyById: Dictionary + regionsKeyByCode: Dictionary + blocklistRecords: Collection + channelsKeyById: Dictionary + subdivisions: Collection + categories: Collection + countries: Collection + languages: Collection + timezones: Collection + channels: Collection + regions: Collection + streams: Collection + guides: Collection + feeds: Collection +} diff --git a/scripts/types/feed.d.ts b/scripts/types/feed.d.ts new file mode 100644 index 0000000000..5c6722dde2 --- /dev/null +++ b/scripts/types/feed.d.ts @@ -0,0 +1,12 @@ +import { Collection } from '@freearhey/core' + +export type FeedData = { + channel: string + id: string + name: string + is_main: boolean + broadcast_area: Collection + languages: Collection + timezones: Collection + video_format: string +} diff --git a/scripts/types/guide.d.ts b/scripts/types/guide.d.ts new file mode 100644 index 0000000000..63a6ecdb19 --- /dev/null +++ b/scripts/types/guide.d.ts @@ -0,0 +1,17 @@ +export type GuideSerializedData = { + channelId?: string + feedId?: string + siteDomain: string + siteId: string + siteName: string + languageCode: string +} + +export type GuideData = { + channel: string + feed: string + site: string + site_id: string + site_name: string + lang: string +} diff --git a/scripts/types/language.d.ts b/scripts/types/language.d.ts new file mode 100644 index 0000000000..2b9d4525c0 --- /dev/null +++ b/scripts/types/language.d.ts @@ -0,0 +1,9 @@ +export type LanguageSerializedData = { + code: string + name: string +} + +export type LanguageData = { + code: string + name: string +} diff --git a/scripts/types/region.d.ts b/scripts/types/region.d.ts new file mode 100644 index 0000000000..e6773429ee --- /dev/null +++ b/scripts/types/region.d.ts @@ -0,0 +1,13 @@ +export type RegionSerializedData = { + code: string + name: string + countryCodes: string[] + countries?: CountrySerializedData[] + subdivisions?: SubdivisionSerializedData[] +} + +export type RegionData = { + code: string + name: string + countries: string[] +} diff --git a/scripts/types/stream.d.ts b/scripts/types/stream.d.ts new file mode 100644 index 0000000000..667ad25861 --- /dev/null +++ b/scripts/types/stream.d.ts @@ -0,0 +1,10 @@ +export type StreamData = { + channel: string | null + feed: string | null + name: string | null + url: string + referrer: string | null + user_agent: string | null + quality: string | null + label: string | null +} diff --git a/scripts/types/subdivision.d.ts b/scripts/types/subdivision.d.ts new file mode 100644 index 0000000000..bf46831f72 --- /dev/null +++ b/scripts/types/subdivision.d.ts @@ -0,0 +1,12 @@ +export type SubdivisionSerializedData = { + code: string + name: string + countryCode: string + country?: CountrySerializedData +} + +export type SubdivisionData = { + code: string + name: string + country: string +} From 88e62715e81d7318b238203ff95b74753cda7dd0 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Tue, 22 Apr 2025 03:47:28 +0300 Subject: [PATCH 07/24] Update tests --- tests/commands/api/generate.test.ts | 25 ++++++--- tests/commands/playlist/format.test.ts | 30 +++++++---- tests/commands/playlist/generate.test.ts | 39 +++++++++------ tests/commands/playlist/test.test.ts | 35 +++++++++---- tests/commands/playlist/update.test.ts | 40 ++++++++------- tests/commands/playlist/validate.test.ts | 64 +++++++++++++----------- tests/commands/readme/update.test.ts | 27 ++++++---- tests/commands/report/create.test.ts | 26 ++++++---- 8 files changed, 175 insertions(+), 111 deletions(-) diff --git a/tests/commands/api/generate.test.ts b/tests/commands/api/generate.test.ts index 7f8f68aa93..3f2178da67 100644 --- a/tests/commands/api/generate.test.ts +++ b/tests/commands/api/generate.test.ts @@ -1,19 +1,28 @@ import { execSync } from 'child_process' import fs from 'fs-extra' +import os from 'os' + +let ENV_VAR = + 'DATA_DIR=tests/__data__/input/data STREAMS_DIR=tests/__data__/input/api_generate API_DIR=tests/__data__/output/.api' +if (os.platform() === 'win32') { + ENV_VAR = + 'SET "DATA_DIR=tests/__data__/input/data" && SET "STREAMS_DIR=tests/__data__/input/api_generate" && SET "API_DIR=tests/__data__/output/.api" &&' +} beforeEach(() => { fs.emptyDirSync('tests/__data__/output') }) -it('can create streams.json', () => { - execSync( - 'DATA_DIR=tests/__data__/input/data STREAMS_DIR=tests/__data__/input/api_generate API_DIR=tests/__data__/output/.api npm run api:generate', - { encoding: 'utf8' } - ) +describe('api:generate', () => { + it('can create streams.json', () => { + const cmd = `${ENV_VAR} npm run api:generate` + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) - expect(content('output/.api/streams.json')).toMatchObject( - content('expected/api_generate/.api/streams.json') - ) + expect(content('output/.api/streams.json')).toMatchObject( + content('expected/api_generate/.api/streams.json') + ) + }) }) function content(filepath: string) { diff --git a/tests/commands/playlist/format.test.ts b/tests/commands/playlist/format.test.ts index d513c8b386..58174f93a3 100644 --- a/tests/commands/playlist/format.test.ts +++ b/tests/commands/playlist/format.test.ts @@ -1,25 +1,33 @@ import { execSync } from 'child_process' import * as fs from 'fs-extra' import { glob } from 'glob' +import os from 'os' + +let ENV_VAR = 'STREAMS_DIR=tests/__data__/output/streams' +if (os.platform() === 'win32') { + ENV_VAR = 'SET "STREAMS_DIR=tests/__data__/output/streams" &&' +} beforeEach(() => { fs.emptyDirSync('tests/__data__/output') fs.copySync('tests/__data__/input/playlist_format', 'tests/__data__/output/streams') }) -it('can format playlists', () => { - execSync('STREAMS_DIR=tests/__data__/output/streams npm run playlist:format', { - encoding: 'utf8' - }) +describe('playlist:format', () => { + it('can format playlists', () => { + const cmd = `${ENV_VAR} npm run playlist:format` + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) - const files = glob - .sync('tests/__data__/expected/playlist_format/*.m3u') - .map(f => f.replace('tests/__data__/expected/playlist_format/', '')) + const files = glob + .sync('tests/__data__/expected/playlist_format/*.m3u') + .map(f => f.replace('tests/__data__/expected/playlist_format/', '')) - files.forEach(filepath => { - expect(content(`output/streams/${filepath}`), filepath).toBe( - content(`expected/playlist_format/${filepath}`) - ) + files.forEach(filepath => { + expect(content(`output/streams/${filepath}`), filepath).toBe( + content(`expected/playlist_format/${filepath}`) + ) + }) }) }) diff --git a/tests/commands/playlist/generate.test.ts b/tests/commands/playlist/generate.test.ts index 389ee022ee..d51428aca6 100644 --- a/tests/commands/playlist/generate.test.ts +++ b/tests/commands/playlist/generate.test.ts @@ -1,32 +1,39 @@ import { execSync } from 'child_process' import * as fs from 'fs-extra' import * as glob from 'glob' +import os from 'os' + +let ENV_VAR = + 'STREAMS_DIR=tests/__data__/input/playlist_generate DATA_DIR=tests/__data__/input/data PUBLIC_DIR=tests/__data__/output/.gh-pages LOGS_DIR=tests/__data__/output/logs' +if (os.platform() === 'win32') { + ENV_VAR = + 'SET "STREAMS_DIR=tests/__data__/input/playlist_generate" && SET "DATA_DIR=tests/__data__/input/data" && SET "PUBLIC_DIR=tests/__data__/output/.gh-pages" && SET "LOGS_DIR=tests/__data__/output/logs" &&' +} beforeEach(() => { fs.emptyDirSync('tests/__data__/output') }) -it('can generate playlists and logs', () => { - const stdout = execSync( - 'STREAMS_DIR=tests/__data__/input/playlist_generate DATA_DIR=tests/__data__/input/data PUBLIC_DIR=tests/__data__/output/.gh-pages LOGS_DIR=tests/__data__/output/logs npm run playlist:generate', - { encoding: 'utf8' } - ) +describe('playlist:generate', () => { + it('can generate playlists and logs', () => { + const cmd = `${ENV_VAR} npm run playlist:generate` + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) - if (process.env.DEBUG === 'true') console.log(stdout) + const playlists = glob + .sync('tests/__data__/expected/playlist_generate/.gh-pages/**/*.m3u') + .map((file: string) => file.replace('tests/__data__/expected/playlist_generate/', '')) - const playlists = glob - .sync('tests/__data__/expected/playlist_generate/.gh-pages/**/*.m3u') - .map((file: string) => file.replace('tests/__data__/expected/playlist_generate/', '')) + playlists.forEach((filepath: string) => { + expect(content(`output/${filepath}`), filepath).toBe( + content(`expected/playlist_generate/${filepath}`) + ) + }) - playlists.forEach((filepath: string) => { - expect(content(`output/${filepath}`), filepath).toBe( - content(`expected/playlist_generate/${filepath}`) + expect(content('output/logs/generators.log').split('\n').sort()).toStrictEqual( + content('expected/playlist_generate/logs/generators.log').split('\n').sort() ) }) - - expect(content('output/logs/generators.log').split('\n').sort()).toStrictEqual( - content('expected/playlist_generate/logs/generators.log').split('\n').sort() - ) }) function content(filepath: string) { diff --git a/tests/commands/playlist/test.test.ts b/tests/commands/playlist/test.test.ts index 0842f0f108..0020106dce 100644 --- a/tests/commands/playlist/test.test.ts +++ b/tests/commands/playlist/test.test.ts @@ -1,19 +1,32 @@ import { execSync } from 'child_process' +import os from 'os' type ExecError = { status: number stdout: string } -it('shows an error if the playlist contains a broken link', () => { - try { - execSync('ROOT_DIR=tests/__data__/input npm run playlist:test playlist_test/ag.m3u', { - encoding: 'utf8' - }) - process.exit(1) - } catch (error) { - expect((error as ExecError).status).toBe(1) - expect((error as ExecError).stdout).toContain('playlist_test/ag.m3u') - expect((error as ExecError).stdout).toContain('2 problems (1 errors, 1 warnings)') - } +let ENV_VAR = 'ROOT_DIR=tests/__data__/input' +if (os.platform() === 'win32') { + ENV_VAR = 'SET "ROOT_DIR=tests/__data__/input" &&' +} + +describe('playlist:test', () => { + it('shows an error if the playlist contains a broken link', () => { + const cmd = `${ENV_VAR} npm run playlist:test playlist_test/ag.m3u` + try { + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + checkStdout(stdout) + } catch (error) { + // NOTE: for Windows only + if (process.env.DEBUG === 'true') console.log(cmd, error) + checkStdout((error as ExecError).stdout) + } + }) }) + +function checkStdout(stdout: string) { + expect(stdout).toContain('playlist_test/ag.m3u') + expect(stdout).toContain('2 problems (1 errors, 1 warnings)') +} diff --git a/tests/commands/playlist/update.test.ts b/tests/commands/playlist/update.test.ts index cc104d8cce..16dbefaf85 100644 --- a/tests/commands/playlist/update.test.ts +++ b/tests/commands/playlist/update.test.ts @@ -1,33 +1,39 @@ import { execSync } from 'child_process' import * as fs from 'fs-extra' import { glob } from 'glob' +import os from 'os' + +let ENV_VAR = 'DATA_DIR=tests/__data__/input/data STREAMS_DIR=tests/__data__/output/streams' +if (os.platform() === 'win32') { + ENV_VAR = + 'SET "DATA_DIR=tests/__data__/input/data" && SET "STREAMS_DIR=tests/__data__/output/streams" &&' +} beforeEach(() => { fs.emptyDirSync('tests/__data__/output') fs.copySync('tests/__data__/input/playlist_update', 'tests/__data__/output/streams') }) -it('can update playlists', () => { - const stdout = execSync( - 'DATA_DIR=tests/__data__/input/data STREAMS_DIR=tests/__data__/output/streams npm run playlist:update --silent', - { - encoding: 'utf8' - } - ) +describe('playlist:update', () => { + it('can update playlists', () => { + const cmd = `${ENV_VAR} npm run playlist:update --silent` + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) - const files = glob - .sync('tests/__data__/expected/playlist_update/*.m3u') - .map(f => f.replace('tests/__data__/expected/playlist_update/', '')) + const files = glob + .sync('tests/__data__/expected/playlist_update/*.m3u') + .map(f => f.replace('tests/__data__/expected/playlist_update/', '')) - files.forEach(filepath => { - expect(content(`output/streams/${filepath}`), filepath).toBe( - content(`expected/playlist_update/${filepath}`) + files.forEach(filepath => { + expect(content(`output/streams/${filepath}`), filepath).toBe( + content(`expected/playlist_update/${filepath}`) + ) + }) + + expect(stdout).toBe( + 'OUTPUT=closes #14151, closes #14150, closes #14110, closes #14120, closes #14175, closes #14105, closes #14104, closes #14057, closes #14034, closes #13964, closes #13893, closes #13881, closes #13793, closes #13751, closes #13715\n' ) }) - - expect(stdout).toBe( - 'OUTPUT=closes #14151, closes #14150, closes #14110, closes #14120, closes #14175, closes #14105, closes #14104, closes #14057, closes #14034, closes #13964, closes #13893, closes #13881, closes #13793, closes #13751, closes #13715\n' - ) }) function content(filepath: string) { diff --git a/tests/commands/playlist/validate.test.ts b/tests/commands/playlist/validate.test.ts index b2a43324e4..933099bc9c 100644 --- a/tests/commands/playlist/validate.test.ts +++ b/tests/commands/playlist/validate.test.ts @@ -1,41 +1,47 @@ import { execSync } from 'child_process' +import os from 'os' type ExecError = { status: number stdout: string } -it('show an error if channel id in the blocklist', () => { - try { - const stdout = execSync( - 'DATA_DIR=tests/__data__/input/data STREAMS_DIR=tests/__data__/input/playlist_validate npm run playlist:validate -- us_blocked.m3u', - { - encoding: 'utf8' - } +let ENV_VAR = + 'DATA_DIR=tests/__data__/input/data STREAMS_DIR=tests/__data__/input/playlist_validate' +if (os.platform() === 'win32') { + ENV_VAR = + 'SET "DATA_DIR=tests/__data__/input/data" && SET "STREAMS_DIR=tests/__data__/input/playlist_validate" &&' +} + +describe('playlist:validate', () => { + it('show an error if channel id in the blocklist', () => { + const cmd = `${ENV_VAR} npm run playlist:validate -- us_blocked.m3u` + try { + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + checkStdout(stdout) + } catch (error) { + // NOTE: for Windows only + if (process.env.DEBUG === 'true') console.log(cmd, error) + checkStdout((error as ExecError).stdout) + } + }) + + it('show a warning if channel has wrong id', () => { + const cmd = `${ENV_VAR} npm run playlist:validate -- wrong_id.m3u` + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + + expect(stdout).toContain( + 'wrong_id.m3u\n 2 warning "qib22lAq1L.us" is not in the database\n\n1 problems (0 errors, 1 warnings)\n' ) - if (process.env.DEBUG === 'true') console.log(stdout) - process.exit(1) - } catch (error) { - if (process.env.DEBUG === 'true') console.log((error as ExecError).stdout) - expect((error as ExecError).status).toBe(1) - expect((error as ExecError).stdout).toContain(`us_blocked.m3u + }) +}) + +function checkStdout(stdout: string) { + expect(stdout).toContain(`us_blocked.m3u 2 error "FoxSports2.us" is on the blocklist due to claims of copyright holders (https://github.com/iptv-org/iptv/issues/0002) 4 error "TVN.pl" is on the blocklist due to NSFW content (https://github.com/iptv-org/iptv/issues/0003) 2 problems (2 errors, 0 warnings)`) - } -}) - -it('show a warning if channel has wrong id', () => { - const stdout = execSync( - 'DATA_DIR=tests/__data__/input/data STREAMS_DIR=tests/__data__/input/playlist_validate npm run playlist:validate -- wrong_id.m3u', - { - encoding: 'utf8' - } - ) - if (process.env.DEBUG === 'true') console.log(stdout) - - expect(stdout).toContain( - 'wrong_id.m3u\n 2 warning "qib22lAq1L.us" is not in the database\n\n1 problems (0 errors, 1 warnings)\n' - ) -}) +} diff --git a/tests/commands/readme/update.test.ts b/tests/commands/readme/update.test.ts index f15e71f48b..0ba363409d 100644 --- a/tests/commands/readme/update.test.ts +++ b/tests/commands/readme/update.test.ts @@ -1,6 +1,14 @@ import { execSync } from 'child_process' import fs from 'fs-extra' import path from 'path' +import os from 'os' + +let ENV_VAR = + 'DATA_DIR=tests/__data__/input/data LOGS_DIR=tests/__data__/input/readme_update README_DIR=tests/__data__/output/.readme' +if (os.platform() === 'win32') { + ENV_VAR = + 'SET "DATA_DIR=tests/__data__/input/data" && SET "LOGS_DIR=tests/__data__/input/readme_update" && SET "README_DIR=tests/__data__/output/.readme" &&' +} beforeEach(() => { fs.emptyDirSync('tests/__data__/output') @@ -13,17 +21,18 @@ beforeEach(() => { 'tests/__data__/input/readme_update/.readme/template.md', 'tests/__data__/output/.readme/template.md' ) - - execSync( - 'DATA_DIR=tests/__data__/input/data LOGS_DIR=tests/__data__/input/readme_update README_DIR=tests/__data__/output/.readme npm run readme:update', - { encoding: 'utf8' } - ) }) -it('can update readme.md', () => { - expect(content('tests/__data__/output/readme.md')).toEqual( - content('tests/__data__/expected/readme_update/_readme.md') - ) +describe('readme:update', () => { + it('can update readme.md', () => { + const cmd = `${ENV_VAR} npm run readme:update` + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + + expect(content('tests/__data__/output/readme.md')).toEqual( + content('tests/__data__/expected/readme_update/_readme.md') + ) + }) }) function content(filepath: string) { diff --git a/tests/commands/report/create.test.ts b/tests/commands/report/create.test.ts index de449347af..18c7994455 100644 --- a/tests/commands/report/create.test.ts +++ b/tests/commands/report/create.test.ts @@ -1,15 +1,20 @@ import { execSync } from 'child_process' +import os from 'os' -it('can create report', () => { - const stdout = execSync( - 'DATA_DIR=tests/__data__/input/data STREAMS_DIR=tests/__data__/input/report_create npm run report:create', - { - encoding: 'utf8' - } - ) +let ENV_VAR = 'DATA_DIR=tests/__data__/input/data STREAMS_DIR=tests/__data__/input/report_create' +if (os.platform() === 'win32') { + ENV_VAR = + 'SET "DATA_DIR=tests/__data__/input/data" && SET "STREAMS_DIR=tests/__data__/input/report_create" &&' +} - expect( - stdout.includes(` +describe('report:create', () => { + it('can create report', () => { + const cmd = `${ENV_VAR} npm run report:create` + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + + expect( + stdout.includes(` ┌─────────┬─────────────┬──────────────────┬─────────────────────────────┬───────────────────────────────────────────────────────────────────────────────────────────────────────────┬───────────────┐ │ (index) │ issueNumber │ type │ streamId │ streamUrl │ status │ ├─────────┼─────────────┼──────────────────┼─────────────────────────────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────┼───────────────┤ @@ -20,5 +25,6 @@ it('can create report', () => { │ 4 │ 16120 │ 'broken stream' │ undefined │ 'http://190.61.102.67:2000/play/a038/index.m3u8' │ 'wrong_link' │ │ 5 │ 19956 │ 'channel search' │ 'CNBCe.tr' │ undefined │ 'invalid_id' │ └─────────┴─────────────┴──────────────────┴─────────────────────────────┴───────────────────────────────────────────────────────────────────────────────────────────────────────────┴───────────────┘`) - ).toBe(true) + ).toBe(true) + }) }) From 5e8dee13a99e05488afe62bdfe90801c646e2cbe Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Tue, 22 Apr 2025 23:17:48 +0300 Subject: [PATCH 08/24] Update tests --- tests/commands/api/generate.test.ts | 11 ++++------- tests/commands/playlist/format.test.ts | 9 ++++----- tests/commands/playlist/generate.test.ts | 13 ++++++------- tests/commands/playlist/update.test.ts | 9 ++++----- tests/commands/readme/update.test.ts | 7 ++----- 5 files changed, 20 insertions(+), 29 deletions(-) diff --git a/tests/commands/api/generate.test.ts b/tests/commands/api/generate.test.ts index 3f2178da67..931f543cb8 100644 --- a/tests/commands/api/generate.test.ts +++ b/tests/commands/api/generate.test.ts @@ -1,3 +1,4 @@ +import { pathToFileURL } from 'node:url' import { execSync } from 'child_process' import fs from 'fs-extra' import os from 'os' @@ -19,16 +20,12 @@ describe('api:generate', () => { const stdout = execSync(cmd, { encoding: 'utf8' }) if (process.env.DEBUG === 'true') console.log(cmd, stdout) - expect(content('output/.api/streams.json')).toMatchObject( - content('expected/api_generate/.api/streams.json') + expect(content('tests/__data__/output/.api/streams.json')).toMatchObject( + content('tests/__data__/expected/api_generate/.api/streams.json') ) }) }) function content(filepath: string) { - return JSON.parse( - fs.readFileSync(`tests/__data__/${filepath}`, { - encoding: 'utf8' - }) - ) + return JSON.parse(fs.readFileSync(pathToFileURL(filepath), { encoding: 'utf8' })) } diff --git a/tests/commands/playlist/format.test.ts b/tests/commands/playlist/format.test.ts index 58174f93a3..070bfd7d98 100644 --- a/tests/commands/playlist/format.test.ts +++ b/tests/commands/playlist/format.test.ts @@ -1,3 +1,4 @@ +import { pathToFileURL } from 'node:url' import { execSync } from 'child_process' import * as fs from 'fs-extra' import { glob } from 'glob' @@ -24,15 +25,13 @@ describe('playlist:format', () => { .map(f => f.replace('tests/__data__/expected/playlist_format/', '')) files.forEach(filepath => { - expect(content(`output/streams/${filepath}`), filepath).toBe( - content(`expected/playlist_format/${filepath}`) + expect(content(`tests/__data__/output/streams/${filepath}`), filepath).toBe( + content(`tests/__data__/expected/playlist_format/${filepath}`) ) }) }) }) function content(filepath: string) { - return fs.readFileSync(`tests/__data__/${filepath}`, { - encoding: 'utf8' - }) + return fs.readFileSync(pathToFileURL(filepath), { encoding: 'utf8' }) } diff --git a/tests/commands/playlist/generate.test.ts b/tests/commands/playlist/generate.test.ts index d51428aca6..5c99f02696 100644 --- a/tests/commands/playlist/generate.test.ts +++ b/tests/commands/playlist/generate.test.ts @@ -1,3 +1,4 @@ +import { pathToFileURL } from 'node:url' import { execSync } from 'child_process' import * as fs from 'fs-extra' import * as glob from 'glob' @@ -25,19 +26,17 @@ describe('playlist:generate', () => { .map((file: string) => file.replace('tests/__data__/expected/playlist_generate/', '')) playlists.forEach((filepath: string) => { - expect(content(`output/${filepath}`), filepath).toBe( - content(`expected/playlist_generate/${filepath}`) + expect(content(`tests/__data__/output/${filepath}`), filepath).toBe( + content(`tests/__data__/expected/playlist_generate/${filepath}`) ) }) - expect(content('output/logs/generators.log').split('\n').sort()).toStrictEqual( - content('expected/playlist_generate/logs/generators.log').split('\n').sort() + expect(content('tests/__data__/output/logs/generators.log').split('\n').sort()).toStrictEqual( + content('tests/__data__/expected/playlist_generate/logs/generators.log').split('\n').sort() ) }) }) function content(filepath: string) { - return fs.readFileSync(`tests/__data__/${filepath}`, { - encoding: 'utf8' - }) + return fs.readFileSync(pathToFileURL(filepath), { encoding: 'utf8' }) } diff --git a/tests/commands/playlist/update.test.ts b/tests/commands/playlist/update.test.ts index 16dbefaf85..a9949c4994 100644 --- a/tests/commands/playlist/update.test.ts +++ b/tests/commands/playlist/update.test.ts @@ -1,3 +1,4 @@ +import { pathToFileURL } from 'node:url' import { execSync } from 'child_process' import * as fs from 'fs-extra' import { glob } from 'glob' @@ -25,8 +26,8 @@ describe('playlist:update', () => { .map(f => f.replace('tests/__data__/expected/playlist_update/', '')) files.forEach(filepath => { - expect(content(`output/streams/${filepath}`), filepath).toBe( - content(`expected/playlist_update/${filepath}`) + expect(content(`tests/__data__/output/streams/${filepath}`), filepath).toBe( + content(`tests/__data__/expected/playlist_update/${filepath}`) ) }) @@ -37,7 +38,5 @@ describe('playlist:update', () => { }) function content(filepath: string) { - return fs.readFileSync(`tests/__data__/${filepath}`, { - encoding: 'utf8' - }) + return fs.readFileSync(pathToFileURL(filepath), { encoding: 'utf8' }) } diff --git a/tests/commands/readme/update.test.ts b/tests/commands/readme/update.test.ts index 0ba363409d..061179ed4d 100644 --- a/tests/commands/readme/update.test.ts +++ b/tests/commands/readme/update.test.ts @@ -1,3 +1,4 @@ +import { pathToFileURL } from 'node:url' import { execSync } from 'child_process' import fs from 'fs-extra' import path from 'path' @@ -36,9 +37,5 @@ describe('readme:update', () => { }) function content(filepath: string) { - const data = fs.readFileSync(path.resolve(filepath), { - encoding: 'utf8' - }) - - return JSON.stringify(data) + return JSON.stringify(fs.readFileSync(pathToFileURL(filepath), { encoding: 'utf8' })) } From c0514887ae53b8b2e6fe5af15cebd7f1d9b4dd13 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Tue, 22 Apr 2025 23:24:44 +0300 Subject: [PATCH 09/24] Update .gitignore --- .gitignore | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 3fe46ea012..ad225b41d1 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ node_modules .secrets .actrc .DS_Store -.gh-pages -.api +/.gh-pages/ +/.api/ .env -/temp \ No newline at end of file +/temp/ \ No newline at end of file From b505949b821ff5ce99a6fb7ed8026aae1eb7b42a Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Tue, 22 Apr 2025 23:25:16 +0300 Subject: [PATCH 10/24] Add tests/__data__/expected --- .../expected/api_generate/.api/streams.json | 57 +++++++++++++++++++ .../.gh-pages/categories/general.m3u | 7 +++ .../.gh-pages/categories/legislative.m3u | 1 + .../.gh-pages/categories/news.m3u | 3 + .../.gh-pages/categories/undefined.m3u | 15 +++++ .../.gh-pages/categories/weather.m3u | 3 + .../.gh-pages/categories/xxx.m3u | 3 + .../.gh-pages/countries/ad.m3u | 3 + .../.gh-pages/countries/ca.m3u | 3 + .../.gh-pages/countries/ru.m3u | 3 + .../.gh-pages/countries/undefined.m3u | 11 ++++ .../.gh-pages/index.category.m3u | 25 ++++++++ .../.gh-pages/index.country.m3u | 31 ++++++++++ .../.gh-pages/index.language.m3u | 23 ++++++++ .../playlist_generate/.gh-pages/index.m3u | 23 ++++++++ .../.gh-pages/index.region.m3u | 43 ++++++++++++++ .../.gh-pages/languages/eng.m3u | 3 + .../.gh-pages/languages/rus.m3u | 3 + .../.gh-pages/languages/undefined.m3u | 17 ++++++ .../.gh-pages/regions/afr.m3u | 1 + .../.gh-pages/regions/amer.m3u | 3 + .../.gh-pages/regions/apac.m3u | 1 + .../.gh-pages/regions/arab.m3u | 1 + .../.gh-pages/regions/asia.m3u | 5 ++ .../.gh-pages/regions/carib.m3u | 1 + .../.gh-pages/regions/cas.m3u | 3 + .../.gh-pages/regions/cis.m3u | 5 ++ .../.gh-pages/regions/emea.m3u | 7 +++ .../.gh-pages/regions/eur.m3u | 7 +++ .../.gh-pages/regions/hispam.m3u | 1 + .../.gh-pages/regions/int.m3u | 5 ++ .../.gh-pages/regions/lac.m3u | 1 + .../.gh-pages/regions/latam.m3u | 1 + .../.gh-pages/regions/maghreb.m3u | 1 + .../.gh-pages/regions/mena.m3u | 1 + .../.gh-pages/regions/mideast.m3u | 1 + .../.gh-pages/regions/nam.m3u | 3 + .../.gh-pages/regions/noram.m3u | 3 + .../.gh-pages/regions/nord.m3u | 1 + .../.gh-pages/regions/oce.m3u | 1 + .../.gh-pages/regions/sas.m3u | 1 + .../.gh-pages/regions/ssa.m3u | 1 + .../.gh-pages/regions/undefined.m3u | 11 ++++ .../.gh-pages/regions/wafr.m3u | 1 + .../.gh-pages/subdivisions/ca-on.m3u | 3 + 45 files changed, 347 insertions(+) create mode 100644 tests/__data__/expected/api_generate/.api/streams.json create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/categories/general.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/categories/legislative.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/categories/news.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/categories/undefined.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/categories/weather.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/categories/xxx.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/countries/ad.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/countries/ca.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/countries/ru.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/countries/undefined.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/index.category.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/index.country.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/index.language.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/index.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/index.region.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/languages/eng.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/languages/rus.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/languages/undefined.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/afr.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/amer.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/apac.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/arab.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/asia.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/carib.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/cas.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/cis.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/emea.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/eur.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/hispam.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/int.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/lac.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/latam.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/maghreb.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/mena.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/mideast.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/nam.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/noram.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/nord.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/oce.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/sas.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/ssa.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/undefined.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/regions/wafr.m3u create mode 100644 tests/__data__/expected/playlist_generate/.gh-pages/subdivisions/ca-on.m3u diff --git a/tests/__data__/expected/api_generate/.api/streams.json b/tests/__data__/expected/api_generate/.api/streams.json new file mode 100644 index 0000000000..b85871048b --- /dev/null +++ b/tests/__data__/expected/api_generate/.api/streams.json @@ -0,0 +1,57 @@ +[ + { + "channel": null, + "feed": null, + "url": "http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8", + "referrer": null, + "user_agent": null + }, + { + "channel": null, + "feed": null, + "url": "http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8", + "referrer": "http://imn.iq", + "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148" + }, + { + "channel": "AndorraTV.ad", + "feed": "SD", + "url": "https://iptv-all.lanesh4d0w.repl.co/andorra/atv", + "referrer": null, + "user_agent": null + }, + { + "channel": "BBCNews.uk", + "url": "http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8", + "referrer": null, + "user_agent": null + }, + { + "channel": "LDPRTV.ru", + "feed": null, + "url": "http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.m3u8", + "referrer": null, + "user_agent": null + }, + { + "channel": "MeteoMedia.ca", + "feed": null, + "url": "http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8", + "referrer": null, + "user_agent": null + }, + { + "channel": "VisitXTV.nl", + "feed": null, + "url": "https://stream.visit-x.tv/vxtv/ngrp:live_all/30fps.m3u8", + "referrer": null, + "user_agent": null + }, + { + "channel": "Zoo.ad", + "feed": null, + "url": "https://iptv-all.lanesh4d0w.repl.co/andorra/zoo", + "referrer": null, + "user_agent": null + } +] \ No newline at end of file diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/categories/general.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/categories/general.m3u new file mode 100644 index 0000000000..e9587edc70 --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/categories/general.m3u @@ -0,0 +1,7 @@ +#EXTM3U +#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General;News",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) +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="General",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/categories/legislative.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/categories/legislative.m3u new file mode 100644 index 0000000000..fcd718794a --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/categories/legislative.m3u @@ -0,0 +1 @@ +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/categories/news.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/categories/news.m3u new file mode 100644 index 0000000000..cf4e62a9a5 --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/categories/news.m3u @@ -0,0 +1,3 @@ +#EXTM3U +#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General;News",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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 new file mode 100644 index 0000000000..4182fcb1d6 --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/categories/undefined.m3u @@ -0,0 +1,15 @@ +#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] +#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 +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Undefined",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv +#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD +https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV +http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 +#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 +#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) +https://iptv-all.lanesh4d0w.repl.co/andorra/zoo diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/categories/weather.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/categories/weather.m3u new file mode 100644 index 0000000000..4686c68f4b --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/categories/weather.m3u @@ -0,0 +1,3 @@ +#EXTM3U +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/categories/xxx.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/categories/xxx.m3u new file mode 100644 index 0000000000..d1e3a69bcd --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/categories/xxx.m3u @@ -0,0 +1,3 @@ +#EXTM3U +#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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/countries/ad.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/countries/ad.m3u new file mode 100644 index 0000000000..de466399ac --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/countries/ad.m3u @@ -0,0 +1,3 @@ +#EXTM3U +#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Undefined",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/countries/ca.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/countries/ca.m3u new file mode 100644 index 0000000000..4686c68f4b --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/countries/ca.m3u @@ -0,0 +1,3 @@ +#EXTM3U +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/countries/ru.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/countries/ru.m3u new file mode 100644 index 0000000000..cbfde92492 --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/countries/ru.m3u @@ -0,0 +1,3 @@ +#EXTM3U +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) +http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/countries/undefined.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/countries/undefined.m3u new file mode 100644 index 0000000000..da6b5f4bb7 --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/countries/undefined.m3u @@ -0,0 +1,11 @@ +#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] +#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 +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD +https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV +http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 +#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) +https://iptv-all.lanesh4d0w.repl.co/andorra/zoo diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/index.category.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/index.category.m3u new file mode 100644 index 0000000000..2ce7c040e5 --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/index.category.m3u @@ -0,0 +1,25 @@ +#EXTM3U +#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) +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="General",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="News",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/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] +#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 +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Undefined",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv +#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD +https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV +http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 +#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 +#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) +https://iptv-all.lanesh4d0w.repl.co/andorra/zoo diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/index.country.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/index.country.m3u new file mode 100644 index 0000000000..f5eba15e42 --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/index.country.m3u @@ -0,0 +1,31 @@ +#EXTM3U +#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Andorra",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Canada",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Kazakhstan",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Kyrgyzstan",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Russia",ЛДПР ТВ (1080p) +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="Tajikistan",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Turkmenistan",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Uzbekistan",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="International",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 +#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="International",Duna World (576i) +http://146.59.85.40:89/dunaworld/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] +#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 +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD +https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV +http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 +#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) +https://iptv-all.lanesh4d0w.repl.co/andorra/zoo diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/index.language.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/index.language.m3u new file mode 100644 index 0000000000..ed5f240141 --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/index.language.m3u @@ -0,0 +1,23 @@ +#EXTM3U +#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Catalan",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv +#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="English",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Russian",ЛДПР ТВ (1080p) +http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.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] +#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 +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD +https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV +http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 +#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 +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Undefined",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) +https://iptv-all.lanesh4d0w.repl.co/andorra/zoo +#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Undefined",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/index.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/index.m3u new file mode 100644 index 0000000000..1d27713b1d --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/index.m3u @@ -0,0 +1,23 @@ +#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] +#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 +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Undefined",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv +#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD +https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd +#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General;News",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV +http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 +#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 +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) +https://iptv-all.lanesh4d0w.repl.co/andorra/zoo +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) +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="General",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/index.region.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/index.region.m3u new file mode 100644 index 0000000000..90c7438cec --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/index.region.m3u @@ -0,0 +1,43 @@ +#EXTM3U +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Americas",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Asia",ЛДПР ТВ (1080p) +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="Asia",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Central Asia",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Commonwealth of Independent States",ЛДПР ТВ (1080p) +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="Commonwealth of Independent States",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Europe",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Europe",ЛДПР ТВ (1080p) +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="Europe",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Europe, the Middle East and Africa",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Europe, the Middle East and Africa",ЛДПР ТВ (1080p) +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="Europe, the Middle East and Africa",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="North America",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Northern America",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="International",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 +#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="International",Duna World (576i) +http://146.59.85.40:89/dunaworld/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] +#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 +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD +https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV +http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 +#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) +https://iptv-all.lanesh4d0w.repl.co/andorra/zoo diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/languages/eng.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/languages/eng.m3u new file mode 100644 index 0000000000..cf4e62a9a5 --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/languages/eng.m3u @@ -0,0 +1,3 @@ +#EXTM3U +#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General;News",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/languages/rus.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/languages/rus.m3u new file mode 100644 index 0000000000..cbfde92492 --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/languages/rus.m3u @@ -0,0 +1,3 @@ +#EXTM3U +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) +http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/languages/undefined.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/languages/undefined.m3u new file mode 100644 index 0000000000..5d59e689d7 --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/languages/undefined.m3u @@ -0,0 +1,17 @@ +#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] +#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 +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD +https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV +http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 +#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 +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) +https://iptv-all.lanesh4d0w.repl.co/andorra/zoo +#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="General",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/afr.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/afr.m3u new file mode 100644 index 0000000000..fcd718794a --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/afr.m3u @@ -0,0 +1 @@ +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/amer.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/amer.m3u new file mode 100644 index 0000000000..4686c68f4b --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/amer.m3u @@ -0,0 +1,3 @@ +#EXTM3U +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/apac.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/apac.m3u new file mode 100644 index 0000000000..fcd718794a --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/apac.m3u @@ -0,0 +1 @@ +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/arab.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/arab.m3u new file mode 100644 index 0000000000..fcd718794a --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/arab.m3u @@ -0,0 +1 @@ +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/asia.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/asia.m3u new file mode 100644 index 0000000000..6f6d448e87 --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/asia.m3u @@ -0,0 +1,5 @@ +#EXTM3U +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) +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="General",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/carib.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/carib.m3u new file mode 100644 index 0000000000..fcd718794a --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/carib.m3u @@ -0,0 +1 @@ +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/cas.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/cas.m3u new file mode 100644 index 0000000000..a9387b8b44 --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/cas.m3u @@ -0,0 +1,3 @@ +#EXTM3U +#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="General",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/cis.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/cis.m3u new file mode 100644 index 0000000000..6f6d448e87 --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/cis.m3u @@ -0,0 +1,5 @@ +#EXTM3U +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) +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="General",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/emea.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/emea.m3u new file mode 100644 index 0000000000..16dd0c3954 --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/emea.m3u @@ -0,0 +1,7 @@ +#EXTM3U +#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Undefined",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) +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="General",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/eur.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/eur.m3u new file mode 100644 index 0000000000..16dd0c3954 --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/eur.m3u @@ -0,0 +1,7 @@ +#EXTM3U +#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Undefined",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) +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="General",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/hispam.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/hispam.m3u new file mode 100644 index 0000000000..fcd718794a --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/hispam.m3u @@ -0,0 +1 @@ +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/int.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/int.m3u new file mode 100644 index 0000000000..c549c09ce1 --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/int.m3u @@ -0,0 +1,5 @@ +#EXTM3U +#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General;News",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 +#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/regions/lac.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/lac.m3u new file mode 100644 index 0000000000..fcd718794a --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/lac.m3u @@ -0,0 +1 @@ +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/latam.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/latam.m3u new file mode 100644 index 0000000000..fcd718794a --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/latam.m3u @@ -0,0 +1 @@ +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/maghreb.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/maghreb.m3u new file mode 100644 index 0000000000..fcd718794a --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/maghreb.m3u @@ -0,0 +1 @@ +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/mena.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/mena.m3u new file mode 100644 index 0000000000..fcd718794a --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/mena.m3u @@ -0,0 +1 @@ +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/mideast.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/mideast.m3u new file mode 100644 index 0000000000..fcd718794a --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/mideast.m3u @@ -0,0 +1 @@ +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/nam.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/nam.m3u new file mode 100644 index 0000000000..4686c68f4b --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/nam.m3u @@ -0,0 +1,3 @@ +#EXTM3U +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/noram.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/noram.m3u new file mode 100644 index 0000000000..4686c68f4b --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/noram.m3u @@ -0,0 +1,3 @@ +#EXTM3U +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/nord.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/nord.m3u new file mode 100644 index 0000000000..fcd718794a --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/nord.m3u @@ -0,0 +1 @@ +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/oce.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/oce.m3u new file mode 100644 index 0000000000..fcd718794a --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/oce.m3u @@ -0,0 +1 @@ +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/sas.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/sas.m3u new file mode 100644 index 0000000000..fcd718794a --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/sas.m3u @@ -0,0 +1 @@ +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/ssa.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/ssa.m3u new file mode 100644 index 0000000000..fcd718794a --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/ssa.m3u @@ -0,0 +1 @@ +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/undefined.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/undefined.m3u new file mode 100644 index 0000000000..da6b5f4bb7 --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/undefined.m3u @@ -0,0 +1,11 @@ +#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] +#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 +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD +https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV +http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 +#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) +https://iptv-all.lanesh4d0w.repl.co/andorra/zoo diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/wafr.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/wafr.m3u new file mode 100644 index 0000000000..fcd718794a --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/wafr.m3u @@ -0,0 +1 @@ +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/subdivisions/ca-on.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/subdivisions/ca-on.m3u new file mode 100644 index 0000000000..4686c68f4b --- /dev/null +++ b/tests/__data__/expected/playlist_generate/.gh-pages/subdivisions/ca-on.m3u @@ -0,0 +1,3 @@ +#EXTM3U +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 From e05896a567e2feaab103d25e2c9d31b7b62facf1 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Tue, 22 Apr 2025 23:30:31 +0300 Subject: [PATCH 11/24] Update tests/__data__ --- .../expected/readme_update/_readme.md | 514 +++++++++--------- .../input/readme_update/.readme/template.md | 316 +++++------ 2 files changed, 415 insertions(+), 415 deletions(-) diff --git a/tests/__data__/expected/readme_update/_readme.md b/tests/__data__/expected/readme_update/_readme.md index 9fc78acc23..67b094c3de 100644 --- a/tests/__data__/expected/readme_update/_readme.md +++ b/tests/__data__/expected/readme_update/_readme.md @@ -1,257 +1,257 @@ -# IPTV [![update](https://github.com/iptv-org/iptv/actions/workflows/update.yml/badge.svg)](https://github.com/iptv-org/iptv/actions/workflows/update.yml) - -Collection of publicly available IPTV (Internet Protocol television) channels from all over the world. - -## Table of contents - -- 🚀 [How to use?](#how-to-use) -- 📺 [Playlists](#playlists) -- 🗄 [Database](#database) -- 👨‍💻 [API](#api) -- 📚 [Resources](#resources) -- 💬 [Discussions](#discussions) -- ❓ [FAQ](#faq) -- 🛠 [Contribution](#contribution) -- ⚖ [Legal](#legal) -- © [License](#license) - -## How to use? - -Simply insert one of the links below into [any video player](https://github.com/iptv-org/awesome-iptv#apps) that supports live streaming and press _Open_. - -![VLC Network Panel](https://github.com/iptv-org/iptv/raw/master/.readme/preview.png) - -## Playlists - -There are several versions of playlists that differ in the way they are grouped. - -### Main playlist - -Playlist includes all known channels except adult channels. - -``` -https://iptv-org.github.io/iptv/index.m3u -``` - -And here is the full version: - -``` -https://iptv-org.github.io/iptv/index.nsfw.m3u -``` - -### Grouped by category - -
-Expand -
- -Playlist in which each channel has its _category_ as a group title: - -``` -https://iptv-org.github.io/iptv/index.category.m3u -``` - -Same thing, but split up into separate files: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CategoryChannelsPlaylist
Animation0https://iptv-org.github.io/iptv/categories/animation.m3u
Auto0https://iptv-org.github.io/iptv/categories/auto.m3u
Business0https://iptv-org.github.io/iptv/categories/business.m3u
Classic0https://iptv-org.github.io/iptv/categories/classic.m3u
Comedy0https://iptv-org.github.io/iptv/categories/comedy.m3u
Cooking0https://iptv-org.github.io/iptv/categories/cooking.m3u
Culture0https://iptv-org.github.io/iptv/categories/culture.m3u
Documentary0https://iptv-org.github.io/iptv/categories/documentary.m3u
Education0https://iptv-org.github.io/iptv/categories/education.m3u
Entertainment0https://iptv-org.github.io/iptv/categories/entertainment.m3u
Family0https://iptv-org.github.io/iptv/categories/family.m3u
General2https://iptv-org.github.io/iptv/categories/general.m3u
Kids0https://iptv-org.github.io/iptv/categories/kids.m3u
Legislative0https://iptv-org.github.io/iptv/categories/legislative.m3u
Lifestyle0https://iptv-org.github.io/iptv/categories/lifestyle.m3u
Movies0https://iptv-org.github.io/iptv/categories/movies.m3u
Music0https://iptv-org.github.io/iptv/categories/music.m3u
News1https://iptv-org.github.io/iptv/categories/news.m3u
Outdoor0https://iptv-org.github.io/iptv/categories/outdoor.m3u
Relax0https://iptv-org.github.io/iptv/categories/relax.m3u
Religious0https://iptv-org.github.io/iptv/categories/religious.m3u
Science0https://iptv-org.github.io/iptv/categories/science.m3u
Series0https://iptv-org.github.io/iptv/categories/series.m3u
Shop0https://iptv-org.github.io/iptv/categories/shop.m3u
Sports0https://iptv-org.github.io/iptv/categories/sports.m3u
Travel0https://iptv-org.github.io/iptv/categories/travel.m3u
Weather1https://iptv-org.github.io/iptv/categories/weather.m3u
XXX1https://iptv-org.github.io/iptv/categories/xxx.m3u
Undefined3https://iptv-org.github.io/iptv/categories/undefined.m3u
- -
- -### Grouped by language - -
-Expand -
- -Playlist in which each channel has its _language_ as a group title: - -``` -https://iptv-org.github.io/iptv/index.language.m3u -``` - -Same thing, but split up into separate files: - - - - - - - - - - - - - -
LanguageChannelsPlaylist
Catalan1https://iptv-org.github.io/iptv/languages/cat.m3u
English1https://iptv-org.github.io/iptv/languages/eng.m3u
French1https://iptv-org.github.io/iptv/languages/fra.m3u
Russian1https://iptv-org.github.io/iptv/languages/rus.m3u
Undefined2https://iptv-org.github.io/iptv/languages/undefined.m3u
- -
- -### Grouped by country - -
-Expand -
- -Playlist in which each channel has its _country_ as a group title: - -``` -https://iptv-org.github.io/iptv/index.country.m3u -``` - -Same thing, but split up into separate files: - - - - - - - - - - - - - - - - - - - - - -
CountryChannelsPlaylist
🇨🇲 Cameroon1https://iptv-org.github.io/iptv/countries/cm.m3u
🇨🇦 Canada2https://iptv-org.github.io/iptv/countries/ca.m3u
      Ontario1https://iptv-org.github.io/iptv/subdivisions/ca-on.m3u
🇨🇻 Cape Verde1https://iptv-org.github.io/iptv/countries/cv.m3u
🇨🇬 Republic of the Congo1https://iptv-org.github.io/iptv/countries/cg.m3u
🇷🇪 Réunion1https://iptv-org.github.io/iptv/countries/re.m3u
🇷🇴 Romania1https://iptv-org.github.io/iptv/countries/ro.m3u
🇷🇺 Russia2https://iptv-org.github.io/iptv/countries/ru.m3u
🇷🇼 Rwanda1https://iptv-org.github.io/iptv/countries/rw.m3u
🇧🇱 Saint Barthélemy1https://iptv-org.github.io/iptv/countries/bl.m3u
🇸🇭 Saint Helena1https://iptv-org.github.io/iptv/countries/sh.m3u
🇰🇳 Saint Kitts and Nevis1https://iptv-org.github.io/iptv/countries/kn.m3u
Undefined2https://iptv-org.github.io/iptv/countries/undefined.m3u
- -
- -### Grouped by region - -
-Expand -
- -Playlist in which each channel has its _region_ as a group title: - -``` -https://iptv-org.github.io/iptv/index.region.m3u -``` - -Same thing, but split up into separate files: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
RegionChannelsPlaylist
Africa0https://iptv-org.github.io/iptv/regions/afr.m3u
Americas1https://iptv-org.github.io/iptv/regions/amer.m3u
Arab world0https://iptv-org.github.io/iptv/regions/arab.m3u
Asia2https://iptv-org.github.io/iptv/regions/asia.m3u
Asia-Pacific1https://iptv-org.github.io/iptv/regions/apac.m3u
Association of Southeast Asian Nations0https://iptv-org.github.io/iptv/regions/asean.m3u
Caribbean0https://iptv-org.github.io/iptv/regions/carib.m3u
Central America0https://iptv-org.github.io/iptv/regions/cenamer.m3u
Central Asia0https://iptv-org.github.io/iptv/regions/cas.m3u
Commonwealth of Independent States1https://iptv-org.github.io/iptv/regions/cis.m3u
Europe3https://iptv-org.github.io/iptv/regions/eur.m3u
Europe, the Middle East and Africa3https://iptv-org.github.io/iptv/regions/emea.m3u
Hispanic America0https://iptv-org.github.io/iptv/regions/hispam.m3u
Latin America0https://iptv-org.github.io/iptv/regions/latam.m3u
Latin America and the Caribbean0https://iptv-org.github.io/iptv/regions/lac.m3u
Maghreb0https://iptv-org.github.io/iptv/regions/maghreb.m3u
Middle East0https://iptv-org.github.io/iptv/regions/mideast.m3u
Middle East and North Africa0https://iptv-org.github.io/iptv/regions/mena.m3u
Nordics0https://iptv-org.github.io/iptv/regions/nord.m3u
North America1https://iptv-org.github.io/iptv/regions/noram.m3u
Northern America1https://iptv-org.github.io/iptv/regions/nam.m3u
Oceania0https://iptv-org.github.io/iptv/regions/oce.m3u
South America0https://iptv-org.github.io/iptv/regions/southam.m3u
South Asia1https://iptv-org.github.io/iptv/regions/sas.m3u
Sub-Saharan Africa0https://iptv-org.github.io/iptv/regions/ssa.m3u
West Africa0https://iptv-org.github.io/iptv/regions/wafr.m3u
Worldwide1https://iptv-org.github.io/iptv/regions/int.m3u
Undefined2https://iptv-org.github.io/iptv/regions/undefined.m3u
- -
- -## Database - -All channel data is taken from the [iptv-org/database](https://github.com/iptv-org/database) repository. If you find any errors please open a new [issue](https://github.com/iptv-org/database/issues) there. - -## API - -The API documentation can be found in the [iptv-org/api](https://github.com/iptv-org/api) repository. - -## Resources - -Links to other useful IPTV-related resources can be found in the [iptv-org/awesome-iptv](https://github.com/iptv-org/awesome-iptv) repository. - -## Discussions - -If you need help finding a channel, have a question or idea, welcome to the [Discussions](https://github.com/orgs/iptv-org/discussions). - -## FAQ - -The answers to the most popular questions can be found in the [FAQ.md](FAQ.md) file. - -## Contribution - -Please make sure to read the [Contributing Guide](CONTRIBUTING.md) before sending an issue or making a pull request. - -And thank you to everyone who has already contributed! - -### Backers - - - -### Contributors - - - -## Legal - -No video files are stored in this repository. The repository simply contains user-submitted links to publicly available video stream URLs, which to the best of our knowledge have been intentionally made publicly by the copyright holders. If any links in these playlists infringe on your rights as a copyright holder, they may be removed by sending a [pull request](https://github.com/iptv-org/iptv/pulls) or opening an [issue](https://github.com/iptv-org/iptv/issues/new?assignees=freearhey&labels=removal+request&template=--removal-request.yml&title=Remove%3A+). However, note that we have **no control** over the destination of the link, and just removing the link from the playlist will not remove its contents from the web. Note that linking does not directly infringe copyright because no copy is made on the site providing the link, and thus this is **not** a valid reason to send a DMCA notice to GitHub. To remove this content from the web, you should contact the web host that's actually hosting the content (**not** GitHub, nor the maintainers of this repository). - -## License - -[![CC0](http://mirrors.creativecommons.org/presskit/buttons/88x31/svg/cc-zero.svg)](LICENSE) +# IPTV [![update](https://github.com/iptv-org/iptv/actions/workflows/update.yml/badge.svg)](https://github.com/iptv-org/iptv/actions/workflows/update.yml) + +Collection of publicly available IPTV (Internet Protocol television) channels from all over the world. + +## Table of contents + +- 🚀 [How to use?](#how-to-use) +- 📺 [Playlists](#playlists) +- 🗄 [Database](#database) +- 👨‍💻 [API](#api) +- 📚 [Resources](#resources) +- 💬 [Discussions](#discussions) +- ❓ [FAQ](#faq) +- 🛠 [Contribution](#contribution) +- ⚖ [Legal](#legal) +- © [License](#license) + +## How to use? + +Simply insert one of the links below into [any video player](https://github.com/iptv-org/awesome-iptv#apps) that supports live streaming and press _Open_. + +![VLC Network Panel](https://github.com/iptv-org/iptv/raw/master/.readme/preview.png) + +## Playlists + +There are several versions of playlists that differ in the way they are grouped. + +### Main playlist + +Playlist includes all known channels except adult channels. + +``` +https://iptv-org.github.io/iptv/index.m3u +``` + +And here is the full version: + +``` +https://iptv-org.github.io/iptv/index.nsfw.m3u +``` + +### Grouped by category + +
+Expand +
+ +Playlist in which each channel has its _category_ as a group title: + +``` +https://iptv-org.github.io/iptv/index.category.m3u +``` + +Same thing, but split up into separate files: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CategoryChannelsPlaylist
Animation0https://iptv-org.github.io/iptv/categories/animation.m3u
Auto0https://iptv-org.github.io/iptv/categories/auto.m3u
Business0https://iptv-org.github.io/iptv/categories/business.m3u
Classic0https://iptv-org.github.io/iptv/categories/classic.m3u
Comedy0https://iptv-org.github.io/iptv/categories/comedy.m3u
Cooking0https://iptv-org.github.io/iptv/categories/cooking.m3u
Culture0https://iptv-org.github.io/iptv/categories/culture.m3u
Documentary0https://iptv-org.github.io/iptv/categories/documentary.m3u
Education0https://iptv-org.github.io/iptv/categories/education.m3u
Entertainment0https://iptv-org.github.io/iptv/categories/entertainment.m3u
Family0https://iptv-org.github.io/iptv/categories/family.m3u
General2https://iptv-org.github.io/iptv/categories/general.m3u
Kids0https://iptv-org.github.io/iptv/categories/kids.m3u
Legislative0https://iptv-org.github.io/iptv/categories/legislative.m3u
Lifestyle0https://iptv-org.github.io/iptv/categories/lifestyle.m3u
Movies0https://iptv-org.github.io/iptv/categories/movies.m3u
Music0https://iptv-org.github.io/iptv/categories/music.m3u
News1https://iptv-org.github.io/iptv/categories/news.m3u
Outdoor0https://iptv-org.github.io/iptv/categories/outdoor.m3u
Relax0https://iptv-org.github.io/iptv/categories/relax.m3u
Religious0https://iptv-org.github.io/iptv/categories/religious.m3u
Science0https://iptv-org.github.io/iptv/categories/science.m3u
Series0https://iptv-org.github.io/iptv/categories/series.m3u
Shop0https://iptv-org.github.io/iptv/categories/shop.m3u
Sports0https://iptv-org.github.io/iptv/categories/sports.m3u
Travel0https://iptv-org.github.io/iptv/categories/travel.m3u
Weather1https://iptv-org.github.io/iptv/categories/weather.m3u
XXX1https://iptv-org.github.io/iptv/categories/xxx.m3u
Undefined3https://iptv-org.github.io/iptv/categories/undefined.m3u
+ +
+ +### Grouped by language + +
+Expand +
+ +Playlist in which each channel has its _language_ as a group title: + +``` +https://iptv-org.github.io/iptv/index.language.m3u +``` + +Same thing, but split up into separate files: + + + + + + + + + + + + + +
LanguageChannelsPlaylist
Catalan1https://iptv-org.github.io/iptv/languages/cat.m3u
English1https://iptv-org.github.io/iptv/languages/eng.m3u
French1https://iptv-org.github.io/iptv/languages/fra.m3u
Russian1https://iptv-org.github.io/iptv/languages/rus.m3u
Undefined2https://iptv-org.github.io/iptv/languages/undefined.m3u
+ +
+ +### Grouped by country + +
+Expand +
+ +Playlist in which each channel has its _country_ as a group title: + +``` +https://iptv-org.github.io/iptv/index.country.m3u +``` + +Same thing, but split up into separate files: + + + + + + + + + + + + + + + + + + + + + +
CountryChannelsPlaylist
🇨🇲 Cameroon1https://iptv-org.github.io/iptv/countries/cm.m3u
🇨🇦 Canada2https://iptv-org.github.io/iptv/countries/ca.m3u
      Ontario1https://iptv-org.github.io/iptv/subdivisions/ca-on.m3u
🇨🇻 Cape Verde1https://iptv-org.github.io/iptv/countries/cv.m3u
🇨🇬 Republic of the Congo1https://iptv-org.github.io/iptv/countries/cg.m3u
🇷🇪 Réunion1https://iptv-org.github.io/iptv/countries/re.m3u
🇷🇴 Romania1https://iptv-org.github.io/iptv/countries/ro.m3u
🇷🇺 Russia2https://iptv-org.github.io/iptv/countries/ru.m3u
🇷🇼 Rwanda1https://iptv-org.github.io/iptv/countries/rw.m3u
🇧🇱 Saint Barthélemy1https://iptv-org.github.io/iptv/countries/bl.m3u
🇸🇭 Saint Helena1https://iptv-org.github.io/iptv/countries/sh.m3u
🇰🇳 Saint Kitts and Nevis1https://iptv-org.github.io/iptv/countries/kn.m3u
Undefined2https://iptv-org.github.io/iptv/countries/undefined.m3u
+ +
+ +### Grouped by region + +
+Expand +
+ +Playlist in which each channel has its _region_ as a group title: + +``` +https://iptv-org.github.io/iptv/index.region.m3u +``` + +Same thing, but split up into separate files: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RegionChannelsPlaylist
Africa0https://iptv-org.github.io/iptv/regions/afr.m3u
Americas1https://iptv-org.github.io/iptv/regions/amer.m3u
Arab world0https://iptv-org.github.io/iptv/regions/arab.m3u
Asia2https://iptv-org.github.io/iptv/regions/asia.m3u
Asia-Pacific1https://iptv-org.github.io/iptv/regions/apac.m3u
Association of Southeast Asian Nations0https://iptv-org.github.io/iptv/regions/asean.m3u
Caribbean0https://iptv-org.github.io/iptv/regions/carib.m3u
Central America0https://iptv-org.github.io/iptv/regions/cenamer.m3u
Central Asia0https://iptv-org.github.io/iptv/regions/cas.m3u
Commonwealth of Independent States1https://iptv-org.github.io/iptv/regions/cis.m3u
Europe3https://iptv-org.github.io/iptv/regions/eur.m3u
Europe, the Middle East and Africa3https://iptv-org.github.io/iptv/regions/emea.m3u
Hispanic America0https://iptv-org.github.io/iptv/regions/hispam.m3u
Latin America0https://iptv-org.github.io/iptv/regions/latam.m3u
Latin America and the Caribbean0https://iptv-org.github.io/iptv/regions/lac.m3u
Maghreb0https://iptv-org.github.io/iptv/regions/maghreb.m3u
Middle East0https://iptv-org.github.io/iptv/regions/mideast.m3u
Middle East and North Africa0https://iptv-org.github.io/iptv/regions/mena.m3u
Nordics0https://iptv-org.github.io/iptv/regions/nord.m3u
North America1https://iptv-org.github.io/iptv/regions/noram.m3u
Northern America1https://iptv-org.github.io/iptv/regions/nam.m3u
Oceania0https://iptv-org.github.io/iptv/regions/oce.m3u
South America0https://iptv-org.github.io/iptv/regions/southam.m3u
South Asia1https://iptv-org.github.io/iptv/regions/sas.m3u
Sub-Saharan Africa0https://iptv-org.github.io/iptv/regions/ssa.m3u
West Africa0https://iptv-org.github.io/iptv/regions/wafr.m3u
Worldwide1https://iptv-org.github.io/iptv/regions/int.m3u
Undefined2https://iptv-org.github.io/iptv/regions/undefined.m3u
+ +
+ +## Database + +All channel data is taken from the [iptv-org/database](https://github.com/iptv-org/database) repository. If you find any errors please open a new [issue](https://github.com/iptv-org/database/issues) there. + +## API + +The API documentation can be found in the [iptv-org/api](https://github.com/iptv-org/api) repository. + +## Resources + +Links to other useful IPTV-related resources can be found in the [iptv-org/awesome-iptv](https://github.com/iptv-org/awesome-iptv) repository. + +## Discussions + +If you need help finding a channel, have a question or idea, welcome to the [Discussions](https://github.com/orgs/iptv-org/discussions). + +## FAQ + +The answers to the most popular questions can be found in the [FAQ.md](FAQ.md) file. + +## Contribution + +Please make sure to read the [Contributing Guide](CONTRIBUTING.md) before sending an issue or making a pull request. + +And thank you to everyone who has already contributed! + +### Backers + + + +### Contributors + + + +## Legal + +No video files are stored in this repository. The repository simply contains user-submitted links to publicly available video stream URLs, which to the best of our knowledge have been intentionally made publicly by the copyright holders. If any links in these playlists infringe on your rights as a copyright holder, they may be removed by sending a [pull request](https://github.com/iptv-org/iptv/pulls) or opening an [issue](https://github.com/iptv-org/iptv/issues/new?assignees=freearhey&labels=removal+request&template=--removal-request.yml&title=Remove%3A+). However, note that we have **no control** over the destination of the link, and just removing the link from the playlist will not remove its contents from the web. Note that linking does not directly infringe copyright because no copy is made on the site providing the link, and thus this is **not** a valid reason to send a DMCA notice to GitHub. To remove this content from the web, you should contact the web host that's actually hosting the content (**not** GitHub, nor the maintainers of this repository). + +## License + +[![CC0](http://mirrors.creativecommons.org/presskit/buttons/88x31/svg/cc-zero.svg)](LICENSE) diff --git a/tests/__data__/input/readme_update/.readme/template.md b/tests/__data__/input/readme_update/.readme/template.md index 04cab4cd5e..4883a98e4f 100644 --- a/tests/__data__/input/readme_update/.readme/template.md +++ b/tests/__data__/input/readme_update/.readme/template.md @@ -1,158 +1,158 @@ -# IPTV [![update](https://github.com/iptv-org/iptv/actions/workflows/update.yml/badge.svg)](https://github.com/iptv-org/iptv/actions/workflows/update.yml) - -Collection of publicly available IPTV (Internet Protocol television) channels from all over the world. - -## Table of contents - -- 🚀 [How to use?](#how-to-use) -- 📺 [Playlists](#playlists) -- 🗄 [Database](#database) -- 👨‍💻 [API](#api) -- 📚 [Resources](#resources) -- 💬 [Discussions](#discussions) -- ❓ [FAQ](#faq) -- 🛠 [Contribution](#contribution) -- ⚖ [Legal](#legal) -- © [License](#license) - -## How to use? - -Simply insert one of the links below into [any video player](https://github.com/iptv-org/awesome-iptv#apps) that supports live streaming and press _Open_. - -![VLC Network Panel](https://github.com/iptv-org/iptv/raw/master/.readme/preview.png) - -## Playlists - -There are several versions of playlists that differ in the way they are grouped. - -### Main playlist - -Playlist includes all known channels except adult channels. - -``` -https://iptv-org.github.io/iptv/index.m3u -``` - -And here is the full version: - -``` -https://iptv-org.github.io/iptv/index.nsfw.m3u -``` - -### Grouped by category - -
-Expand -
- -Playlist in which each channel has its _category_ as a group title: - -``` -https://iptv-org.github.io/iptv/index.category.m3u -``` - -Same thing, but split up into separate files: - - -#include "tests/__data__/output/.readme/_categories.md" - -
- -### Grouped by language - -
-Expand -
- -Playlist in which each channel has its _language_ as a group title: - -``` -https://iptv-org.github.io/iptv/index.language.m3u -``` - -Same thing, but split up into separate files: - - -#include "tests/__data__/output/.readme/_languages.md" - -
- -### Grouped by country - -
-Expand -
- -Playlist in which each channel has its _country_ as a group title: - -``` -https://iptv-org.github.io/iptv/index.country.m3u -``` - -Same thing, but split up into separate files: - - -#include "tests/__data__/output/.readme/_countries.md" - -
- -### Grouped by region - -
-Expand -
- -Playlist in which each channel has its _region_ as a group title: - -``` -https://iptv-org.github.io/iptv/index.region.m3u -``` - -Same thing, but split up into separate files: - - -#include "tests/__data__/output/.readme/_regions.md" - -
- -## Database - -All channel data is taken from the [iptv-org/database](https://github.com/iptv-org/database) repository. If you find any errors please open a new [issue](https://github.com/iptv-org/database/issues) there. - -## API - -The API documentation can be found in the [iptv-org/api](https://github.com/iptv-org/api) repository. - -## Resources - -Links to other useful IPTV-related resources can be found in the [iptv-org/awesome-iptv](https://github.com/iptv-org/awesome-iptv) repository. - -## Discussions - -If you need help finding a channel, have a question or idea, welcome to the [Discussions](https://github.com/orgs/iptv-org/discussions). - -## FAQ - -The answers to the most popular questions can be found in the [FAQ.md](FAQ.md) file. - -## Contribution - -Please make sure to read the [Contributing Guide](CONTRIBUTING.md) before sending an issue or making a pull request. - -And thank you to everyone who has already contributed! - -### Backers - - - -### Contributors - - - -## Legal - -No video files are stored in this repository. The repository simply contains user-submitted links to publicly available video stream URLs, which to the best of our knowledge have been intentionally made publicly by the copyright holders. If any links in these playlists infringe on your rights as a copyright holder, they may be removed by sending a [pull request](https://github.com/iptv-org/iptv/pulls) or opening an [issue](https://github.com/iptv-org/iptv/issues/new?assignees=freearhey&labels=removal+request&template=--removal-request.yml&title=Remove%3A+). However, note that we have **no control** over the destination of the link, and just removing the link from the playlist will not remove its contents from the web. Note that linking does not directly infringe copyright because no copy is made on the site providing the link, and thus this is **not** a valid reason to send a DMCA notice to GitHub. To remove this content from the web, you should contact the web host that's actually hosting the content (**not** GitHub, nor the maintainers of this repository). - -## License - -[![CC0](http://mirrors.creativecommons.org/presskit/buttons/88x31/svg/cc-zero.svg)](LICENSE) +# IPTV [![update](https://github.com/iptv-org/iptv/actions/workflows/update.yml/badge.svg)](https://github.com/iptv-org/iptv/actions/workflows/update.yml) + +Collection of publicly available IPTV (Internet Protocol television) channels from all over the world. + +## Table of contents + +- 🚀 [How to use?](#how-to-use) +- 📺 [Playlists](#playlists) +- 🗄 [Database](#database) +- 👨‍💻 [API](#api) +- 📚 [Resources](#resources) +- 💬 [Discussions](#discussions) +- ❓ [FAQ](#faq) +- 🛠 [Contribution](#contribution) +- ⚖ [Legal](#legal) +- © [License](#license) + +## How to use? + +Simply insert one of the links below into [any video player](https://github.com/iptv-org/awesome-iptv#apps) that supports live streaming and press _Open_. + +![VLC Network Panel](https://github.com/iptv-org/iptv/raw/master/.readme/preview.png) + +## Playlists + +There are several versions of playlists that differ in the way they are grouped. + +### Main playlist + +Playlist includes all known channels except adult channels. + +``` +https://iptv-org.github.io/iptv/index.m3u +``` + +And here is the full version: + +``` +https://iptv-org.github.io/iptv/index.nsfw.m3u +``` + +### Grouped by category + +
+Expand +
+ +Playlist in which each channel has its _category_ as a group title: + +``` +https://iptv-org.github.io/iptv/index.category.m3u +``` + +Same thing, but split up into separate files: + + +#include "tests/__data__/output/.readme/_categories.md" + +
+ +### Grouped by language + +
+Expand +
+ +Playlist in which each channel has its _language_ as a group title: + +``` +https://iptv-org.github.io/iptv/index.language.m3u +``` + +Same thing, but split up into separate files: + + +#include "tests/__data__/output/.readme/_languages.md" + +
+ +### Grouped by country + +
+Expand +
+ +Playlist in which each channel has its _country_ as a group title: + +``` +https://iptv-org.github.io/iptv/index.country.m3u +``` + +Same thing, but split up into separate files: + + +#include "tests/__data__/output/.readme/_countries.md" + +
+ +### Grouped by region + +
+Expand +
+ +Playlist in which each channel has its _region_ as a group title: + +``` +https://iptv-org.github.io/iptv/index.region.m3u +``` + +Same thing, but split up into separate files: + + +#include "tests/__data__/output/.readme/_regions.md" + +
+ +## Database + +All channel data is taken from the [iptv-org/database](https://github.com/iptv-org/database) repository. If you find any errors please open a new [issue](https://github.com/iptv-org/database/issues) there. + +## API + +The API documentation can be found in the [iptv-org/api](https://github.com/iptv-org/api) repository. + +## Resources + +Links to other useful IPTV-related resources can be found in the [iptv-org/awesome-iptv](https://github.com/iptv-org/awesome-iptv) repository. + +## Discussions + +If you need help finding a channel, have a question or idea, welcome to the [Discussions](https://github.com/orgs/iptv-org/discussions). + +## FAQ + +The answers to the most popular questions can be found in the [FAQ.md](FAQ.md) file. + +## Contribution + +Please make sure to read the [Contributing Guide](CONTRIBUTING.md) before sending an issue or making a pull request. + +And thank you to everyone who has already contributed! + +### Backers + + + +### Contributors + + + +## Legal + +No video files are stored in this repository. The repository simply contains user-submitted links to publicly available video stream URLs, which to the best of our knowledge have been intentionally made publicly by the copyright holders. If any links in these playlists infringe on your rights as a copyright holder, they may be removed by sending a [pull request](https://github.com/iptv-org/iptv/pulls) or opening an [issue](https://github.com/iptv-org/iptv/issues/new?assignees=freearhey&labels=removal+request&template=--removal-request.yml&title=Remove%3A+). However, note that we have **no control** over the destination of the link, and just removing the link from the playlist will not remove its contents from the web. Note that linking does not directly infringe copyright because no copy is made on the site providing the link, and thus this is **not** a valid reason to send a DMCA notice to GitHub. To remove this content from the web, you should contact the web host that's actually hosting the content (**not** GitHub, nor the maintainers of this repository). + +## License + +[![CC0](http://mirrors.creativecommons.org/presskit/buttons/88x31/svg/cc-zero.svg)](LICENSE) From 66136e051cafbbe14c861232d9ebe8a03ba094df Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Tue, 22 Apr 2025 23:30:34 +0300 Subject: [PATCH 12/24] Update htmlTable.ts --- scripts/core/htmlTable.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/core/htmlTable.ts b/scripts/core/htmlTable.ts index 1caa85fa0c..b4f7b42ac6 100644 --- a/scripts/core/htmlTable.ts +++ b/scripts/core/htmlTable.ts @@ -16,15 +16,15 @@ export class HTMLTable { } toString() { - let output = '\n' + let output = '
\r\n' - output += ' \n ' + output += ' \r\n ' for (const column of this.columns) { output += `` } - output += '\n \n' + output += '\r\n \r\n' - output += ' \n' + output += ' \r\n' for (const item of this.data) { output += ' ' let i = 0 @@ -35,9 +35,9 @@ export class HTMLTable { output += `${item[prop]}` i++ } - output += '\n' + output += '\r\n' } - output += ' \n' + output += ' \r\n' output += '
${column.name}
' From a5ebe89004f4e96ef8bee97d1983701cc68b6279 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 23 Apr 2025 05:19:55 +0300 Subject: [PATCH 13/24] Update tests/__data__ --- .../expected/api_generate/.api/streams.json | 112 +++++++++--------- .../__data__/expected/playlist_format/in.m3u | 6 +- .../__data__/expected/playlist_format/nl.m3u | 22 ++-- .../.gh-pages/categories/general.m3u | 14 +-- .../.gh-pages/categories/legislative.m3u | 2 +- .../.gh-pages/categories/news.m3u | 6 +- .../.gh-pages/categories/undefined.m3u | 30 ++--- .../.gh-pages/categories/weather.m3u | 6 +- .../.gh-pages/categories/xxx.m3u | 6 +- .../.gh-pages/countries/ad.m3u | 6 +- .../.gh-pages/countries/ca.m3u | 6 +- .../.gh-pages/countries/ru.m3u | 6 +- .../.gh-pages/countries/undefined.m3u | 22 ++-- .../.gh-pages/index.category.m3u | 50 ++++---- .../.gh-pages/index.country.m3u | 62 +++++----- .../.gh-pages/index.language.m3u | 46 +++---- .../playlist_generate/.gh-pages/index.m3u | 46 +++---- .../.gh-pages/index.region.m3u | 86 +++++++------- .../.gh-pages/languages/eng.m3u | 6 +- .../.gh-pages/languages/rus.m3u | 6 +- .../.gh-pages/languages/undefined.m3u | 34 +++--- .../.gh-pages/regions/afr.m3u | 2 +- .../.gh-pages/regions/amer.m3u | 6 +- .../.gh-pages/regions/apac.m3u | 2 +- .../.gh-pages/regions/arab.m3u | 2 +- .../.gh-pages/regions/asia.m3u | 10 +- .../.gh-pages/regions/carib.m3u | 2 +- .../.gh-pages/regions/cas.m3u | 6 +- .../.gh-pages/regions/cis.m3u | 10 +- .../.gh-pages/regions/emea.m3u | 14 +-- .../.gh-pages/regions/eur.m3u | 14 +-- .../.gh-pages/regions/hispam.m3u | 2 +- .../.gh-pages/regions/int.m3u | 10 +- .../.gh-pages/regions/lac.m3u | 2 +- .../.gh-pages/regions/latam.m3u | 2 +- .../.gh-pages/regions/maghreb.m3u | 2 +- .../.gh-pages/regions/mena.m3u | 2 +- .../.gh-pages/regions/mideast.m3u | 2 +- .../.gh-pages/regions/nam.m3u | 6 +- .../.gh-pages/regions/noram.m3u | 6 +- .../.gh-pages/regions/nord.m3u | 2 +- .../.gh-pages/regions/oce.m3u | 2 +- .../.gh-pages/regions/sas.m3u | 2 +- .../.gh-pages/regions/ssa.m3u | 2 +- .../.gh-pages/regions/undefined.m3u | 22 ++-- .../.gh-pages/regions/wafr.m3u | 2 +- .../.gh-pages/subdivisions/ca-on.m3u | 6 +- .../__data__/expected/playlist_update/cy.m3u | 6 +- .../__data__/expected/playlist_update/fr.m3u | 10 +- .../__data__/expected/playlist_update/uk.m3u | 14 +-- .../__data__/expected/playlist_update/us.m3u | 12 +- tests/__data__/input/api_generate/ad.m3u | 10 +- tests/__data__/input/api_generate/ca.m3u | 6 +- tests/__data__/input/api_generate/in.m3u | 6 +- tests/__data__/input/api_generate/uk.m3u | 6 +- .../__data__/input/api_generate/unsorted.m3u | 18 +-- .../__data__/input/playlist_edit/playlist.m3u | 8 +- tests/__data__/input/playlist_format/in.m3u | 6 +- tests/__data__/input/playlist_format/nl.m3u | 26 ++-- tests/__data__/input/playlist_generate/ad.m3u | 18 +-- tests/__data__/input/playlist_generate/ca.m3u | 6 +- tests/__data__/input/playlist_generate/in.m3u | 6 +- tests/__data__/input/playlist_generate/kg.m3u | 6 +- tests/__data__/input/playlist_generate/uk.m3u | 6 +- .../input/playlist_generate/unsorted.m3u | 22 ++-- tests/__data__/input/playlist_test/ag.m3u | 8 +- tests/__data__/input/playlist_update/br.m3u | 12 +- tests/__data__/input/playlist_update/cy.m3u | 14 +-- tests/__data__/input/playlist_update/uk.m3u | 10 +- .../input/playlist_validate/us_blocked.m3u | 14 +-- .../input/playlist_validate/wrong_id.m3u | 6 +- tests/__data__/input/report_create/br.m3u | 12 +- tests/__data__/input/report_create/cy.m3u | 10 +- tests/__data__/input/report_create/uk.m3u | 10 +- tests/__data__/input/report_create/us.m3u | 6 +- 75 files changed, 507 insertions(+), 507 deletions(-) diff --git a/tests/__data__/expected/api_generate/.api/streams.json b/tests/__data__/expected/api_generate/.api/streams.json index b85871048b..55128bca36 100644 --- a/tests/__data__/expected/api_generate/.api/streams.json +++ b/tests/__data__/expected/api_generate/.api/streams.json @@ -1,57 +1,57 @@ -[ - { - "channel": null, - "feed": null, - "url": "http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8", - "referrer": null, - "user_agent": null - }, - { - "channel": null, - "feed": null, - "url": "http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8", - "referrer": "http://imn.iq", - "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148" - }, - { - "channel": "AndorraTV.ad", - "feed": "SD", - "url": "https://iptv-all.lanesh4d0w.repl.co/andorra/atv", - "referrer": null, - "user_agent": null - }, - { - "channel": "BBCNews.uk", - "url": "http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8", - "referrer": null, - "user_agent": null - }, - { - "channel": "LDPRTV.ru", - "feed": null, - "url": "http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.m3u8", - "referrer": null, - "user_agent": null - }, - { - "channel": "MeteoMedia.ca", - "feed": null, - "url": "http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8", - "referrer": null, - "user_agent": null - }, - { - "channel": "VisitXTV.nl", - "feed": null, - "url": "https://stream.visit-x.tv/vxtv/ngrp:live_all/30fps.m3u8", - "referrer": null, - "user_agent": null - }, - { - "channel": "Zoo.ad", - "feed": null, - "url": "https://iptv-all.lanesh4d0w.repl.co/andorra/zoo", - "referrer": null, - "user_agent": null - } +[ + { + "channel": null, + "feed": null, + "url": "http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8", + "referrer": null, + "user_agent": null + }, + { + "channel": null, + "feed": null, + "url": "http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8", + "referrer": "http://imn.iq", + "user_agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148" + }, + { + "channel": "AndorraTV.ad", + "feed": "SD", + "url": "https://iptv-all.lanesh4d0w.repl.co/andorra/atv", + "referrer": null, + "user_agent": null + }, + { + "channel": "BBCNews.uk", + "url": "http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8", + "referrer": null, + "user_agent": null + }, + { + "channel": "LDPRTV.ru", + "feed": null, + "url": "http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.m3u8", + "referrer": null, + "user_agent": null + }, + { + "channel": "MeteoMedia.ca", + "feed": null, + "url": "http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8", + "referrer": null, + "user_agent": null + }, + { + "channel": "VisitXTV.nl", + "feed": null, + "url": "https://stream.visit-x.tv/vxtv/ngrp:live_all/30fps.m3u8", + "referrer": null, + "user_agent": null + }, + { + "channel": "Zoo.ad", + "feed": null, + "url": "https://iptv-all.lanesh4d0w.repl.co/andorra/zoo", + "referrer": null, + "user_agent": null + } ] \ No newline at end of file diff --git a/tests/__data__/expected/playlist_format/in.m3u b/tests/__data__/expected/playlist_format/in.m3u index 44a00ace12..2615da4e4c 100644 --- a/tests/__data__/expected/playlist_format/in.m3u +++ b/tests/__data__/expected/playlist_format/in.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="",Manorama News -2 [U3] (480p) [Geo-blocked] [Not 24/7] -https://ythls.onrender.com/channel/UCP0uG-mcMImgKnJz-VjJZmQ.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="",Manorama News -2 [U3] (480p) [Geo-blocked] [Not 24/7] +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 8f1e0ba2ac..d08a2ac80c 100644 --- a/tests/__data__/expected/playlist_format/nl.m3u +++ b/tests/__data__/expected/playlist_format/nl.m3u @@ -1,11 +1,11 @@ -#EXTM3U -#EXTINF:-1 tvg-id="NPO1.nl@SD",NPO 1 (342p) [Geo-blocked] -http://resolver.streaming.api.nos.nl/livestream?url=/live/npo/tvlive/npo1/npo1.isml/.m3u8 -#EXTINF:-1 tvg-id="NPO2.nl",NPO 2 (342p) -http://resolver.streaming.api.nos.nl/livestream?url=/live/npo/tvlive/npo2/npo2.isml/.m3u8 -#EXTINF:-1 tvg-id="NPO2.nl" 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] -#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 -http://stream.tvtap.net:8081/live/nl-npo2.stream/playlist.m3u8 -#EXTINF:-1 tvg-id="NPO2.nl",NPO 2 [Geo-blocked] -http://resolver.streaming.api.nos.nl/livestream?url=/live/npo/tvlive/npo2/npo22.isml/.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="NPO1.nl@SD",NPO 1 (342p) [Geo-blocked] +http://resolver.streaming.api.nos.nl/livestream?url=/live/npo/tvlive/npo1/npo1.isml/.m3u8 +#EXTINF:-1 tvg-id="NPO2.nl",NPO 2 (342p) +http://resolver.streaming.api.nos.nl/livestream?url=/live/npo/tvlive/npo2/npo2.isml/.m3u8 +#EXTINF:-1 tvg-id="NPO2.nl" 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] +#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 +http://stream.tvtap.net:8081/live/nl-npo2.stream/playlist.m3u8 +#EXTINF:-1 tvg-id="NPO2.nl",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/general.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/categories/general.m3u index e9587edc70..d43fa35f64 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/categories/general.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/categories/general.m3u @@ -1,7 +1,7 @@ -#EXTM3U -#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General;News",BBC News HD -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 -#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) -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="General",ЭлТР (480p) [Not 24/7] -http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General;News",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) +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="General",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/categories/legislative.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/categories/legislative.m3u index fcd718794a..7452e53aad 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/categories/legislative.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/categories/legislative.m3u @@ -1 +1 @@ -#EXTM3U +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/categories/news.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/categories/news.m3u index cf4e62a9a5..85c75b78bf 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/categories/news.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/categories/news.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General;News",BBC News HD -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General;News",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.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 4182fcb1d6..cfdce7d0bf 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/categories/undefined.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/categories/undefined.m3u @@ -1,15 +1,15 @@ -#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] -#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 -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 -#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Undefined",ATV -https://iptv-all.lanesh4d0w.repl.co/andorra/atv -#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD -https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd -#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV -http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 -#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 -#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) -https://iptv-all.lanesh4d0w.repl.co/andorra/zoo +#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] +#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 +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Undefined",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv +#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD +https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV +http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 +#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 +#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) +https://iptv-all.lanesh4d0w.repl.co/andorra/zoo diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/categories/weather.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/categories/weather.m3u index 4686c68f4b..03a6963a33 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/categories/weather.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/categories/weather.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia -http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/categories/xxx.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/categories/xxx.m3u index d1e3a69bcd..a6791d64b8 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/categories/xxx.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/categories/xxx.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#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 +#EXTM3U +#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 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/countries/ad.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/countries/ad.m3u index de466399ac..9bc95be27d 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/countries/ad.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/countries/ad.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Undefined",ATV -https://iptv-all.lanesh4d0w.repl.co/andorra/atv +#EXTM3U +#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Undefined",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/countries/ca.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/countries/ca.m3u index 4686c68f4b..03a6963a33 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/countries/ca.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/countries/ca.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia -http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/countries/ru.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/countries/ru.m3u index cbfde92492..b701890a36 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/countries/ru.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/countries/ru.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) -http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) +http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.m3u8 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 da6b5f4bb7..eb0364cd58 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,11 @@ -#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] -#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 -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 -#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD -https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd -#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV -http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 -#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) -https://iptv-all.lanesh4d0w.repl.co/andorra/zoo +#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] +#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 +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD +https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV +http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 +#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) +https://iptv-all.lanesh4d0w.repl.co/andorra/zoo 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 2ce7c040e5..b3235a876c 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/index.category.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/index.category.m3u @@ -1,25 +1,25 @@ -#EXTM3U -#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General",BBC News HD -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 -#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) -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="General",ЭлТР (480p) [Not 24/7] -http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 -#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="News",BBC News HD -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 -#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia -http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/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] -#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 -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 -#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Undefined",ATV -https://iptv-all.lanesh4d0w.repl.co/andorra/atv -#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD -https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd -#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV -http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 -#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 -#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) -https://iptv-all.lanesh4d0w.repl.co/andorra/zoo +#EXTM3U +#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) +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="General",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="News",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/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] +#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 +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Undefined",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv +#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD +https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV +http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 +#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 +#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) +https://iptv-all.lanesh4d0w.repl.co/andorra/zoo 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 f5eba15e42..332df0029b 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/index.country.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/index.country.m3u @@ -1,31 +1,31 @@ -#EXTM3U -#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Andorra",ATV -https://iptv-all.lanesh4d0w.repl.co/andorra/atv -#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Canada",Meteomedia -http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 -#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Kazakhstan",ЭлТР (480p) [Not 24/7] -http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 -#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Kyrgyzstan",ЭлТР (480p) [Not 24/7] -http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 -#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Russia",ЛДПР ТВ (1080p) -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="Tajikistan",ЭлТР (480p) [Not 24/7] -http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 -#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Turkmenistan",ЭлТР (480p) [Not 24/7] -http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 -#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Uzbekistan",ЭлТР (480p) [Not 24/7] -http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 -#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="International",BBC News HD -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 -#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="International",Duna World (576i) -http://146.59.85.40:89/dunaworld/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] -#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 -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 -#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD -https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd -#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV -http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 -#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) -https://iptv-all.lanesh4d0w.repl.co/andorra/zoo +#EXTM3U +#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Andorra",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Canada",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Kazakhstan",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Kyrgyzstan",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Russia",ЛДПР ТВ (1080p) +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="Tajikistan",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Turkmenistan",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Uzbekistan",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="International",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 +#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="International",Duna World (576i) +http://146.59.85.40:89/dunaworld/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] +#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 +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD +https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV +http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 +#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) +https://iptv-all.lanesh4d0w.repl.co/andorra/zoo 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 ed5f240141..e9f88f8ac2 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/index.language.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/index.language.m3u @@ -1,23 +1,23 @@ -#EXTM3U -#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Catalan",ATV -https://iptv-all.lanesh4d0w.repl.co/andorra/atv -#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="English",BBC News HD -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 -#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Russian",ЛДПР ТВ (1080p) -http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.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] -#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 -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 -#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD -https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd -#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV -http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 -#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 -#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Undefined",Meteomedia -http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 -#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) -https://iptv-all.lanesh4d0w.repl.co/andorra/zoo -#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Undefined",ЭлТР (480p) [Not 24/7] -http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Catalan",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv +#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="English",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Russian",ЛДПР ТВ (1080p) +http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.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] +#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 +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD +https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV +http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 +#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 +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Undefined",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) +https://iptv-all.lanesh4d0w.repl.co/andorra/zoo +#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Undefined",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/index.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/index.m3u index 1d27713b1d..fbf8cd7556 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/index.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/index.m3u @@ -1,23 +1,23 @@ -#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] -#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 -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 -#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Undefined",ATV -https://iptv-all.lanesh4d0w.repl.co/andorra/atv -#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD -https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd -#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General;News",BBC News HD -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 -#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV -http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 -#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 -#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia -http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 -#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) -https://iptv-all.lanesh4d0w.repl.co/andorra/zoo -#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) -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="General",ЭлТР (480p) [Not 24/7] -http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#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] +#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 +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Undefined",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv +#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD +https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd +#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General;News",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV +http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 +#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 +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) +https://iptv-all.lanesh4d0w.repl.co/andorra/zoo +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) +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="General",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/index.region.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/index.region.m3u index 90c7438cec..aa3534ff75 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/index.region.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/index.region.m3u @@ -1,43 +1,43 @@ -#EXTM3U -#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Americas",Meteomedia -http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 -#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Asia",ЛДПР ТВ (1080p) -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="Asia",ЭлТР (480p) [Not 24/7] -http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 -#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Central Asia",ЭлТР (480p) [Not 24/7] -http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 -#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Commonwealth of Independent States",ЛДПР ТВ (1080p) -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="Commonwealth of Independent States",ЭлТР (480p) [Not 24/7] -http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 -#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Europe",ATV -https://iptv-all.lanesh4d0w.repl.co/andorra/atv -#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Europe",ЛДПР ТВ (1080p) -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="Europe",ЭлТР (480p) [Not 24/7] -http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 -#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Europe, the Middle East and Africa",ATV -https://iptv-all.lanesh4d0w.repl.co/andorra/atv -#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Europe, the Middle East and Africa",ЛДПР ТВ (1080p) -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="Europe, the Middle East and Africa",ЭлТР (480p) [Not 24/7] -http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 -#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="North America",Meteomedia -http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 -#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Northern America",Meteomedia -http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 -#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="International",BBC News HD -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 -#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="International",Duna World (576i) -http://146.59.85.40:89/dunaworld/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] -#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 -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 -#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD -https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd -#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV -http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 -#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) -https://iptv-all.lanesh4d0w.repl.co/andorra/zoo +#EXTM3U +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Americas",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Asia",ЛДПР ТВ (1080p) +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="Asia",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="Central Asia",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Commonwealth of Independent States",ЛДПР ТВ (1080p) +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="Commonwealth of Independent States",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Europe",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Europe",ЛДПР ТВ (1080p) +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="Europe",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Europe, the Middle East and Africa",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="Europe, the Middle East and Africa",ЛДПР ТВ (1080p) +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="Europe, the Middle East and Africa",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="North America",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Northern America",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="International",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 +#EXTINF:-1 tvg-id="DunaWorld.hu" tvg-logo="https://i.imgur.com/uOBQJZS.png" group-title="International",Duna World (576i) +http://146.59.85.40:89/dunaworld/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] +#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 +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD +https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV +http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 +#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) +https://iptv-all.lanesh4d0w.repl.co/andorra/zoo diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/languages/eng.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/languages/eng.m3u index cf4e62a9a5..85c75b78bf 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/languages/eng.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/languages/eng.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General;News",BBC News HD -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General;News",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/languages/rus.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/languages/rus.m3u index cbfde92492..b701890a36 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/languages/rus.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/languages/rus.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) -http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) +http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.m3u8 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 5d59e689d7..0eeed846f3 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/languages/undefined.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/languages/undefined.m3u @@ -1,17 +1,17 @@ -#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] -#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 -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 -#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD -https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd -#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV -http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 -#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 -#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia -http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 -#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) -https://iptv-all.lanesh4d0w.repl.co/andorra/zoo -#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="General",ЭлТР (480p) [Not 24/7] -http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#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] +#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 +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD +https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV +http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 +#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 +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) +https://iptv-all.lanesh4d0w.repl.co/andorra/zoo +#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="General",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/afr.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/afr.m3u index fcd718794a..7452e53aad 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/afr.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/afr.m3u @@ -1 +1 @@ -#EXTM3U +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/amer.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/amer.m3u index 4686c68f4b..03a6963a33 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/amer.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/amer.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia -http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/apac.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/apac.m3u index fcd718794a..7452e53aad 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/apac.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/apac.m3u @@ -1 +1 @@ -#EXTM3U +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/arab.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/arab.m3u index fcd718794a..7452e53aad 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/arab.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/arab.m3u @@ -1 +1 @@ -#EXTM3U +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/asia.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/asia.m3u index 6f6d448e87..1a2c778e09 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/asia.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/asia.m3u @@ -1,5 +1,5 @@ -#EXTM3U -#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) -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="General",ЭлТР (480p) [Not 24/7] -http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) +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="General",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/carib.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/carib.m3u index fcd718794a..7452e53aad 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/carib.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/carib.m3u @@ -1 +1 @@ -#EXTM3U +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/cas.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/cas.m3u index a9387b8b44..847591ab04 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/cas.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/cas.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="General",ЭлТР (480p) [Not 24/7] -http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="ElTR.kg" tvg-logo="https://i.ibb.co/r6czQwQ/365049798-774721644658455-5702658175909463406-n-2.png" group-title="General",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/cis.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/cis.m3u index 6f6d448e87..1a2c778e09 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/cis.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/cis.m3u @@ -1,5 +1,5 @@ -#EXTM3U -#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) -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="General",ЭлТР (480p) [Not 24/7] -http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) +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="General",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/emea.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/emea.m3u index 16dd0c3954..10fbb43f94 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/emea.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/emea.m3u @@ -1,7 +1,7 @@ -#EXTM3U -#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Undefined",ATV -https://iptv-all.lanesh4d0w.repl.co/andorra/atv -#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) -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="General",ЭлТР (480p) [Not 24/7] -http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Undefined",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) +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="General",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/eur.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/eur.m3u index 16dd0c3954..10fbb43f94 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/eur.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/eur.m3u @@ -1,7 +1,7 @@ -#EXTM3U -#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Undefined",ATV -https://iptv-all.lanesh4d0w.repl.co/andorra/atv -#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) -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="General",ЭлТР (480p) [Not 24/7] -http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="AndorraTV.ad@SD" tvg-logo="" group-title="Undefined",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv +#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p) +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="General",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/hispam.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/hispam.m3u index fcd718794a..7452e53aad 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/hispam.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/hispam.m3u @@ -1 +1 @@ -#EXTM3U +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/int.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/int.m3u index c549c09ce1..15240af7d0 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/int.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/int.m3u @@ -1,5 +1,5 @@ -#EXTM3U -#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General;News",BBC News HD -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 -#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 +#EXTM3U +#EXTINF:-1 tvg-id="BBCNews.uk" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="General;News",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 +#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/regions/lac.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/lac.m3u index fcd718794a..7452e53aad 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/lac.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/lac.m3u @@ -1 +1 @@ -#EXTM3U +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/latam.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/latam.m3u index fcd718794a..7452e53aad 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/latam.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/latam.m3u @@ -1 +1 @@ -#EXTM3U +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/maghreb.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/maghreb.m3u index fcd718794a..7452e53aad 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/maghreb.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/maghreb.m3u @@ -1 +1 @@ -#EXTM3U +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/mena.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/mena.m3u index fcd718794a..7452e53aad 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/mena.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/mena.m3u @@ -1 +1 @@ -#EXTM3U +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/mideast.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/mideast.m3u index fcd718794a..7452e53aad 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/mideast.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/mideast.m3u @@ -1 +1 @@ -#EXTM3U +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/nam.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/nam.m3u index 4686c68f4b..03a6963a33 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/nam.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/nam.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia -http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/noram.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/noram.m3u index 4686c68f4b..03a6963a33 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/noram.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/noram.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia -http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/nord.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/nord.m3u index fcd718794a..7452e53aad 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/nord.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/nord.m3u @@ -1 +1 @@ -#EXTM3U +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/oce.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/oce.m3u index fcd718794a..7452e53aad 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/oce.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/oce.m3u @@ -1 +1 @@ -#EXTM3U +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/sas.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/sas.m3u index fcd718794a..7452e53aad 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/sas.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/sas.m3u @@ -1 +1 @@ -#EXTM3U +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/ssa.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/ssa.m3u index fcd718794a..7452e53aad 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/ssa.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/ssa.m3u @@ -1 +1 @@ -#EXTM3U +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/undefined.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/undefined.m3u index da6b5f4bb7..eb0364cd58 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/undefined.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/undefined.m3u @@ -1,11 +1,11 @@ -#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] -#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 -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 -#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD -https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd -#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV -http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 -#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) -https://iptv-all.lanesh4d0w.repl.co/andorra/zoo +#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] +#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 +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 +#EXTINF:-1 tvg-id="AndorraTV.ad@HD" tvg-logo="" group-title="Undefined",ATV HD +https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd +#EXTINF:-1 tvg-id="" tvg-logo="" group-title="Undefined",Daawah TV +http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 +#EXTINF:-1 tvg-id="Zoo.ad@HD" tvg-logo="" group-title="Undefined",Zoo (720p) +https://iptv-all.lanesh4d0w.repl.co/andorra/zoo diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/regions/wafr.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/regions/wafr.m3u index fcd718794a..7452e53aad 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/regions/wafr.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/regions/wafr.m3u @@ -1 +1 @@ -#EXTM3U +#EXTM3U diff --git a/tests/__data__/expected/playlist_generate/.gh-pages/subdivisions/ca-on.m3u b/tests/__data__/expected/playlist_generate/.gh-pages/subdivisions/ca-on.m3u index 4686c68f4b..03a6963a33 100644 --- a/tests/__data__/expected/playlist_generate/.gh-pages/subdivisions/ca-on.m3u +++ b/tests/__data__/expected/playlist_generate/.gh-pages/subdivisions/ca-on.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia -http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="MeteoMedia.ca" tvg-logo="https://s1.twnmm.com/images/en_ca/mobile/logos/twn-mobile-logo.png" group-title="Weather",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 diff --git a/tests/__data__/expected/playlist_update/cy.m3u b/tests/__data__/expected/playlist_update/cy.m3u index 987825ca1d..13482713e9 100644 --- a/tests/__data__/expected/playlist_update/cy.m3u +++ b/tests/__data__/expected/playlist_update/cy.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="AdaTV.cy",AdaTV -https://ythls.onrender.com/channel/UC40TUSUx490U5uR1lZt3Ajg.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="AdaTV.cy",AdaTV +https://ythls.onrender.com/channel/UC40TUSUx490U5uR1lZt3Ajg.m3u8 diff --git a/tests/__data__/expected/playlist_update/fr.m3u b/tests/__data__/expected/playlist_update/fr.m3u index 82856b3387..f4f93afdcf 100644 --- a/tests/__data__/expected/playlist_update/fr.m3u +++ b/tests/__data__/expected/playlist_update/fr.m3u @@ -1,5 +1,5 @@ -#EXTM3U -#EXTINF:-1 tvg-id="TFX.fr" 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 -#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 +#EXTM3U +#EXTINF:-1 tvg-id="TFX.fr" 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 +#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 6ebfc71cd2..a60ea6210e 100644 --- a/tests/__data__/expected/playlist_update/uk.m3u +++ b/tests/__data__/expected/playlist_update/uk.m3u @@ -1,7 +1,7 @@ -#EXTM3U -#EXTINF:-1 tvg-id="BBCNews.uk",BBC News HD (720p) [Not 24/7] -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",Beano TV -https://a5b4bacecd47433dad06d3189fc7422e.mediatailor.us-east-1.amazonaws.com/v1/manifest/04fd913bb278d8775298c26fdca9d9841f37601f/RakutenTV-eu_BeanoTV/b1f233d5-847c-437d-aa4f-f73e67a85323/2.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="BBCNews.uk",BBC News HD (720p) [Not 24/7] +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",Beano TV +https://a5b4bacecd47433dad06d3189fc7422e.mediatailor.us-east-1.amazonaws.com/v1/manifest/04fd913bb278d8775298c26fdca9d9841f37601f/RakutenTV-eu_BeanoTV/b1f233d5-847c-437d-aa4f-f73e67a85323/2.m3u8 diff --git a/tests/__data__/expected/playlist_update/us.m3u b/tests/__data__/expected/playlist_update/us.m3u index 5271483e5d..80ce51958a 100644 --- a/tests/__data__/expected/playlist_update/us.m3u +++ b/tests/__data__/expected/playlist_update/us.m3u @@ -1,6 +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-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",Fast TV -https://3fa797d5.wurl.com/manifest/f36d25e7e52f1ba8d7e56eb859c636563214f541/T05PX01vdG9yVHJlbmRGYXN0VFZfSExT/b5e5e0e2-12b3-4312-93c9-c0a7c50b41ca/4.m3u8 +#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-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",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/api_generate/ad.m3u index 64a1dc0cf4..9d8926e0ea 100644 --- a/tests/__data__/input/api_generate/ad.m3u +++ b/tests/__data__/input/api_generate/ad.m3u @@ -1,5 +1,5 @@ -#EXTM3U -#EXTINF:-1 tvg-id="Zoo.ad",Zoo (720p) -https://iptv-all.lanesh4d0w.repl.co/andorra/zoo -#EXTINF:-1 tvg-id="AndorraTV.ad@SD",ATV -https://iptv-all.lanesh4d0w.repl.co/andorra/atv +#EXTM3U +#EXTINF:-1 tvg-id="Zoo.ad",Zoo (720p) +https://iptv-all.lanesh4d0w.repl.co/andorra/zoo +#EXTINF:-1 tvg-id="AndorraTV.ad@SD",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv diff --git a/tests/__data__/input/api_generate/ca.m3u b/tests/__data__/input/api_generate/ca.m3u index 9c198b6829..672601d7f7 100644 --- a/tests/__data__/input/api_generate/ca.m3u +++ b/tests/__data__/input/api_generate/ca.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="MeteoMedia.ca",Meteomedia -http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="MeteoMedia.ca",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 diff --git a/tests/__data__/input/api_generate/in.m3u b/tests/__data__/input/api_generate/in.m3u index 62594eb4bf..c375e17ace 100644 --- a/tests/__data__/input/api_generate/in.m3u +++ b/tests/__data__/input/api_generate/in.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="",Daawah TV -http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="",Daawah TV +http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 diff --git a/tests/__data__/input/api_generate/uk.m3u b/tests/__data__/input/api_generate/uk.m3u index 5e8c7318e5..4a8a1f64ab 100644 --- a/tests/__data__/input/api_generate/uk.m3u +++ b/tests/__data__/input/api_generate/uk.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="BBCNews.uk",BBC News HD -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="BBCNews.uk",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 diff --git a/tests/__data__/input/api_generate/unsorted.m3u b/tests/__data__/input/api_generate/unsorted.m3u index 1a9c40b9fd..96f9299e2d 100644 --- a/tests/__data__/input/api_generate/unsorted.m3u +++ b/tests/__data__/input/api_generate/unsorted.m3u @@ -1,9 +1,9 @@ -#EXTM3U -#EXTINF:-1 tvg-id="LDPRTV.ru",ЛДПР ТВ (1080p) -http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.m3u8 -#EXTINF:-1 tvg-id="VisitXTV.nl",Visit-X TV -https://stream.visit-x.tv/vxtv/ngrp:live_all/30fps.m3u8 -#EXTINF:-1 tvg-id="" 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] -#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 -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="LDPRTV.ru",ЛДПР ТВ (1080p) +http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.m3u8 +#EXTINF:-1 tvg-id="VisitXTV.nl",Visit-X TV +https://stream.visit-x.tv/vxtv/ngrp:live_all/30fps.m3u8 +#EXTINF:-1 tvg-id="" 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] +#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 +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 diff --git a/tests/__data__/input/playlist_edit/playlist.m3u b/tests/__data__/input/playlist_edit/playlist.m3u index 2ebf1ffa7a..0abd41b6f2 100644 --- a/tests/__data__/input/playlist_edit/playlist.m3u +++ b/tests/__data__/input/playlist_edit/playlist.m3u @@ -1,5 +1,5 @@ -#EXTM3U -#EXTINF:-1 tvg-id="HewadTV.af",Hewad TV (720p) [Not 24/7] -http://51.210.199.58/hls/stream.m3u8 -#EXTINF:-1 tvg-id="",Télévision française 1 (480p) +#EXTM3U +#EXTINF:-1 tvg-id="HewadTV.af",Hewad TV (720p) [Not 24/7] +http://51.210.199.58/hls/stream.m3u8 +#EXTINF:-1 tvg-id="",Télévision française 1 (480p) https://live.relentlessinnovations.net:1936/imantv/imantv/playlist.m3u8 \ No newline at end of file diff --git a/tests/__data__/input/playlist_format/in.m3u b/tests/__data__/input/playlist_format/in.m3u index 47c77da2e0..3d0bcd3596 100644 --- a/tests/__data__/input/playlist_format/in.m3u +++ b/tests/__data__/input/playlist_format/in.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="mn.in",Manorama News -2 [U3] (480p) [Geo-blocked] [Not 24/7] -https://ythls.onrender.com/channel/UCP0uG-mcMImgKnJz-VjJZmQ.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="mn.in",Manorama News -2 [U3] (480p) [Geo-blocked] [Not 24/7] +https://ythls.onrender.com/channel/UCP0uG-mcMImgKnJz-VjJZmQ.m3u8 diff --git a/tests/__data__/input/playlist_format/nl.m3u b/tests/__data__/input/playlist_format/nl.m3u index 5fdeaea52e..39d56a3ef4 100644 --- a/tests/__data__/input/playlist_format/nl.m3u +++ b/tests/__data__/input/playlist_format/nl.m3u @@ -1,13 +1,13 @@ -#EXTM3U -#EXTINF:-1 tvg-id="NPO2.nl",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 -http://stream.tvtap.net:8081/live/nl-npo2.stream/playlist.m3u8? -#EXTINF:-1 tvg-id="NPO2.nl",NPO 2 [Geo-blocked] -http://resolver.streaming.api.nos.nl/livestream?url=/live/npo/tvlive/npo2/npo22.isml/.m3u8 -#EXTINF:-1 tvg-id="NPO2.nl",NPO 2 (342p) -http://resolver.streaming.api.nos.nl/livestream?url=/live/npo/tvlive/npo2/npo2.isml/.m3u8 -#EXTINF:-1 tvg-id="NPO1.nl@SD",NPO 1 (342p) [Geo-blocked] -http://resolver.streaming.api.nos.nl/livestream?url=/live/npo/tvlive/npo1/npo1.isml/.m3u8 -#EXTINF:-1 tvg-id="",NPO 2 (Duplicate) -http://resolver.streaming.api.nos.nl/livestream?url=/live/npo/tvlive/npo2/npo2.isml/.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="NPO2.nl",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 +http://stream.tvtap.net:8081/live/nl-npo2.stream/playlist.m3u8? +#EXTINF:-1 tvg-id="NPO2.nl",NPO 2 [Geo-blocked] +http://resolver.streaming.api.nos.nl/livestream?url=/live/npo/tvlive/npo2/npo22.isml/.m3u8 +#EXTINF:-1 tvg-id="NPO2.nl",NPO 2 (342p) +http://resolver.streaming.api.nos.nl/livestream?url=/live/npo/tvlive/npo2/npo2.isml/.m3u8 +#EXTINF:-1 tvg-id="NPO1.nl@SD",NPO 1 (342p) [Geo-blocked] +http://resolver.streaming.api.nos.nl/livestream?url=/live/npo/tvlive/npo1/npo1.isml/.m3u8 +#EXTINF:-1 tvg-id="",NPO 2 (Duplicate) +http://resolver.streaming.api.nos.nl/livestream?url=/live/npo/tvlive/npo2/npo2.isml/.m3u8 diff --git a/tests/__data__/input/playlist_generate/ad.m3u b/tests/__data__/input/playlist_generate/ad.m3u index 9a1c9e175a..3c3bcae1db 100644 --- a/tests/__data__/input/playlist_generate/ad.m3u +++ b/tests/__data__/input/playlist_generate/ad.m3u @@ -1,9 +1,9 @@ -#EXTM3U -#EXTINF:-1 tvg-id="Zoo.ad@HD",Zoo (720p) -https://iptv-all.lanesh4d0w.repl.co/andorra/zoo -#EXTINF:-1 tvg-id="AndorraTV.ad@SD",ATV -https://iptv-all.lanesh4d0w.repl.co/andorra/atv -#EXTINF:-1 tvg-id="AndorraTV.ad@HD",ATV HD -https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd -#EXTINF:-1 tvg-id="AndorraTV.ad",ATV -https://iptv-all.lanesh4d0w.repl.co/andorra/atv2 +#EXTM3U +#EXTINF:-1 tvg-id="Zoo.ad@HD",Zoo (720p) +https://iptv-all.lanesh4d0w.repl.co/andorra/zoo +#EXTINF:-1 tvg-id="AndorraTV.ad@SD",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv +#EXTINF:-1 tvg-id="AndorraTV.ad@HD",ATV HD +https://iptv-all.lanesh4d0w.repl.co/andorra/atv_hd +#EXTINF:-1 tvg-id="AndorraTV.ad",ATV +https://iptv-all.lanesh4d0w.repl.co/andorra/atv2 diff --git a/tests/__data__/input/playlist_generate/ca.m3u b/tests/__data__/input/playlist_generate/ca.m3u index 9c198b6829..672601d7f7 100644 --- a/tests/__data__/input/playlist_generate/ca.m3u +++ b/tests/__data__/input/playlist_generate/ca.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="MeteoMedia.ca",Meteomedia -http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="MeteoMedia.ca",Meteomedia +http://encodercdn1.frontline.ca/encoder181/output/Meteo_Media_720p/playlist.m3u8 diff --git a/tests/__data__/input/playlist_generate/in.m3u b/tests/__data__/input/playlist_generate/in.m3u index 62594eb4bf..c375e17ace 100644 --- a/tests/__data__/input/playlist_generate/in.m3u +++ b/tests/__data__/input/playlist_generate/in.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="",Daawah TV -http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="",Daawah TV +http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8 diff --git a/tests/__data__/input/playlist_generate/kg.m3u b/tests/__data__/input/playlist_generate/kg.m3u index 9c1d5a5755..9bf0832b51 100644 --- a/tests/__data__/input/playlist_generate/kg.m3u +++ b/tests/__data__/input/playlist_generate/kg.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="ElTR.kg",ЭлТР (480p) [Not 24/7] -http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="ElTR.kg",ЭлТР (480p) [Not 24/7] +http://gohoski.fvds.ru:3000/mediabay/162/index.m3u8 diff --git a/tests/__data__/input/playlist_generate/uk.m3u b/tests/__data__/input/playlist_generate/uk.m3u index 5e8c7318e5..4a8a1f64ab 100644 --- a/tests/__data__/input/playlist_generate/uk.m3u +++ b/tests/__data__/input/playlist_generate/uk.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="BBCNews.uk",BBC News HD -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="BBCNews.uk",BBC News HD +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8 diff --git a/tests/__data__/input/playlist_generate/unsorted.m3u b/tests/__data__/input/playlist_generate/unsorted.m3u index b2b798b155..826414ad22 100644 --- a/tests/__data__/input/playlist_generate/unsorted.m3u +++ b/tests/__data__/input/playlist_generate/unsorted.m3u @@ -1,11 +1,11 @@ -#EXTM3U -#EXTINF:-1 tvg-id="LDPRTV.ru",ЛДПР ТВ (1080p) -http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.m3u8 -#EXTINF:-1 tvg-id="VisitXTV.nl",Visit-X TV -https://stream.visit-x.tv/vxtv/ngrp:live_all/30fps.m3u8 -#EXTINF:-1 tvg-id="" 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] -#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 -http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 -#EXTINF:-1 tvg-id="DunaWorld.hu",Duna World (576i) -http://146.59.85.40:89/dunaworld/index.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="LDPRTV.ru",ЛДПР ТВ (1080p) +http://46.46.143.222:1935/live/mp4:ldpr.stream/blocked.m3u8 +#EXTINF:-1 tvg-id="VisitXTV.nl",Visit-X TV +https://stream.visit-x.tv/vxtv/ngrp:live_all/30fps.m3u8 +#EXTINF:-1 tvg-id="" 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] +#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 +http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index2.m3u8 +#EXTINF:-1 tvg-id="DunaWorld.hu",Duna World (576i) +http://146.59.85.40:89/dunaworld/index.m3u8 diff --git a/tests/__data__/input/playlist_test/ag.m3u b/tests/__data__/input/playlist_test/ag.m3u index 66b32b21d7..f4716e013b 100644 --- a/tests/__data__/input/playlist_test/ag.m3u +++ b/tests/__data__/input/playlist_test/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] +#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] https://query-streamlink.herokuapp.com/iptv-query?streaming-ip=https://www.twitch.tv/absliveantigua3 \ No newline at end of file diff --git a/tests/__data__/input/playlist_update/br.m3u b/tests/__data__/input/playlist_update/br.m3u index 6dcdaee152..31a8698610 100644 --- a/tests/__data__/input/playlist_update/br.m3u +++ b/tests/__data__/input/playlist_update/br.m3u @@ -1,6 +1,6 @@ -#EXTM3U -#EXTINF:-1 tvg-id="",VTV [Not 24/7] -https://ythls.onrender.com/channel/UC40TUSUx490U5uR1lZt3Ajg.m3u8 -#EXTINF:-1 tvg-id="",Tele2000 [Not 24/7] -#EXTVLCOPT:http-referrer=https://example2.com/ -https://servilive.com:3126/live/tele2000live.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="",VTV [Not 24/7] +https://ythls.onrender.com/channel/UC40TUSUx490U5uR1lZt3Ajg.m3u8 +#EXTINF:-1 tvg-id="",Tele2000 [Not 24/7] +#EXTVLCOPT:http-referrer=https://example2.com/ +https://servilive.com:3126/live/tele2000live.m3u8 diff --git a/tests/__data__/input/playlist_update/cy.m3u b/tests/__data__/input/playlist_update/cy.m3u index 439794ad78..03c605e3eb 100644 --- a/tests/__data__/input/playlist_update/cy.m3u +++ b/tests/__data__/input/playlist_update/cy.m3u @@ -1,7 +1,7 @@ -#EXTM3U -#EXTINF:-1 tvg-id="",RIK HD Cyprus -http://l6.cloudskep.com/rikcy/rikhd/playlist.m3u8 -#EXTINF:-1 tvg-id="",RIK 2 -http://l6.cloudskep.com/rikcy/rik2/playlist.m3u8 -#EXTINF:-1 tvg-id="AdaTV.cy",AdaTV -https://ythls.onrender.com/channel/UC40TUSUx490U5uR1lZt3Ajg.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="",RIK HD Cyprus +http://l6.cloudskep.com/rikcy/rikhd/playlist.m3u8 +#EXTINF:-1 tvg-id="",RIK 2 +http://l6.cloudskep.com/rikcy/rik2/playlist.m3u8 +#EXTINF:-1 tvg-id="AdaTV.cy",AdaTV +https://ythls.onrender.com/channel/UC40TUSUx490U5uR1lZt3Ajg.m3u8 diff --git a/tests/__data__/input/playlist_update/uk.m3u b/tests/__data__/input/playlist_update/uk.m3u index c1deeea978..033f5a36b5 100644 --- a/tests/__data__/input/playlist_update/uk.m3u +++ b/tests/__data__/input/playlist_update/uk.m3u @@ -1,5 +1,5 @@ -#EXTM3U -#EXTINF:-1 tvg-id="BBCNews.uk",BBC News HD (720p) [Not 24/7] -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 +#EXTM3U +#EXTINF:-1 tvg-id="BBCNews.uk",BBC News HD (720p) [Not 24/7] +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 diff --git a/tests/__data__/input/playlist_validate/us_blocked.m3u b/tests/__data__/input/playlist_validate/us_blocked.m3u index 2ffa2273b6..3a5e4bd531 100644 --- a/tests/__data__/input/playlist_validate/us_blocked.m3u +++ b/tests/__data__/input/playlist_validate/us_blocked.m3u @@ -1,7 +1,7 @@ -#EXTM3U -#EXTINF:-1 tvg-id="FoxSports2.us@Asia",Fox Sports 2 Asia (Thai) (720p) -https://example.com/playlist.m3u8 -#EXTINF:-1 tvg-id="TVN.pl",TVN -https://example.com/playlist2.m3u8 -#EXTINF:-1 tvg-id="EverydayHeroes.us",Everyday Heroes (720p) -https://a.jsrdn.com/broadcast/7b1451fa52/+0000/c.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="FoxSports2.us@Asia",Fox Sports 2 Asia (Thai) (720p) +https://example.com/playlist.m3u8 +#EXTINF:-1 tvg-id="TVN.pl",TVN +https://example.com/playlist2.m3u8 +#EXTINF:-1 tvg-id="EverydayHeroes.us",Everyday Heroes (720p) +https://a.jsrdn.com/broadcast/7b1451fa52/+0000/c.m3u8 diff --git a/tests/__data__/input/playlist_validate/wrong_id.m3u b/tests/__data__/input/playlist_validate/wrong_id.m3u index fd98677736..dc7ab8504f 100644 --- a/tests/__data__/input/playlist_validate/wrong_id.m3u +++ b/tests/__data__/input/playlist_validate/wrong_id.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="qib22lAq1L.us",ABC (720p) -https://example.com/playlist2.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="qib22lAq1L.us",ABC (720p) +https://example.com/playlist2.m3u8 diff --git a/tests/__data__/input/report_create/br.m3u b/tests/__data__/input/report_create/br.m3u index 6dcdaee152..31a8698610 100644 --- a/tests/__data__/input/report_create/br.m3u +++ b/tests/__data__/input/report_create/br.m3u @@ -1,6 +1,6 @@ -#EXTM3U -#EXTINF:-1 tvg-id="",VTV [Not 24/7] -https://ythls.onrender.com/channel/UC40TUSUx490U5uR1lZt3Ajg.m3u8 -#EXTINF:-1 tvg-id="",Tele2000 [Not 24/7] -#EXTVLCOPT:http-referrer=https://example2.com/ -https://servilive.com:3126/live/tele2000live.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="",VTV [Not 24/7] +https://ythls.onrender.com/channel/UC40TUSUx490U5uR1lZt3Ajg.m3u8 +#EXTINF:-1 tvg-id="",Tele2000 [Not 24/7] +#EXTVLCOPT:http-referrer=https://example2.com/ +https://servilive.com:3126/live/tele2000live.m3u8 diff --git a/tests/__data__/input/report_create/cy.m3u b/tests/__data__/input/report_create/cy.m3u index 581d0961b6..82d0b78d44 100644 --- a/tests/__data__/input/report_create/cy.m3u +++ b/tests/__data__/input/report_create/cy.m3u @@ -1,5 +1,5 @@ -#EXTM3U -#EXTINF:-1 tvg-id="",RIK HD Cyprus -http://l6.cloudskep.com/rikcy/rikhd/playlist.m3u8 -#EXTINF:-1 tvg-id="",RIK 2 -http://l6.cloudskep.com/rikcy/rik2/playlist.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="",RIK HD Cyprus +http://l6.cloudskep.com/rikcy/rikhd/playlist.m3u8 +#EXTINF:-1 tvg-id="",RIK 2 +http://l6.cloudskep.com/rikcy/rik2/playlist.m3u8 diff --git a/tests/__data__/input/report_create/uk.m3u b/tests/__data__/input/report_create/uk.m3u index c1deeea978..033f5a36b5 100644 --- a/tests/__data__/input/report_create/uk.m3u +++ b/tests/__data__/input/report_create/uk.m3u @@ -1,5 +1,5 @@ -#EXTM3U -#EXTINF:-1 tvg-id="BBCNews.uk",BBC News HD (720p) [Not 24/7] -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 +#EXTM3U +#EXTINF:-1 tvg-id="BBCNews.uk",BBC News HD (720p) [Not 24/7] +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 diff --git a/tests/__data__/input/report_create/us.m3u b/tests/__data__/input/report_create/us.m3u index 92cee65b08..78ea4deb5c 100644 --- a/tests/__data__/input/report_create/us.m3u +++ b/tests/__data__/input/report_create/us.m3u @@ -1,3 +1,3 @@ -#EXTM3U -#EXTINF:-1 tvg-id="",TUTV -https://livestream.telvue.com/templeuni1/f7b44cfafd5c52223d5498196c8a2e7b.sdp/playlist.m3u8 +#EXTM3U +#EXTINF:-1 tvg-id="",TUTV +https://livestream.telvue.com/templeuni1/f7b44cfafd5c52223d5498196c8a2e7b.sdp/playlist.m3u8 From c38ab0f070fb97f7bc7505fca9d2fe9378e7351e Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 23 Apr 2025 05:21:23 +0300 Subject: [PATCH 14/24] Update update.test.ts --- tests/commands/playlist/update.test.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/commands/playlist/update.test.ts b/tests/commands/playlist/update.test.ts index a9949c4994..a430b8df45 100644 --- a/tests/commands/playlist/update.test.ts +++ b/tests/commands/playlist/update.test.ts @@ -21,9 +21,12 @@ describe('playlist:update', () => { const stdout = execSync(cmd, { encoding: 'utf8' }) if (process.env.DEBUG === 'true') console.log(cmd, stdout) - const files = glob - .sync('tests/__data__/expected/playlist_update/*.m3u') - .map(f => f.replace('tests/__data__/expected/playlist_update/', '')) + const files = glob.sync('tests/__data__/expected/playlist_update/*.m3u').map(filepath => { + const fileUrl = pathToFileURL(filepath).toString() + const pathToRemove = pathToFileURL('tests/__data__/expected/playlist_update/').toString() + + return fileUrl.replace(pathToRemove, '') + }) files.forEach(filepath => { expect(content(`tests/__data__/output/streams/${filepath}`), filepath).toBe( From 0deb0c8e015a32fba552d287769728e860187045 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 23 Apr 2025 05:21:33 +0300 Subject: [PATCH 15/24] Update scripts --- scripts/models/playlist.ts | 4 ++-- scripts/models/stream.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/models/playlist.ts b/scripts/models/playlist.ts index ba0a25c5c4..d3022a5af6 100644 --- a/scripts/models/playlist.ts +++ b/scripts/models/playlist.ts @@ -17,10 +17,10 @@ export class Playlist { } toString() { - let output = '#EXTM3U\n' + let output = '#EXTM3U\r\n' this.streams.forEach((stream: Stream) => { - output += stream.toString(this.options) + '\n' + output += stream.toString(this.options) + '\r\n' }) return output diff --git a/scripts/models/stream.ts b/scripts/models/stream.ts index 2de399efc7..443a249b9c 100644 --- a/scripts/models/stream.ts +++ b/scripts/models/stream.ts @@ -352,14 +352,14 @@ export class Stream { output += `,${this.getTitle()}` if (this.referrer) { - output += `\n#EXTVLCOPT:http-referrer=${this.referrer}` + output += `\r\n#EXTVLCOPT:http-referrer=${this.referrer}` } if (this.userAgent) { - output += `\n#EXTVLCOPT:http-user-agent=${this.userAgent}` + output += `\r\n#EXTVLCOPT:http-user-agent=${this.userAgent}` } - output += `\n${this.url}` + output += `\r\n${this.url}` return output } From 844771bfb1ca6e29b6be97c970b7d2dcfff9dee1 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 23 Apr 2025 05:42:52 +0300 Subject: [PATCH 16/24] Update tests --- tests/commands/playlist/format.test.ts | 9 ++++++--- tests/commands/playlist/generate.test.ts | 7 ++++++- tests/commands/playlist/test.test.ts | 7 ++++++- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/tests/commands/playlist/format.test.ts b/tests/commands/playlist/format.test.ts index 070bfd7d98..f6c6312528 100644 --- a/tests/commands/playlist/format.test.ts +++ b/tests/commands/playlist/format.test.ts @@ -20,9 +20,12 @@ describe('playlist:format', () => { const stdout = execSync(cmd, { encoding: 'utf8' }) if (process.env.DEBUG === 'true') console.log(cmd, stdout) - const files = glob - .sync('tests/__data__/expected/playlist_format/*.m3u') - .map(f => f.replace('tests/__data__/expected/playlist_format/', '')) + const files = glob.sync('tests/__data__/expected/playlist_format/*.m3u').map(filepath => { + const fileUrl = pathToFileURL(filepath).toString() + const pathToRemove = pathToFileURL('tests/__data__/expected/playlist_format/').toString() + + return fileUrl.replace(pathToRemove, '') + }) files.forEach(filepath => { expect(content(`tests/__data__/output/streams/${filepath}`), filepath).toBe( diff --git a/tests/commands/playlist/generate.test.ts b/tests/commands/playlist/generate.test.ts index 5c99f02696..fa27be5b1f 100644 --- a/tests/commands/playlist/generate.test.ts +++ b/tests/commands/playlist/generate.test.ts @@ -23,7 +23,12 @@ describe('playlist:generate', () => { const playlists = glob .sync('tests/__data__/expected/playlist_generate/.gh-pages/**/*.m3u') - .map((file: string) => file.replace('tests/__data__/expected/playlist_generate/', '')) + .map(filepath => { + const fileUrl = pathToFileURL(filepath).toString() + const pathToRemove = pathToFileURL('tests/__data__/expected/playlist_generate/').toString() + + return fileUrl.replace(pathToRemove, '') + }) playlists.forEach((filepath: string) => { expect(content(`tests/__data__/output/${filepath}`), filepath).toBe( diff --git a/tests/commands/playlist/test.test.ts b/tests/commands/playlist/test.test.ts index 0020106dce..1b42c3d585 100644 --- a/tests/commands/playlist/test.test.ts +++ b/tests/commands/playlist/test.test.ts @@ -1,4 +1,5 @@ import { execSync } from 'child_process' +import path from 'node:path' import os from 'os' type ExecError = { @@ -27,6 +28,10 @@ describe('playlist:test', () => { }) function checkStdout(stdout: string) { - expect(stdout).toContain('playlist_test/ag.m3u') + expect(stdout).toContain(slash('playlist_test/ag.m3u')) expect(stdout).toContain('2 problems (1 errors, 1 warnings)') } + +function slash(filepath: string) { + return filepath.split(path.sep).join(path.posix.sep) +} From e11619c0542915a827f96c418fdb8da3a749894e Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 23 Apr 2025 20:55:48 +0300 Subject: [PATCH 17/24] Update dependencies --- package-lock.json | 865 +++++++++------------------------------------- package.json | 3 +- yarn.lock | 317 ++++------------- 3 files changed, 230 insertions(+), 955 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4df46535d8..7f48efc865 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@eslint/eslintrc": "^3.3.0", "@eslint/js": "^9.21.0", - "@freearhey/core": "^0.7.0", + "@freearhey/core": "^0.8.2", "@freearhey/search-js": "^0.1.2", "@inquirer/prompts": "^7.4.1", "@octokit/core": "^6.1.4", @@ -18,6 +18,7 @@ "@octokit/plugin-rest-endpoint-methods": "^7.1.3", "@octokit/types": "^11.1.0", "@types/cli-progress": "^3.11.3", + "@types/fs-extra": "^11.0.4", "@types/jest": "^29.5.14", "@types/lodash": "^4.14.198", "@types/numeral": "^2.0.3", @@ -1065,23 +1066,20 @@ } }, "node_modules/@freearhey/core": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@freearhey/core/-/core-0.7.0.tgz", - "integrity": "sha512-HXkKPYGY7ife7JAc1q/Qxzy0WUdSnyt3rHThCShZHgnH3rz0tpkjHFW7LNegB3he0IKn/Zc95/YSOQ97Fq8ctA==", + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@freearhey/core/-/core-0.8.2.tgz", + "integrity": "sha512-jlb1XUbhUf3lqD3B9Wmx3c8qYG4+s1I0cr2FFQfiMpJh4nMvfUNdJr2OhH31S/dbNP12ycT6RPVoZ2j2G3+mXA==", "dependencies": { - "@types/fs-extra": "^11.0.2", - "@types/lodash": "^4.14.198", - "@types/luxon": "^3.3.2", - "fs-extra": "^11.1.1", - "glob": "^10.3.4", + "consola": "^3.4.2", + "dayjs": "^1.11.13", + "fs-extra": "^11.3.0", + "glob": "^11.0.1", "lodash": "^4.17.21", - "luxon": "^3.4.3", - "natural-orderby": "^3.0.2", - "node-gzip": "^1.1.2", + "natural-orderby": "^5.0.0", "normalize-url": "^6.1.0", "object-treeify": "^2.1.1", - "run-script-os": "^1.1.6", - "signale": "^1.4.0" + "pako": "^2.1.0", + "timer-node": "^5.0.9" } }, "node_modules/@freearhey/core/node_modules/normalize-url": { @@ -1493,9 +1491,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "engines": { "node": ">=12" }, @@ -2287,15 +2285,6 @@ "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==" }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "optional": true, - "engines": { - "node": ">=14" - } - }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -2379,9 +2368,9 @@ "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" }, "node_modules/@types/fs-extra": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.2.tgz", - "integrity": "sha512-c0hrgAOVYr21EX8J0jBMXGLMgJqVf/v6yxi0dLaJboW9aQPh16Id+z6w2Tx1hm+piJOLv8xPfVKZCLfjPw/IMQ==", + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", + "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", "dependencies": { "@types/jsonfile": "*", "@types/node": "*" @@ -2432,9 +2421,9 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, "node_modules/@types/jsonfile": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.1.tgz", - "integrity": "sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png==", + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", + "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", "dependencies": { "@types/node": "*" } @@ -2444,11 +2433,6 @@ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.198.tgz", "integrity": "sha512-trNJ/vtMZYMLhfN45uLq4ShQSw0/S7xCTLLVM+WM1rmFpba/VS42jVUgaO3w/NOLiWR/09lnYk0yMaA/atdIsg==" }, - "node_modules/@types/luxon": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.2.tgz", - "integrity": "sha512-l5cpE57br4BIjK+9BSkFBOsWtwv6J9bJpC7gdXIzZyI0vuKvNTk0wZZrkQxMGsUAuGW9+WMNWF2IJMD7br2yeQ==" - }, "node_modules/@types/node": { "version": "22.13.10", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", @@ -3221,6 +3205,14 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, "node_modules/console-table-printer": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.12.1.tgz", @@ -3285,6 +3277,11 @@ "node": ">=12.20" } }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -3400,6 +3397,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "peer": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -3407,7 +3405,8 @@ "node_modules/error-ex/node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "peer": true }, "node_modules/esbuild": { "version": "0.25.1", @@ -3790,25 +3789,6 @@ "bser": "2.1.1" } }, - "node_modules/figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/figures/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -3949,9 +3929,9 @@ } }, "node_modules/fs-extra": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", - "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -3962,9 +3942,9 @@ } }, "node_modules/fs-extra/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "engines": { "node": ">= 10.0.0" } @@ -4058,21 +4038,22 @@ } }, "node_modules/glob": { - "version": "10.3.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.4.tgz", - "integrity": "sha512-6LFElP3A+i/Q8XQKEvZjkEWEOTgAIALR9AO2rwT8bgPhDd1anmqDJDZ6lLddI4ehxxxR1S5RIqKe1uapMQfYaQ==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", + "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" }, "bin": { - "glob": "dist/cjs/src/bin.js" + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -4117,14 +4098,14 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -4529,20 +4510,17 @@ } }, "node_modules/jackspeak": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.3.tgz", - "integrity": "sha512-R2bUw+kVZFS/h1AZqBKrSgDmdmjApzgY0AlCPumopFiAlbUxE2gf+SCuBzQ0cP5hHmUmFYF5yw55T97Th5Kstg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz", + "integrity": "sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==", "dependencies": { "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": ">=14" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" } }, "node_modules/jake": { @@ -5197,11 +5175,6 @@ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, - "node_modules/json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -5241,9 +5214,9 @@ } }, "node_modules/jsonfile/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "engines": { "node": ">= 10.0.0" } @@ -5292,40 +5265,6 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "peer": true }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "engines": { - "node": ">=4" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -5364,14 +5303,6 @@ "yallist": "^3.0.2" } }, - "node_modules/luxon": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.3.tgz", - "integrity": "sha512-tFWBiv3h7z+T/tDaoxA8rqTxy1CHV6gHS//QdaH4pulbq/JuBSGgQspQQqcgnwdAx6pNI7cmvz5Sv/addzHmUg==", - "engines": { - "node": ">=12" - } - }, "node_modules/m3u-linter": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/m3u-linter/-/m3u-linter-0.4.2.tgz", @@ -5389,14 +5320,6 @@ "node": ">=10.0.0" } }, - "node_modules/m3u-linter/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/m3u-linter/node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -5405,79 +5328,6 @@ "node": ">= 10" } }, - "node_modules/m3u-linter/node_modules/glob": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", - "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/m3u-linter/node_modules/jackspeak": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", - "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/m3u-linter/node_modules/lru-cache": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", - "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/m3u-linter/node_modules/minimatch": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/m3u-linter/node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -5664,9 +5514,9 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" }, "node_modules/natural-orderby": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/natural-orderby/-/natural-orderby-3.0.2.tgz", - "integrity": "sha512-x7ZdOwBxZCEm9MM7+eQCjkrNLrW3rkBKNHVr78zbtqnMGVNlnDi6C/eUEYgxHNrcbu0ymvjzcwIL/6H1iHri9g==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/natural-orderby/-/natural-orderby-5.0.0.tgz", + "integrity": "sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==", "engines": { "node": ">=18" } @@ -5676,11 +5526,6 @@ "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", "integrity": "sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==" }, - "node_modules/node-gzip": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/node-gzip/-/node-gzip-1.1.2.tgz", - "integrity": "sha512-ZB6zWpfZHGtxZnPMrJSKHVPrRjURoUzaDbLFj3VO70mpLTW5np96vXyHwft4Id0o+PYIzgDkBUjIzaNHhQ8srw==" - }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -5836,6 +5681,11 @@ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5897,26 +5747,26 @@ "peer": true }, "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", - "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", "engines": { - "node": "14 || >=16.14" + "node": "20 || >=22" } }, "node_modules/path-to-regexp": { @@ -5940,14 +5790,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "engines": { - "node": ">=4" - } - }, "node_modules/pirates": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", @@ -5957,79 +5799,6 @@ "node": ">= 6" } }, - "node_modules/pkg-conf": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", - "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", - "dependencies": { - "find-up": "^2.0.0", - "load-json-file": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-conf/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-conf/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-conf/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-conf/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-conf/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "engines": { - "node": ">=4" - } - }, - "node_modules/pkg-conf/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "engines": { - "node": ">=4" - } - }, "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -6335,15 +6104,6 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/run-script-os": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/run-script-os/-/run-script-os-1.1.6.tgz", - "integrity": "sha512-ql6P2LzhBTTDfzKts+Qo4H94VUKpxKDFz6QxxwaUZN0mwvi7L3lpOI7BqPCq7lgDh3XLl0dpeXwfcVIitlrYrw==", - "bin": { - "run-os": "index.js", - "run-script-os": "index.js" - } - }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -6385,83 +6145,6 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "peer": true }, - "node_modules/signale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", - "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", - "dependencies": { - "chalk": "^2.3.2", - "figures": "^2.0.0", - "pkg-conf": "^2.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/signale/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/signale/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/signale/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/signale/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "node_modules/signale/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/signale/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "engines": { - "node": ">=4" - } - }, - "node_modules/signale/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/simple-wcswidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.0.1.tgz", @@ -6723,6 +6406,11 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/timer-node": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/timer-node/-/timer-node-5.0.9.tgz", + "integrity": "sha512-zXxCE/5/YDi0hY9pygqgRqjRbrFRzigYxOudG0I3syaqAAmX9/w9sxex1bNFCN6c1S66RwPtEIJv65dN+1psew==" + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -7741,23 +7429,20 @@ } }, "@freearhey/core": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@freearhey/core/-/core-0.7.0.tgz", - "integrity": "sha512-HXkKPYGY7ife7JAc1q/Qxzy0WUdSnyt3rHThCShZHgnH3rz0tpkjHFW7LNegB3he0IKn/Zc95/YSOQ97Fq8ctA==", + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@freearhey/core/-/core-0.8.2.tgz", + "integrity": "sha512-jlb1XUbhUf3lqD3B9Wmx3c8qYG4+s1I0cr2FFQfiMpJh4nMvfUNdJr2OhH31S/dbNP12ycT6RPVoZ2j2G3+mXA==", "requires": { - "@types/fs-extra": "^11.0.2", - "@types/lodash": "^4.14.198", - "@types/luxon": "^3.3.2", - "fs-extra": "^11.1.1", - "glob": "^10.3.4", + "consola": "^3.4.2", + "dayjs": "^1.11.13", + "fs-extra": "^11.3.0", + "glob": "^11.0.1", "lodash": "^4.17.21", - "luxon": "^3.4.3", - "natural-orderby": "^3.0.2", - "node-gzip": "^1.1.2", + "natural-orderby": "^5.0.0", "normalize-url": "^6.1.0", "object-treeify": "^2.1.1", - "run-script-os": "^1.1.6", - "signale": "^1.4.0" + "pako": "^2.1.0", + "timer-node": "^5.0.9" }, "dependencies": { "normalize-url": { @@ -7982,9 +7667,9 @@ }, "dependencies": { "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==" }, "ansi-styles": { "version": "6.2.1", @@ -8623,12 +8308,6 @@ "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==" }, - "@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "optional": true - }, "@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -8712,9 +8391,9 @@ "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" }, "@types/fs-extra": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.2.tgz", - "integrity": "sha512-c0hrgAOVYr21EX8J0jBMXGLMgJqVf/v6yxi0dLaJboW9aQPh16Id+z6w2Tx1hm+piJOLv8xPfVKZCLfjPw/IMQ==", + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", + "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", "requires": { "@types/jsonfile": "*", "@types/node": "*" @@ -8765,9 +8444,9 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, "@types/jsonfile": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.1.tgz", - "integrity": "sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png==", + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", + "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", "requires": { "@types/node": "*" } @@ -8777,11 +8456,6 @@ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.198.tgz", "integrity": "sha512-trNJ/vtMZYMLhfN45uLq4ShQSw0/S7xCTLLVM+WM1rmFpba/VS42jVUgaO3w/NOLiWR/09lnYk0yMaA/atdIsg==" }, - "@types/luxon": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.2.tgz", - "integrity": "sha512-l5cpE57br4BIjK+9BSkFBOsWtwv6J9bJpC7gdXIzZyI0vuKvNTk0wZZrkQxMGsUAuGW9+WMNWF2IJMD7br2yeQ==" - }, "@types/node": { "version": "22.13.10", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz", @@ -9318,6 +8992,11 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==" + }, "console-table-printer": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.12.1.tgz", @@ -9367,6 +9046,11 @@ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-5.0.3.tgz", "integrity": "sha512-Kvr6HmPXUMerlLcLF+Pwq3K7apHpYmGDVqrxcDasBg86UcKeTSNWbEzU8bwdXnxnR44FtMhJAxI4Bov6Y/KUfA==" }, + "dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" + }, "debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -9443,6 +9127,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "peer": true, "requires": { "is-arrayish": "^0.2.1" }, @@ -9450,7 +9135,8 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "peer": true } } }, @@ -9726,21 +9412,6 @@ "bser": "2.1.1" } }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "requires": { - "escape-string-regexp": "^1.0.5" - }, - "dependencies": { - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - } - } - }, "file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -9838,9 +9509,9 @@ } }, "fs-extra": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", - "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", "requires": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -9848,9 +9519,9 @@ }, "dependencies": { "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==" } } }, @@ -9909,15 +9580,16 @@ } }, "glob": { - "version": "10.3.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.4.tgz", - "integrity": "sha512-6LFElP3A+i/Q8XQKEvZjkEWEOTgAIALR9AO2rwT8bgPhDd1anmqDJDZ6lLddI4ehxxxR1S5RIqKe1uapMQfYaQ==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", + "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", "requires": { "foreground-child": "^3.1.0", - "jackspeak": "^2.0.3", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" }, "dependencies": { "brace-expansion": { @@ -9929,9 +9601,9 @@ } }, "minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", "requires": { "brace-expansion": "^2.0.1" } @@ -10255,12 +9927,11 @@ } }, "jackspeak": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.3.tgz", - "integrity": "sha512-R2bUw+kVZFS/h1AZqBKrSgDmdmjApzgY0AlCPumopFiAlbUxE2gf+SCuBzQ0cP5hHmUmFYF5yw55T97Th5Kstg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz", + "integrity": "sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==", "requires": { - "@isaacs/cliui": "^8.0.2", - "@pkgjs/parseargs": "^0.11.0" + "@isaacs/cliui": "^8.0.2" } }, "jake": { @@ -10761,11 +10432,6 @@ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -10797,9 +10463,9 @@ }, "dependencies": { "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==" } } }, @@ -10838,33 +10504,6 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "peer": true }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" - } - } - }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -10897,11 +10536,6 @@ "yallist": "^3.0.2" } }, - "luxon": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.3.tgz", - "integrity": "sha512-tFWBiv3h7z+T/tDaoxA8rqTxy1CHV6gHS//QdaH4pulbq/JuBSGgQspQQqcgnwdAx6pNI7cmvz5Sv/addzHmUg==" - }, "m3u-linter": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/m3u-linter/-/m3u-linter-0.4.2.tgz", @@ -10913,61 +10547,10 @@ "is-valid-path": "^0.1.1" }, "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "requires": { - "balanced-match": "^1.0.0" - } - }, "commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" - }, - "glob": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", - "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", - "requires": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - } - }, - "jackspeak": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", - "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", - "requires": { - "@isaacs/cliui": "^8.0.2" - } - }, - "lru-cache": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz", - "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==" - }, - "minimatch": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", - "requires": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - } } } }, @@ -11102,20 +10685,15 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" }, "natural-orderby": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/natural-orderby/-/natural-orderby-3.0.2.tgz", - "integrity": "sha512-x7ZdOwBxZCEm9MM7+eQCjkrNLrW3rkBKNHVr78zbtqnMGVNlnDi6C/eUEYgxHNrcbu0ymvjzcwIL/6H1iHri9g==" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/natural-orderby/-/natural-orderby-5.0.0.tgz", + "integrity": "sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==" }, "node-cleanup": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", "integrity": "sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==" }, - "node-gzip": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/node-gzip/-/node-gzip-1.1.2.tgz", - "integrity": "sha512-ZB6zWpfZHGtxZnPMrJSKHVPrRjURoUzaDbLFj3VO70mpLTW5np96vXyHwft4Id0o+PYIzgDkBUjIzaNHhQ8srw==" - }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -11226,6 +10804,11 @@ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" }, + "pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -11269,18 +10852,18 @@ "peer": true }, "path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", "requires": { - "lru-cache": "^9.1.1 || ^10.0.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "dependencies": { "lru-cache": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz", - "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==" + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==" } } }, @@ -11299,71 +10882,12 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" - }, "pirates": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "peer": true }, - "pkg-conf": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", - "integrity": "sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=", - "requires": { - "find-up": "^2.0.0", - "load-json-file": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - } - } - }, "pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -11560,11 +11084,6 @@ "queue-microtask": "^1.2.2" } }, - "run-script-os": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/run-script-os/-/run-script-os-1.1.6.tgz", - "integrity": "sha512-ql6P2LzhBTTDfzKts+Qo4H94VUKpxKDFz6QxxwaUZN0mwvi7L3lpOI7BqPCq7lgDh3XLl0dpeXwfcVIitlrYrw==" - }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -11594,67 +11113,6 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "peer": true }, - "signale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz", - "integrity": "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==", - "requires": { - "chalk": "^2.3.2", - "figures": "^2.0.0", - "pkg-conf": "^2.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, "simple-wcswidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.0.1.tgz", @@ -11846,6 +11304,11 @@ } } }, + "timer-node": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/timer-node/-/timer-node-5.0.9.tgz", + "integrity": "sha512-zXxCE/5/YDi0hY9pygqgRqjRbrFRzigYxOudG0I3syaqAAmX9/w9sxex1bNFCN6c1S66RwPtEIJv65dN+1psew==" + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", diff --git a/package.json b/package.json index 6533852a23..c03d75ccc4 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "dependencies": { "@eslint/eslintrc": "^3.3.0", "@eslint/js": "^9.21.0", - "@freearhey/core": "^0.7.0", + "@freearhey/core": "^0.8.2", "@freearhey/search-js": "^0.1.2", "@inquirer/prompts": "^7.4.1", "@octokit/core": "^6.1.4", @@ -48,6 +48,7 @@ "@octokit/plugin-rest-endpoint-methods": "^7.1.3", "@octokit/types": "^11.1.0", "@types/cli-progress": "^3.11.3", + "@types/fs-extra": "^11.0.4", "@types/jest": "^29.5.14", "@types/lodash": "^4.14.198", "@types/numeral": "^2.0.3", diff --git a/yarn.lock b/yarn.lock index 94e14d2f5d..1491b68a9a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -366,24 +366,21 @@ "@eslint/core" "^0.12.0" levn "^0.4.1" -"@freearhey/core@^0.7.0": - version "0.7.0" - resolved "https://registry.npmjs.org/@freearhey/core/-/core-0.7.0.tgz" - integrity sha512-HXkKPYGY7ife7JAc1q/Qxzy0WUdSnyt3rHThCShZHgnH3rz0tpkjHFW7LNegB3he0IKn/Zc95/YSOQ97Fq8ctA== +"@freearhey/core@^0.8.2": + version "0.8.2" + resolved "https://registry.npmjs.org/@freearhey/core/-/core-0.8.2.tgz" + integrity sha512-jlb1XUbhUf3lqD3B9Wmx3c8qYG4+s1I0cr2FFQfiMpJh4nMvfUNdJr2OhH31S/dbNP12ycT6RPVoZ2j2G3+mXA== dependencies: - "@types/fs-extra" "^11.0.2" - "@types/lodash" "^4.14.198" - "@types/luxon" "^3.3.2" - fs-extra "^11.1.1" - glob "^10.3.4" + consola "^3.4.2" + dayjs "^1.11.13" + fs-extra "^11.3.0" + glob "^11.0.1" lodash "^4.17.21" - luxon "^3.4.3" - natural-orderby "^3.0.2" - node-gzip "^1.1.2" + natural-orderby "^5.0.0" normalize-url "^6.1.0" object-treeify "^2.1.1" - run-script-os "^1.1.6" - signale "^1.4.0" + pako "^2.1.0" + timer-node "^5.0.9" "@freearhey/search-js@^0.1.2": version "0.1.2" @@ -967,11 +964,6 @@ resolved "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz" integrity sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg== -"@pkgjs/parseargs@^0.11.0": - version "0.11.0" - resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz" - integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== - "@sinclair/typebox@^0.27.8": version "0.27.8" resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz" @@ -1041,10 +1033,10 @@ resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz" integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== -"@types/fs-extra@^11.0.2": - version "11.0.2" - resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.2.tgz" - integrity sha512-c0hrgAOVYr21EX8J0jBMXGLMgJqVf/v6yxi0dLaJboW9aQPh16Id+z6w2Tx1hm+piJOLv8xPfVKZCLfjPw/IMQ== +"@types/fs-extra@^11.0.4": + version "11.0.4" + resolved "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz" + integrity sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ== dependencies: "@types/jsonfile" "*" "@types/node" "*" @@ -1089,9 +1081,9 @@ integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/jsonfile@*": - version "6.1.1" - resolved "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.1.tgz" - integrity sha512-GSgiRCVeapDN+3pqA35IkQwasaCh/0YFH5dEF6S88iDvEn901DjOeH3/QPY+XYP1DFzDZPvIvfeEgk+7br5png== + version "6.1.4" + resolved "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz" + integrity sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ== dependencies: "@types/node" "*" @@ -1100,11 +1092,6 @@ resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.198.tgz" integrity sha512-trNJ/vtMZYMLhfN45uLq4ShQSw0/S7xCTLLVM+WM1rmFpba/VS42jVUgaO3w/NOLiWR/09lnYk0yMaA/atdIsg== -"@types/luxon@^3.3.2": - version "3.3.2" - resolved "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.2.tgz" - integrity sha512-l5cpE57br4BIjK+9BSkFBOsWtwv6J9bJpC7gdXIzZyI0vuKvNTk0wZZrkQxMGsUAuGW9+WMNWF2IJMD7br2yeQ== - "@types/node@*", "@types/node@>=18": version "22.13.10" resolved "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz" @@ -1263,16 +1250,9 @@ ansi-regex@^5.0.1: integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-regex@^6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz" - integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" + version "6.1.0" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" @@ -1486,15 +1466,6 @@ caniuse-lite@^1.0.30001688: resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz" integrity sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w== -chalk@^2.3.2: - version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" @@ -1554,13 +1525,6 @@ collect-v8-coverage@^1.0.0: resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz" integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q== -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - color-convert@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" @@ -1573,11 +1537,6 @@ color-name@~1.1.4: resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - colors@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz" @@ -1615,6 +1574,11 @@ concat-map@0.0.1: resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +consola@^3.4.2: + version "3.4.2" + resolved "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz" + integrity sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA== + console-table-printer@^2.12.1: version "2.12.1" resolved "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.12.1.tgz" @@ -1659,6 +1623,11 @@ dateformat@^5.0.3: resolved "https://registry.npmjs.org/dateformat/-/dateformat-5.0.3.tgz" integrity sha512-Kvr6HmPXUMerlLcLF+Pwq3K7apHpYmGDVqrxcDasBg86UcKeTSNWbEzU8bwdXnxnR44FtMhJAxI4Bov6Y/KUfA== +dayjs@^1.11.13: + version "1.11.13" + resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz" + integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== + debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: version "4.4.0" resolved "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz" @@ -1771,11 +1740,6 @@ escalade@^3.1.1, escalade@^3.2.0: resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - escape-string-regexp@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" @@ -1968,13 +1932,6 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" -figures@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz" - integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= - dependencies: - escape-string-regexp "^1.0.5" - file-entry-cache@^8.0.0: version "8.0.0" resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz" @@ -1996,13 +1953,6 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" -find-up@^2.0.0: - version "2.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= - dependencies: - locate-path "^2.0.0" - find-up@^4.0.0: version "4.1.0" resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" @@ -2062,10 +2012,10 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -fs-extra@^11.1.1: - version "11.1.1" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz" - integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== +fs-extra@^11.3.0: + version "11.3.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz" + integrity sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" @@ -2132,21 +2082,10 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@^10.3.4: - version "10.3.4" - resolved "https://registry.npmjs.org/glob/-/glob-10.3.4.tgz" - integrity sha512-6LFElP3A+i/Q8XQKEvZjkEWEOTgAIALR9AO2rwT8bgPhDd1anmqDJDZ6lLddI4ehxxxR1S5RIqKe1uapMQfYaQ== - dependencies: - foreground-child "^3.1.0" - jackspeak "^2.0.3" - minimatch "^9.0.1" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - path-scurry "^1.10.1" - -glob@^11.0.0: - version "11.0.0" - resolved "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz" - integrity sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g== +glob@^11.0.0, glob@^11.0.1: + version "11.0.1" + resolved "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz" + integrity sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw== dependencies: foreground-child "^3.1.0" jackspeak "^4.0.1" @@ -2194,7 +2133,7 @@ globals@^16.0.0: resolved "https://registry.npmjs.org/globals/-/globals-16.0.0.tgz" integrity sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A== -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9: +graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9: version "4.2.9" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz" integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== @@ -2209,11 +2148,6 @@ graphql@^16.8.1: resolved "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz" integrity sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ== -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - has-flag@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" @@ -2484,19 +2418,10 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -jackspeak@^2.0.3: - version "2.3.3" - resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.3.tgz" - integrity sha512-R2bUw+kVZFS/h1AZqBKrSgDmdmjApzgY0AlCPumopFiAlbUxE2gf+SCuBzQ0cP5hHmUmFYF5yw55T97Th5Kstg== - dependencies: - "@isaacs/cliui" "^8.0.2" - optionalDependencies: - "@pkgjs/parseargs" "^0.11.0" - jackspeak@^4.0.1: - version "4.0.2" - resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz" - integrity sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw== + version "4.1.0" + resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz" + integrity sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw== dependencies: "@isaacs/cliui" "^8.0.2" @@ -2908,11 +2833,6 @@ json-buffer@3.0.1: resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== -json-parse-better-errors@^1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - json-parse-even-better-errors@^2.3.0: version "2.3.1" resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" @@ -2972,24 +2892,6 @@ lines-and-columns@^1.1.6: resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== -load-json-file@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz" - integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= - dependencies: - graceful-fs "^4.1.2" - parse-json "^4.0.0" - pify "^3.0.0" - strip-bom "^3.0.0" - -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - locate-path@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" @@ -3020,9 +2922,9 @@ lodash@^4.17.21: integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== lru-cache@^11.0.0: - version "11.0.2" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz" - integrity sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA== + version "11.1.0" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz" + integrity sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A== lru-cache@^5.1.1: version "5.1.1" @@ -3031,16 +2933,6 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -"lru-cache@^9.1.1 || ^10.0.0": - version "10.0.1" - resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz" - integrity sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g== - -luxon@^3.4.3: - version "3.4.3" - resolved "https://registry.npmjs.org/luxon/-/luxon-3.4.3.tgz" - integrity sha512-tFWBiv3h7z+T/tDaoxA8rqTxy1CHV6gHS//QdaH4pulbq/JuBSGgQspQQqcgnwdAx6pNI7cmvz5Sv/addzHmUg== - m3u-linter@^0.4.2: version "0.4.2" resolved "https://registry.npmjs.org/m3u-linter/-/m3u-linter-0.4.2.tgz" @@ -3133,13 +3025,6 @@ minimatch@^5.0.1: dependencies: brace-expansion "^2.0.1" -minimatch@^9.0.1: - version "9.0.3" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz" - integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== - dependencies: - brace-expansion "^2.0.1" - minimatch@^9.0.4: version "9.0.5" resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz" @@ -3147,7 +3032,7 @@ minimatch@^9.0.4: dependencies: brace-expansion "^2.0.1" -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: +minipass@^7.1.2: version "7.1.2" resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== @@ -3191,21 +3076,16 @@ natural-compare@^1.4.0: resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -natural-orderby@^3.0.2: - version "3.0.2" - resolved "https://registry.npmjs.org/natural-orderby/-/natural-orderby-3.0.2.tgz" - integrity sha512-x7ZdOwBxZCEm9MM7+eQCjkrNLrW3rkBKNHVr78zbtqnMGVNlnDi6C/eUEYgxHNrcbu0ymvjzcwIL/6H1iHri9g== +natural-orderby@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/natural-orderby/-/natural-orderby-5.0.0.tgz" + integrity sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg== node-cleanup@^2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz" integrity sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw== -node-gzip@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/node-gzip/-/node-gzip-1.1.2.tgz" - integrity sha512-ZB6zWpfZHGtxZnPMrJSKHVPrRjURoUzaDbLFj3VO70mpLTW5np96vXyHwft4Id0o+PYIzgDkBUjIzaNHhQ8srw== - node-int64@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" @@ -3284,13 +3164,6 @@ outvariant@^1.4.0, outvariant@^1.4.3: resolved "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz" integrity sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA== -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - p-limit@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" @@ -3305,13 +3178,6 @@ p-limit@^3.0.2, p-limit@^3.1.0: dependencies: yocto-queue "^0.1.0" -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= - dependencies: - p-limit "^1.1.0" - p-locate@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" @@ -3326,11 +3192,6 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= - p-try@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" @@ -3341,6 +3202,11 @@ package-json-from-dist@^1.0.0: resolved "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz" integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== +pako@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz" + integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" @@ -3348,14 +3214,6 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - parse-json@^5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" @@ -3366,11 +3224,6 @@ parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= - path-exists@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" @@ -3391,14 +3244,6 @@ path-parse@^1.0.7: resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-scurry@^1.10.1: - version "1.10.1" - resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz" - integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== - dependencies: - lru-cache "^9.1.1 || ^10.0.0" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - path-scurry@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz" @@ -3422,24 +3267,11 @@ picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1: resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= - pirates@^4.0.4: version "4.0.6" resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz" integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== -pkg-conf@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz" - integrity sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg= - dependencies: - find-up "^2.0.0" - load-json-file "^4.0.0" - pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" @@ -3574,11 +3406,6 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -run-script-os@^1.1.6: - version "1.1.6" - resolved "https://registry.npmjs.org/run-script-os/-/run-script-os-1.1.6.tgz" - integrity sha512-ql6P2LzhBTTDfzKts+Qo4H94VUKpxKDFz6QxxwaUZN0mwvi7L3lpOI7BqPCq7lgDh3XLl0dpeXwfcVIitlrYrw== - "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" @@ -3626,15 +3453,6 @@ signal-exit@^4.1.0: resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== -signale@^1.4.0: - version "1.4.0" - resolved "https://registry.npmjs.org/signale/-/signale-1.4.0.tgz" - integrity sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w== - dependencies: - chalk "^2.3.2" - figures "^2.0.0" - pkg-conf "^2.1.0" - simple-wcswidth@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.0.1.tgz" @@ -3768,11 +3586,6 @@ strip-ansi@^7.0.1: dependencies: ansi-regex "^6.0.1" -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= - strip-bom@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz" @@ -3788,13 +3601,6 @@ strip-json-comments@^3.1.1: resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - supports-color@^7.1.0: version "7.2.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" @@ -3823,6 +3629,11 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +timer-node@^5.0.9: + version "5.0.9" + resolved "https://registry.npmjs.org/timer-node/-/timer-node-5.0.9.tgz" + integrity sha512-zXxCE/5/YDi0hY9pygqgRqjRbrFRzigYxOudG0I3syaqAAmX9/w9sxex1bNFCN6c1S66RwPtEIJv65dN+1psew== + tmp@^0.0.33: version "0.0.33" resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz" @@ -3925,9 +3736,9 @@ universalify@^0.2.0: integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== universalify@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" - integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + version "2.0.1" + resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== update-browserslist-db@^1.1.1: version "1.1.1" From e82155e38ede595017dd12c8ddfc8b3cd0e4ba8e Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 23 Apr 2025 20:56:02 +0300 Subject: [PATCH 18/24] Update tests/__data__ --- .../input/readme_update/generators.log | 148 +++++++++--------- 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/tests/__data__/input/readme_update/generators.log b/tests/__data__/input/readme_update/generators.log index 63cfea0585..55adb62b06 100644 --- a/tests/__data__/input/readme_update/generators.log +++ b/tests/__data__/input/readme_update/generators.log @@ -1,75 +1,75 @@ -{"type":"category","filepath":"categories/auto.m3u","count":0} -{"type":"category","filepath":"categories/animation.m3u","count":0} -{"type":"category","filepath":"categories/business.m3u","count":0} -{"type":"category","filepath":"categories/classic.m3u","count":0} -{"type":"category","filepath":"categories/comedy.m3u","count":0} -{"type":"category","filepath":"categories/cooking.m3u","count":0} -{"type":"category","filepath":"categories/culture.m3u","count":0} -{"type":"category","filepath":"categories/documentary.m3u","count":0} -{"type":"category","filepath":"categories/education.m3u","count":0} -{"type":"category","filepath":"categories/entertainment.m3u","count":0} -{"type":"category","filepath":"categories/family.m3u","count":0} -{"type":"category","filepath":"categories/general.m3u","count":2} -{"type":"category","filepath":"categories/kids.m3u","count":0} -{"type":"category","filepath":"categories/legislative.m3u","count":0} -{"type":"category","filepath":"categories/lifestyle.m3u","count":0} -{"type":"category","filepath":"categories/movies.m3u","count":0} -{"type":"category","filepath":"categories/music.m3u","count":0} -{"type":"category","filepath":"categories/news.m3u","count":1} -{"type":"category","filepath":"categories/outdoor.m3u","count":0} -{"type":"category","filepath":"categories/relax.m3u","count":0} -{"type":"category","filepath":"categories/religious.m3u","count":0} -{"type":"category","filepath":"categories/series.m3u","count":0} -{"type":"category","filepath":"categories/science.m3u","count":0} -{"type":"category","filepath":"categories/shop.m3u","count":0} -{"type":"category","filepath":"categories/sports.m3u","count":0} -{"type":"category","filepath":"categories/travel.m3u","count":0} -{"type":"category","filepath":"categories/weather.m3u","count":1} -{"type":"category","filepath":"categories/xxx.m3u","count":1} -{"type":"category","filepath":"categories/undefined.m3u","count":3} -{"type":"country","filepath":"countries/cm.m3u","count":1} -{"type":"country","filepath":"countries/ca.m3u","count":2} -{"type":"country","filepath":"countries/cv.m3u","count":1} -{"type":"country","filepath":"countries/cg.m3u","count":1} -{"type":"country","filepath":"countries/ro.m3u","count":1} -{"type":"subdivision","filepath":"subdivisions/ca-on.m3u","count":1} -{"type":"country","filepath":"countries/ru.m3u","count":2} -{"type":"country","filepath":"countries/rw.m3u","count":1} -{"type":"country","filepath":"countries/re.m3u","count":1} -{"type":"country","filepath":"countries/undefined.m3u","count":2} -{"type":"country","filepath":"countries/bl.m3u","count":1} -{"type":"country","filepath":"countries/sh.m3u","count":1} -{"type":"country","filepath":"countries/kn.m3u","count":1} -{"type":"language","filepath":"languages/cat.m3u","count":1} -{"type":"language","filepath":"languages/eng.m3u","count":1} -{"type":"language","filepath":"languages/fra.m3u","count":1} -{"type":"language","filepath":"languages/rus.m3u","count":1} -{"type":"language","filepath":"languages/undefined.m3u","count":2} -{"type":"region","filepath":"regions/afr.m3u","count":0} -{"type":"region","filepath":"regions/amer.m3u","count":1} -{"type":"region","filepath":"regions/apac.m3u","count":1} -{"type":"region","filepath":"regions/arab.m3u","count":0} -{"type":"region","filepath":"regions/asean.m3u","count":0} -{"type":"region","filepath":"regions/asia.m3u","count":2} -{"type":"region","filepath":"regions/carib.m3u","count":0} -{"type":"region","filepath":"regions/cas.m3u","count":0} -{"type":"region","filepath":"regions/cenamer.m3u","count":0} -{"type":"region","filepath":"regions/cis.m3u","count":1} -{"type":"region","filepath":"regions/emea.m3u","count":3} -{"type":"region","filepath":"regions/eur.m3u","count":3} -{"type":"region","filepath":"regions/hispam.m3u","count":0} -{"type":"region","filepath":"regions/lac.m3u","count":0} -{"type":"region","filepath":"regions/latam.m3u","count":0} -{"type":"region","filepath":"regions/maghreb.m3u","count":0} -{"type":"region","filepath":"regions/mena.m3u","count":0} -{"type":"region","filepath":"regions/mideast.m3u","count":0} -{"type":"region","filepath":"regions/nam.m3u","count":1} -{"type":"region","filepath":"regions/noram.m3u","count":1} -{"type":"region","filepath":"regions/nord.m3u","count":0} -{"type":"region","filepath":"regions/oce.m3u","count":0} -{"type":"region","filepath":"regions/undefined.m3u","count":2} -{"type":"region","filepath":"regions/sas.m3u","count":1} -{"type":"region","filepath":"regions/int.m3u","count":1} -{"type":"region","filepath":"regions/southam.m3u","count":0} -{"type":"region","filepath":"regions/ssa.m3u","count":0} +{"type":"category","filepath":"categories/auto.m3u","count":0} +{"type":"category","filepath":"categories/animation.m3u","count":0} +{"type":"category","filepath":"categories/business.m3u","count":0} +{"type":"category","filepath":"categories/classic.m3u","count":0} +{"type":"category","filepath":"categories/comedy.m3u","count":0} +{"type":"category","filepath":"categories/cooking.m3u","count":0} +{"type":"category","filepath":"categories/culture.m3u","count":0} +{"type":"category","filepath":"categories/documentary.m3u","count":0} +{"type":"category","filepath":"categories/education.m3u","count":0} +{"type":"category","filepath":"categories/entertainment.m3u","count":0} +{"type":"category","filepath":"categories/family.m3u","count":0} +{"type":"category","filepath":"categories/general.m3u","count":2} +{"type":"category","filepath":"categories/kids.m3u","count":0} +{"type":"category","filepath":"categories/legislative.m3u","count":0} +{"type":"category","filepath":"categories/lifestyle.m3u","count":0} +{"type":"category","filepath":"categories/movies.m3u","count":0} +{"type":"category","filepath":"categories/music.m3u","count":0} +{"type":"category","filepath":"categories/news.m3u","count":1} +{"type":"category","filepath":"categories/outdoor.m3u","count":0} +{"type":"category","filepath":"categories/relax.m3u","count":0} +{"type":"category","filepath":"categories/religious.m3u","count":0} +{"type":"category","filepath":"categories/series.m3u","count":0} +{"type":"category","filepath":"categories/science.m3u","count":0} +{"type":"category","filepath":"categories/shop.m3u","count":0} +{"type":"category","filepath":"categories/sports.m3u","count":0} +{"type":"category","filepath":"categories/travel.m3u","count":0} +{"type":"category","filepath":"categories/weather.m3u","count":1} +{"type":"category","filepath":"categories/xxx.m3u","count":1} +{"type":"category","filepath":"categories/undefined.m3u","count":3} +{"type":"country","filepath":"countries/cm.m3u","count":1} +{"type":"country","filepath":"countries/ca.m3u","count":2} +{"type":"country","filepath":"countries/cv.m3u","count":1} +{"type":"country","filepath":"countries/cg.m3u","count":1} +{"type":"country","filepath":"countries/ro.m3u","count":1} +{"type":"subdivision","filepath":"subdivisions/ca-on.m3u","count":1} +{"type":"country","filepath":"countries/ru.m3u","count":2} +{"type":"country","filepath":"countries/rw.m3u","count":1} +{"type":"country","filepath":"countries/re.m3u","count":1} +{"type":"country","filepath":"countries/undefined.m3u","count":2} +{"type":"country","filepath":"countries/bl.m3u","count":1} +{"type":"country","filepath":"countries/sh.m3u","count":1} +{"type":"country","filepath":"countries/kn.m3u","count":1} +{"type":"language","filepath":"languages/cat.m3u","count":1} +{"type":"language","filepath":"languages/eng.m3u","count":1} +{"type":"language","filepath":"languages/fra.m3u","count":1} +{"type":"language","filepath":"languages/rus.m3u","count":1} +{"type":"language","filepath":"languages/undefined.m3u","count":2} +{"type":"region","filepath":"regions/afr.m3u","count":0} +{"type":"region","filepath":"regions/amer.m3u","count":1} +{"type":"region","filepath":"regions/apac.m3u","count":1} +{"type":"region","filepath":"regions/arab.m3u","count":0} +{"type":"region","filepath":"regions/asean.m3u","count":0} +{"type":"region","filepath":"regions/asia.m3u","count":2} +{"type":"region","filepath":"regions/carib.m3u","count":0} +{"type":"region","filepath":"regions/cas.m3u","count":0} +{"type":"region","filepath":"regions/cenamer.m3u","count":0} +{"type":"region","filepath":"regions/cis.m3u","count":1} +{"type":"region","filepath":"regions/emea.m3u","count":3} +{"type":"region","filepath":"regions/eur.m3u","count":3} +{"type":"region","filepath":"regions/hispam.m3u","count":0} +{"type":"region","filepath":"regions/lac.m3u","count":0} +{"type":"region","filepath":"regions/latam.m3u","count":0} +{"type":"region","filepath":"regions/maghreb.m3u","count":0} +{"type":"region","filepath":"regions/mena.m3u","count":0} +{"type":"region","filepath":"regions/mideast.m3u","count":0} +{"type":"region","filepath":"regions/nam.m3u","count":1} +{"type":"region","filepath":"regions/noram.m3u","count":1} +{"type":"region","filepath":"regions/nord.m3u","count":0} +{"type":"region","filepath":"regions/oce.m3u","count":0} +{"type":"region","filepath":"regions/undefined.m3u","count":2} +{"type":"region","filepath":"regions/sas.m3u","count":1} +{"type":"region","filepath":"regions/int.m3u","count":1} +{"type":"region","filepath":"regions/southam.m3u","count":0} +{"type":"region","filepath":"regions/ssa.m3u","count":0} {"type":"region","filepath":"regions/wafr.m3u","count":0} \ No newline at end of file From 7f78b5770ecbd73480ce16a239650a5bb8388ddf Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 23 Apr 2025 20:56:11 +0300 Subject: [PATCH 19/24] Update tests --- tests/commands/playlist/edit.test.ts | 1 - tests/commands/playlist/generate.test.ts | 6 ++-- tests/commands/playlist/test.test.ts | 15 +++------- tests/commands/playlist/validate.test.ts | 36 +++++++++++------------- 4 files changed, 24 insertions(+), 34 deletions(-) diff --git a/tests/commands/playlist/edit.test.ts b/tests/commands/playlist/edit.test.ts index 4651e5ef88..3334b6dee5 100644 --- a/tests/commands/playlist/edit.test.ts +++ b/tests/commands/playlist/edit.test.ts @@ -39,5 +39,4 @@ function checkStdout(stdout: string) { expect(stdout).toContain('TF1.fr (TF1, Télévision française 1)') expect(stdout).toContain('Type...') expect(stdout).toContain('Skip') - expect(stdout).toContain("File 'tests/__data__/output/playlist.m3u' successfully saved") } diff --git a/tests/commands/playlist/generate.test.ts b/tests/commands/playlist/generate.test.ts index fa27be5b1f..4e14d0d30b 100644 --- a/tests/commands/playlist/generate.test.ts +++ b/tests/commands/playlist/generate.test.ts @@ -1,8 +1,8 @@ import { pathToFileURL } from 'node:url' import { execSync } from 'child_process' +import os, { EOL } from 'node:os' import * as fs from 'fs-extra' import * as glob from 'glob' -import os from 'os' let ENV_VAR = 'STREAMS_DIR=tests/__data__/input/playlist_generate DATA_DIR=tests/__data__/input/data PUBLIC_DIR=tests/__data__/output/.gh-pages LOGS_DIR=tests/__data__/output/logs' @@ -36,8 +36,8 @@ describe('playlist:generate', () => { ) }) - expect(content('tests/__data__/output/logs/generators.log').split('\n').sort()).toStrictEqual( - content('tests/__data__/expected/playlist_generate/logs/generators.log').split('\n').sort() + expect(content('tests/__data__/output/logs/generators.log').split(EOL).sort()).toStrictEqual( + content('tests/__data__/expected/playlist_generate/logs/generators.log').split(EOL).sort() ) }) }) diff --git a/tests/commands/playlist/test.test.ts b/tests/commands/playlist/test.test.ts index 1b42c3d585..515dbf824c 100644 --- a/tests/commands/playlist/test.test.ts +++ b/tests/commands/playlist/test.test.ts @@ -1,6 +1,6 @@ import { execSync } from 'child_process' import path from 'node:path' -import os from 'os' +import os from 'node:os' type ExecError = { status: number @@ -16,22 +16,15 @@ describe('playlist:test', () => { it('shows an error if the playlist contains a broken link', () => { const cmd = `${ENV_VAR} npm run playlist:test playlist_test/ag.m3u` try { - const stdout = execSync(cmd, { encoding: 'utf8' }) - if (process.env.DEBUG === 'true') console.log(cmd, stdout) - checkStdout(stdout) + execSync(cmd, { encoding: 'utf8' }) } catch (error) { - // NOTE: for Windows only if (process.env.DEBUG === 'true') console.log(cmd, error) - checkStdout((error as ExecError).stdout) + expect((error as ExecError).stdout).toContain(slash('playlist_test/ag.m3u')) + expect((error as ExecError).stdout).toContain('2 problems (1 errors, 1 warnings)') } }) }) -function checkStdout(stdout: string) { - expect(stdout).toContain(slash('playlist_test/ag.m3u')) - expect(stdout).toContain('2 problems (1 errors, 1 warnings)') -} - function slash(filepath: string) { return filepath.split(path.sep).join(path.posix.sep) } diff --git a/tests/commands/playlist/validate.test.ts b/tests/commands/playlist/validate.test.ts index 933099bc9c..feef97bbfa 100644 --- a/tests/commands/playlist/validate.test.ts +++ b/tests/commands/playlist/validate.test.ts @@ -17,31 +17,29 @@ describe('playlist:validate', () => { it('show an error if channel id in the blocklist', () => { const cmd = `${ENV_VAR} npm run playlist:validate -- us_blocked.m3u` try { - const stdout = execSync(cmd, { encoding: 'utf8' }) - if (process.env.DEBUG === 'true') console.log(cmd, stdout) - checkStdout(stdout) + execSync(cmd, { encoding: 'utf8' }) } catch (error) { - // NOTE: for Windows only if (process.env.DEBUG === 'true') console.log(cmd, error) - checkStdout((error as ExecError).stdout) + expect((error as ExecError).stdout).toContain('us_blocked.m3u') + expect((error as ExecError).stdout).toContain( + '2 error "FoxSports2.us" is on the blocklist due to claims of copyright holders (https://github.com/iptv-org/iptv/issues/0002)' + ) + expect((error as ExecError).stdout).toContain( + '4 error "TVN.pl" is on the blocklist due to NSFW content (https://github.com/iptv-org/iptv/issues/0003)' + ) + expect((error as ExecError).stdout).toContain('2 problems (2 errors, 0 warnings)') } }) it('show a warning if channel has wrong id', () => { const cmd = `${ENV_VAR} npm run playlist:validate -- wrong_id.m3u` - const stdout = execSync(cmd, { encoding: 'utf8' }) - if (process.env.DEBUG === 'true') console.log(cmd, stdout) - - expect(stdout).toContain( - 'wrong_id.m3u\n 2 warning "qib22lAq1L.us" is not in the database\n\n1 problems (0 errors, 1 warnings)\n' - ) + try { + execSync(cmd, { encoding: 'utf8' }) + } catch (error) { + if (process.env.DEBUG === 'true') console.log(cmd, error) + expect((error as ExecError).stdout).toContain( + 'wrong_id.m3u\n 2 warning "qib22lAq1L.us" is not in the database\n\n1 problems (0 errors, 1 warnings)\n' + ) + } }) }) - -function checkStdout(stdout: string) { - expect(stdout).toContain(`us_blocked.m3u - 2 error "FoxSports2.us" is on the blocklist due to claims of copyright holders (https://github.com/iptv-org/iptv/issues/0002) - 4 error "TVN.pl" is on the blocklist due to NSFW content (https://github.com/iptv-org/iptv/issues/0003) - -2 problems (2 errors, 0 warnings)`) -} From e6354bdd2390cb4e1102419dd3f1c90b99c07091 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 23 Apr 2025 20:56:19 +0300 Subject: [PATCH 20/24] Update scripts --- scripts/commands/playlist/edit.ts | 2 +- scripts/commands/playlist/generate.ts | 54 ++++++++++---------- scripts/commands/playlist/test.ts | 2 +- scripts/commands/playlist/validate.ts | 6 +-- scripts/generators/categoriesGenerator.ts | 21 ++++---- scripts/generators/countriesGenerator.ts | 25 ++++----- scripts/generators/indexCategoryGenerator.ts | 17 +++--- scripts/generators/indexCountryGenerator.ts | 17 +++--- scripts/generators/indexGenerator.ts | 17 +++--- scripts/generators/indexLanguageGenerator.ts | 17 +++--- scripts/generators/indexNsfwGenerator.ts | 17 +++--- scripts/generators/indexRegionGenerator.ts | 17 +++--- scripts/generators/languagesGenerator.ts | 21 ++++---- scripts/generators/regionsGenerator.ts | 25 +++++---- scripts/models/feed.ts | 4 +- 15 files changed, 144 insertions(+), 118 deletions(-) diff --git a/scripts/commands/playlist/edit.ts b/scripts/commands/playlist/edit.ts index c57a62d1e1..d87590b1f2 100644 --- a/scripts/commands/playlist/edit.ts +++ b/scripts/commands/playlist/edit.ts @@ -1,7 +1,7 @@ import { Storage, Collection, Logger, Dictionary } from '@freearhey/core' import { DataLoader, DataProcessor, PlaylistParser } from '../../core' -import { Channel, Feed, Playlist, Stream } from '../../models' import type { ChannelSearchableData } from '../../types/channel' +import { Channel, Feed, Playlist, Stream } from '../../models' import { DataProcessorData } from '../../types/dataProcessor' import { DataLoaderData } from '../../types/dataLoader' import { select, input } from '@inquirer/prompts' diff --git a/scripts/commands/playlist/generate.ts b/scripts/commands/playlist/generate.ts index c323d5395c..b903b5a435 100644 --- a/scripts/commands/playlist/generate.ts +++ b/scripts/commands/playlist/generate.ts @@ -1,27 +1,25 @@ -import { Logger, Storage } from '@freearhey/core' import { PlaylistParser, DataProcessor, DataLoader } from '../../core' +import type { DataProcessorData } from '../../types/dataProcessor' +import { DATA_DIR, LOGS_DIR, STREAMS_DIR } from '../../constants' +import type { DataLoaderData } from '../../types/dataLoader' +import { Logger, Storage, File } from '@freearhey/core' import { Stream } from '../../models' import { uniqueId } from 'lodash' import { + IndexCategoryGenerator, + IndexLanguageGenerator, + IndexCountryGenerator, + IndexRegionGenerator, CategoriesGenerator, CountriesGenerator, LanguagesGenerator, RegionsGenerator, - IndexGenerator, - IndexCategoryGenerator, - IndexCountryGenerator, - IndexLanguageGenerator, - IndexRegionGenerator + IndexGenerator } from '../../generators' -import { DATA_DIR, LOGS_DIR, STREAMS_DIR } from '../../constants' -import type { DataProcessorData } from '../../types/dataProcessor' -import type { DataLoaderData } from '../../types/dataLoader' async function main() { const logger = new Logger() - const generatorsLogger = new Logger({ - stream: await new Storage(LOGS_DIR).createStream(`generators.log`) - }) + const logFile = new File('generators.log') logger.info('loading data from api...') const processor = new DataProcessor() @@ -29,19 +27,19 @@ async function main() { const loader = new DataLoader({ storage: dataStorage }) const data: DataLoaderData = await loader.load() const { + feedsGroupedByChannelId, + channelsKeyById, categories, countries, - regions, - channelsKeyById, - feedsGroupedByChannelId + regions }: DataProcessorData = processor.process(data) logger.info('loading streams...') const streamsStorage = new Storage(STREAMS_DIR) const parser = new PlaylistParser({ storage: streamsStorage, - channelsKeyById, - feedsGroupedByChannelId + feedsGroupedByChannelId, + channelsKeyById }) const files = await streamsStorage.list('**/*.m3u') let streams = await parser.parse(files) @@ -62,42 +60,46 @@ async function main() { ) logger.info('generating categories/...') - await new CategoriesGenerator({ categories, streams, logger: generatorsLogger }).generate() + await new CategoriesGenerator({ categories, streams, logFile }).generate() logger.info('generating countries/...') await new CountriesGenerator({ countries, streams, - logger: generatorsLogger + logFile }).generate() logger.info('generating languages/...') - await new LanguagesGenerator({ streams, logger: generatorsLogger }).generate() + await new LanguagesGenerator({ streams, logFile }).generate() logger.info('generating regions/...') await new RegionsGenerator({ streams, regions, - logger: generatorsLogger + logFile }).generate() logger.info('generating index.m3u...') - await new IndexGenerator({ streams, logger: generatorsLogger }).generate() + await new IndexGenerator({ streams, logFile }).generate() logger.info('generating index.category.m3u...') - await new IndexCategoryGenerator({ streams, logger: generatorsLogger }).generate() + await new IndexCategoryGenerator({ streams, logFile }).generate() logger.info('generating index.country.m3u...') await new IndexCountryGenerator({ streams, - logger: generatorsLogger + logFile }).generate() logger.info('generating index.language.m3u...') - await new IndexLanguageGenerator({ streams, logger: generatorsLogger }).generate() + await new IndexLanguageGenerator({ streams, logFile }).generate() logger.info('generating index.region.m3u...') - await new IndexRegionGenerator({ streams, regions, logger: generatorsLogger }).generate() + await new IndexRegionGenerator({ streams, regions, logFile }).generate() + + logger.info('saving generators.log...') + const logStorage = new Storage(LOGS_DIR) + logStorage.saveFile(logFile) } main() diff --git a/scripts/commands/playlist/test.ts b/scripts/commands/playlist/test.ts index cc6f1dc148..a37b7ecd02 100644 --- a/scripts/commands/playlist/test.ts +++ b/scripts/commands/playlist/test.ts @@ -159,7 +159,7 @@ function onFinish() { drawTable() - logger.error(`\n${errors + warnings} problems (${errors} errors, ${warnings} warnings)`) + console.log(chalk.red(`\n${errors + warnings} problems (${errors} errors, ${warnings} warnings)`)) if (errors > 0) { process.exit(1) diff --git a/scripts/commands/playlist/validate.ts b/scripts/commands/playlist/validate.ts index 57bca4f6fb..e089f7eff8 100644 --- a/scripts/commands/playlist/validate.ts +++ b/scripts/commands/playlist/validate.ts @@ -94,14 +94,14 @@ async function main() { }) if (log.notEmpty()) { - logger.info(`\n${chalk.underline(filepath)}`) + console.log(`\n${chalk.underline(filepath)}`) log.forEach((logItem: LogItem) => { const position = logItem.line.toString().padEnd(6, ' ') const type = logItem.type.padEnd(9, ' ') const status = logItem.type === 'error' ? chalk.red(type) : chalk.yellow(type) - logger.info(` ${chalk.gray(position)}${status}${logItem.message}`) + console.log(` ${chalk.gray(position)}${status}${logItem.message}`) }) errors = errors.concat(log.filter((logItem: LogItem) => logItem.type === 'error')) @@ -109,7 +109,7 @@ async function main() { } } - logger.error( + console.log( chalk.red( `\n${ errors.count() + warnings.count() diff --git a/scripts/generators/categoriesGenerator.ts b/scripts/generators/categoriesGenerator.ts index cd20b6ea4f..f6d455d4be 100644 --- a/scripts/generators/categoriesGenerator.ts +++ b/scripts/generators/categoriesGenerator.ts @@ -1,25 +1,26 @@ -import { Generator } from './generator' -import { Collection, Storage, Logger } from '@freearhey/core' +import { Collection, Storage, Logger, File } from '@freearhey/core' import { Stream, Category, Playlist } from '../models' import { PUBLIC_DIR } from '../constants' +import { Generator } from './generator' +import { EOL } from 'node:os' type CategoriesGeneratorProps = { streams: Collection categories: Collection - logger: Logger + logFile: File } export class CategoriesGenerator implements Generator { streams: Collection categories: Collection storage: Storage - logger: Logger + logFile: File - constructor({ streams, categories, logger }: CategoriesGeneratorProps) { + constructor({ streams, categories, logFile }: CategoriesGeneratorProps) { this.streams = streams this.categories = categories this.storage = new Storage(PUBLIC_DIR) - this.logger = logger + this.logFile = logFile } async generate() { @@ -37,8 +38,8 @@ export class CategoriesGenerator implements Generator { const playlist = new Playlist(categoryStreams, { public: true }) const filepath = `categories/${category.id}.m3u` await this.storage.save(filepath, playlist.toString()) - this.logger.info( - JSON.stringify({ type: 'category', filepath, count: playlist.streams.count() }) + this.logFile.append( + JSON.stringify({ type: 'category', filepath, count: playlist.streams.count() }) + EOL ) }) @@ -46,8 +47,8 @@ export class CategoriesGenerator implements Generator { const playlist = new Playlist(undefinedStreams, { public: true }) const filepath = 'categories/undefined.m3u' await this.storage.save(filepath, playlist.toString()) - this.logger.info( - JSON.stringify({ type: 'category', filepath, count: playlist.streams.count() }) + this.logFile.append( + JSON.stringify({ type: 'category', filepath, count: playlist.streams.count() }) + EOL ) } } diff --git a/scripts/generators/countriesGenerator.ts b/scripts/generators/countriesGenerator.ts index c935da5a4a..7dc707cf91 100644 --- a/scripts/generators/countriesGenerator.ts +++ b/scripts/generators/countriesGenerator.ts @@ -1,25 +1,26 @@ -import { Generator } from './generator' -import { Collection, Storage, Logger } from '@freearhey/core' import { Country, Subdivision, Stream, Playlist } from '../models' +import { Collection, Storage, File } from '@freearhey/core' import { PUBLIC_DIR } from '../constants' +import { Generator } from './generator' +import { EOL } from 'node:os' type CountriesGeneratorProps = { streams: Collection countries: Collection - logger: Logger + logFile: File } export class CountriesGenerator implements Generator { streams: Collection countries: Collection storage: Storage - logger: Logger + logFile: File - constructor({ streams, countries, logger }: CountriesGeneratorProps) { + constructor({ streams, countries, logFile }: CountriesGeneratorProps) { this.streams = streams this.countries = countries this.storage = new Storage(PUBLIC_DIR) - this.logger = logger + this.logFile = logFile } async generate(): Promise { @@ -36,8 +37,8 @@ export class CountriesGenerator implements Generator { const playlist = new Playlist(countryStreams, { public: true }) const filepath = `countries/${country.code.toLowerCase()}.m3u` await this.storage.save(filepath, playlist.toString()) - this.logger.info( - JSON.stringify({ type: 'country', filepath, count: playlist.streams.count() }) + this.logFile.append( + JSON.stringify({ type: 'country', filepath, count: playlist.streams.count() }) + EOL ) country.getSubdivisions().forEach(async (subdivision: Subdivision) => { @@ -50,8 +51,8 @@ export class CountriesGenerator implements Generator { const playlist = new Playlist(subdivisionStreams, { public: true }) const filepath = `subdivisions/${subdivision.code.toLowerCase()}.m3u` await this.storage.save(filepath, playlist.toString()) - this.logger.info( - JSON.stringify({ type: 'subdivision', filepath, count: playlist.streams.count() }) + this.logFile.append( + JSON.stringify({ type: 'subdivision', filepath, count: playlist.streams.count() }) + EOL ) }) }) @@ -60,12 +61,12 @@ export class CountriesGenerator implements Generator { const undefinedPlaylist = new Playlist(undefinedStreams, { public: true }) const undefinedFilepath = 'countries/undefined.m3u' await this.storage.save(undefinedFilepath, undefinedPlaylist.toString()) - this.logger.info( + this.logFile.append( JSON.stringify({ type: 'country', filepath: undefinedFilepath, count: undefinedPlaylist.streams.count() - }) + }) + EOL ) } } diff --git a/scripts/generators/indexCategoryGenerator.ts b/scripts/generators/indexCategoryGenerator.ts index 529ee83360..665f4cb0cf 100644 --- a/scripts/generators/indexCategoryGenerator.ts +++ b/scripts/generators/indexCategoryGenerator.ts @@ -1,22 +1,23 @@ -import { Generator } from './generator' -import { Collection, Storage, Logger } from '@freearhey/core' +import { Collection, Storage, File } from '@freearhey/core' import { Stream, Playlist, Category } from '../models' import { PUBLIC_DIR } from '../constants' +import { Generator } from './generator' +import { EOL } from 'node:os' type IndexCategoryGeneratorProps = { streams: Collection - logger: Logger + logFile: File } export class IndexCategoryGenerator implements Generator { streams: Collection storage: Storage - logger: Logger + logFile: File - constructor({ streams, logger }: IndexCategoryGeneratorProps) { + constructor({ streams, logFile }: IndexCategoryGeneratorProps) { this.streams = streams this.storage = new Storage(PUBLIC_DIR) - this.logger = logger + this.logFile = logFile } async generate(): Promise { @@ -48,6 +49,8 @@ export class IndexCategoryGenerator implements Generator { const playlist = new Playlist(groupedStreams, { public: true }) const filepath = 'index.category.m3u' await this.storage.save(filepath, playlist.toString()) - this.logger.info(JSON.stringify({ type: 'index', filepath, count: playlist.streams.count() })) + this.logFile.append( + JSON.stringify({ type: 'index', filepath, count: playlist.streams.count() }) + EOL + ) } } diff --git a/scripts/generators/indexCountryGenerator.ts b/scripts/generators/indexCountryGenerator.ts index c65a43734d..82eb335efd 100644 --- a/scripts/generators/indexCountryGenerator.ts +++ b/scripts/generators/indexCountryGenerator.ts @@ -1,22 +1,23 @@ -import { Generator } from './generator' -import { Collection, Storage, Logger } from '@freearhey/core' +import { Collection, Storage, File } from '@freearhey/core' import { Stream, Playlist, Country } from '../models' import { PUBLIC_DIR } from '../constants' +import { Generator } from './generator' +import { EOL } from 'node:os' type IndexCountryGeneratorProps = { streams: Collection - logger: Logger + logFile: File } export class IndexCountryGenerator implements Generator { streams: Collection storage: Storage - logger: Logger + logFile: File - constructor({ streams, logger }: IndexCountryGeneratorProps) { + constructor({ streams, logFile }: IndexCountryGeneratorProps) { this.streams = streams this.storage = new Storage(PUBLIC_DIR) - this.logger = logger + this.logFile = logFile } async generate(): Promise { @@ -56,6 +57,8 @@ export class IndexCountryGenerator implements Generator { const playlist = new Playlist(groupedStreams, { public: true }) const filepath = 'index.country.m3u' await this.storage.save(filepath, playlist.toString()) - this.logger.info(JSON.stringify({ type: 'index', filepath, count: playlist.streams.count() })) + this.logFile.append( + JSON.stringify({ type: 'index', filepath, count: playlist.streams.count() }) + EOL + ) } } diff --git a/scripts/generators/indexGenerator.ts b/scripts/generators/indexGenerator.ts index fafda061f1..5cfa86c666 100644 --- a/scripts/generators/indexGenerator.ts +++ b/scripts/generators/indexGenerator.ts @@ -1,22 +1,23 @@ -import { Collection, Logger, Storage } from '@freearhey/core' +import { Collection, File, Storage } from '@freearhey/core' import { Stream, Playlist } from '../models' -import { Generator } from './generator' import { PUBLIC_DIR } from '../constants' +import { Generator } from './generator' +import { EOL } from 'node:os' type IndexGeneratorProps = { streams: Collection - logger: Logger + logFile: File } export class IndexGenerator implements Generator { streams: Collection storage: Storage - logger: Logger + logFile: File - constructor({ streams, logger }: IndexGeneratorProps) { + constructor({ streams, logFile }: IndexGeneratorProps) { this.streams = streams this.storage = new Storage(PUBLIC_DIR) - this.logger = logger + this.logFile = logFile } async generate(): Promise { @@ -27,6 +28,8 @@ export class IndexGenerator implements Generator { const playlist = new Playlist(sfwStreams, { public: true }) const filepath = 'index.m3u' await this.storage.save(filepath, playlist.toString()) - this.logger.info(JSON.stringify({ type: 'index', filepath, count: playlist.streams.count() })) + this.logFile.append( + JSON.stringify({ type: 'index', filepath, count: playlist.streams.count() }) + EOL + ) } } diff --git a/scripts/generators/indexLanguageGenerator.ts b/scripts/generators/indexLanguageGenerator.ts index 1116eb7408..3df9f71f2e 100644 --- a/scripts/generators/indexLanguageGenerator.ts +++ b/scripts/generators/indexLanguageGenerator.ts @@ -1,22 +1,23 @@ -import { Generator } from './generator' -import { Collection, Storage, Logger } from '@freearhey/core' +import { Collection, Storage, File } from '@freearhey/core' import { Stream, Playlist, Language } from '../models' import { PUBLIC_DIR } from '../constants' +import { Generator } from './generator' +import { EOL } from 'node:os' type IndexLanguageGeneratorProps = { streams: Collection - logger: Logger + logFile: File } export class IndexLanguageGenerator implements Generator { streams: Collection storage: Storage - logger: Logger + logFile: File - constructor({ streams, logger }: IndexLanguageGeneratorProps) { + constructor({ streams, logFile }: IndexLanguageGeneratorProps) { this.streams = streams this.storage = new Storage(PUBLIC_DIR) - this.logger = logger + this.logFile = logFile } async generate(): Promise { @@ -47,6 +48,8 @@ export class IndexLanguageGenerator implements Generator { const playlist = new Playlist(groupedStreams, { public: true }) const filepath = 'index.language.m3u' await this.storage.save(filepath, playlist.toString()) - this.logger.info(JSON.stringify({ type: 'index', filepath, count: playlist.streams.count() })) + this.logFile.append( + JSON.stringify({ type: 'index', filepath, count: playlist.streams.count() }) + EOL + ) } } diff --git a/scripts/generators/indexNsfwGenerator.ts b/scripts/generators/indexNsfwGenerator.ts index a89cf0a100..e1e98375b6 100644 --- a/scripts/generators/indexNsfwGenerator.ts +++ b/scripts/generators/indexNsfwGenerator.ts @@ -1,22 +1,23 @@ -import { Collection, Logger, Storage } from '@freearhey/core' +import { Collection, File, Storage } from '@freearhey/core' import { Stream, Playlist } from '../models' -import { Generator } from './generator' import { PUBLIC_DIR } from '../constants' +import { Generator } from './generator' +import { EOL } from 'node:os' type IndexNsfwGeneratorProps = { streams: Collection - logger: Logger + logFile: File } export class IndexNsfwGenerator implements Generator { streams: Collection storage: Storage - logger: Logger + logFile: File - constructor({ streams, logger }: IndexNsfwGeneratorProps) { + constructor({ streams, logFile }: IndexNsfwGeneratorProps) { this.streams = streams this.storage = new Storage(PUBLIC_DIR) - this.logger = logger + this.logFile = logFile } async generate(): Promise { @@ -25,6 +26,8 @@ export class IndexNsfwGenerator implements Generator { const playlist = new Playlist(allStreams, { public: true }) const filepath = 'index.nsfw.m3u' await this.storage.save(filepath, playlist.toString()) - this.logger.info(JSON.stringify({ type: 'index', filepath, count: playlist.streams.count() })) + this.logFile.append( + JSON.stringify({ type: 'index', filepath, count: playlist.streams.count() }) + EOL + ) } } diff --git a/scripts/generators/indexRegionGenerator.ts b/scripts/generators/indexRegionGenerator.ts index 94537c9af0..c462fcfceb 100644 --- a/scripts/generators/indexRegionGenerator.ts +++ b/scripts/generators/indexRegionGenerator.ts @@ -1,25 +1,26 @@ -import { Generator } from './generator' -import { Collection, Storage, Logger } from '@freearhey/core' +import { Collection, Storage, File } from '@freearhey/core' import { Stream, Playlist, Region } from '../models' import { PUBLIC_DIR } from '../constants' +import { Generator } from './generator' +import { EOL } from 'node:os' type IndexRegionGeneratorProps = { streams: Collection regions: Collection - logger: Logger + logFile: File } export class IndexRegionGenerator implements Generator { streams: Collection regions: Collection storage: Storage - logger: Logger + logFile: File - constructor({ streams, regions, logger }: IndexRegionGeneratorProps) { + constructor({ streams, regions, logFile }: IndexRegionGeneratorProps) { this.streams = streams this.regions = regions this.storage = new Storage(PUBLIC_DIR) - this.logger = logger + this.logFile = logFile } async generate(): Promise { @@ -58,6 +59,8 @@ export class IndexRegionGenerator implements Generator { const playlist = new Playlist(groupedStreams, { public: true }) const filepath = 'index.region.m3u' await this.storage.save(filepath, playlist.toString()) - this.logger.info(JSON.stringify({ type: 'index', filepath, count: playlist.streams.count() })) + this.logFile.append( + JSON.stringify({ type: 'index', filepath, count: playlist.streams.count() }) + EOL + ) } } diff --git a/scripts/generators/languagesGenerator.ts b/scripts/generators/languagesGenerator.ts index 114fcddb2b..f7ae9976e4 100644 --- a/scripts/generators/languagesGenerator.ts +++ b/scripts/generators/languagesGenerator.ts @@ -1,19 +1,20 @@ -import { Generator } from './generator' -import { Collection, Storage, Logger } from '@freearhey/core' +import { Collection, Storage, File } from '@freearhey/core' import { Playlist, Language, Stream } from '../models' import { PUBLIC_DIR } from '../constants' +import { Generator } from './generator' +import { EOL } from 'node:os' -type LanguagesGeneratorProps = { streams: Collection; logger: Logger } +type LanguagesGeneratorProps = { streams: Collection; logFile: File } export class LanguagesGenerator implements Generator { streams: Collection storage: Storage - logger: Logger + logFile: File - constructor({ streams, logger }: LanguagesGeneratorProps) { + constructor({ streams, logFile }: LanguagesGeneratorProps) { this.streams = streams this.storage = new Storage(PUBLIC_DIR) - this.logger = logger + this.logFile = logFile } async generate(): Promise { @@ -38,8 +39,8 @@ export class LanguagesGenerator implements Generator { const playlist = new Playlist(languageStreams, { public: true }) const filepath = `languages/${language.code}.m3u` await this.storage.save(filepath, playlist.toString()) - this.logger.info( - JSON.stringify({ type: 'language', filepath, count: playlist.streams.count() }) + this.logFile.append( + JSON.stringify({ type: 'language', filepath, count: playlist.streams.count() }) + EOL ) }) @@ -50,8 +51,8 @@ export class LanguagesGenerator implements Generator { const playlist = new Playlist(undefinedStreams, { public: true }) const filepath = 'languages/undefined.m3u' await this.storage.save(filepath, playlist.toString()) - this.logger.info( - JSON.stringify({ type: 'language', filepath, count: playlist.streams.count() }) + this.logFile.append( + JSON.stringify({ type: 'language', filepath, count: playlist.streams.count() }) + EOL ) } } diff --git a/scripts/generators/regionsGenerator.ts b/scripts/generators/regionsGenerator.ts index fb0a5d6882..4d649a3517 100644 --- a/scripts/generators/regionsGenerator.ts +++ b/scripts/generators/regionsGenerator.ts @@ -1,25 +1,26 @@ -import { Generator } from './generator' -import { Collection, Storage, Logger } from '@freearhey/core' +import { Collection, Storage, File } from '@freearhey/core' import { Playlist, Region, Stream } from '../models' import { PUBLIC_DIR } from '../constants' +import { Generator } from './generator' +import { EOL } from 'node:os' type RegionsGeneratorProps = { streams: Collection regions: Collection - logger: Logger + logFile: File } export class RegionsGenerator implements Generator { streams: Collection regions: Collection storage: Storage - logger: Logger + logFile: File - constructor({ streams, regions, logger }: RegionsGeneratorProps) { + constructor({ streams, regions, logFile }: RegionsGeneratorProps) { this.streams = streams this.regions = regions this.storage = new Storage(PUBLIC_DIR) - this.logger = logger + this.logFile = logFile } async generate(): Promise { @@ -35,8 +36,8 @@ export class RegionsGenerator implements Generator { const playlist = new Playlist(regionStreams, { public: true }) const filepath = `regions/${region.code.toLowerCase()}.m3u` await this.storage.save(filepath, playlist.toString()) - this.logger.info( - JSON.stringify({ type: 'region', filepath, count: playlist.streams.count() }) + this.logFile.append( + JSON.stringify({ type: 'region', filepath, count: playlist.streams.count() }) + EOL ) }) @@ -44,18 +45,20 @@ export class RegionsGenerator implements Generator { const internationalPlaylist = new Playlist(internationalStreams, { public: true }) const internationalFilepath = 'regions/int.m3u' await this.storage.save(internationalFilepath, internationalPlaylist.toString()) - this.logger.info( + this.logFile.append( JSON.stringify({ type: 'region', filepath: internationalFilepath, count: internationalPlaylist.streams.count() - }) + }) + EOL ) const undefinedStreams = streams.filter((stream: Stream) => !stream.hasBroadcastArea()) const playlist = new Playlist(undefinedStreams, { public: true }) const filepath = 'regions/undefined.m3u' await this.storage.save(filepath, playlist.toString()) - this.logger.info(JSON.stringify({ type: 'region', filepath, count: playlist.streams.count() })) + this.logFile.append( + JSON.stringify({ type: 'region', filepath, count: playlist.streams.count() }) + EOL + ) } } diff --git a/scripts/models/feed.ts b/scripts/models/feed.ts index 03ae31184e..2b1fa9d34d 100644 --- a/scripts/models/feed.ts +++ b/scripts/models/feed.ts @@ -144,8 +144,8 @@ export class Feed { this.broadcastRegions = regions.filter((region: Region) => { if (region.code === 'INT') return false - - return region.countryCodes.intersects(countriesCodes) + const intersected = region.countryCodes.intersects(countriesCodes) + return intersected.notEmpty() }) return this From 5ae1d81c4deb15838c3d36c3ed95af3ef84c7bdb Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 23 Apr 2025 20:58:21 +0300 Subject: [PATCH 21/24] Update dependencies --- package-lock.json | 13 +++++++------ package.json | 1 + yarn.lock | 8 ++++---- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7f48efc865..e09fd39497 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,7 @@ "commander": "^8.3.0", "console-table-printer": "^2.12.1", "eslint": "^9.17.0", + "glob": "^11.0.2", "globals": "^16.0.0", "iptv-checker": "^0.29.1", "iptv-playlist-parser": "^0.13.0", @@ -4038,9 +4039,9 @@ } }, "node_modules/glob": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", - "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.2.tgz", + "integrity": "sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^4.0.1", @@ -9580,9 +9581,9 @@ } }, "glob": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", - "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.2.tgz", + "integrity": "sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ==", "requires": { "foreground-child": "^3.1.0", "jackspeak": "^4.0.1", diff --git a/package.json b/package.json index c03d75ccc4..8b957e01c5 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "commander": "^8.3.0", "console-table-printer": "^2.12.1", "eslint": "^9.17.0", + "glob": "^11.0.2", "globals": "^16.0.0", "iptv-checker": "^0.29.1", "iptv-playlist-parser": "^0.13.0", diff --git a/yarn.lock b/yarn.lock index 1491b68a9a..188beea60e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2082,10 +2082,10 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" -glob@^11.0.0, glob@^11.0.1: - version "11.0.1" - resolved "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz" - integrity sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw== +glob@^11.0.0, glob@^11.0.1, glob@^11.0.2: + version "11.0.2" + resolved "https://registry.npmjs.org/glob/-/glob-11.0.2.tgz" + integrity sha512-YT7U7Vye+t5fZ/QMkBFrTJ7ZQxInIUjwyAjVj84CYXqgBdv30MFUPGnBR6sQaVq6Is15wYJUsnzTuWaGRBhBAQ== dependencies: foreground-child "^3.1.0" jackspeak "^4.0.1" From a8d68acd7bf09d2c962fb30fbec8dd9d88475084 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 23 Apr 2025 21:25:18 +0300 Subject: [PATCH 22/24] Update test.test.ts --- tests/commands/playlist/test.test.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/commands/playlist/test.test.ts b/tests/commands/playlist/test.test.ts index 515dbf824c..813ed401d4 100644 --- a/tests/commands/playlist/test.test.ts +++ b/tests/commands/playlist/test.test.ts @@ -1,5 +1,4 @@ import { execSync } from 'child_process' -import path from 'node:path' import os from 'node:os' type ExecError = { @@ -19,12 +18,8 @@ describe('playlist:test', () => { execSync(cmd, { encoding: 'utf8' }) } catch (error) { if (process.env.DEBUG === 'true') console.log(cmd, error) - expect((error as ExecError).stdout).toContain(slash('playlist_test/ag.m3u')) + expect((error as ExecError).stdout).toContain('playlist_test/ag.m3u') expect((error as ExecError).stdout).toContain('2 problems (1 errors, 1 warnings)') } }) }) - -function slash(filepath: string) { - return filepath.split(path.sep).join(path.posix.sep) -} From b0a299fad7e45f33d654215902dd83a06f6b156b Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 23 Apr 2025 23:34:43 +0300 Subject: [PATCH 23/24] Update scripts --- scripts/commands/playlist/test.ts | 7 ++++++- scripts/core/streamTester.ts | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/scripts/commands/playlist/test.ts b/scripts/commands/playlist/test.ts index a37b7ecd02..a5add7cb68 100644 --- a/scripts/commands/playlist/test.ts +++ b/scripts/commands/playlist/test.ts @@ -154,9 +154,14 @@ function drawTable() { } } -function onFinish() { +function onFinish(error) { clearInterval(interval) + if (error) { + console.error(error) + process.exit(1) + } + drawTable() console.log(chalk.red(`\n${errors + warnings} problems (${errors} errors, ${warnings} warnings)`)) diff --git a/scripts/core/streamTester.ts b/scripts/core/streamTester.ts index 89c44de746..860844b403 100644 --- a/scripts/core/streamTester.ts +++ b/scripts/core/streamTester.ts @@ -18,8 +18,8 @@ export class StreamTester { return this.checker.checkStream({ url: stream.url, http: { - referrer: stream.getHttpReferrer(), - 'user-agent': stream.getHttpUserAgent() + referrer: stream.getReferrer(), + 'user-agent': stream.getUserAgent() } }) } From 01c5aecb46c730704ac2e827a05114436dad241e Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Wed, 23 Apr 2025 23:51:39 +0300 Subject: [PATCH 24/24] Update scripts --- scripts/commands/playlist/test.ts | 10 +++++++--- scripts/commands/playlist/validate.ts | 18 ++++++++++-------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/scripts/commands/playlist/test.ts b/scripts/commands/playlist/test.ts index a5add7cb68..777c19f8d8 100644 --- a/scripts/commands/playlist/test.ts +++ b/scripts/commands/playlist/test.ts @@ -164,10 +164,14 @@ function onFinish(error) { drawTable() - console.log(chalk.red(`\n${errors + warnings} problems (${errors} errors, ${warnings} warnings)`)) + if (errors > 0 || warnings > 0) { + console.log( + chalk.red(`\n${errors + warnings} problems (${errors} errors, ${warnings} warnings)`) + ) - if (errors > 0) { - process.exit(1) + if (errors > 0) { + process.exit(1) + } } process.exit(0) diff --git a/scripts/commands/playlist/validate.ts b/scripts/commands/playlist/validate.ts index e089f7eff8..f1f7742eb9 100644 --- a/scripts/commands/playlist/validate.ts +++ b/scripts/commands/playlist/validate.ts @@ -109,16 +109,18 @@ async function main() { } } - console.log( - chalk.red( - `\n${ - errors.count() + warnings.count() - } problems (${errors.count()} errors, ${warnings.count()} warnings)` + if (errors.count() || warnings.count()) { + console.log( + chalk.red( + `\n${ + errors.count() + warnings.count() + } problems (${errors.count()} errors, ${warnings.count()} warnings)` + ) ) - ) - if (errors.count()) { - process.exit(1) + if (errors.count()) { + process.exit(1) + } } }