diff --git a/bin/epg-grabber.js b/bin/epg-grabber.js
index 35a8c63..bbd7f5d 100755
--- a/bin/epg-grabber.js
+++ b/bin/epg-grabber.js
@@ -2,15 +2,13 @@
const { Command } = require('commander')
const program = new Command()
-const fs = require('fs')
-const path = require('path')
-const EPGGrabber = require('../src/index')
-const utils = require('../src/utils')
-const { name, version, description } = require('../package.json')
const { merge } = require('lodash')
const { gzip } = require('node-gzip')
-const { createLogger, format, transports } = require('winston')
-const { combine, timestamp, printf } = format
+const file = require('../src/file')
+const EPGGrabber = require('../src/index')
+const { create: createLogger } = require('../src/logger')
+const { parseInteger, getUTCDate } = require('../src/utils')
+const { name, version, description } = require('../package.json')
program
.name(name)
@@ -36,39 +34,13 @@ program
.parse(process.argv)
const options = program.opts()
-
-const fileFormat = printf(({ level, message, timestamp }) => {
- return `[${timestamp}] ${level.toUpperCase()}: ${message}`
-})
-
-const consoleFormat = printf(({ level, message, timestamp }) => {
- if (level === 'error') return ` Error: ${message}`
-
- return message
-})
-
-const t = [new transports.Console({ format: consoleFormat })]
-
-if (options.log) {
- t.push(
- new transports.File({
- filename: path.resolve(options.log),
- format: combine(timestamp(), fileFormat),
- options: { flags: 'w' }
- })
- )
-}
-
-const logger = createLogger({
- level: options.logLevel,
- transports: t
-})
+const logger = createLogger(options)
async function main() {
logger.info('Starting...')
logger.info(`Loading '${options.config}'...`)
- let config = require(path.resolve(options.config))
+ let config = require(file.resolve(options.config))
config = merge(config, {
days: options.days,
debug: options.debug,
@@ -83,22 +55,27 @@ async function main() {
if (options.cacheTtl) config.request.cache.ttl = options.cacheTtl
if (options.channels) config.channels = options.channels
else if (config.channels)
- config.channels = path.join(path.dirname(options.config), config.channels)
+ config.channels = file.join(file.dirname(options.config), config.channels)
else throw new Error("The required 'channels' property is missing")
if (!config.channels) return logger.error('Path to [site].channels.xml is missing')
logger.info(`Loading '${config.channels}'...`)
- const channelsXML = fs.readFileSync(path.resolve(config.channels), { encoding: 'utf-8' })
- const { channels } = utils.parseChannels(channelsXML)
+ const grabber = new EPGGrabber(config)
+
+ const channelsXML = file.read(config.channels)
+ const { channels } = grabber.parseChannels(channelsXML)
let programs = []
let i = 1
let days = options.days || 1
const total = channels.length * days
- const utcDate = utils.getUTCDate()
+ const utcDate = getUTCDate()
const dates = Array.from({ length: config.days }, (_, i) => utcDate.add(i, 'd'))
- const grabber = new EPGGrabber(config)
for (let channel of channels) {
+ if (!channel.logo && config.logo) {
+ channel.logo = await grabber.loadLogo(channel)
+ }
+
for (let date of dates) {
await grabber
.grab(channel, date, (data, err) => {
@@ -118,15 +95,15 @@ async function main() {
}
}
- const xml = utils.convertToXMLTV({ config, channels, programs })
+ const xml = grabber.generateXMLTV({ config, channels, programs })
let outputPath = options.output || config.output
if (options.gzip) {
outputPath = outputPath || 'guide.xml.gz'
const compressed = await gzip(xml)
- utils.writeToFile(outputPath, compressed)
+ file.write(outputPath, compressed)
} else {
outputPath = outputPath || 'guide.xml'
- utils.writeToFile(outputPath, xml)
+ file.write(outputPath, xml)
}
logger.info(`File '${outputPath}' successfully saved`)
@@ -134,7 +111,3 @@ async function main() {
}
main()
-
-function parseInteger(val) {
- return val ? parseInt(val) : null
-}
diff --git a/package-lock.json b/package-lock.json
index e7cfd8f..51bb6e5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "epg-grabber",
- "version": "0.25.4",
+ "version": "0.27.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "epg-grabber",
- "version": "0.25.4",
+ "version": "0.27.2",
"license": "MIT",
"dependencies": {
"axios": "^0.21.1",
@@ -30,6 +30,9 @@
"@babel/core": "^7.13.14",
"@babel/preset-env": "^7.13.12",
"babel-jest": "^26.6.3",
+ "babel-plugin-syntax-dynamic-import": "^6.18.0",
+ "babel-plugin-transform-runtime": "^6.23.0",
+ "babel-preset-es2015": "^6.24.1",
"eslint": "^7.22.0",
"jest": "^26.6.3",
"jest-mock-axios": "^4.4.1"
@@ -2439,6 +2442,170 @@
"node": ">=4"
}
},
+ "node_modules/babel-code-frame": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
+ "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^1.1.3",
+ "esutils": "^2.0.2",
+ "js-tokens": "^3.0.2"
+ }
+ },
+ "node_modules/babel-code-frame/node_modules/ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/babel-code-frame/node_modules/ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/babel-code-frame/node_modules/chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/babel-code-frame/node_modules/js-tokens": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
+ "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==",
+ "dev": true
+ },
+ "node_modules/babel-code-frame/node_modules/strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/babel-code-frame/node_modules/supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/babel-helper-call-delegate": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz",
+ "integrity": "sha512-RL8n2NiEj+kKztlrVJM9JT1cXzzAdvWFh76xh/H1I4nKwunzE4INBXn8ieCZ+wh4zWszZk7NBS1s/8HR5jDkzQ==",
+ "dev": true,
+ "dependencies": {
+ "babel-helper-hoist-variables": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "node_modules/babel-helper-define-map": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz",
+ "integrity": "sha512-bHkmjcC9lM1kmZcVpA5t2om2nzT/xiZpo6TJq7UlZ3wqKfzia4veeXbIhKvJXAMzhhEBd3cR1IElL5AenWEUpA==",
+ "dev": true,
+ "dependencies": {
+ "babel-helper-function-name": "^6.24.1",
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "lodash": "^4.17.4"
+ }
+ },
+ "node_modules/babel-helper-function-name": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz",
+ "integrity": "sha512-Oo6+e2iX+o9eVvJ9Y5eKL5iryeRdsIkwRYheCuhYdVHsdEQysbc2z2QkqCLIYnNxkT5Ss3ggrHdXiDI7Dhrn4Q==",
+ "dev": true,
+ "dependencies": {
+ "babel-helper-get-function-arity": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "node_modules/babel-helper-get-function-arity": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz",
+ "integrity": "sha512-WfgKFX6swFB1jS2vo+DwivRN4NB8XUdM3ij0Y1gnC21y1tdBoe6xjVnd7NSI6alv+gZXCtJqvrTeMW3fR/c0ng==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "node_modules/babel-helper-hoist-variables": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz",
+ "integrity": "sha512-zAYl3tqerLItvG5cKYw7f1SpvIxS9zi7ohyGHaI9cgDUjAT6YcY9jIEH5CstetP5wHIVSceXwNS7Z5BpJg+rOw==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "node_modules/babel-helper-optimise-call-expression": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz",
+ "integrity": "sha512-Op9IhEaxhbRT8MDXx2iNuMgciu2V8lDvYCNQbDGjdBNCjaMvyLf4wl4A3b8IgndCyQF8TwfgsQ8T3VD8aX1/pA==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "node_modules/babel-helper-regex": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz",
+ "integrity": "sha512-VlPiWmqmGJp0x0oK27Out1D+71nVVCTSdlbhIVoaBAj2lUgrNjBCRR9+llO4lTSb2O4r7PJg+RobRkhBrf6ofg==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "lodash": "^4.17.4"
+ }
+ },
+ "node_modules/babel-helper-replace-supers": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz",
+ "integrity": "sha512-sLI+u7sXJh6+ToqDr57Bv973kCepItDhMou0xCP2YPVmR1jkHSCY+p1no8xErbV1Siz5QE8qKT1WIwybSWlqjw==",
+ "dev": true,
+ "dependencies": {
+ "babel-helper-optimise-call-expression": "^6.24.1",
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
+ }
+ },
"node_modules/babel-jest": {
"version": "26.6.3",
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz",
@@ -2531,6 +2698,24 @@
"node": ">=8"
}
},
+ "node_modules/babel-messages": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
+ "integrity": "sha512-Bl3ZiA+LjqaMtNYopA9TYE9HP1tQ+E5dLxE0XrAzcIJeK2UqF0/EaqXwBn9esd4UmTfEab+P+UYQ1GnioFIb/w==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "node_modules/babel-plugin-check-es2015-constants": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz",
+ "integrity": "sha512-B1M5KBP29248dViEo1owyY32lk1ZSH2DaNNrXLGt8lyjjHm7pBqAdQ7VKUPR6EEDO323+OvT3MQXbCin8ooWdA==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
"node_modules/babel-plugin-dynamic-import-node": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz",
@@ -2610,6 +2795,323 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/babel-plugin-syntax-dynamic-import": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz",
+ "integrity": "sha512-MioUE+LfjCEz65Wf7Z/Rm4XCP5k2c+TbMd2Z2JKc7U9uwjBhAfNPE48KC4GTGKhppMeYVepwDBNO/nGY6NYHBA==",
+ "dev": true
+ },
+ "node_modules/babel-plugin-transform-es2015-arrow-functions": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz",
+ "integrity": "sha512-PCqwwzODXW7JMrzu+yZIaYbPQSKjDTAsNNlK2l5Gg9g4rz2VzLnZsStvp/3c46GfXpwkyufb3NCyG9+50FF1Vg==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-block-scoped-functions": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz",
+ "integrity": "sha512-2+ujAT2UMBzYFm7tidUsYh+ZoIutxJ3pN9IYrF1/H6dCKtECfhmB8UkHVpyxDwkj0CYbQG35ykoz925TUnBc3A==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-block-scoping": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz",
+ "integrity": "sha512-YiN6sFAQ5lML8JjCmr7uerS5Yc/EMbgg9G8ZNmk2E3nYX4ckHR01wrkeeMijEf5WHNK5TW0Sl0Uu3pv3EdOJWw==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.26.0",
+ "babel-template": "^6.26.0",
+ "babel-traverse": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "lodash": "^4.17.4"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-classes": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz",
+ "integrity": "sha512-5Dy7ZbRinGrNtmWpquZKZ3EGY8sDgIVB4CU8Om8q8tnMLrD/m94cKglVcHps0BCTdZ0TJeeAWOq2TK9MIY6cag==",
+ "dev": true,
+ "dependencies": {
+ "babel-helper-define-map": "^6.24.1",
+ "babel-helper-function-name": "^6.24.1",
+ "babel-helper-optimise-call-expression": "^6.24.1",
+ "babel-helper-replace-supers": "^6.24.1",
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-computed-properties": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz",
+ "integrity": "sha512-C/uAv4ktFP/Hmh01gMTvYvICrKze0XVX9f2PdIXuriCSvUmV9j+u+BB9f5fJK3+878yMK6dkdcq+Ymr9mrcLzw==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-destructuring": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz",
+ "integrity": "sha512-aNv/GDAW0j/f4Uy1OEPZn1mqD+Nfy9viFGBfQ5bZyT35YqOiqx7/tXdyfZkJ1sC21NyEsBdfDY6PYmLHF4r5iA==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-duplicate-keys": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz",
+ "integrity": "sha512-ossocTuPOssfxO2h+Z3/Ea1Vo1wWx31Uqy9vIiJusOP4TbF7tPs9U0sJ9pX9OJPf4lXRGj5+6Gkl/HHKiAP5ug==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-for-of": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz",
+ "integrity": "sha512-DLuRwoygCoXx+YfxHLkVx5/NpeSbVwfoTeBykpJK7JhYWlL/O8hgAK/reforUnZDlxasOrVPPJVI/guE3dCwkw==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-function-name": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz",
+ "integrity": "sha512-iFp5KIcorf11iBqu/y/a7DK3MN5di3pNCzto61FqCNnUX4qeBwcV1SLqe10oXNnCaxBUImX3SckX2/o1nsrTcg==",
+ "dev": true,
+ "dependencies": {
+ "babel-helper-function-name": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-literals": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz",
+ "integrity": "sha512-tjFl0cwMPpDYyoqYA9li1/7mGFit39XiNX5DKC/uCNjBctMxyL1/PT/l4rSlbvBG1pOKI88STRdUsWXB3/Q9hQ==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-modules-amd": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz",
+ "integrity": "sha512-LnIIdGWIKdw7zwckqx+eGjcS8/cl8D74A3BpJbGjKTFFNJSMrjN4bIh22HY1AlkUbeLG6X6OZj56BDvWD+OeFA==",
+ "dev": true,
+ "dependencies": {
+ "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-modules-commonjs": {
+ "version": "6.26.2",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz",
+ "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==",
+ "dev": true,
+ "dependencies": {
+ "babel-plugin-transform-strict-mode": "^6.24.1",
+ "babel-runtime": "^6.26.0",
+ "babel-template": "^6.26.0",
+ "babel-types": "^6.26.0"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-modules-systemjs": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz",
+ "integrity": "sha512-ONFIPsq8y4bls5PPsAWYXH/21Hqv64TBxdje0FvU3MhIV6QM2j5YS7KvAzg/nTIVLot2D2fmFQrFWCbgHlFEjg==",
+ "dev": true,
+ "dependencies": {
+ "babel-helper-hoist-variables": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-modules-umd": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz",
+ "integrity": "sha512-LpVbiT9CLsuAIp3IG0tfbVo81QIhn6pE8xBJ7XSeCtFlMltuar5VuBV6y6Q45tpui9QWcy5i0vLQfCfrnF7Kiw==",
+ "dev": true,
+ "dependencies": {
+ "babel-plugin-transform-es2015-modules-amd": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-object-super": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz",
+ "integrity": "sha512-8G5hpZMecb53vpD3mjs64NhI1au24TAmokQ4B+TBFBjN9cVoGoOvotdrMMRmHvVZUEvqGUPWL514woru1ChZMA==",
+ "dev": true,
+ "dependencies": {
+ "babel-helper-replace-supers": "^6.24.1",
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-parameters": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz",
+ "integrity": "sha512-8HxlW+BB5HqniD+nLkQ4xSAVq3bR/pcYW9IigY+2y0dI+Y7INFeTbfAQr+63T3E4UDsZGjyb+l9txUnABWxlOQ==",
+ "dev": true,
+ "dependencies": {
+ "babel-helper-call-delegate": "^6.24.1",
+ "babel-helper-get-function-arity": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-shorthand-properties": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz",
+ "integrity": "sha512-mDdocSfUVm1/7Jw/FIRNw9vPrBQNePy6wZJlR8HAUBLybNp1w/6lr6zZ2pjMShee65t/ybR5pT8ulkLzD1xwiw==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-spread": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz",
+ "integrity": "sha512-3Ghhi26r4l3d0Js933E5+IhHwk0A1yiutj9gwvzmFbVV0sPMYk2lekhOufHBswX7NCoSeF4Xrl3sCIuSIa+zOg==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-sticky-regex": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz",
+ "integrity": "sha512-CYP359ADryTo3pCsH0oxRo/0yn6UsEZLqYohHmvLQdfS9xkf+MbCzE3/Kolw9OYIY4ZMilH25z/5CbQbwDD+lQ==",
+ "dev": true,
+ "dependencies": {
+ "babel-helper-regex": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-template-literals": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz",
+ "integrity": "sha512-x8b9W0ngnKzDMHimVtTfn5ryimars1ByTqsfBDwAqLibmuuQY6pgBQi5z1ErIsUOWBdw1bW9FSz5RZUojM4apg==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-typeof-symbol": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz",
+ "integrity": "sha512-fz6J2Sf4gYN6gWgRZaoFXmq93X+Li/8vf+fb0sGDVtdeWvxC9y5/bTD7bvfWMEq6zetGEHpWjtzRGSugt5kNqw==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-unicode-regex": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz",
+ "integrity": "sha512-v61Dbbihf5XxnYjtBN04B/JBvsScY37R1cZT5r9permN1cp+b70DY3Ib3fIkgn1DI9U3tGgBJZVD8p/mE/4JbQ==",
+ "dev": true,
+ "dependencies": {
+ "babel-helper-regex": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "regexpu-core": "^2.0.0"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-unicode-regex/node_modules/jsesc": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+ "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==",
+ "dev": true,
+ "bin": {
+ "jsesc": "bin/jsesc"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-unicode-regex/node_modules/regexpu-core": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz",
+ "integrity": "sha512-tJ9+S4oKjxY8IZ9jmjnp/mtytu1u3iyIQAfmI51IKWH6bFf7XR1ybtaO6j7INhZKXOTYADk7V5qxaqLkmNxiZQ==",
+ "dev": true,
+ "dependencies": {
+ "regenerate": "^1.2.1",
+ "regjsgen": "^0.2.0",
+ "regjsparser": "^0.1.4"
+ }
+ },
+ "node_modules/babel-plugin-transform-es2015-unicode-regex/node_modules/regjsgen": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz",
+ "integrity": "sha512-x+Y3yA24uF68m5GA+tBjbGYo64xXVJpbToBaWCoSNSc1hdk6dfctaRWrNFTVJZIIhL5GxW8zwjoixbnifnK59g==",
+ "dev": true
+ },
+ "node_modules/babel-plugin-transform-es2015-unicode-regex/node_modules/regjsparser": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz",
+ "integrity": "sha512-jlQ9gYLfk2p3V5Ag5fYhA7fv7OHzd1KUH0PRP46xc3TgwjwgROIW572AfYg/X9kaNq/LJnu6oJcFRXlIrGoTRw==",
+ "dev": true,
+ "dependencies": {
+ "jsesc": "~0.5.0"
+ },
+ "bin": {
+ "regjsparser": "bin/parser"
+ }
+ },
+ "node_modules/babel-plugin-transform-regenerator": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz",
+ "integrity": "sha512-LS+dBkUGlNR15/5WHKe/8Neawx663qttS6AGqoOUhICc9d1KciBvtrQSuc0PI+CxQ2Q/S1aKuJ+u64GtLdcEZg==",
+ "dev": true,
+ "dependencies": {
+ "regenerator-transform": "^0.10.0"
+ }
+ },
+ "node_modules/babel-plugin-transform-regenerator/node_modules/regenerator-transform": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz",
+ "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.18.0",
+ "babel-types": "^6.19.0",
+ "private": "^0.1.6"
+ }
+ },
+ "node_modules/babel-plugin-transform-runtime": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz",
+ "integrity": "sha512-cpGMVC1vt/772y3jx1gwSaTitQVZuFDlllgreMsZ+rTYC6jlYXRyf5FQOgSnckOiA5QmzbXTyBY2A5AmZXF1fA==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "node_modules/babel-plugin-transform-strict-mode": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz",
+ "integrity": "sha512-j3KtSpjyLSJxNoCDrhwiJad8kw0gJ9REGj8/CqL0HeRyLnvUNYV9zcqluL6QJSXh3nfsLEmSLvwRfGzrgR96Pw==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
"node_modules/babel-preset-current-node-syntax": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
@@ -2633,6 +3135,39 @@
"@babel/core": "^7.0.0"
}
},
+ "node_modules/babel-preset-es2015": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz",
+ "integrity": "sha512-XfwUqG1Ry6R43m4Wfob+vHbIVBIqTg/TJY4Snku1iIzeH7mUnwHA8Vagmv+ZQbPwhS8HgsdQvy28Py3k5zpoFQ==",
+ "deprecated": "๐ Thanks for using Babel: we recommend using babel-preset-env now: please read https://babeljs.io/env to update!",
+ "dev": true,
+ "dependencies": {
+ "babel-plugin-check-es2015-constants": "^6.22.0",
+ "babel-plugin-transform-es2015-arrow-functions": "^6.22.0",
+ "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0",
+ "babel-plugin-transform-es2015-block-scoping": "^6.24.1",
+ "babel-plugin-transform-es2015-classes": "^6.24.1",
+ "babel-plugin-transform-es2015-computed-properties": "^6.24.1",
+ "babel-plugin-transform-es2015-destructuring": "^6.22.0",
+ "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1",
+ "babel-plugin-transform-es2015-for-of": "^6.22.0",
+ "babel-plugin-transform-es2015-function-name": "^6.24.1",
+ "babel-plugin-transform-es2015-literals": "^6.22.0",
+ "babel-plugin-transform-es2015-modules-amd": "^6.24.1",
+ "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1",
+ "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1",
+ "babel-plugin-transform-es2015-modules-umd": "^6.24.1",
+ "babel-plugin-transform-es2015-object-super": "^6.24.1",
+ "babel-plugin-transform-es2015-parameters": "^6.24.1",
+ "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1",
+ "babel-plugin-transform-es2015-spread": "^6.22.0",
+ "babel-plugin-transform-es2015-sticky-regex": "^6.24.1",
+ "babel-plugin-transform-es2015-template-literals": "^6.22.0",
+ "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0",
+ "babel-plugin-transform-es2015-unicode-regex": "^6.24.1",
+ "babel-plugin-transform-regenerator": "^6.24.1"
+ }
+ },
"node_modules/babel-preset-jest": {
"version": "26.6.2",
"resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz",
@@ -2649,6 +3184,106 @@
"@babel/core": "^7.0.0"
}
},
+ "node_modules/babel-runtime": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
+ "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
+ "dev": true,
+ "dependencies": {
+ "core-js": "^2.4.0",
+ "regenerator-runtime": "^0.11.0"
+ }
+ },
+ "node_modules/babel-runtime/node_modules/regenerator-runtime": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+ "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
+ "dev": true
+ },
+ "node_modules/babel-template": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz",
+ "integrity": "sha512-PCOcLFW7/eazGUKIoqH97sO9A2UYMahsn/yRQ7uOk37iutwjq7ODtcTNF+iFDSHNfkctqsLRjLP7URnOx0T1fg==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.26.0",
+ "babel-traverse": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "lodash": "^4.17.4"
+ }
+ },
+ "node_modules/babel-traverse": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz",
+ "integrity": "sha512-iSxeXx7apsjCHe9c7n8VtRXGzI2Bk1rBSOJgCCjfyXb6v1aCqE1KSEpq/8SXuVN8Ka/Rh1WDTF0MDzkvTA4MIA==",
+ "dev": true,
+ "dependencies": {
+ "babel-code-frame": "^6.26.0",
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "debug": "^2.6.8",
+ "globals": "^9.18.0",
+ "invariant": "^2.2.2",
+ "lodash": "^4.17.4"
+ }
+ },
+ "node_modules/babel-traverse/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/babel-traverse/node_modules/globals": {
+ "version": "9.18.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
+ "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/babel-traverse/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "node_modules/babel-types": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
+ "integrity": "sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g==",
+ "dev": true,
+ "dependencies": {
+ "babel-runtime": "^6.26.0",
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.4",
+ "to-fast-properties": "^1.0.3"
+ }
+ },
+ "node_modules/babel-types/node_modules/to-fast-properties": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
+ "integrity": "sha512-lxrWP8ejsq+7E3nNjwYmUBMAgjMTZoTI+sdBOpvNyijeDLa29LUn9QaoXAHv4+Z578hbmHHJKZknzxVtvo77og==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/babylon": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
+ "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==",
+ "dev": true,
+ "bin": {
+ "babylon": "bin/babylon.js"
+ }
+ },
"node_modules/balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@@ -3124,6 +3759,14 @@
"node": ">=0.10.0"
}
},
+ "node_modules/core-js": {
+ "version": "2.6.12",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
+ "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
+ "deprecated": "core-js@<3.4 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.",
+ "dev": true,
+ "hasInstallScript": true
+ },
"node_modules/core-js-compat": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.10.0.tgz",
@@ -4505,6 +5148,27 @@
"node": ">= 0.4.0"
}
},
+ "node_modules/has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-ansi/node_modules/ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -4713,6 +5377,15 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
+ "node_modules/invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "dev": true,
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
"node_modules/is-accessor-descriptor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
@@ -6867,6 +7540,18 @@
"triple-beam": "^1.3.0"
}
},
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dev": true,
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@@ -7604,6 +8289,15 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
+ "node_modules/private": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
+ "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@@ -11858,6 +12552,157 @@
}
}
},
+ "babel-code-frame": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
+ "integrity": "sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.1.3",
+ "esutils": "^2.0.2",
+ "js-tokens": "^3.0.2"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "js-tokens": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
+ "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
+ "dev": true
+ }
+ }
+ },
+ "babel-helper-call-delegate": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz",
+ "integrity": "sha512-RL8n2NiEj+kKztlrVJM9JT1cXzzAdvWFh76xh/H1I4nKwunzE4INBXn8ieCZ+wh4zWszZk7NBS1s/8HR5jDkzQ==",
+ "dev": true,
+ "requires": {
+ "babel-helper-hoist-variables": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-helper-define-map": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz",
+ "integrity": "sha512-bHkmjcC9lM1kmZcVpA5t2om2nzT/xiZpo6TJq7UlZ3wqKfzia4veeXbIhKvJXAMzhhEBd3cR1IElL5AenWEUpA==",
+ "dev": true,
+ "requires": {
+ "babel-helper-function-name": "^6.24.1",
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "lodash": "^4.17.4"
+ }
+ },
+ "babel-helper-function-name": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz",
+ "integrity": "sha512-Oo6+e2iX+o9eVvJ9Y5eKL5iryeRdsIkwRYheCuhYdVHsdEQysbc2z2QkqCLIYnNxkT5Ss3ggrHdXiDI7Dhrn4Q==",
+ "dev": true,
+ "requires": {
+ "babel-helper-get-function-arity": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-helper-get-function-arity": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz",
+ "integrity": "sha512-WfgKFX6swFB1jS2vo+DwivRN4NB8XUdM3ij0Y1gnC21y1tdBoe6xjVnd7NSI6alv+gZXCtJqvrTeMW3fR/c0ng==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-helper-hoist-variables": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz",
+ "integrity": "sha512-zAYl3tqerLItvG5cKYw7f1SpvIxS9zi7ohyGHaI9cgDUjAT6YcY9jIEH5CstetP5wHIVSceXwNS7Z5BpJg+rOw==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-helper-optimise-call-expression": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz",
+ "integrity": "sha512-Op9IhEaxhbRT8MDXx2iNuMgciu2V8lDvYCNQbDGjdBNCjaMvyLf4wl4A3b8IgndCyQF8TwfgsQ8T3VD8aX1/pA==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-helper-regex": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz",
+ "integrity": "sha512-VlPiWmqmGJp0x0oK27Out1D+71nVVCTSdlbhIVoaBAj2lUgrNjBCRR9+llO4lTSb2O4r7PJg+RobRkhBrf6ofg==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "lodash": "^4.17.4"
+ }
+ },
+ "babel-helper-replace-supers": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz",
+ "integrity": "sha512-sLI+u7sXJh6+ToqDr57Bv973kCepItDhMou0xCP2YPVmR1jkHSCY+p1no8xErbV1Siz5QE8qKT1WIwybSWlqjw==",
+ "dev": true,
+ "requires": {
+ "babel-helper-optimise-call-expression": "^6.24.1",
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
+ }
+ },
"babel-jest": {
"version": "26.6.3",
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz",
@@ -11925,6 +12770,24 @@
}
}
},
+ "babel-messages": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
+ "integrity": "sha512-Bl3ZiA+LjqaMtNYopA9TYE9HP1tQ+E5dLxE0XrAzcIJeK2UqF0/EaqXwBn9esd4UmTfEab+P+UYQ1GnioFIb/w==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-check-es2015-constants": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz",
+ "integrity": "sha512-B1M5KBP29248dViEo1owyY32lk1ZSH2DaNNrXLGt8lyjjHm7pBqAdQ7VKUPR6EEDO323+OvT3MQXbCin8ooWdA==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
"babel-plugin-dynamic-import-node": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz",
@@ -11989,6 +12852,321 @@
"@babel/helper-define-polyfill-provider": "^0.1.5"
}
},
+ "babel-plugin-syntax-dynamic-import": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz",
+ "integrity": "sha512-MioUE+LfjCEz65Wf7Z/Rm4XCP5k2c+TbMd2Z2JKc7U9uwjBhAfNPE48KC4GTGKhppMeYVepwDBNO/nGY6NYHBA==",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-arrow-functions": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz",
+ "integrity": "sha512-PCqwwzODXW7JMrzu+yZIaYbPQSKjDTAsNNlK2l5Gg9g4rz2VzLnZsStvp/3c46GfXpwkyufb3NCyG9+50FF1Vg==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-es2015-block-scoped-functions": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz",
+ "integrity": "sha512-2+ujAT2UMBzYFm7tidUsYh+ZoIutxJ3pN9IYrF1/H6dCKtECfhmB8UkHVpyxDwkj0CYbQG35ykoz925TUnBc3A==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-es2015-block-scoping": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz",
+ "integrity": "sha512-YiN6sFAQ5lML8JjCmr7uerS5Yc/EMbgg9G8ZNmk2E3nYX4ckHR01wrkeeMijEf5WHNK5TW0Sl0Uu3pv3EdOJWw==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "babel-template": "^6.26.0",
+ "babel-traverse": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "lodash": "^4.17.4"
+ }
+ },
+ "babel-plugin-transform-es2015-classes": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz",
+ "integrity": "sha512-5Dy7ZbRinGrNtmWpquZKZ3EGY8sDgIVB4CU8Om8q8tnMLrD/m94cKglVcHps0BCTdZ0TJeeAWOq2TK9MIY6cag==",
+ "dev": true,
+ "requires": {
+ "babel-helper-define-map": "^6.24.1",
+ "babel-helper-function-name": "^6.24.1",
+ "babel-helper-optimise-call-expression": "^6.24.1",
+ "babel-helper-replace-supers": "^6.24.1",
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-plugin-transform-es2015-computed-properties": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz",
+ "integrity": "sha512-C/uAv4ktFP/Hmh01gMTvYvICrKze0XVX9f2PdIXuriCSvUmV9j+u+BB9f5fJK3+878yMK6dkdcq+Ymr9mrcLzw==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1"
+ }
+ },
+ "babel-plugin-transform-es2015-destructuring": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz",
+ "integrity": "sha512-aNv/GDAW0j/f4Uy1OEPZn1mqD+Nfy9viFGBfQ5bZyT35YqOiqx7/tXdyfZkJ1sC21NyEsBdfDY6PYmLHF4r5iA==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-es2015-duplicate-keys": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz",
+ "integrity": "sha512-ossocTuPOssfxO2h+Z3/Ea1Vo1wWx31Uqy9vIiJusOP4TbF7tPs9U0sJ9pX9OJPf4lXRGj5+6Gkl/HHKiAP5ug==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-plugin-transform-es2015-for-of": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz",
+ "integrity": "sha512-DLuRwoygCoXx+YfxHLkVx5/NpeSbVwfoTeBykpJK7JhYWlL/O8hgAK/reforUnZDlxasOrVPPJVI/guE3dCwkw==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-es2015-function-name": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz",
+ "integrity": "sha512-iFp5KIcorf11iBqu/y/a7DK3MN5di3pNCzto61FqCNnUX4qeBwcV1SLqe10oXNnCaxBUImX3SckX2/o1nsrTcg==",
+ "dev": true,
+ "requires": {
+ "babel-helper-function-name": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-plugin-transform-es2015-literals": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz",
+ "integrity": "sha512-tjFl0cwMPpDYyoqYA9li1/7mGFit39XiNX5DKC/uCNjBctMxyL1/PT/l4rSlbvBG1pOKI88STRdUsWXB3/Q9hQ==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-es2015-modules-amd": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz",
+ "integrity": "sha512-LnIIdGWIKdw7zwckqx+eGjcS8/cl8D74A3BpJbGjKTFFNJSMrjN4bIh22HY1AlkUbeLG6X6OZj56BDvWD+OeFA==",
+ "dev": true,
+ "requires": {
+ "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1"
+ }
+ },
+ "babel-plugin-transform-es2015-modules-commonjs": {
+ "version": "6.26.2",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz",
+ "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==",
+ "dev": true,
+ "requires": {
+ "babel-plugin-transform-strict-mode": "^6.24.1",
+ "babel-runtime": "^6.26.0",
+ "babel-template": "^6.26.0",
+ "babel-types": "^6.26.0"
+ }
+ },
+ "babel-plugin-transform-es2015-modules-systemjs": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz",
+ "integrity": "sha512-ONFIPsq8y4bls5PPsAWYXH/21Hqv64TBxdje0FvU3MhIV6QM2j5YS7KvAzg/nTIVLot2D2fmFQrFWCbgHlFEjg==",
+ "dev": true,
+ "requires": {
+ "babel-helper-hoist-variables": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1"
+ }
+ },
+ "babel-plugin-transform-es2015-modules-umd": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz",
+ "integrity": "sha512-LpVbiT9CLsuAIp3IG0tfbVo81QIhn6pE8xBJ7XSeCtFlMltuar5VuBV6y6Q45tpui9QWcy5i0vLQfCfrnF7Kiw==",
+ "dev": true,
+ "requires": {
+ "babel-plugin-transform-es2015-modules-amd": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1"
+ }
+ },
+ "babel-plugin-transform-es2015-object-super": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz",
+ "integrity": "sha512-8G5hpZMecb53vpD3mjs64NhI1au24TAmokQ4B+TBFBjN9cVoGoOvotdrMMRmHvVZUEvqGUPWL514woru1ChZMA==",
+ "dev": true,
+ "requires": {
+ "babel-helper-replace-supers": "^6.24.1",
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-es2015-parameters": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz",
+ "integrity": "sha512-8HxlW+BB5HqniD+nLkQ4xSAVq3bR/pcYW9IigY+2y0dI+Y7INFeTbfAQr+63T3E4UDsZGjyb+l9txUnABWxlOQ==",
+ "dev": true,
+ "requires": {
+ "babel-helper-call-delegate": "^6.24.1",
+ "babel-helper-get-function-arity": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-template": "^6.24.1",
+ "babel-traverse": "^6.24.1",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-plugin-transform-es2015-shorthand-properties": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz",
+ "integrity": "sha512-mDdocSfUVm1/7Jw/FIRNw9vPrBQNePy6wZJlR8HAUBLybNp1w/6lr6zZ2pjMShee65t/ybR5pT8ulkLzD1xwiw==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-plugin-transform-es2015-spread": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz",
+ "integrity": "sha512-3Ghhi26r4l3d0Js933E5+IhHwk0A1yiutj9gwvzmFbVV0sPMYk2lekhOufHBswX7NCoSeF4Xrl3sCIuSIa+zOg==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-es2015-sticky-regex": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz",
+ "integrity": "sha512-CYP359ADryTo3pCsH0oxRo/0yn6UsEZLqYohHmvLQdfS9xkf+MbCzE3/Kolw9OYIY4ZMilH25z/5CbQbwDD+lQ==",
+ "dev": true,
+ "requires": {
+ "babel-helper-regex": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
+ "babel-plugin-transform-es2015-template-literals": {
+ "version": "6.22.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz",
+ "integrity": "sha512-x8b9W0ngnKzDMHimVtTfn5ryimars1ByTqsfBDwAqLibmuuQY6pgBQi5z1ErIsUOWBdw1bW9FSz5RZUojM4apg==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-es2015-typeof-symbol": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz",
+ "integrity": "sha512-fz6J2Sf4gYN6gWgRZaoFXmq93X+Li/8vf+fb0sGDVtdeWvxC9y5/bTD7bvfWMEq6zetGEHpWjtzRGSugt5kNqw==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-es2015-unicode-regex": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz",
+ "integrity": "sha512-v61Dbbihf5XxnYjtBN04B/JBvsScY37R1cZT5r9permN1cp+b70DY3Ib3fIkgn1DI9U3tGgBJZVD8p/mE/4JbQ==",
+ "dev": true,
+ "requires": {
+ "babel-helper-regex": "^6.24.1",
+ "babel-runtime": "^6.22.0",
+ "regexpu-core": "^2.0.0"
+ },
+ "dependencies": {
+ "jsesc": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+ "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==",
+ "dev": true
+ },
+ "regexpu-core": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz",
+ "integrity": "sha512-tJ9+S4oKjxY8IZ9jmjnp/mtytu1u3iyIQAfmI51IKWH6bFf7XR1ybtaO6j7INhZKXOTYADk7V5qxaqLkmNxiZQ==",
+ "dev": true,
+ "requires": {
+ "regenerate": "^1.2.1",
+ "regjsgen": "^0.2.0",
+ "regjsparser": "^0.1.4"
+ }
+ },
+ "regjsgen": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz",
+ "integrity": "sha512-x+Y3yA24uF68m5GA+tBjbGYo64xXVJpbToBaWCoSNSc1hdk6dfctaRWrNFTVJZIIhL5GxW8zwjoixbnifnK59g==",
+ "dev": true
+ },
+ "regjsparser": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz",
+ "integrity": "sha512-jlQ9gYLfk2p3V5Ag5fYhA7fv7OHzd1KUH0PRP46xc3TgwjwgROIW572AfYg/X9kaNq/LJnu6oJcFRXlIrGoTRw==",
+ "dev": true,
+ "requires": {
+ "jsesc": "~0.5.0"
+ }
+ }
+ }
+ },
+ "babel-plugin-transform-regenerator": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz",
+ "integrity": "sha512-LS+dBkUGlNR15/5WHKe/8Neawx663qttS6AGqoOUhICc9d1KciBvtrQSuc0PI+CxQ2Q/S1aKuJ+u64GtLdcEZg==",
+ "dev": true,
+ "requires": {
+ "regenerator-transform": "^0.10.0"
+ },
+ "dependencies": {
+ "regenerator-transform": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz",
+ "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.18.0",
+ "babel-types": "^6.19.0",
+ "private": "^0.1.6"
+ }
+ }
+ }
+ },
+ "babel-plugin-transform-runtime": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz",
+ "integrity": "sha512-cpGMVC1vt/772y3jx1gwSaTitQVZuFDlllgreMsZ+rTYC6jlYXRyf5FQOgSnckOiA5QmzbXTyBY2A5AmZXF1fA==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-transform-strict-mode": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz",
+ "integrity": "sha512-j3KtSpjyLSJxNoCDrhwiJad8kw0gJ9REGj8/CqL0HeRyLnvUNYV9zcqluL6QJSXh3nfsLEmSLvwRfGzrgR96Pw==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0",
+ "babel-types": "^6.24.1"
+ }
+ },
"babel-preset-current-node-syntax": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
@@ -12009,6 +13187,38 @@
"@babel/plugin-syntax-top-level-await": "^7.8.3"
}
},
+ "babel-preset-es2015": {
+ "version": "6.24.1",
+ "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz",
+ "integrity": "sha512-XfwUqG1Ry6R43m4Wfob+vHbIVBIqTg/TJY4Snku1iIzeH7mUnwHA8Vagmv+ZQbPwhS8HgsdQvy28Py3k5zpoFQ==",
+ "dev": true,
+ "requires": {
+ "babel-plugin-check-es2015-constants": "^6.22.0",
+ "babel-plugin-transform-es2015-arrow-functions": "^6.22.0",
+ "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0",
+ "babel-plugin-transform-es2015-block-scoping": "^6.24.1",
+ "babel-plugin-transform-es2015-classes": "^6.24.1",
+ "babel-plugin-transform-es2015-computed-properties": "^6.24.1",
+ "babel-plugin-transform-es2015-destructuring": "^6.22.0",
+ "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1",
+ "babel-plugin-transform-es2015-for-of": "^6.22.0",
+ "babel-plugin-transform-es2015-function-name": "^6.24.1",
+ "babel-plugin-transform-es2015-literals": "^6.22.0",
+ "babel-plugin-transform-es2015-modules-amd": "^6.24.1",
+ "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1",
+ "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1",
+ "babel-plugin-transform-es2015-modules-umd": "^6.24.1",
+ "babel-plugin-transform-es2015-object-super": "^6.24.1",
+ "babel-plugin-transform-es2015-parameters": "^6.24.1",
+ "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1",
+ "babel-plugin-transform-es2015-spread": "^6.22.0",
+ "babel-plugin-transform-es2015-sticky-regex": "^6.24.1",
+ "babel-plugin-transform-es2015-template-literals": "^6.22.0",
+ "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0",
+ "babel-plugin-transform-es2015-unicode-regex": "^6.24.1",
+ "babel-plugin-transform-regenerator": "^6.24.1"
+ }
+ },
"babel-preset-jest": {
"version": "26.6.2",
"resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz",
@@ -12019,6 +13229,103 @@
"babel-preset-current-node-syntax": "^1.0.0"
}
},
+ "babel-runtime": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
+ "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
+ "dev": true,
+ "requires": {
+ "core-js": "^2.4.0",
+ "regenerator-runtime": "^0.11.0"
+ },
+ "dependencies": {
+ "regenerator-runtime": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+ "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
+ "dev": true
+ }
+ }
+ },
+ "babel-template": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz",
+ "integrity": "sha512-PCOcLFW7/eazGUKIoqH97sO9A2UYMahsn/yRQ7uOk37iutwjq7ODtcTNF+iFDSHNfkctqsLRjLP7URnOx0T1fg==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "babel-traverse": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "lodash": "^4.17.4"
+ }
+ },
+ "babel-traverse": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz",
+ "integrity": "sha512-iSxeXx7apsjCHe9c7n8VtRXGzI2Bk1rBSOJgCCjfyXb6v1aCqE1KSEpq/8SXuVN8Ka/Rh1WDTF0MDzkvTA4MIA==",
+ "dev": true,
+ "requires": {
+ "babel-code-frame": "^6.26.0",
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "debug": "^2.6.8",
+ "globals": "^9.18.0",
+ "invariant": "^2.2.2",
+ "lodash": "^4.17.4"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "globals": {
+ "version": "9.18.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
+ "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ }
+ }
+ },
+ "babel-types": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
+ "integrity": "sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.4",
+ "to-fast-properties": "^1.0.3"
+ },
+ "dependencies": {
+ "to-fast-properties": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
+ "integrity": "sha512-lxrWP8ejsq+7E3nNjwYmUBMAgjMTZoTI+sdBOpvNyijeDLa29LUn9QaoXAHv4+Z578hbmHHJKZknzxVtvo77og==",
+ "dev": true
+ }
+ }
+ },
+ "babylon": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
+ "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==",
+ "dev": true
+ },
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@@ -12403,6 +13710,12 @@
"integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
"dev": true
},
+ "core-js": {
+ "version": "2.6.12",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
+ "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
+ "dev": true
+ },
"core-js-compat": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.10.0.tgz",
@@ -13461,6 +14774,23 @@
"function-bind": "^1.1.1"
}
},
+ "has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
+ "dev": true
+ }
+ }
+ },
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -13618,6 +14948,15 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
+ "invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ },
"is-accessor-descriptor": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
@@ -15247,6 +16586,15 @@
"triple-beam": "^1.3.0"
}
},
+ "loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dev": true,
+ "requires": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ }
+ },
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@@ -15814,6 +17162,12 @@
}
}
},
+ "private": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
+ "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==",
+ "dev": true
+ },
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
diff --git a/src/channels.js b/src/channels.js
new file mode 100644
index 0000000..507856f
--- /dev/null
+++ b/src/channels.js
@@ -0,0 +1,26 @@
+const convert = require('xml-js')
+
+module.exports.parse = parse
+
+function parse(xml) {
+ const result = convert.xml2js(xml)
+ const siteTag = result.elements.find(el => el.name === 'site') || {}
+ if (!siteTag.elements) return []
+ const site = siteTag.attributes.site
+
+ const channelsTag = siteTag.elements.find(el => el.name === 'channels')
+ if (!channelsTag.elements) return []
+
+ const channels = channelsTag.elements
+ .filter(el => el.name === 'channel')
+ .map(el => {
+ const channel = el.attributes
+ if (!el.elements) throw new Error(`Channel '${channel.xmltv_id}' has no valid name`)
+ channel.name = el.elements.find(el => el.type === 'text').text
+ channel.site = channel.site || site
+
+ return channel
+ })
+
+ return { site, channels }
+}
diff --git a/src/client.js b/src/client.js
new file mode 100644
index 0000000..57e0d14
--- /dev/null
+++ b/src/client.js
@@ -0,0 +1,143 @@
+const { merge } = require('lodash')
+const { CurlGenerator } = require('curl-generator')
+const axios = require('axios').default
+const axiosCookieJarSupport = require('axios-cookiejar-support').default
+const { setupCache } = require('axios-cache-interceptor')
+const { isPromise, getUTCDate } = require('./utils')
+
+axiosCookieJarSupport(axios)
+
+module.exports.create = create
+module.exports.buildRequest = buildRequest
+module.exports.parseResponse = parseResponse
+
+let timeout
+
+class Client {
+ constructor() {}
+}
+
+function create(config) {
+ const client = setupCache(
+ axios.create({
+ headers: {
+ 'User-Agent':
+ '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'
+ }
+ })
+ )
+
+ client.interceptors.request.use(
+ function (request) {
+ if (config.debug) {
+ console.log('Request:', JSON.stringify(request, null, 2))
+ }
+ return request
+ },
+ function (error) {
+ return Promise.reject(error)
+ }
+ )
+
+ client.interceptors.response.use(
+ function (response) {
+ if (config.debug) {
+ const data =
+ isObject(response.data) || Array.isArray(response.data)
+ ? JSON.stringify(response.data)
+ : response.data.toString()
+ console.log(
+ 'Response:',
+ JSON.stringify(
+ {
+ headers: response.headers,
+ data,
+ cached: response.cached
+ },
+ null,
+ 2
+ )
+ )
+ }
+
+ clearTimeout(timeout)
+ return response
+ },
+ function (error) {
+ clearTimeout(timeout)
+ return Promise.reject(error)
+ }
+ )
+
+ return client
+}
+
+async function buildRequest({ channel, date, config }) {
+ date = typeof date === 'string' ? getUTCDate(date) : date
+ const CancelToken = axios.CancelToken
+ const source = CancelToken.source()
+ const request = { ...config.request }
+ timeout = setTimeout(() => {
+ source.cancel('Connection timeout')
+ }, request.timeout)
+ request.headers = await getRequestHeaders({ channel, date, config })
+ request.url = await getRequestUrl({ channel, date, config })
+ request.data = await getRequestData({ channel, date, config })
+ request.cancelToken = source.token
+
+ if (config.curl) {
+ const curl = CurlGenerator({
+ url: request.url,
+ method: request.method,
+ headers: request.headers,
+ body: request.data
+ })
+ console.log(curl)
+ }
+
+ return request
+}
+
+function parseResponse(response) {
+ return {
+ content: response.data.toString(),
+ buffer: response.data,
+ headers: response.headers,
+ request: response.request,
+ cached: response.cached
+ }
+}
+
+async function getRequestHeaders({ channel, date, config }) {
+ if (typeof config.request.headers === 'function') {
+ const headers = config.request.headers({ channel, date })
+ if (isPromise(headers)) {
+ return await headers
+ }
+ return headers
+ }
+
+ return config.request.headers || null
+}
+
+async function getRequestData({ channel, date, config }) {
+ if (typeof config.request.data === 'function') {
+ const data = config.request.data({ channel, date })
+ if (isPromise(data)) {
+ return await data
+ }
+ return data
+ }
+ return config.request.data || null
+}
+
+async function getRequestUrl({ channel, date, config }) {
+ if (typeof config.url === 'function') {
+ const url = config.url({ channel, date })
+ if (isPromise(url)) {
+ return await url
+ }
+ return url
+ }
+ return config.url
+}
diff --git a/src/config.js b/src/config.js
new file mode 100644
index 0000000..8b553fb
--- /dev/null
+++ b/src/config.js
@@ -0,0 +1,34 @@
+const tough = require('tough-cookie')
+const { merge } = require('lodash')
+
+module.exports.load = load
+
+function load(config) {
+ if (!config.site) throw new Error("The required 'site' property is missing")
+ if (!config.url) throw new Error("The required 'url' property is missing")
+ if (typeof config.url !== 'function' && typeof config.url !== 'string')
+ throw new Error("The 'url' property should return the function or string")
+ if (!config.parser) throw new Error("The required 'parser' function is missing")
+ if (typeof config.parser !== 'function')
+ throw new Error("The 'parser' property should return the function")
+ if (config.logo && typeof config.logo !== 'function')
+ throw new Error("The 'logo' property should return the function")
+
+ const defaultConfig = {
+ days: 1,
+ lang: 'en',
+ delay: 3000,
+ output: 'guide.xml',
+ request: {
+ method: 'GET',
+ maxContentLength: 5 * 1024 * 1024,
+ timeout: 5000,
+ withCredentials: true,
+ jar: new tough.CookieJar(),
+ responseType: 'arraybuffer',
+ cache: false
+ }
+ }
+
+ return merge(defaultConfig, config)
+}
diff --git a/src/file.js b/src/file.js
new file mode 100644
index 0000000..339c438
--- /dev/null
+++ b/src/file.js
@@ -0,0 +1,33 @@
+const fs = require('fs')
+const path = require('path')
+
+module.exports.read = read
+module.exports.write = write
+module.exports.resolve = resolve
+module.exports.join = join
+module.exports.dirname = dirname
+
+function read(filepath) {
+ return fs.readFileSync(path.resolve(filepath), { encoding: 'utf-8' })
+}
+
+function write(filepath, data) {
+ const dir = path.resolve(path.dirname(filepath))
+ if (!fs.existsSync(dir)) {
+ fs.mkdirSync(dir, { recursive: true })
+ }
+
+ fs.writeFileSync(path.resolve(filepath), data)
+}
+
+function resolve(filepath) {
+ return path.resolve(filepath)
+}
+
+function join(path1, path2) {
+ return path.join(path1, path2)
+}
+
+function dirname(filepath) {
+ return path.dirname(filepath)
+}
diff --git a/src/index.js b/src/index.js
index b0b10d9..ad7576b 100755
--- a/src/index.js
+++ b/src/index.js
@@ -1,41 +1,48 @@
-const utils = require('./utils')
+const { merge } = require('lodash')
+const { create: createClient, buildRequest, parseResponse } = require('./client')
+const { generate: generateXMLTV } = require('./xmltv')
+const { parse: parseChannels } = require('./channels')
+const { parse: parsePrograms } = require('./programs')
+const { load: loadConfig } = require('./config')
+const { sleep, isPromise } = require('./utils')
class EPGGrabber {
constructor(config = {}) {
- this.config = utils.loadConfig(config)
- this.client = utils.createClient(config)
+ this.config = loadConfig(config)
+ this.client = createClient(config)
+ }
+
+ async loadLogo(channel) {
+ const logo = this.config.logo({ channel })
+ if (isPromise(logo)) {
+ return await logo
+ }
+ return logo
}
async grab(channel, date, cb = () => {}) {
- date = typeof date === 'string' ? utils.getUTCDate(date) : date
- channel.lang = channel.lang || this.config.lang || null
+ await sleep(this.config.delay)
- let programs = []
- const item = { date, channel }
- await utils
- .buildRequest(item, this.config)
- .then(request => utils.fetchData(this.client, request))
- .then(response => utils.parseResponse(item, response, this.config))
- .then(results => {
- item.programs = results
- cb(item, null)
- programs = programs.concat(results)
+ return buildRequest({ channel, date, config: this.config })
+ .then(this.client)
+ .then(parseResponse)
+ .then(data => merge({ channel, date, config: this.config }, data))
+ .then(parsePrograms)
+ .then(programs => {
+ cb({ channel, date, programs })
+
+ return programs
})
- .catch(error => {
- item.programs = []
- if (this.config.debug) {
- console.log('Error:', JSON.stringify(error, null, 2))
- }
- cb(item, error)
+ .catch(err => {
+ if (this.config.debug) console.log('Error:', JSON.stringify(err, null, 2))
+ cb({ channel, date, programs: [] }, err)
+
+ return []
})
-
- await utils.sleep(this.config.delay)
-
- return programs
}
}
-EPGGrabber.convertToXMLTV = utils.convertToXMLTV
-EPGGrabber.parseChannels = utils.parseChannels
+EPGGrabber.prototype.generateXMLTV = generateXMLTV
+EPGGrabber.prototype.parseChannels = parseChannels
module.exports = EPGGrabber
diff --git a/src/logger.js b/src/logger.js
new file mode 100644
index 0000000..9261c71
--- /dev/null
+++ b/src/logger.js
@@ -0,0 +1,33 @@
+const { createLogger, format, transports } = require('winston')
+const { combine, timestamp, printf } = format
+
+module.exports.create = create
+
+function create(options) {
+ const fileFormat = printf(({ level, message, timestamp }) => {
+ return `[${timestamp}] ${level.toUpperCase()}: ${message}`
+ })
+
+ const consoleFormat = printf(({ level, message, timestamp }) => {
+ if (level === 'error') return ` Error: ${message}`
+
+ return message
+ })
+
+ const t = [new transports.Console({ format: consoleFormat })]
+
+ if (options.log) {
+ t.push(
+ new transports.File({
+ filename: path.resolve(options.log),
+ format: combine(timestamp(), fileFormat),
+ options: { flags: 'w' }
+ })
+ )
+ }
+
+ return createLogger({
+ level: options.logLevel,
+ transports: t
+ })
+}
diff --git a/src/programs.js b/src/programs.js
new file mode 100644
index 0000000..dfbb933
--- /dev/null
+++ b/src/programs.js
@@ -0,0 +1,29 @@
+const dayjs = require('dayjs')
+const { isPromise } = require('./utils')
+
+module.exports.parse = parse
+
+async function parse(data) {
+ const { config, channel } = data
+ let programs = config.parser(data)
+
+ if (isPromise(programs)) {
+ programs = await programs
+ }
+
+ if (!Array.isArray(programs)) {
+ throw new Error('Parser should return an array')
+ }
+
+ return programs
+ .filter(i => i)
+ .map(program => {
+ program.channel = channel.xmltv_id
+
+ return program
+ })
+}
+
+function toBaseObject(data) {
+ return data
+}
diff --git a/src/utils.js b/src/utils.js
index 5fab4a6..5904034 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -1,127 +1,34 @@
-const fs = require('fs')
-const { padStart } = require('lodash')
-const path = require('path')
-const axios = require('axios').default
-const axiosCookieJarSupport = require('axios-cookiejar-support').default
-const { setupCache } = require('axios-cache-interceptor')
-const tough = require('tough-cookie')
-const convert = require('xml-js')
-const { merge } = require('lodash')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
-const { CurlGenerator } = require('curl-generator')
+
dayjs.extend(utc)
-axiosCookieJarSupport(axios)
-let timeout
-const utils = {}
-const defaultUserAgent =
- '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'
+module.exports.sleep = sleep
+module.exports.getUTCDate = getUTCDate
+module.exports.isPromise = isPromise
+module.exports.isObject = isObject
+module.exports.escapeString = escapeString
+module.exports.parseInteger = parseInteger
-utils.loadConfig = function (config) {
- if (!config.site) throw new Error("The required 'site' property is missing")
- if (!config.url) throw new Error("The required 'url' property is missing")
- if (typeof config.url !== 'function' && typeof config.url !== 'string')
- throw new Error("The 'url' property should return the function or string")
- if (!config.parser) throw new Error("The required 'parser' function is missing")
- if (typeof config.parser !== 'function')
- throw new Error("The 'parser' property should return the function")
- if (config.logo && typeof config.logo !== 'function')
- throw new Error("The 'logo' property should return the function")
-
- const defaultConfig = {
- days: 1,
- lang: 'en',
- delay: 3000,
- output: 'guide.xml',
- request: {
- method: 'GET',
- maxContentLength: 5 * 1024 * 1024,
- timeout: 5000,
- withCredentials: true,
- jar: new tough.CookieJar(),
- responseType: 'arraybuffer',
- cache: false
- }
- }
-
- return merge(defaultConfig, config)
-}
-
-utils.createClient = function (config) {
- const client = setupCache(axios.create())
- client.interceptors.request.use(
- function (request) {
- if (config.debug) {
- console.log('Request:', JSON.stringify(request, null, 2))
- }
- return request
- },
- function (error) {
- return Promise.reject(error)
- }
- )
- client.interceptors.response.use(
- function (response) {
- if (config.debug) {
- const data =
- utils.isObject(response.data) || Array.isArray(response.data)
- ? JSON.stringify(response.data)
- : response.data.toString()
- console.log(
- 'Response:',
- JSON.stringify(
- {
- headers: response.headers,
- data,
- cached: response.cached
- },
- null,
- 2
- )
- )
- }
-
- clearTimeout(timeout)
- return response
- },
- function (error) {
- clearTimeout(timeout)
- return Promise.reject(error)
- }
- )
-
- return client
-}
-
-utils.parseChannels = function (xml) {
- const result = convert.xml2js(xml)
- const siteTag = result.elements.find(el => el.name === 'site') || {}
- if (!siteTag.elements) return []
- const site = siteTag.attributes.site
-
- const channelsTag = siteTag.elements.find(el => el.name === 'channels')
- if (!channelsTag.elements) return []
-
- const channels = channelsTag.elements
- .filter(el => el.name === 'channel')
- .map(el => {
- const channel = el.attributes
- if (!el.elements) throw new Error(`Channel '${channel.xmltv_id}' has no valid name`)
- channel.name = el.elements.find(el => el.type === 'text').text
- channel.site = channel.site || site
-
- return channel
- })
-
- return { site, channels }
-}
-
-utils.sleep = function (ms) {
+function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
-utils.escapeString = function (string, defaultValue = '') {
+function isObject(a) {
+ return !!a && a.constructor === Object
+}
+
+function isPromise(promise) {
+ return !!promise && typeof promise.then === 'function'
+}
+
+function getUTCDate(d = null) {
+ if (typeof d === 'string') return dayjs.utc(d).startOf('d')
+
+ return dayjs.utc().startOf('d')
+}
+
+function escapeString(string, defaultValue = '') {
if (!string) return defaultValue
const regex = new RegExp(
@@ -149,372 +56,6 @@ utils.escapeString = function (string, defaultValue = '') {
.trim()
}
-utils.convertToXMLTV = function ({ channels, programs, date = dayjs.utc() }) {
- let output = `\r\n`
- for (let channel of channels) {
- const id = utils.escapeString(channel['xmltv_id'])
- const displayName = utils.escapeString(channel.name)
- output += `${displayName}`
- if (channel.logo) {
- const logo = utils.escapeString(channel.logo)
- output += ``
- }
- if (channel.site) {
- const url = channel.site ? 'https://' + channel.site : null
- output += `${url}`
- }
- output += `\r\n`
- }
-
- for (let program of programs) {
- if (!program) continue
-
- const channel = utils.escapeString(program.channel)
- const title = utils.escapeString(program.title)
- const description = utils.escapeString(program.description)
- const categories = Array.isArray(program.category) ? program.category : [program.category]
- const start = program.start ? dayjs.unix(program.start).utc().format('YYYYMMDDHHmmss ZZ') : ''
- const stop = program.stop ? dayjs.unix(program.stop).utc().format('YYYYMMDDHHmmss ZZ') : ''
- const lang = program.lang || 'en'
- const xmltv_ns = createXMLTVNS(program.season, program.episode)
- const onscreen = createOnScreen(program.season, program.episode)
- const date = program.date || ''
- const credits = createCredits({
- director: program.director,
- actor: program.actor,
- writer: program.writer,
- adapter: program.adapter,
- producer: program.producer,
- composer: program.composer,
- editor: program.editor,
- presenter: program.presenter,
- commentator: program.commentator,
- guest: program.guest
- })
- const icon = utils.escapeString(program.icon)
- const sub_title = utils.escapeString(program.sub_title)
- const url = program.url ? createURL(program.url, channel) : ''
-
- if (start && stop && title) {
- output += `${title}`
-
- if (sub_title) {
- output += `${sub_title}`
- }
-
- if (description) {
- output += `${description}`
- }
-
- if (categories.length) {
- categories.forEach(category => {
- if (category) {
- output += `${utils.escapeString(category)}`
- }
- })
- }
-
- if (url) {
- output += url
- }
-
- if (xmltv_ns) {
- output += `${xmltv_ns}`
- }
-
- if (onscreen) {
- output += `${onscreen}`
- }
- if (date) {
- output += `${date}`
- }
-
- if (icon) {
- output += ``
- }
-
- if (credits) {
- output += `${credits}`
- }
-
- output += '\r\n'
- }
- }
-
- output += ''
-
- function createXMLTVNS(s, e) {
- if (!e) return null
- s = s || 1
-
- return `${s - 1}.${e - 1}.0/1`
- }
-
- function createOnScreen(s, e) {
- if (!e) return null
- s = s || 1
-
- s = padStart(s, 2, '0')
- e = padStart(e, 2, '0')
-
- return `S${s}E${e}`
- }
-
- function createURL(urlObj, channel = '') {
- const urls = Array.isArray(urlObj) ? urlObj : [urlObj]
- let output = ''
- for (let url of urls) {
- if (typeof url === 'string' || url instanceof String) {
- url = { value: url }
- }
-
- let attr = url.system ? ` system="${url.system}"` : ''
- if (url.value.includes('http')) {
- output += `${url.value}`
- } else if (channel) {
- let chan = channels.find(c => c.xmltv_id.localeCompare(channel) === 0)
- if (chan && chan.site) {
- output += `https://${chan.site}${url.value}`
- }
- }
- }
-
- return output
- }
-
- function createImage(imgObj, channel = '') {
- const imgs = Array.isArray(imgObj) ? imgObj : [imgObj]
- let output = ''
- for (let img of imgs) {
- if (typeof img === 'string' || img instanceof String) {
- img = { value: img }
- }
-
- const imageTypes = ['poster', 'backdrop', 'still', 'person', 'character']
- const imageSizes = ['1', '2', '3']
- const imageOrients = ['P', 'L']
-
- let attr = ''
-
- if (img.type && imageTypes.some(el => img.type.includes(el))) {
- attr += ` type="${img.type}"`
- }
-
- if (img.size && imageSizes.some(el => img.size.includes(el))) {
- attr += ` size="${img.size}"`
- }
-
- if (img.orient && imageOrients.some(el => img.orient.includes(el))) {
- attr += ` orient="${img.orient}"`
- }
-
- if (img.system) {
- attr += ` system="${img.system}"`
- }
-
- if (img.value.includes('http')) {
- output += `${img.value}`
- } else if (channel) {
- let chan = channels.find(c => c.xmltv_id.localeCompare(channel) === 0)
- if (chan && chan.site) {
- output += `https://${chan.site}${img.value}`
- }
- }
- }
-
- return output
- }
-
- function createCredits(obj) {
- let cast = Object.entries(obj)
- .filter(x => x[1])
- .map(([name, value]) => ({ name, value }))
-
- let output = ''
- for (let type of cast) {
- const r = Array.isArray(type.value) ? type.value : [type.value]
- for (let person of r) {
- if (typeof person === 'string' || person instanceof String) {
- person = { value: person }
- }
-
- let attr = ''
- if (type.name.localeCompare('actor') === 0 && type.value.role) {
- attr += ` role="${type.value.role}"`
- }
- if (type.name.localeCompare('actor') === 0 && type.value.guest) {
- attr += ` guest="${type.value.guest}"`
- }
- output += `<${type.name}${attr}>${person.value}`
-
- if (person.url) {
- output += createURL(person.url)
- }
- if (person.image) {
- output += createImage(person.image)
- }
-
- output += `${type.name}>`
- }
- }
- return output
- }
- return output
+function parseInteger(val) {
+ return val ? parseInt(val) : null
}
-
-utils.writeToFile = function (filename, data) {
- const dir = path.resolve(path.dirname(filename))
- if (!fs.existsSync(dir)) {
- fs.mkdirSync(dir, { recursive: true })
- }
-
- fs.writeFileSync(path.resolve(filename), data)
-}
-
-utils.buildRequest = async function (item, config) {
- const CancelToken = axios.CancelToken
- const source = CancelToken.source()
- const request = { ...config.request }
- timeout = setTimeout(() => {
- source.cancel('Connection timeout')
- }, request.timeout)
- const headers = await utils.getRequestHeaders(item, config)
- request.headers = { 'User-Agent': defaultUserAgent, ...headers }
- request.url = await utils.getRequestUrl(item, config)
- request.data = await utils.getRequestData(item, config)
- request.cancelToken = source.token
-
- if (config.curl) {
- const curl = CurlGenerator({
- url: request.url,
- method: request.method,
- headers: request.headers,
- body: request.data
- })
- console.log(curl)
- }
-
- return request
-}
-
-utils.fetchData = function (client, request) {
- return client(request)
-}
-
-utils.getRequestHeaders = async function (item, config) {
- if (typeof config.request.headers === 'function') {
- const headers = config.request.headers(item)
- if (this.isPromise(headers)) {
- return await headers
- }
- return headers
- }
- return config.request.headers || null
-}
-
-utils.getRequestData = async function (item, config) {
- if (typeof config.request.data === 'function') {
- const data = config.request.data(item)
- if (this.isPromise(data)) {
- return await data
- }
- return data
- }
- return config.request.data || null
-}
-
-utils.getRequestUrl = async function (item, config) {
- if (typeof config.url === 'function') {
- const url = config.url(item)
- if (this.isPromise(url)) {
- return await url
- }
- return url
- }
- return config.url
-}
-
-utils.getUTCDate = function (d = null) {
- if (typeof d === 'string') return dayjs.utc(d).startOf('d')
-
- return dayjs.utc().startOf('d')
-}
-
-utils.parseResponse = async (item, response, config) => {
- const data = merge(item, config, {
- content: response.data.toString(),
- buffer: response.data,
- headers: response.headers,
- request: response.request,
- cached: response.cached
- })
-
- if (!item.channel.logo && config.logo) {
- data.channel.logo = await utils.loadLogo(data, config)
- }
-
- return await utils.parsePrograms(data, config)
-}
-
-utils.parsePrograms = async function (data, config) {
- let programs = config.parser(data)
-
- if (this.isPromise(programs)) {
- programs = await programs
- }
-
- if (!Array.isArray(programs)) {
- throw new Error('Parser should return an array')
- }
-
- const channel = data.channel
- return programs
- .filter(i => i)
- .map(program => {
- return {
- title: program.title,
- description: program.description || null,
- category: program.category || null,
- season: program.season || null,
- episode: program.episode || null,
- sub_title: program.sub_title || null,
- url: program.url || null,
- icon: program.icon || null,
- channel: channel.xmltv_id,
- lang: program.lang || channel.lang || config.lang || 'en',
- start: program.start ? dayjs(program.start).unix() : null,
- stop: program.stop ? dayjs(program.stop).unix() : null,
- date: program.date || null,
- director: program.director || null,
- actor: program.actor || null,
- writer: program.writer || null,
- adapter: program.adapter || null,
- producer: program.producer || null,
- composer: program.composer || null,
- editor: program.editor || null,
- presenter: program.presenter || null,
- commentator: program.commentator || null,
- guest: program.guest || null
- }
- })
-}
-
-utils.loadLogo = async function (options, config) {
- const logo = config.logo(options)
- if (this.isPromise(logo)) {
- return await logo
- }
- return logo
-}
-
-utils.isPromise = function (promise) {
- return !!promise && typeof promise.then === 'function'
-}
-
-utils.isObject = function (a) {
- return !!a && a.constructor === Object
-}
-
-module.exports = utils
diff --git a/src/xmltv.js b/src/xmltv.js
new file mode 100644
index 0000000..acac35f
--- /dev/null
+++ b/src/xmltv.js
@@ -0,0 +1,224 @@
+const { padStart } = require('lodash')
+const { escapeString, getUTCDate } = require('./utils')
+const dayjs = require('dayjs')
+const utc = require('dayjs/plugin/utc')
+
+dayjs.extend(utc)
+
+module.exports.generate = generate
+
+function generate({ channels, programs, date = getUTCDate() }) {
+ let output = `\r\n`
+ for (let channel of channels) {
+ const id = escapeString(channel['xmltv_id'])
+ const displayName = escapeString(channel.name)
+ output += `${displayName}`
+ if (channel.logo) {
+ const logo = escapeString(channel.logo)
+ output += ``
+ }
+ if (channel.site) {
+ const url = channel.site ? 'https://' + channel.site : null
+ output += `${url}`
+ }
+ output += `\r\n`
+ }
+
+ for (let program of programs) {
+ if (!program) continue
+
+ const channel = escapeString(program.channel)
+ const title = escapeString(program.title)
+ const description = escapeString(program.description)
+ const categories = Array.isArray(program.category) ? program.category : [program.category]
+ const start = program.start ? dayjs.unix(program.start).utc().format('YYYYMMDDHHmmss ZZ') : ''
+ const stop = program.stop ? dayjs.unix(program.stop).utc().format('YYYYMMDDHHmmss ZZ') : ''
+ const lang = program.lang || 'en'
+ const xmltv_ns = createXMLTVNS(program.season, program.episode)
+ const onscreen = createOnScreen(program.season, program.episode)
+ const date = program.date || ''
+ const credits = createCredits({
+ director: program.director,
+ actor: program.actor,
+ writer: program.writer,
+ adapter: program.adapter,
+ producer: program.producer,
+ composer: program.composer,
+ editor: program.editor,
+ presenter: program.presenter,
+ commentator: program.commentator,
+ guest: program.guest
+ })
+ const icon = escapeString(program.icon)
+ const sub_title = escapeString(program.sub_title)
+ const url = program.url ? createURL(program.url, channel) : ''
+
+ if (start && stop && title) {
+ output += `${title}`
+
+ if (sub_title) {
+ output += `${sub_title}`
+ }
+
+ if (description) {
+ output += `${description}`
+ }
+
+ if (categories.length) {
+ categories.forEach(category => {
+ if (category) {
+ output += `${escapeString(category)}`
+ }
+ })
+ }
+
+ if (url) {
+ output += url
+ }
+
+ if (xmltv_ns) {
+ output += `${xmltv_ns}`
+ }
+
+ if (onscreen) {
+ output += `${onscreen}`
+ }
+ if (date) {
+ output += `${date}`
+ }
+
+ if (icon) {
+ output += ``
+ }
+
+ if (credits) {
+ output += `${credits}`
+ }
+
+ output += '\r\n'
+ }
+ }
+
+ output += ''
+
+ return output
+}
+
+function createXMLTVNS(s, e) {
+ if (!e) return null
+ s = s || 1
+
+ return `${s - 1}.${e - 1}.0/1`
+}
+
+function createOnScreen(s, e) {
+ if (!e) return null
+ s = s || 1
+
+ s = padStart(s, 2, '0')
+ e = padStart(e, 2, '0')
+
+ return `S${s}E${e}`
+}
+
+function createURL(urlObj, channel = '') {
+ const urls = Array.isArray(urlObj) ? urlObj : [urlObj]
+ let output = ''
+ for (let url of urls) {
+ if (typeof url === 'string' || url instanceof String) {
+ url = { value: url }
+ }
+
+ let attr = url.system ? ` system="${url.system}"` : ''
+ if (url.value.includes('http')) {
+ output += `${url.value}`
+ } else if (channel) {
+ let chan = channels.find(c => c.xmltv_id.localeCompare(channel) === 0)
+ if (chan && chan.site) {
+ output += `https://${chan.site}${url.value}`
+ }
+ }
+ }
+
+ return output
+}
+
+function createImage(imgObj, channel = '') {
+ const imgs = Array.isArray(imgObj) ? imgObj : [imgObj]
+ let output = ''
+ for (let img of imgs) {
+ if (typeof img === 'string' || img instanceof String) {
+ img = { value: img }
+ }
+
+ const imageTypes = ['poster', 'backdrop', 'still', 'person', 'character']
+ const imageSizes = ['1', '2', '3']
+ const imageOrients = ['P', 'L']
+
+ let attr = ''
+
+ if (img.type && imageTypes.some(el => img.type.includes(el))) {
+ attr += ` type="${img.type}"`
+ }
+
+ if (img.size && imageSizes.some(el => img.size.includes(el))) {
+ attr += ` size="${img.size}"`
+ }
+
+ if (img.orient && imageOrients.some(el => img.orient.includes(el))) {
+ attr += ` orient="${img.orient}"`
+ }
+
+ if (img.system) {
+ attr += ` system="${img.system}"`
+ }
+
+ if (img.value.includes('http')) {
+ output += `${img.value}`
+ } else if (channel) {
+ let chan = channels.find(c => c.xmltv_id.localeCompare(channel) === 0)
+ if (chan && chan.site) {
+ output += `https://${chan.site}${img.value}`
+ }
+ }
+ }
+
+ return output
+}
+
+function createCredits(obj) {
+ let cast = Object.entries(obj)
+ .filter(x => x[1])
+ .map(([name, value]) => ({ name, value }))
+
+ let output = ''
+ for (let type of cast) {
+ const r = Array.isArray(type.value) ? type.value : [type.value]
+ for (let person of r) {
+ if (typeof person === 'string' || person instanceof String) {
+ person = { value: person }
+ }
+
+ let attr = ''
+ if (type.name.localeCompare('actor') === 0 && type.value.role) {
+ attr += ` role="${type.value.role}"`
+ }
+ if (type.name.localeCompare('actor') === 0 && type.value.guest) {
+ attr += ` guest="${type.value.guest}"`
+ }
+ output += `<${type.name}${attr}>${person.value}`
+
+ if (person.url) {
+ output += createURL(person.url)
+ }
+ if (person.image) {
+ output += createImage(person.image)
+ }
+
+ output += `${type.name}>`
+ }
+ }
+ return output
+}
diff --git a/tests/channels.test.js b/tests/channels.test.js
new file mode 100644
index 0000000..b51558a
--- /dev/null
+++ b/tests/channels.test.js
@@ -0,0 +1,26 @@
+import { parse as parseChannels } from '../src/channels'
+import path from 'path'
+import fs from 'fs'
+
+it('can parse valid channels.xml', () => {
+ const file = fs.readFileSync('./tests/input/example.com.channels.xml', { encoding: 'utf-8' })
+ const { channels } = parseChannels(file)
+ expect(channels).toEqual([
+ {
+ name: '1 TV',
+ xmltv_id: '1TV.com',
+ site_id: '1',
+ site: 'example.com',
+ lang: 'fr',
+ logo: 'https://example.com/logos/1TV.png'
+ },
+ {
+ name: '2 TV',
+ xmltv_id: '2TV.com',
+ site_id: '2',
+ site: 'example.com',
+ lang: undefined,
+ logo: undefined
+ }
+ ])
+})
diff --git a/tests/client.test.js b/tests/client.test.js
new file mode 100644
index 0000000..06b18e3
--- /dev/null
+++ b/tests/client.test.js
@@ -0,0 +1,47 @@
+import { buildRequest, create as createClient } from '../src/client'
+
+const config = {
+ days: 1,
+ lang: 'en',
+ delay: 3000,
+ output: 'guide.xml',
+ request: {
+ method: 'POST',
+ maxContentLength: 5 * 1024 * 1024,
+ timeout: 5000,
+ withCredentials: true,
+ responseType: 'arraybuffer',
+ cache: false,
+ data: { accountID: '123' },
+ headers: {
+ 'Content-Type': 'application/json',
+ Cookie: 'abc=123',
+ 'User-Agent':
+ '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'
+ }
+ },
+ url: 'http://example.com/20210319/1tv.json'
+}
+
+it('can build request', done => {
+ buildRequest({ config })
+ .then(request => {
+ expect(request).toMatchObject({
+ data: { accountID: '123' },
+ headers: {
+ 'Content-Type': 'application/json',
+ Cookie: 'abc=123',
+ 'User-Agent':
+ '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'
+ },
+ maxContentLength: 5242880,
+ method: 'POST',
+ responseType: 'arraybuffer',
+ timeout: 5000,
+ url: 'http://example.com/20210319/1tv.json',
+ withCredentials: true
+ })
+ done()
+ })
+ .catch(done)
+})
diff --git a/tests/config.test.js b/tests/config.test.js
new file mode 100644
index 0000000..c2a51ce
--- /dev/null
+++ b/tests/config.test.js
@@ -0,0 +1,26 @@
+import { load as loadConfig } from '../src/config'
+import path from 'path'
+import fs from 'fs'
+
+it('can load config', () => {
+ const config = loadConfig(require(path.resolve('./tests/input/example.com.config.js')))
+ expect(config).toMatchObject({
+ days: 1,
+ delay: 3000,
+ lang: 'en',
+ site: 'example.com'
+ })
+ expect(config.request).toMatchObject({
+ timeout: 5000,
+ headers: {
+ 'Content-Type': 'application/json',
+ Cookie: 'abc=123'
+ }
+ })
+ expect(typeof config.request.data).toEqual('function')
+ expect(typeof config.url).toEqual('function')
+ expect(typeof config.logo).toEqual('function')
+ expect(config.request.data()).toEqual({ accountID: '123' })
+ expect(config.url()).toEqual('http://example.com/20210319/1tv.json')
+ expect(config.logo()).toEqual('http://example.com/logos/1TV.png?x=ัะตะปะปั&sid=777')
+})
diff --git a/tests/programs.test.js b/tests/programs.test.js
new file mode 100644
index 0000000..01106ed
--- /dev/null
+++ b/tests/programs.test.js
@@ -0,0 +1,66 @@
+import { parse as parsePrograms } from '../src/programs'
+
+const channel = { xmltv_id: '1tv', lang: 'en' }
+
+it('can parse programs', done => {
+ const config = {
+ parser: () => [
+ {
+ title: 'Title',
+ description: 'Description',
+ lang: 'en',
+ category: ['Category1', 'Category2'],
+ icon: 'https://example.com/image.jpg',
+ season: 9,
+ episode: 238,
+ start: 1640995200,
+ stop: 1640998800
+ }
+ ]
+ }
+
+ parsePrograms({ channel, config })
+ .then(programs => {
+ expect(programs).toMatchObject([
+ {
+ title: 'Title',
+ description: 'Description',
+ lang: 'en',
+ category: ['Category1', 'Category2'],
+ icon: 'https://example.com/image.jpg',
+ season: 9,
+ episode: 238,
+ start: 1640995200,
+ stop: 1640998800,
+ channel: '1tv'
+ }
+ ])
+ done()
+ })
+ .catch(done)
+})
+
+it('can parse programs async', done => {
+ const config = {
+ parser: async () => [
+ {
+ title: 'Title',
+ description: 'Description',
+ lang: 'en',
+ category: ['Category1', 'Category2'],
+ icon: 'https://example.com/image.jpg',
+ season: 9,
+ episode: 238,
+ start: 1640995200,
+ stop: 1640998800
+ }
+ ]
+ }
+
+ parsePrograms({ channel, config })
+ .then(programs => {
+ expect(programs.length).toBe(1)
+ done()
+ })
+ .catch(done)
+})
diff --git a/tests/utils.test.js b/tests/utils.test.js
index 3694501..4ebef97 100644
--- a/tests/utils.test.js
+++ b/tests/utils.test.js
@@ -1,401 +1,11 @@
-import mockAxios from 'jest-mock-axios'
-import utils from '../src/utils'
-import axios from 'axios'
-import path from 'path'
-import fs from 'fs'
-
-jest.useFakeTimers('modern').setSystemTime(new Date('2022-05-05'))
-
-it('can load valid config.js', () => {
- const config = utils.loadConfig(require(path.resolve('./tests/input/example.com.config.js')))
- expect(config).toMatchObject({
- days: 1,
- delay: 3000,
- lang: 'en',
- site: 'example.com'
- })
- expect(config.request).toMatchObject({
- timeout: 5000,
- headers: {
- 'Content-Type': 'application/json',
- Cookie: 'abc=123'
- }
- })
- expect(typeof config.request.data).toEqual('function')
- expect(typeof config.url).toEqual('function')
- expect(typeof config.logo).toEqual('function')
- expect(config.request.data()).toEqual({ accountID: '123' })
- expect(config.url()).toEqual('http://example.com/20210319/1tv.json')
- expect(config.logo()).toEqual('http://example.com/logos/1TV.png?x=ัะตะปะปั&sid=777')
-})
-
-it('can parse valid channels.xml', () => {
- const file = fs.readFileSync('./tests/input/example.com.channels.xml', { encoding: 'utf-8' })
- const { channels } = utils.parseChannels(file)
- expect(channels).toEqual([
- {
- name: '1 TV',
- xmltv_id: '1TV.com',
- site_id: '1',
- site: 'example.com',
- lang: 'fr',
- logo: 'https://example.com/logos/1TV.png'
- },
- {
- name: '2 TV',
- xmltv_id: '2TV.com',
- site_id: '2',
- site: 'example.com',
- lang: undefined,
- logo: undefined
- }
- ])
-})
-
-it('can convert object to xmltv string', () => {
- const file = fs.readFileSync('./tests/input/example.com.channels.xml', { encoding: 'utf-8' })
- const { channels } = utils.parseChannels(file)
- const programs = [
- {
- title: 'Program 1',
- sub_title: 'Sub-title & 1',
- description: 'Description for Program 1',
- url: 'http://example.com/title.html',
- start: 1616133600,
- stop: 1616135400,
- category: 'Test',
- season: 9,
- episode: 239,
- icon: 'https://example.com/images/Program1.png?x=ัะตะปะปั&sid=777',
- channel: '1TV.com',
- lang: 'it',
- date: '20220505',
- director: {
- value: 'Director 1',
- url: { value: 'http://example.com/director1.html', system: 'TestSystem' }
- },
- actor: ['Actor 1', 'Actor 2'],
- writer: 'Writer 1'
- }
- ]
- const output = utils.convertToXMLTV({ channels, programs })
- expect(output).toBe(
- '\r\n1 TVhttps://example.com\r\n2 TVhttps://example.com\r\nProgram 1Sub-title & 1Description for Program 1Testhttp://example.com/title.html8.238.0/1S09E23920220505Director 1http://example.com/director1.htmlActor 1Actor 2Writer 1\r\n'
- )
-})
-
-it('can convert object to xmltv string without season number', () => {
- const file = fs.readFileSync('./tests/input/example.com.channels.xml', { encoding: 'utf-8' })
- const { channels } = utils.parseChannels(file)
- const programs = [
- {
- title: 'Program 1',
- description: 'Description for Program 1',
- start: 1616133600,
- stop: 1616135400,
- category: 'Test',
- episode: 239,
- icon: 'https://example.com/images/Program1.png?x=ัะตะปะปั&sid=777',
- channel: '1TV.com',
- lang: 'it'
- }
- ]
- const output = utils.convertToXMLTV({ channels, programs })
- expect(output).toBe(
- '\r\n1 TVhttps://example.com\r\n2 TVhttps://example.com\r\nProgram 1Description for Program 1Test0.238.0/1S01E239\r\n'
- )
-})
-
-it('can convert object to xmltv string without episode number', () => {
- const file = fs.readFileSync('./tests/input/example.com.channels.xml', { encoding: 'utf-8' })
- const { channels } = utils.parseChannels(file)
- const programs = [
- {
- title: 'Program 1',
- description: 'Description for Program 1',
- start: 1616133600,
- stop: 1616135400,
- category: 'Test',
- season: 1,
- icon: 'https://example.com/images/Program1.png?x=ัะตะปะปั&sid=777',
- channel: '1TV.com',
- lang: 'it'
- }
- ]
- const output = utils.convertToXMLTV({ channels, programs })
- expect(output).toBe(
- '\r\n1 TVhttps://example.com\r\n2 TVhttps://example.com\r\nProgram 1Description for Program 1Test\r\n'
- )
-})
-
-it('can convert object to xmltv string without categories', () => {
- const channels = [
- {
- name: '1 TV',
- xmltv_id: '1TV.com',
- site_id: '1',
- site: 'example.com',
- lang: 'fr',
- logo: 'https://example.com/logos/1TV.png'
- }
- ]
- const programs = [
- {
- title: 'Program 1',
- start: 1616133600,
- stop: 1616135400,
- channel: '1TV.com',
- lang: 'it'
- }
- ]
- const config = { site: 'example.com' }
- const output = utils.convertToXMLTV({ config, channels, programs })
- expect(output).toBe(
- '\r\n1 TVhttps://example.com\r\nProgram 1\r\n'
- )
-})
-
-it('can convert object to xmltv string with multiple categories', () => {
- const file = fs.readFileSync('./tests/input/example.com.channels.xml', { encoding: 'utf-8' })
- const { channels } = utils.parseChannels(file)
- const programs = [
- {
- title: 'Program 1',
- description: 'Description for Program 1',
- start: 1616133600,
- stop: 1616135400,
- category: ['Test1', 'Test2'],
- icon: 'https://example.com/images/Program1.png?x=ัะตะปะปั&sid=777',
- channel: '1TV.com',
- lang: 'it'
- }
- ]
- const output = utils.convertToXMLTV({ channels, programs })
- expect(output).toBe(
- '\r\n1 TVhttps://example.com\r\n2 TVhttps://example.com\r\nProgram 1Description for Program 1Test1Test2\r\n'
- )
-})
-
-it('can convert object to xmltv string with multiple urls', () => {
- const file = fs.readFileSync('./tests/input/example.com.channels.xml', { encoding: 'utf-8' })
- const { channels } = utils.parseChannels(file)
- const programs = [
- {
- title: 'Program 1',
- description: 'Description for Program 1',
- start: 1616133600,
- stop: 1616135400,
- category: ['Test1', 'Test2'],
- url: [
- 'https://example.com/noattr.html',
- { value: 'https://example.com/attr.html', system: 'TestSystem' }
- ],
- icon: 'https://example.com/images/Program1.png?x=ัะตะปะปั&sid=777',
- channel: '1TV.com',
- lang: 'it'
- }
- ]
- const output = utils.convertToXMLTV({ channels, programs })
- expect(output).toBe(
- '\r\n1 TVhttps://example.com\r\n2 TVhttps://example.com\r\nProgram 1Description for Program 1Test1Test2https://example.com/noattr.htmlhttps://example.com/attr.html\r\n'
- )
-})
-
-it('can convert object to xmltv string with multiple images', () => {
- const file = fs.readFileSync('./tests/input/example.com.channels.xml', { encoding: 'utf-8' })
- const { channels } = utils.parseChannels(file)
- const programs = [
- {
- title: 'Program 1',
- description: 'Description for Program 1',
- start: 1616133600,
- stop: 1616135400,
- category: ['Test1', 'Test2'],
- url: [
- 'https://example.com/noattr.html',
- { value: 'https://example.com/attr.html', system: 'TestSystem' }
- ],
- actor: {
- value: 'Actor 1',
- image: [
- 'https://example.com/image1.jpg',
- {
- value: 'https://example.com/image2.jpg',
- type: 'person',
- size: '2',
- system: 'TestSystem',
- orient: 'P'
- }
- ]
- },
- icon: 'https://example.com/images/Program1.png?x=ัะตะปะปั&sid=777',
- channel: '1TV.com',
- lang: 'it'
- }
- ]
- const output = utils.convertToXMLTV({ channels, programs })
- expect(output).toBe(
- '\r\n1 TVhttps://example.com\r\n2 TVhttps://example.com\r\nProgram 1Description for Program 1Test1Test2https://example.com/noattr.htmlhttps://example.com/attr.htmlActor 1https://example.com/image1.jpghttps://example.com/image2.jpg\r\n'
- )
-})
-
-it('can convert object to xmltv string with multiple credits member', () => {
- const file = fs.readFileSync('./tests/input/example.com.channels.xml', { encoding: 'utf-8' })
- const { channels } = utils.parseChannels(file)
- const programs = [
- {
- title: 'Program 1',
- sub_title: 'Sub-title 1',
- description: 'Description for Program 1',
- url: 'http://example.com/title.html',
- start: 1616133600,
- stop: 1616135400,
- category: 'Test',
- season: 9,
- episode: 239,
- icon: 'https://example.com/images/Program1.png?x=ัะตะปะปั&sid=777',
- channel: '1TV.com',
- lang: 'it',
- date: '20220505',
- director: {
- value: 'Director 1',
- url: { value: 'http://example.com/director1.html', system: 'TestSystem' }
- },
- actor: {
- value: 'Actor 1',
- role: 'Manny',
- guest: 'yes',
- url: { value: 'http://example.com/actor1.html', system: 'TestSystem' }
- },
- writer: [
- { value: 'Writer 1', url: { value: 'http://example.com/w1.html', system: 'TestSystem' } },
- { value: 'Writer 2', url: { value: 'http://example.com/w2.html', system: 'TestSystem' } }
- ]
- }
- ]
- const output = utils.convertToXMLTV({ channels, programs })
- expect(output).toBe(
- '\r\n1 TVhttps://example.com\r\n2 TVhttps://example.com\r\nProgram 1Sub-title 1Description for Program 1Testhttp://example.com/title.html8.238.0/1S09E23920220505Director 1http://example.com/director1.htmlActor 1http://example.com/actor1.htmlWriter 1http://example.com/w1.htmlWriter 2http://example.com/w2.html\r\n'
- )
-})
+import { escapeString } from '../src/utils'
it('can escape string', () => {
const string = 'Mรบsica ัะตัั dun. &<>"\'\r\n'
- expect(utils.escapeString(string)).toBe('Mรบsica ัะตัั dun. &<>"'')
+ expect(escapeString(string)).toBe('Mรบsica ัะตัั dun. &<>"'')
})
it('can escape url', () => {
const string = 'http://example.com/logos/1TV.png?param1=val¶m2=val'
- expect(utils.escapeString(string)).toBe(
- 'http://example.com/logos/1TV.png?param1=val¶m2=val'
- )
-})
-
-it('can fetch data', () => {
- const request = {
- data: { accountID: '123' },
- headers: {
- 'Content-Type': 'application/json',
- Cookie: 'abc=123',
- 'User-Agent':
- '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'
- },
- maxContentLength: 5242880,
- method: 'POST',
- responseType: 'arraybuffer',
- timeout: 5000,
- url: 'http://example.com/20210319/1tv.json',
- withCredentials: true
- }
- utils.fetchData(mockAxios, request).then(jest.fn).catch(jest.fn)
- expect(mockAxios).toHaveBeenCalledWith(
- expect.objectContaining({
- data: { accountID: '123' },
- headers: {
- 'Content-Type': 'application/json',
- Cookie: 'abc=123',
- 'User-Agent':
- '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'
- },
- method: 'POST',
- responseType: 'arraybuffer',
- timeout: 5000,
- url: 'http://example.com/20210319/1tv.json',
- withCredentials: true
- })
- )
-})
-
-it('can build request async', done => {
- const config = utils.loadConfig(require(path.resolve('./tests/input/async.config.js')))
-
- utils
- .buildRequest({}, config)
- .then(request => {
- expect(request).toMatchObject({
- data: { accountID: '123' },
- headers: {
- 'Content-Type': 'application/json',
- Cookie: 'abc=123',
- 'User-Agent':
- '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'
- },
- maxContentLength: 5242880,
- method: 'POST',
- responseType: 'arraybuffer',
- timeout: 5000,
- url: 'http://example.com/20210319/1tv.json',
- withCredentials: true
- })
- done()
- })
- .catch(done)
-})
-
-it('can load logo async', done => {
- const config = utils.loadConfig(require(path.resolve('./tests/input/async.config.js')))
-
- utils
- .loadLogo({}, config)
- .then(logo => {
- expect(logo).toBe('http://example.com/logos/1TV.png?x=ัะตะปะปั&sid=777')
- done()
- })
- .catch(done)
-})
-
-it('can parse programs', done => {
- const config = utils.loadConfig(require(path.resolve('./tests/input/example.com.config.js')))
-
- utils
- .parsePrograms({ channel: { xmltv_id: '1tv', lang: 'en' } }, config)
- .then(programs => {
- expect(programs).toMatchObject([
- {
- title: 'Title',
- description: 'Description',
- lang: 'en',
- category: ['Category1', 'Category2'],
- icon: 'https://example.com/image.jpg',
- season: 9,
- episode: 238,
- start: 1640995200,
- stop: 1640998800
- }
- ])
- done()
- })
- .catch(done)
-})
-
-it('can parse programs async', done => {
- const config = utils.loadConfig(require(path.resolve('./tests/input/async.config.js')))
-
- utils
- .parsePrograms({ channel: { xmltv_id: '1tv', lang: 'en' } }, config)
- .then(programs => {
- expect(programs.length).toBe(0)
- done()
- })
- .catch(done)
+ expect(escapeString(string)).toBe('http://example.com/logos/1TV.png?param1=val¶m2=val')
})
diff --git a/tests/xmltv.test.js b/tests/xmltv.test.js
new file mode 100644
index 0000000..a1d70a8
--- /dev/null
+++ b/tests/xmltv.test.js
@@ -0,0 +1,220 @@
+import xmltv from '../src/xmltv'
+
+jest.useFakeTimers('modern').setSystemTime(new Date('2022-05-05'))
+
+const channels = [
+ {
+ xmltv_id: '1TV.com',
+ name: '1 TV',
+ logo: 'https://example.com/logos/1TV.png',
+ site: 'example.com'
+ },
+ {
+ xmltv_id: '2TV.com',
+ name: '2 TV',
+ site: 'example.com'
+ }
+]
+
+it('can generate xmltv', () => {
+ const programs = [
+ {
+ title: 'Program 1',
+ sub_title: 'Sub-title & 1',
+ description: 'Description for Program 1',
+ url: 'http://example.com/title.html',
+ start: 1616133600,
+ stop: 1616135400,
+ category: 'Test',
+ season: 9,
+ episode: 239,
+ icon: 'https://example.com/images/Program1.png?x=ัะตะปะปั&sid=777',
+ channel: '1TV.com',
+ lang: 'it',
+ date: '20220505',
+ director: {
+ value: 'Director 1',
+ url: { value: 'http://example.com/director1.html', system: 'TestSystem' }
+ },
+ actor: ['Actor 1', 'Actor 2'],
+ writer: 'Writer 1'
+ }
+ ]
+ const output = xmltv.generate({ channels, programs })
+ expect(output).toBe(
+ '\r\n1 TVhttps://example.com\r\n2 TVhttps://example.com\r\nProgram 1Sub-title & 1Description for Program 1Testhttp://example.com/title.html8.238.0/1S09E23920220505Director 1http://example.com/director1.htmlActor 1Actor 2Writer 1\r\n'
+ )
+})
+
+it('can generate xmltv without season number', () => {
+ const programs = [
+ {
+ title: 'Program 1',
+ description: 'Description for Program 1',
+ start: 1616133600,
+ stop: 1616135400,
+ category: 'Test',
+ episode: 239,
+ icon: 'https://example.com/images/Program1.png?x=ัะตะปะปั&sid=777',
+ channel: '1TV.com',
+ lang: 'it'
+ }
+ ]
+ const output = xmltv.generate({ channels, programs })
+ expect(output).toBe(
+ '\r\n1 TVhttps://example.com\r\n2 TVhttps://example.com\r\nProgram 1Description for Program 1Test0.238.0/1S01E239\r\n'
+ )
+})
+
+it('can generate xmltv without episode number', () => {
+ const programs = [
+ {
+ title: 'Program 1',
+ description: 'Description for Program 1',
+ start: 1616133600,
+ stop: 1616135400,
+ category: 'Test',
+ season: 1,
+ icon: 'https://example.com/images/Program1.png?x=ัะตะปะปั&sid=777',
+ channel: '1TV.com',
+ lang: 'it'
+ }
+ ]
+ const output = xmltv.generate({ channels, programs })
+ expect(output).toBe(
+ '\r\n1 TVhttps://example.com\r\n2 TVhttps://example.com\r\nProgram 1Description for Program 1Test\r\n'
+ )
+})
+
+it('can generate xmltv without categories', () => {
+ const programs = [
+ {
+ title: 'Program 1',
+ start: 1616133600,
+ stop: 1616135400,
+ channel: '1TV.com',
+ lang: 'it'
+ }
+ ]
+ const config = { site: 'example.com' }
+ const output = xmltv.generate({ config, channels, programs })
+ expect(output).toBe(
+ '\r\n1 TVhttps://example.com\r\n2 TVhttps://example.com\r\nProgram 1\r\n'
+ )
+})
+
+it('can generate xmltv with multiple categories', () => {
+ const programs = [
+ {
+ title: 'Program 1',
+ description: 'Description for Program 1',
+ start: 1616133600,
+ stop: 1616135400,
+ category: ['Test1', 'Test2'],
+ icon: 'https://example.com/images/Program1.png?x=ัะตะปะปั&sid=777',
+ channel: '1TV.com',
+ lang: 'it'
+ }
+ ]
+ const output = xmltv.generate({ channels, programs })
+ expect(output).toBe(
+ '\r\n1 TVhttps://example.com\r\n2 TVhttps://example.com\r\nProgram 1Description for Program 1Test1Test2\r\n'
+ )
+})
+
+it('can generate xmltv with multiple urls', () => {
+ const programs = [
+ {
+ title: 'Program 1',
+ description: 'Description for Program 1',
+ start: 1616133600,
+ stop: 1616135400,
+ category: ['Test1', 'Test2'],
+ url: [
+ 'https://example.com/noattr.html',
+ { value: 'https://example.com/attr.html', system: 'TestSystem' }
+ ],
+ icon: 'https://example.com/images/Program1.png?x=ัะตะปะปั&sid=777',
+ channel: '1TV.com',
+ lang: 'it'
+ }
+ ]
+ const output = xmltv.generate({ channels, programs })
+ expect(output).toBe(
+ '\r\n1 TVhttps://example.com\r\n2 TVhttps://example.com\r\nProgram 1Description for Program 1Test1Test2https://example.com/noattr.htmlhttps://example.com/attr.html\r\n'
+ )
+})
+
+it('can generate xmltv with multiple images', () => {
+ const programs = [
+ {
+ title: 'Program 1',
+ description: 'Description for Program 1',
+ start: 1616133600,
+ stop: 1616135400,
+ category: ['Test1', 'Test2'],
+ url: [
+ 'https://example.com/noattr.html',
+ { value: 'https://example.com/attr.html', system: 'TestSystem' }
+ ],
+ actor: {
+ value: 'Actor 1',
+ image: [
+ 'https://example.com/image1.jpg',
+ {
+ value: 'https://example.com/image2.jpg',
+ type: 'person',
+ size: '2',
+ system: 'TestSystem',
+ orient: 'P'
+ }
+ ]
+ },
+ icon: 'https://example.com/images/Program1.png?x=ัะตะปะปั&sid=777',
+ channel: '1TV.com',
+ lang: 'it'
+ }
+ ]
+ const output = xmltv.generate({ channels, programs })
+ expect(output).toBe(
+ '\r\n1 TVhttps://example.com\r\n2 TVhttps://example.com\r\nProgram 1Description for Program 1Test1Test2https://example.com/noattr.htmlhttps://example.com/attr.htmlActor 1https://example.com/image1.jpghttps://example.com/image2.jpg\r\n'
+ )
+})
+
+it('can generate xmltv with multiple credits member', () => {
+ const programs = [
+ {
+ title: 'Program 1',
+ sub_title: 'Sub-title 1',
+ description: 'Description for Program 1',
+ url: 'http://example.com/title.html',
+ start: 1616133600,
+ stop: 1616135400,
+ category: 'Test',
+ season: 9,
+ episode: 239,
+ icon: 'https://example.com/images/Program1.png?x=ัะตะปะปั&sid=777',
+ channel: '1TV.com',
+ lang: 'it',
+ date: '20220505',
+ director: {
+ value: 'Director 1',
+ url: { value: 'http://example.com/director1.html', system: 'TestSystem' }
+ },
+ actor: {
+ value: 'Actor 1',
+ role: 'Manny',
+ guest: 'yes',
+ url: { value: 'http://example.com/actor1.html', system: 'TestSystem' }
+ },
+ writer: [
+ { value: 'Writer 1', url: { value: 'http://example.com/w1.html', system: 'TestSystem' } },
+ { value: 'Writer 2', url: { value: 'http://example.com/w2.html', system: 'TestSystem' } }
+ ]
+ }
+ ]
+ const output = xmltv.generate({ channels, programs })
+ expect(output).toBe(
+ '\r\n1 TVhttps://example.com\r\n2 TVhttps://example.com\r\nProgram 1Sub-title 1Description for Program 1Testhttp://example.com/title.html8.238.0/1S09E23920220505Director 1http://example.com/director1.htmlActor 1http://example.com/actor1.htmlWriter 1http://example.com/w1.htmlWriter 2http://example.com/w2.html\r\n'
+ )
+})