diff --git a/README.md b/README.md index 5cb4b6e..e1e599d 100644 --- a/README.md +++ b/README.md @@ -17,17 +17,23 @@ epg-grabber --config=example.com.config.js Arguments: - `-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 ```js module.exports = { - lang: 'fr', // default language for all programs (default: 'en') site: 'example.com', // site domain name (required) output: 'example.com.guide.xml', // path to output file (default: 'guide.xml') 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) + delay: 5000, // delay between requests (default: 3000) request: { // request options (details: https://github.com/axios/axios#request-config) diff --git a/package-lock.json b/package-lock.json index 102c303..01e0474 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,12 @@ { "name": "epg-grabber", - "version": "0.4.1", + "version": "0.7.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "0.4.1", + "name": "epg-grabber", + "version": "0.7.0", "license": "MIT", "dependencies": { "axios": "^0.21.1", @@ -1652,7 +1653,6 @@ "jest-resolve": "^26.6.2", "jest-util": "^26.6.2", "jest-worker": "^26.6.2", - "node-notifier": "^8.0.0", "slash": "^3.0.0", "source-map": "^0.6.0", "string-length": "^4.0.1", @@ -2742,10 +2742,14 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001205", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001205.tgz", - "integrity": "sha512-TL1GrS5V6LElbitPazidkBMD9sa448bQDDLrumDqaggmKFcuU2JW1wTOHJPukAcOMtEmLcmDJEzfRrf+GjM0Og==", - "dev": true + "version": "1.0.30001257", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001257.tgz", + "integrity": "sha512-JN49KplOgHSXpIsVSF+LUyhD8PUp6xPpAXeRrrcBh4KBeP7W864jHn6RvzJgDlrReyeVjMFJL3PLpPvKIxlIHA==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + } }, "node_modules/capture-exit": { "version": "2.0.0", @@ -3338,8 +3342,7 @@ "esprima": "^4.0.1", "estraverse": "^5.2.0", "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" + "optionator": "^0.8.1" }, "bin": { "escodegen": "bin/escodegen.js", @@ -5371,7 +5374,6 @@ "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", - "fsevents": "^2.1.2", "graceful-fs": "^4.2.4", "jest-regex-util": "^26.0.0", "jest-serializer": "^26.6.2", @@ -11788,9 +11790,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001205", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001205.tgz", - "integrity": "sha512-TL1GrS5V6LElbitPazidkBMD9sa448bQDDLrumDqaggmKFcuU2JW1wTOHJPukAcOMtEmLcmDJEzfRrf+GjM0Og==", + "version": "1.0.30001257", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001257.tgz", + "integrity": "sha512-JN49KplOgHSXpIsVSF+LUyhD8PUp6xPpAXeRrrcBh4KBeP7W864jHn6RvzJgDlrReyeVjMFJL3PLpPvKIxlIHA==", "dev": true }, "capture-exit": { diff --git a/package.json b/package.json index 92d4fa7..73fdd42 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "epg-grabber", - "version": "0.7.0", + "version": "0.8.0", "description": "Node.js CLI tool for grabbing EPG from different sites", "main": "src/index.js", "preferGlobal": true, @@ -20,6 +20,10 @@ ], "author": "Arhey", "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/freearhey/epg-grabber.git" + }, "engines": { "node": ">=10.0.0" }, diff --git a/src/index.js b/src/index.js index ee4ff69..af22e5b 100755 --- a/src/index.js +++ b/src/index.js @@ -10,11 +10,16 @@ program .version(version, '-v, --version') .description(description) .option('-c, --config ', 'Path to [site].config.js file') - .option('-d, --debug', 'Enable debug mode') + .option('-o, --output ', 'Path to output file', 'guide.xml') + .option('--channels ', 'Path to channels.xml file') + .option('--lang ', 'Set default language for all programs', 'en') + .option('--days ', 'Number of days for which to grab the program', 1) + .option('--delay ', 'Delay between requests (in mileseconds)', 3000) + .option('--debug', 'Enable debug mode', false) .parse(process.argv) const options = program.opts() -const config = utils.loadConfig(options.config) +const config = utils.loadConfig(options) async function main() { console.log('\r\nStarting...') diff --git a/src/utils.js b/src/utils.js index 87fef59..ca1502b 100644 --- a/src/utils.js +++ b/src/utils.js @@ -14,15 +14,19 @@ 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' -utils.loadConfig = function (file) { +utils.loadConfig = function (options) { + const file = options.config if (!file) throw new Error('Path to [site].config.js is missing') console.log(`Loading '${file}'...`) const configPath = path.resolve(file) 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.channels) throw new Error("The required 'channels' 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") @@ -32,13 +36,11 @@ utils.loadConfig = function (file) { if (config.logo && typeof config.logo !== 'function') throw new Error("The 'logo' property should return the function") - config.channels = path.join(path.dirname(file), config.channels) - const defaultConfig = { - days: 1, - lang: 'en', - delay: 3000, - output: 'guide.xml', + days: options.days ? parseInt(options.days) : 1, + lang: options.lang || 'en', + delay: options.delay ? parseInt(options.delay) : 3000, + output: options.output || 'guide.xml', request: { method: 'GET', maxContentLength: 5 * 1024 * 1024, diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..9b1960e --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1 @@ +output/ \ No newline at end of file diff --git a/tests/index.test.js b/tests/index.test.js new file mode 100644 index 0000000..1132fdf --- /dev/null +++ b/tests/index.test.js @@ -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) +}) diff --git a/tests/input/example.com.config.js b/tests/input/example.com.config.js index c48a0f9..825db0a 100644 --- a/tests/input/example.com.config.js +++ b/tests/input/example.com.config.js @@ -1,6 +1,7 @@ module.exports = { site: 'example.com', channels: 'example.com.channels.xml', + output: 'tests/output/guide.xml', url: () => 'http://example.com/20210319/1tv.json', request: { method: 'POST', diff --git a/tests/input/mini.config.js b/tests/input/mini.config.js new file mode 100644 index 0000000..cf0d14a --- /dev/null +++ b/tests/input/mini.config.js @@ -0,0 +1,5 @@ +module.exports = { + site: 'example.com', + url: () => 'http://example.com/20210319/1tv.json', + parser: () => [] +} diff --git a/tests/utils.test.js b/tests/utils.test.js index df69795..f8d57aa 100644 --- a/tests/utils.test.js +++ b/tests/utils.test.js @@ -2,13 +2,13 @@ import mockAxios from 'jest-mock-axios' import utils from '../src/utils' 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({ channels: 'tests/input/example.com.channels.xml', days: 1, delay: 3000, lang: 'en', - output: 'guide.xml', + output: 'tests/output/guide.xml', site: 'example.com' }) expect(config.request).toMatchObject({ @@ -115,7 +115,7 @@ it('can fetch data', 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 => { expect(request).toMatchObject({ data: { accountID: '123' }, @@ -136,14 +136,14 @@ it('can build request 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 => { expect(logo).toBe('http://example.com/logos/1TV.png?x=шеллы&sid=777') }) }) 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 .parsePrograms({ channel: { xmltv_id: '1tv', lang: 'en' } }, config) .then(programs => {