# SPDX-License-Identifier: GPL-2.0-or-later """ Example Usage: ./blender.bin --background --python tests/python/batch_import.py -- \ --operator="bpy.ops.import_scene.obj" \ --path="/fe/obj" \ --match="*.obj" \ --start=0 --end=10 \ --save_path=/tmp/test ./blender.bin --background --python tests/python/batch_import.py -- \ --operator="bpy.ops.import_scene.autodesk_3ds" \ --path="/fe/" \ --match="*.3ds" \ --start=0 --end=1000 \ --save_path=/tmp/test ./blender.bin --background --addons io_curve_svg --python tests/python/batch_import.py -- \ --operator="bpy.ops.import_curve.svg" \ --path="/usr/" \ --match="*.svg" \ --start=0 --end=1000 \ --save_path=/tmp/test """ import os import sys def batch_import( operator="", path="", save_path="", match="", start=0, end=sys.maxsize, ): import addon_utils _reset_all = addon_utils.reset_all # XXX, hack _disable_all = addon_utils.disable_all # XXX, hack import fnmatch path = os.path.normpath(path) path = os.path.abspath(path) match_upper = match.upper() def pattern_match(a): return fnmatch.fnmatchcase(a.upper(), match_upper) def file_generator(path): for dirpath, dirnames, filenames in os.walk(path): # skip '.git' dirnames[:] = [d for d in dirnames if not d.startswith(".")] for filename in filenames: if pattern_match(filename): yield os.path.join(dirpath, filename) print("Collecting %r files in %s" % (match, path), end="") files = list(file_generator(path)) files_len = len(files) end = min(end, len(files)) print(" found %d" % files_len, end="") files.sort() files = files[start:end] if len(files) != files_len: print(" using a subset in (%d, %d), total %d" % (start, end, len(files)), end="") import bpy op = eval(operator) tot_done = 0 tot_fail = 0 for i, f in enumerate(files): print(" %s(filepath=%r) # %d of %d" % (operator, f, i + start, len(files))) # hack so loading the new file doesn't undo our loaded addons addon_utils.reset_all = lambda: None # XXX, hack addon_utils.disable_all = lambda: None # XXX, hack bpy.ops.wm.read_factory_settings(use_empty=True) addon_utils.reset_all = _reset_all # XXX, hack addon_utils.disable_all = _disable_all # XXX, hack result = op(filepath=f) if 'FINISHED' in result: tot_done += 1 else: tot_fail += 1 if save_path: fout = os.path.join(save_path, os.path.relpath(f, path)) fout_blend = os.path.splitext(fout)[0] + ".blend" print("\tSaving: %r" % fout_blend) fout_dir = os.path.dirname(fout_blend) os.makedirs(fout_dir, exist_ok=True) bpy.ops.wm.save_as_mainfile(filepath=fout_blend) print("finished, done:%d, fail:%d" % (tot_done, tot_fail)) def main(): import optparse # get the args passed to blender after "--", all of which are ignored by blender specifically # so python may receive its own arguments argv = sys.argv if "--" not in argv: argv = [] # as if no args are passed else: argv = argv[argv.index("--") + 1:] # get all args after "--" # When --help or no args are given, print this help usage_text = "Run blender in background mode with this script:" usage_text += " blender --background --python " + __file__ + " -- [options]" parser = optparse.OptionParser(usage=usage_text) # Example background utility, add some text and renders or saves it (with options) # Possible types are: string, int, long, choice, float and complex. parser.add_option( "-o", "--operator", dest="operator", help="This text will be used to render an image", type="string") parser.add_option("-p", "--path", dest="path", help="Path to use for searching for files", type='string') parser.add_option("-m", "--match", dest="match", help="Wildcard to match filename", type="string") parser.add_option( "-s", "--save_path", dest="save_path", help="Save the input file to a blend file in a new location", metavar='string') parser.add_option("-S", "--start", dest="start", help="From collected files, start with this index", metavar='int') parser.add_option("-E", "--end", dest="end", help="From collected files, end with this index", metavar='int') options, _args = parser.parse_args(argv) # In this example we won't use the args if not argv: parser.print_help() return if not options.operator: print("Error: --operator=\"some string\" argument not given, aborting.") parser.print_help() return if options.start is None: options.start = 0 if options.end is None: options.end = sys.maxsize # Run the example function batch_import(operator=options.operator, path=options.path, save_path=options.save_path, match=options.match, start=int(options.start), end=int(options.end), ) print("batch job finished, exiting") if __name__ == "__main__": main()