Merge branch 'iptv-org:master' into master

This commit is contained in:
Timotej Kusy 2022-03-01 22:23:42 +01:00 committed by GitHub
commit c0de89eaf9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
73 changed files with 982 additions and 696 deletions

View file

@ -10,12 +10,22 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
fetch-depth: 2 fetch-depth: 2
- name: Download channels from API - name: Download data from API
run: | run: |
mkdir -p scripts/data mkdir -p scripts/data
curl -L -o scripts/data/channels.json https://iptv-org.github.io/api/channels.json curl -L -o scripts/data/channels.json https://iptv-org.github.io/api/channels.json
- id: files - uses: actions/setup-node@v2
uses: jitterbit/get-changed-files@v1 if: ${{ !env.ACT && steps.files.outputs.any_changed == 'true' }}
- run: npm install with:
- run: npm run lint -- ${{ steps.files.outputs.added_modified }} node-version: '14'
- run: npm run validate -- ${{ steps.files.outputs.added_modified }} cache: 'npm'
- uses: tj-actions/changed-files@v12.2
id: files
with:
files: 'sites'
- name: validate
if: steps.files.outputs.any_changed == 'true'
run: |
npm install
npm run channels:lint -- ${{ steps.files.outputs.all_changed_files }}
npm run channels:validate -- ${{ steps.files.outputs.all_changed_files }}

View file

@ -14,18 +14,22 @@ jobs:
load: load:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- run: echo running on branch ${GITHUB_REF##*/}
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Download data from API - name: Download data from API
run: | run: |
mkdir -p scripts/data mkdir -p scripts/data
curl -L -o scripts/data/channels.json https://iptv-org.github.io/api/channels.json curl -L -o scripts/data/channels.json https://iptv-org.github.io/api/channels.json
- uses: FedericoCarboni/setup-ffmpeg@v1 - uses: FedericoCarboni/setup-ffmpeg@v1
- uses: actions/setup-node@v2
if: ${{ !env.ACT }}
with:
node-version: '14'
cache: 'npm'
- run: npm install - run: npm install
- run: CHANNELS_PATH=sites/${{inputs.site}}/*.channels.xml node scripts/commands/create-queue.js --max-clusters=1 --days=2 - run: CHANNELS_PATH=sites/${{inputs.site}}/*.channels.xml npm run queue:create -- --max-clusters=1 --days=2
- run: NODE_OPTIONS=--insecure-http-parser node scripts/commands/load-cluster.js --timeout=30000 --cluster-id=1 - run: NODE_OPTIONS=--insecure-http-parser npm run cluster:load -- --timeout=30000 --cluster-id=1
- run: node scripts/commands/save-results.js - run: npm run programs:save
- run: node scripts/commands/update-guides.js - run: npm run guides:update
- uses: tibdex/github-app-token@v1 - uses: tibdex/github-app-token@v1
if: ${{ !env.ACT }} if: ${{ !env.ACT }}
id: create-app-token id: create-app-token

View file

@ -4,12 +4,17 @@ on:
schedule: schedule:
- cron: '0 12 * * *' - cron: '0 12 * * *'
jobs: jobs:
check: update:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-node@v2
if: ${{ !env.ACT }}
with:
node-version: '14'
cache: 'npm'
- run: npm install - run: npm install
- run: node scripts/commands/update-api.js - run: npm run api:update
- uses: tibdex/github-app-token@v1 - uses: tibdex/github-app-token@v1
if: ${{ !env.ACT }} if: ${{ !env.ACT }}
id: create-app-token id: create-app-token
@ -21,7 +26,7 @@ jobs:
with: with:
repository-name: iptv-org/api repository-name: iptv-org/api
branch: gh-pages branch: gh-pages
folder: .gh-pages/api folder: .api
token: ${{ steps.create-app-token.outputs.token }} token: ${{ steps.create-app-token.outputs.token }}
git-config-name: iptv-bot[bot] git-config-name: iptv-bot[bot]
git-config-email: 84861620+iptv-bot[bot]@users.noreply.github.com git-config-email: 84861620+iptv-bot[bot]@users.noreply.github.com

57
.github/workflows/_update-readme.yml vendored Normal file
View file

@ -0,0 +1,57 @@
name: _update-readme
on:
workflow_dispatch:
schedule:
- cron: '0 12 * * *'
jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: echo "::set-output name=branch_name::$(date +'bot/auto-update-%s')"
id: create-branch-name
- run: git config user.name 'iptv-bot[bot]'
- run: git config user.email '84861620+iptv-bot[bot]@users.noreply.github.com'
- run: git checkout -b ${{ steps.create-branch-name.outputs.branch_name }}
- name: Download data from API
run: |
mkdir -p scripts/data
curl -L -o scripts/data/countries.json https://iptv-org.github.io/api/countries.json
- uses: actions/setup-node@v2
if: ${{ !env.ACT }}
with:
node-version: '14'
cache: 'npm'
- run: npm install
- run: npm run readme:update
- name: Commit Changes
if: ${{ !env.ACT }}
run: |
git add README.md
git commit -m "[Bot] Update README.md"
git status
git push -u origin ${{ steps.create-branch-name.outputs.branch_name }}
- uses: tibdex/github-app-token@v1
if: ${{ !env.ACT }}
id: create-app-token
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.APP_PRIVATE_KEY }}
- uses: repo-sync/pull-request@v2
if: ${{ !env.ACT && github.ref == 'refs/heads/master' }}
id: pull-request
with:
github_token: ${{ steps.create-app-token.outputs.token }}
source_branch: ${{ steps.create-branch-name.outputs.branch_name }}
destination_branch: 'master'
pr_title: '[Bot] Daily update'
pr_body: |
This pull request is created via [update-readme][1] workflow.
[1]: https://github.com/iptv-org/epg/actions/runs/${{ github.run_id }}
- uses: juliangruber/merge-pull-request-action@v1
if: ${{ !env.ACT && github.ref == 'refs/heads/master' }}
with:
github-token: ${{ secrets.PAT }}
number: ${{ steps.pull-request.outputs.pr_number }}
method: squash

17
.github/workflows/sky.de.yml vendored Normal file
View file

@ -0,0 +1,17 @@
name: sky.de
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
workflow_run:
workflows: [_trigger]
types:
- completed
jobs:
load:
uses: ./.github/workflows/_load.yml
with:
site: ${{github.workflow}}
secrets:
APP_ID: ${{ secrets.APP_ID }}
APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }}

View file

@ -17,10 +17,6 @@ The API documentation can be found in the [iptv-org/api](https://github.com/iptv
Links to other useful IPTV-related resources can be found in the [iptv-org/awesome-iptv](https://github.com/iptv-org/awesome-iptv) repository. Links to other useful IPTV-related resources can be found in the [iptv-org/awesome-iptv](https://github.com/iptv-org/awesome-iptv) repository.
## API
The API documentation can be found in the [iptv-org/api](https://github.com/iptv-org/api) repository.
## Contribution ## Contribution
If you find a bug or want to contribute to the code or documentation, you can help by submitting an [issue](https://github.com/iptv-org/epg/issues) or a [pull request](https://github.com/iptv-org/epg/pulls). If you find a bug or want to contribute to the code or documentation, you can help by submitting an [issue](https://github.com/iptv-org/epg/issues) or a [pull request](https://github.com/iptv-org/epg/pulls).

View file

@ -38,15 +38,15 @@ To load a program guide, all you need to do is copy the link to one or more of t
<tr><td valign="top" rowspan="2">🇧🇦&nbsp;Bosnia and Herzegovina</td><td align="right">178</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ba/mtel.ba.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/mtel.ba.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/mtel.ba.yml/badge.svg" alt="mtel.ba" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇧🇦&nbsp;Bosnia and Herzegovina</td><td align="right">178</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ba/mtel.ba.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/mtel.ba.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/mtel.ba.yml/badge.svg" alt="mtel.ba" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">4</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ba/tvarenasport.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tvarenasport.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tvarenasport.com.yml/badge.svg" alt="tvarenasport.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">4</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ba/tvarenasport.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tvarenasport.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tvarenasport.com.yml/badge.svg" alt="tvarenasport.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇧🇼&nbsp;Botswana</td><td align="right">130</td><td nowrap><code>https://iptv-org.github.io/epg/guides/bw/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇧🇼&nbsp;Botswana</td><td align="right">130</td><td nowrap><code>https://iptv-org.github.io/epg/guides/bw/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇧🇷&nbsp;Brazil</td><td align="right">248</td><td nowrap><code>https://iptv-org.github.io/epg/guides/br/mi.tv.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/mi.tv.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/mi.tv.yml/badge.svg" alt="mi.tv" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇧🇷&nbsp;Brazil</td><td align="right">251</td><td nowrap><code>https://iptv-org.github.io/epg/guides/br/mi.tv.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/mi.tv.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/mi.tv.yml/badge.svg" alt="mi.tv" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇧🇬&nbsp;Bulgaria</td><td align="right">105</td><td nowrap><code>https://iptv-org.github.io/epg/guides/bg/tv.dir.bg.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tv.dir.bg.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tv.dir.bg.yml/badge.svg" alt="tv.dir.bg" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇧🇬&nbsp;Bulgaria</td><td align="right">103</td><td nowrap><code>https://iptv-org.github.io/epg/guides/bg/tv.dir.bg.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tv.dir.bg.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tv.dir.bg.yml/badge.svg" alt="tv.dir.bg" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇧🇫&nbsp;Burkina Faso</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/bf/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇧🇫&nbsp;Burkina Faso</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/bf/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">128</td><td nowrap><code>https://iptv-org.github.io/epg/guides/bf/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">128</td><td nowrap><code>https://iptv-org.github.io/epg/guides/bf/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇧🇮&nbsp;Burundi</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/bi/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇧🇮&nbsp;Burundi</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/bi/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">128</td><td nowrap><code>https://iptv-org.github.io/epg/guides/bi/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">128</td><td nowrap><code>https://iptv-org.github.io/epg/guides/bi/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇨🇲&nbsp;Cameroon</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cm/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇨🇲&nbsp;Cameroon</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cm/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">128</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cm/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">128</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cm/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇨🇦&nbsp;Canada</td><td align="right">62</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ca/tvtv.us.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tvtv.us.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tvtv.us.yml/badge.svg" alt="tvtv.us" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇨🇦&nbsp;Canada</td><td align="right">76</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ca/tvtv.us.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tvtv.us.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tvtv.us.yml/badge.svg" alt="tvtv.us" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇨🇻&nbsp;Cape Verde</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cv/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇨🇻&nbsp;Cape Verde</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cv/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">125</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cv/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">125</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cv/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇨🇫&nbsp;Central African Republic</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cf/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇨🇫&nbsp;Central African Republic</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cf/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr>
@ -57,17 +57,17 @@ To load a program guide, all you need to do is copy the link to one or more of t
<tr><td align="right">98</td><td nowrap><code>https://iptv-org.github.io/epg/guides/td-en/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">98</td><td nowrap><code>https://iptv-org.github.io/epg/guides/td-en/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇨🇱&nbsp;Chile</td><td align="right">79</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cl/mi.tv.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/mi.tv.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/mi.tv.yml/badge.svg" alt="mi.tv" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇨🇱&nbsp;Chile</td><td align="right">79</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cl/mi.tv.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/mi.tv.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/mi.tv.yml/badge.svg" alt="mi.tv" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">52</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cl/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">52</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cl/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇨🇳&nbsp;China</td><td align="right">98</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cn/tv.cctv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tv.cctv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tv.cctv.com.yml/badge.svg" alt="tv.cctv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇨🇳&nbsp;China</td><td align="right">97</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cn/tv.cctv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tv.cctv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tv.cctv.com.yml/badge.svg" alt="tv.cctv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="3">🇨🇴&nbsp;Colombia</td><td align="right">103</td><td nowrap><code>https://iptv-org.github.io/epg/guides/co/siba.com.co.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/siba.com.co.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/siba.com.co.yml/badge.svg" alt="siba.com.co" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="3">🇨🇴&nbsp;Colombia</td><td align="right">103</td><td nowrap><code>https://iptv-org.github.io/epg/guides/co/siba.com.co.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/siba.com.co.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/siba.com.co.yml/badge.svg" alt="siba.com.co" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">56</td><td nowrap><code>https://iptv-org.github.io/epg/guides/co/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">56</td><td nowrap><code>https://iptv-org.github.io/epg/guides/co/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">42</td><td nowrap><code>https://iptv-org.github.io/epg/guides/co/mi.tv.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/mi.tv.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/mi.tv.yml/badge.svg" alt="mi.tv" style="max-width: 100%;"></a></td></tr> <tr><td align="right">42</td><td nowrap><code>https://iptv-org.github.io/epg/guides/co/mi.tv.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/mi.tv.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/mi.tv.yml/badge.svg" alt="mi.tv" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇰🇲&nbsp;Comoros</td><td align="right">119</td><td nowrap><code>https://iptv-org.github.io/epg/guides/km/canalplus-reunion.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-reunion.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-reunion.com.yml/badge.svg" alt="canalplus-reunion.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇰🇲&nbsp;Comoros</td><td align="right">119</td><td nowrap><code>https://iptv-org.github.io/epg/guides/km/canalplus-reunion.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-reunion.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-reunion.com.yml/badge.svg" alt="canalplus-reunion.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇨🇷&nbsp;Costa Rica</td><td align="right">49</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cr/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇨🇷&nbsp;Costa Rica</td><td align="right">49</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cr/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇭🇷&nbsp;Croatia</td><td align="right">169</td><td nowrap><code>https://iptv-org.github.io/epg/guides/hr/maxtv.hrvatskitelekom.hr.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/maxtv.hrvatskitelekom.hr.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/maxtv.hrvatskitelekom.hr.yml/badge.svg" alt="maxtv.hrvatskitelekom.hr" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇭🇷&nbsp;Croatia</td><td align="right">168</td><td nowrap><code>https://iptv-org.github.io/epg/guides/hr/maxtv.hrvatskitelekom.hr.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/maxtv.hrvatskitelekom.hr.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/maxtv.hrvatskitelekom.hr.yml/badge.svg" alt="maxtv.hrvatskitelekom.hr" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">10</td><td nowrap><code>https://iptv-org.github.io/epg/guides/hr/tvarenasport.hr.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tvarenasport.hr.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tvarenasport.hr.yml/badge.svg" alt="tvarenasport.hr" style="max-width: 100%;"></a></td></tr> <tr><td align="right">10</td><td nowrap><code>https://iptv-org.github.io/epg/guides/hr/tvarenasport.hr.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tvarenasport.hr.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tvarenasport.hr.yml/badge.svg" alt="tvarenasport.hr" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇨🇺&nbsp;Cuba</td><td align="right">10</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cu/tvcubana.icrt.cu.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tvcubana.icrt.cu.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tvcubana.icrt.cu.yml/badge.svg" alt="tvcubana.icrt.cu" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇨🇺&nbsp;Cuba</td><td align="right">10</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cu/tvcubana.icrt.cu.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tvcubana.icrt.cu.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tvcubana.icrt.cu.yml/badge.svg" alt="tvcubana.icrt.cu" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇨🇾&nbsp;Cyprus</td><td align="right">30</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cy/novacyprus.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/novacyprus.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/novacyprus.com.yml/badge.svg" alt="novacyprus.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇨🇾&nbsp;Cyprus</td><td align="right">30</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cy/novacyprus.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/novacyprus.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/novacyprus.com.yml/badge.svg" alt="novacyprus.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇨🇿&nbsp;Czech Republic</td><td align="right">512</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cz/m.tv.sms.cz.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/m.tv.sms.cz.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/m.tv.sms.cz.yml/badge.svg" alt="m.tv.sms.cz" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇨🇿&nbsp;Czech Republic</td><td align="right">519</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cz/m.tv.sms.cz.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/m.tv.sms.cz.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/m.tv.sms.cz.yml/badge.svg" alt="m.tv.sms.cz" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇨🇩&nbsp;Democratic Republic of the Congo</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cd/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇨🇩&nbsp;Democratic Republic of the Congo</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cd/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">126</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cd/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">126</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cd/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇩🇰&nbsp;Denmark</td><td align="right">61</td><td nowrap><code>https://iptv-org.github.io/epg/guides/dk/allente.se.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/allente.se.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/allente.se.yml/badge.svg" alt="allente.se" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇩🇰&nbsp;Denmark</td><td align="right">61</td><td nowrap><code>https://iptv-org.github.io/epg/guides/dk/allente.se.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/allente.se.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/allente.se.yml/badge.svg" alt="allente.se" style="max-width: 100%;"></a></td></tr>
@ -75,8 +75,8 @@ To load a program guide, all you need to do is copy the link to one or more of t
<tr><td align="right">121</td><td nowrap><code>https://iptv-org.github.io/epg/guides/dj/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">121</td><td nowrap><code>https://iptv-org.github.io/epg/guides/dj/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇩🇴&nbsp;Dominican Republic</td><td align="right">60</td><td nowrap><code>https://iptv-org.github.io/epg/guides/do/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇩🇴&nbsp;Dominican Republic</td><td align="right">60</td><td nowrap><code>https://iptv-org.github.io/epg/guides/do/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇪🇨&nbsp;Ecuador</td><td align="right">45</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ec/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇪🇨&nbsp;Ecuador</td><td align="right">45</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ec/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="4">🇪🇬&nbsp;Egypt</td><td align="right">108</td><td nowrap><code>https://iptv-org.github.io/epg/guides/eg-ar/elcinema.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/elcinema.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/elcinema.com.yml/badge.svg" alt="elcinema.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="4">🇪🇬&nbsp;Egypt</td><td align="right">106</td><td nowrap><code>https://iptv-org.github.io/epg/guides/eg-ar/elcinema.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/elcinema.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/elcinema.com.yml/badge.svg" alt="elcinema.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">108</td><td nowrap><code>https://iptv-org.github.io/epg/guides/eg-en/elcinema.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/elcinema.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/elcinema.com.yml/badge.svg" alt="elcinema.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">106</td><td nowrap><code>https://iptv-org.github.io/epg/guides/eg-en/elcinema.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/elcinema.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/elcinema.com.yml/badge.svg" alt="elcinema.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/eg-ar/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/eg-ar/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/eg-en/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/eg-en/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇸🇻&nbsp;El Salvador</td><td align="right">52</td><td nowrap><code>https://iptv-org.github.io/epg/guides/sv/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇸🇻&nbsp;El Salvador</td><td align="right">52</td><td nowrap><code>https://iptv-org.github.io/epg/guides/sv/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr>
@ -100,7 +100,7 @@ To load a program guide, all you need to do is copy the link to one or more of t
<tr><td align="right">125</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ga/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">125</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ga/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇬🇲&nbsp;Gambia</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/gm/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇬🇲&nbsp;Gambia</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/gm/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">124</td><td nowrap><code>https://iptv-org.github.io/epg/guides/gm/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">124</td><td nowrap><code>https://iptv-org.github.io/epg/guides/gm/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇬🇪&nbsp;Georgia</td><td align="right">117</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ge/magticom.ge.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/magticom.ge.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/magticom.ge.yml/badge.svg" alt="magticom.ge" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇬🇪&nbsp;Georgia</td><td align="right">116</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ge/magticom.ge.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/magticom.ge.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/magticom.ge.yml/badge.svg" alt="magticom.ge" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇩🇪&nbsp;Germany</td><td align="right">122</td><td nowrap><code>https://iptv-org.github.io/epg/guides/de/hd-plus.de.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/hd-plus.de.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/hd-plus.de.yml/badge.svg" alt="hd-plus.de" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇩🇪&nbsp;Germany</td><td align="right">122</td><td nowrap><code>https://iptv-org.github.io/epg/guides/de/hd-plus.de.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/hd-plus.de.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/hd-plus.de.yml/badge.svg" alt="hd-plus.de" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇬🇭&nbsp;Ghana</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/gh/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇬🇭&nbsp;Ghana</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/gh/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">139</td><td nowrap><code>https://iptv-org.github.io/epg/guides/gh/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">139</td><td nowrap><code>https://iptv-org.github.io/epg/guides/gh/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
@ -122,13 +122,13 @@ To load a program guide, all you need to do is copy the link to one or more of t
<tr><td valign="top">🇭🇺&nbsp;Hungary</td><td align="right">91</td><td nowrap><code>https://iptv-org.github.io/epg/guides/hu/tvmusor.hu.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tvmusor.hu.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tvmusor.hu.yml/badge.svg" alt="tvmusor.hu" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇭🇺&nbsp;Hungary</td><td align="right">91</td><td nowrap><code>https://iptv-org.github.io/epg/guides/hu/tvmusor.hu.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tvmusor.hu.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tvmusor.hu.yml/badge.svg" alt="tvmusor.hu" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇮🇸&nbsp;Iceland</td><td align="right">2</td><td nowrap><code>https://iptv-org.github.io/epg/guides/is/ruv.is.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/ruv.is.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/ruv.is.yml/badge.svg" alt="ruv.is" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇮🇸&nbsp;Iceland</td><td align="right">2</td><td nowrap><code>https://iptv-org.github.io/epg/guides/is/ruv.is.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/ruv.is.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/ruv.is.yml/badge.svg" alt="ruv.is" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇮🇳&nbsp;India</td><td align="right">364</td><td nowrap><code>https://iptv-org.github.io/epg/guides/in/dishtv.in.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dishtv.in.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dishtv.in.yml/badge.svg" alt="dishtv.in" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇮🇳&nbsp;India</td><td align="right">364</td><td nowrap><code>https://iptv-org.github.io/epg/guides/in/dishtv.in.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dishtv.in.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dishtv.in.yml/badge.svg" alt="dishtv.in" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇮🇩&nbsp;Indonesia</td><td align="right">90</td><td nowrap><code>https://iptv-org.github.io/epg/guides/id/mncvision.id.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/mncvision.id.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/mncvision.id.yml/badge.svg" alt="mncvision.id" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇮🇩&nbsp;Indonesia</td><td align="right">98</td><td nowrap><code>https://iptv-org.github.io/epg/guides/id/mncvision.id.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/mncvision.id.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/mncvision.id.yml/badge.svg" alt="mncvision.id" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">29</td><td nowrap><code>https://iptv-org.github.io/epg/guides/id/vidio.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/vidio.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/vidio.com.yml/badge.svg" alt="vidio.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">53</td><td nowrap><code>https://iptv-org.github.io/epg/guides/id/vidio.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/vidio.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/vidio.com.yml/badge.svg" alt="vidio.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇮🇷&nbsp;Iran</td><td align="right">29</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ir/tva.tv.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tva.tv.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tva.tv.yml/badge.svg" alt="tva.tv" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇮🇷&nbsp;Iran</td><td align="right">29</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ir/tva.tv.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tva.tv.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tva.tv.yml/badge.svg" alt="tva.tv" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇮🇶&nbsp;Iraq</td><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/iq-ar/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇮🇶&nbsp;Iraq</td><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/iq-ar/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/iq-en/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/iq-en/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇮🇪&nbsp;Ireland</td><td align="right">3</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ie/ontvtonight.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/ontvtonight.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/ontvtonight.com.yml/badge.svg" alt="ontvtonight.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇮🇪&nbsp;Ireland</td><td align="right">3</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ie/ontvtonight.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/ontvtonight.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/ontvtonight.com.yml/badge.svg" alt="ontvtonight.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇮🇹&nbsp;Italy</td><td align="right">145</td><td nowrap><code>https://iptv-org.github.io/epg/guides/it/guidatv.sky.it.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/guidatv.sky.it.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/guidatv.sky.it.yml/badge.svg" alt="guidatv.sky.it" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇮🇹&nbsp;Italy</td><td align="right">142</td><td nowrap><code>https://iptv-org.github.io/epg/guides/it/guidatv.sky.it.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/guidatv.sky.it.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/guidatv.sky.it.yml/badge.svg" alt="guidatv.sky.it" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">15</td><td nowrap><code>https://iptv-org.github.io/epg/guides/it/mediaset.it.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/mediaset.it.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/mediaset.it.yml/badge.svg" alt="mediaset.it" style="max-width: 100%;"></a></td></tr> <tr><td align="right">15</td><td nowrap><code>https://iptv-org.github.io/epg/guides/it/mediaset.it.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/mediaset.it.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/mediaset.it.yml/badge.svg" alt="mediaset.it" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇨🇮&nbsp;Ivory Coast</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ci/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇨🇮&nbsp;Ivory Coast</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ci/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">128</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ci/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">128</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ci/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
@ -143,13 +143,13 @@ To load a program guide, all you need to do is copy the link to one or more of t
<tr><td valign="top">🇱🇻&nbsp;Latvia</td><td align="right">21</td><td nowrap><code>https://iptv-org.github.io/epg/guides/lv/tv.lv.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tv.lv.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tv.lv.yml/badge.svg" alt="tv.lv" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇱🇻&nbsp;Latvia</td><td align="right">21</td><td nowrap><code>https://iptv-org.github.io/epg/guides/lv/tv.lv.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tv.lv.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tv.lv.yml/badge.svg" alt="tv.lv" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇱🇧&nbsp;Lebanon</td><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/lb-ar/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇱🇧&nbsp;Lebanon</td><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/lb-ar/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/lb-en/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/lb-en/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇱🇸&nbsp;Lesotho</td><td align="right">146</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ls/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇱🇸&nbsp;Lesotho</td><td align="right">145</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ls/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇱🇷&nbsp;Liberia</td><td align="right">125</td><td nowrap><code>https://iptv-org.github.io/epg/guides/lr/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇱🇷&nbsp;Liberia</td><td align="right">125</td><td nowrap><code>https://iptv-org.github.io/epg/guides/lr/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇱🇾&nbsp;Libya</td><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ly-ar/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇱🇾&nbsp;Libya</td><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ly-ar/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ly-en/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ly-en/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇲🇬&nbsp;Madagascar</td><td align="right">125</td><td nowrap><code>https://iptv-org.github.io/epg/guides/mg/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇲🇬&nbsp;Madagascar</td><td align="right">125</td><td nowrap><code>https://iptv-org.github.io/epg/guides/mg/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇲🇼&nbsp;Malawi</td><td align="right">132</td><td nowrap><code>https://iptv-org.github.io/epg/guides/mw/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇲🇼&nbsp;Malawi</td><td align="right">131</td><td nowrap><code>https://iptv-org.github.io/epg/guides/mw/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇲🇾&nbsp;Malaysia</td><td align="right">123</td><td nowrap><code>https://iptv-org.github.io/epg/guides/my/astro.com.my.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/astro.com.my.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/astro.com.my.yml/badge.svg" alt="astro.com.my" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇲🇾&nbsp;Malaysia</td><td align="right">141</td><td nowrap><code>https://iptv-org.github.io/epg/guides/my/astro.com.my.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/astro.com.my.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/astro.com.my.yml/badge.svg" alt="astro.com.my" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇲🇱&nbsp;Mali</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ml/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇲🇱&nbsp;Mali</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ml/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">125</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ml/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">125</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ml/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇲🇶&nbsp;Martinique</td><td align="right">128</td><td nowrap><code>https://iptv-org.github.io/epg/guides/mq/canalplus-caraibes.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-caraibes.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-caraibes.com.yml/badge.svg" alt="canalplus-caraibes.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇲🇶&nbsp;Martinique</td><td align="right">128</td><td nowrap><code>https://iptv-org.github.io/epg/guides/mq/canalplus-caraibes.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-caraibes.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-caraibes.com.yml/badge.svg" alt="canalplus-caraibes.com" style="max-width: 100%;"></a></td></tr>
@ -170,7 +170,7 @@ To load a program guide, all you need to do is copy the link to one or more of t
<tr><td valign="top">🇳🇮&nbsp;Nicaragua</td><td align="right">50</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ni/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇳🇮&nbsp;Nicaragua</td><td align="right">50</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ni/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇳🇪&nbsp;Niger</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ne/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇳🇪&nbsp;Niger</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ne/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">128</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ne/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">128</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ne/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇳🇬&nbsp;Nigeria</td><td align="right">147</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ng/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇳🇬&nbsp;Nigeria</td><td align="right">146</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ng/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇲🇰&nbsp;North Macedonia</td><td align="right">52</td><td nowrap><code>https://iptv-org.github.io/epg/guides/mk/maxtvgo.mk.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/maxtvgo.mk.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/maxtvgo.mk.yml/badge.svg" alt="maxtvgo.mk" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇲🇰&nbsp;North Macedonia</td><td align="right">52</td><td nowrap><code>https://iptv-org.github.io/epg/guides/mk/maxtvgo.mk.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/maxtvgo.mk.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/maxtvgo.mk.yml/badge.svg" alt="maxtvgo.mk" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">4</td><td nowrap><code>https://iptv-org.github.io/epg/guides/mk/tvarenasport.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tvarenasport.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tvarenasport.com.yml/badge.svg" alt="tvarenasport.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">4</td><td nowrap><code>https://iptv-org.github.io/epg/guides/mk/tvarenasport.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tvarenasport.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tvarenasport.com.yml/badge.svg" alt="tvarenasport.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇳🇴&nbsp;Norway</td><td align="right">71</td><td nowrap><code>https://iptv-org.github.io/epg/guides/no/allente.se.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/allente.se.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/allente.se.yml/badge.svg" alt="allente.se" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇳🇴&nbsp;Norway</td><td align="right">71</td><td nowrap><code>https://iptv-org.github.io/epg/guides/no/allente.se.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/allente.se.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/allente.se.yml/badge.svg" alt="allente.se" style="max-width: 100%;"></a></td></tr>
@ -183,15 +183,15 @@ To load a program guide, all you need to do is copy the link to one or more of t
<tr><td valign="top">🇵🇾&nbsp;Paraguay</td><td align="right">39</td><td nowrap><code>https://iptv-org.github.io/epg/guides/py/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇵🇾&nbsp;Paraguay</td><td align="right">39</td><td nowrap><code>https://iptv-org.github.io/epg/guides/py/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇵🇪&nbsp;Peru</td><td align="right">48</td><td nowrap><code>https://iptv-org.github.io/epg/guides/pe/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇵🇪&nbsp;Peru</td><td align="right">48</td><td nowrap><code>https://iptv-org.github.io/epg/guides/pe/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">21</td><td nowrap><code>https://iptv-org.github.io/epg/guides/pe/mi.tv.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/mi.tv.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/mi.tv.yml/badge.svg" alt="mi.tv" style="max-width: 100%;"></a></td></tr> <tr><td align="right">21</td><td nowrap><code>https://iptv-org.github.io/epg/guides/pe/mi.tv.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/mi.tv.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/mi.tv.yml/badge.svg" alt="mi.tv" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇵🇱&nbsp;Poland</td><td align="right">341</td><td nowrap><code>https://iptv-org.github.io/epg/guides/pl/programtv.onet.pl.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/programtv.onet.pl.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/programtv.onet.pl.yml/badge.svg" alt="programtv.onet.pl" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇵🇱&nbsp;Poland</td><td align="right">340</td><td nowrap><code>https://iptv-org.github.io/epg/guides/pl/programtv.onet.pl.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/programtv.onet.pl.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/programtv.onet.pl.yml/badge.svg" alt="programtv.onet.pl" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇵🇹&nbsp;Portugal</td><td align="right">110</td><td nowrap><code>https://iptv-org.github.io/epg/guides/pt/meo.pt.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/meo.pt.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/meo.pt.yml/badge.svg" alt="meo.pt" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇵🇹&nbsp;Portugal</td><td align="right">203</td><td nowrap><code>https://iptv-org.github.io/epg/guides/pt/meo.pt.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/meo.pt.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/meo.pt.yml/badge.svg" alt="meo.pt" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="3">🇶🇦&nbsp;Qatar</td><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/qa-ar/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="3">🇶🇦&nbsp;Qatar</td><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/qa-ar/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/qa-en/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/qa-en/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">22</td><td nowrap><code>https://iptv-org.github.io/epg/guides/qa/beinsports.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/beinsports.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/beinsports.com.yml/badge.svg" alt="beinsports.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">22</td><td nowrap><code>https://iptv-org.github.io/epg/guides/qa/beinsports.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/beinsports.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/beinsports.com.yml/badge.svg" alt="beinsports.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇨🇬&nbsp;Republic of the Congo</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cg/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇨🇬&nbsp;Republic of the Congo</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cg/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">125</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cg/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">125</td><td nowrap><code>https://iptv-org.github.io/epg/guides/cg/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇷🇴&nbsp;Romania</td><td align="right">224</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ro/programetv.ro.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/programetv.ro.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/programetv.ro.yml/badge.svg" alt="programetv.ro" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇷🇴&nbsp;Romania</td><td align="right">224</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ro/programetv.ro.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/programetv.ro.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/programetv.ro.yml/badge.svg" alt="programetv.ro" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇷🇺&nbsp;Russia</td><td align="right">285</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ru/tv.yandex.ru.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tv.yandex.ru.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tv.yandex.ru.yml/badge.svg" alt="tv.yandex.ru" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇷🇺&nbsp;Russia</td><td align="right">283</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ru/tv.yandex.ru.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tv.yandex.ru.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tv.yandex.ru.yml/badge.svg" alt="tv.yandex.ru" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇷🇼&nbsp;Rwanda</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/rw/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇷🇼&nbsp;Rwanda</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/rw/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">132</td><td nowrap><code>https://iptv-org.github.io/epg/guides/rw/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">132</td><td nowrap><code>https://iptv-org.github.io/epg/guides/rw/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇷🇪&nbsp;Réunion</td><td align="right">119</td><td nowrap><code>https://iptv-org.github.io/epg/guides/re/canalplus-reunion.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-reunion.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-reunion.com.yml/badge.svg" alt="canalplus-reunion.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇷🇪&nbsp;Réunion</td><td align="right">119</td><td nowrap><code>https://iptv-org.github.io/epg/guides/re/canalplus-reunion.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-reunion.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-reunion.com.yml/badge.svg" alt="canalplus-reunion.com" style="max-width: 100%;"></a></td></tr>
@ -208,13 +208,13 @@ To load a program guide, all you need to do is copy the link to one or more of t
<tr><td align="right">125</td><td nowrap><code>https://iptv-org.github.io/epg/guides/sl/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">125</td><td nowrap><code>https://iptv-org.github.io/epg/guides/sl/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇸🇮&nbsp;Slovenia</td><td align="right">277</td><td nowrap><code>https://iptv-org.github.io/epg/guides/si/tv2go.t-2.net.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tv2go.t-2.net.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tv2go.t-2.net.yml/badge.svg" alt="tv2go.t-2.net" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇸🇮&nbsp;Slovenia</td><td align="right">277</td><td nowrap><code>https://iptv-org.github.io/epg/guides/si/tv2go.t-2.net.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tv2go.t-2.net.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tv2go.t-2.net.yml/badge.svg" alt="tv2go.t-2.net" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇸🇴&nbsp;Somalia</td><td align="right">120</td><td nowrap><code>https://iptv-org.github.io/epg/guides/so/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇸🇴&nbsp;Somalia</td><td align="right">120</td><td nowrap><code>https://iptv-org.github.io/epg/guides/so/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇿🇦&nbsp;South Africa</td><td align="right">160</td><td nowrap><code>https://iptv-org.github.io/epg/guides/za/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇿🇦&nbsp;South Africa</td><td align="right">159</td><td nowrap><code>https://iptv-org.github.io/epg/guides/za/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇸🇸&nbsp;South Sudan</td><td align="right">125</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ss/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇸🇸&nbsp;South Sudan</td><td align="right">125</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ss/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇪🇸&nbsp;Spain</td><td align="right">112</td><td nowrap><code>https://iptv-org.github.io/epg/guides/es/programacion-tv.elpais.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/programacion-tv.elpais.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/programacion-tv.elpais.com.yml/badge.svg" alt="programacion-tv.elpais.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇪🇸&nbsp;Spain</td><td align="right">110</td><td nowrap><code>https://iptv-org.github.io/epg/guides/es/programacion-tv.elpais.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/programacion-tv.elpais.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/programacion-tv.elpais.com.yml/badge.svg" alt="programacion-tv.elpais.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">58</td><td nowrap><code>https://iptv-org.github.io/epg/guides/es/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">58</td><td nowrap><code>https://iptv-org.github.io/epg/guides/es/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇸🇩&nbsp;Sudan</td><td align="right">118</td><td nowrap><code>https://iptv-org.github.io/epg/guides/sd/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇸🇩&nbsp;Sudan</td><td align="right">118</td><td nowrap><code>https://iptv-org.github.io/epg/guides/sd/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇸🇿&nbsp;Swaziland</td><td align="right">127</td><td nowrap><code>https://iptv-org.github.io/epg/guides/sz/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇸🇿&nbsp;Swaziland</td><td align="right">127</td><td nowrap><code>https://iptv-org.github.io/epg/guides/sz/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇸🇪&nbsp;Sweden</td><td align="right">89</td><td nowrap><code>https://iptv-org.github.io/epg/guides/se/allente.se.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/allente.se.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/allente.se.yml/badge.svg" alt="allente.se" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇸🇪&nbsp;Sweden</td><td align="right">94</td><td nowrap><code>https://iptv-org.github.io/epg/guides/se/allente.se.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/allente.se.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/allente.se.yml/badge.svg" alt="allente.se" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇨🇭&nbsp;Switzerland</td><td align="right">598</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ch/tv.blue.ch.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tv.blue.ch.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tv.blue.ch.yml/badge.svg" alt="tv.blue.ch" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇨🇭&nbsp;Switzerland</td><td align="right">598</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ch/tv.blue.ch.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tv.blue.ch.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tv.blue.ch.yml/badge.svg" alt="tv.blue.ch" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇸🇹&nbsp;São Tomé and Príncipe</td><td align="right">128</td><td nowrap><code>https://iptv-org.github.io/epg/guides/st/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇸🇹&nbsp;São Tomé and Príncipe</td><td align="right">128</td><td nowrap><code>https://iptv-org.github.io/epg/guides/st/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇹🇿&nbsp;Tanzania</td><td align="right">30</td><td nowrap><code>https://iptv-org.github.io/epg/guides/tz/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇹🇿&nbsp;Tanzania</td><td align="right">30</td><td nowrap><code>https://iptv-org.github.io/epg/guides/tz/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
@ -222,15 +222,15 @@ To load a program guide, all you need to do is copy the link to one or more of t
<tr><td valign="top" rowspan="2">🇹🇬&nbsp;Togo</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/tg/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇹🇬&nbsp;Togo</td><td align="right">242</td><td nowrap><code>https://iptv-org.github.io/epg/guides/tg/canalplus-afrique.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/canalplus-afrique.com.yml/badge.svg" alt="canalplus-afrique.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">137</td><td nowrap><code>https://iptv-org.github.io/epg/guides/tg/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">137</td><td nowrap><code>https://iptv-org.github.io/epg/guides/tg/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="3">🇹🇷&nbsp;Turkey</td><td align="right">145</td><td nowrap><code>https://iptv-org.github.io/epg/guides/tr/tvplus.com.tr.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tvplus.com.tr.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tvplus.com.tr.yml/badge.svg" alt="tvplus.com.tr" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="3">🇹🇷&nbsp;Turkey</td><td align="right">145</td><td nowrap><code>https://iptv-org.github.io/epg/guides/tr/tvplus.com.tr.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tvplus.com.tr.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tvplus.com.tr.yml/badge.svg" alt="tvplus.com.tr" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">118</td><td nowrap><code>https://iptv-org.github.io/epg/guides/tr/digiturk.com.tr.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/digiturk.com.tr.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/digiturk.com.tr.yml/badge.svg" alt="digiturk.com.tr" style="max-width: 100%;"></a></td></tr> <tr><td align="right">117</td><td nowrap><code>https://iptv-org.github.io/epg/guides/tr/digiturk.com.tr.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/digiturk.com.tr.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/digiturk.com.tr.yml/badge.svg" alt="digiturk.com.tr" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">105</td><td nowrap><code>https://iptv-org.github.io/epg/guides/tr/dsmart.com.tr.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dsmart.com.tr.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dsmart.com.tr.yml/badge.svg" alt="dsmart.com.tr" style="max-width: 100%;"></a></td></tr> <tr><td align="right">106</td><td nowrap><code>https://iptv-org.github.io/epg/guides/tr/dsmart.com.tr.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dsmart.com.tr.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dsmart.com.tr.yml/badge.svg" alt="dsmart.com.tr" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇺🇬&nbsp;Uganda</td><td align="right">151</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ug/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇺🇬&nbsp;Uganda</td><td align="right">151</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ug/dstv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/dstv.com.yml/badge.svg" alt="dstv.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top">🇺🇦&nbsp;Ukraine</td><td align="right">114</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ua/tvgid.ua.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tvgid.ua.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tvgid.ua.yml/badge.svg" alt="tvgid.ua" style="max-width: 100%;"></a></td></tr> <tr><td valign="top">🇺🇦&nbsp;Ukraine</td><td align="right">114</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ua/tvgid.ua.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tvgid.ua.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tvgid.ua.yml/badge.svg" alt="tvgid.ua" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇦🇪&nbsp;United Arab Emirates</td><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ae-ar/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇦🇪&nbsp;United Arab Emirates</td><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ae-ar/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ae-en/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">99</td><td nowrap><code>https://iptv-org.github.io/epg/guides/ae-en/osn.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/osn.com.yml/badge.svg" alt="osn.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="2">🇬🇧&nbsp;United Kingdom</td><td align="right">190</td><td nowrap><code>https://iptv-org.github.io/epg/guides/uk/sky.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/sky.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/sky.com.yml/badge.svg" alt="sky.com" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="2">🇬🇧&nbsp;United Kingdom</td><td align="right">258</td><td nowrap><code>https://iptv-org.github.io/epg/guides/uk/sky.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/sky.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/sky.com.yml/badge.svg" alt="sky.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">97</td><td nowrap><code>https://iptv-org.github.io/epg/guides/uk/ontvtonight.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/ontvtonight.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/ontvtonight.com.yml/badge.svg" alt="ontvtonight.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">97</td><td nowrap><code>https://iptv-org.github.io/epg/guides/uk/ontvtonight.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/ontvtonight.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/ontvtonight.com.yml/badge.svg" alt="ontvtonight.com" style="max-width: 100%;"></a></td></tr>
<tr><td valign="top" rowspan="4">🇺🇸&nbsp;United States</td><td align="right">1298</td><td nowrap><code>https://iptv-org.github.io/epg/guides/us/tvtv.us.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tvtv.us.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tvtv.us.yml/badge.svg" alt="tvtv.us" style="max-width: 100%;"></a></td></tr> <tr><td valign="top" rowspan="4">🇺🇸&nbsp;United States</td><td align="right">1683</td><td nowrap><code>https://iptv-org.github.io/epg/guides/us/tvtv.us.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tvtv.us.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tvtv.us.yml/badge.svg" alt="tvtv.us" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">410</td><td nowrap><code>https://iptv-org.github.io/epg/guides/us/directv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/directv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/directv.com.yml/badge.svg" alt="directv.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">410</td><td nowrap><code>https://iptv-org.github.io/epg/guides/us/directv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/directv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/directv.com.yml/badge.svg" alt="directv.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">88</td><td nowrap><code>https://iptv-org.github.io/epg/guides/us/tvguide.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tvguide.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tvguide.com.yml/badge.svg" alt="tvguide.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">88</td><td nowrap><code>https://iptv-org.github.io/epg/guides/us/tvguide.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/tvguide.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/tvguide.com.yml/badge.svg" alt="tvguide.com" style="max-width: 100%;"></a></td></tr>
<tr><td align="right">22</td><td nowrap><code>https://iptv-org.github.io/epg/guides/us/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr> <tr><td align="right">22</td><td nowrap><code>https://iptv-org.github.io/epg/guides/us/gatotv.com.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/gatotv.com.yml/badge.svg" alt="gatotv.com" style="max-width: 100%;"></a></td></tr>
@ -244,14 +244,14 @@ To load a program guide, all you need to do is copy the link to one or more of t
</tbody> </tbody>
</table> </table>
## EPG Codes
📋&nbsp;&nbsp;[iptv-org.github.io](https://iptv-org.github.io/)
## API ## API
The API documentation can be found in the [iptv-org/api](https://github.com/iptv-org/api) repository. The API documentation can be found in the [iptv-org/api](https://github.com/iptv-org/api) repository.
## Resources
Links to other useful IPTV-related resources can be found in the [iptv-org/awesome-iptv](https://github.com/iptv-org/awesome-iptv) repository.
## Contribution ## Contribution
If you find a bug or want to contribute to the code or documentation, you can help by submitting an [issue](https://github.com/iptv-org/epg/issues) or a [pull request](https://github.com/iptv-org/epg/pulls). If you find a bug or want to contribute to the code or documentation, you can help by submitting an [issue](https://github.com/iptv-org/epg/issues) or a [pull request](https://github.com/iptv-org/epg/pulls).

152
package-lock.json generated
View file

@ -16,6 +16,7 @@
"epg-grabber": "^0.20.0", "epg-grabber": "^0.20.0",
"epg-parser": "^0.1.6", "epg-parser": "^0.1.6",
"form-data": "^4.0.0", "form-data": "^4.0.0",
"fs-extra": "^10.0.1",
"glob": "^7.2.0", "glob": "^7.2.0",
"iconv-lite": "^0.4.24", "iconv-lite": "^0.4.24",
"jest": "^27.3.1", "jest": "^27.3.1",
@ -23,7 +24,6 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"markdown-include": "^0.4.3", "markdown-include": "^0.4.3",
"mockdate": "^3.0.5", "mockdate": "^3.0.5",
"mz": "^2.7.0",
"nedb-promises": "^5.0.3", "nedb-promises": "^5.0.3",
"parse-duration": "^1.0.0", "parse-duration": "^1.0.0",
"pdf-parse": "^1.1.1", "pdf-parse": "^1.1.1",
@ -1179,11 +1179,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1" "url": "https://github.com/chalk/ansi-styles?sponsor=1"
} }
}, },
"node_modules/any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
"integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
},
"node_modules/anymatch": { "node_modules/anymatch": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
@ -2346,9 +2341,9 @@
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
}, },
"node_modules/follow-redirects": { "node_modules/follow-redirects": {
"version": "1.14.5", "version": "1.14.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==", "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
"funding": [ "funding": [
{ {
"type": "individual", "type": "individual",
@ -2377,6 +2372,27 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/fs-extra": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz",
"integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==",
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/fs-extra/node_modules/universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/fs-minipass": { "node_modules/fs-minipass": {
"version": "1.2.7", "version": "1.2.7",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
@ -3614,6 +3630,25 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"dependencies": {
"universalify": "^2.0.0"
},
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/jsonfile/node_modules/universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
"engines": {
"node": ">= 10.0.0"
}
},
"node_modules/keyv": { "node_modules/keyv": {
"version": "4.0.4", "version": "4.0.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.4.tgz", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.4.tgz",
@ -3913,16 +3948,6 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}, },
"node_modules/mz": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
"dependencies": {
"any-promise": "^1.0.0",
"object-assign": "^4.0.1",
"thenify-all": "^1.0.0"
}
},
"node_modules/nan": { "node_modules/nan": {
"version": "2.14.2", "version": "2.14.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
@ -5066,25 +5091,6 @@
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
"integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="
}, },
"node_modules/thenify": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
"dependencies": {
"any-promise": "^1.0.0"
}
},
"node_modules/thenify-all": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
"integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=",
"dependencies": {
"thenify": ">= 3.1.0 < 4"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/throat": { "node_modules/throat": {
"version": "6.0.1", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz",
@ -6460,11 +6466,6 @@
"color-convert": "^2.0.1" "color-convert": "^2.0.1"
} }
}, },
"any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
"integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
},
"anymatch": { "anymatch": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
@ -7368,9 +7369,9 @@
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
}, },
"follow-redirects": { "follow-redirects": {
"version": "1.14.5", "version": "1.14.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==" "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w=="
}, },
"form-data": { "form-data": {
"version": "4.0.0", "version": "4.0.0",
@ -7382,6 +7383,23 @@
"mime-types": "^2.1.12" "mime-types": "^2.1.12"
} }
}, },
"fs-extra": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.1.tgz",
"integrity": "sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==",
"requires": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"dependencies": {
"universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ=="
}
}
},
"fs-minipass": { "fs-minipass": {
"version": "1.2.7", "version": "1.2.7",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
@ -8308,6 +8326,22 @@
"minimist": "^1.2.5" "minimist": "^1.2.5"
} }
}, },
"jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
"requires": {
"graceful-fs": "^4.1.6",
"universalify": "^2.0.0"
},
"dependencies": {
"universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
"integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ=="
}
}
},
"keyv": { "keyv": {
"version": "4.0.4", "version": "4.0.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.4.tgz", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.4.tgz",
@ -8550,16 +8584,6 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}, },
"mz": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
"requires": {
"any-promise": "^1.0.0",
"object-assign": "^4.0.1",
"thenify-all": "^1.0.0"
}
},
"nan": { "nan": {
"version": "2.14.2", "version": "2.14.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
@ -9422,22 +9446,6 @@
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
"integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="
}, },
"thenify": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
"requires": {
"any-promise": "^1.0.0"
}
},
"thenify-all": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
"integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=",
"requires": {
"thenify": ">= 3.1.0 < 4"
}
},
"throat": { "throat": {
"version": "6.0.1", "version": "6.0.1",
"resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz",

View file

@ -1,13 +1,21 @@
{ {
"name": "epg", "name": "epg",
"scripts": { "scripts": {
"lint": "node scripts/commands/lint.js", "channels:validate": "node scripts/commands/channels/validate.js",
"validate": "node scripts/commands/validate.js", "channels:lint": "node scripts/commands/channels/lint.js",
"channels:parse": "node scripts/commands/channels/parse.js",
"queue:create": "node scripts/commands/queue/create.js",
"cluster:load": "node scripts/commands/cluster/load.js",
"programs:save": "node scripts/commands/programs/save.js",
"guides:update": "node scripts/commands/guides/update.js",
"api:update": "node scripts/commands/api/update.js",
"readme:update": "node scripts/commands/readme/update.js",
"test": "npx jest --runInBand", "test": "npx jest --runInBand",
"test:commands": "npx jest --runInBand -- commands", "test:commands": "npx jest --runInBand -- commands",
"test:sites": "npx jest --runInBand -- sites", "test:sites": "npx jest --runInBand -- sites",
"act": "act workflow_dispatch", "act:check": "act workflow_dispatch -W .github/workflows/_check.yml",
"update-readme": "node scripts/commands/update-readme.js" "act:update-readme": "act workflow_dispatch -W .github/workflows/_update-readme.yml",
"act:update-api": "act workflow_dispatch -W .github/workflows/_update-api.yml"
}, },
"private": true, "private": true,
"author": "Arhey", "author": "Arhey",
@ -25,6 +33,7 @@
"epg-grabber": "^0.20.0", "epg-grabber": "^0.20.0",
"epg-parser": "^0.1.6", "epg-parser": "^0.1.6",
"form-data": "^4.0.0", "form-data": "^4.0.0",
"fs-extra": "^10.0.1",
"glob": "^7.2.0", "glob": "^7.2.0",
"iconv-lite": "^0.4.24", "iconv-lite": "^0.4.24",
"jest": "^27.3.1", "jest": "^27.3.1",
@ -32,7 +41,6 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"markdown-include": "^0.4.3", "markdown-include": "^0.4.3",
"mockdate": "^3.0.5", "mockdate": "^3.0.5",
"mz": "^2.7.0",
"nedb-promises": "^5.0.3", "nedb-promises": "^5.0.3",
"parse-duration": "^1.0.0", "parse-duration": "^1.0.0",
"pdf-parse": "^1.1.1", "pdf-parse": "^1.1.1",

View file

@ -0,0 +1,42 @@
const { file, parser, logger } = require('../../core')
const { program } = require('commander')
const _ = require('lodash')
const CHANNELS_PATH = process.env.CHANNELS_PATH || 'sites/**/*.channels.xml'
const OUTPUT_DIR = process.env.OUTPUT_DIR || '.api'
async function main() {
let guides = []
try {
const files = await file.list(CHANNELS_PATH)
for (const filepath of files) {
const { site, channels } = await parser.parseChannels(filepath)
const dir = file.dirname(filepath)
const config = require(file.resolve(`${dir}/${site}.config.js`))
if (config.ignore) continue
const filename = file.basename(filepath)
const [__, suffix] = filename.match(/\_(.*)\.channels\.xml$/) || [null, null]
for (const channel of channels) {
guides.push({
channel: channel.xmltv_id,
site,
lang: channel.lang,
url: `https://iptv-org.github.io/epg/guides/${suffix}/${site}.epg.xml`
})
}
}
} catch (err) {
console.error(err)
}
guides = _.sortBy(guides, 'channel')
const outputFilepath = `${OUTPUT_DIR}/guides.json`
await file.create(outputFilepath, JSON.stringify(guides))
logger.info(`saved to "${outputFilepath}"...`)
}
main()

View file

@ -1,7 +1,7 @@
const chalk = require('chalk') const chalk = require('chalk')
const libxml = require('libxmljs') const libxml = require('libxmljs')
const { program } = require('commander') const { program } = require('commander')
const { logger, file } = require('../core') const { logger, file } = require('../../core')
const xsd = `<?xml version="1.0" encoding="UTF-8"?> const xsd = `<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">

View file

@ -0,0 +1,43 @@
const { logger, file, xml } = require('../../core')
const { Command } = require('commander')
const path = require('path')
const _ = require('lodash')
const program = new Command()
program
.requiredOption('-c, --config <config>', 'Config file')
.option('-s, --set [args...]', 'Set custom arguments', [])
.option('-o, --output <output>', 'Output file')
.parse(process.argv)
const options = program.opts()
async function main() {
const config = require(path.resolve(options.config))
const args = {}
options.set.forEach(arg => {
const [key, value] = arg.split(':')
args[key] = value
})
let channels = config.channels(args)
if (isPromise(channels)) {
channels = await channels
}
channels = _.sortBy(channels, 'xmltv_id')
const dir = file.dirname(options.config)
const outputFilepath = options.output || `${dir}/${config.site}.channels.xml`
const output = xml.create(channels, config.site)
await file.write(outputFilepath, output)
logger.info(`File '${output}' successfully saved`)
}
main()
function isPromise(promise) {
return !!promise && typeof promise.then === 'function'
}

View file

@ -1,4 +1,4 @@
const { parser, logger, api } = require('../core') const { parser, logger, api } = require('../../core')
const { program } = require('commander') const { program } = require('commander')
const chalk = require('chalk') const chalk = require('chalk')

View file

@ -1,7 +1,7 @@
const _ = require('lodash') const _ = require('lodash')
const grabber = require('epg-grabber') const grabber = require('epg-grabber')
const { program } = require('commander') const { program } = require('commander')
const { db, logger, timer, file, parser } = require('../core') const { db, logger, timer, file, parser } = require('../../core')
const options = program const options = program
.requiredOption('-c, --cluster-id <cluster-id>', 'The ID of cluster to load', parser.parseNumber) .requiredOption('-c, --cluster-id <cluster-id>', 'The ID of cluster to load', parser.parseNumber)
@ -16,7 +16,7 @@ const options = program
.opts() .opts()
const LOGS_DIR = process.env.LOGS_DIR || 'scripts/logs' const LOGS_DIR = process.env.LOGS_DIR || 'scripts/logs'
const CLUSTER_PATH = `${LOGS_DIR}/load-cluster/cluster_${options.clusterId}.log` const CLUSTER_PATH = `${LOGS_DIR}/cluster/load/cluster_${options.clusterId}.log`
async function main() { async function main() {
logger.info('Starting...') logger.info('Starting...')

View file

@ -1,16 +0,0 @@
const { logger, db } = require('../core')
async function main() {
await db.queue.load()
const docs = await db.queue.find({}).sort({ cluster_id: 1 })
const cluster_id = docs.reduce((acc, curr) => {
if (!acc.includes(curr.cluster_id)) acc.push(curr.cluster_id)
return acc
}, [])
const matrix = { cluster_id }
const output = `::set-output name=matrix::${JSON.stringify(matrix)}`
logger.info(output)
}
main()

View file

@ -0,0 +1,76 @@
const { db, logger, file, api } = require('../../core')
const grabber = require('epg-grabber')
const _ = require('lodash')
const PUBLIC_DIR = process.env.PUBLIC_DIR || '.gh-pages'
async function main() {
logger.info(`Generating guides/...`)
logger.info('Loading "database/programs.db"...')
await db.programs.load()
await api.channels.load()
const grouped = groupByGroup(await loadQueue())
for (const key in grouped) {
let channels = {}
let programs = []
for (const item of grouped[key]) {
if (item.error) continue
const itemPrograms = await loadProgramsForItem(item)
programs = programs.concat(itemPrograms)
if (channels[item.channel.xmltv_id]) continue
const channel = api.channels.find({ id: item.channel.xmltv_id })
if (channel) {
channels[channel.id] = {
xmltv_id: channel.id,
name: item.channel.display_name,
logo: channel.logo,
site: item.channel.site
}
}
}
channels = Object.values(channels)
channels = _.sortBy(channels, 'xmltv_id')
programs = _.sortBy(programs, ['channel', 'start'])
const filepath = `${PUBLIC_DIR}/guides/${key}.epg.xml`
logger.info(`Creating "${filepath}"...`)
const output = grabber.convertToXMLTV({ channels, programs })
await file.create(filepath, output)
}
logger.info(`Done`)
}
main()
function groupByGroup(items = []) {
const groups = {}
items.forEach(item => {
item.groups.forEach(key => {
if (!groups[key]) {
groups[key] = []
}
groups[key].push(item)
})
})
return groups
}
async function loadQueue() {
logger.info('Loading queue...')
await db.queue.load()
return await db.queue.find({}).sort({ xmltv_id: 1 })
}
async function loadProgramsForItem(item) {
return await db.programs.find({ _qid: item._id }).sort({ channel: 1, start: 1 })
}

View file

@ -1,4 +1,4 @@
const { db, logger, file, parser } = require('../core') const { db, logger, file, parser } = require('../../core')
const _ = require('lodash') const _ = require('lodash')
const LOGS_DIR = process.env.LOGS_DIR || 'scripts/logs' const LOGS_DIR = process.env.LOGS_DIR || 'scripts/logs'
@ -7,7 +7,7 @@ async function main() {
await db.queue.load() await db.queue.load()
await db.programs.load() await db.programs.load()
await db.programs.reset() await db.programs.reset()
const files = await file.list(`${LOGS_DIR}/load-cluster/cluster_*.log`) const files = await file.list(`${LOGS_DIR}/cluster/load/cluster_*.log`)
for (const filepath of files) { for (const filepath of files) {
logger.info(`Parsing "${filepath}"...`) logger.info(`Parsing "${filepath}"...`)
const results = await parser.parseLogs(filepath) const results = await parser.parseLogs(filepath)

View file

@ -1,4 +1,4 @@
const { db, file, parser, logger, date, api } = require('../core') const { db, file, parser, logger, date, api } = require('../../core')
const { program } = require('commander') const { program } = require('commander')
const _ = require('lodash') const _ = require('lodash')
@ -37,53 +37,57 @@ async function createQueue() {
const utcDate = date.getUTC() const utcDate = date.getUTC()
const dates = Array.from({ length: options.days }, (_, i) => utcDate.add(i, 'd')) const dates = Array.from({ length: options.days }, (_, i) => utcDate.add(i, 'd'))
for (const filepath of files) { for (const filepath of files) {
const dir = file.dirname(filepath) try {
const { site, channels: items } = await parser.parseChannels(filepath) const dir = file.dirname(filepath)
if (!site) continue const { site, channels: items } = await parser.parseChannels(filepath)
const configPath = `${dir}/${site}.config.js` if (!site) continue
const config = require(file.resolve(configPath)) const configPath = `${dir}/${site}.config.js`
if (config.ignore) continue const config = require(file.resolve(configPath))
const filename = file.basename(filepath) if (config.ignore) continue
const [__, region] = filename.match(/_([a-z-]+)\.channels\.xml/i) || [null, null] const filename = file.basename(filepath)
const groupId = `${region}/${site}` const [__, region] = filename.match(/_([a-z-]+)\.channels\.xml/i) || [null, null]
for (const item of items) { const groupId = `${region}/${site}`
if (!item.site || !item.site_id || !item.xmltv_id) continue for (const item of items) {
const channel = api.channels.find({ id: item.xmltv_id }) if (!item.site || !item.site_id || !item.xmltv_id) continue
if (!channel) { const channel = api.channels.find({ id: item.xmltv_id })
await logError(groupId, { if (!channel) {
xmltv_id: item.xmltv_id, await logError(groupId, {
site: item.site, xmltv_id: item.xmltv_id,
site_id: item.site_id, site: item.site,
lang: item.lang, site_id: item.site_id,
date: undefined, lang: item.lang,
error: 'The channel has the wrong xmltv_id' date: undefined,
}) error: 'The channel has the wrong xmltv_id'
continue })
} continue
}
for (const d of dates) { for (const d of dates) {
const dString = d.toJSON() const dString = d.toJSON()
const key = `${item.site}:${item.lang}:${item.xmltv_id}:${dString}` const key = `${item.site}:${item.lang}:${item.xmltv_id}:${dString}`
if (!queue[key]) { if (!queue[key]) {
queue[key] = { queue[key] = {
channel: { channel: {
lang: item.lang, lang: item.lang,
xmltv_id: item.xmltv_id, xmltv_id: item.xmltv_id,
display_name: item.name, display_name: item.name,
site_id: item.site_id, site_id: item.site_id,
site: item.site site: item.site
}, },
date: dString, date: dString,
configPath, configPath,
groups: [], groups: [],
error: null error: null
}
}
if (!queue[key].groups.includes(groupId)) {
queue[key].groups.push(groupId)
} }
} }
if (!queue[key].groups.includes(groupId)) {
queue[key].groups.push(groupId)
}
} }
} catch (err) {
console.error(err)
} }
} }

View file

@ -1,4 +1,4 @@
const { file, markdown, parser, logger, api, table } = require('../core') const { file, markdown, parser, logger, api, table } = require('../../core')
const { program } = require('commander') const { program } = require('commander')
const _ = require('lodash') const _ = require('lodash')
@ -10,25 +10,30 @@ const options = program
.opts() .opts()
async function main() { async function main() {
await api.countries.load()
const files = await file.list(CHANNELS_PATH)
const items = [] const items = []
for (const filepath of files) {
const { site, channels } = await parser.parseChannels(filepath)
const dir = file.dirname(filepath)
const config = require(file.resolve(`${dir}/${site}.config.js`))
if (config.ignore) continue
const filename = file.basename(filepath) try {
const [__, suffix] = filename.match(/\_(.*)\.channels\.xml$/) || [null, null] await api.countries.load()
const [code] = suffix.split('-') const files = await file.list(CHANNELS_PATH)
for (const filepath of files) {
const { site, channels } = await parser.parseChannels(filepath)
const dir = file.dirname(filepath)
const config = require(file.resolve(`${dir}/${site}.config.js`))
if (config.ignore) continue
items.push({ const filename = file.basename(filepath)
code, const [__, suffix] = filename.match(/\_(.*)\.channels\.xml$/) || [null, null]
site, const [code] = suffix.split('-')
count: channels.length,
group: `${suffix}/${site}` items.push({
}) code,
site,
count: channels.length,
group: `${suffix}/${site}`
})
}
} catch (err) {
console.error(err)
} }
await generateCountriesTable(items) await generateCountriesTable(items)

View file

@ -1,37 +0,0 @@
const { file, parser, logger } = require('../core')
const { program } = require('commander')
const _ = require('lodash')
const CHANNELS_PATH = process.env.CHANNELS_PATH || 'sites/**/*.channels.xml'
const OUTPUT_DIR = process.env.OUTPUT_DIR || '.gh-pages/api'
async function main() {
const files = await file.list(CHANNELS_PATH)
let guides = []
for (const filepath of files) {
const { site, channels } = await parser.parseChannels(filepath)
const dir = file.dirname(filepath)
const config = require(file.resolve(`${dir}/${site}.config.js`))
if (config.ignore) continue
const filename = file.basename(filepath)
const [__, suffix] = filename.match(/\_(.*)\.channels\.xml$/) || [null, null]
for (const channel of channels) {
guides.push({
channel: channel.xmltv_id,
site,
lang: channel.lang,
url: `https://iptv-org.github.io/epg/guides/${suffix}/${site}.epg.xml`
})
}
}
guides = _.sortBy(guides, 'channel')
const outputFilepath = `${OUTPUT_DIR}/guides.json`
await file.create(outputFilepath, JSON.stringify(guides))
logger.info(`saved to "${outputFilepath}"...`)
}
main()

View file

@ -1,149 +0,0 @@
const { db, logger, file, api } = require('../core')
const grabber = require('epg-grabber')
const _ = require('lodash')
const LOGS_DIR = process.env.LOGS_DIR || 'scripts/logs'
const PUBLIC_DIR = process.env.PUBLIC_DIR || '.gh-pages'
const GUIDES_PATH = `${LOGS_DIR}/guides.log`
async function main() {
await setUp()
await generateGuides()
}
main()
async function generateGuides() {
logger.info(`Generating guides/...`)
logger.info('Loading "database/programs.db"...')
await db.programs.load()
await api.channels.load()
const grouped = groupByGroup(await loadQueue())
for (const key in grouped) {
const filepath = `${PUBLIC_DIR}/guides/${key}.epg.xml`
const criticalErrors = []
let channels = {}
let programs = []
for (const item of grouped[key]) {
const itemPrograms = await loadProgramsForItem(item)
programs = programs.concat(itemPrograms)
if (channels[item.channel.xmltv_id]) continue
if (item.error) {
const error = {
xmltv_id: item.channel.xmltv_id,
site: item.channel.site,
site_id: item.channel.site_id,
lang: item.channel.lang,
date: item.date,
error: item.error
}
criticalErrors.push(error)
await logError(key, error)
} else {
if (!itemPrograms.length) {
await logError(key, {
xmltv_id: item.channel.xmltv_id,
site: item.channel.site,
site_id: item.channel.site_id,
lang: item.channel.lang,
date: item.date,
error: 'Programs not found'
})
continue
}
const channel = api.channels.find({ id: item.channel.xmltv_id })
if (!channel) {
await logError(key, {
xmltv_id: item.channel.xmltv_id,
site: item.channel.site,
site_id: item.channel.site_id,
lang: item.channel.lang,
date: item.date,
error: 'The channel has the wrong xmltv_id'
})
continue
}
channels[channel.id] = {
xmltv_id: channel.id,
name: item.channel.display_name,
logo: channel.logo,
site: item.channel.site
}
}
}
channels = Object.values(channels)
channels = _.sortBy(channels, 'xmltv_id')
programs = _.sortBy(programs, ['channel', 'start'])
logger.info(`Creating "${filepath}"...`)
const output = grabber.convertToXMLTV({ channels, programs })
await file.create(filepath, output)
let status = 0
if (criticalErrors.length > 0 || !channels.length) {
status = 1
}
await logGuide({
group: key,
count: channels.length,
status
})
}
logger.info(`Done`)
}
function groupByGroup(items = []) {
const groups = {}
items.forEach(item => {
item.groups.forEach(key => {
if (!groups[key]) {
groups[key] = []
}
groups[key].push(item)
})
})
return groups
}
async function loadQueue() {
logger.info('Loading queue...')
await db.queue.load()
return await db.queue.find({}).sort({ xmltv_id: 1 })
}
async function loadProgramsForItem(item) {
return await db.programs.find({ _qid: item._id }).sort({ channel: 1, start: 1 })
}
async function setUp() {
logger.info(`Creating '${GUIDES_PATH}'...`)
await file.create(GUIDES_PATH)
await file.createDir(`${LOGS_DIR}/errors`)
}
async function logGuide(data) {
await file.append(GUIDES_PATH, JSON.stringify(data) + '\r\n')
}
async function logError(key, data) {
const filepath = `${LOGS_DIR}/errors/${key}.log`
if (!(await file.exists(filepath))) {
await file.create(filepath)
}
await file.append(filepath, JSON.stringify(data) + '\r\n')
}

View file

@ -1,6 +1,6 @@
const path = require('path') const path = require('path')
const glob = require('glob') const glob = require('glob')
const fs = require('mz/fs') const fs = require('fs-extra')
const file = {} const file = {}

View file

@ -7,3 +7,4 @@ exports.markdown = require('./markdown')
exports.api = require('./api') exports.api = require('./api')
exports.date = require('./date') exports.date = require('./date')
exports.table = require('./table') exports.table = require('./table')
exports.xml = require('./xml')

View file

@ -1,61 +1,6 @@
const { Command } = require('commander') const xml = {}
const { db, logger } = require('../core')
const path = require('path')
const _ = require('lodash')
const fs = require('fs')
const program = new Command() xml.create = function (items, site) {
program
.requiredOption('-c, --config <config>', 'Config file')
.option('-s, --set [args...]', 'Set custom arguments', [])
.option('-o, --output <output>', 'Output file')
.parse(process.argv)
const options = program.opts()
async function main() {
await db.channels.load()
const config = require(path.resolve(options.config))
const args = {}
options.set.forEach(arg => {
const [key, value] = arg.split(':')
args[key] = value
})
let channels = config.channels(args)
if (isPromise(channels)) {
channels = await channels
}
channels = _.uniqBy(channels, 'site_id')
const siteChannels = await db.channels.find({ site: config.site })
for (const channel of channels) {
if (channel.xmltv_id) continue
const data = siteChannels.find(c => c.site_id === channel.site_id.toString())
if (data) {
channel.xmltv_id = data.xmltv_id
channel.name = data.name
}
}
channels = _.sortBy(channels, 'xmltv_id')
const xml = json2xml(channels, config.site)
const dir = path.parse(options.config).dir
const output = options.output || `${dir}/${config.site}.channels.xml`
fs.writeFileSync(path.resolve(output), xml)
logger.info(`File '${output}' successfully saved`)
}
main()
function isPromise(promise) {
return !!promise && typeof promise.then === 'function'
}
function json2xml(items, site) {
let output = `<?xml version="1.0" encoding="UTF-8"?>\r\n<site site="${site}">\r\n <channels>\r\n` let output = `<?xml version="1.0" encoding="UTF-8"?>\r\n<site site="${site}">\r\n <channels>\r\n`
items.forEach(channel => { items.forEach(channel => {
@ -101,4 +46,4 @@ function escapeString(string, defaultValue = '') {
.trim() .trim()
} }
module.exports = { json2xml } module.exports = xml

View file

@ -252,7 +252,7 @@
<channel lang="en" xmltv_id="PenthouseTV.us" site_id="595">Penthouse TV</channel> <channel lang="en" xmltv_id="PenthouseTV.us" site_id="595">Penthouse TV</channel>
<channel lang="en" xmltv_id="PenthouseTVMonthlyOffer.us" site_id="587">Penthouse TV Monthly Offer</channel> <channel lang="en" xmltv_id="PenthouseTVMonthlyOffer.us" site_id="587">Penthouse TV Monthly Offer</channel>
<channel lang="en" xmltv_id="PeruMagico.pe" site_id="431">Perú Mágico</channel> <channel lang="en" xmltv_id="PeruMagico.pe" site_id="431">Perú Mágico</channel>
<channel lang="en" xmltv_id="PerviykanalAmerica.ru" site_id="2140">Perviy kanal America</channel> <channel lang="en" xmltv_id="PervyykanalAmerica.ru" site_id="2140">Pervyy kanal America</channel>
<channel lang="en" xmltv_id="PhoenixHongKong.hk" site_id="2104">Phoenix Hong Kong</channel> <channel lang="en" xmltv_id="PhoenixHongKong.hk" site_id="2104">Phoenix Hong Kong</channel>
<channel lang="en" xmltv_id="PhoenixInfoNewsChannel.hk" site_id="2051">Phoenix InfoNews Channel</channel> <channel lang="en" xmltv_id="PhoenixInfoNewsChannel.hk" site_id="2051">Phoenix InfoNews Channel</channel>
<channel lang="en" xmltv_id="PhoenixNorthAmericaChineseChannel.hk" site_id="2050">Phoenix North America Chinese Channel</channel> <channel lang="en" xmltv_id="PhoenixNorthAmericaChineseChannel.hk" site_id="2050">Phoenix North America Chinese Channel</channel>

View file

@ -298,7 +298,7 @@
<channel lang="cz" xmltv_id="ParkTV.sk" site_id="Park+TV">Park TV</channel> <channel lang="cz" xmltv_id="ParkTV.sk" site_id="Park+TV">Park TV</channel>
<channel lang="cz" xmltv_id="PassionXXX.nl" site_id="Passion+XXX">Passion XXX</channel> <channel lang="cz" xmltv_id="PassionXXX.nl" site_id="Passion+XXX">Passion XXX</channel>
<channel lang="cz" xmltv_id="PaxTV.hu" site_id="Pax+TV">Pax TV</channel> <channel lang="cz" xmltv_id="PaxTV.hu" site_id="Pax+TV">Pax TV</channel>
<channel lang="cz" xmltv_id="Perviykanal.ru" site_id="1TV">Perviy kanal</channel> <channel lang="cz" xmltv_id="Pervyykanal.ru" site_id="1TV">Perviy kanal</channel>
<channel lang="cz" xmltv_id="Phoenix.de" site_id="Phoenix">Phoenix</channel> <channel lang="cz" xmltv_id="Phoenix.de" site_id="Phoenix">Phoenix</channel>
<channel lang="cz" xmltv_id="PickUK.uk" site_id="Pick+TV">Pick UK</channel> <channel lang="cz" xmltv_id="PickUK.uk" site_id="Pick+TV">Pick UK</channel>
<channel lang="cz" xmltv_id="PlayboyTVEurope.us" site_id="Playboy+TV">Playboy TV Europe</channel> <channel lang="cz" xmltv_id="PlayboyTVEurope.us" site_id="Playboy+TV">Playboy TV Europe</channel>

View file

@ -80,7 +80,7 @@
<channel lang="ru" xmltv_id="ParamountChannelRussia.us" site_id="172">Paramount Channel Russia</channel> <channel lang="ru" xmltv_id="ParamountChannelRussia.us" site_id="172">Paramount Channel Russia</channel>
<channel lang="ru" xmltv_id="ParamountComedyRussia.us" site_id="173">Paramount Comedy Russia</channel> <channel lang="ru" xmltv_id="ParamountComedyRussia.us" site_id="173">Paramount Comedy Russia</channel>
<channel lang="ru" xmltv_id="PeretzInternational.ru" site_id="220">Peretz International</channel> <channel lang="ru" xmltv_id="PeretzInternational.ru" site_id="220">Peretz International</channel>
<channel lang="ru" xmltv_id="PerviykanalCIS.ru" site_id="5">Perviy kanal CIS</channel> <channel lang="ru" xmltv_id="PervyykanalCIS.ru" site_id="5">Perviy kanal CIS</channel>
<channel lang="ka" xmltv_id="PosTV.ge" site_id="65000">Pos TV</channel> <channel lang="ka" xmltv_id="PosTV.ge" site_id="65000">Pos TV</channel>
<channel lang="ru" xmltv_id="PyatnitsaInternational.ru" site_id="124">Pyatnitsa! International</channel> <channel lang="ru" xmltv_id="PyatnitsaInternational.ru" site_id="124">Pyatnitsa! International</channel>
<channel lang="ka" xmltv_id="QartuliArkhi.ge" site_id="195">Qartuli Arkhi</channel> <channel lang="ka" xmltv_id="QartuliArkhi.ge" site_id="195">Qartuli Arkhi</channel>

View file

@ -120,7 +120,7 @@
<channel lang="pt" xmltv_id="PFCInternacional.br" site_id="PFC">PFC Internacional</channel> <channel lang="pt" xmltv_id="PFCInternacional.br" site_id="PFC">PFC Internacional</channel>
<channel lang="pt" xmltv_id="PandaKids.pt" site_id="PANDAK">Panda Kids</channel> <channel lang="pt" xmltv_id="PandaKids.pt" site_id="PANDAK">Panda Kids</channel>
<channel lang="pt" xmltv_id="PenthouseGold.us" site_id="PENTHG">Penthouse Gold</channel> <channel lang="pt" xmltv_id="PenthouseGold.us" site_id="PENTHG">Penthouse Gold</channel>
<channel lang="pt" xmltv_id="PerviykanalEuropa.ru" site_id="1RUSS">Perviy kanal Europa</channel> <channel lang="pt" xmltv_id="PervyykanalEuropa.ru" site_id="1RUSS">Perviy kanal Europa</channel>
<channel lang="pt" xmltv_id="PhoenixCNE.hk" site_id="PHCNE">Phoenix CNE</channel> <channel lang="pt" xmltv_id="PhoenixCNE.hk" site_id="PHCNE">Phoenix CNE</channel>
<channel lang="pt" xmltv_id="PlayboyTVEurope.us" site_id="PLAY">Playboy TV Europe</channel> <channel lang="pt" xmltv_id="PlayboyTVEurope.us" site_id="PLAY">Playboy TV Europe</channel>
<channel lang="pt" xmltv_id="PortoCanal.pt" site_id="PORTO">Porto Canal</channel> <channel lang="pt" xmltv_id="PortoCanal.pt" site_id="PORTO">Porto Canal</channel>

View file

@ -17,9 +17,9 @@
<channel lang="fr" xmltv_id="BBCFour.uk" site_id="bbc4-247">BBC Four</channel> <channel lang="fr" xmltv_id="BBCFour.uk" site_id="bbc4-247">BBC Four</channel>
<channel lang="fr" xmltv_id="BBCOne.uk" site_id="bbc-1-230">BBC One</channel> <channel lang="fr" xmltv_id="BBCOne.uk" site_id="bbc-1-230">BBC One</channel>
<channel lang="fr" xmltv_id="BBCTwo.uk" site_id="bbc-2-231">BBC Two</channel> <channel lang="fr" xmltv_id="BBCTwo.uk" site_id="bbc-2-231">BBC Two</channel>
<channel lang="fr" xmltv_id="BeInSports1.qa" site_id="bein-sports-1-183">BeIn Sports 1</channel> <channel lang="fr" xmltv_id="BeInSports1France.qa" site_id="bein-sports-1-183">BeIn Sports 1 France</channel>
<channel lang="fr" xmltv_id="BeInSports2.qa" site_id="bein-sports-2-184">BeIn Sports 2</channel> <channel lang="fr" xmltv_id="BeInSports2France.qa" site_id="bein-sports-2-184">BeIn Sports 2 France</channel>
<channel lang="fr" xmltv_id="BeInSports3.qa" site_id="bein-sports-3-265">BeIn Sports 3</channel> <channel lang="fr" xmltv_id="BeInSports3France.qa" site_id="bein-sports-3-265">BeIn Sports 3 France</channel>
<channel lang="fr" xmltv_id="BeInSportsMax10France.qa" site_id="bein-sports-max-10-305">BeIn Sports Max 10 France</channel> <channel lang="fr" xmltv_id="BeInSportsMax10France.qa" site_id="bein-sports-max-10-305">BeIn Sports Max 10 France</channel>
<channel lang="fr" xmltv_id="BeInSportsMax4France.qa" site_id="bein-sports-max-4-300">BeIn Sports Max 4 France</channel> <channel lang="fr" xmltv_id="BeInSportsMax4France.qa" site_id="bein-sports-max-4-300">BeIn Sports Max 4 France</channel>
<channel lang="fr" xmltv_id="BeInSportsMax5France.qa" site_id="bein-sports-max-5-301">BeIn Sports Max 5 France</channel> <channel lang="fr" xmltv_id="BeInSportsMax5France.qa" site_id="bein-sports-max-5-301">BeIn Sports Max 5 France</channel>

View file

@ -198,7 +198,7 @@
<channel lang="pl" xmltv_id="ORF1.at" site_id="orf-1-390">ORF 1</channel> <channel lang="pl" xmltv_id="ORF1.at" site_id="orf-1-390">ORF 1</channel>
<channel lang="pl" xmltv_id="ORF2.at" site_id="orf-2-393">ORF 2</channel> <channel lang="pl" xmltv_id="ORF2.at" site_id="orf-2-393">ORF 2</channel>
<channel lang="pl" xmltv_id="ParamountChannelPolska.us" site_id="paramount-channel-hd-65">Paramount Channel Polska</channel> <channel lang="pl" xmltv_id="ParamountChannelPolska.us" site_id="paramount-channel-hd-65">Paramount Channel Polska</channel>
<channel lang="pl" xmltv_id="PerviykanalEuropa.ru" site_id="channel-one-russia-276">Perviy kanal Europa</channel> <channel lang="pl" xmltv_id="PervyykanalEuropa.ru" site_id="channel-one-russia-276">Perviy kanal Europa</channel>
<channel lang="pl" xmltv_id="Phoenix.de" site_id="phoenix-391">Phoenix</channel> <channel lang="pl" xmltv_id="Phoenix.de" site_id="phoenix-391">Phoenix</channel>
<channel lang="pl" xmltv_id="PlanetePlusPolska.fr" site_id="planete-349">Planete + Polska</channel> <channel lang="pl" xmltv_id="PlanetePlusPolska.fr" site_id="planete-349">Planete + Polska</channel>
<channel lang="pl" xmltv_id="PlayboyTVEurope.us" site_id="playboy-tv-482">Playboy TV Europe</channel> <channel lang="pl" xmltv_id="PlayboyTVEurope.us" site_id="playboy-tv-482">Playboy TV Europe</channel>

View file

@ -0,0 +1,45 @@
const dayjs = require('dayjs')
module.exports = {
site: 'sky.de',
url: `https://www.sky.de/sgtvg/service/getBroadcastsForGrid`,
request: {
method: 'POST',
data: function ({ channel, date }) {
return {
cil: [channel.site_id],
d: date.valueOf()
}
}
},
parser: function ({ content, channel }) {
const programs = []
const items = parseItems(content, channel)
items.forEach(item => {
programs.push({
title: item.et,
description: item.epit,
category: item.ec,
start: dayjs(item.bsdt),
stop: dayjs(item.bedt),
season: item.sn,
episode: item.en,
icon: item.pu ? `http://sky.de${item.pu}` : null
})
})
return programs
}
}
function parseContent(content, channel) {
const json = JSON.parse(content)
if (!Array.isArray(json.cl)) return null
return json.cl.find(i => i.ci == channel.site_id)
}
function parseItems(content, channel) {
const data = parseContent(content, channel)
return data && Array.isArray(data.el) ? data.el : []
}

View file

@ -0,0 +1,55 @@
const { parser, url } = require('./sky.de.config.js')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
dayjs.extend(utc)
const date = dayjs.utc('2022-02-28', 'YYYY-MM-DD').startOf('d')
const channel = {
site_id: '522',
xmltv_id: 'WarnerTVComedyDeutschlandHD.us'
}
const content = `{"cl":[{"ci":522,"el":[{"ei":122309300,"bsdt":1645916700000,"bst":"00:05","bedt":1645918200000,"len":25,"et":"King of Queens","ec":"Comedyserie","cop":"USA","yop":2001,"fsk":"ab 0 Jahre","epit":"Der Experte","sn":"4","en":"11","pu":"/static/img/program_guide/1522936_s.jpg"},{"ei":122309301,"bsdt":1645918200000,"bst":"00:30","bedt":1645919700000,"len":25,"et":"King of Queens","ec":"Comedyserie","cop":"USA","yop":2001,"fsk":"ab 0 Jahre","epit":"Speedy Gonzales","sn":"4","en":"12","pu":"/static/img/program_guide/1522937_s.jpg"}]}]}`
it('can generate valid url', () => {
expect(url).toBe('https://www.sky.de/sgtvg/service/getBroadcastsForGrid')
})
it('can parse response', () => {
const result = parser({ content, channel }).map(p => {
p.start = p.start.toJSON()
p.stop = p.stop.toJSON()
return p
})
expect(result).toMatchObject([
{
title: 'King of Queens',
description: 'Der Experte',
category: 'Comedyserie',
start: '2022-02-26T23:05:00.000Z',
stop: '2022-02-26T23:30:00.000Z',
season: '4',
episode: '11',
icon: 'http://sky.de/static/img/program_guide/1522936_s.jpg'
},
{
title: 'King of Queens',
description: 'Speedy Gonzales',
category: 'Comedyserie',
start: '2022-02-26T23:30:00.000Z',
stop: '2022-02-26T23:55:00.000Z',
season: '4',
episode: '12',
icon: 'http://sky.de/static/img/program_guide/1522937_s.jpg'
}
])
})
it('can handle empty guide', () => {
const result = parser({
content: `[]`
})
expect(result).toMatchObject([])
})

View file

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="UTF-8"?>
<site site="sky.de">
<channels>
<channel lang="de" xmltv_id="13thStreetDeutschland.us" site_id="116">13th Street Deutschland</channel>
<channel lang="de" xmltv_id="BeateUhseTV.de" site_id="690">Beate Uhse TV</channel>
<channel lang="de" xmltv_id="BoomerangDeutschland.us" site_id="761">Boomerang Deutschland</channel>
<channel lang="de" xmltv_id="CartoonNetworkDeutschland.us" site_id="95">Cartoon Network Deutschland</channel>
<channel lang="de" xmltv_id="ClassicaHD.de" site_id="99">Classica HD</channel>
<channel lang="de" xmltv_id="CrimePlusInvestigationDeutschland.us" site_id="451">Crime + Investigation Deutschland</channel>
<channel lang="de" xmltv_id="DAZN1Deutschland.uk" site_id="659">DAZN 1 Deutschland</channel>
<channel lang="de" xmltv_id="DAZN2Deutschland.uk" site_id="900">DAZN 2 Deutschland</channel>
<channel lang="de" xmltv_id="DiscoveryChannelDeutschland.us" site_id="83">Discovery Channel Deutschland</channel>
<channel lang="de" xmltv_id="EEurope.us" site_id="117">E! Europe</channel>
<channel lang="de" xmltv_id="Eurosport1Germany.fr" site_id="596">Eurosport 1 Germany</channel>
<channel lang="de" xmltv_id="Eurosport2Germany.fr" site_id="161">Eurosport 2 Germany</channel>
<channel lang="de" xmltv_id="Eurosport360HD1.fr" site_id="163">Eurosport360 HD 1</channel>
<channel lang="de" xmltv_id="Eurosport360HD2.fr" site_id="164">Eurosport360 HD 2</channel>
<channel lang="de" xmltv_id="Eurosport360HD3.fr" site_id="165">Eurosport360 HD 3</channel>
<channel lang="de" xmltv_id="Eurosport360HD4.fr" site_id="166">Eurosport360 HD 4</channel>
<channel lang="de" xmltv_id="Eurosport360HD5.fr" site_id="167">Eurosport360 HD 5</channel>
<channel lang="de" xmltv_id="Eurosport360HD6.fr" site_id="168">Eurosport360 HD 6</channel>
<channel lang="de" xmltv_id="Eurosport360HD7.fr" site_id="169">Eurosport360 HD 7</channel>
<channel lang="de" xmltv_id="Eurosport360HD8.fr" site_id="170">Eurosport360 HD 8</channel>
<channel lang="de" xmltv_id="Eurosport360HD9.fr" site_id="171">Eurosport360 HD 9</channel>
<channel lang="de" xmltv_id="Heimatkanal.de" site_id="72">Heimatkanal</channel>
<channel lang="de" xmltv_id="HistoryDeutschland.us" site_id="86">History Deutschland</channel>
<channel lang="de" xmltv_id="Jukebox.de" site_id="452">Jukebox</channel>
<channel lang="de" xmltv_id="Junior.de" site_id="96">Junior</channel>
<channel lang="de" xmltv_id="KinoweltTV.de" site_id="70">Kinowelt TV</channel>
<channel lang="de" xmltv_id="MotorvisionTV.de" site_id="52">Motorvision TV</channel>
<channel lang="de" xmltv_id="NationalGeographicDeutschland.us" site_id="81">National Geographic Deutschland</channel>
<channel lang="de" xmltv_id="NationalGeographicWildDeutschland.us" site_id="88">National Geographic Wild Deutschland</channel>
<channel lang="de" xmltv_id="NickJrDeutschland.us" site_id="783">Nick Jr Deutschland</channel>
<channel lang="de" xmltv_id="NicktoonsDeutschland.us" site_id="798">Nicktoons Deutschland</channel>
<channel lang="de" xmltv_id="RomanceTVDeutschland.de" site_id="16">Romance TV Deutschland</channel>
<channel lang="de" xmltv_id="SkyAtlantic.de" site_id="2">Sky Atlantic</channel>
<channel lang="de" xmltv_id="SkyCinemaAction.de" site_id="59">Sky Cinema Action</channel>
<channel lang="de" xmltv_id="SkyCinemaActionHD.de" site_id="60">Sky Cinema Action HD</channel>
<channel lang="de" xmltv_id="SkyCinemaBestOf.de" site_id="790">Sky Cinema Best Of</channel>
<channel lang="de" xmltv_id="SkyCinemaBestOfHD.de" site_id="792">Sky Cinema Best Of HD</channel>
<channel lang="de" xmltv_id="SkyCinemaClassics.de" site_id="794">Sky Cinema Classics</channel>
<channel lang="de" xmltv_id="SkyCinemaFamily.de" site_id="532">Sky Cinema Family</channel>
<channel lang="de" xmltv_id="SkyCinemaFamilyHD.de" site_id="533">Sky Cinema Family HD</channel>
<channel lang="de" xmltv_id="SkyCinemaFun.de" site_id="793">Sky Cinema Fun</channel>
<channel lang="de" xmltv_id="SkyCinemaPremieren.de" site_id="787">Sky Cinema Premieren</channel>
<channel lang="de" xmltv_id="SkyCinemaPremierenPlus24.de" site_id="789">Sky Cinema Premieren +24</channel>
<channel lang="de" xmltv_id="SkyCinemaPremierenPlus24HD.de" site_id="791">Sky Cinema Premieren +24 HD</channel>
<channel lang="de" xmltv_id="SkyCinemaPremierenHD.de" site_id="788">Sky Cinema Premieren HD</channel>
<channel lang="de" xmltv_id="SkyCinemaSpecial.de" site_id="890">Sky Cinema Special</channel>
<channel lang="de" xmltv_id="SkyCinemaThriller.de" site_id="784">Sky Cinema Thriller</channel>
<channel lang="de" xmltv_id="SkyComedy.de" site_id="854">Sky Comedy</channel>
<channel lang="de" xmltv_id="SkyCrime.de" site_id="855">Sky Crime</channel>
<channel lang="de" xmltv_id="SkyDocumentariesDeutschland.de" site_id="876">Sky Documentaries Deutschland</channel>
<channel lang="de" xmltv_id="SkyKrimi.de" site_id="753">Sky Krimi HD</channel>
<channel lang="de" xmltv_id="SkyNatureDeutschland.de" site_id="875">Sky Nature Deutschland</channel>
<channel lang="de" xmltv_id="SkyOne.de" site_id="535">Sky One</channel>
<channel lang="de" xmltv_id="SkyReplayDeutschland.de" site_id="888">Sky Replay Deutschland</channel>
<channel lang="de" xmltv_id="SkySport1.de" site_id="149">Sky Sport 1</channel>
<channel lang="de" xmltv_id="SkySport10.de" site_id="158">Sky Sport 10</channel>
<channel lang="de" xmltv_id="SkySport11.de" site_id="159">Sky Sport 11</channel>
<channel lang="de" xmltv_id="SkySport2.de" site_id="150">Sky Sport 2</channel>
<channel lang="de" xmltv_id="SkySport3.de" site_id="151">Sky Sport 3</channel>
<channel lang="de" xmltv_id="SkySport4.de" site_id="152">Sky Sport 4</channel>
<channel lang="de" xmltv_id="SkySport5.de" site_id="153">Sky Sport 5</channel>
<channel lang="de" xmltv_id="SkySport6.de" site_id="154">Sky Sport 6</channel>
<channel lang="de" xmltv_id="SkySport7.de" site_id="155">Sky Sport 7</channel>
<channel lang="de" xmltv_id="SkySport8.de" site_id="156">Sky Sport 8</channel>
<channel lang="de" xmltv_id="SkySport9.de" site_id="157">Sky Sport 9</channel>
<channel lang="de" xmltv_id="SkySport1HD.de" site_id="128">Sky Sport 1 HD</channel>
<channel lang="de" xmltv_id="SkySport10HD.de" site_id="137">Sky Sport 10 HD</channel>
<channel lang="de" xmltv_id="SkySport11HD.de" site_id="138">Sky Sport 11 HD</channel>
<channel lang="de" xmltv_id="SkySport2HD.de" site_id="129">Sky Sport 2 HD</channel>
<channel lang="de" xmltv_id="SkySport3HD.de" site_id="130">Sky Sport 3 HD</channel>
<channel lang="de" xmltv_id="SkySport4HD.de" site_id="131">Sky Sport 4 HD</channel>
<channel lang="de" xmltv_id="SkySport5HD.de" site_id="132">Sky Sport 5 HD</channel>
<channel lang="de" xmltv_id="SkySport6HD.de" site_id="133">Sky Sport 6 HD</channel>
<channel lang="de" xmltv_id="SkySport7HD.de" site_id="134">Sky Sport 7 HD</channel>
<channel lang="de" xmltv_id="SkySport8HD.de" site_id="135">Sky Sport 8 HD</channel>
<channel lang="de" xmltv_id="SkySport9HD.de" site_id="136">Sky Sport 9 HD</channel>
<channel lang="de" xmltv_id="SkySportAustria1.de" site_id="492">Sky Sport Austria 1</channel>
<channel lang="de" xmltv_id="SkySportAustria2.de" site_id="729">Sky Sport Austria 2</channel>
<channel lang="de" xmltv_id="SkySportAustria3.de" site_id="730">Sky Sport Austria 3</channel>
<channel lang="de" xmltv_id="SkySportAustria4.de" site_id="898">Sky Sport Austria 4</channel>
<channel lang="de" xmltv_id="SkySportAustria5.de" site_id="883">Sky Sport Austria 5</channel>
<channel lang="de" xmltv_id="SkySportAustria6.de" site_id="884">Sky Sport Austria 6</channel>
<channel lang="de" xmltv_id="SkySportAustria7.de" site_id="885">Sky Sport Austria 7</channel>
<channel lang="de" xmltv_id="SkySportBundesliga1.de" site_id="139">Sky Sport Bundesliga 1</channel>
<channel lang="de" xmltv_id="SkySportBundesliga1HD.de" site_id="118">Sky Sport Bundesliga 1 HD</channel>
<channel lang="de" xmltv_id="SkySportBundesliga10.de" site_id="148">Sky Sport Bundesliga 10</channel>
<channel lang="de" xmltv_id="SkySportBundesliga10HD.de" site_id="127">Sky Sport Bundesliga 10 HD</channel>
<channel lang="de" xmltv_id="SkySportBundesliga2.de" site_id="140">Sky Sport Bundesliga 2</channel>
<channel lang="de" xmltv_id="SkySportBundesliga2HD.de" site_id="119">Sky Sport Bundesliga 2 HD</channel>
<channel lang="de" xmltv_id="SkySportBundesliga3.de" site_id="141">Sky Sport Bundesliga 3</channel>
<channel lang="de" xmltv_id="SkySportBundesliga3HD.de" site_id="120">Sky Sport Bundesliga 3 HD</channel>
<channel lang="de" xmltv_id="SkySportBundesliga4.de" site_id="142">Sky Sport Bundesliga 4</channel>
<channel lang="de" xmltv_id="SkySportBundesliga4HD.de" site_id="121">Sky Sport Bundesliga 4 HD</channel>
<channel lang="de" xmltv_id="SkySportBundesliga5.de" site_id="143">Sky Sport Bundesliga 5</channel>
<channel lang="de" xmltv_id="SkySportBundesliga5HD.de" site_id="122">Sky Sport Bundesliga 5 HD</channel>
<channel lang="de" xmltv_id="SkySportBundesliga6.de" site_id="144">Sky Sport Bundesliga 6</channel>
<channel lang="de" xmltv_id="SkySportBundesliga6HD.de" site_id="123">Sky Sport Bundesliga 6 HD</channel>
<channel lang="de" xmltv_id="SkySportBundesliga7.de" site_id="145">Sky Sport Bundesliga 7</channel>
<channel lang="de" xmltv_id="SkySportBundesliga7HD.de" site_id="124">Sky Sport Bundesliga 7 HD</channel>
<channel lang="de" xmltv_id="SkySportBundesliga8.de" site_id="146">Sky Sport Bundesliga 8</channel>
<channel lang="de" xmltv_id="SkySportBundesliga8HD.de" site_id="125">Sky Sport Bundesliga 8 HD</channel>
<channel lang="de" xmltv_id="SkySportBundesliga9.de" site_id="147">Sky Sport Bundesliga 9</channel>
<channel lang="de" xmltv_id="SkySportBundesliga9HD.de" site_id="126">Sky Sport Bundesliga 9 HD</channel>
<channel lang="de" xmltv_id="SkySportBundesligaUHD.de" site_id="531">Sky Sport Bundesliga UHD</channel>
<channel lang="de" xmltv_id="SkySportF1.de" site_id="853">Sky Sport F1</channel>
<channel lang="de" xmltv_id="SkySportNews.de" site_id="17">Sky Sport News</channel>
<channel lang="de" xmltv_id="SkySportUHD.de" site_id="526">Sky Sport UHD</channel>
<channel lang="de" xmltv_id="SpiegelGeschichte.de" site_id="84">Spiegel Geschichte</channel>
<channel lang="de" xmltv_id="SpiegelGeschichteHD.de" site_id="388">Spiegel Geschichte HD</channel>
<channel lang="de" xmltv_id="SpiegelTVWissen.de" site_id="658">Spiegel TV Wissen</channel>
<channel lang="de" xmltv_id="SportdigitalFussball.de" site_id="114">Sportdigital Fussball</channel>
<channel lang="de" xmltv_id="SyfyDeutschland.us" site_id="115">Syfy Deutschland</channel>
<channel lang="de" xmltv_id="UniversalTVDeutschland.us" site_id="172">Universal TV Deutschland</channel>
<channel lang="de" xmltv_id="WarnerTVComedyDeutschland.us" site_id="674">Warner TV Comedy Deutschland</channel>
<channel lang="de" xmltv_id="WarnerTVComedyDeutschlandHD.us" site_id="522">Warner TV Comedy Deutschland HD</channel>
<channel lang="de" xmltv_id="WarnerTVFilmDeutschland.us" site_id="762">Warner TV Film Deutschland</channel>
<channel lang="de" xmltv_id="WarnerTVFilmDeutschlandHD.us" site_id="689">Warner TV Film Deutschland HD</channel>
<channel lang="de" xmltv_id="WarnerTVSerieDeutschland.us" site_id="6">Warner TV Serie Deutschland</channel>
</channels>
</site>

View file

@ -84,7 +84,7 @@
<channel lang="en" xmltv_id="NTVSerial.ru" site_id="1289">NTV Serial</channel> <channel lang="en" xmltv_id="NTVSerial.ru" site_id="1289">NTV Serial</channel>
<channel lang="en" xmltv_id="OkhotaiRybalka.ru" site_id="74">Okhota i Rybalka</channel> <channel lang="en" xmltv_id="OkhotaiRybalka.ru" site_id="74">Okhota i Rybalka</channel>
<channel lang="en" xmltv_id="OrsentTV.ee" site_id="141">Orsent TV</channel> <channel lang="en" xmltv_id="OrsentTV.ee" site_id="141">Orsent TV</channel>
<channel lang="en" xmltv_id="PerviyBaltijskyiKanal.ru" site_id="28">Perviy Baltijskyi Kanal</channel> <channel lang="en" xmltv_id="PervyyBaltijskyiKanal.ru" site_id="28">Perviy Baltijskyi Kanal</channel>
<channel lang="en" xmltv_id="PrivateTV.nl" site_id="148">Private TV</channel> <channel lang="en" xmltv_id="PrivateTV.nl" site_id="148">Private TV</channel>
<channel lang="en" xmltv_id="ProSiebenDeutschland.de" site_id="60">ProSieben Deutschland</channel> <channel lang="en" xmltv_id="ProSiebenDeutschland.de" site_id="60">ProSieben Deutschland</channel>
<channel lang="en" xmltv_id="PyatnitsaInternational.ru" site_id="1304">Pyatnitsa! International</channel> <channel lang="en" xmltv_id="PyatnitsaInternational.ru" site_id="1304">Pyatnitsa! International</channel>

View file

@ -84,7 +84,7 @@
<channel lang="et" xmltv_id="NTVSerial.ru" site_id="1289">NTV Serial</channel> <channel lang="et" xmltv_id="NTVSerial.ru" site_id="1289">NTV Serial</channel>
<channel lang="et" xmltv_id="OkhotaiRybalka.ru" site_id="74">Okhota i Rybalka</channel> <channel lang="et" xmltv_id="OkhotaiRybalka.ru" site_id="74">Okhota i Rybalka</channel>
<channel lang="et" xmltv_id="OrsentTV.ee" site_id="141">Orsent TV</channel> <channel lang="et" xmltv_id="OrsentTV.ee" site_id="141">Orsent TV</channel>
<channel lang="et" xmltv_id="PerviyBaltijskyiKanal.ru" site_id="28">Perviy Baltijskyi Kanal</channel> <channel lang="et" xmltv_id="PervyyBaltijskyiKanal.ru" site_id="28">Perviy Baltijskyi Kanal</channel>
<channel lang="et" xmltv_id="PrivateTV.nl" site_id="148">Private TV</channel> <channel lang="et" xmltv_id="PrivateTV.nl" site_id="148">Private TV</channel>
<channel lang="et" xmltv_id="ProSiebenDeutschland.de" site_id="60">ProSieben Deutschland</channel> <channel lang="et" xmltv_id="ProSiebenDeutschland.de" site_id="60">ProSieben Deutschland</channel>
<channel lang="et" xmltv_id="PyatnitsaInternational.ru" site_id="1304">Pyatnitsa! International</channel> <channel lang="et" xmltv_id="PyatnitsaInternational.ru" site_id="1304">Pyatnitsa! International</channel>

View file

@ -84,7 +84,7 @@
<channel lang="ru" xmltv_id="NTVSerial.ru" site_id="1289">NTV Serial</channel> <channel lang="ru" xmltv_id="NTVSerial.ru" site_id="1289">NTV Serial</channel>
<channel lang="ru" xmltv_id="OkhotaiRybalka.ru" site_id="74">Okhota i Rybalka</channel> <channel lang="ru" xmltv_id="OkhotaiRybalka.ru" site_id="74">Okhota i Rybalka</channel>
<channel lang="ru" xmltv_id="OrsentTV.ee" site_id="141">Orsent TV</channel> <channel lang="ru" xmltv_id="OrsentTV.ee" site_id="141">Orsent TV</channel>
<channel lang="ru" xmltv_id="PerviyBaltijskyiKanal.ru" site_id="28">Perviy Baltijskyi Kanal</channel> <channel lang="ru" xmltv_id="PervyyBaltijskyiKanal.ru" site_id="28">Perviy Baltijskyi Kanal</channel>
<channel lang="ru" xmltv_id="PrivateTV.nl" site_id="148">Private TV</channel> <channel lang="ru" xmltv_id="PrivateTV.nl" site_id="148">Private TV</channel>
<channel lang="ru" xmltv_id="ProSiebenDeutschland.de" site_id="60">ProSieben Deutschland</channel> <channel lang="ru" xmltv_id="ProSiebenDeutschland.de" site_id="60">ProSieben Deutschland</channel>
<channel lang="ru" xmltv_id="PyatnitsaInternational.ru" site_id="1304">Pyatnitsa! International</channel> <channel lang="ru" xmltv_id="PyatnitsaInternational.ru" site_id="1304">Pyatnitsa! International</channel>

View file

@ -546,7 +546,7 @@
<channel lang="ru" xmltv_id="KaruselInternational.ru" site_id="1676">Karusel International</channel> <channel lang="ru" xmltv_id="KaruselInternational.ru" site_id="1676">Karusel International</channel>
<channel lang="ru" xmltv_id="NashKinomir.de" site_id="229">Nash Kinomir</channel> <channel lang="ru" xmltv_id="NashKinomir.de" site_id="229">Nash Kinomir</channel>
<channel lang="ru" xmltv_id="OstWest.de" site_id="922">Ost West</channel> <channel lang="ru" xmltv_id="OstWest.de" site_id="922">Ost West</channel>
<channel lang="ru" xmltv_id="PerviykanalEuropa.ru" site_id="90">Perviy kanal Europa</channel> <channel lang="ru" xmltv_id="PervyykanalEuropa.ru" site_id="90">Perviy kanal Europa</channel>
<channel lang="ru" xmltv_id="RTRPlaneta.ru" site_id="366">RTR Planeta</channel> <channel lang="ru" xmltv_id="RTRPlaneta.ru" site_id="366">RTR Planeta</channel>
<channel lang="ru" xmltv_id="TelebomTeledom.de" site_id="587">Telebom &amp; Teledom</channel> <channel lang="ru" xmltv_id="TelebomTeledom.de" site_id="587">Telebom &amp; Teledom</channel>
<channel lang="sq" xmltv_id="ABCNews.al" site_id="1880">ABC News</channel> <channel lang="sq" xmltv_id="ABCNews.al" site_id="1880">ABC News</channel>

View file

@ -83,7 +83,7 @@
<channel lang="bg" xmltv_id="NovaTV.bg" site_id="28">Nova TV</channel> <channel lang="bg" xmltv_id="NovaTV.bg" site_id="28">Nova TV</channel>
<channel lang="bg" xmltv_id="NTVMir.ru" site_id="379">NTV Mir</channel> <channel lang="bg" xmltv_id="NTVMir.ru" site_id="379">NTV Mir</channel>
<channel lang="bg" xmltv_id="OkhotaiRybalka.ru" site_id="156">Okhota i Rybalka</channel> <channel lang="bg" xmltv_id="OkhotaiRybalka.ru" site_id="156">Okhota i Rybalka</channel>
<channel lang="bg" xmltv_id="Perviykanal.ru" site_id="55">Perviy kanal</channel> <channel lang="bg" xmltv_id="Pervyykanal.ru" site_id="55">Perviy kanal</channel>
<channel lang="bg" xmltv_id="PlanetaFolk.bg" site_id="114">Planeta Folk</channel> <channel lang="bg" xmltv_id="PlanetaFolk.bg" site_id="114">Planeta Folk</channel>
<channel lang="bg" xmltv_id="PlanetaHD.bg" site_id="60">Planeta HD</channel> <channel lang="bg" xmltv_id="PlanetaHD.bg" site_id="60">Planeta HD</channel>
<channel lang="bg" xmltv_id="POTV.bg" site_id="211">POTV</channel> <channel lang="bg" xmltv_id="POTV.bg" site_id="211">POTV</channel>

View file

@ -8,7 +8,7 @@
<channel lang="lv" xmltv_id="HistoryEurope.us" site_id="history-europe">History Europe</channel> <channel lang="lv" xmltv_id="HistoryEurope.us" site_id="history-europe">History Europe</channel>
<channel lang="lv" xmltv_id="LTV1.lv" site_id="ltv1">LTV 1</channel> <channel lang="lv" xmltv_id="LTV1.lv" site_id="ltv1">LTV 1</channel>
<channel lang="lv" xmltv_id="LTV7.lv" site_id="ltv7">LTV 7</channel> <channel lang="lv" xmltv_id="LTV7.lv" site_id="ltv7">LTV 7</channel>
<channel lang="lv" xmltv_id="PerviykanalEuropa.ru" site_id="pbk-1">Perviy kanal Europa</channel> <channel lang="lv" xmltv_id="PervyykanalEuropa.ru" site_id="pbk-1">Perviy kanal Europa</channel>
<channel lang="lv" xmltv_id="RENTVBaltic.ru" site_id="rentv-baltic">REN TV Baltic</channel> <channel lang="lv" xmltv_id="RENTVBaltic.ru" site_id="rentv-baltic">REN TV Baltic</channel>
<channel lang="lv" xmltv_id="TV1000East.se" site_id="tv1000-cee">TV 1000 East</channel> <channel lang="lv" xmltv_id="TV1000East.se" site_id="tv1000-cee">TV 1000 East</channel>
<channel lang="lv" xmltv_id="TV1000RusskoeKino.se" site_id="tv1000-kino">TV 1000 Russkoe Kino</channel> <channel lang="lv" xmltv_id="TV1000RusskoeKino.se" site_id="tv1000-kino">TV 1000 Russkoe Kino</channel>

View file

@ -4,7 +4,7 @@
<channel lang="ru" xmltv_id="31Kanal.kz" site_id="162#31-kanal-429">31 Kanal</channel> <channel lang="ru" xmltv_id="31Kanal.kz" site_id="162#31-kanal-429">31 Kanal</channel>
<channel lang="ru" xmltv_id="AlmatyTV.kz" site_id="162#almaty-189">Almaty TV</channel> <channel lang="ru" xmltv_id="AlmatyTV.kz" site_id="162#almaty-189">Almaty TV</channel>
<channel lang="ru" xmltv_id="AtamekenBusiness.kz" site_id="163#atameken-business-1264">Atameken Business</channel> <channel lang="ru" xmltv_id="AtamekenBusiness.kz" site_id="163#atameken-business-1264">Atameken Business</channel>
<channel lang="ru" xmltv_id="PerviykanalEvraziya.ru" site_id="163#400">Perviy kanal Evrasia</channel> <channel lang="ru" xmltv_id="PervyykanalEvraziya.kz" site_id="163#400">Perviy kanal Evrasia</channel>
<channel lang="ru" xmltv_id="RTRPlaneta.ru" site_id="163#rtr-planeta-sng-784">RTR Planeta</channel> <channel lang="ru" xmltv_id="RTRPlaneta.ru" site_id="163#rtr-planeta-sng-784">RTR Planeta</channel>
<channel lang="ru" xmltv_id="SetantaSportsUkraine.ie" site_id="162#setanta-sport-1099">Setanta Sports Ukraine</channel> <channel lang="ru" xmltv_id="SetantaSportsUkraine.ie" site_id="162#setanta-sport-1099">Setanta Sports Ukraine</channel>
<channel lang="ru" xmltv_id="STSInternational.ru" site_id="162#sts-international-441">STS International</channel> <channel lang="ru" xmltv_id="STSInternational.ru" site_id="162#sts-international-441">STS International</channel>

View file

@ -171,7 +171,7 @@
<channel lang="ru" xmltv_id="OTR.ru" site_id="213#obshchestvennoe-televidenie-rossii-51">OTR</channel> <channel lang="ru" xmltv_id="OTR.ru" site_id="213#obshchestvennoe-televidenie-rossii-51">OTR</channel>
<channel lang="ru" xmltv_id="ParamountChannelRussia.us" site_id="213#paramount-channel-1209">Paramount Channel Russia</channel> <channel lang="ru" xmltv_id="ParamountChannelRussia.us" site_id="213#paramount-channel-1209">Paramount Channel Russia</channel>
<channel lang="ru" xmltv_id="ParamountComedyRussia.us" site_id="213#paramount-comedy-733">Paramount Comedy Russia</channel> <channel lang="ru" xmltv_id="ParamountComedyRussia.us" site_id="213#paramount-comedy-733">Paramount Comedy Russia</channel>
<channel lang="ru" xmltv_id="Perviykanal.ru" site_id="213#pervyy-16">Perviy kanal</channel> <channel lang="ru" xmltv_id="Pervyykanal.ru" site_id="213#pervyy-16">Perviy kanal</channel>
<channel lang="ru" xmltv_id="Pobeda.ru" site_id="213#pobeda-1355">Pobeda</channel> <channel lang="ru" xmltv_id="Pobeda.ru" site_id="213#pobeda-1355">Pobeda</channel>
<channel lang="ru" xmltv_id="Poehali.ru" site_id="213#poehali-1246">Poehali!</channel> <channel lang="ru" xmltv_id="Poehali.ru" site_id="213#poehali-1246">Poehali!</channel>
<channel lang="ru" xmltv_id="Priklyucheniya.ru" site_id="213#priklyucheniya-hd-499">Priklyucheniya</channel> <channel lang="ru" xmltv_id="Priklyucheniya.ru" site_id="213#priklyucheniya-hd-499">Priklyucheniya</channel>

View file

@ -168,7 +168,7 @@
<channel lang="sl" xmltv_id="Oto.si" site_id="1000365">Oto</channel> <channel lang="sl" xmltv_id="Oto.si" site_id="1000365">Oto</channel>
<channel lang="sl" xmltv_id="OTV.hr" site_id="1000190">OTV</channel> <channel lang="sl" xmltv_id="OTV.hr" site_id="1000190">OTV</channel>
<channel lang="sl" xmltv_id="OTVValentino.ba" site_id="1000073">OTV Valentino</channel> <channel lang="sl" xmltv_id="OTVValentino.ba" site_id="1000073">OTV Valentino</channel>
<channel lang="sl" xmltv_id="PerviykanalEuropa.ru" site_id="1000272">Perviy kanal Europa</channel> <channel lang="sl" xmltv_id="PervyykanalEuropa.ru" site_id="1000272">Perviy kanal Europa</channel>
<channel lang="sl" xmltv_id="PeTV.si" site_id="1000101">PeTV</channel> <channel lang="sl" xmltv_id="PeTV.si" site_id="1000101">PeTV</channel>
<channel lang="sl" xmltv_id="PinkExtra.rs" site_id="1000095">Pink Extra</channel> <channel lang="sl" xmltv_id="PinkExtra.rs" site_id="1000095">Pink Extra</channel>
<channel lang="sl" xmltv_id="PinkFilm.rs" site_id="1000096">Pink Film</channel> <channel lang="sl" xmltv_id="PinkFilm.rs" site_id="1000096">Pink Film</channel>

View file

View file

@ -16,14 +16,14 @@ To load a program guide, all you need to do is copy the link to one or more of t
</tbody> </tbody>
</table> </table>
## EPG Codes
📋&nbsp;&nbsp;[iptv-org.github.io](https://iptv-org.github.io/)
## API ## API
The API documentation can be found in the [iptv-org/api](https://github.com/iptv-org/api) repository. The API documentation can be found in the [iptv-org/api](https://github.com/iptv-org/api) repository.
## Resources
Links to other useful IPTV-related resources can be found in the [iptv-org/awesome-iptv](https://github.com/iptv-org/awesome-iptv) repository.
## Contribution ## Contribution
If you find a bug or want to contribute to the code or documentation, you can help by submitting an [issue](https://github.com/iptv-org/epg/issues) or a [pull request](https://github.com/iptv-org/epg/pulls). If you find a bug or want to contribute to the code or documentation, you can help by submitting an [issue](https://github.com/iptv-org/epg/issues) or a [pull request](https://github.com/iptv-org/epg/pulls).

View file

@ -1 +0,0 @@
{"xmltv_id":"CNNInternationalEurope2.us","site":"example.com","site_id":"141","lang":"en","error":"The channel has the wrong xmltv_id"}

View file

@ -1 +0,0 @@
{"xmltv_id":"CNNInternationalEurope.us","site":"magticom.ge","site_id":"140","lang":"ru","date":"2022-01-21T00:00:00Z","error":"Programs not found"}

View file

@ -1 +0,0 @@
{"xmltv_id":"Perviykanal.ru","site":"yandex.ru","site_id":"1","lang":"ru","date":"2022-01-21T00:00:00Z","error":"Some error"}

View file

@ -1 +0,0 @@
{"xmltv_id":"BravoEast.us","site":"directv.com","site_id":"237","lang":"en","date":"2022-01-21T00:00:00Z","error":"Invalid header value char"}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<site site="parse-channels.com">
<channels>
<channel lang="en" xmltv_id="CNNInternational.us" site_id="140">CNN International</channel>
</channels>
</site>

View file

@ -1 +0,0 @@
{"lang":"en","xmltv_id":"BravoEast.us","site_id":"237","site":"directv.com","configPath":"sites/directv.com/directv.com.config.js","groups":["us/directv.com"],"cluster_id":84,"_id":"00AluKCrCnfgrl8W","date":"2022-01-21T00:00:00Z","error":"Invalid header value char"}

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<site site="duplicate.com">
<channels>
<channel lang="en" xmltv_id="CNNInternationalEurope.us" site_id="140">CNN International</channel>
<channel lang="en" xmltv_id="CNNInternationalEurope.us" site_id="140">CNN International</channel>
</channels>
</site>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<site site="lint.com">
<channels>
<channel xmltv_id="CNNInternationalEurope.us" site_id="140">CNN International</channel>
</channels>
</site>

View file

@ -0,0 +1,19 @@
module.exports = {
site: 'parse-channels.com',
url() {
return `https://parse-channels.com`
},
parser() {
return []
},
channels() {
return [
{
lang: 'en',
xmltv_id: 'CNNInternational.us',
site_id: 140,
name: 'CNN International'
}
]
}
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<site site="wrong_xmltv_id.com">
<channels>
<channel lang="en" xmltv_id="CNNInternational" site_id="140">CNN International</channel>
</channels>
</site>

View file

@ -0,0 +1,26 @@
const { execSync } = require('child_process')
const fs = require('fs-extra')
const path = require('path')
beforeEach(() => {
fs.emptyDirSync('tests/__data__/output')
const stdout = execSync(
'CHANNELS_PATH=tests/__data__/input/sites/example.com_ca.channels.xml OUTPUT_DIR=tests/__data__/output/api npm run api:update',
{ encoding: 'utf8' }
)
})
it('can generate guides.json', () => {
expect(content('tests/__data__/output/api/guides.json')).toBe(
content('tests/__data__/expected/api/guides.json')
)
})
function content(filepath) {
const data = fs.readFileSync(path.resolve(filepath), {
encoding: 'utf8'
})
return JSON.stringify(data)
}

View file

@ -0,0 +1,19 @@
const { execSync } = require('child_process')
it('will show a message if the file contains a syntax error', () => {
try {
const stdout = execSync(
'npm run channels:lint -- tests/__data__/input/sites/lint.channels.xml',
{
encoding: 'utf8'
}
)
console.log(stdout)
process.exit(1)
} catch (err) {
expect(err.status).toBe(1)
expect(err.stdout).toBe(
`\n> channels:lint\n> node scripts/commands/channels/lint.js "tests/__data__/input/sites/lint.channels.xml"\n\n\ntests/__data__/input/sites/lint.channels.xml\n 4:0 Element 'channel': The attribute 'lang' is required but missing.\n\n1 error(s)\n`
)
}
})

View file

@ -0,0 +1,24 @@
const { execSync } = require('child_process')
const fs = require('fs-extra')
const path = require('path')
beforeEach(() => {
fs.emptyDirSync('tests/__data__/output')
const stdout = execSync(
'npm run channels:parse -- --config=tests/__data__/input/sites/parse-channels.config.js --output=tests/__data__/output/channels.xml',
{ encoding: 'utf8' }
)
})
it('can parse channels', () => {
expect(content('tests/__data__/output/channels.xml')).toEqual(
content('tests/__data__/expected/sites/parse-channels.channels.xml')
)
})
function content(filepath) {
return fs.readFileSync(path.resolve(filepath), {
encoding: 'utf8'
})
}

View file

@ -0,0 +1,49 @@
const { execSync } = require('child_process')
it('will show a message if the file contains a duplicate', () => {
try {
const stdout = execSync(
'npm run channels:validate -- tests/__data__/input/sites/duplicate.channels.xml',
{
encoding: 'utf8'
}
)
console.log(stdout)
process.exit(1)
} catch (err) {
expect(err.status).toBe(1)
expect(err.stdout).toBe(
`\n> channels:validate\n> node scripts/commands/channels/validate.js "tests/__data__/input/sites/duplicate.channels.xml"\n\ntests/__data__/input/sites/duplicate.channels.xml
(index) type lang xmltv_id site_id name
0 'duplicate' 'en' 'CNNInternationalEurope.us' '140' 'CNN International'
\n1 error(s) in 1 file(s)\n`
)
}
})
it('will show a message if the file contains a channel with wrong xmltv_id', () => {
try {
const stdout = execSync(
'npm run channels:validate -- tests/__data__/input/sites/wrong_xmltv_id.channels.xml',
{
encoding: 'utf8'
}
)
console.log(stdout)
process.exit(1)
} catch (err) {
expect(err.status).toBe(1)
expect(err.stdout).toBe(
`\n> channels:validate\n> node scripts/commands/channels/validate.js "tests/__data__/input/sites/wrong_xmltv_id.channels.xml"\n\ntests/__data__/input/sites/wrong_xmltv_id.channels.xml
(index) type lang xmltv_id site_id name
0 'wrong_xmltv_id' 'en' 'CNNInternational' '140' 'CNN International'
\n1 error(s) in 1 file(s)\n`
)
}
})

View file

@ -0,0 +1,32 @@
const { execSync } = require('child_process')
const fs = require('fs-extra')
const path = require('path')
beforeEach(() => {
fs.emptyDirSync('tests/__data__/output')
fs.copyFileSync('tests/__data__/input/database/queue.db', 'tests/__data__/output/queue.db')
execSync(
'DB_DIR=tests/__data__/output LOGS_DIR=tests/__data__/output/logs npm run cluster:load -- --cluster-id=1 --timeout=10000',
{ encoding: 'utf8' }
)
})
it('can load cluster', () => {
expect(content('tests/__data__/output/logs/cluster/load/cluster_1.log')).toEqual(
content('tests/__data__/expected/logs/cluster/load/cluster_1.log')
)
})
function content(filepath) {
const data = fs.readFileSync(path.resolve(filepath), {
encoding: 'utf8'
})
return data
.split('\n')
.filter(l => l)
.map(l => {
return JSON.parse(l)
})
}

View file

@ -1,25 +0,0 @@
const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process')
beforeEach(() => {
fs.rmdirSync('tests/__data__/output', { recursive: true })
fs.mkdirSync('tests/__data__/output')
fs.mkdirSync('tests/__data__/temp/database', { recursive: true })
fs.copyFileSync('tests/__data__/input/database/queue.db', 'tests/__data__/temp/database/queue.db')
})
afterEach(() => {
fs.rmdirSync('tests/__data__/temp', { recursive: true })
})
it('can create valid matrix', () => {
const result = execSync(
'DB_DIR=tests/__data__/temp/database node scripts/commands/create-matrix.js',
{
encoding: 'utf8'
}
)
expect(result).toBe('::set-output name=matrix::{"cluster_id":[1,4,84,120]}\n')
})

View file

@ -0,0 +1,32 @@
const { execSync } = require('child_process')
const fs = require('fs-extra')
const path = require('path')
beforeEach(() => {
fs.emptyDirSync('tests/__data__/output')
fs.copyFileSync('tests/__data__/input/database/queue.db', 'tests/__data__/output/queue.db')
fs.copyFileSync('tests/__data__/input/database/programs.db', 'tests/__data__/output/programs.db')
const stdout = execSync(
'DB_DIR=tests/__data__/output DATA_DIR=tests/__data__/input/data PUBLIC_DIR=tests/__data__/output npm run guides:update',
{ encoding: 'utf8' }
)
})
it('can generate /guides', () => {
expect(content('tests/__data__/output/guides/fr/chaines-tv.orange.fr.epg.xml')).toBe(
content('tests/__data__/expected/guides/fr/chaines-tv.orange.fr.epg.xml')
)
expect(content('tests/__data__/output/guides/zw/dstv.com.epg.xml')).toBe(
content('tests/__data__/expected/guides/zw/dstv.com.epg.xml')
)
})
function content(filepath) {
const data = fs.readFileSync(path.resolve(filepath), {
encoding: 'utf8'
})
return JSON.stringify(data)
}

View file

@ -1,40 +0,0 @@
const fs = require('fs')
const path = require('path')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const { execSync } = require('child_process')
dayjs.extend(utc)
beforeEach(() => {
fs.rmdirSync('tests/__data__/temp', { recursive: true })
fs.rmdirSync('tests/__data__/output', { recursive: true })
fs.mkdirSync('tests/__data__/output')
fs.mkdirSync('tests/__data__/temp/database', { recursive: true })
fs.copyFileSync('tests/__data__/input/database/queue.db', 'tests/__data__/temp/database/queue.db')
execSync(
'DB_DIR=tests/__data__/temp/database LOGS_DIR=tests/__data__/output/logs node scripts/commands/load-cluster.js --cluster-id=1 --timeout=10000',
{ encoding: 'utf8' }
)
})
it('can load cluster', () => {
let output = content('tests/__data__/output/logs/load-cluster/cluster_1.log')
let expected = content('tests/__data__/expected/logs/load-cluster/cluster_1.log')
expect(output).toEqual(expected)
})
function content(filepath) {
const data = fs.readFileSync(path.resolve(filepath), {
encoding: 'utf8'
})
return data
.split('\n')
.filter(l => l)
.map(l => {
return JSON.parse(l)
})
}

View file

@ -1,25 +1,19 @@
const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process') const { execSync } = require('child_process')
const fs = require('fs-extra')
const path = require('path')
beforeEach(() => { beforeEach(() => {
fs.rmdirSync('tests/__data__/output', { recursive: true }) fs.emptyDirSync('tests/__data__/output')
fs.mkdirSync('tests/__data__/output') fs.copyFileSync('tests/__data__/input/database/queue.db', 'tests/__data__/output/queue.db')
fs.mkdirSync('tests/__data__/output/database', { recursive: true })
fs.copyFileSync(
'tests/__data__/input/database/queue.db',
'tests/__data__/output/database/queue.db'
)
const stdout = execSync( const stdout = execSync(
'DB_DIR=tests/__data__/output/database LOGS_DIR=tests/__data__/input/logs node scripts/commands/save-results.js', 'DB_DIR=tests/__data__/output LOGS_DIR=tests/__data__/input/logs npm run programs:save',
{ encoding: 'utf8' } { encoding: 'utf8' }
) )
}) })
it('can save programs to database', () => { it('can save programs to database', () => {
let output = content('tests/__data__/output/database/programs.db') let output = content('tests/__data__/output/programs.db')
let expected = content('tests/__data__/expected/database/programs.db') let expected = content('tests/__data__/expected/database/programs.db')
output = output.map(i => { output = output.map(i => {
@ -35,10 +29,9 @@ it('can save programs to database', () => {
}) })
it('can update queue', () => { it('can update queue', () => {
const output = content('tests/__data__/output/database/queue.db') expect(content('tests/__data__/output/queue.db')).toEqual(
const expected = content('tests/__data__/expected/database/queue-with-errors.db') content('tests/__data__/expected/database/queue-with-errors.db')
)
expect(output).toEqual(expected)
}) })
function content(filepath) { function content(filepath) {

View file

@ -1,13 +1,12 @@
const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process') const { execSync } = require('child_process')
const fs = require('fs-extra')
const path = require('path')
beforeEach(() => { beforeEach(() => {
fs.rmdirSync('tests/__data__/output', { recursive: true }) fs.emptyDirSync('tests/__data__/output')
fs.mkdirSync('tests/__data__/output')
const stdout = execSync( const stdout = execSync(
'DB_DIR=tests/__data__/output/database LOGS_DIR=tests/__data__/output/logs CHANNELS_PATH=tests/__data__/input/sites/*.channels.xml node scripts/commands/create-queue.js --max-clusters=1 --days=2', 'DB_DIR=tests/__data__/output/database LOGS_DIR=tests/__data__/output/logs CHANNELS_PATH=tests/__data__/input/sites/example.com_ca.channels.xml npm run queue:create -- --max-clusters=1 --days=2',
{ encoding: 'utf8' } { encoding: 'utf8' }
) )
}) })
@ -35,13 +34,6 @@ it('can create queue', () => {
) )
}) })
it('can log errors', () => {
let output = content('tests/__data__/output/logs/errors/ca/example.com.log')
let expected = content('tests/__data__/expected/logs/errors/ca/example.com.log')
expect(output).toEqual(expected)
})
function content(filepath) { function content(filepath) {
const data = fs.readFileSync(path.resolve(filepath), { const data = fs.readFileSync(path.resolve(filepath), {
encoding: 'utf8' encoding: 'utf8'

View file

@ -0,0 +1,26 @@
const { execSync } = require('child_process')
const fs = require('fs-extra')
const path = require('path')
beforeEach(() => {
fs.emptyDirSync('tests/__data__/output')
const stdout = execSync(
'CHANNELS_PATH=tests/__data__/input/sites/example.com_ca.channels.xml DATA_DIR=tests/__data__/input/data npm run readme:update -- --config=tests/__data__/input/readme.json',
{ encoding: 'utf8' }
)
})
it('can update readme.md', () => {
expect(content('tests/__data__/output/readme.md')).toBe(
content('tests/__data__/expected/_readme.md')
)
})
function content(filepath) {
const data = fs.readFileSync(path.resolve(filepath), {
encoding: 'utf8'
})
return JSON.stringify(data)
}

View file

@ -1,28 +0,0 @@
const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process')
beforeEach(() => {
fs.rmdirSync('tests/__data__/output', { recursive: true })
fs.mkdirSync('tests/__data__/output')
const stdout = execSync(
'CHANNELS_PATH=tests/__data__/input/sites/**.channels.xml OUTPUT_DIR=tests/__data__/output/api node scripts/commands/update-api.js',
{ encoding: 'utf8' }
)
})
it('can generate guides.json', () => {
const output = content('tests/__data__/output/api/guides.json')
const expected = content('tests/__data__/expected/api/guides.json')
expect(output).toBe(expected)
})
function content(filepath) {
const data = fs.readFileSync(path.resolve(filepath), {
encoding: 'utf8'
})
return JSON.stringify(data)
}

View file

@ -1,67 +0,0 @@
const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process')
beforeEach(() => {
fs.rmdirSync('tests/__data__/output', { recursive: true })
fs.mkdirSync('tests/__data__/output')
fs.mkdirSync('tests/__data__/temp/database', { recursive: true })
fs.copyFileSync('tests/__data__/input/database/queue.db', 'tests/__data__/temp/database/queue.db')
fs.copyFileSync(
'tests/__data__/input/database/programs.db',
'tests/__data__/temp/database/programs.db'
)
const stdout = execSync(
'DB_DIR=tests/__data__/temp/database DATA_DIR=tests/__data__/input/data PUBLIC_DIR=tests/__data__/output LOGS_DIR=tests/__data__/output/logs node scripts/commands/update-guides.js',
{ encoding: 'utf8' }
)
})
afterEach(() => {
fs.rmdirSync('tests/__data__/temp', { recursive: true })
})
it('can generate /guides', () => {
const output1 = content('tests/__data__/output/guides/fr/chaines-tv.orange.fr.epg.xml')
const expected1 = content('tests/__data__/expected/guides/fr/chaines-tv.orange.fr.epg.xml')
expect(output1).toBe(expected1)
const output2 = content('tests/__data__/output/guides/zw/dstv.com.epg.xml')
const expected2 = content('tests/__data__/expected/guides/zw/dstv.com.epg.xml')
expect(output2).toBe(expected2)
})
it('can create guides.log', () => {
const output = content('tests/__data__/output/logs/guides.log')
const expected = content('tests/__data__/expected/logs/guides.log')
expect(output).toBe(expected)
})
it('can log errors', () => {
const output1 = content('tests/__data__/output/logs/errors/ru/yandex.ru.log')
const expected1 = content('tests/__data__/expected/logs/errors/ru/yandex.ru.log')
expect(output1).toBe(expected1)
const output2 = content('tests/__data__/output/logs/errors/us/directv.com.log')
const expected2 = content('tests/__data__/expected/logs/errors/us/directv.com.log')
expect(output2).toBe(expected2)
const output3 = content('tests/__data__/output/logs/errors/ge/magticom.ge.log')
const expected3 = content('tests/__data__/expected/logs/errors/ge/magticom.ge.log')
expect(output3).toBe(expected3)
})
function content(filepath) {
const data = fs.readFileSync(path.resolve(filepath), {
encoding: 'utf8'
})
return JSON.stringify(data)
}

View file

@ -1,28 +0,0 @@
const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process')
beforeEach(() => {
fs.rmdirSync('tests/__data__/output', { recursive: true })
fs.mkdirSync('tests/__data__/output')
const stdout = execSync(
'CHANNELS_PATH=tests/__data__/input/sites/*.channels.xml DATA_DIR=tests/__data__/input/data node scripts/commands/update-readme.js --config=tests/__data__/input/_readme.json',
{ encoding: 'utf8' }
)
})
it('can update readme.md', () => {
const output = content('tests/__data__/output/readme.md')
const expected = content('tests/__data__/expected/readme.md')
expect(output).toBe(expected)
})
function content(filepath) {
const data = fs.readFileSync(path.resolve(filepath), {
encoding: 'utf8'
})
return JSON.stringify(data)
}