mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-28 06:22:45 -08:00
pretty
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
export type SubsyncEngine = "alass" | "ffsubsync";
|
||||
export type SubsyncEngine = 'alass' | 'ffsubsync';
|
||||
|
||||
export interface SubsyncCommandResult {
|
||||
ok: boolean;
|
||||
@@ -14,33 +14,22 @@ export interface SubsyncEngineExecutionContext {
|
||||
inputSubtitlePath: string;
|
||||
outputPath: string;
|
||||
audioStreamIndex: number | null;
|
||||
resolveExecutablePath: (
|
||||
configuredPath: string,
|
||||
commandName: string,
|
||||
) => string;
|
||||
resolveExecutablePath: (configuredPath: string, commandName: string) => string;
|
||||
resolvedPaths: {
|
||||
alassPath: string;
|
||||
ffsubsyncPath: string;
|
||||
};
|
||||
runCommand: (
|
||||
command: string,
|
||||
args: string[],
|
||||
) => Promise<SubsyncCommandResult>;
|
||||
runCommand: (command: string, args: string[]) => Promise<SubsyncCommandResult>;
|
||||
}
|
||||
|
||||
export interface SubsyncEngineProvider {
|
||||
engine: SubsyncEngine;
|
||||
execute: (
|
||||
context: SubsyncEngineExecutionContext,
|
||||
) => Promise<SubsyncCommandResult>;
|
||||
execute: (context: SubsyncEngineExecutionContext) => Promise<SubsyncCommandResult>;
|
||||
}
|
||||
|
||||
type SubsyncEngineProviderFactory = () => SubsyncEngineProvider;
|
||||
|
||||
const subsyncEngineProviderFactories = new Map<
|
||||
SubsyncEngine,
|
||||
SubsyncEngineProviderFactory
|
||||
>();
|
||||
const subsyncEngineProviderFactories = new Map<SubsyncEngine, SubsyncEngineProviderFactory>();
|
||||
|
||||
export function registerSubsyncEngineProvider(
|
||||
engine: SubsyncEngine,
|
||||
@@ -52,22 +41,17 @@ export function registerSubsyncEngineProvider(
|
||||
subsyncEngineProviderFactories.set(engine, factory);
|
||||
}
|
||||
|
||||
export function createSubsyncEngineProvider(
|
||||
engine: SubsyncEngine,
|
||||
): SubsyncEngineProvider | null {
|
||||
export function createSubsyncEngineProvider(engine: SubsyncEngine): SubsyncEngineProvider | null {
|
||||
const factory = subsyncEngineProviderFactories.get(engine);
|
||||
if (!factory) return null;
|
||||
return factory();
|
||||
}
|
||||
|
||||
function registerDefaultSubsyncEngineProviders(): void {
|
||||
registerSubsyncEngineProvider("alass", () => ({
|
||||
engine: "alass",
|
||||
registerSubsyncEngineProvider('alass', () => ({
|
||||
engine: 'alass',
|
||||
execute: async (context: SubsyncEngineExecutionContext) => {
|
||||
const alassPath = context.resolveExecutablePath(
|
||||
context.resolvedPaths.alassPath,
|
||||
"alass",
|
||||
);
|
||||
const alassPath = context.resolveExecutablePath(context.resolvedPaths.alassPath, 'alass');
|
||||
return context.runCommand(alassPath, [
|
||||
context.referenceFilePath,
|
||||
context.inputSubtitlePath,
|
||||
@@ -76,22 +60,16 @@ function registerDefaultSubsyncEngineProviders(): void {
|
||||
},
|
||||
}));
|
||||
|
||||
registerSubsyncEngineProvider("ffsubsync", () => ({
|
||||
engine: "ffsubsync",
|
||||
registerSubsyncEngineProvider('ffsubsync', () => ({
|
||||
engine: 'ffsubsync',
|
||||
execute: async (context: SubsyncEngineExecutionContext) => {
|
||||
const ffsubsyncPath = context.resolveExecutablePath(
|
||||
context.resolvedPaths.ffsubsyncPath,
|
||||
"ffsubsync",
|
||||
'ffsubsync',
|
||||
);
|
||||
const args = [
|
||||
context.videoPath,
|
||||
"-i",
|
||||
context.inputSubtitlePath,
|
||||
"-o",
|
||||
context.outputPath,
|
||||
];
|
||||
const args = [context.videoPath, '-i', context.inputSubtitlePath, '-o', context.outputPath];
|
||||
if (context.audioStreamIndex !== null) {
|
||||
args.push("--reference-stream", `0:${context.audioStreamIndex}`);
|
||||
args.push('--reference-stream', `0:${context.audioStreamIndex}`);
|
||||
}
|
||||
return context.runCommand(ffsubsyncPath, args);
|
||||
},
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { codecToExtension } from "./utils";
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { codecToExtension } from './utils';
|
||||
|
||||
test("codecToExtension maps stream/web formats to ffmpeg extractable extensions", () => {
|
||||
assert.equal(codecToExtension("subrip"), "srt");
|
||||
assert.equal(codecToExtension("webvtt"), "vtt");
|
||||
assert.equal(codecToExtension("vtt"), "vtt");
|
||||
assert.equal(codecToExtension("ttml"), "ttml");
|
||||
test('codecToExtension maps stream/web formats to ffmpeg extractable extensions', () => {
|
||||
assert.equal(codecToExtension('subrip'), 'srt');
|
||||
assert.equal(codecToExtension('webvtt'), 'vtt');
|
||||
assert.equal(codecToExtension('vtt'), 'vtt');
|
||||
assert.equal(codecToExtension('ttml'), 'ttml');
|
||||
});
|
||||
|
||||
test("codecToExtension returns null for unsupported codecs", () => {
|
||||
assert.equal(codecToExtension("unsupported-codec"), null);
|
||||
test('codecToExtension returns null for unsupported codecs', () => {
|
||||
assert.equal(codecToExtension('unsupported-codec'), null);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as fs from "fs";
|
||||
import * as childProcess from "child_process";
|
||||
import { DEFAULT_CONFIG } from "../config";
|
||||
import { SubsyncConfig, SubsyncMode } from "../types";
|
||||
import * as fs from 'fs';
|
||||
import * as childProcess from 'child_process';
|
||||
import { DEFAULT_CONFIG } from '../config';
|
||||
import { SubsyncConfig, SubsyncMode } from '../types';
|
||||
|
||||
export interface MpvTrack {
|
||||
id?: number;
|
||||
@@ -11,8 +11,8 @@ export interface MpvTrack {
|
||||
lang?: string;
|
||||
title?: string;
|
||||
codec?: string;
|
||||
"ff-index"?: number;
|
||||
"external-filename"?: string;
|
||||
'ff-index'?: number;
|
||||
'external-filename'?: string;
|
||||
}
|
||||
|
||||
export interface SubsyncResolvedConfig {
|
||||
@@ -23,9 +23,9 @@ export interface SubsyncResolvedConfig {
|
||||
}
|
||||
|
||||
const DEFAULT_SUBSYNC_EXECUTABLE_PATHS = {
|
||||
alass: "/usr/bin/alass",
|
||||
ffsubsync: "/usr/bin/ffsubsync",
|
||||
ffmpeg: "/usr/bin/ffmpeg",
|
||||
alass: '/usr/bin/alass',
|
||||
ffsubsync: '/usr/bin/ffsubsync',
|
||||
ffmpeg: '/usr/bin/ffmpeg',
|
||||
} as const;
|
||||
|
||||
export interface SubsyncContext {
|
||||
@@ -44,9 +44,7 @@ export interface CommandResult {
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export function getSubsyncConfig(
|
||||
config: SubsyncConfig | undefined,
|
||||
): SubsyncResolvedConfig {
|
||||
export function getSubsyncConfig(config: SubsyncConfig | undefined): SubsyncResolvedConfig {
|
||||
const resolvePath = (value: string | undefined, fallback: string): string => {
|
||||
const trimmed = value?.trim();
|
||||
return trimmed && trimmed.length > 0 ? trimmed : fallback;
|
||||
@@ -54,23 +52,14 @@ export function getSubsyncConfig(
|
||||
|
||||
return {
|
||||
defaultMode: config?.defaultMode ?? DEFAULT_CONFIG.subsync.defaultMode,
|
||||
alassPath: resolvePath(
|
||||
config?.alass_path,
|
||||
DEFAULT_SUBSYNC_EXECUTABLE_PATHS.alass,
|
||||
),
|
||||
ffsubsyncPath: resolvePath(
|
||||
config?.ffsubsync_path,
|
||||
DEFAULT_SUBSYNC_EXECUTABLE_PATHS.ffsubsync,
|
||||
),
|
||||
ffmpegPath: resolvePath(
|
||||
config?.ffmpeg_path,
|
||||
DEFAULT_SUBSYNC_EXECUTABLE_PATHS.ffmpeg,
|
||||
),
|
||||
alassPath: resolvePath(config?.alass_path, DEFAULT_SUBSYNC_EXECUTABLE_PATHS.alass),
|
||||
ffsubsyncPath: resolvePath(config?.ffsubsync_path, DEFAULT_SUBSYNC_EXECUTABLE_PATHS.ffsubsync),
|
||||
ffmpegPath: resolvePath(config?.ffmpeg_path, DEFAULT_SUBSYNC_EXECUTABLE_PATHS.ffmpeg),
|
||||
};
|
||||
}
|
||||
|
||||
export function hasPathSeparators(value: string): boolean {
|
||||
return value.includes("/") || value.includes("\\");
|
||||
return value.includes('/') || value.includes('\\');
|
||||
}
|
||||
|
||||
export function fileExists(pathOrEmpty: string): boolean {
|
||||
@@ -83,17 +72,14 @@ export function fileExists(pathOrEmpty: string): boolean {
|
||||
}
|
||||
|
||||
export function formatTrackLabel(track: MpvTrack): string {
|
||||
const trackId = typeof track.id === "number" ? track.id : -1;
|
||||
const source = track.external ? "External" : "Internal";
|
||||
const lang = track.lang || track.title || "unknown";
|
||||
const active = track.selected ? " (active)" : "";
|
||||
const trackId = typeof track.id === 'number' ? track.id : -1;
|
||||
const source = track.external ? 'External' : 'Internal';
|
||||
const lang = track.lang || track.title || 'unknown';
|
||||
const active = track.selected ? ' (active)' : '';
|
||||
return `${source} #${trackId} - ${lang}${active}`;
|
||||
}
|
||||
|
||||
export function getTrackById(
|
||||
tracks: MpvTrack[],
|
||||
trackId: number | null,
|
||||
): MpvTrack | null {
|
||||
export function getTrackById(tracks: MpvTrack[], trackId: number | null): MpvTrack | null {
|
||||
if (trackId === null) return null;
|
||||
return tracks.find((track) => track.id === trackId) ?? null;
|
||||
}
|
||||
@@ -102,15 +88,15 @@ export function codecToExtension(codec: string | undefined): string | null {
|
||||
if (!codec) return null;
|
||||
const normalized = codec.toLowerCase();
|
||||
if (
|
||||
normalized === "subrip" ||
|
||||
normalized === "srt" ||
|
||||
normalized === "text" ||
|
||||
normalized === "mov_text"
|
||||
normalized === 'subrip' ||
|
||||
normalized === 'srt' ||
|
||||
normalized === 'text' ||
|
||||
normalized === 'mov_text'
|
||||
)
|
||||
return "srt";
|
||||
if (normalized === "ass" || normalized === "ssa") return "ass";
|
||||
if (normalized === "webvtt" || normalized === "vtt") return "vtt";
|
||||
if (normalized === "ttml") return "ttml";
|
||||
return 'srt';
|
||||
if (normalized === 'ass' || normalized === 'ssa') return 'ass';
|
||||
if (normalized === 'webvtt' || normalized === 'vtt') return 'vtt';
|
||||
if (normalized === 'ttml') return 'ttml';
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -121,21 +107,21 @@ export function runCommand(
|
||||
): Promise<CommandResult> {
|
||||
return new Promise((resolve) => {
|
||||
const child = childProcess.spawn(executable, args, {
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
const timeout = setTimeout(() => {
|
||||
child.kill("SIGKILL");
|
||||
child.kill('SIGKILL');
|
||||
}, timeoutMs);
|
||||
|
||||
child.stdout.on("data", (chunk: Buffer) => {
|
||||
child.stdout.on('data', (chunk: Buffer) => {
|
||||
stdout += chunk.toString();
|
||||
});
|
||||
child.stderr.on("data", (chunk: Buffer) => {
|
||||
child.stderr.on('data', (chunk: Buffer) => {
|
||||
stderr += chunk.toString();
|
||||
});
|
||||
child.on("error", (error: Error) => {
|
||||
child.on('error', (error: Error) => {
|
||||
clearTimeout(timeout);
|
||||
resolve({
|
||||
ok: false,
|
||||
@@ -145,7 +131,7 @@ export function runCommand(
|
||||
error: error.message,
|
||||
});
|
||||
});
|
||||
child.on("close", (code: number | null) => {
|
||||
child.on('close', (code: number | null) => {
|
||||
clearTimeout(timeout);
|
||||
resolve({
|
||||
ok: code === 0,
|
||||
|
||||
Reference in New Issue
Block a user