From 52ca0dbd15ea52665833dd5a2ef9051d476a7499 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Mon, 18 May 2026 20:47:10 +0300 Subject: [PATCH 01/10] Update GUIDES.md --- tests/__data__/expected/guides_update/GUIDES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/__data__/expected/guides_update/GUIDES.md b/tests/__data__/expected/guides_update/GUIDES.md index 2abafcce9..404494f86 100644 --- a/tests/__data__/expected/guides_update/GUIDES.md +++ b/tests/__data__/expected/guides_update/GUIDES.md @@ -10,4 +10,4 @@ -[How can I add my server to the list?](CONTRIBUTING.md#how-to-add-my-server-to-the-guides-md) +[How can I add my server to the list?](CONTRIBUTING.md#how-to-add-my-server-to-the-guidesmd) From 5777259f835cf3cf187570caf9af91dac4af6677 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Mon, 18 May 2026 20:47:31 +0300 Subject: [PATCH 02/10] Update grab.test.ts --- tests/commands/epg/grab.test.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/commands/epg/grab.test.ts b/tests/commands/epg/grab.test.ts index 743cb0fb7..382429599 100644 --- a/tests/commands/epg/grab.test.ts +++ b/tests/commands/epg/grab.test.ts @@ -113,6 +113,31 @@ describe('epg:grab', () => { expect(output).toEqual(expected) }) + it('can grab epg with GZIP environment variable', () => { + const cmd = `${ENV_VAR} GZIP=true npm run grab --- --channels=tests/__data__/input/epg_grab/sites/example2.com/example2.com.channels.xml --output="${path.resolve( + 'tests/__data__/output/guides/guide.xml' + )}"` + const stdout = execSync(cmd, { encoding: 'utf8' }) + if (process.env.DEBUG === 'true') console.log(cmd, stdout) + + expect(content('tests/__data__/output/guides/guide.xml')).toEqual( + content('tests/__data__/expected/epg_grab/gzip/guide.xml') + ) + + const outputString = pako.ungzip(fs.readFileSync('tests/__data__/output/guides/guide.xml.gz'), { + to: 'string' + }) + const expectedString = pako.ungzip( + fs.readFileSync('tests/__data__/expected/epg_grab/gzip/guide.xml.gz'), + { to: 'string' } + ) + + const output = new Set(outputString.split('\r\n')) + const expected = new Set(expectedString.split('\r\n')) + + expect(output).toEqual(expected) + }) + it('can grab epg with gzip option enabled', () => { const cmd = `${ENV_VAR} npm run grab --- --channels=tests/__data__/input/epg_grab/sites/example2.com/example2.com.channels.xml --output="${path.resolve( 'tests/__data__/output/guides/guide.xml' From 6c6a4fce05e20f07e386bfc84d8fecafad6f3bbf Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Mon, 18 May 2026 20:47:43 +0300 Subject: [PATCH 03/10] Update utils.ts --- scripts/core/utils.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/scripts/core/utils.ts b/scripts/core/utils.ts index 2ad7673c4..e1461bf07 100644 --- a/scripts/core/utils.ts +++ b/scripts/core/utils.ts @@ -114,3 +114,23 @@ export function parseNumber(value: string): number { export function parseList(value: string): string[] { return value.split(',') } + +export function parseBoolean(value: string | boolean | undefined): boolean { + if (value === undefined) return true + if (typeof value === 'boolean') return value + if (typeof value === 'string' && value.toLowerCase() === 'true') return true + + return false +} + +export function parseBooleanOrString(value: string | boolean): string | boolean { + if (value === undefined) return true + if (typeof value === 'boolean') return value + if (typeof value === 'string') { + const normalized = value.toLowerCase() + if (normalized === 'true') return true + if (normalized === 'false') return false + } + + return value +} From 6ec759c97230053b8886133c485d642c2448e6c0 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Mon, 18 May 2026 20:47:52 +0300 Subject: [PATCH 04/10] Update grab.ts --- scripts/commands/epg/grab.ts | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/scripts/commands/epg/grab.ts b/scripts/commands/epg/grab.ts index eb9ee55e3..122993ffc 100644 --- a/scripts/commands/epg/grab.ts +++ b/scripts/commands/epg/grab.ts @@ -1,4 +1,3 @@ -import { loadJs, parseProxy, parseNumber, parseList } from '../../core' import { Logger, Timer, Collection, Template } from '@freearhey/core' import epgGrabber, { EPGGrabber, EPGGrabberMock } from 'epg-grabber' import { CurlBody } from 'curl-generator/dist/bodies/body' @@ -15,6 +14,14 @@ import { data, loadData } from '../../api' import dayjs, { Dayjs } from 'dayjs' import merge from 'lodash.merge' import path from 'path' +import { + parseBooleanOrString, + parseBoolean, + parseNumber, + parseProxy, + parseList, + loadJs +} from '../../core' program .addOption( @@ -55,11 +62,19 @@ program .env('MAX_CONNECTIONS') ) .addOption( - new Option('--gzip [path]', 'Create a compressed version of the guide as well').env('GZIP') + new Option('--gzip [path]', 'Create a compressed version of the guide as well') + .argParser(parseBooleanOrString) + .env('GZIP') ) - .addOption(new Option('--json [path]', 'Create a JSON version of the guide as well').env('JSON')) - .addOption(new Option('--curl', 'Display each request as CURL').env('CURL')) - .addOption(new Option('--debug', 'Enable debug mode').env('DEBUG')) + .addOption( + new Option('--json [path]', 'Create a JSON version of the guide as well') + .argParser(parseBooleanOrString) + .env('JSON') + ) + .addOption( + new Option('--curl', 'Display each request as CURL').argParser(parseBoolean).env('CURL') + ) + .addOption(new Option('--debug', 'Enable debug mode').argParser(parseBoolean).env('DEBUG')) .parse() interface GrabOptions { From 147abb1899b6baf2c8001e768b38041ffc640c3f Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Sun, 24 May 2026 02:12:21 +0300 Subject: [PATCH 05/10] Update tests/__data__ --- tests/__data__/expected/guides_export/guides.json | 2 +- tests/__data__/input/data/feeds.json | 4 ++-- tests/__data__/input/data/workers.json | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 tests/__data__/input/data/workers.json diff --git a/tests/__data__/expected/guides_export/guides.json b/tests/__data__/expected/guides_export/guides.json index 1239c7edf..0c3047418 100644 --- a/tests/__data__/expected/guides_export/guides.json +++ b/tests/__data__/expected/guides_export/guides.json @@ -1 +1 @@ -[{"channel":"Channel1.us","feed":"SD","site":"example.com","site_id":"140","site_name":"Channel 1","lang":"en","sources":[]},{"channel":"Channel2.us","feed":null,"site":"example.com","site_id":"142","site_name":"Channel 2","lang":"en","sources":[]},{"channel":"Channel1.us","feed":"HD","site":"example.com","site_id":"140","site_name":"Channel 1","lang":"fr","sources":[]},{"channel":"Channel3.us","feed":"HD","site":"example2.com","site_id":"150","site_name":"Channel 3","lang":"en","sources":[]},{"channel":"Channel4.us","feed":null,"site":"example2.com","site_id":"152","site_name":"Channel 4","lang":"en","sources":[]},{"channel":"Channel1.us","feed":null,"site":"example2.com","site_id":"140","site_name":"Channel 1","lang":"fr","sources":[]}] \ No newline at end of file +[{"channel":"Channel1.us","feed":"SD","site":"example.com","site_id":"140","site_name":"Channel 1","lang":"en","sources":[{"host":"worker-9dd4.onrender.com","url":"https://worker-9dd4.onrender.com/guide.xml","format":"XML"}]},{"channel":"Channel2.us","feed":null,"site":"example.com","site_id":"142","site_name":"Channel 2","lang":"en","sources":[]},{"channel":"Channel1.us","feed":"HD","site":"example.com","site_id":"140","site_name":"Channel 1","lang":"fr","sources":[]},{"channel":"Channel3.us","feed":"HD","site":"example2.com","site_id":"150","site_name":"Channel 3","lang":"en","sources":[]},{"channel":"Channel4.us","feed":null,"site":"example2.com","site_id":"152","site_name":"Channel 4","lang":"en","sources":[]},{"channel":"Channel1.us","feed":null,"site":"example2.com","site_id":"140","site_name":"Channel 1","lang":"fr","sources":[]}] \ No newline at end of file diff --git a/tests/__data__/input/data/feeds.json b/tests/__data__/input/data/feeds.json index cf0e5e34a..4a7e8ed5d 100644 --- a/tests/__data__/input/data/feeds.json +++ b/tests/__data__/input/data/feeds.json @@ -1,8 +1,8 @@ [ { "channel": "CNNInternational.us", - "id": "SD", - "name": "SD", + "id": "MENA", + "name": "MENA", "is_main": true, "broadcast_area": [ "r/INT" diff --git a/tests/__data__/input/data/workers.json b/tests/__data__/input/data/workers.json new file mode 100644 index 000000000..3fd77eb7d --- /dev/null +++ b/tests/__data__/input/data/workers.json @@ -0,0 +1 @@ +[{"host":"worker-9dd4.onrender.com","channelsPath":"channels.xml","channels":[{"xmltv_id":"Channel1.us@SD","name":"Channel 1","site":"example.com","site_id":"140","lang":"en","logo":null,"url":null,"lcn":null,"index":0}],"guideXmlPath":"guide.xml","status":"OK","lastUpdated":"2026-05-03T00:00:00.000Z"}] \ No newline at end of file From 8ad22c2488f11431d0d1c1fb4d330d9c0eb472c8 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Sun, 24 May 2026 02:12:34 +0300 Subject: [PATCH 06/10] Update tests --- tests/commands/channels/format.test.ts | 4 +++- tests/commands/guides/export.test.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/commands/channels/format.test.ts b/tests/commands/channels/format.test.ts index 503d2a608..5ba3328bb 100644 --- a/tests/commands/channels/format.test.ts +++ b/tests/commands/channels/format.test.ts @@ -2,6 +2,8 @@ import { execSync } from 'child_process' import { pathToFileURL } from 'node:url' import fs from 'fs-extra' +const ENV_VAR = 'cross-env DATA_DIR=tests/__data__/input/data' + beforeEach(() => { fs.emptyDirSync('tests/__data__/output') fs.copySync( @@ -12,7 +14,7 @@ beforeEach(() => { describe('channels:format', () => { it('can format *.channels.xml files', () => { - const cmd = 'npm run channels:format --- tests/__data__/output/example.com.channels.xml' + const cmd = `${ENV_VAR} npm run channels:format --- tests/__data__/output/example.com.channels.xml` const stdout = execSync(cmd, { encoding: 'utf8' }) if (process.env.DEBUG === 'true') console.log(cmd, stdout) diff --git a/tests/commands/guides/export.test.ts b/tests/commands/guides/export.test.ts index ee787e8a2..3b826e24b 100644 --- a/tests/commands/guides/export.test.ts +++ b/tests/commands/guides/export.test.ts @@ -3,7 +3,7 @@ import fs from 'fs-extra' import { pathToFileURL } from 'node:url' const ENV_VAR = - 'cross-env SITES_DIR=tests/__data__/input/guides_export/sites API_DIR=tests/__data__/output' + 'cross-env SITES_DIR=tests/__data__/input/guides_export/sites DATA_DIR=tests/__data__/input/data API_DIR=tests/__data__/output' beforeEach(() => { fs.emptyDirSync('tests/__data__/output') From 745cc0344cb7f118f3675424bc0d6610214ab447 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Sun, 24 May 2026 02:12:44 +0300 Subject: [PATCH 07/10] Update load.ts --- scripts/commands/workers/load.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/commands/workers/load.ts b/scripts/commands/workers/load.ts index 7145c2c2c..4c2aa32d4 100644 --- a/scripts/commands/workers/load.ts +++ b/scripts/commands/workers/load.ts @@ -56,7 +56,7 @@ async function main() { if (!workerConfig) { worker.setStatus('MISSING_WORKER_CONFIG') - logger.error('Unable to load "workers.json"') + logger.error('Unable to load "worker.json"') continue } From 87c554d23a98ea1d1abbcd84718d8cfc2f290ccd Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Mon, 25 May 2026 07:29:32 +0300 Subject: [PATCH 08/10] Update grab.test.ts --- tests/commands/epg/grab.test.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/commands/epg/grab.test.ts b/tests/commands/epg/grab.test.ts index 382429599..89966c37c 100644 --- a/tests/commands/epg/grab.test.ts +++ b/tests/commands/epg/grab.test.ts @@ -138,7 +138,7 @@ describe('epg:grab', () => { expect(output).toEqual(expected) }) - it('can grab epg with gzip option enabled', () => { + it('can grab epg with gzip path', () => { const cmd = `${ENV_VAR} npm run grab --- --channels=tests/__data__/input/epg_grab/sites/example2.com/example2.com.channels.xml --output="${path.resolve( 'tests/__data__/output/guides/guide.xml' )}" --gzip="${path.resolve('tests/__data__/output/guides/custom.xml.gz')}"` @@ -182,10 +182,11 @@ describe('epg:grab', () => { ) }) - it('can grab epg with json option enabled', () => { - const cmd = `${ENV_VAR} npm run grab --- --channels=tests/__data__/input/epg_grab/sites/example2.com/example2.com.channels.xml --output="${path.resolve( + it('can grab epg with json path', () => { + const cmd = `${ENV_VAR} npm run grab --- --channels=tests/__data__/input/epg_grab/sites/example2.com/example2.com.channels.xml --output="${path.relative( + process.cwd(), 'tests/__data__/output/guides/guide.xml' - )}" --json="${path.resolve('tests/__data__/output/guides/custom.json')}"` + )}" --json="${path.relative(process.cwd(), 'tests/__data__/output/guides/custom.json')}"` const stdout = execSync(cmd, { encoding: 'utf8' }) if (process.env.DEBUG === 'true') console.log(cmd, stdout) From a62c32e0cebdbaf33ec2d867f1a5de28169b93ec Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Mon, 25 May 2026 07:31:29 +0300 Subject: [PATCH 09/10] Update guide.ts --- scripts/models/guide.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/scripts/models/guide.ts b/scripts/models/guide.ts index cb68ae1f1..2dab00a65 100644 --- a/scripts/models/guide.ts +++ b/scripts/models/guide.ts @@ -44,23 +44,21 @@ export class Guide { } async save({ logger }: { logger: Logger }) { - const dir = path.dirname(this.filepath) - const storage = new Storage(dir) - const xmlFilepath = this.filepath - const xmlFilename = path.basename(xmlFilepath) - logger.info(` saving to "${xmlFilepath}"...`) + const storage = new Storage() const xmltv = this.toString() - await storage.save(xmlFilename, xmltv) + const xmlFilepath = this.filepath + logger.info(` saving to "${xmlFilepath}"...`) + await storage.save(xmlFilepath, xmltv) if (this.gzip) { const compressed = pako.gzip(xmltv) const gzFilepath = typeof this.gzip === 'string' ? this.gzip : `${this.filepath}.gz` - const gzFilename = path.basename(gzFilepath) logger.info(` saving to "${gzFilepath}"...`) - await storage.save(gzFilename, compressed) + await storage.save(gzFilepath, compressed) } if (this.json) { + const dir = path.dirname(this.filepath) const filename = path.basename(this.filepath).split('.')[0] const jsonFilepath = typeof this.json === 'string' ? this.json : path.join(dir, `${filename}.json`) From b99a5f36da52726a4ccf3237e83500592a0a2280 Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Mon, 25 May 2026 07:31:36 +0300 Subject: [PATCH 10/10] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ce62a05e9..7c1dba2d5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ /custom.xml /guide.xml /guide.xml.gz +/guide.json .secrets /guides/ /dev/