mirror of
https://github.com/iptv-org/epg.git
synced 2025-05-10 00:50:09 -04:00
Merge branch 'iptv-org:master' into master
This commit is contained in:
commit
3047311f8c
88 changed files with 1283 additions and 794 deletions
22
.github/workflows/_check.yml
vendored
22
.github/workflows/_check.yml
vendored
|
@ -10,12 +10,22 @@ jobs:
|
|||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 2
|
||||
- name: Download channels from API
|
||||
- name: Download data from API
|
||||
run: |
|
||||
mkdir -p scripts/data
|
||||
curl -L -o scripts/data/channels.json https://iptv-org.github.io/api/channels.json
|
||||
- id: files
|
||||
uses: jitterbit/get-changed-files@v1
|
||||
- run: npm install
|
||||
- run: npm run lint -- ${{ steps.files.outputs.added_modified }}
|
||||
- run: npm run validate -- ${{ steps.files.outputs.added_modified }}
|
||||
- uses: actions/setup-node@v2
|
||||
if: ${{ !env.ACT && steps.files.outputs.any_changed == 'true' }}
|
||||
with:
|
||||
node-version: '14'
|
||||
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 }}
|
||||
|
|
14
.github/workflows/_load.yml
vendored
14
.github/workflows/_load.yml
vendored
|
@ -14,18 +14,22 @@ jobs:
|
|||
load:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo running on branch ${GITHUB_REF##*/}
|
||||
- uses: actions/checkout@v2
|
||||
- name: Download data from API
|
||||
run: |
|
||||
mkdir -p scripts/data
|
||||
curl -L -o scripts/data/channels.json https://iptv-org.github.io/api/channels.json
|
||||
- uses: FedericoCarboni/setup-ffmpeg@v1
|
||||
- uses: actions/setup-node@v2
|
||||
if: ${{ !env.ACT }}
|
||||
with:
|
||||
node-version: '14'
|
||||
cache: 'npm'
|
||||
- run: npm install
|
||||
- run: CHANNELS_PATH=sites/${{inputs.site}}/*.channels.xml node scripts/commands/create-queue.js --max-clusters=1 --days=2
|
||||
- run: NODE_OPTIONS=--insecure-http-parser node scripts/commands/load-cluster.js --timeout=30000 --cluster-id=1
|
||||
- run: node scripts/commands/save-results.js
|
||||
- run: node scripts/commands/update-guides.js
|
||||
- run: CHANNELS_PATH=sites/${{inputs.site}}/*.channels.xml npm run queue:create -- --max-clusters=1 --days=2
|
||||
- run: NODE_OPTIONS=--insecure-http-parser npm run cluster:load -- --timeout=30000 --cluster-id=1
|
||||
- run: npm run programs:save
|
||||
- run: npm run guides:update
|
||||
- uses: tibdex/github-app-token@v1
|
||||
if: ${{ !env.ACT }}
|
||||
id: create-app-token
|
||||
|
|
11
.github/workflows/_update-api.yml
vendored
11
.github/workflows/_update-api.yml
vendored
|
@ -4,12 +4,17 @@ on:
|
|||
schedule:
|
||||
- cron: '0 12 * * *'
|
||||
jobs:
|
||||
check:
|
||||
update:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
if: ${{ !env.ACT }}
|
||||
with:
|
||||
node-version: '14'
|
||||
cache: 'npm'
|
||||
- run: npm install
|
||||
- run: node scripts/commands/update-api.js
|
||||
- run: npm run api:update
|
||||
- uses: tibdex/github-app-token@v1
|
||||
if: ${{ !env.ACT }}
|
||||
id: create-app-token
|
||||
|
@ -21,7 +26,7 @@ jobs:
|
|||
with:
|
||||
repository-name: iptv-org/api
|
||||
branch: gh-pages
|
||||
folder: .gh-pages/api
|
||||
folder: .api
|
||||
token: ${{ steps.create-app-token.outputs.token }}
|
||||
git-config-name: iptv-bot[bot]
|
||||
git-config-email: 84861620+iptv-bot[bot]@users.noreply.github.com
|
||||
|
|
57
.github/workflows/_update-readme.yml
vendored
Normal file
57
.github/workflows/_update-readme.yml
vendored
Normal 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/artonline.tv.yml
vendored
Normal file
17
.github/workflows/artonline.tv.yml
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
name: artonline.tv
|
||||
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 }}
|
2
.github/workflows/guidatv.sky.it.yml
vendored
2
.github/workflows/guidatv.sky.it.yml
vendored
|
@ -1,7 +1,7 @@
|
|||
name: guidatv.sky.it
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
- cron: '5 0 * * *'
|
||||
workflow_dispatch:
|
||||
workflow_run:
|
||||
workflows: [_trigger]
|
||||
|
|
2
.github/workflows/m.tv.sms.cz.yml
vendored
2
.github/workflows/m.tv.sms.cz.yml
vendored
|
@ -1,7 +1,7 @@
|
|||
name: m.tv.sms.cz
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
- cron: '5 0 * * *'
|
||||
workflow_dispatch:
|
||||
workflow_run:
|
||||
workflows: [_trigger]
|
||||
|
|
2
.github/workflows/siba.com.co.yml
vendored
2
.github/workflows/siba.com.co.yml
vendored
|
@ -1,7 +1,7 @@
|
|||
name: siba.com.co
|
||||
on:
|
||||
schedule:
|
||||
- cron: '10 0 * * *'
|
||||
- cron: '15 0 * * *'
|
||||
workflow_dispatch:
|
||||
workflow_run:
|
||||
workflows: [_trigger]
|
||||
|
|
17
.github/workflows/sky.de.yml
vendored
Normal file
17
.github/workflows/sky.de.yml
vendored
Normal 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 }}
|
2
.github/workflows/telkussa.fi.yml
vendored
2
.github/workflows/telkussa.fi.yml
vendored
|
@ -1,7 +1,7 @@
|
|||
name: telkussa.fi
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
- cron: '5 0 * * *'
|
||||
workflow_dispatch:
|
||||
workflow_run:
|
||||
workflows: [_trigger]
|
||||
|
|
2
.github/workflows/tvarenasport.com.yml
vendored
2
.github/workflows/tvarenasport.com.yml
vendored
|
@ -1,7 +1,7 @@
|
|||
name: tvarenasport.com
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
- cron: '5 0 * * *'
|
||||
workflow_dispatch:
|
||||
workflow_run:
|
||||
workflows: [_trigger]
|
||||
|
|
2
.github/workflows/tvguide.myjcom.jp.yml
vendored
2
.github/workflows/tvguide.myjcom.jp.yml
vendored
|
@ -1,7 +1,7 @@
|
|||
name: tvguide.myjcom.jp
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
- cron: '5 0 * * *'
|
||||
workflow_dispatch:
|
||||
workflow_run:
|
||||
workflows: [_trigger]
|
||||
|
|
|
@ -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.
|
||||
|
||||
## API
|
||||
|
||||
The API documentation can be found in the [iptv-org/api](https://github.com/iptv-org/api) repository.
|
||||
|
||||
## 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).
|
||||
|
|
63
README.md
63
README.md
|
@ -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">🇧🇦 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 valign="top">🇧🇼 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">🇧🇷 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">🇧🇬 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">🇧🇷 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">🇧🇬 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">🇧🇫 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 valign="top" rowspan="2">🇧🇮 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 valign="top" rowspan="2">🇨🇲 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 valign="top">🇨🇦 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">🇨🇦 Canada</td><td align="right">89</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">🇨🇻 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 valign="top" rowspan="2">🇨🇫 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 valign="top" rowspan="2">🇨🇱 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 valign="top">🇨🇳 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">🇨🇳 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">🇨🇴 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">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">🇰🇲 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">🇨🇷 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">🇭🇷 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">🇭🇷 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 valign="top">🇨🇺 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">🇨🇾 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">🇨🇿 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">🇨🇿 Czech Republic</td><td align="right">522</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">🇨🇩 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 valign="top">🇩🇰 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 valign="top">🇩🇴 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">🇪🇨 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">🇪🇬 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 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 valign="top" rowspan="4">🇪🇬 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">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-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">🇸🇻 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,8 +100,9 @@ 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 valign="top" rowspan="2">🇬🇲 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 valign="top">🇬🇪 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">🇩🇪 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">🇬🇪 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" rowspan="2">🇩🇪 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 align="right">118</td><td nowrap><code>https://iptv-org.github.io/epg/guides/de/sky.de.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/sky.de.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/sky.de.yml/badge.svg" alt="sky.de" style="max-width: 100%;"></a></td></tr>
|
||||
<tr><td valign="top" rowspan="2">🇬🇭 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 valign="top">🇬🇷 Greece</td><td align="right">86</td><td nowrap><code>https://iptv-org.github.io/epg/guides/gr/cosmote.gr.epg.xml</code></td><td><a href="https://github.com/iptv-org/epg/actions/workflows/cosmote.gr.yml"><img src="https://github.com/iptv-org/epg/actions/workflows/cosmote.gr.yml/badge.svg" alt="cosmote.gr" style="max-width: 100%;"></a></td></tr>
|
||||
|
@ -122,13 +123,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">🇭🇺 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">🇮🇸 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">🇮🇳 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">🇮🇩 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 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 valign="top" rowspan="2">🇮🇩 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">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">🇮🇷 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">🇮🇶 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 valign="top">🇮🇪 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">🇮🇹 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">🇮🇹 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 valign="top" rowspan="2">🇨🇮 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>
|
||||
|
@ -143,13 +144,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">🇱🇻 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">🇱🇧 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 valign="top">🇱🇸 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">🇱🇸 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">🇱🇷 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">🇱🇾 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 valign="top">🇲🇬 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">🇲🇼 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">🇲🇾 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">🇲🇼 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">🇲🇾 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">🇲🇱 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 valign="top">🇲🇶 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 +171,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">🇳🇮 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">🇳🇪 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 valign="top">🇳🇬 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">🇳🇬 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">🇲🇰 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 valign="top" rowspan="2">🇳🇴 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 +184,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">🇵🇾 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">🇵🇪 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 valign="top">🇵🇱 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">🇵🇹 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">🇵🇱 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">🇵🇹 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">🇶🇦 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">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">🇨🇬 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 valign="top">🇷🇴 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">🇷🇺 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">🇷🇺 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">🇷🇼 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 valign="top">🇷🇪 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 +209,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 valign="top">🇸🇮 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">🇸🇴 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">🇿🇦 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">🇿🇦 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">🇸🇸 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">🇪🇸 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">🇪🇸 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 valign="top">🇸🇩 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">🇸🇿 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">🇸🇪 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">🇸🇪 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">🇨🇭 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">🇸🇹 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">🇹🇿 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 +223,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">🇹🇬 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 valign="top" rowspan="3">🇹🇷 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">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">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">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">🇺🇬 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">🇺🇦 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">🇦🇪 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 valign="top" rowspan="2">🇬🇧 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">🇬🇧 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 valign="top" rowspan="4">🇺🇸 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">🇺🇸 United States</td><td align="right">1714</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">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>
|
||||
|
@ -244,14 +245,14 @@ To load a program guide, all you need to do is copy the link to one or more of t
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
## EPG Codes
|
||||
|
||||
📋 [iptv-org.github.io](https://iptv-org.github.io/)
|
||||
|
||||
## API
|
||||
|
||||
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
|
||||
|
||||
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
152
package-lock.json
generated
|
@ -16,6 +16,7 @@
|
|||
"epg-grabber": "^0.20.0",
|
||||
"epg-parser": "^0.1.6",
|
||||
"form-data": "^4.0.0",
|
||||
"fs-extra": "^10.0.1",
|
||||
"glob": "^7.2.0",
|
||||
"iconv-lite": "^0.4.24",
|
||||
"jest": "^27.3.1",
|
||||
|
@ -23,7 +24,6 @@
|
|||
"lodash": "^4.17.21",
|
||||
"markdown-include": "^0.4.3",
|
||||
"mockdate": "^3.0.5",
|
||||
"mz": "^2.7.0",
|
||||
"nedb-promises": "^5.0.3",
|
||||
"parse-duration": "^1.0.0",
|
||||
"pdf-parse": "^1.1.1",
|
||||
|
@ -1179,11 +1179,6 @@
|
|||
"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": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
|
||||
|
@ -2346,9 +2341,9 @@
|
|||
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.14.5",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
|
||||
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA==",
|
||||
"version": "1.14.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
|
||||
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
|
@ -2377,6 +2372,27 @@
|
|||
"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": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
|
||||
|
@ -3614,6 +3630,25 @@
|
|||
"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": {
|
||||
"version": "4.0.4",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "2.14.2",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz",
|
||||
|
@ -6460,11 +6466,6 @@
|
|||
"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": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
|
||||
|
@ -7368,9 +7369,9 @@
|
|||
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.14.5",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.5.tgz",
|
||||
"integrity": "sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA=="
|
||||
"version": "1.14.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
|
||||
"integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w=="
|
||||
},
|
||||
"form-data": {
|
||||
"version": "4.0.0",
|
||||
|
@ -7382,6 +7383,23 @@
|
|||
"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": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
|
||||
|
@ -8308,6 +8326,22 @@
|
|||
"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": {
|
||||
"version": "4.0.4",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "2.14.2",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz",
|
||||
|
|
18
package.json
18
package.json
|
@ -1,13 +1,21 @@
|
|||
{
|
||||
"name": "epg",
|
||||
"scripts": {
|
||||
"lint": "node scripts/commands/lint.js",
|
||||
"validate": "node scripts/commands/validate.js",
|
||||
"channels:validate": "node scripts/commands/channels/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:commands": "npx jest --runInBand -- commands",
|
||||
"test:sites": "npx jest --runInBand -- sites",
|
||||
"act": "act workflow_dispatch",
|
||||
"update-readme": "node scripts/commands/update-readme.js"
|
||||
"act:check": "act workflow_dispatch -W .github/workflows/_check.yml",
|
||||
"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,
|
||||
"author": "Arhey",
|
||||
|
@ -25,6 +33,7 @@
|
|||
"epg-grabber": "^0.20.0",
|
||||
"epg-parser": "^0.1.6",
|
||||
"form-data": "^4.0.0",
|
||||
"fs-extra": "^10.0.1",
|
||||
"glob": "^7.2.0",
|
||||
"iconv-lite": "^0.4.24",
|
||||
"jest": "^27.3.1",
|
||||
|
@ -32,7 +41,6 @@
|
|||
"lodash": "^4.17.21",
|
||||
"markdown-include": "^0.4.3",
|
||||
"mockdate": "^3.0.5",
|
||||
"mz": "^2.7.0",
|
||||
"nedb-promises": "^5.0.3",
|
||||
"parse-duration": "^1.0.0",
|
||||
"pdf-parse": "^1.1.1",
|
||||
|
|
42
scripts/commands/api/update.js
Normal file
42
scripts/commands/api/update.js
Normal 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()
|
|
@ -1,7 +1,7 @@
|
|||
const chalk = require('chalk')
|
||||
const libxml = require('libxmljs')
|
||||
const { program } = require('commander')
|
||||
const { logger, file } = require('../core')
|
||||
const { logger, file } = require('../../core')
|
||||
|
||||
const xsd = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
|
43
scripts/commands/channels/parse.js
Normal file
43
scripts/commands/channels/parse.js
Normal 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 '${outputFilepath}' successfully saved`)
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
function isPromise(promise) {
|
||||
return !!promise && typeof promise.then === 'function'
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
const { parser, logger, api } = require('../core')
|
||||
const { parser, logger, api } = require('../../core')
|
||||
const { program } = require('commander')
|
||||
const chalk = require('chalk')
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
const _ = require('lodash')
|
||||
const grabber = require('epg-grabber')
|
||||
const { program } = require('commander')
|
||||
const { db, logger, timer, file, parser } = require('../core')
|
||||
const { db, logger, timer, file, parser } = require('../../core')
|
||||
|
||||
const options = program
|
||||
.requiredOption('-c, --cluster-id <cluster-id>', 'The ID of cluster to load', parser.parseNumber)
|
||||
|
@ -16,7 +16,7 @@ const options = program
|
|||
.opts()
|
||||
|
||||
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() {
|
||||
logger.info('Starting...')
|
|
@ -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()
|
|
@ -1,130 +0,0 @@
|
|||
const { db, file, parser, logger, date, api } = require('../core')
|
||||
const { program } = require('commander')
|
||||
const _ = require('lodash')
|
||||
|
||||
const options = program
|
||||
.option(
|
||||
'--max-clusters <max-clusters>',
|
||||
'Set maximum number of clusters',
|
||||
parser.parseNumber,
|
||||
256
|
||||
)
|
||||
.option('--days <days>', 'Number of days for which to grab the program', parser.parseNumber, 1)
|
||||
.parse(process.argv)
|
||||
.opts()
|
||||
|
||||
const CHANNELS_PATH = process.env.CHANNELS_PATH || 'sites/**/*.channels.xml'
|
||||
const LOGS_DIR = process.env.LOGS_DIR || 'scripts/logs'
|
||||
|
||||
async function main() {
|
||||
logger.info('Starting...')
|
||||
logger.info(`Number of clusters: ${options.maxClusters}`)
|
||||
|
||||
await saveToDatabase(await createQueue())
|
||||
|
||||
logger.info('Done')
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
async function createQueue() {
|
||||
logger.info(`Create queue...`)
|
||||
|
||||
let queue = {}
|
||||
|
||||
await api.channels.load()
|
||||
const files = await file.list(CHANNELS_PATH)
|
||||
const utcDate = date.getUTC()
|
||||
const dates = Array.from({ length: options.days }, (_, i) => utcDate.add(i, 'd'))
|
||||
for (const filepath of files) {
|
||||
const dir = file.dirname(filepath)
|
||||
const { site, channels: items } = await parser.parseChannels(filepath)
|
||||
if (!site) continue
|
||||
const configPath = `${dir}/${site}.config.js`
|
||||
const config = require(file.resolve(configPath))
|
||||
if (config.ignore) continue
|
||||
const filename = file.basename(filepath)
|
||||
const [__, region] = filename.match(/_([a-z-]+)\.channels\.xml/i) || [null, null]
|
||||
const groupId = `${region}/${site}`
|
||||
for (const item of items) {
|
||||
if (!item.site || !item.site_id || !item.xmltv_id) continue
|
||||
const channel = api.channels.find({ id: item.xmltv_id })
|
||||
if (!channel) {
|
||||
await logError(groupId, {
|
||||
xmltv_id: item.xmltv_id,
|
||||
site: item.site,
|
||||
site_id: item.site_id,
|
||||
lang: item.lang,
|
||||
date: undefined,
|
||||
error: 'The channel has the wrong xmltv_id'
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
for (const d of dates) {
|
||||
const dString = d.toJSON()
|
||||
const key = `${item.site}:${item.lang}:${item.xmltv_id}:${dString}`
|
||||
if (!queue[key]) {
|
||||
queue[key] = {
|
||||
channel: {
|
||||
lang: item.lang,
|
||||
xmltv_id: item.xmltv_id,
|
||||
display_name: item.name,
|
||||
site_id: item.site_id,
|
||||
site: item.site
|
||||
},
|
||||
date: dString,
|
||||
configPath,
|
||||
groups: [],
|
||||
error: null
|
||||
}
|
||||
}
|
||||
|
||||
if (!queue[key].groups.includes(groupId)) {
|
||||
queue[key].groups.push(groupId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
queue = Object.values(queue)
|
||||
|
||||
logger.info(`Added ${queue.length} items`)
|
||||
|
||||
return queue
|
||||
}
|
||||
|
||||
async function saveToDatabase(items = []) {
|
||||
logger.info('Saving to the database...')
|
||||
await db.queue.load()
|
||||
await db.queue.reset()
|
||||
let queue = []
|
||||
const chunks = split(_.shuffle(items), options.maxClusters)
|
||||
for (const [i, chunk] of chunks.entries()) {
|
||||
for (const item of chunk) {
|
||||
item.cluster_id = i + 1
|
||||
queue.push(item)
|
||||
}
|
||||
}
|
||||
|
||||
queue = _.sortBy(queue, ['channel.lang', 'channel.xmltv_id', 'date'])
|
||||
|
||||
await db.queue.insert(queue)
|
||||
}
|
||||
|
||||
function split(arr, n) {
|
||||
let result = []
|
||||
for (let i = n; i > 0; i--) {
|
||||
result.push(arr.splice(0, Math.ceil(arr.length / i)))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
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')
|
||||
}
|
76
scripts/commands/guides/update.js
Normal file
76
scripts/commands/guides/update.js
Normal 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 })
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
const { db, logger, file, parser } = require('../core')
|
||||
const { db, logger, file, parser } = require('../../core')
|
||||
const _ = require('lodash')
|
||||
|
||||
const LOGS_DIR = process.env.LOGS_DIR || 'scripts/logs'
|
||||
|
@ -7,7 +7,7 @@ async function main() {
|
|||
await db.queue.load()
|
||||
await db.programs.load()
|
||||
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) {
|
||||
logger.info(`Parsing "${filepath}"...`)
|
||||
const results = await parser.parseLogs(filepath)
|
114
scripts/commands/queue/create.js
Normal file
114
scripts/commands/queue/create.js
Normal file
|
@ -0,0 +1,114 @@
|
|||
const { db, file, parser, logger, date, api } = require('../../core')
|
||||
const { program } = require('commander')
|
||||
const _ = require('lodash')
|
||||
|
||||
const options = program
|
||||
.option(
|
||||
'--max-clusters <max-clusters>',
|
||||
'Set maximum number of clusters',
|
||||
parser.parseNumber,
|
||||
256
|
||||
)
|
||||
.option('--days <days>', 'Number of days for which to grab the program', parser.parseNumber, 1)
|
||||
.parse(process.argv)
|
||||
.opts()
|
||||
|
||||
const CHANNELS_PATH = process.env.CHANNELS_PATH || 'sites/**/*.channels.xml'
|
||||
const LOGS_DIR = process.env.LOGS_DIR || 'scripts/logs'
|
||||
|
||||
async function main() {
|
||||
logger.info('Starting...')
|
||||
logger.info(`Number of clusters: ${options.maxClusters}`)
|
||||
|
||||
await saveToDatabase(await createQueue())
|
||||
|
||||
logger.info('Done')
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
async function createQueue() {
|
||||
logger.info(`Create queue...`)
|
||||
|
||||
let queue = {}
|
||||
|
||||
await api.channels.load()
|
||||
const files = await file.list(CHANNELS_PATH)
|
||||
const utcDate = date.getUTC()
|
||||
const dates = Array.from({ length: options.days }, (_, i) => utcDate.add(i, 'd'))
|
||||
for (const filepath of files) {
|
||||
try {
|
||||
const dir = file.dirname(filepath)
|
||||
const { site, channels: items } = await parser.parseChannels(filepath)
|
||||
if (!site) continue
|
||||
const configPath = `${dir}/${site}.config.js`
|
||||
const config = require(file.resolve(configPath))
|
||||
if (config.ignore) continue
|
||||
const filename = file.basename(filepath)
|
||||
const [__, region] = filename.match(/_([a-z-]+)\.channels\.xml/i) || [null, null]
|
||||
const groupId = `${region}/${site}`
|
||||
for (const item of items) {
|
||||
if (!item.site || !item.xmltv_id) continue
|
||||
const channel = api.channels.find({ id: item.xmltv_id })
|
||||
if (!channel) continue
|
||||
for (const d of dates) {
|
||||
const dString = d.toJSON()
|
||||
const key = `${item.site}:${item.lang}:${item.xmltv_id}:${dString}`
|
||||
if (!queue[key]) {
|
||||
queue[key] = {
|
||||
channel: {
|
||||
lang: item.lang,
|
||||
xmltv_id: item.xmltv_id,
|
||||
display_name: item.name,
|
||||
site_id: item.site_id,
|
||||
site: item.site
|
||||
},
|
||||
date: dString,
|
||||
configPath,
|
||||
groups: [],
|
||||
error: null
|
||||
}
|
||||
}
|
||||
|
||||
if (!queue[key].groups.includes(groupId)) {
|
||||
queue[key].groups.push(groupId)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
queue = Object.values(queue)
|
||||
|
||||
logger.info(`Added ${queue.length} items`)
|
||||
|
||||
return queue
|
||||
}
|
||||
|
||||
async function saveToDatabase(items = []) {
|
||||
logger.info('Saving to the database...')
|
||||
await db.queue.load()
|
||||
await db.queue.reset()
|
||||
let queue = []
|
||||
const chunks = split(_.shuffle(items), options.maxClusters)
|
||||
for (const [i, chunk] of chunks.entries()) {
|
||||
for (const item of chunk) {
|
||||
item.cluster_id = i + 1
|
||||
queue.push(item)
|
||||
}
|
||||
}
|
||||
|
||||
queue = _.sortBy(queue, ['channel.lang', 'channel.xmltv_id', 'date'])
|
||||
|
||||
await db.queue.insert(queue)
|
||||
}
|
||||
|
||||
function split(arr, n) {
|
||||
let result = []
|
||||
for (let i = n; i > 0; i--) {
|
||||
result.push(arr.splice(0, Math.ceil(arr.length / i)))
|
||||
}
|
||||
return result
|
||||
}
|
|
@ -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 _ = require('lodash')
|
||||
|
||||
|
@ -10,25 +10,30 @@ const options = program
|
|||
.opts()
|
||||
|
||||
async function main() {
|
||||
await api.countries.load()
|
||||
const files = await file.list(CHANNELS_PATH)
|
||||
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)
|
||||
const [__, suffix] = filename.match(/\_(.*)\.channels\.xml$/) || [null, null]
|
||||
const [code] = suffix.split('-')
|
||||
try {
|
||||
await api.countries.load()
|
||||
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({
|
||||
code,
|
||||
site,
|
||||
count: channels.length,
|
||||
group: `${suffix}/${site}`
|
||||
})
|
||||
const filename = file.basename(filepath)
|
||||
const [__, suffix] = filename.match(/\_(.*)\.channels\.xml$/) || [null, null]
|
||||
const [code] = suffix.split('-')
|
||||
|
||||
items.push({
|
||||
code,
|
||||
site,
|
||||
count: channels.length,
|
||||
group: `${suffix}/${site}`
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
|
||||
await generateCountriesTable(items)
|
|
@ -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()
|
|
@ -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')
|
||||
}
|
|
@ -16,6 +16,10 @@ class API {
|
|||
find(query) {
|
||||
return _.find(this.collection, query)
|
||||
}
|
||||
|
||||
all() {
|
||||
return this.collection
|
||||
}
|
||||
}
|
||||
|
||||
const api = {}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
const path = require('path')
|
||||
const glob = require('glob')
|
||||
const fs = require('mz/fs')
|
||||
const fs = require('fs-extra')
|
||||
|
||||
const file = {}
|
||||
|
||||
|
@ -48,6 +48,10 @@ file.write = function (filepath, data = '') {
|
|||
return fs.writeFile(path.resolve(filepath), data, { encoding: 'utf8' }).catch(console.error)
|
||||
}
|
||||
|
||||
file.writeSync = function (filepath, data = '') {
|
||||
return fs.writeFileSync(path.resolve(filepath), data, { encoding: 'utf8' })
|
||||
}
|
||||
|
||||
file.clear = async function (filepath) {
|
||||
if (await file.exists(filepath)) return file.write(filepath, '')
|
||||
return true
|
||||
|
|
|
@ -7,3 +7,4 @@ exports.markdown = require('./markdown')
|
|||
exports.api = require('./api')
|
||||
exports.date = require('./date')
|
||||
exports.table = require('./table')
|
||||
exports.xml = require('./xml')
|
||||
|
|
|
@ -4,11 +4,10 @@ const grabber = require('epg-grabber')
|
|||
|
||||
const parser = {}
|
||||
|
||||
parser.parseChannels = async function(filepath) {
|
||||
parser.parseChannels = async function (filepath) {
|
||||
const content = await file.read(filepath)
|
||||
const channels = grabber.parseChannels(content)
|
||||
|
||||
return channels
|
||||
return grabber.parseChannels(content)
|
||||
}
|
||||
|
||||
parser.parseLogs = async function (filepath) {
|
||||
|
|
|
@ -1,61 +1,6 @@
|
|||
const { Command } = require('commander')
|
||||
const { db, logger } = require('../core')
|
||||
const path = require('path')
|
||||
const _ = require('lodash')
|
||||
const fs = require('fs')
|
||||
const xml = {}
|
||||
|
||||
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() {
|
||||
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) {
|
||||
xml.create = function (items, site) {
|
||||
let output = `<?xml version="1.0" encoding="UTF-8"?>\r\n<site site="${site}">\r\n <channels>\r\n`
|
||||
|
||||
items.forEach(channel => {
|
||||
|
@ -101,4 +46,4 @@ function escapeString(string, defaultValue = '') {
|
|||
.trim()
|
||||
}
|
||||
|
||||
module.exports = { json2xml }
|
||||
module.exports = xml
|
65
sites/artonline.tv/artonline.tv.config.js
Normal file
65
sites/artonline.tv/artonline.tv.config.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const dayjs = require('dayjs')
|
||||
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(timezone)
|
||||
dayjs.extend(utc)
|
||||
|
||||
module.exports = {
|
||||
site: 'artonline.tv',
|
||||
url: function ({ channel }) {
|
||||
return `https://www.artonline.tv/Home/Tvlist${channel.site_id}`
|
||||
},
|
||||
request: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
data: function({ date }) {
|
||||
const diff = date.diff(dayjs.utc().startOf('d'), 'd')
|
||||
const params = new URLSearchParams()
|
||||
params.append('objId', diff)
|
||||
|
||||
return params
|
||||
}
|
||||
},
|
||||
parser: function ({ content }) {
|
||||
const programs = []
|
||||
if(!content) return programs
|
||||
const items = JSON.parse(content)
|
||||
items.forEach(item => {
|
||||
const icon = parseIcon(item)
|
||||
const start = parseStart(item)
|
||||
const duration = parseDuration(item)
|
||||
const stop = start.add(duration, 's')
|
||||
programs.push({
|
||||
title: item.title,
|
||||
description: item.description,
|
||||
icon,
|
||||
start,
|
||||
stop
|
||||
})
|
||||
})
|
||||
|
||||
return programs
|
||||
}
|
||||
}
|
||||
|
||||
function parseStart(item) {
|
||||
const [_, M, D, YYYY] = item.adddate.match(/(\d+)\/(\d+)\/(\d+) /)
|
||||
const [HH, mm] = item.start_Time.split(':')
|
||||
|
||||
return dayjs(`${YYYY}-${M}-${D}T${HH}:${mm}:00`, 'YYYY-M-DTHH:mm:ss', 'Asia/Riyadh')
|
||||
}
|
||||
|
||||
function parseDuration(item) {
|
||||
const [__, HH, mm, ss] = item.duration.match(/(\d+):(\d+):(\d+)/)
|
||||
|
||||
return parseInt(HH) * 3600 + parseInt(mm) * 60 + parseInt(ss)
|
||||
}
|
||||
|
||||
function parseIcon(item) {
|
||||
return item.thumbnail ? `https://www.artonline.tv${item.thumbnail}` : null
|
||||
}
|
65
sites/artonline.tv/artonline.tv.test.js
Normal file
65
sites/artonline.tv/artonline.tv.test.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
// npx epg-grabber --config=sites/artonline.tv/artonline.tv.config.js --channels=sites/artonline.tv/artonline.tv_sa.channels.xml --output=guide.xml --days=2
|
||||
|
||||
const { parser, url, request } = require('./artonline.tv.config.js')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(utc)
|
||||
|
||||
const channel = {
|
||||
site_id: 'Aflam2',
|
||||
xmltv_id: 'ARTAflam2.sa'
|
||||
}
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ channel })).toBe('https://www.artonline.tv/Home/TvlistAflam2')
|
||||
})
|
||||
|
||||
it('can generate valid request method', () => {
|
||||
expect(request.method).toBe('POST')
|
||||
})
|
||||
|
||||
it('can generate valid request headers', () => {
|
||||
expect(request.headers).toMatchObject({
|
||||
'content-type': 'application/x-www-form-urlencoded'
|
||||
})
|
||||
})
|
||||
|
||||
it('can generate valid request data for today', () => {
|
||||
const date = dayjs.utc().startOf('d')
|
||||
const data = request.data({ date })
|
||||
expect(data.get('objId')).toBe('0')
|
||||
})
|
||||
|
||||
it('can generate valid request data for tomorrow', () => {
|
||||
const date = dayjs.utc().startOf('d').add(1, 'd')
|
||||
const data = request.data({ date })
|
||||
expect(data.get('objId')).toBe('1')
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = `[{"id":158963,"eventid":null,"duration":"01:34:00","lang":"Arabic","title":"الراقصه و السياسي","description":"تقرر الراقصه سونيا انشاء دار حضانه للأطفال اليتامى و عندما تتقدم بمشورعها للمسئول يرفض فتتحداه ، تلجأ للوزير عبد الحميد رأفت تربطه بها علاقة قديمة ، يخشى على مركزه و يرفض مساعدتها فتقرر كتابة مذكراتها بمساعدة أحد الصحفيين ، يتخوف عبد الحميد و المسئولين ثم يفاجأ عبد الحميد بحصول سونيا على الموافقه للمشورع و البدء في تنفيذه و ذلك لعلاقتها بأحد كبار المسئولين .","thumbnail":"/UploadImages/Channel/ARTAFLAM1/03/AlRaqesaWaAlSeyasi.jpg","image":"0","start_Time":"00:30","adddate":"3/4/2022 12:00:00 AM","repeat1":null,"iD_genre":0,"iD_Show_Type":0,"iD_Channel":77,"iD_country":0,"iD_rating":0,"end_time":"02:04","season_Number":0,"epoisode_Number":0,"hasCatchup":0,"cmsid":0,"containerID":0,"imagePath":"../../UploadImages/Channel/ARTAFLAM1/3/","youtube":"0","published_at":"0","directed_by":"0","composition":"0","cast":"0","timeShow":null,"short_description":"تقرر الراقصه سونيا انشاء دار حضانه للأطفال اليتامى و عندما تتقدم بمشورعها للمسئول يرفض فتتحداه ، تلجأ للوزير عبد الحميد رأفت تربطه بها علاقة قديمة ، يخشى على مركزه و يرفض مساعدتها فتقرر كتابة مذكراتها بمساعدة أحد الصحفيين ، يتخوف عبد الحميد و المسئولين ثم يفاجأ عبد الحميد بحصول سونيا على الموافقه للمشورع و البدء في تنفيذه و ذلك لعلاقتها بأحد كبار المسئولين .","seOdescription":null,"tagseo":null,"channel_name":null,"pathimage":null,"pathThumbnail":null}]`
|
||||
const result = parser({ content }).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
start: '2022-03-03T21:30:00.000Z',
|
||||
stop: '2022-03-03T23:04:00.000Z',
|
||||
title: 'الراقصه و السياسي',
|
||||
description: 'تقرر الراقصه سونيا انشاء دار حضانه للأطفال اليتامى و عندما تتقدم بمشورعها للمسئول يرفض فتتحداه ، تلجأ للوزير عبد الحميد رأفت تربطه بها علاقة قديمة ، يخشى على مركزه و يرفض مساعدتها فتقرر كتابة مذكراتها بمساعدة أحد الصحفيين ، يتخوف عبد الحميد و المسئولين ثم يفاجأ عبد الحميد بحصول سونيا على الموافقه للمشورع و البدء في تنفيذه و ذلك لعلاقتها بأحد كبار المسئولين .',
|
||||
icon: 'https://www.artonline.tv/UploadImages/Channel/ARTAFLAM1/03/AlRaqesaWaAlSeyasi.jpg'
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const result = parser({
|
||||
content: ''
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
10
sites/artonline.tv/artonline.tv_sa.channels.xml
Normal file
10
sites/artonline.tv/artonline.tv_sa.channels.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<site site="artonline.tv">
|
||||
<channels>
|
||||
<channel lang="ar" xmltv_id="ARTAflam1.sa" site_id="">ART Aflam 1</channel>
|
||||
<channel lang="ar" xmltv_id="ARTAflam2.sa" site_id="Aflam2">ART Aflam 2</channel>
|
||||
<channel lang="ar" xmltv_id="ARTCinema.sa" site_id="Cinema">ART Cinema</channel>
|
||||
<channel lang="ar" xmltv_id="ARTHekayat.sa" site_id="Hekayat">ART Hekayat</channel>
|
||||
<channel lang="ar" xmltv_id="ARTHekayat2.sa" site_id="Hekayat2">ART Hekayat 2</channel>
|
||||
</channels>
|
||||
</site>
|
|
@ -252,7 +252,7 @@
|
|||
<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="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="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>
|
||||
|
|
|
@ -80,6 +80,7 @@
|
|||
<channel lang="cz" xmltv_id="CSHistory.cz" site_id="CS+History">CS History</channel>
|
||||
<channel lang="cz" xmltv_id="CSHorror.cz" site_id="CS+Horror">CS Horror</channel>
|
||||
<channel lang="cz" xmltv_id="CSMystery.cz" site_id="CS+Mystery">CS Mystery</channel>
|
||||
<channel lang="cz" xmltv_id="ChuckTV.sk" site_id="Chuck%20TV">Chuck TV</channel>
|
||||
<channel lang="cz" xmltv_id="CT1.cz" site_id="%C8T1">CT 1</channel>
|
||||
<channel lang="cz" xmltv_id="CT2.cz" site_id="%C8T2">CT 2</channel>
|
||||
<channel lang="cz" xmltv_id="CT24.cz" site_id="%C8T24">CT 24</channel>
|
||||
|
@ -146,7 +147,7 @@
|
|||
<channel lang="cz" xmltv_id="FilmBoxFamily.us" site_id="Filmbox+Family">FilmBox Family</channel>
|
||||
<channel lang="cz" xmltv_id="FilmBoxPremiumCzechia.us" site_id="Filmbox+Premium">FilmBox Premium Czechia</channel>
|
||||
<channel lang="cz" xmltv_id="FilmBoxPremiumPolska.us" site_id="Filmbox+Premium+%28pl%29">FilmBox Premium Polska</channel>
|
||||
<channel lang="cz" xmltv_id="FilmBoxStarsHungary.us" site_id="Filmbox+Stars">FilmBox Stars Hungary</channel>
|
||||
<channel lang="cz" xmltv_id="FilmBoxStarsCzechia.us" site_id="Filmbox+Stars">FilmBox Stars Czechia</channel>
|
||||
<channel lang="cz" xmltv_id="FilmCafeHungary.hu" site_id="Film+Caf%E9">Film Café Hungary</channel>
|
||||
<channel lang="cz" xmltv_id="FilmEurope.cz" site_id="Film+Europe">Film Europe</channel>
|
||||
<channel lang="cz" xmltv_id="FilmEuropePlus.cz" site_id="Film+Europe+%2B">Film Europe +</channel>
|
||||
|
@ -254,6 +255,7 @@
|
|||
<channel lang="cz" xmltv_id="MyZenTV.fr" site_id="myZen">MyZen TV</channel>
|
||||
<channel lang="cz" xmltv_id="MyZenTV4K.fr" site_id="myZen+4K">MyZen TV 4K</channel>
|
||||
<channel lang="cz" xmltv_id="NASATVPublic.us" site_id="NASA+TV">NASA TV Public</channel>
|
||||
<channel lang="cz" xmltv_id="NASATVUHD.us" site_id="NASA+TV+UHD">NASA TV UHD Public</channel>
|
||||
<channel lang="cz" xmltv_id="NationalGeographicHrvatska.us" site_id="National+Geographic+%28hr%29">National Geographic Hrvatska</channel>
|
||||
<channel lang="cz" xmltv_id="NationalGeographicHungaryCzechia.us" site_id="National+Geographic">National Geographic Hungary & Czechia</channel>
|
||||
<channel lang="cz" xmltv_id="NationalGeographicUK.us" site_id="National+Geographic+HD+UK">National Geographic UK</channel>
|
||||
|
@ -296,7 +298,7 @@
|
|||
<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="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="PickUK.uk" site_id="Pick+TV">Pick UK</channel>
|
||||
<channel lang="cz" xmltv_id="PlayboyTVEurope.us" site_id="Playboy+TV">Playboy TV Europe</channel>
|
||||
|
@ -312,7 +314,7 @@
|
|||
<channel lang="cz" xmltv_id="PolsatSport.pl" site_id="Polsat+Sport">Polsat Sport</channel>
|
||||
<channel lang="cz" xmltv_id="PolsatSportExtra.pl" site_id="Polsat+Sport+Extra">Polsat Sport Extra</channel>
|
||||
<channel lang="cz" xmltv_id="PowerTV.pl" site_id="Power+TV">Power TV</channel>
|
||||
<channel lang="cz" xmltv_id="PremierSport.sk" site_id="Premier+Sport">Premier Sport</channel>
|
||||
<channel lang="cz" xmltv_id="PremierSport1.sk" site_id="Premier+Sport+1">Premier Sport 1</channel>
|
||||
<channel lang="cz" xmltv_id="PremierSport2.sk" site_id="Premier+Sport+2">Premier Sport 2</channel>
|
||||
<channel lang="cz" xmltv_id="Prima.cz" site_id="Prima">Prima</channel>
|
||||
<channel lang="cz" xmltv_id="PrimaCool.cz" site_id="Prima+Cool">Prima Cool</channel>
|
||||
|
@ -402,6 +404,7 @@
|
|||
<channel lang="cz" xmltv_id="SkySportsPremierLeagueUK.uk" site_id="Sky+Sports+Premier+League+HD">Sky Sports Premier League UK</channel>
|
||||
<channel lang="cz" xmltv_id="SkyWitnessUK.uk" site_id="Sky+Living+HD">Sky Witness UK</channel>
|
||||
<channel lang="cz" xmltv_id="SlagerTV.hu" site_id="Sl%E1ger+TV">Sláger TV</channel>
|
||||
<channel lang="cz" xmltv_id="SlagrOriginal.cz" site_id="%8Al%E1gr+Origin%E1l">Slágr Original</channel>
|
||||
<channel lang="cz" xmltv_id="Slagr2.cz" site_id="%8Al%E1gr+2">Slágr 2</channel>
|
||||
<channel lang="cz" xmltv_id="SlagrMuzika.cz" site_id="%8Al%E1gr+Muzika">Slágr Muzika</channel>
|
||||
<channel lang="cz" xmltv_id="SlagrPremium.cz" site_id="%8Al%E1gr+Premium">Slágr Premium</channel>
|
||||
|
@ -468,6 +471,7 @@
|
|||
<channel lang="cz" xmltv_id="TVN.pl" site_id="TVN">TVN</channel>
|
||||
<channel lang="cz" xmltv_id="TVN24.pl" site_id="TVN24">TVN 24</channel>
|
||||
<channel lang="cz" xmltv_id="TVN7.pl" site_id="TVN7">TVN 7</channel>
|
||||
<channel lang="cz" xmltv_id="TVNatura.cz" site_id="TV+Natura">TV Natura</channel>
|
||||
<channel lang="cz" xmltv_id="TVNoe.cz" site_id="Noe">TV Noe</channel>
|
||||
<channel lang="cz" xmltv_id="TVNova.cz" site_id="Nova">TV Nova</channel>
|
||||
<channel lang="cz" xmltv_id="TVNTurbo.pl" site_id="TVN+Turbo">TVN Turbo</channel>
|
||||
|
|
|
@ -80,7 +80,7 @@
|
|||
<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="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="ru" xmltv_id="PyatnitsaInternational.ru" site_id="124">Pyatnitsa! International</channel>
|
||||
<channel lang="ka" xmltv_id="QartuliArkhi.ge" site_id="195">Qartuli Arkhi</channel>
|
||||
|
|
|
@ -120,7 +120,7 @@
|
|||
<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="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="PlayboyTVEurope.us" site_id="PLAY">Playboy TV Europe</channel>
|
||||
<channel lang="pt" xmltv_id="PortoCanal.pt" site_id="PORTO">Porto Canal</channel>
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
<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="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="BeInSports2.qa" site_id="bein-sports-2-184">BeIn Sports 2</channel>
|
||||
<channel lang="fr" xmltv_id="BeInSports3.qa" site_id="bein-sports-3-265">BeIn Sports 3</channel>
|
||||
<channel lang="fr" xmltv_id="BeInSports1France.qa" site_id="bein-sports-1-183">BeIn Sports 1 France</channel>
|
||||
<channel lang="fr" xmltv_id="BeInSports2France.qa" site_id="bein-sports-2-184">BeIn Sports 2 France</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="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>
|
||||
|
|
|
@ -198,7 +198,7 @@
|
|||
<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="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="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>
|
||||
|
|
45
sites/sky.de/sky.de.config.js
Normal file
45
sites/sky.de/sky.de.config.js
Normal 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 : []
|
||||
}
|
55
sites/sky.de/sky.de.test.js
Normal file
55
sites/sky.de/sky.de.test.js
Normal 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([])
|
||||
})
|
123
sites/sky.de/sky.de_de.channels.xml
Normal file
123
sites/sky.de/sky.de_de.channels.xml
Normal 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>
|
|
@ -84,7 +84,7 @@
|
|||
<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="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="ProSiebenDeutschland.de" site_id="60">ProSieben Deutschland</channel>
|
||||
<channel lang="en" xmltv_id="PyatnitsaInternational.ru" site_id="1304">Pyatnitsa! International</channel>
|
||||
|
|
|
@ -84,7 +84,7 @@
|
|||
<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="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="ProSiebenDeutschland.de" site_id="60">ProSieben Deutschland</channel>
|
||||
<channel lang="et" xmltv_id="PyatnitsaInternational.ru" site_id="1304">Pyatnitsa! International</channel>
|
||||
|
|
|
@ -84,7 +84,7 @@
|
|||
<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="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="ProSiebenDeutschland.de" site_id="60">ProSieben Deutschland</channel>
|
||||
<channel lang="ru" xmltv_id="PyatnitsaInternational.ru" site_id="1304">Pyatnitsa! International</channel>
|
||||
|
|
|
@ -546,7 +546,7 @@
|
|||
<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="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="TelebomTeledom.de" site_id="587">Telebom & Teledom</channel>
|
||||
<channel lang="sq" xmltv_id="ABCNews.al" site_id="1880">ABC News</channel>
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
<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="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="PlanetaHD.bg" site_id="60">Planeta HD</channel>
|
||||
<channel lang="bg" xmltv_id="POTV.bg" site_id="211">POTV</channel>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<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="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="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>
|
||||
|
|
|
@ -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="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="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="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>
|
||||
|
|
|
@ -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="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="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="Poehali.ru" site_id="213#poehali-1246">Poehali!</channel>
|
||||
<channel lang="ru" xmltv_id="Priklyucheniya.ru" site_id="213#priklyucheniya-hd-499">Priklyucheniya</channel>
|
||||
|
|
|
@ -168,7 +168,7 @@
|
|||
<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="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="PinkExtra.rs" site_id="1000095">Pink Extra</channel>
|
||||
<channel lang="sl" xmltv_id="PinkFilm.rs" site_id="1000096">Pink Film</channel>
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
<channel lang="fr" xmltv_id="LCN.ca" site_id="67231">LCN</channel>
|
||||
<channel lang="en" xmltv_id="LeafsNationNetwork.ca" site_id="60020">Leafs Nation Network</channel>
|
||||
<channel lang="en" xmltv_id="PrimeAsiaTV.ca" site_id="32677">Prime Asia TV</channel>
|
||||
<channel lang="en" xmltv_id="StingrayQello.ca" site_id="113296">Qello Concerts by Stingray</channel>
|
||||
<channel lang="en" xmltv_id="SaisonsCanada.ca" site_id="20046">Saisons Canada</channel>
|
||||
<channel lang="en" xmltv_id="SportsmanChannelCanada.us" site_id="32992">Sportsman Channel Canada</channel>
|
||||
<channel lang="en" xmltv_id="SportsNetCanucks.ca" site_id="69200">SportsNet Canucks</channel>
|
||||
|
@ -58,13 +59,25 @@
|
|||
<channel lang="en" xmltv_id="StingrayBroadway.ca" site_id="19050">Stingray Broadway</channel>
|
||||
<channel lang="en" xmltv_id="StingrayClassica.ca" site_id="32619">Stingray Classica</channel>
|
||||
<channel lang="en" xmltv_id="StingrayClassicRB.ca" site_id="19047">Stingray Classic R&B</channel>
|
||||
<channel lang="en" xmltv_id="StingrayClassicRock.ca" site_id="67567">Stingray Classic Rock</channel>
|
||||
<channel lang="en" xmltv_id="StingrayCountry.ca" site_id="35064">Stingray Country</channel>
|
||||
<channel lang="en" xmltv_id="StingrayTodaysLatinPop.ca" site_id="68639">Stingray Exitos del Momento</channel>
|
||||
<channel lang="en" xmltv_id="StingrayFestival4K.ca" site_id="34379">Stingray Festival 4K</channel>
|
||||
<channel lang="en" xmltv_id="StingrayFlashback70s.ca" site_id="67565">Stingray Flashback 70s</channel>
|
||||
<channel lang="en" xmltv_id="StingrayFrancoFetes.ca" site_id="17046">Stingray Franco Fêtes</channel>
|
||||
<channel lang="en" xmltv_id="StingrayGospel.ca" site_id="18845">Stingray Gospel</channel>
|
||||
<channel lang="en" xmltv_id="StingrayGreatestHits.ca" site_id="110433">Stingray Greatest Hits</channel>
|
||||
<channel lang="en" xmltv_id="StingrayUrbanBeat.ca" site_id="67564">Stingray Hip-Hop/R&B</channel>
|
||||
<channel lang="en" xmltv_id="StingrayHitList.ca" site_id="67555">Stingray Hit List</channel>
|
||||
<channel lang="en" xmltv_id="StingrayHotCountry.ca" site_id="67557">Stingray Hot Country</channel>
|
||||
<channel lang="en" xmltv_id="StingrayKaraoke.ca" site_id="110657">Stingray Karaoke</channel>
|
||||
<channel lang="en" xmltv_id="StingrayNaturescape.ca" site_id="90613">Stingray Naturescape</channel>
|
||||
<channel lang="en" xmltv_id="StingrayNoFences.ca" site_id="18841">Stingray No Fences</channel>
|
||||
<channel lang="en" xmltv_id="StingrayPopAdult.ca" site_id="67558">Stingray Pop Adult</channel>
|
||||
<channel lang="en" xmltv_id="StingrayEverything80s.ca" site_id="67566">Stingray Remember the 80's</channel>
|
||||
<channel lang="en" xmltv_id="StingrayRockAlternative.ca" site_id="67568">Stingray Rock Alternative</channel>
|
||||
<channel lang="en" xmltv_id="StingrayRomanceLatino.ca" site_id="19049">Stingray Romance Latino</channel>
|
||||
<channel lang="en" xmltv_id="StingraySoulStorm.ca" site_id="18840">Stingray Soul Storm</channel>
|
||||
<channel lang="en" xmltv_id="StingraySoulStorm.ca" site_id="67563">Stingray Soul Storm</channel>
|
||||
<channel lang="en" xmltv_id="Telebimbi.ca" site_id="19655">Telebimbi</channel>
|
||||
<channel lang="en" xmltv_id="TheRuralChannel.ca" site_id="11109">The Rural Channel</channel>
|
||||
<channel lang="en" xmltv_id="TSN1.ca" site_id="11182">TSN1</channel>
|
||||
|
|
|
@ -1677,5 +1677,46 @@
|
|||
<channel lang="en" xmltv_id="VSiN.us" site_id="108970">VSiN</channel>
|
||||
<channel lang="en" xmltv_id="WeatherNation.us" site_id="72413">WeatherNation</channel>
|
||||
<channel lang="en" xmltv_id="PlutoTVNick.us" site_id="121301">Nick on PlutoTV</channel>
|
||||
<channel lang="en" xmltv_id="WFTVDT1.us" site_id="20491">ABC 9 (WFTV-DT1) Orlando FL</channel>
|
||||
<channel lang="en" xmltv_id="WKMGDT1.us" site_id="21299">CBS 6 (WKMG-DT1) Orlando FL</channel>
|
||||
<channel lang="en" xmltv_id="WOFLDT1.us" site_id="21221">FOX 35 (WOFL-DT1) Orlando FL</channel>
|
||||
<channel lang="en" xmltv_id="WRBWDT1.us" site_id="26191">My65 / FOX 35 Plus (WRBW-DT1) Orlando FL</channel>
|
||||
<channel lang="en" xmltv_id="WESHDT1.us" site_id="21647">NBC 2 (WESH-DT1) Orlando FL</channel>
|
||||
<channel lang="en" xmltv_id="WESHDT2.us" site_id="77535">MeTV (WESH-DT2) Orlando FL</channel>
|
||||
<channel lang="en" xmltv_id="WKCFDT1.us" site_id="26588">CW 18(WKCF-DT1) Orlando FL</channel>
|
||||
<channel lang="en" xmltv_id="WTMOCD1.us" site_id="78613">Telemundo 31 (WTMO-CD1) Orlando FL</channel>
|
||||
<channel lang="en" xmltv_id="WVENDT1.us" site_id="34579">Univision 43 (WVEN-DT1) Orlando FL</channel>
|
||||
<channel lang="en" xmltv_id="WRCFCD1.us" site_id="83296">UniMás 29 (WRCF-CD1) Orlando FL</channel>
|
||||
<channel lang="en" xmltv_id="NESNPlus.us" site_id="63516">NESN Plus</channel>
|
||||
<channel lang="en" xmltv_id="WTTVDT1.us" site_id="35595">CBS (WTTV-DT1) Indianapolis IN</channel>
|
||||
<channel lang="en" xmltv_id="WTTVDT2.us" site_id="50839">the Dot (WTTV-DT2) Indianapolis IN</channel>
|
||||
<channel lang="en" xmltv_id="WTTVDT3.us" site_id="70497">Comet (WTTV-DT3) Indianapolis IN</channel>
|
||||
<channel lang="en" xmltv_id="WRTVDT1.us" site_id="21102">abc (WRTV-DT1) Indianapolis IN</channel>
|
||||
<channel lang="en" xmltv_id="WRTVDT2.us" site_id="48923">Grit (WRTV-DT2) Indianapolis IN</channel>
|
||||
<channel lang="en" xmltv_id="WRTVDT3.us" site_id="73364">Laff (WRTV-DT3) Indianapolis IN</channel>
|
||||
<channel lang="en" xmltv_id="WRTVDT4.us" site_id="95139">QVC (WRTV-DT4) Indianapolis IN</channel>
|
||||
<channel lang="en" xmltv_id="WTHRDT1.us" site_id="19593">NBC (WTHR-DT1) Indianapolis IN</channel>
|
||||
<channel lang="en" xmltv_id="WTHRDT2.us" site_id="30411">Dabl (WTHR-DT2) Indianapolis IN</channel>
|
||||
<channel lang="en" xmltv_id="WTHRDT3.us" site_id="62985">MeTV (WTHR-DT3) Indianapolis IN</channel>
|
||||
<channel lang="en" xmltv_id="WTHRDT4.us" site_id="116940">True Crime Network (WTHR-DT4) Indianapolis IN</channel>
|
||||
<channel lang="en" xmltv_id="WTHRDT5.us" site_id="113175">Quest (WTHR-DT5) Indianapolis IN</channel>
|
||||
<channel lang="en" xmltv_id="WTHRDT6.us" site_id="114151">Circle (WTHR-DT6) Indianapolis IN</channel>
|
||||
<channel lang="en" xmltv_id="WISHDT1.us" site_id="19592">CW (WISH-DT1) Indianapolis IN</channel>
|
||||
<channel lang="en" xmltv_id="WISHDT2.us" site_id="25526">GetTV (WISH-DT2) Indianapolis IN</channel>
|
||||
<channel lang="en" xmltv_id="WISHDT3.us" site_id="25527">Twist (WISH-DT3) Indianapolis IN</channel>
|
||||
<channel lang="en" xmltv_id="WFYIDT1.us" site_id="33787">PBS (WFYI-DT1) Indianapolis IN</channel>
|
||||
<channel lang="en" xmltv_id="WFYIDT2.us" site_id="33788">PBS Kids (WFYI-DT2) Indianapolis IN</channel>
|
||||
<channel lang="en" xmltv_id="WFYIDT3.us" site_id="44032">Create (WFYI-DT3) Indianapolis IN</channel>
|
||||
<channel lang="en" xmltv_id="WDNICD1.us" site_id="68610">Telemundo (WDNI-CD1) Indianapolis IN</channel>
|
||||
<channel lang="en" xmltv_id="WCVBDT1.us" site_id="19596">abc (WCVB-DT1) Boston MA</channel>
|
||||
<channel lang="en" xmltv_id="WCVBDT2.us" site_id="35356">MeTV (WCVB-DT2) Boston MA</channel>
|
||||
<channel lang="en" xmltv_id="WBZDT1.us" site_id="20431">CBS (WBZ-DT1) Boston MA</channel>
|
||||
<channel lang="en" xmltv_id="WBZDT2.us" site_id="91906">Start TV (WBZ-DT2) Boston MA</channel>
|
||||
<channel lang="en" xmltv_id="WBZDT3.us" site_id="112382">Dabl (WBZ-DT3) Boston MA</channel>
|
||||
<channel lang="en" xmltv_id="WLVIDT1.us" site_id="32633">CW (WLVI-DT1) Boston MA</channel>
|
||||
<channel lang="en" xmltv_id="WLVIDT2.us" site_id="50830">Buzzr (WLVI-DT2) Boston MA</channel>
|
||||
<channel lang="en" xmltv_id="UnivisionEste.us" site_id="68049">Univision East (National Feed)</channel>
|
||||
<channel lang="en" xmltv_id="UnivisionOeste.us" site_id="14762">Univision West (National Feed)</channel>
|
||||
<channel lang="en" xmltv_id="UniMasOeste.us" site_id="31541">Unimas West (National Feed)</channel>
|
||||
</channels>
|
||||
</site>
|
||||
|
|
0
tests/__data__/database/queue.db
Normal file
0
tests/__data__/database/queue.db
Normal 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>
|
||||
</table>
|
||||
|
||||
## EPG Codes
|
||||
|
||||
📋 [iptv-org.github.io](https://iptv-org.github.io/)
|
||||
|
||||
## API
|
||||
|
||||
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
|
||||
|
||||
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).
|
|
@ -1 +0,0 @@
|
|||
{"xmltv_id":"CNNInternationalEurope2.us","site":"example.com","site_id":"141","lang":"en","error":"The channel has the wrong xmltv_id"}
|
|
@ -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"}
|
|
@ -1 +0,0 @@
|
|||
{"xmltv_id":"Perviykanal.ru","site":"yandex.ru","site_id":"1","lang":"ru","date":"2022-01-21T00:00:00Z","error":"Some error"}
|
|
@ -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"}
|
|
@ -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>
|
|
@ -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"}
|
7
tests/__data__/input/sites/duplicate.channels.xml
Normal file
7
tests/__data__/input/sites/duplicate.channels.xml
Normal 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>
|
6
tests/__data__/input/sites/lint.channels.xml
Normal file
6
tests/__data__/input/sites/lint.channels.xml
Normal 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>
|
19
tests/__data__/input/sites/parse-channels.config.js
Normal file
19
tests/__data__/input/sites/parse-channels.config.js
Normal 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'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
6
tests/__data__/input/sites/wrong_xmltv_id.channels.xml
Normal file
6
tests/__data__/input/sites/wrong_xmltv_id.channels.xml
Normal 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>
|
26
tests/commands/api/update.test.js
Normal file
26
tests/commands/api/update.test.js
Normal 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)
|
||||
}
|
19
tests/commands/channels/lint.test.js
Normal file
19
tests/commands/channels/lint.test.js
Normal 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`
|
||||
)
|
||||
}
|
||||
})
|
24
tests/commands/channels/parse.test.js
Normal file
24
tests/commands/channels/parse.test.js
Normal 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'
|
||||
})
|
||||
}
|
49
tests/commands/channels/validate.test.js
Normal file
49
tests/commands/channels/validate.test.js
Normal 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`
|
||||
)
|
||||
}
|
||||
})
|
32
tests/commands/cluster/load.test.js
Normal file
32
tests/commands/cluster/load.test.js
Normal 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)
|
||||
})
|
||||
}
|
|
@ -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')
|
||||
})
|
32
tests/commands/guides/update.test.js
Normal file
32
tests/commands/guides/update.test.js
Normal 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)
|
||||
}
|
|
@ -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)
|
||||
})
|
||||
}
|
|
@ -1,25 +1,19 @@
|
|||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const { execSync } = require('child_process')
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
|
||||
beforeEach(() => {
|
||||
fs.rmdirSync('tests/__data__/output', { recursive: true })
|
||||
fs.mkdirSync('tests/__data__/output')
|
||||
fs.mkdirSync('tests/__data__/output/database', { recursive: true })
|
||||
|
||||
fs.copyFileSync(
|
||||
'tests/__data__/input/database/queue.db',
|
||||
'tests/__data__/output/database/queue.db'
|
||||
)
|
||||
fs.emptyDirSync('tests/__data__/output')
|
||||
fs.copyFileSync('tests/__data__/input/database/queue.db', 'tests/__data__/output/queue.db')
|
||||
|
||||
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' }
|
||||
)
|
||||
})
|
||||
|
||||
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')
|
||||
|
||||
output = output.map(i => {
|
||||
|
@ -35,10 +29,9 @@ it('can save programs to database', () => {
|
|||
})
|
||||
|
||||
it('can update queue', () => {
|
||||
const output = content('tests/__data__/output/database/queue.db')
|
||||
const expected = content('tests/__data__/expected/database/queue-with-errors.db')
|
||||
|
||||
expect(output).toEqual(expected)
|
||||
expect(content('tests/__data__/output/queue.db')).toEqual(
|
||||
content('tests/__data__/expected/database/queue-with-errors.db')
|
||||
)
|
||||
})
|
||||
|
||||
function content(filepath) {
|
|
@ -1,13 +1,12 @@
|
|||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const { execSync } = require('child_process')
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
|
||||
beforeEach(() => {
|
||||
fs.rmdirSync('tests/__data__/output', { recursive: true })
|
||||
fs.mkdirSync('tests/__data__/output')
|
||||
fs.emptyDirSync('tests/__data__/output')
|
||||
|
||||
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' }
|
||||
)
|
||||
})
|
||||
|
@ -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) {
|
||||
const data = fs.readFileSync(path.resolve(filepath), {
|
||||
encoding: 'utf8'
|
26
tests/commands/readme/update.test.js
Normal file
26
tests/commands/readme/update.test.js
Normal 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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue