Browse Source

Enabler option

pull/1/merge
Ruud 14 years ago
parent
commit
2b8e96e267
  1. 2
      couchpotato/cli.py
  2. 6
      couchpotato/core/__init__.py
  3. 5
      couchpotato/core/downloaders/base.py
  4. 10
      couchpotato/core/downloaders/blackhole/__init__.py
  5. 13
      couchpotato/core/downloaders/sabnzbd/__init__.py
  6. 5
      couchpotato/core/notifications/base.py
  7. 6
      couchpotato/core/notifications/growl/__init__.py
  8. 7
      couchpotato/core/notifications/nmj/__init__.py
  9. 5
      couchpotato/core/notifications/notifo/__init__.py
  10. 6
      couchpotato/core/notifications/plex/__init__.py
  11. 6
      couchpotato/core/notifications/prowl/__init__.py
  12. 6
      couchpotato/core/notifications/xbmc/__init__.py
  13. 7
      couchpotato/core/plugins/base.py
  14. 21
      couchpotato/core/plugins/file_browser/main.py
  15. 5
      couchpotato/core/providers/base.py
  16. 1
      couchpotato/core/providers/imdb/main.py
  17. 7
      couchpotato/core/providers/themoviedb/__init__.py
  18. 8
      couchpotato/core/providers/themoviedb/main.py
  19. 10
      couchpotato/core/settings/__init__.py
  20. 23
      couchpotato/static/scripts/block/search.js
  21. 39
      couchpotato/static/scripts/couchpotato.js
  22. 71
      couchpotato/static/scripts/library/eventstack.js
  23. 30
      couchpotato/static/scripts/library/eventstack_outerclick.js
  24. 171
      couchpotato/static/scripts/page/settings.js
  25. 12
      couchpotato/static/scripts/page/wanted.js
  26. 2
      couchpotato/templates/_desktop.html

2
couchpotato/cli.py

@ -133,7 +133,7 @@ def cmd_couchpotato(base_path, args):
# Register modules
app.register_module(web, url_prefix = '%s/' % url_base)
app.register_module(api, url_prefix = '%s/%s/' % (url_base, api_key if not debug else 'api'))
app.register_module(api, url_prefix = '%s/%s/' % (url_base, api_key))
# Go go go!
app.run(use_reloader = reloader)

6
couchpotato/core/__init__.py

@ -35,7 +35,7 @@ config = [{
},
{
'name': 'launch_browser',
'default': True,
'default': 1,
'type': 'bool',
'label': 'Launch Browser',
'description': 'Launch the browser when I start.',
@ -51,13 +51,13 @@ config = [{
{
'name': 'api_key',
'default': uuid4().hex,
'readonly': True,
'readonly': 1,
'label': 'Api Key',
'description': "This is top-secret! Don't share this!",
},
{
'name': 'debug',
'default': False,
'default': 0,
'type': 'bool',
'label': 'Debug',
'description': 'Enable debugging.',

5
couchpotato/core/downloaders/base.py

@ -1,6 +1,6 @@
from couchpotato.core.event import addEvent
from couchpotato.core.plugins.base import Plugin
from couchpotato.environment import Env
class Downloader(Plugin):
@ -10,9 +10,6 @@ class Downloader(Plugin):
def download(self, data = {}):
pass
def conf(self, attr):
return Env.setting(attr, self.__class__.__name__.lower())
def isDisabled(self):
return not self.isEnabled()

10
couchpotato/core/downloaders/blackhole/__init__.py

@ -10,20 +10,16 @@ config = [{
'tab': 'downloaders',
'name': 'blackhole',
'label': 'Black hole',
'description': 'Fill in your Sabnzbd settings.',
'description': 'Download NZBs to a specific folder.',
'options': [
{
'name': 'enabled',
'default': False,
'type': 'bool',
'label': 'Enabled',
'description': 'Send snatched NZBs to Sabnzbd',
'default': 0,
'type': 'enabler',
},
{
'name': 'directory',
'default': '',
'type': 'directory',
'label': 'Directory',
'description': 'Directory where the .nzb (or .torrent) file is saved to.',
},
],

13
couchpotato/core/downloaders/sabnzbd/__init__.py

@ -10,26 +10,19 @@ config = [{
'tab': 'downloaders',
'name': 'sabnzbd',
'label': 'Sabnzbd',
'description': 'Fill in your Sabnzbd settings.',
'description': 'Send NZBs to your Sabnzbd installation.',
'options': [
{
'name': 'enabled',
'default': False,
'type': 'bool',
'label': 'Enabled',
'description': 'Send snatched NZBs to Sabnzbd',
'default': 0,
'type': 'enabler',
},
{
'name': 'host',
'default': 'localhost:8080',
'type': 'string',
'label': 'Host',
'description': 'Test',
},
{
'name': 'api_key',
'default': '',
'type': 'string',
'label': 'Api Key',
'description': 'Used for all calls to Sabnzbd.',
},

5
couchpotato/core/notifications/base.py

@ -1,7 +1,7 @@
from couchpotato.core.event import addEvent
from couchpotato.core.helpers.request import jsonified
from couchpotato.core.plugins.base import Plugin
from couchpotato.environment import Env
class Notification(Plugin):
@ -14,9 +14,6 @@ class Notification(Plugin):
def notify(self, message = '', data = {}):
pass
def conf(self, attr):
return Env.setting(attr, self.__class__.__name__.lower())
def isDisabled(self):
return not self.isEnabled()

6
couchpotato/core/notifications/growl/__init__.py

@ -12,20 +12,16 @@ config = [{
'options': [
{
'name': 'enabled',
'default': False,
'default': 0,
'type': 'enabler',
'description': '',
},
{
'name': 'host',
'default': 'localhost',
'description': '',
},
{
'name': 'password',
'default': '',
'type': 'password',
'description': '',
},
],
}

7
couchpotato/core/notifications/nmj/__init__.py

@ -13,23 +13,18 @@ config = [{
'options': [
{
'name': 'enabled',
'default': False,
'default': 0,
'type': 'enabler',
},
{
'name': 'host',
'default': 'localhost',
'description': '',
},
{
'name': 'database',
'default': '',
'description': '',
},
{
'name': 'mount',
'default': '',
'description': '',
},
],
}

5
couchpotato/core/notifications/notifo/__init__.py

@ -13,17 +13,14 @@ config = [{
'options': [
{
'name': 'enabled',
'default': False,
'default': 0,
'type': 'enabler',
},
{
'name': 'username',
'default': '',
'type': 'string',
},
{
'name': 'password',
'default': '',
'type': 'password',
},
],

6
couchpotato/core/notifications/plex/__init__.py

@ -12,20 +12,16 @@ config = [{
'options': [
{
'name': 'enabled',
'default': False,
'default': 0,
'type': 'enabler',
'description': '',
},
{
'name': 'host',
'default': 'localhost',
'description': '',
},
{
'name': 'password',
'default': '',
'type': 'password',
'description': '',
},
],
}

6
couchpotato/core/notifications/prowl/__init__.py

@ -12,21 +12,17 @@ config = [{
'options': [
{
'name': 'enabled',
'default': False,
'default': 0,
'type': 'enabler',
'description': '',
},
{
'name': 'api_key',
'default': '',
'label': 'Api key',
'description': '',
},
{
'name': 'priority',
'default': '0',
'type': 'dropdown',
'description': '',
'values': [('Very Low', -2), ('Moderate', -1), ('Normal', 0), ('High', 1), ('Emergency', 2)]
},
],

6
couchpotato/core/notifications/xbmc/__init__.py

@ -12,25 +12,21 @@ config = [{
'options': [
{
'name': 'enabled',
'default': False,
'default': 0,
'type': 'enabler',
'description': '',
},
{
'name': 'host',
'default': 'localhost:8080',
'description': '',
},
{
'name': 'username',
'default': 'xbmc',
'description': '',
},
{
'name': 'password',
'default': 'xbmc',
'type': 'password',
'description': '',
},
],
}

7
couchpotato/core/plugins/base.py

@ -1,4 +1,7 @@
from couchpotato.environment import Env
class Plugin():
def __init__(self):
pass
def conf(self, attr):
return Env.setting(attr, self.__class__.__name__.lower())

21
couchpotato/core/plugins/file_browser/main.py

@ -8,24 +8,22 @@ if os.name == 'nt':
class FileBrowser():
def __init__(self, path = '/'):
self.path = path
def __init__(self):
addApiView('directory.list', self.view)
def getDirectories(self):
def getDirectories(self, path = '/', show_hidden = True):
# Return driveletters or root if path is empty
if self.path == '/' or not self.path:
if path == '/' or not path:
if os.name == 'nt':
return self.getDriveLetters()
self.path = '/'
path = '/'
dirs = []
for f in os.listdir(self.path):
path = os.path.join(self.path, f)
if(os.path.isdir(path)):
dirs.append(path)
for f in os.listdir(path):
p = os.path.join(path, f)
if(os.path.isdir(p)):
dirs.append(p + '/')
return dirs
@ -44,8 +42,7 @@ class FileBrowser():
def view(self):
try:
fb = FileBrowser(getParam('path', '/'))
dirs = fb.getDirectories()
dirs = self.getDirectories(path = getParam('path', '/'), show_hidden = getParam('show_hidden', True))
except:
dirs = []

5
couchpotato/core/providers/base.py

@ -3,11 +3,8 @@ from couchpotato.core.plugins.base import Plugin
log = CPLog(__name__)
class Provider(Plugin):
type = None # movie, nzb, torrent, subtitle, trailer
timeout = 10 # Default timeout for url requests
def __init__(self):
pass

1
couchpotato/core/providers/imdb/main.py

@ -5,6 +5,7 @@ from imdb import IMDb
log = CPLog(__name__)
class IMDB(Provider):
type = 'movie'

7
couchpotato/core/providers/themoviedb/__init__.py

@ -1,7 +1,7 @@
from couchpotato.core.providers.tmdb.main import TMDBWrapper
from .main import TheMovieDb
def start():
return TMDBWrapper()
return TheMovieDb()
config = [{
'name': 'themoviedb',
@ -11,13 +11,12 @@ config = [{
'name': 'tmdb',
'label': 'TheMovieDB',
'advanced': True,
'description': 'Used for all calls to TheMovieDB.',
'options': [
{
'name': 'api_key',
'default': '9b939aee0aaafc12a65bf448e4af9543',
'type': 'string',
'label': 'Api Key',
'description': 'Used for all calls to TheMovieDB.',
},
],
},

8
couchpotato/core/providers/themoviedb/main.py

@ -3,13 +3,12 @@ from couchpotato.core.event import addEvent
from couchpotato.core.helpers.encoding import simplifyString, toUnicode
from couchpotato.core.logger import CPLog
from couchpotato.core.providers.base import Provider
from couchpotato.environment import Env
from libs.themoviedb import tmdb
import copy
log = CPLog(__name__)
class TMDBWrapper(Provider):
class TheMovieDb(Provider):
"""Api for theMovieDb"""
type = 'movie'
@ -23,9 +22,6 @@ class TMDBWrapper(Provider):
# Use base wrapper
tmdb.Config.api_key = self.conf('api_key')
def conf(self, attr):
return Env.setting(attr, 'themoviedb')
def search(self, q, limit = 12):
''' Find movie by name '''

10
couchpotato/core/settings/__init__.py

@ -108,13 +108,13 @@ class Settings():
def saveView(self):
a = getParams()
params = getParams()
section = a.get('section')
option = a.get('name')
value = a.get('value')
section = params.get('section')
option = params.get('name')
value = params.get('value')
self.set(option, section, value)
self.set(section, option, value)
self.save()
return jsonified({

23
couchpotato/static/scripts/block/search.js

@ -33,12 +33,6 @@ Block.Search = new Class({
self.spinner = new Spinner(self.result_container);
self.OuterClickStack = new EventStack.OuterClick();
History.addEvent('change', self.hideResults.bind(self, true));
//debug
//self.input.set('value', 'kick ass')
//self.autocomplete()
},
clear: function(e){
@ -59,8 +53,14 @@ Block.Search = new Class({
self.result_container.fade(bool ? 0 : 1)
if(!bool && self.OuterClickStack.stack.length == 0)
self.OuterClickStack.push(self.hideResults.bind(self, true), self.el);
if(bool){
History.removeEvent('change', self.hideResults.bind(self, !bool));
self.el.removeEvent('outerClick', self.hideResults.bind(self, !bool));
}
else {
History.addEvent('change', self.hideResults.bind(self, !bool));
self.el.addEvent('outerClick', self.hideResults.bind(self, !bool));
}
self.hidden = bool;
},
@ -161,8 +161,6 @@ Block.Search.Item = new Class({
self.alternative_titles = [];
self.create();
self.OuterClickStack = new EventStack.OuterClick();
},
create: function(){
@ -215,7 +213,7 @@ Block.Search.Item = new Class({
})
}
info.titles.each(function(title){
self.alternativeTitle({
'title': title
@ -239,7 +237,7 @@ Block.Search.Item = new Class({
self.data_container.tween('margin-left', 0, self.width);
self.OuterClickStack.push(self.closeOptions.bind(self), self.el);
self.el.addEvents('outerClick', self.closeOptions.bind(self))
},
@ -321,6 +319,7 @@ Block.Search.Item = new Class({
var self = this;
self.data_container.tween('margin-left', self.width, 0);
self.el.removeEvents('outerClick', self.closeOptions.bind(self))
},
toElement: function(){

39
couchpotato/static/scripts/couchpotato.js

@ -186,6 +186,45 @@ var p = function(){
console.log(arguments)
};
(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;
}
}
};
})();
function randomString(length, extra) {
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz" + (extra ? '-._!@#$%^&*()+=' : '');
var stringLength = length || 8;

71
couchpotato/static/scripts/library/eventstack.js

@ -1,71 +0,0 @@
/*
---
name: EventStack
description: Helps you Escape.
authors: Christoph Pojer (@cpojer)
license: MIT-style license.
requires: [Core/Class.Extras, Core/Element.Event, Class-Extras/Class.Binds]
provides: EventStack
...
*/
(function(){
this.EventStack = new Class({
Implements: [Options, Class.Binds],
options: {
event: 'keyup',
condition: function(event){
return (event.key == 'esc');
}
},
initialize: function(options){
this.setOptions(options);
this.stack = [];
this.data = [];
document.addEvent(this.options.event, this.bound('condition'));
},
condition: function(event){
if (this.options.condition.call(this, event, this.data.getLast()))
this.pop(event);
},
erase: function(fn){
this.data.erase(this.data[this.stack.indexOf(fn)]);
this.stack.erase(fn);
return this;
},
push: function(fn, data){
this.erase(fn);
this.data.push(data || null);
this.stack.push(fn);
return this;
},
pop: function(event){
var fn = this.stack.pop(),
data = this.data.pop();
if (fn) fn.call(this, event, data);
return this;
}
});
}).call(this);

30
couchpotato/static/scripts/library/eventstack_outerclick.js

@ -1,30 +0,0 @@
/*
---
name: EventStack.OuterClick
description: Helps you escape from clicks outside of a certain area.
authors: Christoph Pojer (@cpojer)
license: MIT-style license.
requires: [EventStack]
provides: EventStack.OuterClick
...
*/
EventStack.OuterClick = new Class({
Extends: EventStack,
options: {
event: 'click',
condition: function(event, element){
return element && !element.contains(event.target);
}
}
});

171
couchpotato/static/scripts/page/settings.js

@ -77,7 +77,7 @@ Page.Settings = new Class({
getValue: function(section, name){
var self = this;
try {
return self.data.values[section][name] || '';
return self.data.values[section][name];
}
catch(e){
return ''
@ -131,6 +131,7 @@ Page.Settings = new Class({
var class_name = (option.type || 'string').capitalize();
var input = new Option[class_name](self, section_name, option.name, option);
input.inject(group_el);
input.fireEvent('injected')
});
});
@ -218,6 +219,8 @@ var OptionBase = new Class({
'keyup': self.changed.bind(self)
});
self.addEvent('injected', self.afterInject.bind(self))
},
/**
@ -229,7 +232,7 @@ var OptionBase = new Class({
},
create: function(){},
createLabel: function(){
var self = this;
return new Element('label', {
@ -249,6 +252,8 @@ var OptionBase = new Class({
}).inject(self.el);
},
afterInject: function(){},
// Element has changed, do something
changed: function(){
var self = this;
@ -266,7 +271,7 @@ var OptionBase = new Class({
save: function(){
var self = this;
Api.request('setting.save', {
Api.request('settings.save', {
'data': {
'section': self.section,
'name': self.name,
@ -327,7 +332,7 @@ var Option = {}
Option.String = new Class({
Extends: OptionBase,
type: 'input',
type: 'string',
create: function(){
var self = this
@ -375,23 +380,35 @@ Option.Checkbox = new Class({
create: function(){
var self = this;
var randomId = 'option-'+Math.floor(Math.random()*1000000)
var randomId = 'r-'+randomString()
self.el.adopt(
self.createLabel().set('for', randomId),
self.input = new Element('input', {
'type': 'checkbox',
'value': self.getSettingValue(),
'checked': self.getSettingValue() !== undefined,
'checked': self.getSettingValue(),
'id': randomId
})
)
},
getValue: function(){
var self = this;
return +self.input.checked;
}
});
Option.Password = new Class({
Extends: Option.String,
type: 'password'
type: 'password',
create: function(){
var self = this;
self.parent()
self.input.set('type', 'password')
}
});
Option.Bool = new Class({
@ -399,7 +416,37 @@ Option.Bool = new Class({
});
Option.Enabler = new Class({
Extends: Option.Bool
Extends: Option.Bool,
create: function(){
var self = this;
self.el.adopt(
self.input = new Element('input', {
'type': 'checkbox',
'checked': self.getSettingValue(),
'id': 'r-'+randomString(),
'events': {
'change': self.checkState.bind(self)
}
})
)
},
checkState: function(){
var self = this;
self.parentFieldset[ self.getValue() ? 'addClass' : 'removeClass']('enabled');
},
afterInject: function(){
var self = this;
self.parentFieldset = self.el.getParent('fieldset')
self.el.inject(self.parentFieldset, 'top')
self.checkState()
}
});
Option.Int = new Class({
@ -413,18 +460,17 @@ Option.Directory = new Class({
type: 'span',
browser: '',
save_on_change: false,
show_hidden: false,
create: function(){
var self = this;
self.el.adopt(
self.createLabel(),
self.input = new Element('span', {
'text': self.getSettingValue(),
'events': {
'click': self.showBrowser.bind(self),
'outerClick': self.hideBrowser.bind(self)
'click': self.showBrowser.bind(self)
}
})
);
@ -432,47 +478,99 @@ Option.Directory = new Class({
self.cached = {};
},
selectDirectory: function(e, el){
var self = this;
self.input.set('text', el.get('data-value'));
self.getDirs()
self.fireEvent('change')
},
previousDirectory: function(e){
var self = this;
self.selectDirectory(null, self.back_button)
},
showBrowser: function(){
var self = this;
if(!self.browser)
self.browser = new Element('div.directory_list').adopt(
self.dir_list = new Element('ul')
self.back_button = new Element('a.button.back', {
'text': '',
'events': {
'click': self.previousDirectory.bind(self)
}
}),
self.dir_list = new Element('ul', {
'events': {
'click:relay(li)': self.selectDirectory.bind(self)
}
}),
new Element('div.actions').adopt(
new Element('a.button.cancel', {
'text': 'Cancel',
'events': {
'click': self.hideBrowser.bind(self)
}
}),
new Element('span', {
'text': 'or'
}),
self.save_button = new Element('a.button.save', {
'text': 'Save',
'events': {
'click': self.hideBrowser.bind(self, true)
}
})
)
).inject(self.input, 'after')
self.getDirs()
self.browser.show()
self.el.addEvent('outerClick', self.hideBrowser.bind(self))
},
hideBrowser: function(){
this.browser.hide()
hideBrowser: function(save){
var self = this;
if(save) self.save()
self.browser.hide()
self.el.removeEvent('outerClick', self.hideBrowser.bind(self))
},
fillBrowser: function(json){
var self = this;
var c = self.getCurrentDir();
var v = self.input.get('value');
var add = true
var c = self.getParentDir();
var v = self.input.get('text');
var previous_dir = self.getParentDir(c.substring(0, c.length-1));
if(!json){
json = self.cached[c];
if(previous_dir){
self.back_button.set('data-value', previous_dir)
self.back_button.set('text', self.getCurrentDirname(previous_dir))
self.back_button.show()
}
else {
self.cached[c] = json;
self.back_button.hide()
}
if(!json)
json = self.cached[c];
else
self.cached[c] = json;
self.dir_list.empty();
json.dirs.each(function(dir){
if(dir.indexOf(v) != -1){
new Element('li', {
'text': dir
'data-value': dir,
'text': self.getCurrentDirname(dir)
}).inject(self.dir_list)
if(add){
self.input.insertAtCursor(dir.substring(v.length), true);
add = false
}
}
})
},
@ -480,7 +578,7 @@ Option.Directory = new Class({
getDirs: function(){
var self = this;
var c = self.getCurrentDir();
var c = self.getParentDir();
if(self.cached[c]){
self.fillBrowser()
@ -488,22 +586,31 @@ Option.Directory = new Class({
else {
Api.request('directory.list', {
'data': {
'path': c
'path': c,
'show_hidden': +self.show_hidden
},
'onComplete': self.fillBrowser.bind(self)
})
}
},
getCurrentDir: function(){
getParentDir: function(dir){
var self = this;
var v = self.input.get('value');
var v = dir || self.input.get('text');
var sep = Api.getOption('path_sep');
var dirs = v.split(sep);
dirs.pop();
return dirs.join(sep)
return dirs.join(sep) + sep
},
getCurrentDirname: function(dir){
var self = this;
var dir_split = dir.split(Api.getOption('path_sep'));
return dir_split[dir_split.length-2] || '/'
},
getValue: function(){

12
couchpotato/static/scripts/page/wanted.js

@ -297,19 +297,19 @@ Movie.Action.Delete = new Class({
});
$(self.mask).adopt(
new Element('a.button.delete', {
'text': 'Delete movie',
new Element('a.button.cancel', {
'text': 'Cancel',
'events': {
'click': self.del.bind(self)
'click': self.mask.hide.bind(self.mask)
}
}),
new Element('span', {
'text': 'or'
}),
new Element('a.button.cancel', {
'text': 'Cancel',
new Element('a.button.delete', {
'text': 'Delete movie',
'events': {
'click': self.mask.hide.bind(self.mask)
'click': self.del.bind(self)
}
})
);

2
couchpotato/templates/_desktop.html

@ -15,8 +15,6 @@
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/library/uniform.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/couchpotato.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/library/history.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/library/eventstack.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/library/eventstack_outerclick.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/block.js') }}"></script>
<script type="text/javascript" src="{{ url_for('.static', filename='scripts/block/navigation.js') }}"></script>

Loading…
Cancel
Save