diff --git a/.github/workflows/melita.com.yml b/.github/workflows/melita.com.yml new file mode 100644 index 00000000..d2bb08be --- /dev/null +++ b/.github/workflows/melita.com.yml @@ -0,0 +1,17 @@ +name: melita.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/melita.com/melita.com.config.js b/sites/melita.com/melita.com.config.js new file mode 100644 index 00000000..6735cd36 --- /dev/null +++ b/sites/melita.com/melita.com.config.js @@ -0,0 +1,87 @@ +const axios = require('axios') +const dayjs = require('dayjs') + +module.exports = { + site: 'melita.com', + url: function ({ channel, date }) { + return `https://androme.melitacable.com/api/epg/v1/schedule/channel/${ + channel.site_id + }/from/${date.format('YYYY-MM-DDTHH:mmZ')}/until/${date + .add(1, 'd') + .format('YYYY-MM-DDTHH:mmZ')}` + }, + parser: function ({ content }) { + let programs = [] + const items = parseItems(content) + items.forEach(item => { + programs.push({ + title: item.title, + description: item.shortSynopsis, + icon: parseIcon(item), + category: item.tags, + season: item.season, + episode: item.episode, + start: parseStart(item), + stop: parseStop(item) + }) + }) + + return programs + }, + async channels() { + const channels = await axios + .get('https://androme.melitacable.com/api/epg/v2/channel') + .then(r => r.data) + .catch(console.log) + + return channels + .filter(i => !i.audioOnly && i.enabled) + .map(i => { + return { + name: i.name, + site_id: i.id + } + }) + } +} + +function parseStart(item) { + if (!item.published || !item.published.start) return null + + return dayjs(item.published.start) +} + +function parseStop(item) { + if (!item.published || !item.published.end) return null + + return dayjs(item.published.end) +} + +function parseIcon(item) { + return item.posterImage ? item.posterImage + '?form=epg-card-6' : null +} + +function parseItems(content) { + const data = JSON.parse(content) + if ( + !data || + !data.schedules || + !data.programs || + !data.seasons || + !data.series || + !Array.isArray(data.schedules) + ) + return [] + + return data.schedules + .map(i => { + const program = data.programs.find(p => p.id === i.program) || {} + if (!program.season) return null + const season = data.seasons.find(s => s.id === program.season) || {} + if (!season.series) return null + const series = data.series.find(s => s.id === season.series) + + return { ...i, ...program, ...season, ...series } + }) + .filter(i => i) +} diff --git a/sites/melita.com/melita.com.test.js b/sites/melita.com/melita.com.test.js new file mode 100644 index 00000000..73f492ba --- /dev/null +++ b/sites/melita.com/melita.com.test.js @@ -0,0 +1,51 @@ +// npm run channels:parse -- --config=./sites/melita.com/melita.com.config.js --output=./sites/melita.com/melita.com_mt.channels.xml +// npx epg-grabber --config=sites/melita.com/melita.com.config.js --channels=sites/melita.com/melita.com_mt.channels.xml --output=guide.xml --days=2 + +const { parser, url } = require('./melita.com.config.js') +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-04-20', 'YYYY-MM-DD').startOf('d') +const channel = { + site_id: '4d40a9f9-12fd-4f03-8072-61c637ff6995', + xmltv_id: 'TVM.mt' +} + +it('can generate valid url', () => { + expect(url({ channel, date })).toBe( + 'https://androme.melitacable.com/api/epg/v1/schedule/channel/4d40a9f9-12fd-4f03-8072-61c637ff6995/from/2022-04-20T00:00+00:00/until/2022-04-21T00:00+00:00' + ) +}) + +it('can parse response', () => { + const content = `{"schedules":[{"id":"138dabff-131a-42a0-9373-203545933dd0","published":{"start":"2022-04-20T06:25:00Z","end":"2022-04-20T06:45:00Z"},"program":"ae52299a-3c99-4d34-9932-e21d383f9800","live":false,"blackouts":[]}],"programs":[{"id":"ae52299a-3c99-4d34-9932-e21d383f9800","title":"How I Met Your Mother","shortSynopsis":"Symphony of Illumination - Robin gets some bad news and decides to keep it to herself. Marshall decorates the house.","posterImage":"https://androme.melitacable.com/media/images/epg/bc/07/p8953134_e_h10_ad.jpg","episode":12,"episodeTitle":"Symphony of Illumination","season":"fdd6e42c-97f9-4d7a-aaca-78b53378f960","genres":["3.5.7.3"],"tags":["comedy"],"adult":false}],"seasons":[{"id":"fdd6e42c-97f9-4d7a-aaca-78b53378f960","title":"How I Met Your Mother","adult":false,"season":7,"series":"858c535a-abbb-451b-807a-94196997ea2d"}],"series":[{"id":"858c535a-abbb-451b-807a-94196997ea2d","title":"How I Met Your Mother","adult":false}]}` + const result = parser({ content }).map(p => { + p.start = p.start.toJSON() + p.stop = p.stop.toJSON() + return p + }) + + expect(result).toMatchObject([ + { + start: '2022-04-20T06:25:00.000Z', + stop: '2022-04-20T06:45:00.000Z', + title: 'How I Met Your Mother', + description: + 'Symphony of Illumination - Robin gets some bad news and decides to keep it to herself. Marshall decorates the house.', + season: 7, + episode: 12, + icon: 'https://androme.melitacable.com/media/images/epg/bc/07/p8953134_e_h10_ad.jpg?form=epg-card-6', + category: ['comedy'] + } + ]) +}) + +it('can handle empty guide', () => { + const result = parser({ + content: `{}` + }) + expect(result).toMatchObject([]) +}) diff --git a/sites/melita.com/melita.com_mt.channels.xml b/sites/melita.com/melita.com_mt.channels.xml new file mode 100644 index 00000000..647a9e24 --- /dev/null +++ b/sites/melita.com/melita.com_mt.channels.xml @@ -0,0 +1,128 @@ + + + + Al Jazeera Arabic + Al Jazeera English + AMC HD + Animal Planet HD + Baby TV + BBC Earth HD + BBC Entertainment + BBC World News HD + Bloomberg + Boing + Boomerang + BVN Europa + Caccia e Pesca + Canale 5 HD + Canale Italia + Cartoonito + Cartoon Network + CBeeBies HD + CBS Reality + CGTN + Channel One Russia + Cielo + TV Moda + Club MTV + C Music TV + CNBC + CNN International + Crime + Investigation + Daystar Network HD + Discovery Channel + Discovery HD + Discovery Science + Disney Channel + Disney Junior + Discovery Turbo Xtra HD + Dubai One + Duck TV + Deutsche Welle HD + E! Entertainment HD + Euronews HD + Eurosport HD + Eurosport 2 + EWTN + Extreme Sports + FTV HD + f Living + Food Network HD + FOX HD + FOX LIFE HD + France 2 HD + France 24 in English + France 24 in French + Ginx TV + GOD TV + GREAT! Movies Action + HGTV HD + HISTORY HD + ID HD + Iris + Italia 1 HD + Italia 2 + ITV Shopping + JimJam + La 5 + LBC Europe + Luxe TV HD + Mediaset Extra + Mediaset Italia + Melita More + Melita More HD + Mezzo HD + MTV 00s + MTV 80s + MTV 90s + MTV + MTV Hits + MTV Live HD + National Geographic HD + Nat Geo Wild HD + NET HD + Nickelodeon HD + Nick Junior Global + ONE HD + Parliament Channel + Rai Uno + Rai Uno HD + Rai Due + Rai Due HD + Rai Tre + Rai Tre HD + Rai News + Rai Scuola + Rai Storia + Rete 4 HD + Radio TV + RTL + RTL II + Russia Today + RTR Planeta + Sky News HD + Smash TV + iConcerts HD + TGCOM 24 + TLC + Top Crime + TRACE Urban HD + Travel HD + TRT Turk + TRT World HD + TSN1 + TSN2 + TSN3 + TSN4 + TSN5 + TSN6 + TSN7 + TSN8 + TV 2000 + TV5 Monde + TVE Internacional + TVM HD + TVMNEWS+ + Xejk + +