diff --git a/@types/m3u8-parsed.d.ts b/@types/m3u8-parsed.d.ts deleted file mode 100644 index 2c7c616..0000000 --- a/@types/m3u8-parsed.d.ts +++ /dev/null @@ -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; -} diff --git a/adn.ts b/adn.ts index 7adb898..d9c4b97 100644 --- a/adn.ts +++ b/adn.ts @@ -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> = {}, 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})`; diff --git a/crunchy.ts b/crunchy.ts index 632e9fb..76e78f7 100644 --- a/crunchy.ts +++ b/crunchy.ts @@ -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> = {}, 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})`; diff --git a/gui/server/index.ts b/gui/server/index.ts index 34e484d..909d22c 100644 --- a/gui/server/index.ts +++ b/gui/server/index.ts @@ -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`); diff --git a/hidive.ts b/hidive.ts index 3d66844..da588ca 100644 --- a/hidive.ts +++ b/hidive.ts @@ -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`); diff --git a/modules/build.ts b/modules/build.ts index 90fdb99..7e9624f 100644 --- a/modules/build.ts +++ b/modules/build.ts @@ -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 diff --git a/modules/hls-download.ts b/modules/hls-download.ts index 6d02adc..eab0a40 100644 --- a/modules/hls-download.ts +++ b/modules/hls-download.ts @@ -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; output?: string; threads?: number; retries?: number; @@ -52,7 +53,7 @@ type Data = { total: number; completed: number; }; - m3u8json: M3U8Json; + m3u8json: M3U8Json | Partial; 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); diff --git a/modules/log.ts b/modules/log.ts index b9214d0..c711f09 100644 --- a/modules/log.ts +++ b/modules/log.ts @@ -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`)); diff --git a/modules/module.cfg-loader.ts b/modules/module.cfg-loader.ts index e0af795..9cd096f 100644 --- a/modules/module.cfg-loader.ts +++ b/modules/module.cfg-loader.ts @@ -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 = (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) => { 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) => { 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) => { 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) => { 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) => { 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) => { const saveHDProfile = (data: Record) => { 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) => { 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!"); diff --git a/modules/module.updater.ts b/modules/module.updater.ts index 8d6b87b..ea2a82a 100644 --- a/modules/module.updater.ts +++ b/modules/module.updater.ts @@ -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) { diff --git a/package.json b/package.json index bb84d95..6da8eff 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c84af9a..a7058e4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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 diff --git a/tsc.ts b/tsc.ts index 0669a41..fe0b433 100644 --- a/tsc.ts +++ b/tsc.ts @@ -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 }); } });