Fix T38788: zero area faces raised exception with overhang test
[blender-addons.git] / io_sequencer_edl / __init__.py
blob66e306f8eb88695542da22bd616dc46c3fd7845c
1 # ##### BEGIN GPL LICENSE BLOCK #####
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.
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.
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.
17 # ##### END GPL LICENSE BLOCK #####
19 # <pep8 compliant>
21 bl_info = {
22 "name": "Import EDL",
23 "author": "Campbell Barton",
24 "version": (1, 0),
25 "blender": (2, 65, 0),
26 "location": "Sequencer -> Track View Properties",
27 "description": "Load a CMX formatted EDL into the sequencer",
28 "warning": "",
29 "wiki_url": "http://wiki.blender.org/index.php/Extensions:"
30 "2.6/Py/Scripts/Import-Export/EDL_Import",
31 "tracker_url": "",
32 "category": "Import-Export"}
34 import bpy
37 # ImportHelper is a helper class, defines filename and
38 # invoke() function which calls the file selector.
39 from bpy_extras.io_utils import ImportHelper
40 from bpy.props import (StringProperty,
41 IntProperty,
42 PointerProperty,
43 CollectionProperty,
45 from bpy.types import Operator
47 # ----------------------------------------------------------------------------
48 # Main Operators
51 class ReloadEDL(Operator):
52 bl_idname = "sequencer.import_edl_refresh"
53 bl_label = "Refresh Reels"
55 def execute(self, context):
56 import os
57 from . import parse_edl
59 scene = context.scene
60 edl_import_info = scene.edl_import_info
62 filepath = edl_import_info.filepath
63 dummy_fps = 25
65 if not os.path.exists(filepath):
66 self.report({'ERROR'}, "File Not Found %r" % filepath)
67 return {'CANCELLED'}
69 elist = parse_edl.EditList()
70 if not elist.parse(filepath, dummy_fps):
71 self.report({'ERROR'}, "Failed to parse EDL %r" % filepath)
72 return {'CANCELLED'}
74 scene = context.scene
75 edl_import_info = scene.edl_import_info
76 bl_reels = edl_import_info.reels
78 data_prev = {reel.name: (reel.filepath, reel.frame_offset)
79 for reel in edl_import_info.reels}
81 reels = elist.reels_as_dict()
82 reels = [k for k in reels.keys() if k not in parse_edl.BLACK_ID]
84 # re-create reels collection, keeping old values
85 bl_reels.clear()
86 for k in sorted(reels):
87 reel = bl_reels.add()
88 reel.name = k
89 filepath, frame_offset = data_prev.get(k, (None, None))
90 if filepath is not None:
91 reel.filepath = filepath
92 reel.frame_offset = frame_offset
94 return {'FINISHED'}
97 class FindReelsEDL(Operator):
98 """Scan a path for missing reel files, """ \
99 """ Matching by reel name and existing filename when set"""
100 bl_idname = "sequencer.import_edl_findreel"
101 bl_label = "Find Missing Reel Files"
102 directory = StringProperty(
103 subtype='DIR_PATH',
106 @staticmethod
107 def missing_reels(context):
108 import os
109 scene = context.scene
110 edl_import_info = scene.edl_import_info
111 return [reel for reel in edl_import_info.reels
112 if not os.path.exists(reel.filepath)]
114 def execute(self, context):
115 import os
117 # walk over .avi, .mov, .wav etc.
118 def media_file_walker(path):
119 ext_check = bpy.path.extensions_movie | bpy.path.extensions_audio
120 for dirpath, dirnames, filenames in os.walk(path):
121 # skip '.svn'
122 if dirpath.startswith("."):
123 continue
124 for filename in filenames:
125 fileonly, ext = os.path.splitext(filename)
126 ext_lower = ext.lower()
127 if ext_lower in ext_check:
128 yield os.path.join(dirpath, filename), fileonly
130 scene = context.scene
131 edl_import_info = scene.edl_import_info
133 bl_reels = FindReelsEDL.missing_reels(context)
134 assert(len(bl_reels))
136 # Search is as follows
137 # Each reel has a triple:
138 # ([search_names, ...], [(priority, found_file), ...], bl_reel)
139 bl_reels_search = [(set(), [], reel) for reel in bl_reels]
141 # first get the search names...
142 for reel_names, reel_files_found, reel in bl_reels_search:
143 reel_names_list = []
144 reel_names_list.append(reel.name.lower())
146 # add non-extension version of the reel name
147 if "." in reel_names_list[-1]:
148 reel_names_list.append(os.path.splitext(reel_names_list[-1])[0])
150 # use the filepath if set
151 reel_filepath = reel.filepath
152 if reel_filepath:
153 reel_filepath = os.path.basename(reel_filepath)
154 reel_filepath = os.path.splitext(reel_filepath)[0]
155 reel_names_list.append(reel_filepath.lower())
157 # when '_' are found, replace with space
158 reel_names_list += [reel_filepath.replace("_", " ")
159 for reel_filepath in reel_names_list
160 if "_" in reel_filepath]
161 reel_names.update(reel_names_list)
163 # debug info
164 print("Searching or %d reels" % len(bl_reels_search))
165 for reel_names, reel_files_found, reel in bl_reels_search:
166 print("Reel: %r --> (%s)" % (reel.name, " ".join(sorted(reel_names))))
167 print()
169 for filename, fileonly in media_file_walker(self.directory):
170 for reel_names, reel_files_found, reel in bl_reels_search:
171 if fileonly.lower() in reel_names:
172 reel_files_found.append((0, filename))
173 else:
174 # check on partial match
175 for r in reel_names:
176 if fileonly.startswith(r):
177 reel_files_found.append((1, filename))
178 if fileonly.endswith(r):
179 reel_files_found.append((2, filename))
181 # apply back and report
182 tot_done = 0
183 tot_fail = 0
184 for reel_names, reel_files_found, reel in bl_reels_search:
185 if reel_files_found:
186 # make sure partial matches end last
187 reel_files_found.sort()
188 reel.filepath = reel_files_found[0][1]
189 tot_done += 1
190 else:
191 tot_fail += 1
193 self.report({'INFO'} if tot_fail == 0 else {'WARNING'},
194 "Found %d clips, missing %d" % (tot_done, tot_fail))
196 return {'FINISHED'}
198 def invoke(self, context, event):
199 import os
200 scene = context.scene
201 edl_import_info = scene.edl_import_info
203 if not FindReelsEDL.missing_reels(context):
204 self.report({'INFO'},
205 "Nothing to do, all reels point to valid files")
206 return {'CANCELLED'}
208 # default to the EDL path
209 if not self.directory and edl_import_info.filepath:
210 self.directory = os.path.dirname(edl_import_info.filepath)
212 wm = context.window_manager
213 wm.fileselect_add(self)
214 return {"RUNNING_MODAL"}
217 class ImportEDL(Operator):
218 """Import an EDL into the sequencer"""
219 bl_idname = "sequencer.import_edl"
220 bl_label = "Import Video Sequence (.edl)"
222 def execute(self, context):
223 import os
224 from . import import_edl
225 scene = context.scene
226 edl_import_info = scene.edl_import_info
228 filepath = edl_import_info.filepath
229 reel_filepaths = {reel.name: reel.filepath
230 for reel in edl_import_info.reels}
231 reel_offsets = {reel.name: reel.frame_offset
232 for reel in edl_import_info.reels}
234 if not os.path.exists(filepath):
235 self.report({'ERROR'}, "File Not Found %r" % filepath)
236 return {'CANCELLED'}
238 msg = import_edl.load_edl(
239 scene, filepath,
240 reel_filepaths, reel_offsets,
241 edl_import_info.frame_offset)
243 if msg:
244 self.report({'WARNING'}, msg)
246 return {'FINISHED'}
249 # ----------------------------------------------------------------------------
250 # Persistent Scene Data Types (store EDL import info)
252 class EDLReelInfo(bpy.types.PropertyGroup):
253 name = StringProperty(
254 name="Name",
256 filepath = StringProperty(
257 name="Video File",
258 subtype='FILE_PATH',
260 frame_offset = IntProperty(
261 name="Frame Offset",
265 class EDLImportInfo(bpy.types.PropertyGroup):
266 filepath = StringProperty(
267 subtype='FILE_PATH',
269 reels = bpy.props.CollectionProperty(
270 type=EDLReelInfo,
272 frame_offset = IntProperty(
273 name="Global Frame Offset",
276 # ----------------------------------------------------------------------------
277 # Panel to show EDL Import UI
280 class SEQUENCER_PT_import_edl(bpy.types.Panel):
281 bl_label = "EDL Import"
282 bl_space_type = 'SEQUENCE_EDITOR'
283 bl_region_type = 'UI'
285 def draw(self, context):
286 layout = self.layout
288 scene = context.scene
289 edl_import_info = scene.edl_import_info
291 layout.operator(ImportEDL.bl_idname)
293 col = layout.column(align=True)
294 col.prop(edl_import_info, "frame_offset")
295 col.prop(edl_import_info, "filepath", text="")
296 col.operator(ReloadEDL.bl_idname, icon='FILE_REFRESH')
298 box = layout.box()
299 reel = None
300 for reel in scene.edl_import_info.reels:
301 col = box.column(align=True)
302 col.label(text=reel.name)
303 col.prop(reel, "filepath", text="")
304 col.prop(reel, "frame_offset")
306 if reel is None:
307 box.label("Empty (No EDL Data)")
309 box.operator(FindReelsEDL.bl_idname, icon='EXTERNAL_DATA')
312 def register():
313 bpy.utils.register_class(ReloadEDL)
314 bpy.utils.register_class(FindReelsEDL)
315 bpy.utils.register_class(ImportEDL)
316 bpy.utils.register_class(SEQUENCER_PT_import_edl)
318 # edl_import_info
319 bpy.utils.register_class(EDLReelInfo)
320 bpy.utils.register_class(EDLImportInfo)
321 bpy.types.Scene.edl_import_info = PointerProperty(type=EDLImportInfo)
324 def unregister():
325 bpy.utils.unregister_class(ReloadEDL)
326 bpy.utils.unregister_class(FindReelsEDL)
327 bpy.utils.unregister_class(ImportEDL)
328 bpy.utils.unregister_class(SEQUENCER_PT_import_edl)
330 # edl_import_info
331 bpy.utils.unregister_class(EDLImportInfo)
332 bpy.utils.unregister_class(EDLReelInfo)
333 del bpy.types.Scene.edl_import_info