renamerOnUpdate

renamerOnUpdate

Using metadata from your Stash to rename/move your file.

Requirement

Installation (manually)

  • Download the whole folder ā€˜renamerOnUpdate’
    • renamerOnUpdate_config.py
    • log.py
    • renamerOnUpdate.py
    • renamerOnUpdate.yml
  • Place it in your plugins folder (where the config.yml is)
  • Reload plugins (Settings > Plugins > Reload)
  • renamerOnUpdate appears

:exclamation: Make sure to copy renamerOnUpdate_config.py to config.py
and configure the plugin before running it :exclamation:

Installation (via manager)

  • Go to Settings > Plugins
  • Find Available Plugins and expand the package called Community (stable).
  • Select renamerOnUpdate and click Install

:exclamation: Make sure to copy renamerOnUpdate_config.py to config.py
and configure the plugin before running it :exclamation:

Usage

  • Everytime you update a scene, it will check/rename your file. An update can be:

    • Saving in Scene Edit.
    • Clicking the Organized button.
    • Running a scan that updates the path.
  • By pressing the button in the Task menu.

    • It will go through each of your scenes.
    • :warning: It’s recommended to understand correctly how this plugin works,
      and use DryRun first.

Configuration

  • Read/Edit config.py

    • Change template filename/path
    • Add log_file path
  • There are multiple buttons in Task menu:

    • Enable: (default) Enable the trigger update
    • Disable: Disable the trigger update
    • Dry-run: A switch to enable/disable dry-run mode
  • Dry-run mode:

    • It prevents editing the file, only shows in your log.
    • This mode can write into a file (dryrun_renamerOnUpdate.txt), the change that the plugin will do.
      • You need to set a path for log_file in renamerOnUpdate_config.py
      • The format will be: scene_id|current path|new path. (e.g. 100|C:\Temp\foo.mp4|C:\Temp\bar.mp4)
      • This file will be overwritten everytime the plugin is triggered.

Custom configuration file

Due to the nature of how plugin updates work, your renamerOnUpdate_config.py
file will get replaced with the fresh copy resetting it to default values.
To work around that you can create a custom config file and use it instead.

  • Create a copy of renamerOnUpdate_config.py
  • Rename your copy to config.py
  • Use the config.py(it will default to renamerOnUpdate_config.py if not found)

[!note]
Since config.py file is not tracked it won’t get updated with new configuration options, so you will need to update it manually.

renamerOnUpdate_config.py explained

Template

To modify your path/filename, you can use variables.
These are elements that will change based on your metadata.

  • Variables are represented with a word preceded with a $ symbol. (E.g. $date)
  • If the metadata exists, this term will be replaced by it:
    • Scene date = 2006-01-02, $date = 2006-01-02
  • You can find the list of available variables in renamerOnUpdate_config.py

In the example below, we will use:

Filename

Change your filename (C:\Temp\QmlnQnVja0J1bm55.mp4)


Priority : Tags > Studios > Default

Filename - Based on a Tag

tag_templates  = {
 "rename_tag": "$year $title - $studio $resolution $video_codec",
 "rename_tag2": "$title"
}
tag new path
rename_tag C:\Temp\2008 Big Buck Bunny - Blender Institute 1080p H264.mp4
rename_tag2 C:\Temp\Big Buck Bunny.mp4

Filename - Based on a Studio

studio_templates  = {
 "Blender Institute": "$date - $title [$studio]",
 "Pixar": "$title [$studio]"
}
studio new path
Blender Institute C:\Temp\2008-05-20 - Big Buck Bunny [Blender Institute].mp4
Pixar C:\Temp\Big Buck Bunny [Pixar].mp4

Filename - Change filename no matter what

use_default_template  =  True
default_template  =  "$date $title"

The file became: C:\Temp\2008-05-20 - Big Buck Bunny.mp4

Path

Change your path (C:\Temp\QmlnQnVja0J1bm55.mp4)

Path - Based on a Tag

p_tag_templates  = {
 "rename_tag": r"D:\Video\\",
 "rename_tag2": r"E:\Video\$year"
}
tag new path
rename_tag D:\Video\QmlnQnVja0J1bm55.mp4
rename_tag2 E:\Video\2008\QmlnQnVja0J1bm55.mp4

Path - Based on a Studio

p_studio_templates  = {
 "Blender Institute": r"D:\Video\Blender\\",
 "Pixar": r"E:\Video\$studio\\"
}
studio new path
Blender Institute D:\Video\Blender\QmlnQnVja0J1bm55.mp4
Pixar E:\Video\Pixar\QmlnQnVja0J1bm55.mp4

Path - Based on a Path

p_path_templates = {
 r"C:\Temp": r"D:\Video\\",
 r"C:\Video": r"E:\Video\Win\\"
}
file path new path
C:\Temp D:\Video\QmlnQnVja0J1bm55.mp4
C:\Video E:\Video\Win\QmlnQnVja0J1bm55.mp4

Path - Change path no matter what

p_use_default_template  =  True
p_default_template  =  r"D:\Video\\"

The file is moved to: D:\Video\QmlnQnVja0J1bm55.mp4

Path - Special Variables

$studio_hierarchy - Create the entire hierarchy of studio
as folder (E.g. ../MindGeek/Brazzers/Hot And Mean/video.mp4). Use your parent studio.

^* - The current directory of the file.
Explanation:

  • If: p_default_template = r"^*\$performer"
  • It creates a folder with a performer name
    in the current directory where the file is.
  • C:\Temp\video.mp4 so ^*=C:\Temp\, result: C:\Temp\Jane Doe\video.mp4
  • If you don’t use prevent_consecutive option,
    the plugin will create a new folder everytime (C:\Temp\Jane Doe\Jane Doe\...\video.mp4).

Advanced

Groups

You can group elements in the template with {},
it’s used when you want to remove a character if a variable is null.

Example:

With date in Stash:

  • [$studio] $date - $title → [Blender] 2008-05-20 - Big Buck Bunny

Without date in Stash:

  • [$studio] $date - $title → [Blender] - Big Buck Bunny

If you want to use the - only when you have the date,
you can group the - with $date
Without date in Stash:

  • [$studio] {$date -} $title → [Blender] Big Buck Bunny
6 Likes
1 Like

I would like to see some sample configurations if someone wouldn’t mind sharing theirs. Here’s mine:

tag_templates = {
    "Hentai": "$date $title"
}
studio_templates = {}
use_default_template = True
default_template = "$date [{$parent_studio, }$studio]{ $performer}{ - $movie_title $movie_scene}{ - $title}{ ($duration $resolution $height $video_codec)}{ ($tags)}"
p_tag_templates = {
    "Webcam": "/public/nsfw/performers/$performer/webcam",
    "JAV": "/public/nsfw/categories/jav/$studio_hierarchy",
    "Movie": "/public/nsfw/studios/$studio_hierarchy",
    "PMV": "/public/nsfw/categories/pmv",
    "HMV": "/public/nsfw/categories/hmv",
    "Hentai": "/public/nsfw/categories/hentai/$studio_hierarchy",
    "Animated": "/public/nsfw/categories/animated",
}
p_studio_templates = {
}
p_path_templates = {}
p_use_default_template = True
p_default_template = r"/public/nsfw/studios/$studio_hierarchy"
p_non_organized = r""
p_tag_option = {}
log_file = r"/home/user/.stash/plugins/community/renamerOnUpdate/rename_log.txt"
associated_extension = ["srt", "vtt", "funscript"]
filename_as_title = False
filename_splitchar = " "
field_whitespaceSeperator = ""
field_replacer = {}
replace_words = {}
date_format = r"%Y-%m-%d"
duration_format = r"%H;%M;%S"
lowercase_Filename = False
titlecase_Filename = False
removecharac_Filename = "#"
performer_splitchar = ", "
performer_limit = 3
performer_limit_keep = True
performer_sort = "name"
performer_ignoreGender = []
duplicate_suffix = ["", "_1", "_2", "_3", "_4", "_5", "_6", "_7", "_8", "_9", "_10"]
prevent_title_performer = False
prevent_consecutive = True
remove_emptyfolder = True
path_one_performer = True
path_noperformer_folder = False
path_keep_alrperf = True
prepositions_list = ["The", "A", "An"]
prepositions_removal = False
squeeze_studio_names = False
rating_format = "{}"
tags_splitchar = ", "
tags_whitelist = [
    "Low Quality", "Trailer"
]
tags_blacklist = [
]
only_organized = True
ignore_path_length = False
order_field = [
    "$video_codec",
    "$audio_codec",
    "$resolution",
    "tags",
    "rating",
    "$height",
    "$title",
    "$studio_family",
    "$studio",
    "$parent_studio",
    "$performer",
]
alt_diff_display = False
batch_number_scene = -1
enable_hook = True
dry_run = False
dry_run_append = True
process_getall = False
process_kill_attach = False
use_ascii = False

First off, thanks for developing and maintaining this plugin—it really helps streamline media organization for many of us!

The main issue I keep running into with this plugin is that sometimes files get renamed incorrectly because Stash misidentifies some scenes, especially with shorter clips. After this happens, I’m stuck: if I manually fix the filename, it just gets auto-renamed again the next time the scene updates. So basically, the only workaround I’ve found is to copy those affected files elsewhere to keep track of them, which isn’t ideal.

Does anyone have any better ideas or solutions for this?
Maybe some way to mark scenes as ā€œlockedā€ so the renamer skips them, or exclude by some criteria? Or any suggestions for alternative methods to avoid this renaming loop with misidentified files?

Would love to hear how others handle this, or if there are any tips from the developer for dealing with this edge case!

Thanks in advance!

You can setup tag to automatically move it to new directory https://discourse.stashapp.cc/t/renameronupdate/1754#p-3298-path-based-on-a-tag-15.

When I set a tag to a scene, the scene file gets duplicated… Any fix to this issue?

What has changed in this version over the original?

The recent develop builds of Stash, and the upcoming stable release introduces,

Partial dates (year only or month/year) are now supported for all date fields. (#6359)

This breaks renamerOnUpdate’s date format handling when it expects to receive dates in the YYYY-MM-DD format but may receive them in YYYY or YYYY-MM format instead.

ERRO[2025-12-16 22:00:01] [Plugin / renamerOnUpdate] main function error: time data ā€˜2017’ does not match format ā€˜%Y-%m-%d’
ERRO[2025-12-16 22:00:01] [Plugin / renamerOnUpdate] Traceback (most recent call last):
ERRO[2025-12-16 22:00:01] [Plugin / renamerOnUpdate]   File ā€œ/home/voorhees/.stash/plugins/community/renamerOnUpdate/renamerOnUpdate.pyā€, line 1594, in 
ERRO[2025-12-16 22:00:01] [Plugin / renamerOnUpdate]     renamer(FRAGMENT_SCENE_ID)
ERRO[2025-12-16 22:00:01] [Plugin / renamerOnUpdate]   File ā€œ/home/voorhees/.stash/plugins/community/renamerOnUpdate/renamerOnUpdate.pyā€, line 1295, in renamer
ERRO[2025-12-16 22:00:01] [Plugin / renamerOnUpdate]     scene_information = extract_info(stash_scene, template)
ERRO[2025-12-16 22:00:01] [Plugin / renamerOnUpdate]                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ERRO[2025-12-16 22:00:01] [Plugin / renamerOnUpdate]   File ā€œ/home/voorhees/.stash/plugins/community/renamerOnUpdate/renamerOnUpdate.pyā€, line 537, in extract_info
ERRO[2025-12-16 22:00:01] [Plugin / renamerOnUpdate]     date_scene = datetime.strptime(scene_information[ā€œdateā€], r"%Y-%m-%d")
ERRO[2025-12-16 22:00:01] [Plugin / renamerOnUpdate]                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ERRO[2025-12-16 22:00:01] [Plugin / renamerOnUpdate]   File ā€œ/usr/lib/python3.12/_strptime.pyā€, line 554, in _strptime_datetime
ERRO[2025-12-16 22:00:01] [Plugin / renamerOnUpdate]     tt, fraction, gmtoff_fraction = _strptime(data_string, format)
ERRO[2025-12-16 22:00:01] [Plugin / renamerOnUpdate]                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ERRO[2025-12-16 22:00:01] [Plugin / renamerOnUpdate]   File ā€œ/usr/lib/python3.12/_strptime.pyā€, line 333, in _strptime
ERRO[2025-12-16 22:00:01] [Plugin / renamerOnUpdate]     raise ValueError(ā€œtime data %r does not match format %rā€ %
ERRO[2025-12-16 22:00:01] [Plugin / renamerOnUpdate] ValueError: time data ā€˜2017’ does not match format ā€˜%Y-%m-%d’

Can you make an option to only run for scenes that have a StashID?
Such as:
require_stashid = True # Only process scenes with StashIDs

1 Like

I too would like a way to have this plugin run on scenes with a stash id.

Wouldn’t it be easier to just use the filter ā€œStash ID is not nullā€ and than select everything. I do it like this. But I also deactivate it after using it to prevent renaming I didn’t wanted

Getting the message out of the blue. No issue with my config, I have used my current config for awhile.

[Plugin / renamerOnUpdate] error when trying to update the database (NOT NULL constraint

It’s due to the v0.31.0 DB migrations. folders.basename didn’t used to be required in the pragma, but now it is.

Manual workaround: create subdirectory (the destination) manually, and manually scan it.

Quick code fix: this is around line 907 in my copy, no idea how old it is. In the def db_rename_refactor. Basically you need to append ā€œbasenameā€ file to the INSERT, and values it to scene_info[ā€˜studio’].

            if parent_id:
                # create a new row with the new folder with the parent folder find above
                cursor.execute(
                    "INSERT INTO 'main'.'folders'('id', 'basename', 'path', 'parent_folder_id', 'mod_time', 'created_at', 'updated_at', 'zip_file_id') VALUES (?, ?, ?, ?, ?, ?, ?, ?);",
                    [
                        new_id, scene_info['studio'], scene_info['new_directory'], parent_id[0][0],
                        mod_time, mod_time, mod_time, None
                    ])
                stash_db.commit()
                folder_id = new_id
                break

NB: I’ve only tested this with my simple p_path_templates being rā€/blahblah/unsorted'ā€œ: rā€/blahblah/sorted/$studioā€. If you have more complex naming it won’t work. But if you’re simple like me, it should be fine.

Marked as broken as multiple people report issues now.

1 Like

Tried the code it didn’t seem to work.

Here is my setup:

I have the naming convention to have date - performer - title - studio code

default_template = ā€œ$date - $performer - $title ($studio_code)ā€

I have it set to move to the folder set by studio hierarchy (it not already created, folder will be created)

p_default_template = r"/data/$studio_hierarchy"

Different from yours but pretty basic.

Try scene_information[ā€˜studio_hierarchy’] in the query instead of scene_information[ā€˜studio’], eg ... new_id, scene_info['studio_hierarchy'], scene_info['new_directory'], ... in that query. It wouldn’t be fully proper (I think basename is intended to be just the final folder name, not the multiple layers that studio_hierarchy gives)… but would probably bypass validation. Take a DB backup first and try?

Sorry I can’t dig at it more, but I would hope anyways that most people use just $studio, but a quick glance looks like the $studio_hierarchy would work in the same way.

Okay, I will give it a shot.

I messed around with my config. It’s definitely related to the ā€œmoveā€ portion. Rename works fine.

I get the following error now:

[Plugin / renamerOnUpdate] error when trying to update the database (Error binding parameter 2: type ā€˜list’ is not supported), revert the move…

in db_rename_refactor:

I replaced:

        if parent_id:
            # create a new row with the new folder with the parent folder find above
            cursor.execute(
                "INSERT INTO 'main'.'folders'('id', 'path', 'parent_folder_id', 'mod_time', 'created_at', 'updated_at', 'zip_file_id') VALUES (?, ?, ?, ?, ?, ?, ?);",
                [
                    new_id,
                    scene_info["new_directory"],
                    parent_id[0][0],
                    mod_time,
                    mod_time,
                    mod_time,
                    None,
                ],
            )
            stash_db.commit()
            folder_id = new_id
            break

with:

        if parent_id:
            # create a new row with the new folder with the parent folder find above
            folder_basename = os.path.basename(scene_info["new_directory"])
            cursor.execute(
                "INSERT INTO 'main'.'folders'('id', 'basename', 'path', 'parent_folder_id', 'mod_time', 'created_at', 'updated_at', 'zip_file_id') VALUES (?, ?, ?, ?, ?, ?, ?, ?);",
                [
                    new_id,
                    folder_basename,
                    scene_info["new_directory"],
                    parent_id[0][0],
                    mod_time,
                    mod_time,
                    mod_time,
                    None,
                ],
            )
            stash_db.commit()
            folder_id = new_id
            break

Thank you, I will give it a try in the next day or so. Server is currently doing a parity sync.