167 lines
5.4 KiB
Python
167 lines
5.4 KiB
Python
import bpy
|
|
import os
|
|
from pathlib import Path
|
|
import re
|
|
from time import time
|
|
|
|
|
|
def renumber_sequence_on_disk_from_file_slots(apply=True, active_scene_only=False):
|
|
'''renumber sequence on disk from scenes file slots'''
|
|
|
|
scn = bpy.context.scene
|
|
blend = Path(bpy.data.filepath)
|
|
render = blend.parent / 'render'
|
|
|
|
prenum = re.compile(r'\d{3}_')
|
|
|
|
print('-- starting rename sequences numbers from fileslots number')
|
|
if not apply:
|
|
print('-- Dry run')
|
|
|
|
t0 = time()
|
|
ct = 0
|
|
|
|
if active_scene_only:
|
|
# Only on currrent scene
|
|
file_outs = [n for n in bpy.context.scene.node_tree.nodes if n.type == 'OUTPUT_FILE' and n.name.startswith('OUT_') and not n.mute]
|
|
else:
|
|
# multi scene check:
|
|
file_outs = []
|
|
for S in bpy.data.scenes:
|
|
if S.name == 'Scene' or not S.node_tree or not S.use_nodes:
|
|
continue
|
|
file_outs += [n for n in S.node_tree.nodes if n.type == 'OUTPUT_FILE' and n.name.startswith('OUT_') and not n.mute]
|
|
|
|
|
|
if not file_outs:
|
|
return 'No file output found (should be unmuted nodes with name starting with OUT_)', '_'
|
|
|
|
for fo in file_outs:
|
|
obj_full = fo.base_path.split('/')[-1]
|
|
obj = prenum.sub('', obj_full)
|
|
obj_num = prenum.search(obj_full)
|
|
if obj_num:
|
|
obj_num = obj_num.group(0)
|
|
|
|
## check if folder exists
|
|
folder_path = None
|
|
|
|
for d in os.scandir(render):
|
|
if d.is_dir() and prenum.sub('', d.name) == obj:
|
|
folder_path = render / d.name
|
|
break
|
|
|
|
if not folder_path:
|
|
print(f'Could not find obj folder for: {obj}')
|
|
continue
|
|
|
|
# rename inside folder dirst so that root path isn't changed while iterating
|
|
for fs in fo.file_slots:
|
|
img_full = fs.path.split('/')[0]
|
|
img = prenum.sub('', img_full)
|
|
img_num = prenum.search(img_full)
|
|
if img_num:
|
|
img_num = img_num.group(0)
|
|
else:
|
|
print(f'! no num : {fo.base_path} : {img_full}')
|
|
continue # If no img_num no point in renaming sequences
|
|
|
|
img_dir_path = None
|
|
|
|
for img_dir in os.scandir(folder_path):
|
|
if img_dir.is_dir() and prenum.sub('', img_dir.name) == img:
|
|
img_dir_path = folder_path / img_dir.name
|
|
break
|
|
|
|
if not img_dir_path:
|
|
print(f'Could not find img folder for: {img}')
|
|
continue
|
|
|
|
# if folder exists check if full name is ok
|
|
if img_full == img_dir_path.name:
|
|
continue # name already (maybe not in sequence but should be good)
|
|
|
|
|
|
# rename sequence and image folder
|
|
for frame in os.scandir(img_dir_path):
|
|
good = img_num + prenum.sub('', frame.name)
|
|
if frame.name != good:
|
|
print(f' img: {frame.name} > {good}')
|
|
ct += 1
|
|
if apply:
|
|
fp = Path(frame.path)
|
|
fp.rename(fp.parent / good)
|
|
|
|
# rename image folder
|
|
if img_dir_path.name != img_full:
|
|
print(f' dir:{img_dir_path.name} > {img_full}')
|
|
ct += 1
|
|
if apply:
|
|
img_dir_path.rename(img_dir_path.parent / img_full)
|
|
|
|
# rename object folder
|
|
if obj_num and folder_path.name != obj_full:
|
|
print(f'obj: {folder_path.name} > {obj_full}')
|
|
ct += 1
|
|
if apply:
|
|
folder_path.rename(folder_path.parent / obj_full)
|
|
|
|
elapsed = f'{time() - t0:.2f}s'
|
|
print(f'Eslapsed time: {elapsed}')
|
|
return ct, elapsed
|
|
|
|
|
|
class GPEXP_OT_renumber_files_on_disk(bpy.types.Operator):
|
|
bl_idname = "gp.renumber_files_on_disk"
|
|
bl_label = "Renumber Files On Disk"
|
|
bl_description = "Rename folder/files in render folder on disk according to unmuted file output numbering"
|
|
bl_options = {"REGISTER"}
|
|
|
|
def invoke(self, context, event):
|
|
# return self.execute(context)
|
|
return context.window_manager.invoke_props_dialog(self)
|
|
|
|
dry_run: bpy.props.BoolProperty(name='Dry-run (no actions, prints in console only)',
|
|
default=False,
|
|
description='Test mode. If checked, no action is actually performed')
|
|
|
|
active_scene_only: bpy.props.BoolProperty(name='Only Active Scene',
|
|
default=False,
|
|
description='use only file output of active scene instead of all scenes (skipping "Scene")')
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.prop(self, 'dry_run')
|
|
layout.prop(self, 'active_scene_only')
|
|
|
|
def execute(self, context):
|
|
ct, timing = renumber_sequence_on_disk_from_file_slots(apply = not self.dry_run, active_scene_only=self.active_scene_only)
|
|
if isinstance(ct, str):
|
|
self.report({'ERROR'}, ct)
|
|
return {"CANCELLED"}
|
|
|
|
if not ct:
|
|
self.report({'WARNING'}, 'Already good or nothing to rename')
|
|
return {"CANCELLED"}
|
|
|
|
if self.dry_run:
|
|
mess = f'Dry run : {ct} items would have been renamed, see console'
|
|
else:
|
|
mess = f'{ct} items renamed in {timing}'
|
|
|
|
self.report({'INFO'}, mess)
|
|
return {"FINISHED"}
|
|
|
|
|
|
|
|
classes=(
|
|
GPEXP_OT_renumber_files_on_disk,
|
|
)
|
|
|
|
def register():
|
|
for cls in classes:
|
|
bpy.utils.register_class(cls)
|
|
|
|
def unregister():
|
|
for cls in reversed(classes):
|
|
bpy.utils.unregister_class(cls) |