# Record Daemon

A high-performance League of Legends recording daemon built with Rust and libobs.

## Features

- **Automatic Recording**: Detects when League of Legends starts and automatically records matches
- **Game Event Capture**: Captures game events (kills, deaths, objectives) via the League Client API (LQP)
- **Event Timeline**: Maps game events to video timestamps for easy navigation
- **Hardware Encoding**: Supports NVIDIA NVENC, AMD AMF, and software encoding (x264)
- **IPC Interface**: Unix sockets (Linux) / Named pipes (Windows) for configuration and control from a Tauri app
- **Configurable Presets**: Quality presets from 720p30 to 1440p60
- **Cross-Platform**: Supports Windows and Linux

## Architecture

```
record-daemon/
├── src/
│   ├── main.rs              # Entry point, daemon setup
│   ├── lib.rs               # Library exports
│   ├── config/              # Configuration management
│   │   ├── mod.rs
│   │   ├── settings.rs      # User settings
│   │   ├── presets.rs       # Encoder presets
│   │   └── persistence.rs   # Config file I/O
│   ├── lqp/                 # League Client API
│   │   ├── mod.rs
│   │   ├── auth.rs          # Lockfile authentication
│   │   ├── client.rs        # WebSocket/REST client
│   │   └── events.rs        # Game event types
│   ├── recording/           # libobs recording
│   │   ├── mod.rs
│   │   ├── obs_context.rs   # OBS initialization
│   │   ├── encoder.rs       # Video encoding
│   │   ├── capture.rs       # Game capture
│   │   └── output.rs        # File output
│   ├── ipc/                 # IPC server
│   │   ├── mod.rs
│   │   ├── server.rs        # Platform-specific server
│   │   ├── protocol.rs      # Message protocol
│   │   └── handlers.rs      # Command handlers
│   ├── timeline/            # Event timeline
│   │   ├── mod.rs
│   │   ├── store.rs         # Storage backend
│   │   └── mapper.rs        # Event-to-video mapping
│   ├── state/               # State machine
│   │   ├── mod.rs
│   │   └── machine.rs       # State transitions
│   └── error.rs             # Error types
```

## Platform Support

| Feature | Linux | Windows |
|---------|-------|---------|
| Game Detection | ✅ Wine/Proton paths | ✅ Native paths |
| IPC Transport | Unix sockets | Named pipes |
| Lockfile Detection | ✅ | ✅ |
| Hardware Encoding | NVENC | NVENC, AMF |

## Building

### Linux

```bash
cd record-daemon
cargo build --release
```

### Windows

```powershell
cd record-daemon
cargo build --release
```

**Prerequisites for Windows:**
- Visual Studio Build Tools (MSVC)
- OBS Studio installed (for libobs DLLs)

## Usage

### Running the Daemon

```bash
# Run with default settings
./record-daemon

# Run with custom config
./record-daemon --config /path/to/config.toml

# Run with debug logging
./record-daemon --log-level debug

# Run in foreground
./record-daemon --foreground
```

### Command Line Options

| Option | Description |
|--------|-------------|
| `-c, --config <PATH>` | Path to configuration file |
| `-l, --log-level <LEVEL>` | Log level (trace, debug, info, warn, error) |
| `-f, --foreground` | Run in foreground (don't daemonize) |
| `-s, --socket <PATH>` | Socket path for IPC |

## Configuration

The configuration file is stored at:
- Linux: `~/.config/record-daemon/config.toml`
- Windows: `%APPDATA%\record-daemon\config.toml`

### Example Configuration

```toml
[video]
encoder_preset = { type = "nvenc", bitrate = 8000, cq_level = 20, two_pass = true }
quality = "high"
frame_rate = 60
hardware_acceleration = true

[output]
path = "C:\\Users\\User\\Videos\\LeagueRecordings"  # Windows
# path = "/home/user/Videos/LeagueRecordings"       # Linux
naming_pattern = "{date}_{time}_{champion}"
container = "mp4"

[audio]
enabled = true
bitrate = 192
sample_rate = 48000
channels = 2
capture_game = true
capture_mic = false

[daemon]
auto_record = true
monitor_client = true
poll_interval_ms = 1000
log_level = "info"
save_timeline = true
```

### Encoder Presets

#### NVIDIA NVENC
```toml
[video.encoder_preset]
type = "nvenc"
bitrate = 8000
cq_level = 20
two_pass = true
```

#### AMD AMF (Windows only)
```toml
[video.encoder_preset]
type = "amf"
bitrate = 8000
quality = "balanced"
```

#### x264 (Software)
```toml
[video.encoder_preset]
type = "x264"
preset = "veryfast"
bitrate = 6000
crf = 23
```

### Quality Levels

| Level | Resolution | FPS | Bitrate |
|-------|------------|-----|---------|
| Low | 720p | 30 | 4500 kbps |
| Medium | 1080p | 30 | 6000 kbps |
| High | 1080p | 60 | 8000 kbps |
| Ultra | 1440p | 60 | 12000 kbps |

## IPC Protocol

The daemon exposes an IPC server for communication with the Tauri app.

### Socket Location
- Linux: `$XDG_RUNTIME_DIR/record-daemon.sock` (or `/tmp/record-daemon.sock`)
- Windows: `\\.\pipe\record-daemon` (named pipe)

### Message Format

All messages are JSON-encoded:

```json
{
  "type": "request",
  "id": "uuid-v4",
  "command": "GetSettings",
  "payload": null
}
```

### Available Commands

| Command | Description |
|---------|-------------|
| `GetSettings` | Get current settings |
| `UpdateSettings` | Update settings |
| `ResetSettings` | Reset to defaults |
| `GetStatus` | Get daemon status |
| `GetEncoders` | Get available encoders |
| `StartRecording` | Start recording manually |
| `StopRecording` | Stop recording |
| `GetRecordings` | List all recordings |
| `GetTimeline` | Get event timeline for a recording |
| `Shutdown` | Shutdown the daemon |

### Example IPC Session (Linux)

```bash
# Connect to socket
nc -U /tmp/record-daemon.sock

# Get status
{"type":"request","id":"00000000-0000-0000-0000-000000000001","command":"GetStatus"}

# Response
{"request_id":"00000000-0000-0000-0000-000000000001","success":true,"data":{"status":"idle","isRecording":false,"clientConnected":false}}
```

### Example IPC Session (Windows)

```powershell
# Connect to named pipe using PowerShell
$client = New-Object System.IO.Pipes.NamedPipeClientStream(".", "record-daemon", [System.IO.Pipes.PipeDirection]::InOut)
$client.Connect()
$reader = New-Object System.IO.StreamReader($client)
$writer = New-Object System.IO.StreamWriter($client)

# Send command
$writer.WriteLine('{"type":"request","id":"00000000-0000-0000-0000-000000000001","command":"GetStatus"}')
$writer.Flush()

# Read response
$response = $reader.ReadLine()
Write-Host $response

$client.Close()
```

## Event Timeline

Each recording has an associated timeline that maps game events to video timestamps:

```json
{
  "recording_id": "uuid",
  "start_time": "2024-01-15T10:30:00Z",
  "end_time": "2024-01-15T11:15:00Z",
  "duration_secs": 2700,
  "events": [
    {
      "video_timestamp": 120,
      "game_timestamp": 115,
      "event_type": "kill",
      "description": "Player1 killed Player2",
      "timestamp": "2024-01-15T10:32:00Z"
    }
  ]
}
```

---

## Windows Test Plan

This section provides a comprehensive test plan for validating the daemon on Windows with League of Legends.

### Prerequisites

1. **System Requirements**
   - Windows 10/11 64-bit
   - League of Legends installed
   - OBS Studio installed (for libobs)
   - NVIDIA GPU (for NVENC) or AMD GPU (for AMF) - optional but recommended

2. **Build Requirements**
   - Rust toolchain (stable)
   - Visual Studio Build Tools 2022
   - Windows SDK

### Test Environment Setup

```powershell
# 1. Install Rust if not already installed
winget install Rustlang.Rustup

# 2. Verify Rust installation
rustc --version
cargo --version

# 3. Build the daemon
cd record-daemon
cargo build --release
```

### Test 1: Daemon Startup

**Objective:** Verify daemon starts correctly on Windows.

**Steps:**
```powershell
# Run daemon in foreground with debug logging
.\target\release\record-daemon.exe --foreground --log-level debug
```

**Expected Results:**
- Daemon starts without errors
- Named pipe `\\.\pipe\record-daemon` is created
- Log shows "IPC server started successfully"
- Config file created at `%APPDATA%\record-daemon\config.toml`

**Verification:**
```powershell
# Check if named pipe exists
[System.IO.Directory]::GetFiles("\\.\\pipe\\") | Where-Object { $_ -like "*record-daemon*" }
```

### Test 2: League Client Detection

**Objective:** Verify daemon detects League Client startup.

**Steps:**
1. Start the daemon with debug logging
2. Launch League of Legends Client
3. Wait for client to fully load (login screen)

**Expected Results:**
- Log shows "League Client detected (PID: XXXX, Port: XXXX)"
- Lockfile found at `C:\Riot Games\League of Legends\lockfile`
- Daemon transitions to Monitoring state

**Verification:**
```powershell
# Check lockfile exists
Get-Content "C:\Riot Games\League of Legends\lockfile"

# Expected format: LeagueClient:PID:PORT:PASSWORD:PROTOCOL
# Example: LeagueClient:12345:52432:abc123:https
```

### Test 3: Game Start Detection

**Objective:** Verify daemon detects game start and begins recording.

**Steps:**
1. Start daemon
2. Launch League Client
3. Start a game (Practice Tool, ARAM, or Summoner's Rift)
4. Wait for game to load

**Expected Results:**
- Log shows "Starting recording for game XXXXX"
- Recording file created in output directory
- Daemon transitions to Recording state

**Verification:**
```powershell
# Check for recording files
Get-ChildItem -Path "C:\Users\$env:USERNAME\Videos\LeagueRecordings" -Recurse
```

### Test 4: Event Capture

**Objective:** Verify game events are captured and mapped to timeline.

**Steps:**
1. Start daemon
2. Play a game (Practice Tool recommended for controlled testing)
3. Perform actions: kills, deaths, take objectives
4. End the game

**Expected Results:**
- Events logged in debug output
- Timeline JSON file created after game ends
- Events contain correct timestamps

**Verification:**
```powershell
# Check timeline files
Get-ChildItem -Path "$env:APPDATA\record-daemon\timelines" -Recurse

# View timeline content
Get-Content "$env:APPDATA\record-daemon\timelines\*.json" | ConvertFrom-Json
```

### Test 5: Game End Detection

**Objective:** Verify daemon stops recording when game ends.

**Steps:**
1. Start daemon
2. Play a game
3. End the game (win, lose, or surrender)

**Expected Results:**
- Log shows "Stopping recording"
- Recording file finalized
- Timeline saved
- Daemon transitions back to Monitoring state

### Test 6: IPC Communication

**Objective:** Verify IPC commands work on Windows named pipes.

**Steps:**
```powershell
# Create test script
$pipe = New-Object System.IO.Pipes.NamedPipeClientStream(".", "record-daemon", [System.IO.Pipes.PipeDirection]::InOut)
$pipe.Connect(5000)
$reader = New-Object System.IO.StreamReader($pipe)
$writer = New-Object System.IO.StreamWriter($pipe)

# Test GetStatus
$writer.WriteLine('{"type":"request","id":"00000000-0000-0000-0000-000000000001","command":"GetStatus"}')
$writer.Flush()
$response = $reader.ReadLine()
Write-Host "Status: $response"

# Test GetEncoders
$writer.WriteLine('{"type":"request","id":"00000000-0000-0000-0000-000000000002","command":"GetEncoders"}')
$writer.Flush()
$response = $reader.ReadLine()
Write-Host "Encoders: $response"

$pipe.Close()
```

**Expected Results:**
- GetStatus returns current daemon state
- GetEncoders returns available hardware encoders

### Test 7: Client Disconnect/Reconnect

**Objective:** Verify daemon handles client restarts correctly.

**Steps:**
1. Start daemon
2. Launch League Client
3. Close League Client
4. Reopen League Client

**Expected Results:**
- Log shows "League Client stopped" when closed
- Log shows "League Client detected" when reopened
- Daemon state transitions correctly

### Test 8: Multiple Game Sessions

**Objective:** Verify daemon handles multiple consecutive games.

**Steps:**
1. Start daemon
2. Play 3 consecutive games
3. Check all recordings

**Expected Results:**
- Each game creates separate recording
- Each recording has correct timeline
- No memory leaks or performance degradation

### Test 9: Hardware Encoding

**Objective:** Verify hardware encoding works correctly.

**Steps:**
1. Configure NVENC or AMF in config
2. Start daemon
3. Play a game
4. Check recording quality

**NVIDIA GPU Verification:**
```powershell
# Check NVENC is being used
nvidia-smi dmon -s u -d pwr,enc
# Should show encoder utilization during recording
```

**Expected Results:**
- Recording uses hardware encoder
- GPU encoder utilization shown in monitoring tools
- CPU usage remains low during recording

### Test 10: Error Recovery

**Objective:** Verify daemon recovers from errors gracefully.

**Steps:**
1. Start daemon
2. Start recording
3. Simulate error scenarios:
   - Kill game process
   - Delete output directory
   - Fill disk space (careful!)

**Expected Results:**
- Daemon logs error appropriately
- Daemon recovers and continues operation
- No crash or hang

### Test 11: Configuration Persistence

**Objective:** Verify settings are saved and loaded correctly.

**Steps:**
```powershell
# Edit config
$configPath = "$env:APPDATA\record-daemon\config.toml"
notepad $configPath

# Restart daemon
.\target\release\record-daemon.exe --foreground
```

**Expected Results:**
- Config changes persist across restarts
- Invalid config shows appropriate error
- Default config created if missing

### Test 12: Shutdown Handling

**Objective:** Verify clean shutdown.

**Steps:**
1. Start daemon
2. Start recording
3. Send Ctrl+C or shutdown signal

**Expected Results:**
- Recording stops cleanly
- Timeline saved
- Named pipe cleaned up
- No orphaned processes

---

## Development

### Running Tests

```bash
cargo test
```

### Building Documentation

```bash
cargo doc --open
```

### Code Structure

The daemon follows a state machine pattern:

```
Idle -> Monitoring -> Recording -> Monitoring -> Idle
         |               |              |
         v               v              v
       Error <-------- Error -------- Error
         |
         v
       Idle (recovery)
```

## Dependencies

- **tokio**: Async runtime
- **tokio-tungstenite**: WebSocket for LQP
- **reqwest**: HTTP client for LQP REST API
- **serde/serde_json**: Serialization
- **tracing**: Logging
- **parking_lot**: High-performance locks
- **chrono**: Date/time handling
- **uuid**: Unique identifiers

## Troubleshooting

### Windows: Named Pipe Connection Failed

```
Error: Cannot connect to named pipe
```

**Solution:** Ensure daemon is running and pipe exists:
```powershell
# Check pipe exists
[System.IO.Directory]::GetFiles("\\.\\pipe\\") | Where-Object { $_ -like "*record-daemon*" }
```

### Windows: Lockfile Not Found

```
Error: League Client lockfile not found
```

**Solution:** Check League Client installation path:
```powershell
# Common paths
Test-Path "C:\Riot Games\League of Legends\lockfile"
Test-Path "D:\Riot Games\League of Legends\lockfile"
```

### Windows: NVENC Not Available

```
Error: NVENC encoder not available
```

**Solution:**
1. Ensure NVIDIA GPU is present
2. Update NVIDIA drivers
3. Check NVENC support: `nvidia-smi`

## License

MIT
