refactoed rpc and templates with ui hints for download status

This commit is contained in:
hamza zia 2013-01-17 11:51:03 +05:00
parent 50218debae
commit e64b3fbd66
9 changed files with 241 additions and 244 deletions

View File

@ -136,73 +136,73 @@
<div id="active_downloads"> <div id="active_downloads">
<table class="active-download" ng-repeat="download in getDownloads()"> <table class="active-download" ng-repeat="download in getDownloads()">
<tbody> <tbody>
<tr> <tr>
<td data-toggle="collapse" data-target=".download-detail .collapse" class="download-overview"> <td data-toggle="collapse" data-target=".download-detail .collapse" class="download-overview">
<b class="download-name">{{name}}</b> <b class="download-name">{{name}}</b>
<ul class="stats hidden-phone pull-right"> <ul class="stats hidden-phone pull-right">
<li class="label label-success">Time left: <span class="download-eta">{{eta}}</span></li> <li class="label label-success">Time left: <span class="download-eta">{{eta}}</span></li>
<li class="label label-success">Download Speed: <span class="download-down_speed">{{down_speed}}</span></li> <li class="label label-success">Download Speed: <span class="download-down_speed">{{down_speed}}</span></li>
<li class="label label-success">Progress: <span class="download-down_speed">{{percentage}}%</span></li> <li class="label label-success">Progress: <span class="download-down_speed">{{percentage}}%</span></li>
</ul> </ul>
<div class="progress full-progress progress-striped active" style="width: 100%; margin: 0; padding: 0;"> <div class="progress full-progress progress-striped active" style="width: 100%; margin: 0; padding: 0;">
<div class="bar" style="width: 90%;"></div> <div class="bar" style="width: 90%;"></div>
</div> </div>
</td> </td>
<td class="download-controls"> <td class="download-controls">
<div class="btn-group"> <div class="btn-group">
<button class="btn btn-mini download_pause"><i class="icon-pause"></i></button> <button class="btn btn-mini download_pause"><i class="icon-pause"></i></button>
<button class="btn dropdown-toggle" data-toggle="dropdown"> <button class="btn dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span> <span class="caret"></span>
</button> </button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a><i class="icon-cog"></i> Settings</a></li> <li><a><i class="icon-cog"></i> Settings</a></li>
<li><a><i class="icon-list-alt"></i> Peers</a></li> <li><a><i class="icon-list-alt"></i> Peers</a></li>
<li><a data-toggle="collapse" data-target=".download-detail .collapse"><i class="icon-info-sign"></i> More Info</a></li> <li><a data-toggle="collapse" data-target=".download-detail .collapse"><i class="icon-info-sign"></i> More Info</a></li>
<li><a href="#"><i class="icon-remove"></i> Remove</a></li> <li><a href="#"><i class="icon-remove"></i> Remove</a></li>
</ul> </ul>
</div> </div>
</td> </td>
</tr> </tr>
<tr data-toggle="collapse" data-target=".download-detail .collapse" class="download-detail"> <tr data-toggle="collapse" data-target=".download-detail .collapse" class="download-detail">
<td colspan="2"> <td colspan="2">
<div class="collapse more_info"> <div class="collapse more_info">
<canvas class="progress chunk-canvas" width="1400" style="width: 100%;"></canvas> <canvas class="progress chunk-canvas" width="1400" style="width: 100%;"></canvas>
<ul class="stats"> <ul class="stats">
<li class="label">Status: <span class="download-status">{{status}}</span></li> <li class="label">Status: <span class="download-status">{{status}}</span></li>
<li class="label">GID: <span class="download-gid">{{gid}}</span></li> <li class="label">GID: <span class="download-gid">{{gid}}</span></li>
<li class="label">Dir: <span class="download-dir">{{dir}}</span></li> <li class="label">Dir: <span class="download-dir">{{dir}}</span></li>
<li class="label">Size: <span class="download-size">{{size}}</span></li> <li class="label">Size: <span class="download-size">{{size}}</span></li>
<li class="label">Downloaded: <span class="download-downloaded">{{downloaded}}</span></li> <li class="label">Downloaded: <span class="download-downloaded">{{downloaded}}</span></li>
<li class="label">Num of Pieces: <span class="download-numPieces">{{numPieces}}</span></li> <li class="label">Num of Pieces: <span class="download-numPieces">{{numPieces}}</span></li>
<li class="label">Piece Length: <span class="download-pieceLength">{{pieceLength}}</span></li> <li class="label">Piece Length: <span class="download-pieceLength">{{pieceLength}}</span></li>
<li class="label">ETA: <span class="download-eta">{{eta}}</span></li> <li class="label">ETA: <span class="download-eta">{{eta}}</span></li>
<li class="label">Down Speed: <span class="download-down_speed">{{down_speed}}</span></li> <li class="label">Down Speed: <span class="download-down_speed">{{down_speed}}</span></li>
<li class="label">Upload Speed: <span class="download-upload_speed">{{upload_speed}}</span></li> <li class="label">Upload Speed: <span class="download-upload_speed">{{upload_speed}}</span></li>
<li class="label">Upload Length: <span class="download-uploadLength">{{uploadLength}}</span></li> <li class="label">Upload Length: <span class="download-uploadLength">{{uploadLength}}</span></li>
<li class="label">Connections: <span class="download-connections">{{connections}}</span></li> <li class="label">Connections: <span class="download-connections">{{connections}}</span></li>
</ul> </ul>
<h4 class="hidden-phone">Download Files</h4> <h4 class="hidden-phone">Download Files</h4>
<ul class="download-files hidden-phone"> <ul class="download-files hidden-phone">
<li class="label">{{path}} ({{size}})</li> <li class="label">{{path}} ({{size}})</li>
</ul> </ul>
<div> <div>
<div class="download-graph"></div> <div class="download-graph"></div>
</div> </div>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<div id="waiting_downloads"></div> <div id="waiting_downloads"></div>
<div id="stopped_downloads"></div> <div id="stopped_downloads"></div>
</div> </div>

View File

@ -73,35 +73,88 @@
</style> </style>
<!-- }}} --> <!-- }}} -->
<!--{{{ active downloads template --> <!--- {{{ download template -->
<script type="text/mustache" id="download_active_template"> <script type="text/mustache" id="download_template">
<table class="active-download" data-gid="{{gid}}" data-settingsName={{sett_name}}> <table class="{{type}}-download" data-gid="{{gid}}" data-settingsName={{sett_name}}>
<tbody> <tbody>
<tr> <tr>
<td data-toggle="collapse" data-target="[data-gid={{gid}}] .download-detail .collapse" class="download-overview"> <td data-toggle="collapse" data-target="[data-gid={{gid}}] .download-detail .collapse" class="download-overview">
<b class="download-name">{{name}}</b> <b class="download-name">{{name}}</b>
<ul class="stats hidden-phone pull-right"> <ul class="stats hidden-phone pull-right">
{{#booleans.can_pause}}
<li class="label label-success">Time left: <span class="download-eta">{{eta}}</span></li> <li class="label label-success">Time left: <span class="download-eta">{{eta}}</span></li>
<li class="label label-success">Download Speed: <span class="download-down_speed">{{down_speed}}</span></li> <li class="label label-success">Download Speed: <span class="download-down_speed">{{down_speed}}</span></li>
<li class="label label-success">Progress: <span class="download-down_speed">{{percentage}}%</span></li> <li class="label label-success">Progress: <span class="download-percentage">{{percentage}}</span>%</li>
{{/booleans.can_pause}}
{{#booleans.can_play}}
<li class="label label-info">Size: <span class="download-size">{{size}}</span></li>
<li class="label label-info">Downloaded: <span class="download-downloaded">{{downloaded}}</span></li>
<li class="label label-info">Path: <span class="download-dir">{{dir}}</span></li>
{{/booleans.can_play}}
{{#booleans.is_complete}}
<li class="label">Path: <span class="download-dir">{{dir}}</span></li>
<li class="label">Size: <span class="download-size">{{size}}</span></li>
{{/booleans.is_complete}}
{{#booleans.is_removed}}
<li class="label label-warning">Path: <span class="download-dir">{{dir}}</span></li>
<li class="label label-warning">Size: <span class="download-size">{{size}}</span></li>
{{/booleans.is_removed}}
{{#booleans.is_error}}
<li class="label label-important">Path: <span class="download-dir">{{dir}}</span></li>
<li class="label label-important">Size: <span class="download-size">{{size}}</span></li>
{{/booleans.is_error}}
</ul> </ul>
<div class="progress full-progress progress-striped active" style="width: 100%; margin: 0; padding: 0;"> {{#booleans.can_pause}}
<div class="progress progress-striped active" style="width: 100%; margin: 0; padding: 0;">
<div class="bar" style="width: {{percentage}}%;"></div> <div class="bar" style="width: {{percentage}}%;"></div>
</div> </div>
{{/booleans.can_pause}}
{{#booleans.can_play}}
<div class="progress progress-info progress-striped" style="width: 100%; margin: 0; padding: 0;">
<div class="bar" style="width: {{percentage}}%;"></div>
</div>
{{/booleans.can_play}}
{{#booleans.is_error}}
<div class="progress progress-danger progress-striped" style="width: 100%; margin: 0; padding: 0;">
<div class="bar" style="width: {{percentage}}%;"></div>
</div>
{{/booleans.is_error}}
{{#booleans.is_removed}}
<div class="progress progress-warning progress-striped" style="width: 100%; margin: 0; padding: 0;">
<div class="bar" style="width: {{percentage}}%;"></div>
</div>
{{/booleans.is_removed}}
{{#booleans.is_complete}}
<div class="progress progress-success progress-striped" style="width: 100%; margin: 0; padding: 0;">
<div class="bar" style="width: {{percentage}}%;"></div>
</div>
{{/booleans.is_complete}}
</td> </td>
<td class="download-controls"> <td class="download-controls">
<div class="btn-group"> <div class="btn-group">
{{#booleans.can_pause}}
<button class="btn btn-mini download_pause"><i class="icon-pause"></i></button> <button class="btn btn-mini download_pause"><i class="icon-pause"></i></button>
{{/booleans.can_pause}}
{{#booleans.can_play}}
<button class="btn btn-mini download_play"><i class="icon-play"></i></button>
{{/booleans.can_play}}
{{#booleans.can_restart}}
<button class="btn btn-mini download_restart"><i class="icon-repeat"></i></button>
{{/booleans.can_restart}}
<button class="btn dropdown-toggle" data-toggle="dropdown"> <button class="btn dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span> <span class="caret"></span>
</button> </button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
{{#booleans.has_settings}}
<li><a href="#" class="download_settings"><i class="icon-cog"></i> Settings</a></li> <li><a href="#" class="download_settings"><i class="icon-cog"></i> Settings</a></li>
{{#bittorrent}} {{#bittorrent}}
<li><a href="#" class="torrent_info"><i class="icon-list-alt"></i> Peers</a></li> <li><a href="#" class="torrent_info"><i class="icon-list-alt"></i> Peers</a></li>
{{/bittorrent}} {{/bittorrent}}
{{/booleans.has_settings}}
<li><a href="#" data-toggle="collapse" data-target="[data-gid={{gid}}] .download-detail .collapse"><i class="icon-info-sign"></i> More Info</a></li> <li><a href="#" data-toggle="collapse" data-target="[data-gid={{gid}}] .download-detail .collapse"><i class="icon-info-sign"></i> More Info</a></li>
<li><a href="#" class="download_remove"><i class="icon-remove"></i> Remove</a></li> <li><a href="#" class="download_remove"><i class="icon-remove"></i> Remove</a></li>
</ul> </ul>
@ -143,125 +196,7 @@
</tbody> </tbody>
</table> </table>
</script> </script>
<!-- active downloads template end }}}--> <!-- }}} -->
<!--{{{ waiting downloads template-->
<script type="text/mustache" id="download_waiting_template">
<table class="waiting-download" data-gid="{{gid}}" data-settingsName={{sett_name}}>
<tbody>
<tr>
<td data-toggle="collapse" data-target="[data-gid={{gid}}] .download-detail .collapse" class="download-overview">
<b class="download-name">{{name}}</b>
<ul class="stats hidden-phone pull-right">
<li class="label label-success">Size: <span class="download-size">{{size}}</span></li>
<li class="label label-success">Downloaded: <span class="download-downloaded">{{downloaded}}</span></li>
<li class="label label-success">Path: <span class="download-dir">{{dir}}</span></li>
</ul>
<div class="progress full-progress progress-striped active" style="width: 100%; margin: 0; padding: 0;">
<div class="bar" style="width: {{percentage}}%;"></div>
</div>
</td>
<td class="download-controls">
<div class="btn-group">
<button class="btn btn-mini download_play"><i class="icon-play"></i></button>
<button class="btn dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="#" class="download_settings"><i class="icon-cog"></i> Settings</a></li>
<li><a href="#" data-toggle="collapse" data-target="[data-gid={{gid}}] .download-detail .collapse"><i class="icon-info-sign"></i> More Info</a></li>
<li><a href="#" class="download_remove"><i class="icon-remove"></i> Remove</a></li>
</ul>
</div>
</td>
</tr>
<tr data-toggle="collapse" data-target="[data-gid={{gid}}] .download-detail .collapse" class="download-detail">
<td colspan="2">
<div class="collapse more_info">
<ul class="stats">
<li class="label">Status: <span class="download-status">{{status}}</span></li>
<li class="label">GID: <span class="download-gid">{{gid}}</span></li>
<li class="label">Dir: <span class="download-dir">{{dir}}</span></li>
<li class="label">Size: <span class="download-size">{{size}}</span></li>
<li class="label">Downloaded: <span class="download-downloaded">{{downloaded}}</span></li>
<li class="label">Num of Pieces: <span class="download-numPieces">{{numPieces}}</span></li>
<li class="label">Piece Length: <span class="download-pieceLength">{{pieceLength}}</span></li>
<li class="label">ETA: <span class="download-eta">{{eta}}</span></li>
<li class="label">Down Speed: <span class="download-down_speed">{{down_speed}}</span></li>
<li class="label">Upload Speed: <span class="download-upload_speed">{{upload_speed}}</span></li>
<li class="label">Upload Length: <span class="download-uploadLength">{{uploadLength}}</span></li>
<li class="label">Connections: <span class="download-connections">{{connections}}</span></li>
</ul>
<h4 class="hidden-phone">Download Files</h4>
<ul class="download-files hidden-phone">
{{#files}}
<li class="label">{{path}} ({{size}})</li>
{{/files}}
</ul>
</div>
</td>
</tr>
</tbody>
</table>
</script>
<!-- waiting downloads template end }}}-->
<!--{{{ stopped downloads template-->
<script type="text/mustache" id="download_stopped_template">
<table class="stopped-download" data-gid="{{gid}}">
<tbody>
<tr>
<td data-toggle="collapse" data-target="[data-gid={{gid}}] .download-detail .collapse" class="download-overview">
<b class="download-name">{{name}}</b>
<ul class="stats hidden-phone pull-right">
<li class="label label-success">Path: <span class="download-dir">{{dir}}</span></li>
</ul>
<div class="progress full-progress progress-striped active" style="width: 100%; margin: 0; padding: 0;">
<div class="bar" style="width: {{percentage}}%;"></div>
</div>
</td>
<td class="download-controls">
<div class="btn-group">
<button class="btn btn-mini download_restart"><i class="icon-repeat"></i></button>
<button class="btn dropdown-toggle" data-toggle="dropdown">
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li><a href="#" data-toggle="collapse" data-target="[data-gid={{gid}}] .download-detail .collapse"><i class="icon-info-sign"></i> More Info</a></li>
<li><a href="#" class="download_remove"><i class="icon-remove"></i> Remove</a></li>
</ul>
</div>
</td>
</tr>
<tr data-toggle="collapse" data-target="[data-gid={{gid}}] .download-detail .collapse" class="download-detail">
<td colspan="2">
<div class="collapse more_info">
<ul class="stats">
<li class="label">Status: <span class="download-status">{{status}}</span></li>
<li class="label">GID: <span class="download-gid">{{gid}}</span></li>
<li class="label">Dir: <span class="download-dir">{{dir}}</span></li>
<li class="label">Size: <span class="download-size">{{size}}</span></li>
<li class="label">Downloaded: <span class="download-downloaded">{{downloaded}}</span></li>
<li class="label">Num of Pieces: <span class="download-numPieces">{{numPieces}}</span></li>
<li class="label">Piece Length: <span class="download-pieceLength">{{pieceLength}}</span></li>
<li class="label">ETA: <span class="download-eta">{{eta}}</span></li>
<li class="label">Down Speed: <span class="download-down_speed">{{down_speed}}</span></li>
<li class="label">Upload Speed: <span class="download-upload_speed">{{upload_speed}}</span></li>
<li class="label">Upload Length: <span class="download-uploadLength">{{uploadLength}}</span></li>
<li class="label">Connections: <span class="download-connections">{{connections}}</span></li>
</ul>
</div>
</td>
</tr>
</tbody>
</table>
</script>
<!-- stopped downloads template end }}}-->
<!--{{{ start global settings item template --> <!--{{{ start global settings item template -->
<script type="text/mustache" id="global_general_settings_template"> <script type="text/mustache" id="global_general_settings_template">
@ -445,7 +380,6 @@
<a class="brand" href="#">aria2 Web-Client</a> <a class="brand" href="#">aria2 Web-Client</a>
<div class="nav-collapse"> <div class="nav-collapse">
<ul class="nav"> <ul class="nav">
<li class="active"><a href="#">All Downloads</a></li>
<li class="dropdown" id="add_download"> <li class="dropdown" id="add_download">
<a class="dropdown-toggle" data-toggle="dropdown" href="#add_download"> <a class="dropdown-toggle" data-toggle="dropdown" href="#add_download">
Add <b class="caret"></b> Add <b class="caret"></b>

View File

@ -1,40 +1,36 @@
app.controller('DownloadCtrl', [ '$scope', '$rpc', app.controller('DownloadCtrl', [ '$scope', '$rpc', '$utils',
function(scope, rpc) { function(scope, rpc, utils) {
rpc.configure({
host: 'localhost',
port: 6800
});
scope.active = scope.waiting = scope.stopped = []; scope.active = scope.waiting = scope.stopped = [];
rpc.subscribe('tellActive', [], function(data) { rpc.subscribe('tellActive', [], function(data) {
console.log('got active data');
scope.$apply(function() { scope.$apply(function() {
scope.active = data[0]; scope.active = data[0].map(scope.normalize);
}); });
}); });
rpc.subscribe('tellWaiting', [0, 100], function(data) { rpc.subscribe('tellWaiting', [0, 100], function(data) {
scope.$apply(function() { scope.$apply(function() {
scope.waiting = data[0]; scope.waiting = data[0].map(scope.normalize);
}); });
}); });
rpc.subscribe('tellStopped', [0, 100], function(data) { rpc.subscribe('tellStopped', [0, 100], function(data) {
scope.$apply(function() { scope.$apply(function() {
scope.stopped = data[0]; scope.stopped = data[0].map(scope.normalize);
}); });
}); });
scope.getDownloads = function() { scope.getDownloads = function() {
var rets = scope.active var rets = scope.active
.concat(scope.waiting).concat(scope.stopped); .concat(scope.waiting).concat(scope.stopped)
return rets; return rets;
} }
scope.normalize = function(d) { scope.normalize = function(d) {
return "hello"; return d;
} }
}]); }]);

View File

@ -371,10 +371,14 @@ function getTemplateCtx(data) {
var chunks = percentage !== 100 && data.bitfield ? getChunksFromHex(data.bitfield, data.numPieces) : []; var chunks = percentage !== 100 && data.bitfield ? getChunksFromHex(data.bitfield, data.numPieces) : [];
var eta = changeTime((data.totalLength-data.completedLength)/data.downloadSpeed); var eta = changeTime((data.totalLength-data.completedLength)/data.downloadSpeed);
var type = data.status;
if (type == "paused") type = "waiting";
if (type == "error" || type == "removed" || type == "complete") type = "stopped";
return { return {
name: name, name: name,
sett_name: name.substr(0,name.lastIndexOf('.')) || name, sett_name: name.substr(0,name.lastIndexOf('.')) || name,
status: data.status, status: data.status,
type: type,
percentage:percentage, percentage:percentage,
gid: data.gid, gid: data.gid,
size: changeLength(data.totalLength, "B"), size: changeLength(data.totalLength, "B"),
@ -389,7 +393,13 @@ function getTemplateCtx(data) {
connections: data.connections, connections: data.connections,
upload_speed: changeLength(data.uploadSpeed, "B/s"), upload_speed: changeLength(data.uploadSpeed, "B/s"),
booleans: { booleans: {
is_error: data.status === "error" is_error: data.status === "error",
is_complete: data.status === "complete",
is_removed: data.status === "removed",
has_settings: ["active", "waiting", "paused"].indexOf(data.status) != -1,
can_pause: type == "active",
can_play: type == "waiting",
can_restart: type == "stopped"
}, },
chunks: chunks, chunks: chunks,
files: data.files.map(function(e) { e.size = changeLength(e.length, "B"); return e }), files: data.files.map(function(e) { e.size = changeLength(e.length, "B"); return e }),
@ -410,7 +420,6 @@ function updateDownloadTemplates(elem, ctx) {
html += '<li class="label">' + ctx.files[i].path + ' (' + ctx.files[i].size + ')</li>'; html += '<li class="label">' + ctx.files[i].path + ' (' + ctx.files[i].size + ')</li>';
} }
elem.find('.download-files').html(html); elem.find('.download-files').html(html);
console.log(html);
// update progress bar // update progress bar
elem.find('.full-progress .bar').css('width', ctx.percentage + '%'); elem.find('.full-progress .bar').css('width', ctx.percentage + '%');
@ -464,7 +473,7 @@ function deleteDownloadTemplates($top_elem, data) {
} }
} }
function refreshDownloadTemplates(top_elem, data) { function refreshDownloadTemplates(top_elem, data) {
var down_template = $('#download_' + top_elem + '_template').text(), var down_template = $('#download_template').text(),
new_items = [], new_items = [],
$top_elem = $('#' + top_elem + '_downloads'); $top_elem = $('#' + top_elem + '_downloads');

View File

@ -40,7 +40,7 @@ app.factory('$jsoncall', ['$', '$json', '$base64', function($, JSON, base64) {
// check if authentication details are given, if yes then use a hack to support // check if authentication details are given, if yes then use a hack to support
// http authentication otherwise emit error // http authentication otherwise emit error
if (!rpc.serverConf.auth) { if (!rpc.serverConf.auth) {
console.log("no user name and still error!!!"); console.log("jsonrpc disconnect!!!");
return opts.error(); return opts.error();
} }

View File

@ -1,19 +1,20 @@
app.factory('$rpc', ['$syscall', '$globalTimeout', function(syscall, time) { app.factory('$rpc', ['$syscall', '$globalTimeout', function(syscall, time) {
var subscriptions = [], var subscriptions = []
timeout = null, , configurations = [{ host: 'localhost', port: 6800 }]
forceNextUpdate = false; , timeout = null
, forceNextUpdate = false;
syscall.init({
host: 'localhost',
port: 6800
});
// update is implemented such that // update is implemented such that
// only one syscall at max is ongoing // only one syscall at max is ongoing
// (i.e. serially) so should be private // (i.e. serially) so should be private
// to maintain that invariant // to maintain that invariant
var update = function() { var update = function() {
if (!subscriptions.length) return; if (!subscriptions.length)
return;
if (configurations.length)
syscall.init(configurations.pop());
subscriptions = _.filter(subscriptions, function(e) { return !!e }); subscriptions = _.filter(subscriptions, function(e) { return !!e });
var params = _.map(subscriptions, function(s) { var params = _.map(subscriptions, function(s) {
@ -27,6 +28,8 @@ app.factory('$rpc', ['$syscall', '$globalTimeout', function(syscall, time) {
name: 'system.multicall', name: 'system.multicall',
params: [params], params: [params],
success: function(data) { success: function(data) {
// configuration worked, leave this as it is
configurations = [];
_.each(data.result, function(d, i) { _.each(data.result, function(d, i) {
var handle = subscriptions[i]; var handle = subscriptions[i];
if (handle) { if (handle) {
@ -44,17 +47,24 @@ app.factory('$rpc', ['$syscall', '$globalTimeout', function(syscall, time) {
timeout = setTimeout(update, time); timeout = setTimeout(update, time);
}, },
error: function() { error: function() {
// TODO: implement a retry error handler // If some proposed configurations are still in the pipeline then retry
// which would also look for other suitable if (configurations.length) update();
// connection configurations for the connection else {
console.log('cannot connect!!!');
timeout = setTimeout(update, time);
}
} }
}); });
} }
return { return {
// change the connection configuration, // conf can be configuration or array of configurations,
// for all options read rpc/syscall.js // each one will be tried one after the other till success,
// for all options for one conf read rpc/syscall.js
configure: function(conf) { configure: function(conf) {
syscall.init(conf); if (conf instanceof Array)
configurations = conf;
else
configurations = [conf];
}, },
// syscall is done only once // syscall is done only once
once: function(name, params, cb) { once: function(name, params, cb) {

View File

@ -1,5 +1,5 @@
app.factory('$sockcall', ['$_', '$json', '$name', '$utils', function(_, JSON, name, utils) { app.factory('$sockcall', ['$_', '$json', '$name', '$utils', function(_, JSON, name, utils) {
return { var sockRPC = {
// true when sockrpc is ready to be used, // true when sockrpc is ready to be used,
// false when either initializing // false when either initializing
// or no support for web sockets // or no support for web sockets
@ -18,14 +18,14 @@ app.factory('$sockcall', ['$_', '$json', '$name', '$utils', function(_, JSON, na
scheme: 'ws', scheme: 'ws',
// called when a connection error occurs // called when a connection error occurs
onerror: function() { onerror: function(err) {
_.each(this.handles, function(h) { h.error() }); _.each(sockRPC.handles, function(h) { h.error() });
this.handles = []; sockRPC.handles = [];
}, },
// when connection opens // when connection opens
onopen: function() { onopen: function() {
this.initialized = true; sockRPC.initialized = true;
}, },
@ -35,10 +35,10 @@ app.factory('$sockcall', ['$_', '$json', '$name', '$utils', function(_, JSON, na
// reverse loop because we are deleting elements // reverse loop because we are deleting elements
// while looping over the old items // while looping over the old items
for (var i = this.handles.length - 1; i >= 0; i--) { for (var i = sockRPC.handles.length - 1; i >= 0; i--) {
if (this.handles[i].id === data.id) { if (sockRPC.handles[i].id === data.id) {
this.handles[i].success(data); sockRPC.handles[i].success(data);
this.handles.splice(i, 1); sockRPC.handles.splice(i, 1);
return; return;
} }
} }
@ -49,18 +49,18 @@ app.factory('$sockcall', ['$_', '$json', '$name', '$utils', function(_, JSON, na
var data = { var data = {
jsonrpc: 2.0, jsonrpc: 2.0,
id: name + '_' + utils.randStr(), id: name + '_' + utils.randStr(),
method: opts.func, method: opts.name,
params: opts.params && opts.params.length ? opts.params : undefined params: opts.params && opts.params.length ? opts.params : undefined
}; };
if (data.params && !data.params.length) data.params = undefined; if (data.params && !data.params.length) data.params = undefined;
this.handles.push({ sockRPC.handles.push({
success: opts.success || angular.noop, success: opts.success || angular.noop,
error: opts.error || angular.noop, error: opts.error || angular.noop,
id: data.id id: data.id
}); });
this.sock.send( JSON.stringify(data) ); sockRPC.sock.send( JSON.stringify(data) );
}, },
// should be called initially to start using the sock rpc // should be called initially to start using the sock rpc
@ -68,26 +68,29 @@ app.factory('$sockcall', ['$_', '$json', '$name', '$utils', function(_, JSON, na
if (typeof WebSocket == "undefined") { if (typeof WebSocket == "undefined") {
return; return;
} }
this.conf = conf || this.conf; sockRPC.conf = conf || sockRPC.conf;
this.initialized = false; sockRPC.initialized = false;
this.scheme = this.conf.encryption ? 'wss' : 'ws'; sockRPC.scheme = sockRPC.conf.encryption ? 'wss' : 'ws';
if (this.sock) { if (sockRPC.sock) {
this.onopen = this.sock.onmessage = this.sock.onerror = this.sock.onclose = null; sockRPC.onopen = sockRPC.sock.onmessage = sockRPC.sock.onerror = sockRPC.sock.onclose = null;
this.onerror(); sockRPC.onerror();
} }
try { try {
this.sock = new WebSocket(this.scheme + '://' + conf.host + ':' + conf.port + '/jsonrpc'); sockRPC.sock = new WebSocket(sockRPC.scheme + '://' + conf.host + ':' + conf.port + '/jsonrpc');
this.sock.onopen = this.onopen; sockRPC.sock.onopen = sockRPC.onopen;
this.sock.onclose = this.sock.onerror = this.onerror; sockRPC.sock.onclose = sockRPC.sock.onerror = sockRPC.onerror;
this.sock.onmessage = this.onmessage; sockRPC.sock.onmessage = sockRPC.onmessage;
} }
catch (ex) { catch (ex) {
// ignoring IE securty exception on local ip addresses // ignoring IE securty exception on local ip addresses
console.log('not using websocket for aria2 rpc due to: ', ex);
} }
}, },
}; };
return sockRPC;
}]); }]);

View File

@ -32,10 +32,12 @@ app.factory('$syscall', ['$log', '$jsoncall', '$sockcall', function(log, jsonRPC
opts.success = opts.success || angular.noop; opts.success = opts.success || angular.noop;
opts.error = opts.error || angular.noop; opts.error = opts.error || angular.noop;
if (sockRPC.initialized) if (sockRPC.initialized) {
return sockRPC.invoke(opts); return sockRPC.invoke(opts);
else }
else {
return jsonRPC.invoke(opts); return jsonRPC.invoke(opts);
}
} }
}; };
}]); }]);

View File

@ -8,13 +8,56 @@ app.factory('$utils', function() {
} }
return str.join(""); return str.join("");
}, },
mixin: function(fromObj, toObj) {
for (var key in toObj) { // change time units
if (toObj.hasOwnProperty(key) && fromObj[key]) { changeTime: function(time) {
toObj[key] = fromObj[key]; time = parseInt(time);
if (!time) return " infinite";
if (time < 60) return time + " s";
else if (time < 60*60) return (time/60).toFixed(2) + " min";
else if (time < 60*60*24) return (time/(60*60)).toFixed(2) + " hours";
else return (time/(60*60*24)).toFixed(2) + " days!";
},
// change length units
changeLength: function(len, pref) {
len = parseInt(len);
if(len <= (1<<10)) return len + " " + pref;
else if(len <= (1<<20)) return (len/(1<<10)).toFixed(1) + " K" + pref;
else if(len <= (1<<30)) return (len/(1<<20)).toFixed(1) + " M" + pref;
else return (len/(1<<30)).toFixed(1) + " G" + pref;
},
// get download chunks from aria2 bitfield
getChunksFromHex: function(bitfield, numOfPieces) {
var chunks = [], len = 0, numPieces = parseInt(numOfPieces);
var totalDownloaded = 0;
if (numPieces > 1) {
var chunk_ratio = 1 / numPieces;
var piecesProcessed = 0;
for (var i = 0; i < bitfield.length; i++) {
var hex = parseInt(bitfield[i], 16);
for (var j = 1; j <= 4; j++) {
var bit = hex & (1 << (4 - j));
if (bit) totalDownloaded++;
var prog = !!bit;
if (len >= 1 && chunks[len - 1].show == prog) {
chunks[len - 1].ratio += chunk_ratio;
}
else {
chunks.push({
ratio: chunk_ratio,
show: prog
});
len++;
}
piecesProcessed++;
if (piecesProcessed == numPieces)
return chunks;
}
} }
} }
return toObj; return chunks;
} }
}; };
}); });