40
40
var $DownloadsTabBadgeEmpty;
41
41
var $DownloadQueueEmpty;
42
42
var $DownloadsRecordsPerPage;
43
var $DownloadsTable_Name;
45
46
var notification = null;
50
var nameColumnWidth = null;
54
'QUEUED': { Text: 'QUEUED', PostProcess: false },
55
'FETCHING': { Text: 'FETCHING', PostProcess: false },
56
'DOWNLOADING': { Text: 'DOWNLOADING', PostProcess: false },
57
'PP_QUEUED': { Text: 'PP-QUEUED', PostProcess: true },
58
'PAUSED': { Text: 'PAUSED', PostProcess: false },
59
'LOADING_PARS': { Text: 'CHECKING', PostProcess: true },
60
'VERIFYING_SOURCES': { Text: 'CHECKING', PostProcess: true },
61
'REPAIRING': { Text: 'REPAIRING', PostProcess: true },
62
'VERIFYING_REPAIRED': { Text: 'VERIFYING', PostProcess: true },
63
'RENAMING': { Text: 'RENAMING', PostProcess: true },
64
'MOVING': { Text: 'MOVING', PostProcess: true },
65
'UNPACKING': { Text: 'UNPACKING', PostProcess: true },
66
'EXECUTING_SCRIPT': { Text: 'PROCESSING', PostProcess: true },
67
'PP_FINISHED': { Text: 'FINISHED', PostProcess: false }
69
this.statusData = statusData;
50
71
this.init = function(options)
94
116
this.update = function()
96
RPC.call('listgroups', [], groups_loaded);
120
$('#DownloadsTable_Category').css('width', DownloadsUI.calcCategoryColumnWidth());
123
RPC.call('listgroups', [100], groups_loaded);
99
126
function groups_loaded(_groups)
101
128
groups = _groups;
102
RPC.call('postqueue', [100], posts_loaded);
105
function posts_loaded(posts)
109
RPC.call('urlqueue', [], urls_loaded);
112
function urls_loaded(_urls)
118
function mergequeues(posts)
120
var lastPPItemIndex = -1;
121
for (var i=0, il=posts.length; i < il; i++)
125
for (var j=0, jl=groups.length; j < jl; j++)
127
var group = groups[j];
128
if (group.NZBID === post.NZBID)
142
// create a virtual group-item
143
var group = {post: post};
144
group.NZBID = post.NZBID;
145
group.NZBName = post.NZBName;
146
group.MaxPriority = 0;
149
group.MinPostTime = 0;
150
group.FileSizeMB = 0;
151
group.FileSizeLo = 0;
152
group.RemainingSizeMB = 0;
153
group.RemainingSizeLo = 0;
154
group.PausedSizeMB = 0;
155
group.PausedSizeLo = 0;
157
group.RemainingFileCount = 0;
158
group.RemainingParCount = 0;
160
// insert it after the last pp-item
161
if (lastPPItemIndex > -1)
163
groups.splice(lastPPItemIndex + 1, 0, group);
167
groups.unshift(group);
173
133
function prepare()
175
135
for (var j=0, jl=groups.length; j < jl; j++)
177
detectStatus(groups[j]);
137
var group = groups[j];
138
group.postprocess = statusData[group.Status].PostProcess;
198
164
var group = groups[i];
200
166
var nametext = group.NZBName;
167
var statustext = DownloadsUI.buildStatusText(group);
201
168
var priority = DownloadsUI.buildPriorityText(group.MaxPriority);
202
169
var estimated = DownloadsUI.buildEstimated(group);
203
170
var age = Util.formatAge(group.MinPostTime + UISettings.timeZoneCorrection*60*60);
204
171
var size = Util.formatSizeMB(group.FileSizeMB, group.FileSizeLo);
205
172
var remaining = Util.formatSizeMB(group.RemainingSizeMB-group.PausedSizeMB, group.RemainingSizeLo-group.PausedSizeLo);
173
var dupe = DownloadsUI.buildDupeText(group.DupeKey, group.DupeScore, group.DupeMode);
211
179
data: { age: age, estimated: estimated, size: size, remaining: remaining },
212
search: group.status + ' ' + nametext + ' ' + priority + ' ' + group.Category + ' ' + age + ' ' + size + ' ' + remaining + ' ' + estimated
180
search: statustext + ' ' + nametext + ' ' + priority + ' ' + dupe + ' ' + group.Category + ' ' + age + ' ' + size + ' ' + remaining + ' ' + estimated
225
193
var status = DownloadsUI.buildStatus(group);
226
194
var priority = DownloadsUI.buildPriority(group.MaxPriority);
227
var progresslabel = DownloadsUI.buildProgressLabel(group);
195
var progresslabel = DownloadsUI.buildProgressLabel(group, nameColumnWidth);
228
196
var progress = DownloadsUI.buildProgress(group, item.data.size, item.data.remaining, item.data.estimated);
230
var name = '<a href="#" nzbid="' + group.NZBID + '">' + Util.textToHtml(Util.formatNZBName(group.NZBName)) + '</a>';
197
var dupe = DownloadsUI.buildDupe(group.DupeKey, group.DupeScore, group.DupeMode);
199
var age = new Date().getTime() / 1000 - (group.MinPostTime + UISettings.timeZoneCorrection*60*60);
200
var propagation = '';
201
if (group.ActiveDownloads == 0 && age < parseInt(Options.option('PropagationDelay')) * 60)
203
propagation = '<span class="label label-warning" title="Very recent post, temporary delayed (see option PropagationDelay)">delayed</span> ';
206
var name = '<a href="#" data-nzbid="' + group.NZBID + '">' + Util.textToHtml(Util.formatNZBName(group.NZBName)) + '</a>';
209
if (group.Kind === 'URL')
211
url = '<span class="label label-info">URL</span> ';
215
if (group.Health < 1000 && (!group.postprocess ||
216
(group.Status === 'PP_QUEUED' && group.PostTotalTimeSec === 0)))
218
health = ' <span class="label ' +
219
(group.Health >= group.CriticalHealth ? 'label-warning' : 'label-important') +
220
'">health: ' + Math.floor(group.Health / 10) + '%</span> ';
224
var backupPercent = calcBackupPercent(group);
225
if (backupPercent > 0)
227
backup = ' <a href="#" data-nzbid="' + group.NZBID + '" data-area="backup" class="badge-link"><span class="label label-warning" title="using backup news servers">backup: ' +
228
(backupPercent < 10 ? Util.round1(backupPercent) : Util.round0(backupPercent)) + '%</span> ';
231
231
var category = Util.textToHtml(group.Category);
233
233
if (!UISettings.miniTheme)
235
var info = name + ' ' + priority + progresslabel;
235
var info = name + ' ' + url + priority + dupe + health + backup + propagation + progresslabel;
236
236
item.fields = ['<div class="check img-check"></div>', status, info, category, item.data.age, progress, item.data.estimated];
240
var info = '<div class="check img-check"></div><span class="row-title">' + name + '</span>' +
241
' ' + (group.status === 'queued' ? '' : status) + ' ' + priority;
240
var info = '<div class="check img-check"></div><span class="row-title">' + name + '</span>' + url +
241
' ' + (group.Status === 'QUEUED' ? '' : status) + ' ' + priority + dupe + health + backup + propagation;
244
244
info += ' <span class="label label-status">' + category + '</span>';
259
259
cell.className = 'text-right';
263
function detectStatus(group)
263
function calcBackupPercent(group)
265
group.paused = (group.PausedSizeLo != 0) && (group.RemainingSizeLo == group.PausedSizeLo);
266
group.postprocess = group.post !== undefined;
267
if (group.postprocess)
269
switch (group.post.Stage)
271
case 'QUEUED': group.status = 'pp-queued'; break;
272
case 'LOADING_PARS': group.status = 'checking'; break;
273
case 'VERIFYING_SOURCES': group.status = 'checking'; break;
274
case 'REPAIRING': group.status = 'repairing'; break;
275
case 'VERIFYING_REPAIRED': group.status = 'verifying'; break;
276
case 'RENAMING': group.status = 'renaming'; break;
277
case 'MOVING': group.status = 'moving'; break;
278
case 'UNPACKING': group.status = 'unpacking'; break;
279
case 'EXECUTING_SCRIPT': group.status = 'processing'; break;
280
case 'FINISHED': group.status = 'finished'; break;
281
default: group.status = 'error: ' + group.post.Stage; break;
265
var downloadedArticles = group.SuccessArticles + group.FailedArticles;
266
if (downloadedArticles === 0)
271
if (minLevel === null)
273
for (var i=0; i < Status.status.NewsServers.length; i++)
275
var server = Status.status.NewsServers[i];
276
var level = parseInt(Options.option('Server' + server.ID + '.Level'));
277
if (minLevel === null || minLevel > level)
284
var backupArticles = 0;
285
for (var j=0; j < group.ServerStats.length; j++)
287
var stat = group.ServerStats[j];
288
var level = parseInt(Options.option('Server' + stat.ServerID + '.Level'));
289
if (level > minLevel && stat.SuccessArticles > 0)
291
backupArticles += stat.SuccessArticles;
284
else if (group.ActiveDownloads > 0)
286
group.status = 'downloading';
288
else if (group.paused)
290
group.status = 'paused';
294
group.status = 'queued';
295
var backupPercent = 0;
296
if (backupArticles > 0)
298
backupPercent = backupArticles * 100.0 / downloadedArticles;
300
return backupPercent;
298
303
this.recordsPerPageChange = function()
307
312
updateTabInfo($DownloadsTabBadge, stat);
315
function calcProgressLabels()
317
var progressLabels = $('.label-inline', $DownloadsTable);
319
if (UISettings.miniTheme)
321
nameColumnWidth = null;
322
progressLabels.css('max-width', '');
326
progressLabels.hide();
327
nameColumnWidth = Math.max($DownloadsTable_Name.width(), 50) - 4*2; // 4 - padding of span
328
progressLabels.css('max-width', nameColumnWidth);
329
progressLabels.show();
310
332
/*** EDIT ******************************************************/
334
function itemClick(e)
314
var nzbid = $(this).attr('nzbid');
337
var nzbid = $(this).attr('data-nzbid');
338
var area = $(this).attr('data-area');
316
DownloadsEditDialog.showModal(nzbid, groups);
340
DownloadsEditDialog.showModal(nzbid, groups, area);
319
343
function editCompleted()
559
var categoryColumnWidth = null;
560
var dupeCheck = null;
531
562
this.fillPriorityCombo = function(combo)
565
combo.append('<option value="900">force</option>');
566
combo.append('<option value="100">very high</option>');
567
combo.append('<option value="50">high</option>');
568
combo.append('<option value="0">normal</option>');
569
combo.append('<option value="-50">low</option>');
534
570
combo.append('<option value="-100">very low</option>');
535
combo.append('<option value="-50">low</option>');
536
combo.append('<option value="0">normal</option>');
537
combo.append('<option value="50">high</option>');
538
combo.append('<option value="100">very high</option>');
541
573
this.fillCategoryCombo = function(combo)
584
this.buildStatusText = function(group)
586
var statusText = Downloads.statusData[group.Status].Text;
587
if (statusText === undefined)
589
statusText = 'Internal error(' + group.Status + ')';
552
594
this.buildStatus = function(group)
554
if (group.postprocess && group.status !== 'pp-queued')
556
if (Status.status.PostPaused)
558
return '<span class="label label-status label-warning">' + group.status + '</span>';
562
return '<span class="label label-status label-success">' + group.status + '</span>';
565
switch (group.status)
567
case 'pp-queued': return '<span class="label label-status">pp-queued</span>';
568
case 'downloading': return '<span class="label label-status label-success">downloading</span>';
569
case 'paused': return '<span class="label label-status label-warning">paused</span>';
570
case 'queued': return '<span class="label label-status">queued</span>';
571
default: return '<span class="label label-status label-important">internal error(' + group.status + ')</span>';
596
var statusText = Downloads.statusData[group.Status].Text;
599
if (group.postprocess && group.Status !== 'PP_QUEUED')
601
badgeClass = Status.status.PostPaused && group.MinPriority < 900 ? 'label-warning' : 'label-success';
603
else if (group.Status === 'DOWNLOADING' || group.Status === 'FETCHING')
605
badgeClass = 'label-success';
607
else if (group.Status === 'PAUSED')
609
badgeClass = 'label-warning';
611
else if (statusText === undefined)
613
statusText = 'INTERNAL_ERROR (' + group.Status + ')';
614
badgeClass = 'label-important';
617
return '<span class="label label-status ' + badgeClass + '">' + statusText + '</span>';
575
620
this.buildProgress = function(group, totalsize, remaining, estimated)
577
if (group.status === 'downloading' || (group.postprocess && !Status.status.PostPaused))
622
if (group.Status === 'DOWNLOADING' ||
623
(group.postprocess && !(Status.status.PostPaused && group.MinPriority < 900)))
579
625
var kind = 'progress-success';
581
else if (group.status === 'paused' || (group.postprocess && Status.status.PostPaused))
627
else if (group.Status === 'PAUSED' ||
628
(group.postprocess && !(Status.status.PostPaused && group.MinPriority < 900)))
583
630
var kind = 'progress-warning';
657
710
case "VERIFYING_REPAIRED":
658
711
case "UNPACKING":
660
text = group.post.ProgressLabel;
713
text = group.PostInfoText;
662
715
case "EXECUTING_SCRIPT":
663
if (group.post.Log && group.post.Log.length > 0)
716
if (group.Log && group.Log.length > 0)
665
text = group.post.Log[group.post.Log.length-1].Text;
718
text = group.Log[group.Log.length-1].Text;
666
719
// remove "for <nzb-name>" from label text
667
720
text = text.replace(' for ' + group.NZBName, ' ');
671
text = group.post.ProgressLabel;
724
text = group.PostInfoText;
677
return text !== '' ? ' <span class="label label-success">' + text + '</span>' : '';
730
return text !== '' ? ' <span class="label label-success label-inline" style="max-width:' +
731
maxWidth +'px">' + text + '</span>' : '';
680
734
this.buildPriorityText = function(priority)
709
765
return ' <span class="label label-priority label-info">priority: ' + priority + '</span>';
769
function formatDupeText(dupeKey, dupeScore, dupeMode)
771
dupeKey = dupeKey.replace('rageid=', '');
772
dupeKey = dupeKey.replace('imdb=', '');
773
dupeKey = dupeKey.replace('series=', '');
774
dupeKey = dupeKey.replace('nzb=', '#');
775
dupeKey = dupeKey.replace('=', ' ');
776
dupeKey = dupeKey === '' ? 'title' : dupeKey;
780
this.buildDupeText = function(dupeKey, dupeScore, dupeMode)
782
if (dupeCheck == null)
784
dupeCheck = Options.option('DupeCheck') === 'yes';
787
if (dupeCheck && dupeKey != '' && UISettings.dupeBadges)
789
return formatDupeText(dupeKey, dupeScore, dupeMode);
797
this.buildDupe = function(dupeKey, dupeScore, dupeMode)
799
if (dupeCheck == null)
801
dupeCheck = Options.option('DupeCheck') === 'yes';
804
if (dupeCheck && dupeKey != '' && UISettings.dupeBadges)
806
return ' <span class="label' + (dupeMode === 'FORCE' ? ' label-important' : '') +
807
'" title="Duplicate key: ' + dupeKey +
808
(dupeScore !== 0 ? '; score: ' + dupeScore : '') +
809
(dupeMode !== 'SCORE' ? '; mode: ' + dupeMode.toLowerCase() : '') +
810
'">' + formatDupeText(dupeKey, dupeScore, dupeMode) + '</span> ';
818
this.resetCategoryColumnWidth = function()
820
categoryColumnWidth = null;
823
this.calcCategoryColumnWidth = function()
825
if (categoryColumnWidth === null)
827
var widthHelper = $('<div></div>').css({'position': 'absolute', 'float': 'left', 'white-space': 'nowrap', 'visibility': 'hidden'}).appendTo($('body'));
829
// default (min) width
830
categoryColumnWidth = 60;
832
for (var i = 1; ; i++)
834
var opt = Options.option('Category' + i + '.Name');
839
widthHelper.text(opt);
840
var catWidth = widthHelper.width();
841
categoryColumnWidth = Math.max(categoryColumnWidth, catWidth);
844
widthHelper.remove();
846
categoryColumnWidth += 'px';
849
return categoryColumnWidth;
852
this.deleteConfirm = function(actionCallback, multi, hasNzb, hasUrl)
854
var dupeCheck = Options.option('DupeCheck') === 'yes';
855
var cleanupDisk = Options.option('DeleteCleanupDisk') === 'yes';
856
var history = Options.option('KeepHistory') !== '0';
859
function init(_dialog)
865
var html = $('#ConfirmDialog_Text').html();
866
html = html.replace(/downloads/g, 'download');
867
$('#ConfirmDialog_Text').html(html);
870
$('#DownloadsDeleteConfirmDialog_Delete', dialog).prop('checked', true);
871
$('#DownloadsDeleteConfirmDialog_Delete', dialog).prop('checked', true);
872
$('#DownloadsDeleteConfirmDialog_DeleteDupe', dialog).prop('checked', false);
873
$('#DownloadsDeleteConfirmDialog_DeleteFinal', dialog).prop('checked', false);
874
Util.show($('#DownloadsDeleteConfirmDialog_Options', dialog), history);
875
Util.show($('#DownloadsDeleteConfirmDialog_Simple', dialog), !history);
876
Util.show($('#DownloadsDeleteConfirmDialog_DeleteDupe,#DownloadsDeleteConfirmDialog_DeleteDupeLabel', dialog), dupeCheck && hasNzb);
877
Util.show($('#DownloadsDeleteConfirmDialog_Remain', dialog), !cleanupDisk && hasNzb);
878
Util.show($('#DownloadsDeleteConfirmDialog_Cleanup', dialog), cleanupDisk && hasNzb);
879
Util.show('#ConfirmDialog_Help', history && dupeCheck && hasNzb);
884
var deleteNormal = $('#DownloadsDeleteConfirmDialog_Delete', dialog).is(':checked');
885
var deleteDupe = $('#DownloadsDeleteConfirmDialog_DeleteDupe', dialog).is(':checked');
886
var deleteFinal = $('#DownloadsDeleteConfirmDialog_DeleteFinal', dialog).is(':checked');
887
var command = deleteNormal ? 'GroupDelete' : (deleteDupe ? 'GroupDupeDelete' : 'GroupFinalDelete');
888
actionCallback(command);
891
ConfirmDialog.showModal('DownloadsDeleteConfirmDialog', action, init);