Merge pull request #3005 from iptv-org/patch-2026.02.1

Patch 2026.02.1
This commit is contained in:
Ismaël Moret authored and GitHub committed 2026-02-19 16:27:01 +01:00
commit 6bdbc369b0
18 files changed
+704 -279

No files matched your search

+17 -6
View File
@@ -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
+24
View File
@@ -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.
<details>
<summary>worker.json</summary>
<br>
```json
{
"channels": "path/to/channels.xml",
"guide": "path/to/guide.xml"
}
```
</details>
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 <script-name>` 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.
+12
View File
@@ -0,0 +1,12 @@
# Guides
<table>
<thead>
<tr><th align="left">Host</th><th align="left">Status</th><th align="left">Channels</th><th align="left">Last Updated</th><th align="left">Links</th></tr>
</thead>
<tbody>
<tr><td>worker-9dd4.onrender.com</td><td align="center">🟢</td><td align="right">1</td><td align="left">a day ago</td><td><a href="https://worker-9dd4.onrender.com/channels.xml">channels.xml</a><br><a href="https://worker-9dd4.onrender.com/guide.xml">guide.xml</a></td></tr>
</tbody>
</table>
[How can I add my server to the list?](CONTRIBUTING.md#how-to-add-my-server-to-the-guides-md)
+7 -4
View File
@@ -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)
+300 -233
View File
@@ -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",
+3 -1
View File
@@ -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",
+35 -35
View File
@@ -1,35 +1,35 @@
const grab = process.env.SITE
? `npm run grab -- --site=${process.env.SITE} ${process.env.CLANG ? `--lang=${process.env.CLANG}` : ''
} --output=public/guide.xml`
: 'npm run grab -- --channels=channels.xml --output=public/guide.xml'
const apps = [
{
name: 'serve',
script: 'npx serve -- public',
instances: 1,
watch: false,
autorestart: true
},
{
name: 'grab',
script: `npx chronos -e "${grab}" -p "${process.env.CRON_SCHEDULE}" -l`,
instances: 1,
watch: false,
autorestart: true
}
];
if (process.env.RUN_AT_STARTUP === 'true') {
apps.push({
name: 'grab-at-startup',
script: grab,
instances: 1,
autorestart: false,
watch: false,
max_restarts: 1
});
}
module.exports = { apps };
const grab = process.env.SITE
? `npm run grab -- --site=${process.env.SITE} ${
process.env.CLANG ? `--lang=${process.env.CLANG}` : ''
} --output=public/guide.xml`
: 'npm run grab -- --channels=public/channels.xml --output=public/guide.xml'
const apps = [
{
name: 'serve',
script: 'npx serve -- public',
instances: 1,
watch: false,
autorestart: true
},
{
name: 'grab',
script: `npx chronos -e "${grab}" -p "${process.env.CRON_SCHEDULE}" -l`,
instances: 1,
watch: false,
autorestart: true
}
]
if (process.env.RUN_AT_STARTUP === 'true') {
apps.push({
name: 'grab-at-startup',
script: grab,
instances: 1,
autorestart: false,
watch: false,
max_restarts: 1
})
}
module.exports = { apps }
+144
View File
@@ -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<string, Worker>()
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<HTMLTableRow>()
workers.forEach((worker: Worker) => {
rows.add(
new Collection<HTMLTableDataItem>([
{ value: worker.host },
{ value: worker.getStatusEmoji(), align: 'center' },
{ value: worker.getChannelsCount().toString(), align: 'right' },
{ value: worker.getLastUpdated(), align: 'left' },
{
value:
worker.status === 'OK'
? `<a href="${worker.getChannelsUrl()}">${worker.channelsPath}</a><br><a href="${worker.getGuideUrl()}">${worker.guidePath}</a>`
: ''
}
])
)
})
logger.info('updating guides.md...')
const table = new HTMLTable(
rows,
new Collection<HTMLTableColumn>([
{ 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()
+1
View File
@@ -3,3 +3,4 @@ export * from './issue'
export * from './site'
export * from './channel'
export * from './program'
export * from './worker'
+73
View File
@@ -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<Channel>
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)
}
}
+5
View File
@@ -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)
@@ -0,0 +1,13 @@
# Guides
<table>
<thead>
<tr><th align="left">Host</th><th align="left">Status</th><th align="left">Channels</th><th align="left">Last Updated</th><th align="left">Links</th></tr>
</thead>
<tbody>
<tr><td>example.com</td><td align="center">🔴</td><td align="right">0</td><td align="left">-</td><td></td></tr>
<tr><td>worker-9dd4.onrender.com</td><td align="center">🟢</td><td align="right">1</td><td align="left">a day ago</td><td><a href="https://worker-9dd4.onrender.com/channels.xml">channels.xml</a><br><a href="https://worker-9dd4.onrender.com/guide.xml">guide.xml</a></td></tr>
</tbody>
</table>
[How can I add my server to the list?](CONTRIBUTING.md#how-to-add-my-server-to-the-guides-md)
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<channels>
<channel site="antennaeurope.gr" site_id="#" lang="el" xmltv_id="ANT1Europe.gr@SD">ANT1 Europe</channel>
</channels>
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8" ?><tv date="20260212">
<channel id="ANT1Europe.gr@SD"><display-name>ANT1 Europe</display-name><icon src="https://i.imgur.com/DTfwJGM.png"/><url>https://antennaeurope.gr</url></channel>
<programme start="20260212040000 +0000" stop="20260212070000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">ΚΑΛΗΜΕΡΑ ΕΛΛΑΔΑ</title></programme>
<programme start="20260212070000 +0000" stop="20260212103000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">ΤΟ ΠΡΩΙΝΟ</title></programme>
<programme start="20260212103000 +0000" stop="20260212113000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">ANT1 NEWS</title></programme>
<programme start="20260212113000 +0000" stop="20260212141500 +0000" channel="ANT1Europe.gr@SD"><title lang="el">ΑΠΟΚΑΛΥΨΕΙΣ</title></programme>
<programme start="20260212141500 +0000" stop="20260212153000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">ΡΟΥΚ ΖΟΥΚ</title></programme>
<programme start="20260212153000 +0000" stop="20260212164500 +0000" channel="ANT1Europe.gr@SD"><title lang="el">5 X 5</title></programme>
<programme start="20260212164500 +0000" stop="20260212180000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">ANT1 NEWS</title></programme>
<programme start="20260212180000 +0000" stop="20260212190000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">ΠΟΙΟΣ ΘΕΛΕΙ ΝΑ ΓΙΝΕΙ ΕΚΑΤΟΜΜΥΡΙΟΥΧΟΣ</title></programme>
<programme start="20260212190000 +0000" stop="20260212200000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">GRAND HOTEL</title></programme>
<programme start="20260212200000 +0000" stop="20260212210000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">ΓΙΑΤΙ ΡΕ ΠΑΤΕΡΑ ;</title></programme>
<programme start="20260212210000 +0000" stop="20260212220000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">THE ROADSHOW - BEST OF</title></programme>
<programme start="20260212220000 +0000" stop="20260213000000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">ΕΝΩΠΙΟΣ ΕΝΩΠΙΩ</title></programme>
<programme start="20260213000000 +0000" stop="20260213010000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">5 X 5</title></programme>
<programme start="20260213010000 +0000" stop="20260213013000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">ΤΟ ΠΡΩΙΝΟ</title></programme>
<programme start="20260213040000 +0000" stop="20260213070000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">ΚΑΛΗΜΕΡΑ ΕΛΛΑΔΑ</title></programme>
<programme start="20260213070000 +0000" stop="20260213103000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">ΤΟ ΠΡΩΙΝΟ</title></programme>
<programme start="20260213103000 +0000" stop="20260213113000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">ANT1 NEWS</title></programme>
<programme start="20260213113000 +0000" stop="20260213141500 +0000" channel="ANT1Europe.gr@SD"><title lang="el">ΑΠΟΚΑΛΥΨΕΙΣ</title></programme>
<programme start="20260213141500 +0000" stop="20260213153000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">ΡΟΥΚ ΖΟΥΚ</title></programme>
<programme start="20260213153000 +0000" stop="20260213164500 +0000" channel="ANT1Europe.gr@SD"><title lang="el">5 X 5</title></programme>
<programme start="20260213164500 +0000" stop="20260213180000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">ANT1 NEWS</title></programme>
<programme start="20260213180000 +0000" stop="20260213190000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">ΠΟΙΟΣ ΘΕΛΕΙ ΝΑ ΓΙΝΕΙ ΕΚΑΤΟΜΜΥΡΙΟΥΧΟΣ</title></programme>
<programme start="20260213190000 +0000" stop="20260213200000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">DON&apos;T FORGET THE LYRICS</title></programme>
<programme start="20260213200000 +0000" stop="20260213220000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">VIΠ</title></programme>
<programme start="20260213220000 +0000" stop="20260214000000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">ΠΕΦΤΕΙ Η ΝΥΧΤΑ ΜΕ... ΡΥΘΜΟ</title></programme>
<programme start="20260214000000 +0000" stop="20260214010000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">5 X 5</title></programme>
<programme start="20260214010000 +0000" stop="20260214013000 +0000" channel="ANT1Europe.gr@SD"><title lang="el">ΤΟ ΠΡΩΙΝΟ</title></programme>
</tv>
@@ -0,0 +1,4 @@
{
"channels": "channels.xml",
"guide": "guide.xml"
}
@@ -0,0 +1,2 @@
example.com
worker-9dd4.onrender.com
+29
View File
@@ -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)
}
+1
View File
@@ -0,0 +1 @@
worker-9dd4.onrender.com