# TVH2Jellyfin Transfer completed recordings from TVHeadend to Jellyfin media storage. ## Features - Monitors TVHeadend for completed recordings via JSON API - Pulls files from TVHeadend server via SCP - Writes to local Jellyfin media directory (Docker volume) - Organizes files by show name: `{dest_path}/{show_name}/{episode}.ts` - Deletes recordings from TVHeadend after successful transfer - Tracks transferred files to avoid duplicates - Runs as a Docker container - Dry run mode for testing without making changes ## Prerequisites 1. SSH key access from the Docker host to the TVHeadend server 2. TVHeadend API access (username/password) 3. Docker and Docker Compose ## Setup ### 1. Generate SSH Key (if needed) ```bash ssh-keygen -t rsa -b 4096 -f ~/.ssh/tvh2jellyfin ssh-copy-id -i ~/.ssh/tvh2jellyfin.pub user@tvheadend-server ``` ### 2. Configure Environment Copy the example environment file and edit with your settings: ```bash cp .env.example .env ``` Edit `.env` with your configuration: ```bash # TVHeadend server TVH_HOST=192.168.1.100 TVH_PORT=9981 TVH_USER=admin TVH_PASS=your_password # SSH user on TVHeadend server (defaults to TVH_USER) TVH_SSH_USER=admin # SSH key path on Docker host SSH_KEY_PATH=~/.ssh/tvh2jellyfin # Jellyfin media library path on Docker host JELLYFIN_MEDIA_PATH=/mnt/jellyfin/tv # Poll interval in seconds (default: 300 = 5 minutes) POLL_INTERVAL=300 ``` ### 3. Build and Run ```bash docker compose up -d ``` ### 4. View Logs ```bash docker compose logs -f ``` ## Configuration Options | Variable | Description | Default | |----------|-------------|---------| | `TVH_HOST` | TVHeadend server hostname/IP | `localhost` | | `TVH_PORT` | TVHeadend HTTP port | `9981` | | `TVH_USER` | TVHeadend API username | (required) | | `TVH_PASS` | TVHeadend API password | (required) | | `TVH_SSH_USER` | SSH username for TVHeadend server | `TVH_USER` | | `SSH_KEY_PATH` | Path to SSH private key on Docker host | `~/.ssh/id_rsa` | | `JELLYFIN_MEDIA_PATH` | Jellyfin media path on Docker host | `/mnt/jellyfin/tv` | | `POLL_INTERVAL` | Seconds between checks | `300` | | `LOG_LEVEL` | Logging level | `INFO` | | `DRY_RUN` | Test mode (no transfers/deletions) | `false` | ## How It Works ``` ┌─────────────────────┐ ┌─────────────────────┐ │ TVHeadend Server │ │ Docker Host │ │ │ │ │ │ ┌───────────────┐ │ HTTP API │ ┌───────────────┐ │ │ │ TVHeadend │◄─┼────────────────────┼──│ tvh2jellyfin │ │ │ │ API (:9981) │ │ │ │ container │ │ │ └───────────────┘ │ │ └───────┬───────┘ │ │ │ │ │ │ │ ┌───────────────┐ │ SCP (SSH) │ ▼ │ │ │ Recordings │──┼────────────────────┼──►┌─────────────┐ │ │ │ /recordings │ │ │ │ /media/tv │ │ │ └───────────────┘ │ │ │ (Jellyfin) │ │ │ │ │ └─────────────┘ │ └─────────────────────┘ └─────────────────────┘ ``` 1. Container queries TVHeadend API for completed recordings 2. Downloads files via SCP from TVHeadend server 3. Writes to local Jellyfin media volume 4. Removes recording from TVHeadend via API ## File Organization Recordings are organized by show name: ``` /mnt/jellyfin/tv/ ├── Show Name/ │ ├── Show Name - Episode Title.ts │ └── Show Name - 2024-01-15 2000.ts (if no episode title) └── Another Show/ └── Another Show - Episode.ts ``` ## Dry Run Mode Test what would happen without actually transferring or deleting anything: ```bash DRY_RUN=true docker compose up ``` In dry run mode: - Connects to TVHeadend API and lists completed recordings - Shows what files would be transferred and where - Shows which recordings have already been transferred - Does NOT connect via SSH - Does NOT transfer any files - Does NOT delete recordings from TVHeadend - Does NOT update the state file - Exits after a single check (no polling loop) Example output: ``` [DRY RUN] Starting sync cycle... [DRY RUN] Found 2 new recording(s) to transfer [DRY RUN] Would process: The News - Evening Edition [DRY RUN] Source: /recordings/The News - Evening Edition.ts [DRY RUN] Destination: /media/tv/The News/The News - Evening Edition.ts [DRY RUN] Size: 1250.5 MB [DRY RUN] Would delete from TVHeadend after transfer ``` ## Development ### Install Dependencies ```bash python -m venv venv source venv/bin/activate pip install -e ".[dev]" ``` ### Run Tests ```bash pytest ``` ### Run Locally ```bash export TVH_HOST=... export TVH_USER=... export TVH_PASS=... export TVH_SSH_USER=... export DEST_PATH=/path/to/destination python -m tvh2jellyfin.main ``` ## Troubleshooting ### SSH Connection Issues Ensure the SSH key has correct permissions: ```bash chmod 600 ~/.ssh/tvh2jellyfin ``` Test SSH connection manually: ```bash ssh -i ~/.ssh/tvh2jellyfin user@tvheadend-server ``` ### TVHeadend API Issues Test API access: ```bash curl -u admin:password http://tvheadend:9981/api/serverinfo ``` ### Permission Issues Ensure the Jellyfin media directory is writable: ```bash touch /mnt/jellyfin/tv/.test && rm /mnt/jellyfin/tv/.test ``` ### View Transfer History The state file (`data/state.json`) contains all transfer records.