mirror of
https://github.com/iptv-org/epg
synced 2026-09-09 05:16:51 -04:00
Merge pull request #3203 from luuvasara/fix/dna-fi-grabber
Fix dna.fi guide: full-day results, hard errors on bad responses
This commit is contained in:
@@ -1,20 +1,35 @@
|
|||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
|
const utc = require('dayjs/plugin/utc')
|
||||||
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
|
|
||||||
|
dayjs.extend(utc)
|
||||||
|
dayjs.extend(timezone)
|
||||||
|
|
||||||
|
// The API groups programmes into broadcast days that start at 04:00
|
||||||
|
// Europe/Helsinki. Requesting exactly that window (instead of a fixed
|
||||||
|
// UTC+2 offset) keeps the interval aligned year-round; a misaligned
|
||||||
|
// interval gets answered with a "303 See Other" to a widened window.
|
||||||
|
// Without an explicit `limit` the API paginates 20 items per response,
|
||||||
|
// so only the first ~20 programmes of each day would be returned.
|
||||||
|
const dayStart = date => dayjs.tz(date.format('YYYY-MM-DD'), 'Europe/Helsinki').add(4, 'h')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'dna.fi',
|
site: 'dna.fi',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ date, channel }) {
|
url({ date, channel }) {
|
||||||
const beginTimestamp = date.add(2, 'h').valueOf()
|
const beginTimestamp = dayStart(date).valueOf()
|
||||||
const endTimestamp = date.add(1, 'd').add(2, 'h').subtract(1, 's').valueOf()
|
const endTimestamp = dayStart(date.add(1, 'd')).subtract(1, 's').valueOf()
|
||||||
|
|
||||||
return `https://mts-pro-envoy-vip.dna.fi/hbx/api/pub/xrtv/g/media?q=channel:${channel.site_id}&q=profile:pr&q=start-interval:${beginTimestamp}/${endTimestamp}`
|
return `https://mts-pro-envoy-vip.dna.fi/hbx/api/pub/xrtv/g/media?q=channel:${channel.site_id}&q=profile:pr&q=start-interval:${beginTimestamp}/${endTimestamp}&limit=1000`
|
||||||
},
|
},
|
||||||
parser({ content, date }) {
|
parser({ content }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
let items = parseItems(content, date)
|
let items = parseItems(content)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const data = item?._embedded?.['xrtv:meta']?.data
|
const data = item?._embedded?.['xrtv:meta']?.data
|
||||||
|
if (!data?.start) return
|
||||||
|
|
||||||
programs.push({
|
programs.push({
|
||||||
title: data?.title,
|
title: data?.title,
|
||||||
subtitle: data?.episode_title,
|
subtitle: data?.episode_title,
|
||||||
@@ -27,8 +42,8 @@ module.exports = {
|
|||||||
images: parseImages(item),
|
images: parseImages(item),
|
||||||
directors: parseCast(data, 'director'),
|
directors: parseCast(data, 'director'),
|
||||||
actors: parseCast(data, 'actors'),
|
actors: parseCast(data, 'actors'),
|
||||||
start: dayjs(data?.start),
|
start: dayjs(data.start),
|
||||||
stop: dayjs(data?.end)
|
stop: dayjs(data.end)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -80,20 +95,17 @@ function parseImages(item) {
|
|||||||
return Array.isArray(images) ? images.map(image => image.src) : []
|
return Array.isArray(images) ? images.map(image => image.src) : []
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, date) {
|
function parseItems(content) {
|
||||||
try {
|
if (!content) return []
|
||||||
const data = JSON.parse(content)
|
|
||||||
let items = data?._embedded?.['xrtv:media-item']
|
|
||||||
items = Array.isArray(items) ? items : []
|
|
||||||
items = items.filter(item => {
|
|
||||||
const start = item?._embedded?.['xrtv:meta']?.data?.start
|
|
||||||
if (!start) return false
|
|
||||||
|
|
||||||
return date.isSame(dayjs(start), 'day')
|
// Let malformed content (e.g. an HTML error page) throw, so the grab
|
||||||
})
|
// is reported as an error instead of silently producing no programmes.
|
||||||
|
const data = JSON.parse(content)
|
||||||
return items
|
if (!data?.page) {
|
||||||
} catch {
|
throw new Error('Unexpected response structure')
|
||||||
return []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const items = data?._embedded?.['xrtv:media-item']
|
||||||
|
|
||||||
|
return Array.isArray(items) ? items : []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,14 @@ const channel = {
|
|||||||
|
|
||||||
it('can generate valid url', async () => {
|
it('can generate valid url', async () => {
|
||||||
expect(url({ date, channel })).toBe(
|
expect(url({ date, channel })).toBe(
|
||||||
'https://mts-pro-envoy-vip.dna.fi/hbx/api/pub/xrtv/g/media?q=channel:ch-216356&q=profile:pr&q=start-interval:1736906400000/1736992799000'
|
'https://mts-pro-envoy-vip.dna.fi/hbx/api/pub/xrtv/g/media?q=channel:ch-216356&q=profile:pr&q=start-interval:1736906400000/1736992799000&limit=1000'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('can generate valid url during daylight saving time', async () => {
|
||||||
|
const dstDate = dayjs.utc('2025-07-15', 'YYYY-MM-DD').startOf('d')
|
||||||
|
expect(url({ date: dstDate, channel })).toBe(
|
||||||
|
'https://mts-pro-envoy-vip.dna.fi/hbx/api/pub/xrtv/g/media?q=channel:ch-216356&q=profile:pr&q=start-interval:1752541200000/1752627599000&limit=1000'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -136,3 +143,10 @@ it('can handle empty guide', () => {
|
|||||||
|
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('throws on unexpected response', () => {
|
||||||
|
expect(() => parser({ date, content: '<html>Error</html>' })).toThrow()
|
||||||
|
expect(() => parser({ date, content: '{"message":"Internal Server Error"}' })).toThrow(
|
||||||
|
'Unexpected response structure'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user