Merge pull request #3 from freearhey/v0.8.0

v0.8.0
This commit is contained in:
Aleksandr Statciuk 2021-09-16 00:40:04 +03:00 committed by GitHub
commit c58852ef33
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 91 additions and 31 deletions

View File

@ -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)

28
package-lock.json generated
View File

@ -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": {

View File

@ -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"
}, },

View File

@ -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...')

View File

@ -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,

1
tests/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
output/

34
tests/index.test.js Normal file
View File

@ -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)
})

View File

@ -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',

View File

@ -0,0 +1,5 @@
module.exports = {
site: 'example.com',
url: () => 'http://example.com/20210319/1tv.json',
parser: () => []
}

View File

@ -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 => {