file.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. # ##### BEGIN GPL LICENSE BLOCK #####
  2. #
  3. # This program is free software; you can redistribute it and/or
  4. # modify it under the terms of the GNU General Public License
  5. # as published by the Free Software Foundation; either version 2
  6. # of the License, or (at your option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program; if not, write to the Free Software Foundation,
  15. # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  16. #
  17. # ##### END GPL LICENSE BLOCK #####
  18. # <pep8 compliant>
  19. import bpy
  20. from bpy.types import Operator
  21. from bpy.props import (
  22. BoolProperty,
  23. CollectionProperty,
  24. StringProperty,
  25. )
  26. # ########## Datablock previews... ##########
  27. class WM_OT_previews_batch_generate(Operator):
  28. """Generate selected .blend file's previews"""
  29. bl_idname = "wm.previews_batch_generate"
  30. bl_label = "Batch-Generate Previews"
  31. bl_options = {'REGISTER'}
  32. # -----------
  33. # File props.
  34. files: CollectionProperty(
  35. type=bpy.types.OperatorFileListElement,
  36. options={'HIDDEN', 'SKIP_SAVE'},
  37. )
  38. directory: StringProperty(
  39. maxlen=1024,
  40. subtype='FILE_PATH',
  41. options={'HIDDEN', 'SKIP_SAVE'},
  42. )
  43. # Show only images/videos, and directories!
  44. filter_blender: BoolProperty(
  45. default=True,
  46. options={'HIDDEN', 'SKIP_SAVE'},
  47. )
  48. filter_folder: BoolProperty(
  49. default=True,
  50. options={'HIDDEN', 'SKIP_SAVE'},
  51. )
  52. # -----------
  53. # Own props.
  54. use_scenes: BoolProperty(
  55. default=True,
  56. name="Scenes",
  57. description="Generate scenes' previews",
  58. )
  59. use_collections: BoolProperty(
  60. default=True,
  61. name="Collections",
  62. description="Generate collections' previews",
  63. )
  64. use_objects: BoolProperty(
  65. default=True,
  66. name="Objects",
  67. description="Generate objects' previews",
  68. )
  69. use_intern_data: BoolProperty(
  70. default=True,
  71. name="Mat/Tex/...",
  72. description="Generate 'internal' previews (materials, textures, images, etc.)",
  73. )
  74. use_trusted: BoolProperty(
  75. default=False,
  76. name="Trusted Blend Files",
  77. description="Enable python evaluation for selected files",
  78. )
  79. use_backups: BoolProperty(
  80. default=True,
  81. name="Save Backups",
  82. description="Keep a backup (.blend1) version of the files when saving with generated previews",
  83. )
  84. def invoke(self, context, _event):
  85. context.window_manager.fileselect_add(self)
  86. return {'RUNNING_MODAL'}
  87. def execute(self, context):
  88. import os
  89. import subprocess
  90. from bl_previews_utils import bl_previews_render as preview_render
  91. context.window_manager.progress_begin(0, len(self.files))
  92. context.window_manager.progress_update(0)
  93. for i, fn in enumerate(self.files):
  94. blen_path = os.path.join(self.directory, fn.name)
  95. cmd = [
  96. bpy.app.binary_path,
  97. "--background",
  98. "--factory-startup",
  99. "-noaudio",
  100. ]
  101. if self.use_trusted:
  102. cmd.append("--enable-autoexec")
  103. cmd.extend([
  104. blen_path,
  105. "--python",
  106. os.path.join(os.path.dirname(preview_render.__file__), "bl_previews_render.py"),
  107. "--",
  108. ])
  109. if not self.use_scenes:
  110. cmd.append('--no_scenes')
  111. if not self.use_collections:
  112. cmd.append('--no_collections')
  113. if not self.use_objects:
  114. cmd.append('--no_objects')
  115. if not self.use_intern_data:
  116. cmd.append('--no_data_intern')
  117. if not self.use_backups:
  118. cmd.append("--no_backups")
  119. if subprocess.call(cmd):
  120. self.report({'ERROR'}, "Previews generation process failed for file '%s'!" % blen_path)
  121. context.window_manager.progress_end()
  122. return {'CANCELLED'}
  123. context.window_manager.progress_update(i + 1)
  124. context.window_manager.progress_end()
  125. return {'FINISHED'}
  126. class WM_OT_previews_batch_clear(Operator):
  127. """Clear selected .blend file's previews"""
  128. bl_idname = "wm.previews_batch_clear"
  129. bl_label = "Batch-Clear Previews"
  130. bl_options = {'REGISTER'}
  131. # -----------
  132. # File props.
  133. files: CollectionProperty(
  134. type=bpy.types.OperatorFileListElement,
  135. options={'HIDDEN', 'SKIP_SAVE'},
  136. )
  137. directory: StringProperty(
  138. maxlen=1024,
  139. subtype='FILE_PATH',
  140. options={'HIDDEN', 'SKIP_SAVE'},
  141. )
  142. # Show only images/videos, and directories!
  143. filter_blender: BoolProperty(
  144. default=True,
  145. options={'HIDDEN', 'SKIP_SAVE'},
  146. )
  147. filter_folder: BoolProperty(
  148. default=True,
  149. options={'HIDDEN', 'SKIP_SAVE'},
  150. )
  151. # -----------
  152. # Own props.
  153. use_scenes: BoolProperty(
  154. default=True,
  155. name="Scenes",
  156. description="Clear scenes' previews",
  157. )
  158. use_collections: BoolProperty(
  159. default=True,
  160. name="Collections",
  161. description="Clear collections' previews",
  162. )
  163. use_objects: BoolProperty(
  164. default=True,
  165. name="Objects",
  166. description="Clear objects' previews",
  167. )
  168. use_intern_data: BoolProperty(
  169. default=True,
  170. name="Mat/Tex/...",
  171. description="Clear 'internal' previews (materials, textures, images, etc.)",
  172. )
  173. use_trusted: BoolProperty(
  174. default=False,
  175. name="Trusted Blend Files",
  176. description="Enable python evaluation for selected files",
  177. )
  178. use_backups: BoolProperty(
  179. default=True,
  180. name="Save Backups",
  181. description="Keep a backup (.blend1) version of the files when saving with cleared previews",
  182. )
  183. def invoke(self, context, _event):
  184. context.window_manager.fileselect_add(self)
  185. return {'RUNNING_MODAL'}
  186. def execute(self, context):
  187. import os
  188. import subprocess
  189. from bl_previews_utils import bl_previews_render as preview_render
  190. context.window_manager.progress_begin(0, len(self.files))
  191. context.window_manager.progress_update(0)
  192. for i, fn in enumerate(self.files):
  193. blen_path = os.path.join(self.directory, fn.name)
  194. cmd = [
  195. bpy.app.binary_path,
  196. "--background",
  197. "--factory-startup",
  198. "-noaudio",
  199. ]
  200. if self.use_trusted:
  201. cmd.append("--enable-autoexec")
  202. cmd.extend([
  203. blen_path,
  204. "--python",
  205. os.path.join(os.path.dirname(preview_render.__file__), "bl_previews_render.py"),
  206. "--",
  207. "--clear",
  208. ])
  209. if not self.use_scenes:
  210. cmd.append('--no_scenes')
  211. if not self.use_collections:
  212. cmd.append('--no_collections')
  213. if not self.use_objects:
  214. cmd.append('--no_objects')
  215. if not self.use_intern_data:
  216. cmd.append('--no_data_intern')
  217. if not self.use_backups:
  218. cmd.append("--no_backups")
  219. if subprocess.call(cmd):
  220. self.report({'ERROR'}, "Previews clear process failed for file '%s'!" % blen_path)
  221. context.window_manager.progress_end()
  222. return {'CANCELLED'}
  223. context.window_manager.progress_update(i + 1)
  224. context.window_manager.progress_end()
  225. return {'FINISHED'}
  226. class WM_OT_blend_strings_utf8_validate(Operator):
  227. """Check and fix all strings in current .blend file to be valid UTF-8 Unicode """ \
  228. """(needed for some old, 2.4x area files)"""
  229. bl_idname = "wm.blend_strings_utf8_validate"
  230. bl_label = "Validate .blend strings"
  231. bl_options = {'REGISTER'}
  232. def validate_strings(self, item, done_items):
  233. if item is None:
  234. return False
  235. if item in done_items:
  236. return False
  237. done_items.add(item)
  238. if getattr(item, "library", None) is not None:
  239. return False # No point in checking library data, we cannot fix it anyway...
  240. changed = False
  241. for prop in item.bl_rna.properties:
  242. if prop.identifier in {"bl_rna", "rna_type"}:
  243. continue # Or we'd recurse 'till Hell freezes.
  244. if prop.is_readonly:
  245. continue
  246. if prop.type == 'STRING':
  247. val_bytes = item.path_resolve(prop.identifier, False).as_bytes()
  248. val_utf8 = val_bytes.decode('utf-8', 'replace')
  249. val_bytes_valid = val_utf8.encode('utf-8')
  250. if val_bytes_valid != val_bytes:
  251. print("found bad utf8 encoded string %r, fixing to %r (%r)..."
  252. "" % (val_bytes, val_bytes_valid, val_utf8))
  253. setattr(item, prop.identifier, val_utf8)
  254. changed = True
  255. elif prop.type == 'POINTER':
  256. it = getattr(item, prop.identifier)
  257. changed |= self.validate_strings(it, done_items)
  258. elif prop.type == 'COLLECTION':
  259. for it in getattr(item, prop.identifier):
  260. changed |= self.validate_strings(it, done_items)
  261. return changed
  262. def execute(self, _context):
  263. changed = False
  264. done_items = set()
  265. for prop in bpy.data.bl_rna.properties:
  266. if prop.type == 'COLLECTION':
  267. for it in getattr(bpy.data, prop.identifier):
  268. changed |= self.validate_strings(it, done_items)
  269. if changed:
  270. self.report({'WARNING'},
  271. "Some strings were fixed, don't forget to save the .blend file to keep those changes")
  272. return {'FINISHED'}
  273. classes = (
  274. WM_OT_previews_batch_clear,
  275. WM_OT_previews_batch_generate,
  276. WM_OT_blend_strings_utf8_validate,
  277. )