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/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 67329f2c2..273068e4e 100644 --- a/pm2.config.js +++ b/pm2.config.js @@ -32,4 +32,4 @@ if (process.env.RUN_AT_STARTUP === 'true') { }) } -module.exports = { apps } \ No newline at end of file +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/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