From c055c220e2d9575c84a402d3cc82af6d932c5414 Mon Sep 17 00:00:00 2001 From: j <0x006A@0x2620.org> Date: Sat, 8 Mar 2014 11:18:12 +0000 Subject: [PATCH] support merged download for specific resolution,format fixes #2353 --- pandora/item/models.py | 53 ++++++++++++++++++++++++++---------------- pandora/item/urls.py | 8 ++++--- pandora/item/views.py | 51 ++++++++++------------------------------ 3 files changed, 50 insertions(+), 62 deletions(-) diff --git a/pandora/item/models.py b/pandora/item/models.py index 8e2a254d5..7d12ef727 100644 --- a/pandora/item/models.py +++ b/pandora/item/models.py @@ -2,15 +2,16 @@ # vi:si:et:sw=4:sts=4:ts=4 from __future__ import division, with_statement -from datetime import datetime -import os.path -import subprocess -from glob import glob -import shutil -import uuid -import unicodedata -from urllib import quote import json +import os +import shutil +import subprocess +import tempfile +import unicodedata +import uuid +from datetime import datetime +from glob import glob +from urllib import quote from django.db import models, transaction from django.db.models import Q, Sum, Max @@ -451,6 +452,30 @@ class Item(models.Model): other.save() #FIXME: update poster, stills and streams after this + def merge_streams(self, output, resolution=None, format="webm"): + streams = [s.get(resolution, format).media.path for s in self.streams()] + if len(streams) > 1: + if format == "webm": + first = True + cmd = ['mkvmerge', '-o', output] + cmd += [streams[0]] + ['+' + s for s in streams[1:]] + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p.wait() + return True + elif format == "mp4": + fd, tmp_output = tempfile.mkstemp('.mp4') + shutil.copy(streams[0], tmp_output) + for s in streams[1:]: + cmd = ['MP4Box', '-cat', s, tmp_output] + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p.wait() + shutil.copy(tmp_output, output) + os.unlink(tmp_output) + return True + else: + return None + return streams[0] if streams else None + def get_posters(self): url = self.prefered_poster_url() external_posters = self.external_data.get('posters', {}) @@ -1191,18 +1216,6 @@ class Item(models.Model): Q(file__is_audio=True)|Q(file__is_video=True) ).order_by('file__part', 'file__sort_path') - def merge_streams(self, output): - first = True - cmd = ['mkvmerge', '-o', output] - streams = [s.media.path for s in self.streams()] - if len(streams) > 1: - cmd += [streams[0]] + ['+' + s for s in streams[1:]] - print cmd - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - p.wait() - return True - return streams[0] if streams else None - def update_timeline(self, force=False, async=True): streams = self.streams() self.make_timeline() diff --git a/pandora/item/urls.py b/pandora/item/urls.py index 0fcd8fc97..4911431f1 100644 --- a/pandora/item/urls.py +++ b/pandora/item/urls.py @@ -12,6 +12,11 @@ urlpatterns = patterns("item.views", (r'^(?P[A-Z0-9].*)/timeline(?P[a-z]*)(?P\d+)p(?P\d+)\.(?Ppng|jpg)$', 'timeline'), (r'^(?P[A-Z0-9].*)/timeline(?P[a-z]*)(?P\d+)p\.(?Ppng|jpg)$', 'timeline'), + #download + (r'^(?P[A-Z0-9].*)/download$', 'download'), + (r'^(?P[A-Z0-9].*)/download/$', 'download'), + (r'^(?P[A-Z0-9].*)/download/(?P\d+)p\.(?Pwebm|ogv|mp4)$', 'download'), + #video (r'^(?P[A-Z0-9].*)/(?P\d+)p(?P\d*)\.(?Pwebm|ogv|mp4)$', 'video'), @@ -19,9 +24,6 @@ urlpatterns = patterns("item.views", (r'^(?P[A-Z0-9].*)/torrent$', 'torrent'), (r'^(?P[A-Z0-9].*)/torrent/(?P.*?)$', 'torrent'), - #download - (r'^(?P[A-Z0-9].*)/download/$', 'download'), - #export (r'^(?P[A-Z0-9].*)/json$', 'item_json'), (r'^(?P[A-Z0-9].*)/xml$', 'item_xml'), diff --git a/pandora/item/views.py b/pandora/item/views.py index 55bd6b3a1..5cc91fdfe 100644 --- a/pandora/item/views.py +++ b/pandora/item/views.py @@ -840,51 +840,24 @@ def timeline(request, id, size, position=-1, format='jpg', mode=None): response.allow_access() return response -def download(request, id, index=1): +def download(request, id, resolution=None, format='webm'): + print 'download', id, resolution, format item = get_object_or_404(models.Item, itemId=id) - resolution = max(settings.CONFIG['video']['resolutions']) - format = 'webm' - - if not item.access(request.user): - return HttpResponseForbidden() - if index: - index = int(index) - 1 + if not resolution or int(resolution) not in settings.CONFIG['video']['resolutions']: + resolution = max(settings.CONFIG['video']['resolutions']) else: - index = 0 - streams = item.streams() - if index + 1 > streams.count(): - raise Http404 - stream = streams[index].get(resolution, format) - if not stream.available or not stream.media: - raise Http404 - path = stream.media.path - content_type = mimetypes.guess_type(path)[0] - ext = os.path.splitext(path)[-1] - filename = "%s - %s %s%s" % ( - item.get('title'), - settings.SITENAME, - item.itemId, - ext - ) - response = HttpFileResponse(path, content_type=content_type) - response['Content-Disposition'] = "attachment; filename*=UTF-8''%s" % quote(filename.encode('utf-8')) - return response - -def download(request, id): - item = get_object_or_404(models.Item, itemId=id) - resolution = max(settings.CONFIG['video']['resolutions']) + resolution = int(resolution) if not item.access(request.user) or not item.rendered: return HttpResponseForbidden() - ext = '.webm' - filename = "%s - %s %s%s" % ( - item.get('title'), - settings.SITENAME, - item.itemId, - ext - ) + ext = '.%s' % format + parts = ['%s - %s ' % (item.get('title'), settings.SITENAME), item.itemId] + if resolution != max(settings.CONFIG['video']['resolutions']): + parts.append('.%dp' % resolution) + parts.append(ext) + filename = ''.join(parts) video = NamedTemporaryFile(suffix=ext) content_type = mimetypes.guess_type(video.name)[0] - r = item.merge_streams(video.name) + r = item.merge_streams(video.name, resolution, format) if not r: return HttpResponseForbidden() elif r == True: