Promotes the v6 detection slice into a complete, buildable Tauri 2 app and renames Stream Studio → Sing Detect (detection-only; video-concat split to its own project). Proper src/ + src-tauri/ layout, Python sidecar under sidecar/, app icons, package/Cargo manifests, and dev/build/pack scripts. Pipeline unchanged in spirit: Rust drives ffmpeg to a 16 kHz mono WAV, the Python sidecar (inaSpeechSegmenter) emits line-JSON segments, and Rust writes EDL / markers CSV / CSV / JSON exports. Long ops run on spawn_blocking; cancel kills the whole py -3.10 process tree. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
44 lines
2.3 KiB
TypeScript
44 lines
2.3 KiB
TypeScript
// Typed bridge to the Rust backend (detection-only build).
|
|
|
|
import { invoke } from "@tauri-apps/api/core";
|
|
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
|
|
import { getCurrentWebview } from "@tauri-apps/api/webview";
|
|
import { open } from "@tauri-apps/plugin-dialog";
|
|
import { revealItemInDir } from "@tauri-apps/plugin-opener";
|
|
|
|
export interface Tools { ffmpeg: string | null; ffprobe: string | null; }
|
|
export interface Segment { index: number; start: number; end: number; duration: number; }
|
|
export interface DetectResult { segments: Segment[]; stats: Record<string, number>; }
|
|
export interface ExportResult { files: string[]; }
|
|
export interface DetectionStatus { python: boolean; packages: boolean; model: boolean; ready: boolean; detail: string; }
|
|
export interface SetupProgress { message: string; pct: number; }
|
|
|
|
export const getTools = () => invoke<Tools>("get_tools");
|
|
export const checkDetection = () => invoke<DetectionStatus>("check_detection");
|
|
export const setupDetection = () => invoke<void>("setup_detection");
|
|
export const detectSongs = (video: string, gap: number, minDuration: number, padding: number) =>
|
|
invoke<DetectResult>("detect_songs", { video, gap, minDuration, padding });
|
|
export const exportSegments = (segments: Segment[], sourceVideo: string, outDir: string | null, fps: number) =>
|
|
invoke<ExportResult>("export_segments", { segments, sourceVideo, outDir, fps });
|
|
export const cancel = () => invoke<void>("cancel");
|
|
|
|
export const onDetectProgress = (cb: (msg: string) => void): Promise<UnlistenFn> =>
|
|
listen<{ message: string }>("detect-progress", (e) => cb(e.payload.message));
|
|
export const onDetectSetupProgress = (cb: (p: SetupProgress) => void): Promise<UnlistenFn> =>
|
|
listen<SetupProgress>("detect-setup-progress", (e) => cb(e.payload));
|
|
|
|
export const onFilesDropped = (cb: (paths: string[]) => void): Promise<UnlistenFn> =>
|
|
getCurrentWebview().onDragDropEvent((e) => {
|
|
if (e.payload.type === "drop") cb(e.payload.paths);
|
|
});
|
|
|
|
export async function pickVideo(): Promise<string | null> {
|
|
const sel = await open({
|
|
multiple: false,
|
|
filters: [{ name: "Video", extensions: ["mp4", "mov", "mkv", "avi", "flv", "ts", "wmv", "m4v", "webm", "mts", "m2ts"] }],
|
|
});
|
|
return typeof sel === "string" ? sel : null;
|
|
}
|
|
|
|
export const reveal = (path: string) => revealItemInDir(path);
|