mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-27 18:22:41 -08:00
139 lines
3.9 KiB
TypeScript
139 lines
3.9 KiB
TypeScript
import type { SubtitleData } from '../../types';
|
|
|
|
export const HOVER_SCRIPT_NAME = 'subminer';
|
|
export const HOVER_TOKEN_MESSAGE = 'subminer-hover-token';
|
|
|
|
const DEFAULT_HOVER_TOKEN_COLOR = 'C6A0F6';
|
|
const DEFAULT_TOKEN_COLOR = 'FFFFFF';
|
|
|
|
export type HoverPayloadToken = {
|
|
text: string;
|
|
index: number;
|
|
startPos: number | null;
|
|
endPos: number | null;
|
|
};
|
|
|
|
export type HoverTokenPayload = {
|
|
revision: number;
|
|
subtitle: string | null;
|
|
hoveredTokenIndex: number | null;
|
|
tokens: HoverPayloadToken[];
|
|
colors: {
|
|
base: string;
|
|
hover: string;
|
|
};
|
|
};
|
|
|
|
type HoverTokenInput = {
|
|
subtitle: SubtitleData | null;
|
|
hoveredTokenIndex: number | null;
|
|
revision: number;
|
|
hoverColor?: string | null;
|
|
};
|
|
|
|
function normalizeHexColor(color: string | null | undefined, fallback: string): string {
|
|
if (typeof color !== 'string') {
|
|
return fallback;
|
|
}
|
|
const normalized = color.trim().replace(/^#/, '').toUpperCase();
|
|
return /^[0-9A-F]{6}$/.test(normalized) ? normalized : fallback;
|
|
}
|
|
|
|
function sanitizeSubtitleText(text: string): string {
|
|
return text
|
|
.replace(/\\N/g, '\n')
|
|
.replace(/\\n/g, '\n')
|
|
.replace(/\{[^}]*\}/g, '')
|
|
.trim();
|
|
}
|
|
|
|
function sanitizeTokenSurface(surface: unknown): string {
|
|
return typeof surface === 'string' ? surface : '';
|
|
}
|
|
|
|
function hasHoveredToken(subtitle: SubtitleData | null, hoveredTokenIndex: number | null): boolean {
|
|
if (!subtitle || hoveredTokenIndex === null || hoveredTokenIndex < 0) {
|
|
return false;
|
|
}
|
|
|
|
return subtitle.tokens?.some((token, index) => index === hoveredTokenIndex) ?? false;
|
|
}
|
|
|
|
export function buildHoveredTokenPayload(input: HoverTokenInput): HoverTokenPayload {
|
|
const { subtitle, hoveredTokenIndex, revision, hoverColor } = input;
|
|
|
|
const tokens: HoverPayloadToken[] = [];
|
|
|
|
if (subtitle?.tokens && subtitle.tokens.length > 0) {
|
|
for (let tokenIndex = 0; tokenIndex < subtitle.tokens.length; tokenIndex += 1) {
|
|
const token = subtitle.tokens[tokenIndex];
|
|
if (!token) {
|
|
continue;
|
|
}
|
|
const surface = sanitizeTokenSurface(token?.surface);
|
|
if (!surface || surface.trim().length === 0) {
|
|
continue;
|
|
}
|
|
|
|
tokens.push({
|
|
text: surface,
|
|
index: tokenIndex,
|
|
startPos: Number.isFinite(token.startPos) ? token.startPos : null,
|
|
endPos: Number.isFinite(token.endPos) ? token.endPos : null,
|
|
});
|
|
}
|
|
}
|
|
|
|
return {
|
|
revision,
|
|
subtitle: subtitle ? sanitizeSubtitleText(subtitle.text) : null,
|
|
hoveredTokenIndex:
|
|
hoveredTokenIndex !== null && hoveredTokenIndex >= 0 ? hoveredTokenIndex : null,
|
|
tokens,
|
|
colors: {
|
|
base: DEFAULT_TOKEN_COLOR,
|
|
hover: normalizeHexColor(hoverColor, DEFAULT_HOVER_TOKEN_COLOR),
|
|
},
|
|
};
|
|
}
|
|
|
|
export function buildHoveredTokenMessageCommand(payload: HoverTokenPayload): (string | number)[] {
|
|
return [
|
|
'script-message-to',
|
|
HOVER_SCRIPT_NAME,
|
|
HOVER_TOKEN_MESSAGE,
|
|
JSON.stringify(payload),
|
|
];
|
|
}
|
|
|
|
export function createApplyHoveredTokenOverlayHandler(deps: {
|
|
getMpvClient: () => {
|
|
connected: boolean;
|
|
send: (payload: { command: (string | number)[] }) => boolean;
|
|
} | null;
|
|
getCurrentSubtitleData: () => SubtitleData | null;
|
|
getHoveredTokenIndex: () => number | null;
|
|
getHoveredSubtitleRevision: () => number;
|
|
getHoverTokenColor: () => string | null;
|
|
}) {
|
|
return (): void => {
|
|
const mpvClient = deps.getMpvClient();
|
|
if (!mpvClient || !mpvClient.connected) {
|
|
return;
|
|
}
|
|
|
|
const subtitle = deps.getCurrentSubtitleData();
|
|
const hoveredTokenIndex = deps.getHoveredTokenIndex();
|
|
const revision = deps.getHoveredSubtitleRevision();
|
|
const hoverColor = deps.getHoverTokenColor();
|
|
const payload = buildHoveredTokenPayload({
|
|
subtitle: subtitle && hasHoveredToken(subtitle, hoveredTokenIndex) ? subtitle : null,
|
|
hoveredTokenIndex: hoveredTokenIndex,
|
|
revision,
|
|
hoverColor,
|
|
});
|
|
|
|
mpvClient.send({ command: buildHoveredTokenMessageCommand(payload) });
|
|
};
|
|
}
|