mirror of
https://github.com/iptv-org/epg
synced 2025-12-16 10:26:41 -05:00
Fix linter issues in sites/
This commit is contained in:
@@ -1,132 +1,134 @@
|
||||
const axios = require('axios')
|
||||
const cheerio = require('cheerio')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
const doFetch = require('@ntlab/sfetch')
|
||||
const debug = require('debug')('site:tivie.id')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
doFetch
|
||||
.setDebugger(debug)
|
||||
|
||||
const tz = 'Asia/Jakarta'
|
||||
|
||||
module.exports = {
|
||||
site: 'tivie.id',
|
||||
days: 2,
|
||||
url({ channel, date }) {
|
||||
return `https://tivie.id/channel/${
|
||||
channel.site_id
|
||||
}/${
|
||||
date.format('YYYYMMDD')
|
||||
}`
|
||||
},
|
||||
async parser({ content, date }) {
|
||||
const programs = []
|
||||
if (content) {
|
||||
const $ = cheerio.load(content)
|
||||
const items = $('ul[x-data] > li[id*="event-"] > div.w-full').toArray()
|
||||
.map(item => {
|
||||
const $item = $(item)
|
||||
const time = $item.find('div:nth-child(1) span:nth-child(1)')
|
||||
const info = $item.find('div:nth-child(2) h5')
|
||||
const detail = info.find('a')
|
||||
const p = {
|
||||
start: dayjs.tz(`${date.format('YYYY-MM-DD')} ${time.html()}`, 'YYYY-MM-DD HH:mm', tz)
|
||||
}
|
||||
if (detail.length) {
|
||||
const subtitle = detail.find('div')
|
||||
p.title = parseText(subtitle.length ? subtitle : detail)
|
||||
p.url = detail.attr('href')
|
||||
} else {
|
||||
p.title = parseText(info)
|
||||
}
|
||||
if (p.title) {
|
||||
const [, , season, episode] = p.title.match(/( S(\d+))?, Ep\. (\d+)/) || [null, null, null, null]
|
||||
if (season) {
|
||||
p.season = parseInt(season)
|
||||
}
|
||||
if (episode) {
|
||||
p.episode = parseInt(episode)
|
||||
}
|
||||
}
|
||||
return p
|
||||
})
|
||||
// fetch detailed guide if necessary
|
||||
const queues = items
|
||||
.filter(i => i.url)
|
||||
.map(i => {
|
||||
const url = i.url
|
||||
delete i.url
|
||||
return {i, url}
|
||||
})
|
||||
if (queues.length) {
|
||||
await doFetch(queues, (queue, res) => {
|
||||
const $ = cheerio.load(res)
|
||||
const img = $('#main-content > div > div:nth-child(1) img')
|
||||
const info = $('#main-content > div > div:nth-child(2)')
|
||||
const title = parseText(info.find('h2:nth-child(2)'))
|
||||
if (!queue.i.title.startsWith(title)) {
|
||||
queue.i.subTitle = parseText(info.find('h2:nth-child(2)'))
|
||||
}
|
||||
queue.i.description = parseText(info.find('div[class=""]:nth-child(4)'))
|
||||
queue.i.date = parseText(info.find('h2:nth-child(3)'))
|
||||
queue.i.image = img.length ? img.attr('src') : null
|
||||
})
|
||||
}
|
||||
// fill start-stop
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (i < items.length - 1) {
|
||||
items[i].stop = items[i + 1].start
|
||||
} else {
|
||||
items[i].stop = dayjs.tz(`${date.add(1, 'd').format('YYYY-MM-DD')} 00:00`, 'YYYY-MM-DD HH:mm', tz)
|
||||
}
|
||||
}
|
||||
// add programs
|
||||
programs.push(...items)
|
||||
}
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels({ lang = 'id' }) {
|
||||
const result = await axios
|
||||
.get('https://tivie.id/channel')
|
||||
.then(response => response.data)
|
||||
.catch(console.error)
|
||||
|
||||
const $ = cheerio.load(result)
|
||||
const items = $('ul[x-data] li[x-data] div header h2 a').toArray()
|
||||
const channels = items.map(item => {
|
||||
const $item = $(item)
|
||||
const url = $item.attr('href')
|
||||
return {
|
||||
lang,
|
||||
site_id: url.substr(url.lastIndexOf('/') + 1),
|
||||
name: $item.find('strong').text()
|
||||
}
|
||||
})
|
||||
|
||||
return channels
|
||||
}
|
||||
}
|
||||
|
||||
function parseText($item) {
|
||||
let text = $item.text()
|
||||
.replace(/\t/g, '')
|
||||
.replace(/\n/g, ' ')
|
||||
.trim()
|
||||
while (true) {
|
||||
if (text.match(/ /)) {
|
||||
text = text.replace(/ /g, ' ')
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
const axios = require('axios')
|
||||
const cheerio = require('cheerio')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
const doFetch = require('@ntlab/sfetch')
|
||||
const debug = require('debug')('site:tivie.id')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
doFetch.setDebugger(debug)
|
||||
|
||||
const tz = 'Asia/Jakarta'
|
||||
|
||||
module.exports = {
|
||||
site: 'tivie.id',
|
||||
days: 2,
|
||||
url({ channel, date }) {
|
||||
return `https://tivie.id/channel/${channel.site_id}/${date.format('YYYYMMDD')}`
|
||||
},
|
||||
async parser({ content, date }) {
|
||||
const programs = []
|
||||
if (content) {
|
||||
const $ = cheerio.load(content)
|
||||
const items = $('ul[x-data] > li[id*="event-"] > div.w-full')
|
||||
.toArray()
|
||||
.map(item => {
|
||||
const $item = $(item)
|
||||
const time = $item.find('div:nth-child(1) span:nth-child(1)')
|
||||
const info = $item.find('div:nth-child(2) h5')
|
||||
const detail = info.find('a')
|
||||
const p = {
|
||||
start: dayjs.tz(`${date.format('YYYY-MM-DD')} ${time.html()}`, 'YYYY-MM-DD HH:mm', tz)
|
||||
}
|
||||
if (detail.length) {
|
||||
const subtitle = detail.find('div')
|
||||
p.title = parseText(subtitle.length ? subtitle : detail)
|
||||
p.url = detail.attr('href')
|
||||
} else {
|
||||
p.title = parseText(info)
|
||||
}
|
||||
if (p.title) {
|
||||
const [, , season, episode] = p.title.match(/( S(\d+))?, Ep\. (\d+)/) || [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
if (season) {
|
||||
p.season = parseInt(season)
|
||||
}
|
||||
if (episode) {
|
||||
p.episode = parseInt(episode)
|
||||
}
|
||||
}
|
||||
return p
|
||||
})
|
||||
// fetch detailed guide if necessary
|
||||
const queues = items
|
||||
.filter(i => i.url)
|
||||
.map(i => {
|
||||
const url = i.url
|
||||
delete i.url
|
||||
return { i, url }
|
||||
})
|
||||
if (queues.length) {
|
||||
await doFetch(queues, (queue, res) => {
|
||||
const $ = cheerio.load(res)
|
||||
const img = $('#main-content > div > div:nth-child(1) img')
|
||||
const info = $('#main-content > div > div:nth-child(2)')
|
||||
const title = parseText(info.find('h2:nth-child(2)'))
|
||||
if (!queue.i.title.startsWith(title)) {
|
||||
queue.i.subTitle = parseText(info.find('h2:nth-child(2)'))
|
||||
}
|
||||
queue.i.description = parseText(info.find('div[class=""]:nth-child(4)'))
|
||||
queue.i.date = parseText(info.find('h2:nth-child(3)'))
|
||||
queue.i.image = img.length ? img.attr('src') : null
|
||||
})
|
||||
}
|
||||
// fill start-stop
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (i < items.length - 1) {
|
||||
items[i].stop = items[i + 1].start
|
||||
} else {
|
||||
items[i].stop = dayjs.tz(
|
||||
`${date.add(1, 'd').format('YYYY-MM-DD')} 00:00`,
|
||||
'YYYY-MM-DD HH:mm',
|
||||
tz
|
||||
)
|
||||
}
|
||||
}
|
||||
// add programs
|
||||
programs.push(...items)
|
||||
}
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels({ lang = 'id' }) {
|
||||
const result = await axios
|
||||
.get('https://tivie.id/channel')
|
||||
.then(response => response.data)
|
||||
.catch(console.error)
|
||||
|
||||
const $ = cheerio.load(result)
|
||||
const items = $('ul[x-data] li[x-data] div header h2 a').toArray()
|
||||
const channels = items.map(item => {
|
||||
const $item = $(item)
|
||||
const url = $item.attr('href')
|
||||
return {
|
||||
lang,
|
||||
site_id: url.substr(url.lastIndexOf('/') + 1),
|
||||
name: $item.find('strong').text()
|
||||
}
|
||||
})
|
||||
|
||||
return channels
|
||||
}
|
||||
}
|
||||
|
||||
function parseText($item) {
|
||||
let text = $item.text().replace(/\t/g, '').replace(/\n/g, ' ').trim()
|
||||
while (true) {
|
||||
if (text.match(/\s\s/)) {
|
||||
text = text.replace(/\s\s/g, ' ')
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
@@ -1,77 +1,75 @@
|
||||
const { parser, url } = require('./tivie.id.config')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
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('2024-12-31').startOf('d')
|
||||
const channel = {
|
||||
site_id: 'axn',
|
||||
xmltv_id: 'AXN.id',
|
||||
lang: 'id'
|
||||
}
|
||||
|
||||
axios.get.mockImplementation(url => {
|
||||
const urls = {
|
||||
'https://tivie.id/film/white-house-down-nwzDnwz9nAv6':
|
||||
'program01.html',
|
||||
'https://tivie.id/program/hudson-rex-s6-e14-nwzDnwvBmQr9':
|
||||
'program02.html',
|
||||
}
|
||||
let data = ''
|
||||
if (urls[url] !== undefined) {
|
||||
data = fs.readFileSync(path.join(__dirname, '__data__', urls[url])).toString()
|
||||
}
|
||||
return Promise.resolve({ data })
|
||||
})
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ channel, date })).toBe('https://tivie.id/channel/axn/20241231')
|
||||
})
|
||||
|
||||
it('can parse response', async () => {
|
||||
const content = fs.readFileSync(path.join(__dirname, '__data__', 'content.html'))
|
||||
const results = (
|
||||
await parser({ date, content, channel })
|
||||
).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
|
||||
expect(results.length).toBe(27)
|
||||
expect(results[0]).toMatchObject({
|
||||
start: '2024-12-30T17:00:00.000Z',
|
||||
stop: '2024-12-30T17:05:00.000Z',
|
||||
title: 'White House Down',
|
||||
description:
|
||||
'Saat melakukan tur di Gedung Putih bersama putrinya yang masih kecil, seorang perwira polisi beraksi untuk melindungi anaknya dan presiden dari sekelompok penjajah paramiliter bersenjata lengkap.',
|
||||
image: 'https://i0.wp.com/is3.cloudhost.id/tivie/poster/2023/09/65116c78791c2-1695640694.jpg?resize=480,270',
|
||||
})
|
||||
expect(results[2]).toMatchObject({
|
||||
start: '2024-12-30T18:00:00.000Z',
|
||||
stop: '2024-12-30T18:55:00.000Z',
|
||||
title: 'Hudson & Rex S6, Ep. 14',
|
||||
description:
|
||||
'Saat guru musik Jesse terbunuh di studio rekamannya, Charlie dan Rex menghubungkan kejahatan tersebut dengan pembunuhan yang tampaknya tak ada hubungannya.',
|
||||
image: 'https://i0.wp.com/is3.cloudhost.id/tivie/poster/2024/07/668b7ced47b25-1720417517.jpg?resize=480,270',
|
||||
season: 6,
|
||||
episode: 14,
|
||||
})
|
||||
})
|
||||
|
||||
it('can handle empty guide', async () => {
|
||||
const results = await parser({
|
||||
date,
|
||||
channel,
|
||||
content: '',
|
||||
})
|
||||
expect(results).toMatchObject([])
|
||||
})
|
||||
const { parser, url } = require('./tivie.id.config')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
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('2024-12-31').startOf('d')
|
||||
const channel = {
|
||||
site_id: 'axn',
|
||||
xmltv_id: 'AXN.id',
|
||||
lang: 'id'
|
||||
}
|
||||
|
||||
axios.get.mockImplementation(url => {
|
||||
const urls = {
|
||||
'https://tivie.id/film/white-house-down-nwzDnwz9nAv6': 'program01.html',
|
||||
'https://tivie.id/program/hudson-rex-s6-e14-nwzDnwvBmQr9': 'program02.html'
|
||||
}
|
||||
let data = ''
|
||||
if (urls[url] !== undefined) {
|
||||
data = fs.readFileSync(path.join(__dirname, '__data__', urls[url])).toString()
|
||||
}
|
||||
return Promise.resolve({ data })
|
||||
})
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ channel, date })).toBe('https://tivie.id/channel/axn/20241231')
|
||||
})
|
||||
|
||||
it('can parse response', async () => {
|
||||
const content = fs.readFileSync(path.join(__dirname, '__data__', 'content.html'))
|
||||
const results = (await parser({ date, content, channel })).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
|
||||
expect(results.length).toBe(27)
|
||||
expect(results[0]).toMatchObject({
|
||||
start: '2024-12-30T17:00:00.000Z',
|
||||
stop: '2024-12-30T17:05:00.000Z',
|
||||
title: 'White House Down',
|
||||
description:
|
||||
'Saat melakukan tur di Gedung Putih bersama putrinya yang masih kecil, seorang perwira polisi beraksi untuk melindungi anaknya dan presiden dari sekelompok penjajah paramiliter bersenjata lengkap.',
|
||||
image:
|
||||
'https://i0.wp.com/is3.cloudhost.id/tivie/poster/2023/09/65116c78791c2-1695640694.jpg?resize=480,270'
|
||||
})
|
||||
expect(results[2]).toMatchObject({
|
||||
start: '2024-12-30T18:00:00.000Z',
|
||||
stop: '2024-12-30T18:55:00.000Z',
|
||||
title: 'Hudson & Rex S6, Ep. 14',
|
||||
description:
|
||||
'Saat guru musik Jesse terbunuh di studio rekamannya, Charlie dan Rex menghubungkan kejahatan tersebut dengan pembunuhan yang tampaknya tak ada hubungannya.',
|
||||
image:
|
||||
'https://i0.wp.com/is3.cloudhost.id/tivie/poster/2024/07/668b7ced47b25-1720417517.jpg?resize=480,270',
|
||||
season: 6,
|
||||
episode: 14
|
||||
})
|
||||
})
|
||||
|
||||
it('can handle empty guide', async () => {
|
||||
const results = await parser({
|
||||
date,
|
||||
channel,
|
||||
content: ''
|
||||
})
|
||||
expect(results).toMatchObject([])
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user