Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): modernize command structure with subcommands #912

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
174 changes: 169 additions & 5 deletions screenpipe-server/src/bin/screenpipe-server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@
};
use screenpipe_core::find_ffmpeg_path;
use screenpipe_server::{
cli::{Cli, CliAudioTranscriptionEngine, CliOcrEngine, Command, OutputFormat, PipeCommand},
cli::{Cli, CliAudioTranscriptionEngine, CliOcrEngine, Command, OutputFormat, PipeCommand, ConfigCommand, DeviceCommand, MonitorCommand},
highlight::{Highlight, HighlightConfig},
pipe_manager::PipeInfo,
start_continuous_recording, watch_pid, DatabaseManager, PipeManager, ResourceMonitor, Server,
};
use screenpipe_vision::monitor::list_monitors;
use screenpipe_vision::monitor::{get_default_monitor, list_monitors};
#[cfg(target_os = "macos")]
use screenpipe_vision::run_ui;
use serde_json::{json, Value};
Expand All @@ -38,6 +38,8 @@
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::{fmt, EnvFilter};

static mut SELECTED_MONITORS: Vec<u32> = Vec::new();

fn print_devices(devices: &[AudioDevice]) {
println!("available audio devices:");
for device in devices.iter() {
Expand Down Expand Up @@ -260,6 +262,152 @@
info!("database migrations completed successfully");
return Ok(());
}
Command::Monitors { subcommand } => {
match subcommand {
MonitorCommand::List { output } => {
let monitors = list_monitors().await;
match output {
OutputFormat::Json => info!("{}", serde_json::to_string_pretty(&monitors.iter().map(|m| m.id()).collect::<Vec<_>>())?),
OutputFormat::Text => {
info!("available monitors:");
for monitor in monitors {
info!("monitor id: {}, name: {}, is primary: {}", monitor.id(), monitor.name(), monitor.is_primary());
}
}
}
return Ok(());
}
MonitorCommand::Select { id, save } => {
info!("validating monitor ids...");
let monitors = list_monitors().await;

// Validate monitor IDs
for monitor_id in &id {
if !monitors.iter().any(|m| m.id() == *monitor_id) {
warn!("invalid monitor id: {}", monitor_id);
return Err(anyhow::anyhow!("invalid monitor id: {}", monitor_id));
}
}

let selected_ids = if id.is_empty() {
// If no IDs provided, use default monitor
let default_monitor = get_default_monitor().await;
info!("no monitor ids provided, using default monitor: {:?}", default_monitor.id());
vec![default_monitor.id()]
} else {
info!("selected monitor ids: {:?}", id);
id
};

if save {
unsafe {
SELECTED_MONITORS = selected_ids;
}
info!("monitor selection saved in memory");
}

info!("monitor selection {}saved", if save { "" } else { "not " });
return Ok(());
}
}
}
Command::Devices { subcommand } => {
match subcommand {
DeviceCommand::List { output } => {
let devices = list_audio_devices().await.map_err(|e| {
error!("failed to list audio devices: {:?}", e);
e
})?;
match output {
OutputFormat::Json => info!("{}", serde_json::to_string_pretty(&devices)?),
OutputFormat::Text => {
info!("available audio devices:");
for device in devices {
info!(" {}", device);
}
}
}
return Ok(());
}
DeviceCommand::Select { id } => {
info!("selected audio devices: {:?}", id);
// TODO: Implement device selection persistence
warn!("audio device selection persistence not implemented yet");
return Ok(());
}
}
}
Command::Config { subcommand } => {
match subcommand {
ConfigCommand::Show { output } => {
let config = json!({
"fps": cli.fps,
"audio_chunk_duration": cli.audio_chunk_duration,
"video_chunk_duration": cli.video_chunk_duration,
"port": cli.port,
"audio_disabled": cli.disable_audio,
"vision_disabled": cli.disable_vision,
"save_text_files": cli.save_text_files,
"audio_transcription_engine": format!("{:?}", cli.audio_transcription_engine),
"ocr_engine": format!("{:?}", cli.ocr_engine),
"vad_engine": format!("{:?}", cli.vad_engine),
"vad_sensitivity": format!("{:?}", cli.vad_sensitivity),
"debug": cli.debug,
"telemetry": !cli.disable_telemetry,
"local_llm": cli.enable_llm,
"use_pii_removal": cli.use_pii_removal,
"ui_monitoring": cli.enable_ui_monitoring,
"frame_cache": cli.enable_frame_cache,
"data_dir": local_data_dir.to_string_lossy().to_string(),
"deepgram_api_key": cli.deepgram_api_key.as_deref().unwrap_or("not set"),
"ignored_windows": cli.ignored_windows,
"included_windows": cli.included_windows,
});
match output {
OutputFormat::Json => info!("{}", serde_json::to_string_pretty(&config)?),
OutputFormat::Text => {
info!("current configuration:");
for (key, value) in config.as_object().unwrap() {
info!(" {}: {}", key, value);
}
}
}
return Ok(());
}
ConfigCommand::Set {
fps, audio_chunk_duration, video_chunk_duration, port,
audio_transcription_engine, ocr_engine, vad_engine, vad_sensitivity,
disable_audio, disable_vision, save_text_files, debug,
disable_telemetry, enable_llm, use_pii_removal,
enable_ui_monitoring, enable_frame_cache,
data_dir, deepgram_api_key,
} => {
// TODO: Implement config persistence
warn!("config persistence not implemented yet");
info!("configuration updated (in-memory only):");
if let Some(v) = fps { info!(" fps: {}", v); }
if let Some(v) = audio_chunk_duration { info!(" audio_chunk_duration: {}", v); }
if let Some(v) = video_chunk_duration { info!(" video_chunk_duration: {}", v); }
if let Some(v) = port { info!(" port: {}", v); }
if let Some(v) = audio_transcription_engine { info!(" audio_transcription_engine: {:?}", v); }
if let Some(v) = ocr_engine { info!(" ocr_engine: {:?}", v); }
if let Some(v) = vad_engine { info!(" vad_engine: {:?}", v); }
if let Some(v) = vad_sensitivity { info!(" vad_sensitivity: {:?}", v); }
if let Some(v) = disable_audio { info!(" disable_audio: {}", v); }
if let Some(v) = disable_vision { info!(" disable_vision: {}", v); }
if let Some(v) = save_text_files { info!(" save_text_files: {}", v); }
if let Some(v) = debug { info!(" debug: {}", v); }
if let Some(v) = disable_telemetry { info!(" disable_telemetry: {}", v); }
if let Some(v) = enable_llm { info!(" enable_llm: {}", v); }
if let Some(v) = use_pii_removal { info!(" use_pii_removal: {}", v); }
if let Some(v) = enable_ui_monitoring { info!(" enable_ui_monitoring: {}", v); }
if let Some(v) = enable_frame_cache { info!(" enable_frame_cache: {}", v); }
if let Some(v) = data_dir { info!(" data_dir: {}", v); }
if let Some(_) = deepgram_api_key { info!(" deepgram_api_key: [hidden]"); }
return Ok(());
}
}
}
}
}

Expand All @@ -282,11 +430,13 @@
let all_audio_devices = list_audio_devices().await?;
let mut devices_status = HashMap::new();
if cli.list_audio_devices {
warn!("--list-audio-devices is deprecated and will be removed in a future version");
print_devices(&all_audio_devices);
return Ok(());
}
let all_monitors = list_monitors().await;
if cli.list_monitors {
warn!("--list-monitors is deprecated and will be removed in a future version");
println!("available monitors:");
for monitor in all_monitors.iter() {
println!(" {}. {:?}", monitor.id(), monitor);
Expand Down Expand Up @@ -383,10 +533,24 @@

let warning_ocr_engine_clone = cli.ocr_engine.clone();
let warning_audio_transcription_engine_clone = cli.audio_transcription_engine.clone();
let monitor_ids = if cli.monitor_id.is_empty() {
all_monitors.iter().map(|m| m.id()).collect::<Vec<_>>()
} else {
// let monitor_ids = if cli.monitor_id.is_empty() {
// all_monitors.iter().map(|m| m.id()).collect::<Vec<_>>()
// } else {
// cli.monitor_id.clone()
// };
let monitor_ids = if !cli.monitor_id.is_empty() {
warn!("--monitor-id is deprecated. please use 'screenpipe monitors select' instead");
cli.monitor_id.clone()
} else {
unsafe {
if !SELECTED_MONITORS.is_empty() {

Check warning on line 546 in screenpipe-server/src/bin/screenpipe-server.rs

View workflow job for this annotation

GitHub Actions / test-ubuntu

creating a shared reference to mutable static is discouraged

Check warning on line 546 in screenpipe-server/src/bin/screenpipe-server.rs

View workflow job for this annotation

GitHub Actions / test-linux

creating a shared reference to mutable static is discouraged

Check warning on line 546 in screenpipe-server/src/bin/screenpipe-server.rs

View workflow job for this annotation

GitHub Actions / test-windows

creating a shared reference to mutable static is discouraged

Check warning on line 546 in screenpipe-server/src/bin/screenpipe-server.rs

View workflow job for this annotation

GitHub Actions / test-macos

creating a shared reference to mutable static is discouraged
info!("using saved monitor selection: {:?}", SELECTED_MONITORS);

Check warning on line 547 in screenpipe-server/src/bin/screenpipe-server.rs

View workflow job for this annotation

GitHub Actions / test-ubuntu

creating a shared reference to mutable static is discouraged

Check warning on line 547 in screenpipe-server/src/bin/screenpipe-server.rs

View workflow job for this annotation

GitHub Actions / test-linux

creating a shared reference to mutable static is discouraged

Check warning on line 547 in screenpipe-server/src/bin/screenpipe-server.rs

View workflow job for this annotation

GitHub Actions / test-windows

creating a shared reference to mutable static is discouraged

Check warning on line 547 in screenpipe-server/src/bin/screenpipe-server.rs

View workflow job for this annotation

GitHub Actions / test-macos

creating a shared reference to mutable static is discouraged
SELECTED_MONITORS.clone()

Check warning on line 548 in screenpipe-server/src/bin/screenpipe-server.rs

View workflow job for this annotation

GitHub Actions / test-ubuntu

creating a shared reference to mutable static is discouraged

Check warning on line 548 in screenpipe-server/src/bin/screenpipe-server.rs

View workflow job for this annotation

GitHub Actions / test-linux

creating a shared reference to mutable static is discouraged

Check warning on line 548 in screenpipe-server/src/bin/screenpipe-server.rs

View workflow job for this annotation

GitHub Actions / test-windows

creating a shared reference to mutable static is discouraged

Check warning on line 548 in screenpipe-server/src/bin/screenpipe-server.rs

View workflow job for this annotation

GitHub Actions / test-macos

creating a shared reference to mutable static is discouraged
} else {
info!("no saved monitor selection found, using all monitors");
all_monitors.iter().map(|m| m.id()).collect()
}
}
};

let languages = cli.unique_languages().unwrap();
Expand Down
129 changes: 124 additions & 5 deletions screenpipe-server/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ pub struct Cli {
#[arg(short = 'i', long)]
pub audio_device: Vec<String>,

/// List available audio devices
#[arg(long)]
/// List available audio devices (deprecated: use 'screenpipe devices list' instead)
#[arg(long, hide = true)]
pub list_audio_devices: bool,

/// Data directory. Default to $HOME/.screenpipe
Expand Down Expand Up @@ -167,11 +167,11 @@ pub struct Cli {
)]
pub ocr_engine: CliOcrEngine,

/// List available monitors, then you can use --monitor-id to select one (with the ID)
#[arg(long)]
/// List available monitors (deprecated: use 'screenpipe monitors list' instead)
#[arg(long, hide = true)]
pub list_monitors: bool,

/// Monitor IDs to use, these will be used to select the monitors to record
/// Monitor IDs to use (deprecated: use 'screenpipe monitors select' instead)
#[arg(short = 'm', long)]
pub monitor_id: Vec<u32>,

Expand Down Expand Up @@ -265,6 +265,21 @@ pub enum Command {
#[command(subcommand)]
subcommand: PipeCommand,
},
/// Monitor management commands
Monitors {
#[command(subcommand)]
subcommand: MonitorCommand,
},
/// Device management commands
Devices {
#[command(subcommand)]
subcommand: DeviceCommand,
},
/// Configuration management commands
Config {
#[command(subcommand)]
subcommand: ConfigCommand,
},
/// Setup screenpipe environment
Setup {
/// Enable beta features
Expand All @@ -275,6 +290,110 @@ pub enum Command {
Migrate,
}

#[derive(Subcommand)]
pub enum MonitorCommand {
/// List all available monitors
List {
/// Output format
#[arg(short, long, value_enum, default_value_t = OutputFormat::Text)]
output: OutputFormat,
},
/// Select monitors to use for recording
Select {
/// Monitor IDs to use
#[arg(short = 'm', long)]
id: Vec<u32>,
/// Save selection to config
#[arg(short, long, default_value_t = true)]
save: bool,
},
}

#[derive(Subcommand)]
pub enum DeviceCommand {
/// List all available audio devices
List {
/// Output format
#[arg(short, long, value_enum, default_value_t = OutputFormat::Text)]
output: OutputFormat,
},
/// Select audio devices to use
Select {
/// Audio device IDs to use
#[arg(short, long)]
id: Vec<String>,
},
}

#[derive(Subcommand)]
pub enum ConfigCommand {
/// Show current configuration
Show {
/// Output format
#[arg(short, long, value_enum, default_value_t = OutputFormat::Text)]
output: OutputFormat,
},
/// Set configuration values
Set {
/// FPS value
#[arg(long)]
fps: Option<f32>,
/// Audio chunk duration in seconds
#[arg(long)]
audio_chunk_duration: Option<u64>,
/// Video chunk duration in seconds
#[arg(long)]
video_chunk_duration: Option<u64>,
/// Port number
#[arg(long)]
port: Option<u16>,
/// Audio transcription engine
#[arg(long, value_enum)]
audio_transcription_engine: Option<CliAudioTranscriptionEngine>,
/// OCR engine
#[arg(long, value_enum)]
ocr_engine: Option<CliOcrEngine>,
/// VAD engine
#[arg(long, value_enum)]
vad_engine: Option<CliVadEngine>,
/// VAD sensitivity
#[arg(long, value_enum)]
vad_sensitivity: Option<CliVadSensitivity>,
/// Enable/disable audio recording
#[arg(long)]
disable_audio: Option<bool>,
/// Enable/disable vision recording
#[arg(long)]
disable_vision: Option<bool>,
/// Enable/disable text file saving
#[arg(long)]
save_text_files: Option<bool>,
/// Enable/disable debug mode
#[arg(long)]
debug: Option<bool>,
/// Enable/disable telemetry
#[arg(long)]
disable_telemetry: Option<bool>,
/// Enable/disable local LLM
#[arg(long)]
enable_llm: Option<bool>,
/// Enable/disable PII removal
#[arg(long)]
use_pii_removal: Option<bool>,
/// Enable/disable UI monitoring
#[arg(long)]
enable_ui_monitoring: Option<bool>,
/// Enable/disable frame cache
#[arg(long)]
enable_frame_cache: Option<bool>,
/// Data directory path
#[arg(long)]
data_dir: Option<String>,
/// Deepgram API key
#[arg(long)]
deepgram_api_key: Option<String>,
},
}

#[derive(Subcommand)]
pub enum PipeCommand {
Expand Down
Loading