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
+
+
+
+ | Host | Status | Channels | Last Updated | Links |
+
+
+ | worker-9dd4.onrender.com | 🟢 | 1 | a day ago | channels.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
[](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
+
+
+
+ | Host | Status | Channels | Last Updated | Links |
+
+
+ | example.com | 🔴 | 0 | - | |
+ | worker-9dd4.onrender.com | 🟢 | 1 | a day ago | channels.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