epg-grabber 0.28.2 → 0.28.5
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/package.json +1 -1
- package/src/Channel.js +6 -2
- package/src/Program.js +11 -9
- package/src/index.js +4 -0
- package/src/parser.js +13 -6
- package/src/utils.js +5 -0
- package/src/xmltv.js +17 -2
- package/tests/Channel.test.js +21 -0
- package/tests/Program.test.js +104 -62
- package/tests/xmltv.test.js +1 -1
package/package.json
CHANGED
package/src/Channel.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
class Channel {
|
|
2
2
|
constructor(c) {
|
|
3
3
|
const data = {
|
|
4
|
-
id: c.xmltv_id,
|
|
4
|
+
id: c.id || c.xmltv_id,
|
|
5
5
|
name: c.name,
|
|
6
6
|
site: c.site || '',
|
|
7
7
|
site_id: c.site_id,
|
|
8
8
|
lang: c.lang || '',
|
|
9
9
|
logo: c.logo || '',
|
|
10
|
-
url: c.
|
|
10
|
+
url: c.url || toURL(c.site)
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
for (let key in data) {
|
|
@@ -17,3 +17,7 @@ class Channel {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
module.exports = Channel
|
|
20
|
+
|
|
21
|
+
function toURL(site) {
|
|
22
|
+
return site ? `https://${site}` : ''
|
|
23
|
+
}
|
package/src/Program.js
CHANGED
|
@@ -2,18 +2,19 @@ const { padStart } = require('lodash')
|
|
|
2
2
|
const { toArray, toUnix, parseNumber } = require('./utils')
|
|
3
3
|
|
|
4
4
|
class Program {
|
|
5
|
-
constructor(p
|
|
5
|
+
constructor(p) {
|
|
6
6
|
const data = {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
site: p.site || '',
|
|
8
|
+
channel: p.channel || '',
|
|
9
|
+
title: p.title || '',
|
|
9
10
|
sub_title: p.sub_title || '',
|
|
10
|
-
description: p.description
|
|
11
|
+
description: [p.description, p.desc].find(i => i) || '',
|
|
11
12
|
icon: toIconObject(p.icon),
|
|
12
|
-
episodeNumbers: getEpisodeNumbers(p.season, p.episode),
|
|
13
|
+
episodeNumbers: p.episodeNumbers || getEpisodeNumbers(p.season, p.episode),
|
|
13
14
|
date: p.date ? toUnix(p.date) : null,
|
|
14
|
-
start: toUnix(p.start),
|
|
15
|
-
stop: toUnix(p.stop),
|
|
16
|
-
urls: toArray(p.url).map(toUrlObject),
|
|
15
|
+
start: p.start ? toUnix(p.start) : null,
|
|
16
|
+
stop: p.stop ? toUnix(p.stop) : null,
|
|
17
|
+
urls: toArray(p.urls || p.url).map(toUrlObject),
|
|
17
18
|
ratings: toArray(p.ratings || p.rating).map(toRatingObject),
|
|
18
19
|
categories: toArray(p.categories || p.category),
|
|
19
20
|
directors: toArray(p.directors || p.director).map(toPersonObject),
|
|
@@ -84,7 +85,8 @@ function toUrlObject(url) {
|
|
|
84
85
|
}
|
|
85
86
|
|
|
86
87
|
function toIconObject(icon) {
|
|
87
|
-
if (!icon
|
|
88
|
+
if (!icon) return { src: '' }
|
|
89
|
+
if (typeof icon === 'string') return { src: icon }
|
|
88
90
|
|
|
89
91
|
return {
|
|
90
92
|
src: icon.src || ''
|
package/src/index.js
CHANGED
|
@@ -4,9 +4,13 @@ const { parseChannels, parsePrograms } = require('./parser')
|
|
|
4
4
|
const { generate: generateXMLTV } = require('./xmltv')
|
|
5
5
|
const { load: loadConfig } = require('./config')
|
|
6
6
|
const { sleep, isPromise } = require('./utils')
|
|
7
|
+
const Channel = require('./Channel')
|
|
8
|
+
const Program = require('./Program')
|
|
7
9
|
|
|
8
10
|
module.exports.generateXMLTV = generateXMLTV
|
|
9
11
|
module.exports.parseChannels = parseChannels
|
|
12
|
+
module.exports.Channel = Channel
|
|
13
|
+
module.exports.Program = Program
|
|
10
14
|
|
|
11
15
|
class EPGGrabber {
|
|
12
16
|
constructor(config = {}) {
|
package/src/parser.js
CHANGED
|
@@ -18,12 +18,12 @@ function parseChannels(xml) {
|
|
|
18
18
|
const channels = channelsTag.elements
|
|
19
19
|
.filter(el => el.name === 'channel')
|
|
20
20
|
.map(el => {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
const c = el.attributes
|
|
22
|
+
c.name = el.elements.find(el => el.type === 'text').text
|
|
23
|
+
c.site = c.site || rootSite
|
|
24
|
+
if (!c.name) throw new Error(`Channel '${c.xmltv_id}' has no valid name`)
|
|
25
25
|
|
|
26
|
-
return new Channel(
|
|
26
|
+
return new Channel(c)
|
|
27
27
|
})
|
|
28
28
|
|
|
29
29
|
return { site: rootSite, channels }
|
|
@@ -41,5 +41,12 @@ async function parsePrograms(data) {
|
|
|
41
41
|
throw new Error('Parser should return an array')
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
return programs
|
|
44
|
+
return programs
|
|
45
|
+
.filter(i => i)
|
|
46
|
+
.map(p => {
|
|
47
|
+
p.site = channel.site
|
|
48
|
+
p.channel = p.channel || channel.id
|
|
49
|
+
|
|
50
|
+
return new Program(p)
|
|
51
|
+
})
|
|
45
52
|
}
|
package/src/utils.js
CHANGED
|
@@ -12,11 +12,16 @@ module.exports.parseNumber = parseNumber
|
|
|
12
12
|
module.exports.formatDate = formatDate
|
|
13
13
|
module.exports.toArray = toArray
|
|
14
14
|
module.exports.toUnix = toUnix
|
|
15
|
+
module.exports.isDate = isDate
|
|
15
16
|
|
|
16
17
|
function sleep(ms) {
|
|
17
18
|
return new Promise(resolve => setTimeout(resolve, ms))
|
|
18
19
|
}
|
|
19
20
|
|
|
21
|
+
function isDate(d) {
|
|
22
|
+
return dayjs(d).isValid()
|
|
23
|
+
}
|
|
24
|
+
|
|
20
25
|
function isObject(a) {
|
|
21
26
|
return !!a && a.constructor === Object
|
|
22
27
|
}
|
package/src/xmltv.js
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
|
-
const
|
|
1
|
+
const Channel = require('./Channel')
|
|
2
|
+
const Program = require('./Program')
|
|
3
|
+
const { escapeString, getUTCDate, formatDate, isDate } = require('./utils')
|
|
2
4
|
const el = createElement
|
|
3
5
|
|
|
4
6
|
module.exports.generate = generate
|
|
5
7
|
|
|
6
8
|
function generate({ channels, programs, date = getUTCDate() }) {
|
|
9
|
+
if (!channels.every(c => c instanceof Channel)) {
|
|
10
|
+
throw new Error('"channels" must be an array of Channels')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (!programs.every(p => p instanceof Program)) {
|
|
14
|
+
throw new Error('"programs" must be an array of Programs')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (!isDate(date)) {
|
|
18
|
+
throw new Error('"date" must be a valid date')
|
|
19
|
+
}
|
|
20
|
+
|
|
7
21
|
let output = `<?xml version="1.0" encoding="UTF-8" ?>`
|
|
8
22
|
output += createElements(channels, programs, date)
|
|
9
23
|
|
|
@@ -64,7 +78,8 @@ function createElements(channels, programs, date) {
|
|
|
64
78
|
]
|
|
65
79
|
)
|
|
66
80
|
)
|
|
67
|
-
})
|
|
81
|
+
}),
|
|
82
|
+
'\r\n'
|
|
68
83
|
])
|
|
69
84
|
}
|
|
70
85
|
|
package/tests/Channel.test.js
CHANGED
|
@@ -20,3 +20,24 @@ it('can create new Channel', () => {
|
|
|
20
20
|
logo: 'https://example.com/logos/1TV.png'
|
|
21
21
|
})
|
|
22
22
|
})
|
|
23
|
+
|
|
24
|
+
it('can create channel from exist object', () => {
|
|
25
|
+
const channel = new Channel({
|
|
26
|
+
name: '1 TV',
|
|
27
|
+
id: '1TV.com',
|
|
28
|
+
site_id: '1',
|
|
29
|
+
site: 'example.com',
|
|
30
|
+
lang: 'fr',
|
|
31
|
+
logo: 'https://example.com/logos/1TV.png'
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
expect(channel).toMatchObject({
|
|
35
|
+
name: '1 TV',
|
|
36
|
+
id: '1TV.com',
|
|
37
|
+
site_id: '1',
|
|
38
|
+
site: 'example.com',
|
|
39
|
+
url: 'https://example.com',
|
|
40
|
+
lang: 'fr',
|
|
41
|
+
logo: 'https://example.com/logos/1TV.png'
|
|
42
|
+
})
|
|
43
|
+
})
|
package/tests/Program.test.js
CHANGED
|
@@ -1,55 +1,55 @@
|
|
|
1
1
|
import Channel from '../src/Channel'
|
|
2
2
|
import Program from '../src/Program'
|
|
3
3
|
|
|
4
|
-
const channel = new Channel({ xmltv_id: '1tv', lang: 'en' })
|
|
4
|
+
const channel = new Channel({ xmltv_id: '1tv', lang: 'en', site: 'example.com' })
|
|
5
5
|
|
|
6
6
|
it('can create new Program', () => {
|
|
7
|
-
const program = new Program(
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
adapters: [
|
|
42
|
-
{
|
|
43
|
-
value: 'Adapter1',
|
|
44
|
-
url: ['http://imdb.com/p/adapter1', 'http://imdb.com/p/adapter2'],
|
|
45
|
-
image: ['https://example.com/image1.jpg', 'https://example.com/image2.jpg']
|
|
46
|
-
}
|
|
47
|
-
]
|
|
7
|
+
const program = new Program({
|
|
8
|
+
site: channel.site,
|
|
9
|
+
channel: channel.id,
|
|
10
|
+
title: 'Title',
|
|
11
|
+
sub_title: 'Subtitle',
|
|
12
|
+
description: 'Description',
|
|
13
|
+
icon: 'https://example.com/image.jpg',
|
|
14
|
+
season: 9,
|
|
15
|
+
episode: 238,
|
|
16
|
+
date: '20220506',
|
|
17
|
+
start: 1616133600000,
|
|
18
|
+
stop: '2021-03-19T06:30:00.000Z',
|
|
19
|
+
url: 'http://example.com/title.html',
|
|
20
|
+
category: ['Category1', 'Category2'],
|
|
21
|
+
rating: {
|
|
22
|
+
system: 'MPAA',
|
|
23
|
+
value: 'PG',
|
|
24
|
+
icon: 'http://example.com/pg_symbol.png'
|
|
25
|
+
},
|
|
26
|
+
directors: 'Director1',
|
|
27
|
+
actors: [
|
|
28
|
+
'Actor1',
|
|
29
|
+
{ value: 'Actor2', url: 'http://actor2.com', image: 'http://actor2.com/image.png' }
|
|
30
|
+
],
|
|
31
|
+
writer: {
|
|
32
|
+
value: 'Writer1',
|
|
33
|
+
url: { system: 'imdb', value: 'http://imdb.com/p/writer1' },
|
|
34
|
+
image: {
|
|
35
|
+
value: 'https://example.com/image.jpg',
|
|
36
|
+
type: 'person',
|
|
37
|
+
size: '2',
|
|
38
|
+
system: 'TestSystem',
|
|
39
|
+
orient: 'P'
|
|
40
|
+
}
|
|
48
41
|
},
|
|
49
|
-
|
|
50
|
-
|
|
42
|
+
adapters: [
|
|
43
|
+
{
|
|
44
|
+
value: 'Adapter1',
|
|
45
|
+
url: ['http://imdb.com/p/adapter1', 'http://imdb.com/p/adapter2'],
|
|
46
|
+
image: ['https://example.com/image1.jpg', 'https://example.com/image2.jpg']
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
})
|
|
51
50
|
|
|
52
51
|
expect(program).toMatchObject({
|
|
52
|
+
site: 'example.com',
|
|
53
53
|
channel: '1tv',
|
|
54
54
|
title: 'Title',
|
|
55
55
|
sub_title: 'Subtitle',
|
|
@@ -131,16 +131,60 @@ it('can create new Program', () => {
|
|
|
131
131
|
})
|
|
132
132
|
})
|
|
133
133
|
|
|
134
|
-
it('can create program
|
|
135
|
-
const program = new Program(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
134
|
+
it('can create program from exist object', () => {
|
|
135
|
+
const program = new Program({
|
|
136
|
+
channel: channel.id,
|
|
137
|
+
title: 'Program 1',
|
|
138
|
+
start: '2021-03-19T06:00:00.000Z',
|
|
139
|
+
stop: '2021-03-19T06:30:00.000Z',
|
|
140
|
+
ratings: {
|
|
141
|
+
system: 'MPAA',
|
|
142
|
+
value: 'PG',
|
|
143
|
+
icon: 'http://example.com/pg_symbol.png'
|
|
141
144
|
},
|
|
142
|
-
|
|
143
|
-
)
|
|
145
|
+
actors: [{ value: 'Actor1', url: [], image: [] }]
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
expect(program).toMatchObject({
|
|
149
|
+
channel: '1tv',
|
|
150
|
+
title: 'Program 1',
|
|
151
|
+
sub_title: '',
|
|
152
|
+
description: '',
|
|
153
|
+
urls: [],
|
|
154
|
+
categories: [],
|
|
155
|
+
icon: {},
|
|
156
|
+
episodeNumbers: [],
|
|
157
|
+
date: null,
|
|
158
|
+
start: 1616133600000,
|
|
159
|
+
stop: 1616135400000,
|
|
160
|
+
ratings: [
|
|
161
|
+
{
|
|
162
|
+
system: 'MPAA',
|
|
163
|
+
value: 'PG',
|
|
164
|
+
icon: 'http://example.com/pg_symbol.png'
|
|
165
|
+
}
|
|
166
|
+
],
|
|
167
|
+
directors: [],
|
|
168
|
+
actors: [{ value: 'Actor1', url: [], image: [] }],
|
|
169
|
+
writers: [],
|
|
170
|
+
adapters: [],
|
|
171
|
+
producers: [],
|
|
172
|
+
composers: [],
|
|
173
|
+
editors: [],
|
|
174
|
+
presenters: [],
|
|
175
|
+
commentators: [],
|
|
176
|
+
guests: []
|
|
177
|
+
})
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('can create program without season number', () => {
|
|
181
|
+
const program = new Program({
|
|
182
|
+
channel: channel.id,
|
|
183
|
+
title: 'Program 1',
|
|
184
|
+
start: '2021-03-19T06:00:00.000Z',
|
|
185
|
+
stop: '2021-03-19T06:30:00.000Z',
|
|
186
|
+
episode: 238
|
|
187
|
+
})
|
|
144
188
|
|
|
145
189
|
expect(program.episodeNumbers).toMatchObject([
|
|
146
190
|
{ system: 'xmltv_ns', value: '0.237.0/1' },
|
|
@@ -149,15 +193,13 @@ it('can create program without season number', () => {
|
|
|
149
193
|
})
|
|
150
194
|
|
|
151
195
|
it('can create program without episode number', () => {
|
|
152
|
-
const program = new Program(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
channel
|
|
160
|
-
)
|
|
196
|
+
const program = new Program({
|
|
197
|
+
channel: channel.id,
|
|
198
|
+
title: 'Program 1',
|
|
199
|
+
start: '2021-03-19T06:00:00.000Z',
|
|
200
|
+
stop: '2021-03-19T06:30:00.000Z',
|
|
201
|
+
season: 3
|
|
202
|
+
})
|
|
161
203
|
|
|
162
204
|
expect(program.episodeNumbers).toMatchObject([])
|
|
163
205
|
})
|
package/tests/xmltv.test.js
CHANGED
|
@@ -66,6 +66,6 @@ fit('can generate xmltv', () => {
|
|
|
66
66
|
const output = xmltv.generate({ channels, programs })
|
|
67
67
|
|
|
68
68
|
expect(output).toBe(
|
|
69
|
-
'<?xml version="1.0" encoding="UTF-8" ?><tv date="20220505">\r\n<channel id="1TV.co"><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.co"><display-name>2 TV</display-name><url>https://example.com</url></channel>\r\n<programme start="20210319060000 +0000" stop="20210319063000 +0000" channel="1TV.co"><title>Program 1</title><sub-title>Sub-title & 1</sub-title><desc>Description for Program 1</desc><credits><director>Director 1<url system="TestSystem">http://example.com/director1.html</url><image>https://example.com/image1.jpg</image><image type="person" size="2" orient="P" system="TestSystem">https://example.com/image2.jpg</image></director><director>Director 2</director><actor>Actor 1</actor><actor>Actor 2</actor><writer>Writer 1</writer></credits><date>20220506</date><category>Test</category><icon src="https://example.com/images/Program1.png?x=шеллы&sid=777"/><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><rating system="MPAA"><value>PG</value><icon src="http://example.com/pg_symbol.png"/></rating></programme
|
|
69
|
+
'<?xml version="1.0" encoding="UTF-8" ?><tv date="20220505">\r\n<channel id="1TV.co"><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.co"><display-name>2 TV</display-name><url>https://example.com</url></channel>\r\n<programme start="20210319060000 +0000" stop="20210319063000 +0000" channel="1TV.co"><title>Program 1</title><sub-title>Sub-title & 1</sub-title><desc>Description for Program 1</desc><credits><director>Director 1<url system="TestSystem">http://example.com/director1.html</url><image>https://example.com/image1.jpg</image><image type="person" size="2" orient="P" system="TestSystem">https://example.com/image2.jpg</image></director><director>Director 2</director><actor>Actor 1</actor><actor>Actor 2</actor><writer>Writer 1</writer></credits><date>20220506</date><category>Test</category><icon src="https://example.com/images/Program1.png?x=шеллы&sid=777"/><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><rating system="MPAA"><value>PG</value><icon src="http://example.com/pg_symbol.png"/></rating></programme>\r\n</tv>'
|
|
70
70
|
)
|
|
71
71
|
})
|