epg-grabber 0.25.3 → 0.27.0
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 +5 -7
- package/bin/epg-grabber.js +4 -7
- package/package.json +2 -2
- package/src/utils.js +156 -26
- package/tests/utils.test.js +177 -29
package/README.md
CHANGED
|
@@ -80,7 +80,7 @@ Arguments:
|
|
|
80
80
|
- `--days`: number of days for which to grab the program (default: 1)
|
|
81
81
|
- `--delay`: delay between requests (default: 3000)
|
|
82
82
|
- `--timeout`: set a timeout for each request (default: 5000)
|
|
83
|
-
- `--cache-
|
|
83
|
+
- `--cache-ttl`: maximum time for storing each request in milliseconds (default: 0)
|
|
84
84
|
- `--gzip`: compress the output (default: false)
|
|
85
85
|
- `--debug`: enable debug mode (default: false)
|
|
86
86
|
- `--curl`: display current request as CURL (default: false)
|
|
@@ -102,12 +102,8 @@ module.exports = {
|
|
|
102
102
|
|
|
103
103
|
method: 'GET',
|
|
104
104
|
timeout: 5000,
|
|
105
|
-
cache: { // cache options (details: https://
|
|
106
|
-
|
|
107
|
-
readHeaders: false,
|
|
108
|
-
exclude: {
|
|
109
|
-
query: false
|
|
110
|
-
}
|
|
105
|
+
cache: { // cache options (details: https://axios-cache-interceptor.js.org/#/pages/per-request-configuration)
|
|
106
|
+
ttl: 60 * 1000 // 60s
|
|
111
107
|
},
|
|
112
108
|
|
|
113
109
|
/**
|
|
@@ -192,6 +188,8 @@ From each function in `config.js` you can access a `context` object containing t
|
|
|
192
188
|
- `content`: The response data as a String
|
|
193
189
|
- `buffer`: The response data as an ArrayBuffer
|
|
194
190
|
- `headers`: The response headers
|
|
191
|
+
- `request`: The request config
|
|
192
|
+
- `cached`: A boolean to check whether this request was cached or not
|
|
195
193
|
|
|
196
194
|
## Channels List
|
|
197
195
|
|
package/bin/epg-grabber.js
CHANGED
|
@@ -24,7 +24,7 @@ program
|
|
|
24
24
|
.option('--delay <delay>', 'Delay between requests (in mileseconds)', parseInteger)
|
|
25
25
|
.option('--timeout <timeout>', 'Set a timeout for each request (in mileseconds)', parseInteger)
|
|
26
26
|
.option(
|
|
27
|
-
'--cache-
|
|
27
|
+
'--cache-ttl <cacheTtl>',
|
|
28
28
|
'Maximum time for storing each request (in milliseconds)',
|
|
29
29
|
parseInteger
|
|
30
30
|
)
|
|
@@ -76,14 +76,11 @@ async function main() {
|
|
|
76
76
|
curl: options.curl,
|
|
77
77
|
lang: options.lang,
|
|
78
78
|
delay: options.delay,
|
|
79
|
-
request: {
|
|
80
|
-
timeout: options.timeout,
|
|
81
|
-
cache: {
|
|
82
|
-
maxAge: options.cacheMaxAge
|
|
83
|
-
}
|
|
84
|
-
}
|
|
79
|
+
request: {}
|
|
85
80
|
})
|
|
86
81
|
|
|
82
|
+
if (options.timeout) config.request.timeout = options.timeout
|
|
83
|
+
if (options.cacheTtl) config.request.cache.ttl = options.cacheTtl
|
|
87
84
|
if (options.channels) config.channels = options.channels
|
|
88
85
|
else if (config.channels)
|
|
89
86
|
config.channels = path.join(path.dirname(options.config), config.channels)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "epg-grabber",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "Node.js CLI tool for grabbing EPG from different sites",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"preferGlobal": true,
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"axios": "^0.21.1",
|
|
32
|
-
"axios-cache-
|
|
32
|
+
"axios-cache-interceptor": "^0.10.3",
|
|
33
33
|
"axios-cookiejar-support": "^1.0.1",
|
|
34
34
|
"axios-mock-adapter": "^1.20.0",
|
|
35
35
|
"commander": "^7.1.0",
|
package/src/utils.js
CHANGED
|
@@ -3,7 +3,7 @@ const { padStart } = require('lodash')
|
|
|
3
3
|
const path = require('path')
|
|
4
4
|
const axios = require('axios').default
|
|
5
5
|
const axiosCookieJarSupport = require('axios-cookiejar-support').default
|
|
6
|
-
const
|
|
6
|
+
const { setupCache } = require('axios-cache-interceptor')
|
|
7
7
|
const tough = require('tough-cookie')
|
|
8
8
|
const convert = require('xml-js')
|
|
9
9
|
const { merge } = require('lodash')
|
|
@@ -41,13 +41,7 @@ utils.loadConfig = function (config) {
|
|
|
41
41
|
withCredentials: true,
|
|
42
42
|
jar: new tough.CookieJar(),
|
|
43
43
|
responseType: 'arraybuffer',
|
|
44
|
-
cache:
|
|
45
|
-
readHeaders: false,
|
|
46
|
-
exclude: {
|
|
47
|
-
query: false
|
|
48
|
-
},
|
|
49
|
-
maxAge: 0
|
|
50
|
-
}
|
|
44
|
+
cache: false
|
|
51
45
|
}
|
|
52
46
|
}
|
|
53
47
|
|
|
@@ -55,7 +49,7 @@ utils.loadConfig = function (config) {
|
|
|
55
49
|
}
|
|
56
50
|
|
|
57
51
|
utils.createClient = function (config) {
|
|
58
|
-
const client =
|
|
52
|
+
const client = setupCache(axios.create())
|
|
59
53
|
client.interceptors.request.use(
|
|
60
54
|
function (request) {
|
|
61
55
|
if (config.debug) {
|
|
@@ -80,7 +74,7 @@ utils.createClient = function (config) {
|
|
|
80
74
|
{
|
|
81
75
|
headers: response.headers,
|
|
82
76
|
data,
|
|
83
|
-
|
|
77
|
+
cached: response.cached
|
|
84
78
|
},
|
|
85
79
|
null,
|
|
86
80
|
2
|
|
@@ -156,7 +150,9 @@ utils.escapeString = function (string, defaultValue = '') {
|
|
|
156
150
|
}
|
|
157
151
|
|
|
158
152
|
utils.convertToXMLTV = function ({ channels, programs }) {
|
|
159
|
-
let output = `<?xml version="1.0" encoding="UTF-8" ?><tv
|
|
153
|
+
let output = `<?xml version="1.0" encoding="UTF-8" ?><tv date="${dayjs
|
|
154
|
+
.utc()
|
|
155
|
+
.format('YYYYMMDD')}">\r\n`
|
|
160
156
|
for (let channel of channels) {
|
|
161
157
|
const id = utils.escapeString(channel['xmltv_id'])
|
|
162
158
|
const displayName = utils.escapeString(channel.name)
|
|
@@ -184,11 +180,30 @@ utils.convertToXMLTV = function ({ channels, programs }) {
|
|
|
184
180
|
const lang = program.lang || 'en'
|
|
185
181
|
const xmltv_ns = createXMLTVNS(program.season, program.episode)
|
|
186
182
|
const onscreen = createOnScreen(program.season, program.episode)
|
|
183
|
+
const date = program.date || ''
|
|
184
|
+
const credits = createCredits({
|
|
185
|
+
director: program.director,
|
|
186
|
+
actor: program.actor,
|
|
187
|
+
writer: program.writer,
|
|
188
|
+
adapter: program.adapter,
|
|
189
|
+
producer: program.producer,
|
|
190
|
+
composer: program.composer,
|
|
191
|
+
editor: program.editor,
|
|
192
|
+
presenter: program.presenter,
|
|
193
|
+
commentator: program.commentator,
|
|
194
|
+
guest: program.guest
|
|
195
|
+
})
|
|
187
196
|
const icon = utils.escapeString(program.icon)
|
|
197
|
+
const sub_title = program.sub_title || ''
|
|
198
|
+
const url = program.url ? createURL(program.url, channel) : ''
|
|
188
199
|
|
|
189
200
|
if (start && stop && title) {
|
|
190
201
|
output += `<programme start="${start}" stop="${stop}" channel="${channel}"><title lang="${lang}">${title}</title>`
|
|
191
202
|
|
|
203
|
+
if (sub_title) {
|
|
204
|
+
output += `<sub-title>${sub_title}</sub-title>`
|
|
205
|
+
}
|
|
206
|
+
|
|
192
207
|
if (description) {
|
|
193
208
|
output += `<desc lang="${lang}">${description}</desc>`
|
|
194
209
|
}
|
|
@@ -201,6 +216,10 @@ utils.convertToXMLTV = function ({ channels, programs }) {
|
|
|
201
216
|
})
|
|
202
217
|
}
|
|
203
218
|
|
|
219
|
+
if (url) {
|
|
220
|
+
output += url
|
|
221
|
+
}
|
|
222
|
+
|
|
204
223
|
if (xmltv_ns) {
|
|
205
224
|
output += `<episode-num system="xmltv_ns">${xmltv_ns}</episode-num>`
|
|
206
225
|
}
|
|
@@ -208,11 +227,18 @@ utils.convertToXMLTV = function ({ channels, programs }) {
|
|
|
208
227
|
if (onscreen) {
|
|
209
228
|
output += `<episode-num system="onscreen">${onscreen}</episode-num>`
|
|
210
229
|
}
|
|
230
|
+
if (date) {
|
|
231
|
+
output += `<date>${date}</date>`
|
|
232
|
+
}
|
|
211
233
|
|
|
212
234
|
if (icon) {
|
|
213
235
|
output += `<icon src="${icon}"/>`
|
|
214
236
|
}
|
|
215
237
|
|
|
238
|
+
if (credits) {
|
|
239
|
+
output += `<credits>${credits}</credits>`
|
|
240
|
+
}
|
|
241
|
+
|
|
216
242
|
output += '</programme>\r\n'
|
|
217
243
|
}
|
|
218
244
|
}
|
|
@@ -220,14 +246,14 @@ utils.convertToXMLTV = function ({ channels, programs }) {
|
|
|
220
246
|
output += '</tv>'
|
|
221
247
|
|
|
222
248
|
function createXMLTVNS(s, e) {
|
|
223
|
-
if (!
|
|
249
|
+
if (!e) return null
|
|
224
250
|
s = s || 1
|
|
225
251
|
|
|
226
252
|
return `${s - 1}.${e - 1}.0/1`
|
|
227
253
|
}
|
|
228
254
|
|
|
229
255
|
function createOnScreen(s, e) {
|
|
230
|
-
if (!
|
|
256
|
+
if (!e) return null
|
|
231
257
|
s = s || 1
|
|
232
258
|
|
|
233
259
|
s = padStart(s, 2, '0')
|
|
@@ -236,6 +262,105 @@ utils.convertToXMLTV = function ({ channels, programs }) {
|
|
|
236
262
|
return `S${s}E${e}`
|
|
237
263
|
}
|
|
238
264
|
|
|
265
|
+
function createURL(urlObj, channel = '') {
|
|
266
|
+
const urls = Array.isArray(urlObj) ? urlObj : [urlObj]
|
|
267
|
+
let output = ''
|
|
268
|
+
for (let url of urls) {
|
|
269
|
+
if (typeof url === 'string' || url instanceof String) {
|
|
270
|
+
url = { value: url }
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
let attr = url.system ? ` system="${url.system}"` : ''
|
|
274
|
+
if (url.value.includes('http')) {
|
|
275
|
+
output += `<url${attr}>${url.value}</url>`
|
|
276
|
+
} else if (channel) {
|
|
277
|
+
let chan = channels.find(c => c.xmltv_id.localeCompare(channel) === 0)
|
|
278
|
+
if (chan && chan.site) {
|
|
279
|
+
output += `<url${attr}>https://${chan.site}${url.value}</url>`
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return output
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function createImage(imgObj, channel = '') {
|
|
288
|
+
const imgs = Array.isArray(imgObj) ? imgObj : [imgObj]
|
|
289
|
+
let output = ''
|
|
290
|
+
for (let img of imgs) {
|
|
291
|
+
if (typeof img === 'string' || img instanceof String) {
|
|
292
|
+
img = { value: img }
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const imageTypes = ['poster', 'backdrop', 'still', 'person', 'character']
|
|
296
|
+
const imageSizes = ['1', '2', '3']
|
|
297
|
+
const imageOrients = ['P', 'L']
|
|
298
|
+
|
|
299
|
+
let attr = ''
|
|
300
|
+
|
|
301
|
+
if (img.type && imageTypes.some(el => img.type.includes(el))) {
|
|
302
|
+
attr += ` type="${img.type}"`
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (img.size && imageSizes.some(el => img.size.includes(el))) {
|
|
306
|
+
attr += ` size="${img.size}"`
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (img.orient && imageOrients.some(el => img.orient.includes(el))) {
|
|
310
|
+
attr += ` orient="${img.orient}"`
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (img.system) {
|
|
314
|
+
attr += ` system="${img.system}"`
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (img.value.includes('http')) {
|
|
318
|
+
output += `<image${attr}>${img.value}</image>`
|
|
319
|
+
} else if (channel) {
|
|
320
|
+
let chan = channels.find(c => c.xmltv_id.localeCompare(channel) === 0)
|
|
321
|
+
if (chan && chan.site) {
|
|
322
|
+
output += `<image${attr}>https://${chan.site}${img.value}</image>`
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return output
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function createCredits(obj) {
|
|
331
|
+
let cast = Object.entries(obj)
|
|
332
|
+
.filter(x => x[1])
|
|
333
|
+
.map(([name, value]) => ({ name, value }))
|
|
334
|
+
|
|
335
|
+
let output = ''
|
|
336
|
+
for (let type of cast) {
|
|
337
|
+
const r = Array.isArray(type.value) ? type.value : [type.value]
|
|
338
|
+
for (let person of r) {
|
|
339
|
+
if (typeof person === 'string' || person instanceof String) {
|
|
340
|
+
person = { value: person }
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
let attr = ''
|
|
344
|
+
if (type.name.localeCompare('actor') === 0 && type.value.role) {
|
|
345
|
+
attr += ` role="${type.value.role}"`
|
|
346
|
+
}
|
|
347
|
+
if (type.name.localeCompare('actor') === 0 && type.value.guest) {
|
|
348
|
+
attr += ` guest="${type.value.guest}"`
|
|
349
|
+
}
|
|
350
|
+
output += `<${type.name}${attr}>${person.value}`
|
|
351
|
+
|
|
352
|
+
if (person.url) {
|
|
353
|
+
output += createURL(person.url)
|
|
354
|
+
}
|
|
355
|
+
if (person.image) {
|
|
356
|
+
output += createImage(person.image)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
output += `</${type.name}>`
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return output
|
|
363
|
+
}
|
|
239
364
|
return output
|
|
240
365
|
}
|
|
241
366
|
|
|
@@ -318,20 +443,12 @@ utils.getUTCDate = function (d = null) {
|
|
|
318
443
|
}
|
|
319
444
|
|
|
320
445
|
utils.parseResponse = async (item, response, config) => {
|
|
321
|
-
let buffer
|
|
322
|
-
let content
|
|
323
|
-
if (utils.isObject(response.data) || Array.isArray(response.data)) {
|
|
324
|
-
content = JSON.stringify(response.data)
|
|
325
|
-
buffer = Buffer.from(content, 'utf8')
|
|
326
|
-
} else {
|
|
327
|
-
content = response.data.toString()
|
|
328
|
-
buffer = response.data
|
|
329
|
-
}
|
|
330
446
|
const data = merge(item, config, {
|
|
331
|
-
content,
|
|
332
|
-
buffer,
|
|
447
|
+
content: response.data.toString(),
|
|
448
|
+
buffer: response.data,
|
|
333
449
|
headers: response.headers,
|
|
334
|
-
request: response.request
|
|
450
|
+
request: response.request,
|
|
451
|
+
cached: response.cached
|
|
335
452
|
})
|
|
336
453
|
|
|
337
454
|
if (!item.channel.logo && config.logo) {
|
|
@@ -362,11 +479,24 @@ utils.parsePrograms = async function (data, config) {
|
|
|
362
479
|
category: program.category || null,
|
|
363
480
|
season: program.season || null,
|
|
364
481
|
episode: program.episode || null,
|
|
482
|
+
sub_title: program.sub_title || null,
|
|
483
|
+
url: program.url || null,
|
|
365
484
|
icon: program.icon || null,
|
|
366
485
|
channel: channel.xmltv_id,
|
|
367
486
|
lang: program.lang || channel.lang || config.lang || 'en',
|
|
368
487
|
start: program.start ? dayjs(program.start).unix() : null,
|
|
369
|
-
stop: program.stop ? dayjs(program.stop).unix() : null
|
|
488
|
+
stop: program.stop ? dayjs(program.stop).unix() : null,
|
|
489
|
+
date: program.date || null,
|
|
490
|
+
director: program.director || null,
|
|
491
|
+
actor: program.actor || null,
|
|
492
|
+
writer: program.writer || null,
|
|
493
|
+
adapter: program.adapter || null,
|
|
494
|
+
producer: program.producer || null,
|
|
495
|
+
composer: program.composer || null,
|
|
496
|
+
editor: program.editor || null,
|
|
497
|
+
presenter: program.presenter || null,
|
|
498
|
+
commentator: program.commentator || null,
|
|
499
|
+
guest: program.guest || null
|
|
370
500
|
}
|
|
371
501
|
})
|
|
372
502
|
}
|
package/tests/utils.test.js
CHANGED
|
@@ -4,6 +4,8 @@ import axios from 'axios'
|
|
|
4
4
|
import path from 'path'
|
|
5
5
|
import fs from 'fs'
|
|
6
6
|
|
|
7
|
+
jest.useFakeTimers('modern').setSystemTime(new Date('2022-05-05'))
|
|
8
|
+
|
|
7
9
|
it('can load valid config.js', () => {
|
|
8
10
|
const config = utils.loadConfig(require(path.resolve('./tests/input/example.com.config.js')))
|
|
9
11
|
expect(config).toMatchObject({
|
|
@@ -56,7 +58,9 @@ it('can convert object to xmltv string', () => {
|
|
|
56
58
|
const programs = [
|
|
57
59
|
{
|
|
58
60
|
title: 'Program 1',
|
|
61
|
+
sub_title: 'Sub-title 1',
|
|
59
62
|
description: 'Description for Program 1',
|
|
63
|
+
url: 'http://example.com/title.html',
|
|
60
64
|
start: 1616133600,
|
|
61
65
|
stop: 1616135400,
|
|
62
66
|
category: 'Test',
|
|
@@ -64,12 +68,19 @@ it('can convert object to xmltv string', () => {
|
|
|
64
68
|
episode: 239,
|
|
65
69
|
icon: 'https://example.com/images/Program1.png?x=шеллы&sid=777',
|
|
66
70
|
channel: '1TV.com',
|
|
67
|
-
lang: 'it'
|
|
71
|
+
lang: 'it',
|
|
72
|
+
date: '20220505',
|
|
73
|
+
director: {
|
|
74
|
+
value: 'Director 1',
|
|
75
|
+
url: { value: 'http://example.com/director1.html', system: 'TestSystem' }
|
|
76
|
+
},
|
|
77
|
+
actor: ['Actor 1', 'Actor 2'],
|
|
78
|
+
writer: 'Writer 1'
|
|
68
79
|
}
|
|
69
80
|
]
|
|
70
81
|
const output = utils.convertToXMLTV({ channels, programs })
|
|
71
82
|
expect(output).toBe(
|
|
72
|
-
'<?xml version="1.0" encoding="UTF-8" ?><tv>\r\n<channel id="1TV.com"><display-name>1 TV</display-name><icon src="https://example.com/logos/1TV.png"/><url>https://example.com</url></channel>\r\n<channel id="2TV.com"><display-name>2 TV</display-name><url>https://example.com</url></channel>\r\n<programme start="20210319060000 +0000" stop="20210319063000 +0000" channel="1TV.com"><title lang="it">Program 1</title><desc lang="it">Description for Program 1</desc><category lang="it">Test</category><episode-num system="xmltv_ns">8.238.0/1</episode-num><episode-num system="onscreen">S09E239</episode-num><icon src="https://example.com/images/Program1.png?x=шеллы&sid=777"
|
|
83
|
+
'<?xml version="1.0" encoding="UTF-8" ?><tv date="20220505">\r\n<channel id="1TV.com"><display-name>1 TV</display-name><icon src="https://example.com/logos/1TV.png"/><url>https://example.com</url></channel>\r\n<channel id="2TV.com"><display-name>2 TV</display-name><url>https://example.com</url></channel>\r\n<programme start="20210319060000 +0000" stop="20210319063000 +0000" channel="1TV.com"><title lang="it">Program 1</title><sub-title>Sub-title 1</sub-title><desc lang="it">Description for Program 1</desc><category lang="it">Test</category><url>http://example.com/title.html</url><episode-num system="xmltv_ns">8.238.0/1</episode-num><episode-num system="onscreen">S09E239</episode-num><date>20220505</date><icon src="https://example.com/images/Program1.png?x=шеллы&sid=777"/><credits><director>Director 1<url system="TestSystem">http://example.com/director1.html</url></director><actor>Actor 1</actor><actor>Actor 2</actor><writer>Writer 1</writer></credits></programme>\r\n</tv>'
|
|
73
84
|
)
|
|
74
85
|
})
|
|
75
86
|
|
|
@@ -91,7 +102,29 @@ it('can convert object to xmltv string without season number', () => {
|
|
|
91
102
|
]
|
|
92
103
|
const output = utils.convertToXMLTV({ channels, programs })
|
|
93
104
|
expect(output).toBe(
|
|
94
|
-
'<?xml version="1.0" encoding="UTF-8" ?><tv>\r\n<channel id="1TV.com"><display-name>1 TV</display-name><icon src="https://example.com/logos/1TV.png"/><url>https://example.com</url></channel>\r\n<channel id="2TV.com"><display-name>2 TV</display-name><url>https://example.com</url></channel>\r\n<programme start="20210319060000 +0000" stop="20210319063000 +0000" channel="1TV.com"><title lang="it">Program 1</title><desc lang="it">Description for Program 1</desc><category lang="it">Test</category><episode-num system="xmltv_ns">0.238.0/1</episode-num><episode-num system="onscreen">S01E239</episode-num><icon src="https://example.com/images/Program1.png?x=шеллы&sid=777"/></programme>\r\n</tv>'
|
|
105
|
+
'<?xml version="1.0" encoding="UTF-8" ?><tv date="20220505">\r\n<channel id="1TV.com"><display-name>1 TV</display-name><icon src="https://example.com/logos/1TV.png"/><url>https://example.com</url></channel>\r\n<channel id="2TV.com"><display-name>2 TV</display-name><url>https://example.com</url></channel>\r\n<programme start="20210319060000 +0000" stop="20210319063000 +0000" channel="1TV.com"><title lang="it">Program 1</title><desc lang="it">Description for Program 1</desc><category lang="it">Test</category><episode-num system="xmltv_ns">0.238.0/1</episode-num><episode-num system="onscreen">S01E239</episode-num><icon src="https://example.com/images/Program1.png?x=шеллы&sid=777"/></programme>\r\n</tv>'
|
|
106
|
+
)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('can convert object to xmltv string without episode number', () => {
|
|
110
|
+
const file = fs.readFileSync('./tests/input/example.com.channels.xml', { encoding: 'utf-8' })
|
|
111
|
+
const { channels } = utils.parseChannels(file)
|
|
112
|
+
const programs = [
|
|
113
|
+
{
|
|
114
|
+
title: 'Program 1',
|
|
115
|
+
description: 'Description for Program 1',
|
|
116
|
+
start: 1616133600,
|
|
117
|
+
stop: 1616135400,
|
|
118
|
+
category: 'Test',
|
|
119
|
+
season: 1,
|
|
120
|
+
icon: 'https://example.com/images/Program1.png?x=шеллы&sid=777',
|
|
121
|
+
channel: '1TV.com',
|
|
122
|
+
lang: 'it'
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
const output = utils.convertToXMLTV({ channels, programs })
|
|
126
|
+
expect(output).toBe(
|
|
127
|
+
'<?xml version="1.0" encoding="UTF-8" ?><tv date="20220505">\r\n<channel id="1TV.com"><display-name>1 TV</display-name><icon src="https://example.com/logos/1TV.png"/><url>https://example.com</url></channel>\r\n<channel id="2TV.com"><display-name>2 TV</display-name><url>https://example.com</url></channel>\r\n<programme start="20210319060000 +0000" stop="20210319063000 +0000" channel="1TV.com"><title lang="it">Program 1</title><desc lang="it">Description for Program 1</desc><category lang="it">Test</category><icon src="https://example.com/images/Program1.png?x=шеллы&sid=777"/></programme>\r\n</tv>'
|
|
95
128
|
)
|
|
96
129
|
})
|
|
97
130
|
|
|
@@ -118,7 +151,7 @@ it('can convert object to xmltv string without categories', () => {
|
|
|
118
151
|
const config = { site: 'example.com' }
|
|
119
152
|
const output = utils.convertToXMLTV({ config, channels, programs })
|
|
120
153
|
expect(output).toBe(
|
|
121
|
-
'<?xml version="1.0" encoding="UTF-8" ?><tv>\r\n<channel id="1TV.com"><display-name>1 TV</display-name><icon src="https://example.com/logos/1TV.png"/><url>https://example.com</url></channel>\r\n<programme start="20210319060000 +0000" stop="20210319063000 +0000" channel="1TV.com"><title lang="it">Program 1</title></programme>\r\n</tv>'
|
|
154
|
+
'<?xml version="1.0" encoding="UTF-8" ?><tv date="20220505">\r\n<channel id="1TV.com"><display-name>1 TV</display-name><icon src="https://example.com/logos/1TV.png"/><url>https://example.com</url></channel>\r\n<programme start="20210319060000 +0000" stop="20210319063000 +0000" channel="1TV.com"><title lang="it">Program 1</title></programme>\r\n</tv>'
|
|
122
155
|
)
|
|
123
156
|
})
|
|
124
157
|
|
|
@@ -139,7 +172,110 @@ it('can convert object to xmltv string with multiple categories', () => {
|
|
|
139
172
|
]
|
|
140
173
|
const output = utils.convertToXMLTV({ channels, programs })
|
|
141
174
|
expect(output).toBe(
|
|
142
|
-
'<?xml version="1.0" encoding="UTF-8" ?><tv>\r\n<channel id="1TV.com"><display-name>1 TV</display-name><icon src="https://example.com/logos/1TV.png"/><url>https://example.com</url></channel>\r\n<channel id="2TV.com"><display-name>2 TV</display-name><url>https://example.com</url></channel>\r\n<programme start="20210319060000 +0000" stop="20210319063000 +0000" channel="1TV.com"><title lang="it">Program 1</title><desc lang="it">Description for Program 1</desc><category lang="it">Test1</category><category lang="it">Test2</category><icon src="https://example.com/images/Program1.png?x=шеллы&sid=777"/></programme>\r\n</tv>'
|
|
175
|
+
'<?xml version="1.0" encoding="UTF-8" ?><tv date="20220505">\r\n<channel id="1TV.com"><display-name>1 TV</display-name><icon src="https://example.com/logos/1TV.png"/><url>https://example.com</url></channel>\r\n<channel id="2TV.com"><display-name>2 TV</display-name><url>https://example.com</url></channel>\r\n<programme start="20210319060000 +0000" stop="20210319063000 +0000" channel="1TV.com"><title lang="it">Program 1</title><desc lang="it">Description for Program 1</desc><category lang="it">Test1</category><category lang="it">Test2</category><icon src="https://example.com/images/Program1.png?x=шеллы&sid=777"/></programme>\r\n</tv>'
|
|
176
|
+
)
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('can convert object to xmltv string with multiple urls', () => {
|
|
180
|
+
const file = fs.readFileSync('./tests/input/example.com.channels.xml', { encoding: 'utf-8' })
|
|
181
|
+
const { channels } = utils.parseChannels(file)
|
|
182
|
+
const programs = [
|
|
183
|
+
{
|
|
184
|
+
title: 'Program 1',
|
|
185
|
+
description: 'Description for Program 1',
|
|
186
|
+
start: 1616133600,
|
|
187
|
+
stop: 1616135400,
|
|
188
|
+
category: ['Test1', 'Test2'],
|
|
189
|
+
url: [
|
|
190
|
+
'https://example.com/noattr.html',
|
|
191
|
+
{ value: 'https://example.com/attr.html', system: 'TestSystem' }
|
|
192
|
+
],
|
|
193
|
+
icon: 'https://example.com/images/Program1.png?x=шеллы&sid=777',
|
|
194
|
+
channel: '1TV.com',
|
|
195
|
+
lang: 'it'
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
const output = utils.convertToXMLTV({ channels, programs })
|
|
199
|
+
expect(output).toBe(
|
|
200
|
+
'<?xml version="1.0" encoding="UTF-8" ?><tv date="20220505">\r\n<channel id="1TV.com"><display-name>1 TV</display-name><icon src="https://example.com/logos/1TV.png"/><url>https://example.com</url></channel>\r\n<channel id="2TV.com"><display-name>2 TV</display-name><url>https://example.com</url></channel>\r\n<programme start="20210319060000 +0000" stop="20210319063000 +0000" channel="1TV.com"><title lang="it">Program 1</title><desc lang="it">Description for Program 1</desc><category lang="it">Test1</category><category lang="it">Test2</category><url>https://example.com/noattr.html</url><url system="TestSystem">https://example.com/attr.html</url><icon src="https://example.com/images/Program1.png?x=шеллы&sid=777"/></programme>\r\n</tv>'
|
|
201
|
+
)
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it('can convert object to xmltv string with multiple images', () => {
|
|
205
|
+
const file = fs.readFileSync('./tests/input/example.com.channels.xml', { encoding: 'utf-8' })
|
|
206
|
+
const { channels } = utils.parseChannels(file)
|
|
207
|
+
const programs = [
|
|
208
|
+
{
|
|
209
|
+
title: 'Program 1',
|
|
210
|
+
description: 'Description for Program 1',
|
|
211
|
+
start: 1616133600,
|
|
212
|
+
stop: 1616135400,
|
|
213
|
+
category: ['Test1', 'Test2'],
|
|
214
|
+
url: [
|
|
215
|
+
'https://example.com/noattr.html',
|
|
216
|
+
{ value: 'https://example.com/attr.html', system: 'TestSystem' }
|
|
217
|
+
],
|
|
218
|
+
actor: {
|
|
219
|
+
value: 'Actor 1',
|
|
220
|
+
image: [
|
|
221
|
+
'https://example.com/image1.jpg',
|
|
222
|
+
{
|
|
223
|
+
value: 'https://example.com/image2.jpg',
|
|
224
|
+
type: 'person',
|
|
225
|
+
size: '2',
|
|
226
|
+
system: 'TestSystem',
|
|
227
|
+
orient: 'P'
|
|
228
|
+
}
|
|
229
|
+
]
|
|
230
|
+
},
|
|
231
|
+
icon: 'https://example.com/images/Program1.png?x=шеллы&sid=777',
|
|
232
|
+
channel: '1TV.com',
|
|
233
|
+
lang: 'it'
|
|
234
|
+
}
|
|
235
|
+
]
|
|
236
|
+
const output = utils.convertToXMLTV({ channels, programs })
|
|
237
|
+
expect(output).toBe(
|
|
238
|
+
'<?xml version="1.0" encoding="UTF-8" ?><tv date="20220505">\r\n<channel id="1TV.com"><display-name>1 TV</display-name><icon src="https://example.com/logos/1TV.png"/><url>https://example.com</url></channel>\r\n<channel id="2TV.com"><display-name>2 TV</display-name><url>https://example.com</url></channel>\r\n<programme start="20210319060000 +0000" stop="20210319063000 +0000" channel="1TV.com"><title lang="it">Program 1</title><desc lang="it">Description for Program 1</desc><category lang="it">Test1</category><category lang="it">Test2</category><url>https://example.com/noattr.html</url><url system="TestSystem">https://example.com/attr.html</url><icon src="https://example.com/images/Program1.png?x=шеллы&sid=777"/><credits><actor>Actor 1<image>https://example.com/image1.jpg</image><image type="person" size="2" orient="P" system="TestSystem">https://example.com/image2.jpg</image></actor></credits></programme>\r\n</tv>'
|
|
239
|
+
)
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
it('can convert object to xmltv string with multiple credits member', () => {
|
|
243
|
+
const file = fs.readFileSync('./tests/input/example.com.channels.xml', { encoding: 'utf-8' })
|
|
244
|
+
const { channels } = utils.parseChannels(file)
|
|
245
|
+
const programs = [
|
|
246
|
+
{
|
|
247
|
+
title: 'Program 1',
|
|
248
|
+
sub_title: 'Sub-title 1',
|
|
249
|
+
description: 'Description for Program 1',
|
|
250
|
+
url: 'http://example.com/title.html',
|
|
251
|
+
start: 1616133600,
|
|
252
|
+
stop: 1616135400,
|
|
253
|
+
category: 'Test',
|
|
254
|
+
season: 9,
|
|
255
|
+
episode: 239,
|
|
256
|
+
icon: 'https://example.com/images/Program1.png?x=шеллы&sid=777',
|
|
257
|
+
channel: '1TV.com',
|
|
258
|
+
lang: 'it',
|
|
259
|
+
date: '20220505',
|
|
260
|
+
director: {
|
|
261
|
+
value: 'Director 1',
|
|
262
|
+
url: { value: 'http://example.com/director1.html', system: 'TestSystem' }
|
|
263
|
+
},
|
|
264
|
+
actor: {
|
|
265
|
+
value: 'Actor 1',
|
|
266
|
+
role: 'Manny',
|
|
267
|
+
guest: 'yes',
|
|
268
|
+
url: { value: 'http://example.com/actor1.html', system: 'TestSystem' }
|
|
269
|
+
},
|
|
270
|
+
writer: [
|
|
271
|
+
{ value: 'Writer 1', url: { value: 'http://example.com/w1.html', system: 'TestSystem' } },
|
|
272
|
+
{ value: 'Writer 2', url: { value: 'http://example.com/w2.html', system: 'TestSystem' } }
|
|
273
|
+
]
|
|
274
|
+
}
|
|
275
|
+
]
|
|
276
|
+
const output = utils.convertToXMLTV({ channels, programs })
|
|
277
|
+
expect(output).toBe(
|
|
278
|
+
'<?xml version="1.0" encoding="UTF-8" ?><tv date="20220505">\r\n<channel id="1TV.com"><display-name>1 TV</display-name><icon src="https://example.com/logos/1TV.png"/><url>https://example.com</url></channel>\r\n<channel id="2TV.com"><display-name>2 TV</display-name><url>https://example.com</url></channel>\r\n<programme start="20210319060000 +0000" stop="20210319063000 +0000" channel="1TV.com"><title lang="it">Program 1</title><sub-title>Sub-title 1</sub-title><desc lang="it">Description for Program 1</desc><category lang="it">Test</category><url>http://example.com/title.html</url><episode-num system="xmltv_ns">8.238.0/1</episode-num><episode-num system="onscreen">S09E239</episode-num><date>20220505</date><icon src="https://example.com/images/Program1.png?x=шеллы&sid=777"/><credits><director>Director 1<url system="TestSystem">http://example.com/director1.html</url></director><actor role="Manny" guest="yes">Actor 1<url system="TestSystem">http://example.com/actor1.html</url></actor><writer>Writer 1<url system="TestSystem">http://example.com/w1.html</url></writer><writer>Writer 2<url system="TestSystem">http://example.com/w2.html</url></writer></credits></programme>\r\n</tv>'
|
|
143
279
|
)
|
|
144
280
|
})
|
|
145
281
|
|
|
@@ -171,7 +307,7 @@ it('can fetch data', () => {
|
|
|
171
307
|
url: 'http://example.com/20210319/1tv.json',
|
|
172
308
|
withCredentials: true
|
|
173
309
|
}
|
|
174
|
-
utils.fetchData(
|
|
310
|
+
utils.fetchData(mockAxios, request).then(jest.fn).catch(jest.fn)
|
|
175
311
|
expect(mockAxios).toHaveBeenCalledWith(
|
|
176
312
|
expect.objectContaining({
|
|
177
313
|
data: { accountID: '123' },
|
|
@@ -192,37 +328,46 @@ it('can fetch data', () => {
|
|
|
192
328
|
|
|
193
329
|
it('can build request async', done => {
|
|
194
330
|
const config = utils.loadConfig(require(path.resolve('./tests/input/async.config.js')))
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
'
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
331
|
+
|
|
332
|
+
utils
|
|
333
|
+
.buildRequest({}, config)
|
|
334
|
+
.then(request => {
|
|
335
|
+
expect(request).toMatchObject({
|
|
336
|
+
data: { accountID: '123' },
|
|
337
|
+
headers: {
|
|
338
|
+
'Content-Type': 'application/json',
|
|
339
|
+
Cookie: 'abc=123',
|
|
340
|
+
'User-Agent':
|
|
341
|
+
'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'
|
|
342
|
+
},
|
|
343
|
+
maxContentLength: 5242880,
|
|
344
|
+
method: 'POST',
|
|
345
|
+
responseType: 'arraybuffer',
|
|
346
|
+
timeout: 5000,
|
|
347
|
+
url: 'http://example.com/20210319/1tv.json',
|
|
348
|
+
withCredentials: true
|
|
349
|
+
})
|
|
350
|
+
done()
|
|
210
351
|
})
|
|
211
|
-
done
|
|
212
|
-
})
|
|
352
|
+
.catch(done)
|
|
213
353
|
})
|
|
214
354
|
|
|
215
355
|
it('can load logo async', done => {
|
|
216
356
|
const config = utils.loadConfig(require(path.resolve('./tests/input/async.config.js')))
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
357
|
+
|
|
358
|
+
utils
|
|
359
|
+
.loadLogo({}, config)
|
|
360
|
+
.then(logo => {
|
|
361
|
+
expect(logo).toBe('http://example.com/logos/1TV.png?x=шеллы&sid=777')
|
|
362
|
+
done()
|
|
363
|
+
})
|
|
364
|
+
.catch(done)
|
|
221
365
|
})
|
|
222
366
|
|
|
223
367
|
it('can parse programs', done => {
|
|
224
368
|
const config = utils.loadConfig(require(path.resolve('./tests/input/example.com.config.js')))
|
|
225
|
-
|
|
369
|
+
|
|
370
|
+
utils
|
|
226
371
|
.parsePrograms({ channel: { xmltv_id: '1tv', lang: 'en' } }, config)
|
|
227
372
|
.then(programs => {
|
|
228
373
|
expect(programs).toMatchObject([
|
|
@@ -240,14 +385,17 @@ it('can parse programs', done => {
|
|
|
240
385
|
])
|
|
241
386
|
done()
|
|
242
387
|
})
|
|
388
|
+
.catch(done)
|
|
243
389
|
})
|
|
244
390
|
|
|
245
391
|
it('can parse programs async', done => {
|
|
246
392
|
const config = utils.loadConfig(require(path.resolve('./tests/input/async.config.js')))
|
|
247
|
-
|
|
393
|
+
|
|
394
|
+
utils
|
|
248
395
|
.parsePrograms({ channel: { xmltv_id: '1tv', lang: 'en' } }, config)
|
|
249
396
|
.then(programs => {
|
|
250
397
|
expect(programs.length).toBe(0)
|
|
251
398
|
done()
|
|
252
399
|
})
|
|
400
|
+
.catch(done)
|
|
253
401
|
})
|