epg-grabber 0.25.4 → 0.27.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 +5 -7
- package/bin/epg-grabber.js +4 -7
- package/package.json +2 -2
- package/src/utils.js +154 -24
- package/tests/utils.test.js +120 -6
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.1",
|
|
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 = utils.escapeString(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
|
}
|
|
@@ -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,7 @@ 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>'
|
|
95
106
|
)
|
|
96
107
|
})
|
|
97
108
|
|
|
@@ -113,7 +124,7 @@ it('can convert object to xmltv string without episode number', () => {
|
|
|
113
124
|
]
|
|
114
125
|
const output = utils.convertToXMLTV({ channels, programs })
|
|
115
126
|
expect(output).toBe(
|
|
116
|
-
'<?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><icon src="https://example.com/images/Program1.png?x=шеллы&sid=777"/></programme>\r\n</tv>'
|
|
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>'
|
|
117
128
|
)
|
|
118
129
|
})
|
|
119
130
|
|
|
@@ -140,7 +151,7 @@ it('can convert object to xmltv string without categories', () => {
|
|
|
140
151
|
const config = { site: 'example.com' }
|
|
141
152
|
const output = utils.convertToXMLTV({ config, channels, programs })
|
|
142
153
|
expect(output).toBe(
|
|
143
|
-
'<?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>'
|
|
144
155
|
)
|
|
145
156
|
})
|
|
146
157
|
|
|
@@ -161,7 +172,110 @@ it('can convert object to xmltv string with multiple categories', () => {
|
|
|
161
172
|
]
|
|
162
173
|
const output = utils.convertToXMLTV({ channels, programs })
|
|
163
174
|
expect(output).toBe(
|
|
164
|
-
'<?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>'
|
|
165
279
|
)
|
|
166
280
|
})
|
|
167
281
|
|