From 158e23e14bcb7bedbdf3fc346b86fbef212ae81b Mon Sep 17 00:00:00 2001 From: freearhey <7253922+freearhey@users.noreply.github.com> Date: Fri, 19 Jun 2026 19:14:08 +0300 Subject: [PATCH] Update scripts --- scripts/commands/playlist/format.ts | 25 +++++++++------ scripts/commands/playlist/update.ts | 26 ++++++++++----- scripts/models/stream.ts | 4 +++ scripts/utils.ts | 49 ++++++++++++++++++++++++----- 4 files changed, 79 insertions(+), 25 deletions(-) diff --git a/scripts/commands/playlist/format.ts b/scripts/commands/playlist/format.ts index a7c69ee013..a19d695d6d 100644 --- a/scripts/commands/playlist/format.ts +++ b/scripts/commands/playlist/format.ts @@ -1,10 +1,10 @@ +import { getStreamInfo, loadStreamData } from '../../utils' import { Collection, Logger } from '@freearhey/core' import { OptionValues, program } from 'commander' import { Stream, Playlist } from '../../models' import { Storage } from '@freearhey/storage-js' import { STREAMS_DIR } from '../../constants' import { PlaylistParser } from '../../core' -import { getStreamInfo } from '../../utils' import { loadData, data } from '../../api' import cliProgress from 'cli-progress' import { eachLimit } from 'async' @@ -83,7 +83,7 @@ async function main() { return stream }) - logger.info('adding the missing quality...') + logger.info('adding the missing quality and label...') const progressBar = new cliProgress.SingleBar({ clearOnComplete: true, format: '[{bar}] {percentage}% | {value}/{total}' @@ -91,21 +91,28 @@ async function main() { progressBar.start(streams.count(), 0) await eachLimit(streams.all(), options.parallel, async (stream: Stream) => { progressBar.increment() - if (stream.quality) return + if (stream.quality && stream.label) return - const streamInfo = await getStreamInfo(stream.url, { + const { data: streamData, error } = await loadStreamData(stream.url, { httpUserAgent: stream.user_agent, httpReferrer: stream.referrer, timeout: options.timeout, proxy: options.proxy }) - if (streamInfo) { - const height = streamInfo?.resolution?.height + if (error?.code === 403 && !stream.label) { + stream.label = 'Geo-blocked' + } - if (height) { - stream.quality = `${height}p` - } + if (stream.quality) return + if (!streamData) return + + const streamInfo = getStreamInfo(stream.url, streamData) + if (!streamInfo) return + + const height = streamInfo?.resolution?.height + if (height) { + stream.quality = `${height}p` } }) progressBar.stop() diff --git a/scripts/commands/playlist/update.ts b/scripts/commands/playlist/update.ts index f0477ed84e..7ded5aaf34 100644 --- a/scripts/commands/playlist/update.ts +++ b/scripts/commands/playlist/update.ts @@ -1,4 +1,4 @@ -import { getStreamInfo, loadIssues, createThread } from '../../utils' +import { getStreamInfo, loadIssues, createThread, loadStreamData } from '../../utils' import { STREAMS_DIR, LOGS_DIR } from '../../constants' import { Playlist, Issue, Stream } from '../../models' import { loadData, data as apiData } from '../../api' @@ -241,14 +241,24 @@ async function addStream(issue: Issue) { const httpReferrer = data.getString('http_referrer') || null let quality = data.getString('quality') || null - if (!quality) { - const streamInfo = await getStreamInfo(streamUrl, { httpUserAgent, httpReferrer }) + let label = data.getString('label') || null + if (!quality || !label) { + const { data: streamData, error } = await loadStreamData(streamUrl, { + httpUserAgent, + httpReferrer + }) - if (streamInfo) { - const height = streamInfo?.resolution?.height + if (error?.code === 403 && !label) { + label = `Geo-blocked` + } - if (height) { - quality = `${height}p` + if (streamData && !quality) { + const streamInfo = getStreamInfo(streamUrl, streamData) + if (streamInfo) { + const height = streamInfo?.resolution?.height + if (height) { + quality = `${height}p` + } } } } @@ -261,7 +271,7 @@ async function addStream(issue: Issue) { user_agent: httpUserAgent, referrer: httpReferrer, quality, - label: data.getString('label') || '' + label }) stream.updateTitle().updateFilepath() diff --git a/scripts/models/stream.ts b/scripts/models/stream.ts index 173f4554f5..469fb33844 100644 --- a/scripts/models/stream.ts +++ b/scripts/models/stream.ts @@ -107,6 +107,10 @@ export class Stream extends sdk.Models.Stream { return !channel.is_nsfw } + isGeoBlocked(): boolean { + return this.label === 'Geo-blocked' + } + getUniqKey(): string { const filepath = this.getFilepath() const tvgId = this.getTvgId() diff --git a/scripts/utils.ts b/scripts/utils.ts index 8909e9dba9..ac151dd14d 100644 --- a/scripts/utils.ts +++ b/scripts/utils.ts @@ -44,7 +44,18 @@ type StreamInfo = { codecs: string } -export async function getStreamInfo( +function getStreamType(url: string): string | undefined { + if (url.includes('.m3u8')) return 'HLS' + if (url.includes('.mpd')) return 'DASH' + return undefined +} + +type HTTPError = { + code: number + message: string +} + +export async function loadStreamData( url: string, options: { httpUserAgent?: string | null @@ -52,10 +63,21 @@ export async function getStreamInfo( timeout?: number proxy?: string } -): Promise { +): Promise<{ data: string | undefined; error: HTTPError | undefined }> { let data: string | undefined + let error: HTTPError | undefined if (TESTING) { - if (url.includes('.m3u8')) { + if ( + [ + 'srt://stream.alabbassia.com:8890?mode=caller&latency=200&streamid=read:live/alabbassia', + 'https://60efd7a2b4d02.streamlock.net/a_steiermark/ngrp:livestream_all/playlist.m3u8' + ].includes(url) + ) { + error = { + code: 403, + message: 'Forbidden' + } + } else if (url.includes('.m3u8')) { data = fs.readFileSync( path.resolve(__dirname, '../tests/__data__/input/playlist_update/playlist.m3u8'), 'utf8' @@ -96,16 +118,27 @@ export async function getStreamInfo( const response = await axios(url, request) data = response.data - } catch { - // do nothing + } catch (err) { + if (err.response) { + error = { + code: err.status, + message: err.message + } + } } } - if (!data) return undefined + return { data, error } +} + +export function getStreamInfo(url: string, data: string): StreamInfo | undefined { + if (!url || !data) return undefined + + const type = getStreamType(url) let info: StreamInfo | undefined - if (url.includes('.m3u8')) { + if (type === 'HLS') { setOptions({ silent: true }) try { @@ -126,7 +159,7 @@ export async function getStreamInfo( } catch { // do nothing } - } else if (url.includes('.mpd')) { + } else if (type === 'DASH') { const manifest = parseManifest(data, { manifestUri: url, eventHandler: ({ type, message }) => console.log(`${type}: ${message}`)