mirror of
https://github.com/transmission/transmission
synced 2025-03-10 22:22:58 +00:00
* chore: add precommit hook for testing code style * chore: use prettier + eslint for js code
451 lines
12 KiB
JavaScript
451 lines
12 KiB
JavaScript
/**
|
||
* Copyright © Mnemosyne LLC
|
||
*
|
||
* This file is licensed under the GPLv2.
|
||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||
*/
|
||
|
||
function TorrentRendererHelper() {}
|
||
|
||
TorrentRendererHelper.getProgressInfo = function (controller, t) {
|
||
let pct, extra;
|
||
const s = t.getStatus();
|
||
const seed_ratio_limit = t.seedRatioLimit(controller);
|
||
|
||
if (t.needsMetaData()) {
|
||
pct = t.getMetadataPercentComplete() * 100;
|
||
} else if (!t.isDone()) {
|
||
pct = Math.round(t.getPercentDone() * 100);
|
||
} else if (seed_ratio_limit > 0 && t.isSeeding()) {
|
||
// don't split up the bar if paused or queued
|
||
pct = Math.round((t.getUploadRatio() * 100) / seed_ratio_limit);
|
||
} else {
|
||
pct = 100;
|
||
}
|
||
|
||
if (s === Torrent._StatusStopped) {
|
||
extra = 'paused';
|
||
} else if (s === Torrent._StatusDownloadWait) {
|
||
extra = 'leeching queued';
|
||
} else if (t.needsMetaData()) {
|
||
extra = 'magnet';
|
||
} else if (s === Torrent._StatusDownload) {
|
||
extra = 'leeching';
|
||
} else if (s === Torrent._StatusSeedWait) {
|
||
extra = 'seeding queued';
|
||
} else if (s === Torrent._StatusSeed) {
|
||
extra = 'seeding';
|
||
} else {
|
||
extra = '';
|
||
}
|
||
|
||
return {
|
||
percent: pct,
|
||
complete: ['torrent_progress_bar', 'complete', extra].join(' '),
|
||
incomplete: ['torrent_progress_bar', 'incomplete', extra].join(' '),
|
||
};
|
||
};
|
||
|
||
TorrentRendererHelper.createProgressbar = function (classes) {
|
||
let complete, incomplete, progressbar;
|
||
|
||
complete = document.createElement('div');
|
||
complete.className = 'torrent_progress_bar complete';
|
||
|
||
incomplete = document.createElement('div');
|
||
incomplete.className = 'torrent_progress_bar incomplete';
|
||
|
||
progressbar = document.createElement('div');
|
||
progressbar.className = 'torrent_progress_bar_container ' + classes;
|
||
progressbar.appendChild(complete);
|
||
progressbar.appendChild(incomplete);
|
||
|
||
return {
|
||
element: progressbar,
|
||
complete: complete,
|
||
incomplete: incomplete,
|
||
};
|
||
};
|
||
|
||
TorrentRendererHelper.renderProgressbar = function (controller, t, progressbar) {
|
||
let e, style, width, display;
|
||
const info = TorrentRendererHelper.getProgressInfo(controller, t);
|
||
|
||
// update the complete progressbar
|
||
e = progressbar.complete;
|
||
style = e.style;
|
||
width = '' + info.percent + '%';
|
||
display = info.percent > 0 ? 'block' : 'none';
|
||
if (style.width !== width || style.display !== display) {
|
||
$(e).css({
|
||
width: '' + info.percent + '%',
|
||
display: display,
|
||
});
|
||
}
|
||
|
||
if (e.className !== info.complete) {
|
||
e.className = info.complete;
|
||
}
|
||
|
||
// update the incomplete progressbar
|
||
e = progressbar.incomplete;
|
||
display = info.percent < 100 ? 'block' : 'none';
|
||
|
||
if (e.style.display !== display) {
|
||
e.style.display = display;
|
||
}
|
||
|
||
if (e.className !== info.incomplete) {
|
||
e.className = info.incomplete;
|
||
}
|
||
};
|
||
|
||
TorrentRendererHelper.formatUL = function (t) {
|
||
return '▲' + Transmission.fmt.speedBps(t.getUploadSpeed());
|
||
};
|
||
|
||
TorrentRendererHelper.formatDL = function (t) {
|
||
return '▼' + Transmission.fmt.speedBps(t.getDownloadSpeed());
|
||
};
|
||
|
||
TorrentRendererHelper.formatETA = function (t) {
|
||
const eta = t.getETA();
|
||
if (eta < 0 || eta >= 999 * 60 * 60) {
|
||
return '';
|
||
}
|
||
return 'ETA: ' + Transmission.fmt.timeInterval(eta);
|
||
};
|
||
|
||
/****
|
||
*****
|
||
*****
|
||
****/
|
||
|
||
function TorrentRendererFull() {}
|
||
TorrentRendererFull.prototype = {
|
||
createRow: function () {
|
||
let root, name, peers, progressbar, details, image, button;
|
||
|
||
root = document.createElement('li');
|
||
root.className = 'torrent';
|
||
|
||
name = document.createElement('div');
|
||
name.className = 'torrent_name';
|
||
|
||
peers = document.createElement('div');
|
||
peers.className = 'torrent_peer_details';
|
||
|
||
progressbar = TorrentRendererHelper.createProgressbar('full');
|
||
|
||
details = document.createElement('div');
|
||
details.className = 'torrent_progress_details';
|
||
|
||
image = document.createElement('div');
|
||
button = document.createElement('a');
|
||
button.appendChild(image);
|
||
|
||
root.appendChild(name);
|
||
root.appendChild(peers);
|
||
root.appendChild(button);
|
||
root.appendChild(progressbar.element);
|
||
root.appendChild(details);
|
||
|
||
root._name_container = name;
|
||
root._peer_details_container = peers;
|
||
root._progress_details_container = details;
|
||
root._progressbar = progressbar;
|
||
root._pause_resume_button_image = image;
|
||
root._toggle_running_button = button;
|
||
|
||
return root;
|
||
},
|
||
|
||
getPeerDetails: function (t) {
|
||
let err,
|
||
peer_count,
|
||
webseed_count,
|
||
fmt = Transmission.fmt;
|
||
|
||
if ((err = t.getErrorMessage())) {
|
||
return err;
|
||
}
|
||
|
||
if (t.isDownloading()) {
|
||
peer_count = t.getPeersConnected();
|
||
webseed_count = t.getWebseedsSendingToUs();
|
||
|
||
if (webseed_count && peer_count) {
|
||
// Downloading from 2 of 3 peer(s) and 2 webseed(s)
|
||
return [
|
||
'Downloading from',
|
||
t.getPeersSendingToUs(),
|
||
'of',
|
||
fmt.countString('peer', 'peers', peer_count),
|
||
'and',
|
||
fmt.countString('web seed', 'web seeds', webseed_count),
|
||
'–',
|
||
TorrentRendererHelper.formatDL(t),
|
||
TorrentRendererHelper.formatUL(t),
|
||
].join(' ');
|
||
} else if (webseed_count) {
|
||
// Downloading from 2 webseed(s)
|
||
return [
|
||
'Downloading from',
|
||
fmt.countString('web seed', 'web seeds', webseed_count),
|
||
'–',
|
||
TorrentRendererHelper.formatDL(t),
|
||
TorrentRendererHelper.formatUL(t),
|
||
].join(' ');
|
||
} else {
|
||
// Downloading from 2 of 3 peer(s)
|
||
return [
|
||
'Downloading from',
|
||
t.getPeersSendingToUs(),
|
||
'of',
|
||
fmt.countString('peer', 'peers', peer_count),
|
||
'–',
|
||
TorrentRendererHelper.formatDL(t),
|
||
TorrentRendererHelper.formatUL(t),
|
||
].join(' ');
|
||
}
|
||
}
|
||
|
||
if (t.isSeeding()) {
|
||
return [
|
||
'Seeding to',
|
||
t.getPeersGettingFromUs(),
|
||
'of',
|
||
fmt.countString('peer', 'peers', t.getPeersConnected()),
|
||
'-',
|
||
TorrentRendererHelper.formatUL(t),
|
||
].join(' ');
|
||
}
|
||
|
||
if (t.isChecking()) {
|
||
return [
|
||
'Verifying local data (',
|
||
Transmission.fmt.percentString(100.0 * t.getRecheckProgress()),
|
||
'% tested)',
|
||
].join('');
|
||
}
|
||
|
||
return t.getStateString();
|
||
},
|
||
|
||
getProgressDetails: function (controller, t) {
|
||
if (t.needsMetaData()) {
|
||
let MetaDataStatus = 'retrieving';
|
||
if (t.isStopped()) {
|
||
MetaDataStatus = 'needs';
|
||
}
|
||
const percent = 100 * t.getMetadataPercentComplete();
|
||
return [
|
||
'Magnetized transfer - ' + MetaDataStatus + ' metadata (',
|
||
Transmission.fmt.percentString(percent),
|
||
'%)',
|
||
].join('');
|
||
}
|
||
|
||
let c;
|
||
const sizeWhenDone = t.getSizeWhenDone();
|
||
const totalSize = t.getTotalSize();
|
||
const is_done = t.isDone() || t.isSeeding();
|
||
|
||
if (is_done) {
|
||
if (totalSize === sizeWhenDone) {
|
||
// seed: '698.05 MiB'
|
||
c = [Transmission.fmt.size(totalSize)];
|
||
} else {
|
||
// partial seed: '127.21 MiB of 698.05 MiB (18.2%)'
|
||
c = [
|
||
Transmission.fmt.size(sizeWhenDone),
|
||
' of ',
|
||
Transmission.fmt.size(t.getTotalSize()),
|
||
' (',
|
||
t.getPercentDoneStr(),
|
||
'%)',
|
||
];
|
||
}
|
||
// append UL stats: ', uploaded 8.59 GiB (Ratio: 12.3)'
|
||
c.push(
|
||
', uploaded ',
|
||
Transmission.fmt.size(t.getUploadedEver()),
|
||
' (Ratio ',
|
||
Transmission.fmt.ratioString(t.getUploadRatio()),
|
||
')'
|
||
);
|
||
} else {
|
||
// not done yet
|
||
c = [
|
||
Transmission.fmt.size(sizeWhenDone - t.getLeftUntilDone()),
|
||
' of ',
|
||
Transmission.fmt.size(sizeWhenDone),
|
||
' (',
|
||
t.getPercentDoneStr(),
|
||
'%)',
|
||
];
|
||
}
|
||
|
||
// maybe append eta
|
||
if (!t.isStopped() && (!is_done || t.seedRatioLimit(controller) > 0)) {
|
||
c.push(' - ');
|
||
const eta = t.getETA();
|
||
if (eta < 0 || eta >= 999 * 60 * 60 /* arbitrary */) {
|
||
c.push('remaining time unknown');
|
||
} else {
|
||
c.push(Transmission.fmt.timeInterval(t.getETA()), ' remaining');
|
||
}
|
||
}
|
||
|
||
return c.join('');
|
||
},
|
||
|
||
render: function (controller, t, root) {
|
||
// name
|
||
setTextContent(root._name_container, t.getName());
|
||
|
||
// progressbar
|
||
TorrentRendererHelper.renderProgressbar(controller, t, root._progressbar);
|
||
|
||
// peer details
|
||
const has_error = t.getError() !== Torrent._ErrNone;
|
||
let e = root._peer_details_container;
|
||
$(e).toggleClass('error', has_error);
|
||
setTextContent(e, this.getPeerDetails(t));
|
||
|
||
// progress details
|
||
e = root._progress_details_container;
|
||
setTextContent(e, this.getProgressDetails(controller, t));
|
||
|
||
// pause/resume button
|
||
const is_stopped = t.isStopped();
|
||
e = root._pause_resume_button_image;
|
||
e.alt = is_stopped ? 'Resume' : 'Pause';
|
||
e.className = is_stopped ? 'torrent_resume' : 'torrent_pause';
|
||
},
|
||
};
|
||
|
||
/****
|
||
*****
|
||
*****
|
||
****/
|
||
|
||
function TorrentRendererCompact() {}
|
||
TorrentRendererCompact.prototype = {
|
||
createRow: function () {
|
||
let progressbar, details, name, root;
|
||
|
||
progressbar = TorrentRendererHelper.createProgressbar('compact');
|
||
|
||
details = document.createElement('div');
|
||
details.className = 'torrent_peer_details compact';
|
||
|
||
name = document.createElement('div');
|
||
name.className = 'torrent_name compact';
|
||
|
||
root = document.createElement('li');
|
||
root.appendChild(progressbar.element);
|
||
root.appendChild(details);
|
||
root.appendChild(name);
|
||
root.className = 'torrent compact';
|
||
root._progressbar = progressbar;
|
||
root._details_container = details;
|
||
root._name_container = name;
|
||
return root;
|
||
},
|
||
|
||
getPeerDetails: function (t) {
|
||
let c;
|
||
if ((c = t.getErrorMessage())) {
|
||
return c;
|
||
}
|
||
if (t.isDownloading()) {
|
||
const have_dn = t.getDownloadSpeed() > 0;
|
||
const have_up = t.getUploadSpeed() > 0;
|
||
|
||
if (!have_up && !have_dn) {
|
||
return 'Idle';
|
||
}
|
||
let s = '';
|
||
if (!isMobileDevice) {
|
||
s = TorrentRendererHelper.formatETA(t) + ' ';
|
||
}
|
||
if (have_dn) {
|
||
s += TorrentRendererHelper.formatDL(t);
|
||
}
|
||
if (have_dn && have_up) {
|
||
s += ' ';
|
||
}
|
||
if (have_up) {
|
||
s += TorrentRendererHelper.formatUL(t);
|
||
}
|
||
return s;
|
||
}
|
||
if (t.isSeeding()) {
|
||
return [
|
||
'Ratio: ',
|
||
Transmission.fmt.ratioString(t.getUploadRatio()),
|
||
', ',
|
||
TorrentRendererHelper.formatUL(t),
|
||
].join('');
|
||
}
|
||
return t.getStateString();
|
||
},
|
||
|
||
render: function (controller, t, root) {
|
||
// name
|
||
const is_stopped = t.isStopped();
|
||
let e = root._name_container;
|
||
$(e).toggleClass('paused', is_stopped);
|
||
setTextContent(e, t.getName());
|
||
|
||
// peer details
|
||
const has_error = t.getError() !== Torrent._ErrNone;
|
||
e = root._details_container;
|
||
$(e).toggleClass('error', has_error);
|
||
setTextContent(e, this.getPeerDetails(t));
|
||
|
||
// progressbar
|
||
TorrentRendererHelper.renderProgressbar(controller, t, root._progressbar);
|
||
},
|
||
};
|
||
|
||
/****
|
||
*****
|
||
*****
|
||
****/
|
||
|
||
function TorrentRow(view, controller, torrent) {
|
||
this.initialize(view, controller, torrent);
|
||
}
|
||
TorrentRow.prototype = {
|
||
initialize: function (view, controller, torrent) {
|
||
const row = this;
|
||
this._view = view;
|
||
this._torrent = torrent;
|
||
this._element = view.createRow();
|
||
this.render(controller);
|
||
$(this._torrent).bind('dataChanged.torrentRowListener', function () {
|
||
row.render(controller);
|
||
});
|
||
},
|
||
getElement: function () {
|
||
return this._element;
|
||
},
|
||
render: function (controller) {
|
||
const tor = this.getTorrent();
|
||
if (tor) {
|
||
this._view.render(controller, tor, this.getElement());
|
||
}
|
||
},
|
||
isSelected: function () {
|
||
return this.getElement().className.indexOf('selected') !== -1;
|
||
},
|
||
|
||
getTorrent: function () {
|
||
return this._torrent;
|
||
},
|
||
getTorrentId: function () {
|
||
return this.getTorrent().getId();
|
||
},
|
||
};
|