mirror of
https://github.com/iptv-org/epg
synced 2025-12-16 10:26:41 -05:00
9
.gitignore
vendored
9
.gitignore
vendored
@@ -5,4 +5,11 @@
|
||||
/guide.xml.gz
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
|
||||
# If Yarn is used (yarn.lock)
|
||||
/.yarn/*
|
||||
.yarnrc
|
||||
.yarnrc.yml
|
||||
|
||||
.idea
|
||||
12160
package-lock.json
generated
12160
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
121
package.json
121
package.json
@@ -3,19 +3,17 @@
|
||||
"scripts": {
|
||||
"act:check": "act pull_request -W .github/workflows/check.yml",
|
||||
"act:update": "act workflow_dispatch -W .github/workflows/update.yml",
|
||||
"api:load": "npx tsx scripts/commands/api/load.ts",
|
||||
"api:generate": "npx tsx scripts/commands/api/generate.ts",
|
||||
"channels:lint": "npx tsx scripts/commands/channels/lint.mts",
|
||||
"channels:parse": "npx tsx scripts/commands/channels/parse.ts",
|
||||
"channels:edit": "npx tsx scripts/commands/channels/edit.ts",
|
||||
"channels:validate": "npx tsx scripts/commands/channels/validate.ts",
|
||||
"sites:init": "npx tsx scripts/commands/sites/init.ts",
|
||||
"sites:update": "npx tsx scripts/commands/sites/update.ts",
|
||||
"grab": "npx tsx scripts/commands/epg/grab.ts",
|
||||
"api:load": "tsx scripts/commands/api/load.ts",
|
||||
"api:generate": "tsx scripts/commands/api/generate.ts",
|
||||
"channels:lint": "tsx scripts/commands/channels/lint.mts",
|
||||
"channels:parse": "tsx scripts/commands/channels/parse.ts",
|
||||
"channels:edit": "tsx scripts/commands/channels/edit.ts",
|
||||
"channels:validate": "tsx scripts/commands/channels/validate.ts",
|
||||
"sites:init": "tsx scripts/commands/sites/init.ts",
|
||||
"sites:update": "tsx scripts/commands/sites/update.ts",
|
||||
"grab": "tsx scripts/commands/epg/grab.ts",
|
||||
"lint": "npx eslint \"{scripts,tests,sites}/**/*.{ts,mts,js}\"",
|
||||
"test": "run-script-os",
|
||||
"test:win32": "SET \"TZ=Pacific/Nauru\" && npx jest --runInBand",
|
||||
"test:default": "TZ=Pacific/Nauru npx jest --runInBand",
|
||||
"test": "cross-env TZ=Pacific/Nauru npx jest --runInBand",
|
||||
"postinstall": "skip-postinstall || npm run api:load",
|
||||
"prepare": "husky"
|
||||
},
|
||||
@@ -35,79 +33,84 @@
|
||||
],
|
||||
"testTimeout": 10000,
|
||||
"transformIgnorePatterns": [
|
||||
"<rootDir>/node_modules/(?!parse-duration/.*)"
|
||||
"<rootDir>/node_modules/(?!parse-duration/.*|@freearhey/core/.*|glob/.*|srcset/.*|balanced-match/.*|minimatch/.*)"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@alex_neo/jest-expect-message": "^1.0.5",
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
"@eslint/js": "^9.17.0",
|
||||
"@freearhey/core": "^0.7.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "^9.30.0",
|
||||
"@freearhey/core": "^0.8.2",
|
||||
"@freearhey/search-js": "^0.1.2",
|
||||
"@ntlab/sfetch": "^1.2.0",
|
||||
"@octokit/core": "^6.1.3",
|
||||
"@octokit/plugin-paginate-rest": "^11.3.6",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^13.2.6",
|
||||
"@swc/core": "^1.10.4",
|
||||
"@swc/jest": "^0.2.37",
|
||||
"@types/cli-progress": "^3.11.3",
|
||||
"@types/fs-extra": "^11.0.2",
|
||||
"@types/inquirer": "^9.0.3",
|
||||
"@types/jest": "^29.5.5",
|
||||
"@types/lodash": "^4.14.199",
|
||||
"@types/node-cleanup": "^2.1.2",
|
||||
"@types/numeral": "^2.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^8.18.0",
|
||||
"@typescript-eslint/parser": "^8.20.0",
|
||||
"axios": "^1.5.1",
|
||||
"axios-cookiejar-support": "^5.0.4",
|
||||
"chalk": "^4.1.2",
|
||||
"cheerio": "^1.0.0-rc.10",
|
||||
"@octokit/core": "^7.0.2",
|
||||
"@octokit/plugin-paginate-rest": "^13.1.1",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^16.0.0",
|
||||
"@swc/core": "^1.12.7",
|
||||
"@swc/jest": "^0.2.38",
|
||||
"@types/cli-progress": "^3.11.6",
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/inquirer": "^9.0.8",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/langs": "^2.0.5",
|
||||
"@types/lodash": "^4.17.19",
|
||||
"@types/node": "^24.0.7",
|
||||
"@types/node-cleanup": "^2.1.5",
|
||||
"@types/numeral": "^2.0.5",
|
||||
"@typescript-eslint/eslint-plugin": "^8.35.0",
|
||||
"@typescript-eslint/parser": "^8.35.0",
|
||||
"axios": "^1.10.0",
|
||||
"axios-cookiejar-support": "^6.0.2",
|
||||
"chalk": "^5.4.1",
|
||||
"cheerio": "^1.1.0",
|
||||
"cli-progress": "^3.12.0",
|
||||
"commander": "^8.2.0",
|
||||
"consola": "^3.2.3",
|
||||
"csv-parser": "^3.0.0",
|
||||
"commander": "^14.0.0",
|
||||
"consola": "^3.4.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"csv-parser": "^3.2.0",
|
||||
"cwait": "^1.1.2",
|
||||
"dayjs": "^1.11.10",
|
||||
"dayjs": "^1.11.13",
|
||||
"epg-grabber": "^0.38.0",
|
||||
"epg-parser": "^0.3.1",
|
||||
"eslint": "^9.17.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"form-data": "^4.0.0",
|
||||
"fs-extra": "^10.0.1",
|
||||
"glob": "^7.2.0",
|
||||
"globals": "^15.14.0",
|
||||
"eslint": "^9.30.0",
|
||||
"eslint-config-prettier": "^10.1.5",
|
||||
"form-data": "^4.0.3",
|
||||
"fs-extra": "^11.3.0",
|
||||
"glob": "^11.0.3",
|
||||
"globals": "^16.2.0",
|
||||
"husky": "^9.1.7",
|
||||
"iconv-lite": "^0.4.24",
|
||||
"inquirer": "^12.5.0",
|
||||
"jest": "^29.7.0",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"inquirer": "^12.6.3",
|
||||
"jest": "^30.0.3",
|
||||
"jest-offline": "^1.0.1",
|
||||
"langs": "^2.0.0",
|
||||
"libxml2-wasm": "^0.5.0",
|
||||
"lodash": "^4.17.21",
|
||||
"luxon": "^3.3.0",
|
||||
"luxon": "^3.6.1",
|
||||
"mockdate": "^3.0.5",
|
||||
"nedb-promises": "^6.0.3",
|
||||
"nedb-promises": "^6.2.3",
|
||||
"node-cleanup": "^2.1.2",
|
||||
"node-gzip": "^1.1.2",
|
||||
"numeral": "^2.0.6",
|
||||
"pako": "^2.1.0",
|
||||
"parse-duration": "^2.1.3",
|
||||
"parse-duration": "^2.1.4",
|
||||
"pdf-parse": "^1.1.1",
|
||||
"pm2": "^5.4.3",
|
||||
"pm2": "^6.0.8",
|
||||
"readline": "^1.3.0",
|
||||
"run-script-os": "^1.1.6",
|
||||
"serve": "^14.2.4",
|
||||
"signale": "^1.4.0",
|
||||
"skip-postinstall": "^1.0.0",
|
||||
"socks-proxy-agent": "^8.0.5",
|
||||
"srcset": "^4.0.0",
|
||||
"srcset": "^5.0.1",
|
||||
"table2array": "^0.0.2",
|
||||
"tabletojson": "^2.0.7",
|
||||
"tough-cookie": "^5.0.0",
|
||||
"transliteration": "^2.2.0",
|
||||
"tsx": "^4.19.2",
|
||||
"unzipit": "^1.4.0",
|
||||
"wildcard-match": "^5.1.2"
|
||||
}
|
||||
"tabletojson": "^4.1.6",
|
||||
"tough-cookie": "^5.1.2",
|
||||
"transliteration": "^2.3.5",
|
||||
"tsx": "^4.20.3",
|
||||
"typescript": "^5.8.3",
|
||||
"unzipit": "^1.4.3",
|
||||
"wildcard-match": "^5.1.4"
|
||||
},
|
||||
"packageManager": "yarn@4.9.2"
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ const xsd = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
</xs:attribute>
|
||||
</xs:schema>`
|
||||
|
||||
program.argument('[filepath]', 'Path to *.channels.xml files to check').parse(process.argv)
|
||||
program.argument('[filepath...]', 'Path to *.channels.xml files to check').parse(process.argv)
|
||||
|
||||
async function main() {
|
||||
const storage = new Storage()
|
||||
|
||||
@@ -48,7 +48,7 @@ async function main() {
|
||||
totalErrors++
|
||||
}
|
||||
|
||||
if (!langs.where('1', channel.lang)) {
|
||||
if (!langs.where('1', channel.lang ?? '')) {
|
||||
errors.push({ type: 'wrong_lang', ...channel })
|
||||
totalErrors++
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ program
|
||||
.default(false)
|
||||
.env('GZIP')
|
||||
)
|
||||
.parse(process.argv)
|
||||
.parse()
|
||||
|
||||
export type GrabOptions = {
|
||||
site?: string
|
||||
|
||||
@@ -35,7 +35,7 @@ export class Guide {
|
||||
const programs = this.programs
|
||||
|
||||
const currDate = new DateTime(process.env.CURR_DATE || new Date().toISOString(), {
|
||||
zone: 'UTC'
|
||||
timezone: 'UTC'
|
||||
})
|
||||
const xmltv = new XMLTV({
|
||||
channels,
|
||||
@@ -50,7 +50,7 @@ export class Guide {
|
||||
|
||||
if (this.gzip) {
|
||||
const zip = new Zip()
|
||||
const compressed = await zip.compress(xmltv.toString())
|
||||
const compressed = zip.compress(xmltv.toString())
|
||||
const gzFilepath = `${this.filepath}.gz`
|
||||
const gzFilename = path.basename(gzFilepath)
|
||||
this.logger.info(` saving to "${gzFilepath}"...`)
|
||||
|
||||
@@ -91,7 +91,7 @@ function parseDuration($item) {
|
||||
function parseImage($item) {
|
||||
const img = $item('.mainBroadcastCard-imageContent').first().find('img')
|
||||
const value = img.attr('srcset') || img.data('srcset')
|
||||
const obj = value ? srcset.parse(value).find(i => i.width === 128) : {}
|
||||
const obj = value ? srcset.parseSrcset(value).find(i => i.width === 128) : {}
|
||||
|
||||
if (obj.url) {
|
||||
obj.url = obj.url.replace('128x180', '960x540')
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import { execSync } from 'child_process'
|
||||
import fs from 'fs-extra'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import os from 'os'
|
||||
|
||||
let ENV_VAR = 'SITES_DIR=tests/__data__/input/api_generate/sites API_DIR=tests/__data__/output'
|
||||
if (os.platform() === 'win32') {
|
||||
ENV_VAR =
|
||||
'SET "SITES_DIR=tests/__data__/input/api_generate/sites" && SET "API_DIR=tests/__data__/output" &&'
|
||||
}
|
||||
const ENV_VAR = 'cross-env SITES_DIR=tests/__data__/input/api_generate/sites API_DIR=tests/__data__/output'
|
||||
|
||||
beforeEach(() => {
|
||||
fs.emptyDirSync('tests/__data__/output')
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
import { execSync } from 'child_process'
|
||||
import fs from 'fs-extra'
|
||||
import os from 'os'
|
||||
|
||||
type ExecError = {
|
||||
status: number
|
||||
stdout: string
|
||||
}
|
||||
|
||||
let ENV_VAR = 'DATA_DIR=tests/__data__/input/__data__'
|
||||
if (os.platform() === 'win32') {
|
||||
ENV_VAR = 'SET "DATA_DIR=tests/__data__/input/__data__" &&'
|
||||
}
|
||||
const ENV_VAR = 'cross-env DATA_DIR=tests/__data__/input/__data__'
|
||||
|
||||
beforeEach(() => {
|
||||
fs.emptyDirSync('tests/__data__/output')
|
||||
@@ -27,10 +18,12 @@ describe('channels:edit', () => {
|
||||
const stdout = execSync(cmd, { encoding: 'utf8' })
|
||||
if (process.env.DEBUG === 'true') console.log(cmd, stdout)
|
||||
checkStdout(stdout)
|
||||
} catch (error) {
|
||||
} catch (error: unknown) {
|
||||
// NOTE: for Windows only
|
||||
if (process.env.DEBUG === 'true') console.log(cmd, error)
|
||||
checkStdout((error as ExecError).stdout)
|
||||
if (error && typeof error === 'object' && 'stdout' in error) {
|
||||
checkStdout(error.stdout as string)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import { execSync } from 'child_process'
|
||||
import os from 'os'
|
||||
|
||||
type ExecError = {
|
||||
status: number
|
||||
stdout: string
|
||||
}
|
||||
|
||||
let ENV_VAR = 'DATA_DIR=tests/__data__/input/__data__'
|
||||
if (os.platform() === 'win32') {
|
||||
ENV_VAR = 'SET "DATA_DIR=tests/__data__/input/__data__" &&'
|
||||
}
|
||||
const ENV_VAR = 'cross-env DATA_DIR=tests/__data__/input/__data__'
|
||||
|
||||
describe('channels:validate', () => {
|
||||
it('will show a message if the file contains a duplicate', () => {
|
||||
|
||||
@@ -3,14 +3,8 @@ import { execSync } from 'child_process'
|
||||
import { Zip } from '@freearhey/core'
|
||||
import fs from 'fs-extra'
|
||||
import path from 'path'
|
||||
import os from 'os'
|
||||
|
||||
let ENV_VAR =
|
||||
'SITES_DIR=tests/__data__/input/epg_grab/sites CURR_DATE=2022-10-20 DATA_DIR=tests/__data__/input/__data__'
|
||||
if (os.platform() === 'win32') {
|
||||
ENV_VAR =
|
||||
'SET "SITES_DIR=tests/__data__/input/epg_grab/sites" && SET "CURR_DATE=2022-10-20" && SET "DATA_DIR=tests/__data__/input/__data__" &&'
|
||||
}
|
||||
const ENV_VAR = 'cross-env SITES_DIR=tests/__data__/input/epg_grab/sites CURR_DATE=2022-10-20 DATA_DIR=tests/__data__/input/__data__'
|
||||
|
||||
beforeEach(() => {
|
||||
fs.emptyDirSync('tests/__data__/output')
|
||||
@@ -51,15 +45,15 @@ describe('epg:grab', () => {
|
||||
)
|
||||
|
||||
const zip = new Zip()
|
||||
const expected = await zip.decompress(fs.readFileSync('tests/__data__/output/guide.xml.gz'))
|
||||
const result = await zip.decompress(
|
||||
const expected = zip.decompress(fs.readFileSync('tests/__data__/output/guide.xml.gz'))
|
||||
const result = zip.decompress(
|
||||
fs.readFileSync('tests/__data__/expected/epg_grab/guide.xml.gz')
|
||||
)
|
||||
expect(expected).toEqual(result)
|
||||
}, 30000)
|
||||
|
||||
it('can grab epg with wildcard as output', () => {
|
||||
const cmd = `${ENV_VAR} npm run grab --- --channels=tests/__data__/input/epg_grab/sites/example.com/example.com.channels.xml --output=tests/__data__/output/guides/{lang}/{site}.xml`
|
||||
const cmd = `${ENV_VAR} npm run grab --- --channels="tests/__data__/input/epg_grab/sites/example.com/example.com.channels.xml" --output="tests/__data__/output/guides/{lang}/{site}.xml"`
|
||||
const stdout = execSync(cmd, { encoding: 'utf8' })
|
||||
if (process.env.DEBUG === 'true') console.log(cmd, stdout)
|
||||
|
||||
@@ -104,10 +98,25 @@ describe('epg:grab', () => {
|
||||
|
||||
it('it will raise an error if the timeout is exceeded', () => {
|
||||
const cmd = `${ENV_VAR} npm run grab --- --channels=tests/__data__/input/epg_grab/custom.channels.xml --output=tests/__data__/output/guide.xml --timeout=0`
|
||||
const stdout = execSync(cmd, { encoding: 'utf8' })
|
||||
if (process.env.DEBUG === 'true') console.log(cmd, stdout)
|
||||
|
||||
expect(stdout).toContain('ERR: Connection timeout')
|
||||
let errorThrown = false
|
||||
try {
|
||||
execSync(cmd, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] })
|
||||
// If no error is thrown, explicitly fail the test
|
||||
fail('Expected command to throw an error due to timeout, but it did not.')
|
||||
} catch (error) {
|
||||
errorThrown = true
|
||||
if (process.env.DEBUG === 'true') {
|
||||
const stderr = error.stderr?.toString() || ''
|
||||
const stdout = error.stdout?.toString() || ''
|
||||
const combined = stderr + stdout
|
||||
console.log('stdout:', stdout)
|
||||
console.log('stderr:', stderr)
|
||||
console.log('combined:', combined)
|
||||
console.log('exit code:', error.exitCode)
|
||||
console.log('Error output:', combined)
|
||||
}
|
||||
}
|
||||
expect(errorThrown).toBe(true)
|
||||
})
|
||||
|
||||
it('can grab epg via https proxy', () => {
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
import { execSync } from 'child_process'
|
||||
import fs from 'fs-extra'
|
||||
import os from 'os'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
|
||||
let ENV_VAR = 'SITES_DIR=tests/__data__/output/sites'
|
||||
if (os.platform() === 'win32') {
|
||||
ENV_VAR = 'SET "SITES_DIR=tests/__data__/output/sites" &&'
|
||||
}
|
||||
const ENV_VAR = 'cross-env SITES_DIR=tests/__data__/output/sites'
|
||||
|
||||
beforeEach(() => {
|
||||
fs.emptyDirSync('tests/__data__/output')
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import { execSync } from 'child_process'
|
||||
import fs from 'fs-extra'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import os from 'os'
|
||||
|
||||
let ENV_VAR = 'SITES_DIR=tests/__data__/input/sites_update/sites ROOT_DIR=tests/__data__/output'
|
||||
if (os.platform() === 'win32') {
|
||||
ENV_VAR =
|
||||
'SET "SITES_DIR=tests/__data__/input/sites_update/sites" && SET "ROOT_DIR=tests/__data__/output" &&'
|
||||
}
|
||||
const ENV_VAR = 'cross-env SITES_DIR=tests/__data__/input/sites_update/sites ROOT_DIR=tests/__data__/output'
|
||||
|
||||
beforeEach(() => {
|
||||
fs.emptyDirSync('tests/__data__/output')
|
||||
|
||||
@@ -3,13 +3,16 @@
|
||||
"strict": true,
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"target": "es2020",
|
||||
"target": "es2022",
|
||||
"esModuleInterop": true,
|
||||
"declaration": true,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"./src/types"
|
||||
"./scripts/types"
|
||||
],
|
||||
"allowJs": true
|
||||
}
|
||||
"allowJs": true,
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": ["scripts/**/*", "src/**/*"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user