fix(launcher): restore Matroska thumbnails in Linux rofi picker (#210)

This commit is contained in:
2026-08-18 20:30:58 -07:00
committed by GitHub
parent 4ed878270f
commit 2a77ba9dd4
26 changed files with 648 additions and 119 deletions
+8
View File
@@ -23,6 +23,7 @@ import {
type HistorySeriesEntry,
} from '../history.js';
import type { Args } from '../types.js';
import { ensureLinuxRuntimePluginAvailable } from '../runtime-plugin-preflight.js';
import type { LauncherCommandContext } from './context.js';
export type HistorySessionAction = 'previous' | 'replay' | 'next' | 'browse' | 'quit';
@@ -333,6 +334,13 @@ export async function runHistoryCommand(
const { args, scriptPath } = context;
checkPickerDependencies(args);
if (args.useRofi) {
await ensureLinuxRuntimePluginAvailable({
appPath: context.appPath ?? undefined,
scriptPath,
logLevel: args.logLevel,
});
}
const themePath = args.useRofi ? findRofiTheme(scriptPath) : null;
const dbPath = resolveImmersionDbPath();
+8
View File
@@ -2,6 +2,7 @@ import { fail } from '../log.js';
import { runAppCommandWithInherit } from '../mpv.js';
import { commandExists } from '../util.js';
import { runJellyfinPlayMenu } from '../jellyfin.js';
import { ensureLinuxRuntimePluginAvailable } from '../runtime-plugin-preflight.js';
import { shouldForwardLogLevel } from '../types.js';
import type { LauncherCommandContext } from './context.js';
@@ -64,6 +65,13 @@ export async function runJellyfinCommand(context: LauncherCommandContext): Promi
if (args.useRofi && !commandExists('rofi')) {
fail('rofi not found. Install rofi or omit -R for fzf.');
}
if (args.useRofi) {
await ensureLinuxRuntimePluginAvailable({
appPath,
scriptPath,
logLevel: args.logLevel,
});
}
await runJellyfinPlayMenu(appPath, args, scriptPath, mpvSocketPath);
return true;
}
@@ -496,3 +496,39 @@ test('playback command ensures Linux runtime plugin before mpv launch', async ()
assert.deepEqual(calls, ['plugin', 'startMpv']);
});
test('rofi playback repairs support assets before opening the picker', async () => {
const context = createContext();
context.args = {
...context.args,
target: '',
targetKind: '',
useRofi: true,
};
const calls: string[] = [];
await runPlaybackCommandWithDeps(context, {
ensurePlaybackSetupReady: async () => {},
ensureRuntimePluginReady: async () => {
calls.push('assets');
},
chooseTarget: async () => {
calls.push('picker');
return { target: '/tmp/movie.mkv', kind: 'file' };
},
checkPickerDependencies: () => {},
checkDependencies: () => {},
registerCleanup: () => {},
startMpv: async () => {
calls.push('startMpv');
},
waitForUnixSocketReady: async () => true,
startOverlay: async () => {},
launchAppCommandDetached: () => {},
log: () => {},
cleanupPlaybackSession: async () => {},
getMpvProc: () => null,
});
assert.deepEqual(calls, ['assets', 'picker', 'startMpv']);
});
+15 -2
View File
@@ -157,6 +157,7 @@ export async function runPlaybackCommand(context: LauncherCommandContext): Promi
});
},
chooseTarget,
checkPickerDependencies,
checkDependencies,
registerCleanup,
startMpv,
@@ -177,6 +178,7 @@ type PlaybackCommandDeps = {
args: Args,
scriptPath: string,
) => Promise<{ target: string; kind: 'file' | 'url' } | null>;
checkPickerDependencies?: (args: Args) => void;
checkDependencies: (args: Args) => void;
registerCleanup: (context: LauncherCommandContext) => void;
startMpv: typeof startMpv;
@@ -201,7 +203,18 @@ export async function runPlaybackCommandWithDeps(
await deps.ensurePlaybackSetupReady(context);
if (!args.target) {
checkPickerDependencies(args);
(deps.checkPickerDependencies ?? checkPickerDependencies)(args);
}
let runtimeAssetsReady = false;
const ensureRuntimeAssetsReady = async (): Promise<void> => {
if (runtimeAssetsReady) return;
await deps.ensureRuntimePluginReady(context);
runtimeAssetsReady = true;
};
if (!args.target && args.useRofi) {
await ensureRuntimeAssetsReady();
}
const targetChoice = await deps.chooseTarget(args, scriptPath);
@@ -266,7 +279,7 @@ export async function runPlaybackCommandWithDeps(
);
}
await deps.ensureRuntimePluginReady(context);
await ensureRuntimeAssetsReady();
await deps.startMpv(
selectedTarget.target,
+6
View File
@@ -36,6 +36,11 @@ test('runUpdateCommand updates directly on Linux without launching Electron', as
launcher: { status: 'updated' },
supportAssets: [
{ status: 'updated', component: 'theme', message: 'Installed theme.' },
{
status: 'updated',
component: 'thumbnailer',
message: 'Installed rofi thumbnailer.',
},
{ status: 'skipped', component: 'plugin', message: 'Plugin already up to date.' },
],
};
@@ -52,6 +57,7 @@ test('runUpdateCommand updates directly on Linux without launching Electron', as
'info:AppImage update: updated',
'info:Launcher update: updated',
'info:Support assets (theme) update: updated - Installed theme.',
'info:Support assets (thumbnailer) update: updated - Installed rofi thumbnailer.',
'info:Support assets (plugin) update: skipped - Plugin already up to date.',
]);
});
+10 -13
View File
@@ -21,7 +21,10 @@ import {
parseSha256Sums,
type FetchLike,
} from '../../src/main/runtime/update/release-assets.js';
import { updateSupportAssetsFromRelease } from '../../src/main/runtime/update/support-assets.js';
import {
updateSupportAssetsFromRelease,
type SupportAssetsUpdateResult,
} from '../../src/main/runtime/update/support-assets.js';
type UpdateCommandResponse = {
ok: boolean;
@@ -36,15 +39,14 @@ type DirectReleaseUpdateRequest = {
channel: UpdateChannel;
};
type DirectSupportAssetsUpdateResult = Omit<SupportAssetsUpdateResult, 'status'> & {
status: string;
};
type DirectReleaseUpdateResult = {
appImage: { status: string; command?: string; message?: string };
launcher: { status: string; command?: string; message?: string };
supportAssets: Array<{
status: string;
component?: 'theme' | 'plugin';
command?: string;
message?: string;
}>;
supportAssets: DirectSupportAssetsUpdateResult[];
};
type UpdateCommandDeps = {
@@ -129,12 +131,7 @@ function readUpdateChannel(root: Record<string, unknown> | null): UpdateChannel
function logUpdateResult(
label: string,
result: {
status: string;
component?: 'theme' | 'plugin';
command?: string;
message?: string;
},
result: DirectSupportAssetsUpdateResult,
configuredLogLevel: NonNullable<LauncherCommandContext['args']['logLevel']>,
deps: Pick<UpdateCommandDeps, 'log'>,
): void {