Feat/env-manager #1

Merged
math-pixel merged 46 commits from feat/env-manager into develop 2026-05-11 15:23:32 +00:00
Showing only changes of commit 3bc7524220 - Show all commits
+54 -3
View File
@@ -2,9 +2,11 @@ import { useEffect, useState } from "react";
import { Download, RefreshCw, Save } from "lucide-react"; import { Download, RefreshCw, Save } from "lucide-react";
import type { SubtitleLanguage } from "@/managers/stores/useSettingsStore"; import type { SubtitleLanguage } from "@/managers/stores/useSettingsStore";
import type { import type {
DialogueManifest,
DialogueSpeaker, DialogueSpeaker,
DialogueVoiceId, DialogueVoiceId,
} from "@/types/dialogues/dialogues"; } from "@/types/dialogues/dialogues";
import { loadDialogueManifest } from "@/utils/dialogues/loadDialogueManifest";
import { parseSrt } from "@/utils/subtitles/parseSrt"; import { parseSrt } from "@/utils/subtitles/parseSrt";
interface SrtVoiceOption { interface SrtVoiceOption {
@@ -14,6 +16,7 @@ interface SrtVoiceOption {
interface SrtDiagnostic { interface SrtDiagnostic {
cueCount: number; cueCount: number;
expectedCueCount: number;
errors: string[]; errors: string[];
} }
@@ -42,7 +45,10 @@ function createEmptySrtTemplate(speaker: DialogueSpeaker): string {
return `1\n00:00:00,000 --> 00:00:02,000\n${speaker}: Nouveau sous-titre\n`; return `1\n00:00:00,000 --> 00:00:02,000\n${speaker}: Nouveau sous-titre\n`;
} }
function getSrtDiagnostic(content: string): SrtDiagnostic { function getSrtDiagnostic(
content: string,
expectedCueIndexes: number[],
): SrtDiagnostic {
const normalizedContent = content.replace(/^\uFEFF/, "").replace(/\r/g, ""); const normalizedContent = content.replace(/^\uFEFF/, "").replace(/\r/g, "");
const blocks = normalizedContent const blocks = normalizedContent
.trim() .trim()
@@ -92,12 +98,39 @@ function getSrtDiagnostic(content: string): SrtDiagnostic {
); );
} }
const cueIndexes = new Set(cues.map((cue) => cue.index));
const missingCueIndexes = expectedCueIndexes.filter(
(cueIndex) => !cueIndexes.has(cueIndex),
);
if (missingCueIndexes.length > 0) {
errors.push(
`Cues attendues par le manifeste manquantes: ${missingCueIndexes.join(", ")}.`,
);
}
return { return {
cueCount: cues.length, cueCount: cues.length,
expectedCueCount: expectedCueIndexes.length,
errors, errors,
}; };
} }
function getExpectedCueIndexes(
manifest: DialogueManifest | null,
voice: DialogueVoiceId,
): number[] {
if (!manifest) return [];
return manifest.dialogues
.filter((dialogue) => dialogue.voice === voice)
.map((dialogue) => dialogue.subtitleCueIndex)
.filter(
(cueIndex, index, cueIndexes) => cueIndexes.indexOf(cueIndex) === index,
)
.sort((a, b) => a - b);
}
function downloadSrtFile( function downloadSrtFile(
voice: DialogueVoiceId, voice: DialogueVoiceId,
language: SubtitleLanguage, language: SubtitleLanguage,
@@ -137,9 +170,11 @@ export function EditorSrtPanel(): React.JSX.Element {
const [content, setContent] = useState(""); const [content, setContent] = useState("");
const [status, setStatus] = useState("Chargement du SRT..."); const [status, setStatus] = useState("Chargement du SRT...");
const [isSaving, setIsSaving] = useState(false); const [isSaving, setIsSaving] = useState(false);
const [manifest, setManifest] = useState<DialogueManifest | null>(null);
const selectedVoice = const selectedVoice =
SRT_VOICES.find((item) => item.id === voice) ?? DEFAULT_SRT_VOICE; SRT_VOICES.find((item) => item.id === voice) ?? DEFAULT_SRT_VOICE;
const diagnostic = getSrtDiagnostic(content); const expectedCueIndexes = getExpectedCueIndexes(manifest, voice);
const diagnostic = getSrtDiagnostic(content, expectedCueIndexes);
const isSrtValid = diagnostic.errors.length === 0; const isSrtValid = diagnostic.errors.length === 0;
async function handleSave(): Promise<void> { async function handleSave(): Promise<void> {
@@ -162,6 +197,22 @@ export function EditorSrtPanel(): React.JSX.Element {
} }
} }
useEffect(() => {
let mounted = true;
void loadDialogueManifest()
.then((loadedManifest) => {
if (mounted) setManifest(loadedManifest);
})
.catch(() => {
if (mounted) setManifest(null);
});
return () => {
mounted = false;
};
}, []);
useEffect(() => { useEffect(() => {
let mounted = true; let mounted = true;
const srtPath = getSrtPath(voice, language); const srtPath = getSrtPath(voice, language);
@@ -276,7 +327,7 @@ export function EditorSrtPanel(): React.JSX.Element {
> >
<strong> <strong>
{isSrtValid {isSrtValid
? `${diagnostic.cueCount} cue${diagnostic.cueCount > 1 ? "s" : ""} valide${diagnostic.cueCount > 1 ? "s" : ""}` ? `${diagnostic.cueCount} cue${diagnostic.cueCount > 1 ? "s" : ""} valide${diagnostic.cueCount > 1 ? "s" : ""} / ${diagnostic.expectedCueCount} attendue${diagnostic.expectedCueCount > 1 ? "s" : ""}`
: `${diagnostic.errors.length} erreur${diagnostic.errors.length > 1 ? "s" : ""} SRT`} : `${diagnostic.errors.length} erreur${diagnostic.errors.length > 1 ? "s" : ""} SRT`}
</strong> </strong>
{!isSrtValid && ( {!isSrtValid && (