diff --git a/Cargo.lock b/Cargo.lock index 0bded2e4f8..0a5d667777 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -622,9 +622,9 @@ dependencies = [ "bytes", "futures-util", "http 1.3.1", - "http-body", + "http-body 1.0.1", "http-body-util", - "hyper", + "hyper 1.7.0", "hyper-util", "itoa", "matchit", @@ -638,7 +638,7 @@ dependencies = [ "serde_path_to_error", "serde_urlencoded", "sha1", - "sync_wrapper", + "sync_wrapper 1.0.2", "tokio", "tokio-tungstenite", "tower", @@ -657,12 +657,12 @@ dependencies = [ "bytes", "futures-util", "http 1.3.1", - "http-body", + "http-body 1.0.1", "http-body-util", "mime", "pin-project-lite", "rustversion", - "sync_wrapper", + "sync_wrapper 1.0.2", "tower-layer", "tower-service", "tracing", @@ -1046,7 +1046,7 @@ dependencies = [ name = "cap-api" version = "0.0.0" dependencies = [ - "reqwest", + "reqwest 0.12.23", ] [[package]] @@ -1217,9 +1217,10 @@ dependencies = [ "opentelemetry-otlp", "opentelemetry_sdk", "png 0.17.16", + "posthog-rs", "rand 0.8.5", "relative-path", - "reqwest", + "reqwest 0.12.23", "rodio", "scap", "scap-direct3d", @@ -2515,6 +2516,37 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.106", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.106", +] + [[package]] name = "derive_more" version = "0.99.20" @@ -2792,7 +2824,7 @@ dependencies = [ "rustc_version", "toml 0.9.7", "vswhom", - "winreg", + "winreg 0.55.0", ] [[package]] @@ -3839,6 +3871,25 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17e2ac29387b1aa07a1e448f7bb4f35b500787971e965b02842b900afa5c8f6f" +[[package]] +name = "h2" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap 2.11.4", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "h2" version = "0.4.12" @@ -3986,6 +4037,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.1" @@ -4005,7 +4067,7 @@ dependencies = [ "bytes", "futures-core", "http 1.3.1", - "http-body", + "http-body 1.0.1", "pin-project-lite", ] @@ -4027,6 +4089,30 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.10", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.7.0" @@ -4037,9 +4123,9 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2", + "h2 0.4.12", "http 1.3.1", - "http-body", + "http-body 1.0.1", "httparse", "httpdate", "itoa", @@ -4050,6 +4136,20 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.32", + "rustls 0.21.12", + "tokio", + "tokio-rustls 0.24.1", +] + [[package]] name = "hyper-rustls" version = "0.27.7" @@ -4057,14 +4157,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ "http 1.3.1", - "hyper", + "hyper 1.7.0", "hyper-util", - "rustls", + "rustls 0.23.31", "rustls-pki-types", "tokio", - "tokio-rustls", + "tokio-rustls 0.26.3", "tower-service", - "webpki-roots", + "webpki-roots 1.0.2", ] [[package]] @@ -4075,7 +4175,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper", + "hyper 1.7.0", "hyper-util", "native-tls", "tokio", @@ -4095,14 +4195,14 @@ dependencies = [ "futures-core", "futures-util", "http 1.3.1", - "http-body", - "hyper", + "http-body 1.0.1", + "hyper 1.7.0", "ipnet", "libc", "percent-encoding", "pin-project-lite", "socket2 0.6.0", - "system-configuration", + "system-configuration 0.6.1", "tokio", "tower-service", "tracing", @@ -6100,7 +6200,7 @@ dependencies = [ "bytes", "http 1.3.1", "opentelemetry", - "reqwest", + "reqwest 0.12.23", ] [[package]] @@ -6115,7 +6215,7 @@ dependencies = [ "opentelemetry-proto", "opentelemetry_sdk", "prost", - "reqwest", + "reqwest 0.12.23", "thiserror 2.0.16", "tracing", ] @@ -6629,6 +6729,20 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "posthog-rs" +version = "0.3.7" +source = "git+https://github.com/CapSoftware/posthog-rs?rev=c7e9712be2f9a9122b1df685d5a067afa5415288#c7e9712be2f9a9122b1df685d5a067afa5415288" +dependencies = [ + "chrono", + "derive_builder", + "reqwest 0.11.27", + "semver", + "serde", + "serde_json", + "uuid", +] + [[package]] name = "potential_utf" version = "0.1.3" @@ -6875,7 +6989,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash 2.1.1", - "rustls", + "rustls 0.23.31", "socket2 0.6.0", "thiserror 2.0.16", "tokio", @@ -6895,7 +7009,7 @@ dependencies = [ "rand 0.9.2", "ring", "rustc-hash 2.1.1", - "rustls", + "rustls 0.23.31", "rustls-pki-types", "slab", "thiserror 2.0.16", @@ -7288,6 +7402,47 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51743d3e274e2b18df81c4dc6caf8a5b8e15dbe799e0dca05c7617380094e884" +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper-rustls 0.24.2", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls 0.21.12", + "rustls-pemfile 1.0.4", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 0.1.2", + "system-configuration 0.5.1", + "tokio", + "tokio-rustls 0.24.1", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots 0.25.4", + "winreg 0.50.0", +] + [[package]] name = "reqwest" version = "0.12.23" @@ -7302,12 +7457,12 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.4.12", "http 1.3.1", - "http-body", + "http-body 1.0.1", "http-body-util", - "hyper", - "hyper-rustls", + "hyper 1.7.0", + "hyper-rustls 0.27.7", "hyper-tls", "hyper-util", "js-sys", @@ -7318,15 +7473,15 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls", + "rustls 0.23.31", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 1.0.2", "tokio", "tokio-native-tls", - "tokio-rustls", + "tokio-rustls 0.26.3", "tokio-util", "tower", "tower-http", @@ -7336,7 +7491,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots", + "webpki-roots 1.0.2", ] [[package]] @@ -7508,6 +7663,18 @@ dependencies = [ "windows-sys 0.61.0", ] +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.7", + "sct", +] + [[package]] name = "rustls" version = "0.23.31" @@ -7517,11 +7684,20 @@ dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki", + "rustls-webpki 0.103.6", "subtle", "zeroize", ] +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + [[package]] name = "rustls-pemfile" version = "2.2.0" @@ -7541,6 +7717,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "rustls-webpki" version = "0.103.6" @@ -7803,6 +7989,16 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "security-framework" version = "2.11.1" @@ -7877,7 +8073,7 @@ checksum = "989425268ab5c011e06400187eed6c298272f8ef913e49fcadc3fda788b45030" dependencies = [ "httpdate", "native-tls", - "reqwest", + "reqwest 0.12.23", "sentry-actix", "sentry-anyhow", "sentry-backtrace", @@ -8738,6 +8934,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "sync_wrapper" version = "1.0.2" @@ -8782,6 +8984,17 @@ dependencies = [ "windows 0.52.0", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "system-configuration-sys 0.5.0", +] + [[package]] name = "system-configuration" version = "0.6.1" @@ -8790,7 +9003,17 @@ checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ "bitflags 2.9.4", "core-foundation 0.9.4", - "system-configuration-sys", + "system-configuration-sys 0.6.0", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", ] [[package]] @@ -8916,7 +9139,7 @@ dependencies = [ "percent-encoding", "plist", "raw-window-handle", - "reqwest", + "reqwest 0.12.23", "serde", "serde_json", "serde_repr", @@ -9137,7 +9360,7 @@ dependencies = [ "data-url", "http 1.3.1", "regex", - "reqwest", + "reqwest 0.12.23", "schemars 0.8.22", "serde", "serde_json", @@ -9333,7 +9556,7 @@ dependencies = [ "minisign-verify", "osakit", "percent-encoding", - "reqwest", + "reqwest 0.12.23", "semver", "serde", "serde_json", @@ -9743,13 +9966,23 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.12", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f63835928ca123f1bef57abbcd23bb2ba0ac9ae1235f1e65bda0d06e7786bd" dependencies = [ - "rustls", + "rustls 0.23.31", "tokio", ] @@ -9896,11 +10129,11 @@ dependencies = [ "base64 0.22.1", "bytes", "http 1.3.1", - "http-body", + "http-body 1.0.1", "http-body-util", "percent-encoding", "pin-project", - "sync_wrapper", + "sync_wrapper 1.0.2", "tokio-stream", "tower-layer", "tower-service", @@ -9927,7 +10160,7 @@ dependencies = [ "futures-core", "futures-util", "pin-project-lite", - "sync_wrapper", + "sync_wrapper 1.0.2", "tokio", "tower-layer", "tower-service", @@ -9944,7 +10177,7 @@ dependencies = [ "bytes", "futures-util", "http 1.3.1", - "http-body", + "http-body 1.0.1", "iri-string", "pin-project-lite", "tower", @@ -10337,7 +10570,7 @@ dependencies = [ "log", "native-tls", "percent-encoding", - "rustls-pemfile", + "rustls-pemfile 2.2.0", "rustls-pki-types", "ureq-proto", "utf-8", @@ -10772,6 +11005,12 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + [[package]] name = "webpki-roots" version = "1.0.2" @@ -11782,6 +12021,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "winreg" version = "0.55.0" diff --git a/Cargo.toml b/Cargo.toml index dc023746e9..852bbe2629 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,3 +82,6 @@ debug = true cidre = { git = "https://github.com/CapSoftware/cidre", rev = "bf84b67079a8" } # https://github.com/gfx-rs/wgpu/pull/7550 # wgpu = { git = "https://github.com/gfx-rs/wgpu", rev = "cd41a6e32a6239b65d1cecbeccde6a43a100914a" } + +# https://github.com/CapSoftware/posthog-rs/commit/c7e9712be2f9a9122b1df685d5a067afa5415288 +posthog-rs = { git = "https://github.com/CapSoftware/posthog-rs", rev = "c7e9712be2f9a9122b1df685d5a067afa5415288" } diff --git a/apps/desktop/src-tauri/Cargo.toml b/apps/desktop/src-tauri/Cargo.toml index 2c0d415dee..abd8fc26c3 100644 --- a/apps/desktop/src-tauri/Cargo.toml +++ b/apps/desktop/src-tauri/Cargo.toml @@ -112,6 +112,7 @@ tracing-opentelemetry = "0.32.0" opentelemetry = "0.31.0" opentelemetry-otlp = "0.31.0" #{ version = , features = ["http-proto", "reqwest-client"] } opentelemetry_sdk = { version = "0.31.0", features = ["rt-tokio", "trace"] } +posthog-rs = "0.3.7" [target.'cfg(target_os = "macos")'.dependencies] diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs index 29338b56c8..ea2b7aceff 100644 --- a/apps/desktop/src-tauri/src/lib.rs +++ b/apps/desktop/src-tauri/src/lib.rs @@ -16,6 +16,7 @@ mod hotkeys; mod notifications; mod permissions; mod platform; +mod posthog; mod presets; mod recording; mod recording_settings; @@ -66,7 +67,6 @@ use serde_json::json; use specta::Type; use std::{ collections::BTreeMap, - fmt, fs::File, future::Future, io::BufWriter, @@ -1945,6 +1945,8 @@ pub async fn run(recording_logging_handle: LoggingHandle) { }) .ok(); + posthog::init(); + let tauri_context = tauri::generate_context!(); let specta_builder = tauri_specta::Builder::new() diff --git a/apps/desktop/src-tauri/src/posthog.rs b/apps/desktop/src-tauri/src/posthog.rs new file mode 100644 index 0000000000..87dc404a0c --- /dev/null +++ b/apps/desktop/src-tauri/src/posthog.rs @@ -0,0 +1,54 @@ +use std::time::Duration; +use tracing::error; + +#[derive(Debug)] +pub enum PostHogEvent { + MultipartUploadComplete { duration: Duration }, + MultipartUploadFailed { duration: Duration, error: String }, +} + +impl From for posthog_rs::Event { + fn from(event: PostHogEvent) -> Self { + match event { + PostHogEvent::MultipartUploadComplete { duration } => { + let mut e = posthog_rs::Event::new_anon("multipart_upload_complete"); + e.insert_prop("duration", duration.as_secs()) + .map_err(|err| error!("Error adding PostHog property: {err:?}")) + .ok(); + e + } + PostHogEvent::MultipartUploadFailed { duration, error } => { + let mut e = posthog_rs::Event::new_anon("multipart_upload_failed"); + e.insert_prop("duration", duration.as_secs()) + .map_err(|err| error!("Error adding PostHog property: {err:?}")) + .ok(); + e.insert_prop("error", error) + .map_err(|err| error!("Error adding PostHog property: {err:?}")) + .ok(); + e + } + } + } +} + +pub fn init() { + if let Some(env) = option_env!("VITE_POSTHOG_KEY") { + tokio::spawn(async move { + posthog_rs::init_global(env) + .await + .map_err(|err| error!("Error initializing PostHog: {err}")) + .ok(); + }); + } +} + +pub fn async_capture_event(event: PostHogEvent) { + if option_env!("VITE_POSTHOG_KEY").is_some() { + tokio::spawn(async move { + posthog_rs::capture(event.into()) + .await + .map_err(|err| error!("Error sending event to PostHog: {err:?}")) + .ok(); + }); + } +} diff --git a/apps/desktop/src-tauri/src/upload.rs b/apps/desktop/src-tauri/src/upload.rs index 06e37f0822..d854fdb0b2 100644 --- a/apps/desktop/src-tauri/src/upload.rs +++ b/apps/desktop/src-tauri/src/upload.rs @@ -4,6 +4,7 @@ use crate::{ UploadProgress, VideoUploadInfo, api::{self, PresignedS3PutRequest, PresignedS3PutRequestMethod, S3VideoMeta, UploadedPart}, general_settings::GeneralSettingsStore, + posthog::{PostHogEvent, async_capture_event}, upload_legacy, web_api::{AuthedApiError, ManagerExt}, }; @@ -92,6 +93,7 @@ pub async fn upload_video( info!("Uploading video {video_id}..."); + let start = Instant::now(); let upload_id = api::upload_multipart_initiate(&app, &video_id).await?; let video_fut = async { @@ -148,6 +150,16 @@ pub async fn upload_video( let (video_result, thumbnail_result): (Result<_, AuthedApiError>, Result<_, AuthedApiError>) = tokio::join!(video_fut, thumbnail_fut); + async_capture_event(match &video_result { + Ok(()) => PostHogEvent::MultipartUploadComplete { + duration: start.elapsed(), + }, + Err(err) => PostHogEvent::MultipartUploadFailed { + duration: start.elapsed(), + error: err.to_string(), + }, + }); + let _ = (video_result?, thumbnail_result?); Ok(UploadedItem { @@ -351,13 +363,28 @@ impl InstantMultipartUpload { recording_dir: PathBuf, ) -> Self { Self { - handle: spawn_actor(Self::run( - app, - file_path, - pre_created_video, - realtime_upload_done, - recording_dir, - )), + handle: spawn_actor(async move { + let start = Instant::now(); + let result = Self::run( + app, + file_path, + pre_created_video, + realtime_upload_done, + recording_dir, + ) + .await; + async_capture_event(match &result { + Ok(()) => PostHogEvent::MultipartUploadComplete { + duration: start.elapsed(), + }, + Err(err) => PostHogEvent::MultipartUploadFailed { + duration: start.elapsed(), + error: err.to_string(), + }, + }); + + result.map(|_| ()) + }), } }