diff --git a/package-lock.json b/package-lock.json index fdbd6b9..70e3395 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "openscreen", - "version": "1.3.0", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "openscreen", - "version": "1.3.0", + "version": "1.2.0", "dependencies": { "@fix-webm-duration/fix": "^1.0.1", "@pixi/filter-drop-shadow": "^5.2.0", diff --git a/src/components/launch/LaunchWindow.tsx b/src/components/launch/LaunchWindow.tsx index 249dd77..d1185e8 100644 --- a/src/components/launch/LaunchWindow.tsx +++ b/src/components/launch/LaunchWindow.tsx @@ -1,6 +1,6 @@ import { ChevronDown, Languages } from "lucide-react"; import { useEffect, useState } from "react"; -import { BsPauseCircle, BsPlayCircle, BsRecordCircle } from "react-icons/bs"; +import { BsRecordCircle } from "react-icons/bs"; import { FaRegStopCircle } from "react-icons/fa"; import { FaFolderOpen } from "react-icons/fa6"; import { FiMinus, FiX } from "react-icons/fi"; @@ -42,8 +42,6 @@ const ICON_CONFIG = { micOff: { icon: MdMicOff, size: ICON_SIZE }, webcamOn: { icon: MdVideocam, size: ICON_SIZE }, webcamOff: { icon: MdVideocamOff, size: ICON_SIZE }, - pause: { icon: BsPauseCircle, size: ICON_SIZE }, - resume: { icon: BsPlayCircle, size: ICON_SIZE }, stop: { icon: FaRegStopCircle, size: ICON_SIZE }, restart: { icon: MdRestartAlt, size: ICON_SIZE }, cancel: { icon: MdCancel, size: ICON_SIZE }, @@ -81,10 +79,7 @@ export function LaunchWindow() { const { recording, - paused, - elapsedSeconds, toggleRecording, - togglePaused, restartRecording, cancelRecording, microphoneEnabled, @@ -98,6 +93,8 @@ export function LaunchWindow() { webcamDeviceId, setWebcamDeviceId, } = useScreenRecorder(); + const [recordingStart, setRecordingStart] = useState(null); + const [elapsed, setElapsed] = useState(0); const showMicControls = microphoneEnabled && !recording; const showWebcamControls = webcamEnabled && !recording; @@ -152,6 +149,25 @@ export function LaunchWindow() { } }, [selectedCameraId, setWebcamDeviceId]); + useEffect(() => { + let timer: NodeJS.Timeout | null = null; + if (recording) { + if (!recordingStart) setRecordingStart(Date.now()); + timer = setInterval(() => { + if (recordingStart) { + setElapsed(Math.floor((Date.now() - recordingStart) / 1000)); + } + }, 1000); + } else { + setRecordingStart(null); + setElapsed(0); + if (timer) clearInterval(timer); + } + return () => { + if (timer) clearInterval(timer); + }; + }, [recording, recordingStart]); + useEffect(() => { if (!import.meta.env.DEV) { return; @@ -434,11 +450,7 @@ export function LaunchWindow() { {/* Record/Stop group */} - {recording && ( - - - - )} - {/* Restart recording */} {recording && ( diff --git a/src/hooks/useScreenRecorder.ts b/src/hooks/useScreenRecorder.ts index a676d66..25adf6d 100644 --- a/src/hooks/useScreenRecorder.ts +++ b/src/hooks/useScreenRecorder.ts @@ -41,10 +41,7 @@ const WEBCAM_TARGET_FRAME_RATE = 30; type UseScreenRecorderReturn = { recording: boolean; - paused: boolean; - elapsedSeconds: number; toggleRecording: () => void; - togglePaused: () => void; restartRecording: () => void; cancelRecording: () => void; microphoneEnabled: boolean; @@ -89,8 +86,6 @@ function createRecorderHandle(stream: MediaStream, options: MediaRecorderOptions export function useScreenRecorder(): UseScreenRecorderReturn { const t = useScopedT("editor"); const [recording, setRecording] = useState(false); - const [paused, setPaused] = useState(false); - const [elapsedSeconds, setElapsedSeconds] = useState(0); const [microphoneEnabled, setMicrophoneEnabled] = useState(false); const [microphoneDeviceId, setMicrophoneDeviceId] = useState(undefined); const [webcamDeviceId, setWebcamDeviceId] = useState(undefined); @@ -103,20 +98,13 @@ export function useScreenRecorder(): UseScreenRecorderReturn { const microphoneStream = useRef(null); const webcamStream = useRef(null); const mixingContext = useRef(null); + const startTime = useRef(0); const recordingId = useRef(0); - const accumulatedDurationMs = useRef(0); - const segmentStartedAt = useRef(null); const finalizingRecordingId = useRef(null); const allowAutoFinalize = useRef(false); const discardRecordingId = useRef(null); const restarting = useRef(false); - const getRecordingDurationMs = useCallback(() => { - const segmentDuration = - segmentStartedAt.current === null ? 0 : Date.now() - segmentStartedAt.current; - return accumulatedDurationMs.current + segmentDuration; - }, []); - const selectMimeType = () => { const preferred = [ "video/webm;codecs=av1", @@ -215,10 +203,6 @@ export function useScreenRecorder(): UseScreenRecorderReturn { teardownMedia(); setRecording(false); - setPaused(false); - setElapsedSeconds(0); - accumulatedDurationMs.current = 0; - segmentStartedAt.current = null; window.electronAPI?.setRecordingState(false); void (async () => { @@ -290,7 +274,7 @@ export function useScreenRecorder(): UseScreenRecorderReturn { } const activeWebcamRecorder = webcamRecorder.current; - const duration = getRecordingDurationMs(); + const duration = Date.now() - startTime.current; const activeRecordingId = recordingId.current; finalizeRecording( @@ -300,10 +284,7 @@ export function useScreenRecorder(): UseScreenRecorderReturn { activeRecordingId, ); - if ( - activeScreenRecorder.recorder.state === "recording" || - activeScreenRecorder.recorder.state === "paused" - ) { + if (activeScreenRecorder.recorder.state === "recording") { try { activeScreenRecorder.recorder.stop(); } catch { @@ -311,10 +292,7 @@ export function useScreenRecorder(): UseScreenRecorderReturn { } } if (activeWebcamRecorder) { - if ( - activeWebcamRecorder.recorder.state === "recording" || - activeWebcamRecorder.recorder.state === "paused" - ) { + if (activeWebcamRecorder.recorder.state === "recording") { try { activeWebcamRecorder.recorder.stop(); } catch { @@ -339,20 +317,14 @@ export function useScreenRecorder(): UseScreenRecorderReturn { restarting.current = false; discardRecordingId.current = null; - if ( - screenRecorder.current?.recorder.state === "recording" || - screenRecorder.current?.recorder.state === "paused" - ) { + if (screenRecorder.current?.recorder.state === "recording") { try { screenRecorder.current.recorder.stop(); } catch { // Ignore recorder teardown errors during cleanup. } } - if ( - webcamRecorder.current?.recorder.state === "recording" || - webcamRecorder.current?.recorder.state === "paused" - ) { + if (webcamRecorder.current?.recorder.state === "recording") { try { webcamRecorder.current.recorder.stop(); } catch { @@ -547,12 +519,9 @@ export function useScreenRecorder(): UseScreenRecorderReturn { } recordingId.current = Date.now(); - accumulatedDurationMs.current = 0; - segmentStartedAt.current = Date.now(); + startTime.current = recordingId.current; allowAutoFinalize.current = true; setRecording(true); - setPaused(false); - setElapsedSeconds(0); window.electronAPI?.setRecordingState(true); const activeScreenRecorder = screenRecorder.current; @@ -568,7 +537,7 @@ export function useScreenRecorder(): UseScreenRecorderReturn { finalizeRecording( activeScreenRecorder, activeWebcamRecorder ?? null, - Math.max(0, getRecordingDurationMs()), + Math.max(0, Date.now() - startTime.current), activeRecordingId, ); }, @@ -584,56 +553,12 @@ export function useScreenRecorder(): UseScreenRecorderReturn { toast.error(errorMsg); } setRecording(false); - setPaused(false); - setElapsedSeconds(0); - accumulatedDurationMs.current = 0; - segmentStartedAt.current = null; screenRecorder.current = null; webcamRecorder.current = null; teardownMedia(); } }; - const togglePaused = () => { - const activeScreenRecorder = screenRecorder.current?.recorder; - if (!activeScreenRecorder || activeScreenRecorder.state === "inactive") { - return; - } - - const activeWebcamRecorder = webcamRecorder.current?.recorder; - - if (activeScreenRecorder.state === "paused") { - try { - activeScreenRecorder.resume(); - if (activeWebcamRecorder?.state === "paused") { - activeWebcamRecorder.resume(); - } - segmentStartedAt.current = Date.now(); - setPaused(false); - } catch (error) { - console.error("Failed to resume recording:", error); - } - return; - } - - if (activeScreenRecorder.state !== "recording") { - return; - } - - try { - accumulatedDurationMs.current = getRecordingDurationMs(); - segmentStartedAt.current = null; - setElapsedSeconds(Math.floor(accumulatedDurationMs.current / 1000)); - activeScreenRecorder.pause(); - if (activeWebcamRecorder?.state === "recording") { - activeWebcamRecorder.pause(); - } - setPaused(true); - } catch (error) { - console.error("Failed to pause recording:", error); - } - }; - const toggleRecording = () => { recording ? stopRecording.current() : startRecording(); }; @@ -642,7 +567,7 @@ export function useScreenRecorder(): UseScreenRecorderReturn { if (restarting.current) return; const activeScreenRecorder = screenRecorder.current; - if (!activeScreenRecorder || activeScreenRecorder.recorder.state === "inactive") return; + if (!activeScreenRecorder || activeScreenRecorder.recorder.state !== "recording") return; const activeWebcamRecorder = webcamRecorder.current; const activeRecordingId = recordingId.current; @@ -657,10 +582,7 @@ export function useScreenRecorder(): UseScreenRecorderReturn { }), ]; - if ( - activeWebcamRecorder?.recorder.state === "recording" || - activeWebcamRecorder?.recorder.state === "paused" - ) { + if (activeWebcamRecorder?.recorder.state === "recording") { stopPromises.push( new Promise((resolve) => { activeWebcamRecorder.recorder.addEventListener("stop", () => resolve(), { @@ -691,30 +613,9 @@ export function useScreenRecorder(): UseScreenRecorderReturn { stopRecording.current(); }; - useEffect(() => { - if (!recording) { - setElapsedSeconds(0); - return; - } - - setElapsedSeconds(Math.floor(getRecordingDurationMs() / 1000)); - if (paused) { - return; - } - - const interval = window.setInterval(() => { - setElapsedSeconds(Math.floor(getRecordingDurationMs() / 1000)); - }, 250); - - return () => window.clearInterval(interval); - }, [getRecordingDurationMs, paused, recording]); - return { recording, - paused, - elapsedSeconds, toggleRecording, - togglePaused, restartRecording, cancelRecording, microphoneEnabled, diff --git a/src/i18n/locales/en/launch.json b/src/i18n/locales/en/launch.json index cf111c4..c1229cc 100644 --- a/src/i18n/locales/en/launch.json +++ b/src/i18n/locales/en/launch.json @@ -4,8 +4,6 @@ "closeApp": "Close App", "restartRecording": "Restart recording", "cancelRecording": "Cancel recording", - "pauseRecording": "Pause recording", - "resumeRecording": "Resume recording", "openVideoFile": "Open video file", "openProject": "Open project" }, diff --git a/src/i18n/locales/es/launch.json b/src/i18n/locales/es/launch.json index f47bc81..f5be07c 100644 --- a/src/i18n/locales/es/launch.json +++ b/src/i18n/locales/es/launch.json @@ -4,8 +4,6 @@ "closeApp": "Cerrar aplicación", "restartRecording": "Reiniciar grabación", "cancelRecording": "Cancelar grabación", - "pauseRecording": "Pausar grabación", - "resumeRecording": "Reanudar grabación", "openVideoFile": "Abrir archivo de video", "openProject": "Abrir proyecto" }, diff --git a/src/i18n/locales/zh-CN/launch.json b/src/i18n/locales/zh-CN/launch.json index 6b63df1..0c2b319 100644 --- a/src/i18n/locales/zh-CN/launch.json +++ b/src/i18n/locales/zh-CN/launch.json @@ -4,8 +4,6 @@ "closeApp": "关闭应用", "restartRecording": "重新开始录制", "cancelRecording": "取消录制", - "pauseRecording": "暂停录制", - "resumeRecording": "继续录制", "openVideoFile": "打开视频文件", "openProject": "打开项目" },