mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-20 12:11:28 -07:00
fix: skip aniskip for url playback
This commit is contained in:
@@ -5,7 +5,14 @@ import path from 'node:path';
|
||||
import net from 'node:net';
|
||||
import { EventEmitter } from 'node:events';
|
||||
import type { Args } from './types';
|
||||
import { runAppCommandCaptureOutput, startOverlay, state, waitForUnixSocketReady } from './mpv';
|
||||
import {
|
||||
cleanupPlaybackSession,
|
||||
runAppCommandCaptureOutput,
|
||||
shouldResolveAniSkipMetadata,
|
||||
startOverlay,
|
||||
state,
|
||||
waitForUnixSocketReady,
|
||||
} from './mpv';
|
||||
import * as mpvModule from './mpv';
|
||||
|
||||
function createTempSocketPath(): { dir: string; socketPath: string } {
|
||||
@@ -73,6 +80,20 @@ test('waitForUnixSocketReady returns true when socket becomes connectable before
|
||||
}
|
||||
});
|
||||
|
||||
test('shouldResolveAniSkipMetadata skips URL and YouTube-preloaded playback', () => {
|
||||
assert.equal(shouldResolveAniSkipMetadata('/media/show.mkv', 'file'), true);
|
||||
assert.equal(
|
||||
shouldResolveAniSkipMetadata('https://www.youtube.com/watch?v=test123', 'url'),
|
||||
false,
|
||||
);
|
||||
assert.equal(
|
||||
shouldResolveAniSkipMetadata('/tmp/video123.webm', 'file', {
|
||||
primaryPath: '/tmp/video123.ja.srt',
|
||||
}),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
function makeArgs(overrides: Partial<Args> = {}): Args {
|
||||
return {
|
||||
backend: 'x11',
|
||||
@@ -80,16 +101,19 @@ function makeArgs(overrides: Partial<Args> = {}): Args {
|
||||
recursive: false,
|
||||
profile: '',
|
||||
startOverlay: false,
|
||||
youtubeSubgenMode: 'off',
|
||||
whisperBin: '',
|
||||
whisperModel: '',
|
||||
whisperVadModel: '',
|
||||
whisperThreads: 4,
|
||||
youtubeSubgenOutDir: '',
|
||||
youtubeSubgenAudioFormat: 'wav',
|
||||
youtubeSubgenKeepTemp: false,
|
||||
youtubeFixWithAi: false,
|
||||
youtubePrimarySubLangs: [],
|
||||
youtubeSecondarySubLangs: [],
|
||||
youtubeAudioLangs: [],
|
||||
youtubeWhisperSourceLanguage: 'ja',
|
||||
aiConfig: {},
|
||||
useTexthooker: false,
|
||||
autoStartOverlay: false,
|
||||
texthookerOnly: false,
|
||||
@@ -152,3 +176,59 @@ test('startOverlay resolves without fixed 2s sleep when readiness signals arrive
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test('cleanupPlaybackSession preserves background app while stopping mpv-owned children', async () => {
|
||||
const { dir } = createTempSocketPath();
|
||||
const appPath = path.join(dir, 'fake-subminer.sh');
|
||||
const appInvocationsPath = path.join(dir, 'app-invocations.log');
|
||||
fs.writeFileSync(
|
||||
appPath,
|
||||
`#!/bin/sh\necho \"$@\" >> ${JSON.stringify(appInvocationsPath)}\nexit 0\n`,
|
||||
);
|
||||
fs.chmodSync(appPath, 0o755);
|
||||
|
||||
const calls: string[] = [];
|
||||
const overlayProc = {
|
||||
killed: false,
|
||||
kill: () => {
|
||||
calls.push('overlay-kill');
|
||||
return true;
|
||||
},
|
||||
} as unknown as NonNullable<typeof state.overlayProc>;
|
||||
const mpvProc = {
|
||||
killed: false,
|
||||
kill: () => {
|
||||
calls.push('mpv-kill');
|
||||
return true;
|
||||
},
|
||||
} as unknown as NonNullable<typeof state.mpvProc>;
|
||||
const helperProc = {
|
||||
killed: false,
|
||||
kill: () => {
|
||||
calls.push('helper-kill');
|
||||
return true;
|
||||
},
|
||||
} as unknown as NonNullable<typeof state.overlayProc>;
|
||||
|
||||
state.stopRequested = false;
|
||||
state.appPath = appPath;
|
||||
state.overlayManagedByLauncher = true;
|
||||
state.overlayProc = overlayProc;
|
||||
state.mpvProc = mpvProc;
|
||||
state.youtubeSubgenChildren.add(helperProc);
|
||||
|
||||
try {
|
||||
await cleanupPlaybackSession(makeArgs());
|
||||
|
||||
assert.deepEqual(calls, ['mpv-kill', 'helper-kill']);
|
||||
assert.equal(fs.existsSync(appInvocationsPath), false);
|
||||
} finally {
|
||||
state.overlayProc = null;
|
||||
state.mpvProc = null;
|
||||
state.youtubeSubgenChildren.clear();
|
||||
state.overlayManagedByLauncher = false;
|
||||
state.appPath = '';
|
||||
state.stopRequested = false;
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -419,6 +419,20 @@ export async function loadSubtitleIntoMpv(
|
||||
}
|
||||
}
|
||||
|
||||
export function shouldResolveAniSkipMetadata(
|
||||
target: string,
|
||||
targetKind: 'file' | 'url',
|
||||
preloadedSubtitles?: { primaryPath?: string; secondaryPath?: string },
|
||||
): boolean {
|
||||
if (targetKind !== 'file') {
|
||||
return false;
|
||||
}
|
||||
if (preloadedSubtitles?.primaryPath || preloadedSubtitles?.secondaryPath) {
|
||||
return false;
|
||||
}
|
||||
return !isYoutubeTarget(target);
|
||||
}
|
||||
|
||||
export async function startMpv(
|
||||
target: string,
|
||||
targetKind: 'file' | 'url',
|
||||
@@ -456,17 +470,13 @@ export async function startMpv(
|
||||
log('debug', args.logLevel, `YouTube subtitle langs: ${subtitleLangs}`);
|
||||
log('debug', args.logLevel, `YouTube audio langs: ${audioLangs}`);
|
||||
mpvArgs.push(`--ytdl-format=${DEFAULT_YOUTUBE_YTDL_FORMAT}`, `--alang=${audioLangs}`);
|
||||
|
||||
if (args.youtubeSubgenMode === 'off') {
|
||||
mpvArgs.push(
|
||||
'--sub-auto=fuzzy',
|
||||
`--slang=${subtitleLangs}`,
|
||||
'--ytdl-raw-options-append=write-auto-subs=',
|
||||
'--ytdl-raw-options-append=write-subs=',
|
||||
'--ytdl-raw-options-append=sub-format=vtt/best',
|
||||
`--ytdl-raw-options-append=sub-langs=${subtitleLangs}`,
|
||||
);
|
||||
}
|
||||
mpvArgs.push(
|
||||
'--sub-auto=fuzzy',
|
||||
`--slang=${subtitleLangs}`,
|
||||
'--ytdl-raw-options-append=write-subs=',
|
||||
'--ytdl-raw-options-append=sub-format=vtt/best',
|
||||
`--ytdl-raw-options-append=sub-langs=${subtitleLangs}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,8 +489,9 @@ export async function startMpv(
|
||||
if (options?.startPaused) {
|
||||
mpvArgs.push('--pause=yes');
|
||||
}
|
||||
const aniSkipMetadata =
|
||||
targetKind === 'file' ? await resolveAniSkipMetadataForFile(target) : null;
|
||||
const aniSkipMetadata = shouldResolveAniSkipMetadata(target, targetKind, preloadedSubtitles)
|
||||
? await resolveAniSkipMetadataForFile(target)
|
||||
: null;
|
||||
const scriptOpts = buildSubminerScriptOpts(appPath, socketPath, aniSkipMetadata);
|
||||
if (aniSkipMetadata) {
|
||||
log(
|
||||
@@ -628,6 +639,29 @@ export function stopOverlay(args: Args): void {
|
||||
void terminateTrackedDetachedMpv(args.logLevel);
|
||||
}
|
||||
|
||||
export async function cleanupPlaybackSession(args: Args): Promise<void> {
|
||||
if (state.mpvProc && !state.mpvProc.killed) {
|
||||
try {
|
||||
state.mpvProc.kill('SIGTERM');
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
for (const child of state.youtubeSubgenChildren) {
|
||||
if (!child.killed) {
|
||||
try {
|
||||
child.kill('SIGTERM');
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
state.youtubeSubgenChildren.clear();
|
||||
|
||||
await terminateTrackedDetachedMpv(args.logLevel);
|
||||
}
|
||||
|
||||
function buildAppEnv(): NodeJS.ProcessEnv {
|
||||
const env: Record<string, string | undefined> = {
|
||||
...process.env,
|
||||
|
||||
Reference in New Issue
Block a user