add firefogg.jsm, this should move back to firefogg later
This commit is contained in:
parent
edc69304e0
commit
166268a30c
1 changed files with 299 additions and 0 deletions
299
OxFF/modules/firefogg.jsm
Normal file
299
OxFF/modules/firefogg.jsm
Normal file
|
@ -0,0 +1,299 @@
|
||||||
|
// -*- coding: utf-8 -*-
|
||||||
|
// vi:si:et:sw=2:sts=2:ts=2
|
||||||
|
|
||||||
|
let EXPORTED_SYMBOLS = [ "FirefoggUploader" ];
|
||||||
|
|
||||||
|
const Cc = Components.classes;
|
||||||
|
const Ci = Components.interfaces;
|
||||||
|
|
||||||
|
function FirefoggUploader(parent, path, url, data) {
|
||||||
|
this._parent = parent;
|
||||||
|
this._path = path;
|
||||||
|
this._url = url;
|
||||||
|
this._chunkUrl = false;
|
||||||
|
this._data = data;
|
||||||
|
this._status = 'initiated';
|
||||||
|
this.encodingStatus = 'encoding';
|
||||||
|
this.progress = 0.0;
|
||||||
|
this.chunkSize = 1024*1024; //1MB, is there a need to make this variable?
|
||||||
|
this.chunksUploaded = 0;
|
||||||
|
this.chunkStatus = {};
|
||||||
|
this._done_cb = function(upload) {};
|
||||||
|
this._onProgress = function(progress) {};
|
||||||
|
this.filename = 'video.ogv';
|
||||||
|
this.canceled = false;
|
||||||
|
this._retries = 0;
|
||||||
|
this._maxRetry = -1;
|
||||||
|
this.ready = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Internal helper object dealing with chunked upload as defined at
|
||||||
|
http://firefogg.org/dev/chunk_post.html
|
||||||
|
*/
|
||||||
|
FirefoggUploader.prototype = {
|
||||||
|
start: function() {
|
||||||
|
var boundary = "--------XX" + Math.random();
|
||||||
|
|
||||||
|
this._status = 'requesting chunk upload';
|
||||||
|
|
||||||
|
var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
|
||||||
|
|
||||||
|
//XMLHttpRequest status callbacks
|
||||||
|
var upload = this;
|
||||||
|
function transferComplete(evt) {
|
||||||
|
var response = {};
|
||||||
|
upload.responseText = evt.target.responseText;
|
||||||
|
try {
|
||||||
|
response = JSON.parse(evt.target.responseText);
|
||||||
|
//dump(evt.target.responseText);
|
||||||
|
} catch(e) {
|
||||||
|
dump('FAILED to parse response:\n\n');
|
||||||
|
dump(evt.target.responseText);
|
||||||
|
response = {};
|
||||||
|
}
|
||||||
|
if (response.maxRetry) {
|
||||||
|
upload._maxRetry = response.maxRetry;
|
||||||
|
}
|
||||||
|
upload._chunkUrl = response.uploadUrl;
|
||||||
|
if (upload._chunkUrl) {
|
||||||
|
//dump('now tracking chunk uploads to ' + upload._chunkUrl + '\n');
|
||||||
|
upload._status = 'uploading';
|
||||||
|
upload._progress = 0.0;
|
||||||
|
upload._uploadChunkId = 0;
|
||||||
|
upload.uploadChunk(0, false);
|
||||||
|
} else {
|
||||||
|
upload._status = 'upload failed, no upload url provided';
|
||||||
|
upload._progress = -1;
|
||||||
|
upload._parent.cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function transferFailed(evt) {
|
||||||
|
upload._status = 'uplaod failed';
|
||||||
|
upload._progress = -1;
|
||||||
|
upload.responseText = evt.target.responseText;
|
||||||
|
//dump(evt.target.responseText);
|
||||||
|
}
|
||||||
|
req.addEventListener("load", transferComplete, false);
|
||||||
|
req.addEventListener("error", transferFailed, false);
|
||||||
|
|
||||||
|
req.open("POST", this._url);
|
||||||
|
|
||||||
|
var formData = "";
|
||||||
|
for (key in this._data) {
|
||||||
|
formData += "--" + boundary + "\r\n" +
|
||||||
|
"Content-Disposition: form-data; name=\""+key+"\"\r\n\r\n" + this._data[key] + "\r\n";
|
||||||
|
}
|
||||||
|
formData += "--" + boundary + "--\r\n";
|
||||||
|
|
||||||
|
var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||||
|
converter.charset = "UTF-8";
|
||||||
|
var formStream = converter.convertToInputStream(formData);
|
||||||
|
|
||||||
|
req.setRequestHeader("Content-type", "multipart/form-data; boundary=" + boundary);
|
||||||
|
req.setRequestHeader("Content-length", formStream.available());
|
||||||
|
req.send(formStream);
|
||||||
|
},
|
||||||
|
onProgress: function(cb) {
|
||||||
|
this._onProgress = cb;
|
||||||
|
},
|
||||||
|
done: function(done_cb) {
|
||||||
|
//used by Firefogg to indicate that encoding is done
|
||||||
|
//provies callback that is called once upload is done too
|
||||||
|
this.encodingStatus = 'done';
|
||||||
|
this._done_cb = done_cb;
|
||||||
|
},
|
||||||
|
uploadChunk: function(chunkId, last) {
|
||||||
|
/*
|
||||||
|
upload chunk with given chunkId, last indicated if this is the last chunk.
|
||||||
|
once upload is done next chunk is queued
|
||||||
|
*/
|
||||||
|
var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
|
||||||
|
file.initWithPath(this._path);
|
||||||
|
this.filename = file.leafName;
|
||||||
|
this.filename = this.filename.replace(/\(\.\d+\).ogv/, '.ogv');
|
||||||
|
this.filename = this.filename.replace(/\(\.\d+\).webm/, '.webm');
|
||||||
|
|
||||||
|
var fileStream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
|
||||||
|
fileStream.init(file, -1, -1, false);
|
||||||
|
fileStream.QueryInterface(Ci.nsISeekableStream);
|
||||||
|
var f = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
|
||||||
|
f.setInputStream(fileStream);
|
||||||
|
|
||||||
|
var bytesAvailable = fileStream.available();
|
||||||
|
var chunk = false;
|
||||||
|
var chunkOffset = chunkId * this.chunkSize;
|
||||||
|
this.progress = parseFloat(chunkOffset)/bytesAvailable;
|
||||||
|
|
||||||
|
if (last) {
|
||||||
|
//last chunk, read add remaining data
|
||||||
|
fileStream.seek(fileStream.NS_SEEK_SET, chunkOffset);
|
||||||
|
chunk = f.readBytes(bytesAvailable-chunkOffset);
|
||||||
|
}
|
||||||
|
else if (this.ready && bytesAvailable >= chunkOffset + this.chunkSize) {
|
||||||
|
//read next chunk
|
||||||
|
fileStream.seek(fileStream.NS_SEEK_SET, chunkOffset);
|
||||||
|
chunk = f.readBytes(this.chunkSize);
|
||||||
|
} else {
|
||||||
|
if (this.encodingStatus == 'done' && this._status == 'uploading') {
|
||||||
|
//uploading is done and last chunk is < chunkSize, upload remaining data
|
||||||
|
this._status = 'success';
|
||||||
|
this.uploadChunk(chunkId, true);
|
||||||
|
} else {
|
||||||
|
//encoding not ready. wait for 2 seconds and try again
|
||||||
|
var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||||
|
timer.initWithCallback(
|
||||||
|
function(obj, chunkId, last) {
|
||||||
|
return function() { obj.uploadChunk(chunkId, last) }
|
||||||
|
}(this, chunkId, last),
|
||||||
|
2000, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||||
|
}
|
||||||
|
f.close();
|
||||||
|
fileStream.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
f.close();
|
||||||
|
fileStream.close();
|
||||||
|
|
||||||
|
//FIXME: should this be checked so it does not get called again?
|
||||||
|
this.chunkStatus[chunkId] = {};
|
||||||
|
|
||||||
|
//dump('POST ' + this._chunkUrl + ' uploading chunk ' + chunkId +' ('+chunk.length+' bytes)\n');
|
||||||
|
|
||||||
|
var boundary = "--------XX" + Math.random();
|
||||||
|
|
||||||
|
var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
|
||||||
|
|
||||||
|
var upload = this;
|
||||||
|
function transferComplete(evt) {
|
||||||
|
//if server resonds with valid {result=1} and status was is still 'uploading' go on to next chunk
|
||||||
|
upload.responseText = evt.target.responseText;
|
||||||
|
try {
|
||||||
|
var response = JSON.parse(evt.target.responseText);
|
||||||
|
} catch(e) {
|
||||||
|
dump('FAILED to parse response:\n\n');
|
||||||
|
dump(evt.target.responseText);
|
||||||
|
var response = {};
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
if (upload.canceled) {
|
||||||
|
//FIXME: should we let the server know that upload was canceled?
|
||||||
|
}
|
||||||
|
else */
|
||||||
|
|
||||||
|
/*
|
||||||
|
dump('\n\n');
|
||||||
|
dump(evt.target.responseText);
|
||||||
|
dump('\n************\n\n');
|
||||||
|
*/
|
||||||
|
if (response.done == 1) {
|
||||||
|
//upload finished, update state and expose result
|
||||||
|
upload.resultUrl = response.resultUrl;
|
||||||
|
upload.progress = 1;
|
||||||
|
if (upload._done_cb)
|
||||||
|
upload._done_cb(upload);
|
||||||
|
//reset retry counter
|
||||||
|
upload._retries = 0;
|
||||||
|
}
|
||||||
|
else if (response.result == 1) {
|
||||||
|
//start uploading next chunk
|
||||||
|
upload._uploadChunkId = chunkId + 1;
|
||||||
|
upload.uploadChunk(upload._uploadChunkId, false);
|
||||||
|
//upload status
|
||||||
|
upload.chunkStatus[chunkId].progress = 1;
|
||||||
|
upload.chunkStatus[chunkId].loaded = evt.loaded;
|
||||||
|
upload.chunksUploaded++;
|
||||||
|
//reset retry counter
|
||||||
|
upload._retries = 0;
|
||||||
|
} else {
|
||||||
|
//failed to upload, try again in 3 second
|
||||||
|
//dump('could not parse response, failed to upload, will try again in 3 second\n');
|
||||||
|
//dump(evt.target.responseText);
|
||||||
|
if (upload.max_retry > 0 && upload._retries > upload.max_retry) {
|
||||||
|
upload.cancel();
|
||||||
|
upload._status = 'uplaod failed';
|
||||||
|
upload._progress = -1;
|
||||||
|
} else {
|
||||||
|
upload._retries++;
|
||||||
|
var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||||
|
timer.initWithCallback(
|
||||||
|
function(obj, chunkId, last) {
|
||||||
|
return function() { obj.uploadChunk(chunkId, last); };
|
||||||
|
}(upload, chunkId, last), 3000, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function transferFailed(evt) {
|
||||||
|
//failed to upload, try again in 3 second
|
||||||
|
//dump('transferFailed, will try again in 3 second\n');
|
||||||
|
if (upload.max_retry > 0 && upload._retries > upload.max_retry) {
|
||||||
|
upload.cancel();
|
||||||
|
upload._status = 'uplaod failed';
|
||||||
|
upload._progress = -1;
|
||||||
|
} else {
|
||||||
|
upload._retries++;
|
||||||
|
var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||||
|
timer.initWithCallback(
|
||||||
|
function(obj, chunkId, last) {
|
||||||
|
return function() { obj.uploadChunk(chunkId, last); };
|
||||||
|
}(upload, chunkId, last), 3000, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function updateProgress(evt) {
|
||||||
|
if (evt.lengthComputable) {
|
||||||
|
upload.progress = parseFloat(chunkOffset + evt.loaded)/bytesAvailable;
|
||||||
|
upload.chunkStatus[chunkId].loaded = evt.loaded;
|
||||||
|
upload.chunkStatus[chunkId].loaded = evt.total;
|
||||||
|
//dump('progress: chunk ' + chunkId + ' uploaded ' + evt.loaded + '\n');
|
||||||
|
upload._onProgress(upload.progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req.upload.addEventListener("progress", updateProgress, false);
|
||||||
|
|
||||||
|
req.addEventListener("load", transferComplete, false);
|
||||||
|
req.addEventListener("error", transferFailed, false);
|
||||||
|
|
||||||
|
req.open("POST", this._chunkUrl);
|
||||||
|
|
||||||
|
var chunk = "--" + boundary + "\r\n" +
|
||||||
|
"Content-Disposition: form-data; name=\"chunk\"; filename=\"" + this.filename + "\"\r\n" +
|
||||||
|
"Content-type: video/ogg\r\n\r\n" + chunk;
|
||||||
|
var chunkStream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
|
||||||
|
chunkStream.setData(chunk, chunk.length);
|
||||||
|
|
||||||
|
// write done flag into stream
|
||||||
|
var formData = "\r\n";
|
||||||
|
var _data = {
|
||||||
|
chunkId: chunkId
|
||||||
|
};
|
||||||
|
if (last) {
|
||||||
|
_data['done'] = 1;
|
||||||
|
}
|
||||||
|
for (key in _data) {
|
||||||
|
formData += "--" + boundary + "\r\n" +
|
||||||
|
"Content-Disposition: form-data; name=\""+key+"\"\r\n\r\n" + _data[key] + "\r\n";
|
||||||
|
}
|
||||||
|
formData += "--" + boundary + "--\r\n";
|
||||||
|
var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||||
|
converter.charset = "UTF-8";
|
||||||
|
var formStream = converter.convertToInputStream(formData);
|
||||||
|
|
||||||
|
var multiStream = Cc["@mozilla.org/io/multiplex-input-stream;1"].createInstance(Ci.nsIMultiplexInputStream);
|
||||||
|
multiStream.appendStream(chunkStream);
|
||||||
|
multiStream.appendStream(formStream);
|
||||||
|
|
||||||
|
//send mupltiplrex stream
|
||||||
|
req.setRequestHeader("Content-type", "multipart/form-data; boundary=" + boundary);
|
||||||
|
req.setRequestHeader("Content-length", multiStream.available());
|
||||||
|
//this._current_req = req;
|
||||||
|
req.send(multiStream);
|
||||||
|
},
|
||||||
|
cancel: function() {
|
||||||
|
this.canceled = true;
|
||||||
|
if (this._current_req) {
|
||||||
|
this._current_req.abort();
|
||||||
|
this._current_req = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
Loading…
Reference in a new issue