From eba7821c8efbb46634d8c47adf157db1a5f73d82 Mon Sep 17 00:00:00 2001 From: daniloroxette Date: Sat, 28 Mar 2026 21:13:26 -0300 Subject: [PATCH 1/2] Add app.tvufop.com.br source for TV UFOP --- SITES.md | 1 + .../app.tvufop.com.br.channels.xml | 7 + .../app.tvufop.com.br.config.js | 167 ++++++++++++++++++ .../app.tvufop.com.br.test.js | 97 ++++++++++ sites/app.tvufop.com.br/readme.md | 31 ++++ 5 files changed, 303 insertions(+) create mode 100644 sites/app.tvufop.com.br/app.tvufop.com.br.channels.xml create mode 100644 sites/app.tvufop.com.br/app.tvufop.com.br.config.js create mode 100644 sites/app.tvufop.com.br/app.tvufop.com.br.test.js create mode 100644 sites/app.tvufop.com.br/readme.md diff --git a/SITES.md b/SITES.md index c00b693e..6829c970 100644 --- a/SITES.md +++ b/SITES.md @@ -14,6 +14,7 @@ antennaeurope.gr11🟢 antennapacific.gr11🟢 antennasatellite.gr11🟢 + app.tvufop.com.br11🟢 arianatelevision.com11🟢 arirang.com33🟢 artonline.tv55🟢 diff --git a/sites/app.tvufop.com.br/app.tvufop.com.br.channels.xml b/sites/app.tvufop.com.br/app.tvufop.com.br.channels.xml new file mode 100644 index 00000000..cc2338e5 --- /dev/null +++ b/sites/app.tvufop.com.br/app.tvufop.com.br.channels.xml @@ -0,0 +1,7 @@ + + + + + TV UFOP + + diff --git a/sites/app.tvufop.com.br/app.tvufop.com.br.config.js b/sites/app.tvufop.com.br/app.tvufop.com.br.config.js new file mode 100644 index 00000000..a113981b --- /dev/null +++ b/sites/app.tvufop.com.br/app.tvufop.com.br.config.js @@ -0,0 +1,167 @@ +const { File } = require('node:buffer') + + +if (typeof global.File === 'undefined') { + + global.File = File + +} + + +const cheerio = require('cheerio') + + +module.exports = { + + site: 'app.tvufop.com.br', + + days: 7, + + url() { + + return 'https://app.tvufop.com.br/epg/epg_tvufop_web.xml' + + }, + + parser({ content, channel, date }) { + + const $ = cheerio.load(content || '', { xmlMode: true, decodeEntities: false }) + + const programs = [] + + + const dayStart = date.startOf('d').toDate() + + const dayEnd = date.add(1, 'd').startOf('d').toDate() + + + $(`programme[channel="${channel.site_id}"]`).each((_, el) => { + + const $el = $(el) + + + const start = parseXmltvDate($el.attr('start')) + + const stop = parseXmltvDate($el.attr('stop')) + + + if (!start || !stop) return + + if (start >= dayEnd || stop <= dayStart) return + + + const title = textOf($el, 'title') + + if (!title) return + + + const item = { + + title, + + start, + + stop + + } + + + const description = textOf($el, 'desc') + + if (description) item.description = description + + + const icon = $el.find('icon').attr('src') + + if (icon) item.icon = icon + + + const rating = $el.find('rating > value').first().text().trim() + + if (rating) item.rating = rating + + + programs.push(item) + + }) + + + return programs + + } + +} + + +function textOf($el, tagName) { + + return $el.find(tagName).first().text().trim() + +} + + +function parseXmltvDate(value) { + + if (!value) return null + + + const m = value.trim().match( + + /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\s+([+-])(\d{2})(\d{2})$/ + + ) + + + if (!m) return null + + + const [ + + , + + year, + + month, + + day, + + hour, + + minute, + + second, + + sign, + + tzHour, + + tzMinute + + ] = m + + + const utcMs = Date.UTC( + + Number(year), + + Number(month) - 1, + + Number(day), + + Number(hour), + + Number(minute), + + Number(second) + + ) + + + const offsetMinutes = + + (Number(tzHour) * 60 + Number(tzMinute)) * (sign === '+' ? 1 : -1) + + + return new Date(utcMs - offsetMinutes * 60 * 1000) + +} diff --git a/sites/app.tvufop.com.br/app.tvufop.com.br.test.js b/sites/app.tvufop.com.br/app.tvufop.com.br.test.js new file mode 100644 index 00000000..3111ae9f --- /dev/null +++ b/sites/app.tvufop.com.br/app.tvufop.com.br.test.js @@ -0,0 +1,97 @@ + +const { parser, url } = require('./app.tvufop.com.br.config.js') + +const dayjs = require('dayjs') + +const utc = require('dayjs/plugin/utc') + +const customParseFormat = require('dayjs/plugin/customParseFormat') + + + +dayjs.extend(utc) + +dayjs.extend(customParseFormat) + + + +const date = dayjs.utc('2026-03-28', 'YYYY-MM-DD').startOf('d') + +const channel = { + + site_id: 'TVUFOP.br@HD', + + xmltv_id: 'TVUFOP.br@HD', + + lang: 'pt' + +} + + + +it('can generate valid url', () => { + + expect(url({ channel, date })).toBe('https://app.tvufop.com.br/epg/epg_tvufop_web.xml') + +}) + + + +it('can parse response', () => { + + const content = ` + + + + + + TV UFOP + + + + + + (FUTURA) CANAL DA HISTÓRIA - CARMEN MIRANDA + + Clara e Neto usam uma máquina do tempo. + + + + Livre + + + + ` + + + + const results = parser({ content, channel, date }) + + + + expect(results.length).toBe(1) + + expect(results[0]).toMatchObject({ + + title: '(FUTURA) CANAL DA HISTÓRIA - CARMEN MIRANDA', + + description: 'Clara e Neto usam uma máquina do tempo.', + + icon: 'https://app.tvufop.com.br/epg/CANAL.jpg', + + rating: 'Livre' + + }) + +}) + + + +it('can handle empty guide', () => { + + const results = parser({ content: '', channel, date }) + + expect(results).toMatchObject([]) + +}) + diff --git a/sites/app.tvufop.com.br/readme.md b/sites/app.tvufop.com.br/readme.md new file mode 100644 index 00000000..a4e61539 --- /dev/null +++ b/sites/app.tvufop.com.br/readme.md @@ -0,0 +1,31 @@ + +# app.tvufop.com.br + + + +https://app.tvufop.com.br + + + +### Download the guide + + + +Run: + + + +npm run grab --- --site=app.tvufop.com.br + + + +### Test + + + +Run: + + + +npm test --- app.tvufop.com.br + From 1a40105eb36dd15f6dce69c4fe6fe0b1ae96fa40 Mon Sep 17 00:00:00 2001 From: daniloroxette Date: Sat, 28 Mar 2026 21:47:16 -0300 Subject: [PATCH 2/2] Adjust TV UFOP readme URL --- sites/app.tvufop.com.br/readme.md | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/sites/app.tvufop.com.br/readme.md b/sites/app.tvufop.com.br/readme.md index a4e61539..31886f12 100644 --- a/sites/app.tvufop.com.br/readme.md +++ b/sites/app.tvufop.com.br/readme.md @@ -1,31 +1,17 @@ - # app.tvufop.com.br +XMLTV source for TV UFOP: - -https://app.tvufop.com.br - - +https://app.tvufop.com.br/epg/epg_tvufop_web.xml ### Download the guide - - Run: - - npm run grab --- --site=app.tvufop.com.br - - ### Test - - Run: - - npm test --- app.tvufop.com.br -