def truncate(seq, **kwargs): """ truncate clips to absolute framerange. Everything outside start-end range will get dropped or clipped """ start = get_frame(kwargs['start'], kwargs['fps'], kwargs['tc_start']) end = get_frame(kwargs['end'], kwargs['fps'], kwargs['tc_start']) n = 0 # n stores length of previous clips combined h = seq.frame_amount() # default to trim head and tail fully t = seq.frame_amount() for clip in seq.clips: # Each clip can be described as line # frame = step * increment + offset offset = clip['start'] inc = clip['increment'] clip_len = get_clip_len(clip) # Solve steps where clip passes frames start and end t1 = ((start - offset) / float(inc)) t2 = ((end - offset) / float(inc)) # This is the range where t1,t2 solve and our clip overlap range_start = max(min(t1, t2), 0) range_end = min(max(t1, t2), clip_len - 1) # If range start and end are in logical order our clip contains frames that will survive the trimming # if several clips fit the bill, min() makes sure that we trim keeping as many of them as possible still in seq. if range_start <= range_end: h = min(h, n + int(ceil(range_start))) t = min(t, (seq.frame_amount() - 1) - (n + int(floor(range_end)))) n += clip_len return h, t
def offset(seqlist, **kwargs): def offset_each(seq, amount): """ simple offset by X frames """ def offset_clip(clip, amount): clip['start'] += amount clip['end'] += amount return clip ret_seq = copy.deepcopy(seq) try: ret_seq.clips = map(offset_clip, ret_seq.clips, [amount] * len(ret_seq.clips)) except AttributeError: pass return ret_seq def calculate_offset(new, old): return new - old def offset_based_on_start(seqlist, new_start): ret = [] for seq in seqlist: try: amount = calculate_offset(new_start, seq.clips[0]['start']) ret.append(offset_each(seq, amount)) except AttributeError: ret.append(seq) return ret def offset_based_on_end(seqlist, new_end): ret = [] for seq in seqlist: try: amount = calculate_offset(new_end, seq.clips[-1]['end']) ret.append(offset_each(seq, amount)) except AttributeError: ret.append(seq) return ret ret_seqs = [] if kwargs.has_key('amount'): # Simple offset by amount amount = get_frame(kwargs['amount'], kwargs['fps'], kwargs['tc_start']) ret_seqs = map(offset_each, seqlist, [amount] * len(seqlist)) else: if kwargs.has_key('start_at'): ret_seqs = offset_based_on_start(seqlist, get_frame(kwargs['start_at'], kwargs['fps'], kwargs['tc_start'])) if kwargs.has_key('end_at'): ret_seqs = offset_based_on_end(seqlist, get_frame(kwargs['end_at'], kwargs['fps'], kwargs['tc_start'])) return ret_seqs
def minseq(original_seqlist, modified_seqlist, **kwargs): amount = get_frame(kwargs['amount'], kwargs['fps']) if amount <= 1: return original_seqlist, modified_seqlist # Early terminate for short min limit clean_list = filter(lambda pair: not is_single(pair[1]), zip(original_seqlist, modified_seqlist)) # Purge nonsequence items long_seqs = filter(lambda pair: pair[1].frame_amount() >= amount, clean_list) if long_seqs != []: return zip(*long_seqs) else: return [], []
def trim(seq, **kwargs): """ trim by 'head' and 'tail' from start and end of seq """ # If we are provided with integer, input refers to amount of actual frames to trim # If it is timecode it means trim N seconds:frames so we need to calculate how many actual frames this means # in the context of this sequence as this might skip frames try: h = int(kwargs['head']) t = int(kwargs['tail']) except ValueError: head = get_frame(kwargs['head'], kwargs['fps']) tail = get_frame(kwargs['tail'], kwargs['fps']) first_frame, last_frame = get_relative_frames(seq, head, tail) if first_frame != None and last_frame !=None: h, t = truncate(seq, start=first_frame, end=last_frame, fps=kwargs['fps'], tc_start=0) else: h = seq.frame_amount() t = h return h, t
def head_tail(seq, **kwargs): """ get N frames from head or tail of seq, returns instruction usable with absolute_trim_sequence """ def absolute_head_tail(seq, is_head, amount): """ Simple case of taking 'amount' frames from head or tail """ if is_head: h = 0 t = seq.frame_amount() - amount if t < 0: t = 0 else: h = seq.frame_amount() - amount t = 0 if h < 0: h = 0 return h, t def relative_head_tail(seq, is_head, amount): """ how many frames to remove from seq, if amount refers to timecode. """ # We assume that person giving timecode is actually interested shortening the clip by certain time amount # and whether sequence skips frames or not is not relevant # test.[0-50x2@@@@].dpx --head 25 takes 25 first filenames and becomes test.[0-48x2@@@@].dpx # test.[0-50x2@@@@].dpx --head 01:00 takes one second of sequence (numbers 0-24) and becomes test.[0-24x2@@@@].dpx seq_len = max(seq.clips[-1]['end'], seq.clips[0]['start']) - min(seq.clips[0]['start'], seq.clips[-1]['end']) + 1 # Sequence length in timecode, not actual frames if is_head: first_frame, last_frame = get_relative_frames(seq, 0, seq_len - amount) else: first_frame, last_frame = get_relative_frames(seq, seq_len - amount, 0) if first_frame != None and last_frame != None: h, t = truncate(seq, start=first_frame, end=last_frame, fps=kwargs['fps'], tc_start=0) else: h = seq.frame_amount() t = h return h, t is_head = kwargs['is_head'] try: # We are passed frames, take literally 'amount' frames from head or tail amount = int(kwargs['amount']) return absolute_head_tail(seq, is_head, amount) except ValueError: # We are passed timecode, figure out what that means in terms of absolute frames amount = get_frame(kwargs['amount'], kwargs['fps']) return relative_head_tail(seq, is_head, amount)
def maxseq(original_seqlist, modified_seqlist, **kwargs): amount = get_frame(kwargs['amount'], kwargs['fps']) ret_original = [] ret_modified = [] for pair in zip(original_seqlist, modified_seqlist): if is_single(pair[1]): ret_original.append(pair[0]) ret_modified.append(pair[1]) else: if pair[1].frame_amount() <= amount: ret_original.append(pair[0]) ret_modified.append(pair[1]) return ret_original, ret_modified