Added additional fields

Added fields resolution, width, and galleries.
Fixed bug with studio.
Modified GUI option to simplify.
Added postfix styles option to advance renamefile_settings.py file.
Added logic to avoid running rename logic twice for the same file.
Implemented code to limit max logging file size.
This commit is contained in:
David Maisonave
2024-07-26 06:28:22 -04:00
parent 0e50b59957
commit 2f20c5507c
6 changed files with 117 additions and 34 deletions

View File

@@ -9,6 +9,8 @@ desktop.ini
*.ipch *.ipch
*.lib *.lib
*.log *.log
*.log.1
*.log.2
*.manifest *.manifest
*.obj *.obj
*.pch *.pch

View File

@@ -1,4 +1,4 @@
# RenameFile: Ver 0.2.5 # RenameFile: Ver 0.3.1
RenameFile is a [Stash](https://github.com/stashapp/stash) plugin which performs the following two main task. RenameFile is a [Stash](https://github.com/stashapp/stash) plugin which performs the following two main task.
- **Rename Scene File Name** (On-The-Fly) - **Rename Scene File Name** (On-The-Fly)
- **Append tag names** to file name - **Append tag names** to file name

View File

@@ -0,0 +1,14 @@
id: renamefile
name: RenameFile
metadata:
description: Renames video (scene) file names when the user edits the [Title] field located in the scene [Edit] tab.
version: 0.4.1
date: "2024-07-26 08:00:00"
requires: [pip install stashapp-tools, pip install pyYAML]
source_repository: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/RenameFile
files:
- README.md
- renamefile.yml
- renamefile.py
- renamefile_settings.py
- requirements.txt

View File

@@ -1,18 +1,19 @@
# This is a Stash plugin which allows users to rename the video (scene) file name by editing the [Title] field located in the scene [Edit] tab.
# By David Maisonave (aka Axter) 2024
# Get the latest developers version from following link: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/RenameFile
# Based on source code from https://github.com/Serechops/Serechops-Stash/tree/main/plugins/Renamer
import requests import requests
import os import os
import logging import logging
import stashapi.log as log # Importing stashapi.log as log for critical events ONLY
import shutil import shutil
from pathlib import Path from pathlib import Path
import hashlib import hashlib
import json import json
import sys import sys
from stashapi.stashapp import StashInterface from stashapi.stashapp import StashInterface
from logging.handlers import RotatingFileHandler
# This is a Stash plugin which allows users to rename the video (scene) file name by editing the [Title] field located in the scene [Edit] tab.
# Importing stashapi.log as log for critical events
import stashapi.log as log
# Import settings from renamefile_settings.py # Import settings from renamefile_settings.py
from renamefile_settings import config from renamefile_settings import config
@@ -21,14 +22,24 @@ script_dir = Path(__file__).resolve().parent
# Configure logging for your script # Configure logging for your script
log_file_path = script_dir / 'renamefile.log' log_file_path = script_dir / 'renamefile.log'
rfh = RotatingFileHandler(
filename=log_file_path,
mode='a',
maxBytes=2*1024*1024,
backupCount=2,
encoding=None,
delay=0
)
FORMAT = "[%(asctime)s - LN:%(lineno)s] %(message)s" FORMAT = "[%(asctime)s - LN:%(lineno)s] %(message)s"
logging.basicConfig(filename=log_file_path, level=logging.INFO, format=FORMAT) logging.basicConfig(level=logging.INFO, format=FORMAT, datefmt="%y%m%d %H:%M:%S", handlers=[rfh])
logger = logging.getLogger('renamefile') logger = logging.getLogger('renamefile')
DEFAULT_ENDPOINT = "http://localhost:9999/graphql" # Default GraphQL endpoint DEFAULT_ENDPOINT = "http://localhost:9999/graphql" # Default GraphQL endpoint
DEFAULT_FIELD_KEY_LIST = "title,performers,studio,tags" # Default Field Key List with the desired order DEFAULT_FIELD_KEY_LIST = "title,performers,studio,tags" # Default Field Key List with the desired order
DEFAULT_SEPERATOR = "-" DEFAULT_SEPERATOR = "-"
PLUGIN_ARGS = False PLUGIN_ARGS = False
errOccurred = False
inputToUpdateScenePost = False
exitMsg = "Change success!!"
# ------------------------------------------ # ------------------------------------------
@@ -65,7 +76,11 @@ try:
PLUGIN_ARGS = json_input['args']["mode"] PLUGIN_ARGS = json_input['args']["mode"]
except: except:
pass pass
logger.info(f"\nStarting (debugTracing={debugTracing}) (dry_run={dry_run}) (PLUGIN_ARGS={PLUGIN_ARGS})************************************************") try:
if json_input['args']['hookContext']['input']: inputToUpdateScenePost = True # This avoid calling rename logic twice
except:
pass
logger.info(f"\nStarting (debugTracing={debugTracing}) (dry_run={dry_run}) (PLUGIN_ARGS={PLUGIN_ARGS}) (inputToUpdateScenePost={inputToUpdateScenePost})************************************************")
if debugTracing: logger.info("settings: %s " % (settings,)) if debugTracing: logger.info("settings: %s " % (settings,))
if dry_run: if dry_run:
logger.info("Dry run mode is enabled.") logger.info("Dry run mode is enabled.")
@@ -102,7 +117,14 @@ separator = settings["zseparators"]
# ------------------------------------------ # ------------------------------------------
double_separator = separator + separator double_separator = separator + separator
# Extract styles from config
wrapper_styles = config["wrapper_styles"]
postfix_styles = config["postfix_styles"]
try:
if debugTracing: logger.info(f"Debug Tracing (json_input['args']={json_input['args']})................")
except:
pass
# GraphQL query to fetch all scenes # GraphQL query to fetch all scenes
query_all_scenes = """ query_all_scenes = """
@@ -142,7 +164,7 @@ def should_exclude_path(scene_details):
return False return False
# Function to form the new filename based on scene details and user settings # Function to form the new filename based on scene details and user settings
def form_filename(original_file_stem, scene_details, wrapper_styles): def form_filename(original_file_stem, scene_details):
if debugTracing: logger.info("Debug Tracing................") if debugTracing: logger.info("Debug Tracing................")
filename_parts = [] filename_parts = []
tag_keys_added = 0 tag_keys_added = 0
@@ -165,7 +187,6 @@ def form_filename(original_file_stem, scene_details, wrapper_styles):
def add_tag(tag_name): def add_tag(tag_name):
nonlocal tag_keys_added nonlocal tag_keys_added
nonlocal filename_parts nonlocal filename_parts
nonlocal wrapper_styles
if debugTracing: logger.info(f"Debug Tracing (tag_name={tag_name})................") if debugTracing: logger.info(f"Debug Tracing (tag_name={tag_name})................")
if max_tag_keys == -1 or (max_tag_keys is not None and tag_keys_added >= int(max_tag_keys)): if max_tag_keys == -1 or (max_tag_keys is not None and tag_keys_added >= int(max_tag_keys)):
return # Skip adding more tags if the maximum limit is reached return # Skip adding more tags if the maximum limit is reached
@@ -194,6 +215,7 @@ def form_filename(original_file_stem, scene_details, wrapper_styles):
studio_name = scene_details.get('studio', {}).get('name', '') studio_name = scene_details.get('studio', {}).get('name', '')
if debugTracing: logger.info(f"Debug Tracing (studio_name={studio_name})................") if debugTracing: logger.info(f"Debug Tracing (studio_name={studio_name})................")
if studio_name: if studio_name:
studio_name += postfix_styles.get('studio')
if debugTracing: logger.info("Debug Tracing................") if debugTracing: logger.info("Debug Tracing................")
if include_keyField_if_in_name or studio_name.lower() not in title.lower(): if include_keyField_if_in_name or studio_name.lower() not in title.lower():
if wrapper_styles.get('studio'): if wrapper_styles.get('studio'):
@@ -202,6 +224,7 @@ def form_filename(original_file_stem, scene_details, wrapper_styles):
filename_parts.append(studio_name) filename_parts.append(studio_name)
elif key == 'title': elif key == 'title':
if title: # This value has already been fetch in start of function because it needs to be defined before tags and performers if title: # This value has already been fetch in start of function because it needs to be defined before tags and performers
title += postfix_styles.get('title')
if wrapper_styles.get('title'): if wrapper_styles.get('title'):
filename_parts.append(f"{wrapper_styles['title'][0]}{title}{wrapper_styles['title'][1]}") filename_parts.append(f"{wrapper_styles['title'][0]}{title}{wrapper_styles['title'][1]}")
else: else:
@@ -210,6 +233,7 @@ def form_filename(original_file_stem, scene_details, wrapper_styles):
if settings["performerAppend"]: if settings["performerAppend"]:
performers = '-'.join([performer.get('name', '') for performer in scene_details.get('performers', [])]) performers = '-'.join([performer.get('name', '') for performer in scene_details.get('performers', [])])
if performers: if performers:
performers += postfix_styles.get('performers')
if debugTracing: logger.info(f"Debug Tracing (include_keyField_if_in_name={include_keyField_if_in_name})................") if debugTracing: logger.info(f"Debug Tracing (include_keyField_if_in_name={include_keyField_if_in_name})................")
if include_keyField_if_in_name or performers.lower() not in title.lower(): if include_keyField_if_in_name or performers.lower() not in title.lower():
if debugTracing: logger.info(f"Debug Tracing (performers={performers})................") if debugTracing: logger.info(f"Debug Tracing (performers={performers})................")
@@ -221,15 +245,33 @@ def form_filename(original_file_stem, scene_details, wrapper_styles):
scene_date = scene_details.get('date', '') scene_date = scene_details.get('date', '')
if debugTracing: logger.info("Debug Tracing................") if debugTracing: logger.info("Debug Tracing................")
if scene_date: if scene_date:
scene_date += postfix_styles.get('date')
if debugTracing: logger.info("Debug Tracing................") if debugTracing: logger.info("Debug Tracing................")
if wrapper_styles.get('date'): if wrapper_styles.get('date'):
filename_parts.append(f"{wrapper_styles['date'][0]}{scene_date}{wrapper_styles['date'][1]}") filename_parts.append(f"{wrapper_styles['date'][0]}{scene_date}{wrapper_styles['date'][1]}")
else: else:
filename_parts.append(scene_date) filename_parts.append(scene_date)
elif key == 'resolution':
width = str(scene_details.get('files', [{}])[0].get('width', '')) # Convert width to string
height = str(scene_details.get('files', [{}])[0].get('height', '')) # Convert height to string
if width and height:
resolution = width + postfix_styles.get('width_height_seperator') + height + postfix_styles.get('resolution')
if wrapper_styles.get('resolution'):
filename_parts.append(f"{wrapper_styles['resolution'][0]}{resolution}{wrapper_styles['width'][1]}")
else:
filename_parts.append(resolution)
elif key == 'width':
width = str(scene_details.get('files', [{}])[0].get('width', '')) # Convert width to string
if width:
width += postfix_styles.get('width')
if wrapper_styles.get('width'):
filename_parts.append(f"{wrapper_styles['width'][0]}{width}{wrapper_styles['width'][1]}")
else:
filename_parts.append(width)
elif key == 'height': elif key == 'height':
height = str(scene_details.get('files', [{}])[0].get('height', '')) # Convert height to string height = str(scene_details.get('files', [{}])[0].get('height', '')) # Convert height to string
if height: if height:
height += 'p' height += postfix_styles.get('height')
if wrapper_styles.get('height'): if wrapper_styles.get('height'):
filename_parts.append(f"{wrapper_styles['height'][0]}{height}{wrapper_styles['height'][1]}") filename_parts.append(f"{wrapper_styles['height'][0]}{height}{wrapper_styles['height'][1]}")
else: else:
@@ -237,6 +279,7 @@ def form_filename(original_file_stem, scene_details, wrapper_styles):
elif key == 'video_codec': elif key == 'video_codec':
video_codec = scene_details.get('files', [{}])[0].get('video_codec', '').upper() # Convert to uppercase video_codec = scene_details.get('files', [{}])[0].get('video_codec', '').upper() # Convert to uppercase
if video_codec: if video_codec:
video_codec += postfix_styles.get('video_codec')
if wrapper_styles.get('video_codec'): if wrapper_styles.get('video_codec'):
filename_parts.append(f"{wrapper_styles['video_codec'][0]}{video_codec}{wrapper_styles['video_codec'][1]}") filename_parts.append(f"{wrapper_styles['video_codec'][0]}{video_codec}{wrapper_styles['video_codec'][1]}")
else: else:
@@ -244,6 +287,7 @@ def form_filename(original_file_stem, scene_details, wrapper_styles):
elif key == 'frame_rate': elif key == 'frame_rate':
frame_rate = str(scene_details.get('files', [{}])[0].get('frame_rate', '')) + 'FPS' # Convert to string and append ' FPS' frame_rate = str(scene_details.get('files', [{}])[0].get('frame_rate', '')) + 'FPS' # Convert to string and append ' FPS'
if frame_rate: if frame_rate:
frame_rate += postfix_styles.get('frame_rate')
if wrapper_styles.get('frame_rate'): if wrapper_styles.get('frame_rate'):
filename_parts.append(f"{wrapper_styles['frame_rate'][0]}{frame_rate}{wrapper_styles['frame_rate'][1]}") filename_parts.append(f"{wrapper_styles['frame_rate'][0]}{frame_rate}{wrapper_styles['frame_rate'][1]}")
else: else:
@@ -254,6 +298,7 @@ def form_filename(original_file_stem, scene_details, wrapper_styles):
for gallery_name in galleries: for gallery_name in galleries:
if debugTracing: logger.info(f"Debug Tracing (include_keyField_if_in_name={include_keyField_if_in_name}) (gallery_name={gallery_name})................") if debugTracing: logger.info(f"Debug Tracing (include_keyField_if_in_name={include_keyField_if_in_name}) (gallery_name={gallery_name})................")
if include_keyField_if_in_name or gallery_name.lower() not in title.lower(): if include_keyField_if_in_name or gallery_name.lower() not in title.lower():
gallery_name += postfix_styles.get('galleries')
if wrapper_styles.get('galleries'): if wrapper_styles.get('galleries'):
filename_parts.append(f"{wrapper_styles['galleries'][0]}{gallery_name}{wrapper_styles['galleries'][1]}") filename_parts.append(f"{wrapper_styles['galleries'][0]}{gallery_name}{wrapper_styles['galleries'][1]}")
if debugTracing: logger.info("Debug Tracing................") if debugTracing: logger.info("Debug Tracing................")
@@ -269,11 +314,11 @@ def form_filename(original_file_stem, scene_details, wrapper_styles):
for tag_name in tags: for tag_name in tags:
if debugTracing: logger.info(f"Debug Tracing (include_keyField_if_in_name={include_keyField_if_in_name}) (tag_name={tag_name})................") if debugTracing: logger.info(f"Debug Tracing (include_keyField_if_in_name={include_keyField_if_in_name}) (tag_name={tag_name})................")
if include_keyField_if_in_name or tag_name.lower() not in title.lower(): if include_keyField_if_in_name or tag_name.lower() not in title.lower():
add_tag(tag_name) add_tag(tag_name + postfix_styles.get('tag'))
if debugTracing: logger.info(f"Debug Tracing (tag_name={tag_name})................") if debugTracing: logger.info(f"Debug Tracing (tag_name={tag_name})................")
if debugTracing: logger.info("Debug Tracing................") if debugTracing: logger.info("Debug Tracing................")
if debugTracing: logger.info("Debug Tracing................") if debugTracing: logger.info(f"Debug Tracing (filename_parts={filename_parts})................")
new_filename = separator.join(filename_parts).replace(double_separator, separator) new_filename = separator.join(filename_parts).replace(double_separator, separator)
if debugTracing: logger.info(f"Debug Tracing (new_filename={new_filename})................") if debugTracing: logger.info(f"Debug Tracing (new_filename={new_filename})................")
@@ -293,6 +338,7 @@ def find_scene_by_id(scene_id):
date date
files { files {
path path
width
height height
video_codec video_codec
frame_rate frame_rate
@@ -316,6 +362,7 @@ def find_scene_by_id(scene_id):
return scene_result.get('data', {}).get('findScene') return scene_result.get('data', {}).get('findScene')
def move_or_rename_files(scene_details, new_filename, original_parent_directory): def move_or_rename_files(scene_details, new_filename, original_parent_directory):
global exitMsg
studio_directory = None studio_directory = None
for file_info in scene_details['files']: for file_info in scene_details['files']:
path = file_info['path'] path = file_info['path']
@@ -354,10 +401,12 @@ def move_or_rename_files(scene_details, new_filename, original_parent_directory)
except FileNotFoundError: except FileNotFoundError:
log.error(f"File not found: {path}. Skipping...") log.error(f"File not found: {path}. Skipping...")
logger.error(f"File not found: {path}. Skipping...") logger.error(f"File not found: {path}. Skipping...")
exitMsg = "File not found"
continue continue
except OSError as e: except OSError as e:
log.error(f"Failed to move or rename file: {path}. Error: {e}") log.error(f"Failed to move or rename file: {path}. Error: {e}")
logger.error(f"Failed to move or rename file: {path}. Error: {e}") logger.error(f"Failed to move or rename file: {path}. Error: {e}")
exitMsg = "Failed to move or rename file"
continue continue
return new_path # Return the new_path variable after the loop return new_path # Return the new_path variable after the loop
@@ -374,7 +423,8 @@ def perform_metadata_scan(metadata_scan_path):
logger.info(f"Mutation string: {mutation_metadata_scan}") logger.info(f"Mutation string: {mutation_metadata_scan}")
graphql_request(mutation_metadata_scan) graphql_request(mutation_metadata_scan)
def rename_scene(scene_id, wrapper_styles, stash_directory): def rename_scene(scene_id, stash_directory):
global exitMsg
scene_details = find_scene_by_id(scene_id) scene_details = find_scene_by_id(scene_id)
if debugTracing: logger.info(f"Debug Tracing (scene_details={scene_details})................") if debugTracing: logger.info(f"Debug Tracing (scene_details={scene_details})................")
if not scene_details: if not scene_details:
@@ -401,7 +451,7 @@ def rename_scene(scene_id, wrapper_styles, stash_directory):
original_file_stem = Path(original_file_path).stem original_file_stem = Path(original_file_path).stem
original_file_name = Path(original_file_path).name original_file_name = Path(original_file_path).name
new_filename = form_filename(original_file_stem, scene_details, wrapper_styles) new_filename = form_filename(original_file_stem, scene_details)
newFilenameWithExt = new_filename + Path(original_file_path).suffix newFilenameWithExt = new_filename + Path(original_file_path).suffix
if debugTracing: logger.info(f"Debug Tracing (original_file_name={original_file_name})(newFilenameWithExt={newFilenameWithExt})................") if debugTracing: logger.info(f"Debug Tracing (original_file_name={original_file_name})(newFilenameWithExt={newFilenameWithExt})................")
if original_file_name == newFilenameWithExt: if original_file_name == newFilenameWithExt:
@@ -429,6 +479,7 @@ def rename_scene(scene_id, wrapper_styles, stash_directory):
os.rename(original_file_path, new_file_path) os.rename(original_file_path, new_file_path)
logger.info(f"{dry_run_prefix}Renamed file: {original_file_path} -> {new_file_path}") logger.info(f"{dry_run_prefix}Renamed file: {original_file_path} -> {new_file_path}")
except Exception as e: except Exception as e:
exitMsg = "Failed to rename file"
log.error(f"Failed to rename file: {original_file_path}. Error: {e}") log.error(f"Failed to rename file: {original_file_path}. Error: {e}")
logger.error(f"Failed to rename file: {original_file_path}. Error: {e}") logger.error(f"Failed to rename file: {original_file_path}. Error: {e}")
@@ -443,10 +494,12 @@ def rename_scene(scene_id, wrapper_styles, stash_directory):
hash_suffix = hashlib.md5(new_filename.encode()).hexdigest() hash_suffix = hashlib.md5(new_filename.encode()).hexdigest()
new_filename = truncated_filename + '_' + hash_suffix + Path(original_file_path).suffix new_filename = truncated_filename + '_' + hash_suffix + Path(original_file_path).suffix
if debugTracing: logger.info(f"Debug Tracing (exitMsg={exitMsg})................")
return new_filename, original_path_info, new_path_info return new_filename, original_path_info, new_path_info
# Main default function for rename scene # Main default function for rename scene
def rename_files_task(): def rename_files_task():
global exitMsg
if debugTracing: logger.info("Debug Tracing................") if debugTracing: logger.info("Debug Tracing................")
# Execute the GraphQL query to fetch all scenes # Execute the GraphQL query to fetch all scenes
scene_result = graphql_request(query_all_scenes) scene_result = graphql_request(query_all_scenes)
@@ -466,10 +519,6 @@ def rename_files_task():
# Extract the ID of the latest scene # Extract the ID of the latest scene
latest_scene_id = latest_scene.get('id') latest_scene_id = latest_scene.get('id')
# Extract wrapper styles
wrapper_styles = config["wrapper_styles"]
# Read stash directory from renamefile_settings.py # Read stash directory from renamefile_settings.py
stash_directory = config.get('stash_directory', '') stash_directory = config.get('stash_directory', '')
if debugTracing: logger.info("Debug Tracing................") if debugTracing: logger.info("Debug Tracing................")
@@ -477,8 +526,8 @@ def rename_files_task():
if debugTracing: logger.info("Debug Tracing................") if debugTracing: logger.info("Debug Tracing................")
# Rename the latest scene and trigger metadata scan # Rename the latest scene and trigger metadata scan
new_filename = rename_scene(latest_scene_id, wrapper_styles, stash_directory) new_filename = rename_scene(latest_scene_id, stash_directory)
if debugTracing: logger.info("Debug Tracing................") if debugTracing: logger.info(f"Debug Tracing (exitMsg={exitMsg})................")
# Log dry run state and indicate if no changes were made # Log dry run state and indicate if no changes were made
if dry_run: if dry_run:
@@ -487,7 +536,7 @@ def rename_files_task():
elif not new_filename: elif not new_filename:
logger.info("No changes were made.") logger.info("No changes were made.")
else: else:
logger.info("Change success!") logger.info(f"{exitMsg}")
return return
def fetch_dup_filename_tags(): # Place holder for new implementation def fetch_dup_filename_tags(): # Place holder for new implementation
@@ -497,7 +546,7 @@ if PLUGIN_ARGS == "fetch_dup_filename_tags":
fetch_dup_filename_tags() fetch_dup_filename_tags()
elif PLUGIN_ARGS == "rename_files_task": elif PLUGIN_ARGS == "rename_files_task":
rename_files_task() rename_files_task()
else: elif inputToUpdateScenePost:
rename_files_task() rename_files_task()
if debugTracing: logger.info("\n*********************************\nEXITING ***********************\n*********************************") if debugTracing: logger.info("\n*********************************\nEXITING ***********************\n*********************************")

View File

@@ -1,6 +1,7 @@
name: RenameFile name: RenameFile
description: Renames video (scene) file names when the user edits the [Title] field located in the scene [Edit] tab. description: Renames video (scene) file names when the user edits the [Title] field located in the scene [Edit] tab.
version: 0.3.0 # By David Maisonave (aka Axter) 2024
version: 0.4.0
url: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/RenameFile url: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/RenameFile
settings: settings:
performerAppend: performerAppend:
@@ -25,7 +26,7 @@ settings:
type: BOOLEAN type: BOOLEAN
zfieldKeyList: zfieldKeyList:
displayName: Key Fields displayName: Key Fields
description: '(Default=title,performers,studio,tags) Define key fields to use to format the file name. This is a comma seperated list, and the list should be in the desired format order. For example, if the user wants the performers name before the title, set the performers name first. Example:"performers,title,tags". This is an example of user adding height:"title,performers,tags,height" Here''s an example using all of the supported fields: "title,performers,tags,studio,galleries,date,height,video_codec,frame_rate".' description: '(Default=title,performers,studio,tags) Define key fields to use to format the file name. This is a comma seperated list, and the list should be in the desired format order. For example, if the user wants the performers name before the title, set the performers name first. Example:"performers,title,tags". This is an example of user adding height:"title,performers,tags,height" Here''s an example using all of the supported fields: "title,performers,tags,studio,galleries,resolution,width,height,video_codec,frame_rate,date".'
type: STRING type: STRING
zgraphqlEndpoint: zgraphqlEndpoint:
displayName: GraphQL Endpoint displayName: GraphQL Endpoint

View File

@@ -1,4 +1,4 @@
# Importing config dictionary # By David Maisonave (aka Axter) 2024
# RenameFile plugin main configuration options are available on the Stash GUI under Settings->Plugins->Plugins->[RenameFile]. # RenameFile plugin main configuration options are available on the Stash GUI under Settings->Plugins->Plugins->[RenameFile].
# Most users should only use the GUI options. # Most users should only use the GUI options.
# The configuration options in this file are for advanced users ONLY!!! # The configuration options in this file are for advanced users ONLY!!!
@@ -9,16 +9,33 @@
config = { config = {
# Define wrapper styles for different parts of the filename. # Define wrapper styles for different parts of the filename.
# Use '[]' for square brackets, '{}' for curly brackets, '()' for parentheses, or an empty string for None. # Use '[]' for square brackets, '{}' for curly brackets, '()' for parentheses, or an empty string for None.
"wrapper_styles": { "wrapper_styles": { # Modify these values to change how each part of the filename is wrapped.
"studio": '{}', # Modify these values to change how each part of the filename is wrapped. "title": '',
"title": '', # Use '[]' for square brackets, '{}' for curly brackets, '()' for parentheses, or an empty string for None.
"performers": '()', "performers": '()',
"tag": '[]',
"studio": '{}',
"galleries": '()', "galleries": '()',
"date": '()', "resolution": '', # Contains both WITH and HEIGHT
"width": '',
"height": '', "height": '',
"video_codec": '', "video_codec": '',
"frame_rate": '', "frame_rate": '',
"tag": '[]' "date": '()', # This field is not populated in the DB by default. It's usually empty.
},
# Define the field postfix
"postfix_styles": {
"title": '',
"performers": '',
"tag": '',
"studio": '',
"galleries": '',
"resolution": 'P', # Contains both WITH and HEIGHT
"width": 'W',
"height": 'P',
"width_height_seperator": 'x', # Used in RESOLUTION field as the string seperating WITH and HEIGHT. Example: 720x480 or 1280X720
"video_codec": '',
"frame_rate": 'FR',
"date": '',
}, },
# Define whether files should be renamed when moved # Define whether files should be renamed when moved
"rename_files": True, "rename_files": True,