Compare commits

...

5 Commits

Author SHA1 Message Date
e7b49898df fixes movistar plus
All checks were successful
Build docker container / build (push) Successful in 4m32s
2024-11-22 20:49:52 +01:00
99c7d0e2c6 change custom fix for movistarplus.es
All checks were successful
Build docker container / build (push) Successful in 4m58s
2024-11-22 15:46:57 +01:00
a46995b833 update readme for fixes autor attributions 2024-11-15 13:37:30 +01:00
1a73bc2b25 add custom fix and credit fix authors properly
All checks were successful
Build docker container / build (push) Successful in 6m20s
2024-11-14 11:29:24 +01:00
f15b5c36da add funding file for github 2024-11-12 16:21:16 +01:00
6 changed files with 297 additions and 45 deletions

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
github: davidclaeysquinones

View File

@ -32,7 +32,19 @@ You can do this by creating a mapping in the `/config` folder.
#### Custom fixes #### Custom fixes
Through the `ENABLE_FIXES` variable custom provider fixes can be applied to the container. Through the `ENABLE_FIXES` variable custom provider fixes can be applied to the container.
By default some fixes are available. If you have suggestions or a problem with them please submit an issue. By default some fixes are available. These fixes have been validated before being added to this repo.
However this option is disabled by default since you might only want to run the unmodified source.
If you have suggestions or a problem with them please submit an issue.
This the list of the provided custom fixes :
| Provider | Author | Status |
|------------------|---------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| movistarplus.es | [davidclaeysquinones](https://github.com/davidclaeysquinones) | [PR](https://github.com/iptv-org/epg/pull/2440) pending approval |
| pickx.be | [davidclaeysquinones](https://github.com/davidclaeysquinones) | [PR](https://github.com/iptv-org/epg/pull/2430) pending approval |
| 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) pending approval |
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>
It is recommended that you take existing provider code as a base for your customisations. It is recommended that you take existing provider code as a base for your customisations.
@ -101,4 +113,8 @@ Sometimes a new version of this image will be bound to the same source commit. T
- 1.0.11 - 1.0.11
[10-14-2024 17:34](https://github.com/iptv-org/epg/commit/7610f7b9f5cc1ccab8d17f3408a95d31b36ace7c) [10-14-2024 17:34](https://github.com/iptv-org/epg/commit/7610f7b9f5cc1ccab8d17f3408a95d31b36ace7c)
- 1.0.12 - 1.0.12
[10-14-2024 17:34](https://github.com/iptv-org/epg/commit/7610f7b9f5cc1ccab8d17f3408a95d31b36ace7c)<br>Fix Pickx.be url [10-14-2024](https://github.com/iptv-org/epg/commit/7610f7b9f5cc1ccab8d17f3408a95d31b36ace7c)<br>Fix Pickx.be url
- 1.0.13
[10-14-2024](https://github.com/iptv-org/epg/commit/7610f7b9f5cc1ccab8d17f3408a95d31b36ace7c)<br>Add custom fix for web.magentatv.de
- 1.0.14
[10-14-2024](https://github.com/iptv-org/epg/commit/7610f7b9f5cc1ccab8d17f3408a95d31b36ace7c)<br>Change fix for movistarplus.es in order t owork with new API

View File

@ -1,74 +1,73 @@
const { DateTime } = require('luxon') const { DateTime } = require('luxon')
const API_PROD_ENDPOINT = 'https://www.movistarplus.es/programacion-tv' const API_PROGRAM_ENDPOINT = 'https://comunicacion.movistarplus.es'
const API_IMAGE_ENDPOINT = 'https://www.movistarplus.es/recorte/n/caratulaH/'; const API_IMAGE_ENDPOINT = 'https://www.movistarplus.es/recorte/n/caratulaH/';
module.exports = { module.exports = {
site: 'movistarplus.es', site: 'movistarplus.es',
days: 2, days: 2,
url: function ({ date }) { url: function ({ channel, date }) {
return `${API_PROD_ENDPOINT}/${date.format('YYYY-MM-DD')}?v=json` return `${API_PROGRAM_ENDPOINT}/wp-admin/admin-ajax.php`
},
request: {
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 }) { parser({ content, channel, date }) {
let programs = [] let programs = []
let items = parseItems(content, channel) let items = parseItems(content, channel);
if (!items.length) return programs if (!items.length) return programs;
let guideDate = date
items.forEach(item => { items.forEach(item => {
let startTime = DateTime.fromFormat( let startTime = DateTime.fromFormat(
`${guideDate.format('YYYY-MM-DD')} ${item.HORA_INICIO}`, `${item.f_evento_rejilla}`,
'yyyy-MM-dd HH:mm', 'yyyy-MM-dd HH:mm:ss',
{ { zone: 'Europe/Madrid' }
zone: 'Europe/Madrid' ).toUTC();
}
).toUTC()
let stopTime = DateTime.fromFormat( let stopTime = DateTime.fromFormat(
`${guideDate.format('YYYY-MM-DD')} ${item.HORA_FIN}`, `${item.f_fin_evento_rejilla}`,
'yyyy-MM-dd HH:mm', 'yyyy-MM-dd HH:mm:ss',
{ { zone: 'Europe/Madrid' }
zone: 'Europe/Madrid'
}
).toUTC() ).toUTC()
// Adjust stop time if it's on the next day
if (stopTime < startTime) { if (stopTime < startTime) {
guideDate = guideDate.add(1, 'd') stopTime = stopTime.plus({ days: 1 });
stopTime = stopTime.plus({ days: 1 })
} }
programs.push({ programs.push({
title: item.TITULO, title: item.des_evento_rejilla,
icon: parseIcon(item, channel), icon: parseIcon(item, channel),
category: item.GENERO, category: item.des_genero,
start: startTime, start: startTime,
stop: stopTime stop: stopTime,
}) })
}) })
return programs 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) { function parseIcon(item, channel) {
return `${API_IMAGE_ENDPOINT}/M${channel.site_id}P${item.ELEMENTO}`; return `${API_IMAGE_ENDPOINT}/M${channel.site_id}P${item.cod_evento_rejilla}`;
} }
function parseItems(content, channel) { function parseItems(content, channel) {
const json = typeof content === 'string' ? JSON.parse(content) : content const json = typeof content === 'string' ? JSON.parse(content) : content;
if (!(`${channel.site_id}-CODE` in json.data)) return [] const data = json.channelsProgram;
const data = json.data[`${channel.site_id}-CODE`]
return data ? data.PROGRAMAS : [] if (data.length !== 1) return [];
return data[0];
} }

View File

@ -1,3 +1,5 @@
// credit for this fix goes to davidclaeysquinones for his PR on https://github.com/iptv-org/epg/pull/2430
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')

View File

@ -1,3 +1,5 @@
// credit for this fix goes to davidclaeysquinones for his PR on https://github.com/iptv-org/epg/pull/2429
const axios = require('axios') const axios = require('axios')
const dayjs = require('dayjs') const dayjs = require('dayjs')

View File

@ -0,0 +1,232 @@
// credit for this fix goes to klausellus-wallace for his PR on https://github.com/iptv-org/epg/pull/2458
const axios = require('axios')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const customParseFormat = require('dayjs/plugin/customParseFormat')
const fetch = require('node-fetch')
const { upperCase } = require('lodash')
let X_CSRFTOKEN
let COOKIE
const cookiesToExtract = ['JSESSIONID', 'CSESSIONID', 'CSRFSESSION']
const extractedCookies = {}
dayjs.extend(utc)
dayjs.extend(customParseFormat)
module.exports = {
site: 'web.magentatv.de',
days: 2,
url: 'https://api.prod.sngtv.magentatv.de/EPG/JSON/PlayBillList',
request: {
method: 'POST',
headers: function () {
return setHeaders()
},
data({ channel, date }) {
return {
count: -1,
isFillProgram: 1,
offset: 0,
properties: [
{
include: 'endtime,genres,id,name,starttime,channelid,pictures,introduce,subName,seasonNum,subNum,cast,country,producedate,externalIds',
name: 'playbill'
}
],
type: 2,
begintime: date.format('YYYYMMDD000000'),
channelid: channel.site_id,
endtime: date.add(1, 'd').format('YYYYMMDD000000')
}
}
},
parser: function ({ content }) {
let programs = []
const items = parseItems(content)
items.forEach(item => {
programs.push({
title: item.name,
description: item.introduce,
image: parseImage(item),
category: parseCategory(item),
start: parseStart(item),
stop: parseStop(item),
sub_title: item.subName,
season: item.seasonNum,
episode: item.subNum,
directors: parseDirectors(item),
producers: parseProducers(item),
adapters: parseAdapters(item),
country: upperCase(item.country),
date: item.producedate,
urls: parseUrls(item)
})
})
return programs
},
async channels() {
const url = 'https://api.prod.sngtv.magentatv.de/EPG/JSON/AllChannel'
const body = {
channelNamespace: 2,
filterlist: [
{
key: 'IsHide',
value: '-1'
}
],
metaDataVer: 'Channel/1.1',
properties: [
{
include: '/channellist/logicalChannel/contentId,/channellist/logicalChannel/name',
name: 'logicalChannel'
}
],
returnSatChannel: 0
}
const params = {
headers: await setHeaders()
}
const data = await axios
.post(url, body, params)
.then(r => r.data)
.catch(console.log)
return data.channellist.map(item => {
return {
lang: 'de',
site_id: item.contentId,
name: item.name
}
})
}
}
function parseCategory(item) {
return item.genres
? item.genres
.replace('und', ',')
.split(',')
.map(i => i.trim())
: []
}
function parseDirectors(item) {
if (!item.cast || !item.cast.director) return [];
return item.cast.director
.replace('und', ',')
.split(',')
.map(i => i.trim());
}
function parseProducers(item) {
if (!item.cast || !item.cast.producer) return [];
return item.cast.producer
.replace('und', ',')
.split(',')
.map(i => i.trim())
}
function parseAdapters(item) {
if (!item.cast || !item.cast.adaptor) return [];
return item.cast.adaptor
.replace('und', ',')
.split(',')
.map(i => i.trim())
}
function parseUrls(item) {
// currently only a imdb id is returned by the api, thus we can construct the url here
if (!item.externalIds) return [];
return JSON.parse(item.externalIds)
.filter(externalId => externalId.type === 'imdb' && externalId.id)
.map(externalId => ({ system: 'imdb', value: `https://www.imdb.com/title/${externalId.id}` }))
}
function parseImage(item) {
if (!Array.isArray(item.pictures) || !item.pictures.length) return null
return item.pictures[0].href
}
function parseStart(item) {
return dayjs.utc(item.starttime, 'YYYY-MM-DD HH:mm:ss')
}
function parseStop(item) {
return dayjs.utc(item.endtime, 'YYYY-MM-DD HH:mm:ss')
}
function parseItems(content) {
const data = JSON.parse(content)
if (!data || !Array.isArray(data.playbilllist)) return []
return data.playbilllist
}
// Function to try to fetch COOKIE and X_CSRFTOKEN
function fetchCookieAndToken() {
return fetch(
'https://api.prod.sngtv.magentatv.de/EPG/JSON/Authenticate?SID=firstup&T=Windows_chrome_118',
{
headers: {
accept: 'application/json, text/javascript, */*; q=0.01',
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-origin',
'x-requested-with': 'XMLHttpRequest',
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'
}
)
.then(response => {
// Check if the response status is OK (2xx)
if (!response.ok) {
throw new Error('HTTP request failed')
}
// Extract the set-cookie header
const setCookieHeader = response.headers.raw()['set-cookie']
// Extract the cookies specified in cookiesToExtract
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() {
return fetchCookieAndToken().then(() => {
return {
X_CSRFTOKEN: X_CSRFTOKEN,
'Content-Type': 'application/json',
Cookie: COOKIE
}
})
}