MovieMapper/README.md
Jarian Cottingham 09c191a8be feat: release packaging — electron-builder installers + release pipeline
- electron-builder config: AppImage/deb (linux), dmg (mac), nsis (win)
- app icon, desktopName sync, deb maintainer metadata
- move electron to devDependencies; add author/license/repo metadata
- pin @noble/hashes 1.8.0 (electron-builder 26 ESM conflict)
- .github/workflows/release.yml: tag-triggered multi-OS build + release
- scripts/publish-release.sh: upload dist/ assets to a Gitea release
- README: installation + installer build instructions
- add missing .env.example
- verified locally: AppImage + deb build, deb structure checks out,
  118 JS tests still green
2026-08-20 20:46:35 +00:00

213 lines
7.6 KiB
Markdown

# 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.
[![CI](https://github.com/actions/workflows/test.yml/badge.svg)](https://github.com/actions/workflows/test.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
## 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 `extra` or `commentary` with 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 `SxxExx` naming convention
- **Audit logging** — every move/rename writes a `.audit` entry 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](https://git.example.com/jarianc/MovieMapper/releases)
(AppImage + .deb for Linux, .dmg for macOS, .exe for Windows).
### Linux — AppImage
```bash
chmod +x MovieMapper-*.AppImage
./MovieMapper-*.AppImage
```
### Linux — Debian/Ubuntu
```bash
sudo dpkg -i MovieMapper_*_amd64.deb
```
### Rust CLI
```bash
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.
```bash
npm install
cp .env.example .env # add your TVDB_API_KEY
npm start
```
### Building installers
```bash
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
```bash
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:
```bash
# 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`:
```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
```bash
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](https://docs.jellyfin.org/):
`behind the scenes`, `deleted scenes`, `interviews`, `scenes`, `samples`,
`shorts`, `featurettes`, `clips`, `other`, `extras`, `trailers`,
`theme-music`, `backdrops`.
## Documentation
- [Project overview](docs/PROJECT_OVERVIEW.md)
- [Feature matrix](docs/FEATURES.md)
- [Rust implementation notes](docs/RUST_IMPLEMENTATION.md)
- [Folder navigation design](docs/FOLDER_NAVIGATION_PLAN.md)
- [iOS plan](docs/iOS_PLAN.md)
## License
[MIT](LICENSE)