Development Setup
Prerequisites
- Docker and Docker Compose
- Just — command runner (
cargo install justor other methods) - .NET 9.0 SDK (for plugin development)
- Rust 1.83+ (for server development)
- Node.js 20+ (optional, for JS tooling)
- mold (recommended, for faster Rust linking)
Quick Start
# Clone the repository
git clone https://github.com/TIGamingTV/JellyWatchParty.git
cd JellyWatchParty
# Configure git hooks (required once after clone)
just setup
# Start development environment
just up
This will:
- Start Jellyfin on
http://localhost:8096 - Build and mount the plugin
- Start the Rust session server on
http://localhost:3000 - Auto-inject the client script into Jellyfin’s
index.html
First-Time Setup
1. Jellyfin Configuration
After running just up:
- Open
http://localhost:8096 - Complete the Jellyfin setup wizard
- Add a media library (can use sample media)
- Create a user account
2. Plugin Configuration (Optional)
- Go to Dashboard > Plugins > JellyWatchParty
- Configure JWT Secret if testing authentication
- Save and restart Jellyfin
3. Verify Installation
- Play any video
- Look for the Watch Party button in the header
- Click to open the panel
Project Structure
JellyWatchParty/
├── src/
│ ├── clients/
│ │ └── jellyfin-web/ # JavaScript client modules
│ │ ├── plugin.js # Loader/entry point
│ │ ├── state.js # State management
│ │ ├── utils/ # Utility functions
│ │ │ ├── log.js, media.js, misc.js
│ │ │ ├── time.js, video.js
│ │ ├── ui/ # User interface
│ │ │ ├── cards.js, home.js, indicators.js
│ │ │ ├── render.js, styles.js, toasts.js
│ │ ├── playback/ # Video sync
│ │ │ ├── bind.js, play.js, sync.js
│ │ ├── chat/ # Text chat
│ │ │ ├── input.js, messages.js
│ │ ├── ws/ # WebSocket
│ │ │ ├── auth.js, connection.js, send.js
│ │ │ └── handlers/
│ │ │ ├── clock.js, playback.js
│ │ │ ├── room.js, sync.js
│ │ └── app/ # Initialization
│ │ ├── cleanup.js, lifecycle.js
│ │
│ ├── plugins/
│ │ └── jellyfin/
│ │ └── JellyWatchParty/ # C# Jellyfin plugin
│ │ ├── Plugin.cs
│ │ ├── Controllers/
│ │ ├── Configuration/
│ │ └── Web/ # Bundled JS (copied from clients/)
│ │
│ └── server/ # Rust WebSocket server
│ ├── src/
│ │ ├── main.rs
│ │ ├── types.rs
│ │ ├── routes.rs
│ │ ├── tasks.rs
│ │ ├── messaging.rs
│ │ ├── auth.rs
│ │ ├── utils.rs
│ │ ├── ws/
│ │ │ ├── mod.rs, connection.rs, dispatch.rs
│ │ │ ├── constants.rs, validation.rs, pending_play.rs
│ │ │ └── handlers/
│ │ │ ├── auth.rs, chat.rs, create.rs
│ │ │ ├── join.rs, misc.rs, playback.rs
│ │ └── room/
│ │ ├── mod.rs, leave.rs, close.rs
│ └── Cargo.toml
│
├── .githooks/
│ └── pre-commit # cargo fmt check on staged .rs files
│
├── infra/
│ ├── docker/ # Docker configuration
│ │ ├── server.Dockerfile
│ │ ├── dev/
│ │ │ ├── docker-compose.yml # Dev environment
│ │ │ ├── config/ # Jellyfin runtime config (gitignored)
│ │ │ └── scripts/
│ │ │ └── jellyfin-entrypoint.sh
│ │ └── prod/
│ │ └── docker-compose.yml # Prod / release builds
│ └── just/ # Just modules
│ ├── common.just # Shared variables
│ ├── build.just
│ ├── test.just
│ ├── lint.just
│ ├── logs.just
│ ├── clean.just
│ └── shell.just
│
├── docs/ # Documentation
│
├── justfile # Build automation
├── CLAUDE.md # AI assistant context
└── README.md # Project overview
Commands
Run just for a full list (with submodules). Key commands:
Development
| Command | Description |
|---|---|
just up |
Start full development environment |
just down |
Stop all services |
just dev |
Start stack and follow logs |
just restart |
Restart all services |
just watch |
Watch JS files and auto-restart on change |
just status |
Show service status with health checks |
Build (just build ...)
| Command | Description |
|---|---|
just build |
Build the Jellyfin plugin (default) |
just build plugin |
Build the Jellyfin plugin |
just build server |
Build the session server locally (Rust) |
just build image |
Rebuild session server Docker image |
just build all |
Build everything (plugin + server image) |
just rebuild |
Clean + rebuild + restart everything |
just release |
Build release artifacts (zip) |
Testing (just test ...)
| Command | Description |
|---|---|
just test |
Run all tests (Rust + .NET) |
just test server |
Run Rust server tests |
just test plugin |
Run .NET plugin tests |
Linting (just lint ...)
| Command | Description |
|---|---|
just lint |
Run all linters (Rust + JS) |
just lint server |
Lint Rust code (clippy) |
just lint client |
Lint JavaScript (eslint) |
just fmt |
Format all code |
just check |
Run cargo check (fast compile check) |
Logs (just logs ...)
| Command | Description |
|---|---|
just logs |
Follow logs from all services |
just logs server |
Follow session server logs |
just logs jellyfin |
Follow Jellyfin logs |
Clean (just clean ...)
| Command | Description |
|---|---|
just clean |
Clean all build artifacts |
just clean plugin |
Clean plugin build artifacts |
just clean server |
Clean server build artifacts |
just clean docker |
Remove Docker images and volumes |
just reset |
Full reset (containers + artifacts) |
Shell (just shell ...)
| Command | Description |
|---|---|
just shell server |
Open shell in session server container |
just shell jellyfin |
Open shell in Jellyfin container |
Quick aliases: u=up, d=down, s=status
Development Workflow
JavaScript Client
- Edit files in
src/clients/jellyfin-web/ - Rebuild and restart:
just rebuild - Hard refresh browser (Ctrl+F5)
Tip: Use just watch to automatically restart Jellyfin when JS files change.
Rust Session Server
- Edit files in
src/server/src/ - Rebuild everything:
just rebuild
C# Plugin
- Edit files in
src/plugins/jellyfin/JellyWatchParty/ - Build and restart:
just rebuild
Hot Reload
JavaScript
Use just watch for automatic reload on JS file changes. Otherwise:
- Run
just rebuild - Hard refresh browser (Ctrl+F5)
Rust
The session server needs restart after changes:
just rebuild
For faster iteration, run locally:
cd src/server
cargo watch -x run
C# Plugin
Requires rebuilding and restarting Jellyfin:
just rebuild
Debugging
JavaScript (Browser)
- Open Developer Tools (F12)
- Go to Console tab
- Filter by “JWP”
- Set breakpoints in Sources tab
Useful console commands:
// View current state
console.log(JWP.state);
// Check WebSocket connection
console.log(JWP.state.ws?.readyState);
// View rooms
console.log(JWP.state.rooms);
Rust (Server)
Enable debug logging:
# docker-compose.yml
environment:
- RUST_LOG=debug
Or when running the binary directly:
RUST_LOG=debug cargo run
C# (Plugin)
Check Jellyfin logs:
docker logs jellyfin-dev
Or enable debug logging in Jellyfin settings.
Testing Changes
Manual Testing
- Open Jellyfin in two browser windows
- Play the same video in both
- Create a room in one window
- Join from the other window
- Test sync functionality
Sync Testing
Things to test:
- Room creation
- Room joining
- Play/pause sync
- Seek sync
- Drift correction (watch for 5+ minutes)
- Disconnect/reconnect
- Host leaving
Common Development Issues
Script Not Updating
- Clear browser cache (Ctrl+Shift+Delete)
- Hard refresh (Ctrl+F5)
- Check ETag is changing:
curl -I http://localhost:8096/JellyWatchParty/ClientScript
Tip: Use just watch to automatically restart Jellyfin when JS files change, avoiding stale cache issues.
For other issues (plugin not loading, WebSocket connection problems, build errors), see the Troubleshooting & FAQ.
Build Optimization (Rust)
The Rust server has optimized build configuration for faster development cycles.
Docker Build Modes
The Dockerfile supports a BUILD_MODE argument:
| Mode | Usage | Optimization |
|---|---|---|
dev |
Local development (docker-compose.yml) |
Fast builds, debug symbols |
release |
CI/CD and production | Full optimization, smaller binary |
Development builds use BUILD_MODE=dev by default. CI releases use BUILD_MODE=release.
Mold Linker (Recommended)
Install the mold linker for 5-10x faster linking:
# Arch Linux / Manjaro
sudo pacman -S mold
# Ubuntu/Debian
sudo apt install mold
# macOS (via Homebrew)
brew install mold
The project’s .cargo/config.toml automatically uses mold when available.
Cargo Configuration
Located in src/server/.cargo/config.toml:
[build]
rustflags = ["-C", "link-arg=-fuse-ld=mold"]
[profile.dev]
incremental = true
opt-level = 0
[profile.dev.package."*"]
opt-level = 2 # Optimize dependencies (they rarely change)
Tokio Features
The server uses minimal tokio features to reduce compile times:
# Only what's needed (instead of "full")
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time", "signal"] }
Build Times
| Build Type | Without Optimization | With Optimization |
|---|---|---|
| Clean build | ~4-5 min | ~2-3 min |
| Incremental rebuild | ~15-20s | ~2-3s |
IDE Setup
VS Code
Recommended extensions:
- rust-analyzer - Rust support
- C# Dev Kit - C# support
- ESLint - JavaScript linting
- Docker - Docker support
.vscode/settings.json:
{
"rust-analyzer.cargo.buildScripts.enable": true,
"editor.formatOnSave": true
}
JetBrains
- RustRover for Rust
- Rider for C#
Environment Variables
For local development, create .env file:
# .env
JWT_SECRET=dev-secret-at-least-32-characters-long
ALLOWED_ORIGINS=http://localhost:8096
RUST_LOG=debug
Next Steps
- Contributing - How to contribute
- Testing - Running tests
- Core Structure - System design