You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
550 lines
18 KiB
550 lines
18 KiB
/**
|
|
Model for the whole History with all its items
|
|
**/
|
|
function HistoryListModel(parent) {
|
|
var self = this;
|
|
self.parent = parent;
|
|
|
|
// Variables
|
|
self.lastUpdate = 0;
|
|
self.historyItems = ko.observableArray([])
|
|
self.showFailed = ko.observable(false);
|
|
self.isLoading = ko.observable(false).extend({ rateLimit: 100 });
|
|
self.searchTerm = ko.observable('').extend({ rateLimit: { timeout: 200, method: "notifyWhenChangesStop" } });
|
|
self.paginationLimit = ko.observable(10).extend({ persist: 'historyPaginationLimit' });
|
|
self.totalItems = ko.observable(0);
|
|
self.pagination = new paginationModel(self);
|
|
|
|
// Download history info
|
|
self.downloadedToday = ko.observable();
|
|
self.downloadedWeek = ko.observable();
|
|
self.downloadedMonth = ko.observable();
|
|
self.downloadedTotal = ko.observable();
|
|
|
|
// Update function for history list
|
|
self.updateFromData = function(data) {
|
|
/***
|
|
See if there's anything to update
|
|
***/
|
|
if(!data) return;
|
|
self.lastUpdate = data.last_history_update
|
|
|
|
/***
|
|
History list functions per item
|
|
***/
|
|
var itemIds = $.map(self.historyItems(), function(i) {
|
|
return i.historyStatus.nzo_id();
|
|
});
|
|
|
|
// For new items
|
|
var newItems = [];
|
|
$.each(data.slots, function(index, slot) {
|
|
var existingItem = ko.utils.arrayFirst(self.historyItems(), function(i) {
|
|
return i.historyStatus.nzo_id() == slot.nzo_id;
|
|
});
|
|
// Set index in the results
|
|
slot.index = index
|
|
|
|
// Update or add?
|
|
if(existingItem) {
|
|
existingItem.updateFromData(slot);
|
|
itemIds.splice(itemIds.indexOf(slot.nzo_id), 1);
|
|
} else {
|
|
// Add history item
|
|
newItems.push(new HistoryModel(self, slot));
|
|
}
|
|
});
|
|
|
|
// Remove all items
|
|
if(itemIds.length == self.paginationLimit()) {
|
|
// Replace it, so only 1 Knockout DOM-update!
|
|
self.historyItems(newItems);
|
|
newItems = [];
|
|
} else {
|
|
// Remove the un-used ones
|
|
$.each(itemIds, function() {
|
|
var id = this.toString();
|
|
self.historyItems.remove(ko.utils.arrayFirst(self.historyItems(), function(i) {
|
|
return i.historyStatus.nzo_id() == id;
|
|
}));
|
|
});
|
|
}
|
|
|
|
// Add new ones
|
|
if(newItems.length > 0) {
|
|
ko.utils.arrayPushAll(self.historyItems, newItems);
|
|
self.historyItems.valueHasMutated();
|
|
|
|
// We also check if it might be in the Multi-edit
|
|
if(self.parent.queue.multiEditItems().length > 0) {
|
|
$.each(newItems, function() {
|
|
var currentItem = this;
|
|
self.parent.queue.multiEditItems.remove(function(inList) { return inList.id == currentItem.nzo_id; })
|
|
})
|
|
}
|
|
}
|
|
|
|
// Sort every time (takes just few msec)
|
|
self.historyItems.sort(function(a, b) {
|
|
return a.index < b.index ? -1 : 1;
|
|
});
|
|
|
|
/***
|
|
History information
|
|
***/
|
|
self.totalItems(data.noofslots);
|
|
self.downloadedToday(data.day_size);
|
|
self.downloadedWeek(data.week_size);
|
|
self.downloadedMonth(data.month_size);
|
|
self.downloadedTotal(data.total_size);
|
|
};
|
|
|
|
// Save pagination state
|
|
self.paginationLimit.subscribe(function(newValue) {
|
|
// Save in config if global config
|
|
if(self.parent.useGlobalOptions()) {
|
|
callAPI({
|
|
mode: "set_config",
|
|
section: "misc",
|
|
keyword: "history_limit",
|
|
value: newValue
|
|
})
|
|
}
|
|
});
|
|
|
|
// Retry a job
|
|
self.retryJob = function(form) {
|
|
// Adding a extra retry file happens through this special function
|
|
var data = new FormData();
|
|
data.append("nzbfile", $(form.nzbFile)[0].files[0]);
|
|
data.append("job", $('#modal-retry-job input[name="retry_job_id"]').val());
|
|
data.append("password", $('#retry_job_password').val());
|
|
data.append("apikey", apiKey);
|
|
|
|
// Add
|
|
$.ajax({
|
|
url: "./retry_pp",
|
|
type: "POST",
|
|
cache: false,
|
|
processData: false,
|
|
contentType: false,
|
|
data: data
|
|
}).then(function() {
|
|
self.parent.refresh(true)
|
|
});
|
|
|
|
$("#modal-retry-job").modal("hide");
|
|
$('.btn-file em').html(glitterTranslate.chooseFile + '…')
|
|
form.reset()
|
|
}
|
|
|
|
// Searching in history (rate-limited in decleration)
|
|
self.searchTerm.subscribe(function() {
|
|
// Make sure we refresh
|
|
self.lastUpdate = 0
|
|
self.parent.refresh();
|
|
// Go back to page 1
|
|
if(self.pagination.currentPage() != 1) {
|
|
self.pagination.moveToPage(1);
|
|
}
|
|
})
|
|
|
|
// Clear searchterm
|
|
self.clearSearchTerm = function(data, event) {
|
|
// Was it escape key or click?
|
|
if(event.type == 'mousedown' || (event.keyCode && event.keyCode == 27)) {
|
|
// Set the loader so it doesn't flicker and then switch
|
|
self.isLoading(true)
|
|
self.searchTerm('');
|
|
self.parent.refresh()
|
|
}
|
|
// Was it click and the field is empty? Then we focus on the field
|
|
if(event.type == 'mousedown' && self.searchTerm() == '') {
|
|
$(event.target).parents('.search-box').find('input[type="text"]').focus()
|
|
return;
|
|
}
|
|
// Need to return true to allow typing
|
|
return true;
|
|
}
|
|
|
|
// Toggle showing failed
|
|
self.toggleShowFailed = function(data, event) {
|
|
// Set the loader so it doesn't flicker and then switch
|
|
self.isLoading(true)
|
|
self.showFailed(!self.showFailed())
|
|
// Forde hide tooltip so it doesn't linger
|
|
$('#history-options a').tooltip('hide')
|
|
// Force refresh
|
|
self.parent.refresh(true)
|
|
}
|
|
|
|
// Retry all failed
|
|
self.retryAllFailed = function(data, event) {
|
|
// Ask to be sure
|
|
if(confirm(glitterTranslate.retryAll)) {
|
|
// Send the command
|
|
callAPI({
|
|
mode: 'retry_all'
|
|
}).then(function() {
|
|
// Force refresh
|
|
self.parent.refresh(true)
|
|
})
|
|
}
|
|
}
|
|
|
|
// Empty history options
|
|
self.emptyHistory = function(data, event) {
|
|
// Make sure no flickering
|
|
self.isLoading(true)
|
|
|
|
// What event?
|
|
var whatToRemove = $(event.target).data('action');
|
|
var del_files, value;
|
|
|
|
// Purge failed
|
|
if(whatToRemove == 'history-purge-failed') {
|
|
del_files = 0;
|
|
value = 'failed';
|
|
}
|
|
// Also remove files
|
|
if(whatToRemove == 'history-purgeremove-failed') {
|
|
del_files = 1;
|
|
value = 'failed';
|
|
}
|
|
// Remove completed
|
|
if(whatToRemove == 'history-purge-completed') {
|
|
del_files = 0;
|
|
value = 'completed';
|
|
}
|
|
// Remove the ones on this page
|
|
if(whatToRemove == 'history-purge-page') {
|
|
// List all the ID's
|
|
var strIDs = '';
|
|
$.each(self.historyItems(), function(index) {
|
|
// Only append when it's a download that can be deleted
|
|
if(!this.processingDownload() && !this.processingWaiting()) {
|
|
strIDs = strIDs + this.nzo_id + ',';
|
|
}
|
|
})
|
|
// Send the command
|
|
callAPI({
|
|
mode: 'history',
|
|
name: 'delete',
|
|
del_files: 1,
|
|
value: strIDs
|
|
}).then(function() {
|
|
// Clear search, refresh and hide
|
|
self.searchTerm('');
|
|
self.parent.refresh();
|
|
$("#modal-purge-history").modal('hide');
|
|
})
|
|
return;
|
|
}
|
|
|
|
// Call API and close the window
|
|
callAPI({
|
|
mode: 'history',
|
|
name: 'delete',
|
|
value: value,
|
|
del_files: del_files
|
|
}).then(function() {
|
|
self.parent.refresh();
|
|
$("#modal-purge-history").modal('hide');
|
|
});
|
|
};
|
|
}
|
|
|
|
/**
|
|
Model for each History item
|
|
**/
|
|
function HistoryModel(parent, data) {
|
|
var self = this;
|
|
self.parent = parent;
|
|
|
|
// We only update the whole set of information on first add
|
|
// If we update the full set every time it uses lot of CPU
|
|
// The Status/Actionline/scriptline/completed we do update every time
|
|
// When clicked on the more-info button we load the rest again
|
|
self.nzo_id = data.nzo_id;
|
|
self.index = data.index;
|
|
self.updateAllHistory = false;
|
|
self.hasDropdown = ko.observable(false);
|
|
self.historyStatus = ko.mapping.fromJS(data);
|
|
self.status = ko.observable(data.status);
|
|
self.action_line = ko.observable(data.action_line);
|
|
self.script_line = ko.observable(data.script_line);
|
|
self.fail_message = ko.observable(data.fail_message);
|
|
self.completed = ko.observable(data.completed);
|
|
self.canRetry = ko.observable(data.retry);
|
|
|
|
// Update function
|
|
self.updateFromData = function(data) {
|
|
// Fill all the basic info
|
|
self.index = data.index
|
|
self.status(data.status)
|
|
self.action_line(data.action_line)
|
|
self.script_line(data.script_line)
|
|
self.fail_message(data.fail_message)
|
|
self.completed(data.completed)
|
|
self.canRetry(data.retry)
|
|
|
|
// Update all ONCE?
|
|
if(self.updateAllHistory) {
|
|
ko.mapping.fromJS(data, {}, self.historyStatus);
|
|
self.updateAllHistory = false;
|
|
}
|
|
};
|
|
|
|
// True/false if failed or not
|
|
self.failed = ko.pureComputed(function() {
|
|
return self.status() === 'Failed';
|
|
});
|
|
|
|
// Waiting?
|
|
self.processingWaiting = ko.pureComputed(function() {
|
|
return(self.status() == 'Queued')
|
|
})
|
|
|
|
// Processing or done?
|
|
self.processingDownload = ko.pureComputed(function() {
|
|
var status = self.status();
|
|
// When we can cancel
|
|
if (status === 'Extracting' || status === 'Verifying' || status == 'Repairing' || status === 'Running') {
|
|
return 2
|
|
}
|
|
// These cannot be cancelled
|
|
if(status === 'Moving') {
|
|
return 1
|
|
}
|
|
return false;
|
|
})
|
|
|
|
// Format status text
|
|
self.statusText = ko.pureComputed(function() {
|
|
if(self.action_line() !== '')
|
|
return self.action_line();
|
|
if(self.status() === 'Failed') // Failed
|
|
return self.fail_message();
|
|
if(self.status() === 'Queued')
|
|
return glitterTranslate.status['Queued'];
|
|
if(self.script_line() === '') // No script line
|
|
return glitterTranslate.status['Completed']
|
|
|
|
return self.script_line();
|
|
});
|
|
|
|
// Extra history columns
|
|
self.showColumn = function(param) {
|
|
// Picked anything?
|
|
switch(param) {
|
|
case 'speed':
|
|
// Anything to calculate?
|
|
if(self.historyStatus.bytes() > 0 && self.historyStatus.download_time() > 0) {
|
|
try {
|
|
// Extract the Download section
|
|
var downloadLog = ko.utils.arrayFirst(self.historyStatus.stage_log(), function(item) {
|
|
return item.name() == 'Download'
|
|
});
|
|
// Extract the speed
|
|
return downloadLog.actions()[0].match(/(\S*\s\S+)(?=<br\/>)/)[0]
|
|
} catch(err) { }
|
|
}
|
|
return;
|
|
case 'category':
|
|
// Exception for *
|
|
if(self.historyStatus.category() == "*")
|
|
return glitterTranslate.defaultText
|
|
return self.historyStatus.category();
|
|
case 'size':
|
|
return self.historyStatus.size();
|
|
}
|
|
return;
|
|
};
|
|
|
|
// Format completion time
|
|
self.completedOn = ko.pureComputed(function() {
|
|
return displayDateTime(self.completed(), parent.parent.dateFormat(), 'X')
|
|
});
|
|
|
|
// Subscribe to retryEvent so we can load the password
|
|
self.canRetry.subscribe(function() {
|
|
self.updateAllHistory = true;
|
|
})
|
|
|
|
// Re-try button
|
|
self.retry = function() {
|
|
// Set JOB-id
|
|
$('#modal-retry-job input[name="retry_job_id"]').val(self.nzo_id)
|
|
// Set password
|
|
$('#retry_job_password').val(self.historyStatus.password())
|
|
// Open modal
|
|
$('#modal-retry-job').modal("show")
|
|
};
|
|
|
|
// Update information only on click
|
|
self.updateAllHistoryInfo = function(data, event) {
|
|
// Show
|
|
self.hasDropdown(true);
|
|
|
|
// Update all info
|
|
self.updateAllHistory = true;
|
|
parent.parent.refresh(true);
|
|
|
|
// Try to keep open
|
|
keepOpen(event.target)
|
|
}
|
|
|
|
// Use KO-afterRender to add the click-functionality always
|
|
self.addHistoryStatusStuff = function(item) {
|
|
$(item).find('.history-status-modallink a').click(function(e) {
|
|
// Modal or 'More' click?
|
|
if($(this).is('.history-status-dmca')) {
|
|
// Pass
|
|
return true;
|
|
} else if($(this).is('.history-status-more')) {
|
|
// Expand the rest of the text and hide the button
|
|
$(this).siblings('.history-status-hidden').slideDown()
|
|
$(this).hide()
|
|
} else {
|
|
// Info in modal
|
|
$('#history-script-log .modal-body').load($(this).attr('href'), function(result) {
|
|
// Set title and then remove it
|
|
$('#history-script-log .modal-title').text($(this).find("h3").text())
|
|
$(this).find("h3, title").remove()
|
|
$('#history-script-log').modal('show');
|
|
});
|
|
}
|
|
return false;
|
|
})
|
|
}
|
|
|
|
// Delete button
|
|
self.deleteSlot = function(item, event) {
|
|
// Confirm?
|
|
if(!self.parent.parent.confirmDeleteHistory() || confirm(glitterTranslate.deleteMsg + ":\n" + item.historyStatus.name() + "\n\n" + glitterTranslate.removeDow1)) {
|
|
// Are we still processing and it can be stopped?
|
|
if(item.processingDownload() == 2) {
|
|
callAPI({
|
|
mode: 'cancel_pp',
|
|
value: self.nzo_id
|
|
})
|
|
// All we can do is wait
|
|
} else {
|
|
// Delete the item
|
|
callAPI({
|
|
mode: 'history',
|
|
name: 'delete',
|
|
del_files: 1,
|
|
value: self.nzo_id
|
|
}).then(function(response) {
|
|
if(response.status) {
|
|
// Make sure no flickering (if there are more items left) and then remove
|
|
self.parent.isLoading(self.parent.totalItems() > 1)
|
|
self.parent.historyItems.remove(self);
|
|
self.parent.parent.refresh();
|
|
}
|
|
});
|
|
}
|
|
|
|
}
|
|
};
|
|
|
|
// User voting
|
|
self.setUserVote = function(item, event) {
|
|
// Send vote
|
|
callAPI({
|
|
mode: 'queue',
|
|
name: 'rating',
|
|
type: 'vote',
|
|
setting: $(event.target).val(),
|
|
value: self.nzo_id
|
|
}).then(function(response) {
|
|
// Update all info
|
|
self.updateAllHistory = true;
|
|
self.parent.parent.refresh(true)
|
|
})
|
|
}
|
|
|
|
// User rating
|
|
self.setUserRating = function(item, event) {
|
|
// Audio or video
|
|
var changeWhat = 'audio';
|
|
if($(event.target).attr('name') == 'ratings-video') {
|
|
changeWhat = 'video';
|
|
}
|
|
|
|
// Only on user-event, not the auto-fired ones
|
|
if(!event.originalEvent) return;
|
|
|
|
// Send vote
|
|
callAPI({
|
|
mode: 'queue',
|
|
name: 'rating',
|
|
type: changeWhat,
|
|
setting: $(event.target).val(),
|
|
value: self.nzo_id
|
|
}).then(function(response) {
|
|
// Update all info
|
|
self.updateAllHistory = true;
|
|
self.parent.parent.refresh(true)
|
|
})
|
|
}
|
|
|
|
// User comment
|
|
self.setUserReport = function(form) {
|
|
// What are we reporting?
|
|
var userReport = $(form).find('input[name="rating_flag"]:checked').val();
|
|
var userDetail = '';
|
|
|
|
// Anything selected?
|
|
if(!userReport) {
|
|
alert(glitterTranslate.noSelect)
|
|
return;
|
|
}
|
|
|
|
// Extra info?
|
|
if(userReport == 'comment') userDetail = $(form).find('input[name="ratings-report-comment"]').val();
|
|
if(userReport == 'other') userDetail = $(form).find('input[name="ratings-report-other"]').val();
|
|
|
|
// Exception for servers
|
|
if(userReport == 'expired') {
|
|
// Which server?
|
|
userDetail = $(form).find('select[name="ratings-report-expired-server"]').val();
|
|
|
|
// All?
|
|
if(userDetail == "") {
|
|
// Loop over all servers
|
|
$.each(parent.parent.servers, function(index, server) {
|
|
// Set timeout because simultanious requests don't work (yet)
|
|
setTimeout(function() {
|
|
submitUserReport(server.name)
|
|
}, index * 1500)
|
|
})
|
|
|
|
} else {
|
|
// Just the one server
|
|
submitUserReport(userDetail)
|
|
}
|
|
} else {
|
|
submitUserReport(userDetail)
|
|
}
|
|
|
|
// After all, close it
|
|
form.reset();
|
|
$(form).parent().parent().dropdown('toggle');
|
|
alert(glitterTranslate.sendThanks)
|
|
|
|
function submitUserReport(theDetail) {
|
|
// Send note
|
|
callAPI({
|
|
mode: 'queue',
|
|
name: 'rating',
|
|
type: 'flag',
|
|
setting: userReport,
|
|
detail: theDetail,
|
|
value: self.nzo_id
|
|
})
|
|
}
|
|
return false
|
|
}
|
|
}
|