diff --git a/src/components/video-editor/SettingsPanel.tsx b/src/components/video-editor/SettingsPanel.tsx index 657398a2..7e05203d 100644 --- a/src/components/video-editor/SettingsPanel.tsx +++ b/src/components/video-editor/SettingsPanel.tsx @@ -469,11 +469,11 @@ interface SettingsPanelProps { selectedClipId?: string | null; selectedClipSpeed?: number | null; selectedClipMuted?: boolean | null; + selectedClipShowSourceAudio?: boolean | null; hasClipSourceAudio?: boolean; - showClipSourceAudioTrack?: boolean; onClipSpeedChange?: (speed: number) => void; onClipMutedChange?: (muted: boolean) => void; - onShowClipSourceAudioTrackChange?: (show: boolean) => void; + onClipShowSourceAudioChange?: (show: boolean) => void; sourceAudioTrackMeta?: Array<{ id: string; label: string }>; sourceAudioTrackSettings?: Record; onSourceAudioTrackVolumeChange?: (id: string, volume: number) => void; @@ -869,11 +869,11 @@ export function SettingsPanel({ selectedClipId, selectedClipSpeed, selectedClipMuted, + selectedClipShowSourceAudio = false, hasClipSourceAudio = false, - showClipSourceAudioTrack = false, onClipSpeedChange, onClipMutedChange, - onShowClipSourceAudioTrackChange, + onClipShowSourceAudioChange, sourceAudioTrackMeta = [], sourceAudioTrackSettings = {}, onSourceAudioTrackVolumeChange, @@ -3059,42 +3059,7 @@ export function SettingsPanel({ )} -
- - {tSettings("clip.muteAudio", "Mute Audio")} - -
- - {selectedClipMuted - ? tSettings("clip.mutedState", "Muted") - : tSettings("clip.audioOnState", "Audio On")} - - onClipMutedChange?.(!v)} - className="data-[state=checked]:bg-[#06b6d4] scale-75" - /> -
-
- {hasClipSourceAudio && ( -
- - {tSettings("clip.separateSourceAudio", "Separate Clip Audio")} - - onShowClipSourceAudioTrackChange?.(v)} - className="data-[state=checked]:bg-[#06b6d4] scale-75" - /> -
- )} +
{tSettings("speed.label", "Speed")}
@@ -3135,6 +3100,32 @@ export function SettingsPanel({ ); })} + +
+ + {selectedClipMuted + ? tSettings("clip.unmuteAudio", "Unmute Audio") + : tSettings("clip.muteAudio", "Mute Audio")} + + onClipMutedChange?.(!v)} + className="data-[state=checked]:bg-[#06b6d4] scale-75" + /> +
+ {hasClipSourceAudio && ( +
+ + {tSettings("clip.separateClipFromAudio", "Separate clip from audio")} + + onClipShowSourceAudioChange?.(v)} + className="data-[state=checked]:bg-[#06b6d4] scale-75" + /> +
+ )} + {selectedClipId && ( - ) : selectedClipId && hasClipSourceAudio && showClipSourceAudioTrack ? ( + ) : selectedClipId && hasClipSourceAudio && selectedClipShowSourceAudio ? (
{tSettings("audio.sourceTracksTitle", "Clip Source Audio")} diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx index 0930186e..dacffaa9 100644 --- a/src/components/video-editor/VideoEditor.tsx +++ b/src/components/video-editor/VideoEditor.tsx @@ -665,7 +665,6 @@ export default function VideoEditor() { const [audioRegions, setAudioRegions] = useState([]); const [selectedAudioId, setSelectedAudioId] = useState(null); const [hasClipSourceAudio, setHasClipSourceAudio] = useState(false); - const [showClipSourceAudioTrack, setShowClipSourceAudioTrack] = useState(false); const [autoCaptions, setAutoCaptions] = useState([]); const [autoCaptionSettings, setAutoCaptionSettings] = useState( DEFAULT_AUTO_CAPTION_SETTINGS, @@ -3709,6 +3708,18 @@ export default function VideoEditor() { [selectedClipId], ); + const handleClipShowSourceAudioChange = useCallback( + (showSourceAudio: boolean) => { + if (!selectedClipId) return; + setClipRegions((prev) => + prev.map((clip) => + clip.id === selectedClipId ? { ...clip, showSourceAudio } : clip, + ), + ); + }, + [selectedClipId], + ); + const handleClipDelete = useCallback( (id: string) => { const deletedClip = clipRegions.find((clip) => clip.id === id); @@ -5697,26 +5708,26 @@ export default function VideoEditor() { selectedClipId={selectedClipId} selectedClipSpeed={ selectedClipId - ? (clipRegions.find((c) => c.id === selectedClipId) - ?.speed ?? 1) + ? clipRegions.find((c) => c.id === selectedClipId)?.speed ?? 1 : null } selectedClipMuted={ selectedClipId - ? (clipRegions.find((c) => c.id === selectedClipId) - ?.muted ?? false) + ? clipRegions.find((c) => c.id === selectedClipId)?.muted ?? + false : null } - onClipSpeedChange={(speed) => - selectedClipId && handleClipSpeedChange(speed) - } - onClipMutedChange={(muted) => - selectedClipId && handleClipMutedChange(muted) + selectedClipShowSourceAudio={ + selectedClipId + ? clipRegions.find((c) => c.id === selectedClipId) + ?.showSourceAudio ?? false + : null } + onClipSpeedChange={handleClipSpeedChange} + onClipMutedChange={handleClipMutedChange} + onClipShowSourceAudioChange={handleClipShowSourceAudioChange} onClipDelete={handleClipDelete} hasClipSourceAudio={hasClipSourceAudio} - showClipSourceAudioTrack={showClipSourceAudioTrack} - onShowClipSourceAudioTrackChange={setShowClipSourceAudioTrack} sourceAudioTrackMeta={audio.sourceAudioTrackMeta} sourceAudioTrackSettings={audio.selectedClipSourceAudioTrackSettings} onSourceAudioTrackVolumeChange={ @@ -6314,16 +6325,13 @@ export default function VideoEditor() { selectedAnnotationId={selectedAnnotationId} onSelectAnnotation={handleSelectAnnotation} aspectRatio={aspectRatio} - showSourceAudioTrack={showClipSourceAudioTrack} + showSourceAudioTrack={clipRegions.some((c) => c.showSourceAudio)} sourceAudioTrackSettings={audio.activeSourceAudioTrackSettings} getSourceAudioTrackSettingsForClip={ audio.getSourceAudioTrackSettingsForClip } onSourceAudioAvailabilityChange={(available) => { setHasClipSourceAudio(available); - if (!available) { - setShowClipSourceAudioTrack(false); - } }} onSourceAudioTracksMetaChange={(tracks) => { audio.onSourceAudioTracksMetaChange(tracks); diff --git a/src/components/video-editor/timeline/components/viewport/TimelineCanvas.tsx b/src/components/video-editor/timeline/components/viewport/TimelineCanvas.tsx index 12013a27..a1617779 100644 --- a/src/components/video-editor/timeline/components/viewport/TimelineCanvas.tsx +++ b/src/components/video-editor/timeline/components/viewport/TimelineCanvas.tsx @@ -378,7 +378,7 @@ const TimelineCanvasRows = memo(function TimelineCanvasRows({ {showSourceAudioTrack && sourceAudioTracks.map((track) => ( - {clipItems.map((item) => { + {clipItems.filter(item => item.showSourceAudio).map((item) => { const settings = getSourceAudioTrackSettingsForClip?.(item.id)?.[ track.id ] ?? { volume: 1, normalize: false }; diff --git a/src/components/video-editor/timeline/core/timelineTypes.ts b/src/components/video-editor/timeline/core/timelineTypes.ts index 4a58c969..1fc9a66a 100644 --- a/src/components/video-editor/timeline/core/timelineTypes.ts +++ b/src/components/video-editor/timeline/core/timelineTypes.ts @@ -36,6 +36,7 @@ export interface TimelineRenderItem { zoomDepth?: number; zoomMode?: ZoomMode; speedValue?: number; + showSourceAudio?: boolean; variant: "zoom" | "trim" | "clip" | "annotation" | "speed" | "audio"; } diff --git a/src/components/video-editor/timeline/model/timelineModel.ts b/src/components/video-editor/timeline/model/timelineModel.ts index b36aabc9..600859a5 100644 --- a/src/components/video-editor/timeline/model/timelineModel.ts +++ b/src/components/video-editor/timeline/model/timelineModel.ts @@ -52,6 +52,7 @@ export function buildTimelineItems(params: { rowId: CLIP_ROW_ID, span: { start: region.startMs, end: region.endMs }, label: `Clip ${index + 1}`, + showSourceAudio: region.showSourceAudio, variant: "clip", })); diff --git a/src/components/video-editor/types.ts b/src/components/video-editor/types.ts index 92f84fd6..ec65c44e 100644 --- a/src/components/video-editor/types.ts +++ b/src/components/video-editor/types.ts @@ -169,6 +169,7 @@ export interface ClipRegion { endMs: number; speed: number; muted?: boolean; + showSourceAudio?: boolean; } export function getClipSourceEndMs(clip: ClipRegion): number { diff --git a/src/i18n/locales/en/settings.json b/src/i18n/locales/en/settings.json index 3a2b6a76..7b942498 100644 --- a/src/i18n/locales/en/settings.json +++ b/src/i18n/locales/en/settings.json @@ -20,6 +20,8 @@ "clip": { "title": "Clip", "muteAudio": "Mute Audio", + "unmuteAudio": "Unmute Audio", + "separateClipFromAudio": "Separate clip from audio", "delete": "Delete Clip" }, "effects": { @@ -201,5 +203,11 @@ "exportVideo": "Export {{format}}", "reportBug": "Report Bug", "starOnGithub": "Star on GitHub" + }, + "audio": { + "sourceTracksTitle": "Clip Source Audio", + "volumeTitle": "Volume", + "normalize": "Normalize", + "deleteRegion": "Delete Audio" } } diff --git a/src/i18n/locales/es/settings.json b/src/i18n/locales/es/settings.json index 0c7ca244..75f78d75 100644 --- a/src/i18n/locales/es/settings.json +++ b/src/i18n/locales/es/settings.json @@ -20,6 +20,8 @@ "clip": { "title": "Clip", "muteAudio": "Silenciar audio", + "unmuteAudio": "Activar sonido", + "separateClipFromAudio": "Separar clip del audio", "delete": "Eliminar clip" }, "effects": { @@ -181,5 +183,11 @@ "exportVideo": "Exportar {{format}}", "reportBug": "Reportar error", "starOnGithub": "Estrella en GitHub" + }, + "audio": { + "sourceTracksTitle": "Audio fuente del clip", + "volumeTitle": "Volumen", + "normalize": "Normalizar", + "deleteRegion": "Eliminar audio" } } diff --git a/src/i18n/locales/fr/settings.json b/src/i18n/locales/fr/settings.json index 04804828..241bc269 100644 --- a/src/i18n/locales/fr/settings.json +++ b/src/i18n/locales/fr/settings.json @@ -20,6 +20,8 @@ "clip": { "title": "Clip", "muteAudio": "Couper le son", + "unmuteAudio": "Réactiver le son", + "separateClipFromAudio": "Séparer le clip de l'audio", "delete": "Supprimer le clip" }, "effects": { @@ -181,5 +183,11 @@ "exportVideo": "Exporter en {{format}}", "reportBug": "Signaler un bug", "starOnGithub": "Mettre une étoile sur GitHub" + }, + "audio": { + "sourceTracksTitle": "Audio source du clip", + "volumeTitle": "Volume", + "normalize": "Normaliser", + "deleteRegion": "Supprimer l'audio" } } diff --git a/src/i18n/locales/ko/settings.json b/src/i18n/locales/ko/settings.json index 8c32549a..0adaa6c7 100644 --- a/src/i18n/locales/ko/settings.json +++ b/src/i18n/locales/ko/settings.json @@ -20,6 +20,8 @@ "clip": { "title": "클립", "muteAudio": "오디오 음소거", + "unmuteAudio": "음소거 해제", + "separateClipFromAudio": "클립과 오디오 분리", "delete": "클립 삭제" }, "effects": { diff --git a/src/i18n/locales/nl/settings.json b/src/i18n/locales/nl/settings.json index 1e7bb77d..00d78905 100644 --- a/src/i18n/locales/nl/settings.json +++ b/src/i18n/locales/nl/settings.json @@ -20,6 +20,8 @@ "clip": { "title": "Clip", "muteAudio": "Audio dempen", + "unmuteAudio": "Geluid inschakelen", + "separateClipFromAudio": "Clip scheiden van audio", "delete": "Clip verwijderen" }, "effects": { diff --git a/src/i18n/locales/pt-BR/settings.json b/src/i18n/locales/pt-BR/settings.json index 58783df0..810d7f91 100644 --- a/src/i18n/locales/pt-BR/settings.json +++ b/src/i18n/locales/pt-BR/settings.json @@ -20,6 +20,8 @@ "clip": { "title": "Clipe", "muteAudio": "Silenciar áudio", + "unmuteAudio": "Ativar som", + "separateClipFromAudio": "Separar clipe do áudio", "delete": "Excluir clipe" }, "effects": { diff --git a/src/i18n/locales/ru/settings.json b/src/i18n/locales/ru/settings.json index 88e48ec5..6a2f052d 100644 --- a/src/i18n/locales/ru/settings.json +++ b/src/i18n/locales/ru/settings.json @@ -20,6 +20,8 @@ "clip": { "title": "Клип", "muteAudio": "Выключить звук", + "unmuteAudio": "Включить звук", + "separateClipFromAudio": "Отделить клип от аудио", "delete": "Удалить" }, "effects": { diff --git a/src/i18n/locales/zh-CN/settings.json b/src/i18n/locales/zh-CN/settings.json index 5f5b277a..f895653f 100644 --- a/src/i18n/locales/zh-CN/settings.json +++ b/src/i18n/locales/zh-CN/settings.json @@ -20,6 +20,8 @@ "clip": { "title": "片段", "muteAudio": "静音音频", + "unmuteAudio": "取消静音", + "separateClipFromAudio": "将剪辑与音频分离", "delete": "删除片段" }, "effects": { diff --git a/src/i18n/locales/zh-TW/settings.json b/src/i18n/locales/zh-TW/settings.json index bf2ffc9c..fd9ff235 100644 --- a/src/i18n/locales/zh-TW/settings.json +++ b/src/i18n/locales/zh-TW/settings.json @@ -20,6 +20,8 @@ "clip": { "title": "片段", "muteAudio": "靜音", + "unmuteAudio": "取消靜音", + "separateClipFromAudio": "將片段與音訊分離", "delete": "刪除片段" }, "effects": {