I’m trying to make an addon for Blender 4.1 that adds current seconds to rendered file name. We need it for our workflow when rendering overnight, to check later which frames had the biggest gap between seconds. We’ve tried other solutions and settled for this one.
Addon works perfectly in the main output filepath, in the Output Properties.
- I specify output path - “//@second@_”
- When I hit render, addon stores the original filepath “//@second@_”
- Addon replaces @second@ with the current second
- Blender render out an image with a file name “41_0001.png”
- Addon restores the original filepath “//@second@_”
The problem is when I need to render other passes (alpha) in the Compositor’s File Output node.
- I specify output path - “//@second@”
- When I hit render, I can see in Blender UI that the addon replaces @second@ with the current second
- Blender creates a folder named “@second@”
- Addon restores the original filepath “//@second@”
For the actual pass (alpha) files:
- In the Compositor’s sidebar in the File Subpath I specify output path - “//@second@alpha”
- When I hit render, I can see in Blender UI that the addon replaces @second@ with the current second
- In the folder named “@second@” Blender creates a file named “@second@_alpha_0001.png”
- Addon restores the original filepath “//@second@alpha”
What I need the structure to look like:
Either:
folder named “41” with files “41_0001.png”, “43_0002.png”, “45_0003.png”
Or:
folder named “41” with file “41_0001.png”
folder named “43” with file “43_0002.png”
folder named “45” with file “45_0003.png”
Can someone help me please?
My code here:
import bpy
from bpy.app.handlers import persistent
from datetime import datetime
# Store the original file paths
original_file_paths = {
"render_file_path": "",
"node_base_paths": {},
"file_slot_paths": {}
}
@persistent
def replace_seconds(scene):
global original_file_paths
# Store the original render file path with @second@
original_file_paths["render_file_path"] = scene.render.filepath
# Get the project name from the .blend file name
project_name = bpy.path.basename(bpy.data.filepath).rpartition('.')[0]
# Get the current date and time
from datetime import datetime
now = datetime.now()
# Replace @second@ in the render file path
scene.render.filepath = scene.render.filepath.replace("@second@", str(now.second).zfill(2))
# Replace @second@ for Compositor File Output nodes
for node in scene.node_tree.nodes:
if node.type == 'OUTPUT_FILE':
# Store the original node base path
original_file_paths["node_base_paths"][node.name] = node.base_path
# Replace @second@ in the node base path
node.base_path = node.base_path.replace("@second@", str(now.second).zfill(2))
for file_slot in node.file_slots:
# Store the original file slot path with the modified path as the key
original_file_paths["file_slot_paths"][file_slot.path.replace("@second@", str(now.second).zfill(2))] = file_slot.path
# Replace @second@ in the file slot path
file_slot.path = file_slot.path.replace("@second@", str(now.second).zfill(2))
@persistent
def restore_seconds(scene):
global original_file_paths
# Restore the original render file path with @second@
scene.render.filepath = original_file_paths["render_file_path"]
# Restore the original node base paths with @second@
for node in bpy.context.scene.node_tree.nodes:
if node.type == 'OUTPUT_FILE':
node.base_path = original_file_paths["node_base_paths"].get(node.name, "")
# Restore the original file slot paths with @second@
for file_slot in node.file_slots:
# Use the modified path to get the original path
file_slot.path = original_file_paths["file_slot_paths"].get(file_slot.path, "")
def register():
# Make sure to clear any existing handlers to avoid duplicates
bpy.app.handlers.render_pre.clear()
bpy.app.handlers.render_post.clear()
# Register the handlers to trigger before and after each frame
bpy.app.handlers.render_pre.append(replace_seconds)
bpy.app.handlers.render_post.append(restore_seconds)
def unregister():
bpy.app.handlers.render_pre.remove(replace_seconds)
bpy.app.handlers.render_post.remove(restore_seconds)
if __name__ == "__main__":
register()