support merged download for specific resolution,format fixes #2353
This commit is contained in:
parent
6b58767d31
commit
c055c220e2
3 changed files with 50 additions and 62 deletions
|
@ -2,15 +2,16 @@
|
||||||
# vi:si:et:sw=4:sts=4:ts=4
|
# vi:si:et:sw=4:sts=4:ts=4
|
||||||
from __future__ import division, with_statement
|
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 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 import models, transaction
|
||||||
from django.db.models import Q, Sum, Max
|
from django.db.models import Q, Sum, Max
|
||||||
|
@ -451,6 +452,30 @@ class Item(models.Model):
|
||||||
other.save()
|
other.save()
|
||||||
#FIXME: update poster, stills and streams after this
|
#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):
|
def get_posters(self):
|
||||||
url = self.prefered_poster_url()
|
url = self.prefered_poster_url()
|
||||||
external_posters = self.external_data.get('posters', {})
|
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)
|
Q(file__is_audio=True)|Q(file__is_video=True)
|
||||||
).order_by('file__part', 'file__sort_path')
|
).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):
|
def update_timeline(self, force=False, async=True):
|
||||||
streams = self.streams()
|
streams = self.streams()
|
||||||
self.make_timeline()
|
self.make_timeline()
|
||||||
|
|
|
@ -12,6 +12,11 @@ urlpatterns = patterns("item.views",
|
||||||
(r'^(?P<id>[A-Z0-9].*)/timeline(?P<mode>[a-z]*)(?P<size>\d+)p(?P<position>\d+)\.(?P<format>png|jpg)$', 'timeline'),
|
(r'^(?P<id>[A-Z0-9].*)/timeline(?P<mode>[a-z]*)(?P<size>\d+)p(?P<position>\d+)\.(?P<format>png|jpg)$', 'timeline'),
|
||||||
(r'^(?P<id>[A-Z0-9].*)/timeline(?P<mode>[a-z]*)(?P<size>\d+)p\.(?P<format>png|jpg)$', 'timeline'),
|
(r'^(?P<id>[A-Z0-9].*)/timeline(?P<mode>[a-z]*)(?P<size>\d+)p\.(?P<format>png|jpg)$', 'timeline'),
|
||||||
|
|
||||||
|
#download
|
||||||
|
(r'^(?P<id>[A-Z0-9].*)/download$', 'download'),
|
||||||
|
(r'^(?P<id>[A-Z0-9].*)/download/$', 'download'),
|
||||||
|
(r'^(?P<id>[A-Z0-9].*)/download/(?P<resolution>\d+)p\.(?P<format>webm|ogv|mp4)$', 'download'),
|
||||||
|
|
||||||
#video
|
#video
|
||||||
(r'^(?P<id>[A-Z0-9].*)/(?P<resolution>\d+)p(?P<index>\d*)\.(?P<format>webm|ogv|mp4)$', 'video'),
|
(r'^(?P<id>[A-Z0-9].*)/(?P<resolution>\d+)p(?P<index>\d*)\.(?P<format>webm|ogv|mp4)$', 'video'),
|
||||||
|
|
||||||
|
@ -19,9 +24,6 @@ urlpatterns = patterns("item.views",
|
||||||
(r'^(?P<id>[A-Z0-9].*)/torrent$', 'torrent'),
|
(r'^(?P<id>[A-Z0-9].*)/torrent$', 'torrent'),
|
||||||
(r'^(?P<id>[A-Z0-9].*)/torrent/(?P<filename>.*?)$', 'torrent'),
|
(r'^(?P<id>[A-Z0-9].*)/torrent/(?P<filename>.*?)$', 'torrent'),
|
||||||
|
|
||||||
#download
|
|
||||||
(r'^(?P<id>[A-Z0-9].*)/download/$', 'download'),
|
|
||||||
|
|
||||||
#export
|
#export
|
||||||
(r'^(?P<id>[A-Z0-9].*)/json$', 'item_json'),
|
(r'^(?P<id>[A-Z0-9].*)/json$', 'item_json'),
|
||||||
(r'^(?P<id>[A-Z0-9].*)/xml$', 'item_xml'),
|
(r'^(?P<id>[A-Z0-9].*)/xml$', 'item_xml'),
|
||||||
|
|
|
@ -840,51 +840,24 @@ def timeline(request, id, size, position=-1, format='jpg', mode=None):
|
||||||
response.allow_access()
|
response.allow_access()
|
||||||
return response
|
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)
|
item = get_object_or_404(models.Item, itemId=id)
|
||||||
resolution = max(settings.CONFIG['video']['resolutions'])
|
if not resolution or int(resolution) not in settings.CONFIG['video']['resolutions']:
|
||||||
format = 'webm'
|
resolution = max(settings.CONFIG['video']['resolutions'])
|
||||||
|
|
||||||
if not item.access(request.user):
|
|
||||||
return HttpResponseForbidden()
|
|
||||||
if index:
|
|
||||||
index = int(index) - 1
|
|
||||||
else:
|
else:
|
||||||
index = 0
|
resolution = int(resolution)
|
||||||
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'])
|
|
||||||
if not item.access(request.user) or not item.rendered:
|
if not item.access(request.user) or not item.rendered:
|
||||||
return HttpResponseForbidden()
|
return HttpResponseForbidden()
|
||||||
ext = '.webm'
|
ext = '.%s' % format
|
||||||
filename = "%s - %s %s%s" % (
|
parts = ['%s - %s ' % (item.get('title'), settings.SITENAME), item.itemId]
|
||||||
item.get('title'),
|
if resolution != max(settings.CONFIG['video']['resolutions']):
|
||||||
settings.SITENAME,
|
parts.append('.%dp' % resolution)
|
||||||
item.itemId,
|
parts.append(ext)
|
||||||
ext
|
filename = ''.join(parts)
|
||||||
)
|
|
||||||
video = NamedTemporaryFile(suffix=ext)
|
video = NamedTemporaryFile(suffix=ext)
|
||||||
content_type = mimetypes.guess_type(video.name)[0]
|
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:
|
if not r:
|
||||||
return HttpResponseForbidden()
|
return HttpResponseForbidden()
|
||||||
elif r == True:
|
elif r == True:
|
||||||
|
|
Loading…
Reference in a new issue