mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-05-26 00:55:16 -07:00
fix(config): remove trailing commas from config.example.jsonc
- Strip trailing commas throughout both config.example.jsonc copies - Reformat inline arrays to multi-line for JSON strictness - Update Jellyfin subtitle preload and playback launch tests and impl
This commit is contained in:
@@ -267,3 +267,46 @@ test('playback handler does not let stats metadata failures block playback start
|
||||
|
||||
assert.deepEqual(commands[1], ['loadfile', 'https://stream.example/video.m3u8', 'replace']);
|
||||
});
|
||||
|
||||
test('playback handler does not let media title failures block playback startup', async () => {
|
||||
const commands: Array<Array<string | number>> = [];
|
||||
const handler = createPlayJellyfinItemInMpvHandler({
|
||||
ensureMpvConnectedForPlayback: async () => true,
|
||||
getMpvClient: () => ({ connected: true, send: () => {} }),
|
||||
resolvePlaybackPlan: async () => ({
|
||||
url: 'https://stream.example/video.m3u8',
|
||||
mode: 'direct',
|
||||
title: 'Episode 4',
|
||||
itemTitle: 'Episode 4',
|
||||
seriesTitle: null,
|
||||
seasonNumber: null,
|
||||
episodeNumber: null,
|
||||
startTimeTicks: 0,
|
||||
audioStreamIndex: null,
|
||||
subtitleStreamIndex: null,
|
||||
}),
|
||||
applyJellyfinMpvDefaults: () => {},
|
||||
showVisibleOverlay: () => {},
|
||||
sendMpvCommand: (command) => commands.push(command),
|
||||
armQuitOnDisconnect: () => {},
|
||||
schedule: () => {},
|
||||
convertTicksToSeconds: (ticks) => ticks / 10_000_000,
|
||||
preloadExternalSubtitles: () => {},
|
||||
setActivePlayback: () => {},
|
||||
setLastProgressAtMs: () => {},
|
||||
reportPlaying: () => {},
|
||||
showMpvOsd: () => {},
|
||||
updateCurrentMediaTitle: () => {
|
||||
throw new Error('title state unavailable');
|
||||
},
|
||||
});
|
||||
|
||||
await handler({
|
||||
session: baseSession,
|
||||
clientInfo: baseClientInfo,
|
||||
jellyfinConfig: {},
|
||||
itemId: 'item-4',
|
||||
});
|
||||
|
||||
assert.deepEqual(commands[1], ['loadfile', 'https://stream.example/video.m3u8', 'replace']);
|
||||
});
|
||||
|
||||
@@ -107,8 +107,8 @@ export function createPlayJellyfinItemInMpvHandler(deps: {
|
||||
deps.applyJellyfinMpvDefaults(mpvClient);
|
||||
deps.sendMpvCommand(['set_property', 'sub-auto', 'no']);
|
||||
const playbackUrl = applyStartTimeTicksToPlaybackUrl(plan.url, params.startTimeTicksOverride);
|
||||
deps.updateCurrentMediaTitle?.(plan.title);
|
||||
try {
|
||||
deps.updateCurrentMediaTitle?.(plan.title);
|
||||
deps.recordJellyfinPlaybackMetadata?.({
|
||||
mediaPath: playbackUrl,
|
||||
displayTitle: plan.title,
|
||||
@@ -119,7 +119,7 @@ export function createPlayJellyfinItemInMpvHandler(deps: {
|
||||
itemId: params.itemId,
|
||||
});
|
||||
} catch {
|
||||
// Best-effort stats metadata must not block playback startup.
|
||||
// Best-effort metadata/title hooks must not block playback startup.
|
||||
}
|
||||
deps.sendMpvCommand(['loadfile', playbackUrl, 'replace']);
|
||||
if (params.setQuitOnDisconnectArm !== false) {
|
||||
|
||||
@@ -331,6 +331,35 @@ test('preload jellyfin subtitles cleans previous cached subtitles before a new p
|
||||
assert.deepEqual(cleanupCalls, [['/tmp/subminer-jellyfin-subtitles-0']]);
|
||||
});
|
||||
|
||||
test('preload jellyfin subtitles logs cleanup failures without rejecting', async () => {
|
||||
const logs: string[] = [];
|
||||
let cleanupShouldFail = false;
|
||||
const preload = createPreloadJellyfinExternalSubtitlesHandler(
|
||||
makeDeps({
|
||||
listJellyfinSubtitleTracks: async () => [
|
||||
{ index: 0, language: 'eng', title: 'English', deliveryUrl: 'https://sub/a.srt' },
|
||||
],
|
||||
getMpvClient: () => ({ requestProperty: async () => [] }),
|
||||
cacheSubtitleTrack: async (track) => ({
|
||||
path: `/tmp/subminer-jellyfin-subtitles-${track.index}/track.srt`,
|
||||
cleanupDir: `/tmp/subminer-jellyfin-subtitles-${track.index}`,
|
||||
}),
|
||||
cleanupCachedSubtitles: () => {
|
||||
if (cleanupShouldFail) {
|
||||
throw new Error('cleanup failed');
|
||||
}
|
||||
},
|
||||
logDebug: (message) => logs.push(message),
|
||||
}),
|
||||
);
|
||||
|
||||
await preload({ session, clientInfo, itemId: 'item-1' });
|
||||
cleanupShouldFail = true;
|
||||
await assert.doesNotReject(() => preload({ session, clientInfo, itemId: 'item-2' }));
|
||||
|
||||
assert.deepEqual(logs, ['Failed to preload Jellyfin external subtitles']);
|
||||
});
|
||||
|
||||
test('preload jellyfin subtitles serializes overlapping preload runs', async () => {
|
||||
let releaseFirstList!: () => void;
|
||||
const firstListBlocked = new Promise<void>((resolve) => {
|
||||
|
||||
@@ -174,9 +174,7 @@ function hasExpectedExternalSubtitleTracks(
|
||||
return true;
|
||||
}
|
||||
const loadedExternalFilenames = new Set(
|
||||
tracks
|
||||
.filter((track) => track.externalFilename)
|
||||
.map((track) => track.externalFilename),
|
||||
tracks.filter((track) => track.externalFilename).map((track) => track.externalFilename),
|
||||
);
|
||||
return expectedExternalFilenames.every((filePath) => loadedExternalFilenames.has(filePath));
|
||||
}
|
||||
@@ -247,9 +245,8 @@ export function createPreloadJellyfinExternalSubtitlesHandler(deps: {
|
||||
clientInfo: JellyfinClientInfo;
|
||||
itemId: string;
|
||||
}): Promise<void> => {
|
||||
cleanupActiveCache();
|
||||
|
||||
try {
|
||||
cleanupActiveCache();
|
||||
const tracks = await deps.listJellyfinSubtitleTracks(
|
||||
params.session,
|
||||
params.clientInfo,
|
||||
|
||||
@@ -390,9 +390,5 @@ test('manual update check keeps current prerelease builds on configured stable c
|
||||
const result = await service.checkForUpdates({ source: 'manual' });
|
||||
|
||||
assert.equal(result.status, 'up-to-date');
|
||||
assert.deepEqual(calls, [
|
||||
'app:stable',
|
||||
'fetch:stable',
|
||||
'no-update:0.15.0-beta.3',
|
||||
]);
|
||||
assert.deepEqual(calls, ['app:stable', 'fetch:stable', 'no-update:0.15.0-beta.3']);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user