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.

530 lines
11 KiB

Seeding support Design intent: - Option to turn seeding support on or off - After torrent downloading is complete the seeding phase starts, seeding parameters can be set per torrent provide (0 disables them) - When the seeding phase starts the checkSnatched function renames all files if (sym)linking/copying is used. The movie is set to done (!), the release to seeding status. - Note that Direct symlink functionality is removed as the original file needs to end up in the movies store and not the downloader store (if the downloader cleans up his files, the original is deleted and the symlinks are useless) - checkSnatched waits until downloader sets the download to completed (met the seeding parameters) - When completed, checkSnatched intiates the renamer if move is used, or if linking is used asks the downloader to remove the torrent and clean-up it's files and sets the release to downloaded - Updated some of the .ignore file behavior to allow the downloader to remove its files Known items/issues: - only implemented for uTorrent and Transmission - text in downloader settings is too long and messes up the layout... To do (after this PR): - implement for other torrent downloaders - complete download removal for NZBs (remove from history in sabNZBd) - failed download management for torrents (no seeders, takes too long, etc.) - unrar support Updates: - Added transmission support - Simplified uTorrent - Added checkSnatched to renamer to make sure the poller is always first - Updated default values and removed advanced option tag for providers - Updated the tagger to allow removing of ignore tags and tagging when the group is not known - Added tagging of downloading torrents - fixed subtitles being leftover after seeding
12 years ago
var CouchPotato = new Class({
14 years ago
Implements: [Events, Options],
14 years ago
defaults: {
12 years ago
page: 'home',
14 years ago
action: 'index',
params: {}
},
pages: [],
block: [],
14 years ago
setup: function(options) {
14 years ago
var self = this;
self.setOptions(options);
self.c = $(document.body);
14 years ago
self.route = new Route(self.defaults);
14 years ago
self.createLayout();
self.createPages();
if(window.location.hash)
History.handleInitialState();
else
self.openPage(window.location.pathname);
14 years ago
History.addEvent('change', self.openPage.bind(self));
self.c.addEvent('click:relay(a[href^=/]:not([target]))', self.pushState.bind(self));
self.c.addEvent('click:relay(a[href^=http])', self.openDerefered.bind(self));
// Check if device is touchenabled
self.touch_device = 'ontouchstart' in document.documentElement;
if(self.touch_device)
self.c.addClass('touch_enabled');
},
14 years ago
getOption: function(name){
14 years ago
try {
return this.options[name];
}
catch(e){
return null
}
},
pushState: function(e){
if((!e.meta && Browser.Platform.mac) || (!e.control && !Browser.Platform.mac)){
(e).preventDefault();
var url = e.target.get('href');
if(History.getPath() != url)
History.push(url);
}
14 years ago
},
isMac: function(){
return Browser.Platform.mac
},
14 years ago
createLayout: function(){
var self = this;
14 years ago
self.block.header = new Block();
14 years ago
self.c.adopt(
14 years ago
$(self.block.header).addClass('header').adopt(
new Element('div').adopt(
self.block.navigation = new Block.Navigation(self, {}),
self.block.search = new Block.Search(self, {}),
self.block.more = new Block.Menu(self, {'button_class': 'icon2.cog'})
)
14 years ago
),
self.content = new Element('div.content'),
self.block.footer = new Block.Footer(self, {})
);
12 years ago
var setting_links = [
new Element('a', {
'text': 'About CouchPotato',
'href': App.createUrl('settings/about')
}),
new Element('a', {
'text': 'Check for Updates',
'events': {
'click': self.checkForUpdate.bind(self, null)
}
}),
new Element('span.separator'),
new Element('a', {
'text': 'Settings',
'href': App.createUrl('settings/general')
}),
new Element('a', {
'text': 'Logs',
'href': App.createUrl('log')
}),
new Element('span.separator'),
new Element('a', {
'text': 'Restart',
'events': {
'click': self.restartQA.bind(self)
}
}),
new Element('a', {
'text': 'Shutdown',
'events': {
'click': self.shutdownQA.bind(self)
}
})
];
12 years ago
setting_links.each(function(a){
self.block.more.addLink(a)
});
new ScrollSpy({
min: 10,
onLeave: function(){
$(self.block.header).removeClass('with_shadow')
},
onEnter: function(){
$(self.block.header).addClass('with_shadow')
}
})
14 years ago
},
createPages: function(){
14 years ago
var self = this;
Object.each(Page, function(page_class, class_name){
var pg = new Page[class_name](self, {});
self.pages[class_name] = pg;
14 years ago
$(pg).inject(self.content);
});
self.fireEvent('load');
},
14 years ago
openPage: function(url) {
14 years ago
var self = this;
13 years ago
self.route.parse();
var page_name = self.route.getPage().capitalize();
var action = self.route.getAction();
var params = self.route.getParams();
14 years ago
var current_url = self.route.getCurrentUrl();
if(current_url == self.current_url)
return;
if(self.current_page)
self.current_page.hide();
try {
12 years ago
var page = self.pages[page_name] || self.pages.Home;
page.open(action, params, current_url);
14 years ago
page.show();
}
catch(e){
14 years ago
console.error("Can't open page:" + url, e)
}
14 years ago
self.current_page = page;
14 years ago
self.current_url = current_url;
},
getBlock: function(block_name){
return this.block[block_name]
},
getPage: function(name){
return this.pages[name]
},
shutdown: function(){
var self = this;
Seeding support Design intent: - Option to turn seeding support on or off - After torrent downloading is complete the seeding phase starts, seeding parameters can be set per torrent provide (0 disables them) - When the seeding phase starts the checkSnatched function renames all files if (sym)linking/copying is used. The movie is set to done (!), the release to seeding status. - Note that Direct symlink functionality is removed as the original file needs to end up in the movies store and not the downloader store (if the downloader cleans up his files, the original is deleted and the symlinks are useless) - checkSnatched waits until downloader sets the download to completed (met the seeding parameters) - When completed, checkSnatched intiates the renamer if move is used, or if linking is used asks the downloader to remove the torrent and clean-up it's files and sets the release to downloaded - Updated some of the .ignore file behavior to allow the downloader to remove its files Known items/issues: - only implemented for uTorrent and Transmission - text in downloader settings is too long and messes up the layout... To do (after this PR): - implement for other torrent downloaders - complete download removal for NZBs (remove from history in sabNZBd) - failed download management for torrents (no seeders, takes too long, etc.) - unrar support Updates: - Added transmission support - Simplified uTorrent - Added checkSnatched to renamer to make sure the poller is always first - Updated default values and removed advanced option tag for providers - Updated the tagger to allow removing of ignore tags and tagging when the group is not known - Added tagging of downloading torrents - fixed subtitles being leftover after seeding
12 years ago
self.blockPage('You have shutdown. This is what is supposed to happen ;)');
Api.request('app.shutdown', {
'onComplete': self.blockPage.bind(self)
});
self.checkAvailable(1000);
},
shutdownQA: function(){
var self = this;
var q = new Question('Are you sure you want to shutdown CouchPotato?', '', [{
'text': 'Shutdown',
'class': 'shutdown red',
'events': {
'click': function(e){
(e).preventDefault();
self.shutdown();
q.close.delay(100, q);
}
}
}, {
'text': 'No, nevah!',
'cancel': true
}]);
},
13 years ago
restart: function(message, title){
var self = this;
self.blockPage(message || 'Restarting... please wait. If this takes too long, something must have gone wrong.', title);
Api.request('app.restart');
self.checkAvailable(1000);
},
restartQA: function(e, message, title){
var self = this;
var q = new Question('Are you sure you want to restart CouchPotato?', '', [{
'text': 'Restart',
'class': 'restart orange',
'events': {
'click': function(e){
(e).preventDefault();
self.restart(message, title);
q.close.delay(100, q);
}
}
}, {
'text': 'No, nevah!',
'cancel': true
}]);
},
checkForUpdate: function(onComplete){
var self = this;
Updater.check(onComplete);
self.blockPage('Please wait. If this takes too long, something must have gone wrong.', 'Checking for updates');
self.checkAvailable(3000);
},
checkAvailable: function(delay, onAvailable){
var self = this;
(function(){
Api.request('app.available', {
'onFailure': function(){
self.checkAvailable.delay(1000, self, [delay, onAvailable]);
self.fireEvent('unload');
},
'onSuccess': function(){
if(onAvailable)
onAvailable();
self.unBlockPage();
self.fireEvent('reload');
}
});
}).delay(delay || 0)
},
blockPage: function(message, title){
var self = this;
self.unBlockPage();
self.mask = new Element('div.mask').adopt(
new Element('div').adopt(
new Element('h1', {'text': title || 'Unavailable'}),
new Element('div', {'text': message || 'Something must have crashed.. check the logs ;)'})
)
).fade('hide').inject(document.body).fade('in');
createSpinner(self.mask, {
'top': -50
});
},
unBlockPage: function(){
var self = this;
if(self.mask)
self.mask.get('tween').start('opacity', 0).chain(function(){
this.element.destroy()
});
14 years ago
},
createUrl: function(action, params){
return this.options.base_url + (action ? action+'/' : '') + (params ? '?'+Object.toQueryString(params) : '')
},
openDerefered: function(e, el){
(e).stop();
var url = 'http://www.dereferer.org/?' + el.get('href');
if(el.get('target') == '_blank' || (e.meta && Browser.Platform.mac) || (e.control && !Browser.Platform.mac))
window.open(url);
else
window.location = url;
13 years ago
},
createUserscriptButtons: function(){
var host_url = window.location.protocol + '//' + window.location.host;
return new Element('div.group_userscript').adopt(
new Element('a.userscript.button', {
13 years ago
'text': 'Install userscript',
'href': Api.createUrl('userscript.get')+randomString()+'/couchpotato.user.js',
'target': '_blank'
}),
new Element('span.or[text=or]'),
13 years ago
new Element('span.bookmarklet').adopt(
new Element('a.button.orange', {
'text': '+CouchPotato',
'href': "javascript:void((function(){var e=document.createElement('script');e.setAttribute('type','text/javascript');e.setAttribute('charset','UTF-8');e.setAttribute('src','" +
host_url + Api.createUrl('userscript.bookmark') +
"?host="+ encodeURI(host_url + Api.createUrl('userscript.get')+randomString()+'/') +
"&r='+Math.random()*99999999);document.body.appendChild(e)})());",
'target': '',
'events': {
'click': function(e){
(e).stop();
13 years ago
alert('Drag it to your bookmark ;)')
}
}
}),
new Element('span', {
'text': '⇽ Drag this to your bookmarks'
})
)
);
}
});
window.App = new CouchPotato();
14 years ago
var Route = new Class({
defaults: {},
14 years ago
page: '',
action: 'index',
params: {},
initialize: function(defaults){
var self = this;
self.defaults = defaults
},
13 years ago
parse: function(){
14 years ago
var self = this;
var rep = function (pa) {
12 years ago
return pa.replace(Api.getOption('url'), '/').replace(App.getOption('base_url'), '/')
};
12 years ago
var path = rep(History.getPath());
12 years ago
if(path == '/' && location.hash){
path = rep(location.hash.replace('#', '/'))
}
self.current = path.replace(/^\/+|\/+$/g, '');
var url = self.current.split('/');
self.page = (url.length > 0) ? url.shift() : self.defaults.page;
self.action = (url.length > 0) ? url.shift() : self.defaults.action;
self.params = Object.merge({}, self.defaults.params);
if(url.length > 1){
var key;
url.each(function(el, nr){
if(nr%2 == 0)
key = el;
else if(key) {
self.params[key] = el;
key = null
}
})
}
else if(url.length == 1){
self.params[url] = true;
}
14 years ago
return self
},
getPage: function(){
return this.page
},
getAction: function(){
return this.action
},
getParams: function(){
return this.params
},
getCurrentUrl: function(){
return this.current
},
14 years ago
get: function(param){
return this.params[param]
}
});
var p = function(){
if(typeof(console) !== 'undefined' && console != null)
console.log(arguments)
};
14 years ago
(function(){
var events;
var check = function(e) {
var target = $(e.target);
var parents = target.getParents();
events.each(function(item) {
var element = item.element;
if (element != target && !parents.contains(element))
item.fn.call(element, e);
});
};
Element.Events.outerClick = {
onAdd : function(fn) {
if (!events) {
document.addEvent('click', check);
events = [];
}
events.push( {
element : this,
fn : fn
});
},
onRemove : function(fn) {
events = events.filter(function(item) {
return item.element != this || item.fn != fn;
}, this);
if (!events.length) {
document.removeEvent('click', check);
events = null;
}
}
};
})();
14 years ago
function randomString(length, extra) {
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz" + (extra ? '-._!@#$%^&*()+=' : '');
var stringLength = length || 8;
var randomString = '';
for (var i = 0; i < stringLength; i++) {
var rnum = Math.floor(Math.random() * chars.length);
randomString += chars.charAt(rnum);
}
return randomString;
}
(function(){
var keyPaths = [];
var saveKeyPath = function(path) {
keyPaths.push({
sign: (path[0] === '+' || path[0] === '-')? parseInt(path.shift()+1) : 1,
path: path
});
};
var valueOf = function(object, path) {
var ptr = object;
path.each(function(key) { ptr = ptr[key] });
return ptr;
};
var comparer = function(a, b) {
for (var i = 0, l = keyPaths.length; i < l; i++) {
var aVal = valueOf(a, keyPaths[i].path),
bVal = valueOf(b, keyPaths[i].path);
if (aVal > bVal) return keyPaths[i].sign;
if (aVal < bVal) return -keyPaths[i].sign;
}
return 0;
};
Array.implement({
sortBy: function(){
keyPaths.empty();
Array.each(arguments, function(argument) {
switch (typeOf(argument)) {
case "array": saveKeyPath(argument); break;
case "string": saveKeyPath(argument.match(/[+-]|[^.]+/g)); break;
}
});
return this.sort(comparer);
}
});
14 years ago
})();
var createSpinner = function(target, options){
var opts = Object.merge({
lines: 12,
length: 5,
width: 4,
radius: 9,
color: '#fff',
speed: 1.9,
trail: 53,
shadow: false,
hwaccel: true,
className: 'spinner',
zIndex: 2e9,
top: 'auto',
left: 'auto'
}, options);
return new Spinner(opts).spin(target);
};