Skip to content

feat: support screenrecord#178

Open
gmegidish wants to merge 1 commit intomainfrom
feat-support-screenrecord
Open

feat: support screenrecord#178
gmegidish wants to merge 1 commit intomainfrom
feat-support-screenrecord

Conversation

@gmegidish
Copy link
Member

No description provided.

@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

Walkthrough

This change introduces end-to-end screen recording functionality across the codebase. It adds a new CLI subcommand screenrecord with configurable flags for device ID, bit-rate, time-limit, and output path. A command handler orchestrates the operation by resolving the target device, validating and setting defaults for bit-rate and time-limit, determining the output path, and invoking the device's recording capability. The ControllableDevice interface is extended with a RecordVideo method and supporting configuration struct. Android devices implement video recording via adb shell screenrecord with signal handling and file transfer. iOS, remote, and simulator device types define the method with not-implemented errors.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to assess whether any description relates to the changeset. Add a description explaining the screenrecord feature, its purpose, supported devices (iOS/Android), and any relevant implementation details or testing notes.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title 'feat: support screenrecord' clearly and concisely summarizes the main change: introducing screenrecord capability as a new feature.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat-support-screenrecord

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
commands/screenrecord.go (2)

102-108: Starting the agent is unnecessary for Android and may cause errors for unimplemented device types.

StartAgent is always called before RecordVideo. For Android, this is a no-op. For iOS, simulator, and remote devices, RecordVideo returns "not yet implemented" anyway, so starting the agent is wasted effort (or could fail, masking the real error). This is acceptable for now, but consider guarding with a platform check or moving agent start into the device's RecordVideo implementation when iOS support is added.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/screenrecord.go` around lines 102 - 108, The code always calls
targetDevice.StartAgent(...) before RecordVideo which is unnecessary for Android
(no-op) and may fail or mask errors for unimplemented types
(iOS/simulator/remote); update the logic to avoid starting the agent
unconditionally—either add a platform/type check before calling StartAgent
(e.g., only call StartAgent for device types that require it) or remove the
StartAgent call here and move agent startup into each device's RecordVideo
implementation (so StartAgent and GetShutdownHook are invoked only when needed).

58-123: Recording provides no progress feedback to the user during long operations.

RecordVideo is called without an OnProgress callback (Line 111), so the user sees no output during recording (which can last up to 5 minutes). The Android implementation supports OnProgress for "Recording video" and "Pulling recorded video" messages. Consider wiring it up, e.g., via fmt.Fprintf(os.Stderr, ...).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/screenrecord.go` around lines 58 - 123, ScreenRecordCommand
currently calls targetDevice.RecordVideo(devices.RecordVideoConfig{...}) without
providing an OnProgress callback, so long recordings show no feedback; update
the devices.RecordVideoConfig passed in ScreenRecordCommand to set OnProgress to
a function that writes progress messages to stderr (e.g., fmt.Fprintf(os.Stderr,
"...")), and if there is a separate pull step (e.g., PullRecordedVideo) also
wire an OnProgress callback there—use the symbols ScreenRecordCommand,
targetDevice.RecordVideo, and devices.RecordVideoConfig to locate and update the
call site.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@commands/screenrecord.go`:
- Around line 27-56: parseBitRate currently accepts zero and negative values
(e.g., "-5M"), which are passed to adb; after parsing the numeric value in
parseBitRate (both in the "M" and "K" branches and the plain-number branch),
validate that the resulting bit-rate (num or computed value) is > 0 and return
an error like "bit-rate must be positive" if not; you can either perform the
check in each branch after computing the int value or consolidate by converting
all branches to a single positive-int validation before returning.

In `@devices/android.go`:
- Around line 1264-1290: RecordVideo's local signal handling (sigChan +
signal.Notify) creates a race with the global shutdown and is Windows-unsafe;
remove the local signal registration and instead accept a context.Context (or
use the existing shutdown context) in RecordVideo so cancellation is driven by
main's hook, then when cancelling wait for cmd.Wait() to finish before pulling
the file; also replace the unhandled call to cmd.Process.Signal(syscall.SIGINT)
in the interrupt path with a platform-safe attempt that checks the error and
falls back to cmd.Process.Kill() on failure (or when runtime.GOOS == "windows")
so Windows users can stop recording cleanly.

---

Nitpick comments:
In `@commands/screenrecord.go`:
- Around line 102-108: The code always calls targetDevice.StartAgent(...) before
RecordVideo which is unnecessary for Android (no-op) and may fail or mask errors
for unimplemented types (iOS/simulator/remote); update the logic to avoid
starting the agent unconditionally—either add a platform/type check before
calling StartAgent (e.g., only call StartAgent for device types that require it)
or remove the StartAgent call here and move agent startup into each device's
RecordVideo implementation (so StartAgent and GetShutdownHook are invoked only
when needed).
- Around line 58-123: ScreenRecordCommand currently calls
targetDevice.RecordVideo(devices.RecordVideoConfig{...}) without providing an
OnProgress callback, so long recordings show no feedback; update the
devices.RecordVideoConfig passed in ScreenRecordCommand to set OnProgress to a
function that writes progress messages to stderr (e.g., fmt.Fprintf(os.Stderr,
"...")), and if there is a separate pull step (e.g., PullRecordedVideo) also
wire an OnProgress callback there—use the symbols ScreenRecordCommand,
targetDevice.RecordVideo, and devices.RecordVideoConfig to locate and update the
call site.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 87bc7cc and d20abc7.

📒 Files selected for processing (8)
  • cli/flags.go
  • cli/screenrecord.go
  • commands/screenrecord.go
  • devices/android.go
  • devices/common.go
  • devices/ios.go
  • devices/remote.go
  • devices/simulator.go

Comment on lines +27 to +56
func parseBitRate(s string) (int, error) {
s = strings.TrimSpace(s)
if s == "" {
return 0, fmt.Errorf("empty bit-rate string")
}

upper := strings.ToUpper(s)

if strings.HasSuffix(upper, "M") {
num, err := strconv.ParseFloat(upper[:len(upper)-1], 64)
if err != nil {
return 0, fmt.Errorf("invalid bit-rate: %w", err)
}
return int(num * 1_000_000), nil
}

if strings.HasSuffix(upper, "K") {
num, err := strconv.ParseFloat(upper[:len(upper)-1], 64)
if err != nil {
return 0, fmt.Errorf("invalid bit-rate: %w", err)
}
return int(num * 1_000), nil
}

num, err := strconv.Atoi(s)
if err != nil {
return 0, fmt.Errorf("invalid bit-rate: %w", err)
}
return num, nil
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

No validation that parsed bit-rate is positive.

parseBitRate successfully parses negative values (e.g., "-5M"-5000000) and zero. These are passed directly to adb screenrecord --bit-rate, which will either reject them with a confusing error or produce unexpected behavior. Consider adding a positivity check after parsing.

Proposed fix
 	num, err := strconv.Atoi(s)
 	if err != nil {
 		return 0, fmt.Errorf("invalid bit-rate: %w", err)
 	}
+	if num <= 0 {
+		return 0, fmt.Errorf("bit-rate must be a positive value, got %d", num)
+	}
 	return num, nil
 }

The same check should be applied after the M and K suffix branches as well, or consolidated after all branches:

+func parseBitRate(s string) (int, error) {
+	// ... existing parsing logic ...
+	// After computing the final int value:
+	if result <= 0 {
+		return 0, fmt.Errorf("bit-rate must be a positive value")
+	}
+	return result, nil
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@commands/screenrecord.go` around lines 27 - 56, parseBitRate currently
accepts zero and negative values (e.g., "-5M"), which are passed to adb; after
parsing the numeric value in parseBitRate (both in the "M" and "K" branches and
the plain-number branch), validate that the resulting bit-rate (num or computed
value) is > 0 and return an error like "bit-rate must be positive" if not; you
can either perform the check in each branch after computing the int value or
consolidate by converting all branches to a single positive-int validation
before returning.

Comment on lines +1264 to +1290
// set up signal handling so Ctrl+C stops recording gracefully
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
defer signal.Stop(sigChan)

if err := cmd.Start(); err != nil {
return fmt.Errorf("failed to start screenrecord: %w", err)
}

// wait for either process exit or signal
done := make(chan error, 1)
go func() {
done <- cmd.Wait()
}()

select {
case <-sigChan:
// user interrupted — send SIGINT to adb so screenrecord finalizes the mp4
utils.Verbose("Interrupt received, stopping recording...")
_ = cmd.Process.Signal(syscall.SIGINT)
<-done // wait for process to finish writing
case err := <-done:
if err != nil {
// screenrecord may exit non-zero on some devices; log but continue
utils.Verbose("screenrecord exited with: %v", err)
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's look for all signal handling in the codebase
rg -n 'signal\.Notify' --type=go -C3

Repository: mobile-next/mobilecli

Length of output: 1461


🏁 Script executed:

# Check for GetShutdownHook usage
rg -n 'GetShutdownHook\|shutdown' --type=go

Repository: mobile-next/mobilecli

Length of output: 47


🏁 Script executed:

# Look at the RecordVideo function context around the signal handling
fd -e go | xargs grep -l "RecordVideo\|signal.Notify" | head -5

Repository: mobile-next/mobilecli

Length of output: 159


🏁 Script executed:

# Check the specific file context
wc -l devices/android.go

Repository: mobile-next/mobilecli

Length of output: 88


🏁 Script executed:

# Check the ScreenRecordCommand to see if it uses shutdown hooks
rg -n 'ScreenRecordCommand\|GetShutdownHook' --type=go -C5

Repository: mobile-next/mobilecli

Length of output: 47


🏁 Script executed:

# Look at the specific signal handling line in android.go to see exact error handling
sed -n '1280,1290p' devices/android.go

Repository: mobile-next/mobilecli

Length of output: 478


🏁 Script executed:

# Search for Windows-specific handling or comments about Windows
rg -n 'windows\|Windows\|SIGINT' --type=go | grep -i windows

Repository: mobile-next/mobilecli

Length of output: 47


🏁 Script executed:

# Check if there's any build tags or Windows-specific signal handling
rg -n '//\s*\+build\|GOOS' --type=go

Repository: mobile-next/mobilecli

Length of output: 47


🏁 Script executed:

# Look for where RecordVideo is called from
rg -n 'RecordVideo' --type=go

Repository: mobile-next/mobilecli

Length of output: 722


🏁 Script executed:

# Check if there's a shutdown hook pattern in the codebase
rg -n 'shutdown\|Shutdown' --type=go | head -20

Repository: mobile-next/mobilecli

Length of output: 47


🏁 Script executed:

# Look at main.go to see the full shutdown pattern
cat -n main.go | head -60

Repository: mobile-next/mobilecli

Length of output: 1522


🏁 Script executed:

# Check what ShutdownHook does
rg -n 'NewShutdownHook\|type.*ShutdownHook' --type=go -A 15

Repository: mobile-next/mobilecli

Length of output: 47


🏁 Script executed:

# Look at SetShutdownHook in commands package
rg -n 'SetShutdownHook' --type=go -C 5

Repository: mobile-next/mobilecli

Length of output: 2619


🏁 Script executed:

# Check if the hook kills child processes or just cleans up
rg -n 'Shutdown\(\)' --type=go -C 3 | head -40

Repository: mobile-next/mobilecli

Length of output: 1665


🏁 Script executed:

# Look at the full RecordVideo function to understand the complete flow
sed -n '1241,1311p' devices/android.go

Repository: mobile-next/mobilecli

Length of output: 2260


🏁 Script executed:

# Search for how other RecordVideo implementations (iOS, simulator) handle signals
sed -n '1174,1250p' devices/ios.go

Repository: mobile-next/mobilecli

Length of output: 2818


🏁 Script executed:

# Check if there's any cleanup registered in ScreenRecordCommand
cat -n commands/screenrecord.go | head -150

Repository: mobile-next/mobilecli

Length of output: 4187


🏁 Script executed:

# Check if context.Context is used elsewhere in RecordVideo interface or similar functions
rg -n 'context.Context' --type=go | head -20

Repository: mobile-next/mobilecli

Length of output: 257


🏁 Script executed:

# Verify Windows behavior of syscall.SIGINT - check documentation comments
rg -n 'windows\|Windows\|signal' devices/android.go -C 2

Repository: mobile-next/mobilecli

Length of output: 47


Process-wide signal interception creates a race condition that can prevent file download, and graceful stop is broken on Windows.

Two critical issues with the signal handling:

  1. Race condition with process shutdown: signal.Notify in RecordVideo registers for SIGINT/SIGTERM while main.go already has a process-wide handler registered for the same signals. In Go, both handlers receive the signal simultaneously. When Ctrl+C is pressed, main's signal handler (line 41 of main.go) calls hook.Shutdown() and os.Exit(0) immediately, terminating the process before RecordVideo can complete the file pull (lines 1292–1296). This leaves the recording on the device or partially downloaded.

  2. Windows incompatibility: cmd.Process.Signal(syscall.SIGINT) is not supported on Windows and silently fails (the error is discarded). Users on Windows cannot gracefully stop recording early and must wait for the full --time-limit duration, or kill the process (which may corrupt the MP4).

Fix: Remove the local signal handler in RecordVideo and instead pass a context.Context (tied to the main shutdown hook) to allow cancellation. Or, coordinate with main's handler to prevent process exit while the goroutine is still pulling the file. For Windows, fallback to cmd.Process.Kill() if Signal() fails.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@devices/android.go` around lines 1264 - 1290, RecordVideo's local signal
handling (sigChan + signal.Notify) creates a race with the global shutdown and
is Windows-unsafe; remove the local signal registration and instead accept a
context.Context (or use the existing shutdown context) in RecordVideo so
cancellation is driven by main's hook, then when cancelling wait for cmd.Wait()
to finish before pulling the file; also replace the unhandled call to
cmd.Process.Signal(syscall.SIGINT) in the interrupt path with a platform-safe
attempt that checks the error and falls back to cmd.Process.Kill() on failure
(or when runtime.GOOS == "windows") so Windows users can stop recording cleanly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant