mirror of
https://github.com/iptv-org/epg
synced 2026-04-30 14:36:58 -04:00
Replace LF endings with CRLF
This commit is contained in:
@@ -1,45 +1,45 @@
|
|||||||
import { Logger, Storage } from '@freearhey/core'
|
import { Logger, Storage } from '@freearhey/core'
|
||||||
import { SITES_DIR } from '../../constants'
|
import { SITES_DIR } from '../../constants'
|
||||||
import { pathToFileURL } from 'node:url'
|
import { pathToFileURL } from 'node:url'
|
||||||
import { program } from 'commander'
|
import { program } from 'commander'
|
||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
|
|
||||||
program.argument('<site>', 'Domain name of the site').parse(process.argv)
|
program.argument('<site>', 'Domain name of the site').parse(process.argv)
|
||||||
|
|
||||||
const domain = program.args[0]
|
const domain = program.args[0]
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const storage = new Storage(SITES_DIR)
|
const storage = new Storage(SITES_DIR)
|
||||||
const logger = new Logger()
|
const logger = new Logger()
|
||||||
|
|
||||||
logger.info(`Initializing "${domain}"...\r\n`)
|
logger.info(`Initializing "${domain}"...\r\n`)
|
||||||
|
|
||||||
const dir = domain
|
const dir = domain
|
||||||
if (await storage.exists(dir)) {
|
if (await storage.exists(dir)) {
|
||||||
throw new Error(`Folder "${dir}" already exists`)
|
throw new Error(`Folder "${dir}" already exists`)
|
||||||
}
|
}
|
||||||
|
|
||||||
await storage.createDir(dir)
|
await storage.createDir(dir)
|
||||||
|
|
||||||
logger.info(`Creating "${dir}/${domain}.test.js"...`)
|
logger.info(`Creating "${dir}/${domain}.test.js"...`)
|
||||||
const testTemplate = fs.readFileSync(pathToFileURL('scripts/templates/_test.js'), {
|
const testTemplate = fs.readFileSync(pathToFileURL('scripts/templates/_test.js'), {
|
||||||
encoding: 'utf8'
|
encoding: 'utf8'
|
||||||
})
|
})
|
||||||
await storage.save(`${dir}/${domain}.test.js`, testTemplate.replace(/<DOMAIN>/g, domain))
|
await storage.save(`${dir}/${domain}.test.js`, testTemplate.replace(/<DOMAIN>/g, domain))
|
||||||
|
|
||||||
logger.info(`Creating "${dir}/${domain}.config.js"...`)
|
logger.info(`Creating "${dir}/${domain}.config.js"...`)
|
||||||
const configTemplate = fs.readFileSync(pathToFileURL('scripts/templates/_config.js'), {
|
const configTemplate = fs.readFileSync(pathToFileURL('scripts/templates/_config.js'), {
|
||||||
encoding: 'utf8'
|
encoding: 'utf8'
|
||||||
})
|
})
|
||||||
await storage.save(`${dir}/${domain}.config.js`, configTemplate.replace(/<DOMAIN>/g, domain))
|
await storage.save(`${dir}/${domain}.config.js`, configTemplate.replace(/<DOMAIN>/g, domain))
|
||||||
|
|
||||||
logger.info(`Creating "${dir}/readme.md"...`)
|
logger.info(`Creating "${dir}/readme.md"...`)
|
||||||
const readmeTemplate = fs.readFileSync(pathToFileURL('scripts/templates/_readme.md'), {
|
const readmeTemplate = fs.readFileSync(pathToFileURL('scripts/templates/_readme.md'), {
|
||||||
encoding: 'utf8'
|
encoding: 'utf8'
|
||||||
})
|
})
|
||||||
await storage.save(`${dir}/readme.md`, readmeTemplate.replace(/<DOMAIN>/g, domain))
|
await storage.save(`${dir}/readme.md`, readmeTemplate.replace(/<DOMAIN>/g, domain))
|
||||||
|
|
||||||
logger.info('\r\nDone')
|
logger.info('\r\nDone')
|
||||||
}
|
}
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
export const ROOT_DIR = process.env.ROOT_DIR || '.'
|
export const ROOT_DIR = process.env.ROOT_DIR || '.'
|
||||||
export const SITES_DIR = process.env.SITES_DIR || './sites'
|
export const SITES_DIR = process.env.SITES_DIR || './sites'
|
||||||
export const GUIDES_DIR = process.env.GUIDES_DIR || './guides'
|
export const GUIDES_DIR = process.env.GUIDES_DIR || './guides'
|
||||||
export const DATA_DIR = process.env.DATA_DIR || './temp/data'
|
export const DATA_DIR = process.env.DATA_DIR || './temp/data'
|
||||||
export const API_DIR = process.env.API_DIR || '.api'
|
export const API_DIR = process.env.API_DIR || '.api'
|
||||||
export const DOT_SITES_DIR = process.env.DOT_SITES_DIR || './.sites'
|
export const DOT_SITES_DIR = process.env.DOT_SITES_DIR || './.sites'
|
||||||
export const TESTING = process.env.NODE_ENV === 'test' ? true : false
|
export const TESTING = process.env.NODE_ENV === 'test' ? true : false
|
||||||
export const OWNER = 'iptv-org'
|
export const OWNER = 'iptv-org'
|
||||||
export const REPO = 'epg'
|
export const REPO = 'epg'
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import axios, { AxiosInstance, AxiosResponse, AxiosRequestConfig } from 'axios'
|
import axios, { AxiosInstance, AxiosResponse, AxiosRequestConfig } from 'axios'
|
||||||
|
|
||||||
export class ApiClient {
|
export class ApiClient {
|
||||||
instance: AxiosInstance
|
instance: AxiosInstance
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.instance = axios.create({
|
this.instance = axios.create({
|
||||||
baseURL: 'https://iptv-org.github.io/api',
|
baseURL: 'https://iptv-org.github.io/api',
|
||||||
responseType: 'stream'
|
responseType: 'stream'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
get(url: string, options: AxiosRequestConfig): Promise<AxiosResponse> {
|
get(url: string, options: AxiosRequestConfig): Promise<AxiosResponse> {
|
||||||
return this.instance.get(url, options)
|
return this.instance.get(url, options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import utc from 'dayjs/plugin/utc'
|
import utc from 'dayjs/plugin/utc'
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = {}
|
const date = {}
|
||||||
|
|
||||||
date.getUTC = function (d = null) {
|
date.getUTC = function (d = null) {
|
||||||
if (typeof d === 'string') return dayjs.utc(d).startOf('d')
|
if (typeof d === 'string') return dayjs.utc(d).startOf('d')
|
||||||
|
|
||||||
return dayjs.utc().startOf('d')
|
return dayjs.utc().startOf('d')
|
||||||
}
|
}
|
||||||
|
|
||||||
export default date
|
export default date
|
||||||
|
|||||||
@@ -1,34 +1,34 @@
|
|||||||
import { Dictionary } from '@freearhey/core'
|
import { Dictionary } from '@freearhey/core'
|
||||||
import { Issue } from '../models'
|
import { Issue } from '../models'
|
||||||
|
|
||||||
const FIELDS = new Dictionary({
|
const FIELDS = new Dictionary({
|
||||||
Site: 'site'
|
Site: 'site'
|
||||||
})
|
})
|
||||||
|
|
||||||
export class IssueParser {
|
export class IssueParser {
|
||||||
parse(issue: { number: number; body: string; labels: { name: string }[] }): Issue {
|
parse(issue: { number: number; body: string; labels: { name: string }[] }): Issue {
|
||||||
const fields = issue.body.split('###')
|
const fields = issue.body.split('###')
|
||||||
|
|
||||||
const data = new Dictionary()
|
const data = new Dictionary()
|
||||||
fields.forEach((field: string) => {
|
fields.forEach((field: string) => {
|
||||||
const parsed = field.split(/\r?\n/).filter(Boolean)
|
const parsed = field.split(/\r?\n/).filter(Boolean)
|
||||||
let _label = parsed.shift()
|
let _label = parsed.shift()
|
||||||
_label = _label ? _label.trim() : ''
|
_label = _label ? _label.trim() : ''
|
||||||
let _value = parsed.join('\r\n')
|
let _value = parsed.join('\r\n')
|
||||||
_value = _value ? _value.trim() : ''
|
_value = _value ? _value.trim() : ''
|
||||||
|
|
||||||
if (!_label || !_value) return data
|
if (!_label || !_value) return data
|
||||||
|
|
||||||
const id: string = FIELDS.get(_label)
|
const id: string = FIELDS.get(_label)
|
||||||
const value: string = _value === '_No response_' || _value === 'None' ? '' : _value
|
const value: string = _value === '_No response_' || _value === 'None' ? '' : _value
|
||||||
|
|
||||||
if (!id) return
|
if (!id) return
|
||||||
|
|
||||||
data.set(id, value)
|
data.set(id, value)
|
||||||
})
|
})
|
||||||
|
|
||||||
const labels = issue.labels.map(label => label.name)
|
const labels = issue.labels.map(label => label.name)
|
||||||
|
|
||||||
return new Issue({ number: issue.number, labels, data })
|
return new Issue({ number: issue.number, labels, data })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
site: '<DOMAIN>',
|
site: '<DOMAIN>',
|
||||||
url({ channel, date }) {
|
url({ channel, date }) {
|
||||||
return `https://example.com/api/${channel.site_id}/${date.format('YYYY-MM-DD')}`
|
return `https://example.com/api/${channel.site_id}/${date.format('YYYY-MM-DD')}`
|
||||||
},
|
},
|
||||||
parser({ content }) {
|
parser({ content }) {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(content)
|
return JSON.parse(content)
|
||||||
} catch {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
channels() {
|
channels() {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +1,38 @@
|
|||||||
const { parser, url } = require('./<DOMAIN>.config.js')
|
const { parser, url } = require('./<DOMAIN>.config.js')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2025-01-12', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-01-12', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = { site_id: 'bbc1' }
|
const channel = { site_id: 'bbc1' }
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ channel, date })).toBe('https://example.com/api/bbc1/2025-01-12')
|
expect(url({ channel, date })).toBe('https://example.com/api/bbc1/2025-01-12')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content =
|
const content =
|
||||||
'[{"title":"Program 1","start":"2025-01-12T00:00:00.000Z","stop":"2025-01-12T00:30:00.000Z"},{"title":"Program 2","start":"2025-01-12T00:30:00.000Z","stop":"2025-01-12T01:00:00.000Z"}]'
|
'[{"title":"Program 1","start":"2025-01-12T00:00:00.000Z","stop":"2025-01-12T00:30:00.000Z"},{"title":"Program 2","start":"2025-01-12T00:30:00.000Z","stop":"2025-01-12T01:00:00.000Z"}]'
|
||||||
|
|
||||||
const results = parser({ content })
|
const results = parser({ content })
|
||||||
|
|
||||||
expect(results.length).toBe(2)
|
expect(results.length).toBe(2)
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
title: 'Program 1',
|
title: 'Program 1',
|
||||||
start: '2025-01-12T00:00:00.000Z',
|
start: '2025-01-12T00:00:00.000Z',
|
||||||
stop: '2025-01-12T00:30:00.000Z'
|
stop: '2025-01-12T00:30:00.000Z'
|
||||||
})
|
})
|
||||||
expect(results[1]).toMatchObject({
|
expect(results[1]).toMatchObject({
|
||||||
title: 'Program 2',
|
title: 'Program 2',
|
||||||
start: '2025-01-12T00:30:00.000Z',
|
start: '2025-01-12T00:30:00.000Z',
|
||||||
stop: '2025-01-12T01:00:00.000Z'
|
stop: '2025-01-12T01:00:00.000Z'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const results = parser({ content: '' })
|
const results = parser({ content: '' })
|
||||||
|
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
2
scripts/types/langs.d.ts
vendored
2
scripts/types/langs.d.ts
vendored
@@ -1 +1 @@
|
|||||||
declare module 'langs'
|
declare module 'langs'
|
||||||
|
|||||||
@@ -1,69 +1,69 @@
|
|||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: '9tv.co.il',
|
site: '9tv.co.il',
|
||||||
days: 2,
|
days: 2,
|
||||||
url: function ({ date }) {
|
url: function ({ date }) {
|
||||||
return `https://www.9tv.co.il/BroadcastSchedule/getBrodcastSchedule?date=${date.format(
|
return `https://www.9tv.co.il/BroadcastSchedule/getBrodcastSchedule?date=${date.format(
|
||||||
'DD/MM/YYYY 00:00:00'
|
'DD/MM/YYYY 00:00:00'
|
||||||
)}`
|
)}`
|
||||||
},
|
},
|
||||||
parser: function ({ content, date }) {
|
parser: function ({ content, date }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
const items = parseItems(content)
|
const items = parseItems(content)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const prev = programs[programs.length - 1]
|
const prev = programs[programs.length - 1]
|
||||||
const $item = cheerio.load(item)
|
const $item = cheerio.load(item)
|
||||||
const start = parseStart($item, date)
|
const start = parseStart($item, date)
|
||||||
if (prev) prev.stop = start
|
if (prev) prev.stop = start
|
||||||
const stop = start.add(1, 'h')
|
const stop = start.add(1, 'h')
|
||||||
programs.push({
|
programs.push({
|
||||||
title: parseTitle($item),
|
title: parseTitle($item),
|
||||||
image: parseImage($item),
|
image: parseImage($item),
|
||||||
description: parseDescription($item),
|
description: parseDescription($item),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart($item, date) {
|
function parseStart($item, date) {
|
||||||
let time = $item('a > div.guide_list_time').text().trim()
|
let time = $item('a > div.guide_list_time').text().trim()
|
||||||
|
|
||||||
return dayjs.tz(`${date.format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm', 'Asia/Jerusalem')
|
return dayjs.tz(`${date.format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm', 'Asia/Jerusalem')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseImage($item) {
|
function parseImage($item) {
|
||||||
const backgroundImage = $item('a > div.guide_info_group > div.guide_info_pict').css(
|
const backgroundImage = $item('a > div.guide_info_group > div.guide_info_pict').css(
|
||||||
'background-image'
|
'background-image'
|
||||||
)
|
)
|
||||||
if (!backgroundImage) return null
|
if (!backgroundImage) return null
|
||||||
const [, relativePath] = backgroundImage.match(/url\((.*)\)/) || [null, null]
|
const [, relativePath] = backgroundImage.match(/url\((.*)\)/) || [null, null]
|
||||||
|
|
||||||
return relativePath ? `https://www.9tv.co.il${relativePath}` : null
|
return relativePath ? `https://www.9tv.co.il${relativePath}` : null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDescription($item) {
|
function parseDescription($item) {
|
||||||
return $item('a > div.guide_info_group > div.guide_txt_group > div').text().trim()
|
return $item('a > div.guide_info_group > div.guide_txt_group > div').text().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTitle($item) {
|
function parseTitle($item) {
|
||||||
return $item('a > div.guide_info_group > div.guide_txt_group > h3').text().trim()
|
return $item('a > div.guide_info_group > div.guide_txt_group > h3').text().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content) {
|
function parseItems(content) {
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
|
|
||||||
return $('li').toArray()
|
return $('li').toArray()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,122 +1,122 @@
|
|||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'abc.net.au',
|
site: 'abc.net.au',
|
||||||
days: 3,
|
days: 3,
|
||||||
request: {
|
request: {
|
||||||
cache: {
|
cache: {
|
||||||
ttl: 60 * 60 * 1000 // 1 hour
|
ttl: 60 * 60 * 1000 // 1 hour
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
url({ date, channel }) {
|
url({ date, channel }) {
|
||||||
const [region] = channel.site_id.split('#')
|
const [region] = channel.site_id.split('#')
|
||||||
|
|
||||||
return `https://cdn.iview.abc.net.au/epg/processed/${region}_${date.format('YYYY-MM-DD')}.json`
|
return `https://cdn.iview.abc.net.au/epg/processed/${region}_${date.format('YYYY-MM-DD')}.json`
|
||||||
},
|
},
|
||||||
parser({ content, channel }) {
|
parser({ content, channel }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
const items = parseItems(content, channel)
|
const items = parseItems(content, channel)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
sub_title: item.episode_title,
|
sub_title: item.episode_title,
|
||||||
category: item.genres,
|
category: item.genres,
|
||||||
description: item.description,
|
description: item.description,
|
||||||
season: parseSeason(item),
|
season: parseSeason(item),
|
||||||
episode: parseEpisode(item),
|
episode: parseEpisode(item),
|
||||||
rating: parseRating(item),
|
rating: parseRating(item),
|
||||||
image: parseImage(item),
|
image: parseImage(item),
|
||||||
start: parseTime(item.start_time),
|
start: parseTime(item.start_time),
|
||||||
stop: parseTime(item.end_time)
|
stop: parseTime(item.end_time)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels({ region = 'syd' }) {
|
async channels({ region = 'syd' }) {
|
||||||
const now = dayjs()
|
const now = dayjs()
|
||||||
const regions = {
|
const regions = {
|
||||||
syd: 'Sydney',
|
syd: 'Sydney',
|
||||||
mel: 'Melbourne',
|
mel: 'Melbourne',
|
||||||
bri: 'Brisbane',
|
bri: 'Brisbane',
|
||||||
gc: 'GoldCoast',
|
gc: 'GoldCoast',
|
||||||
per: 'Perth',
|
per: 'Perth',
|
||||||
adl: 'Adelaide',
|
adl: 'Adelaide',
|
||||||
hbr: 'Hobart',
|
hbr: 'Hobart',
|
||||||
drw: 'Darwin',
|
drw: 'Darwin',
|
||||||
cbr: 'Canberra',
|
cbr: 'Canberra',
|
||||||
nsw: 'New South Wales',
|
nsw: 'New South Wales',
|
||||||
vic: 'Victoria',
|
vic: 'Victoria',
|
||||||
tsv: 'Townsville',
|
tsv: 'Townsville',
|
||||||
qld: 'Queensland',
|
qld: 'Queensland',
|
||||||
wa: 'Western Australia',
|
wa: 'Western Australia',
|
||||||
sa: 'South Australia',
|
sa: 'South Australia',
|
||||||
tas: 'Tasmania',
|
tas: 'Tasmania',
|
||||||
nt: 'Northern Territory'
|
nt: 'Northern Territory'
|
||||||
}
|
}
|
||||||
|
|
||||||
let channels = []
|
let channels = []
|
||||||
const regionName = regions[region]
|
const regionName = regions[region]
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(
|
.get(
|
||||||
`https://cdn.iview.abc.net.au/epg/processed/${regionName}_${now.format('YYYY-MM-DD')}.json`
|
`https://cdn.iview.abc.net.au/epg/processed/${regionName}_${now.format('YYYY-MM-DD')}.json`
|
||||||
)
|
)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
for (let item of data.schedule) {
|
for (let item of data.schedule) {
|
||||||
channels.push({
|
channels.push({
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
site_id: `${regionName}#${item.channel}`,
|
site_id: `${regionName}#${item.channel}`,
|
||||||
name: item.channel
|
name: item.channel
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return channels
|
return channels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, channel) {
|
function parseItems(content, channel) {
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
if (!data) return []
|
if (!data) return []
|
||||||
if (!Array.isArray(data.schedule)) return []
|
if (!Array.isArray(data.schedule)) return []
|
||||||
|
|
||||||
const [, channelId] = channel.site_id.split('#')
|
const [, channelId] = channel.site_id.split('#')
|
||||||
const channelData = data.schedule.find(i => i.channel == channelId)
|
const channelData = data.schedule.find(i => i.channel == channelId)
|
||||||
return channelData.listing && Array.isArray(channelData.listing) ? channelData.listing : []
|
return channelData.listing && Array.isArray(channelData.listing) ? channelData.listing : []
|
||||||
} catch {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseSeason(item) {
|
function parseSeason(item) {
|
||||||
return item.series_num || null
|
return item.series_num || null
|
||||||
}
|
}
|
||||||
function parseEpisode(item) {
|
function parseEpisode(item) {
|
||||||
return item.episode_num || null
|
return item.episode_num || null
|
||||||
}
|
}
|
||||||
function parseTime(time) {
|
function parseTime(time) {
|
||||||
return dayjs.tz(time, 'YYYY-MM-DD HH:mm', 'Australia/Sydney')
|
return dayjs.tz(time, 'YYYY-MM-DD HH:mm', 'Australia/Sydney')
|
||||||
}
|
}
|
||||||
function parseImage(item) {
|
function parseImage(item) {
|
||||||
return item.image_file
|
return item.image_file
|
||||||
? `https://www.abc.net.au/tv/common/images/publicity/${item.image_file}`
|
? `https://www.abc.net.au/tv/common/images/publicity/${item.image_file}`
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
function parseRating(item) {
|
function parseRating(item) {
|
||||||
return item.rating
|
return item.rating
|
||||||
? {
|
? {
|
||||||
system: 'ACB',
|
system: 'ACB',
|
||||||
value: item.rating
|
value: item.rating
|
||||||
}
|
}
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,51 +1,51 @@
|
|||||||
const { parser, url } = require('./abc.net.au.config.js')
|
const { parser, url } = require('./abc.net.au.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2025-02-04', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-02-04', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = { site_id: 'Sydney#ABC1' }
|
const channel = { site_id: 'Sydney#ABC1' }
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ date, channel })).toBe(
|
expect(url({ date, channel })).toBe(
|
||||||
'https://cdn.iview.abc.net.au/epg/processed/Sydney_2025-02-04.json'
|
'https://cdn.iview.abc.net.au/epg/processed/Sydney_2025-02-04.json'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
||||||
const results = parser({ content, channel }).map(p => {
|
const results = parser({ content, channel }).map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(30)
|
expect(results.length).toBe(30)
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
title: "Julia Zemiro's Home Delivery",
|
title: "Julia Zemiro's Home Delivery",
|
||||||
sub_title: 'Maggie Beer',
|
sub_title: 'Maggie Beer',
|
||||||
description:
|
description:
|
||||||
"The kitchen Maggie Beer made famous in The Cook and the Chef may be in the heart of the Barossa Valley, but our most beloved foodie meets up with Julia where she grew up in Sydney's Lakemba.",
|
"The kitchen Maggie Beer made famous in The Cook and the Chef may be in the heart of the Barossa Valley, but our most beloved foodie meets up with Julia where she grew up in Sydney's Lakemba.",
|
||||||
category: ['Entertainment', 'Factual'],
|
category: ['Entertainment', 'Factual'],
|
||||||
rating: {
|
rating: {
|
||||||
system: 'ACB',
|
system: 'ACB',
|
||||||
value: 'G'
|
value: 'G'
|
||||||
},
|
},
|
||||||
season: null,
|
season: null,
|
||||||
episode: null,
|
episode: null,
|
||||||
image: 'https://www.abc.net.au/tv/common/images/publicity/LE1761H002S00_460.jpg',
|
image: 'https://www.abc.net.au/tv/common/images/publicity/LE1761H002S00_460.jpg',
|
||||||
start: '2025-02-03T12:40:00.000Z',
|
start: '2025-02-03T12:40:00.000Z',
|
||||||
stop: '2025-02-03T13:09:00.000Z'
|
stop: '2025-02-03T13:09:00.000Z'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const results = parser({
|
const results = parser({
|
||||||
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.html')),
|
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.html')),
|
||||||
channel
|
channel
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,65 +1,65 @@
|
|||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'allente.dk',
|
site: 'allente.dk',
|
||||||
days: 2,
|
days: 2,
|
||||||
request: {
|
request: {
|
||||||
cache: {
|
cache: {
|
||||||
ttl: 60 * 60 * 1000 // 1 hour
|
ttl: 60 * 60 * 1000 // 1 hour
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
url({ date }) {
|
url({ date }) {
|
||||||
return `https://cs-vcb.allente.dk/epg/events?date=${date.format('YYYY-MM-DD')}`
|
return `https://cs-vcb.allente.dk/epg/events?date=${date.format('YYYY-MM-DD')}`
|
||||||
},
|
},
|
||||||
parser({ content, channel }) {
|
parser({ content, channel }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
const items = parseItems(content, channel)
|
const items = parseItems(content, channel)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
if (!item.details) return
|
if (!item.details) return
|
||||||
const start = dayjs(item.time)
|
const start = dayjs(item.time)
|
||||||
const stop = start.add(item.details.duration, 'm')
|
const stop = start.add(item.details.duration, 'm')
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
category: item.details.categories,
|
category: item.details.categories,
|
||||||
description: item.details.description,
|
description: item.details.description,
|
||||||
image: item.details.image,
|
image: item.details.image,
|
||||||
season: parseSeason(item),
|
season: parseSeason(item),
|
||||||
episode: parseEpisode(item),
|
episode: parseEpisode(item),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(`https://cs-vcb.allente.dk/epg/events?date=${dayjs().format('YYYY-MM-DD')}`)
|
.get(`https://cs-vcb.allente.dk/epg/events?date=${dayjs().format('YYYY-MM-DD')}`)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
return data.channels.map(item => {
|
return data.channels.map(item => {
|
||||||
return {
|
return {
|
||||||
lang: 'da',
|
lang: 'da',
|
||||||
site_id: item.id,
|
site_id: item.id,
|
||||||
name: item.name
|
name: item.name
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, channel) {
|
function parseItems(content, channel) {
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
if (!data || !Array.isArray(data.channels)) return []
|
if (!data || !Array.isArray(data.channels)) return []
|
||||||
const channelData = data.channels.find(i => i.id === channel.site_id)
|
const channelData = data.channels.find(i => i.id === channel.site_id)
|
||||||
|
|
||||||
return channelData && Array.isArray(channelData.events) ? channelData.events : []
|
return channelData && Array.isArray(channelData.events) ? channelData.events : []
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseSeason(item) {
|
function parseSeason(item) {
|
||||||
return item.details.season || null
|
return item.details.season || null
|
||||||
}
|
}
|
||||||
function parseEpisode(item) {
|
function parseEpisode(item) {
|
||||||
return item.details.episode || null
|
return item.details.episode || null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,65 +1,65 @@
|
|||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'allente.fi',
|
site: 'allente.fi',
|
||||||
days: 2,
|
days: 2,
|
||||||
request: {
|
request: {
|
||||||
cache: {
|
cache: {
|
||||||
ttl: 60 * 60 * 1000 // 1 hour
|
ttl: 60 * 60 * 1000 // 1 hour
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
url({ date }) {
|
url({ date }) {
|
||||||
return `https://cs-vcb.allente.fi/epg/events?date=${date.format('YYYY-MM-DD')}`
|
return `https://cs-vcb.allente.fi/epg/events?date=${date.format('YYYY-MM-DD')}`
|
||||||
},
|
},
|
||||||
parser({ content, channel }) {
|
parser({ content, channel }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
const items = parseItems(content, channel)
|
const items = parseItems(content, channel)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
if (!item.details) return
|
if (!item.details) return
|
||||||
const start = dayjs(item.time)
|
const start = dayjs(item.time)
|
||||||
const stop = start.add(item.details.duration, 'm')
|
const stop = start.add(item.details.duration, 'm')
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
category: item.details.categories,
|
category: item.details.categories,
|
||||||
description: item.details.description,
|
description: item.details.description,
|
||||||
image: item.details.image,
|
image: item.details.image,
|
||||||
season: parseSeason(item),
|
season: parseSeason(item),
|
||||||
episode: parseEpisode(item),
|
episode: parseEpisode(item),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(`https://cs-vcb.allente.fi/epg/events?date=${dayjs().format('YYYY-MM-DD')}`)
|
.get(`https://cs-vcb.allente.fi/epg/events?date=${dayjs().format('YYYY-MM-DD')}`)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
return data.channels.map(item => {
|
return data.channels.map(item => {
|
||||||
return {
|
return {
|
||||||
lang: 'fi',
|
lang: 'fi',
|
||||||
site_id: item.id,
|
site_id: item.id,
|
||||||
name: item.name
|
name: item.name
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, channel) {
|
function parseItems(content, channel) {
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
if (!data || !Array.isArray(data.channels)) return []
|
if (!data || !Array.isArray(data.channels)) return []
|
||||||
const channelData = data.channels.find(i => i.id === channel.site_id)
|
const channelData = data.channels.find(i => i.id === channel.site_id)
|
||||||
|
|
||||||
return channelData && Array.isArray(channelData.events) ? channelData.events : []
|
return channelData && Array.isArray(channelData.events) ? channelData.events : []
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseSeason(item) {
|
function parseSeason(item) {
|
||||||
return item.details.season || null
|
return item.details.season || null
|
||||||
}
|
}
|
||||||
function parseEpisode(item) {
|
function parseEpisode(item) {
|
||||||
return item.details.episode || null
|
return item.details.episode || null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,65 +1,65 @@
|
|||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'allente.no',
|
site: 'allente.no',
|
||||||
days: 2,
|
days: 2,
|
||||||
request: {
|
request: {
|
||||||
cache: {
|
cache: {
|
||||||
ttl: 60 * 60 * 1000 // 1 hour
|
ttl: 60 * 60 * 1000 // 1 hour
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
url({ date }) {
|
url({ date }) {
|
||||||
return `https://cs-vcb.allente.no/epg/events?date=${date.format('YYYY-MM-DD')}`
|
return `https://cs-vcb.allente.no/epg/events?date=${date.format('YYYY-MM-DD')}`
|
||||||
},
|
},
|
||||||
parser({ content, channel }) {
|
parser({ content, channel }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
const items = parseItems(content, channel)
|
const items = parseItems(content, channel)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
if (!item.details) return
|
if (!item.details) return
|
||||||
const start = dayjs(item.time)
|
const start = dayjs(item.time)
|
||||||
const stop = start.add(item.details.duration, 'm')
|
const stop = start.add(item.details.duration, 'm')
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
category: item.details.categories,
|
category: item.details.categories,
|
||||||
description: item.details.description,
|
description: item.details.description,
|
||||||
image: item.details.image,
|
image: item.details.image,
|
||||||
season: parseSeason(item),
|
season: parseSeason(item),
|
||||||
episode: parseEpisode(item),
|
episode: parseEpisode(item),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(`https://cs-vcb.allente.no/epg/events?date=${dayjs().format('YYYY-MM-DD')}`)
|
.get(`https://cs-vcb.allente.no/epg/events?date=${dayjs().format('YYYY-MM-DD')}`)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
return data.channels.map(item => {
|
return data.channels.map(item => {
|
||||||
return {
|
return {
|
||||||
lang: 'no',
|
lang: 'no',
|
||||||
site_id: item.id,
|
site_id: item.id,
|
||||||
name: item.name
|
name: item.name
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, channel) {
|
function parseItems(content, channel) {
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
if (!data || !Array.isArray(data.channels)) return []
|
if (!data || !Array.isArray(data.channels)) return []
|
||||||
const channelData = data.channels.find(i => i.id === channel.site_id)
|
const channelData = data.channels.find(i => i.id === channel.site_id)
|
||||||
|
|
||||||
return channelData && Array.isArray(channelData.events) ? channelData.events : []
|
return channelData && Array.isArray(channelData.events) ? channelData.events : []
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseSeason(item) {
|
function parseSeason(item) {
|
||||||
return item.details.season || null
|
return item.details.season || null
|
||||||
}
|
}
|
||||||
function parseEpisode(item) {
|
function parseEpisode(item) {
|
||||||
return item.details.episode || null
|
return item.details.episode || null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,65 +1,65 @@
|
|||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'allente.se',
|
site: 'allente.se',
|
||||||
days: 2,
|
days: 2,
|
||||||
request: {
|
request: {
|
||||||
cache: {
|
cache: {
|
||||||
ttl: 60 * 60 * 1000 // 1 hour
|
ttl: 60 * 60 * 1000 // 1 hour
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
url({ date }) {
|
url({ date }) {
|
||||||
return `https://cs-vcb.allente.se/epg/events?date=${date.format('YYYY-MM-DD')}`
|
return `https://cs-vcb.allente.se/epg/events?date=${date.format('YYYY-MM-DD')}`
|
||||||
},
|
},
|
||||||
parser({ content, channel }) {
|
parser({ content, channel }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
const items = parseItems(content, channel)
|
const items = parseItems(content, channel)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
if (!item.details) return
|
if (!item.details) return
|
||||||
const start = dayjs(item.time)
|
const start = dayjs(item.time)
|
||||||
const stop = start.add(item.details.duration, 'm')
|
const stop = start.add(item.details.duration, 'm')
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
category: item.details.categories,
|
category: item.details.categories,
|
||||||
description: item.details.description,
|
description: item.details.description,
|
||||||
image: item.details.image,
|
image: item.details.image,
|
||||||
season: parseSeason(item),
|
season: parseSeason(item),
|
||||||
episode: parseEpisode(item),
|
episode: parseEpisode(item),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(`https://cs-vcb.allente.se/epg/events?date=${dayjs().format('YYYY-MM-DD')}`)
|
.get(`https://cs-vcb.allente.se/epg/events?date=${dayjs().format('YYYY-MM-DD')}`)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
return data.channels.map(item => {
|
return data.channels.map(item => {
|
||||||
return {
|
return {
|
||||||
lang: 'sv',
|
lang: 'sv',
|
||||||
site_id: item.id,
|
site_id: item.id,
|
||||||
name: item.name
|
name: item.name
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, channel) {
|
function parseItems(content, channel) {
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
if (!data || !Array.isArray(data.channels)) return []
|
if (!data || !Array.isArray(data.channels)) return []
|
||||||
const channelData = data.channels.find(i => i.id === channel.site_id)
|
const channelData = data.channels.find(i => i.id === channel.site_id)
|
||||||
|
|
||||||
return channelData && Array.isArray(channelData.events) ? channelData.events : []
|
return channelData && Array.isArray(channelData.events) ? channelData.events : []
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseSeason(item) {
|
function parseSeason(item) {
|
||||||
return item.details.season || null
|
return item.details.season || null
|
||||||
}
|
}
|
||||||
function parseEpisode(item) {
|
function parseEpisode(item) {
|
||||||
return item.details.episode || null
|
return item.details.episode || null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,59 +1,59 @@
|
|||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const { DateTime } = require('luxon')
|
const { DateTime } = require('luxon')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'andorradifusio.ad',
|
site: 'andorradifusio.ad',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ channel }) {
|
url({ channel }) {
|
||||||
return `https://www.andorradifusio.ad/programacio/${channel.site_id}`
|
return `https://www.andorradifusio.ad/programacio/${channel.site_id}`
|
||||||
},
|
},
|
||||||
parser({ content, date }) {
|
parser({ content, date }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
const items = parseItems(content, date)
|
const items = parseItems(content, date)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const prev = programs[programs.length - 1]
|
const prev = programs[programs.length - 1]
|
||||||
let start = parseStart(item, date)
|
let start = parseStart(item, date)
|
||||||
if (prev) {
|
if (prev) {
|
||||||
if (start < prev.start) {
|
if (start < prev.start) {
|
||||||
start = start.plus({ days: 1 })
|
start = start.plus({ days: 1 })
|
||||||
date = date.add(1, 'd')
|
date = date.add(1, 'd')
|
||||||
}
|
}
|
||||||
prev.stop = start
|
prev.stop = start
|
||||||
}
|
}
|
||||||
const stop = start.plus({ hours: 1 })
|
const stop = start.plus({ hours: 1 })
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart(item, date) {
|
function parseStart(item, date) {
|
||||||
const dateString = `${date.format('MM/DD/YYYY')} ${item.time}`
|
const dateString = `${date.format('MM/DD/YYYY')} ${item.time}`
|
||||||
|
|
||||||
return DateTime.fromFormat(dateString, 'MM/dd/yyyy HH:mm', { zone: 'Europe/Madrid' }).toUTC()
|
return DateTime.fromFormat(dateString, 'MM/dd/yyyy HH:mm', { zone: 'Europe/Madrid' }).toUTC()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, date) {
|
function parseItems(content, date) {
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
const day = DateTime.fromMillis(date.valueOf()).setLocale('ca').toFormat('dd LLLL').toLowerCase()
|
const day = DateTime.fromMillis(date.valueOf()).setLocale('ca').toFormat('dd LLLL').toLowerCase()
|
||||||
const column = $('.programacio-dia > h3 > .dia')
|
const column = $('.programacio-dia > h3 > .dia')
|
||||||
.filter((i, el) => $(el).text() === day.slice(0, 6) + '.')
|
.filter((i, el) => $(el).text() === day.slice(0, 6) + '.')
|
||||||
.first()
|
.first()
|
||||||
.parent()
|
.parent()
|
||||||
.parent()
|
.parent()
|
||||||
const items = []
|
const items = []
|
||||||
const titles = column.find('p').toArray()
|
const titles = column.find('p').toArray()
|
||||||
column.find('h4').each((i, time) => {
|
column.find('h4').each((i, time) => {
|
||||||
items.push({
|
items.push({
|
||||||
time: $(time).text(),
|
time: $(time).text(),
|
||||||
title: $(titles[i]).text()
|
title: $(titles[i]).text()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +1,47 @@
|
|||||||
const { parser, url } = require('./andorradifusio.ad.config.js')
|
const { parser, url } = require('./andorradifusio.ad.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2023-06-07', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2023-06-07', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: 'atv',
|
site_id: 'atv',
|
||||||
xmltv_id: 'AndorraTV.ad'
|
xmltv_id: 'AndorraTV.ad'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ channel })).toBe('https://www.andorradifusio.ad/programacio/atv')
|
expect(url({ channel })).toBe('https://www.andorradifusio.ad/programacio/atv')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
||||||
const results = parser({ content, date }).map(p => {
|
const results = parser({ content, date }).map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2023-06-07T05:00:00.000Z',
|
start: '2023-06-07T05:00:00.000Z',
|
||||||
stop: '2023-06-07T06:00:00.000Z',
|
stop: '2023-06-07T06:00:00.000Z',
|
||||||
title: 'Club Piolet'
|
title: 'Club Piolet'
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[20]).toMatchObject({
|
expect(results[20]).toMatchObject({
|
||||||
start: '2023-06-07T23:00:00.000Z',
|
start: '2023-06-07T23:00:00.000Z',
|
||||||
stop: '2023-06-08T00:00:00.000Z',
|
stop: '2023-06-08T00:00:00.000Z',
|
||||||
title: 'Àrea Andorra Difusió'
|
title: 'Àrea Andorra Difusió'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const result = parser({
|
const result = parser({
|
||||||
date,
|
date,
|
||||||
content: '<!DOCTYPE html><html><head></head><body></body></html>'
|
content: '<!DOCTYPE html><html><head></head><body></body></html>'
|
||||||
})
|
})
|
||||||
expect(result).toMatchObject([])
|
expect(result).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,108 +1,108 @@
|
|||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
const API_ENDPOINT = 'https://cds-frontend.vera.com.uy/api-contenidos'
|
const API_ENDPOINT = 'https://cds-frontend.vera.com.uy/api-contenidos'
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'anteltv.com.uy',
|
site: 'anteltv.com.uy',
|
||||||
days: 2,
|
days: 2,
|
||||||
async url({ date, channel }) {
|
async url({ date, channel }) {
|
||||||
const session = await loadSessionDetails()
|
const session = await loadSessionDetails()
|
||||||
if (!session || !session.token) return null
|
if (!session || !session.token) return null
|
||||||
|
|
||||||
return `${API_ENDPOINT}/canales/epg/${
|
return `${API_ENDPOINT}/canales/epg/${
|
||||||
channel.site_id
|
channel.site_id
|
||||||
}?limit=500&dias_siguientes=0&fecha=${date.format('YYYY-MM-DD')}&token=${session.token}`
|
}?limit=500&dias_siguientes=0&fecha=${date.format('YYYY-MM-DD')}&token=${session.token}`
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
async headers() {
|
async headers() {
|
||||||
const session = await loadSessionDetails()
|
const session = await loadSessionDetails()
|
||||||
if (!session || !session.jwt) return null
|
if (!session || !session.jwt) return null
|
||||||
|
|
||||||
return {
|
return {
|
||||||
authorization: `Bearer ${session.jwt}`,
|
authorization: `Bearer ${session.jwt}`,
|
||||||
'x-frontend-id': 1196,
|
'x-frontend-id': 1196,
|
||||||
'x-service-id': 3,
|
'x-service-id': 3,
|
||||||
'x-system-id': 1
|
'x-system-id': 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
parser({ content }) {
|
parser({ content }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
let items = parseItems(content)
|
let items = parseItems(content)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.nombre_programa,
|
title: item.nombre_programa,
|
||||||
sub_title: item.subtitle,
|
sub_title: item.subtitle,
|
||||||
description: item.descripcion_programa,
|
description: item.descripcion_programa,
|
||||||
start: parseStart(item),
|
start: parseStart(item),
|
||||||
stop: parseStop(item)
|
stop: parseStop(item)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const session = await loadSessionDetails()
|
const session = await loadSessionDetails()
|
||||||
if (!session || !session.jwt || !session.token) return null
|
if (!session || !session.jwt || !session.token) return null
|
||||||
|
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(`${API_ENDPOINT}/listas/68?token=${session.token}`, {
|
.get(`${API_ENDPOINT}/listas/68?token=${session.token}`, {
|
||||||
headers: {
|
headers: {
|
||||||
authorization: `Bearer ${session.jwt}`,
|
authorization: `Bearer ${session.jwt}`,
|
||||||
'x-frontend-id': 1196,
|
'x-frontend-id': 1196,
|
||||||
'x-service-id': 3,
|
'x-service-id': 3,
|
||||||
'x-system-id': 1
|
'x-system-id': 1
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
return data.contenidos.map(c => {
|
return data.contenidos.map(c => {
|
||||||
return {
|
return {
|
||||||
lang: 'es',
|
lang: 'es',
|
||||||
site_id: c.public_id,
|
site_id: c.public_id,
|
||||||
name: c.nombre
|
name: c.nombre
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart(item) {
|
function parseStart(item) {
|
||||||
return dayjs.tz(item.fecha_hora_inicio, 'YYYY-MM-DD HH:mm:ss', 'America/Montevideo')
|
return dayjs.tz(item.fecha_hora_inicio, 'YYYY-MM-DD HH:mm:ss', 'America/Montevideo')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStop(item) {
|
function parseStop(item) {
|
||||||
return dayjs.tz(item.fecha_hora_fin, 'YYYY-MM-DD HH:mm:ss', 'America/Montevideo')
|
return dayjs.tz(item.fecha_hora_fin, 'YYYY-MM-DD HH:mm:ss', 'America/Montevideo')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content) {
|
function parseItems(content) {
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
if (!data || !Array.isArray(data.data)) return []
|
if (!data || !Array.isArray(data.data)) return []
|
||||||
|
|
||||||
return data.data
|
return data.data
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadSessionDetails() {
|
function loadSessionDetails() {
|
||||||
return axios
|
return axios
|
||||||
.post(
|
.post(
|
||||||
'https://veratv-be.vera.com.uy/api/sesiones',
|
'https://veratv-be.vera.com.uy/api/sesiones',
|
||||||
{
|
{
|
||||||
tipo: 'anonima'
|
tipo: 'anonima'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,85 +1,85 @@
|
|||||||
const { parser, url, request } = require('./anteltv.com.uy.config.js')
|
const { parser, url, request } = require('./anteltv.com.uy.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
jest.mock('axios')
|
jest.mock('axios')
|
||||||
|
|
||||||
axios.post.mockImplementation((url, data, opts) => {
|
axios.post.mockImplementation((url, data, opts) => {
|
||||||
if (
|
if (
|
||||||
url === 'https://veratv-be.vera.com.uy/api/sesiones' &&
|
url === 'https://veratv-be.vera.com.uy/api/sesiones' &&
|
||||||
JSON.stringify(opts.headers) ===
|
JSON.stringify(opts.headers) ===
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
}) &&
|
}) &&
|
||||||
JSON.stringify(data) ===
|
JSON.stringify(data) ===
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
tipo: 'anonima'
|
tipo: 'anonima'
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/session.json')))
|
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/session.json')))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/no_session.json')))
|
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/no_session.json')))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const date = dayjs.utc('2023-02-11', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2023-02-11', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: '2s6nd',
|
site_id: '2s6nd',
|
||||||
xmltv_id: 'Canal5.uy'
|
xmltv_id: 'Canal5.uy'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', async () => {
|
it('can generate valid url', async () => {
|
||||||
const result = await url({ date, channel })
|
const result = await url({ date, channel })
|
||||||
|
|
||||||
expect(result).toBe(
|
expect(result).toBe(
|
||||||
'https://cds-frontend.vera.com.uy/api-contenidos/canales/epg/2s6nd?limit=500&dias_siguientes=0&fecha=2023-02-11&token=MpDY52p1V6g511VSABp1015B'
|
'https://cds-frontend.vera.com.uy/api-contenidos/canales/epg/2s6nd?limit=500&dias_siguientes=0&fecha=2023-02-11&token=MpDY52p1V6g511VSABp1015B'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request headers', async () => {
|
it('can generate valid request headers', async () => {
|
||||||
const result = await request.headers()
|
const result = await request.headers()
|
||||||
|
|
||||||
expect(result).toMatchObject({
|
expect(result).toMatchObject({
|
||||||
authorization:
|
authorization:
|
||||||
'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOnsidGlwbyI6ImFub25pbWEifSwic3ViIjoiTXBEWTUycDFWNmc1MTFWU0FCcDEwMTVCIiwicHJuIjp7ImlkX3NlcnZpY2lvIjozLCJpZF9mcm9udGVuZCI6MTE5NiwiaXAiOiIxNzkuMjcuMTU0LjI0MiIsImlwX3JlZmVyZW5jaWFkYSI6IjE4OC4yNDIuNDguOTMiLCJpZF9kaXNwb3NpdGl2byI6MH0sImF1ZCI6IkFwcHNcL1dlYnMgRnJvbnRlbmRzIiwiaWF0IjoxNjc1ODI3NDU2LCJleHAiOjE2NzU4NDkwNTZ9.8bAQciQl5DOIZF7GgCl6ad-KJUSpqQREetozGv_IH5s',
|
'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOnsidGlwbyI6ImFub25pbWEifSwic3ViIjoiTXBEWTUycDFWNmc1MTFWU0FCcDEwMTVCIiwicHJuIjp7ImlkX3NlcnZpY2lvIjozLCJpZF9mcm9udGVuZCI6MTE5NiwiaXAiOiIxNzkuMjcuMTU0LjI0MiIsImlwX3JlZmVyZW5jaWFkYSI6IjE4OC4yNDIuNDguOTMiLCJpZF9kaXNwb3NpdGl2byI6MH0sImF1ZCI6IkFwcHNcL1dlYnMgRnJvbnRlbmRzIiwiaWF0IjoxNjc1ODI3NDU2LCJleHAiOjE2NzU4NDkwNTZ9.8bAQciQl5DOIZF7GgCl6ad-KJUSpqQREetozGv_IH5s',
|
||||||
'x-frontend-id': 1196,
|
'x-frontend-id': 1196,
|
||||||
'x-service-id': 3,
|
'x-service-id': 3,
|
||||||
'x-system-id': 1
|
'x-system-id': 1
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'), 'utf8')
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'), 'utf8')
|
||||||
let results = parser({ content })
|
let results = parser({ content })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2023-02-11T02:30:00.000Z',
|
start: '2023-02-11T02:30:00.000Z',
|
||||||
stop: '2023-02-11T04:00:00.000Z',
|
stop: '2023-02-11T04:00:00.000Z',
|
||||||
title: 'Canal 5 Noticias rep.',
|
title: 'Canal 5 Noticias rep.',
|
||||||
sub_title: '',
|
sub_title: '',
|
||||||
description: ''
|
description: ''
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const results = parser({
|
const results = parser({
|
||||||
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.json'), 'utf8')
|
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.json'), 'utf8')
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,59 +1,59 @@
|
|||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'antennaeurope.gr',
|
site: 'antennaeurope.gr',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ date }) {
|
url({ date }) {
|
||||||
return `https://www.antennaeurope.gr/el/tvguide.html?date=${date.format('YYYY-MM-DD')}`
|
return `https://www.antennaeurope.gr/el/tvguide.html?date=${date.format('YYYY-MM-DD')}`
|
||||||
},
|
},
|
||||||
parser({ content, date }) {
|
parser({ content, date }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
const items = parseItems(content, date)
|
const items = parseItems(content, date)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const $item = cheerio.load(item)
|
const $item = cheerio.load(item)
|
||||||
const prev = programs[programs.length - 1]
|
const prev = programs[programs.length - 1]
|
||||||
let start = parseStart($item, date)
|
let start = parseStart($item, date)
|
||||||
if (prev) {
|
if (prev) {
|
||||||
if (start.isBefore(prev.start)) {
|
if (start.isBefore(prev.start)) {
|
||||||
start = start.add(1, 'd')
|
start = start.add(1, 'd')
|
||||||
date = date.add(1, 'd')
|
date = date.add(1, 'd')
|
||||||
}
|
}
|
||||||
prev.stop = start
|
prev.stop = start
|
||||||
}
|
}
|
||||||
const stop = start.add(30, 'm')
|
const stop = start.add(30, 'm')
|
||||||
programs.push({
|
programs.push({
|
||||||
title: parseTitle($item),
|
title: parseTitle($item),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTitle($item) {
|
function parseTitle($item) {
|
||||||
return $item('.title').text().trim()
|
return $item('.title').text().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart($item, date) {
|
function parseStart($item, date) {
|
||||||
const time = $item('dt.col-time').clone().children().remove().end().text().trim()
|
const time = $item('dt.col-time').clone().children().remove().end().text().trim()
|
||||||
|
|
||||||
return time
|
return time
|
||||||
? dayjs.tz(`${date.format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm', 'Europe/Athens')
|
? dayjs.tz(`${date.format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm', 'Europe/Athens')
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content) {
|
function parseItems(content) {
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
|
|
||||||
return $('dl.show').toArray()
|
return $('dl.show').toArray()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +1,46 @@
|
|||||||
const { parser, url } = require('./antennaeurope.gr.config.js')
|
const { parser, url } = require('./antennaeurope.gr.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2025-01-21', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-01-21', 'YYYY-MM-DD').startOf('d')
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ date })).toBe('https://www.antennaeurope.gr/el/tvguide.html?date=2025-01-21')
|
expect(url({ date })).toBe('https://www.antennaeurope.gr/el/tvguide.html?date=2025-01-21')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'), 'utf8')
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'), 'utf8')
|
||||||
let results = parser({ content, date })
|
let results = parser({ content, date })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(16)
|
expect(results.length).toBe(16)
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2025-01-21T03:45:00.000Z',
|
start: '2025-01-21T03:45:00.000Z',
|
||||||
stop: '2025-01-21T07:50:00.000Z',
|
stop: '2025-01-21T07:50:00.000Z',
|
||||||
title: 'ΚΑΛΗΜΕΡΑ ΕΛΛΑΔΑ'
|
title: 'ΚΑΛΗΜΕΡΑ ΕΛΛΑΔΑ'
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[15]).toMatchObject({
|
expect(results[15]).toMatchObject({
|
||||||
start: '2025-01-22T01:30:00.000Z',
|
start: '2025-01-22T01:30:00.000Z',
|
||||||
stop: '2025-01-22T02:00:00.000Z',
|
stop: '2025-01-22T02:00:00.000Z',
|
||||||
title: 'ΤΟ ΠΡΩΙΝΟ'
|
title: 'ΤΟ ΠΡΩΙΝΟ'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const results = parser({
|
const results = parser({
|
||||||
date,
|
date,
|
||||||
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.html'), 'utf8')
|
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.html'), 'utf8')
|
||||||
})
|
})
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,59 +1,59 @@
|
|||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'antennapacific.gr',
|
site: 'antennapacific.gr',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ date }) {
|
url({ date }) {
|
||||||
return `https://www.antennapacific.gr/el/tvguide.html?date=${date.format('YYYY-MM-DD')}`
|
return `https://www.antennapacific.gr/el/tvguide.html?date=${date.format('YYYY-MM-DD')}`
|
||||||
},
|
},
|
||||||
parser({ content, date }) {
|
parser({ content, date }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
const items = parseItems(content, date)
|
const items = parseItems(content, date)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const $item = cheerio.load(item)
|
const $item = cheerio.load(item)
|
||||||
const prev = programs[programs.length - 1]
|
const prev = programs[programs.length - 1]
|
||||||
let start = parseStart($item, date)
|
let start = parseStart($item, date)
|
||||||
if (prev) {
|
if (prev) {
|
||||||
if (start.isBefore(prev.start)) {
|
if (start.isBefore(prev.start)) {
|
||||||
start = start.add(1, 'd')
|
start = start.add(1, 'd')
|
||||||
date = date.add(1, 'd')
|
date = date.add(1, 'd')
|
||||||
}
|
}
|
||||||
prev.stop = start
|
prev.stop = start
|
||||||
}
|
}
|
||||||
const stop = start.add(30, 'm')
|
const stop = start.add(30, 'm')
|
||||||
programs.push({
|
programs.push({
|
||||||
title: parseTitle($item),
|
title: parseTitle($item),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTitle($item) {
|
function parseTitle($item) {
|
||||||
return $item('.title').text().trim()
|
return $item('.title').text().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart($item, date) {
|
function parseStart($item, date) {
|
||||||
const time = $item('dt.col-time').clone().children().remove().end().text().trim()
|
const time = $item('dt.col-time').clone().children().remove().end().text().trim()
|
||||||
|
|
||||||
return time
|
return time
|
||||||
? dayjs.tz(`${date.format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm', 'Europe/Athens')
|
? dayjs.tz(`${date.format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm', 'Europe/Athens')
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content) {
|
function parseItems(content) {
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
|
|
||||||
return $('dl.show').toArray()
|
return $('dl.show').toArray()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +1,46 @@
|
|||||||
const { parser, url } = require('./antennapacific.gr.config.js')
|
const { parser, url } = require('./antennapacific.gr.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2025-01-21', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-01-21', 'YYYY-MM-DD').startOf('d')
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ date })).toBe('https://www.antennapacific.gr/el/tvguide.html?date=2025-01-21')
|
expect(url({ date })).toBe('https://www.antennapacific.gr/el/tvguide.html?date=2025-01-21')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'), 'utf8')
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'), 'utf8')
|
||||||
let results = parser({ content, date })
|
let results = parser({ content, date })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(17)
|
expect(results.length).toBe(17)
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2025-01-21T05:00:00.000Z',
|
start: '2025-01-21T05:00:00.000Z',
|
||||||
stop: '2025-01-21T06:00:00.000Z',
|
stop: '2025-01-21T06:00:00.000Z',
|
||||||
title: 'ANT1 NEWS - ΚΕΝΤΡΙΚΟ ΔΕΛΤΙΟ'
|
title: 'ANT1 NEWS - ΚΕΝΤΡΙΚΟ ΔΕΛΤΙΟ'
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[16]).toMatchObject({
|
expect(results[16]).toMatchObject({
|
||||||
start: '2025-01-22T02:45:00.000Z',
|
start: '2025-01-22T02:45:00.000Z',
|
||||||
stop: '2025-01-22T03:15:00.000Z',
|
stop: '2025-01-22T03:15:00.000Z',
|
||||||
title: 'ΚΑΛΗΜΕΡΑ ΕΛΛΑΔΑ'
|
title: 'ΚΑΛΗΜΕΡΑ ΕΛΛΑΔΑ'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const results = parser({
|
const results = parser({
|
||||||
date,
|
date,
|
||||||
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.html'), 'utf8')
|
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.html'), 'utf8')
|
||||||
})
|
})
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,59 +1,59 @@
|
|||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'antennasatellite.gr',
|
site: 'antennasatellite.gr',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ date }) {
|
url({ date }) {
|
||||||
return `https://www.antennasatellite.gr/el/tvguide.html?date=${date.format('YYYY-MM-DD')}`
|
return `https://www.antennasatellite.gr/el/tvguide.html?date=${date.format('YYYY-MM-DD')}`
|
||||||
},
|
},
|
||||||
parser({ content, date }) {
|
parser({ content, date }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
const items = parseItems(content, date)
|
const items = parseItems(content, date)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const $item = cheerio.load(item)
|
const $item = cheerio.load(item)
|
||||||
const prev = programs[programs.length - 1]
|
const prev = programs[programs.length - 1]
|
||||||
let start = parseStart($item, date)
|
let start = parseStart($item, date)
|
||||||
if (prev) {
|
if (prev) {
|
||||||
if (start.isBefore(prev.start)) {
|
if (start.isBefore(prev.start)) {
|
||||||
start = start.add(1, 'd')
|
start = start.add(1, 'd')
|
||||||
date = date.add(1, 'd')
|
date = date.add(1, 'd')
|
||||||
}
|
}
|
||||||
prev.stop = start
|
prev.stop = start
|
||||||
}
|
}
|
||||||
const stop = start.add(30, 'm')
|
const stop = start.add(30, 'm')
|
||||||
programs.push({
|
programs.push({
|
||||||
title: parseTitle($item),
|
title: parseTitle($item),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTitle($item) {
|
function parseTitle($item) {
|
||||||
return $item('.title').text().trim()
|
return $item('.title').text().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart($item, date) {
|
function parseStart($item, date) {
|
||||||
const time = $item('dt.col-time').clone().children().remove().end().text().trim()
|
const time = $item('dt.col-time').clone().children().remove().end().text().trim()
|
||||||
|
|
||||||
return time
|
return time
|
||||||
? dayjs.tz(`${date.format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm', 'Europe/Athens')
|
? dayjs.tz(`${date.format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm', 'Europe/Athens')
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content) {
|
function parseItems(content) {
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
|
|
||||||
return $('dl.show').toArray()
|
return $('dl.show').toArray()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +1,46 @@
|
|||||||
const { parser, url } = require('./antennasatellite.gr.config.js')
|
const { parser, url } = require('./antennasatellite.gr.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2025-01-21', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-01-21', 'YYYY-MM-DD').startOf('d')
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ date })).toBe('https://www.antennasatellite.gr/el/tvguide.html?date=2025-01-21')
|
expect(url({ date })).toBe('https://www.antennasatellite.gr/el/tvguide.html?date=2025-01-21')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'), 'utf8')
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'), 'utf8')
|
||||||
let results = parser({ content, date })
|
let results = parser({ content, date })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(16)
|
expect(results.length).toBe(16)
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2025-01-21T04:00:00.000Z',
|
start: '2025-01-21T04:00:00.000Z',
|
||||||
stop: '2025-01-21T04:40:00.000Z',
|
stop: '2025-01-21T04:40:00.000Z',
|
||||||
title: 'ANT1 NEWS'
|
title: 'ANT1 NEWS'
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[15]).toMatchObject({
|
expect(results[15]).toMatchObject({
|
||||||
start: '2025-01-22T00:50:00.000Z',
|
start: '2025-01-22T00:50:00.000Z',
|
||||||
stop: '2025-01-22T01:20:00.000Z',
|
stop: '2025-01-22T01:20:00.000Z',
|
||||||
title: 'ΤΟ ΠΡΩΙΝΟ'
|
title: 'ΤΟ ΠΡΩΙΝΟ'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const results = parser({
|
const results = parser({
|
||||||
date,
|
date,
|
||||||
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.html'), 'utf8')
|
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.html'), 'utf8')
|
||||||
})
|
})
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,60 +1,60 @@
|
|||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const { DateTime } = require('luxon')
|
const { DateTime } = require('luxon')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'arianatelevision.com',
|
site: 'arianatelevision.com',
|
||||||
days: 2,
|
days: 2,
|
||||||
url: 'https://www.arianatelevision.com/program-schedule/',
|
url: 'https://www.arianatelevision.com/program-schedule/',
|
||||||
parser({ content, date }) {
|
parser({ content, date }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
const items = parseItems(content, date)
|
const items = parseItems(content, date)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const prev = programs[programs.length - 1]
|
const prev = programs[programs.length - 1]
|
||||||
let start = parseStart(item, date)
|
let start = parseStart(item, date)
|
||||||
if (prev) {
|
if (prev) {
|
||||||
if (start < prev.start) {
|
if (start < prev.start) {
|
||||||
start = start.plus({ days: 1 })
|
start = start.plus({ days: 1 })
|
||||||
date = date.add(1, 'd')
|
date = date.add(1, 'd')
|
||||||
}
|
}
|
||||||
prev.stop = start
|
prev.stop = start
|
||||||
}
|
}
|
||||||
const stop = start.plus({ minutes: 30 })
|
const stop = start.plus({ minutes: 30 })
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart(item, date) {
|
function parseStart(item, date) {
|
||||||
const time = `${date.format('YYYY-MM-DD')} ${item.start}`
|
const time = `${date.format('YYYY-MM-DD')} ${item.start}`
|
||||||
|
|
||||||
return DateTime.fromFormat(time, 'yyyy-MM-dd H:mm', { zone: 'Asia/Kabul' }).toUTC()
|
return DateTime.fromFormat(time, 'yyyy-MM-dd H:mm', { zone: 'Asia/Kabul' }).toUTC()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, date) {
|
function parseItems(content, date) {
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
const settings = $('#jtrt_table_settings_508').text()
|
const settings = $('#jtrt_table_settings_508').text()
|
||||||
if (!settings) return []
|
if (!settings) return []
|
||||||
const data = JSON.parse(settings)
|
const data = JSON.parse(settings)
|
||||||
if (!data || !Array.isArray(data)) return []
|
if (!data || !Array.isArray(data)) return []
|
||||||
|
|
||||||
let rows = data[0]
|
let rows = data[0]
|
||||||
rows.shift()
|
rows.shift()
|
||||||
const output = []
|
const output = []
|
||||||
rows.forEach(row => {
|
rows.forEach(row => {
|
||||||
let day = date.day() + 2
|
let day = date.day() + 2
|
||||||
if (day > 7) day = 1
|
if (day > 7) day = 1
|
||||||
if (!row[0] || !row[day]) return
|
if (!row[0] || !row[day]) return
|
||||||
output.push({
|
output.push({
|
||||||
start: row[0].trim(),
|
start: row[0].trim(),
|
||||||
title: row[day].trim()
|
title: row[day].trim()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,163 +1,163 @@
|
|||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'arirang.com',
|
site: 'arirang.com',
|
||||||
output: 'arirang.com.guide.xml',
|
output: 'arirang.com.guide.xml',
|
||||||
channels: 'arirang.com.channels.xml',
|
channels: 'arirang.com.channels.xml',
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
days: 7,
|
days: 7,
|
||||||
delay: 5000,
|
delay: 5000,
|
||||||
url: 'https://www.arirang.com/v1.0/open/external/proxy',
|
url: 'https://www.arirang.com/v1.0/open/external/proxy',
|
||||||
|
|
||||||
request: {
|
request: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
cache: { ttl: 60 * 60 * 1000 },
|
cache: { ttl: 60 * 60 * 1000 },
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json, text/plain, */*',
|
Accept: 'application/json, text/plain, */*',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
Origin: 'https://www.arirang.com',
|
Origin: 'https://www.arirang.com',
|
||||||
Referer: 'https://www.arirang.com/schedule',
|
Referer: 'https://www.arirang.com/schedule',
|
||||||
'User-Agent':
|
'User-Agent':
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36'
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36'
|
||||||
},
|
},
|
||||||
data: function (context) {
|
data: function (context) {
|
||||||
const { channel, date } = context
|
const { channel, date } = context
|
||||||
return {
|
return {
|
||||||
address: 'https://script.arirang.com/api/v1/bis/listScheduleV3.do',
|
address: 'https://script.arirang.com/api/v1/bis/listScheduleV3.do',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {},
|
headers: {},
|
||||||
body: {
|
body: {
|
||||||
data: {
|
data: {
|
||||||
dmParam: {
|
dmParam: {
|
||||||
chanId: channel.site_id,
|
chanId: channel.site_id,
|
||||||
broadYmd: dayjs.tz(date, 'Asia/Seoul').format('YYYYMMDD'),
|
broadYmd: dayjs.tz(date, 'Asia/Seoul').format('YYYYMMDD'),
|
||||||
planNo: '1'
|
planNo: '1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
logo: function (context) {
|
logo: function (context) {
|
||||||
return context.channel.logo
|
return context.channel.logo
|
||||||
},
|
},
|
||||||
|
|
||||||
async parser(context) {
|
async parser(context) {
|
||||||
const programs = []
|
const programs = []
|
||||||
const items = parseItems(context.content)
|
const items = parseItems(context.content)
|
||||||
|
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
const programDetail = await parseProgramDetail(item)
|
const programDetail = await parseProgramDetail(item)
|
||||||
|
|
||||||
programs.push({
|
programs.push({
|
||||||
title: parseTitle(programDetail),
|
title: parseTitle(programDetail),
|
||||||
start: parseStart(item),
|
start: parseStart(item),
|
||||||
stop: parseStop(item),
|
stop: parseStop(item),
|
||||||
image: parseImage(programDetail),
|
image: parseImage(programDetail),
|
||||||
category: parseCategory(programDetail),
|
category: parseCategory(programDetail),
|
||||||
description: parseDescription(programDetail)
|
description: parseDescription(programDetail)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content) {
|
function parseItems(content) {
|
||||||
if (content != '') {
|
if (content != '') {
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
return !data || !data.responseBody || !Array.isArray(data.responseBody.dsSchWeek)
|
return !data || !data.responseBody || !Array.isArray(data.responseBody.dsSchWeek)
|
||||||
? []
|
? []
|
||||||
: data.responseBody.dsSchWeek
|
: data.responseBody.dsSchWeek
|
||||||
} else {
|
} else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart(item) {
|
function parseStart(item) {
|
||||||
return dayjs.tz(item.broadYmd + ' ' + item.broadHm, 'YYYYMMDD HHmm', 'Asia/Seoul')
|
return dayjs.tz(item.broadYmd + ' ' + item.broadHm, 'YYYYMMDD HHmm', 'Asia/Seoul')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStop(item) {
|
function parseStop(item) {
|
||||||
return dayjs
|
return dayjs
|
||||||
.tz(item.broadYmd + ' ' + item.broadHm, 'YYYYMMDD HHmm', 'Asia/Seoul')
|
.tz(item.broadYmd + ' ' + item.broadHm, 'YYYYMMDD HHmm', 'Asia/Seoul')
|
||||||
.add(item.broadRun, 'minute')
|
.add(item.broadRun, 'minute')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function parseProgramDetail(item) {
|
async function parseProgramDetail(item) {
|
||||||
return axios
|
return axios
|
||||||
.post(
|
.post(
|
||||||
'https://www.arirang.com/v1.0/open/program/detail',
|
'https://www.arirang.com/v1.0/open/program/detail',
|
||||||
{
|
{
|
||||||
bis_program_code: item.pgmCd
|
bis_program_code: item.pgmCd
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json, text/plain, */*',
|
Accept: 'application/json, text/plain, */*',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
Origin: 'https://www.arirang.com',
|
Origin: 'https://www.arirang.com',
|
||||||
Referer: 'https://www.arirang.com/schedule',
|
Referer: 'https://www.arirang.com/schedule',
|
||||||
'User-Agent':
|
'User-Agent':
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36'
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36'
|
||||||
},
|
},
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
cache: { ttl: 60 * 1000 }
|
cache: { ttl: 60 * 1000 }
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
// console.log('Retrieved program detail: bis_program_code ' + item.pgmCd)
|
// console.log('Retrieved program detail: bis_program_code ' + item.pgmCd)
|
||||||
return response.data
|
return response.data
|
||||||
})
|
})
|
||||||
.catch(function () {
|
.catch(function () {
|
||||||
// The provider/server may not have details on every single programs.
|
// The provider/server may not have details on every single programs.
|
||||||
// console.log('Unavailable program detail: bis_program_code ' + item.pgmCd)
|
// console.log('Unavailable program detail: bis_program_code ' + item.pgmCd)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTitle(programDetail) {
|
function parseTitle(programDetail) {
|
||||||
if (programDetail && programDetail.title && programDetail.title[0] && programDetail.title[0].text) {
|
if (programDetail && programDetail.title && programDetail.title[0] && programDetail.title[0].text) {
|
||||||
return programDetail.title[0].text
|
return programDetail.title[0].text
|
||||||
} else {
|
} else {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseImage(programDetail) {
|
function parseImage(programDetail) {
|
||||||
if (programDetail && programDetail.image && programDetail.image[0].url) {
|
if (programDetail && programDetail.image && programDetail.image[0].url) {
|
||||||
return programDetail.image[0].url
|
return programDetail.image[0].url
|
||||||
} else {
|
} else {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseCategory(programDetail) {
|
function parseCategory(programDetail) {
|
||||||
if (programDetail && programDetail.category_Info && programDetail.category_Info[0].title) {
|
if (programDetail && programDetail.category_Info && programDetail.category_Info[0].title) {
|
||||||
return programDetail.category_Info[0].title
|
return programDetail.category_Info[0].title
|
||||||
} else {
|
} else {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDescription(programDetail) {
|
function parseDescription(programDetail) {
|
||||||
if (
|
if (
|
||||||
programDetail &&
|
programDetail &&
|
||||||
programDetail.content &&
|
programDetail.content &&
|
||||||
programDetail.content[0] &&
|
programDetail.content[0] &&
|
||||||
programDetail.content[0].text
|
programDetail.content[0].text
|
||||||
) {
|
) {
|
||||||
let description = programDetail.content[0].text
|
let description = programDetail.content[0].text
|
||||||
let regex = /(<([^>]+)>)/gi
|
let regex = /(<([^>]+)>)/gi
|
||||||
return description.replace(regex, '')
|
return description.replace(regex, '')
|
||||||
} else {
|
} else {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,72 +1,72 @@
|
|||||||
const { url, parser } = require('./arirang.com.config.js')
|
const { url, parser } = require('./arirang.com.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
jest.mock('axios')
|
jest.mock('axios')
|
||||||
|
|
||||||
const date = dayjs.tz('2025-04-20', 'Asia/Seoul').startOf('d')
|
const date = dayjs.tz('2025-04-20', 'Asia/Seoul').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
xmltv_id: 'ArirangWorld.kr',
|
xmltv_id: 'ArirangWorld.kr',
|
||||||
site_id: 'CH_W',
|
site_id: 'CH_W',
|
||||||
name: 'Arirang World',
|
name: 'Arirang World',
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
logo: 'https://i.imgur.com/5Aoithj.png'
|
logo: 'https://i.imgur.com/5Aoithj.png'
|
||||||
}
|
}
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/schedule.json'), 'utf8')
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/schedule.json'), 'utf8')
|
||||||
const programDetail = fs.readFileSync(path.resolve(__dirname, '__data__/detail.json'), 'utf8')
|
const programDetail = fs.readFileSync(path.resolve(__dirname, '__data__/detail.json'), 'utf8')
|
||||||
const context = { channel: channel, content: content, date: date }
|
const context = { channel: channel, content: content, date: date }
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url).toBe('https://www.arirang.com/v1.0/open/external/proxy')
|
expect(url).toBe('https://www.arirang.com/v1.0/open/external/proxy')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', async () => {
|
it('can handle empty guide', async () => {
|
||||||
const results = await parser({ channel: channel, content: '', date: date })
|
const results = await parser({ channel: channel, content: '', date: date })
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', async () => {
|
it('can parse response', async () => {
|
||||||
axios.post.mockImplementation((url, data) => {
|
axios.post.mockImplementation((url, data) => {
|
||||||
if (
|
if (
|
||||||
url === 'https://www.arirang.com/v1.0/open/external/proxy' &&
|
url === 'https://www.arirang.com/v1.0/open/external/proxy' &&
|
||||||
JSON.stringify(data) ===
|
JSON.stringify(data) ===
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
address: 'https://script.arirang.com/api/v1/bis/listScheduleV3.do',
|
address: 'https://script.arirang.com/api/v1/bis/listScheduleV3.do',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {},
|
headers: {},
|
||||||
body: { data: { dmParam: { chanId: 'CH_W', broadYmd: '20250420', planNo: '1' } } }
|
body: { data: { dmParam: { chanId: 'CH_W', broadYmd: '20250420', planNo: '1' } } }
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: JSON.parse(content)
|
data: JSON.parse(content)
|
||||||
})
|
})
|
||||||
} else if (
|
} else if (
|
||||||
url === 'https://www.arirang.com/v1.0/open/program/detail' &&
|
url === 'https://www.arirang.com/v1.0/open/program/detail' &&
|
||||||
JSON.stringify(data) === JSON.stringify({ bis_program_code: '2025006T' })
|
JSON.stringify(data) === JSON.stringify({ bis_program_code: '2025006T' })
|
||||||
) {
|
) {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: JSON.parse(programDetail)
|
data: JSON.parse(programDetail)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: ''
|
data: ''
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const results = await parser(context)
|
const results = await parser(context)
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
title: 'Diplomat Archives: Hidden Stories',
|
title: 'Diplomat Archives: Hidden Stories',
|
||||||
start: dayjs.tz(date, 'Asia/Seoul'),
|
start: dayjs.tz(date, 'Asia/Seoul'),
|
||||||
stop: dayjs.tz(date, 'Asia/Seoul').add(30, 'minute'),
|
stop: dayjs.tz(date, 'Asia/Seoul').add(30, 'minute'),
|
||||||
image:
|
image:
|
||||||
'https://img.arirang.com/v1/AUTH_d52449c16d3b4bbca17d4fffd9fc44af/public/images/202504/2985531324875408146.jpg',
|
'https://img.arirang.com/v1/AUTH_d52449c16d3b4bbca17d4fffd9fc44af/public/images/202504/2985531324875408146.jpg',
|
||||||
description: 'As of April 2025, S. Korea has established diplomatic relations with a total of 194 countries.\nAmong them are countries that have had ties and exchanges with Korea for hundreds of years.\nWith such long-standing relationships with so many nations,\nmight there be fascinating hidden stories between Korea and the rest of the world that we don’t know yet? \n\n"Diplomat’s Archives: Hidden Stories" begins with this very question.\nTogether with foreign embassies in Korea, the series uncovers and sheds light on meaningful yet lesser-known stories between Korea and other countries.\nThrough this, we aim to reaffirm the deep friendships that have been built over time, highlight how countries are interconnected—bilaterally and multilaterally—\nand emphasize the importance of cooperation on the global stage today.',
|
description: 'As of April 2025, S. Korea has established diplomatic relations with a total of 194 countries.\nAmong them are countries that have had ties and exchanges with Korea for hundreds of years.\nWith such long-standing relationships with so many nations,\nmight there be fascinating hidden stories between Korea and the rest of the world that we don’t know yet? \n\n"Diplomat’s Archives: Hidden Stories" begins with this very question.\nTogether with foreign embassies in Korea, the series uncovers and sheds light on meaningful yet lesser-known stories between Korea and other countries.\nThrough this, we aim to reaffirm the deep friendships that have been built over time, highlight how countries are interconnected—bilaterally and multilaterally—\nand emphasize the importance of cooperation on the global stage today.',
|
||||||
category: 'Current Affairs'
|
category: 'Current Affairs'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -1,70 +1,70 @@
|
|||||||
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0
|
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0
|
||||||
|
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
|
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'artonline.tv',
|
site: 'artonline.tv',
|
||||||
days: 2,
|
days: 2,
|
||||||
url: function ({ channel }) {
|
url: function ({ channel }) {
|
||||||
const [, site_id] = channel.site_id.split('#')
|
const [, site_id] = channel.site_id.split('#')
|
||||||
|
|
||||||
return `https://www.artonline.tv/Home/Tvlist${site_id}`
|
return `https://www.artonline.tv/Home/Tvlist${site_id}`
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'content-type': 'application/x-www-form-urlencoded'
|
'content-type': 'application/x-www-form-urlencoded'
|
||||||
},
|
},
|
||||||
data: function ({ date }) {
|
data: function ({ date }) {
|
||||||
const diff = date.diff(dayjs.utc().startOf('d'), 'd')
|
const diff = date.diff(dayjs.utc().startOf('d'), 'd')
|
||||||
const params = new URLSearchParams()
|
const params = new URLSearchParams()
|
||||||
params.append('objId', diff)
|
params.append('objId', diff)
|
||||||
|
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
parser: function ({ content }) {
|
parser: function ({ content }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
if (!content) return programs
|
if (!content) return programs
|
||||||
const items = JSON.parse(content)
|
const items = JSON.parse(content)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const image = parseImage(item)
|
const image = parseImage(item)
|
||||||
const start = parseStart(item)
|
const start = parseStart(item)
|
||||||
const duration = parseDuration(item)
|
const duration = parseDuration(item)
|
||||||
const stop = start.add(duration, 's')
|
const stop = start.add(duration, 's')
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
description: item.description,
|
description: item.description,
|
||||||
image,
|
image,
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart(item) {
|
function parseStart(item) {
|
||||||
const [, M, D, YYYY] = item.adddate.match(/(\d+)\/(\d+)\/(\d+) /)
|
const [, M, D, YYYY] = item.adddate.match(/(\d+)\/(\d+)\/(\d+) /)
|
||||||
const [HH, mm] = item.start_Time.split(':')
|
const [HH, mm] = item.start_Time.split(':')
|
||||||
|
|
||||||
return dayjs.tz(`${YYYY}-${M}-${D}T${HH}:${mm}:00`, 'YYYY-M-DTHH:mm:ss', 'Asia/Riyadh')
|
return dayjs.tz(`${YYYY}-${M}-${D}T${HH}:${mm}:00`, 'YYYY-M-DTHH:mm:ss', 'Asia/Riyadh')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDuration(item) {
|
function parseDuration(item) {
|
||||||
const [, HH, mm, ss] = item.duration.match(/(\d+):(\d+):(\d+)/)
|
const [, HH, mm, ss] = item.duration.match(/(\d+):(\d+):(\d+)/)
|
||||||
|
|
||||||
return parseInt(HH) * 3600 + parseInt(mm) * 60 + parseInt(ss)
|
return parseInt(HH) * 3600 + parseInt(mm) * 60 + parseInt(ss)
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseImage(item) {
|
function parseImage(item) {
|
||||||
return item.thumbnail ? `https://www.artonline.tv${item.thumbnail}` : null
|
return item.thumbnail ? `https://www.artonline.tv${item.thumbnail}` : null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,86 +1,86 @@
|
|||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const { DateTime } = require('luxon')
|
const { DateTime } = require('luxon')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'awilime.com',
|
site: 'awilime.com',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ channel, date }) {
|
url({ channel, date }) {
|
||||||
return `https://www.awilime.com/tv/napi_musor/${channel.site_id}/${date.format('YYYY_MM_DD')}`
|
return `https://www.awilime.com/tv/napi_musor/${channel.site_id}/${date.format('YYYY_MM_DD')}`
|
||||||
},
|
},
|
||||||
parser({ content, date }) {
|
parser({ content, date }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
const items = parseItems(content)
|
const items = parseItems(content)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const prev = programs[programs.length - 1]
|
const prev = programs[programs.length - 1]
|
||||||
const $item = cheerio.load(item)
|
const $item = cheerio.load(item)
|
||||||
let start = parseStart($item, date)
|
let start = parseStart($item, date)
|
||||||
if (!start) return
|
if (!start) return
|
||||||
if (prev) {
|
if (prev) {
|
||||||
prev.stop = start
|
prev.stop = start
|
||||||
}
|
}
|
||||||
const stop = start.plus({ minute: 30 })
|
const stop = start.plus({ minute: 30 })
|
||||||
|
|
||||||
programs.push({
|
programs.push({
|
||||||
title: parseTitle($item),
|
title: parseTitle($item),
|
||||||
sub_title: parseSubTitle($item),
|
sub_title: parseSubTitle($item),
|
||||||
description: parseDescription($item),
|
description: parseDescription($item),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const html = await axios
|
const html = await axios
|
||||||
.get('https://www.awilime.com/tv/napi_musor')
|
.get('https://www.awilime.com/tv/napi_musor')
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
const $ = cheerio.load(html)
|
const $ = cheerio.load(html)
|
||||||
const items = $('#body > div.tk > div > div').toArray()
|
const items = $('#body > div.tk > div > div').toArray()
|
||||||
|
|
||||||
const channels = []
|
const channels = []
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const name = $(item).find('a').text().trim()
|
const name = $(item).find('a').text().trim()
|
||||||
const url = $(item).find('a').attr('href')
|
const url = $(item).find('a').attr('href')
|
||||||
const [, site_id] = url.match(/\/tv\/napi_musor\/(.*)/) || [null, null]
|
const [, site_id] = url.match(/\/tv\/napi_musor\/(.*)/) || [null, null]
|
||||||
if (!site_id) return
|
if (!site_id) return
|
||||||
if (channels.find(channel => channel.site_id === site_id)) return
|
if (channels.find(channel => channel.site_id === site_id)) return
|
||||||
|
|
||||||
channels.push({
|
channels.push({
|
||||||
lang: 'hu',
|
lang: 'hu',
|
||||||
site_id,
|
site_id,
|
||||||
name
|
name
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return channels
|
return channels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTitle($item) {
|
function parseTitle($item) {
|
||||||
return $item('b > a').text().trim()
|
return $item('b > a').text().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseSubTitle($item) {
|
function parseSubTitle($item) {
|
||||||
return $item('i').clone().children().remove('s').end().text().trim()
|
return $item('i').clone().children().remove('s').end().text().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDescription($item) {
|
function parseDescription($item) {
|
||||||
return $item('p').text().trim()
|
return $item('p').text().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart($item, date) {
|
function parseStart($item, date) {
|
||||||
let time = $item('b').clone().children().remove().end().text().trim()
|
let time = $item('b').clone().children().remove().end().text().trim()
|
||||||
if (!time || !/^\d/.test(time)) return null
|
if (!time || !/^\d/.test(time)) return null
|
||||||
time = `${date.format('YYYY-MM-DD')} ${time}`
|
time = `${date.format('YYYY-MM-DD')} ${time}`
|
||||||
|
|
||||||
return DateTime.fromFormat(time, 'yyyy-MM-dd HH:mm', { zone: 'Europe/Budapest' }).toUTC()
|
return DateTime.fromFormat(time, 'yyyy-MM-dd HH:mm', { zone: 'Europe/Budapest' }).toUTC()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content) {
|
function parseItems(content) {
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
|
|
||||||
return $('#body > div.tdc > div.td2 > div').toArray()
|
return $('#body > div.tdc > div.td2 > div').toArray()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +1,49 @@
|
|||||||
const { parser, url } = require('./awilime.com.config.js')
|
const { parser, url } = require('./awilime.com.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2024-06-26', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2024-06-26', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: 'budapest_europa_tv',
|
site_id: 'budapest_europa_tv',
|
||||||
xmltv_id: 'BudapestEuropaTelevizio.hu'
|
xmltv_id: 'BudapestEuropaTelevizio.hu'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ channel, date })).toBe(
|
expect(url({ channel, date })).toBe(
|
||||||
'https://www.awilime.com/tv/napi_musor/budapest_europa_tv/2024_06_26'
|
'https://www.awilime.com/tv/napi_musor/budapest_europa_tv/2024_06_26'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
||||||
const results = parser({ content, date }).map(p => {
|
const results = parser({ content, date }).map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(15)
|
expect(results.length).toBe(15)
|
||||||
|
|
||||||
expect(results[3]).toMatchObject({
|
expect(results[3]).toMatchObject({
|
||||||
start: '2024-06-26T07:00:00.000Z',
|
start: '2024-06-26T07:00:00.000Z',
|
||||||
stop: '2024-06-26T08:00:00.000Z',
|
stop: '2024-06-26T08:00:00.000Z',
|
||||||
title: 'Ébredés',
|
title: 'Ébredés',
|
||||||
sub_title: 'Amerikai dokumentumfilm (2018)',
|
sub_title: 'Amerikai dokumentumfilm (2018)',
|
||||||
description: 'Balla Tibor misszionárius'
|
description: 'Balla Tibor misszionárius'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const result = parser({
|
const result = parser({
|
||||||
date,
|
date,
|
||||||
channel,
|
channel,
|
||||||
content:
|
content:
|
||||||
'<html><head><title>Object moved</title></head><body><h2>Object moved to <a href="/tv/napi_musor/budapest_europa_tv/2024_06_24">here</a>.</h2></body></html>'
|
'<html><head><title>Object moved</title></head><body><h2>Object moved to <a href="/tv/napi_musor/budapest_europa_tv/2024_06_24">here</a>.</h2></body></html>'
|
||||||
})
|
})
|
||||||
expect(result).toMatchObject([])
|
expect(result).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,110 +1,110 @@
|
|||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const { DateTime } = require('luxon')
|
const { DateTime } = require('luxon')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'bein.com',
|
site: 'bein.com',
|
||||||
days: 2,
|
days: 2,
|
||||||
request: {
|
request: {
|
||||||
cache: {
|
cache: {
|
||||||
ttl: 60 * 60 * 1000 // 1 hour
|
ttl: 60 * 60 * 1000 // 1 hour
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
url: function ({ date, channel }) {
|
url: function ({ date, channel }) {
|
||||||
const [category] = channel.site_id.split('#')
|
const [category] = channel.site_id.split('#')
|
||||||
const postid = channel.lang === 'ar' ? '25344' : '25356'
|
const postid = channel.lang === 'ar' ? '25344' : '25356'
|
||||||
|
|
||||||
return `https://www.bein.com/${
|
return `https://www.bein.com/${
|
||||||
channel.lang
|
channel.lang
|
||||||
}/epg-ajax-template/?action=epg_fetch&category=${category}&cdate=${date.format(
|
}/epg-ajax-template/?action=epg_fetch&category=${category}&cdate=${date.format(
|
||||||
'YYYY-MM-DD'
|
'YYYY-MM-DD'
|
||||||
)}&language=${channel.lang.toUpperCase()}&loadindex=0&mins=00&offset=0&postid=${postid}&serviceidentity=bein.net`
|
)}&language=${channel.lang.toUpperCase()}&loadindex=0&mins=00&offset=0&postid=${postid}&serviceidentity=bein.net`
|
||||||
},
|
},
|
||||||
parser: function ({ content, channel, date }) {
|
parser: function ({ content, channel, date }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
const items = parseItems(content, channel)
|
const items = parseItems(content, channel)
|
||||||
date = DateTime.fromMillis(date.valueOf()).minus({ days: 1 })
|
date = DateTime.fromMillis(date.valueOf()).minus({ days: 1 })
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const $item = cheerio.load(item)
|
const $item = cheerio.load(item)
|
||||||
const title = parseTitle($item)
|
const title = parseTitle($item)
|
||||||
if (!title) return
|
if (!title) return
|
||||||
const category = parseCategory($item)
|
const category = parseCategory($item)
|
||||||
const prev = programs[programs.length - 1]
|
const prev = programs[programs.length - 1]
|
||||||
let start = parseTime($item, date)
|
let start = parseTime($item, date)
|
||||||
if (prev) {
|
if (prev) {
|
||||||
if (start < prev.start) {
|
if (start < prev.start) {
|
||||||
start = start.plus({ days: 1 })
|
start = start.plus({ days: 1 })
|
||||||
date = date.plus({ days: 1 })
|
date = date.plus({ days: 1 })
|
||||||
}
|
}
|
||||||
prev.stop = start
|
prev.stop = start
|
||||||
}
|
}
|
||||||
let stop = parseTime($item, start)
|
let stop = parseTime($item, start)
|
||||||
if (stop < start) {
|
if (stop < start) {
|
||||||
stop = stop.plus({ days: 1 })
|
stop = stop.plus({ days: 1 })
|
||||||
}
|
}
|
||||||
programs.push({
|
programs.push({
|
||||||
title,
|
title,
|
||||||
category,
|
category,
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels({ lang }) {
|
async channels({ lang }) {
|
||||||
const categories = ['entertainment', 'sports']
|
const categories = ['entertainment', 'sports']
|
||||||
|
|
||||||
let channels = []
|
let channels = []
|
||||||
for (let category of categories) {
|
for (let category of categories) {
|
||||||
const url = `https://www.bein.com/en/epg-ajax-template/?action=epg_fetch&offset=0&category=${category}&serviceidentity=bein.net&mins=00&cdate=${dayjs().format(
|
const url = `https://www.bein.com/en/epg-ajax-template/?action=epg_fetch&offset=0&category=${category}&serviceidentity=bein.net&mins=00&cdate=${dayjs().format(
|
||||||
'YYYY-MM-DD'
|
'YYYY-MM-DD'
|
||||||
)}&language=${lang.toUpperCase()}&postid=25356&loadindex=0`
|
)}&language=${lang.toUpperCase()}&postid=25356&loadindex=0`
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(url)
|
.get(url)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
const $ = cheerio.load(data)
|
const $ = cheerio.load(data)
|
||||||
$('.container-tvguide > div').each((i, el) => {
|
$('.container-tvguide > div').each((i, el) => {
|
||||||
const id = $(el).attr('id')
|
const id = $(el).attr('id')
|
||||||
if (!id || !/^channels_\d+/.test(id)) return
|
if (!id || !/^channels_\d+/.test(id)) return
|
||||||
const [, channelId] = id.split('_')
|
const [, channelId] = id.split('_')
|
||||||
|
|
||||||
channels.push({
|
channels.push({
|
||||||
lang,
|
lang,
|
||||||
site_id: `${category}#${channelId}`,
|
site_id: `${category}#${channelId}`,
|
||||||
name: channelId
|
name: channelId
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return channels
|
return channels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTitle($item) {
|
function parseTitle($item) {
|
||||||
return $item('.title').text()
|
return $item('.title').text()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseCategory($item) {
|
function parseCategory($item) {
|
||||||
return $item('.format').text()
|
return $item('.format').text()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTime($item, date) {
|
function parseTime($item, date) {
|
||||||
let [, time] = $item('.time')
|
let [, time] = $item('.time')
|
||||||
.text()
|
.text()
|
||||||
.match(/^(\d{2}:\d{2})/) || [null, null]
|
.match(/^(\d{2}:\d{2})/) || [null, null]
|
||||||
if (!time) return null
|
if (!time) return null
|
||||||
time = `${date.toFormat('yyyy-MM-dd')} ${time}`
|
time = `${date.toFormat('yyyy-MM-dd')} ${time}`
|
||||||
|
|
||||||
return DateTime.fromFormat(time, 'yyyy-MM-dd HH:mm', { zone: 'Asia/Qatar' }).toUTC()
|
return DateTime.fromFormat(time, 'yyyy-MM-dd HH:mm', { zone: 'Asia/Qatar' }).toUTC()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, channel) {
|
function parseItems(content, channel) {
|
||||||
const [, channelId] = channel.site_id.split('#')
|
const [, channelId] = channel.site_id.split('#')
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
|
|
||||||
return $(`#channels_${channelId} .slider > ul:first-child > li`).toArray()
|
return $(`#channels_${channelId} .slider > ul:first-child > li`).toArray()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,58 +1,58 @@
|
|||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const { parser, url } = require('./bein.com.config.js')
|
const { parser, url } = require('./bein.com.config.js')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2023-01-19', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2023-01-19', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = { site_id: 'entertainment#1', xmltv_id: 'beINMovies1Premiere.qa', lang: 'en' }
|
const channel = { site_id: 'entertainment#1', xmltv_id: 'beINMovies1Premiere.qa', lang: 'en' }
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
const result = url({ date, channel })
|
const result = url({ date, channel })
|
||||||
expect(result).toBe(
|
expect(result).toBe(
|
||||||
'https://www.bein.com/en/epg-ajax-template/?action=epg_fetch&category=entertainment&cdate=2023-01-19&language=EN&loadindex=0&mins=00&offset=0&postid=25356&serviceidentity=bein.net'
|
'https://www.bein.com/en/epg-ajax-template/?action=epg_fetch&category=entertainment&cdate=2023-01-19&language=EN&loadindex=0&mins=00&offset=0&postid=25356&serviceidentity=bein.net'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve('sites/bein.com/__data__/content.html'))
|
const content = fs.readFileSync(path.resolve('sites/bein.com/__data__/content.html'))
|
||||||
const results = parser({ date, channel, content }).map(p => {
|
const results = parser({ date, channel, content }).map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2023-01-18T20:15:00.000Z',
|
start: '2023-01-18T20:15:00.000Z',
|
||||||
stop: '2023-01-18T22:15:00.000Z',
|
stop: '2023-01-18T22:15:00.000Z',
|
||||||
title: 'The Walk',
|
title: 'The Walk',
|
||||||
category: 'Movies'
|
category: 'Movies'
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[1]).toMatchObject({
|
expect(results[1]).toMatchObject({
|
||||||
start: '2023-01-18T22:15:00.000Z',
|
start: '2023-01-18T22:15:00.000Z',
|
||||||
stop: '2023-01-19T00:00:00.000Z',
|
stop: '2023-01-19T00:00:00.000Z',
|
||||||
title: 'Resident Evil: Welcome To Raccoon City',
|
title: 'Resident Evil: Welcome To Raccoon City',
|
||||||
category: 'Movies'
|
category: 'Movies'
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[10]).toMatchObject({
|
expect(results[10]).toMatchObject({
|
||||||
start: '2023-01-19T15:30:00.000Z',
|
start: '2023-01-19T15:30:00.000Z',
|
||||||
stop: '2023-01-19T18:00:00.000Z',
|
stop: '2023-01-19T18:00:00.000Z',
|
||||||
title: 'Spider-Man: No Way Home',
|
title: 'Spider-Man: No Way Home',
|
||||||
category: 'Movies'
|
category: 'Movies'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const noContent = fs.readFileSync(path.resolve('sites/bein.com/__data__/no-content.html'))
|
const noContent = fs.readFileSync(path.resolve('sites/bein.com/__data__/no-content.html'))
|
||||||
const result = parser({
|
const result = parser({
|
||||||
date,
|
date,
|
||||||
channel,
|
channel,
|
||||||
content: noContent
|
content: noContent
|
||||||
})
|
})
|
||||||
expect(result).toMatchObject([])
|
expect(result).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,73 +1,73 @@
|
|||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'beinsports.com',
|
site: 'beinsports.com',
|
||||||
days: 2,
|
days: 2,
|
||||||
request: {
|
request: {
|
||||||
headers: {
|
headers: {
|
||||||
'User-Agent':
|
'User-Agent':
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36'
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
url: function ({ date, channel }) {
|
url: function ({ date, channel }) {
|
||||||
return `https://www.beinsports.com/api/opta/tv-event?&startBefore=${date
|
return `https://www.beinsports.com/api/opta/tv-event?&startBefore=${date
|
||||||
.add(1, 'd')
|
.add(1, 'd')
|
||||||
.format('YYYY-MM-DDTHH:mm:ss.SSS')}Z&endAfter=${date.format(
|
.format('YYYY-MM-DDTHH:mm:ss.SSS')}Z&endAfter=${date.format(
|
||||||
'YYYY-MM-DDTHH:mm:ss.SSS'
|
'YYYY-MM-DDTHH:mm:ss.SSS'
|
||||||
)}Z&channelIds=${channel.site_id}`
|
)}Z&channelIds=${channel.site_id}`
|
||||||
},
|
},
|
||||||
parser: function ({ content }) {
|
parser: function ({ content }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
const items = parseItems(content)
|
const items = parseItems(content)
|
||||||
if (!items.length == 0) {
|
if (!items.length == 0) {
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const start = dayjs.utc(item.startDate)
|
const start = dayjs.utc(item.startDate)
|
||||||
const stop = dayjs.utc(item.endDate)
|
const stop = dayjs.utc(item.endDate)
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
description: item.description,
|
description: item.description,
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels({ region, lang }) {
|
async channels({ region, lang }) {
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(`https://www.beinsports.com/api/opta/tv-channel?region=${lang}-${region}`, this.request)
|
.get(`https://www.beinsports.com/api/opta/tv-channel?region=${lang}-${region}`, this.request)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
return data.rows.map(item => {
|
return data.rows.map(item => {
|
||||||
return {
|
return {
|
||||||
lang,
|
lang,
|
||||||
site_id: item.id,
|
site_id: item.id,
|
||||||
name: item.name
|
name: item.name
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content) {
|
function parseItems(content) {
|
||||||
let data
|
let data
|
||||||
try {
|
try {
|
||||||
data = JSON.parse(content)
|
data = JSON.parse(content)
|
||||||
} catch {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data || !data['rows']) {
|
if (!data || !data['rows']) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
return data.rows
|
return data.rows
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,43 @@
|
|||||||
const { parser, url } = require('./beinsports.com.config.js')
|
const { parser, url } = require('./beinsports.com.config.js')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2023-10-22T00:00:00.000', '"YYYY-MM-DDTHH:mm:ss.SSS').startOf('d')
|
const date = dayjs.utc('2023-10-22T00:00:00.000', '"YYYY-MM-DDTHH:mm:ss.SSS').startOf('d')
|
||||||
const channel = { site_id: 'C244C48D-3B54-406A-94C9-D63B16318267', xmltv_id: 'beINSportsUSA.us' }
|
const channel = { site_id: 'C244C48D-3B54-406A-94C9-D63B16318267', xmltv_id: 'beINSportsUSA.us' }
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
const result = url({ date, channel })
|
const result = url({ date, channel })
|
||||||
expect(result).toBe(
|
expect(result).toBe(
|
||||||
'https://www.beinsports.com/api/opta/tv-event?&startBefore=2023-10-23T00:00:00.000Z&endAfter=2023-10-22T00:00:00.000Z&channelIds=C244C48D-3B54-406A-94C9-D63B16318267'
|
'https://www.beinsports.com/api/opta/tv-event?&startBefore=2023-10-23T00:00:00.000Z&endAfter=2023-10-22T00:00:00.000Z&channelIds=C244C48D-3B54-406A-94C9-D63B16318267'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const content =
|
const content =
|
||||||
'{"count":1,"rows":[{"data":{"eventId":"2028126","eventDate":"2023-10-21T10:30:00","utcEventDate":"2023-10-20T23:30:00","duration":"90","programId":"106230","programTypeId":"5","title":"ATP 500"},"duration":5400000,"title":"Tokyo Day 5 QF 2","startDate":"2023-10-20T23:30:00.000Z","endDate":"2023-10-21T01:00:00.000Z","description":"Exclusive coverage of the 2023 ATP Tour on beIN SPORTS","channelId":"164C0EDA-EBCE-4AA6-9DDA-D603E0948B9F"}]}'
|
'{"count":1,"rows":[{"data":{"eventId":"2028126","eventDate":"2023-10-21T10:30:00","utcEventDate":"2023-10-20T23:30:00","duration":"90","programId":"106230","programTypeId":"5","title":"ATP 500"},"duration":5400000,"title":"Tokyo Day 5 QF 2","startDate":"2023-10-20T23:30:00.000Z","endDate":"2023-10-21T01:00:00.000Z","description":"Exclusive coverage of the 2023 ATP Tour on beIN SPORTS","channelId":"164C0EDA-EBCE-4AA6-9DDA-D603E0948B9F"}]}'
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const result = parser({ content, channel, date }).map(p => {
|
const result = parser({ content, channel, date }).map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(result).toMatchObject([
|
expect(result).toMatchObject([
|
||||||
{
|
{
|
||||||
start: '2023-10-20T23:30:00.000Z',
|
start: '2023-10-20T23:30:00.000Z',
|
||||||
stop: '2023-10-21T01:00:00.000Z',
|
stop: '2023-10-21T01:00:00.000Z',
|
||||||
title: 'Tokyo Day 5 QF 2',
|
title: 'Tokyo Day 5 QF 2',
|
||||||
description: 'Exclusive coverage of the 2023 ATP Tour on beIN SPORTS'
|
description: 'Exclusive coverage of the 2023 ATP Tour on beIN SPORTS'
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const result = parser({
|
const result = parser({
|
||||||
content: '[]'
|
content: '[]'
|
||||||
})
|
})
|
||||||
expect(result).toMatchObject([])
|
expect(result).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,93 +1,93 @@
|
|||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
dayjs.Ls.en.weekStart = 1
|
dayjs.Ls.en.weekStart = 1
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'berrymedia.co.kr',
|
site: 'berrymedia.co.kr',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ channel }) {
|
url({ channel }) {
|
||||||
return `http://www.berrymedia.co.kr/schedule_proc${channel.site_id}.php`
|
return `http://www.berrymedia.co.kr/schedule_proc${channel.site_id}.php`
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
},
|
},
|
||||||
data({ date }) {
|
data({ date }) {
|
||||||
let params = new URLSearchParams()
|
let params = new URLSearchParams()
|
||||||
let startOfWeek = date.startOf('week').format('YYYY-MM-DD')
|
let startOfWeek = date.startOf('week').format('YYYY-MM-DD')
|
||||||
let endOfWeek = date.endOf('week').format('YYYY-MM-DD')
|
let endOfWeek = date.endOf('week').format('YYYY-MM-DD')
|
||||||
|
|
||||||
params.append('week', `${startOfWeek}~${endOfWeek}`)
|
params.append('week', `${startOfWeek}~${endOfWeek}`)
|
||||||
params.append('day', date.format('YYYY-MM-DD'))
|
params.append('day', date.format('YYYY-MM-DD'))
|
||||||
|
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
parser({ content, date }) {
|
parser({ content, date }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
const items = parseItems(content)
|
const items = parseItems(content)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const $item = cheerio.load(item)
|
const $item = cheerio.load(item)
|
||||||
const prev = programs[programs.length - 1]
|
const prev = programs[programs.length - 1]
|
||||||
let start = parseStart($item, date)
|
let start = parseStart($item, date)
|
||||||
if (prev) {
|
if (prev) {
|
||||||
if (start.isBefore(prev.start)) {
|
if (start.isBefore(prev.start)) {
|
||||||
start = start.add(1, 'd')
|
start = start.add(1, 'd')
|
||||||
date = date.add(1, 'd')
|
date = date.add(1, 'd')
|
||||||
}
|
}
|
||||||
prev.stop = start
|
prev.stop = start
|
||||||
}
|
}
|
||||||
const stop = start.add(30, 'm')
|
const stop = start.add(30, 'm')
|
||||||
programs.push({
|
programs.push({
|
||||||
title: parseTitle($item),
|
title: parseTitle($item),
|
||||||
category: parseCategory($item),
|
category: parseCategory($item),
|
||||||
rating: parseRating($item),
|
rating: parseRating($item),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart($item, date) {
|
function parseStart($item, date) {
|
||||||
const time = $item('span:nth-child(1)').text().trim()
|
const time = $item('span:nth-child(1)').text().trim()
|
||||||
|
|
||||||
return dayjs.tz(`${date.format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm', 'Asia/Seoul')
|
return dayjs.tz(`${date.format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm', 'Asia/Seoul')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTitle($item) {
|
function parseTitle($item) {
|
||||||
return $item('span.sdfsdf').clone().children().remove().end().text().trim()
|
return $item('span.sdfsdf').clone().children().remove().end().text().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseCategory($item) {
|
function parseCategory($item) {
|
||||||
return $item('span:nth-child(2) > p').text().trim()
|
return $item('span:nth-child(2) > p').text().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseRating($item) {
|
function parseRating($item) {
|
||||||
const rating = $item('span:nth-child(5) > p:nth-child(1)').text().trim()
|
const rating = $item('span:nth-child(5) > p:nth-child(1)').text().trim()
|
||||||
|
|
||||||
return rating
|
return rating
|
||||||
? {
|
? {
|
||||||
system: 'KMRB',
|
system: 'KMRB',
|
||||||
value: rating
|
value: rating
|
||||||
}
|
}
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content) {
|
function parseItems(content) {
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
|
|
||||||
return $('.sc_time dd').toArray()
|
return $('.sc_time dd').toArray()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,77 +1,77 @@
|
|||||||
const { parser, url, request } = require('./berrymedia.co.kr.config.js')
|
const { parser, url, request } = require('./berrymedia.co.kr.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2023-01-26', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2023-01-26', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: '',
|
site_id: '',
|
||||||
xmltv_id: 'GTV.kr'
|
xmltv_id: 'GTV.kr'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ channel })).toBe('http://www.berrymedia.co.kr/schedule_proc.php')
|
expect(url({ channel })).toBe('http://www.berrymedia.co.kr/schedule_proc.php')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate request method', () => {
|
it('can generate request method', () => {
|
||||||
expect(request.method).toBe('POST')
|
expect(request.method).toBe('POST')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request headers', () => {
|
it('can generate valid request headers', () => {
|
||||||
expect(request.headers).toMatchObject({
|
expect(request.headers).toMatchObject({
|
||||||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request data', () => {
|
it('can generate valid request data', () => {
|
||||||
let params = request.data({ date })
|
let params = request.data({ date })
|
||||||
|
|
||||||
expect(params.get('week')).toBe('2023-01-23~2023-01-29')
|
expect(params.get('week')).toBe('2023-01-23~2023-01-29')
|
||||||
expect(params.get('day')).toBe('2023-01-26')
|
expect(params.get('day')).toBe('2023-01-26')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'), 'utf8')
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'), 'utf8')
|
||||||
let results = parser({ content, date })
|
let results = parser({ content, date })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2023-01-25T15:00:00.000Z',
|
start: '2023-01-25T15:00:00.000Z',
|
||||||
stop: '2023-01-25T16:00:00.000Z',
|
stop: '2023-01-25T16:00:00.000Z',
|
||||||
title: '더트롯쇼',
|
title: '더트롯쇼',
|
||||||
category: '연예/오락',
|
category: '연예/오락',
|
||||||
rating: {
|
rating: {
|
||||||
system: 'KMRB',
|
system: 'KMRB',
|
||||||
value: '15'
|
value: '15'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[17]).toMatchObject({
|
expect(results[17]).toMatchObject({
|
||||||
start: '2023-01-26T13:50:00.000Z',
|
start: '2023-01-26T13:50:00.000Z',
|
||||||
stop: '2023-01-26T14:20:00.000Z',
|
stop: '2023-01-26T14:20:00.000Z',
|
||||||
title: '나는 자연인이다',
|
title: '나는 자연인이다',
|
||||||
category: '교양',
|
category: '교양',
|
||||||
rating: {
|
rating: {
|
||||||
system: 'KMRB',
|
system: 'KMRB',
|
||||||
value: 'ALL'
|
value: 'ALL'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const results = parser({
|
const results = parser({
|
||||||
date,
|
date,
|
||||||
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.html'), 'utf8')
|
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.html'), 'utf8')
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,133 +1,133 @@
|
|||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
const API_ENDPOINT = 'https://www.reportv.com.ar/finder'
|
const API_ENDPOINT = 'https://www.reportv.com.ar/finder'
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'cableplus.com.uy',
|
site: 'cableplus.com.uy',
|
||||||
days: 2,
|
days: 2,
|
||||||
url: `${API_ENDPOINT}/channel`,
|
url: `${API_ENDPOINT}/channel`,
|
||||||
request: {
|
request: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
||||||
},
|
},
|
||||||
data({ date, channel }) {
|
data({ date, channel }) {
|
||||||
const params = new URLSearchParams()
|
const params = new URLSearchParams()
|
||||||
params.append('idAlineacion', '3017')
|
params.append('idAlineacion', '3017')
|
||||||
params.append('idSenial', channel.site_id)
|
params.append('idSenial', channel.site_id)
|
||||||
params.append('fecha', date.format('YYYY-MM-DD'))
|
params.append('fecha', date.format('YYYY-MM-DD'))
|
||||||
params.append('hora', '00:00')
|
params.append('hora', '00:00')
|
||||||
|
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
parser({ content, date }) {
|
parser({ content, date }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
const items = parseItems(content, date)
|
const items = parseItems(content, date)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const $item = cheerio.load(item)
|
const $item = cheerio.load(item)
|
||||||
const prev = programs[programs.length - 1]
|
const prev = programs[programs.length - 1]
|
||||||
let start = parseStart($item, date)
|
let start = parseStart($item, date)
|
||||||
if (prev) {
|
if (prev) {
|
||||||
if (start.isBefore(prev.start)) {
|
if (start.isBefore(prev.start)) {
|
||||||
start = start.add(1, 'd')
|
start = start.add(1, 'd')
|
||||||
date = date.add(1, 'd')
|
date = date.add(1, 'd')
|
||||||
}
|
}
|
||||||
prev.stop = start
|
prev.stop = start
|
||||||
}
|
}
|
||||||
const stop = start.add(30, 'm')
|
const stop = start.add(30, 'm')
|
||||||
programs.push({
|
programs.push({
|
||||||
title: parseTitle($item),
|
title: parseTitle($item),
|
||||||
categories: parseCategories($item),
|
categories: parseCategories($item),
|
||||||
image: parseImage($item),
|
image: parseImage($item),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const params = new URLSearchParams({ idAlineacion: '3017' })
|
const params = new URLSearchParams({ idAlineacion: '3017' })
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.post(`${API_ENDPOINT}/channelGrid`, params, {
|
.post(`${API_ENDPOINT}/channelGrid`, params, {
|
||||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }
|
||||||
})
|
})
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
const $ = cheerio.load(data)
|
const $ = cheerio.load(data)
|
||||||
|
|
||||||
return $('.senial')
|
return $('.senial')
|
||||||
.map(function () {
|
.map(function () {
|
||||||
return {
|
return {
|
||||||
lang: 'es',
|
lang: 'es',
|
||||||
site_id: $(this).attr('id'),
|
site_id: $(this).attr('id'),
|
||||||
name: $(this).find('img').attr('alt')
|
name: $(this).find('img').attr('alt')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.get()
|
.get()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTitle($item) {
|
function parseTitle($item) {
|
||||||
return $item('p.evento_titulo.texto_a_continuacion.dotdotdot,.programa-titulo > span:first-child')
|
return $item('p.evento_titulo.texto_a_continuacion.dotdotdot,.programa-titulo > span:first-child')
|
||||||
.text()
|
.text()
|
||||||
.trim()
|
.trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseImage($item) {
|
function parseImage($item) {
|
||||||
return $item('img').data('src') || $item('img').attr('src') || null
|
return $item('img').data('src') || $item('img').attr('src') || null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseCategories($item) {
|
function parseCategories($item) {
|
||||||
return $item('p.evento_genero')
|
return $item('p.evento_genero')
|
||||||
.map(function () {
|
.map(function () {
|
||||||
return $item(this).text().trim()
|
return $item(this).text().trim()
|
||||||
})
|
})
|
||||||
.toArray()
|
.toArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart($item, date) {
|
function parseStart($item, date) {
|
||||||
let time = $item('.grid_fecha_hora').text().trim()
|
let time = $item('.grid_fecha_hora').text().trim()
|
||||||
|
|
||||||
if (time) {
|
if (time) {
|
||||||
return dayjs.tz(`${date.format('YYYY')} ${time}`, 'YYYY DD-MM HH:mm[hs.]', 'America/Montevideo')
|
return dayjs.tz(`${date.format('YYYY')} ${time}`, 'YYYY DD-MM HH:mm[hs.]', 'America/Montevideo')
|
||||||
}
|
}
|
||||||
|
|
||||||
time = $item('.fechaHora').text().trim()
|
time = $item('.fechaHora').text().trim()
|
||||||
|
|
||||||
return time
|
return time
|
||||||
? dayjs.tz(`${date.format('YYYY')} ${time}`, 'YYYY DD/MM HH:mm[hs.]', 'America/Montevideo')
|
? dayjs.tz(`${date.format('YYYY')} ${time}`, 'YYYY DD/MM HH:mm[hs.]', 'America/Montevideo')
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, date) {
|
function parseItems(content, date) {
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
|
|
||||||
let featuredItems = $('.vista-pc > .programacion-fila > .channel-programa')
|
let featuredItems = $('.vista-pc > .programacion-fila > .channel-programa')
|
||||||
.filter(function () {
|
.filter(function () {
|
||||||
return $(this).find('.grid_fecha_hora').text().indexOf(date.format('DD-MM')) > -1
|
return $(this).find('.grid_fecha_hora').text().indexOf(date.format('DD-MM')) > -1
|
||||||
})
|
})
|
||||||
.toArray()
|
.toArray()
|
||||||
let otherItems = $('#owl-pc > .item-program')
|
let otherItems = $('#owl-pc > .item-program')
|
||||||
.filter(function () {
|
.filter(function () {
|
||||||
return (
|
return (
|
||||||
$(this)
|
$(this)
|
||||||
.find('.evento_titulo > .horario > p.fechaHora')
|
.find('.evento_titulo > .horario > p.fechaHora')
|
||||||
.text()
|
.text()
|
||||||
.indexOf(date.format('DD/MM')) > -1
|
.indexOf(date.format('DD/MM')) > -1
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.toArray()
|
.toArray()
|
||||||
|
|
||||||
return featuredItems.concat(otherItems)
|
return featuredItems.concat(otherItems)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,73 +1,73 @@
|
|||||||
const { parser, url, request } = require('./cableplus.com.uy.config.js')
|
const { parser, url, request } = require('./cableplus.com.uy.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2023-02-12', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2023-02-12', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: '2035',
|
site_id: '2035',
|
||||||
xmltv_id: 'APlusV.uy'
|
xmltv_id: 'APlusV.uy'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url).toBe('https://www.reportv.com.ar/finder/channel')
|
expect(url).toBe('https://www.reportv.com.ar/finder/channel')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request method', () => {
|
it('can generate valid request method', () => {
|
||||||
expect(request.method).toBe('POST')
|
expect(request.method).toBe('POST')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request headers', () => {
|
it('can generate valid request headers', () => {
|
||||||
expect(request.headers).toMatchObject({
|
expect(request.headers).toMatchObject({
|
||||||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request data', () => {
|
it('can generate valid request data', () => {
|
||||||
const params = request.data({ date, channel })
|
const params = request.data({ date, channel })
|
||||||
|
|
||||||
expect(params.get('idAlineacion')).toBe('3017')
|
expect(params.get('idAlineacion')).toBe('3017')
|
||||||
expect(params.get('idSenial')).toBe('2035')
|
expect(params.get('idSenial')).toBe('2035')
|
||||||
expect(params.get('fecha')).toBe('2023-02-12')
|
expect(params.get('fecha')).toBe('2023-02-12')
|
||||||
expect(params.get('hora')).toBe('00:00')
|
expect(params.get('hora')).toBe('00:00')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'), 'utf8')
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'), 'utf8')
|
||||||
let results = parser({ content, date })
|
let results = parser({ content, date })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(21)
|
expect(results.length).toBe(21)
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2023-02-12T09:30:00.000Z',
|
start: '2023-02-12T09:30:00.000Z',
|
||||||
stop: '2023-02-12T10:30:00.000Z',
|
stop: '2023-02-12T10:30:00.000Z',
|
||||||
title: 'Revista agropecuaria',
|
title: 'Revista agropecuaria',
|
||||||
image: 'https://www.reportv.com.ar/buscador/img/Programas/2797844.jpg',
|
image: 'https://www.reportv.com.ar/buscador/img/Programas/2797844.jpg',
|
||||||
categories: []
|
categories: []
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[4]).toMatchObject({
|
expect(results[4]).toMatchObject({
|
||||||
start: '2023-02-12T12:30:00.000Z',
|
start: '2023-02-12T12:30:00.000Z',
|
||||||
stop: '2023-02-12T13:30:00.000Z',
|
stop: '2023-02-12T13:30:00.000Z',
|
||||||
title: 'De pago en pago',
|
title: 'De pago en pago',
|
||||||
image: 'https://www.reportv.com.ar/buscador/img/Programas/3772835.jpg',
|
image: 'https://www.reportv.com.ar/buscador/img/Programas/3772835.jpg',
|
||||||
categories: ['Cultural']
|
categories: ['Cultural']
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const result = parser({
|
const result = parser({
|
||||||
date,
|
date,
|
||||||
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.html'), 'utf8')
|
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.html'), 'utf8')
|
||||||
})
|
})
|
||||||
expect(result).toMatchObject([])
|
expect(result).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,197 +1,197 @@
|
|||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'canalplus.com',
|
site: 'canalplus.com',
|
||||||
days: 2,
|
days: 2,
|
||||||
url: async function ({ channel, date }) {
|
url: async function ({ channel, date }) {
|
||||||
const [region, site_id] = channel.site_id.split('#')
|
const [region, site_id] = channel.site_id.split('#')
|
||||||
|
|
||||||
const baseUrl =
|
const baseUrl =
|
||||||
region === 'pl'
|
region === 'pl'
|
||||||
? 'https://www.canalplus.com/pl/program-tv/'
|
? 'https://www.canalplus.com/pl/program-tv/'
|
||||||
: `https://www.canalplus.com/${region}/programme-tv/`
|
: `https://www.canalplus.com/${region}/programme-tv/`
|
||||||
|
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(baseUrl)
|
.get(baseUrl)
|
||||||
.then(r => r.data.toString())
|
.then(r => r.data.toString())
|
||||||
.catch(err => console.log(err))
|
.catch(err => console.log(err))
|
||||||
|
|
||||||
const token = parseToken(data)
|
const token = parseToken(data)
|
||||||
const path = region === 'pl' ? 'mycanalint' : 'mycanal'
|
const path = region === 'pl' ? 'mycanalint' : 'mycanal'
|
||||||
const diff = date.diff(dayjs.utc().startOf('d'), 'd')
|
const diff = date.diff(dayjs.utc().startOf('d'), 'd')
|
||||||
|
|
||||||
return `https://hodor.canalplus.pro/api/v2/${path}/channels/${token}/${site_id}/broadcasts/day/${diff}`
|
return `https://hodor.canalplus.pro/api/v2/${path}/channels/${token}/${site_id}/broadcasts/day/${diff}`
|
||||||
},
|
},
|
||||||
async parser({ content }) {
|
async parser({ content }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
const items = parseItems(content)
|
const items = parseItems(content)
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
const prev = programs[programs.length - 1]
|
const prev = programs[programs.length - 1]
|
||||||
const details = await loadProgramDetails(item)
|
const details = await loadProgramDetails(item)
|
||||||
const info = parseInfo(details)
|
const info = parseInfo(details)
|
||||||
const start = parseStart(item)
|
const start = parseStart(item)
|
||||||
if (prev) prev.stop = start
|
if (prev) prev.stop = start
|
||||||
const stop = start.add(1, 'h')
|
const stop = start.add(1, 'h')
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
description: parseDescription(info),
|
description: parseDescription(info),
|
||||||
image: parseImage(info),
|
image: parseImage(info),
|
||||||
actors: parseCast(info, 'Avec :'),
|
actors: parseCast(info, 'Avec :'),
|
||||||
director: parseCast(info, 'De :'),
|
director: parseCast(info, 'De :'),
|
||||||
writer: parseCast(info, 'Scénario :'),
|
writer: parseCast(info, 'Scénario :'),
|
||||||
composer: parseCast(info, 'Musique :'),
|
composer: parseCast(info, 'Musique :'),
|
||||||
presenter: parseCast(info, 'Présenté par :'),
|
presenter: parseCast(info, 'Présenté par :'),
|
||||||
date: parseDate(info),
|
date: parseDate(info),
|
||||||
rating: parseRating(info),
|
rating: parseRating(info),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels({ country }) {
|
async channels({ country }) {
|
||||||
const paths = {
|
const paths = {
|
||||||
ad: 'cpafr/ad',
|
ad: 'cpafr/ad',
|
||||||
bf: 'cpafr/bf',
|
bf: 'cpafr/bf',
|
||||||
bi: 'cpafr/bi',
|
bi: 'cpafr/bi',
|
||||||
bj: 'cpafr/bj',
|
bj: 'cpafr/bj',
|
||||||
bl: 'cpant/bl',
|
bl: 'cpant/bl',
|
||||||
cd: 'cpafr/cd',
|
cd: 'cpafr/cd',
|
||||||
cf: 'cpafr/cf',
|
cf: 'cpafr/cf',
|
||||||
cg: 'cpafr/cg',
|
cg: 'cpafr/cg',
|
||||||
ch: 'cpche',
|
ch: 'cpche',
|
||||||
ci: 'cpafr/ci',
|
ci: 'cpafr/ci',
|
||||||
cm: 'cpafr/cm',
|
cm: 'cpafr/cm',
|
||||||
cv: 'cpafr/cv',
|
cv: 'cpafr/cv',
|
||||||
dj: 'cpafr/dj',
|
dj: 'cpafr/dj',
|
||||||
fr: 'cpfra',
|
fr: 'cpfra',
|
||||||
ga: 'cpafr/ga',
|
ga: 'cpafr/ga',
|
||||||
gf: 'cpant/gf',
|
gf: 'cpant/gf',
|
||||||
gh: 'cpafr/gh',
|
gh: 'cpafr/gh',
|
||||||
gm: 'cpafr/gm',
|
gm: 'cpafr/gm',
|
||||||
gn: 'cpafr/gn',
|
gn: 'cpafr/gn',
|
||||||
gp: 'cpafr/gp',
|
gp: 'cpafr/gp',
|
||||||
gw: 'cpafr/gw',
|
gw: 'cpafr/gw',
|
||||||
ht: 'cpant/ht',
|
ht: 'cpant/ht',
|
||||||
mf: 'cpant/mf',
|
mf: 'cpant/mf',
|
||||||
mg: 'cpafr/mg',
|
mg: 'cpafr/mg',
|
||||||
ml: 'cpafr/ml',
|
ml: 'cpafr/ml',
|
||||||
mq: 'cpant/mq',
|
mq: 'cpant/mq',
|
||||||
mr: 'cpafr/mr',
|
mr: 'cpafr/mr',
|
||||||
mu: 'cpmus/mu',
|
mu: 'cpmus/mu',
|
||||||
nc: 'cpncl/nc',
|
nc: 'cpncl/nc',
|
||||||
ne: 'cpafr/ne',
|
ne: 'cpafr/ne',
|
||||||
pf: 'cppyf/pf',
|
pf: 'cppyf/pf',
|
||||||
pl: 'cppol',
|
pl: 'cppol',
|
||||||
re: 'cpreu/re',
|
re: 'cpreu/re',
|
||||||
rw: 'cpafr/rw',
|
rw: 'cpafr/rw',
|
||||||
sl: 'cpafr/sl',
|
sl: 'cpafr/sl',
|
||||||
sn: 'cpafr/sn',
|
sn: 'cpafr/sn',
|
||||||
td: 'cpafr/td',
|
td: 'cpafr/td',
|
||||||
tg: 'cpafr/tg',
|
tg: 'cpafr/tg',
|
||||||
wf: 'cpncl/wf',
|
wf: 'cpncl/wf',
|
||||||
yt: 'cpreu/yt'
|
yt: 'cpreu/yt'
|
||||||
}
|
}
|
||||||
|
|
||||||
let channels = []
|
let channels = []
|
||||||
const path = paths[country]
|
const path = paths[country]
|
||||||
const url = `https://secure-webtv-static.canal-plus.com/metadata/${path}/all/v2.2/globalchannels.json`
|
const url = `https://secure-webtv-static.canal-plus.com/metadata/${path}/all/v2.2/globalchannels.json`
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(url)
|
.get(url)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
data.channels.forEach(channel => {
|
data.channels.forEach(channel => {
|
||||||
const site_id = country === 'fr' ? `#${channel.id}` : `${country}#${channel.id}`
|
const site_id = country === 'fr' ? `#${channel.id}` : `${country}#${channel.id}`
|
||||||
|
|
||||||
if (channel.name === '.') return
|
if (channel.name === '.') return
|
||||||
|
|
||||||
channels.push({
|
channels.push({
|
||||||
lang: 'fr',
|
lang: 'fr',
|
||||||
site_id,
|
site_id,
|
||||||
name: channel.name
|
name: channel.name
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return channels
|
return channels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseToken(data) {
|
function parseToken(data) {
|
||||||
const [, token] = data.match(/"token":"([^"]+)/) || [null, null]
|
const [, token] = data.match(/"token":"([^"]+)/) || [null, null]
|
||||||
|
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart(item) {
|
function parseStart(item) {
|
||||||
return item && item.startTime ? dayjs(item.startTime) : null
|
return item && item.startTime ? dayjs(item.startTime) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseImage(info) {
|
function parseImage(info) {
|
||||||
return info ? info.URLImage : null
|
return info ? info.URLImage : null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDescription(info) {
|
function parseDescription(info) {
|
||||||
return info ? info.summary : null
|
return info ? info.summary : null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseInfo(data) {
|
function parseInfo(data) {
|
||||||
if (!data || !data.detail || !data.detail.informations) return null
|
if (!data || !data.detail || !data.detail.informations) return null
|
||||||
|
|
||||||
return data.detail.informations
|
return data.detail.informations
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadProgramDetails(item) {
|
async function loadProgramDetails(item) {
|
||||||
if (!item.onClick || !item.onClick.URLPage) return {}
|
if (!item.onClick || !item.onClick.URLPage) return {}
|
||||||
|
|
||||||
return await axios
|
return await axios
|
||||||
.get(item.onClick.URLPage)
|
.get(item.onClick.URLPage)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content) {
|
function parseItems(content) {
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
if (!data || !Array.isArray(data.timeSlices)) return []
|
if (!data || !Array.isArray(data.timeSlices)) return []
|
||||||
|
|
||||||
return data.timeSlices.reduce((acc, curr) => {
|
return data.timeSlices.reduce((acc, curr) => {
|
||||||
acc = acc.concat(curr.contents)
|
acc = acc.concat(curr.contents)
|
||||||
return acc
|
return acc
|
||||||
}, [])
|
}, [])
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseCast(info, type) {
|
function parseCast(info, type) {
|
||||||
let people = []
|
let people = []
|
||||||
if (info && info.personnalities) {
|
if (info && info.personnalities) {
|
||||||
const personnalities = info.personnalities.find(i => i.prefix == type)
|
const personnalities = info.personnalities.find(i => i.prefix == type)
|
||||||
if (!personnalities) return people
|
if (!personnalities) return people
|
||||||
for (let person of personnalities.personnalitiesList) {
|
for (let person of personnalities.personnalitiesList) {
|
||||||
people.push(person.title)
|
people.push(person.title)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return people
|
return people
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDate(info) {
|
function parseDate(info) {
|
||||||
return info && info.productionYear ? info.productionYear : null
|
return info && info.productionYear ? info.productionYear : null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseRating(info) {
|
function parseRating(info) {
|
||||||
if (!info || !info.parentalRatings) return null
|
if (!info || !info.parentalRatings) return null
|
||||||
let rating = info.parentalRatings.find(i => i.authority === 'CSA')
|
let rating = info.parentalRatings.find(i => i.authority === 'CSA')
|
||||||
if (!rating || Array.isArray(rating)) return null
|
if (!rating || Array.isArray(rating)) return null
|
||||||
if (rating.value === '1') return null
|
if (rating.value === '1') return null
|
||||||
if (rating.value === '2') rating.value = '-10'
|
if (rating.value === '2') rating.value = '-10'
|
||||||
if (rating.value === '3') rating.value = '-12'
|
if (rating.value === '3') rating.value = '-12'
|
||||||
if (rating.value === '4') rating.value = '-16'
|
if (rating.value === '4') rating.value = '-16'
|
||||||
if (rating.value === '5') rating.value = '-18'
|
if (rating.value === '5') rating.value = '-18'
|
||||||
return {
|
return {
|
||||||
system: rating.authority,
|
system: rating.authority,
|
||||||
value: rating.value
|
value: rating.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,146 +1,146 @@
|
|||||||
const { parser, url } = require('./canalplus.com.config.js')
|
const { parser, url } = require('./canalplus.com.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
jest.mock('axios')
|
jest.mock('axios')
|
||||||
|
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: 'bi#198',
|
site_id: 'bi#198',
|
||||||
xmltv_id: 'CanalPlusCinemaFrance.fr'
|
xmltv_id: 'CanalPlusCinemaFrance.fr'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url for today', done => {
|
it('can generate valid url for today', done => {
|
||||||
axios.get.mockImplementation(url => {
|
axios.get.mockImplementation(url => {
|
||||||
if (url === 'https://www.canalplus.com/bi/programme-tv/') {
|
if (url === 'https://www.canalplus.com/bi/programme-tv/') {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: fs.readFileSync(path.resolve(__dirname, '__data__/programme-tv.html'))
|
data: fs.readFileSync(path.resolve(__dirname, '__data__/programme-tv.html'))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve({ data: '' })
|
return Promise.resolve({ data: '' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const today = dayjs.utc().startOf('d')
|
const today = dayjs.utc().startOf('d')
|
||||||
url({ channel, date: today })
|
url({ channel, date: today })
|
||||||
.then(result => {
|
.then(result => {
|
||||||
expect(result).toBe(
|
expect(result).toBe(
|
||||||
'https://hodor.canalplus.pro/api/v2/mycanal/channels/f000c6f4ebf44647682b3a0fa66d7d99/198/broadcasts/day/0'
|
'https://hodor.canalplus.pro/api/v2/mycanal/channels/f000c6f4ebf44647682b3a0fa66d7d99/198/broadcasts/day/0'
|
||||||
)
|
)
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
.catch(done)
|
.catch(done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid url for tomorrow', done => {
|
it('can generate valid url for tomorrow', done => {
|
||||||
axios.get.mockImplementation(url => {
|
axios.get.mockImplementation(url => {
|
||||||
if (url === 'https://www.canalplus.com/bi/programme-tv/') {
|
if (url === 'https://www.canalplus.com/bi/programme-tv/') {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: fs.readFileSync(path.resolve(__dirname, '__data__/programme-tv.html'))
|
data: fs.readFileSync(path.resolve(__dirname, '__data__/programme-tv.html'))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve({ data: '' })
|
return Promise.resolve({ data: '' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const tomorrow = dayjs.utc().startOf('d').add(1, 'd')
|
const tomorrow = dayjs.utc().startOf('d').add(1, 'd')
|
||||||
url({ channel, date: tomorrow })
|
url({ channel, date: tomorrow })
|
||||||
.then(result => {
|
.then(result => {
|
||||||
expect(result).toBe(
|
expect(result).toBe(
|
||||||
'https://hodor.canalplus.pro/api/v2/mycanal/channels/f000c6f4ebf44647682b3a0fa66d7d99/198/broadcasts/day/1'
|
'https://hodor.canalplus.pro/api/v2/mycanal/channels/f000c6f4ebf44647682b3a0fa66d7d99/198/broadcasts/day/1'
|
||||||
)
|
)
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
.catch(done)
|
.catch(done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', done => {
|
it('can parse response', done => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
||||||
|
|
||||||
axios.get.mockImplementation(url => {
|
axios.get.mockImplementation(url => {
|
||||||
if (
|
if (
|
||||||
url ===
|
url ===
|
||||||
'https://hodor.canalplus.pro/api/v2/mycanal/detail/f000c6f4ebf44647682b3a0fa66d7d99/okapi/6564630_50001.json?detailType=detailSeason&objectType=season&broadcastID=PLM_1196447642&episodeId=20482220_50001&brandID=4501558_50001&fromDiff=true'
|
'https://hodor.canalplus.pro/api/v2/mycanal/detail/f000c6f4ebf44647682b3a0fa66d7d99/okapi/6564630_50001.json?detailType=detailSeason&objectType=season&broadcastID=PLM_1196447642&episodeId=20482220_50001&brandID=4501558_50001&fromDiff=true'
|
||||||
) {
|
) {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/program1.json')))
|
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/program1.json')))
|
||||||
})
|
})
|
||||||
} else if (
|
} else if (
|
||||||
url ===
|
url ===
|
||||||
'https://hodor.canalplus.pro/api/v2/mycanal/detail/f000c6f4ebf44647682b3a0fa66d7d99/okapi/17230453_50001.json?detailType=detailPage&objectType=unit&broadcastID=PLM_1196447637&fromDiff=true'
|
'https://hodor.canalplus.pro/api/v2/mycanal/detail/f000c6f4ebf44647682b3a0fa66d7d99/okapi/17230453_50001.json?detailType=detailPage&objectType=unit&broadcastID=PLM_1196447637&fromDiff=true'
|
||||||
) {
|
) {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/program2.json')))
|
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/program2.json')))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve({ data: '' })
|
return Promise.resolve({ data: '' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
parser({ content })
|
parser({ content })
|
||||||
.then(result => {
|
.then(result => {
|
||||||
result.map(p => {
|
result.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(result).toMatchObject([
|
expect(result).toMatchObject([
|
||||||
{
|
{
|
||||||
start: '2023-01-12T06:28:00.000Z',
|
start: '2023-01-12T06:28:00.000Z',
|
||||||
stop: '2023-01-12T12:06:00.000Z',
|
stop: '2023-01-12T12:06:00.000Z',
|
||||||
title: 'Le cercle',
|
title: 'Le cercle',
|
||||||
description:
|
description:
|
||||||
"Tant qu'il y aura du cinéma, LE CERCLE sera là. C'est la seule émission télévisée de débats critiques 100% consacrée au cinéma et elle rentre dans sa 18e saison. Chaque semaine, elle offre des joutes enflammées, joyeuses et sans condescendance, sur les films à l'affiche ; et invite avec \"Le questionnaire du CERCLE\" les réalisatrices et réalisateurs à venir partager leur passion cinéphile.",
|
"Tant qu'il y aura du cinéma, LE CERCLE sera là. C'est la seule émission télévisée de débats critiques 100% consacrée au cinéma et elle rentre dans sa 18e saison. Chaque semaine, elle offre des joutes enflammées, joyeuses et sans condescendance, sur les films à l'affiche ; et invite avec \"Le questionnaire du CERCLE\" les réalisatrices et réalisateurs à venir partager leur passion cinéphile.",
|
||||||
image:
|
image:
|
||||||
'https://thumb.canalplus.pro/http/unsafe/{resolutionXY}/filters:quality({imageQualityPercentage})/img-hapi.canalplus.pro:80/ServiceImage/ImageID/107297573',
|
'https://thumb.canalplus.pro/http/unsafe/{resolutionXY}/filters:quality({imageQualityPercentage})/img-hapi.canalplus.pro:80/ServiceImage/ImageID/107297573',
|
||||||
presenter: ['Lily Bloom'],
|
presenter: ['Lily Bloom'],
|
||||||
rating: {
|
rating: {
|
||||||
system: 'CSA',
|
system: 'CSA',
|
||||||
value: '-10'
|
value: '-10'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
start: '2023-01-12T12:06:00.000Z',
|
start: '2023-01-12T12:06:00.000Z',
|
||||||
stop: '2023-01-12T13:06:00.000Z',
|
stop: '2023-01-12T13:06:00.000Z',
|
||||||
title: 'Illusions perdues',
|
title: 'Illusions perdues',
|
||||||
description:
|
description:
|
||||||
"Pendant la Restauration, Lucien de Rubempré, jeune provincial d'Angoulême, se rêve poète. Il débarque à Paris en quête de gloire. Il a le soutien de Louise de Bargeton, une aristocrate qui croit en son talent. Pour gagner sa vie, Lucien trouve un emploi dans le journal dirigé par le peu scrupuleux Etienne Lousteau...",
|
"Pendant la Restauration, Lucien de Rubempré, jeune provincial d'Angoulême, se rêve poète. Il débarque à Paris en quête de gloire. Il a le soutien de Louise de Bargeton, une aristocrate qui croit en son talent. Pour gagner sa vie, Lucien trouve un emploi dans le journal dirigé par le peu scrupuleux Etienne Lousteau...",
|
||||||
image:
|
image:
|
||||||
'https://thumb.canalplus.pro/http/unsafe/{resolutionXY}/filters:quality({imageQualityPercentage})/img-hapi.canalplus.pro:80/ServiceImage/ImageID/107356485',
|
'https://thumb.canalplus.pro/http/unsafe/{resolutionXY}/filters:quality({imageQualityPercentage})/img-hapi.canalplus.pro:80/ServiceImage/ImageID/107356485',
|
||||||
director: ['Xavier Giannoli'],
|
director: ['Xavier Giannoli'],
|
||||||
actors: [
|
actors: [
|
||||||
'Benjamin Voisin',
|
'Benjamin Voisin',
|
||||||
'Cécile de France',
|
'Cécile de France',
|
||||||
'Vincent Lacoste',
|
'Vincent Lacoste',
|
||||||
'Xavier Dolan',
|
'Xavier Dolan',
|
||||||
'Gérard Depardieu',
|
'Gérard Depardieu',
|
||||||
'Salomé Dewaels',
|
'Salomé Dewaels',
|
||||||
'Jeanne Balibar',
|
'Jeanne Balibar',
|
||||||
'Louis-Do de Lencquesaing',
|
'Louis-Do de Lencquesaing',
|
||||||
'Alexis Barbosa',
|
'Alexis Barbosa',
|
||||||
'Jean-François Stévenin',
|
'Jean-François Stévenin',
|
||||||
'André Marcon',
|
'André Marcon',
|
||||||
'Marie Cornillon'
|
'Marie Cornillon'
|
||||||
],
|
],
|
||||||
writer: ['Xavier Giannoli'],
|
writer: ['Xavier Giannoli'],
|
||||||
rating: {
|
rating: {
|
||||||
system: 'CSA',
|
system: 'CSA',
|
||||||
value: '-10'
|
value: '-10'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
.catch(done)
|
.catch(done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', async () => {
|
it('can handle empty guide', async () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/no_content.json'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/no_content.json'))
|
||||||
const result = await parser({ content })
|
const result = await parser({ content })
|
||||||
expect(result).toMatchObject([])
|
expect(result).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,92 +1,92 @@
|
|||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'cgates.lt',
|
site: 'cgates.lt',
|
||||||
days: 2,
|
days: 2,
|
||||||
url: function ({ channel }) {
|
url: function ({ channel }) {
|
||||||
return `https://www.cgates.lt/tv-kanalai/${channel.site_id}/`
|
return `https://www.cgates.lt/tv-kanalai/${channel.site_id}/`
|
||||||
},
|
},
|
||||||
parser: function ({ content, date }) {
|
parser: function ({ content, date }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
const items = parseItems(content, date)
|
const items = parseItems(content, date)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const prev = programs[programs.length - 1]
|
const prev = programs[programs.length - 1]
|
||||||
const $item = cheerio.load(item)
|
const $item = cheerio.load(item)
|
||||||
let start = parseStart($item, date)
|
let start = parseStart($item, date)
|
||||||
if (prev) {
|
if (prev) {
|
||||||
if (start.isBefore(prev.start)) {
|
if (start.isBefore(prev.start)) {
|
||||||
start = start.add(1, 'd')
|
start = start.add(1, 'd')
|
||||||
date = date.add(1, 'd')
|
date = date.add(1, 'd')
|
||||||
}
|
}
|
||||||
prev.stop = start
|
prev.stop = start
|
||||||
}
|
}
|
||||||
const stop = start.add(30, 'm')
|
const stop = start.add(30, 'm')
|
||||||
programs.push({
|
programs.push({
|
||||||
title: parseTitle($item),
|
title: parseTitle($item),
|
||||||
description: parseDescription($item),
|
description: parseDescription($item),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
let html = await axios
|
let html = await axios
|
||||||
.get('https://www.cgates.lt/televizija/tv-programa-savaitei/')
|
.get('https://www.cgates.lt/televizija/tv-programa-savaitei/')
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
let $ = cheerio.load(html)
|
let $ = cheerio.load(html)
|
||||||
const items = $('.vc_tta-panel.vc_active .kanalas_wrap').toArray()
|
const items = $('.vc_tta-panel.vc_active .kanalas_wrap').toArray()
|
||||||
|
|
||||||
return items.map(item => {
|
return items.map(item => {
|
||||||
const name = $(item).find('h6').text().trim()
|
const name = $(item).find('h6').text().trim()
|
||||||
const link = $(item).find('a').attr('href')
|
const link = $(item).find('a').attr('href')
|
||||||
const [, site_id] = link.match(/\/tv-kanalai\/(.*)\//) || [null, null]
|
const [, site_id] = link.match(/\/tv-kanalai\/(.*)\//) || [null, null]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
lang: 'lt',
|
lang: 'lt',
|
||||||
site_id,
|
site_id,
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTitle($item) {
|
function parseTitle($item) {
|
||||||
const title = $item('td:nth-child(2) > .vc_toggle > .vc_toggle_title').text().trim()
|
const title = $item('td:nth-child(2) > .vc_toggle > .vc_toggle_title').text().trim()
|
||||||
|
|
||||||
return title || $item('td:nth-child(2)').text().trim()
|
return title || $item('td:nth-child(2)').text().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDescription($item) {
|
function parseDescription($item) {
|
||||||
return $item('.vc_toggle_content > p').text().trim()
|
return $item('.vc_toggle_content > p').text().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart($item, date) {
|
function parseStart($item, date) {
|
||||||
const time = $item('.laikas')
|
const time = $item('.laikas')
|
||||||
|
|
||||||
return dayjs.tz(`${date.format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm', 'Europe/Vilnius')
|
return dayjs.tz(`${date.format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm', 'Europe/Vilnius')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, date) {
|
function parseItems(content, date) {
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
const section = $(
|
const section = $(
|
||||||
'article > div:nth-child(2) > div.vc_row.wpb_row.vc_row-fluid > div > div > div > div > div'
|
'article > div:nth-child(2) > div.vc_row.wpb_row.vc_row-fluid > div > div > div > div > div'
|
||||||
)
|
)
|
||||||
.filter(function () {
|
.filter(function () {
|
||||||
return $(`.dt-fancy-title:contains("${date.format('YYYY-MM-DD')}")`, this).length === 1
|
return $(`.dt-fancy-title:contains("${date.format('YYYY-MM-DD')}")`, this).length === 1
|
||||||
})
|
})
|
||||||
.first()
|
.first()
|
||||||
|
|
||||||
return $('.tv_programa tr', section).toArray()
|
return $('.tv_programa tr', section).toArray()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +1,49 @@
|
|||||||
const { parser, url } = require('./cgates.lt.config.js')
|
const { parser, url } = require('./cgates.lt.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2022-08-30', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2022-08-30', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: 'lrt-televizija-hd',
|
site_id: 'lrt-televizija-hd',
|
||||||
xmltv_id: 'LRTTV.lt'
|
xmltv_id: 'LRTTV.lt'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ channel, date })).toBe('https://www.cgates.lt/tv-kanalai/lrt-televizija-hd/')
|
expect(url({ channel, date })).toBe('https://www.cgates.lt/tv-kanalai/lrt-televizija-hd/')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
||||||
const results = parser({ content, date }).map(p => {
|
const results = parser({ content, date }).map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(35)
|
expect(results.length).toBe(35)
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2022-08-29T21:05:00.000Z',
|
start: '2022-08-29T21:05:00.000Z',
|
||||||
stop: '2022-08-29T21:30:00.000Z',
|
stop: '2022-08-29T21:30:00.000Z',
|
||||||
title: '31-oji nuovada (District 31), Drama, 2016',
|
title: '31-oji nuovada (District 31), Drama, 2016',
|
||||||
description:
|
description:
|
||||||
'Seriale pasakojama apie kasdienius policijos išbandymus ir sunkumus. Vadovybė pertvarko Monrealio miesto policijos struktūrą: išskirsto į 36 policijos nuovadas, kad šios būtų arčiau gyventojų. 31-osios nuovados darbuotojams tenka kone sunkiausias darbas: šiame miesto rajone gyvena socialiai remtinos šeimos, nuolat kovojančios su turtingųjų klase, įsipliekia ir rasinių konfliktų. Be to, čia akivaizdus kartų atotrūkis, o tapti nusikalstamo pasaulio dalimi labai lengva. Serialo siužetas – intensyvus, nauji nusikaltimai tiriami kiekvieną savaitę. Čia vaizduojamas nepagražintas nusikalstamas pasaulis, jo poveikis rajono gyventojams. Policijos nuovados darbuotojai narplios įvairiausių nusikaltimų schemas. Tai ir pagrobimai, įsilaužimai, žmogžudystės, smurtas artimoje aplinkoje, lytiniai nusikaltimai, prekyba narkotikais, teroristinių išpuolių grėsmė ir pan. Šis serialas leis žiūrovui įsigilinti į policijos pareigūnų realybę, pateiks skirtingą požiūrį į kiekvieną nusikaltimą.'
|
'Seriale pasakojama apie kasdienius policijos išbandymus ir sunkumus. Vadovybė pertvarko Monrealio miesto policijos struktūrą: išskirsto į 36 policijos nuovadas, kad šios būtų arčiau gyventojų. 31-osios nuovados darbuotojams tenka kone sunkiausias darbas: šiame miesto rajone gyvena socialiai remtinos šeimos, nuolat kovojančios su turtingųjų klase, įsipliekia ir rasinių konfliktų. Be to, čia akivaizdus kartų atotrūkis, o tapti nusikalstamo pasaulio dalimi labai lengva. Serialo siužetas – intensyvus, nauji nusikaltimai tiriami kiekvieną savaitę. Čia vaizduojamas nepagražintas nusikalstamas pasaulis, jo poveikis rajono gyventojams. Policijos nuovados darbuotojai narplios įvairiausių nusikaltimų schemas. Tai ir pagrobimai, įsilaužimai, žmogžudystės, smurtas artimoje aplinkoje, lytiniai nusikaltimai, prekyba narkotikais, teroristinių išpuolių grėsmė ir pan. Šis serialas leis žiūrovui įsigilinti į policijos pareigūnų realybę, pateiks skirtingą požiūrį į kiekvieną nusikaltimą.'
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[34]).toMatchObject({
|
expect(results[34]).toMatchObject({
|
||||||
start: '2022-08-30T20:45:00.000Z',
|
start: '2022-08-30T20:45:00.000Z',
|
||||||
stop: '2022-08-30T21:15:00.000Z',
|
stop: '2022-08-30T21:15:00.000Z',
|
||||||
title: '31-oji nuovada (District 31), Drama, 2016!'
|
title: '31-oji nuovada (District 31), Drama, 2016!'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const result = parser({
|
const result = parser({
|
||||||
content: ''
|
content: ''
|
||||||
})
|
})
|
||||||
expect(result).toMatchObject([])
|
expect(result).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,55 +1,55 @@
|
|||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'chada.ma',
|
site: 'chada.ma',
|
||||||
channels: 'chada.ma.channels.xml',
|
channels: 'chada.ma.channels.xml',
|
||||||
days: 1,
|
days: 1,
|
||||||
request: {
|
request: {
|
||||||
cache: {
|
cache: {
|
||||||
ttl: 60 * 60 * 1000 // 1 hour
|
ttl: 60 * 60 * 1000 // 1 hour
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
url() {
|
url() {
|
||||||
return 'https://chada.ma/fr/chada-tv/grille-tv/'
|
return 'https://chada.ma/fr/chada-tv/grille-tv/'
|
||||||
},
|
},
|
||||||
parser: function ({ content }) {
|
parser: function ({ content }) {
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
const programs = []
|
const programs = []
|
||||||
|
|
||||||
$('#stopfix .posts-area h2').each((i, element) => {
|
$('#stopfix .posts-area h2').each((i, element) => {
|
||||||
const timeRange = $(element).text().trim()
|
const timeRange = $(element).text().trim()
|
||||||
const [start, stop] = timeRange.split(' - ').map(t => parseProgramTime(t.trim()))
|
const [start, stop] = timeRange.split(' - ').map(t => parseProgramTime(t.trim()))
|
||||||
|
|
||||||
const titleElement = $(element).next('div').next('h3')
|
const titleElement = $(element).next('div').next('h3')
|
||||||
const title = titleElement.text().trim()
|
const title = titleElement.text().trim()
|
||||||
|
|
||||||
const description = titleElement.next('div').text().trim() || 'No description available'
|
const description = titleElement.next('div').text().trim() || 'No description available'
|
||||||
|
|
||||||
programs.push({
|
programs.push({
|
||||||
title,
|
title,
|
||||||
description,
|
description,
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseProgramTime(timeStr) {
|
function parseProgramTime(timeStr) {
|
||||||
const timeZone = 'Africa/Casablanca'
|
const timeZone = 'Africa/Casablanca'
|
||||||
const currentDate = dayjs().format('YYYY-MM-DD')
|
const currentDate = dayjs().format('YYYY-MM-DD')
|
||||||
|
|
||||||
return dayjs
|
return dayjs
|
||||||
.tz(`${currentDate} ${timeStr}`, 'YYYY-MM-DD HH:mm', timeZone)
|
.tz(`${currentDate} ${timeStr}`, 'YYYY-MM-DD HH:mm', timeZone)
|
||||||
.format('YYYY-MM-DDTHH:mm:ssZ')
|
.format('YYYY-MM-DDTHH:mm:ssZ')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,100 +1,100 @@
|
|||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const { DateTime } = require('luxon')
|
const { DateTime } = require('luxon')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'clickthecity.com',
|
site: 'clickthecity.com',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ channel }) {
|
url({ channel }) {
|
||||||
return `https://www.clickthecity.com/tv/channels/?netid=${channel.site_id}`
|
return `https://www.clickthecity.com/tv/channels/?netid=${channel.site_id}`
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'content-type': 'application/x-www-form-urlencoded'
|
'content-type': 'application/x-www-form-urlencoded'
|
||||||
},
|
},
|
||||||
data({ date }) {
|
data({ date }) {
|
||||||
const params = new URLSearchParams()
|
const params = new URLSearchParams()
|
||||||
params.append(
|
params.append(
|
||||||
'optDate',
|
'optDate',
|
||||||
DateTime.fromMillis(date.valueOf()).setZone('Asia/Manila').toFormat('yyyy-MM-dd')
|
DateTime.fromMillis(date.valueOf()).setZone('Asia/Manila').toFormat('yyyy-MM-dd')
|
||||||
)
|
)
|
||||||
params.append('optTime', '00:00:00')
|
params.append('optTime', '00:00:00')
|
||||||
|
|
||||||
return params
|
return params
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
parser({ content, date }) {
|
parser({ content, date }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
const items = parseItems(content)
|
const items = parseItems(content)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const $item = cheerio.load(item)
|
const $item = cheerio.load(item)
|
||||||
let start = parseStart($item, date)
|
let start = parseStart($item, date)
|
||||||
let stop = parseStop($item, date)
|
let stop = parseStop($item, date)
|
||||||
if (!start || !stop) return
|
if (!start || !stop) return
|
||||||
if (start > stop) {
|
if (start > stop) {
|
||||||
stop = stop.plus({ days: 1 })
|
stop = stop.plus({ days: 1 })
|
||||||
}
|
}
|
||||||
|
|
||||||
programs.push({
|
programs.push({
|
||||||
title: parseTitle($item),
|
title: parseTitle($item),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const html = await axios
|
const html = await axios
|
||||||
.get('https://www.clickthecity.com/tv/channels/')
|
.get('https://www.clickthecity.com/tv/channels/')
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
const $ = cheerio.load(html)
|
const $ = cheerio.load(html)
|
||||||
const items = $('#channels .col').toArray()
|
const items = $('#channels .col').toArray()
|
||||||
|
|
||||||
return items.map(item => {
|
return items.map(item => {
|
||||||
const name = $(item).find('.card-body').text().trim()
|
const name = $(item).find('.card-body').text().trim()
|
||||||
const url = $(item).find('a').attr('href')
|
const url = $(item).find('a').attr('href')
|
||||||
const [, site_id] = url.match(/netid=(\d+)/) || [null, null]
|
const [, site_id] = url.match(/netid=(\d+)/) || [null, null]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
site_id,
|
site_id,
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTitle($item) {
|
function parseTitle($item) {
|
||||||
return $item('td > a').text().trim()
|
return $item('td > a').text().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart($item, date) {
|
function parseStart($item, date) {
|
||||||
const url = $item('td.cPrg > a').attr('href') || ''
|
const url = $item('td.cPrg > a').attr('href') || ''
|
||||||
let [, time] = url.match(/starttime=(\d{1,2}%3A\d{2}\+(AM|PM))/) || [null, null]
|
let [, time] = url.match(/starttime=(\d{1,2}%3A\d{2}\+(AM|PM))/) || [null, null]
|
||||||
if (!time) return null
|
if (!time) return null
|
||||||
time = `${date.format('YYYY-MM-DD')} ${time.replace('%3A', ':').replace('+', ' ')}`
|
time = `${date.format('YYYY-MM-DD')} ${time.replace('%3A', ':').replace('+', ' ')}`
|
||||||
|
|
||||||
return DateTime.fromFormat(time, 'yyyy-MM-dd h:mm a', { zone: 'Asia/Manila' }).toUTC()
|
return DateTime.fromFormat(time, 'yyyy-MM-dd h:mm a', { zone: 'Asia/Manila' }).toUTC()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStop($item, date) {
|
function parseStop($item, date) {
|
||||||
const url = $item('td.cPrg > a').attr('href') || ''
|
const url = $item('td.cPrg > a').attr('href') || ''
|
||||||
let [, time] = url.match(/endtime=(\d{1,2}%3A\d{2}\+(AM|PM))/) || [null, null]
|
let [, time] = url.match(/endtime=(\d{1,2}%3A\d{2}\+(AM|PM))/) || [null, null]
|
||||||
if (!time) return null
|
if (!time) return null
|
||||||
time = `${date.format('YYYY-MM-DD')} ${time.replace('%3A', ':').replace('+', ' ')}`
|
time = `${date.format('YYYY-MM-DD')} ${time.replace('%3A', ':').replace('+', ' ')}`
|
||||||
|
|
||||||
return DateTime.fromFormat(time, 'yyyy-MM-dd h:mm a', { zone: 'Asia/Manila' }).toUTC()
|
return DateTime.fromFormat(time, 'yyyy-MM-dd h:mm a', { zone: 'Asia/Manila' }).toUTC()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content) {
|
function parseItems(content) {
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
|
|
||||||
return $('#tvlistings > tbody > tr')
|
return $('#tvlistings > tbody > tr')
|
||||||
.filter(function () {
|
.filter(function () {
|
||||||
return $(this).find('td.cPrg').length
|
return $(this).find('td.cPrg').length
|
||||||
})
|
})
|
||||||
.toArray()
|
.toArray()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,67 +1,67 @@
|
|||||||
const { parser, url, request } = require('./clickthecity.com.config.js')
|
const { parser, url, request } = require('./clickthecity.com.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2023-06-12', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2023-06-12', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: '5',
|
site_id: '5',
|
||||||
xmltv_id: 'TV5.ph'
|
xmltv_id: 'TV5.ph'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ channel })).toBe('https://www.clickthecity.com/tv/channels/?netid=5')
|
expect(url({ channel })).toBe('https://www.clickthecity.com/tv/channels/?netid=5')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request method', () => {
|
it('can generate valid request method', () => {
|
||||||
expect(request.method).toBe('POST')
|
expect(request.method).toBe('POST')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request headers', () => {
|
it('can generate valid request headers', () => {
|
||||||
expect(request.headers).toMatchObject({
|
expect(request.headers).toMatchObject({
|
||||||
'content-type': 'application/x-www-form-urlencoded'
|
'content-type': 'application/x-www-form-urlencoded'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request data', () => {
|
it('can generate valid request data', () => {
|
||||||
const result = request.data({ date })
|
const result = request.data({ date })
|
||||||
expect(result.get('optDate')).toBe('2023-06-12')
|
expect(result.get('optDate')).toBe('2023-06-12')
|
||||||
expect(result.get('optTime')).toBe('00:00:00')
|
expect(result.get('optTime')).toBe('00:00:00')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
||||||
const results = parser({ content, date }).map(p => {
|
const results = parser({ content, date }).map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(20)
|
expect(results.length).toBe(20)
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2023-06-11T21:00:00.000Z',
|
start: '2023-06-11T21:00:00.000Z',
|
||||||
stop: '2023-06-11T22:00:00.000Z',
|
stop: '2023-06-11T22:00:00.000Z',
|
||||||
title: 'Word Of God'
|
title: 'Word Of God'
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[19]).toMatchObject({
|
expect(results[19]).toMatchObject({
|
||||||
start: '2023-06-12T15:30:00.000Z',
|
start: '2023-06-12T15:30:00.000Z',
|
||||||
stop: '2023-06-12T16:00:00.000Z',
|
stop: '2023-06-12T16:00:00.000Z',
|
||||||
title: 'La Suerte De Loli'
|
title: 'La Suerte De Loli'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const result = parser({
|
const result = parser({
|
||||||
date,
|
date,
|
||||||
channel,
|
channel,
|
||||||
content:
|
content:
|
||||||
'<!DOCTYPE html><html class="html" lang="en-US" prefix="og: https://ogp.me/ns#"><head></head><body></body></html>'
|
'<!DOCTYPE html><html class="html" lang="en-US" prefix="og: https://ogp.me/ns#"><head></head><body></body></html>'
|
||||||
})
|
})
|
||||||
expect(result).toMatchObject([])
|
expect(result).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,137 +1,137 @@
|
|||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const API_ENDPOINT = 'https://contenthub-api.eco.astro.com.my'
|
const API_ENDPOINT = 'https://contenthub-api.eco.astro.com.my'
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'content.astro.com.my',
|
site: 'content.astro.com.my',
|
||||||
days: 2,
|
days: 2,
|
||||||
url: function ({ channel }) {
|
url: function ({ channel }) {
|
||||||
return `${API_ENDPOINT}/channel/${channel.site_id}.json`
|
return `${API_ENDPOINT}/channel/${channel.site_id}.json`
|
||||||
},
|
},
|
||||||
async parser({ content, date }) {
|
async parser({ content, date }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
const items = parseItems(content, date)
|
const items = parseItems(content, date)
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
const start = dayjs.utc(item.datetimeInUtc)
|
const start = dayjs.utc(item.datetimeInUtc)
|
||||||
const duration = parseDuration(item.duration)
|
const duration = parseDuration(item.duration)
|
||||||
const stop = start.add(duration, 's')
|
const stop = start.add(duration, 's')
|
||||||
const details = await loadProgramDetails(item)
|
const details = await loadProgramDetails(item)
|
||||||
programs.push({
|
programs.push({
|
||||||
title: details.title,
|
title: details.title,
|
||||||
sub_title: item.subtitles,
|
sub_title: item.subtitles,
|
||||||
description: details.longSynopsis || details.shortSynopsis,
|
description: details.longSynopsis || details.shortSynopsis,
|
||||||
actors: parseList(details.cast),
|
actors: parseList(details.cast),
|
||||||
directors: parseList(details.director),
|
directors: parseList(details.director),
|
||||||
image: details.imageUrl,
|
image: details.imageUrl,
|
||||||
rating: parseRating(details),
|
rating: parseRating(details),
|
||||||
categories: parseCategories(details),
|
categories: parseCategories(details),
|
||||||
episode: parseEpisode(item),
|
episode: parseEpisode(item),
|
||||||
season: parseSeason(details),
|
season: parseSeason(details),
|
||||||
start: start,
|
start: start,
|
||||||
stop: stop
|
stop: stop
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get('https://contenthub-api.eco.astro.com.my/channel/all.json')
|
.get('https://contenthub-api.eco.astro.com.my/channel/all.json')
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
return data.response.map(item => {
|
return data.response.map(item => {
|
||||||
return {
|
return {
|
||||||
lang: 'ms',
|
lang: 'ms',
|
||||||
site_id: item.id,
|
site_id: item.id,
|
||||||
name: item.title
|
name: item.title
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseEpisode(item) {
|
function parseEpisode(item) {
|
||||||
const [, number] = item.title.match(/Ep(\d+)$/) || [null, null]
|
const [, number] = item.title.match(/Ep(\d+)$/) || [null, null]
|
||||||
|
|
||||||
return number ? parseInt(number) : null
|
return number ? parseInt(number) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseSeason(details) {
|
function parseSeason(details) {
|
||||||
const [, season] = details.title ? details.title.match(/ S(\d+)/) || [null, null] : [null, null]
|
const [, season] = details.title ? details.title.match(/ S(\d+)/) || [null, null] : [null, null]
|
||||||
|
|
||||||
return season ? parseInt(season) : null
|
return season ? parseInt(season) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseList(list) {
|
function parseList(list) {
|
||||||
return typeof list === 'string' ? list.split(',') : []
|
return typeof list === 'string' ? list.split(',') : []
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseRating(details) {
|
function parseRating(details) {
|
||||||
return details.certification
|
return details.certification
|
||||||
? {
|
? {
|
||||||
system: 'LPF',
|
system: 'LPF',
|
||||||
value: details.certification
|
value: details.certification
|
||||||
}
|
}
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, date) {
|
function parseItems(content, date) {
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
const schedules = data.response.schedule
|
const schedules = data.response.schedule
|
||||||
|
|
||||||
return schedules[date.format('YYYY-MM-DD')] || []
|
return schedules[date.format('YYYY-MM-DD')] || []
|
||||||
} catch {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDuration(duration) {
|
function parseDuration(duration) {
|
||||||
const match = duration.match(/(\d{2}):(\d{2}):(\d{2})/)
|
const match = duration.match(/(\d{2}):(\d{2}):(\d{2})/)
|
||||||
const hours = parseInt(match[1])
|
const hours = parseInt(match[1])
|
||||||
const minutes = parseInt(match[2])
|
const minutes = parseInt(match[2])
|
||||||
const seconds = parseInt(match[3])
|
const seconds = parseInt(match[3])
|
||||||
|
|
||||||
return hours * 3600 + minutes * 60 + seconds
|
return hours * 3600 + minutes * 60 + seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseCategories(details) {
|
function parseCategories(details) {
|
||||||
const genres = {
|
const genres = {
|
||||||
'filter/2': 'Action',
|
'filter/2': 'Action',
|
||||||
'filter/4': 'Anime',
|
'filter/4': 'Anime',
|
||||||
'filter/12': 'Cartoons',
|
'filter/12': 'Cartoons',
|
||||||
'filter/16': 'Comedy',
|
'filter/16': 'Comedy',
|
||||||
'filter/19': 'Crime',
|
'filter/19': 'Crime',
|
||||||
'filter/24': 'Drama',
|
'filter/24': 'Drama',
|
||||||
'filter/25': 'Educational',
|
'filter/25': 'Educational',
|
||||||
'filter/36': 'Horror',
|
'filter/36': 'Horror',
|
||||||
'filter/39': 'Live Action',
|
'filter/39': 'Live Action',
|
||||||
'filter/55': 'Pre-school',
|
'filter/55': 'Pre-school',
|
||||||
'filter/56': 'Reality',
|
'filter/56': 'Reality',
|
||||||
'filter/60': 'Romance',
|
'filter/60': 'Romance',
|
||||||
'filter/68': 'Talk Show',
|
'filter/68': 'Talk Show',
|
||||||
'filter/69': 'Thriller',
|
'filter/69': 'Thriller',
|
||||||
'filter/72': 'Variety',
|
'filter/72': 'Variety',
|
||||||
'filter/75': 'Series',
|
'filter/75': 'Series',
|
||||||
'filter/100': 'Others (Children)'
|
'filter/100': 'Others (Children)'
|
||||||
}
|
}
|
||||||
|
|
||||||
return Array.isArray(details.subFilter)
|
return Array.isArray(details.subFilter)
|
||||||
? details.subFilter.map(g => genres[g.toLowerCase()]).filter(Boolean)
|
? details.subFilter.map(g => genres[g.toLowerCase()]).filter(Boolean)
|
||||||
: []
|
: []
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadProgramDetails(item) {
|
async function loadProgramDetails(item) {
|
||||||
const url = `${API_ENDPOINT}/api/v1/linear-detail?siTrafficKey=${item.siTrafficKey}`
|
const url = `${API_ENDPOINT}/api/v1/linear-detail?siTrafficKey=${item.siTrafficKey}`
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(url)
|
.get(url)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(error => console.log(error.message))
|
.catch(error => console.log(error.message))
|
||||||
if (!data) return {}
|
if (!data) return {}
|
||||||
|
|
||||||
return data.response || {}
|
return data.response || {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,71 +1,71 @@
|
|||||||
const { parser, url } = require('./content.astro.com.my.config.js')
|
const { parser, url } = require('./content.astro.com.my.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
jest.mock('axios')
|
jest.mock('axios')
|
||||||
|
|
||||||
const date = dayjs.utc('2022-10-31', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2022-10-31', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: '425',
|
site_id: '425',
|
||||||
xmltv_id: 'TVBClassic.hk'
|
xmltv_id: 'TVBClassic.hk'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ channel })).toBe('https://contenthub-api.eco.astro.com.my/channel/425.json')
|
expect(url({ channel })).toBe('https://contenthub-api.eco.astro.com.my/channel/425.json')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', async () => {
|
it('can parse response', async () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
||||||
|
|
||||||
axios.get.mockImplementation(url => {
|
axios.get.mockImplementation(url => {
|
||||||
if (
|
if (
|
||||||
url ===
|
url ===
|
||||||
'https://contenthub-api.eco.astro.com.my/api/v1/linear-detail?siTrafficKey=1:10000526:47979653'
|
'https://contenthub-api.eco.astro.com.my/api/v1/linear-detail?siTrafficKey=1:10000526:47979653'
|
||||||
) {
|
) {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/program.json')))
|
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/program.json')))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve({ data: '' })
|
return Promise.resolve({ data: '' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let results = await parser({ content, channel, date })
|
let results = await parser({ content, channel, date })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(31)
|
expect(results.length).toBe(31)
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2022-10-30T16:10:00.000Z',
|
start: '2022-10-30T16:10:00.000Z',
|
||||||
stop: '2022-10-30T17:02:00.000Z',
|
stop: '2022-10-30T17:02:00.000Z',
|
||||||
title: 'Triumph in the Skies S1 Ep06',
|
title: 'Triumph in the Skies S1 Ep06',
|
||||||
description:
|
description:
|
||||||
'This classic drama depicts the many aspects of two complicated relationships set against an airline company. Will those involved ever find true love?',
|
'This classic drama depicts the many aspects of two complicated relationships set against an airline company. Will those involved ever find true love?',
|
||||||
actors: ['Francis Ng Chun Yu', 'Joe Ma Tak Chung', 'Flora Chan Wai San'],
|
actors: ['Francis Ng Chun Yu', 'Joe Ma Tak Chung', 'Flora Chan Wai San'],
|
||||||
directors: ['Joe Ma Tak Chung'],
|
directors: ['Joe Ma Tak Chung'],
|
||||||
image: 'https://s3-ap-southeast-1.amazonaws.com/ams-astro/production/images/1035X328883.jpg',
|
image: 'https://s3-ap-southeast-1.amazonaws.com/ams-astro/production/images/1035X328883.jpg',
|
||||||
rating: {
|
rating: {
|
||||||
system: 'LPF',
|
system: 'LPF',
|
||||||
value: 'U'
|
value: 'U'
|
||||||
},
|
},
|
||||||
episode: 6,
|
episode: 6,
|
||||||
season: 1,
|
season: 1,
|
||||||
categories: ['Drama']
|
categories: ['Drama']
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', async () => {
|
it('can handle empty guide', async () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/no_content.html'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/no_content.html'))
|
||||||
const results = await parser({ date, content })
|
const results = await parser({ date, content })
|
||||||
|
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,85 +1,85 @@
|
|||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'cosmotetv.gr',
|
site: 'cosmotetv.gr',
|
||||||
days: 5,
|
days: 5,
|
||||||
request: {
|
request: {
|
||||||
cache: {
|
cache: {
|
||||||
ttl: 60 * 60 * 1000 // 1 hour
|
ttl: 60 * 60 * 1000 // 1 hour
|
||||||
},
|
},
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
referer: 'https://www.cosmotetv.gr/',
|
referer: 'https://www.cosmotetv.gr/',
|
||||||
'User-Agent':
|
'User-Agent':
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
||||||
Accept: '*/*',
|
Accept: '*/*',
|
||||||
'Accept-Language': 'en-US,en;q=0.9',
|
'Accept-Language': 'en-US,en;q=0.9',
|
||||||
'Accept-Encoding': 'gzip, deflate, br, zstd',
|
'Accept-Encoding': 'gzip, deflate, br, zstd',
|
||||||
Origin: 'https://www.cosmotetv.gr',
|
Origin: 'https://www.cosmotetv.gr',
|
||||||
'Sec-Ch-Ua': '"Not.A/Brand";v="24", "Chromium";v="131", "Google Chrome";v="131"',
|
'Sec-Ch-Ua': '"Not.A/Brand";v="24", "Chromium";v="131", "Google Chrome";v="131"',
|
||||||
'Sec-Ch-Ua-Mobile': '?0',
|
'Sec-Ch-Ua-Mobile': '?0',
|
||||||
'Sec-Ch-Ua-Platform': '"Windows"',
|
'Sec-Ch-Ua-Platform': '"Windows"',
|
||||||
'Sec-Fetch-Dest': 'empty',
|
'Sec-Fetch-Dest': 'empty',
|
||||||
'Sec-Fetch-Mode': 'cors',
|
'Sec-Fetch-Mode': 'cors',
|
||||||
'Sec-Fetch-Site': 'cross-site'
|
'Sec-Fetch-Site': 'cross-site'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
url: function ({ date, channel }) {
|
url: function ({ date, channel }) {
|
||||||
const startOfDay = dayjs(date).startOf('day').utc().unix()
|
const startOfDay = dayjs(date).startOf('day').utc().unix()
|
||||||
const endOfDay = dayjs(date).endOf('day').utc().unix()
|
const endOfDay = dayjs(date).endOf('day').utc().unix()
|
||||||
return `https://mwapi-prod.cosmotetvott.gr/api/v3.4/epg/listings/el?from=${startOfDay}&to=${endOfDay}&callSigns=${channel.site_id}&endingIncludedInRange=false`
|
return `https://mwapi-prod.cosmotetvott.gr/api/v3.4/epg/listings/el?from=${startOfDay}&to=${endOfDay}&callSigns=${channel.site_id}&endingIncludedInRange=false`
|
||||||
},
|
},
|
||||||
parser: function ({ content }) {
|
parser: function ({ content }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
data.channels.forEach(channel => {
|
data.channels.forEach(channel => {
|
||||||
channel.items.forEach(item => {
|
channel.items.forEach(item => {
|
||||||
const start = dayjs(item.startTime).utc().toISOString()
|
const start = dayjs(item.startTime).utc().toISOString()
|
||||||
const stop = dayjs(item.endTime).utc().toISOString()
|
const stop = dayjs(item.endTime).utc().toISOString()
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
description: item.description || 'No description available',
|
description: item.description || 'No description available',
|
||||||
category: item.qoe.genre,
|
category: item.qoe.genre,
|
||||||
image: item.thumbnails.standard,
|
image: item.thumbnails.standard,
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
'https://mwapi-prod.cosmotetvott.gr/api/v3.4/epg/channels/all/el',
|
'https://mwapi-prod.cosmotetvott.gr/api/v3.4/epg/channels/all/el',
|
||||||
{
|
{
|
||||||
headers: this.request.headers
|
headers: this.request.headers
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
const data = response.data
|
const data = response.data
|
||||||
|
|
||||||
if (data && data.channels) {
|
if (data && data.channels) {
|
||||||
return data.channels.map(item => ({
|
return data.channels.map(item => ({
|
||||||
lang: 'el',
|
lang: 'el',
|
||||||
site_id: item.callSign,
|
site_id: item.callSign,
|
||||||
name: item.title
|
name: item.title
|
||||||
//logo: item.logos.square
|
//logo: item.logos.square
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
console.error('Unexpected response structure:', data)
|
console.error('Unexpected response structure:', data)
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching channel data:', error)
|
console.error('Error fetching channel data:', error)
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,114 +1,114 @@
|
|||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
|
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'cubmu.com',
|
site: 'cubmu.com',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ channel, date }) {
|
url({ channel, date }) {
|
||||||
return `https://servicebuss.transvision.co.id/v2/cms/getEPGData?app_id=cubmu&tvs_platform_id=standalone&schedule_date=${date.format(
|
return `https://servicebuss.transvision.co.id/v2/cms/getEPGData?app_id=cubmu&tvs_platform_id=standalone&schedule_date=${date.format(
|
||||||
'YYYY-MM-DD'
|
'YYYY-MM-DD'
|
||||||
)}&channel_id=${channel.site_id}`
|
)}&channel_id=${channel.site_id}`
|
||||||
},
|
},
|
||||||
parser({ content, channel }) {
|
parser({ content, channel }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
const items = parseItems(content)
|
const items = parseItems(content)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
programs.push({
|
programs.push({
|
||||||
title: parseTitle(item),
|
title: parseTitle(item),
|
||||||
description: parseDescription(item, channel.lang),
|
description: parseDescription(item, channel.lang),
|
||||||
episode: parseEpisode(item),
|
episode: parseEpisode(item),
|
||||||
start: parseStart(item).toISOString(),
|
start: parseStart(item).toISOString(),
|
||||||
stop: parseStop(item).toISOString()
|
stop: parseStop(item).toISOString()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels({ lang = 'id' }) {
|
async channels({ lang = 'id' }) {
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const result = await axios
|
const result = await axios
|
||||||
.get('https://cubmu.com/live-tv')
|
.get('https://cubmu.com/live-tv')
|
||||||
.then(response => response.data)
|
.then(response => response.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
const $ = cheerio.load(result)
|
const $ = cheerio.load(result)
|
||||||
|
|
||||||
// retrieve service api data
|
// retrieve service api data
|
||||||
const config = JSON.parse($('#__NEXT_DATA__').text()).runtimeConfig || {}
|
const config = JSON.parse($('#__NEXT_DATA__').text()).runtimeConfig || {}
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
headers: {
|
headers: {
|
||||||
Origin: 'https://cubmu.com',
|
Origin: 'https://cubmu.com',
|
||||||
Referer: 'https://cubmu.com/live-tv'
|
Referer: 'https://cubmu.com/live-tv'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// login to service bus
|
// login to service bus
|
||||||
await axios
|
await axios
|
||||||
.post(
|
.post(
|
||||||
`https://servicebuss.transvision.co.id/tvs/login/external?email=${config.email}&password=${config.password}&deviceId=${config.deviceId}&deviceType=${config.deviceType}&deviceModel=${config.deviceModel}&deviceToken=&serial=&platformId=${config.platformId}`,
|
`https://servicebuss.transvision.co.id/tvs/login/external?email=${config.email}&password=${config.password}&deviceId=${config.deviceId}&deviceType=${config.deviceType}&deviceModel=${config.deviceModel}&deviceToken=&serial=&platformId=${config.platformId}`,
|
||||||
options
|
options
|
||||||
)
|
)
|
||||||
.then(response => response.data)
|
.then(response => response.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
// list channels
|
// list channels
|
||||||
const subscribedChannels = await axios
|
const subscribedChannels = await axios
|
||||||
.post(
|
.post(
|
||||||
`https://servicebuss.transvision.co.id/tvs/subscribe_product/list?platformId=${config.platformId}`,
|
`https://servicebuss.transvision.co.id/tvs/subscribe_product/list?platformId=${config.platformId}`,
|
||||||
options
|
options
|
||||||
)
|
)
|
||||||
.then(response => response.data)
|
.then(response => response.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
const channels = []
|
const channels = []
|
||||||
const included = []
|
const included = []
|
||||||
if (Array.isArray(subscribedChannels.channelPackageList)) {
|
if (Array.isArray(subscribedChannels.channelPackageList)) {
|
||||||
subscribedChannels.channelPackageList.forEach(pkg => {
|
subscribedChannels.channelPackageList.forEach(pkg => {
|
||||||
pkg.channelList.forEach(channel => {
|
pkg.channelList.forEach(channel => {
|
||||||
if (included.indexOf(channel.id) < 0) {
|
if (included.indexOf(channel.id) < 0) {
|
||||||
included.push(channel.id)
|
included.push(channel.id)
|
||||||
channels.push({
|
channels.push({
|
||||||
lang,
|
lang,
|
||||||
site_id: channel.id,
|
site_id: channel.id,
|
||||||
name: channel.name
|
name: channel.name
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return channels
|
return channels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content) {
|
function parseItems(content) {
|
||||||
return content ? JSON.parse(content.trim()).result || [] : []
|
return content ? JSON.parse(content.trim()).result || [] : []
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTitle(item) {
|
function parseTitle(item) {
|
||||||
return item.scehedule_title
|
return item.scehedule_title
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDescription(item, lang = 'id') {
|
function parseDescription(item, lang = 'id') {
|
||||||
return lang === 'id' ? item.schedule_json.primarySynopsis : item.schedule_json.secondarySynopsis
|
return lang === 'id' ? item.schedule_json.primarySynopsis : item.schedule_json.secondarySynopsis
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseEpisode(item) {
|
function parseEpisode(item) {
|
||||||
return item.schedule_json.episodeName
|
return item.schedule_json.episodeName
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart(item) {
|
function parseStart(item) {
|
||||||
return dayjs.tz(item.schedule_date, 'YYYY-MM-DD HH:mm:ss', 'Asia/Jakarta')
|
return dayjs.tz(item.schedule_date, 'YYYY-MM-DD HH:mm:ss', 'Asia/Jakarta')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStop(item) {
|
function parseStop(item) {
|
||||||
return dayjs.tz(
|
return dayjs.tz(
|
||||||
[item.schedule_date.split(' ')[0], item.schedule_end_time].join(' '),
|
[item.schedule_date.split(' ')[0], item.schedule_end_time].join(' '),
|
||||||
'YYYY-MM-DD HH:mm:ss',
|
'YYYY-MM-DD HH:mm:ss',
|
||||||
'Asia/Jakarta'
|
'Asia/Jakarta'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,59 +1,59 @@
|
|||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'cyta.com.cy',
|
site: 'cyta.com.cy',
|
||||||
days: 7,
|
days: 7,
|
||||||
request: {
|
request: {
|
||||||
cache: {
|
cache: {
|
||||||
ttl: 60 * 60 * 1000 // 1 hour
|
ttl: 60 * 60 * 1000 // 1 hour
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
url: function ({ date, channel }) {
|
url: function ({ date, channel }) {
|
||||||
// Get the epoch timestamp
|
// Get the epoch timestamp
|
||||||
const todayEpoch = date.startOf('day').utc().valueOf()
|
const todayEpoch = date.startOf('day').utc().valueOf()
|
||||||
// Get the epoch timestamp for the next day
|
// Get the epoch timestamp for the next day
|
||||||
const nextDayEpoch = date.add(1, 'day').startOf('day').utc().valueOf()
|
const nextDayEpoch = date.add(1, 'day').startOf('day').utc().valueOf()
|
||||||
return `https://epg.cyta.com.cy/api/mediacatalog/fetchEpg?startTimeEpoch=${todayEpoch}&endTimeEpoch=${nextDayEpoch}&language=1&channelIds=${channel.site_id}`
|
return `https://epg.cyta.com.cy/api/mediacatalog/fetchEpg?startTimeEpoch=${todayEpoch}&endTimeEpoch=${nextDayEpoch}&language=1&channelIds=${channel.site_id}`
|
||||||
},
|
},
|
||||||
parser: function ({ content }) {
|
parser: function ({ content }) {
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
const programs = []
|
const programs = []
|
||||||
|
|
||||||
data.channelEpgs.forEach(channel => {
|
data.channelEpgs.forEach(channel => {
|
||||||
channel.epgPlayables.forEach(epg => {
|
channel.epgPlayables.forEach(epg => {
|
||||||
const start = new Date(epg.startTime).toISOString()
|
const start = new Date(epg.startTime).toISOString()
|
||||||
const stop = new Date(epg.endTime).toISOString()
|
const stop = new Date(epg.endTime).toISOString()
|
||||||
|
|
||||||
programs.push({
|
programs.push({
|
||||||
title: epg.name,
|
title: epg.name,
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get('https://epg.cyta.com.cy/api/mediacatalog/fetchChannels?language=1')
|
.get('https://epg.cyta.com.cy/api/mediacatalog/fetchChannels?language=1')
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
return data.channels.map(item => {
|
return data.channels.map(item => {
|
||||||
return {
|
return {
|
||||||
lang: 'el',
|
lang: 'el',
|
||||||
site_id: item.id,
|
site_id: item.id,
|
||||||
name: item.name
|
name: item.name
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +1,49 @@
|
|||||||
const { url, parser } = require('./cyta.com.cy.config.js')
|
const { url, parser } = require('./cyta.com.cy.config.js')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
const date = dayjs.utc('2025-01-03', 'YYYY-MM-DD').startOf('day')
|
const date = dayjs.utc('2025-01-03', 'YYYY-MM-DD').startOf('day')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: '561066',
|
site_id: '561066',
|
||||||
xmltv_id: 'RIK1.cy'
|
xmltv_id: 'RIK1.cy'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
const generatedUrl = url({ date, channel })
|
const generatedUrl = url({ date, channel })
|
||||||
expect(generatedUrl).toBe(
|
expect(generatedUrl).toBe(
|
||||||
'https://epg.cyta.com.cy/api/mediacatalog/fetchEpg?startTimeEpoch=1735862400000&endTimeEpoch=1735948800000&language=1&channelIds=561066'
|
'https://epg.cyta.com.cy/api/mediacatalog/fetchEpg?startTimeEpoch=1735862400000&endTimeEpoch=1735948800000&language=1&channelIds=561066'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = `
|
const content = `
|
||||||
{
|
{
|
||||||
"channelEpgs": [
|
"channelEpgs": [
|
||||||
{
|
{
|
||||||
"epgPlayables": [
|
"epgPlayables": [
|
||||||
{ "name": "Πρώτη Ενημέρωση", "startTime": 1735879500000, "endTime": 1735889400000 }
|
{ "name": "Πρώτη Ενημέρωση", "startTime": 1735879500000, "endTime": 1735889400000 }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}`
|
}`
|
||||||
|
|
||||||
const result = parser({ content })
|
const result = parser({ content })
|
||||||
|
|
||||||
expect(result).toMatchObject([
|
expect(result).toMatchObject([
|
||||||
{
|
{
|
||||||
title: 'Πρώτη Ενημέρωση',
|
title: 'Πρώτη Ενημέρωση',
|
||||||
start: '2025-01-03T04:45:00.000Z',
|
start: '2025-01-03T04:45:00.000Z',
|
||||||
stop: '2025-01-03T07:30:00.000Z'
|
stop: '2025-01-03T07:30:00.000Z'
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const result = parser({
|
const result = parser({
|
||||||
content: '{"channelEpgs":[]}'
|
content: '{"channelEpgs":[]}'
|
||||||
})
|
})
|
||||||
expect(result).toMatchObject([])
|
expect(result).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,73 +1,73 @@
|
|||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
const tz = 'Asia/Jakarta'
|
const tz = 'Asia/Jakarta'
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'dens.tv',
|
site: 'dens.tv',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ channel, date }) {
|
url({ channel, date }) {
|
||||||
return `https://www.dens.tv/api/dens3/tv/TvChannels/listEpgByDate?date=${date.format(
|
return `https://www.dens.tv/api/dens3/tv/TvChannels/listEpgByDate?date=${date.format(
|
||||||
'YYYY-MM-DD'
|
'YYYY-MM-DD'
|
||||||
)}&id_channel=${channel.site_id}&app_type=10`
|
)}&id_channel=${channel.site_id}&app_type=10`
|
||||||
},
|
},
|
||||||
parser({ content }) {
|
parser({ content }) {
|
||||||
// parsing
|
// parsing
|
||||||
const response = JSON.parse(content)
|
const response = JSON.parse(content)
|
||||||
const programs = []
|
const programs = []
|
||||||
|
|
||||||
if (Array.isArray(response?.data)) {
|
if (Array.isArray(response?.data)) {
|
||||||
response.data.forEach(item => {
|
response.data.forEach(item => {
|
||||||
const title = item.title
|
const title = item.title
|
||||||
const [, , , season, , , episode] = title.match(
|
const [, , , season, , , episode] = title.match(
|
||||||
/( (Season |Season|S)(\d+))?( (Episode|Ep) (\d+))/
|
/( (Season |Season|S)(\d+))?( (Episode|Ep) (\d+))/
|
||||||
) || [null, null, null, null, null, null, null]
|
) || [null, null, null, null, null, null, null]
|
||||||
programs.push({
|
programs.push({
|
||||||
title,
|
title,
|
||||||
description: item.description,
|
description: item.description,
|
||||||
season: season ? parseInt(season) : season,
|
season: season ? parseInt(season) : season,
|
||||||
episode: episode ? parseInt(episode) : episode,
|
episode: episode ? parseInt(episode) : episode,
|
||||||
start: dayjs.tz(item.start_time, 'YYYY-MM-DD HH:mm:ss', tz),
|
start: dayjs.tz(item.start_time, 'YYYY-MM-DD HH:mm:ss', tz),
|
||||||
stop: dayjs.tz(item.end_time, 'YYYY-MM-DD HH:mm:ss', tz)
|
stop: dayjs.tz(item.end_time, 'YYYY-MM-DD HH:mm:ss', tz)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
|
|
||||||
const categories = {
|
const categories = {
|
||||||
local: 1,
|
local: 1,
|
||||||
premium: 2,
|
premium: 2,
|
||||||
international: 3
|
international: 3
|
||||||
}
|
}
|
||||||
|
|
||||||
const channels = []
|
const channels = []
|
||||||
for (const id_category of Object.values(categories)) {
|
for (const id_category of Object.values(categories)) {
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get('https://www.dens.tv/api/dens3/tv/TvChannels/listByCategory', {
|
.get('https://www.dens.tv/api/dens3/tv/TvChannels/listByCategory', {
|
||||||
params: { id_category }
|
params: { id_category }
|
||||||
})
|
})
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
data.data.contents.forEach(item => {
|
data.data.contents.forEach(item => {
|
||||||
channels.push({
|
channels.push({
|
||||||
lang: 'id',
|
lang: 'id',
|
||||||
site_id: item.meta.id,
|
site_id: item.meta.id,
|
||||||
name: item.meta.title
|
name: item.meta.title
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return channels
|
return channels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +1,50 @@
|
|||||||
const { url, parser } = require('./dens.tv.config.js')
|
const { url, parser } = require('./dens.tv.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2024-11-24').startOf('d')
|
const date = dayjs.utc('2024-11-24').startOf('d')
|
||||||
const channel = { site_id: '38', xmltv_id: 'AniplusAsia.sg', lang: 'id' }
|
const channel = { site_id: '38', xmltv_id: 'AniplusAsia.sg', lang: 'id' }
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ channel, date })).toBe(
|
expect(url({ channel, date })).toBe(
|
||||||
'https://www.dens.tv/api/dens3/tv/TvChannels/listEpgByDate?date=2024-11-24&id_channel=38&app_type=10'
|
'https://www.dens.tv/api/dens3/tv/TvChannels/listEpgByDate?date=2024-11-24&id_channel=38&app_type=10'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
||||||
let results = parser({ content })
|
let results = parser({ content })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(2)
|
expect(results.length).toBe(2)
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2024-11-23T17:00:00.000Z',
|
start: '2024-11-23T17:00:00.000Z',
|
||||||
stop: '2024-11-23T17:30:00.000Z',
|
stop: '2024-11-23T17:30:00.000Z',
|
||||||
title: 'Migi & Dali Episode 2',
|
title: 'Migi & Dali Episode 2',
|
||||||
episode: 2
|
episode: 2
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[1]).toMatchObject({
|
expect(results[1]).toMatchObject({
|
||||||
start: '2024-11-23T19:30:00.000Z',
|
start: '2024-11-23T19:30:00.000Z',
|
||||||
stop: '2024-11-23T20:00:00.000Z',
|
stop: '2024-11-23T20:00:00.000Z',
|
||||||
title: 'Attack on Titan Season 3 Episode 7',
|
title: 'Attack on Titan Season 3 Episode 7',
|
||||||
season: 3,
|
season: 3,
|
||||||
episode: 7
|
episode: 7
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/no_content.json'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/no_content.json'))
|
||||||
const results = parser({ content })
|
const results = parser({ content })
|
||||||
|
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,50 +1,50 @@
|
|||||||
const { parser, url } = require('./derana.lk.config.js')
|
const { parser, url } = require('./derana.lk.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2025-05-18', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-05-18', 'YYYY-MM-DD').startOf('d')
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ date })).toBe('https://derana.lk/api/schedules/18-05-2025')
|
expect(url({ date })).toBe('https://derana.lk/api/schedules/18-05-2025')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
||||||
|
|
||||||
let results = parser({ content })
|
let results = parser({ content })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
|
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(20)
|
expect(results.length).toBe(20)
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
title: 'Dahami Derana',
|
title: 'Dahami Derana',
|
||||||
image: 'https://derana.lk/storage/uploads/imgs/program/51/20240717062206.jpg',
|
image: 'https://derana.lk/storage/uploads/imgs/program/51/20240717062206.jpg',
|
||||||
start: '2025-05-17T23:05:00.000Z',
|
start: '2025-05-17T23:05:00.000Z',
|
||||||
stop: '2025-05-18T00:55:00.000Z'
|
stop: '2025-05-18T00:55:00.000Z'
|
||||||
})
|
})
|
||||||
expect(results[1]).toMatchObject({
|
expect(results[1]).toMatchObject({
|
||||||
title: 'Derana Aruna',
|
title: 'Derana Aruna',
|
||||||
image: 'https://derana.lk/storage/uploads/imgs/program/15/20240613075807.jpg',
|
image: 'https://derana.lk/storage/uploads/imgs/program/15/20240613075807.jpg',
|
||||||
start: '2025-05-18T00:55:00.000Z',
|
start: '2025-05-18T00:55:00.000Z',
|
||||||
stop: '2025-05-18T02:00:00.000Z'
|
stop: '2025-05-18T02:00:00.000Z'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const results = parser({
|
const results = parser({
|
||||||
content: {
|
content: {
|
||||||
error: 'An error occurred'
|
error: 'An error occurred'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,86 +1,86 @@
|
|||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'digea.gr',
|
site: 'digea.gr',
|
||||||
days: 2,
|
days: 2,
|
||||||
url: 'https://www.digea.gr/el/api/epg/get-events',
|
url: 'https://www.digea.gr/el/api/epg/get-events',
|
||||||
request: {
|
request: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
||||||
},
|
},
|
||||||
data({ date }) {
|
data({ date }) {
|
||||||
const data = new URLSearchParams()
|
const data = new URLSearchParams()
|
||||||
data.append('action', 'get_events')
|
data.append('action', 'get_events')
|
||||||
data.append('date', date.format('YYYY-M-D'))
|
data.append('date', date.format('YYYY-M-D'))
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
parser({ content, channel }) {
|
parser({ content, channel }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
let items = parseItems(content, channel)
|
let items = parseItems(content, channel)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
description: item.long_synopsis,
|
description: item.long_synopsis,
|
||||||
start: parseStart(item),
|
start: parseStart(item),
|
||||||
stop: parseStop(item)
|
stop: parseStop(item)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.post(
|
.post(
|
||||||
'https://www.digea.gr/el/api/epg/get-channels',
|
'https://www.digea.gr/el/api/epg/get-channels',
|
||||||
new URLSearchParams({
|
new URLSearchParams({
|
||||||
action: 'get_chanels',
|
action: 'get_chanels',
|
||||||
lang: 'el'
|
lang: 'el'
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
return data.map(channel => {
|
return data.map(channel => {
|
||||||
return {
|
return {
|
||||||
lang: 'el',
|
lang: 'el',
|
||||||
site_id: channel.id,
|
site_id: channel.id,
|
||||||
name: channel.name
|
name: channel.name
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart(item) {
|
function parseStart(item) {
|
||||||
return dayjs.tz(item.actual_time, 'YYYY-MM-DD HH:mm:ss', 'Europe/Athens')
|
return dayjs.tz(item.actual_time, 'YYYY-MM-DD HH:mm:ss', 'Europe/Athens')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStop(item) {
|
function parseStop(item) {
|
||||||
return dayjs.tz(item.end_time, 'YYYY-MM-DD HH:mm:ss', 'Europe/Athens')
|
return dayjs.tz(item.end_time, 'YYYY-MM-DD HH:mm:ss', 'Europe/Athens')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, channel) {
|
function parseItems(content, channel) {
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
if (!Array.isArray(data)) return []
|
if (!Array.isArray(data)) return []
|
||||||
|
|
||||||
return data.filter(p => p.channel_id === channel.site_id)
|
return data.filter(p => p.channel_id === channel.site_id)
|
||||||
} catch {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,69 +1,69 @@
|
|||||||
const { parser, url, request } = require('./digea.gr.config.js')
|
const { parser, url, request } = require('./digea.gr.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2025-01-17', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-01-17', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: '1100',
|
site_id: '1100',
|
||||||
xmltv_id: 'AlphaTV.gr'
|
xmltv_id: 'AlphaTV.gr'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url).toBe('https://www.digea.gr/el/api/epg/get-events')
|
expect(url).toBe('https://www.digea.gr/el/api/epg/get-events')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request method', () => {
|
it('can generate valid request method', () => {
|
||||||
expect(request.method).toBe('POST')
|
expect(request.method).toBe('POST')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request headers', () => {
|
it('can generate valid request headers', () => {
|
||||||
expect(request.headers).toMatchObject({
|
expect(request.headers).toMatchObject({
|
||||||
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request data', () => {
|
it('can generate valid request data', () => {
|
||||||
const data = request.data({ date })
|
const data = request.data({ date })
|
||||||
|
|
||||||
expect(data.get('action')).toBe('get_events')
|
expect(data.get('action')).toBe('get_events')
|
||||||
expect(data.get('date')).toBe('2025-1-17')
|
expect(data.get('date')).toBe('2025-1-17')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'), 'utf8')
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'), 'utf8')
|
||||||
let results = parser({ content, channel })
|
let results = parser({ content, channel })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(19)
|
expect(results.length).toBe(19)
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2025-01-16T23:30:00.000Z',
|
start: '2025-01-16T23:30:00.000Z',
|
||||||
stop: '2025-01-17T01:30:00.000Z',
|
stop: '2025-01-17T01:30:00.000Z',
|
||||||
title: '[K12] Το Ξεκαθάρισμα (A Score To Settle)',
|
title: '[K12] Το Ξεκαθάρισμα (A Score To Settle)',
|
||||||
description:
|
description:
|
||||||
"Περιπέτεια αμερικανικής παραγωγής 2019 [Το πρόγραμμα περιέχει σκηνές σεξουαλικές, βίας, χρήσης ναρκωτικών κι άλλων εξαρτησιογόνων ουσιών και απρεπή εκφορά λόγου]. Ο Φρανκ απελευθερώνεται από τη φυλακή πολλά χρόνια μετά αφού κατηγορήθηκε για ένα έγκλημα που δεν διέπραξε. Τώρα, ελεύθερος, ξεκινά μια πορεία εκδίκησης εναντίον των ανθρώπων των οποίων οι πράξεις τον έστειλαν στη φυλακή. Ηθοποιοί: Νίκολας Κέιτζ, Μπέντζαμιν Μπρατ, Νόα Λε Γκρος, Καρολίνα Γουίντρα. Σενάριο: Σον Κου, Τζον Νιούμαν. Σκηνοθεσία: Σον Κου. Διάρκεια: 94'. "
|
"Περιπέτεια αμερικανικής παραγωγής 2019 [Το πρόγραμμα περιέχει σκηνές σεξουαλικές, βίας, χρήσης ναρκωτικών κι άλλων εξαρτησιογόνων ουσιών και απρεπή εκφορά λόγου]. Ο Φρανκ απελευθερώνεται από τη φυλακή πολλά χρόνια μετά αφού κατηγορήθηκε για ένα έγκλημα που δεν διέπραξε. Τώρα, ελεύθερος, ξεκινά μια πορεία εκδίκησης εναντίον των ανθρώπων των οποίων οι πράξεις τον έστειλαν στη φυλακή. Ηθοποιοί: Νίκολας Κέιτζ, Μπέντζαμιν Μπρατ, Νόα Λε Γκρος, Καρολίνα Γουίντρα. Σενάριο: Σον Κου, Τζον Νιούμαν. Σκηνοθεσία: Σον Κου. Διάρκεια: 94'. "
|
||||||
})
|
})
|
||||||
expect(results[18]).toMatchObject({
|
expect(results[18]).toMatchObject({
|
||||||
start: '2025-01-17T21:30:00.000Z',
|
start: '2025-01-17T21:30:00.000Z',
|
||||||
stop: '2025-01-17T23:30:00.000Z',
|
stop: '2025-01-17T23:30:00.000Z',
|
||||||
title: '[K8] Βασικά Καλησπέρα Σας',
|
title: '[K8] Βασικά Καλησπέρα Σας',
|
||||||
description:
|
description:
|
||||||
"Κωμωδία ελληνικής παραγωγής 1982. Δύο πειρατικοί ραδιοσταθμοί, εκ των οποίων ο ένας βάζει λαϊκά άσματα και ο άλλος ροκ μουσική, ανταγωνίζονται για την πρωτιά στην ακροαματικότητα. Ο ανταγωνισμός γίνεται βαθμηδόν όλο και πιο σκληρός, αλλά ξάφνου τα πράγματα αλλάζουν ρότα καθώς ο μεγαλοδύναμος έρως παρεμβαίνει και κάνει το θαύμα του. Παίζουν: Στάθης Ψάλτης, Πάνος Μιχαλόπουλος, Σταμάτης Γαρδέλης, Έφη Πίκουλα, Γιώργος Ρήγας, Γιάννης Μποσταντζόγλου, Σοφία Αλιμπέρτη, Καίτη Φίνου. Σκηνοθεσία - Σενάριο: Γιάννης Δαλιανίδης. Διάρκεια: 89'."
|
"Κωμωδία ελληνικής παραγωγής 1982. Δύο πειρατικοί ραδιοσταθμοί, εκ των οποίων ο ένας βάζει λαϊκά άσματα και ο άλλος ροκ μουσική, ανταγωνίζονται για την πρωτιά στην ακροαματικότητα. Ο ανταγωνισμός γίνεται βαθμηδόν όλο και πιο σκληρός, αλλά ξάφνου τα πράγματα αλλάζουν ρότα καθώς ο μεγαλοδύναμος έρως παρεμβαίνει και κάνει το θαύμα του. Παίζουν: Στάθης Ψάλτης, Πάνος Μιχαλόπουλος, Σταμάτης Γαρδέλης, Έφη Πίκουλα, Γιώργος Ρήγας, Γιάννης Μποσταντζόγλου, Σοφία Αλιμπέρτη, Καίτη Φίνου. Σκηνοθεσία - Σενάριο: Γιάννης Δαλιανίδης. Διάρκεια: 89'."
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const results = parser({
|
const results = parser({
|
||||||
content: '[]'
|
content: '[]'
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,86 +1,86 @@
|
|||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
const tz = 'Europe/Istanbul'
|
const tz = 'Europe/Istanbul'
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'digiturk.com.tr',
|
site: 'digiturk.com.tr',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ date }) {
|
url({ date }) {
|
||||||
return `https://www.digiturk.com.tr/Ajax/GetTvGuideFromDigiturk?Day=${
|
return `https://www.digiturk.com.tr/Ajax/GetTvGuideFromDigiturk?Day=${
|
||||||
encodeURIComponent(date.format('MM/DD/YYYY'))
|
encodeURIComponent(date.format('MM/DD/YYYY'))
|
||||||
}+00%3A00%3A00`
|
}+00%3A00%3A00`
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
cache: {
|
cache: {
|
||||||
ttl: 24 * 60 * 60 * 1000 // 1 day
|
ttl: 24 * 60 * 60 * 1000 // 1 day
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
parser({ content, channel, date }) {
|
parser({ content, channel, date }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
if (content) {
|
if (content) {
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
$('.channelDetail').toArray()
|
$('.channelDetail').toArray()
|
||||||
.forEach(item => {
|
.forEach(item => {
|
||||||
const $item = $(item)
|
const $item = $(item)
|
||||||
const title = $item.find('.tvGuideResult-box-wholeDates-title')
|
const title = $item.find('.tvGuideResult-box-wholeDates-title')
|
||||||
if (title.length) {
|
if (title.length) {
|
||||||
const channelId = title.attr('onclick')
|
const channelId = title.attr('onclick')
|
||||||
if (channelId) {
|
if (channelId) {
|
||||||
const site_id = channelId.match(/\s(\d+)\)/)[1]
|
const site_id = channelId.match(/\s(\d+)\)/)[1]
|
||||||
if (channel.site_id === site_id) {
|
if (channel.site_id === site_id) {
|
||||||
const startTime = $item.find('.tvGuideResult-box-wholeDates-time-hour').text().trim()
|
const startTime = $item.find('.tvGuideResult-box-wholeDates-time-hour').text().trim()
|
||||||
const duration = $item.find('.tvGuideResult-box-wholeDates-time-totalMinute')
|
const duration = $item.find('.tvGuideResult-box-wholeDates-time-totalMinute')
|
||||||
.text().trim().match(/\d+/)[0]
|
.text().trim().match(/\d+/)[0]
|
||||||
const start = dayjs.tz(`${date.format('YYYY-MM-DD')} ${startTime}`, 'YYYY-MM-DD HH:mm', tz)
|
const start = dayjs.tz(`${date.format('YYYY-MM-DD')} ${startTime}`, 'YYYY-MM-DD HH:mm', tz)
|
||||||
const stop = start.add(parseInt(duration), 'm')
|
const stop = start.add(parseInt(duration), 'm')
|
||||||
programs.push({
|
programs.push({
|
||||||
title: title.text().trim(),
|
title: title.text().trim(),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const channels = {}
|
const channels = {}
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(this.url({ date: dayjs() }))
|
.get(this.url({ date: dayjs() }))
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
const $ = cheerio.load(data)
|
const $ = cheerio.load(data)
|
||||||
$('.channelContent').toArray()
|
$('.channelContent').toArray()
|
||||||
.forEach(el => {
|
.forEach(el => {
|
||||||
const item = $(el)
|
const item = $(el)
|
||||||
const channelId = item.find('.channelDetail .tvGuideResult-box-wholeDates-title')
|
const channelId = item.find('.channelDetail .tvGuideResult-box-wholeDates-title')
|
||||||
.first()
|
.first()
|
||||||
.attr('onclick')
|
.attr('onclick')
|
||||||
if (channelId) {
|
if (channelId) {
|
||||||
const site_id = channelId.match(/\s(\d+)\)/)[1]
|
const site_id = channelId.match(/\s(\d+)\)/)[1]
|
||||||
if (channels[site_id] === undefined) {
|
if (channels[site_id] === undefined) {
|
||||||
channels[site_id] = {
|
channels[site_id] = {
|
||||||
lang: 'tr',
|
lang: 'tr',
|
||||||
site_id,
|
site_id,
|
||||||
name: item.find('#channelID').val()
|
name: item.find('#channelID').val()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return Object.values(channels)
|
return Object.values(channels)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,48 +1,48 @@
|
|||||||
const { parser, url } = require('./digiturk.com.tr.config.js')
|
const { parser, url } = require('./digiturk.com.tr.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2025-01-12', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-01-12', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: '351',
|
site_id: '351',
|
||||||
xmltv_id: 'Nickelodeon.tr'
|
xmltv_id: 'Nickelodeon.tr'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
const result = url({ date, channel })
|
const result = url({ date, channel })
|
||||||
expect(result).toBe(
|
expect(result).toBe(
|
||||||
'https://www.digiturk.com.tr/Ajax/GetTvGuideFromDigiturk?Day=01%2F12%2F2025+00%3A00%3A00'
|
'https://www.digiturk.com.tr/Ajax/GetTvGuideFromDigiturk?Day=01%2F12%2F2025+00%3A00%3A00'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.join(__dirname, '__data__', 'content.html'))
|
const content = fs.readFileSync(path.join(__dirname, '__data__', 'content.html'))
|
||||||
const results = parser({ content, channel, date }).map(p => {
|
const results = parser({ content, channel, date }).map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(57)
|
expect(results.length).toBe(57)
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2025-01-11T21:00:00.000Z',
|
start: '2025-01-11T21:00:00.000Z',
|
||||||
stop: '2025-01-11T21:25:00.000Z',
|
stop: '2025-01-11T21:25:00.000Z',
|
||||||
title: 'Sünger Bob Kare Pantolon'
|
title: 'Sünger Bob Kare Pantolon'
|
||||||
})
|
})
|
||||||
expect(results[56]).toMatchObject({
|
expect(results[56]).toMatchObject({
|
||||||
start: '2025-01-12T17:40:00.000Z',
|
start: '2025-01-12T17:40:00.000Z',
|
||||||
stop: '2025-01-12T18:00:00.000Z',
|
stop: '2025-01-12T18:00:00.000Z',
|
||||||
title: 'Casagrande Ailesi'
|
title: 'Casagrande Ailesi'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const result = parser({ content: '', channel, date })
|
const result = parser({ content: '', channel, date })
|
||||||
expect(result).toMatchObject([])
|
expect(result).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,100 +1,100 @@
|
|||||||
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0
|
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'directv.com.ar',
|
site: 'directv.com.ar',
|
||||||
days: 2,
|
days: 2,
|
||||||
url: 'https://www.directv.com.ar/guia/ChannelDetail.aspx/GetProgramming',
|
url: 'https://www.directv.com.ar/guia/ChannelDetail.aspx/GetProgramming',
|
||||||
request: {
|
request: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
Cookie: 'PGCSS=16; PGLang=S; PGCulture=es-AR;',
|
Cookie: 'PGCSS=16; PGLang=S; PGCulture=es-AR;',
|
||||||
Accept: '*/*',
|
Accept: '*/*',
|
||||||
'Accept-Language': 'es-419,es;q=0.9',
|
'Accept-Language': 'es-419,es;q=0.9',
|
||||||
Connection: 'keep-alive',
|
Connection: 'keep-alive',
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
Origin: 'https://www.directv.com.ar',
|
Origin: 'https://www.directv.com.ar',
|
||||||
Referer: 'https://www.directv.com.ar/guia/ChannelDetail.aspx?id=1740&name=TLCHD',
|
Referer: 'https://www.directv.com.ar/guia/ChannelDetail.aspx?id=1740&name=TLCHD',
|
||||||
'Sec-Fetch-Dest': 'empty',
|
'Sec-Fetch-Dest': 'empty',
|
||||||
'Sec-Fetch-Mode': 'cors',
|
'Sec-Fetch-Mode': 'cors',
|
||||||
'Sec-Fetch-Site': 'same-origin',
|
'Sec-Fetch-Site': 'same-origin',
|
||||||
'User-Agent':
|
'User-Agent':
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
|
||||||
'sec-ch-ua': '"Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"',
|
'sec-ch-ua': '"Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"',
|
||||||
'sec-ch-ua-mobile': '?0',
|
'sec-ch-ua-mobile': '?0',
|
||||||
'sec-ch-ua-platform': '"Windows"'
|
'sec-ch-ua-platform': '"Windows"'
|
||||||
},
|
},
|
||||||
data({ channel, date }) {
|
data({ channel, date }) {
|
||||||
const [channelNum, channelName] = channel.site_id.split('#')
|
const [channelNum, channelName] = channel.site_id.split('#')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
filterParameters: {
|
filterParameters: {
|
||||||
day: date.date(),
|
day: date.date(),
|
||||||
time: 0,
|
time: 0,
|
||||||
minute: 0,
|
minute: 0,
|
||||||
month: date.month() + 1,
|
month: date.month() + 1,
|
||||||
year: date.year(),
|
year: date.year(),
|
||||||
offSetValue: 0,
|
offSetValue: 0,
|
||||||
homeScreenFilter: '',
|
homeScreenFilter: '',
|
||||||
filtersScreenFilters: [''],
|
filtersScreenFilters: [''],
|
||||||
isHd: '',
|
isHd: '',
|
||||||
isChannelDetails: 'Y',
|
isChannelDetails: 'Y',
|
||||||
channelNum,
|
channelNum,
|
||||||
channelName: channelName.replace('&', '&')
|
channelName: channelName.replace('&', '&')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
parser({ content, channel }) {
|
parser({ content, channel }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
const items = parseItems(content, channel)
|
const items = parseItems(content, channel)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
description: item.description,
|
description: item.description,
|
||||||
rating: parseRating(item),
|
rating: parseRating(item),
|
||||||
start: parseStart(item),
|
start: parseStart(item),
|
||||||
stop: parseStop(item)
|
stop: parseStop(item)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseRating(item) {
|
function parseRating(item) {
|
||||||
return item.rating
|
return item.rating
|
||||||
? {
|
? {
|
||||||
system: 'MPA',
|
system: 'MPA',
|
||||||
value: item.rating
|
value: item.rating
|
||||||
}
|
}
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart(item) {
|
function parseStart(item) {
|
||||||
return dayjs.tz(item.startTimeString, 'M/D/YYYY h:mm:ss A', 'America/Argentina/Buenos_Aires')
|
return dayjs.tz(item.startTimeString, 'M/D/YYYY h:mm:ss A', 'America/Argentina/Buenos_Aires')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStop(item) {
|
function parseStop(item) {
|
||||||
return dayjs.tz(item.endTimeString, 'M/D/YYYY h:mm:ss A', 'America/Argentina/Buenos_Aires')
|
return dayjs.tz(item.endTimeString, 'M/D/YYYY h:mm:ss A', 'America/Argentina/Buenos_Aires')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, channel) {
|
function parseItems(content, channel) {
|
||||||
if (!content) return []
|
if (!content) return []
|
||||||
let [ChannelNumber, ChannelName] = channel.site_id.split('#')
|
let [ChannelNumber, ChannelName] = channel.site_id.split('#')
|
||||||
ChannelName = ChannelName.replace('&', '&')
|
ChannelName = ChannelName.replace('&', '&')
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
if (!data || !Array.isArray(data.d)) return []
|
if (!data || !Array.isArray(data.d)) return []
|
||||||
const channelData = data.d.find(
|
const channelData = data.d.find(
|
||||||
c => c.ChannelNumber == ChannelNumber && c.ChannelName === ChannelName
|
c => c.ChannelNumber == ChannelNumber && c.ChannelName === ChannelName
|
||||||
)
|
)
|
||||||
|
|
||||||
return channelData && Array.isArray(channelData.ProgramList) ? channelData.ProgramList : []
|
return channelData && Array.isArray(channelData.ProgramList) ? channelData.ProgramList : []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,85 +1,85 @@
|
|||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'directv.com.uy',
|
site: 'directv.com.uy',
|
||||||
days: 2,
|
days: 2,
|
||||||
url: 'https://www.directv.com.uy/guia/ChannelDetail.aspx/GetProgramming',
|
url: 'https://www.directv.com.uy/guia/ChannelDetail.aspx/GetProgramming',
|
||||||
request: {
|
request: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
Cookie: 'PGCSS=16384; PGLang=S; PGCulture=es-UY;'
|
Cookie: 'PGCSS=16384; PGLang=S; PGCulture=es-UY;'
|
||||||
},
|
},
|
||||||
data({ channel, date }) {
|
data({ channel, date }) {
|
||||||
const [channelNum, channelName] = channel.site_id.split('#')
|
const [channelNum, channelName] = channel.site_id.split('#')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
filterParameters: {
|
filterParameters: {
|
||||||
day: date.date(),
|
day: date.date(),
|
||||||
time: 0,
|
time: 0,
|
||||||
minute: 0,
|
minute: 0,
|
||||||
month: date.month() + 1,
|
month: date.month() + 1,
|
||||||
year: date.year(),
|
year: date.year(),
|
||||||
offSetValue: 0,
|
offSetValue: 0,
|
||||||
filtersScreenFilters: [''],
|
filtersScreenFilters: [''],
|
||||||
isHd: '',
|
isHd: '',
|
||||||
isChannelDetails: 'Y',
|
isChannelDetails: 'Y',
|
||||||
channelNum,
|
channelNum,
|
||||||
channelName: channelName.replace('&', '&')
|
channelName: channelName.replace('&', '&')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
parser({ content, channel }) {
|
parser({ content, channel }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
const items = parseItems(content, channel)
|
const items = parseItems(content, channel)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
description: item.description,
|
description: item.description,
|
||||||
rating: parseRating(item),
|
rating: parseRating(item),
|
||||||
start: parseStart(item),
|
start: parseStart(item),
|
||||||
stop: parseStop(item)
|
stop: parseStop(item)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseRating(item) {
|
function parseRating(item) {
|
||||||
return item.rating
|
return item.rating
|
||||||
? {
|
? {
|
||||||
system: 'MPA',
|
system: 'MPA',
|
||||||
value: item.rating
|
value: item.rating
|
||||||
}
|
}
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart(item) {
|
function parseStart(item) {
|
||||||
return dayjs.tz(item.startTimeString, 'M/D/YYYY h:mm:ss A', 'America/Montevideo')
|
return dayjs.tz(item.startTimeString, 'M/D/YYYY h:mm:ss A', 'America/Montevideo')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStop(item) {
|
function parseStop(item) {
|
||||||
return dayjs.tz(item.endTimeString, 'M/D/YYYY h:mm:ss A', 'America/Montevideo')
|
return dayjs.tz(item.endTimeString, 'M/D/YYYY h:mm:ss A', 'America/Montevideo')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, channel) {
|
function parseItems(content, channel) {
|
||||||
if (!content) return []
|
if (!content) return []
|
||||||
let [ChannelNumber, ChannelName] = channel.site_id.split('#')
|
let [ChannelNumber, ChannelName] = channel.site_id.split('#')
|
||||||
ChannelName = ChannelName.replace('&', '&')
|
ChannelName = ChannelName.replace('&', '&')
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
if (!data || !Array.isArray(data.d)) return []
|
if (!data || !Array.isArray(data.d)) return []
|
||||||
const channelData = data.d.find(
|
const channelData = data.d.find(
|
||||||
c => c.ChannelNumber == ChannelNumber && c.ChannelName === ChannelName
|
c => c.ChannelNumber == ChannelNumber && c.ChannelName === ChannelName
|
||||||
)
|
)
|
||||||
|
|
||||||
return channelData && Array.isArray(channelData.ProgramList) ? channelData.ProgramList : []
|
return channelData && Array.isArray(channelData.ProgramList) ? channelData.ProgramList : []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,76 +1,76 @@
|
|||||||
const { parser, url, request } = require('./directv.com.uy.config.js')
|
const { parser, url, request } = require('./directv.com.uy.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2022-08-29', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2022-08-29', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: '184#VTV',
|
site_id: '184#VTV',
|
||||||
xmltv_id: 'VTV.uy'
|
xmltv_id: 'VTV.uy'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url).toBe('https://www.directv.com.uy/guia/ChannelDetail.aspx/GetProgramming')
|
expect(url).toBe('https://www.directv.com.uy/guia/ChannelDetail.aspx/GetProgramming')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request method', () => {
|
it('can generate valid request method', () => {
|
||||||
expect(request.method).toBe('POST')
|
expect(request.method).toBe('POST')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request headers', () => {
|
it('can generate valid request headers', () => {
|
||||||
expect(request.headers).toMatchObject({
|
expect(request.headers).toMatchObject({
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
'Content-Type': 'application/json; charset=UTF-8',
|
||||||
Cookie: 'PGCSS=16384; PGLang=S; PGCulture=es-UY;'
|
Cookie: 'PGCSS=16384; PGLang=S; PGCulture=es-UY;'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request data', () => {
|
it('can generate valid request data', () => {
|
||||||
expect(request.data({ channel, date })).toMatchObject({
|
expect(request.data({ channel, date })).toMatchObject({
|
||||||
filterParameters: {
|
filterParameters: {
|
||||||
day: 29,
|
day: 29,
|
||||||
time: 0,
|
time: 0,
|
||||||
minute: 0,
|
minute: 0,
|
||||||
month: 8,
|
month: 8,
|
||||||
year: 2022,
|
year: 2022,
|
||||||
offSetValue: 0,
|
offSetValue: 0,
|
||||||
filtersScreenFilters: [''],
|
filtersScreenFilters: [''],
|
||||||
isHd: '',
|
isHd: '',
|
||||||
isChannelDetails: 'Y',
|
isChannelDetails: 'Y',
|
||||||
channelNum: '184',
|
channelNum: '184',
|
||||||
channelName: 'VTV'
|
channelName: 'VTV'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
||||||
const results = parser({ content, channel }).map(p => {
|
const results = parser({ content, channel }).map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2022-08-29T03:00:00.000Z',
|
start: '2022-08-29T03:00:00.000Z',
|
||||||
stop: '2022-08-29T05:00:00.000Z',
|
stop: '2022-08-29T05:00:00.000Z',
|
||||||
title: 'Peñarol vs. Danubio : Fútbol Uruguayo Primera División - Peñarol vs. Danubio',
|
title: 'Peñarol vs. Danubio : Fútbol Uruguayo Primera División - Peñarol vs. Danubio',
|
||||||
description:
|
description:
|
||||||
'Jornada 5 del Torneo Clausura 2022. Peñarol recibe a Danubio en el estadio Campeón del Siglo. Los carboneros llevan 3 partidos sin caer (2PG 1PE), mientras que los franjeados acumulan 6 juegos sin derrotas (4PG 2PE).',
|
'Jornada 5 del Torneo Clausura 2022. Peñarol recibe a Danubio en el estadio Campeón del Siglo. Los carboneros llevan 3 partidos sin caer (2PG 1PE), mientras que los franjeados acumulan 6 juegos sin derrotas (4PG 2PE).',
|
||||||
rating: {
|
rating: {
|
||||||
system: 'MPA',
|
system: 'MPA',
|
||||||
value: 'NR'
|
value: 'NR'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const result = parser({
|
const result = parser({
|
||||||
content: '',
|
content: '',
|
||||||
channel
|
channel
|
||||||
})
|
})
|
||||||
expect(result).toMatchObject([])
|
expect(result).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,118 +1,118 @@
|
|||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'directv.com',
|
site: 'directv.com',
|
||||||
days: 2,
|
days: 2,
|
||||||
request: {
|
request: {
|
||||||
cache: {
|
cache: {
|
||||||
ttl: 60 * 60 * 1000 // 1 hour
|
ttl: 60 * 60 * 1000 // 1 hour
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
'Accept-Language': 'en-US,en;q=0.5',
|
'Accept-Language': 'en-US,en;q=0.5',
|
||||||
Connection: 'keep-alive'
|
Connection: 'keep-alive'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
url({ date, channel }) {
|
url({ date, channel }) {
|
||||||
const [channelId, childId] = channel.site_id.split('#')
|
const [channelId, childId] = channel.site_id.split('#')
|
||||||
return `https://www.directv.com/json/channelschedule?channels=${channelId}&startTime=${date.format()}&hours=24&chId=${childId}`
|
return `https://www.directv.com/json/channelschedule?channels=${channelId}&startTime=${date.format()}&hours=24&chId=${childId}`
|
||||||
},
|
},
|
||||||
async parser({ content, channel }) {
|
async parser({ content, channel }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
const items = parseItems(content, channel)
|
const items = parseItems(content, channel)
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
if (item.programID === '-1') continue
|
if (item.programID === '-1') continue
|
||||||
const detail = await loadProgramDetail(item.programID)
|
const detail = await loadProgramDetail(item.programID)
|
||||||
const start = parseStart(item)
|
const start = parseStart(item)
|
||||||
const stop = start.add(item.duration, 'm')
|
const stop = start.add(item.duration, 'm')
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
sub_title: item.episodeTitle,
|
sub_title: item.episodeTitle,
|
||||||
description: parseDescription(detail),
|
description: parseDescription(detail),
|
||||||
rating: parseRating(item),
|
rating: parseRating(item),
|
||||||
date: parseYear(detail),
|
date: parseYear(detail),
|
||||||
category: item.subcategoryList,
|
category: item.subcategoryList,
|
||||||
season: item.seasonNumber,
|
season: item.seasonNumber,
|
||||||
episode: item.episodeNumber,
|
episode: item.episodeNumber,
|
||||||
image: parseImage(item),
|
image: parseImage(item),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const codes = [10001]
|
const codes = [10001]
|
||||||
|
|
||||||
let channels = []
|
let channels = []
|
||||||
for (let code of codes) {
|
for (let code of codes) {
|
||||||
const html = await axios
|
const html = await axios
|
||||||
.get('https://www.directv.com/guide', {
|
.get('https://www.directv.com/guide', {
|
||||||
headers: {
|
headers: {
|
||||||
cookie: `dtve-prospect-zip=${code}`
|
cookie: `dtve-prospect-zip=${code}`
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
const $ = cheerio.load(html)
|
const $ = cheerio.load(html)
|
||||||
const script = $('#dtvClientData').html()
|
const script = $('#dtvClientData').html()
|
||||||
const [, json] = script.match(/var dtvClientData = (.*);/) || [null, null]
|
const [, json] = script.match(/var dtvClientData = (.*);/) || [null, null]
|
||||||
const data = JSON.parse(json)
|
const data = JSON.parse(json)
|
||||||
|
|
||||||
data.guideData.channels.forEach(item => {
|
data.guideData.channels.forEach(item => {
|
||||||
channels.push({
|
channels.push({
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
site_id: item.chNum,
|
site_id: item.chNum,
|
||||||
name: item.chName
|
name: item.chName
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return channels
|
return channels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDescription(detail) {
|
function parseDescription(detail) {
|
||||||
return detail ? detail.description : null
|
return detail ? detail.description : null
|
||||||
}
|
}
|
||||||
function parseYear(detail) {
|
function parseYear(detail) {
|
||||||
return detail ? detail.releaseYear : null
|
return detail ? detail.releaseYear : null
|
||||||
}
|
}
|
||||||
function parseRating(item) {
|
function parseRating(item) {
|
||||||
return item.rating
|
return item.rating
|
||||||
? {
|
? {
|
||||||
system: 'MPA',
|
system: 'MPA',
|
||||||
value: item.rating
|
value: item.rating
|
||||||
}
|
}
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
function parseImage(item) {
|
function parseImage(item) {
|
||||||
return item.primaryImageUrl ? `https://www.directv.com${item.primaryImageUrl}` : null
|
return item.primaryImageUrl ? `https://www.directv.com${item.primaryImageUrl}` : null
|
||||||
}
|
}
|
||||||
function loadProgramDetail(programID) {
|
function loadProgramDetail(programID) {
|
||||||
return axios
|
return axios
|
||||||
.get(`https://www.directv.com/json/program/flip/${programID}`)
|
.get(`https://www.directv.com/json/program/flip/${programID}`)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.then(d => d.programDetail)
|
.then(d => d.programDetail)
|
||||||
.catch(console.err)
|
.catch(console.err)
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart(item) {
|
function parseStart(item) {
|
||||||
return dayjs.utc(item.airTime)
|
return dayjs.utc(item.airTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, channel) {
|
function parseItems(content, channel) {
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
if (!data) return []
|
if (!data) return []
|
||||||
if (!Array.isArray(data.schedule)) return []
|
if (!Array.isArray(data.schedule)) return []
|
||||||
|
|
||||||
const [, childId] = channel.site_id.split('#')
|
const [, childId] = channel.site_id.split('#')
|
||||||
const channelData = data.schedule.find(i => i.chId == childId)
|
const channelData = data.schedule.find(i => i.chId == childId)
|
||||||
return channelData.schedules && Array.isArray(channelData.schedules) ? channelData.schedules : []
|
return channelData.schedules && Array.isArray(channelData.schedules) ? channelData.schedules : []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,96 +1,96 @@
|
|||||||
const { parser, url } = require('./directv.com.config.js')
|
const { parser, url } = require('./directv.com.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
jest.mock('axios')
|
jest.mock('axios')
|
||||||
|
|
||||||
const date = dayjs.utc('2023-01-15', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2023-01-15', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: '249#249',
|
site_id: '249#249',
|
||||||
xmltv_id: 'ComedyCentralEast.us'
|
xmltv_id: 'ComedyCentralEast.us'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
const result = url({ date, channel })
|
const result = url({ date, channel })
|
||||||
expect(result).toBe(
|
expect(result).toBe(
|
||||||
'https://www.directv.com/json/channelschedule?channels=249&startTime=2023-01-15T00:00:00Z&hours=24&chId=249'
|
'https://www.directv.com/json/channelschedule?channels=249&startTime=2023-01-15T00:00:00Z&hours=24&chId=249'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', done => {
|
it('can parse response', done => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
||||||
|
|
||||||
axios.get.mockImplementation(url => {
|
axios.get.mockImplementation(url => {
|
||||||
if (url === 'https://www.directv.com/json/program/flip/MV001173520000') {
|
if (url === 'https://www.directv.com/json/program/flip/MV001173520000') {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/program1.json')))
|
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/program1.json')))
|
||||||
})
|
})
|
||||||
} else if (url === 'https://www.directv.com/json/program/flip/EP002298270445') {
|
} else if (url === 'https://www.directv.com/json/program/flip/EP002298270445') {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/program2.json')))
|
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/program2.json')))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve({ data: '' })
|
return Promise.resolve({ data: '' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
parser({ content, channel })
|
parser({ content, channel })
|
||||||
.then(result => {
|
.then(result => {
|
||||||
result = result.map(p => {
|
result = result.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(result).toMatchObject([
|
expect(result).toMatchObject([
|
||||||
{
|
{
|
||||||
start: '2023-01-14T23:00:00.000Z',
|
start: '2023-01-14T23:00:00.000Z',
|
||||||
stop: '2023-01-15T01:00:00.000Z',
|
stop: '2023-01-15T01:00:00.000Z',
|
||||||
title: 'Men in Black II',
|
title: 'Men in Black II',
|
||||||
description:
|
description:
|
||||||
'Kay (Tommy Lee Jones) and Jay (Will Smith) reunite to provide our best line of defense against a seductress who levels the toughest challenge yet to the MIBs mission statement: protecting the earth from the scum of the universe. While investigating a routine crime, Jay uncovers a plot masterminded by Serleena (Boyle), a Kylothian monster who disguises herself as a lingerie model. When Serleena takes the MIB building hostage, there is only one person Jay can turn to -- his former MIB partner.',
|
'Kay (Tommy Lee Jones) and Jay (Will Smith) reunite to provide our best line of defense against a seductress who levels the toughest challenge yet to the MIBs mission statement: protecting the earth from the scum of the universe. While investigating a routine crime, Jay uncovers a plot masterminded by Serleena (Boyle), a Kylothian monster who disguises herself as a lingerie model. When Serleena takes the MIB building hostage, there is only one person Jay can turn to -- his former MIB partner.',
|
||||||
date: '2002',
|
date: '2002',
|
||||||
image: 'https://www.directv.com/db_photos/movies/AllPhotosAPGI/29160/29160_aa.jpg',
|
image: 'https://www.directv.com/db_photos/movies/AllPhotosAPGI/29160/29160_aa.jpg',
|
||||||
category: ['Comedy', 'Movies Anywhere', 'Action/Adventure', 'Science Fiction'],
|
category: ['Comedy', 'Movies Anywhere', 'Action/Adventure', 'Science Fiction'],
|
||||||
rating: {
|
rating: {
|
||||||
system: 'MPA',
|
system: 'MPA',
|
||||||
value: 'TV14'
|
value: 'TV14'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
start: '2023-01-15T06:00:00.000Z',
|
start: '2023-01-15T06:00:00.000Z',
|
||||||
stop: '2023-01-15T06:30:00.000Z',
|
stop: '2023-01-15T06:30:00.000Z',
|
||||||
title: 'South Park',
|
title: 'South Park',
|
||||||
sub_title: 'Goth Kids 3: Dawn of the Posers',
|
sub_title: 'Goth Kids 3: Dawn of the Posers',
|
||||||
description: 'The goth kids are sent to a camp for troubled children.',
|
description: 'The goth kids are sent to a camp for troubled children.',
|
||||||
image:
|
image:
|
||||||
'https://www.directv.com/db_photos/showcards/v5/AllPhotos/184338/p184338_b_v5_aa.jpg',
|
'https://www.directv.com/db_photos/showcards/v5/AllPhotos/184338/p184338_b_v5_aa.jpg',
|
||||||
category: ['Series', 'Animation', 'Comedy'],
|
category: ['Series', 'Animation', 'Comedy'],
|
||||||
season: 17,
|
season: 17,
|
||||||
episode: 4,
|
episode: 4,
|
||||||
rating: {
|
rating: {
|
||||||
system: 'MPA',
|
system: 'MPA',
|
||||||
value: 'TVMA'
|
value: 'TVMA'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
.catch(done)
|
.catch(done)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', done => {
|
it('can handle empty guide', done => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/no-content.json'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/no-content.json'))
|
||||||
parser({ content, channel })
|
parser({ content, channel })
|
||||||
.then(result => {
|
.then(result => {
|
||||||
expect(result).toMatchObject([])
|
expect(result).toMatchObject([])
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
.catch(done)
|
.catch(done)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,167 +1,167 @@
|
|||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
|
|
||||||
let authToken
|
let authToken
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'dishtv.in',
|
site: 'dishtv.in',
|
||||||
days: 2,
|
days: 2,
|
||||||
url: 'https://epg.mysmartstick.com/dishtv/api/v1/epg/entities/programs',
|
url: 'https://epg.mysmartstick.com/dishtv/api/v1/epg/entities/programs',
|
||||||
request: {
|
request: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
async headers() {
|
async headers() {
|
||||||
await fetchToken()
|
await fetchToken()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Authorization: authToken
|
Authorization: authToken
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data({ channel, date }) {
|
data({ channel, date }) {
|
||||||
return {
|
return {
|
||||||
allowPastEvents: true,
|
allowPastEvents: true,
|
||||||
channelid: channel.site_id,
|
channelid: channel.site_id,
|
||||||
date: date.format('DD/MM/YYYY')
|
date: date.format('DD/MM/YYYY')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
parser: ({ content }) => {
|
parser: ({ content }) => {
|
||||||
const programs = []
|
const programs = []
|
||||||
const items = parseItems(content)
|
const items = parseItems(content)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
programs.push({
|
programs.push({
|
||||||
title: parseTitle(item),
|
title: parseTitle(item),
|
||||||
description: parseDescription(item),
|
description: parseDescription(item),
|
||||||
category: parseCategory(item),
|
category: parseCategory(item),
|
||||||
actors: item.credits.actors,
|
actors: item.credits.actors,
|
||||||
directors: item.credits.directors,
|
directors: item.credits.directors,
|
||||||
producers: item.credits.producers,
|
producers: item.credits.producers,
|
||||||
date: item.productionyear,
|
date: item.productionyear,
|
||||||
icon: parseIcon(item),
|
icon: parseIcon(item),
|
||||||
image: parseImage(item),
|
image: parseImage(item),
|
||||||
episode: parseEpisode(item),
|
episode: parseEpisode(item),
|
||||||
start: dayjs(item.start),
|
start: dayjs(item.start),
|
||||||
stop: dayjs(item.stop)
|
stop: dayjs(item.stop)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
await fetchToken()
|
await fetchToken()
|
||||||
|
|
||||||
const totalPages = await fetchPages()
|
const totalPages = await fetchPages()
|
||||||
|
|
||||||
const queue = Array.from(Array(totalPages).keys()).map(i => {
|
const queue = Array.from(Array(totalPages).keys()).map(i => {
|
||||||
const data = new FormData()
|
const data = new FormData()
|
||||||
data.append('pageNum', i + 1)
|
data.append('pageNum', i + 1)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
url: 'https://www.dishtv.in/services/epg/channels',
|
url: 'https://www.dishtv.in/services/epg/channels',
|
||||||
data,
|
data,
|
||||||
headers: {
|
headers: {
|
||||||
'authorization-token': authToken
|
'authorization-token': authToken
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const channels = []
|
const channels = []
|
||||||
for (let item of queue) {
|
for (let item of queue) {
|
||||||
const data = await axios(item)
|
const data = await axios(item)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
data.programDetailsByChannel.forEach(channel => {
|
data.programDetailsByChannel.forEach(channel => {
|
||||||
channels.push({
|
channels.push({
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
site_id: channel.channelid,
|
site_id: channel.channelid,
|
||||||
name: channel.channelname
|
name: channel.channelname
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return channels
|
return channels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTitle(item) {
|
function parseTitle(item) {
|
||||||
return Object.values(item.regional)
|
return Object.values(item.regional)
|
||||||
.map(region => ({
|
.map(region => ({
|
||||||
lang: region.languagecode,
|
lang: region.languagecode,
|
||||||
value: region.title
|
value: region.title
|
||||||
}))
|
}))
|
||||||
.filter(i => Boolean(i.value))
|
.filter(i => Boolean(i.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDescription(item) {
|
function parseDescription(item) {
|
||||||
return Object.values(item.regional)
|
return Object.values(item.regional)
|
||||||
.map(region => ({
|
.map(region => ({
|
||||||
lang: region.languagecode,
|
lang: region.languagecode,
|
||||||
value: region.desc
|
value: region.desc
|
||||||
}))
|
}))
|
||||||
.filter(i => Boolean(i.value))
|
.filter(i => Boolean(i.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseCategory(item) {
|
function parseCategory(item) {
|
||||||
return Object.values(item.regional)
|
return Object.values(item.regional)
|
||||||
.map(region => ({
|
.map(region => ({
|
||||||
lang: region.languagecode,
|
lang: region.languagecode,
|
||||||
value: region.genre
|
value: region.genre
|
||||||
}))
|
}))
|
||||||
.filter(i => Boolean(i.value))
|
.filter(i => Boolean(i.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseEpisode(item) {
|
function parseEpisode(item) {
|
||||||
return item['episode-num'] ? parseInt(item['episode-num']) : null
|
return item['episode-num'] ? parseInt(item['episode-num']) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseIcon(item) {
|
function parseIcon(item) {
|
||||||
return item.programmeurl || null
|
return item.programmeurl || null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseImage(item) {
|
function parseImage(item) {
|
||||||
return item?.images?.landscape?.['1280x720'] ? item.images.landscape['1280x720'] : null
|
return item?.images?.landscape?.['1280x720'] ? item.images.landscape['1280x720'] : null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content) {
|
function parseItems(content) {
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
|
|
||||||
return Array.isArray(data) ? data : []
|
return Array.isArray(data) ? data : []
|
||||||
} catch {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchToken() {
|
async function fetchToken() {
|
||||||
if (authToken) return
|
if (authToken) return
|
||||||
|
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.post('https://www.dishtv.in/services/epg/signin', null, {
|
.post('https://www.dishtv.in/services/epg/signin', null, {
|
||||||
headers: {
|
headers: {
|
||||||
'sec-fetch-dest': 'empty',
|
'sec-fetch-dest': 'empty',
|
||||||
'sec-fetch-mode': 'cors',
|
'sec-fetch-mode': 'cors',
|
||||||
'sec-fetch-site': 'same-origin',
|
'sec-fetch-site': 'same-origin',
|
||||||
'x-requested-with': 'XMLHttpRequest',
|
'x-requested-with': 'XMLHttpRequest',
|
||||||
Referer: 'https://www.dishtv.in/channel-guide.html'
|
Referer: 'https://www.dishtv.in/channel-guide.html'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
authToken = data.token
|
authToken = data.token
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchPages() {
|
async function fetchPages() {
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('pageNum', 1)
|
formData.append('pageNum', 1)
|
||||||
|
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.post('https://www.dishtv.in/services/epg/channels', formData, {
|
.post('https://www.dishtv.in/services/epg/channels', formData, {
|
||||||
headers: { 'authorization-token': authToken }
|
headers: { 'authorization-token': authToken }
|
||||||
})
|
})
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
return data.totalPages ? parseInt(data.totalPages) : 0
|
return data.totalPages ? parseInt(data.totalPages) : 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,140 +1,140 @@
|
|||||||
const { parser, url, request } = require('./dishtv.in.config.js')
|
const { parser, url, request } = require('./dishtv.in.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
jest.mock('axios')
|
jest.mock('axios')
|
||||||
|
|
||||||
axios.post.mockImplementation((url, data, params) => {
|
axios.post.mockImplementation((url, data, params) => {
|
||||||
if (
|
if (
|
||||||
url === 'https://www.dishtv.in/services/epg/signin' &&
|
url === 'https://www.dishtv.in/services/epg/signin' &&
|
||||||
data === null &&
|
data === null &&
|
||||||
JSON.stringify(params) ===
|
JSON.stringify(params) ===
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
headers: {
|
headers: {
|
||||||
'sec-fetch-dest': 'empty',
|
'sec-fetch-dest': 'empty',
|
||||||
'sec-fetch-mode': 'cors',
|
'sec-fetch-mode': 'cors',
|
||||||
'sec-fetch-site': 'same-origin',
|
'sec-fetch-site': 'same-origin',
|
||||||
'x-requested-with': 'XMLHttpRequest',
|
'x-requested-with': 'XMLHttpRequest',
|
||||||
Referer: 'https://www.dishtv.in/channel-guide.html'
|
Referer: 'https://www.dishtv.in/channel-guide.html'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/session.json'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/session.json'))
|
||||||
|
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: JSON.parse(content)
|
data: JSON.parse(content)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: ''
|
data: ''
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const date = dayjs.utc('2025-01-26', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-01-26', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = { site_id: '142639', xmltv_id: 'AndpriveHD.in' }
|
const channel = { site_id: '142639', xmltv_id: 'AndpriveHD.in' }
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url).toBe('https://epg.mysmartstick.com/dishtv/api/v1/epg/entities/programs')
|
expect(url).toBe('https://epg.mysmartstick.com/dishtv/api/v1/epg/entities/programs')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request method', () => {
|
it('can generate valid request method', () => {
|
||||||
expect(request.method).toBe('POST')
|
expect(request.method).toBe('POST')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request headers', async () => {
|
it('can generate valid request headers', async () => {
|
||||||
expect(await request.headers()).toMatchObject({
|
expect(await request.headers()).toMatchObject({
|
||||||
Authorization:
|
Authorization:
|
||||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRpZCI6ImRpc2h0di13ZWJzaXRlIiwicGxhdGZvcm0iOiJkaXNodHYiLCJpYXQiOjE3Mzc2ODIxNjEsImV4cCI6MTczNzc2ODU2MX0.sPrYfodVTbf1kJ-wGICDlnH-Yt3J0-mB-M2YROU8v2Q'
|
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRpZCI6ImRpc2h0di13ZWJzaXRlIiwicGxhdGZvcm0iOiJkaXNodHYiLCJpYXQiOjE3Mzc2ODIxNjEsImV4cCI6MTczNzc2ODU2MX0.sPrYfodVTbf1kJ-wGICDlnH-Yt3J0-mB-M2YROU8v2Q'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request data', () => {
|
it('can generate valid request data', () => {
|
||||||
expect(request.data({ channel, date })).toMatchObject({
|
expect(request.data({ channel, date })).toMatchObject({
|
||||||
allowPastEvents: true,
|
allowPastEvents: true,
|
||||||
channelid: '142639',
|
channelid: '142639',
|
||||||
date: '26/01/2025'
|
date: '26/01/2025'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
||||||
let results = parser({ content })
|
let results = parser({ content })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(16)
|
expect(results.length).toBe(16)
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2025-01-26T00:30:00.000Z',
|
start: '2025-01-26T00:30:00.000Z',
|
||||||
stop: '2025-01-26T02:05:00.000Z',
|
stop: '2025-01-26T02:05:00.000Z',
|
||||||
title: [
|
title: [
|
||||||
{ lang: 'en', value: 'Train to Busan 2: Peninsula' },
|
{ lang: 'en', value: 'Train to Busan 2: Peninsula' },
|
||||||
{ lang: 'hi', value: 'ट्रेन टू बुसान 2: पेनीनसुला' },
|
{ lang: 'hi', value: 'ट्रेन टू बुसान 2: पेनीनसुला' },
|
||||||
{ lang: 'ta', value: 'ட்ரெயின் டு பூசன் ப்ரெசென்ட்ஸ்: பெனின்சுலா' },
|
{ lang: 'ta', value: 'ட்ரெயின் டு பூசன் ப்ரெசென்ட்ஸ்: பெனின்சுலா' },
|
||||||
{ lang: 'te', value: 'ట్రేన్ టు బూసాన్ ప్రజెంట్స్: పెనిన్సులా' }
|
{ lang: 'te', value: 'ట్రేన్ టు బూసాన్ ప్రజెంట్స్: పెనిన్సులా' }
|
||||||
],
|
],
|
||||||
description: [
|
description: [
|
||||||
{
|
{
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
value:
|
value:
|
||||||
'Jung Seok, a former soldier, along with his teammates, sets out on a mission to battle hordes of post-apocalyptic zombies in the Korean peninsula wastelands.'
|
'Jung Seok, a former soldier, along with his teammates, sets out on a mission to battle hordes of post-apocalyptic zombies in the Korean peninsula wastelands.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
lang: 'hi',
|
lang: 'hi',
|
||||||
value:
|
value:
|
||||||
'एक भूतपूर्व सैनिक जंग सोक अपने साथियों के साथ कोरियाई प्रायद्वीप के बंजर इलाकों में सर्वनाश के बाद की जोंबी से लड़ने के मिशन पर निकलता है।'
|
'एक भूतपूर्व सैनिक जंग सोक अपने साथियों के साथ कोरियाई प्रायद्वीप के बंजर इलाकों में सर्वनाश के बाद की जोंबी से लड़ने के मिशन पर निकलता है।'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
lang: 'ta',
|
lang: 'ta',
|
||||||
value:
|
value:
|
||||||
'கொரிய தீபகற்பத்தின் தரிசு நிலங்களில் அபோகாலிப்டிக் ஜாம்பிக்களின் கூட்டத்தை எதிர்த்து தன் குழுவுடன் போரிடும் ஜங் சியோக்.'
|
'கொரிய தீபகற்பத்தின் தரிசு நிலங்களில் அபோகாலிப்டிக் ஜாம்பிக்களின் கூட்டத்தை எதிர்த்து தன் குழுவுடன் போரிடும் ஜங் சியோக்.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
lang: 'te',
|
lang: 'te',
|
||||||
value:
|
value:
|
||||||
'మాజీ సైనికుడు జంగ్ సియోక్ తన సహచరులతో కలిసి కొరియా ద్వీపకల్పంలో పోస్ట్-అపోకలిప్టిక్ జాంబీలతో యుద్దానికి సిద్దమవుతాడు.'
|
'మాజీ సైనికుడు జంగ్ సియోక్ తన సహచరులతో కలిసి కొరియా ద్వీపకల్పంలో పోస్ట్-అపోకలిప్టిక్ జాంబీలతో యుద్దానికి సిద్దమవుతాడు.'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
category: [
|
category: [
|
||||||
{ lang: 'en', value: 'Film' },
|
{ lang: 'en', value: 'Film' },
|
||||||
{ lang: 'hi', value: 'फ़िल्म' },
|
{ lang: 'hi', value: 'फ़िल्म' },
|
||||||
{ lang: 'ta', value: '??????????' },
|
{ lang: 'ta', value: '??????????' },
|
||||||
{ lang: 'te', value: 'సినిమా' },
|
{ lang: 'te', value: 'సినిమా' },
|
||||||
{ lang: 'mr', value: 'चित्रपट' }
|
{ lang: 'mr', value: 'चित्रपट' }
|
||||||
],
|
],
|
||||||
actors: [
|
actors: [
|
||||||
'Gang Dong-won',
|
'Gang Dong-won',
|
||||||
'Lee Jung-hyun',
|
'Lee Jung-hyun',
|
||||||
'Lee Re',
|
'Lee Re',
|
||||||
'Kwon Hae-hyo',
|
'Kwon Hae-hyo',
|
||||||
'John D. Michaels',
|
'John D. Michaels',
|
||||||
'Kim Min-jae',
|
'Kim Min-jae',
|
||||||
'Kim Doyun',
|
'Kim Doyun',
|
||||||
'Lee Ye-won',
|
'Lee Ye-won',
|
||||||
'Daniel Joey Albright',
|
'Daniel Joey Albright',
|
||||||
'Pierce Conran',
|
'Pierce Conran',
|
||||||
'Geoffrey Giuliano',
|
'Geoffrey Giuliano',
|
||||||
'Milan-Devi LaBrey'
|
'Milan-Devi LaBrey'
|
||||||
],
|
],
|
||||||
producers: [],
|
producers: [],
|
||||||
directors: ['Yeon Sang-ho'],
|
directors: ['Yeon Sang-ho'],
|
||||||
icon: 'https://dtil.tmsimg.com/assets/p17850257_v_h9_al.jpg?lock=880x660',
|
icon: 'https://dtil.tmsimg.com/assets/p17850257_v_h9_al.jpg?lock=880x660',
|
||||||
image: 'https://dtil.tmsimg.com/assets/p17850257_v_h8_am.jpg?lock=1280x720',
|
image: 'https://dtil.tmsimg.com/assets/p17850257_v_h8_am.jpg?lock=1280x720',
|
||||||
date: '2020'
|
date: '2020'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const results = parser({ content: '[]' })
|
const results = parser({ content: '[]' })
|
||||||
|
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,99 +1,99 @@
|
|||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'dna.fi',
|
site: 'dna.fi',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ date, channel }) {
|
url({ date, channel }) {
|
||||||
const beginTimestamp = date.add(2, 'h').valueOf()
|
const beginTimestamp = date.add(2, 'h').valueOf()
|
||||||
const endTimestamp = date.add(1, 'd').add(2, 'h').subtract(1, 's').valueOf()
|
const endTimestamp = date.add(1, 'd').add(2, 'h').subtract(1, 's').valueOf()
|
||||||
|
|
||||||
return `https://mts-pro-envoy-vip.dna.fi/hbx/api/pub/xrtv/g/media?q=channel:${channel.site_id}&q=profile:pr&q=start-interval:${beginTimestamp}/${endTimestamp}`
|
return `https://mts-pro-envoy-vip.dna.fi/hbx/api/pub/xrtv/g/media?q=channel:${channel.site_id}&q=profile:pr&q=start-interval:${beginTimestamp}/${endTimestamp}`
|
||||||
},
|
},
|
||||||
parser({ content, date }) {
|
parser({ content, date }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
let items = parseItems(content, date)
|
let items = parseItems(content, date)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const data = item?._embedded?.['xrtv:meta']?.data
|
const data = item?._embedded?.['xrtv:meta']?.data
|
||||||
programs.push({
|
programs.push({
|
||||||
title: data?.title,
|
title: data?.title,
|
||||||
subtitle: data?.episode_title,
|
subtitle: data?.episode_title,
|
||||||
description: data?.description,
|
description: data?.description,
|
||||||
season: data?.season_number,
|
season: data?.season_number,
|
||||||
episode: data?.episode_number,
|
episode: data?.episode_number,
|
||||||
date: data?.year,
|
date: data?.year,
|
||||||
categories: parseCategories(item),
|
categories: parseCategories(item),
|
||||||
rating: parseRating(data),
|
rating: parseRating(data),
|
||||||
images: parseImages(item),
|
images: parseImages(item),
|
||||||
directors: parseCast(data, 'director'),
|
directors: parseCast(data, 'director'),
|
||||||
actors: parseCast(data, 'actors'),
|
actors: parseCast(data, 'actors'),
|
||||||
start: dayjs(data?.start),
|
start: dayjs(data?.start),
|
||||||
stop: dayjs(data?.end)
|
stop: dayjs(data?.end)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get('https://mts-pro-envoy-vip.dna.fi/hbx/api/pub/xrtv/g/media?q=profile:ch&limit=1000')
|
.get('https://mts-pro-envoy-vip.dna.fi/hbx/api/pub/xrtv/g/media?q=profile:ch&limit=1000')
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
return data._embedded['xrtv:media-item'].map(c => {
|
return data._embedded['xrtv:media-item'].map(c => {
|
||||||
return {
|
return {
|
||||||
lang: 'fi',
|
lang: 'fi',
|
||||||
site_id: c.datalistTerm,
|
site_id: c.datalistTerm,
|
||||||
name: c.name
|
name: c.name
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseCast(data, role) {
|
function parseCast(data, role) {
|
||||||
if (!data[role] || !data[role].value) return []
|
if (!data[role] || !data[role].value) return []
|
||||||
|
|
||||||
return data[role].value.split(', ').map(name => ({
|
return data[role].value.split(', ').map(name => ({
|
||||||
lang: data[role].lang,
|
lang: data[role].lang,
|
||||||
value: name
|
value: name
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseCategories(item) {
|
function parseCategories(item) {
|
||||||
const categories = item?._embedded?.['xrtv:media-category']
|
const categories = item?._embedded?.['xrtv:media-category']
|
||||||
|
|
||||||
return Array.isArray(categories) ? categories.map(category => category.name) : []
|
return Array.isArray(categories) ? categories.map(category => category.name) : []
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseRating(data) {
|
function parseRating(data) {
|
||||||
if (!data.age_rating) return null
|
if (!data.age_rating) return null
|
||||||
|
|
||||||
return {
|
return {
|
||||||
system: 'VET',
|
system: 'VET',
|
||||||
value: data.age_rating
|
value: data.age_rating
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseImages(item) {
|
function parseImages(item) {
|
||||||
const images = item?._embedded?.['xrtv:image']
|
const images = item?._embedded?.['xrtv:image']
|
||||||
|
|
||||||
return Array.isArray(images) ? images.map(image => image.src) : []
|
return Array.isArray(images) ? images.map(image => image.src) : []
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, date) {
|
function parseItems(content, date) {
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
let items = data?._embedded?.['xrtv:media-item']
|
let items = data?._embedded?.['xrtv:media-item']
|
||||||
items = Array.isArray(items) ? items : []
|
items = Array.isArray(items) ? items : []
|
||||||
items = items.filter(item => {
|
items = items.filter(item => {
|
||||||
const start = item?._embedded?.['xrtv:meta']?.data?.start
|
const start = item?._embedded?.['xrtv:meta']?.data?.start
|
||||||
if (!start) return false
|
if (!start) return false
|
||||||
|
|
||||||
return date.isSame(dayjs(start), 'day')
|
return date.isSame(dayjs(start), 'day')
|
||||||
})
|
})
|
||||||
|
|
||||||
return items
|
return items
|
||||||
} catch {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,138 +1,138 @@
|
|||||||
const { parser, url } = require('./dna.fi.config.js')
|
const { parser, url } = require('./dna.fi.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2025-01-15', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-01-15', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: 'ch-216356',
|
site_id: 'ch-216356',
|
||||||
xmltv_id: 'MTV3.fi'
|
xmltv_id: 'MTV3.fi'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', async () => {
|
it('can generate valid url', async () => {
|
||||||
expect(url({ date, channel })).toBe(
|
expect(url({ date, channel })).toBe(
|
||||||
'https://mts-pro-envoy-vip.dna.fi/hbx/api/pub/xrtv/g/media?q=channel:ch-216356&q=profile:pr&q=start-interval:1736906400000/1736992799000'
|
'https://mts-pro-envoy-vip.dna.fi/hbx/api/pub/xrtv/g/media?q=channel:ch-216356&q=profile:pr&q=start-interval:1736906400000/1736992799000'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'), 'utf8')
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'), 'utf8')
|
||||||
let results = parser({ date, content })
|
let results = parser({ date, content })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(20)
|
expect(results.length).toBe(20)
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2025-01-15T02:30:00.000Z',
|
start: '2025-01-15T02:30:00.000Z',
|
||||||
stop: '2025-01-15T03:22:00.000Z',
|
stop: '2025-01-15T03:22:00.000Z',
|
||||||
title: {
|
title: {
|
||||||
lang: 'fi',
|
lang: 'fi',
|
||||||
value: 'Next Level Chef'
|
value: 'Next Level Chef'
|
||||||
},
|
},
|
||||||
subtitle: {
|
subtitle: {
|
||||||
lang: 'fi',
|
lang: 'fi',
|
||||||
value: 'Brunssi'
|
value: 'Brunssi'
|
||||||
},
|
},
|
||||||
season: 1,
|
season: 1,
|
||||||
episode: 6,
|
episode: 6,
|
||||||
rating: {
|
rating: {
|
||||||
system: 'VET',
|
system: 'VET',
|
||||||
value: 'S'
|
value: 'S'
|
||||||
},
|
},
|
||||||
date: '2022',
|
date: '2022',
|
||||||
images: [
|
images: [
|
||||||
'https://mts-pro-cache-vip.dna.fi/meme/v2/37f/3851073346622580374_aspect_ratio_16_9_1.jpg'
|
'https://mts-pro-cache-vip.dna.fi/meme/v2/37f/3851073346622580374_aspect_ratio_16_9_1.jpg'
|
||||||
],
|
],
|
||||||
description: {
|
description: {
|
||||||
lang: 'fi',
|
lang: 'fi',
|
||||||
value:
|
value:
|
||||||
'Kausi 1, 6/11. Brunssi. Päivän haasteessa valmistetaan rentoa brunssiruokaa. Yksi kilpailija tekee valtaisan virheen myöhästyessään annosten luovutuksesta. Amerikkalainen tosi-tv-sarja.'
|
'Kausi 1, 6/11. Brunssi. Päivän haasteessa valmistetaan rentoa brunssiruokaa. Yksi kilpailija tekee valtaisan virheen myöhästyessään annosten luovutuksesta. Amerikkalainen tosi-tv-sarja.'
|
||||||
},
|
},
|
||||||
categories: ['Reality TV', 'Entertainment', 'TV Show', 'Next Level Chef', 'Series 1']
|
categories: ['Reality TV', 'Entertainment', 'TV Show', 'Next Level Chef', 'Series 1']
|
||||||
})
|
})
|
||||||
expect(results[5]).toMatchObject({
|
expect(results[5]).toMatchObject({
|
||||||
title: {
|
title: {
|
||||||
lang: 'fi',
|
lang: 'fi',
|
||||||
value: 'Kauniit ja rohkeat (S)'
|
value: 'Kauniit ja rohkeat (S)'
|
||||||
},
|
},
|
||||||
subtitle: {
|
subtitle: {
|
||||||
lang: 'fi',
|
lang: 'fi',
|
||||||
value: 'Parantava syleily'
|
value: 'Parantava syleily'
|
||||||
},
|
},
|
||||||
start: '2025-01-15T08:30:00.000Z',
|
start: '2025-01-15T08:30:00.000Z',
|
||||||
stop: '2025-01-15T09:00:00.000Z',
|
stop: '2025-01-15T09:00:00.000Z',
|
||||||
season: 37,
|
season: 37,
|
||||||
episode: 9380,
|
episode: 9380,
|
||||||
rating: {
|
rating: {
|
||||||
system: 'VET',
|
system: 'VET',
|
||||||
value: 'S'
|
value: 'S'
|
||||||
},
|
},
|
||||||
date: '2023',
|
date: '2023',
|
||||||
images: [
|
images: [
|
||||||
'https://mts-pro-cache-vip.dna.fi/meme/v2/79e/6509488401145439178_aspect_ratio_16_9_1.jpg'
|
'https://mts-pro-cache-vip.dna.fi/meme/v2/79e/6509488401145439178_aspect_ratio_16_9_1.jpg'
|
||||||
],
|
],
|
||||||
description: {
|
description: {
|
||||||
lang: 'fi',
|
lang: 'fi',
|
||||||
value:
|
value:
|
||||||
'Steffy on vähällä yllättää Hopen ja Carterin kesken herkän hetken. Ridgen kannustamana Taylor suostuu kokeilemaan Shandran parannusmenetelmää, ja pitkään padotut tunteet saavat viimein vapautua.'
|
'Steffy on vähällä yllättää Hopen ja Carterin kesken herkän hetken. Ridgen kannustamana Taylor suostuu kokeilemaan Shandran parannusmenetelmää, ja pitkään padotut tunteet saavat viimein vapautua.'
|
||||||
},
|
},
|
||||||
categories: [
|
categories: [
|
||||||
'Soap',
|
'Soap',
|
||||||
'Drama',
|
'Drama',
|
||||||
'Romance',
|
'Romance',
|
||||||
'Series',
|
'Series',
|
||||||
'TV Show',
|
'TV Show',
|
||||||
'The Bold and the Beautiful',
|
'The Bold and the Beautiful',
|
||||||
'Series 37'
|
'Series 37'
|
||||||
],
|
],
|
||||||
actors: [{ lang: 'en', value: 'Katherine Kelly Lang' }]
|
actors: [{ lang: 'en', value: 'Katherine Kelly Lang' }]
|
||||||
})
|
})
|
||||||
expect(results[19]).toMatchObject({
|
expect(results[19]).toMatchObject({
|
||||||
start: '2025-01-15T16:30:00.000Z',
|
start: '2025-01-15T16:30:00.000Z',
|
||||||
stop: '2025-01-15T17:00:00.000Z',
|
stop: '2025-01-15T17:00:00.000Z',
|
||||||
title: {
|
title: {
|
||||||
lang: 'fi',
|
lang: 'fi',
|
||||||
value: 'Emmerdale (S)'
|
value: 'Emmerdale (S)'
|
||||||
},
|
},
|
||||||
subtitle: {
|
subtitle: {
|
||||||
lang: 'fi',
|
lang: 'fi',
|
||||||
value: 'Epäilyksen varjossa'
|
value: 'Epäilyksen varjossa'
|
||||||
},
|
},
|
||||||
season: 54,
|
season: 54,
|
||||||
episode: 9845,
|
episode: 9845,
|
||||||
rating: {
|
rating: {
|
||||||
system: 'VET',
|
system: 'VET',
|
||||||
value: 'S'
|
value: 'S'
|
||||||
},
|
},
|
||||||
date: '2023',
|
date: '2023',
|
||||||
images: [
|
images: [
|
||||||
'https://mts-pro-cache-vip.dna.fi/meme/v2/5e8/5978592001161112833_aspect_ratio_16_9_1.jpg'
|
'https://mts-pro-cache-vip.dna.fi/meme/v2/5e8/5978592001161112833_aspect_ratio_16_9_1.jpg'
|
||||||
],
|
],
|
||||||
description: {
|
description: {
|
||||||
lang: 'fi',
|
lang: 'fi',
|
||||||
value:
|
value:
|
||||||
'Caleb haistaa palaneen käryä Craigin kuolemaan liittyen. Mackenzien yllätysvierailu antaa vahvistuksen Chloen päätökselle. Lydia pohtii, pitäisikö hänen mennä Craigin hautajaisiin. Dawnin supistukset säikäyttävät Rhonan.'
|
'Caleb haistaa palaneen käryä Craigin kuolemaan liittyen. Mackenzien yllätysvierailu antaa vahvistuksen Chloen päätökselle. Lydia pohtii, pitäisikö hänen mennä Craigin hautajaisiin. Dawnin supistukset säikäyttävät Rhonan.'
|
||||||
},
|
},
|
||||||
categories: ['Soap', 'Drama', 'Romance', 'Series', 'TV Show', 'Emmerdale', 'Series 54'],
|
categories: ['Soap', 'Drama', 'Romance', 'Series', 'TV Show', 'Emmerdale', 'Series 54'],
|
||||||
directors: [
|
directors: [
|
||||||
{ lang: 'en', value: 'Ian Bevitt' },
|
{ lang: 'en', value: 'Ian Bevitt' },
|
||||||
{ lang: 'en', value: 'Munir Malik' }
|
{ lang: 'en', value: 'Munir Malik' }
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const results = parser({
|
const results = parser({
|
||||||
date,
|
date,
|
||||||
content: ''
|
content: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,130 +1,130 @@
|
|||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
const duration = require('dayjs/plugin/duration')
|
const duration = require('dayjs/plugin/duration')
|
||||||
const doFetch = require('@ntlab/sfetch')
|
const doFetch = require('@ntlab/sfetch')
|
||||||
const debug = require('debug')('site:dsmart.com.tr')
|
const debug = require('debug')('site:dsmart.com.tr')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(duration)
|
dayjs.extend(duration)
|
||||||
|
|
||||||
doFetch.setDebugger(debug)
|
doFetch.setDebugger(debug)
|
||||||
|
|
||||||
const channelsWithSchedule = true
|
const channelsWithSchedule = true
|
||||||
const pageLimit = 10
|
const pageLimit = 10
|
||||||
const caches = {}
|
const caches = {}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'dsmart.com.tr',
|
site: 'dsmart.com.tr',
|
||||||
days: 2,
|
days: 2,
|
||||||
request: {
|
request: {
|
||||||
cache: {
|
cache: {
|
||||||
ttl: 24 * 60 * 60 * 1000 // 1 day
|
ttl: 24 * 60 * 60 * 1000 // 1 day
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
url({ date, page = 1 }) {
|
url({ date, page = 1 }) {
|
||||||
return `https://www.dsmart.com.tr/api/v1/public/epg/schedules?page=${
|
return `https://www.dsmart.com.tr/api/v1/public/epg/schedules?page=${
|
||||||
page
|
page
|
||||||
}&limit=${
|
}&limit=${
|
||||||
pageLimit
|
pageLimit
|
||||||
}&day=${
|
}&day=${
|
||||||
date.format('YYYY-MM-DD')
|
date.format('YYYY-MM-DD')
|
||||||
}`
|
}`
|
||||||
},
|
},
|
||||||
async parser({ content, channel, date, useCache = true }) {
|
async parser({ content, channel, date, useCache = true }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
if (content) {
|
if (content) {
|
||||||
if (typeof content === 'string') {
|
if (typeof content === 'string') {
|
||||||
content = JSON.parse(content)
|
content = JSON.parse(content)
|
||||||
}
|
}
|
||||||
if (useCache) {
|
if (useCache) {
|
||||||
const cacheKey = date.format('YYYYMMDD')
|
const cacheKey = date.format('YYYYMMDD')
|
||||||
// cache whole channels for the day
|
// cache whole channels for the day
|
||||||
if (caches[cacheKey] === undefined) {
|
if (caches[cacheKey] === undefined) {
|
||||||
if (content?.data?.total) {
|
if (content?.data?.total) {
|
||||||
const queues = []
|
const queues = []
|
||||||
const pages = Math.ceil(content.data.total / pageLimit)
|
const pages = Math.ceil(content.data.total / pageLimit)
|
||||||
for (let page = 2; page <= pages; page++) {
|
for (let page = 2; page <= pages; page++) {
|
||||||
queues.push(module.exports.url({ date, page }))
|
queues.push(module.exports.url({ date, page }))
|
||||||
}
|
}
|
||||||
await doFetch(queues, (url, res) => {
|
await doFetch(queues, (url, res) => {
|
||||||
if (Array.isArray(res?.data?.channels)) {
|
if (Array.isArray(res?.data?.channels)) {
|
||||||
content.data.channels.push(...res.data.channels)
|
content.data.channels.push(...res.data.channels)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
caches[cacheKey] = content
|
caches[cacheKey] = content
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
content = caches[cacheKey]
|
content = caches[cacheKey]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Array.isArray(content?.data?.channels)) {
|
if (Array.isArray(content?.data?.channels)) {
|
||||||
content.data.channels
|
content.data.channels
|
||||||
.filter(i => i._id === channel.site_id)
|
.filter(i => i._id === channel.site_id)
|
||||||
.forEach(i => {
|
.forEach(i => {
|
||||||
if (i.schedule.length) {
|
if (i.schedule.length) {
|
||||||
let dayStart, ofs
|
let dayStart, ofs
|
||||||
programs.push(...i.schedule
|
programs.push(...i.schedule
|
||||||
.map(p => {
|
.map(p => {
|
||||||
const baseDate = dayjs.utc(p.day)
|
const baseDate = dayjs.utc(p.day)
|
||||||
const startDate = dayjs.utc(p.start_date)
|
const startDate = dayjs.utc(p.start_date)
|
||||||
// calculate base offset if needed
|
// calculate base offset if needed
|
||||||
if (!dayStart) {
|
if (!dayStart) {
|
||||||
dayStart = startDate
|
dayStart = startDate
|
||||||
ofs = dayjs.duration(dayjs.utc(`${p.day.substr(0, 11)}${p.start_date.substr(11)}`).diff(baseDate))
|
ofs = dayjs.duration(dayjs.utc(`${p.day.substr(0, 11)}${p.start_date.substr(11)}`).diff(baseDate))
|
||||||
.asSeconds()
|
.asSeconds()
|
||||||
}
|
}
|
||||||
const delta = dayjs.duration(startDate.diff(dayStart)).asSeconds()
|
const delta = dayjs.duration(startDate.diff(dayStart)).asSeconds()
|
||||||
// ignore days in duration
|
// ignore days in duration
|
||||||
const [h, m, s] = (p.duration.includes(',') ? p.duration.split(',')[1].trim() : p.duration)
|
const [h, m, s] = (p.duration.includes(',') ? p.duration.split(',')[1].trim() : p.duration)
|
||||||
.split(':').map(Number)
|
.split(':').map(Number)
|
||||||
const duration = (h * 3600) + (m * 60) + s
|
const duration = (h * 3600) + (m * 60) + s
|
||||||
const start = baseDate.add(ofs + delta, 's')
|
const start = baseDate.add(ofs + delta, 's')
|
||||||
const stop = start.add(duration, 's')
|
const stop = start.add(duration, 's')
|
||||||
return {
|
return {
|
||||||
title: p.program_name,
|
title: p.program_name,
|
||||||
description: p.description,
|
description: p.description,
|
||||||
category: p.genre && p.genre.includes('/') ?
|
category: p.genre && p.genre.includes('/') ?
|
||||||
p.genre.split('/').map(g => `${g.substr(0, 1).toUpperCase()}${g.substr(1)}`) : null,
|
p.genre.split('/').map(g => `${g.substr(0, 1).toUpperCase()}${g.substr(1)}`) : null,
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const channels = []
|
const channels = []
|
||||||
const f = page => this.url({ date: dayjs(), page })
|
const f = page => this.url({ date: dayjs(), page })
|
||||||
let pages, page = 1
|
let pages, page = 1
|
||||||
const queues = [f(page)]
|
const queues = [f(page)]
|
||||||
await doFetch(queues, (url, res) => {
|
await doFetch(queues, (url, res) => {
|
||||||
if (!pages && res.data.total) {
|
if (!pages && res.data.total) {
|
||||||
pages = Math.ceil(res.data.total / pageLimit)
|
pages = Math.ceil(res.data.total / pageLimit)
|
||||||
while (page < pages) {
|
while (page < pages) {
|
||||||
queues.push(f(++page))
|
queues.push(f(++page))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Array.isArray(res?.data?.channels)) {
|
if (Array.isArray(res?.data?.channels)) {
|
||||||
channels.push(...res.data.channels
|
channels.push(...res.data.channels
|
||||||
.filter(i => (channelsWithSchedule && i.schedule.length) || !channelsWithSchedule)
|
.filter(i => (channelsWithSchedule && i.schedule.length) || !channelsWithSchedule)
|
||||||
.map(i => {
|
.map(i => {
|
||||||
return {
|
return {
|
||||||
lang: 'tr',
|
lang: 'tr',
|
||||||
name: i.channel_name,
|
name: i.channel_name,
|
||||||
site_id: i._id
|
site_id: i._id
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return channels
|
return channels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,82 +1,82 @@
|
|||||||
const { parser, url } = require('./dsmart.com.tr.config.js')
|
const { parser, url } = require('./dsmart.com.tr.config.js')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
jest.mock('axios')
|
jest.mock('axios')
|
||||||
|
|
||||||
const date = dayjs.utc('2025-01-13', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-01-13', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: '5fe07f5dcfef0b1593275822',
|
site_id: '5fe07f5dcfef0b1593275822',
|
||||||
xmltv_id: 'Sinema1001.tr'
|
xmltv_id: 'Sinema1001.tr'
|
||||||
}
|
}
|
||||||
|
|
||||||
axios.get.mockImplementation(url => {
|
axios.get.mockImplementation(url => {
|
||||||
const result = {}
|
const result = {}
|
||||||
const urls = {
|
const urls = {
|
||||||
'https://www.dsmart.com.tr/api/v1/public/epg/schedules?page=1&limit=10&day=2025-01-13':
|
'https://www.dsmart.com.tr/api/v1/public/epg/schedules?page=1&limit=10&day=2025-01-13':
|
||||||
'content1.json',
|
'content1.json',
|
||||||
'https://www.dsmart.com.tr/api/v1/public/epg/schedules?page=2&limit=10&day=2025-01-13':
|
'https://www.dsmart.com.tr/api/v1/public/epg/schedules?page=2&limit=10&day=2025-01-13':
|
||||||
'content2.json',
|
'content2.json',
|
||||||
}
|
}
|
||||||
if (urls[url] !== undefined) {
|
if (urls[url] !== undefined) {
|
||||||
result.data = fs.readFileSync(path.join(__dirname, '__data__', urls[url])).toString()
|
result.data = fs.readFileSync(path.join(__dirname, '__data__', urls[url])).toString()
|
||||||
if (!urls[url].startsWith('content1')) {
|
if (!urls[url].startsWith('content1')) {
|
||||||
result.data = JSON.parse(result.data)
|
result.data = JSON.parse(result.data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.resolve(result)
|
return Promise.resolve(result)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ date })).toBe(
|
expect(url({ date })).toBe(
|
||||||
'https://www.dsmart.com.tr/api/v1/public/epg/schedules?page=1&limit=10&day=2025-01-13'
|
'https://www.dsmart.com.tr/api/v1/public/epg/schedules?page=1&limit=10&day=2025-01-13'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', async () => {
|
it('can parse response', async () => {
|
||||||
const content = fs.readFileSync(path.join(__dirname, '__data__', 'content1.json')).toString()
|
const content = fs.readFileSync(path.join(__dirname, '__data__', 'content1.json')).toString()
|
||||||
const results = (await parser({ content, channel, date })).map(p => {
|
const results = (await parser({ content, channel, date })).map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(11)
|
expect(results.length).toBe(11)
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2025-01-12T21:30:00.000Z',
|
start: '2025-01-12T21:30:00.000Z',
|
||||||
stop: '2025-01-12T23:30:00.000Z',
|
stop: '2025-01-12T23:30:00.000Z',
|
||||||
title: 'Taksi Şoförü',
|
title: 'Taksi Şoförü',
|
||||||
description:
|
description:
|
||||||
'Vietnam savaşının izlerinin etkisindeki bir asker ve New York sokakları. Travis Bickle, geceleri taksi şoförlüğü yaptığı New York’ta bir yandan da gündelik yaşama ayak uydurmaya çalışır. Çürümeye yüz tutmuş bir topluma karşı tutulan bir ayna niteliğindeki film, yönetmen Martin Scorsese’nin kariyerinin en önemli filmlerinden biri olarak kabul görür.',
|
'Vietnam savaşının izlerinin etkisindeki bir asker ve New York sokakları. Travis Bickle, geceleri taksi şoförlüğü yaptığı New York’ta bir yandan da gündelik yaşama ayak uydurmaya çalışır. Çürümeye yüz tutmuş bir topluma karşı tutulan bir ayna niteliğindeki film, yönetmen Martin Scorsese’nin kariyerinin en önemli filmlerinden biri olarak kabul görür.',
|
||||||
category: ['Sinema', 'Genel']
|
category: ['Sinema', 'Genel']
|
||||||
})
|
})
|
||||||
expect(results[10]).toMatchObject({
|
expect(results[10]).toMatchObject({
|
||||||
start: '2025-01-13T19:00:00.000Z',
|
start: '2025-01-13T19:00:00.000Z',
|
||||||
stop: '2025-01-13T21:00:00.000Z',
|
stop: '2025-01-13T21:00:00.000Z',
|
||||||
title: 'Senin Adın',
|
title: 'Senin Adın',
|
||||||
description:
|
description:
|
||||||
'Dağların sardığı bir bölgede yaşayan Mitsuha, hayatından çok da memnun olmayan liseli bir kızdır. Babası vali olarak çalışmakta ve seçim kampanyaları ile uğraşmaktadır. Evde kendisi, kardeşi ve büyükannesi dışında kimse yoktur. Kırsal kesimdeki yaşamı onu bunaltmaktadır ve esas isteği Tokyo\'nun muhteşem şehir hayatının bir parçası olmaktır. Diğer tarafta ise Taki vardır.',
|
'Dağların sardığı bir bölgede yaşayan Mitsuha, hayatından çok da memnun olmayan liseli bir kızdır. Babası vali olarak çalışmakta ve seçim kampanyaları ile uğraşmaktadır. Evde kendisi, kardeşi ve büyükannesi dışında kimse yoktur. Kırsal kesimdeki yaşamı onu bunaltmaktadır ve esas isteği Tokyo\'nun muhteşem şehir hayatının bir parçası olmaktır. Diğer tarafta ise Taki vardır.',
|
||||||
category: ['Sinema', 'Genel']
|
category: ['Sinema', 'Genel']
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', async () => {
|
it('can handle empty guide', async () => {
|
||||||
const results = await parser({
|
const results = await parser({
|
||||||
channel,
|
channel,
|
||||||
date,
|
date,
|
||||||
content: fs.readFileSync(path.join(__dirname, '__data__', 'no_content.json')).toString(),
|
content: fs.readFileSync(path.join(__dirname, '__data__', 'no_content.json')).toString(),
|
||||||
useCache: false
|
useCache: false
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,111 +1,111 @@
|
|||||||
const { parser, url } = require('./dstv.com.config.js')
|
const { parser, url } = require('./dstv.com.config.js')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
jest.mock('axios')
|
jest.mock('axios')
|
||||||
|
|
||||||
const API_ENDPOINT = 'https://www.dstv.com/umbraco/api/TvGuide'
|
const API_ENDPOINT = 'https://www.dstv.com/umbraco/api/TvGuide'
|
||||||
|
|
||||||
const date = dayjs.utc('2022-11-22', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2022-11-22', 'YYYY-MM-DD').startOf('d')
|
||||||
const channelZA = {
|
const channelZA = {
|
||||||
site_id: 'zaf#201',
|
site_id: 'zaf#201',
|
||||||
xmltv_id: 'SuperSportGrandstand.za'
|
xmltv_id: 'SuperSportGrandstand.za'
|
||||||
}
|
}
|
||||||
const channelNG = {
|
const channelNG = {
|
||||||
site_id: 'nga#201',
|
site_id: 'nga#201',
|
||||||
xmltv_id: 'SuperSportGrandstand.za'
|
xmltv_id: 'SuperSportGrandstand.za'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url for zaf', () => {
|
it('can generate valid url for zaf', () => {
|
||||||
expect(url({ channel: channelZA, date })).toBe(
|
expect(url({ channel: channelZA, date })).toBe(
|
||||||
`${API_ENDPOINT}/GetProgrammes?d=2022-11-22&country=zaf`
|
`${API_ENDPOINT}/GetProgrammes?d=2022-11-22&country=zaf`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid url for nga', () => {
|
it('can generate valid url for nga', () => {
|
||||||
expect(url({ channel: channelNG, date })).toBe(
|
expect(url({ channel: channelNG, date })).toBe(
|
||||||
`${API_ENDPOINT}/GetProgrammes?d=2022-11-22&package=DStv%20Premium&country=nga`
|
`${API_ENDPOINT}/GetProgrammes?d=2022-11-22&package=DStv%20Premium&country=nga`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response for ZA', async () => {
|
it('can parse response for ZA', async () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content_zaf.json'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content_zaf.json'))
|
||||||
|
|
||||||
axios.get.mockImplementation(url => {
|
axios.get.mockImplementation(url => {
|
||||||
if (url === `${API_ENDPOINT}/GetProgramme?id=8b237235-aa17-4bb8-9ea6-097e7a813336`) {
|
if (url === `${API_ENDPOINT}/GetProgramme?id=8b237235-aa17-4bb8-9ea6-097e7a813336`) {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/program_zaf.json')))
|
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/program_zaf.json')))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve({ data: '' })
|
return Promise.resolve({ data: '' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let results = await parser({ content, channel: channelZA })
|
let results = await parser({ content, channel: channelZA })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[1]).toMatchObject({
|
expect(results[1]).toMatchObject({
|
||||||
start: '2022-11-21T23:00:00.000Z',
|
start: '2022-11-21T23:00:00.000Z',
|
||||||
stop: '2022-11-22T00:00:00.000Z',
|
stop: '2022-11-22T00:00:00.000Z',
|
||||||
title: 'UFC FN HL: Nzechukwu v Cutelaba',
|
title: 'UFC FN HL: Nzechukwu v Cutelaba',
|
||||||
description:
|
description:
|
||||||
"'UFC Fight Night Highlights - Heavyweight Bout: Kennedy Nzechukwu vs Ion Cutelaba'. From The UFC APEX Center - Las Vegas, USA.",
|
"'UFC Fight Night Highlights - Heavyweight Bout: Kennedy Nzechukwu vs Ion Cutelaba'. From The UFC APEX Center - Las Vegas, USA.",
|
||||||
image:
|
image:
|
||||||
'https://03mcdecdnimagerepository.blob.core.windows.net/epguideimage/img/271546_UFC Fight Night.png',
|
'https://03mcdecdnimagerepository.blob.core.windows.net/epguideimage/img/271546_UFC Fight Night.png',
|
||||||
category: ['All Sport', 'Mixed Martial Arts']
|
category: ['All Sport', 'Mixed Martial Arts']
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response for NG', async () => {
|
it('can parse response for NG', async () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content_nga.json'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content_nga.json'))
|
||||||
|
|
||||||
axios.get.mockImplementation(url => {
|
axios.get.mockImplementation(url => {
|
||||||
if (url === `${API_ENDPOINT}/GetProgramme?id=6d58931e-2192-486a-a202-14720136d204`) {
|
if (url === `${API_ENDPOINT}/GetProgramme?id=6d58931e-2192-486a-a202-14720136d204`) {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/program_nga.json')))
|
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/program_nga.json')))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve({ data: '' })
|
return Promise.resolve({ data: '' })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let results = await parser({ content, channel: channelNG })
|
let results = await parser({ content, channel: channelNG })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2022-11-21T23:00:00.000Z',
|
start: '2022-11-21T23:00:00.000Z',
|
||||||
stop: '2022-11-22T00:00:00.000Z',
|
stop: '2022-11-22T00:00:00.000Z',
|
||||||
title: 'UFC FN HL: Nzechukwu v Cutelaba',
|
title: 'UFC FN HL: Nzechukwu v Cutelaba',
|
||||||
description:
|
description:
|
||||||
"'UFC Fight Night Highlights - Heavyweight Bout: Kennedy Nzechukwu vs Ion Cutelaba'. From The UFC APEX Center - Las Vegas, USA.",
|
"'UFC Fight Night Highlights - Heavyweight Bout: Kennedy Nzechukwu vs Ion Cutelaba'. From The UFC APEX Center - Las Vegas, USA.",
|
||||||
image:
|
image:
|
||||||
'https://03mcdecdnimagerepository.blob.core.windows.net/epguideimage/img/271546_UFC Fight Night.png',
|
'https://03mcdecdnimagerepository.blob.core.windows.net/epguideimage/img/271546_UFC Fight Night.png',
|
||||||
category: ['All Sport', 'Mixed Martial Arts']
|
category: ['All Sport', 'Mixed Martial Arts']
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', done => {
|
it('can handle empty guide', done => {
|
||||||
parser({
|
parser({
|
||||||
content: '{"Total":0,"Channels":[]}',
|
content: '{"Total":0,"Channels":[]}',
|
||||||
channel: channelZA
|
channel: channelZA
|
||||||
})
|
})
|
||||||
.then(result => {
|
.then(result => {
|
||||||
expect(result).toMatchObject([])
|
expect(result).toMatchObject([])
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
.catch(done)
|
.catch(done)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,90 +1,90 @@
|
|||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'dtv8.net',
|
site: 'dtv8.net',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ date }) {
|
url({ date }) {
|
||||||
const day = date.format('dddd')
|
const day = date.format('dddd')
|
||||||
|
|
||||||
return `https://dtv8.net/tv-listings/${day.toLowerCase()}/`
|
return `https://dtv8.net/tv-listings/${day.toLowerCase()}/`
|
||||||
},
|
},
|
||||||
parser({ content, date }) {
|
parser({ content, date }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
|
|
||||||
const items = parseItems(content)
|
const items = parseItems(content)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const $item = cheerio.load(item)
|
const $item = cheerio.load(item)
|
||||||
let prev = programs[programs.length - 1]
|
let prev = programs[programs.length - 1]
|
||||||
let start = parseStart($item, date)
|
let start = parseStart($item, date)
|
||||||
if (prev) {
|
if (prev) {
|
||||||
if (start < prev.start) {
|
if (start < prev.start) {
|
||||||
start = start.add(1, 'd')
|
start = start.add(1, 'd')
|
||||||
date = date.add(1, 'd')
|
date = date.add(1, 'd')
|
||||||
}
|
}
|
||||||
prev.stop = start
|
prev.stop = start
|
||||||
}
|
}
|
||||||
const stop = start.add(30, 'm')
|
const stop = start.add(30, 'm')
|
||||||
|
|
||||||
programs.push({
|
programs.push({
|
||||||
title: parseTitle($item),
|
title: parseTitle($item),
|
||||||
description: parseDescription($item),
|
description: parseDescription($item),
|
||||||
image: parseImage($item),
|
image: parseImage($item),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
channels() {
|
channels() {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTitle($item) {
|
function parseTitle($item) {
|
||||||
return $item(
|
return $item(
|
||||||
'td:nth-child(2) > strong:nth-child(1),td:nth-child(2) > span > strong,td:nth-child(2) > span > b'
|
'td:nth-child(2) > strong:nth-child(1),td:nth-child(2) > span > strong,td:nth-child(2) > span > b'
|
||||||
).text()
|
).text()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDescription($item) {
|
function parseDescription($item) {
|
||||||
return (
|
return (
|
||||||
$item(
|
$item(
|
||||||
'td:nth-child(2) > strong:nth-child(3) > span,td:nth-child(2) > p:nth-child(3) > strong > span'
|
'td:nth-child(2) > strong:nth-child(3) > span,td:nth-child(2) > p:nth-child(3) > strong > span'
|
||||||
).text() || null
|
).text() || null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseImage($item) {
|
function parseImage($item) {
|
||||||
return $item('td:nth-child(1) > img.size-full').attr('src') || null
|
return $item('td:nth-child(1) > img.size-full').attr('src') || null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart($item, date) {
|
function parseStart($item, date) {
|
||||||
const time = $item('td:nth-child(1)').text()
|
const time = $item('td:nth-child(1)').text()
|
||||||
|
|
||||||
return dayjs.tz(
|
return dayjs.tz(
|
||||||
`${date.format('YYYY-MM-DD')} ${time}`,
|
`${date.format('YYYY-MM-DD')} ${time}`,
|
||||||
'YYYY-MM-DD HH:mm [hrs.]',
|
'YYYY-MM-DD HH:mm [hrs.]',
|
||||||
'America/Guyana'
|
'America/Guyana'
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content) {
|
function parseItems(content) {
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
|
|
||||||
return $('table tr')
|
return $('table tr')
|
||||||
.filter((i, el) => {
|
.filter((i, el) => {
|
||||||
const firstColumn = $(el).find('td').text()
|
const firstColumn = $(el).find('td').text()
|
||||||
|
|
||||||
return Boolean(firstColumn) && !firstColumn.includes('Time')
|
return Boolean(firstColumn) && !firstColumn.includes('Time')
|
||||||
})
|
})
|
||||||
.toArray()
|
.toArray()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +1,79 @@
|
|||||||
const { parser, url } = require('./dtv8.net.config.js')
|
const { parser, url } = require('./dtv8.net.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2025-02-21', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-02-21', 'YYYY-MM-DD').startOf('d')
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ date })).toBe('https://dtv8.net/tv-listings/friday/')
|
expect(url({ date })).toBe('https://dtv8.net/tv-listings/friday/')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response for friday', () => {
|
it('can parse response for friday', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content_fri.html'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content_fri.html'))
|
||||||
|
|
||||||
let results = parser({ content, date })
|
let results = parser({ content, date })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
|
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(18)
|
expect(results.length).toBe(18)
|
||||||
expect(results[9]).toMatchObject({
|
expect(results[9]).toMatchObject({
|
||||||
title: 'Smallville',
|
title: 'Smallville',
|
||||||
image: 'http://dtv8.net/wp-content/uploads/71P0aShCBXL._SL1300_.jpg',
|
image: 'http://dtv8.net/wp-content/uploads/71P0aShCBXL._SL1300_.jpg',
|
||||||
description:
|
description:
|
||||||
'A young Clark Kent struggles to find his place in the world as he learns to harness his alien powers for good and deals with the typical troubles of teenage life in Smallville, Kansas.',
|
'A young Clark Kent struggles to find his place in the world as he learns to harness his alien powers for good and deals with the typical troubles of teenage life in Smallville, Kansas.',
|
||||||
start: '2025-02-21T21:00:00.000Z',
|
start: '2025-02-21T21:00:00.000Z',
|
||||||
stop: '2025-02-21T22:00:00.000Z'
|
stop: '2025-02-21T22:00:00.000Z'
|
||||||
})
|
})
|
||||||
expect(results[15]).toMatchObject({
|
expect(results[15]).toMatchObject({
|
||||||
title: 'Law & Order',
|
title: 'Law & Order',
|
||||||
image: null,
|
image: null,
|
||||||
description:
|
description:
|
||||||
'In God We Trust: A young lawyer with a secret past is found dead; Price and Baxter debate the pros and cons of prison as a punishment versus alternative justice options.',
|
'In God We Trust: A young lawyer with a secret past is found dead; Price and Baxter debate the pros and cons of prison as a punishment versus alternative justice options.',
|
||||||
start: '2025-02-22T01:45:00.000Z',
|
start: '2025-02-22T01:45:00.000Z',
|
||||||
stop: '2025-02-22T02:30:00.000Z'
|
stop: '2025-02-22T02:30:00.000Z'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response for saturday', () => {
|
it('can parse response for saturday', () => {
|
||||||
const date = dayjs.utc('2025-02-22', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-02-22', 'YYYY-MM-DD').startOf('d')
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content_sat.html'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content_sat.html'))
|
||||||
|
|
||||||
let results = parser({ content, date })
|
let results = parser({ content, date })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
|
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(11)
|
expect(results.length).toBe(11)
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
title: 'Sign On',
|
title: 'Sign On',
|
||||||
image: null,
|
image: null,
|
||||||
description: null,
|
description: null,
|
||||||
start: '2025-02-22T13:55:00.000Z',
|
start: '2025-02-22T13:55:00.000Z',
|
||||||
stop: '2025-02-22T14:00:00.000Z'
|
stop: '2025-02-22T14:00:00.000Z'
|
||||||
})
|
})
|
||||||
expect(results[10]).toMatchObject({
|
expect(results[10]).toMatchObject({
|
||||||
title: 'Sign Off',
|
title: 'Sign Off',
|
||||||
image: null,
|
image: null,
|
||||||
description: null,
|
description: null,
|
||||||
start: '2025-02-23T04:00:00.000Z',
|
start: '2025-02-23T04:00:00.000Z',
|
||||||
stop: '2025-02-23T04:30:00.000Z'
|
stop: '2025-02-23T04:30:00.000Z'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const results = parser({ content: '' })
|
const results = parser({ content: '' })
|
||||||
|
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,149 +1,149 @@
|
|||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
require('dayjs/locale/ar')
|
require('dayjs/locale/ar')
|
||||||
|
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
'User-Agent':
|
'User-Agent':
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 OPR/115.0.0.0' }
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 OPR/115.0.0.0' }
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'elcinema.com',
|
site: 'elcinema.com',
|
||||||
days: 2,
|
days: 2,
|
||||||
request: { headers },
|
request: { headers },
|
||||||
url({ channel }) {
|
url({ channel }) {
|
||||||
const lang = channel.lang === 'en' ? 'en/' : '/'
|
const lang = channel.lang === 'en' ? 'en/' : '/'
|
||||||
|
|
||||||
return `https://elcinema.com/${lang}tvguide/${channel.site_id}/`
|
return `https://elcinema.com/${lang}tvguide/${channel.site_id}/`
|
||||||
},
|
},
|
||||||
parser({ content, channel, date }) {
|
parser({ content, channel, date }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
const items = parseItems(content, channel, date)
|
const items = parseItems(content, channel, date)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const start = parseStart(item, date)
|
const start = parseStart(item, date)
|
||||||
const duration = parseDuration(item)
|
const duration = parseDuration(item)
|
||||||
const stop = start.add(duration, 'm')
|
const stop = start.add(duration, 'm')
|
||||||
programs.push({
|
programs.push({
|
||||||
title: parseTitle(item),
|
title: parseTitle(item),
|
||||||
description: parseDescription(item),
|
description: parseDescription(item),
|
||||||
category: parseCategory(item),
|
category: parseCategory(item),
|
||||||
image: parseImage(item),
|
image: parseImage(item),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels({ lang }) {
|
async channels({ lang }) {
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(`https://elcinema.com/${lang}/tvguide/`, {
|
.get(`https://elcinema.com/${lang}/tvguide/`, {
|
||||||
headers: headers
|
headers: headers
|
||||||
})
|
})
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
const $ = cheerio.load(data)
|
const $ = cheerio.load(data)
|
||||||
|
|
||||||
return $('.tv-line')
|
return $('.tv-line')
|
||||||
.map((i, el) => {
|
.map((i, el) => {
|
||||||
const link = $(el).find('.channel > div > div.hide-for-small-only > a')
|
const link = $(el).find('.channel > div > div.hide-for-small-only > a')
|
||||||
const name = $(link).text()
|
const name = $(link).text()
|
||||||
const href = $(link).attr('href')
|
const href = $(link).attr('href')
|
||||||
const [, site_id] = href.match(/\/(\d+)\/$/)
|
const [, site_id] = href.match(/\/(\d+)\/$/)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
lang,
|
lang,
|
||||||
site_id,
|
site_id,
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.get()
|
.get()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseImage(item) {
|
function parseImage(item) {
|
||||||
const $ = cheerio.load(item)
|
const $ = cheerio.load(item)
|
||||||
const imgSrc =
|
const imgSrc =
|
||||||
$('.row > div.columns.small-3.large-1 > a > img').data('src') ||
|
$('.row > div.columns.small-3.large-1 > a > img').data('src') ||
|
||||||
$('.row > div.columns.small-5.large-1 > img').data('src')
|
$('.row > div.columns.small-5.large-1 > img').data('src')
|
||||||
|
|
||||||
return imgSrc || null
|
return imgSrc || null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseCategory(item) {
|
function parseCategory(item) {
|
||||||
const $ = cheerio.load(item)
|
const $ = cheerio.load(item)
|
||||||
const category = $('.row > div.columns.small-6.large-3 > ul > li:nth-child(2)').text()
|
const category = $('.row > div.columns.small-6.large-3 > ul > li:nth-child(2)').text()
|
||||||
|
|
||||||
return category.replace(/\(\d+\)/, '').trim() || null
|
return category.replace(/\(\d+\)/, '').trim() || null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDuration(item) {
|
function parseDuration(item) {
|
||||||
const $ = cheerio.load(item)
|
const $ = cheerio.load(item)
|
||||||
const duration =
|
const duration =
|
||||||
$('.row > div.columns.small-3.large-2 > ul > li:nth-child(2) > span').text() ||
|
$('.row > div.columns.small-3.large-2 > ul > li:nth-child(2) > span').text() ||
|
||||||
$('.row > div.columns.small-7.large-11 > ul > li:nth-child(2) > span').text()
|
$('.row > div.columns.small-7.large-11 > ul > li:nth-child(2) > span').text()
|
||||||
|
|
||||||
return duration.replace(/\D/g, '') || ''
|
return duration.replace(/\D/g, '') || ''
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart(item, initDate) {
|
function parseStart(item, initDate) {
|
||||||
const $ = cheerio.load(item)
|
const $ = cheerio.load(item)
|
||||||
let time =
|
let time =
|
||||||
$('.row > div.columns.small-3.large-2 > ul > li:nth-child(1)').text() ||
|
$('.row > div.columns.small-3.large-2 > ul > li:nth-child(1)').text() ||
|
||||||
$('.row > div.columns.small-7.large-11 > ul > li:nth-child(2)').text() ||
|
$('.row > div.columns.small-7.large-11 > ul > li:nth-child(2)').text() ||
|
||||||
''
|
''
|
||||||
|
|
||||||
time = time
|
time = time
|
||||||
.replace(/\[.*\]/, '')
|
.replace(/\[.*\]/, '')
|
||||||
.replace('مساءً', 'PM')
|
.replace('مساءً', 'PM')
|
||||||
.replace('صباحًا', 'AM')
|
.replace('صباحًا', 'AM')
|
||||||
.trim()
|
.trim()
|
||||||
|
|
||||||
time = `${initDate.format('YYYY-MM-DD')} ${time}`
|
time = `${initDate.format('YYYY-MM-DD')} ${time}`
|
||||||
|
|
||||||
return dayjs.tz(time, 'YYYY-MM-DD hh:mm A', dayjs.tz.guess())
|
return dayjs.tz(time, 'YYYY-MM-DD hh:mm A', dayjs.tz.guess())
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTitle(item) {
|
function parseTitle(item) {
|
||||||
const $ = cheerio.load(item)
|
const $ = cheerio.load(item)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
$('.row > div.columns.small-6.large-3 > ul > li:nth-child(1) > a').text() ||
|
$('.row > div.columns.small-6.large-3 > ul > li:nth-child(1) > a').text() ||
|
||||||
$('.row > div.columns.small-7.large-11 > ul > li:nth-child(1)').text() ||
|
$('.row > div.columns.small-7.large-11 > ul > li:nth-child(1)').text() ||
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDescription(item) {
|
function parseDescription(item) {
|
||||||
const $ = cheerio.load(item)
|
const $ = cheerio.load(item)
|
||||||
const excerpt = $('.row > div.columns.small-12.large-6 > ul > li:nth-child(3)').text() || ''
|
const excerpt = $('.row > div.columns.small-12.large-6 > ul > li:nth-child(3)').text() || ''
|
||||||
|
|
||||||
return excerpt.replace('...اقرأ المزيد', '').replace('...Read more', '')
|
return excerpt.replace('...اقرأ المزيد', '').replace('...Read more', '')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, channel, date) {
|
function parseItems(content, channel, date) {
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
|
|
||||||
const dateString = date.locale(channel.lang).format('dddd D')
|
const dateString = date.locale(channel.lang).format('dddd D')
|
||||||
|
|
||||||
const list = $('.dates')
|
const list = $('.dates')
|
||||||
.filter((i, el) => {
|
.filter((i, el) => {
|
||||||
let parsedDateString = $(el).text().trim()
|
let parsedDateString = $(el).text().trim()
|
||||||
parsedDateString = parsedDateString.replace(/\s\s+/g, ' ')
|
parsedDateString = parsedDateString.replace(/\s\s+/g, ' ')
|
||||||
|
|
||||||
return parsedDateString.includes(dateString)
|
return parsedDateString.includes(dateString)
|
||||||
})
|
})
|
||||||
.first()
|
.first()
|
||||||
.parent()
|
.parent()
|
||||||
.next()
|
.next()
|
||||||
|
|
||||||
return $('.padded-half', list).toArray()
|
return $('.padded-half', list).toArray()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,69 +1,69 @@
|
|||||||
const { parser, url } = require('./elcinema.com.config.js')
|
const { parser, url } = require('./elcinema.com.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2022-08-28', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2022-08-28', 'YYYY-MM-DD').startOf('d')
|
||||||
const channelAR = {
|
const channelAR = {
|
||||||
lang: 'ar',
|
lang: 'ar',
|
||||||
site_id: '1254',
|
site_id: '1254',
|
||||||
xmltv_id: 'OSNSeries.ae'
|
xmltv_id: 'OSNSeries.ae'
|
||||||
}
|
}
|
||||||
const channelEN = {
|
const channelEN = {
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
site_id: '1254',
|
site_id: '1254',
|
||||||
xmltv_id: 'OSNSeries.ae'
|
xmltv_id: 'OSNSeries.ae'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ channel: channelEN })).toBe('https://elcinema.com/en/tvguide/1254/')
|
expect(url({ channel: channelEN })).toBe('https://elcinema.com/en/tvguide/1254/')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response (en)', () => {
|
it('can parse response (en)', () => {
|
||||||
const contentEN = fs.readFileSync(path.resolve(__dirname, '__data__/content.en.html'))
|
const contentEN = fs.readFileSync(path.resolve(__dirname, '__data__/content.en.html'))
|
||||||
const results = parser({ date, channel: channelEN, content: contentEN }).map(p => {
|
const results = parser({ date, channel: channelEN, content: contentEN }).map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2022-08-27T14:25:00.000Z',
|
start: '2022-08-27T14:25:00.000Z',
|
||||||
stop: '2022-08-27T15:15:00.000Z',
|
stop: '2022-08-27T15:15:00.000Z',
|
||||||
title: 'Station 19 S5',
|
title: 'Station 19 S5',
|
||||||
image:
|
image:
|
||||||
'https://media.elcinema.com/uploads/_150x200_ec30d1a2251c8edf83334be4860184c74d2534d7ba508a334ad66fa59acc4926.jpg',
|
'https://media.elcinema.com/uploads/_150x200_ec30d1a2251c8edf83334be4860184c74d2534d7ba508a334ad66fa59acc4926.jpg',
|
||||||
category: 'Series'
|
category: 'Series'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response (ar)', () => {
|
it('can parse response (ar)', () => {
|
||||||
const contentAR = fs.readFileSync(path.resolve(__dirname, '__data__/content.ar.html'))
|
const contentAR = fs.readFileSync(path.resolve(__dirname, '__data__/content.ar.html'))
|
||||||
const results = parser({ date, channel: channelAR, content: contentAR }).map(p => {
|
const results = parser({ date, channel: channelAR, content: contentAR }).map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2022-08-27T14:25:00.000Z',
|
start: '2022-08-27T14:25:00.000Z',
|
||||||
stop: '2022-08-27T15:15:00.000Z',
|
stop: '2022-08-27T15:15:00.000Z',
|
||||||
title: 'Station 19 S5',
|
title: 'Station 19 S5',
|
||||||
image:
|
image:
|
||||||
'https://media.elcinema.com/uploads/_150x200_ec30d1a2251c8edf83334be4860184c74d2534d7ba508a334ad66fa59acc4926.jpg',
|
'https://media.elcinema.com/uploads/_150x200_ec30d1a2251c8edf83334be4860184c74d2534d7ba508a334ad66fa59acc4926.jpg',
|
||||||
category: 'مسلسل'
|
category: 'مسلسل'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const result = parser({
|
const result = parser({
|
||||||
date,
|
date,
|
||||||
channel: channelEN,
|
channel: channelEN,
|
||||||
content: '<!DOCTYPE html><html lang="ar" dir="rtl"><head></head><body></body></html>'
|
content: '<!DOCTYPE html><html lang="ar" dir="rtl"><head></head><body></body></html>'
|
||||||
})
|
})
|
||||||
expect(result).toMatchObject([])
|
expect(result).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,68 +1,68 @@
|
|||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'ena.skylifetv.co.kr',
|
site: 'ena.skylifetv.co.kr',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ channel, date }) {
|
url({ channel, date }) {
|
||||||
return `http://ena.skylifetv.co.kr/${channel.site_id}/?day=${date.format('YYYYMMDD')}&sc_dvsn=U`
|
return `http://ena.skylifetv.co.kr/${channel.site_id}/?day=${date.format('YYYYMMDD')}&sc_dvsn=U`
|
||||||
},
|
},
|
||||||
parser({ content, date }) {
|
parser({ content, date }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
const items = parseItems(content, date)
|
const items = parseItems(content, date)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const $item = cheerio.load(item)
|
const $item = cheerio.load(item)
|
||||||
const start = parseStart($item, date)
|
const start = parseStart($item, date)
|
||||||
const duration = parseDuration($item)
|
const duration = parseDuration($item)
|
||||||
const stop = start.add(duration, 'm')
|
const stop = start.add(duration, 'm')
|
||||||
programs.push({
|
programs.push({
|
||||||
title: parseTitle($item),
|
title: parseTitle($item),
|
||||||
rating: parseRating($item),
|
rating: parseRating($item),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTitle($item) {
|
function parseTitle($item) {
|
||||||
return $item('.col2 > .tit').text().trim()
|
return $item('.col2 > .tit').text().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseRating($item) {
|
function parseRating($item) {
|
||||||
const rating = $item('.col4').text().trim()
|
const rating = $item('.col4').text().trim()
|
||||||
|
|
||||||
return rating
|
return rating
|
||||||
? {
|
? {
|
||||||
system: 'KMRB',
|
system: 'KMRB',
|
||||||
value: rating
|
value: rating
|
||||||
}
|
}
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDuration($item) {
|
function parseDuration($item) {
|
||||||
const duration = $item('.col5').text().trim()
|
const duration = $item('.col5').text().trim()
|
||||||
|
|
||||||
return duration ? parseInt(duration) : 30
|
return duration ? parseInt(duration) : 30
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart($item, date) {
|
function parseStart($item, date) {
|
||||||
const time = $item('.col1').text().trim()
|
const time = $item('.col1').text().trim()
|
||||||
|
|
||||||
return dayjs.tz(`${date.format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm', 'Asia/Seoul')
|
return dayjs.tz(`${date.format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm', 'Asia/Seoul')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content) {
|
function parseItems(content) {
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
|
|
||||||
return $('.tbl_schedule > tbody > tr').toArray()
|
return $('.tbl_schedule > tbody > tr').toArray()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +1,57 @@
|
|||||||
const { parser, url } = require('./ena.skylifetv.co.kr.config.js')
|
const { parser, url } = require('./ena.skylifetv.co.kr.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2023-01-27', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2023-01-27', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: 'ENA',
|
site_id: 'ENA',
|
||||||
xmltv_id: 'ENA.kr'
|
xmltv_id: 'ENA.kr'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ channel, date })).toBe('http://ena.skylifetv.co.kr/ENA/?day=20230127&sc_dvsn=U')
|
expect(url({ channel, date })).toBe('http://ena.skylifetv.co.kr/ENA/?day=20230127&sc_dvsn=U')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'), 'utf8')
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'), 'utf8')
|
||||||
let results = parser({ content, date })
|
let results = parser({ content, date })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2023-01-26T16:05:00.000Z',
|
start: '2023-01-26T16:05:00.000Z',
|
||||||
stop: '2023-01-26T17:20:00.000Z',
|
stop: '2023-01-26T17:20:00.000Z',
|
||||||
title: '법쩐 6화',
|
title: '법쩐 6화',
|
||||||
rating: {
|
rating: {
|
||||||
system: 'KMRB',
|
system: 'KMRB',
|
||||||
value: '15'
|
value: '15'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[17]).toMatchObject({
|
expect(results[17]).toMatchObject({
|
||||||
start: '2023-01-27T14:10:00.000Z',
|
start: '2023-01-27T14:10:00.000Z',
|
||||||
stop: '2023-01-27T15:25:00.000Z',
|
stop: '2023-01-27T15:25:00.000Z',
|
||||||
title: '남이 될 수 있을까 4화',
|
title: '남이 될 수 있을까 4화',
|
||||||
rating: {
|
rating: {
|
||||||
system: 'KMRB',
|
system: 'KMRB',
|
||||||
value: '15'
|
value: '15'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const results = parser({
|
const results = parser({
|
||||||
date,
|
date,
|
||||||
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.html'), 'utf8')
|
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.html'), 'utf8')
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
const parser = require('epg-parser')
|
const parser = require('epg-parser')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'energeek.cl',
|
site: 'energeek.cl',
|
||||||
days: 2,
|
days: 2,
|
||||||
request: {
|
request: {
|
||||||
cache: {
|
cache: {
|
||||||
ttl: 60 * 60 * 1000 // 1 hour
|
ttl: 60 * 60 * 1000 // 1 hour
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
url: 'https://raw.githubusercontent.com/luisms123/tdt/master/guiaenergeek.xml',
|
url: 'https://raw.githubusercontent.com/luisms123/tdt/master/guiaenergeek.xml',
|
||||||
parser: function ({ content, channel, date }) {
|
parser: function ({ content, channel, date }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
const items = parseItems(content, channel, date)
|
const items = parseItems(content, channel, date)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title?.[0]?.value,
|
title: item.title?.[0]?.value,
|
||||||
description: item.desc?.[0]?.value,
|
description: item.desc?.[0]?.value,
|
||||||
icon: item.icon?.[0]?.src,
|
icon: item.icon?.[0]?.src,
|
||||||
start: item.start,
|
start: item.start,
|
||||||
stop: item.stop
|
stop: item.stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, channel, date) {
|
function parseItems(content, channel, date) {
|
||||||
const { programs } = parser.parse(content)
|
const { programs } = parser.parse(content)
|
||||||
|
|
||||||
return programs.filter(p => p.channel === channel.site_id && date.isSame(p.start, 'day'))
|
return programs.filter(p => p.channel === channel.site_id && date.isSame(p.start, 'day'))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,37 @@
|
|||||||
const { parser, url } = require('./energeek.cl.config.js')
|
const { parser, url } = require('./energeek.cl.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2022-11-29', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2022-11-29', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: 'EnerGeek Retro',
|
site_id: 'EnerGeek Retro',
|
||||||
xmltv_id: 'EnerGeekRetro.cl'
|
xmltv_id: 'EnerGeekRetro.cl'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url).toBe('https://raw.githubusercontent.com/luisms123/tdt/master/guiaenergeek.xml')
|
expect(url).toBe('https://raw.githubusercontent.com/luisms123/tdt/master/guiaenergeek.xml')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.xml'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.xml'))
|
||||||
let results = parser({ content, channel, date })
|
let results = parser({ content, channel, date })
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2022-11-29T03:00:00.000Z',
|
start: '2022-11-29T03:00:00.000Z',
|
||||||
stop: '2022-11-29T03:30:00.000Z',
|
stop: '2022-11-29T03:30:00.000Z',
|
||||||
title: 'Noir',
|
title: 'Noir',
|
||||||
description:
|
description:
|
||||||
'Kirika Yuumura es una adolescente japonesa que no recuerda nada de su pasado, salvo la palabra NOIR, por lo que decidirá contactar con Mireille Bouquet, una asesina profesional para que la ayude a investigar. Ambas forman un equipo muy eficiente, que resuelve un trabajo tras otro con gran éxito, hasta que aparece un grupo conocido como "Les Soldats", relacionados con el pasado de Kirika. Estos tratarán de eliminar a las dos chicas, antes de que indaguen más hondo sobre la verdad acerca de Noir',
|
'Kirika Yuumura es una adolescente japonesa que no recuerda nada de su pasado, salvo la palabra NOIR, por lo que decidirá contactar con Mireille Bouquet, una asesina profesional para que la ayude a investigar. Ambas forman un equipo muy eficiente, que resuelve un trabajo tras otro con gran éxito, hasta que aparece un grupo conocido como "Les Soldats", relacionados con el pasado de Kirika. Estos tratarán de eliminar a las dos chicas, antes de que indaguen más hondo sobre la verdad acerca de Noir',
|
||||||
icon: 'https://pics.filmaffinity.com/nowaru_noir_tv_series-225888552-mmed.jpg'
|
icon: 'https://pics.filmaffinity.com/nowaru_noir_tv_series-225888552-mmed.jpg'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const result = parser({ content: '', channel, date })
|
const result = parser({ content: '', channel, date })
|
||||||
expect(result).toMatchObject([])
|
expect(result).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,96 +1,96 @@
|
|||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const { DateTime } = require('luxon')
|
const { DateTime } = require('luxon')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'entertainment.ie',
|
site: 'entertainment.ie',
|
||||||
days: 2,
|
days: 2,
|
||||||
url: function ({ date, channel }) {
|
url: function ({ date, channel }) {
|
||||||
return `https://entertainment.ie/tv/${channel.site_id}/?date=${date.format(
|
return `https://entertainment.ie/tv/${channel.site_id}/?date=${date.format(
|
||||||
'DD-MM-YYYY'
|
'DD-MM-YYYY'
|
||||||
)}&time=all-day`
|
)}&time=all-day`
|
||||||
},
|
},
|
||||||
parser: function ({ content, date }) {
|
parser: function ({ content, date }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
const items = parseItems(content)
|
const items = parseItems(content)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const prev = programs[programs.length - 1]
|
const prev = programs[programs.length - 1]
|
||||||
const $item = cheerio.load(item)
|
const $item = cheerio.load(item)
|
||||||
let start = parseStart($item, date)
|
let start = parseStart($item, date)
|
||||||
if (!start) return
|
if (!start) return
|
||||||
if (prev && start < prev.start) {
|
if (prev && start < prev.start) {
|
||||||
start = start.plus({ days: 1 })
|
start = start.plus({ days: 1 })
|
||||||
}
|
}
|
||||||
const duration = parseDuration($item)
|
const duration = parseDuration($item)
|
||||||
const stop = start.plus({ minutes: duration })
|
const stop = start.plus({ minutes: duration })
|
||||||
programs.push({
|
programs.push({
|
||||||
title: parseTitle($item),
|
title: parseTitle($item),
|
||||||
description: parseDescription($item),
|
description: parseDescription($item),
|
||||||
categories: parseCategories($item),
|
categories: parseCategories($item),
|
||||||
image: parseImage($item),
|
image: parseImage($item),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get('https://entertainment.ie/tv/all-channels/')
|
.get('https://entertainment.ie/tv/all-channels/')
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
const $ = cheerio.load(data)
|
const $ = cheerio.load(data)
|
||||||
let channels = $('.tv-filter-container > tv-filter').attr(':channels')
|
let channels = $('.tv-filter-container > tv-filter').attr(':channels')
|
||||||
channels = JSON.parse(channels)
|
channels = JSON.parse(channels)
|
||||||
|
|
||||||
return channels.map(c => {
|
return channels.map(c => {
|
||||||
return {
|
return {
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
site_id: c.slug,
|
site_id: c.slug,
|
||||||
name: c.name
|
name: c.name
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseImage($item) {
|
function parseImage($item) {
|
||||||
return $item('.text-holder > .btn-hold > .btn-wrap > a.btn-share').data('img')
|
return $item('.text-holder > .btn-hold > .btn-wrap > a.btn-share').data('img')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTitle($item) {
|
function parseTitle($item) {
|
||||||
return $item('.text-holder h3').text().trim()
|
return $item('.text-holder h3').text().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDescription($item) {
|
function parseDescription($item) {
|
||||||
return $item('.text-holder > .btn-hold > .btn-wrap > a.btn-share').data('description')
|
return $item('.text-holder > .btn-hold > .btn-wrap > a.btn-share').data('description')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseCategories($item) {
|
function parseCategories($item) {
|
||||||
const genres = $item('.text-holder > .btn-hold > .btn-wrap > a.btn-share').data('genres')
|
const genres = $item('.text-holder > .btn-hold > .btn-wrap > a.btn-share').data('genres')
|
||||||
|
|
||||||
return genres ? genres.split(', ') : []
|
return genres ? genres.split(', ') : []
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart($item, date) {
|
function parseStart($item, date) {
|
||||||
let d = $item('.text-holder > .btn-hold > .btn-wrap > a.btn-share').data('time')
|
let d = $item('.text-holder > .btn-hold > .btn-wrap > a.btn-share').data('time')
|
||||||
let [, time] = d ? d.split(', ') : [null, null]
|
let [, time] = d ? d.split(', ') : [null, null]
|
||||||
|
|
||||||
return time
|
return time
|
||||||
? DateTime.fromFormat(`${date.format('YYYY-MM-DD')} ${time}`, 'yyyy-MM-dd HH:mm', {
|
? DateTime.fromFormat(`${date.format('YYYY-MM-DD')} ${time}`, 'yyyy-MM-dd HH:mm', {
|
||||||
zone: 'UTC'
|
zone: 'UTC'
|
||||||
}).toUTC()
|
}).toUTC()
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDuration($item) {
|
function parseDuration($item) {
|
||||||
const duration = $item('.text-holder > .btn-hold > .btn-wrap > a.btn-share').data('duration')
|
const duration = $item('.text-holder > .btn-hold > .btn-wrap > a.btn-share').data('duration')
|
||||||
|
|
||||||
return parseInt(duration)
|
return parseInt(duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content) {
|
function parseItems(content) {
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
|
|
||||||
return $('.info-list > li').toArray()
|
return $('.info-list > li').toArray()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,58 +1,58 @@
|
|||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const { parser, url } = require('./entertainment.ie.config.js')
|
const { parser, url } = require('./entertainment.ie.config.js')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2023-06-29', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2023-06-29', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = { site_id: 'rte2', xmltv_id: 'RTE2.ie' }
|
const channel = { site_id: 'rte2', xmltv_id: 'RTE2.ie' }
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ date, channel })).toBe(
|
expect(url({ date, channel })).toBe(
|
||||||
'https://entertainment.ie/tv/rte2/?date=29-06-2023&time=all-day'
|
'https://entertainment.ie/tv/rte2/?date=29-06-2023&time=all-day'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
||||||
const results = parser({ date, content }).map(p => {
|
const results = parser({ date, content }).map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(51)
|
expect(results.length).toBe(51)
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2023-06-29T06:00:00.000Z',
|
start: '2023-06-29T06:00:00.000Z',
|
||||||
stop: '2023-06-29T08:00:00.000Z',
|
stop: '2023-06-29T08:00:00.000Z',
|
||||||
title: 'EuroNews',
|
title: 'EuroNews',
|
||||||
description: 'European and international headlines live via satellite',
|
description: 'European and international headlines live via satellite',
|
||||||
image:
|
image:
|
||||||
'https://img.resized.co/entertainment/eyJkYXRhIjoie1widXJsXCI6XCJodHRwczpcXFwvXFxcL3R2LmFzc2V0cy5wcmVzc2Fzc29jaWF0aW9uLmlvXFxcLzcxZDdkYWY2LWQxMjItNTliYy1iMGRjLTFkMjc2ODg1MzhkNC5qcGdcIixcIndpZHRoXCI6NDgwLFwiaGVpZ2h0XCI6Mjg4LFwiZGVmYXVsdFwiOlwiaHR0cHM6XFxcL1xcXC9lbnRlcnRhaW5tZW50LmllXFxcL2ltYWdlc1xcXC9uby1pbWFnZS5wbmdcIn0iLCJoYXNoIjoiZDhjYzA0NzFhMGZhOTI1Yjc5ODI0M2E3OWZjMGI2ZGJmMDIxMjllNyJ9/71d7daf6-d122-59bc-b0dc-1d27688538d4.jpg',
|
'https://img.resized.co/entertainment/eyJkYXRhIjoie1widXJsXCI6XCJodHRwczpcXFwvXFxcL3R2LmFzc2V0cy5wcmVzc2Fzc29jaWF0aW9uLmlvXFxcLzcxZDdkYWY2LWQxMjItNTliYy1iMGRjLTFkMjc2ODg1MzhkNC5qcGdcIixcIndpZHRoXCI6NDgwLFwiaGVpZ2h0XCI6Mjg4LFwiZGVmYXVsdFwiOlwiaHR0cHM6XFxcL1xcXC9lbnRlcnRhaW5tZW50LmllXFxcL2ltYWdlc1xcXC9uby1pbWFnZS5wbmdcIn0iLCJoYXNoIjoiZDhjYzA0NzFhMGZhOTI1Yjc5ODI0M2E3OWZjMGI2ZGJmMDIxMjllNyJ9/71d7daf6-d122-59bc-b0dc-1d27688538d4.jpg',
|
||||||
categories: ['Factual']
|
categories: ['Factual']
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[50]).toMatchObject({
|
expect(results[50]).toMatchObject({
|
||||||
start: '2023-06-30T02:25:00.000Z',
|
start: '2023-06-30T02:25:00.000Z',
|
||||||
stop: '2023-06-30T06:00:00.000Z',
|
stop: '2023-06-30T06:00:00.000Z',
|
||||||
title: 'EuroNews',
|
title: 'EuroNews',
|
||||||
description: 'European and international headlines live via satellite',
|
description: 'European and international headlines live via satellite',
|
||||||
image:
|
image:
|
||||||
'https://img.resized.co/entertainment/eyJkYXRhIjoie1widXJsXCI6XCJodHRwczpcXFwvXFxcL3R2LmFzc2V0cy5wcmVzc2Fzc29jaWF0aW9uLmlvXFxcLzcxZDdkYWY2LWQxMjItNTliYy1iMGRjLTFkMjc2ODg1MzhkNC5qcGdcIixcIndpZHRoXCI6NDgwLFwiaGVpZ2h0XCI6Mjg4LFwiZGVmYXVsdFwiOlwiaHR0cHM6XFxcL1xcXC9lbnRlcnRhaW5tZW50LmllXFxcL2ltYWdlc1xcXC9uby1pbWFnZS5wbmdcIn0iLCJoYXNoIjoiZDhjYzA0NzFhMGZhOTI1Yjc5ODI0M2E3OWZjMGI2ZGJmMDIxMjllNyJ9/71d7daf6-d122-59bc-b0dc-1d27688538d4.jpg',
|
'https://img.resized.co/entertainment/eyJkYXRhIjoie1widXJsXCI6XCJodHRwczpcXFwvXFxcL3R2LmFzc2V0cy5wcmVzc2Fzc29jaWF0aW9uLmlvXFxcLzcxZDdkYWY2LWQxMjItNTliYy1iMGRjLTFkMjc2ODg1MzhkNC5qcGdcIixcIndpZHRoXCI6NDgwLFwiaGVpZ2h0XCI6Mjg4LFwiZGVmYXVsdFwiOlwiaHR0cHM6XFxcL1xcXC9lbnRlcnRhaW5tZW50LmllXFxcL2ltYWdlc1xcXC9uby1pbWFnZS5wbmdcIn0iLCJoYXNoIjoiZDhjYzA0NzFhMGZhOTI1Yjc5ODI0M2E3OWZjMGI2ZGJmMDIxMjllNyJ9/71d7daf6-d122-59bc-b0dc-1d27688538d4.jpg',
|
||||||
categories: ['Factual']
|
categories: ['Factual']
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/no-content.html'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/no-content.html'))
|
||||||
const result = parser({
|
const result = parser({
|
||||||
date,
|
date,
|
||||||
channel,
|
channel,
|
||||||
content
|
content
|
||||||
})
|
})
|
||||||
expect(result).toMatchObject([])
|
expect(result).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,45 +1,45 @@
|
|||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const parser = require('epg-parser')
|
const parser = require('epg-parser')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'epg.112114.xyz',
|
site: 'epg.112114.xyz',
|
||||||
days: 1,
|
days: 1,
|
||||||
url: 'https://epg.112114.xyz/pp.xml',
|
url: 'https://epg.112114.xyz/pp.xml',
|
||||||
request: {
|
request: {
|
||||||
cache: {
|
cache: {
|
||||||
ttl: 24 * 60 * 60 * 1000 // 1 day
|
ttl: 24 * 60 * 60 * 1000 // 1 day
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
parser: function ({ content, channel, date }) {
|
parser: function ({ content, channel, date }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
const items = parseItems(content, channel, date)
|
const items = parseItems(content, channel, date)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title?.[0]?.value,
|
title: item.title?.[0]?.value,
|
||||||
start: item.start,
|
start: item.start,
|
||||||
stop: item.stop
|
stop: item.stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get('https://epg.112114.xyz/pp.xml')
|
.get('https://epg.112114.xyz/pp.xml')
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
const { channels } = parser.parse(data)
|
const { channels } = parser.parse(data)
|
||||||
|
|
||||||
return channels.map(channel => ({
|
return channels.map(channel => ({
|
||||||
lang: 'zh',
|
lang: 'zh',
|
||||||
site_id: channel.id,
|
site_id: channel.id,
|
||||||
name: channel.displayName[0].value
|
name: channel.displayName[0].value
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, channel, date) {
|
function parseItems(content, channel, date) {
|
||||||
const { programs } = parser.parse(content)
|
const { programs } = parser.parse(content)
|
||||||
|
|
||||||
return programs.filter(p => p.channel === channel.site_id && date.isSame(p.start, 'day'))
|
return programs.filter(p => p.channel === channel.site_id && date.isSame(p.start, 'day'))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +1,42 @@
|
|||||||
const { parser, url } = require('./epg.112114.xyz.config.js')
|
const { parser, url } = require('./epg.112114.xyz.config.js')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
|
|
||||||
const date = dayjs.utc('2025-01-11', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-01-11', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = { site_id: 'BTV文艺', xmltv_id: 'BRTVArtsChannel.cn', lang: 'zh' }
|
const channel = { site_id: 'BTV文艺', xmltv_id: 'BRTVArtsChannel.cn', lang: 'zh' }
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url).toBe('https://epg.112114.xyz/pp.xml')
|
expect(url).toBe('https://epg.112114.xyz/pp.xml')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.xml'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.xml'))
|
||||||
const results = parser({ date, content, channel })
|
const results = parser({ date, content, channel })
|
||||||
|
|
||||||
expect(results.length).toBe(28)
|
expect(results.length).toBe(28)
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2025-01-11T00:07:00.000Z',
|
start: '2025-01-11T00:07:00.000Z',
|
||||||
stop: '2025-01-11T00:24:00.000Z',
|
stop: '2025-01-11T00:24:00.000Z',
|
||||||
title: '每日文艺播报'
|
title: '每日文艺播报'
|
||||||
})
|
})
|
||||||
expect(results[27]).toMatchObject({
|
expect(results[27]).toMatchObject({
|
||||||
start: '2025-01-11T15:16:00.000Z',
|
start: '2025-01-11T15:16:00.000Z',
|
||||||
stop: '2025-01-11T15:59:00.000Z',
|
stop: '2025-01-11T15:59:00.000Z',
|
||||||
title: '笑动剧场'
|
title: '笑动剧场'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const result = parser({
|
const result = parser({
|
||||||
date,
|
date,
|
||||||
channel,
|
channel,
|
||||||
content: ''
|
content: ''
|
||||||
})
|
})
|
||||||
expect(result).toMatchObject([])
|
expect(result).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,64 +1,64 @@
|
|||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const iconv = require('iconv-lite')
|
const iconv = require('iconv-lite')
|
||||||
const parser = require('epg-parser')
|
const parser = require('epg-parser')
|
||||||
const { ungzip } = require('pako')
|
const { ungzip } = require('pako')
|
||||||
|
|
||||||
let cachedContent
|
let cachedContent
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'epg.iptvx.one',
|
site: 'epg.iptvx.one',
|
||||||
days: 2,
|
days: 2,
|
||||||
url: 'https://iptvx.one/epg/epg_noarch.xml.gz',
|
url: 'https://iptvx.one/epg/epg_noarch.xml.gz',
|
||||||
request: {
|
request: {
|
||||||
maxContentLength: 500000000, // 500 MB
|
maxContentLength: 500000000, // 500 MB
|
||||||
cache: {
|
cache: {
|
||||||
ttl: 24 * 60 * 60 * 1000 // 1 day
|
ttl: 24 * 60 * 60 * 1000 // 1 day
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
parser: function ({ buffer, channel, date, cached }) {
|
parser: function ({ buffer, channel, date, cached }) {
|
||||||
if (!cached) cachedContent = undefined
|
if (!cached) cachedContent = undefined
|
||||||
|
|
||||||
let programs = []
|
let programs = []
|
||||||
const items = parseItems(buffer, channel, date)
|
const items = parseItems(buffer, channel, date)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title?.[0]?.value,
|
title: item.title?.[0]?.value,
|
||||||
description: item.desc?.[0]?.value,
|
description: item.desc?.[0]?.value,
|
||||||
start: item.start,
|
start: item.start,
|
||||||
stop: item.stop
|
stop: item.stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get('https://epg.iptvx.one/api/channels.json')
|
.get('https://epg.iptvx.one/api/channels.json')
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
return data.channels.map(channel => {
|
return data.channels.map(channel => {
|
||||||
const [name] = channel.chan_names.split(' • ')
|
const [name] = channel.chan_names.split(' • ')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
lang: 'ru',
|
lang: 'ru',
|
||||||
site_id: channel.chan_id,
|
site_id: channel.chan_id,
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(buffer, channel, date) {
|
function parseItems(buffer, channel, date) {
|
||||||
if (!buffer) return []
|
if (!buffer) return []
|
||||||
|
|
||||||
if (!cachedContent) {
|
if (!cachedContent) {
|
||||||
const content = ungzip(buffer)
|
const content = ungzip(buffer)
|
||||||
const encoded = iconv.decode(content, 'utf8')
|
const encoded = iconv.decode(content, 'utf8')
|
||||||
cachedContent = parser.parse(encoded)
|
cachedContent = parser.parse(encoded)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { programs } = cachedContent
|
const { programs } = cachedContent
|
||||||
|
|
||||||
return programs.filter(p => p.channel === channel.site_id && date.isSame(p.start, 'day'))
|
return programs.filter(p => p.channel === channel.site_id && date.isSame(p.start, 'day'))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +1,46 @@
|
|||||||
const { parser, url } = require('./epg.iptvx.one.config.js')
|
const { parser, url } = require('./epg.iptvx.one.config.js')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
|
|
||||||
const date = dayjs.utc('2025-01-13', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-01-13', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = { site_id: '12-omsk', xmltv_id: 'Channel12.ru' }
|
const channel = { site_id: '12-omsk', xmltv_id: 'Channel12.ru' }
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url).toBe('https://iptvx.one/epg/epg_noarch.xml.gz')
|
expect(url).toBe('https://iptvx.one/epg/epg_noarch.xml.gz')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const buffer = fs.readFileSync(path.resolve(__dirname, '__data__/content.xml.gz'))
|
const buffer = fs.readFileSync(path.resolve(__dirname, '__data__/content.xml.gz'))
|
||||||
const results = parser({ date, buffer, channel })
|
const results = parser({ date, buffer, channel })
|
||||||
|
|
||||||
expect(results.length).toBe(29)
|
expect(results.length).toBe(29)
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2025-01-13T00:00:00.000Z',
|
start: '2025-01-13T00:00:00.000Z',
|
||||||
stop: '2025-01-13T00:55:00.000Z',
|
stop: '2025-01-13T00:55:00.000Z',
|
||||||
title: 'Акценты недели',
|
title: 'Акценты недели',
|
||||||
description:
|
description:
|
||||||
'Программа расскажет зрителям о том, как развивались самые яркие события недели, поможет расставить акценты над самыми обсуждаемыми новостями. Россия, ток-шоу'
|
'Программа расскажет зрителям о том, как развивались самые яркие события недели, поможет расставить акценты над самыми обсуждаемыми новостями. Россия, ток-шоу'
|
||||||
})
|
})
|
||||||
expect(results[28]).toMatchObject({
|
expect(results[28]).toMatchObject({
|
||||||
start: '2025-01-13T22:15:00.000Z',
|
start: '2025-01-13T22:15:00.000Z',
|
||||||
stop: '2025-01-14T00:00:00.000Z',
|
stop: '2025-01-14T00:00:00.000Z',
|
||||||
title: 'д/с Необыкновенные люди',
|
title: 'д/с Необыкновенные люди',
|
||||||
description:
|
description:
|
||||||
'Герои цикла – врачи, спортсмены, представители творческих профессий, волонтеры и многие-многие другие. Их деятельность связана с жизнью особенных людей. Россия, док. сериал'
|
'Герои цикла – врачи, спортсмены, представители творческих профессий, волонтеры и многие-многие другие. Их деятельность связана с жизнью особенных людей. Россия, док. сериал'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const result = parser({
|
const result = parser({
|
||||||
date,
|
date,
|
||||||
channel,
|
channel,
|
||||||
buffer: ''
|
buffer: ''
|
||||||
})
|
})
|
||||||
expect(result).toMatchObject([])
|
expect(result).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,100 +1,100 @@
|
|||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
|
|
||||||
const BASIC_TOKEN =
|
const BASIC_TOKEN =
|
||||||
'MjdlMTFmNWUtODhlMi00OGU0LWJkNDItOGUxNWFiYmM2NmY1OjEyejJzMXJ3bXdhZmsxMGNkdzl0cjloOWFjYjZwdjJoZDhscXZ0aGc='
|
'MjdlMTFmNWUtODhlMi00OGU0LWJkNDItOGUxNWFiYmM2NmY1OjEyejJzMXJ3bXdhZmsxMGNkdzl0cjloOWFjYjZwdjJoZDhscXZ0aGc='
|
||||||
|
|
||||||
let session
|
let session
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'epg.telemach.ba',
|
site: 'epg.telemach.ba',
|
||||||
days: 3,
|
days: 3,
|
||||||
url({ channel, date }) {
|
url({ channel, date }) {
|
||||||
return `https://api-web.ug-be.cdn.united.cloud/v1/public/events/epg?fromTime=${date.format(
|
return `https://api-web.ug-be.cdn.united.cloud/v1/public/events/epg?fromTime=${date.format(
|
||||||
'YYYY-MM-DDTHH:mm:ss-00:00'
|
'YYYY-MM-DDTHH:mm:ss-00:00'
|
||||||
)}&toTime=${date
|
)}&toTime=${date
|
||||||
.add(1, 'days')
|
.add(1, 'days')
|
||||||
.subtract(1, 's')
|
.subtract(1, 's')
|
||||||
.format('YYYY-MM-DDTHH:mm:ss-00:00')}&communityId=12&languageId=59&cid=${channel.site_id}`
|
.format('YYYY-MM-DDTHH:mm:ss-00:00')}&communityId=12&languageId=59&cid=${channel.site_id}`
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
async headers() {
|
async headers() {
|
||||||
if (!session) {
|
if (!session) {
|
||||||
session = await loadSessionDetails()
|
session = await loadSessionDetails()
|
||||||
if (!session || !session.access_token) return null
|
if (!session || !session.access_token) return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Authorization: `Bearer ${session.access_token}`
|
Authorization: `Bearer ${session.access_token}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
parser({ content }) {
|
parser({ content }) {
|
||||||
try {
|
try {
|
||||||
const programs = []
|
const programs = []
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
for (const channelId in data) {
|
for (const channelId in data) {
|
||||||
if (Array.isArray(data[channelId])) {
|
if (Array.isArray(data[channelId])) {
|
||||||
data[channelId].forEach(item => {
|
data[channelId].forEach(item => {
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
description: item.shortDescription,
|
description: item.shortDescription,
|
||||||
image: parseImage(item),
|
image: parseImage(item),
|
||||||
season: item.seasonNumber,
|
season: item.seasonNumber,
|
||||||
episode: item.episodeNumber,
|
episode: item.episodeNumber,
|
||||||
start: dayjs(item.startTime),
|
start: dayjs(item.startTime),
|
||||||
stop: dayjs(item.endTime)
|
stop: dayjs(item.endTime)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
} catch {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const session = await loadSessionDetails()
|
const session = await loadSessionDetails()
|
||||||
if (!session || !session.access_token) return null
|
if (!session || !session.access_token) return null
|
||||||
|
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(
|
.get(
|
||||||
'https://api-web.ug-be.cdn.united.cloud/v1/public/channels?channelType=TV&communityId=12&languageId=59&imageSize=L',
|
'https://api-web.ug-be.cdn.united.cloud/v1/public/channels?channelType=TV&communityId=12&languageId=59&imageSize=L',
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${session.access_token}`
|
Authorization: `Bearer ${session.access_token}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
return data.map(item => ({
|
return data.map(item => ({
|
||||||
lang: 'hr',
|
lang: 'hr',
|
||||||
site_id: item.id,
|
site_id: item.id,
|
||||||
name: item.name
|
name: item.name
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseImage(item) {
|
function parseImage(item) {
|
||||||
const baseURL = 'https://images-web.ug-be.cdn.united.cloud'
|
const baseURL = 'https://images-web.ug-be.cdn.united.cloud'
|
||||||
|
|
||||||
return Array.isArray(item?.images) && item.images[0] ? `${baseURL}${item.images[0].path}` : null
|
return Array.isArray(item?.images) && item.images[0] ? `${baseURL}${item.images[0].path}` : null
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadSessionDetails() {
|
function loadSessionDetails() {
|
||||||
return axios
|
return axios
|
||||||
.post(
|
.post(
|
||||||
'https://api-web.ug-be.cdn.united.cloud/oauth/token?grant_type=client_credentials',
|
'https://api-web.ug-be.cdn.united.cloud/oauth/token?grant_type=client_credentials',
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Basic ${BASIC_TOKEN}`
|
Authorization: `Basic ${BASIC_TOKEN}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,94 +1,94 @@
|
|||||||
const { parser, url, request } = require('./epg.telemach.ba.config.js')
|
const { parser, url, request } = require('./epg.telemach.ba.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
jest.mock('axios')
|
jest.mock('axios')
|
||||||
|
|
||||||
axios.post.mockImplementation((url, data, opts) => {
|
axios.post.mockImplementation((url, data, opts) => {
|
||||||
if (
|
if (
|
||||||
url === 'https://api-web.ug-be.cdn.united.cloud/oauth/token?grant_type=client_credentials' &&
|
url === 'https://api-web.ug-be.cdn.united.cloud/oauth/token?grant_type=client_credentials' &&
|
||||||
JSON.stringify(opts.headers) ===
|
JSON.stringify(opts.headers) ===
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
Authorization:
|
Authorization:
|
||||||
'Basic MjdlMTFmNWUtODhlMi00OGU0LWJkNDItOGUxNWFiYmM2NmY1OjEyejJzMXJ3bXdhZmsxMGNkdzl0cjloOWFjYjZwdjJoZDhscXZ0aGc='
|
'Basic MjdlMTFmNWUtODhlMi00OGU0LWJkNDItOGUxNWFiYmM2NmY1OjEyejJzMXJ3bXdhZmsxMGNkdzl0cjloOWFjYjZwdjJoZDhscXZ0aGc='
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/session.json')))
|
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/session.json')))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/no_session.json')))
|
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/no_session.json')))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const date = dayjs.utc('2025-01-20', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-01-20', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: '1607',
|
site_id: '1607',
|
||||||
xmltv_id: 'N1HD.hr'
|
xmltv_id: 'N1HD.hr'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', async () => {
|
it('can generate valid url', async () => {
|
||||||
const result = url({ date, channel })
|
const result = url({ date, channel })
|
||||||
|
|
||||||
expect(result).toBe(
|
expect(result).toBe(
|
||||||
'https://api-web.ug-be.cdn.united.cloud/v1/public/events/epg?fromTime=2025-01-20T00:00:00-00:00&toTime=2025-01-20T23:59:59-00:00&communityId=12&languageId=59&cid=1607'
|
'https://api-web.ug-be.cdn.united.cloud/v1/public/events/epg?fromTime=2025-01-20T00:00:00-00:00&toTime=2025-01-20T23:59:59-00:00&communityId=12&languageId=59&cid=1607'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request headers', async () => {
|
it('can generate valid request headers', async () => {
|
||||||
const result = await request.headers()
|
const result = await request.headers()
|
||||||
|
|
||||||
expect(result).toMatchObject({
|
expect(result).toMatchObject({
|
||||||
Authorization:
|
Authorization:
|
||||||
'Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidWMtaW5mby1zZXJ2aWNlIl0sInNjb3BlIjpbInJlYWQiXSwiZXhwIjoxNzM3Mzc3NDUxLCJhdXRob3JpdGllcyI6WyJST0xFX1BVQkxJQ19FUEciXSwianRpIjoiUVBubHdRSDczS1EwSnU0WDZwRTc2Zm5mUmRnIiwiY2xpZW50X2lkIjoiMjdlMTFmNWUtODhlMi00OGU0LWJkNDItOGUxNWFiYmM2NmY1In0.LqJAZUWEqIOcLrRSMpxZxnF-f1arKbHgfweLMXt-MBjCDbVJD39OQEsh_b68mtePAoa3n8LRbf3IFT40Ys5Vbe-k_Btm4a9gdEGr6cNi_4HGk4Bto6RUDvCp59VRfoRZhWe145Q2b5TS6szmC4Ws2YWIcZU5vrJcYs2GZiCk6U11MOcd1i52WmZj8cLPq0ZPDB_bzmTgYkvkVa7zOzUOPSl4M8T6fPUa__vVKUt0jOgtFoHeue2mQVgISC2puEGsBN0jJwvJ8PzM6IVxXrQno3MBv0VJy_qILiFPcxRePGRAmKLuEqagvikO7P_XQgFjZgg-j8u8wX2WwO0Yxft0Pg'
|
'Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidWMtaW5mby1zZXJ2aWNlIl0sInNjb3BlIjpbInJlYWQiXSwiZXhwIjoxNzM3Mzc3NDUxLCJhdXRob3JpdGllcyI6WyJST0xFX1BVQkxJQ19FUEciXSwianRpIjoiUVBubHdRSDczS1EwSnU0WDZwRTc2Zm5mUmRnIiwiY2xpZW50X2lkIjoiMjdlMTFmNWUtODhlMi00OGU0LWJkNDItOGUxNWFiYmM2NmY1In0.LqJAZUWEqIOcLrRSMpxZxnF-f1arKbHgfweLMXt-MBjCDbVJD39OQEsh_b68mtePAoa3n8LRbf3IFT40Ys5Vbe-k_Btm4a9gdEGr6cNi_4HGk4Bto6RUDvCp59VRfoRZhWe145Q2b5TS6szmC4Ws2YWIcZU5vrJcYs2GZiCk6U11MOcd1i52WmZj8cLPq0ZPDB_bzmTgYkvkVa7zOzUOPSl4M8T6fPUa__vVKUt0jOgtFoHeue2mQVgISC2puEGsBN0jJwvJ8PzM6IVxXrQno3MBv0VJy_qILiFPcxRePGRAmKLuEqagvikO7P_XQgFjZgg-j8u8wX2WwO0Yxft0Pg'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'), 'utf8')
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'), 'utf8')
|
||||||
let results = parser({ content })
|
let results = parser({ content })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(35)
|
expect(results.length).toBe(35)
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2025-01-20T00:00:00.000Z',
|
start: '2025-01-20T00:00:00.000Z',
|
||||||
stop: '2025-01-20T00:30:00.000Z',
|
stop: '2025-01-20T00:30:00.000Z',
|
||||||
title: 'DW Euromaxx',
|
title: 'DW Euromaxx',
|
||||||
description:
|
description:
|
||||||
'Euromaxx je lifestyle Europe magazine, koji nam donosi zanimljivosti iz evropskih gradova, priče o načinu života ljudi i upoznaje nas sa njihovim kulturama.',
|
'Euromaxx je lifestyle Europe magazine, koji nam donosi zanimljivosti iz evropskih gradova, priče o načinu života ljudi i upoznaje nas sa njihovim kulturama.',
|
||||||
image:
|
image:
|
||||||
'https://images-web.ug-be.cdn.united.cloud/2021/02/18/06/05/21/stb_xl_cd4f72e01d308ecce782e29b69af7de6707b9e85.jpg',
|
'https://images-web.ug-be.cdn.united.cloud/2021/02/18/06/05/21/stb_xl_cd4f72e01d308ecce782e29b69af7de6707b9e85.jpg',
|
||||||
season: null,
|
season: null,
|
||||||
episode: null
|
episode: null
|
||||||
})
|
})
|
||||||
expect(results[34]).toMatchObject({
|
expect(results[34]).toMatchObject({
|
||||||
start: '2025-01-20T23:50:00.000Z',
|
start: '2025-01-20T23:50:00.000Z',
|
||||||
stop: '2025-01-21T00:00:00.000Z',
|
stop: '2025-01-21T00:00:00.000Z',
|
||||||
title: 'DW Shift',
|
title: 'DW Shift',
|
||||||
description: 'Tjedni magazin koji nam donosi najnovije vijesti vezane za Internet.',
|
description: 'Tjedni magazin koji nam donosi najnovije vijesti vezane za Internet.',
|
||||||
image:
|
image:
|
||||||
'https://images-web.ug-be.cdn.united.cloud/2023/06/09/13/07/53/stb_xl_0849d5d70c1337651b85b6335e340e15bd5d6a73_340fc454bc73019d052cf936ebee5da3.jpg',
|
'https://images-web.ug-be.cdn.united.cloud/2023/06/09/13/07/53/stb_xl_0849d5d70c1337651b85b6335e340e15bd5d6a73_340fc454bc73019d052cf936ebee5da3.jpg',
|
||||||
season: null,
|
season: null,
|
||||||
episode: null
|
episode: null
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const results = parser({
|
const results = parser({
|
||||||
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.json'), 'utf8')
|
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.json'), 'utf8')
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,101 +1,101 @@
|
|||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
|
|
||||||
const BASIC_TOKEN =
|
const BASIC_TOKEN =
|
||||||
'MjdlMTFmNWUtODhlMi00OGU0LWJkNDItOGUxNWFiYmM2NmY1OjEyejJzMXJ3bXdhZmsxMGNkdzl0cjloOWFjYjZwdjJoZDhscXZ0aGc='
|
'MjdlMTFmNWUtODhlMi00OGU0LWJkNDItOGUxNWFiYmM2NmY1OjEyejJzMXJ3bXdhZmsxMGNkdzl0cjloOWFjYjZwdjJoZDhscXZ0aGc='
|
||||||
|
|
||||||
let session
|
let session
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'epg.telemach.me',
|
site: 'epg.telemach.me',
|
||||||
days: 3,
|
days: 3,
|
||||||
url({ channel, date }) {
|
url({ channel, date }) {
|
||||||
return `https://api-web.ug-be.cdn.united.cloud/v1/public/events/epg?fromTime=${date.format(
|
return `https://api-web.ug-be.cdn.united.cloud/v1/public/events/epg?fromTime=${date.format(
|
||||||
'YYYY-MM-DDTHH:mm:ss-00:00'
|
'YYYY-MM-DDTHH:mm:ss-00:00'
|
||||||
)}&toTime=${date
|
)}&toTime=${date
|
||||||
.add(1, 'days')
|
.add(1, 'days')
|
||||||
.subtract(1, 's')
|
.subtract(1, 's')
|
||||||
.format('YYYY-MM-DDTHH:mm:ss-00:00')}&communityId=5&languageId=10001&cid=${channel.site_id}`
|
.format('YYYY-MM-DDTHH:mm:ss-00:00')}&communityId=5&languageId=10001&cid=${channel.site_id}`
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
async headers() {
|
async headers() {
|
||||||
if (!session) {
|
if (!session) {
|
||||||
session = await loadSessionDetails()
|
session = await loadSessionDetails()
|
||||||
if (!session || !session.access_token) return null
|
if (!session || !session.access_token) return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Authorization: `Bearer ${session.access_token}`,
|
Authorization: `Bearer ${session.access_token}`,
|
||||||
Referer: 'https://epg.telemach.me/'
|
Referer: 'https://epg.telemach.me/'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
parser({ content }) {
|
parser({ content }) {
|
||||||
try {
|
try {
|
||||||
const programs = []
|
const programs = []
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
for (const channelId in data) {
|
for (const channelId in data) {
|
||||||
if (Array.isArray(data[channelId])) {
|
if (Array.isArray(data[channelId])) {
|
||||||
data[channelId].forEach(item => {
|
data[channelId].forEach(item => {
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
description: item.shortDescription,
|
description: item.shortDescription,
|
||||||
image: parseImage(item),
|
image: parseImage(item),
|
||||||
season: item.seasonNumber,
|
season: item.seasonNumber,
|
||||||
episode: item.episodeNumber,
|
episode: item.episodeNumber,
|
||||||
start: dayjs(item.startTime),
|
start: dayjs(item.startTime),
|
||||||
stop: dayjs(item.endTime)
|
stop: dayjs(item.endTime)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
} catch {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const session = await loadSessionDetails()
|
const session = await loadSessionDetails()
|
||||||
if (!session || !session.access_token) return null
|
if (!session || !session.access_token) return null
|
||||||
|
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(
|
.get(
|
||||||
'https://api-web.ug-be.cdn.united.cloud/v1/public/channels?channelType=TV&communityId=5&languageId=10001&imageSize=L',
|
'https://api-web.ug-be.cdn.united.cloud/v1/public/channels?channelType=TV&communityId=5&languageId=10001&imageSize=L',
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${session.access_token}`
|
Authorization: `Bearer ${session.access_token}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
return data.map(item => ({
|
return data.map(item => ({
|
||||||
lang: 'bs',
|
lang: 'bs',
|
||||||
site_id: item.id,
|
site_id: item.id,
|
||||||
name: item.name
|
name: item.name
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseImage(item) {
|
function parseImage(item) {
|
||||||
const baseURL = 'https://images-web.ug-be.cdn.united.cloud'
|
const baseURL = 'https://images-web.ug-be.cdn.united.cloud'
|
||||||
|
|
||||||
return Array.isArray(item?.images) && item.images[0] ? `${baseURL}${item.images[0].path}` : null
|
return Array.isArray(item?.images) && item.images[0] ? `${baseURL}${item.images[0].path}` : null
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadSessionDetails() {
|
function loadSessionDetails() {
|
||||||
return axios
|
return axios
|
||||||
.post(
|
.post(
|
||||||
'https://api-web.ug-be.cdn.united.cloud/oauth/token?grant_type=client_credentials',
|
'https://api-web.ug-be.cdn.united.cloud/oauth/token?grant_type=client_credentials',
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Basic ${BASIC_TOKEN}`
|
Authorization: `Basic ${BASIC_TOKEN}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,96 +1,96 @@
|
|||||||
const { parser, url, request } = require('./epg.telemach.me.config.js')
|
const { parser, url, request } = require('./epg.telemach.me.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
jest.mock('axios')
|
jest.mock('axios')
|
||||||
|
|
||||||
axios.post.mockImplementation((url, data, opts) => {
|
axios.post.mockImplementation((url, data, opts) => {
|
||||||
if (
|
if (
|
||||||
url === 'https://api-web.ug-be.cdn.united.cloud/oauth/token?grant_type=client_credentials' &&
|
url === 'https://api-web.ug-be.cdn.united.cloud/oauth/token?grant_type=client_credentials' &&
|
||||||
JSON.stringify(opts.headers) ===
|
JSON.stringify(opts.headers) ===
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
Authorization:
|
Authorization:
|
||||||
'Basic MjdlMTFmNWUtODhlMi00OGU0LWJkNDItOGUxNWFiYmM2NmY1OjEyejJzMXJ3bXdhZmsxMGNkdzl0cjloOWFjYjZwdjJoZDhscXZ0aGc='
|
'Basic MjdlMTFmNWUtODhlMi00OGU0LWJkNDItOGUxNWFiYmM2NmY1OjEyejJzMXJ3bXdhZmsxMGNkdzl0cjloOWFjYjZwdjJoZDhscXZ0aGc='
|
||||||
})
|
})
|
||||||
) {
|
) {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/session.json')))
|
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/session.json')))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/no_session.json')))
|
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/no_session.json')))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const date = dayjs.utc('2025-01-20', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-01-20', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: '92',
|
site_id: '92',
|
||||||
xmltv_id: 'PinkKids.rs'
|
xmltv_id: 'PinkKids.rs'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', async () => {
|
it('can generate valid url', async () => {
|
||||||
const result = url({ date, channel })
|
const result = url({ date, channel })
|
||||||
|
|
||||||
expect(result).toBe(
|
expect(result).toBe(
|
||||||
'https://api-web.ug-be.cdn.united.cloud/v1/public/events/epg?fromTime=2025-01-20T00:00:00-00:00&toTime=2025-01-20T23:59:59-00:00&communityId=5&languageId=10001&cid=92'
|
'https://api-web.ug-be.cdn.united.cloud/v1/public/events/epg?fromTime=2025-01-20T00:00:00-00:00&toTime=2025-01-20T23:59:59-00:00&communityId=5&languageId=10001&cid=92'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request headers', async () => {
|
it('can generate valid request headers', async () => {
|
||||||
const result = await request.headers()
|
const result = await request.headers()
|
||||||
|
|
||||||
expect(result).toMatchObject({
|
expect(result).toMatchObject({
|
||||||
Authorization:
|
Authorization:
|
||||||
'Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidWMtaW5mby1zZXJ2aWNlIl0sInNjb3BlIjpbInJlYWQiXSwiZXhwIjoxNzM3Mzc3NDUxLCJhdXRob3JpdGllcyI6WyJST0xFX1BVQkxJQ19FUEciXSwianRpIjoiUVBubHdRSDczS1EwSnU0WDZwRTc2Zm5mUmRnIiwiY2xpZW50X2lkIjoiMjdlMTFmNWUtODhlMi00OGU0LWJkNDItOGUxNWFiYmM2NmY1In0.LqJAZUWEqIOcLrRSMpxZxnF-f1arKbHgfweLMXt-MBjCDbVJD39OQEsh_b68mtePAoa3n8LRbf3IFT40Ys5Vbe-k_Btm4a9gdEGr6cNi_4HGk4Bto6RUDvCp59VRfoRZhWe145Q2b5TS6szmC4Ws2YWIcZU5vrJcYs2GZiCk6U11MOcd1i52WmZj8cLPq0ZPDB_bzmTgYkvkVa7zOzUOPSl4M8T6fPUa__vVKUt0jOgtFoHeue2mQVgISC2puEGsBN0jJwvJ8PzM6IVxXrQno3MBv0VJy_qILiFPcxRePGRAmKLuEqagvikO7P_XQgFjZgg-j8u8wX2WwO0Yxft0Pg',
|
'Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidWMtaW5mby1zZXJ2aWNlIl0sInNjb3BlIjpbInJlYWQiXSwiZXhwIjoxNzM3Mzc3NDUxLCJhdXRob3JpdGllcyI6WyJST0xFX1BVQkxJQ19FUEciXSwianRpIjoiUVBubHdRSDczS1EwSnU0WDZwRTc2Zm5mUmRnIiwiY2xpZW50X2lkIjoiMjdlMTFmNWUtODhlMi00OGU0LWJkNDItOGUxNWFiYmM2NmY1In0.LqJAZUWEqIOcLrRSMpxZxnF-f1arKbHgfweLMXt-MBjCDbVJD39OQEsh_b68mtePAoa3n8LRbf3IFT40Ys5Vbe-k_Btm4a9gdEGr6cNi_4HGk4Bto6RUDvCp59VRfoRZhWe145Q2b5TS6szmC4Ws2YWIcZU5vrJcYs2GZiCk6U11MOcd1i52WmZj8cLPq0ZPDB_bzmTgYkvkVa7zOzUOPSl4M8T6fPUa__vVKUt0jOgtFoHeue2mQVgISC2puEGsBN0jJwvJ8PzM6IVxXrQno3MBv0VJy_qILiFPcxRePGRAmKLuEqagvikO7P_XQgFjZgg-j8u8wX2WwO0Yxft0Pg',
|
||||||
Referer: 'https://epg.telemach.me/'
|
Referer: 'https://epg.telemach.me/'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'), 'utf8')
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'), 'utf8')
|
||||||
let results = parser({ content })
|
let results = parser({ content })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(55)
|
expect(results.length).toBe(55)
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2025-01-19T23:20:00.000Z',
|
start: '2025-01-19T23:20:00.000Z',
|
||||||
stop: '2025-01-20T00:10:00.000Z',
|
stop: '2025-01-20T00:10:00.000Z',
|
||||||
title: 'Pinkove Zvezdice',
|
title: 'Pinkove Zvezdice',
|
||||||
description:
|
description:
|
||||||
'Četvrta sezona najgledanijeg dečijeg muzičkog takmičenja, "Pinkove zvezdice" došlo do promena, pa će tako gledaoci imati priliku da najtalentovaniju decu gledaju na novoj, spektakularnoj sceni. Nova...',
|
'Četvrta sezona najgledanijeg dečijeg muzičkog takmičenja, "Pinkove zvezdice" došlo do promena, pa će tako gledaoci imati priliku da najtalentovaniju decu gledaju na novoj, spektakularnoj sceni. Nova...',
|
||||||
image:
|
image:
|
||||||
'https://images-web.ug-be.cdn.united.cloud/2023/06/22/11/19/19/stb_xl_115752ec1e05872b86ceda7726d347f533e17f43_340fc454bc73019d052cf936ebee5da3.jpg',
|
'https://images-web.ug-be.cdn.united.cloud/2023/06/22/11/19/19/stb_xl_115752ec1e05872b86ceda7726d347f533e17f43_340fc454bc73019d052cf936ebee5da3.jpg',
|
||||||
season: null,
|
season: null,
|
||||||
episode: null
|
episode: null
|
||||||
})
|
})
|
||||||
expect(results[54]).toMatchObject({
|
expect(results[54]).toMatchObject({
|
||||||
start: '2025-01-20T23:50:00.000Z',
|
start: '2025-01-20T23:50:00.000Z',
|
||||||
stop: '2025-01-21T00:10:00.000Z',
|
stop: '2025-01-21T00:10:00.000Z',
|
||||||
title: 'Hajdi',
|
title: 'Hajdi',
|
||||||
description:
|
description:
|
||||||
'Život nekada nije jednostavan. To najbolje zna Hajdi. Nakon što je ostala siroče, njena tetka je odvodi visoko u Alpe kod njenog dede. Ona uz nove prijatelje i dedu uskoro zavoli svoj novi život. Ipak...',
|
'Život nekada nije jednostavan. To najbolje zna Hajdi. Nakon što je ostala siroče, njena tetka je odvodi visoko u Alpe kod njenog dede. Ona uz nove prijatelje i dedu uskoro zavoli svoj novi život. Ipak...',
|
||||||
image:
|
image:
|
||||||
'https://images-web.ug-be.cdn.united.cloud/2024/05/10/14/49/09/stb_xl_7d1c73ee4df7de5c4157e9daccae098d50ee853d_99230e7f5bdc95451f37aa31f8425b4d.jpg',
|
'https://images-web.ug-be.cdn.united.cloud/2024/05/10/14/49/09/stb_xl_7d1c73ee4df7de5c4157e9daccae098d50ee853d_99230e7f5bdc95451f37aa31f8425b4d.jpg',
|
||||||
season: null,
|
season: null,
|
||||||
episode: null
|
episode: null
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const results = parser({
|
const results = parser({
|
||||||
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.json'), 'utf8')
|
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.json'), 'utf8')
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,45 +1,45 @@
|
|||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const TOKEN = '1610283054'
|
const TOKEN = '1610283054'
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'epgmaster.com',
|
site: 'epgmaster.com',
|
||||||
url({ channel }) {
|
url({ channel }) {
|
||||||
return `https://epgmaster.com/api/channels/${channel.site_id}/epgs?token=${TOKEN}`
|
return `https://epgmaster.com/api/channels/${channel.site_id}/epgs?token=${TOKEN}`
|
||||||
},
|
},
|
||||||
parser({ content, date }) {
|
parser({ content, date }) {
|
||||||
return parseItems(content, date).map(item => {
|
return parseItems(content, date).map(item => {
|
||||||
return {
|
return {
|
||||||
title: item.programName,
|
title: item.programName,
|
||||||
start: parseStart(item),
|
start: parseStart(item),
|
||||||
stop: parseStop(item)
|
stop: parseStop(item)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart(item) {
|
function parseStart(item) {
|
||||||
return dayjs.utc(`${item.startDate} ${item.startTime}`, 'YYYY-MM-DD HH:mm:ss')
|
return dayjs.utc(`${item.startDate} ${item.startTime}`, 'YYYY-MM-DD HH:mm:ss')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStop(item) {
|
function parseStop(item) {
|
||||||
return dayjs.utc(`${item.startDate} ${item.endTime}`, 'YYYY-MM-DD HH:mm:ss')
|
return dayjs.utc(`${item.startDate} ${item.endTime}`, 'YYYY-MM-DD HH:mm:ss')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, date) {
|
function parseItems(content, date) {
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
if (!data || !Array.isArray(data)) return []
|
if (!data || !Array.isArray(data)) return []
|
||||||
const filtered = data.find(group => date.format('YYYY-MM-DD') === group.date)
|
const filtered = data.find(group => date.format('YYYY-MM-DD') === group.date)
|
||||||
if (!filtered || !Array.isArray(filtered.epgTokenList)) return []
|
if (!filtered || !Array.isArray(filtered.epgTokenList)) return []
|
||||||
|
|
||||||
return filtered.epgTokenList
|
return filtered.epgTokenList
|
||||||
} catch {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,45 +1,45 @@
|
|||||||
const { parser, url } = require('./epgmaster.com.config.js')
|
const { parser, url } = require('./epgmaster.com.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2025-05-18', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-05-18', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = { site_id: 'ntv' }
|
const channel = { site_id: 'ntv' }
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ channel })).toBe('https://epgmaster.com/api/channels/ntv/epgs?token=1610283054')
|
expect(url({ channel })).toBe('https://epgmaster.com/api/channels/ntv/epgs?token=1610283054')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
||||||
|
|
||||||
let results = parser({ content, date })
|
let results = parser({ content, date })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
|
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(46)
|
expect(results.length).toBe(46)
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
title: 'Krishi Teleflim-Bharosa Yuwama',
|
title: 'Krishi Teleflim-Bharosa Yuwama',
|
||||||
start: '2025-05-18T00:00:00.000Z',
|
start: '2025-05-18T00:00:00.000Z',
|
||||||
stop: '2025-05-18T00:15:00.000Z'
|
stop: '2025-05-18T00:15:00.000Z'
|
||||||
})
|
})
|
||||||
expect(results[1]).toMatchObject({
|
expect(results[1]).toMatchObject({
|
||||||
title: 'News in Nepali [Rec.]',
|
title: 'News in Nepali [Rec.]',
|
||||||
start: '2025-05-18T00:15:00.000Z',
|
start: '2025-05-18T00:15:00.000Z',
|
||||||
stop: '2025-05-18T00:45:00.000Z'
|
stop: '2025-05-18T00:45:00.000Z'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const results = parser({ content: '', date })
|
const results = parser({ content: '', date })
|
||||||
|
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,75 +1,75 @@
|
|||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const iconv = require('iconv-lite')
|
const iconv = require('iconv-lite')
|
||||||
const parser = require('epg-parser')
|
const parser = require('epg-parser')
|
||||||
const { ungzip } = require('pako')
|
const { ungzip } = require('pako')
|
||||||
|
|
||||||
let cachedContent
|
let cachedContent
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'epgshare01.online',
|
site: 'epgshare01.online',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ channel }) {
|
url({ channel }) {
|
||||||
const [tag] = channel.site_id.split('#')
|
const [tag] = channel.site_id.split('#')
|
||||||
|
|
||||||
return `https://epgshare01.online/epgshare01/epg_ripper_${tag}.xml.gz`
|
return `https://epgshare01.online/epgshare01/epg_ripper_${tag}.xml.gz`
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
cache: {
|
cache: {
|
||||||
ttl: 24 * 60 * 60 * 1000 // 1 day
|
ttl: 24 * 60 * 60 * 1000 // 1 day
|
||||||
},
|
},
|
||||||
maxContentLength: 100000000 // 100 MB
|
maxContentLength: 100000000 // 100 MB
|
||||||
},
|
},
|
||||||
parser({ buffer, channel, date, cached }) {
|
parser({ buffer, channel, date, cached }) {
|
||||||
if (!cached) cachedContent = undefined
|
if (!cached) cachedContent = undefined
|
||||||
|
|
||||||
let programs = []
|
let programs = []
|
||||||
const items = parseItems(buffer, channel, date)
|
const items = parseItems(buffer, channel, date)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title?.[0]?.value,
|
title: item.title?.[0]?.value,
|
||||||
description: item.desc?.[0]?.value,
|
description: item.desc?.[0]?.value,
|
||||||
start: item.start,
|
start: item.start,
|
||||||
stop: item.stop
|
stop: item.stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels({ tag }) {
|
async channels({ tag }) {
|
||||||
const buffer = await axios
|
const buffer = await axios
|
||||||
.get(`https://epgshare01.online/epgshare01/epg_ripper_${tag}.xml.gz`, {
|
.get(`https://epgshare01.online/epgshare01/epg_ripper_${tag}.xml.gz`, {
|
||||||
responseType: 'arraybuffer'
|
responseType: 'arraybuffer'
|
||||||
})
|
})
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
const content = ungzip(buffer)
|
const content = ungzip(buffer)
|
||||||
const encoded = iconv.decode(content, 'utf8')
|
const encoded = iconv.decode(content, 'utf8')
|
||||||
const { channels } = parser.parse(encoded)
|
const { channels } = parser.parse(encoded)
|
||||||
|
|
||||||
return channels.map(channel => {
|
return channels.map(channel => {
|
||||||
const displayName = channel.displayName[0]
|
const displayName = channel.displayName[0]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
lang: displayName.lang || 'en',
|
lang: displayName.lang || 'en',
|
||||||
site_id: `${tag}#${channel.id}`,
|
site_id: `${tag}#${channel.id}`,
|
||||||
name: displayName.value
|
name: displayName.value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(buffer, channel, date) {
|
function parseItems(buffer, channel, date) {
|
||||||
if (!buffer) return []
|
if (!buffer) return []
|
||||||
|
|
||||||
if (!cachedContent) {
|
if (!cachedContent) {
|
||||||
const content = ungzip(buffer)
|
const content = ungzip(buffer)
|
||||||
const encoded = iconv.decode(content, 'utf8')
|
const encoded = iconv.decode(content, 'utf8')
|
||||||
cachedContent = parser.parse(encoded)
|
cachedContent = parser.parse(encoded)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { programs } = cachedContent
|
const { programs } = cachedContent
|
||||||
const [, channelId] = channel.site_id.split('#')
|
const [, channelId] = channel.site_id.split('#')
|
||||||
|
|
||||||
return programs.filter(p => p.channel === channelId && date.isSame(p.start, 'day'))
|
return programs.filter(p => p.channel === channelId && date.isSame(p.start, 'day'))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,43 @@
|
|||||||
const { parser, url } = require('./epgshare01.online.config.js')
|
const { parser, url } = require('./epgshare01.online.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2025-02-09', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-02-09', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = { site_id: 'ALJAZEERA1#AlJazeera.English.net' }
|
const channel = { site_id: 'ALJAZEERA1#AlJazeera.English.net' }
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ channel })).toBe('https://epgshare01.online/epgshare01/epg_ripper_ALJAZEERA1.xml.gz')
|
expect(url({ channel })).toBe('https://epgshare01.online/epgshare01/epg_ripper_ALJAZEERA1.xml.gz')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const buffer = fs.readFileSync(path.resolve(__dirname, '__data__/content.xml.gz'))
|
const buffer = fs.readFileSync(path.resolve(__dirname, '__data__/content.xml.gz'))
|
||||||
|
|
||||||
const results = parser({ buffer, channel, date, cached: false })
|
const results = parser({ buffer, channel, date, cached: false })
|
||||||
|
|
||||||
expect(results.length).toBe(40)
|
expect(results.length).toBe(40)
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
title: 'The Palestine Laboratory',
|
title: 'The Palestine Laboratory',
|
||||||
description:
|
description:
|
||||||
"Exposing how Israel's sales of military technology is aiding state control around the world.",
|
"Exposing how Israel's sales of military technology is aiding state control around the world.",
|
||||||
start: '2025-02-09T00:00:00.000Z',
|
start: '2025-02-09T00:00:00.000Z',
|
||||||
stop: '2025-02-09T01:00:00.000Z'
|
stop: '2025-02-09T01:00:00.000Z'
|
||||||
})
|
})
|
||||||
expect(results[39]).toMatchObject({
|
expect(results[39]).toMatchObject({
|
||||||
title: 'Inside Story',
|
title: 'Inside Story',
|
||||||
description:
|
description:
|
||||||
'Beyond the headlines to the heart of the news of the day. Al Jazeera gets the Inside Story from some of the best minds from around the globe.',
|
'Beyond the headlines to the heart of the news of the day. Al Jazeera gets the Inside Story from some of the best minds from around the globe.',
|
||||||
start: '2025-02-09T23:30:00.000Z',
|
start: '2025-02-09T23:30:00.000Z',
|
||||||
stop: '2025-02-10T00:00:00.000Z'
|
stop: '2025-02-10T00:00:00.000Z'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const results = parser({ content: '', channel, date, cached: false })
|
const results = parser({ content: '', channel, date, cached: false })
|
||||||
|
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,102 +1,102 @@
|
|||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
|
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'firstmedia.com',
|
site: 'firstmedia.com',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ channel, date }) {
|
url({ channel, date }) {
|
||||||
return `https://api.firstmedia.com/api/content/tv-guide/list?date=${date.format(
|
return `https://api.firstmedia.com/api/content/tv-guide/list?date=${date.format(
|
||||||
'DD/MM/YYYY'
|
'DD/MM/YYYY'
|
||||||
)}&channel=${channel.site_id}&startTime=1&endTime=24`
|
)}&channel=${channel.site_id}&startTime=1&endTime=24`
|
||||||
},
|
},
|
||||||
parser({ content, channel, date }) {
|
parser({ content, channel, date }) {
|
||||||
if (!content || !channel || !date) return []
|
if (!content || !channel || !date) return []
|
||||||
|
|
||||||
const programs = []
|
const programs = []
|
||||||
const items = parseItems(content, channel.site_id)
|
const items = parseItems(content, channel.site_id)
|
||||||
.map(item => {
|
.map(item => {
|
||||||
item.start = toDelta(item.date, item.startTime)
|
item.start = toDelta(item.date, item.startTime)
|
||||||
item.stop = toDelta(item.date, item.endTime)
|
item.stop = toDelta(item.date, item.endTime)
|
||||||
return item
|
return item
|
||||||
})
|
})
|
||||||
.sort((a, b) => a.start - b.start)
|
.sort((a, b) => a.start - b.start)
|
||||||
|
|
||||||
const dt = date.tz('Asia/Jakarta').startOf('d')
|
const dt = date.tz('Asia/Jakarta').startOf('d')
|
||||||
let lastStop
|
let lastStop
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
if (lastStop === undefined || item.start >= lastStop) {
|
if (lastStop === undefined || item.start >= lastStop) {
|
||||||
lastStop = item.stop
|
lastStop = item.stop
|
||||||
programs.push({
|
programs.push({
|
||||||
title: parseTitle(item),
|
title: parseTitle(item),
|
||||||
description: parseDescription(item),
|
description: parseDescription(item),
|
||||||
start: asDate(parseStart({ item, date: dt })),
|
start: asDate(parseStart({ item, date: dt })),
|
||||||
stop: asDate(parseStop({ item, date: dt }))
|
stop: asDate(parseStop({ item, date: dt }))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const result = await axios
|
const result = await axios
|
||||||
.get(
|
.get(
|
||||||
`https://api.firstmedia.com/api/content/tv-guide/list?date=${dayjs().format(
|
`https://api.firstmedia.com/api/content/tv-guide/list?date=${dayjs().format(
|
||||||
'DD/MM/YYYY'
|
'DD/MM/YYYY'
|
||||||
)}&channel=&startTime=0&endTime=24`
|
)}&channel=&startTime=0&endTime=24`
|
||||||
)
|
)
|
||||||
.then(response => response.data)
|
.then(response => response.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
const channels = []
|
const channels = []
|
||||||
if (result.data && result.data.entries) {
|
if (result.data && result.data.entries) {
|
||||||
Object.values(result.data.entries).forEach(schedules => {
|
Object.values(result.data.entries).forEach(schedules => {
|
||||||
if (schedules.length) {
|
if (schedules.length) {
|
||||||
channels.push({
|
channels.push({
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
site_id: schedules[0].channel.no,
|
site_id: schedules[0].channel.no,
|
||||||
name: schedules[0].channel.name
|
name: schedules[0].channel.name
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return channels
|
return channels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, channel) {
|
function parseItems(content, channel) {
|
||||||
return JSON.parse(content.trim()).data.entries[channel] || []
|
return JSON.parse(content.trim()).data.entries[channel] || []
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTitle(item) {
|
function parseTitle(item) {
|
||||||
return item.title
|
return item.title
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDescription(item) {
|
function parseDescription(item) {
|
||||||
return item.long_description
|
return item.long_description
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart({ item, date }) {
|
function parseStart({ item, date }) {
|
||||||
return date.add(item.start, 'ms')
|
return date.add(item.start, 'ms')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStop({ item, date }) {
|
function parseStop({ item, date }) {
|
||||||
return date.add(item.stop, 'ms')
|
return date.add(item.stop, 'ms')
|
||||||
}
|
}
|
||||||
|
|
||||||
function toDelta(from, to) {
|
function toDelta(from, to) {
|
||||||
return toDate(to).diff(toDate(from), 'milliseconds')
|
return toDate(to).diff(toDate(from), 'milliseconds')
|
||||||
}
|
}
|
||||||
|
|
||||||
function toDate(date) {
|
function toDate(date) {
|
||||||
return dayjs(date, 'YYYY-MM-DD HH:mm:ss')
|
return dayjs(date, 'YYYY-MM-DD HH:mm:ss')
|
||||||
}
|
}
|
||||||
|
|
||||||
function asDate(date) {
|
function asDate(date) {
|
||||||
return date.toISOString()
|
return date.toISOString()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,69 +1,69 @@
|
|||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'foxsports.com.au',
|
site: 'foxsports.com.au',
|
||||||
days: 3,
|
days: 3,
|
||||||
request: {
|
request: {
|
||||||
cache: {
|
cache: {
|
||||||
ttl: 60 * 60 * 1000 // 1 hour
|
ttl: 60 * 60 * 1000 // 1 hour
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
url({ date }) {
|
url({ date }) {
|
||||||
return `https://tvguide.foxsports.com.au/granite-api/programmes.json?from=${date.format(
|
return `https://tvguide.foxsports.com.au/granite-api/programmes.json?from=${date.format(
|
||||||
'YYYY-MM-DD'
|
'YYYY-MM-DD'
|
||||||
)}&to=${date.add(1, 'd').format('YYYY-MM-DD')}`
|
)}&to=${date.add(1, 'd').format('YYYY-MM-DD')}`
|
||||||
},
|
},
|
||||||
parser({ content, channel }) {
|
parser({ content, channel }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
const items = parseItems(content, channel)
|
const items = parseItems(content, channel)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.programmeTitle,
|
title: item.programmeTitle,
|
||||||
sub_title: item.title,
|
sub_title: item.title,
|
||||||
category: item.genreTitle,
|
category: item.genreTitle,
|
||||||
description: item.synopsis,
|
description: item.synopsis,
|
||||||
start: dayjs.utc(item.startTime),
|
start: dayjs.utc(item.startTime),
|
||||||
stop: dayjs.utc(item.endTime)
|
stop: dayjs.utc(item.endTime)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(
|
.get(
|
||||||
`https://tvguide.foxsports.com.au/granite-api/programmes.json?from=${dayjs().format(
|
`https://tvguide.foxsports.com.au/granite-api/programmes.json?from=${dayjs().format(
|
||||||
'YYYY-MM-DD'
|
'YYYY-MM-DD'
|
||||||
)}&to=${dayjs().add(1, 'd').format('YYYY-MM-DD')}`
|
)}&to=${dayjs().add(1, 'd').format('YYYY-MM-DD')}`
|
||||||
)
|
)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
let channels = {}
|
let channels = {}
|
||||||
data['channel-programme'].forEach(item => {
|
data['channel-programme'].forEach(item => {
|
||||||
if (channels[item.channelId]) return
|
if (channels[item.channelId]) return
|
||||||
|
|
||||||
channels[item.channelId] = {
|
channels[item.channelId] = {
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
site_id: item.channelId,
|
site_id: item.channelId,
|
||||||
name: item.channelName
|
name: item.channelName
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return Object.values(channels)
|
return Object.values(channels)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, channel) {
|
function parseItems(content, channel) {
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
if (!data) return []
|
if (!data) return []
|
||||||
const programmes = data['channel-programme']
|
const programmes = data['channel-programme']
|
||||||
if (!Array.isArray(programmes)) return []
|
if (!Array.isArray(programmes)) return []
|
||||||
|
|
||||||
const channelData = programmes.filter(i => i.channelId == channel.site_id)
|
const channelData = programmes.filter(i => i.channelId == channel.site_id)
|
||||||
return channelData && Array.isArray(channelData) ? channelData : []
|
return channelData && Array.isArray(channelData) ? channelData : []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,138 +1,138 @@
|
|||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'foxtel.com.au',
|
site: 'foxtel.com.au',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ channel, date }) {
|
url({ channel, date }) {
|
||||||
return `https://www.foxtel.com.au/tv-guide/channel/${channel.site_id}/${date.format(
|
return `https://www.foxtel.com.au/tv-guide/channel/${channel.site_id}/${date.format(
|
||||||
'YYYY/MM/DD'
|
'YYYY/MM/DD'
|
||||||
)}`
|
)}`
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
headers: {
|
headers: {
|
||||||
'Accept-Language': 'en-US,en;',
|
'Accept-Language': 'en-US,en;',
|
||||||
Cookie: 'AAMC_foxtel_0=REGION|7',
|
Cookie: 'AAMC_foxtel_0=REGION|7',
|
||||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
parser: function ({ content, date }) {
|
parser: function ({ content, date }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
const items = parseItems(content)
|
const items = parseItems(content)
|
||||||
for (let item of items) {
|
for (let item of items) {
|
||||||
const $item = cheerio.load(item)
|
const $item = cheerio.load(item)
|
||||||
const prev = programs[programs.length - 1]
|
const prev = programs[programs.length - 1]
|
||||||
let start = parseStart($item)
|
let start = parseStart($item)
|
||||||
if (prev) {
|
if (prev) {
|
||||||
if (start.isBefore(prev.start)) {
|
if (start.isBefore(prev.start)) {
|
||||||
start = start.add(1, 'd')
|
start = start.add(1, 'd')
|
||||||
date = date.add(1, 'd')
|
date = date.add(1, 'd')
|
||||||
}
|
}
|
||||||
prev.stop = start
|
prev.stop = start
|
||||||
}
|
}
|
||||||
const stop = start.add(30, 'm')
|
const stop = start.add(30, 'm')
|
||||||
programs.push({
|
programs.push({
|
||||||
title: parseTitle($item),
|
title: parseTitle($item),
|
||||||
sub_title: parseSubTitle($item),
|
sub_title: parseSubTitle($item),
|
||||||
image: parseImage($item),
|
image: parseImage($item),
|
||||||
rating: parseRating($item),
|
rating: parseRating($item),
|
||||||
season: parseSeason($item),
|
season: parseSeason($item),
|
||||||
episode: parseEpisode($item),
|
episode: parseEpisode($item),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get('https://www.foxtel.com.au/webepg/ws/foxtel/channels?regionId=8336', {
|
.get('https://www.foxtel.com.au/webepg/ws/foxtel/channels?regionId=8336', {
|
||||||
headers: {
|
headers: {
|
||||||
'User-Agent': 'insomnia/2022.7.5'
|
'User-Agent': 'insomnia/2022.7.5'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
return data.channels.map(item => {
|
return data.channels.map(item => {
|
||||||
const slug = item.name
|
const slug = item.name
|
||||||
.replace(/\+/g, '-')
|
.replace(/\+/g, '-')
|
||||||
.replace(/&/g, '')
|
.replace(/&/g, '')
|
||||||
.replace(/[^a-z0-9\s]/gi, '')
|
.replace(/[^a-z0-9\s]/gi, '')
|
||||||
.replace(/\s/g, '-')
|
.replace(/\s/g, '-')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
name: item.name,
|
name: item.name,
|
||||||
site_id: `${slug}/${item.channelTag}`
|
site_id: `${slug}/${item.channelTag}`
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseSeason($item) {
|
function parseSeason($item) {
|
||||||
let seasonString = $item('.epg-event-description > div > abbr:nth-child(1)').attr('title')
|
let seasonString = $item('.epg-event-description > div > abbr:nth-child(1)').attr('title')
|
||||||
if (!seasonString) return null
|
if (!seasonString) return null
|
||||||
let [, season] = seasonString.match(/^Season: (\d+)/) || [null, null]
|
let [, season] = seasonString.match(/^Season: (\d+)/) || [null, null]
|
||||||
|
|
||||||
return season ? parseInt(season) : null
|
return season ? parseInt(season) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseEpisode($item) {
|
function parseEpisode($item) {
|
||||||
let episodeString = $item('.epg-event-description > div > abbr:nth-child(2)').attr('title')
|
let episodeString = $item('.epg-event-description > div > abbr:nth-child(2)').attr('title')
|
||||||
if (!episodeString) return null
|
if (!episodeString) return null
|
||||||
let [, episode] = episodeString.match(/^Episode: (\d+)/) || [null, null]
|
let [, episode] = episodeString.match(/^Episode: (\d+)/) || [null, null]
|
||||||
|
|
||||||
return episode ? parseInt(episode) : null
|
return episode ? parseInt(episode) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseImage($item) {
|
function parseImage($item) {
|
||||||
return $item('.epg-event-thumbnail > img').attr('src')
|
return $item('.epg-event-thumbnail > img').attr('src')
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTitle($item) {
|
function parseTitle($item) {
|
||||||
return $item('.epg-event-description').clone().children().remove().end().text().trim()
|
return $item('.epg-event-description').clone().children().remove().end().text().trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseSubTitle($item) {
|
function parseSubTitle($item) {
|
||||||
let subtitle = $item('.epg-event-description > div')
|
let subtitle = $item('.epg-event-description > div')
|
||||||
.clone()
|
.clone()
|
||||||
.children()
|
.children()
|
||||||
.remove()
|
.remove()
|
||||||
.end()
|
.end()
|
||||||
.text()
|
.text()
|
||||||
.trim()
|
.trim()
|
||||||
.split(',')
|
.split(',')
|
||||||
|
|
||||||
subtitle = subtitle.pop()
|
subtitle = subtitle.pop()
|
||||||
const [, rating] = subtitle.match(/\(([^)]+)\)$/) || [null, null]
|
const [, rating] = subtitle.match(/\(([^)]+)\)$/) || [null, null]
|
||||||
|
|
||||||
return subtitle.replace(`(${rating})`, '').trim()
|
return subtitle.replace(`(${rating})`, '').trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseRating($item) {
|
function parseRating($item) {
|
||||||
const subtitle = $item('.epg-event-description > div').text().trim()
|
const subtitle = $item('.epg-event-description > div').text().trim()
|
||||||
const [, rating] = subtitle.match(/\(([^)]+)\)$/) || [null, null]
|
const [, rating] = subtitle.match(/\(([^)]+)\)$/) || [null, null]
|
||||||
|
|
||||||
return rating
|
return rating
|
||||||
? {
|
? {
|
||||||
system: 'ACB',
|
system: 'ACB',
|
||||||
value: rating
|
value: rating
|
||||||
}
|
}
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart($item) {
|
function parseStart($item) {
|
||||||
const unix = $item('*').data('scheduled-date')
|
const unix = $item('*').data('scheduled-date')
|
||||||
|
|
||||||
return dayjs(parseInt(unix))
|
return dayjs(parseInt(unix))
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content) {
|
function parseItems(content) {
|
||||||
if (!content) return []
|
if (!content) return []
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
|
|
||||||
return $('#epg-channel-events > a').toArray()
|
return $('#epg-channel-events > a').toArray()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,60 +1,60 @@
|
|||||||
const { parser, url, request } = require('./foxtel.com.au.config.js')
|
const { parser, url, request } = require('./foxtel.com.au.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2022-11-08', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2022-11-08', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: 'Channel-9-Sydney/NIN',
|
site_id: 'Channel-9-Sydney/NIN',
|
||||||
xmltv_id: 'Channel9Sydney.au'
|
xmltv_id: 'Channel9Sydney.au'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ channel, date })).toBe(
|
expect(url({ channel, date })).toBe(
|
||||||
'https://www.foxtel.com.au/tv-guide/channel/Channel-9-Sydney/NIN/2022/11/08'
|
'https://www.foxtel.com.au/tv-guide/channel/Channel-9-Sydney/NIN/2022/11/08'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request headers', () => {
|
it('can generate valid request headers', () => {
|
||||||
expect(request.headers).toMatchObject({
|
expect(request.headers).toMatchObject({
|
||||||
'Accept-Language': 'en-US,en;',
|
'Accept-Language': 'en-US,en;',
|
||||||
Cookie: 'AAMC_foxtel_0=REGION|7'
|
Cookie: 'AAMC_foxtel_0=REGION|7'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
||||||
|
|
||||||
let results = parser({ content })
|
let results = parser({ content })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2022-11-07T12:40:00.000Z',
|
start: '2022-11-07T12:40:00.000Z',
|
||||||
stop: '2022-11-07T13:30:00.000Z',
|
stop: '2022-11-07T13:30:00.000Z',
|
||||||
title: 'The Equalizer',
|
title: 'The Equalizer',
|
||||||
sub_title: 'Glory',
|
sub_title: 'Glory',
|
||||||
image:
|
image:
|
||||||
'https://images1.resources.foxtel.com.au/store2/mount1/16/3/69e0v.jpg?maxheight=90&limit=91aa1c7a2c485aeeba0706941f79f111adb35830',
|
'https://images1.resources.foxtel.com.au/store2/mount1/16/3/69e0v.jpg?maxheight=90&limit=91aa1c7a2c485aeeba0706941f79f111adb35830',
|
||||||
rating: {
|
rating: {
|
||||||
system: 'ACB',
|
system: 'ACB',
|
||||||
value: 'M'
|
value: 'M'
|
||||||
},
|
},
|
||||||
season: 1,
|
season: 1,
|
||||||
episode: 2
|
episode: 2
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const result = parser({
|
const result = parser({
|
||||||
content: fs.readFileSync(path.resolve(__dirname, '__data__/no-content.html'))
|
content: fs.readFileSync(path.resolve(__dirname, '__data__/no-content.html'))
|
||||||
})
|
})
|
||||||
expect(result).toMatchObject([])
|
expect(result).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,62 +1,62 @@
|
|||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'freetv.tv',
|
site: 'freetv.tv',
|
||||||
days: 2,
|
days: 2,
|
||||||
url: function ({ channel, date }) {
|
url: function ({ channel, date }) {
|
||||||
const localDate = dayjs(date).tz('Asia/Jerusalem')
|
const localDate = dayjs(date).tz('Asia/Jerusalem')
|
||||||
const since = localDate.startOf('day').format('YYYY-MM-DDTHH:mmZZ')
|
const since = localDate.startOf('day').format('YYYY-MM-DDTHH:mmZZ')
|
||||||
const till = localDate.add(1, 'day').startOf('day').format('YYYY-MM-DDTHH:mmZZ')
|
const till = localDate.add(1, 'day').startOf('day').format('YYYY-MM-DDTHH:mmZZ')
|
||||||
|
|
||||||
return `https://web.freetv.tv/api/products/lives/programmes?liveId[]=${
|
return `https://web.freetv.tv/api/products/lives/programmes?liveId[]=${
|
||||||
channel.site_id
|
channel.site_id
|
||||||
}&since=${encodeURIComponent(since)}&till=${encodeURIComponent(till)}&lang=HEB&platform=BROWSER`
|
}&since=${encodeURIComponent(since)}&till=${encodeURIComponent(till)}&lang=HEB&platform=BROWSER`
|
||||||
},
|
},
|
||||||
parser: function ({ content }) {
|
parser: function ({ content }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
let items = []
|
let items = []
|
||||||
|
|
||||||
try {
|
try {
|
||||||
items = JSON.parse(content)
|
items = JSON.parse(content)
|
||||||
} catch {
|
} catch {
|
||||||
return programs
|
return programs
|
||||||
}
|
}
|
||||||
|
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const start = parseStart(item)
|
const start = parseStart(item)
|
||||||
const stop = parseStop(item)
|
const stop = parseStop(item)
|
||||||
if (!start.isValid() || !stop.isValid()) return
|
if (!start.isValid() || !stop.isValid()) return
|
||||||
|
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
description: item.description || item.lead,
|
description: item.description || item.lead,
|
||||||
image: getImageUrl(item),
|
image: getImageUrl(item),
|
||||||
icon: getImageUrl(item),
|
icon: getImageUrl(item),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart(item) {
|
function parseStart(item) {
|
||||||
return item.since ? dayjs.utc(item.since).tz('Asia/Jerusalem') : null
|
return item.since ? dayjs.utc(item.since).tz('Asia/Jerusalem') : null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStop(item) {
|
function parseStop(item) {
|
||||||
return item.till ? dayjs.utc(item.till).tz('Asia/Jerusalem') : null
|
return item.till ? dayjs.utc(item.till).tz('Asia/Jerusalem') : null
|
||||||
}
|
}
|
||||||
|
|
||||||
function getImageUrl(item) {
|
function getImageUrl(item) {
|
||||||
const url = item.images?.['16x9']?.[0]?.url
|
const url = item.images?.['16x9']?.[0]?.url
|
||||||
return url ? `https:${url}` : null
|
return url ? `https:${url}` : null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,73 +1,73 @@
|
|||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const parseDuration = require('parse-duration').default
|
const parseDuration = require('parse-duration').default
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'freeview.co.uk',
|
site: 'freeview.co.uk',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ date, channel }) {
|
url({ date, channel }) {
|
||||||
const [networkId] = channel.site_id.split('#')
|
const [networkId] = channel.site_id.split('#')
|
||||||
const startTimestamp = date.startOf('d').unix()
|
const startTimestamp = date.startOf('d').unix()
|
||||||
|
|
||||||
return `https://www.freeview.co.uk/api/tv-guide?nid=${networkId}&start=${startTimestamp}`
|
return `https://www.freeview.co.uk/api/tv-guide?nid=${networkId}&start=${startTimestamp}`
|
||||||
},
|
},
|
||||||
parser({ content, channel }) {
|
parser({ content, channel }) {
|
||||||
let programs = []
|
let programs = []
|
||||||
let items = parseItems(content, channel)
|
let items = parseItems(content, channel)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const start = parseStart(item)
|
const start = parseStart(item)
|
||||||
const duration = parseDuration(item.duration)
|
const duration = parseDuration(item.duration)
|
||||||
const stop = start.add(duration, 'ms')
|
const stop = start.add(duration, 'ms')
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.main_title,
|
title: item.main_title,
|
||||||
subtitle: item.secondary_title,
|
subtitle: item.secondary_title,
|
||||||
image: parseImage(item),
|
image: parseImage(item),
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const networkId = '64257' // Great London
|
const networkId = '64257' // Great London
|
||||||
const startTimestamp = dayjs.utc().startOf('d').unix()
|
const startTimestamp = dayjs.utc().startOf('d').unix()
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(`https://www.freeview.co.uk/api/tv-guide?nid=${networkId}&start=${startTimestamp}`)
|
.get(`https://www.freeview.co.uk/api/tv-guide?nid=${networkId}&start=${startTimestamp}`)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
return data.data.programs.map(item => ({
|
return data.data.programs.map(item => ({
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
site_id: `${networkId}#${item.service_id}`,
|
site_id: `${networkId}#${item.service_id}`,
|
||||||
name: item.title
|
name: item.title
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseImage(item) {
|
function parseImage(item) {
|
||||||
return item.image_url ? `${item.image_url}?w=800` : null
|
return item.image_url ? `${item.image_url}?w=800` : null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart(item) {
|
function parseStart(item) {
|
||||||
return dayjs(item.start_time)
|
return dayjs(item.start_time)
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, channel) {
|
function parseItems(content, channel) {
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
const programs = data?.data?.programs
|
const programs = data?.data?.programs
|
||||||
if (!Array.isArray(programs)) return []
|
if (!Array.isArray(programs)) return []
|
||||||
const [, channelId] = channel.site_id.split('#')
|
const [, channelId] = channel.site_id.split('#')
|
||||||
const channelData = programs.find(p => p.service_id === channelId)
|
const channelData = programs.find(p => p.service_id === channelId)
|
||||||
const channelPrograms = channelData?.events
|
const channelPrograms = channelData?.events
|
||||||
if (!Array.isArray(channelPrograms)) return []
|
if (!Array.isArray(channelPrograms)) return []
|
||||||
|
|
||||||
return channelPrograms
|
return channelPrograms
|
||||||
} catch {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,55 +1,55 @@
|
|||||||
const { parser, url } = require('./freeview.co.uk.config.js')
|
const { parser, url } = require('./freeview.co.uk.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2025-01-16', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-01-16', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: '64257#4164',
|
site_id: '64257#4164',
|
||||||
xmltv_id: 'BBCOneLondon.uk'
|
xmltv_id: 'BBCOneLondon.uk'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ date, channel })).toBe(
|
expect(url({ date, channel })).toBe(
|
||||||
'https://www.freeview.co.uk/api/tv-guide?nid=64257&start=1736985600'
|
'https://www.freeview.co.uk/api/tv-guide?nid=64257&start=1736985600'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
||||||
let results = parser({ content, channel })
|
let results = parser({ content, channel })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(25)
|
expect(results.length).toBe(25)
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2025-01-16T00:00:00.000Z',
|
start: '2025-01-16T00:00:00.000Z',
|
||||||
stop: '2025-01-16T00:45:00.000Z',
|
stop: '2025-01-16T00:45:00.000Z',
|
||||||
title: 'The Weakest Link',
|
title: 'The Weakest Link',
|
||||||
subtitle: 'Series 4: Episode 7',
|
subtitle: 'Series 4: Episode 7',
|
||||||
image: 'https://img.freeviewplay.tv/p0b041486e4378cbf074511098f74e78f?w=800'
|
image: 'https://img.freeviewplay.tv/p0b041486e4378cbf074511098f74e78f?w=800'
|
||||||
})
|
})
|
||||||
expect(results[24]).toMatchObject({
|
expect(results[24]).toMatchObject({
|
||||||
start: '2025-01-16T23:40:00.000Z',
|
start: '2025-01-16T23:40:00.000Z',
|
||||||
stop: '2025-01-17T00:10:00.000Z',
|
stop: '2025-01-17T00:10:00.000Z',
|
||||||
title: 'Newscast',
|
title: 'Newscast',
|
||||||
subtitle: 'Series 5: 16/01/2025',
|
subtitle: 'Series 5: 16/01/2025',
|
||||||
image: 'https://img.freeviewplay.tv/pb43e790fe10fe5ba668caf22224bc312?w=800'
|
image: 'https://img.freeviewplay.tv/pb43e790fe10fe5ba668caf22224bc312?w=800'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const results = parser({
|
const results = parser({
|
||||||
content: '[]',
|
content: '[]',
|
||||||
channel
|
channel
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user