stop_video
Ends active video recording on an Android device to complete screen capture sessions and save recorded content.
Instructions
Stop the active video recording for the specified device.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| device_id | No |
Implementation Reference
- puppeteer.py:1184-1266 (handler)Handler function for the stop_video tool. Stops the scrcpy recording process by sending SIGTERM (graceful) or SIGKILL (force) to the process group, calculates duration, checks file existence/size, and cleans up the global active_recordings tracker. Registered via @mcp.tool() decorator.
@mcp.tool() async def stop_video(device_id: str = None) -> dict: """Stop the active video recording for the specified device.""" try: recording_key = device_id or "default" if recording_key not in active_recordings: return { "success": False, "error": f"No active recording found for device {recording_key}", "device_id": device_id or "default" } recording_info = active_recordings[recording_key] process = recording_info["process"] filepath = recording_info["filepath"] filename = recording_info["filename"] start_time = recording_info["start_time"] # Check if process is still running if process.poll() is not None: # Process already terminated del active_recordings[recording_key] return { "success": False, "error": "Recording process has already terminated", "filepath": filepath, "filename": filename } # Gracefully terminate the scrcpy process try: # Send SIGTERM to the process group os.killpg(os.getpgid(process.pid), signal.SIGTERM) # Wait for the process to terminate (with timeout) process.wait(timeout=10) except subprocess.TimeoutExpired: # If it doesn't terminate gracefully, force kill os.killpg(os.getpgid(process.pid), signal.SIGKILL) process.wait() except ProcessLookupError: # Process already terminated pass # Remove from active recordings del active_recordings[recording_key] # Calculate recording duration end_time = datetime.now() duration = end_time - start_time duration_seconds = duration.total_seconds() # Check if file exists and get size file_size = None file_exists = os.path.exists(filepath) if file_exists: file_size = os.path.getsize(filepath) return { "success": True, "message": f"Video recording stopped successfully", "filepath": filepath if file_exists else None, "filename": filename, "device_id": device_id or "default", "duration_seconds": duration_seconds, "file_size_bytes": file_size, "file_exists": file_exists } except Exception as e: # Clean up the recording entry even if there was an error recording_key = device_id or "default" if recording_key in active_recordings: del active_recordings[recording_key] return { "success": False, "error": f"Error stopping recording: {e}", "device_id": device_id or "default" } - puppeteer.py:20-21 (helper)Global dictionary used by both record_video and stop_video tools to track active scrcpy recording processes, filepaths, and metadata for proper termination.
# Global dictionary to track active video recordings active_recordings = {} - puppeteer.py:1067-1182 (handler)Related handler for record_video tool, which initializes the recording process tracked by stop_video.
async def record_video(device_id: str = None, filename: str = None, resolution: str = None, bitrate: str = "8M") -> dict: """Start recording a video using scrcpy. The video will be saved to the videos directory.""" try: # Use android-puppeteer/videos directory for video recordings current_dir = os.path.dirname(os.path.abspath(__file__)) videos_dir = os.path.join(current_dir, "videos") os.makedirs(videos_dir, exist_ok=True) # Generate filename if not provided if filename: # Ensure it has .mp4 extension if not filename.endswith('.mp4'): filename = f"{filename}.mp4" else: # Use timestamp if no filename provided timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"recording_{timestamp}.mp4" filepath = os.path.join(videos_dir, filename) # Create recording key for tracking recording_key = device_id or "default" # Check if already recording for this device if recording_key in active_recordings: return { "success": False, "error": f"Already recording for device {recording_key}. Stop the current recording first.", "device_id": device_id or "default" } # Build scrcpy command for recording cmd = ['scrcpy'] # Add device selection if specified if device_id: cmd.extend(['-s', device_id]) # Add recording parameters cmd.extend(['--record', filepath]) # Add video quality parameters cmd.extend(['--video-bit-rate', bitrate]) if resolution: cmd.extend(['--max-size', resolution]) # Add minimal parameters for recording cmd.extend([ '--no-playback' # Don't show the device screen (updated flag for scrcpy v3.2+) ]) # Start the recording process process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=os.setsid # Create new process group for proper termination ) # Give the process a moment to start and check if it's still running import time time.sleep(1) # Check if process started successfully if process.poll() is not None: # Process terminated immediately, capture error output stderr_output = process.stderr.read().decode() if process.stderr else "" stdout_output = process.stdout.read().decode() if process.stdout else "" return { "success": False, "error": f"scrcpy process terminated immediately. stderr: {stderr_output}, stdout: {stdout_output}", "filepath": None, "command": ' '.join(cmd) } # Store the process for later termination active_recordings[recording_key] = { "process": process, "filepath": filepath, "filename": filename, "start_time": datetime.now(), "device_id": device_id or "default" } return { "success": True, "message": f"Video recording started successfully", "filepath": filepath, "filename": filename, "device_id": device_id or "default", "recording_key": recording_key, "bitrate": bitrate, "resolution": resolution, "process_id": process.pid } except FileNotFoundError: return { "success": False, "error": "scrcpy not found. Please ensure scrcpy is installed and in PATH.", "filepath": None } except PermissionError: return { "success": False, "error": f"Permission denied: Cannot create directory or write to {videos_dir}", "filepath": None } except Exception as e: return { "success": False, "error": f"Unexpected error starting recording: {e}", "filepath": None }