add custom fixes
This commit is contained in:
parent
f66ad104af
commit
6e16e71ab5
@ -4,12 +4,16 @@ ARG GIT_BRANCH=master
|
|||||||
ENV CRON_SCHEDULE="0 0,12 * * *"
|
ENV CRON_SCHEDULE="0 0,12 * * *"
|
||||||
ENV DAYS=14
|
ENV DAYS=14
|
||||||
ENV MAX_CONNECTIONS=10
|
ENV MAX_CONNECTIONS=10
|
||||||
|
ENV ENABLE_FIXES=false
|
||||||
ARG BIN_FOLDER=/bin
|
ARG BIN_FOLDER=/bin
|
||||||
ARG EPG_FOLDER=epg
|
ARG EPG_FOLDER=epg
|
||||||
|
ARG FIXES_FOLDER_ARG=fixes
|
||||||
ARG START_SCRIPT_ARG=$BIN_FOLDER/$EPG_FOLDER/start.sh
|
ARG START_SCRIPT_ARG=$BIN_FOLDER/$EPG_FOLDER/start.sh
|
||||||
ENV WORKDIR=${BIN_FOLDER}/${EPG_FOLDER}
|
ENV WORKDIR=${BIN_FOLDER}/${EPG_FOLDER}
|
||||||
|
ENV FIXES_FOLDER=$FIXES_FOLDER_ARG
|
||||||
ENV START_SCRIPT=$START_SCRIPT_ARG
|
ENV START_SCRIPT=$START_SCRIPT_ARG
|
||||||
COPY channels.xml /config/channels.xml
|
COPY channels.xml /config/channels.xml
|
||||||
|
ADD $FIXES_FOLDER /fixes
|
||||||
RUN apk update \
|
RUN apk update \
|
||||||
&& apk upgrade --available \
|
&& apk upgrade --available \
|
||||||
&& apk add curl \
|
&& apk add curl \
|
||||||
@ -40,5 +44,6 @@ COPY serve.json $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/*
|
||||||
ENTRYPOINT bash $START_SCRIPT chron-schedule="$CRON_SCHEDULE" work-dir="$WORKDIR" days="$DAYS" max_connections="$MAX_CONNECTIONS"
|
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"
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
74
fixes/movistarplus.es/movistarplus.es.config.js
Normal file
74
fixes/movistarplus.es/movistarplus.es.config.js
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
const { DateTime } = require('luxon')
|
||||||
|
|
||||||
|
const API_PROD_ENDPOINT = 'https://www.movistarplus.es/programacion-tv'
|
||||||
|
const API_IMAGE_ENDPOINT = 'https://www.movistarplus.es/recorte/n/caratulaH/';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
site: 'movistarplus.es',
|
||||||
|
days: 2,
|
||||||
|
url: function ({ date }) {
|
||||||
|
return `${API_PROD_ENDPOINT}/${date.format('YYYY-MM-DD')}?v=json`
|
||||||
|
},
|
||||||
|
parser({ content, channel, date }) {
|
||||||
|
let programs = []
|
||||||
|
let items = parseItems(content, channel)
|
||||||
|
if (!items.length) return programs
|
||||||
|
let guideDate = date
|
||||||
|
|
||||||
|
items.forEach(item => {
|
||||||
|
let startTime = DateTime.fromFormat(
|
||||||
|
`${guideDate.format('YYYY-MM-DD')} ${item.HORA_INICIO}`,
|
||||||
|
'yyyy-MM-dd HH:mm',
|
||||||
|
{
|
||||||
|
zone: 'Europe/Madrid'
|
||||||
|
}
|
||||||
|
).toUTC()
|
||||||
|
let stopTime = DateTime.fromFormat(
|
||||||
|
`${guideDate.format('YYYY-MM-DD')} ${item.HORA_FIN}`,
|
||||||
|
'yyyy-MM-dd HH:mm',
|
||||||
|
{
|
||||||
|
zone: 'Europe/Madrid'
|
||||||
|
}
|
||||||
|
).toUTC()
|
||||||
|
if (stopTime < startTime) {
|
||||||
|
guideDate = guideDate.add(1, 'd')
|
||||||
|
stopTime = stopTime.plus({ days: 1 })
|
||||||
|
}
|
||||||
|
programs.push({
|
||||||
|
title: item.TITULO,
|
||||||
|
icon: parseIcon(item, channel),
|
||||||
|
category: item.GENERO,
|
||||||
|
start: startTime,
|
||||||
|
stop: stopTime
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return programs
|
||||||
|
},
|
||||||
|
async channels() {
|
||||||
|
const axios = require('axios')
|
||||||
|
const dayjs = require('dayjs')
|
||||||
|
const data = await axios
|
||||||
|
.get(`${API_PROD_ENDPOINT}/${dayjs().format('YYYY-MM-DD')}?v=json`)
|
||||||
|
.then(r => r.data)
|
||||||
|
.catch(console.log)
|
||||||
|
|
||||||
|
return Object.values(data.data).map(item => {
|
||||||
|
return {
|
||||||
|
lang: 'es',
|
||||||
|
site_id: item.DATOS_CADENA.CODIGO,
|
||||||
|
name: item.DATOS_CADENA.NOMBRE
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseIcon(item, channel) {
|
||||||
|
return `${API_IMAGE_ENDPOINT}/M${channel.site_id}P${item.ELEMENTO}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseItems(content, channel) {
|
||||||
|
const json = typeof content === 'string' ? JSON.parse(content) : content
|
||||||
|
if (!(`${channel.site_id}-CODE` in json.data)) return []
|
||||||
|
const data = json.data[`${channel.site_id}-CODE`]
|
||||||
|
return data ? data.PROGRAMAS : []
|
||||||
|
}
|
178
fixes/pickx.be/pickx.be.config.js
Normal file
178
fixes/pickx.be/pickx.be.config.js
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
const axios = require('axios')
|
||||||
|
const dayjs = require('dayjs')
|
||||||
|
const utc = require('dayjs/plugin/utc')
|
||||||
|
|
||||||
|
let apiVersion
|
||||||
|
let isApiVersionFetched = false
|
||||||
|
|
||||||
|
;(async () => {
|
||||||
|
try {
|
||||||
|
await fetchApiVersion()
|
||||||
|
isApiVersionFetched = true
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error during script initialization:', error)
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
|
||||||
|
dayjs.extend(utc)
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
site: 'pickx.be',
|
||||||
|
days: 2,
|
||||||
|
apiVersion: function () {
|
||||||
|
return apiVersion
|
||||||
|
},
|
||||||
|
fetchApiVersion: fetchApiVersion, // Export fetchApiVersion
|
||||||
|
url: async function ({ channel, date }) {
|
||||||
|
while (!isApiVersionFetched) {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100)) // Wait for 100 milliseconds
|
||||||
|
}
|
||||||
|
return `https://px-epg.azureedge.net/airings/${apiVersion}/${date.format(
|
||||||
|
'YYYY-MM-DD'
|
||||||
|
)}/channel/${channel.site_id}?timezone=Europe%2FBrussels`
|
||||||
|
},
|
||||||
|
request: {
|
||||||
|
headers: {
|
||||||
|
Origin: 'https://www.pickx.be',
|
||||||
|
Referer: 'https://www.pickx.be/'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parser({ channel, content }) {
|
||||||
|
const programs = []
|
||||||
|
if (content) {
|
||||||
|
const items = JSON.parse(content)
|
||||||
|
items.forEach(item => {
|
||||||
|
programs.push({
|
||||||
|
title: item.program.title,
|
||||||
|
sub_title: item.program.episodeTitle,
|
||||||
|
description: item.program.description,
|
||||||
|
category: item.program.translatedCategory?.[channel.lang]
|
||||||
|
? item.program.translatedCategory[channel.lang]
|
||||||
|
: item.program.category.split('.')[1],
|
||||||
|
image: item.program.posterFileName
|
||||||
|
? `https://experience-cache.proximustv.be/posterserver/poster/EPG/w-166_h-110/${item.program.posterFileName}`
|
||||||
|
: null,
|
||||||
|
season: item.program.seasonNumber,
|
||||||
|
episode: item.program.episodeNumber,
|
||||||
|
actors: item.program.actors,
|
||||||
|
director: item.program.director ? [item.program.director] : null,
|
||||||
|
start: dayjs.utc(item.programScheduleStart),
|
||||||
|
stop: dayjs.utc(item.programScheduleEnd)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return programs
|
||||||
|
},
|
||||||
|
async channels({ lang = '' }) {
|
||||||
|
const query = {
|
||||||
|
operationName: 'getChannels',
|
||||||
|
variables: {
|
||||||
|
language: lang,
|
||||||
|
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
|
||||||
|
channelReferenceNumber
|
||||||
|
name
|
||||||
|
callLetter
|
||||||
|
number
|
||||||
|
logo {
|
||||||
|
key
|
||||||
|
url
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
language
|
||||||
|
hd
|
||||||
|
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
|
||||||
|
.post('https://api.proximusmwc.be/tiams/v2/graphql', query)
|
||||||
|
.then(r => r.data)
|
||||||
|
.catch(console.error)
|
||||||
|
|
||||||
|
return (
|
||||||
|
result?.data?.channels
|
||||||
|
.filter(
|
||||||
|
channel =>
|
||||||
|
!channel.radio && (!lang || channel.language === (lang === 'de' ? 'ger' : lang))
|
||||||
|
)
|
||||||
|
.map(channel => {
|
||||||
|
return {
|
||||||
|
lang: channel.language === 'ger' ? 'de' : channel.language,
|
||||||
|
site_id: channel.id,
|
||||||
|
name: channel.name
|
||||||
|
}
|
||||||
|
}) || []
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function fetchApiVersion() {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
// https://px-epg.azureedge.net/version is deprecated
|
||||||
|
// probably the version url will be changed around over time
|
||||||
|
|
||||||
|
//history of used version urls
|
||||||
|
//const versionUrl = 'https://www.pickx.be/api/s-3b36540f3cef64510112f3f95c2c0cdca321997ed2b1042ad778523235e155eb'
|
||||||
|
//const versionUrl = 'https://www.pickx.be/api/s-671f172425e1bc74cd0440fd67aaa6cbe68b582f3f401186c2f46ae97e80516b'
|
||||||
|
//const versionUrl = 'https://www.pickx.be/api/s-a6b4b4fefaa20e438523a6167e63b8504d96b9df8303473349763c4418cffe30'
|
||||||
|
//const versionUrl = 'https://www.pickx.be/api/s-8546c5fd136241d42aab714d2fe3ccc5671fd899035efae07cd0b8f4eb23994e'
|
||||||
|
//const versionUrl = 'https://www.pickx.be/api/s-64464ad9a3bc117af5dca620027216ecade6a51c230135a0f134c0ee042ff407';
|
||||||
|
//const versionUrl = 'https://www.pickx.be/api/s-626d8fdabfb1d44e5a614cd69f4b45d6843fdb63566fc80ea4f97f40e4ea3152';
|
||||||
|
//const versionUrl = 'https://www.pickx.be/api/s-cefaf96e249e53648c4895c279e7a621233c50b4357d62b0bdf6bff45f31b5c0';
|
||||||
|
//const versionUrl = 'https://www.pickx.be/api/s-7fa35253080e9665f9c7d9d85e707d6fb1d1bf07ede11965e859fcb57c723949';
|
||||||
|
//the new strategy to break the provider is to leave old version url's available and to return invalid results on those endpoints
|
||||||
|
|
||||||
|
const versionUrl = 'https://www.pickx.be/api/s-0e58be3938175b6b900dfb5233bd5cfc0bcf915b633fe57b935f7ce8dbe5f6eb';
|
||||||
|
|
||||||
|
|
||||||
|
const response = await axios.get(versionUrl, {
|
||||||
|
headers: {
|
||||||
|
Origin: 'https://www.pickx.be',
|
||||||
|
Referer: 'https://www.pickx.be/'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response.status === 200) {
|
||||||
|
apiVersion = response.data.version
|
||||||
|
resolve()
|
||||||
|
} else {
|
||||||
|
console.error(`Failed to fetch API version. Status: ${response.status}`)
|
||||||
|
reject(`Failed to fetch API version. Status: ${response.status}`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching API version:', error.message)
|
||||||
|
reject(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
138
fixes/telenet.tv/telenet.tv.config.js
Normal file
138
fixes/telenet.tv/telenet.tv.config.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
const axios = require('axios')
|
||||||
|
const dayjs = require('dayjs')
|
||||||
|
|
||||||
|
const API_STATIC_ENDPOINT = 'https://static.spark.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_IMAGE_ENDPOINT = 'https://staticqbr-prod-be.gnp.cloud.telenet.tv/image-service';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
site: 'telenet.tv',
|
||||||
|
days: 2,
|
||||||
|
request: {
|
||||||
|
cache: {
|
||||||
|
ttl: 60 * 60 * 1000 // 1 hour
|
||||||
|
}
|
||||||
|
},
|
||||||
|
url: function ({ date, channel }) {
|
||||||
|
return `${API_STATIC_ENDPOINT}/${channel.lang}/events/segments/${date.format('YYYYMMDDHHmmss')}`
|
||||||
|
},
|
||||||
|
async parser({ content, channel, date }) {
|
||||||
|
let programs = []
|
||||||
|
let items = parseItems(content, channel)
|
||||||
|
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)
|
||||||
|
.then(results => {
|
||||||
|
results.forEach(r => {
|
||||||
|
if (r.status === 'fulfilled') {
|
||||||
|
const parsed = parseItems(r.value.data, channel)
|
||||||
|
|
||||||
|
items = items.concat(parsed)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(console.error)
|
||||||
|
|
||||||
|
for (let item of items) {
|
||||||
|
const detail = await loadProgramDetails(item, channel)
|
||||||
|
programs.push({
|
||||||
|
title: item.title,
|
||||||
|
icon: parseIcon(item),
|
||||||
|
description: detail.longDescription,
|
||||||
|
category: detail.genres,
|
||||||
|
actors: detail.actors,
|
||||||
|
season: parseSeason(detail),
|
||||||
|
episode: parseEpisode(detail),
|
||||||
|
start: parseStart(item),
|
||||||
|
stop: parseStop(item)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return programs
|
||||||
|
},
|
||||||
|
async channels() {
|
||||||
|
const data = await axios
|
||||||
|
.get(`${API_PROD_ENDPOINT}/channels?cityId=28001&language=en&productClass=Orion-DASH`)
|
||||||
|
.then(r => r.data)
|
||||||
|
.catch(console.log)
|
||||||
|
|
||||||
|
return data.map(item => {
|
||||||
|
return {
|
||||||
|
lang: 'nl',
|
||||||
|
site_id: item.id,
|
||||||
|
name: item.name
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadProgramDetails(item, channel) {
|
||||||
|
if (!item.id) return {}
|
||||||
|
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 || {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseStart(item) {
|
||||||
|
return dayjs.unix(item.startTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseStop(item) {
|
||||||
|
return dayjs.unix(item.endTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseItems(content, channel) {
|
||||||
|
if (!content) return []
|
||||||
|
const data = JSON.parse(content)
|
||||||
|
if (!data || !Array.isArray(data.entries)) return []
|
||||||
|
const channelData = data.entries.find(e => e.channelId === channel.site_id)
|
||||||
|
if (!channelData) return []
|
||||||
|
|
||||||
|
return Array.isArray(channelData.events) ? channelData.events : []
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseSeason(detail) {
|
||||||
|
if (!detail.seasonNumber) return null
|
||||||
|
if (String(detail.seasonNumber).length > 2) return null
|
||||||
|
return detail.seasonNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseEpisode(detail) {
|
||||||
|
if (!detail.episodeNumber) return null
|
||||||
|
if (String(detail.episodeNumber).length > 3) return null
|
||||||
|
return detail.episodeNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseIcon(item) {
|
||||||
|
return `${API_IMAGE_ENDPOINT}/intent/${item.id}/posterTile`;
|
||||||
|
}
|
6
start.sh
6
start.sh
@ -7,6 +7,7 @@ for arg in "$@"; do
|
|||||||
work-dir=*) work_dir="${arg#*=}" ;;
|
work-dir=*) work_dir="${arg#*=}" ;;
|
||||||
days=*) days="${arg#*=}" ;;
|
days=*) days="${arg#*=}" ;;
|
||||||
max_connections=*) max_connections="${arg#*=}" ;;
|
max_connections=*) max_connections="${arg#*=}" ;;
|
||||||
|
enable_fixes=*) enable_fixes="${arg#*=}" ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
@ -15,6 +16,11 @@ cd $work_dir
|
|||||||
echo "working dir : " $(pwd)
|
echo "working dir : " $(pwd)
|
||||||
echo "days : ${days}"
|
echo "days : ${days}"
|
||||||
echo "max_connections : ${max_connections}"
|
echo "max_connections : ${max_connections}"
|
||||||
|
echo "enable_fixes : ${enable_fixes}"
|
||||||
|
|
||||||
|
if [ "$enable_fixes" = true ] ; then
|
||||||
|
cp -R /fixes/* /bin/epg/sites/
|
||||||
|
fi
|
||||||
|
|
||||||
pm2 --name epg start npm -- run serve
|
pm2 --name epg start npm -- run serve
|
||||||
npm run grab -- --channels=channels.xml --maxConnections=$max_connections --days=$days --gzip
|
npm run grab -- --channels=channels.xml --maxConnections=$max_connections --days=$days --gzip
|
||||||
|
Loading…
x
Reference in New Issue
Block a user