update: gros commit fix editor srt panel 2
This commit is contained in:
@@ -26,6 +26,9 @@ interface TextRange {
|
||||
end: number;
|
||||
}
|
||||
|
||||
type CueTimeEdge = "start" | "end";
|
||||
const CUE_NUDGE_SECONDS = 0.1;
|
||||
|
||||
const SRT_VOICES: SrtVoiceOption[] = [
|
||||
{ id: "narrateur", label: "Narrateur" },
|
||||
{ id: "fermier", label: "Fermier" },
|
||||
@@ -75,6 +78,21 @@ function formatPreviewTime(totalSeconds: number): string {
|
||||
return `${Math.floor(totalSeconds)}.${Math.floor((totalSeconds % 1) * 10)}s`;
|
||||
}
|
||||
|
||||
function parseSrtTime(value: string): number | null {
|
||||
const match = value.match(/^(\d{2}):(\d{2}):(\d{2}),(\d{3})$/);
|
||||
if (!match) return null;
|
||||
|
||||
const [, hours, minutes, seconds, milliseconds] = match;
|
||||
if (!hours || !minutes || !seconds || !milliseconds) return null;
|
||||
|
||||
return (
|
||||
Number(hours) * 3600 +
|
||||
Number(minutes) * 60 +
|
||||
Number(seconds) +
|
||||
Number(milliseconds) / 1000
|
||||
);
|
||||
}
|
||||
|
||||
function padTime(value: number): string {
|
||||
return value.toString().padStart(2, "0");
|
||||
}
|
||||
@@ -190,6 +208,58 @@ function findCueBlockRange(
|
||||
return { start, end };
|
||||
}
|
||||
|
||||
function updateCueTimecode(
|
||||
content: string,
|
||||
cueIndex: number,
|
||||
edge: CueTimeEdge,
|
||||
time: number,
|
||||
): string | null {
|
||||
const range = findCueBlockRange(content, cueIndex);
|
||||
if (!range) return null;
|
||||
|
||||
const block = content.slice(range.start, range.end);
|
||||
const lines = block.split("\n");
|
||||
const timecodeLine = lines[1];
|
||||
if (!timecodeLine) return null;
|
||||
|
||||
const [start, end] = timecodeLine.split(" --> ");
|
||||
if (!start || !end) return null;
|
||||
|
||||
lines[1] =
|
||||
edge === "start"
|
||||
? `${formatSrtTime(time)} --> ${end}`
|
||||
: `${start} --> ${formatSrtTime(time)}`;
|
||||
|
||||
return `${content.slice(0, range.start)}${lines.join("\n")}${content.slice(range.end)}`;
|
||||
}
|
||||
|
||||
function nudgeCueTimecode(
|
||||
content: string,
|
||||
cueIndex: number,
|
||||
delta: number,
|
||||
): string | null {
|
||||
const range = findCueBlockRange(content, cueIndex);
|
||||
if (!range) return null;
|
||||
|
||||
const block = content.slice(range.start, range.end);
|
||||
const lines = block.split("\n");
|
||||
const timecodeLine = lines[1];
|
||||
if (!timecodeLine) return null;
|
||||
|
||||
const [start, end] = timecodeLine.split(" --> ");
|
||||
if (!start || !end) return null;
|
||||
|
||||
const startTime = parseSrtTime(start);
|
||||
const endTime = parseSrtTime(end);
|
||||
if (startTime === null || endTime === null) return null;
|
||||
|
||||
const nextStartTime = Math.max(0, startTime + delta);
|
||||
const nextEndTime = Math.max(nextStartTime + 0.001, endTime + delta);
|
||||
lines[1] = `${formatSrtTime(nextStartTime)} --> ${formatSrtTime(nextEndTime)}`;
|
||||
|
||||
return `${content.slice(0, range.start)}${lines.join("\n")}${content.slice(range.end)}`;
|
||||
}
|
||||
|
||||
function downloadSrtFile(
|
||||
voice: DialogueVoiceId,
|
||||
language: SubtitleLanguage,
|
||||
@@ -287,6 +357,39 @@ export function EditorSrtPanel(): React.JSX.Element {
|
||||
setStatus(`Cue ${cueIndex} selectionnee dans le SRT.`);
|
||||
}
|
||||
|
||||
function handleSetCueTime(cueIndex: number, edge: CueTimeEdge): void {
|
||||
const updatedContent = updateCueTimecode(
|
||||
content,
|
||||
cueIndex,
|
||||
edge,
|
||||
audioCurrentTime,
|
||||
);
|
||||
|
||||
if (!updatedContent) {
|
||||
setStatus(`Cue ${cueIndex} introuvable ou timecode invalide.`);
|
||||
return;
|
||||
}
|
||||
|
||||
setContent(updatedContent);
|
||||
setStatus(
|
||||
`Cue ${cueIndex}: ${edge === "start" ? "debut" : "fin"} place a ${formatSrtTime(audioCurrentTime)}.`,
|
||||
);
|
||||
}
|
||||
|
||||
function handleNudgeCue(cueIndex: number, delta: number): void {
|
||||
const updatedContent = nudgeCueTimecode(content, cueIndex, delta);
|
||||
|
||||
if (!updatedContent) {
|
||||
setStatus(`Cue ${cueIndex} introuvable ou timecode invalide.`);
|
||||
return;
|
||||
}
|
||||
|
||||
setContent(updatedContent);
|
||||
setStatus(
|
||||
`Cue ${cueIndex} decalee de ${delta > 0 ? "+" : ""}${delta.toFixed(1)}s.`,
|
||||
);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
|
||||
@@ -414,6 +517,46 @@ export function EditorSrtPanel(): React.JSX.Element {
|
||||
<p>Aucune cue active a ce moment.</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="editor-srt-time-actions">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
handleSetCueTime(selectedDialogue.subtitleCueIndex, "start")
|
||||
}
|
||||
>
|
||||
Set start
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
handleSetCueTime(selectedDialogue.subtitleCueIndex, "end")
|
||||
}
|
||||
>
|
||||
Set end
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
handleNudgeCue(
|
||||
selectedDialogue.subtitleCueIndex,
|
||||
-CUE_NUDGE_SECONDS,
|
||||
)
|
||||
}
|
||||
>
|
||||
-100ms
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
handleNudgeCue(
|
||||
selectedDialogue.subtitleCueIndex,
|
||||
CUE_NUDGE_SECONDS,
|
||||
)
|
||||
}
|
||||
>
|
||||
+100ms
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
className="editor-srt-jump-button"
|
||||
type="button"
|
||||
|
||||
@@ -1532,6 +1532,28 @@ canvas {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.editor-srt-time-actions {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.editor-srt-time-actions button {
|
||||
padding: 8px 10px;
|
||||
border: 1px solid rgba(125, 211, 252, 0.24);
|
||||
border-radius: 12px;
|
||||
background: rgba(125, 211, 252, 0.08);
|
||||
color: #bae6fd;
|
||||
cursor: pointer;
|
||||
font-size: 0.74rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.editor-srt-time-actions button:hover {
|
||||
border-color: #7dd3fc;
|
||||
background: rgba(125, 211, 252, 0.14);
|
||||
}
|
||||
|
||||
.editor-srt-jump-button {
|
||||
width: 100%;
|
||||
padding: 8px 10px;
|
||||
|
||||
Reference in New Issue
Block a user