From 8947e51243b92e6fc49ec4f7d852059cc2ae3ac5 Mon Sep 17 00:00:00 2001 From: j <0x006A@0x2620.org> Date: Wed, 8 Sep 2010 15:38:44 +0200 Subject: [PATCH] strip timeline --- pandora/archive/extract.py | 68 ++++++++++++++++++++++++++++++++++- pandora/backend/models.py | 7 ++-- pandora/backend/urls.py | 2 +- pandora/backend/views.py | 11 +++--- pandora/static/js/timeline.js | 9 +++-- 5 files changed, 85 insertions(+), 12 deletions(-) diff --git a/pandora/archive/extract.py b/pandora/archive/extract.py index e7d88ee01..6117cc969 100644 --- a/pandora/archive/extract.py +++ b/pandora/archive/extract.py @@ -260,7 +260,7 @@ def average_color(prefix): for i in range(0, len(pixels)): p = np.sum(pixels[i], axis=0) / frames color += p - return list(color) + return list(map(float, color)) def get_distance(rgb0, rgb1): dst = math.sqrt(pow(rgb0[0] - rgb1[0], 2) + pow(rgb0[0] - rgb1[0], 2) + pow(rgb0[0] - rgb1[0], 2)) @@ -292,3 +292,69 @@ def cuts(prefix): cuts.append(frame / fps) return cuts +def divide(num, by): + # >>> divide(100, 3) + # [33, 33, 34] + arr = [] + div = int(num / by) + mod = num % by + for i in range(by): + arr.append(div + (i > by - 1 - mod)) + return arr + +def strip_timeline(movie, cuts, info, prefix): + _debug = False + duration = info['duration'] + video_height = info['video'][0]['height'] + video_width = info['video'][0]['width'] + video_ratio = video_width / video_height + + line_image = [] + timeline_height = 64 + timeline_width = 1500 + fps = 25 + frames = duration * fps + if cuts[0] != 0: + cuts.insert(0, 0) + + cuts = map(lambda x: int(round(x * fps)), cuts) + + for frame in range(frames): + i = int(frame / timeline_width) + x = frame % timeline_width + if x == 0: + timeline_width = min(timeline_width, frames - frame) + timeline_image = Image.new('RGB', (timeline_width, timeline_height)) + if frame in cuts: + c = cuts.index(frame) + duration = cuts[c + 1] - cuts[c] + stills = math.ceil(duration / (video_width * timeline_height / video_height)) + widths = divide(duration, stills) + still = frame + if _debug: + print widths, duration, stills, cuts[c], cuts[c + 1] + for s in range(int(stills)): + still_ratio = widths[s] / timeline_height + if video_ratio > still_ratio: + width = int(round(video_height * still_ratio)) + left = int((video_width - width) / 2) + box = (left, 0, left + width, video_height) + else: + height = int(round(video_width / still_ratio)) + top = int((video_height - height) / 2) + box = (0, top, video_width, top + height) + if _debug: + print frame, 'cut', c, 'still', s, still, 'width', widths[s], box + #FIXME: why does this have to be still+1? + frame_image = Image.open(movie.frame((still+1)/fps)) + frame_image = frame_image.crop(box).resize((widths[s], timeline_height), Image.ANTIALIAS) + for x_ in range(widths[s]): + line_image.append(frame_image.crop((x_, 0, x_ + 1, timeline_height))) + still += widths[s] + timeline_image.paste(line_image[frame], (x, 0)) + if x == timeline_width - 1: + timeline_file = '%sstrip.64.%04d.png' % (prefix, i) + if _debug: + print 'writing', timeline_file + timeline_image.save(timeline_file) + diff --git a/pandora/backend/models.py b/pandora/backend/models.py index 0c42603ae..b6d8fd352 100644 --- a/pandora/backend/models.py +++ b/pandora/backend/models.py @@ -415,7 +415,7 @@ class Movie(models.Model): @property def timeline_prefix(self): - return os.path.join('stream', movieid_path(self.movieId), 'timeline') + return os.path.join(settings.MEDIA_ROOT, 'stream', movieid_path(self.movieId), 'timeline') def updateStreams(self): files = {} @@ -440,9 +440,10 @@ class Movie(models.Model): subprocess.Popen(cmd) stream.save() - extract.timeline(stream.video.path, os.path.join(settings.MEDIA_ROOT, self.timeline_prefix)) + extract.timeline(stream.video.path, self.timeline_prefix) stream.extract_derivatives() - + self.metadata['cuts'] = extract.cuts(self.timeline_prefix) + self.metadata['average_color'] = extract.average_color(self.timeline_prefix) #something with poster self.available = True self.save() diff --git a/pandora/backend/urls.py b/pandora/backend/urls.py index 9fdaf7fb1..477479b78 100644 --- a/pandora/backend/urls.py +++ b/pandora/backend/urls.py @@ -10,7 +10,7 @@ urlpatterns = patterns("backend.views", (r'^(?P.*)/(?P.*.mp4)$', 'video'), (r'^(?P.*)/poster\.(?P\d+)\.jpg$', 'poster'), (r'^(?P.*)/poster\.jpg$', 'poster'), - (r'^(?P.*)/timelines/timeline\.(?P\d+)\.(?P\d+)\.png$', 'timeline'), + (r'^(?P.*)/timelines/(?P.+)\.(?P\d+)\.(?P\d+)\.png$', 'timeline'), (r'^(?P.*)/data/(?P.+)\.json$', 'data'), (r'^api/$', 'api'), ) diff --git a/pandora/backend/views.py b/pandora/backend/views.py index a8f845384..95a87a9b9 100644 --- a/pandora/backend/views.py +++ b/pandora/backend/views.py @@ -540,6 +540,8 @@ def data(request, id, data): response = {} if data == 'video': response = movie.get_stream() + if data == 'cuts': + response = movie.metadata.get('cuts', {}) return render_to_json_response(response) #media delivery @@ -570,9 +572,12 @@ def poster(request, id, size=128): poster_path = os.path.join(settings.STATIC_ROOT, 'png/posterDark.48.png') return HttpFileResponse(poster_path, content_type='image/jpeg') -def timeline(request, id, size, position): +def timeline(request, id, timeline, size, position): movie = get_object_or_404(models.Movie, movieId=id) - timeline = os.path.join(settings.MEDIA_ROOT, '%s.%s.%04d.png' %(movie.timeline_prefix, size, int(position))) + if timeline == 'strip': + timeline = '%s.%s.%04d.png' %(movie.timeline_prefix[:-8] + 'strip', size, int(position)) + else: + timeline = '%s.%s.%04d.png' %(movie.timeline_prefix, size, int(position)) return HttpFileResponse(timeline, content_type='image/png') def video(request, id, profile): @@ -580,7 +585,5 @@ def video(request, id, profile): stream = get_object_or_404(movie.streams, profile=profile) path = stream.video.path content_type = path.endswith('.mp4') and 'video/mp4' or 'video/webm' - #url = 'http://127.0.0.1/pandora_media' + path[len(settings.MEDIA_ROOT):] - #return redirect(url) return HttpFileResponse(path, content_type=content_type) diff --git a/pandora/static/js/timeline.js b/pandora/static/js/timeline.js index 2865f8fa5..11664a5f4 100644 --- a/pandora/static/js/timeline.js +++ b/pandora/static/js/timeline.js @@ -23,8 +23,9 @@ $(function() { posterFrame = 1515, points = [2059, 2748], videoId = document.location.hash.substring(1), - videoUrl = "/" + videoId + "/96p." + ($.support.video.webm ? "webm": "mp4"); - + videoUrl = "/" + videoId + "/96p." + ($.support.video.webm ? "webm": "mp4"). + stripTimeline = false; + $.getJSON("/" + videoId + "/data/video.json", function(video) { var duration = video.duration, videoRatio = video.aspectRatio, @@ -33,6 +34,8 @@ $(function() { position = duration/2; videoWidth += videoWidth%2; + videoUrl = video.baseUrl + "/96p." + ($.support.video.webm ? "webm": "mp4"); + //resizeVideoPlayers(pageWidth); Ox.Editor = function(options, self) { @@ -577,7 +580,7 @@ $(function() { if (!self.$tiles[v]) { self.$tiles[v] = $("") .attr({ - src: "/" + self.options.videoId + "/timelines/" + (window.location.hash == "#strip" ? "strip" : "timeline") + ".64." + v + ".png" + src: "/" + self.options.videoId + "/timelines/" + (stripTimeline ? "strip" : "timeline") + ".64." + v + ".png" }) .css({ left: (v * self.tileWidth) + "px"