From b047c0e9c9c221daacdb9f3354e558c76f062967 Mon Sep 17 00:00:00 2001 From: j <0x006A@0x2620.org> Date: Tue, 26 Mar 2013 14:11:19 +0000 Subject: [PATCH] Add UI to support video upload without Firefogg, fixes #1351; split encoding/upload into two 100% progress steps, fixes #1453 --- static/js/pandora/filesView.js | 6 +- static/js/pandora/importAnnotations.js | 10 +- static/js/pandora/upload.js | 180 ----------------------- static/js/pandora/uploadDialog.js | 194 +++++++++++++++---------- 4 files changed, 127 insertions(+), 263 deletions(-) delete mode 100644 static/js/pandora/upload.js diff --git a/static/js/pandora/filesView.js b/static/js/pandora/filesView.js index 86a8590f..a6d33166 100644 --- a/static/js/pandora/filesView.js +++ b/static/js/pandora/filesView.js @@ -76,7 +76,7 @@ pandora.ui.filesView = function(options, self) { columns: [ { clickable: function(data) { - return true; + return !data.encoding; }, format: function(value, data) { return $('') @@ -99,7 +99,9 @@ pandora.ui.filesView = function(options, self) { title: 'Status', titleImage: 'check', tooltip: function (data) { - return data.instances.filter(function(i) {return i.ignore; }).length > 0 + return data.encoding + ? 'Processing video on server' + : data.instances.filter(function(i) {return i.ignore; }).length > 0 ? 'Use this file' : 'Dont use this file'; }, diff --git a/static/js/pandora/importAnnotations.js b/static/js/pandora/importAnnotations.js index 599b6b1a..2fde7ab7 100644 --- a/static/js/pandora/importAnnotations.js +++ b/static/js/pandora/importAnnotations.js @@ -14,7 +14,7 @@ pandora.ui.importAnnotations = function(data) { importButton, selectLayer, selectFile, - that = pandora.ui.iconDialog({ + that = Ox.Dialog({ buttons: [ Ox.Button({ id: 'close', @@ -36,11 +36,13 @@ pandora.ui.importAnnotations = function(data) { }) ], closeButton: true, - text: content, + content: content, keys: { 'escape': 'close' }, + height: 128, removeOnClose: true, + width: 368, title: 'Import Annotations' }) .bindEvent({ @@ -77,7 +79,9 @@ pandora.ui.importAnnotations = function(data) { layer: layer }, function(result) { if (result.data.taskId) { - setStatus('Waiting for server to import annotations...'); + $status.html('').append(Ox.LoadingScreen({ + text: 'Importing ' + srt.length + ' annotations...' + })); pandora.wait(result.data.taskId, function(result) { if(result.data.status == 'SUCCESS') { setStatus(annotations.length + ' annotations imported.'); diff --git a/static/js/pandora/upload.js b/static/js/pandora/upload.js deleted file mode 100644 index bd76d554..00000000 --- a/static/js/pandora/upload.js +++ /dev/null @@ -1,180 +0,0 @@ -// vi:si:et:sw=4:sts=4:ts=4 -'use strict'; - -pandora.ui.upload = function(oshash, file) { - var self = {}, - chunkSize = 1024*1024, - chunkUrl, - format = pandora.site.video.formats[0], - maxRetry = -1, - resolution = Ox.max(pandora.site.video.resolutions), - retries = 0, - that = Ox.Element(), - uploadData = {}, - uploadUrl = '/api/upload/?profile='+resolution+'p.'+format+'&id=' + oshash; - - initUpload(); - - function done() { - that.triggerEvent('done', { - status: that.status, - progress: that.progress, - responseText: that.responseText - }); - } - - function initUpload() { - //request upload slot from server - that.status = 'requesting chunk upload'; - that.progress = 0; - self.req = new XMLHttpRequest(); - self.req.addEventListener('load', function (evt) { - var response = {}; - that.responseText = evt.target.responseText; - try { - response = JSON.parse(evt.target.responseText); - } catch(e) { - response = {}; - that.status = 'failed to parse response'; - that.progress = -1; - done(); - } - if (response.status && response.status.code != 200) { - that.status = response.status.text; - that.progress = -1; - done(); - response = {}; - } - if (response.maxRetry) { - maxRetry = response.maxRetry; - } - chunkUrl = response.uploadUrl; - if (chunkUrl) { - if (document.location.protocol == 'https:') { - chunkUrl = chunkUrl.replace(/http:\/\//, 'https://'); - } - that.status = 'uploading'; - that.progress = 0.0; - //start upload - uploadChunk(0); - } else { - that.status = 'upload failed, no upload url provided'; - that.progress = -1; - done(); - } - }, false); - self.req.addEventListener('error', function (evt) { - that.status = 'uplaod failed'; - that.progress = -1; - that.responseText = evt.target.responseText; - done(); - }, false); - self.req.addEventListener('abort', function (evt) { - that.status = 'aborted'; - that.progress = -1; - done(); - }, false); - var formData = new FormData(); - Ox.forEach(uploadData, function(value, key) { - formData.append(key, value); - }); - self.req.open('POST', uploadUrl); - self.req.send(formData); - } - - function progress(p) { - that.progress = p; - that.triggerEvent('progress', { - progress: that.progress - }); - } - - function uploadChunk(chunkId) { - var bytesAvailable = file.size, - chunk, - chunkOffset = chunkId * chunkSize; - - if (file.mozSlice) { - chunk = file.mozSlice(chunkOffset, chunkOffset+chunkSize, file.type); - } else if (file.webkitSlice) { - chunk = file.webkitSlice(chunkOffset, chunkOffset+chunkSize, file.type); - } - - progress(parseFloat(chunkOffset)/bytesAvailable); - - self.req = new XMLHttpRequest(); - self.req.addEventListener('load', function (evt) { - var response; - that.responseText = evt.target.responseText; - try { - response = JSON.parse(evt.target.responseText); - } catch(e) { - response = {}; - } - if (response.done == 1) { - //upload finished - that.resultUrl = response.resultUrl; - that.progress = 1; - that.status = 'done'; - done(); - } else if (response.result == 1) { - //reset retry counter - retries = 0; - //start uploading next chunk - uploadChunk(chunkId + 1); - } else { - //failed to upload, try again in 5 second - retries++; - if (maxRetry > 0 && retries > maxRetry) { - that.status = 'uplaod failed'; - that.progress = -1; - done(); - } else { - setTimeout(function() { - uploadChunk(chunkId); - }, 5000); - } - } - }, false); - self.req.addEventListener('error', function (evt) { - //failed to upload, try again in 3 second - retries++; - if (maxRetry > 0 && retries > maxRetry) { - that.status = 'uplaod failed'; - that.progress = -1; - done(); - } else { - setTimeout(function() { - uploadChunk(chunkId); - }, 3000); - } - }, false); - self.req.upload.addEventListener('progress', function (evt) { - if (evt.lengthComputable) { - progress(parseFloat(chunkOffset + evt.loaded) / bytesAvailable); - } - }, false); - self.req.addEventListener('abort', function (evt) { - that.status = 'aborted'; - that.progress = -1; - done(); - }, false); - - var formData = new FormData(); - formData.append('chunkId', chunkId); - if (bytesAvailable <= chunkOffset + chunkSize) { - formData.append('done', 1); - } - formData.append('chunk', chunk); - self.req.open('POST', chunkUrl, true); - self.req.send(formData); - } - - that.abort = function() { - if (self.req) { - self.req.abort(); - self.req = null; - } - }; - return that; -}; diff --git a/static/js/pandora/uploadDialog.js b/static/js/pandora/uploadDialog.js index 9d5e7c64..33f89fcf 100644 --- a/static/js/pandora/uploadDialog.js +++ b/static/js/pandora/uploadDialog.js @@ -5,6 +5,7 @@ pandora.ui.uploadDialog = function(data) { var cancelled = false, file, + hasFirefogg = !(typeof Firefogg == 'undefined'), selectFile, $actionButton, $closeButton, @@ -18,12 +19,22 @@ pandora.ui.uploadDialog = function(data) { $closeButton = Ox.Button({ id: 'close', title: 'Close' + }).css({ + float: 'left' }).bindEvent({ click: function() { - that.triggerEvent('close'); + if ($closeButton.options('title') == 'Cancel') { + cancelled = true; + pandora.firefogg && pandora.firefogg.cancel(); + pandora.$ui.upload && pandora.$ui.upload.abort(); + $closeButton.options('title', 'Close'); + $actionButton.show(); + } else { + that.triggerEvent('close'); + } } }), - $actionButton = Ox.Button({ + $actionButton = hasFirefogg ? Ox.Button({ id: 'action', title: 'Select Video' }).bindEvent({ @@ -39,11 +50,24 @@ pandora.ui.uploadDialog = function(data) { $actionButton.options('title', 'Select Video'); $closeButton.show(); } else { - $actionButton.options('title', 'Cancel'); - $closeButton.hide(); + $closeButton.options('title', 'Cancel'); + $actionButton.hide().options('title', 'Select Video'); encode(); } } + }) : Ox.FileButton({ + id: 'action', + title: 'Select Video', + maxFiles: 1, + width: 96 + }).bindEvent({ + click: function(data) { + if(data.files.length) { + $actionButton.hide(); + $closeButton.options('title', 'Cancel'); + upload(data.files[0]); + } + } }) ], content: $content, @@ -62,9 +86,6 @@ pandora.ui.uploadDialog = function(data) { } }); - // FIXME: is this necessary? - pandora._status = $status; - pandora._info = $info; if (!pandora.site.itemRequiresVideo && !pandora.user.ui.item) { $info.html( 'You can only upload a video to an existing ' @@ -74,44 +95,6 @@ pandora.ui.uploadDialog = function(data) { + ' you want to upload exists and create otherwise.' ); $actionButton.hide(); - } else if (typeof Firefogg == 'undefined') { - /* - selectFile = $('') - .attr({ - type: 'file' - }) - .css({ - padding: '8px' - }) - .on({ - change: function(event) { - if (this.files.length) { - file = this.files[0]; - if (file.type == 'video/webm') { - $status.html(''); - uploadButton.options({ - disabled: false - }); - } else { - $status.html('Currently only WebM files are supported. (Help encoding video)'); - } - } else { - uploadButton.options({ - disabled: true - }); - } - } - }) - .appendTo($content); - */ - $info.html( - 'Currently, video upload is only supported in ' - + 'Firefox, with ' - + 'Firefogg installed.

' - + 'Alternatively, you can use ' - + 'pandora_client.' - ); - $actionButton.hide(); } $content.append($info); $content.append($status); @@ -138,14 +121,14 @@ pandora.ui.uploadDialog = function(data) { }; } - function resetProgress() { + function resetProgress(status) { $progress = Ox.Progressbar({ progress: 0, showPercent: true, showTime: true, width: 304 }); - $status.html('').append($progress); + $status.html(status || '').append($progress); } function encode() { @@ -153,6 +136,7 @@ pandora.ui.uploadDialog = function(data) { info = JSON.parse(pandora.firefogg.sourceInfo), item, oshash = info.oshash; + $info.html('' + filename + '
encoding...'); resetProgress(); pandora.api.addMedia({ filename: filename, @@ -171,46 +155,100 @@ pandora.ui.uploadDialog = function(data) { return; } setTimeout(function() { - //$status.html('uploading... '); - pandora.$ui.upload = pandora.ui.upload(oshash, file) - .bindEvent({ - progress: function(data) { - var progress = data.progress || 0; - $progress.options({progress: 0.5 + progress / 2}); - }, - done: function(data) { - if (data.progress == 1) { - Ox.Request.clearCache(); - if (pandora.user.ui.item == item && pandora.user.ui.itemView == 'files') { - pandora.$ui.item.reload(); - } else { - pandora.UI.set({ - item: item, - itemView: 'files' - }); - } - delete pandora.firefogg; - that.close(); - } else { - $status.html('Upload Failed.'); - pandora.api.log({ - text: data.responseText, - url: '/' + item, - line: 1 - }); - } - } - }); + $info.html('' + filename + '
uploading...'); + uploadStream(item, oshash, file); }); }, function(progress) { progress = JSON.parse(progress).progress || 0; - $progress.options({progress: progress / 2}); + $progress.options({progress: progress}); } ); }); } + function uploadStream(item, oshash, file) { + var format = pandora.site.video.formats[0], + resolution = Ox.max(pandora.site.video.resolutions); + pandora.$ui.upload = pandora.chunkupload({ + file: file, + url: '/api/upload/?profile=' + resolution + 'p.' + format + '&id=' + oshash, + data: {} + }).bindEvent({ + done: function(data) { + if (data.progress == 1) { + Ox.Request.clearCache(); + if (pandora.user.ui.item == item && pandora.user.ui.itemView == 'files') { + pandora.$ui.item.reload(); + } else { + pandora.UI.set({ + item: item, + itemView: 'files' + }); + } + delete pandora.firefogg; + that.close(); + } else { + $status.html('Upload Failed.'); + pandora.api.log({ + text: data.responseText, + url: '/' + item, + line: 1 + }); + } + }, + progress: function(data) { + $progress.options({progress: data.progress || 0}); + }, + }); + } + + function upload(file) { + resetProgress(); + $info.html('Uploading ' + file.name); + Ox.oshash(file, function(oshash) { + pandora.api.addMedia({ + filename: file.name, + id: oshash, + item: pandora.site.itemRequiresVideo ? undefined : pandora.user.ui.item + }, function(result) { + var item = result.data.item; + pandora.$ui.upload = pandora.chunkupload({ + file: file, + url: '/api/upload/direct/', + data: { + id: oshash + } + }).bindEvent({ + done: function(data) { + if (data.progress == 1) { + Ox.Request.clearCache(); + if (pandora.user.ui.item == item && pandora.user.ui.itemView == 'files') { + pandora.$ui.item.reload(); + } else { + pandora.UI.set({ + item: item, + itemView: 'files' + }); + } + that.close(); + } else { + $status.html(cancelled ? 'Upload cancelled.' : 'Upload failed.'); + !cancelled && pandora.api.log({ + text: data.responseText, + url: '/' + item, + line: 1 + }); + } + }, + progress: function(data) { + $progress.options({progress: data.progress || 0}); + } + }); + }); + }); + } + function getEncodingOptions(info) { var bpp = 0.17, dar,