fix(anime): scope preferences, paginate sources, harden installs

- Preference store keys entries by extension package + bridge source id; legacy unscoped entries are discarded once instead of being handed to whichever extension asks first
- Source picker's "Load more" appends the next page without duplicating streamed results
- Repository index fetches and subtitle/APK downloads now time out and are size-bounded instead of hanging or growing unbounded
- APK installs are staged to a temp file and renamed into place
- Stream metadata lookup matches the requested path, not only the currently playing one
- Reworked animeui into browse-state/detail-panel/panels.css modules
- Reverted premature CHANGELOG unreleased entries; refreshed anime-browser docs
This commit is contained in:
2026-08-02 00:50:14 -07:00
parent 30f79857d8
commit c07ba665ba
52 changed files with 2471 additions and 1598 deletions
+2 -1
View File
@@ -93,7 +93,8 @@ export function scheduleOverlayContentReadyFallback(deps: {
delayMs?: number;
}): void {
const setTimeoutFn =
deps.setTimeoutFn ?? ((callback: () => void, delayMs: number): unknown => setTimeout(callback, delayMs));
deps.setTimeoutFn ??
((callback: () => void, delayMs: number): unknown => setTimeout(callback, delayMs));
setTimeoutFn(() => {
if (deps.isContentReady() || deps.isDestroyed()) {
return;
+28
View File
@@ -163,6 +163,34 @@ test('extractSubtitleTrackToFile rejects a missing local external track', async
);
});
test('internal WebVTT extraction uses ffmpeg webvtt muxer with a vtt output file', async () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'subminer-extract-'));
const ffmpegPath = path.join(dir, 'ffmpeg-stub');
const argsPath = path.join(dir, 'args.txt');
fs.writeFileSync(
ffmpegPath,
`#!/bin/sh\nprintf '%s\\n' "$@" > '${argsPath}'\nfor arg in "$@"; do output="$arg"; done\n: > "$output"\n`,
{ mode: 0o755 },
);
try {
const result = await extractSubtitleTrackToFile({
resolveFfmpegPath: () => ffmpegPath,
videoPath: path.join(dir, 'video.mkv'),
track: { id: 2, type: 'sub', codec: 'webvtt', 'ff-index': 3 },
httpHeaders: null,
});
const args = fs.readFileSync(argsPath, 'utf8').trimEnd().split('\n');
const formatIndex = args.indexOf('-f');
assert.equal(args[formatIndex + 1], 'webvtt');
assert.equal(path.extname(result.path), '.vtt');
cleanupTemporaryFile(result);
} finally {
fs.rmSync(dir, { recursive: true, force: true });
}
});
test('cleanupTemporaryFile preserves the retimed output sharing the temp directory', () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'subminer-extract-'));
const sourcePath = path.join(dir, 'remote_track.srt');
+2 -1
View File
@@ -100,6 +100,7 @@ async function extractInternalTrack(input: SubtitleExtractionInput): Promise<Fil
if (!extension) {
throw new Error(`Unsupported subtitle codec: ${input.track.codec ?? 'unknown'}`);
}
const ffmpegMuxer = extension === 'vtt' ? 'webvtt' : extension;
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'subminer-subsync-'));
try {
@@ -123,7 +124,7 @@ async function extractInternalTrack(input: SubtitleExtractionInput): Promise<Fil
'-map',
`0:${ffIndex}`,
'-f',
extension,
ffmpegMuxer,
outputPath,
]);