Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions src/cli/CliRecordRunner.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ describe("CliRecordRunner", () => {
recorder.startRecordingImmediately.mockClear();
recorder.toggleRecording.mockClear();
window.electronAPI = {
getPlatform: vi.fn(() => "win32"),
isNativeLinuxCaptureAvailable: vi.fn(async () => ({ success: true, available: false })),
cliGetRequest: vi.fn(async () => request),
cliLog: vi.fn(),
cliDone: vi.fn(async () => undefined),
Expand Down Expand Up @@ -81,4 +83,45 @@ describe("CliRecordRunner", () => {
await waitFor(() => expect(recorder.startRecordingImmediately).toHaveBeenCalledTimes(1));
expect(window.electronAPI.cliDone).not.toHaveBeenCalled();
});

it("keeps source selection for Linux without the native helper", async () => {
vi.mocked(window.electronAPI.getPlatform).mockReturnValue("linux");
render(<CliRecordRunner />);
await waitFor(() => expect(recorder.startRecordingImmediately).toHaveBeenCalledTimes(1));
expect(window.electronAPI.getSources).toHaveBeenCalledTimes(1);
expect(window.electronAPI.selectSource).toHaveBeenCalledWith(screenSource, { persist: false });
});

it("reports missing sources in the browser fallback", async () => {
vi.mocked(window.electronAPI.getPlatform).mockReturnValue("linux");
vi.mocked(window.electronAPI.getSources).mockResolvedValueOnce([]);
render(<CliRecordRunner />);
await waitFor(() =>
expect(window.electronAPI.cliDone).toHaveBeenCalledWith(
expect.objectContaining({
success: false,
error: expect.stringContaining("Display index 0 not found"),
}),
),
);
expect(recorder.startRecordingImmediately).not.toHaveBeenCalled();
});

it("lets the native Linux portal choose the source without Chromium enumeration", async () => {
vi.mocked(window.electronAPI.getPlatform).mockReturnValue("linux");
vi.mocked(window.electronAPI.isNativeLinuxCaptureAvailable).mockResolvedValue({
success: true,
available: true,
});
vi.mocked(window.electronAPI.getSources).mockResolvedValueOnce([]);
render(<CliRecordRunner />);
await waitFor(() => expect(recorder.startRecordingImmediately).toHaveBeenCalledTimes(1));
expect(window.electronAPI.getSources).not.toHaveBeenCalled();
expect(window.electronAPI.selectSource).not.toHaveBeenCalled();
expect(window.electronAPI.cliDone).not.toHaveBeenCalled();
expect(window.electronAPI.cliLog).toHaveBeenCalledWith(
"info",
expect.stringContaining("--window do not apply"),
);
});
});
24 changes: 17 additions & 7 deletions src/cli/CliRecordRunner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from "@/components/video-editor/projectPersistence";
import { useScreenRecorder } from "@/hooks/useScreenRecorder";
import type { CliRecordRequest } from "@/lib/cliContracts";
import { portalOwnsSourceSelection } from "@/lib/nativeLinuxRecording";
import { DEFAULT_PROJECT_APPEARANCE } from "@/lib/projectDefaults";

type Phase = "init" | "recording" | "stopping" | "done";
Expand Down Expand Up @@ -164,15 +165,24 @@ export function CliRecordRunner() {
}
requestRef.current = request;

const source = await pickSource(request);
const selected = await window.electronAPI.selectSource(source, { persist: false });
if (!selected) {
throw new Error(
`Recording source "${source.name}" (${source.id}) is no longer available. ` +
"Re-list sources with `openscreen sources` and pick one that is currently shared.",
// The native Linux helper asks the portal to choose a source. Enumerating
// Chromium sources first opens a redundant picker and can return no screens.
if (await portalOwnsSourceSelection(window.electronAPI)) {
window.electronAPI.cliLog(
"info",
"Recording source: chosen in the system picker (--display and --window do not apply)",
);
} else {
const source = await pickSource(request);
const selected = await window.electronAPI.selectSource(source, { persist: false });
if (!selected) {
throw new Error(
`Recording source "${source.name}" (${source.id}) is no longer available. ` +
"Re-list sources with `openscreen sources` and pick one that is currently shared.",
);
}
window.electronAPI.cliLog("info", `Recording source: ${selected.name}`);
}
window.electronAPI.cliLog("info", `Recording source: ${selected.name}`);

setMicrophoneEnabled(Boolean(request.mic));
if (request.mic) {
Expand Down
Loading