Skip to main content

Jellyfin Format Media Organizer

Project description

Logo

Jellyfin Format Media Organizer

~ JFMO ~

Releases · License

latest version 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 775 /data/media
sudo chmod g+s /data/media
sudo chmod g+s /data/media/download
sudo chmod g+s /data/media/download/manual
sudo chmod g+s /data/media/download/incomplete
sudo chmod g+s /data/media/movies
sudo chmod g+s /data/media/tv

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=jfmo
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 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:jfmo (created above):

id jfmo                # get uid
getent group jfmo     # 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.1.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.1-py3-none-any.whl (4.2 MB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: jfmo-3.0.1.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.1.tar.gz
Algorithm Hash digest
SHA256 c489988f52a6901f76476b6379acffd3a7396f23a55b18d9a706d5612feac9bc
MD5 ec447fb22c5c73a7fd988f20fc8a1b82
BLAKE2b-256 bf1472d672c0b545e134a98e1af8ada41bd6033485aee5b14329e44019c15a17

See more details on using hashes here.

File details

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

File metadata

  • Download URL: jfmo-3.0.1-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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 64ca48cda02a3ad7feb86d2c0dc6bc081e23d56aeb55553a59f0903d09344964
MD5 8bd58143377da723995c28c8c64439b0
BLAKE2b-256 cfc5f55630bfcd313ebe6ee017369b08a978bc892231637e47fc10265435206a

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