diff --git a/couchpotato/core/helpers/variable.py b/couchpotato/core/helpers/variable.py index 208709d1..dc92f6a0 100644 --- a/couchpotato/core/helpers/variable.py +++ b/couchpotato/core/helpers/variable.py @@ -4,7 +4,6 @@ import os.path def isDict(object): return isinstance(object, dict) - def mergeDicts(a, b): assert isDict(a), isDict(b) dst = a.copy() @@ -16,7 +15,7 @@ def mergeDicts(a, b): if key not in current_dst: current_dst[key] = current_src[key] else: - if isDict(current_src[key]) and isDict(current_dst[key]) : + if isDict(current_src[key]) and isDict(current_dst[key]): stack.append((current_dst[key], current_src[key])) else: current_dst[key] = current_src[key] diff --git a/couchpotato/core/plugins/__init__.py b/couchpotato/core/plugins/__init__.py index 84714c75..8b137891 100644 --- a/couchpotato/core/plugins/__init__.py +++ b/couchpotato/core/plugins/__init__.py @@ -1,75 +1 @@ -from uuid import uuid4 - -def start(): - pass - -config = [{ - 'name': 'core', - 'groups': [ - { - 'tab': 'general', - 'name': 'basics', - 'description': 'Needs restart before changes take effect.', - 'options': [ - { - 'name': 'username', - 'default': '', - }, - { - 'name': 'password', - 'default': '', - 'type': 'password', - }, - { - 'name': 'host', - 'advanced': True, - 'default': '0.0.0.0', - 'label': 'IP', - 'description': 'Host that I should listen to. "0.0.0.0" listens to all ips.', - }, - { - 'name': 'port', - 'default': 5000, - 'type': 'int', - 'description': 'The port I should listen to.', - }, - { - 'name': 'launch_browser', - 'default': 1, - 'type': 'bool', - 'label': 'Launch Browser', - 'description': 'Launch the browser when I start.', - }, - ], - }, - { - 'tab': 'general', - 'name': 'advanced', - 'description': "For those who know what the're doing", - 'advanced': True, - 'options': [ - { - 'name': 'api_key', - 'default': uuid4().hex, - 'readonly': 1, - 'label': 'Api Key', - 'description': "This is top-secret! Don't share this!", - }, - { - 'name': 'debug', - 'default': 0, - 'type': 'bool', - 'label': 'Debug', - 'description': 'Enable debugging.', - }, - { - 'name': 'url_base', - 'default': '', - 'label': 'Url Base', - 'description': 'When using mod_proxy use this to append the url with this.', - }, - ], - }, - ], -}] diff --git a/couchpotato/core/plugins/browser/main.py b/couchpotato/core/plugins/browser/main.py index d4829b40..d1d23730 100644 --- a/couchpotato/core/plugins/browser/main.py +++ b/couchpotato/core/plugins/browser/main.py @@ -1,6 +1,7 @@ from couchpotato.api import addApiView from couchpotato.core.helpers.request import getParam, jsonified from couchpotato.core.plugins.base import Plugin +import ctypes import os import string @@ -23,7 +24,7 @@ class FileBrowser(Plugin): dirs = [] for f in os.listdir(path): p = os.path.join(path, f) - if(os.path.isdir(p)): + if os.path.isdir(p) and ((self.is_hidden(p) and bool(int(show_hidden))) or not self.is_hidden(p)): dirs.append(p + '/') return dirs @@ -48,6 +49,21 @@ class FileBrowser(Plugin): dirs = [] return jsonified({ + 'is_root': getParam('path', '/') == '/', 'empty': len(dirs) == 0, 'dirs': dirs, }) + + + def is_hidden(self, filepath): + name = os.path.basename(os.path.abspath(filepath)) + return name.startswith('.') or self.has_hidden_attribute(filepath) + + def has_hidden_attribute(self, filepath): + try: + attrs = ctypes.windll.kernel32.GetFileAttributesW(unicode(filepath)) + assert attrs != -1 + result = bool(attrs & 2) + except (AttributeError, AssertionError): + result = False + return result diff --git a/couchpotato/core/plugins/library/main.py b/couchpotato/core/plugins/library/main.py index 77179b0a..d9876f1f 100644 --- a/couchpotato/core/plugins/library/main.py +++ b/couchpotato/core/plugins/library/main.py @@ -51,7 +51,9 @@ class LibraryPlugin(Plugin): library = db.query(Library).filter_by(identifier = identifier).first() done_status = fireEvent('status.get', 'done', single = True) - library_dict = library.to_dict(self.default_dict) + if library: + library_dict = library.to_dict(self.default_dict) + do_update = True if library.status_id == done_status.get('id') and not force: diff --git a/couchpotato/core/plugins/movie/main.py b/couchpotato/core/plugins/movie/main.py index c20048ad..2df4fa5e 100644 --- a/couchpotato/core/plugins/movie/main.py +++ b/couchpotato/core/plugins/movie/main.py @@ -10,6 +10,14 @@ from urllib import urlencode class MoviePlugin(Plugin): + default_dict = { + 'profile': {'types': {'quality': {}}}, + 'releases': {'status': {}, 'quality': {}, 'files':{}, 'info': {}}, + 'library': {'titles': {}, 'files':{}}, + 'files': {}, + 'status': {} + } + def __init__(self): addApiView('movie.search', self.search) addApiView('movie.list', self.list) @@ -30,12 +38,7 @@ class MoviePlugin(Plugin): movies = [] for movie in results: - temp = movie.to_dict(deep = { - 'releases': {'status': {}, 'quality': {}, 'files':{}, 'info': {}}, - 'library': {'titles': {}, 'files':{}}, - 'files': {} - }) - + temp = movie.to_dict(self.default_dict) movies.append(temp) return jsonified({ @@ -59,12 +62,7 @@ class MoviePlugin(Plugin): if movie: #addEvent('library.update.after', ) fireEventAsync('library.update', identifier = movie.library.identifier, default_title = default_title, force = True) - fireEventAsync('searcher.single', movie.to_dict(deep = { - 'profile': {'types': {'quality': {}}}, - 'releases': {'status': {}, 'quality': {}, 'files': {}, 'info': {}}, - 'library': {'titles': {}, 'files':{}}, - 'files': {} - })) + fireEventAsync('searcher.single', movie.to_dict(self.default_dict)) return jsonified({ 'success': True, @@ -119,10 +117,7 @@ class MoviePlugin(Plugin): m.status_id = status_active.get('id') db.commit() - movie_dict = m.to_dict(deep = { - 'releases': {'status': {}, 'quality': {}, 'files': {}, 'info': {}}, - 'library': {'titles': {}} - }) + movie_dict = m.to_dict(self.default_dict) return jsonified({ 'success': True, diff --git a/couchpotato/core/plugins/wizard/__init__.py b/couchpotato/core/plugins/wizard/__init__.py index 7ee4a45c..78876470 100644 --- a/couchpotato/core/plugins/wizard/__init__.py +++ b/couchpotato/core/plugins/wizard/__init__.py @@ -4,7 +4,7 @@ def start(): return Wizard() config = [{ - 'name': 'global', + 'name': 'core', 'groups': [ { 'tab': 'general', diff --git a/couchpotato/core/providers/torrent/thepiratebay/__init__.py b/couchpotato/core/providers/torrent/thepiratebay/__init__.py index 1e154ce0..810e713a 100644 --- a/couchpotato/core/providers/torrent/thepiratebay/__init__.py +++ b/couchpotato/core/providers/torrent/thepiratebay/__init__.py @@ -3,22 +3,4 @@ from .main import ThePirateBay def start(): return ThePirateBay() -config = [{ - 'name': 'themoviedb', - 'groups': [ - { - 'tab': 'providers', - 'name': 'tmdb', - 'label': 'TheMovieDB', - 'advanced': True, - 'description': 'Used for all calls to TheMovieDB.', - 'options': [ - { - 'name': 'api_key', - 'default': '9b939aee0aaafc12a65bf448e4af9543', - 'label': 'Api Key', - }, - ], - }, - ], -}] +config = [] diff --git a/couchpotato/core/settings/__init__.py b/couchpotato/core/settings/__init__.py index 1c462932..77813586 100644 --- a/couchpotato/core/settings/__init__.py +++ b/couchpotato/core/settings/__init__.py @@ -3,6 +3,7 @@ from couchpotato.api import addApiView from couchpotato.core.event import addEvent from couchpotato.core.helpers.encoding import isInt from couchpotato.core.helpers.request import getParams, jsonified +from couchpotato.core.helpers.variable import mergeDicts import ConfigParser import os.path import time @@ -93,7 +94,12 @@ class Settings(): self.p.set(section, option, value) def addOptions(self, section_name, options): - self.options[section_name] = options + + if not self.options.get(section_name): + self.options[section_name] = options + else: + options['groups'] = self.options[section_name].get('groups') + options.get('groups') + self.options[section_name] = mergeDicts(self.options[section_name], options) def getOptions(self): return self.options diff --git a/couchpotato/static/images/icon.folder.gif b/couchpotato/static/images/icon.folder.gif new file mode 100644 index 00000000..e19ce53a Binary files /dev/null and b/couchpotato/static/images/icon.folder.gif differ diff --git a/couchpotato/static/images/right.arrow.png b/couchpotato/static/images/right.arrow.png new file mode 100644 index 00000000..39677d05 Binary files /dev/null and b/couchpotato/static/images/right.arrow.png differ diff --git a/couchpotato/static/scripts/library/form_replacement/form_check.js b/couchpotato/static/scripts/library/form_replacement/form_check.js index 3b29819b..c2464009 100644 --- a/couchpotato/static/scripts/library/form_replacement/form_check.js +++ b/couchpotato/static/scripts/library/form_replacement/form_check.js @@ -114,6 +114,7 @@ Form.Check = new Class({ this.check(); } this.fireEvent('change', this); + this.input.fireEvent('change', this); return this; }, uncheck: function() { diff --git a/couchpotato/static/scripts/page/settings.js b/couchpotato/static/scripts/page/settings.js index 8a43d525..ef8040bc 100644 --- a/couchpotato/static/scripts/page/settings.js +++ b/couchpotato/static/scripts/page/settings.js @@ -103,7 +103,7 @@ Page.Settings = new Class({ ) ) ); - + new Form.Check(self.advanced_toggle, { 'onChange': self.showAdvanced.bind(self) }) @@ -479,63 +479,77 @@ Option.Directory = new Class({ type: 'span', browser: '', save_on_change: false, + use_cache: false, create: function(){ var self = this; self.el.adopt( self.createLabel(), - self.input = new Element('span.directory', { - 'text': self.getSettingValue(), + new Element('span.directory.inlay', { 'events': { 'click': self.showBrowser.bind(self) } - }) + }).adopt( + self.input = new Element('span', { + 'text': self.getSettingValue() + }) + ) ); self.cached = {}; }, - selectDirectory: function(e, el){ + selectDirectory: function(dir){ var self = this; - self.input.set('text', el.get('data-value')); + self.input.set('text', dir); self.getDirs() - self.fireEvent('change') }, previousDirectory: function(e){ var self = this; - self.selectDirectory(null, self.back_button) + self.selectDirectory(self.getParentDir()) }, showBrowser: function(){ var self = this; - if(!self.browser) + if(!self.browser){ self.browser = new Element('div.directory_list').adopt( + new Element('div.pointer'), new Element('div.actions').adopt( - self.back_button = new Element('a.button.back', { - 'text': '', + self.back_button = new Element('a.back', { + 'html': '', 'events': { 'click': self.previousDirectory.bind(self) } }), new Element('label', { - 'text': 'Show hidden files' + 'text': 'Hidden folders' }).adopt( - self.show_hidden = new Element('input[type=checkbox].inlay') + self.show_hidden = new Element('input[type=checkbox].inlay', { + 'events': { + 'change': self.getDirs.bind(self) + } + }) ) ), self.dir_list = new Element('ul', { 'events': { - 'click:relay(li)': self.selectDirectory.bind(self) + 'click:relay(li)': function(e, el){ + (e).stop(); + self.selectDirectory(el.get('data-value')) + }, + 'mousewheel': function(e){ + (e).stopPropagation(); + } } }), new Element('div.actions').adopt( - new Element('a.button.cancel', { + new Element('a.cancel', { 'text': 'Cancel', 'events': { 'click': self.hideBrowser.bind(self) @@ -547,21 +561,32 @@ Option.Directory = new Class({ self.save_button = new Element('a.button.save', { 'text': 'Save', 'events': { - 'click': self.hideBrowser.bind(self, true) + 'click': function(e){ + self.hideBrowser(e, true) + } } }) ) - ).inject(self.input, 'after') + ).inject(self.el) + + new Form.Check(self.show_hidden); + } + + self.initial_directory = self.input.get('text'); self.getDirs() self.browser.show() self.el.addEvent('outerClick', self.hideBrowser.bind(self)) }, - hideBrowser: function(save){ + hideBrowser: function(e, save){ var self = this; + (e).stop(); - if(save) self.save() + if(save) + self.save() + else + self.input.set('text', self.initial_directory); self.browser.hide() self.el.removeEvent('outerClick', self.hideBrowser.bind(self)) @@ -571,41 +596,43 @@ Option.Directory = new Class({ fillBrowser: function(json){ var self = this; - var c = self.getParentDir(); var v = self.input.get('text'); - var previous_dir = self.getParentDir(c.substring(0, c.length-1)); + var previous_dir = self.getParentDir(); - if(previous_dir){ + if(previous_dir != v){ self.back_button.set('data-value', previous_dir) - self.back_button.set('text', self.getCurrentDirname(previous_dir)) + self.back_button.set('html', '« '+self.getCurrentDirname(previous_dir)) self.back_button.show() } else { self.back_button.hide() } - if(!json) - json = self.cached[c]; - else - self.cached[c] = json; + if(self.use_cache) + if(!json) + json = self.cached[v]; + else + self.cached[v] = json; - self.dir_list.empty(); - json.dirs.each(function(dir){ - if(dir.indexOf(v) != -1){ - new Element('li', { - 'data-value': dir, - 'text': self.getCurrentDirname(dir) - }).inject(self.dir_list) - } - }) + setTimeout(function(){ + self.dir_list.empty(); + json.dirs.each(function(dir){ + if(dir.indexOf(v) != -1){ + new Element('li', { + 'data-value': dir, + 'text': self.getCurrentDirname(dir) + }).inject(self.dir_list) + } + }); + }, 50); }, getDirs: function(){ var self = this; - var c = self.getParentDir(); + var c = self.input.get('text'); - if(self.cached[c]){ + if(self.cached[c] && self.use_cache){ self.fillBrowser() } else { @@ -625,7 +652,8 @@ Option.Directory = new Class({ var v = dir || self.input.get('text'); var sep = Api.getOption('path_sep'); var dirs = v.split(sep); - dirs.pop(); + if(dirs.pop() == '') + dirs.pop(); return dirs.join(sep) + sep }, diff --git a/couchpotato/static/style/page/settings.css b/couchpotato/static/style/page/settings.css index 7d7ec344..6a1de689 100644 --- a/couchpotato/static/style/page/settings.css +++ b/couchpotato/static/style/page/settings.css @@ -1,7 +1,15 @@ -/* @override http://localhost:5000/static/style/page/settings.css */ +/* @override + http://localhost:5000/static/style/page/settings.css + http://192.168.1.20:5000/static/style/page/settings.css +*/ -.page.settings { - overflow: hidden; +.page.settings:after { + content: "."; + display: block; + clear: both; + visibility: hidden; + line-height: 0; + height: 0; } .page.settings .tabs { @@ -165,39 +173,104 @@ .page.settings .directory { display: inline-block; - padding: 0 4px; + padding: 0 4% 0 4px; font-size: 13px; - width: 29.7%; - } - .page.settings .directory_list { - position: absolute; - width: 300px; - margin: 0 0 0 16.5%; - background: #282d34; - border: 1px solid #1f242b; - position: absolute; - box-shadow: 0 1px 2px rgba(0,0,0,0.4); - -moz-box-shadow: 0 1px 2px rgba(0,0,0,0.4); - -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.4); - border-radius:3px; - -moz-border-radius: 3px; - -webkit-border-radius: 3px; + width: 26.3%; + background-image: url('../../images/icon.folder.gif'); + background-repeat: no-repeat; + background-position: 97% center; + overflow: hidden; } + .page.settings .directory > span { + height: 25px; + display: inline-block; + float: right; + text-align: right; + white-space: nowrap; + cursor: pointer; + } + + .page.settings .directory_list { + z-index: 2; + position: absolute; + width: 360px; + margin: -2px 0 20px 60px; + background: #5c697b; + border-radius: 3px; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + box-shadow: 0 0 50px rgba(0,0,0,0.55); + -moz-box-shadow: 0 0 50px rgba(0,0,0,0.55); + -webkit-box-shadow: 0 0 50px rgba(0,0,0,0.55); + } + + .page.settings .directory_list .pointer { + border-right: 10px solid transparent; + border-left: 10px solid transparent; + border-bottom: 10px solid #5c697b; + display: block; + position: absolute; + width: 0px; + margin: -9px 0 0 48.5%; + } .page.settings .directory_list ul { width: 100%; - max-height: 200px; + height: 200px; overflow: auto; } - .page.settings .directory_list li { - padding: 2px 10px; - } + .page.settings .directory_list li { + padding: 0 10px; + cursor: pointer; + margin: 0; + border-top: 1px solid rgba(255,255,255,0.1); + background: url('../../images/right.arrow.png') no-repeat 98% center; + } + .page.settings .directory_list li:last-child { + border-bottom: 1px solid rgba(255,255,255,0.1); + } + + .page.settings .directory_list li:hover { + background-color: #515c68; + } .page.settings .directory_list .actions { clear: both; padding: 10px; - background: #414953; } - .page.settings .directory_list .actions:first-child { border-bottom: 1px solid #1f242b; } - .page.settings .directory_list .actions:last-child { border-top: 1px solid #1f242b; } \ No newline at end of file + + .page.settings .directory_list .actions {} + + .page.settings .directory_list .actions label { + float: right; + width: auto; + padding: 5px 3px; + } + .page.settings .directory_list .actions .inlay { + margin: -2px 0 0 7px; + } + + .page.settings .directory_list .actions .back { + font-weight: bold; + width: 160px; + display: inline-block; + } + + .page.settings .directory_list .actions:last-child { + float: right; + } + + .page.settings .directory_list .actions:last-child > span { + padding: 0 5px; + text-shadow: none; + } + + .page.settings .directory_list .actions:last-child > .cancel { + font-weight: bold; + color: #ddd; + } + + .page.settings .directory_list .actions:last-child > .save { + background: #9dc156; + } \ No newline at end of file