main
..
rw-r--r--
5.5 KB
rw-r--r--
1.4 KB
rwxr-xr-x
13.9 KB

Jellyfin Favorites Sync

Sync Jellyfin playlist or favorite movies and series to a remote host via rsync.

Overview

This tool queries a Jellyfin server for items in a playlist (or favorited items), expands series to individual episodes, discovers parent directories containing media files and metadata (subtitles, .nfo files, posters), and syncs them to a remote host using rsync in mirror mode.

Features

  • Playlist or Favorites: Query items from a Jellyfin playlist or favorites
  • Series Expansion: Expands TV series to include all episodes
  • Complete Sync: Syncs parent directories to include all auxiliary files (.srt, .nfo, poster.jpg, etc.)
  • Mirror Mode: Uses rsync --delete to remove files when items are removed from playlist/favorites
  • Efficient: Single rsync invocation using --files-from for large sets
  • Resumable: Supports --partial and --append-verify for interrupted transfers
  • Configurable: Target host, paths, and SSH arguments all configurable

Usage

CLI

Using a playlist:

jellyfin-favorites-sync \
  --jellyfin-url https://jellyfin.sbr.pm \
  --api-key-file /run/agenix/jellyfin-api-key \
  --user-id vincent \
  --playlist-name "Sync to NAS" \
  --source-root /neo/videos \
  --dest-host aix.sbr.pm \
  --dest-user vincent \
  --dest-root /data/favorites \
  --dry-run \
  --verbose

Using favorites:

jellyfin-favorites-sync \
  --jellyfin-url https://jellyfin.sbr.pm \
  --api-key-file /run/agenix/jellyfin-api-key \
  --user-id vincent \
  --source-root /neo/videos \
  --dest-host aix.sbr.pm \
  --dest-user vincent \
  --dest-root /data/favorites \
  --dry-run \
  --verbose

Options

  • --jellyfin-url: Jellyfin server URL (required)
  • --api-key: Jellyfin API key (use –api-key-file for secrets)
  • --api-key-file: Path to file containing API key (recommended)
  • --user-id: Jellyfin user ID or username (required)
  • --playlist-id: Jellyfin playlist ID to sync (optional, instead of favorites)
  • --playlist-name: Jellyfin playlist name to sync (optional, instead of favorites)
  • --source-root: Root path of Jellyfin library (default: /neo/videos)
  • --dest-host: Destination SSH host (required)
  • --dest-user: SSH user (default: vincent)
  • --dest-root: Destination path (default: /data/favorites)
  • --ssh-arg: Additional SSH arguments (can be repeated)
  • --dry-run: Show operations without executing
  • --verbose: Enable verbose output

NixOS Service

Configure as a systemd service with scheduled execution:

Using a playlist:

services.jellyfin-favorites-sync = {
  enable = true;
  schedule = "daily";

  jellyfinUrl = "http://localhost:8096";
  apiKeyFile = config.age.secrets."jellyfin-favorites-sync-api-key".path;
  userId = "vincent";

  # Use a playlist instead of favorites
  playlistName = "Sync to NAS";

  sourceRoot = "/neo/videos";

  destination = {
    host = "aix.sbr.pm";
    user = "vincent";
    root = "/data/favorites";
  };

  sshArgs = [ "-o StrictHostKeyChecking=accept-new" ];
};

Using favorites (default):

services.jellyfin-favorites-sync = {
  enable = true;
  schedule = "daily";

  jellyfinUrl = "http://localhost:8096";
  apiKeyFile = config.age.secrets."jellyfin-favorites-sync-api-key".path;
  userId = "vincent";

  sourceRoot = "/neo/videos";

  destination = {
    host = "aix.sbr.pm";
    user = "vincent";
    root = "/data/favorites";
  };

  sshArgs = [ "-o StrictHostKeyChecking=accept-new" ];
};

How It Works

  1. Connect to Jellyfin: Authenticate using API key
  2. Query Items: Fetch items from playlist (if specified) or favorites
  3. Expand Series: For each series, fetch all episodes
  4. Discover Paths: Extract parent directories containing media + metadata
  5. Generate File List: Create rsync --files-from input
  6. Execute Rsync: Sync to remote host with --delete for mirror mode

Examples

Dry-Run to See What Would Be Synced

jellyfin-favorites-sync \
  --jellyfin-url https://jellyfin.sbr.pm \
  --api-key-file ~/.secrets/jellyfin-api-key \
  --user-id vincent \
  --dest-host aix.sbr.pm \
  --dry-run \
  --verbose

Sync to Different Host

jellyfin-favorites-sync \
  --jellyfin-url https://jellyfin.sbr.pm \
  --api-key-file /run/agenix/jellyfin-api-key \
  --user-id vincent \
  --dest-host backup.example.com \
  --dest-root /backups/jellyfin-favorites

With Custom SSH Port

jellyfin-favorites-sync \
  --jellyfin-url https://jellyfin.sbr.pm \
  --api-key-file ~/.secrets/jellyfin-api-key \
  --user-id vincent \
  --dest-host aix.sbr.pm \
  --ssh-arg "-p" \
  --ssh-arg "2222"

Requirements

  • Python 3.11+
  • requests library
  • click library
  • rsync command
  • openssh (for SSH transport)

Security

  • API Key: Stored in encrypted file via agenix, never logged
  • Path Validation: All paths validated within source root
  • SSH: Dedicated service user with restricted SSH key recommended

Troubleshooting

No Favorites Found

  • Verify user ID is correct (run jellyfin-favorites-sync --verbose)
  • Check that items are actually marked as favorite in Jellyfin UI
  • Ensure API key has correct permissions

Rsync Fails

  • Verify SSH connectivity: ssh dest-user@dest-host
  • Check destination path exists and has correct permissions
  • Use --verbose to see full rsync command

Series Not Expanding

  • Check Jellyfin logs for API errors
  • Verify series has episodes in Jellyfin library
  • Use --verbose to see API responses

License

MIT