mirror of
https://github.com/iptv-org/epg
synced 2026-06-30 12:17:11 -04:00
@@ -4,6 +4,7 @@
|
||||
/custom.xml
|
||||
/guide.xml
|
||||
/guide.xml.gz
|
||||
/guide.json
|
||||
.secrets
|
||||
/guides/
|
||||
/dev/
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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`)
|
||||
|
||||
@@ -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":[]}]
|
||||
[{"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":[]}]
|
||||
@@ -10,4 +10,4 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
[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)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
[
|
||||
{
|
||||
"channel": "CNNInternational.us",
|
||||
"id": "SD",
|
||||
"name": "SD",
|
||||
"id": "MENA",
|
||||
"name": "MENA",
|
||||
"is_main": true,
|
||||
"broadcast_area": [
|
||||
"r/INT"
|
||||
|
||||
@@ -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"}]
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -113,7 +113,32 @@ describe('epg:grab', () => {
|
||||
expect(output).toEqual(expected)
|
||||
})
|
||||
|
||||
it('can grab epg with gzip option enabled', () => {
|
||||
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 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')}"`
|
||||
@@ -157,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)
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user