epg-grabber 0.34.0 → 0.36.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "epg-grabber",
3
- "version": "0.34.0",
3
+ "version": "0.36.0",
4
4
  "description": "Node.js CLI tool for grabbing EPG from different sites",
5
5
  "main": "src/index.js",
6
6
  "preferGlobal": true,
@@ -28,9 +28,8 @@
28
28
  "node": ">=10.0.0"
29
29
  },
30
30
  "dependencies": {
31
- "axios": "^0.21.1",
31
+ "axios": "^1.6.1",
32
32
  "axios-cache-interceptor": "^0.10.3",
33
- "axios-cookiejar-support": "^1.0.1",
34
33
  "axios-mock-adapter": "^1.20.0",
35
34
  "commander": "^7.1.0",
36
35
  "curl-generator": "^0.2.0",
@@ -39,9 +38,10 @@
39
38
  "epg-parser": "^0.1.6",
40
39
  "fs-extra": "^11.1.1",
41
40
  "glob": "^7.1.6",
41
+ "http-cookie-agent": "^5.0.4",
42
42
  "lodash": "^4.17.21",
43
43
  "node-gzip": "^1.1.2",
44
- "tough-cookie": "^4.0.0",
44
+ "tough-cookie": "^4.1.3",
45
45
  "winston": "^3.3.3",
46
46
  "xml-js": "^1.6.11"
47
47
  },
@@ -50,7 +50,7 @@
50
50
  "@babel/preset-env": "^7.13.12",
51
51
  "babel-jest": "^26.6.3",
52
52
  "eslint": "^7.22.0",
53
- "jest": "^26.6.3",
53
+ "jest": "^29.7.0",
54
54
  "jest-mock-axios": "^4.4.1"
55
55
  },
56
56
  "jest": {
package/src/client.js CHANGED
@@ -1,10 +1,11 @@
1
1
  const { CurlGenerator } = require('curl-generator')
2
- const axios = require('axios').default
3
- const axiosCookieJarSupport = require('axios-cookiejar-support').default
2
+ const { default: axios } = require('axios')
3
+ const { CookieJar } = require('tough-cookie')
4
4
  const { setupCache } = require('axios-cache-interceptor')
5
5
  const { isObject, isPromise } = require('./utils')
6
+ const { HttpCookieAgent, HttpsCookieAgent } = require('http-cookie-agent/http')
6
7
 
7
- axiosCookieJarSupport(axios)
8
+ const jar = new CookieJar()
8
9
 
9
10
  module.exports.create = create
10
11
  module.exports.buildRequest = buildRequest
@@ -13,15 +14,20 @@ module.exports.parseResponse = parseResponse
13
14
  let timeout
14
15
 
15
16
  function create(config) {
16
- const client = setupCache(
17
- axios.create({
18
- ignoreCookieErrors: true,
19
- headers: {
20
- 'User-Agent':
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'
22
- }
23
- })
24
- )
17
+ const client = axios.defaults.cache
18
+ ? axios
19
+ : setupCookie(
20
+ setupCache(
21
+ axios.create({
22
+ jar,
23
+ ignoreCookieErrors: true,
24
+ headers: {
25
+ 'User-Agent':
26
+ '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'
27
+ }
28
+ })
29
+ )
30
+ )
25
31
 
26
32
  client.interceptors.request.use(
27
33
  function (request) {
@@ -136,3 +142,44 @@ async function getRequestUrl({ channel, date, config }) {
136
142
  }
137
143
  return config.url
138
144
  }
145
+
146
+ const AGENT_CREATED_BY_AXIOS_COOKIEJAR_SUPPORT = Symbol('AGENT_CREATED_BY_AXIOS_COOKIEJAR_SUPPORT')
147
+
148
+ function requestInterceptor(config) {
149
+ if (!config.jar) {
150
+ return config
151
+ }
152
+
153
+ config.httpAgent = new HttpCookieAgent({ cookies: { jar: config.jar } })
154
+ Object.defineProperty(config.httpAgent, AGENT_CREATED_BY_AXIOS_COOKIEJAR_SUPPORT, {
155
+ configurable: false,
156
+ enumerable: false,
157
+ value: true,
158
+ writable: false
159
+ })
160
+
161
+ config.httpsAgent = new HttpsCookieAgent({ cookies: { jar: config.jar } })
162
+ Object.defineProperty(config.httpsAgent, AGENT_CREATED_BY_AXIOS_COOKIEJAR_SUPPORT, {
163
+ configurable: false,
164
+ enumerable: false,
165
+ value: true,
166
+ writable: false
167
+ })
168
+
169
+ return config
170
+ }
171
+
172
+ function setupCookie(axios) {
173
+ axios.interceptors.request.use(requestInterceptor)
174
+
175
+ if ('create' in axios) {
176
+ const create = axios.create
177
+ axios.create = (...args) => {
178
+ const instance = create.apply(axios, args)
179
+ instance.interceptors.request.use(requestInterceptor)
180
+ return instance
181
+ }
182
+ }
183
+
184
+ return axios
185
+ }
package/src/config.js CHANGED
@@ -1,34 +1,34 @@
1
1
  const tough = require('tough-cookie')
2
2
  const { merge } = require('lodash')
3
3
 
4
- module.exports.load = load
4
+ module.exports.parse = parse
5
5
 
6
- function load(config) {
7
- if (!config.site) throw new Error("The required 'site' property is missing")
8
- if (!config.url) throw new Error("The required 'url' property is missing")
9
- if (typeof config.url !== 'function' && typeof config.url !== 'string')
10
- throw new Error("The 'url' property should return the function or string")
11
- if (!config.parser) throw new Error("The required 'parser' function is missing")
12
- if (typeof config.parser !== 'function')
13
- throw new Error("The 'parser' property should return the function")
14
- if (config.logo && typeof config.logo !== 'function')
15
- throw new Error("The 'logo' property should return the function")
6
+ function parse(config) {
7
+ if (!config.site) throw new Error("The required 'site' property is missing")
8
+ if (!config.url) throw new Error("The required 'url' property is missing")
9
+ if (typeof config.url !== 'function' && typeof config.url !== 'string')
10
+ throw new Error("The 'url' property should return the function or string")
11
+ if (!config.parser) throw new Error("The required 'parser' function is missing")
12
+ if (typeof config.parser !== 'function')
13
+ throw new Error("The 'parser' property should return the function")
14
+ if (config.logo && typeof config.logo !== 'function')
15
+ throw new Error("The 'logo' property should return the function")
16
16
 
17
- const defaultConfig = {
18
- days: 1,
19
- lang: 'en',
20
- delay: 3000,
21
- output: 'guide.xml',
22
- request: {
23
- method: 'GET',
24
- maxContentLength: 5 * 1024 * 1024,
25
- timeout: 5000,
26
- withCredentials: true,
27
- jar: new tough.CookieJar(),
28
- responseType: 'arraybuffer',
29
- cache: false
30
- }
31
- }
17
+ const defaultConfig = {
18
+ days: 1,
19
+ lang: 'en',
20
+ delay: 3000,
21
+ output: 'guide.xml',
22
+ request: {
23
+ method: 'GET',
24
+ maxContentLength: 5 * 1024 * 1024,
25
+ timeout: 5000,
26
+ withCredentials: true,
27
+ jar: new tough.CookieJar(),
28
+ responseType: 'arraybuffer',
29
+ cache: false
30
+ }
31
+ }
32
32
 
33
- return merge(defaultConfig, config)
33
+ return merge(defaultConfig, config)
34
34
  }
package/src/index.js CHANGED
@@ -3,7 +3,7 @@ const { create: createClient, buildRequest, parseResponse } = require('./client'
3
3
  const { parseChannels, parsePrograms } = require('./parser')
4
4
  const { sleep, isPromise, getUTCDate } = require('./utils')
5
5
  const { generate: generateXMLTV } = require('./xmltv')
6
- const { load: loadConfig } = require('./config')
6
+ const { parse: parseConfig } = require('./config')
7
7
  const Channel = require('./Channel')
8
8
  const Program = require('./Program')
9
9
 
@@ -14,7 +14,7 @@ module.exports.Program = Program
14
14
 
15
15
  class EPGGrabber {
16
16
  constructor(config = {}) {
17
- this.config = loadConfig(config)
17
+ this.config = config
18
18
  this.client = createClient(config)
19
19
  }
20
20
 
@@ -26,18 +26,24 @@ class EPGGrabber {
26
26
  return logo
27
27
  }
28
28
 
29
- async grab(channel, date, cb = () => {}) {
29
+ async grab(channel, date, config = {}, cb = () => {}) {
30
+ if (typeof config == 'function') {
31
+ cb = config
32
+ config = {}
33
+ }
34
+ config = merge(this.config, config)
35
+ config = parseConfig(config)
30
36
  if (!(channel instanceof Channel)) {
31
37
  throw new Error('The first argument must be the "Channel" class')
32
38
  }
33
39
 
34
- await sleep(this.config.delay)
40
+ await sleep(config.delay)
35
41
 
36
42
  date = typeof date === 'string' ? getUTCDate(date) : date
37
- return buildRequest({ channel, date, config: this.config })
43
+ return buildRequest({ channel, date, config })
38
44
  .then(this.client)
39
45
  .then(parseResponse)
40
- .then(data => merge({ channel, date, config: this.config }, data))
46
+ .then(data => merge({ channel, date, config }, data))
41
47
  .then(parsePrograms)
42
48
  .then(programs => {
43
49
  cb({ channel, date, programs })
@@ -45,7 +51,7 @@ class EPGGrabber {
45
51
  return programs
46
52
  })
47
53
  .catch(err => {
48
- if (this.config.debug) console.log('Error:', JSON.stringify(err, null, 2))
54
+ if (config.debug) console.log('Error:', JSON.stringify(err, null, 2))
49
55
  cb({ channel, date, programs: [] }, err)
50
56
 
51
57
  return []
@@ -1,4 +1,4 @@
1
- <?xml version="1.0" encoding="UTF-8" ?><tv date="20230930">
1
+ <?xml version="1.0" encoding="UTF-8" ?><tv date="20231125">
2
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
3
  <channel id="2TV.com"><display-name>2 TV</display-name><icon src="http://example.com/logos/1TV.png?x=шеллы&amp;sid=777"/><url>https://example.com</url></channel>
4
4
  <channel id="3TV.com"><display-name>3 TV</display-name><icon src="http://example.com/logos/1TV.png?x=шеллы&amp;sid=777"/><url>https://example2.com</url></channel>
@@ -1,9 +1,9 @@
1
- import { load as loadConfig } from '../src/config'
1
+ import { parse as parseConfig } from '../src/config'
2
2
  import path from 'path'
3
3
  import fs from 'fs'
4
4
 
5
5
  it('can load config', () => {
6
- const config = loadConfig(require(path.resolve('./tests/__data__/input/example.config.js')))
6
+ const config = parseConfig(require(path.resolve('./tests/__data__/input/example.config.js')))
7
7
  expect(config).toMatchObject({
8
8
  days: 2,
9
9
  delay: 3000,
@@ -28,16 +28,18 @@ it('return "Connection timeout" error if server does not response', done => {
28
28
  name: 'CNN'
29
29
  })
30
30
  const grabber = new EPGGrabber(config)
31
- grabber.grab(channel, '2022-01-01', (data, err) => {
32
- expect(err.message).toBe('Connection timeout')
33
- done()
34
- })
31
+ grabber
32
+ .grab(channel, '2022-01-01', (data, err) => {
33
+ expect(err.message).toBe('Connection timeout')
34
+ done()
35
+ })
36
+ .catch(done)
35
37
  })
36
38
 
37
39
  it('can grab single channel programs', done => {
38
40
  const data = {
39
41
  data: {
40
- toString: () => 'string'
42
+ toString: () => '[{"title":"Program1"}]'
41
43
  }
42
44
  }
43
45
  axios.mockImplementation(() => Promise.resolve(data))
@@ -45,7 +47,7 @@ it('can grab single channel programs', done => {
45
47
  const config = {
46
48
  site: 'example.com',
47
49
  url: 'http://example.com/20210319/1tv.json',
48
- parser: () => []
50
+ parser: ({ content }) => JSON.parse(content)
49
51
  }
50
52
  const channel = new Channel({
51
53
  site: 'example.com',
@@ -56,13 +58,53 @@ it('can grab single channel programs', done => {
56
58
  })
57
59
  const grabber = new EPGGrabber(config)
58
60
  grabber
59
- .grab(channel, '2022-01-01', (data, err) => {
61
+ .grab(channel, '2022-01-01')
62
+ .then(programs => {
63
+ expect(programs[0].titles).toMatchObject([
64
+ {
65
+ lang: 'fr',
66
+ value: 'Program1'
67
+ }
68
+ ])
69
+ done()
70
+ })
71
+ .catch(done)
72
+ })
73
+
74
+ it('can use a different config for different requests', done => {
75
+ const data = {
76
+ data: {
77
+ toString: () => '[{"title":"Program1"}]'
78
+ }
79
+ }
80
+ axios.mockImplementation(() => Promise.resolve(data))
81
+
82
+ const config = {
83
+ site: 'example.com',
84
+ url: 'http://example.com/20210319/1tv.json',
85
+ parser: ({ content }) => JSON.parse(content)
86
+ }
87
+ const channel = new Channel({
88
+ site: 'example.com',
89
+ site_id: '1',
90
+ xmltv_id: '1TV.fr',
91
+ lang: 'fr',
92
+ name: '1TV'
93
+ })
94
+ const grabber = new EPGGrabber()
95
+ grabber
96
+ .grab(channel, '2022-01-01', config, (data, err) => {
60
97
  if (err) {
61
- done()
98
+ done(err)
62
99
  }
63
100
  })
64
101
  .then(programs => {
65
- expect(programs.length).toBe(0)
102
+ expect(programs[0].titles).toMatchObject([
103
+ {
104
+ lang: 'fr',
105
+ value: 'Program1'
106
+ }
107
+ ])
66
108
  done()
67
109
  })
68
110
  })