update tests & replace ffprobe -> mediainfo.js

This commit is contained in:
Ismaël Moret
2025-09-02 17:00:50 +00:00
parent 825d198ac8
commit 1ca59cba57
6 changed files with 207 additions and 476 deletions

View File

@@ -5,21 +5,16 @@ import { Stream } from '../../models'
import { program } from 'commander'
import { eachLimit } from 'async-es'
import chalk from 'chalk'
import child_process from 'node:child_process'
import os from 'node:os'
import dns from 'node:dns'
import type { DataLoaderData } from '../../types/dataLoader'
import type { DataProcessorData } from '../../types/dataProcessor'
const cpus = os.cpus()
const LIVE_UPDATE_INTERVAL = 5000
const LIVE_UPDATE_MAX_STREAMS = 100
let errors = 0
let warnings = 0
const results = {}
let interval
const results: { [key: string]: string } = {}
let interval: string | number | NodeJS.Timeout | undefined
let streams = new Collection()
let isLiveUpdateEnabled = true
@@ -28,8 +23,8 @@ program
.option(
'-p, --parallel <number>',
'Batch size of streams to test concurrently',
cpus.length,
(value: string) => parseInt(value)
(value: string) => parseInt(value),
10
)
.option('-x, --proxy <url>', 'Use the specified proxy')
.parse(process.argv)
@@ -40,24 +35,6 @@ const logger = new Logger()
const tester = new StreamTester()
async function main() {
if (await isOffline()) {
logger.error(chalk.red('Internet connection is required for the script to work'))
return
}
try {
child_process.execSync('ffprobe -version', { stdio: 'ignore' })
} catch {
logger.error(
chalk.red(
'For the script to work, the "ffprobe" library must be installed (https://ffmpeg.org/download.html)'
)
)
return
}
logger.info('loading data from api...')
const processor = new DataProcessor()
const dataStorage = new Storage(DATA_DIR)
@@ -158,7 +135,7 @@ function drawTable() {
}
}
function onFinish(error) {
function onFinish(error: any) {
clearInterval(interval)
if (error) {
@@ -181,11 +158,3 @@ function onFinish(error) {
process.exit(0)
}
async function isOffline() {
return new Promise((resolve, reject) => {
dns.lookup('info.cern.ch', err => {
if (err) resolve(true)
reject(false)
})
}).catch(() => {})
}

View File

@@ -1,27 +1,83 @@
import { Stream } from '../models'
import { IPTVChecker } from 'iptv-checker'
import { TESTING } from '../constants'
import MediainfoFactory from 'mediainfo.js'
export class StreamTester {
checker: IPTVChecker
constructor() {
this.checker = new IPTVChecker()
}
constructor() {}
async test(stream: Stream) {
if (TESTING) {
const results = (await import('../../tests/__data__/input/playlist_test/results.js')).default
return results[stream.url]
return results[stream.url as keyof typeof results]
} else {
return this.checker.checkStream({
url: stream.url,
http: {
referrer: stream.getReferrer(),
'user-agent': stream.getUserAgent()
try {
const controller = new AbortController()
const timeout = 10000
const timeoutId = setTimeout(() => controller.abort(), timeout)
const res = await fetch(stream.url, {
signal: controller.signal,
headers: {
'User-Agent': stream.getUserAgent() || 'Mozilla/5.0',
Referer: stream.getReferrer()
}
})
clearTimeout(timeoutId)
if (!res.ok) {
return {
status: {
ok: false,
code: `HTTP_${res.status}`
}
}
}
})
const mediainfo = await MediainfoFactory({ format: 'object' })
const buffer = await res.arrayBuffer()
const result = await mediainfo.analyzeData(
() => buffer.byteLength,
(size: any, offset: number | undefined) =>
Buffer.from(buffer).subarray(offset, offset + size)
)
if (result && result.media && result.media.track.length > 0) {
return {
status: {
ok: true,
code: 'OK'
}
}
} else {
return {
status: {
ok: false,
code: 'NO_VIDEO'
}
}
}
} catch (error: any) {
let code = 'UNKNOWN_ERROR'
if (error.name === 'AbortError') {
code = 'TIMEOUT'
} else if (error.cause) {
const cause = error.cause as Error & { code?: string }
if (cause.code) {
code = cause.code
} else {
code = cause.name
}
}
return {
status: {
ok: false,
code
}
}
}
}
}
}