From d983b8d8b53d18b3499d247a00fa68e9a3e6c022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Fl=C3=B3rez?= Date: Thu, 20 Aug 2026 09:02:07 -0500 Subject: [PATCH] v1.10.0: el video en primer plano no cuenta como inactividad MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regla pedida por el usuario: si alguien mira un video en el navegador sin tocar el mouse, no es que este quieto — esta viendo algo. Ahora el sensor lo cuenta como tiempo de navegador en vez de inactividad. - Excepcion del VIDEO (Windows): en el tick, si `ocioso` (3 min sin entrada) PERO al frente hay un navegador (chrome/brave/edge/firefox/opera/vivaldi, se excluye el host WebView2) Y el sistema esta sacando sonido, es un video reproduciendose -> `viendo_video`. Se introduce `ocioso_efectivo = ocioso && !viendo_video`, y TODO (tramo de inactividad, bandeja, acumulacion de la ventana) pasa a usar `ocioso_efectivo`. El bloque de navegador se acumula con a=0 (segundos sin entrada real): presente, sin teclear. La bandeja lo dice («video en primer plano»). - Deteccion de sonido: medidor de pico de la salida de audio por defecto (WASAPI, IAudioMeterInformation via el crate `windows`, solo cfg(windows)). No abre el navegador, no lee la pestaña, no graba nada — solo pregunta al SO si hay sonido saliendo AHORA (pico > 0.01). Limite honesto documentado: un video MUDO no se detecta. macOS: stub que devuelve false (no hay API publica sencilla de nivel de audio; el equipo de edicion es Windows) -> comportamiento identico al de siempre en Mac. - La ventana al frente ahora se lee cada tic mientras se mide (antes solo con actividad), porque hace falta para decidir la excepcion; se reutiliza esa misma lectura para acumular, sin volver a preguntar al SO. - COM se inicializa una vez al arrancar el hilo del sensor (cfg(windows)). - El contrato con el servidor NO cambia: se sigue mandando {blocks, idles}, solo que ahora hay menos idles y algun bloque de navegador cuando hay video. Nada que tocar en labstream-os. Verificado con `cargo check`: el reensamblado compila limpio en el host (macOS), y el codigo WASAPI type-checkea contra el target x86_64-pc-windows-msvc (aislado en un crate minimo, porque `ring` de la TLS de ureq no cross-compila su C a Windows desde Mac — eso lo hace la CI). Falta la validacion final en una maquina Windows real (no puedo reproducir audio de Windows aqui): probar que un video en YouTube con el mouse quieto sale como navegador y no como inactividad. Co-Authored-By: Claude Opus 5 --- README.md | 11 +- package.json | 2 +- src-tauri/Cargo.lock | 713 +++++++++++++++++++++++++++++++++++--- src-tauri/Cargo.toml | 20 +- src-tauri/src/tracker.rs | 101 +++++- src-tauri/tauri.conf.json | 2 +- 6 files changed, 797 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 2e7f853..f11276c 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ NAS. Esto le da al equipo un cliente nativo de verdad en Windows y Mac: resultado. Es un popup nativo del sistema, igual en Windows y macOS, y vive solo en el shell (la web app no se toca). -- **Rastreador de trabajo efectivo** (v1.9.0, `src-tauri/src/tracker.rs`): mide +- **Rastreador de trabajo efectivo** (v1.10.0, `src-tauri/src/tracker.rs`): mide cuánto tiempo se trabaja de verdad y en qué aplicación, y lo publica en el panel **/rastreo** del servidor. Cada 5 s mira la ventana al frente y si hubo entrada de ratón/teclado; **con 3 minutos sin entrada deja de contar** @@ -55,6 +55,15 @@ NAS. Esto le da al equipo un cliente nativo de verdad en Windows y Mac: su último latido en vez de perderlo). Y un tramo se cierra solo a las 4 h: más que eso sin tocar el equipo es ausencia, no descanso frente a la pantalla. En pausa no se anota nada. + - **Desde la 1.10, la excepción del VIDEO** (solo Windows): si no hay entrada + PERO al frente hay un navegador (Chrome/Brave/Edge/Firefox…) y está saliendo + sonido —un video reproduciéndose—, ese rato NO se anota como inactividad: se + cuenta como tiempo de navegador (con 0 segundos «activos», porque no se + teclea). Se detecta con el medidor de audio del sistema (WASAPI, + `IAudioMeterInformation`): no abre el navegador, no lee la pestaña, no graba + nada — solo pregunta si hay sonido saliendo. Límite honesto: un video **mudo** + no se detecta (no suena). En macOS no aplica (no hay API pública sencilla de + nivel de audio); el equipo de edición es Windows. - **Qué NO registra, a propósito:** ni qué teclas se pulsan (solo compara una huella de posición del ratón y cuántas teclas hay presionadas), ni pantallazos, ni el contenido de nada, ni los procesos de fondo. diff --git a/package.json b/package.json index 0b2e388..341ce70 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "labstream-desktop", - "version": "1.9.0", + "version": "1.10.0", "private": true, "description": "App de escritorio (Windows) que envuelve Labstream OS — cliente del servidor del NAS.", "scripts": { diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 0302134..37e60e7 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2,6 +2,22 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "active-win-pos-rs" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e2d4aadc5e08b1ae3e26c39454c18d4d3454af88edd88d3561a79e1d6062d4" +dependencies = [ + "appkit-nsworkspace-bindings", + "core-foundation 0.9.4", + "core-graphics 0.23.2", + "hyprland", + "kdotool", + "objc", + "windows 0.48.0", + "xcb", +] + [[package]] name = "adler2" version = "2.0.1" @@ -47,6 +63,16 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "appkit-nsworkspace-bindings" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "062382938604cfa02c03689ab75af0e7eb79175ba0d0b2bcfad18f5190702dd7" +dependencies = [ + "bindgen", + "objc", +] + [[package]] name = "arbitrary" version = "1.4.2" @@ -128,7 +154,7 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix", + "rustix 1.1.4", "slab", "windows-sys 0.61.2", ] @@ -159,7 +185,7 @@ dependencies = [ "cfg-if", "event-listener", "futures-lite", - "rustix", + "rustix 1.1.4", ] [[package]] @@ -185,12 +211,34 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix", + "rustix 1.1.4", "signal-hook-registry", "slab", "windows-sys 0.61.2", ] +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "async-task" version = "4.7.1" @@ -266,6 +314,29 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bindgen" +version = "0.68.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" +dependencies = [ + "bitflags 2.13.0", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex 1.3.0", + "syn 2.0.118", + "which", +] + [[package]] name = "bit-set" version = "0.8.0" @@ -464,7 +535,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" dependencies = [ "find-msvc-tools", - "shlex", + "shlex 2.0.1", ] [[package]] @@ -473,6 +544,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom 7.1.3", +] + [[package]] name = "cfb" version = "0.7.3" @@ -507,11 +587,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-link 0.2.1", ] +[[package]] +name = "clang-sys" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a" +dependencies = [ + "glob", + "libc", + "libloading 0.8.9", +] + [[package]] name = "clipboard-win" version = "5.4.1" @@ -540,6 +633,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cookie" version = "0.18.1" @@ -550,6 +652,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation" version = "0.10.1" @@ -566,6 +678,19 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", + "foreign-types", + "libc", +] + [[package]] name = "core-graphics" version = "0.25.0" @@ -573,12 +698,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" dependencies = [ "bitflags 2.13.0", - "core-foundation", - "core-graphics-types", + "core-foundation 0.10.1", + "core-graphics-types 0.2.0", "foreign-types", "libc", ] +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "libc", +] + [[package]] name = "core-graphics-types" version = "0.2.0" @@ -586,7 +722,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ "bitflags 2.13.0", - "core-foundation", + "core-foundation 0.10.1", "libc", ] @@ -678,14 +814,38 @@ version = "0.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + [[package]] name = "darling" version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.23.0", + "darling_macro 0.23.0", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", ] [[package]] @@ -701,13 +861,24 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.118", +] + [[package]] name = "darling_macro" version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ - "darling_core", + "darling_core 0.23.0", "quote", "syn 2.0.118", ] @@ -743,6 +914,37 @@ dependencies = [ "syn 2.0.118", ] +[[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 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[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.118", +] + [[package]] name = "derive_more" version = "2.1.1" @@ -758,10 +960,26 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ + "convert_case", "proc-macro2", "quote", "rustc_version", "syn 2.0.118", + "unicode-xid", +] + +[[package]] +name = "device_query" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7331225604b9b097b41872d550134933d4be83465d70a2c4a132b929f99aaca" +dependencies = [ + "macos-accessibility-client", + "pkg-config", + "readkey", + "readmouse", + "windows 0.48.0", + "x11", ] [[package]] @@ -933,6 +1151,12 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" +[[package]] +name = "either" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" + [[package]] name = "embed-resource" version = "3.0.9" @@ -1344,13 +1568,23 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc3655aa6818d65bc620d6911f05aa7b6aeb596291e1e9f79e52df85583d1e30" +dependencies = [ + "rustix 0.38.44", + "windows-targets 0.52.6", +] + [[package]] name = "gethostname" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ - "rustix", + "rustix 1.1.4", "windows-link 0.2.1", ] @@ -1547,6 +1781,22 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "handlebars" +version = "6.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75c54236f9045c8004a77942bebc52145b4844639db934a5c70fe08617fbe61a" +dependencies = [ + "derive_builder", + "log", + "num-order", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror 2.0.18", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1592,6 +1842,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "html5ever" version = "0.38.0" @@ -1699,6 +1958,34 @@ dependencies = [ "tracing", ] +[[package]] +name = "hyprland" +version = "0.4.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62fc24052f578592af91e5c60da1893ba6aea266b6ab86ffb72a644cf213fea9" +dependencies = [ + "async-stream", + "derive_more", + "either", + "futures-lite", + "hyprland-macros", + "pastey", + "serde", + "serde_json", + "serde_repr", +] + +[[package]] +name = "hyprland-macros" +version = "0.4.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31157e6ccefbad4b0cd7e549db6696691a70c11b108f26bf6bf76eef26af8c10" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "iana-time-zone" version = "0.1.65" @@ -2049,6 +2336,23 @@ dependencies = [ "serde_json", ] +[[package]] +name = "kdotool" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2eee83d474f719244c71d8ebbe360b5634ed76e4da6bdfae9524cf9de6b9e4c" +dependencies = [ + "anyhow", + "chrono", + "dbus", + "handlebars", + "log", + "phf", + "serde", + "serde_json", + "tempfile", +] + [[package]] name = "keyboard-types" version = "0.7.0" @@ -2062,8 +2366,11 @@ dependencies = [ [[package]] name = "labstream-desktop" -version = "1.7.1" +version = "1.10.0" dependencies = [ + "active-win-pos-rs", + "device_query", + "gethostname 0.5.0", "serde", "serde_json", "tauri", @@ -2074,8 +2381,22 @@ dependencies = [ "tauri-plugin-opener", "tauri-plugin-single-instance", "tauri-plugin-updater", + "ureq", + "windows 0.61.3", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libappindicator" version = "0.9.0" @@ -2096,7 +2417,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" dependencies = [ "gtk-sys", - "libloading", + "libloading 0.7.4", "once_cell", ] @@ -2125,6 +2446,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link 0.2.1", +] + [[package]] name = "libredox" version = "0.1.17" @@ -2134,6 +2465,12 @@ dependencies = [ "libc", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -2175,6 +2512,25 @@ dependencies = [ "uuid", ] +[[package]] +name = "macos-accessibility-client" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edf7710fbff50c24124331760978fb9086d6de6288dcdb38b25a97f8b1bdebbb" +dependencies = [ + "core-foundation 0.9.4", + "core-foundation-sys", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + [[package]] name = "markup5ever" version = "0.38.0" @@ -2207,6 +2563,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "minisign-verify" version = "0.2.5" @@ -2295,6 +2657,16 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nom" version = "8.0.0" @@ -2324,6 +2696,21 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" +[[package]] +name = "num-modular" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd8e500409e6cd603b03e477c26a6caecdc27ac58979a53e881c75eafc079f44" + +[[package]] +name = "num-order" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537b596b97c40fcf8056d153049eb22f481c17ebce72a513ec9286e4986d1bb6" +dependencies = [ + "num-modular", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -2355,6 +2742,15 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + [[package]] name = "objc2" version = "0.6.4" @@ -2682,18 +3078,72 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" + [[package]] name = "pathdiff" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "percent-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "pest" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a07a60cc7a4d00c91f95c685609d1d2f79050e6804b70ebedd7650f0b839bcf" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a83744a5c8455b8b3e0dc5031362780a347c878bdd11584d1a8984228cc88d" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0cd3451aa3de60d4b9a1e736885e4dea6b31617598026f12256ad566d63304a" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "pest_meta" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e04d3a0849e241d7dfce834c83b1c5edc8622009e8dd51a12ba1927c32f05496" +dependencies = [ + "pest", +] + [[package]] name = "petgraph" version = "0.8.3" @@ -2830,7 +3280,7 @@ dependencies = [ "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix", + "rustix 1.1.4", "windows-sys 0.61.2", ] @@ -2864,6 +3314,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.118", +] + [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -3012,6 +3472,18 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" +[[package]] +name = "readkey" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a36870cefdfcff57edbc0fa62165f42dfd4e5a0d8965117c1ea84c5700e4450" + +[[package]] +name = "readmouse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be105c72a1e6a5a1198acee3d5b506a15676b74a02ecd78060042a447f408d94" + [[package]] name = "redox_syscall" version = "0.5.18" @@ -3145,6 +3617,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc-hash" version = "2.1.2" @@ -3160,6 +3638,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.13.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + [[package]] name = "rustix" version = "1.1.4" @@ -3169,7 +3660,7 @@ dependencies = [ "bitflags 2.13.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.12.1", "windows-sys 0.61.2", ] @@ -3179,6 +3670,7 @@ version = "0.23.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" dependencies = [ + "log", "once_cell", "ring", "rustls-pki-types", @@ -3214,7 +3706,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" dependencies = [ - "core-foundation", + "core-foundation 0.10.1", "core-foundation-sys", "jni 0.22.4", "log", @@ -3334,7 +3826,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ "bitflags 2.13.0", - "core-foundation", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -3364,7 +3856,7 @@ dependencies = [ "phf", "phf_codegen", "precomputed-hash", - "rustc-hash", + "rustc-hash 2.1.2", "servo_arc", "smallvec", ] @@ -3500,7 +3992,7 @@ version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" dependencies = [ - "darling", + "darling 0.23.0", "proc-macro2", "quote", "syn 2.0.118", @@ -3548,6 +4040,12 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "shlex" version = "2.0.1" @@ -3777,8 +4275,8 @@ checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9" dependencies = [ "bitflags 2.13.0", "block2", - "core-foundation", - "core-graphics", + "core-foundation 0.10.1", + "core-graphics 0.25.0", "crossbeam-channel", "dbus", "dispatch2", @@ -3803,7 +4301,7 @@ dependencies = [ "tao-macros", "unicode-segmentation", "url", - "windows", + "windows 0.61.3", "windows-core 0.61.2", "windows-version", "x11-dl", @@ -3885,7 +4383,7 @@ dependencies = [ "webkit2gtk", "webview2-com", "window-vibrancy", - "windows", + "windows 0.61.3", ] [[package]] @@ -4032,7 +4530,7 @@ dependencies = [ "tauri-plugin", "thiserror 2.0.18", "url", - "windows", + "windows 0.61.3", "zbus", ] @@ -4107,7 +4605,7 @@ dependencies = [ "url", "webkit2gtk", "webview2-com", - "windows", + "windows 0.61.3", ] [[package]] @@ -4132,7 +4630,7 @@ dependencies = [ "url", "webkit2gtk", "webview2-com", - "windows", + "windows 0.61.3", "wry", ] @@ -4192,7 +4690,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed071c670382e85fc2f48ae706492d8c338f4f89bf72520d32f8abfe880aade" dependencies = [ "thiserror 2.0.18", - "windows", + "windows 0.61.3", "windows-version", ] @@ -4205,7 +4703,7 @@ dependencies = [ "fastrand", "getrandom 0.4.3", "once_cell", - "rustix", + "rustix 1.1.4", "windows-sys 0.61.2", ] @@ -4590,7 +5088,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6" dependencies = [ "memchr", - "nom", + "nom 8.0.0", "petgraph", ] @@ -4612,6 +5110,12 @@ version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "uds_windows" version = "1.2.1" @@ -4676,12 +5180,36 @@ version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "untrusted" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "ureq" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" +dependencies = [ + "base64 0.22.1", + "flate2", + "log", + "once_cell", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "url", + "webpki-roots 0.26.11", +] + [[package]] name = "url" version = "2.5.8" @@ -4873,7 +5401,7 @@ checksum = "016ccf01d1c58b6f8999612813e17c9b2390f7d70671428869913310f83f54b8" dependencies = [ "cc", "downcast-rs", - "rustix", + "rustix 1.1.4", "smallvec", "wayland-sys", ] @@ -4885,7 +5413,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c36a0f861ad76d0901f2800b46321410d9f73f2ea88aac0650d86c32688073" dependencies = [ "bitflags 2.13.0", - "rustix", + "rustix 1.1.4", "wayland-backend", "wayland-scanner", ] @@ -5010,6 +5538,24 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.9", +] + +[[package]] +name = "webpki-roots" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webview2-com" version = "0.38.2" @@ -5018,7 +5564,7 @@ checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows", + "windows 0.61.3", "windows-core 0.61.2", "windows-implement", "windows-interface", @@ -5042,7 +5588,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" dependencies = [ "thiserror 2.0.18", - "windows", + "windows 0.61.3", "windows-core 0.61.2", ] @@ -5052,6 +5598,18 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + [[package]] name = "winapi" version = "0.3.9" @@ -5098,6 +5656,15 @@ dependencies = [ "windows-version", ] +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows" version = "0.61.3" @@ -5297,6 +5864,21 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -5354,6 +5936,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -5372,6 +5960,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -5390,6 +5984,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -5420,6 +6020,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -5438,6 +6044,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -5456,6 +6068,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -5474,6 +6092,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -5544,7 +6168,7 @@ dependencies = [ "libc", "log", "os_pipe", - "rustix", + "rustix 1.1.4", "thiserror 2.0.18", "tree_magic_mini", "wayland-backend", @@ -5597,7 +6221,7 @@ dependencies = [ "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows", + "windows 0.61.3", "windows-core 0.61.2", "windows-version", "x11-dl", @@ -5630,8 +6254,8 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" dependencies = [ - "gethostname", - "rustix", + "gethostname 1.1.0", + "rustix 1.1.4", "x11rb-protocol", ] @@ -5648,7 +6272,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" dependencies = [ "libc", - "rustix", + "rustix 1.1.4", +] + +[[package]] +name = "xcb" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6c2ad15e0e922856ee89afe862b8992334bbe7953adad56cd1199358cb30566" +dependencies = [ + "bitflags 2.13.0", + "libc", + "quick-xml 0.41.0", ] [[package]] @@ -5696,7 +6331,7 @@ dependencies = [ "hex", "libc", "ordered-stream", - "rustix", + "rustix 1.1.4", "serde", "serde_repr", "tracing", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 34fc86a..ec0183a 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "labstream-desktop" -version = "1.9.0" +version = "1.10.0" description = "Cliente de escritorio de Labstream OS" authors = ["Labstream SAS"] edition = "2021" @@ -37,3 +37,21 @@ active-win-pos-rs = "0.11" device_query = "4" ureq = { version = "2", features = ["json"] } gethostname = "0.5" + +# ── Excepción del video (Windows) ── +# Para saber si «hay un video reproduciéndose» sin espiar el navegador: se mira el MEDIDOR de +# la salida de audio por defecto (WASAPI, IAudioMeterInformation). Si al frente hay un navegador +# y está saliendo sonido, es un video sonando → cuenta como navegador, no como inactividad. +# Solo Windows: en macOS no hay API pública sencilla de nivel de audio por app (el equipo de +# edición es Windows). El `windows` crate ya está en la caché por Tauri. +[target.'cfg(windows)'.dependencies] +windows = { version = "0.61", features = [ + "Win32_Foundation", + "Win32_System_Com", + # IMMDevice::Activate exige AMBAS (su firma lleva un PROPVARIANT): sin ellas el método + # queda cfg'd out y «no existe». Verificado con cargo check --target windows-msvc. + "Win32_System_Com_StructuredStorage", + "Win32_System_Variant", + "Win32_Media_Audio", + "Win32_Media_Audio_Endpoints", +] } diff --git a/src-tauri/src/tracker.rs b/src-tauri/src/tracker.rs index d621019..1e8bb8f 100644 --- a/src-tauri/src/tracker.rs +++ b/src-tauri/src/tracker.rs @@ -244,6 +244,52 @@ fn normaliza(s: &str, max: usize) -> String { limpio.chars().take(max).collect() } +// ── Excepción del video ── +// ¿La ventana al frente es un NAVEGADOR? El servidor ya clasifica el sitio por el título; aquí +// solo hace falta saber que es un navegador para aplicar la excepción del video. Se excluye el +// host WebView2 (el motor de Edge embebido que usa ESTA misma app y otras de escritorio): no es +// navegación de nadie, igual que en el servidor. +fn es_navegador(app: &str) -> bool { + let a = app.to_lowercase(); + if a.contains("webview") { + return false; + } + ["chrome", "brave", "edge", "firefox", "opera", "vivaldi"].iter().any(|n| a.contains(n)) +} + +// ¿Está saliendo SONIDO por la salida de audio por defecto? Es el proxy de «hay un video +// reproduciéndose»: se lee el medidor de pico de WASAPI (IAudioMeterInformation) del dispositivo +// de salida. No abre el navegador, no lee la pestaña, no graba nada — solo pregunta al sistema +// si hay sonido saliendo AHORA. Un video mudo no se detecta (no suena); es el límite honesto de +// medirlo desde fuera del navegador. +#[cfg(windows)] +fn hay_audio_sonando() -> bool { + use windows::Win32::Media::Audio::Endpoints::IAudioMeterInformation; + use windows::Win32::Media::Audio::{eMultimedia, eRender, IMMDeviceEnumerator, MMDeviceEnumerator}; + use windows::Win32::System::Com::{CoCreateInstance, CLSCTX_ALL}; + unsafe { + let Ok(enumerador) = CoCreateInstance::<_, IMMDeviceEnumerator>(&MMDeviceEnumerator, None, CLSCTX_ALL) else { + return false; + }; + // eMultimedia = el endpoint que usan música y video (no el de comunicaciones). + let Ok(dispositivo) = enumerador.GetDefaultAudioEndpoint(eRender, eMultimedia) else { + return false; // sin dispositivo de salida (p. ej. sin tarjeta/monitor de audio) + }; + let Ok(medidor) = dispositivo.Activate::(CLSCTX_ALL, None) else { + return false; + }; + // Umbral pequeño para no dispararse con el ruido de fondo del silencio digital. + matches!(medidor.GetPeakValue(), Ok(pico) if pico > 0.01) + } +} + +#[cfg(not(windows))] +fn hay_audio_sonando() -> bool { + // macOS: no hay API pública sencilla de nivel de audio; la excepción del video no aplica aquí + // (el equipo de edición es Windows). Follow-up posible con CoreAudio. false = como siempre. + false +} + // ── Envío ── // Sube la cola entera; deja en la cola lo que no pudo. Un 401 = token revocado → se descarta // el token (la bandeja pasa a «sin vincular») y la cola se vacía: ya nadie la quiere. @@ -372,6 +418,15 @@ pub fn iniciar(app: &AppHandle) { // El sensor: un hilo con tic de 5 s. Nada de esto toca la interfaz salvo el menú del tray. let h = app.clone(); thread::spawn(move || { + // COM para este hilo: lo necesita la lectura del medidor de audio (WASAPI) de la + // excepción del video. Se hace UNA vez al arrancar el hilo; si ya estaba inicializado, + // devuelve S_FALSE y no pasa nada. En macOS no existe y se omite. + #[cfg(windows)] + unsafe { + use windows::Win32::System::Com::{CoInitializeEx, COINIT_MULTITHREADED}; + let _ = CoInitializeEx(None, COINIT_MULTITHREADED); + } + let device_state = device_query::DeviceState::new(); let mut ultimo_input = ahora_seg(); let mut huella_anterior: (i32, i32, usize) = (0, 0, 0); @@ -462,18 +517,40 @@ pub fn iniciar(app: &AppHandle) { } let ocioso = ahora.saturating_sub(ultimo_input) >= OCIO_PROFUNDO_SEG; + // ── Ventana al frente + excepción del VIDEO ── + // La ventana se lee cada tic mientras se mide (antes solo cuando había actividad): + // ahora hace falta AUNQUE se esté ocioso, para saber si al frente hay un video. + let ventana = if token_ok && !pausado { + active_win_pos_rs::get_active_window() + .ok() + .map(|v| (normaliza(&v.app_name, 80), normaliza(&v.title, 140))) + } else { + None + }; + // Sin entrada PERO con un navegador al frente y sonido saliendo = un video + // reproduciéndose. El usuario lo pidió: eso NO es inactividad, cuenta como tiempo de + // navegador (abajo se acumula el bloque con a=0, porque no hubo entrada). Solo se + // consulta el audio en ese caso puntual (ocioso + navegador al frente), no en cada tic. + let viendo_video = ocioso + && ventana.as_ref().map(|(app, _)| es_navegador(app)).unwrap_or(false) + && hay_audio_sonando(); + // La inactividad EFECTIVA: quieto de verdad, salvo que sea un video en primer plano. + // Todo lo de abajo (tramo de ocio, bandeja, acumulación) usa esta, no `ocioso`. + let ocioso_efectivo = ocioso && !viendo_video; + // ── El tramo de inactividad ── // Abre cuando se cruza el umbral (retrocedido al minuto exacto en que se dejó de - // contar trabajo: ultimo_input + umbral) y cierra cuando vuelve la entrada. En + // contar trabajo: ultimo_input + umbral) y cierra cuando vuelve la entrada —o cuando + // arranca un video en primer plano, que cuenta como actividad de navegador. En // pausa o sin vincular no se anota nada: pausar es «no me midas», y eso incluye // la inactividad. if token_ok && !pausado { - if ocioso && !ocioso_antes { + if ocioso_efectivo && !ocioso_antes { let ini = (ultimo_input + OCIO_PROFUNDO_SEG) * 1000; ocio_abierto = Some(ini); guardar_encurso(&h, ahora, ini); encurso_guardado = ahora; - } else if !ocioso && ocioso_antes { + } else if !ocioso_efectivo && ocioso_antes { if let Some(ini) = ocio_abierto.take() { let fin = ahora * 1000; if fin > ini { @@ -502,7 +579,7 @@ pub fn iniciar(app: &AppHandle) { ultimo_input = ahora; } if token_ok && !pausado { - ocioso_antes = ocioso; + ocioso_antes = ocioso_efectivo; } // El tope del tramo: a las 4 h se cierra y NO se reabre (ocioso_antes queda en @@ -525,7 +602,10 @@ pub fn iniciar(app: &AppHandle) { "sin vincular" } else if pausado { "en pausa" - } else if ocioso { + } else if viendo_video { + // Ocioso, pero con un video en primer plano: se cuenta como navegador. + "video en primer plano" + } else if ocioso_efectivo { // Desde la 1.9 el tramo quieto SÍ queda anotado (aparte de las horas // efectivas); la bandeja lo dice para que la medición siga siendo transparente. "inactivo (se anota aparte)" @@ -542,10 +622,13 @@ pub fn iniciar(app: &AppHandle) { ultimo_estado = estado.to_string(); } - if token_ok && !pausado && !ocioso { - if let Ok(v) = active_win_pos_rs::get_active_window() { - let app_name = normaliza(&v.app_name, 80); - let titulo = normaliza(&v.title, 140); + // Se acumula la ventana al frente cuando NO hay inactividad efectiva: o sea, con + // actividad real, O con un video en primer plano (viendo_video → ocioso_efectivo es + // false). En el caso del video, `hubo_input` es false, así que el bloque suma + // segundos de navegador pero 0 activos — honesto: presente, sin teclear. Se usa la + // `ventana` ya leída arriba, sin volver a preguntar al sistema. + if token_ok && !pausado && !ocioso_efectivo { + if let Some((app_name, titulo)) = ventana.clone() { if !app_name.is_empty() { let b = acumulado.entry((app_name.clone(), titulo.clone())).or_insert_with(|| Bloque { s: ahora_ms(), diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 2f86f38..5ded770 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "Labstream OS", - "version": "1.9.0", + "version": "1.10.0", "identifier": "co.labstream.os", "build": { "frontendDist": "../dist"