const axios = require('axios') const dayjs = require('dayjs') const utc = require('dayjs/plugin/utc') const timezone = require('dayjs/plugin/timezone') const customParseFormat = require('dayjs/plugin/customParseFormat') const uniqBy = require('lodash.uniqby') dayjs.extend(utc) dayjs.extend(timezone) dayjs.extend(customParseFormat) const API_ENDPOINT = 'https://www.dstv.com/umbraco/api/TvGuide' module.exports = { site: 'dstv.com', days: 2, request: { cache: { ttl: 3 * 60 * 60 * 1000, // 3h interpretHeader: false } }, url: function ({ channel, date }) { const [region] = channel.site_id.split('#') const packageName = region === 'nga' ? '&package=DStv%20Premium' : '' return `${API_ENDPOINT}/GetProgrammes?d=${date.format( 'YYYY-MM-DD' )}${packageName}&country=${region}` }, async parser({ content, channel }) { let programs = [] const items = parseItems(content, channel) for (const item of items) { const details = await loadProgramDetails(item) programs.push({ title: item.Title, description: parseDescription(details), image: parseImage(details), category: parseCategory(details), start: parseTime(item.StartTime, channel), stop: parseTime(item.EndTime, channel) }) } return programs }, async channels({ country }) { const countries = { ao: 'ago', bj: 'ben', bw: 'bwa', bf: 'bfa', bi: 'bdi', cm: 'cmr', cv: 'cpv', td: 'tcd', cf: 'caf', km: 'com', cd: 'cod', dj: 'dji', gq: 'gnq', er: 'eri', sz: 'swz', et: 'eth', ga: 'gab', gm: 'gmb', gh: 'gha', gn: 'gin', gw: 'gnb', ci: 'civ', ke: 'ken', lr: 'lbr', mg: 'mdg', mw: 'mwi', ml: 'mli', mr: 'mrt', mu: 'mus', mz: 'moz', na: 'nam', ne: 'ner', ng: 'nga', cg: 'cog', rw: 'rwa', st: 'stp', sn: 'sen', sc: 'syc', sl: 'sle', so: 'som', za: 'zaf', ss: 'ssd', sd: 'sdn', tz: 'tza', tg: 'tgo', ug: 'uga', zm: 'zmb', zw: 'zwe' } const code = countries[country] const data = await axios .get(`${API_ENDPOINT}/GetProgrammes?d=${dayjs().format('YYYY-MM-DD')}&country=${code}`) .then(r => r.data) .catch(console.log) let channels = [] data.Channels.forEach(item => { channels.push({ lang: 'en', site_id: `${code}#${item.Number}`, name: item.Name }) }) return uniqBy(channels, 'site_id') } } function parseTime(time, channel) { const tz = { ago: 'Africa/Luanda', ben: 'Africa/Porto-Novo', bwa: 'Africa/Gaborone', bfa: 'Africa/Ouagadougou', bdi: 'Africa/Bujumbura', cmr: 'Africa/Douala', cpv: 'CVT', tcd: 'Africa/Ndjamena', caf: 'Africa/Bangui', com: 'Indian/Comoro', cod: 'Africa/Kinshasa', dji: 'Africa/Djibouti', gnq: 'Africa/Malabo', eri: 'Africa/Asmara', swz: 'SAST', eth: 'Africa/Addis_Ababa', gap: 'Africa/Libreville', gmb: 'Africa/Banjul', gha: 'Africa/Accra', gin: 'Africa/Conakry', gnb: 'Africa/Bissau', civ: 'Africa/Abidjan', ken: 'Africa/Nairobi', lbr: 'Africa/Monrovia', mdg: 'Indian/Antananarivo', mwi: 'Africa/Blantyre', mli: 'Africa/Bamako', mrt: 'Africa/Nouakchott', mus: 'Indian/Mauritius', moz: 'Africa/Maputo', nam: 'Africa/Windhoek', ner: 'Africa/Niamey', nga: 'Africa/Lagos', cog: 'Africa/Brazzaville', rwa: 'Africa/Kigali', stp: 'Africa/Sao_Tome', sen: 'Africa/Dakar', syc: 'Indian/Mahe', sle: 'Africa/Freetown', som: 'Africa/Mogadishu', zaf: 'Africa/Johannesburg', ssd: 'Africa/Juba', sdn: 'Africa/Khartoum', tza: 'Africa/Dar_es_Salaam', tgo: 'Africa/Lome', uga: 'Africa/Kampala', zmb: 'Africa/Lusaka', zwe: 'Africa/Harare' } const [region] = channel.site_id.split('#') return dayjs.tz(time, 'YYYY-MM-DDTHH:mm:ss', tz[region]) } function parseDescription(details) { return details ? details.Synopsis : null } function parseImage(details) { return details ? details.ThumbnailUri : null } function parseCategory(details) { return details ? details.SubGenres : null } async function loadProgramDetails(item) { const url = `${API_ENDPOINT}/GetProgramme?id=${item.Id}` return axios .get(url) .then(r => r.data) .catch(console.error) } function parseItems(content, channel) { 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.Number === channelId) if (!channelData || !Array.isArray(channelData.Programmes)) return [] return channelData.Programmes }