mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-27 18:22:41 -08:00
chore(scripts): align tooling with runtime/service updates
This commit is contained in:
2
Makefile
2
Makefile
@@ -134,7 +134,7 @@ build-macos-unsigned: deps
|
|||||||
build-launcher:
|
build-launcher:
|
||||||
@printf '%s\n' "[INFO] Bundling launcher script"
|
@printf '%s\n' "[INFO] Bundling launcher script"
|
||||||
@bun build ./launcher/main.ts --target=bun --packages=bundle --outfile=subminer
|
@bun build ./launcher/main.ts --target=bun --packages=bundle --outfile=subminer
|
||||||
@sed -i '1s|^// @bun|#!/usr/bin/env bun\n// @bun|' subminer
|
@python3 -c 'from pathlib import Path; p=Path("subminer"); c=p.read_text(); c=("#!/usr/bin/env bun\n"+c) if not c.startswith("#!/usr/bin/env bun\n") else c; p.write_text(c)'
|
||||||
@chmod +x subminer
|
@chmod +x subminer
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import path from "node:path";
|
|||||||
import process from "node:process";
|
import process from "node:process";
|
||||||
|
|
||||||
import { createTokenizerDepsRuntime, tokenizeSubtitle } from "../src/core/services/tokenizer.js";
|
import { createTokenizerDepsRuntime, tokenizeSubtitle } from "../src/core/services/tokenizer.js";
|
||||||
import { createFrequencyDictionaryLookup } from "../src/core/services/index.js";
|
import { createFrequencyDictionaryLookup } from "../src/core/services/frequency-dictionary.js";
|
||||||
import { MecabTokenizer } from "../src/mecab-tokenizer.js";
|
import { MecabTokenizer } from "../src/mecab-tokenizer.js";
|
||||||
import type { MergedToken, FrequencyDictionaryLookup } from "../src/types.js";
|
import type { MergedToken, FrequencyDictionaryLookup } from "../src/types.js";
|
||||||
|
|
||||||
@@ -496,6 +496,27 @@ interface YomitanRuntimeState {
|
|||||||
note?: string;
|
note?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function withTimeout<T>(
|
||||||
|
promise: Promise<T>,
|
||||||
|
timeoutMs: number,
|
||||||
|
label: string,
|
||||||
|
): Promise<T> {
|
||||||
|
return new Promise<T>((resolve, reject) => {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
reject(new Error(`${label} timed out after ${timeoutMs}ms`));
|
||||||
|
}, timeoutMs);
|
||||||
|
promise
|
||||||
|
.then((value) => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
resolve(value);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function destroyUnknownParserWindow(window: unknown): void {
|
function destroyUnknownParserWindow(window: unknown): void {
|
||||||
if (!window || typeof window !== "object") {
|
if (!window || typeof window !== "object") {
|
||||||
return;
|
return;
|
||||||
@@ -785,30 +806,31 @@ async function main(): Promise<void> {
|
|||||||
)
|
)
|
||||||
: null;
|
: null;
|
||||||
const hasYomitan = Boolean(yomitanState?.available && yomitanState?.yomitanExt);
|
const hasYomitan = Boolean(yomitanState?.available && yomitanState?.yomitanExt);
|
||||||
|
let useYomitan = hasYomitan;
|
||||||
|
|
||||||
const deps = createTokenizerDepsRuntime({
|
const deps = createTokenizerDepsRuntime({
|
||||||
getYomitanExt: () =>
|
getYomitanExt: () =>
|
||||||
(hasYomitan ? yomitanState!.yomitanExt : null) as never,
|
(useYomitan ? yomitanState!.yomitanExt : null) as never,
|
||||||
getYomitanParserWindow: () =>
|
getYomitanParserWindow: () =>
|
||||||
(hasYomitan ? yomitanState!.parserWindow : null) as never,
|
(useYomitan ? yomitanState!.parserWindow : null) as never,
|
||||||
setYomitanParserWindow: (window) => {
|
setYomitanParserWindow: (window) => {
|
||||||
if (!hasYomitan) {
|
if (!useYomitan) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
yomitanState!.parserWindow = window;
|
yomitanState!.parserWindow = window;
|
||||||
},
|
},
|
||||||
getYomitanParserReadyPromise: () =>
|
getYomitanParserReadyPromise: () =>
|
||||||
(hasYomitan ? yomitanState!.parserReadyPromise : null) as never,
|
(useYomitan ? yomitanState!.parserReadyPromise : null) as never,
|
||||||
setYomitanParserReadyPromise: (promise) => {
|
setYomitanParserReadyPromise: (promise) => {
|
||||||
if (!hasYomitan) {
|
if (!useYomitan) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
yomitanState!.parserReadyPromise = promise;
|
yomitanState!.parserReadyPromise = promise;
|
||||||
},
|
},
|
||||||
getYomitanParserInitPromise: () =>
|
getYomitanParserInitPromise: () =>
|
||||||
(hasYomitan ? yomitanState!.parserInitPromise : null) as never,
|
(useYomitan ? yomitanState!.parserInitPromise : null) as never,
|
||||||
setYomitanParserInitPromise: (promise) => {
|
setYomitanParserInitPromise: (promise) => {
|
||||||
if (!hasYomitan) {
|
if (!useYomitan) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
yomitanState!.parserInitPromise = promise;
|
yomitanState!.parserInitPromise = promise;
|
||||||
@@ -823,7 +845,31 @@ async function main(): Promise<void> {
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const subtitleData = await tokenizeSubtitle(args.input, deps);
|
let subtitleData;
|
||||||
|
if (useYomitan) {
|
||||||
|
try {
|
||||||
|
subtitleData = await withTimeout(
|
||||||
|
tokenizeSubtitle(args.input, deps),
|
||||||
|
8000,
|
||||||
|
"Yomitan tokenizer",
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
useYomitan = false;
|
||||||
|
destroyUnknownParserWindow(yomitanState?.parserWindow ?? null);
|
||||||
|
if (yomitanState) {
|
||||||
|
yomitanState.parserWindow = null;
|
||||||
|
yomitanState.parserReadyPromise = null;
|
||||||
|
yomitanState.parserInitPromise = null;
|
||||||
|
const fallbackNote = error instanceof Error ? error.message : "Yomitan tokenizer timed out";
|
||||||
|
yomitanState.note = yomitanState.note
|
||||||
|
? `${yomitanState.note}; ${fallbackNote}`
|
||||||
|
: fallbackNote;
|
||||||
|
}
|
||||||
|
subtitleData = await tokenizeSubtitle(args.input, deps);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
subtitleData = await tokenizeSubtitle(args.input, deps);
|
||||||
|
}
|
||||||
const tokenCount = subtitleData.tokens?.length ?? 0;
|
const tokenCount = subtitleData.tokens?.length ?? 0;
|
||||||
const mergedCount = subtitleData.tokens?.filter((token) => token.isMerged).length ?? 0;
|
const mergedCount = subtitleData.tokens?.filter((token) => token.isMerged).length ?? 0;
|
||||||
const tokens =
|
const tokens =
|
||||||
@@ -835,7 +881,7 @@ async function main(): Promise<void> {
|
|||||||
const diagnostics = {
|
const diagnostics = {
|
||||||
yomitan: {
|
yomitan: {
|
||||||
available: Boolean(yomitanState?.available),
|
available: Boolean(yomitanState?.available),
|
||||||
loaded: hasYomitan,
|
loaded: useYomitan,
|
||||||
forceMecabOnly: args.forceMecabOnly,
|
forceMecabOnly: args.forceMecabOnly,
|
||||||
note: yomitanState?.note ?? null,
|
note: yomitanState?.note ?? null,
|
||||||
},
|
},
|
||||||
@@ -848,7 +894,7 @@ async function main(): Promise<void> {
|
|||||||
sourceHint:
|
sourceHint:
|
||||||
tokenCount === 0
|
tokenCount === 0
|
||||||
? "none"
|
? "none"
|
||||||
: hasYomitan ? "yomitan-merged" : "mecab-merge",
|
: useYomitan ? "yomitan-merged" : "mecab-merge",
|
||||||
mergedTokenCount: mergedCount,
|
mergedTokenCount: mergedCount,
|
||||||
totalTokenCount: tokenCount,
|
totalTokenCount: tokenCount,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
|
import os from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import process from "node:process";
|
import process from "node:process";
|
||||||
|
|
||||||
@@ -54,6 +55,12 @@ interface YomitanRuntimeState {
|
|||||||
parserInitPromise: Promise<boolean> | null;
|
parserInitPromise: Promise<boolean> | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DEFAULT_YOMITAN_USER_DATA_PATH = path.join(
|
||||||
|
os.homedir(),
|
||||||
|
".config",
|
||||||
|
"SubMiner",
|
||||||
|
);
|
||||||
|
|
||||||
function destroyParserWindow(window: Electron.BrowserWindow | null): void {
|
function destroyParserWindow(window: Electron.BrowserWindow | null): void {
|
||||||
if (!window || window.isDestroyed()) {
|
if (!window || window.isDestroyed()) {
|
||||||
return;
|
return;
|
||||||
@@ -72,11 +79,11 @@ async function shutdownYomitanRuntime(yomitan: YomitanRuntimeState): Promise<voi
|
|||||||
function parseCliArgs(argv: string[]): CliOptions {
|
function parseCliArgs(argv: string[]): CliOptions {
|
||||||
const args = [...argv];
|
const args = [...argv];
|
||||||
const inputParts: string[] = [];
|
const inputParts: string[] = [];
|
||||||
let emitPretty = false;
|
let emitPretty = true;
|
||||||
let emitJson = false;
|
let emitJson = false;
|
||||||
let forceMecabOnly = false;
|
let forceMecabOnly = false;
|
||||||
let yomitanExtensionPath: string | undefined;
|
let yomitanExtensionPath: string | undefined;
|
||||||
let yomitanUserDataPath: string | undefined;
|
let yomitanUserDataPath: string | undefined = DEFAULT_YOMITAN_USER_DATA_PATH;
|
||||||
let mecabCommand: string | undefined;
|
let mecabCommand: string | undefined;
|
||||||
let mecabDictionaryPath: string | undefined;
|
let mecabDictionaryPath: string | undefined;
|
||||||
|
|
||||||
@@ -212,7 +219,7 @@ function printUsage(): void {
|
|||||||
--json Emit machine-readable JSON output.
|
--json Emit machine-readable JSON output.
|
||||||
--force-mecab Skip Yomitan parser setup and test MeCab fallback only.
|
--force-mecab Skip Yomitan parser setup and test MeCab fallback only.
|
||||||
--yomitan-extension <path> Optional path to Yomitan extension directory.
|
--yomitan-extension <path> Optional path to Yomitan extension directory.
|
||||||
--yomitan-user-data <path> Optional Electron userData directory.
|
--yomitan-user-data <path> Optional Electron userData directory (default: ~/.config/SubMiner).
|
||||||
--mecab-command <path> Optional MeCab binary path (default: mecab).
|
--mecab-command <path> Optional MeCab binary path (default: mecab).
|
||||||
--mecab-dictionary <path> Optional MeCab dictionary directory.
|
--mecab-dictionary <path> Optional MeCab dictionary directory.
|
||||||
-h, --help Show usage.
|
-h, --help Show usage.
|
||||||
|
|||||||
Reference in New Issue
Block a user