commit
c58852ef33
10
README.md
10
README.md
|
@ -17,17 +17,23 @@ epg-grabber --config=example.com.config.js
|
||||||
Arguments:
|
Arguments:
|
||||||
|
|
||||||
- `-c, --config`: path to config file
|
- `-c, --config`: path to config file
|
||||||
- `-d, --debug`: enable debug mode
|
- `-o, --output`: path to output file (default: 'guide.xml')
|
||||||
|
- `--channels`: path to list of channels (can be specified via config file)
|
||||||
|
- `--lang`: set default language for all programs (default: 'en')
|
||||||
|
- `--days`: number of days for which to grab the program (default: 1)
|
||||||
|
- `--delay`: delay between requests (default: 3000)
|
||||||
|
- `--debug`: enable debug mode (default: false)
|
||||||
|
|
||||||
#### example.com.config.js
|
#### example.com.config.js
|
||||||
|
|
||||||
```js
|
```js
|
||||||
module.exports = {
|
module.exports = {
|
||||||
lang: 'fr', // default language for all programs (default: 'en')
|
|
||||||
site: 'example.com', // site domain name (required)
|
site: 'example.com', // site domain name (required)
|
||||||
output: 'example.com.guide.xml', // path to output file (default: 'guide.xml')
|
output: 'example.com.guide.xml', // path to output file (default: 'guide.xml')
|
||||||
channels: 'example.com.channels.xml', // path to channels.xml file (required)
|
channels: 'example.com.channels.xml', // path to channels.xml file (required)
|
||||||
|
lang: 'fr', // default language for all programs (default: 'en')
|
||||||
days: 3, // number of days for which to grab the program (default: 1)
|
days: 3, // number of days for which to grab the program (default: 1)
|
||||||
|
delay: 5000, // delay between requests (default: 3000)
|
||||||
|
|
||||||
request: { // request options (details: https://github.com/axios/axios#request-config)
|
request: { // request options (details: https://github.com/axios/axios#request-config)
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "epg-grabber",
|
"name": "epg-grabber",
|
||||||
"version": "0.4.1",
|
"version": "0.7.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"version": "0.4.1",
|
"name": "epg-grabber",
|
||||||
|
"version": "0.7.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
|
@ -1652,7 +1653,6 @@
|
||||||
"jest-resolve": "^26.6.2",
|
"jest-resolve": "^26.6.2",
|
||||||
"jest-util": "^26.6.2",
|
"jest-util": "^26.6.2",
|
||||||
"jest-worker": "^26.6.2",
|
"jest-worker": "^26.6.2",
|
||||||
"node-notifier": "^8.0.0",
|
|
||||||
"slash": "^3.0.0",
|
"slash": "^3.0.0",
|
||||||
"source-map": "^0.6.0",
|
"source-map": "^0.6.0",
|
||||||
"string-length": "^4.0.1",
|
"string-length": "^4.0.1",
|
||||||
|
@ -2742,10 +2742,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001205",
|
"version": "1.0.30001257",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001205.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001257.tgz",
|
||||||
"integrity": "sha512-TL1GrS5V6LElbitPazidkBMD9sa448bQDDLrumDqaggmKFcuU2JW1wTOHJPukAcOMtEmLcmDJEzfRrf+GjM0Og==",
|
"integrity": "sha512-JN49KplOgHSXpIsVSF+LUyhD8PUp6xPpAXeRrrcBh4KBeP7W864jHn6RvzJgDlrReyeVjMFJL3PLpPvKIxlIHA==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/browserslist"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/capture-exit": {
|
"node_modules/capture-exit": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
|
@ -3338,8 +3342,7 @@
|
||||||
"esprima": "^4.0.1",
|
"esprima": "^4.0.1",
|
||||||
"estraverse": "^5.2.0",
|
"estraverse": "^5.2.0",
|
||||||
"esutils": "^2.0.2",
|
"esutils": "^2.0.2",
|
||||||
"optionator": "^0.8.1",
|
"optionator": "^0.8.1"
|
||||||
"source-map": "~0.6.1"
|
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"escodegen": "bin/escodegen.js",
|
"escodegen": "bin/escodegen.js",
|
||||||
|
@ -5371,7 +5374,6 @@
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
"anymatch": "^3.0.3",
|
"anymatch": "^3.0.3",
|
||||||
"fb-watchman": "^2.0.0",
|
"fb-watchman": "^2.0.0",
|
||||||
"fsevents": "^2.1.2",
|
|
||||||
"graceful-fs": "^4.2.4",
|
"graceful-fs": "^4.2.4",
|
||||||
"jest-regex-util": "^26.0.0",
|
"jest-regex-util": "^26.0.0",
|
||||||
"jest-serializer": "^26.6.2",
|
"jest-serializer": "^26.6.2",
|
||||||
|
@ -11788,9 +11790,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"caniuse-lite": {
|
"caniuse-lite": {
|
||||||
"version": "1.0.30001205",
|
"version": "1.0.30001257",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001205.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001257.tgz",
|
||||||
"integrity": "sha512-TL1GrS5V6LElbitPazidkBMD9sa448bQDDLrumDqaggmKFcuU2JW1wTOHJPukAcOMtEmLcmDJEzfRrf+GjM0Og==",
|
"integrity": "sha512-JN49KplOgHSXpIsVSF+LUyhD8PUp6xPpAXeRrrcBh4KBeP7W864jHn6RvzJgDlrReyeVjMFJL3PLpPvKIxlIHA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"capture-exit": {
|
"capture-exit": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "epg-grabber",
|
"name": "epg-grabber",
|
||||||
"version": "0.7.0",
|
"version": "0.8.0",
|
||||||
"description": "Node.js CLI tool for grabbing EPG from different sites",
|
"description": "Node.js CLI tool for grabbing EPG from different sites",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"preferGlobal": true,
|
"preferGlobal": true,
|
||||||
|
@ -20,6 +20,10 @@
|
||||||
],
|
],
|
||||||
"author": "Arhey",
|
"author": "Arhey",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/freearhey/epg-grabber.git"
|
||||||
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
},
|
},
|
||||||
|
|
|
@ -10,11 +10,16 @@ program
|
||||||
.version(version, '-v, --version')
|
.version(version, '-v, --version')
|
||||||
.description(description)
|
.description(description)
|
||||||
.option('-c, --config <config>', 'Path to [site].config.js file')
|
.option('-c, --config <config>', 'Path to [site].config.js file')
|
||||||
.option('-d, --debug', 'Enable debug mode')
|
.option('-o, --output <output>', 'Path to output file', 'guide.xml')
|
||||||
|
.option('--channels <channels>', 'Path to channels.xml file')
|
||||||
|
.option('--lang <lang>', 'Set default language for all programs', 'en')
|
||||||
|
.option('--days <days>', 'Number of days for which to grab the program', 1)
|
||||||
|
.option('--delay <delay>', 'Delay between requests (in mileseconds)', 3000)
|
||||||
|
.option('--debug', 'Enable debug mode', false)
|
||||||
.parse(process.argv)
|
.parse(process.argv)
|
||||||
|
|
||||||
const options = program.opts()
|
const options = program.opts()
|
||||||
const config = utils.loadConfig(options.config)
|
const config = utils.loadConfig(options)
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
console.log('\r\nStarting...')
|
console.log('\r\nStarting...')
|
||||||
|
|
18
src/utils.js
18
src/utils.js
|
@ -14,15 +14,19 @@ const utils = {}
|
||||||
const defaultUserAgent =
|
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'
|
'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'
|
||||||
|
|
||||||
utils.loadConfig = function (file) {
|
utils.loadConfig = function (options) {
|
||||||
|
const file = options.config
|
||||||
if (!file) throw new Error('Path to [site].config.js is missing')
|
if (!file) throw new Error('Path to [site].config.js is missing')
|
||||||
console.log(`Loading '${file}'...`)
|
console.log(`Loading '${file}'...`)
|
||||||
|
|
||||||
const configPath = path.resolve(file)
|
const configPath = path.resolve(file)
|
||||||
const config = require(configPath)
|
const config = require(configPath)
|
||||||
|
|
||||||
|
if (options.channels) config.channels = options.channels
|
||||||
|
else if (config.channels) config.channels = path.join(path.dirname(file), config.channels)
|
||||||
|
else throw new Error("The required 'channels' property is missing")
|
||||||
|
|
||||||
if (!config.site) throw new Error("The required 'site' property is missing")
|
if (!config.site) throw new Error("The required 'site' property is missing")
|
||||||
if (!config.channels) throw new Error("The required 'channels' property is missing")
|
|
||||||
if (!config.url) throw new Error("The required 'url' property is missing")
|
if (!config.url) throw new Error("The required 'url' property is missing")
|
||||||
if (typeof config.url !== 'function' && typeof config.url !== 'string')
|
if (typeof config.url !== 'function' && typeof config.url !== 'string')
|
||||||
throw new Error("The 'url' property should return the function or string")
|
throw new Error("The 'url' property should return the function or string")
|
||||||
|
@ -32,13 +36,11 @@ utils.loadConfig = function (file) {
|
||||||
if (config.logo && typeof config.logo !== 'function')
|
if (config.logo && typeof config.logo !== 'function')
|
||||||
throw new Error("The 'logo' property should return the function")
|
throw new Error("The 'logo' property should return the function")
|
||||||
|
|
||||||
config.channels = path.join(path.dirname(file), config.channels)
|
|
||||||
|
|
||||||
const defaultConfig = {
|
const defaultConfig = {
|
||||||
days: 1,
|
days: options.days ? parseInt(options.days) : 1,
|
||||||
lang: 'en',
|
lang: options.lang || 'en',
|
||||||
delay: 3000,
|
delay: options.delay ? parseInt(options.delay) : 3000,
|
||||||
output: 'guide.xml',
|
output: options.output || 'guide.xml',
|
||||||
request: {
|
request: {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
maxContentLength: 5 * 1024 * 1024,
|
maxContentLength: 5 * 1024 * 1024,
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
output/
|
|
@ -0,0 +1,34 @@
|
||||||
|
const { execSync } = require('child_process')
|
||||||
|
const pwd = `${__dirname}/..`
|
||||||
|
|
||||||
|
function stdoutResultTester(stdout) {
|
||||||
|
return [`Finish`].every(val => {
|
||||||
|
return RegExp(val).test(stdout)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
it('can load config', () => {
|
||||||
|
const result = execSync(`node ${pwd}/src/index.js --config=tests/input/example.com.config.js`, {
|
||||||
|
encoding: 'utf8'
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(stdoutResultTester(result)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('can load mini config', () => {
|
||||||
|
const result = execSync(
|
||||||
|
`node ${pwd}/src/index.js \
|
||||||
|
--config=tests/input/mini.config.js \
|
||||||
|
--channels=tests/input/example.com.channels.xml \
|
||||||
|
--output=tests/output/mini.guide.xml \
|
||||||
|
--lang=fr \
|
||||||
|
--days=3 \
|
||||||
|
--delay=5000`,
|
||||||
|
{
|
||||||
|
encoding: 'utf8'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(stdoutResultTester(result)).toBe(true)
|
||||||
|
expect(result.includes("File 'tests/output/mini.guide.xml' successfully saved")).toBe(true)
|
||||||
|
})
|
|
@ -1,6 +1,7 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'example.com',
|
site: 'example.com',
|
||||||
channels: 'example.com.channels.xml',
|
channels: 'example.com.channels.xml',
|
||||||
|
output: 'tests/output/guide.xml',
|
||||||
url: () => 'http://example.com/20210319/1tv.json',
|
url: () => 'http://example.com/20210319/1tv.json',
|
||||||
request: {
|
request: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
module.exports = {
|
||||||
|
site: 'example.com',
|
||||||
|
url: () => 'http://example.com/20210319/1tv.json',
|
||||||
|
parser: () => []
|
||||||
|
}
|
|
@ -2,13 +2,13 @@ import mockAxios from 'jest-mock-axios'
|
||||||
import utils from '../src/utils'
|
import utils from '../src/utils'
|
||||||
|
|
||||||
it('can load valid config.js', () => {
|
it('can load valid config.js', () => {
|
||||||
const config = utils.loadConfig('./tests/input/example.com.config.js')
|
const config = utils.loadConfig({ config: './tests/input/example.com.config.js' })
|
||||||
expect(config).toMatchObject({
|
expect(config).toMatchObject({
|
||||||
channels: 'tests/input/example.com.channels.xml',
|
channels: 'tests/input/example.com.channels.xml',
|
||||||
days: 1,
|
days: 1,
|
||||||
delay: 3000,
|
delay: 3000,
|
||||||
lang: 'en',
|
lang: 'en',
|
||||||
output: 'guide.xml',
|
output: 'tests/output/guide.xml',
|
||||||
site: 'example.com'
|
site: 'example.com'
|
||||||
})
|
})
|
||||||
expect(config.request).toMatchObject({
|
expect(config.request).toMatchObject({
|
||||||
|
@ -115,7 +115,7 @@ it('can fetch data', async () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can build request async', async () => {
|
it('can build request async', async () => {
|
||||||
const config = utils.loadConfig('./tests/input/async.config.js')
|
const config = utils.loadConfig({ config: './tests/input/async.config.js' })
|
||||||
return utils.buildRequest({}, config).then(request => {
|
return utils.buildRequest({}, config).then(request => {
|
||||||
expect(request).toMatchObject({
|
expect(request).toMatchObject({
|
||||||
data: { accountID: '123' },
|
data: { accountID: '123' },
|
||||||
|
@ -136,14 +136,14 @@ it('can build request async', async () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can load logo async', async () => {
|
it('can load logo async', async () => {
|
||||||
const config = utils.loadConfig('./tests/input/async.config.js')
|
const config = utils.loadConfig({ config: './tests/input/async.config.js' })
|
||||||
return utils.loadLogo({}, config).then(logo => {
|
return utils.loadLogo({}, config).then(logo => {
|
||||||
expect(logo).toBe('http://example.com/logos/1TV.png?x=шеллы&sid=777')
|
expect(logo).toBe('http://example.com/logos/1TV.png?x=шеллы&sid=777')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse programs async', async () => {
|
it('can parse programs async', async () => {
|
||||||
const config = utils.loadConfig('./tests/input/async.config.js')
|
const config = utils.loadConfig({ config: './tests/input/async.config.js' })
|
||||||
return utils
|
return utils
|
||||||
.parsePrograms({ channel: { xmltv_id: '1tv', lang: 'en' } }, config)
|
.parsePrograms({ channel: { xmltv_id: '1tv', lang: 'en' } }, config)
|
||||||
.then(programs => {
|
.then(programs => {
|
||||||
|
|
Loading…
Reference in New Issue