Skip to content

Weegley/MovieRenamer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 

Repository files navigation

MoviesRenamer

Русская версия: README.ru.md

Production-oriented Python script for normalizing movie files and movie folders for Jellyfin and similar media libraries.

Unlike the TV-series version, this script keeps the existing high-level folder structure intact and renames movies conservatively inside that structure.

It supports common real-world layouts such as:

Movie.mkv
Movie.srt
Movie Folder/
├── movie.mkv
└── movie.srt
Collection/
├── Movie One.mkv
├── Movie One.eng.srt
├── Movie Two/
│   ├── movie two.mkv
│   └── movie two.forced_ru.srt
└── Movie Three.mkv

API keys and credentials

This script uses:

Kinopoisk Api Unofficial

The script currently contains a bundled shared Kinopoisk Api Unofficial key in the source code for quick testing.

This is convenient for trying the script, but for regular use it is strongly recommended to replace it with your own Kinopoisk Api Unofficial key.

Why:

  • shared keys can hit rate limits
  • shared keys may stop working unexpectedly
  • your own key is more reliable and predictable

TMDb

For TMDb, you should set your own bearer token in:

DEFAULT_TMDB_BEARER = "..."

If DEFAULT_TMDB_BEARER is empty or still contains the placeholder value, the script stops immediately with a clear error message.

If the token is present but invalid, the script shows the TMDb API error returned by the server.

TMDb is effectively required for real usage because:

  • it is the primary source for the intl profile
  • it is used for enrichment, external IDs, and localized titles in the ru profile
  • multilingual title fields depend on TMDb translations

In short:

  • bundled Kinopoisk key is acceptable for quick testing
  • your own Kinopoisk key is strongly recommended
  • your own TMDb bearer token is required

The script is designed to be interactive, conservative, and filesystem-safe:

  • it can preview changes before applying them
  • it asks for confirmation per movie
  • it avoids aggressive guessing in ambiguous cases
  • it keeps non-empty directories instead of deleting them blindly
  • it preserves the existing high-level movie library structure

Features

Metadata profiles

The script supports two metadata profiles:

  • ru
  • intl

ru profile

  • primary source: Kinopoisk
  • enrichment: TMDb
  • IMDb id: from Kinopoisk when available, otherwise from TMDb
  • primary title is usually Russian

Resolution flow:

kp -> tmdb ; imdb = kp if present else tmdb

intl profile

  • primary source: TMDb
  • IMDb id: from TMDb
  • Kinopoisk is not used as the primary metadata source
  • primary title is usually English / international

Resolution flow:

tmdb -> imdb

Canonical ID formats

The script always renders IDs in canonical Jellyfin-friendly form:

  • [kp-1234567]
  • [tmdbid-123456]
  • [imdbid-tt1234567]

Older input forms are still recognized during parsing.

Supported incoming variants include:

  • Kinopoisk:

    • [kp1234567]
    • [kp-1234567]
  • TMDb:

    • [tmdb12345]
    • [tmdb-12345]
    • [tmdbid-12345]
  • IMDb:

    • [tt1234567]
    • [imdbidtt1234567]
    • [imdbid=tt1234567]
    • [imdbid-tt1234567]

Movie recognition

  • searches movies via Kinopoisk or TMDb depending on profile
  • supports direct kp id input during manual Kinopoisk selection
  • supports direct tmdb id input during manual TMDb selection
  • reuses IDs already embedded in file and folder names
  • supports tolerant parsing of old and new ID formats

Conservative structure handling

The script does not force a new folder per movie.

It keeps the existing structure intact:

  • standalone movie files stay in their current folder
  • single-movie folders stay as folders
  • collection folders stay as collections
  • nested movie folders inside collections stay inside the collection

Collection support

Supports collection layouts such as:

  • multiple movie files in one folder
  • mixed collections where some movies are files and some are in subfolders
  • subfolders that contain video plus paired subtitles

Each movie inside a collection is processed separately.

The collection folder itself is not treated as one movie.

Update mode

--update-movie-folders

This mode:

  • scans already normalized movie folders
  • refreshes missing metadata and IDs
  • renames only the movie folder when needed
  • does not move movie or subtitle files

This is a safe metadata update mode.

Subtitle support

Supports subtitle files:

  • .srt
  • .ass
  • .ssa
  • .sub

Legacy subtitle metadata detection still recognizes common tokens such as:

  • language:
    • ru, rus, russian
    • en, eng, english
  • subtype:
    • forced
    • full
    • sdh

Subtitle suffix preservation

External subtitles are handled conservatively:

  • the matching video filename is used as the base
  • everything after the video stem is treated as the subtitle suffix
  • the suffix is preserved in the final subtitle filename
  • suffix separators are normalized to dots

Examples:

  • Movie.srtMovie.srt
  • Movie_ru.srtMovie.ru.srt
  • Movie.en.srtMovie.en.srt
  • Movie_forced.rus.srtMovie.forced.rus.srt
  • Movie_full.V1.rus.srtMovie.full.V1.rus.srt

Search query cleanup

For metadata search, the script uses a normalized query:

  • dots, dashes, underscores, colons, and noisy punctuation are treated as separators
  • if a year is detected, everything after the year is dropped from the search query
  • release garbage after the year does not pollute search
  • existing IDs still take precedence over text search

Conservative behavior

  • ambiguous subtitle-only files are not guessed aggressively
  • non-empty directories are not deleted
  • already normalized movies are left untouched
  • Ctrl+C or menu option 0 exits cleanly
  • cache and operations log failures are handled more gracefully on flaky or read-only paths

Default resulting structure

Default output naming:

ru profile

{title} ({original_title}) ({year}) [kp-{kp}][tmdbid-{tmdb}][imdbid-{tt}]{ext}
{title} ({original_title}) ({year}) [kp-{kp}][tmdbid-{tmdb}][imdbid-{tt}]{sub_suffix}{ext}

intl profile

{title} ({original_title}) ({year}) [tmdbid-{tmdb}][imdbid-{tt}]{ext}
{title} ({original_title}) ({year}) [tmdbid-{tmdb}][imdbid-{tt}]{sub_suffix}{ext}

Example inside an existing folder:

Достать ножи/
├── Достать ножи - Стеклянная луковица (Glass Onion) (2022) [kp-1343908][tmdbid-661374][imdbid-tt11564570].avi
├── Достать ножи - Стеклянная луковица (Glass Onion) (2022) [kp-1343908][tmdbid-661374][imdbid-tt11564570].forced.rus.srt
└── Достать ножи - Стеклянная луковица (Glass Onion) (2022) [kp-1343908][tmdbid-661374][imdbid-tt11564570].full.eng.srt

Installation

Requirements

  • Python 3.10+
  • internet access for Kinopoisk and TMDb requests

No external Python dependencies are required.


Usage

Basic run

python MoviesRenamer.py /path/to/Movies

Preview changes only

python MoviesRenamer.py /path/to/Movies --dry-run

Interactive preview before each movie

python MoviesRenamer.py /path/to/Movies --dry-first

Use international metadata profile

python MoviesRenamer.py /path/to/Movies --metadata-profile intl

Always confirm search results manually

python MoviesRenamer.py /path/to/Movies --mode manual

Update already normalized movie folders only

python MoviesRenamer.py /path/to/Movies --update-movie-folders --dry-run

Command line options

root                   Root path to Movies
--cache                Cache json path
--ops-log              Operations log file path
--metadata-profile     ru | intl
--mode                 smart | manual
--dry-first            Show resulting rename plan for each movie and ask confirmation
--dry-run              Do not modify filesystem; only print/log planned operations
--update-movie-folders Only update existing movie folder names using fresh metadata

Interactive workflow

For each detected movie item, the script:

  1. scans files
  2. tries to infer title from file/folder structure
  3. resolves movie metadata using the active profile
  4. builds a rename plan
  5. optionally shows preview
  6. applies changes after confirmation

Search result menu

Typical options:

  • 0 — exit the whole script
  • 98 — skip current search result selection
  • 99 — retry same search
  • Enter — accept the only result, when there is exactly one
  • any other text — new search query or direct kp / tmdb id

Examples of valid manual input:

  • kp1234567
  • 1234567
  • tmdb661374
  • Glass Onion

Preview mode

With --dry-first, the script shows a per-movie preview.

If nothing needs changing:

Already normalized: no moves needed

Logging

The script writes a human-readable operations log.

Default log file:

MoviesRenamer.operations.log

Typical entries:

============================================================
MOVIE  Glass.Onion.A.Knives.Out.Mystery.2022.WEB-DLRip_от New-Team  ->  Достать ножи - Стеклянная луковица (Glass Onion) (2022) [kp-1343908][tmdbid-661374][imdbid-tt11564570]

MOVE   Glass.Onion.A.Knives.Out.Mystery.2022.WEB-DLRip_от New-Team.avi  =>  Достать ножи - Стеклянная луковица (Glass Onion) (2022) [kp-1343908][tmdbid-661374][imdbid-tt11564570].avi
MOVE   Glass.Onion.A.Knives.Out.Mystery.2022.WEB-DLRip_от New-Team_[forced, rus].srt  =>  Достать ножи - Стеклянная луковица (Glass Onion) (2022) [kp-1343908][tmdbid-661374][imdbid-tt11564570].forced.rus.srt
SUMMARY ops=2 removed_dirs=0 kept_dirs=0 errors=0

Operation types

  • RENAME — rename movie folder
  • MOVE — normal recognized rename or move
  • DELDIR — removed empty directory
  • KEEPDIR — directory left in place because it is not empty
  • ERROR — operation failed

Cache

The script caches resolved metadata to reduce repeated manual work.

Default cache file:

.movies_rename_cache.json

Cached data may include:

  • chosen title
  • Kinopoisk ID
  • TMDb ID
  • IMDb ID
  • year
  • original title
  • localized titles

When testing template or localization changes, it is recommended to use a fresh cache file or delete the old one.

If cache saving fails on a network path or due to permissions, the script prints a warning and continues shutdown cleanly.


Naming templates

The script uses templates for final movie names.

Default templates

RU_MOVIE_FOLDER_TEMPLATE = "{title} ({original_title}) ({year}) [kp-{kp}][tmdbid-{tmdb}][imdbid-{tt}]"
INTL_MOVIE_FOLDER_TEMPLATE = "{title} ({original_title}) ({year}) [tmdbid-{tmdb}][imdbid-{tt}]"
RU_MOVIE_FILE_TEMPLATE = "{title} ({original_title}) ({year}) [kp-{kp}][tmdbid-{tmdb}][imdbid-{tt}]{ext}"
INTL_MOVIE_FILE_TEMPLATE = "{title} ({original_title}) ({year}) [tmdbid-{tmdb}][imdbid-{tt}]{ext}"
RU_SUBTITLE_FILE_TEMPLATE = "{title} ({original_title}) ({year}) [kp-{kp}][tmdbid-{tmdb}][imdbid-{tt}]{sub_suffix}{ext}"
INTL_SUBTITLE_FILE_TEMPLATE = "{title} ({original_title}) ({year}) [tmdbid-{tmdb}][imdbid-{tt}]{sub_suffix}{ext}"

Available template fields

Primary fields:

  • {title} — primary display title for the active metadata profile
  • {original_title} — original or provider-original title
  • {title_local} — localized title for the active profile context

Localized title fields:

  • {title_ru}
  • {title_en}
  • {title_de}
  • {title_fr}
  • {title_ko}
  • other title_XX fields, including regional variants such as {title_pt_BR} or {title_zh_CN}

Other fields:

  • {kp} — Kinopoisk ID without prefix
  • {tmdb} — TMDb ID without prefix
  • {tt} — IMDb ID including tt
  • {year} — release year
  • {lang} — subtitle language token
  • {subtype} — subtitle subtype token
  • {sub_suffix} — preserved subtitle suffix after the matching video stem
  • {ext} — file extension including dot

Deduplication rules

{title} is always the primary field.

Before rendering:

  • if {original_title} is equal to {title}, it is cleared
  • if any {title_XX} is equal to {title}, it is cleared
  • if secondary fields duplicate each other, only the first unique value is kept and later duplicates are cleared

Deduplication uses normalized title keys:

  • case-insensitive
  • repeated spaces ignored
  • separator and punctuation differences normalized

The script validates template fields on startup and fails fast if a template contains an unknown field.


Safety rules

The script is intentionally conservative.

It will do

  • rename recognized movie files and folders
  • preserve collection structure
  • preserve subtitle suffixes when a matching video stem is found
  • remove empty source directories when appropriate
  • keep non-empty directories untouched
  • update only movie folders in --update-movie-folders mode

It will not do

  • blindly create a new folder for every movie file
  • treat a collection folder as one movie
  • overwrite existing destination files
  • aggressively guess arbitrary subtitle-only files
  • delete non-empty directories

Recommended workflow

For new collections:

python MoviesRenamer.py /path/to/Movies --dry-first

This is the safest mode:

  • you see the plan before it is applied
  • you can skip or re-search if needed
  • you can stop anytime with 0 or Ctrl+C

Once you trust the current batch:

python MoviesRenamer.py /path/to/Movies

Limitations

  • relies on Kinopoisk and TMDb availability
  • does not try to understand every possible custom fan naming scheme
  • ambiguous subtitle-only cases are intentionally not guessed too aggressively
  • unusual collections may still need occasional manual selection

Exit behavior

  • 0 in menus exits the whole script cleanly
  • Ctrl+C also exits cleanly
  • cache is still saved on exit when possible
  • cache save failures are downgraded to warnings
  • operations log finalization is handled more defensively on flaky paths

License

MIT

About

Production-oriented Python script for normalizing movies files and folders. Powered by chatgpt

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages