2013-01-29 13:47:40 +01:00
|
|
|
angular
|
|
|
|
.module('webui.ctrls.download', [
|
2014-02-24 07:46:11 +01:00
|
|
|
"ui.bootstrap",
|
2013-02-25 11:50:42 +01:00
|
|
|
'webui.services.utils', 'webui.services.rpc', 'webui.services.alerts',
|
|
|
|
'webui.services.settings', 'webui.services.modals'
|
2013-01-29 13:47:40 +01:00
|
|
|
])
|
2013-02-25 11:50:42 +01:00
|
|
|
.controller('DownloadCtrl', [
|
|
|
|
'$scope', '$rpc', '$utils', '$alerts', '$modals',
|
|
|
|
'$fileSettings', '$activeInclude', '$waitingExclude',
|
2013-07-17 16:10:24 +02:00
|
|
|
// for document title
|
|
|
|
'$window',
|
2013-02-25 11:50:42 +01:00
|
|
|
function(
|
|
|
|
scope, rpc, utils, alerts, modals,
|
2013-07-17 16:10:24 +02:00
|
|
|
fsettings, activeInclude, waitingExclude, window
|
2013-02-25 11:50:42 +01:00
|
|
|
) {
|
2014-02-23 22:57:29 +01:00
|
|
|
|
|
|
|
var re_slashes = /\\/g;
|
|
|
|
var slash = "/";
|
|
|
|
|
2013-01-17 17:18:23 +01:00
|
|
|
scope.active = [], scope.waiting = [], scope.stopped = [];
|
2013-03-20 08:45:28 +01:00
|
|
|
scope.gstats = {};
|
2013-01-15 09:24:09 +01:00
|
|
|
|
2013-01-22 11:12:40 +01:00
|
|
|
// pause the download
|
|
|
|
// d: the download ctx
|
|
|
|
scope.pause = function(d) {
|
|
|
|
rpc.once('forcePause', [d.gid]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// resume the download
|
|
|
|
// d: the download ctx
|
|
|
|
scope.resume = function(d) {
|
|
|
|
rpc.once('unpause', [d.gid]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// remove the download,
|
|
|
|
// put it in stopped list if active,
|
|
|
|
// otherwise permanantly remove it
|
|
|
|
// d: the download ctx
|
2013-03-18 15:59:40 +01:00
|
|
|
scope.remove = function(d, cb) {
|
2013-01-22 13:11:38 +01:00
|
|
|
var method = 'remove';
|
2013-03-18 15:59:40 +01:00
|
|
|
|
2013-01-22 13:11:38 +01:00
|
|
|
if (scope.getType(d) == 'stopped')
|
|
|
|
method = 'removeDownloadResult';
|
|
|
|
|
2013-03-18 15:59:40 +01:00
|
|
|
rpc.once(method, [d.gid], cb);
|
|
|
|
|
|
|
|
// also remove it from client cache assuming that it will be deleted in the aria2 list,
|
|
|
|
// but we could be wrong but the cache will update in next global update
|
|
|
|
var downloads = [scope.active, scope.waiting, scope.stopped], ind = -1, i;
|
|
|
|
for (i = 0; i < downloads.length; i++) {
|
|
|
|
ind = downloads[i].indexOf(d);
|
|
|
|
if (ind != -1) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ind == -1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
downloads[i].splice(ind, 1);
|
2013-01-22 11:12:40 +01:00
|
|
|
}
|
2013-01-15 09:24:09 +01:00
|
|
|
|
2013-01-22 13:11:38 +01:00
|
|
|
scope.restart = function(d) {
|
|
|
|
var uris =
|
|
|
|
_.chain(d.files).map(function(f) { return f.uris })
|
|
|
|
.filter(function(uris) { return uris.length })
|
|
|
|
.map(function(uris) {
|
|
|
|
return _.chain(uris)
|
|
|
|
.map(function(u) { return u.uri })
|
|
|
|
.uniq().value();
|
|
|
|
}).value();
|
|
|
|
|
|
|
|
if (uris.length > 0) {
|
2013-03-18 15:59:40 +01:00
|
|
|
scope.remove(d, function() {
|
|
|
|
rpc.once('addUri', uris, angular.noop, true);
|
2013-01-22 13:11:38 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2013-01-22 11:12:40 +01:00
|
|
|
|
|
|
|
// start filling in the model of active,
|
|
|
|
// waiting and stopped download
|
2013-01-15 09:24:09 +01:00
|
|
|
rpc.subscribe('tellActive', [], function(data) {
|
2013-03-12 07:51:44 +01:00
|
|
|
utils.mergeMap(data[0], scope.active, scope.getCtx);
|
2013-01-15 09:24:09 +01:00
|
|
|
});
|
|
|
|
|
2013-01-21 15:07:55 +01:00
|
|
|
rpc.subscribe('tellWaiting', [0, 1000], function(data) {
|
2013-03-12 07:51:44 +01:00
|
|
|
utils.mergeMap(data[0], scope.waiting, scope.getCtx);
|
2013-01-15 09:24:09 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
2013-01-21 15:07:55 +01:00
|
|
|
rpc.subscribe('tellStopped', [0, 1000], function(data) {
|
2013-03-12 07:51:44 +01:00
|
|
|
utils.mergeMap(data[0], scope.stopped, scope.getCtx);
|
2013-01-15 09:24:09 +01:00
|
|
|
});
|
|
|
|
|
2013-03-19 21:57:52 +01:00
|
|
|
rpc.subscribe('getGlobalStat', [], function(data) {
|
|
|
|
scope.gstats = data[0];
|
2013-07-17 16:10:24 +02:00
|
|
|
window.document.title = utils.getTitle(scope.gstats);
|
|
|
|
|
2013-03-19 21:57:52 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
rpc.once('getVersion', [], function(data) {
|
|
|
|
scope.miscellaneous = data[0];
|
|
|
|
});
|
|
|
|
|
|
|
|
// total number of downloads, updates dynamically as downloads are
|
|
|
|
// stored in scope
|
|
|
|
scope.totalDownloads = 0;
|
|
|
|
|
|
|
|
// download search filter
|
|
|
|
scope.downloadFilter = "";
|
2014-02-24 02:50:23 +01:00
|
|
|
scope.downloadFilterCommitted = "";
|
|
|
|
|
|
|
|
scope.onDownloadFilter = function() {
|
|
|
|
if (scope.downloadFilterTimer) {
|
|
|
|
clearTimeout(scope.downloadFilterTimer);
|
|
|
|
}
|
|
|
|
scope.downloadFilterTimer = setTimeout(function() {
|
|
|
|
delete scope.downloadFilterTimer;
|
|
|
|
if (scope.downloadFilterCommitted !== scope.downloadFilter) {
|
|
|
|
scope.downloadFilterCommitted = scope.downloadFilter;
|
|
|
|
scope.$digest();
|
|
|
|
}
|
|
|
|
}, 500);
|
|
|
|
};
|
2013-03-19 21:57:52 +01:00
|
|
|
|
|
|
|
scope.filterDownloads = function(downloads) {
|
2014-02-24 02:50:23 +01:00
|
|
|
if (!scope.downloadFilterCommitted) {
|
|
|
|
return downloads;
|
|
|
|
}
|
2014-02-20 20:14:27 +01:00
|
|
|
var filter = scope.downloadFilterCommitted.
|
|
|
|
replace(/[{}()\[\]\\^$.?]/g, "\\$&").
|
|
|
|
replace(/\*/g, ".*").
|
|
|
|
replace(/\./g, ".");
|
|
|
|
filter = new RegExp(filter, "i");
|
2013-03-19 21:57:52 +01:00
|
|
|
return _.filter(downloads, function(d) {
|
2014-02-20 20:14:27 +01:00
|
|
|
if (filter.test(d.name)) return true;
|
2013-03-19 21:57:52 +01:00
|
|
|
return _.filter(d.files, function(f) {
|
2014-02-20 20:14:27 +01:00
|
|
|
return filter.test(f.relpath);
|
2013-03-19 21:57:52 +01:00
|
|
|
}).length;
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2014-02-20 20:14:27 +01:00
|
|
|
scope.clearFilter = function() {
|
|
|
|
scope.downloadFilter = scope.downloadFilterCommitted = "";
|
|
|
|
};
|
|
|
|
|
|
|
|
scope.toggleStateFilters = function() {
|
|
|
|
scope.filterActive = !scope.filterActive;
|
|
|
|
scope.filterWaiting = !scope.filterWaiting;
|
|
|
|
scope.filterComplete = !scope.filterComplete;
|
|
|
|
scope.filterError = !scope.filterError;
|
|
|
|
scope.filterPaused = !scope.filterPaused;
|
|
|
|
scope.filterRemoved = !scope.filterRemoved;
|
|
|
|
};
|
|
|
|
|
|
|
|
scope.resetFilters = function() {
|
|
|
|
scope.filterActive =
|
|
|
|
scope.filterWaiting =
|
|
|
|
scope.filterComplete =
|
|
|
|
scope.filterError =
|
|
|
|
scope.filterPaused =
|
|
|
|
scope.filterRemoved =
|
|
|
|
true;
|
|
|
|
scope.clearFilter();
|
|
|
|
};
|
|
|
|
|
|
|
|
scope.resetFilters();
|
|
|
|
|
|
|
|
|
2013-03-16 09:13:06 +01:00
|
|
|
// max downloads shown in one page
|
|
|
|
scope.pageSize = 10;
|
|
|
|
|
|
|
|
// current displayed page
|
|
|
|
scope.currentPage = 1;
|
|
|
|
|
2013-03-19 21:57:52 +01:00
|
|
|
// total amount of downloads returned by aria2
|
|
|
|
scope.totalAria2Downloads = function() {
|
|
|
|
return scope.active.length
|
|
|
|
+ scope.waiting.length
|
|
|
|
+ scope.stopped.length;
|
|
|
|
}
|
|
|
|
|
2013-01-22 11:12:40 +01:00
|
|
|
// actual downloads used by the view
|
2013-01-15 09:24:09 +01:00
|
|
|
scope.getDownloads = function() {
|
2014-02-20 20:14:27 +01:00
|
|
|
var downloads = [];
|
|
|
|
if (scope.filterActive) {
|
|
|
|
downloads = scope.active;
|
|
|
|
}
|
|
|
|
if (scope.filterWaiting) {
|
|
|
|
downloads = downloads.concat(_.filter(scope.waiting, function (e) {
|
|
|
|
return e.status == "waiting";
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
if (scope.filterPaused) {
|
|
|
|
downloads = downloads.concat(_.filter(scope.waiting, function (e) {
|
|
|
|
return e.status == "paused";
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
if (scope.filterError) {
|
|
|
|
downloads = downloads.concat(_.filter(scope.stopped, function (e) {
|
|
|
|
return e.status == "error";
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
if (scope.filterComplete) {
|
|
|
|
downloads = downloads.concat(_.filter(scope.stopped, function (e) {
|
|
|
|
return e.status == "complete";
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
if (scope.filterRemoved) {
|
|
|
|
downloads = downloads.concat(_.filter(scope.stopped, function (e) {
|
|
|
|
return e.status == "removed";
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
downloads = scope.filterDownloads(downloads);
|
2013-03-16 09:13:06 +01:00
|
|
|
|
|
|
|
scope.totalDownloads = downloads.length;
|
|
|
|
|
|
|
|
downloads = downloads.slice( (scope.currentPage - 1) * scope.pageSize );
|
|
|
|
downloads.splice( scope.pageSize );
|
|
|
|
|
|
|
|
return downloads;
|
|
|
|
}
|
|
|
|
|
2013-01-22 11:12:40 +01:00
|
|
|
// convert the donwload form aria2 to once used by the view,
|
|
|
|
// minor additions of some fields and checks
|
2013-01-17 17:18:23 +01:00
|
|
|
scope.getCtx = function(d, ctx) {
|
2014-02-24 02:50:23 +01:00
|
|
|
if (!ctx) {
|
|
|
|
ctx = {
|
|
|
|
dir: d.dir,
|
|
|
|
status: d.status,
|
|
|
|
gid: d.gid,
|
|
|
|
numPieces: d.numPieces,
|
|
|
|
connections: d.connections,
|
|
|
|
bitfield: d.bitfield,
|
|
|
|
totalLength: d.totalLength,
|
|
|
|
fmtTotalLength: utils.fmtsize(d.totalLength),
|
|
|
|
completedLength: d.completedLength,
|
|
|
|
fmtCompletedLength: utils.fmtsize(d.completedLength),
|
|
|
|
uploadLength: d.uploadLength,
|
|
|
|
fmtUploadLength: utils.fmtsize(d.uploadLength),
|
|
|
|
pieceLength: d.pieceLength,
|
|
|
|
fmtPieceLength: utils.fmtsize(d.pieceLength),
|
|
|
|
downloadSpeed: d.downloadSpeed,
|
|
|
|
fmtDownloadSpeed: utils.fmtspeed(d.downloadSpeed),
|
|
|
|
uploadSpeed: d.uploadSpeed,
|
|
|
|
fmtUploadSpeed: utils.fmtspeed(d.uploadSpeed),
|
|
|
|
files: []
|
|
|
|
};
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ctx.dir = d.dir;
|
|
|
|
ctx.status = d.status;
|
|
|
|
ctx.gid = d.gid;
|
|
|
|
ctx.numPieces = d.numPieces;
|
|
|
|
ctx.connections = d.connections;
|
|
|
|
ctx.bitfield = d.bitfield;
|
|
|
|
if (ctx.totalLength !== d.totalLength) {
|
|
|
|
ctx.totalLength = d.totalLength;
|
|
|
|
ctx.fmtTotalLength = utils.fmtsize(d.totalLength);
|
|
|
|
}
|
|
|
|
if (ctx.completedLength !== d.completedLength) {
|
|
|
|
ctx.completedLength = d.completedLength;
|
|
|
|
ctx.fmtCompletedLength = utils.fmtsize(d.completedLength);
|
|
|
|
}
|
|
|
|
if (ctx.uploadLength !== d.uploadength) {
|
|
|
|
ctx.uploadLength = d.uploadlength;
|
|
|
|
ctx.fmtUploadLength = utils.fmtsize(d.uploadLength);
|
|
|
|
}
|
|
|
|
if (ctx.pieceLength !== d.pieceLength) {
|
|
|
|
ctx.pieceLength = d.pieceLength;
|
|
|
|
ctx.fmtPieceLength = utils.fmtsize(d.pieceLength);
|
|
|
|
}
|
|
|
|
if (ctx.downloadSpeed !== d.downloadSpeed) {
|
|
|
|
ctx.downloadSpeed = d.downloadSpeed;
|
|
|
|
ctx.fmtDownloadSpeed = utils.fmtspeed(d.downloadSpeed);
|
|
|
|
}
|
|
|
|
if (ctx.uploadSpeed !== d.uploadSpeed) {
|
|
|
|
ctx.uploadSpeed = d.uploadSpeed;
|
|
|
|
ctx.fmtUploadSpeed = utils.fmtspeed(d.uploadSpeed);
|
|
|
|
}
|
|
|
|
}
|
2013-01-19 18:46:22 +01:00
|
|
|
|
2014-02-24 02:50:23 +01:00
|
|
|
var dlName;
|
|
|
|
var files = d.files;
|
2014-01-11 10:36:17 +01:00
|
|
|
if (files) {
|
2014-02-24 02:50:23 +01:00
|
|
|
var cfiles = ctx.files;
|
2014-01-11 10:36:17 +01:00
|
|
|
for (var i = 0; i < files.length; ++i) {
|
2014-03-14 15:49:25 +01:00
|
|
|
var cfile = cfiles[i] || (cfiles[i] = {});
|
2014-02-23 22:57:29 +01:00
|
|
|
var file = files[i];
|
|
|
|
if (file.path !== cfile.path) {
|
|
|
|
cfile.path = file.path;
|
|
|
|
cfile.length = file.length;
|
2014-02-24 02:50:23 +01:00
|
|
|
cfile.fmtLength = utils.fmtsize(file.length);
|
2014-02-23 22:57:29 +01:00
|
|
|
cfile.relpath = file.path.replace(re_slashes, slash);
|
2014-02-24 02:50:23 +01:00
|
|
|
if (!cfile.relpath) {
|
|
|
|
cfile.relpath = (file.uris && file.uris[0] && file.uris[0].uri) || "Unknown";
|
|
|
|
}
|
|
|
|
else if (!cfile.relpath.startsWith("[")) { // METADATA
|
2014-02-23 22:57:29 +01:00
|
|
|
cfile.relpath = cfile.relpath.substr(ctx.dir.length + 1);
|
|
|
|
}
|
|
|
|
}
|
2014-01-11 10:36:17 +01:00
|
|
|
}
|
|
|
|
cfiles.length = files.length;
|
2014-02-27 01:35:32 +01:00
|
|
|
if (cfiles.length) {
|
|
|
|
dlName = cfiles[0].relpath;
|
|
|
|
}
|
2014-01-11 10:36:17 +01:00
|
|
|
}
|
|
|
|
else {
|
2014-02-24 02:50:23 +01:00
|
|
|
delete ctx.files;
|
2014-01-11 10:36:17 +01:00
|
|
|
}
|
|
|
|
|
2014-02-24 02:50:23 +01:00
|
|
|
var btName;
|
2014-01-11 10:36:17 +01:00
|
|
|
if (d.bittorrent) {
|
2014-02-24 02:50:23 +01:00
|
|
|
btName = d.bittorrent.info && d.bittorrent.info.name;
|
2014-02-20 20:56:32 +01:00
|
|
|
ctx.bittorrent = true;
|
2014-01-11 10:36:17 +01:00
|
|
|
}
|
|
|
|
else {
|
2014-02-24 02:50:23 +01:00
|
|
|
delete ctx.bittorrent;
|
2014-01-11 10:36:17 +01:00
|
|
|
}
|
|
|
|
|
2014-03-14 14:01:14 +01:00
|
|
|
ctx.name = btName || dlName || "Unknown";
|
2014-02-24 02:50:23 +01:00
|
|
|
|
2013-01-28 15:18:21 +01:00
|
|
|
// collapse the download details initially
|
2014-02-24 02:50:23 +01:00
|
|
|
if (ctx.collapsed === undefined) {
|
2013-01-28 15:18:21 +01:00
|
|
|
ctx.collapsed = true;
|
2014-02-24 02:50:23 +01:00
|
|
|
}
|
2013-01-28 15:18:21 +01:00
|
|
|
|
2013-01-22 13:11:38 +01:00
|
|
|
return ctx;
|
|
|
|
};
|
|
|
|
|
|
|
|
scope.canRestart = function(d) {
|
|
|
|
if (['active', 'paused'].indexOf(d.status) == -1
|
|
|
|
&& !d.bittorrent)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
};
|
2013-01-19 18:46:22 +01:00
|
|
|
|
2013-03-18 10:51:03 +01:00
|
|
|
scope.hasStatus = function hasStatus(d, status) {
|
|
|
|
if (_.isArray(status)) {
|
|
|
|
if (status.length == 0) return false;
|
|
|
|
return hasStatus(d, status[0]) || hasStatus(d, status.slice(1));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return d.status == status;
|
|
|
|
}
|
2013-01-22 13:11:38 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
// get time left for the download with
|
|
|
|
// current download speed,
|
|
|
|
// could be smarter by different heuristics
|
|
|
|
scope.getEta = function(d) {
|
|
|
|
return (d.totalLength-d.completedLength) / d.downloadSpeed;
|
|
|
|
}
|
|
|
|
|
2014-03-14 14:01:14 +01:00
|
|
|
scope.getProgressClass = function(d) {
|
2014-02-20 21:36:21 +01:00
|
|
|
switch (d.status) {
|
|
|
|
case "paused":
|
|
|
|
return "progress-info";
|
|
|
|
case "error":
|
|
|
|
return "progress-danger";
|
|
|
|
case "removed":
|
|
|
|
return "progress-warning";
|
|
|
|
case "active":
|
|
|
|
return "progress-active";
|
|
|
|
case "complete":
|
|
|
|
return "progress-success";
|
|
|
|
default:
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-01-22 13:11:38 +01:00
|
|
|
// gets the progress in percentages
|
|
|
|
scope.getProgress = function(d) {
|
2014-01-11 10:36:17 +01:00
|
|
|
var percentage = (d.completedLength / d.totalLength)*100 || 0;
|
2013-01-19 18:46:22 +01:00
|
|
|
percentage = percentage.toFixed(2);
|
|
|
|
if(!percentage) percentage = 0;
|
|
|
|
|
2013-01-22 13:11:38 +01:00
|
|
|
return percentage;
|
|
|
|
};
|
2013-01-17 17:18:23 +01:00
|
|
|
|
2013-01-22 13:11:38 +01:00
|
|
|
// gets the type for the download as classified by the aria2 rpc calls
|
|
|
|
scope.getType = function(d) {
|
|
|
|
var type = d.status;
|
|
|
|
if (type == "paused") type = "waiting";
|
|
|
|
if (["error", "removed", "complete"].indexOf(type) != -1)
|
|
|
|
type = "stopped";
|
|
|
|
return type;
|
|
|
|
};
|
|
|
|
|
2013-02-25 11:50:42 +01:00
|
|
|
scope.showSettings = function(d) {
|
2013-03-16 09:13:06 +01:00
|
|
|
var type = scope.getType(d)
|
2013-02-25 11:50:42 +01:00
|
|
|
, settings = {};
|
|
|
|
|
|
|
|
rpc.once('getOption', [d.gid], function(data) {
|
|
|
|
var vals = data[0];
|
|
|
|
|
|
|
|
for (var i in fsettings) {
|
|
|
|
if (type == 'active' && activeInclude.indexOf(i) == -1) continue;
|
|
|
|
|
|
|
|
if (type == 'waiting' && waitingExclude.indexOf(i) != -1) continue;
|
|
|
|
|
|
|
|
settings[i] = fsettings[i];
|
|
|
|
settings[i].val = vals[i] || settings[i].val;
|
|
|
|
}
|
|
|
|
|
2014-02-24 02:50:23 +01:00
|
|
|
modals.invoke('settings', settings, scope.name + ' settings', function(settings) {
|
2013-02-25 11:50:42 +01:00
|
|
|
var sets = {};
|
|
|
|
for (var i in settings) { sets[i] = settings[i].val };
|
|
|
|
|
|
|
|
rpc.once('changeOption', [d.gid, sets]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-01-15 09:24:09 +01:00
|
|
|
}]);
|