removed unnecessary packages for better integrity

This commit is contained in:
stratumadev 2025-11-26 04:17:21 +01:00
parent 96cec167e1
commit 9cc0ddc193
13 changed files with 148 additions and 285 deletions

View file

@ -1,49 +0,0 @@
declare module 'm3u8-parsed' {
export type M3U8 = {
allowCache: boolean;
discontinuityStarts: [];
segments: {
duration: number;
byterange?: {
length: number;
offset: number;
};
uri: string;
key: {
method: string;
uri: string;
};
timeline: number;
}[];
version: number;
mediaGroups: {
[type: string]: {
[index: string]: {
[language: string]: {
default: boolean;
autoselect: boolean;
language: string;
uri: string;
};
};
};
};
playlists: {
uri: string;
timeline: number;
attributes: {
'CLOSED-CAPTIONS': string;
AUDIO: string;
'FRAME-RATE': number;
RESOLUTION: {
width: number;
height: number;
};
CODECS: string;
'AVERAGE-BANDWIDTH': string;
BANDWIDTH: number;
};
}[];
};
export default function (data: string): M3U8;
}

46
adn.ts
View file

@ -3,11 +3,11 @@ import packageJson from './package.json';
// Node // Node
import path from 'path'; import path from 'path';
import fs from 'fs-extra'; import fs from 'fs';
import crypto from 'crypto'; import crypto from 'crypto';
// Plugins // Plugins
import m3u8 from 'm3u8-parsed'; import m3u8 from 'm3u8-parser';
// Modules // Modules
import * as fontsData from './modules/module.fontsData'; import * as fontsData from './modules/module.fontsData';
@ -600,22 +600,32 @@ export default class AnimationDigitalNetwork implements ServiceClass {
dlFailed = true; dlFailed = true;
} else { } else {
const streamPlaylistBody = await streamPlaylistsReq.res.text(); const streamPlaylistBody = await streamPlaylistsReq.res.text();
const streamPlaylists = m3u8(streamPlaylistBody);
// Init parser
const parser = new m3u8.Parser();
// Parse M3U8
parser.push(streamPlaylistBody);
parser.end();
const streamPlaylists = parser.manifest;
if (!streamPlaylists) throw Error('Failed to parse M3U8');
const plServerList: string[] = [], const plServerList: string[] = [],
plStreams: Record<string, Record<string, string>> = {}, plStreams: Record<string, Record<string, string>> = {},
plQuality: { plQuality: {
str: string; str: string;
dim: string; dim: string;
CODECS: string; CODECS?: string;
RESOLUTION: { RESOLUTION?: {
width: number; width?: number;
height: number; height?: number;
}; };
}[] = []; }[] = [];
for (const pl of streamPlaylists.playlists) { for (const pl of streamPlaylists.playlists ?? []) {
// set quality // set quality
const plResolution = pl.attributes.RESOLUTION; const plResolution = pl.attributes.RESOLUTION;
const plResolutionText = `${plResolution.width}x${plResolution.height}`; const plResolutionText = `${plResolution?.width}x${plResolution?.height}`;
// set codecs // set codecs
const plCodecs = pl.attributes.CODECS; const plCodecs = pl.attributes.CODECS;
// parse uri // parse uri
@ -642,7 +652,7 @@ export default class AnimationDigitalNetwork implements ServiceClass {
plStreams[plServer][plResolutionText] = pl.uri; plStreams[plServer][plResolutionText] = pl.uri;
} }
// set plQualityStr // set plQualityStr
const plBandwidth = Math.round(pl.attributes.BANDWIDTH / 1024); const plBandwidth = Math.round(pl.attributes?.BANDWIDTH ?? 0 / 1024);
const qualityStrAdd = `${plResolutionText} (${plBandwidth}KiB/s)`; const qualityStrAdd = `${plResolutionText} (${plBandwidth}KiB/s)`;
const qualityStrRegx = new RegExp(qualityStrAdd.replace(/([:()/])/g, '\\$1'), 'm'); const qualityStrRegx = new RegExp(qualityStrAdd.replace(/([:()/])/g, '\\$1'), 'm');
const qualityStrMatch = !plQuality const qualityStrMatch = !plQuality
@ -691,12 +701,12 @@ export default class AnimationDigitalNetwork implements ServiceClass {
{ {
name: 'height', name: 'height',
type: 'number', type: 'number',
replaceWith: quality === 0 ? (plQuality[plQuality.length - 1].RESOLUTION.height as number) : plQuality[quality - 1].RESOLUTION.height replaceWith: quality === 0 ? (plQuality[plQuality.length - 1].RESOLUTION?.height as number) : (plQuality[quality - 1].RESOLUTION?.height as number)
}, },
{ {
name: 'width', name: 'width',
type: 'number', type: 'number',
replaceWith: quality === 0 ? (plQuality[plQuality.length - 1].RESOLUTION.width as number) : plQuality[quality - 1].RESOLUTION.width replaceWith: quality === 0 ? (plQuality[plQuality.length - 1].RESOLUTION?.width as number) : (plQuality[quality - 1].RESOLUTION?.width as number)
} }
); );
@ -712,7 +722,17 @@ export default class AnimationDigitalNetwork implements ServiceClass {
dlFailed = true; dlFailed = true;
} else { } else {
const chunkPageBody = await chunkPage.res.text(); const chunkPageBody = await chunkPage.res.text();
const chunkPlaylist = m3u8(chunkPageBody);
// Init parser
const parser = new m3u8.Parser();
// Parse M3U8
parser.push(chunkPageBody);
parser.end();
const chunkPlaylist = parser.manifest;
if (!chunkPlaylist) throw Error('Failed to parse M3U8');
const totalParts = chunkPlaylist.segments.length; const totalParts = chunkPlaylist.segments.length;
const mathParts = Math.ceil(totalParts / options.partsize); const mathParts = Math.ceil(totalParts / options.partsize);
const mathMsg = `(${mathParts}*${options.partsize})`; const mathMsg = `(${mathParts}*${options.partsize})`;

View file

@ -1,15 +1,15 @@
// build-in // build-in
import path from 'path'; import path from 'path';
import fs from 'fs-extra'; import fs from 'fs';
// package program // package program
import packageJson from './package.json'; import packageJson from './package.json';
// plugins // plugins
import { console } from './modules/log'; import { console } from './modules/log';
import m3u8 from 'm3u8-parsed';
import streamdl, { M3U8Json } from './modules/hls-download'; import streamdl, { M3U8Json } from './modules/hls-download';
import Helper from './modules/module.helper'; import Helper from './modules/module.helper';
import m3u8 from 'm3u8-parser';
// custom modules // custom modules
import * as fontsData from './modules/module.fontsData'; import * as fontsData from './modules/module.fontsData';
@ -347,10 +347,10 @@ export default class Crunchy implements ServiceClass {
} else { } else {
const fontFolder = path.dirname(fontLoc); const fontFolder = path.dirname(fontLoc);
if (fs.existsSync(fontLoc) && fs.statSync(fontLoc).size == 0) { if (fs.existsSync(fontLoc) && fs.statSync(fontLoc).size == 0) {
fs.unlinkSync(fontLoc); fs.rmSync(fontLoc, { recursive: true, force: true });
} }
try { try {
fs.ensureDirSync(fontFolder); fs.existsSync(fontFolder);
} catch (e) { } catch (e) {
console.info(''); console.info('');
} }
@ -2400,7 +2400,7 @@ export default class Crunchy implements ServiceClass {
} else { } else {
console.info('Decryption done for video'); console.info('Decryption done for video');
if (!options.nocleanup) { if (!options.nocleanup) {
fs.removeSync(`${tempTsFile}.video.enc.m4s`); fs.unlinkSync(`${tempTsFile}.video.enc.m4s`);
} }
fs.copyFileSync(`${tempTsFile}.video.m4s`, `${tsFile}.video.m4s`); fs.copyFileSync(`${tempTsFile}.video.m4s`, `${tsFile}.video.m4s`);
fs.unlinkSync(`${tempTsFile}.video.m4s`); fs.unlinkSync(`${tempTsFile}.video.m4s`);
@ -2430,7 +2430,7 @@ export default class Crunchy implements ServiceClass {
return undefined; return undefined;
} else { } else {
if (!options.nocleanup) { if (!options.nocleanup) {
fs.removeSync(`${tempTsFile}.audio.enc.m4s`); fs.unlinkSync(`${tempTsFile}.audio.enc.m4s`);
} }
fs.copyFileSync(`${tempTsFile}.audio.m4s`, `${tsFile}.audio.m4s`); fs.copyFileSync(`${tempTsFile}.audio.m4s`, `${tsFile}.audio.m4s`);
fs.unlinkSync(`${tempTsFile}.audio.m4s`); fs.unlinkSync(`${tempTsFile}.audio.m4s`);
@ -2467,22 +2467,31 @@ export default class Crunchy implements ServiceClass {
} }
} }
} else if (!options.novids) { } else if (!options.novids) {
const streamPlaylists = m3u8(vstreamPlaylistBody); // Init parser
const parser = new m3u8.Parser();
// Parse M3U8
parser.push(vstreamPlaylistBody);
parser.end();
const streamPlaylists = parser.manifest;
if (!streamPlaylists) throw Error('Failed to parse M3U8');
const plServerList: string[] = [], const plServerList: string[] = [],
plStreams: Record<string, Record<string, string>> = {}, plStreams: Record<string, Record<string, string>> = {},
plQuality: { plQuality: {
str: string; str: string;
dim: string; dim: string;
CODECS: string; CODECS?: string;
RESOLUTION: { RESOLUTION?: {
width: number; width?: number;
height: number; height?: number;
}; };
}[] = []; }[] = [];
for (const pl of streamPlaylists.playlists) { for (const pl of streamPlaylists.playlists ?? []) {
// set quality // set quality
const plResolution = pl.attributes.RESOLUTION; const plResolution = pl.attributes.RESOLUTION;
const plResolutionText = `${plResolution.width}x${plResolution.height}`; const plResolutionText = `${plResolution?.width}x${plResolution?.height}`;
// set codecs // set codecs
const plCodecs = pl.attributes.CODECS; const plCodecs = pl.attributes.CODECS;
// parse uri // parse uri
@ -2509,7 +2518,7 @@ export default class Crunchy implements ServiceClass {
plStreams[plServer][plResolutionText] = pl.uri; plStreams[plServer][plResolutionText] = pl.uri;
} }
// set plQualityStr // set plQualityStr
const plBandwidth = Math.round(pl.attributes.BANDWIDTH / 1024); const plBandwidth = Math.round((pl.attributes?.BANDWIDTH ?? 0) / 1024);
const qualityStrAdd = `${plResolutionText} (${plBandwidth}KiB/s)`; const qualityStrAdd = `${plResolutionText} (${plBandwidth}KiB/s)`;
const qualityStrRegx = new RegExp(qualityStrAdd.replace(/([:()/])/g, '\\$1'), 'm'); const qualityStrRegx = new RegExp(qualityStrAdd.replace(/([:()/])/g, '\\$1'), 'm');
const qualityStrMatch = !plQuality const qualityStrMatch = !plQuality
@ -2556,12 +2565,14 @@ export default class Crunchy implements ServiceClass {
{ {
name: 'height', name: 'height',
type: 'number', type: 'number',
replaceWith: quality === 0 ? (plQuality[plQuality.length - 1].RESOLUTION.height as number) : plQuality[quality - 1].RESOLUTION.height replaceWith:
quality === 0 ? (plQuality[plQuality.length - 1].RESOLUTION?.height as number) : (plQuality[quality - 1].RESOLUTION?.height as number)
}, },
{ {
name: 'width', name: 'width',
type: 'number', type: 'number',
replaceWith: quality === 0 ? (plQuality[plQuality.length - 1].RESOLUTION.width as number) : plQuality[quality - 1].RESOLUTION.width replaceWith:
quality === 0 ? (plQuality[plQuality.length - 1].RESOLUTION?.width as number) : (plQuality[quality - 1].RESOLUTION?.width as number)
} }
); );
const lang = langsData.languages.find((a) => a.code === vcurStream?.audio_lang); const lang = langsData.languages.find((a) => a.code === vcurStream?.audio_lang);
@ -2598,7 +2609,16 @@ export default class Crunchy implements ServiceClass {
} }
const chunkPageBody = await chunkPage.res.text(); const chunkPageBody = await chunkPage.res.text();
const chunkPlaylist = m3u8(chunkPageBody); // Init parser
const parser = new m3u8.Parser();
// Parse M3U8
parser.push(chunkPageBody);
parser.end();
const chunkPlaylist = parser.manifest;
if (!chunkPlaylist) throw Error('Failed to parse M3U8');
const totalParts = chunkPlaylist.segments.length; const totalParts = chunkPlaylist.segments.length;
const mathParts = Math.ceil(totalParts / options.partsize); const mathParts = Math.ceil(totalParts / options.partsize);
const mathMsg = `(${mathParts}*${options.partsize})`; const mathMsg = `(${mathParts}*${options.partsize})`;

View file

@ -1,8 +1,7 @@
import express from 'express'; import express from 'express';
import { ensureConfig, loadCfg, workingDir } from '../../modules/module.cfg-loader'; import { ensureConfig, loadCfg, workingDir } from '../../modules/module.cfg-loader';
import cors from 'cors';
import ServiceHandler from './serviceHandler';
import open from 'open'; import open from 'open';
import ServiceHandler from './serviceHandler';
import path from 'path'; import path from 'path';
import { PublicWebSocket } from './websocket'; import { PublicWebSocket } from './websocket';
import { console } from '../../modules/log'; import { console } from '../../modules/log';
@ -19,7 +18,14 @@ const app = express();
export { app, cfg }; export { app, cfg };
app.use(express.json()); app.use(express.json());
app.use(cors());
app.use((_, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});
app.use(express.static(path.join(workingDir, 'gui', 'server', 'build'), { maxAge: 1000 * 60 * 20 })); app.use(express.static(path.join(workingDir, 'gui', 'server', 'build'), { maxAge: 1000 * 60 * 20 }));
console.info(`\n=== Multi Downloader NX GUI ${packageJson.version} ===\n`); console.info(`\n=== Multi Downloader NX GUI ${packageJson.version} ===\n`);

View file

@ -1,6 +1,6 @@
// build-in // build-in
import path from 'path'; import path from 'path';
import fs from 'fs-extra'; import fs from 'fs';
// package program // package program
import packageJson from './package.json'; import packageJson from './package.json';
@ -934,7 +934,7 @@ export default class Hidive implements ServiceClass {
} else { } else {
console.info('Decryption done for video'); console.info('Decryption done for video');
if (!options.nocleanup) { if (!options.nocleanup) {
fs.removeSync(`${tempTsFile}.video.enc.m4s`); fs.unlinkSync(`${tempTsFile}.video.enc.m4s`);
} }
fs.copyFileSync(`${tempTsFile}.video.m4s`, `${tsFile}.video.m4s`); fs.copyFileSync(`${tempTsFile}.video.m4s`, `${tsFile}.video.m4s`);
fs.unlinkSync(`${tempTsFile}.video.m4s`); fs.unlinkSync(`${tempTsFile}.video.m4s`);
@ -1028,7 +1028,7 @@ export default class Hidive implements ServiceClass {
return undefined; return undefined;
} else { } else {
if (!options.nocleanup) { if (!options.nocleanup) {
fs.removeSync(`${tempTsFile}.audio.enc.m4s`); fs.unlinkSync(`${tempTsFile}.audio.enc.m4s`);
} }
fs.copyFileSync(`${tempTsFile}.audio.m4s`, `${tsFile}.audio.m4s`); fs.copyFileSync(`${tempTsFile}.audio.m4s`, `${tsFile}.audio.m4s`);
fs.unlinkSync(`${tempTsFile}.audio.m4s`); fs.unlinkSync(`${tempTsFile}.audio.m4s`);

View file

@ -1,6 +1,6 @@
// build requirements // build requirements
import crypto from 'crypto'; import crypto from 'crypto';
import fs from 'fs-extra'; import fs from 'fs';
import pkg from '../package.json'; import pkg from '../package.json';
import modulesCleanup from 'removeNPMAbsolutePaths'; import modulesCleanup from 'removeNPMAbsolutePaths';
import { exec } from '@yao-pkg/pkg'; import { exec } from '@yao-pkg/pkg';
@ -38,14 +38,14 @@ async function buildBinary(buildType: BuildTypes, gui: boolean) {
} }
await modulesCleanup('.'); await modulesCleanup('.');
if (!fs.existsSync(buildsDir)) { if (!fs.existsSync(buildsDir)) {
fs.mkdirSync(buildsDir); fs.mkdirSync(buildsDir, { recursive: true });
} }
const buildFull = `${buildStr}-${getFriendlyName(buildType)}-${gui ? 'gui' : 'cli'}`; const buildFull = `${buildStr}-${getFriendlyName(buildType)}-${gui ? 'gui' : 'cli'}`;
const buildDir = `${buildsDir}/${buildFull}`; const buildDir = `${buildsDir}/${buildFull}`;
if (fs.existsSync(buildDir)) { if (fs.existsSync(buildDir)) {
fs.removeSync(buildDir); fs.rmSync(buildDir, { recursive: true, force: true });
} }
fs.mkdirSync(buildDir); fs.mkdirSync(buildDir, { recursive: true });
console.info('Running esbuild'); console.info('Running esbuild');
const build = await esbuild.build({ const build = await esbuild.build({
@ -79,24 +79,24 @@ async function buildBinary(buildType: BuildTypes, gui: boolean) {
} }
// Moving required default files/folders into build dir // Moving required default files/folders into build dir
fs.mkdirSync(`${buildDir}/config`); fs.mkdirSync(`${buildDir}/config`, { recursive: true });
fs.mkdirSync(`${buildDir}/videos`); fs.mkdirSync(`${buildDir}/videos`, { recursive: true });
fs.mkdirSync(`${buildDir}/widevine`); fs.mkdirSync(`${buildDir}/widevine`, { recursive: true });
fs.mkdirSync(`${buildDir}/playready`); fs.mkdirSync(`${buildDir}/playready`, { recursive: true });
fs.copySync('./config/cli-defaults.yml', `${buildDir}/config/cli-defaults.yml`); fs.copyFileSync('./config/cli-defaults.yml', `${buildDir}/config/cli-defaults.yml`);
fs.copySync('./config/dir-path.yml', `${buildDir}/config/dir-path.yml`); fs.copyFileSync('./config/dir-path.yml', `${buildDir}/config/dir-path.yml`);
fs.copySync('./config/gui.yml', `${buildDir}/config/gui.yml`); fs.copyFileSync('./config/gui.yml', `${buildDir}/config/gui.yml`);
fs.copySync('./modules/cmd-here.bat', `${buildDir}/cmd-here.bat`); fs.copyFileSync('./modules/cmd-here.bat', `${buildDir}/cmd-here.bat`);
fs.copySync('./modules/NotoSans-Regular.ttf', `${buildDir}/NotoSans-Regular.ttf`); fs.copyFileSync('./modules/NotoSans-Regular.ttf', `${buildDir}/NotoSans-Regular.ttf`);
fs.copySync('./package.json', `${buildDir}/package.json`); fs.copyFileSync('./package.json', `${buildDir}/package.json`);
fs.copySync('./docs/', `${buildDir}/docs/`); fs.cpSync('./docs/', `${buildDir}/docs/`, { recursive: true });
fs.copySync('./LICENSE.md', `${buildDir}/docs/LICENSE.md`); fs.copyFileSync('./LICENSE.md', `${buildDir}/docs/LICENSE.md`);
if (gui) { if (gui) {
fs.copySync('./gui', `${buildDir}/gui`); fs.cpSync('./gui', `${buildDir}/gui`, { recursive: true });
fs.copySync('./node_modules/open/xdg-open', `${buildDir}/xdg-open`); fs.cpSync('./node_modules/open/xdg-open', `${buildDir}/xdg-open`, { recursive: true });
} }
if (fs.existsSync(`${buildsDir}/${buildFull}.7z`)) { if (fs.existsSync(`${buildsDir}/${buildFull}.7z`)) {
fs.removeSync(`${buildsDir}/${buildFull}.7z`); fs.unlinkSync(`${buildsDir}/${buildFull}.7z`);
} }
// Generate bin-path.yml // Generate bin-path.yml

View file

@ -8,6 +8,7 @@ import { console } from './log';
import { ProgressData } from '../@types/messageHandler'; import { ProgressData } from '../@types/messageHandler';
import Helper from './module.helper'; import Helper from './module.helper';
import * as reqModule from './module.fetch'; import * as reqModule from './module.fetch';
import { Manifest } from 'm3u8-parser';
const req = new reqModule.Req(); const req = new reqModule.Req();
@ -33,7 +34,7 @@ type Key = {
}; };
export type HLSOptions = { export type HLSOptions = {
m3u8json: M3U8Json; m3u8json: M3U8Json | Partial<Manifest>;
output?: string; output?: string;
threads?: number; threads?: number;
retries?: number; retries?: number;
@ -52,7 +53,7 @@ type Data = {
total: number; total: number;
completed: number; completed: number;
}; };
m3u8json: M3U8Json; m3u8json: M3U8Json | Partial<Manifest>;
outputFile: string; outputFile: string;
threads: number; threads: number;
retries: number; retries: number;
@ -118,7 +119,7 @@ class hlsDownload {
if (age < 24 * 60 * 60 * 1000) { if (age < 24 * 60 * 60 * 1000) {
console.info('Resume data found! Trying to resume...'); console.info('Resume data found! Trying to resume...');
const resumeData = JSON.parse(await fs.readFile(`${fn}.resume`, 'utf-8')); const resumeData = JSON.parse(await fs.readFile(`${fn}.resume`, 'utf-8'));
if (resumeData.total == this.data.m3u8json.segments.length && resumeData.completed != resumeData.total && !isNaN(resumeData.completed)) { if (resumeData.total == this.data.m3u8json.segments?.length && resumeData.completed != resumeData.total && !isNaN(resumeData.completed)) {
console.info('Resume data is ok!'); console.info('Resume data is ok!');
this.data.offset = resumeData.completed; this.data.offset = resumeData.completed;
this.data.isResume = true; this.data.isResume = true;
@ -126,7 +127,7 @@ class hlsDownload {
console.warn(' Resume data is wrong!'); console.warn(' Resume data is wrong!');
console.warn({ console.warn({
resume: { total: resumeData.total, dled: resumeData.completed }, resume: { total: resumeData.total, dled: resumeData.completed },
current: { total: this.data.m3u8json.segments.length } current: { total: this.data.m3u8json.segments?.length }
}); });
} }
} else { } else {
@ -166,7 +167,7 @@ class hlsDownload {
this.data.dateStart = Date.now(); this.data.dateStart = Date.now();
let segments = this.data.m3u8json.segments; let segments = this.data.m3u8json.segments;
// download init part // download init part
if (segments[0].map && this.data.offset === 0 && !this.data.skipInit) { if (segments?.[0].map && this.data.offset === 0 && !this.data.skipInit) {
console.info('Download and save init part...'); console.info('Download and save init part...');
const initSeg = segments[0].map as Segment; const initSeg = segments[0].map as Segment;
if (segments[0].key) { if (segments[0].key) {
@ -179,7 +180,7 @@ class hlsDownload {
`${fn}.resume`, `${fn}.resume`,
JSON.stringify({ JSON.stringify({
completed: 0, completed: 0,
total: this.data.m3u8json.segments.length total: this.data.m3u8json.segments?.length
}) })
); );
console.info('Init part downloaded.'); console.info('Init part downloaded.');
@ -187,17 +188,17 @@ class hlsDownload {
console.error(`Part init download error:\n\t${e.message}`); console.error(`Part init download error:\n\t${e.message}`);
return { ok: false, parts: this.data.parts }; return { ok: false, parts: this.data.parts };
} }
} else if (segments[0].map && this.data.offset === 0 && this.data.skipInit) { } else if (segments?.[0].map && this.data.offset === 0 && this.data.skipInit) {
console.warn('Skipping init part can lead to broken video!'); console.warn('Skipping init part can lead to broken video!');
} }
// resuming ... // resuming ...
if (this.data.offset > 0) { if (this.data.offset > 0) {
segments = segments.slice(this.data.offset); segments = segments?.slice(this.data.offset);
console.info(`Resuming download from part ${this.data.offset + 1}...`); console.info(`Resuming download from part ${this.data.offset + 1}...`);
this.data.parts.completed = this.data.offset; this.data.parts.completed = this.data.offset;
} }
// dl process // dl process
for (let p = 0; p < segments.length / this.data.threads; p++) { for (let p = 0; p < (segments?.length ?? 0) / this.data.threads; p++) {
// set offsets // set offsets
const offset = p * this.data.threads; const offset = p * this.data.threads;
const dlOffset = offset + this.data.threads; const dlOffset = offset + this.data.threads;
@ -206,9 +207,9 @@ class hlsDownload {
prq = new Map(); prq = new Map();
const res: any[] = []; const res: any[] = [];
let errcnt = 0; let errcnt = 0;
for (let px = offset; px < dlOffset && px < segments.length; px++) { for (let px = offset; px < dlOffset && px < (segments?.length ?? 0); px++) {
const curp = segments[px]; const curp = segments?.[px];
const key = curp.key as Key; const key = curp?.key as Key;
if (key && !krq.has(key.uri) && !this.data.keys[key.uri as string]) { if (key && !krq.has(key.uri) && !this.data.keys[key.uri as string]) {
krq.set(key.uri, this.downloadKey(key, px, this.data.offset)); krq.set(key.uri, this.downloadKey(key, px, this.data.offset));
} }
@ -219,8 +220,8 @@ class hlsDownload {
console.error(`Key ${er.p + 1} download error:\n\t${er.message}`); console.error(`Key ${er.p + 1} download error:\n\t${er.message}`);
return { ok: false, parts: this.data.parts }; return { ok: false, parts: this.data.parts };
} }
for (let px = offset; px < dlOffset && px < segments.length; px++) { for (let px = offset; px < dlOffset && px < (segments?.length ?? 0); px++) {
const curp = segments[px] as Segment; const curp = segments?.[px] as Segment;
prq.set(px, () => this.downloadPart(curp, px, this.data.offset)); prq.set(px, () => this.downloadPart(curp, px, this.data.offset));
} }
// Parallelized part download with retry logic and optional concurrency limit // Parallelized part download with retry logic and optional concurrency limit
@ -286,7 +287,7 @@ class hlsDownload {
} }
} }
// log downloaded // log downloaded
const totalSeg = segments.length + this.data.offset; // Add the sliced lenght back so the resume data will be correct even if an resumed download fails const totalSeg = (segments?.length ?? 0) + this.data.offset; // Add the sliced lenght back so the resume data will be correct even if an resumed download fails
const downloadedSeg = dlOffset < totalSeg ? dlOffset : totalSeg; const downloadedSeg = dlOffset < totalSeg ? dlOffset : totalSeg;
this.data.parts.completed = downloadedSeg + this.data.offset; this.data.parts.completed = downloadedSeg + this.data.offset;
const data = extFn.getDownloadInfo(this.data.dateStart, downloadedSeg, totalSeg, this.data.bytesDownloaded); const data = extFn.getDownloadInfo(this.data.dateStart, downloadedSeg, totalSeg, this.data.bytesDownloaded);

View file

@ -7,7 +7,7 @@ const logFolder = path.join(workingDir, 'logs');
const latest = path.join(logFolder, 'latest.log'); const latest = path.join(logFolder, 'latest.log');
const makeLogFolder = () => { const makeLogFolder = () => {
if (!fs.existsSync(logFolder)) fs.mkdirSync(logFolder); if (!fs.existsSync(logFolder)) fs.mkdirSync(logFolder, { recursive: true });
if (fs.existsSync(latest)) { if (fs.existsSync(latest)) {
const stats = fs.statSync(latest); const stats = fs.statSync(latest);
fs.renameSync(latest, path.join(logFolder, `${stats.mtimeMs}.log`)); fs.renameSync(latest, path.join(logFolder, `${stats.mtimeMs}.log`));

View file

@ -1,6 +1,6 @@
import path from 'path'; import path from 'path';
import yaml from 'yaml'; import yaml from 'yaml';
import fs from 'fs-extra'; import fs from 'fs';
import { lookpath } from 'lookpath'; import { lookpath } from 'lookpath';
import { console } from './log'; import { console } from './log';
import { GuiState } from '../@types/messageHandler'; import { GuiState } from '../@types/messageHandler';
@ -37,7 +37,7 @@ const tokenFile = {
}; };
export const ensureConfig = () => { export const ensureConfig = () => {
if (!fs.existsSync(path.join(workingDir, 'config'))) fs.mkdirSync(path.join(workingDir, 'config')); if (!fs.existsSync(path.join(workingDir, 'config'))) fs.mkdirSync(path.join(workingDir, 'config'), { recursive: true });
if (process.env.contentDirectory) if (process.env.contentDirectory)
[binCfgFile, dirCfgFile, cliCfgFile, guiCfgFile].forEach((a) => { [binCfgFile, dirCfgFile, cliCfgFile, guiCfgFile].forEach((a) => {
if (!fs.existsSync(`${a}.yml`)) fs.copyFileSync(path.join(__dirname, '..', 'config', `${path.basename(a)}.yml`), `${a}.yml`); if (!fs.existsSync(`${a}.yml`)) fs.copyFileSync(path.join(__dirname, '..', 'config', `${path.basename(a)}.yml`), `${a}.yml`);
@ -66,7 +66,7 @@ export type WriteObjects = {
const writeYamlCfgFile = <T extends keyof WriteObjects>(file: T, data: WriteObjects[T]) => { const writeYamlCfgFile = <T extends keyof WriteObjects>(file: T, data: WriteObjects[T]) => {
const fn = path.join(workingDir, 'config', `${file}.yml`); const fn = path.join(workingDir, 'config', `${file}.yml`);
if (fs.existsSync(fn)) fs.removeSync(fn); if (fs.existsSync(fn)) fs.unlinkSync(fn);
fs.writeFileSync(fn, yaml.stringify(data)); fs.writeFileSync(fn, yaml.stringify(data));
}; };
@ -131,7 +131,7 @@ const loadCfg = (): ConfigObject => {
} }
if (!fs.existsSync(defaultCfg.dir.content)) { if (!fs.existsSync(defaultCfg.dir.content)) {
try { try {
fs.ensureDirSync(defaultCfg.dir.content); fs.mkdirSync(defaultCfg.dir.content, { recursive: true });
} catch (e) { } catch (e) {
console.error('Content directory not accessible!'); console.error('Content directory not accessible!');
return defaultCfg; return defaultCfg;
@ -192,7 +192,7 @@ const loadCRSession = () => {
const saveCRSession = (data: Record<string, unknown>) => { const saveCRSession = (data: Record<string, unknown>) => {
const cfgFolder = path.dirname(sessCfgFile.cr); const cfgFolder = path.dirname(sessCfgFile.cr);
try { try {
fs.ensureDirSync(cfgFolder); fs.mkdirSync(cfgFolder, { recursive: true });
fs.writeFileSync(`${sessCfgFile.cr}.yml`, yaml.stringify(data)); fs.writeFileSync(`${sessCfgFile.cr}.yml`, yaml.stringify(data));
} catch (e) { } catch (e) {
console.error("Can't save session file to disk!"); console.error("Can't save session file to disk!");
@ -210,7 +210,7 @@ const loadCRToken = () => {
const saveCRToken = (data: Record<string, unknown>) => { const saveCRToken = (data: Record<string, unknown>) => {
const cfgFolder = path.dirname(tokenFile.cr); const cfgFolder = path.dirname(tokenFile.cr);
try { try {
fs.ensureDirSync(cfgFolder); fs.mkdirSync(cfgFolder, { recursive: true });
fs.writeFileSync(`${tokenFile.cr}.yml`, yaml.stringify(data)); fs.writeFileSync(`${tokenFile.cr}.yml`, yaml.stringify(data));
} catch (e) { } catch (e) {
console.error("Can't save token file to disk!"); console.error("Can't save token file to disk!");
@ -228,7 +228,7 @@ const loadADNToken = () => {
const saveADNToken = (data: Record<string, unknown>) => { const saveADNToken = (data: Record<string, unknown>) => {
const cfgFolder = path.dirname(tokenFile.adn); const cfgFolder = path.dirname(tokenFile.adn);
try { try {
fs.ensureDirSync(cfgFolder); fs.mkdirSync(cfgFolder, { recursive: true });
fs.writeFileSync(`${tokenFile.adn}.yml`, yaml.stringify(data)); fs.writeFileSync(`${tokenFile.adn}.yml`, yaml.stringify(data));
} catch (e) { } catch (e) {
console.error("Can't save token file to disk!"); console.error("Can't save token file to disk!");
@ -251,7 +251,7 @@ const loadHDSession = () => {
const saveHDSession = (data: Record<string, unknown>) => { const saveHDSession = (data: Record<string, unknown>) => {
const cfgFolder = path.dirname(sessCfgFile.hd); const cfgFolder = path.dirname(sessCfgFile.hd);
try { try {
fs.ensureDirSync(cfgFolder); fs.mkdirSync(cfgFolder, { recursive: true });
fs.writeFileSync(`${sessCfgFile.hd}.yml`, yaml.stringify(data)); fs.writeFileSync(`${sessCfgFile.hd}.yml`, yaml.stringify(data));
} catch (e) { } catch (e) {
console.error("Can't save session file to disk!"); console.error("Can't save session file to disk!");
@ -269,7 +269,7 @@ const loadHDToken = () => {
const saveHDToken = (data: Record<string, unknown>) => { const saveHDToken = (data: Record<string, unknown>) => {
const cfgFolder = path.dirname(tokenFile.hd); const cfgFolder = path.dirname(tokenFile.hd);
try { try {
fs.ensureDirSync(cfgFolder); fs.mkdirSync(cfgFolder, { recursive: true });
fs.writeFileSync(`${tokenFile.hd}.yml`, yaml.stringify(data)); fs.writeFileSync(`${tokenFile.hd}.yml`, yaml.stringify(data));
} catch (e) { } catch (e) {
console.error("Can't save token file to disk!"); console.error("Can't save token file to disk!");
@ -279,7 +279,7 @@ const saveHDToken = (data: Record<string, unknown>) => {
const saveHDProfile = (data: Record<string, unknown>) => { const saveHDProfile = (data: Record<string, unknown>) => {
const cfgFolder = path.dirname(hdPflCfgFile); const cfgFolder = path.dirname(hdPflCfgFile);
try { try {
fs.ensureDirSync(cfgFolder); fs.mkdirSync(cfgFolder, { recursive: true });
fs.writeFileSync(`${hdPflCfgFile}.yml`, yaml.stringify(data)); fs.writeFileSync(`${hdPflCfgFile}.yml`, yaml.stringify(data));
} catch (e) { } catch (e) {
console.error("Can't save profile file to disk!"); console.error("Can't save profile file to disk!");
@ -318,7 +318,7 @@ const loadNewHDToken = () => {
const saveNewHDToken = (data: Record<string, unknown>) => { const saveNewHDToken = (data: Record<string, unknown>) => {
const cfgFolder = path.dirname(tokenFile.hdNew); const cfgFolder = path.dirname(tokenFile.hdNew);
try { try {
fs.ensureDirSync(cfgFolder); fs.mkdirSync(cfgFolder, { recursive: true });
fs.writeFileSync(`${tokenFile.hdNew}.yml`, yaml.stringify(data)); fs.writeFileSync(`${tokenFile.hdNew}.yml`, yaml.stringify(data));
} catch (e) { } catch (e) {
console.error("Can't save token file to disk!"); console.error("Can't save token file to disk!");

View file

@ -1,11 +1,10 @@
import fs from 'fs';
import { GithubTag, TagCompare } from '../@types/github'; import { GithubTag, TagCompare } from '../@types/github';
import path from 'path'; import path from 'path';
import { UpdateFile } from '../@types/updateFile'; import { UpdateFile } from '../@types/updateFile';
import packageJson from '../package.json'; import packageJson from '../package.json';
import { CompilerOptions, transpileModule } from 'typescript'; import { CompilerOptions, transpileModule } from 'typescript';
import tsConfig from '../tsconfig.json'; import tsConfig from '../tsconfig.json';
import fsextra from 'fs-extra'; import fs from 'fs';
import { workingDir } from './module.cfg-loader'; import { workingDir } from './module.cfg-loader';
import { console } from './log'; import { console } from './log';
import Helper from './module.helper'; import Helper from './module.helper';
@ -142,7 +141,7 @@ export default async (force = false) => {
changesToApply.forEach((a) => { changesToApply.forEach((a) => {
try { try {
fsextra.ensureDirSync(path.dirname(a.path)); fs.mkdirSync(path.dirname(a.path), { recursive: true });
fs.writeFileSync(path.join(__dirname, '..', a.path), a.content); fs.writeFileSync(path.join(__dirname, '..', a.path), a.content);
console.info('✓ Written %s', a.path); console.info('✓ Written %s', a.path);
} catch (er) { } catch (er) {

View file

@ -42,19 +42,16 @@
"dependencies": { "dependencies": {
"@bufbuild/protobuf": "^2.10.1", "@bufbuild/protobuf": "^2.10.1",
"commander": "^14.0.2", "commander": "^14.0.2",
"cors": "^2.8.5",
"express": "^5.1.0", "express": "^5.1.0",
"ffprobe": "^1.1.2", "ffprobe": "^1.1.2",
"fs-extra": "^11.3.2",
"iso-639": "^0.2.2", "iso-639": "^0.2.2",
"leven": "^4.1.0", "leven": "^4.1.0",
"log4js": "^6.9.1", "log4js": "^6.9.1",
"lookpath": "^1.2.3", "lookpath": "^1.2.3",
"m3u8-parsed": "^2.0.0", "m3u8-parser": "^7.2.0",
"mpd-parser": "^1.3.1", "mpd-parser": "^1.3.1",
"node-playready": "^1.1.1", "node-playready": "^1.1.1",
"open": "^11.0.0", "open": "^11.0.0",
"protobufjs": "^7.5.4",
"undici": "^7.16.0", "undici": "^7.16.0",
"widevine": "^1.0.3", "widevine": "^1.0.3",
"ws": "^8.18.3", "ws": "^8.18.3",
@ -62,10 +59,9 @@
}, },
"devDependencies": { "devDependencies": {
"@eslint/js": "^9.39.1", "@eslint/js": "^9.39.1",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.5", "@types/express": "^5.0.5",
"@types/ffprobe": "^1.1.8", "@types/ffprobe": "^1.1.8",
"@types/fs-extra": "^11.0.4", "@types/m3u8-parser": "^7.2.5",
"@types/node": "^24.10.1", "@types/node": "^24.10.1",
"@types/ws": "^8.18.1", "@types/ws": "^8.18.1",
"@typescript-eslint/eslint-plugin": "^8.48.0", "@typescript-eslint/eslint-plugin": "^8.48.0",
@ -88,7 +84,7 @@
"tsc": "ts-node tsc.ts", "tsc": "ts-node tsc.ts",
"eslint": "npx eslint . --quiet", "eslint": "npx eslint . --quiet",
"prettier": "npx prettier . --check", "prettier": "npx prettier . --check",
"prettier-fix": "npx prettier . --write", "prettier:fix": "npx prettier . --write",
"pretest": "pnpm run tsc", "pretest": "pnpm run tsc",
"proto:compile": "protoc --plugin=protoc-gen-ts_proto=.\\node_modules\\.bin\\protoc-gen-ts_proto.cmd --ts_proto_opt=\"esModuleInterop=true\" --ts_proto_opt=\"forceLong=long\" --ts_proto_opt=\"env=node\" --ts_proto_out=. modules/*.proto", "proto:compile": "protoc --plugin=protoc-gen-ts_proto=.\\node_modules\\.bin\\protoc-gen-ts_proto.cmd --ts_proto_opt=\"esModuleInterop=true\" --ts_proto_opt=\"forceLong=long\" --ts_proto_opt=\"env=node\" --ts_proto_out=. modules/*.proto",
"prebuild-cli": "pnpm run tsc false false", "prebuild-cli": "pnpm run tsc false false",

View file

@ -14,18 +14,12 @@ importers:
commander: commander:
specifier: ^14.0.2 specifier: ^14.0.2
version: 14.0.2 version: 14.0.2
cors:
specifier: ^2.8.5
version: 2.8.5
express: express:
specifier: ^5.1.0 specifier: ^5.1.0
version: 5.1.0 version: 5.1.0
ffprobe: ffprobe:
specifier: ^1.1.2 specifier: ^1.1.2
version: 1.1.2 version: 1.1.2
fs-extra:
specifier: ^11.3.2
version: 11.3.2
iso-639: iso-639:
specifier: ^0.2.2 specifier: ^0.2.2
version: 0.2.2 version: 0.2.2
@ -38,9 +32,9 @@ importers:
lookpath: lookpath:
specifier: ^1.2.3 specifier: ^1.2.3
version: 1.2.3 version: 1.2.3
m3u8-parsed: m3u8-parser:
specifier: ^2.0.0 specifier: ^7.2.0
version: 2.0.0 version: 7.2.0
mpd-parser: mpd-parser:
specifier: ^1.3.1 specifier: ^1.3.1
version: 1.3.1 version: 1.3.1
@ -50,9 +44,6 @@ importers:
open: open:
specifier: ^11.0.0 specifier: ^11.0.0
version: 11.0.0 version: 11.0.0
protobufjs:
specifier: ^7.5.4
version: 7.5.4
undici: undici:
specifier: ^7.16.0 specifier: ^7.16.0
version: 7.16.0 version: 7.16.0
@ -69,18 +60,15 @@ importers:
'@eslint/js': '@eslint/js':
specifier: ^9.39.1 specifier: ^9.39.1
version: 9.39.1 version: 9.39.1
'@types/cors':
specifier: ^2.8.19
version: 2.8.19
'@types/express': '@types/express':
specifier: ^5.0.5 specifier: ^5.0.5
version: 5.0.5 version: 5.0.5
'@types/ffprobe': '@types/ffprobe':
specifier: ^1.1.8 specifier: ^1.1.8
version: 1.1.8 version: 1.1.8
'@types/fs-extra': '@types/m3u8-parser':
specifier: ^11.0.4 specifier: ^7.2.5
version: 11.0.4 version: 7.2.5
'@types/node': '@types/node':
specifier: ^24.10.1 specifier: ^24.10.1
version: 24.10.1 version: 24.10.1
@ -393,36 +381,6 @@ packages:
resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==} resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==}
engines: {node: '>= 20.19.0'} engines: {node: '>= 20.19.0'}
'@protobufjs/aspromise@1.1.2':
resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==}
'@protobufjs/base64@1.1.2':
resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==}
'@protobufjs/codegen@2.0.4':
resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==}
'@protobufjs/eventemitter@1.1.0':
resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==}
'@protobufjs/fetch@1.1.0':
resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==}
'@protobufjs/float@1.0.2':
resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==}
'@protobufjs/inquire@1.1.0':
resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==}
'@protobufjs/path@1.1.2':
resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==}
'@protobufjs/pool@1.1.0':
resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==}
'@protobufjs/utf8@1.1.0':
resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==}
'@tsconfig/node10@1.0.12': '@tsconfig/node10@1.0.12':
resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==}
@ -441,9 +399,6 @@ packages:
'@types/connect@3.4.38': '@types/connect@3.4.38':
resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
'@types/cors@2.8.19':
resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==}
'@types/estree@1.0.8': '@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
@ -456,17 +411,14 @@ packages:
'@types/ffprobe@1.1.8': '@types/ffprobe@1.1.8':
resolution: {integrity: sha512-qPxx8Dy0HyH12hQCESN39NQOmU2Yl2b7tCyUhWy0l11HQv/8Yv7U+vcaveFXmXK8hcAP0oj29DPFuSM7vcC3Tg==} resolution: {integrity: sha512-qPxx8Dy0HyH12hQCESN39NQOmU2Yl2b7tCyUhWy0l11HQv/8Yv7U+vcaveFXmXK8hcAP0oj29DPFuSM7vcC3Tg==}
'@types/fs-extra@11.0.4':
resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==}
'@types/http-errors@2.0.5': '@types/http-errors@2.0.5':
resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==}
'@types/json-schema@7.0.15': '@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
'@types/jsonfile@6.1.4': '@types/m3u8-parser@7.2.5':
resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==} resolution: {integrity: sha512-e4aoWGzTV+tQRGN1CfJhp1jt5yIHW0X0Lt2rjflDh/cTXYY9nDLRehX7S5iu+gyoaqVLDletBtPcDcPcQBKaeg==}
'@types/mime@1.3.5': '@types/mime@1.3.5':
resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==}
@ -753,10 +705,6 @@ packages:
core-util-is@1.0.3: core-util-is@1.0.3:
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
cors@2.8.5:
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
engines: {node: '>= 0.10'}
create-require@1.1.1: create-require@1.1.1:
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
@ -1190,17 +1138,11 @@ packages:
resolution: {integrity: sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==} resolution: {integrity: sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==}
engines: {node: '>=8.0'} engines: {node: '>=8.0'}
long@5.3.2:
resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==}
lookpath@1.2.3: lookpath@1.2.3:
resolution: {integrity: sha512-kthRVhf4kH4+HW3anM4UBHxsw/XFESf13euCEldhXr6GpBdmBoa7rDd7WO5G0Mhd4G5XtKTcEy8OR0iRZXpS3Q==} resolution: {integrity: sha512-kthRVhf4kH4+HW3anM4UBHxsw/XFESf13euCEldhXr6GpBdmBoa7rDd7WO5G0Mhd4G5XtKTcEy8OR0iRZXpS3Q==}
engines: {npm: '>=6.13.4'} engines: {npm: '>=6.13.4'}
hasBin: true hasBin: true
m3u8-parsed@2.0.0:
resolution: {integrity: sha512-41xr3I4eI8/gRULPJhijTHigeGvwMVwschrh9ueOW7/XlgWldVgarJG1fkDa1mjy4Z+KIeT38LYt/zOYpmW/GA==}
m3u8-parser@7.2.0: m3u8-parser@7.2.0:
resolution: {integrity: sha512-CRatFqpjVtMiMaKXxNvuI3I++vUumIXVVT/JpCpdU/FynV/ceVw1qpPyyBNindL+JlPMSesx+WX1QJaZEJSaMQ==} resolution: {integrity: sha512-CRatFqpjVtMiMaKXxNvuI3I++vUumIXVVT/JpCpdU/FynV/ceVw1qpPyyBNindL+JlPMSesx+WX1QJaZEJSaMQ==}
@ -1301,10 +1243,6 @@ packages:
node-playready@1.1.1: node-playready@1.1.1:
resolution: {integrity: sha512-lGoS0T4cVnRZA5mQbHOn31EXqRwijb0pXdxISCL8cjFhE6DjWjEtlcCL/XgpUFwODajVUg5GI0zFVJZOH4nRtg==} resolution: {integrity: sha512-lGoS0T4cVnRZA5mQbHOn31EXqRwijb0pXdxISCL8cjFhE6DjWjEtlcCL/XgpUFwODajVUg5GI0zFVJZOH4nRtg==}
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
object-inspect@1.13.4: object-inspect@1.13.4:
resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -1394,10 +1332,6 @@ packages:
resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
engines: {node: '>=0.4.0'} engines: {node: '>=0.4.0'}
protobufjs@7.5.4:
resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==}
engines: {node: '>=12.0.0'}
proxy-addr@2.0.7: proxy-addr@2.0.7:
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
engines: {node: '>= 0.10'} engines: {node: '>= 0.10'}
@ -1943,29 +1877,6 @@ snapshots:
'@noble/hashes@2.0.1': {} '@noble/hashes@2.0.1': {}
'@protobufjs/aspromise@1.1.2': {}
'@protobufjs/base64@1.1.2': {}
'@protobufjs/codegen@2.0.4': {}
'@protobufjs/eventemitter@1.1.0': {}
'@protobufjs/fetch@1.1.0':
dependencies:
'@protobufjs/aspromise': 1.1.2
'@protobufjs/inquire': 1.1.0
'@protobufjs/float@1.0.2': {}
'@protobufjs/inquire@1.1.0': {}
'@protobufjs/path@1.1.2': {}
'@protobufjs/pool@1.1.0': {}
'@protobufjs/utf8@1.1.0': {}
'@tsconfig/node10@1.0.12': {} '@tsconfig/node10@1.0.12': {}
'@tsconfig/node12@1.0.11': {} '@tsconfig/node12@1.0.11': {}
@ -1983,10 +1894,6 @@ snapshots:
dependencies: dependencies:
'@types/node': 24.10.1 '@types/node': 24.10.1
'@types/cors@2.8.19':
dependencies:
'@types/node': 24.10.1
'@types/estree@1.0.8': {} '@types/estree@1.0.8': {}
'@types/express-serve-static-core@5.1.0': '@types/express-serve-static-core@5.1.0':
@ -2004,18 +1911,11 @@ snapshots:
'@types/ffprobe@1.1.8': {} '@types/ffprobe@1.1.8': {}
'@types/fs-extra@11.0.4':
dependencies:
'@types/jsonfile': 6.1.4
'@types/node': 24.10.1
'@types/http-errors@2.0.5': {} '@types/http-errors@2.0.5': {}
'@types/json-schema@7.0.15': {} '@types/json-schema@7.0.15': {}
'@types/jsonfile@6.1.4': '@types/m3u8-parser@7.2.5': {}
dependencies:
'@types/node': 24.10.1
'@types/mime@1.3.5': {} '@types/mime@1.3.5': {}
@ -2366,11 +2266,6 @@ snapshots:
core-util-is@1.0.3: {} core-util-is@1.0.3: {}
cors@2.8.5:
dependencies:
object-assign: 4.1.1
vary: 1.1.2
create-require@1.1.1: {} create-require@1.1.1: {}
cross-spawn@7.0.6: cross-spawn@7.0.6:
@ -2836,14 +2731,8 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
long@5.3.2: {}
lookpath@1.2.3: {} lookpath@1.2.3: {}
m3u8-parsed@2.0.0:
dependencies:
m3u8-parser: 7.2.0
m3u8-parser@7.2.0: m3u8-parser@7.2.0:
dependencies: dependencies:
'@babel/runtime': 7.28.4 '@babel/runtime': 7.28.4
@ -2927,8 +2816,6 @@ snapshots:
'@noble/curves': 2.0.1 '@noble/curves': 2.0.1
fast-xml-parser: 5.3.2 fast-xml-parser: 5.3.2
object-assign@4.1.1: {}
object-inspect@1.13.4: {} object-inspect@1.13.4: {}
on-finished@2.4.1: on-finished@2.4.1:
@ -3012,21 +2899,6 @@ snapshots:
progress@2.0.3: {} progress@2.0.3: {}
protobufjs@7.5.4:
dependencies:
'@protobufjs/aspromise': 1.1.2
'@protobufjs/base64': 1.1.2
'@protobufjs/codegen': 2.0.4
'@protobufjs/eventemitter': 1.1.0
'@protobufjs/fetch': 1.1.0
'@protobufjs/float': 1.0.2
'@protobufjs/inquire': 1.1.0
'@protobufjs/path': 1.1.2
'@protobufjs/pool': 1.1.0
'@protobufjs/utf8': 1.1.0
'@types/node': 24.10.1
long: 5.3.2
proxy-addr@2.0.7: proxy-addr@2.0.7:
dependencies: dependencies:
forwarded: 0.2.0 forwarded: 0.2.0

12
tsc.ts
View file

@ -1,7 +1,6 @@
import { ChildProcess, exec } from 'child_process'; import { ChildProcess, exec } from 'child_process';
import fs from 'fs-extra';
import path from 'path'; import path from 'path';
import { removeSync, copyFileSync } from 'fs-extra'; import fs from 'fs';
const argv = process.argv.slice(2); const argv = process.argv.slice(2);
let buildIgnore: string[] = []; let buildIgnore: string[] = [];
@ -57,15 +56,14 @@ export { ignore };
}; };
process.stdout.write('Removing lib dir... '); process.stdout.write('Removing lib dir... ');
removeSync('lib'); fs.rmSync('lib', { recursive: true, force: true });
process.stdout.write('✓\nRunning tsc... '); process.stdout.write('✓\nRunning tsc... ');
const tsc = exec('npx tsc'); const tsc = exec('npx tsc');
await waitForProcess(tsc); await waitForProcess(tsc);
if (!isGUI) { if (!isGUI) {
fs.emptyDirSync(path.join('lib', 'gui')); fs.rmSync(path.join('lib', 'gui'), { recursive: true, force: true });
fs.rmdirSync(path.join('lib', 'gui'));
} }
if (!isTest && isGUI) { if (!isTest && isGUI) {
@ -97,9 +95,9 @@ export { ignore };
files.forEach((item) => { files.forEach((item) => {
const itemPath = path.join(__dirname, 'lib', item.path.replace(__dirname, '')); const itemPath = path.join(__dirname, 'lib', item.path.replace(__dirname, ''));
if (item.stats.isDirectory()) { if (item.stats.isDirectory()) {
if (!fs.existsSync(itemPath)) fs.mkdirSync(itemPath); if (!fs.existsSync(itemPath)) fs.mkdirSync(itemPath, { recursive: true });
} else { } else {
copyFileSync(item.path, itemPath); fs.cpSync(item.path, itemPath, { recursive: true });
} }
}); });