diff --git a/SITES.md b/SITES.md
index 0d212b1b..c1dc9247 100644
--- a/SITES.md
+++ b/SITES.md
@@ -88,6 +88,7 @@
| novasports.gr | π’ | |
| nowplayer.now.com | π‘ | https://github.com/iptv-org/epg/issues/2247 |
| nuevosiglo.com.uy | π’ | |
+| nzxmltv.com | π’ | |
| ontvtonight.com | π’ | |
| osn.com | π’ | |
| pbsguam.org | π’ | |
diff --git a/sites/nzxmltv.com/__data__/content.xml b/sites/nzxmltv.com/__data__/content.xml
new file mode 100644
index 00000000..1a1e9f13
--- /dev/null
+++ b/sites/nzxmltv.com/__data__/content.xml
@@ -0,0 +1,93 @@
+
+
+
+
+TVNZ 1
+https://helptvnz.zendesk.com/hc/en-us/requests/new
+
+
+As New Zealand's leading producer of local entertainment programmes, news and current affairs, and national television events. TVNZ 1 is uniquely positioned to deliver the content New Zealanders value and celebrate. Selected programmes on TVNZ 1 are broadcast in HD via UHF. To access HD programming, you will need UHF coverage and use a UHF aerial.
+1
+
+
+
+Sunday
+Sunday
+On Sunday, an unmissable show with stories about divorce, weight loss, and the incomprehensible devastation of Gaza.
+
+Documentary
+series
+
+New Zealand
+1035483
+10681949
+episode/28725468-c065-4355-86ce-093f5d09dd14
+show/a9fa9bab-883b-4b22-b890-b61ee34338a1
+2023-11-19 19:30:00
+tv/11796
+series/358626
+2022.36.0
+show/ibms+show:320
+
+
+
+
+English (NZ)
+
+
+0/10
+
+Join Miriama Kamo and the team as they delve into the subjects that matter to you.
+
+
+
+Rich House, Poor House
+Wildes/ Pickston
+Kim and Dave have risked everything by converting a field in Northumberland into an organic farm, but rarely have time off and money is tight. They swap with millionaire businesswoman Ampika.
+2017
+Documentary
+Factual
+series
+
+United Kingdom
+1034142
+10649234
+episode/1abb3ded-e6c4-4457-9fef-f66015837aa5
+show/dd2c2dee-2c78-47ad-9a98-6e1e91a33de4
+2023-06-22 20:30:00
+tv/70899
+series/325959
+7.5.0
+show/ibms+show:3839
+
+
+
+
+English (NZ)
+
+
+G
+
+
+7/10
+
+Two families find out how the other half lives, swapping homes, social status and budgets with people from opposite ends of the financial spectrum and class divide.
+
+
+
diff --git a/sites/nzxmltv.com/nzxmltv.com.channels.xml b/sites/nzxmltv.com/nzxmltv.com.channels.xml
new file mode 100644
index 00000000..29521e11
--- /dev/null
+++ b/sites/nzxmltv.com/nzxmltv.com.channels.xml
@@ -0,0 +1,40 @@
+
+
+
+ Al Jazeera
+ Bravo
+ Bravo Plus 1
+ CH200
+ eden
+ eden+1
+ Firstlight
+ HGTV
+ Hope Channel
+ PRIME
+ PRIME +1
+ Rush
+ Shine TV
+ Te Reo
+ Three
+ ThreePlus1
+ TVNZ 1
+ TVNZ 1 +1
+ TVNZ 2
+ TVNZ 2 +1
+ TVNZ DUKE
+ TVNZ DUKE +1
+ Wairarapa TV
+ Whakaata MΔori
+
diff --git a/sites/nzxmltv.com/nzxmltv.com.config.js b/sites/nzxmltv.com/nzxmltv.com.config.js
new file mode 100644
index 00000000..bb9f7c48
--- /dev/null
+++ b/sites/nzxmltv.com/nzxmltv.com.config.js
@@ -0,0 +1,65 @@
+const parser = require('epg-parser')
+
+module.exports = {
+ site: 'nzxmltv.com',
+ days: 2,
+ request: {
+ cache: {
+ ttl: 3600000 // 1 hour
+ },
+ maxContentLength: 10485760 // 10 MB
+ },
+ url: 'https://nzxmltv.com/xmltv/guide.xml',
+ parser({ content, channel, date }) {
+ const programs = []
+ parseItems(content, channel, date).forEach(item => {
+ const program = {
+ title: item.title?.[0]?.value,
+ description: item.desc?.[0]?.value,
+ icon: item.icon?.[0],
+ start: item.start,
+ stop: item.stop
+ }
+ if (item.episodeNum) {
+ item.episodeNum.forEach(ep => {
+ if (ep.system === 'xmltv_ns') {
+ const [season, episode, _] = ep.value.split('.')
+ program.season = parseInt(season) + 1
+ program.episode = parseInt(episode) + 1
+ return true
+ }
+ })
+ }
+ programs.push(program)
+ })
+
+ return programs
+ },
+ async channels() {
+ const axios = require('axios')
+ const cheerio = require('cheerio')
+ const xml = await axios
+ .get('https://nzxmltv.com/xmltv/guide.xml')
+ .then(r => r.data)
+ .catch(console.error)
+
+ const channels = []
+ const $ = cheerio.load(xml)
+ $('tv channel').each((i, el) => {
+ const disp = $(el).find('display-name')
+ channels.push({
+ lang: disp.attr('lang').substr(0, 2),
+ site_id: $(el).attr('id'),
+ name: disp.text().trim()
+ })
+ })
+
+ return channels
+ }
+}
+
+function parseItems(content, channel, date) {
+ const { programs } = parser.parse(content)
+
+ return programs.filter(p => p.channel === channel.site_id && date.isSame(p.start, 'day'))
+}
diff --git a/sites/nzxmltv.com/nzxmltv.com.test.js b/sites/nzxmltv.com/nzxmltv.com.test.js
new file mode 100644
index 00000000..0408cd38
--- /dev/null
+++ b/sites/nzxmltv.com/nzxmltv.com.test.js
@@ -0,0 +1,43 @@
+// npm run channels:parse -- --config=./sites/nzxmltv.com/nzxmltv.com.config.js --output=./sites/nzxmltv.com/nzxmltv.com.channels.xml
+// npm run grab -- --site=nzxmltv.com
+
+const { parser, url } = require('./nzxmltv.com.config.js')
+const fs = require('fs')
+const path = require('path')
+const dayjs = require('dayjs')
+const utc = require('dayjs/plugin/utc')
+const customParseFormat = require('dayjs/plugin/customParseFormat')
+
+dayjs.extend(customParseFormat)
+dayjs.extend(utc)
+
+const date = dayjs.utc('2023-11-21').startOf('d')
+const channel = {
+ site_id: '1',
+ xmltv_id: 'TVNZ1.nz'
+}
+
+it('can generate valid url', () => {
+ expect(url).toBe('https://nzxmltv.com/xmltv/guide.xml')
+})
+
+it('can parse response', () => {
+ const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.xml'))
+ const results = parser({ content, channel, date })
+
+ expect(results[0]).toMatchObject({
+ start: '2023-11-21T10:30:00.000Z',
+ stop: '2023-11-21T11:25:00.000Z',
+ title: 'Sunday',
+ description:
+ 'On Sunday, an unmissable show with stories about divorce, weight loss, and the incomprehensible devastation of Gaza.',
+ season: 2023,
+ episode: 37,
+ icon: 'https://www.thetvdb.com/banners/posters/5dbebff2986f2.jpg'
+ })
+})
+
+it('can handle empty guide', () => {
+ const result = parser({ content: '', channel, date })
+ expect(result).toMatchObject([])
+})