epg-grabber 0.13.0 → 0.15.2
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 +2 -0
- package/bin/epg-grabber.js +12 -4
- package/package.json +1 -1
- package/src/utils.js +15 -7
- package/tests/bin.test.js +7 -1
- package/tests/input/example.com.channels.xml +1 -1
- package/tests/utils.test.js +12 -17
package/README.md
CHANGED
|
@@ -56,6 +56,7 @@ module.exports = {
|
|
|
56
56
|
<tv>
|
|
57
57
|
<channel id="CNN.us">
|
|
58
58
|
<display-name>CNN</display-name>
|
|
59
|
+
<url>https://example.com</url>
|
|
59
60
|
</channel>
|
|
60
61
|
<programme start="20211116040000 +0000" stop="20211116050000 +0000" channel="CNN.us">
|
|
61
62
|
<title lang="en">News at 10PM</title>
|
|
@@ -78,6 +79,7 @@ Arguments:
|
|
|
78
79
|
- `--lang`: set default language for all programs (default: 'en')
|
|
79
80
|
- `--days`: number of days for which to grab the program (default: 1)
|
|
80
81
|
- `--delay`: delay between requests (default: 3000)
|
|
82
|
+
- `--timeout`: set a timeout for each request (default: 5000)
|
|
81
83
|
- `--debug`: enable debug mode (default: false)
|
|
82
84
|
- `--log`: path to log file (optional)
|
|
83
85
|
- `--log-level`: set the log level (default: 'info')
|
package/bin/epg-grabber.js
CHANGED
|
@@ -21,6 +21,7 @@ program
|
|
|
21
21
|
.option('--lang <lang>', 'Set default language for all programs')
|
|
22
22
|
.option('--days <days>', 'Number of days for which to grab the program', parseInteger, 1)
|
|
23
23
|
.option('--delay <delay>', 'Delay between requests (in mileseconds)', parseInteger)
|
|
24
|
+
.option('--timeout <timeout>', 'Set a timeout for each request (in mileseconds)', parseInteger)
|
|
24
25
|
.option('--debug', 'Enable debug mode', false)
|
|
25
26
|
.option('--log <log>', 'Path to log file')
|
|
26
27
|
.option('--log-level <level>', 'Set log level', 'info')
|
|
@@ -60,7 +61,15 @@ async function main() {
|
|
|
60
61
|
|
|
61
62
|
logger.info(`Loading '${options.config}'...`)
|
|
62
63
|
let config = require(path.resolve(options.config))
|
|
63
|
-
config = merge(config,
|
|
64
|
+
config = merge(config, {
|
|
65
|
+
days: options.days,
|
|
66
|
+
debug: options.debug,
|
|
67
|
+
lang: options.lang,
|
|
68
|
+
delay: options.delay,
|
|
69
|
+
request: {
|
|
70
|
+
timeout: options.timeout
|
|
71
|
+
}
|
|
72
|
+
})
|
|
64
73
|
|
|
65
74
|
if (options.channels) config.channels = options.channels
|
|
66
75
|
else if (config.channels)
|
|
@@ -70,8 +79,7 @@ async function main() {
|
|
|
70
79
|
if (!config.channels) return logger.error('Path to [site].channels.xml is missing')
|
|
71
80
|
logger.info(`Loading '${config.channels}'...`)
|
|
72
81
|
const channelsXML = fs.readFileSync(path.resolve(config.channels), { encoding: 'utf-8' })
|
|
73
|
-
const
|
|
74
|
-
const channels = parsed.channels || []
|
|
82
|
+
const channels = utils.parseChannels(channelsXML)
|
|
75
83
|
|
|
76
84
|
let programs = []
|
|
77
85
|
let i = 1
|
|
@@ -96,7 +104,7 @@ async function main() {
|
|
|
96
104
|
}
|
|
97
105
|
|
|
98
106
|
const xml = utils.convertToXMLTV({ config, channels, programs })
|
|
99
|
-
const outputPath = config.output || 'guide.xml'
|
|
107
|
+
const outputPath = options.output || config.output || 'guide.xml'
|
|
100
108
|
utils.writeToFile(outputPath, xml)
|
|
101
109
|
|
|
102
110
|
logger.info(`File '${outputPath}' successfully saved`)
|
package/package.json
CHANGED
package/src/utils.js
CHANGED
|
@@ -59,11 +59,12 @@ utils.parseChannels = function (xml) {
|
|
|
59
59
|
const channel = el.attributes
|
|
60
60
|
if (!el.elements) throw new Error(`Channel '${channel.xmltv_id}' has no valid name`)
|
|
61
61
|
channel.name = el.elements.find(el => el.type === 'text').text
|
|
62
|
+
channel.site = channel.site || site
|
|
62
63
|
|
|
63
64
|
return channel
|
|
64
65
|
})
|
|
65
66
|
|
|
66
|
-
return
|
|
67
|
+
return channels
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
utils.sleep = function (ms) {
|
|
@@ -122,8 +123,8 @@ utils.convertToXMLTV = function ({ channels, programs }) {
|
|
|
122
123
|
const title = utils.escapeString(program.title)
|
|
123
124
|
const description = utils.escapeString(program.description)
|
|
124
125
|
const categories = Array.isArray(program.category) ? program.category : [program.category]
|
|
125
|
-
const start = program.start ? dayjs.
|
|
126
|
-
const stop = program.stop ? dayjs.
|
|
126
|
+
const start = program.start ? dayjs.unix(program.start).utc().format('YYYYMMDDHHmmss ZZ') : ''
|
|
127
|
+
const stop = program.stop ? dayjs.unix(program.stop).utc().format('YYYYMMDDHHmmss ZZ') : ''
|
|
127
128
|
const lang = program.lang || 'en'
|
|
128
129
|
const icon = utils.escapeString(program.icon)
|
|
129
130
|
|
|
@@ -243,7 +244,7 @@ utils.parseResponse = async (item, response, config) => {
|
|
|
243
244
|
})
|
|
244
245
|
|
|
245
246
|
if (!item.channel.logo && config.logo) {
|
|
246
|
-
|
|
247
|
+
data.channel.logo = await utils.loadLogo(data, config)
|
|
247
248
|
}
|
|
248
249
|
|
|
249
250
|
return await utils.parsePrograms(data, config)
|
|
@@ -264,9 +265,16 @@ utils.parsePrograms = async function (data, config) {
|
|
|
264
265
|
return programs
|
|
265
266
|
.filter(i => i)
|
|
266
267
|
.map(program => {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
268
|
+
return {
|
|
269
|
+
title: program.title,
|
|
270
|
+
description: program.description || null,
|
|
271
|
+
category: program.category || null,
|
|
272
|
+
icon: program.icon || null,
|
|
273
|
+
channel: channel.xmltv_id,
|
|
274
|
+
lang: program.lang || channel.lang || config.lang || 'en',
|
|
275
|
+
start: program.start ? dayjs(program.start).unix() : null,
|
|
276
|
+
stop: program.stop ? dayjs(program.stop).unix() : null
|
|
277
|
+
}
|
|
270
278
|
})
|
|
271
279
|
}
|
|
272
280
|
|
package/tests/bin.test.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
const { execSync } = require('child_process')
|
|
2
2
|
const pwd = `${__dirname}/..`
|
|
3
|
+
import axios from 'axios'
|
|
4
|
+
|
|
5
|
+
jest.mock('axios')
|
|
3
6
|
|
|
4
7
|
function stdoutResultTester(stdout) {
|
|
5
8
|
return [`Finish`].every(val => {
|
|
@@ -19,6 +22,8 @@ it('can load config', () => {
|
|
|
19
22
|
})
|
|
20
23
|
|
|
21
24
|
it('can load mini config', () => {
|
|
25
|
+
axios.mockImplementation(() => Promise.resolve({ data: '' }))
|
|
26
|
+
|
|
22
27
|
const result = execSync(
|
|
23
28
|
`node ${pwd}/bin/epg-grabber.js \
|
|
24
29
|
--config=tests/input/mini.config.js \
|
|
@@ -26,7 +31,8 @@ it('can load mini config', () => {
|
|
|
26
31
|
--output=tests/output/mini.guide.xml \
|
|
27
32
|
--lang=fr \
|
|
28
33
|
--days=3 \
|
|
29
|
-
--delay=0
|
|
34
|
+
--delay=0 \
|
|
35
|
+
--timeout=10000`,
|
|
30
36
|
{
|
|
31
37
|
encoding: 'utf8'
|
|
32
38
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
2
|
<site site="example.com">
|
|
3
3
|
<channels>
|
|
4
|
-
<channel xmltv_id="1TV.com"
|
|
4
|
+
<channel xmltv_id="1TV.com" site_id="1" lang="fr" logo="https://example.com/logos/1TV.png">1 TV</channel>
|
|
5
5
|
<channel xmltv_id="2TV.com" site="example.com" site_id="2">2 TV</channel>
|
|
6
6
|
</channels>
|
|
7
7
|
</site>
|
package/tests/utils.test.js
CHANGED
|
@@ -28,9 +28,8 @@ it('can load valid config.js', () => {
|
|
|
28
28
|
|
|
29
29
|
it('can parse valid channels.xml', () => {
|
|
30
30
|
const file = fs.readFileSync('./tests/input/example.com.channels.xml', { encoding: 'utf-8' })
|
|
31
|
-
const
|
|
32
|
-
expect(
|
|
33
|
-
expect(parsed.channels).toEqual([
|
|
31
|
+
const channels = utils.parseChannels(file)
|
|
32
|
+
expect(channels).toEqual([
|
|
34
33
|
{
|
|
35
34
|
name: '1 TV',
|
|
36
35
|
xmltv_id: '1TV.com',
|
|
@@ -52,22 +51,20 @@ it('can parse valid channels.xml', () => {
|
|
|
52
51
|
|
|
53
52
|
it('can convert object to xmltv string', () => {
|
|
54
53
|
const file = fs.readFileSync('./tests/input/example.com.channels.xml', { encoding: 'utf-8' })
|
|
55
|
-
const
|
|
56
|
-
const channels = parsed.channels
|
|
54
|
+
const channels = utils.parseChannels(file)
|
|
57
55
|
const programs = [
|
|
58
56
|
{
|
|
59
57
|
title: 'Program 1',
|
|
60
58
|
description: 'Description for Program 1',
|
|
61
|
-
start:
|
|
62
|
-
stop:
|
|
59
|
+
start: 1616133600,
|
|
60
|
+
stop: 1616135400,
|
|
63
61
|
category: 'Test',
|
|
64
62
|
icon: 'https://example.com/images/Program1.png?x=шеллы&sid=777',
|
|
65
63
|
channel: '1TV.com',
|
|
66
64
|
lang: 'it'
|
|
67
65
|
}
|
|
68
66
|
]
|
|
69
|
-
const
|
|
70
|
-
const output = utils.convertToXMLTV({ config, channels, programs })
|
|
67
|
+
const output = utils.convertToXMLTV({ channels, programs })
|
|
71
68
|
expect(output).toBe(
|
|
72
69
|
'<?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>'
|
|
73
70
|
)
|
|
@@ -87,8 +84,8 @@ it('can convert object to xmltv string without categories', () => {
|
|
|
87
84
|
const programs = [
|
|
88
85
|
{
|
|
89
86
|
title: 'Program 1',
|
|
90
|
-
start:
|
|
91
|
-
stop:
|
|
87
|
+
start: 1616133600,
|
|
88
|
+
stop: 1616135400,
|
|
92
89
|
channel: '1TV.com',
|
|
93
90
|
lang: 'it'
|
|
94
91
|
}
|
|
@@ -102,22 +99,20 @@ it('can convert object to xmltv string without categories', () => {
|
|
|
102
99
|
|
|
103
100
|
it('can convert object to xmltv string with multiple categories', () => {
|
|
104
101
|
const file = fs.readFileSync('./tests/input/example.com.channels.xml', { encoding: 'utf-8' })
|
|
105
|
-
const
|
|
106
|
-
const channels = parsed.channels
|
|
102
|
+
const channels = utils.parseChannels(file)
|
|
107
103
|
const programs = [
|
|
108
104
|
{
|
|
109
105
|
title: 'Program 1',
|
|
110
106
|
description: 'Description for Program 1',
|
|
111
|
-
start:
|
|
112
|
-
stop:
|
|
107
|
+
start: 1616133600,
|
|
108
|
+
stop: 1616135400,
|
|
113
109
|
category: ['Test1', 'Test2'],
|
|
114
110
|
icon: 'https://example.com/images/Program1.png?x=шеллы&sid=777',
|
|
115
111
|
channel: '1TV.com',
|
|
116
112
|
lang: 'it'
|
|
117
113
|
}
|
|
118
114
|
]
|
|
119
|
-
const
|
|
120
|
-
const output = utils.convertToXMLTV({ config, channels, programs })
|
|
115
|
+
const output = utils.convertToXMLTV({ channels, programs })
|
|
121
116
|
expect(output).toBe(
|
|
122
117
|
'<?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>'
|
|
123
118
|
)
|