Update Init

This commit is contained in:
CyberPoison
2026-01-23 18:28:27 +01:00
parent 71792ab5e4
commit 57a82236f5
3 changed files with 94 additions and 123 deletions

File diff suppressed because one or more lines are too long

View File

@@ -3,65 +3,55 @@ const { DateTime } = require('luxon')
module.exports = { module.exports = {
site: 'meo.pt', site: 'meo.pt',
days: 2, days: 2,
url: 'https://authservice.apps.meo.pt/Services/GridTv/GridTvMng.svc/getProgramsFromChannels', url: function ({ channel, date }) {
return `https://meogouser.apps.meo.pt/Services/GridTv/GridTv.svc/GetLiveChannelProgramsByDate?callLetter=${channel.site_id}&date=${date.format('YYYY-MM-DD')}&userAgent=IPTV_OFR_GTV`
},
request: { request: {
method: 'POST', method: 'GET',
headers: { headers: {
Origin: 'https://www.meo.pt', 'accept': '*/*',
'User-Agent': 'Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; en-US Trident/4.0)' 'accept-language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7,pt;q=0.6,cs;q=0.5',
}, 'cache-control': 'no-cache',
data: function ({ channel, date }) { 'origin': 'https://www.meo.pt',
return { 'pragma': 'no-cache',
service: 'channelsguide', 'priority': 'u=1, i',
channels: [channel.site_id], 'referer': 'https://www.meo.pt/',
dateStart: date.format('YYYY-MM-DDT00:00:00-00:00'), 'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"',
dateEnd: date.add(1, 'd').format('YYYY-MM-DDT00:00:00-00:00'), 'sec-ch-ua-mobile': '?1',
accountID: '' 'sec-ch-ua-platform': '"Android"',
} 'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-site',
'user-agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Mobile Safari/537.36'
} }
}, },
async parser({ content }) { async parser({ content, channel }) {
const axios = require('axios')
let programs = [] let programs = []
const items = parseItems(content) const items = parseItems(content)
if (!items.length) return programs if (!items.length) return programs
// simple per-run in-memory cache
const detailsCache = new Map()
for (const item of items) { for (const item of items) {
const start = parseStart(item) const start = DateTime.fromISO(item.StartDate, { zone: 'Europe/Lisbon' }).toUTC()
let stop = parseStop(item) const stop = DateTime.fromISO(item.EndDate, { zone: 'Europe/Lisbon' }).toUTC()
if (stop < start) {
stop = stop.plus({ days: 1 })
}
let description = ''
let image = ''
const programID = item.uniqueId || null
if (programID) {
let details = detailsCache.get(programID)
if (!details) {
details = await fetchProgramDetails(programID, axios).catch(() => null)
if (details) detailsCache.set(programID, details)
}
if (details) {
description = details.description || description
image = details.image || image
}
}
const prog = { const prog = {
title: item.name || 'Sem título', title: item.Title || 'Sem título',
start, start,
stop stop
} }
if (description) prog.description = description
if (image) { if (item.Synopsis) {
prog.description = item.Synopsis
}
// Construct image URL using the same logic as before if possible
if (item.Title && channel.site_id) {
const encodedTitle = encodeURIComponent(item.Title)
const image = `https://proxycache.online.meo.pt/eemstb/ImageHandler.ashx?evTitle=${encodedTitle}&chCallLetter=${channel.site_id}&profile=16_9&width=600`
prog.icon = { src: image } prog.icon = { src: image }
prog.image = image prog.image = image
} }
programs.push(prog) programs.push(prog)
} }
@@ -70,84 +60,45 @@ module.exports = {
async channels() { async channels() {
const axios = require('axios') const axios = require('axios')
const data = await axios const data = await axios
.post('https://authservice.apps.meo.pt/Services/GridTv/GridTvMng.svc/getGridAnon', null, { .get('https://meogouser.apps.meo.pt/Services/GridTv/GridTv.svc/GetContentsForChannels?userAgent=IPTV_OFR_GTV', {
headers: { headers: {
Origin: 'https://www.meo.pt', 'accept': '*/*',
'User-Agent': 'Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; en-US Trident/4.0)' 'accept-language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7,pt;q=0.6,cs;q=0.5',
'cache-control': 'no-cache',
'origin': 'https://www.meo.pt',
'pragma': 'no-cache',
'priority': 'u=1, i',
'referer': 'https://www.meo.pt/',
'sec-ch-ua': '"Not(A:Brand";v="8", "Chromium";v="144", "Google Chrome";v="144"',
'sec-ch-ua-mobile': '?1',
'sec-ch-ua-platform': '"Android"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-site',
'user-agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Mobile Safari/537.36'
} }
}) })
.then(r => r.data) .then(r => r.data)
.catch(console.log) .catch(console.log)
// channel logo at data.d.channels.logo return data.Result
return data.d.channels
.map(item => { .map(item => {
return { return {
lang: 'pt', lang: 'pt',
site_id: item.sigla, site_id: item.CallLetter,
name: item.name name: item.Title
} }
}) })
.filter(channel => channel.site_id) .filter(channel => channel.site_id)
} }
} }
function parseStart(item) {
return DateTime.fromFormat(`${item.date} ${item.timeIni}`, 'd-M-yyyy HH:mm', {
zone: 'Europe/Lisbon'
}).toUTC()
}
function parseStop(item) {
return DateTime.fromFormat(`${item.date} ${item.timeEnd}`, 'd-M-yyyy HH:mm', {
zone: 'Europe/Lisbon'
}).toUTC()
}
function parseItems(content) { function parseItems(content) {
if (!content) return [] if (!content) return []
const data = JSON.parse(content)
const programs = data?.d?.channels?.[0]?.programs
return Array.isArray(programs) ? programs : []
}
async function fetchProgramDetails(programID, axiosInstance) {
try { try {
const response = await axiosInstance.post( const data = typeof content === 'string' ? JSON.parse(content) : content
'https://authservice.apps.meo.pt/Services/GridTv/GridTvMng.svc/getProgramDetails', return Array.isArray(data.Result) ? data.Result : []
{
service: 'programdetail',
programID: String(programID),
accountID: ''
},
{
headers: {
Origin: 'https://www.meo.pt',
'User-Agent': 'Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; en-US Trident/4.0)'
},
timeout: 10000
}
)
const data = response.data
// Response structure has program data directly in data.d
const program = data?.d
if (!program || typeof program !== 'object') return null
// Build image URL using MEO's image handler
let image = null
if (program.progName && program.channelSigla) {
const encodedTitle = encodeURIComponent(program.progName)
image = `https://proxycache.online.meo.pt/eemstb/ImageHandler.ashx?evTitle=${encodedTitle}&chCallLetter=${program.channelSigla}&profile=16_9&width=600`
}
const description = program.description || null
return { description, image }
} catch { } catch {
// Silent fail returning null so parser continues return []
return null
} }
} }

View File

@@ -18,37 +18,28 @@ const channel = {
} }
it('can generate valid url', () => { it('can generate valid url', () => {
expect(url).toBe( expect(url({ channel, date })).toBe(
'https://authservice.apps.meo.pt/Services/GridTv/GridTvMng.svc/getProgramsFromChannels' 'https://meogouser.apps.meo.pt/Services/GridTv/GridTv.svc/GetLiveChannelProgramsByDate?callLetter=RTPM&date=2022-12-02&userAgent=IPTV_OFR_GTV'
) )
}) })
it('can generate valid request method', () => { it('can generate valid request method', () => {
expect(request.method).toBe('POST') expect(request.method).toBe('GET')
}) })
it('can generate valid request headers', () => { it('can generate valid request headers', () => {
expect(request.headers).toMatchObject({ expect(request.headers).toMatchObject({
Origin: 'https://www.meo.pt' 'origin': 'https://www.meo.pt',
}) 'user-agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Mobile Safari/537.36'
})
it('can generate valid request method', () => {
expect(request.data({ channel, date })).toMatchObject({
service: 'channelsguide',
channels: ['RTPM'],
dateStart: '2022-12-02T00:00:00-00:00',
dateEnd: '2022-12-03T00:00:00-00:00',
accountID: ''
}) })
}) })
it('can parse response', async () => { it('can parse response', async () => {
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json')) const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'), 'utf-8')
axios.post.mockResolvedValue({ data: {} }) axios.get.mockResolvedValue({ data: {} })
let results = await parser({ content }) let results = await parser({ content, channel })
results = results.map(p => { results = results.map(p => {
p.start = p.start.toJSON() p.start = p.start.toJSON()
p.stop = p.stop.toJSON() p.stop = p.stop.toJSON()
@@ -56,9 +47,13 @@ it('can parse response', async () => {
}) })
expect(results[0]).toMatchObject({ expect(results[0]).toMatchObject({
start: '2022-12-01T23:35:00.000Z', start: '2026-01-22T23:40:00.000Z',
stop: '2022-12-02T00:17:00.000Z', stop: '2026-01-23T00:04:00.000Z',
title: 'Walker, O Ranger Do Texas T6 - Ep. 14' title: 'Barman - Ep. 4',
description: "'Barman' é uma série de comédia dramática sobre um jovem comediante que começa a trabalhar como Barman porque precisa de arranjar dinheiro depressa, pelo caminho é obrigado a lidar com a vida noturna e conciliar duas realidades diferentes.",
icon: {
src: 'https://proxycache.online.meo.pt/eemstb/ImageHandler.ashx?evTitle=Barman%20-%20Ep.%204&chCallLetter=RTPM&profile=16_9&width=600'
}
}) })
}) })