epg-grabber 0.27.1 → 0.28.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -2
- package/bin/epg-grabber.js +20 -47
- package/package.json +1 -1
- package/src/channels.js +26 -0
- package/src/client.js +138 -0
- package/src/config.js +34 -0
- package/src/file.js +33 -0
- package/src/index.js +37 -30
- package/src/logger.js +34 -0
- package/src/programs.js +24 -0
- package/src/utils.js +20 -479
- package/src/xmltv.js +187 -0
- package/tests/channels.test.js +26 -0
- package/tests/client.test.js +47 -0
- package/tests/config.test.js +26 -0
- package/tests/index.test.js +1 -8
- package/tests/programs.test.js +63 -0
- package/tests/utils.test.js +3 -393
- package/tests/xmltv.test.js +146 -0
package/README.md
CHANGED
|
@@ -166,12 +166,24 @@ module.exports = {
|
|
|
166
166
|
title, // program title (required)
|
|
167
167
|
start, // start time of the program (required)
|
|
168
168
|
stop, // end time of the program (required)
|
|
169
|
+
sub_title, // program sub-title (optional)
|
|
169
170
|
description, // description of the program (optional)
|
|
170
|
-
category, // program
|
|
171
|
+
category, // type of program (optional)
|
|
171
172
|
season, // season number (optional)
|
|
172
173
|
episode, // episode number (optional)
|
|
174
|
+
date, // the date the programme or film was finished (optional)
|
|
173
175
|
icon, // image associated with the program (optional)
|
|
174
|
-
|
|
176
|
+
rating, // program rating (optional)
|
|
177
|
+
director, // the name of director (optional)
|
|
178
|
+
actor, // the name of actor (optional)
|
|
179
|
+
writer, // the name of writer (optional)
|
|
180
|
+
adapter, // the name of adapter (optional)
|
|
181
|
+
producer, // the name of producer (optional)
|
|
182
|
+
composer, // the name of composer (optional)
|
|
183
|
+
editor, // the name of editor (optional)
|
|
184
|
+
presenter, // the name of presenter (optional)
|
|
185
|
+
commentator, // the name of commentator (optional)
|
|
186
|
+
guest // the name of guest (optional)
|
|
175
187
|
},
|
|
176
188
|
...
|
|
177
189
|
]
|
package/bin/epg-grabber.js
CHANGED
|
@@ -2,15 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
const { Command } = require('commander')
|
|
4
4
|
const program = new Command()
|
|
5
|
-
const fs = require('fs')
|
|
6
|
-
const path = require('path')
|
|
7
|
-
const EPGGrabber = require('../src/index')
|
|
8
|
-
const utils = require('../src/utils')
|
|
9
|
-
const { name, version, description } = require('../package.json')
|
|
10
5
|
const { merge } = require('lodash')
|
|
11
6
|
const { gzip } = require('node-gzip')
|
|
12
|
-
const
|
|
13
|
-
const {
|
|
7
|
+
const file = require('../src/file')
|
|
8
|
+
const { EPGGrabber, parseChannels, generateXMLTV, loadLogo } = require('../src/index')
|
|
9
|
+
const { create: createLogger } = require('../src/logger')
|
|
10
|
+
const { parseInteger, getUTCDate } = require('../src/utils')
|
|
11
|
+
const { name, version, description } = require('../package.json')
|
|
14
12
|
|
|
15
13
|
program
|
|
16
14
|
.name(name)
|
|
@@ -36,39 +34,13 @@ program
|
|
|
36
34
|
.parse(process.argv)
|
|
37
35
|
|
|
38
36
|
const options = program.opts()
|
|
39
|
-
|
|
40
|
-
const fileFormat = printf(({ level, message, timestamp }) => {
|
|
41
|
-
return `[${timestamp}] ${level.toUpperCase()}: ${message}`
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
const consoleFormat = printf(({ level, message, timestamp }) => {
|
|
45
|
-
if (level === 'error') return ` Error: ${message}`
|
|
46
|
-
|
|
47
|
-
return message
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
const t = [new transports.Console({ format: consoleFormat })]
|
|
51
|
-
|
|
52
|
-
if (options.log) {
|
|
53
|
-
t.push(
|
|
54
|
-
new transports.File({
|
|
55
|
-
filename: path.resolve(options.log),
|
|
56
|
-
format: combine(timestamp(), fileFormat),
|
|
57
|
-
options: { flags: 'w' }
|
|
58
|
-
})
|
|
59
|
-
)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const logger = createLogger({
|
|
63
|
-
level: options.logLevel,
|
|
64
|
-
transports: t
|
|
65
|
-
})
|
|
37
|
+
const logger = createLogger(options)
|
|
66
38
|
|
|
67
39
|
async function main() {
|
|
68
40
|
logger.info('Starting...')
|
|
69
41
|
|
|
70
42
|
logger.info(`Loading '${options.config}'...`)
|
|
71
|
-
let config = require(
|
|
43
|
+
let config = require(file.resolve(options.config))
|
|
72
44
|
config = merge(config, {
|
|
73
45
|
days: options.days,
|
|
74
46
|
debug: options.debug,
|
|
@@ -83,22 +55,27 @@ async function main() {
|
|
|
83
55
|
if (options.cacheTtl) config.request.cache.ttl = options.cacheTtl
|
|
84
56
|
if (options.channels) config.channels = options.channels
|
|
85
57
|
else if (config.channels)
|
|
86
|
-
config.channels =
|
|
58
|
+
config.channels = file.join(file.dirname(options.config), config.channels)
|
|
87
59
|
else throw new Error("The required 'channels' property is missing")
|
|
88
60
|
|
|
89
61
|
if (!config.channels) return logger.error('Path to [site].channels.xml is missing')
|
|
90
62
|
logger.info(`Loading '${config.channels}'...`)
|
|
91
|
-
const
|
|
92
|
-
|
|
63
|
+
const grabber = new EPGGrabber(config)
|
|
64
|
+
|
|
65
|
+
const channelsXML = file.read(config.channels)
|
|
66
|
+
const { channels } = parseChannels(channelsXML)
|
|
93
67
|
|
|
94
68
|
let programs = []
|
|
95
69
|
let i = 1
|
|
96
70
|
let days = options.days || 1
|
|
97
71
|
const total = channels.length * days
|
|
98
|
-
const utcDate =
|
|
72
|
+
const utcDate = getUTCDate()
|
|
99
73
|
const dates = Array.from({ length: config.days }, (_, i) => utcDate.add(i, 'd'))
|
|
100
|
-
const grabber = new EPGGrabber(config)
|
|
101
74
|
for (let channel of channels) {
|
|
75
|
+
if (!channel.logo && config.logo) {
|
|
76
|
+
channel.logo = await grabber.loadLogo(channel)
|
|
77
|
+
}
|
|
78
|
+
|
|
102
79
|
for (let date of dates) {
|
|
103
80
|
await grabber
|
|
104
81
|
.grab(channel, date, (data, err) => {
|
|
@@ -118,15 +95,15 @@ async function main() {
|
|
|
118
95
|
}
|
|
119
96
|
}
|
|
120
97
|
|
|
121
|
-
const xml =
|
|
98
|
+
const xml = generateXMLTV({ channels, programs })
|
|
122
99
|
let outputPath = options.output || config.output
|
|
123
100
|
if (options.gzip) {
|
|
124
101
|
outputPath = outputPath || 'guide.xml.gz'
|
|
125
102
|
const compressed = await gzip(xml)
|
|
126
|
-
|
|
103
|
+
file.write(outputPath, compressed)
|
|
127
104
|
} else {
|
|
128
105
|
outputPath = outputPath || 'guide.xml'
|
|
129
|
-
|
|
106
|
+
file.write(outputPath, xml)
|
|
130
107
|
}
|
|
131
108
|
|
|
132
109
|
logger.info(`File '${outputPath}' successfully saved`)
|
|
@@ -134,7 +111,3 @@ async function main() {
|
|
|
134
111
|
}
|
|
135
112
|
|
|
136
113
|
main()
|
|
137
|
-
|
|
138
|
-
function parseInteger(val) {
|
|
139
|
-
return val ? parseInt(val) : null
|
|
140
|
-
}
|
package/package.json
CHANGED
package/src/channels.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const convert = require('xml-js')
|
|
2
|
+
|
|
3
|
+
module.exports.parse = parse
|
|
4
|
+
|
|
5
|
+
function parse(xml) {
|
|
6
|
+
const result = convert.xml2js(xml)
|
|
7
|
+
const siteTag = result.elements.find(el => el.name === 'site') || {}
|
|
8
|
+
if (!siteTag.elements) return []
|
|
9
|
+
const site = siteTag.attributes.site
|
|
10
|
+
|
|
11
|
+
const channelsTag = siteTag.elements.find(el => el.name === 'channels')
|
|
12
|
+
if (!channelsTag.elements) return []
|
|
13
|
+
|
|
14
|
+
const channels = channelsTag.elements
|
|
15
|
+
.filter(el => el.name === 'channel')
|
|
16
|
+
.map(el => {
|
|
17
|
+
const channel = el.attributes
|
|
18
|
+
if (!el.elements) throw new Error(`Channel '${channel.xmltv_id}' has no valid name`)
|
|
19
|
+
channel.name = el.elements.find(el => el.type === 'text').text
|
|
20
|
+
channel.site = channel.site || site
|
|
21
|
+
|
|
22
|
+
return channel
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
return { site, channels }
|
|
26
|
+
}
|
package/src/client.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
const { CurlGenerator } = require('curl-generator')
|
|
2
|
+
const axios = require('axios').default
|
|
3
|
+
const axiosCookieJarSupport = require('axios-cookiejar-support').default
|
|
4
|
+
const { setupCache } = require('axios-cache-interceptor')
|
|
5
|
+
const { isObject, isPromise, getUTCDate } = require('./utils')
|
|
6
|
+
|
|
7
|
+
axiosCookieJarSupport(axios)
|
|
8
|
+
|
|
9
|
+
module.exports.create = create
|
|
10
|
+
module.exports.buildRequest = buildRequest
|
|
11
|
+
module.exports.parseResponse = parseResponse
|
|
12
|
+
|
|
13
|
+
let timeout
|
|
14
|
+
|
|
15
|
+
function create(config) {
|
|
16
|
+
const client = setupCache(
|
|
17
|
+
axios.create({
|
|
18
|
+
headers: {
|
|
19
|
+
'User-Agent':
|
|
20
|
+
'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'
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
client.interceptors.request.use(
|
|
26
|
+
function (request) {
|
|
27
|
+
if (config.debug) {
|
|
28
|
+
console.log('Request:', JSON.stringify(request, null, 2))
|
|
29
|
+
}
|
|
30
|
+
return request
|
|
31
|
+
},
|
|
32
|
+
function (error) {
|
|
33
|
+
return Promise.reject(error)
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
client.interceptors.response.use(
|
|
38
|
+
function (response) {
|
|
39
|
+
if (config.debug) {
|
|
40
|
+
const data =
|
|
41
|
+
isObject(response.data) || Array.isArray(response.data)
|
|
42
|
+
? JSON.stringify(response.data)
|
|
43
|
+
: response.data.toString()
|
|
44
|
+
console.log(
|
|
45
|
+
'Response:',
|
|
46
|
+
JSON.stringify(
|
|
47
|
+
{
|
|
48
|
+
headers: response.headers,
|
|
49
|
+
data,
|
|
50
|
+
cached: response.cached
|
|
51
|
+
},
|
|
52
|
+
null,
|
|
53
|
+
2
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
clearTimeout(timeout)
|
|
59
|
+
return response
|
|
60
|
+
},
|
|
61
|
+
function (error) {
|
|
62
|
+
clearTimeout(timeout)
|
|
63
|
+
return Promise.reject(error)
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
return client
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function buildRequest({ channel, date, config }) {
|
|
71
|
+
date = typeof date === 'string' ? getUTCDate(date) : date
|
|
72
|
+
const CancelToken = axios.CancelToken
|
|
73
|
+
const source = CancelToken.source()
|
|
74
|
+
const request = { ...config.request }
|
|
75
|
+
timeout = setTimeout(() => {
|
|
76
|
+
source.cancel('Connection timeout')
|
|
77
|
+
}, request.timeout)
|
|
78
|
+
request.headers = await getRequestHeaders({ channel, date, config })
|
|
79
|
+
request.url = await getRequestUrl({ channel, date, config })
|
|
80
|
+
request.data = await getRequestData({ channel, date, config })
|
|
81
|
+
request.cancelToken = source.token
|
|
82
|
+
|
|
83
|
+
if (config.curl) {
|
|
84
|
+
const curl = CurlGenerator({
|
|
85
|
+
url: request.url,
|
|
86
|
+
method: request.method,
|
|
87
|
+
headers: request.headers,
|
|
88
|
+
body: request.data
|
|
89
|
+
})
|
|
90
|
+
console.log(curl)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return request
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function parseResponse(response) {
|
|
97
|
+
return {
|
|
98
|
+
content: response.data.toString(),
|
|
99
|
+
buffer: response.data,
|
|
100
|
+
headers: response.headers,
|
|
101
|
+
request: response.request,
|
|
102
|
+
cached: response.cached
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function getRequestHeaders({ channel, date, config }) {
|
|
107
|
+
if (typeof config.request.headers === 'function') {
|
|
108
|
+
const headers = config.request.headers({ channel, date })
|
|
109
|
+
if (isPromise(headers)) {
|
|
110
|
+
return await headers
|
|
111
|
+
}
|
|
112
|
+
return headers
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return config.request.headers || null
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function getRequestData({ channel, date, config }) {
|
|
119
|
+
if (typeof config.request.data === 'function') {
|
|
120
|
+
const data = config.request.data({ channel, date })
|
|
121
|
+
if (isPromise(data)) {
|
|
122
|
+
return await data
|
|
123
|
+
}
|
|
124
|
+
return data
|
|
125
|
+
}
|
|
126
|
+
return config.request.data || null
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function getRequestUrl({ channel, date, config }) {
|
|
130
|
+
if (typeof config.url === 'function') {
|
|
131
|
+
const url = config.url({ channel, date })
|
|
132
|
+
if (isPromise(url)) {
|
|
133
|
+
return await url
|
|
134
|
+
}
|
|
135
|
+
return url
|
|
136
|
+
}
|
|
137
|
+
return config.url
|
|
138
|
+
}
|
package/src/config.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const tough = require('tough-cookie')
|
|
2
|
+
const { merge } = require('lodash')
|
|
3
|
+
|
|
4
|
+
module.exports.load = load
|
|
5
|
+
|
|
6
|
+
function load(config) {
|
|
7
|
+
if (!config.site) throw new Error("The required 'site' property is missing")
|
|
8
|
+
if (!config.url) throw new Error("The required 'url' property is missing")
|
|
9
|
+
if (typeof config.url !== 'function' && typeof config.url !== 'string')
|
|
10
|
+
throw new Error("The 'url' property should return the function or string")
|
|
11
|
+
if (!config.parser) throw new Error("The required 'parser' function is missing")
|
|
12
|
+
if (typeof config.parser !== 'function')
|
|
13
|
+
throw new Error("The 'parser' property should return the function")
|
|
14
|
+
if (config.logo && typeof config.logo !== 'function')
|
|
15
|
+
throw new Error("The 'logo' property should return the function")
|
|
16
|
+
|
|
17
|
+
const defaultConfig = {
|
|
18
|
+
days: 1,
|
|
19
|
+
lang: 'en',
|
|
20
|
+
delay: 3000,
|
|
21
|
+
output: 'guide.xml',
|
|
22
|
+
request: {
|
|
23
|
+
method: 'GET',
|
|
24
|
+
maxContentLength: 5 * 1024 * 1024,
|
|
25
|
+
timeout: 5000,
|
|
26
|
+
withCredentials: true,
|
|
27
|
+
jar: new tough.CookieJar(),
|
|
28
|
+
responseType: 'arraybuffer',
|
|
29
|
+
cache: false
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return merge(defaultConfig, config)
|
|
34
|
+
}
|
package/src/file.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
|
|
4
|
+
module.exports.read = read
|
|
5
|
+
module.exports.write = write
|
|
6
|
+
module.exports.resolve = resolve
|
|
7
|
+
module.exports.join = join
|
|
8
|
+
module.exports.dirname = dirname
|
|
9
|
+
|
|
10
|
+
function read(filepath) {
|
|
11
|
+
return fs.readFileSync(path.resolve(filepath), { encoding: 'utf-8' })
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function write(filepath, data) {
|
|
15
|
+
const dir = path.resolve(path.dirname(filepath))
|
|
16
|
+
if (!fs.existsSync(dir)) {
|
|
17
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
fs.writeFileSync(path.resolve(filepath), data)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function resolve(filepath) {
|
|
24
|
+
return path.resolve(filepath)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function join(path1, path2) {
|
|
28
|
+
return path.join(path1, path2)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function dirname(filepath) {
|
|
32
|
+
return path.dirname(filepath)
|
|
33
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,41 +1,48 @@
|
|
|
1
|
-
const
|
|
1
|
+
const { merge } = require('lodash')
|
|
2
|
+
const { create: createClient, buildRequest, parseResponse } = require('./client')
|
|
3
|
+
const { generate: generateXMLTV } = require('./xmltv')
|
|
4
|
+
const { parse: parseChannels } = require('./channels')
|
|
5
|
+
const { parse: parsePrograms } = require('./programs')
|
|
6
|
+
const { load: loadConfig } = require('./config')
|
|
7
|
+
const { sleep, isPromise } = require('./utils')
|
|
8
|
+
|
|
9
|
+
module.exports.generateXMLTV = generateXMLTV
|
|
10
|
+
module.exports.parseChannels = parseChannels
|
|
2
11
|
|
|
3
12
|
class EPGGrabber {
|
|
4
13
|
constructor(config = {}) {
|
|
5
|
-
this.config =
|
|
6
|
-
this.client =
|
|
14
|
+
this.config = loadConfig(config)
|
|
15
|
+
this.client = createClient(config)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async loadLogo(channel) {
|
|
19
|
+
const logo = this.config.logo({ channel })
|
|
20
|
+
if (isPromise(logo)) {
|
|
21
|
+
return await logo
|
|
22
|
+
}
|
|
23
|
+
return logo
|
|
7
24
|
}
|
|
8
25
|
|
|
9
26
|
async grab(channel, date, cb = () => {}) {
|
|
10
|
-
|
|
11
|
-
channel.lang = channel.lang || this.config.lang || null
|
|
12
|
-
|
|
13
|
-
let programs = []
|
|
14
|
-
const item = { date, channel }
|
|
15
|
-
await utils
|
|
16
|
-
.buildRequest(item, this.config)
|
|
17
|
-
.then(request => utils.fetchData(this.client, request))
|
|
18
|
-
.then(response => utils.parseResponse(item, response, this.config))
|
|
19
|
-
.then(results => {
|
|
20
|
-
item.programs = results
|
|
21
|
-
cb(item, null)
|
|
22
|
-
programs = programs.concat(results)
|
|
23
|
-
})
|
|
24
|
-
.catch(error => {
|
|
25
|
-
item.programs = []
|
|
26
|
-
if (this.config.debug) {
|
|
27
|
-
console.log('Error:', JSON.stringify(error, null, 2))
|
|
28
|
-
}
|
|
29
|
-
cb(item, error)
|
|
30
|
-
})
|
|
27
|
+
await sleep(this.config.delay)
|
|
31
28
|
|
|
32
|
-
|
|
29
|
+
return buildRequest({ channel, date, config: this.config })
|
|
30
|
+
.then(this.client)
|
|
31
|
+
.then(parseResponse)
|
|
32
|
+
.then(data => merge({ channel, date, config: this.config }, data))
|
|
33
|
+
.then(parsePrograms)
|
|
34
|
+
.then(programs => {
|
|
35
|
+
cb({ channel, date, programs })
|
|
33
36
|
|
|
34
|
-
|
|
37
|
+
return programs
|
|
38
|
+
})
|
|
39
|
+
.catch(err => {
|
|
40
|
+
if (this.config.debug) console.log('Error:', JSON.stringify(err, null, 2))
|
|
41
|
+
cb({ channel, date, programs: [] }, err)
|
|
42
|
+
|
|
43
|
+
return []
|
|
44
|
+
})
|
|
35
45
|
}
|
|
36
46
|
}
|
|
37
47
|
|
|
38
|
-
EPGGrabber
|
|
39
|
-
EPGGrabber.parseChannels = utils.parseChannels
|
|
40
|
-
|
|
41
|
-
module.exports = EPGGrabber
|
|
48
|
+
module.exports.EPGGrabber = EPGGrabber
|
package/src/logger.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const { createLogger, format, transports } = require('winston')
|
|
2
|
+
const { combine, timestamp, printf } = format
|
|
3
|
+
const path = require('path')
|
|
4
|
+
|
|
5
|
+
module.exports.create = create
|
|
6
|
+
|
|
7
|
+
function create(options) {
|
|
8
|
+
const fileFormat = printf(({ level, message, timestamp }) => {
|
|
9
|
+
return `[${timestamp}] ${level.toUpperCase()}: ${message}`
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
const consoleFormat = printf(({ level, message }) => {
|
|
13
|
+
if (level === 'error') return ` Error: ${message}`
|
|
14
|
+
|
|
15
|
+
return message
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const t = [new transports.Console({ format: consoleFormat })]
|
|
19
|
+
|
|
20
|
+
if (options.log) {
|
|
21
|
+
t.push(
|
|
22
|
+
new transports.File({
|
|
23
|
+
filename: path.resolve(options.log),
|
|
24
|
+
format: combine(timestamp(), fileFormat),
|
|
25
|
+
options: { flags: 'w' }
|
|
26
|
+
})
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return createLogger({
|
|
31
|
+
level: options.logLevel,
|
|
32
|
+
transports: t
|
|
33
|
+
})
|
|
34
|
+
}
|
package/src/programs.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const { isPromise } = require('./utils')
|
|
2
|
+
|
|
3
|
+
module.exports.parse = parse
|
|
4
|
+
|
|
5
|
+
async function parse(data) {
|
|
6
|
+
const { config, channel } = data
|
|
7
|
+
let programs = config.parser(data)
|
|
8
|
+
|
|
9
|
+
if (isPromise(programs)) {
|
|
10
|
+
programs = await programs
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (!Array.isArray(programs)) {
|
|
14
|
+
throw new Error('Parser should return an array')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return programs
|
|
18
|
+
.filter(i => i)
|
|
19
|
+
.map(program => {
|
|
20
|
+
program.channel = channel.xmltv_id
|
|
21
|
+
|
|
22
|
+
return program
|
|
23
|
+
})
|
|
24
|
+
}
|