Skip to main content

Jellyfin Format Media Organizer

Project description

Logo

Jellyfin Format Media Organizer

~ JFMO ~

Releases · License

latest version CI Release PyPI downloads

Automatically organizes and renames media files according to Jellyfin's naming conventions. Detects movies vs TV shows, fetches TMDB metadata, and handles transliteration of non-Latin filenames.

Features

  • Smart movie vs TV show detection
  • TMDB integration for IDs and metadata
  • Configurable naming via tokens
  • Russian transliteration detection and conversion
  • Daemon mode for continuous monitoring

Installation

1. Create a system user and add it to the media group:

sudo groupadd media
sudo useradd --system --no-create-home --shell /usr/sbin/nologin jfmo
sudo usermod -aG media jfmo

Make sure your media directories are owned or readable by the media group:

sudo chown -R :media /data/media
sudo chmod -R g+rw /data/media

2. Set up the config:

Default config path: /etc/jfmo/config.yaml. See config.template.yaml for all options.

sudo mkdir -p /etc/jfmo
sudo vim /etc/jfmo/config.yaml
sudo chown -R jfmo:jfmo /etc/jfmo

Option 1 — pip / pipx

3. Install the package:

sudo pipx install jfmo --global

4. Create the systemd unit /etc/systemd/system/jfmo.service:

[Unit]
Description=Jellyfin Format Media Organizer
After=network.target

[Service]
Type=simple
User=jfmo
Group=media
ExecStart=/usr/local/bin/jfmo daemon
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target

5. Enable and start:

sudo systemctl daemon-reload
sudo systemctl enable --now jfmo
sudo systemctl status jfmo

Run once manually (without stopping the daemon):

sudo -u jfmo -g media jfmo run --apply

Option 2 — Docker

See example of docker compose file in docker-compose.template.yaml.

Set user in docker-compose.yaml to the uid:gid of jfmo:media (created above):

id jfmo                # get uid
getent group media     # get gid

1. Set up files:

sudo mkdir -p /opt/jfmo
cd /opt/jfmo
sudo vim docker-compose.yaml

Start as a background daemon (restarts automatically on reboot):

sudo docker compose up -d

Run once manually (e.g. to process a backlog):

# Dry-run preview — no files moved
sudo docker compose run --rm jfmo run

# Apply changes
sudo docker compose run --rm jfmo run --apply

Update

pipx

sudo pipx upgrade jfmo --global
sudo systemctl restart jfmo

Docker

sudo docker compose pull
sudo docker compose up -d

Usage

jfmo run              # dry-run preview (no files moved)
jfmo run --apply      # apply changes
jfmo daemon           # watch downloads directory continuously
jfmo --version

Naming

Available tokens

Token Description Example
{title} Media title Inception
{year} Release year 2010
{tmdb_id} TMDB numeric ID 27205
{quality} Resolution label [1080p]
{season} Season number, zero-padded 01
{episode} Episode number, zero-padded 04
{source} Release source WEB-DL, BluRay, BDRip
{codec} Video codec x265, HEVC, AV1
{hdr} HDR format HDR10, DV, DoVi
{service} Streaming service NF, AMZN, DSNP
{release_group} Release group name LostFilm, NOOBDL

Each pattern only accepts a specific subset of tokens:

Pattern (naming.) Allowed tokens
movie.file title, year, tmdb_id, quality, source, codec, hdr, service, release_group
tv.folder title, year, tmdb_id
tv.season season
tv.file title, season, episode, quality, source, codec, hdr, service, release_group

Example: before → after

downloads/
├── Severance.S02E02.1080p.mkv
├── The.Accountant.2.2024.2160p.mkv
├── Podslushano.v.Rybinske.S01E01.2160p.mkv   ← Russian transliteration
└── La Casa de Papel 3 - LostFilm [1080p]/

films/
└── The Accountant 2 (2024) [tmdbid-717559] - 2160p.mkv

tv/
├── Severance (2022) [tmdbid-95396]/
│   └── Season 02/
│       └── Severance S02E02 - 1080p.mkv
├── Подслушано в Рыбинске (2024) [tmdbid-245083]/   ← converted to Cyrillic
│   └── Season 01/
│       └── Подслушано в Рыбинске S01E01 - 2160p.mkv
└── La Casa de Papel (2017) [tmdbid-71446]/
    └── Season 03/
        └── La Casa de Papel S03E01 - 1080p.mkv

Transliteration Detection

Most media organizers (Radarr, Sonarr, etc.) cannot handle files where the title is written in Latin-script transliteration of Russian — e.g. Podslushano.v.Rybinske.S01.mkv looks like English but is actually «Подслушано в Рыбинске».

JFMO detects this automatically using a custom character n-gram language model trained to distinguish genuine English titles from Russian titles written in transliteration. When a transliterated title is detected, JFMO converts it back to Cyrillic before searching TMDB — resulting in a correct match instead of a failed lookup.

Podslushano.v.Rybinske.S01E01.mkv
  detected: Russian transliteration
  converted: Подслушано в Рыбинске
  TMDB match: tmdbid-XXXXXX
  → Подслушано в Рыбинске (2024) [tmdbid-XXXXXX]/Season 01/...

The model was trained on a custom dataset of ~2.5M titles (165k Russian + 2.4M English) built specifically for this project, achieving 93% accuracy on a diverse test set of 334 cases.

Acknowledgments

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jfmo-3.0.0.tar.gz (4.2 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jfmo-3.0.0-py3-none-any.whl (4.2 MB view details)

Uploaded Python 3

File details

Details for the file jfmo-3.0.0.tar.gz.

File metadata

  • Download URL: jfmo-3.0.0.tar.gz
  • Upload date:
  • Size: 4.2 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for jfmo-3.0.0.tar.gz
Algorithm Hash digest
SHA256 e83642e8cf340be00d68eb40aed1c30988d1347bbd61fb60f9f7787ee89ad9b2
MD5 95f8f7a801b8d6c99e3f3d0b19948f1c
BLAKE2b-256 7b3042d3939ec3d13bd18e0290cb060e606183b87ad00f3893ee0878d741674f

See more details on using hashes here.

File details

Details for the file jfmo-3.0.0-py3-none-any.whl.

File metadata

  • Download URL: jfmo-3.0.0-py3-none-any.whl
  • Upload date:
  • Size: 4.2 MB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for jfmo-3.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 667e4726919e23aed9f941905d79d3e9da327abe6701f19f2870e0702634dead
MD5 d364ff250ea90592c111e1bdfe788258
BLAKE2b-256 e4cf46164f3b9e616f619d63cc95bb70c3cc155a714efc1e75cab3799e4be3d9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page