diff --git a/.github/workflows/dstv.com.yml b/.github/workflows/dstv.com.yml
new file mode 100644
index 00000000..7c94c891
--- /dev/null
+++ b/.github/workflows/dstv.com.yml
@@ -0,0 +1,17 @@
+name: dstv.com
+on:
+ schedule:
+ - cron: '0 0 * * *'
+ workflow_dispatch:
+ workflow_run:
+ workflows: [_trigger]
+ types:
+ - completed
+jobs:
+ load:
+ uses: ./.github/workflows/_load.yml
+ with:
+ site: ${{github.workflow}}
+ secrets:
+ APP_ID: ${{ secrets.APP_ID }}
+ APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }}
diff --git a/sites/dstv.com/dstv.com.config.js b/sites/dstv.com/dstv.com.config.js
new file mode 100644
index 00000000..baca2786
--- /dev/null
+++ b/sites/dstv.com/dstv.com.config.js
@@ -0,0 +1,76 @@
+const axios = require('axios')
+const dayjs = require('dayjs')
+const utc = require('dayjs/plugin/utc')
+
+dayjs.extend(utc)
+
+module.exports = {
+ site: 'dstv.com',
+ url: function ({ channel, date }) {
+ const [region] = channel.site_id.split('#')
+ const packageName = region === 'nga' ? 'DStv%20Premium' : ''
+
+ return `https://www.dstv.com/umbraco/api/TvGuide/GetProgrammes?d=${date.format(
+ 'YYYY-MM-DD'
+ )}&package=${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: details.Synopsis,
+ icon: details.ThumbnailUri,
+ category: details.SubGenres,
+ start: parseStart(item),
+ stop: parseStop(item)
+ })
+ }
+
+ return programs
+ },
+ async channels({ country }) {
+ const data = await axios
+ .get(
+ `https://www.dstv.com/umbraco/api/TvGuide/GetProgrammes?d=2022-03-10&package=DStv%20Premium&country=${country}`
+ )
+ .then(r => r.data)
+ .catch(console.log)
+
+ return data.Channels.map(item => {
+ return {
+ site_id: `${country}#${item.Number}`,
+ name: item.Name
+ }
+ })
+ }
+}
+
+async function loadProgramDetails(item) {
+ const url = `https://www.dstv.com/umbraco/api/TvGuide/GetProgramme?id=${item.Id}`
+
+ return axios
+ .get(url)
+ .then(r => r.data)
+ .catch(console.error)
+}
+
+function parseStart(item) {
+ return dayjs.utc(item.StartTime, 'YYYY-MM-DDTHH:mm:ss')
+}
+
+function parseStop(item) {
+ return dayjs.utc(item.EndTime, 'YYYY-MM-DDTHH:mm:ss')
+}
+
+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
+}
diff --git a/sites/dstv.com/dstv.com.test.js b/sites/dstv.com/dstv.com.test.js
new file mode 100644
index 00000000..16b78ded
--- /dev/null
+++ b/sites/dstv.com/dstv.com.test.js
@@ -0,0 +1,90 @@
+// npm run channels:parse -- --config=./sites/dstv.com/dstv.com.config.js --output=./sites/dstv.com/dstv.com_za.channels.xml --set=country:zaf
+// npm run channels:parse -- --config=./sites/dstv.com/dstv.com.config.js --output=./sites/dstv.com/dstv.com_ng.channels.xml --set=country:nga
+// npx epg-grabber --config=sites/dstv.com/dstv.com.config.js --channels=sites/dstv.com/dstv.com_za.channels.xml --output=guide.xml --days=2
+// npx epg-grabber --config=sites/dstv.com/dstv.com.config.js --channels=sites/dstv.com/dstv.com_ng.channels.xml --output=guide.xml --days=2
+
+const { parser, url } = require('./dstv.com.config.js')
+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)
+
+jest.mock('axios')
+
+const date = dayjs.utc('2022-03-11', 'YYYY-MM-DD').startOf('d')
+const channelZA = {
+ site_id: 'zaf#101',
+ xmltv_id: 'MNetWest.za'
+}
+const channelNG = {
+ site_id: 'nga#101',
+ xmltv_id: 'MNetWest.za'
+}
+
+it('can generate valid url for zaf', () => {
+ expect(url({ channel: channelZA, date })).toBe(
+ 'https://www.dstv.com/umbraco/api/TvGuide/GetProgrammes?d=2022-03-11&package=&country=zaf'
+ )
+})
+
+it('can generate valid url for nga', () => {
+ expect(url({ channel: channelNG, date })).toBe(
+ 'https://www.dstv.com/umbraco/api/TvGuide/GetProgrammes?d=2022-03-11&package=DStv%20Premium&country=nga'
+ )
+})
+
+it('can parse response', done => {
+ const content = `{"Total":4483,"Channels":[{"Number":"214","Tag":"S34","Name":"TENNIS","Programmes":[{"Id":"5c5a56e8-9959-4305-ae2f-7cc32ec39f5f","StartTime":"2022-03-11T09:00:00","EndTime":"2022-03-11T09:15:00","Title":"WTA 1000 HL '22: Indian Wells D2 M1"}]},{"Number":"101","Tag":"HDT","Name":"M-Net HD","Programmes":[{"Id":"a6ada4cd-93df-4eaf-bab4-041e2537ed23","StartTime":"2022-03-11T00:10:00","EndTime":"2022-03-11T00:50:00","Title":"Curb Your Enthusiasm"}]}]}`
+
+ axios.get.mockImplementation(url => {
+ if (
+ url ===
+ 'https://www.dstv.com/umbraco/api/TvGuide/GetProgramme?id=a6ada4cd-93df-4eaf-bab4-041e2537ed23'
+ ) {
+ return Promise.resolve({
+ data: JSON.parse(
+ `{"Id":"a6ada4cd-93df-4eaf-bab4-041e2537ed23","ChannelTag":"HDT","Channel":"M-Net Domestic HD","EventID":"HDT0000109067787","StartTime":"2022-03-11T00:10:00","EndTime":"2022-03-11T00:50:00","Duration":"00:40:00","Language":"eng","Title":"Curb Your Enthusiasm","Synopsis":"'S11/E6 of 10 - Man Fights Tiny Woman'. A general entertainment channel showcasing the best international content, focusing on scripted drama, comedy and talk.","Rating":"16","MainGenres":["Series"],"SubGenres":["Sitcom","Comedy"],"Items":[{"Key":"Year","Value":"2020"},{"Key":"Source Key ID","Value":"100338770"}],"ThumbnailUri":"https://03mcdecdnimagerepository.blob.core.windows.net/epguideimage/img/21893_curb_your_enthusiam169.jpg"}`
+ )
+ })
+ } else {
+ return Promise.resolve({ data: '' })
+ }
+ })
+
+ parser({ content, channel: channelZA })
+ .then(result => {
+ result = result.map(p => {
+ p.start = p.start.toJSON()
+ p.stop = p.stop.toJSON()
+ return p
+ })
+
+ expect(result).toMatchObject([
+ {
+ start: '2022-03-11T00:10:00.000Z',
+ stop: '2022-03-11T00:50:00.000Z',
+ title: 'Curb Your Enthusiasm',
+ description:
+ "'S11/E6 of 10 - Man Fights Tiny Woman'. A general entertainment channel showcasing the best international content, focusing on scripted drama, comedy and talk.",
+ icon: 'https://03mcdecdnimagerepository.blob.core.windows.net/epguideimage/img/21893_curb_your_enthusiam169.jpg',
+ category: ['Sitcom', 'Comedy']
+ }
+ ])
+ done()
+ })
+ .catch(done)
+})
+
+it('can handle empty guide', done => {
+ parser({
+ content: `{"Total":0,"Channels":[]}`,
+ channel: channelZA
+ })
+ .then(result => {
+ expect(result).toMatchObject([])
+ done()
+ })
+ .catch(done)
+})
diff --git a/sites/dstv.com/dstv.com_ng.channels.xml b/sites/dstv.com/dstv.com_ng.channels.xml
new file mode 100644
index 00000000..dc51cb34
--- /dev/null
+++ b/sites/dstv.com/dstv.com_ng.channels.xml
@@ -0,0 +1,154 @@
+
+
+
+ 1 Magic
+ Africa Magic Epic
+ Africa Magic Family
+ Africa Magic Hausa
+ Africa Magic Igbo
+ Africa Magic Showcase HD
+ Africa Magic Urban
+ Africa Magic Yoruba
+ Africanews
+ AFRO Music English
+ AIT
+ Al Jazeera
+ Arewa 24
+ Arise News
+ B4U Movies
+ BBC Brit
+ BBC Lifestyle
+ BBC World News
+ BET
+ Bloomberg Television
+ Boomerang
+ Cartoon Network
+ Cbeebies
+ CBS Justice
+ CBS Reality
+ CCTV 4
+ CCTV Entertainment – Mandarin Entertainment Channel
+ CGTN News
+ CGTN Documentary
+ CGTN French
+ China Movie Channel
+ Citi TV
+ CNBC Africa
+ CNN International
+ Comedy Central
+ Curiosity Channel
+ Da Vinci Kids
+ Day Star
+ Discovery Channel HD
+ Discovery Family HD
+ Disney Channel
+ Disney Junior
+ Dominion TV
+ Dove TV
+ Deutsche Welle
+ E! Entertainment
+ Emmanuel TV
+ ESPN 2 HD
+ ESPN
+ eTV Africa
+ EuroNews German
+ EuroNews
+ EuroNews French
+ Eternal Word Television Network
+ FAITH
+ Food Network
+ Galaxy TV
+ HIP TV
+ The History Channel
+ TV Mundial (P)
+ Discovery ID
+ ISLAM CHANNEL
+ Jim Jam
+ Joy News
+ K24
+ KIX
+ LAGOS TV
+ Lifetime Entertainment
+ Lumen Christi
+ Maisha Magic Bongo
+ Maisha Magic Plus HD
+ Mindset
+ MiTV
+ M-Net Movies 1 West HD
+ M-Net Movies 2
+ M-Net Movies 3
+ M-Net Movies 4
+ M-Net West HD
+ MTV
+ MTV base
+ National Geographic Channel
+ NatGeo Wild
+ NDTV 24x7
+ Newzroom Afrika
+ NHK
+ Nickelodeon
+ NickJr
+ NickTOONS
+ NTA 2
+ NTA I
+ NTA News 24
+ NTA Parliament
+ OGTV
+ ONMAX
+ PBS Kids
+ Phoenix News and Entertainment
+ Cloud Plus
+ Plus TV Africa
+ POP Central
+ RAI International
+ Real Time
+ ROK
+ ROK 2
+ ROK GH
+ RTPi (P)
+ SABC News
+ SBN
+ SILVERBIRD
+ Sky News
+ Sound City
+ Spice TV
+ Star Life
+ Studio Universal HD
+ Sunna TV
+ SS Blitz Nigeria
+ SS Football Nigeria
+ SS Football Plus Nigeria
+ SS Grandstand Nigeria
+ SS LaLiga Nigeria
+ SS Maximo 1 (P)
+ SS Maximo 2 (P)
+ SS Premier League Nigeria
+ SS Tennis Nigeria
+ SS Variety 1 Nigeria
+ SS Variety 2 Nigeria
+ SS Variety 3 Nigeria
+ SS Variety 4 Nigeria
+ TBN
+ TeleMundo
+ Televista
+ Discovery TLC HD
+ TLNovelas
+ TNT Africa
+ Trace Gospel
+ Trace Jama
+ TRACE Muzika
+ TRACE Mziki
+ TRACE Naija
+ Trybe
+ TV5 Monde Afrique
+ TVC News Nigeria
+ Universal TV
+ URBAN TV
+ WAP TV
+ Wasafi TV
+ Wazobia Max
+ WWE Channel
+ Zee World
+ Zhejiang TV
+
+
diff --git a/sites/dstv.com/dstv.com_za.channels.xml b/sites/dstv.com/dstv.com_za.channels.xml
new file mode 100644
index 00000000..7f4dae35
--- /dev/null
+++ b/sites/dstv.com/dstv.com_za.channels.xml
@@ -0,0 +1,150 @@
+
+
+
+ 1 KZN
+ 1 Magic HD
+ Africa Magic Epic
+ Africa Magic Family
+ Africa Magic Urban
+ African Easter
+ Al Jazeera
+ Bay TV
+ BBC Brit HD
+ BBC Earth HD
+ BBC First HD
+ BBC Lifestyle
+ BBC World News
+ BET
+ Big Brother Mzansi HD
+ Bloomberg Television
+ Business Day
+ Cape Town TV
+ Cartoon Network HD
+ Cbeebies
+ CBS Justice HD
+ CBS Reality
+ CCTV 4
+ CGTN News
+ Channel O HD
+ CNBC Africa
+ CNN International HD
+ Comedy Central
+ Curiosity Stream
+ Day Star
+ Discovery Channel HD
+ Discovery Family HD
+ Disney Channel
+ Disney Junior
+ Dumisa
+ Deutsche Welle
+ E! Entertainment HD
+ eTV Extra HD
+ Emmanuel TV
+ eMovies HD
+ eMovies Extra HD
+ eNews Channel Africa
+ ESPN 2 HD
+ ESPN HD
+ eToonz
+ e.TV HD
+ EuroNews Africa
+ FAITH
+ Fashion One
+ FliekNet HD
+ Food Network HD
+ FOX Life
+ FOX HD
+ GauTV
+ Ginx eSports HD
+ Home and Garden TV HD
+ The History Channel HD
+ Honey
+ IGNITION
+ Discovery ID HD
+ Jim Jam
+ Kyk NET HD
+ Kyk NET & Kie HD
+ KyKNet Nou
+ Lifetime Entertainment
+ ME Channel
+ Mindset
+ M Movies 1
+ M Movies 2
+ M Movies 3
+ M Movies 4
+ M-Net Movies Men of Action HD
+ M-Net Plus 1 HD
+ M-Net HD
+ Moja Love HD
+ MTV
+ MTV base
+ Mzansi Bioskop
+ Mzansi Magic HD
+ Mzansi Music
+ Mzansi Wethu HD
+ National Geographic Channel
+ NatGeo Wild
+ NDTV 24x7
+ Newzroom Afrika
+ Nickelodeon
+ NickJr
+ NickTOONS
+ NTA I
+ One Gospel
+ Parliamentary Service
+ PBS Kids
+ Peoples Weather
+ RAI International
+ Real Time
+ ROK
+ Russia Today
+ RTPI
+ SABC 1 HD
+ SABC 2 HD
+ SABC 3 HD
+ SABC News
+ Sky News
+ Sound City
+ Soweto TV
+ Spice TV
+ Star Life HD
+ Studio Universal HD
+ ACTION
+ BLITZ
+ CRICKET
+ FOOTBALL
+ GOLF
+ GRANDSTAND
+ LA LIGA
+ MAXIMO 1
+ MOTORSPORT
+ EPL
+ PSL
+ RUGBY
+ TENNIS
+ VARIETY 1
+ VARIETY 2
+ VARIETY 3
+ VARIETY 4
+ TBN
+ TeleMundo English
+ TellyTrack
+ The Home Channel
+ Discovery TLC HD
+ TLNovelas
+ TNT Africa HD
+ TRACE Africa
+ Trace Gospel
+ TRACE URBAN HD
+ Travel Channel
+ Tshwane TV
+ TV5 Monde Afrique
+ Lesotho TV
+ Universal TV HD
+ VIA HD
+ VUZU HD
+ WildEarth
+ WWE
+ Zee World HD
+
+