mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-09-04 23:54:28 -07:00
fix(anime): keep browser state in sync and alias HLS segments
- Toggle the player Anime Browser without losing its state - Share active playback state and support rotating fake segment extensions
This commit is contained in:
@@ -10,6 +10,9 @@
|
||||
|
||||
/** Where a stream came from, resolved into the fields consumers actually want. */
|
||||
export interface AnimeStreamMetadata {
|
||||
sourceId: string;
|
||||
animeUrl: string;
|
||||
episodeUrl: string;
|
||||
/** The URL handed to mpv. Matches what mpv reports as `path`. */
|
||||
mediaPath: string;
|
||||
/**
|
||||
@@ -192,6 +195,9 @@ export function buildAnimeStreamMetadata(input: AnimeStreamMetadataInput): Anime
|
||||
const displayTitle = buildStreamDisplayTitle(seriesTitle, season, episodeNumber, label.title);
|
||||
|
||||
return {
|
||||
sourceId: input.sourceId,
|
||||
animeUrl: input.animeUrl,
|
||||
episodeUrl: input.episodeUrl,
|
||||
mediaPath: input.mediaPath,
|
||||
statsPath: buildAnimeStreamStatsPath(input.sourceId, input.animeUrl, input.episodeUrl),
|
||||
seriesTitle,
|
||||
|
||||
@@ -61,7 +61,7 @@ test('rewritePlaylistOrigins swaps absolute upstream URLs and keeps relative lin
|
||||
assert.ok(!rewritten.includes('41569'));
|
||||
});
|
||||
|
||||
test('rewritePlaylistOrigins gives proxied image segments an ffmpeg-safe TS suffix', () => {
|
||||
test('rewritePlaylistOrigins gives proxied disguised segments an ffmpeg-safe TS suffix', () => {
|
||||
const body = [
|
||||
'#EXTM3U',
|
||||
'#EXTINF:6.006,',
|
||||
@@ -70,6 +70,14 @@ test('rewritePlaylistOrigins gives proxied image segments an ffmpeg-safe TS suff
|
||||
'http://127.0.0.1:41569/video/absolute.image',
|
||||
'#EXTINF:3,',
|
||||
'https://cdn.example/video/external.image',
|
||||
'#EXTINF:2,',
|
||||
'/video/rotating.jpg',
|
||||
'#EXTINF:2,',
|
||||
'/video/rotating.css',
|
||||
'#EXTINF:2,',
|
||||
'/video/rotating.html',
|
||||
'#EXTINF:2,',
|
||||
'/video/extensionless',
|
||||
].join('\n');
|
||||
const rewritten = rewritePlaylistOrigins(body, 'http://127.0.0.1:41569', 'http://127.0.0.1:9999');
|
||||
|
||||
@@ -78,6 +86,25 @@ test('rewritePlaylistOrigins gives proxied image segments an ffmpeg-safe TS suff
|
||||
rewritten.includes(`http://127.0.0.1:9999/video/absolute.image${TS_SEGMENT_ALIAS_SUFFIX}`),
|
||||
);
|
||||
assert.ok(rewritten.includes('https://cdn.example/video/external.image'));
|
||||
assert.ok(rewritten.includes(`/video/rotating.jpg${TS_SEGMENT_ALIAS_SUFFIX}`));
|
||||
assert.ok(rewritten.includes(`/video/rotating.css${TS_SEGMENT_ALIAS_SUFFIX}`));
|
||||
assert.ok(rewritten.includes(`/video/rotating.html${TS_SEGMENT_ALIAS_SUFFIX}`));
|
||||
assert.ok(rewritten.includes(`/video/extensionless${TS_SEGMENT_ALIAS_SUFFIX}`));
|
||||
});
|
||||
|
||||
test('rewritePlaylistOrigins leaves recognized media extensions unaliased', () => {
|
||||
const body = [
|
||||
'#EXTM3U',
|
||||
'#EXTINF:6,',
|
||||
'/video/plain.ts',
|
||||
'#EXTINF:6,',
|
||||
'/video/fragmented.m4s',
|
||||
'#EXTINF:6,',
|
||||
'/subs/line.vtt',
|
||||
'/video/nested.m3u8',
|
||||
].join('\n');
|
||||
const rewritten = rewritePlaylistOrigins(body, 'http://127.0.0.1:41569', 'http://127.0.0.1:9999');
|
||||
assert.ok(!rewritten.includes(TS_SEGMENT_ALIAS_SUFFIX));
|
||||
});
|
||||
|
||||
/* ---------- proxy end-to-end ---------- */
|
||||
|
||||
@@ -25,10 +25,54 @@ const TS_SYNC_BYTE = 0x47;
|
||||
const SYNC_RUN = 5;
|
||||
/**
|
||||
* FFmpeg 8.1 rejects HLS media whose URL suffix is not in its segment allowlist.
|
||||
* The disguised MPEG-TS segments seen in the wild use `.image`, so the local
|
||||
* playlist gives them this safe alias and removes it again before forwarding.
|
||||
* Hosts disguise MPEG-TS segments behind rotating fake extensions (`.image`,
|
||||
* `.jpg`, `.css`, ...), so the local playlist gives every proxied segment
|
||||
* without a recognized media extension this safe alias and removes it again
|
||||
* before forwarding.
|
||||
*/
|
||||
export const TS_SEGMENT_ALIAS_SUFFIX = '.subminer.ts';
|
||||
/** ffmpeg 8.1 hls demuxer `allowed_segment_extensions` defaults (minus `html`,
|
||||
* which only newer builds accept and is a disguise whenever it shows up here). */
|
||||
const FFMPEG_SAFE_SEGMENT_EXTENSIONS = new Set([
|
||||
'3gp',
|
||||
'aac',
|
||||
'avi',
|
||||
'ac3',
|
||||
'eac3',
|
||||
'flac',
|
||||
'mkv',
|
||||
'm3u8',
|
||||
'm4a',
|
||||
'm4s',
|
||||
'm4v',
|
||||
'mpg',
|
||||
'mov',
|
||||
'mp2',
|
||||
'mp3',
|
||||
'mp4',
|
||||
'mpeg',
|
||||
'mpegts',
|
||||
'ogg',
|
||||
'ogv',
|
||||
'oga',
|
||||
'ts',
|
||||
'vob',
|
||||
'vtt',
|
||||
'wav',
|
||||
'webvtt',
|
||||
'cmfv',
|
||||
'cmfa',
|
||||
'ec3',
|
||||
'fmp4',
|
||||
]);
|
||||
|
||||
/** True when ffmpeg's picky segment-extension check would reject this path. */
|
||||
function needsTsSegmentAlias(pathname: string): boolean {
|
||||
const name = pathname.slice(pathname.lastIndexOf('/') + 1).toLowerCase();
|
||||
const dot = name.lastIndexOf('.');
|
||||
if (dot === -1) return true;
|
||||
return !FFMPEG_SAFE_SEGMENT_EXTENSIONS.has(name.slice(dot + 1));
|
||||
}
|
||||
/** A disguise prefix is small; give up scanning after this much. */
|
||||
export const DEFAULT_SCAN_LIMIT_BYTES = 1024 * 1024;
|
||||
/** Bytes needed to either find a run within the limit or rule one out. */
|
||||
@@ -80,7 +124,7 @@ export function rewritePlaylistOrigins(
|
||||
} catch {
|
||||
return line;
|
||||
}
|
||||
if (resolved.origin !== proxyOrigin || !resolved.pathname.toLowerCase().endsWith('.image')) {
|
||||
if (resolved.origin !== proxyOrigin || !needsTsSegmentAlias(resolved.pathname)) {
|
||||
return line;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user