diff --git a/.github/workflows/novasports.gr.yml b/.github/workflows/novasports.gr.yml
new file mode 100644
index 00000000..f092f86d
--- /dev/null
+++ b/.github/workflows/novasports.gr.yml
@@ -0,0 +1,17 @@
+name: novasports.gr
+on:
+ schedule:
+ - cron: '0 3 * * *'
+ 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/novasports.gr/__data__/content.html b/sites/novasports.gr/__data__/content.html
new file mode 100644
index 00000000..ed8272ff
--- /dev/null
+++ b/sites/novasports.gr/__data__/content.html
@@ -0,0 +1,3507 @@
+
Oops, tältä ajankohdalta ei löydy ohjelmatietoja :(
\ No newline at end of file
diff --git a/sites/novasports.gr/novasports.gr.config.js b/sites/novasports.gr/novasports.gr.config.js
new file mode 100644
index 00000000..653751b3
--- /dev/null
+++ b/sites/novasports.gr/novasports.gr.config.js
@@ -0,0 +1,86 @@
+const axios = require('axios')
+const cheerio = require('cheerio')
+const dayjs = require('dayjs')
+const utc = require('dayjs/plugin/utc')
+const timezone = require('dayjs/plugin/timezone')
+
+dayjs.extend(utc)
+dayjs.extend(timezone)
+
+module.exports = {
+ site: 'novasports.gr',
+ url: function ({ date, channel }) {
+ return `https://www.novasports.gr/wp-admin/admin-ajax.php?action=nova_get_template&template=tv-program/broadcast&dt=${date.format(
+ 'YYYY-MM-DD'
+ )}`
+ },
+ parser: function ({ content, channel, date }) {
+ let programs = []
+ const items = parseItems(content, channel)
+ items.forEach(item => {
+ const $item = cheerio.load(item)
+ const prev = programs[programs.length - 1]
+ let start = parseStart($item, date)
+ let stop = start.add(30, 'm')
+ if (prev) {
+ if (start.isBefore(prev.start)) {
+ start = start.add(1, 'd')
+ stop = stop.add(1, 'd')
+ }
+ prev.stop = start
+ }
+ programs.push({
+ title: parseTitle($item),
+ description: parseDescription($item),
+ start,
+ stop
+ })
+ })
+
+ return programs
+ },
+ async channels() {
+ const html = await axios
+ .get(
+ `https://www.novasports.gr/wp-admin/admin-ajax.php?action=nova_get_template&template=tv-program/broadcast&dt=2022-10-29`
+ )
+ .then(r => r.data)
+ .catch(console.log)
+ const $ = cheerio.load(html)
+ const items = $(
+ `#mc-broadcast-content:nth-child(2) > div > #channelist-slider > div.channelist-wrapper.slider-wrapper.content > div > div`
+ ).toArray()
+ return items.map(item => {
+ const name = $(item).find('.channel').text().trim()
+
+ return {
+ lang: 'el',
+ site_id: name,
+ name
+ }
+ })
+ }
+}
+
+function parseTitle($item) {
+ return $item('.title').text().trim()
+}
+
+function parseDescription($item) {
+ return $item('.subtitle').text().trim()
+}
+
+function parseStart($item, date) {
+ const timeString = $item('.time').text().trim()
+
+ return dayjs.tz(`${date.format('YYYY-MM-DD')} ${timeString}`, 'YYYY-MM-DD HH:mm', 'Europe/Athens')
+}
+
+function parseItems(content, channel) {
+ const $ = cheerio.load(content)
+ const $channelElement = $(
+ `#mc-broadcast-content:nth-child(2) > div > #channelist-slider > div.channelist-wrapper.slider-wrapper.content > div > div:contains("${channel.site_id}")`
+ )
+
+ return $channelElement.find('.channel-program > div').toArray()
+}
diff --git a/sites/novasports.gr/novasports.gr.test.js b/sites/novasports.gr/novasports.gr.test.js
new file mode 100644
index 00000000..cbaafe31
--- /dev/null
+++ b/sites/novasports.gr/novasports.gr.test.js
@@ -0,0 +1,49 @@
+// npm run channels:parse -- --config=./sites/novasports.gr/novasports.gr.config.js --output=./sites/novasports.gr/novasports.gr_gr.channels.xml
+// npx epg-grabber --config=sites/novasports.gr/novasports.gr.config.js --channels=sites/novasports.gr/novasports.gr_gr.channels.xml --output=guide.xml --days=2
+
+const { parser, url } = require('./novasports.gr.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('2022-10-29', 'YYYY-MM-DD').startOf('d')
+const channel = {
+ site_id: 'Novasports Premier League',
+ xmltv_id: 'NovasportsPremierLeague.gr'
+}
+
+it('can generate valid url', () => {
+ expect(url({ date })).toBe(
+ 'https://www.novasports.gr/wp-admin/admin-ajax.php?action=nova_get_template&template=tv-program/broadcast&dt=2022-10-29'
+ )
+})
+
+it('can parse response', () => {
+ const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
+ const results = parser({ content, channel, date }).map(p => {
+ p.start = p.start.toJSON()
+ p.stop = p.stop.toJSON()
+ return p
+ })
+
+ expect(results[0]).toMatchObject({
+ start: '2022-10-29T07:00:00.000Z',
+ stop: '2022-10-29T07:30:00.000Z',
+ title: 'Classic Match',
+ description: 'Τσέλσι - Μάντσεστερ Γ. (1999/00)'
+ })
+})
+
+it('can handle empty guide', () => {
+ const results = parser({
+ content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.html')),
+ channel,
+ date
+ })
+
+ expect(results).toMatchObject([])
+})
diff --git a/sites/novasports.gr/novasports.gr_gr.channels.xml b/sites/novasports.gr/novasports.gr_gr.channels.xml
new file mode 100644
index 00000000..7d47afb6
--- /dev/null
+++ b/sites/novasports.gr/novasports.gr_gr.channels.xml
@@ -0,0 +1,21 @@
+
+
+
+ Eurosport 1 HD
+ Eurosport 2 HD
+ Novasports1HD
+ Novasports2HD
+ Novasports3HD
+ Novasports4HD
+ Novasports5HD
+ Novasports6HD
+ Novasports Εxtra1
+ Novasports Extra2
+ Novasports Extra3
+ Novasports Extra4
+ ΝovasportsNews
+ Novasports Premier League
+ Novasports Prime
+ ΝovasportsStart
+
+