MovieMapper
Desktop application for organizing and managing movie and TV show collections. Scan media directories, extract metadata with FFmpeg, tag files as extras or commentary, and move them into Jellyfin-compatible folder structures — with a full audit trail.
Features
- Directory scanning — non-recursive folder browsing with real-time progress
- Metadata extraction — duration, video quality, and FPS via
ffprobe - File tagging — mark files as
extraorcommentarywith visual state - Jellyfin-compatible moves — tagged files move into spec-compliant folders
(
extras/,commentary/,behind the scenes/, ...) - TheTVDB integration — search shows, browse seasons/episodes, map episodes
to
SxxExxnaming convention - Audit logging — every move/rename writes a
.auditentry in the target directory; nothing happens silently - Progress reporting — IPC-driven progress updates during scans
Architecture
Three implementations share the same core concepts (scanner → metadata → tagging → mapping → audit):
┌─────────────────────────────────────────────────────────────┐
│ MovieMapper │
├──────────────┬──────────────────────────┬───────────────────┤
│ Electron │ Rust core (crate) │ iOS (Swift) │
│ desktop app │ movie_mapper │ companion client │
│ ├──────────────────────────┤ │
│ main.js │ model/ Show, Season, │ SharedModels/ │
│ renderer.js │ Episode │ Swift mirrors │
│ index.html │ service/ │ │
│ utils/ │ FileScanner │ build.sh │
│ │ MetadataExtractor │ archive.sh │
│ Electron │ TVDBClient │ Xcode project │
│ IPC bridge │ FileMapper │ │
│ │ TagManager │ │
│ │ AuditLogger │ │
└──────────────┴──────────────────────────┴───────────────────┘
| Component | Stack | Role |
|---|---|---|
main.js, renderer.js |
Electron (Node 20) | Desktop UI, IPC handlers, filesystem ops |
utils/fileUtils.js |
Node + fluent-ffmpeg | Directory scanning, ffprobe metadata |
Rust/ (crate movie_mapper) |
Rust, tokio, reqwest | Reusable core: scanner, TVDB client, mapper, tags, audit |
backend/ (crate) |
Rust | Async backend integration layer |
ui/ (crate) |
Rust, Iced | Native Rust UI prototype |
SharedModels/ |
Swift | Data models for the iOS companion |
Data flow
directory scan ──▶ ffprobe metadata ──▶ tag (extra/commentary)
│
▼
Jellyfin folder mapping
│
▼
.audit log entry per operation
Project structure
├── main.js # Electron main process (IPC, file ops, TVDB)
├── renderer.js # Renderer process (UI state, events)
├── index.html # UI (dark theme)
├── preload.js # Context bridge
├── utils/fileUtils.js # Directory scan + ffprobe metadata
├── tests/ # vitest suite (118 tests)
│ └── legacy/ # Earlier node:test integration tests
├── Rust/ # Rust core crate (movie_mapper)
│ ├── src/service/ # Scanner, TVDB client, mapper, tags, audit
│ ├── src/model/ # Show, Season, Episode, MediaFile
│ └── tests/ # Unit, integration, e2e, benchmarks
├── backend/ # Rust backend integration crate
├── ui/ # Rust Iced UI prototype
├── SharedModels/ # Swift models (iOS companion)
└── docs/ # Design docs, phase summaries, plans
Installation
Installers are published on the releases page (AppImage + .deb for Linux, .dmg for macOS, .exe for Windows).
Linux — AppImage
chmod +x MovieMapper-*.AppImage
./MovieMapper-*.AppImage
Linux — Debian/Ubuntu
sudo dpkg -i MovieMapper_*_amd64.deb
Rust CLI
cargo install --path Rust
Requires Rust and ffmpeg/ffprobe on PATH (used for metadata extraction).
Getting started
Desktop app (Electron) — from source
Prerequisites: Node.js 20+, npm, ffmpeg/ffprobe on PATH.
npm install
cp .env.example .env # add your TVDB_API_KEY
npm start
Building installers
npm run dist # current platform
npm run dist:linux # AppImage + deb
npm run dist:mac # dmg (macOS only)
npm run dist:win # nsis exe
Rust core
cargo build --workspace
cargo test --workspace
Rust CLI
The movie_mapper binary exposes the core as a command-line tool for
scripting and headless use:
# List media files with metadata
movie-mapper scan /path/to/season [--json]
# Rename files to Jellyfin naming (Show S01E01 - Quality.ext)
movie-mapper map /path/to/season --show "The Show" --season 1 [--episode-start 1] [--dry-run]
# Tag / untag files (persisted to .moviemapper.json)
movie-mapper tag /path/to/media "behind the scenes"
movie-mapper untag /path/to/media "behind the scenes"
# Move tagged files into the Jellyfin folder for that tag
movie-mapper move /path/to/media "behind the scenes" [--dry-run]
# TheTVDB (requires TVDB_API_KEY)
movie-mapper tvdb search "Breaking Bad"
movie-mapper tvdb show 81189
# Show the audit log for a directory
movie-mapper audit /path/to/season
All destructive commands support --dry-run for a filesystem-safe preview.
Every rename and move writes an entry to the directory's .audit log.
Configuration is read from ~/.config/movemapper/config.toml:
tvdb_api_key = "your-api-key"
media_dir = "/path/to/media"
Precedence for the API key: --tvdb-key flag > TVDB_API_KEY env var > config file.
Tests
npm test # vitest suite (JS)
cargo test --workspace # Rust unit + integration
Configuration
| Variable | Required | Description |
|---|---|---|
TVDB_API_KEY |
for TVDB search | TheTVDB v4 API token |
Jellyfin folder compatibility
Moves target the Jellyfin extras folder specification:
behind the scenes, deleted scenes, interviews, scenes, samples,
shorts, featurettes, clips, other, extras, trailers,
theme-music, backdrops.