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"
edition = "2021"
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]
# Core dependencies
@ -17,7 +17,15 @@ env_logger = "0.11"
screenshots = "0.6"
image = "0.24"
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)
rusqlite = { version = "0.31", features = ["bundled"] }

View File

@ -3,12 +3,14 @@
![Version](https://img.shields.io/badge/version-0.1.0-blue)
![License](https://img.shields.io/badge/license-MIT-green)
![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)
- **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
- **Analyse intelligente** : Classification automatique en 5 catégories
- **Rapports journaliers** : Export JSON avec statistiques détaillées
@ -18,28 +20,27 @@
### Prérequis
- Rust 1.70+
- Cargo
- SQLite3
- **Windows 10 ou supérieur** (requis)
- Rust 1.70+ et Cargo (installer via [rustup](https://rustup.rs/))
- SQLite3 (inclus automatiquement via Cargo)
### Compilation
```bash
```powershell
git clone https://gitea.legion-muyue.fr/Muyue/activity-tracker.git
cd activity-tracker
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
```bash
# Linux/macOS
sudo cp target/release/activity-tracker /usr/local/bin/
```powershell
# Ajoutez le répertoire à votre PATH ou copiez le binaire
copy target\release\activity-tracker.exe C:\Program Files\ActivityTracker\
# Ou ajoutez le chemin à votre PATH
export PATH=$PATH:$(pwd)/target/release
# Ou utilisez directement depuis le dossier target\release
```
## Utilisation
@ -173,7 +174,7 @@ inactivity_threshold = 600 # 10 minutes
[storage]
max_storage_mb = 500
retention_days = 30
db_path = "data/activity_tracker.db"
db_path = "data\\activity_tracker.db"
[ai]
categories = ["Development", "Meeting", "Research", "Design", "Other"]
@ -277,11 +278,12 @@ Pattern::new(vec!["slack", "discord", "telegram"], 0.9),
- [ ] **Détection audio** de réunions
- [ ] **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é)
- **macOS** : Nécessite autorisations Accessibilité (voir documentation officielle)
- **Windows** : Fonctionne avec les privilèges standards
- **Windows uniquement** : Fonctionne avec Windows 10 et supérieur
- Utilise les APIs Windows natives pour la capture de fenêtres
- Aucune autorisation spéciale requise (privilèges standards suffisants)
- Les plateformes Linux et macOS ne sont **pas supportées**
## Contribution

View File

@ -1,4 +1,4 @@
# Activity Tracker MVP Configuration
# Activity Tracker MVP Configuration (Windows uniquement)
[capture]
interval_seconds = 300 # 5 minutes (as per MVP spec)
@ -8,7 +8,7 @@ inactivity_threshold = 600 # 10 minutes
[storage]
max_storage_mb = 500
retention_days = 30
db_path = "data/activity_tracker.db"
db_path = "data\\activity_tracker.db" # Chemin Windows
[ai]
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 crate::error::{AppError, Result};
#[cfg(target_os = "linux")]
use xcap::Window;
#[cfg(windows)]
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
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -81,39 +87,72 @@ impl WindowMetadata {
}
}
/// Get metadata for the currently active window
#[cfg(target_os = "linux")]
/// Get metadata for the currently active window (Windows implementation)
#[cfg(windows)]
pub fn get_active_window_metadata() -> Result<WindowMetadata> {
let windows = Window::all()
.map_err(|e| AppError::Capture(format!("Failed to get windows: {}", e)))?;
unsafe {
// Get the foreground window handle
let hwnd = GetForegroundWindow();
if hwnd.0 == 0 {
return Ok(WindowMetadata::inactive());
}
// Find the active/focused window
// For MVP, we'll use the first window as a fallback
let active_window = windows.first()
.ok_or_else(|| AppError::Capture("No windows found".to_string()))?;
// Get window title
let mut title_buffer = [0u16; 512];
let title_len = GetWindowTextW(hwnd, &mut title_buffer);
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 {
title: active_window.title().to_string(),
process_name: active_window.app_name().to_string(),
process_id: active_window.id() as u32,
title,
process_name,
process_id,
is_active: true,
})
}
/// Get metadata for the currently active window (Windows implementation)
#[cfg(target_os = "windows")]
pub fn get_active_window_metadata() -> Result<WindowMetadata> {
// Simplified implementation for MVP
// In production, would use Windows API to get active window
Ok(WindowMetadata::unknown())
}
/// Get metadata for the currently active window (macOS implementation)
#[cfg(target_os = "macos")]
/// Get process name from process ID (Windows)
#[cfg(windows)]
fn get_process_name(process_id: u32) -> Option<String> {
unsafe {
let process_handle = OpenProcess(
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
}
}
}
/// Fallback for non-Windows platforms (not supported)
#[cfg(not(windows))]
pub fn get_active_window_metadata() -> Result<WindowMetadata> {
// Simplified implementation for MVP
// In production, would use macOS APIs to get active window
Ok(WindowMetadata::unknown())
Err(AppError::Capture(
"This application only supports Windows".to_string()
))
}
#[cfg(test)]

View File

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