diff --git a/sites/xumo.tv/xumo.tv.config.js b/sites/xumo.tv/xumo.tv.config.js new file mode 100644 index 00000000..e523770a --- /dev/null +++ b/sites/xumo.tv/xumo.tv.config.js @@ -0,0 +1,132 @@ +const axios = require('axios') +const dayjs = require('dayjs') + +const API_ENDPOINT = `https://valencia-app-mds.xumo.com/v2` + +const client = axios.create({ + baseURL: API_ENDPOINT, + responseType: 'arraybuffer' +}) + +module.exports = { + site: 'xumo.tv', + request: { + cache: { + ttl: 60 * 60 * 1000 // 1 hour + } + }, + url: function ({ date, channel }) { + const [offset] = channel.site_id.split('#') + + return `${API_ENDPOINT}/epg/10006/${date.format( + 'YYYYMMDD' + )}/0.json?f=asset.title&f=asset.descriptions&limit=50&offset=${offset}` + }, + async parser({ content, channel, date }) { + let programs = [] + let items = parseItems(content, channel) + if (!items.length) return programs + const d = date.format('YYYYMMDD') + const [offset] = channel.site_id.split('#') + const promises = [ + client.get( + `/epg/10006/${d}/1.json?f=asset.title&f=asset.descriptions&limit=50&offset=${offset}` + ), + client.get( + `/epg/10006/${d}/2.json?f=asset.title&f=asset.descriptions&limit=50&offset=${offset}` + ), + client.get( + `/epg/10006/${d}/3.json?f=asset.title&f=asset.descriptions&limit=50&offset=${offset}` + ) + ] + const results = await Promise.allSettled(promises) + results.forEach(r => { + if (r.status === 'fulfilled') { + items = items.concat(parseItems(r.value.data, channel)) + } + }) + + items.forEach(item => { + programs.push({ + title: item.title, + sub_title: item.episodeTitle, + description: parseDescription(item), + start: parseStart(item), + stop: parseStop(item) + }) + }) + + return programs + }, + async channels() { + const channels = await axios + .get( + `https://valencia-app-mds.xumo.com/v2/channels/list/10006.json?sort=hybrid&geoId=unknown` + ) + .then(r => r.data.channel.item) + .catch(console.log) + + const promises = [ + axios.get(`${API_ENDPOINT}/epg/10006/19700101/0.json?limit=50&offset=0`), + axios.get(`${API_ENDPOINT}/epg/10006/19700101/0.json?limit=50&offset=50`), + axios.get(`${API_ENDPOINT}/epg/10006/19700101/0.json?limit=50&offset=100`), + axios.get(`${API_ENDPOINT}/epg/10006/19700101/0.json?limit=50&offset=150`), + axios.get(`${API_ENDPOINT}/epg/10006/19700101/0.json?limit=50&offset=200`), + axios.get(`${API_ENDPOINT}/epg/10006/19700101/0.json?limit=50&offset=250`), + axios.get(`${API_ENDPOINT}/epg/10006/19700101/0.json?limit=50&offset=300`) + ] + + const output = [] + const results = await Promise.allSettled(promises) + results.forEach((r, i) => { + if (r.status !== 'fulfilled') return + + r.value.data.channels.forEach(item => { + const info = channels.find(c => c.guid.value == item.channelId) + + if (!info) { + console.log(item.channelId) + } + + output.push({ + site_id: `${i * 50}#${item.channelId}`, + name: info.title + }) + }) + }) + + return output + } +} + +function parseDescription(item) { + if (!item.descriptions) return null + + return item.descriptions.medium || item.descriptions.small || item.descriptions.tiny +} + +function parseStart(item) { + return dayjs(item.start) +} + +function parseStop(item) { + return dayjs(item.end) +} + +function parseItems(content, channel) { + if (!content) return [] + const [_, channelId] = channel.site_id.split('#') + const data = JSON.parse(content) + if (!data || !Array.isArray(data.channels)) return [] + const channelData = data.channels.find(c => c.channelId == channelId) + if (!channelData || !Array.isArray(channelData.schedule)) return [] + + return channelData.schedule + .map(item => { + const details = data.assets[item.assetId] + if (!details) return null + + return { ...item, ...details } + }) + .filter(Boolean) +}