diff --git a/pandora/config.0xdb.jsonc b/pandora/config.0xdb.jsonc index 7b300891e..bcae71254 100644 --- a/pandora/config.0xdb.jsonc +++ b/pandora/config.0xdb.jsonc @@ -38,6 +38,7 @@ "canAddItems": {"staff": true, "admin": true}, "canAddDocuments": {"staff": true, "admin": true}, "canDownloadVideo": {"guest": -1, "member": -1, "friend": -1, "staff": -1, "admin": -1}, + "canDownloadSource": {"guest": -1, "member": -1, "friend": -1, "staff": -1, "admin": -1}, "canEditAnnotations": {"staff": true, "admin": true}, "canEditEntities": {"staff": true, "admin": true}, "canEditDocuments": {"staff": true, "admin": true}, diff --git a/pandora/config.indiancinema.jsonc b/pandora/config.indiancinema.jsonc index 77f5b8559..408760542 100644 --- a/pandora/config.indiancinema.jsonc +++ b/pandora/config.indiancinema.jsonc @@ -39,6 +39,7 @@ "canAddItems": {"researcher": true, "staff": true, "admin": true}, "canAddDocuments": {"researcher": true, "staff": true, "admin": true}, "canDownloadVideo": {"guest": -1, "member": -1, "researcher": 3, "staff": 3, "admin": 3}, + "canDownloadSource": {"guest": -1, "member": -1, "researcher": -1, "staff": -1, "admin": -1}, "canEditAnnotations": {"staff": true, "admin": true}, "canEditDocuments": {"researcher": true, "staff": true, "admin": true}, "canEditEntities": {"staff": true, "admin": true}, diff --git a/pandora/config.padma.jsonc b/pandora/config.padma.jsonc index b406bb879..c78740a90 100644 --- a/pandora/config.padma.jsonc +++ b/pandora/config.padma.jsonc @@ -38,6 +38,7 @@ "canAddItems": {"member": true, "staff": true, "admin": true}, "canAddDocuments": {"member": true, "staff": true, "admin": true}, "canDownloadVideo": {"guest": 0, "member": 0, "staff": 4, "admin": 4}, + "canDownloadSource": {"guest": -1, "member": -1, "staff": 4, "admin": 4}, "canEditAnnotations": {"staff": true, "admin": true}, "canEditEntities": {"staff": true, "admin": true}, "canEditDocuments": {"staff": true, "admin": true}, diff --git a/pandora/config.pandora.jsonc b/pandora/config.pandora.jsonc index afec959ec..c418566f8 100644 --- a/pandora/config.pandora.jsonc +++ b/pandora/config.pandora.jsonc @@ -45,6 +45,7 @@ examples (config.SITENAME.jsonc) that are part of this pan.do/ra distribution. "canAddItems": {"member": true, "staff": true, "admin": true}, "canAddDocuments": {"member": true, "staff": true, "admin": true}, "canDownloadVideo": {"guest": 1, "member": 1, "staff": 4, "admin": 4}, + "canDownloadSource": {"member": 1, "staff": 4, "admin": 4}, "canEditAnnotations": {"staff": true, "admin": true}, "canEditDocuments": {"staff": true, "admin": true}, "canEditEntities": {"staff": true, "admin": true}, diff --git a/pandora/item/models.py b/pandora/item/models.py index ba9f7f616..dfd593df9 100644 --- a/pandora/item/models.py +++ b/pandora/item/models.py @@ -639,6 +639,9 @@ class Item(models.Model): if self.poster_height: i['posterRatio'] = self.poster_width / self.poster_height + if 'source' in keys: + i['source'] = self.files.filter(selected=True).exclude(data='').exists() + streams = self.streams() i['durations'] = [s.duration for s in streams] i['duration'] = sum(i['durations']) diff --git a/pandora/item/urls.py b/pandora/item/urls.py index 948a1878b..332da86de 100644 --- a/pandora/item/urls.py +++ b/pandora/item/urls.py @@ -16,6 +16,7 @@ urlpatterns = [ url(r'^(?P[A-Z0-9].*)/download$', views.download), url(r'^(?P[A-Z0-9].*)/download/$', views.download), url(r'^(?P[A-Z0-9].*)/download/source/(?P\d+)?$', views.download_source), + url(r'^(?P[A-Z0-9].*)/download/(?P\d+)p(?P\d+)\.(?Pwebm|ogv|mp4)$', views.download), url(r'^(?P[A-Z0-9].*)/download/(?P\d+)p\.(?Pwebm|ogv|mp4)$', views.download), #video diff --git a/pandora/item/views.py b/pandora/item/views.py index 182f70682..f5539b619 100644 --- a/pandora/item/views.py +++ b/pandora/item/views.py @@ -992,6 +992,8 @@ def download_source(request, id, part=None): raise Http404 parts = ['%s - %s ' % (item.get('title'), settings.SITENAME), item.public_id] + if len(streams) > 1: + parts.append('.Part %d' % (part + 1)) parts.append('.') parts.append(f.extension) filename = ''.join(parts) @@ -1002,7 +1004,7 @@ def download_source(request, id, part=None): response['Content-Disposition'] = "attachment; filename*=UTF-8''%s" % quote(filename.encode('utf-8')) return response -def download(request, id, resolution=None, format='webm'): +def download(request, id, resolution=None, format='webm', part=None): item = get_object_or_404(models.Item, public_id=id) if not resolution or int(resolution) not in settings.CONFIG['video']['resolutions']: resolution = max(settings.CONFIG['video']['resolutions']) @@ -1010,22 +1012,35 @@ def download(request, id, resolution=None, format='webm'): resolution = int(resolution) if not item.access(request.user) or not item.rendered: return HttpResponseForbidden() + if part is not None: + part = int(part) - 1 + streams = item.streams() + if part > len(streams): + raise Http404 ext = '.%s' % format parts = ['%s - %s ' % (item.get('title'), settings.SITENAME), item.public_id] if resolution != max(settings.CONFIG['video']['resolutions']): parts.append('.%dp' % resolution) + if part is not None: + parts.append('.Part %d' % (part + 1)) parts.append(ext) filename = ''.join(parts) video = NamedTemporaryFile(suffix=ext) content_type = mimetypes.guess_type(video.name)[0] - r = item.merge_streams(video.name, resolution, format) - if not r: - return HttpResponseForbidden() - elif r is True: - response = HttpResponse(FileWrapper(video), content_type=content_type) - response['Content-Length'] = os.path.getsize(video.name) + if part is None: + r = item.merge_streams(video.name, resolution, format) + if not r: + return HttpResponseForbidden() + elif r is True: + response = HttpResponse(FileWrapper(video), content_type=content_type) + response['Content-Length'] = os.path.getsize(video.name) + else: + response = HttpFileResponse(r, content_type=content_type) else: - response = HttpFileResponse(r, content_type=content_type) + stream = streams[part].get(resolution, format) + path = stream.media.path + content_type = mimetypes.guess_type(path)[0] + response = HttpFileResponse(path, content_type=content_type) response['Content-Disposition'] = "attachment; filename*=UTF-8''%s" % quote(filename.encode('utf-8')) return response diff --git a/static/js/downloadVideoDialog.js b/static/js/downloadVideoDialog.js index 214bcd217..88d58b7e5 100644 --- a/static/js/downloadVideoDialog.js +++ b/static/js/downloadVideoDialog.js @@ -9,6 +9,10 @@ pandora.ui.downloadVideoDialog = function(options) { 'mp4': 'MP4', }, + parts = Ox.max(options.video.map(function(video) { + return video.index + })), + $content = Ox.Element() .css({margin: '16px'}), @@ -27,6 +31,9 @@ pandora.ui.downloadVideoDialog = function(options) { .css({marginBottom: '16px'}) .appendTo($content), + $format, + $resolution, + $form = window.$form = Ox.Form({ items: [ Ox.Select({ @@ -36,7 +43,10 @@ pandora.ui.downloadVideoDialog = function(options) { id: format, title: formats[format] }; - }), + }).concat(!options.out && options.source ? [{ + id: 'source', + title: Ox._('Source') + }] : []), label: Ox._('Format'), labelWidth: 120, value: pandora.site.video.downloadFormat, @@ -44,9 +54,14 @@ pandora.ui.downloadVideoDialog = function(options) { }) .bindEvent({ change: function(data) { + if (data.value == 'source') { + $resolution.hide() + } else { + $resolution.show() + } } }), - Ox.Select({ + $resolution = Ox.Select({ id: 'resolution', items: pandora.site.video.resolutions.map(function(resolution) { return { @@ -63,7 +78,25 @@ pandora.ui.downloadVideoDialog = function(options) { change: function(data) { } }) - ] + ].concat(parts ? [ + Ox.Select({ + id: 'part', + items: Ox.range(parts + 1).map(function(resolution, idx) { + return { + id: idx + 1, + title: 'Part ' + (idx+1) + }; + }), + label: Ox._('Part'), + labelWidth: 120, + value: 1, + width: 240 + }) + .bindEvent({ + change: function(data) { + } + }) + ] : []) }).appendTo($content), failed = false, @@ -116,9 +149,17 @@ pandora.ui.downloadVideoDialog = function(options) { }) } else { - url = '/' + options.item - + '/download/' + values.resolution - + 'p.' + values.format + if (values.format == 'source') { + url = '/' + options.item + + '/download/source/' + + (values.part ? values.part : '') + } else { + url = '/' + options.item + + '/download/' + values.resolution + + 'p' + + (values.part ? values.part : '') + + '.' + values.format + } } if (url) { that.close(); diff --git a/static/js/editor.js b/static/js/editor.js index 0997ac84d..562c3bc0f 100644 --- a/static/js/editor.js +++ b/static/js/editor.js @@ -189,7 +189,9 @@ pandora.ui.editor = function(data) { pandora.ui.downloadVideoDialog({ item: ui.item, rightsLevel: rightsLevel, - title: data.title + source: data.source && pandora.hasCapability('canDownloadSource'), + title: data.title, + video: data.video }).open(); }, downloadselection: function(selection) { diff --git a/static/js/utils.js b/static/js/utils.js index ebdc5cd00..d2667546e 100644 --- a/static/js/utils.js +++ b/static/js/utils.js @@ -2359,6 +2359,7 @@ pandora.VIDEO_OPTIONS_KEYS = [ 'rendered', 'rightslevel', 'size', + 'source', 'streams', 'title', 'videoRatio'