-
Notifications
You must be signed in to change notification settings - Fork 110
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ff0cfe1
commit 552dd6b
Showing
12 changed files
with
667 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Generated by Cargo | ||
# will have compiled files and executables | ||
/target/ | ||
/dist/ | ||
/static/ | ||
/.dioxus/ | ||
|
||
# These are backup files generated by rustfmt | ||
**/*.rs.bk |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
[package] | ||
name = "screenpipe-app" | ||
version = "0.1.0" | ||
authors = ["Louis Beaumont <[email protected]>"] | ||
edition = "2021" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
|
||
dioxus = { version = "0.5.1", features = ["desktop", "router"] } | ||
|
||
# Debug | ||
tracing = "0.1.40" | ||
dioxus-logger = "0.5.0" | ||
|
||
serde_json = "1.0" | ||
serde = { version = "1.0", features = ["derive"] } | ||
|
||
screenpipe-vision = { path = "../../../screenpipe-vision" } | ||
|
||
axum = "0.7.5" | ||
|
||
# Logging | ||
log = "0.4.17" | ||
env_logger = "0.11.3" | ||
|
||
# Tokio | ||
tokio = { version = "1.36.0", features = ["full"] } | ||
|
||
# Directory management | ||
dirs = "5.0" | ||
|
||
# HTTP client | ||
reqwest = { version = "0.12", features = ["json"] } | ||
|
||
# Uuid | ||
uuid = { version = "1.10.0", features = ["v4"] } | ||
|
||
# Sentry | ||
sentry = "0.34.0" | ||
|
||
# Chrono | ||
chrono = "0.4.3" | ||
|
||
anyhow = "1.0" | ||
|
||
# M series MacOS | ||
[target.'cfg(target_os = "macos")'.dependencies] | ||
screenpipe-server = { path = "../../../screenpipe-server", features = ["metal"] } | ||
screenpipe-audio = { path = "../../../screenpipe-audio", features = ["metal"] } | ||
|
||
# Linux | ||
[target.'cfg(target_os = "linux")'.dependencies] | ||
screenpipe-server = { path = "../../../screenpipe-server", features = [] } | ||
screenpipe-audio = { path = "../../../screenpipe-audio", features = [] } | ||
|
||
# Windows | ||
[target.'cfg(target_os = "windows")'.dependencies] | ||
screenpipe-server = { path = "../../../screenpipe-server", features = [] } | ||
screenpipe-audio = { path = "../../../screenpipe-audio", features = [] } | ||
|
||
[features] | ||
cuda = ["screenpipe-server/cuda", "screenpipe-audio/cuda"] | ||
metal = ["screenpipe-server/metal", "screenpipe-audio/metal"] | ||
bundle = [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
[application] | ||
|
||
# App (Project) Name | ||
name = "screenpipe-app" | ||
|
||
# Dioxus App Default Platform | ||
# desktop, web | ||
default_platform = "desktop" | ||
|
||
# `build` & `serve` dist path | ||
out_dir = "dist" | ||
|
||
# assets file folder | ||
asset_dir = "assets" | ||
|
||
[web.app] | ||
|
||
# HTML title tag content | ||
title = "screenpipe-app" | ||
|
||
[web.watcher] | ||
|
||
# when watcher trigger, regenerate the `index.html` | ||
reload_html = true | ||
|
||
# which files or dirs will be watcher monitoring | ||
watch_path = ["src", "assets"] | ||
|
||
# include `assets` in web platform | ||
[web.resource] | ||
|
||
# CSS style file | ||
|
||
style = ["tailwind.css"] | ||
|
||
# Javascript code file | ||
script = [] | ||
|
||
[web.resource.dev] | ||
|
||
# Javascript code file | ||
# serve: [dev-server] only | ||
script = [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# Development | ||
|
||
1. Install npm: https://docs.npmjs.com/downloading-and-installing-node-js-and-npm | ||
2. Install the tailwind css cli: https://tailwindcss.com/docs/installation | ||
3. Run the following command in the root of the project to start the tailwind CSS compiler: | ||
|
||
```bash | ||
npx tailwindcss -i ./input.css -o ./assets/tailwind.css --watch | ||
``` | ||
|
||
Run the following command in the root of the project to start the Dioxus dev server: | ||
|
||
```bash | ||
dx serve --hot-reload --platform desktop | ||
``` |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
body { | ||
background-color: #111216; | ||
} | ||
|
||
#main { | ||
margin: 0; | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: center; | ||
align-items: center; | ||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif | ||
} | ||
|
||
#links { | ||
width: 400px; | ||
text-align: left; | ||
font-size: x-large; | ||
color: white; | ||
display: flex; | ||
flex-direction: column; | ||
} | ||
|
||
#links a { | ||
color: white; | ||
text-decoration: none; | ||
margin-top: 20px; | ||
margin: 10px; | ||
border: white 1px solid; | ||
border-radius: 5px; | ||
padding: 10px; | ||
} | ||
|
||
#links a:hover { | ||
background-color: #1f1f1f; | ||
cursor: pointer; | ||
} | ||
|
||
#header { | ||
max-width: 1200px; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
@tailwind base; | ||
@tailwind components; | ||
@tailwind utilities; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
use log::{error, info}; | ||
use reqwest::Client; | ||
use serde_json::json; | ||
use std::fs; | ||
use std::sync::{Arc, Mutex}; | ||
use std::time::Duration; | ||
use tokio::time::interval; | ||
use uuid::Uuid; | ||
|
||
pub struct AnalyticsManager { | ||
client: Client, | ||
posthog_api_key: String, | ||
distinct_id: String, | ||
interval: Duration, | ||
enabled: Arc<Mutex<bool>>, | ||
api_host: String, | ||
} | ||
|
||
impl AnalyticsManager { | ||
pub fn new(posthog_api_key: String, distinct_id: String, interval_hours: u64) -> Self { | ||
Self { | ||
client: Client::new(), | ||
posthog_api_key, | ||
distinct_id, | ||
interval: Duration::from_secs(interval_hours * 3600), | ||
enabled: Arc::new(Mutex::new(true)), | ||
api_host: "https://eu.i.posthog.com".to_string(), | ||
} | ||
} | ||
|
||
pub async fn send_event( | ||
&self, | ||
event: &str, | ||
properties: Option<serde_json::Value>, | ||
) -> Result<(), Box<dyn std::error::Error>> { | ||
if !*self.enabled.lock().unwrap() { | ||
return Ok(()); | ||
} | ||
|
||
let posthog_url = format!("{}/capture/", self.api_host); | ||
|
||
let mut payload = json!({ | ||
"api_key": self.posthog_api_key, | ||
"event": event, | ||
"properties": { | ||
"distinct_id": self.distinct_id, | ||
"$lib": "rust-reqwest", | ||
"timestamp": chrono::Utc::now().to_rfc3339(), | ||
}, | ||
}); | ||
|
||
if let Some(props) = properties { | ||
if let Some(payload_props) = payload["properties"].as_object_mut() { | ||
payload_props.extend(props.as_object().unwrap_or(&serde_json::Map::new()).clone()); | ||
} | ||
} | ||
|
||
let response = self.client.post(posthog_url).json(&payload).send().await?; | ||
|
||
if !response.status().is_success() { | ||
return Err(format!("PostHog API error: {}", response.status()).into()); | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
pub async fn start_periodic_event(&self) { | ||
let mut interval = interval(self.interval); | ||
|
||
loop { | ||
interval.tick().await; | ||
if *self.enabled.lock().unwrap() { | ||
if let Err(e) = self.send_event("app_still_running", None).await { | ||
error!("Failed to send periodic PostHog event: {}", e); | ||
} | ||
} | ||
} | ||
} | ||
|
||
pub fn toggle_analytics(&self) -> bool { | ||
let mut enabled = self.enabled.lock().unwrap(); | ||
*enabled = !*enabled; | ||
*enabled | ||
} | ||
|
||
pub async fn track_search(&self) -> Result<(), Box<dyn std::error::Error>> { | ||
if !*self.enabled.lock().unwrap() { | ||
return Ok(()); | ||
} | ||
|
||
self.send_event("search_request", None).await | ||
} | ||
} | ||
|
||
pub fn get_or_create_unique_id(app_name: &str) -> Result<String, Box<dyn std::error::Error>> { | ||
let home_dir = dirs::home_dir().ok_or("Failed to get home directory")?; | ||
let app_dir = home_dir.join(format!(".{}", app_name)); | ||
let id_file = app_dir.join("unique_id"); | ||
|
||
if !app_dir.exists() { | ||
fs::create_dir_all(&app_dir)?; | ||
} | ||
|
||
if id_file.exists() { | ||
Ok(fs::read_to_string(id_file)?) | ||
} else { | ||
let new_id = Uuid::new_v4().to_string(); | ||
fs::write(id_file, &new_id)?; | ||
Ok(new_id) | ||
} | ||
} | ||
|
||
pub fn start_analytics( | ||
posthog_api_key: String, | ||
app_name: &str, | ||
interval_hours: u64, | ||
) -> Result<Arc<AnalyticsManager>, Box<dyn std::error::Error>> { | ||
let distinct_id = get_or_create_unique_id(app_name)?; | ||
let analytics_manager = Arc::new(AnalyticsManager::new( | ||
posthog_api_key, | ||
distinct_id, | ||
interval_hours, | ||
)); | ||
|
||
// Send initial event at boot | ||
tokio::spawn({ | ||
let analytics_manager = analytics_manager.clone(); | ||
async move { | ||
if let Err(e) = analytics_manager.send_event("app_started", None).await { | ||
error!("Failed to send initial PostHog event: {}", e); | ||
} | ||
info!("Analytics started"); | ||
} | ||
}); | ||
|
||
// Start periodic events | ||
tokio::spawn({ | ||
let analytics_manager = analytics_manager.clone(); | ||
async move { | ||
analytics_manager.start_periodic_event().await; | ||
} | ||
}); | ||
|
||
Ok(analytics_manager) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
use std::io::Write; | ||
pub struct MultiWriter { | ||
writers: Vec<Box<dyn Write + Send>>, | ||
} | ||
|
||
impl MultiWriter { | ||
pub fn new(writers: Vec<Box<dyn Write + Send>>) -> Self { | ||
MultiWriter { writers } | ||
} | ||
} | ||
|
||
impl Write for MultiWriter { | ||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { | ||
for writer in &mut self.writers { | ||
writer.write_all(buf)?; | ||
} | ||
Ok(buf.len()) | ||
} | ||
|
||
fn flush(&mut self) -> std::io::Result<()> { | ||
for writer in &mut self.writers { | ||
writer.flush()?; | ||
} | ||
Ok(()) | ||
} | ||
} |
Oops, something went wrong.