Skip to content

hewel/jmsr

Repository files navigation

JMSR - Jellyfin MPV Shim Rust

CI Rust Tauri Solid.js License: MIT

A high-performance Jellyfin cast receiver that controls an external MPV player.
Built with Tauri v2, Solid.js, and Rust.

Features β€’ Roadmap β€’ Quick Start β€’ Architecture β€’ Troubleshooting


πŸ“– Overview

JMSR allows you to cast media from any Jellyfin client (web, mobile, TV) to your desktop, where it plays in MPV with full support for your custom configurations, shaders, and scripts.

πŸ’‘ Key Philosophy

JMSR does NOT embed libmpv. Instead, it spawns and controls a standalone MPV process via JSON IPC. This preserves your existing mpv.conf, shader packs (Anime4K, FSR, etc.), and all local customizations without compromise.

✨ Features

Feature Description
πŸ“Ί Cast Target Appears as a controllable device in Jellyfin's cast menu
πŸš€ External MPV Full compatibility with your system MPV configuration and shaders
πŸ”’ Persistent Auth Login once, stay connected with secure token storage
πŸ”„ Auto-Reconnect Resilient WebSocket connection with exponential backoff strategy
⏭️ Smart Playback Automatically plays the next episode when the current one finishes
🧠 Series Memory Remembers audio/subtitle language preferences per TV series
⌨️ Shortcuts Use Shift+N / Shift+P directly in MPV to skip episodes
πŸ–₯️ System Tray Runs quietly in the background with quick access controls
πŸ›‘οΈ Type-Safe 100% type-safe Rust-to-TypeScript communication via tauri-specta
🍏 Cross-Platform Native support for Windows, macOS, and Linux

πŸ—ΊοΈ Roadmap

  • Quick Connect - Login via code from another device
  • Intro Skipper Integration - Auto-skip intros/credits
  • Full-Featured Client UI - Browse libraries, manage media, and control playback like other Jellyfin clients
  • Embedded Player - Optional built-in video player without external MPV dependency
  • MPRIS Support - Linux media player integration for desktop controls

πŸš€ Enhanced Experience

  • Discord Rich Presence - Show current playback status on Discord profile
  • Custom Shaders Manager - Easy toggle for Anime4K, FSR, and other MPV shaders
  • SyncPlay Support - Watch together with friends via Jellyfin SyncPlay group

πŸ–₯️ Desktop Power User

  • Picture-in-Picture (PiP) - Floating mini-player mode
  • Global Hotkeys - Customizable shortcuts that work in the background
  • Offline Mode - Download media for offline playback

πŸ—οΈ Architecture

JMSR utilizes a robust three-actor architecture to ensure stability and separation of concerns.

graph LR
    subgraph JMSR[JMSR Desktop App]
        A[<b>Sentinel</b><br>Tauri GUI]
        B[<b>Bridge</b><br>Rust Backend]
        A <--> B
    end
    
    B <-->|JSON IPC| C[<b>Player</b><br>External MPV]
    B <-->|WebSocket + REST| D[<b>Jellyfin Server</b>]
    
    style A fill:#00a4dc,stroke:#333,color:white
    style B fill:#dea584,stroke:#333,color:black
    style C fill:#4c3c69,stroke:#333,color:white
    style D fill:#aa5cc3,stroke:#333,color:white
Loading
  1. Sentinel (Tauri GUI): Handles UI, WebSocket connection to Jellyfin, and state management.
  2. Bridge (Rust IPC): Translates commands and manages the external process.
  3. Player (MPV): The standalone media player instance running your config.

πŸš€ Quick Start

Prerequisites

  • MPV installed and in PATH

Installation

Download Pre-built Binaries (Recommended)

Download the latest release for your platform from the Releases page:

Platform Download
Windows .msi (installer) or .exe (NSIS)
macOS .dmg
Linux .deb or .AppImage

Build from Source

Development prerequisites
  • Bun (or npm/yarn)
  • Rust (latest stable)
  • Tauri CLI: bun add -g @tauri-apps/cli
# Clone the repository
git clone https://github.com/your-username/jmsr.git
cd jmsr

# Install dependencies
bun install

# Build production binaries
bunx tauri build

Binaries will be in src-tauri/target/release/bundle/.

Usage Steps

  1. Launch JMSR from your application menu or terminal.
  2. Authenticate by entering your Jellyfin server URL and credentials.
  3. Cast Media: JMSR will appear as "JMSR" in your Jellyfin client's cast menu.
  4. Enjoy: Media plays in MPV on your desktop with full control syncing.

πŸ› οΈ How It Works

  1. Authentication: User logs into Jellyfin and receives an access token.
  2. Registration: JMSR posts capabilities to /Sessions/Capabilities/Full.
  3. WebSocket: Connects to Jellyfin for real-time play state control.
  4. Cast Event: When user casts, Jellyfin sends a Play command.
  5. MPV Control: JMSR spawns MPV (if needed) and sends JSON IPC commands.
  6. Progress: Event-driven progress reporting via MPV property observation.
  7. Sync: Pause/seek/volume commands flow bidirectionally (Jellyfin ↔ MPV).
  8. Auto-Play: Automatically fetches the next episode upon natural file end.

πŸ’» Development

Project Structure

jmsr/
β”œβ”€β”€ src/                    # Solid.js frontend
β”‚   β”œβ”€β”€ index.tsx          # Entry point
β”‚   β”œβ”€β”€ bindings.ts        # Auto-generated IPC bindings
β”‚   └── components/        # UI components
β”œβ”€β”€ src-tauri/             # Rust backend
β”‚   β”œβ”€β”€ src/
β”‚   β”‚   β”œβ”€β”€ jellyfin/      # Jellyfin client implementation
β”‚   β”‚   └── mpv/           # MPV IPC driver logic
β”‚   └── tauri.conf.json    # Tauri configuration
└── doc/PRD.md            # Product requirements

Commands

Task Command
Frontend Dev bun run dev
Tauri Dev bunx tauri dev
Build Prod bunx tauri build
Test bun run test
Lint/Format bun run check

πŸ“ Code Conventions

  • TypeScript: Single quotes, Biome formatting.
  • Rust: 2-space indent (standard rustfmt.toml).
  • IPC: Always use typed commands.* from bindings, never raw invoke().
  • Solid.js: Use createSignal, createResource β€” NOT React hooks.

βž• Adding a Tauri Command

  1. Add function in src-tauri/src/command.rs with #[tauri::command] and #[specta].
  2. Register in src-tauri/src/lib.rs inside collect_commands![].
  3. Regenerate bindings by running bunx tauri dev.
  4. Import from commands in your TypeScript file.

Technology Stack

Component Technology
Framework Tauri v2
Frontend Solid.js + TypeScript
Backend Rust
Bundler Rsbuild
Styling TailwindCSS
IPC tauri-specta
Linting Biome
Testing Rstest

❓ Troubleshooting

JMSR doesn't appear as cast target
  • Ensure you're logged in (check Settings page shows "Connected").
  • Refresh the Jellyfin web page after JMSR connects.
  • Check Jellyfin Dashboard > Activity for the JMSR session.
MPV doesn't start
  • Verify MPV is installed: mpv --version.
  • Check MPV is in PATH (or set explicit path in Settings).
  • Windows (Scoop): JMSR auto-resolves symlinks, but ensure the shim is valid.
  • Check Settings > MPV Player for detected path.
Video doesn't play
  • Check Jellyfin transcoding settings.
  • Verify network connectivity to Jellyfin server.
  • Check JMSR log panel (Settings page) for error messages.
Connection lost
  • JMSR auto-reconnects with exponential backoff (1s β†’ 60s).
  • Check network connectivity.
  • Toast notifications will indicate connection status.

🀝 Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository.
  2. Create a feature branch.
  3. Follow existing code conventions (Biome for TS, rustfmt for Rust).
  4. Run bun run check before committing.
  5. Submit a pull request.

πŸ“„ License

MIT License - see LICENSE for details.

πŸ™ Acknowledgments

  • jellyfin-mpv-shim - The original Python inspiration.
  • Tauri - For the amazing desktop framework.
  • MPV - The best media player in existence.

About

A high-performance Jellyfin cast receiver that controls an external MPV player.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published