Skip to content

Commit f7e98f7

Browse files
authored
Create blend_backup_addon.py
1 parent 2b92d84 commit f7e98f7

File tree

1 file changed

+85
-0
lines changed

1 file changed

+85
-0
lines changed

blend_backup_addon.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
bl_info = {
2+
"name": "Save .blend Backup",
3+
"author": "RimuruDev",
4+
"version": (1, 2),
5+
"blender": (4, 4, 0),
6+
"location": "File > Save Backup",
7+
"description": "Creates *_backup.blend next to current file and disables .blend1",
8+
"category": "System",
9+
}
10+
11+
import bpy, os, shutil
12+
from bpy.app.handlers import persistent
13+
14+
@persistent
15+
def save_post_handler(_):
16+
path = bpy.data.filepath
17+
if not path:
18+
return
19+
d, n = os.path.dirname(path), os.path.splitext(os.path.basename(path))[0]
20+
shutil.copy2(path, os.path.join(d, f"{n}_backup.blend"))
21+
22+
def manual_backup():
23+
path = bpy.data.filepath
24+
if not path:
25+
bpy.ops.wm.save_mainfile('INVOKE_DEFAULT')
26+
return
27+
d, n = os.path.dirname(path), os.path.splitext(os.path.basename(path))[0]
28+
shutil.copy2(path, os.path.join(d, f"{n}_backup.blend"))
29+
30+
class OT_SaveBackup(bpy.types.Operator):
31+
bl_idname = "wm.save_blend_backup"
32+
bl_label = "Save Backup (.blend)"
33+
bl_description = "Copy current .blend to *_backup.blend"
34+
35+
def execute(self, _):
36+
manual_backup()
37+
return {'FINISHED'}
38+
39+
class OT_SaveAndBackup(bpy.types.Operator):
40+
bl_idname = "wm.save_and_backup"
41+
bl_label = "Save and Backup (.blend)"
42+
bl_description = "Save file and make *_backup.blend"
43+
44+
def execute(self, _):
45+
if not bpy.data.filepath:
46+
bpy.ops.wm.save_mainfile('INVOKE_DEFAULT')
47+
return {'CANCELLED'}
48+
bpy.ops.wm.save_mainfile()
49+
manual_backup()
50+
return {'FINISHED'}
51+
52+
def draw_menu(self, _):
53+
self.layout.operator(OT_SaveBackup.bl_idname, icon='FILE_BACKUP')
54+
55+
addon_keymaps = []
56+
57+
def register():
58+
bpy.utils.register_class(OT_SaveBackup)
59+
bpy.utils.register_class(OT_SaveAndBackup)
60+
bpy.types.TOPBAR_MT_file.append(draw_menu)
61+
bpy.context.preferences.filepaths.save_version = 0
62+
if save_post_handler not in bpy.app.handlers.save_post:
63+
bpy.app.handlers.save_post.append(save_post_handler)
64+
65+
kc = bpy.context.window_manager.keyconfigs.addon
66+
if kc:
67+
km = kc.keymaps.new(name='Window', space_type='EMPTY')
68+
addon_keymaps.extend([
69+
(km, km.keymap_items.new("wm.save_blend_backup", type='B', value='PRESS', ctrl=True, alt=True)),
70+
(km, km.keymap_items.new("wm.save_and_backup", type='S', value='PRESS', ctrl=True, shift=True)),
71+
(km, km.keymap_items.new("wm.save_and_backup", type='S', value='PRESS', oskey=True)),
72+
])
73+
74+
def unregister():
75+
bpy.types.TOPBAR_MT_file.remove(draw_menu)
76+
bpy.utils.unregister_class(OT_SaveBackup)
77+
bpy.utils.unregister_class(OT_SaveAndBackup)
78+
if save_post_handler in bpy.app.handlers.save_post:
79+
bpy.app.handlers.save_post.remove(save_post_handler)
80+
for km, kmi in addon_keymaps:
81+
km.keymap_items.remove(kmi)
82+
addon_keymaps.clear()
83+
84+
if __name__ == "__main__":
85+
register()

0 commit comments

Comments
 (0)