-
-
Save robertguetzkow/8dacd4b565538d657b72efcaf0afe07e to your computer and use it in GitHub Desktop.
bl_info = { | |
"name": "Auto-Filepath", | |
"author": "Robert Guetzkow", | |
"version": (1, 0, 2), | |
"blender": (2, 80, 0), | |
"location": "Output Properties > Auto-filepath", | |
"description": "Automatically sets a unique filepath for each frame based on the current timestamp.", | |
"warning": "", | |
"wiki_url": "", | |
"category": "Render"} | |
import bpy | |
import datetime | |
from pathlib import Path | |
from bpy.app.handlers import persistent | |
@persistent | |
def update_filepath(self): | |
if not bpy.context.scene.auto_filepath_settings.use_auto_filepath: | |
return | |
now = datetime.datetime.now() | |
path = Path(bpy.context.scene.auto_filepath_settings.directory) | |
base_path = path / now.strftime('%Y-%m-%d_%H_%M_%S_%f') | |
bpy.context.scene.render.filepath = str(base_path / "####") | |
bpy.context.scene.render.use_file_extension = True | |
bpy.context.scene.render.use_overwrite = False | |
if bpy.context.scene.use_nodes: | |
for node in bpy.context.scene.node_tree.nodes: | |
if node.type == "OUTPUT_FILE": | |
node.base_path = str(base_path / node.name) | |
def set_directory(self, value): | |
path = Path(value) | |
if path.is_dir(): | |
self["directory"] = value | |
def get_directory(self): | |
return self.get("directory", bpy.context.scene.auto_filepath_settings.bl_rna.properties["directory"].default) | |
class AutoFilepathSettings(bpy.types.PropertyGroup): | |
use_auto_filepath: bpy.props.BoolProperty(name="Automatic filepath generation.", | |
description="Enable/disable automatic filepath generation. When enabled, " | |
"this will overwrite the output path and the base path of " | |
"all File Output nodes.", | |
default=False) | |
directory: bpy.props.StringProperty(name="Directory", | |
description="Directory where files shall be stored.", | |
default="/", | |
maxlen=4096, | |
subtype="DIR_PATH", | |
set=set_directory, | |
get=get_directory) | |
class AUTOFILEPATH_PT_panel(bpy.types.Panel): | |
bl_label = "Auto-Filepath" | |
bl_space_type = "PROPERTIES" | |
bl_region_type = "WINDOW" | |
bl_context = "output" | |
bl_options = {"DEFAULT_CLOSED"} | |
def draw_header(self, context): | |
self.layout.prop(context.scene.auto_filepath_settings, "use_auto_filepath", text="") | |
def draw(self, context): | |
layout = self.layout | |
layout.prop(context.scene.auto_filepath_settings, "directory") | |
classes = (AutoFilepathSettings, AUTOFILEPATH_PT_panel) | |
def register(): | |
for cls in classes: | |
bpy.utils.register_class(cls) | |
bpy.types.Scene.auto_filepath_settings = bpy.props.PointerProperty(type=AutoFilepathSettings) | |
if update_filepath not in bpy.app.handlers.render_pre: | |
bpy.app.handlers.render_pre.append(update_filepath) | |
def unregister(): | |
for cls in classes: | |
bpy.utils.unregister_class(cls) | |
del bpy.types.Scene.auto_filepath_settings | |
if update_filepath in bpy.app.handlers.render_pre: | |
bpy.app.handlers.render_pre.remove(update_filepath) | |
if __name__ == "__main__": | |
register() |
@mhe500 I've updated the script.
@robertguetzkow I think it also needs to be added to set_directory (I should have been clearer). Else, the addon will not allow you to save a path using the "//" notation. After installing, the first thing I did was try to set the output path to "//render/" and noticed the setting wouldn't stick because Path.is_dir returns false at L37. Thanks again.
Right, I missed that. I will take a look when I have time. Until then I've reverted it back to the original version.
Here is my take on this. Rather than generating new folder for each image I just save images with timestamps in filename.
https://gist.github.com/QuietNoise/c56c5f4bf1e42238928bf2cef481cde0
@QuietNoise I think I originally wrote this for animations, hence the separate folder. Thanks for leaving a link to the adapted version, might be useful for someone looking at the old question on Stack Exchange.
Thank you for sharing this. It is very helpful.
I might suggest applying bpy.path.abspath before creating a Path object to handle cases using the Blender "//" convention.