Compare commits
31 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ab9a8d7160 | ||
|
e080a976a6 | ||
|
252a7b23aa | ||
|
0a4d9c4452 | ||
|
6939823646 | ||
|
60503e87ed | ||
|
2f732b6dcc | ||
|
1f999d1c3c | ||
|
3f5b36a878 | ||
|
f1c905f5dd | ||
|
d83069f300 | ||
|
092ad56487 | ||
|
cfedd1e091 | ||
|
7bf14e4f10 | ||
|
1521795dd8 | ||
|
645569dc97 | ||
|
113b6b678e | ||
|
512d8257fd | ||
|
db7680861e | ||
|
8623771537 | ||
|
4199210593 | ||
|
3c9432d1f3 | ||
|
fa76db456f | ||
|
b434ce505b | ||
|
2d3ffcbd42 | ||
|
c4c0bf5270 | ||
|
8736114213 | ||
|
075ac41e2a | ||
|
c7dc12f4bb | ||
|
3a7f23577a | ||
|
02725264fd |
@ -38,6 +38,11 @@ jobs:
|
|||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
|
labels: |
|
||||||
|
org.opencontainers.image.title=epg-info
|
||||||
|
org.opencontainers.image.description=Docker image of https://github.com/iptv-org/epg
|
||||||
|
org.opencontainers.image.authors=Davidquinonescl
|
||||||
|
org.opencontainers.image.documentation=https://git.claeyscloud.com/david/epg-info-docker/src/branch/main/README.md
|
||||||
images: |
|
images: |
|
||||||
davidquinonescl/epg-info
|
davidquinonescl/epg-info
|
||||||
git.claeyscloud.com/david/epg-info
|
git.claeyscloud.com/david/epg-info
|
||||||
|
5
.github/workflows/action.yml
vendored
5
.github/workflows/action.yml
vendored
@ -24,6 +24,11 @@ jobs:
|
|||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
with:
|
with:
|
||||||
|
labels: |
|
||||||
|
org.opencontainers.image.title=epg-info
|
||||||
|
org.opencontainers.image.description=Docker image of https://github.com/iptv-org/epg
|
||||||
|
org.opencontainers.image.authors=Davidquinonescl
|
||||||
|
org.opencontainers.image.documentation=https://github.com/davidclaeysquinones/epg-info-docker/blob/main/README.md
|
||||||
images: ghcr.io/davidclaeysquinones/epg-info
|
images: ghcr.io/davidclaeysquinones/epg-info
|
||||||
tags: |
|
tags: |
|
||||||
type=semver,pattern={{raw}}
|
type=semver,pattern={{raw}}
|
||||||
|
12
Dockerfile
12
Dockerfile
@ -4,6 +4,7 @@ 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
|
||||||
@ -23,7 +24,8 @@ RUN apk update \
|
|||||||
&& 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 .eslintrc.json \
|
&& rm -rf .sites \
|
||||||
|
&& rm -rf .husky \
|
||||||
&& rm -rf .github \
|
&& rm -rf .github \
|
||||||
&& rm -rf .git \
|
&& rm -rf .git \
|
||||||
&& rm .gitignore \
|
&& rm .gitignore \
|
||||||
@ -41,14 +43,20 @@ RUN apk update \
|
|||||||
&& rm -rf node_modules/**/.github \
|
&& rm -rf node_modules/**/.github \
|
||||||
&& rm -rf node_modules/**/docs \
|
&& rm -rf node_modules/**/docs \
|
||||||
&& rm -rf node_modules/**/LICENSE \
|
&& rm -rf node_modules/**/LICENSE \
|
||||||
|
&& rm -rf node_modules/**/license \
|
||||||
&& rm -rf node_modules/**/**.md \
|
&& rm -rf node_modules/**/**.md \
|
||||||
|
&& rm -rf node_modules/**/**/LICENSE \
|
||||||
|
&& rm -rf node_modules/**/**/license \
|
||||||
|
&& rm -rf node_modules/**/**/.github \
|
||||||
|
&& rm -rf node_modules/**/**/**.md \
|
||||||
&& ln -s /config/channels.xml $(echo "${WORKDIR}/channels.xml") \
|
&& ln -s /config/channels.xml $(echo "${WORKDIR}/channels.xml") \
|
||||||
&& mkdir /public
|
&& mkdir /public
|
||||||
COPY start.sh $WORKDIR
|
COPY start.sh $WORKDIR
|
||||||
COPY serve.json $WORKDIR
|
COPY serve.json $WORKDIR
|
||||||
|
COPY pm2.config.js $WORKDIR
|
||||||
RUN chmod +x "$START_SCRIPT" \
|
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
|
53
README.md
53
README.md
@ -41,7 +41,7 @@ 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 | [davidclaeysquinones](https://github.com/davidclaeysquinones) | [PR](https://github.com/iptv-org/epg/pull/2440) pending approval |
|
||||||
| 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 [fd91a9c](https://github.com/iptv-org/epg/commit/8a8262eacb46b2d35df7eb11f46de22263eab053) |
|
| 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) |
|
||||||
| 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) 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) |
|
||||||
| 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 | [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) |
|
||||||
| 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 | [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) |
|
||||||
@ -56,6 +56,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` |
|
||||||
@ -164,3 +165,53 @@ Sometimes a new version of this image will be bound to the same source commit. T
|
|||||||
[12-31-2024 06:25](https://github.com/iptv-org/epg/commit/7e7efaa48717d6b96f6d05aa9cf73271750d788b)
|
[12-31-2024 06:25](https://github.com/iptv-org/epg/commit/7e7efaa48717d6b96f6d05aa9cf73271750d788b)
|
||||||
- 1.0.36
|
- 1.0.36
|
||||||
[12-31-2024 17:32](https://github.com/iptv-org/epg/commit/5ffe285c1e5882e905c5aaee672849f6f89e5cf3)
|
[12-31-2024 17:32](https://github.com/iptv-org/epg/commit/5ffe285c1e5882e905c5aaee672849f6f89e5cf3)
|
||||||
|
- 1.0.37
|
||||||
|
[01-09-2025](https://github.com/iptv-org/epg/commit/8e39af2a4d7c15f442a3e686144278e97151d46e)
|
||||||
|
- 1.0.38
|
||||||
|
[01-13-2025](https://github.com/iptv-org/epg/commit/9a565f16f4016e49d17b762477e0f6d29bb0f970)
|
||||||
|
- 1.0.39
|
||||||
|
[01-14-2025](https://github.com/iptv-org/epg/commit/76df1541d8b0b90533ea74dcbb7815c27425b608)
|
||||||
|
- 1.0.40
|
||||||
|
[01-14-2025](https://github.com/iptv-org/epg/commit/76df1541d8b0b90533ea74dcbb7815c27425b608)<br> Fixes issue with api url
|
||||||
|
- 1.0.41
|
||||||
|
[01-15-2025](https://github.com/iptv-org/epg/commit/65331dff1c6728c3012e314e51d40da85d2d7f3c)
|
||||||
|
- 1.0.42
|
||||||
|
[01-15-2025](https://github.com/iptv-org/epg/commit/5958c77c65a652285da64ad8a77d137306ca46d7)
|
||||||
|
- 1.0.43
|
||||||
|
[01-20-2025](https://github.com/iptv-org/epg/commit/7b2cfba7f5d4df8c01ff74a7c26d7695cb750244)
|
||||||
|
- 1.0.44
|
||||||
|
[01-21-2025](https://github.com/iptv-org/epg/commit/b69d61af5e46cea4f7dcb15a00d897397c23defa)
|
||||||
|
- 1.0.45
|
||||||
|
[01-23-2025](https://github.com/iptv-org/epg/commit/bc4b7fcfd51325cc597ccce13821f355dd0fbc72)
|
||||||
|
- 1.0.46
|
||||||
|
[01-27-2025](https://github.com/iptv-org/epg/commit/a45a346ec83cae3863b8d0e1cbe7abd99d6fef36)
|
||||||
|
- 1.0.47
|
||||||
|
[01-29-2025](https://github.com/iptv-org/epg/commit/106ae083d243df825958dcf4fea1d48d2765cf72)
|
||||||
|
- 1.0.48
|
||||||
|
[01-30-2025](https://github.com/iptv-org/epg/commit/e57dfaff41f498ffbfe79ecadd37f7f254dad0cc)
|
||||||
|
- 1.0.49
|
||||||
|
[02-02-2025](https://github.com/iptv-org/epg/commit/6b45cd9bd60058fdb7b974ad610c2d6565317f3b)
|
||||||
|
- 1.0.50
|
||||||
|
[02-05-2025](https://github.com/iptv-org/epg/commit/7f6849869f7182ddfa1a01b08a160ff8d2129441)
|
||||||
|
- 1.0.51
|
||||||
|
[02-11-2025](https://github.com/iptv-org/epg/commit/6cbe64f2dde47a3eb042cac35932989a7eefb2db)
|
||||||
|
- 1.0.52
|
||||||
|
[02-18-2025](https://github.com/iptv-org/epg/commit/39c4c5143e7cf7591ac49227e73e564be70c7615)
|
||||||
|
- 1.0.53
|
||||||
|
[02-23-2025](https://github.com/iptv-org/epg/commit/2721fe1ba06761fd06799a233dda27af6184fd17)
|
||||||
|
- 1.0.54
|
||||||
|
[03-07-2025](https://github.com/iptv-org/epg/commit/40c9af82d6f7f4e562cd239237fdf46a396d5728)
|
||||||
|
- 1.0.55
|
||||||
|
[03-11-2025](https://github.com/iptv-org/epg/commit/40c9af82d6f7f4e562cd239237fdf46a396d5728)
|
||||||
|
- 1.0.56
|
||||||
|
[03-16-2025](https://github.com/iptv-org/epg/commit/cf82b4089ef00c1fc94b7751652bfa598f8ab06a)
|
||||||
|
- 1.0.57
|
||||||
|
[03-25-2025](https://github.com/iptv-org/epg/commit/138842009bb3f9135430cdc667502ffa51d4a295)
|
||||||
|
- 1.0.58
|
||||||
|
[04-04-2025](https://github.com/iptv-org/epg/commit/4df25c92bcad1e4892640f532eae71cf9f5e7b95)
|
||||||
|
- 1.0.59
|
||||||
|
[04-04-2025](https://github.com/iptv-org/epg/commit/4df25c92bcad1e4892640f532eae71cf9f5e7b95)<br>Includes fixes for new configuration changes
|
||||||
|
- 1.0.60
|
||||||
|
[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,107 +1,60 @@
|
|||||||
const { DateTime } = require('luxon')
|
const axios = require('axios')
|
||||||
|
const cheerio = require('cheerio')
|
||||||
const API_PROGRAM_ENDPOINT = 'https://comunicacion.movistarplus.es'
|
const dayjs = require('dayjs')
|
||||||
const API_IMAGE_ENDPOINT = 'https://www.movistarplus.es/recorte/n/externov';
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'movistarplus.es',
|
site: 'movistarplus.es',
|
||||||
days: 2,
|
days: 2,
|
||||||
url: function ({ channel, date }) {
|
url({ channel, date }) {
|
||||||
return `${API_PROGRAM_ENDPOINT}/wp-admin/admin-ajax.php`
|
return `https://www.movistarplus.es/programacion-tv/${channel.site_id}/${date.format(
|
||||||
|
'YYYY-MM-DD'
|
||||||
|
)}`
|
||||||
},
|
},
|
||||||
request: {
|
parser({ content }) {
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
Origin: API_PROGRAM_ENDPOINT,
|
|
||||||
Referer: `${API_PROGRAM_ENDPOINT}/programacion/`,
|
|
||||||
"Content-Type" : 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
||||||
},
|
|
||||||
data: function ({ channel, date }) {
|
|
||||||
return {
|
|
||||||
action: 'getProgramation',
|
|
||||||
day: date.format('YYYY-MM-DD'),
|
|
||||||
"channels[]": channel.site_id,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
parser({ content, channel, date }) {
|
|
||||||
let programs = []
|
let programs = []
|
||||||
let items = parseItems(content, channel);
|
let items = parseItems(content)
|
||||||
if (!items.length) return programs;
|
if (!items.length) return programs
|
||||||
|
items.forEach(el => {
|
||||||
items.forEach(item => {
|
|
||||||
let startTime = DateTime.fromFormat(
|
|
||||||
`${item.f_evento_rejilla}`,
|
|
||||||
'yyyy-MM-dd HH:mm:ss',
|
|
||||||
{ zone: 'Europe/Madrid' }
|
|
||||||
).toUTC();
|
|
||||||
|
|
||||||
let stopTime = DateTime.fromFormat(
|
|
||||||
`${item.f_fin_evento_rejilla}`,
|
|
||||||
'yyyy-MM-dd HH:mm:ss',
|
|
||||||
{ zone: 'Europe/Madrid' }
|
|
||||||
).toUTC()
|
|
||||||
|
|
||||||
// Adjust stop time if it's on the next day
|
|
||||||
if (stopTime < startTime) {
|
|
||||||
stopTime = stopTime.plus({ days: 1 });
|
|
||||||
}
|
|
||||||
|
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.des_evento_rejilla,
|
title: el.item.name,
|
||||||
icon: parseIcon(item, channel),
|
start: dayjs(el.item.startDate),
|
||||||
category: item.des_genero,
|
stop: dayjs(el.item.endDate)
|
||||||
start: startTime,
|
|
||||||
stop: stopTime,
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const axios = require('axios')
|
const html = await axios
|
||||||
//const dayjs = require('dayjs')
|
.get('https://www.movistarplus.es/programacion-tv')
|
||||||
const data = await axios
|
|
||||||
.post(`${API_PROGRAM_ENDPOINT}/wp-admin/admin-ajax.php`,
|
|
||||||
{
|
|
||||||
action: 'getChannels',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Origin: API_PROGRAM_ENDPOINT,
|
|
||||||
Referer: `${API_PROGRAM_ENDPOINT}/programacion/`,
|
|
||||||
"Content-Type" : 'application/x-www-form-urlencoded; charset=UTF-8',
|
|
||||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36 Edg/79.0.309.71'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
return Object.values(data).map(item => {
|
const $ = cheerio.load(html)
|
||||||
|
let scheme = $('script:contains(ItemList)').html()
|
||||||
|
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',
|
||||||
site_id: item.cod_cadena_tv,
|
name: el.item.name,
|
||||||
name: item.des_cadena_tv
|
site_id
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseItems(content) {
|
||||||
|
try {
|
||||||
|
const $ = cheerio.load(content)
|
||||||
|
let scheme = $('script:contains("@type": "ItemList")').html()
|
||||||
|
scheme = JSON.parse(scheme)
|
||||||
|
if (!scheme || !Array.isArray(scheme.itemListElement)) return []
|
||||||
|
|
||||||
function parseIcon(item, channel) {
|
return scheme.itemListElement
|
||||||
if(item.cod_elemento_emision)
|
} catch {
|
||||||
{
|
return []
|
||||||
return `${API_IMAGE_ENDPOINT}/M${channel.site_id}P${item.cod_elemento_emision}`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseItems(content, channel) {
|
|
||||||
const json = typeof content === 'string' ? JSON.parse(content) : content;
|
|
||||||
const data = json.channelsProgram;
|
|
||||||
|
|
||||||
if (data.length !== 1) return [];
|
|
||||||
return data[0];
|
|
||||||
}
|
}
|
@ -1,11 +1,15 @@
|
|||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const axios = require('axios')
|
const doFetch = require('@ntlab/sfetch')
|
||||||
|
const debug = require('debug')('site:orangetv.orange.es')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
|
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/Smartphone_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_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'
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@ -13,53 +17,39 @@ module.exports = {
|
|||||||
days: 2,
|
days: 2,
|
||||||
request: {
|
request: {
|
||||||
cache: {
|
cache: {
|
||||||
ttl: 60 * 60 * 1000 // 1 hour
|
ttl: 24 * 60 * 60 * 1000 // 1 day
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
url({ date }) {
|
url({ date, segment = 1 }) {
|
||||||
return `${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_1.json`
|
return `${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_${segment}.json`
|
||||||
},
|
},
|
||||||
async parser({ content, channel, date }) {
|
async parser({ content, channel, date }) {
|
||||||
let programs = []
|
const programs = []
|
||||||
let items = parseItems(content, channel)
|
const items = parseItems(content, channel)
|
||||||
if (!items.length) return programs
|
if (items.length) {
|
||||||
|
const queues = [
|
||||||
const promises = [
|
module.exports.url({ date, segment: 2 }),
|
||||||
axios.get(
|
module.exports.url({ date, segment: 3 })
|
||||||
`${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_1.json`,
|
|
||||||
),
|
|
||||||
axios.get(
|
|
||||||
`${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_2.json`,
|
|
||||||
),
|
|
||||||
axios.get(
|
|
||||||
`${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_3.json`,
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
await doFetch(queues, (url, res) => {
|
||||||
await Promise.allSettled(promises)
|
items.push(...parseItems(res, channel))
|
||||||
.then(results => {
|
|
||||||
results.forEach(r => {
|
|
||||||
if (r.status === 'fulfilled') {
|
|
||||||
const parsed = parseItems(r.value.data, channel)
|
|
||||||
|
|
||||||
items = items.filter((item, index) => items.findIndex(oi => oi.id === item.id) === index).concat(parsed)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
})
|
programs.push(
|
||||||
.catch(console.error)
|
...items.map(item => {
|
||||||
|
return {
|
||||||
items.forEach(item => {
|
|
||||||
programs.push({
|
|
||||||
title: item.name,
|
title: item.name,
|
||||||
|
sub_title: item.seriesName,
|
||||||
description: item.description,
|
description: item.description,
|
||||||
category: parseGenres(item),
|
category: parseGenres(item),
|
||||||
season: item.seriesSeason || null,
|
season: item.seriesSeason ? parseInt(item.seriesSeason) : null,
|
||||||
episode: item.episodeId || null,
|
episode: item.episodeId ? parseInt(item.episodeId) : null,
|
||||||
icon: parseIcon(item),
|
icon: parseIcon(item),
|
||||||
start: dayjs.utc(item.startDate) || null,
|
start: dayjs.utc(item.startDate),
|
||||||
stop: dayjs.utc(item.endDate) || null,
|
stop: dayjs.utc(item.endDate)
|
||||||
})
|
}
|
||||||
})
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
@ -68,7 +58,8 @@ module.exports = {
|
|||||||
const data = await axios
|
const data = await axios
|
||||||
.get(API_CHANNEL_ENDPOINT)
|
.get(API_CHANNEL_ENDPOINT)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.error)
|
||||||
|
|
||||||
return data.response.map(item => {
|
return data.response.map(item => {
|
||||||
return {
|
return {
|
||||||
lang: 'es',
|
lang: 'es',
|
||||||
@ -80,34 +71,29 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function parseIcon(item) {
|
function parseIcon(item) {
|
||||||
|
if (item.attachments.length) {
|
||||||
if(item.attachments.length > 0){
|
const cover = item.attachments.find(i => i.name.match(/cover/i))
|
||||||
const cover = item.attachments.find(i => i.name === "COVER" || i.name === "cover")
|
if (cover) {
|
||||||
|
return `${API_IMAGE_ENDPOINT}${cover.value}`
|
||||||
if(cover)
|
|
||||||
{
|
|
||||||
return `${API_IMAGE_ENDPOINT}${cover.value}`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ''
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseGenres(item) {
|
function parseGenres(item) {
|
||||||
return item.genres.map(i => i.name);
|
return item.genres.map(i => i.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, channel) {
|
function parseItems(content, channel) {
|
||||||
const json = typeof content === 'string' ? JSON.parse(content) : Array.isArray(content) ? content : []
|
const result = []
|
||||||
|
const json =
|
||||||
if (!Array.isArray(json)) {
|
typeof content === 'string' ? JSON.parse(content) : Array.isArray(content) ? content : []
|
||||||
return [];
|
if (Array.isArray(json)) {
|
||||||
|
json
|
||||||
|
.filter(i => i.channelExternalId === channel.site_id)
|
||||||
|
.forEach(i => {
|
||||||
|
result.push(...i.programs)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const channelData = json.find(i => i.channelExternalId == channel.site_id);
|
return result
|
||||||
|
|
||||||
if(!channelData)
|
|
||||||
return [];
|
|
||||||
|
|
||||||
return channelData.programs;
|
|
||||||
}
|
}
|
@ -2,26 +2,17 @@
|
|||||||
|
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
|
||||||
|
|
||||||
let apiVersion
|
let apiVersion
|
||||||
|
|
||||||
dayjs.extend(utc)
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'pickx.be',
|
site: 'pickx.be',
|
||||||
days: 2,
|
days: 2,
|
||||||
setApiVersion: function (version) {
|
async url({ channel, date }) {
|
||||||
apiVersion = version
|
|
||||||
},
|
|
||||||
getApiVersion: function () {
|
|
||||||
return apiVersion
|
|
||||||
},
|
|
||||||
fetchApiVersion: fetchApiVersion,
|
|
||||||
url: async function ({ channel, date }) {
|
|
||||||
if (!apiVersion) {
|
if (!apiVersion) {
|
||||||
await fetchApiVersion()
|
await fetchApiVersion()
|
||||||
}
|
}
|
||||||
|
|
||||||
return `https://px-epg.azureedge.net/airings/${apiVersion}/${date.format(
|
return `https://px-epg.azureedge.net/airings/${apiVersion}/${date.format(
|
||||||
'YYYY-MM-DD'
|
'YYYY-MM-DD'
|
||||||
)}/channel/${channel.site_id}?timezone=Europe%2FBrussels`
|
)}/channel/${channel.site_id}?timezone=Europe%2FBrussels`
|
||||||
@ -51,19 +42,21 @@ module.exports = {
|
|||||||
episode: item.program.episodeNumber,
|
episode: item.program.episodeNumber,
|
||||||
actors: item.program.actors,
|
actors: item.program.actors,
|
||||||
director: item.program.director ? [item.program.director] : null,
|
director: item.program.director ? [item.program.director] : null,
|
||||||
start: dayjs.utc(item.programScheduleStart),
|
start: dayjs(item.programScheduleStart),
|
||||||
stop: dayjs.utc(item.programScheduleEnd)
|
stop: dayjs(item.programScheduleEnd)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels({ lang = '' }) {
|
async channels() {
|
||||||
|
let channels = []
|
||||||
|
|
||||||
const query = {
|
const query = {
|
||||||
operationName: 'getChannels',
|
operationName: 'getChannels',
|
||||||
variables: {
|
variables: {
|
||||||
language: lang,
|
language: 'fr',
|
||||||
queryParams: {},
|
queryParams: {},
|
||||||
id: '0',
|
id: '0',
|
||||||
params: {
|
params: {
|
||||||
@ -73,74 +66,37 @@ module.exports = {
|
|||||||
query: `query getChannels($language: String!, $queryParams: ChannelQueryParams, $id: String, $params: ChannelParams) {
|
query: `query getChannels($language: String!, $queryParams: ChannelQueryParams, $id: String, $params: ChannelParams) {
|
||||||
channels(language: $language, queryParams: $queryParams, id: $id, params: $params) {
|
channels(language: $language, queryParams: $queryParams, id: $id, params: $params) {
|
||||||
id
|
id
|
||||||
channelReferenceNumber
|
|
||||||
name
|
name
|
||||||
callLetter
|
|
||||||
number
|
|
||||||
logo {
|
|
||||||
key
|
|
||||||
url
|
|
||||||
__typename
|
|
||||||
}
|
|
||||||
language
|
language
|
||||||
hd
|
|
||||||
radio
|
radio
|
||||||
replayable
|
|
||||||
ottReplayable
|
|
||||||
playable
|
|
||||||
ottPlayable
|
|
||||||
recordable
|
|
||||||
subscribed
|
|
||||||
cloudRecordable
|
|
||||||
catchUpWindowInHours
|
|
||||||
isOttNPVREnabled
|
|
||||||
ottNPVRStart
|
|
||||||
subscription {
|
|
||||||
channelRef
|
|
||||||
subscribed
|
|
||||||
upselling {
|
|
||||||
upsellable
|
|
||||||
packages
|
|
||||||
__typename
|
|
||||||
}
|
|
||||||
__typename
|
|
||||||
}
|
|
||||||
packages
|
|
||||||
__typename
|
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
}
|
}
|
||||||
const result = await axios
|
|
||||||
|
const data = await axios
|
||||||
.post('https://api.proximusmwc.be/tiams/v3/graphql', query)
|
.post('https://api.proximusmwc.be/tiams/v3/graphql', query)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
return (
|
data.data.channels.forEach(channel => {
|
||||||
result?.data?.channels
|
let lang = channel.language || 'fr'
|
||||||
.filter(
|
if (channel.language === 'ger') lang = 'de'
|
||||||
channel =>
|
|
||||||
!channel.radio && (!lang || channel.language === (lang === 'de' ? 'ger' : lang))
|
channels.push({
|
||||||
)
|
lang,
|
||||||
.map(channel => {
|
|
||||||
return {
|
|
||||||
lang: channel.language === 'ger' ? 'de' : channel.language,
|
|
||||||
site_id: channel.id,
|
site_id: channel.id,
|
||||||
name: channel.name
|
name: channel.name
|
||||||
}
|
})
|
||||||
}) || []
|
})
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function fetchApiVersion() {
|
|
||||||
return new Promise(async (resolve, reject) => {
|
|
||||||
try {
|
|
||||||
// you'll never find what happened here :)
|
|
||||||
// load the pickx page and get the hash from the MWC configuration.
|
|
||||||
// it's not the best way to get the version but it's the only way to get it.
|
|
||||||
|
|
||||||
const hashUrl = 'https://www.pickx.be/nl/televisie/tv-gids';
|
return channels
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const hashData = await axios.get(hashUrl)
|
async function fetchApiVersion() {
|
||||||
|
const hashUrl = 'https://www.pickx.be/nl/televisie/tv-gids'
|
||||||
|
const hashData = await axios
|
||||||
|
.get(hashUrl)
|
||||||
.then(r => {
|
.then(r => {
|
||||||
const re = /"hashes":\["(.*)"\]/
|
const re = /"hashes":\["(.*)"\]/
|
||||||
const match = r.data.match(re)
|
const match = r.data.match(re)
|
||||||
@ -150,10 +106,9 @@ function fetchApiVersion() {
|
|||||||
throw new Error('React app version hash not found')
|
throw new Error('React app version hash not found')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(console.error);
|
.catch(console.error)
|
||||||
|
|
||||||
const versionUrl = `https://www.pickx.be/api/s-${hashData}`
|
const versionUrl = `https://www.pickx.be/api/s-${hashData}`
|
||||||
|
|
||||||
const response = await axios.get(versionUrl, {
|
const response = await axios.get(versionUrl, {
|
||||||
headers: {
|
headers: {
|
||||||
Origin: 'https://www.pickx.be',
|
Origin: 'https://www.pickx.be',
|
||||||
@ -161,6 +116,8 @@ function fetchApiVersion() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
if (response.status === 200) {
|
if (response.status === 200) {
|
||||||
apiVersion = response.data.version
|
apiVersion = response.data.version
|
||||||
resolve()
|
resolve()
|
||||||
|
23
pm2.config.js
Normal file
23
pm2.config.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
module.exports = {
|
||||||
|
apps: [
|
||||||
|
{
|
||||||
|
name: 'serve',
|
||||||
|
script: 'npx serve -- public',
|
||||||
|
instances: 1,
|
||||||
|
watch: false,
|
||||||
|
autorestart: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'grab',
|
||||||
|
script: process.env.SITE
|
||||||
|
? `npm run grab -- --site=${process.env.SITE} ${
|
||||||
|
process.env.CLANG ? `--lang=${process.env.CLANG}` : ''
|
||||||
|
} --output=public/guide.xml`
|
||||||
|
: `npm run grab -- --gzip --channels=channels.xml --output=public/guide.xml`,
|
||||||
|
cron_restart: process.env.CRON || null,
|
||||||
|
instances: 1,
|
||||||
|
watch: false,
|
||||||
|
autorestart: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
9
start.sh
9
start.sh
@ -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}"
|
||||||
@ -24,10 +26,7 @@ if [ "$enable_fixes" = true ] ; then
|
|||||||
cp -R /fixes/* /bin/epg/sites/
|
cp -R /fixes/* /bin/epg/sites/
|
||||||
fi
|
fi
|
||||||
|
|
||||||
sed -i -E "s/(https:\x2f\x2fiptv-org.github.io\x2fapi)/$api_url/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
|
||||||
|
|
||||||
pm2 --name epg start npm -- run serve
|
|
||||||
npm run grab --- --channels=channels.xml --maxConnections=$max_connections --days=$days --gzip
|
|
||||||
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
|
||||||
npm run grab --- --channels=channels.xml --cron="$chron_schedule" --maxConnections=$max_connections --days=$days --gzip
|
pm2-runtime pm2.config.js --name epg --node-args="--no-autorestart --cron-restart="$chron_schedule" --maxConnections=$max_connections --days=$days --delay=$delay"
|
Loading…
x
Reference in New Issue
Block a user