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
import path from 'path';
import fs from 'fs-extra';
import fs from 'fs';
import crypto from 'crypto';
// Plugins
import m3u8 from 'm3u8-parsed';
import m3u8 from 'm3u8-parser';
// Modules
import * as fontsData from './modules/module.fontsData';
@ -600,22 +600,32 @@ export default class AnimationDigitalNetwork implements ServiceClass {
dlFailed = true;
} else {
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[] = [],
plStreams: Record<string, Record<string, string>> = {},
plQuality: {
str: string;
dim: string;
CODECS: string;
RESOLUTION: {
width: number;
height: number;
CODECS?: string;
RESOLUTION?: {
width?: number;
height?: number;
};
}[] = [];
for (const pl of streamPlaylists.playlists) {
for (const pl of streamPlaylists.playlists ?? []) {
// set quality
const plResolution = pl.attributes.RESOLUTION;
const plResolutionText = `${plResolution.width}x${plResolution.height}`;
const plResolutionText = `${plResolution?.width}x${plResolution?.height}`;
// set codecs
const plCodecs = pl.attributes.CODECS;
// parse uri
@ -642,7 +652,7 @@ export default class AnimationDigitalNetwork implements ServiceClass {
plStreams[plServer][plResolutionText] = pl.uri;
}
// 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 qualityStrRegx = new RegExp(qualityStrAdd.replace(/([:()/])/g, '\\$1'), 'm');
const qualityStrMatch = !plQuality
@ -691,12 +701,12 @@ export default class AnimationDigitalNetwork implements ServiceClass {
{
name: 'height',
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',
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;
} else {
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 mathParts = Math.ceil(totalParts / options.partsize);
const mathMsg = `(${mathParts}*${options.partsize})`;

View file

@ -1,15 +1,15 @@
// build-in
import path from 'path';
import fs from 'fs-extra';
import fs from 'fs';
// package program
import packageJson from './package.json';
// plugins
import { console } from './modules/log';
import m3u8 from 'm3u8-parsed';
import streamdl, { M3U8Json } from './modules/hls-download';
import Helper from './modules/module.helper';
import m3u8 from 'm3u8-parser';
// custom modules
import * as fontsData from './modules/module.fontsData';
@ -347,10 +347,10 @@ export default class Crunchy implements ServiceClass {
} else {
const fontFolder = path.dirname(fontLoc);
if (fs.existsSync(fontLoc) && fs.statSync(fontLoc).size == 0) {
fs.unlinkSync(fontLoc);
fs.rmSync(fontLoc, { recursive: true, force: true });
}
try {
fs.ensureDirSync(fontFolder);
fs.existsSync(fontFolder);
} catch (e) {
console.info('');
}
@ -2400,7 +2400,7 @@ export default class Crunchy implements ServiceClass {
} else {
console.info('Decryption done for video');
if (!options.nocleanup) {
fs.removeSync(`${tempTsFile}.video.enc.m4s`);
fs.unlinkSync(`${tempTsFile}.video.enc.m4s`);
}
fs.copyFileSync(`${tempTsFile}.video.m4s`, `${tsFile}.video.m4s`);
fs.unlinkSync(`${tempTsFile}.video.m4s`);
@ -2430,7 +2430,7 @@ export default class Crunchy implements ServiceClass {
return undefined;
} else {
if (!options.nocleanup) {
fs.removeSync(`${tempTsFile}.audio.enc.m4s`);
fs.unlinkSync(`${tempTsFile}.audio.enc.m4s`);
}
fs.copyFileSync(`${tempTsFile}.audio.m4s`, `${tsFile}.audio.m4s`);
fs.unlinkSync(`${tempTsFile}.audio.m4s`);
@ -2467,22 +2467,31 @@ export default class Crunchy implements ServiceClass {
}
}
} 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[] = [],
plStreams: Record<string, Record<string, string>> = {},
plQuality: {
str: string;
dim: string;
CODECS: string;
RESOLUTION: {
width: number;
height: number;
CODECS?: string;
RESOLUTION?: {
width?: number;
height?: number;
};
}[] = [];
for (const pl of streamPlaylists.playlists) {
for (const pl of streamPlaylists.playlists ?? []) {
// set quality
const plResolution = pl.attributes.RESOLUTION;
const plResolutionText = `${plResolution.width}x${plResolution.height}`;
const plResolutionText = `${plResolution?.width}x${plResolution?.height}`;
// set codecs
const plCodecs = pl.attributes.CODECS;
// parse uri
@ -2509,7 +2518,7 @@ export default class Crunchy implements ServiceClass {
plStreams[plServer][plResolutionText] = pl.uri;
}
// 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 qualityStrRegx = new RegExp(qualityStrAdd.replace(/([:()/])/g, '\\$1'), 'm');
const qualityStrMatch = !plQuality
@ -2556,12 +2565,14 @@ export default class Crunchy implements ServiceClass {
{
name: 'height',
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',
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);
@ -2598,7 +2609,16 @@ export default class Crunchy implements ServiceClass {
}
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 mathParts = Math.ceil(totalParts / options.partsize);
const mathMsg = `(${mathParts}*${options.partsize})`;

View file

@ -1,8 +1,7 @@
import express from 'express';
import { ensureConfig, loadCfg, workingDir } from '../../modules/module.cfg-loader';
import cors from 'cors';
import ServiceHandler from './serviceHandler';
import open from 'open';
import ServiceHandler from './serviceHandler';
import path from 'path';
import { PublicWebSocket } from './websocket';
import { console } from '../../modules/log';
@ -19,7 +18,14 @@ const app = express();
export { app, cfg };
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 }));
console.info(`\n=== Multi Downloader NX GUI ${packageJson.version} ===\n`);

View file

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

View file

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

View file

@ -8,6 +8,7 @@ import { console } from './log';
import { ProgressData } from '../@types/messageHandler';
import Helper from './module.helper';
import * as reqModule from './module.fetch';
import { Manifest } from 'm3u8-parser';
const req = new reqModule.Req();
@ -33,7 +34,7 @@ type Key = {
};
export type HLSOptions = {
m3u8json: M3U8Json;
m3u8json: M3U8Json | Partial<Manifest>;
output?: string;
threads?: number;
retries?: number;
@ -52,7 +53,7 @@ type Data = {
total: number;
completed: number;
};
m3u8json: M3U8Json;
m3u8json: M3U8Json | Partial<Manifest>;
outputFile: string;
threads: number;
retries: number;
@ -118,7 +119,7 @@ class hlsDownload {
if (age < 24 * 60 * 60 * 1000) {
console.info('Resume data found! Trying to resume...');
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!');
this.data.offset = resumeData.completed;
this.data.isResume = true;
@ -126,7 +127,7 @@ class hlsDownload {
console.warn(' Resume data is wrong!');
console.warn({
resume: { total: resumeData.total, dled: resumeData.completed },
current: { total: this.data.m3u8json.segments.length }
current: { total: this.data.m3u8json.segments?.length }
});
}
} else {
@ -166,7 +167,7 @@ class hlsDownload {
this.data.dateStart = Date.now();
let segments = this.data.m3u8json.segments;
// 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...');
const initSeg = segments[0].map as Segment;
if (segments[0].key) {
@ -179,7 +180,7 @@ class hlsDownload {
`${fn}.resume`,
JSON.stringify({
completed: 0,
total: this.data.m3u8json.segments.length
total: this.data.m3u8json.segments?.length
})
);
console.info('Init part downloaded.');
@ -187,17 +188,17 @@ class hlsDownload {
console.error(`Part init download error:\n\t${e.message}`);
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!');
}
// resuming ...
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}...`);
this.data.parts.completed = this.data.offset;
}
// 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
const offset = p * this.data.threads;
const dlOffset = offset + this.data.threads;
@ -206,9 +207,9 @@ class hlsDownload {
prq = new Map();
const res: any[] = [];
let errcnt = 0;
for (let px = offset; px < dlOffset && px < segments.length; px++) {
const curp = segments[px];
const key = curp.key as Key;
for (let px = offset; px < dlOffset && px < (segments?.length ?? 0); px++) {
const curp = segments?.[px];
const key = curp?.key as Key;
if (key && !krq.has(key.uri) && !this.data.keys[key.uri as string]) {
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}`);
return { ok: false, parts: this.data.parts };
}
for (let px = offset; px < dlOffset && px < segments.length; px++) {
const curp = segments[px] as Segment;
for (let px = offset; px < dlOffset && px < (segments?.length ?? 0); px++) {
const curp = segments?.[px] as Segment;
prq.set(px, () => this.downloadPart(curp, px, this.data.offset));
}
// Parallelized part download with retry logic and optional concurrency limit
@ -286,7 +287,7 @@ class hlsDownload {
}
}
// 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;
this.data.parts.completed = downloadedSeg + this.data.offset;
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 makeLogFolder = () => {
if (!fs.existsSync(logFolder)) fs.mkdirSync(logFolder);
if (!fs.existsSync(logFolder)) fs.mkdirSync(logFolder, { recursive: true });
if (fs.existsSync(latest)) {
const stats = fs.statSync(latest);
fs.renameSync(latest, path.join(logFolder, `${stats.mtimeMs}.log`));

View file

@ -1,6 +1,6 @@
import path from 'path';
import yaml from 'yaml';
import fs from 'fs-extra';
import fs from 'fs';
import { lookpath } from 'lookpath';
import { console } from './log';
import { GuiState } from '../@types/messageHandler';
@ -37,7 +37,7 @@ const tokenFile = {
};
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)
[binCfgFile, dirCfgFile, cliCfgFile, guiCfgFile].forEach((a) => {
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 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));
};
@ -131,7 +131,7 @@ const loadCfg = (): ConfigObject => {
}
if (!fs.existsSync(defaultCfg.dir.content)) {
try {
fs.ensureDirSync(defaultCfg.dir.content);
fs.mkdirSync(defaultCfg.dir.content, { recursive: true });
} catch (e) {
console.error('Content directory not accessible!');
return defaultCfg;
@ -192,7 +192,7 @@ const loadCRSession = () => {
const saveCRSession = (data: Record<string, unknown>) => {
const cfgFolder = path.dirname(sessCfgFile.cr);
try {
fs.ensureDirSync(cfgFolder);
fs.mkdirSync(cfgFolder, { recursive: true });
fs.writeFileSync(`${sessCfgFile.cr}.yml`, yaml.stringify(data));
} catch (e) {
console.error("Can't save session file to disk!");
@ -210,7 +210,7 @@ const loadCRToken = () => {
const saveCRToken = (data: Record<string, unknown>) => {
const cfgFolder = path.dirname(tokenFile.cr);
try {
fs.ensureDirSync(cfgFolder);
fs.mkdirSync(cfgFolder, { recursive: true });
fs.writeFileSync(`${tokenFile.cr}.yml`, yaml.stringify(data));
} catch (e) {
console.error("Can't save token file to disk!");
@ -228,7 +228,7 @@ const loadADNToken = () => {
const saveADNToken = (data: Record<string, unknown>) => {
const cfgFolder = path.dirname(tokenFile.adn);
try {
fs.ensureDirSync(cfgFolder);
fs.mkdirSync(cfgFolder, { recursive: true });
fs.writeFileSync(`${tokenFile.adn}.yml`, yaml.stringify(data));
} catch (e) {
console.error("Can't save token file to disk!");
@ -251,7 +251,7 @@ const loadHDSession = () => {
const saveHDSession = (data: Record<string, unknown>) => {
const cfgFolder = path.dirname(sessCfgFile.hd);
try {
fs.ensureDirSync(cfgFolder);
fs.mkdirSync(cfgFolder, { recursive: true });
fs.writeFileSync(`${sessCfgFile.hd}.yml`, yaml.stringify(data));
} catch (e) {
console.error("Can't save session file to disk!");
@ -269,7 +269,7 @@ const loadHDToken = () => {
const saveHDToken = (data: Record<string, unknown>) => {
const cfgFolder = path.dirname(tokenFile.hd);
try {
fs.ensureDirSync(cfgFolder);
fs.mkdirSync(cfgFolder, { recursive: true });
fs.writeFileSync(`${tokenFile.hd}.yml`, yaml.stringify(data));
} catch (e) {
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 cfgFolder = path.dirname(hdPflCfgFile);
try {
fs.ensureDirSync(cfgFolder);
fs.mkdirSync(cfgFolder, { recursive: true });
fs.writeFileSync(`${hdPflCfgFile}.yml`, yaml.stringify(data));
} catch (e) {
console.error("Can't save profile file to disk!");
@ -318,7 +318,7 @@ const loadNewHDToken = () => {
const saveNewHDToken = (data: Record<string, unknown>) => {
const cfgFolder = path.dirname(tokenFile.hdNew);
try {
fs.ensureDirSync(cfgFolder);
fs.mkdirSync(cfgFolder, { recursive: true });
fs.writeFileSync(`${tokenFile.hdNew}.yml`, yaml.stringify(data));
} catch (e) {
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 path from 'path';
import { UpdateFile } from '../@types/updateFile';
import packageJson from '../package.json';
import { CompilerOptions, transpileModule } from 'typescript';
import tsConfig from '../tsconfig.json';
import fsextra from 'fs-extra';
import fs from 'fs';
import { workingDir } from './module.cfg-loader';
import { console } from './log';
import Helper from './module.helper';
@ -142,7 +141,7 @@ export default async (force = false) => {
changesToApply.forEach((a) => {
try {
fsextra.ensureDirSync(path.dirname(a.path));
fs.mkdirSync(path.dirname(a.path), { recursive: true });
fs.writeFileSync(path.join(__dirname, '..', a.path), a.content);
console.info('✓ Written %s', a.path);
} catch (er) {

View file

@ -42,19 +42,16 @@
"dependencies": {
"@bufbuild/protobuf": "^2.10.1",
"commander": "^14.0.2",
"cors": "^2.8.5",
"express": "^5.1.0",
"ffprobe": "^1.1.2",
"fs-extra": "^11.3.2",
"iso-639": "^0.2.2",
"leven": "^4.1.0",
"log4js": "^6.9.1",
"lookpath": "^1.2.3",
"m3u8-parsed": "^2.0.0",
"m3u8-parser": "^7.2.0",
"mpd-parser": "^1.3.1",
"node-playready": "^1.1.1",
"open": "^11.0.0",
"protobufjs": "^7.5.4",
"undici": "^7.16.0",
"widevine": "^1.0.3",
"ws": "^8.18.3",
@ -62,10 +59,9 @@
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.5",
"@types/ffprobe": "^1.1.8",
"@types/fs-extra": "^11.0.4",
"@types/m3u8-parser": "^7.2.5",
"@types/node": "^24.10.1",
"@types/ws": "^8.18.1",
"@typescript-eslint/eslint-plugin": "^8.48.0",
@ -88,7 +84,7 @@
"tsc": "ts-node tsc.ts",
"eslint": "npx eslint . --quiet",
"prettier": "npx prettier . --check",
"prettier-fix": "npx prettier . --write",
"prettier:fix": "npx prettier . --write",
"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",
"prebuild-cli": "pnpm run tsc false false",

View file

@ -14,18 +14,12 @@ importers:
commander:
specifier: ^14.0.2
version: 14.0.2
cors:
specifier: ^2.8.5
version: 2.8.5
express:
specifier: ^5.1.0
version: 5.1.0
ffprobe:
specifier: ^1.1.2
version: 1.1.2
fs-extra:
specifier: ^11.3.2
version: 11.3.2
iso-639:
specifier: ^0.2.2
version: 0.2.2
@ -38,9 +32,9 @@ importers:
lookpath:
specifier: ^1.2.3
version: 1.2.3
m3u8-parsed:
specifier: ^2.0.0
version: 2.0.0
m3u8-parser:
specifier: ^7.2.0
version: 7.2.0
mpd-parser:
specifier: ^1.3.1
version: 1.3.1
@ -50,9 +44,6 @@ importers:
open:
specifier: ^11.0.0
version: 11.0.0
protobufjs:
specifier: ^7.5.4
version: 7.5.4
undici:
specifier: ^7.16.0
version: 7.16.0
@ -69,18 +60,15 @@ importers:
'@eslint/js':
specifier: ^9.39.1
version: 9.39.1
'@types/cors':
specifier: ^2.8.19
version: 2.8.19
'@types/express':
specifier: ^5.0.5
version: 5.0.5
'@types/ffprobe':
specifier: ^1.1.8
version: 1.1.8
'@types/fs-extra':
specifier: ^11.0.4
version: 11.0.4
'@types/m3u8-parser':
specifier: ^7.2.5
version: 7.2.5
'@types/node':
specifier: ^24.10.1
version: 24.10.1
@ -393,36 +381,6 @@ packages:
resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==}
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':
resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==}
@ -441,9 +399,6 @@ packages:
'@types/connect@3.4.38':
resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
'@types/cors@2.8.19':
resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==}
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
@ -456,17 +411,14 @@ packages:
'@types/ffprobe@1.1.8':
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':
resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==}
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
'@types/jsonfile@6.1.4':
resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==}
'@types/m3u8-parser@7.2.5':
resolution: {integrity: sha512-e4aoWGzTV+tQRGN1CfJhp1jt5yIHW0X0Lt2rjflDh/cTXYY9nDLRehX7S5iu+gyoaqVLDletBtPcDcPcQBKaeg==}
'@types/mime@1.3.5':
resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==}
@ -753,10 +705,6 @@ packages:
core-util-is@1.0.3:
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:
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
@ -1190,17 +1138,11 @@ packages:
resolution: {integrity: sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==}
engines: {node: '>=8.0'}
long@5.3.2:
resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==}
lookpath@1.2.3:
resolution: {integrity: sha512-kthRVhf4kH4+HW3anM4UBHxsw/XFESf13euCEldhXr6GpBdmBoa7rDd7WO5G0Mhd4G5XtKTcEy8OR0iRZXpS3Q==}
engines: {npm: '>=6.13.4'}
hasBin: true
m3u8-parsed@2.0.0:
resolution: {integrity: sha512-41xr3I4eI8/gRULPJhijTHigeGvwMVwschrh9ueOW7/XlgWldVgarJG1fkDa1mjy4Z+KIeT38LYt/zOYpmW/GA==}
m3u8-parser@7.2.0:
resolution: {integrity: sha512-CRatFqpjVtMiMaKXxNvuI3I++vUumIXVVT/JpCpdU/FynV/ceVw1qpPyyBNindL+JlPMSesx+WX1QJaZEJSaMQ==}
@ -1301,10 +1243,6 @@ packages:
node-playready@1.1.1:
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:
resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
engines: {node: '>= 0.4'}
@ -1394,10 +1332,6 @@ packages:
resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
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:
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
engines: {node: '>= 0.10'}
@ -1943,29 +1877,6 @@ snapshots:
'@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/node12@1.0.11': {}
@ -1983,10 +1894,6 @@ snapshots:
dependencies:
'@types/node': 24.10.1
'@types/cors@2.8.19':
dependencies:
'@types/node': 24.10.1
'@types/estree@1.0.8': {}
'@types/express-serve-static-core@5.1.0':
@ -2004,18 +1911,11 @@ snapshots:
'@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/json-schema@7.0.15': {}
'@types/jsonfile@6.1.4':
dependencies:
'@types/node': 24.10.1
'@types/m3u8-parser@7.2.5': {}
'@types/mime@1.3.5': {}
@ -2366,11 +2266,6 @@ snapshots:
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: {}
cross-spawn@7.0.6:
@ -2836,14 +2731,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
long@5.3.2: {}
lookpath@1.2.3: {}
m3u8-parsed@2.0.0:
dependencies:
m3u8-parser: 7.2.0
m3u8-parser@7.2.0:
dependencies:
'@babel/runtime': 7.28.4
@ -2927,8 +2816,6 @@ snapshots:
'@noble/curves': 2.0.1
fast-xml-parser: 5.3.2
object-assign@4.1.1: {}
object-inspect@1.13.4: {}
on-finished@2.4.1:
@ -3012,21 +2899,6 @@ snapshots:
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:
dependencies:
forwarded: 0.2.0

12
tsc.ts
View file

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