mirror of
https://github.com/iptv-org/epg.git
synced 2025-05-09 08:30:06 -04:00
Merge pull request #2582 from iptv-org/tohenk/fix-orangetv.orange.es
Update orangetv.orange.es guide.
This commit is contained in:
commit
78623a2930
7 changed files with 103 additions and 77 deletions
File diff suppressed because one or more lines are too long
1
sites/orangetv.orange.es/__data__/data1.json
Normal file
1
sites/orangetv.orange.es/__data__/data1.json
Normal file
|
@ -0,0 +1 @@
|
|||
[{"responseElementType": "ProgramList", "channelExternalId": "1010", "programs": [{"startDate": 1736636100000, "endDate": 1736642400000, "attachments": [{"name": "COVER", "value": "/epg/COVER/COVER_2247567.jpg"}], "seriesSeason": "", "episodeId": "", "referenceProgramId": "780906206", "prLevel": 7, "id": 15071377885, "description": "Cassie tenía un brillante futuro por delante. Sin embargo, un incidente provocó que no pudiese cumplir sus sueños. Con el paso del tiempo, tendrá la oportunidad de enmendar los errores del pasado.", "seriesName": "", "genres": [{"externalId": "Cine", "name": "Cine", "id": 128839623}, {"externalId": "Drama", "name": "Drama", "id": 862336010}, {"externalId": "Suspense", "name": "Suspense", "id": 862337524}], "name": "Una joven prometedora", "flags": 14}, {"startDate": 1736642400000, "endDate": 1736648100000, "attachments": [{"name": "COVER", "value": "/epg/COVER/COVER_3522912.jpg"}], "seriesSeason": "", "episodeId": "", "referenceProgramId": "780916568", "prLevel": 1, "id": 15073959700, "description": "César deambula por las calles de Montmarte en París, donde interpreta su música. Un día, se encuentra con Salomé, a quien dejó sin decir palabra un tiempo atrás, y descubre que es padre de una niña.", "seriesName": "", "genres": [{"externalId": "Cine", "name": "Cine", "id": 128839623}, {"externalId": "Comedia", "name": "Comedia", "id": 862331198}], "name": "Una comedia romántica", "flags": 14}, {"startDate": 1736648100000, "endDate": 1736658000000, "attachments": [{"name": "COVER", "value": "/epg/COVER/COVER_3108557.jpg"}], "seriesSeason": "", "episodeId": "", "referenceProgramId": "780871990", "prLevel": 1, "id": 15058341721, "description": "Noticias de los servicios informativos del Canal 24 Horas", "seriesName": "", "genres": [{"externalId": "Programa", "name": "Programa", "id": 9169074970}, {"externalId": "Informativo", "name": "Informativo", "id": 862353792}], "name": "Noticias 24H", "flags": 14}, {"startDate": 1736658000000, "endDate": 1736674500000, "attachments": [{"name": "COVER", "value": "/epg/COVER/COVER_3108557.jpg"}], "seriesSeason": "", "episodeId": "", "referenceProgramId": "780871999", "prLevel": 1, "id": 15058342585, "description": "Noticias de los servicios informativos del Canal 24 Horas", "seriesName": "", "genres": [{"externalId": "Programa", "name": "Programa", "id": 9169074970}, {"externalId": "Informativo", "name": "Informativo", "id": 862353792}], "name": "Noticias 24H", "flags": 14}]}]
|
1
sites/orangetv.orange.es/__data__/data2.json
Normal file
1
sites/orangetv.orange.es/__data__/data2.json
Normal file
|
@ -0,0 +1 @@
|
|||
[{"responseElementType": "ProgramList", "channelExternalId": "1010", "programs": [{"startDate": 1736658000000, "endDate": 1736674500000, "attachments": [{"name": "COVER", "value": "/epg/COVER/COVER_3108557.jpg"}], "seriesSeason": "", "episodeId": "", "referenceProgramId": "780871999", "prLevel": 1, "id": 15058342585, "description": "Noticias de los servicios informativos del Canal 24 Horas", "seriesName": "", "genres": [{"externalId": "Programa", "name": "Programa", "id": 9169074970}, {"externalId": "Informativo", "name": "Informativo", "id": 862353792}], "name": "Noticias 24H", "flags": 14}, {"startDate": 1736674500000, "endDate": 1736676600000, "attachments": [{"name": "COVER", "value": "/epg/COVER/COVER_469238.jpg"}], "seriesSeason": "8", "episodeId": "12", "referenceProgramId": "780872016", "prLevel": 1, "id": 15058342081, "description": "Magacín de salud semanal con la periodista Miriam Moreno con vocación de servicio público. En cada edición ofrece a los espectadores los mejores consejos y cambios en la rutina que pueden servir para llevar una vida más saludable.", "seriesName": "Saber vivir", "genres": [{"externalId": "Programa", "name": "Programa", "id": 9169074970}, {"externalId": "Salud", "name": "Salud", "id": 862341634}], "name": "Saber vivir - T8, E12: Saber vivir", "flags": 15}, {"startDate": 1736676600000, "endDate": 1736679900000, "attachments": [{"name": "COVER", "value": "/epg/COVER/COVER_3261229.jpg"}], "seriesSeason": "17", "episodeId": "8", "referenceProgramId": "780916571", "prLevel": 4, "id": 15073959340, "description": "El programa viaja hasta Bulgaria para conocer su capital Sofía, una de las ciudades más antiguas de Europa y también de las más desconocidas. El espacio de viajes de La 1 también llegará hasta la ciudad medieval de Veliko Tarnovo y Varna.", "seriesName": "Españoles en el mundo", "genres": [{"externalId": "Documental", "name": "Documental", "id": 30089199}, {"externalId": "Viajes y aventuras", "name": "Viajes y aventuras", "id": 9198977905}], "name": "Españoles en el mundo - T17, E08: Sofia, Bulgaria", "flags": 15}, {"startDate": 1736679900000, "endDate": 1736683200000, "attachments": [{"name": "COVER", "value": "/epg/COVER/COVER_3261229.jpg"}], "seriesSeason": "16", "episodeId": "9", "referenceProgramId": "780872010", "prLevel": 4, "id": 15058342225, "description": "El programa cruza el charco para trasladarse a Washington D.C., donde de la mano de seis personas españolas, se descubren lugares como la Casa Blanca, el Capitolio, el Museo de Historia Natural o el Memorial de los Veteranos de Vietnam.", "seriesName": "Españoles en el mundo", "genres": [{"externalId": "Documental", "name": "Documental", "id": 30089199}, {"externalId": "Viajes y aventuras", "name": "Viajes y aventuras", "id": 9198977905}], "name": "Españoles en el mundo - T16, E09: Washington, Distrito Columbia", "flags": 15}, {"startDate": 1736683200000, "endDate": 1736686500000, "attachments": [{"name": "COVER", "value": "/epg/COVER/COVER_3256949.jpg"}], "seriesSeason": "12", "episodeId": "4", "referenceProgramId": "780872014", "prLevel": 4, "id": 15058342441, "description": "Cruzamos el Estrecho de Gibraltar para conocer la huella española en los dominios del antiguo Protectorado en Marruecos.", "seriesName": "Españoles en el mundo", "genres": [{"externalId": "Documental", "name": "Documental", "id": 30089199}, {"externalId": "Viajes y aventuras", "name": "Viajes y aventuras", "id": 9198977905}], "name": "Españoles en el mundo - T12, E04: Tánger, Tetuán y Chauen", "flags": 15}, {"startDate": 1736686500000, "endDate": 1736690400000, "attachments": [{"name": "COVER", "value": "/epg/COVER/COVER_415745.jpg"}], "seriesSeason": "2", "episodeId": "4", "referenceProgramId": "780872015", "prLevel": 1, "id": 15058341793, "description": "Anne Igartiburu y Jordi González hacen un repaso a la actualidad de la crónica social, con los nombres propios más importantes de la semana y estando en directo en todos los acontecimientos y eventos que se produzcan.", "seriesName": "D Corazón", "genres": [{"externalId": "Programa", "name": "Programa", "id": 9169074970}, {"externalId": "Prensa rosa", "name": "Prensa rosa", "id": 11611838587}], "name": "D Corazón - T2, E04: D Corazón", "flags": 15}, {"startDate": 1736690400000, "endDate": 1736694000000, "attachments": [{"name": "COVER", "value": "/epg/COVER/COVER_663318.jpg"}], "seriesSeason": "", "episodeId": "", "referenceProgramId": "780872012", "prLevel": 1, "id": 15058342729, "description": "Informativo de Televisión Española de los fines de semana. Lara Siscar e Igor Gómez se encargan de presentar las últimas noticias de ámbito nacional e internacional surgidas durante el día", "seriesName": "", "genres": [{"externalId": "Programa", "name": "Programa", "id": 9169074970}, {"externalId": "Informativo", "name": "Informativo", "id": 862353792}], "name": "Telediario 1 Fin de Semana", "flags": 14}]}]
|
1
sites/orangetv.orange.es/__data__/data3.json
Normal file
1
sites/orangetv.orange.es/__data__/data3.json
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,9 +1,12 @@
|
|||
const dayjs = require('dayjs')
|
||||
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)
|
||||
|
||||
doFetch.setDebugger(debug)
|
||||
|
||||
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'
|
||||
|
@ -14,49 +17,39 @@ module.exports = {
|
|||
days: 2,
|
||||
request: {
|
||||
cache: {
|
||||
ttl: 60 * 60 * 1000 // 1 hour
|
||||
ttl: 24 * 60 * 60 * 1000 // 1 day
|
||||
}
|
||||
},
|
||||
url({ date }) {
|
||||
return `${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_1.json`
|
||||
url({ date, segment = 1 }) {
|
||||
return `${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_${segment}.json`
|
||||
},
|
||||
async parser({ content, channel, date }) {
|
||||
let programs = []
|
||||
let items = parseItems(content, channel)
|
||||
if (!items.length) return programs
|
||||
|
||||
const promises = [
|
||||
axios.get(`${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 Promise.allSettled(promises)
|
||||
.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)
|
||||
const programs = []
|
||||
const items = parseItems(content, channel)
|
||||
if (items.length) {
|
||||
const queues = [
|
||||
module.exports.url({ date, segment: 2 }),
|
||||
module.exports.url({ date, segment: 3 })
|
||||
]
|
||||
await doFetch(queues, (url, res) => {
|
||||
items.push(...parseItems(res, channel))
|
||||
})
|
||||
programs.push(
|
||||
...items.map(item => {
|
||||
return {
|
||||
title: item.name,
|
||||
sub_title: item.seriesName,
|
||||
description: item.description,
|
||||
category: parseGenres(item),
|
||||
season: item.seriesSeason ? parseInt(item.seriesSeason) : null,
|
||||
episode: item.episodeId ? parseInt(item.episodeId) : null,
|
||||
icon: parseIcon(item),
|
||||
start: dayjs.utc(item.startDate),
|
||||
stop: dayjs.utc(item.endDate)
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(console.error)
|
||||
|
||||
items.forEach(item => {
|
||||
programs.push({
|
||||
title: item.name,
|
||||
description: item.description,
|
||||
category: parseGenres(item),
|
||||
season: item.seriesSeason || null,
|
||||
episode: item.episodeId || null,
|
||||
icon: parseIcon(item),
|
||||
start: dayjs.utc(item.startDate) || null,
|
||||
stop: dayjs.utc(item.endDate) || null
|
||||
})
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return programs
|
||||
},
|
||||
|
@ -65,7 +58,8 @@ module.exports = {
|
|||
const data = await axios
|
||||
.get(API_CHANNEL_ENDPOINT)
|
||||
.then(r => r.data)
|
||||
.catch(console.log)
|
||||
.catch(console.error)
|
||||
|
||||
return data.response.map(item => {
|
||||
return {
|
||||
lang: 'es',
|
||||
|
@ -77,15 +71,12 @@ module.exports = {
|
|||
}
|
||||
|
||||
function parseIcon(item) {
|
||||
if (item.attachments.length > 0) {
|
||||
const cover = item.attachments.find(i => i.name === 'COVER' || i.name === 'cover')
|
||||
|
||||
if (item.attachments.length) {
|
||||
const cover = item.attachments.find(i => i.name.match(/cover/i))
|
||||
if (cover) {
|
||||
return `${API_IMAGE_ENDPOINT}${cover.value}`
|
||||
}
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
function parseGenres(item) {
|
||||
|
@ -93,16 +84,16 @@ function parseGenres(item) {
|
|||
}
|
||||
|
||||
function parseItems(content, channel) {
|
||||
const result = []
|
||||
const json =
|
||||
typeof content === 'string' ? JSON.parse(content) : Array.isArray(content) ? content : []
|
||||
|
||||
if (!Array.isArray(json)) {
|
||||
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)
|
||||
|
||||
if (!channelData) return []
|
||||
|
||||
return channelData.programs
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -1,51 +1,82 @@
|
|||
const { parser, url } = require('./orangetv.orange.es.config.js')
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const axios = require('axios')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(utc)
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
|
||||
const date = dayjs.utc('2024-12-01', 'YYYY-MM-DD').startOf('d')
|
||||
jest.mock('axios')
|
||||
|
||||
const date = dayjs.utc('2025-01-12', 'YYYY-MM-DD').startOf('d')
|
||||
const channel = {
|
||||
site_id: '1010',
|
||||
xmltv_id: 'La1.es'
|
||||
}
|
||||
|
||||
axios.get.mockImplementation(url => {
|
||||
const result = {}
|
||||
const urls = {
|
||||
'https://epg.orangetv.orange.es/epg/Smartphone_Android/1_PRO/20250112_8h_1.json':
|
||||
'data1.json',
|
||||
'https://epg.orangetv.orange.es/epg/Smartphone_Android/1_PRO/20250112_8h_2.json':
|
||||
'data2.json',
|
||||
'https://epg.orangetv.orange.es/epg/Smartphone_Android/1_PRO/20250112_8h_3.json':
|
||||
'data3.json',
|
||||
}
|
||||
if (urls[url] !== undefined) {
|
||||
result.data = fs.readFileSync(path.join(__dirname, '__data__', urls[url])).toString()
|
||||
if (!urls[url].startsWith('data1')) {
|
||||
result.data = JSON.parse(result.data)
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve(result)
|
||||
})
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ date })).toBe(
|
||||
`https://epg.orangetv.orange.es/epg/Smartphone_Android/1_PRO/${date.format(
|
||||
'YYYYMMDD'
|
||||
)}_8h_1.json`
|
||||
'https://epg.orangetv.orange.es/epg/Smartphone_Android/1_PRO/20250112_8h_1.json'
|
||||
)
|
||||
})
|
||||
|
||||
it('can parse response', async () => {
|
||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/data.json')).toString()
|
||||
let results = await parser({ content, channel, date })
|
||||
results = results.map(p => {
|
||||
const content = fs.readFileSync(path.join(__dirname, '__data__', 'data1.json')).toString()
|
||||
const results = (await parser({ content, channel, date })).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
|
||||
expect(results.length).toBe(4)
|
||||
|
||||
var sampleResult = results[0]
|
||||
|
||||
expect(sampleResult).toMatchObject({
|
||||
start: '2024-11-30T22:36:51.000Z',
|
||||
stop: '2024-11-30T23:57:25.000Z',
|
||||
category: ['Cine', 'Romance', 'Comedia', 'Comedia Romántica'],
|
||||
expect(results.length).toBe(18)
|
||||
expect(results[0]).toMatchObject({
|
||||
start: '2025-01-11T22:55:00.000Z',
|
||||
stop: '2025-01-12T00:40:00.000Z',
|
||||
title: 'Una joven prometedora',
|
||||
description:
|
||||
'Charlie trabaja como director en una escuela de primaria y goza de una placentera existencia junto a sus amigos. A pesar de ello, no es feliz porque cada vez que se enamora pierde la cordura.',
|
||||
title: 'Loco de amor'
|
||||
'Cassie tenía un brillante futuro por delante. Sin embargo, un incidente provocó que no pudiese cumplir sus sueños. Con el paso del tiempo, tendrá la oportunidad de enmendar los errores del pasado.',
|
||||
category: ['Cine', 'Drama', 'Suspense'],
|
||||
icon: 'https://pc.orangetv.orange.es/pc/api/rtv/v1/images/epg/COVER/COVER_2247567.jpg'
|
||||
})
|
||||
expect(results[17]).toMatchObject({
|
||||
start: '2025-01-12T21:05:00.000Z',
|
||||
stop: '2025-01-12T23:05:00.000Z',
|
||||
title: 'Bake Off: Famosos al horno - T2, E01: Bake Off: Famosos al horno',
|
||||
sub_title: 'Bake Off: Famosos al horno',
|
||||
description:
|
||||
'Nervios y emoción en el debut de los 14 pasteleros de la nueva temporada de Bake off Famosos al horno. En el primer programa hornearán unas galletas dedicadas a sus mascotas y una tradicional tarta de queso.',
|
||||
category: ['Programa', 'Reality'],
|
||||
icon: 'https://pc.orangetv.orange.es/pc/api/rtv/v1/images/epg/COVER/COVER_3520028.jpg',
|
||||
season: 2,
|
||||
episode: 1
|
||||
})
|
||||
})
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const result = parser({
|
||||
it('can handle empty guide', async () => {
|
||||
const result = await parser({
|
||||
date,
|
||||
channel,
|
||||
content: '{}'
|
||||
|
|
|
@ -5,18 +5,20 @@ https://orangetv.orange.es/epg
|
|||
### Download the guide
|
||||
|
||||
```sh
|
||||
npm run grab -- --site=orangetv.orange.es
|
||||
npm run grab --- --site=orangetv.orange.es
|
||||
```
|
||||
|
||||
### Update channel list
|
||||
|
||||
```sh
|
||||
npm run channels:parse -- --config=./sites/orangetv.orange.es/orangetv.orange.es.config.js --output=./sites/orangetv.orange.es/orangetv.orange.es.channels.xml
|
||||
npm run channels:parse --- --config=./sites/orangetv.orange.es/orangetv.orange.es.config.js --output=./sites/orangetv.orange.es/orangetv.orange.es.channels.xml
|
||||
```
|
||||
|
||||
### Test
|
||||
|
||||
npm test -- orangetv.orange.es
|
||||
```sh
|
||||
npm test --- orangetv.orange.es
|
||||
```
|
||||
|
||||
### Todo
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue