epg-grabber 0.31.0 → 0.33.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 +11 -14
- package/bin/epg-grabber.js +23 -9
- package/package.json +2 -2
- package/src/client.js +1 -0
- package/src/file.js +10 -0
- package/src/index.d.ts +110 -0
- package/src/parser.js +13 -5
- package/tests/__data__/input/example.channels.xml +4 -6
- package/tests/__data__/input/example_2.channels.xml +7 -0
- package/tests/__data__/input/example_3.channels.xml +5 -0
- package/tests/__data__/input/example_channels.config.js +32 -0
- package/tests/__data__/input/legacy.channels.xml +7 -0
- package/tests/__data__/output/guide.xml +6 -0
- package/tests/__mocks__/axios.js +3 -2
- package/tests/bin.test.js +23 -1
- package/tests/parser.test.js +73 -4
- package/tests/__data__/output/duplicates.guide.xml +0 -6
package/README.md
CHANGED
|
@@ -43,11 +43,9 @@ module.exports = {
|
|
|
43
43
|
|
|
44
44
|
```xml
|
|
45
45
|
<?xml version="1.0" ?>
|
|
46
|
-
<
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
</channels>
|
|
50
|
-
</site>
|
|
46
|
+
<channels site="example.com">
|
|
47
|
+
<channel site_id="cnn-23" xmltv_id="CNN.us">CNN</channel>
|
|
48
|
+
</channels>
|
|
51
49
|
```
|
|
52
50
|
|
|
53
51
|
## Example Output
|
|
@@ -75,7 +73,7 @@ Arguments:
|
|
|
75
73
|
|
|
76
74
|
- `-c, --config`: path to config file
|
|
77
75
|
- `-o, --output`: path to output file or path template (example: `guides/{site}.{lang}.xml`; default: `guide.xml`)
|
|
78
|
-
- `--channels`: path to list of channels
|
|
76
|
+
- `--channels`: path to list of channels; you can also use wildcard to specify the path to multiple files at once (example: `example.com_*.channels.xml`)
|
|
79
77
|
- `--lang`: set default language for all programs (default: `en`)
|
|
80
78
|
- `--days`: number of days for which to grab the program (default: `1`)
|
|
81
79
|
- `--delay`: delay between requests in milliseconds (default: `3000`)
|
|
@@ -94,7 +92,7 @@ Arguments:
|
|
|
94
92
|
module.exports = {
|
|
95
93
|
site: 'example.com', // site domain name (required)
|
|
96
94
|
output: 'example.com.guide.xml', // path to output file or path template (example: 'guides/{site}.{lang}.xml'; default: 'guide.xml')
|
|
97
|
-
channels: 'example.com.channels.xml', // path to channels.xml
|
|
95
|
+
channels: 'example.com.channels.xml', // path to list of channels; you can also use an array to specify the path to multiple files at once (example: ['channels1.xml', 'channels2.xml']; required)
|
|
98
96
|
lang: 'fr', // default language for all programs (default: 'en')
|
|
99
97
|
days: 3, // number of days for which to grab the program (default: 1)
|
|
100
98
|
delay: 5000, // delay between requests (default: 3000)
|
|
@@ -218,18 +216,17 @@ From each function in `config.js` you can access a `context` object containing t
|
|
|
218
216
|
|
|
219
217
|
```xml
|
|
220
218
|
<?xml version="1.0" ?>
|
|
221
|
-
<
|
|
222
|
-
<
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
</channels>
|
|
226
|
-
</site>
|
|
219
|
+
<channels site="example.com">
|
|
220
|
+
<channel site_id="cnn-23" xmltv_id="CNN.us">CNN</channel>
|
|
221
|
+
...
|
|
222
|
+
</channels>
|
|
227
223
|
```
|
|
228
224
|
|
|
229
|
-
You can also specify the language and logo for each channel individually, like so:
|
|
225
|
+
You can also specify the language, site and logo for each channel individually, like so:
|
|
230
226
|
|
|
231
227
|
```xml
|
|
232
228
|
<channel
|
|
229
|
+
site="example.com"
|
|
233
230
|
site_id="france-24"
|
|
234
231
|
xmltv_id="France24.fr"
|
|
235
232
|
lang="fr"
|
package/bin/epg-grabber.js
CHANGED
|
@@ -22,7 +22,7 @@ program
|
|
|
22
22
|
.description(description)
|
|
23
23
|
.requiredOption('-c, --config <config>', 'Path to [site].config.js file')
|
|
24
24
|
.option('-o, --output <output>', 'Path to output file')
|
|
25
|
-
.option('--channels <channels>', 'Path to channels
|
|
25
|
+
.option('--channels <channels>', 'Path to list of channels')
|
|
26
26
|
.option('--lang <lang>', 'Set default language for all programs')
|
|
27
27
|
.option('--days <days>', 'Number of days for which to grab the program', parseNumber)
|
|
28
28
|
.option('--delay <delay>', 'Delay between requests (in milliseconds)', parseNumber)
|
|
@@ -65,17 +65,31 @@ async function main() {
|
|
|
65
65
|
|
|
66
66
|
if (options.timeout) config.request.timeout = options.timeout
|
|
67
67
|
if (options.cacheTtl) config.request.cache.ttl = options.cacheTtl
|
|
68
|
+
|
|
68
69
|
if (options.channels) config.channels = options.channels
|
|
69
|
-
else if (config.channels)
|
|
70
|
-
config.channels = file.join(file.dirname(options.config), config.channels)
|
|
71
|
-
else throw new Error("The required 'channels' property is missing")
|
|
72
70
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
let parsedChannels = []
|
|
72
|
+
if (config.channels) {
|
|
73
|
+
const dir = file.dirname(options.config)
|
|
74
|
+
|
|
75
|
+
let files = []
|
|
76
|
+
if (Array.isArray(config.channels)) {
|
|
77
|
+
files = config.channels.map(path => file.join(dir, path))
|
|
78
|
+
} else if (typeof config.channels === 'string') {
|
|
79
|
+
files = await file.list(config.channels)
|
|
80
|
+
} else {
|
|
81
|
+
throw new Error('The "channels" attribute must be of type array or string')
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
for (let filepath of files) {
|
|
85
|
+
logger.info(`Loading '${filepath}'...`)
|
|
86
|
+
const channelsXML = file.read(filepath)
|
|
87
|
+
const channels = parseChannels(channelsXML)
|
|
88
|
+
parsedChannels = parsedChannels.concat(channels)
|
|
89
|
+
}
|
|
90
|
+
} else throw new Error('Path to "channels" is missing')
|
|
76
91
|
|
|
77
|
-
const
|
|
78
|
-
const { channels: parsedChannels } = parseChannels(channelsXML)
|
|
92
|
+
const grabber = new EPGGrabber(config)
|
|
79
93
|
|
|
80
94
|
let template = options.output || config.output
|
|
81
95
|
const variables = file.templateVariables(template)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "epg-grabber",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
4
4
|
"description": "Node.js CLI tool for grabbing EPG from different sites",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"preferGlobal": true,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"epg-grabber": "bin/epg-grabber.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"lint": "npx eslint ./src/**/*.js",
|
|
11
|
+
"lint": "npx eslint ./src/**/*.js ./tests/**/*.js",
|
|
12
12
|
"test": "npx jest"
|
|
13
13
|
},
|
|
14
14
|
"publishConfig": {
|
package/src/client.js
CHANGED
|
@@ -15,6 +15,7 @@ let timeout
|
|
|
15
15
|
function create(config) {
|
|
16
16
|
const client = setupCache(
|
|
17
17
|
axios.create({
|
|
18
|
+
ignoreCookieErrors: true,
|
|
18
19
|
headers: {
|
|
19
20
|
'User-Agent':
|
|
20
21
|
'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'
|
package/src/file.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const path = require('path')
|
|
3
|
+
const glob = require('glob')
|
|
3
4
|
|
|
5
|
+
module.exports.list = list
|
|
4
6
|
module.exports.read = read
|
|
5
7
|
module.exports.write = write
|
|
6
8
|
module.exports.resolve = resolve
|
|
@@ -9,6 +11,14 @@ module.exports.dirname = dirname
|
|
|
9
11
|
module.exports.templateVariables = templateVariables
|
|
10
12
|
module.exports.templateFormat = templateFormat
|
|
11
13
|
|
|
14
|
+
function list(pattern) {
|
|
15
|
+
return new Promise(resolve => {
|
|
16
|
+
glob(pattern, function (err, files) {
|
|
17
|
+
resolve(files)
|
|
18
|
+
})
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
|
|
12
22
|
function read(filepath) {
|
|
13
23
|
return fs.readFileSync(path.resolve(filepath), { encoding: 'utf-8' })
|
|
14
24
|
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import dayjs from 'dayjs'
|
|
2
|
+
|
|
3
|
+
export declare class Channel {
|
|
4
|
+
xmltv_id?: string
|
|
5
|
+
name: string
|
|
6
|
+
site?: string
|
|
7
|
+
site_id: string
|
|
8
|
+
lang?: string
|
|
9
|
+
logo?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type TextObject = {
|
|
13
|
+
lang: string
|
|
14
|
+
value: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type IconObject = {
|
|
18
|
+
src: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
type UrlObject = {
|
|
22
|
+
system: string
|
|
23
|
+
value: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type RatingObject = {
|
|
27
|
+
system: string
|
|
28
|
+
icon: string
|
|
29
|
+
value: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type ImageObject = {
|
|
33
|
+
type: string
|
|
34
|
+
size: string
|
|
35
|
+
orient: string
|
|
36
|
+
system: string
|
|
37
|
+
value: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type PersonObject = {
|
|
41
|
+
value: string
|
|
42
|
+
url: UrlObject[]
|
|
43
|
+
image: ImageObject[]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export declare class Program {
|
|
47
|
+
site?: string
|
|
48
|
+
channel?: string
|
|
49
|
+
titles: TextObject[]
|
|
50
|
+
sub_titles?: TextObject[]
|
|
51
|
+
descriptions?: TextObject[]
|
|
52
|
+
icon?: IconObject
|
|
53
|
+
episodeNumbers?: string[]
|
|
54
|
+
date?: number
|
|
55
|
+
start: number
|
|
56
|
+
stop?: number
|
|
57
|
+
urls?: UrlObject[]
|
|
58
|
+
ratings?: RatingObject[]
|
|
59
|
+
categories?: TextObject[]
|
|
60
|
+
directors?: PersonObject[]
|
|
61
|
+
actors?: PersonObject[]
|
|
62
|
+
writers?: PersonObject[]
|
|
63
|
+
adapters?: PersonObject[]
|
|
64
|
+
producers?: PersonObject[]
|
|
65
|
+
composers?: PersonObject[]
|
|
66
|
+
editors?: PersonObject[]
|
|
67
|
+
presenters?: PersonObject[]
|
|
68
|
+
commentators?: PersonObject[]
|
|
69
|
+
guests?: PersonObject[]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export declare type SiteConfig = {
|
|
73
|
+
site: string
|
|
74
|
+
days?: number
|
|
75
|
+
output?: string
|
|
76
|
+
channels?: () => object[] | string | Promise<object[]>
|
|
77
|
+
delay?: number
|
|
78
|
+
maxConnections?: number
|
|
79
|
+
request?: object
|
|
80
|
+
url: () => string | string | Promise<string>
|
|
81
|
+
logo?: () => string | string | Promise<string>
|
|
82
|
+
parser: () => object[] | Promise<object[]>
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export declare function generateXMLTV({
|
|
86
|
+
channels,
|
|
87
|
+
programs,
|
|
88
|
+
date
|
|
89
|
+
}: {
|
|
90
|
+
channels: Channel[]
|
|
91
|
+
programs: Program[]
|
|
92
|
+
date?: string | null
|
|
93
|
+
}): string
|
|
94
|
+
|
|
95
|
+
export declare function parseChannels(xml: string): { site: string; channels: Channel[] }
|
|
96
|
+
|
|
97
|
+
export type GrabCallbackData = {
|
|
98
|
+
channel: Channel
|
|
99
|
+
programs: Program[]
|
|
100
|
+
date: dayjs.Dayjs
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export declare class EPGGrabber {
|
|
104
|
+
constructor(config: object)
|
|
105
|
+
grab(
|
|
106
|
+
channel: Channel,
|
|
107
|
+
date: string | dayjs.Dayjs,
|
|
108
|
+
cb: (data: GrabCallbackData, err: Error) => void
|
|
109
|
+
): Promise<Program[]>
|
|
110
|
+
}
|
package/src/parser.js
CHANGED
|
@@ -9,11 +9,19 @@ module.exports.parsePrograms = parsePrograms
|
|
|
9
9
|
function parseChannels(xml) {
|
|
10
10
|
const result = convert.xml2js(xml)
|
|
11
11
|
const siteTag = result.elements.find(el => el.name === 'site') || {}
|
|
12
|
-
if (!siteTag.elements) return []
|
|
13
|
-
const rootSite = siteTag.attributes.site
|
|
14
12
|
|
|
15
|
-
const channelsTag =
|
|
16
|
-
|
|
13
|
+
const channelsTag =
|
|
14
|
+
siteTag && Array.isArray(siteTag.elements)
|
|
15
|
+
? siteTag.elements.find(el => el.name === 'channels')
|
|
16
|
+
: result.elements.find(el => el.name === 'channels')
|
|
17
|
+
if (!channelsTag || !channelsTag.elements) return []
|
|
18
|
+
|
|
19
|
+
let rootSite = ''
|
|
20
|
+
if (siteTag && siteTag.attributes && siteTag.attributes.site) {
|
|
21
|
+
rootSite = siteTag.attributes.site
|
|
22
|
+
} else if (channelsTag && channelsTag.attributes && channelsTag.attributes.site) {
|
|
23
|
+
rootSite = channelsTag.attributes.site
|
|
24
|
+
}
|
|
17
25
|
|
|
18
26
|
const channels = channelsTag.elements
|
|
19
27
|
.filter(el => el.name === 'channel')
|
|
@@ -26,7 +34,7 @@ function parseChannels(xml) {
|
|
|
26
34
|
return new Channel(c)
|
|
27
35
|
})
|
|
28
36
|
|
|
29
|
-
return
|
|
37
|
+
return channels
|
|
30
38
|
}
|
|
31
39
|
|
|
32
40
|
async function parsePrograms(data) {
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
</channels>
|
|
7
|
-
</site>
|
|
2
|
+
<channels site="example.com">
|
|
3
|
+
<channel xmltv_id="1TV.com" site_id="1" lang="fr" logo="https://example.com/logos/1TV.png">1 TV</channel>
|
|
4
|
+
<channel xmltv_id="2TV.com" site_id="2">2 TV</channel>
|
|
5
|
+
</channels>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const dayjs = require('dayjs')
|
|
2
|
+
const utc = require('dayjs/plugin/utc')
|
|
3
|
+
|
|
4
|
+
dayjs.extend(utc)
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
site: 'example.com',
|
|
8
|
+
days: 2,
|
|
9
|
+
channels: ['example.channels.xml', 'example_2.channels.xml'],
|
|
10
|
+
output: 'tests/__data__/output/guide.xml',
|
|
11
|
+
url: () => 'http://example.com/20210319/1tv.json',
|
|
12
|
+
request: {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
headers: {
|
|
15
|
+
'Content-Type': 'application/json',
|
|
16
|
+
Cookie: 'abc=123'
|
|
17
|
+
},
|
|
18
|
+
data() {
|
|
19
|
+
return { accountID: '123' }
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
parser: () => {
|
|
23
|
+
return [
|
|
24
|
+
{
|
|
25
|
+
title: 'Program1',
|
|
26
|
+
start: 1640995200000,
|
|
27
|
+
stop: 1640998800000
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
logo: () => 'http://example.com/logos/1TV.png?x=шеллы&sid=777'
|
|
32
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<site site="example.com">
|
|
3
|
+
<channels>
|
|
4
|
+
<channel xmltv_id="1TV.com" site_id="1" lang="fr" logo="https://example.com/logos/1TV.png">1 TV</channel>
|
|
5
|
+
<channel xmltv_id="2TV.com" site_id="2">2 TV</channel>
|
|
6
|
+
</channels>
|
|
7
|
+
</site>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" ?><tv date="20230927">
|
|
2
|
+
<channel id="1TV.com"><display-name>1 TV</display-name><icon src="https://example.com/logos/1TV.png"/><url>https://example.com</url></channel>
|
|
3
|
+
<channel id="2TV.com"><display-name>2 TV</display-name><icon src="http://example.com/logos/1TV.png?x=шеллы&sid=777"/><url>https://example.com</url></channel>
|
|
4
|
+
<channel id="3TV.com"><display-name>3 TV</display-name><icon src="http://example.com/logos/1TV.png?x=шеллы&sid=777"/><url>https://example2.com</url></channel>
|
|
5
|
+
<channel id="4TV.com"><display-name>4 TV</display-name><icon src="http://example.com/logos/1TV.png?x=шеллы&sid=777"/><url>https://example2.com</url></channel>
|
|
6
|
+
</tv>
|
package/tests/__mocks__/axios.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
const mockAxios = require('jest-mock-axios')
|
|
2
|
+
|
|
3
|
+
module.exports = mockAxios
|
package/tests/bin.test.js
CHANGED
|
@@ -87,7 +87,7 @@ it('can produce multiple outputs', () => {
|
|
|
87
87
|
})
|
|
88
88
|
|
|
89
89
|
it('removes duplicates of the program', () => {
|
|
90
|
-
|
|
90
|
+
execSync(
|
|
91
91
|
`node ${pwd}/bin/epg-grabber.js \
|
|
92
92
|
--config=tests/__data__/input/duplicates.config.js \
|
|
93
93
|
--channels=tests/__data__/input/example.channels.xml \
|
|
@@ -105,3 +105,25 @@ it('removes duplicates of the program', () => {
|
|
|
105
105
|
|
|
106
106
|
expect(output.programs).toEqual(expected.programs)
|
|
107
107
|
})
|
|
108
|
+
|
|
109
|
+
it('can load multiple "channels.xml" files at once', () => {
|
|
110
|
+
const stdout = execSync(
|
|
111
|
+
`node ${pwd}/bin/epg-grabber.js --config=tests/__data__/input/example.config.js --channels=tests/__data__/input/example*.channels.xml --timeout=1`,
|
|
112
|
+
{
|
|
113
|
+
encoding: 'utf8'
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
expect(stdoutResultTester(stdout)).toBe(true)
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('can parse list of "channels.xml" from array', () => {
|
|
121
|
+
const stdout = execSync(
|
|
122
|
+
`node ${pwd}/bin/epg-grabber.js --config=tests/__data__/input/example_channels.config.js --timeout=1`,
|
|
123
|
+
{
|
|
124
|
+
encoding: 'utf8'
|
|
125
|
+
}
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
expect(stdoutResultTester(stdout)).toBe(true)
|
|
129
|
+
})
|
package/tests/parser.test.js
CHANGED
|
@@ -3,14 +3,83 @@ import Channel from '../src/Channel'
|
|
|
3
3
|
import Program from '../src/Program'
|
|
4
4
|
import fs from 'fs'
|
|
5
5
|
|
|
6
|
-
it('can parse
|
|
7
|
-
const file = fs.readFileSync('./tests/__data__/input/example.channels.xml', {
|
|
8
|
-
|
|
6
|
+
it('can parse channels.xml', () => {
|
|
7
|
+
const file = fs.readFileSync('./tests/__data__/input/example.channels.xml', {
|
|
8
|
+
encoding: 'utf-8'
|
|
9
|
+
})
|
|
10
|
+
const channels = parseChannels(file)
|
|
9
11
|
|
|
10
|
-
expect(typeof site).toBe('string')
|
|
11
12
|
expect(channels.length).toBe(2)
|
|
12
13
|
expect(channels[0]).toBeInstanceOf(Channel)
|
|
13
14
|
expect(channels[1]).toBeInstanceOf(Channel)
|
|
15
|
+
expect(channels[0]).toMatchObject({
|
|
16
|
+
site: 'example.com',
|
|
17
|
+
site_id: '1',
|
|
18
|
+
xmltv_id: '1TV.com',
|
|
19
|
+
lang: 'fr',
|
|
20
|
+
logo: 'https://example.com/logos/1TV.png',
|
|
21
|
+
name: '1 TV'
|
|
22
|
+
})
|
|
23
|
+
expect(channels[1]).toMatchObject({
|
|
24
|
+
site: 'example.com',
|
|
25
|
+
site_id: '2',
|
|
26
|
+
lang: '',
|
|
27
|
+
logo: '',
|
|
28
|
+
xmltv_id: '2TV.com',
|
|
29
|
+
name: '2 TV'
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('can parse channels.xml with inline site attribute', () => {
|
|
34
|
+
const file = fs.readFileSync('./tests/__data__/input/example_3.channels.xml', {
|
|
35
|
+
encoding: 'utf-8'
|
|
36
|
+
})
|
|
37
|
+
const channels = parseChannels(file)
|
|
38
|
+
|
|
39
|
+
expect(channels.length).toBe(2)
|
|
40
|
+
expect(channels[0]).toBeInstanceOf(Channel)
|
|
41
|
+
expect(channels[1]).toBeInstanceOf(Channel)
|
|
42
|
+
expect(channels[0]).toMatchObject({
|
|
43
|
+
site: 'example.com',
|
|
44
|
+
site_id: '1',
|
|
45
|
+
xmltv_id: '1TV.com',
|
|
46
|
+
lang: 'fr',
|
|
47
|
+
logo: 'https://example.com/logos/1TV.png',
|
|
48
|
+
name: '1 TV'
|
|
49
|
+
})
|
|
50
|
+
expect(channels[1]).toMatchObject({
|
|
51
|
+
site: 'example.com',
|
|
52
|
+
site_id: '2',
|
|
53
|
+
lang: '',
|
|
54
|
+
logo: '',
|
|
55
|
+
xmltv_id: '2TV.com',
|
|
56
|
+
name: '2 TV'
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('can parse legacy channels.xml', () => {
|
|
61
|
+
const file = fs.readFileSync('./tests/__data__/input/legacy.channels.xml', { encoding: 'utf-8' })
|
|
62
|
+
const channels = parseChannels(file)
|
|
63
|
+
|
|
64
|
+
expect(channels.length).toBe(2)
|
|
65
|
+
expect(channels[0]).toBeInstanceOf(Channel)
|
|
66
|
+
expect(channels[1]).toBeInstanceOf(Channel)
|
|
67
|
+
expect(channels[0]).toMatchObject({
|
|
68
|
+
site: 'example.com',
|
|
69
|
+
site_id: '1',
|
|
70
|
+
xmltv_id: '1TV.com',
|
|
71
|
+
lang: 'fr',
|
|
72
|
+
logo: 'https://example.com/logos/1TV.png',
|
|
73
|
+
name: '1 TV'
|
|
74
|
+
})
|
|
75
|
+
expect(channels[1]).toMatchObject({
|
|
76
|
+
site: 'example.com',
|
|
77
|
+
site_id: '2',
|
|
78
|
+
lang: '',
|
|
79
|
+
logo: '',
|
|
80
|
+
xmltv_id: '2TV.com',
|
|
81
|
+
name: '2 TV'
|
|
82
|
+
})
|
|
14
83
|
})
|
|
15
84
|
|
|
16
85
|
it('can parse programs', done => {
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8" ?><tv date="20230521">
|
|
2
|
-
<channel id="1TV.com"><display-name>1 TV</display-name><icon src="https://example.com/logos/1TV.png"/><url>https://example.com</url></channel>
|
|
3
|
-
<channel id="2TV.com"><display-name>2 TV</display-name><url>https://example.com</url></channel>
|
|
4
|
-
<programme start="20220101000000 +0000" stop="20220101010000 +0000" channel="1TV.com"><title lang="fr">Program1</title></programme>
|
|
5
|
-
<programme start="20220101000000 +0000" stop="20220101010000 +0000" channel="2TV.com"><title>Program1</title></programme>
|
|
6
|
-
</tv>
|