Compare commits

..
52 Commits
Author SHA1 Message Date
david 8edb33819e update readme
Build docker container / build (push) Successful in 15m56s
2026-10-29 09:29:05 +01:00
david 6fe29e4d71 update readme
Build docker container / build (push) Successful in 2m51s
2026-10-27 12:25:57 +01:00
david 0ff4dc7c7a fix issue with custom fix 2026-10-27 12:25:05 +01:00
david f2959af0ac update readme
Build docker container / build (push) Successful in 3m33s
2026-10-27 10:52:21 +01:00
david 1062769e13 add fixes to pick.be provider 2026-10-27 10:45:02 +01:00
david bcfd38cb19 update custom fixes to latest code 2026-10-27 10:43:05 +01:00
david 3876463747 update readme
Build docker container / build (push) Successful in 3m5s
2026-10-25 15:52:20 +01:00
david 8411e28b0a update readme 2026-10-25 15:51:44 +01:00
david 5f228031e1 update readme
Build docker container / build (push) Successful in 4m4s
2026-10-23 14:28:36 +02:00
david 21881c43a7 update readme
Build docker container / build (push) Successful in 4m44s
2026-10-06 10:59:36 +02:00
david d72cc13659 update action 2026-09-30 15:40:07 +02:00
david 3a325f4cb3 update readme
Build docker container / build (push) Successful in 11m48s
2026-09-25 13:01:38 +02:00
david 59ba4f298e update readme
Build docker container / build (push) Successful in 15m16s
2026-09-08 13:09:58 +02:00
david fd1df8ac0b update readme 2026-08-05 11:33:24 +02:00
david 65c2ed8208 update build action 2026-08-05 11:27:01 +02:00
david 054adcd9ec update readme
Build docker container / build (push) Successful in 17m47s
2026-08-05 10:52:22 +02:00
david 51a830bb2c update readme and build action
Build docker container / build (push) Successful in 21m24s
2026-07-22 09:20:10 +02:00
david cedf4cb9cf update readme
Build docker container / build (push) Successful in 23m6s
2026-07-09 15:45:11 +02:00
david 09b2a32c7e update readme
Build docker container / build (push) Successful in 19m37s
2026-06-09 08:20:41 +02:00
david faf359760e update readme
Build docker container / build (push) Successful in 14m3s
2026-05-22 08:44:09 +02:00
david d1c2a2cce6 update readme
Build docker container / build (push) Successful in 21m35s
2026-04-17 08:34:17 +02:00
david f10acb48dd update readme
Build docker container / build (push) Successful in 10m44s
2026-04-13 09:59:08 +02:00
david 97b2e2b7c9 fix issues in docker file
Build docker container / build (push) Successful in 9m6s
2026-04-01 16:37:22 +02:00
david 421bebb294 update readme
Build docker container / build (push) Failing after 14m5s
2026-04-01 15:44:22 +02:00
david b77bf97acd update readme
Build docker container / build (push) Successful in 8m43s
2026-03-23 08:41:38 +01:00
david b671040deb update readme
Build docker container / build (push) Successful in 8m43s
2026-03-16 09:12:30 +01:00
david 86e66ede1d update readme and update custom fixes
Build docker container / build (push) Successful in 15m44s
2026-02-24 12:46:24 +01:00
david c113fd0bbc update readme
Build docker container / build (push) Successful in 10m25s
2026-02-18 12:56:29 +01:00
david 54f8e6b157 update action 2026-02-18 12:54:40 +01:00
david be44be878c change action
Build docker container / build (push) Successful in 7m33s
2026-02-12 10:17:30 +01:00
david 1700fbe251 update readme
Build docker container / build (push) Failing after 1m49s
2026-02-12 08:59:35 +01:00
david b760bbdf3e update action 2026-02-12 08:58:18 +01:00
david 8c609f5515 update action 2026-02-09 15:48:15 +01:00
david ff31a3b737 update action 2026-02-09 15:32:40 +01:00
david 123285107d update action
Build docker container / build (push) Successful in 8m2s
2026-02-09 10:16:06 +01:00
david 3d13569146 update action 2026-02-09 09:57:40 +01:00
david e9bc6f5246 update action
Build docker container / build (push) Failing after 6m51s
2026-02-09 09:37:49 +01:00
david 5c662bae69 update docker action + readme
Build docker container / build (push) Failing after 7m26s
2026-02-09 09:25:34 +01:00
David Claeys eece21742c update readme
Build docker container / build (push) Successful in 8m2s
2026-01-08 13:44:06 +01:00
David Claeys ee126cf60c update readme
Build docker container / build (push) Successful in 8m51s
2025-12-24 14:25:57 +01:00
David Claeys db28863295 update readme
Build docker container / build (push) Successful in 11m38s
2025-11-26 11:26:24 +01:00
David Claeys 223a4ea6b5 update readme
Build docker container / build (push) Successful in 7m36s
2025-11-19 10:53:46 +01:00
David Claeys e6ee948355 update readme
Build docker container / build (push) Successful in 9m23s
2025-10-09 14:18:30 +02:00
David Claeys 2662567576 update readme
Build docker container / build (push) Successful in 18m38s
2025-09-25 09:36:24 +02:00
David Claeys 309038bb4a update readme + config
Build docker container / build (push) Successful in 25m21s
2025-07-15 09:24:34 +02:00
David Claeys 3b019df919 update readme
Build docker container / build (push) Successful in 8m14s
2025-07-07 08:53:11 +02:00
David Claeys 1cf21ddb98 update readme
Build docker container / build (push) Successful in 8m52s
2025-06-27 11:15:21 +02:00
David Claeys 0aeb48fe2f update readme
Build docker container / build (push) Successful in 6m37s
2025-06-17 08:10:34 +02:00
David Claeys 70c7ee79b0 update readme
Build docker container / build (push) Successful in 8m4s
2025-06-02 11:13:55 +02:00
David Claeys 62937901c2 update readme
Build docker container / build (push) Successful in 6m34s
2025-05-16 08:42:21 +02:00
David Claeys 4e5a2b4d27 update readme
Build docker container / build (push) Successful in 6m3s
2025-04-23 08:43:25 +02:00
David Claeys ab9a8d7160 add delay option
Build docker container / build (push) Successful in 6m12s
2025-04-14 09:15:43 +02:00
12 changed files with 1027 additions and 489 deletions

No files matched your search

+19 -14
View File
@@ -8,21 +8,12 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
defaults: defaults:
run: run:
working-directory: ${{ GITHUB_WORKSPACE }} working-directory: ${{ github.workspace }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Docker - name: Setup docker
run: | uses: https://git.claeyscloud.com/david/gitea-actions/setup-docker@main
echo "Checking docker installation"
if command -v docker &> /dev/null; then
echo "Docker installation found"
else
echo "Docker installation not found. Docker will be installed"
curl -fsSL https://get.docker.com | sh
fi
- name: Set up Docker Buildx
uses: https://github.com/docker/setup-buildx-action@v3
- name: Login to Gitea container registry - name: Login to Gitea container registry
uses: https://github.com/docker/login-action@v3 uses: https://github.com/docker/login-action@v3
with: with:
@@ -32,8 +23,14 @@ jobs:
- name: Login to DockerHub container registry - name: Login to DockerHub container registry
uses: https://github.com/docker/login-action@v3 uses: https://github.com/docker/login-action@v3
with: with:
username: ${{ secrets.DOCKER_HUB_USERNAME}} username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }} password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: Login to Lavaforge container registry
uses: https://github.com/docker/login-action@v3
with:
registry: lavaforge.org
username: ${{ secrets.LAVAFORGE_USERNAME }}
password: ${{ secrets.LAVAFORGE_PASSWORD }}
- name: Extract metadata (tags, labels) for Docker - name: Extract metadata (tags, labels) for Docker
id: meta id: meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v5
@@ -46,9 +43,18 @@ jobs:
images: | images: |
davidquinonescl/epg-info davidquinonescl/epg-info
git.claeyscloud.com/david/epg-info git.claeyscloud.com/david/epg-info
lavaforge.org/davidquinonescl/epg-info
flavor: |
latest=true
tags: | tags: |
type=semver,pattern={{raw}} type=semver,pattern={{raw}}
type=sha type=sha
- name: Update docker Hub Description
uses: https://git.claeyscloud.com/david/gitea-actions/dockerhub-description@main
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
repository: davidquinonescl/epg-info
- name: Build and push - name: Build and push
uses: https://github.com/docker/build-push-action@v5 uses: https://github.com/docker/build-push-action@v5
with: with:
@@ -56,4 +62,3 @@ jobs:
push: true push: true
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
+13 -10
View File
@@ -1,9 +1,10 @@
FROM node:22-alpine FROM node:24-alpine
ARG GIT_REPO=https://github.com/iptv-org/epg.git ARG GIT_REPO=https://github.com/iptv-org/epg.git
ARG GIT_BRANCH=master ARG GIT_BRANCH=master
ENV CRON_SCHEDULE="0 0,12 * * *" ENV CRON_SCHEDULE="0 0,12 * * *"
ENV API_URL="https://iptv-org.github.io/api" ENV API_URL="https://iptv-org.github.io/api"
ENV DAYS=14 ENV DAYS=14
ENV DELAY=0
ENV MAX_CONNECTIONS=10 ENV MAX_CONNECTIONS=10
ENV ENABLE_FIXES=false ENV ENABLE_FIXES=false
ARG BIN_FOLDER=/bin ARG BIN_FOLDER=/bin
@@ -22,20 +23,22 @@ RUN apk update \
&& npm install pm2 -g \ && npm install pm2 -g \
&& mkdir $(echo "${BIN_FOLDER}/${EPG_FOLDER}") -p \ && mkdir $(echo "${BIN_FOLDER}/${EPG_FOLDER}") -p \
&& git -C $(echo "${BIN_FOLDER}") clone --depth 1 -b $(echo "${GIT_BRANCH} ${GIT_REPO}") \ && git -C $(echo "${BIN_FOLDER}") clone --depth 1 -b $(echo "${GIT_BRANCH} ${GIT_REPO}") \
&& cd $WORKDIR && npm install && npm update \ && cd $WORKDIR && npm install && npm update \
&& rm -rf .sites \ && rm -rf .sites \
&& rm -rf .husky \ && rm -rf .husky \
&& rm -rf .github \ && rm -rf .github \
&& rm -rf .git \ && rm -rf .git \
&& rm .gitignore \ && rm Dockerfile \
&& rm CONTRIBUTING.md \ && rm .gitignore \
&& rm LICENSE \ && rm -f CONTRIBUTING.md \
&& rm README.md \ && rm -f GUIDES.md \
&& rm SITES.md \ && rm -f LICENSE \
&& rm -f README.md \
&& rm -f SITES.md \
&& rm -rf tests \ && rm -rf tests \
&& rm sites/**/readme.md \ && rm -f sites/**/readme.md \
&& rm -rf sites/**/__data__ \ && rm -rf sites/**/__data__ \
&& rm sites/**/**.test.js \ && rm -f sites/**/**.test.js \
&& rm -rf node_modules/**/.package-lock.json \ && rm -rf node_modules/**/.package-lock.json \
&& rm -rf node_modules/**/.tsconfig.json \ && rm -rf node_modules/**/.tsconfig.json \
&& rm -rf node_modules/**/.tsconfig.tsbuildinfo.json \ && rm -rf node_modules/**/.tsconfig.tsbuildinfo.json \
@@ -57,5 +60,5 @@ RUN chmod +x "$START_SCRIPT" \
&& apk del git curl \ && apk del git curl \
&& rm -rf /var/cache/apk/* && rm -rf /var/cache/apk/*
SHELL ["/bin/bash", "-c"] SHELL ["/bin/bash", "-c"]
ENTRYPOINT bash $START_SCRIPT chron-schedule="$CRON_SCHEDULE" work-dir="$WORKDIR" days="$DAYS" max_connections="$MAX_CONNECTIONS" enable_fixes="$ENABLE_FIXES" api_url="$API_URL" ENTRYPOINT bash $START_SCRIPT chron-schedule="$CRON_SCHEDULE" work-dir="$WORKDIR" days="$DAYS" delay=$DELAY max_connections="$MAX_CONNECTIONS" enable_fixes="$ENABLE_FIXES" api_url="$API_URL"
EXPOSE 3000 EXPOSE 3000
+91 -8
View File
@@ -4,14 +4,20 @@ This repo builds and Docker image of [iptv-org/epg](https://github.com/iptv-org/
The purpose is to make the deployment easier and more suitable for different environments. The purpose is to make the deployment easier and more suitable for different environments.
The original repository of this image is hosted on https://git.claeyscloud.com/david/epg-info-docker.<br> The original repository of this image is hosted on https://git.claeyscloud.com/david/epg-info-docker.<br>
A public mirror is available at https://github.com/davidclaeysquinones/epg-info-docker. A mirror of this repository can be found in https://github.com/davidclaeysquinones/epg-info-docker and in [lavaforge](https://lavaforge.org/davidquinonescl/epg-info-docker).
The image is distributed in the following container registries :
- [gitea](https://git.claeyscloud.com/david/-/packages/container/epg-info/latest)
- [lavaforge](https://lavaforge.org/davidquinonescl/-/packages/container/epg-info/latest)
- [github](https://github.com/davidclaeysquinones/epg-info-docker/pkgs/container/epg-info)
- [docker hub](https://hub.docker.com/repository/docker/davidquinonescl/epg-info/general)
## Dependencies ## Dependencies
[Node](https://nodejs.org/en)<br> [Node](https://nodejs.org/en)<br>
[pm2](https://www.npmjs.com/package/pm2)<br> [pm2](https://www.npmjs.com/package/pm2)<br>
[serve](https://www.npmjs.com/package/serve)<br> [serve](https://www.npmjs.com/package/serve)<br>
The image is based on `node:22-alpine` in order to be more lightweight. The image is based on `node:24-alpine` in order to be more lightweight.
The `pm2` and `serve` packages are used in order to run the application in the container. The `pm2` and `serve` packages are used in order to run the application in the container.
## Docker image ## Docker image
@@ -40,11 +46,11 @@ This the list of the provided custom fixes :
| Provider | Author(s) | Status | | Provider | Author(s) | Status |
|--------------------|------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| |--------------------|------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| movistarplus.es | [davidclaeysquinones](https://github.com/davidclaeysquinones) | [PR](https://github.com/iptv-org/epg/pull/2440) pending approval | | movistarplus.es | | |
| orangetv.orange.es | [fraudiay79](https://github.com/fraudiay79) and [davidclaeysquinones](https://github.com/davidclaeysquinones) | [PR](https://github.com/iptv-org/epg/pull/2485) merged since commit [8a8262e](https://github.com/iptv-org/epg/commit/8a8262eacb46b2d35df7eb11f46de22263eab053) | | orangetv.orange.es | | |
| pickx.be | [davidclaeysquinones](https://github.com/davidclaeysquinones) and [BellezaEmporium](https://github.com/BellezaEmporium) | [PR](https://github.com/iptv-org/epg/pull/2525) merged since commit [fd91a9c](https://github.com/iptv-org/epg/commit/fd91a9c532b476f6e192a564371d30e766b762ab) | | pickx.be | [davidclaeysquinones](https://github.com/davidclaeysquinones) | In testing |
| telenet.tv | [davidclaeysquinones](https://github.com/davidclaeysquinones) | [PR](https://github.com/iptv-org/epg/pull/2429) merged since commit [fd382db](https://github.com/iptv-org/epg/commit/fd382db08da7a96150928b8dcfef115e29e661d3) | | telenet.tv | | |
| web.magentatv.de | [klausellus-wallace](https://github.com/klausellus-wallace) | [PR](https://github.com/iptv-org/epg/pull/2458) merged since commit [61afe09](https://github.com/iptv-org/epg/commit/61afe090b6e7892cc5426457d960e9452222f885) | | web.magentatv.de | | |
If for some reason you want to include your own provider fixes this is possible by creation a mapping in the `/fixes` folder.<br> If for some reason you want to include your own provider fixes this is possible by creation a mapping in the `/fixes` folder.<br>
The expected structure is */fixes/`provider_name`/`provider_name`.config.js*.<br> The expected structure is */fixes/`provider_name`/`provider_name`.config.js*.<br>
@@ -56,6 +62,7 @@ It is recommended that you take existing provider code as a base for your custom
|-------------------------------|----------------------------------------------------------------------------|----------------------------------| |-------------------------------|----------------------------------------------------------------------------|----------------------------------|
| CRON_SCHEDULE | CRON expression describing the recurrence for epg retrieval. | `0 0,12 * * *` | | CRON_SCHEDULE | CRON expression describing the recurrence for epg retrieval. | `0 0,12 * * *` |
| DAYS | Describes the desired amount of days in the future for for epg retrieval. | 14 | | DAYS | Describes the desired amount of days in the future for for epg retrieval. | 14 |
| DELAY | Delay between requests in milliseconds | 0 |
| MAX_CONNECTIONS | The maximum amount of parallel connections that can be established | 10 | | MAX_CONNECTIONS | The maximum amount of parallel connections that can be established | 10 |
| ENABLE_FIXES | Some fixes to providers take a long time to be merged into the main branch.<br>When this option is enabled some of these fixes will also be included.<br>The source code for these fixes can be seen under the `fixes` folder.<br> Recreate the container when changing this variable in order for it to take effect | false | | ENABLE_FIXES | Some fixes to providers take a long time to be merged into the main branch.<br>When this option is enabled some of these fixes will also be included.<br>The source code for these fixes can be seen under the `fixes` folder.<br> Recreate the container when changing this variable in order for it to take effect | false |
| API_URL | The endpoint where channel information will be grabbed | `https://iptv-org.github.io/api` | | API_URL | The endpoint where channel information will be grabbed | `https://iptv-org.github.io/api` |
@@ -211,4 +218,80 @@ Sometimes a new version of this image will be bound to the same source commit. T
- 1.0.59 - 1.0.59
[04-04-2025](https://github.com/iptv-org/epg/commit/4df25c92bcad1e4892640f532eae71cf9f5e7b95)<br>Includes fixes for new configuration changes [04-04-2025](https://github.com/iptv-org/epg/commit/4df25c92bcad1e4892640f532eae71cf9f5e7b95)<br>Includes fixes for new configuration changes
- 1.0.60 - 1.0.60
[04-07-2025](https://github.com/iptv-org/epg/commit/7e1fbcbe154f4efd5c81341351cceb06f71b79a0) [04-07-2025](https://github.com/iptv-org/epg/commit/7e1fbcbe154f4efd5c81341351cceb06f71b79a0)
- 1.0.61
[04-07-2025](https://github.com/iptv-org/epg/commit/7e1fbcbe154f4efd5c81341351cceb06f71b79a0)<br>Add delay option
- 1.0.62
[04-22-2025](https://github.com/iptv-org/epg/commit/db56a4d6c0ec7f1169ae60361b623dc032365e47)
- 1.0.63
[05-10-2025](https://github.com/iptv-org/epg/commit/db56a4d6c0ec7f1169ae60361b623dc032365e47)
- 1.0.64
[06-02-2025](https://github.com/iptv-org/epg/commit/cb7e91d3938804618625e381a7fd139e11dfa380)
- 1.0.65
[06-16-2025](https://github.com/iptv-org/epg/commit/e0fdf221e2d2707fe7a9d06a4c2797672888c0eb)
- 1.0.66
[06-26-2025](https://github.com/iptv-org/epg/commit/93f857f3c36cbe00e76fceb4ad875d8e6f6ec6aa)
- 1.0.67
[07-07-2025](https://github.com/iptv-org/epg/commit/3107571168eea356e6fa6311519e3777db99b5a6)
- 1.0.68
[07-14-2025](https://github.com/iptv-org/epg/commit/10685b064d9cc65c1a22234a19527da53d544cbf)
- 1.0.69
[09-21-2025](https://github.com/iptv-org/epg/commit/69774a7e6a2c752d769ad5dfdc41b3fe4baeace1)
- 1.0.70
[10-07-2025](https://github.com/iptv-org/epg/commit/eebfcc8278dc31e9ef17ebbd298d4af061cdc23f)
- 1.0.71
[11-15-2025](https://github.com/iptv-org/epg/commit/4ed1e39d95da18d54628525f9173b7b7f7ae1d21)
- 1.0.72
[11-23-2025](https://github.com/iptv-org/epg/commit/9b331e6cfab19ad2f683b212aa079aac07fe72fe)
- 1.0.73
[12-24-2025](https://github.com/iptv-org/epg/commit/ba1fb58194464a6c50a597ac28a35dc8a4895038)
- 1.0.74
[01-06-2026](https://github.com/iptv-org/epg/commit/cbe17252ca7ef70780600952cf97790f352c8c15)
- 1.0.75
[02-07-2026](https://github.com/iptv-org/epg/commit/c1b3ef5a0e22979ffe06e4e688366eadc05b8537)
- 1.0.76
[02-10-2026](https://github.com/iptv-org/epg/commit/de08d7df85184bb6418cabf56501a9faed2889be)
- 1.0.77
[02-18-2026](https://github.com/iptv-org/epg/commit/a1168b74f5d51a26fdf51129c9af7a296f04b9f6)
- 1.0.78
[02-23-2026](https://github.com/iptv-org/epg/commit/e4f92bb2a2768dcba3dbbd52b19d78d96bebc31e)
<br>Updates custom fixes to latest code
<br>Includes fix for pickx.be provider
- 1.0.79
[03-16-2026](https://github.com/iptv-org/epg/commit/d10c95f326b8c36798c47e63bfd28ec0f116f974)
- 1.0.80
[03-23-2026](https://github.com/iptv-org/epg/commit/9d6339bf0f22444fa8967f0a5b2af64d13a7cdc3)
- 1.0.81
[04-01-2026](https://github.com/iptv-org/epg/commit/03250d4a07eae90200d9e0af6962776e91d7a0c1)
- 1.0.82
[04-13-2026](https://github.com/iptv-org/epg/commit/d27160fd2b8f0509e76de0463aa5f6466c832095)
- 1.0.83
[04-17-2026](https://github.com/iptv-org/epg/commit/ce691529a58dbf9d4776563aea6dec8cd0914c06)
- 1.0.84
[05-19-2026](https://github.com/iptv-org/epg/commit/157e77c50b08aa587e126095cedc330716d78fa0)
- 1.0.85
[06-09-2026](https://github.com/iptv-org/epg/commit/ed40bb597a0c538368988822e1c0413d370a4d5c)
- 1.0.86
[07-09-2026](https://github.com/iptv-org/epg/commit/0d5e1ff08b7a133f157d18fd945792498d5ea6cb)
- 1.0.87
[07-19-2026](https://github.com/iptv-org/epg/commit/1525e895fad70151e1895691bbf2e3426bf65920)
- 1.0.88
[08-05-2026](https://github.com/iptv-org/epg/commit/a7ce846b4e761ed37cfb968dc774d693a986fc88)
- 1.0.89
[09-02-2026](https://github.com/iptv-org/epg/commit/335458005387e07417a98399eb423c78d62a3bd9)
- 1.0.90
[09-21-2026](https://github.com/iptv-org/epg/commit/c5a88bcc6b09d75b32a0b22e6312db785cd9eb80)
- 1.0.91
[10-04-2026](https://github.com/iptv-org/epg/commit/aab705c6b90decfad2c9b27d1d6dbc8eeab9bbfd)
- 1.0.92
[10-23-2026](https://github.com/iptv-org/epg/commit/4abfae092f79fd3808ccadab65dc1873bcf6d259)
- 1.0.93
[10-25-2026](https://github.com/iptv-org/epg/commit/916c225d0a5e44534f09853674769c57005e61e5)
- 1.0.94
[10-26-2026](https://github.com/iptv-org/epg/commit/2c3e24fdab86140181878af00e01e9b440962d9b)
<br> Add fix for Pickx.be provder
- 1.0.95
[10-26-2026](https://github.com/iptv-org/epg/commit/2c3e24fdab86140181878af00e01e9b440962d9b)
<br> Solves issue in Telenet.TV custom fix
- 1.0.96
[10-29-2026](https://github.com/iptv-org/epg/commit/ae9cc64f9497c602ec2f6e17857ae38b3aa1ff05)
+88 -28
View File
@@ -1,59 +1,119 @@
//https://github.com/iptv-org/epg/blob/master/sites/movistarplus.es/movistarplus.es.config.js
const axios = require('axios') const axios = require('axios')
const cheerio = require('cheerio')
const dayjs = require('dayjs') const dayjs = require('dayjs')
const timezone = require('dayjs/plugin/timezone')
const utc = require('dayjs/plugin/utc')
dayjs.extend(utc)
dayjs.extend(timezone)
dayjs.tz.setDefault('Europe/Madrid')
module.exports = { module.exports = {
site: 'movistarplus.es', site: 'movistarplus.es',
days: 2, days: 2,
url({ channel, date }) { url({ channel, date }) {
return `https://www.movistarplus.es/programacion-tv/${channel.site_id}/${date.format( return `https://ottcache.dof6.com/movistarplus/webplayer/OTT/epg?from=${date.format('YYYY-MM-DDTHH:mm:ss')}&span=1&channel=${channel.site_id}&version=8&mdrm=true&tlsstream=true&demarcation=18`
'YYYY-MM-DD'
)}`
}, },
parser({ content }) { request: {
headers: {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'es-ES,es;q=0.9,en;q=0.8',
Referer: 'https://www.movistarplus.es/programacion-tv'
},
maxRedirects: 5
},
async parser({ content }) {
let programs = [] let programs = []
let items = parseItems(content) let items = await parseItems(content)
if (!items.length) return programs if (!items.length) return programs
items.forEach(el => { items.forEach(el => {
programs.push({ programs.push({
title: el.item.name, title: el.title,
start: dayjs(el.item.startDate), description: el.description,
stop: dayjs(el.item.endDate) icon: el.icon,
images: el.images,
season: el.season,
episode: el.episode,
start: el.start,
stop: el.stop
}) })
}) })
return programs return programs
}, },
async channels() { async channels() {
const html = await axios const json = await axios
.get('https://www.movistarplus.es/programacion-tv') .get(
'https://ottcache.dof6.com/movistarplus/webplayer/OTT/contents/channels?mdrm=true&tlsstream=true&demarcation=18&version=8'
)
.then(r => r.data) .then(r => r.data)
.catch(console.log) .catch(console.log)
const $ = cheerio.load(html) // Load JSON, CodCadenaTv is the closest to the old MVSTR site ch. ID
let scheme = $('script:contains(ItemList)').html() return json.map(channel => {
scheme = JSON.parse(scheme)
return scheme.itemListElement.map(el => {
const urlParts = el.item.url.split('/')
const site_id = urlParts.pop().toLowerCase()
return { return {
lang: 'es', lang: 'es',
name: el.item.name, site_id: channel.CodCadenaTv,
site_id name: channel.Nombre,
logo: channel.Logo || channel.Logos?.[0]?.uri || null
} }
}) })
} }
} }
function parseItems(content) { function parseImages(images) {
try { return images.filter(image => image.id === 'watch2tgr-end').map(image => image.uri)
const $ = cheerio.load(content) }
let scheme = $('script:contains("@type": "ItemList")').html()
scheme = JSON.parse(scheme)
if (!scheme || !Array.isArray(scheme.itemListElement)) return []
return scheme.itemListElement async function parseItems(content) {
try {
const data = JSON.parse(content)
const programs = Array.isArray(data) ? data : [data]
return await Promise.all(
programs.map(async json => {
const start = dayjs.utc(Number(json?.FechaHoraInicio))
const stop = dayjs.utc(Number(json?.FechaHoraFin))
const ficha = json?.Ficha || null
if (!ficha) {
return {
title: json?.Titulo || '',
description: json?.Resena || '',
icon: json?.Imagen || '',
images: json.Imagenes ? parseImages(json.Imagenes) : [],
start,
stop
}
} else {
try {
const fichaJson = await axios.get(ficha).then(r => r.data)
return {
title: json?.Titulo || fichaJson?.Titulo || '',
description: fichaJson?.Descripcion || json?.Resena || '',
icon: fichaJson?.Imagen || '',
images: fichaJson.Imagenes ? parseImages(fichaJson.Imagenes) : [],
actors: fichaJson?.Actores || [],
directors: fichaJson?.Directores || [],
classification: fichaJson?.Clasificacion || '',
season: fichaJson?.Temporada || null,
episode: fichaJson?.NumeroEpisodio || null,
start,
stop
}
} catch {
return {
title: json?.Titulo || '',
description: json?.Resena || '',
icon: json?.Imagen || '',
images: json.Imagenes ? parseImages(json.Imagenes) : [],
start,
stop
}
}
}
})
)
} catch { } catch {
return [] return []
} }
@@ -1,172 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<channels>
<channel site="orangetv.orange.es" lang="es" xmltv_id="La1.es" site_id="1010">LA 1 HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="La2.es" site_id="1062">La 2 HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="24Horas.es" site_id="12051">24 Horas</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Teledeporte.es" site_id="1063">TDP HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Clan.es" site_id="1064">Clan HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Antena3.es" site_id="1011">Antena 3 HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Cuatro.es" site_id="1012">Cuatro HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Telecinco.es" site_id="1013">Telecinco HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LaSexta.es" site_id="1014">laSexta HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Selekt.es" site_id="11045">SELEKT</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="StarChannelSpain.es" site_id="11001">STAR Channel</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="AMC.es" site_id="11011">AMC</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="AMCBreak.es" site_id="11009">AMC Break</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="AMCCrime.es" site_id="11042">AMC CRIME</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="WarnerTV.es" site_id="11007">Warner TV</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="AXN.es" site_id="11002">AXN</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="ComedyCentral.es" site_id="11005">Comedy Central</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Calle13Universal.es" site_id="11003">Calle 13</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="XTRM.es" site_id="12057">XTRM</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Syfy.es" site_id="11004">SYFY</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="COSMO.es" site_id="11006">Cosmo</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Enfamilia.es" site_id="11063">Enfamilia</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Decasa.es" site_id="11027">Decasa</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="CanalCocina.es" site_id="11020">CanalCocina</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="RuntimeSeries.es" site_id="11093">Runtime Series</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="FactoriadeFiccion.es" site_id="1032">FDF</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Neox.es" site_id="1027">Neox</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Energy.es" site_id="1030">Energy</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Atreseries.es" site_id="1042">Atreseries</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Divinity.es" site_id="1034">Divinity</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Nova.es" site_id="1028">Nova</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="CanalHollywood.es" site_id="11010">Canal Hollywood</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="AXNMovies.es" site_id="11008">AXN Movies</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Somos.es" site_id="11029">Somos</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="TCM.es" site_id="11012">TCM</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="SundanceTV.es" site_id="11052">Sundance TV</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Dark.es" site_id="11053">Dark</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="RuntimeCineSeries.es" site_id="1089">Runtime Cine y Series</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="RuntimeThriller.es" site_id="11091">Runtime Thriller/Terror</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="RuntimeAccion.es" site_id="11094">Runtime Acción</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="RuntimeComedia.es" site_id="11092">Runtime Comedia</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="RuntimeCrimen.es" site_id="11095">Runtime Crimen</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="RuntimeRomance.es" site_id="11096">Runtime Romance</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="RunTimeClasicos.es" site_id="11097">RunTime Clásicos</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="CinesVerdi.es" site_id="11098">Cines Verdi TV</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="CineFeelGood.es" site_id="11099">Cine Feel Good</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="ParamountNetwork.es" site_id="12032">Paramount Network</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="DreamWorksChannel.es" site_id="1085">Dreamworks</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="BomCine.es" site_id="2063">Bom Cine</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="DKISS.es" site_id="1044">DKISS</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="TEN.es" site_id="12054">Ten</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Odisea.es" site_id="11018">Odisea</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Mega.es" site_id="1016">Mega</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Trece.es" site_id="1017">TR3CE</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="DMAX.es" site_id="1018">DMAX</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LaOtra.es" site_id="2061">La otra HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Boing.es" site_id="1021">Boing</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="ToonGogglesenEspanol.us" site_id="11100">Toon Goggles</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="NickJr.es" site_id="11055">Nick JR</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="DisneyJunior.es" site_id="11015">Disney Junior</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Nickelodeon.es" site_id="11016">Nick</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="BeMad.es" site_id="1043">Be Mad TV</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Historia.es" site_id="11019">Canal Historia</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="NationalGeographic.es" site_id="11017">National Geographic</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="NationalGeographicWild.es" site_id="1008">NatGeo Wild</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="DiscoveryChannel.es" site_id="1039">Discovery</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="NatureTime.es" site_id="1084">NatureTime</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="La7.es" site_id="2011">7RM HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LevanteTV.es" site_id="2070">Levante TV</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="CanalSur.es" site_id="1035">Canal Sur</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="CanalSur2.es" site_id="2022">CanalSur 2</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="CanalSurAndalucia.es" site_id="2031">Andalucía TV</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="7TVAndalucia.e" site_id="2065">7 TV Andalucia</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="TV3.es" site_id="2008">TV3 HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="TV3CAT.es" site_id="1036">TV3CAT</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="324.es" site_id="2050">3/24</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="TelevisioCastello.es" site_id="2067">TV Castellon</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="La8Mediterraneo.es" site_id="2062">8 Mediterraneo</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="APunt.es" site_id="2057">A punt HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="IB3.es" site_id="2013">IB3 HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="TelevisionCanaria.es" site_id="2059">RTV Canaria HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="ETB1.es" site_id="2014">ETB1 HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="ETB2.es" site_id="1037">ETB2 HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="ETB3.es" site_id="2032">ETB3</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="ETB4.es" site_id="2037">ETB4</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="ETBBasque.es" site_id="1037">ETB Basque</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="GaliciaTVEuropa.es" site_id="2052">TVG HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="TVG2.es" site_id="2053">TVG2 HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="TPA7.es" site_id="2009">TPA HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="TeleVigo.es" site_id="2068">Televigo</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="GaliciaTVEuropa.es" site_id="1038">Galicia TV</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="CeutaTV.es" site_id="2002">TV Ceuta</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="TVR.es" site_id="2055">TVR HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="NavarraTV.es" site_id="2060">Navarra TV HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="NavarraTV2.es" site_id="2029">Navarra TV2</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Telemadrid.es" site_id="12050">Telemadrid HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="RealMadridTV.es" site_id="12052">Real Madrid</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="SevillaFCTV.es" site_id="2081">SEVILLA FC Televisión</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="BetisTV.es" site_id="1019">Betis TV</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LigadeCampeonesporMovistarPlusPlus.es" site_id="1023">M+ Liga de Campeones</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LigadeCampeones1porMovistarPlusPlus.es" site_id="1050">M+ Liga de Campeones 2</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LigadeCampeones2porMovistarPlusPlus.es" site_id="1051">M+ Liga de Campeones 3</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LigadeCampeones3porMovistarPlusPlus.es" site_id="1052">M+ Liga de Campeones 4</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LigadeCampeones4porMovistarPlusPlus.es" site_id="1053">M+ Liga de Campeones 5</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LigadeCampeones5porMovistarPlusPlus.es" site_id="1054">M+ Liga de Campeones 6</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LigadeCampeones6porMovistarPlusPlus.es" site_id="1055">M+ Liga de Campeones 7</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LigadeCampeones7porMovistarPlusPlus.es" site_id="1056">M+ Liga de Campeones 8</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LigadeCampeones8porMovistarPlusPlus.es" site_id="11036">M+ Liga de Campeones 9</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LigadeCampeones9porMovistarPlusPlus.es" site_id="1096">M+ Liga de Campeones 10</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LigadeCampeones10porMovistarPlusPlus.es" site_id="1097">M+ Liga de Campeones 11</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LigadeCampeones11porMovistarPlusPlus.es" site_id="1098">M+ Liga de Campeones 12</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LigadeCampeones12porMovistarPlusPlus.es" site_id="1099">M+ Liga de Campeones 13</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LigadeCampeones13porMovistarPlusPlus.es" site_id="11065">M+ Liga de Campeones 14</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LigadeCampeones14porMovistarPlusPlus.es" site_id="11066">M+ Liga de Campeones 15</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LigadeCampeones15porMovistarPlusPlus.es" site_id="11067">M+ Liga de Campeones 16</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LigadeCampeones16porMovistarPlusPlus.es" site_id="11068">M+ Liga de Campeones 17</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LaLigaTVUHDporMovistarPlusPlus.es" site_id="11043">M LALIGA TV UHD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LaLigaTVporMovistarPlusPlus.es" site_id="11013">M+ LALIGA TV</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LaLiga1porMovistarPlusPlus.es" site_id="11026">M+ LALIGA TV 2</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LaLigaTV2porMovistarPlusPlus.es" site_id="1057">M+ LALIGATV 3</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LaLigaTV3porMovistarPlusPlus.es" site_id="1058">M+ LALIGATV 4</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LaLigaTV.es" site_id="11129">LA LIGA TV</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="DAZNLaLiga.es" site_id="11077">DAZN LALIGA</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="DAZNLaLiga2.es" site_id="11080">DAZN LALIGA 2</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LaLigaHypermotion.es" site_id="11083">LALIGATV HYPERMOTION</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LaLigaHypermotion2.es" site_id="11084">LALIGATV HYPERMOTION 2</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="LaLigaHypermotion3.es" site_id="11085">LALIGATV HYPERMOTION 3</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="GOLPLAY.es" site_id="11031">GOL PLAY</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Mezzo.fr" site_id="1031">Mezzo HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="MTV00s.uk" site_id="1026">MTV 00s</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="HITTV.es" site_id="2046">HIT TV</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="MTV.es" site_id="11050">MTV España</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="MTVLive.us" site_id="11051">MTV Live</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="SolMusica.es" site_id="11119">Sol Música</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="StingrayClassica.ca" site_id="12003">Classica HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="StingrayFestival4K.ca" site_id="11060">Festival 4K</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="StingrayDJAZZ.ca" site_id="12004">Djazz</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="StingrayiConcerts.ca" site_id="12002">iConcerts</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="MezzoLive.fr" site_id="12005">MezzoLiveHD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="TraceLatina.fr" site_id="11106">TRACE Latina</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="TraceUrban.fr" site_id="11107">TRACE Urban</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="CaracolTV.co" site_id="1029">Caracol TV</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="EWTNEspanaLatinAmerica.us" site_id="1068">EWTN</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Gulli.fr" site_id="1077">Gulli</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="France2.fr" site_id="1082">France 2</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="France5.fr" site_id="1083">France 5</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="TraceSportStars.fr" site_id="1095">Trace Sport Stars</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="TV5MondeEurope.fr" site_id="11023">TV5MONDE</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="QwestTV.fr" site_id="1088">Qwest TV</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Eurosport1.fr" site_id="11035">Eurosport 1</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Eurosport2.fr" site_id="11059">Eurosport 2</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="MyZenTV.fr" site_id="11070">Myzen TV</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="FightSports.us" site_id="12007">FightSports</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="FightBoxHD.nl" site_id="1091">Fight Box HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Gametoon.es" site_id="1086">Gametoon</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="EuronewsSpanish.fr" site_id="1090">Euronews</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="ElToroTV.es" site_id="11049">El Toro TV</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Motorvision.de" site_id="11105">Motorvision</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Esport3.es" site_id="2038">Esport3</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="BBCNewsEurope.uk" site_id="11021">BBC World HD</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="CNNInternationalEurope.us" site_id="11022">CNN</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="DWEspanol.de" site_id="11024">Deutsche Welle</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="AlJazeeraEnglish.qa" site_id="11025">Al Jazeera</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="PROTV.ba" site_id="11028">Pro TV</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="Ubeat.es" site_id="11061">Ubeat</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="OUTtv.ca" site_id="11072">OUTtv</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="BUENVIAJE.es" site_id="11112">¡BUENVIAJE!</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="TennisChannel.us" site_id="11120">Tennis Channel</channel>
<channel site="orangetv.orange.es" lang="es" xmltv_id="NauticalChannel.it" site_id="11128">Nautical Channel</channel>
</channels>
@@ -1,3 +1,5 @@
//https://github.com/iptv-org/epg/blob/master/sites/orangetv.orange.es/orangetv.orange.es.config.js
const dayjs = require('dayjs') const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc') const utc = require('dayjs/plugin/utc')
const doFetch = require('@ntlab/sfetch') const doFetch = require('@ntlab/sfetch')
@@ -7,10 +9,12 @@ dayjs.extend(utc)
doFetch.setDebugger(debug) doFetch.setDebugger(debug)
const API_PROGRAM_ENDPOINT = 'https://epg.orangetv.orange.es/epg/Smartphone_Android/1_PRO' const API_PROGRAM_ENDPOINT = 'https://epg.orangetv.orange.es/epg/SmartTV_Android/1_PRO'
const API_CHANNEL_ENDPOINT =
'https://pc.orangetv.orange.es/pc/api/rtv/v1/GetChannelList?bouquet_id=1&model_external_id=PC&filter_unsupported_channels=false&client=json'
const API_IMAGE_ENDPOINT = 'https://pc.orangetv.orange.es/pc/api/rtv/v1/images' const API_IMAGE_ENDPOINT = 'https://pc.orangetv.orange.es/pc/api/rtv/v1/images'
const API_CHANNEL_ENDPOINT =
'https://pc.orangetv.orange.es/pc/api/rtv/v1/GetChannelList?bouquet_external_id=1_PRO&model_external_id=PC&filter_unsupported_channels=true&max_pr_level=8&client=json'
const caches = {}
module.exports = { module.exports = {
site: 'orangetv.orange.es', site: 'orangetv.orange.es',
@@ -18,22 +22,36 @@ module.exports = {
request: { request: {
cache: { cache: {
ttl: 24 * 60 * 60 * 1000 // 1 day ttl: 24 * 60 * 60 * 1000 // 1 day
},
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36'
} }
}, },
url({ date, segment = 1 }) { url({ date }) {
return `${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_${segment}.json` return segmentUrl(date)
}, },
async parser({ content, channel, date }) { async parser({ content, channel, date }) {
const programs = [] const programs = []
const items = parseItems(content, channel) const items = parseItems(content, channel)
if (items.length) { const queues = []
const queues = [ // fetch other segments or use cache if exist
module.exports.url({ date, segment: 2 }), for (let i = 2; i <= 3; i++) {
module.exports.url({ date, segment: 3 }) const url = segmentUrl(date, i)
] if (caches[url] !== undefined) {
await doFetch(queues, (url, res) => { items.push(...parseItems(caches[url], channel))
} else {
queues.push({ url, params: module.exports.request })
}
}
if (queues.length) {
await doFetch(queues, (queue, res) => {
if (caches[queue.url] === undefined) {
caches[queue.url] = res
}
items.push(...parseItems(res, channel)) items.push(...parseItems(res, channel))
}) })
}
if (items.length) {
programs.push( programs.push(
...items.map(item => { ...items.map(item => {
return { return {
@@ -70,6 +88,10 @@ module.exports = {
} }
} }
function segmentUrl(date, segment = 1) {
return `${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_${segment}.json`
}
function parseIcon(item) { function parseIcon(item) {
if (item.attachments.length) { if (item.attachments.length) {
const cover = item.attachments.find(i => i.name.match(/cover/i)) const cover = item.attachments.find(i => i.name.match(/cover/i))
@@ -89,7 +111,7 @@ function parseItems(content, channel) {
typeof content === 'string' ? JSON.parse(content) : Array.isArray(content) ? content : [] typeof content === 'string' ? JSON.parse(content) : Array.isArray(content) ? content : []
if (Array.isArray(json)) { if (Array.isArray(json)) {
json json
.filter(i => i.channelExternalId === channel.site_id) .filter(i => String(i.channelExternalId) === String(channel.site_id))
.forEach(i => { .forEach(i => {
result.push(...i.programs) result.push(...i.programs)
}) })
+412
View File
@@ -0,0 +1,412 @@
<?xml version="1.0" encoding="UTF-8"?>
<channels>
<channel site="pickx.be" site_id="1143" lang="fr" xmltv_id="MaghrebTV.be@SD">Maghreb TV</channel>
<channel site="pickx.be" site_id="1043" lang="en" xmltv_id="">Netflix</channel>
<channel site="pickx.be" site_id="1058" lang="fr" xmltv_id="NationalGeographicWild.fr@SD">National Geographic Wild F</channel>
<channel site="pickx.be" site_id="1190" lang="nl" xmltv_id="NationalGeographicWild.nl@SD">National Geographic Wild N</channel>
<channel site="pickx.be" site_id="1130" lang="de" xmltv_id="DasErste.de@SD">Das Erste</channel>
<channel site="pickx.be" site_id="1093" lang="fr" xmltv_id="TipikVision.be@SD">TIPIK vision</channel>
<channel site="pickx.be" site_id="1131" lang="de" xmltv_id="ZDF.de@SD">ZDF</channel>
<channel site="pickx.be" site_id="1195" lang="nl" xmltv_id="DobbitTV.be@NL">Dobbit TV N</channel>
<channel site="pickx.be" site_id="1149" lang="nl" xmltv_id="VTM2.be@HD">VTM 2 HD</channel>
<channel site="pickx.be" site_id="1011" lang="fr" xmltv_id="PickxPlus.be@FR">Pickx+ F</channel>
<channel site="pickx.be" site_id="1155" lang="nl" xmltv_id="PickxPlus.be@NL">Pickx+ N</channel>
<channel site="pickx.be" site_id="1065" lang="fr" xmltv_id="DobbitTV.be@FR">Dobbit TV F</channel>
<channel site="pickx.be" site_id="1001" lang="fr" xmltv_id="Tipik.be@HD">TIPIK HD</channel>
<channel site="pickx.be" site_id="1201" lang="nl" xmltv_id="PlattelandsTV.be@SD">Plattelands</channel>
<channel site="pickx.be" site_id="1174" lang="nl" xmltv_id="DisneyJunior.nl@SD">Disney Jr. N</channel>
<channel site="pickx.be" site_id="1036" lang="fr" xmltv_id="PickxPlus.be@FRHD">Pickx+ F HD</channel>
<channel site="pickx.be" site_id="1182" lang="nl" xmltv_id="PickxPlus.be@NLHD">Pickx+ N HD</channel>
<channel site="pickx.be" site_id="1016" lang="fr" xmltv_id="AB3.be@HD">AB3 HD</channel>
<channel site="pickx.be" site_id="1054" lang="fr" xmltv_id="DiscoveryChannel.fr@SD">Discovery F</channel>
<channel site="pickx.be" site_id="1054" lang="fr" xmltv_id="DiscoveryChannel.fr@HD">Disc HD F</channel>
<channel site="pickx.be" site_id="1098" lang="fr" xmltv_id="MezzoLive.fr@SD">MezzoLive</channel>
<channel site="pickx.be" site_id="1002" lang="fr" xmltv_id="RTLTVI.be@HD">RTL tvi HD</channel>
<channel site="pickx.be" site_id="1004" lang="fr" xmltv_id="RTLPlug.be@HD">RTL plug HD</channel>
<channel site="pickx.be" site_id="1110" lang="fr" xmltv_id="Telesambre.be@SD">Télé Sambre</channel>
<channel site="pickx.be" site_id="1113" lang="fr" xmltv_id="Vedia.be@SD">Védia</channel>
<channel site="pickx.be" site_id="1111" lang="fr" xmltv_id="">TV Com</channel>
<channel site="pickx.be" site_id="1112" lang="fr" xmltv_id="TVLux.be@SD">TV Lux</channel>
<channel site="pickx.be" site_id="1106" lang="fr" xmltv_id="Matele.be@SD">MaTélé</channel>
<channel site="pickx.be" site_id="1044" lang="fr" xmltv_id="">Disney+</channel>
<channel site="pickx.be" site_id="1167" lang="nl" xmltv_id="BBCFirst.uk@Benelux">BBC First</channel>
<channel site="pickx.be" site_id="1171" lang="nl" xmltv_id="Nickelodeon.nl@HD">nick N HD</channel>
<channel site="pickx.be" site_id="1157" lang="nl" xmltv_id="Play6.be@SD">Play6</channel>
<channel site="pickx.be" site_id="1157" lang="nl" xmltv_id="Play6.be@HD">Play6 HD</channel>
<channel site="pickx.be" site_id="1129" lang="fr" xmltv_id="France24.fr@FrenchHD">France 24 HD</channel>
<channel site="pickx.be" site_id="1128" lang="fr" xmltv_id="EuronewsFrench.fr@SD">Euronews F</channel>
<channel site="pickx.be" site_id="1150" lang="nl" xmltv_id="VTM3.be@HD">VTM 3 HD</channel>
<channel site="pickx.be" site_id="1063" lang="fr" xmltv_id="CrimeDistrict.fr@SD">Crime District</channel>
<channel site="pickx.be" site_id="1092" lang="fr" xmltv_id="BelRTL.be@HD">bel RTL HD</channel>
<channel site="pickx.be" site_id="1196" lang="nl" xmltv_id="EclipsTV.be@SD">Eclips</channel>
<channel site="pickx.be" site_id="1017" lang="fr" xmltv_id="ABXplore.be@HD">ABXPLO HD</channel>
<channel site="pickx.be" site_id="1194" lang="nl" xmltv_id="Njam.be@HD">Njam! HD</channel>
<channel site="pickx.be" site_id="1194" lang="nl" xmltv_id="Njam.be@SD">Njam!</channel>
<channel site="pickx.be" site_id="1020" lang="fr" xmltv_id="arte.fr@HD">arte HD</channel>
<channel site="pickx.be" site_id="1187" lang="nl" xmltv_id="AnimalPlanet.nl@HD">Animal Pl HD</channel>
<channel site="pickx.be" site_id="1154" lang="nl" xmltv_id="Ketnet.be@HD">Ketnet HD</channel>
<channel site="pickx.be" site_id="1161" lang="nl" xmltv_id="NPO1.nl@HD">NPO 1 HD</channel>
<channel site="pickx.be" site_id="1162" lang="nl" xmltv_id="NPO2.nl@HD">NPO 2 HD</channel>
<channel site="pickx.be" site_id="1163" lang="nl" xmltv_id="NPO3.nl@HD">NPO 3 HD</channel>
<channel site="pickx.be" site_id="1037" lang="fr" xmltv_id="Be1.be@SD">Be1</channel>
<channel site="pickx.be" site_id="1039" lang="fr" xmltv_id="BeCine.be@SD">Be Ciné</channel>
<channel site="pickx.be" site_id="1038" lang="fr" xmltv_id="BeSeries.be@SD">Be Séries</channel>
<channel site="pickx.be" site_id="1040" lang="fr" xmltv_id="CinePlusOCS.fr@SD">Ciné+OCS</channel>
<channel site="pickx.be" site_id="1041" lang="fr" xmltv_id="CinePlusFrisson.fr@SD">Ciné+Frisson</channel>
<channel site="pickx.be" site_id="1042" lang="fr" xmltv_id="CinePlusClassic.be@SD">Ciné+Classic</channel>
<channel site="pickx.be" site_id="1200" lang="nl" xmltv_id="OUTtv.nl@SD">OUT tv</channel>
<channel site="pickx.be" site_id="1007" lang="fr" xmltv_id="France3.fr@HD">France 3 HD</channel>
<channel site="pickx.be" site_id="1018" lang="fr" xmltv_id="France4.fr@HD">France 4 HD</channel>
<channel site="pickx.be" site_id="1019" lang="fr" xmltv_id="France5.fr@HD">France 5 HD</channel>
<channel site="pickx.be" site_id="1015" lang="fr" xmltv_id="LN24.be@SD">LN24</channel>
<channel site="pickx.be" site_id="1160" lang="nl" xmltv_id="VTMGold.be@SD">VTM Gold</channel>
<channel site="pickx.be" site_id="1227" lang="en" xmltv_id="PenthouseGold.us@SD">Penthouse Gold</channel>
<channel site="pickx.be" site_id="1204" lang="nl" xmltv_id="PickxPlusSports1.be@NL">Pickx+ Sports 1 N</channel>
<channel site="pickx.be" site_id="1205" lang="nl" xmltv_id="PickxPlusSports2.be@NL">Pickx+ Sports 2 N</channel>
<channel site="pickx.be" site_id="1206" lang="nl" xmltv_id="PickxPlusSports3.be@NL">Pickx+ Sports 3 N</channel>
<channel site="pickx.be" site_id="1207" lang="nl" xmltv_id="PickxPlusSports4.be@NL">Pickx+ Sports 4 N</channel>
<channel site="pickx.be" site_id="1208" lang="nl" xmltv_id="PickxPlusSports5.be@NL">Pickx+ Sports 5 N</channel>
<channel site="pickx.be" site_id="1209" lang="nl" xmltv_id="PickxPlusSports6.be@NL">Pickx+ Sports 6 N</channel>
<channel site="pickx.be" site_id="1211" lang="nl" xmltv_id="PickxPlusSports8.be@NLHD">Pickx+ Sports 8 N HD</channel>
<channel site="pickx.be" site_id="1212" lang="nl" xmltv_id="PickxPlusSports9.be@NLHD">Pickx+ Sports 9 N HD</channel>
<channel site="pickx.be" site_id="1228" lang="en" xmltv_id="DuskTV.nl@SD">Dusk</channel>
<channel site="pickx.be" site_id="1203" lang="nl" xmltv_id="PickxLive.be@NLHD">Pickx Sports Pop-up N HD</channel>
<channel site="pickx.be" site_id="1156" lang="nl" xmltv_id="PickxPlusSports1.be@NLHD">Pickx+ Sports 1 N HD</channel>
<channel site="pickx.be" site_id="1205" lang="nl" xmltv_id="PickxPlusSports2.be@NLHD">Pickx+ Sports 2 N HD</channel>
<channel site="pickx.be" site_id="1206" lang="nl" xmltv_id="PickxPlusSports3.be@NLHD">Pickx+ Sports 3 N HD</channel>
<channel site="pickx.be" site_id="1207" lang="nl" xmltv_id="PickxPlusSports4.be@NLHD">Pickx+ Sports 4 N HD</channel>
<channel site="pickx.be" site_id="1208" lang="nl" xmltv_id="PickxPlusSports5.be@NLHD">Pickx+ Sports 5 N HD</channel>
<channel site="pickx.be" site_id="1209" lang="nl" xmltv_id="PickxPlusSports6.be@NLHD">Pickx+ Sports 6 N HD</channel>
<channel site="pickx.be" site_id="1084" lang="fr" xmltv_id="DAZN1.uk@France">DAZN 1 F</channel>
<channel site="pickx.be" site_id="1217" lang="nl" xmltv_id="DAZN1.uk@Netherlands">DAZN 1 N</channel>
<channel site="pickx.be" site_id="1085" lang="fr" xmltv_id="DAZN2.uk@France">DAZN 2 F</channel>
<channel site="pickx.be" site_id="1218" lang="nl" xmltv_id="DAZN2.uk@Netherlands">DAZN 2 N</channel>
<channel site="pickx.be" site_id="1084" lang="fr" xmltv_id="DAZN1.uk@FranceHD">DAZN 1 F HD</channel>
<channel site="pickx.be" site_id="1217" lang="nl" xmltv_id="DAZN1.uk@NetherlandsHD">DAZN 1 N HD</channel>
<channel site="pickx.be" site_id="1085" lang="fr" xmltv_id="DAZN2.uk@FranceHD">DAZN 2 F HD</channel>
<channel site="pickx.be" site_id="1218" lang="nl" xmltv_id="DAZN2.uk@NetherlandsHD">DAZN 2 N HD</channel>
<channel site="pickx.be" site_id="1071" lang="fr" xmltv_id="PickxPlusSports2.be@FR">Pickx+ Sports 1 F</channel>
<channel site="pickx.be" site_id="1073" lang="fr" xmltv_id="PickxPlusSports3.be@FR">Pickx+ Sports 3 F</channel>
<channel site="pickx.be" site_id="1074" lang="fr" xmltv_id="PickxPlusSports4.be@FR">Pickx+ Sports 4 F</channel>
<channel site="pickx.be" site_id="1075" lang="fr" xmltv_id="PickxPlusSports5.be@FR">Pickx+ Sports 5 F</channel>
<channel site="pickx.be" site_id="1076" lang="fr" xmltv_id="PickxPlusSports6.be@FR">Pickx+ Sports 6 F</channel>
<channel site="pickx.be" site_id="1078" lang="fr" xmltv_id="PickxPlusSports8.be@FRHD">Pickx+ Sports 8 F HD</channel>
<channel site="pickx.be" site_id="1079" lang="fr" xmltv_id="PickxPlusSports9.be@FRHD">Pickx+ Sports 9 F HD</channel>
<channel site="pickx.be" site_id="1080" lang="fr" xmltv_id="PickxPlusSports10.be@FRHD">Pickx+ Sports 10 F HD</channel>
<channel site="pickx.be" site_id="1010" lang="fr" xmltv_id="PickxLive.be@FRHD">Pickx Sports Pop-up F HD</channel>
<channel site="pickx.be" site_id="1012" lang="fr" xmltv_id="PickxPlusSports1.be@FRHD">Pickx+ Sports 1 F HD</channel>
<channel site="pickx.be" site_id="1072" lang="fr" xmltv_id="PickxPlusSports2.be@FRHD">Pickx+ Sports 2 F HD</channel>
<channel site="pickx.be" site_id="1073" lang="fr" xmltv_id="PickxPlusSports3.be@FRHD">Pickx+ Sports 3 F HD</channel>
<channel site="pickx.be" site_id="1074" lang="fr" xmltv_id="PickxPlusSports4.be@FRHD">Pickx+ Sports 4 F HD</channel>
<channel site="pickx.be" site_id="1075" lang="fr" xmltv_id="PickxPlusSports5.be@FRHD">Pickx+ Sports 5 F HD</channel>
<channel site="pickx.be" site_id="1076" lang="fr" xmltv_id="PickxPlusSports6.be@FRHD">Pickx+ Sports 6 F HD</channel>
<channel site="pickx.be" site_id="1213" lang="nl" xmltv_id="PickxPlusSports10.be@NLHD">Pickx+ Sports 10 N HD</channel>
<channel site="pickx.be" site_id="1236" lang="nl" xmltv_id="StuBru.be@SD">StuBru</channel>
<channel site="pickx.be" site_id="1256" lang="fr" xmltv_id="PromiusPickxInfoChannel.be@SD">InfoChannel</channel>
<channel site="pickx.be" site_id="1114" lang="nl" xmltv_id="VlaamsParlementTV.be@SD">Vlaamsparlement.tv</channel>
<channel site="pickx.be" site_id="1064" lang="fr" xmltv_id="E.fr@HD">E! HD F</channel>
<channel site="pickx.be" site_id="1197" lang="nl" xmltv_id="EEurope.nl@HD">E! HD N</channel>
<channel site="pickx.be" site_id="1024" lang="fr" xmltv_id="DisneyChannel.fr@HD">Disn Ch HD F</channel>
<channel site="pickx.be" site_id="1173" lang="nl" xmltv_id="DisneyChannel.nl@HD">Disn Ch HD N</channel>
<channel site="pickx.be" site_id="1140" lang="fr" xmltv_id="2MMonde.ma@SD">2M Monde</channel>
<channel site="pickx.be" site_id="1172" lang="nl" xmltv_id="VTMLife.be@HD">VTM Life HD</channel>
<channel site="pickx.be" site_id="1159" lang="nl" xmltv_id="KanaalZ.be@HD">Kanaal Z HD</channel>
<channel site="pickx.be" site_id="1014" lang="fr" xmltv_id="CanalZ.be@HD">Canal Z HD</channel>
<channel site="pickx.be" site_id="1046" lang="fr" xmltv_id="TVBreizh.fr@HD">TV Breizh HD</channel>
<channel site="pickx.be" site_id="1049" lang="fr" xmltv_id="TCMCinema.fr@HD">TCM Cinéma HD</channel>
<channel site="pickx.be" site_id="1048" lang="fr" xmltv_id="Syfy.fr@HD">SyFy HD F</channel>
<channel site="pickx.be" site_id="1047" lang="fr" xmltv_id="13emeRue.fr@HD">13è Rue HD</channel>
<channel site="pickx.be" site_id="1045" lang="fr" xmltv_id="Action.fr@HD">Action HD</channel>
<channel site="pickx.be" site_id="1222" lang="nl" xmltv_id="Sport10.be@SD">Sport 10</channel>
<channel site="pickx.be" site_id="1034" lang="fr" xmltv_id="Gulli.fr@HD">Gulli HD</channel>
<channel site="pickx.be" site_id="1186" lang="nl" xmltv_id="NationalGeographic.nl@HD">NGC HD N</channel>
<channel site="pickx.be" site_id="1089" lang="en" xmltv_id="Motorvision.de@SD">Motorvision+</channel>
<channel site="pickx.be" site_id="1021" lang="fr" xmltv_id="TV5MondeFranceBelgiumSwitzerlandMonaco.fr@HD">TV5 Monde HD</channel>
<channel site="pickx.be" site_id="1089" lang="en" xmltv_id="Motorvision.de@HD">Motorvision+ HD</channel>
<channel site="pickx.be" site_id="1050" lang="fr" xmltv_id="ComedyCentral.fr@SD">Comedy Central F</channel>
<channel site="pickx.be" site_id="1030" lang="fr" xmltv_id="NickelodeonJunior.fr@SD">nick jr F</channel>
<channel site="pickx.be" site_id="1151" lang="nl" xmltv_id="VTM4.be@HD">VTM 4 HD</channel>
<channel site="pickx.be" site_id="1203" lang="nl" xmltv_id="PickxLive.be@NL">Pickx Showcase N HD</channel>
<channel site="pickx.be" site_id="1147" lang="nl" xmltv_id="Play4.be@HD">Play4 HD</channel>
<channel site="pickx.be" site_id="1148" lang="nl" xmltv_id="Play5.be@HD">Play5 HD</channel>
<channel site="pickx.be" site_id="1081" lang="fr" xmltv_id="DAZNProLeague1.uk@FranceHD">DAZN Pro League 1 F HD</channel>
<channel site="pickx.be" site_id="1081" lang="fr" xmltv_id="DAZNProLeague1.uk@France">DAZN Pro League 1 F</channel>
<channel site="pickx.be" site_id="1193" lang="nl" xmltv_id="TLC.nl@SD">TLC</channel>
<channel site="pickx.be" site_id="1172" lang="nl" xmltv_id="VTMLife.be@SD">VTM Life</channel>
<channel site="pickx.be" site_id="1056" lang="fr" xmltv_id="Trek.fr@HD">TREK HD</channel>
<channel site="pickx.be" site_id="1214" lang="nl" xmltv_id="DAZNProLeague1.uk@NetherlandsHD">DAZN Pro League 1 N HD</channel>
<channel site="pickx.be" site_id="1214" lang="nl" xmltv_id="DAZNProLeague1.uk@Netherlands">DAZN Pro League 1 N</channel>
<channel site="pickx.be" site_id="1082" lang="fr" xmltv_id="DAZNProLeague2.uk@FranceHD">DAZN Pro League 2 F HD</channel>
<channel site="pickx.be" site_id="1082" lang="fr" xmltv_id="DAZNProLeague2.uk@France">DAZN Pro League 2 F</channel>
<channel site="pickx.be" site_id="1215" lang="nl" xmltv_id="DAZNProLeague2.uk@NetherlandsHD">DAZN Pro League 2 N HD</channel>
<channel site="pickx.be" site_id="1215" lang="nl" xmltv_id="DAZNProLeague2.uk@Netherlands">DAZN Pro League 2 N</channel>
<channel site="pickx.be" site_id="1132" lang="de" xmltv_id="RTL.de@SD">RTL</channel>
<channel site="pickx.be" site_id="1051" lang="fr" xmltv_id="TMC.fr@HD">TMC HD</channel>
<channel site="pickx.be" site_id="1158" lang="nl" xmltv_id="Play7.be@HD">Play7 HD</channel>
<channel site="pickx.be" site_id="1068" lang="fr" xmltv_id="MyZenTV.fr@SD">MyZen.tv</channel>
<channel site="pickx.be" site_id="1057" lang="fr" xmltv_id="MuseumTVFrench.fr@SD">Museum TV</channel>
<channel site="pickx.be" site_id="1103" lang="fr" xmltv_id="ACTV.be@HD">Antenne Centre HD</channel>
<channel site="pickx.be" site_id="1115" lang="nl" xmltv_id="ATV.be@HD">ATV HD</channel>
<channel site="pickx.be" site_id="1116" lang="nl" xmltv_id="AVS.be@HD">AVS HD</channel>
<channel site="pickx.be" site_id="1013" lang="nl" xmltv_id="Bruzz.be@HD">BRUZZ HD</channel>
<channel site="pickx.be" site_id="1009" lang="fr" xmltv_id="BX1.be@HD">bx1 HD</channel>
<channel site="pickx.be" site_id="1104" lang="fr" xmltv_id="Bouke.be@HD">Bouké HD</channel>
<channel site="pickx.be" site_id="1105" lang="fr" xmltv_id="CanalZoom.be@HD">Canal Zoom HD</channel>
<channel site="pickx.be" site_id="1117" lang="nl" xmltv_id="FocusTV.be@HD">Focus TV HD</channel>
<channel site="pickx.be" site_id="1106" lang="fr" xmltv_id="Matele.be@HD">MaTélé HD</channel>
<channel site="pickx.be" site_id="1107" lang="fr" xmltv_id="Notele.be@HD">No Télé HD</channel>
<channel site="pickx.be" site_id="1118" lang="nl" xmltv_id="RingTV.be@HD">Ring TV HD</channel>
<channel site="pickx.be" site_id="1119" lang="nl" xmltv_id="ROBTV.be@HD">ROBTV HD</channel>
<channel site="pickx.be" site_id="1108" lang="fr" xmltv_id="QU4TRELiegeMedia.be@HD">Quatre HD</channel>
<channel site="pickx.be" site_id="1120" lang="nl" xmltv_id="RTVKempen.be@HD">RTV Kempen HD</channel>
<channel site="pickx.be" site_id="1121" lang="nl" xmltv_id="RTVMech.be@HD">RTV Mech HD</channel>
<channel site="pickx.be" site_id="1109" lang="fr" xmltv_id="TeleMB.be@HD">Télé MB HD</channel>
<channel site="pickx.be" site_id="1110" lang="fr" xmltv_id="Telesambre.be@HD">Télé Sambre HD</channel>
<channel site="pickx.be" site_id="1122" lang="nl" xmltv_id="TVLimburg.be@HD">TV Limburg HD</channel>
<channel site="pickx.be" site_id="1112" lang="fr" xmltv_id="TVLux.be@HD">TV Lux HD</channel>
<channel site="pickx.be" site_id="1123" lang="nl" xmltv_id="TVOost.be@HD">TV Oost HD</channel>
<channel site="pickx.be" site_id="1113" lang="fr" xmltv_id="Vedia.be@HD">Védia HD</channel>
<channel site="pickx.be" site_id="1124" lang="nl" xmltv_id="WTV.be@HD">WTV HD</channel>
<channel site="pickx.be" site_id="1224" lang="nl" xmltv_id="MentTV.be@HD">Ment TV HD</channel>
<channel site="pickx.be" site_id="1192" lang="nl" xmltv_id="InsightTV.nl@SD">INPLUS</channel>
<channel site="pickx.be" site_id="1053" lang="fr" xmltv_id="TF1SeriesFilms.fr@SD">TF1 Séries Films</channel>
<channel site="pickx.be" site_id="1223" lang="nl" xmltv_id="QMusic.be@SD">Q-Music Kijk Live</channel>
<channel site="pickx.be" site_id="1165" lang="en" xmltv_id="BBCTwo.uk@HD">BBC Two HD</channel>
<channel site="pickx.be" site_id="1221" lang="nl" xmltv_id="Eurosport2.fr@NetherlandsHD">Eurosp2 HD N</channel>
<channel site="pickx.be" site_id="1029" lang="fr" xmltv_id="Boomerang.fr@HD">Boom F HD</channel>
<channel site="pickx.be" site_id="1180" lang="nl" xmltv_id="Cartoonito.uk@HD">Cartoonito HD</channel>
<channel site="pickx.be" site_id="1026" lang="fr" xmltv_id="CartoonNetworkCEE.uk@FranceHD">Cartoon F HD</channel>
<channel site="pickx.be" site_id="1176" lang="nl" xmltv_id="CartoonNetworkWesternEurope.uk@NetherlandsHD">Cartoon N HD</channel>
<channel site="pickx.be" site_id="1188" lang="nl" xmltv_id="InvestigationDiscovery.nl@HD">Inv Disc HD</channel>
<channel site="pickx.be" site_id="1271" lang="en" xmltv_id="WSport.za@SD">W Sports</channel>
<channel site="pickx.be" site_id="1025" lang="fr" xmltv_id="DisneyJr.fr@HD">Disney Jr. F HD</channel>
<channel site="pickx.be" site_id="1059" lang="fr" xmltv_id="ScienceVieTV.fr@HD">Sc et Vie TV HD</channel>
<channel site="pickx.be" site_id="1060" lang="fr" xmltv_id="HistoireTV.fr@HD">Histoire HD</channel>
<channel site="pickx.be" site_id="1189" lang="nl" xmltv_id="History.nl@HD">History HD</channel>
<channel site="pickx.be" site_id="1136" lang="fr" xmltv_id="Rai1.it@HD">RAI Uno HD</channel>
<channel site="pickx.be" site_id="1137" lang="fr" xmltv_id="Rai2.it@HD">RAI Due HD</channel>
<channel site="pickx.be" site_id="1138" lang="fr" xmltv_id="Rai3.it@HD">RAI Tre HD</channel>
<channel site="pickx.be" site_id="1195" lang="nl" xmltv_id="DobbitTV.be@NLHD">Dobbit N HD</channel>
<channel site="pickx.be" site_id="1130" lang="de" xmltv_id="DasErste.de@HD">Das Erste HD</channel>
<channel site="pickx.be" site_id="1183" lang="nl" xmltv_id="ComedyCentral.nl@HD">Comedy Central N HD</channel>
<channel site="pickx.be" site_id="1127" lang="fr" xmltv_id="LCI.fr@HD">LCI HD</channel>
<channel site="pickx.be" site_id="1033" lang="fr" xmltv_id="Mangas.fr@HD">Mangas HD</channel>
<channel site="pickx.be" site_id="1131" lang="de" xmltv_id="ZDF.de@HD">ZDF HD</channel>
<channel site="pickx.be" site_id="1226" lang="en" xmltv_id="DorcelXXX.nl@HD">Dorcel XXX HD</channel>
<channel site="pickx.be" site_id="1225" lang="nl" xmltv_id="PassieXXX.nl@HD">Passie XXX HD</channel>
<channel site="pickx.be" site_id="1229" lang="en" xmltv_id="PinkX.fr@HD">Pink HD</channel>
<channel site="pickx.be" site_id="1031" lang="fr" xmltv_id="TiJi.fr@HD">Tiji HD</channel>
<channel site="pickx.be" site_id="1096" lang="fr" xmltv_id="TraceUrban.fr@HD">Trace HD</channel>
<channel site="pickx.be" site_id="1032" lang="fr" xmltv_id="CanalJ.fr@HD">Canal J HD</channel>
<channel site="pickx.be" site_id="1198" lang="en" xmltv_id="FashionTVEurope.fr@HD">Fashion TV HD</channel>
<channel site="pickx.be" site_id="1102" lang="fr" xmltv_id="Melody.fr@HD">Melody.tv HD</channel>
<channel site="pickx.be" site_id="1199" lang="nl" xmltv_id="MTV.nl@HD">MTV N HD</channel>
<channel site="pickx.be" site_id="1141" lang="fr" xmltv_id="AlAoula.ma@HD">Al Aoula HD</channel>
<channel site="pickx.be" site_id="1142" lang="fr" xmltv_id="AlJazeera.qa@EnglishHD">Al Jazeera HD</channel>
<channel site="pickx.be" site_id="1090" lang="fr" xmltv_id="Automotolachaine.fr@HD">AUTOMOTO HD</channel>
<channel site="pickx.be" site_id="1095" lang="fr" xmltv_id="RFMTV.fr@HD">RFM TV HD</channel>
<channel site="pickx.be" site_id="1132" lang="de" xmltv_id="RTL.de@HD">RTL Deutschland HD</channel>
<channel site="pickx.be" site_id="1134" lang="fr" xmltv_id="TVEInternacionalEuropeAsia.es@HD">TVE International HD</channel>
<channel site="pickx.be" site_id="1062" lang="fr" xmltv_id="Animaux.fr@HD">Animaux HD</channel>
<channel site="pickx.be" site_id="1126" lang="en" xmltv_id="EuronewsEnglish.fr@HD">Euronews E HD</channel>
<channel site="pickx.be" site_id="1128" lang="fr" xmltv_id="EuronewsFrench.fr@HD">Euronews F HD</channel>
<channel site="pickx.be" site_id="1295" lang="nl" xmltv_id="HGTV.be@SD">HGTV</channel>
<channel site="pickx.be" site_id="1046" lang="fr" xmltv_id="TVBreizh.fr@SD">TV Breizh</channel>
<channel site="pickx.be" site_id="1060" lang="fr" xmltv_id="HistoireTV.fr@SD">Histoire</channel>
<channel site="pickx.be" site_id="1094" lang="fr" xmltv_id="RadioContact.be@SD">ContactV</channel>
<channel site="pickx.be" site_id="1137" lang="fr" xmltv_id="Rai2.it@SD">RAI Due</channel>
<channel site="pickx.be" site_id="1185" lang="nl" xmltv_id="DiscoveryChannel.nl@SD">Discovery N</channel>
<channel site="pickx.be" site_id="1020" lang="fr" xmltv_id="arte.fr@SD">Arte</channel>
<channel site="pickx.be" site_id="1045" lang="fr" xmltv_id="Action.fr@SD">Action</channel>
<channel site="pickx.be" site_id="1026" lang="fr" xmltv_id="CartoonNetworkCEE.uk@France">Cartoon F</channel>
<channel site="pickx.be" site_id="1176" lang="nl" xmltv_id="CartoonNetworkWesternEurope.uk@Netherlands">Cartoon N</channel>
<channel site="pickx.be" site_id="1056" lang="fr" xmltv_id="Trek.fr@SD">TREK</channel>
<channel site="pickx.be" site_id="1024" lang="fr" xmltv_id="DisneyChannel.fr@SD">Disn Ch F</channel>
<channel site="pickx.be" site_id="1061" lang="fr" xmltv_id="UshuaiaTV.fr@SD">Ushuaïa</channel>
<channel site="pickx.be" site_id="1004" lang="fr" xmltv_id="RTLPlug.be@SD">RTL plug</channel>
<channel site="pickx.be" site_id="1134" lang="fr" xmltv_id="TVEInternacionalEuropeAsia.es@SD">TVE Int</channel>
<channel site="pickx.be" site_id="1096" lang="fr" xmltv_id="TraceUrban.fr@SD">TraceUrban</channel>
<channel site="pickx.be" site_id="1191" lang="nl" xmltv_id="DiscoveryScienceEurope.uk@Netherlands">Discovery Science</channel>
<channel site="pickx.be" site_id="1021" lang="fr" xmltv_id="TV5MondeFranceBelgiumSwitzerlandMonaco.fr@SD">TV5 Monde</channel>
<channel site="pickx.be" site_id="1198" lang="en" xmltv_id="FashionTVEurope.fr@SD">FashionTV</channel>
<channel site="pickx.be" site_id="1125" lang="en" xmltv_id="CNNInternational.us@MENA">CNN</channel>
<channel site="pickx.be" site_id="1159" lang="nl" xmltv_id="KanaalZ.be@SD">Kanaal Z</channel>
<channel site="pickx.be" site_id="1014" lang="fr" xmltv_id="CanalZ.be@SD">Canal Z</channel>
<channel site="pickx.be" site_id="1144" lang="nl" xmltv_id="VRT1.be@SD">VRT 1</channel>
<channel site="pickx.be" site_id="1019" lang="fr" xmltv_id="France5.fr@SD">France 5</channel>
<channel site="pickx.be" site_id="1171" lang="nl" xmltv_id="Nickelodeon.nl@SD">nickelodeon N</channel>
<channel site="pickx.be" site_id="1146" lang="nl" xmltv_id="VTM.be@SD">VTM</channel>
<channel site="pickx.be" site_id="1017" lang="fr" xmltv_id="ABXplore.be@SD">ABXPLORE</channel>
<channel site="pickx.be" site_id="1095" lang="fr" xmltv_id="RFMTV.fr@SD">RFM TV</channel>
<channel site="pickx.be" site_id="1149" lang="nl" xmltv_id="VTM2.be@SD">VTM 2</channel>
<channel site="pickx.be" site_id="1033" lang="fr" xmltv_id="Mangas.fr@SD">Mangas</channel>
<channel site="pickx.be" site_id="1161" lang="nl" xmltv_id="NPO1.nl@SD">NPO 1</channel>
<channel site="pickx.be" site_id="1136" lang="fr" xmltv_id="Rai1.it@SD">RAI Uno</channel>
<channel site="pickx.be" site_id="1062" lang="fr" xmltv_id="Animaux.fr@SD">Animaux</channel>
<channel site="pickx.be" site_id="1032" lang="fr" xmltv_id="CanalJ.fr@SD">Canal J</channel>
<channel site="pickx.be" site_id="1016" lang="fr" xmltv_id="AB3.be@SD">AB3</channel>
<channel site="pickx.be" site_id="1001" lang="fr" xmltv_id="Tipik.be@SD">RTBF TIPIK</channel>
<channel site="pickx.be" site_id="1000" lang="fr" xmltv_id="LaUne.be@SD">RTBF La Une</channel>
<channel site="pickx.be" site_id="1048" lang="fr" xmltv_id="Syfy.fr@SD">SyFy F</channel>
<channel site="pickx.be" site_id="1055" lang="fr" xmltv_id="NationalGeographic.fr@SD">National Geographic F</channel>
<channel site="pickx.be" site_id="1148" lang="nl" xmltv_id="Play5.be@SD">Play5</channel>
<channel site="pickx.be" site_id="1127" lang="fr" xmltv_id="LCI.fr@SD">LCI</channel>
<channel site="pickx.be" site_id="1005" lang="fr" xmltv_id="TF1.fr@SD">TF1</channel>
<channel site="pickx.be" site_id="1166" lang="en" xmltv_id="BBCNews.uk@Europe">BBC News</channel>
<channel site="pickx.be" site_id="1003" lang="fr" xmltv_id="RTLClub.be@SD">RTL club</channel>
<channel site="pickx.be" site_id="1164" lang="en" xmltv_id="BBCOne.uk@London">BBC One</channel>
<channel site="pickx.be" site_id="1002" lang="fr" xmltv_id="RTLTVI.be@SD">RTL tvi</channel>
<channel site="pickx.be" site_id="1129" lang="fr" xmltv_id="France24.fr@French">France 24 F</channel>
<channel site="pickx.be" site_id="1009" lang="fr" xmltv_id="BX1.be@SD">bx1</channel>
<channel site="pickx.be" site_id="1087" lang="fr" xmltv_id="Eurosport1.fr@France">Eurosport F</channel>
<channel site="pickx.be" site_id="1163" lang="nl" xmltv_id="NPO3.nl@SD">NPO 3</channel>
<channel site="pickx.be" site_id="1162" lang="nl" xmltv_id="NPO2.nl@SD">NPO 2</channel>
<channel site="pickx.be" site_id="1018" lang="fr" xmltv_id="France4.fr@SD">France 4</channel>
<channel site="pickx.be" site_id="1150" lang="nl" xmltv_id="VTM3.be@SD">VTM 3</channel>
<channel site="pickx.be" site_id="1147" lang="nl" xmltv_id="Play4.be@SD">Play4</channel>
<channel site="pickx.be" site_id="1145" lang="nl" xmltv_id="VRTCanvas.be@SD">VRT Canvas</channel>
<channel site="pickx.be" site_id="1126" lang="en" xmltv_id="EuronewsEnglish.fr@SD">Euronews E</channel>
<channel site="pickx.be" site_id="1186" lang="nl" xmltv_id="NationalGeographic.nl@SD">National Geographic N</channel>
<channel site="pickx.be" site_id="1006" lang="fr" xmltv_id="France2.fr@SD">France 2</channel>
<channel site="pickx.be" site_id="1007" lang="fr" xmltv_id="France3.fr@SD">France 3</channel>
<channel site="pickx.be" site_id="1088" lang="fr" xmltv_id="Eurosport2.fr@France">Eurosp2 F</channel>
<channel site="pickx.be" site_id="1187" lang="nl" xmltv_id="AnimalPlanet.nl@SD">Animal Planet</channel>
<channel site="pickx.be" site_id="1220" lang="nl" xmltv_id="Eurosport1.fr@Netherlands">Eurosport N</channel>
<channel site="pickx.be" site_id="1031" lang="fr" xmltv_id="TiJi.fr@SD">Tiji</channel>
<channel site="pickx.be" site_id="1049" lang="fr" xmltv_id="TCMCinema.fr@SD">TCM Cinéma</channel>
<channel site="pickx.be" site_id="1226" lang="fr" xmltv_id="DorcelXXX.nl@SD">Dorcel XXX</channel>
<channel site="pickx.be" site_id="1059" lang="fr" xmltv_id="ScienceVieTV.fr@SD">Science et Vie TV</channel>
<channel site="pickx.be" site_id="1029" lang="fr" xmltv_id="Boomerang.fr@SD">Boomerang F</channel>
<channel site="pickx.be" site_id="1064" lang="fr" xmltv_id="E.fr@SD">E! F</channel>
<channel site="pickx.be" site_id="1047" lang="fr" xmltv_id="13emeRue.fr@SD">13è Rue</channel>
<channel site="pickx.be" site_id="1090" lang="fr" xmltv_id="Automotolachaine.fr@SD">AUTOMOTO</channel>
<channel site="pickx.be" site_id="1070" lang="fr" xmltv_id="PickxLive.be@FR">Pickx Showcase F</channel>
<channel site="pickx.be" site_id="1124" lang="nl" xmltv_id="WTV.be@SD">WTV</channel>
<channel site="pickx.be" site_id="1117" lang="nl" xmltv_id="FocusTV.be@SD">Focus TV</channel>
<channel site="pickx.be" site_id="1115" lang="nl" xmltv_id="ATV.be@SD">ATV</channel>
<channel site="pickx.be" site_id="1122" lang="nl" xmltv_id="TVLimburg.be@SD">TV Limburg</channel>
<channel site="pickx.be" site_id="1119" lang="nl" xmltv_id="ROBTV.be@SD">ROBTV</channel>
<channel site="pickx.be" site_id="1197" lang="nl" xmltv_id="EEurope.nl@SD">E! N</channel>
<channel site="pickx.be" site_id="1123" lang="nl" xmltv_id="TVOost.be@SD">TV Oost</channel>
<channel site="pickx.be" site_id="1013" lang="nl" xmltv_id="Bruzz.be@SD">BRUZZ</channel>
<channel site="pickx.be" site_id="1120" lang="nl" xmltv_id="RTVKempen.be@SD">RTV Kempen O</channel>
<channel site="pickx.be" site_id="1121" lang="nl" xmltv_id="RTVMech.be@SD">RTV Mech</channel>
<channel site="pickx.be" site_id="1118" lang="nl" xmltv_id="RingTV.be@SD">Ring TV</channel>
<channel site="pickx.be" site_id="1107" lang="fr" xmltv_id="Notele.be@SD">No Télé</channel>
<channel site="pickx.be" site_id="1025" lang="fr" xmltv_id="DisneyJr.fr@SD">Disney Jr. F</channel>
<channel site="pickx.be" site_id="1109" lang="fr" xmltv_id="TeleMB.be@SD">Télé MB</channel>
<channel site="pickx.be" site_id="1141" lang="fr" xmltv_id="AlAoula.ma@SD">Al Aoula</channel>
<channel site="pickx.be" site_id="1067" lang="fr" xmltv_id="MTV.fr@SD">MTV F</channel>
<channel site="pickx.be" site_id="1199" lang="nl" xmltv_id="MTV.nl@SD">MTV N</channel>
<channel site="pickx.be" site_id="1183" lang="nl" xmltv_id="ComedyCentral.nl@SD">Comedy Central N</channel>
<channel site="pickx.be" site_id="1104" lang="fr" xmltv_id="Bouke.be@SD">Bouké</channel>
<channel site="pickx.be" site_id="1105" lang="fr" xmltv_id="CanalZoom.be@SD">Canal Zoom</channel>
<channel site="pickx.be" site_id="1103" lang="fr" xmltv_id="ACTV.be@SD">Antenne C</channel>
<channel site="pickx.be" site_id="1225" lang="nl" xmltv_id="PassieXXX.nl@SD">Passie XXX</channel>
<channel site="pickx.be" site_id="1178" lang="nl" xmltv_id="NickJr.nl@SD">nick jr N</channel>
<channel site="pickx.be" site_id="1189" lang="nl" xmltv_id="History.nl@SD">History</channel>
<channel site="pickx.be" site_id="1102" lang="fr" xmltv_id="Melody.fr@SD">Melody.tv</channel>
<channel site="pickx.be" site_id="1142" lang="fr" xmltv_id="AlJazeera.qa@English">Al Jazeera</channel>
<channel site="pickx.be" site_id="1135" lang="fr" xmltv_id="KTO.fr@SD">KTO</channel>
<channel site="pickx.be" site_id="1179" lang="nl" xmltv_id="Nicktoons.nl@SD">nick Toons</channel>
<channel site="pickx.be" site_id="1005" lang="fr" xmltv_id="TF1.fr@HD">TF1 HD</channel>
<channel site="pickx.be" site_id="1229" lang="fr" xmltv_id="PinkX.fr@SD">Pink</channel>
<channel site="pickx.be" site_id="1061" lang="fr" xmltv_id="UshuaiaTV.fr@HD">Ushuaïa HD</channel>
<channel site="pickx.be" site_id="1097" lang="nl" xmltv_id="StingrayClassica.ca@SD">Classica</channel>
<channel site="pickx.be" site_id="1185" lang="nl" xmltv_id="DiscoveryChannel.nl@HD">Disc HD N</channel>
<channel site="pickx.be" site_id="1222" lang="nl" xmltv_id="Sport10.be@HD">Sport 10 HD</channel>
<channel site="pickx.be" site_id="1055" lang="fr" xmltv_id="NationalGeographic.fr@HD">NGC HD F</channel>
<channel site="pickx.be" site_id="1144" lang="nl" xmltv_id="VRT1.be@HD">VRT 1 HD</channel>
<channel site="pickx.be" site_id="1003" lang="fr" xmltv_id="RTLClub.be@HD">RTL club HD</channel>
<channel site="pickx.be" site_id="1220" lang="nl" xmltv_id="Eurosport1.fr@NetherlandsHD">Eurosp HD N</channel>
<channel site="pickx.be" site_id="1006" lang="fr" xmltv_id="France2.fr@HD">France 2 HD</channel>
<channel site="pickx.be" site_id="1210" lang="nl" xmltv_id="PickxPlusSports7.be@NL">Pickx+ Sports 7 N</channel>
<channel site="pickx.be" site_id="1188" lang="nl" xmltv_id="InvestigationDiscovery.nl@SD">Inv Disc</channel>
<channel site="pickx.be" site_id="1212" lang="nl" xmltv_id="PickxPlusSports9.be@NL">Pickx+ Sports 9 N</channel>
<channel site="pickx.be" site_id="1213" lang="nl" xmltv_id="PickxPlusSports10.be@NL">Pickx+ Sports 10 N</channel>
<channel site="pickx.be" site_id="1211" lang="nl" xmltv_id="PickxPlusSports8.be@NL">Pickx+ Sports 8 N</channel>
<channel site="pickx.be" site_id="1080" lang="fr" xmltv_id="PickxPlusSports10.be@FR">Pickx+ Sports 10 F</channel>
<channel site="pickx.be" site_id="1077" lang="fr" xmltv_id="PickxPlusSports7.be@FR">Pickx+ Sports 7 F</channel>
<channel site="pickx.be" site_id="1078" lang="fr" xmltv_id="PickxPlusSports8.be@FR">Pickx+ Sports 8 F</channel>
<channel site="pickx.be" site_id="1079" lang="fr" xmltv_id="PickxPlusSports9.be@FR">Pickx+ Sports 9 F</channel>
<channel site="pickx.be" site_id="1221" lang="nl" xmltv_id="Eurosport2.fr@Netherlands">Eurosp2 N</channel>
<channel site="pickx.be" site_id="1077" lang="fr" xmltv_id="PickxPlusSports7.be@FRHD">Pickx+ Sports 7 F HD</channel>
<channel site="pickx.be" site_id="1210" lang="nl" xmltv_id="PickxPlusSports7.be@NLHD">Pickx+ Sports 7 N HD</channel>
<channel site="pickx.be" site_id="1165" lang="en" xmltv_id="BBCTwo.uk@England">BBC Two</channel>
<channel site="pickx.be" site_id="1146" lang="nl" xmltv_id="VTM.be@HD">VTM HD</channel>
<channel site="pickx.be" site_id="1151" lang="nl" xmltv_id="VTM4.be@SD">VTM 4</channel>
<channel site="pickx.be" site_id="1008" lang="fr" xmltv_id="LaTrois.be@SD">La Trois</channel>
<channel site="pickx.be" site_id="1180" lang="nl" xmltv_id="Cartoonito.uk@SD">Cartoonito</channel>
<channel site="pickx.be" site_id="1145" lang="nl" xmltv_id="VRTCanvas.be@HD">VRT Canvas HD</channel>
<channel site="pickx.be" site_id="1170" lang="nl" xmltv_id="Ketnet.be@SD">Ketnet</channel>
<channel site="pickx.be" site_id="1023" lang="fr" xmltv_id="Nickelodeon.fr@SD">nick F</channel>
<channel site="pickx.be" site_id="1224" lang="nl" xmltv_id="MentTV.be@SD">Ment TV</channel>
<channel site="pickx.be" site_id="1000" lang="fr" xmltv_id="LaUne.be@HD">La Une HD</channel>
<channel site="pickx.be" site_id="1173" lang="nl" xmltv_id="DisneyChannel.nl@SD">Disn Ch N</channel>
<channel site="pickx.be" site_id="1008" lang="fr" xmltv_id="LaTrois.be@HD">La Trois HD</channel>
<channel site="pickx.be" site_id="1108" lang="fr" xmltv_id="QU4TRELiegeMedia.be@SD">Quatre</channel>
<channel site="pickx.be" site_id="1133" lang="de" xmltv_id="">BRF Fernsehen</channel>
<channel site="pickx.be" site_id="1245" lang="de" xmltv_id="">BRF 1</channel>
<channel site="pickx.be" site_id="1261" lang="en" xmltv_id="">Test VRT ENG</channel>
<channel site="pickx.be" site_id="1265" lang="en" xmltv_id="">Test 977</channel>
<channel site="pickx.be" site_id="1266" lang="en" xmltv_id="">Test RTBF ENG</channel>
<channel site="pickx.be" site_id="1267" lang="en" xmltv_id="">Test RTL ENG</channel>
<channel site="pickx.be" site_id="1268" lang="en" xmltv_id="">Test DPG ENG</channel>
<channel site="pickx.be" site_id="1277" lang="en" xmltv_id="">Test 998</channel>
<channel site="pickx.be" site_id="1302" lang="en" xmltv_id="">Horse and Country</channel>
<channel site="pickx.be" site_id="2000" lang="en" xmltv_id="">Sanity Check Live 01</channel>
<channel site="pickx.be" site_id="1069" lang="fr" xmltv_id="">Pickx+ F</channel>
<channel site="pickx.be" site_id="1246" lang="fr" xmltv_id="">RTBF La Prem1ère</channel>
<channel site="pickx.be" site_id="1247" lang="fr" xmltv_id="">RTBF VivaCité</channel>
<channel site="pickx.be" site_id="1248" lang="fr" xmltv_id="">RTBF Musiq&apos;3</channel>
<channel site="pickx.be" site_id="1249" lang="fr" xmltv_id="">RTBF Classic21</channel>
<channel site="pickx.be" site_id="1250" lang="fr" xmltv_id="">RTBF Tipik Radio</channel>
<channel site="pickx.be" site_id="1251" lang="fr" xmltv_id="">bel RTL</channel>
<channel site="pickx.be" site_id="1252" lang="fr" xmltv_id="">MINT</channel>
<channel site="pickx.be" site_id="1253" lang="fr" xmltv_id="">Contact</channel>
<channel site="pickx.be" site_id="1254" lang="fr" xmltv_id="">Nostalgie</channel>
<channel site="pickx.be" site_id="1255" lang="fr" xmltv_id="">LN Radio</channel>
<channel site="pickx.be" site_id="1294" lang="fr" xmltv_id="">Test 984</channel>
<channel site="pickx.be" site_id="1296" lang="fr" xmltv_id="">RTL district</channel>
<channel site="pickx.be" site_id="2001" lang="fr" xmltv_id="">Sanity Check Radio</channel>
<channel site="pickx.be" site_id="2002" lang="fr" xmltv_id="">Sanity Check Live 02</channel>
<channel site="pickx.be" site_id="2003" lang="fr" xmltv_id="">Sanity Check Live 03</channel>
<channel site="pickx.be" site_id="2004" lang="fr" xmltv_id="">Sanity Check Live 04</channel>
<channel site="pickx.be" site_id="2005" lang="fr" xmltv_id="">Sanity Check Live 05</channel>
<channel site="pickx.be" site_id="2006" lang="fr" xmltv_id="">Sanity Check Live 06</channel>
<channel site="pickx.be" site_id="2007" lang="fr" xmltv_id="">Sanity Check Live 07</channel>
<channel site="pickx.be" site_id="2008" lang="fr" xmltv_id="">Sanity Check Live 08</channel>
<channel site="pickx.be" site_id="2009" lang="fr" xmltv_id="">Sanity Check Live 09</channel>
<channel site="pickx.be" site_id="2010" lang="fr" xmltv_id="">Sanity Check Live 10</channel>
<channel site="pickx.be" site_id="2011" lang="fr" xmltv_id="">Sanity Check Live 11</channel>
<channel site="pickx.be" site_id="2012" lang="fr" xmltv_id="">Sanity Check Live 12</channel>
<channel site="pickx.be" site_id="2020" lang="fr" xmltv_id="">Sanity Check Live 20</channel>
<channel site="pickx.be" site_id="3350" lang="fr" xmltv_id="">NTVBE Test 11</channel>
<channel site="pickx.be" site_id="3351" lang="fr" xmltv_id="">NTVBE Test 12</channel>
<channel site="pickx.be" site_id="3352" lang="fr" xmltv_id="">NTVBE Test 13</channel>
<channel site="pickx.be" site_id="3353" lang="fr" xmltv_id="">NTVBE Test 14</channel>
<channel site="pickx.be" site_id="3354" lang="fr" xmltv_id="">NTVBE Test 15</channel>
<channel site="pickx.be" site_id="1139" lang="it" xmltv_id="">Mediaset Italia</channel>
<channel site="pickx.be" site_id="1152" lang="nl" xmltv_id="">Studio 100 TV</channel>
<channel site="pickx.be" site_id="1153" lang="nl" xmltv_id="">Proximus Showcase N</channel>
<channel site="pickx.be" site_id="1169" lang="nl" xmltv_id="">Studio 100 TV</channel>
<channel site="pickx.be" site_id="1184" lang="nl" xmltv_id="">STAR Channel</channel>
<channel site="pickx.be" site_id="1202" lang="nl" xmltv_id="">Pickx+ N</channel>
<channel site="pickx.be" site_id="1230" lang="nl" xmltv_id="">Radio 1</channel>
<channel site="pickx.be" site_id="1231" lang="nl" xmltv_id="">Radio 2 Antwerpen</channel>
<channel site="pickx.be" site_id="1232" lang="nl" xmltv_id="">Radio 2 Limburg</channel>
<channel site="pickx.be" site_id="1233" lang="nl" xmltv_id="">Radio 2 Oost-Vlaanderen</channel>
<channel site="pickx.be" site_id="1234" lang="nl" xmltv_id="">Radio 2 Vlaam-Brabant</channel>
<channel site="pickx.be" site_id="1235" lang="nl" xmltv_id="">Radio 2 West-Vlaanderen</channel>
<channel site="pickx.be" site_id="1237" lang="nl" xmltv_id="">MNM</channel>
<channel site="pickx.be" site_id="1238" lang="nl" xmltv_id="">MNM Hits</channel>
<channel site="pickx.be" site_id="1239" lang="nl" xmltv_id="">Klara</channel>
<channel site="pickx.be" site_id="1240" lang="nl" xmltv_id="">Klara Continuo</channel>
<channel site="pickx.be" site_id="1241" lang="nl" xmltv_id="">Radio Bene</channel>
<channel site="pickx.be" site_id="1242" lang="nl" xmltv_id="">De Tijdloze</channel>
<channel site="pickx.be" site_id="1243" lang="nl" xmltv_id="">Qmusic</channel>
<channel site="pickx.be" site_id="1244" lang="nl" xmltv_id="">JOE</channel>
<channel site="pickx.be" site_id="1278" lang="nl" xmltv_id="">Play Crime</channel>
<channel site="pickx.be" site_id="1284" lang="nl" xmltv_id="">Frame</channel>
<channel site="pickx.be" site_id="1301" lang="nl" xmltv_id="">Pickx Crime</channel>
</channels>
+119 -102
View File
@@ -1,133 +1,150 @@
// credit for this fix goes to davidclaeysquinones for his PR on https://github.com/iptv-org/epg/pull/2430, https://github.com/iptv-org/epg/pull/2520 and to BellezaEmporium for his PR on https://github.com/iptv-org/epg/pull/2480, https://github.com/iptv-org/epg/pull/2525
const axios = require('axios') const axios = require('axios')
const dayjs = require('dayjs') const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const timezone = require('dayjs/plugin/timezone')
let apiVersion dayjs.extend(utc)
dayjs.extend(timezone)
const API_ENDPOINT = 'https://api.prod-ntvbe.tvapi-web.pickx.be'
const API_IMAGE_ENDPOINT =
'https://experience-cache.cdi.streaming.proximustv.be/posterserver/poster/EPG'
const TIMEZONE = 'Europe/Brussels'
// every tray of the AVS backend is addressed through
// /{tenant}/{version}/{cluster}/{locale}/{platform}/{property}, where the cluster is the access
// level ("A" is anonymous, which is enough for the public guide) and the platform has to be WEB
const BASE_PATH = '/AGL/1.5/A/{locale}/WEB/PXS'
// the locale only selects the language of the labels the API itself emits, never the language of
// the guide, so a channel keeps its broadcast language whichever locale is requested
const LOCALES = { en: 'ENG', fr: 'FRA', nl: 'NLD', de: 'DEU' }
const LANGUAGES = {
ara: 'ar',
deu: 'de',
eng: 'en',
fra: 'fr',
ita: 'it',
nld: 'nl',
spa: 'es'
}
// the channel tray refuses any page wider than this
const CHANNELS_PER_PAGE = 100
module.exports = { module.exports = {
site: 'pickx.be', site: 'pickx.be',
days: 2, days: 2,
async url({ channel, date }) {
if (!apiVersion) {
await fetchApiVersion()
}
return `https://px-epg.azureedge.net/airings/${apiVersion}/${date.format(
'YYYY-MM-DD'
)}/channel/${channel.site_id}?timezone=Europe%2FBrussels`
},
request: { request: {
headers: { headers: {
Origin: 'https://www.pickx.be', Origin: 'https://tv.pickx.be',
Referer: 'https://www.pickx.be/' Referer: 'https://tv.pickx.be/'
} }
}, },
parser({ channel, content }) { url({ channel, date }) {
// the guide is cut into Brussels days, while the grabber hands us a UTC moment. Both edges
// are resolved from their own calendar date, because adding a day to the start would add a
// flat 24 hours and so cut the 25 hour day short, or overrun the 23 hour one, when the
// Brussels clock shifts
const start = brusselsMidnight(date)
const stop = brusselsMidnight(date.add(1, 'd'))
// from/to paginates the channels of the tray, not their programs, so asking for a single
// channel still returns its whole day
return `${API_ENDPOINT}${basePath(channel.lang)}/TRAY/EPG?from=1&to=1&filter_channelIds=${
channel.site_id
}&filter_startTime=${start.valueOf()}&filter_endTime=${stop.valueOf()}`
},
parser({ content }) {
const programs = [] const programs = []
if (content) { parseItems(content).forEach(item => {
const items = JSON.parse(content) const data = item.metadata
items.forEach(item => { if (!data) return
programs.push({
title: item.program.title, programs.push({
sub_title: item.program.episodeTitle, title: data.title,
description: item.program.description, sub_title: data.episodeTitle,
category: item.program.translatedCategory?.[channel.lang] description: data.longDescription || data.shortDescription,
? item.program.translatedCategory[channel.lang] category: data.genres,
: item.program.category.split('.')[1], image: parseImage(data),
image: item.program.posterFileName season: data.season,
? `https://experience-cache.proximustv.be/posterserver/poster/EPG/w-166_h-110/${item.program.posterFileName}` episode: data.episodeNumber,
: null, actors: data.actors,
season: item.program.seasonNumber, director: data.directors,
episode: item.program.episodeNumber, date: data.year,
actors: item.program.actors, country: data.country,
director: item.program.director ? [item.program.director] : null, start: dayjs(data.airingStartTime),
start: dayjs(item.programScheduleStart), stop: dayjs(data.airingEndTime)
stop: dayjs(item.programScheduleEnd)
})
}) })
} })
return programs return programs
}, },
async channels() { async channels() {
let channels = [] const channels = []
const query = { for (let from = 1; ; from += CHANNELS_PER_PAGE) {
operationName: 'getChannels', const data = await fetchChannels(from)
variables: { if (!data) break
language: 'fr',
queryParams: {},
id: '0',
params: {
shouldReadFromCache: true
}
},
query: `query getChannels($language: String!, $queryParams: ChannelQueryParams, $id: String, $params: ChannelParams) {
channels(language: $language, queryParams: $queryParams, id: $id, params: $params) {
id
name
language
radio
}
}`
}
const data = await axios const items = data.containers || []
.post('https://api.proximusmwc.be/tiams/v3/graphql', query) items.forEach(item => {
.then(r => r.data) const metadata = item.metadata
.catch(console.error) if (!metadata) return
data.data.channels.forEach(channel => { channels.push({
let lang = channel.language || 'fr' lang: LANGUAGES[metadata.channelLanguage] || 'fr',
if (channel.language === 'ger') lang = 'de' site_id: String(metadata.channelId),
name: metadata.channelName
channels.push({ })
lang,
site_id: channel.id,
name: channel.name
}) })
})
if (!items.length || channels.length >= data.total) break
}
return channels return channels
} }
} }
async function fetchApiVersion() { function basePath(lang) {
const hashUrl = 'https://www.pickx.be/nl/televisie/tv-gids' return BASE_PATH.replace('{locale}', LOCALES[lang] || LOCALES.nl)
const hashData = await axios }
.get(hashUrl)
.then(r => { function brusselsMidnight(date) {
const re = /"hashes":\["(.*)"\]/ return dayjs.tz(date.format('YYYY-MM-DD'), TIMEZONE).startOf('d')
const match = r.data.match(re) }
if (match && match[1]) {
return match[1] function fetchChannels(from) {
} else { return axios
throw new Error('React app version hash not found') .get(`${API_ENDPOINT}${basePath('nl')}/TRAY/LIVECHANNELS`, {
params: { from, to: from + CHANNELS_PER_PAGE - 1 },
headers: {
Origin: 'https://tv.pickx.be',
Referer: 'https://tv.pickx.be/'
} }
}) })
.then(r => r.data.resultObj)
.catch(console.error) .catch(console.error)
}
const versionUrl = `https://www.pickx.be/api/s-${hashData}` function parseImage(data) {
const response = await axios.get(versionUrl, { const filename = data.pictureUrl || data.extendedMetadata?.images?.landscapePoster
headers: {
Origin: 'https://www.pickx.be',
Referer: 'https://www.pickx.be/'
}
})
return new Promise((resolve, reject) => { return filename ? `${API_IMAGE_ENDPOINT}/${filename}` : null
try { }
if (response.status === 200) {
apiVersion = response.data.version function parseItems(content) {
resolve() if (!content) return []
} else {
console.error(`Failed to fetch API version. Status: ${response.status}`) try {
reject(`Failed to fetch API version. Status: ${response.status}`) const data = JSON.parse(content)
} // the tray nests the programs of each requested channel under their own container
} catch (error) { const channel = data?.resultObj?.containers?.[0]
console.error('Error during fetchApiVersion:', error)
reject(error) return channel?.containers || []
} } catch {
}) return []
} }
}
+189 -66
View File
@@ -1,11 +1,20 @@
// credit for this fix goes to davidclaeysquinones for his PR on https://github.com/iptv-org/epg/pull/2429 //https://github.com/iptv-org/epg/blob/master/sites/telenet.tv/telenet.tv.config.js
const axios = require('axios') const axios = require('axios')
const dayjs = require('dayjs') const dayjs = require('dayjs')
const doFetch = require('@ntlab/sfetch')
const API_STATIC_ENDPOINT = 'https://static.spark.telenet.tv/eng/web/epg-service-lite/be' const API_STATIC_ENDPOINT = 'https://staticqbr-prod-be.gnp.cloud.telenet.tv/eng/web/epg-service-lite/be'
const API_PROD_ENDPOINT = 'https://spark-prod-be.gnp.cloud.telenet.tv/eng/web/linear-service/v2' const API_PROD_ENDPOINT = 'https://spark-prod-be.gnp.cloud.telenet.tv/eng/web/linear-service/v2'
const API_IMAGE_ENDPOINT = 'https://staticqbr-prod-be.gnp.cloud.telenet.tv/image-service'; const API_IMAGE_ENDPOINT = 'https://staticqbr-prod-be.gnp.cloud.telenet.tv/image-service'
// a segment holds 6 hours of guide for every channel at once, so it is kept around
// and shared between all the channels of the same day
const segments = {}
// which day emitted an event, per channel, so a broadcast running past midnight is not repeated
// by the day it ends in
const claimed = {}
module.exports = { module.exports = {
site: 'telenet.tv', site: 'telenet.tv',
@@ -15,68 +24,42 @@ module.exports = {
ttl: 60 * 60 * 1000 // 1 hour ttl: 60 * 60 * 1000 // 1 hour
} }
}, },
url: function ({ date, channel }) { url: function ({ date, channel, segment = 0 }) {
return `${API_STATIC_ENDPOINT}/${channel.lang}/events/segments/${date.format('YYYYMMDDHHmmss')}` return `${API_STATIC_ENDPOINT}/${channel.lang}/events/segments/${date.format(
'YYYYMMDD'
)}${segment.toString().padStart(2, '0')}0000`
}, },
async parser({ content, channel, date }) { async parser({ content, channel, date }) {
let programs = [] const items = await loadItems({ content, channel, date })
let items = parseItems(content, channel) if (!items.length) return []
if (!items.length) return programs
const promises = [
axios.get(
`${API_STATIC_ENDPOINT}/${channel.lang}/events/segments/${date
.add(6, 'h')
.format('YYYYMMDDHHmmss')}`,
{
responseType: 'arraybuffer'
}
),
axios.get(
`${API_STATIC_ENDPOINT}/${channel.lang}/events/segments/${date
.add(12, 'h')
.format('YYYYMMDDHHmmss')}`,
{
responseType: 'arraybuffer'
}
),
axios.get(
`${API_STATIC_ENDPOINT}/${channel.lang}/events/segments/${date
.add(18, 'h')
.format('YYYYMMDDHHmmss')}`,
{
responseType: 'arraybuffer'
}
)
]
await Promise.allSettled(promises) const details = await loadProgramDetails(items, channel)
.then(results => {
results.forEach(r => {
if (r.status === 'fulfilled') {
const parsed = parseItems(r.value.data, channel)
items = items.concat(parsed) return items.map((item, index) => {
} const detail = details[index] || {}
})
})
.catch(console.error)
for (let item of items) { return {
const detail = await loadProgramDetails(item, channel)
programs.push({
title: item.title, title: item.title,
subTitle: detail.episodeName || item.seriesName,
icon: parseIcon(item), icon: parseIcon(item),
description: detail.longDescription, image: parseImage(item),
description: detail.longDescription || detail.shortDescription,
category: detail.genres, category: detail.genres,
actors: detail.actors, actors: detail.actors,
directors: detail.directors,
producers: detail.producers,
season: parseSeason(detail), season: parseSeason(detail),
episode: parseEpisode(detail), episode: parseEpisode(detail),
date: parseYear(detail),
country: detail.countryOfOrigin,
rating: parseRating(detail, item),
language: parseLanguages(detail, item),
subtitles: parseSubtitles(detail, item),
new: Boolean(detail.premiere || item.premiere),
start: parseStart(item), start: parseStart(item),
stop: parseStop(item) stop: parseStop(item)
}) }
} })
return programs
}, },
async channels() { async channels() {
const data = await axios const data = await axios
@@ -94,15 +77,63 @@ module.exports = {
} }
} }
async function loadProgramDetails(item, channel) { async function loadItems({ content, channel, date }) {
if (!item.id) return {} const urls = [0, 6, 12, 18].map(segment => module.exports.url({ date, channel, segment }))
const url = `${API_PROD_ENDPOINT}/replayEvent/${item.id}?returnLinearContent=true&language=${channel.lang}`
const data = await axios
.get(url)
.then(r => r.data)
.catch(console.log)
return data || {} // the first segment has already been downloaded by the grabber itself
cacheSegment(urls[0], content)
const missing = urls.filter(url => segments[url] === undefined)
if (missing.length) {
await doFetch(missing, (url, res) => cacheSegment(url, res))
}
const items = urls.flatMap(url => findEvents(segments[url], channel)).filter(isBroadcast)
// an event that spans a boundary is listed twice: by both segments, or by both days
return uniqueItems(items, channel, date)
}
// `res` is left out when a request fails, but only when sfetch is checking results, which is a
// setting shared with the other sites using it, so every program is built from its event first
async function loadProgramDetails(items, channel) {
const details = []
const queues = items
.map((item, index) => ({ item, index }))
.filter(({ item }) => item.id)
.map(({ item, index }) => ({
url: `${API_PROD_ENDPOINT}/replayEvent/${item.id}?returnLinearContent=true&language=${channel.lang}`,
index
}))
if (queues.length) {
await doFetch(queues, (queue, res) => {
if (res) details[queue.index] = res
})
}
return details
}
function cacheSegment(url, content) {
if (segments[url] !== undefined) return
const entries = parseEntries(content)
// a failed download must not be cached, the next channel gets to retry it
if (entries.length) segments[url] = entries
}
// the grabber hands over the raw response, while axios has already parsed the ones fetched here
function parseEntries(content) {
if (!content) return []
try {
const data = Array.isArray(content.entries) ? content : JSON.parse(content)
return Array.isArray(data.entries) ? data.entries : []
} catch (err) {
console.error(`Unable to parse guide: ${err.message}!`)
return []
}
} }
function parseStart(item) { function parseStart(item) {
@@ -113,16 +144,41 @@ function parseStop(item) {
return dayjs.unix(item.endTime) return dayjs.unix(item.endTime)
} }
function parseItems(content, channel) { // telenet fills the hours a channel is off air with "Geen uitzending", often several in a row.
if (!content) return [] // Nothing is behind them, so they are dropped and the guide simply has a hole there.
const data = JSON.parse(content) function isBroadcast(item) {
if (!data || !Array.isArray(data.entries)) return [] return !/^geen uitzending\b/i.test(item.title || '')
const channelData = data.entries.find(e => e.channelId === channel.site_id) }
function findEvents(entries, channel) {
if (!Array.isArray(entries)) return []
const channelData = entries.find(e => e.channelId === channel.site_id)
if (!channelData) return [] if (!channelData) return []
return Array.isArray(channelData.events) ? channelData.events : [] return Array.isArray(channelData.events) ? channelData.events : []
} }
// Both guards are needed: the local set drops an event listed by two segments of this day, while
// the claim drops one the previous day already emitted. Re-parsing a day it claimed itself stays
// idempotent, so parsing the same day twice keeps returning the same programs.
function uniqueItems(items, channel, date) {
const day = date.format('YYYYMMDD')
const claimedByChannel = claimed[channel.site_id] || (claimed[channel.site_id] = new Map())
const seen = new Set()
return items.filter(item => {
if (seen.has(item.id)) return false
const owner = claimedByChannel.get(item.id)
if (owner !== undefined && owner !== day) return false
seen.add(item.id)
claimedByChannel.set(item.id, day)
return true
})
}
function parseSeason(detail) { function parseSeason(detail) {
if (!detail.seasonNumber) return null if (!detail.seasonNumber) return null
if (String(detail.seasonNumber).length > 2) return null if (String(detail.seasonNumber).length > 2) return null
@@ -135,6 +191,73 @@ function parseEpisode(detail) {
return detail.episodeNumber return detail.episodeNumber
} }
function parseYear(detail) {
if (!detail.productionDate) return null
if (!/^\d{4}$/.test(String(detail.productionDate))) return null
return detail.productionDate
}
// the minimum age follows the Kijkwijzer (NICAM) classification
function parseRating(detail, item) {
const minimumAge = detail.minimumAge != null ? detail.minimumAge : item.minimumAge
if (minimumAge == null || minimumAge === '') {
if (detail.isAdult || item.isAdult) return { system: 'Kijkwijzer', value: '18' }
return null
}
return { system: 'Kijkwijzer', value: String(minimumAge) }
}
function parseLanguages(detail, item) {
return parseLangCodes(detail.audioLanguages || item.audioLanguages)
}
function parseSubtitles(detail, item) {
const captions = parseLangCodes(detail.captionLanguages || item.captionLanguages)
const signed = parseLangCodes(detail.signLanguages || item.signLanguages)
return [
...captions.map(language => ({ language })),
...signed.map(language => ({ type: 'deaf-signed', language }))
]
}
// the same language can be listed more than once, once per purpose (e.g. audio description)
function parseLangCodes(languages) {
if (!Array.isArray(languages)) return []
return [...new Set(languages.map(language => language.lang).filter(Boolean))]
}
// Three assets hang off one event id. Only the poster is always there: over a day of the guide,
// episodeStill answered for 91% of programmes and titleTreatment for 66%, and nothing in the event
// says which, so all three are emitted and a reader has to expect a 404.
//
// Poster first because the DTD asks for the most authoritative image first -- though Telenet's own
// player prefers the still, its bundle carrying the literal list ['episodeStill','posterTile'].
const IMAGE_INTENTS = [
// Key art cropped to 2:3, always from the season or the movie, never per episode.
{ intent: 'posterTile', type: 'poster', orient: 'P' },
// 16:9; a real production still for two thirds, season or show artwork for the rest. The app
// picks the landscape image on `type` alone, so dropping this one hides it there silently.
{ intent: 'episodeStill', type: 'still', orient: 'L' },
// A transparent wordmark, which is none of the DTD's five types, so `system` is all it has.
{ intent: 'titleTreatment', orient: 'L' },
]
function parseIcon(item) { function parseIcon(item) {
return `${API_IMAGE_ENDPOINT}/intent/${item.id}/posterTile`; return imageUrl(item, 'posterTile')
}
function imageUrl(item, intent) {
return `${API_IMAGE_ENDPOINT}/intent/${item.id}/${intent}`
}
// <icon> repeats the poster because the two elements are read by different clients: <icon> has only
// a src, while <image> is the one that can say what the picture is.
function parseImage(item) {
return IMAGE_INTENTS.map(({ intent, type, orient }) => ({
...(type ? { type } : {}),
orient,
system: intent,
value: imageUrl(item, intent),
}))
} }
@@ -1,16 +1,13 @@
// credit for this fix goes to klausellus-wallace for his PR on https://github.com/iptv-org/epg/pull/2458 //https://github.com/iptv-org/epg/blob/master/sites/web.magentatv.de/web.magentatv.de.config.js
const axios = require('axios') const axios = require('axios')
const dayjs = require('dayjs') const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc') const utc = require('dayjs/plugin/utc')
const customParseFormat = require('dayjs/plugin/customParseFormat') const customParseFormat = require('dayjs/plugin/customParseFormat')
const fetch = require('node-fetch')
const { upperCase } = require('lodash')
let X_CSRFTOKEN let X_CSRFTOKEN
let COOKIE let Cookie
const cookiesToExtract = ['JSESSIONID', 'CSESSIONID', 'CSRFSESSION'] const cookiesToExtract = ['JSESSIONID', 'CSESSIONID', 'CSRFSESSION']
const extractedCookies = {}
dayjs.extend(utc) dayjs.extend(utc)
dayjs.extend(customParseFormat) dayjs.extend(customParseFormat)
@@ -21,10 +18,9 @@ module.exports = {
url: 'https://api.prod.sngtv.magentatv.de/EPG/JSON/PlayBillList', url: 'https://api.prod.sngtv.magentatv.de/EPG/JSON/PlayBillList',
request: { request: {
method: 'POST', method: 'POST',
headers: function () { async headers() {
return setHeaders() return await setHeaders()
}, },
data({ channel, date }) { data({ channel, date }) {
return { return {
count: -1, count: -1,
@@ -32,7 +28,8 @@ module.exports = {
offset: 0, offset: 0,
properties: [ properties: [
{ {
include: 'endtime,genres,id,name,starttime,channelid,pictures,introduce,subName,seasonNum,subNum,cast,country,producedate,externalIds', include:
'endtime,genres,id,name,starttime,channelid,pictures,introduce,subName,seasonNum,subNum,cast,country,producedate,externalIds',
name: 'playbill' name: 'playbill'
} }
], ],
@@ -43,14 +40,15 @@ module.exports = {
} }
} }
}, },
parser: function ({ content }) { parser({ content }) {
let programs = [] const programs = []
const items = parseItems(content) const items = parseItems(content)
items.forEach(item => { items.forEach(item => {
programs.push({ programs.push({
title: item.name, title: item.name,
description: item.introduce, description: item.introduce,
image: parseImage(item), image: parseImage(item),
icon: parseImage(item),
category: parseCategory(item), category: parseCategory(item),
start: parseStart(item), start: parseStart(item),
stop: parseStop(item), stop: parseStop(item),
@@ -60,7 +58,7 @@ module.exports = {
directors: parseDirectors(item), directors: parseDirectors(item),
producers: parseProducers(item), producers: parseProducers(item),
adapters: parseAdapters(item), adapters: parseAdapters(item),
country: upperCase(item.country), country: item.country?.toUpperCase(),
date: item.producedate, date: item.producedate,
urls: parseUrls(item) urls: parseUrls(item)
}) })
@@ -115,15 +113,15 @@ function parseCategory(item) {
} }
function parseDirectors(item) { function parseDirectors(item) {
if (!item.cast || !item.cast.director) return []; if (!item.cast || !item.cast.director) return []
return item.cast.director return item.cast.director
.replace('und', ',') .replace('und', ',')
.split(',') .split(',')
.map(i => i.trim()); .map(i => i.trim())
} }
function parseProducers(item) { function parseProducers(item) {
if (!item.cast || !item.cast.producer) return []; if (!item.cast || !item.cast.producer) return []
return item.cast.producer return item.cast.producer
.replace('und', ',') .replace('und', ',')
.split(',') .split(',')
@@ -131,7 +129,7 @@ function parseProducers(item) {
} }
function parseAdapters(item) { function parseAdapters(item) {
if (!item.cast || !item.cast.adaptor) return []; if (!item.cast || !item.cast.adaptor) return []
return item.cast.adaptor return item.cast.adaptor
.replace('und', ',') .replace('und', ',')
.split(',') .split(',')
@@ -140,7 +138,7 @@ function parseAdapters(item) {
function parseUrls(item) { function parseUrls(item) {
// currently only a imdb id is returned by the api, thus we can construct the url here // currently only a imdb id is returned by the api, thus we can construct the url here
if (!item.externalIds) return []; if (!item.externalIds) return []
return JSON.parse(item.externalIds) return JSON.parse(item.externalIds)
.filter(externalId => externalId.type === 'imdb' && externalId.id) .filter(externalId => externalId.type === 'imdb' && externalId.id)
.map(externalId => ({ system: 'imdb', value: `https://www.imdb.com/title/${externalId.id}` })) .map(externalId => ({ system: 'imdb', value: `https://www.imdb.com/title/${externalId.id}` }))
@@ -167,66 +165,51 @@ function parseItems(content) {
return data.playbilllist return data.playbilllist
} }
// Function to try to fetch COOKIE and X_CSRFTOKEN async function fetchCookieAndToken() {
function fetchCookieAndToken() { // Only fetch the cookies and csrfToken if they are not already set
return fetch( if (X_CSRFTOKEN && Cookie) {
'https://api.prod.sngtv.magentatv.de/EPG/JSON/Authenticate?SID=firstup&T=Windows_chrome_118', return
{ }
headers: {
accept: 'application/json, text/javascript, */*; q=0.01', try {
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8', const response = await axios.request({
'sec-fetch-dest': 'empty', url: 'https://api.prod.sngtv.magentatv.de/EPG/JSON/Authenticate',
'sec-fetch-mode': 'cors', params: {
'sec-fetch-site': 'same-origin', SID: 'firstup',
'x-requested-with': 'XMLHttpRequest', T: 'Windows_chrome_118'
Referer: 'https://web.magentatv.de/',
'Referrer-Policy': 'strict-origin-when-cross-origin'
}, },
body: '{"terminalid":"00:00:00:00:00:00","mac":"00:00:00:00:00:00","terminaltype":"WEBTV","utcEnable":1,"timezone":"Etc/GMT0","userType":3,"terminalvendor":"Unknown"}', method: 'POST',
method: 'POST' data: '{"terminalid":"00:00:00:00:00:00","mac":"00:00:00:00:00:00","terminaltype":"WEBTV","utcEnable":1,"timezone":"Etc/GMT0","userType":3,"terminalvendor":"Unknown"}'
})
// Extract the cookies specified in cookiesToExtract
const setCookieHeader = response.headers['set-cookie'] || []
const extractedCookies = []
cookiesToExtract.forEach(cookieName => {
const regex = new RegExp(`${cookieName}=(.+?)(;|$)`)
const match = setCookieHeader.find(header => regex.test(header))
if (match) {
const cookieString = regex.exec(match)[0]
extractedCookies.push(cookieString)
}
})
// check if we recieved a csrfToken only then store the values
if (!response.data.csrfToken) {
console.log('csrfToken not found in the response.')
return
} }
)
.then(response => {
// Check if the response status is OK (2xx)
if (!response.ok) {
throw new Error('HTTP request failed')
}
// Extract the set-cookie header X_CSRFTOKEN = response.data.csrfToken
const setCookieHeader = response.headers.raw()['set-cookie'] Cookie = extractedCookies.join(' ')
} catch (error) {
// Extract the cookies specified in cookiesToExtract console.error(error)
cookiesToExtract.forEach(cookieName => { }
const regex = new RegExp(`${cookieName}=(.+?)(;|$)`)
const match = setCookieHeader.find(header => regex.test(header))
if (match) {
const cookieValue = regex.exec(match)[1]
extractedCookies[cookieName] = cookieValue
}
})
return response.json()
})
.then(data => {
if (data.csrfToken) {
X_CSRFTOKEN = data.csrfToken
COOKIE = `JSESSIONID=${extractedCookies.JSESSIONID}; CSESSIONID=${extractedCookies.CSESSIONID}; CSRFSESSION=${extractedCookies.CSRFSESSION}; JSESSIONID=${extractedCookies.JSESSIONID};`
} else {
console.log('csrfToken not found in the response.')
}
})
.catch(error => {
console.error(error)
})
} }
function setHeaders() { async function setHeaders() {
return fetchCookieAndToken().then(() => { await fetchCookieAndToken()
return {
X_CSRFTOKEN: X_CSRFTOKEN, return { X_CSRFTOKEN, Cookie }
'Content-Type': 'application/json', }
Cookie: COOKIE
}
})
}
+1 -1
View File
@@ -14,7 +14,7 @@ module.exports = {
process.env.CLANG ? `--lang=${process.env.CLANG}` : '' process.env.CLANG ? `--lang=${process.env.CLANG}` : ''
} --output=public/guide.xml` } --output=public/guide.xml`
: `npm run grab -- --gzip --channels=channels.xml --output=public/guide.xml`, : `npm run grab -- --gzip --channels=channels.xml --output=public/guide.xml`,
cron_restart: process.env.CRON || null, cron_restart: process.env.CRON_SCHEDULE || null,
instances: 1, instances: 1,
watch: false, watch: false,
autorestart: false autorestart: false
+3 -1
View File
@@ -6,6 +6,7 @@ for arg in "$@"; do
chron-schedule=*) chron_schedule="${arg#*=}" ;; chron-schedule=*) chron_schedule="${arg#*=}" ;;
work-dir=*) work_dir="${arg#*=}" ;; work-dir=*) work_dir="${arg#*=}" ;;
days=*) days="${arg#*=}" ;; days=*) days="${arg#*=}" ;;
delay=*) delay="${arg#*=}" ;;
max_connections=*) max_connections="${arg#*=}" ;; max_connections=*) max_connections="${arg#*=}" ;;
enable_fixes=*) enable_fixes="${arg#*=}" ;; enable_fixes=*) enable_fixes="${arg#*=}" ;;
api_url=*) api_url="${arg#*=}" ;; api_url=*) api_url="${arg#*=}" ;;
@@ -16,6 +17,7 @@ echo "chron_schedule : ${chron_schedule}"
cd $work_dir cd $work_dir
echo "working dir : " $(pwd) echo "working dir : " $(pwd)
echo "days : ${days}" echo "days : ${days}"
echo "delay : ${delay}"
echo "max_connections : ${max_connections}" echo "max_connections : ${max_connections}"
echo "enable_fixes : ${enable_fixes}" echo "enable_fixes : ${enable_fixes}"
echo "api url : ${api_url}" echo "api url : ${api_url}"
@@ -27,4 +29,4 @@ fi
sed -i -E "s/(https:\x2f\x2fiptv-org.github.io\x2fapi$\123filename\125)/$api_url$\123filename\125/g" $work_dir/scripts/core/apiClient.ts sed -i -E "s/(https:\x2f\x2fiptv-org.github.io\x2fapi$\123filename\125)/$api_url$\123filename\125/g" $work_dir/scripts/core/apiClient.ts
ln -s $work_dir/guide.xml /public/guide.xml ln -s $work_dir/guide.xml /public/guide.xml
ln -s $work_dir/guide.xml.gz /public/guide.xml.gz ln -s $work_dir/guide.xml.gz /public/guide.xml.gz
pm2-runtime pm2.config.js --name epg --node-args="--no-autorestart --cron-restart="$chron_schedule" --maxConnections=$max_connections --days=$days" pm2-runtime pm2.config.js --name epg --node-args="--no-autorestart --cron-restart="$chron_schedule" --maxConnections=$max_connections --days=$days --delay=$delay"