mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-05-26 00:55:16 -07:00
feat: inject bundled mpv plugin for managed launches, remove legacy glob (#62)
* feat: inject bundled mpv plugin for managed launches, remove legacy glob - SubMiner-managed launcher and Windows shortcut launches inject the bundled plugin when no global plugin is detected - First-run setup detects and removes legacy global plugin files via OS trash before managed playback starts - Makefile `install-plugin` target and Windows config-rewrite script removed; Linux/macOS install now copies plugin to app data dir - AniList stats search and post-watch tracking now go through the shared rate limiter - Stats cover-art lookup reuses cached AniList data before issuing a new request - Closing mpv in a launcher-managed session now terminates the background Electron app * harden bootstrap version load and clean plugin on uninstall - Use pcall for version.lua in bootstrap.lua so missing version module does not crash plugin startup - Remove plugin/subminer from app-data dirs in uninstall-linux and uninstall-macos targets - Add Lua compat test asserting bootstrap uses defensive pcall for version load - Add release-workflow test asserting uninstall targets clean bundled plugin dirs - Delete completed planning document
This commit is contained in:
@@ -2,6 +2,7 @@ import fs from 'node:fs';
|
||||
import { spawn, spawnSync } from 'node:child_process';
|
||||
import { buildMpvLaunchModeArgs } from '../../shared/mpv-launch-mode';
|
||||
import type { MpvLaunchMode } from '../../types/config';
|
||||
import type { InstalledMpvPluginDetection } from './first-run-setup-plugin';
|
||||
|
||||
export interface WindowsMpvLaunchDeps {
|
||||
getEnv: (name: string) => string | undefined;
|
||||
@@ -13,6 +14,15 @@ export interface WindowsMpvLaunchDeps {
|
||||
|
||||
export type ConfiguredWindowsMpvPathStatus = 'blank' | 'configured' | 'invalid';
|
||||
|
||||
export interface WindowsMpvRuntimePluginPolicy {
|
||||
detectInstalledMpvPlugin?: (mpvPath: string) => InstalledMpvPluginDetection;
|
||||
notifyInstalledPluginDetected?: (detection: InstalledMpvPluginDetection) => void;
|
||||
resolveInstalledPluginBeforeLaunch?: (
|
||||
detection: InstalledMpvPluginDetection,
|
||||
mpvPath: string,
|
||||
) => Promise<'removed' | 'continue' | 'cancel'> | 'removed' | 'continue' | 'cancel';
|
||||
}
|
||||
|
||||
function normalizeCandidate(candidate: string | undefined): string {
|
||||
return typeof candidate === 'string' ? candidate.trim() : '';
|
||||
}
|
||||
@@ -100,10 +110,12 @@ export function buildWindowsMpvLaunchArgs(
|
||||
typeof pluginEntrypointPath === 'string' && pluginEntrypointPath.trim().length > 0
|
||||
? `--script=${pluginEntrypointPath.trim()}`
|
||||
: null;
|
||||
const scriptOptPairs = scriptEntrypoint
|
||||
const hasBinaryPath = typeof binaryPath === 'string' && binaryPath.trim().length > 0;
|
||||
const shouldPassSubminerScriptOpts = scriptEntrypoint || hasBinaryPath;
|
||||
const scriptOptPairs = shouldPassSubminerScriptOpts
|
||||
? [`subminer-socket_path=${inputIpcServer.replace(/,/g, '\\,')}`]
|
||||
: [];
|
||||
if (scriptEntrypoint && typeof binaryPath === 'string' && binaryPath.trim().length > 0) {
|
||||
if (hasBinaryPath) {
|
||||
scriptOptPairs.unshift(`subminer-binary_path=${binaryPath.trim().replace(/,/g, '\\,')}`);
|
||||
}
|
||||
const scriptOpts = scriptOptPairs.length > 0 ? `--script-opts=${scriptOptPairs.join(',')}` : null;
|
||||
@@ -136,6 +148,7 @@ export async function launchWindowsMpv(
|
||||
pluginEntrypointPath?: string,
|
||||
configuredMpvPath?: string,
|
||||
launchMode: MpvLaunchMode = 'normal',
|
||||
runtimePluginPolicy?: WindowsMpvRuntimePluginPolicy,
|
||||
): Promise<{ ok: boolean; mpvPath: string }> {
|
||||
const normalizedConfiguredPath = normalizeCandidate(configuredMpvPath);
|
||||
const mpvPath = resolveWindowsMpvPath(deps, normalizedConfiguredPath);
|
||||
@@ -150,9 +163,36 @@ export async function launchWindowsMpv(
|
||||
}
|
||||
|
||||
try {
|
||||
let installedPlugin = runtimePluginPolicy?.detectInstalledMpvPlugin?.(mpvPath);
|
||||
let installedPluginPrompted = false;
|
||||
if (installedPlugin?.installed) {
|
||||
const resolution = await runtimePluginPolicy?.resolveInstalledPluginBeforeLaunch?.(
|
||||
installedPlugin,
|
||||
mpvPath,
|
||||
);
|
||||
installedPluginPrompted = resolution != null;
|
||||
if (resolution === 'cancel') {
|
||||
return { ok: false, mpvPath };
|
||||
}
|
||||
if (resolution === 'removed') {
|
||||
installedPlugin = runtimePluginPolicy?.detectInstalledMpvPlugin?.(mpvPath);
|
||||
}
|
||||
}
|
||||
const runtimePluginEntrypointPath = installedPlugin?.installed
|
||||
? undefined
|
||||
: pluginEntrypointPath;
|
||||
if (installedPlugin?.installed && !installedPluginPrompted) {
|
||||
runtimePluginPolicy?.notifyInstalledPluginDetected?.(installedPlugin);
|
||||
}
|
||||
await deps.spawnDetached(
|
||||
mpvPath,
|
||||
buildWindowsMpvLaunchArgs(targets, extraArgs, binaryPath, pluginEntrypointPath, launchMode),
|
||||
buildWindowsMpvLaunchArgs(
|
||||
targets,
|
||||
extraArgs,
|
||||
binaryPath,
|
||||
runtimePluginEntrypointPath,
|
||||
launchMode,
|
||||
),
|
||||
);
|
||||
return { ok: true, mpvPath };
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user