mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-11-11 08:23:42 +00:00
handlers.ts was ~5967 lines. Extracted into 22 focused modules: - ipc/types.ts — shared TypeScript interfaces and types - ipc/constants.ts — module-level constants - ipc/state.ts — all mutable state with typed setters - ipc/utils.ts — shared low-level utilities (getScreen, normalizePath, etc.) - ipc/ffmpeg/binary.ts — ffmpeg binary resolution - ipc/ffmpeg/filters.ts — audio sync/filter builders - ipc/captions/parser.ts — SRT/Whisper JSON parsers - ipc/captions/whisper.ts — Whisper model download/status - ipc/captions/generate.ts — auto-caption generation - ipc/paths/binaries.ts — native binary path resolution - ipc/cursor/monitor.ts — cursor monitor process management - ipc/cursor/telemetry.ts — cursor sampling and telemetry - ipc/cursor/bounds.ts — window bounds capture and resolution - ipc/cursor/interaction.ts — mouse hook and interaction capture - ipc/recording/events.ts — recording lifecycle events - ipc/recording/diagnostics.ts — media validation and diagnostics - ipc/recording/prune.ts — auto-recording cleanup - ipc/recording/ffmpeg.ts — FFmpeg screen capture - ipc/recording/windows.ts — Windows native capture (WGC) - ipc/recording/mac.ts — Mac ScreenCaptureKit integration - ipc/export/native-video.ts — native video export sessions - ipc/project/session.ts — recording session manifests - ipc/project/manager.ts — project library and file management handlers.ts reduced from 5967 → 2930 lines (registerIpcHandlers + helpers only)
106 lines
2.7 KiB
TypeScript
106 lines
2.7 KiB
TypeScript
import { createRequire } from "node:module";
|
|
import fs from "node:fs/promises";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { app } from "electron";
|
|
import { RECORDINGS_DIR } from "../appPaths";
|
|
import { RECORDINGS_SETTINGS_FILE, AUTO_RECORDING_PREFIX } from "./constants";
|
|
import {
|
|
customRecordingsDir,
|
|
setCustomRecordingsDir,
|
|
recordingsDirLoaded,
|
|
setRecordingsDirLoaded,
|
|
} from "./state";
|
|
|
|
const nodeRequire = createRequire(import.meta.url);
|
|
|
|
export function getScreen() {
|
|
if (!app.isReady()) {
|
|
throw new Error(
|
|
"getScreen() called before app is ready. Ensure all screen access happens after app.whenReady().",
|
|
);
|
|
}
|
|
return nodeRequire("electron").screen as typeof import("electron").screen;
|
|
}
|
|
|
|
export function normalizePath(filePath: string) {
|
|
return path.resolve(filePath);
|
|
}
|
|
|
|
export function normalizeVideoSourcePath(videoPath?: string | null): string | null {
|
|
if (typeof videoPath !== "string") {
|
|
return null;
|
|
}
|
|
|
|
const trimmed = videoPath.trim();
|
|
if (!trimmed) {
|
|
return null;
|
|
}
|
|
|
|
if (/^file:\/\//i.test(trimmed)) {
|
|
try {
|
|
return fileURLToPath(trimmed);
|
|
} catch {
|
|
// Fall through and keep best-effort string path below.
|
|
}
|
|
}
|
|
|
|
return trimmed;
|
|
}
|
|
|
|
export function parseWindowId(sourceId?: string) {
|
|
if (!sourceId) return null;
|
|
const match = sourceId.match(/^window:(\d+)/);
|
|
return match ? Number.parseInt(match[1], 10) : null;
|
|
}
|
|
|
|
export function getTelemetryPathForVideo(videoPath: string) {
|
|
return `${videoPath}.cursor.json`;
|
|
}
|
|
|
|
export function isAutoRecordingPath(filePath: string) {
|
|
return path.basename(filePath).startsWith(AUTO_RECORDING_PREFIX);
|
|
}
|
|
|
|
export async function moveFileWithOverwrite(sourcePath: string, destinationPath: string) {
|
|
await fs.mkdir(path.dirname(destinationPath), { recursive: true });
|
|
await fs.rm(destinationPath, { force: true });
|
|
|
|
try {
|
|
await fs.rename(sourcePath, destinationPath);
|
|
} catch (error) {
|
|
const nodeError = error as NodeJS.ErrnoException;
|
|
if (nodeError.code !== "EXDEV") {
|
|
throw error;
|
|
}
|
|
|
|
await fs.copyFile(sourcePath, destinationPath);
|
|
await fs.unlink(sourcePath);
|
|
}
|
|
}
|
|
|
|
async function loadRecordingsDirectorySetting() {
|
|
if (recordingsDirLoaded) {
|
|
return;
|
|
}
|
|
|
|
setRecordingsDirLoaded(true);
|
|
|
|
try {
|
|
const content = await fs.readFile(RECORDINGS_SETTINGS_FILE, "utf-8");
|
|
const parsed = JSON.parse(content) as { recordingsDir?: unknown };
|
|
if (typeof parsed.recordingsDir === "string" && parsed.recordingsDir.trim()) {
|
|
setCustomRecordingsDir(path.resolve(parsed.recordingsDir));
|
|
}
|
|
} catch {
|
|
setCustomRecordingsDir(null);
|
|
}
|
|
}
|
|
|
|
export async function getRecordingsDir() {
|
|
await loadRecordingsDirectorySetting();
|
|
const targetDir = customRecordingsDir ?? RECORDINGS_DIR;
|
|
await fs.mkdir(targetDir, { recursive: true });
|
|
return targetDir;
|
|
}
|