Skip to main content

Jellyfin Media Organizer (JMO)

A plan-first Python CLI for organizing media into Jellyfin-friendly layouts. The current implementation is deliberately focused on TV shows while the planning and safety model is built out.

Try JMO in five minutes (without your media)

The safest way to evaluate JMO is with its disposable synthetic demo. It never reads your library, never calls a provider, and never changes files outside the demo directory.

PyPI is the preferred installation source, and the current v0.4.0 release is available there. Start in a writable working directory with Python 3.12 installed (or adjust -3.12 to a newer installed version). No environment activation is needed.

py -3.12 -m venv .venv
.\.venv\Scripts\python.exe -m pip install "jellyfin-media-organizer==0.4.0"

After installation succeeds, run the same disposable demo workflow:

.\.venv\Scripts\jmo.exe demo --output .\jmo-demo
.\.venv\Scripts\jmo.exe doctor .\jmo-demo\Shows --destination-root .\jmo-demo\OrganizedShows --output-dir .\jmo-demo\State\runs\initial --cache-dir .\jmo-demo\State\cache
.\.venv\Scripts\jmo.exe inspect .\jmo-demo\State\runs\demo-run

The jmo-demo directory must not already exist; choose a new --output path for another run. The demo's README.txt also prints commands with the exact paths. inspect takes the generated audit bundle (State/runs/demo-run), not the parent state directory. On macOS/Linux, use python3 to create the environment and .venv/bin/python / .venv/bin/jmo for its executables. See package publishing for the maintainer setup and trust boundary.

If you prefer a source checkout, follow Install below first. The demo creates a deterministic synthetic plan with a matched episode, a duplicate review group, and an explicit held file. A simple ready-plan summary looks like this:

records=1
matched=1
extra=0
duplicate=0
held=0
suspicious=0
unresolved=0
preflight_ready=true
apply_safe=true

The complete fabricated walkthrough, including duplicate, held, extra, sidecar, and ambiguous examples, is in the synthetic case study. For safe beta testing and feedback, see Community beta.

JMO is not a universal media manager yet. It is a TV-show-focused, plan-first tool for people who want to inspect and review a proposed Jellyfin layout before anything can move.

For a real library, the simplest entrypoint is the guided wizard:

.\.venv\Scripts\jmo.exe

With no subcommand, JMO walks through setup, doctor, planning, review, and the read-only apply check. If that check passes, the wizard offers the real apply and requires the literal APPLY confirmation; any other response leaves the media untouched. Advanced users and automation can continue to use the explicit plan, review, inspect, and apply commands.

JMO is intentionally conservative: planning and review remain non-mutating, while jmo apply is an explicitly gated executor for one exact reviewed plan. Apply permits only same-filesystem, atomic, no-overwrite renames for matched and extra operation groups. It never copies across filesystems, overwrites, deletes, quarantines, or moves duplicate/held/ignored records.

Current capabilities

  • deterministic parsing of common season/episode, absolute, special/OVA-OAD, and date-based filename patterns;
  • read-only inventory scanning for .mkv, .mp4, and .avi files;
  • explicit handling of samples, unreadable entries, blocked links, extras, and ambiguous evidence;
  • deterministic inventory reconciliation;
  • versioned organizer plan models and immutable JSON schema contracts;
  • data-driven aliases and numbering policies;
  • persistent TVMaze cache primitives for deterministic/offline replay;
  • canonical TVMaze show resolution with fail-closed ambiguity handling;
  • a namespaced metadata-provider boundary while TVMaze remains the configured provider;
  • end-to-end, show-grouped plan generation with cached provider metadata;
  • companion subtitle planning and duplicate-safe operation groups;
  • immutable JSON/CSV/text audit bundles with provenance and stable hashes;
  • whole-plan preflight that blocks unresolved, colliding, or unsafe plans;
  • session-bound, resumable non-mutating review for duplicate groups and held sources;
  • exact-hash, revision, and root-bound apply confirmation;
  • append-only durable apply journals with group rollback, resume, and verification;
  • synthetic regression fixtures for ambiguous and adversarial cases.

jmo plan is operational and remains strictly non-mutating. It inventories one explicit Shows root, resolves each show through the persistent provider cache, constructs destinations, classifies duplicates and companions, runs preflight, and writes an immutable audit bundle. It never moves, copies, renames, overwrites, or deletes media.

For first-time setup, run jmo doctor before planning and jmo inspect after a run to get a concise status summary. jmo config example and jmo overrides example generate starter files for reusable local configuration. jmo init creates that state layout in one step, and jmo demo creates a disposable synthetic workspace for learning the workflow without real media.

After initialization, use jmo run --state-dir <state> for normal repeatable paper runs. It loads the saved roots and provider settings and creates a fresh timestamped audit bundle without overwriting prior runs.

Provider details, including the optional TMDb adapter and its credential-safe cache behavior, are documented in providers.

jmo review consumes a fresh plan-schema-v3 manifest and records explicit review decisions into a resumable session plus a new reviewed override contract. Review can resolve duplicate decisions and held sources as provider-confirmed episodes/specials or explicit extras, but it is also strictly non-mutating: quarantine choices are markers only, and review never moves, deletes, or quarantines media. See the non-mutating review workflow before using it.

jmo apply consumes the already-reviewed plan.json, preflight.json, and run-provenance.json. It does not rerun parsing or make review decisions. Start with --check-only; an actual run additionally requires an external journal and the exact confirmation token bound to the plan hash, review-session hash, clean source revision, source root, and destination root. See the apply safety contract.

Requirements

  • Python 3.12+
  • CI is configured for Linux on Python 3.12, 3.13, and 3.14, plus Windows and macOS on Python 3.12. Installed wheel/sdist workflows run on all three operating systems; consult the CI results for the revision you install.
  • Project metadata advertises Python 3.12, 3.13, and 3.14 support.

The runtime package currently has no third-party dependencies.

Repository identity

The standalone project is maintained in the jellyfin-media-organizer repository, matching the installable project name. This repository name is the intentional long-term identity for JMO rather than a temporary fork-era name.

Maintained project metadata points to this repository and its issue tracker. Packaging or documentation changes should not reintroduce obsolete upstream package/repository URLs as JMO's own project metadata; historical upstream credit remains in the acknowledgments and license sections below.

Install

From a source checkout:

git clone https://github.com/akjroller/jellyfin-media-organizer.git
cd jellyfin-media-organizer
python -m venv .venv
./.venv/bin/python -m pip install .
./.venv/bin/jmo --version
./.venv/bin/jmo plan --help

Windows PowerShell does not require virtual-environment activation or an execution-policy change:

git clone https://github.com/akjroller/jellyfin-media-organizer.git
cd jellyfin-media-organizer
py -3.12 -m venv .venv
.\.venv\Scripts\python.exe -m pip install .
.\.venv\Scripts\jmo.exe --version
.\.venv\Scripts\jmo.exe plan --help

A minimal planning run uses separate existing source and destination roots, plus generated-state locations outside both media roots:

./.venv/bin/jmo plan ExampleMedia/Shows \
  --destination-root ExampleMedia/OrganizedShows \
  --output-dir LocalState/audit-001 \
  --cache-dir LocalState/cache

In PowerShell, use .\.venv\Scripts\jmo.exe instead of ./.venv/bin/jmo and put the arguments on one line (Bash backslashes are not PowerShell continuations).

For a complete first-time sequence, see First run. Other documentation uses jmo as shorthand for the executable in your environment; activation is optional.

Normal wheels and source distributions carry a file-verified build revision. They do not need a Git checkout at runtime. Builds made from dirty checkouts remain ineligible for apply, and altered or unstamped installations fail closed. Reinstall after pulling changes: pip install . installs a snapshot, not a live view of the checkout. Build metadata verifies integrity, not publisher authenticity; install artifacts only from a trusted source.

Review plan.json, plan.sha256, decision.sha256, run-provenance.json, preflight.json, preflight.txt, and the CSV reports in the output directory. A ready plan exits 0; configuration, provider, unresolved, and preflight failures use distinct nonzero exit codes.

Use --offline for a hard zero-provider-call replay from a warmed cache and --refresh for a deliberate refresh. Local override files are passed explicitly with --overrides. --json emits the versioned machine-readable summary; --verbose opts into additional local diagnostic detail.

The historical organizer command remains available as a compatibility alias:

organizer --version
organizer plan --help

You can also run the package directly:

python -m jellyfin_show_organizer --version
python -m jellyfin_show_organizer plan --help

Embedding applications can import the deliberately small read-only API:

from jellyfin_show_organizer.api import inspect_audit, plan_library

The API exposes planning and audit inspection only. It does not bypass review, preflight, approval, journaling, or rollback requirements.

Safety boundary

The current implementation is Shows-only. Do not point it at a Movies directory, a mixed media root, or a parent directory containing unrelated media.

Planning, review, preflight, and jmo apply --check-only are read-only with respect to media. A successful plan or completed review is not authorization to mutate files. Mutation requires the explicit apply subcommand, three exact approval values, a root-bound confirmation token, a journal outside the media roots, and successful live revalidation.

Apply eligibility is status-based. Only matched and extra video records plus their associated companions may move. A duplicate loser can retain the collided destination as audit evidence, so implementations and operators must never interpret a non-null destination as movement eligibility.

Repository examples and tests use synthetic paths and fixtures. Real library inventories, provider caches, manifests, reports, media files, deployment-specific overrides, machine-specific paths, and other environment-specific data should remain local and untracked.

Documentation

Development

The quickest complete local validation is:

python tools/run_checks.py

It runs the same constraints, lint, format, type, test, and repository-safety gates used by CI. Use the individual commands below when iterating on one check.

python -m pip install -e ".[dev]"
python tools/check_ci_constraints.py
python -m ruff check jellyfin_show_organizer tests tools
python -m ruff format --check jellyfin_show_organizer tests tools
python -m mypy jellyfin_show_organizer tests
python -m pytest
python tools/check_repository_safety.py

Public regression tests use fabricated data and offline provider fixtures. See the contributor workflow before adding parser, matcher, numbering, destination, sidecar, override, or provider behavior.

Project layout

jellyfin_show_organizer/   core application package
  data/                    versioned schemas and generic packaged defaults
tests/
  fixtures/                synthetic deterministic fixtures
  local/                   offline test suite
docs/                      architecture and operating guidance

Releases

JMO uses Semantic Versioning. Pull-request CI builds and verifies both wheel and source-distribution installs in isolated environments. Verified artifacts can be built by the deliberate release-artifact workflow or a matching version tag; the repository does not automatically publish packages to a package registry.

The v0.1.0 version tag was the first verified release. The v0.1.1 patch release added the reviewed-duplicate winner fix, v0.2.0 added onboarding, review-status, reporting, and contributor-workflow foundations, v0.3.0 added guided review workflows and quarantine hardening, v0.3.1 added the protected PyPI publication path, and v0.4.0 adds guided run plus the optional second provider. A GitHub Release is published separately from the tag and does not itself approve or authorize any media run. The presence of jmo apply in source remains subject to the explicit apply safety contract.

See the release policy for the version source of truth, supported runtime matrix, tag rules, artifact verification process, and privacy boundary.

Project history and credit

JMO began as a fork of jkwill87/mnamer, created and maintained by Jessy Williams. That MIT-licensed project provided the original media-organizing groundwork from which this project started.

JMO has since diverged into its own Jellyfin-focused, plan-first design. The upstream project and its maintainers are not responsible for JMO's current behavior or support. See ACKNOWLEDGMENTS.md for the retained attribution.

License

MIT. The original upstream copyright and permission notice is retained in LICENSE.txt.

Release files for jellyfin-media-organizer 0.4.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jellyfin-media-organizer 0.4.1
File Size Uploaded
jellyfin_media_organizer-0.4.1.tar.gz 230.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for jellyfin-media-organizer 0.4.1
File Interpreter ABI Platform
jellyfin_media_organizer-0.4.1-py3-none-any.whl Python 3 none any Details

Total release size: 499.5 kB

Release files / jellyfin_media_organizer-0.4.1.tar.gz

Download URL jellyfin_media_organizer-0.4.1.tar.gz
Size 230.8 kB
Tags Source
SHA-256 checksum
How to use checksums
3494eb31d6513d0dd19a6f7cf743cbc6882562e6af311c51ef3816d8df9d62b7
BLAKE2b-256 checksum
How to use checksums
a979e80b83d01068ee9b77596d7c89b90b9c5ca5aa0186a5220846c4dc9372c8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release files / jellyfin_media_organizer-0.4.1-py3-none-any.whl

Download URL jellyfin_media_organizer-0.4.1-py3-none-any.whl
Size 268.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
32fb6829127530a035a44d7cb57cb2f07d3c064fccdc4bcffbdc5b8800f4bebb
BLAKE2b-256 checksum
How to use checksums
8d72c3eeba87169401d548b3e957024b7e77d758202bf80bd5404ac7fc7f7115
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.4.1 This release

2 release files

0.4.0

2 release files

0.3.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page