Make project Windows-only compatible

- Remove xcap dependency (Linux-focused)
- Add Windows crate with Win32 APIs support
- Implement native Windows window capture using GetForegroundWindow
- Implement process name retrieval using GetModuleBaseNameW
- Update all paths to use Windows backslash separators
- Update README to specify Windows-only platform
- Add Windows badge and requirements
- Update installation instructions for PowerShell
- Add error handling for non-Windows platforms

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Augustin 2025-10-16 13:48:46 +02:00
parent 9c46beb11c
commit 9eea0199bb
5 changed files with 102 additions and 53 deletions

View File

@ -3,7 +3,7 @@ name = "activity-tracker"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
authors = ["Activity Tracker Team"] authors = ["Activity Tracker Team"]
description = "Backend de suivi d'activité pour reconstruire l'historique de travail" description = "Backend de suivi d'activité pour reconstruire l'historique de travail (Windows uniquement)"
[dependencies] [dependencies]
# Core dependencies # Core dependencies
@ -17,7 +17,15 @@ env_logger = "0.11"
screenshots = "0.6" screenshots = "0.6"
image = "0.24" image = "0.24"
webp = "0.2" webp = "0.2"
xcap = "0.0.10"
# Windows APIs
[target.'cfg(windows)'.dependencies]
windows = { version = "0.52", features = [
"Win32_Foundation",
"Win32_UI_WindowsAndMessaging",
"Win32_System_Threading",
"Win32_System_ProcessStatus",
] }
# Storage (SQLite + Encryption) # Storage (SQLite + Encryption)
rusqlite = { version = "0.31", features = ["bundled"] } rusqlite = { version = "0.31", features = ["bundled"] }

View File

@ -3,12 +3,14 @@
![Version](https://img.shields.io/badge/version-0.1.0-blue) ![Version](https://img.shields.io/badge/version-0.1.0-blue)
![License](https://img.shields.io/badge/license-MIT-green) ![License](https://img.shields.io/badge/license-MIT-green)
![Rust](https://img.shields.io/badge/rust-1.70+-orange) ![Rust](https://img.shields.io/badge/rust-1.70+-orange)
![Platform](https://img.shields.io/badge/platform-Windows-blue)
**Activity Tracker** est un système de suivi d'activité conçu pour aider les utilisateurs à reconstruire leur historique de travail via une analyse automatisée des actions numériques. **Activity Tracker** est un système de suivi d'activité **Windows uniquement** conçu pour aider les utilisateurs à reconstruire leur historique de travail via une analyse automatisée des actions numériques.
## Caractéristiques (MVP) ## Caractéristiques (MVP)
- **Capture passive** : Screenshots toutes les 5 minutes + métadonnées fenêtres - **Capture passive** : Screenshots toutes les 5 minutes + métadonnées fenêtres Windows
- **API Windows native** : Utilise GetForegroundWindow et les APIs Win32
- **Stockage sécurisé** : Base SQLite avec chiffrement AES-256-GCM - **Stockage sécurisé** : Base SQLite avec chiffrement AES-256-GCM
- **Analyse intelligente** : Classification automatique en 5 catégories - **Analyse intelligente** : Classification automatique en 5 catégories
- **Rapports journaliers** : Export JSON avec statistiques détaillées - **Rapports journaliers** : Export JSON avec statistiques détaillées
@ -18,28 +20,27 @@
### Prérequis ### Prérequis
- Rust 1.70+ - **Windows 10 ou supérieur** (requis)
- Cargo - Rust 1.70+ et Cargo (installer via [rustup](https://rustup.rs/))
- SQLite3 - SQLite3 (inclus automatiquement via Cargo)
### Compilation ### Compilation
```bash ```powershell
git clone https://gitea.legion-muyue.fr/Muyue/activity-tracker.git git clone https://gitea.legion-muyue.fr/Muyue/activity-tracker.git
cd activity-tracker cd activity-tracker
cargo build --release cargo build --release
``` ```
Le binaire compilé sera disponible dans `target/release/activity-tracker`. Le binaire compilé sera disponible dans `target\release\activity-tracker.exe`.
### Installation système ### Installation système
```bash ```powershell
# Linux/macOS # Ajoutez le répertoire à votre PATH ou copiez le binaire
sudo cp target/release/activity-tracker /usr/local/bin/ copy target\release\activity-tracker.exe C:\Program Files\ActivityTracker\
# Ou ajoutez le chemin à votre PATH # Ou utilisez directement depuis le dossier target\release
export PATH=$PATH:$(pwd)/target/release
``` ```
## Utilisation ## Utilisation
@ -173,7 +174,7 @@ inactivity_threshold = 600 # 10 minutes
[storage] [storage]
max_storage_mb = 500 max_storage_mb = 500
retention_days = 30 retention_days = 30
db_path = "data/activity_tracker.db" db_path = "data\\activity_tracker.db"
[ai] [ai]
categories = ["Development", "Meeting", "Research", "Design", "Other"] categories = ["Development", "Meeting", "Research", "Design", "Other"]
@ -277,11 +278,12 @@ Pattern::new(vec!["slack", "discord", "telegram"], 0.9),
- [ ] **Détection audio** de réunions - [ ] **Détection audio** de réunions
- [ ] **Intégrations** (Trello, Jira, calendriers) - [ ] **Intégrations** (Trello, Jira, calendriers)
## Problèmes connus ## Plateforme supportée
- **Linux** : L'accès aux métadonnées de fenêtres nécessite X11 (Wayland non supporté) - **Windows uniquement** : Fonctionne avec Windows 10 et supérieur
- **macOS** : Nécessite autorisations Accessibilité (voir documentation officielle) - Utilise les APIs Windows natives pour la capture de fenêtres
- **Windows** : Fonctionne avec les privilèges standards - Aucune autorisation spéciale requise (privilèges standards suffisants)
- Les plateformes Linux et macOS ne sont **pas supportées**
## Contribution ## Contribution

View File

@ -1,4 +1,4 @@
# Activity Tracker MVP Configuration # Activity Tracker MVP Configuration (Windows uniquement)
[capture] [capture]
interval_seconds = 300 # 5 minutes (as per MVP spec) interval_seconds = 300 # 5 minutes (as per MVP spec)
@ -8,7 +8,7 @@ inactivity_threshold = 600 # 10 minutes
[storage] [storage]
max_storage_mb = 500 max_storage_mb = 500
retention_days = 30 retention_days = 30
db_path = "data/activity_tracker.db" db_path = "data\\activity_tracker.db" # Chemin Windows
[ai] [ai]
categories = ["Development", "Meeting", "Research", "Design", "Other"] categories = ["Development", "Meeting", "Research", "Design", "Other"]

View File

@ -1,9 +1,15 @@
/// Window metadata extraction /// Window metadata extraction (Windows uniquement)
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::error::{AppError, Result}; use crate::error::{AppError, Result};
#[cfg(target_os = "linux")] #[cfg(windows)]
use xcap::Window; use windows::{
core::PWSTR,
Win32::Foundation::{HWND, MAX_PATH},
Win32::System::ProcessStatus::GetModuleBaseNameW,
Win32::System::Threading::{OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_VM_READ},
Win32::UI::WindowsAndMessaging::{GetForegroundWindow, GetWindowTextW, GetWindowThreadProcessId},
};
/// Window metadata structure /// Window metadata structure
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@ -81,39 +87,72 @@ impl WindowMetadata {
} }
} }
/// Get metadata for the currently active window /// Get metadata for the currently active window (Windows implementation)
#[cfg(target_os = "linux")] #[cfg(windows)]
pub fn get_active_window_metadata() -> Result<WindowMetadata> { pub fn get_active_window_metadata() -> Result<WindowMetadata> {
let windows = Window::all() unsafe {
.map_err(|e| AppError::Capture(format!("Failed to get windows: {}", e)))?; // Get the foreground window handle
let hwnd = GetForegroundWindow();
if hwnd.0 == 0 {
return Ok(WindowMetadata::inactive());
}
// Find the active/focused window // Get window title
// For MVP, we'll use the first window as a fallback let mut title_buffer = [0u16; 512];
let active_window = windows.first() let title_len = GetWindowTextW(hwnd, &mut title_buffer);
.ok_or_else(|| AppError::Capture("No windows found".to_string()))?; let title = if title_len > 0 {
String::from_utf16_lossy(&title_buffer[..title_len as usize])
} else {
"Unknown".to_string()
};
// Get process ID
let mut process_id: u32 = 0;
GetWindowThreadProcessId(hwnd, Some(&mut process_id));
// Get process name
let process_name = get_process_name(process_id).unwrap_or_else(|| "unknown".to_string());
Ok(WindowMetadata { Ok(WindowMetadata {
title: active_window.title().to_string(), title,
process_name: active_window.app_name().to_string(), process_name,
process_id: active_window.id() as u32, process_id,
is_active: true, is_active: true,
}) })
}
} }
/// Get metadata for the currently active window (Windows implementation) /// Get process name from process ID (Windows)
#[cfg(target_os = "windows")] #[cfg(windows)]
pub fn get_active_window_metadata() -> Result<WindowMetadata> { fn get_process_name(process_id: u32) -> Option<String> {
// Simplified implementation for MVP unsafe {
// In production, would use Windows API to get active window let process_handle = OpenProcess(
Ok(WindowMetadata::unknown()) PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
false,
process_id,
).ok()?;
let mut name_buffer = [0u16; MAX_PATH as usize];
let name_len = GetModuleBaseNameW(
process_handle,
None,
&mut name_buffer,
);
if name_len > 0 {
Some(String::from_utf16_lossy(&name_buffer[..name_len as usize]))
} else {
None
}
}
} }
/// Get metadata for the currently active window (macOS implementation) /// Fallback for non-Windows platforms (not supported)
#[cfg(target_os = "macos")] #[cfg(not(windows))]
pub fn get_active_window_metadata() -> Result<WindowMetadata> { pub fn get_active_window_metadata() -> Result<WindowMetadata> {
// Simplified implementation for MVP Err(AppError::Capture(
// In production, would use macOS APIs to get active window "This application only supports Windows".to_string()
Ok(WindowMetadata::unknown()) ))
} }
#[cfg(test)] #[cfg(test)]

View File

@ -77,7 +77,7 @@ impl Config {
storage: StorageConfig { storage: StorageConfig {
max_storage_mb: 500, max_storage_mb: 500,
retention_days: 30, retention_days: 30,
db_path: "data/activity_tracker.db".to_string(), db_path: "data\\activity_tracker.db".to_string(),
}, },
ai: AiConfig { ai: AiConfig {
categories: vec![ categories: vec![