# Record Daemon Architecture

## Overview

The record-daemon is a high-performance Rust daemon that automatically records League of Legends matches using libobs, captures game events via the League Client API (LQP), and exposes configuration via a Unix socket IPC for the Tauri app.

## High-Level Architecture

```mermaid
flowchart TB
    subgraph Daemon[Record Daemon]
        direction TB
        Main[Main Event Loop]
        State[State Machine]
        
        subgraph Core[Core Modules]
            Config[Configuration Manager]
            LQP[LQP Client]
            OBS[libobs Recording Engine]
            IPC[IPC Server]
            Timeline[Event Timeline Store]
        end
    end
    
    subgraph External[External Systems]
        LC[League Client]
        Game[League of Legends Game]
        Tauri[Tauri App]
        FS[File System]
    end
    
    LQP -->|WebSocket| LC
    OBS -->|Game Capture| Game
    IPC -->|Unix Socket| Tauri
    Config -->|JSON/TOML| FS
    Timeline -->|SQLite/JSON| FS
    OBS -->|Video Files| FS
    
    Main --> State
    State --> Config
    State --> LQP
    State --> OBS
    State --> IPC
    State --> Timeline
```

## Module Structure

```
record-daemon/
├── Cargo.toml
├── src/
│   ├── main.rs              # Entry point, daemon setup
│   ├── lib.rs               # Library exports
│   ├── config/
│   │   ├── mod.rs           # Config module exports
│   │   ├── settings.rs      # User-configurable settings
│   │   ├── presets.rs       # Encoding presets (quality/performance)
│   │   └── persistence.rs   # Config file I/O
│   ├── lqp/
│   │   ├── mod.rs           # LQP module exports
│   │   ├── client.rs        # LQP WebSocket client
│   │   ├── events.rs        # Game event types and parsing
│   │   └── auth.rs          # LQP authentication (port/password from lockfile)
│   ├── recording/
│   │   ├── mod.rs           # Recording module exports
│   │   ├── obs_context.rs   # libobs initialization and context
│   │   ├── capture.rs       # Game capture source setup
│   │   ├── encoder.rs       # Video encoder configuration
│   │   └── output.rs        # File output configuration
│   ├── ipc/
│   │   ├── mod.rs           # IPC module exports
│   │   ├── server.rs        # Unix socket server
│   │   ├── protocol.rs      # Request/response protocol
│   │   └── handlers.rs      # Command handlers
│   ├── timeline/
│   │   ├── mod.rs           # Timeline module exports
│   │   ├── store.rs         # Event storage backend
│   │   └── mapper.rs        # Event-to-video timestamp mapping
│   ├── state/
│   │   ├── mod.rs           # State module exports
│   │   ├── machine.rs       # Daemon state machine
│   │   └── transitions.rs   # State transition logic
│   └── error.rs             # Error types and handling
└── tests/
    └── ...
```

## State Machine

The daemon operates as a finite state machine with the following states:

```mermaid
stateDiagram-v2
    [*] --> Idle: Daemon Start
    
    Idle --> Monitoring: League Client Detected
    Monitoring --> Idle: League Client Closed
    
    Monitoring --> Recording: Match Started
    Recording --> Monitoring: Match Ended
    
    Recording --> Recording: Event Received
    
    Idle --> Error: Fatal Error
    Monitoring --> Error: Fatal Error
    Recording --> Error: Fatal Error
    
    Error --> Idle: Recovery
```

### State Descriptions

| State | Description |
|-------|-------------|
| **Idle** | Daemon is running but League Client is not detected |
| **Monitoring** | League Client is running, waiting for match to start |
| **Recording** | Active recording in progress, capturing events |
| **Error** | Recoverable error state, attempting recovery |

## Data Flow

### Match Recording Flow

```mermaid
sequenceDiagram
    participant D as Daemon
    participant LC as League Client
    participant OBS as libobs
    participant TL as Timeline
    participant FS as FileSystem
    
    Note over D: State: Monitoring
    LC->>D: MatchFound event via LQP
    D->>OBS: Initialize recording context
    D->>FS: Create output file
    
    Note over D: State: Recording
    LC->>D: GameStart event
    D->>OBS: Start recording
    D->>TL: Record start timestamp
    
    loop Game Events
        LC->>D: Kill/Death/Objective events
        D->>TL: Store event with timestamp
    end
    
    LC->>D: GameEnd event
    D->>OBS: Stop recording
    D->>TL: Finalize timeline
    D->>FS: Save timeline metadata
    
    Note over D: State: Monitoring
```

### IPC Communication Flow

```mermaid
sequenceDiagram
    participant T as Tauri App
    participant IPC as IPC Server
    participant D as Daemon Core
    participant C as Config Manager
    
    T->>IPC: Connect via Unix Socket
    T->>IPC: GetSettings request
    IPC->>D: Handle request
    D->>C: Read current settings
    C-->>D: Settings data
    D-->>IPC: Settings response
    IPC-->>T: JSON response
    
    T->>IPC: UpdateSettings request
    IPC->>D: Handle request
    D->>C: Validate and save
    C-->>D: Success
    D-->>IPC: Confirmation
    IPC-->>T: Success response
```

## Key Components

### 1. Configuration Module

Handles user-configurable settings with hot-reload support.

```rust
// Key configuration structures
struct Settings {
    output_path: PathBuf,
    encoder_preset: EncoderPreset,
    frame_rate: u32,
    quality: QualityLevel,
    audio_capture: AudioSettings,
}

enum EncoderPreset {
    Nvenc { bitrate: u32, cq_level: u32 },
    Amf { bitrate: u32 },
    X264 { preset: String, bitrate: u32 },
}

enum QualityLevel {
    Low,    // 720p30, lower bitrate
    Medium, // 1080p30, moderate bitrate
    High,   // 1080p60, high bitrate
    Ultra,  // 1440p60, max quality
}
```

### 2. LQP Client

Connects to the League Client via WebSocket using credentials from the lockfile.

```rust
struct LqpClient {
    port: u16,
    password: String,
    websocket: Option<WebSocket>,
}

// Key events to capture
enum GameEvent {
    MatchFound(MatchInfo),
    GameStart(GameStartInfo),
    Kill(KillEvent),
    Death(DeathEvent),
    Objective(ObjectiveEvent),
    GameEnd(GameEndInfo),
}
```

### 3. libobs Recording Engine

Manages OBS context, capture sources, and encoding.

```rust
struct ObsRecordingEngine {
    context: ObsContext,
    video_encoder: Box<dyn VideoEncoder>,
    audio_encoder: Box<dyn AudioEncoder>,
    output: Box<dyn Output>,
    active: bool,
}

impl ObsRecordingEngine {
    fn initialize(config: &Settings) -> Result<Self>;
    fn start_recording(&mut self, output_path: &Path) -> Result<()>;
    fn stop_recording(&mut self) -> Result<RecordingResult>;
    fn get_current_timestamp(&self) -> Duration;
}
```

### 4. IPC Server

Unix socket server for communication with Tauri app.

```rust
struct IpcServer {
    socket_path: PathBuf,
    listener: UnixListener,
    handlers: HashMap<IpcCommand, CommandHandler>,
}

enum IpcCommand {
    GetSettings,
    UpdateSettings,
    GetStatus,
    GetRecordings,
    GetTimeline { recording_id: Uuid },
    StartRecording,
    StopRecording,
}
```

### 5. Event Timeline Store

Stores game events mapped to video timestamps.

```rust
struct TimelineStore {
    backend: StorageBackend,
}

struct RecordingMetadata {
    id: Uuid,
    game_id: u64,
    start_time: DateTime<Utc>,
    end_time: Option<DateTime<Utc>>,
    output_file: PathBuf,
    events: Vec<TimestampedEvent>,
}

struct TimestampedEvent {
    video_timestamp: Duration,  // Offset from recording start
    game_timestamp: Duration,   // Game time
    event: GameEvent,
}
```

## Performance Considerations

### Memory Management
- Use `Arc<RwLock<T>>` for shared state between modules
- Pre-allocate buffers for video encoding
- Use `crossbeam` channels for inter-thread communication
- Implement backpressure on event channels

### CPU Efficiency
- Run libobs on dedicated thread
- Use async/await with `tokio` for I/O operations
- Minimize allocations in hot paths
- Use `parking_lot` locks for better performance

### GPU Acceleration
- Prioritize NVENC/AMF hardware encoding
- Fall back to software encoding only if unavailable
- Configure GPU-based video capture

### Disk I/O
- Use buffered writes for video output
- Store timeline data in memory during recording
- Flush to disk only on recording end
- Consider SSD for output directory

## Dependencies

```toml
[dependencies]
# Async runtime
tokio = { version = "1", features = ["full"] }

# Serialization
serde = { version = "1", features = ["derive"] }
serde_json = "1"
toml = "0.8"

# libobs bindings
libobs-rs = "0.1"  # or appropriate version

# WebSocket for LQP
tokio-tungstenite = "0.21"

# HTTP client for LQP REST API
reqwest = { version = "0.11", features = ["json", "rustls-tls"] }

# Async utilities
futures = "0.3"
async-trait = "0.1"

# Error handling
thiserror = "1"
anyhow = "1"

# Logging
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

# UUID for recording IDs
uuid = { version = "1", features = ["v4", "serde"] }

# Date/time
chrono = { version = "0.4", features = ["serde"] }

# Locking primitives
parking_lot = "0.12"

# Crossbeam channels
crossbeam = "0.8"

# Unix socket IPC
tokio-util = { version = "0.7", features = ["codec"] }

# Configuration directories
directories = "5"

# CLI (optional, for debugging)
clap = { version = "4", features = ["derive"] }
```

## IPC Protocol Specification

### Message Format

All messages are JSON-encoded with the following structure:

```json
{
  "type": "request|response|notification",
  "id": "uuid-v4",
  "command": "CommandName",
  "payload": { ... }
}
```

### Commands

| Command | Direction | Payload | Description |
|---------|-----------|---------|-------------|
| `GetSettings` | Request → | `{}` | Get current settings |
| `GetSettings` | ← Response | `Settings` | Current settings object |
| `UpdateSettings` | Request → | `Settings` | Update settings |
| `UpdateSettings` | ← Response | `{ "success": true }` | Confirmation |
| `GetStatus` | Request → | `{}` | Get daemon status |
| `GetStatus` | ← Response | `DaemonStatus` | Current state and info |
| `GetRecordings` | Request → | `{}` | List all recordings |
| `GetRecordings` | ← Response | `[RecordingMetadata]` | Recording list |
| `GetTimeline` | Request → | `{ "recording_id": "uuid" }` | Get specific timeline |
| `GetTimeline` | ← Response | `Timeline` | Event timeline |
| `RecordingStarted` | ← Notification | `RecordingInfo` | Recording started |
| `RecordingStopped` | ← Notification | `RecordingResult` | Recording ended |
| `GameEvent` | ← Notification | `GameEvent` | Live game event |

## Error Handling Strategy

```rust
#[derive(Debug, thiserror::Error)]
pub enum DaemonError {
    #[error("LQP connection failed: {0}")]
    LqpConnection(#[from] LqpError),
    
    #[error("Recording error: {0}")]
    Recording(#[from] RecordingError),
    
    #[error("Configuration error: {0}")]
    Config(#[from] ConfigError),
    
    #[error("IPC error: {0}")]
    Ipc(#[from] IpcError),
    
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),
}
```

## Next Steps

1. Set up `Cargo.toml` with all required dependencies
2. Implement configuration module first (needed by all other modules)
3. Implement LQP client for game detection and event capture
4. Implement libobs recording engine
5. Implement IPC server
6. Implement timeline storage
7. Wire everything together in the main daemon loop
8. Add comprehensive logging and error handling
9. Write tests and documentation
