Integrated Freestyle to rendering pipeline
[blender.git] / release / scripts / import_edl.py
1 #!BPY
2  
3 """
4 Name: 'Video Sequence (.edl)...'
5 Blender: 248
6 Group: 'Import'
7 Tooltip: 'Load a CMX formatted EDL into the sequencer'
8 """
9
10 # ***** BEGIN GPL LICENSE BLOCK *****
11 #
12 # Copyright (C) 2009: Campbell Barton, ideasman42@gmail.com
13 #
14 # This program is free software; you can redistribute it and/or
15 # modify it under the terms of the GNU General Public License
16 # as published by the Free Software Foundation; either version 2
17 # of the License, or (at your option) any later version.
18 #
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 # GNU General Public License for more details.
23 #
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software Foundation,
26 # --------------------------------------------------------------------------
27
28 class TimeCode(object):
29         '''
30         Simple timecode class
31         also supports conversion from other time strings used by EDL
32         '''
33         def __init__(self, data, fps):
34                 self.fps= fps
35                 if type(data)==str:
36                         self.fromString(data)
37                         frame = self.asFrame()
38                         self.fromFrame(frame)
39                 else:
40                         self.fromFrame(data)
41                 
42         def fromString(self, text):
43                 # hh:mm:ss:ff
44                 # No dropframe support yet
45
46                 if text.lower().endswith('mps'): # 5.2mps
47                         return self.fromFrame( int( float(text[:-3]) * self.fps ) )
48                 elif text.lower().endswith('s'): # 5.2s
49                         return self.fromFrame( int( float(text[:-1]) * self.fps ) )
50                 elif text.isdigit(): # 1234
51                         return self.fromFrame( int(text) )
52                 elif ':' in text: # hh:mm:ss:ff
53                         text= text.replace(';', ':').replace(',', ':').replace('.', ':')
54                         text= text.split(':')
55                         
56                         self.hours= int(text[0])
57                         self.minutes= int(text[1])
58                         self.seconds= int(text[2])
59                         self.frame= int(text[3])
60                         return self
61                 else:
62                         print 'ERROR: could not convert this into timecode "%s"' % test
63                         return self
64
65                 
66         def fromFrame(self, frame):
67                 
68                 if frame < 0:
69                         frame = -frame;
70                         neg=True
71                 else:
72                         neg=False
73                 
74                 fpm = 60 * self.fps
75                 fph = 60 * fpm
76                 
77                 if frame < fph:
78                         self.hours= 0
79                 else:
80                         self.hours= int(frame/fph)
81                         frame = frame % fph
82                 
83                 if frame < fpm:
84                         self.minutes= 0
85                 else:
86                         self.minutes= int(frame/fpm)
87                         frame = frame % fpm
88                 
89                 if frame < self.fps:
90                         self.seconds= 0
91                 else:
92                         self.seconds= int(frame/self.fps)
93                         frame = frame % self.fps
94                 
95                 self.frame= frame
96                 
97                 if neg:
98                         self.frame = -self.frame
99                         self.seconds = -self.seconds
100                         self.minutes = -self.minutes
101                         self.hours = -self.hours
102                 
103                 return self
104                 
105         def asFrame(self):
106                 abs_frame= self.frame
107                 abs_frame += self.seconds * self.fps
108                 abs_frame += self.minutes * 60 * self.fps
109                 abs_frame += self.hours * 60 * 60 * self.fps
110                 
111                 return abs_frame
112         
113         def asString(self):
114                 self.fromFrame(int(self))
115                 return '%.2d:%.2d:%.2d:%.2d' % (self.hours, self.minutes, self.seconds, self.frame)
116         
117         def __repr__(self):
118                 return self.asString()
119         
120         # Numeric stuff, may as well have this
121         def __neg__(self):                      return TimeCode(-int(self), self.fps)
122         def __int__(self):                      return self.asFrame()
123         def __sub__(self, other):               return TimeCode(int(self)-int(other), self.fps)
124         def __add__(self, other):               return TimeCode(int(self)+int(other), self.fps)
125         def __mul__(self, other):               return TimeCode(int(self)*int(other), self.fps)
126         def __div__(self, other):               return TimeCode(int(self)/int(other), self.fps)
127         def __abs__(self):                      return TimeCode(abs(int(self)), self.fps)
128         def __iadd__(self, other):      return self.fromFrame(int(self)+int(other))
129         def __imul__(self, other):      return self.fromFrame(int(self)*int(other))
130         def __idiv__(self, other):              return self.fromFrame(int(self)/int(other))
131 # end timecode
132
133
134 '''Comments
135 Comments can appear at the beginning of the EDL file (header) or between the edit lines in the EDL. The first block of comments in the file is defined to be the header comments and they are associated with the EDL as a whole. Subsequent comments in the EDL file are associated with the first edit line that appears after them.
136 Edit Entries
137 <filename|tag>  <EditMode>  <TransitionType>[num]  [duration]  [srcIn]  [srcOut]  [recIn]  [recOut]
138
139     * <filename|tag>: Filename or tag value. Filename can be for an MPEG file, Image file, or Image file template. Image file templates use the same pattern matching as for command line glob, and can be used to specify images to encode into MPEG. i.e. /usr/data/images/image*.jpg
140     * <EditMode>: 'V' | 'A' | 'VA' | 'B' | 'v' | 'a' | 'va' | 'b' which equals Video, Audio, Video_Audio edits (note B or b can be used in place of VA or va).
141     * <TransitonType>: 'C' | 'D' | 'E' | 'FI' | 'FO' | 'W' | 'c' | 'd' | 'e' | 'fi' | 'fo' | 'w'. which equals Cut, Dissolve, Effect, FadeIn, FadeOut, Wipe.
142     * [num]: if TransitionType = Wipe, then a wipe number must be given. At the moment only wipe 'W0' and 'W1' are supported.
143     * [duration]: if the TransitionType is not equal to Cut, then an effect duration must be given. Duration is in frames.
144     * [srcIn]: Src in. If no srcIn is given, then it defaults to the first frame of the video or the first frame in the image pattern. If srcIn isn't specified, then srcOut, recIn, recOut can't be specified.
145     * [srcOut]: Src out. If no srcOut is given, then it defaults to the last frame of the video - or last image in the image pattern. if srcOut isn't given, then recIn and recOut can't be specified.
146     * [recIn]: Rec in. If no recIn is given, then it is calculated based on its position in the EDL and the length of its input.
147       [recOut]: Rec out. If no recOut is given, then it is calculated based on its position in the EDL and the length of its input. first frame of the video. 
148
149 For srcIn, srcOut, recIn, recOut, the values can be specified as either timecode, frame number, seconds, or mps seconds. i.e.
150 [tcode | fnum | sec | mps], where:
151
152     * tcode : SMPTE timecode in hh:mm:ss:ff
153     * fnum : frame number (the first decodable frame in the video is taken to be frame 0).
154     * sec : seconds with 's' suffix (e.g. 5.2s)
155     * mps : seconds with 'mps' suffix (e.g. 5.2mps). This corresponds to the 'seconds' value displayed by Windows MediaPlayer.
156
157 More notes, 
158 Key
159         
160 '''
161
162 enum= 0
163 TRANSITION_UNKNOWN= enum
164 TRANSITION_CUT= enum;                           enum+=1
165 TRANSITION_DISSOLVE= enum;                      enum+=1
166 TRANSITION_EFFECT= enum;                        enum+=1
167 TRANSITION_FADEIN= enum;                        enum+=1
168 TRANSITION_FADEOUT= enum;                       enum+=1
169 TRANSITION_WIPE= enum;                          enum+=1
170 TRANSITION_KEY= enum;                           enum+=1
171
172 TRANSITION_DICT={ \
173                                 'c':TRANSITION_CUT,
174                                 'd':TRANSITION_DISSOLVE,
175                                 'e':TRANSITION_EFFECT,
176                                 'fi':TRANSITION_FADEIN,
177                                 'fo':TRANSITION_FADEOUT,
178                                 'w':TRANSITION_WIPE,
179                                 'k':TRANSITION_KEY,
180                                 }
181
182 enum= 0
183 EDIT_UNKNOWN=                           1<<enum; enum+=1
184 EDIT_VIDEO=                             1<<enum; enum+=1
185 EDIT_AUDIO=                             1<<enum; enum+=1
186 EDIT_AUDIO_STEREO=                      1<<enum; enum+=1
187 EDIT_VIDEO_AUDIO=                       1<<enum; enum+=1
188
189 EDIT_DICT=              { \
190                                 'v':EDIT_VIDEO,
191                                 'a':EDIT_AUDIO,
192                                 'aa':EDIT_AUDIO_STEREO,
193                                 'va':EDIT_VIDEO_AUDIO,
194                                 'b':EDIT_VIDEO_AUDIO
195                                 }
196
197
198 enum= 0
199 WIPE_UNKNOWN= enum
200 WIPE_0= enum;                                   enum+=1
201 WIPE_1= enum;                                   enum+=1
202
203 enum= 0
204 KEY_UNKNOWN= enum
205 KEY_BG= enum;                                   enum+=1 # K B
206 KEY_IN= enum;                                   enum+=1 # This is assumed if no second type is set
207 KEY_OUT= enum;                                  enum+=1 # K O
208
209
210 '''
211 Most sytems:
212 Non-dropframe: 1:00:00:00 - colon in last position
213 Dropframe: 1:00:00;00 - semicolon in last position
214 PAL/SECAM: 1:00:00:00 - colon in last position
215
216 SONY:
217 Non-dropframe: 1:00:00.00 - period in last position
218 Dropframe: 1:00:00,00 - comma in last position
219 PAL/SECAM: 1:00:00.00 - period in last position
220 '''
221
222 '''
223 t = abs(timecode('-124:-12:-43:-22', 25))
224 t /= 2
225 print t
226 '''
227
228 def editFlagsToText(flag):
229         items = []
230         for item, val in EDIT_DICT.items():
231                 if val & flag:
232                         items.append(item)
233         return '/'.join(items)
234         
235
236 class EditDecision(object):
237         def __init__(self, text= None, fps= 25):
238                 # print text
239                 self.number = -1
240                 self.reel = '' # Uniqie name for this 'file' but not filename, when BL signifies black
241                 self.transition_duration= 0
242                 self.edit_type= EDIT_UNKNOWN
243                 self.transition_type= TRANSITION_UNKNOWN
244                 self.wipe_type = WIPE_UNKNOWN
245                 self.key_type = KEY_UNKNOWN
246                 self.key_fade = -1      # true/false
247                 self.srcIn = None  # Where on the original field recording the event begins
248                 self.srcOut = None # Where on the original field recording the event ends
249                 self.recIn = None  # Beginning of the original event in the edited program
250                 self.recOut = None # End of the original event in the edited program
251                 self.m2 = None          # fps set by the m2 command
252                 self.filename = '' 
253                 
254                 self.custom_data= [] # use for storing any data you want (blender strip for eg)
255
256                 if text != None:
257                         self.read(text, fps)
258         
259         def __repr__(self):
260                 txt= 'num: %d, ' % self.number
261                 txt += 'reel: %s, ' % self.reel
262                 txt += 'edit_type: '
263                 txt += editFlagsToText(self.edit_type) + ', '
264                 
265                 txt += 'trans_type: '
266                 for item, val in TRANSITION_DICT.items():
267                         if val == self.transition_type:
268                                 txt += item + ', '
269                                 break
270                 
271                 
272                 txt += 'm2: '
273                 if self.m2:
274                         txt += '%g' % float(self.m2.fps)
275                         txt += '\n\t'
276                         txt += self.m2.data
277                 else:
278                         txt += 'nil'
279                         
280                 txt += ', '
281                 txt += 'recIn: ' + str(self.recIn) + ', '
282                 txt += 'recOut: ' + str(self.recOut) + ', '
283                 txt += 'srcIn: ' + str(self.srcIn) + ', '
284                 txt += 'srcOut: ' + str(self.srcOut) + ', '
285                 
286                 return txt
287                 
288                 
289         def read(self, line, fps):
290                 line= line.split()
291                 index= 0
292                 self.number= int(line[index]); index+=1
293                 self.reel= line[index].lower(); index+=1
294                 
295                 # AA/V can be an edit type
296                 self.edit_type= 0
297                 for edit_type in line[index].lower().split('/'):
298                         self.edit_type |= EDIT_DICT[edit_type]; 
299                 index+=1
300                 
301                 tx_name = ''.join([c for c in line[index].lower() if not c.isdigit()])
302                 self.transition_type= TRANSITION_DICT[tx_name]; # advance the index later
303                 
304                 if self.transition_type== TRANSITION_WIPE:
305                         tx_num = ''.join([c for c in line[index].lower() if c.isdigit()])
306                         if tx_num:      tx_num = int(tx_num)
307                         else:           tx_num = 0
308                                         
309                         self.wipe_type= tx_num
310                 
311                 elif self.transition_type== TRANSITION_KEY: # UNTESTED
312                         
313                         val= line[index+1].lower()
314                         
315                         if val == 'b':
316                                 self.key_type= KEY_BG
317                                 index+=1
318                         elif val == 'o':
319                                 self.key_type= KEY_OUT
320                                 index+=1
321                         else:
322                                 self.key_type= KEY_IN # if no args given
323                                 
324                         # there may be an (F) after, eg 'K B (F)'
325                         # in the docs this should only be after K B but who knows, it may be after K O also?
326                         val= line[index+1].lower()
327                         if val == '(f)':
328                                 index+=1
329                                 self.key_fade = True
330                         else:
331                                 self.key_fade = False
332                         
333                 index+=1
334
335                 if self.transition_type in (TRANSITION_DISSOLVE, TRANSITION_EFFECT, TRANSITION_FADEIN, TRANSITION_FADEOUT, TRANSITION_WIPE):
336                         self.transition_duration= TimeCode(line[index], fps); index+=1
337
338                 if index < len(line):
339                         self.srcIn= TimeCode(line[index], fps); index+=1
340                 if index < len(line):
341                         self.srcOut= TimeCode(line[index], fps); index+=1
342                 
343                 if index < len(line):
344                         self.recIn= TimeCode(line[index], fps); index+=1
345                 if index < len(line):
346                         self.recOut= TimeCode(line[index], fps); index+=1
347         
348         def renumber(self):
349                 self.edits.sort( key=lambda e: int(e.recIn) )
350                 for i, edit in enumerate(self.edits):
351                         edit.number= i
352                 
353         def clean(self):
354                 '''
355                 Clean up double ups
356                 '''
357                 self.renumber()
358                 
359                 # TODO
360         def asName(self):
361                 cut_type = 'nil'
362                 for k,v in TRANSITION_DICT.iteritems():
363                         if v==self.transition_type:
364                                 cut_type = k
365                                 break
366                 
367                 return '%d_%s_%s' % (self.number, self.reel, cut_type)
368
369 class M2(object):
370         def __init__(self):
371                 self.reel = None
372                 self.fps = None
373                 self.time = None
374                 self.data = None
375                 
376                 self.index = -1
377                 self.tot = -1
378         
379         def read(self, line, fps):
380                 
381                 # M2   TAPEC          050.5                00:08:11:08
382                 words = line.split()
383                 
384                 self.reel=      words[1].lower()
385                 self.fps=       float(words[2])
386                 self.time=      TimeCode(words[3], fps)
387                 
388                 self.data = line
389         
390 class EditList(object):
391         def __init__(self):
392                 self.edits= []
393                 self.title= ''
394                 
395         def parse(self, filename, fps):
396                 try:
397                         file= open(filename, 'rU')
398                 except:
399                         return False
400
401                 self.edits= []
402                 edits_m2 = [] # edits with m2's
403                 
404                 has_m2 = False
405                 
406                 for line in file:
407                         line= ' '.join(line.split())
408                         
409                         if not line or line.startswith('*') or line.startswith('#'):
410                                 continue
411                         elif line.startswith('TITLE:'):
412                                 self.title= ' '.join(line.split()[1:])
413                         elif line.split()[0].lower() == 'm2':
414                                 has_m2 = True
415                                 m2 = M2()
416                                 m2.read(line, fps)
417                                 edits_m2.append( m2 )
418                         elif not line.split()[0].isdigit():
419                                 print 'Ignoring:', line
420                         else:
421                                 self.edits.append( EditDecision(line, fps) )
422                                 edits_m2.append( self.edits[-1] )
423                 
424                 if has_m2:
425                         # Group indexes
426                         i = 0
427                         for item in edits_m2:
428                                 if isinstance(item, M2):
429                                         item.index = i
430                                         i += 1
431                                 else:
432                                         # not an m2
433                                         i = 0
434                         
435                         # Set total group indexes
436                         for item in reversed(edits_m2):
437                                 if isinstance(item, M2):
438                                         if tot_m2 == -1:
439                                                 tot_m2 = item.index + 1
440                                         
441                                         item.tot = tot_m2
442                                 else:
443                                         # not an m2
444                                         tot_m2 = -1
445                         
446                         
447                         for i, item in enumerate(edits_m2):
448                                 if isinstance(item, M2):
449                                         # make a list of all items that match the m2's reel name
450                                         edits_m2_tmp = [item_tmp for item_tmp in edits_m2 if (isinstance(item, M2) or item_tmp.reel == item.reel)]
451                                         
452                                         # get the new index
453                                         i_tmp = edits_m2_tmp.index(item)
454                                         
455                                         # Seek back to get the edit.
456                                         edit = edits_m2[i_tmp-item.tot]
457                                         
458                                         # Note, docs say time should also match with edit start time
459                                         # but from final cut pro, this seems not to be the case
460                                         if not isinstance(edit, EditDecision):
461                                                 print "ERROR!", 'M2 incorrect'
462                                         else:
463                                                 edit.m2 = item
464                         
465                         
466                 return True
467         
468         def testOverlap(self, edit_test):
469                 recIn= int(edit_test.recIn)
470                 recOut= int(edit_test.recOut)
471                 
472                 for edit in self.edits:
473                         if edit is edit_test:
474                                 break
475                         
476                         recIn_other= int(edit.recIn)
477                         recOut_other= int(edit.recOut)
478                         
479                         if recIn_other < recIn < recOut_other:
480                                 return True
481                         if recIn_other < recOut < recOut_other:
482                                 return True
483                         
484                         if recIn < recIn_other < recOut:
485                                 return True
486                         if recIn < recOut_other < recOut:
487                                 return True
488                         
489                 return False
490         
491         def getReels(self):
492                 reels = {}
493                 for edit in self.edits:
494                         reels.setdefault(edit.reel, []).append(edit)
495                 
496                 return reels
497                 
498                 
499                 
500 # from DNA
501 SEQ_IMAGE=              0
502 SEQ_META=               1
503 SEQ_SCENE=              2
504 SEQ_MOVIE=              3
505 SEQ_RAM_SOUND=  4
506 SEQ_HD_SOUND=            5
507 SEQ_MOVIE_AND_HD_SOUND=  6
508
509 SEQ_EFFECT=             8
510 SEQ_CROSS=              8
511 SEQ_ADD=                9
512 SEQ_SUB=                10
513 SEQ_ALPHAOVER=  11
514 SEQ_ALPHAUNDER= 12
515 SEQ_GAMCROSS=   13
516 SEQ_MUL=                14
517 SEQ_OVERDROP=   15
518 SEQ_PLUGIN=             24
519 SEQ_WIPE=               25
520 SEQ_GLOW=               26
521 SEQ_TRANSFORM=  27
522 SEQ_COLOR=              28
523 SEQ_SPEED=              29
524
525 # Blender spesific stuff starts here
526 import bpy
527 import Blender
528
529 def scale_meta_speed(seq, mov, scale):
530         # Add an effect
531         speed= seq.new((SEQ_SPEED, mov,), 199, mov.channel+1)
532         speed.speedEffectFrameBlending = True
533         meta= seq.new([mov, speed], 199, mov.channel)
534
535         if scale >= 1.0:
536                 mov.endStill = int(mov.length * (scale - 1.0))
537         else:
538                 speed.speedEffectGlobalSpeed = 1.0/scale
539                 meta.endOffset = mov.length - int(mov.length*scale)
540
541         speed.update()
542         meta.update()
543         return meta
544
545 def apply_dissolve_ipo(mov, blendin):
546         len_disp = float(mov.endDisp - mov.startDisp)
547         
548         if len_disp <= 0.0:
549                 print 'Error, strip is zero length'
550                 return
551         
552         mov.ipo= ipo= bpy.data.ipos.new("fade", "Sequence")
553         icu= ipo.addCurve('Fac')
554         
555         icu.interpolation= Blender.IpoCurve.InterpTypes.LINEAR
556         icu.append((0, 0))
557         icu.append(((int(blendin)/len_disp) * 100, 1))
558         
559         if mov.type not in (SEQ_HD_SOUND, SEQ_RAM_SOUND):
560                 mov.blendMode = Blender.Scene.Sequence.BlendModes.ALPHAOVER
561
562
563 def replace_ext(path, ext):
564         return path[:path.rfind('.')+1] + ext
565
566 def load_edl(filename, reel_files, reel_offsets):
567         '''
568         reel_files - key:reel <--> reel:filename
569         '''
570         
571         # For test file
572         # frame_offset = -769
573         
574         
575         sce= bpy.data.scenes.active
576         fps= sce.render.fps
577         
578         elist= EditList()
579         if not elist.parse(filename, fps):
580                 return 'Unable to parse "%s"' % filename
581         # elist.clean()
582         
583         
584         seq= sce.sequence
585         
586         track= 0 
587         
588         edits = elist.edits[:]
589         # edits.sort(key = lambda edit: int(edit.recIn))
590         
591         prev_edit = None
592         for edit in edits:
593                 print edit
594                 frame_offset = reel_offsets[edit.reel]
595                 
596                 
597                 src_start=      int(edit.srcIn) + frame_offset
598                 src_end=        int(edit.srcOut) + frame_offset
599                 src_length=     src_end-src_start
600                 
601                 rec_start=      int(edit.recIn) + 1
602                 rec_end=        int(edit.recOut) + 1
603                 rec_length=     rec_end-rec_start
604                 
605                 # print src_length, rec_length, src_start
606                 
607                 if edit.m2 != None:     scale = fps/float(edit.m2.fps)
608                 else:                                   scale = 1.0
609                 
610                 unedited_start= rec_start - src_start
611                 offset_start = src_start - int(src_start*scale) # works for scaling up AND down
612                 
613                 if edit.transition_type == TRANSITION_CUT and (not elist.testOverlap(edit)):
614                         track = 1
615                 
616                 strip= None
617                 final_strips = []
618                 if edit.reel.lower()=='bw':
619                         strip= seq.new((0,0,0), rec_start, track+1)
620                         strip.length= rec_length # for color its simple
621                         final_strips.append(strip)
622                 else:
623
624                         path_full = reel_files[edit.reel]
625                         path_fileonly= path_full.split('/')[-1].split('\\')[-1] # os.path.basename(full)
626                         path_dironly= path_full[:-len(path_fileonly)] # os.path.dirname(full)
627                         
628                         if edit.edit_type & EDIT_VIDEO: #and edit.transition_type == TRANSITION_CUT:    
629                                 
630                                 try:
631                                         strip= seq.new((path_fileonly, path_dironly, path_full, 'movie'), unedited_start + offset_start, track+1)
632                                 except:
633                                         return 'Invalid input for movie'
634                                 
635                                 # Apply scaled rec in bounds
636                                 if scale != 1.0:
637                                         meta = scale_meta_speed(seq, strip, scale)
638                                         final_strip = meta
639                                 else:
640                                         final_strip = strip
641                                 
642                                 
643                                 final_strip.update()
644                                 final_strip.startOffset= rec_start - final_strip.startDisp
645                                 final_strip.endOffset= rec_end- final_strip.endDisp
646                                 final_strip.update()
647                                 final_strip.endOffset += (final_strip.endDisp - rec_end)
648                                 final_strip.update()
649                                 
650                                 
651                                 if edit.transition_duration:
652                                         if not prev_edit:
653                                                 print "Error no previous strip"
654                                         else:
655                                                 new_end = rec_start + int(edit.transition_duration)
656                                                 for other in prev_edit.custom_data:
657                                                         if other.type != SEQ_HD_SOUND and other.type != SEQ_RAM_SOUND:
658                                                                 other.endOffset += (other.endDisp - new_end)
659                                                                 other.update()
660                                                 
661                                 # Apply disolve
662                                 if edit.transition_type == TRANSITION_DISSOLVE:
663                                         apply_dissolve_ipo(final_strip, edit.transition_duration)
664                                 
665                                 if edit.transition_type == TRANSITION_WIPE:
666                                         other_track = track + 2
667                                         for other in prev_edit.custom_data:
668                                                 if other.type != SEQ_HD_SOUND and other.type != SEQ_RAM_SOUND:
669                                                         
670                                                         strip_wipe= seq.new((SEQ_WIPE, other, final_strip), 1, other_track)
671                                                         
672                                                         if edit.wipe_type == WIPE_0:
673                                                                 strip_wipe.wipeEffectAngle =  90
674                                                         else:
675                                                                 strip_wipe.wipeEffectAngle = -90
676                                                         
677                                                         other_track += 1
678                                                         
679                                 
680                                 
681                                 # strip.endOffset= strip.length - int(edit.srcOut)
682                                 #end_offset= (unedited_start+strip.length) - end
683                                 # print start, end, end_offset
684                                 #strip.endOffset = end_offset
685                                 
686                                 # break
687                                 # print strip
688                                 
689                                 final_strips.append(final_strip)
690                         
691                                 
692                         if edit.edit_type & (EDIT_AUDIO | EDIT_AUDIO_STEREO | EDIT_VIDEO_AUDIO):
693                                 
694                                 if scale == 1.0:  # TODO - scaled audio
695                                         
696                                         try:
697                                                 strip= seq.new((path_fileonly, path_dironly, path_full, 'audio_hd'), unedited_start + offset_start, track+6)
698                                         except:
699                                                 
700                                                 # See if there is a wave file there
701                                                 path_full_wav = replace_ext(path_full, 'wav')
702                                                 path_fileonly_wav = replace_ext(path_fileonly, 'wav')
703                                                 
704                                                 #try:
705                                                 strip= seq.new((path_fileonly_wav, path_dironly, path_full_wav, 'audio_hd'), unedited_start + offset_start, track+6)
706                                                 #except:
707                                                 #       return 'Invalid input for audio'
708                                         
709                                         final_strip = strip
710                                         
711                                         # Copied from above
712                                         final_strip.update()
713                                         final_strip.startOffset= rec_start - final_strip.startDisp
714                                         final_strip.endOffset= rec_end- final_strip.endDisp
715                                         final_strip.update()
716                                         final_strip.endOffset += (final_strip.endDisp - rec_end)
717                                         final_strip.update()
718                                         
719                                         if edit.transition_type == TRANSITION_DISSOLVE:
720                                                 apply_dissolve_ipo(final_strip, edit.transition_duration)
721                                         
722                                         final_strips.append(final_strip)
723                                 
724                         # strip= seq.new((0.6, 0.6, 0.6), start, track+1)
725                         
726                 if final_strips:
727                         for strip in final_strips:
728                                 # strip.length= length
729                                 final_strip.name = edit.asName()
730                                 edit.custom_data[:]= final_strips
731                                 # track = not track
732                                 prev_edit = edit
733                         track += 1
734                         
735                 #break
736         
737         
738         def recursive_update(s):
739                 s.update(1)
740                 for s_kid in s:
741                         recursive_update(s_kid)
742                         
743                 
744         for s in seq:
745                 recursive_update(s)
746         
747         return ''
748
749
750
751 #load_edl('/fe/edl/EP30CMXtrk1.edl') # /tmp/test.edl
752 #load_edl('/fe/edl/EP30CMXtrk2.edl') # /tmp/test.edl
753 #load_edl('/fe/edl/EP30CMXtrk3.edl') # /tmp/test.edl
754 #load_edl('/root/vid/rush/blender_edl.edl', ['/root/vid/rush/rushes3.avi',]) # /tmp/test.edl
755
756
757
758
759 # ---------------------- Blender UI part
760 from Blender import Draw, Window
761 import BPyWindow
762
763 if 0:
764         DEFAULT_FILE_EDL = '/root/vid/rush/blender_edl.edl'
765         DEFAULT_FILE_MEDIA = '/root/vid/rush/rushes3_wav.avi'
766         DEFAULT_FRAME_OFFSET = -769
767 else:
768         DEFAULT_FILE_EDL = ''
769         DEFAULT_FILE_MEDIA = ''
770         DEFAULT_FRAME_OFFSET = 0
771
772 B_EVENT_IMPORT = 1
773 B_EVENT_RELOAD = 2
774 B_EVENT_FILESEL_EDL = 3
775 B_EVENT_NOP = 4
776
777 B_EVENT_FILESEL = 100 # or greater
778
779 class ReelItemUI(object):
780         __slots__ = 'filename_but', 'offset_but', 'ui_text'
781         def __init__(self):
782                 self.filename_but =     Draw.Create(DEFAULT_FILE_MEDIA)
783                 self.offset_but =       Draw.Create(DEFAULT_FRAME_OFFSET)
784                 self.ui_text = ''
785         
786
787
788 REEL_UI = {}    # reel:ui_string
789
790
791 #REEL_FILENAMES = {}            # reel:filename
792 #REEL_OFFSETS = {}              # reel:filename
793
794 PREF = {}
795
796 PREF['filename'] = Draw.Create(DEFAULT_FILE_EDL)
797 PREF['reel_act'] = ''
798
799 def edl_reload():
800         Window.WaitCursor(1)
801         filename = PREF['filename'].val
802         sce= bpy.data.scenes.active
803         fps= sce.render.fps
804         
805         elist= EditList()
806         
807         if filename:
808                 if not elist.parse(filename, fps):
809                         Draw.PupMenu('Error%t|Could not open the file "' + filename + '"')
810                 reels = elist.getReels()
811         else:
812                 reels = {}
813         
814         REEL_UI.clear()
815         for reel_key, edits in reels.iteritems():
816                 
817                 if reel_key == 'bw':
818                         continue
819                 
820                 flag = 0
821                 for edit in edits:
822                         flag |= edit.edit_type
823                 
824                 reel_item = REEL_UI[reel_key] = ReelItemUI()
825                 
826                 reel_item.ui_text = '%s (%s): ' % (reel_key, editFlagsToText(flag))
827                 
828         Window.WaitCursor(0)
829
830 def edl_set_path(filename):
831         PREF['filename'].val = filename
832         edl_reload()
833         Draw.Redraw()
834         
835 def edl_set_path_reel(filename):
836         REEL_UI[PREF['reel_act']].filename_but.val = filename
837         Draw.Redraw()
838
839 def edl_reel_keys():
840         reel_keys = REEL_UI.keys()
841         
842         if 'bw' in reel_keys:
843                 reel_keys.remove('bw')
844         
845         reel_keys.sort()
846         return reel_keys
847
848 def edl_draw():
849         
850         MARGIN = 4
851         rect = BPyWindow.spaceRect()
852         but_width = int((rect[2]-MARGIN*2)/4.0) # 72
853         # Clamp
854         if but_width>100: but_width = 100
855         but_height = 17
856         
857         x=MARGIN
858         y=rect[3]-but_height-MARGIN
859         xtmp = x
860         
861         
862         
863         # ---------- ---------- ---------- ----------
864         Blender.Draw.BeginAlign()
865         PREF['filename'] =      Draw.String('edl path: ', B_EVENT_RELOAD, xtmp, y, (but_width*3)-20, but_height, PREF['filename'].val, 256, 'EDL Path'); xtmp += (but_width*3)-20;
866         Draw.PushButton('..',   B_EVENT_FILESEL_EDL, xtmp, y, 20, but_height,   'Select an EDL file'); xtmp += 20;
867         Blender.Draw.EndAlign()
868         
869         Draw.PushButton('Reload',       B_EVENT_RELOAD, xtmp + MARGIN, y, but_width - MARGIN, but_height,       'Read the ID Property settings from the active curve object'); xtmp += but_width;
870         y-=but_height + MARGIN
871         xtmp = x
872         # ---------- ---------- ---------- ----------
873         
874         reel_keys = edl_reel_keys()
875         
876         
877         
878         if reel_keys:                                           text = 'Reel file list...'
879         elif PREF['filename'].val == '':        text = 'No EDL loaded.'
880         else:                                                           text = 'No reels found!'
881
882         Draw.Label(text, xtmp + MARGIN, y, but_width*4, but_height); xtmp += but_width*4;
883         
884         y-=but_height + MARGIN
885         xtmp = x
886         
887         # ---------- ---------- ---------- ----------
888
889         
890         for i, reel_key in enumerate(reel_keys):
891                 reel_item = REEL_UI[reel_key]
892                 
893                 Blender.Draw.BeginAlign()
894                 REEL_UI[reel_key].filename_but = Draw.String(reel_item.ui_text, B_EVENT_NOP, xtmp, y, (but_width*3)-20, but_height, REEL_UI[reel_key].filename_but.val, 256, 'Select the reel path'); xtmp += (but_width*3)-20;
895                 Draw.PushButton('..',   B_EVENT_FILESEL + i, xtmp, y, 20, but_height,   'Media path to use for this reel'); xtmp += 20;
896                 Blender.Draw.EndAlign()
897                 
898                 reel_item.offset_but= Draw.Number('ofs:',       B_EVENT_NOP, xtmp + MARGIN, y, but_width - MARGIN, but_height, reel_item.offset_but.val, -100000, 100000,       'Start offset in frames when applying timecode'); xtmp += but_width - MARGIN;
899                 
900                 y-=but_height + MARGIN
901                 xtmp = x
902         
903         # ---------- ---------- ---------- ----------
904         
905         Draw.PushButton('Import CMX-EDL Sequencer Strips',      B_EVENT_IMPORT, xtmp + MARGIN, MARGIN, but_width*4 - MARGIN, but_height,        'Load the EDL file into the sequencer'); xtmp += but_width*4;
906         y-=but_height + MARGIN
907         xtmp = x
908
909
910 def edl_event(evt, val):
911         pass
912
913 def edl_bevent(evt):
914         
915         if evt == B_EVENT_NOP:
916                 pass
917         elif evt == B_EVENT_IMPORT:
918                 '''
919                 Load the file into blender with UI settings
920                 '''
921                 filename = PREF['filename'].val
922
923                 reel_files = {}
924                 reel_offsets = {}
925                 
926                 for reel_key, reel_item in REEL_UI.iteritems():
927                         reel_files[reel_key] = reel_item.filename_but.val
928                         reel_offsets[reel_key] = reel_item.offset_but.val
929                 
930                 error = load_edl(filename, reel_files, reel_offsets)
931                 if error != '':
932                         Draw.PupMenu('Error%t|' + error)
933                 else:
934                         Window.RedrawAll()
935                 
936         elif evt == B_EVENT_RELOAD:
937                 edl_reload()
938                 Draw.Redraw()
939                 
940         elif evt == B_EVENT_FILESEL_EDL:
941                 filename = PREF['filename'].val
942                 if not filename: filename = Blender.sys.join(Blender.sys.expandpath('//'), '*.edl')
943                         
944                 Window.FileSelector(edl_set_path, 'Select EDL', filename)
945                 
946         elif evt >= B_EVENT_FILESEL:
947                 reel_keys = edl_reel_keys()
948                 reel_key = reel_keys[evt - B_EVENT_FILESEL]
949                 
950                 filename = REEL_UI[reel_key].filename_but.val
951                 if not filename: filename = Blender.sys.expandpath('//')
952
953                 PREF['reel_act'] = reel_key # so file set path knows which one to set
954                 Window.FileSelector(edl_set_path_reel, 'Reel Media', filename)
955                 
956                 
957
958 if __name__ == '__main__':
959         Draw.Register(edl_draw, edl_event, edl_bevent)
960         edl_reload()
961