diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 610e70d2..c8c72d5c 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -1,53 +1,56 @@ -name: check -on: - workflow_dispatch: - pull_request: - types: [opened, synchronize, reopened, edited] - branches: - - master -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: true -jobs: - check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 2 - - name: changed files - id: files - run: | - git fetch origin master:master - JS_ANY_CHANGED=false - JS_ALL_CHANGED_FILES=$(git diff --name-only master -- tests/**/*.js tests/**/*.ts scripts/**/*.js scripts/**/*.mts scripts/**/*.ts sites/**/*.js sites/**/*.ts | tr '\n' ' ') - if [ -n "${JS_ALL_CHANGED_FILES}" ]; then - JS_ANY_CHANGED=true - fi - echo "js_all_changed_files=$JS_ALL_CHANGED_FILES" >> "$GITHUB_OUTPUT" - echo "js_any_changed=$JS_ANY_CHANGED" >> "$GITHUB_OUTPUT" - CHANNELS_ANY_CHANGED=false - CHANNELS_ALL_CHANGED_FILES=$(git diff --name-only master -- sites/**/*.channels.xml | tr '\n' ' ') - if [ -n "${CHANNELS_ALL_CHANGED_FILES}" ]; then - CHANNELS_ANY_CHANGED=true - fi - echo "channels_all_changed_files=$CHANNELS_ALL_CHANGED_FILES" >> "$GITHUB_OUTPUT" - echo "channels_any_changed=$CHANNELS_ANY_CHANGED" >> "$GITHUB_OUTPUT" - - uses: actions/setup-node@v4 - if: ${{ steps.files.outputs.js_any_changed == 'true' || steps.files.outputs.channels_any_changed == 'true' }} - with: - node-version: 22 - cache: 'npm' - - name: install dependencies - if: steps.files.outputs.js_any_changed == 'true' || steps.files.outputs.channels_any_changed == 'true' - run: SKIP_POSTINSTALL=1 npm install - - name: check changed js-files - if: steps.files.outputs.js_any_changed == 'true' - run: | - npx eslint ${{ steps.files.outputs.js_all_changed_files }} - - name: check changed *.channels.xml - if: steps.files.outputs.channels_any_changed == 'true' - run: | - npm run channels:lint -- ${{ steps.files.outputs.channels_all_changed_files }} - npm run postinstall +name: check +on: + workflow_dispatch: + pull_request: + types: [opened, synchronize, reopened] + branches: + - master +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true +permissions: + packages: read +jobs: + main: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Get list of changed files + id: files + run: | + git fetch origin master:master + ANY_CHANGED=false + JS_ANY_CHANGED=false + JS_ALL_CHANGED_FILES=$(git diff --diff-filter=ACMRT --name-only master -- tests/**/*.js tests/**/*.ts scripts/**/*.js scripts/**/*.mts scripts/**/*.ts sites/**/*.js sites/**/*.ts | tr '\n' ' ') + if [ -n "${JS_ALL_CHANGED_FILES}" ]; then + JS_ANY_CHANGED=true + ANY_CHANGED=true + fi + echo "js_all_changed_files=$JS_ALL_CHANGED_FILES" >> "$GITHUB_OUTPUT" + echo "js_any_changed=$JS_ANY_CHANGED" >> "$GITHUB_OUTPUT" + CHANNELS_ANY_CHANGED=false + CHANNELS_ALL_CHANGED_FILES=$(git diff --diff-filter=ACMRT --name-only master -- sites/**/*.channels.xml | tr '\n' ' ') + if [ -n "${CHANNELS_ALL_CHANGED_FILES}" ]; then + CHANNELS_ANY_CHANGED=true + ANY_CHANGED=true + fi + echo "channels_all_changed_files=$CHANNELS_ALL_CHANGED_FILES" >> "$GITHUB_OUTPUT" + echo "channels_any_changed=$CHANNELS_ANY_CHANGED" >> "$GITHUB_OUTPUT" + echo "any_changed=$ANY_CHANGED" >> "$GITHUB_OUTPUT" + - uses: actions/setup-node@v4 + if: steps.files.outputs.any_changed == 'true' + with: + node-version: 22 + cache: 'npm' + - name: Install dependencies + if: steps.files.outputs.any_changed == 'true' + run: SKIP_POSTINSTALL=1 npm install + - name: Check changed js-files + if: steps.files.outputs.js_any_changed == 'true' + run: npx eslint ${{ steps.files.outputs.js_all_changed_files }} + - name: Check changed *.channels.xml files + if: steps.files.outputs.channels_any_changed == 'true' + run: | + npm run postinstall + npm run channels:lint -- ${{ steps.files.outputs.channels_all_changed_files }} npm run channels:validate -- ${{ steps.files.outputs.channels_all_changed_files }} \ No newline at end of file diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml new file mode 100644 index 00000000..d1ad75ea --- /dev/null +++ b/.github/workflows/format.yml @@ -0,0 +1,59 @@ +name: format +on: + workflow_dispatch: + # schedule: + # - cron: "0 12 * * *" +permissions: + packages: read +jobs: + main: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: tibdex/github-app-token@v1.8.2 + if: ${{ !env.ACT }} + id: create-app-token + with: + app_id: ${{ secrets.APP_ID }} + private_key: ${{ secrets.APP_PRIVATE_KEY }} + - uses: actions/checkout@v4 + if: ${{ !env.ACT }} + with: + token: ${{ steps.create-app-token.outputs.token }} + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'npm' + - name: Install dependencies + run: npm install + - name: Format *.channels.xml files + run: npm run channels:format + - name: Get list of changed *.channels.xml files + id: files + run: | + git fetch origin master:master + CHANNELS_ANY_CHANGED=false + CHANNELS_ALL_CHANGED_FILES=$(git diff --diff-filter=ACMRT --name-only master -- sites/**/*.channels.xml | tr '\n' ' ') + if [ -n "${CHANNELS_ALL_CHANGED_FILES}" ]; then + CHANNELS_ANY_CHANGED=true + fi + echo "channels_all_changed_files=$CHANNELS_ALL_CHANGED_FILES" >> "$GITHUB_OUTPUT" + echo "channels_any_changed=$CHANNELS_ANY_CHANGED" >> "$GITHUB_OUTPUT" + - name: Check changed *.channels.xml files + if: steps.files.outputs.channels_any_changed == 'true' + run: | + npm run channels:lint -- ${{ steps.files.outputs.channels_all_changed_files }} + npm run channels:validate -- ${{ steps.files.outputs.channels_all_changed_files }} + - name: Setup git + run: | + git config user.name "iptv-bot[bot]" + git config user.email "84861620+iptv-bot[bot]@users.noreply.github.com" + - name: Commit changed *.channels.xml files + if: steps.files_after.outputs.channels_any_changed == 'true' + run: | + git add streams + git status + git commit -m "[Bot] Format *.channels.xml files" -m "Committed by [iptv-bot](https://github.com/apps/iptv-bot) via [format](https://github.com/iptv-org/epg/actions/runs/${{ github.run_id }}) workflow." --no-verify + - name: Push all changes to the repository + if: ${{ !env.ACT && github.ref == 'refs/heads/master' && steps.files_after.outputs.channels_any_changed == 'true' }} + run: git push \ No newline at end of file diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml index 9975722d..4015aa19 100644 --- a/.github/workflows/update.yml +++ b/.github/workflows/update.yml @@ -1,61 +1,60 @@ -name: update -on: - workflow_dispatch: - schedule: - - cron: '0 0 * * *' -jobs: - main: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: tibdex/github-app-token@v1.8.2 - if: ${{ !env.ACT }} - id: create-app-token - with: - app_id: ${{ secrets.APP_ID }} - private_key: ${{ secrets.APP_PRIVATE_KEY }} - - uses: actions/checkout@v4 - if: ${{ !env.ACT }} - with: - token: ${{ steps.create-app-token.outputs.token }} - - name: setup git - run: | - git config user.name "iptv-bot[bot]" - git config user.email "84861620+iptv-bot[bot]@users.noreply.github.com" - - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: 'npm' - - name: install dependencies - run: npm install - - name: check *.channels.xml - run: | - npm run channels:lint - - name: update sites.md - run: npm run sites:update - - 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}) - 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 push - fi - - name: generate .api/guides.json - run: npm run api:generate - - run: git status - - name: move .api/guides.json to iptv-org/api - uses: JamesIves/github-pages-deploy-action@4.1.1 - if: ${{ !env.ACT && github.ref == 'refs/heads/master' }} - with: - repository-name: iptv-org/api - branch: gh-pages - folder: .api - token: ${{ steps.create-app-token.outputs.token }} - git-config-name: iptv-bot[bot] - git-config-email: 84861620+iptv-bot[bot]@users.noreply.github.com - commit-message: '[Bot] Deploy to iptv-org/api' +name: update +on: + workflow_dispatch: + schedule: + - cron: '0 0 * * *' +jobs: + main: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: tibdex/github-app-token@v1.8.2 + if: ${{ !env.ACT }} + id: create-app-token + with: + app_id: ${{ secrets.APP_ID }} + private_key: ${{ secrets.APP_PRIVATE_KEY }} + - uses: actions/checkout@v4 + if: ${{ !env.ACT }} + with: + token: ${{ steps.create-app-token.outputs.token }} + - name: setup git + run: | + git config user.name "iptv-bot[bot]" + git config user.email "84861620+iptv-bot[bot]@users.noreply.github.com" + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: 'npm' + - name: install dependencies + run: npm install + - name: check *.channels.xml + run: npm run channels:lint + - name: update sites.md + run: npm run sites:update + - 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}) + 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 push + fi + - name: generate .api/guides.json + run: npm run api:generate + - run: git status + - name: move .api/guides.json to iptv-org/api + uses: JamesIves/github-pages-deploy-action@4.1.1 + if: ${{ !env.ACT && github.ref == 'refs/heads/master' }} + with: + repository-name: iptv-org/api + branch: gh-pages + folder: .api + token: ${{ steps.create-app-token.outputs.token }} + git-config-name: iptv-bot[bot] + git-config-email: 84861620+iptv-bot[bot]@users.noreply.github.com + commit-message: '[Bot] Deploy to iptv-org/api' clean: false \ No newline at end of file diff --git a/.gitignore b/.gitignore index 62054de4..cabef157 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,9 @@ -/node_modules/ -/temp/ -/channels.xml -/guide.xml -/guide.xml.gz - -# macOS +/node_modules/ +/temp/ +/channels.xml +/guide.xml +/guide.xml.gz +.secrets + +# macOS .DS_Store \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 96797d64..f5e9586b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,437 +1,438 @@ -# Contributing Guide - -- [How to?](#how-to) -- [Project Structure](#project-structure) -- [Scripts](#scripts) - -## How to? - -### How to add a channel to the guide? - -To ask for help with adding a channel simply fill out this [form](https://github.com/iptv-org/epg/issues/new?assignees=&labels=channel+request&projects=&template=2_channel-request.yml). - -If you want to add a channel to the list yourself, here are the instructions on how to do it. - -First select the site from [SITES.md](SITES.md) that you know has a guide for the channel you need. Then go to the folder with its config and open the file `*.channels.xml`. - -Make sure that the desired channel is not already in the list. If it is not, simply add its description to the end of the list as shown here: - -```xml - - - ... - CHANNEL_NAME - -``` - -| Attribute | Description | Example | -| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | -| SITE | Site domain name. | `example.com` | -| LANGUAGE_CODE | Language of the guide ([ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) code). | `en` | -| CHANNEL_ID | ID of the channel. Full list of supported channels with corresponding ID could be found on [iptv-org.github.io](https://iptv-org.github.io/). | `HBO.us@East` | -| SITE_ID | Unique ID of the channel used in the source. | `hbo` | -| CHANNEL_NAME | Name of the channel used in the source. | `HBO East` | - -After that just [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 report broken guide? - -If you start to get errors when downloading the guide or if nothing loads at all, please let us know via this [form](https://github.com/iptv-org/epg/issues/new?assignees=&labels=broken+guide&projects=&template=3_broken-guide.yml). - -### How to add a new source to the repository? - -If you are not familiar with javascript programming, you can ask for help from other community members through this [form](https://github.com/iptv-org/epg/issues/new?assignees=&labels=source+request&projects=&template=1_source-request.yml). Otherwise, below are the instructions for you. - -To start with, you need to create a new folder in the [/sites](/sites) folder and put at least 4 files in it: - -
-example.com.config.js -
- -This file describes what kind of request we need to send to get the guide for a particular channel on a certain date and how to parse the response. - -```js -module.exports = { - site: 'example.com', - url({ channel, date }) { - return `https://example.com/api/${channel.site_id}/${date.format('YYYY-MM-DD')}` - }, - parser(context) { - try { - return JSON.parse(context.content) - } catch { - return [] - } - } -} -``` - -### Context Object - -From each function in `config.js` you can access a `context` object containing the following data: - -- `channel`: The object describing the current channel (xmltv_id, site_id, name, lang) -- `date`: The 'dayjs' instance with the requested date -- `content`: The response data as a String -- `buffer`: The response data as an ArrayBuffer -- `headers`: The response headers -- `request`: The request config -- `cached`: A boolean to check whether this request was cached or not - -### Program Properties - -List of properties that can be assigned to each program during parsing. - -| Property | Aliases | Type | Required | -| --------------- | -------------------------------- | ------------------------------------------ | -------- | -| start | | `String \| Number \| Date()` | true | -| stop | | `String \| Number \| Date()` | true | -| title | titles | `String \| Object \| String[] \| Object[]` | true | -| subTitle | subTitles, sub_title, sub_titles | `String \| Object \| String[] \| Object[]` | false | -| description | desc, descriptions | `String \| Object \| String[] \| Object[]` | false | -| date | | `String \| Number \| Date()` | false | -| category | categories | `String \| Object \| String[] \| Object[]` | false | -| keyword | keywords | `String \| Object \| String[] \| Object[]` | false | -| language | languages | `String \| Object \| String[] \| Object[]` | false | -| origLanguage | origLanguages | `String \| Object \| String[] \| Object[]` | false | -| length | | `String \| Object \| String[] \| Object[]` | false | -| url | urls | `String \| Object \| String[] \| Object[]` | false | -| country | countries | `String \| Object \| String[] \| Object[]` | false | -| video | | `Object` | false | -| audio | | `Object` | false | -| season | | `String \| Number` | false | -| episode | | `String \| Number` | false | -| episodeNumber | episodeNum, episodeNumbers | `Object` | false | -| previouslyShown | | `String \| Object \| String[] \| Object[]` | false | -| premiere | | `String \| Object \| String[] \| Object[]` | false | -| lastChance | | `String \| Object \| String[] \| Object[]` | false | -| new | | `Boolean` | false | -| subtitles | | `Object \| Object[]` | false | -| rating | ratings | `String \| Object \| String[] \| Object[]` | false | -| starRating | starRatings | `String \| Object \| String[] \| Object[]` | false | -| review | reviews | `String \| Object \| String[] \| Object[]` | false | -| director | directors | `String \| Object \| String[] \| Object[]` | false | -| actor | actors | `String \| Object \| String[] \| Object[]` | false | -| writer | writers | `String \| Object \| String[] \| Object[]` | false | -| adapter | adapters | `String \| Object \| String[] \| Object[]` | false | -| producer | producers | `String \| Object \| String[] \| Object[]` | false | -| presenter | presenters | `String \| Object \| String[] \| Object[]` | false | -| composer | composers | `String \| Object \| String[] \| Object[]` | false | -| editor | editors | `String \| Object \| String[] \| Object[]` | false | -| commentator | commentators | `String \| Object \| String[] \| Object[]` | false | -| guest | guests | `String \| Object \| String[] \| Object[]` | false | -| image | images | `String \| Object \| String[] \| Object[]` | false | -| icon | icons | `String \| Object \| String[] \| Object[]` | false | - -Example: - -```js -{ - start: '2021-03-19T06:00:00.000Z', - stop: '2021-03-19T06:30:00.000Z', - title: 'Program 1', - subTitle: 'Sub-title & 1', - description: 'Description for Program 1', - date: '2022-05-06', - categories: ['Comedy', 'Drama'], - keywords: [ - { lang: 'en', value: 'physical-comedy' }, - { lang: 'en', value: 'romantic' } - ], - language: 'English', - origLanguage: { lang: 'en', value: 'French' }, - length: { units: 'minutes', value: '60' }, - url: 'http://example.com/title.html', - country: 'US', - video: { - present: 'yes', - colour: 'no', - aspect: '16:9', - quality: 'HDTV' - }, - audio: { - present: 'yes', - stereo: 'Dolby Digital' - }, - season: 9, - episode: 239, - previouslyShown: [{ start: '20080711000000', channel: 'channel-two.tv' }], - premiere: 'First time on British TV', - lastChance: [{ lang: 'en', value: 'Last time on this channel' }], - new: true, - subtitles: [ - { type: 'teletext', language: 'English' }, - { type: 'onscreen', language: [{ lang: 'en', value: 'Spanish' }] } - ], - rating: { - system: 'MPAA', - value: 'P&G', - icon: 'http://example.com/pg_symbol.png' - }, - starRatings: [ - { - system: 'TV Guide', - value: '4/5', - icon: [{ src: 'stars.png', width: 100, height: 100 }] - }, - { - system: 'IMDB', - value: '8/10' - } - ], - reviews: [ - { - type: 'text', - source: 'Rotten Tomatoes', - reviewer: 'Joe Bloggs', - lang: 'en', - value: 'This is a fantastic show!' - }, - { - type: 'text', - source: 'IDMB', - reviewer: 'Jane Doe', - lang: 'en', - value: 'I love this show!' - }, - { - type: 'url', - source: 'Rotten Tomatoes', - reviewer: 'Joe Bloggs', - lang: 'en', - value: 'https://example.com/programme_one_review' - } - ], - directors: [ - { - value: 'Director 1', - url: { value: 'http://example.com/director1.html', system: 'TestSystem' }, - image: [ - 'https://example.com/image1.jpg', - { - value: 'https://example.com/image2.jpg', - type: 'person', - size: '2', - system: 'TestSystem', - orient: 'P' - } - ] - }, - 'Director 2' - ], - actors: ['Actor 1', 'Actor 2'], - writer: 'Writer 1', - producers: 'Roger Dobkowitz', - presenters: 'Drew Carey', - images: [ - { - type: 'poster', - size: '1', - orient: 'P', - system: 'tvdb', - value: 'https://tvdb.com/programme_one_poster_1.jpg' - }, - { - type: 'poster', - size: '2', - orient: 'P', - system: 'tmdb', - value: 'https://tmdb.com/programme_one_poster_2.jpg' - }, - { - type: 'backdrop', - size: '3', - orient: 'L', - system: 'tvdb', - value: 'https://tvdb.com/programme_one_backdrop_3.jpg' - } - ], - icon: 'https://example.com/images/Program1.png?x=шеллы&sid=777' -} -``` - -
- -
-example.com.test.js -
- -With this file we can test the previously created config and make sure it works as you expect. - -```js -const { parser, url } = require('./example.com.config.js') -const dayjs = require('dayjs') -const utc = require('dayjs/plugin/utc') -const customParseFormat = require('dayjs/plugin/customParseFormat') -dayjs.extend(customParseFormat) -dayjs.extend(utc) - -const date = dayjs.utc('2025-01-12', 'YYYY-MM-DD').startOf('d') -const channel = { site_id: 'bbc1', xmltv_id: 'BBCOne.uk' } - -it('can generate valid url', () => { - expect(url({ channel, date })).toBe('https://example.com/api/bbc1/2025-01-12') -}) - -it('can parse response', () => { - const content = - '[{"title":"Program 1","start":"2025-01-12T00:00:00.000Z","stop":"2025-01-12T00:30:00.000Z"},{"title":"Program 2","start":"2025-01-12T00:30:00.000Z","stop":"2025-01-12T01:00:00.000Z"}]' - - const results = parser({ content }) - - expect(results.length).toBe(2) - expect(results[0]).toMatchObject({ - title: 'Program 1', - start: '2025-01-12T00:00:00.000Z', - stop: '2025-01-12T00:30:00.000Z' - }) - expect(results[1]).toMatchObject({ - title: 'Program 2', - start: '2025-01-12T00:30:00.000Z', - stop: '2025-01-12T01:00:00.000Z' - }) -}) - -it('can handle empty guide', () => { - const result = parser({ - date, - channel, - content: '' - }) - - expect(result).toMatchObject([]) -}) -``` - -Detailed documentation for the tests can be found here: https://jestjs.io/docs/using-matchers - -
- -
-example.com.channels.xml -
- -This file contains a list of channels available at the source. - -```xml - - - BBC One - -``` - -
- -
-readme.md -
- -This file contains instructions on how to use this config. - -```` -# example.com - -https://example.com - -### Download the guide - -```sh -npm run grab --- --site=example.com -``` - -### Test - -```sh -npm test --- example.com -``` -```` - -
- -The fastest way to create all these files is to use the command: - -```sh -npm run sites:init --- example.com -``` - -After you finish working on the files you can make sure that everything works by running the config test: - -``` -npm test --- example.com -``` - -Then check that all channels have the correct `xmltv-id`: - -``` -npm run channels:validate sites/example.com/example.com.channels.xml -``` - -And then try downloading the guide itself: - -``` -npm run grab --- example.com -``` - -If everything goes well just [commit](https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/about-commits) all changes and send us 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 map the channels? - -In order for the guides to be linked with playlists from [iptv-org/iptv](https://github.com/iptv-org/iptv) and also with our other projects, each channel must have the same ID in the description as in our [iptv-org/database](https://github.com/iptv-org/database). - -To check this, select one of the sites in the [SITES.md](SITES.md), open its `*.channels.xml` file and check that all channels have a valid `xmltv_id`. A list of all channels with corresponding IDs can be found at [iptv-org.github.io](https://iptv-org.github.io/). - -If the channel is not in our database yet, you can add it to it through this [form](https://github.com/iptv-org/database/issues/new?assignees=&labels=channels%3Aadd&projects=&template=1_channels_add.yml&title=Add%3A+). - -If the `*.channels.xml` file contains many channels without `xmltv_id`, you can speed up the process by running the command in the [Console](https://en.wikipedia.org/wiki/Windows_Console) (or [Terminal]() if you have macOS): - -```sh -npm run channels:edit path/to/channels.xml -``` - -This way, you can map channels by simply selecting the proper ID from the list: - -```sh -? Select channel ID for "BBC One" (bbc1): (Use arrow keys) -❯ BBCOne.uk (BBC One, BBC1, BBC Television, BBC Television Service) - BBCOneHD.uk (BBC One HD) - Type... - Skip -``` - -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). - -## Project Structure - -- `.github/` - - `ISSUE_TEMPLATE/`: issue templates for the repository. - - `workflows`: contains [GitHub actions](https://docs.github.com/en/actions/quickstart) workflows. - - `CODE_OF_CONDUCT.md`: rules you shouldn't break if you don't want to get banned. -- `scripts/`: contains all scripts used in the repository. -- `sites/`: contains configurations, channel lists and tests for all sites. -- `tests/`: contains tests to check the scripts. -- `CONTRIBUTING.md`: file you are currently reading. -- `README.md`: project description displayed on the home page. -- `SITES.md`: list of all supported sites and their current status. - -## Scripts - -These scripts are created to automate routine processes in the repository and make it a bit easier to maintain. - -For scripts to work, you must have [Node.js](https://nodejs.org/en) installed on your computer. - -To run scripts use the `npm run ` command. - -- `act:check`: allows to test the [check](https://github.com/iptv-org/iptv/blob/master/.github/workflows/check.yml) workflow locally. Depends on [nektos/act](https://github.com/nektos/act). -- `act:update`: allows to test the [update](https://github.com/iptv-org/iptv/blob/master/.github/workflows/update.yml) workflow locally. Depends on [nektos/act](https://github.com/nektos/act). -- `api:load`: downloads the latest channels data from the [iptv-org/api](https://github.com/iptv-org/api). -- `api:generate`: generates a JSON file with all channels for the [iptv-org/api](https://github.com/iptv-org/api) repository. -- `channels:lint`: сhecks the channel lists for syntax errors. -- `channels:parse`: generates a list of channels based on the site configuration. -- `channels:edit`: utility for quick channels mapping. -- `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). -- `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. -- `test`: runs a test of all the scripts described above. +# Contributing Guide + +- [How to?](#how-to) +- [Project Structure](#project-structure) +- [Scripts](#scripts) + +## How to? + +### How to add a channel to the guide? + +To ask for help with adding a channel simply fill out this [form](https://github.com/iptv-org/epg/issues/new?assignees=&labels=channel+request&projects=&template=2_channel-request.yml). + +If you want to add a channel to the list yourself, here are the instructions on how to do it. + +First select the site from [SITES.md](SITES.md) that you know has a guide for the channel you need. Then go to the folder with its config and open the file `*.channels.xml`. + +Make sure that the desired channel is not already in the list. If it is not, simply add its description to the end of the list as shown here: + +```xml + + + ... + CHANNEL_NAME + +``` + +| Attribute | Description | Example | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | +| SITE | Site domain name. | `example.com` | +| SITE_ID | Unique ID of the channel used in the source. | `hbo` | +| LANGUAGE_CODE | Language of the guide ([ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) code). | `en` | +| STREAM_ID | ID of the stream (`@`) for which the guide is intended. Full list of supported channels with corresponding ID could be found on [iptv-org.github.io](https://iptv-org.github.io/). | `HBO.us@East` | +| CHANNEL_NAME | Name of the channel used in the source. | `HBO East` | + +After that just [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 report broken guide? + +If you start to get errors when downloading the guide or if nothing loads at all, please let us know via this [form](https://github.com/iptv-org/epg/issues/new?assignees=&labels=broken+guide&projects=&template=3_broken-guide.yml). + +### How to add a new source to the repository? + +If you are not familiar with javascript programming, you can ask for help from other community members through this [form](https://github.com/iptv-org/epg/issues/new?assignees=&labels=source+request&projects=&template=1_source-request.yml). Otherwise, below are the instructions for you. + +To start with, you need to create a new folder in the [/sites](/sites) folder and put at least 4 files in it: + +
+example.com.config.js +
+ +This file describes what kind of request we need to send to get the guide for a particular channel on a certain date and how to parse the response. + +```js +module.exports = { + site: 'example.com', + url({ channel, date }) { + return `https://example.com/api/${channel.site_id}/${date.format('YYYY-MM-DD')}` + }, + parser(context) { + try { + return JSON.parse(context.content) + } catch { + return [] + } + } +} +``` + +### Context Object + +From each function in `config.js` you can access a `context` object containing the following data: + +- `channel`: The object describing the current channel (xmltv_id, site_id, name, lang) +- `date`: The 'dayjs' instance with the requested date +- `content`: The response data as a String +- `buffer`: The response data as an ArrayBuffer +- `headers`: The response headers +- `request`: The request config +- `cached`: A boolean to check whether this request was cached or not + +### Program Properties + +List of properties that can be assigned to each program during parsing. + +| Property | Aliases | Type | Required | +| --------------- | -------------------------------- | ------------------------------------------ | -------- | +| start | | `String \| Number \| Date()` | true | +| stop | | `String \| Number \| Date()` | true | +| title | titles | `String \| Object \| String[] \| Object[]` | true | +| subTitle | subTitles, sub_title, sub_titles | `String \| Object \| String[] \| Object[]` | false | +| description | desc, descriptions | `String \| Object \| String[] \| Object[]` | false | +| date | | `String \| Number \| Date()` | false | +| category | categories | `String \| Object \| String[] \| Object[]` | false | +| keyword | keywords | `String \| Object \| String[] \| Object[]` | false | +| language | languages | `String \| Object \| String[] \| Object[]` | false | +| origLanguage | origLanguages | `String \| Object \| String[] \| Object[]` | false | +| length | | `String \| Object \| String[] \| Object[]` | false | +| url | urls | `String \| Object \| String[] \| Object[]` | false | +| country | countries | `String \| Object \| String[] \| Object[]` | false | +| video | | `Object` | false | +| audio | | `Object` | false | +| season | | `String \| Number` | false | +| episode | | `String \| Number` | false | +| episodeNumber | episodeNum, episodeNumbers | `Object` | false | +| previouslyShown | | `String \| Object \| String[] \| Object[]` | false | +| premiere | | `String \| Object \| String[] \| Object[]` | false | +| lastChance | | `String \| Object \| String[] \| Object[]` | false | +| new | | `Boolean` | false | +| subtitles | | `Object \| Object[]` | false | +| rating | ratings | `String \| Object \| String[] \| Object[]` | false | +| starRating | starRatings | `String \| Object \| String[] \| Object[]` | false | +| review | reviews | `String \| Object \| String[] \| Object[]` | false | +| director | directors | `String \| Object \| String[] \| Object[]` | false | +| actor | actors | `String \| Object \| String[] \| Object[]` | false | +| writer | writers | `String \| Object \| String[] \| Object[]` | false | +| adapter | adapters | `String \| Object \| String[] \| Object[]` | false | +| producer | producers | `String \| Object \| String[] \| Object[]` | false | +| presenter | presenters | `String \| Object \| String[] \| Object[]` | false | +| composer | composers | `String \| Object \| String[] \| Object[]` | false | +| editor | editors | `String \| Object \| String[] \| Object[]` | false | +| commentator | commentators | `String \| Object \| String[] \| Object[]` | false | +| guest | guests | `String \| Object \| String[] \| Object[]` | false | +| image | images | `String \| Object \| String[] \| Object[]` | false | +| icon | icons | `String \| Object \| String[] \| Object[]` | false | + +Example: + +```js +{ + start: '2021-03-19T06:00:00.000Z', + stop: '2021-03-19T06:30:00.000Z', + title: 'Program 1', + subTitle: 'Sub-title & 1', + description: 'Description for Program 1', + date: '2022-05-06', + categories: ['Comedy', 'Drama'], + keywords: [ + { lang: 'en', value: 'physical-comedy' }, + { lang: 'en', value: 'romantic' } + ], + language: 'English', + origLanguage: { lang: 'en', value: 'French' }, + length: { units: 'minutes', value: '60' }, + url: 'http://example.com/title.html', + country: 'US', + video: { + present: 'yes', + colour: 'no', + aspect: '16:9', + quality: 'HDTV' + }, + audio: { + present: 'yes', + stereo: 'Dolby Digital' + }, + season: 9, + episode: 239, + previouslyShown: [{ start: '20080711000000', channel: 'channel-two.tv' }], + premiere: 'First time on British TV', + lastChance: [{ lang: 'en', value: 'Last time on this channel' }], + new: true, + subtitles: [ + { type: 'teletext', language: 'English' }, + { type: 'onscreen', language: [{ lang: 'en', value: 'Spanish' }] } + ], + rating: { + system: 'MPAA', + value: 'P&G', + icon: 'http://example.com/pg_symbol.png' + }, + starRatings: [ + { + system: 'TV Guide', + value: '4/5', + icon: [{ src: 'stars.png', width: 100, height: 100 }] + }, + { + system: 'IMDB', + value: '8/10' + } + ], + reviews: [ + { + type: 'text', + source: 'Rotten Tomatoes', + reviewer: 'Joe Bloggs', + lang: 'en', + value: 'This is a fantastic show!' + }, + { + type: 'text', + source: 'IDMB', + reviewer: 'Jane Doe', + lang: 'en', + value: 'I love this show!' + }, + { + type: 'url', + source: 'Rotten Tomatoes', + reviewer: 'Joe Bloggs', + lang: 'en', + value: 'https://example.com/programme_one_review' + } + ], + directors: [ + { + value: 'Director 1', + url: { value: 'http://example.com/director1.html', system: 'TestSystem' }, + image: [ + 'https://example.com/image1.jpg', + { + value: 'https://example.com/image2.jpg', + type: 'person', + size: '2', + system: 'TestSystem', + orient: 'P' + } + ] + }, + 'Director 2' + ], + actors: ['Actor 1', 'Actor 2'], + writer: 'Writer 1', + producers: 'Roger Dobkowitz', + presenters: 'Drew Carey', + images: [ + { + type: 'poster', + size: '1', + orient: 'P', + system: 'tvdb', + value: 'https://tvdb.com/programme_one_poster_1.jpg' + }, + { + type: 'poster', + size: '2', + orient: 'P', + system: 'tmdb', + value: 'https://tmdb.com/programme_one_poster_2.jpg' + }, + { + type: 'backdrop', + size: '3', + orient: 'L', + system: 'tvdb', + value: 'https://tvdb.com/programme_one_backdrop_3.jpg' + } + ], + icon: 'https://example.com/images/Program1.png?x=шеллы&sid=777' +} +``` + +
+ +
+example.com.test.js +
+ +With this file we can test the previously created config and make sure it works as you expect. + +```js +const { parser, url } = require('./example.com.config.js') +const dayjs = require('dayjs') +const utc = require('dayjs/plugin/utc') +const customParseFormat = require('dayjs/plugin/customParseFormat') +dayjs.extend(customParseFormat) +dayjs.extend(utc) + +const date = dayjs.utc('2025-01-12', 'YYYY-MM-DD').startOf('d') +const channel = { site_id: 'bbc1', xmltv_id: 'BBCOne.uk' } + +it('can generate valid url', () => { + expect(url({ channel, date })).toBe('https://example.com/api/bbc1/2025-01-12') +}) + +it('can parse response', () => { + const content = + '[{"title":"Program 1","start":"2025-01-12T00:00:00.000Z","stop":"2025-01-12T00:30:00.000Z"},{"title":"Program 2","start":"2025-01-12T00:30:00.000Z","stop":"2025-01-12T01:00:00.000Z"}]' + + const results = parser({ content }) + + expect(results.length).toBe(2) + expect(results[0]).toMatchObject({ + title: 'Program 1', + start: '2025-01-12T00:00:00.000Z', + stop: '2025-01-12T00:30:00.000Z' + }) + expect(results[1]).toMatchObject({ + title: 'Program 2', + start: '2025-01-12T00:30:00.000Z', + stop: '2025-01-12T01:00:00.000Z' + }) +}) + +it('can handle empty guide', () => { + const result = parser({ + date, + channel, + content: '' + }) + + expect(result).toMatchObject([]) +}) +``` + +Detailed documentation for the tests can be found here: https://jestjs.io/docs/using-matchers + +
+ +
+example.com.channels.xml +
+ +This file contains a list of channels available at the source. + +```xml + + + BBC One + +``` + +
+ +
+readme.md +
+ +This file contains instructions on how to use this config. + +```` +# example.com + +https://example.com + +### Download the guide + +```sh +npm run grab --- --site=example.com +``` + +### Test + +```sh +npm test --- example.com +``` +```` + +
+ +The fastest way to create all these files is to use the command: + +```sh +npm run sites:init --- example.com +``` + +After you finish working on the files you can make sure that everything works by running the config test: + +``` +npm test --- example.com +``` + +Then check that all channels have the correct `xmltv-id`: + +``` +npm run channels:validate sites/example.com/example.com.channels.xml +``` + +And then try downloading the guide itself: + +``` +npm run grab --- example.com +``` + +If everything goes well just [commit](https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/about-commits) all changes and send us 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 map the channels? + +In order for the guides to be linked with playlists from [iptv-org/iptv](https://github.com/iptv-org/iptv) and also with our other projects, each channel must have the same ID in the description as in our [iptv-org/database](https://github.com/iptv-org/database). + +To check this, select one of the sites in the [SITES.md](SITES.md), open its `*.channels.xml` file and check that all channels have a valid `xmltv_id`. A list of all channels with corresponding IDs can be found at [iptv-org.github.io](https://iptv-org.github.io/). + +If the channel is not in our database yet, you can add it to it through this [form](https://github.com/iptv-org/database/issues/new?assignees=&labels=channels%3Aadd&projects=&template=1_channels_add.yml&title=Add%3A+). + +If the `*.channels.xml` file contains many channels without `xmltv_id`, you can speed up the process by running the command in the [Console](https://en.wikipedia.org/wiki/Windows_Console) (or [Terminal]() if you have macOS): + +```sh +npm run channels:edit path/to/channels.xml +``` + +This way, you can map channels by simply selecting the proper ID from the list: + +```sh +? Select channel ID for "BBC One" (bbc1): (Use arrow keys) +❯ BBCOne.uk (BBC One, BBC1, BBC Television, BBC Television Service) + BBCOneHD.uk (BBC One HD) + Type... + Skip +``` + +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). + +## Project Structure + +- `.github/` + - `ISSUE_TEMPLATE/`: issue templates for the repository. + - `workflows`: contains [GitHub actions](https://docs.github.com/en/actions/quickstart) workflows. + - `CODE_OF_CONDUCT.md`: rules you shouldn't break if you don't want to get banned. +- `scripts/`: contains all scripts used in the repository. +- `sites/`: contains configurations, channel lists and tests for all sites. +- `tests/`: contains tests to check the scripts. +- `CONTRIBUTING.md`: file you are currently reading. +- `README.md`: project description displayed on the home page. +- `SITES.md`: list of all supported sites and their current status. + +## Scripts + +These scripts are created to automate routine processes in the repository and make it a bit easier to maintain. + +For scripts to work, you must have [Node.js](https://nodejs.org/en) installed on your computer. + +To run scripts use the `npm run ` command. + +- `act:check`: allows to test the [check](https://github.com/iptv-org/iptv/blob/master/.github/workflows/check.yml) workflow locally. Depends on [nektos/act](https://github.com/nektos/act). +- `act:update`: allows to test the [update](https://github.com/iptv-org/iptv/blob/master/.github/workflows/update.yml) workflow locally. Depends on [nektos/act](https://github.com/nektos/act). +- `api:load`: downloads the latest channels data from the [iptv-org/api](https://github.com/iptv-org/api). +- `api:generate`: generates a JSON file with all channels for the [iptv-org/api](https://github.com/iptv-org/api) repository. +- `channels:lint`: сhecks the channel lists for syntax errors. +- `channels:parse`: generates a list of channels based on the site configuration. +- `channels:parse`: formats `*.channels.xml` files. The process involves removing invalid `xmltv_id`, adding missing Feed ID, and sorting the list. +- `channels:edit`: utility for quick channels mapping. +- `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). +- `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. +- `test`: runs a test of all the scripts described above. diff --git a/README.md b/README.md index 626c1cba..e1d4fe47 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ After that open the [Console](https://en.wikipedia.org/wiki/Windows_Console) (or git clone --depth 1 -b master https://github.com/iptv-org/epg.git ``` -Then navigate to the downloaded `epg` folder: +This will copy all the code from the repository to your computer into the `epg` folder. After that, we just need to go to the folder we created: ```sh cd epg @@ -76,7 +76,7 @@ By default, the guide for each channel is downloaded one by one, but you can cha npm run grab --- --site=example.com --maxConnections=10 ``` -But be aware that under heavy load, some sites may start return an error or completely block your access. +But be aware that under heavy load some sites may start return an error or completely block your access. ### Use custom channel list @@ -98,7 +98,7 @@ npm run grab --- --channels=path/to/custom.channels.xml ### Run on schedule -If you want to download guides on a schedule, you can use [cron](https://en.wikipedia.org/wiki/Cron) or any other task scheduler. Currently, we use a tool called `chronos` for this purpose. +If you want to download guides on a schedule, you can use [cron](https://en.wikipedia.org/wiki/Cron) or any other task scheduler. Currently, we use a tool called [chronos](https://github.com/freearhey/chronos) for this purpose. To start it, you only need to specify the necessary `grab` command and [cron expression](https://crontab.guru/): @@ -188,7 +188,7 @@ docker run \ -e DAYS=14 \ -e TIMEOUT=5 \ -e DELAY=2 \ -iptv-org/epg +ghcr.io/iptv-org/epg:master ``` | Variable | Description | diff --git a/SITES.md b/SITES.md index 1d86bef2..a9da004c 100644 --- a/SITES.md +++ b/SITES.md @@ -65,7 +65,7 @@ gigatv.3bbtv.co.th7938🟢 guiadetv.com1240🟢 guida.tv8888🟢 - guidatv.sky.it154154🟢https://github.com/iptv-org/epg/issues/2849 + guidatv.sky.it154154🟢 guidetnt.com6969🟢 horizon.tv184172🟢 hoy.tv31🟢 @@ -93,7 +93,7 @@ meuguia.tv10297🟢 mewatch.sg2524🟢 mi.tv2084620🟢 - mncvision.id276223🟢https://github.com/iptv-org/epg/issues/2848 + mncvision.id276223🟢 moji.id11🟢 mojmaxtv.hrvatskitelekom.hr2430🟢 mon-programme-tv.be11195🟢 @@ -101,7 +101,7 @@ mtel.ba5010🟢 mts.rs4570🟢 mujtvprogram.cz216202🟢 - musor.tv181145🟢 + musor.tv181143🟢 mysky.com.ph11543🟢 mytelly.co.uk488401🟢 mytvsuper.com10899🟢 @@ -143,12 +143,12 @@ ruv.is22🟢 s.mxtv.jp22🟢 sat.tv30308249🟢 - shahid.mbc.net225183🟢 + shahid.mbc.net228186🟢 siba.com.co9896🟢 singtel.com155113🟢 sjonvarp.is1313🟢 sky.co.nz11193🟢 - sky.com559458🟡https://github.com/iptv-org/epg/issues/2763 + sky.com542489🟡https://github.com/iptv-org/epg/issues/2763 sky.de7575🟢 skylife.co.kr2510🔴https://github.com/iptv-org/epg/issues/2845 skyperfectv.co.jp137130🟢 diff --git a/package-lock.json b/package-lock.json index f99859f6..505bc805 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,35 +10,34 @@ "dependencies": { "@alex_neo/jest-expect-message": "^1.0.5", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "^9.38.0", + "@eslint/js": "^9.33.0", "@freearhey/chronos": "^0.0.1", - "@freearhey/core": "^0.14.3", - "@freearhey/search-js": "^0.2.0", - "@freearhey/storage-js": "^0.2.0", + "@freearhey/core": "^0.10.3", + "@freearhey/search-js": "^0.1.2", "@ntlab/sfetch": "^1.2.0", - "@octokit/core": "^7.0.5", - "@octokit/plugin-paginate-rest": "^13.2.1", - "@octokit/plugin-rest-endpoint-methods": "^16.1.1", - "@stylistic/eslint-plugin": "^5.5.0", - "@swc/core": "^1.13.5", + "@octokit/core": "^7.0.3", + "@octokit/plugin-paginate-rest": "^13.1.1", + "@octokit/plugin-rest-endpoint-methods": "^16.0.0", + "@stylistic/eslint-plugin": "^5.2.3", + "@swc/core": "^1.13.3", "@swc/jest": "^0.2.39", "@types/cli-progress": "^3.11.6", "@types/fs-extra": "^11.0.4", - "@types/inquirer": "^9.0.9", + "@types/inquirer": "^9.0.8", "@types/jest": "^30.0.0", "@types/langs": "^2.0.5", "@types/lodash.orderby": "^4.6.9", "@types/lodash.sortby": "^4.7.9", "@types/lodash.startcase": "^4.4.9", "@types/lodash.uniqby": "^4.7.9", - "@types/node": "^24.9.1", + "@types/node": "^24.3.0", "@types/node-cleanup": "^2.1.5", "@types/numeral": "^2.0.5", - "@typescript-eslint/eslint-plugin": "^8.46.2", - "@typescript-eslint/parser": "^8.46.2", - "axios": "^1.12.2", + "@typescript-eslint/eslint-plugin": "^8.40.0", + "@typescript-eslint/parser": "^8.40.0", + "axios": "^1.11.0", "axios-cookiejar-support": "^6.0.4", - "chalk": "^5.6.2", + "chalk": "^5.6.0", "cheerio": "^1.1.2", "cli-progress": "^3.12.0", "commander": "^14.0.1", @@ -46,22 +45,22 @@ "cross-env": "^10.1.0", "csv-parser": "^3.2.0", "cwait": "^1.1.2", - "dayjs": "^1.11.18", - "epg-grabber": "^0.44.0", + "dayjs": "^1.11.13", + "epg-grabber": "^0.41.0", "epg-parser": "^0.4.0", - "eslint": "^9.38.0", + "eslint": "^9.33.0", "eslint-config-prettier": "^10.1.8", "form-data": "^4.0.4", - "fs-extra": "^11.3.2", + "fs-extra": "^11.3.1", "glob": "^11.0.3", "globals": "^16.4.0", "husky": "^9.1.7", "iconv-lite": "^0.7.0", - "inquirer": "^12.10.0", - "jest": "^30.2.0", + "inquirer": "^12.9.3", + "jest": "^30.0.5", "jest-offline": "^1.0.1", "langs": "^2.0.0", - "libxml2-wasm": "^0.6.0", + "libxml2-wasm": "^0.5.0", "lodash.orderby": "^4.6.0", "lodash.sortby": "^4.7.0", "lodash.startcase": "^4.4.0", @@ -84,10 +83,10 @@ "srcset": "^5.0.2", "table2array": "^0.0.2", "tabletojson": "^4.1.6", - "tough-cookie": "^6.0.0", + "tough-cookie": "^5.1.2", "transliteration": "^2.3.5", - "tsx": "^4.20.6", - "typescript": "^5.9.3", + "tsx": "^4.20.4", + "typescript": "^5.9.2", "unzipit": "^1.4.3", "uuid": "^13.0.0", "wildcard-match": "^5.1.4" @@ -594,11 +593,11 @@ } }, "node_modules/@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", "dependencies": { - "colorspace": "1.1.x", + "@so-ric/colorspace": "^1.1.6", "enabled": "2.0.x", "kuler": "^2.0.0" } @@ -1055,21 +1054,18 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.1.tgz", - "integrity": "sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.16.0" - }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz", - "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" @@ -1129,9 +1125,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.38.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.38.0.tgz", - "integrity": "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==", + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.33.0.tgz", + "integrity": "sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1150,12 +1146,12 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz", - "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.16.0", + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { @@ -1176,10 +1172,9 @@ } }, "node_modules/@freearhey/core": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/@freearhey/core/-/core-0.14.3.tgz", - "integrity": "sha512-w/kaoUdZlbqvOSOmid0nrBI9iGZkUZ+KLZUfkWpfZRPXOEu/FxlEP5gPANGgovwijrgogAbVf/qnf9VV+429ag==", - "license": "MIT", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@freearhey/core/-/core-0.10.3.tgz", + "integrity": "sha512-in5wgFHo+PJF585TXpaPLInlsgXuDLbUpXfcWrctd17mJpNNr5Mt5DetpzBqSqLERgh2jxxGRHyPGhm3Yl02hQ==", "dependencies": { "@types/lodash": "^4.14.198", "@types/pako": "^2.0.3", @@ -1206,17 +1201,6 @@ "node": ">=16.0.0" } }, - "node_modules/@freearhey/storage-js": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@freearhey/storage-js/-/storage-js-0.2.0.tgz", - "integrity": "sha512-IPFeOqE6/N8CdCxld5q4/eYR71eysn3FOizRmN3PGLUcnR3QiMjoYC/CwKFJYGuYs1CTK8pGTvik4d/WqIiDqg==", - "license": "MIT", - "dependencies": { - "@types/fs-extra": "^11.0.4", - "fs-extra": "^11.3.1", - "glob": "^11.0.3" - } - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1284,9 +1268,9 @@ } }, "node_modules/@inquirer/checkbox": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.0.tgz", - "integrity": "sha512-5+Q3PKH35YsnoPTh75LucALdAxom6xh5D1oeY561x4cqBuH24ZFVyFREPe14xgnrtmGu3EEt1dIi60wRVSnGCw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.1.tgz", + "integrity": "sha512-bevKGO6kX1eM/N+pdh9leS5L7TBF4ICrzi9a+cbWkrxeAeIcwlo/7OfWGCDERdRCI2/Q6tjltX4bt07ALHDwFw==", "license": "MIT", "dependencies": { "@inquirer/ansi": "^1.0.1", @@ -1308,9 +1292,9 @@ } }, "node_modules/@inquirer/confirm": { - "version": "5.1.19", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.19.tgz", - "integrity": "sha512-wQNz9cfcxrtEnUyG5PndC8g3gZ7lGDBzmWiXZkX8ot3vfZ+/BLjR8EvyGX4YzQLeVqtAlY/YScZpW7CW8qMoDQ==", + "version": "5.1.15", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.15.tgz", + "integrity": "sha512-SwHMGa8Z47LawQN0rog0sT+6JpiL0B7eW9p1Bb7iCeKDGTI5Ez25TSc2l8kw52VV7hA4sX/C78CGkMrKXfuspA==", "license": "MIT", "dependencies": { "@inquirer/core": "^10.3.0", @@ -1329,9 +1313,9 @@ } }, "node_modules/@inquirer/core": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.0.tgz", - "integrity": "sha512-Uv2aPPPSK5jeCplQmQ9xadnFx2Zhj9b5Dj7bU6ZeCdDNNY11nhYy4btcSdtDguHqCT2h5oNeQTcUNSGGLA7NTA==", + "version": "10.1.15", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.15.tgz", + "integrity": "sha512-8xrp836RZvKkpNbVvgWUlxjT4CraKk2q+I3Ksy+seI2zkcE+y6wNs1BVhgcv8VyImFecUhdQrYLdW32pAjwBdA==", "license": "MIT", "dependencies": { "@inquirer/ansi": "^1.0.1", @@ -1359,7 +1343,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", - "license": "ISC", "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -1368,7 +1351,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", "engines": { "node": ">=14" }, @@ -1377,9 +1359,9 @@ } }, "node_modules/@inquirer/editor": { - "version": "4.2.21", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.21.tgz", - "integrity": "sha512-MjtjOGjr0Kh4BciaFShYpZ1s9400idOdvQ5D7u7lE6VztPFoyLcVNE5dXBmEEIQq5zi4B9h2kU+q7AVBxJMAkQ==", + "version": "4.2.17", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.17.tgz", + "integrity": "sha512-r6bQLsyPSzbWrZZ9ufoWL+CztkSatnJ6uSxqd6N+o41EZC51sQeWOzI6s5jLb+xxTWxl7PlUppqm8/sow241gg==", "license": "MIT", "dependencies": { "@inquirer/core": "^10.3.0", @@ -1399,9 +1381,9 @@ } }, "node_modules/@inquirer/expand": { - "version": "4.0.21", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.21.tgz", - "integrity": "sha512-+mScLhIcbPFmuvU3tAGBed78XvYHSvCl6dBiYMlzCLhpr0bzGzd8tfivMMeqND6XZiaZ1tgusbUHJEfc6YzOdA==", + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.17.tgz", + "integrity": "sha512-PSqy9VmJx/VbE3CT453yOfNa+PykpKg/0SYP7odez1/NWBGuDXgPhp4AeGYYKjhLn5lUUavVS/JbeYMPdH50Mw==", "license": "MIT", "dependencies": { "@inquirer/core": "^10.3.0", @@ -1421,9 +1403,9 @@ } }, "node_modules/@inquirer/external-editor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.2.tgz", - "integrity": "sha512-yy9cOoBnx58TlsPrIxauKIFQTiyH+0MK4e97y4sV9ERbI+zDxw7i2hxHLCIEGIE/8PPvDxGhgzIOTSOWcs6/MQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.1.tgz", + "integrity": "sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q==", "license": "MIT", "dependencies": { "chardet": "^2.1.0", @@ -1442,18 +1424,18 @@ } }, "node_modules/@inquirer/figures": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.14.tgz", - "integrity": "sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ==", + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz", + "integrity": "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==", "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@inquirer/input": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.5.tgz", - "integrity": "sha512-7GoWev7P6s7t0oJbenH0eQ0ThNdDJbEAEtVt9vsrYZ9FulIokvd823yLyhQlWHJPGce1wzP53ttfdCZmonMHyA==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.1.tgz", + "integrity": "sha512-tVC+O1rBl0lJpoUZv4xY+WGWY8V5b0zxU1XDsMsIHYregdh7bN5X5QnIONNBAl0K765FYlAfNHS2Bhn7SSOVow==", "license": "MIT", "dependencies": { "@inquirer/core": "^10.3.0", @@ -1472,9 +1454,9 @@ } }, "node_modules/@inquirer/number": { - "version": "3.0.21", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.21.tgz", - "integrity": "sha512-5QWs0KGaNMlhbdhOSCFfKsW+/dcAVC2g4wT/z2MCiZM47uLgatC5N20kpkDQf7dHx+XFct/MJvvNGy6aYJn4Pw==", + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.17.tgz", + "integrity": "sha512-GcvGHkyIgfZgVnnimURdOueMk0CztycfC8NZTiIY9arIAkeOgt6zG57G+7vC59Jns3UX27LMkPKnKWAOF5xEYg==", "license": "MIT", "dependencies": { "@inquirer/core": "^10.3.0", @@ -1493,9 +1475,9 @@ } }, "node_modules/@inquirer/password": { - "version": "4.0.21", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.21.tgz", - "integrity": "sha512-xxeW1V5SbNFNig2pLfetsDb0svWlKuhmr7MPJZMYuDnCTkpVBI+X/doudg4pznc1/U+yYmWFFOi4hNvGgUo7EA==", + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.17.tgz", + "integrity": "sha512-DJolTnNeZ00E1+1TW+8614F7rOJJCM4y4BAGQ3Gq6kQIG+OJ4zr3GLjIjVVJCbKsk2jmkmv6v2kQuN/vriHdZA==", "license": "MIT", "dependencies": { "@inquirer/ansi": "^1.0.1", @@ -1515,21 +1497,21 @@ } }, "node_modules/@inquirer/prompts": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.9.0.tgz", - "integrity": "sha512-X7/+dG9SLpSzRkwgG5/xiIzW0oMrV3C0HOa7YHG1WnrLK+vCQHfte4k/T80059YBdei29RBC3s+pSMvPJDU9/A==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.8.3.tgz", + "integrity": "sha512-iHYp+JCaCRktM/ESZdpHI51yqsDgXu+dMs4semzETftOaF8u5hwlqnbIsuIR/LrWZl8Pm1/gzteK9I7MAq5HTA==", "license": "MIT", "dependencies": { - "@inquirer/checkbox": "^4.3.0", - "@inquirer/confirm": "^5.1.19", - "@inquirer/editor": "^4.2.21", - "@inquirer/expand": "^4.0.21", - "@inquirer/input": "^4.2.5", - "@inquirer/number": "^3.0.21", - "@inquirer/password": "^4.0.21", - "@inquirer/rawlist": "^4.1.9", - "@inquirer/search": "^3.2.0", - "@inquirer/select": "^4.4.0" + "@inquirer/checkbox": "^4.2.1", + "@inquirer/confirm": "^5.1.15", + "@inquirer/editor": "^4.2.17", + "@inquirer/expand": "^4.0.17", + "@inquirer/input": "^4.2.1", + "@inquirer/number": "^3.0.17", + "@inquirer/password": "^4.0.17", + "@inquirer/rawlist": "^4.1.5", + "@inquirer/search": "^3.1.0", + "@inquirer/select": "^4.3.1" }, "engines": { "node": ">=18" @@ -1544,9 +1526,9 @@ } }, "node_modules/@inquirer/rawlist": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.9.tgz", - "integrity": "sha512-AWpxB7MuJrRiSfTKGJ7Y68imYt8P9N3Gaa7ySdkFj1iWjr6WfbGAhdZvw/UnhFXTHITJzxGUI9k8IX7akAEBCg==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.5.tgz", + "integrity": "sha512-R5qMyGJqtDdi4Ht521iAkNqyB6p2UPuZUbMifakg1sWtu24gc2Z8CJuw8rP081OckNDMgtDCuLe42Q2Kr3BolA==", "license": "MIT", "dependencies": { "@inquirer/core": "^10.3.0", @@ -1566,9 +1548,9 @@ } }, "node_modules/@inquirer/search": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.0.tgz", - "integrity": "sha512-a5SzB/qrXafDX1Z4AZW3CsVoiNxcIYCzYP7r9RzrfMpaLpB+yWi5U8BWagZyLmwR0pKbbL5umnGRd0RzGVI8bQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.1.0.tgz", + "integrity": "sha512-PMk1+O/WBcYJDq2H7foV0aAZSmDdkzZB9Mw2v/DmONRJopwA/128cS9M/TXWLKKdEQKZnKwBzqu2G4x/2Nqx8Q==", "license": "MIT", "dependencies": { "@inquirer/core": "^10.3.0", @@ -1589,9 +1571,9 @@ } }, "node_modules/@inquirer/select": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.0.tgz", - "integrity": "sha512-kaC3FHsJZvVyIjYBs5Ih8y8Bj4P/QItQWrZW22WJax7zTN+ZPXVGuOM55vzbdCP9zKUiBd9iEJVdesujfF+cAA==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.3.1.tgz", + "integrity": "sha512-Gfl/5sqOF5vS/LIrSndFgOh7jgoe0UXEizDqahFRkq5aJBLegZ6WjuMh/hVEJwlFQjyLq1z9fRtvUMkb7jM1LA==", "license": "MIT", "dependencies": { "@inquirer/ansi": "^1.0.1", @@ -1613,9 +1595,9 @@ } }, "node_modules/@inquirer/type": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.9.tgz", - "integrity": "sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.8.tgz", + "integrity": "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==", "license": "MIT", "engines": { "node": ">=18" @@ -1629,6 +1611,47 @@ } } }, + "node_modules/@iptv-org/sdk": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@iptv-org/sdk/-/sdk-1.1.0.tgz", + "integrity": "sha512-/lEIcWGmbBSWWgS8uOKEFdfGBYPlEOcDrETHqU4k3VxsnggL46mvIsA8+ZvCV5Fa3sADD2ded5FfjDmYhoX2DA==", + "dependencies": { + "@freearhey/core": "^0.14.3", + "@freearhey/search-js": "^0.2.0", + "@ntlab/sfetch": "^1.2.0", + "axios": "^1.11.0", + "dayjs": "^1.11.18" + } + }, + "node_modules/@iptv-org/sdk/node_modules/@freearhey/core": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@freearhey/core/-/core-0.14.3.tgz", + "integrity": "sha512-w/kaoUdZlbqvOSOmid0nrBI9iGZkUZ+KLZUfkWpfZRPXOEu/FxlEP5gPANGgovwijrgogAbVf/qnf9VV+429ag==", + "dependencies": { + "@types/lodash": "^4.14.198", + "@types/pako": "^2.0.3", + "consola": "^3.4.2", + "dayjs": "^1.11.13", + "glob": "^11.0.1", + "lodash": "^4.17.21", + "natural-orderby": "^5.0.0", + "normalize-url": "^8.1.0", + "object-treeify": "^2.1.1", + "pako": "^2.1.0", + "timer-node": "^5.0.9" + } + }, + "node_modules/@iptv-org/sdk/node_modules/@freearhey/search-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@freearhey/search-js/-/search-js-0.2.0.tgz", + "integrity": "sha512-1sxfCRbxM12Js3nM/S51cVKLYEjoksERidz539bleMAXes44eTC2m0TEQTJzJyE7l1pw2qUwsIhjd2l2l88fSw==", + "dependencies": { + "lodash": "^4.17.21" + }, + "engines": { + "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", @@ -2859,7 +2882,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -2872,7 +2894,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", "engines": { "node": ">= 8" } @@ -2881,7 +2902,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -3119,17 +3139,6 @@ "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", @@ -3278,14 +3287,23 @@ "@sinonjs/commons": "^3.0.1" } }, + "node_modules/@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "dependencies": { + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, "node_modules/@stylistic/eslint-plugin": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.5.0.tgz", - "integrity": "sha512-IeZF+8H0ns6prg4VrkhgL+yrvDXWDH2cKchrbh80ejG9dQgZWp10epHMbgRuQvgchLII/lfh6Xn3lu6+6L86Hw==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.2.3.tgz", + "integrity": "sha512-oY7GVkJGVMI5benlBDCaRrSC1qPasafyv5dOBLLv5MTilMGnErKhO6ziEfodDDIZbo5QxPUNW360VudJOFODMw==", "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.9.0", - "@typescript-eslint/types": "^8.46.1", + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/types": "^8.38.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "estraverse": "^5.3.0", @@ -3321,11 +3339,10 @@ } }, "node_modules/@swc/core": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.5.tgz", - "integrity": "sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.3.tgz", + "integrity": "sha512-ZaDETVWnm6FE0fc+c2UE8MHYVS3Fe91o5vkmGfgwGXFbxYvAjKSqxM/j4cRc9T7VZNSJjriXq58XkfCp3Y6f+w==", "hasInstallScript": true, - "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.24" @@ -3338,16 +3355,16 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.13.5", - "@swc/core-darwin-x64": "1.13.5", - "@swc/core-linux-arm-gnueabihf": "1.13.5", - "@swc/core-linux-arm64-gnu": "1.13.5", - "@swc/core-linux-arm64-musl": "1.13.5", - "@swc/core-linux-x64-gnu": "1.13.5", - "@swc/core-linux-x64-musl": "1.13.5", - "@swc/core-win32-arm64-msvc": "1.13.5", - "@swc/core-win32-ia32-msvc": "1.13.5", - "@swc/core-win32-x64-msvc": "1.13.5" + "@swc/core-darwin-arm64": "1.13.3", + "@swc/core-darwin-x64": "1.13.3", + "@swc/core-linux-arm-gnueabihf": "1.13.3", + "@swc/core-linux-arm64-gnu": "1.13.3", + "@swc/core-linux-arm64-musl": "1.13.3", + "@swc/core-linux-x64-gnu": "1.13.3", + "@swc/core-linux-x64-musl": "1.13.3", + "@swc/core-win32-arm64-msvc": "1.13.3", + "@swc/core-win32-ia32-msvc": "1.13.3", + "@swc/core-win32-x64-msvc": "1.13.3" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" @@ -3359,13 +3376,12 @@ } }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.5.tgz", - "integrity": "sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.3.tgz", + "integrity": "sha512-ux0Ws4pSpBTqbDS9GlVP354MekB1DwYlbxXU3VhnDr4GBcCOimpocx62x7cFJkSpEBF8bmX8+/TTCGKh4PbyXw==", "cpu": [ "arm64" ], - "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "darwin" @@ -3375,13 +3391,12 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.5.tgz", - "integrity": "sha512-ILd38Fg/w23vHb0yVjlWvQBoE37ZJTdlLHa8LRCFDdX4WKfnVBiblsCU9ar4QTMNdeTBEX9iUF4IrbNWhaF1Ng==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.3.tgz", + "integrity": "sha512-p0X6yhxmNUOMZrbeZ3ZNsPige8lSlSe1llllXvpCLkKKxN/k5vZt1sULoq6Nj4eQ7KeHQVm81/+AwKZyf/e0TA==", "cpu": [ "x64" ], - "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "darwin" @@ -3391,13 +3406,12 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.5.tgz", - "integrity": "sha512-Q6eS3Pt8GLkXxqz9TAw+AUk9HpVJt8Uzm54MvPsqp2yuGmY0/sNaPPNVqctCX9fu/Nu8eaWUen0si6iEiCsazQ==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.3.tgz", + "integrity": "sha512-OmDoiexL2fVWvQTCtoh0xHMyEkZweQAlh4dRyvl8ugqIPEVARSYtaj55TBMUJIP44mSUOJ5tytjzhn2KFxFcBA==", "cpu": [ "arm" ], - "license": "Apache-2.0", "optional": true, "os": [ "linux" @@ -3407,13 +3421,12 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.5.tgz", - "integrity": "sha512-aNDfeN+9af+y+M2MYfxCzCy/VDq7Z5YIbMqRI739o8Ganz6ST+27kjQFd8Y/57JN/hcnUEa9xqdS3XY7WaVtSw==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.3.tgz", + "integrity": "sha512-STfKku3QfnuUj6k3g9ld4vwhtgCGYIFQmsGPPgT9MK/dI3Lwnpe5Gs5t1inoUIoGNP8sIOLlBB4HV4MmBjQuhw==", "cpu": [ "arm64" ], - "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "linux" @@ -3423,13 +3436,12 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.5.tgz", - "integrity": "sha512-9+ZxFN5GJag4CnYnq6apKTnnezpfJhCumyz0504/JbHLo+Ue+ZtJnf3RhyA9W9TINtLE0bC4hKpWi8ZKoETyOQ==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.3.tgz", + "integrity": "sha512-bc+CXYlFc1t8pv9yZJGus372ldzOVscBl7encUBlU1m/Sig0+NDJLz6cXXRcFyl6ABNOApWeR4Yl7iUWx6C8og==", "cpu": [ "arm64" ], - "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "linux" @@ -3439,13 +3451,12 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.5.tgz", - "integrity": "sha512-WD530qvHrki8Ywt/PloKUjaRKgstQqNGvmZl54g06kA+hqtSE2FTG9gngXr3UJxYu/cNAjJYiBifm7+w4nbHbA==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.3.tgz", + "integrity": "sha512-dFXoa0TEhohrKcxn/54YKs1iwNeW6tUkHJgXW33H381SvjKFUV53WR231jh1sWVJETjA3vsAwxKwR23s7UCmUA==", "cpu": [ "x64" ], - "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "linux" @@ -3455,13 +3466,12 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.5.tgz", - "integrity": "sha512-Luj8y4OFYx4DHNQTWjdIuKTq2f5k6uSXICqx+FSabnXptaOBAbJHNbHT/06JZh6NRUouaf0mYXN0mcsqvkhd7Q==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.3.tgz", + "integrity": "sha512-ieyjisLB+ldexiE/yD8uomaZuZIbTc8tjquYln9Quh5ykOBY7LpJJYBWvWtm1g3pHv6AXlBI8Jay7Fffb6aLfA==", "cpu": [ "x64" ], - "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "linux" @@ -3471,13 +3481,12 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.5.tgz", - "integrity": "sha512-cZ6UpumhF9SDJvv4DA2fo9WIzlNFuKSkZpZmPG1c+4PFSEMy5DFOjBSllCvnqihCabzXzpn6ykCwBmHpy31vQw==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.3.tgz", + "integrity": "sha512-elTQpnaX5vESSbhCEgcwXjpMsnUbqqHfEpB7ewpkAsLzKEXZaK67ihSRYAuAx6ewRQTo7DS5iTT6X5aQD3MzMw==", "cpu": [ "arm64" ], - "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "win32" @@ -3487,13 +3496,12 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.5.tgz", - "integrity": "sha512-C5Yi/xIikrFUzZcyGj9L3RpKljFvKiDMtyDzPKzlsDrKIw2EYY+bF88gB6oGY5RGmv4DAX8dbnpRAqgFD0FMEw==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.3.tgz", + "integrity": "sha512-nvehQVEOdI1BleJpuUgPLrclJ0TzbEMc+MarXDmmiRFwEUGqj+pnfkTSb7RZyS1puU74IXdK/YhTirHurtbI9w==", "cpu": [ "ia32" ], - "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "win32" @@ -3503,13 +3511,12 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.5.tgz", - "integrity": "sha512-YrKdMVxbYmlfybCSbRtrilc6UA8GF5aPmGKBdPvjrarvsmf4i7ZHGCEnLtfOMd3Lwbs2WUZq3WdMbozYeLU93Q==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.3.tgz", + "integrity": "sha512-A+JSKGkRbPLVV2Kwx8TaDAV0yXIXm/gc8m98hSkVDGlPBBmydgzNdWy3X7HTUBM7IDk7YlWE7w2+RUGjdgpTmg==", "cpu": [ "x64" ], - "license": "Apache-2.0 AND MIT", "optional": true, "os": [ "win32" @@ -3611,6 +3618,11 @@ "integrity": "sha512-Jhy+MWRlro6UjVi578V/4ZGNfeCOcNCp0YaFNIUGFKlImowqwb1O/22wDVk3FDGMLqxdpOV3qQHD5fPEH4hK6A==", "license": "MIT" }, + "node_modules/@types/bluebird": { + "version": "3.5.42", + "resolved": "https://registry.npmjs.org/@types/bluebird/-/bluebird-3.5.42.tgz", + "integrity": "sha512-Jhy+MWRlro6UjVi578V/4ZGNfeCOcNCp0YaFNIUGFKlImowqwb1O/22wDVk3FDGMLqxdpOV3qQHD5fPEH4hK6A==" + }, "node_modules/@types/cli-progress": { "version": "3.11.6", "resolved": "https://registry.npmjs.org/@types/cli-progress/-/cli-progress-3.11.6.tgz", @@ -3636,9 +3648,9 @@ } }, "node_modules/@types/inquirer": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-9.0.9.tgz", - "integrity": "sha512-/mWx5136gts2Z2e5izdoRCo46lPp5TMs9R15GTSsgg/XnZyxDWVqoVU3R9lWnccKpqwsJLvRoxbCjoJtZB7DSw==", + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-9.0.8.tgz", + "integrity": "sha512-CgPD5kFGWsb8HJ5K7rfWlifao87m4ph8uioU7OTncJevmE/VLIqAAjfQtko578JZg7/f69K4FgqYym3gNr7DeA==", "license": "MIT", "dependencies": { "@types/through": "*", @@ -3742,12 +3754,12 @@ } }, "node_modules/@types/node": { - "version": "24.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz", - "integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==", + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", + "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", "license": "MIT", "dependencies": { - "undici-types": "~7.16.0" + "undici-types": "~7.10.0" } }, "node_modules/@types/node-cleanup": { @@ -3762,12 +3774,6 @@ "integrity": "sha512-kH8I7OSSwQu9DS9JYdFWbuvhVzvFRoCPCkGxNwoGgaPeDfEPJlcxNvEOypZhQ3XXHsGbfIuYcxcJxKUfJHnRfw==", "license": "MIT" }, - "node_modules/@types/pako": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz", - "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==", - "license": "MIT" - }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -3802,16 +3808,16 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.2.tgz", - "integrity": "sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.40.0.tgz", + "integrity": "sha512-w/EboPlBwnmOBtRbiOvzjD+wdiZdgFeo17lkltrtn7X37vagKKWJABvyfsJXTlHe6XBzugmYgd4A4nW+k8Mixw==", "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.46.2", - "@typescript-eslint/type-utils": "8.46.2", - "@typescript-eslint/utils": "8.46.2", - "@typescript-eslint/visitor-keys": "8.46.2", + "@typescript-eslint/scope-manager": "8.40.0", + "@typescript-eslint/type-utils": "8.40.0", + "@typescript-eslint/utils": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -3825,9 +3831,9 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.46.2", + "@typescript-eslint/parser": "^8.40.0", "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { @@ -3840,15 +3846,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.2.tgz", - "integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.40.0.tgz", + "integrity": "sha512-jCNyAuXx8dr5KJMkecGmZ8KI61KBUhkCob+SD+C+I5+Y1FWI2Y3QmY4/cxMCC5WAsZqoEtEETVhUiUMIGCf6Bw==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.46.2", - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/typescript-estree": "8.46.2", - "@typescript-eslint/visitor-keys": "8.46.2", + "@typescript-eslint/scope-manager": "8.40.0", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0", "debug": "^4.3.4" }, "engines": { @@ -3860,17 +3866,17 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.2.tgz", - "integrity": "sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.40.0.tgz", + "integrity": "sha512-/A89vz7Wf5DEXsGVvcGdYKbVM9F7DyFXj52lNYUDS1L9yJfqjW/fIp5PgMuEJL/KeqVTe2QSbXAGUZljDUpArw==", "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.46.2", - "@typescript-eslint/types": "^8.46.2", + "@typescript-eslint/tsconfig-utils": "^8.40.0", + "@typescript-eslint/types": "^8.40.0", "debug": "^4.3.4" }, "engines": { @@ -3881,17 +3887,17 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.2.tgz", - "integrity": "sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.40.0.tgz", + "integrity": "sha512-y9ObStCcdCiZKzwqsE8CcpyuVMwRouJbbSrNuThDpv16dFAj429IkM6LNb1dZ2m7hK5fHyzNcErZf7CEeKXR4w==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/visitor-keys": "8.46.2" + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3902,9 +3908,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.2.tgz", - "integrity": "sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.40.0.tgz", + "integrity": "sha512-jtMytmUaG9d/9kqSl/W3E3xaWESo4hFDxAIHGVW/WKKtQhesnRIJSAJO6XckluuJ6KDB5woD1EiqknriCtAmcw==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3914,18 +3920,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.2.tgz", - "integrity": "sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.40.0.tgz", + "integrity": "sha512-eE60cK4KzAc6ZrzlJnflXdrMqOBaugeukWICO2rB0KNvwdIMaEaYiywwHMzA1qFpTxrLhN9Lp4E/00EgWcD3Ow==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/typescript-estree": "8.46.2", - "@typescript-eslint/utils": "8.46.2", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0", + "@typescript-eslint/utils": "8.40.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3938,13 +3944,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.2.tgz", - "integrity": "sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.40.0.tgz", + "integrity": "sha512-ETdbFlgbAmXHyFPwqUIYrfc12ArvpBhEVgGAxVYSwli26dn8Ko+lIo4Su9vI9ykTZdJn+vJprs/0eZU0YMAEQg==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3955,15 +3961,15 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.2.tgz", - "integrity": "sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.40.0.tgz", + "integrity": "sha512-k1z9+GJReVVOkc1WfVKs1vBrR5MIKKbdAjDTPvIK3L8De6KbFfPFt6BKpdkdk7rZS2GtC/m6yI5MYX+UsuvVYQ==", "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.46.2", - "@typescript-eslint/tsconfig-utils": "8.46.2", - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/visitor-keys": "8.46.2", + "@typescript-eslint/project-service": "8.40.0", + "@typescript-eslint/tsconfig-utils": "8.40.0", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -3979,14 +3985,13 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -3995,7 +4000,6 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -4007,15 +4011,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.2.tgz", - "integrity": "sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.40.0.tgz", + "integrity": "sha512-Cgzi2MXSZyAUOY+BFwGs17s7ad/7L+gKt6Y8rAVVWS+7o6wrjeFN4nVfTpbE25MNcxyJ+iYUXflbs2xR9h4UBg==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.46.2", - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/typescript-estree": "8.46.2" + "@typescript-eslint/scope-manager": "8.40.0", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4026,16 +4030,16 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <5.9.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.2.tgz", - "integrity": "sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.40.0.tgz", + "integrity": "sha512-8CZ47QwalyRjsypfwnbI3hKy5gJDPmrkLjkgMxhi0+DZZ2QNx2naS6/hWoVYUHU7LU2zleF68V9miaVZvhFfTA==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.46.2", + "@typescript-eslint/types": "8.40.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -4050,7 +4054,6 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -4317,8 +4320,7 @@ "node_modules/@zeit/schemas": { "version": "2.36.0", "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.36.0.tgz", - "integrity": "sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg==", - "license": "MIT" + "integrity": "sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg==" }, "node_modules/acorn": { "version": "8.15.0", @@ -4382,7 +4384,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "license": "ISC", "dependencies": { "string-width": "^4.1.0" } @@ -4470,14 +4471,12 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "license": "MIT" + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" }, "node_modules/argparse": { "version": "1.0.10", @@ -4537,10 +4536,9 @@ } }, "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==", - "license": "MIT", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/axios-cache-interceptor/-/axios-cache-interceptor-1.8.0.tgz", + "integrity": "sha512-cTNnPGJyQkxnWp0EWvE3NRvgURU5cWw/Qx3dIhXyHSM4Ip0c7EEe0I3an0Jwa549m1CAOg57ibj27YRNLmQCcg==", "dependencies": { "cache-parser": "1.2.5", "fast-defer": "1.1.8", @@ -4574,30 +4572,6 @@ "tough-cookie": ">=4.0.0" } }, - "node_modules/axios-cookiejar-support/node_modules/http-cookie-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-cookie-agent/-/http-cookie-agent-7.0.2.tgz", - "integrity": "sha512-aHaES6SOFtnSlmWu0yEaaQvu+QexUG2gscSAvMhJ7auzW8r/jYOgGrzuAm9G9nHbksuhz7Lw4zOwDHmfQaxZvw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.4" - }, - "engines": { - "node": ">=20.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/3846masa" - }, - "peerDependencies": { - "tough-cookie": "^4.0.0 || ^5.0.0", - "undici": "^7.0.0" - }, - "peerDependenciesMeta": { - "undici": { - "optional": true - } - } - }, "node_modules/babel-jest": { "version": "30.2.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.2.0.tgz", @@ -4748,12 +4722,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "license": "MIT" - }, "node_modules/bodec": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/bodec/-/bodec-0.1.0.tgz", @@ -4768,7 +4736,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz", "integrity": "sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==", - "license": "MIT", "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^7.0.0", @@ -4787,10 +4754,9 @@ } }, "node_modules/boxen/node_modules/ansi-regex": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", - "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", - "license": "MIT", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "engines": { "node": ">=12" }, @@ -4802,7 +4768,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "license": "MIT", "engines": { "node": ">=12" }, @@ -4814,7 +4779,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", - "license": "MIT", "engines": { "node": ">=14.16" }, @@ -4825,14 +4789,12 @@ "node_modules/boxen/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, "node_modules/boxen/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -4846,10 +4808,9 @@ } }, "node_modules/boxen/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -4864,7 +4825,6 @@ "version": "2.19.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=12.20" }, @@ -4876,7 +4836,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -4961,7 +4920,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "license": "MIT", "engines": { "node": ">= 0.8" } @@ -5062,9 +5020,9 @@ "integrity": "sha512-z0R4cT5357OEAVkP1CEFTHz1egpu2gYiWm2WJOY/sQDhojEXUYL4m3v2kYi5wER3PkMRL+GgfDhed2kGzrHSZA==" }, "node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.0.tgz", + "integrity": "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==", "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -5077,7 +5035,6 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", - "license": "MIT", "dependencies": { "chalk": "^4.1.2" }, @@ -5116,8 +5073,7 @@ "node_modules/chardet": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.0.tgz", - "integrity": "sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==", - "license": "MIT" + "integrity": "sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==" }, "node_modules/charm": { "version": "0.1.2", @@ -5223,7 +5179,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", - "license": "MIT", "engines": { "node": ">=10" }, @@ -5269,7 +5224,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "license": "ISC", "engines": { "node": ">= 12" } @@ -5278,7 +5232,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz", "integrity": "sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==", - "license": "MIT", "dependencies": { "arch": "^2.2.0", "execa": "^5.1.1", @@ -5339,12 +5292,15 @@ "license": "MIT" }, "node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.2.tgz", + "integrity": "sha512-e2hz5BzbUPcYlIRHo8ieAhYgoajrJr+hWoceg6E345TPsATMUKqDgzt8fSXZJJbxfpiPzkWyphz8yn8At7q3fA==", "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" + "color-convert": "^3.0.1", + "color-string": "^2.0.0" + }, + "engines": { + "node": ">=18" } }, "node_modules/color-convert": { @@ -5364,34 +5320,41 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.2.tgz", + "integrity": "sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA==", "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-string/node_modules/color-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz", + "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==", + "engines": { + "node": ">=12.20" } }, "node_modules/color/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.2.tgz", + "integrity": "sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg==", "dependencies": { - "color-name": "1.1.3" + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" } }, "node_modules/color/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", - "dependencies": { - "color": "^3.1.3", - "text-hex": "1.0.x" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz", + "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==", + "engines": { + "node": ">=12.20" } }, "node_modules/combined-stream": { @@ -5481,7 +5444,6 @@ "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -5596,10 +5558,9 @@ } }, "node_modules/dayjs": { - "version": "1.11.18", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz", - "integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==", - "license": "MIT" + "version": "1.11.15", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.15.tgz", + "integrity": "sha512-MC+DfnSWiM9APs7fpiurHGCoeIx0Gdl6QZBy+5lu8MbYKN5FZEXqOgrundfibdfhGZ15o9hzmZ2xJjZnbvgKXQ==" }, "node_modules/debug": { "version": "4.3.4", @@ -5640,7 +5601,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", "engines": { "node": ">=4.0.0" } @@ -5819,18 +5779,6 @@ "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" } }, - "node_modules/encoding-sniffer/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/enquirer": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", @@ -5854,10 +5802,9 @@ } }, "node_modules/epg-grabber": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/epg-grabber/-/epg-grabber-0.44.0.tgz", - "integrity": "sha512-M4k/PG1/OIbHV7p8rM23yDWig6WwtpB/LafRzealc8A50HDOGskmkvuhzxf1S34Oe8xL3aU529oW2vocrpijuA==", - "license": "MIT", + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/epg-grabber/-/epg-grabber-0.41.0.tgz", + "integrity": "sha512-975WApyNb/ICZWYHxE2i/MDSDQOWZN0F+VARWZR1eFJovAs2CffQXJnG2Qx28RG312M3I+B+GkyPv2GxKxhQCA==", "dependencies": { "@freearhey/core": "^0.14.0", "@types/bluebird": "^3.5.42", @@ -5883,17 +5830,27 @@ "epg-grabber": "dist/cli.js" }, "engines": { - "node": ">=22.12.0" + "node": ">=10.0.0" } }, - "node_modules/epg-parser": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/epg-parser/-/epg-parser-0.4.0.tgz", - "integrity": "sha512-SynflAhgfrgFjFnMVggIjzgLpg7EANYymy4Q1OBiaMcEYSaIjp8iw36djam8/4B7htaNPa5ckIQ+4TUwEc0pdA==", + "node_modules/epg-grabber/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==", "license": "MIT", "dependencies": { "dayjs": "^1.11.6", - "lodash.groupby": "^4.6.0", + "lodash": "^4.17.21", + "xml-js": "^1.6.11" + } + }, + "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==", + "dependencies": { + "dayjs": "^1.11.6", + "lodash": "^4.17.21", "xml-js": "^1.6.11" } }, @@ -6028,19 +5985,19 @@ } }, "node_modules/eslint": { - "version": "9.38.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.38.0.tgz", - "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.33.0.tgz", + "integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.1", - "@eslint/config-helpers": "^0.4.1", - "@eslint/core": "^0.16.0", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.38.0", - "@eslint/plugin-kit": "^0.4.0", + "@eslint/js": "9.33.0", + "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -6384,7 +6341,6 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -6400,7 +6356,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -6428,7 +6383,6 @@ "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "license": "ISC", "dependencies": { "reusify": "^1.0.4" } @@ -6588,9 +6542,9 @@ } }, "node_modules/fs-extra": { - "version": "11.3.2", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz", - "integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==", + "version": "11.3.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.1.tgz", + "integrity": "sha512-eXvGGwZ5CL17ZSwHWd3bbgk7UUpF6IFHtP57NYYakPvHOs8GDgDe5KJI36jIJzDkJ6eJjuzRA8eBQb6SkKue0g==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", @@ -6919,6 +6873,29 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/http-cookie-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-cookie-agent/-/http-cookie-agent-7.0.2.tgz", + "integrity": "sha512-aHaES6SOFtnSlmWu0yEaaQvu+QexUG2gscSAvMhJ7auzW8r/jYOgGrzuAm9G9nHbksuhz7Lw4zOwDHmfQaxZvw==", + "dependencies": { + "agent-base": "^7.1.4" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/3846masa" + }, + "peerDependencies": { + "tough-cookie": "^4.0.0 || ^5.0.0", + "undici": "^7.0.0" + }, + "peerDependenciesMeta": { + "undici": { + "optional": true + } + } + }, "node_modules/http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", @@ -6968,19 +6945,15 @@ } }, "node_modules/iconv-lite": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", - "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" } }, "node_modules/ignore": { @@ -7069,15 +7042,15 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "node_modules/inquirer": { - "version": "12.10.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.10.0.tgz", - "integrity": "sha512-K/epfEnDBZj2Q3NMDcgXWZye3nhSPeoJnOh8lcKWrldw54UEZfS4EmAMsAsmVbl7qKi+vjAsy39Sz4fbgRMewg==", + "version": "12.9.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.9.3.tgz", + "integrity": "sha512-Hpw2JWdrYY8xJSmhU05Idd5FPshQ1CZErH00WO+FK6fKxkBeqj+E+yFXSlERZLKtzWeQYFCMfl8U2TK9SvVbtQ==", "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.1", - "@inquirer/core": "^10.3.0", - "@inquirer/prompts": "^7.9.0", - "@inquirer/type": "^3.0.9", + "@inquirer/core": "^10.1.15", + "@inquirer/prompts": "^7.8.3", + "@inquirer/type": "^3.0.8", + "ansi-escapes": "^4.3.2", "mute-stream": "^2.0.0", "run-async": "^4.0.5", "rxjs": "^7.8.2" @@ -7182,7 +7155,6 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "license": "MIT", "bin": { "is-docker": "cli.js" }, @@ -7259,7 +7231,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-port-reachable/-/is-port-reachable-4.0.0.tgz", "integrity": "sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==", - "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -7315,7 +7286,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "license": "MIT", "dependencies": { "is-docker": "^2.0.0" }, @@ -9034,12 +9004,11 @@ } }, "node_modules/libxml2-wasm": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/libxml2-wasm/-/libxml2-wasm-0.6.0.tgz", - "integrity": "sha512-CQ6bJT10o49FkL6O/zLYNf4DAm2eE6LH+ELMLf1z9842CXjgWejfOc5Fu/Yn/d6EnqTPrJbuB4i3D0MaYJ92Og==", - "license": "MIT", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/libxml2-wasm/-/libxml2-wasm-0.5.0.tgz", + "integrity": "sha512-ANq8aMCg/+pYJv3QqgrvYzJldvm2P2V2T08303AVyzjdeCuOAOjxPUSazQj/NA2+rOcS9BMx/HTTtq1I2g8foQ==", "engines": { - "node": ">=18" + "node": ">=16" } }, "node_modules/lie": { @@ -9125,12 +9094,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "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==", - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -9236,7 +9199,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", "engines": { "node": ">= 8" } @@ -9293,13 +9255,9 @@ } }, "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, "node_modules/minipass": { "version": "7.1.2", @@ -9465,6 +9423,16 @@ "node": ">=6.0.0" } }, + "node_modules/node-ensure": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz", + "integrity": "sha1-7K52QVDemYYexcgQ/V0Jaxg5Mqc=" + }, + "node_modules/node-gzip": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/node-gzip/-/node-gzip-1.1.2.tgz", + "integrity": "sha512-ZB6zWpfZHGtxZnPMrJSKHVPrRjURoUzaDbLFj3VO70mpLTW5np96vXyHwft4Id0o+PYIzgDkBUjIzaNHhQ8srw==" + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -9486,10 +9454,9 @@ } }, "node_modules/normalize-url": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.1.0.tgz", - "integrity": "sha512-X06Mfd/5aKsRHc0O0J5CUedwnPmnDtLF2+nq+KN9KSDlJHkPuh0JUviWjEWMe0SW/9TDdSLVPuk7L5gGTIA1/w==", - "license": "MIT", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", "engines": { "node": ">=14.16" }, @@ -9793,8 +9760,7 @@ "node_modules/path-is-inside": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", - "license": "(WTFPL OR MIT)" + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==" }, "node_modules/path-key": { "version": "3.1.1", @@ -9838,8 +9804,7 @@ "node_modules/path-to-regexp": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", - "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", - "license": "MIT" + "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==" }, "node_modules/pdf-parse": { "version": "2.4.5", @@ -9996,36 +9961,36 @@ } }, "node_modules/pm2": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/pm2/-/pm2-6.0.13.tgz", - "integrity": "sha512-1hS/adMgKoDpX4S1ichJW8SiGpex+oBSZK31dP1FSYOOGtaeuemXzhXPOCefmddgIY4K6v7uu+7xNPnmEnK3ag==", + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/pm2/-/pm2-6.0.10.tgz", + "integrity": "sha512-sbk4HsnhtJMx1wJlhFQhYfDRzHtVK+cvdrIezbjM9WjSyc7kLtQ4nZ5K7JLOdLe3AevytmRcTiOa3VvAQrve2A==", "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", "ansis": "4.0.0-node10", - "async": "3.2.6", - "chokidar": "3.6.0", - "cli-tableau": "2.0.1", + "async": "~3.2.6", + "blessed": "0.1.81", + "chokidar": "^3.5.3", + "cli-tableau": "^2.0.0", "commander": "2.15.1", - "croner": "4.1.97", - "dayjs": "1.11.15", - "debug": "4.4.3", + "croner": "~4.1.92", + "dayjs": "~1.11.13", + "debug": "^4.3.7", "enquirer": "2.3.6", "eventemitter2": "5.0.1", "fclone": "1.0.11", - "js-yaml": "4.1.0", + "js-yaml": "~4.1.0", "mkdirp": "1.0.4", "needle": "2.4.0", - "pidusage": "3.0.2", + "pidusage": "~3.0", "pm2-axon": "~4.0.1", "pm2-axon-rpc": "~0.7.1", "pm2-deploy": "~1.0.2", "pm2-multimeter": "^0.1.2", - "promptly": "2.2.0", - "semver": "7.7.2", + "promptly": "^2", + "semver": "^7.6.2", "source-map-support": "0.5.21", "sprintf-js": "1.1.2", "vizion": "~2.2.1" @@ -10144,9 +10109,10 @@ "license": "MIT" }, "node_modules/pm2/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -10320,14 +10286,12 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/range-parser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==", - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -10336,7 +10300,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -10351,7 +10314,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -10406,7 +10368,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", - "license": "MIT", "dependencies": { "rc": "^1.1.6", "safe-buffer": "^5.0.1" @@ -10416,7 +10377,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==", - "license": "MIT", "dependencies": { "rc": "^1.0.1" }, @@ -10437,7 +10397,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -10509,7 +10468,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -10541,7 +10499,6 @@ "url": "https://feross.org/support" } ], - "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } @@ -10599,8 +10556,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "license": "MIT" + ] }, "node_modules/safe-regex-test": { "version": "1.1.0", @@ -10677,7 +10633,6 @@ "version": "6.1.6", "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz", "integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==", - "license": "MIT", "dependencies": { "bytes": "3.0.0", "content-disposition": "0.5.2", @@ -10692,7 +10647,6 @@ "version": "1.33.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -10701,7 +10655,6 @@ "version": "2.1.18", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", - "license": "MIT", "dependencies": { "mime-db": "~1.33.0" }, @@ -10713,7 +10666,6 @@ "version": "8.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", @@ -10729,7 +10681,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", - "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -10740,8 +10691,7 @@ "node_modules/serve/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, "node_modules/set-function-length": { "version": "1.2.2", @@ -10870,19 +10820,6 @@ "node": ">=4" } }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, "node_modules/skip-postinstall": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/skip-postinstall/-/skip-postinstall-1.0.0.tgz", @@ -11241,22 +11178,20 @@ "license": "MIT" }, "node_modules/tldts": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.12.tgz", - "integrity": "sha512-M9ZQBPp6FyqhMcl233vHYyYRkxXOA1SKGlnq13S0mJdUhRSwr2w6I8rlchPL73wBwRlyIZpFvpu2VcdSMWLYXw==", - "license": "MIT", + "version": "6.1.68", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.68.tgz", + "integrity": "sha512-JKF17jROiYkjJPT73hUTEiTp2OBCf+kAlB+1novk8i6Q6dWjHsgEjw9VLiipV4KTJavazXhY1QUXyQFSem2T7w==", "dependencies": { - "tldts-core": "^7.0.12" + "tldts-core": "^6.1.68" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.12.tgz", - "integrity": "sha512-3K76aXywJFduGRsOYoY5JzINLs/WMlOkeDwPL+8OCPq2Rh39gkSDtWAxdJQlWjpun/xF/LHf29yqCi6VC/rHDA==", - "license": "MIT" + "version": "6.1.68", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.68.tgz", + "integrity": "sha512-85TdlS/DLW/gVdf2oyyzqp3ocS30WxjaL4la85EArl9cHUR/nizifKAJPziWewSZjDZS71U517/i6ciUeqtB5Q==" }, "node_modules/tmpl": { "version": "1.0.5", @@ -11276,12 +11211,12 @@ } }, "node_modules/tough-cookie": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz", - "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", "license": "BSD-3-Clause", "dependencies": { - "tldts": "^7.0.5" + "tldts": "^6.1.32" }, "engines": { "node": ">=16" @@ -11315,7 +11250,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", - "license": "MIT", "engines": { "node": ">=18.12" }, @@ -11329,9 +11263,9 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/tsx": { - "version": "4.20.6", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", - "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", + "version": "4.20.4", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.4.tgz", + "integrity": "sha512-yyxBKfORQ7LuRt/BQKBXrpcq59ZvSW0XxwfjAt3w2/8PmdxaFzijtMhTawprSHhpzeM5BgU2hXHG3lklIERZXg==", "license": "MIT", "dependencies": { "esbuild": "~0.25.0", @@ -11397,9 +11331,9 @@ } }, "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -11418,9 +11352,9 @@ } }, "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", "license": "MIT" }, "node_modules/universal-user-agent": { @@ -11517,7 +11451,6 @@ "version": "1.5.4", "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.4.tgz", "integrity": "sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==", - "license": "MIT", "dependencies": { "registry-auth-token": "3.3.2", "registry-url": "3.1.0" @@ -11632,18 +11565,6 @@ "node": ">=18" } }, - "node_modules/whatwg-encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/whatwg-mimetype": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", @@ -11692,7 +11613,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", - "license": "MIT", "dependencies": { "string-width": "^5.0.1" }, @@ -11704,10 +11624,9 @@ } }, "node_modules/widest-line/node_modules/ansi-regex": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", - "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", - "license": "MIT", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "engines": { "node": ">=12" }, @@ -11718,14 +11637,12 @@ "node_modules/widest-line/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, "node_modules/widest-line/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -11739,10 +11656,9 @@ } }, "node_modules/widest-line/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -11760,12 +11676,12 @@ "license": "ISC" }, "node_modules/winston": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", - "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "version": "3.18.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.18.3.tgz", + "integrity": "sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww==", "dependencies": { "@colors/colors": "^1.6.0", - "@dabh/diagnostics": "^2.0.2", + "@dabh/diagnostics": "^2.0.8", "async": "^3.2.3", "is-stream": "^2.0.0", "logform": "^2.7.0", @@ -11797,7 +11713,6 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -11941,9 +11856,9 @@ } }, "node_modules/yoctocolors-cjs": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", - "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", "license": "MIT", "engines": { "node": ">=18" @@ -12280,11 +12195,11 @@ "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==" }, "@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", "requires": { - "colorspace": "1.1.x", + "@so-ric/colorspace": "^1.1.6", "enabled": "2.0.x", "kuler": "^2.0.0" } @@ -12496,17 +12411,14 @@ } }, "@eslint/config-helpers": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.1.tgz", - "integrity": "sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==", - "requires": { - "@eslint/core": "^0.16.0" - } + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==" }, "@eslint/core": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz", - "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "requires": { "@types/json-schema": "^7.0.15" } @@ -12548,9 +12460,9 @@ } }, "@eslint/js": { - "version": "9.38.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.38.0.tgz", - "integrity": "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==" + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.33.0.tgz", + "integrity": "sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==" }, "@eslint/object-schema": { "version": "2.1.7", @@ -12558,11 +12470,11 @@ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==" }, "@eslint/plugin-kit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz", - "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "requires": { - "@eslint/core": "^0.16.0", + "@eslint/core": "^0.15.2", "levn": "^0.4.1" } }, @@ -12577,9 +12489,9 @@ } }, "@freearhey/core": { - "version": "0.14.3", - "resolved": "https://registry.npmjs.org/@freearhey/core/-/core-0.14.3.tgz", - "integrity": "sha512-w/kaoUdZlbqvOSOmid0nrBI9iGZkUZ+KLZUfkWpfZRPXOEu/FxlEP5gPANGgovwijrgogAbVf/qnf9VV+429ag==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@freearhey/core/-/core-0.10.3.tgz", + "integrity": "sha512-in5wgFHo+PJF585TXpaPLInlsgXuDLbUpXfcWrctd17mJpNNr5Mt5DetpzBqSqLERgh2jxxGRHyPGhm3Yl02hQ==", "requires": { "@types/lodash": "^4.14.198", "@types/pako": "^2.0.3", @@ -12649,9 +12561,9 @@ "integrity": "sha512-yqq0aJW/5XPhi5xOAL1xRCpe1eh8UFVgYFpFsjEqmIR8rKLyP+HINvFXwUaxYICflJrVlxnp7lLN6As735kVpw==" }, "@inquirer/checkbox": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.0.tgz", - "integrity": "sha512-5+Q3PKH35YsnoPTh75LucALdAxom6xh5D1oeY561x4cqBuH24ZFVyFREPe14xgnrtmGu3EEt1dIi60wRVSnGCw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.1.tgz", + "integrity": "sha512-bevKGO6kX1eM/N+pdh9leS5L7TBF4ICrzi9a+cbWkrxeAeIcwlo/7OfWGCDERdRCI2/Q6tjltX4bt07ALHDwFw==", "requires": { "@inquirer/ansi": "^1.0.1", "@inquirer/core": "^10.3.0", @@ -12661,9 +12573,9 @@ } }, "@inquirer/confirm": { - "version": "5.1.19", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.19.tgz", - "integrity": "sha512-wQNz9cfcxrtEnUyG5PndC8g3gZ7lGDBzmWiXZkX8ot3vfZ+/BLjR8EvyGX4YzQLeVqtAlY/YScZpW7CW8qMoDQ==", + "version": "5.1.15", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.15.tgz", + "integrity": "sha512-SwHMGa8Z47LawQN0rog0sT+6JpiL0B7eW9p1Bb7iCeKDGTI5Ez25TSc2l8kw52VV7hA4sX/C78CGkMrKXfuspA==", "requires": { "@inquirer/core": "^10.3.0", "@inquirer/type": "^3.0.9" @@ -12722,7 +12634,17 @@ "integrity": "sha512-yy9cOoBnx58TlsPrIxauKIFQTiyH+0MK4e97y4sV9ERbI+zDxw7i2hxHLCIEGIE/8PPvDxGhgzIOTSOWcs6/MQ==", "requires": { "chardet": "^2.1.0", - "iconv-lite": "^0.7.0" + "iconv-lite": "^0.6.3" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } } }, "@inquirer/figures": { @@ -12759,20 +12681,20 @@ } }, "@inquirer/prompts": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.9.0.tgz", - "integrity": "sha512-X7/+dG9SLpSzRkwgG5/xiIzW0oMrV3C0HOa7YHG1WnrLK+vCQHfte4k/T80059YBdei29RBC3s+pSMvPJDU9/A==", + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.8.3.tgz", + "integrity": "sha512-iHYp+JCaCRktM/ESZdpHI51yqsDgXu+dMs4semzETftOaF8u5hwlqnbIsuIR/LrWZl8Pm1/gzteK9I7MAq5HTA==", "requires": { - "@inquirer/checkbox": "^4.3.0", - "@inquirer/confirm": "^5.1.19", - "@inquirer/editor": "^4.2.21", - "@inquirer/expand": "^4.0.21", - "@inquirer/input": "^4.2.5", - "@inquirer/number": "^3.0.21", - "@inquirer/password": "^4.0.21", - "@inquirer/rawlist": "^4.1.9", - "@inquirer/search": "^3.2.0", - "@inquirer/select": "^4.4.0" + "@inquirer/checkbox": "^4.2.1", + "@inquirer/confirm": "^5.1.15", + "@inquirer/editor": "^4.2.17", + "@inquirer/expand": "^4.0.17", + "@inquirer/input": "^4.2.1", + "@inquirer/number": "^3.0.17", + "@inquirer/password": "^4.0.17", + "@inquirer/rawlist": "^4.1.5", + "@inquirer/search": "^3.1.0", + "@inquirer/select": "^4.3.1" } }, "@inquirer/rawlist": { @@ -12814,6 +12736,46 @@ "integrity": "sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w==", "requires": {} }, + "@iptv-org/sdk": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@iptv-org/sdk/-/sdk-1.1.0.tgz", + "integrity": "sha512-/lEIcWGmbBSWWgS8uOKEFdfGBYPlEOcDrETHqU4k3VxsnggL46mvIsA8+ZvCV5Fa3sADD2ded5FfjDmYhoX2DA==", + "requires": { + "@freearhey/core": "^0.14.3", + "@freearhey/search-js": "^0.2.0", + "@ntlab/sfetch": "^1.2.0", + "axios": "^1.11.0", + "dayjs": "^1.11.18" + }, + "dependencies": { + "@freearhey/core": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/@freearhey/core/-/core-0.14.3.tgz", + "integrity": "sha512-w/kaoUdZlbqvOSOmid0nrBI9iGZkUZ+KLZUfkWpfZRPXOEu/FxlEP5gPANGgovwijrgogAbVf/qnf9VV+429ag==", + "requires": { + "@types/lodash": "^4.14.198", + "@types/pako": "^2.0.3", + "consola": "^3.4.2", + "dayjs": "^1.11.13", + "glob": "^11.0.1", + "lodash": "^4.17.21", + "natural-orderby": "^5.0.0", + "normalize-url": "^8.1.0", + "object-treeify": "^2.1.1", + "pako": "^2.1.0", + "timer-node": "^5.0.9" + } + }, + "@freearhey/search-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@freearhey/search-js/-/search-js-0.2.0.tgz", + "integrity": "sha512-1sxfCRbxM12Js3nM/S51cVKLYEjoksERidz539bleMAXes44eTC2m0TEQTJzJyE7l1pw2qUwsIhjd2l2l88fSw==", + "requires": { + "lodash": "^4.17.21" + } + } + } + }, "@isaacs/balanced-match": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", @@ -13816,11 +13778,6 @@ } } }, - "@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", @@ -13948,13 +13905,22 @@ "@sinonjs/commons": "^3.0.1" } }, - "@stylistic/eslint-plugin": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.5.0.tgz", - "integrity": "sha512-IeZF+8H0ns6prg4VrkhgL+yrvDXWDH2cKchrbh80ejG9dQgZWp10epHMbgRuQvgchLII/lfh6Xn3lu6+6L86Hw==", + "@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", "requires": { - "@eslint-community/eslint-utils": "^4.9.0", - "@typescript-eslint/types": "^8.46.1", + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, + "@stylistic/eslint-plugin": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.2.3.tgz", + "integrity": "sha512-oY7GVkJGVMI5benlBDCaRrSC1qPasafyv5dOBLLv5MTilMGnErKhO6ziEfodDDIZbo5QxPUNW360VudJOFODMw==", + "requires": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/types": "^8.38.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "estraverse": "^5.3.0", @@ -13974,82 +13940,82 @@ } }, "@swc/core": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.5.tgz", - "integrity": "sha512-WezcBo8a0Dg2rnR82zhwoR6aRNxeTGfK5QCD6TQ+kg3xx/zNT02s/0o+81h/3zhvFSB24NtqEr8FTw88O5W/JQ==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.3.tgz", + "integrity": "sha512-ZaDETVWnm6FE0fc+c2UE8MHYVS3Fe91o5vkmGfgwGXFbxYvAjKSqxM/j4cRc9T7VZNSJjriXq58XkfCp3Y6f+w==", "requires": { - "@swc/core-darwin-arm64": "1.13.5", - "@swc/core-darwin-x64": "1.13.5", - "@swc/core-linux-arm-gnueabihf": "1.13.5", - "@swc/core-linux-arm64-gnu": "1.13.5", - "@swc/core-linux-arm64-musl": "1.13.5", - "@swc/core-linux-x64-gnu": "1.13.5", - "@swc/core-linux-x64-musl": "1.13.5", - "@swc/core-win32-arm64-msvc": "1.13.5", - "@swc/core-win32-ia32-msvc": "1.13.5", - "@swc/core-win32-x64-msvc": "1.13.5", + "@swc/core-darwin-arm64": "1.13.3", + "@swc/core-darwin-x64": "1.13.3", + "@swc/core-linux-arm-gnueabihf": "1.13.3", + "@swc/core-linux-arm64-gnu": "1.13.3", + "@swc/core-linux-arm64-musl": "1.13.3", + "@swc/core-linux-x64-gnu": "1.13.3", + "@swc/core-linux-x64-musl": "1.13.3", + "@swc/core-win32-arm64-msvc": "1.13.3", + "@swc/core-win32-ia32-msvc": "1.13.3", + "@swc/core-win32-x64-msvc": "1.13.3", "@swc/counter": "^0.1.3", "@swc/types": "^0.1.24" } }, "@swc/core-darwin-arm64": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.5.tgz", - "integrity": "sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.13.3.tgz", + "integrity": "sha512-ux0Ws4pSpBTqbDS9GlVP354MekB1DwYlbxXU3VhnDr4GBcCOimpocx62x7cFJkSpEBF8bmX8+/TTCGKh4PbyXw==", "optional": true }, "@swc/core-darwin-x64": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.5.tgz", - "integrity": "sha512-ILd38Fg/w23vHb0yVjlWvQBoE37ZJTdlLHa8LRCFDdX4WKfnVBiblsCU9ar4QTMNdeTBEX9iUF4IrbNWhaF1Ng==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.13.3.tgz", + "integrity": "sha512-p0X6yhxmNUOMZrbeZ3ZNsPige8lSlSe1llllXvpCLkKKxN/k5vZt1sULoq6Nj4eQ7KeHQVm81/+AwKZyf/e0TA==", "optional": true }, "@swc/core-linux-arm-gnueabihf": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.5.tgz", - "integrity": "sha512-Q6eS3Pt8GLkXxqz9TAw+AUk9HpVJt8Uzm54MvPsqp2yuGmY0/sNaPPNVqctCX9fu/Nu8eaWUen0si6iEiCsazQ==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.13.3.tgz", + "integrity": "sha512-OmDoiexL2fVWvQTCtoh0xHMyEkZweQAlh4dRyvl8ugqIPEVARSYtaj55TBMUJIP44mSUOJ5tytjzhn2KFxFcBA==", "optional": true }, "@swc/core-linux-arm64-gnu": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.5.tgz", - "integrity": "sha512-aNDfeN+9af+y+M2MYfxCzCy/VDq7Z5YIbMqRI739o8Ganz6ST+27kjQFd8Y/57JN/hcnUEa9xqdS3XY7WaVtSw==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.13.3.tgz", + "integrity": "sha512-STfKku3QfnuUj6k3g9ld4vwhtgCGYIFQmsGPPgT9MK/dI3Lwnpe5Gs5t1inoUIoGNP8sIOLlBB4HV4MmBjQuhw==", "optional": true }, "@swc/core-linux-arm64-musl": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.5.tgz", - "integrity": "sha512-9+ZxFN5GJag4CnYnq6apKTnnezpfJhCumyz0504/JbHLo+Ue+ZtJnf3RhyA9W9TINtLE0bC4hKpWi8ZKoETyOQ==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.13.3.tgz", + "integrity": "sha512-bc+CXYlFc1t8pv9yZJGus372ldzOVscBl7encUBlU1m/Sig0+NDJLz6cXXRcFyl6ABNOApWeR4Yl7iUWx6C8og==", "optional": true }, "@swc/core-linux-x64-gnu": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.5.tgz", - "integrity": "sha512-WD530qvHrki8Ywt/PloKUjaRKgstQqNGvmZl54g06kA+hqtSE2FTG9gngXr3UJxYu/cNAjJYiBifm7+w4nbHbA==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.13.3.tgz", + "integrity": "sha512-dFXoa0TEhohrKcxn/54YKs1iwNeW6tUkHJgXW33H381SvjKFUV53WR231jh1sWVJETjA3vsAwxKwR23s7UCmUA==", "optional": true }, "@swc/core-linux-x64-musl": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.5.tgz", - "integrity": "sha512-Luj8y4OFYx4DHNQTWjdIuKTq2f5k6uSXICqx+FSabnXptaOBAbJHNbHT/06JZh6NRUouaf0mYXN0mcsqvkhd7Q==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.13.3.tgz", + "integrity": "sha512-ieyjisLB+ldexiE/yD8uomaZuZIbTc8tjquYln9Quh5ykOBY7LpJJYBWvWtm1g3pHv6AXlBI8Jay7Fffb6aLfA==", "optional": true }, "@swc/core-win32-arm64-msvc": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.5.tgz", - "integrity": "sha512-cZ6UpumhF9SDJvv4DA2fo9WIzlNFuKSkZpZmPG1c+4PFSEMy5DFOjBSllCvnqihCabzXzpn6ykCwBmHpy31vQw==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.13.3.tgz", + "integrity": "sha512-elTQpnaX5vESSbhCEgcwXjpMsnUbqqHfEpB7ewpkAsLzKEXZaK67ihSRYAuAx6ewRQTo7DS5iTT6X5aQD3MzMw==", "optional": true }, "@swc/core-win32-ia32-msvc": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.5.tgz", - "integrity": "sha512-C5Yi/xIikrFUzZcyGj9L3RpKljFvKiDMtyDzPKzlsDrKIw2EYY+bF88gB6oGY5RGmv4DAX8dbnpRAqgFD0FMEw==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.13.3.tgz", + "integrity": "sha512-nvehQVEOdI1BleJpuUgPLrclJ0TzbEMc+MarXDmmiRFwEUGqj+pnfkTSb7RZyS1puU74IXdK/YhTirHurtbI9w==", "optional": true }, "@swc/core-win32-x64-msvc": { - "version": "1.13.5", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.5.tgz", - "integrity": "sha512-YrKdMVxbYmlfybCSbRtrilc6UA8GF5aPmGKBdPvjrarvsmf4i7ZHGCEnLtfOMd3Lwbs2WUZq3WdMbozYeLU93Q==", + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.13.3.tgz", + "integrity": "sha512-A+JSKGkRbPLVV2Kwx8TaDAV0yXIXm/gc8m98hSkVDGlPBBmydgzNdWy3X7HTUBM7IDk7YlWE7w2+RUGjdgpTmg==", "optional": true }, "@swc/counter": { @@ -14154,9 +14120,9 @@ } }, "@types/inquirer": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-9.0.9.tgz", - "integrity": "sha512-/mWx5136gts2Z2e5izdoRCo46lPp5TMs9R15GTSsgg/XnZyxDWVqoVU3R9lWnccKpqwsJLvRoxbCjoJtZB7DSw==", + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/inquirer/-/inquirer-9.0.8.tgz", + "integrity": "sha512-CgPD5kFGWsb8HJ5K7rfWlifao87m4ph8uioU7OTncJevmE/VLIqAAjfQtko578JZg7/f69K4FgqYym3gNr7DeA==", "requires": { "@types/through": "*", "rxjs": "^7.2.0" @@ -14248,11 +14214,11 @@ } }, "@types/node": { - "version": "24.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz", - "integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==", + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", + "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", "requires": { - "undici-types": "~7.16.0" + "undici-types": "~7.10.0" } }, "@types/node-cleanup": { @@ -14302,15 +14268,15 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" }, "@typescript-eslint/eslint-plugin": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.2.tgz", - "integrity": "sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.40.0.tgz", + "integrity": "sha512-w/EboPlBwnmOBtRbiOvzjD+wdiZdgFeo17lkltrtn7X37vagKKWJABvyfsJXTlHe6XBzugmYgd4A4nW+k8Mixw==", "requires": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.46.2", - "@typescript-eslint/type-utils": "8.46.2", - "@typescript-eslint/utils": "8.46.2", - "@typescript-eslint/visitor-keys": "8.46.2", + "@typescript-eslint/scope-manager": "8.40.0", + "@typescript-eslint/type-utils": "8.40.0", + "@typescript-eslint/utils": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -14325,68 +14291,68 @@ } }, "@typescript-eslint/parser": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.2.tgz", - "integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.40.0.tgz", + "integrity": "sha512-jCNyAuXx8dr5KJMkecGmZ8KI61KBUhkCob+SD+C+I5+Y1FWI2Y3QmY4/cxMCC5WAsZqoEtEETVhUiUMIGCf6Bw==", "requires": { - "@typescript-eslint/scope-manager": "8.46.2", - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/typescript-estree": "8.46.2", - "@typescript-eslint/visitor-keys": "8.46.2", + "@typescript-eslint/scope-manager": "8.40.0", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0", "debug": "^4.3.4" } }, "@typescript-eslint/project-service": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.2.tgz", - "integrity": "sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.40.0.tgz", + "integrity": "sha512-/A89vz7Wf5DEXsGVvcGdYKbVM9F7DyFXj52lNYUDS1L9yJfqjW/fIp5PgMuEJL/KeqVTe2QSbXAGUZljDUpArw==", "requires": { - "@typescript-eslint/tsconfig-utils": "^8.46.2", - "@typescript-eslint/types": "^8.46.2", + "@typescript-eslint/tsconfig-utils": "^8.40.0", + "@typescript-eslint/types": "^8.40.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.2.tgz", - "integrity": "sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.40.0.tgz", + "integrity": "sha512-y9ObStCcdCiZKzwqsE8CcpyuVMwRouJbbSrNuThDpv16dFAj429IkM6LNb1dZ2m7hK5fHyzNcErZf7CEeKXR4w==", "requires": { - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/visitor-keys": "8.46.2" + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0" } }, "@typescript-eslint/tsconfig-utils": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.2.tgz", - "integrity": "sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.40.0.tgz", + "integrity": "sha512-jtMytmUaG9d/9kqSl/W3E3xaWESo4hFDxAIHGVW/WKKtQhesnRIJSAJO6XckluuJ6KDB5woD1EiqknriCtAmcw==", "requires": {} }, "@typescript-eslint/type-utils": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.2.tgz", - "integrity": "sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.40.0.tgz", + "integrity": "sha512-eE60cK4KzAc6ZrzlJnflXdrMqOBaugeukWICO2rB0KNvwdIMaEaYiywwHMzA1qFpTxrLhN9Lp4E/00EgWcD3Ow==", "requires": { - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/typescript-estree": "8.46.2", - "@typescript-eslint/utils": "8.46.2", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0", + "@typescript-eslint/utils": "8.40.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" } }, "@typescript-eslint/types": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.2.tgz", - "integrity": "sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==" + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.40.0.tgz", + "integrity": "sha512-ETdbFlgbAmXHyFPwqUIYrfc12ArvpBhEVgGAxVYSwli26dn8Ko+lIo4Su9vI9ykTZdJn+vJprs/0eZU0YMAEQg==" }, "@typescript-eslint/typescript-estree": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.2.tgz", - "integrity": "sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.40.0.tgz", + "integrity": "sha512-k1z9+GJReVVOkc1WfVKs1vBrR5MIKKbdAjDTPvIK3L8De6KbFfPFt6BKpdkdk7rZS2GtC/m6yI5MYX+UsuvVYQ==", "requires": { - "@typescript-eslint/project-service": "8.46.2", - "@typescript-eslint/tsconfig-utils": "8.46.2", - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/visitor-keys": "8.46.2", + "@typescript-eslint/project-service": "8.40.0", + "@typescript-eslint/tsconfig-utils": "8.40.0", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/visitor-keys": "8.40.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -14414,22 +14380,22 @@ } }, "@typescript-eslint/utils": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.2.tgz", - "integrity": "sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.40.0.tgz", + "integrity": "sha512-Cgzi2MXSZyAUOY+BFwGs17s7ad/7L+gKt6Y8rAVVWS+7o6wrjeFN4nVfTpbE25MNcxyJ+iYUXflbs2xR9h4UBg==", "requires": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.46.2", - "@typescript-eslint/types": "8.46.2", - "@typescript-eslint/typescript-estree": "8.46.2" + "@typescript-eslint/scope-manager": "8.40.0", + "@typescript-eslint/types": "8.40.0", + "@typescript-eslint/typescript-estree": "8.40.0" } }, "@typescript-eslint/visitor-keys": { - "version": "8.46.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.2.tgz", - "integrity": "sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==", + "version": "8.40.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.40.0.tgz", + "integrity": "sha512-8CZ47QwalyRjsypfwnbI3hKy5gJDPmrkLjkgMxhi0+DZZ2QNx2naS6/hWoVYUHU7LU2zleF68V9miaVZvhFfTA==", "requires": { - "@typescript-eslint/types": "8.46.2", + "@typescript-eslint/types": "8.40.0", "eslint-visitor-keys": "^4.2.1" }, "dependencies": { @@ -14725,16 +14691,6 @@ "integrity": "sha512-4Bzj+l63eGwnWDBFdJHeGS6Ij3ytpyqvo//ocsb5kCLN/rKthzk27Afh2iSkZtuudOBkHUWWIcyCb4GKhXqovQ==", "requires": { "http-cookie-agent": "^7.0.2" - }, - "dependencies": { - "http-cookie-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-cookie-agent/-/http-cookie-agent-7.0.2.tgz", - "integrity": "sha512-aHaES6SOFtnSlmWu0yEaaQvu+QexUG2gscSAvMhJ7auzW8r/jYOgGrzuAm9G9nHbksuhz7Lw4zOwDHmfQaxZvw==", - "requires": { - "agent-base": "^7.1.4" - } - } } }, "babel-jest": { @@ -14838,11 +14794,6 @@ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==" }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, "bodec": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/bodec/-/bodec-0.1.0.tgz", @@ -14869,9 +14820,9 @@ }, "dependencies": { "ansi-regex": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", - "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==" + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" }, "ansi-styles": { "version": "6.2.1", @@ -14899,9 +14850,9 @@ } }, "strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", "requires": { "ansi-regex": "^6.0.1" } @@ -15025,9 +14976,9 @@ "integrity": "sha512-z0R4cT5357OEAVkP1CEFTHz1egpu2gYiWm2WJOY/sQDhojEXUYL4m3v2kYi5wER3PkMRL+GgfDhed2kGzrHSZA==" }, "chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==" + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.0.tgz", + "integrity": "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==" }, "chalk-template": { "version": "0.4.0", @@ -15209,26 +15160,26 @@ "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==" }, "color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.2.tgz", + "integrity": "sha512-e2hz5BzbUPcYlIRHo8ieAhYgoajrJr+hWoceg6E345TPsATMUKqDgzt8fSXZJJbxfpiPzkWyphz8yn8At7q3fA==", "requires": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" + "color-convert": "^3.0.1", + "color-string": "^2.0.0" }, "dependencies": { "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.2.tgz", + "integrity": "sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg==", "requires": { - "color-name": "1.1.3" + "color-name": "^2.0.0" } }, "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz", + "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==" } } }, @@ -15246,21 +15197,18 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.2.tgz", + "integrity": "sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA==", "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", - "requires": { - "color": "^3.1.3", - "text-hex": "1.0.x" + "color-name": "^2.0.0" + }, + "dependencies": { + "color-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz", + "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==" + } } }, "combined-stream": { @@ -15556,16 +15504,6 @@ "requires": { "iconv-lite": "^0.6.3", "whatwg-encoding": "^3.1.1" - }, - "dependencies": { - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } } }, "enquirer": { @@ -15605,15 +15543,27 @@ "socks-proxy-agent": "^8.0.5", "winston": "^3.17.0", "xml-js": "^1.6.11" + }, + "dependencies": { + "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==", + "requires": { + "dayjs": "^1.11.6", + "lodash": "^4.17.21", + "xml-js": "^1.6.11" + } + } } }, "epg-parser": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/epg-parser/-/epg-parser-0.4.0.tgz", - "integrity": "sha512-SynflAhgfrgFjFnMVggIjzgLpg7EANYymy4Q1OBiaMcEYSaIjp8iw36djam8/4B7htaNPa5ckIQ+4TUwEc0pdA==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/epg-parser/-/epg-parser-0.3.1.tgz", + "integrity": "sha512-y131hXfDthUdSeKbN0Ru1wiFF5er4t/TLT+IaAnHF2CYB0cnygHTJteQMDYIlHWHDsGj+z9ejm1cU3saFNF3nQ==", "requires": { "dayjs": "^1.11.6", - "lodash.groupby": "^4.6.0", + "lodash": "^4.17.21", "xml-js": "^1.6.11" } }, @@ -15708,18 +15658,18 @@ } }, "eslint": { - "version": "9.38.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.38.0.tgz", - "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.33.0.tgz", + "integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==", "requires": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.1", - "@eslint/config-helpers": "^0.4.1", - "@eslint/core": "^0.16.0", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.38.0", - "@eslint/plugin-kit": "^0.4.0", + "@eslint/js": "9.33.0", + "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -16280,6 +16230,14 @@ } } }, + "http-cookie-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-cookie-agent/-/http-cookie-agent-7.0.2.tgz", + "integrity": "sha512-aHaES6SOFtnSlmWu0yEaaQvu+QexUG2gscSAvMhJ7auzW8r/jYOgGrzuAm9G9nHbksuhz7Lw4zOwDHmfQaxZvw==", + "requires": { + "agent-base": "^7.1.4" + } + }, "http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", @@ -16309,9 +16267,9 @@ "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==" }, "iconv-lite": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", - "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "requires": { "safer-buffer": ">= 2.1.2 < 3.0.0" } @@ -16376,14 +16334,14 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "inquirer": { - "version": "12.10.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.10.0.tgz", - "integrity": "sha512-K/epfEnDBZj2Q3NMDcgXWZye3nhSPeoJnOh8lcKWrldw54UEZfS4EmAMsAsmVbl7qKi+vjAsy39Sz4fbgRMewg==", + "version": "12.9.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.9.3.tgz", + "integrity": "sha512-Hpw2JWdrYY8xJSmhU05Idd5FPshQ1CZErH00WO+FK6fKxkBeqj+E+yFXSlERZLKtzWeQYFCMfl8U2TK9SvVbtQ==", "requires": { - "@inquirer/ansi": "^1.0.1", - "@inquirer/core": "^10.3.0", - "@inquirer/prompts": "^7.9.0", - "@inquirer/type": "^3.0.9", + "@inquirer/core": "^10.1.15", + "@inquirer/prompts": "^7.8.3", + "@inquirer/type": "^3.0.8", + "ansi-escapes": "^4.3.2", "mute-stream": "^2.0.0", "run-async": "^4.0.5", "rxjs": "^7.8.2" @@ -17751,9 +17709,9 @@ } }, "libxml2-wasm": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/libxml2-wasm/-/libxml2-wasm-0.6.0.tgz", - "integrity": "sha512-CQ6bJT10o49FkL6O/zLYNf4DAm2eE6LH+ELMLf1z9842CXjgWejfOc5Fu/Yn/d6EnqTPrJbuB4i3D0MaYJ92Og==" + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/libxml2-wasm/-/libxml2-wasm-0.5.0.tgz", + "integrity": "sha512-ANq8aMCg/+pYJv3QqgrvYzJldvm2P2V2T08303AVyzjdeCuOAOjxPUSazQj/NA2+rOcS9BMx/HTTtq1I2g8foQ==" }, "lie": { "version": "3.1.1", @@ -17821,11 +17779,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "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", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -17944,9 +17897,9 @@ } }, "minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, "minipass": { "version": "7.1.2", @@ -18064,6 +18017,16 @@ "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-4.2.1.tgz", "integrity": "sha512-lgimEHPE/QDgFlywTd8yTR61ptugX3Qer29efeyWw2rv259HtGBNn1vZVmp8lB9uo9wC0t/AT4iGqXxia+CJFg==" }, + "node-ensure": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/node-ensure/-/node-ensure-0.0.0.tgz", + "integrity": "sha1-7K52QVDemYYexcgQ/V0Jaxg5Mqc=" + }, + "node-gzip": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/node-gzip/-/node-gzip-1.1.2.tgz", + "integrity": "sha512-ZB6zWpfZHGtxZnPMrJSKHVPrRjURoUzaDbLFj3VO70mpLTW5np96vXyHwft4Id0o+PYIzgDkBUjIzaNHhQ8srw==" + }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -18427,37 +18390,37 @@ } }, "pm2": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/pm2/-/pm2-6.0.13.tgz", - "integrity": "sha512-1hS/adMgKoDpX4S1ichJW8SiGpex+oBSZK31dP1FSYOOGtaeuemXzhXPOCefmddgIY4K6v7uu+7xNPnmEnK3ag==", + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/pm2/-/pm2-6.0.10.tgz", + "integrity": "sha512-sbk4HsnhtJMx1wJlhFQhYfDRzHtVK+cvdrIezbjM9WjSyc7kLtQ4nZ5K7JLOdLe3AevytmRcTiOa3VvAQrve2A==", "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", "ansis": "4.0.0-node10", - "async": "3.2.6", - "chokidar": "3.6.0", - "cli-tableau": "2.0.1", + "async": "~3.2.6", + "blessed": "0.1.81", + "chokidar": "^3.5.3", + "cli-tableau": "^2.0.0", "commander": "2.15.1", - "croner": "4.1.97", - "dayjs": "1.11.15", - "debug": "4.4.3", + "croner": "~4.1.92", + "dayjs": "~1.11.13", + "debug": "^4.3.7", "enquirer": "2.3.6", "eventemitter2": "5.0.1", "fclone": "1.0.11", - "js-yaml": "4.1.0", + "js-yaml": "~4.1.0", "mkdirp": "1.0.4", "needle": "2.4.0", - "pidusage": "3.0.2", + "pidusage": "~3.0", "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.2.0", - "semver": "7.7.2", + "promptly": "^2", + "semver": "^7.6.2", "source-map-support": "0.5.21", "sprintf-js": "1.1.2", "vizion": "~2.2.1" @@ -18479,9 +18442,9 @@ "integrity": "sha512-MC+DfnSWiM9APs7fpiurHGCoeIx0Gdl6QZBy+5lu8MbYKN5FZEXqOgrundfibdfhGZ15o9hzmZ2xJjZnbvgKXQ==" }, "debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "requires": { "ms": "^2.1.3" } @@ -19027,21 +18990,6 @@ } } }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "requires": { - "is-arrayish": "^0.3.1" - }, - "dependencies": { - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - } - } - }, "skip-postinstall": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/skip-postinstall/-/skip-postinstall-1.0.0.tgz", @@ -19272,17 +19220,17 @@ "integrity": "sha512-zXxCE/5/YDi0hY9pygqgRqjRbrFRzigYxOudG0I3syaqAAmX9/w9sxex1bNFCN6c1S66RwPtEIJv65dN+1psew==" }, "tldts": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.12.tgz", - "integrity": "sha512-M9ZQBPp6FyqhMcl233vHYyYRkxXOA1SKGlnq13S0mJdUhRSwr2w6I8rlchPL73wBwRlyIZpFvpu2VcdSMWLYXw==", + "version": "6.1.68", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.68.tgz", + "integrity": "sha512-JKF17jROiYkjJPT73hUTEiTp2OBCf+kAlB+1novk8i6Q6dWjHsgEjw9VLiipV4KTJavazXhY1QUXyQFSem2T7w==", "requires": { - "tldts-core": "^7.0.12" + "tldts-core": "^6.1.68" } }, "tldts-core": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.12.tgz", - "integrity": "sha512-3K76aXywJFduGRsOYoY5JzINLs/WMlOkeDwPL+8OCPq2Rh39gkSDtWAxdJQlWjpun/xF/LHf29yqCi6VC/rHDA==" + "version": "6.1.68", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.68.tgz", + "integrity": "sha512-85TdlS/DLW/gVdf2oyyzqp3ocS30WxjaL4la85EArl9cHUR/nizifKAJPziWewSZjDZS71U517/i6ciUeqtB5Q==" }, "tmpl": { "version": "1.0.5", @@ -19298,11 +19246,11 @@ } }, "tough-cookie": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz", - "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", "requires": { - "tldts": "^7.0.5" + "tldts": "^6.1.32" } }, "transliteration": { @@ -19330,9 +19278,9 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "tsx": { - "version": "4.20.6", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.6.tgz", - "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", + "version": "4.20.4", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.4.tgz", + "integrity": "sha512-yyxBKfORQ7LuRt/BQKBXrpcq59ZvSW0XxwfjAt3w2/8PmdxaFzijtMhTawprSHhpzeM5BgU2hXHG3lklIERZXg==", "requires": { "esbuild": "~0.25.0", "fsevents": "~2.3.3", @@ -19372,9 +19320,9 @@ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" }, "typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==" + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==" }, "undici": { "version": "7.12.0", @@ -19382,9 +19330,9 @@ "integrity": "sha512-GrKEsc3ughskmGA9jevVlIOPMiiAHJ4OFUtaAH+NhfTUSiZ1wMPIQqQvAJUrJspFXJt3EBWgpAeoHEDVT1IBug==" }, "undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==" + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==" }, "universal-user-agent": { "version": "7.0.3", @@ -19534,16 +19482,6 @@ "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", "requires": { "iconv-lite": "0.6.3" - }, - "dependencies": { - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } } }, "whatwg-mimetype": { @@ -19582,9 +19520,9 @@ }, "dependencies": { "ansi-regex": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", - "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==" + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" }, "emoji-regex": { "version": "9.2.2", @@ -19602,9 +19540,9 @@ } }, "strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", "requires": { "ansi-regex": "^6.0.1" } @@ -19617,12 +19555,12 @@ "integrity": "sha512-wldeCaczs8XXq7hj+5d/F38JE2r7EXgb6WQDM84RVwxy81T/sxB5e9+uZLK9Q9oNz1mlvjut+QtvgaOQFPVq/g==" }, "winston": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", - "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", + "version": "3.18.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.18.3.tgz", + "integrity": "sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww==", "requires": { "@colors/colors": "^1.6.0", - "@dabh/diagnostics": "^2.0.2", + "@dabh/diagnostics": "^2.0.8", "async": "^3.2.3", "is-stream": "^2.0.0", "logform": "^2.7.0", diff --git a/package.json b/package.json index 918da0b3..73682b17 100644 --- a/package.json +++ b/package.json @@ -2,11 +2,13 @@ "name": "epg", "scripts": { "act:check": "act pull_request -W .github/workflows/check.yml", + "act:format": "act workflow_dispatch -W .github/workflows/format.yml", "act:update": "act workflow_dispatch -W .github/workflows/update.yml", "api:load": "tsx scripts/commands/api/load.ts", "api:generate": "tsx scripts/commands/api/generate.ts", "channels:lint": "tsx scripts/commands/channels/lint.mts", "channels:parse": "tsx scripts/commands/channels/parse.ts", + "channels:format": "tsx scripts/commands/channels/format.ts", "channels:edit": "tsx scripts/commands/channels/edit.ts", "channels:validate": "tsx scripts/commands/channels/validate.ts", "sites:init": "tsx scripts/commands/sites/init.ts", @@ -32,92 +34,94 @@ ], "testTimeout": 10000, "transformIgnorePatterns": [ - "/node_modules/(?!parse-duration/.*|glob/.*|srcset/.*|balanced-match/.*|minimatch/.*)" + "/node_modules/(?!parse-duration/.*|@freearhey/core/.*|glob/.*|srcset/.*|balanced-match/.*|minimatch/.*)" ] }, "dependencies": { "@alex_neo/jest-expect-message": "^1.0.5", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "^9.38.0", + "@eslint/js": "^9.32.0", "@freearhey/chronos": "^0.0.1", - "@freearhey/core": "^0.14.3", - "@freearhey/search-js": "^0.2.0", + "@freearhey/core": "^0.13.2", + "@freearhey/search-js": "^0.1.2", "@freearhey/storage-js": "^0.2.0", + "@iptv-org/sdk": "^1.1.0", "@ntlab/sfetch": "^1.2.0", - "@octokit/core": "^7.0.5", - "@octokit/plugin-paginate-rest": "^13.2.1", - "@octokit/plugin-rest-endpoint-methods": "^16.1.1", - "@stylistic/eslint-plugin": "^5.5.0", - "@swc/core": "^1.13.5", + "@octokit/core": "^7.0.3", + "@octokit/plugin-paginate-rest": "^13.1.1", + "@octokit/plugin-rest-endpoint-methods": "^16.0.0", + "@stylistic/eslint-plugin": "^5.2.2", + "@swc/core": "^1.13.2", "@swc/jest": "^0.2.39", "@types/cli-progress": "^3.11.6", "@types/fs-extra": "^11.0.4", - "@types/inquirer": "^9.0.9", + "@types/inquirer": "^9.0.8", "@types/jest": "^30.0.0", "@types/langs": "^2.0.5", "@types/lodash.orderby": "^4.6.9", "@types/lodash.sortby": "^4.7.9", "@types/lodash.startcase": "^4.4.9", "@types/lodash.uniqby": "^4.7.9", - "@types/node": "^24.9.1", + "@types/node": "^24.1.0", "@types/node-cleanup": "^2.1.5", "@types/numeral": "^2.0.5", - "@typescript-eslint/eslint-plugin": "^8.46.2", - "@typescript-eslint/parser": "^8.46.2", - "axios": "^1.12.2", + "@types/pako": "^2.0.4", + "@typescript-eslint/eslint-plugin": "^8.38.0", + "@typescript-eslint/parser": "^8.38.0", + "axios": "^1.11.0", "axios-cookiejar-support": "^6.0.4", - "chalk": "^5.6.2", + "chalk": "^5.4.1", "cheerio": "^1.1.2", "cli-progress": "^3.12.0", - "commander": "^14.0.1", + "commander": "^14.0.0", "consola": "^3.4.2", - "cross-env": "^10.1.0", + "cross-env": "^10.0.0", "csv-parser": "^3.2.0", "cwait": "^1.1.2", - "dayjs": "^1.11.18", + "dayjs": "^1.11.13", "epg-grabber": "^0.44.0", - "epg-parser": "^0.4.0", - "eslint": "^9.38.0", + "epg-parser": "^0.3.1", + "eslint": "^9.32.0", "eslint-config-prettier": "^10.1.8", "form-data": "^4.0.4", - "fs-extra": "^11.3.2", + "fs-extra": "^11.3.0", "glob": "^11.0.3", - "globals": "^16.4.0", + "globals": "^16.3.0", "husky": "^9.1.7", - "iconv-lite": "^0.7.0", - "inquirer": "^12.10.0", - "jest": "^30.2.0", + "iconv-lite": "^0.6.3", + "inquirer": "^12.8.2", + "jest": "^30.0.5", "jest-offline": "^1.0.1", "langs": "^2.0.0", - "libxml2-wasm": "^0.6.0", + "libxml2-wasm": "^0.5.0", "lodash.orderby": "^4.6.0", "lodash.sortby": "^4.7.0", "lodash.startcase": "^4.4.0", "lodash.uniqby": "^4.7.0", - "luxon": "^3.7.2", + "luxon": "^3.7.1", "mockdate": "^3.0.5", "nedb-promises": "^6.2.3", "node-cleanup": "^2.1.2", "numeral": "^2.0.6", "pako": "^2.1.0", "parse-duration": "^2.1.4", - "pdf-parse": "^2.4.5", - "pm2": "^6.0.13", + "pdf-parse": "^1.1.1", + "pm2": "^6.0.8", "readline": "^1.3.0", "run-script-os": "^1.1.6", - "serve": "^14.2.5", + "serve": "^14.2.4", "signale": "^1.4.0", "skip-postinstall": "^1.0.0", "socks-proxy-agent": "^8.0.5", - "srcset": "^5.0.2", + "srcset": "^5.0.1", "table2array": "^0.0.2", "tabletojson": "^4.1.6", - "tough-cookie": "^6.0.0", + "tough-cookie": "^5.1.2", "transliteration": "^2.3.5", - "tsx": "^4.20.6", - "typescript": "^5.9.3", + "tsx": "^4.20.3", + "typescript": "^5.8.3", "unzipit": "^1.4.3", - "uuid": "^13.0.0", + "uuid": "^11.1.0", "wildcard-match": "^5.1.4" } } diff --git a/scripts/api.ts b/scripts/api.ts new file mode 100644 index 00000000..4535b721 --- /dev/null +++ b/scripts/api.ts @@ -0,0 +1,117 @@ +import { Collection, Dictionary } from '@freearhey/core' +import { DATA_DIR } from './constants' +import cliProgress from 'cli-progress' +import * as sdk from '@iptv-org/sdk' + +const data = { + channelsKeyById: new Dictionary(), + feedsKeyByStreamId: new Dictionary(), + feedsGroupedByChannelId: new Dictionary() +} + +interface SearchIndex { + search: (query: string) => sdk.Types.ChannelSearchableData[] +} + +let searchIndex: SearchIndex + +async function loadData() { + const dataManager = new sdk.DataManager({ dataDir: DATA_DIR }) + await dataManager.loadFromDisk() + dataManager.processData() + + const { channels, feeds } = dataManager.getProcessedData() + + data.channelsKeyById = channels.keyBy((channel: sdk.Models.Channel) => channel.id) + data.feedsKeyByStreamId = feeds.keyBy((feed: sdk.Models.Feed) => feed.getStreamId()) + data.feedsGroupedByChannelId = feeds.groupBy((feed: sdk.Models.Feed) => feed.channel) + + searchIndex = sdk.SearchEngine.createIndex(channels) +} + +async function downloadData() { + function formatBytes(bytes: number) { + if (bytes === 0) return '0 B' + const k = 1024 + const sizes = ['B', 'KB', 'MB', 'GB'] + const i = Math.floor(Math.log(bytes) / Math.log(k)) + return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i] + } + + const files = [ + 'blocklist', + 'categories', + 'channels', + 'cities', + 'countries', + 'feeds', + 'guides', + 'languages', + 'logos', + 'regions', + 'streams', + 'subdivisions', + 'timezones' + ] + + const multiBar = new cliProgress.MultiBar({ + stopOnComplete: true, + hideCursor: true, + forceRedraw: true, + barsize: 36, + format(options, params, payload) { + const filename = payload.filename.padEnd(18, ' ') + const barsize = options.barsize || 40 + const percent = (params.progress * 100).toFixed(2) + const speed = payload.speed ? formatBytes(payload.speed) + '/s' : 'N/A' + const total = formatBytes(params.total) + const completeSize = Math.round(params.progress * barsize) + const incompleteSize = barsize - completeSize + const bar = + options.barCompleteString && options.barIncompleteString + ? options.barCompleteString.substr(0, completeSize) + + options.barGlue + + options.barIncompleteString.substr(0, incompleteSize) + : '-'.repeat(barsize) + + return `${filename} [${bar}] ${percent}% | ETA: ${params.eta}s | ${total} | ${speed}` + } + }) + + const dataManager = new sdk.DataManager({ dataDir: DATA_DIR }) + + const requests: Promise[] = [] + for (const basename of files) { + const filename = `${basename}.json` + const progressBar = multiBar.create(0, 0, { filename }) + const request = dataManager.downloadFileToDisk(basename, { + onDownloadProgress({ total, loaded, rate }) { + if (total) progressBar.setTotal(total) + progressBar.update(loaded, { speed: rate }) + } + }) + + requests.push(request) + } + + await Promise.allSettled(requests).catch(console.error) +} + +function searchChannels(query: string): Collection { + if (!searchIndex) return new Collection() + + const results = searchIndex.search(query) + + const channels = new Collection() + + new Collection(results).forEach( + (item: sdk.Types.ChannelSearchableData) => { + const channel = data.channelsKeyById.get(item.id) + if (channel) channels.add(channel) + } + ) + + return channels +} + +export { data, loadData, downloadData, searchChannels } diff --git a/scripts/commands/api/generate.ts b/scripts/commands/api/generate.ts index 17b03aa3..0ca61940 100644 --- a/scripts/commands/api/generate.ts +++ b/scripts/commands/api/generate.ts @@ -1,9 +1,9 @@ -import { Logger, Collection } from '@freearhey/core' -import { Storage } from '@freearhey/storage-js' +import { ChannelGuideObject } from '../../types/channel' import { SITES_DIR, API_DIR } from '../../constants' -import { GuideChannel } from '../../models' -import { ChannelsParser } from '../../core' -import epgGrabber from 'epg-grabber' +import { Logger, Collection } from '@freearhey/core' +import epgGrabber, { EPGGrabber } from 'epg-grabber' +import { Storage } from '@freearhey/storage-js' +import { Channel } from '../../models' import path from 'path' async function main() { @@ -13,24 +13,25 @@ async function main() { logger.info('loading channels...') const sitesStorage = new Storage(SITES_DIR) - const parser = new ChannelsParser({ - storage: sitesStorage - }) const files: string[] = await sitesStorage.list('**/*.channels.xml') - const channels = new Collection() + const channels = new Collection() for (const filepath of files) { - const channelList = await parser.parse(filepath) + const xml = await sitesStorage.load(filepath) + const parsedChannels = EPGGrabber.parseChannelsXML(xml) + const channelsFromXML = new Collection(parsedChannels).map( + (channel: epgGrabber.Channel) => new Channel(channel.toObject()) + ) - channelList.channels.forEach((data: epgGrabber.Channel) => { - channels.add(new GuideChannel(data)) + channelsFromXML.forEach((channel: Channel) => { + channels.add(channel) }) } logger.info(`found ${channels.count()} channel(s)`) - const output = channels.map((channel: GuideChannel) => channel.toJSON()) + const output = channels.map((channel: Channel) => channel.getGuideObject()) const apiStorage = new Storage(API_DIR) const outputFilename = 'guides.json' diff --git a/scripts/commands/api/load.ts b/scripts/commands/api/load.ts index 8ce068e9..2ab0b182 100644 --- a/scripts/commands/api/load.ts +++ b/scripts/commands/api/load.ts @@ -1,25 +1,7 @@ -import { DATA_DIR } from '../../constants.js' -import { DataLoader } from '../../core/dataLoader.js' +import { downloadData } from '../../api' async function main() { - const { Storage } = await import('@freearhey/storage-js') - const storage = new Storage(DATA_DIR) - const loader = new DataLoader({ storage }) - - await Promise.all([ - loader.download('blocklist.json'), - loader.download('categories.json'), - loader.download('channels.json'), - loader.download('countries.json'), - loader.download('languages.json'), - loader.download('regions.json'), - loader.download('subdivisions.json'), - loader.download('feeds.json'), - loader.download('timezones.json'), - loader.download('guides.json'), - loader.download('streams.json'), - loader.download('logos.json') - ]) + await downloadData() } main() diff --git a/scripts/commands/channels/edit.ts b/scripts/commands/channels/edit.ts index 8298343a..650d8b07 100644 --- a/scripts/commands/channels/edit.ts +++ b/scripts/commands/channels/edit.ts @@ -1,21 +1,25 @@ -import { Collection, Logger, Dictionary } from '@freearhey/core' -import { Storage } from '@freearhey/storage-js' -import type { DataProcessorData } from '../../types/dataProcessor' -import type { DataLoaderData } from '../../types/dataLoader' -import { ChannelSearchableData } from '../../types/channel' -import { Channel, ChannelList, Feed } from '../../models' -import { DataProcessor, DataLoader } from '../../core' +import { loadData, data, searchChannels } from '../../api' +import epgGrabber, { EPGGrabber } from 'epg-grabber' +import { Collection, Logger } from '@freearhey/core' import { select, input } from '@inquirer/prompts' -import { ChannelsParser } from '../../core' -import { DATA_DIR } from '../../constants' +import { generateChannelsXML } from '../../core' +import { Storage } from '@freearhey/storage-js' +import { Channel } from '../../models' import nodeCleanup from 'node-cleanup' -import sjs from '@freearhey/search-js' -import epgGrabber from 'epg-grabber' +import * as sdk from '@iptv-org/sdk' import { Command } from 'commander' import readline from 'readline' -interface ChoiceValue { type: string; value?: Feed | Channel } -interface Choice { name: string; short?: string; value: ChoiceValue; default?: boolean } +interface ChoiceValue { + type: string + value?: sdk.Models.Feed | sdk.Models.Channel +} +interface Choice { + name: string + short?: string + value: ChoiceValue + default?: boolean +} if (process.platform === 'win32') { readline @@ -35,11 +39,11 @@ program.argument('', 'Path to *.channels.xml file to edit').parse(proc const filepath = program.args[0] const logger = new Logger() const storage = new Storage() -let channelList = new ChannelList({ channels: [] }) +let channelsFromXML = new Collection() main(filepath) nodeCleanup(() => { - save(filepath, channelList) + save(filepath, channelsFromXML) }) export default async function main(filepath: string) { @@ -48,67 +52,46 @@ export default async function main(filepath: string) { } logger.info('loading data from api...') - const processor = new DataProcessor() - const dataStorage = new Storage(DATA_DIR) - const loader = new DataLoader({ storage: dataStorage }) - const data: DataLoaderData = await loader.load() - const { channels, channelsKeyById, feedsGroupedByChannelId }: DataProcessorData = - processor.process(data) + await loadData() logger.info('loading channels...') - const parser = new ChannelsParser({ storage }) - channelList = await parser.parse(filepath) - const parsedChannelsWithoutId = channelList.channels.filter( - (channel: epgGrabber.Channel) => !channel.xmltv_id + const xml = await storage.load(filepath) + const parsedChannels = EPGGrabber.parseChannelsXML(xml) + channelsFromXML = new Collection(parsedChannels).map( + (channel: epgGrabber.Channel) => new Channel(channel.toObject()) ) + const channelsFromXMLWithoutId = channelsFromXML.filter((channel: Channel) => !channel.xmltv_id) logger.info( - `found ${channelList.channels.count()} channels (including ${parsedChannelsWithoutId.count()} without ID)` + `found ${channelsFromXML.count()} channels (including ${channelsFromXMLWithoutId.count()} without ID)` ) - logger.info('creating search index...') - const items = channels.map((channel: Channel) => channel.getSearchable()).all() - const searchIndex = sjs.createIndex(items, { - searchable: ['name', 'altNames', 'guideNames', 'streamNames', 'feedFullNames'] - }) + logger.info('starting...') + console.log() - logger.info('starting...\n') - - for (const channel of parsedChannelsWithoutId.all()) { + for (const channel of channelsFromXMLWithoutId.all()) { try { - channel.xmltv_id = await selectChannel( - channel, - searchIndex, - feedsGroupedByChannelId, - channelsKeyById - ) - } catch (err) { - logger.info(err.message) + channel.xmltv_id = await selectChannel(channel) + } catch { break } } - parsedChannelsWithoutId.forEach((channel: epgGrabber.Channel) => { + channelsFromXMLWithoutId.forEach((channel: epgGrabber.Channel) => { if (channel.xmltv_id === '-') { channel.xmltv_id = '' } }) } -async function selectChannel( - channel: epgGrabber.Channel, - searchIndex, - feedsGroupedByChannelId: Dictionary, - channelsKeyById: Dictionary -): Promise { +async function selectChannel(channel: epgGrabber.Channel): Promise { const query = escapeRegex(channel.name) - const similarChannels = searchIndex - .search(query) - .map((item: ChannelSearchableData) => channelsKeyById.get(item.id)) + const similarChannels = searchChannels(query) + const choices = getChoicesForChannel(similarChannels).all() const selected: ChoiceValue = await select({ message: `Select channel ID for "${channel.name}" (${channel.site_id}):`, - choices: getChannelChoises(new Collection(similarChannels)), + choices, pageSize: 10 }) @@ -118,14 +101,14 @@ async function selectChannel( case 'type': { const typedChannelId = await input({ message: ' Channel ID:' }) if (!typedChannelId) return '' - const selectedFeedId = await selectFeed(typedChannelId, feedsGroupedByChannelId) + const selectedFeedId = await selectFeed(typedChannelId) if (selectedFeedId === '-') return typedChannelId return [typedChannelId, selectedFeedId].join('@') } case 'channel': { const selectedChannel = selected.value if (!selectedChannel) return '' - const selectedFeedId = await selectFeed(selectedChannel.id || '', feedsGroupedByChannelId) + const selectedFeedId = await selectFeed(selectedChannel.id || '') if (selectedFeedId === '-') return selectedChannel.id || '' return [selectedChannel.id, selectedFeedId].join('@') } @@ -134,11 +117,9 @@ async function selectChannel( return '' } -async function selectFeed(channelId: string, feedsGroupedByChannelId: Dictionary): Promise { - const channelFeeds = feedsGroupedByChannelId.has(channelId) - ? new Collection(feedsGroupedByChannelId.get(channelId)) - : new Collection() - const choices = getFeedChoises(channelFeeds) +async function selectFeed(channelId: string): Promise { + const channelFeeds = new Collection(data.feedsGroupedByChannelId.get(channelId)) + const choices = getChoicesForFeed(channelFeeds).all() const selected: ChoiceValue = await select({ message: `Select feed ID for "${channelId}":`, @@ -160,13 +141,13 @@ async function selectFeed(channelId: string, feedsGroupedByChannelId: Dictionary return '' } -function getChannelChoises(channels: Collection): Choice[] { - const choises: Choice[] = [] +function getChoicesForChannel(channels: Collection): Collection { + const choices = new Collection() - channels.forEach((channel: Channel) => { - const names = new Collection([channel.name, ...channel.getAltNames().all()]).uniq().join(', ') + channels.forEach((channel: sdk.Models.Channel) => { + const names = new Collection([channel.name, ...channel.alt_names]).uniq().join(', ') - choises.push({ + choices.add({ value: { type: 'channel', value: channel @@ -176,40 +157,42 @@ function getChannelChoises(channels: Collection): Choice[] { }) }) - choises.push({ name: 'Type...', value: { type: 'type' } }) - choises.push({ name: 'Skip', value: { type: 'skip' } }) + choices.add({ name: 'Type...', value: { type: 'type' } }) + choices.add({ name: 'Skip', value: { type: 'skip' } }) - return choises + return choices } -function getFeedChoises(feeds: Collection): Choice[] { - const choises: Choice[] = [] +function getChoicesForFeed(feeds: Collection): Collection { + const choices = new Collection() - feeds.forEach((feed: Feed) => { + feeds.forEach((feed: sdk.Models.Feed) => { let name = `${feed.id} (${feed.name})` - if (feed.isMain) name += ' [main]' + if (feed.is_main) name += ' [main]' - choises.push({ + choices.add({ value: { type: 'feed', value: feed }, - default: feed.isMain, + default: feed.is_main, name, short: feed.id }) }) - choises.push({ name: 'Type...', value: { type: 'type' } }) - choises.push({ name: 'Skip', value: { type: 'skip' } }) + choices.add({ name: 'Type...', value: { type: 'type' } }) + choices.add({ name: 'Skip', value: { type: 'skip' } }) - return choises + return choices } -function save(filepath: string, channelList: ChannelList) { +function save(filepath: string, channelsFromXML: Collection) { if (!storage.existsSync(filepath)) return - storage.saveSync(filepath, channelList.toString()) - logger.info(`\nFile '${filepath}' successfully saved`) + const xml = generateChannelsXML(channelsFromXML) + storage.saveSync(filepath, xml) + console.log() + logger.info(`File '${filepath}' successfully saved`) } function escapeRegex(string: string) { diff --git a/scripts/commands/channels/format.ts b/scripts/commands/channels/format.ts new file mode 100644 index 00000000..d830a484 --- /dev/null +++ b/scripts/commands/channels/format.ts @@ -0,0 +1,60 @@ +import { Collection, Logger } from '@freearhey/core' +import epgGrabber, { EPGGrabber } from 'epg-grabber' +import { generateChannelsXML } from '../../core' +import { Storage } from '@freearhey/storage-js' +import { SITES_DIR } from '../../constants' +import { data, loadData } from '../../api' +import { Channel } from '../../models' +import { program } from 'commander' + +program.argument('[filepath...]', 'Path to file to format').parse(process.argv) + +async function main() { + const logger = new Logger() + + logger.info('loading data from api...') + await loadData() + + logger.info('loading *.channels.xml files...') + const storage = new Storage() + const files = program.args.length + ? program.args + : await storage.list(`${SITES_DIR}/**/*.channels.xml`) + + logger.info(`found ${files.length} file(s)`) + + logger.info('formating...') + for (const filepath of files) { + if (!storage.existsSync(filepath)) continue + + const xml = await storage.load(filepath) + const parsedChannels = EPGGrabber.parseChannelsXML(xml) + const channelsFromXML = new Collection(parsedChannels).map( + (channel: epgGrabber.Channel) => new Channel(channel.toObject()) + ) + + channelsFromXML.forEach((channel: Channel) => { + if (!channel.xmltv_id) return + if (data.feedsKeyByStreamId.get(channel.xmltv_id)) return + + const channelData = data.channelsKeyById.get(channel.xmltv_id) + if (channelData) { + const mainFeed = channelData.getMainFeed() + if (mainFeed) { + channel.xmltv_id = mainFeed.getStreamId() + return + } + } + + channel.xmltv_id = '' + }) + + channelsFromXML.sortBy((channel: Channel) => channel.site_id) + + const output = generateChannelsXML(channelsFromXML) + + await storage.save(filepath, output) + } +} + +main() diff --git a/scripts/commands/channels/lint.mts b/scripts/commands/channels/lint.mts index 47f1182e..016fe834 100644 --- a/scripts/commands/channels/lint.mts +++ b/scripts/commands/channels/lint.mts @@ -1,7 +1,7 @@ -import chalk from 'chalk' -import { program } from 'commander' -import { Storage, File } from '@freearhey/storage-js' import { XmlDocument, XsdValidator, XmlValidateError, ErrorDetail } from 'libxml2-wasm' +import { Storage, File } from '@freearhey/storage-js' +import { program } from 'commander' +import chalk from 'chalk' const xsd = ` diff --git a/scripts/commands/channels/parse.ts b/scripts/commands/channels/parse.ts index fa0fa35d..56e1f13a 100644 --- a/scripts/commands/channels/parse.ts +++ b/scripts/commands/channels/parse.ts @@ -1,11 +1,21 @@ -import { Logger } from '@freearhey/core' import { Storage, File } from '@freearhey/storage-js' -import { ChannelsParser } from '../../core' -import { ChannelList } from '../../models' +import { Collection, Logger } from '@freearhey/core' +import epgGrabber, { EPGGrabber } from 'epg-grabber' +import { generateChannelsXML } from '../../core' import { pathToFileURL } from 'node:url' -import epgGrabber from 'epg-grabber' +import { Channel } from '../../models' import { Command } from 'commander' +interface SiteConfigChannelData { + xmltv_id: string + name: string + site_id: string + lang?: string + logo?: string + url?: string + lcn?: string +} + const program = new Command() program .requiredOption('-c, --config ', 'Config file') @@ -33,17 +43,11 @@ async function main() { const storage = new Storage() const logger = new Logger() - const parser = new ChannelsParser({ storage }) const file = new File(options.config) const dir = file.dirname() const config = (await import(pathToFileURL(options.config).toString())).default const outputFilepath = options.output || `${dir}/${config.site}.channels.xml` - let channelList = new ChannelList({ channels: [] }) - if (await storage.exists(outputFilepath)) { - channelList = await parser.parse(outputFilepath) - } - const args: Record = {} if (Array.isArray(options.set)) { @@ -53,29 +57,43 @@ async function main() { }) } - let parsedChannels: epgGrabber.Channel[] | Promise = [] - - if (!config.channels || typeof config.channels !== 'function') { - logger.error(`Config file '${options.config}' does not export a channels(...) function`) - return + let channelsFromXML = new Collection() + if (await storage.exists(outputFilepath)) { + const xml = await storage.load(outputFilepath) + const parsedChannels = EPGGrabber.parseChannelsXML(xml) + channelsFromXML = new Collection(parsedChannels).map( + (channel: epgGrabber.Channel) => new Channel(channel.toObject()) + ) } - parsedChannels = config.channels(args) - - if (isPromise(parsedChannels)) { - parsedChannels = await parsedChannels + let configChannels = config.channels(args) + if (isPromise(configChannels)) { + configChannels = await configChannels } - parsedChannels = (parsedChannels as epgGrabber.Channel[]).map((channel: epgGrabber.Channel) => { - channel.site = config.site - return channel - }) + const channelsFromConfig = new Collection(configChannels).map( + (data: SiteConfigChannelData) => { + return new Channel({ + xmltv_id: data.xmltv_id, + name: data.name, + site_id: data.site_id, + lang: data.lang || null, + logo: data.logo || null, + url: data.url || null, + lcn: data.lcn || null, + site: config.site, + index: -1 + }) + } + ) - const newChannelList = new ChannelList({ channels: [] }) - parsedChannels.forEach((channel: epgGrabber.Channel) => { + const newChannelList = new Collection() + channelsFromConfig.forEach((channel: Channel) => { if (!channel.site_id) return - const found: epgGrabber.Channel | undefined = channelList.get(channel.site_id) + const found: Channel | undefined = channelsFromXML.find( + (_channel: Channel) => _channel.site_id == channel.site_id + ) if (found) { channel.xmltv_id = found.xmltv_id @@ -85,9 +103,15 @@ async function main() { newChannelList.add(channel) }) - newChannelList.sort() + newChannelList.sortBy([ + (channel: Channel) => channel.lang || '_', + (channel: Channel) => (channel.xmltv_id ? channel.xmltv_id.toLowerCase() : '0'), + (channel: Channel) => channel.site_id + ]) - await storage.save(outputFilepath, newChannelList.toString()) + const xml = generateChannelsXML(newChannelList) + + await storage.save(outputFilepath, xml) logger.info(`File '${outputFilepath}' successfully saved`) } diff --git a/scripts/commands/channels/validate.ts b/scripts/commands/channels/validate.ts index 93e6945e..80ef732f 100644 --- a/scripts/commands/channels/validate.ts +++ b/scripts/commands/channels/validate.ts @@ -1,100 +1,96 @@ -import { ChannelsParser, DataLoader, DataProcessor } from '../../core' -import { DataProcessorData } from '../../types/dataProcessor' -import { Storage, Dictionary, File } from '@freearhey/core' -import { DataLoaderData } from '../../types/dataLoader' -import { ChannelList } from '../../models' -import { DATA_DIR } from '../../constants' -import epgGrabber from 'epg-grabber' -import { program } from 'commander' -import chalk from 'chalk' -import langs from 'langs' - -program.argument('[filepath...]', 'Path to *.channels.xml files to validate').parse(process.argv) - -interface ValidationError { - type: 'duplicate' | 'wrong_channel_id' | 'wrong_feed_id' | 'wrong_lang' - name: string - lang?: string - xmltv_id?: string - site_id?: string - logo?: string -} - -async function main() { - const processor = new DataProcessor() - const dataStorage = new Storage(DATA_DIR) - const loader = new DataLoader({ storage: dataStorage }) - const data: DataLoaderData = await loader.load() - const { channelsKeyById, feedsKeyByStreamId }: DataProcessorData = processor.process(data) - const parser = new ChannelsParser({ - storage: new Storage() - }) - - let totalFiles = 0 - let totalErrors = 0 - let totalWarnings = 0 - - const storage = new Storage() - const files = program.args.length ? program.args : await storage.list('sites/**/*.channels.xml') - for (const filepath of files) { - const file = new File(filepath) - if (file.extension() !== 'xml') continue - - const channelList: ChannelList = await parser.parse(filepath) - - const bufferBySiteId = new Dictionary() - const errors: ValidationError[] = [] - channelList.channels.forEach((channel: epgGrabber.Channel) => { - const bufferId: string = channel.site_id - if (bufferBySiteId.missing(bufferId)) { - bufferBySiteId.set(bufferId, true) - } else { - errors.push({ type: 'duplicate', ...channel }) - totalErrors++ - } - - if (!langs.where('1', channel.lang ?? '')) { - errors.push({ type: 'wrong_lang', ...channel }) - totalErrors++ - } - - if (!channel.xmltv_id) return - const [channelId, feedId] = channel.xmltv_id.split('@') - - const foundChannel = channelsKeyById.get(channelId) - if (!foundChannel) { - errors.push({ type: 'wrong_channel_id', ...channel }) - totalWarnings++ - } - - if (feedId) { - const foundFeed = feedsKeyByStreamId.get(channel.xmltv_id) - if (!foundFeed) { - errors.push({ type: 'wrong_feed_id', ...channel }) - totalWarnings++ - } - } - }) - - if (errors.length) { - console.log(chalk.underline(filepath)) - console.table(errors, ['type', 'lang', 'xmltv_id', 'site_id', 'name']) - console.log() - totalFiles++ - } - } - - const totalProblems = totalWarnings + totalErrors - if (totalProblems > 0) { - console.log( - chalk.red( - `${totalProblems} problems (${totalErrors} errors, ${totalWarnings} warnings) in ${totalFiles} file(s)` - ) - ) - if (totalErrors > 0) { - process.exit(1) - } - } -} - -main() +import { Collection, Dictionary } from '@freearhey/core' +import { Storage, File } from '@freearhey/storage-js' +import epgGrabber, { EPGGrabber } from 'epg-grabber' +import { loadData, data } from '../../api' +import { Channel } from '../../models' +import { program } from 'commander' +import chalk from 'chalk' +import langs from 'langs' + +program.argument('[filepath...]', 'Path to *.channels.xml files to validate').parse(process.argv) + +interface ValidationError { + type: 'duplicate' | 'wrong_channel_id' | 'wrong_feed_id' | 'wrong_lang' + name: string + lang: string | null + xmltv_id: string | null + site_id: string | null + logo: string | null +} + +async function main() { + await loadData() + const { channelsKeyById, feedsKeyByStreamId } = data + + let totalFiles = 0 + let totalErrors = 0 + let totalWarnings = 0 + + const storage = new Storage() + const files = program.args.length ? program.args : await storage.list('sites/**/*.channels.xml') + for (const filepath of files) { + const file = new File(filepath) + if (file.extension() !== 'xml') continue + + const xml = await storage.load(filepath) + const parsedChannels = EPGGrabber.parseChannelsXML(xml) + const channelList = new Collection(parsedChannels).map( + (channel: epgGrabber.Channel) => new Channel(channel.toObject()) + ) + + const bufferBySiteId = new Dictionary() + const errors: ValidationError[] = [] + channelList.forEach((channel: Channel) => { + const bufferId: string = channel.site_id + if (bufferBySiteId.missing(bufferId)) { + bufferBySiteId.set(bufferId, true) + } else { + errors.push({ type: 'duplicate', ...channel.toObject() }) + totalErrors++ + } + + if (!langs.where('1', channel.lang ?? '')) { + errors.push({ type: 'wrong_lang', ...channel.toObject() }) + totalErrors++ + } + + if (!channel.xmltv_id) return + const [channelId, feedId] = channel.xmltv_id.split('@') + + const foundChannel = channelsKeyById.get(channelId) + if (!foundChannel) { + errors.push({ type: 'wrong_channel_id', ...channel.toObject() }) + totalWarnings++ + } + + if (feedId) { + const foundFeed = feedsKeyByStreamId.get(channel.xmltv_id) + if (!foundFeed) { + errors.push({ type: 'wrong_feed_id', ...channel.toObject() }) + totalWarnings++ + } + } + }) + + if (errors.length) { + console.log(chalk.underline(filepath)) + console.table(errors, ['type', 'lang', 'xmltv_id', 'site_id', 'name']) + console.log() + totalFiles++ + } + } + + const totalProblems = totalWarnings + totalErrors + if (totalProblems > 0) { + console.log( + chalk.red( + `${totalProblems} problems (${totalErrors} errors, ${totalWarnings} warnings) in ${totalFiles} file(s)` + ) + ) + if (totalErrors > 0) { + process.exit(1) + } + } +} + +main() diff --git a/scripts/commands/epg/grab.ts b/scripts/commands/epg/grab.ts index 6c8cc8f2..e7a55af4 100644 --- a/scripts/commands/epg/grab.ts +++ b/scripts/commands/epg/grab.ts @@ -1,11 +1,16 @@ -import { Logger, Timer, Collection } from '@freearhey/core' +import { Logger, Timer, Collection, Template } from '@freearhey/core' +import epgGrabber, { EPGGrabber, EPGGrabberMock } from 'epg-grabber' +import { loadJs, parseProxy, SiteConfig, Queue } from '../../core' +import { Channel, Guide, Program } from '../../models' +import { SocksProxyAgent } from 'socks-proxy-agent' +import { PromisyClass, TaskQueue } from 'cwait' import { Storage } from '@freearhey/storage-js' -import { QueueCreator, Job, ChannelsParser } from '../../core' +import { QueueItem } from '../../types/queue' import { Option, program } from 'commander' import { SITES_DIR } from '../../constants' -import { Channel } from 'epg-grabber' +import { data, loadData } from '../../api' +import dayjs, { Dayjs } from 'dayjs' import path from 'path' -import { ChannelList } from '../../models' program .addOption(new Option('-s, --site ', 'Name of the site to parse')) @@ -18,14 +23,14 @@ program .addOption(new Option('-o, --output ', 'Path to output file').default('guide.xml')) .addOption(new Option('-l, --lang ', 'Filter channels by languages (ISO 639-1 codes)')) .addOption( - new Option('-t, --timeout ', 'Override the default timeout for each request').env( - 'TIMEOUT' - ) + new Option('-t, --timeout ', 'Override the default timeout for each request') + .env('TIMEOUT') + .argParser(parseInt) ) .addOption( - new Option('-d, --delay ', 'Override the default delay between request').env( - 'DELAY' - ) + new Option('-d, --delay ', 'Override the default delay between request') + .env('DELAY') + .argParser(parseInt) ) .addOption(new Option('-x, --proxy ', 'Use the specified proxy').env('PROXY')) .addOption( @@ -33,12 +38,13 @@ program '--days ', 'Override the number of days for which the program will be loaded (defaults to the value from the site config)' ) - .argParser(value => parseInt(value)) + .argParser(parseInt) .env('DAYS') ) .addOption( new Option('--maxConnections ', 'Limit on the number of concurrent requests') .default(1) + .argParser(parseInt) .env('MAX_CONNECTIONS') ) .addOption( @@ -49,15 +55,15 @@ program .addOption(new Option('--curl', 'Display each request as CURL').default(false).env('CURL')) .parse() -export interface GrabOptions { +interface GrabOptions { site?: string channels?: string output: string gzip: boolean curl: boolean maxConnections: number - timeout?: string - delay?: string + timeout?: number + delay?: number lang?: string days?: number proxy?: string @@ -71,14 +77,13 @@ async function main() { const logger = new Logger() - logger.start('starting...') + logger.info('starting...') logger.info('config:') logger.tree(options) logger.info('loading channels...') const storage = new Storage() - const parser = new ChannelsParser({ storage }) let files: string[] = [] if (options.site) { @@ -89,46 +94,196 @@ async function main() { files = await storage.list(options.channels) } - let channels = new Collection() + let channelsFromXML = new Collection() for (const filepath of files) { - const channelList: ChannelList = await parser.parse(filepath) + const xml = await storage.load(filepath) + const parsedChannels = EPGGrabber.parseChannelsXML(xml) + const _channelsFromXML = new Collection(parsedChannels).map( + (channel: epgGrabber.Channel) => new Channel(channel.toObject()) + ) - channels = channels.concat(channelList.channels) + channelsFromXML.concat(_channelsFromXML) } if (options.lang) { - channels = channels.filter((channel: Channel) => { - if (!options.lang || !channel.lang) return true + channelsFromXML = channelsFromXML.filter((channel: Channel) => { + if (!options.lang) return true return options.lang.includes(channel.lang) }) } - logger.info(` found ${channels.count()} channel(s)`) + logger.info(`found ${channelsFromXML.count()} channel(s)`) + + logger.info('loading api data...') + await loadData() + + logger.info('creating queue...') + + let index = 0 + const queue = new Queue() + + for (const channel of channelsFromXML.all()) { + channel.index = index++ + if (!channel.site || !channel.site_id || !channel.name) continue + + const configObject = await loadJs(channel.getConfigPath()) + + const siteConfig = new SiteConfig(configObject) + + siteConfig.filepath = channel.getConfigPath() + + if (options.timeout !== undefined) { + siteConfig.request = { ...siteConfig.request, ...{ timeout: options.timeout } } + } + if (options.delay !== undefined) siteConfig.delay = options.delay + if (options.curl !== undefined) siteConfig.curl = options.curl + if (options.proxy !== undefined) { + const proxy = parseProxy(options.proxy) + + if ( + proxy.protocol && + ['socks', 'socks5', 'socks5h', 'socks4', 'socks4a'].includes(String(proxy.protocol)) + ) { + const socksProxyAgent = new SocksProxyAgent(options.proxy) + + siteConfig.request = { + ...siteConfig.request, + ...{ httpAgent: socksProxyAgent, httpsAgent: socksProxyAgent } + } + } else { + siteConfig.request = { ...siteConfig.request, ...{ proxy } } + } + } + + if (!channel.xmltv_id) channel.xmltv_id = channel.site_id + + const days = options.days || siteConfig.days || 1 + const currDate = dayjs.utc(process.env.CURR_DATE || new Date().toISOString()) + const dates = Array.from({ length: days }, (_, day) => currDate.add(day, 'd')) + + dates.forEach((date: Dayjs) => { + const key = `${channel.site}:${channel.lang}:${channel.xmltv_id}:${date.toJSON()}` + if (queue.has(key)) return + queue.add(key, { + channel, + date, + siteConfig, + error: null + }) + }) + } + + const grabber = process.env.NODE_ENV === 'test' ? new EPGGrabberMock() : new EPGGrabber() + + const taskQueue = new TaskQueue(Promise as PromisyClass, options.maxConnections) + + const queueItems = queue.getItems() + + const channels = new Collection() + const programs = new Collection() + + let i = 1 + const total = queueItems.count() + + const requests = queueItems.map( + taskQueue.wrap(async (queueItem: QueueItem) => { + const { channel, siteConfig, date } = queueItem + + if (!channel.logo) { + if (siteConfig.logo) { + channel.logo = await grabber.loadLogo(channel, date) + } else { + channel.logo = getLogoForChannel(channel) + } + } + + channels.add(channel) + + const channelPrograms = await grabber.grab( + channel, + date, + siteConfig, + (context: epgGrabber.Types.GrabCallbackContext, error: Error | null) => { + logger.info( + ` [${i}/${total}] ${context.channel.site} (${context.channel.lang}) - ${ + context.channel.xmltv_id + } - ${context.date.format('MMM D, YYYY')} (${context.programs.length} programs)` + ) + if (i < total) i++ + + if (error) { + logger.info(` ERR: ${error.message}`) + } + } + ) + + const _programs = new Collection(channelPrograms).map( + program => new Program(program.toObject()) + ) + + programs.concat(_programs) + }) + ) logger.info('run:') - runJob({ logger, channels }) + + const timer = new Timer() + timer.start() + + await Promise.all(requests.all()) + + const pathTemplate = new Template(options.output) + + const channelsGroupedByKey = channels + .sortBy([(channel: Channel) => channel.index, (channel: Channel) => channel.xmltv_id]) + .uniqBy((channel: Channel) => `${channel.xmltv_id}:${channel.site}:${channel.lang}`) + .groupBy((channel: Channel) => { + return pathTemplate.format({ lang: channel.lang || 'en', site: channel.site || '' }) + }) + + const programsGroupedByKey = programs + .sortBy([(program: Program) => program.channel, (program: Program) => program.start]) + .groupBy((program: Program) => { + const lang = + program.titles && program.titles.length && program.titles[0].lang + ? program.titles[0].lang + : 'en' + + return pathTemplate.format({ lang, site: program.site || '' }) + }) + + for (const groupKey of channelsGroupedByKey.keys()) { + const groupChannels = new Collection(channelsGroupedByKey.get(groupKey)) + const groupPrograms = new Collection(programsGroupedByKey.get(groupKey)) + const guide = new Guide({ + filepath: groupKey, + gzip: options.gzip, + channels: groupChannels, + programs: groupPrograms + }) + + await guide.save({ logger }) + } + + logger.success(` done in ${timer.format('HH[h] mm[m] ss[s]')}`) } main() -async function runJob({ logger, channels }: { logger: Logger; channels: Collection }) { - const timer = new Timer() - timer.start() +function getLogoForChannel(channel: Channel): string | null { + const feedData = data.feedsKeyByStreamId.get(channel.xmltv_id) + if (feedData) { + const firstLogo = feedData.getLogos().first() + if (firstLogo) return firstLogo.url + } - const queueCreator = new QueueCreator({ - channels, - logger, - options - }) - const queue = await queueCreator.create() - const job = new Job({ - queue, - logger, - options - }) + const [channelId] = channel.xmltv_id.split('@') + const channelData = data.channelsKeyById.get(channelId) + if (channelData) { + const firstLogo = channelData.getLogos().first() + if (firstLogo) return firstLogo.url + } - await job.run() - - logger.success(` done in ${timer.format('HH[h] mm[m] ss[s]')}`) + return null } diff --git a/scripts/commands/sites/init.ts b/scripts/commands/sites/init.ts index 44afd09b..03575a53 100644 --- a/scripts/commands/sites/init.ts +++ b/scripts/commands/sites/init.ts @@ -1,6 +1,6 @@ -import { Logger } from '@freearhey/core' +import { SITES_DIR, EOL } from '../../constants' import { Storage } from '@freearhey/storage-js' -import { SITES_DIR } from '../../constants' +import { Logger } from '@freearhey/core' import { pathToFileURL } from 'node:url' import { program } from 'commander' import fs from 'fs-extra' @@ -13,7 +13,7 @@ async function main() { const storage = new Storage(SITES_DIR) const logger = new Logger() - logger.info(`Initializing "${domain}"...\r\n`) + logger.info(`Initializing "${domain}"...${EOL}`) const dir = domain if (await storage.exists(dir)) { @@ -40,7 +40,7 @@ async function main() { }) await storage.save(`${dir}/readme.md`, readmeTemplate.replace(//g, domain)) - logger.info('\r\nDone') + logger.info(`${EOL}Done`) } main() diff --git a/scripts/commands/sites/update.ts b/scripts/commands/sites/update.ts index 889f12fe..7f2939a7 100644 --- a/scripts/commands/sites/update.ts +++ b/scripts/commands/sites/update.ts @@ -1,26 +1,22 @@ -import { IssueLoader, HTMLTable, ChannelsParser } from '../../core' -import { Logger, Collection } from '@freearhey/core' -import { Storage } from '@freearhey/storage-js' -import { ChannelList, Issue, Site } from '../../models' +import { HTMLTableDataItem, HTMLTableRow, HTMLTableColumn } from '../../types/htmlTable' import { SITES_DIR, ROOT_DIR } from '../../constants' -import { Channel } from 'epg-grabber' +import { Logger, Collection } from '@freearhey/core' +import { Issue, Site, Channel } from '../../models' +import { HTMLTable, loadIssues } from '../../core' +import { Storage } from '@freearhey/storage-js' +import * as epgGrabber from 'epg-grabber' +import { EPGGrabber } from 'epg-grabber' async function main() { const logger = new Logger({ level: -999 }) - const issueLoader = new IssueLoader() const sitesStorage = new Storage(SITES_DIR) - const sites = new Collection() - - logger.info('loading channels...') - const channelsParser = new ChannelsParser({ - storage: sitesStorage - }) + const sites = new Collection() logger.info('loading list of sites') const folders = await sitesStorage.list('*/') logger.info('loading issues...') - const issues = await issueLoader.load() + const issues = await loadIssues() logger.info('putting the data together...') const brokenGuideReports = issues.filter(issue => @@ -38,36 +34,43 @@ async function main() { const files = await sitesStorage.list(`${domain}/*.channels.xml`) for (const filepath of files) { - const channelList: ChannelList = await channelsParser.parse(filepath) + const xml = await sitesStorage.load(filepath) + const channelsFromXML = EPGGrabber.parseChannelsXML(xml) + const channels = new Collection(channelsFromXML).map( + (channel: epgGrabber.Channel) => new Channel(channel.toObject()) + ) - site.totalChannels += channelList.channels.count() - site.markedChannels += channelList.channels - .filter((channel: Channel) => channel.xmltv_id) - .count() + site.totalChannels += channels.count() + site.markedChannels += channels.filter((channel: Channel) => channel.xmltv_id).count() } sites.add(site) } logger.info('creating sites table...') - const tableData = new Collection() + const rows = new Collection() sites.forEach((site: Site) => { - tableData.add([ - { value: `${site.domain}` }, - { value: site.totalChannels, align: 'right' }, - { value: site.markedChannels, align: 'right' }, - { value: site.getStatus().emoji, align: 'center' }, - { value: site.getIssues().all().join(', ') } - ]) + rows.add( + new Collection([ + { value: `${site.domain}` }, + { value: site.totalChannels.toString(), align: 'right' }, + { value: site.markedChannels.toString(), align: 'right' }, + { value: site.getStatus().emoji, align: 'center' }, + { value: site.getIssueUrls().all().join(', ') } + ]) + ) }) logger.info('updating sites.md...') - const table = new HTMLTable(tableData.all(), [ - { name: 'Site', align: 'left' }, - { name: 'Channels
(total / with xmltv-id)', colspan: 2, align: 'left' }, - { name: 'Status', align: 'left' }, - { name: 'Notes', align: 'left' } - ]) + const table = new HTMLTable( + rows, + new Collection([ + { name: 'Site', align: 'left' }, + { name: 'Channels
(total / with xmltv-id)', colspan: 2, align: 'left' }, + { name: 'Status', align: 'left' }, + { name: 'Notes', align: 'left' } + ]) + ) const rootStorage = new Storage(ROOT_DIR) const sitesTemplate = await new Storage().load('scripts/templates/_sites.md') const sitesContent = sitesTemplate.replace('_TABLE_', table.toString()) diff --git a/scripts/constants.ts b/scripts/constants.ts index 52c5d798..bdb32548 100644 --- a/scripts/constants.ts +++ b/scripts/constants.ts @@ -1,9 +1,10 @@ -export const ROOT_DIR = process.env.ROOT_DIR || '.' -export const SITES_DIR = process.env.SITES_DIR || './sites' -export const GUIDES_DIR = process.env.GUIDES_DIR || './guides' -export const DATA_DIR = process.env.DATA_DIR || './temp/data' -export const API_DIR = process.env.API_DIR || '.api' -export const DOT_SITES_DIR = process.env.DOT_SITES_DIR || './.sites' -export const TESTING = process.env.NODE_ENV === 'test' ? true : false -export const OWNER = 'iptv-org' -export const REPO = 'epg' +export const ROOT_DIR = process.env.ROOT_DIR || '.' +export const SITES_DIR = process.env.SITES_DIR || './sites' +export const GUIDES_DIR = process.env.GUIDES_DIR || './guides' +export const DATA_DIR = process.env.DATA_DIR || './temp/data' +export const API_DIR = process.env.API_DIR || '.api' +export const DOT_SITES_DIR = process.env.DOT_SITES_DIR || './.sites' +export const TESTING = process.env.NODE_ENV === 'test' ? true : false +export const OWNER = 'iptv-org' +export const REPO = 'epg' +export const EOL = '\r\n' diff --git a/scripts/core/apiClient.ts b/scripts/core/apiClient.ts deleted file mode 100644 index e4815a81..00000000 --- a/scripts/core/apiClient.ts +++ /dev/null @@ -1,16 +0,0 @@ -import axios, { AxiosInstance, AxiosResponse, AxiosRequestConfig } from 'axios' - -export class ApiClient { - instance: AxiosInstance - - constructor() { - this.instance = axios.create({ - baseURL: 'https://iptv-org.github.io/api', - responseType: 'stream' - }) - } - - get(url: string, options: AxiosRequestConfig): Promise { - return this.instance.get(url, options) - } -} diff --git a/scripts/core/channelsParser.ts b/scripts/core/channelsParser.ts deleted file mode 100644 index 1ddcdfd2..00000000 --- a/scripts/core/channelsParser.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { EPGGrabber } from 'epg-grabber' -import { Storage } from '@freearhey/storage-js' -import { ChannelList } from '../models' - -interface ChannelsParserProps { - storage: Storage -} - -export class ChannelsParser { - storage: Storage - - constructor({ storage }: ChannelsParserProps) { - this.storage = storage - } - - async parse(filepath: string): Promise { - const content = await this.storage.load(filepath) - const parsed = EPGGrabber.parseChannelsXML(content) - - return new ChannelList({ channels: parsed }) - } -} diff --git a/scripts/core/configLoader.ts b/scripts/core/configLoader.ts deleted file mode 100644 index a49aee5a..00000000 --- a/scripts/core/configLoader.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { SiteConfig } from 'epg-grabber' -import { pathToFileURL } from 'url' - -export class ConfigLoader { - async load(filepath: string): Promise { - const fileUrl = pathToFileURL(filepath).toString() - const config = (await import(fileUrl)).default - const defaultConfig = { - days: 1, - delay: 0, - output: 'guide.xml', - request: { - method: 'GET', - maxContentLength: 5242880, - timeout: 30000, - withCredentials: true, - jar: null, - responseType: 'arraybuffer', - cache: false, - headers: null, - data: null - }, - maxConnections: 1, - site: undefined, - url: undefined, - parser: undefined, - channels: undefined - } - - return { ...defaultConfig, ...config } as SiteConfig - } -} diff --git a/scripts/core/dataLoader.ts b/scripts/core/dataLoader.ts deleted file mode 100644 index d46478df..00000000 --- a/scripts/core/dataLoader.ts +++ /dev/null @@ -1,103 +0,0 @@ -import type { DataLoaderProps, DataLoaderData } from '../types/dataLoader' -import cliProgress, { MultiBar } from 'cli-progress' -import { Storage } from '@freearhey/storage-js' -import { ApiClient } from './apiClient' -import numeral from 'numeral' - -export class DataLoader { - client: ApiClient - storage: Storage - progressBar: MultiBar - - constructor(props: DataLoaderProps) { - this.client = new ApiClient() - this.storage = props.storage - this.progressBar = new cliProgress.MultiBar({ - stopOnComplete: true, - hideCursor: true, - forceRedraw: true, - barsize: 36, - format(options, params, payload) { - const filename = payload.filename.padEnd(18, ' ') - const barsize = options.barsize || 40 - const percent = (params.progress * 100).toFixed(2) - const speed = payload.speed ? numeral(payload.speed).format('0.0 b') + '/s' : 'N/A' - const total = numeral(params.total).format('0.0 b') - const completeSize = Math.round(params.progress * barsize) - const incompleteSize = barsize - completeSize - const bar = - options.barCompleteString && options.barIncompleteString - ? options.barCompleteString.substr(0, completeSize) + - options.barGlue + - options.barIncompleteString.substr(0, incompleteSize) - : '-'.repeat(barsize) - - return `${filename} [${bar}] ${percent}% | ETA: ${params.eta}s | ${total} | ${speed}` - } - }) - } - - async load(): Promise { - const [ - countries, - regions, - subdivisions, - languages, - categories, - blocklist, - channels, - feeds, - timezones, - guides, - streams, - logos - ] = await Promise.all([ - this.storage.json('countries.json'), - this.storage.json('regions.json'), - this.storage.json('subdivisions.json'), - this.storage.json('languages.json'), - this.storage.json('categories.json'), - this.storage.json('blocklist.json'), - this.storage.json('channels.json'), - this.storage.json('feeds.json'), - this.storage.json('timezones.json'), - this.storage.json('guides.json'), - this.storage.json('streams.json'), - this.storage.json('logos.json') - ]) - - return { - countries, - regions, - subdivisions, - languages, - categories, - blocklist, - channels, - feeds, - timezones, - guides, - streams, - logos - } - } - - async download(filename: string) { - if (!this.storage || !this.progressBar) return - - const stream = await this.storage.createStream(filename) - const progressBar = this.progressBar.create(0, 0, { filename }) - - this.client - .get(filename, { - responseType: 'stream', - onDownloadProgress({ total, loaded, rate }) { - if (total) progressBar.setTotal(total) - progressBar.update(loaded, { speed: rate }) - } - }) - .then(response => { - response.data.pipe(stream) - }) - } -} diff --git a/scripts/core/dataProcessor.ts b/scripts/core/dataProcessor.ts deleted file mode 100644 index a104e675..00000000 --- a/scripts/core/dataProcessor.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Channel, Feed, GuideChannel, Logo, Stream } from '../models' -import { DataLoaderData } from '../types/dataLoader' -import { Collection } from '@freearhey/core' - -export class DataProcessor { - - process(data: DataLoaderData) { - let channels = new Collection(data.channels).map(data => new Channel(data)) - const channelsKeyById = channels.keyBy((channel: Channel) => channel.id) - - const guideChannels = new Collection(data.guides).map(data => new GuideChannel(data)) - const guideChannelsGroupedByStreamId = guideChannels.groupBy((channel: GuideChannel) => - channel.getStreamId() - ) - - const streams = new Collection(data.streams).map(data => new Stream(data)) - const streamsGroupedById = streams.groupBy((stream: Stream) => stream.getId()) - - let feeds = new Collection(data.feeds).map(data => - new Feed(data) - .withGuideChannels(guideChannelsGroupedByStreamId) - .withStreams(streamsGroupedById) - .withChannel(channelsKeyById) - ) - const feedsKeyByStreamId = feeds.keyBy((feed: Feed) => feed.getStreamId()) - - const logos = new Collection(data.logos).map(data => - new Logo(data).withFeed(feedsKeyByStreamId) - ) - const logosGroupedByChannelId = logos.groupBy((logo: Logo) => logo.channelId) - const logosGroupedByStreamId = logos.groupBy((logo: Logo) => logo.getStreamId()) - - feeds = feeds.map((feed: Feed) => feed.withLogos(logosGroupedByStreamId)) - const feedsGroupedByChannelId = feeds.groupBy((feed: Feed) => feed.channelId) - - channels = channels.map((channel: Channel) => - channel.withFeeds(feedsGroupedByChannelId).withLogos(logosGroupedByChannelId) - ) - - return { - guideChannelsGroupedByStreamId, - feedsGroupedByChannelId, - logosGroupedByChannelId, - logosGroupedByStreamId, - streamsGroupedById, - feedsKeyByStreamId, - channelsKeyById, - guideChannels, - channels, - streams, - feeds, - logos - } - } -} diff --git a/scripts/core/date.js b/scripts/core/date.js deleted file mode 100644 index 777f9c33..00000000 --- a/scripts/core/date.js +++ /dev/null @@ -1,14 +0,0 @@ -import dayjs from 'dayjs' -import utc from 'dayjs/plugin/utc' - -dayjs.extend(utc) - -const date = {} - -date.getUTC = function (d = null) { - if (typeof d === 'string') return dayjs.utc(d).startOf('d') - - return dayjs.utc().startOf('d') -} - -export default date diff --git a/scripts/core/grabber.ts b/scripts/core/grabber.ts deleted file mode 100644 index d1881af7..00000000 --- a/scripts/core/grabber.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { EPGGrabber, GrabCallbackData, EPGGrabberMock, SiteConfig, Channel } from 'epg-grabber' -import { Logger, Collection } from '@freearhey/core' -import { Queue, ProxyParser } from './' -import { GrabOptions } from '../commands/epg/grab' -import { TaskQueue, PromisyClass } from 'cwait' -import { SocksProxyAgent } from 'socks-proxy-agent' - -interface GrabberProps { - logger: Logger - queue: Queue - options: GrabOptions -} - -export class Grabber { - logger: Logger - queue: Queue - options: GrabOptions - grabber: EPGGrabber | EPGGrabberMock - - constructor({ logger, queue, options }: GrabberProps) { - this.logger = logger - this.queue = queue - this.options = options - this.grabber = process.env.NODE_ENV === 'test' ? new EPGGrabberMock() : new EPGGrabber() - } - - async grab(): Promise<{ channels: Collection; programs: Collection }> { - const proxyParser = new ProxyParser() - const taskQueue = new TaskQueue(Promise as PromisyClass, this.options.maxConnections) - - const total = this.queue.size() - - const channels = new Collection() - let programs = new Collection() - let i = 1 - - await Promise.all( - this.queue.items().map( - taskQueue.wrap( - async (queueItem: { channel: Channel; config: SiteConfig; date: string }) => { - const { channel, config, date } = queueItem - - channels.add(channel) - - if (this.options.timeout !== undefined) { - const timeout = parseInt(this.options.timeout) - config.request = { ...config.request, ...{ timeout } } - } - - if (this.options.delay !== undefined) { - const delay = parseInt(this.options.delay) - config.delay = delay - } - - if (this.options.proxy !== undefined) { - const proxy = proxyParser.parse(this.options.proxy) - - if ( - proxy.protocol && - ['socks', 'socks5', 'socks5h', 'socks4', 'socks4a'].includes(String(proxy.protocol)) - ) { - const socksProxyAgent = new SocksProxyAgent(this.options.proxy) - - config.request = { - ...config.request, - ...{ httpAgent: socksProxyAgent, httpsAgent: socksProxyAgent } - } - } else { - config.request = { ...config.request, ...{ proxy } } - } - } - - if (this.options.curl === true) { - config.curl = true - } - - const _programs = await this.grabber.grab( - channel, - date, - config, - (data: GrabCallbackData, error: Error | null) => { - const { programs, date } = data - - this.logger.info( - ` [${i}/${total}] ${channel.site} (${channel.lang}) - ${ - channel.xmltv_id - } - ${date.format('MMM D, YYYY')} (${programs.length} programs)` - ) - if (i < total) i++ - - if (error) { - this.logger.info(` ERR: ${error.message}`) - } - } - ) - - programs = programs.concat(new Collection(_programs)) - } - ) - ) - ) - - return { channels, programs } - } -} diff --git a/scripts/core/guideManager.ts b/scripts/core/guideManager.ts deleted file mode 100644 index a84304ac..00000000 --- a/scripts/core/guideManager.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { Collection, Logger, Zip, StringTemplate } from '@freearhey/core' -import { Storage } from '@freearhey/storage-js' -import epgGrabber from 'epg-grabber' -import { OptionValues } from 'commander' -import { Channel, Feed, Guide } from '../models' -import path from 'path' -import { DataLoader, DataProcessor } from '.' -import { DataLoaderData } from '../types/dataLoader' -import { DataProcessorData } from '../types/dataProcessor' -import { DATA_DIR } from '../constants' - -interface GuideManagerProps { - options: OptionValues - logger: Logger - channels: Collection - programs: Collection -} - -export class GuideManager { - options: OptionValues - logger: Logger - channels: Collection - programs: Collection - - constructor({ channels, programs, logger, options }: GuideManagerProps) { - this.options = options - this.logger = logger - this.channels = channels - this.programs = programs - } - - async createGuides() { - const pathTemplate = new StringTemplate(this.options.output) - - const processor = new DataProcessor() - const dataStorage = new Storage(DATA_DIR) - const loader = new DataLoader({ storage: dataStorage }) - const data: DataLoaderData = await loader.load() - const { feedsKeyByStreamId, channelsKeyById }: DataProcessorData = processor.process(data) - - const groupedChannels = this.channels - .map((channel: epgGrabber.Channel) => { - if (channel.xmltv_id && !channel.icon) { - const foundFeed: Feed = feedsKeyByStreamId.get(channel.xmltv_id) - if (foundFeed && foundFeed.hasLogo()) { - channel.icon = foundFeed.getLogoUrl() - } else { - const [channelId] = channel.xmltv_id.split('@') - const foundChannel: Channel = channelsKeyById.get(channelId) - if (foundChannel && foundChannel.hasLogo()) { - channel.icon = foundChannel.getLogoUrl() - } - } - } - - return channel - }) - .orderBy([ - (channel: epgGrabber.Channel) => channel.index, - (channel: epgGrabber.Channel) => channel.xmltv_id - ]) - .uniqBy( - (channel: epgGrabber.Channel) => `${channel.xmltv_id}:${channel.site}:${channel.lang}` - ) - .groupBy((channel: epgGrabber.Channel) => { - return pathTemplate.format({ lang: channel.lang || 'en', site: channel.site || '' }) - }) - - const groupedPrograms = this.programs - .orderBy([ - (program: epgGrabber.Program) => program.channel, - (program: epgGrabber.Program) => program.start - ]) - .groupBy((program: epgGrabber.Program) => { - const lang = - program.titles && program.titles.length && program.titles[0].lang - ? program.titles[0].lang - : 'en' - - return pathTemplate.format({ lang, site: program.site || '' }) - }) - - for (const groupKey of groupedPrograms.keys()) { - const guide = new Guide({ - filepath: groupKey, - gzip: this.options.gzip, - channels: new Collection(groupedChannels.get(groupKey)), - programs: new Collection(groupedPrograms.get(groupKey)) - }) - - await this.save(guide) - } - } - - async save(guide: Guide) { - const storage = new Storage(path.dirname(guide.filepath)) - const xmlFilepath = guide.filepath - const xmlFilename = path.basename(xmlFilepath) - this.logger.info(` saving to "${xmlFilepath}"...`) - const xmltv = guide.toString() - await storage.save(xmlFilename, xmltv) - - if (guide.gzip) { - const zip = new Zip() - const compressed = zip.compress(xmltv) - const gzFilepath = `${guide.filepath}.gz` - const gzFilename = path.basename(gzFilepath) - this.logger.info(` saving to "${gzFilepath}"...`) - await storage.save(gzFilename, compressed) - } - } -} diff --git a/scripts/core/htmlTable.ts b/scripts/core/htmlTable.ts index 6b917d6b..fd199e91 100644 --- a/scripts/core/htmlTable.ts +++ b/scripts/core/htmlTable.ts @@ -1,55 +1,45 @@ -interface Column { - name: string - nowrap?: boolean - align?: string - colspan?: number -} - -type DataItem = { - value: string - nowrap?: boolean - align?: string - colspan?: number -}[] - -export class HTMLTable { - data: DataItem[] - columns: Column[] - - constructor(data: DataItem[], columns: Column[]) { - this.data = data - this.columns = columns - } - - toString() { - let output = '\r\n' - - output += ' \r\n ' - for (const column of this.columns) { - const nowrap = column.nowrap ? ' nowrap' : '' - const align = column.align ? ` align="${column.align}"` : '' - const colspan = column.colspan ? ` colspan="${column.colspan}"` : '' - - output += `${column.name}` - } - output += '\r\n \r\n' - - output += ' \r\n' - for (const row of this.data) { - output += ' ' - for (const item of row) { - const nowrap = item.nowrap ? ' nowrap' : '' - const align = item.align ? ` align="${item.align}"` : '' - const colspan = item.colspan ? ` colspan="${item.colspan}"` : '' - - output += `${item.value}` - } - output += '\r\n' - } - output += ' \r\n' - - output += '
' - - return output - } -} +import { HTMLTableColumn, HTMLTableDataItem, HTMLTableRow } from '../types/htmlTable' +import { Collection } from '@freearhey/core' +import { EOL } from '../constants' + +export class HTMLTable { + rows: Collection + columns: Collection + + constructor(rows: Collection, columns: Collection) { + this.rows = rows + this.columns = columns + } + + toString() { + let output = `${EOL}` + + output += ` ${EOL} ` + this.columns.forEach((column: HTMLTableColumn) => { + const nowrap = column.nowrap ? ' nowrap' : '' + const align = column.align ? ` align="${column.align}"` : '' + const colspan = column.colspan ? ` colspan="${column.colspan}"` : '' + + output += `${column.name}` + }) + output += `${EOL} ${EOL}` + + output += ` ${EOL}` + this.rows.forEach((row: HTMLTableRow) => { + output += ' ' + row.forEach((item: HTMLTableDataItem) => { + const nowrap = item.nowrap ? ' nowrap' : '' + const align = item.align ? ` align="${item.align}"` : '' + const colspan = item.colspan ? ` colspan="${item.colspan}"` : '' + + output += `${item.value}` + }) + output += `${EOL}` + }) + output += ` ${EOL}` + + output += '
' + + return output + } +} diff --git a/scripts/core/index.ts b/scripts/core/index.ts index 8694174a..6d69e053 100644 --- a/scripts/core/index.ts +++ b/scripts/core/index.ts @@ -1,14 +1,4 @@ -export * from './apiClient' -export * from './channelsParser' -export * from './configLoader' -export * from './dataLoader' -export * from './dataProcessor' -export * from './grabber' -export * from './guideManager' -export * from './htmlTable' -export * from './issueLoader' -export * from './issueParser' -export * from './job' -export * from './proxyParser' -export * from './queue' -export * from './queueCreator' +export * from './htmlTable' +export * from './siteConfig' +export * from './utils' +export * from './queue' diff --git a/scripts/core/issueLoader.ts b/scripts/core/issueLoader.ts deleted file mode 100644 index 855f99e2..00000000 --- a/scripts/core/issueLoader.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { restEndpointMethods } from '@octokit/plugin-rest-endpoint-methods' -import { paginateRest } from '@octokit/plugin-paginate-rest' -import { TESTING, OWNER, REPO } from '../constants' -import { Collection } from '@freearhey/core' -import { Octokit } from '@octokit/core' -import { IssueParser } from './' - -const CustomOctokit = Octokit.plugin(paginateRest, restEndpointMethods) -const octokit = new CustomOctokit() - -export class IssueLoader { - async load(props?: { labels: string[] | string }) { - let labels = '' - if (props && props.labels) { - labels = Array.isArray(props.labels) ? props.labels.join(',') : props.labels - } - let issues: object[] = [] - if (TESTING) { - issues = (await import('../../tests/__data__/input/sites_update/issues.mjs')).default - } else { - issues = await octokit.paginate(octokit.rest.issues.listForRepo, { - owner: OWNER, - repo: REPO, - per_page: 100, - labels, - state: 'open', - headers: { - 'X-GitHub-Api-Version': '2022-11-28' - } - }) - } - - const parser = new IssueParser() - - return new Collection(issues).map(parser.parse) - } -} diff --git a/scripts/core/issueParser.ts b/scripts/core/issueParser.ts deleted file mode 100644 index 2db0f9dd..00000000 --- a/scripts/core/issueParser.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Dictionary } from '@freearhey/core' -import { Issue } from '../models' - -const FIELDS = new Dictionary({ - Site: 'site' -}) - -export class IssueParser { - parse(issue: { number: number; body: string; labels: { name: string }[] }): Issue { - const fields = issue.body.split('###') - - const data = new Dictionary() - fields.forEach((field: string) => { - const parsed = field.split(/\r?\n/).filter(Boolean) - let _label = parsed.shift() - _label = _label ? _label.trim() : '' - let _value = parsed.join('\r\n') - _value = _value ? _value.trim() : '' - - if (!_label || !_value) return data - - const id: string = FIELDS.get(_label) - const value: string = _value === '_No response_' || _value === 'None' ? '' : _value - - if (!id) return - - data.set(id, value) - }) - - const labels = issue.labels.map(label => label.name) - - return new Issue({ number: issue.number, labels, data }) - } -} diff --git a/scripts/core/job.ts b/scripts/core/job.ts deleted file mode 100644 index db37ba15..00000000 --- a/scripts/core/job.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Logger } from '@freearhey/core' -import { Queue, Grabber, GuideManager } from '.' -import { GrabOptions } from '../commands/epg/grab' - -interface JobProps { - options: GrabOptions - logger: Logger - queue: Queue -} - -export class Job { - options: GrabOptions - logger: Logger - grabber: Grabber - - constructor({ queue, logger, options }: JobProps) { - this.options = options - this.logger = logger - this.grabber = new Grabber({ logger, queue, options }) - } - - async run() { - const { channels, programs } = await this.grabber.grab() - - const manager = new GuideManager({ - channels, - programs, - options: this.options, - logger: this.logger - }) - - await manager.createGuides() - } -} diff --git a/scripts/core/proxyParser.ts b/scripts/core/proxyParser.ts deleted file mode 100644 index 9cede1af..00000000 --- a/scripts/core/proxyParser.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { URL } from 'node:url' - -interface ProxyParserResult { - protocol: string | null - auth?: { - username?: string - password?: string - } - host: string - port: number | null -} - -export class ProxyParser { - parse(_url: string): ProxyParserResult { - const parsed = new URL(_url) - - const result: ProxyParserResult = { - protocol: parsed.protocol.replace(':', '') || null, - host: parsed.hostname, - port: parsed.port ? parseInt(parsed.port) : null - } - - if (parsed.username || parsed.password) { - result.auth = {} - if (parsed.username) result.auth.username = parsed.username - if (parsed.password) result.auth.password = parsed.password - } - - return result - } -} diff --git a/scripts/core/queue.ts b/scripts/core/queue.ts index 106c111e..1237e5d0 100644 --- a/scripts/core/queue.ts +++ b/scripts/core/queue.ts @@ -1,45 +1,18 @@ -import { Dictionary } from '@freearhey/core' -import { SiteConfig, Channel } from 'epg-grabber' - -export interface QueueItem { - channel: Channel - date: string - config: SiteConfig - error: string | null -} - -export class Queue { - _data: Dictionary - - constructor() { - this._data = new Dictionary() - } - - missing(key: string): boolean { - return this._data.missing(key) - } - - add( - key: string, - { channel, config, date }: { channel: Channel; date: string | null; config: SiteConfig } - ) { - this._data.set(key, { - channel, - date, - config, - error: null - }) - } - - size(): number { - return Object.values(this._data.data()).length - } - - items(): QueueItem[] { - return Object.values(this._data.data()) as QueueItem[] - } - - isEmpty(): boolean { - return this.size() === 0 - } -} +import { Collection, Dictionary } from '@freearhey/core' +import { QueueItem } from '../types/queue' + +export class Queue { + #items: Dictionary = new Dictionary() + + add(key: string, data: QueueItem) { + this.#items.set(key, data) + } + + has(key: string): boolean { + return this.#items.has(key) + } + + getItems(): Collection { + return new Collection(Object.values(this.#items.data())) + } +} diff --git a/scripts/core/queueCreator.ts b/scripts/core/queueCreator.ts deleted file mode 100644 index 56333dcb..00000000 --- a/scripts/core/queueCreator.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Storage, Collection, DateTime, Logger } from '@freearhey/core' -import { SITES_DIR, DATA_DIR } from '../constants' -import { GrabOptions } from '../commands/epg/grab' -import { ConfigLoader, Queue } from './' -import { SiteConfig } from 'epg-grabber' -import path from 'path' - -interface QueueCreatorProps { - logger: Logger - options: GrabOptions - channels: Collection -} - -export class QueueCreator { - configLoader: ConfigLoader - logger: Logger - sitesStorage: Storage - dataStorage: Storage - channels: Collection - options: GrabOptions - - constructor({ channels, logger, options }: QueueCreatorProps) { - this.channels = channels - this.logger = logger - this.sitesStorage = new Storage() - this.dataStorage = new Storage(DATA_DIR) - this.options = options - this.configLoader = new ConfigLoader() - } - - async create(): Promise { - let index = 0 - const queue = new Queue() - for (const channel of this.channels.all()) { - channel.index = index++ - if (!channel.site || !channel.site_id || !channel.name) continue - - const configPath = path.resolve(SITES_DIR, `${channel.site}/${channel.site}.config.js`) - const config: SiteConfig = await this.configLoader.load(configPath) - - if (!channel.xmltv_id) { - channel.xmltv_id = channel.site_id - } - - const days = this.options.days || config.days || 1 - const currDate = new DateTime(process.env.CURR_DATE || new Date().toISOString()) - const dates = Array.from({ length: days }, (_, day) => currDate.add(day, 'd')) - dates.forEach((date: DateTime) => { - const dateString = date.toJSON() - const key = `${channel.site}:${channel.lang}:${channel.xmltv_id}:${dateString}` - if (queue.missing(key)) { - queue.add(key, { - channel, - date: dateString, - config - }) - } - }) - } - - return queue - } -} diff --git a/scripts/core/siteConfig.ts b/scripts/core/siteConfig.ts new file mode 100644 index 00000000..e7895020 --- /dev/null +++ b/scripts/core/siteConfig.ts @@ -0,0 +1,71 @@ +import * as epgGrabber from 'epg-grabber' +import _ from 'lodash' + +const _default = { + days: 1, + delay: 0, + output: 'guide.xml', + request: { + method: 'GET', + maxContentLength: 5242880, + timeout: 30000, + withCredentials: true, + jar: null, + responseType: 'arraybuffer', + cache: false, + headers: null, + data: null + }, + maxConnections: 1, + site: undefined, + url: undefined, + parser: undefined, + channels: undefined, + lang: 'en', + debug: false, + gzip: false, + curl: false, + logo: '' +} + +export class SiteConfig { + days: number + lang: string + delay: number + debug: boolean + gzip: boolean + curl: boolean + maxConnections: number + output: string + request: epgGrabber.Types.SiteConfigRequestConfig + site: string + channels?: string | string[] + url: ((context: epgGrabber.Types.SiteConfigRequestContext) => string | Promise) | string + parser: ( + context: epgGrabber.Types.SiteConfigParserContext + ) => + | epgGrabber.Types.SiteConfigParserResult[] + | Promise + logo: ((context: epgGrabber.Types.SiteConfigRequestContext) => string | Promise) | string + filepath: string + + constructor(config: epgGrabber.Types.SiteConfigObject) { + this.site = config.site + this.channels = config.channels + this.url = config.url + this.parser = config.parser + this.filepath = config.filepath + + this.days = config.days || _default.days + this.lang = config.lang || _default.lang + this.delay = config.delay || _default.delay + this.debug = config.debug || _default.debug + this.maxConnections = config.maxConnections || _default.maxConnections + this.gzip = config.gzip || _default.gzip + this.curl = config.curl || _default.curl + this.output = config.output || _default.output + this.logo = config.logo || _default.logo + + this.request = _.merge(_default.request, config.request) + } +} diff --git a/scripts/core/utils.ts b/scripts/core/utils.ts new file mode 100644 index 00000000..23d2145e --- /dev/null +++ b/scripts/core/utils.ts @@ -0,0 +1,106 @@ +import { restEndpointMethods } from '@octokit/plugin-rest-endpoint-methods' +import { paginateRest } from '@octokit/plugin-paginate-rest' +import { TESTING, OWNER, REPO, EOL } from '../constants' +import { Collection } from '@freearhey/core' +import { Channel } from '../models/channel' +import { AxiosProxyConfig } from 'axios' +import { Octokit } from '@octokit/core' +import { pathToFileURL } from 'url' +import { Issue } from '../models' +import { URL } from 'node:url' + +export function generateChannelsXML(channels: Collection): string { + let output = `${EOL}${EOL}` + + channels.forEach((channel: Channel) => { + const logo = channel.logo ? ` logo="${escapeString(channel.logo)}"` : '' + const xmltv_id = channel.xmltv_id ? escapeString(channel.xmltv_id) : '' + const lang = channel.lang || '' + const site_id = channel.site_id ? escapeString(channel.site_id) : '' + const site = channel.site || '' + const displayName = channel.name ? escapeString(channel.name) : '' + + output += ` ${displayName}${EOL}` + }) + + output += `${EOL}` + + return output +} + +export function escapeString(value: string, defaultValue = '') { + if (!value) return defaultValue + + const regex = new RegExp( + '((?:[\0-\x08\x0B\f\x0E-\x1F\uFFFD\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]))|([\\x7F-\\x84]|[\\x86-\\x9F]|[\\uFDD0-\\uFDEF]|(?:\\uD83F[\\uDFFE\\uDFFF])|(?:\\uD87F[\\uDF' + + 'FE\\uDFFF])|(?:\\uD8BF[\\uDFFE\\uDFFF])|(?:\\uD8FF[\\uDFFE\\uDFFF])|(?:\\uD93F[\\uDFFE\\uD' + + 'FFF])|(?:\\uD97F[\\uDFFE\\uDFFF])|(?:\\uD9BF[\\uDFFE\\uDFFF])|(?:\\uD9FF[\\uDFFE\\uDFFF])' + + '|(?:\\uDA3F[\\uDFFE\\uDFFF])|(?:\\uDA7F[\\uDFFE\\uDFFF])|(?:\\uDABF[\\uDFFE\\uDFFF])|(?:\\' + + 'uDAFF[\\uDFFE\\uDFFF])|(?:\\uDB3F[\\uDFFE\\uDFFF])|(?:\\uDB7F[\\uDFFE\\uDFFF])|(?:\\uDBBF' + + '[\\uDFFE\\uDFFF])|(?:\\uDBFF[\\uDFFE\\uDFFF])(?:[\\0-\\t\\x0B\\f\\x0E-\\u2027\\u202A-\\uD7FF\\' + + 'uE000-\\uFFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]|[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|' + + '(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF]))', + 'g' + ) + + value = String(value || '').replace(regex, '') + + return value + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') + .replace(/\n|\r/g, ' ') + .replace(/ +/g, ' ') + .trim() +} + +export function parseProxy(string: string): AxiosProxyConfig { + const parsed = new URL(string) + + const proxy: AxiosProxyConfig = { + protocol: parsed.protocol.replace(':', ''), + host: parsed.hostname, + port: parsed.port ? parseInt(parsed.port) : 8080 + } + + if (parsed.username || parsed.password) { + proxy.auth = { username: parsed.username, password: parsed.password } + } + + return proxy +} + +export async function loadJs(filepath: string) { + const fileUrl = pathToFileURL(filepath).toString() + + return (await import(fileUrl)).default +} + +export async function loadIssues(props?: { labels: string[] | string }) { + const CustomOctokit = Octokit.plugin(paginateRest, restEndpointMethods) + const octokit = new CustomOctokit() + + let labels = '' + if (props && props.labels) { + labels = Array.isArray(props.labels) ? props.labels.join(',') : props.labels + } + let issues: object[] = [] + if (TESTING) { + issues = (await import('../../tests/__data__/input/sites_update/issues.mjs')).default + } else { + issues = await octokit.paginate(octokit.rest.issues.listForRepo, { + owner: OWNER, + repo: REPO, + per_page: 100, + labels, + state: 'open', + headers: { + 'X-GitHub-Api-Version': '2022-11-28' + } + }) + } + + return new Collection(issues).map(data => new Issue(data)) +} diff --git a/scripts/models/channel.ts b/scripts/models/channel.ts index e5bbde5e..087a3977 100644 --- a/scripts/models/channel.ts +++ b/scripts/models/channel.ts @@ -1,164 +1,23 @@ -import { ChannelData, ChannelSearchableData } from '../types/channel' -import { Collection, Dictionary } from '@freearhey/core' -import { Stream, Feed, Logo, GuideChannel } from './' - -export class Channel { - id?: string - name?: string - altNames?: Collection - network?: string - owners?: Collection - countryCode?: string - subdivisionCode?: string - cityName?: string - categoryIds?: Collection - isNSFW = false - launched?: string - closed?: string - replacedBy?: string - website?: string - feeds?: Collection - logos: Collection = new Collection() - - constructor(data?: ChannelData) { - if (!data) return - - this.id = data.id - this.name = data.name - this.altNames = new Collection(data.alt_names) - this.network = data.network || undefined - this.owners = new Collection(data.owners) - this.countryCode = data.country - this.subdivisionCode = data.subdivision || undefined - this.cityName = data.city || undefined - this.categoryIds = new Collection(data.categories) - this.isNSFW = data.is_nsfw - this.launched = data.launched || undefined - this.closed = data.closed || undefined - this.replacedBy = data.replaced_by || undefined - this.website = data.website || undefined - } - - withFeeds(feedsGroupedByChannelId: Dictionary): this { - if (this.id) this.feeds = new Collection(feedsGroupedByChannelId.get(this.id)) - - return this - } - - withLogos(logosGroupedByChannelId: Dictionary): this { - if (this.id) this.logos = new Collection(logosGroupedByChannelId.get(this.id)) - - return this - } - - getFeeds(): Collection { - if (!this.feeds) return new Collection() - - return this.feeds - } - - getGuideChannels(): Collection { - let channels = new Collection() - - this.getFeeds().forEach((feed: Feed) => { - channels = channels.concat(feed.getGuideChannels()) - }) - - return channels - } - - getGuideChannelNames(): Collection { - return this.getGuideChannels() - .map((channel: GuideChannel) => channel.siteName) - .uniq() - } - - getStreams(): Collection { - let streams = new Collection() - - this.getFeeds().forEach((feed: Feed) => { - streams = streams.concat(feed.getStreams()) - }) - - return streams - } - - getStreamNames(): Collection { - return this.getStreams() - .map((stream: Stream) => stream.getName()) - .uniq() - } - - getFeedFullNames(): Collection { - return this.getFeeds() - .map((feed: Feed) => feed.getFullName()) - .uniq() - } - - getName(): string { - return this.name || '' - } - - getId(): string { - return this.id || '' - } - - getAltNames(): Collection { - return this.altNames || new Collection() - } - - getLogos(): Collection { - function feed(logo: Logo): number { - if (!logo.feed) return 1 - if (logo.feed.isMain) return 1 - - return 0 - } - - function format(logo: Logo): number { - const levelByFormat: Record = { - SVG: 0, - PNG: 3, - APNG: 1, - WebP: 1, - AVIF: 1, - JPEG: 2, - GIF: 1 - } - - return logo.format ? levelByFormat[logo.format] : 0 - } - - function size(logo: Logo): number { - return Math.abs(512 - logo.width) + Math.abs(512 - logo.height) - } - - return this.logos.orderBy([feed, format, size], ['desc', 'desc', 'asc'], false) - } - - getLogo(): Logo | undefined { - return this.getLogos().first() - } - - hasLogo(): boolean { - return this.getLogos().notEmpty() - } - - getLogoUrl(): string { - const logo = this.getLogo() - if (!logo) return '' - - return logo.url || '' - } - - getSearchable(): ChannelSearchableData { - return { - id: this.getId(), - name: this.getName(), - altNames: this.getAltNames().all(), - guideNames: this.getGuideChannelNames().all(), - streamNames: this.getStreamNames().all(), - feedFullNames: this.getFeedFullNames().all() - } - } -} +import { ChannelGuideObject } from '../types/channel' +import * as epgGrabber from 'epg-grabber' +import { SITES_DIR } from '../constants' +import path from 'node:path' + +export class Channel extends epgGrabber.Channel { + getGuideObject(): ChannelGuideObject { + const [channelId, feedId] = this.xmltv_id.split('@') + + return { + channel: channelId || null, + feed: feedId || null, + site: this.site, + site_id: this.site_id, + site_name: this.name, + lang: this.lang || 'en' + } + } + + getConfigPath(): string { + return path.resolve(SITES_DIR, `${this.site}/${this.site}.config.js`) + } +} diff --git a/scripts/models/channelList.ts b/scripts/models/channelList.ts deleted file mode 100644 index 951e0a90..00000000 --- a/scripts/models/channelList.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Collection } from '@freearhey/core' -import epgGrabber from 'epg-grabber' - -export class ChannelList { - channels: Collection = new Collection() - - constructor(data: { channels: epgGrabber.Channel[] }) { - this.channels = new Collection(data.channels) - } - - add(channel: epgGrabber.Channel): this { - this.channels.add(channel) - - return this - } - - get(siteId: string): epgGrabber.Channel | undefined { - return this.channels.find((channel: epgGrabber.Channel) => channel.site_id == siteId) - } - - sort(): this { - this.channels = this.channels.orderBy([ - (channel: epgGrabber.Channel) => channel.lang || '_', - (channel: epgGrabber.Channel) => (channel.xmltv_id ? channel.xmltv_id.toLowerCase() : '0'), - (channel: epgGrabber.Channel) => channel.site_id - ]) - - return this - } - - toString() { - function escapeString(value: string, defaultValue = '') { - if (!value) return defaultValue - - const regex = new RegExp( - '((?:[\0-\x08\x0B\f\x0E-\x1F\uFFFD\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]))|([\\x7F-\\x84]|[\\x86-\\x9F]|[\\uFDD0-\\uFDEF]|(?:\\uD83F[\\uDFFE\\uDFFF])|(?:\\uD87F[\\uDF' + - 'FE\\uDFFF])|(?:\\uD8BF[\\uDFFE\\uDFFF])|(?:\\uD8FF[\\uDFFE\\uDFFF])|(?:\\uD93F[\\uDFFE\\uD' + - 'FFF])|(?:\\uD97F[\\uDFFE\\uDFFF])|(?:\\uD9BF[\\uDFFE\\uDFFF])|(?:\\uD9FF[\\uDFFE\\uDFFF])' + - '|(?:\\uDA3F[\\uDFFE\\uDFFF])|(?:\\uDA7F[\\uDFFE\\uDFFF])|(?:\\uDABF[\\uDFFE\\uDFFF])|(?:\\' + - 'uDAFF[\\uDFFE\\uDFFF])|(?:\\uDB3F[\\uDFFE\\uDFFF])|(?:\\uDB7F[\\uDFFE\\uDFFF])|(?:\\uDBBF' + - '[\\uDFFE\\uDFFF])|(?:\\uDBFF[\\uDFFE\\uDFFF])(?:[\\0-\\t\\x0B\\f\\x0E-\\u2027\\u202A-\\uD7FF\\' + - 'uE000-\\uFFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]|[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|' + - '(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF]))', - 'g' - ) - - value = String(value || '').replace(regex, '') - - return value - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, ''') - .replace(/\n|\r/g, ' ') - .replace(/ +/g, ' ') - .trim() - } - - let output = '\r\n\r\n' - - this.channels.forEach((channel: epgGrabber.Channel) => { - const logo = channel.logo ? ` logo="${channel.logo}"` : '' - const xmltv_id = channel.xmltv_id ? escapeString(channel.xmltv_id) : '' - const lang = channel.lang || '' - const site_id = channel.site_id || '' - const site = channel.site || '' - const displayName = channel.name ? escapeString(channel.name) : '' - - output += ` ${displayName}\r\n` - }) - - output += '\r\n' - - return output - } -} diff --git a/scripts/models/feed.ts b/scripts/models/feed.ts deleted file mode 100644 index 7e91e305..00000000 --- a/scripts/models/feed.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { Collection, Dictionary } from '@freearhey/core' -import { FeedData } from '../types/feed' -import { Logo, Channel } from '.' - -export class Feed { - channelId: string - channel?: Channel - id: string - name: string - isMain: boolean - broadcastAreaCodes: Collection - languageCodes: Collection - timezoneIds: Collection - videoFormat: string - guideChannels?: Collection - streams?: Collection - logos: Collection = new Collection() - - constructor(data: FeedData) { - this.channelId = data.channel - this.id = data.id - this.name = data.name - this.isMain = data.is_main - this.broadcastAreaCodes = new Collection(data.broadcast_area) - this.languageCodes = new Collection(data.languages) - this.timezoneIds = new Collection(data.timezones) - this.videoFormat = data.video_format - } - - withChannel(channelsKeyById: Dictionary): this { - this.channel = channelsKeyById.get(this.channelId) - - return this - } - - withStreams(streamsGroupedById: Dictionary): this { - this.streams = new Collection(streamsGroupedById.get(`${this.channelId}@${this.id}`)) - - if (this.isMain) { - this.streams = this.streams.concat(new Collection(streamsGroupedById.get(this.channelId))) - } - - return this - } - - withGuideChannels(guideChannelsGroupedByStreamId: Dictionary): this { - this.guideChannels = new Collection( - guideChannelsGroupedByStreamId.get(`${this.channelId}@${this.id}`) - ) - - if (this.isMain) { - this.guideChannels = this.guideChannels.concat( - new Collection(guideChannelsGroupedByStreamId.get(this.channelId)) - ) - } - - return this - } - - withLogos(logosGroupedByStreamId: Dictionary): this { - this.logos = new Collection(logosGroupedByStreamId.get(this.getStreamId())) - - return this - } - - getGuideChannels(): Collection { - if (!this.guideChannels) return new Collection() - - return this.guideChannels - } - - getStreams(): Collection { - if (!this.streams) return new Collection() - - return this.streams - } - - getFullName(): string { - if (!this.channel) return '' - - return `${this.channel.name} ${this.name}` - } - - getStreamId(): string { - return `${this.channelId}@${this.id}` - } - - getLogos(): Collection { - function format(logo: Logo): number { - const levelByFormat: Record = { - SVG: 0, - PNG: 3, - APNG: 1, - WebP: 1, - AVIF: 1, - JPEG: 2, - GIF: 1 - } - - return logo.format ? levelByFormat[logo.format] : 0 - } - - function size(logo: Logo): number { - return Math.abs(512 - logo.width) + Math.abs(512 - logo.height) - } - - return this.logos.orderBy([format, size], ['desc', 'asc'], false) - } - - getLogo(): Logo | undefined { - return this.getLogos().first() - } - - hasLogo(): boolean { - return this.getLogos().notEmpty() - } - - getLogoUrl(): string { - const logo = this.getLogo() - if (!logo) return '' - - return logo.url || '' - } -} diff --git a/scripts/models/guide.ts b/scripts/models/guide.ts index 4072c8bc..8c367e6e 100644 --- a/scripts/models/guide.ts +++ b/scripts/models/guide.ts @@ -1,35 +1,59 @@ -import { Collection, DateTime } from '@freearhey/core' -import { generateXMLTV } from 'epg-grabber' - -interface GuideData { - channels: Collection - programs: Collection - filepath: string - gzip: boolean -} - -export class Guide { - channels: Collection - programs: Collection - filepath: string - gzip: boolean - - constructor({ channels, programs, filepath, gzip }: GuideData) { - this.channels = channels - this.programs = programs - this.filepath = filepath - this.gzip = gzip || false - } - - toString() { - const currDate = new DateTime(process.env.CURR_DATE || new Date().toISOString(), { - timezone: 'UTC' - }) - - return generateXMLTV({ - channels: this.channels.all(), - programs: this.programs.all(), - date: currDate.toJSON() - }) - } -} +import { Collection, Logger } from '@freearhey/core' +import { Storage } from '@freearhey/storage-js' +import { EPGGrabber } from 'epg-grabber' +import { Channel, Program } from '.' +import utc from 'dayjs/plugin/utc' +import dayjs from 'dayjs' +import path from 'node:path' +import pako from 'pako' + +dayjs.extend(utc) + +interface GuideData { + channels: Collection + programs: Collection + filepath: string + gzip: boolean +} + +export class Guide { + channels: Collection + programs: Collection + filepath: string + gzip: boolean + + constructor(data: GuideData) { + this.channels = data.channels + this.programs = data.programs + this.filepath = data.filepath + this.gzip = data.gzip || false + } + + addChannel(channel: Channel) { + this.channels.add(channel) + } + + toString() { + const currDate = dayjs.utc(process.env.CURR_DATE || new Date().toISOString()) + + return EPGGrabber.generateXMLTV(this.channels.all(), this.programs.all(), currDate) + } + + async save({ logger }: { logger: Logger }) { + const dir = path.dirname(this.filepath) + const storage = new Storage(dir) + const xmlFilepath = this.filepath + const xmlFilename = path.basename(xmlFilepath) + logger.info(` saving to "${xmlFilepath}"...`) + const xmltv = this.toString() + await storage.save(xmlFilename, xmltv) + + if (this.gzip) { + const compressed = pako.gzip(xmltv) + const gzFilepath = `${this.filepath}.gz` + const gzFilename = path.basename(gzFilepath) + logger.info(` saving to "${gzFilepath}"...`) + await storage.save(gzFilename, compressed) + } + } +} diff --git a/scripts/models/guideChannel.ts b/scripts/models/guideChannel.ts deleted file mode 100644 index 4876a89d..00000000 --- a/scripts/models/guideChannel.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Dictionary } from '@freearhey/core' -import epgGrabber from 'epg-grabber' -import { Feed, Channel } from '.' - -export class GuideChannel { - channelId?: string - channel?: Channel - feedId?: string - feed?: Feed - xmltvId?: string - languageCode?: string - siteId?: string - logoUrl?: string - siteDomain?: string - siteName?: string - - constructor(data: epgGrabber.Channel) { - const [channelId, feedId] = data.xmltv_id ? data.xmltv_id.split('@') : [undefined, undefined] - - this.channelId = channelId - this.feedId = feedId - this.xmltvId = data.xmltv_id - this.languageCode = data.lang - this.siteId = data.site_id - this.logoUrl = data.logo - this.siteDomain = data.site - this.siteName = data.name - } - - withChannel(channelsKeyById: Dictionary): this { - if (this.channelId) this.channel = channelsKeyById.get(this.channelId) - - return this - } - - withFeed(feedsKeyByStreamId: Dictionary): this { - if (this.feedId) this.feed = feedsKeyByStreamId.get(this.getStreamId()) - - return this - } - - getStreamId(): string { - if (!this.channelId) return '' - if (!this.feedId) return this.channelId - - return `${this.channelId}@${this.feedId}` - } - - toJSON() { - return { - channel: this.channelId || null, - feed: this.feedId || null, - site: this.siteDomain || '', - site_id: this.siteId || '', - site_name: this.siteName || '', - lang: this.languageCode || '' - } - } -} diff --git a/scripts/models/index.ts b/scripts/models/index.ts index b97d859c..a05f13a8 100644 --- a/scripts/models/index.ts +++ b/scripts/models/index.ts @@ -1,9 +1,5 @@ -export * from './channel' -export * from './feed' -export * from './guide' -export * from './guideChannel' -export * from './issue' -export * from './logo' -export * from './site' -export * from './stream' -export * from './channelList' +export * from './guide' +export * from './issue' +export * from './site' +export * from './channel' +export * from './program' diff --git a/scripts/models/issue.ts b/scripts/models/issue.ts index a26e71ff..6710b148 100644 --- a/scripts/models/issue.ts +++ b/scripts/models/issue.ts @@ -1,24 +1,47 @@ -import { Dictionary } from '@freearhey/core' -import { OWNER, REPO } from '../constants' - -interface IssueProps { - number: number - labels: string[] - data: Dictionary -} - -export class Issue { - number: number - labels: string[] - data: Dictionary - - constructor({ number, labels, data }: IssueProps) { - this.number = number - this.labels = labels - this.data = data - } - - getURL() { - return `https://github.com/${OWNER}/${REPO}/issues/${this.number}` - } -} +import { EOL, OWNER, REPO } from '../constants' +import { Dictionary } from '@freearhey/core' + +const FIELDS = new Dictionary({ + Site: 'site' +}) + +interface IssueData { + number: number + body: string + labels: { name: string }[] +} + +export class Issue { + number: number + labels: string[] + data: Dictionary + + constructor(issue: IssueData) { + const fields = typeof issue.body === 'string' ? issue.body.split('###') : [] + + this.data = new Dictionary() + fields.forEach((field: string) => { + const parsed = field.split(/\r?\n/).filter(Boolean) + let _label = parsed.shift() + _label = _label ? _label.trim() : '' + let _value = parsed.join(EOL) + _value = _value ? _value.trim() : '' + + if (!_label || !_value) return + + const id: string | undefined = FIELDS.get(_label) + const value: string = _value === '_No response_' || _value === 'None' ? '' : _value + + if (!id) return + + this.data.set(id, value) + }) + + this.labels = issue.labels.map(label => label.name) + this.number = issue.number + } + + getURL() { + return `https://github.com/${OWNER}/${REPO}/issues/${this.number}` + } +} diff --git a/scripts/models/logo.ts b/scripts/models/logo.ts deleted file mode 100644 index e08f443e..00000000 --- a/scripts/models/logo.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Collection, type Dictionary } from '@freearhey/core' -import type { LogoData } from '../types/logo' -import { type Feed } from './feed' - -export class Logo { - channelId?: string - feedId?: string - feed?: Feed - tags: Collection = new Collection() - width = 0 - height = 0 - format?: string - url?: string - - constructor(data?: LogoData) { - if (!data) return - - this.channelId = data.channel - this.feedId = data.feed || undefined - this.tags = new Collection(data.tags) - this.width = data.width - this.height = data.height - this.format = data.format || undefined - this.url = data.url - } - - withFeed(feedsKeyByStreamId: Dictionary): this { - if (!this.feedId) return this - - this.feed = feedsKeyByStreamId.get(this.getStreamId()) - - return this - } - - getStreamId(): string { - if (!this.channelId) return '' - if (!this.feedId) return this.channelId - - return `${this.channelId}@${this.feedId}` - } -} diff --git a/scripts/models/program.ts b/scripts/models/program.ts new file mode 100644 index 00000000..11051607 --- /dev/null +++ b/scripts/models/program.ts @@ -0,0 +1,3 @@ +import * as epgGrabber from 'epg-grabber' + +export class Program extends epgGrabber.Program {} diff --git a/scripts/models/site.ts b/scripts/models/site.ts index d4ddfdaa..27d9d1ef 100644 --- a/scripts/models/site.ts +++ b/scripts/models/site.ts @@ -1,63 +1,63 @@ -import { Collection } from '@freearhey/core' -import { Issue } from './' - -enum StatusCode { - DOWN = 'down', - WARNING = 'warning', - OK = 'ok' -} - -interface Status { - code: StatusCode - emoji: string -} - -interface SiteProps { - domain: string - totalChannels?: number - markedChannels?: number - issues: Collection -} - -export class Site { - domain: string - totalChannels: number - markedChannels: number - issues: Collection - - constructor({ domain, totalChannels = 0, markedChannels = 0, issues }: SiteProps) { - this.domain = domain - this.totalChannels = totalChannels - this.markedChannels = markedChannels - this.issues = issues - } - - getStatus(): Status { - const issuesWithStatusDown = this.issues.filter((issue: Issue) => - issue.labels.find(label => label === 'status:down') - ) - if (issuesWithStatusDown.notEmpty()) - return { - code: StatusCode.DOWN, - emoji: '🔴' - } - - const issuesWithStatusWarning = this.issues.filter((issue: Issue) => - issue.labels.find(label => label === 'status:warning') - ) - if (issuesWithStatusWarning.notEmpty()) - return { - code: StatusCode.WARNING, - emoji: '🟡' - } - - return { - code: StatusCode.OK, - emoji: '🟢' - } - } - - getIssues(): Collection { - return this.issues.map((issue: Issue) => issue.getURL()) - } -} +import { Collection } from '@freearhey/core' +import { Issue } from './' + +enum StatusCode { + DOWN = 'down', + WARNING = 'warning', + OK = 'ok' +} + +export interface Status { + code: StatusCode + emoji: string +} + +export interface SiteData { + domain: string + totalChannels?: number + markedChannels?: number + issues: Collection +} + +export class Site { + domain: string + totalChannels: number + markedChannels: number + issues: Collection + + constructor(data: SiteData) { + this.domain = data.domain + this.totalChannels = data.totalChannels || 0 + this.markedChannels = data.markedChannels || 0 + this.issues = data.issues + } + + getStatus(): Status { + const issuesWithStatusDown = this.issues.filter((issue: Issue) => + issue.labels.find(label => label === 'status:down') + ) + if (issuesWithStatusDown.isNotEmpty()) + return { + code: StatusCode.DOWN, + emoji: '🔴' + } + + const issuesWithStatusWarning = this.issues.filter((issue: Issue) => + issue.labels.find(label => label === 'status:warning') + ) + if (issuesWithStatusWarning.isNotEmpty()) + return { + code: StatusCode.WARNING, + emoji: '🟡' + } + + return { + code: StatusCode.OK, + emoji: '🟢' + } + } + + getIssueUrls(): Collection { + return this.issues.map((issue: Issue) => issue.getURL()) + } +} diff --git a/scripts/models/stream.ts b/scripts/models/stream.ts deleted file mode 100644 index c519bdfb..00000000 --- a/scripts/models/stream.ts +++ /dev/null @@ -1,58 +0,0 @@ -import type { StreamData } from '../types/stream' -import { Feed, Channel } from './index' - -export class Stream { - name?: string - url: string - id?: string - channelId?: string - channel?: Channel - feedId?: string - feed?: Feed - filepath?: string - line?: number - label?: string - verticalResolution?: number - isInterlaced?: boolean - referrer?: string - userAgent?: string - groupTitle = 'Undefined' - removed = false - - constructor(data: StreamData) { - const id = data.channel && data.feed ? [data.channel, data.feed].join('@') : data.channel - const { verticalResolution, isInterlaced } = parseQuality(data.quality) - - this.id = id || undefined - this.channelId = data.channel || undefined - this.feedId = data.feed || undefined - this.name = data.name || undefined - this.url = data.url - this.referrer = data.referrer || undefined - this.userAgent = data.user_agent || undefined - this.verticalResolution = verticalResolution || undefined - this.isInterlaced = isInterlaced || undefined - this.label = data.label || undefined - } - - getId(): string { - return this.id || '' - } - - getName(): string { - return this.name || '' - } -} - -function parseQuality(quality: string | null): { - verticalResolution: number | null - isInterlaced: boolean | null -} { - if (!quality) return { verticalResolution: null, isInterlaced: null } - const [, verticalResolutionString] = quality.match(/^(\d+)/) || [null, undefined] - const isInterlaced = /i$/i.test(quality) - let verticalResolution = 0 - if (verticalResolutionString) verticalResolution = parseInt(verticalResolutionString) - - return { verticalResolution, isInterlaced } -} diff --git a/scripts/templates/_config.js b/scripts/templates/_config.js index b4eb9b46..2e40921d 100644 --- a/scripts/templates/_config.js +++ b/scripts/templates/_config.js @@ -1,16 +1,16 @@ -module.exports = { - site: '', - url({ channel, date }) { - return `https://example.com/api/${channel.site_id}/${date.format('YYYY-MM-DD')}` - }, - parser({ content }) { - try { - return JSON.parse(content) - } catch { - return [] - } - }, - channels() { - return [] - } -} +module.exports = { + site: '', + url({ channel, date }) { + return `https://example.com/api/${channel.site_id}/${date.format('YYYY-MM-DD')}` + }, + parser({ content }) { + try { + return JSON.parse(content) + } catch { + return [] + } + }, + channels() { + return [] + } +} diff --git a/scripts/templates/_readme.md b/scripts/templates/_readme.md index 0b807f7f..4aa94527 100644 --- a/scripts/templates/_readme.md +++ b/scripts/templates/_readme.md @@ -1,21 +1,21 @@ -# - -https:// - -### Download the guide - -```sh -npm run grab --- --site= -``` - -### Update channel list - -```sh -npm run channels:parse --- --config=./sites//.config.js --output=./sites//.channels.xml -``` - -### Test - -```sh -npm test --- -``` +# + +https:// + +### Download the guide + +```sh +npm run grab --- --site= +``` + +### Update channel list + +```sh +npm run channels:parse --- --config=./sites//.config.js --output=./sites//.channels.xml +``` + +### Test + +```sh +npm test --- +``` diff --git a/scripts/templates/_sites.md b/scripts/templates/_sites.md index d0633a4c..46191a4e 100644 --- a/scripts/templates/_sites.md +++ b/scripts/templates/_sites.md @@ -1,3 +1,3 @@ -# Sites - -_TABLE_ +# Sites + +_TABLE_ diff --git a/scripts/templates/_test.js b/scripts/templates/_test.js index b02a2648..6375d7e7 100644 --- a/scripts/templates/_test.js +++ b/scripts/templates/_test.js @@ -1,38 +1,38 @@ -const { parser, url } = require('./.config.js') -const dayjs = require('dayjs') -const utc = require('dayjs/plugin/utc') -const customParseFormat = require('dayjs/plugin/customParseFormat') -dayjs.extend(customParseFormat) -dayjs.extend(utc) - -const date = dayjs.utc('2025-01-12', 'YYYY-MM-DD').startOf('d') -const channel = { site_id: 'bbc1' } - -it('can generate valid url', () => { - expect(url({ channel, date })).toBe('https://example.com/api/bbc1/2025-01-12') -}) - -it('can parse response', () => { - const content = - '[{"title":"Program 1","start":"2025-01-12T00:00:00.000Z","stop":"2025-01-12T00:30:00.000Z"},{"title":"Program 2","start":"2025-01-12T00:30:00.000Z","stop":"2025-01-12T01:00:00.000Z"}]' - - const results = parser({ content }) - - expect(results.length).toBe(2) - expect(results[0]).toMatchObject({ - title: 'Program 1', - start: '2025-01-12T00:00:00.000Z', - stop: '2025-01-12T00:30:00.000Z' - }) - expect(results[1]).toMatchObject({ - title: 'Program 2', - start: '2025-01-12T00:30:00.000Z', - stop: '2025-01-12T01:00:00.000Z' - }) -}) - -it('can handle empty guide', () => { - const results = parser({ content: '' }) - - expect(results).toMatchObject([]) -}) +const { parser, url } = require('./.config.js') +const dayjs = require('dayjs') +const utc = require('dayjs/plugin/utc') +const customParseFormat = require('dayjs/plugin/customParseFormat') +dayjs.extend(customParseFormat) +dayjs.extend(utc) + +const date = dayjs.utc('2025-01-12', 'YYYY-MM-DD').startOf('d') +const channel = { site_id: 'bbc1' } + +it('can generate valid url', () => { + expect(url({ channel, date })).toBe('https://example.com/api/bbc1/2025-01-12') +}) + +it('can parse response', () => { + const content = + '[{"title":"Program 1","start":"2025-01-12T00:00:00.000Z","stop":"2025-01-12T00:30:00.000Z"},{"title":"Program 2","start":"2025-01-12T00:30:00.000Z","stop":"2025-01-12T01:00:00.000Z"}]' + + const results = parser({ content }) + + expect(results.length).toBe(2) + expect(results[0]).toMatchObject({ + title: 'Program 1', + start: '2025-01-12T00:00:00.000Z', + stop: '2025-01-12T00:30:00.000Z' + }) + expect(results[1]).toMatchObject({ + title: 'Program 2', + start: '2025-01-12T00:30:00.000Z', + stop: '2025-01-12T01:00:00.000Z' + }) +}) + +it('can handle empty guide', () => { + const results = parser({ content: '' }) + + expect(results).toMatchObject([]) +}) diff --git a/scripts/types/channel.d.ts b/scripts/types/channel.d.ts index b2c709e1..3bd35d6f 100644 --- a/scripts/types/channel.d.ts +++ b/scripts/types/channel.d.ts @@ -1,27 +1,8 @@ -import { Collection } from '@freearhey/core' - -export interface ChannelData { - id: string - name: string - alt_names: string[] - network: string - owners: Collection - country: string - subdivision: string - city: string - categories: Collection - is_nsfw: boolean - launched: string - closed: string - replaced_by: string - website: string -} - -export interface ChannelSearchableData { - id: string - name: string - altNames: string[] - guideNames: string[] - streamNames: string[] - feedFullNames: string[] -} +export interface ChannelGuideObject { + channel: string | null + feed: string | null + site: string + site_id: string + site_name: string + lang: string +} diff --git a/scripts/types/dataLoader.d.ts b/scripts/types/dataLoader.d.ts deleted file mode 100644 index 98d3d911..00000000 --- a/scripts/types/dataLoader.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Storage } from '@freearhey/core' - -export interface DataLoaderProps { - storage: Storage -} - -export interface DataLoaderData { - countries: object | object[] - regions: object | object[] - subdivisions: object | object[] - languages: object | object[] - categories: object | object[] - blocklist: object | object[] - channels: object | object[] - feeds: object | object[] - timezones: object | object[] - guides: object | object[] - streams: object | object[] - logos: object | object[] -} diff --git a/scripts/types/dataProcessor.d.ts b/scripts/types/dataProcessor.d.ts deleted file mode 100644 index e50915d1..00000000 --- a/scripts/types/dataProcessor.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Collection, Dictionary } from '@freearhey/core' - -export interface DataProcessorData { - guideChannelsGroupedByStreamId: Dictionary - feedsGroupedByChannelId: Dictionary - logosGroupedByChannelId: Dictionary - logosGroupedByStreamId: Dictionary - feedsKeyByStreamId: Dictionary - streamsGroupedById: Dictionary - channelsKeyById: Dictionary - guideChannels: Collection - channels: Collection - streams: Collection - feeds: Collection - logos: Collection -} diff --git a/scripts/types/feed.d.ts b/scripts/types/feed.d.ts deleted file mode 100644 index 00c49260..00000000 --- a/scripts/types/feed.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Collection } from '@freearhey/core' - -export interface FeedData { - channel: string - id: string - name: string - is_main: boolean - broadcast_area: Collection - languages: Collection - timezones: Collection - video_format: string -} diff --git a/scripts/types/guide.d.ts b/scripts/types/guide.d.ts deleted file mode 100644 index 9fbe4da3..00000000 --- a/scripts/types/guide.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface GuideData { - channel: string - feed: string - site: string - site_id: string - site_name: string - lang: string -} diff --git a/scripts/types/htmlTable.d.ts b/scripts/types/htmlTable.d.ts new file mode 100644 index 00000000..2dc9a0a0 --- /dev/null +++ b/scripts/types/htmlTable.d.ts @@ -0,0 +1,17 @@ +import { Collection } from '@freearhey/core' + +export interface HTMLTableColumn { + name: string + nowrap?: boolean + align?: string + colspan?: number +} + +export interface HTMLTableDataItem { + value: string + nowrap?: boolean + align?: string + colspan?: number +} + +export type HTMLTableRow = Collection diff --git a/scripts/types/langs.d.ts b/scripts/types/langs.d.ts index 60fb498a..74921c68 100644 --- a/scripts/types/langs.d.ts +++ b/scripts/types/langs.d.ts @@ -1 +1 @@ -declare module 'langs' +declare module 'langs' diff --git a/scripts/types/logo.d.ts b/scripts/types/logo.d.ts deleted file mode 100644 index d8c54b04..00000000 --- a/scripts/types/logo.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -export interface LogoData { - channel: string - feed: string | null - tags: string[] - width: number - height: number - format: string | null - url: string -} diff --git a/scripts/types/queue.d.ts b/scripts/types/queue.d.ts new file mode 100644 index 00000000..26615c15 --- /dev/null +++ b/scripts/types/queue.d.ts @@ -0,0 +1,10 @@ +import { SiteConfig } from '../core/siteConfig' +import { Channel } from '../models/channel' +import { Dayjs } from 'dayjs' + +export interface QueueItem { + channel: Channel + date: Dayjs + siteConfig: SiteConfig + error: string | null +} diff --git a/scripts/types/stream.d.ts b/scripts/types/stream.d.ts deleted file mode 100644 index c3365889..00000000 --- a/scripts/types/stream.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -export interface StreamData { - channel: string | null - feed: string | null - name?: string - url: string - referrer: string | null - user_agent: string | null - quality: string | null - label: string | null -} diff --git a/tests/__data__/expected/channels_format/example.com.channels.xml b/tests/__data__/expected/channels_format/example.com.channels.xml new file mode 100644 index 00000000..398df20e --- /dev/null +++ b/tests/__data__/expected/channels_format/example.com.channels.xml @@ -0,0 +1,5 @@ + + + CNN International + BBC World News + diff --git a/tests/__data__/expected/channels_parse/example.com.channels.xml b/tests/__data__/expected/channels_parse/example.com.channels.xml index 71622a50..66ea7de5 100644 --- a/tests/__data__/expected/channels_parse/example.com.channels.xml +++ b/tests/__data__/expected/channels_parse/example.com.channels.xml @@ -1,5 +1,5 @@ - - - BBC World News - CNN International - + + + BBC World News + CNN International + diff --git a/tests/__data__/expected/epg_grab/base.guide.xml b/tests/__data__/expected/epg_grab/base.guide.xml index 0f9c2ca0..a0ed3732 100644 --- a/tests/__data__/expected/epg_grab/base.guide.xml +++ b/tests/__data__/expected/epg_grab/base.guide.xml @@ -1,9 +1,12 @@ - -Channel 2https://example.com36 -Channel 1https://example.com -Channel 1https://example.com -Programme1 (example.com) -Program1 (example.com) -Programme1 (example.com) -Program1 (example.com) + +Channel 1https://example.com +Channel 2https://example.com36 +Channel 1https://example.com +Channel 3https://example.com +Programme1 (example.com) +Programme1 (example.com) +Program1 (example.com) +Program1 (example.com) +Program1 (example.com) +Program1 (example.com) \ No newline at end of file diff --git a/tests/__data__/expected/epg_grab/custom_channels.guide.xml b/tests/__data__/expected/epg_grab/custom_channels.guide.xml index dd84f7dc..932a9e6a 100644 --- a/tests/__data__/expected/epg_grab/custom_channels.guide.xml +++ b/tests/__data__/expected/epg_grab/custom_channels.guide.xml @@ -1,15 +1,15 @@ - -Custom Channel 1https://example.com -Custom Channel 2https://example.com -Channel 1https://example.com -Channel 3https://example2.com -Channel 4https://example2.com -Channel 1https://example2.com -Programme1 (example.com) -Program1 (example.com) -Programme1 (example2.com) -Programme1 (example.com) -Program1 (example.com) -Program1 (example2.com) -Program1 (example2.com) + +Custom Channel 1https://example.com +Custom Channel 2https://example.com +Channel 1https://example.com +Channel 3https://example2.com +Channel 4https://example2.com +Channel 1https://example2.com +Programme1 (example.com) +Program1 (example.com) +Programme1 (example2.com) +Programme1 (example.com) +Program1 (example.com) +Program1 (example2.com) +Program1 (example2.com) \ No newline at end of file diff --git a/tests/__data__/expected/epg_grab/guides/en/example.com.xml b/tests/__data__/expected/epg_grab/guides/en/example.com.xml deleted file mode 100644 index 8b20e42a..00000000 --- a/tests/__data__/expected/epg_grab/guides/en/example.com.xml +++ /dev/null @@ -1,6 +0,0 @@ - -Channel 2https://example.com36 -Channel 1https://example.com -Program1 (example.com) -Program1 (example.com) - \ No newline at end of file diff --git a/tests/__data__/expected/epg_grab/guides/fr/example.com.xml b/tests/__data__/expected/epg_grab/guides/fr/example.com.xml deleted file mode 100644 index 09dc5a3c..00000000 --- a/tests/__data__/expected/epg_grab/guides/fr/example.com.xml +++ /dev/null @@ -1,5 +0,0 @@ - -Channel 1https://example.com -Programme1 (example.com) -Programme1 (example.com) - \ No newline at end of file diff --git a/tests/__data__/expected/epg_grab/gzip.guide.xml b/tests/__data__/expected/epg_grab/gzip.guide.xml new file mode 100644 index 00000000..67968c1b --- /dev/null +++ b/tests/__data__/expected/epg_grab/gzip.guide.xml @@ -0,0 +1,8 @@ + +Channel 3https://example2.com +Channel 4https://example2.com +Channel 1https://example2.com +Programme1 (example2.com) +Program1 (example2.com) +Program1 (example2.com) + \ No newline at end of file diff --git a/tests/__data__/expected/epg_grab/gzip.guide.xml.gz b/tests/__data__/expected/epg_grab/gzip.guide.xml.gz new file mode 100644 index 00000000..ccce7906 Binary files /dev/null and b/tests/__data__/expected/epg_grab/gzip.guide.xml.gz differ diff --git a/tests/__data__/expected/epg_grab/lang/fr/guide.xml b/tests/__data__/expected/epg_grab/lang/fr/guide.xml new file mode 100644 index 00000000..dde894e5 --- /dev/null +++ b/tests/__data__/expected/epg_grab/lang/fr/guide.xml @@ -0,0 +1,5 @@ + +Channel 1https://example.com +Programme1 (example.com) +Programme1 (example.com) + \ No newline at end of file diff --git a/tests/__data__/expected/epg_grab/lang.guide.xml b/tests/__data__/expected/epg_grab/multilang.guide.xml similarity index 57% rename from tests/__data__/expected/epg_grab/lang.guide.xml rename to tests/__data__/expected/epg_grab/multilang.guide.xml index 7016b8e8..40e5b5aa 100644 --- a/tests/__data__/expected/epg_grab/lang.guide.xml +++ b/tests/__data__/expected/epg_grab/multilang.guide.xml @@ -1,8 +1,8 @@ - -Channel 1https://example.com -Channel 3https://example.com -Programme1 (example.com) -Programme1 (example.com) -Program1 (example.com) -Program1 (example.com) + +Channel 1https://example.com +Channel 3https://example.com +Programme1 (example.com) +Programme1 (example.com) +Program1 (example.com) +Program1 (example.com) \ No newline at end of file diff --git a/tests/__data__/expected/epg_grab/multiple_channels.guide.xml b/tests/__data__/expected/epg_grab/multiple_channels.guide.xml new file mode 100644 index 00000000..489914a7 --- /dev/null +++ b/tests/__data__/expected/epg_grab/multiple_channels.guide.xml @@ -0,0 +1,18 @@ + +Channel 1https://example.com +Channel 2https://example.com36 +Channel 1https://example.com +Channel 3https://example.com +Channel 3https://example2.com +Channel 4https://example2.com +Channel 1https://example2.com +Programme1 (example.com) +Programme1 (example.com) +Programme1 (example2.com) +Program1 (example.com) +Program1 (example.com) +Program1 (example2.com) +Program1 (example.com) +Program1 (example.com) +Program1 (example2.com) + \ No newline at end of file diff --git a/tests/__data__/expected/epg_grab/proxy.guide.xml b/tests/__data__/expected/epg_grab/proxy.guide.xml deleted file mode 100644 index 0f9c2ca0..00000000 --- a/tests/__data__/expected/epg_grab/proxy.guide.xml +++ /dev/null @@ -1,9 +0,0 @@ - -Channel 2https://example.com36 -Channel 1https://example.com -Channel 1https://example.com -Programme1 (example.com) -Program1 (example.com) -Programme1 (example.com) -Program1 (example.com) - \ No newline at end of file diff --git a/tests/__data__/expected/epg_grab/template.guide.xml b/tests/__data__/expected/epg_grab/template.guide.xml deleted file mode 100644 index 8eb79411..00000000 --- a/tests/__data__/expected/epg_grab/template.guide.xml +++ /dev/null @@ -1,15 +0,0 @@ - -Channel 2https://example.com36 -Channel 1https://example.com -Channel 1https://example.com -Channel 3https://example2.com -Channel 4https://example2.com -Channel 1https://example2.com -Programme1 (example.com) -Program1 (example.com) -Programme1 (example2.com) -Programme1 (example.com) -Program1 (example.com) -Program1 (example2.com) -Program1 (example2.com) - \ No newline at end of file diff --git a/tests/__data__/expected/epg_grab/template.guide.xml.gz b/tests/__data__/expected/epg_grab/template.guide.xml.gz deleted file mode 100644 index 126b426d..00000000 Binary files a/tests/__data__/expected/epg_grab/template.guide.xml.gz and /dev/null differ diff --git a/tests/__data__/expected/epg_grab/wildcard/example.com/en/guide.xml b/tests/__data__/expected/epg_grab/wildcard/example.com/en/guide.xml new file mode 100644 index 00000000..6426626a --- /dev/null +++ b/tests/__data__/expected/epg_grab/wildcard/example.com/en/guide.xml @@ -0,0 +1,6 @@ + +Channel 1https://example.com +Channel 2https://example.com36 +Program1 (example.com) +Program1 (example.com) + \ No newline at end of file diff --git a/tests/__data__/expected/epg_grab/wildcard/example.com/fr/guide.xml b/tests/__data__/expected/epg_grab/wildcard/example.com/fr/guide.xml new file mode 100644 index 00000000..dde894e5 --- /dev/null +++ b/tests/__data__/expected/epg_grab/wildcard/example.com/fr/guide.xml @@ -0,0 +1,5 @@ + +Channel 1https://example.com +Programme1 (example.com) +Programme1 (example.com) + \ No newline at end of file diff --git a/tests/__data__/expected/epg_grab/wildcard/example.com/it/guide.xml b/tests/__data__/expected/epg_grab/wildcard/example.com/it/guide.xml new file mode 100644 index 00000000..9d839e5e --- /dev/null +++ b/tests/__data__/expected/epg_grab/wildcard/example.com/it/guide.xml @@ -0,0 +1,5 @@ + +Channel 3https://example.com +Program1 (example.com) +Program1 (example.com) + \ No newline at end of file diff --git a/tests/__data__/input/__data__/channels.json b/tests/__data__/input/__data__/channels.json deleted file mode 100644 index d2e6a4d5..00000000 --- a/tests/__data__/input/__data__/channels.json +++ /dev/null @@ -1,61 +0,0 @@ -[ - { - "id": "Bravo.us", - "name": "Bravo", - "network": null, - "country": "US", - "subdivision": null, - "city": null, - "categories": [], - "is_nsfw": false, - "closed": "2020-01-01", - "replaced_by": "R6.co" - }, - { - "id": "Bravos.us", - "name": "Bravos", - "network": null, - "country": "US", - "subdivision": null, - "city": null, - "categories": [], - "is_nsfw": false - }, - { - "id": "CNNInternational.us", - "name": "CNN International", - "alt_names": ["CNN", "CNN Int"], - "network": null, - "country": "US", - "subdivision": null, - "city": null, - "categories": [ - "news" - ], - "is_nsfw": false - }, - { - "id": "MNetMovies2.za", - "name": "M-Net Movies 2", - "network": null, - "country": "ZA", - "subdivision": null, - "city": null, - "categories": [], - "is_nsfw": false - }, - {"id":"6eren.dk","name":"6'eren","alt_names":[],"network":null,"owners":["Warner Bros. Discovery EMEA"],"country":"DK","subdivision":null,"city":null,"broadcast_area":["c/DK"],"languages":["dan"],"categories":[],"is_nsfw":false,"launched":"2009-01-01","closed":null,"replaced_by":null,"website":"http://www.6-eren.dk/"}, - {"id":"BBCNews.uk","name":"BBC News","alt_names":[],"network":null,"owners":[],"country":"UK","subdivision":null,"city":null,"broadcast_area":["c/UK"],"languages":["eng"],"categories":["news"],"is_nsfw":false,"launched":null,"closed":null,"replaced_by":null,"website":"http://news.bbc.co.uk/"}, - { - "id": "CNN.us", - "name": "CNN", - "network": null, - "country": "US", - "subdivision": null, - "city": null, - "categories": [], - "is_nsfw": false - }, - {"id":"Channel2.us","name":"Channel 2 [API]","alt_names":[],"network":null,"owners":[],"country":"UK","subdivision":null,"city":null,"broadcast_area":["c/US"],"languages":["eng"],"categories":[],"is_nsfw":false,"launched":null,"closed":null,"replaced_by":null,"website":""}, - {"id":"Channel3.us","name":"Channel 3 [API]","alt_names":[],"network":null,"owners":[],"country":"UK","subdivision":null,"city":null,"broadcast_area":["c/US"],"languages":["eng"],"categories":[],"is_nsfw":false,"launched":null,"closed":null,"replaced_by":null,"website":""} -] \ No newline at end of file diff --git a/tests/__data__/input/__data__/logos.json b/tests/__data__/input/__data__/logos.json deleted file mode 100644 index 4aa0a9a9..00000000 --- a/tests/__data__/input/__data__/logos.json +++ /dev/null @@ -1,4 +0,0 @@ -[ - {"channel":"Channel3.us","feed":null,"tags":[],"width":334,"height":210,"format":"PNG","url":"https://upload.wikimedia.org/wikipedia/commons/6/64/6%27eren_2015.png"}, - {"channel":"Channel4.us","feed":"HD","tags":[],"width":334,"height":210,"format":"PNG","url":"https://i.imgur.com/BPzH88J.png"} -] \ No newline at end of file diff --git a/tests/__data__/input/channels_format/example.com.channels.xml b/tests/__data__/input/channels_format/example.com.channels.xml new file mode 100644 index 00000000..cbe336df --- /dev/null +++ b/tests/__data__/input/channels_format/example.com.channels.xml @@ -0,0 +1,5 @@ + + + BBC World News + CNN International + diff --git a/tests/__data__/input/__data__/blocklist.json b/tests/__data__/input/data/blocklist.json similarity index 100% rename from tests/__data__/input/__data__/blocklist.json rename to tests/__data__/input/data/blocklist.json diff --git a/tests/__data__/input/__data__/categories.json b/tests/__data__/input/data/categories.json similarity index 100% rename from tests/__data__/input/__data__/categories.json rename to tests/__data__/input/data/categories.json diff --git a/tests/__data__/input/data/channels.json b/tests/__data__/input/data/channels.json new file mode 100644 index 00000000..269920f6 --- /dev/null +++ b/tests/__data__/input/data/channels.json @@ -0,0 +1,138 @@ +[ + { + "id": "Bravo.us", + "name": "Bravo", + "network": null, + "country": "US", + "categories": [], + "is_nsfw": false, + "closed": "2020-01-01", + "replaced_by": "R6.co" + }, + { + "id": "Bravos.us", + "name": "Bravos", + "network": null, + "country": "US", + "categories": [], + "is_nsfw": false + }, + { + "id": "CNNInternational.us", + "name": "CNN International", + "alt_names": [ + "CNN", + "CNN Int" + ], + "network": null, + "country": "US", + "categories": [ + "news" + ], + "is_nsfw": false + }, + { + "id": "MNetMovies2.za", + "name": "M-Net Movies 2", + "network": null, + "country": "ZA", + "categories": [], + "is_nsfw": false + }, + { + "id": "6eren.dk", + "name": "6'eren", + "alt_names": [], + "network": null, + "owners": [ + "Warner Bros. Discovery EMEA" + ], + "country": "DK", + "categories": [], + "is_nsfw": false, + "launched": "2009-01-01", + "closed": null, + "replaced_by": null, + "website": "http://www.6-eren.dk/" + }, + { + "id": "BBCNews.uk", + "name": "BBC News", + "alt_names": [], + "network": null, + "owners": [], + "country": "UK", + "categories": [ + "news" + ], + "is_nsfw": false, + "launched": null, + "closed": null, + "replaced_by": null, + "website": "http://news.bbc.co.uk/" + }, + { + "id": "CNN.us", + "name": "CNN", + "network": null, + "country": "US", + "categories": [], + "is_nsfw": false + }, + { + "id": "Channel1.us", + "name": "Channel 1", + "alt_names": [], + "network": null, + "owners": [], + "country": "US", + "categories": [], + "is_nsfw": false, + "launched": null, + "closed": null, + "replaced_by": null, + "website": "" + }, + { + "id": "Channel2.us", + "name": "Channel 2", + "alt_names": [], + "network": null, + "owners": [], + "country": "UK", + "categories": [], + "is_nsfw": false, + "launched": null, + "closed": null, + "replaced_by": null, + "website": "" + }, + { + "id": "Channel3.us", + "name": "Channel 3", + "alt_names": [], + "network": null, + "owners": [], + "country": "UK", + "categories": [], + "is_nsfw": false, + "launched": null, + "closed": null, + "replaced_by": null, + "website": "" + }, + { + "id": "Channel4.us", + "name": "Channel 4", + "alt_names": [], + "network": null, + "owners": [], + "country": "UK", + "categories": [], + "is_nsfw": false, + "launched": null, + "closed": null, + "replaced_by": null, + "website": "" + } +] \ No newline at end of file diff --git a/tests/__data__/input/data/cities.json b/tests/__data__/input/data/cities.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/tests/__data__/input/data/cities.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/tests/__data__/input/__data__/countries.json b/tests/__data__/input/data/countries.json similarity index 100% rename from tests/__data__/input/__data__/countries.json rename to tests/__data__/input/data/countries.json diff --git a/tests/__data__/input/__data__/feeds.json b/tests/__data__/input/data/feeds.json similarity index 75% rename from tests/__data__/input/__data__/feeds.json rename to tests/__data__/input/data/feeds.json index 875f7723..cf0e5e34 100644 --- a/tests/__data__/input/__data__/feeds.json +++ b/tests/__data__/input/data/feeds.json @@ -1,58 +1,72 @@ -[ - { - "channel": "CNNInternational.us", - "id": "SD", - "name": "SD", - "is_main": true, - "broadcast_area": [ - "r/INT" - ], - "timezones": [ - "America/New_York" - ], - "languages": [], - "video_format": "480i" - }, - { - "channel": "CNNInternational.us", - "id": "Europe", - "name": "Europe", - "is_main": false, - "broadcast_area": [ - "r/EUR" - ], - "timezones": [ - "America/New_York" - ], - "languages": [], - "video_format": "480i" - }, - { - "channel": "Bravo.us", - "id": "East", - "name": "East", - "is_main": true, - "broadcast_area": [ - "r/EUR" - ], - "timezones": [ - "America/New_York" - ], - "languages": [], - "video_format": "480i" - }, - { - "channel": "Channel4.us", - "id": "HD", - "name": "HD", - "is_main": true, - "broadcast_area": [ - "r/EUR" - ], - "timezones": [ - "America/New_York" - ], - "languages": [], - "video_format": "480i" - } +[ + { + "channel": "CNNInternational.us", + "id": "SD", + "name": "SD", + "is_main": true, + "broadcast_area": [ + "r/INT" + ], + "timezones": [ + "America/New_York" + ], + "languages": [], + "video_format": "480i" + }, + { + "channel": "CNNInternational.us", + "id": "Europe", + "name": "Europe", + "is_main": false, + "broadcast_area": [ + "r/EUR" + ], + "timezones": [ + "America/New_York" + ], + "languages": [], + "video_format": "480i" + }, + { + "channel": "Bravo.us", + "id": "East", + "name": "East", + "is_main": true, + "broadcast_area": [ + "r/EUR" + ], + "timezones": [ + "America/New_York" + ], + "languages": [], + "video_format": "480i" + }, + { + "channel": "Channel1.us", + "id": "HD", + "name": "HD", + "is_main": false, + "broadcast_area": [ + "r/EUR" + ], + "timezones": [ + "America/New_York" + ], + "languages": [], + "video_format": "480i" + }, + { + "channel": "Channel4.us", + "id": "HD", + "name": "HD", + "is_main": true, + "broadcast_area": [ + "r/EUR" + ], + "timezones": [ + "America/New_York" + ], + "languages": [], + "video_format": "480i" + } ] \ No newline at end of file diff --git a/tests/__data__/input/__data__/guides.json b/tests/__data__/input/data/guides.json similarity index 94% rename from tests/__data__/input/__data__/guides.json rename to tests/__data__/input/data/guides.json index fd21fb22..e0470ab3 100644 --- a/tests/__data__/input/__data__/guides.json +++ b/tests/__data__/input/data/guides.json @@ -1,10 +1,10 @@ -[ - { - "channel": "CNNInternational.us", - "feed": "SD", - "site": "9tv.co.il", - "site_id": "#", - "site_name": "CNN (guide)", - "lang": "en" - } +[ + { + "channel": "CNNInternational.us", + "feed": "SD", + "site": "9tv.co.il", + "site_id": "#", + "site_name": "CNN (guide)", + "lang": "en" + } ] \ No newline at end of file diff --git a/tests/__data__/input/__data__/languages.json b/tests/__data__/input/data/languages.json similarity index 100% rename from tests/__data__/input/__data__/languages.json rename to tests/__data__/input/data/languages.json diff --git a/tests/__data__/input/data/logos.json b/tests/__data__/input/data/logos.json new file mode 100644 index 00000000..a868b1de --- /dev/null +++ b/tests/__data__/input/data/logos.json @@ -0,0 +1,29 @@ +[ + { + "channel": "Channel3.us", + "feed": null, + "tags": [], + "width": 334, + "height": 210, + "format": "PNG", + "url": "https://upload.wikimedia.org/wikipedia/commons/6/64/6%27eren_2015.png" + }, + { + "channel": "Channel4.us", + "feed": "HD", + "tags": [], + "width": 334, + "height": 210, + "format": "PNG", + "url": "https://i.imgur.com/BPzH88J.png" + }, + { + "channel": "Channel1.us", + "feed": null, + "tags": [], + "width": 334, + "height": 210, + "format": "PNG", + "url": "https://i.imgur.com/GPzH88J.png" + } +] \ No newline at end of file diff --git a/tests/__data__/input/__data__/regions.json b/tests/__data__/input/data/regions.json similarity index 100% rename from tests/__data__/input/__data__/regions.json rename to tests/__data__/input/data/regions.json diff --git a/tests/__data__/input/__data__/streams.json b/tests/__data__/input/data/streams.json similarity index 96% rename from tests/__data__/input/__data__/streams.json rename to tests/__data__/input/data/streams.json index ce52ae7c..9410f1b2 100644 --- a/tests/__data__/input/__data__/streams.json +++ b/tests/__data__/input/data/streams.json @@ -1,10 +1,10 @@ -[ - { - "channel": "CNNInternational.us", - "feed": "SD", - "url": "https://live.relentlessinnovations.net:1936/imantv/imantv/playlist.m3u8", - "referrer": null, - "user_agent": null, - "quality": "480p" - } +[ + { + "channel": "CNNInternational.us", + "feed": "SD", + "url": "https://live.relentlessinnovations.net:1936/imantv/imantv/playlist.m3u8", + "referrer": null, + "user_agent": null, + "quality": "480p" + } ] \ No newline at end of file diff --git a/tests/__data__/input/__data__/subdivisions.json b/tests/__data__/input/data/subdivisions.json similarity index 93% rename from tests/__data__/input/__data__/subdivisions.json rename to tests/__data__/input/data/subdivisions.json index 88a6d1bd..dea26f94 100644 --- a/tests/__data__/input/__data__/subdivisions.json +++ b/tests/__data__/input/data/subdivisions.json @@ -1,352 +1,352 @@ -[ - { - "country": "CA", - "name": "Alberta", - "code": "CA-AB" - }, - { - "country": "CA", - "name": "British Columbia", - "code": "CA-BC" - }, - { - "country": "CA", - "name": "Manitoba", - "code": "CA-MB" - }, - { - "country": "CA", - "name": "New Brunswick", - "code": "CA-NB" - }, - { - "country": "CA", - "name": "Newfoundland and Labrador", - "code": "CA-NL" - }, - { - "country": "CA", - "name": "Northwest Territories", - "code": "CA-NT" - }, - { - "country": "CA", - "name": "Nova Scotia", - "code": "CA-NS" - }, - { - "country": "CA", - "name": "Nunavut", - "code": "CA-NU" - }, - { - "country": "CA", - "name": "Ontario", - "code": "CA-ON" - }, - { - "country": "CA", - "name": "Prince Edward Island", - "code": "CA-PE" - }, - { - "country": "CA", - "name": "Quebec", - "code": "CA-QC" - }, - { - "country": "CA", - "name": "Saskatchewan", - "code": "CA-SK" - }, - { - "country": "CA", - "name": "Yukon", - "code": "CA-YT" - }, - { - "country": "US", - "name": "Alabama", - "code": "US-AL" - }, - { - "country": "US", - "name": "Alaska", - "code": "US-AK" - }, - { - "country": "US", - "name": "American Samoa", - "code": "US-AS" - }, - { - "country": "US", - "name": "Arizona", - "code": "US-AZ" - }, - { - "country": "US", - "name": "Arkansas", - "code": "US-AR" - }, - { - "country": "US", - "name": "California", - "code": "US-CA" - }, - { - "country": "US", - "name": "Colorado", - "code": "US-CO" - }, - { - "country": "US", - "name": "Connecticut", - "code": "US-CT" - }, - { - "country": "US", - "name": "Delaware", - "code": "US-DE" - }, - { - "country": "US", - "name": "District of Columbia", - "code": "US-DC" - }, - { - "country": "US", - "name": "Florida", - "code": "US-FL" - }, - { - "country": "US", - "name": "Georgia", - "code": "US-GA" - }, - { - "country": "US", - "name": "Guam", - "code": "US-GU" - }, - { - "country": "US", - "name": "Hawaii", - "code": "US-HI" - }, - { - "country": "US", - "name": "Idaho", - "code": "US-ID" - }, - { - "country": "US", - "name": "Illinois", - "code": "US-IL" - }, - { - "country": "US", - "name": "Indiana", - "code": "US-IN" - }, - { - "country": "US", - "name": "Iowa", - "code": "US-IA" - }, - { - "country": "US", - "name": "Kansas", - "code": "US-KS" - }, - { - "country": "US", - "name": "Kentucky", - "code": "US-KY" - }, - { - "country": "US", - "name": "Louisiana", - "code": "US-LA" - }, - { - "country": "US", - "name": "Maine", - "code": "US-ME" - }, - { - "country": "US", - "name": "Maryland", - "code": "US-MD" - }, - { - "country": "US", - "name": "Massachusetts", - "code": "US-MA" - }, - { - "country": "US", - "name": "Michigan", - "code": "US-MI" - }, - { - "country": "US", - "name": "Minnesota", - "code": "US-MN" - }, - { - "country": "US", - "name": "Mississippi", - "code": "US-MS" - }, - { - "country": "US", - "name": "Missouri", - "code": "US-MO" - }, - { - "country": "US", - "name": "Montana", - "code": "US-MT" - }, - { - "country": "US", - "name": "Nebraska", - "code": "US-NE" - }, - { - "country": "US", - "name": "Nevada", - "code": "US-NV" - }, - { - "country": "US", - "name": "New Hampshire", - "code": "US-NH" - }, - { - "country": "US", - "name": "New Jersey", - "code": "US-NJ" - }, - { - "country": "US", - "name": "New Mexico", - "code": "US-NM" - }, - { - "country": "US", - "name": "New York", - "code": "US-NY" - }, - { - "country": "US", - "name": "North Carolina", - "code": "US-NC" - }, - { - "country": "US", - "name": "North Dakota", - "code": "US-ND" - }, - { - "country": "US", - "name": "Northern Mariana Islands", - "code": "US-MP" - }, - { - "country": "US", - "name": "Ohio", - "code": "US-OH" - }, - { - "country": "US", - "name": "Oklahoma", - "code": "US-OK" - }, - { - "country": "US", - "name": "Oregon", - "code": "US-OR" - }, - { - "country": "US", - "name": "Pennsylvania", - "code": "US-PA" - }, - { - "country": "US", - "name": "Puerto Rico", - "code": "US-PR" - }, - { - "country": "US", - "name": "Rhode Island", - "code": "US-RI" - }, - { - "country": "US", - "name": "South Carolina", - "code": "US-SC" - }, - { - "country": "US", - "name": "South Dakota", - "code": "US-SD" - }, - { - "country": "US", - "name": "Tennessee", - "code": "US-TN" - }, - { - "country": "US", - "name": "Texas", - "code": "US-TX" - }, - { - "country": "US", - "name": "U.S. Virgin Islands", - "code": "US-VI" - }, - { - "country": "US", - "name": "United States Minor Outlying Islands", - "code": "US-UM" - }, - { - "country": "US", - "name": "Utah", - "code": "US-UT" - }, - { - "country": "US", - "name": "Vermont", - "code": "US-VT" - }, - { - "country": "US", - "name": "Virginia", - "code": "US-VA" - }, - { - "country": "US", - "name": "Washington", - "code": "US-WA" - }, - { - "country": "US", - "name": "West Virginia", - "code": "US-WV" - }, - { - "country": "US", - "name": "Wisconsin", - "code": "US-WI" - }, - { - "country": "US", - "name": "Wyoming", - "code": "US-WY" - } +[ + { + "country": "CA", + "name": "Alberta", + "code": "CA-AB" + }, + { + "country": "CA", + "name": "British Columbia", + "code": "CA-BC" + }, + { + "country": "CA", + "name": "Manitoba", + "code": "CA-MB" + }, + { + "country": "CA", + "name": "New Brunswick", + "code": "CA-NB" + }, + { + "country": "CA", + "name": "Newfoundland and Labrador", + "code": "CA-NL" + }, + { + "country": "CA", + "name": "Northwest Territories", + "code": "CA-NT" + }, + { + "country": "CA", + "name": "Nova Scotia", + "code": "CA-NS" + }, + { + "country": "CA", + "name": "Nunavut", + "code": "CA-NU" + }, + { + "country": "CA", + "name": "Ontario", + "code": "CA-ON" + }, + { + "country": "CA", + "name": "Prince Edward Island", + "code": "CA-PE" + }, + { + "country": "CA", + "name": "Quebec", + "code": "CA-QC" + }, + { + "country": "CA", + "name": "Saskatchewan", + "code": "CA-SK" + }, + { + "country": "CA", + "name": "Yukon", + "code": "CA-YT" + }, + { + "country": "US", + "name": "Alabama", + "code": "US-AL" + }, + { + "country": "US", + "name": "Alaska", + "code": "US-AK" + }, + { + "country": "US", + "name": "American Samoa", + "code": "US-AS" + }, + { + "country": "US", + "name": "Arizona", + "code": "US-AZ" + }, + { + "country": "US", + "name": "Arkansas", + "code": "US-AR" + }, + { + "country": "US", + "name": "California", + "code": "US-CA" + }, + { + "country": "US", + "name": "Colorado", + "code": "US-CO" + }, + { + "country": "US", + "name": "Connecticut", + "code": "US-CT" + }, + { + "country": "US", + "name": "Delaware", + "code": "US-DE" + }, + { + "country": "US", + "name": "District of Columbia", + "code": "US-DC" + }, + { + "country": "US", + "name": "Florida", + "code": "US-FL" + }, + { + "country": "US", + "name": "Georgia", + "code": "US-GA" + }, + { + "country": "US", + "name": "Guam", + "code": "US-GU" + }, + { + "country": "US", + "name": "Hawaii", + "code": "US-HI" + }, + { + "country": "US", + "name": "Idaho", + "code": "US-ID" + }, + { + "country": "US", + "name": "Illinois", + "code": "US-IL" + }, + { + "country": "US", + "name": "Indiana", + "code": "US-IN" + }, + { + "country": "US", + "name": "Iowa", + "code": "US-IA" + }, + { + "country": "US", + "name": "Kansas", + "code": "US-KS" + }, + { + "country": "US", + "name": "Kentucky", + "code": "US-KY" + }, + { + "country": "US", + "name": "Louisiana", + "code": "US-LA" + }, + { + "country": "US", + "name": "Maine", + "code": "US-ME" + }, + { + "country": "US", + "name": "Maryland", + "code": "US-MD" + }, + { + "country": "US", + "name": "Massachusetts", + "code": "US-MA" + }, + { + "country": "US", + "name": "Michigan", + "code": "US-MI" + }, + { + "country": "US", + "name": "Minnesota", + "code": "US-MN" + }, + { + "country": "US", + "name": "Mississippi", + "code": "US-MS" + }, + { + "country": "US", + "name": "Missouri", + "code": "US-MO" + }, + { + "country": "US", + "name": "Montana", + "code": "US-MT" + }, + { + "country": "US", + "name": "Nebraska", + "code": "US-NE" + }, + { + "country": "US", + "name": "Nevada", + "code": "US-NV" + }, + { + "country": "US", + "name": "New Hampshire", + "code": "US-NH" + }, + { + "country": "US", + "name": "New Jersey", + "code": "US-NJ" + }, + { + "country": "US", + "name": "New Mexico", + "code": "US-NM" + }, + { + "country": "US", + "name": "New York", + "code": "US-NY" + }, + { + "country": "US", + "name": "North Carolina", + "code": "US-NC" + }, + { + "country": "US", + "name": "North Dakota", + "code": "US-ND" + }, + { + "country": "US", + "name": "Northern Mariana Islands", + "code": "US-MP" + }, + { + "country": "US", + "name": "Ohio", + "code": "US-OH" + }, + { + "country": "US", + "name": "Oklahoma", + "code": "US-OK" + }, + { + "country": "US", + "name": "Oregon", + "code": "US-OR" + }, + { + "country": "US", + "name": "Pennsylvania", + "code": "US-PA" + }, + { + "country": "US", + "name": "Puerto Rico", + "code": "US-PR" + }, + { + "country": "US", + "name": "Rhode Island", + "code": "US-RI" + }, + { + "country": "US", + "name": "South Carolina", + "code": "US-SC" + }, + { + "country": "US", + "name": "South Dakota", + "code": "US-SD" + }, + { + "country": "US", + "name": "Tennessee", + "code": "US-TN" + }, + { + "country": "US", + "name": "Texas", + "code": "US-TX" + }, + { + "country": "US", + "name": "U.S. Virgin Islands", + "code": "US-VI" + }, + { + "country": "US", + "name": "United States Minor Outlying Islands", + "code": "US-UM" + }, + { + "country": "US", + "name": "Utah", + "code": "US-UT" + }, + { + "country": "US", + "name": "Vermont", + "code": "US-VT" + }, + { + "country": "US", + "name": "Virginia", + "code": "US-VA" + }, + { + "country": "US", + "name": "Washington", + "code": "US-WA" + }, + { + "country": "US", + "name": "West Virginia", + "code": "US-WV" + }, + { + "country": "US", + "name": "Wisconsin", + "code": "US-WI" + }, + { + "country": "US", + "name": "Wyoming", + "code": "US-WY" + } ] \ No newline at end of file diff --git a/tests/__data__/input/__data__/timezones.json b/tests/__data__/input/data/timezones.json similarity index 100% rename from tests/__data__/input/__data__/timezones.json rename to tests/__data__/input/data/timezones.json diff --git a/tests/__data__/input/epg_grab/custom.channels.xml b/tests/__data__/input/epg_grab/custom.channels.xml index e107685e..5643ee72 100644 --- a/tests/__data__/input/epg_grab/custom.channels.xml +++ b/tests/__data__/input/epg_grab/custom.channels.xml @@ -1,9 +1,9 @@ - - - Custom Channel 1 - Custom Channel 2 - Channel 1 - Channel 3 - Channel 4 - Channel 1 + + + Custom Channel 1 + Custom Channel 2 + Channel 1 + Channel 3 + Channel 4 + Channel 1 \ No newline at end of file diff --git a/tests/__data__/input/epg_grab/example.com/example.com.channels.xml b/tests/__data__/input/epg_grab/example.com/example.com.channels.xml deleted file mode 100644 index 6f589e0b..00000000 --- a/tests/__data__/input/epg_grab/example.com/example.com.channels.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - Channel 1 - Channel 2 - Channel 1 - Channel 3 - \ No newline at end of file diff --git a/tests/__data__/input/epg_grab/example.com/example.com.config.js b/tests/__data__/input/epg_grab/example.com/example.com.config.js deleted file mode 100644 index 107a761e..00000000 --- a/tests/__data__/input/epg_grab/example.com/example.com.config.js +++ /dev/null @@ -1,28 +0,0 @@ -module.exports = { - site: 'example.com', - days: 2, - request: { - timeout: 1000 - }, - url: 'https://example.com', - parser({ channel, date }) { - if (channel.xmltv_id === 'Channel2.us') return [] - else if (channel.xmltv_id === 'Channel1.us' && channel.lang === 'fr') { - return [ - { - title: 'Programme1 (example.com)', - start: `${date.format('YYYY-MM-DD')}T04:30:00.000Z`, - stop: `${date.format('YYYY-MM-DD')}T07:10:00.000Z` - } - ] - } - - return [ - { - title: 'Program1 (example.com)', - start: `${date.format('YYYY-MM-DD')}T04:31:00.000Z`, - stop: `${date.format('YYYY-MM-DD')}T07:10:00.000Z` - } - ] - } -} diff --git a/tests/__data__/input/epg_grab/sites/example.com/example.com.channels.xml b/tests/__data__/input/epg_grab/sites/example.com/example.com.channels.xml index cfd7a7bb..343ff70c 100644 --- a/tests/__data__/input/epg_grab/sites/example.com/example.com.channels.xml +++ b/tests/__data__/input/epg_grab/sites/example.com/example.com.channels.xml @@ -1,6 +1,7 @@ - - - Channel 2 - Channel 1 - Channel 1 + + + Channel 1 + Channel 2 + Channel 1 + Channel 3 \ No newline at end of file diff --git a/tests/__data__/input/epg_grab/sites/example.com/example.com.config.js b/tests/__data__/input/epg_grab/sites/example.com/example.com.config.js index 107a761e..01849370 100644 --- a/tests/__data__/input/epg_grab/sites/example.com/example.com.config.js +++ b/tests/__data__/input/epg_grab/sites/example.com/example.com.config.js @@ -1,28 +1,28 @@ -module.exports = { - site: 'example.com', - days: 2, - request: { - timeout: 1000 - }, - url: 'https://example.com', - parser({ channel, date }) { - if (channel.xmltv_id === 'Channel2.us') return [] - else if (channel.xmltv_id === 'Channel1.us' && channel.lang === 'fr') { - return [ - { - title: 'Programme1 (example.com)', - start: `${date.format('YYYY-MM-DD')}T04:30:00.000Z`, - stop: `${date.format('YYYY-MM-DD')}T07:10:00.000Z` - } - ] - } - - return [ - { - title: 'Program1 (example.com)', - start: `${date.format('YYYY-MM-DD')}T04:31:00.000Z`, - stop: `${date.format('YYYY-MM-DD')}T07:10:00.000Z` - } - ] - } -} +module.exports = { + site: 'example.com', + days: 2, + request: { + timeout: 1000 + }, + url: 'https://example.com', + parser({ channel, date }) { + if (channel.xmltv_id === 'Channel2.us') return [] + else if (channel.lang === 'fr') { + return [ + { + title: 'Programme1 (example.com)', + start: `${date.format('YYYY-MM-DD')}T04:30:00.000Z`, + stop: `${date.format('YYYY-MM-DD')}T07:10:00.000Z` + } + ] + } + + return [ + { + title: 'Program1 (example.com)', + start: `${date.format('YYYY-MM-DD')}T04:31:00.000Z`, + stop: `${date.format('YYYY-MM-DD')}T07:10:00.000Z` + } + ] + } +} diff --git a/tests/__data__/input/sites_update/issues.mjs b/tests/__data__/input/sites_update/issues.mjs index 96048350..a4187b8e 100644 --- a/tests/__data__/input/sites_update/issues.mjs +++ b/tests/__data__/input/sites_update/issues.mjs @@ -1,2523 +1,2603 @@ -export default [ - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2547', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2547/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2547/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2547/events', - html_url: 'https://github.com/iptv-org/epg/issues/2547', - id: 2760418979, - node_id: 'I_kwDOFLVvtM6kiKaj', - number: 2547, - title: 'digiturk.com.tr', - user: { - login: 'freearhey', - id: 7253922, - node_id: 'MDQ6VXNlcjcyNTM5MjI=', - avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/freearhey', - html_url: 'https://github.com/freearhey', - followers_url: 'https://api.github.com/users/freearhey/followers', - following_url: 'https://api.github.com/users/freearhey/following{/other_user}', - gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', - starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', - organizations_url: 'https://api.github.com/users/freearhey/orgs', - repos_url: 'https://api.github.com/users/freearhey/repos', - events_url: 'https://api.github.com/users/freearhey/events{/privacy}', - received_events_url: 'https://api.github.com/users/freearhey/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710795, - node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', - name: 'status:down', - color: 'df3a4a', - default: false, - description: "The guide doesn't work" - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 1, - created_at: '2024-12-27T05:33:47Z', - updated_at: '2024-12-28T20:37:53Z', - closed_at: null, - author_association: 'COLLABORATOR', - active_lock_reason: null, - body: '### Site\n\ndigiturk.com.tr\n\n### Description\n\n```sh\r\nnpm run grab --- --site=digiturk.com.tr\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=digiturk.com.tr\r\n\r\nstarting...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: digiturk.com.tr\r\nloading channels...\r\n found 139 channel(s)\r\nrun #1:\r\n [1/278] digiturk.com.tr (en) - AlJazeeraEnglish.qa - Dec 27, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [2/278] digiturk.com.tr (en) - AlJazeeraEnglish.qa - Dec 28, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [3/278] digiturk.com.tr (tr) - Yaban.tr - Dec 28, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n```', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2547/reactions', - total_count: 1, - '+1': 1, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2547/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2542', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2542/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2542/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2542/events', - html_url: 'https://github.com/iptv-org/epg/issues/2542', - id: 2759639980, - node_id: 'I_kwDOFLVvtM6kfMOs', - number: 2542, - title: 'tvguide.com not working', - user: { - login: 'SiWafer', - id: 10903014, - node_id: 'MDQ6VXNlcjEwOTAzMDE0', - avatar_url: 'https://avatars.githubusercontent.com/u/10903014?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/SiWafer', - html_url: 'https://github.com/SiWafer', - followers_url: 'https://api.github.com/users/SiWafer/followers', - following_url: 'https://api.github.com/users/SiWafer/following{/other_user}', - gists_url: 'https://api.github.com/users/SiWafer/gists{/gist_id}', - starred_url: 'https://api.github.com/users/SiWafer/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/SiWafer/subscriptions', - organizations_url: 'https://api.github.com/users/SiWafer/orgs', - repos_url: 'https://api.github.com/users/SiWafer/repos', - events_url: 'https://api.github.com/users/SiWafer/events{/privacy}', - received_events_url: 'https://api.github.com/users/SiWafer/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710795, - node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', - name: 'status:down', - color: 'df3a4a', - default: false, - description: "The guide doesn't work" - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 0, - created_at: '2024-12-26T11:11:24Z', - updated_at: '2024-12-27T05:28:55Z', - closed_at: null, - author_association: 'NONE', - active_lock_reason: null, - body: '### Site\n\ntvguide.com\n\n### Description\n\n```\r\nepg@rpi:~/epg$ npm run grab --- --site=tvguide.com\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=tvguide.com\r\n\r\nstarting...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: tvguide.com\r\nloading channels...\r\n found 39 channel(s)\r\nrun #1:\r\n [1/78] tvguide.com (en) - AEEast.us - Dec 26, 2024 (0 programs)\r\n ERR: Request failed with status code 403\r\n [2/78] tvguide.com (en) - AEEast.us - Dec 27, 2024 (0 programs)\r\n ERR: Request failed with status code 403\r\n [3/78] tvguide.com (en) - AMCEast.us - Dec 27, 2024 (0 programs)\r\n ERR: Request failed with status code 403\r\n [4/78] tvguide.com (en) - AMCEast.us - Dec 26, 2024 (0 programs)\r\n ERR: Request failed with status code 403\r\n [5/78] tvguide.com (en) - BravoEast.us - Dec 27, 2024 (0 programs)\r\n ERR: Request failed with status code 403\r\n [6/78] tvguide.com (en) - BravoEast.us - Dec 26, 2024 (0 programs)\r\n ERR: Request failed with status code 403\r\n [7/78] tvguide.com (en) - BBCAmericaEast.us - Dec 27, 2024 (0 programs)\r\n ERR: Request failed with status code 403\r\n [8/78] tvguide.com (en) - CNBC.us - Dec 27, 2024 (0 programs)\r\n ERR: Request failed with status code 403\r\n [9/78] tvguide.com (en) - CNBC.us - Dec 26, 2024 (0 programs)\r\n...\r\n...\r\n...\r\n```', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2542/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2542/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2541', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2541/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2541/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2541/events', - html_url: 'https://github.com/iptv-org/epg/issues/2541', - id: 2758749907, - node_id: 'I_kwDOFLVvtM6kby7T', - number: 2541, - title: 'elcinema.com grab error 403', - user: { - login: 'xercessss', - id: 189765257, - node_id: 'U_kgDOC0-WiQ', - avatar_url: 'https://avatars.githubusercontent.com/u/189765257?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/xercessss', - html_url: 'https://github.com/xercessss', - followers_url: 'https://api.github.com/users/xercessss/followers', - following_url: 'https://api.github.com/users/xercessss/following{/other_user}', - gists_url: 'https://api.github.com/users/xercessss/gists{/gist_id}', - starred_url: 'https://api.github.com/users/xercessss/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/xercessss/subscriptions', - organizations_url: 'https://api.github.com/users/xercessss/orgs', - repos_url: 'https://api.github.com/users/xercessss/repos', - events_url: 'https://api.github.com/users/xercessss/events{/privacy}', - received_events_url: 'https://api.github.com/users/xercessss/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710795, - node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', - name: 'status:down', - color: 'df3a4a', - default: false, - description: "The guide doesn't work" - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 0, - created_at: '2024-12-25T11:34:16Z', - updated_at: '2024-12-27T05:28:07Z', - closed_at: null, - author_association: 'NONE', - active_lock_reason: null, - body: '### Site\n\nelcinema.com\n\n### Description\n\ngrabbing elcinema.com epg return error 403 for both ar and en language:\r\nERR: Request failed with status code 403\r\n', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2541/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2541/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2509', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2509/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2509/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2509/events', - html_url: 'https://github.com/iptv-org/epg/issues/2509', - id: 2740938968, - node_id: 'I_kwDOFLVvtM6jX2jY', - number: 2509, - title: 'ssl problem maxtv.hrvatskitelekom.hr', - user: { - login: 'uploadkom', - id: 130712472, - node_id: 'U_kgDOB8qDmA', - avatar_url: 'https://avatars.githubusercontent.com/u/130712472?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/uploadkom', - html_url: 'https://github.com/uploadkom', - followers_url: 'https://api.github.com/users/uploadkom/followers', - following_url: 'https://api.github.com/users/uploadkom/following{/other_user}', - gists_url: 'https://api.github.com/users/uploadkom/gists{/gist_id}', - starred_url: 'https://api.github.com/users/uploadkom/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/uploadkom/subscriptions', - organizations_url: 'https://api.github.com/users/uploadkom/orgs', - repos_url: 'https://api.github.com/users/uploadkom/repos', - events_url: 'https://api.github.com/users/uploadkom/events{/privacy}', - received_events_url: 'https://api.github.com/users/uploadkom/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710795, - node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', - name: 'status:down', - color: 'df3a4a', - default: false, - description: "The guide doesn't work" - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 3, - created_at: '2024-12-15T22:21:22Z', - updated_at: '2024-12-27T05:26:26Z', - closed_at: null, - author_association: 'NONE', - active_lock_reason: null, - body: '### Site\n\nmaxtv.hrvatskitelekom.hr\n\n### Description\n\nssl problem', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2509/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2509/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2498', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2498/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2498/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2498/events', - html_url: 'https://github.com/iptv-org/epg/issues/2498', - id: 2728188494, - node_id: 'I_kwDOFLVvtM6inNpO', - number: 2498, - title: 'movistarplus.es', - user: { - login: 'freearhey', - id: 7253922, - node_id: 'MDQ6VXNlcjcyNTM5MjI=', - avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/freearhey', - html_url: 'https://github.com/freearhey', - followers_url: 'https://api.github.com/users/freearhey/followers', - following_url: 'https://api.github.com/users/freearhey/following{/other_user}', - gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', - starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', - organizations_url: 'https://api.github.com/users/freearhey/orgs', - repos_url: 'https://api.github.com/users/freearhey/repos', - events_url: 'https://api.github.com/users/freearhey/events{/privacy}', - received_events_url: 'https://api.github.com/users/freearhey/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710795, - node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', - name: 'status:down', - color: 'df3a4a', - default: false, - description: "The guide doesn't work" - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 0, - created_at: '2024-12-09T20:26:07Z', - updated_at: '2024-12-27T05:23:48Z', - closed_at: null, - author_association: 'COLLABORATOR', - active_lock_reason: null, - body: '### Site\n\nmovistarplus.es\n\n### Description\n\n```sh\r\nnpm run grab -- --site=movistarplus.es\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=movistarplus.es\r\n\r\n(node:22362) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`:\r\n--import \'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("file%3A///Users/Arhey/Code/iptv-org/epg/node_modules/tsx/dist/loader.mjs", pathToFileURL("./"));\'\r\n(Use `node --trace-warnings ...` to show where the warning was created)\r\n(node:22362) UnsupportedWarning: `globalPreload` has been removed; use `initialize` instead.\r\n(Use `node --trace-warnings ...` to show where the warning was created)\r\nstarting...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: movistarplus.es\r\nloading channels...\r\n(node:22362) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.\r\n found 186 channel(s)\r\nrun #1:\r\n [1/372] movistarplus.es (es) - ANTV - Dec 9, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [2/372] movistarplus.es (es) - ANTV - Dec 10, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [3/372] movistarplus.es (es) - ARAGTV - Dec 10, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [4/372] movistarplus.es (es) - CANSUR - Dec 10, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [5/372] movistarplus.es (es) - DAZNL2 - Dec 10, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [6/372] movistarplus.es (es) - MLIGS - Dec 10, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [7/372] movistarplus.es (es) - AMC.es - Dec 10, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [8/372] movistarplus.es (es) - ComediaporMovistarPlusPlus.es - Dec 10, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [9/372] movistarplus.es (es) - LasEstrellasEuropa.mx - Dec 10, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [10/372] movistarplus.es (es) - LasEstrellasEuropa.mx - Dec 9, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [11/372] movistarplus.es (es) - ComediaporMovistarPlusPlus.es - Dec 9, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [12/372] movistarplus.es (es) - LaResistencia.es - Dec 10, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [13/372] movistarplus.es (es) - LaResistencia.es - Dec 9, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n```', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2498/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2498/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2445', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2445/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2445/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2445/events', - html_url: 'https://github.com/iptv-org/epg/issues/2445', - id: 2567652015, - node_id: 'I_kwDOFLVvtM6ZC0Kv', - number: 2445, - title: 'EPG from dishtv.in stopped with error code 404', - user: { - login: 'dheer99', - id: 20639130, - node_id: 'MDQ6VXNlcjIwNjM5MTMw', - avatar_url: 'https://avatars.githubusercontent.com/u/20639130?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/dheer99', - html_url: 'https://github.com/dheer99', - followers_url: 'https://api.github.com/users/dheer99/followers', - following_url: 'https://api.github.com/users/dheer99/following{/other_user}', - gists_url: 'https://api.github.com/users/dheer99/gists{/gist_id}', - starred_url: 'https://api.github.com/users/dheer99/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/dheer99/subscriptions', - organizations_url: 'https://api.github.com/users/dheer99/orgs', - repos_url: 'https://api.github.com/users/dheer99/repos', - events_url: 'https://api.github.com/users/dheer99/events{/privacy}', - received_events_url: 'https://api.github.com/users/dheer99/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710795, - node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', - name: 'status:down', - color: 'df3a4a', - default: false, - description: "The guide doesn't work" - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 2, - created_at: '2024-10-05T04:30:52Z', - updated_at: '2024-12-27T05:20:11Z', - closed_at: null, - author_association: 'NONE', - active_lock_reason: null, - body: '### Site\n\ndishtv.in\n\n### Description\n\nEPG from dishtv.in stopped with error code 404\r\n\r\n [91/98] dishtv.in (en) - StarPlus.in - Oct 5, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [92/98] dishtv.in (en) - SET.in - Oct 6, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [93/98] dishtv.in (en) - ZeeCinema.in - Oct 6, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [94/98] dishtv.in (en) - SonySportsTen5.in - Oct 5, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [95/98] dishtv.in (en) - StarSports1.in - Oct 6, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [96/98] dishtv.in (en) - SET.in - Oct 5, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [97/98] dishtv.in (en) - SonySportsTen3.in - Oct 6, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [98/98] dishtv.in (en) - Colors.in - Oct 6, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n done in 00h 00m 03s\r\n', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2445/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2445/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2339', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2339/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2339/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2339/events', - html_url: 'https://github.com/iptv-org/epg/issues/2339', - id: 2172864415, - node_id: 'I_kwDOFLVvtM6Bg0ef', - number: 2339, - title: - 'directv.com.ar and directv.com.uy not working ERR: Unexpected token \'<\', " grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=kan.org.il --output=kan.org.il.xml\r\n\r\nstarting...\r\nconfig:\r\n output: kan.org.il.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: kan.org.il\r\nloading channels...\r\n found 3 channel(s)\r\nrun #1:\r\n [1/6] kan.org.il (ar) - Makan33.il - Dec 2, 2023 (0 programs)\r\n ERR: Request failed with status code 404\r\n [2/6] kan.org.il (ar) - Makan33.il - Dec 3, 2023 (0 programs)\r\n ERR: Request failed with status code 404\r\n [3/6] kan.org.il (he) - KanEducational.il - Dec 3, 2023 (0 programs)\r\n ERR: Request failed with status code 404\r\n [4/6] kan.org.il (he) - KanEducational.il - Dec 2, 2023 (0 programs)\r\n ERR: Request failed with status code 404\r\n [5/6] kan.org.il (he) - Kan11.il - Dec 3, 2023 (0 programs)\r\n ERR: Request failed with status code 404\r\n [6/6] kan.org.il (he) - Kan11.il - Dec 2, 2023 (0 programs)\r\n ERR: Request failed with status code 404\r\n done in 00h 00m 01s\r\n```', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2273/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2273/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2270', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2270/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2270/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2270/events', - html_url: 'https://github.com/iptv-org/epg/issues/2270', - id: 2022018570, - node_id: 'I_kwDOFLVvtM54hY4K', - number: 2270, - title: 'vivacom.bg is broken', - user: { - login: 'freearhey', - id: 7253922, - node_id: 'MDQ6VXNlcjcyNTM5MjI=', - avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/freearhey', - html_url: 'https://github.com/freearhey', - followers_url: 'https://api.github.com/users/freearhey/followers', - following_url: 'https://api.github.com/users/freearhey/following{/other_user}', - gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', - starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', - organizations_url: 'https://api.github.com/users/freearhey/orgs', - repos_url: 'https://api.github.com/users/freearhey/repos', - events_url: 'https://api.github.com/users/freearhey/events{/privacy}', - received_events_url: 'https://api.github.com/users/freearhey/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710795, - node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', - name: 'status:down', - color: 'df3a4a', - default: false, - description: "The guide doesn't work" - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 0, - created_at: '2023-12-02T12:16:22Z', - updated_at: '2024-12-27T05:00:36Z', - closed_at: null, - author_association: 'COLLABORATOR', - active_lock_reason: null, - body: '### Site\n\nvivacom.bg\n\n### Description\n\n```sh\r\nnpm run grab -- --site=vivacom.bg\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=vivacom.bg\r\n\r\nstarting...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: vivacom.bg\r\nloading channels...\r\n found 154 channel(s)\r\nrun #1:\r\n [1/308] vivacom.bg (bg) - 24Kitchen.bg - Dec 2, 2023 (0 programs)\r\n [2/308] vivacom.bg (bg) - 24Kitchen.bg - Dec 3, 2023 (0 programs)\r\n [3/308] vivacom.bg (bg) - 78TV.bg - Dec 3, 2023 (0 programs)\r\n [4/308] vivacom.bg (bg) - AlfaTV.bg - Dec 3, 2023 (0 programs)\r\n [5/308] vivacom.bg (bg) - AXNEurope.gr - Dec 3, 2023 (0 programs)\r\n [6/308] vivacom.bg (bg) - BNT3.bg - Dec 3, 2023 (0 programs)\r\n```', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2270/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2270/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2264', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2264/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2264/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2264/events', - html_url: 'https://github.com/iptv-org/epg/issues/2264', - id: 2019407629, - node_id: 'I_kwDOFLVvtM54XbcN', - number: 2264, - title: 'tva.tv is broken', - user: { - login: 'freearhey', - id: 7253922, - node_id: 'MDQ6VXNlcjcyNTM5MjI=', - avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/freearhey', - html_url: 'https://github.com/freearhey', - followers_url: 'https://api.github.com/users/freearhey/followers', - following_url: 'https://api.github.com/users/freearhey/following{/other_user}', - gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', - starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', - organizations_url: 'https://api.github.com/users/freearhey/orgs', - repos_url: 'https://api.github.com/users/freearhey/repos', - events_url: 'https://api.github.com/users/freearhey/events{/privacy}', - received_events_url: 'https://api.github.com/users/freearhey/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710795, - node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', - name: 'status:down', - color: 'df3a4a', - default: false, - description: "The guide doesn't work" - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 0, - created_at: '2023-11-30T20:00:18Z', - updated_at: '2024-12-27T05:00:24Z', - closed_at: null, - author_association: 'COLLABORATOR', - active_lock_reason: null, - body: '### Site\n\ntva.tv\n\n### Description\n\n```sh\r\nnpm run grab -- --site=tva.tv\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=tva.tv\r\n\r\nstaring...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: tva.tv\r\nloading channels...\r\n found 28 channel(s)\r\nrun #1:\r\n [1/56] tva.tv (fa) - DocTV.ir - Nov 30, 2023 (0 programs)\r\n ERR: Connection timeout\r\n [2/56] tva.tv (fa) - DocTV.ir - Dec 1, 2023 (0 programs)\r\n ERR: Connection timeout\r\n```', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2264/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2264/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2263', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2263/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2263/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2263/events', - html_url: 'https://github.com/iptv-org/epg/issues/2263', - id: 2019376043, - node_id: 'I_kwDOFLVvtM54XTur', - number: 2263, - title: 'tv.yettel.hu is broken', - user: { - login: 'freearhey', - id: 7253922, - node_id: 'MDQ6VXNlcjcyNTM5MjI=', - avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/freearhey', - html_url: 'https://github.com/freearhey', - followers_url: 'https://api.github.com/users/freearhey/followers', - following_url: 'https://api.github.com/users/freearhey/following{/other_user}', - gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', - starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', - organizations_url: 'https://api.github.com/users/freearhey/orgs', - repos_url: 'https://api.github.com/users/freearhey/repos', - events_url: 'https://api.github.com/users/freearhey/events{/privacy}', - received_events_url: 'https://api.github.com/users/freearhey/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710795, - node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', - name: 'status:down', - color: 'df3a4a', - default: false, - description: "The guide doesn't work" - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 0, - created_at: '2023-11-30T19:38:28Z', - updated_at: '2024-12-27T04:59:14Z', - closed_at: null, - author_association: 'COLLABORATOR', - active_lock_reason: null, - body: '### Site\n\ntv.yettel.hu\n\n### Description\n\n```sh\r\nnpm run grab -- --site=tv.yettel.hu\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=tv.yettel.hu\r\n\r\nstaring...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: tv.yettel.hu\r\nloading channels...\r\n found 74 channel(s)\r\nrun #1:\r\n [1/148] tv.yettel.hu (en) - BBCNewsEurope.uk - Nov 30, 2023 (0 programs)\r\n ERR: Client network socket disconnected before secure TLS connection was established\r\n [2/148] tv.yettel.hu (en) - BBCNewsEurope.uk - Dec 1, 2023 (0 programs)\r\n ERR: Client network socket disconnected before secure TLS connection was established\r\n```', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2263/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2263/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2257', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2257/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2257/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2257/events', - html_url: 'https://github.com/iptv-org/epg/issues/2257', - id: 2011054011, - node_id: 'I_kwDOFLVvtM533j-7', - number: 2257, - title: 'rtb.gov.bn is broken', - user: { - login: 'freearhey', - id: 7253922, - node_id: 'MDQ6VXNlcjcyNTM5MjI=', - avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/freearhey', - html_url: 'https://github.com/freearhey', - followers_url: 'https://api.github.com/users/freearhey/followers', - following_url: 'https://api.github.com/users/freearhey/following{/other_user}', - gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', - starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', - organizations_url: 'https://api.github.com/users/freearhey/orgs', - repos_url: 'https://api.github.com/users/freearhey/repos', - events_url: 'https://api.github.com/users/freearhey/events{/privacy}', - received_events_url: 'https://api.github.com/users/freearhey/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710795, - node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', - name: 'status:down', - color: 'df3a4a', - default: false, - description: "The guide doesn't work" - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 0, - created_at: '2023-11-26T14:02:13Z', - updated_at: '2024-12-27T04:59:29Z', - closed_at: null, - author_association: 'COLLABORATOR', - active_lock_reason: null, - body: '### Site\n\nrtb.gov.bn\n\n### Description\n\n```sh\r\nnpm run grab -- --site=rtb.gov.bn\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=rtb.gov.bn\r\n\r\nstaring...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: rtb.gov.bn\r\nloading channels...\r\n found 3 channel(s)\r\nrun #1:\r\n [1/6] rtb.gov.bn (ms) - RTBAneka.bn - Nov 26, 2023 (0 programs)\r\n ERR: read ECONNRESET\r\n [2/6] rtb.gov.bn (ms) - RTBAneka.bn - Nov 27, 2023 (0 programs)\r\n ERR: read ECONNRESET\r\n [3/6] rtb.gov.bn (ms) - RTBPerdana.bn - Nov 27, 2023 (0 programs)\r\n ERR: read ECONNRESET\r\n```\r\n\r\nThere is no link to the guide on the website at the moment either: https://www.rtb.gov.bn/SitePages/Programme%20Guide.aspx', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2257/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2257/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2255', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2255/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2255/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2255/events', - html_url: 'https://github.com/iptv-org/epg/issues/2255', - id: 2011043202, - node_id: 'I_kwDOFLVvtM533hWC', - number: 2255, - title: 'rev.bs is broken', - user: { - login: 'freearhey', - id: 7253922, - node_id: 'MDQ6VXNlcjcyNTM5MjI=', - avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/freearhey', - html_url: 'https://github.com/freearhey', - followers_url: 'https://api.github.com/users/freearhey/followers', - following_url: 'https://api.github.com/users/freearhey/following{/other_user}', - gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', - starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', - organizations_url: 'https://api.github.com/users/freearhey/orgs', - repos_url: 'https://api.github.com/users/freearhey/repos', - events_url: 'https://api.github.com/users/freearhey/events{/privacy}', - received_events_url: 'https://api.github.com/users/freearhey/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710795, - node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', - name: 'status:down', - color: 'df3a4a', - default: false, - description: "The guide doesn't work" - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 1, - created_at: '2023-11-26T13:31:22Z', - updated_at: '2024-12-27T22:02:28Z', - closed_at: null, - author_association: 'COLLABORATOR', - active_lock_reason: null, - body: '### Site\n\nrev.bs\n\n### Description\n\n```sh\r\nnpm run grab -- --site=rev.bs\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=rev.bs\r\n\r\nstaring...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: rev.bs\r\nloading channels...\r\n found 179 channel(s)\r\nrun #1:\r\n [1/358] rev.bs (en) - 3ABNEnglish.us - Nov 26, 2023 (0 programs)\r\n [2/358] rev.bs (en) - 3ABNEnglish.us - Nov 27, 2023 (0 programs)\r\n [3/358] rev.bs (en) - AEEast.us - Nov 27, 2023 (0 programs)\r\n [4/358] rev.bs (en) - AMCEast.us - Nov 27, 2023 (0 programs)\r\n```\r\n---\r\n\r\n```json\r\n{\r\n\t"status": "OK",\r\n\t"data": {\r\n\t\t"schedule": [],\r\n\t\t"dateTime": {\r\n\t\t\t"date": "2023-11-27",\r\n\t\t\t"page": 0\r\n\t\t},\r\n\t\t"runtimes": [\r\n\t\t\t{\r\n\t\t\t\t"Rev class loaded": 1700899490.604637\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\t"TVGuide Class Loaded": 1700899490.638891\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\t"Validated form": 1700899490.640116\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\t"Pulled schedules": 1700899490.642099\r\n\t\t\t}\r\n\t\t]\r\n\t}\r\n}\r\n```\r\n\r\nhttps://www.rev.bs/wp-content/uploads/tv-guide/2023-11-27_0.json\r\n\r\n---\r\n\r\nimage\r\n\r\nhttps://www.rev.bs/tv-guide-404/', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2255/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2255/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2241', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2241/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2241/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2241/events', - html_url: 'https://github.com/iptv-org/epg/issues/2241', - id: 2002096969, - node_id: 'I_kwDOFLVvtM53VZNJ', - number: 2241, - title: 'm.tv.sms.cz is broken', - user: { - login: 'freearhey', - id: 7253922, - node_id: 'MDQ6VXNlcjcyNTM5MjI=', - avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/freearhey', - html_url: 'https://github.com/freearhey', - followers_url: 'https://api.github.com/users/freearhey/followers', - following_url: 'https://api.github.com/users/freearhey/following{/other_user}', - gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', - starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', - organizations_url: 'https://api.github.com/users/freearhey/orgs', - repos_url: 'https://api.github.com/users/freearhey/repos', - events_url: 'https://api.github.com/users/freearhey/events{/privacy}', - received_events_url: 'https://api.github.com/users/freearhey/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710795, - node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', - name: 'status:down', - color: 'df3a4a', - default: false, - description: "The guide doesn't work" - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 4, - created_at: '2023-11-20T12:28:47Z', - updated_at: '2024-12-27T04:58:41Z', - closed_at: null, - author_association: 'COLLABORATOR', - active_lock_reason: null, - body: '### Site\n\nm.tv.sms.cz\n\n### Description\n\n```sh\r\nnpm run grab -- --site=m.tv.sms.cz\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=m.tv.sms.cz\r\n\r\nstaring...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: m.tv.sms.cz\r\nloading channels...\r\n found 525 channel(s)\r\nrun #1:\r\n [1/1050] m.tv.sms.cz (bs) - BNTV.ba - Nov 20, 2023 (0 programs)\r\n ERR: write EPROTO C04D101B01000000:error:0A000172:SSL routines:tls12_check_peer_sigalg:wrong signature type:ssl/t1_lib.c:1594:\r\n\r\n [2/1050] m.tv.sms.cz (bs) - BNTV.ba - Nov 21, 2023 (0 programs)\r\n ERR: write EPROTO C04D101B01000000:error:0A000172:SSL routines:tls12_check_peer_sigalg:wrong signature type:ssl/t1_lib.c:1594:\r\n\r\n [3/1050] m.tv.sms.cz (cs) - AMC.cz - Nov 21, 2023 (0 programs)\r\n ERR: write EPROTO C04D101B01000000:error:0A000172:SSL routines:tls12_check_peer_sigalg:wrong signature type:ssl/t1_lib.c:1594:\r\n```\r\n\r\nhttps://check-host.net/check-report/135d7e05kb9d', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2241/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2241/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2240', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2240/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2240/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2240/events', - html_url: 'https://github.com/iptv-org/epg/issues/2240', - id: 2001847449, - node_id: 'I_kwDOFLVvtM53UcSZ', - number: 2240, - title: 'kplus.vn is broken', - user: { - login: 'freearhey', - id: 7253922, - node_id: 'MDQ6VXNlcjcyNTM5MjI=', - avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/freearhey', - html_url: 'https://github.com/freearhey', - followers_url: 'https://api.github.com/users/freearhey/followers', - following_url: 'https://api.github.com/users/freearhey/following{/other_user}', - gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', - starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', - organizations_url: 'https://api.github.com/users/freearhey/orgs', - repos_url: 'https://api.github.com/users/freearhey/repos', - events_url: 'https://api.github.com/users/freearhey/events{/privacy}', - received_events_url: 'https://api.github.com/users/freearhey/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710795, - node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', - name: 'status:down', - color: 'df3a4a', - default: false, - description: "The guide doesn't work" - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 0, - created_at: '2023-11-20T10:19:23Z', - updated_at: '2024-12-27T04:57:49Z', - closed_at: null, - author_association: 'COLLABORATOR', - active_lock_reason: null, - body: '### Site\n\nkplus.vn\n\n### Description\n\n```sh\r\nnpm run grab -- --site=kplus.vn\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=kplus.vn\r\n\r\nstaring...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: kplus.vn\r\nloading channels...\r\n found 26 channel(s)\r\nrun #1:\r\n [1/52] kplus.vn (vi) - AsianFoodNetwork.sg - Nov 20, 2023 (0 programs)\r\n [2/52] kplus.vn (vi) - AsianFoodNetwork.sg - Nov 21, 2023 (0 programs)\r\n [3/52] kplus.vn (vi) - AXN.vn - Nov 21, 2023 (0 programs)\r\n [4/52] kplus.vn (vi) - DiscoveryChannelSoutheastAsia.sg - Nov 21, 2023 (0 programs)\r\n```\r\n\r\nThe site also currently does not display the guide:\r\nimage\r\nhttps://www.kplus.vn/highlights/broadcast-schedule', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2240/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2240/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2239', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2239/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2239/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2239/events', - html_url: 'https://github.com/iptv-org/epg/issues/2239', - id: 2000457187, - node_id: 'I_kwDOFLVvtM53PI3j', - number: 2239, - title: 'comteco.com.bo is broken', - user: { - login: 'freearhey', - id: 7253922, - node_id: 'MDQ6VXNlcjcyNTM5MjI=', - avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/freearhey', - html_url: 'https://github.com/freearhey', - followers_url: 'https://api.github.com/users/freearhey/followers', - following_url: 'https://api.github.com/users/freearhey/following{/other_user}', - gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', - starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', - organizations_url: 'https://api.github.com/users/freearhey/orgs', - repos_url: 'https://api.github.com/users/freearhey/repos', - events_url: 'https://api.github.com/users/freearhey/events{/privacy}', - received_events_url: 'https://api.github.com/users/freearhey/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710795, - node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', - name: 'status:down', - color: 'df3a4a', - default: false, - description: "The guide doesn't work" - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 3, - created_at: '2023-11-18T15:03:48Z', - updated_at: '2024-12-27T21:25:41Z', - closed_at: null, - author_association: 'COLLABORATOR', - active_lock_reason: null, - body: '### Site\n\ncomteco.com.bo\n\n### Description\n\n```sh\r\nnpm run grab -- --site=comteco.com.bo\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=comteco.com.bo\r\n\r\nstaring...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: comteco.com.bo\r\nloading channels...\r\n found 72 channel(s)\r\nrun #1:\r\n [1/144] comteco.com.bo (es) - AbyaYalaTV.bo - Nov 18, 2023 (0 programs)\r\n [2/144] comteco.com.bo (es) - AbyaYalaTV.bo - Nov 19, 2023 (0 programs)\r\n [3/144] comteco.com.bo (es) - AEPanregional.us - Nov 19, 2023 (0 programs)\r\n [4/144] comteco.com.bo (es) - AnimalPlanetLatinAmerica.us - Nov 19, 2023 (0 programs)\r\n [5/144] comteco.com.bo (es) - BoliviaTV72.bo - Nov 19, 2023 (0 programs)\r\n```\r\n\r\nhttps://comteco.com.bo/pages/canales-y-programacion-tv/paquete-oro/ABYA%20YALA\r\nimage', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2239/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2239/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2237', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2237/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2237/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2237/events', - html_url: 'https://github.com/iptv-org/epg/issues/2237', - id: 2000349577, - node_id: 'I_kwDOFLVvtM53OumJ', - number: 2237, - title: 'canalplus-haiti.com is broken', - user: { - login: 'freearhey', - id: 7253922, - node_id: 'MDQ6VXNlcjcyNTM5MjI=', - avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/freearhey', - html_url: 'https://github.com/freearhey', - followers_url: 'https://api.github.com/users/freearhey/followers', - following_url: 'https://api.github.com/users/freearhey/following{/other_user}', - gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', - starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', - organizations_url: 'https://api.github.com/users/freearhey/orgs', - repos_url: 'https://api.github.com/users/freearhey/repos', - events_url: 'https://api.github.com/users/freearhey/events{/privacy}', - received_events_url: 'https://api.github.com/users/freearhey/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710795, - node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', - name: 'status:down', - color: 'df3a4a', - default: false, - description: "The guide doesn't work" - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 0, - created_at: '2023-11-18T09:46:29Z', - updated_at: '2024-12-27T04:57:02Z', - closed_at: null, - author_association: 'COLLABORATOR', - active_lock_reason: null, - body: '### Site\n\ncanalplus-haiti.com\n\n### Description\n\n```sh\r\nnpm run grab -- --site=canalplus-haiti.com\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=canalplus-haiti.com\r\n\r\nstaring...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: canalplus-haiti.com\r\nloading channels...\r\n found 111 channel(s)\r\nrun #1:\r\n [1/222] canalplus-haiti.com (fr) - 13emeRue.fr - Nov 18, 2023 (0 programs)\r\n ERR: Request failed with status code 403\r\n [2/222] canalplus-haiti.com (fr) - 13emeRue.fr - Nov 19, 2023 (0 programs)\r\n ERR: Request failed with status code 403\r\n [3/222] canalplus-haiti.com (fr) - ZoukTV.mq - Nov 19, 2023 (0 programs)\r\n ERR: Request failed with status code 403\r\n [4/222] canalplus-haiti.com (fr) - ZoukTV.mq - Nov 18, 2023 (0 programs)\r\n ERR: Request failed with status code 403\r\n```\r\n\r\nhttps://check-host.net/check-report/134fc56bk333', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2237/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2237/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2173', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2173/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2173/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2173/events', - html_url: 'https://github.com/iptv-org/epg/issues/2173', - id: 1903824500, - node_id: 'I_kwDOFLVvtM5xeg50', - number: 2173, - title: 'hd-plus.de - 0 programs on all channels', - user: { - login: 'x011', - id: 4313821, - node_id: 'MDQ6VXNlcjQzMTM4MjE=', - avatar_url: 'https://avatars.githubusercontent.com/u/4313821?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/x011', - html_url: 'https://github.com/x011', - followers_url: 'https://api.github.com/users/x011/followers', - following_url: 'https://api.github.com/users/x011/following{/other_user}', - gists_url: 'https://api.github.com/users/x011/gists{/gist_id}', - starred_url: 'https://api.github.com/users/x011/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/x011/subscriptions', - organizations_url: 'https://api.github.com/users/x011/orgs', - repos_url: 'https://api.github.com/users/x011/repos', - events_url: 'https://api.github.com/users/x011/events{/privacy}', - received_events_url: 'https://api.github.com/users/x011/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710795, - node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', - name: 'status:down', - color: 'df3a4a', - default: false, - description: "The guide doesn't work" - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 1, - created_at: '2023-09-19T22:45:56Z', - updated_at: '2024-12-27T04:54:11Z', - closed_at: null, - author_association: 'NONE', - active_lock_reason: null, - body: '### Site\r\n\r\nhd-plus.de\r\n\r\n### Description\r\n\r\nAll channels on hd-plus.de have 0 programs:\r\n```\r\n[1/60] hd-plus.de - 123tv.de - Sep 19, 2023 (0 programs)\r\n[2/60] hd-plus.de - 123tv.de - Sep 20, 2023 (0 programs)\r\n[3/60] hd-plus.de - ZDF.de - Sep 20, 2023 (0 programs)\r\n[4/60] hd-plus.de - ZDF.de - Sep 19, 2023 (0 programs)\r\n...\r\n```', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2173/reactions', - total_count: 6, - '+1': 6, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2173/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2543', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2543/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2543/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2543/events', - html_url: 'https://github.com/iptv-org/epg/issues/2543', - id: 2759693796, - node_id: 'I_kwDOFLVvtM6kfZXk', - number: 2543, - title: 'flixed.io is downloding old programming', - user: { - login: 'SiWafer', - id: 10903014, - node_id: 'MDQ6VXNlcjEwOTAzMDE0', - avatar_url: 'https://avatars.githubusercontent.com/u/10903014?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/SiWafer', - html_url: 'https://github.com/SiWafer', - followers_url: 'https://api.github.com/users/SiWafer/followers', - following_url: 'https://api.github.com/users/SiWafer/following{/other_user}', - gists_url: 'https://api.github.com/users/SiWafer/gists{/gist_id}', - starred_url: 'https://api.github.com/users/SiWafer/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/SiWafer/subscriptions', - organizations_url: 'https://api.github.com/users/SiWafer/orgs', - repos_url: 'https://api.github.com/users/SiWafer/repos', - events_url: 'https://api.github.com/users/SiWafer/events{/privacy}', - received_events_url: 'https://api.github.com/users/SiWafer/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710318, - node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', - name: 'status:warning', - color: 'fbca06', - default: false, - description: 'The guide downloads, but contains errors' - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 0, - created_at: '2024-12-26T12:09:43Z', - updated_at: '2024-12-27T05:29:58Z', - closed_at: null, - author_association: 'NONE', - active_lock_reason: null, - body: '### Describe your issue\r\n\r\nJust ran and updated, and seems the output is loading old programming and it also is not complete\r\n\r\nExample is Bravo channel\r\n\r\n```\r\nepg@rpi:~/epg$ npm run grab --- --site=flixed.io --maxConnections=10 -o "flixed.xml"\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=flixed.io --maxConnections=10 -o flixed.xml\r\n\r\nstarting...\r\nconfig:\r\n output: flixed.xml\r\n maxConnections: 10\r\n gzip: false\r\n site: flixed.io\r\nloading channels...\r\n found 94 channel(s)\r\nrun #1:\r\n [1/94] flixed.io (en) - BravoEast.us - Dec 26, 2024 (2 programs)\r\n [2/94] flixed.io (en) - ComedyCentralEast.us - Dec 26, 2024 (5 programs)\r\n [3/94] flixed.io (en) - ACCNetwork.us - Dec 26, 2024 (26 programs)\r\n [4/94] flixed.io (en) - CNN.us - Dec 26, 2024 (25 programs)\r\n [5/94] flixed.io (en) - BloombergTV.us - Dec 26, 2024 (21 programs)\r\n [6/94] flixed.io (en) - AEEast.us - Dec 26, 2024 (6 programs)\r\n [7/94] flixed.io (en) - CNBC.us - Dec 26, 2024 (3 programs)\r\n [8/94] flixed.io (en) - CinemaxEast.us - Dec 26, 2024 (14 programs)\r\n [9/94] flixed.io (en) - AMCEast.us - Dec 26, 2024 (19 programs)\r\n [10/94] flixed.io (en) - BigTenNetwork.us - Dec 26, 2024 (3 programs)\r\n [11/94] flixed.io (en) - FoxWest.us - Dec 26, 2024 (8 programs)\r\n [12/94] flixed.io (en) - FreeformEast.us - Dec 26, 2024 (2 programs)\r\n [13/94] flixed.io (en) - FoxSports2.us - Dec 26, 2024 (2 programs)\r\n [14/94] flixed.io (en) - FoodNetworkEast.us - Dec 26, 2024 (3 programs)\r\n...\r\n...\r\n...\r\n\r\n```\r\nSnip from xml file out\r\n![image](https://github.com/user-attachments/assets/5ed6d7eb-df59-4951-9b2c-5214e24d24ac)\r\n\r\nThe date is September 26, 2024, 01:00:00 for Bravo\r\n\r\n', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2543/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2543/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2516', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2516/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2516/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2516/events', - html_url: 'https://github.com/iptv-org/epg/issues/2516', - id: 2745387354, - node_id: 'I_kwDOFLVvtM6jo0la', - number: 2516, - title: 'Duplicate programmes with sky.com grab', - user: { - login: 'misar1', - id: 69795879, - node_id: 'MDQ6VXNlcjY5Nzk1ODc5', - avatar_url: 'https://avatars.githubusercontent.com/u/69795879?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/misar1', - html_url: 'https://github.com/misar1', - followers_url: 'https://api.github.com/users/misar1/followers', - following_url: 'https://api.github.com/users/misar1/following{/other_user}', - gists_url: 'https://api.github.com/users/misar1/gists{/gist_id}', - starred_url: 'https://api.github.com/users/misar1/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/misar1/subscriptions', - organizations_url: 'https://api.github.com/users/misar1/orgs', - repos_url: 'https://api.github.com/users/misar1/repos', - events_url: 'https://api.github.com/users/misar1/events{/privacy}', - received_events_url: 'https://api.github.com/users/misar1/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710318, - node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', - name: 'status:warning', - color: 'fbca06', - default: false, - description: 'The guide downloads, but contains errors' - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 1, - created_at: '2024-12-17T16:19:36Z', - updated_at: '2024-12-27T05:27:44Z', - closed_at: null, - author_association: 'NONE', - active_lock_reason: null, - body: '### Site\n\nsky.com\n\n### Description\n\n\r\nI changed to the recently updated sky.com script a couple of days ago (using a completely new install as in the readme) and have found an anomaly. This does not occur with an identical grab using my previous installation. The grabs are for about 60 UK FTA channels which I can list if it would be helpful.\r\n\r\nA number of programmes have two identical blocks in the XML, including their start and finish times. This occurs only from around approximately 22:00 each day and up to about midnight GMT. During this period most channels are affected and it is consistent between successive grabs. In case of a timing issue I tested delays of 100, 1000, 3000 and 5000 msec but the XML was unchanged.', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2516/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2516/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2501', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2501/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2501/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2501/events', - html_url: 'https://github.com/iptv-org/epg/issues/2501', - id: 2738899005, - node_id: 'I_kwDOFLVvtM6jQEg9', - number: 2501, - title: 'Sky.com', - user: { - login: 'Chris230291', - id: 5328818, - node_id: 'MDQ6VXNlcjUzMjg4MTg=', - avatar_url: 'https://avatars.githubusercontent.com/u/5328818?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/Chris230291', - html_url: 'https://github.com/Chris230291', - followers_url: 'https://api.github.com/users/Chris230291/followers', - following_url: 'https://api.github.com/users/Chris230291/following{/other_user}', - gists_url: 'https://api.github.com/users/Chris230291/gists{/gist_id}', - starred_url: 'https://api.github.com/users/Chris230291/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/Chris230291/subscriptions', - organizations_url: 'https://api.github.com/users/Chris230291/orgs', - repos_url: 'https://api.github.com/users/Chris230291/repos', - events_url: 'https://api.github.com/users/Chris230291/events{/privacy}', - received_events_url: 'https://api.github.com/users/Chris230291/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710318, - node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', - name: 'status:warning', - color: 'fbca06', - default: false, - description: 'The guide downloads, but contains errors' - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 2, - created_at: '2024-12-13T17:45:02Z', - updated_at: '2024-12-27T05:25:49Z', - closed_at: null, - author_association: 'NONE', - active_lock_reason: null, - body: '### Site\n\nsky.com\n\n### Description\n\n```\r\n Viaplay 1 HD\r\n Viaplay 2 HD\r\n```\r\n\r\nThese should be "Premier Sports 1 HD" and "Premier Sports 2 HD".\r\nAlso, the UK and ROI have different schedules for these 2 channels.', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2501/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2501/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2446', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2446/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2446/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2446/events', - html_url: 'https://github.com/iptv-org/epg/issues/2446', - id: 2568624116, - node_id: 'I_kwDOFLVvtM6ZGhf0', - number: 2446, - title: 'Eleven channels returning 0 programs on meo.pt', - user: { - login: 'jonatasgz', - id: 78122211, - node_id: 'MDQ6VXNlcjc4MTIyMjEx', - avatar_url: 'https://avatars.githubusercontent.com/u/78122211?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/jonatasgz', - html_url: 'https://github.com/jonatasgz', - followers_url: 'https://api.github.com/users/jonatasgz/followers', - following_url: 'https://api.github.com/users/jonatasgz/following{/other_user}', - gists_url: 'https://api.github.com/users/jonatasgz/gists{/gist_id}', - starred_url: 'https://api.github.com/users/jonatasgz/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/jonatasgz/subscriptions', - organizations_url: 'https://api.github.com/users/jonatasgz/orgs', - repos_url: 'https://api.github.com/users/jonatasgz/repos', - events_url: 'https://api.github.com/users/jonatasgz/events{/privacy}', - received_events_url: 'https://api.github.com/users/jonatasgz/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710318, - node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', - name: 'status:warning', - color: 'fbca06', - default: false, - description: 'The guide downloads, but contains errors' - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 2, - created_at: '2024-10-06T12:00:48Z', - updated_at: '2024-12-27T05:23:16Z', - closed_at: null, - author_association: 'NONE', - active_lock_reason: null, - body: '### Site\n\nmeo.pt\n\n### Description\n\nEleven (DAZN) channels return 0 programs.', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2446/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2446/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2400', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2400/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2400/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2400/events', - html_url: 'https://github.com/iptv-org/epg/issues/2400', - id: 2370209498, - node_id: 'I_kwDOFLVvtM6NRoba', - number: 2400, - title: 'tvgids.nl', - user: { - login: 'freearhey', - id: 7253922, - node_id: 'MDQ6VXNlcjcyNTM5MjI=', - avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/freearhey', - html_url: 'https://github.com/freearhey', - followers_url: 'https://api.github.com/users/freearhey/followers', - following_url: 'https://api.github.com/users/freearhey/following{/other_user}', - gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', - starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', - organizations_url: 'https://api.github.com/users/freearhey/orgs', - repos_url: 'https://api.github.com/users/freearhey/repos', - events_url: 'https://api.github.com/users/freearhey/events{/privacy}', - received_events_url: 'https://api.github.com/users/freearhey/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710318, - node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', - name: 'status:warning', - color: 'fbca06', - default: false, - description: 'The guide downloads, but contains errors' - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 0, - created_at: '2024-06-24T13:05:05Z', - updated_at: '2024-12-27T05:19:25Z', - closed_at: null, - author_association: 'COLLABORATOR', - active_lock_reason: null, - body: "### Site\n\ntvgids.nl\n\n### Description\n\nTest failed:\r\n\r\n```sh\r\nnpm test -- tvgids.nl \r\n\r\n> test\r\n> run-script-os tvgids.nl\r\n\r\n\r\n> test:default\r\n> TZ=Pacific/Nauru npx jest --runInBand tvgids.nl\r\n\r\n FAIL sites/tvgids.nl/tvgids.nl.test.js\r\n ✓ can generate valid url (29 ms)\r\n ✕ can generate valid url for today (4 ms)\r\n ✓ can parse response (307 ms)\r\n ✓ can handle empty guide (3 ms)\r\n\r\n ● can generate valid url for today\r\n\r\n expect(received).toBe(expected) // Object.is equality\r\n\r\n Expected: \"https://www.tvgids.nl/gids/npo1\"\r\n Received: \"https://www.tvgids.nl/gids/25-06-2024/npo1\"\r\n\r\n 21 | const today = dayjs().startOf('d')\r\n 22 |\r\n > 23 | expect(url({ date: today, channel })).toBe('https://www.tvgids.nl/gids/npo1')\r\n | ^\r\n 24 | })\r\n 25 |\r\n 26 | it('can parse response', () => {\r\n\r\n at Object. (sites/tvgids.nl/tvgids.nl.test.js:23:41)\r\n\r\nTest Suites: 1 failed, 1 total\r\nTests: 1 failed, 3 passed, 4 total\r\nSnapshots: 0 total\r\nTime: 3.52 s\r\nRan all test suites matching /tvgids.nl/i.\r\n```", - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2400/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2400/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2399', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2399/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2399/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2399/events', - html_url: 'https://github.com/iptv-org/epg/issues/2399', - id: 2370207356, - node_id: 'I_kwDOFLVvtM6NRn58', - number: 2399, - title: 'tvprofil.com', - user: { - login: 'freearhey', - id: 7253922, - node_id: 'MDQ6VXNlcjcyNTM5MjI=', - avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/freearhey', - html_url: 'https://github.com/freearhey', - followers_url: 'https://api.github.com/users/freearhey/followers', - following_url: 'https://api.github.com/users/freearhey/following{/other_user}', - gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', - starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', - organizations_url: 'https://api.github.com/users/freearhey/orgs', - repos_url: 'https://api.github.com/users/freearhey/repos', - events_url: 'https://api.github.com/users/freearhey/events{/privacy}', - received_events_url: 'https://api.github.com/users/freearhey/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710318, - node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', - name: 'status:warning', - color: 'fbca06', - default: false, - description: 'The guide downloads, but contains errors' - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 1, - created_at: '2024-06-24T13:04:08Z', - updated_at: '2024-12-27T05:18:50Z', - closed_at: null, - author_association: 'COLLABORATOR', - active_lock_reason: null, - body: '### Site\n\ntvprofil.com\n\n### Description\n\nTest failed:\r\n\r\n```sh\r\nnpm test -- tvprofil.com\r\n\r\n> test\r\n> run-script-os tvprofil.com\r\n\r\n\r\n> test:default\r\n> TZ=Pacific/Nauru npx jest --runInBand tvprofil.com\r\n\r\n FAIL sites/tvprofil.com/tvprofil.com.test.js\r\n ✕ can generate valid url (14 ms)\r\n ✓ can generate valid request headers (1 ms)\r\n ✓ can parse response (76 ms)\r\n ✓ can handle empty guide (2 ms)\r\n\r\n ● can generate valid url\r\n\r\n expect(received).toBe(expected) // Object.is equality\r\n\r\n Expected: "https://tvprofil.com/bg/tv-programa/program/?datum=2023-01-12&kanal=24kitchen-bg&callback=cb&b51=818933"\r\n Received: "https://tvprofil.com/bg/tv-programa/program/?datum=2023-01-12&kanal=24kitchen-bg&callback=cb&b49=819461"\r\n\r\n 15 |\r\n 16 | it(\'can generate valid url\', () => {\r\n > 17 | expect(url({ channel, date })).toBe(\r\n | ^\r\n 18 | \'https://tvprofil.com/bg/tv-programa/program/?datum=2023-01-12&kanal=24kitchen-bg&callback=cb&b51=818933\'\r\n 19 | )\r\n 20 | })\r\n\r\n at Object. (sites/tvprofil.com/tvprofil.com.test.js:17:34)\r\n\r\nTest Suites: 1 failed, 1 total\r\nTests: 1 failed, 3 passed, 4 total\r\nSnapshots: 0 total\r\nTime: 3.307 s\r\nRan all test suites matching /tvprofil.com/i.\r\n```', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2399/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2399/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2396', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2396/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2396/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2396/events', - html_url: 'https://github.com/iptv-org/epg/issues/2396', - id: 2370189473, - node_id: 'I_kwDOFLVvtM6NRjih', - number: 2396, - title: 'iltalehti.fi', - user: { - login: 'freearhey', - id: 7253922, - node_id: 'MDQ6VXNlcjcyNTM5MjI=', - avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/freearhey', - html_url: 'https://github.com/freearhey', - followers_url: 'https://api.github.com/users/freearhey/followers', - following_url: 'https://api.github.com/users/freearhey/following{/other_user}', - gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', - starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', - organizations_url: 'https://api.github.com/users/freearhey/orgs', - repos_url: 'https://api.github.com/users/freearhey/repos', - events_url: 'https://api.github.com/users/freearhey/events{/privacy}', - received_events_url: 'https://api.github.com/users/freearhey/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710318, - node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', - name: 'status:warning', - color: 'fbca06', - default: false, - description: 'The guide downloads, but contains errors' - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 0, - created_at: '2024-06-24T12:56:45Z', - updated_at: '2024-12-27T05:18:16Z', - closed_at: null, - author_association: 'COLLABORATOR', - active_lock_reason: null, - body: '### Site\n\niltalehti.fi\n\n### Description\n\nTest failed:\r\n\r\n```sh\r\nnpm test -- iltalehti.fi \r\n\r\n> test\r\n> run-script-os iltalehti.fi\r\n\r\n\r\n> test:default\r\n> TZ=Pacific/Nauru npx jest --runInBand iltalehti.fi\r\n\r\n FAIL sites/iltalehti.fi/iltalehti.fi.test.js\r\n ✕ can generate valid url (10 ms)\r\n ✓ can parse response (28 ms)\r\n ✓ can handle empty guide (1 ms)\r\n\r\n ● can generate valid url\r\n\r\n expect(received).toBe(expected) // Object.is equality\r\n\r\n Expected: "https://telkku.com/api/channel-groups/default_builtin_channelgroup1/offering?startTime=00%3A00%3A00.000&duration=PT24H&inclusionPolicy=IncludeOngoingAlso&limit=1000&tvDate=2022-10-29&view=PublicationDetails"\r\n Received: "https://telkku.com/api/channel-groups/default_builtin_channelgroupdefault_builtin_channelgroup1/offering?startTime=00%3A00%3A00.000&duration=PT24H&inclusionPolicy=IncludeOngoingAlso&limit=1000&tvDate=2022-10-29&view=PublicationDetails"\r\n\r\n 15 |\r\n 16 | it(\'can generate valid url\', () => {\r\n > 17 | expect(url({ channel, date })).toBe(\r\n | ^\r\n 18 | \'https://telkku.com/api/channel-groups/default_builtin_channelgroup1/offering?startTime=00%3A00%3A00.000&duration=PT24H&inclusionPolicy=IncludeOngoingAlso&limit=1000&tvDate=2022-10-29&view=PublicationDetails\'\r\n 19 | )\r\n 20 | })\r\n\r\n at Object. (sites/iltalehti.fi/iltalehti.fi.test.js:17:34)\r\n\r\nTest Suites: 1 failed, 1 total\r\nTests: 1 failed, 2 passed, 3 total\r\nSnapshots: 0 total\r\nTime: 3.193 s\r\nRan all test suites matching /iltalehti.fi/i.\r\n```', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2396/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2396/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2395', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2395/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2395/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2395/events', - html_url: 'https://github.com/iptv-org/epg/issues/2395', - id: 2370186157, - node_id: 'I_kwDOFLVvtM6NRiut', - number: 2395, - title: 'chaines-tv.orange.fr', - user: { - login: 'freearhey', - id: 7253922, - node_id: 'MDQ6VXNlcjcyNTM5MjI=', - avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/freearhey', - html_url: 'https://github.com/freearhey', - followers_url: 'https://api.github.com/users/freearhey/followers', - following_url: 'https://api.github.com/users/freearhey/following{/other_user}', - gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', - starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', - organizations_url: 'https://api.github.com/users/freearhey/orgs', - repos_url: 'https://api.github.com/users/freearhey/repos', - events_url: 'https://api.github.com/users/freearhey/events{/privacy}', - received_events_url: 'https://api.github.com/users/freearhey/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710318, - node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', - name: 'status:warning', - color: 'fbca06', - default: false, - description: 'The guide downloads, but contains errors' - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 2, - created_at: '2024-06-24T12:55:21Z', - updated_at: '2024-12-27T05:17:25Z', - closed_at: null, - author_association: 'COLLABORATOR', - active_lock_reason: null, - body: '### Site\r\n\r\nchaines-tv.orange.fr\r\n\r\n### Description\r\n\r\nTest failed:\r\n\r\n```sh\r\nnpm test -- chaines-tv.orange.fr\r\n\r\n> test\r\n> run-script-os chaines-tv.orange.fr\r\n\r\n\r\n> test:default\r\n> TZ=Pacific/Nauru npx jest --runInBand chaines-tv.orange.fr\r\n\r\n FAIL sites/chaines-tv.orange.fr/chaines-tv.orange.fr.test.js\r\n ✓ can generate valid url (5 ms)\r\n ✕ can parse response (12 ms)\r\n ✓ can handle empty guide (1 ms)\r\n\r\n ● can parse response\r\n\r\n expect(received).toMatchObject(expected)\r\n\r\n - Expected - 1\r\n + Received + 1\r\n\r\n @@ -3,8 +3,8 @@\r\n "category": "Série Suspense",\r\n "description": "Un tueur en série prend un plaisir pervers à prévenir les autorités de Tallahassee avant chaque nouveau meurtre. Rossi apprend le décès d\'un de ses vieux amis.",\r\n "icon": "https://proxymedia.woopic.com/340/p/169_EMI_9697669.jpg",\r\n "start": "2021-11-07T23:35:00.000Z",\r\n "stop": "2021-11-08T00:20:00.000Z",\r\n - "title": "Tête de liste",\r\n + "title": "Esprits criminels",\r\n },\r\n ]\r\n\r\n 23 | it(\'can parse response\', () => {\r\n 24 | const result = parser({ date, channel, content })\r\n > 25 | expect(result).toMatchObject([\r\n | ^\r\n 26 | {\r\n 27 | start: \'2021-11-07T23:35:00.000Z\',\r\n 28 | stop: \'2021-11-08T00:20:00.000Z\',\r\n\r\n at Object. (sites/chaines-tv.orange.fr/chaines-tv.orange.fr.test.js:25:18)\r\n\r\nTest Suites: 1 failed, 1 total\r\nTests: 1 failed, 2 passed, 3 total\r\nSnapshots: 0 total\r\nTime: 4.003 s\r\nRan all test suites matching /chaines-tv.orange.fr/i.\r\n```', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2395/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2395/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2304', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2304/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2304/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2304/events', - html_url: 'https://github.com/iptv-org/epg/issues/2304', - id: 2064638324, - node_id: 'I_kwDOFLVvtM57D-F0', - number: 2304, - title: 'digiturk.com.tr Missing Channel Identifications', - user: { - login: 'UzunMuhalefet', - id: 80906858, - node_id: 'MDQ6VXNlcjgwOTA2ODU4', - avatar_url: 'https://avatars.githubusercontent.com/u/80906858?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/UzunMuhalefet', - html_url: 'https://github.com/UzunMuhalefet', - followers_url: 'https://api.github.com/users/UzunMuhalefet/followers', - following_url: 'https://api.github.com/users/UzunMuhalefet/following{/other_user}', - gists_url: 'https://api.github.com/users/UzunMuhalefet/gists{/gist_id}', - starred_url: 'https://api.github.com/users/UzunMuhalefet/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/UzunMuhalefet/subscriptions', - organizations_url: 'https://api.github.com/users/UzunMuhalefet/orgs', - repos_url: 'https://api.github.com/users/UzunMuhalefet/repos', - events_url: 'https://api.github.com/users/UzunMuhalefet/events{/privacy}', - received_events_url: 'https://api.github.com/users/UzunMuhalefet/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710318, - node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', - name: 'status:warning', - color: 'fbca06', - default: false, - description: 'The guide downloads, but contains errors' - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 0, - created_at: '2024-01-03T20:57:58Z', - updated_at: '2024-12-27T05:32:37Z', - closed_at: null, - author_association: 'NONE', - active_lock_reason: null, - body: '### Site\r\n\r\ndigiturk.com.tr\r\n\r\n### Describe your issue\r\n\r\nThe following channels can be corrected as follows. (I added the TVG-IDs if they exist in https://iptv-org.github.io/)\r\n\r\n```\r\nKANAL 24 HD\r\nTARIH TV HD\r\nSÖZCÜ TV HD\r\n```\r\n\r\nLet me clarify the cases:\r\n- SZC changed its name to Sözcü TV\r\n- 24 TV often called as Kanal (Channel) 24\r\n- Tarih TV is straightforward.\r\n', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2304/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2304/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2303', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2303/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2303/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2303/events', - html_url: 'https://github.com/iptv-org/epg/issues/2303', - id: 2064633657, - node_id: 'I_kwDOFLVvtM57D885', - number: 2303, - title: 'dsmart.com.tr Channel Identifications', - user: { - login: 'UzunMuhalefet', - id: 80906858, - node_id: 'MDQ6VXNlcjgwOTA2ODU4', - avatar_url: 'https://avatars.githubusercontent.com/u/80906858?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/UzunMuhalefet', - html_url: 'https://github.com/UzunMuhalefet', - followers_url: 'https://api.github.com/users/UzunMuhalefet/followers', - following_url: 'https://api.github.com/users/UzunMuhalefet/following{/other_user}', - gists_url: 'https://api.github.com/users/UzunMuhalefet/gists{/gist_id}', - starred_url: 'https://api.github.com/users/UzunMuhalefet/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/UzunMuhalefet/subscriptions', - organizations_url: 'https://api.github.com/users/UzunMuhalefet/orgs', - repos_url: 'https://api.github.com/users/UzunMuhalefet/repos', - events_url: 'https://api.github.com/users/UzunMuhalefet/events{/privacy}', - received_events_url: 'https://api.github.com/users/UzunMuhalefet/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710318, - node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', - name: 'status:warning', - color: 'fbca06', - default: false, - description: 'The guide downloads, but contains errors' - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 1, - created_at: '2024-01-03T20:53:34Z', - updated_at: '2024-12-27T05:44:16Z', - closed_at: null, - author_association: 'NONE', - active_lock_reason: null, - body: '### Describe your issue\r\n\r\nThe following channels can be corrected as follows. (I added the TVG-IDs if they exist in https://iptv-org.github.io/)\r\n\r\n```\r\nNR1\r\nNR1 Türk\r\nTRT EBA TV İlkokul\r\nTRT EBA TV Ortaokul\r\nTRT EBA TV Lise\r\nÇifçi TV\r\nKanal 16\r\nFlash Haber\r\nYeni Kocaeli TV\r\nATV Turkiye SD\r\nTRT EBA TV İlkokul SD\r\nTRT EBA TV Ortaokul SD\r\nTRT EBA TV Lise SD\r\nTarih Tv\r\nSZC\r\nTRT 3 Spor\r\n``` \r\n\r\nLet me clarify the cases: \r\n\r\n- NR1 stands for Number One\r\n- Cifci TV - Çiftçi TV - misspelling\r\n- The number 16 is spelled as onaltı (on -> 10, altı->6, on6 -> 16) in Turkish so on6 - Kanal 16\r\n- Flash TV changed its name to Flash Haber TV\r\n- SZC changed its name to Sözcü TV\r\n- TRT 3 is a strange channel that usually streams the same content as TRT Spor but when there is a Turkish parliament event it switches to that event. So, the channel is both called TRT 3 and TRT 3 Spor.\r\n', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2303/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2303/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2272', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2272/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2272/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2272/events', - html_url: 'https://github.com/iptv-org/epg/issues/2272', - id: 2022135766, - node_id: 'I_kwDOFLVvtM54h1fW', - number: 2272, - title: 'Film titles with numbers only in the title (as in "1900" or "1917") don\'t show up', - user: { - login: 'GlamoramaAttack', - id: 116585465, - node_id: 'U_kgDOBvLz-Q', - avatar_url: 'https://avatars.githubusercontent.com/u/116585465?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/GlamoramaAttack', - html_url: 'https://github.com/GlamoramaAttack', - followers_url: 'https://api.github.com/users/GlamoramaAttack/followers', - following_url: 'https://api.github.com/users/GlamoramaAttack/following{/other_user}', - gists_url: 'https://api.github.com/users/GlamoramaAttack/gists{/gist_id}', - starred_url: 'https://api.github.com/users/GlamoramaAttack/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/GlamoramaAttack/subscriptions', - organizations_url: 'https://api.github.com/users/GlamoramaAttack/orgs', - repos_url: 'https://api.github.com/users/GlamoramaAttack/repos', - events_url: 'https://api.github.com/users/GlamoramaAttack/events{/privacy}', - received_events_url: 'https://api.github.com/users/GlamoramaAttack/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710318, - node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', - name: 'status:warning', - color: 'fbca06', - default: false, - description: 'The guide downloads, but contains errors' - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 2, - created_at: '2023-12-02T17:29:50Z', - updated_at: '2024-12-27T05:02:05Z', - closed_at: null, - author_association: 'NONE', - active_lock_reason: null, - body: '### Site\r\n\r\ntvpassport.com\r\n\r\n### Describe your issue\r\n\r\nFilm titles with numbers only in the title -as in "1900" or "1917"- don\'t show up at all.\r\n\r\nSorry, I don\'t understand anything about how to write a script or fix it but I see there is notable difference in the **guide.xml** file - the examples are excerpts from tvpassport.com. This isn\'t a new bug I think, it\'s just that I thought for a long time it\'s a Kodi problem before looking into the guide.xml file...\r\n\r\n```\r\nchannel="Showtime2East.us">The Caine Mutiny Court-MartialA \r\nchannel="Showtime2East.us">Two British soldiers receive seemingly impossible orders \r\nchannel="Showtime2East.us">There Will Be BloodDaniel \r\n```\r\nor:\r\n\r\n```\r\nchannel="MGMPlusEast.us">A Fistful of DynamiteA thief \r\nchannel="MGMPlusEast.us">The son (Robert De Niro) of a landowner and the son \r\nchannel="MGMPlusEast.us">Indecent ProposalA Las Vegas \r\n```\r\n\r\nFor some reason "title lang" gets replaced by "desc lang" and the film title is nowhere displayed.\r\n\r\nOn Kodi it looks like this for the films and you can also see it\'s not a problem if the film title includes more than numbers (see "88 Minutes" or "Resistance: 1942"). But titles as "1900", "1917" or "1408" (Horror with John Cusack) appear blank.\r\n\r\n![NoTitleWithNumbers](https://github.com/iptv-org/epg/assets/116585465/155f98f8-8529-4c42-af3c-def1edcf5651)\r\n\r\nI\'m not sure whether the problem also exists with other sources than tvpassport.com because I use only two other (German language) EPG sources that have currently no film titles with numbers only. But I assume it\'s a bug if it\'s not reduced to tvpassport.', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2272/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2272/timeline', - performed_via_github_app: null, - state_reason: null - }, - { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2176', - repository_url: 'https://api.github.com/repos/iptv-org/epg', - labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2176/labels{/name}', - comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2176/comments', - events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2176/events', - html_url: 'https://github.com/iptv-org/epg/issues/2176', - id: 1915947653, - node_id: 'I_kwDOFLVvtM5yMwqF', - number: 2176, - title: 'tvtv', - user: { - login: 'jvdillon', - id: 1137078, - node_id: 'MDQ6VXNlcjExMzcwNzg=', - avatar_url: 'https://avatars.githubusercontent.com/u/1137078?v=4', - gravatar_id: '', - url: 'https://api.github.com/users/jvdillon', - html_url: 'https://github.com/jvdillon', - followers_url: 'https://api.github.com/users/jvdillon/followers', - following_url: 'https://api.github.com/users/jvdillon/following{/other_user}', - gists_url: 'https://api.github.com/users/jvdillon/gists{/gist_id}', - starred_url: 'https://api.github.com/users/jvdillon/starred{/owner}{/repo}', - subscriptions_url: 'https://api.github.com/users/jvdillon/subscriptions', - organizations_url: 'https://api.github.com/users/jvdillon/orgs', - repos_url: 'https://api.github.com/users/jvdillon/repos', - events_url: 'https://api.github.com/users/jvdillon/events{/privacy}', - received_events_url: 'https://api.github.com/users/jvdillon/received_events', - type: 'User', - user_view_type: 'public', - site_admin: false - }, - labels: [ - { - id: 4542348869, - node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', - url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', - name: 'broken guide', - color: 'eaeaea', - default: false, - description: "There's a problem with the guide" - }, - { - id: 7932710318, - node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', - url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', - name: 'status:warning', - color: 'fbca06', - default: false, - description: 'The guide downloads, but contains errors' - } - ], - state: 'open', - locked: false, - assignee: null, - assignees: [], - milestone: null, - comments: 6, - created_at: '2023-09-27T16:34:00Z', - updated_at: '2024-12-27T04:55:00Z', - closed_at: null, - author_association: 'NONE', - active_lock_reason: null, - body: '### Site\r\n\r\ntvtv.us\r\n\r\n### Description\r\n\r\nStarts getting error 429 after several 100 scrapes. I tried setting the delay to 1000ms, 1500ms, and 2000ms but keep seeing the issue.\r\n\r\nIt almost appears to happen after a sequence of 0 program scrapes. I wonder if the delay is not happening when nothing is downloaded which causes tvtv.us to throttle which triggers 0 programs downloaded. (Ie a feedback loop)', - closed_by: null, - reactions: { - url: 'https://api.github.com/repos/iptv-org/epg/issues/2176/reactions', - total_count: 0, - '+1': 0, - '-1': 0, - laugh: 0, - hooray: 0, - confused: 0, - heart: 0, - rocket: 0, - eyes: 0 - }, - timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2176/timeline', - performed_via_github_app: null, - state_reason: null - } -] +export default [ + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2547', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2547/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2547/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2547/events', + html_url: 'https://github.com/iptv-org/epg/issues/2547', + id: 2760418979, + node_id: 'I_kwDOFLVvtM6kiKaj', + number: 2547, + title: 'digiturk.com.tr', + user: { + login: 'freearhey', + id: 7253922, + node_id: 'MDQ6VXNlcjcyNTM5MjI=', + avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/freearhey', + html_url: 'https://github.com/freearhey', + followers_url: 'https://api.github.com/users/freearhey/followers', + following_url: 'https://api.github.com/users/freearhey/following{/other_user}', + gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', + starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', + organizations_url: 'https://api.github.com/users/freearhey/orgs', + repos_url: 'https://api.github.com/users/freearhey/repos', + events_url: 'https://api.github.com/users/freearhey/events{/privacy}', + received_events_url: 'https://api.github.com/users/freearhey/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710795, + node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', + name: 'status:down', + color: 'df3a4a', + default: false, + description: "The guide doesn't work" + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 1, + created_at: '2024-12-27T05:33:47Z', + updated_at: '2024-12-28T20:37:53Z', + closed_at: null, + author_association: 'COLLABORATOR', + active_lock_reason: null, + body: '### Site\n\ndigiturk.com.tr\n\n### Description\n\n```sh\r\nnpm run grab --- --site=digiturk.com.tr\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=digiturk.com.tr\r\n\r\nstarting...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: digiturk.com.tr\r\nloading channels...\r\n found 139 channel(s)\r\nrun #1:\r\n [1/278] digiturk.com.tr (en) - AlJazeeraEnglish.qa - Dec 27, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [2/278] digiturk.com.tr (en) - AlJazeeraEnglish.qa - Dec 28, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [3/278] digiturk.com.tr (tr) - Yaban.tr - Dec 28, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n```', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2547/reactions', + total_count: 1, + '+1': 1, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2547/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2542', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2542/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2542/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2542/events', + html_url: 'https://github.com/iptv-org/epg/issues/2542', + id: 2759639980, + node_id: 'I_kwDOFLVvtM6kfMOs', + number: 2542, + title: 'tvguide.com not working', + user: { + login: 'SiWafer', + id: 10903014, + node_id: 'MDQ6VXNlcjEwOTAzMDE0', + avatar_url: 'https://avatars.githubusercontent.com/u/10903014?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/SiWafer', + html_url: 'https://github.com/SiWafer', + followers_url: 'https://api.github.com/users/SiWafer/followers', + following_url: 'https://api.github.com/users/SiWafer/following{/other_user}', + gists_url: 'https://api.github.com/users/SiWafer/gists{/gist_id}', + starred_url: 'https://api.github.com/users/SiWafer/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/SiWafer/subscriptions', + organizations_url: 'https://api.github.com/users/SiWafer/orgs', + repos_url: 'https://api.github.com/users/SiWafer/repos', + events_url: 'https://api.github.com/users/SiWafer/events{/privacy}', + received_events_url: 'https://api.github.com/users/SiWafer/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710795, + node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', + name: 'status:down', + color: 'df3a4a', + default: false, + description: "The guide doesn't work" + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 0, + created_at: '2024-12-26T11:11:24Z', + updated_at: '2024-12-27T05:28:55Z', + closed_at: null, + author_association: 'NONE', + active_lock_reason: null, + body: '### Site\n\ntvguide.com\n\n### Description\n\n```\r\nepg@rpi:~/epg$ npm run grab --- --site=tvguide.com\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=tvguide.com\r\n\r\nstarting...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: tvguide.com\r\nloading channels...\r\n found 39 channel(s)\r\nrun #1:\r\n [1/78] tvguide.com (en) - AEEast.us - Dec 26, 2024 (0 programs)\r\n ERR: Request failed with status code 403\r\n [2/78] tvguide.com (en) - AEEast.us - Dec 27, 2024 (0 programs)\r\n ERR: Request failed with status code 403\r\n [3/78] tvguide.com (en) - AMCEast.us - Dec 27, 2024 (0 programs)\r\n ERR: Request failed with status code 403\r\n [4/78] tvguide.com (en) - AMCEast.us - Dec 26, 2024 (0 programs)\r\n ERR: Request failed with status code 403\r\n [5/78] tvguide.com (en) - BravoEast.us - Dec 27, 2024 (0 programs)\r\n ERR: Request failed with status code 403\r\n [6/78] tvguide.com (en) - BravoEast.us - Dec 26, 2024 (0 programs)\r\n ERR: Request failed with status code 403\r\n [7/78] tvguide.com (en) - BBCAmericaEast.us - Dec 27, 2024 (0 programs)\r\n ERR: Request failed with status code 403\r\n [8/78] tvguide.com (en) - CNBC.us - Dec 27, 2024 (0 programs)\r\n ERR: Request failed with status code 403\r\n [9/78] tvguide.com (en) - CNBC.us - Dec 26, 2024 (0 programs)\r\n...\r\n...\r\n...\r\n```', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2542/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2542/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2541', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2541/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2541/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2541/events', + html_url: 'https://github.com/iptv-org/epg/issues/2541', + id: 2758749907, + node_id: 'I_kwDOFLVvtM6kby7T', + number: 2541, + title: 'elcinema.com grab error 403', + user: { + login: 'xercessss', + id: 189765257, + node_id: 'U_kgDOC0-WiQ', + avatar_url: 'https://avatars.githubusercontent.com/u/189765257?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/xercessss', + html_url: 'https://github.com/xercessss', + followers_url: 'https://api.github.com/users/xercessss/followers', + following_url: 'https://api.github.com/users/xercessss/following{/other_user}', + gists_url: 'https://api.github.com/users/xercessss/gists{/gist_id}', + starred_url: 'https://api.github.com/users/xercessss/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/xercessss/subscriptions', + organizations_url: 'https://api.github.com/users/xercessss/orgs', + repos_url: 'https://api.github.com/users/xercessss/repos', + events_url: 'https://api.github.com/users/xercessss/events{/privacy}', + received_events_url: 'https://api.github.com/users/xercessss/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710795, + node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', + name: 'status:down', + color: 'df3a4a', + default: false, + description: "The guide doesn't work" + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 0, + created_at: '2024-12-25T11:34:16Z', + updated_at: '2024-12-27T05:28:07Z', + closed_at: null, + author_association: 'NONE', + active_lock_reason: null, + body: '### Site\n\nelcinema.com\n\n### Description\n\ngrabbing elcinema.com epg return error 403 for both ar and en language:\r\nERR: Request failed with status code 403\r\n', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2541/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2541/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2509', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2509/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2509/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2509/events', + html_url: 'https://github.com/iptv-org/epg/issues/2509', + id: 2740938968, + node_id: 'I_kwDOFLVvtM6jX2jY', + number: 2509, + title: 'ssl problem maxtv.hrvatskitelekom.hr', + user: { + login: 'uploadkom', + id: 130712472, + node_id: 'U_kgDOB8qDmA', + avatar_url: 'https://avatars.githubusercontent.com/u/130712472?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/uploadkom', + html_url: 'https://github.com/uploadkom', + followers_url: 'https://api.github.com/users/uploadkom/followers', + following_url: 'https://api.github.com/users/uploadkom/following{/other_user}', + gists_url: 'https://api.github.com/users/uploadkom/gists{/gist_id}', + starred_url: 'https://api.github.com/users/uploadkom/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/uploadkom/subscriptions', + organizations_url: 'https://api.github.com/users/uploadkom/orgs', + repos_url: 'https://api.github.com/users/uploadkom/repos', + events_url: 'https://api.github.com/users/uploadkom/events{/privacy}', + received_events_url: 'https://api.github.com/users/uploadkom/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710795, + node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', + name: 'status:down', + color: 'df3a4a', + default: false, + description: "The guide doesn't work" + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 3, + created_at: '2024-12-15T22:21:22Z', + updated_at: '2024-12-27T05:26:26Z', + closed_at: null, + author_association: 'NONE', + active_lock_reason: null, + body: '### Site\n\nmaxtv.hrvatskitelekom.hr\n\n### Description\n\nssl problem', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2509/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2509/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2498', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2498/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2498/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2498/events', + html_url: 'https://github.com/iptv-org/epg/issues/2498', + id: 2728188494, + node_id: 'I_kwDOFLVvtM6inNpO', + number: 2498, + title: 'movistarplus.es', + user: { + login: 'freearhey', + id: 7253922, + node_id: 'MDQ6VXNlcjcyNTM5MjI=', + avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/freearhey', + html_url: 'https://github.com/freearhey', + followers_url: 'https://api.github.com/users/freearhey/followers', + following_url: 'https://api.github.com/users/freearhey/following{/other_user}', + gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', + starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', + organizations_url: 'https://api.github.com/users/freearhey/orgs', + repos_url: 'https://api.github.com/users/freearhey/repos', + events_url: 'https://api.github.com/users/freearhey/events{/privacy}', + received_events_url: 'https://api.github.com/users/freearhey/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710795, + node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', + name: 'status:down', + color: 'df3a4a', + default: false, + description: "The guide doesn't work" + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 0, + created_at: '2024-12-09T20:26:07Z', + updated_at: '2024-12-27T05:23:48Z', + closed_at: null, + author_association: 'COLLABORATOR', + active_lock_reason: null, + body: '### Site\n\nmovistarplus.es\n\n### Description\n\n```sh\r\nnpm run grab -- --site=movistarplus.es\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=movistarplus.es\r\n\r\n(node:22362) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`:\r\n--import \'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("file%3A///Users/Arhey/Code/iptv-org/epg/node_modules/tsx/dist/loader.mjs", pathToFileURL("./"));\'\r\n(Use `node --trace-warnings ...` to show where the warning was created)\r\n(node:22362) UnsupportedWarning: `globalPreload` has been removed; use `initialize` instead.\r\n(Use `node --trace-warnings ...` to show where the warning was created)\r\nstarting...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: movistarplus.es\r\nloading channels...\r\n(node:22362) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.\r\n found 186 channel(s)\r\nrun #1:\r\n [1/372] movistarplus.es (es) - ANTV - Dec 9, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [2/372] movistarplus.es (es) - ANTV - Dec 10, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [3/372] movistarplus.es (es) - ARAGTV - Dec 10, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [4/372] movistarplus.es (es) - CANSUR - Dec 10, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [5/372] movistarplus.es (es) - DAZNL2 - Dec 10, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [6/372] movistarplus.es (es) - MLIGS - Dec 10, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [7/372] movistarplus.es (es) - AMC.es - Dec 10, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [8/372] movistarplus.es (es) - ComediaporMovistarPlusPlus.es - Dec 10, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [9/372] movistarplus.es (es) - LasEstrellasEuropa.mx - Dec 10, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [10/372] movistarplus.es (es) - LasEstrellasEuropa.mx - Dec 9, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [11/372] movistarplus.es (es) - ComediaporMovistarPlusPlus.es - Dec 9, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [12/372] movistarplus.es (es) - LaResistencia.es - Dec 10, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [13/372] movistarplus.es (es) - LaResistencia.es - Dec 9, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n```', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2498/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2498/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2445', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2445/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2445/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2445/events', + html_url: 'https://github.com/iptv-org/epg/issues/2445', + id: 2567652015, + node_id: 'I_kwDOFLVvtM6ZC0Kv', + number: 2445, + title: 'EPG from dishtv.in stopped with error code 404', + user: { + login: 'dheer99', + id: 20639130, + node_id: 'MDQ6VXNlcjIwNjM5MTMw', + avatar_url: 'https://avatars.githubusercontent.com/u/20639130?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/dheer99', + html_url: 'https://github.com/dheer99', + followers_url: 'https://api.github.com/users/dheer99/followers', + following_url: 'https://api.github.com/users/dheer99/following{/other_user}', + gists_url: 'https://api.github.com/users/dheer99/gists{/gist_id}', + starred_url: 'https://api.github.com/users/dheer99/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/dheer99/subscriptions', + organizations_url: 'https://api.github.com/users/dheer99/orgs', + repos_url: 'https://api.github.com/users/dheer99/repos', + events_url: 'https://api.github.com/users/dheer99/events{/privacy}', + received_events_url: 'https://api.github.com/users/dheer99/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710795, + node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', + name: 'status:down', + color: 'df3a4a', + default: false, + description: "The guide doesn't work" + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 2, + created_at: '2024-10-05T04:30:52Z', + updated_at: '2024-12-27T05:20:11Z', + closed_at: null, + author_association: 'NONE', + active_lock_reason: null, + body: '### Site\n\ndishtv.in\n\n### Description\n\nEPG from dishtv.in stopped with error code 404\r\n\r\n [91/98] dishtv.in (en) - StarPlus.in - Oct 5, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [92/98] dishtv.in (en) - SET.in - Oct 6, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [93/98] dishtv.in (en) - ZeeCinema.in - Oct 6, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [94/98] dishtv.in (en) - SonySportsTen5.in - Oct 5, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [95/98] dishtv.in (en) - StarSports1.in - Oct 6, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [96/98] dishtv.in (en) - SET.in - Oct 5, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [97/98] dishtv.in (en) - SonySportsTen3.in - Oct 6, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n [98/98] dishtv.in (en) - Colors.in - Oct 6, 2024 (0 programs)\r\n ERR: Request failed with status code 404\r\n done in 00h 00m 03s\r\n', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2445/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2445/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2339', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2339/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2339/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2339/events', + html_url: 'https://github.com/iptv-org/epg/issues/2339', + id: 2172864415, + node_id: 'I_kwDOFLVvtM6Bg0ef', + number: 2339, + title: + 'directv.com.ar and directv.com.uy not working ERR: Unexpected token \'<\', " grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=kan.org.il --output=kan.org.il.xml\r\n\r\nstarting...\r\nconfig:\r\n output: kan.org.il.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: kan.org.il\r\nloading channels...\r\n found 3 channel(s)\r\nrun #1:\r\n [1/6] kan.org.il (ar) - Makan33.il - Dec 2, 2023 (0 programs)\r\n ERR: Request failed with status code 404\r\n [2/6] kan.org.il (ar) - Makan33.il - Dec 3, 2023 (0 programs)\r\n ERR: Request failed with status code 404\r\n [3/6] kan.org.il (he) - KanEducational.il - Dec 3, 2023 (0 programs)\r\n ERR: Request failed with status code 404\r\n [4/6] kan.org.il (he) - KanEducational.il - Dec 2, 2023 (0 programs)\r\n ERR: Request failed with status code 404\r\n [5/6] kan.org.il (he) - Kan11.il - Dec 3, 2023 (0 programs)\r\n ERR: Request failed with status code 404\r\n [6/6] kan.org.il (he) - Kan11.il - Dec 2, 2023 (0 programs)\r\n ERR: Request failed with status code 404\r\n done in 00h 00m 01s\r\n```', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2273/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2273/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2270', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2270/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2270/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2270/events', + html_url: 'https://github.com/iptv-org/epg/issues/2270', + id: 2022018570, + node_id: 'I_kwDOFLVvtM54hY4K', + number: 2270, + title: 'vivacom.bg is broken', + user: { + login: 'freearhey', + id: 7253922, + node_id: 'MDQ6VXNlcjcyNTM5MjI=', + avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/freearhey', + html_url: 'https://github.com/freearhey', + followers_url: 'https://api.github.com/users/freearhey/followers', + following_url: 'https://api.github.com/users/freearhey/following{/other_user}', + gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', + starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', + organizations_url: 'https://api.github.com/users/freearhey/orgs', + repos_url: 'https://api.github.com/users/freearhey/repos', + events_url: 'https://api.github.com/users/freearhey/events{/privacy}', + received_events_url: 'https://api.github.com/users/freearhey/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710795, + node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', + name: 'status:down', + color: 'df3a4a', + default: false, + description: "The guide doesn't work" + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 0, + created_at: '2023-12-02T12:16:22Z', + updated_at: '2024-12-27T05:00:36Z', + closed_at: null, + author_association: 'COLLABORATOR', + active_lock_reason: null, + body: '### Site\n\nvivacom.bg\n\n### Description\n\n```sh\r\nnpm run grab -- --site=vivacom.bg\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=vivacom.bg\r\n\r\nstarting...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: vivacom.bg\r\nloading channels...\r\n found 154 channel(s)\r\nrun #1:\r\n [1/308] vivacom.bg (bg) - 24Kitchen.bg - Dec 2, 2023 (0 programs)\r\n [2/308] vivacom.bg (bg) - 24Kitchen.bg - Dec 3, 2023 (0 programs)\r\n [3/308] vivacom.bg (bg) - 78TV.bg - Dec 3, 2023 (0 programs)\r\n [4/308] vivacom.bg (bg) - AlfaTV.bg - Dec 3, 2023 (0 programs)\r\n [5/308] vivacom.bg (bg) - AXNEurope.gr - Dec 3, 2023 (0 programs)\r\n [6/308] vivacom.bg (bg) - BNT3.bg - Dec 3, 2023 (0 programs)\r\n```', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2270/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2270/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2264', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2264/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2264/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2264/events', + html_url: 'https://github.com/iptv-org/epg/issues/2264', + id: 2019407629, + node_id: 'I_kwDOFLVvtM54XbcN', + number: 2264, + title: 'tva.tv is broken', + user: { + login: 'freearhey', + id: 7253922, + node_id: 'MDQ6VXNlcjcyNTM5MjI=', + avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/freearhey', + html_url: 'https://github.com/freearhey', + followers_url: 'https://api.github.com/users/freearhey/followers', + following_url: 'https://api.github.com/users/freearhey/following{/other_user}', + gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', + starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', + organizations_url: 'https://api.github.com/users/freearhey/orgs', + repos_url: 'https://api.github.com/users/freearhey/repos', + events_url: 'https://api.github.com/users/freearhey/events{/privacy}', + received_events_url: 'https://api.github.com/users/freearhey/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710795, + node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', + name: 'status:down', + color: 'df3a4a', + default: false, + description: "The guide doesn't work" + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 0, + created_at: '2023-11-30T20:00:18Z', + updated_at: '2024-12-27T05:00:24Z', + closed_at: null, + author_association: 'COLLABORATOR', + active_lock_reason: null, + body: '### Site\n\ntva.tv\n\n### Description\n\n```sh\r\nnpm run grab -- --site=tva.tv\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=tva.tv\r\n\r\nstaring...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: tva.tv\r\nloading channels...\r\n found 28 channel(s)\r\nrun #1:\r\n [1/56] tva.tv (fa) - DocTV.ir - Nov 30, 2023 (0 programs)\r\n ERR: Connection timeout\r\n [2/56] tva.tv (fa) - DocTV.ir - Dec 1, 2023 (0 programs)\r\n ERR: Connection timeout\r\n```', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2264/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2264/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2263', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2263/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2263/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2263/events', + html_url: 'https://github.com/iptv-org/epg/issues/2263', + id: 2019376043, + node_id: 'I_kwDOFLVvtM54XTur', + number: 2263, + title: 'tv.yettel.hu is broken', + user: { + login: 'freearhey', + id: 7253922, + node_id: 'MDQ6VXNlcjcyNTM5MjI=', + avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/freearhey', + html_url: 'https://github.com/freearhey', + followers_url: 'https://api.github.com/users/freearhey/followers', + following_url: 'https://api.github.com/users/freearhey/following{/other_user}', + gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', + starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', + organizations_url: 'https://api.github.com/users/freearhey/orgs', + repos_url: 'https://api.github.com/users/freearhey/repos', + events_url: 'https://api.github.com/users/freearhey/events{/privacy}', + received_events_url: 'https://api.github.com/users/freearhey/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710795, + node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', + name: 'status:down', + color: 'df3a4a', + default: false, + description: "The guide doesn't work" + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 0, + created_at: '2023-11-30T19:38:28Z', + updated_at: '2024-12-27T04:59:14Z', + closed_at: null, + author_association: 'COLLABORATOR', + active_lock_reason: null, + body: '### Site\n\ntv.yettel.hu\n\n### Description\n\n```sh\r\nnpm run grab -- --site=tv.yettel.hu\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=tv.yettel.hu\r\n\r\nstaring...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: tv.yettel.hu\r\nloading channels...\r\n found 74 channel(s)\r\nrun #1:\r\n [1/148] tv.yettel.hu (en) - BBCNewsEurope.uk - Nov 30, 2023 (0 programs)\r\n ERR: Client network socket disconnected before secure TLS connection was established\r\n [2/148] tv.yettel.hu (en) - BBCNewsEurope.uk - Dec 1, 2023 (0 programs)\r\n ERR: Client network socket disconnected before secure TLS connection was established\r\n```', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2263/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2263/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2257', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2257/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2257/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2257/events', + html_url: 'https://github.com/iptv-org/epg/issues/2257', + id: 2011054011, + node_id: 'I_kwDOFLVvtM533j-7', + number: 2257, + title: 'rtb.gov.bn is broken', + user: { + login: 'freearhey', + id: 7253922, + node_id: 'MDQ6VXNlcjcyNTM5MjI=', + avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/freearhey', + html_url: 'https://github.com/freearhey', + followers_url: 'https://api.github.com/users/freearhey/followers', + following_url: 'https://api.github.com/users/freearhey/following{/other_user}', + gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', + starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', + organizations_url: 'https://api.github.com/users/freearhey/orgs', + repos_url: 'https://api.github.com/users/freearhey/repos', + events_url: 'https://api.github.com/users/freearhey/events{/privacy}', + received_events_url: 'https://api.github.com/users/freearhey/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710795, + node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', + name: 'status:down', + color: 'df3a4a', + default: false, + description: "The guide doesn't work" + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 0, + created_at: '2023-11-26T14:02:13Z', + updated_at: '2024-12-27T04:59:29Z', + closed_at: null, + author_association: 'COLLABORATOR', + active_lock_reason: null, + body: '### Site\n\nrtb.gov.bn\n\n### Description\n\n```sh\r\nnpm run grab -- --site=rtb.gov.bn\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=rtb.gov.bn\r\n\r\nstaring...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: rtb.gov.bn\r\nloading channels...\r\n found 3 channel(s)\r\nrun #1:\r\n [1/6] rtb.gov.bn (ms) - RTBAneka.bn - Nov 26, 2023 (0 programs)\r\n ERR: read ECONNRESET\r\n [2/6] rtb.gov.bn (ms) - RTBAneka.bn - Nov 27, 2023 (0 programs)\r\n ERR: read ECONNRESET\r\n [3/6] rtb.gov.bn (ms) - RTBPerdana.bn - Nov 27, 2023 (0 programs)\r\n ERR: read ECONNRESET\r\n```\r\n\r\nThere is no link to the guide on the website at the moment either: https://www.rtb.gov.bn/SitePages/Programme%20Guide.aspx', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2257/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2257/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2255', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2255/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2255/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2255/events', + html_url: 'https://github.com/iptv-org/epg/issues/2255', + id: 2011043202, + node_id: 'I_kwDOFLVvtM533hWC', + number: 2255, + title: 'rev.bs is broken', + user: { + login: 'freearhey', + id: 7253922, + node_id: 'MDQ6VXNlcjcyNTM5MjI=', + avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/freearhey', + html_url: 'https://github.com/freearhey', + followers_url: 'https://api.github.com/users/freearhey/followers', + following_url: 'https://api.github.com/users/freearhey/following{/other_user}', + gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', + starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', + organizations_url: 'https://api.github.com/users/freearhey/orgs', + repos_url: 'https://api.github.com/users/freearhey/repos', + events_url: 'https://api.github.com/users/freearhey/events{/privacy}', + received_events_url: 'https://api.github.com/users/freearhey/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710795, + node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', + name: 'status:down', + color: 'df3a4a', + default: false, + description: "The guide doesn't work" + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 1, + created_at: '2023-11-26T13:31:22Z', + updated_at: '2024-12-27T22:02:28Z', + closed_at: null, + author_association: 'COLLABORATOR', + active_lock_reason: null, + body: '### Site\n\nrev.bs\n\n### Description\n\n```sh\r\nnpm run grab -- --site=rev.bs\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=rev.bs\r\n\r\nstaring...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: rev.bs\r\nloading channels...\r\n found 179 channel(s)\r\nrun #1:\r\n [1/358] rev.bs (en) - 3ABNEnglish.us - Nov 26, 2023 (0 programs)\r\n [2/358] rev.bs (en) - 3ABNEnglish.us - Nov 27, 2023 (0 programs)\r\n [3/358] rev.bs (en) - AEEast.us - Nov 27, 2023 (0 programs)\r\n [4/358] rev.bs (en) - AMCEast.us - Nov 27, 2023 (0 programs)\r\n```\r\n---\r\n\r\n```json\r\n{\r\n\t"status": "OK",\r\n\t"data": {\r\n\t\t"schedule": [],\r\n\t\t"dateTime": {\r\n\t\t\t"date": "2023-11-27",\r\n\t\t\t"page": 0\r\n\t\t},\r\n\t\t"runtimes": [\r\n\t\t\t{\r\n\t\t\t\t"Rev class loaded": 1700899490.604637\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\t"TVGuide Class Loaded": 1700899490.638891\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\t"Validated form": 1700899490.640116\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\t"Pulled schedules": 1700899490.642099\r\n\t\t\t}\r\n\t\t]\r\n\t}\r\n}\r\n```\r\n\r\nhttps://www.rev.bs/wp-content/uploads/tv-guide/2023-11-27_0.json\r\n\r\n---\r\n\r\nimage\r\n\r\nhttps://www.rev.bs/tv-guide-404/', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2255/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2255/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2241', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2241/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2241/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2241/events', + html_url: 'https://github.com/iptv-org/epg/issues/2241', + id: 2002096969, + node_id: 'I_kwDOFLVvtM53VZNJ', + number: 2241, + title: 'm.tv.sms.cz is broken', + user: { + login: 'freearhey', + id: 7253922, + node_id: 'MDQ6VXNlcjcyNTM5MjI=', + avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/freearhey', + html_url: 'https://github.com/freearhey', + followers_url: 'https://api.github.com/users/freearhey/followers', + following_url: 'https://api.github.com/users/freearhey/following{/other_user}', + gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', + starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', + organizations_url: 'https://api.github.com/users/freearhey/orgs', + repos_url: 'https://api.github.com/users/freearhey/repos', + events_url: 'https://api.github.com/users/freearhey/events{/privacy}', + received_events_url: 'https://api.github.com/users/freearhey/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710795, + node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', + name: 'status:down', + color: 'df3a4a', + default: false, + description: "The guide doesn't work" + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 4, + created_at: '2023-11-20T12:28:47Z', + updated_at: '2024-12-27T04:58:41Z', + closed_at: null, + author_association: 'COLLABORATOR', + active_lock_reason: null, + body: '### Site\n\nm.tv.sms.cz\n\n### Description\n\n```sh\r\nnpm run grab -- --site=m.tv.sms.cz\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=m.tv.sms.cz\r\n\r\nstaring...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: m.tv.sms.cz\r\nloading channels...\r\n found 525 channel(s)\r\nrun #1:\r\n [1/1050] m.tv.sms.cz (bs) - BNTV.ba - Nov 20, 2023 (0 programs)\r\n ERR: write EPROTO C04D101B01000000:error:0A000172:SSL routines:tls12_check_peer_sigalg:wrong signature type:ssl/t1_lib.c:1594:\r\n\r\n [2/1050] m.tv.sms.cz (bs) - BNTV.ba - Nov 21, 2023 (0 programs)\r\n ERR: write EPROTO C04D101B01000000:error:0A000172:SSL routines:tls12_check_peer_sigalg:wrong signature type:ssl/t1_lib.c:1594:\r\n\r\n [3/1050] m.tv.sms.cz (cs) - AMC.cz - Nov 21, 2023 (0 programs)\r\n ERR: write EPROTO C04D101B01000000:error:0A000172:SSL routines:tls12_check_peer_sigalg:wrong signature type:ssl/t1_lib.c:1594:\r\n```\r\n\r\nhttps://check-host.net/check-report/135d7e05kb9d', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2241/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2241/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2240', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2240/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2240/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2240/events', + html_url: 'https://github.com/iptv-org/epg/issues/2240', + id: 2001847449, + node_id: 'I_kwDOFLVvtM53UcSZ', + number: 2240, + title: 'kplus.vn is broken', + user: { + login: 'freearhey', + id: 7253922, + node_id: 'MDQ6VXNlcjcyNTM5MjI=', + avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/freearhey', + html_url: 'https://github.com/freearhey', + followers_url: 'https://api.github.com/users/freearhey/followers', + following_url: 'https://api.github.com/users/freearhey/following{/other_user}', + gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', + starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', + organizations_url: 'https://api.github.com/users/freearhey/orgs', + repos_url: 'https://api.github.com/users/freearhey/repos', + events_url: 'https://api.github.com/users/freearhey/events{/privacy}', + received_events_url: 'https://api.github.com/users/freearhey/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710795, + node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', + name: 'status:down', + color: 'df3a4a', + default: false, + description: "The guide doesn't work" + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 0, + created_at: '2023-11-20T10:19:23Z', + updated_at: '2024-12-27T04:57:49Z', + closed_at: null, + author_association: 'COLLABORATOR', + active_lock_reason: null, + body: '### Site\n\nkplus.vn\n\n### Description\n\n```sh\r\nnpm run grab -- --site=kplus.vn\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=kplus.vn\r\n\r\nstaring...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: kplus.vn\r\nloading channels...\r\n found 26 channel(s)\r\nrun #1:\r\n [1/52] kplus.vn (vi) - AsianFoodNetwork.sg - Nov 20, 2023 (0 programs)\r\n [2/52] kplus.vn (vi) - AsianFoodNetwork.sg - Nov 21, 2023 (0 programs)\r\n [3/52] kplus.vn (vi) - AXN.vn - Nov 21, 2023 (0 programs)\r\n [4/52] kplus.vn (vi) - DiscoveryChannelSoutheastAsia.sg - Nov 21, 2023 (0 programs)\r\n```\r\n\r\nThe site also currently does not display the guide:\r\nimage\r\nhttps://www.kplus.vn/highlights/broadcast-schedule', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2240/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2240/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2239', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2239/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2239/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2239/events', + html_url: 'https://github.com/iptv-org/epg/issues/2239', + id: 2000457187, + node_id: 'I_kwDOFLVvtM53PI3j', + number: 2239, + title: 'comteco.com.bo is broken', + user: { + login: 'freearhey', + id: 7253922, + node_id: 'MDQ6VXNlcjcyNTM5MjI=', + avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/freearhey', + html_url: 'https://github.com/freearhey', + followers_url: 'https://api.github.com/users/freearhey/followers', + following_url: 'https://api.github.com/users/freearhey/following{/other_user}', + gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', + starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', + organizations_url: 'https://api.github.com/users/freearhey/orgs', + repos_url: 'https://api.github.com/users/freearhey/repos', + events_url: 'https://api.github.com/users/freearhey/events{/privacy}', + received_events_url: 'https://api.github.com/users/freearhey/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710795, + node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', + name: 'status:down', + color: 'df3a4a', + default: false, + description: "The guide doesn't work" + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 3, + created_at: '2023-11-18T15:03:48Z', + updated_at: '2024-12-27T21:25:41Z', + closed_at: null, + author_association: 'COLLABORATOR', + active_lock_reason: null, + body: '### Site\n\ncomteco.com.bo\n\n### Description\n\n```sh\r\nnpm run grab -- --site=comteco.com.bo\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=comteco.com.bo\r\n\r\nstaring...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: comteco.com.bo\r\nloading channels...\r\n found 72 channel(s)\r\nrun #1:\r\n [1/144] comteco.com.bo (es) - AbyaYalaTV.bo - Nov 18, 2023 (0 programs)\r\n [2/144] comteco.com.bo (es) - AbyaYalaTV.bo - Nov 19, 2023 (0 programs)\r\n [3/144] comteco.com.bo (es) - AEPanregional.us - Nov 19, 2023 (0 programs)\r\n [4/144] comteco.com.bo (es) - AnimalPlanetLatinAmerica.us - Nov 19, 2023 (0 programs)\r\n [5/144] comteco.com.bo (es) - BoliviaTV72.bo - Nov 19, 2023 (0 programs)\r\n```\r\n\r\nhttps://comteco.com.bo/pages/canales-y-programacion-tv/paquete-oro/ABYA%20YALA\r\nimage', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2239/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2239/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2237', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2237/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2237/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2237/events', + html_url: 'https://github.com/iptv-org/epg/issues/2237', + id: 2000349577, + node_id: 'I_kwDOFLVvtM53OumJ', + number: 2237, + title: 'canalplus-haiti.com is broken', + user: { + login: 'freearhey', + id: 7253922, + node_id: 'MDQ6VXNlcjcyNTM5MjI=', + avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/freearhey', + html_url: 'https://github.com/freearhey', + followers_url: 'https://api.github.com/users/freearhey/followers', + following_url: 'https://api.github.com/users/freearhey/following{/other_user}', + gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', + starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', + organizations_url: 'https://api.github.com/users/freearhey/orgs', + repos_url: 'https://api.github.com/users/freearhey/repos', + events_url: 'https://api.github.com/users/freearhey/events{/privacy}', + received_events_url: 'https://api.github.com/users/freearhey/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710795, + node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', + name: 'status:down', + color: 'df3a4a', + default: false, + description: "The guide doesn't work" + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 0, + created_at: '2023-11-18T09:46:29Z', + updated_at: '2024-12-27T04:57:02Z', + closed_at: null, + author_association: 'COLLABORATOR', + active_lock_reason: null, + body: '### Site\n\ncanalplus-haiti.com\n\n### Description\n\n```sh\r\nnpm run grab -- --site=canalplus-haiti.com\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=canalplus-haiti.com\r\n\r\nstaring...\r\nconfig:\r\n output: guide.xml\r\n maxConnections: 1\r\n gzip: false\r\n site: canalplus-haiti.com\r\nloading channels...\r\n found 111 channel(s)\r\nrun #1:\r\n [1/222] canalplus-haiti.com (fr) - 13emeRue.fr - Nov 18, 2023 (0 programs)\r\n ERR: Request failed with status code 403\r\n [2/222] canalplus-haiti.com (fr) - 13emeRue.fr - Nov 19, 2023 (0 programs)\r\n ERR: Request failed with status code 403\r\n [3/222] canalplus-haiti.com (fr) - ZoukTV.mq - Nov 19, 2023 (0 programs)\r\n ERR: Request failed with status code 403\r\n [4/222] canalplus-haiti.com (fr) - ZoukTV.mq - Nov 18, 2023 (0 programs)\r\n ERR: Request failed with status code 403\r\n```\r\n\r\nhttps://check-host.net/check-report/134fc56bk333', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2237/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2237/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2173', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2173/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2173/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2173/events', + html_url: 'https://github.com/iptv-org/epg/issues/2173', + id: 1903824500, + node_id: 'I_kwDOFLVvtM5xeg50', + number: 2173, + title: 'hd-plus.de - 0 programs on all channels', + user: { + login: 'x011', + id: 4313821, + node_id: 'MDQ6VXNlcjQzMTM4MjE=', + avatar_url: 'https://avatars.githubusercontent.com/u/4313821?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/x011', + html_url: 'https://github.com/x011', + followers_url: 'https://api.github.com/users/x011/followers', + following_url: 'https://api.github.com/users/x011/following{/other_user}', + gists_url: 'https://api.github.com/users/x011/gists{/gist_id}', + starred_url: 'https://api.github.com/users/x011/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/x011/subscriptions', + organizations_url: 'https://api.github.com/users/x011/orgs', + repos_url: 'https://api.github.com/users/x011/repos', + events_url: 'https://api.github.com/users/x011/events{/privacy}', + received_events_url: 'https://api.github.com/users/x011/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710795, + node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', + name: 'status:down', + color: 'df3a4a', + default: false, + description: "The guide doesn't work" + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 1, + created_at: '2023-09-19T22:45:56Z', + updated_at: '2024-12-27T04:54:11Z', + closed_at: null, + author_association: 'NONE', + active_lock_reason: null, + body: '### Site\r\n\r\nhd-plus.de\r\n\r\n### Description\r\n\r\nAll channels on hd-plus.de have 0 programs:\r\n```\r\n[1/60] hd-plus.de - 123tv.de - Sep 19, 2023 (0 programs)\r\n[2/60] hd-plus.de - 123tv.de - Sep 20, 2023 (0 programs)\r\n[3/60] hd-plus.de - ZDF.de - Sep 20, 2023 (0 programs)\r\n[4/60] hd-plus.de - ZDF.de - Sep 19, 2023 (0 programs)\r\n...\r\n```', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2173/reactions', + total_count: 6, + '+1': 6, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2173/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2543', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2543/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2543/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2543/events', + html_url: 'https://github.com/iptv-org/epg/issues/2543', + id: 2759693796, + node_id: 'I_kwDOFLVvtM6kfZXk', + number: 2543, + title: 'flixed.io is downloding old programming', + user: { + login: 'SiWafer', + id: 10903014, + node_id: 'MDQ6VXNlcjEwOTAzMDE0', + avatar_url: 'https://avatars.githubusercontent.com/u/10903014?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/SiWafer', + html_url: 'https://github.com/SiWafer', + followers_url: 'https://api.github.com/users/SiWafer/followers', + following_url: 'https://api.github.com/users/SiWafer/following{/other_user}', + gists_url: 'https://api.github.com/users/SiWafer/gists{/gist_id}', + starred_url: 'https://api.github.com/users/SiWafer/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/SiWafer/subscriptions', + organizations_url: 'https://api.github.com/users/SiWafer/orgs', + repos_url: 'https://api.github.com/users/SiWafer/repos', + events_url: 'https://api.github.com/users/SiWafer/events{/privacy}', + received_events_url: 'https://api.github.com/users/SiWafer/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710318, + node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', + name: 'status:warning', + color: 'fbca06', + default: false, + description: 'The guide downloads, but contains errors' + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 0, + created_at: '2024-12-26T12:09:43Z', + updated_at: '2024-12-27T05:29:58Z', + closed_at: null, + author_association: 'NONE', + active_lock_reason: null, + body: '### Describe your issue\r\n\r\nJust ran and updated, and seems the output is loading old programming and it also is not complete\r\n\r\nExample is Bravo channel\r\n\r\n```\r\nepg@rpi:~/epg$ npm run grab --- --site=flixed.io --maxConnections=10 -o "flixed.xml"\r\n\r\n> grab\r\n> npx tsx scripts/commands/epg/grab.ts --site=flixed.io --maxConnections=10 -o flixed.xml\r\n\r\nstarting...\r\nconfig:\r\n output: flixed.xml\r\n maxConnections: 10\r\n gzip: false\r\n site: flixed.io\r\nloading channels...\r\n found 94 channel(s)\r\nrun #1:\r\n [1/94] flixed.io (en) - BravoEast.us - Dec 26, 2024 (2 programs)\r\n [2/94] flixed.io (en) - ComedyCentralEast.us - Dec 26, 2024 (5 programs)\r\n [3/94] flixed.io (en) - ACCNetwork.us - Dec 26, 2024 (26 programs)\r\n [4/94] flixed.io (en) - CNN.us - Dec 26, 2024 (25 programs)\r\n [5/94] flixed.io (en) - BloombergTV.us - Dec 26, 2024 (21 programs)\r\n [6/94] flixed.io (en) - AEEast.us - Dec 26, 2024 (6 programs)\r\n [7/94] flixed.io (en) - CNBC.us - Dec 26, 2024 (3 programs)\r\n [8/94] flixed.io (en) - CinemaxEast.us - Dec 26, 2024 (14 programs)\r\n [9/94] flixed.io (en) - AMCEast.us - Dec 26, 2024 (19 programs)\r\n [10/94] flixed.io (en) - BigTenNetwork.us - Dec 26, 2024 (3 programs)\r\n [11/94] flixed.io (en) - FoxWest.us - Dec 26, 2024 (8 programs)\r\n [12/94] flixed.io (en) - FreeformEast.us - Dec 26, 2024 (2 programs)\r\n [13/94] flixed.io (en) - FoxSports2.us - Dec 26, 2024 (2 programs)\r\n [14/94] flixed.io (en) - FoodNetworkEast.us - Dec 26, 2024 (3 programs)\r\n...\r\n...\r\n...\r\n\r\n```\r\nSnip from xml file out\r\n![image](https://github.com/user-attachments/assets/5ed6d7eb-df59-4951-9b2c-5214e24d24ac)\r\n\r\nThe date is September 26, 2024, 01:00:00 for Bravo\r\n\r\n', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2543/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2543/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2516', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2516/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2516/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2516/events', + html_url: 'https://github.com/iptv-org/epg/issues/2516', + id: 2745387354, + node_id: 'I_kwDOFLVvtM6jo0la', + number: 2516, + title: 'Duplicate programmes with sky.com grab', + user: { + login: 'misar1', + id: 69795879, + node_id: 'MDQ6VXNlcjY5Nzk1ODc5', + avatar_url: 'https://avatars.githubusercontent.com/u/69795879?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/misar1', + html_url: 'https://github.com/misar1', + followers_url: 'https://api.github.com/users/misar1/followers', + following_url: 'https://api.github.com/users/misar1/following{/other_user}', + gists_url: 'https://api.github.com/users/misar1/gists{/gist_id}', + starred_url: 'https://api.github.com/users/misar1/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/misar1/subscriptions', + organizations_url: 'https://api.github.com/users/misar1/orgs', + repos_url: 'https://api.github.com/users/misar1/repos', + events_url: 'https://api.github.com/users/misar1/events{/privacy}', + received_events_url: 'https://api.github.com/users/misar1/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710318, + node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', + name: 'status:warning', + color: 'fbca06', + default: false, + description: 'The guide downloads, but contains errors' + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 1, + created_at: '2024-12-17T16:19:36Z', + updated_at: '2024-12-27T05:27:44Z', + closed_at: null, + author_association: 'NONE', + active_lock_reason: null, + body: '### Site\n\nsky.com\n\n### Description\n\n\r\nI changed to the recently updated sky.com script a couple of days ago (using a completely new install as in the readme) and have found an anomaly. This does not occur with an identical grab using my previous installation. The grabs are for about 60 UK FTA channels which I can list if it would be helpful.\r\n\r\nA number of programmes have two identical blocks in the XML, including their start and finish times. This occurs only from around approximately 22:00 each day and up to about midnight GMT. During this period most channels are affected and it is consistent between successive grabs. In case of a timing issue I tested delays of 100, 1000, 3000 and 5000 msec but the XML was unchanged.', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2516/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2516/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2501', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2501/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2501/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2501/events', + html_url: 'https://github.com/iptv-org/epg/issues/2501', + id: 2738899005, + node_id: 'I_kwDOFLVvtM6jQEg9', + number: 2501, + title: 'Sky.com', + user: { + login: 'Chris230291', + id: 5328818, + node_id: 'MDQ6VXNlcjUzMjg4MTg=', + avatar_url: 'https://avatars.githubusercontent.com/u/5328818?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/Chris230291', + html_url: 'https://github.com/Chris230291', + followers_url: 'https://api.github.com/users/Chris230291/followers', + following_url: 'https://api.github.com/users/Chris230291/following{/other_user}', + gists_url: 'https://api.github.com/users/Chris230291/gists{/gist_id}', + starred_url: 'https://api.github.com/users/Chris230291/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/Chris230291/subscriptions', + organizations_url: 'https://api.github.com/users/Chris230291/orgs', + repos_url: 'https://api.github.com/users/Chris230291/repos', + events_url: 'https://api.github.com/users/Chris230291/events{/privacy}', + received_events_url: 'https://api.github.com/users/Chris230291/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710318, + node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', + name: 'status:warning', + color: 'fbca06', + default: false, + description: 'The guide downloads, but contains errors' + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 2, + created_at: '2024-12-13T17:45:02Z', + updated_at: '2024-12-27T05:25:49Z', + closed_at: null, + author_association: 'NONE', + active_lock_reason: null, + body: '### Site\n\nsky.com\n\n### Description\n\n```\r\n Viaplay 1 HD\r\n Viaplay 2 HD\r\n```\r\n\r\nThese should be "Premier Sports 1 HD" and "Premier Sports 2 HD".\r\nAlso, the UK and ROI have different schedules for these 2 channels.', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2501/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2501/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2446', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2446/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2446/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2446/events', + html_url: 'https://github.com/iptv-org/epg/issues/2446', + id: 2568624116, + node_id: 'I_kwDOFLVvtM6ZGhf0', + number: 2446, + title: 'Eleven channels returning 0 programs on meo.pt', + user: { + login: 'jonatasgz', + id: 78122211, + node_id: 'MDQ6VXNlcjc4MTIyMjEx', + avatar_url: 'https://avatars.githubusercontent.com/u/78122211?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/jonatasgz', + html_url: 'https://github.com/jonatasgz', + followers_url: 'https://api.github.com/users/jonatasgz/followers', + following_url: 'https://api.github.com/users/jonatasgz/following{/other_user}', + gists_url: 'https://api.github.com/users/jonatasgz/gists{/gist_id}', + starred_url: 'https://api.github.com/users/jonatasgz/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/jonatasgz/subscriptions', + organizations_url: 'https://api.github.com/users/jonatasgz/orgs', + repos_url: 'https://api.github.com/users/jonatasgz/repos', + events_url: 'https://api.github.com/users/jonatasgz/events{/privacy}', + received_events_url: 'https://api.github.com/users/jonatasgz/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710318, + node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', + name: 'status:warning', + color: 'fbca06', + default: false, + description: 'The guide downloads, but contains errors' + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 2, + created_at: '2024-10-06T12:00:48Z', + updated_at: '2024-12-27T05:23:16Z', + closed_at: null, + author_association: 'NONE', + active_lock_reason: null, + body: '### Site\n\nmeo.pt\n\n### Description\n\nEleven (DAZN) channels return 0 programs.', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2446/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2446/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2400', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2400/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2400/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2400/events', + html_url: 'https://github.com/iptv-org/epg/issues/2400', + id: 2370209498, + node_id: 'I_kwDOFLVvtM6NRoba', + number: 2400, + title: 'tvgids.nl', + user: { + login: 'freearhey', + id: 7253922, + node_id: 'MDQ6VXNlcjcyNTM5MjI=', + avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/freearhey', + html_url: 'https://github.com/freearhey', + followers_url: 'https://api.github.com/users/freearhey/followers', + following_url: 'https://api.github.com/users/freearhey/following{/other_user}', + gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', + starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', + organizations_url: 'https://api.github.com/users/freearhey/orgs', + repos_url: 'https://api.github.com/users/freearhey/repos', + events_url: 'https://api.github.com/users/freearhey/events{/privacy}', + received_events_url: 'https://api.github.com/users/freearhey/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710318, + node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', + name: 'status:warning', + color: 'fbca06', + default: false, + description: 'The guide downloads, but contains errors' + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 0, + created_at: '2024-06-24T13:05:05Z', + updated_at: '2024-12-27T05:19:25Z', + closed_at: null, + author_association: 'COLLABORATOR', + active_lock_reason: null, + body: "### Site\n\ntvgids.nl\n\n### Description\n\nTest failed:\r\n\r\n```sh\r\nnpm test -- tvgids.nl \r\n\r\n> test\r\n> run-script-os tvgids.nl\r\n\r\n\r\n> test:default\r\n> TZ=Pacific/Nauru npx jest --runInBand tvgids.nl\r\n\r\n FAIL sites/tvgids.nl/tvgids.nl.test.js\r\n ✓ can generate valid url (29 ms)\r\n ✕ can generate valid url for today (4 ms)\r\n ✓ can parse response (307 ms)\r\n ✓ can handle empty guide (3 ms)\r\n\r\n ● can generate valid url for today\r\n\r\n expect(received).toBe(expected) // Object.is equality\r\n\r\n Expected: \"https://www.tvgids.nl/gids/npo1\"\r\n Received: \"https://www.tvgids.nl/gids/25-06-2024/npo1\"\r\n\r\n 21 | const today = dayjs().startOf('d')\r\n 22 |\r\n > 23 | expect(url({ date: today, channel })).toBe('https://www.tvgids.nl/gids/npo1')\r\n | ^\r\n 24 | })\r\n 25 |\r\n 26 | it('can parse response', () => {\r\n\r\n at Object. (sites/tvgids.nl/tvgids.nl.test.js:23:41)\r\n\r\nTest Suites: 1 failed, 1 total\r\nTests: 1 failed, 3 passed, 4 total\r\nSnapshots: 0 total\r\nTime: 3.52 s\r\nRan all test suites matching /tvgids.nl/i.\r\n```", + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2400/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2400/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2399', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2399/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2399/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2399/events', + html_url: 'https://github.com/iptv-org/epg/issues/2399', + id: 2370207356, + node_id: 'I_kwDOFLVvtM6NRn58', + number: 2399, + title: 'tvprofil.com', + user: { + login: 'freearhey', + id: 7253922, + node_id: 'MDQ6VXNlcjcyNTM5MjI=', + avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/freearhey', + html_url: 'https://github.com/freearhey', + followers_url: 'https://api.github.com/users/freearhey/followers', + following_url: 'https://api.github.com/users/freearhey/following{/other_user}', + gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', + starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', + organizations_url: 'https://api.github.com/users/freearhey/orgs', + repos_url: 'https://api.github.com/users/freearhey/repos', + events_url: 'https://api.github.com/users/freearhey/events{/privacy}', + received_events_url: 'https://api.github.com/users/freearhey/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710318, + node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', + name: 'status:warning', + color: 'fbca06', + default: false, + description: 'The guide downloads, but contains errors' + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 1, + created_at: '2024-06-24T13:04:08Z', + updated_at: '2024-12-27T05:18:50Z', + closed_at: null, + author_association: 'COLLABORATOR', + active_lock_reason: null, + body: '### Site\n\ntvprofil.com\n\n### Description\n\nTest failed:\r\n\r\n```sh\r\nnpm test -- tvprofil.com\r\n\r\n> test\r\n> run-script-os tvprofil.com\r\n\r\n\r\n> test:default\r\n> TZ=Pacific/Nauru npx jest --runInBand tvprofil.com\r\n\r\n FAIL sites/tvprofil.com/tvprofil.com.test.js\r\n ✕ can generate valid url (14 ms)\r\n ✓ can generate valid request headers (1 ms)\r\n ✓ can parse response (76 ms)\r\n ✓ can handle empty guide (2 ms)\r\n\r\n ● can generate valid url\r\n\r\n expect(received).toBe(expected) // Object.is equality\r\n\r\n Expected: "https://tvprofil.com/bg/tv-programa/program/?datum=2023-01-12&kanal=24kitchen-bg&callback=cb&b51=818933"\r\n Received: "https://tvprofil.com/bg/tv-programa/program/?datum=2023-01-12&kanal=24kitchen-bg&callback=cb&b49=819461"\r\n\r\n 15 |\r\n 16 | it(\'can generate valid url\', () => {\r\n > 17 | expect(url({ channel, date })).toBe(\r\n | ^\r\n 18 | \'https://tvprofil.com/bg/tv-programa/program/?datum=2023-01-12&kanal=24kitchen-bg&callback=cb&b51=818933\'\r\n 19 | )\r\n 20 | })\r\n\r\n at Object. (sites/tvprofil.com/tvprofil.com.test.js:17:34)\r\n\r\nTest Suites: 1 failed, 1 total\r\nTests: 1 failed, 3 passed, 4 total\r\nSnapshots: 0 total\r\nTime: 3.307 s\r\nRan all test suites matching /tvprofil.com/i.\r\n```', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2399/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2399/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2396', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2396/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2396/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2396/events', + html_url: 'https://github.com/iptv-org/epg/issues/2396', + id: 2370189473, + node_id: 'I_kwDOFLVvtM6NRjih', + number: 2396, + title: 'iltalehti.fi', + user: { + login: 'freearhey', + id: 7253922, + node_id: 'MDQ6VXNlcjcyNTM5MjI=', + avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/freearhey', + html_url: 'https://github.com/freearhey', + followers_url: 'https://api.github.com/users/freearhey/followers', + following_url: 'https://api.github.com/users/freearhey/following{/other_user}', + gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', + starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', + organizations_url: 'https://api.github.com/users/freearhey/orgs', + repos_url: 'https://api.github.com/users/freearhey/repos', + events_url: 'https://api.github.com/users/freearhey/events{/privacy}', + received_events_url: 'https://api.github.com/users/freearhey/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710318, + node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', + name: 'status:warning', + color: 'fbca06', + default: false, + description: 'The guide downloads, but contains errors' + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 0, + created_at: '2024-06-24T12:56:45Z', + updated_at: '2024-12-27T05:18:16Z', + closed_at: null, + author_association: 'COLLABORATOR', + active_lock_reason: null, + body: '### Site\n\niltalehti.fi\n\n### Description\n\nTest failed:\r\n\r\n```sh\r\nnpm test -- iltalehti.fi \r\n\r\n> test\r\n> run-script-os iltalehti.fi\r\n\r\n\r\n> test:default\r\n> TZ=Pacific/Nauru npx jest --runInBand iltalehti.fi\r\n\r\n FAIL sites/iltalehti.fi/iltalehti.fi.test.js\r\n ✕ can generate valid url (10 ms)\r\n ✓ can parse response (28 ms)\r\n ✓ can handle empty guide (1 ms)\r\n\r\n ● can generate valid url\r\n\r\n expect(received).toBe(expected) // Object.is equality\r\n\r\n Expected: "https://telkku.com/api/channel-groups/default_builtin_channelgroup1/offering?startTime=00%3A00%3A00.000&duration=PT24H&inclusionPolicy=IncludeOngoingAlso&limit=1000&tvDate=2022-10-29&view=PublicationDetails"\r\n Received: "https://telkku.com/api/channel-groups/default_builtin_channelgroupdefault_builtin_channelgroup1/offering?startTime=00%3A00%3A00.000&duration=PT24H&inclusionPolicy=IncludeOngoingAlso&limit=1000&tvDate=2022-10-29&view=PublicationDetails"\r\n\r\n 15 |\r\n 16 | it(\'can generate valid url\', () => {\r\n > 17 | expect(url({ channel, date })).toBe(\r\n | ^\r\n 18 | \'https://telkku.com/api/channel-groups/default_builtin_channelgroup1/offering?startTime=00%3A00%3A00.000&duration=PT24H&inclusionPolicy=IncludeOngoingAlso&limit=1000&tvDate=2022-10-29&view=PublicationDetails\'\r\n 19 | )\r\n 20 | })\r\n\r\n at Object. (sites/iltalehti.fi/iltalehti.fi.test.js:17:34)\r\n\r\nTest Suites: 1 failed, 1 total\r\nTests: 1 failed, 2 passed, 3 total\r\nSnapshots: 0 total\r\nTime: 3.193 s\r\nRan all test suites matching /iltalehti.fi/i.\r\n```', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2396/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2396/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2395', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2395/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2395/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2395/events', + html_url: 'https://github.com/iptv-org/epg/issues/2395', + id: 2370186157, + node_id: 'I_kwDOFLVvtM6NRiut', + number: 2395, + title: 'chaines-tv.orange.fr', + user: { + login: 'freearhey', + id: 7253922, + node_id: 'MDQ6VXNlcjcyNTM5MjI=', + avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/freearhey', + html_url: 'https://github.com/freearhey', + followers_url: 'https://api.github.com/users/freearhey/followers', + following_url: 'https://api.github.com/users/freearhey/following{/other_user}', + gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', + starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', + organizations_url: 'https://api.github.com/users/freearhey/orgs', + repos_url: 'https://api.github.com/users/freearhey/repos', + events_url: 'https://api.github.com/users/freearhey/events{/privacy}', + received_events_url: 'https://api.github.com/users/freearhey/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710318, + node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', + name: 'status:warning', + color: 'fbca06', + default: false, + description: 'The guide downloads, but contains errors' + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 2, + created_at: '2024-06-24T12:55:21Z', + updated_at: '2024-12-27T05:17:25Z', + closed_at: null, + author_association: 'COLLABORATOR', + active_lock_reason: null, + body: '### Site\r\n\r\nchaines-tv.orange.fr\r\n\r\n### Description\r\n\r\nTest failed:\r\n\r\n```sh\r\nnpm test -- chaines-tv.orange.fr\r\n\r\n> test\r\n> run-script-os chaines-tv.orange.fr\r\n\r\n\r\n> test:default\r\n> TZ=Pacific/Nauru npx jest --runInBand chaines-tv.orange.fr\r\n\r\n FAIL sites/chaines-tv.orange.fr/chaines-tv.orange.fr.test.js\r\n ✓ can generate valid url (5 ms)\r\n ✕ can parse response (12 ms)\r\n ✓ can handle empty guide (1 ms)\r\n\r\n ● can parse response\r\n\r\n expect(received).toMatchObject(expected)\r\n\r\n - Expected - 1\r\n + Received + 1\r\n\r\n @@ -3,8 +3,8 @@\r\n "category": "Série Suspense",\r\n "description": "Un tueur en série prend un plaisir pervers à prévenir les autorités de Tallahassee avant chaque nouveau meurtre. Rossi apprend le décès d\'un de ses vieux amis.",\r\n "icon": "https://proxymedia.woopic.com/340/p/169_EMI_9697669.jpg",\r\n "start": "2021-11-07T23:35:00.000Z",\r\n "stop": "2021-11-08T00:20:00.000Z",\r\n - "title": "Tête de liste",\r\n + "title": "Esprits criminels",\r\n },\r\n ]\r\n\r\n 23 | it(\'can parse response\', () => {\r\n 24 | const result = parser({ date, channel, content })\r\n > 25 | expect(result).toMatchObject([\r\n | ^\r\n 26 | {\r\n 27 | start: \'2021-11-07T23:35:00.000Z\',\r\n 28 | stop: \'2021-11-08T00:20:00.000Z\',\r\n\r\n at Object. (sites/chaines-tv.orange.fr/chaines-tv.orange.fr.test.js:25:18)\r\n\r\nTest Suites: 1 failed, 1 total\r\nTests: 1 failed, 2 passed, 3 total\r\nSnapshots: 0 total\r\nTime: 4.003 s\r\nRan all test suites matching /chaines-tv.orange.fr/i.\r\n```', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2395/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2395/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2304', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2304/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2304/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2304/events', + html_url: 'https://github.com/iptv-org/epg/issues/2304', + id: 2064638324, + node_id: 'I_kwDOFLVvtM57D-F0', + number: 2304, + title: 'digiturk.com.tr Missing Channel Identifications', + user: { + login: 'UzunMuhalefet', + id: 80906858, + node_id: 'MDQ6VXNlcjgwOTA2ODU4', + avatar_url: 'https://avatars.githubusercontent.com/u/80906858?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/UzunMuhalefet', + html_url: 'https://github.com/UzunMuhalefet', + followers_url: 'https://api.github.com/users/UzunMuhalefet/followers', + following_url: 'https://api.github.com/users/UzunMuhalefet/following{/other_user}', + gists_url: 'https://api.github.com/users/UzunMuhalefet/gists{/gist_id}', + starred_url: 'https://api.github.com/users/UzunMuhalefet/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/UzunMuhalefet/subscriptions', + organizations_url: 'https://api.github.com/users/UzunMuhalefet/orgs', + repos_url: 'https://api.github.com/users/UzunMuhalefet/repos', + events_url: 'https://api.github.com/users/UzunMuhalefet/events{/privacy}', + received_events_url: 'https://api.github.com/users/UzunMuhalefet/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710318, + node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', + name: 'status:warning', + color: 'fbca06', + default: false, + description: 'The guide downloads, but contains errors' + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 0, + created_at: '2024-01-03T20:57:58Z', + updated_at: '2024-12-27T05:32:37Z', + closed_at: null, + author_association: 'NONE', + active_lock_reason: null, + body: '### Site\r\n\r\ndigiturk.com.tr\r\n\r\n### Describe your issue\r\n\r\nThe following channels can be corrected as follows. (I added the TVG-IDs if they exist in https://iptv-org.github.io/)\r\n\r\n```\r\nKANAL 24 HD\r\nTARIH TV HD\r\nSÖZCÜ TV HD\r\n```\r\n\r\nLet me clarify the cases:\r\n- SZC changed its name to Sözcü TV\r\n- 24 TV often called as Kanal (Channel) 24\r\n- Tarih TV is straightforward.\r\n', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2304/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2304/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2303', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2303/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2303/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2303/events', + html_url: 'https://github.com/iptv-org/epg/issues/2303', + id: 2064633657, + node_id: 'I_kwDOFLVvtM57D885', + number: 2303, + title: 'dsmart.com.tr Channel Identifications', + user: { + login: 'UzunMuhalefet', + id: 80906858, + node_id: 'MDQ6VXNlcjgwOTA2ODU4', + avatar_url: 'https://avatars.githubusercontent.com/u/80906858?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/UzunMuhalefet', + html_url: 'https://github.com/UzunMuhalefet', + followers_url: 'https://api.github.com/users/UzunMuhalefet/followers', + following_url: 'https://api.github.com/users/UzunMuhalefet/following{/other_user}', + gists_url: 'https://api.github.com/users/UzunMuhalefet/gists{/gist_id}', + starred_url: 'https://api.github.com/users/UzunMuhalefet/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/UzunMuhalefet/subscriptions', + organizations_url: 'https://api.github.com/users/UzunMuhalefet/orgs', + repos_url: 'https://api.github.com/users/UzunMuhalefet/repos', + events_url: 'https://api.github.com/users/UzunMuhalefet/events{/privacy}', + received_events_url: 'https://api.github.com/users/UzunMuhalefet/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710318, + node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', + name: 'status:warning', + color: 'fbca06', + default: false, + description: 'The guide downloads, but contains errors' + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 1, + created_at: '2024-01-03T20:53:34Z', + updated_at: '2024-12-27T05:44:16Z', + closed_at: null, + author_association: 'NONE', + active_lock_reason: null, + body: '### Describe your issue\r\n\r\nThe following channels can be corrected as follows. (I added the TVG-IDs if they exist in https://iptv-org.github.io/)\r\n\r\n```\r\nNR1\r\nNR1 Türk\r\nTRT EBA TV İlkokul\r\nTRT EBA TV Ortaokul\r\nTRT EBA TV Lise\r\nÇifçi TV\r\nKanal 16\r\nFlash Haber\r\nYeni Kocaeli TV\r\nATV Turkiye SD\r\nTRT EBA TV İlkokul SD\r\nTRT EBA TV Ortaokul SD\r\nTRT EBA TV Lise SD\r\nTarih Tv\r\nSZC\r\nTRT 3 Spor\r\n``` \r\n\r\nLet me clarify the cases: \r\n\r\n- NR1 stands for Number One\r\n- Cifci TV - Çiftçi TV - misspelling\r\n- The number 16 is spelled as onaltı (on -> 10, altı->6, on6 -> 16) in Turkish so on6 - Kanal 16\r\n- Flash TV changed its name to Flash Haber TV\r\n- SZC changed its name to Sözcü TV\r\n- TRT 3 is a strange channel that usually streams the same content as TRT Spor but when there is a Turkish parliament event it switches to that event. So, the channel is both called TRT 3 and TRT 3 Spor.\r\n', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2303/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2303/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2272', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2272/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2272/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2272/events', + html_url: 'https://github.com/iptv-org/epg/issues/2272', + id: 2022135766, + node_id: 'I_kwDOFLVvtM54h1fW', + number: 2272, + title: 'Film titles with numbers only in the title (as in "1900" or "1917") don\'t show up', + user: { + login: 'GlamoramaAttack', + id: 116585465, + node_id: 'U_kgDOBvLz-Q', + avatar_url: 'https://avatars.githubusercontent.com/u/116585465?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/GlamoramaAttack', + html_url: 'https://github.com/GlamoramaAttack', + followers_url: 'https://api.github.com/users/GlamoramaAttack/followers', + following_url: 'https://api.github.com/users/GlamoramaAttack/following{/other_user}', + gists_url: 'https://api.github.com/users/GlamoramaAttack/gists{/gist_id}', + starred_url: 'https://api.github.com/users/GlamoramaAttack/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/GlamoramaAttack/subscriptions', + organizations_url: 'https://api.github.com/users/GlamoramaAttack/orgs', + repos_url: 'https://api.github.com/users/GlamoramaAttack/repos', + events_url: 'https://api.github.com/users/GlamoramaAttack/events{/privacy}', + received_events_url: 'https://api.github.com/users/GlamoramaAttack/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710318, + node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', + name: 'status:warning', + color: 'fbca06', + default: false, + description: 'The guide downloads, but contains errors' + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 2, + created_at: '2023-12-02T17:29:50Z', + updated_at: '2024-12-27T05:02:05Z', + closed_at: null, + author_association: 'NONE', + active_lock_reason: null, + body: '### Site\r\n\r\ntvpassport.com\r\n\r\n### Describe your issue\r\n\r\nFilm titles with numbers only in the title -as in "1900" or "1917"- don\'t show up at all.\r\n\r\nSorry, I don\'t understand anything about how to write a script or fix it but I see there is notable difference in the **guide.xml** file - the examples are excerpts from tvpassport.com. This isn\'t a new bug I think, it\'s just that I thought for a long time it\'s a Kodi problem before looking into the guide.xml file...\r\n\r\n```\r\nchannel="Showtime2East.us">The Caine Mutiny Court-MartialA \r\nchannel="Showtime2East.us">Two British soldiers receive seemingly impossible orders \r\nchannel="Showtime2East.us">There Will Be BloodDaniel \r\n```\r\nor:\r\n\r\n```\r\nchannel="MGMPlusEast.us">A Fistful of DynamiteA thief \r\nchannel="MGMPlusEast.us">The son (Robert De Niro) of a landowner and the son \r\nchannel="MGMPlusEast.us">Indecent ProposalA Las Vegas \r\n```\r\n\r\nFor some reason "title lang" gets replaced by "desc lang" and the film title is nowhere displayed.\r\n\r\nOn Kodi it looks like this for the films and you can also see it\'s not a problem if the film title includes more than numbers (see "88 Minutes" or "Resistance: 1942"). But titles as "1900", "1917" or "1408" (Horror with John Cusack) appear blank.\r\n\r\n![NoTitleWithNumbers](https://github.com/iptv-org/epg/assets/116585465/155f98f8-8529-4c42-af3c-def1edcf5651)\r\n\r\nI\'m not sure whether the problem also exists with other sources than tvpassport.com because I use only two other (German language) EPG sources that have currently no film titles with numbers only. But I assume it\'s a bug if it\'s not reduced to tvpassport.', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2272/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2272/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2176', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/2176/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/2176/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/2176/events', + html_url: 'https://github.com/iptv-org/epg/issues/2176', + id: 1915947653, + node_id: 'I_kwDOFLVvtM5yMwqF', + number: 2176, + title: 'tvtv', + user: { + login: 'jvdillon', + id: 1137078, + node_id: 'MDQ6VXNlcjExMzcwNzg=', + avatar_url: 'https://avatars.githubusercontent.com/u/1137078?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/jvdillon', + html_url: 'https://github.com/jvdillon', + followers_url: 'https://api.github.com/users/jvdillon/followers', + following_url: 'https://api.github.com/users/jvdillon/following{/other_user}', + gists_url: 'https://api.github.com/users/jvdillon/gists{/gist_id}', + starred_url: 'https://api.github.com/users/jvdillon/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/jvdillon/subscriptions', + organizations_url: 'https://api.github.com/users/jvdillon/orgs', + repos_url: 'https://api.github.com/users/jvdillon/repos', + events_url: 'https://api.github.com/users/jvdillon/events{/privacy}', + received_events_url: 'https://api.github.com/users/jvdillon/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710318, + node_id: 'LA_kwDOFLVvtM8AAAAB2NONrg', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:warning', + name: 'status:warning', + color: 'fbca06', + default: false, + description: 'The guide downloads, but contains errors' + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 6, + created_at: '2023-09-27T16:34:00Z', + updated_at: '2024-12-27T04:55:00Z', + closed_at: null, + author_association: 'NONE', + active_lock_reason: null, + body: '### Site\r\n\r\ntvtv.us\r\n\r\n### Description\r\n\r\nStarts getting error 429 after several 100 scrapes. I tried setting the delay to 1000ms, 1500ms, and 2000ms but keep seeing the issue.\r\n\r\nIt almost appears to happen after a sequence of 0 program scrapes. I wonder if the delay is not happening when nothing is downloaded which causes tvtv.us to throttle which triggers 0 programs downloaded. (Ie a feedback loop)', + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/2176/reactions', + total_count: 0, + '+1': 0, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/2176/timeline', + performed_via_github_app: null, + state_reason: null + }, + { + url: 'https://api.github.com/repos/iptv-org/epg/issues/12547', + repository_url: 'https://api.github.com/repos/iptv-org/epg', + labels_url: 'https://api.github.com/repos/iptv-org/epg/issues/12547/labels{/name}', + comments_url: 'https://api.github.com/repos/iptv-org/epg/issues/12547/comments', + events_url: 'https://api.github.com/repos/iptv-org/epg/issues/12547/events', + html_url: 'https://github.com/iptv-org/epg/issues/12547', + id: 3960418979, + node_id: 'I_kwDOFLVvtM6kiKaj', + number: 12547, + title: 'digiturk.com.tr', + user: { + login: 'freearhey', + id: 7253922, + node_id: 'MDQ6VXNlcjcyNTM5MjI=', + avatar_url: 'https://avatars.githubusercontent.com/u/7253922?v=4', + gravatar_id: '', + url: 'https://api.github.com/users/freearhey', + html_url: 'https://github.com/freearhey', + followers_url: 'https://api.github.com/users/freearhey/followers', + following_url: 'https://api.github.com/users/freearhey/following{/other_user}', + gists_url: 'https://api.github.com/users/freearhey/gists{/gist_id}', + starred_url: 'https://api.github.com/users/freearhey/starred{/owner}{/repo}', + subscriptions_url: 'https://api.github.com/users/freearhey/subscriptions', + organizations_url: 'https://api.github.com/users/freearhey/orgs', + repos_url: 'https://api.github.com/users/freearhey/repos', + events_url: 'https://api.github.com/users/freearhey/events{/privacy}', + received_events_url: 'https://api.github.com/users/freearhey/received_events', + type: 'User', + user_view_type: 'public', + site_admin: false + }, + labels: [ + { + id: 4542348869, + node_id: 'LA_kwDOFLVvtM8AAAABDr6-RQ', + url: 'https://api.github.com/repos/iptv-org/epg/labels/broken%20guide', + name: 'broken guide', + color: 'eaeaea', + default: false, + description: "There's a problem with the guide" + }, + { + id: 7932710795, + node_id: 'LA_kwDOFLVvtM8AAAAB2NOPiw', + url: 'https://api.github.com/repos/iptv-org/epg/labels/status:down', + name: 'status:down', + color: 'df3a4a', + default: false, + description: "The guide doesn't work" + } + ], + state: 'open', + locked: false, + assignee: null, + assignees: [], + milestone: null, + comments: 1, + created_at: '2024-12-27T05:33:47Z', + updated_at: '2024-12-28T20:37:53Z', + closed_at: null, + author_association: 'COLLABORATOR', + active_lock_reason: null, + closed_by: null, + reactions: { + url: 'https://api.github.com/repos/iptv-org/epg/issues/12547/reactions', + total_count: 1, + '+1': 1, + '-1': 0, + laugh: 0, + hooray: 0, + confused: 0, + heart: 0, + rocket: 0, + eyes: 0 + }, + timeline_url: 'https://api.github.com/repos/iptv-org/epg/issues/12547/timeline', + performed_via_github_app: null, + state_reason: null + } +] diff --git a/tests/commands/channels/edit.test.ts b/tests/commands/channels/edit.test.ts index b150648a..a232af48 100644 --- a/tests/commands/channels/edit.test.ts +++ b/tests/commands/channels/edit.test.ts @@ -1,36 +1,36 @@ -import { execSync } from 'child_process' -import fs from 'fs-extra' - -const ENV_VAR = 'cross-env DATA_DIR=tests/__data__/input/__data__' - -beforeEach(() => { - fs.emptyDirSync('tests/__data__/output') - fs.copySync( - 'tests/__data__/input/channels_edit/example.com.channels.xml', - 'tests/__data__/output/channels.xml' - ) -}) - -describe('channels:edit', () => { - it('shows list of options for a channel', () => { - const cmd = `${ENV_VAR} npm run channels:edit --- tests/__data__/output/channels.xml` - try { - const stdout = execSync(cmd, { encoding: 'utf8' }) - if (process.env.DEBUG === 'true') console.log(cmd, stdout) - checkStdout(stdout) - } catch (error: unknown) { - // NOTE: for Windows only - if (process.env.DEBUG === 'true') console.log(cmd, error) - if (error && typeof error === 'object' && 'stdout' in error) { - checkStdout(error.stdout as string) - } - } - }) -}) - -function checkStdout(stdout: string) { - expect(stdout).toContain('CNNInternational.us (CNN International, CNN, CNN Int)') - expect(stdout).toContain('Type...') - expect(stdout).toContain('Skip') - expect(stdout).toContain("File 'tests/__data__/output/channels.xml' successfully saved") -} +import { execSync } from 'child_process' +import fs from 'fs-extra' + +const ENV_VAR = 'cross-env DATA_DIR=tests/__data__/input/data' + +beforeEach(() => { + fs.emptyDirSync('tests/__data__/output') + fs.copySync( + 'tests/__data__/input/channels_edit/example.com.channels.xml', + 'tests/__data__/output/channels.xml' + ) +}) + +describe('channels:edit', () => { + it('shows list of options for a channel', () => { + const cmd = `${ENV_VAR} npm run channels:edit --- tests/__data__/output/channels.xml` + try { + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + checkStdout(stdout) + } catch (error: unknown) { + // NOTE: for Windows only + if (process.env.DEBUG === 'true') console.log(cmd, error) + if (error && typeof error === 'object' && 'stdout' in error) { + checkStdout(error.stdout as string) + } + } + }) +}) + +function checkStdout(stdout: string) { + expect(stdout).toContain('CNNInternational.us (CNN International, CNN, CNN Int)') + expect(stdout).toContain('Type...') + expect(stdout).toContain('Skip') + expect(stdout).toContain("File 'tests/__data__/output/channels.xml' successfully saved") +} diff --git a/tests/commands/channels/format.test.ts b/tests/commands/channels/format.test.ts new file mode 100644 index 00000000..503d2a60 --- /dev/null +++ b/tests/commands/channels/format.test.ts @@ -0,0 +1,29 @@ +import { execSync } from 'child_process' +import { pathToFileURL } from 'node:url' +import fs from 'fs-extra' + +beforeEach(() => { + fs.emptyDirSync('tests/__data__/output') + fs.copySync( + 'tests/__data__/input/channels_format/example.com.channels.xml', + 'tests/__data__/output/example.com.channels.xml' + ) +}) + +describe('channels:format', () => { + it('can format *.channels.xml files', () => { + const cmd = 'npm run channels:format --- tests/__data__/output/example.com.channels.xml' + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + + expect(content('tests/__data__/output/example.com.channels.xml')).toEqual( + content('tests/__data__/expected/channels_format/example.com.channels.xml') + ) + }) +}) + +function content(filepath: string) { + return fs.readFileSync(pathToFileURL(filepath), { + encoding: 'utf8' + }) +} diff --git a/tests/commands/channels/validate.test.ts b/tests/commands/channels/validate.test.ts index e3dc123a..bdf2a4ff 100644 --- a/tests/commands/channels/validate.test.ts +++ b/tests/commands/channels/validate.test.ts @@ -1,58 +1,58 @@ -import { execSync } from 'child_process' - -interface ExecError { - status: number - stdout: string -} - -const ENV_VAR = 'cross-env DATA_DIR=tests/__data__/input/__data__' - -describe('channels:validate', () => { - it('will show a message if the file contains a duplicate', () => { - try { - const cmd = `${ENV_VAR} npm run channels:validate --- tests/__data__/input/channels_validate/duplicate.channels.xml` - const stdout = execSync(cmd, { encoding: 'utf8' }) - if (process.env.DEBUG === 'true') console.log(cmd, stdout) - process.exit(1) - } catch (error) { - expect((error as ExecError).status).toBe(1) - expect((error as ExecError).stdout).toContain(` -┌─────────┬─────────────┬──────┬─────────────────┬─────────┬─────────┐ -│ (index) │ type │ lang │ xmltv_id │ site_id │ name │ -├─────────┼─────────────┼──────┼─────────────────┼─────────┼─────────┤ -│ 0 │ 'duplicate' │ 'en' │ 'Bravo.us@East' │ '140' │ 'Bravo' │ -└─────────┴─────────────┴──────┴─────────────────┴─────────┴─────────┘ - -1 problems (1 errors, 0 warnings) in 1 file(s) -`) - } - }) - - it('will show a message if the file contains a channel with wrong channel id', () => { - const cmd = `${ENV_VAR} npm run channels:validate --- tests/__data__/input/channels_validate/wrong_channel_id.channels.xml` - const stdout = execSync(cmd, { encoding: 'utf8' }) - expect(stdout).toContain(` -┌─────────┬────────────────────┬──────┬────────────────────┬─────────┬─────────────────────┐ -│ (index) │ type │ lang │ xmltv_id │ site_id │ name │ -├─────────┼────────────────────┼──────┼────────────────────┼─────────┼─────────────────────┤ -│ 0 │ 'wrong_channel_id' │ 'en' │ 'CNNInternational' │ '140' │ 'CNN International' │ -└─────────┴────────────────────┴──────┴────────────────────┴─────────┴─────────────────────┘ - -1 problems (0 errors, 1 warnings) in 1 file(s) -`) - }) - - it('will show a message if the file contains a channel with wrong feed id', () => { - const cmd = `${ENV_VAR} npm run channels:validate --- tests/__data__/input/channels_validate/wrong_feed_id.channels.xml` - const stdout = execSync(cmd, { encoding: 'utf8' }) - expect(stdout).toContain(` -┌─────────┬─────────────────┬──────┬─────────────────┬─────────┬─────────┐ -│ (index) │ type │ lang │ xmltv_id │ site_id │ name │ -├─────────┼─────────────────┼──────┼─────────────────┼─────────┼─────────┤ -│ 0 │ 'wrong_feed_id' │ 'en' │ 'Bravo.us@West' │ '150' │ 'Bravo' │ -└─────────┴─────────────────┴──────┴─────────────────┴─────────┴─────────┘ - -1 problems (0 errors, 1 warnings) in 1 file(s) -`) - }) -}) +import { execSync } from 'child_process' + +interface ExecError { + status: number + stdout: string +} + +const ENV_VAR = 'cross-env DATA_DIR=tests/__data__/input/data' + +describe('channels:validate', () => { + it('will show a message if the file contains a duplicate', () => { + try { + const cmd = `${ENV_VAR} npm run channels:validate --- tests/__data__/input/channels_validate/duplicate.channels.xml` + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + process.exit(1) + } catch (error) { + expect((error as ExecError).status).toBe(1) + expect((error as ExecError).stdout).toContain(` +┌─────────┬─────────────┬──────┬─────────────────┬─────────┬─────────┐ +│ (index) │ type │ lang │ xmltv_id │ site_id │ name │ +├─────────┼─────────────┼──────┼─────────────────┼─────────┼─────────┤ +│ 0 │ 'duplicate' │ 'en' │ 'Bravo.us@East' │ '140' │ 'Bravo' │ +└─────────┴─────────────┴──────┴─────────────────┴─────────┴─────────┘ + +1 problems (1 errors, 0 warnings) in 1 file(s) +`) + } + }) + + it('will show a message if the file contains a channel with wrong channel id', () => { + const cmd = `${ENV_VAR} npm run channels:validate --- tests/__data__/input/channels_validate/wrong_channel_id.channels.xml` + const stdout = execSync(cmd, { encoding: 'utf8' }) + expect(stdout).toContain(` +┌─────────┬────────────────────┬──────┬────────────────────┬─────────┬─────────────────────┐ +│ (index) │ type │ lang │ xmltv_id │ site_id │ name │ +├─────────┼────────────────────┼──────┼────────────────────┼─────────┼─────────────────────┤ +│ 0 │ 'wrong_channel_id' │ 'en' │ 'CNNInternational' │ '140' │ 'CNN International' │ +└─────────┴────────────────────┴──────┴────────────────────┴─────────┴─────────────────────┘ + +1 problems (0 errors, 1 warnings) in 1 file(s) +`) + }) + + it('will show a message if the file contains a channel with wrong feed id', () => { + const cmd = `${ENV_VAR} npm run channels:validate --- tests/__data__/input/channels_validate/wrong_feed_id.channels.xml` + const stdout = execSync(cmd, { encoding: 'utf8' }) + expect(stdout).toContain(` +┌─────────┬─────────────────┬──────┬─────────────────┬─────────┬─────────┐ +│ (index) │ type │ lang │ xmltv_id │ site_id │ name │ +├─────────┼─────────────────┼──────┼─────────────────┼─────────┼─────────┤ +│ 0 │ 'wrong_feed_id' │ 'en' │ 'Bravo.us@West' │ '150' │ 'Bravo' │ +└─────────┴─────────────────┴──────┴─────────────────┴─────────┴─────────┘ + +1 problems (0 errors, 1 warnings) in 1 file(s) +`) + }) +}) diff --git a/tests/commands/epg/grab.test.ts b/tests/commands/epg/grab.test.ts index 37d35da1..afb3ddf0 100644 --- a/tests/commands/epg/grab.test.ts +++ b/tests/commands/epg/grab.test.ts @@ -1,162 +1,114 @@ -import { pathToFileURL } from 'node:url' -import { execSync } from 'child_process' -import { Zip } from '@freearhey/core' -import fs from 'fs-extra' -import path from 'path' - -const ENV_VAR = - 'cross-env SITES_DIR=tests/__data__/input/epg_grab/sites CURR_DATE=2022-10-20 DATA_DIR=tests/__data__/input/__data__' - -beforeEach(() => { - fs.emptyDirSync('tests/__data__/output') -}) - -describe('epg:grab', () => { - it('can grab epg by site name', () => { - const cmd = `${ENV_VAR} npm run grab --- --site=example.com --output="${path.resolve( - 'tests/__data__/output/guide.xml' - )}" --timeout=100` - const stdout = execSync(cmd, { encoding: 'utf8' }) - if (process.env.DEBUG === 'true') console.log(cmd, stdout) - - expect(content('tests/__data__/output/guide.xml')).toEqual( - content('tests/__data__/expected/epg_grab/base.guide.xml') - ) - }) - - it('it will raise an error if the timeout is exceeded', () => { - const cmd = `${ENV_VAR} npm run grab --- --channels=tests/__data__/input/epg_grab/custom.channels.xml --output=tests/__data__/output/guide.xml --timeout=0` - let errorThrown = false - try { - execSync(cmd, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }) - // If no error is thrown, explicitly fail the test - fail('Expected command to throw an error due to timeout, but it did not.') - } catch (error) { - errorThrown = true - if (process.env.DEBUG === 'true') { - const stderr = error.stderr?.toString() || '' - const stdout = error.stdout?.toString() || '' - const combined = stderr + stdout - console.log('stdout:', stdout) - console.log('stderr:', stderr) - console.log('combined:', combined) - console.log('exit code:', error.exitCode) - console.log('Error output:', combined) - } - } - expect(errorThrown).toBe(true) - }) - - it('can grab epg with wildcard as output', () => { - const cmd = `${ENV_VAR} npm run grab --- --channels="tests/__data__/input/epg_grab/sites/example.com/example.com.channels.xml" --output="tests/__data__/output/guides/{lang}/{site}.xml" --timeout=100` - const stdout = execSync(cmd, { encoding: 'utf8' }) - if (process.env.DEBUG === 'true') console.log(cmd, stdout) - - expect(content('tests/__data__/output/guides/en/example.com.xml')).toEqual( - content('tests/__data__/expected/epg_grab/guides/en/example.com.xml') - ) - - expect(content('tests/__data__/output/guides/fr/example.com.xml')).toEqual( - content('tests/__data__/expected/epg_grab/guides/fr/example.com.xml') - ) - }) - - it('can grab epg then language filter enabled', () => { - const cmd = `${ENV_VAR} npm run grab --- --channels=tests/__data__/input/epg_grab/sites/example.com/example.com.channels.xml --output=tests/__data__/output/guides/{lang}/{site}.xml --lang=fr --timeout=100` - const stdout = execSync(cmd, { encoding: 'utf8' }) - if (process.env.DEBUG === 'true') console.log(cmd, stdout) - - expect(content('tests/__data__/output/guides/fr/example.com.xml')).toEqual( - content('tests/__data__/expected/epg_grab/guides/fr/example.com.xml') - ) - }) - - it('can grab epg then using a multi-language filter', () => { - const cmd = `${ENV_VAR} npm run grab --- --channels=tests/__data__/input/epg_grab/example.com/example.com.channels.xml --output=tests/__data__/output/guides/{site}.xml --lang=fr,it --timeout=100` - const stdout = execSync(cmd, { encoding: 'utf8' }) - if (process.env.DEBUG === 'true') console.log(cmd, stdout) - - expect(content('tests/__data__/output/guides/example.com.xml')).toEqual( - content('tests/__data__/expected/epg_grab/lang.guide.xml') - ) - }) - - it('can grab epg via https proxy', () => { - const cmd = `${ENV_VAR} npm run grab --- --site=example.com --proxy=https://bob:123456@proxy.com:1234 --output="${path.resolve( - 'tests/__data__/output/guide.xml' - )}" --timeout=100` - const stdout = execSync(cmd, { encoding: 'utf8' }) - if (process.env.DEBUG === 'true') console.log(cmd, stdout) - - expect(content('tests/__data__/output/guide.xml')).toEqual( - content('tests/__data__/expected/epg_grab/proxy.guide.xml') - ) - }) - - it('can grab epg via socks5 proxy', () => { - const cmd = `${ENV_VAR} npm run grab --- --site=example.com --proxy=socks5://bob:123456@proxy.com:1234 --output="${path.resolve( - 'tests/__data__/output/guide.xml' - )}" --timeout=100` - const stdout = execSync(cmd, { encoding: 'utf8' }) - if (process.env.DEBUG === 'true') console.log(cmd, stdout) - - expect(content('tests/__data__/output/guide.xml')).toEqual( - content('tests/__data__/expected/epg_grab/proxy.guide.xml') - ) - }) - - it('can grab epg with curl option', () => { - const cmd = `${ENV_VAR} npm run grab --- --site=example.com --curl --output="${path.resolve( - 'tests/__data__/output/guide.xml' - )}" --timeout=100` - const stdout = execSync(cmd, { encoding: 'utf8' }) - if (process.env.DEBUG === 'true') console.log(cmd, stdout) - - expect(stdout).toContain('curl https://example.com') - }) - - it('can grab epg with multiple channels.xml files', () => { - const cmd = `${ENV_VAR} npm run grab --- --channels=tests/__data__/input/epg_grab/sites/**/*.channels.xml --output=tests/__data__/output/guide.xml --timeout=100` - const stdout = execSync(cmd, { encoding: 'utf8' }) - if (process.env.DEBUG === 'true') console.log(cmd, stdout) - - expect(content('tests/__data__/output/guide.xml')).toEqual( - content('tests/__data__/expected/epg_grab/template.guide.xml') - ) - }) - - it('can grab epg using custom channels list', () => { - const cmd = `${ENV_VAR} npm run grab --- --channels=tests/__data__/input/epg_grab/custom.channels.xml --output=tests/__data__/output/guide.xml --timeout=100` - const stdout = execSync(cmd, { encoding: 'utf8' }) - if (process.env.DEBUG === 'true') console.log(cmd, stdout) - - expect(content('tests/__data__/output/guide.xml')).toEqual( - content('tests/__data__/expected/epg_grab/custom_channels.guide.xml') - ) - }) - - it('can grab epg with gzip option enabled', () => { - const cmd = `${ENV_VAR} npm run grab --- --channels=tests/__data__/input/epg_grab/sites/**/*.channels.xml --output="${path.resolve( - 'tests/__data__/output/guide.xml' - )}" --gzip --timeout=100` - const stdout = execSync(cmd, { encoding: 'utf8' }) - if (process.env.DEBUG === 'true') console.log(cmd, stdout) - - expect(content('tests/__data__/output/guide.xml')).toEqual( - content('tests/__data__/expected/epg_grab/template.guide.xml') - ) - - const zip = new Zip() - const expected = zip.decompress(fs.readFileSync('tests/__data__/output/guide.xml.gz')) - const result = zip.decompress( - fs.readFileSync('tests/__data__/expected/epg_grab/template.guide.xml.gz') - ) - expect(expected).toEqual(result) - }) -}) - -function content(filepath: string) { - return fs.readFileSync(pathToFileURL(filepath), { - encoding: 'utf8' - }) -} +import { pathToFileURL } from 'node:url' +import { execSync } from 'child_process' +import fs from 'fs-extra' +import path from 'path' +import pako from 'pako' + +const ENV_VAR = + 'cross-env SITES_DIR=tests/__data__/input/epg_grab/sites CURR_DATE=2022-10-20 DATA_DIR=tests/__data__/input/data' + +beforeEach(() => { + fs.emptyDirSync('tests/__data__/output') +}) + +describe('epg:grab', () => { + it('can grab epg by site name', () => { + const cmd = `${ENV_VAR} npm run grab --- --site=example.com --output="${path.resolve( + 'tests/__data__/output/guides/base.guide.xml' + )}"` + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + + expect(content('tests/__data__/output/guides/base.guide.xml')).toEqual( + content('tests/__data__/expected/epg_grab/base.guide.xml') + ) + }) + + it('can grab epg with curl option', () => { + const cmd = `${ENV_VAR} npm run grab --- --site=example.com --curl --output="${path.resolve( + 'tests/__data__/output/guides/curl.guide.xml' + )}"` + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + + expect(stdout).toContain('curl https://example.com') + }) + + it('can grab epg with wildcard as output', () => { + const cmd = `${ENV_VAR} npm run grab --- --channels="tests/__data__/input/epg_grab/sites/example.com/example.com.channels.xml" --output="tests/__data__/output/guides/wildcard/{site}/{lang}/guide.xml"` + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + + expect(content('tests/__data__/output/guides/wildcard/example.com/en/guide.xml')).toEqual( + content('tests/__data__/expected/epg_grab/wildcard/example.com/en/guide.xml') + ) + + expect(content('tests/__data__/output/guides/wildcard/example.com/fr/guide.xml')).toEqual( + content('tests/__data__/expected/epg_grab/wildcard/example.com/fr/guide.xml') + ) + }) + + it('can grab epg then language filter enabled', () => { + const cmd = `${ENV_VAR} npm run grab --- --channels=tests/__data__/input/epg_grab/sites/example.com/example.com.channels.xml --output=tests/__data__/output/guides/lang/{lang}/guide.xml --lang=fr` + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + + expect(content('tests/__data__/output/guides/lang/fr/guide.xml')).toEqual( + content('tests/__data__/expected/epg_grab/lang/fr/guide.xml') + ) + }) + + it('can grab epg then using a multi-language filter', () => { + const cmd = `${ENV_VAR} npm run grab --- --channels=tests/__data__/input/epg_grab/sites/example.com/example.com.channels.xml --output=tests/__data__/output/guides/multilang.guide.xml --lang=fr,it` + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + + expect(content('tests/__data__/output/guides/multilang.guide.xml')).toEqual( + content('tests/__data__/expected/epg_grab/multilang.guide.xml') + ) + }) + + it('can grab epg using custom channels list', () => { + const cmd = `${ENV_VAR} npm run grab --- --channels=tests/__data__/input/epg_grab/custom.channels.xml --output=tests/__data__/output/guides/custom_channels.guide.xml ` + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + + expect(content('tests/__data__/output/guides/custom_channels.guide.xml')).toEqual( + content('tests/__data__/expected/epg_grab/custom_channels.guide.xml') + ) + }) + + it('can grab epg with multiple channels.xml files', () => { + const cmd = `${ENV_VAR} npm run grab --- --channels=tests/__data__/input/epg_grab/sites/**/*.channels.xml --output=tests/__data__/output/guides/multiple_channels.guide.xml` + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + + expect(content('tests/__data__/output/guides/multiple_channels.guide.xml')).toEqual( + content('tests/__data__/expected/epg_grab/multiple_channels.guide.xml') + ) + }) + + it('can grab epg with gzip option enabled', () => { + const cmd = `${ENV_VAR} npm run grab --- --channels=tests/__data__/input/epg_grab/sites/example2.com/example2.com.channels.xml --output="${path.resolve( + 'tests/__data__/output/guides/gzip.guide.xml' + )}" --gzip ` + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + + expect(content('tests/__data__/output/guides/gzip.guide.xml')).toEqual( + content('tests/__data__/expected/epg_grab/gzip.guide.xml') + ) + + const expected = pako.ungzip(fs.readFileSync('tests/__data__/output/guides/gzip.guide.xml.gz')) + const result = pako.ungzip( + fs.readFileSync('tests/__data__/expected/epg_grab/gzip.guide.xml.gz') + ) + expect(expected).toEqual(result) + }) +}) + +function content(filepath: string) { + return fs.readFileSync(pathToFileURL(filepath), { + encoding: 'utf8' + }) +} diff --git a/tsconfig.json b/tsconfig.json index a36345fb..cec89f50 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { "strict": true, - "module": "Node16", - "moduleResolution": "node16", + "module": "commonjs", + "moduleResolution": "nodenext", "target": "es2022", "esModuleInterop": true, "declaration": true,