From a18192c6df60f273b083dc89c38cd8e22225ab37 Mon Sep 17 00:00:00 2001 From: Alan Trebugeais Date: Sat, 9 May 2026 21:37:55 +0200 Subject: [PATCH] fix valid code rabbits comments --- .../audio/useSourceAudioTrackSettings.ts | 92 +++++++++++-------- .../audio/waveform/waveform.worker.ts | 33 ++++--- .../components/viewport/TimelineCanvas.tsx | 2 +- .../components/waveform/AudioWaveform.tsx | 5 +- .../components/wrapper/TimelineWrapper.tsx | 2 +- .../timeline/core/timelineTypes.ts | 1 + .../timeline/model/timelineModel.ts | 25 +++-- src/i18n/locales/fr/settings.json | 6 +- 8 files changed, 99 insertions(+), 67 deletions(-) diff --git a/src/components/video-editor/audio/useSourceAudioTrackSettings.ts b/src/components/video-editor/audio/useSourceAudioTrackSettings.ts index 799d6db9..22688534 100644 --- a/src/components/video-editor/audio/useSourceAudioTrackSettings.ts +++ b/src/components/video-editor/audio/useSourceAudioTrackSettings.ts @@ -110,47 +110,59 @@ export function useSourceAudioTrackSettings({ [defaultSourceAudioTrackSettings, sourceAudioTrackSettingsByClip], ); - const onSelectedClipSourceAudioTrackVolumeChange = useCallback( - (id: string, volume: number) => { - if (!selectedClipId) return; - setSourceAudioTrackSettingsByClip((prev) => { - const prevClip = prev[selectedClipId] ?? defaultSourceAudioTrackSettings; - return { - ...prev, - [selectedClipId]: { - ...prevClip, - [id]: { - volume: Number.isFinite(volume) - ? Math.max(0, Math.min(2, volume)) - : (prevClip[id]?.volume ?? 1), - normalize: prevClip[id]?.normalize ?? false, - }, - }, - }; - }); - }, - [defaultSourceAudioTrackSettings, selectedClipId], - ); + const onSelectedClipSourceAudioTrackVolumeChange = useCallback( + (id: string, volume: number) => { + if (!selectedClipId) return; + setSourceAudioTrackSettingsByClip((prev) => { + const prevClip = prev[selectedClipId] ?? defaultSourceAudioTrackSettings; + const nextVolume = Number.isFinite(volume) + ? Math.max(0, Math.min(2, volume)) + : (prevClip[id]?.volume ?? 1); + const prevNormalize = prevClip[id]?.normalize ?? false; + if ( + prevClip[id]?.volume === nextVolume && + prevClip[id]?.normalize === prevNormalize + ) { + return prev; + } + return { + ...prev, + [selectedClipId]: { + ...prevClip, + [id]: { + volume: nextVolume, + normalize: prevNormalize, + }, + }, + }; + }); + }, + [defaultSourceAudioTrackSettings, selectedClipId], + ); - const onSelectedClipSourceAudioTrackNormalizeChange = useCallback( - (id: string, normalize: boolean) => { - if (!selectedClipId) return; - setSourceAudioTrackSettingsByClip((prev) => { - const prevClip = prev[selectedClipId] ?? defaultSourceAudioTrackSettings; - return { - ...prev, - [selectedClipId]: { - ...prevClip, - [id]: { - volume: prevClip[id]?.volume ?? 1, - normalize, - }, - }, - }; - }); - }, - [defaultSourceAudioTrackSettings, selectedClipId], - ); + const onSelectedClipSourceAudioTrackNormalizeChange = useCallback( + (id: string, normalize: boolean) => { + if (!selectedClipId) return; + setSourceAudioTrackSettingsByClip((prev) => { + const prevClip = prev[selectedClipId] ?? defaultSourceAudioTrackSettings; + const prevVolume = prevClip[id]?.volume ?? 1; + if (prevClip[id]?.normalize === normalize) { + return prev; + } + return { + ...prev, + [selectedClipId]: { + ...prevClip, + [id]: { + volume: prevVolume, + normalize, + }, + }, + }; + }); + }, + [defaultSourceAudioTrackSettings, selectedClipId], + ); return { sourceAudioTrackMeta, diff --git a/src/components/video-editor/audio/waveform/waveform.worker.ts b/src/components/video-editor/audio/waveform/waveform.worker.ts index f5332b64..1e43df98 100644 --- a/src/components/video-editor/audio/waveform/waveform.worker.ts +++ b/src/components/video-editor/audio/waveform/waveform.worker.ts @@ -1,24 +1,33 @@ -self.onmessage = (e: MessageEvent) => { - const { requestId, channels, samples } = e.data as { - requestId: number; - channels: Float32Array[]; - samples: number; - }; +type WaveformWorkerRequest = { + requestId: number; + channels: Float32Array[]; + samples: number; +}; + +interface WorkerContext { + onmessage: (e: MessageEvent) => void; + postMessage: (message: any, transfer?: Transferable[]) => void; +} + +const workerScope = self as unknown as WorkerContext; + +workerScope.onmessage = (e: MessageEvent) => { + const { requestId, channels, samples } = e.data; if (!channels || channels.length === 0 || samples <= 0) { const empty = new Float32Array(0); - (self as any).postMessage({ requestId, peaks: empty }, [empty.buffer]); + workerScope.postMessage({ requestId, peaks: empty }, [empty.buffer]); return; } try { const firstChannel = channels[0]; - const step = Math.max(1, Math.floor(firstChannel.length / samples)); const result = new Float32Array(samples); + const total = firstChannel.length; for (let i = 0; i < samples; i++) { - const start = i * step; - const end = Math.min(start + step, firstChannel.length); + const start = Math.floor((i * total) / samples); + const end = Math.floor(((i + 1) * total) / samples); let max = 0; for (let j = start; j < end; j++) { for (let c = 0; c < channels.length; c++) { @@ -29,9 +38,9 @@ self.onmessage = (e: MessageEvent) => { result[i] = max; } - (self as any).postMessage({ requestId, peaks: result }, [result.buffer]); + workerScope.postMessage({ requestId, peaks: result }, [result.buffer]); } catch (err) { - (self as any).postMessage({ + workerScope.postMessage({ requestId, error: err instanceof Error ? err.message : "Unknown worker error", }); diff --git a/src/components/video-editor/timeline/components/viewport/TimelineCanvas.tsx b/src/components/video-editor/timeline/components/viewport/TimelineCanvas.tsx index 0c742069..aab23f76 100644 --- a/src/components/video-editor/timeline/components/viewport/TimelineCanvas.tsx +++ b/src/components/video-editor/timeline/components/viewport/TimelineCanvas.tsx @@ -399,7 +399,7 @@ const TimelineCanvasRows = memo(function TimelineCanvasRows({ onSelect={() => onSelectClip?.(item.id)} variant="audio" waveformPeaks={track.peaks} - waveformSegmentSpan={liveSpanPreviewById?.[item.id] ?? item.span} + waveformSegmentSpan={item.sourceSpan ?? item.span} waveformGain={Math.max(0, Math.min(2, settings.volume))} waveformNormalize={Boolean(settings.normalize)} muted={item.muted} diff --git a/src/components/video-editor/timeline/components/waveform/AudioWaveform.tsx b/src/components/video-editor/timeline/components/waveform/AudioWaveform.tsx index ae06fd49..2380d598 100644 --- a/src/components/video-editor/timeline/components/waveform/AudioWaveform.tsx +++ b/src/components/video-editor/timeline/components/waveform/AudioWaveform.tsx @@ -51,7 +51,10 @@ function AudioWaveformComponent({ const draw = () => { const now = performance.now(); - if (now - lastDrawAtRef.current < 33) return; + if (now - lastDrawAtRef.current < 33) { + rafId = requestAnimationFrame(draw); + return; + } lastDrawAtRef.current = now; const ctx = canvas.getContext("2d"); diff --git a/src/components/video-editor/timeline/components/wrapper/TimelineWrapper.tsx b/src/components/video-editor/timeline/components/wrapper/TimelineWrapper.tsx index db0925c1..f09b4001 100644 --- a/src/components/video-editor/timeline/components/wrapper/TimelineWrapper.tsx +++ b/src/components/video-editor/timeline/components/wrapper/TimelineWrapper.tsx @@ -146,7 +146,7 @@ export default function TimelineWrapper({ ? (event.activatorEvent as PointerEvent).clientX + (event.delta?.x ?? 0) : undefined; if (span) showTooltip(span, screenX); - const moved = Math.abs(event.delta?.x ?? 0) > 0.01; + const moved = Math.hypot(event.delta?.x ?? 0, event.delta?.y ?? 0) > 0.01; if (moved) { onLiveSpanPreviewChange?.(event.active.id as string, span ?? null); } diff --git a/src/components/video-editor/timeline/core/timelineTypes.ts b/src/components/video-editor/timeline/core/timelineTypes.ts index 573daff4..d1f705d7 100644 --- a/src/components/video-editor/timeline/core/timelineTypes.ts +++ b/src/components/video-editor/timeline/core/timelineTypes.ts @@ -31,6 +31,7 @@ export interface TimelineRenderItem { id: string; rowId: string; span: Span; + sourceSpan?: Span; label: string; audioPath?: string; audioGain?: number; diff --git a/src/components/video-editor/timeline/model/timelineModel.ts b/src/components/video-editor/timeline/model/timelineModel.ts index 70f63c4c..96681351 100644 --- a/src/components/video-editor/timeline/model/timelineModel.ts +++ b/src/components/video-editor/timeline/model/timelineModel.ts @@ -47,15 +47,22 @@ export function buildTimelineItems(params: { variant: "zoom", })); - const clips: TimelineRenderItem[] = clipRegions.map((region, index) => ({ - id: region.id, - rowId: CLIP_ROW_ID, - span: { start: region.startMs, end: region.endMs }, - label: `Clip ${index + 1}`, - showSourceAudio: region.showSourceAudio, - muted: Boolean(region.muted), - variant: "clip", - })); + const clips: TimelineRenderItem[] = clipRegions.map((region, index) => { + const displayDurationMs = Math.max(0, region.endMs - region.startMs); + const speed = Number.isFinite(region.speed) && region.speed > 0 ? region.speed : 1; + const sourceEndMs = region.startMs + displayDurationMs * speed; + + return { + id: region.id, + rowId: CLIP_ROW_ID, + span: { start: region.startMs, end: region.endMs }, + sourceSpan: { start: region.startMs, end: sourceEndMs }, + label: `Clip ${index + 1}`, + showSourceAudio: region.showSourceAudio, + muted: Boolean(region.muted), + variant: "clip", + }; + }); const annotations: TimelineRenderItem[] = annotationRegions.map((region) => ({ id: region.id, diff --git a/src/i18n/locales/fr/settings.json b/src/i18n/locales/fr/settings.json index 4f790b97..200ecaa0 100644 --- a/src/i18n/locales/fr/settings.json +++ b/src/i18n/locales/fr/settings.json @@ -190,10 +190,10 @@ "volumeTitle": "Audio", "volume": "Volume", "normalize": "Normaliser", - "sourceTracksTitle": "Audio source du clip", - "systemLabel": "Son Système", + "sourceTracksTitle": "Source audio du clip", + "systemLabel": "Son système", "micLabel": "Microphone", "mixedLabel": "Source", - "deleteRegion": "Supprimer l'audio" + "deleteRegion": "Supprimer la zone audio" } }