fix timelines for items with many parts
- use durations from streams not from timelines - don't accumulate timeline drift
This commit is contained in:
parent
f0b8b2b81e
commit
94b940436f
2 changed files with 37 additions and 24 deletions
|
@ -1364,8 +1364,12 @@ class Item(models.Model):
|
||||||
def make_timeline(self):
|
def make_timeline(self):
|
||||||
streams = self.streams()
|
streams = self.streams()
|
||||||
if streams.count() > 1:
|
if streams.count() > 1:
|
||||||
timelines = [s.timeline_prefix for s in self.streams()]
|
timelines = []
|
||||||
join_tiles(timelines, self.timeline_prefix)
|
durations = []
|
||||||
|
for s in self.streams():
|
||||||
|
timelines.append(s.timeline_prefix)
|
||||||
|
durations.append(s.duration)
|
||||||
|
join_tiles(timelines, durations, self.timeline_prefix)
|
||||||
else:
|
else:
|
||||||
#remove joined timeline if it was created at some point
|
#remove joined timeline if it was created at some point
|
||||||
for f in glob(os.path.join(settings.MEDIA_ROOT, self.path(), 'timeline*.jpg')):
|
for f in glob(os.path.join(settings.MEDIA_ROOT, self.path(), 'timeline*.jpg')):
|
||||||
|
|
|
@ -10,7 +10,7 @@ import Image
|
||||||
from ox.utils import json
|
from ox.utils import json
|
||||||
|
|
||||||
|
|
||||||
def join_tiles(source_paths, target_path):
|
def join_tiles(source_paths, durations, target_path):
|
||||||
'''
|
'''
|
||||||
This is an implementation of a join_tiles function for new-style timelines.
|
This is an implementation of a join_tiles function for new-style timelines.
|
||||||
Timelines of files will be read from source_paths, the timeline of the item will
|
Timelines of files will be read from source_paths, the timeline of the item will
|
||||||
|
@ -104,24 +104,31 @@ def join_tiles(source_paths, target_path):
|
||||||
modes = ['antialias', 'slitscan', 'keyframes', 'keyframeswide', 'audio']
|
modes = ['antialias', 'slitscan', 'keyframes', 'keyframeswide', 'audio']
|
||||||
source_files = {}
|
source_files = {}
|
||||||
for mode in modes:
|
for mode in modes:
|
||||||
source_files[mode] = []
|
source_files[mode] = {}
|
||||||
|
|
||||||
# read files
|
# read files
|
||||||
durations = [0] * len(source_paths)
|
|
||||||
frame_n = 0
|
frame_n = 0
|
||||||
|
offset = 0
|
||||||
for i, path in enumerate(source_paths):
|
for i, path in enumerate(source_paths):
|
||||||
file_info = map(get_file_info, os.listdir(path))
|
file_info = map(get_file_info, os.listdir(path))
|
||||||
file_info = filter(lambda x: x != None, file_info)
|
file_info = filter(lambda x: x != None, file_info)
|
||||||
|
files = {}
|
||||||
|
for mode in modes:
|
||||||
|
files[mode] = []
|
||||||
for info in sorted(file_info, key=lambda x: x['index']):
|
for info in sorted(file_info, key=lambda x: x['index']):
|
||||||
mode = info['mode']
|
mode = info['mode']
|
||||||
source_files[mode].append(path + info['file'])
|
files[mode].append(path + info['file'])
|
||||||
|
if i:
|
||||||
|
offset = int(sum(durations[:i]) * 25)
|
||||||
|
for mode in files:
|
||||||
|
source_files[mode][offset] = files[mode]
|
||||||
modes = [m for m in modes if source_files[m]]
|
modes = [m for m in modes if source_files[m]]
|
||||||
for i, path in enumerate(source_paths):
|
offsets = sorted(source_files[modes[0]])
|
||||||
for f in source_files[modes[0]]:
|
last_offset = max(offsets)
|
||||||
if f.startswith(path):
|
frame_n = last_offset
|
||||||
width = Image.open(f).size[0]
|
for f in source_files[modes[0]][last_offset]:
|
||||||
durations[i] += width / fps
|
width = Image.open(f).size[0]
|
||||||
frame_n += width
|
frame_n += width
|
||||||
large_tile_n = int(math.ceil(frame_n / large_tile_w))
|
large_tile_n = int(math.ceil(frame_n / large_tile_w))
|
||||||
large_tile_last_w = frame_n % large_tile_w or 60
|
large_tile_last_w = frame_n % large_tile_w or 60
|
||||||
small_tile_n = int(math.ceil(frame_n / fps / small_tile_w))
|
small_tile_n = int(math.ceil(frame_n / fps / small_tile_w))
|
||||||
|
@ -149,19 +156,21 @@ def join_tiles(source_paths, target_path):
|
||||||
data['target_images'] = {'large': None, 'small': None, 'full': full_tile_image}
|
data['target_images'] = {'large': None, 'small': None, 'full': full_tile_image}
|
||||||
for mode in modes:
|
for mode in modes:
|
||||||
target_w = 0
|
target_w = 0
|
||||||
for source_file in source_files[mode]:
|
for offset in sorted(source_files[mode]):
|
||||||
source_image = Image.open(source_file)
|
target_w = offset
|
||||||
source_w = source_image.size[0]
|
for source_file in source_files[mode][offset]:
|
||||||
target_x = target_w % large_tile_w
|
source_image = Image.open(source_file)
|
||||||
if target_x == 0:
|
source_w = source_image.size[0]
|
||||||
save_and_open(data)
|
target_x = target_w % large_tile_w
|
||||||
data['target_images']['large'].paste(source_image, (target_x, 0))
|
if target_x == 0:
|
||||||
target_w += source_w
|
save_and_open(data)
|
||||||
if target_x + source_w > large_tile_w:
|
|
||||||
# target tile overflows into next source tile
|
|
||||||
save_and_open(data)
|
|
||||||
target_x -= large_tile_w
|
|
||||||
data['target_images']['large'].paste(source_image, (target_x, 0))
|
data['target_images']['large'].paste(source_image, (target_x, 0))
|
||||||
|
target_w += source_w
|
||||||
|
if target_x + source_w > large_tile_w:
|
||||||
|
# target tile overflows into next source tile
|
||||||
|
save_and_open(data)
|
||||||
|
target_x -= large_tile_w
|
||||||
|
data['target_images']['large'].paste(source_image, (target_x, 0))
|
||||||
# save_and_open saves previous tile and opens tile at target_w
|
# save_and_open saves previous tile and opens tile at target_w
|
||||||
# increase target_w to be in next tile
|
# increase target_w to be in next tile
|
||||||
target_w += large_tile_w
|
target_w += large_tile_w
|
||||||
|
|
Loading…
Reference in a new issue