Skip to content

Commit

Permalink
fix: avoid loading model if audio disabled, feat: add restart period …
Browse files Browse the repository at this point in the history
…option
  • Loading branch information
louis030195 committed Aug 26, 2024
1 parent 8829a9d commit d4fadbe
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 24 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ resolver = "2"


[workspace.package]
version = "0.1.67"
version = "0.1.68"
authors = ["louis030195 <[email protected]>"]
description = ""
repository = "https://github.com/mediar-ai/screenpipe"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
TooltipTrigger,
} from "./ui/tooltip";
import { Switch } from "./ui/switch";
import { Input } from "./ui/input";

interface AudioDevice {
name: string;
Expand Down Expand Up @@ -196,6 +197,13 @@ export function RecordingSettings({
setLocalSettings({ ...localSettings, usePiiRemoval: checked });
};

const handleRestartIntervalChange = (
e: React.ChangeEvent<HTMLInputElement>
) => {
const newValue = parseInt(e.target.value, 10);
setLocalSettings({ ...localSettings, restartInterval: newValue });
};

return (
<>
<div className="relative">
Expand Down Expand Up @@ -399,6 +407,43 @@ export function RecordingSettings({
</Label>
</div>
</div>
<div className="flex flex-col space-y-2">
<Label
htmlFor="restartInterval"
className="flex items-center space-x-2"
>
<span>restart interval (minutes)</span>
<Badge variant="outline" className="ml-2">
experimental
</Badge>
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<HelpCircle className="h-4 w-4" />
</TooltipTrigger>
<TooltipContent>
<p>
set how often the recording process should restart.
<br />
0 means no automatic restart.
<br />
this can help mitigate potential memory leaks or other
issues.
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</Label>
<Input
id="restartInterval"
type="number"
min="0"
value={localSettings.restartInterval}
onChange={handleRestartIntervalChange}
className="w-full"
placeholder="Enter restart interval in minutes (0 to disable)"
/>
</div>

<div className="flex flex-col space-y-2">
<Button
Expand Down
5 changes: 5 additions & 0 deletions examples/apps/screenpipe-app-tauri/lib/hooks/use-settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const defaultSettings: Settings = {
monitorId: "default",
audioDevices: ["default"],
usePiiRemoval: false,
restartInterval: 0,
};

export interface Settings {
Expand All @@ -47,6 +48,7 @@ export interface Settings {
monitorId: string;
audioDevices: string[];
usePiiRemoval: boolean;
restartInterval: number;
}

let store: Store | null = null;
Expand Down Expand Up @@ -113,6 +115,8 @@ export function useSettings() {
)) as string[]) || ["default"];
const savedUsePiiRemoval =
((await store!.get("usePiiRemoval")) as boolean) || false;
const savedRestartInterval =
((await store!.get("restartInterval")) as number) || 0;

setSettings({
openaiApiKey: savedKey,
Expand All @@ -129,6 +133,7 @@ export function useSettings() {
monitorId: savedMonitorId,
audioDevices: savedAudioDevices,
usePiiRemoval: savedUsePiiRemoval,
restartInterval: savedRestartInterval,
});
} catch (error) {
console.error("Failed to load settings:", error);
Expand Down
2 changes: 1 addition & 1 deletion examples/apps/screenpipe-app-tauri/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "screenpipe-app"
version = "0.1.64"
version = "0.1.65"
description = ""
authors = ["you"]
license = ""
Expand Down
40 changes: 27 additions & 13 deletions examples/apps/screenpipe-app-tauri/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,14 @@ fn spawn_sidecar(app: &tauri::AppHandle) -> Result<CommandChild, String> {
.unwrap_or(false))
})
.map_err(|e| e.to_string())?;
let restart_interval = with_store(app.clone(), stores.clone(), path.clone(), |store| {
Ok(store
.get("restartInterval")
.and_then(|v| v.as_u64())
.unwrap_or(0))
})
.map_err(|e| e.to_string())?;


let _data_dir_str = base_dir.to_string_lossy();
let mut args = vec!["--port", "3030"];
Expand Down Expand Up @@ -196,6 +204,12 @@ fn spawn_sidecar(app: &tauri::AppHandle) -> Result<CommandChild, String> {
args.push("--use-pii-removal");
}

let restart_interval_str = restart_interval.to_string();
if restart_interval > 0 {
args.push("--restart-interval");
args.push(&restart_interval_str);
}

// hardcode TESSDATA_PREFIX for windows
if cfg!(windows) {
let exe_dir = env::current_exe()
Expand Down Expand Up @@ -227,19 +241,19 @@ fn spawn_sidecar(app: &tauri::AppHandle) -> Result<CommandChild, String> {
let (mut rx, child) = result.unwrap();

// only in production mode because it breaks the "bun tauri dev"
#[cfg(not(debug_assertions))]
tauri::async_runtime::spawn(async move {
#[allow(unused_variables)]
let mut i = 0;
while let Some(event) = rx.recv().await {
if let CommandEvent::Stdout(line) = event {
print!("{}", String::from_utf8(line).unwrap());
i += 1;
} else if let CommandEvent::Stderr(line) = event {
error!("Sidecar stderr: {}", String::from_utf8(line).unwrap());
}
}
});
// #[cfg(not(debug_assertions))]
// tauri::async_runtime::spawn(async move {
// #[allow(unused_variables)]
// let mut i = 0;
// while let Some(event) = rx.recv().await {
// if let CommandEvent::Stdout(line) = event {
// print!("{}", String::from_utf8(line).unwrap());
// i += 1;
// } else if let CommandEvent::Stderr(line) = event {
// error!("Sidecar stderr: {}", String::from_utf8(line).unwrap());
// }
// }
// });

info!("Spawned sidecar with args: {:?}", args);

Expand Down
43 changes: 38 additions & 5 deletions screenpipe-server/src/bin/screenpipe-server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use screenpipe_server::{
start_continuous_recording, DatabaseManager, ResourceMonitor, Server,
};
use screenpipe_vision::utils::OcrEngine as CoreOcrEngine;
use tokio::sync::mpsc::channel;
use tokio::{sync::mpsc::channel, time::interval};

fn print_devices(devices: &[AudioDevice]) {
println!("Available audio devices:");
Expand Down Expand Up @@ -253,12 +253,17 @@ async fn main() -> anyhow::Result<()> {
std::process::exit(1);
});
let ocr_engine_clone = cli.ocr_engine.clone();
let restart_interval = cli.restart_interval;

// Function to start or restart the recording task
let _start_recording = tokio::spawn(async move {
// hack
let mut recording_task = tokio::spawn(async move {});

let mut restart_timer = if restart_interval > 0 {
Some(interval(Duration::from_secs(restart_interval * 60))) // Changed to minutes
} else {
None
};
loop {
let db_clone = db.clone();
let local_data_dir = local_data_dir.clone();
Expand All @@ -268,14 +273,20 @@ async fn main() -> anyhow::Result<()> {

tokio::select! {
_ = &mut recording_task => {
// Recording task completed or errored, restart it
debug!("Recording task ended. Restarting...");
}
Some(_) = restart_receiver.recv() => {
// Received restart signal, cancel the current task and restart
info!("Received restart signal. Restarting recording task...");
recording_task.abort();
}
_ = async { if let Some(timer) = &mut restart_timer {
timer.tick().await
} else {
std::future::pending().await
}}, if restart_interval > 0 => {
info!("Periodic restart interval reached. Restarting recording task...");
recording_task.abort();
}
}
let core_ocr_engine: CoreOcrEngine = cli.ocr_engine.clone().into();
let ocr_engine = Arc::new(OcrEngine::from(core_ocr_engine));
Expand All @@ -291,6 +302,7 @@ async fn main() -> anyhow::Result<()> {
Duration::from_secs(cli.audio_chunk_duration),
vision_control,
audio_devices_control,
cli.disable_audio,
cli.save_text_files,
audio_transcription_engine,
ocr_engine,
Expand Down Expand Up @@ -389,6 +401,14 @@ async fn main() -> anyhow::Result<()> {
local_data_dir_clone.display()
);
println!("│ Debug Mode │ {:<34} │", cli.debug);
println!(
"│ Restart Interval │ {:<34} │",
if cli.restart_interval > 0 {
format!("Every {} minutes", cli.restart_interval)
} else {
"Disabled".to_string()
}
);
const VALUE_WIDTH: usize = 34;

// Function to truncate and pad strings
Expand All @@ -403,13 +423,19 @@ async fn main() -> anyhow::Result<()> {
// In the main function, replace the audio devices section with:
println!("├─────────────────────┼────────────────────────────────────┤");
println!("│ Audio Devices │ │");
const MAX_DEVICES_TO_DISPLAY: usize = 5;

if cli.disable_audio {
println!("│ {:<19} │ {:<34} │", "", "Disabled");
} else if audio_devices.is_empty() {
println!("│ {:<19} │ {:<34} │", "", "No devices available");
} else {
for (i, device) in audio_devices.iter().enumerate() {
let total_devices = audio_devices.len();
for (i, device) in audio_devices
.iter()
.enumerate()
.take(MAX_DEVICES_TO_DISPLAY)
{
let device_str = device.deref().to_string();
let formatted_device = format_cell(&device_str, VALUE_WIDTH);
if i == 0 {
Expand All @@ -418,6 +444,13 @@ async fn main() -> anyhow::Result<()> {
println!("│ {:<19} │ {:<34} │", "", formatted_device);
}
}
if total_devices > MAX_DEVICES_TO_DISPLAY {
println!(
"│ {:<19} │ {:<34} │",
"",
format!("... and {} more", total_devices - MAX_DEVICES_TO_DISPLAY)
);
}
}

println!("└─────────────────────┴────────────────────────────────────┘");
Expand Down
4 changes: 4 additions & 0 deletions screenpipe-server/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,8 @@ pub struct Cli {
/// Enable PII removal from OCR text property that is saved to db and returned in search results
#[arg(long, default_value_t = false)]
pub use_pii_removal: bool,

/// Restart recording process every X minutes (0 means no periodic restart)
#[arg(long, default_value_t = 0)]
pub restart_interval: u64,
}
18 changes: 14 additions & 4 deletions screenpipe-server/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
use tokio::task::JoinHandle;

pub async fn start_continuous_recording(
Expand All @@ -25,16 +25,26 @@ pub async fn start_continuous_recording(
audio_chunk_duration: Duration,
vision_control: Arc<AtomicBool>,
audio_devices_control: Arc<SegQueue<(AudioDevice, DeviceControl)>>,
audio_disabled: bool,
save_text_files: bool,
audio_transcription_engine: Arc<AudioTranscriptionEngine>,
ocr_engine: Arc<OcrEngine>,
friend_wearable_uid: Option<String>,
monitor_id: u32,
use_pii_removal: bool,
) -> Result<()> {
let (whisper_sender, whisper_receiver) =
create_whisper_channel(audio_transcription_engine.clone()).await?;

let (whisper_sender, whisper_receiver) = if audio_disabled {
// Create a dummy channel if no audio devices are available, e.g. audio disabled
let (input_sender, _): (UnboundedSender<AudioInput>, UnboundedReceiver<AudioInput>) =
unbounded_channel();
let (_, output_receiver): (
UnboundedSender<TranscriptionResult>,
UnboundedReceiver<TranscriptionResult>,
) = unbounded_channel();
(input_sender, output_receiver)
} else {
create_whisper_channel(audio_transcription_engine.clone()).await?
};
let db_manager_video = Arc::clone(&db);
let db_manager_audio = Arc::clone(&db);

Expand Down

0 comments on commit d4fadbe

Please sign in to comment.