diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml index 26255c8bd..cf94890a5 100644 --- a/.github/workflows/update.yml +++ b/.github/workflows/update.yml @@ -32,17 +32,28 @@ jobs: run: npm run channels:lint - name: update sites.md run: npm run sites:update - env: - GH_TOKEN: ${{ steps.create-app-token.outputs.token }} - run: git status - name: commit changes to sites.md if: ${{ !env.ACT && github.ref == 'refs/heads/master' }} run: | - SITE=SITES.md - CHANGED=$(git diff ${SITE}) + FILE=SITES.md + CHANGED=$(git diff ${FILE}) if [ -n "${CHANGED}" ]; then - git add ${SITE} - git commit -m "[Bot] Update ${SITE}" -m "Committed by [iptv-bot](https://github.com/apps/iptv-bot) via [update](https://github.com/iptv-org/epg/actions/runs/${{ github.run_id }}) workflow." --no-verify + git add ${FILE} + git commit -m "[Bot] Update ${FILE}" -m "Committed by [iptv-bot](https://github.com/apps/iptv-bot) via [update](https://github.com/iptv-org/epg/actions/runs/${{ github.run_id }}) workflow." --no-verify + git push + fi + - name: update guides.md + run: npm run guides:update + - run: git status + - name: commit changes to guides.md + if: ${{ !env.ACT && github.ref == 'refs/heads/master' }} + run: | + FILE=GUIDES.md + CHANGED=$(git diff ${FILE}) + if [ -n "${CHANGED}" ]; then + git add ${FILE} + git commit -m "[Bot] Update ${FILE}" -m "Committed by [iptv-bot](https://github.com/apps/iptv-bot) via [update](https://github.com/iptv-org/epg/actions/runs/${{ github.run_id }}) workflow." --no-verify git push fi - name: generate .api/guides.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e7089e053..5aae9de3a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -407,6 +407,27 @@ This way, you can map channels by simply selecting the proper ID from the list: Once complete, [commit](https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/about-commits) all changes and send a [pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests). +### How to add my server to the GUIDES.md? + +To do this, you just need to place the `worker.json` file in a public directory on your server. + +
+worker.json +
+ +```json +{ + "channels": "path/to/channels.xml", + "guide": "path/to/guide.xml" +} +``` + +
+ +And then add server domain or IP address to the [workers.txt](workers.txt) file. + +Once your request is approved, it will automatically be added to the [GUIDES.md](GUIDES.md). + ## Project Structure - `.github/` @@ -417,8 +438,10 @@ Once complete, [commit](https://docs.github.com/en/pull-requests/committing-chan - `sites/`: contains configurations, channel lists and tests for all sites. - `tests/`: contains tests to check the scripts. - `CONTRIBUTING.md`: file you are currently reading. +- `GUIDES.md`: list of all available guides and their current status. - `README.md`: project description displayed on the home page. - `SITES.md`: list of all supported sites and their current status. +- `workers.txt`: list of all available community workers. ## Scripts @@ -439,6 +462,7 @@ To run scripts use the `npm run ` command. - `channels:validate`: checks the description of channels for errors. - `sites:init`: creates a new site config from the template. - `sites:update`: updates the list of sites and their status in [SITES.md](SITES.md). +- `guides:update`: updates the list of guides and their status in [GUIDES.md](GUIDES.md). - `grab`: downloads a program from a specified source. - `serve`: starts the [web server](https://github.com/vercel/serve). - `lint`: сhecks the scripts for syntax errors. diff --git a/GUIDES.md b/GUIDES.md new file mode 100644 index 000000000..46c58a37d --- /dev/null +++ b/GUIDES.md @@ -0,0 +1,12 @@ +# Guides + + + + + + + + +
HostStatusChannelsLast UpdatedLinks
worker-9dd4.onrender.com🟢1a day agochannels.xml
guide.xml
+ +[How can I add my server to the list?](CONTRIBUTING.md#how-to-add-my-server-to-the-guides-md) diff --git a/README.md b/README.md index 06a8bf352..d6aeecd9a 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Tools for downloading the EPG (Electronic Program Guide) for thousands of TV cha - 🚀 [Usage](#usage) - 💫 [Update](#update) - 🐋 [Docker](#docker) -- 📺 [Playlists](#playlists) +- 📅 [Guides](#guides) - 🗄 [Database](#database) - 👨‍💻 [API](#api) - 📚 [Resources](#resources) @@ -155,7 +155,7 @@ docker pull ghcr.io/iptv-org/epg:master ### Create and run container ```sh -docker run -p 3000:3000 -v /path/to/channels.xml:/epg/channels.xml ghcr.io/iptv-org/epg:master +docker run -p 3000:3000 -v /path/to/channels.xml:/epg/public/channels.xml ghcr.io/iptv-org/epg:master ``` By default, the guide will be downloaded every day at 00:00 UTC and saved to the `/epg/public/guide.xml` file inside the container. @@ -179,7 +179,7 @@ To fine-tune the execution, you can pass environment variables to the container ```sh docker run \ -p 5000:3000 \ --v /path/to/channels.xml:/epg/channels.xml \ +-v /path/to/channels.xml:/epg/public/channels.xml \ -e CRON_SCHEDULE="0 0,12 * * *" \ -e MAX_CONNECTIONS=10 \ -e GZIP=true \ @@ -203,6 +203,10 @@ ghcr.io/iptv-org/epg:master | DELAY | Delay between request in milliseconds (default: 0) | | RUN_AT_STARTUP | Run grab on container startup (default: true) | +## Guides + +Any user can share the guides they have created with the rest of the community. A complete list of these guides and their current status can be found in the [GUIDES.md](GUIDES.md) file. + ## 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. @@ -236,4 +240,3 @@ And thank you to everyone who has already contributed! ## License [![CC0](http://mirrors.creativecommons.org/presskit/buttons/88x31/svg/cc-zero.svg)](LICENSE) - diff --git a/SITES.md b/SITES.md index ae1aa5d7e..c2da69e56 100644 --- a/SITES.md +++ b/SITES.md @@ -99,10 +99,10 @@ moji.id11🟢 mojmaxtv.hrvatskitelekom.hr2430🟢 mon-programme-tv.be11194🟢 - movistarplus.es1780🔴https://github.com/iptv-org/epg/issues/2865 + movistarplus.es1780🟢 mtel.ba5010🟢 mts.rs4570🟢 - mujtvprogram.cz216202🟢 + mujtvprogram.cz213190🟢 musor.tv181142🟢 mysky.com.ph11543🟢 mytelly.co.uk488387🟢https://github.com/iptv-org/epg/issues/2985 @@ -116,10 +116,10 @@ nowplayer.now.com288227🟢 nuevosiglo.com.uy17346🟢 nzxmltv.com532118🟢 - ontvtonight.com5177528🟢 + ontvtonight.com5177528🟢https://github.com/iptv-org/epg/issues/3014 opto.sic.pt44🟢 orangetv.orange.es273151🟢 - osn.com11898🟢 + osn.com13486🟢 passie.nl11🟢 pbsguam.org11🟢 pickx.be404375🟢 @@ -185,7 +185,7 @@ tv.lv13749🟢 tv.magenta.at307219🟢 tv.mail.ru664632🟢 - tv.movistar.com.pe28239🟢 + tv.movistar.com.pe26231🟢 tv.nu199180🟢 tv.post.lu332241🟢 tv.sfr.fr489438🟢https://github.com/iptv-org/epg/issues/2998 diff --git a/package-lock.json b/package-lock.json index dc7fa85f4..352f9b8b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41,6 +41,7 @@ "@typescript-eslint/parser": "^8.38.0", "axios": "^1.11.0", "axios-cookiejar-support": "^6.0.4", + "axios-mock-adapter": "^2.1.0", "chalk": "^5.4.1", "cheerio": "^1.1.2", "cli-progress": "^3.12.0", @@ -52,7 +53,7 @@ "cwait": "^1.1.2", "dayjs": "^1.11.13", "epg-grabber": "^0.46.1", - "epg-parser": "^0.3.1", + "epg-parser": "^0.5.0", "eslint": "^9.32.0", "eslint-config-prettier": "^10.1.8", "form-data": "^4.0.4", @@ -1091,9 +1092,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dependencies": { "argparse": "^2.0.1" }, @@ -1610,27 +1611,6 @@ "node": ">=16.0.0" } }, - "node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/brace-expansion": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", - "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", - "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -2228,9 +2208,10 @@ } }, "node_modules/@jest/reporters/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -2845,6 +2826,17 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "license": "ISC" }, + "node_modules/@pm2/blessed": { + "version": "0.1.81", + "resolved": "https://registry.npmjs.org/@pm2/blessed/-/blessed-0.1.81.tgz", + "integrity": "sha512-ZcNHqQjMuNRcQ7Z1zJbFIQZO/BDKV3KbiTckWdfbUaYhj7uNmUwb+FbdDWSCkvxNr9dBJQwvV17o6QBkAvgO0g==", + "bin": { + "blessed": "bin/tput.js" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/@pm2/io": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@pm2/io/-/io-6.1.0.tgz", @@ -4206,23 +4198,25 @@ } }, "node_modules/axios": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", - "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", + "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "node_modules/axios-cache-interceptor": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/axios-cache-interceptor/-/axios-cache-interceptor-1.8.3.tgz", - "integrity": "sha512-ifuSBoCEkVaiugg1UTjVuTdK+SjSOJ35pdv2OrzhRT3wDMr52QiayQxUqs7jd7GDsfPOjMcw3T3ek0TysbyZZw==", + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/axios-cache-interceptor/-/axios-cache-interceptor-1.11.4.tgz", + "integrity": "sha512-xZ4OZUxdpcFUpZjrqfYlGK0VglpPRKKSoE3vMHrstxolixQNs/MrbMezOAO5uS454hIEcWpnk75RZK26WkPW/g==", "dependencies": { - "cache-parser": "1.2.5", - "fast-defer": "1.1.8", - "object-code": "1.3.3" + "cache-parser": "^1.2.6", + "fast-defer": "^1.1.9", + "http-vary": "^1.0.3", + "object-code": "^2.0.0", + "try": "^1.0.3" }, "engines": { "node": ">=12" @@ -4398,17 +4392,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/blessed": { - "version": "0.1.81", - "resolved": "https://registry.npmjs.org/blessed/-/blessed-0.1.81.tgz", - "integrity": "sha512-LoF5gae+hlmfORcG1M5+5XZi4LBmvlXTzwJWzUlPryN/SJdSflZvROM2TwkT0GMpq7oqT48NRd4GS7BiVBc5OQ==", - "bin": { - "blessed": "bin/tput.js" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -4614,9 +4597,9 @@ } }, "node_modules/cache-parser": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/cache-parser/-/cache-parser-1.2.5.tgz", - "integrity": "sha512-Md/4VhAHByQ9frQ15WD6LrMNiVw9AEl/J7vWIXw+sxT6fSOpbtt6LHTp76vy8+bOESPBO94117Hm2bIjlI7XjA==" + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/cache-parser/-/cache-parser-1.2.6.tgz", + "integrity": "sha512-SjjnKlWgrhDrAWKUxAvmZLRGDa6JExMfjSu59/pvpNoI6mEHYSLcLKUw2RtECEOINvf6dxJo35fY+T/scA0SUA==" }, "node_modules/call-bind": { "version": "1.0.8", @@ -5238,9 +5221,9 @@ } }, "node_modules/dayjs": { - "version": "1.11.18", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz", - "integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==" + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==" }, "node_modules/debug": { "version": "4.3.4", @@ -5538,12 +5521,12 @@ } }, "node_modules/epg-parser": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/epg-parser/-/epg-parser-0.3.1.tgz", - "integrity": "sha512-y131hXfDthUdSeKbN0Ru1wiFF5er4t/TLT+IaAnHF2CYB0cnygHTJteQMDYIlHWHDsGj+z9ejm1cU3saFNF3nQ==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/epg-parser/-/epg-parser-0.5.0.tgz", + "integrity": "sha512-NK9vSev/KkCVcKilJd35PmB9tP4+tN45dhcsSxHagVoUTINh8KLZ1yvgtoPDVYr0IaSPJXIIIZWcu1JPjS5HMw==", "dependencies": { - "dayjs": "^1.11.6", - "lodash": "^4.17.21", + "dayjs": "^1.11.19", + "lodash.groupby": "^4.6.0", "xml-js": "^1.6.11" } }, @@ -6022,9 +6005,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-defer": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/fast-defer/-/fast-defer-1.1.8.tgz", - "integrity": "sha512-lEJeOH5VL5R09j6AA0D4Uvq7AgsHw0dAImQQ+F3iSyHZuAxyQfWobsagGpTcOPvJr3urmKRHrs+Gs9hV+/Qm/Q==" + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/fast-defer/-/fast-defer-1.1.9.tgz", + "integrity": "sha512-JP7Xm9HuePSeTT1DI78NeE9eAQvgNb9qNP2jlyQrcx4jiWM189omV6oyd0xaUPWHPlKmvDzz6H1FfPWIDU+xfg==" }, "node_modules/fast-glob": { "version": "3.3.3", @@ -6152,9 +6135,9 @@ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "funding": [ { "type": "individual", @@ -6214,9 +6197,9 @@ } }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -6377,14 +6360,14 @@ "integrity": "sha512-2e/nZezdVlyCopOCYHeW0onkbZg7xP1Ad6pndPy1rCygeRykefUS6r7oA5cJRGEFvseiaz5a/qUHFVX1dd6Isg==" }, "node_modules/glob": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", - "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", - "license": "ISC", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", - "minimatch": "^10.0.3", + "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" @@ -6410,13 +6393,34 @@ "node": ">=10.13.0" } }, - "node_modules/glob/node_modules/minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", - "license": "ISC", + "node_modules/glob/node_modules/balanced-match": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.2.tgz", + "integrity": "sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg==", "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" + "jackspeak": "^4.2.3" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.2.tgz", + "integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.0.tgz", + "integrity": "sha512-ugkC31VaVg9cF0DFVoADH12k6061zNZkZON+aX8AWsR9GhPcErkcMBceb6znR8wLERM2AkkOxy2nWRLpT9Jq5w==", + "dependencies": { + "brace-expansion": "^5.0.2" }, "engines": { "node": "20 || >=22" @@ -6590,6 +6594,11 @@ "node": ">= 14" } }, + "node_modules/http-vary": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http-vary/-/http-vary-1.0.3.tgz", + "integrity": "sha512-sx7Y8YTqF3o0mFJJvF66n8dbaE8v3liV1RgCz46XP5xK7dnzyZHvwMWRA115q5kjbCPBV65/nOMlgW54WLyiag==" + }, "node_modules/https-proxy-agent": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", @@ -7059,12 +7068,11 @@ } }, "node_modules/jackspeak": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", - "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", - "license": "BlueOak-1.0.0", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", + "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", "dependencies": { - "@isaacs/cliui": "^8.0.2" + "@isaacs/cliui": "^9.0.0" }, "engines": { "node": "20 || >=22" @@ -7073,6 +7081,14 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/jackspeak/node_modules/@isaacs/cliui": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", + "engines": { + "node": ">=18" + } + }, "node_modules/jest": { "version": "30.0.5", "resolved": "https://registry.npmjs.org/jest/-/jest-30.0.5.tgz", @@ -7360,9 +7376,10 @@ } }, "node_modules/jest-config/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -8046,9 +8063,10 @@ } }, "node_modules/jest-runtime/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -8690,9 +8708,14 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==" + }, + "node_modules/lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==" }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -9088,9 +9111,9 @@ } }, "node_modules/object-code": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/object-code/-/object-code-1.3.3.tgz", - "integrity": "sha512-/Ds4Xd5xzrtUOJ+xJQ57iAy0BZsZltOHssnDgcZ8DOhgh41q1YJCnTPnWdWSLkNGNnxYzhYChjc5dgC9mEERCA==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/object-code/-/object-code-2.0.0.tgz", + "integrity": "sha512-qOwMF43O/VAD51nJAB7MKsf1yWksql6O1i0DHRo1yaOQM6xJQH0NAE9UKJzYB7lyKw1jnpeb2BmB8qakjxiYZA==" }, "node_modules/object-treeify": { "version": "2.1.1", @@ -9533,36 +9556,36 @@ } }, "node_modules/pm2": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/pm2/-/pm2-6.0.10.tgz", - "integrity": "sha512-sbk4HsnhtJMx1wJlhFQhYfDRzHtVK+cvdrIezbjM9WjSyc7kLtQ4nZ5K7JLOdLe3AevytmRcTiOa3VvAQrve2A==", + "version": "6.0.14", + "resolved": "https://registry.npmjs.org/pm2/-/pm2-6.0.14.tgz", + "integrity": "sha512-wX1FiFkzuT2H/UUEA8QNXDAA9MMHDsK/3UHj6Dkd5U7kxyigKDA5gyDw78ycTQZAuGCLWyUX5FiXEuVQWafukA==", "dependencies": { "@pm2/agent": "~2.1.1", + "@pm2/blessed": "0.1.81", "@pm2/io": "~6.1.0", "@pm2/js-api": "~0.8.0", - "@pm2/pm2-version-check": "latest", + "@pm2/pm2-version-check": "^1.0.4", "ansis": "4.0.0-node10", - "async": "~3.2.6", - "blessed": "0.1.81", - "chokidar": "^3.5.3", - "cli-tableau": "^2.0.0", + "async": "3.2.6", + "chokidar": "3.6.0", + "cli-tableau": "2.0.1", "commander": "2.15.1", - "croner": "~4.1.92", - "dayjs": "~1.11.13", - "debug": "^4.3.7", + "croner": "4.1.97", + "dayjs": "1.11.15", + "debug": "4.4.3", "enquirer": "2.3.6", "eventemitter2": "5.0.1", "fclone": "1.0.11", - "js-yaml": "~4.1.0", + "js-yaml": "4.1.1", "mkdirp": "1.0.4", "needle": "2.4.0", - "pidusage": "~3.0", + "pidusage": "3.0.2", "pm2-axon": "~4.0.1", "pm2-axon-rpc": "~0.7.1", "pm2-deploy": "~1.0.2", "pm2-multimeter": "^0.1.2", - "promptly": "^2", - "semver": "^7.6.2", + "promptly": "2.2.0", + "semver": "7.7.2", "source-map-support": "0.5.21", "sprintf-js": "1.1.2", "vizion": "~2.2.1" @@ -9674,11 +9697,15 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==" }, + "node_modules/pm2/node_modules/dayjs": { + "version": "1.11.15", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.15.tgz", + "integrity": "sha512-MC+DfnSWiM9APs7fpiurHGCoeIx0Gdl6QZBy+5lu8MbYKN5FZEXqOgrundfibdfhGZ15o9hzmZ2xJjZnbvgKXQ==" + }, "node_modules/pm2/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dependencies": { "ms": "^2.1.3" }, @@ -9692,9 +9719,9 @@ } }, "node_modules/pm2/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dependencies": { "argparse": "^2.0.1" }, @@ -10643,9 +10670,9 @@ } }, "node_modules/systeminformation": { - "version": "5.25.11", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.25.11.tgz", - "integrity": "sha512-jI01fn/t47rrLTQB0FTlMCC+5dYx8o0RRF+R4BPiUNsvg5OdY0s9DKMFmJGrx5SwMZQ4cag0Gl6v8oycso9b/g==", + "version": "5.30.7", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.30.7.tgz", + "integrity": "sha512-33B/cftpaWdpvH+Ho9U1b08ss8GQuLxrWHelbJT1yw4M48Taj8W3ezcPuaLoIHZz5V6tVHuQPr5BprEfnBLBMw==", "optional": true, "os": [ "darwin", @@ -10800,6 +10827,14 @@ "node": ">= 14.0.0" } }, + "node_modules/try": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/try/-/try-1.0.3.tgz", + "integrity": "sha512-AHA8khVCII6zKyRkyPo6pRwoR9v5jb7QFw6e5avtaVSkxVfaEucYIo06xnwB+pJaEarfYNbs7W3Vq+LZLZiWyA==", + "funding": { + "url": "https://github.com/arthurfiorette/try?sponsor=1" + } + }, "node_modules/ts-api-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", @@ -10896,9 +10931,9 @@ } }, "node_modules/undici": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.12.0.tgz", - "integrity": "sha512-GrKEsc3ughskmGA9jevVlIOPMiiAHJ4OFUtaAH+NhfTUSiZ1wMPIQqQvAJUrJspFXJt3EBWgpAeoHEDVT1IBug==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.21.0.tgz", + "integrity": "sha512-Hn2tCQpoDt1wv23a68Ctc8Cr/BHpUSfaPYrkajTXOS9IKpxVRx/X5m1K2YkbK2ipgZgxXSgsUinl3x+2YdSSfg==", "engines": { "node": ">=20.18.1" } @@ -12001,9 +12036,9 @@ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==" }, "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "requires": { "argparse": "^2.0.1" } @@ -12310,19 +12345,6 @@ } } }, - "@isaacs/balanced-match": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", - "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==" - }, - "@isaacs/brace-expansion": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", - "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", - "requires": { - "@isaacs/balanced-match": "^4.0.1" - } - }, "@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -12760,9 +12782,9 @@ } }, "glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "requires": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -13226,6 +13248,11 @@ } } }, + "@pm2/blessed": { + "version": "0.1.81", + "resolved": "https://registry.npmjs.org/@pm2/blessed/-/blessed-0.1.81.tgz", + "integrity": "sha512-ZcNHqQjMuNRcQ7Z1zJbFIQZO/BDKV3KbiTckWdfbUaYhj7uNmUwb+FbdDWSCkvxNr9dBJQwvV17o6QBkAvgO0g==" + }, "@pm2/io": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@pm2/io/-/io-6.1.0.tgz", @@ -14130,23 +14157,25 @@ } }, "axios": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", - "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", + "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", "requires": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "axios-cache-interceptor": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/axios-cache-interceptor/-/axios-cache-interceptor-1.8.3.tgz", - "integrity": "sha512-ifuSBoCEkVaiugg1UTjVuTdK+SjSOJ35pdv2OrzhRT3wDMr52QiayQxUqs7jd7GDsfPOjMcw3T3ek0TysbyZZw==", + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/axios-cache-interceptor/-/axios-cache-interceptor-1.11.4.tgz", + "integrity": "sha512-xZ4OZUxdpcFUpZjrqfYlGK0VglpPRKKSoE3vMHrstxolixQNs/MrbMezOAO5uS454hIEcWpnk75RZK26WkPW/g==", "requires": { - "cache-parser": "1.2.5", - "fast-defer": "1.1.8", - "object-code": "1.3.3" + "cache-parser": "^1.2.6", + "fast-defer": "^1.1.9", + "http-vary": "^1.0.3", + "object-code": "^2.0.0", + "try": "^1.0.3" } }, "axios-cookiejar-support": { @@ -14264,11 +14293,6 @@ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==" }, - "blessed": { - "version": "0.1.81", - "resolved": "https://registry.npmjs.org/blessed/-/blessed-0.1.81.tgz", - "integrity": "sha512-LoF5gae+hlmfORcG1M5+5XZi4LBmvlXTzwJWzUlPryN/SJdSflZvROM2TwkT0GMpq7oqT48NRd4GS7BiVBc5OQ==" - }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", @@ -14401,9 +14425,9 @@ "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==" }, "cache-parser": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/cache-parser/-/cache-parser-1.2.5.tgz", - "integrity": "sha512-Md/4VhAHByQ9frQ15WD6LrMNiVw9AEl/J7vWIXw+sxT6fSOpbtt6LHTp76vy8+bOESPBO94117Hm2bIjlI7XjA==" + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/cache-parser/-/cache-parser-1.2.6.tgz", + "integrity": "sha512-SjjnKlWgrhDrAWKUxAvmZLRGDa6JExMfjSu59/pvpNoI6mEHYSLcLKUw2RtECEOINvf6dxJo35fY+T/scA0SUA==" }, "call-bind": { "version": "1.0.8", @@ -14838,9 +14862,9 @@ "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==" }, "dayjs": { - "version": "1.11.18", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz", - "integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==" + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==" }, "debug": { "version": "4.3.4", @@ -15056,12 +15080,12 @@ } }, "epg-parser": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/epg-parser/-/epg-parser-0.3.1.tgz", - "integrity": "sha512-y131hXfDthUdSeKbN0Ru1wiFF5er4t/TLT+IaAnHF2CYB0cnygHTJteQMDYIlHWHDsGj+z9ejm1cU3saFNF3nQ==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/epg-parser/-/epg-parser-0.5.0.tgz", + "integrity": "sha512-NK9vSev/KkCVcKilJd35PmB9tP4+tN45dhcsSxHagVoUTINh8KLZ1yvgtoPDVYr0IaSPJXIIIZWcu1JPjS5HMw==", "requires": { - "dayjs": "^1.11.6", - "lodash": "^4.17.21", + "dayjs": "^1.11.19", + "lodash.groupby": "^4.6.0", "xml-js": "^1.6.11" } }, @@ -15369,9 +15393,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-defer": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/fast-defer/-/fast-defer-1.1.8.tgz", - "integrity": "sha512-lEJeOH5VL5R09j6AA0D4Uvq7AgsHw0dAImQQ+F3iSyHZuAxyQfWobsagGpTcOPvJr3urmKRHrs+Gs9hV+/Qm/Q==" + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/fast-defer/-/fast-defer-1.1.9.tgz", + "integrity": "sha512-JP7Xm9HuePSeTT1DI78NeE9eAQvgNb9qNP2jlyQrcx4jiWM189omV6oyd0xaUPWHPlKmvDzz6H1FfPWIDU+xfg==" }, "fast-glob": { "version": "3.3.3", @@ -15481,9 +15505,9 @@ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, "follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==" + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==" }, "for-each": { "version": "0.3.5", @@ -15510,9 +15534,9 @@ } }, "form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -15622,24 +15646,40 @@ "integrity": "sha512-2e/nZezdVlyCopOCYHeW0onkbZg7xP1Ad6pndPy1rCygeRykefUS6r7oA5cJRGEFvseiaz5a/qUHFVX1dd6Isg==" }, "glob": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", - "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", "requires": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", - "minimatch": "^10.0.3", + "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "dependencies": { - "minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "balanced-match": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.2.tgz", + "integrity": "sha512-x0K50QvKQ97fdEz2kPehIerj+YTeptKF9hyYkKf6egnwmMWAkADiO0QCzSp0R5xN8FTZgYaBfSaue46Ej62nMg==", "requires": { - "@isaacs/brace-expansion": "^5.0.0" + "jackspeak": "^4.2.3" + } + }, + "brace-expansion": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.2.tgz", + "integrity": "sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw==", + "requires": { + "balanced-match": "^4.0.2" + } + }, + "minimatch": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.0.tgz", + "integrity": "sha512-ugkC31VaVg9cF0DFVoADH12k6061zNZkZON+aX8AWsR9GhPcErkcMBceb6znR8wLERM2AkkOxy2nWRLpT9Jq5w==", + "requires": { + "brace-expansion": "^5.0.2" } } } @@ -15746,6 +15786,11 @@ "debug": "^4.3.4" } }, + "http-vary": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http-vary/-/http-vary-1.0.3.tgz", + "integrity": "sha512-sx7Y8YTqF3o0mFJJvF66n8dbaE8v3liV1RgCz46XP5xK7dnzyZHvwMWRA115q5kjbCPBV65/nOMlgW54WLyiag==" + }, "https-proxy-agent": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", @@ -16042,11 +16087,18 @@ } }, "jackspeak": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", - "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", + "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", "requires": { - "@isaacs/cliui": "^8.0.2" + "@isaacs/cliui": "^9.0.0" + }, + "dependencies": { + "@isaacs/cliui": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==" + } } }, "jest": { @@ -16284,9 +16336,9 @@ } }, "glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "requires": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -16810,9 +16862,9 @@ } }, "glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "requires": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -17279,9 +17331,14 @@ } }, "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==" + }, + "lodash.groupby": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", + "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==" }, "lodash.merge": { "version": "4.6.2", @@ -17573,9 +17630,9 @@ "integrity": "sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==" }, "object-code": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/object-code/-/object-code-1.3.3.tgz", - "integrity": "sha512-/Ds4Xd5xzrtUOJ+xJQ57iAy0BZsZltOHssnDgcZ8DOhgh41q1YJCnTPnWdWSLkNGNnxYzhYChjc5dgC9mEERCA==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/object-code/-/object-code-2.0.0.tgz", + "integrity": "sha512-qOwMF43O/VAD51nJAB7MKsf1yWksql6O1i0DHRo1yaOQM6xJQH0NAE9UKJzYB7lyKw1jnpeb2BmB8qakjxiYZA==" }, "object-treeify": { "version": "2.1.1", @@ -17896,37 +17953,37 @@ } }, "pm2": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/pm2/-/pm2-6.0.10.tgz", - "integrity": "sha512-sbk4HsnhtJMx1wJlhFQhYfDRzHtVK+cvdrIezbjM9WjSyc7kLtQ4nZ5K7JLOdLe3AevytmRcTiOa3VvAQrve2A==", + "version": "6.0.14", + "resolved": "https://registry.npmjs.org/pm2/-/pm2-6.0.14.tgz", + "integrity": "sha512-wX1FiFkzuT2H/UUEA8QNXDAA9MMHDsK/3UHj6Dkd5U7kxyigKDA5gyDw78ycTQZAuGCLWyUX5FiXEuVQWafukA==", "requires": { "@pm2/agent": "~2.1.1", + "@pm2/blessed": "0.1.81", "@pm2/io": "~6.1.0", "@pm2/js-api": "~0.8.0", - "@pm2/pm2-version-check": "latest", + "@pm2/pm2-version-check": "^1.0.4", "ansis": "4.0.0-node10", - "async": "~3.2.6", - "blessed": "0.1.81", - "chokidar": "^3.5.3", - "cli-tableau": "^2.0.0", + "async": "3.2.6", + "chokidar": "3.6.0", + "cli-tableau": "2.0.1", "commander": "2.15.1", - "croner": "~4.1.92", - "dayjs": "~1.11.13", - "debug": "^4.3.7", + "croner": "4.1.97", + "dayjs": "1.11.15", + "debug": "4.4.3", "enquirer": "2.3.6", "eventemitter2": "5.0.1", "fclone": "1.0.11", - "js-yaml": "~4.1.0", + "js-yaml": "4.1.1", "mkdirp": "1.0.4", "needle": "2.4.0", - "pidusage": "~3.0", + "pidusage": "3.0.2", "pm2-axon": "~4.0.1", "pm2-axon-rpc": "~0.7.1", "pm2-deploy": "~1.0.2", "pm2-multimeter": "^0.1.2", "pm2-sysmonit": "^1.2.8", - "promptly": "^2", - "semver": "^7.6.2", + "promptly": "2.2.0", + "semver": "7.7.2", "source-map-support": "0.5.21", "sprintf-js": "1.1.2", "vizion": "~2.2.1" @@ -17942,18 +17999,23 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==" }, + "dayjs": { + "version": "1.11.15", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.15.tgz", + "integrity": "sha512-MC+DfnSWiM9APs7fpiurHGCoeIx0Gdl6QZBy+5lu8MbYKN5FZEXqOgrundfibdfhGZ15o9hzmZ2xJjZnbvgKXQ==" + }, "debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "requires": { "ms": "^2.1.3" } }, "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "requires": { "argparse": "^2.0.1" } @@ -18664,9 +18726,9 @@ } }, "systeminformation": { - "version": "5.25.11", - "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.25.11.tgz", - "integrity": "sha512-jI01fn/t47rrLTQB0FTlMCC+5dYx8o0RRF+R4BPiUNsvg5OdY0s9DKMFmJGrx5SwMZQ4cag0Gl6v8oycso9b/g==", + "version": "5.30.7", + "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.30.7.tgz", + "integrity": "sha512-33B/cftpaWdpvH+Ho9U1b08ss8GQuLxrWHelbJT1yw4M48Taj8W3ezcPuaLoIHZz5V6tVHuQPr5BprEfnBLBMw==", "optional": true }, "table2array": { @@ -18767,6 +18829,11 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==" }, + "try": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/try/-/try-1.0.3.tgz", + "integrity": "sha512-AHA8khVCII6zKyRkyPo6pRwoR9v5jb7QFw6e5avtaVSkxVfaEucYIo06xnwB+pJaEarfYNbs7W3Vq+LZLZiWyA==" + }, "ts-api-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", @@ -18826,9 +18893,9 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==" }, "undici": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.12.0.tgz", - "integrity": "sha512-GrKEsc3ughskmGA9jevVlIOPMiiAHJ4OFUtaAH+NhfTUSiZ1wMPIQqQvAJUrJspFXJt3EBWgpAeoHEDVT1IBug==" + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.21.0.tgz", + "integrity": "sha512-Hn2tCQpoDt1wv23a68Ctc8Cr/BHpUSfaPYrkajTXOS9IKpxVRx/X5m1K2YkbK2ipgZgxXSgsUinl3x+2YdSSfg==" }, "undici-types": { "version": "7.8.0", diff --git a/package.json b/package.json index 6c6d8aa59..4f2be8a44 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "channels:validate": "tsx scripts/commands/channels/validate.ts", "sites:init": "tsx scripts/commands/sites/init.ts", "sites:update": "tsx scripts/commands/sites/update.ts", + "guides:update": "tsx scripts/commands/guides/update.ts", "grab": "tsx scripts/commands/epg/grab.ts", "lint": "npx eslint \"{scripts,tests,sites}/**/*.{ts,mts,js}\"", "test": "cross-env TZ=Pacific/Nauru npx jest --runInBand", @@ -71,6 +72,7 @@ "@typescript-eslint/parser": "^8.38.0", "axios": "^1.11.0", "axios-cookiejar-support": "^6.0.4", + "axios-mock-adapter": "^2.1.0", "chalk": "^5.4.1", "cheerio": "^1.1.2", "cli-progress": "^3.12.0", @@ -82,7 +84,7 @@ "cwait": "^1.1.2", "dayjs": "^1.11.13", "epg-grabber": "^0.46.1", - "epg-parser": "^0.3.1", + "epg-parser": "^0.5.0", "eslint": "^9.32.0", "eslint-config-prettier": "^10.1.8", "form-data": "^4.0.4", diff --git a/pm2.config.js b/pm2.config.js index f1f901166..d06556d3b 100644 --- a/pm2.config.js +++ b/pm2.config.js @@ -1,35 +1,35 @@ -const grab = process.env.SITE - ? `npm run grab -- --site=${process.env.SITE} ${process.env.CLANG ? `--lang=${process.env.CLANG}` : '' - } --output=public/guide.xml` - : 'npm run grab -- --channels=channels.xml --output=public/guide.xml' - - -const apps = [ - { - name: 'serve', - script: 'npx serve -- public', - instances: 1, - watch: false, - autorestart: true - }, - { - name: 'grab', - script: `npx chronos -e "${grab}" -p "${process.env.CRON_SCHEDULE}" -l`, - instances: 1, - watch: false, - autorestart: true - } -]; - -if (process.env.RUN_AT_STARTUP === 'true') { - apps.push({ - name: 'grab-at-startup', - script: grab, - instances: 1, - autorestart: false, - watch: false, - max_restarts: 1 - }); -} - -module.exports = { apps }; \ No newline at end of file +const grab = process.env.SITE + ? `npm run grab -- --site=${process.env.SITE} ${ + process.env.CLANG ? `--lang=${process.env.CLANG}` : '' + } --output=public/guide.xml` + : 'npm run grab -- --channels=public/channels.xml --output=public/guide.xml' + +const apps = [ + { + name: 'serve', + script: 'npx serve -- public', + instances: 1, + watch: false, + autorestart: true + }, + { + name: 'grab', + script: `npx chronos -e "${grab}" -p "${process.env.CRON_SCHEDULE}" -l`, + instances: 1, + watch: false, + autorestart: true + } +] + +if (process.env.RUN_AT_STARTUP === 'true') { + apps.push({ + name: 'grab-at-startup', + script: grab, + instances: 1, + autorestart: false, + watch: false, + max_restarts: 1 + }) +} + +module.exports = { apps } diff --git a/scripts/commands/guides/update.ts b/scripts/commands/guides/update.ts new file mode 100644 index 000000000..f43b96b6c --- /dev/null +++ b/scripts/commands/guides/update.ts @@ -0,0 +1,144 @@ +import { HTMLTableRow, HTMLTableDataItem, HTMLTableColumn } from '../../types/htmlTable' +import epgGrabber, { EPGGrabber } from 'epg-grabber' +import AxiosMockAdapter from 'axios-mock-adapter' +import { Storage } from '@freearhey/storage-js' +import { Channel, Worker } from '../../models' +import { Collection } from '@freearhey/core' +import { ROOT_DIR } from '../../constants' +import { Logger } from '@freearhey/core' +import { HTMLTable } from '../../core' +import epgParser from 'epg-parser' +import axios from 'axios' + +async function main() { + const logger = new Logger({ level: process.env.NODE_ENV === 'test' ? -999 : 3 }) + const rootStorage = new Storage(ROOT_DIR) + const workers = new Map() + + logger.info('loading workers.txt...') + const workersTxt = await rootStorage.load('workers.txt') + + workersTxt.split('\r\n').forEach((host: string) => { + if (!host) return + + const worker = new Worker({ host }) + + workers.set(host, worker) + }) + + for (const worker of workers.values()) { + logger.info(`processing "${worker.host}"...`) + + const client = axios.create({ + baseURL: worker.getBaseUrl(), + timeout: 60000 + }) + + if (process.env.NODE_ENV === 'test') { + const mock = new AxiosMockAdapter(client) + if (worker.host === 'example.com') { + mock.onGet('worker.json').reply(404) + } else { + const testStorage = new Storage('tests/__data__/input/guides_update') + mock.onGet('worker.json').reply(200, await testStorage.load('worker.json')) + mock.onGet('channels.xml').reply(200, await testStorage.load('channels.xml')) + mock.onGet('guide.xml').reply(200, await testStorage.load('guide.xml')) + } + } + + const workerJson = await client + .get('worker.json') + .then(res => res.data) + .catch(err => { + worker.status = err.status + logger.error(err.message) + }) + + if (!workerJson) { + worker.status = 'MISSING_WORKER_CONFIG' + logger.error('Unable to load "workers.json"') + continue + } + + worker.channelsPath = workerJson.channels + worker.guidePath = workerJson.guide + + if (!worker.channelsPath) { + worker.status = 'MISSING_CHANNELS_PATH' + logger.error('The "channels" property is missing from the workers config') + continue + } + + if (!worker.guidePath) { + worker.status = 'MISSING_GUIDE_PATH' + logger.error('The "guide" property is missing from the workers config') + continue + } + + const channelsXml = await client + .get(worker.channelsPath) + .then(res => res.data) + .catch(err => { + worker.status = err.status + logger.error(err.message) + }) + + if (!channelsXml) continue + + const parsedChannels = EPGGrabber.parseChannelsXML(channelsXml) + worker.channels = new Collection(parsedChannels).map( + (channel: epgGrabber.Channel) => new Channel(channel.toObject()) + ) + + const guideXml = await client + .get(worker.guidePath) + .then(res => res.data) + .catch(err => { + worker.status = err.status + logger.error(err.message) + }) + + if (!guideXml) continue + + const parsedGuide = epgParser.parse(guideXml) + worker.lastUpdated = parsedGuide.date + + worker.status = 'OK' + } + + logger.info('creating guides table...') + const rows = new Collection() + workers.forEach((worker: Worker) => { + rows.add( + new Collection([ + { value: worker.host }, + { value: worker.getStatusEmoji(), align: 'center' }, + { value: worker.getChannelsCount().toString(), align: 'right' }, + { value: worker.getLastUpdated(), align: 'left' }, + { + value: + worker.status === 'OK' + ? `${worker.channelsPath}
${worker.guidePath}` + : '' + } + ]) + ) + }) + + logger.info('updating guides.md...') + const table = new HTMLTable( + rows, + new Collection([ + { name: 'Host', align: 'left' }, + { name: 'Status', align: 'left' }, + { name: 'Channels', align: 'left' }, + { name: 'Last Updated', align: 'left' }, + { name: 'Links', align: 'left' } + ]) + ) + const guidesTemplate = await new Storage().load('scripts/templates/_guides.md') + const guidesContent = guidesTemplate.replace('_TABLE_', table.toString()) + await rootStorage.save('GUIDES.md', guidesContent) +} + +main() diff --git a/scripts/models/index.ts b/scripts/models/index.ts index a05f13a89..c891f01bf 100644 --- a/scripts/models/index.ts +++ b/scripts/models/index.ts @@ -3,3 +3,4 @@ export * from './issue' export * from './site' export * from './channel' export * from './program' +export * from './worker' diff --git a/scripts/models/worker.ts b/scripts/models/worker.ts new file mode 100644 index 000000000..94b94dfcb --- /dev/null +++ b/scripts/models/worker.ts @@ -0,0 +1,73 @@ +import relativeTime from 'dayjs/plugin/relativeTime' +import { Collection } from '@freearhey/core' +import { Channel } from './channel' +import utc from 'dayjs/plugin/utc' +import dayjs from 'dayjs' + +dayjs.extend(relativeTime) +dayjs.extend(utc) + +export interface WorkerData { + host: string +} + +export class Worker { + host: string + channelsPath?: string + guidePath?: string + channels?: Collection + status?: string + lastUpdated?: string + + constructor(data: WorkerData) { + this.host = data.host + } + + getBaseUrl(): string { + return `https://${this.host}` + } + + getConfigUrl(): string { + const url = new URL('worker.json', this.getBaseUrl()) + + return url.href + } + + getChannelsUrl(): string { + if (!this.channelsPath) return '' + + const url = new URL(this.channelsPath, this.getBaseUrl()) + + return url.href + } + + getGuideUrl(): string { + if (!this.guidePath) return '' + + const url = new URL(this.guidePath, this.getBaseUrl()) + + return url.href + } + + getStatusEmoji(): string { + if (!this.status) return '⚪' + if (this.status === 'OK') return '🟢' + + return '🔴' + } + + getChannelsCount(): number { + if (!this.channels) return 0 + + return this.channels.count() + } + + getLastUpdated(): string { + if (!this.lastUpdated) return '-' + + let now = dayjs() + if (process.env.NODE_ENV === 'test') now = dayjs.utc('2026-02-13') + + return dayjs.utc(this.lastUpdated).from(now) + } +} diff --git a/scripts/templates/_guides.md b/scripts/templates/_guides.md new file mode 100644 index 000000000..d643a4637 --- /dev/null +++ b/scripts/templates/_guides.md @@ -0,0 +1,5 @@ +# Guides + +_TABLE_ + +[How can I add my server to the list?](CONTRIBUTING.md#how-to-add-my-server-to-the-guides-md) diff --git a/sites/artonline.tv/artonline.tv.config.js b/sites/artonline.tv/artonline.tv.config.js index 2b9fe78c4..b5abb1346 100644 --- a/sites/artonline.tv/artonline.tv.config.js +++ b/sites/artonline.tv/artonline.tv.config.js @@ -34,6 +34,7 @@ module.exports = { const programs = [] if (!content) return programs const items = JSON.parse(content) + if (!Array.isArray(items)) return programs items.forEach(item => { const image = parseImage(item) const start = parseStart(item) @@ -60,9 +61,9 @@ function parseStart(item) { } function parseDuration(item) { - const [, HH, mm, ss] = item.duration.match(/(\d+):(\d+):(\d+)/) + const [, HH, mm, ss] = item.duration.match(/(\d+):(\d+)(?::(\d+))?/) - return parseInt(HH) * 3600 + parseInt(mm) * 60 + parseInt(ss) + return parseInt(HH) * 3600 + parseInt(mm) * 60 + parseInt(ss || 0) } function parseImage(item) { diff --git a/sites/mujtvprogram.cz/mujtvprogram.cz.channels.xml b/sites/mujtvprogram.cz/mujtvprogram.cz.channels.xml index 0c8143117..ad0cf9a3e 100644 --- a/sites/mujtvprogram.cz/mujtvprogram.cz.channels.xml +++ b/sites/mujtvprogram.cz/mujtvprogram.cz.channels.xml @@ -1,219 +1,216 @@ - ČT1 - ČT2 - Nova - Prima - HBO - HBO2 - Cinemax AXN - Spektrum - Animal Planet - Discovery - Viasat Explorer - Viasat History - CS Film - Eurosport 1 - Nova Sport 1 - ČT sport - ÓČKO - MTV Europe - National Geographic - CNN - ČT24 - Eurosport 2 - TV Paprika - Spektrum Home - Disney Channel - Minimax - TVP 1 - TVP 2 - Polsat - ORF1 - ORF2 - ARD - 3SAT - ARTE - RTL - RTL2 - SAT1 - VOX - Kabel1 - Sport 1 - Film+ - FilmBox - FilmBox Plus - MTV 00s - Mezzo - Hustler - Cartoonito - Private TV - TV Noe - JimJam - Nova Cinema - National Geographic Wild - Extreme Sports Channel - TV Barrandov - Prima COOL - Sport 2 - BBC World News - Fishing & Hunting - Playboy - Travel Channel - MTV 80s - Baby TV - Ducktv - Stingray CMusic - Cinemax 2 - Euronews - Fashion TV - FilmBox Extra HD - France 24 (FR) - History Channel - LEO TV - Club MTV - Music Box - RT - Sky News - TV5 Monde - Blue Hustler MTV Europe CZ - DMAX - CNBC Europe - MTV 90s - MTV Hits - Deutsche Welle - PRO7 - Dvojka - Jednotka - Doma - Markíza - PLUS - JOJ - Spectrum - TA3 - Retro - Viasat Nature National Geographic HD - ZDF NEO - Al Jazeera English - Golf Channel - Prima Love - MTV Live HD - Stingray Classica - Stingray iConcerts - Deluxe Music - Duna (SK) - RTL - TV 8 - FilmBox Family - Film Europe - TLC - Bloomberg + Auto Motor Sport HD + CANAL+ Sport + Canal+ Action + Story 4 + Canal+ Sport 2 + Nova Sport 5 + Nova Sport 6 + Warner TV + Viasat True Crime + DVTV Extra + 3SAT Canal 24 Horas A11 - Erox HD - FightBox HD - SUPER RTL - TV Lux - M1 - Dajto - RELAX - DocuBox - CBS Reality - Cartoon Network - EroXXX HD - Prima ZOOM - FashionBox HD - Arena Sport 1 - WAU - ÓČKO STAR - KiKA - Disney Junior - M2 - ČT :D - ČT art - Duna (H) - RTL - Senzi - Rebel - Cool TV - 360TuneBox - Crime & Investigation + AMC + Animal Planet + ARTE AXN Black AXN White - ÓČKO EXPRES - Ginx eSports TV - Brazzers TV Europe - Arena Sport 2 - Mezzo Live HD - Investigation Discovery - ZDF - AMC - France 24 (EN) - FilmBox Premium - Jojko - HTV1 - HTV2 - Fast&FunBox HD - English Club TV - SAT.1 Gold - E! Entertainment - Mňam TV - Auto Motor Sport HD - Kino Barrandov Barrandov Krimi - JOJ Cinema - Nova Sport 2 - Food Network - Prima MAX - Paramount Network - Nova International - Markíza International - HBO3 - Premier Sport - JOJ Family - Dorcel TV - Prima PLUS - Nova Action - Nova Gold - Nova Fun - Mňau TV - Travelxp HD - Epic Drama - Televize Seznam - Prim KRIMI - Nick Jr. Czech - Nickelodeon - Power TV - Skylink 7 - Óčko Black HD - LALA TV + Cartoon Network + Cinemax 2 + Cinemax + CNBC Europe + CNN Prim NEWS + Crime & Investigation + CS Film CS History CS Horror CS Mystery - Nicktoons - Dorcel XXX - Love Nature HD + ČT1 + ČT2 + ČT24 + ČT art + ČT :D + ČT sport + ARD + Discovery + Disney Channel + Duna (SK) + E! Entertainment + Epic Drama + Eurosport 1 + Eurosport 2 + FashionBox HD + FilmBox + FilmBox Extra HD + FilmBox Family + FilmBox Premium + FilmBox Plus + Film Europe Film Europe + HD - CNN Prima News - Šlágr Premium - Vixen - Prima STAR - Premier Sport 2 + Film+ + Food Network + Golf Channel + HBO2 + HBO3 + HBO + History Channel + HTV1 + HTV2 + Investigation Discovery + JimJam + Kabel1 + Kino Barrandov + LALA TV + LEO TV + M1 + M2 + Minimax + Mňam TV + Mňau TV + MTV Europe + National Geographic + National Geographic Wild + Nickelodeon + Nick Jr. Czech + Nicktoons + Nova Action + Nova Cinema + Nova Fun + Nova Krimi + Nova International + Nova Lady + Nova Sport 1 + Nova Sport 2 Nova Sport 3 Nova Sport 4 - Nova Lady - Prima Show - JOJ Šport - RTVS Šport - RTVS 24 + ÓČKO + Óčko Black HD + ÓČKO EXPRES + ÓČKO STAR + ORF1 + ORF2 + Polsat + Power TV + Premier Sport 2 + Prima + Prima COOL + Prim KRIMI + Prim LOVE + Prima MAX + Prim SHOW + Prima STAR + Prima ZOOM + PRO7 + Rebel + Retro + RTL + RTL + RTL2 + SAT1 + SAT.1 Gold + Šlágr Premium + Spektrum + Spektrum Home + Sport 1 + Sport 2 + Televize Seznam + Fishing & Hunting + TLC + Travel Channel + Travelxp HD + TV Barrandov + TV Noe + Nova + TVP 1 + TVP 2 + TV Paprika + Viasat Explorer + Viasat History + Viasat Nature + VOX + ZDF + SUPER RTL + DMAX + KiKA + ZDF NEO + Music Box + 360TuneBox + Al Jazeera English + Baby TV + BBC World News + Bloomberg + Blue Hustler + Brazzers TV Europe + Cartoonito + CNN + Deluxe Music + Disney Junior + DocuBox + Dorcel TV + Dorcel XXX + Deutsche Welle + English Club TV + Erox HD + EroXXX HD + Euronews + Extreme Sports Channel + Fashion TV + Fast&FunBox HD + FightBox HD + France 24 (EN) + Ginx eSports TV + Hustler + Love Nature HD + Playboy + Private TV + RT + Sky News + Stingray Classica + Stingray CMusic + Stingray iConcerts + Vixen + France 24 (FR) + Mezzo + Mezzo Live HD + TV5 Monde + Cool TV + Duna (H) + RTL + Spectrum + ST24 Markíza Krimi - CANAL+ Sport JOJ 24 - Canal+ Action - Story 4 TV Spark JOJ Svet + JOJ Šport + Markíza KLASIK + JOJ Šport 2 + Arena Sport 1 + Arena Sport 2 + ducktv + ST2 + ST1 + JOJ Cinema + JOJ Family + Jojko + PLUS + WAU + Markíza + Dajto + Doma + Markíza International + Prima SK + Senzi + RTVS Šport + TA3 + TV 8 + JOJ + TV Lux diff --git a/sites/osn.com/osn.com_ar.channels.xml b/sites/osn.com/osn.com_ar.channels.xml index 98df113fc..30aeeee39 100644 --- a/sites/osn.com/osn.com_ar.channels.xml +++ b/sites/osn.com/osn.com_ar.channels.xml @@ -1,62 +1,70 @@ - OSN TV Movies Action - Kapatid TV5 - ANC Animal Planet + Blippi and Friends + Bloomberg Originals HD + OSN TV Documentary + DWRR 101.9 + eClutch LIVE 2 + eClutch Access​ + eClutch LIVE 1 + eClutch Arabic​ + Esport 24 + Gametoon​ + Ginx​ + MCM Top + Motorvision + OSNtv Crime + OSNtv Movies Horror + OSNtv Movies Family + OSNtv Now + OSNtv Movies Comedy + OSNtv Showcase Classics + OSNtv One + Padeltime​ + OSNtv Pop up Love story + RFM TV + Kapatid TV5 + Alfa Series Channel + Alfa Series +2 + Al Safwa + Alfa Al Yawm + ANC Bloomberg - Crime & Investigation Network - Cine Mo + Cartoon Network Alfa Cinema 1 Alfa Cinema 2 - CNBC - CNN Cinema One Global - Discovery ID + Cine Mo + CNN + Crime & Investigation Network Discovery HD - DWRR 101.9 - DZMM Teleradyo - E! Entertainment Alfa Fann Fatafeat - GMA Pinoy TV GMA Life TV GMA News TV + GMA Pinoy TV H2 HD History HD - OSN TV Kidzone + Discovery ID Moonbug Kids - MTV Live HD + Music Now Myx Nickelodeon HD Nick Jr NickToons HD - Music Now - OSN TV Crime - OSNtv Movies Horror - OSN TV Comedy - OSN TV Showcase - OSN TV Movies Family - OSN TV Now - OSN TV Movies Premiere - OSN TV Movies Comedy - OSN TV Kids - OSN TV Mezze - OSN TV News - OSN TV Movies Hollywood - OSN TV Showcase Classics - OSN TV One - OSN TV Yahala Bil Arabi - OSN TV Yahala Aflam - OSN TV Yahala + OSNtv Comedy + OSNtv Kids + OSNtv Mezze + OSNtv Movies Action + OSNtv Movies Hollywood + OSNtv Movies Premiere + OSNtv Showcase + OSNtv Yahala + OSNtv Yahala Aflam + OSNtv Yahala Bil Arabi PBA Rush - Al Safwa - Alfa Series +2 - Alfa Series Channel - Sky News HD - Cartoon Network + DZMM Teleradyo TFC TLC HD - MTV 00s - Alfa Al Yawm diff --git a/sites/osn.com/osn.com_en.channels.xml b/sites/osn.com/osn.com_en.channels.xml index 462de041b..1a008dce2 100644 --- a/sites/osn.com/osn.com_en.channels.xml +++ b/sites/osn.com/osn.com_en.channels.xml @@ -1,62 +1,70 @@ - OSN TV Movies Action - Kapatid TV5 - ANC Animal Planet + Blippi and Friends + Bloomberg Originals HD + OSN TV Documentary + DWRR 101.9 + eClutch LIVE 2 + eClutch Access​ + eClutch LIVE 1 + eClutch Arabic​ + Esport 24 + Gametoon​ + Ginx​ + MCM Top + Motorvision + OSNtv Crime + OSNtv Movies Horror + OSNtv Movies Family + OSNtv Now + OSNtv Movies Comedy + OSNtv Showcase Classics + OSNtv One + Padeltime​ + OSNtv Pop up Love story + RFM TV + Kapatid TV5 + Alfa Series Channel + Alfa Series +2 + Al Safwa + Alfa Al Yawm + ANC Bloomberg - Crime & Investigation Network - Cine Mo + Cartoon Network Alfa Cinema 1 Alfa Cinema 2 - CNBC - CNN Cinema One Global - Discovery ID + Cine Mo + CNN + Crime & Investigation Network Discovery HD - DWRR 101.9 - DZMM Teleradyo - E! Entertainment Alfa Fann Fatafeat - GMA Pinoy TV GMA Life TV GMA News TV + GMA Pinoy TV H2 HD History HD - OSN TV Kidzone + Discovery ID Moonbug Kids - MTV Live HD + Music Now Myx Nickelodeon HD Nick Jr NickToons HD - Music Now - OSN TV Crime - OSNtv Movies Horror - OSN TV Comedy - OSN TV Showcase - OSN TV Movies Family - OSN TV Now - OSN TV Movies Premiere - OSN TV Movies Comedy - OSN TV Kids - OSN TV Mezze - OSN TV News - OSN TV Movies Hollywood - OSN TV Showcase Classics - OSN TV One - OSN TV Yahala Bil Arabi - OSN TV Yahala Aflam - OSN TV Yahala + OSNtv Comedy + OSNtv Kids + OSNtv Mezze + OSNtv Movies Action + OSNtv Movies Hollywood + OSNtv Movies Premiere + OSNtv Showcase + OSNtv Yahala + OSNtv Yahala Aflam + OSNtv Yahala Bil Arabi PBA Rush - Al Safwa - Alfa Series +2 - Alfa Series Channel - Sky News HD - Cartoon Network + DZMM Teleradyo TFC TLC HD - MTV 00s - Alfa Al Yawm diff --git a/sites/tv.movistar.com.pe/tv.movistar.com.pe.channels.xml b/sites/tv.movistar.com.pe/tv.movistar.com.pe.channels.xml index 889c09310..d2491aa59 100644 --- a/sites/tv.movistar.com.pe/tv.movistar.com.pe.channels.xml +++ b/sites/tv.movistar.com.pe/tv.movistar.com.pe.channels.xml @@ -1,43 +1,27 @@ - CANAL N ESPN INTERNACIONAL ESPN 3 Cartoon Network HD - TNT HD OTT + TNT HD LATINA HD AMERICA TELEVISION HD PANAMERICANA TELEVISION HD TV PERU HD ATV HD - LATINA - AMERICA TELEVISION - PANAMERICANA TELEVISION - TV PERU - ATV - RPP TV - TV PERU 7.3 GLOBAL LA TELE PERU - WILLAX - ATV+ TNT TNT SERIES TNT SERIES HD - SONY WARNER CHANNEL STAR CHANNEL - AXN - UNIVERSAL CHANNEL FX INVESTIGATION DISCOVERY TRU TV TCM SPACE - PARAMOUNT - I-SAT STUDIO UNIVERSAL - EUROCHANNEL CINECANAL CINEMAX HBO @@ -48,14 +32,12 @@ HBO PLUS ESTE HBO XTREME DISCOVERY CHANNEL - HISTORY CHANNEL NAT GEO HISTORY 2 DISCOVERY SCIENCE DISCOVERY TURBO TLC HOME & HEALTH - GLITZ CARTOON NETWORK DISCOVERY KIDS CARTOONITO @@ -63,58 +45,46 @@ ANIMAL PLANET DISNEY JUNIOR FOX SPORTS PREMIUM - FOX SPORTS 2 - FOX SPORTS 3 + ESPN 7 + ESPN 6 ESPN 2 - GOL TV GOLF CHANNEL - JCP - TOROS TV + JOCKEY CLUB WOBI CNN ESPAÑOL CNN INTERNACIONAL FOX NEWS - PODER JUDICIAL CAPITAL TV ENLACE TBN - ORBE 21 MTV - MTV HITS HTV CANAL N HD - GOLPERU HD STAR CHANNEL HD WARNER CHANNEL HD FX HD - ID HD - INVESTIGATION DISCOVERY HD + INV.DISCOVERY HD COMEDY CENTRAL HD SPACE HD - PARAMOUNT HD CINECANAL HD CINEMAX HD - ESPN 4 - FOX SPORTS 2 HD - FOX SPORTS 3 HD - GOL TV HD + ESPN 5 HD + ESPN 7 HD + ESPN 6 HD DISCOVERY THEATER HD DISCOVERY HD NAT GEO HD - TRU TV HD + LATINA FASHION ONE HD - ATRES SERIES HD DISCOVERY WORLD HD HOME & HEALTH HD DISCOVERY KIDS HD DISNEY CHANNEL HD NICK HD MTV HD - MTV 00S GLOBO TV TELEFE EL TRECE - TV CHILE TELESUR - NUESTRA TELE TVE ANTENA3 DEUTSCHE WELLE @@ -122,34 +92,23 @@ TV5 NHK CCTV - ARTV VOA PLAYBOY - VENUS - SEXTREME PLAYBOY HD CONGRESO - A&E - GOLPERU - ESPN 2 HD RPP HD HBO HD - QUALITY CHANNEL BABY TV - EL GOURMET HD BBC WORLD NEWS ESPN HD ESPN 3 HD - SPACE REGIONAL DTH LIVE - GOL PERU - ESPN EXTRA HD + SPACE HD + ESPN 4 HD ANIMAL PLANET HD TNT HD - M PLUS HD + PLUS LIFE M DEPORTES HD M DEPORTES - M PLUS - NICK JR OTT CABLE NOTICIAS HBO PLUS ESTE HD HBO SIGNATURE HD @@ -158,30 +117,23 @@ NAT GEO HD TEST01 TEST02 - CANAL J - ESPN 2 - KANAL D DRAMA - ZEE MUNDO + Juntos TV + ESPN 2 HD DISCOVERY HOME & GARDEN FOOD NETWORK - USMP TV - MOVISTAR EVENTOS 2 MOVISTAR EVENTOS Food Network HD Exitosa TV Exitosa TV INFO STAR PREMIUM - AZ CORAZON VE PLUS TV HGTV - AZ MUNDO NICKELODEON DISNEY PX SPORTS FRANCE24 JUSTICIA TV EWTN - PAXTV MOVISTAR EVENTOS HD TV PERÚ 7.3 HD WILLAX TV HD @@ -189,51 +141,24 @@ HGTV HD CARTOONITO HD CANAL J HD - RUSIA TODAY HD TVN CHILE - RUSIA TODAY CLOVER UCL TOONCAST - LOLLY KIDS - QUIALITY CHANNEL + KIDS STARS CLOVER CLOVER DISNEY JUNIOR - TOONCAST - LOLLY KIDS - Alianza Lima TV - Alianza Lima TV - Universitario TV - Universitario TV UCL - UCL - EL TIEMPO - Alianza TV + Movistar Te Informa U TV TV PERU HD - CANAL J - AMC HD Star TVE HD - Pasiones HD - Film & Arts HD - Love Nature HD Via X Esports HD - Mas Chic HD - Europa Europa HD EWTN - EUROCHANNEL - RAI ITALIA - ANTENA 3 - PX SPORTS TVE - K-DRAMA - I-SAT - GLITZ HTV TV5 - WOBI - NUESTRA TELE FRANCE24 TV GLOBO TVN CHILE @@ -241,9 +166,6 @@ Telefe Internacional ATV+ BABY TV - GLOBAL TV - CONGRESO - EXITOSA TV JUSTICIA TV ADULT SWIM ENLACE TBN @@ -251,9 +173,7 @@ IPe VIVA TV La Tele - JCP-JNE SOL TV - QUALITY TV JN19 TELEJUAN NUEVO TIEMPO PAXTV @@ -282,4 +202,64 @@ YouTube Youtube Kids Star+ + ATRES SERIES HD + CLAN HD + TVLIBERTAD + DAYSTAR HD + MOVISTAR EVENTOS 2 + AMERICA TELEVISION AREQUIPA + AMERICA TELEVISION CUSCO + AMERICA TELEVISION TRUJILLO + AMERICA TELEVISION CHICLAYO + AMERICA TELEVISION PIURA + PANAMERICANA TELEVISION AREQUIPA + PANAMERICANA TELEVISION CUSCO + PANAMERICANA TELEVISION TRUJILLO + PANAMERICANA TELEVISION CHICLAYO + PANAMERICANA TELEVISION PIURA + ALACOCINA TV + TOP LATINO + LATINA NOTICIAS + PBO + BBC WORLD NEWS + PLIM PLIM + TRIVU + OVACION + JNE TV + CRISTOVISION + NICK JR + L1MAX + L1 + AMERICA TELEVISION + ANTENA 3 + ATV + ATV+ + CANAL J + CANAL N + CONGRESO + EL TIEMPO + EXITOSA TV + GLOBAL TV + PODER JUDICIAL + KANAL D DRAMA + K-DRAMA + LATINA + LOLLY KIDS + Love Nature HD + M PLUS + PANAMERICANA TELEVISION + Pasiones HD + PAXTV + PX SPORTS + RPP TV + TOONCAST + TV CHILE + TV PERU + TV PERU 7.3 + UCL + USMP TV + VENUS + WILLAX + ZEE MUNDO + RAI ITALIA diff --git a/sites/webtv.sk/__data__/content.json b/sites/webtv.sk/__data__/content.json new file mode 100644 index 000000000..817661fd5 --- /dev/null +++ b/sites/webtv.sk/__data__/content.json @@ -0,0 +1 @@ +{"id":"golf_channel","content":[{"ChannelTitle":"Golf Channel","Start":"2026-02-17T00:00:00+01:00","Stop":"2026-02-17T05:00:00+01:00","Title":"Qatar Masters","Subtitle":"","Description":"Fin\u00e1lov\u00e9 kolo turnaje DP World Tour. Doha, Qatar., (\u0161port)","Icon":"","SeriesID":"dc1ad829a6c42cde31b0f84657eb90bb","Genres":["\u0161port"],"Score":0},{"ChannelTitle":"Golf Channel","Start":"2026-02-17T05:00:00+01:00","Stop":"2026-02-17T07:00:00+01:00","Title":"The Bay vs Los Angeles","Subtitle":"","Description":"Tomorrow's Golf League. SoFi Center, Palm Beach State College, USA., (\u0161port)","Icon":"","SeriesID":"39eb4a8d024da076ce509b24c17e1a75","Genres":["\u0161port"],"Score":0},{"ChannelTitle":"Golf Channel","Start":"2026-02-17T07:00:00+01:00","Stop":"2026-02-17T12:00:00+01:00","Title":"LIV Golf Adelaide","Subtitle":"","Description":"Fin\u00e1lov\u00e9 kolo turnaje LIV Golf. The Grange Golf Club, Australia., (\u0161port)","Icon":"","SeriesID":"735549e083f0cb5495881ee0f2530f16","Genres":["\u0161port"],"Score":0},{"ChannelTitle":"Golf Channel","Start":"2026-02-17T12:00:00+01:00","Stop":"2026-02-17T12:25:00+01:00","Title":"The Golf Fix","Subtitle":"","Description":"Jak kontrolovat d\u00e9lku ran wedgemi. Kontrola vzd\u00e1lenosti je p\u0159edev\u0161\u00edm u ran wedgemi naprosto kl\u00ed\u010dov\u00fd p\u0159edpoklad golfov\u00e9ho \u00fasp\u011bchu. Zn\u00e1m\u00fd tren\u00e9r Devan Bonebrake v\u00e1m v dal\u0161\u00edm d\u00edlu po\u0159adu Golf Fix nab\u00eddne skv\u011bl\u00e9 tipy a cvi\u010den\u00ed pro zlep\u0161en\u00ed v t\u00e9to oblasti.., (\u0161port)","Icon":"\/channel\/golf_channel\/programme\/2026-02-17T12:00:00+01:00\/icon\/8793bfb65b85be90d64adfdb7163c022","SeriesID":"8793bfb65b85be90d64adfdb7163c022","Genres":["\u0161port"],"Score":0},{"ChannelTitle":"Golf Channel","Start":"2026-02-17T12:25:00+01:00","Stop":"2026-02-17T12:30:00+01:00","Title":"Cestov\u00e1n\u00ed za golfem 2025","Subtitle":"","Description":"Profesion\u00e1ln\u00ed turnaje. B\u011bhem deseti d\u00edl\u016f s\u00e9rie Cestov\u00e1n\u00ed za golfem se spole\u010dn\u011b pod\u00edv\u00e1me jak na m\u00edsta, kam golfist\u00e9 v roce 2025 cestuj\u00ed nej\u010dast\u011bji, tak zav\u00edt\u00e1me do destinac\u00ed, kter\u00e1 si o sv\u00e9 slovo teprve \u0159\u00edkaj\u00ed.., (\u0161port)","Icon":"\/channel\/golf_channel\/programme\/2026-02-17T12:25:00+01:00\/icon\/724f5af276123ee230befad2a27fa7fd","SeriesID":"724f5af276123ee230befad2a27fa7fd","Genres":["\u0161port"],"Score":0},{"ChannelTitle":"Golf Channel","Start":"2026-02-17T12:30:00+01:00","Stop":"2026-02-17T14:30:00+01:00","Title":"The Bay vs Los Angeles","Subtitle":"","Description":"(\u0161port)","Icon":"","SeriesID":"39eb4a8d024da076ce509b24c17e1a75","Genres":["\u0161port"],"Score":0},{"ChannelTitle":"Golf Channel","Start":"2026-02-17T14:30:00+01:00","Stop":"2026-02-17T15:00:00+01:00","Title":"The Golf Fix","Subtitle":"","Description":"Um\u011bn\u00ed kr\u00e1tk\u00e9 hry. Nikdo nem\u00e1 r\u00e1d tr\u00e1pen\u00ed okolo greenu. P\u0159itom dobr\u00e1 kr\u00e1tk\u00e1 hra m\u016f\u017ee v\u00fdrazn\u011b pomoci sn\u00ed\u017eit sk\u00f3re. Pokud m\u00e1te probl\u00e9my s kr\u00e1tkou hrou, \u0159e\u0161en\u00ed nab\u00edz\u00ed v dal\u0161\u00edm d\u00edlu Golf Fix pr\u016fvodce Devan Bonebrake.., (\u0161port)","Icon":"\/channel\/golf_channel\/programme\/2026-02-17T14:30:00+01:00\/icon\/8793bfb65b85be90d64adfdb7163c022","SeriesID":"8793bfb65b85be90d64adfdb7163c022","Genres":["\u0161port"],"Score":0},{"ChannelTitle":"Golf Channel","Start":"2026-02-17T15:00:00+01:00","Stop":"2026-02-17T20:00:00+01:00","Title":"LIV Golf Adelaide","Subtitle":"","Description":"Fin\u00e1lov\u00e9 kolo turnaje LIV Golf. The Grange Golf Club, Australia., (\u0161port)","Icon":"","SeriesID":"735549e083f0cb5495881ee0f2530f16","Genres":["\u0161port"],"Score":0},{"ChannelTitle":"Golf Channel","Start":"2026-02-17T20:00:00+01:00","Stop":"2026-02-17T21:00:00+01:00","Title":"Sn\u011bhul\u00e1k","Subtitle":"","Description":"Romana Pavelkov\u00e1, (\u0161port)","Icon":"\/channel\/golf_channel\/programme\/2026-02-17T20:00:00+01:00\/icon\/782cd01f689e432d695a5c482dcbeee0","SeriesID":"782cd01f689e432d695a5c482dcbeee0","Genres":["\u0161port"],"Score":0},{"ChannelTitle":"Golf Channel","Start":"2026-02-17T21:00:00+01:00","Stop":"2026-02-17T21:30:00+01:00","Title":"The Golf Fix","Subtitle":"","Description":"Jak potopit v\u00edc putt\u016f. M\u00edjet kr\u00e1tk\u00e9 putty m\u016f\u017ee znamenat pro celkov\u00e9 sk\u00f3re pohromu. Devan Bonebrake v\u00e1s v dal\u0161\u00edm pokra\u010dov\u00e1n\u00ed po\u0159adu Golf Fix provede v\u0161emi z\u00e1ludnostmi spojen\u00fdmi s puttov\u00e1n\u00edm.., (\u0161port)","Icon":"\/channel\/golf_channel\/programme\/2026-02-17T21:00:00+01:00\/icon\/8793bfb65b85be90d64adfdb7163c022","SeriesID":"8793bfb65b85be90d64adfdb7163c022","Genres":["\u0161port"],"Score":0},{"ChannelTitle":"Golf Channel","Start":"2026-02-17T21:30:00+01:00","Stop":"2026-02-17T23:30:00+01:00","Title":"The Bay vs Los Angeles","Subtitle":"","Description":"(\u0161port)","Icon":"","SeriesID":"39eb4a8d024da076ce509b24c17e1a75","Genres":["\u0161port"],"Score":0},{"ChannelTitle":"Golf Channel","Start":"2026-02-17T23:30:00+01:00","Stop":"2026-02-18T00:00:00+01:00","Title":"The Golf Fix","Subtitle":"","Description":"Jak v golfu pracovat se spinem. Spin neboli zp\u011btn\u00e1 rotace m\u00ed\u010dku je jedn\u00edm z nejd\u016fle\u017eit\u011bj\u0161\u00edch faktor\u016f v golfu. Jak s n\u00edm pracovat a jak ho m\u00edt pod kontrolou v\u00e1s v dal\u0161\u00edm pokra\u010dov\u00e1n\u00ed po\u0159adu Golf Fix nau\u010d\u00ed Devan Bonebrake.., (\u0161port)","Icon":"\/channel\/golf_channel\/programme\/2026-02-17T23:30:00+01:00\/icon\/8793bfb65b85be90d64adfdb7163c022","SeriesID":"8793bfb65b85be90d64adfdb7163c022","Genres":["\u0161port"],"Score":0}]} \ No newline at end of file diff --git a/sites/webtv.sk/readme.md b/sites/webtv.sk/readme.md new file mode 100644 index 000000000..70cd24252 --- /dev/null +++ b/sites/webtv.sk/readme.md @@ -0,0 +1,21 @@ +# webtv.sk + +https://webtv.sk/archive + +### Download the guide + +```sh +npm run grab --- --site=webtv.sk +``` + +### Update channel list + +```sh +npm run channels:parse --- --config=./sites/webtv.sk/webtv.sk.config.js --output=./sites/webtv.sk/webtv.sk.channels.xml +``` + +### Test + +```sh +npm test --- webtv.sk +``` diff --git a/sites/webtv.sk/webtv.sk.channels.xml b/sites/webtv.sk/webtv.sk.channels.xml new file mode 100644 index 000000000..058822546 --- /dev/null +++ b/sites/webtv.sk/webtv.sk.channels.xml @@ -0,0 +1,189 @@ + + + 360 TuneBox + Al Jazeera + Al Jazeera English + AMC + Animal Planet + Arena Sport 1 + Arena Sport 2 + Arirang TV + Armenia 1 TV + ATV Žiar + Auto Motor und Sport + AXN + AXN Black + AXN White + Barrandov + Barrandov Krimi + BTV + CETV + CGTN + CGTN Documentary + Cinemax + Cinemax 2 + HaHa TV + Crime & Investigation + ČSRTV + ČT1 + ČT2 + ČT24 + ČT :D - ČT art + Current Time + DajTo + Discovery + Disney Channel + Disney Junior + DocuBox + Doktor TV + Doma + Duck TV + Duck TV Plus + eClutch 1 + eClutch 2 + English Club TV + Espreso TV + Euronews + Eurosport 1 + Eurosport 2 + Fashion TV Czech & Slovak + FashionBox + Fast and Funbox + FightBox + Film + + FilmBox + Filmbox Arthouse + FilmBox Extra + FilmBox Family + FilmBox Stars + FilmBox Premium + Fishing&Hunting + Folklorika + Food Network + France 24 + France 24 English + Golf Channel + GTTV + HBO + HBO 2 + HBO 3 + HGTV + History 2 + History Channel + HN TV + Antik Info TV + ID + JimJam + JOJ + JOJ 24 + JOJ Cinema + JOJ Cinema +1 + JOJ Plus + JOJ +1 + JOJ Šport + JOJ Šport 2 + JOJ Svet + Kino Barrandov + Kitchen TV + LALA TV + LangLab + Life TV + Lingo Toons + TV LocAll + Love Nature + TV LUX + Markíza + Markíza Klasik + Markíza Krimi + Markíza +1 + Mestská Televízia Trnava + Mestská Tv Košice + Mestská Televízia Partizánske + Miestna TV Trebišov + Minimax + TV Mistral + Moldava TV + National Geographic Channel + National Geographic Wild + NHK World Japan + Nick Jr + Nickelodeon + Nicktoons + NOE + Nova International + Nova Sport 1 + Nova Sport 2 + tv NRSR + Óčko + Óčko Black + Óčko Expres + Óčko Star + Padel Time + PARK TV + Rebel + Relax + Polar TV + Prima Cool SK + Prima Krimi SK + Prima Love SK + CNN Prima News + Prima SK + Rai Tre + Rai Uno + Régió TV + A11 + Retro Music Television + RiK TV + Jojko + RVTV + Senzi + Seznam.cz TV + Skvelé TV + Sky News + PREMIUM + Šláger Muzika + Šláger Originál + Spektrum + Spektrum Home + Sport 1 + Sport 2 + STINGRAY iConcerts + Jednotka + Dvojka + :24 + :Šport + SuperYacht TV + TA3 + Tanečné súťaže + TLC + Travel Channel + TRT World + Turbo TV + TV7 + TV 9 + TV8 + TV Bratislava + TV Nitrička + TV Nové Zámky + TV Panoráma + TV Paprika + TV Piánko + TV Poprad + TV Považie + TV RAJ + TV Reduta + TV Romana + TV Ružinov + TV SEN + TV Kežmarok + Tv Vega + Godzone TV + TV Zemplín + TVP Info + TVP Polonia + TVP World + UA TV + Kanal1 + WAU + Televízia Slovensko + diff --git a/sites/webtv.sk/webtv.sk.config.js b/sites/webtv.sk/webtv.sk.config.js new file mode 100644 index 000000000..dc7045a28 --- /dev/null +++ b/sites/webtv.sk/webtv.sk.config.js @@ -0,0 +1,74 @@ +const axios = require('axios') +const dayjs = require('dayjs') +const utc = require('dayjs/plugin/utc') + +dayjs.extend(utc) + +module.exports = { + site: 'webtv.sk', + days: 2, + delay: 1000, + url: 'https://api.webtv.sk/epg/channel', + request: { + method: 'POST', + headers: { + 'content-type': 'application/json' + }, + data({ channel, date }) { + return { + channel_id: channel.site_id, + date: dayjs.utc(date).toJSON() + } + } + }, + parser({ content }) { + return parseItems(content).map(item => { + return { + title: item.Title, + subtitle: item.Subtitle, + description: item.Description, + categories: item.Genres, + start: dayjs(item.Start), + stop: dayjs(item.Stop) + } + }) + }, + async channels() { + const data = await axios + .post( + 'https://api.webtv.sk/channels', + { type: 'TV', channels_content: null }, + { + headers: { + 'content-type': 'application/json' + } + } + ) + .then(r => r.data) + .catch(console.error) + + let channels = [] + for (const site_id in data.data) { + const channel = data.data[site_id] + + channels.push({ + site_id, + name: channel.name, + lang: 'sk' + }) + } + + return channels + } +} + +function parseItems(content) { + try { + const data = JSON.parse(content) + if (!data || !Array.isArray(data.content)) return [] + + return data.content + } catch { + return [] + } +} diff --git a/sites/webtv.sk/webtv.sk.test.js b/sites/webtv.sk/webtv.sk.test.js new file mode 100644 index 000000000..b3b42d56a --- /dev/null +++ b/sites/webtv.sk/webtv.sk.test.js @@ -0,0 +1,68 @@ +const { parser, url, request } = require('./webtv.sk.config.js') +const fs = require('fs') +const path = require('path') +const dayjs = require('dayjs') +const utc = require('dayjs/plugin/utc') +const customParseFormat = require('dayjs/plugin/customParseFormat') +dayjs.extend(customParseFormat) +dayjs.extend(utc) + +const date = dayjs.utc('2026-02-17', 'YYYY-MM-DD').startOf('d') +const channel = { site_id: 'golf_channel' } + +it('can generate valid url', () => { + expect(url).toBe('https://api.webtv.sk/epg/channel') +}) + +it('can generate valid request method', () => { + expect(request.method).toBe('POST') +}) + +it('can generate valid request headers', () => { + expect(request.headers).toMatchObject({ + 'content-type': 'application/json' + }) +}) + +it('can generate valid request data', () => { + expect(request.data({ channel, date })).toMatchObject({ + channel_id: 'golf_channel', + date: '2026-02-17T00:00:00.000Z' + }) +}) + +it('can parse response', () => { + const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json')) + + const results = parser({ content }).map(p => { + p.start = p.start.toJSON() + p.stop = p.stop.toJSON() + + return p + }) + + expect(results.length).toBe(12) + expect(results[0]).toMatchObject({ + title: 'Qatar Masters', + subtitle: '', + description: 'Finálové kolo turnaje DP World Tour. Doha, Qatar., (šport)', + categories: ['šport'], + start: '2026-02-16T23:00:00.000Z', + stop: '2026-02-17T04:00:00.000Z' + }) + expect(results[11]).toMatchObject({ + title: 'The Golf Fix', + subtitle: '', + description: + 'Jak v golfu pracovat se spinem. Spin neboli zpětná rotace míčku je jedním z nejdůležitějších faktorů v golfu. Jak s ním pracovat a jak ho mít pod kontrolou vás v dalším pokračování pořadu Golf Fix naučí Devan Bonebrake.., (šport)', + categories: ['šport'], + start: '2026-02-17T22:30:00.000Z', + stop: '2026-02-17T23:00:00.000Z' + }) +}) + +it('can handle empty guide', () => { + const results = parser({ content: '' }) + + expect(results).toMatchObject([]) +}) diff --git a/tests/__data__/expected/guides_update/GUIDES.md b/tests/__data__/expected/guides_update/GUIDES.md new file mode 100644 index 000000000..f6e86cfc8 --- /dev/null +++ b/tests/__data__/expected/guides_update/GUIDES.md @@ -0,0 +1,13 @@ +# Guides + + + + + + + + + +
HostStatusChannelsLast UpdatedLinks
example.com🔴0-
worker-9dd4.onrender.com🟢1a day agochannels.xml
guide.xml
+ +[How can I add my server to the list?](CONTRIBUTING.md#how-to-add-my-server-to-the-guides-md) diff --git a/tests/__data__/input/guides_update/channels.xml b/tests/__data__/input/guides_update/channels.xml new file mode 100644 index 000000000..46c1b2e93 --- /dev/null +++ b/tests/__data__/input/guides_update/channels.xml @@ -0,0 +1,4 @@ + + + ANT1 Europe + \ No newline at end of file diff --git a/tests/__data__/input/guides_update/guide.xml b/tests/__data__/input/guides_update/guide.xml new file mode 100644 index 000000000..ed17438ed --- /dev/null +++ b/tests/__data__/input/guides_update/guide.xml @@ -0,0 +1,30 @@ + +ANT1 Europehttps://antennaeurope.gr +ΚΑΛΗΜΕΡΑ ΕΛΛΑΔΑ +ΤΟ ΠΡΩΙΝΟ +ANT1 NEWS +ΑΠΟΚΑΛΥΨΕΙΣ +ΡΟΥΚ ΖΟΥΚ +5 X 5 +ANT1 NEWS +ΠΟΙΟΣ ΘΕΛΕΙ ΝΑ ΓΙΝΕΙ ΕΚΑΤΟΜΜΥΡΙΟΥΧΟΣ +GRAND HOTEL +ΓΙΑΤΙ ΡΕ ΠΑΤΕΡΑ ; +THE ROADSHOW - BEST OF +ΕΝΩΠΙΟΣ ΕΝΩΠΙΩ +5 X 5 +ΤΟ ΠΡΩΙΝΟ +ΚΑΛΗΜΕΡΑ ΕΛΛΑΔΑ +ΤΟ ΠΡΩΙΝΟ +ANT1 NEWS +ΑΠΟΚΑΛΥΨΕΙΣ +ΡΟΥΚ ΖΟΥΚ +5 X 5 +ANT1 NEWS +ΠΟΙΟΣ ΘΕΛΕΙ ΝΑ ΓΙΝΕΙ ΕΚΑΤΟΜΜΥΡΙΟΥΧΟΣ +DON'T FORGET THE LYRICS +VIΠ +ΠΕΦΤΕΙ Η ΝΥΧΤΑ ΜΕ... ΡΥΘΜΟ +5 X 5 +ΤΟ ΠΡΩΙΝΟ + \ No newline at end of file diff --git a/tests/__data__/input/guides_update/worker.json b/tests/__data__/input/guides_update/worker.json new file mode 100644 index 000000000..371feec07 --- /dev/null +++ b/tests/__data__/input/guides_update/worker.json @@ -0,0 +1,4 @@ +{ + "channels": "channels.xml", + "guide": "guide.xml" +} \ No newline at end of file diff --git a/tests/__data__/input/guides_update/workers.txt b/tests/__data__/input/guides_update/workers.txt new file mode 100644 index 000000000..02ca158a1 --- /dev/null +++ b/tests/__data__/input/guides_update/workers.txt @@ -0,0 +1,2 @@ +example.com +worker-9dd4.onrender.com \ No newline at end of file diff --git a/tests/commands/guides/update.test.ts b/tests/commands/guides/update.test.ts new file mode 100644 index 000000000..4f3d0f6ef --- /dev/null +++ b/tests/commands/guides/update.test.ts @@ -0,0 +1,29 @@ +import { execSync } from 'child_process' +import { pathToFileURL } from 'node:url' +import fs from 'fs-extra' + +const ENV_VAR = 'cross-env ROOT_DIR=tests/__data__/output' + +beforeEach(() => { + fs.emptyDirSync('tests/__data__/output') + fs.copySync('tests/__data__/input/guides_update/workers.txt', 'tests/__data__/output/workers.txt') +}) + +it('can update GUIDES.md', () => { + const cmd = `${ENV_VAR} npm run guides:update` + + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + + expect(content('tests/__data__/output/GUIDES.md')).toEqual( + content('tests/__data__/expected/guides_update/GUIDES.md') + ) +}) + +function content(filepath: string) { + const data = fs.readFileSync(pathToFileURL(filepath), { + encoding: 'utf8' + }) + + return JSON.stringify(data) +} diff --git a/workers.txt b/workers.txt new file mode 100644 index 000000000..d43ab81c3 --- /dev/null +++ b/workers.txt @@ -0,0 +1 @@ +worker-9dd4.onrender.com \ No newline at end of file