Compare commits

...

36 commits

Author SHA1 Message Date
Stratuma
15067b19a4
Merge pull request #1181 from ektatas/fix-useless-first-comma
Some checks are pending
auto-documentation / documentation (push) Waiting to run
build and push docker image / build-node (push) Waiting to run
Style and build test / tsc (push) Waiting to run
Style and build test / eslint (push) Blocked by required conditions
Style and build test / prettier (push) Blocked by required conditions
Style and build test / build-test-windows-arm64 (push) Blocked by required conditions
Style and build test / build-test-linux-arm64 (push) Blocked by required conditions
Style and build test / build-test-macos-arm64 (push) Blocked by required conditions
Style and build test / build-test-windows-x64 (push) Blocked by required conditions
Style and build test / build-test-linux-x64 (push) Blocked by required conditions
Style and build test / build-test-macos-x64 (push) Blocked by required conditions
fix first comma when select all episodes
2026-01-11 01:09:42 +01:00
ektatas
3bb33819a7
fix first comma when select all episodes 2026-01-03 15:28:04 +01:00
stratumadev
51b4c173ab chore(crunchy): update basic auth token and user-agent
Some checks failed
auto-documentation / documentation (push) Has been cancelled
build and push docker image / build-node (push) Has been cancelled
Style and build test / tsc (push) Has been cancelled
Style and build test / eslint (push) Has been cancelled
Style and build test / prettier (push) Has been cancelled
Style and build test / build-test-windows-arm64 (push) Has been cancelled
Style and build test / build-test-linux-arm64 (push) Has been cancelled
Style and build test / build-test-macos-arm64 (push) Has been cancelled
Style and build test / build-test-windows-x64 (push) Has been cancelled
Style and build test / build-test-linux-x64 (push) Has been cancelled
Style and build test / build-test-macos-x64 (push) Has been cancelled
2025-12-21 20:20:55 +01:00
stratumadev
46f352af8c chore(main): update packages
Some checks failed
auto-documentation / documentation (push) Has been cancelled
build and push docker image / build-node (push) Has been cancelled
Style and build test / tsc (push) Has been cancelled
Style and build test / eslint (push) Has been cancelled
Style and build test / prettier (push) Has been cancelled
Style and build test / build-test-windows-arm64 (push) Has been cancelled
Style and build test / build-test-linux-arm64 (push) Has been cancelled
Style and build test / build-test-macos-arm64 (push) Has been cancelled
Style and build test / build-test-windows-x64 (push) Has been cancelled
Style and build test / build-test-linux-x64 (push) Has been cancelled
Style and build test / build-test-macos-x64 (push) Has been cancelled
2025-12-18 11:01:35 +01:00
stratumadev
c9aa27df23 chore(main): update packages
Some checks failed
auto-documentation / documentation (push) Has been cancelled
build and push docker image / build-node (push) Has been cancelled
Style and build test / tsc (push) Has been cancelled
Style and build test / eslint (push) Has been cancelled
Style and build test / prettier (push) Has been cancelled
Style and build test / build-test-windows-arm64 (push) Has been cancelled
Style and build test / build-test-linux-arm64 (push) Has been cancelled
Style and build test / build-test-macos-arm64 (push) Has been cancelled
Style and build test / build-test-windows-x64 (push) Has been cancelled
Style and build test / build-test-linux-x64 (push) Has been cancelled
Style and build test / build-test-macos-x64 (push) Has been cancelled
2025-12-07 12:00:26 +01:00
stratumadev
a42dafe608 chore(gui): update express
Some checks failed
auto-documentation / documentation (push) Has been cancelled
build and push docker image / build-node (push) Has been cancelled
Style and build test / tsc (push) Has been cancelled
Style and build test / eslint (push) Has been cancelled
Style and build test / prettier (push) Has been cancelled
Style and build test / build-test-windows-arm64 (push) Has been cancelled
Style and build test / build-test-linux-arm64 (push) Has been cancelled
Style and build test / build-test-macos-arm64 (push) Has been cancelled
Style and build test / build-test-windows-x64 (push) Has been cancelled
Style and build test / build-test-linux-x64 (push) Has been cancelled
Style and build test / build-test-macos-x64 (push) Has been cancelled
2025-12-02 01:49:47 +01:00
stratumadev
61cf5a59a6 chore(main): update express and yaml 2025-12-02 01:45:27 +01:00
stratumadev
e8bec44982 perf(merger): remove unused functions and imports
Some checks failed
auto-documentation / documentation (push) Has been cancelled
build and push docker image / build-node (push) Has been cancelled
Style and build test / tsc (push) Has been cancelled
Style and build test / eslint (push) Has been cancelled
Style and build test / prettier (push) Has been cancelled
Style and build test / build-test-windows-arm64 (push) Has been cancelled
Style and build test / build-test-linux-arm64 (push) Has been cancelled
Style and build test / build-test-macos-arm64 (push) Has been cancelled
Style and build test / build-test-windows-x64 (push) Has been cancelled
Style and build test / build-test-linux-x64 (push) Has been cancelled
Style and build test / build-test-macos-x64 (push) Has been cancelled
2025-11-30 03:16:06 +01:00
stratumadev
1ff93f2fbd refactor(merger): move from ffprobe to mediainfo
Removes the necessity of ffprobe completely.
Also removed the mention of ffprobe requirement from the readme.
2025-11-30 03:10:28 +01:00
stratumadev
e4afedfc9c chore(args): remove unwanted console.log 2025-11-30 01:52:40 +01:00
stratumadev
aa1180df48 fix(args): transformer also applies to default values 2025-11-30 01:51:00 +01:00
stratumadev
429bb2d690 fix(args): resolve issue with short flag parsing in overrideArguments function
Some checks are pending
auto-documentation / documentation (push) Waiting to run
build and push docker image / build-node (push) Waiting to run
Style and build test / tsc (push) Waiting to run
Style and build test / eslint (push) Blocked by required conditions
Style and build test / prettier (push) Blocked by required conditions
Style and build test / build-test-windows-arm64 (push) Blocked by required conditions
Style and build test / build-test-linux-arm64 (push) Blocked by required conditions
Style and build test / build-test-macos-arm64 (push) Blocked by required conditions
Style and build test / build-test-windows-x64 (push) Blocked by required conditions
Style and build test / build-test-linux-x64 (push) Blocked by required conditions
Style and build test / build-test-macos-x64 (push) Blocked by required conditions
2025-11-28 22:22:00 +01:00
stratumadev
c9ca51e6ef chore(main): remove unused/outdated files and move to typescript only
Some checks are pending
auto-documentation / documentation (push) Waiting to run
build and push docker image / build-node (push) Waiting to run
Style and build test / tsc (push) Waiting to run
Style and build test / eslint (push) Blocked by required conditions
Style and build test / prettier (push) Blocked by required conditions
Style and build test / build-test-windows-arm64 (push) Blocked by required conditions
Style and build test / build-test-linux-arm64 (push) Blocked by required conditions
Style and build test / build-test-macos-arm64 (push) Blocked by required conditions
Style and build test / build-test-windows-x64 (push) Blocked by required conditions
Style and build test / build-test-linux-x64 (push) Blocked by required conditions
Style and build test / build-test-macos-x64 (push) Blocked by required conditions
2025-11-28 13:49:27 +01:00
stratumadev
7d828a3d47 docs(main): bump version 2025-11-28 12:35:41 +00:00
stratumadev
0bb757f655 chore(main): bump version 2025-11-28 13:34:21 +01:00
stratumadev
1bf0af08e6 fix(args): correct parsing so string options don't treat the next option as a value 2025-11-28 13:30:55 +01:00
stratumadev
aed9169a69 Merge branch 'master' of https://github.com/anidl/multi-downloader-nx
Some checks are pending
auto-documentation / documentation (push) Waiting to run
build and push docker image / build-node (push) Waiting to run
Style and build test / tsc (push) Waiting to run
Style and build test / eslint (push) Blocked by required conditions
Style and build test / prettier (push) Blocked by required conditions
Style and build test / build-test-windows-arm64 (push) Blocked by required conditions
Style and build test / build-test-linux-arm64 (push) Blocked by required conditions
Style and build test / build-test-macos-arm64 (push) Blocked by required conditions
Style and build test / build-test-windows-x64 (push) Blocked by required conditions
Style and build test / build-test-linux-x64 (push) Blocked by required conditions
Style and build test / build-test-macos-x64 (push) Blocked by required conditions
2025-11-28 00:49:53 +01:00
stratumadev
422af6a46c chore(workflow): update commit message of auto-documentation 2025-11-28 00:49:47 +01:00
stratumadev
f22f73dbf1 chore(main): bump version + Documentation 2025-11-27 23:46:09 +00:00
stratumadev
749285d7d1 chore(main): bump version 2025-11-28 00:44:46 +01:00
stratumadev
8764e608dc feat(crunchy): add subtitleTimestampFix function (#1121)
Fix Crunchyroll subtitles with invalid durations.
If start > video length  the line is deleted.
If only end > video length  end is trimmed to video duration. + Documentation
2025-11-27 23:41:39 +00:00
stratumadev
5f192a31c0 feat(crunchy): add subtitleTimestampFix function (#1121)
Fix Crunchyroll subtitles with invalid durations.
If start > video length  the line is deleted.
If only end > video length  end is trimmed to video duration.
2025-11-28 00:40:11 +01:00
stratumadev
045a439b82 fix(m3u8): invalid m3u8-parser import 2025-11-27 22:32:52 +01:00
stratumadev
cd530295a8 chore: remove prettier & eslint commands from precommit test 2025-11-27 22:11:00 +01:00
stratumadev
289265652e chore: add husky for cleaner commits 2025-11-27 22:10:00 +01:00
stratumadev
88e06bbf6e chore: update dependencies 2025-11-27 21:51:07 +01:00
stratumadev
a70521ced7 increased default part download retries 2025-11-27 21:43:12 +01:00
stratumadev
37cac7c789 updated workflows
Some checks are pending
auto-documentation / documentation (push) Waiting to run
build and push docker image / build-node (push) Waiting to run
Style and build test / tsc (push) Waiting to run
Style and build test / eslint (push) Blocked by required conditions
Style and build test / prettier (push) Blocked by required conditions
Style and build test / build-test-windows-arm64 (push) Blocked by required conditions
Style and build test / build-test-linux-arm64 (push) Blocked by required conditions
Style and build test / build-test-macos-arm64 (push) Blocked by required conditions
Style and build test / build-test-windows-x64 (push) Blocked by required conditions
Style and build test / build-test-linux-x64 (push) Blocked by required conditions
Style and build test / build-test-macos-x64 (push) Blocked by required conditions
2025-11-27 15:08:30 +01:00
stratumadev
6f58f3474e Merge pull request #1146 from HyperNylium/master
Some checks are pending
auto-documentation / documentation (push) Waiting to run
build and push docker image / build-node (push) Waiting to run
Style and build test / tsc (push) Waiting to run
Style and build test / eslint (push) Blocked by required conditions
Style and build test / prettier (push) Blocked by required conditions
Style and build test / build-test-windows-arm64 (push) Blocked by required conditions
Style and build test / build-test-linux-arm64 (push) Blocked by required conditions
Style and build test / build-test-macos-arm64 (push) Blocked by required conditions
Style and build test / build-test-windows-x64 (push) Blocked by required conditions
Style and build test / build-test-linux-x64 (push) Blocked by required conditions
Style and build test / build-test-macos-x64 (push) Blocked by required conditions
Update get started guide to include new CDM requirements + Documentation
2025-11-27 10:44:55 +00:00
Stratuma
15e301a965
Merge pull request #1146 from HyperNylium/master
Update get started guide to include new CDM requirements
2025-11-27 11:44:40 +01:00
stratumadev
c4e2be1009 bump version 2025-11-27 11:43:24 +01:00
stratumadev
3d5142540b moved back to commander and fixed React error 2025-11-27 11:42:55 +01:00
HyperNylium
4cbcc24a75
Update GET-STARTED.md 2025-11-26 20:41:39 -05:00
HyperNylium
84965dcc85
Update GET-STARTED.md 2025-11-26 20:38:22 -05:00
HyperNylium
1716183678
Update GET-STARTED.md 2025-11-26 19:40:50 -05:00
HyperNylium
71563f5778
Update GET-STARTED.md 2025-11-26 19:09:48 -05:00
34 changed files with 1547 additions and 909 deletions

6
.commitlintrc.json Normal file
View file

@ -0,0 +1,6 @@
{
"extends": ["@commitlint/config-conventional"],
"rules": {
"type-enum": [2, "always", ["ci", "chore", "docs", "ticket", "feat", "fix", "perf", "refactor", "revert", "style"]]
}
}

View file

@ -4,12 +4,15 @@ on:
push:
branches: [master]
permissions:
contents: write
jobs:
documentation:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
ref: ${{ github.head_ref }}
- uses: pnpm/action-setup@v4
@ -23,4 +26,4 @@ jobs:
- run: pnpm run docs
- uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: ${{ github.event.head_commit.message }} + Documentation
commit_message: 'docs(main): bump version'

View file

@ -7,11 +7,14 @@ on:
branches: [master]
workflow_dispatch:
permissions:
contents: read
jobs:
build-node:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1

View file

@ -4,6 +4,9 @@ on:
release:
types: [published]
permissions:
contents: write
jobs:
build:
strategy:
@ -80,7 +83,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: latest
@ -110,7 +113,7 @@ jobs:
build-docker:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1

View file

@ -6,11 +6,14 @@ on:
pull_request:
branches: [master]
permissions:
contents: read
jobs:
tsc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: latest
@ -25,7 +28,7 @@ jobs:
needs: tsc
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: latest
@ -40,7 +43,7 @@ jobs:
needs: tsc
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: latest
@ -55,7 +58,7 @@ jobs:
needs: [eslint, prettier, tsc]
runs-on: windows-11-arm
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: latest
@ -70,7 +73,7 @@ jobs:
needs: [eslint, prettier, tsc]
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: latest
@ -85,7 +88,7 @@ jobs:
needs: [eslint, prettier, tsc]
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: latest
@ -100,7 +103,7 @@ jobs:
needs: [eslint, prettier, tsc]
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: latest
@ -115,7 +118,7 @@ jobs:
needs: [eslint, prettier, tsc]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: latest
@ -130,7 +133,7 @@ jobs:
needs: [eslint, prettier, tsc]
runs-on: macos-15-intel
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v2
with:
version: latest

1
.husky/commit-msg Normal file
View file

@ -0,0 +1 @@
npx commitlint --edit .git/COMMIT_EDITMSG

1
.husky/pre-commit Normal file
View file

@ -0,0 +1 @@
npx tsc

View file

@ -52,6 +52,7 @@ export type CrunchyDownloadOptions = {
scaledBorderAndShadowFix: boolean;
scaledBorderAndShadow: 'yes' | 'no';
originalScriptFix: boolean;
subtitleTimestampFix: boolean;
};
export type CrunchyMultiDownload = {
@ -88,6 +89,7 @@ export type CrunchyEpMeta = {
versions?: EpisodeVersion[] | null;
isSubbed: boolean;
isDubbed: boolean;
durationMs: number;
}[];
seriesTitle: string;
seasonTitle: string;

15
TODO.md
View file

@ -1,15 +0,0 @@
# Todo/Future Ideas list
- [ ] Look into implementing wvd file support
- [ ] Merge sync branch with latest master
- [ ] Finish implementing old algorithm
- [ ] Look into adding suggested algorithm [#599](https://github.com/anidl/multi-downloader-nx/issues/599)
- [ ] Remove Funimation
- [ ] Remove old hidive API or find a way to make it work
- [ ] Look into adding other services
- [ ] Refactor downloading code
- [ ] Allow audio and video download at the same time
- [ ] Reduce/Refactor the amount of duplicate/boilerplate code required
- [ ] Create a generic service class for the CLI with set inputs/outputs
- [ ] Modularize site modules to ease addition of new sites
- [ ] Create generic MPD/M3U8 playlist downloader

6
adn.ts
View file

@ -7,7 +7,7 @@ import fs from 'fs';
import crypto from 'crypto';
// Plugins
import m3u8 from 'm3u8-parser';
import { Parser } from 'm3u8-parser';
// Modules
import * as fontsData from './modules/module.fontsData';
@ -602,7 +602,7 @@ export default class AnimationDigitalNetwork implements ServiceClass {
const streamPlaylistBody = await streamPlaylistsReq.res.text();
// Init parser
const parser = new m3u8.Parser();
const parser = new Parser();
// Parse M3U8
parser.push(streamPlaylistBody);
@ -724,7 +724,7 @@ export default class AnimationDigitalNetwork implements ServiceClass {
const chunkPageBody = await chunkPage.res.text();
// Init parser
const parser = new m3u8.Parser();
const parser = new Parser();
// Parse M3U8
parser.push(chunkPageBody);

1
commitlint.config.ts Normal file
View file

@ -0,0 +1 @@
export default { extends: ['@commitlint/config-conventional'] };

View file

@ -1,5 +1,4 @@
ffmpeg: 'ffmpeg.exe'
mkvmerge: 'mkvmerge.exe'
ffprobe: 'ffprobe.exe'
mp4decrypt: 'mp4decrypt.exe'
shaka: 'shaka-packager.exe'

View file

@ -9,7 +9,7 @@ import packageJson from './package.json';
import { console } from './modules/log';
import streamdl, { M3U8Json } from './modules/hls-download';
import Helper from './modules/module.helper';
import m3u8 from 'm3u8-parser';
import { Parser } from 'm3u8-parser';
// custom modules
import * as fontsData from './modules/module.fontsData';
@ -105,7 +105,7 @@ export default class Crunchy implements ServiceClass {
await this.getCmsData();
} else if (argv.new) {
await this.refreshToken();
await this.getNewlyAdded(argv.page, argv['search-type'], argv.raw, argv.rawoutput);
await this.getNewlyAdded(argv.page, argv.searchType, argv.raw, argv.rawoutput);
} else if (argv.search && argv.search.length > 2) {
await this.refreshToken();
await this.doSearch({ ...argv, search: argv.search as string });
@ -122,16 +122,16 @@ export default class Crunchy implements ServiceClass {
}
}
return true;
} else if (argv['movie-listing'] && argv['movie-listing'].match(/^[0-9A-Z]{9,}$/)) {
} else if (argv.movieListing && argv.movieListing.match(/^[0-9A-Z]{9,}$/)) {
await this.refreshToken();
await this.logMovieListingById(argv['movie-listing'] as string);
} else if (argv['show-raw'] && argv['show-raw'].match(/^[0-9A-Z]{9,}$/)) {
await this.logMovieListingById(argv.movieListing as string);
} else if (argv.showRaw && argv.showRaw.match(/^[0-9A-Z]{9,}$/)) {
await this.refreshToken();
await this.logShowRawById(argv['show-raw'] as string);
} else if (argv['season-raw'] && argv['season-raw'].match(/^[0-9A-Z]{9,}$/)) {
await this.logShowRawById(argv.showRaw as string);
} else if (argv.seasonRaw && argv.seasonRaw.match(/^[0-9A-Z]{9,}$/)) {
await this.refreshToken();
await this.logSeasonRawById(argv['season-raw'] as string);
} else if (argv['show-list-raw']) {
await this.logSeasonRawById(argv.seasonRaw as string);
} else if (argv.showListRaw) {
await this.refreshToken();
await this.logShowListRaw();
} else if (argv.s && argv.s.match(/^[0-9A-Z]{9,}$/)) {
@ -1209,7 +1209,8 @@ export default class Crunchy implements ServiceClass {
versions: null,
lang: langsData.languages.find((a) => a.code == yargs.appArgv(this.cfg.cli).dubLang[0]),
isSubbed: item.is_subbed,
isDubbed: item.is_dubbed
isDubbed: item.is_dubbed,
durationMs: item.duration_ms ?? 0
}
],
seriesTitle: item.series_title,
@ -1451,7 +1452,8 @@ export default class Crunchy implements ServiceClass {
mediaId: 'E:' + item.id,
versions: item.episode_metadata.versions,
isSubbed: item.episode_metadata.is_subbed,
isDubbed: item.episode_metadata.is_dubbed
isDubbed: item.episode_metadata.is_dubbed,
durationMs: item.episode_metadata.duration_ms ?? 0
}
];
epMeta.seriesTitle = item.episode_metadata.series_title;
@ -1465,7 +1467,8 @@ export default class Crunchy implements ServiceClass {
{
mediaId: 'M:' + item.id,
isSubbed: item.movie_listing_metadata.is_subbed,
isDubbed: item.movie_listing_metadata.is_dubbed
isDubbed: item.movie_listing_metadata.is_dubbed,
durationMs: item.movie_listing_metadata.duration_ms ?? 0
}
];
epMeta.seriesTitle = item.title;
@ -1478,7 +1481,8 @@ export default class Crunchy implements ServiceClass {
{
mediaId: 'M:' + item.id,
isSubbed: item.movie_metadata.is_subbed,
isDubbed: item.movie_metadata.is_dubbed
isDubbed: item.movie_metadata.is_dubbed,
durationMs: item.movie_metadata.duration_ms ?? 0
}
];
epMeta.season = 0;
@ -1513,7 +1517,8 @@ export default class Crunchy implements ServiceClass {
{
mediaId: 'V:' + item.id,
isSubbed: false,
isDubbed: false
isDubbed: false,
durationMs: item.durationMs ?? 0
}
];
epMeta.season = 0;
@ -2076,16 +2081,22 @@ export default class Crunchy implements ServiceClass {
if (options.novids && options.noaudio) {
if (videoStream) {
await this.refreshToken(true, true);
await this.req.getData(`https://www.crunchyroll.com/playback/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${videoStream.token}`, {
...{ method: 'DELETE' },
...AuthHeaders
});
await this.req.getData(
`https://cr-play-service.prd.crunchyrollsvc.com/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${videoStream.token}`,
{
...{ method: 'DELETE' },
...AuthHeaders
}
);
}
if (audioStream && videoStream?.token !== audioStream.token) {
await this.req.getData(`https://www.crunchyroll.com/playback/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${audioStream.token}`, {
...{ method: 'DELETE' },
...AuthHeaders
});
await this.req.getData(
`https://cr-play-service.prd.crunchyrollsvc.com/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${audioStream.token}`,
{
...{ method: 'DELETE' },
...AuthHeaders
}
);
}
}
@ -2208,13 +2219,13 @@ export default class Crunchy implements ServiceClass {
await this.refreshToken(true, true);
if (videoStream) {
await this.req.getData(
`https://www.crunchyroll.com/playback/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${videoStream.token}/keepAlive?playhead=1`,
`https://cr-play-service.prd.crunchyrollsvc.com/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${videoStream.token}/keepAlive?playhead=1`,
{ ...{ method: 'PATCH' }, ...AuthHeaders }
);
}
if (audioStream && videoStream?.token !== audioStream.token) {
await this.req.getData(
`https://www.crunchyroll.com/playback/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${audioStream.token}/keepAlive?playhead=1`,
`https://cr-play-service.prd.crunchyrollsvc.com/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${audioStream.token}/keepAlive?playhead=1`,
{ ...{ method: 'PATCH' }, ...AuthHeaders }
);
}
@ -2286,16 +2297,22 @@ export default class Crunchy implements ServiceClass {
if (videoStream) {
await this.refreshToken(true, true);
await this.req.getData(`https://www.crunchyroll.com/playback/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${videoStream.token}`, {
...{ method: 'DELETE' },
...AuthHeaders
});
await this.req.getData(
`https://cr-play-service.prd.crunchyrollsvc.com/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${videoStream.token}`,
{
...{ method: 'DELETE' },
...AuthHeaders
}
);
}
if (audioStream && videoStream?.token !== audioStream.token) {
await this.req.getData(`https://www.crunchyroll.com/playback/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${audioStream.token}`, {
...{ method: 'DELETE' },
...AuthHeaders
});
await this.req.getData(
`https://cr-play-service.prd.crunchyrollsvc.com/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${audioStream.token}`,
{
...{ method: 'DELETE' },
...AuthHeaders
}
);
}
let [audioDownloaded, videoDownloaded] = [false, false];
@ -2496,7 +2513,7 @@ export default class Crunchy implements ServiceClass {
}
} else if (!options.novids) {
// Init parser
const parser = new m3u8.Parser();
const parser = new Parser();
// Parse M3U8
parser.push(vstreamPlaylistBody);
@ -2625,20 +2642,20 @@ export default class Crunchy implements ServiceClass {
if (videoStream) {
await this.refreshToken(true, true);
await this.req.getData(
`https://www.crunchyroll.com/playback/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${videoStream.token}`,
`https://cr-play-service.prd.crunchyrollsvc.com/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${videoStream.token}`,
{ ...{ method: 'DELETE' }, ...AuthHeaders }
);
}
if (audioStream && videoStream?.token !== audioStream.token) {
await this.req.getData(
`https://www.crunchyroll.com/playback/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${audioStream.token}`,
`https://cr-play-service.prd.crunchyrollsvc.com/v1/token/${currentVersion ? currentVersion.guid : currentMediaId}/${audioStream.token}`,
{ ...{ method: 'DELETE' }, ...AuthHeaders }
);
}
const chunkPageBody = await chunkPage.res.text();
// Init parser
const parser = new m3u8.Parser();
const parser = new Parser();
// Parse M3U8
parser.push(chunkPageBody);
@ -2802,18 +2819,20 @@ export default class Crunchy implements ServiceClass {
});
if (subsAssReq.ok && subsAssReq.res) {
let sBody = await subsAssReq.res.text();
if (subsItem.format == 'vtt') {
if (subsItem.format === 'vtt') {
if (!options.noASSConv) {
const chosenFontSize = options.originalFontSize ? undefined : options.fontSize;
if (!options.originalFontSize) sBody = sBody.replace(/( font-size:.+?;)/g, '').replace(/(font-size:.+?;)/g, '');
sBody = vtt2ass(undefined, chosenFontSize, sBody, '', undefined, options.fontName);
sxData.fonts = fontsData.assFonts(sBody) as Font[];
sxData.file = sxData.file.replace('.vtt', '.ass');
} else {
// Yeah, whatever
sxData.fonts = [];
}
} else {
}
if (!options.noASSConv || subsItem.format !== 'vtt') {
// Extract PlayRes
const mX = sBody.match(/^PlayResX:\s*(\d+)/m);
const mY = sBody.match(/^PlayResY:\s*(\d+)/m);
@ -2956,6 +2975,45 @@ export default class Crunchy implements ServiceClass {
// Remove YCbCr
sBody = sBody.replace(/^[ \t]*YCbCr Matrix:\s*.*\r?\n?/m, '');
// Make sure no Dialogue timestamp goes over video length
if (options.subtitleTimestampFix && mMeta?.durationMs && mMeta.durationMs > 15000) {
const lines = sBody.split('\n');
const newLines: string[] = [];
const durationS = mMeta.durationMs / 1000;
const toSec = (t: string) => {
const [h, m, s] = t.replace(',', '.').split(/[:.]/).map(Number);
return h * 3600 + m * 60 + s;
};
for (let line of lines) {
if (line.startsWith('Dialogue:')) {
const parts = line.split(',');
const start = parts[1];
const end = parts[2];
const s = toSec(start);
const e = toSec(end);
// If start time is longer than durationS skip the subtitle line completely
if (s > durationS) continue;
// If only end time is longer than durationS short it down
if (e > durationS) {
const h = String(Math.floor(durationS / 3600));
const m = String(Math.floor((durationS % 3600) / 60)).padStart(2, '0');
const sec = (durationS % 60).toFixed(2).padStart(5, '0');
parts[2] = `${h}:${m}:${sec}`;
line = parts.join(',');
}
}
newLines.push(line);
}
sBody = newLines.join('\n');
}
// Force outline thickness for ru-RU: if the 17th field (Outline) equals 2.6 → 2
if (langItem.cr_locale === 'ru-RU') {
sBody = sBody.replace(/^[ \t]*(Style:\s*[^,\n]*(?:,[^,\n]*){15}),\s*2(?:[.,]6(?:0+)?)?(\s*,)/gm, '$1,2$2');
@ -3307,7 +3365,8 @@ export default class Crunchy implements ServiceClass {
mediaId: item.id,
versions: item.versions,
isSubbed: item.is_subbed,
isDubbed: item.is_dubbed
isDubbed: item.is_dubbed,
durationMs: item.duration_ms ?? 0
}
],
seriesTitle: itemE.items.find((a) => !a.series_title.match(/\(\w+ Dub\)/))?.series_title ?? itemE.items[0].series_title.replace(/\(\w+ Dub\)/g, '').trimEnd(),

23
dev.js
View file

@ -1,23 +0,0 @@
const { exec } = require('child_process');
const path = require('path');
const toRun = process.argv.slice(2).join(' ').split('---');
const waitForProcess = async (proc) => {
return new Promise((resolve, reject) => {
proc.stdout?.on('data', (data) => process.stdout.write(data));
proc.stderr?.on('data', (data) => process.stderr.write(data));
proc.on('close', resolve);
proc.on('error', reject);
});
};
(async () => {
await waitForProcess(exec('pnpm run tsc test false'));
for (let command of toRun) {
await waitForProcess(
exec(`node index.js --service hidive ${command}`, {
cwd: path.join(__dirname, 'lib')
})
);
}
})();

View file

@ -1,200 +0,0 @@
## Change Log
This changelog is out of date and wont be continued. Please see the releases comments, or if not present the commit comments.
### 4.7.0 (unreleased)
- Change subtitles parser from ttml to vtt
- Improve help command
- Update modules
#### Known issues:
- Proxy not supported
### 4.6.1 (2020/09/19)
- Update modules
#### Known issues:
- Proxy not supported
### 4.6.0 (2020/06/03)
- Bug fixes and improvements
#### Known issues:
- Proxy not supported
### 4.5.1 (2020/03/10)
- Better binary files handling
- Binary build for windows
#### Known issues:
- Proxy not supported
### 4.5.0 (2020/01/21)
- Resume downloading
#### Known issues:
- Proxy not supported
### 4.4.2 (2019/07/21)
- Better proxy handling for stream download
### 4.4.1 (2019/07/21)
- Fixed proxy for stream download
### 4.4.0 (2019/06/04)
- Added `--novids` option (Thanks to @subdiox)
- Update modules
### 4.3.2 (2019/05/09)
- Code improvements
- Fix `hls-download` error printing
### 4.3.1 (2019/05/09)
- Fix auto detection max quality (Regression in d7d280c)
### 4.3.0 (2019/05/09)
- Better server selection (Closes #42)
### 4.2.1 (2019/05/04)
- Filter duplicate urls for cloudfront.net (Closes #40)
### 4.2.0 (2019/05/02)
- Replace `request` module with `got`
- Changed proxy cli options
- Changed `login` option name to `auth`
- Changed `hls-download` parallel download configuration from 5 parts to 10
- Update modules
### 4.1.0 (2019/04/05)
- CLI options for login moved to CUI
- Removed showing set token at startup
### 4.0.5 (2019/02/09)
- Fix downloading shows with autoselect max quality
### 4.0.4 (2019/01/26)
- Fix search when shows not found
- Update modules
### 4.0.3 (2018/12/06)
- Select only non-encrypted (HLS) streams, encrypted streams is MPEG-DASH
### 4.0.2 (2018/11/25)
- Fix typos and update modules
### 4.0.1 (2018/11/23)
- Code refactoring and small fixes
### 4.0.0 RC 1 (2018/11/17)
- Select range of episodes using hyphen-sequence
- Skip muxing if executables not found
- Fixed typos and duplicate options
### 4.0.0 Beta 2 (2018/11/12)
- Select alternative server
- Updated readme
### 4.0.0 Beta 1 (2018/11/10)
- Rearrange folders structure
- Configuration changed to yaml format
- Muxing changed to MKV by default
- tsMuxeR+mp4box replaced with FFMPEG
- Updated commands help and readme
- Fixed typos and duplicate options
- `ttml2srt` moved to separate module
- Drop `m3u8-stream-list` module
- Code improvements
### 3.2.8 (2018/06/16)
- Fix video request when token not specified
### 3.2.7 (2018/06/15)
- Update modules
### 3.2.6 (2018/02/18)
- Fix commands help
### 3.2.5 (2018/02/12)
- Fixes and update modules
### 3.2.4 (2018/02/01)
- Update modules
### 3.2.3 (2018/01/31)
- Rearrange folders structure
### 3.2.2 (2018/01/16)
- Update modules
### 3.2.1 (2018/01/16)
- Update modules
- Small fixes
### 3.2.0 (2018/01/16)
- `hls-download` module moved to independent module
- Auth for socks proxy
### 3.1.0 (2017/12/30)
- Convert DXFP (TTML) subtitles to SRT format
### 3.0.1 (2017/12/05)
- Check subtitles availability
- Download subtitles in SRT format instead of VTT
- Extended hls download progress info
### 3.0.0 Beta 3 (2017/12/03)
- Restored MKV and MP4 muxing
- Convert VTT subtitles to SRT format
### 3.0.0 Beta 2 (2017/10/18)
- Fix video downloading
### 3.0.0 Beta 1 (2017/10/17)
- Major code changes and improvements
- Drop Streamlink and added own module for hls download
### 2.5.0 (2017/09/04)
- `nosubs` option
- Request video with app api
### 2.4.1 (2017/09/02)
- Fixed typo in package.json
- Fix #11: URL for getting video stream url was changed
### 2.4.0 (2017/07/04)
- IPv4 Socks5 proxy support
### 2.3.3 (2017/06/19)
- Removed forgotten debug code
### 2.3.2 (2017/06/19)
- Fix #5: Script fails to multiplex unique file names
### 2.3.1 (2017/04/29)
- Code improvements
### 2.3.0 (2017/04/27)
- Code improvements
### 2.2.5 (2017/04/17)
- Minor code improvements and fixes
### 2.1.4 (2017/04/10)
- Minor changes
### 2.1.3 (2017/04/10)
- Minor changes and fixes
### 2.1.2 (2017/04/10)
- Fix config path
### 2.1.1 (2017/04/10)
- Minor text changes
- Fix config
- Minor changes
### 2.1.0 (2017/04/10)
- First stable release
### 2.0.0 Beta (lost in time)
- First public release

View file

@ -1,4 +1,4 @@
# multi-downloader-nx (v5.6.6)
# multi-downloader-nx (v5.6.9)
If you find any bugs in this documentation or in the program itself please report it [over on GitHub](https://github.com/anidl/multi-downloader-nx/issues).
@ -275,6 +275,12 @@ Select if ScaledBorderAndShadow should be set to "yes" or "no".
| Crunchyroll | `--originalScriptFix ` | `boolean` | `No`| `NaN` | `true`| `originalScriptFix: ` |
Removes the URL in the Original Script line of the ASS subtitles, it prevents from bricking the subs in VLC (Fonts not loading when url not returning 200).
#### `--subtitleTimestampFix`
| **Service** | **Usage** | **Type** | **Required** | **Alias** | **Default** |**cli-default Entry**
| --- | --- | --- | --- | --- | --- | ---|
| Crunchyroll | `--subtitleTimestampFix ` | `boolean` | `No`| `NaN` | `false`| `subtitleTimestampFix: ` |
Fixes subtitle dialogues that go over the video length (deletes dialogues where start is over video length and updates the end timestamp when end is over video length).
#### `--novids`
| **Service** | **Usage** | **Type** | **Required** | **Alias** | **cli-default Entry**
| --- | --- | --- | --- | --- | ---|

View file

@ -126,7 +126,6 @@ C:.
12. Great! Now we have all dependencies installed and available in our PATH. To confirm that everything is working, open a new Command Prompt window and run the following commands:
```
ffmpeg
ffprobe
mkvmerge
mp4decrypt (or shaka-packager's .exe name, if you chose that instead)
```
@ -141,27 +140,32 @@ You have now completed the dependencies installation!
### Widevine
When you dump your CDM key, you will usually get 2 files. One ending in `.bin` and the other in `.pem`. \
All you need to do is place both files in the `widevine` folder, which is in the same directory you opened `aniDL.exe` from. \
It will detect what each file is based on the file contents.
If you do want to name them though (optional):
- The `.bin` file should be named `device_client_id_blob.bin` or `client_id.bin`
- The `.pem` file should be named `device_private_key.pem` or `private_key.pem`
Again, the renaming is totally optional. Just make sure both files are in the `widevine` folder.
If you have a Widevine CDM key dump, its either going to be a single `.wvd` file or a pair of `.bin` and `.pem` files. \
In any case, multi-downloader-nx supports both formats. Place them in the `widevine` folder and you are good to go.
### Playready
If you have a Playready CDM key dump, you just need to make sure:
1. Its provisioned as a V3 Device by [pyplayready](https://github.com/ready-dl/pyplayready).
2. Security level is either SL2000 or SL3000
3. Make sure you are using the latest version of shaka-packager from Stratuma, as he has patched it to work with multi-downloader-nx.\
1. Security level is either SL2000 or SL3000
2. Make sure you are using the latest version of shaka-packager from Stratuma, as he has patched it to work with multi-downloader-nx.\
You can find his releases [here](https://github.com/stratumadev/shaka-packager/releases/latest)
> [!NOTE]
> As of version v5.6.5, multi-downloader-nx now needs the CDM blobs in the form of `bgroupcert.dat` and `zgpriv.dat` files instead of the `.prd` device file. \
> If you have a `.prd` file as your CDM, multi-downloader-nx will convert that to the required blobs on the fly. No action required ;)
File type does not matter, as multi-downloader-nx supports both `.prd` device files and the `bgroupcert.dat` and `zgpriv.dat` blobs. \
`.prd` files can be placed into the `playready` folder with whatever name it has.
But if you are using the 2 `.dat` blob files, you need to rename them like so:
- `.dat` file that is 1.xx KiB -> `bgroupcert.dat`
- `.dat` file that is 32 bytes -> `zgpriv.dat`
Output form [mediainfo](https://mediaarea.net/en/MediaInfo) can help you identify which file is which.
```
bgroupcert.dat
1.26 KiB
zgpriv.dat
32.0 Bytes
```
Keep in mind that the `bgroupcert.dat` may not always be exactly 1.26 KiB but it should be in the KiB range, while the `zgpriv.dat` will always be 32 bytes.
## Installation

View file

@ -18,7 +18,6 @@ This application is not endorsed by or affiliated with *Crunchyroll*, *Hidive* o
By default this application uses the following paths to programs (main executables):
* `ffmpeg.exe` (Windows) or `ffmpeg` (other) (From PATH)
* `ffprobe.exe` (Windows) or `ffprobe` (other) (From PATH)
* `mkvmerge.exe` (Windows) or `mkvmerge` (other) (From PATH)
* `mp4decrypt.exe` (Windows) or `mp4decrypt` (other) (From PATH) (or shaka-packager)
* `shaka-packager.exe` (Windows) or `shaka-packager` (other) (From PATH) (or mp4decrypt)

View file

@ -33,7 +33,7 @@ export default tseslint.config(
}
},
{
ignores: ['**/lib', '**/videos', '**/build', 'dev.js', 'tsc.ts']
ignores: ['**/lib', '**/videos', '**/build', 'tsc.ts']
},
{
files: ['gui/react/**/*'],

View file

@ -21,7 +21,7 @@
"@babel/preset-react": "^7.28.5",
"@babel/preset-typescript": "^7.28.5",
"@types/node": "^24.10.1",
"@types/react": "^19.2.6",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"@types/uuid": "^10.0.0",
"babel-loader": "^10.0.0",

View file

@ -10,19 +10,19 @@ importers:
dependencies:
'@emotion/react':
specifier: ^11.14.0
version: 11.14.0(@types/react@19.2.6)(react@19.2.0)
version: 11.14.0(@types/react@19.2.7)(react@19.2.0)
'@emotion/styled':
specifier: ^11.14.1
version: 11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0)
version: 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0)
'@mui/icons-material':
specifier: ^7.3.5
version: 7.3.5(@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.6)(react@19.2.0)
version: 7.3.5(@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.7)(react@19.2.0)
'@mui/lab':
specifier: 7.0.0-beta.12
version: 7.0.0-beta.12(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
version: 7.0.0-beta.12(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@mui/material':
specifier: ^7.3.5
version: 7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
version: 7.3.5(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
notistack:
specifier: ^3.0.2
version: 3.0.2(csstype@3.2.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
@ -58,11 +58,11 @@ importers:
specifier: ^24.10.1
version: 24.10.1
'@types/react':
specifier: ^19.2.6
version: 19.2.6
specifier: ^19.2.7
version: 19.2.7
'@types/react-dom':
specifier: ^19.2.3
version: 19.2.3(@types/react@19.2.6)
version: 19.2.3(@types/react@19.2.7)
'@types/uuid':
specifier: ^10.0.0
version: 10.0.0
@ -978,8 +978,8 @@ packages:
peerDependencies:
'@types/react': '*'
'@types/react@19.2.6':
resolution: {integrity: sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w==}
'@types/react@19.2.7':
resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==}
'@types/retry@0.12.2':
resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==}
@ -1168,8 +1168,8 @@ packages:
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
baseline-browser-mapping@2.8.29:
resolution: {integrity: sha512-sXdt2elaVnhpDNRDz+1BDx1JQoJRuNk7oVlAlbGiFkLikHCAQiccexF/9e91zVi6RCgqspl04aP+6Cnl9zRLrA==}
baseline-browser-mapping@2.8.32:
resolution: {integrity: sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==}
hasBin: true
batch@0.6.1:
@ -1179,8 +1179,8 @@ packages:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
body-parser@1.20.3:
resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==}
body-parser@1.20.4:
resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
bonjour-service@1.3.0:
@ -1227,8 +1227,8 @@ packages:
camel-case@4.1.2:
resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==}
caniuse-lite@1.0.30001755:
resolution: {integrity: sha512-44V+Jm6ctPj7R52Na4TLi3Zri4dWUljJd+RDm+j8LtNCc/ihLCT+X1TzoOAkRETEWqjuLnh9581Tl80FvK7jVA==}
caniuse-lite@1.0.30001757:
resolution: {integrity: sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==}
chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
@ -1321,11 +1321,11 @@ packages:
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
cookie-signature@1.0.6:
resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
cookie-signature@1.0.7:
resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==}
cookie@0.7.1:
resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==}
cookie@0.7.2:
resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
engines: {node: '>= 0.6'}
core-js-compat@3.47.0:
@ -1453,8 +1453,8 @@ packages:
ee-first@1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
electron-to-chromium@1.5.255:
resolution: {integrity: sha512-Z9oIp4HrFF/cZkDPMpz2XSuVpc1THDpT4dlmATFlJUIBVCy9Vap5/rIXsASP1CscBacBqhabwh8vLctqBwEerQ==}
electron-to-chromium@1.5.262:
resolution: {integrity: sha512-NlAsMteRHek05jRUxUR0a5jpjYq9ykk6+kO0yRaMi5moe7u0fVIOeQ3Y30A8dIiWFBNUoQGi1ljb1i5VtS9WQQ==}
emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
@ -1474,8 +1474,8 @@ packages:
entities@2.2.0:
resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==}
envinfo@7.20.0:
resolution: {integrity: sha512-+zUomDcLXsVkQ37vUqWBvQwLaLlj8eZPSi61llaEFAVBY5mhcXdaSw1pSJVl4yTYD5g/gEfpNl28YYk4IPvrrg==}
envinfo@7.21.0:
resolution: {integrity: sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==}
engines: {node: '>=4'}
hasBin: true
@ -1539,8 +1539,8 @@ packages:
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
engines: {node: '>=0.8.x'}
express@4.21.2:
resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==}
express@4.22.1:
resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==}
engines: {node: '>= 0.10.0'}
fast-deep-equal@3.1.3:
@ -1561,8 +1561,8 @@ packages:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
finalhandler@1.3.1:
resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==}
finalhandler@1.3.2:
resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==}
engines: {node: '>= 0.8'}
find-root@1.1.0:
@ -1712,6 +1712,10 @@ packages:
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
engines: {node: '>= 0.8'}
http-errors@2.0.1:
resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
engines: {node: '>= 0.8'}
http-parser-js@0.5.10:
resolution: {integrity: sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==}
@ -1769,8 +1773,8 @@ packages:
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
engines: {node: '>= 0.10'}
ipaddr.js@2.2.0:
resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==}
ipaddr.js@2.3.0:
resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==}
engines: {node: '>= 10'}
is-arrayish@0.2.1:
@ -1912,8 +1916,8 @@ packages:
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
engines: {node: '>= 0.6'}
memfs@4.51.0:
resolution: {integrity: sha512-4zngfkVM/GpIhC8YazOsM6E8hoB33NP0BCESPOA6z7qaL6umPJNqkO8CNYaLV2FB2MV6H1O3x2luHHOSqppv+A==}
memfs@4.51.1:
resolution: {integrity: sha512-Eyt3XrufitN2ZL9c/uIRMyDwXanLI88h/L3MoWqNY747ha3dMR9dWqp8cRT5ntjZ0U1TNuq4U91ZXK0sMBjYOQ==}
merge-descriptors@1.0.3:
resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==}
@ -1941,9 +1945,9 @@ packages:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
mime-types@3.0.1:
resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==}
engines: {node: '>= 0.6'}
mime-types@3.0.2:
resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==}
engines: {node: '>=18'}
mime@1.6.0:
resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
@ -1985,8 +1989,8 @@ packages:
no-case@3.0.4:
resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
node-forge@1.3.1:
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
node-forge@1.3.2:
resolution: {integrity: sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw==}
engines: {node: '>= 6.13.0'}
node-releases@2.0.27:
@ -2135,8 +2139,8 @@ packages:
peerDependencies:
postcss: ^8.1.0
postcss-selector-parser@7.1.0:
resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==}
postcss-selector-parser@7.1.1:
resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==}
engines: {node: '>=4'}
postcss-value-parser@4.2.0:
@ -2159,8 +2163,8 @@ packages:
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
engines: {node: '>= 0.10'}
qs@6.13.0:
resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
qs@6.14.0:
resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
engines: {node: '>=0.6'}
randombytes@2.1.0:
@ -2170,8 +2174,8 @@ packages:
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
engines: {node: '>= 0.6'}
raw-body@2.5.2:
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
raw-body@2.5.3:
resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==}
engines: {node: '>= 0.8'}
react-dom@19.2.0:
@ -2314,6 +2318,10 @@ packages:
resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==}
engines: {node: '>= 0.8.0'}
send@0.19.1:
resolution: {integrity: sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg==}
engines: {node: '>= 0.8.0'}
serialize-javascript@6.0.2:
resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
@ -2400,6 +2408,10 @@ packages:
resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
engines: {node: '>= 0.8'}
statuses@2.0.2:
resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
engines: {node: '>= 0.8'}
string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
@ -3475,7 +3487,7 @@ snapshots:
'@emotion/memoize@0.9.0': {}
'@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0)':
'@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0)':
dependencies:
'@babel/runtime': 7.28.4
'@emotion/babel-plugin': 11.13.5
@ -3487,7 +3499,7 @@ snapshots:
hoist-non-react-statics: 3.3.2
react: 19.2.0
optionalDependencies:
'@types/react': 19.2.6
'@types/react': 19.2.7
transitivePeerDependencies:
- supports-color
@ -3501,18 +3513,18 @@ snapshots:
'@emotion/sheet@1.4.0': {}
'@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0)':
'@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0)':
dependencies:
'@babel/runtime': 7.28.4
'@emotion/babel-plugin': 11.13.5
'@emotion/is-prop-valid': 1.4.0
'@emotion/react': 11.14.0(@types/react@19.2.6)(react@19.2.0)
'@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.0)
'@emotion/serialize': 1.3.3
'@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.0)
'@emotion/utils': 1.4.2
react: 19.2.0
optionalDependencies:
'@types/react': 19.2.6
'@types/react': 19.2.7
transitivePeerDependencies:
- supports-color
@ -3595,39 +3607,39 @@ snapshots:
'@mui/core-downloads-tracker@7.3.5': {}
'@mui/icons-material@7.3.5(@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.6)(react@19.2.0)':
'@mui/icons-material@7.3.5(@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.7)(react@19.2.0)':
dependencies:
'@babel/runtime': 7.28.4
'@mui/material': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@mui/material': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
react: 19.2.0
optionalDependencies:
'@types/react': 19.2.6
'@types/react': 19.2.7
'@mui/lab@7.0.0-beta.12(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
'@mui/lab@7.0.0-beta.12(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
dependencies:
'@babel/runtime': 7.28.4
'@mui/material': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@mui/system': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0)
'@mui/types': 7.4.8(@types/react@19.2.6)
'@mui/utils': 7.3.5(@types/react@19.2.6)(react@19.2.0)
'@mui/material': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@mui/system': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0)
'@mui/types': 7.4.8(@types/react@19.2.7)
'@mui/utils': 7.3.5(@types/react@19.2.7)(react@19.2.0)
clsx: 2.1.1
prop-types: 15.8.1
react: 19.2.0
react-dom: 19.2.0(react@19.2.0)
optionalDependencies:
'@emotion/react': 11.14.0(@types/react@19.2.6)(react@19.2.0)
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0)
'@types/react': 19.2.6
'@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.0)
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0)
'@types/react': 19.2.7
'@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
'@mui/material@7.3.5(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
dependencies:
'@babel/runtime': 7.28.4
'@mui/core-downloads-tracker': 7.3.5
'@mui/system': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0)
'@mui/types': 7.4.8(@types/react@19.2.6)
'@mui/utils': 7.3.5(@types/react@19.2.6)(react@19.2.0)
'@mui/system': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0)
'@mui/types': 7.4.8(@types/react@19.2.7)
'@mui/utils': 7.3.5(@types/react@19.2.7)(react@19.2.0)
'@popperjs/core': 2.11.8
'@types/react-transition-group': 4.4.12(@types/react@19.2.6)
'@types/react-transition-group': 4.4.12(@types/react@19.2.7)
clsx: 2.1.1
csstype: 3.2.3
prop-types: 15.8.1
@ -3636,20 +3648,20 @@ snapshots:
react-is: 19.2.0
react-transition-group: 4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
optionalDependencies:
'@emotion/react': 11.14.0(@types/react@19.2.6)(react@19.2.0)
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0)
'@types/react': 19.2.6
'@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.0)
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0)
'@types/react': 19.2.7
'@mui/private-theming@7.3.5(@types/react@19.2.6)(react@19.2.0)':
'@mui/private-theming@7.3.5(@types/react@19.2.7)(react@19.2.0)':
dependencies:
'@babel/runtime': 7.28.4
'@mui/utils': 7.3.5(@types/react@19.2.6)(react@19.2.0)
'@mui/utils': 7.3.5(@types/react@19.2.7)(react@19.2.0)
prop-types: 15.8.1
react: 19.2.0
optionalDependencies:
'@types/react': 19.2.6
'@types/react': 19.2.7
'@mui/styled-engine@7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(react@19.2.0)':
'@mui/styled-engine@7.3.5(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(react@19.2.0)':
dependencies:
'@babel/runtime': 7.28.4
'@emotion/cache': 11.14.0
@ -3659,42 +3671,42 @@ snapshots:
prop-types: 15.8.1
react: 19.2.0
optionalDependencies:
'@emotion/react': 11.14.0(@types/react@19.2.6)(react@19.2.0)
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0)
'@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.0)
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0)
'@mui/system@7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0)':
'@mui/system@7.3.5(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0)':
dependencies:
'@babel/runtime': 7.28.4
'@mui/private-theming': 7.3.5(@types/react@19.2.6)(react@19.2.0)
'@mui/styled-engine': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0))(react@19.2.0)
'@mui/types': 7.4.8(@types/react@19.2.6)
'@mui/utils': 7.3.5(@types/react@19.2.6)(react@19.2.0)
'@mui/private-theming': 7.3.5(@types/react@19.2.7)(react@19.2.0)
'@mui/styled-engine': 7.3.5(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0))(react@19.2.0)
'@mui/types': 7.4.8(@types/react@19.2.7)
'@mui/utils': 7.3.5(@types/react@19.2.7)(react@19.2.0)
clsx: 2.1.1
csstype: 3.2.3
prop-types: 15.8.1
react: 19.2.0
optionalDependencies:
'@emotion/react': 11.14.0(@types/react@19.2.6)(react@19.2.0)
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.6)(react@19.2.0))(@types/react@19.2.6)(react@19.2.0)
'@types/react': 19.2.6
'@emotion/react': 11.14.0(@types/react@19.2.7)(react@19.2.0)
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.7)(react@19.2.0))(@types/react@19.2.7)(react@19.2.0)
'@types/react': 19.2.7
'@mui/types@7.4.8(@types/react@19.2.6)':
'@mui/types@7.4.8(@types/react@19.2.7)':
dependencies:
'@babel/runtime': 7.28.4
optionalDependencies:
'@types/react': 19.2.6
'@types/react': 19.2.7
'@mui/utils@7.3.5(@types/react@19.2.6)(react@19.2.0)':
'@mui/utils@7.3.5(@types/react@19.2.7)(react@19.2.0)':
dependencies:
'@babel/runtime': 7.28.4
'@mui/types': 7.4.8(@types/react@19.2.6)
'@mui/types': 7.4.8(@types/react@19.2.7)
'@types/prop-types': 15.7.15
clsx: 2.1.1
prop-types: 15.8.1
react: 19.2.0
react-is: 19.2.0
optionalDependencies:
'@types/react': 19.2.6
'@types/react': 19.2.7
'@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3':
optional: true
@ -3781,15 +3793,15 @@ snapshots:
'@types/range-parser@1.2.7': {}
'@types/react-dom@19.2.3(@types/react@19.2.6)':
'@types/react-dom@19.2.3(@types/react@19.2.7)':
dependencies:
'@types/react': 19.2.6
'@types/react': 19.2.7
'@types/react-transition-group@4.4.12(@types/react@19.2.6)':
'@types/react-transition-group@4.4.12(@types/react@19.2.7)':
dependencies:
'@types/react': 19.2.6
'@types/react': 19.2.7
'@types/react@19.2.6':
'@types/react@19.2.7':
dependencies:
csstype: 3.2.3
@ -4007,24 +4019,24 @@ snapshots:
balanced-match@1.0.2: {}
baseline-browser-mapping@2.8.29: {}
baseline-browser-mapping@2.8.32: {}
batch@0.6.1: {}
binary-extensions@2.3.0: {}
body-parser@1.20.3:
body-parser@1.20.4:
dependencies:
bytes: 3.1.2
content-type: 1.0.5
debug: 2.6.9
depd: 2.0.0
destroy: 1.2.0
http-errors: 2.0.0
http-errors: 2.0.1
iconv-lite: 0.4.24
on-finished: 2.4.1
qs: 6.13.0
raw-body: 2.5.2
qs: 6.14.0
raw-body: 2.5.3
type-is: 1.6.18
unpipe: 1.0.0
transitivePeerDependencies:
@ -4048,9 +4060,9 @@ snapshots:
browserslist@4.28.0:
dependencies:
baseline-browser-mapping: 2.8.29
caniuse-lite: 1.0.30001755
electron-to-chromium: 1.5.255
baseline-browser-mapping: 2.8.32
caniuse-lite: 1.0.30001757
electron-to-chromium: 1.5.262
node-releases: 2.0.27
update-browserslist-db: 1.1.4(browserslist@4.28.0)
@ -4079,7 +4091,7 @@ snapshots:
pascal-case: 3.1.2
tslib: 2.8.1
caniuse-lite@1.0.30001755: {}
caniuse-lite@1.0.30001757: {}
chalk@4.1.2:
dependencies:
@ -4175,9 +4187,9 @@ snapshots:
convert-source-map@2.0.0: {}
cookie-signature@1.0.6: {}
cookie-signature@1.0.7: {}
cookie@0.7.1: {}
cookie@0.7.2: {}
core-js-compat@3.47.0:
dependencies:
@ -4299,7 +4311,7 @@ snapshots:
ee-first@1.1.1: {}
electron-to-chromium@1.5.255: {}
electron-to-chromium@1.5.262: {}
emoji-regex@8.0.0: {}
@ -4314,7 +4326,7 @@ snapshots:
entities@2.2.0: {}
envinfo@7.20.0: {}
envinfo@7.21.0: {}
error-ex@1.3.4:
dependencies:
@ -4357,36 +4369,36 @@ snapshots:
events@3.3.0: {}
express@4.21.2:
express@4.22.1:
dependencies:
accepts: 1.3.8
array-flatten: 1.1.1
body-parser: 1.20.3
body-parser: 1.20.4
content-disposition: 0.5.4
content-type: 1.0.5
cookie: 0.7.1
cookie-signature: 1.0.6
cookie: 0.7.2
cookie-signature: 1.0.7
debug: 2.6.9
depd: 2.0.0
encodeurl: 2.0.0
escape-html: 1.0.3
etag: 1.8.1
finalhandler: 1.3.1
finalhandler: 1.3.2
fresh: 0.5.2
http-errors: 2.0.0
http-errors: 2.0.1
merge-descriptors: 1.0.3
methods: 1.1.2
on-finished: 2.4.1
parseurl: 1.3.3
path-to-regexp: 0.1.12
proxy-addr: 2.0.7
qs: 6.13.0
qs: 6.14.0
range-parser: 1.2.1
safe-buffer: 5.2.1
send: 0.19.0
send: 0.19.1
serve-static: 1.16.2
setprototypeof: 1.2.0
statuses: 2.0.1
statuses: 2.0.2
type-is: 1.6.18
utils-merge: 1.0.1
vary: 1.1.2
@ -4407,14 +4419,14 @@ snapshots:
dependencies:
to-regex-range: 5.0.1
finalhandler@1.3.1:
finalhandler@1.3.2:
dependencies:
debug: 2.6.9
encodeurl: 2.0.0
escape-html: 1.0.3
on-finished: 2.4.1
parseurl: 1.3.3
statuses: 2.0.1
statuses: 2.0.2
unpipe: 1.0.0
transitivePeerDependencies:
- supports-color
@ -4564,6 +4576,14 @@ snapshots:
statuses: 2.0.1
toidentifier: 1.0.1
http-errors@2.0.1:
dependencies:
depd: 2.0.0
inherits: 2.0.4
setprototypeof: 1.2.0
statuses: 2.0.2
toidentifier: 1.0.1
http-parser-js@0.5.10: {}
http-proxy-middleware@2.0.9(@types/express@4.17.25):
@ -4619,7 +4639,7 @@ snapshots:
ipaddr.js@1.9.1: {}
ipaddr.js@2.2.0: {}
ipaddr.js@2.3.0: {}
is-arrayish@0.2.1: {}
@ -4727,7 +4747,7 @@ snapshots:
media-typer@0.3.0: {}
memfs@4.51.0:
memfs@4.51.1:
dependencies:
'@jsonjoy.com/json-pack': 1.21.0(tslib@2.8.1)
'@jsonjoy.com/util': 1.9.0(tslib@2.8.1)
@ -4755,7 +4775,7 @@ snapshots:
dependencies:
mime-db: 1.52.0
mime-types@3.0.1:
mime-types@3.0.2:
dependencies:
mime-db: 1.54.0
@ -4789,7 +4809,7 @@ snapshots:
lower-case: 2.0.2
tslib: 2.8.1
node-forge@1.3.1: {}
node-forge@1.3.2: {}
node-releases@2.0.27: {}
@ -4908,20 +4928,20 @@ snapshots:
dependencies:
icss-utils: 5.1.0(postcss@8.5.6)
postcss: 8.5.6
postcss-selector-parser: 7.1.0
postcss-selector-parser: 7.1.1
postcss-value-parser: 4.2.0
postcss-modules-scope@3.2.1(postcss@8.5.6):
dependencies:
postcss: 8.5.6
postcss-selector-parser: 7.1.0
postcss-selector-parser: 7.1.1
postcss-modules-values@4.0.0(postcss@8.5.6):
dependencies:
icss-utils: 5.1.0(postcss@8.5.6)
postcss: 8.5.6
postcss-selector-parser@7.1.0:
postcss-selector-parser@7.1.1:
dependencies:
cssesc: 3.0.0
util-deprecate: 1.0.2
@ -4952,7 +4972,7 @@ snapshots:
forwarded: 0.2.0
ipaddr.js: 1.9.1
qs@6.13.0:
qs@6.14.0:
dependencies:
side-channel: 1.1.0
@ -4962,10 +4982,10 @@ snapshots:
range-parser@1.2.1: {}
raw-body@2.5.2:
raw-body@2.5.3:
dependencies:
bytes: 3.1.2
http-errors: 2.0.0
http-errors: 2.0.1
iconv-lite: 0.4.24
unpipe: 1.0.0
@ -5092,7 +5112,7 @@ snapshots:
selfsigned@2.4.1:
dependencies:
'@types/node-forge': 1.3.14
node-forge: 1.3.1
node-forge: 1.3.2
semver@5.7.2: {}
@ -5118,6 +5138,24 @@ snapshots:
transitivePeerDependencies:
- supports-color
send@0.19.1:
dependencies:
debug: 2.6.9
depd: 2.0.0
destroy: 1.2.0
encodeurl: 2.0.0
escape-html: 1.0.3
etag: 1.8.1
fresh: 0.5.2
http-errors: 2.0.0
mime: 1.6.0
ms: 2.1.3
on-finished: 2.4.1
range-parser: 1.2.1
statuses: 2.0.1
transitivePeerDependencies:
- supports-color
serialize-javascript@6.0.2:
dependencies:
randombytes: 2.1.0
@ -5231,6 +5269,8 @@ snapshots:
statuses@2.0.1: {}
statuses@2.0.2: {}
string-width@4.2.3:
dependencies:
emoji-regex: 8.0.0
@ -5381,7 +5421,7 @@ snapshots:
colorette: 2.0.20
commander: 12.1.0
cross-spawn: 7.0.6
envinfo: 7.20.0
envinfo: 7.21.0
fastest-levenshtein: 1.0.16
import-local: 3.2.0
interpret: 3.1.1
@ -5394,8 +5434,8 @@ snapshots:
webpack-dev-middleware@7.4.5(webpack@5.103.0):
dependencies:
colorette: 2.0.20
memfs: 4.51.0
mime-types: 3.0.1
memfs: 4.51.1
mime-types: 3.0.2
on-finished: 2.4.1
range-parser: 1.2.1
schema-utils: 4.3.3
@ -5418,10 +5458,10 @@ snapshots:
colorette: 2.0.20
compression: 1.8.1
connect-history-api-fallback: 2.0.0
express: 4.21.2
express: 4.22.1
graceful-fs: 4.2.11
http-proxy-middleware: 2.0.9(@types/express@4.17.25)
ipaddr.js: 2.2.0
ipaddr.js: 2.3.0
launch-editor: 2.12.0
open: 10.2.0
p-retry: 6.2.1

View file

@ -1,3 +1,4 @@
import React from 'react';
import { Box, ThemeProvider, createTheme, Theme } from '@mui/material';
const makeTheme = (mode: 'dark' | 'light'): Partial<Theme> => {

View file

@ -27,17 +27,15 @@ const EpisodeListing: React.FC = () => {
}, [store.episodeListing]);
const close = () => {
dispatch({
type: 'episodeListing',
payload: []
});
const mergedEpisodes = [...parseEpisodes(store.downloadOptions.e), ...selected];
dispatch({
type: 'downloadOptions',
payload: {
...store.downloadOptions,
e: `${[...new Set([...parseSelect(store.downloadOptions.e), ...selected])].join(',')}`
e: serializeEpisodes(mergedEpisodes)
}
});
dispatch({ type: 'episodeListing', payload: [] });
};
const getEpisodesForSeason = (season: string | 'all') => {
@ -168,6 +166,16 @@ const EpisodeListing: React.FC = () => {
</Dialog>
);
};
const parseEpisodes = (e: string): string[] => {
if (!e) return [];
return e
.split(',')
.map((s) => s.trim())
.filter((s) => s.length > 0);
};
const serializeEpisodes = (episodes: string[]): string => {
return [...new Set(episodes)].join(',');
};
const parseSelect = (s: string): string[] => {
const ret: string[] = [];

View file

@ -52,10 +52,6 @@ const SERVICES: Record<string, any> = {
const ids = makeCommand(argv.service);
for (const id of ids) {
overrideArguments(cfg.cli, id);
/* Reimport module to override appArgv */
// Object.keys(require.cache).forEach((key) => {
// if (key.endsWith('crunchy.js') || key.endsWith('hidive.js')) delete require.cache[key];
// });
const Service = SERVICES[argv.service];
if (!Service) {
console.error('Unknown service:', argv.service);

View file

@ -105,7 +105,6 @@ async function buildBinary(buildType: BuildTypes, gui: boolean) {
const binConf = {
ffmpeg: `ffmpeg${ext}`,
mkvmerge: `mkvmerge${ext}`,
ffprobe: `ffprobe${ext}`,
mp4decrypt: `mp4decrypt${ext}`,
shaka: `shaka-packager${ext}`
};

View file

@ -91,7 +91,7 @@ class hlsDownload {
m3u8json: options.m3u8json,
outputFile: options.output || 'stream.ts',
threads: options.threads || 5,
retries: options.retries || 4,
retries: options.retries || 10,
offset: options.offset || 0,
baseurl: options.baseurl,
skipInit: options.skipInit,

View file

@ -53,7 +53,7 @@ const api: APIType = {
bundlejs: 'https://static.crunchyroll.com/vilos-v2/web/vilos/js/bundle.js',
//
// Crunchyroll API
basic_auth_token: 'bmR0aTZicXlqcm9wNXZnZjF0dnU6elpIcS00SEJJVDlDb2FMcnBPREJjRVRCTUNHai1QNlg=',
basic_auth_token: 'b2g0cnYxbHpsOXR5ZzF4b2NqZ2o6cDI4bEhwM3J1ZVV0ek1aNDRhZmNyam84MUNmaFZGemg=',
auth: `${domain.cr_api}/auth/v1/token`,
me: `${domain.cr_api}/accounts/v1/me`,
profile: `${domain.cr_api}/accounts/v1/me/profile`,
@ -71,7 +71,7 @@ const api: APIType = {
cms_auth: `${domain.cr_api}/index/v2`,
//
// Crunchyroll Headers
crunchyDefUserAgent: 'Crunchyroll/ANDROIDTV/3.50.0_22282 (Android 12; en-US; SHIELD Android TV Build/SR1A.211012.001)',
crunchyDefUserAgent: 'Crunchyroll/ANDROIDTV/3.53.1_22290 (Android 12; en-US; SHIELD Android TV Build/SR1A.211012.001)',
crunchyDefHeader: {},
crunchyAuthHeader: {},
crunchyAuthRefreshHeader: {},

View file

@ -1,4 +1,4 @@
import yargs, { Choices } from 'yargs';
import { Command } from 'commander';
import { args, AvailableMuxer, groups } from './module.args';
import { LanguageItem } from './module.langsData';
import { DownloadInfo } from '../@types/messageHandler';
@ -32,13 +32,13 @@ export let argvC: {
auth: boolean | undefined;
dlFonts: boolean | undefined;
search: string | undefined;
'search-type': string;
searchType: string;
page: number | undefined;
locale: string;
new: boolean | undefined;
'movie-listing': string | undefined;
'show-raw': string | undefined;
'season-raw': string | undefined;
movieListing: string | undefined;
showRaw: string | undefined;
seasonRaw: string | undefined;
series: string | undefined;
s: string | undefined;
srz: string | undefined;
@ -95,6 +95,7 @@ export let argvC: {
scaledBorderAndShadowFix: boolean;
scaledBorderAndShadow: 'yes' | 'no';
originalScriptFix: boolean;
subtitleTimestampFix: boolean;
// Proxy
proxy: string;
proxyAll: boolean;
@ -102,6 +103,43 @@ export let argvC: {
export type ArgvType = typeof argvC;
// This functions manages slight mismatches like -srz and returns it as --srz
const processArgv = () => {
const argv = [];
const arrayFlags = args.filter((a) => a.type === 'array').map((a) => `--${a.name}`);
for (let i = 0; i < process.argv.length; i++) {
const arg = process.argv[i];
if (/^-[a-zA-Z]{2,}$/.test(arg)) {
const found = args.find((a) => a.name === arg.substring(1) || a.alias === arg.substring(1));
if (found) {
argv.push(`--${found.name}`);
continue;
}
}
if (arrayFlags.includes(arg)) {
const col = [];
let n = i + 1;
while (n < process.argv.length && !process.argv[n].startsWith('-')) {
col.push(process.argv[n]);
n++;
}
argv.push(arg);
argv.push(col.join(' '));
i = n - 1;
continue;
}
argv.push(arg);
}
return argv;
};
const appArgv = (
cfg: {
[key: string]: unknown;
@ -109,41 +147,91 @@ const appArgv = (
isGUI = false
) => {
if (argvC) return argvC;
yargs(process.argv.slice(2));
const argv = getArgv(cfg, isGUI).parseSync();
argvC = argv;
return argv;
const argv = getCommander(cfg, isGUI).parse(processArgv());
const parsed = argv.opts() as ArgvType;
// Be sure that both vars (name and alias) are defined
for (const item of args) {
const name = item.name;
const alias = item.alias;
if (!alias) continue;
if (parsed[name] !== undefined) {
parsed[alias] = parsed[name];
}
if (parsed[alias] !== undefined) {
parsed[name] = parsed[alias];
}
}
if (!isGUI && (process.argv.length <= 2 || parsed.help)) {
argv.outputHelp();
process.exit(0);
}
argvC = parsed;
return parsed;
};
const overrideArguments = (cfg: { [key: string]: unknown }, override: Partial<typeof argvC>, isGUI = false) => {
const argv = getArgv(cfg, isGUI)
.middleware((ar) => {
for (const key of Object.keys(override)) {
ar[key] = override[key];
}
})
.parseSync();
argvC = argv;
const argv = getCommander(cfg, isGUI);
const baseArgv = [...processArgv()];
for (const [key, val] of Object.entries(override)) {
if (val === undefined) continue;
if (typeof val === 'boolean') {
if (val) baseArgv.push(key.length > 1 ? `--${key}` : `-${key}`);
} else {
baseArgv.push(key.length > 1 ? `--${key}` : `-${key}`, String(val));
}
}
const data = argv.parse(baseArgv);
const parsed = data.opts() as ArgvType;
// Be sure that both vars (name and alias) are defined
for (const item of args) {
const name = item.name;
const alias = item.alias;
if (!alias) continue;
if (parsed[name] !== undefined) {
parsed[alias] = parsed[name];
}
if (parsed[alias] !== undefined) {
parsed[name] = parsed[alias];
}
}
if (!isGUI && (process.argv.length <= 2 || parsed.help)) {
argv.outputHelp();
process.exit(0);
}
argvC = parsed;
};
export { appArgv, overrideArguments };
const getArgv = (cfg: { [key: string]: unknown }, isGUI: boolean) => {
const getCommander = (cfg: Record<string, unknown>, isGUI: boolean) => {
const program = new Command();
program
.name(process.platform === 'win32' ? 'aniDL.exe' : 'aniDL')
.description(pj.description)
.version(pj.version, '-v, --version', 'Show version')
.allowUnknownOption(false)
.allowExcessArguments(true);
const parseDefault = <T = unknown>(key: string, _default: T): T => {
if (Object.prototype.hasOwnProperty.call(cfg, key)) {
return cfg[key] as T;
} else return _default;
};
const argv = yargs
.parserConfiguration({
'duplicate-arguments-array': false,
'camel-case-expansion': false
})
.wrap(yargs.terminalWidth())
.usage('Usage: $0 [options]')
.version(pj.version)
.help(true);
//.strictOptions()
const data = args.map((a) => {
return {
...a,
@ -152,40 +240,93 @@ const getArgv = (cfg: { [key: string]: unknown }, isGUI: boolean) => {
default: typeof a.default === 'object' && !Array.isArray(a.default) ? parseDefault((a.default as any).name || a.name, (a.default as any).default) : a.default
};
});
for (const item of data)
argv.option(item.name, {
...item,
coerce: (value) => {
if (item.transformer) {
return item.transformer(value);
} else {
return value;
for (const item of data) {
const option = program.createOption(
(item.alias
? `${item.alias.length === 1 ? `-${item.alias}` : `--${item.alias}`}, ${item.name.length === 1 ? `-${item.name}` : `--${item.name}`}`
: item.name.length === 1
? `-${item.name}`
: `--${item.name}`) + (item.type === 'boolean' ? '' : ` <value>`),
item.describe ?? ''
);
if (item.default !== undefined) option.default(item.transformer ? item.transformer(item.default) : item.default);
const optionNames = [...args.map((a) => `--${a.name}`), ...args.map((a) => (a.alias ? `-${a.alias}` : null)).filter(Boolean)];
option.argParser((value) => {
if (item.transformer) return item.transformer(value);
// Prevent from passing other options als value for option
if (value && typeof value === 'string' && value.startsWith('-') && optionNames.includes(value)) return undefined;
if (item.type === 'boolean') {
if (value === undefined) return true;
if (value === 'true') return true;
if (value === 'false') return false;
return Boolean(value);
}
if (item.type === 'array') {
if (typeof value === 'string' && value.includes(',')) {
return value.split(',').map((v) => v.trim());
}
},
choices: item.name === 'service' && isGUI ? undefined : (item.choices as unknown as Choices)
if (typeof value === 'string' && value.includes(' ')) {
return value.split(' ').map((v) => v.trim());
}
return Array.isArray(value) ? value : [value];
}
if (item.type === 'number') {
const num = Number(value);
return Number.isFinite(num) ? num : 0;
}
if (item.type === 'string') {
if (value === undefined) return undefined;
return value;
}
if (item.choices && !(isGUI && item.name === 'service')) {
if (!item.choices.includes(value)) {
console.error(`Invalid value '${value}' for --${item.name}. Allowed: ${item.choices.join(', ')}`);
process.exit(1);
}
}
return value;
});
// Custom logic for suggesting corrections for misspelled options
argv.middleware((argv: Record<string, any>) => {
// List of valid options
const validOptions = [...args.map((a) => a.name), ...(args.map((a) => a.alias).filter((alias) => alias !== undefined) as string[])];
const unknownOptions = Object.keys(argv).filter((key) => !validOptions.includes(key) && key !== '_' && key !== '$0'); // Filter out known options
program.addOption(option);
}
const suggestedOptions: Record<string, boolean> = {};
unknownOptions.forEach((actualOption) => {
const closestOption = validOptions.find((option) => {
const levenVal = leven(option, actualOption);
return levenVal <= 2 && levenVal > 0;
// Custom logic for suggesting corrections for misspelled options
program.hook('preAction', (_, command) => {
const used = command.parent?.args || [];
const validOptions = [...args.map((a) => a.name), ...args.map((a) => a.alias).filter((a): a is string => a !== undefined)];
const unknownOptions = used.filter((arg) => arg.startsWith('-'));
const suggestions: Record<string, boolean> = {};
unknownOptions.forEach((opt) => {
const cleaned = opt.replace(/^-+/, '');
const closest = validOptions.find((vo) => {
const dist = leven(vo, cleaned);
return dist <= 2 && dist > 0;
});
if (closestOption && !suggestedOptions[closestOption]) {
suggestedOptions[closestOption] = true;
console.info(`Unknown option ${actualOption}, did you mean ${closestOption}?`);
} else if (!suggestedOptions[actualOption]) {
suggestedOptions[actualOption] = true;
console.info(`Unknown option ${actualOption}`);
if (closest && !suggestions[closest]) {
console.info(`Unknown option ${opt}, did you mean --${closest}?`);
suggestions[closest] = true;
} else if (!suggestions[cleaned]) {
console.info(`Unknown option ${opt}`);
suggestions[cleaned] = true;
}
});
});
return argv as unknown as yargs.Argv<typeof argvC>;
return program;
};

View file

@ -468,6 +468,19 @@ const args: TAppArg<boolean | number | string | unknown[]>[] = [
default: true
}
},
{
name: 'subtitleTimestampFix',
group: 'dl',
describe:
'Fixes subtitle dialogues that go over the video length (deletes dialogues where start is over video length and updates the end timestamp when end is over video length).',
docDescribe: true,
service: ['crunchy'],
type: 'boolean',
usage: '',
default: {
default: false
}
},
{
name: 'novids',
group: 'dl',

View file

@ -85,7 +85,6 @@ export type ConfigObject = {
bin: {
ffmpeg?: string;
mkvmerge?: string;
ffprobe?: string;
mp4decrypt?: string;
shaka?: string;
};
@ -150,7 +149,6 @@ const loadBinCfg = async () => {
const defaultBin = {
ffmpeg: 'ffmpeg',
mkvmerge: 'mkvmerge',
ffprobe: 'ffprobe',
mp4decrypt: 'mp4decrypt',
shaka: 'shaka-packager'
};

View file

@ -1,14 +1,14 @@
import * as iso639 from 'iso-639';
import * as yamlCfg from './module.cfg-loader';
import { fontFamilies, fontMime } from './module.fontsData';
import path from 'path';
import fs from 'fs';
import fsp from 'fs/promises';
import { LanguageItem } from './module.langsData';
import { AvailableMuxer } from './module.args';
import { console } from './log';
import ffprobe from 'ffprobe';
import Helper from './module.helper';
import { convertChaptersToFFmpegFormat } from './module.ffmpegChapter';
import { mediaInfoFactory } from 'mediainfo.js';
export type MergerInput = {
path: string;
@ -67,13 +67,26 @@ class Merger {
public async createDelays() {
//Don't bother scanning it if there is only 1 vna stream
if (this.options.videoAndAudio.length > 1) {
const bin = await yamlCfg.loadBinCfg();
const vnas = this.options.videoAndAudio;
//get and set durations on each videoAndAudio Stream
for (const [vnaIndex, vna] of vnas.entries()) {
const streamInfo = await ffprobe(vna.path, { path: bin.ffprobe as string });
const videoInfo = streamInfo.streams.filter((stream) => stream.codec_type == 'video');
vnas[vnaIndex].duration = parseInt(videoInfo[0].duration as string);
const file = await fsp.open(vna.path);
const { size } = await fsp.stat(vna.path);
// Mediainfo
const mediaInfo = await mediaInfoFactory();
const result = await mediaInfo.analyzeData(
() => size,
async (size, offset) => {
const buf = Buffer.alloc(size);
const { bytesRead } = await file.read(buf, 0, size, offset);
return buf.subarray(0, bytesRead);
}
);
await file.close();
const videoInfo = result?.media?.track?.filter((stream) => stream['@type'] == 'Video');
vnas[vnaIndex].duration = videoInfo?.[0].Duration;
}
//Sort videoAndAudio streams by duration (shortest first)
vnas.sort((a, b) => {

View file

@ -1,7 +1,7 @@
{
"name": "multi-downloader-nx",
"short_name": "aniDL",
"version": "5.6.6",
"version": "5.6.9",
"description": "Downloader for Crunchyroll, Hidive, and AnimationDigitalNetwork with CLI and GUI",
"keywords": [
"download",
@ -40,43 +40,44 @@
},
"license": "MIT",
"dependencies": {
"@bufbuild/protobuf": "^2.10.1",
"@bufbuild/protobuf": "^2.10.2",
"commander": "^14.0.2",
"express": "^5.1.0",
"ffprobe": "^1.1.2",
"express": "^5.2.1",
"iso-639": "^0.2.2",
"leven": "^4.1.0",
"log4js": "^6.9.1",
"lookpath": "^1.2.3",
"m3u8-parser": "^7.2.0",
"mediainfo.js": "^0.3.6",
"mpd-parser": "^1.3.1",
"node-playready": "^1.1.1",
"open": "^11.0.0",
"undici": "^7.16.0",
"widevine": "^1.0.3",
"ws": "^8.18.3",
"yaml": "^2.8.1",
"yargs": "^17.7.2"
"yaml": "^2.8.2"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@types/express": "^5.0.5",
"@types/ffprobe": "^1.1.8",
"@commitlint/cli": "^20.2.0",
"@commitlint/config-conventional": "^20.2.0",
"@eslint/js": "^9.39.2",
"@types/express": "^5.0.6",
"@types/m3u8-parser": "^7.2.5",
"@types/node": "^24.10.1",
"@types/node": "^25.0.3",
"@types/ws": "^8.18.1",
"@types/yargs": "17.0.35",
"@typescript-eslint/eslint-plugin": "^8.48.0",
"@typescript-eslint/parser": "^8.48.0",
"@yao-pkg/pkg": "^6.10.1",
"esbuild": "0.25.12",
"eslint": "^9.39.1",
"@typescript-eslint/eslint-plugin": "^8.50.0",
"@typescript-eslint/parser": "^8.50.0",
"@yao-pkg/pkg": "^6.11.0",
"esbuild": "0.26.0",
"eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8",
"prettier": "^3.6.2",
"husky": "^9.1.7",
"jiti": "^2.6.1",
"prettier": "^3.7.4",
"removeNPMAbsolutePaths": "^3.0.1",
"ts-node": "^10.9.2",
"typescript": "^5.9.3",
"typescript-eslint": "^8.48.0"
"typescript-eslint": "^8.50.0"
},
"scripts": {
"prestart": "pnpm run tsc test",
@ -114,6 +115,7 @@
"test-macos-x64": "pnpm run pretest && cd lib && node modules/build macos-x64",
"test-windows-arm64": "pnpm run pretest && cd lib && node modules/build windows-arm64",
"test-linux-arm64": "pnpm run pretest && cd lib && node modules/build linuxstatic-arm64 && node modules/build alpine-x64",
"test-macos-arm64": "pnpm run pretest && cd lib && node modules/build macos-arm64"
"test-macos-arm64": "pnpm run pretest && cd lib && node modules/build macos-arm64",
"prepare": "husky"
}
}

File diff suppressed because it is too large Load diff

1
tsc.ts
View file

@ -28,7 +28,6 @@ const ignore = [
'*/*\\.tsx?$',
'./fonts*',
'./gui/react*',
'./dev.js$',
'*/node_modules/*',
'./widevine/*',
'./playready/*',