2021-03-13 13:11:33 +01:00
|
|
|
const fs = require('fs')
|
|
|
|
const path = require('path')
|
2021-04-02 19:12:27 +02:00
|
|
|
const axios = require('axios').default
|
2021-03-13 13:11:33 +01:00
|
|
|
const axiosCookieJarSupport = require('axios-cookiejar-support').default
|
|
|
|
const tough = require('tough-cookie')
|
|
|
|
const convert = require('xml-js')
|
2021-04-02 19:12:27 +02:00
|
|
|
const merge = require('lodash.merge')
|
2021-03-13 13:11:33 +01:00
|
|
|
const dayjs = require('dayjs')
|
|
|
|
const utc = require('dayjs/plugin/utc')
|
|
|
|
dayjs.extend(utc)
|
|
|
|
axiosCookieJarSupport(axios)
|
|
|
|
|
|
|
|
const utils = {}
|
2021-03-13 14:17:50 +01:00
|
|
|
|
2021-03-13 13:11:33 +01:00
|
|
|
utils.loadConfig = function (file) {
|
|
|
|
if (!file) throw new Error('Path to [site].config.js is missing')
|
|
|
|
console.log(`Loading '${file}'...`)
|
|
|
|
|
2021-03-13 14:17:50 +01:00
|
|
|
const configPath = path.resolve(file)
|
2021-03-13 13:11:33 +01:00
|
|
|
const config = require(configPath)
|
|
|
|
|
2021-03-13 14:42:18 +01:00
|
|
|
if (!config.site) throw new Error("The required 'site' property is missing")
|
|
|
|
if (!config.channels) throw new Error("The required 'channels' property is missing")
|
|
|
|
if (!config.url) throw new Error("The required 'url' property is missing")
|
2021-04-02 16:49:00 +02:00
|
|
|
if (typeof config.url !== 'function' && typeof config.url !== 'string')
|
|
|
|
throw new Error("The 'url' property should return the function or string")
|
2021-03-13 14:42:18 +01:00
|
|
|
if (!config.parser) throw new Error("The required 'parser' function is missing")
|
|
|
|
if (typeof config.parser !== 'function')
|
|
|
|
throw new Error("The 'parser' property should return the function")
|
2021-03-19 19:26:32 +01:00
|
|
|
if (config.logo && typeof config.logo !== 'function')
|
|
|
|
throw new Error("The 'logo' property should return the function")
|
2021-03-13 14:42:18 +01:00
|
|
|
|
2021-03-19 20:22:58 +01:00
|
|
|
config.channels = path.join(path.dirname(file), config.channels)
|
|
|
|
|
2021-04-02 19:12:27 +02:00
|
|
|
const defaultConfig = {
|
|
|
|
days: 1,
|
|
|
|
lang: 'en',
|
|
|
|
delay: 3000,
|
|
|
|
output: 'guide.xml',
|
|
|
|
request: {
|
|
|
|
method: 'GET',
|
|
|
|
headers: {
|
|
|
|
'User-Agent':
|
|
|
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36 Edg/79.0.309.71',
|
|
|
|
Cookie: ''
|
|
|
|
},
|
2021-03-21 19:16:27 +01:00
|
|
|
timeout: 5000,
|
2021-04-02 19:12:27 +02:00
|
|
|
withCredentials: true,
|
|
|
|
jar: new tough.CookieJar(),
|
|
|
|
responseType: 'arraybuffer'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return merge(defaultConfig, config)
|
2021-03-13 13:11:33 +01:00
|
|
|
}
|
|
|
|
|
2021-03-19 20:22:58 +01:00
|
|
|
utils.parseChannels = function (filename) {
|
|
|
|
if (!filename) throw new Error('Path to [site].channels.xml is missing')
|
|
|
|
console.log(`Loading '${filename}'...`)
|
2021-03-13 13:11:33 +01:00
|
|
|
|
2021-03-19 20:22:58 +01:00
|
|
|
const xml = fs.readFileSync(path.resolve(filename), { encoding: 'utf-8' })
|
2021-03-13 13:11:33 +01:00
|
|
|
const result = convert.xml2js(xml)
|
|
|
|
const site = result.elements.find(el => el.name === 'site')
|
|
|
|
const channels = site.elements.find(el => el.name === 'channels')
|
|
|
|
|
|
|
|
return channels.elements
|
|
|
|
.filter(el => el.name === 'channel')
|
|
|
|
.map(el => {
|
|
|
|
const channel = el.attributes
|
2021-03-28 22:58:02 +02:00
|
|
|
if (!el.elements) throw new Error(`Channel '${channel.xmltv_id}' has no valid name`)
|
2021-03-13 13:11:33 +01:00
|
|
|
channel.name = el.elements.find(el => el.type === 'text').text
|
|
|
|
channel.site = channel.site || site.attributes.site
|
|
|
|
|
|
|
|
return channel
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
utils.sleep = function (ms) {
|
|
|
|
return function (x) {
|
|
|
|
return new Promise(resolve => setTimeout(() => resolve(x), ms))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
utils.escapeString = function (string, defaultValue = '') {
|
|
|
|
if (!string) return defaultValue
|
|
|
|
|
2021-03-20 15:03:17 +01:00
|
|
|
const regex = new RegExp(
|
|
|
|
'((?:[\0-\x08\x0B\f\x0E-\x1F\uFFFD\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]))|([\\x7F-\\x84]|[\\x86-\\x9F]|[\\uFDD0-\\uFDEF]|(?:\\uD83F[\\uDFFE\\uDFFF])|(?:\\uD87F[\\uDF' +
|
|
|
|
'FE\\uDFFF])|(?:\\uD8BF[\\uDFFE\\uDFFF])|(?:\\uD8FF[\\uDFFE\\uDFFF])|(?:\\uD93F[\\uDFFE\\uD' +
|
|
|
|
'FFF])|(?:\\uD97F[\\uDFFE\\uDFFF])|(?:\\uD9BF[\\uDFFE\\uDFFF])|(?:\\uD9FF[\\uDFFE\\uDFFF])' +
|
|
|
|
'|(?:\\uDA3F[\\uDFFE\\uDFFF])|(?:\\uDA7F[\\uDFFE\\uDFFF])|(?:\\uDABF[\\uDFFE\\uDFFF])|(?:\\' +
|
|
|
|
'uDAFF[\\uDFFE\\uDFFF])|(?:\\uDB3F[\\uDFFE\\uDFFF])|(?:\\uDB7F[\\uDFFE\\uDFFF])|(?:\\uDBBF' +
|
|
|
|
'[\\uDFFE\\uDFFF])|(?:\\uDBFF[\\uDFFE\\uDFFF])(?:[\\0-\\t\\x0B\\f\\x0E-\\u2027\\u202A-\\uD7FF\\' +
|
|
|
|
'uE000-\\uFFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]|[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|' +
|
|
|
|
'(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF]))',
|
|
|
|
'g'
|
|
|
|
)
|
|
|
|
|
|
|
|
string = String(string || '').replace(regex, '')
|
|
|
|
|
2021-03-13 13:11:33 +01:00
|
|
|
return string
|
|
|
|
.replace(/&/g, '&')
|
|
|
|
.replace(/</g, '<')
|
|
|
|
.replace(/>/g, '>')
|
|
|
|
.replace(/"/g, '"')
|
|
|
|
.replace(/'/g, ''')
|
|
|
|
.replace(/\n|\r/g, ' ')
|
|
|
|
.replace(/ +/g, ' ')
|
|
|
|
.trim()
|
|
|
|
}
|
|
|
|
|
|
|
|
utils.convertToXMLTV = function ({ config, channels, programs }) {
|
|
|
|
let output = `<?xml version="1.0" encoding="UTF-8" ?><tv>\r\n`
|
|
|
|
|
|
|
|
for (let channel of channels) {
|
|
|
|
const id = this.escapeString(channel['xmltv_id'])
|
|
|
|
const displayName = this.escapeString(channel.name)
|
2021-03-19 19:26:32 +01:00
|
|
|
output += `<channel id="${id}"><display-name>${displayName}</display-name>`
|
|
|
|
if (channel.logo) {
|
2021-03-28 03:00:21 +02:00
|
|
|
const logo = this.escapeString(channel.logo)
|
|
|
|
output += `<icon src="${logo}" />`
|
2021-03-19 19:26:32 +01:00
|
|
|
}
|
|
|
|
output += `</channel>\r\n`
|
2021-03-13 13:11:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for (let program of programs) {
|
|
|
|
if (!program) continue
|
|
|
|
|
|
|
|
const channel = this.escapeString(program.channel)
|
|
|
|
const title = this.escapeString(program.title)
|
|
|
|
const description = this.escapeString(program.description)
|
|
|
|
const category = this.escapeString(program.category)
|
|
|
|
const start = program.start ? dayjs.utc(program.start).format('YYYYMMDDHHmmss ZZ') : ''
|
|
|
|
const stop = program.stop ? dayjs.utc(program.stop).format('YYYYMMDDHHmmss ZZ') : ''
|
|
|
|
const lang = program.lang || config.lang
|
|
|
|
|
|
|
|
if (start && title) {
|
|
|
|
output += `<programme start="${start}"`
|
|
|
|
|
|
|
|
if (stop) {
|
|
|
|
output += ` stop="${stop}"`
|
|
|
|
}
|
|
|
|
|
|
|
|
output += ` channel="${channel}"><title lang="${lang}">${title}</title>`
|
|
|
|
|
|
|
|
if (description) {
|
|
|
|
output += `<desc lang="${lang}">${description}</desc>`
|
|
|
|
}
|
|
|
|
|
|
|
|
if (category) {
|
|
|
|
output += `<category lang="${lang}">${category}</category>`
|
|
|
|
}
|
|
|
|
|
|
|
|
output += '</programme>\r\n'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
output += '</tv>'
|
|
|
|
|
|
|
|
return output
|
|
|
|
}
|
|
|
|
|
2021-03-20 11:55:09 +01:00
|
|
|
utils.parsePrograms = function ({ response, item, config }) {
|
2021-04-02 19:12:27 +02:00
|
|
|
const options = merge(item, config, {
|
2021-03-31 20:47:53 +02:00
|
|
|
content: response.data.toString(),
|
|
|
|
buffer: response.data
|
2021-03-20 11:55:09 +01:00
|
|
|
})
|
|
|
|
|
2021-03-21 19:16:27 +01:00
|
|
|
return config
|
2021-03-20 11:55:09 +01:00
|
|
|
.parser(options)
|
|
|
|
.filter(i => i)
|
|
|
|
.map(p => {
|
|
|
|
p.channel = item.channel.xmltv_id
|
|
|
|
return p
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-03-13 14:17:50 +01:00
|
|
|
utils.writeToFile = function (filename, data) {
|
|
|
|
const dir = path.resolve(path.dirname(filename))
|
|
|
|
if (!fs.existsSync(dir)) {
|
|
|
|
fs.mkdirSync(dir, { recursive: true })
|
2021-03-13 13:11:33 +01:00
|
|
|
}
|
|
|
|
|
2021-03-13 13:49:04 +01:00
|
|
|
fs.writeFileSync(path.resolve(filename), data)
|
2021-03-13 13:11:33 +01:00
|
|
|
}
|
|
|
|
|
2021-04-02 19:12:27 +02:00
|
|
|
utils.fetchData = function (item, config) {
|
|
|
|
const options = config.request
|
|
|
|
options.url = typeof config.url === 'function' ? config.url(item) : config.url
|
|
|
|
options.data = typeof options.data === 'function' ? options.data(item) : options.data
|
|
|
|
|
|
|
|
return axios(options)
|
2021-03-13 13:11:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
utils.getUTCDate = function () {
|
|
|
|
return dayjs.utc()
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = utils
|