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 #####
23 "author": "Campbell Barton",
25 "blender": (2, 65, 0),
26 "location": "Sequencer -> Track View Properties",
27 "description": "Load a CMX formatted EDL into the sequencer",
29 "wiki_url": "http://wiki.blender.org/index.php/Extensions:"
30 "2.6/Py/Scripts/Import-Export/EDL_Import",
32 "category": "Import-Export"}
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
,
45 from bpy
.types
import Operator
47 # ----------------------------------------------------------------------------
51 class ReloadEDL(Operator
):
52 bl_idname
= "sequencer.import_edl_refresh"
53 bl_label
= "Refresh Reels"
55 def execute(self
, context
):
57 from . import parse_edl
60 edl_import_info
= scene
.edl_import_info
62 filepath
= edl_import_info
.filepath
65 if not os
.path
.exists(filepath
):
66 self
.report({'ERROR'}, "File Not Found %r" % filepath
)
69 elist
= parse_edl
.EditList()
70 if not elist
.parse(filepath
, dummy_fps
):
71 self
.report({'ERROR'}, "Failed to parse EDL %r" % filepath
)
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
86 for k
in sorted(reels
):
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
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(
107 def missing_reels(context
):
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
):
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
):
122 if dirpath
.startswith("."):
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
:
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
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
)
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
))))
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
))
174 # check on partial match
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
184 for reel_names
, reel_files_found
, reel
in bl_reels_search
:
186 # make sure partial matches end last
187 reel_files_found
.sort()
188 reel
.filepath
= reel_files_found
[0][1]
193 self
.report({'INFO'} if tot_fail
== 0 else {'WARNING'},
194 "Found %d clips, missing %d" % (tot_done
, tot_fail
))
198 def invoke(self
, context
, event
):
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")
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
):
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
)
238 msg
= import_edl
.load_edl(
240 reel_filepaths
, reel_offsets
,
241 edl_import_info
.frame_offset
)
244 self
.report({'WARNING'}, msg
)
249 # ----------------------------------------------------------------------------
250 # Persistent Scene Data Types (store EDL import info)
252 class EDLReelInfo(bpy
.types
.PropertyGroup
):
253 name
= StringProperty(
256 filepath
= StringProperty(
260 frame_offset
= IntProperty(
265 class EDLImportInfo(bpy
.types
.PropertyGroup
):
266 filepath
= StringProperty(
269 reels
= bpy
.props
.CollectionProperty(
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
):
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')
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")
307 box
.label("Empty (No EDL Data)")
309 box
.operator(FindReelsEDL
.bl_idname
, icon
='EXTERNAL_DATA')
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
)
319 bpy
.utils
.register_class(EDLReelInfo
)
320 bpy
.utils
.register_class(EDLImportInfo
)
321 bpy
.types
.Scene
.edl_import_info
= PointerProperty(type=EDLImportInfo
)
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
)
331 bpy
.utils
.unregister_class(EDLImportInfo
)
332 bpy
.utils
.unregister_class(EDLReelInfo
)
333 del bpy
.types
.Scene
.edl_import_info