revert: undo manual merge of PR #314
This commit is contained in:
Generated
+2
-2
@@ -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",
|
||||
|
||||
@@ -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<number | null>(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 */}
|
||||
<button
|
||||
className={`flex items-center gap-0.5 rounded-full p-2 transition-colors duration-150 ${styles.electronNoDrag} ${
|
||||
recording
|
||||
? paused
|
||||
? "bg-amber-500/10 hover:bg-amber-500/15"
|
||||
: "animate-record-pulse bg-red-500/10"
|
||||
: "bg-white/5 hover:bg-white/[0.08]"
|
||||
recording ? "animate-record-pulse bg-red-500/10" : "bg-white/5 hover:bg-white/[0.08]"
|
||||
}`}
|
||||
onClick={toggleRecording}
|
||||
disabled={!hasSelectedSource && !recording}
|
||||
@@ -446,11 +458,9 @@ export function LaunchWindow() {
|
||||
>
|
||||
{recording ? (
|
||||
<>
|
||||
{getIcon("stop", paused ? "text-amber-400" : "text-red-400")}
|
||||
<span
|
||||
className={`${paused ? "text-amber-400" : "text-red-400"} text-xs font-semibold tabular-nums`}
|
||||
>
|
||||
{formatTimePadded(elapsedSeconds)}
|
||||
{getIcon("stop", "text-red-400")}
|
||||
<span className="text-red-400 text-xs font-semibold tabular-nums">
|
||||
{formatTimePadded(elapsed)}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
@@ -458,17 +468,6 @@ export function LaunchWindow() {
|
||||
)}
|
||||
</button>
|
||||
|
||||
{recording && (
|
||||
<Tooltip content={paused ? t("tooltips.resumeRecording") : t("tooltips.pauseRecording")}>
|
||||
<button
|
||||
className={`${hudIconBtnClasses} ${styles.electronNoDrag}`}
|
||||
onClick={togglePaused}
|
||||
>
|
||||
{getIcon(paused ? "resume" : "pause", paused ? "text-amber-400" : "text-white/60")}
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{/* Restart recording */}
|
||||
{recording && (
|
||||
<Tooltip content={t("tooltips.restartRecording")}>
|
||||
|
||||
+10
-109
@@ -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<string | undefined>(undefined);
|
||||
const [webcamDeviceId, setWebcamDeviceId] = useState<string | undefined>(undefined);
|
||||
@@ -103,20 +98,13 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
|
||||
const microphoneStream = useRef<MediaStream | null>(null);
|
||||
const webcamStream = useRef<MediaStream | null>(null);
|
||||
const mixingContext = useRef<AudioContext | null>(null);
|
||||
const startTime = useRef<number>(0);
|
||||
const recordingId = useRef<number>(0);
|
||||
const accumulatedDurationMs = useRef(0);
|
||||
const segmentStartedAt = useRef<number | null>(null);
|
||||
const finalizingRecordingId = useRef<number | null>(null);
|
||||
const allowAutoFinalize = useRef(false);
|
||||
const discardRecordingId = useRef<number | null>(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<void>((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,
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
"closeApp": "关闭应用",
|
||||
"restartRecording": "重新开始录制",
|
||||
"cancelRecording": "取消录制",
|
||||
"pauseRecording": "暂停录制",
|
||||
"resumeRecording": "继续录制",
|
||||
"openVideoFile": "打开视频文件",
|
||||
"openProject": "打开项目"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user