diff --git a/couchpotato/core/_base/_core/main.py b/couchpotato/core/_base/_core/main.py index c91140f..d2b6e2d 100644 --- a/couchpotato/core/_base/_core/main.py +++ b/couchpotato/core/_base/_core/main.py @@ -79,7 +79,7 @@ class Core(Plugin): def shutdown(): self.initShutdown() - IOLoop.instance().add_callback(shutdown) + IOLoop.current().add_callback(shutdown) return 'shutdown' @@ -89,7 +89,7 @@ class Core(Plugin): def restart(): self.initShutdown(restart = True) - IOLoop.instance().add_callback(restart) + IOLoop.current().add_callback(restart) return 'restarting' @@ -128,7 +128,7 @@ class Core(Plugin): log.debug('Save to shutdown/restart') try: - IOLoop.instance().stop() + IOLoop.current().stop() except RuntimeError: pass except: diff --git a/couchpotato/core/_base/clientscript/main.py b/couchpotato/core/_base/clientscript/main.py index f2a30f6..9536463 100644 --- a/couchpotato/core/_base/clientscript/main.py +++ b/couchpotato/core/_base/clientscript/main.py @@ -1,10 +1,11 @@ from couchpotato.core.event import addEvent +from couchpotato.core.helpers.encoding import ss from couchpotato.core.helpers.variable import tryInt from couchpotato.core.logger import CPLog from couchpotato.core.plugins.base import Plugin from couchpotato.environment import Env -from minify.cssmin import cssmin from minify.jsmin import jsmin +import cssprefixer import os import traceback @@ -23,7 +24,6 @@ class ClientScript(Plugin): 'script': [ 'scripts/library/mootools.js', 'scripts/library/mootools_more.js', - 'scripts/library/prefix_free.js', 'scripts/library/uniform.js', 'scripts/library/form_replacement/form_check.js', 'scripts/library/form_replacement/form_radio.js', @@ -69,7 +69,8 @@ class ClientScript(Plugin): addEvent('clientscript.get_styles', self.getStyles) addEvent('clientscript.get_scripts', self.getScripts) - addEvent('app.load', self.minify) + if not Env.get('dev'): + addEvent('app.load', self.minify) self.addCore() @@ -108,8 +109,10 @@ class ClientScript(Plugin): if file_type == 'script': data = jsmin(f) else: - data = cssmin(f) + data = cssprefixer.process(f, debug = False, minify = True) data = data.replace('../images/', '../static/images/') + data = data.replace('../fonts/', '../static/fonts/') + data = data.replace('../../static/', '../static/') # Replace inside plugins raw.append({'file': file_path, 'date': int(os.path.getmtime(file_path)), 'data': data}) @@ -119,7 +122,7 @@ class ClientScript(Plugin): data += self.comment.get(file_type) % (r.get('file'), r.get('date')) data += r.get('data') + '\n\n' - self.createFile(out, data.strip()) + self.createFile(out, ss(data.strip())) if not self.minified.get(file_type): self.minified[file_type] = {} diff --git a/couchpotato/core/_base/updater/main.py b/couchpotato/core/_base/updater/main.py index 0703155..44ceebd 100644 --- a/couchpotato/core/_base/updater/main.py +++ b/couchpotato/core/_base/updater/main.py @@ -15,6 +15,7 @@ import tarfile import time import traceback import version +import zipfile log = CPLog(__name__) @@ -263,11 +264,11 @@ class SourceUpdater(BaseUpdater): def doUpdate(self): try: - url = 'https://github.com/%s/%s/tarball/%s' % (self.repo_user, self.repo_name, self.branch) - destination = os.path.join(Env.get('cache_dir'), self.update_version.get('hash') + '.tar.gz') - extracted_path = os.path.join(Env.get('cache_dir'), 'temp_updater') + download_data = fireEvent('cp.source_url', repo = self.repo_user, repo_name = self.repo_name, branch = self.branch, single = True) + destination = os.path.join(Env.get('cache_dir'), self.update_version.get('hash')) + '.' + download_data.get('type') - destination = fireEvent('file.download', url = url, dest = destination, single = True) + extracted_path = os.path.join(Env.get('cache_dir'), 'temp_updater') + destination = fireEvent('file.download', url = download_data.get('url'), dest = destination, single = True) # Cleanup leftover from last time if os.path.isdir(extracted_path): @@ -275,9 +276,14 @@ class SourceUpdater(BaseUpdater): self.makeDir(extracted_path) # Extract - tar = tarfile.open(destination) - tar.extractall(path = extracted_path) - tar.close() + if download_data.get('type') == 'zip': + zip = zipfile.ZipFile(destination) + zip.extractall(extracted_path) + else: + tar = tarfile.open(destination) + tar.extractall(path = extracted_path) + tar.close() + os.remove(destination) if self.replaceWith(os.path.join(extracted_path, os.listdir(extracted_path)[0])): diff --git a/couchpotato/core/_base/updater/static/updater.js b/couchpotato/core/_base/updater/static/updater.js index cc17be5..0577c78 100644 --- a/couchpotato/core/_base/updater/static/updater.js +++ b/couchpotato/core/_base/updater/static/updater.js @@ -5,7 +5,7 @@ var UpdaterBase = new Class({ initialize: function(){ var self = this; - App.addEvent('load', self.info.bind(self, 1000)) + App.addEvent('load', self.info.bind(self, 2000)) App.addEvent('unload', function(){ if(self.timer) clearTimeout(self.timer); @@ -84,7 +84,7 @@ var UpdaterBase = new Class({ 'click': self.doUpdate.bind(self) } }) - ).inject($(document.body).getElement('.header')) + ).inject(document.body) }, doUpdate: function(){ diff --git a/couchpotato/core/downloaders/nzbget/__init__.py b/couchpotato/core/downloaders/nzbget/__init__.py index 5349914..a17b2dc 100644 --- a/couchpotato/core/downloaders/nzbget/__init__.py +++ b/couchpotato/core/downloaders/nzbget/__init__.py @@ -25,6 +25,12 @@ config = [{ 'description': 'Hostname with port. Usually localhost:6789', }, { + 'name': 'username', + 'default': 'nzbget', + 'advanced': True, + 'description': 'Set a different username to connect. Default: nzbget', + }, + { 'name': 'password', 'type': 'password', 'description': 'Default NZBGet password is tegbzn6789', diff --git a/couchpotato/core/downloaders/nzbget/main.py b/couchpotato/core/downloaders/nzbget/main.py index 78e2b95..fc54f02 100644 --- a/couchpotato/core/downloaders/nzbget/main.py +++ b/couchpotato/core/downloaders/nzbget/main.py @@ -1,7 +1,7 @@ from base64 import standard_b64encode from couchpotato.core.downloaders.base import Downloader, StatusList from couchpotato.core.helpers.encoding import ss -from couchpotato.core.helpers.variable import tryInt +from couchpotato.core.helpers.variable import tryInt, md5 from couchpotato.core.logger import CPLog from datetime import timedelta import re @@ -16,7 +16,7 @@ class NZBGet(Downloader): type = ['nzb'] - url = 'http://nzbget:%(password)s@%(host)s/xmlrpc' + url = 'http://%(username)s:%(password)s@%(host)s/xmlrpc' def download(self, data = {}, movie = {}, filedata = None): @@ -26,7 +26,7 @@ class NZBGet(Downloader): log.info('Sending "%s" to NZBGet.', data.get('name')) - url = self.url % {'host': self.conf('host'), 'password': self.conf('password')} + url = self.url % {'host': self.conf('host'), 'username': self.conf('username'), 'password': self.conf('password')} nzb_name = ss('%s.nzb' % self.createNzbName(data, movie)) rpc = xmlrpclib.ServerProxy(url) @@ -52,7 +52,14 @@ class NZBGet(Downloader): if xml_response: log.info('NZB sent successfully to NZBGet') - return True + nzb_id = md5(data['url']) # about as unique as they come ;) + couchpotato_id = "couchpotato=" + nzb_id + groups = rpc.listgroups() + file_id = [item['LastID'] for item in groups if item['NZBFilename'] == nzb_name] + confirmed = rpc.editqueue("GroupSetParameter", 0, couchpotato_id, file_id) + if confirmed: + log.debug('couchpotato parameter set in nzbget download') + return self.downloadReturnId(nzb_id) else: log.error('NZBGet could not add %s to the queue.', nzb_name) return False @@ -61,7 +68,7 @@ class NZBGet(Downloader): log.debug('Checking NZBGet download status.') - url = self.url % {'host': self.conf('host'), 'password': self.conf('password')} + url = self.url % {'host': self.conf('host'), 'username': self.conf('username'), 'password': self.conf('password')} rpc = xmlrpclib.ServerProxy(url) try: @@ -93,15 +100,19 @@ class NZBGet(Downloader): for item in groups: log.debug('Found %s in NZBGet download queue', item['NZBFilename']) + try: + nzb_id = [param['Value'] for param in item['Parameters'] if param['Name'] == 'couchpotato'][0] + except: + nzb_id = item['NZBID'] statuses.append({ - 'id': item['NZBID'], + 'id': nzb_id, 'name': item['NZBFilename'], 'original_status': 'DOWNLOADING' if item['ActiveDownloads'] > 0 else 'QUEUED', # Seems to have no native API function for time left. This will return the time left after NZBGet started downloading this item 'timeleft': str(timedelta(seconds = item['RemainingSizeMB'] / status['DownloadRate'] * 2 ^ 20)) if item['ActiveDownloads'] > 0 and not (status['DownloadPaused'] or status['Download2Paused']) else -1, }) - for item in queue: + for item in queue: # 'Parameters' is not passed in rpc.postqueue log.debug('Found %s in NZBGet postprocessing queue', item['NZBFilename']) statuses.append({ 'id': item['NZBID'], @@ -112,8 +123,12 @@ class NZBGet(Downloader): for item in history: log.debug('Found %s in NZBGet history. ParStatus: %s, ScriptStatus: %s, Log: %s', (item['NZBFilename'] , item['ParStatus'], item['ScriptStatus'] , item['Log'])) + try: + nzb_id = [param['Value'] for param in item['Parameters'] if param['Name'] == 'couchpotato'][0] + except: + nzb_id = item['NZBID'] statuses.append({ - 'id': item['NZBID'], + 'id': nzb_id, 'name': item['NZBFilename'], 'status': 'completed' if item['ParStatus'] == 'SUCCESS' and item['ScriptStatus'] == 'SUCCESS' else 'failed', 'original_status': item['ParStatus'] + ', ' + item['ScriptStatus'], @@ -147,8 +162,11 @@ class NZBGet(Downloader): try: history = rpc.history() - if rpc.editqueue('HistoryDelete', 0, "", [tryInt(item['id'])]): - path = [hist['DestDir'] for hist in history if hist['NZBID'] == item['id']][0] + for hist in history: + if hist['Parameters'] and hist['Parameters']['couchpotato'] and hist['Parameters']['couchpotato'] == item['id']: + nzb_id = hist['ID'] + path = hist['DestDir'] + if rpc.editqueue('HistoryDelete', 0, "", [tryInt(nzb_id)]): shutil.rmtree(path, True) except: log.error('Failed deleting: %s', traceback.format_exc(0)) diff --git a/couchpotato/core/event.py b/couchpotato/core/event.py index 19e9754..2f872a6 100644 --- a/couchpotato/core/event.py +++ b/couchpotato/core/event.py @@ -16,10 +16,8 @@ def runHandler(name, handler, *args, **kwargs): def addEvent(name, handler, priority = 100): - if events.get(name): - e = events[name] - else: - e = events[name] = Event(name = name, threads = 10, exc_info = True, traceback = True, lock = threading.RLock()) + if not events.get(name): + events[name] = [] def createHandle(*args, **kwargs): @@ -35,7 +33,10 @@ def addEvent(name, handler, priority = 100): return h - e.handle(createHandle, priority = priority) + events[name].append({ + 'handler': createHandle, + 'priority': priority, + }) def removeEvent(name, handler): e = events[name] @@ -43,6 +44,12 @@ def removeEvent(name, handler): def fireEvent(name, *args, **kwargs): if not events.get(name): return + + e = Event(name = name, threads = 10, asynch = kwargs.get('async', False), exc_info = True, traceback = True, lock = threading.RLock()) + + for event in events[name]: + e.handle(event['handler'], priority = event['priority']) + #log.debug('Firing event %s', name) try: @@ -52,6 +59,7 @@ def fireEvent(name, *args, **kwargs): 'single': False, # Return single handler 'merge': False, # Merge items 'in_order': False, # Fire them in specific order, waits for the other to finish + 'async': False } # Do options @@ -62,13 +70,6 @@ def fireEvent(name, *args, **kwargs): options[x] = val except: pass - e = events[name] - - # Lock this event - e.lock.acquire() - - e.asynchronous = False - # Make sure only 1 event is fired at a time when order is wanted kwargs['event_order_lock'] = threading.RLock() if options['in_order'] or options['single'] else None kwargs['event_return_on_result'] = options['single'] @@ -76,9 +77,6 @@ def fireEvent(name, *args, **kwargs): # Fire result = e(*args, **kwargs) - # Release lock for this event - e.lock.release() - if options['single'] and not options['merge']: results = None @@ -104,10 +102,11 @@ def fireEvent(name, *args, **kwargs): # Merge if options['merge'] and len(results) > 0: - results.reverse() # Priority 1 is higher then 100 # Dict if isinstance(results[0], dict): + results.reverse() + merged = {} for result in results: merged = mergeDicts(merged, result, prepend_list = True) @@ -140,13 +139,8 @@ def fireEvent(name, *args, **kwargs): log.error('%s: %s', (name, traceback.format_exc())) def fireEventAsync(*args, **kwargs): - try: - my_thread = threading.Thread(target = fireEvent, args = args, kwargs = kwargs) - my_thread.setDaemon(True) - my_thread.start() - return True - except Exception, e: - log.error('%s: %s', (args[0], e)) + kwargs['async'] = True + fireEvent(*args, **kwargs) def errorHandler(error): etype, value, tb = error diff --git a/couchpotato/core/helpers/variable.py b/couchpotato/core/helpers/variable.py index ff1043b..25def9a 100644 --- a/couchpotato/core/helpers/variable.py +++ b/couchpotato/core/helpers/variable.py @@ -10,6 +10,20 @@ import sys log = CPLog(__name__) +def link(src, dst): + if os.name == 'nt': + import ctypes + if ctypes.windll.kernel32.CreateHardLinkW(unicode(dst), unicode(src), 0) == 0: raise ctypes.WinError() + else: + os.link(src, dst) + +def symlink(src, dst): + if os.name == 'nt': + import ctypes + if ctypes.windll.kernel32.CreateSymbolicLinkW(unicode(dst), unicode(src), 1 if os.path.isdir(src) else 0) in [0, 1280]: raise ctypes.WinError() + else: + os.symlink(src, dst) + def getUserDir(): try: import pwd diff --git a/couchpotato/core/notifications/base.py b/couchpotato/core/notifications/base.py index 9e80aea..8d1608a 100644 --- a/couchpotato/core/notifications/base.py +++ b/couchpotato/core/notifications/base.py @@ -18,6 +18,7 @@ class Notification(Provider): listen_to = [ 'renamer.after', 'movie.snatched', 'updater.available', 'updater.updated', + 'core.message', ] dont_listen_to = [] diff --git a/couchpotato/core/notifications/core/main.py b/couchpotato/core/notifications/core/main.py index bc8bd86..a28ccb9 100644 --- a/couchpotato/core/notifications/core/main.py +++ b/couchpotato/core/notifications/core/main.py @@ -1,12 +1,13 @@ from couchpotato import get_session from couchpotato.api import addApiView, addNonBlockApiView -from couchpotato.core.event import addEvent +from couchpotato.core.event import addEvent, fireEvent from couchpotato.core.helpers.encoding import toUnicode from couchpotato.core.helpers.request import jsonified, getParam from couchpotato.core.helpers.variable import tryInt, splitString from couchpotato.core.logger import CPLog from couchpotato.core.notifications.base import Notification from couchpotato.core.settings.model import Notification as Notif +from couchpotato.environment import Env from sqlalchemy.sql.expression import or_ import threading import time @@ -21,11 +22,6 @@ class CoreNotifier(Notification): messages = [] listeners = [] - listen_to = [ - 'renamer.after', 'movie.snatched', - 'updater.available', 'updater.updated', - ] - def __init__(self): super(CoreNotifier, self).__init__() @@ -54,7 +50,10 @@ class CoreNotifier(Notification): addNonBlockApiView('notification.listener', (self.addListener, self.removeListener)) addApiView('notification.listener', self.listener) + fireEvent('schedule.interval', 'core.check_messages', self.checkMessages, hours = 12, single = True) + addEvent('app.load', self.clean) + addEvent('app.load', self.checkMessages) def clean(self): @@ -112,6 +111,23 @@ class CoreNotifier(Notification): 'notifications': notifications }) + def checkMessages(self): + + prop_name = 'messages.last_check' + last_check = tryInt(Env.prop(prop_name, default = 0)) + + messages = fireEvent('cp.messages', last_check = last_check, single = True) + + last_time = 0 + for message in messages: + if message.get('time') > last_check: + fireEvent('core.message', message = message.get('message'), data = message) + + if last_time < message.get('time'): + last_time = message.get('time') + + Env.prop(prop_name, value = last_time) + def notify(self, message = '', data = {}, listener = None): db = get_session() diff --git a/couchpotato/core/notifications/core/static/notification.js b/couchpotato/core/notifications/core/static/notification.js index 52062e9..243d561 100644 --- a/couchpotato/core/notifications/core/static/notification.js +++ b/couchpotato/core/notifications/core/static/notification.js @@ -21,20 +21,17 @@ var NotificationBase = new Class({ App.addEvent('load', function(){ App.block.notification = new Block.Menu(self, { + 'button_class': 'icon2.eye-open', 'class': 'notification_menu', 'onOpen': self.markAsRead.bind(self) }) $(App.block.notification).inject(App.getBlock('search'), 'after'); self.badge = new Element('div.badge').inject(App.block.notification, 'top').hide(); - /* App.getBlock('notification').addLink(new Element('a.more', { - 'href': App.createUrl('notifications'), - 'text': 'Show older notifications' - })); */ }); window.addEvent('load', function(){ - self.startInterval.delay(Browser.safari ? 100 : 0, self) + self.startInterval.delay(2000, self) }); }, @@ -47,14 +44,19 @@ var NotificationBase = new Class({ result.el = App.getBlock('notification').addLink( new Element('span.'+(result.read ? 'read' : '' )).adopt( - new Element('span.message', {'text': result.message}), + new Element('span.message', {'html': result.message}), new Element('span.added', {'text': added.timeDiffInWords(), 'title': added}) ) , 'top'); self.notifications.include(result); - if(!result.read) + if(result.data.important !== undefined && !result.read){ + var sticky = true + App.fireEvent('message', [result.message, sticky, result]) + } + else if(!result.read){ self.setBadge(self.notifications.filter(function(n){ return !n.read}).length) + } }, @@ -64,20 +66,26 @@ var NotificationBase = new Class({ self.badge[value ? 'show' : 'hide']() }, - markAsRead: function(){ - var self = this; + markAsRead: function(force_ids){ + var self = this, + ids = force_ids; - var rn = self.notifications.filter(function(n){ - return !n.read - }) + if(!force_ids) { + var rn = self.notifications.filter(function(n){ + return !n.read && n.data.important === undefined + }) - var ids = [] - rn.each(function(n){ - ids.include(n.id) - }) + var ids = [] + rn.each(function(n){ + ids.include(n.id) + }) + } if(ids.length > 0) Api.request('notification.markread', { + 'data': { + 'ids': ids.join(',') + }, 'onSuccess': function(){ self.setBadge('') } @@ -143,26 +151,41 @@ var NotificationBase = new Class({ self.startPoll() }, - showMessage: function(message){ + showMessage: function(message, sticky, data){ var self = this; if(!self.message_container) self.message_container = new Element('div.messages').inject(document.body); - var new_message = new Element('div.message', { - 'text': message - }).inject(self.message_container); + var new_message = new Element('div', { + 'class': 'message' + (sticky ? ' sticky' : ''), + 'html': message + }).inject(self.message_container, 'top'); setTimeout(function(){ new_message.addClass('show') }, 10); - setTimeout(function(){ + var hide_message = function(){ new_message.addClass('hide') setTimeout(function(){ new_message.destroy(); }, 1000); - }, 4000); + } + + if(sticky) + new_message.grab( + new Element('a.close.icon2', { + 'events': { + 'click': function(){ + self.markAsRead([data.id]); + hide_message(); + } + } + }) + ); + else + setTimeout(hide_message, 4000); }, diff --git a/couchpotato/core/plugins/file/main.py b/couchpotato/core/plugins/file/main.py index 87898f5..5239f1e 100644 --- a/couchpotato/core/plugins/file/main.py +++ b/couchpotato/core/plugins/file/main.py @@ -73,7 +73,7 @@ class FileManager(Plugin): db = get_session() for root, dirs, walk_files in os.walk(Env.get('cache_dir')): for filename in walk_files: - if root == python_cache or 'minified' in filename: continue + if root == python_cache or 'minified' in filename or 'version' in filename: continue file_path = os.path.join(root, filename) f = db.query(File).filter(File.path == toUnicode(file_path)).first() if not f: diff --git a/couchpotato/core/plugins/log/static/log.css b/couchpotato/core/plugins/log/static/log.css index 222b8ef..524b588 100644 --- a/couchpotato/core/plugins/log/static/log.css +++ b/couchpotato/core/plugins/log/static/log.css @@ -1,17 +1,18 @@ .page.log .nav { display: block; text-align: center; - padding: 20px 0; + padding: 0 0 30px; margin: 0; font-size: 20px; position: fixed; - width: 960px; + width: 100%; bottom: 0; + left: 0; background: #4E5969; } .page.log .nav li { - display: inline; + display: inline-block; padding: 5px 10px; margin: 0; cursor: pointer; @@ -24,7 +25,17 @@ .page.log .nav li.active { font-weight: bold; cursor: default; - font-size: 30px; + background: rgba(255,255,255,.1); + } + + @media all and (max-width: 480px) { + .page.log .nav { + font-size: 14px; + } + + .page.log .nav li { + padding: 5px; + } } .page.log .loading { diff --git a/couchpotato/core/plugins/manage/main.py b/couchpotato/core/plugins/manage/main.py index 5109489..3e791de 100644 --- a/couchpotato/core/plugins/manage/main.py +++ b/couchpotato/core/plugins/manage/main.py @@ -185,6 +185,8 @@ class Manage(Plugin): # Add it to release and update the info fireEvent('release.add', group = group) fireEventAsync('library.update', identifier = identifier, on_complete = self.createAfterUpdate(folder, identifier)) + else: + self.in_progress[folder]['to_go'] = self.in_progress[folder]['to_go'] - 1 return addToLibrary diff --git a/couchpotato/core/plugins/movie/main.py b/couchpotato/core/plugins/movie/main.py index 0a99fe6..4b595dc 100644 --- a/couchpotato/core/plugins/movie/main.py +++ b/couchpotato/core/plugins/movie/main.py @@ -108,9 +108,8 @@ class MoviePlugin(Plugin): now = time.time() week = 262080 - done_status = fireEvent('status.get', 'done', single = True) - available_status = fireEvent('status.get', 'available', single = True) - snatched_status = fireEvent('status.get', 'snatched', single = True) + done_status, available_status, snatched_status = \ + fireEvent('status.get', ['done', 'available', 'snatched'], single = True) db = get_session() @@ -367,11 +366,8 @@ class MoviePlugin(Plugin): library = fireEvent('library.add', single = True, attrs = params, update_after = update_library) # Status - status_active = fireEvent('status.add', 'active', single = True) - snatched_status = fireEvent('status.add', 'snatched', single = True) - ignored_status = fireEvent('status.add', 'ignored', single = True) - done_status = fireEvent('status.add', 'done', single = True) - downloaded_status = fireEvent('status.add', 'downloaded', single = True) + status_active, snatched_status, ignored_status, done_status, downloaded_status = \ + fireEvent('status.get', ['active', 'snatched', 'ignored', 'done', 'downloaded'], single = True) default_profile = fireEvent('profile.default', single = True) @@ -549,8 +545,7 @@ class MoviePlugin(Plugin): def restatus(self, movie_id): - active_status = fireEvent('status.get', 'active', single = True) - done_status = fireEvent('status.get', 'done', single = True) + active_status, done_status = fireEvent('status.get', ['active', 'done'], single = True) db = get_session() diff --git a/couchpotato/core/plugins/movie/static/list.js b/couchpotato/core/plugins/movie/static/list.js index a89f89a..6f57701 100644 --- a/couchpotato/core/plugins/movie/static/list.js +++ b/couchpotato/core/plugins/movie/static/list.js @@ -1,6 +1,6 @@ var MovieList = new Class({ - Implements: [Options], + Implements: [Events, Options], options: { navigation: true, @@ -8,7 +8,8 @@ var MovieList = new Class({ load_more: true, loader: true, menu: [], - add_new: false + add_new: false, + force_view: false }, movies: [], @@ -43,8 +44,11 @@ var MovieList = new Class({ }) : null ); - self.changeView(self.getSavedView() || self.options.view || 'details'); - + if($(window).getSize().x <= 480 && !self.options.force_view) + self.changeView('list'); + else + self.changeView(self.getSavedView() || self.options.view || 'details'); + self.getMovies(); App.addEvent('movie.added', self.movieAdded.bind(self)) @@ -121,7 +125,7 @@ var MovieList = new Class({ if(!self.navigation_counter) return; - self.navigation_counter.set('text', (count || 0)); + self.navigation_counter.set('text', (count || 0) + ' movies'); }, @@ -145,65 +149,67 @@ var MovieList = new Class({ var self = this; var chars = '#ABCDEFGHIJKLMNOPQRSTUVWXYZ'; - self.current_view = self.getSavedView() || 'details'; - self.el.addClass(self.current_view+'_list') + self.el.addClass('with_navigation') - self.navigation = new Element('div.alph_nav').adopt( - self.navigation_actions = new Element('ul.inlay.actions.reversed'), - self.navigation_counter = new Element('span.counter[title=Total]'), - self.navigation_alpha = new Element('ul.numbers', { - 'events': { - 'click:relay(li)': function(e, el){ - self.movie_list.empty() - self.activateLetter(el.get('data-letter')) - self.getMovies() - } - } - }), - self.navigation_search_input = new Element('input.inlay', { - 'placeholder': 'Search', - 'events': { - 'keyup': self.search.bind(self), - 'change': self.search.bind(self) - } - }), - self.navigation_menu = new Block.Menu(self), - self.mass_edit_form = new Element('div.mass_edit_form').adopt( - new Element('span.select').adopt( - self.mass_edit_select = new Element('input[type=checkbox].inlay', { - 'events': { - 'change': self.massEditToggleAll.bind(self) - } - }), - self.mass_edit_selected = new Element('span.count', {'text': 0}), - self.mass_edit_selected_label = new Element('span', {'text': 'selected'}) - ), - new Element('div.quality').adopt( - self.mass_edit_quality = new Element('select'), - new Element('a.button.orange', { - 'text': 'Change quality', - 'events': { - 'click': self.changeQualitySelected.bind(self) - } - }) - ), - new Element('div.delete').adopt( - new Element('span[text=or]'), - new Element('a.button.red', { - 'text': 'Delete', - 'events': { - 'click': self.deleteSelected.bind(self) - } - }) - ), - new Element('div.refresh').adopt( - new Element('span[text=or]'), - new Element('a.button.green', { - 'text': 'Refresh', - 'events': { - 'click': self.refreshSelected.bind(self) + self.navigation = new Element('div.alph_nav').grab( + new Element('div').adopt( + self.navigation_alpha = new Element('ul.numbers', { + 'events': { + 'click:relay(li)': function(e, el){ + self.movie_list.empty() + self.activateLetter(el.get('data-letter')) + self.getMovies() } - }) + } + }), + self.navigation_counter = new Element('span.counter[title=Total]'), + self.navigation_actions = new Element('ul.inlay.actions.reversed'), + self.navigation_search_input = new Element('input.search.inlay', { + 'title': 'Search through ' + self.options.identifier, + 'placeholder': 'Search through ' + self.options.identifier, + 'events': { + 'keyup': self.search.bind(self), + 'change': self.search.bind(self) + } + }), + self.navigation_menu = new Block.Menu(self), + self.mass_edit_form = new Element('div.mass_edit_form').adopt( + new Element('span.select').adopt( + self.mass_edit_select = new Element('input[type=checkbox].inlay', { + 'events': { + 'change': self.massEditToggleAll.bind(self) + } + }), + self.mass_edit_selected = new Element('span.count', {'text': 0}), + self.mass_edit_selected_label = new Element('span', {'text': 'selected'}) + ), + new Element('div.quality').adopt( + self.mass_edit_quality = new Element('select'), + new Element('a.button.orange', { + 'text': 'Change quality', + 'events': { + 'click': self.changeQualitySelected.bind(self) + } + }) + ), + new Element('div.delete').adopt( + new Element('span[text=or]'), + new Element('a.button.red', { + 'text': 'Delete', + 'events': { + 'click': self.deleteSelected.bind(self) + } + }) + ), + new Element('div.refresh').adopt( + new Element('span[text=or]'), + new Element('a.button.green', { + 'text': 'Refresh', + 'events': { + 'click': self.refreshSelected.bind(self) + } + }) + ) ) ) ).inject(self.el, 'top'); @@ -248,18 +254,19 @@ var MovieList = new Class({ }); // Get available chars and highlight - Api.request('movie.available_chars', { - 'data': Object.merge({ - 'status': self.options.status - }, self.filter), - 'onSuccess': function(json){ - - json.chars.split('').each(function(c){ - self.letters[c.capitalize()].addClass('available') - }) - - } - }); + if(self.navigation.isDisplayed() || self.navigation.isVisible()) + Api.request('movie.available_chars', { + 'data': Object.merge({ + 'status': self.options.status + }, self.filter), + 'onSuccess': function(json){ + + json.chars.split('').each(function(c){ + self.letters[c.capitalize()].addClass('available') + }) + + } + }); // Add menu or hide if (self.options.menu.length > 0) @@ -267,17 +274,7 @@ var MovieList = new Class({ self.navigation_menu.addLink(menu_item); }) else - self.navigation_menu.hide() - - self.nav_scrollspy = new ScrollSpy({ - min: 10, - onEnter: function(){ - self.navigation.addClass('float') - }, - onLeave: function(){ - self.navigation.removeClass('float') - } - }); + self.navigation_menu.hide(); }, @@ -517,6 +514,7 @@ var MovieList = new Class({ } self.checkIfEmpty(); + self.fireEvent('loaded'); } }); }, @@ -543,7 +541,7 @@ var MovieList = new Class({ self.title[is_empty ? 'hide' : 'show']() if(self.description) - self.description[is_empty ? 'hide' : 'show']() + self.description.setStyle('display', [is_empty ? 'none' : '']) if(is_empty && self.options.on_empty_element){ self.options.on_empty_element.inject(self.loader_first || self.title || self.movie_list, 'after'); diff --git a/couchpotato/core/plugins/movie/static/movie.css b/couchpotato/core/plugins/movie/static/movie.css index c6fe4e0..66c0ef0 100644 --- a/couchpotato/core/plugins/movie/static/movie.css +++ b/couchpotato/core/plugins/movie/static/movie.css @@ -1,50 +1,73 @@ .movies { - padding: 60px 0 20px; + padding: 10px 0 20px; position: relative; z-index: 3; + width: 100%; } - .movies .loading { - display: block; - padding: 20px 0 0 0; - width: 100%; - z-index: 3; - transition: all .4s cubic-bezier(0.9,0,0.1,1); - height: 40px; - opacity: 1; - position: absolute; + .movies > div { + clear: both; + } + + .movies.thumbs_list > div:not(.description) { + margin-right: -4px; text-align: center; } - .movies .loading.hide { - height: 0; + + .movies .loading { + display: block; padding: 20px 0 0 0; - opacity: 0; - margin-top: -20px; - } - - .movies .loading .spinner { - display: inline-block; - } - - .movies .loading .message { - margin: 0 20px; + width: 100%; + z-index: 3; + transition: all .4s cubic-bezier(0.9,0,0.1,1); + height: 40px; + opacity: 1; + position: absolute; + text-align: center; } + .movies .loading.hide { + height: 0; + padding: 0; + opacity: 0; + margin-top: -20px; + overflow: hidden; + } + + .movies .loading .spinner { + display: inline-block; + } + + .movies .loading .message { + margin: 0 20px; + } - .movies > h2 { + .movies h2 { margin-bottom: 20px; } + @media all and (max-width: 480px) { + .movies h2 { + font-size: 25px; + margin-bottom: 10px; + } + } + .movies > .description { position: absolute; top: 30px; right: 0; font-style: italic; - text-shadow: none; opacity: 0.8; } .movies:hover > .description { opacity: 1; } + + @media all and (max-width: 860px) { + .movies > .description { + display: none; + } + } .movies.thumbs_list { padding: 20px 0 20px; @@ -53,21 +76,21 @@ .home .movies { padding-top: 6px; } - - - .movies.mass_edit_list { - padding-top: 90px; - } .movies .movie { position: relative; border-radius: 4px; margin: 10px 0; + padding-left: 20px; overflow: hidden; width: 100%; height: 180px; transition: all 0.6s cubic-bezier(0.9,0,0.1,1); } + + .movies.details_list .movie { + padding-left: 120px; + } .movies.list_list .movie:not(.details_view), .movies.mass_edit_list .movie { @@ -75,13 +98,21 @@ } .movies.thumbs_list .movie { - width: 153px; - height: 230px; + width: 16.66667%; + height: auto; display: inline-block; - margin: 0 8px 0 0; + margin: 0; + padding: 0; + vertical-align: top; + border-radius: 0; + box-shadow: none; + border: 0; } - .movies.thumbs_list .movie:nth-child(6n+6) { - margin: 0; + + @media all and (max-width: 800px) { + .movies.thumbs_list .movie { + width: 25%; + } } .movies .movie .mask { @@ -109,8 +140,8 @@ .movies .data { padding: 20px; height: 100%; - width: 840px; - position: absolute; + width: 100%; + position: relative; right: 0; border-radius: 0; transition: all .6s cubic-bezier(0.9,0,0.1,1); @@ -119,14 +150,15 @@ .movies.mass_edit_list .movie .data { height: 30px; padding: 3px 0 3px 10px; - width: 938px; box-shadow: none; border: 0; background: #4e5969; } .movies.thumbs_list .data { + position: absolute; left: 0; + top: 0; width: 100%; padding: 10px; height: 100%; @@ -171,8 +203,11 @@ height: 100%; border-radius: 4px 0 0 4px; transition: all .6s cubic-bezier(0.9,0,0.1,1); - } + .movies.thumbs_list .poster { + position: relative; + border-radius: 0; + } .movies.list_list .movie:not(.details_view) .poster, .movies.mass_edit_list .poster { width: 20px; @@ -186,38 +221,72 @@ .movies.thumbs_list .poster { width: 100%; height: 100%; + transition: none; } .movies .poster img, .options .poster img { - width: 101%; - height: 101%; + width: 100%; + height: 100%; } + .movies.thumbs_list .poster img { + height: auto; + width: 100%; + top: 0; + bottom: 0; + border-radius: 0; + } .movies .info { position: relative; height: 100%; + width: 100%; } .movies .info .title { - display: inline; - position: absolute; font-size: 28px; font-weight: bold; margin-bottom: 10px; + margin-top: 2px; left: 0; top: 0; - width: 90%; + width: 100%; + padding-right: 80px; transition: all 0.2s linear; } + .movies .info .title span { + display: block; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + width: 100%; + height: 30px; + line-height: 30px; + top: -5px; + position: relative; + } + + .movies.thumbs_list .info .title span { + white-space: normal; + overflow: auto; + height: auto; + text-align: left; + } + + @media all and (max-width: 480px) { + .movies.thumbs_list .movie .info .title span, + .movies.thumbs_list .movie .info .year { + font-size: 15px; + line-height: 15px; + overflow: hidden; + } + } + .movies.list_list .movie:not(.details_view) .info .title, .movies.mass_edit_list .info .title { font-size: 16px; font-weight: normal; - text-overflow: ellipsis; width: auto; - overflow: hidden; - } .movies.thumbs_list .movie:not(.no_thumbnail) .info { @@ -229,25 +298,22 @@ .movies.thumbs_list .info .title { font-size: 21px; - text-shadow: 0 0 10px #000; word-wrap: break-word; + padding: 0; } .movies .info .year { position: absolute; - font-size: 30px; - margin-bottom: 10px; color: #bbb; - width: 10%; right: 0; - top: 0; + top: 1px; text-align: right; transition: all 0.2s linear; + font-weight: normal; } .movies.list_list .movie:not(.details_view) .info .year, .movies.mass_edit_list .info .year { - font-size: 16px; - width: 6%; + font-size: 1.25em; right: 10px; } @@ -259,16 +325,14 @@ top: auto; right: auto; color: #FFF; - text-shadow: none; - text-shadow: 0 0 6px #000; } .movies .info .description { - position: absolute; top: 30px; clear: both; - height: 80px; + bottom: 30px; overflow: hidden; + position: absolute; } .movies .data:hover .description { overflow: auto; @@ -281,12 +345,17 @@ .movies .data .quality { position: absolute; - bottom: 0; + bottom: 2px; display: block; min-height: 20px; - vertical-align: mid; } + @media all and (max-width: 480px) { + .movies .data .quality { + display: none; + } + } + .movies .status_suggest .data .quality, .movies.thumbs_list .data .quality { display: none; @@ -302,9 +371,8 @@ vertical-align: middle; display: inline-block; text-transform: uppercase; - text-shadow: none; font-weight: normal; - margin: 0 2px; + margin: 0 4px 0 0; border-radius: 2px; background-color: rgba(255,255,255,0.1); } @@ -312,7 +380,7 @@ .movies.mass_edit_list .data .quality { text-align: right; right: 0; - margin-right: 50px; + margin-right: 60px; z-index: 1; } @@ -342,18 +410,40 @@ bottom: 20px; right: 20px; line-height: 0; - margin-top: -25px; + top: 0; + opacity: 0; + display: none; + width: 0; } + @media all and (max-width: 480px) { + .movies .data .actions { + display: none !important; + } + } + + .movies .movie:hover .data .actions { + opacity: 1; + display: inline-block; + width: auto; + } + + .movies.details_list .data .actions { + top: auto; + bottom: 18px; + } + + .movies .movie:hover .actions { + opacity: 1; + display: inline-block; + } .movies.thumbs_list .data .actions { - bottom: 8px; + bottom: 2px; right: 10px; + top: auto; } .movies .movie:hover .action { opacity: 0.6; } .movies .movie:hover .action:hover { opacity: 1; } - .movies.mass_edit_list .data .actions { - display: none; - } .movies .data .action { background-repeat: no-repeat; @@ -362,7 +452,10 @@ width: 26px; height: 26px; padding: 3px; - opacity: 0; + } + + .movies.mass_edit_list .movie .data .actions { + display: none; } .movies.list_list .movie:not(.details_view):hover .actions, @@ -381,7 +474,8 @@ font-size: 20px; position: absolute; padding: 70px 0 0; - width: 100%; + left: 120px; + right: 0; } .movies .delete_container .cancel { } @@ -399,8 +493,8 @@ .movies .options { position: absolute; - margin-left: 120px; - width: 840px; + right: 0; + left: 120px; } .movies .options .form { @@ -423,7 +517,6 @@ .movies .options .table .item.ignored span { text-decoration: line-through; color: rgba(255,255,255,0.4); - text-shadow: none; } .movies .options .table .item.ignored .delete { background-image: url('../images/icon.undo.png'); @@ -457,7 +550,7 @@ overflow: hidden; } .movies .options .table .name { - width: 350px; + width: 340px; overflow: hidden; text-align: left; padding: 0 10px; @@ -491,6 +584,9 @@ text-align: center; transition: all .6s cubic-bezier(0.9,0,0.1,1); overflow: hidden; + left: 0; + position: absolute; + z-index: 10; } .movies .movie .trailer_container.hide { height: 0 !important; @@ -507,6 +603,7 @@ background: #4e5969; border-radius: 0 0 2px 2px; transition: all .2s cubic-bezier(0.9,0,0.1,1) .2s; + z-index: 11; } .movies .movie .hide_trailer.hide { top: -30px; @@ -543,11 +640,20 @@ right: 135px; z-index: 2; opacity: 0; - text-shadow: none; background: #4e5969; min-width: 300px; text-align: right; + height: 100%; + padding: 3px 0; + top: 0; } + + @media all and (max-width: 480px) { + .movies .movie .trynext { + display: none; + } + } + .movies.mass_edit_list .trynext { display: none; } .wanted .movies .movie .trynext { padding-right: 50px; } @@ -555,6 +661,14 @@ opacity: 1; } + .movies.details_list .movie .trynext { + background: #47515f; + padding: 0; + right: 0; + bottom: 35px; + height: auto; + } + .movies .movie .trynext a { background-position: 5px center; padding: 0 5px 0 25px; @@ -562,6 +676,9 @@ color: #FFF; border-radius: 2px; } + .movies .movie .trynext a:last-child { + margin: 0; + } .movies .movie .trynext a:hover { background-color: #369545; } @@ -578,24 +695,32 @@ .movies .alph_nav { transition: box-shadow .4s linear; - position: fixed; + position: relative; z-index: 4; - top: 0; - padding: 100px 60px 7px; - width: 1080px; - margin: 0 -60px; - box-shadow: 0 20px 20px -22px rgba(0,0,0,0.1); - background: #4e5969; + top: 0px; + right: 0; + margin: 0 auto; + width: 100%; + padding: 10px 0; } - .movies .alph_nav.float { - box-shadow: 0 30px 30px -32px rgba(0,0,0,0.5); - border-radius: 0; + @media all and (max-width: 480px) { + .movies .alph_nav { + display: none; + } } -.movies .alph_nav ul.numbers, + .movies .alph_nav > div { + position: relative; + max-width: 980px; + margin: 0 auto; + padding: 0; + min-height: 24px; + } + +.movies .alph_nav .numbers, .movies .alph_nav .counter, -.movies .alph_nav ul.actions { +.movies .alph_nav .actions { list-style: none; padding: 0 0 1px; margin: 0; @@ -604,49 +729,62 @@ } .movies .alph_nav .counter { - width: 60px; - text-align: center; + text-align: right; + position: absolute; + right: 270px; + background: #4e5969; + padding: 4px 10px; } .movies .alph_nav .numbers li, .movies .alph_nav .actions li { display: inline-block; vertical-align: top; - width: 20px; height: 24px; - line-height: 26px; + line-height: 23px; text-align: center; cursor: pointer; color: rgba(255,255,255,0.2); border: 1px solid transparent; transition: all 0.1s ease-in-out; - text-shadow: none; } - .movies .alph_nav .numbers li:first-child { - width: 43px; - } + + @media all and (max-width: 900px) { + .movies .alph_nav .numbers { + display: none; + } + } + + .movies .alph_nav .numbers li { + width: auto; + padding: 0 4px; + } + .movies .alph_nav li.available { - color: rgba(255,255,255,0.8); + color: #FFF; font-weight: bolder; } - .movies .alph_nav li.active.available, .movies .alph_nav li.available:hover { - color: #fff; - font-size: 20px; - line-height: 20px; + .movies .alph_nav li.active.available, + .movies .alph_nav li.available:hover { + background: rgba(255,255,255,.1); } - .movies .alph_nav input { + .movies .alph_nav .search { padding: 6px 5px; - margin: 0 0 0 6px; - float: left; - width: 155px; + margin: 0 0 0 20px; + position: absolute; + right: 30px; + width: 154px; height: 25px; + transition: all 0.6s cubic-bezier(0.9,0,0.1,1); } .movies .alph_nav .actions { margin: 0 6px 0 0; -moz-user-select: none; + position: absolute; + right: 183px; } .movies .alph_nav .actions li { border-radius: 1px; @@ -730,10 +868,12 @@ } .movies .alph_nav .more_menu { - margin-left: 48px; + right: 0; + position: absolute; } .movies .alph_nav .more_menu > a { + background-color: #4e5969; background-position: center -158px; } @@ -790,7 +930,6 @@ font-weight: bold; display: inline-block; text-transform: uppercase; - text-shadow: none; font-weight: normal; font-size: 20px; border-left: 1px solid rgba(255, 255, 255, .2); diff --git a/couchpotato/core/plugins/movie/static/movie.js b/couchpotato/core/plugins/movie/static/movie.js index ffdd14d..4f90322 100644 --- a/couchpotato/core/plugins/movie/static/movie.js +++ b/couchpotato/core/plugins/movie/static/movie.js @@ -126,12 +126,14 @@ var Movie = new Class({ self.thumbnail = File.Select.single('poster', self.data.library.files), self.data_container = new Element('div.data.inlay.light').adopt( self.info_container = new Element('div.info').adopt( - self.title = new Element('div.title', { - 'text': self.getTitle() || 'n/a' - }), - self.year = new Element('div.year', { - 'text': self.data.library.year || 'n/a' - }), + new Element('div.title').adopt( + self.title = new Element('span', { + 'text': self.getTitle() || 'n/a' + }), + self.year = new Element('div.year', { + 'text': self.data.library.year || 'n/a' + }) + ), self.rating = new Element('div.rating.icon', { 'text': self.data.library.rating }), diff --git a/couchpotato/core/plugins/movie/static/search.css b/couchpotato/core/plugins/movie/static/search.css index 23d87b4..e03c646 100644 --- a/couchpotato/core/plugins/movie/static/search.css +++ b/couchpotato/core/plugins/movie/static/search.css @@ -1,105 +1,143 @@ .search_form { display: inline-block; vertical-align: middle; - width: 25%; + position: absolute; + right: 105px; + top: 0; + text-align: right; + height: 100%; + border-bottom: 4px solid transparent; + transition: all .4s cubic-bezier(0.9,0,0.1,1); + position: absolute; + z-index: 20; + border: 1px solid transparent; + border-width: 0 0 4px; } + .search_form:hover { + border-color: #047792; + } - .search_form input { - padding: 4px 20px 4px 4px; - margin: 0; - font-size: 14px; - width: 100%; - height: 24px; + @media all and (max-width: 480px) { + .search_form { + right: 44px; + } + } + + .search_form.focused, + .search_form.shown { + border-color: #04bce6; } - .search_form input:focus { - padding-right: 83px; + + .search_form .input { + height: 100%; + overflow: hidden; + width: 45px; + transition: all .4s cubic-bezier(0.9,0,0.1,1); + } + + .search_form.focused .input, + .search_form.shown .input { + width: 380px; + background: #4e5969; } - .search_form .input .enter { - background: #369545 url('../images/sprite.png') right -188px no-repeat; - padding: 0 20px 0 4px; - border-radius: 2px; - text-transform: uppercase; - font-size: 10px; - margin-left: -78px; - display: inline-block; - opacity: 0; - position: relative; - top: -2px; - cursor: pointer; - vertical-align: middle; - visibility: hidden; - } - .search_form.focused .input .enter { - visibility: visible; + .search_form .input input { + border-radius: 0; + display: block; + width: 100%; + border: 0; + background: rgba(255,255,255,.08); + color: #FFF; + font-size: 25px; + height: 100%; + padding: 10px; + width: 100%; + opacity: 0; + padding: 0 40px 0 10px; + transition: all .4s ease-in-out .2s; } - .search_form.focused.filled .input .enter { - opacity: 1; + .search_form.focused .input input, + .search_form.shown .input input { + opacity: 1; + } + + @media all and (max-width: 480px) { + .search_form .input input { + font-size: 15px; + } + + .search_form.focused .input, + .search_form.shown .input { + width: 277px; + } } - - .search_form .input a { - width: 17px; - height: 20px; - display: inline-block; - margin: -2px 0 0 2px; - top: 4px; - right: 5px; - background: url('../images/sprite.png') left -37px no-repeat; - cursor: pointer; - opacity: 0; - transition: all 0.2s ease-in-out; - vertical-align: middle; + + .search_form .input a { + position: absolute; + top: 0; + right: 0; + width: 44px; + height: 100%; + cursor: pointer; + vertical-align: middle; + text-align: center; + line-height: 66px; + font-size: 15px; + color: #FFF; + } + + .search_form .input a:after { + content: "\e03e"; } - .search_form.filled .input a { - opacity: 1; + .search_form.shown.filled .input a:after { + content: "\e04e"; + } + + @media all and (max-width: 480px) { + .search_form .input a { + line-height: 44px; + } } .search_form .results_container { + text-align: left; position: absolute; background: #5c697b; - margin: 6px 0 0 -230px; + margin: 4px 0 0; width: 470px; min-height: 140px; - border-radius: 3px; box-shadow: 0 20px 20px -10px rgba(0,0,0,0.55); display: none; } - .search_form.shown.filled .results_container { - display: block; + @media all and (max-width: 480px) { + .search_form .results_container { + width: 320px; + } } - - .search_form .results_container:before { - content: ' '; - height: 0; - position: relative; - width: 0; - border: 10px solid transparent; - border-bottom-color: #5c697b; + .search_form.focused.filled .results_container, + .search_form.shown.filled .results_container { display: block; - top: -20px; - left: 346px; } .search_form .results { max-height: 570px; overflow-x: hidden; - padding: 10px 0; - margin-top: -18px; } .movie_result { overflow: hidden; - height: 140px; + height: 50px; position: relative; } .movie_result .options { position: absolute; height: 100%; - width: 100%; top: 0; - left: 0; + left: 30px; + right: 0; + padding: 13px; border: 1px solid transparent; border-width: 1px 0; border-radius: 0; @@ -107,7 +145,6 @@ } .movie_result .options > div { - padding: 0 15px; border: 0; } @@ -122,6 +159,13 @@ } .movie_result .options select[name=title] { width: 180px; } .movie_result .options select[name=profile] { width: 90px; } + + @media all and (max-width: 480px) { + + .movie_result .options select[name=title] { width: 90px; } + .movie_result .options select[name=profile] { width: 60px; } + + } .movie_result .options .button { vertical-align: middle; @@ -130,25 +174,21 @@ .movie_result .options .message { height: 100%; - line-height: 140px; font-size: 20px; - text-align: center; color: #fff; + line-height: 20px; } .movie_result .data { - padding: 0 15px; position: absolute; height: 100%; - width: 100%; top: 0; - left: 0; + left: 30px; + right: 0; background: #5c697b; cursor: pointer; - - border-bottom: 1px solid #333; - border-top: 1px solid rgba(255,255,255, 0.15); - transition: all .6s cubic-bezier(0.9,0,0.1,1); + border-top: 1px solid rgba(255,255,255, 0.08); + transition: all .4s cubic-bezier(0.9,0,0.1,1); } .movie_result .data.open { left: 100%; @@ -162,49 +202,40 @@ } .movie_result .thumbnail { - width: 17%; - display: inline-block; - margin: 15px 3% 15px 0; + width: 34px; + min-height: 100%; + display: block; + margin: 0; vertical-align: top; - border-radius: 3px; - box-shadow: 0 0 3px rgba(0,0,0,0.35); } .movie_result .info { - width: 80%; - display: inline-block; - vertical-align: top; - padding: 15px 0; - height: 120px; - overflow: hidden; - } - - .movie_result .info .tagline { - max-height: 70px; - overflow: hidden; - display: inline-block; - } - - .movie_result .add +.info { - margin-left: 20%; + position: absolute; + top: 20%; + left: 15px; + right: 60px; + vertical-align: middle; } .movie_result .info h2 { + font-weight: normal; + font-size: 20px; + display: block; margin: 0; - font-size: 17px; - line-height: 20px; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + width: 100%; } .movie_result .info h2 span { padding: 0 5px; + position: absolute; + right: -60px; } - .movie_result .info h2 span:before { content: "("; } - .movie_result .info h2 span:after { content: ")"; } - .search_form .mask, .movie_result .mask { - border-radius: 3px; position: absolute; height: 100%; width: 100%; diff --git a/couchpotato/core/plugins/movie/static/search.js b/couchpotato/core/plugins/movie/static/search.js index dd8e7b0..334f855 100644 --- a/couchpotato/core/plugins/movie/static/search.js +++ b/couchpotato/core/plugins/movie/static/search.js @@ -7,33 +7,30 @@ Block.Search = new Class({ create: function(){ var self = this; + var focus_timer = 0; self.el = new Element('div.search_form').adopt( new Element('div.input').adopt( - self.input = new Element('input.inlay', { + self.input = new Element('input', { 'placeholder': 'Search & add a new movie', 'events': { 'keyup': self.keyup.bind(self), 'focus': function(){ + if(focus_timer) clearTimeout(focus_timer); self.el.addClass('focused') if(this.get('value')) self.hideResults(false) }, 'blur': function(){ - (function(){ + focus_timer = (function(){ self.el.removeClass('focused') - }).delay(2000); + }).delay(100); } } }), - new Element('span.enter', { + new Element('a.icon2', { 'events': { - 'click': self.keyup.bind(self) - }, - 'text':'Enter' - }), - new Element('a', { - 'events': { - 'click': self.clear.bind(self) + 'click': self.clear.bind(self), + 'touchend': self.clear.bind(self) } }) ), @@ -59,13 +56,21 @@ Block.Search = new Class({ var self = this; (e).preventDefault(); - self.last_q = ''; - self.input.set('value', ''); - self.input.focus() + if(self.last_q === ''){ + self.input.blur() + self.last_q = null; + } + else { + + self.last_q = ''; + self.input.set('value', ''); + self.input.focus() - self.movies = [] - self.results.empty() - self.el.removeClass('filled') + self.movies = [] + self.results.empty() + self.el.removeClass('filled') + + } }, hideResults: function(bool){ @@ -92,8 +97,10 @@ Block.Search = new Class({ self.el[self.q() ? 'addClass' : 'removeClass']('filled') - if(self.q() != self.last_q && (['enter'].indexOf(e.key) > -1 || e.type == 'click')) - self.autocomplete() + if(self.q() != self.last_q){ + if(self.autocomplete_timer) clearTimeout(self.autocomplete_timer) + self.autocomplete_timer = self.autocomplete.delay(300, self) + } }, @@ -197,6 +204,11 @@ Block.Search.Item = new Class({ self.el = new Element('div.movie_result', { 'id': info.imdb }).adopt( + self.thumbnail = info.images && info.images.poster.length > 0 ? new Element('img.thumbnail', { + 'src': info.images.poster[0], + 'height': null, + 'width': null + }) : null, self.options_el = new Element('div.options.inlay'), self.data_container = new Element('div.data', { 'tween': { @@ -207,11 +219,6 @@ Block.Search.Item = new Class({ 'click': self.showOptions.bind(self) } }).adopt( - self.thumbnail = info.images && info.images.poster.length > 0 ? new Element('img.thumbnail', { - 'src': info.images.poster[0], - 'height': null, - 'width': null - }) : null, new Element('div.info').adopt( self.title = new Element('h2', { 'text': info.titles[0] @@ -219,28 +226,11 @@ Block.Search.Item = new Class({ self.year = info.year ? new Element('span.year', { 'text': info.year }) : null - ), - self.tagline = new Element('span.tagline', { - 'text': info.tagline ? info.tagline : info.plot, - 'title': info.tagline ? info.tagline : info.plot - }), - self.director = self.info.director ? new Element('span.director', { - 'text': 'Director:' + info.director - }) : null, - self.starring = info.actors ? new Element('span.actors', { - 'text': 'Starring:' - }) : null + ) ) ) ) - if(info.actors){ - Object.each(info.actors, function(actor){ - new Element('span', { - 'text': actor - }).inject(self.starring) - }) - } info.titles.each(function(title){ self.alternativeTitle({ @@ -320,11 +310,6 @@ Block.Search.Item = new Class({ self.options_el.grab( new Element('div').adopt( - self.thumbnail = (info.images && info.images.poster.length > 0) ? new Element('img.thumbnail', { - 'src': info.images.poster[0], - 'height': null, - 'width': null - }) : null, self.info.in_wanted && self.info.in_wanted.profile ? new Element('span.in_wanted', { 'text': 'Already in wanted list: ' + self.info.in_wanted.profile.label }) : (in_library ? new Element('span.in_library', { diff --git a/couchpotato/core/plugins/profile/static/profile.css b/couchpotato/core/plugins/profile/static/profile.css index 9d50d2f..21abc66 100644 --- a/couchpotato/core/plugins/profile/static/profile.css +++ b/couchpotato/core/plugins/profile/static/profile.css @@ -61,6 +61,10 @@ margin-right: 10px; } + .profile .type .check { + margin-top: -1px; + } + .profile .quality_type select { width: 186px; margin-left: -1px; @@ -71,13 +75,13 @@ } .profile .types .type .handle { - background: url('./handle.png') center; + background: url('../../static/profile_plugin/handle.png') center; display: inline-block; height: 20px; width: 20px; - cursor: grab; - cursor: -moz-grab; cursor: -webkit-grab; + cursor: -moz-grab; + cursor: grab; margin: 0; } @@ -105,9 +109,9 @@ } #profile_ordering li { - cursor: grab; - cursor: -moz-grab; cursor: -webkit-grab; + cursor: -moz-grab; + cursor: grab; border-bottom: 1px solid rgba(255,255,255,0.2); padding: 0 5px; } @@ -126,7 +130,7 @@ } #profile_ordering li .handle { - background: url('./handle.png') center; + background: url('../../static/profile_plugin/handle.png') center; width: 20px; float: right; } diff --git a/couchpotato/core/plugins/release/main.py b/couchpotato/core/plugins/release/main.py index 68bf60a..4967bce 100644 --- a/couchpotato/core/plugins/release/main.py +++ b/couchpotato/core/plugins/release/main.py @@ -41,12 +41,15 @@ class Release(Plugin): addEvent('release.clean', self.clean) def add(self, group): + db = get_session() identifier = '%s.%s.%s' % (group['library']['identifier'], group['meta_data'].get('audio', 'unknown'), group['meta_data']['quality']['identifier']) + + done_status, snatched_status = fireEvent('status.get', ['done', 'snatched'], single = True) + # Add movie - done_status = fireEvent('status.get', 'done', single = True) movie = db.query(Movie).filter_by(library_id = group['library'].get('id')).first() if not movie: movie = Movie( @@ -58,7 +61,6 @@ class Release(Plugin): db.commit() # Add Release - snatched_status = fireEvent('status.get', 'snatched', single = True) rel = db.query(Relea).filter( or_( Relea.identifier == identifier, @@ -76,15 +78,19 @@ class Release(Plugin): db.commit() # Add each file type + added_files = [] for type in group['files']: for cur_file in group['files'][type]: added_file = self.saveFile(cur_file, type = type, include_media_info = type is 'movie') - try: - added_file = db.query(File).filter_by(id = added_file.get('id')).one() - rel.files.append(added_file) - db.commit() - except Exception, e: - log.debug('Failed to attach "%s" to release: %s', (cur_file, e)) + added_files.append(added_file.get('id')) + + # Add the release files in batch + try: + added_files = db.query(File).filter(or_(*[File.id == x for x in added_files])).all() + rel.files.append(added_files) + db.commit() + except Exception, e: + log.debug('Failed to attach "%s" to release: %s', (cur_file, e)) fireEvent('movie.restatus', movie.id) @@ -147,8 +153,7 @@ class Release(Plugin): rel = db.query(Relea).filter_by(id = id).first() if rel: - ignored_status = fireEvent('status.get', 'ignored', single = True) - available_status = fireEvent('status.get', 'available', single = True) + ignored_status, available_status = fireEvent('status.get', ['ignored', 'available'], single = True) rel.status_id = available_status.get('id') if rel.status_id is ignored_status.get('id') else ignored_status.get('id') db.commit() @@ -161,8 +166,7 @@ class Release(Plugin): db = get_session() id = getParam('id') - snatched_status = fireEvent('status.add', 'snatched', single = True) - done_status = fireEvent('status.get', 'done', single = True) + snatched_status, done_status = fireEvent('status.get', ['snatched', 'done'], single = True) rel = db.query(Relea).filter_by(id = id).first() if rel: diff --git a/couchpotato/core/plugins/renamer/__init__.py b/couchpotato/core/plugins/renamer/__init__.py index 90f5c98..f3f7f66 100644 --- a/couchpotato/core/plugins/renamer/__init__.py +++ b/couchpotato/core/plugins/renamer/__init__.py @@ -114,11 +114,11 @@ config = [{ }, { 'name': 'file_action', - 'label': 'File Action', + 'label': 'Torrent File Action', 'default': 'move', 'type': 'dropdown', 'values': [('Move', 'move'), ('Copy', 'copy'), ('Hard link', 'hardlink'), ('Sym link', 'symlink'), ('Move & Sym link', 'move_symlink')], - 'description': 'Define which kind of file operation you want to use. Before you start using hard links or sym links, PLEASE read about their possible drawbacks.', + 'description': 'Define which kind of file operation you want to use for torrents. Before you start using hard links or sym links, PLEASE read about their possible drawbacks.', 'advanced': True, }, { diff --git a/couchpotato/core/plugins/renamer/main.py b/couchpotato/core/plugins/renamer/main.py index 87d9aaf..30a57a7 100644 --- a/couchpotato/core/plugins/renamer/main.py +++ b/couchpotato/core/plugins/renamer/main.py @@ -4,13 +4,12 @@ from couchpotato.core.event import addEvent, fireEvent, fireEventAsync from couchpotato.core.helpers.encoding import toUnicode, ss from couchpotato.core.helpers.request import getParams, jsonified from couchpotato.core.helpers.variable import getExt, mergeDicts, getTitle, \ - getImdb + getImdb, link, symlink from couchpotato.core.logger import CPLog from couchpotato.core.plugins.base import Plugin from couchpotato.core.settings.model import Library, File, Profile, Release, \ ReleaseInfo from couchpotato.environment import Env -from linktastic.linktastic import link, symlink import errno import os import re @@ -20,7 +19,6 @@ import traceback log = CPLog(__name__) - class Renamer(Plugin): renaming_started = False @@ -70,15 +68,14 @@ class Renamer(Plugin): fireEventAsync('renamer.scan', movie_folder = movie_folder, - downloader = downloader, - download_id = download_id + download_info = {'id': download_id, 'downloader': downloader} if download_id else None ) return jsonified({ 'success': True }) - def scan(self, movie_folder = None, downloader = None, download_id = None): + def scan(self, movie_folder = None, download_info = None): if self.isDisabled(): return @@ -103,7 +100,7 @@ class Renamer(Plugin): # make sure the movie folder name is included in the search folder = None - movie_files = [] + files = [] if movie_folder: log.info('Scanning movie folder %s...', movie_folder) movie_folder = movie_folder.rstrip(os.path.sep) @@ -112,37 +109,17 @@ class Renamer(Plugin): # Get all files from the specified folder try: for root, folders, names in os.walk(movie_folder): - movie_files.extend([os.path.join(root, name) for name in names]) + files.extend([os.path.join(root, name) for name in names]) except: log.error('Failed getting files from %s: %s', (movie_folder, traceback.format_exc())) db = get_session() - # Get the release with the downloader ID that was downloded by the downloader - download_info = None - if download_id and downloader: - rls = None - - rlsnfo_dwnlds = db.query(ReleaseInfo).filter_by(identifier = 'download_downloader', value = downloader).all() - rlsnfo_ids = db.query(ReleaseInfo).filter_by(identifier = 'download_id', value = download_id).all() - - for rlsnfo_dwnld in rlsnfo_dwnlds: - for rlsnfo_id in rlsnfo_ids: - if rlsnfo_id.release == rlsnfo_dwnld.release: - rls = rlsnfo_id.release - break - if rls: break - - if rls: - download_info = { - 'imdb_id': rls.movie.library.identifier, - 'quality': rls.quality.identifier, - } - else: - log.error('Download ID %s from downloader %s not found in releases', (download_id, downloader)) + # Extend the download info with info stored in the downloaded release + download_info = self.extendDownloadInfo(download_info) groups = fireEvent('scanner.scan', folder = folder if folder else self.conf('from'), - files = movie_files, download_info = download_info, return_ignored = False, single = True) + files = files, download_info = download_info, return_ignored = False, single = True) destination = self.conf('to') folder_name = self.conf('folder_name') @@ -152,10 +129,8 @@ class Renamer(Plugin): separator = self.conf('separator') # Statusses - done_status = fireEvent('status.get', 'done', single = True) - active_status = fireEvent('status.get', 'active', single = True) - downloaded_status = fireEvent('status.get', 'downloaded', single = True) - snatched_status = fireEvent('status.get', 'snatched', single = True) + done_status, active_status, downloaded_status, snatched_status = \ + fireEvent('status.get', ['done', 'active', 'downloaded', 'snatched'], single = True) for group_identifier in groups: @@ -375,7 +350,7 @@ class Renamer(Plugin): else: log.info('Better quality release already exists for %s, with quality %s', (movie.library.titles[0].title, release.quality.label)) - # Add _EXISTS_ to the parent dir + # Add exists tag to the .ignore file self.tagDir(group, 'exists') # Notify on rename fail @@ -396,7 +371,8 @@ class Renamer(Plugin): db.commit() # Remove leftover files - if self.conf('cleanup') and not self.conf('move_leftover') and remove_leftovers: + if self.conf('cleanup') and not self.conf('move_leftover') and remove_leftovers and \ + not (self.conf('file_action') != 'move' and self.downloadIsTorrent(download_info)): log.debug('Removing leftover files') for current_file in group['files']['leftover']: remove_files.append(current_file) @@ -421,7 +397,7 @@ class Renamer(Plugin): os.remove(src) parent_dir = os.path.normpath(os.path.dirname(src)) - if delete_folders.count(parent_dir) == 0 and os.path.isdir(parent_dir) and destination != parent_dir: + if delete_folders.count(parent_dir) == 0 and os.path.isdir(parent_dir) and not parent_dir in [destination, movie_folder] and not self.conf('from') in parent_dir: delete_folders.append(parent_dir) except: @@ -446,13 +422,13 @@ class Renamer(Plugin): self.makeDir(os.path.dirname(dst)) try: - self.moveFile(src, dst) + self.moveFile(src, dst, forcemove = not self.downloadIsTorrent(download_info)) group['renamed_files'].append(dst) except: log.error('Failed moving the file "%s" : %s', (os.path.basename(src), traceback.format_exc())) self.tagDir(group, 'failed_rename') - if self.conf('file_action') != 'move': + if self.conf('file_action') != 'move' and self.downloadIsTorrent(download_info): self.tagDir(group, 'renamed already') # Remove matching releases @@ -518,10 +494,12 @@ Remove it if you want it to be renamed (again, or at least let it try again) self.createFile(ignore_file, text) - def moveFile(self, old, dest): + def moveFile(self, old, dest, forcemove = False): dest = ss(dest) try: - if self.conf('file_action') == 'hardlink': + if forcemove: + shutil.move(old, dest) + elif self.conf('file_action') == 'hardlink': link(old, dest) elif self.conf('file_action') == 'symlink': symlink(old, dest) @@ -606,11 +584,8 @@ Remove it if you want it to be renamed (again, or at least let it try again) self.checking_snatched = True - snatched_status = fireEvent('status.get', 'snatched', single = True) - ignored_status = fireEvent('status.get', 'ignored', single = True) - failed_status = fireEvent('status.get', 'failed', single = True) - - done_status = fireEvent('status.get', 'done', single = True) + snatched_status, ignored_status, failed_status, done_status = \ + fireEvent('status.get', ['snatched', 'ignored', 'failed', 'done'], single = True) db = get_session() rels = db.query(Release).filter_by(status_id = snatched_status.get('id')).all() @@ -665,17 +640,16 @@ Remove it if you want it to be renamed (again, or at least let it try again) pass elif item['status'] == 'failed': fireEvent('download.remove_failed', item, single = True) + rel.status_id = failed_status.get('id') + rel.last_edit = int(time.time()) + db.commit() if self.conf('next_on_failed'): fireEvent('searcher.try_next_release', movie_id = rel.movie_id) - else: - rel.status_id = failed_status.get('id') - rel.last_edit = int(time.time()) - db.commit() elif item['status'] == 'completed': log.info('Download of %s completed!', item['name']) if item['id'] and item['downloader'] and item['folder']: - fireEventAsync('renamer.scan', movie_folder = item['folder'], downloader = item['downloader'], download_id = item['id']) + fireEventAsync('renamer.scan', movie_folder = item['folder'], download_info = item) else: scan_required = True @@ -694,3 +668,38 @@ Remove it if you want it to be renamed (again, or at least let it try again) self.checking_snatched = False return True + + def extendDownloadInfo(self, download_info): + + rls = None + + if download_info and download_info.get('id') and download_info.get('downloader'): + + db = get_session() + + rlsnfo_dwnlds = db.query(ReleaseInfo).filter_by(identifier = 'download_downloader', value = download_info.get('downloader')).all() + rlsnfo_ids = db.query(ReleaseInfo).filter_by(identifier = 'download_id', value = download_info.get('id')).all() + + for rlsnfo_dwnld in rlsnfo_dwnlds: + for rlsnfo_id in rlsnfo_ids: + if rlsnfo_id.release == rlsnfo_dwnld.release: + rls = rlsnfo_id.release + break + if rls: break + + if not rls: + log.error('Download ID %s from downloader %s not found in releases', (download_info.get('id'), download_info.get('downloader'))) + + if rls: + + rls_dict = rls.to_dict({'info':{}}) + download_info.update({ + 'imdb_id': rls.movie.library.identifier, + 'quality': rls.quality.identifier, + 'type': rls_dict.get('info', {}).get('type') + }) + + return download_info + + def downloadIsTorrent(self, download_info): + return download_info and download_info.get('type') in ['torrent', 'torrent_magnet'] diff --git a/couchpotato/core/plugins/searcher/__init__.py b/couchpotato/core/plugins/searcher/__init__.py index 90cece4..aec419a 100644 --- a/couchpotato/core/plugins/searcher/__init__.py +++ b/couchpotato/core/plugins/searcher/__init__.py @@ -25,12 +25,13 @@ config = [{ 'label': 'Required words', 'default': '', 'placeholder': 'Example: DTS, AC3 & English', - 'description': 'Ignore releases that don\'t contain at least one set of words. Sets are separated by "," and each word within a set must be separated with "&"' + 'description': 'A release should contain at least one set of words. Sets are separated by "," and each word within a set must be separated with "&"' }, { 'name': 'ignored_words', 'label': 'Ignored words', 'default': 'german, dutch, french, truefrench, danish, swedish, spanish, italian, korean, dubbed, swesub, korsub, dksubs', + 'description': 'Ignores releases that match any of these sets. (Works like explained above)' }, { 'name': 'preferred_method', diff --git a/couchpotato/core/plugins/searcher/main.py b/couchpotato/core/plugins/searcher/main.py index ad33c6f..dba0ea7 100644 --- a/couchpotato/core/plugins/searcher/main.py +++ b/couchpotato/core/plugins/searcher/main.py @@ -146,8 +146,7 @@ class Searcher(Plugin): pre_releases = fireEvent('quality.pre_releases', single = True) release_dates = fireEvent('library.update_release_date', identifier = movie['library']['identifier'], merge = True) - available_status = fireEvent('status.get', 'available', single = True) - ignored_status = fireEvent('status.get', 'ignored', single = True) + available_status, ignored_status = fireEvent('status.get', ['available', 'ignored'], single = True) found_releases = [] @@ -384,8 +383,9 @@ class Searcher(Plugin): movie_words = re.split('\W+', simplifyString(movie_name)) nzb_name = simplifyString(nzb['name']) nzb_words = re.split('\W+', nzb_name) - required_words = splitString(self.conf('required_words').lower()) + # Make sure it has required words + required_words = splitString(self.conf('required_words').lower()) req_match = 0 for req_set in required_words: req = splitString(req_set, '&') @@ -395,19 +395,24 @@ class Searcher(Plugin): log.info2("Wrong: Required word missing: %s" % nzb['name']) return False + # Ignore releases ignored_words = splitString(self.conf('ignored_words').lower()) - blacklisted = list(set(nzb_words) & set(ignored_words) - set(movie_words)) - if self.conf('ignored_words') and blacklisted: - log.info2("Wrong: '%s' blacklisted words: %s" % (nzb['name'], ", ".join(blacklisted))) + ignored_match = 0 + for ignored_set in ignored_words: + ignored = splitString(ignored_set, '&') + ignored_match += len(list(set(nzb_words) & set(ignored))) == len(ignored) + + if self.conf('ignored_words') and ignored_match: + log.info2("Wrong: '%s' contains 'ignored words'" % (nzb['name'])) return False + # Ignore porn stuff pron_tags = ['xxx', 'sex', 'anal', 'tits', 'fuck', 'porn', 'orgy', 'milf', 'boobs', 'erotica', 'erotic'] pron_words = list(set(nzb_words) & set(pron_tags) - set(movie_words)) if pron_words: log.info('Wrong: %s, probably pr0n', (nzb['name'])) return False - #qualities = fireEvent('quality.all', single = True) preferred_quality = fireEvent('quality.single', identifier = quality['identifier'], single = True) # Contains lower quality string diff --git a/couchpotato/core/plugins/status/main.py b/couchpotato/core/plugins/status/main.py index c01caef..c496c2d 100644 --- a/couchpotato/core/plugins/status/main.py +++ b/couchpotato/core/plugins/status/main.py @@ -25,13 +25,14 @@ class StatusPlugin(Plugin): 'available': 'Available', 'suggest': 'Suggest', } + status_cached = {} def __init__(self): - addEvent('status.add', self.add) - addEvent('status.get', self.add) # Alias for .add + addEvent('status.get', self.get) addEvent('status.get_by_id', self.getById) addEvent('status.all', self.all) addEvent('app.initialize', self.fill) + addEvent('app.load', self.all) # Cache all statuses addApiView('status.list', self.list, docs = { 'desc': 'Check for available update', @@ -67,26 +68,40 @@ class StatusPlugin(Plugin): s = status.to_dict() temp.append(s) - #db.close() + # Update cache + self.status_cached[status.identifier] = s + return temp - def add(self, identifier): + def get(self, identifiers): + + if not isinstance(identifiers, (list)): + identifiers = [identifiers] db = get_session() + return_list = [] - s = db.query(Status).filter_by(identifier = identifier).first() - if not s: - s = Status( - identifier = identifier, - label = toUnicode(identifier.capitalize()) - ) - db.add(s) - db.commit() + for identifier in identifiers: - status_dict = s.to_dict() + if self.status_cached.get(identifier): + return_list.append(self.status_cached.get(identifier)) + continue - #db.close() - return status_dict + s = db.query(Status).filter_by(identifier = identifier).first() + if not s: + s = Status( + identifier = identifier, + label = toUnicode(identifier.capitalize()) + ) + db.add(s) + db.commit() + + status_dict = s.to_dict() + + self.status_cached[identifier] = status_dict + return_list.append(status_dict) + + return return_list if len(identifiers) > 1 else return_list[0] def fill(self): diff --git a/couchpotato/core/plugins/trailer/__init__.py b/couchpotato/core/plugins/trailer/__init__.py index e4c51bd..d8496b3 100644 --- a/couchpotato/core/plugins/trailer/__init__.py +++ b/couchpotato/core/plugins/trailer/__init__.py @@ -29,7 +29,7 @@ config = [{ 'label': 'Naming', 'default': '-trailer', 'advanced': True, - 'description': 'Use to use above settings.' + 'description': 'Use <filename> to use above settings.' }, ], }, diff --git a/couchpotato/core/plugins/trailer/main.py b/couchpotato/core/plugins/trailer/main.py index 4ab51e7..1a8955f 100644 --- a/couchpotato/core/plugins/trailer/main.py +++ b/couchpotato/core/plugins/trailer/main.py @@ -22,10 +22,15 @@ class Trailer(Plugin): return False for trailer in trailers.get(self.conf('quality'), []): - filename = self.conf('name').replace('', group['filename']) + ('.%s' % getExt(trailer)) + + ext = getExt(trailer) + filename = self.conf('name').replace('', group['filename']) + ('.%s' % ('mp4' if len(ext) > 5 else ext)) destination = os.path.join(group['destination_dir'], filename) if not os.path.isfile(destination): - fireEvent('file.download', url = trailer, dest = destination, urlopen_kwargs = {'headers': {'User-Agent': 'Quicktime'}}, single = True) + trailer_file = fireEvent('file.download', url = trailer, dest = destination, urlopen_kwargs = {'headers': {'User-Agent': 'Quicktime'}}, single = True) + if os.path.getsize(trailer_file) < (1024 * 1024): # Don't trust small trailers (1MB), try next one + os.unlink(trailer_file) + continue else: log.debug('Trailer already exists: %s', destination) diff --git a/couchpotato/core/plugins/userscript/static/userscript.css b/couchpotato/core/plugins/userscript/static/userscript.css index 304dfa7..d08953a 100644 --- a/couchpotato/core/plugins/userscript/static/userscript.css +++ b/couchpotato/core/plugins/userscript/static/userscript.css @@ -5,6 +5,7 @@ bottom: 0; left: 0; right: 0; + padding: 0; } .page.userscript .frame.loading { @@ -12,3 +13,26 @@ font-size: 20px; padding: 20px; } + + .page.userscript .movie_result { + height: 140px; + } + .page.userscript .movie_result .thumbnail { + width: 90px; + } + .page.userscript .movie_result .options { + left: 90px; + padding: 54px 15px; + } + + .page.userscript .movie_result .year { + display: none; + } + + .page.userscript .movie_result .options select[name="title"] { + width: 190px; + } + + .page.userscript .movie_result .options select[name="profile"] { + width: 70px; + } diff --git a/couchpotato/core/plugins/userscript/static/userscript.js b/couchpotato/core/plugins/userscript/static/userscript.js index d6d5983..b784976 100644 --- a/couchpotato/core/plugins/userscript/static/userscript.js +++ b/couchpotato/core/plugins/userscript/static/userscript.js @@ -63,28 +63,19 @@ var UserscriptSettingTab = new Class({ self.settings = App.getPage('Settings') self.settings.addEvent('create', function(){ - // See if userscript can be installed - var userscript = false; - try { - if(Components.interfaces.gmIGreasemonkeyService) - userscript = true - } - catch(e){ - userscript = Browser.chrome === true; - } - var host_url = window.location.protocol + '//' + window.location.host; self.settings.createGroup({ 'name': 'userscript', - 'label': 'Install the bookmarklet' + (userscript ? ' or userscript' : ''), + 'label': 'Install the bookmarklet or userscript', 'description': 'Easily add movies via imdb.com, appletrailers and more' }).inject(self.settings.tabs.automation.content, 'top').adopt( - (userscript ? [new Element('a.userscript.button', { + new Element('a.userscript.button', { 'text': 'Install userscript', 'href': Api.createUrl('userscript.get')+randomString()+'/couchpotato.user.js', - 'target': '_self' - }), new Element('span.or[text=or]')] : null), + 'target': '_blank' + }), + new Element('span.or[text=or]'), new Element('span.bookmarklet').adopt( new Element('a.button.green', { 'text': '+CouchPotato', diff --git a/couchpotato/core/plugins/userscript/template.js b/couchpotato/core/plugins/userscript/template.js index c30fad5..c3dd17d 100644 --- a/couchpotato/core/plugins/userscript/template.js +++ b/couchpotato/core/plugins/userscript/template.js @@ -1,4 +1,9 @@ // ==UserScript== +// +// If you can read this, you need to enable or install the Greasemonkey add-on for firefox +// If you are using Chrome, download this file and drag it to the extensions tab +// Other browsers, use the bookmarklet +// // @name CouchPotato UserScript // @description Add movies like a real CouchPotato // @grant none diff --git a/couchpotato/core/plugins/wizard/static/wizard.css b/couchpotato/core/plugins/wizard/static/wizard.css index 8d50d9d..c27c1d8 100644 --- a/couchpotato/core/plugins/wizard/static/wizard.css +++ b/couchpotato/core/plugins/wizard/static/wizard.css @@ -1,49 +1,60 @@ .page.wizard .uniForm { - width: 80%; - margin: 0 auto 30px; + margin: 0 0 30px; + width: 83%; } .page.wizard h1 { - padding: 10px 30px; - margin: 0; + padding: 10px 0; + margin: 0 5px; display: block; font-size: 30px; margin-top: 80px; } .page.wizard .description { - padding: 10px 30px; - font-size: 18px; + padding: 10px 5px; + font-size: 1.45em; + line-height: 1.4em; display: block; } .page.wizard .tab_wrapper { background: #5c697b; - padding: 10px 0; - font-size: 18px; + height: 65px; + font-size: 1.75em; position: fixed; top: 0; margin: 0; width: 100%; - min-width: 960px; left: 0; z-index: 2; - box-shadow: 0 0 50px rgba(0,0,0,0.55); + box-shadow: 0 0 10px rgba(0,0,0,0.1); } .page.wizard .tab_wrapper .tabs { - text-align: center; padding: 0; - margin: 0; + margin: 0 auto; display: block; + height: 100%; + width: 100%; + max-width: 960px; } .page.wizard .tabs li { display: inline-block; + height: 100%; } .page.wizard .tabs li a { padding: 20px 10px; + height: 100%; + display: block; + color: #FFF; + font-weight: normal; + border-bottom: 4px solid transparent; } + + .page.wizard .tabs li:hover a { border-color: #047792; } + .page.wizard .tabs li.done a { border-color: #04bce6; } .page.wizard .tab_wrapper .pointer { border-right: 10px solid transparent; @@ -61,27 +72,13 @@ .page.wizard form > div { min-height: 300px; } -.page.wizard .wgroup_finish { - height: 300px; -} - .page.wizard .wgroup_finish h1 { - text-align: center; - } - .page.wizard .wgroup_finish .wizard_support, - .page.wizard .wgroup_finish .description { - font-size: 25px; - line-height: 120%; - margin: 20px 0; - text-align: center; - } - .page.wizard .button.green { - padding: 20px; - font-size: 25px; - margin: 10px 30px 80px; - display: block; - text-align: center; - } +.page.wizard .button.green { + padding: 20px; + font-size: 25px; + margin: 10px 0 80px; + display: block; +} .page.wizard .tab_nzb_providers { margin: 20px 0 0 0; diff --git a/couchpotato/core/plugins/wizard/static/wizard.js b/couchpotato/core/plugins/wizard/static/wizard.js index 12ae477..71910e2 100644 --- a/couchpotato/core/plugins/wizard/static/wizard.js +++ b/couchpotato/core/plugins/wizard/static/wizard.js @@ -9,27 +9,12 @@ Page.Wizard = new Class({ headers: { 'welcome': { 'title': 'Welcome to the new CouchPotato', - 'description': 'To get started, fill in each of the following settings as much as you can.
Maybe first start with importing your movies from the previous CouchPotato', + 'description': 'To get started, fill in each of the following settings as much as you can.', 'content': new Element('div', { 'styles': { 'margin': '0 0 0 30px' } - }).adopt( - new Element('div', { - 'html': 'Select the data.db. It should be in your CouchPotato root directory.' - }), - self.import_iframe = new Element('iframe', { - 'styles': { - 'height': 40, - 'width': 300, - 'border': 0, - 'overflow': 'hidden' - } - }) - ), - 'event': function(){ - self.import_iframe.set('src', Api.createUrl('v1.import')) - } + }) }, 'general': { 'title': 'General', @@ -178,7 +163,7 @@ Page.Wizard = new Class({ 'href': App.createUrl('wizard/'+group), 'text': (self.headers[group].label || group).capitalize() }) - ).inject(tabs); + ).inject(tabs) } else @@ -214,13 +199,7 @@ Page.Wizard = new Class({ self.el.getElement('.t_searcher').hide(); // Add pointer - new Element('.tab_wrapper').wraps(tabs).adopt( - self.pointer = new Element('.pointer', { - 'tween': { - 'transition': 'quint:in:out' - } - }) - ); + new Element('.tab_wrapper').wraps(tabs); // Add nav var minimum = self.el.getSize().y-window.getSize().y; @@ -232,16 +211,18 @@ Page.Wizard = new Class({ if(!t) return; var func = function(){ - var ct = t.getCoordinates(); - self.pointer.tween('left', ct.left+(ct.width/2)-(self.pointer.getWidth()/2)); + // Activate all previous ones + self.groups.each(function(groups2, nr2){ + var t2 = self.el.getElement('.t_'+groups2); + t2[nr2 > nr ? 'removeClass' : 'addClass' ]('done'); + }) g.tween('opacity', 1); } if(nr == 0) func(); - - var ss = new ScrollSpy( { + new ScrollSpy( { min: function(){ var c = g.getCoordinates(); var top = c.top-(window.getSize().y/2); diff --git a/couchpotato/core/providers/movie/_modifier/main.py b/couchpotato/core/providers/movie/_modifier/main.py index f0f98e0..12d1e32 100644 --- a/couchpotato/core/providers/movie/_modifier/main.py +++ b/couchpotato/core/providers/movie/_modifier/main.py @@ -52,8 +52,7 @@ class MovieResultModifier(Plugin): if l: # Statuses - active_status = fireEvent('status.get', 'active', single = True) - done_status = fireEvent('status.get', 'done', single = True) + active_status, done_status = fireEvent('status.get', ['active', 'done'], single = True) for movie in l.movies: if movie.status_id == active_status['id']: diff --git a/couchpotato/core/providers/movie/couchpotatoapi/main.py b/couchpotato/core/providers/movie/couchpotatoapi/main.py index 8415c2d..2beb7e2 100644 --- a/couchpotato/core/providers/movie/couchpotatoapi/main.py +++ b/couchpotato/core/providers/movie/couchpotatoapi/main.py @@ -2,6 +2,7 @@ from couchpotato import get_session from couchpotato.core.event import addEvent, fireEvent from couchpotato.core.helpers.encoding import tryUrlencode from couchpotato.core.helpers.request import jsonified, getParams +from couchpotato.core.helpers.variable import tryInt from couchpotato.core.logger import CPLog from couchpotato.core.providers.movie.base import MovieProvider from couchpotato.core.settings.model import Movie @@ -19,19 +20,37 @@ class CouchPotatoApi(MovieProvider): 'is_movie': 'https://couchpota.to/api/ismovie/%s/', 'eta': 'https://couchpota.to/api/eta/%s/', 'suggest': 'https://couchpota.to/api/suggest/', + 'updater': 'https://couchpota.to/api/updater/?%s', + 'messages': 'https://couchpota.to/api/messages/?%s', } http_time_between_calls = 0 api_version = 1 def __init__(self): - #addApiView('movie.suggest', self.suggestView) - addEvent('movie.info', self.getInfo, priority = 1) addEvent('movie.search', self.search, priority = 1) addEvent('movie.release_date', self.getReleaseDate) addEvent('movie.suggest', self.suggest) addEvent('movie.is_movie', self.isMovie) + addEvent('cp.source_url', self.getSourceUrl) + addEvent('cp.messages', self.getMessages) + + def getMessages(self, last_check = 0): + + data = self.getJsonData(self.urls['messages'] % tryUrlencode({ + 'last_check': last_check, + }), headers = self.getRequestHeaders(), cache_timeout = 10) + + return data + + def getSourceUrl(self, repo = None, repo_name = None, branch = None): + return self.getJsonData(self.urls['updater'] % tryUrlencode({ + 'repo': repo, + 'name': repo_name, + 'branch': branch, + }), headers = self.getRequestHeaders()) + def search(self, q, limit = 12): return self.getJsonData(self.urls['search'] % tryUrlencode(q), headers = self.getRequestHeaders()) diff --git a/couchpotato/core/providers/nzb/newznab/__init__.py b/couchpotato/core/providers/nzb/newznab/__init__.py index 625da1a..90f81cf 100644 --- a/couchpotato/core/providers/nzb/newznab/__init__.py +++ b/couchpotato/core/providers/nzb/newznab/__init__.py @@ -40,7 +40,7 @@ config = [{ }, { 'name': 'api_key', - 'default': ',,,', + 'default': ',,,,,', 'label': 'Api Key', 'description': 'Can be found on your profile page', 'type': 'combined', diff --git a/couchpotato/core/providers/torrent/torrentleech/main.py b/couchpotato/core/providers/torrent/torrentleech/main.py index 6de18fb..4247c53 100644 --- a/couchpotato/core/providers/torrent/torrentleech/main.py +++ b/couchpotato/core/providers/torrent/torrentleech/main.py @@ -74,3 +74,6 @@ class TorrentLeech(TorrentProvider): 'remember_me': 'on', 'login': 'submit', }) + + def loginSuccess(self, output): + return '/user/account/logout' in output.lower() or 'welcome back' in output.lower() diff --git a/couchpotato/core/providers/userscript/criticker/__init__.py b/couchpotato/core/providers/userscript/criticker/__init__.py new file mode 100644 index 0000000..129d878 --- /dev/null +++ b/couchpotato/core/providers/userscript/criticker/__init__.py @@ -0,0 +1,6 @@ +from .main import Criticker + +def start(): + return Criticker() + +config = [] diff --git a/couchpotato/core/providers/userscript/criticker/main.py b/couchpotato/core/providers/userscript/criticker/main.py new file mode 100644 index 0000000..680d9a3 --- /dev/null +++ b/couchpotato/core/providers/userscript/criticker/main.py @@ -0,0 +1,6 @@ +from couchpotato.core.providers.userscript.base import UserscriptBase + + +class Criticker(UserscriptBase): + + includes = ['http://www.criticker.com/film/*'] diff --git a/couchpotato/runner.py b/couchpotato/runner.py index 0ae14ce..2652217 100644 --- a/couchpotato/runner.py +++ b/couchpotato/runner.py @@ -241,7 +241,8 @@ def runCouchPotato(options, base_path, args, data_dir = None, log_dir = None, En from tornado.ioloop import IOLoop web_container = WSGIContainer(app) web_container._log = _log - loop = IOLoop.instance() + loop = IOLoop.current() + application = Application([ (r'%s/api/%s/nonblock/(.*)/' % (url_base, api_key), NonBlockHandler), diff --git a/couchpotato/static/fonts/Elusive-Icons.eot b/couchpotato/static/fonts/Elusive-Icons.eot new file mode 100755 index 0000000..282e608 Binary files /dev/null and b/couchpotato/static/fonts/Elusive-Icons.eot differ diff --git a/couchpotato/static/fonts/Elusive-Icons.svg b/couchpotato/static/fonts/Elusive-Icons.svg new file mode 100755 index 0000000..bfc6c9b --- /dev/null +++ b/couchpotato/static/fonts/Elusive-Icons.svg @@ -0,0 +1,298 @@ + + + + +This is a custom SVG font generated by IcoMoon. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/couchpotato/static/fonts/Elusive-Icons.ttf b/couchpotato/static/fonts/Elusive-Icons.ttf new file mode 100755 index 0000000..ae31768 Binary files /dev/null and b/couchpotato/static/fonts/Elusive-Icons.ttf differ diff --git a/couchpotato/static/fonts/Elusive-Icons.woff b/couchpotato/static/fonts/Elusive-Icons.woff new file mode 100755 index 0000000..7f974bd Binary files /dev/null and b/couchpotato/static/fonts/Elusive-Icons.woff differ diff --git a/couchpotato/static/fonts/OpenSans-Bold-webfont.eot b/couchpotato/static/fonts/OpenSans-Bold-webfont.eot new file mode 100755 index 0000000..e1c7674 Binary files /dev/null and b/couchpotato/static/fonts/OpenSans-Bold-webfont.eot differ diff --git a/couchpotato/static/fonts/OpenSans-Bold-webfont.svg b/couchpotato/static/fonts/OpenSans-Bold-webfont.svg new file mode 100755 index 0000000..364b368 --- /dev/null +++ b/couchpotato/static/fonts/OpenSans-Bold-webfont.svg @@ -0,0 +1,146 @@ + + + + +This is a custom SVG webfont generated by Font Squirrel. +Copyright : Digitized data copyright 20102011 Google Corporation +Foundry : Ascender Corporation +Foundry URL : httpwwwascendercorpcom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/couchpotato/static/fonts/OpenSans-Bold-webfont.ttf b/couchpotato/static/fonts/OpenSans-Bold-webfont.ttf new file mode 100755 index 0000000..2d94f06 Binary files /dev/null and b/couchpotato/static/fonts/OpenSans-Bold-webfont.ttf differ diff --git a/couchpotato/static/fonts/OpenSans-Bold-webfont.woff b/couchpotato/static/fonts/OpenSans-Bold-webfont.woff new file mode 100755 index 0000000..cd86852 Binary files /dev/null and b/couchpotato/static/fonts/OpenSans-Bold-webfont.woff differ diff --git a/couchpotato/static/fonts/OpenSans-BoldItalic-webfont.eot b/couchpotato/static/fonts/OpenSans-BoldItalic-webfont.eot new file mode 100755 index 0000000..f44ac9a Binary files /dev/null and b/couchpotato/static/fonts/OpenSans-BoldItalic-webfont.eot differ diff --git a/couchpotato/static/fonts/OpenSans-BoldItalic-webfont.svg b/couchpotato/static/fonts/OpenSans-BoldItalic-webfont.svg new file mode 100755 index 0000000..8392240 --- /dev/null +++ b/couchpotato/static/fonts/OpenSans-BoldItalic-webfont.svg @@ -0,0 +1,146 @@ + + + + +This is a custom SVG webfont generated by Font Squirrel. +Copyright : Digitized data copyright 20102011 Google Corporation +Foundry : Ascender Corporation +Foundry URL : httpwwwascendercorpcom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/couchpotato/static/fonts/OpenSans-BoldItalic-webfont.ttf b/couchpotato/static/fonts/OpenSans-BoldItalic-webfont.ttf new file mode 100755 index 0000000..f74e0e3 Binary files /dev/null and b/couchpotato/static/fonts/OpenSans-BoldItalic-webfont.ttf differ diff --git a/couchpotato/static/fonts/OpenSans-BoldItalic-webfont.woff b/couchpotato/static/fonts/OpenSans-BoldItalic-webfont.woff new file mode 100755 index 0000000..f3248c1 Binary files /dev/null and b/couchpotato/static/fonts/OpenSans-BoldItalic-webfont.woff differ diff --git a/couchpotato/static/fonts/OpenSans-Italic-webfont.eot b/couchpotato/static/fonts/OpenSans-Italic-webfont.eot new file mode 100755 index 0000000..277c189 Binary files /dev/null and b/couchpotato/static/fonts/OpenSans-Italic-webfont.eot differ diff --git a/couchpotato/static/fonts/OpenSans-Italic-webfont.svg b/couchpotato/static/fonts/OpenSans-Italic-webfont.svg new file mode 100755 index 0000000..29c7497 --- /dev/null +++ b/couchpotato/static/fonts/OpenSans-Italic-webfont.svg @@ -0,0 +1,146 @@ + + + + +This is a custom SVG webfont generated by Font Squirrel. +Copyright : Digitized data copyright 20102011 Google Corporation +Foundry : Ascender Corporation +Foundry URL : httpwwwascendercorpcom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/couchpotato/static/fonts/OpenSans-Italic-webfont.ttf b/couchpotato/static/fonts/OpenSans-Italic-webfont.ttf new file mode 100755 index 0000000..63f187e Binary files /dev/null and b/couchpotato/static/fonts/OpenSans-Italic-webfont.ttf differ diff --git a/couchpotato/static/fonts/OpenSans-Italic-webfont.woff b/couchpotato/static/fonts/OpenSans-Italic-webfont.woff new file mode 100755 index 0000000..469a29b Binary files /dev/null and b/couchpotato/static/fonts/OpenSans-Italic-webfont.woff differ diff --git a/couchpotato/static/fonts/OpenSans-Regular-webfont.eot b/couchpotato/static/fonts/OpenSans-Regular-webfont.eot new file mode 100755 index 0000000..dd6fd2c Binary files /dev/null and b/couchpotato/static/fonts/OpenSans-Regular-webfont.eot differ diff --git a/couchpotato/static/fonts/OpenSans-Regular-webfont.svg b/couchpotato/static/fonts/OpenSans-Regular-webfont.svg new file mode 100755 index 0000000..01038bb --- /dev/null +++ b/couchpotato/static/fonts/OpenSans-Regular-webfont.svg @@ -0,0 +1,146 @@ + + + + +This is a custom SVG webfont generated by Font Squirrel. +Copyright : Digitized data copyright 20102011 Google Corporation +Foundry : Ascender Corporation +Foundry URL : httpwwwascendercorpcom + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/couchpotato/static/fonts/OpenSans-Regular-webfont.ttf b/couchpotato/static/fonts/OpenSans-Regular-webfont.ttf new file mode 100755 index 0000000..05951e7 Binary files /dev/null and b/couchpotato/static/fonts/OpenSans-Regular-webfont.ttf differ diff --git a/couchpotato/static/fonts/OpenSans-Regular-webfont.woff b/couchpotato/static/fonts/OpenSans-Regular-webfont.woff new file mode 100755 index 0000000..274664b Binary files /dev/null and b/couchpotato/static/fonts/OpenSans-Regular-webfont.woff differ diff --git a/couchpotato/static/images/sprite.png b/couchpotato/static/images/sprite.png index 782229f..0ef3041 100644 Binary files a/couchpotato/static/images/sprite.png and b/couchpotato/static/images/sprite.png differ diff --git a/couchpotato/static/scripts/block/menu.js b/couchpotato/static/scripts/block/menu.js index 4dc143d..cdd7875 100644 --- a/couchpotato/static/scripts/block/menu.js +++ b/couchpotato/static/scripts/block/menu.js @@ -15,7 +15,7 @@ Block.Menu = new Class({ self.wrapper = new Element('div.wrapper').adopt( self.more_option_ul = new Element('ul') ), - new Element('a.button.onlay', { + new Element('a.button.onlay' + (self.options.button_class ? '.' + self.options.button_class : ''), { 'events': { 'click': function(){ self.el.toggleClass('show') diff --git a/couchpotato/static/scripts/block/navigation.js b/couchpotato/static/scripts/block/navigation.js index 85f20c4..feda379 100644 --- a/couchpotato/static/scripts/block/navigation.js +++ b/couchpotato/static/scripts/block/navigation.js @@ -5,7 +5,17 @@ Block.Navigation = new Class({ create: function(){ var self = this; + var settings_added = false; self.el = new Element('div.navigation').adopt( + self.foldout = new Element('a.foldout.icon2.menu', { + 'events': { + 'click': self.toggleMenu.bind(self) + } + }).grab(new Element('span.overlay')), + self.logo = new Element('a.logo', { + 'text': 'CouchPotato', + 'href': App.createUrl('') + }), self.nav = new Element('ul'), self.backtotop = new Element('a.backtotop', { 'text': 'back to top', @@ -28,19 +38,50 @@ Block.Navigation = new Class({ onEnter: function(){ self.backtotop.fade('in') } + }); + + self.nav.addEvents({ + 'click:relay(a)': function(){ + if($(document.body).getParent().hasClass('menu_shown')) + self.toggleMenu(); + } }) }, addTab: function(name, tab){ - var self = this + var self = this; - return new Element('li.tab_'+(name || 'unknown')).adopt( + return new Element('li.tab_'+(name || 'unknown')).grab( new Element('a', tab) ).inject(self.nav) }, + toggleMenu: function(e){ + var self = this, + body = $(document.body), + html = body.getParent(); + + // Copy over settings menu + if(!self.added){ + + new Element('li.separator').inject(self.nav); + body.getElements('.header .more_menu.menu li a').each(function(el, nr){ + if([0, 1, 2, 5].indexOf(nr) > -1){ + self.nav.grab( + new Element('li').grab(el.clone().cloneEvents(el)) + ); + } + }); + + self.added = true; + } + + html.toggleClass('menu_shown'); + + }, + activate: function(name){ var self = this; diff --git a/couchpotato/static/scripts/couchpotato.js b/couchpotato/static/scripts/couchpotato.js index d2e8fa0..673323b 100644 --- a/couchpotato/static/scripts/couchpotato.js +++ b/couchpotato/static/scripts/couchpotato.js @@ -61,7 +61,7 @@ var CouchPotato = new Class({ 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, {}) + self.block.more = new Block.Menu(self, {'button_class': 'icon2.cog'}) ) ), self.content = new Element('div.content'), @@ -285,23 +285,15 @@ var CouchPotato = new Class({ createUserscriptButtons: function(){ - var userscript = false; - try { - if(Components.interfaces.gmIGreasemonkeyService) - userscript = true - } - catch(e){ - userscript = Browser.chrome === true; - } - var host_url = window.location.protocol + '//' + window.location.host; return new Element('div.group_userscript').adopt( - (userscript ? [new Element('a.userscript.button', { + new Element('a.userscript.button', { 'text': 'Install userscript', 'href': Api.createUrl('userscript.get')+randomString()+'/couchpotato.user.js', - 'target': '_self' - }), new Element('span.or[text=or]')] : null), + 'target': '_blank' + }), + new Element('span.or[text=or]'), new Element('span.bookmarklet').adopt( new Element('a.button.orange', { 'text': '+CouchPotato', diff --git a/couchpotato/static/scripts/library/prefix_free.js b/couchpotato/static/scripts/library/prefix_free.js deleted file mode 100644 index b6d9812..0000000 --- a/couchpotato/static/scripts/library/prefix_free.js +++ /dev/null @@ -1,487 +0,0 @@ -/** - * StyleFix 1.0.3 & PrefixFree 1.0.7 - * @author Lea Verou - * MIT license - */ - -(function(){ - -if(!window.addEventListener) { - return; -} - -var self = window.StyleFix = { - link: function(link) { - try { - // Ignore stylesheets with data-noprefix attribute as well as alternate stylesheets - if(link.rel !== 'stylesheet' || link.hasAttribute('data-noprefix')) { - return; - } - } - catch(e) { - return; - } - - var url = link.href || link.getAttribute('data-href'), - base = url.replace(/[^\/]+$/, ''), - base_scheme = (/^[a-z]{3,10}:/.exec(base) || [''])[0], - base_domain = (/^[a-z]{3,10}:\/\/[^\/]+/.exec(base) || [''])[0], - base_query = /^([^?]*)\??/.exec(url)[1], - parent = link.parentNode, - xhr = new XMLHttpRequest(), - process; - - xhr.onreadystatechange = function() { - if(xhr.readyState === 4) { - process(); - } - }; - - process = function() { - var css = xhr.responseText; - - if(css && link.parentNode && (!xhr.status || xhr.status < 400 || xhr.status > 600)) { - css = self.fix(css, true, link); - - // Convert relative URLs to absolute, if needed - if(base) { - css = css.replace(/url\(\s*?((?:"|')?)(.+?)\1\s*?\)/gi, function($0, quote, url) { - if(/^([a-z]{3,10}:|#)/i.test(url)) { // Absolute & or hash-relative - return $0; - } - else if(/^\/\//.test(url)) { // Scheme-relative - // May contain sequences like /../ and /./ but those DO work - return 'url("' + base_scheme + url + '")'; - } - else if(/^\//.test(url)) { // Domain-relative - return 'url("' + base_domain + url + '")'; - } - else if(/^\?/.test(url)) { // Query-relative - return 'url("' + base_query + url + '")'; - } - else { - // Path-relative - return 'url("' + base + url + '")'; - } - }); - - // behavior URLs shoudn’t be converted (Issue #19) - // base should be escaped before added to RegExp (Issue #81) - var escaped_base = base.replace(/([\\\^\$*+[\]?{}.=!:(|)])/g,"\\$1"); - css = css.replace(RegExp('\\b(behavior:\\s*?url\\(\'?"?)' + escaped_base, 'gi'), '$1'); - } - - var style = document.createElement('style'); - style.textContent = css; - style.media = link.media; - style.disabled = link.disabled; - style.setAttribute('data-href', link.getAttribute('href')); - - parent.insertBefore(style, link); - parent.removeChild(link); - - style.media = link.media; // Duplicate is intentional. See issue #31 - } - }; - - try { - xhr.open('GET', url); - xhr.send(null); - } catch (e) { - // Fallback to XDomainRequest if available - if (typeof XDomainRequest != "undefined") { - xhr = new XDomainRequest(); - xhr.onerror = xhr.onprogress = function() {}; - xhr.onload = process; - xhr.open("GET", url); - xhr.send(null); - } - } - - link.setAttribute('data-inprogress', ''); - }, - - styleElement: function(style) { - if (style.hasAttribute('data-noprefix')) { - return; - } - var disabled = style.disabled; - - style.textContent = self.fix(style.textContent, true, style); - - style.disabled = disabled; - }, - - styleAttribute: function(element) { - var css = element.getAttribute('style'); - - css = self.fix(css, false, element); - - element.setAttribute('style', css); - }, - - process: function() { - // Linked stylesheets - $('link[rel="stylesheet"]:not([data-inprogress])').forEach(StyleFix.link); - - // Inline stylesheets - $('style').forEach(StyleFix.styleElement); - - // Inline styles - $('[style]').forEach(StyleFix.styleAttribute); - }, - - register: function(fixer, index) { - (self.fixers = self.fixers || []) - .splice(index === undefined? self.fixers.length : index, 0, fixer); - }, - - fix: function(css, raw, element) { - for(var i=0; i -1) { - // Gradients are supported with a prefix, convert angles to legacy - css = css.replace(/(\s|:|,)(repeating-)?linear-gradient\(\s*(-?\d*\.?\d*)deg/ig, function ($0, delim, repeating, deg) { - return delim + (repeating || '') + 'linear-gradient(' + (90-deg) + 'deg'; - }); - } - - css = fix('functions', '(\\s|:|,)', '\\s*\\(', '$1' + prefix + '$2(', css); - css = fix('keywords', '(\\s|:)', '(\\s|;|\\}|$)', '$1' + prefix + '$2$3', css); - css = fix('properties', '(^|\\{|\\s|;)', '\\s*:', '$1' + prefix + '$2:', css); - - // Prefix properties *inside* values (issue #8) - if (self.properties.length) { - var regex = RegExp('\\b(' + self.properties.join('|') + ')(?!:)', 'gi'); - - css = fix('valueProperties', '\\b', ':(.+?);', function($0) { - return $0.replace(regex, prefix + "$1") - }, css); - } - - if(raw) { - css = fix('selectors', '', '\\b', self.prefixSelector, css); - css = fix('atrules', '@', '\\b', '@' + prefix + '$1', css); - } - - // Fix double prefixing - css = css.replace(RegExp('-' + prefix, 'g'), '-'); - - // Prefix wildcard - css = css.replace(/-\*-(?=[a-z]+)/gi, self.prefix); - - return css; - }, - - property: function(property) { - return (self.properties.indexOf(property)? self.prefix : '') + property; - }, - - value: function(value, property) { - value = fix('functions', '(^|\\s|,)', '\\s*\\(', '$1' + self.prefix + '$2(', value); - value = fix('keywords', '(^|\\s)', '(\\s|$)', '$1' + self.prefix + '$2$3', value); - - // TODO properties inside values - - return value; - }, - - // Warning: Prefixes no matter what, even if the selector is supported prefix-less - prefixSelector: function(selector) { - return selector.replace(/^:{1,2}/, function($0) { return $0 + self.prefix }) - }, - - // Warning: Prefixes no matter what, even if the property is supported prefix-less - prefixProperty: function(property, camelCase) { - var prefixed = self.prefix + property; - - return camelCase? StyleFix.camelCase(prefixed) : prefixed; - } -}; - -/************************************** - * Properties - **************************************/ -(function() { - var prefixes = {}, - properties = [], - shorthands = {}, - style = getComputedStyle(document.documentElement, null), - dummy = document.createElement('div').style; - - // Why are we doing this instead of iterating over properties in a .style object? Cause Webkit won't iterate over those. - var iterate = function(property) { - if(property.charAt(0) === '-') { - properties.push(property); - - var parts = property.split('-'), - prefix = parts[1]; - - // Count prefix uses - prefixes[prefix] = ++prefixes[prefix] || 1; - - // This helps determining shorthands - while(parts.length > 3) { - parts.pop(); - - var shorthand = parts.join('-'); - - if(supported(shorthand) && properties.indexOf(shorthand) === -1) { - properties.push(shorthand); - } - } - } - }, - supported = function(property) { - return StyleFix.camelCase(property) in dummy; - } - - // Some browsers have numerical indices for the properties, some don't - if(style.length > 0) { - for(var i=0; i height){ + lowest = height; + if(timer) clearTimeout(timer); + timer = (function(){ + images.getParent().setStyle('height', lowest); + }).delay(300) + } + }); + + $(window).addEvent('resize', function(){ + if(timer) clearTimeout(timer); + timer = (function(){ + var lowest; + images.each(function(img){ + var height = img.getSize().y; + if(!lowest || lowest > height) + lowest = height; + }); + images.getParent().setStyle('height', lowest); + }).delay(300); + }); + }); + // Still not available self.late_list = new MovieList({ 'navigation': false, diff --git a/couchpotato/static/scripts/page/wanted.js b/couchpotato/static/scripts/page/wanted.js index 6e32997..eabd146 100644 --- a/couchpotato/static/scripts/page/wanted.js +++ b/couchpotato/static/scripts/page/wanted.js @@ -30,7 +30,7 @@ Page.Wanted = new Class({ $(self.wanted).inject(self.el); // Check if search is in progress - self.startProgressInterval(); + self.startProgressInterval.delay(4000, self); } }, diff --git a/couchpotato/static/style/main.css b/couchpotato/static/style/main.css index 91bd152..02dda3d 100644 --- a/couchpotato/static/style/main.css +++ b/couchpotato/static/style/main.css @@ -1,19 +1,14 @@ -html { +body, html { color: #fff; font-size: 12px; line-height: 1.5; - font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif; + font-family: OpenSans, "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif; height: 100%; - text-shadow: 0 1px 0 #000; -} - -body { margin: 0; padding: 0; background: #4e5969; - overflow-y: scroll; - height: 100%; } + body { overflow-y: scroll; } body.noscroll { overflow: hidden; } #clean { @@ -32,14 +27,16 @@ pre { } input, textarea { - font-size: 12px; + font-size: 1em; font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif; } +input:focus, textarea:focus { + outline: none; +} input:-moz-placeholder, textarea:-moz-placeholder { color: rgba(255, 255, 255, 0.6); } - ::-webkit-input-placeholder, ::-webkit-textarea-placeholder { color: rgba(255, 255, 255, 0.6); } @@ -59,55 +56,32 @@ a:hover { color: #f3f3f3; } .page { display: none; - width: 960px; + width: 100%; + max-width: 980px; margin: 0 auto; - line-height: 24px; - padding: 0 0 20px; + line-height: 1.5em; + padding: 0 15px 20px; } .page.active { display: block; } - .page .noticeMe { - background-color: lightgoldenrodyellow; - display: block; - padding: 20px 10px; - margin: 0 -10px 40px; - font-size: 19px; - text-align: center; - } - .content { clear:both; - padding: 80px 0 10px; + padding: 65px 0 10px; + background: #4e5969; } + @media all and (max-width: 480px) { + .content { + padding-top: 40px; + } + } + h2 { - font-size: 30px; + font-size: 2.5em; padding: 0; margin: 20px 0 0 0; } -.footer { - text-align:center; - padding: 50px 0 0 0; - color: #999; - font-size: 10px; - clear: both; -} - - .footer .check { - color: #333; - } - -#toTop { - background: black; - position: fixed; - bottom: 0; - right: 0; - padding: 10px 10px 10px 40px; - background: #f7f7f7 url('../images/toTop.gif') no-repeat 10px center; - border-radius: 5px 0 0 0; -} - form { padding:0; margin:0; @@ -126,6 +100,12 @@ body > .spinner, .mask{ width: 100%; padding: 200px; } + + @media all and (max-width: 480px) { + body > .mask { + padding: 20px; + } + } .button { background: #5082bc url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAyCAYAAACd+7GKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAClJREFUeNpi/v//vwMTAwPDfzjBgMpFI/7hFSOT9Y8qRuF3JLoHAQIMAHYtMmRA+CugAAAAAElFTkSuQmCC") repeat-x; @@ -135,8 +115,6 @@ body > .spinner, .mask{ font-weight: bold; line-height: 1; border-radius: 2px; - box-shadow: 0 1px 2px rgba(0,0,0,0.3); - text-shadow: 0 -1px 1px rgba(0,0,0,0.25); cursor: pointer; } .button.red { background-color: #ff0000; } @@ -164,132 +142,302 @@ body > .spinner, .mask{ .icon.spinner { background-image: url('../images/icon.spinner.gif'); } .icon.attention { background-image: url('../images/icon.attention.png'); } +.icon2 { + display: inline-block; + background: center no-repeat; + font-family: 'Elusive-Icons'; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + -webkit-font-smoothing: antialiased; + font-size: 15px; +} + +.icon2.cog:before { content: "\e109"; } +.icon2.eye-open:before { content: "\e09d"; } +.icon2.search:before { content: "\e03e"; } +.icon2.return-key:before { content: "\e111"; } +.icon2.close:before { content: "\e04e"; } +.icon2.menu:before { + content: "\e076 \e076 \e076"; + line-height: 6px; + transform: scaleX(2); + width: 20px; + font-size: 10px; + display: inline-block; + vertical-align: middle; +} + /*** Navigation ***/ .header { - background: #4e5969; - padding: 10px 0; - height: 80px; + height: 66px; position: fixed; margin: 0; width: 100%; z-index: 5; - box-shadow: 0 20px 30px -30px rgba(0,0,0,0.05); - transition: box-shadow .4s cubic-bezier(0.9,0,0.1,1); + background: #4e5969; + box-shadow: 0 0 10px rgba(0,0,0,.1); + transition: all .4s ease-in-out; } .header.with_shadow { - box-shadow: 0 20px 30px -30px rgba(0,0,0,0.3); + background-color: #46505e; + } + + @media all and (max-width: 480px) { + .header { + height: 44px; + } } .header > div { - width: 960px; + width: 100%; + max-width: 980px; margin: 0 auto; - overflow: hidden; + position: relative; + height: 100%; + padding: 0 15px; } + .header .navigation { display: inline-block; vertical-align: middle; - width: 67.2%; - } - .header .navigation ul { - margin: 0; - padding: 0; - } - .header .navigation li { - color: #fff; - display: inline-block; - font-size:20px; - font-weight: bold; - margin: 0; - text-align: center; + position: absolute; + height: 100%; + left: 0; + bottom: 0; } - - .header .navigation li a { - display: block; - padding: 15px; - position: relative; + + .header .foldout { + width: 44px; + height: 100%; + text-align: center; + border-right: 1px solid rgba(255,255,255,.07); + display: none; + vertical-align: top; + line-height: 42px; + color: #FFF; } - .header .navigation li:first-child a { padding-left: 10px; } - .header .navigation li span { - display: block; - margin-top: 5px; - } - .header .navigation li a:after { - content: ''; + .header .logo { display: inline-block; - height: 2px; - width: 76%; - left: 12%; - position: absolute; - top: 46px; - background-color: #46505e; - outline: none; - box-shadow: inset 0 1px 8px rgba(0,0,0,0.05), 0 1px 0px rgba(255,255,255,0.15); - transition: all .4s cubic-bezier(0.9,0,0.1,1); + font-size: 1.75em; + padding: 15px 30px 0 15px; + height: 100%; + vertical-align: middle; + border-right: 1px solid rgba(255,255,255,.07); + color: #FFF; + font-weight: normal; + vertical-align: top; } - .header .navigation li:hover a:after { background-color: #047792; } - .header .navigation li.active a:after { background-color: #04bce6; } - - .header .navigation li.disabled { color: #e5e5e5; } - .header .navigation li a { color: #fff; } - - .header .navigation .backtotop { - opacity: 0; - display: block; - width: 80px; - left: 50%; - position: absolute; - text-align: center; - margin: -10px 0 0 -40px; - background: #4e5969; - padding: 5px 0; - border-radius: 0 0 5px 5px; - color: rgba(255,255,255,.4); - text-shadow: none; - font-weight: normal; + @media all and (max-width: 480px) { + .header .foldout { + display: inline-block; + } + + .header .logo { + padding-top: 7px; + border: 0; + } + } + + @media all and (min-width: 481px) and (max-width: 640px) { + + .header .logo { + display: none; + } + + } + + .header .navigation ul { + display: inline-block; + margin: 0; + padding: 0; + height: 100%; + } + + .header .navigation li { + color: #fff; + display: inline-block; + font-size: 1.75em; + margin: 0; + text-align: center; + height: 100%; + border: 1px solid rgba(255,255,255,.07); + border-width: 0 0 0 1px; + } + .header .navigation li:first-child { + border: none; + } + + .header .navigation li a { + display: block; + padding: 15px; + position: relative; + height: 100%; + border: 1px solid transparent; + border-width: 0 0 4px 0; + font-weight: normal; + } + + .header .navigation li:hover a { border-color: #047792; } + .header .navigation li.active a { border-color: #04bce6; } + + .header .navigation li.disabled { color: #e5e5e5; } + .header .navigation li a { color: #fff; } + + .header .navigation .backtotop { + opacity: 0; + display: block; + width: 80px; + left: 50%; + position: fixed; + bottom: 0; + text-align: center; + margin: -10px 0 0 -40px; + background: #4e5969; + padding: 5px 0; + color: rgba(255,255,255,.4); + font-weight: normal; + } + .header:hover .navigation .backtotop { color: #fff; } + + @media all and (max-width: 480px) { + + body { + position: absolute; + width: 100%; + transition: all .5s cubic-bezier(0.9,0,0.1,1); + left: 0; + } + + .menu_shown body { + left: 160px; + } + + .header .navigation { + height: 100%; + } + + .menu_shown .header .navigation .overlay { + position: fixed; + right: 0; + top: 0; + bottom: 0; + left: 160px; + } + + .header .navigation ul { + width: 160px; + position: fixed; + left: -160px; + background: rgba(0,0,0,.5); + transition: all .5s cubic-bezier(0.9,0,0.1,1); + } + + .menu_shown .header .navigation ul { + left: 0; + } + + .header .navigation ul li { + display: block; + text-align: left; + border-width: 1px 0 0 0; + height: 44px; + } + .header .navigation ul li a { + border-width: 0 4px 0 0; + padding: 5px 20px; + } + + .header .navigation ul li.separator { + background-color: rgba(255,255,255, .07); + height: 5px; + } } - .header:hover .navigation .backtotop { color: #fff; } .header .more_menu { - margin-left: 12px; + position: absolute; + right: 15px; + height: 100%; + border-left: 1px solid rgba(255,255,255,.07); } + + @media all and (max-width: 480px) { + .header .more_menu { + display: none; + } + } + + .header .more_menu .button { + height: 100%; + width: 44px; + border: 0; + box-shadow: none; + border-radius: 0; + background: none; + line-height: 66px; + text-align: center; + padding: 0; + border: 1px solid transparent; + border-width: 0 0 4px; + } + .header .more_menu .button:hover { + background: none; + border-color: #047792; + } + .header .more_menu .wrapper { width: 150px; - margin-left: -110px; - } - .header .more_menu .wrapper:before { - margin-left: -34px; + margin-left: -106px; + margin-top: 66px; } + + @media all and (max-width: 480px) { + .header .more_menu .button { + line-height: 44px; + } + + .header .more_menu .wrapper { + margin-top: 44px; + } + } .header .more_menu .red { color: red; } .header .more_menu .orange { color: orange; } .badge { position: absolute; - width: 14px; - height: 14px; + width: 20px; + height: 20px; text-align: center; - line-height: 14px; - border-radius: 50%; - font-size: 8px; - margin: -5px 0 0 15px; - box-shadow: inset 0 1px 0 rgba(255,255,255,.6), 0 0 3px rgba(0,0,0,.7); + line-height: 20px; + margin: 0; background-color: #1b79b8; - text-shadow: none; - background-image: -*-linear-gradient(0deg, rgba(255,255,255,.3) 0%, rgba(255,255,255,.1) 100%); + top: 0; + right: 0; } + + .header .notification_menu { + right: 60px; + display: block; + } + + @media all and (max-width: 480px) { + .header .notification_menu { + right: 0; + } + } .header .notification_menu .wrapper { width: 300px; - margin-left: -260px; + margin-left: -255px; text-align: left; } - .header .notification_menu .wrapper:before { - left: 296px; - } - .header .notification_menu ul { max-height: 300px; overflow: auto; @@ -309,7 +457,7 @@ body > .spinner, .mask{ .header .notification_menu li:last-child > span { border: 0; } .header .notification_menu li .added { display: block; - font-size: 10px; + font-size: .85em; color: #aaa; text-align: ; } @@ -318,25 +466,27 @@ body > .spinner, .mask{ text-align: center; } - .header .message.update { + .message.update { text-align: center; - position: relative; - top: -70px; - padding: 2px 0; + position: fixed; + padding: 10px; background: #ff6134; - font-size: 12px; - border-radius: 0 0 5px 5px; - box-shadow: 0 2px 1px rgba(0,0,0, 0.3); + font-size: 15px; + bottom: 0; + left: 0; + width: 100%; + z-index: 19; } - .header .message a { - padding: 0 10px; + .message.update a { + padding: 0 5px; } /*** Global Styles ***/ .check { display: inline-block; - vertical-align: middle; + vertical-align: top; + margin-top: 4px; height: 16px; width: 16px; cursor: pointer; @@ -361,8 +511,8 @@ body > .spinner, .mask{ border-radius:30px; box-shadow: 0 1px 1px rgba(0,0,0,0.35), inset 0 1px 0px rgba(255,255,255,0.20); - background: url('../images/sprite.png') no-repeat 94% -53px, -*-linear-gradient( - 270deg, + background: url('../images/sprite.png') no-repeat 94% -53px, linear-gradient( + 180deg, #5b9bd1 0%, #406db8 100% ); @@ -437,8 +587,8 @@ body > .spinner, .mask{ border: 1px solid #252930; box-shadow: inset 0 1px 0px rgba(255,255,255,0.20), 0 0 3px rgba(0,0,0, 0.2); background: rgb(55,62,74); - background-image: -*-linear-gradient( - 90deg, + background-image: linear-gradient( + 0, rgb(55,62,74) 0%, rgb(73,83,98) 100% ); @@ -454,13 +604,9 @@ body > .spinner, .mask{ display: block; width: 600px; padding: 20px; - background: #f5f5f5; position:fixed; - z-index:101; + z-index: 101; text-align: center; - background: #5c697b; - border-radius: 3px; - box-shadow: 0 0 50px rgba(0,0,0,0.55); } .question h3 { @@ -472,7 +618,6 @@ body > .spinner, .mask{ .question .hint { font-size: 14px; color: #ccc; - text-shadow: none; } .question .answer { @@ -495,10 +640,11 @@ body > .spinner, .mask{ background-color: #4c5766; } - .more_menu { - display: inline-block; - vertical-align: middle; - } +.more_menu { + display: inline-block; + vertical-align: middle; + overflow: visible; +} .more_menu > a { display: block; @@ -508,57 +654,45 @@ body > .spinner, .mask{ border: 1px solid rgba(0,0,0,0.3); transition: all 0.3s ease-in-out; } - .more_menu.show > a:not(:active), .more_menu > a:hover:not(:active) { - background-color: #406db8; - } - + .more_menu.show > a:not(:active), .more_menu > a:hover:not(:active) { + background-color: #406db8; + } + .more_menu .wrapper { display: none; - border: 1px solid #333; background: rgba(255,255,255,0.98); - border-radius: 3px; - padding: 4px !important; + top: 0; + right: 0; + padding: 4px; + margin: 26px 0 0 0; position: absolute; - z-index: 9; - margin: 32px 0 0 -145px; + z-index: 90; width: 185px; - box-shadow: 0 10px 10px -5px rgba(0,0,0,0.4); + box-shadow: 0 20px 20px -5px rgba(0,0,0,0.1); text-align: center; color: #000; - text-shadow: none; - background-image: -*-linear-gradient( - 45deg, + background-image: linear-gradient( + -45deg, rgb(200,200,200) 0%, rgb(255,255,255) 100% ); } - - .more_menu .wrapper:before { - content: ' '; - height: 0; - position: relative; - width: 0; - border: 6px solid transparent; - border-bottom-color: #fff; - display: block; - top: -16px; - left: 146px; - } + .more_menu.show .wrapper { display: block; } - + .more_menu ul { padding: 0; - margin: -12px 0 0 0; + margin: 0; list-style: none; } - + .more_menu .wrapper li { width: 100%; height: auto; } - + .more_menu .wrapper li a { display: block; border-bottom: 1px solid rgba(255,255,255,0.2); @@ -570,7 +704,7 @@ body > .spinner, .mask{ padding: 3px 0; color: #000; } - + .more_menu .wrapper li:last-child a { border: none; } @@ -582,41 +716,109 @@ body > .spinner, .mask{ position: fixed; right: 0; bottom: 0; - padding: 2px; - width: 240px; + width: 320px; z-index: 20; overflow: hidden; font-size: 14px; font-weight: bold; } + @media all and (max-width: 480px) { + .messages { + width: 100%; + } + } .messages .message { - text-align: center; - border-radius: 2px; - margin: 2px 0 0 0; - height: 0; overflow: hidden; transition: all .6s cubic-bezier(0.9,0,0.1,1); - box-shadow: 0 1px 1px rgba(0,0,0,0.35), inset 0 1px 0px rgba(255,255,255,0.20); - background-image: -*-linear-gradient( - 270deg, - #5b9bd1 0%, - #406db8 100% - ); + background: #5b9bd1; width: 100%; - padding: 0 5px; - visibility: hidden; + position: relative; + margin: 1px 0 0; max-height: 0; + padding: 0 30px 0 20px; + font-size: 1.1em; + font-weight: normal; + transform: scale(0); } + .messages .message.sticky { + background-color: #c84040; + } .messages .message.show { - visibility: visible; - height: auto; - padding-top: 3px; - padding-bottom: 3px; - min-height: 1px; - max-height: 400px; + max-height: 100px; + padding: 15px 30px 15px 20px; + transform: scale(1); } .messages .message.hide { - margin-left: 240px; - opacity: 0; - } \ No newline at end of file + max-height: 0; + padding: 0 20px; + margin: 0; + transform: scale(0); + } + .messages .close { + position: absolute; + padding: 10px 8px; + top: 0; + right: 0; + color: #FFF; + } + +/* Fonts */ +@font-face { + font-family: 'Elusive-Icons'; + src:url('../fonts/Elusive-Icons.eot'); + src:url('../fonts/Elusive-Icons.eot?#iefix') format('embedded-opentype'), + url('../fonts/Elusive-Icons.woff') format('woff'), + url('../fonts/Elusive-Icons.ttf') format('truetype'), + url('../fonts/Elusive-Icons.svg#Elusive-Icons') format('svg'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'OpenSans'; + src: url('../fonts/OpenSans-Regular-webfont.eot'); + src: url('../fonts/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'), + url('../fonts/OpenSans-Regular-webfont.woff') format('woff'), + url('../fonts/OpenSans-Regular-webfont.ttf') format('truetype'), + url('../fonts/OpenSans-Regular-webfont.svg#OpenSansRegular') format('svg'); + font-weight: normal; + font-style: normal; + +} + +@font-face { + font-family: 'OpenSans'; + src: url('../fonts/OpenSans-Italic-webfont.eot'); + src: url('../fonts/OpenSans-Italic-webfont.eot?#iefix') format('embedded-opentype'), + url('../fonts/OpenSans-Italic-webfont.woff') format('woff'), + url('../fonts/OpenSans-Italic-webfont.ttf') format('truetype'), + url('../fonts/OpenSans-Italic-webfont.svg#OpenSansItalic') format('svg'); + font-weight: normal; + font-style: italic; + +} + +@font-face { + font-family: 'OpenSans'; + src: url('../fonts/OpenSans-Bold-webfont.eot'); + src: url('../fonts/OpenSans-Bold-webfont.eot?#iefix') format('embedded-opentype'), + url('../fonts/OpenSans-Bold-webfont.woff') format('woff'), + url('../fonts/OpenSans-Bold-webfont.ttf') format('truetype'), + url('../fonts/OpenSans-Bold-webfont.svg#OpenSansBold') format('svg'); + font-weight: bold; + font-style: normal; + +} + +@font-face { + font-family: 'OpenSans'; + src: url('../fonts/OpenSans-BoldItalic-webfont.eot'); + src: url('../fonts/OpenSans-BoldItalic-webfont.eot?#iefix') format('embedded-opentype'), + url('../fonts/OpenSans-BoldItalic-webfont.woff') format('woff'), + url('../fonts/OpenSans-BoldItalic-webfont.ttf') format('truetype'), + url('../fonts/OpenSans-BoldItalic-webfont.svg#OpenSansBoldItalic') format('svg'); + font-weight: bold; + font-style: italic; + +} \ No newline at end of file diff --git a/couchpotato/static/style/settings.css b/couchpotato/static/style/settings.css index a5655f8..bc1b19f 100644 --- a/couchpotato/static/style/settings.css +++ b/couchpotato/static/style/settings.css @@ -1,5 +1,9 @@ +.page.settings { + min-width: 960px; +} + .page.settings:after { - content: "."; + content: ""; display: block; clear: both; visibility: hidden; @@ -9,15 +13,15 @@ .page.settings .tabs { float: left; - width: 20%; - font-size: 20px; + width: 14.7%; + font-size: 17px; text-align: right; list-style: none; - padding: 40px 0; + padding: 35px 0; margin: 0; min-height: 470px; - background-image: -*-linear-gradient( - 20deg, + background-image: linear-gradient( + 76deg, rgba(0,0,0,0) 50%, rgba(0,0,0,0.3) 100% ); @@ -60,9 +64,9 @@ .page.settings .containers { - width: 80%; + width: 84%; float: left; - padding: 20px 2%; + padding: 40px 2%; min-height: 300px; } @@ -135,6 +139,10 @@ padding-left: 2%; line-height: 14px; } + + .page .check { + margin-top: 6px; + } .page .check + .formHint { float: none; @@ -163,6 +171,10 @@ margin-bottom: 20px; } + .page .option_list .check { + margin-top: 5px; + } + .page .option_list .enabler { padding: 0; margin-left: 5px !important; @@ -240,8 +252,11 @@ display: block; text-align: right; height: 20px; - margin: 0; + margin: 0 0 -37px; } + .page .advanced_toggle .check { + margin: 0; + } .page .advanced_toggle span { padding: 0 5px; } .page.show_advanced .advanced_toggle { color: #edc07f; @@ -439,16 +454,16 @@ border-radius: 2px; } .page .tag_input > ul:hover > li.choice { - background: -*-linear-gradient( - 270deg, + background: linear-gradient( + 180deg, rgba(255,255,255,0.3) 0%, rgba(255,255,255,0.1) 100% ); } .page .tag_input > ul > li.choice:hover, .page .tag_input > ul > li.choice.selected { - background: -*-linear-gradient( - 270deg, + background: linear-gradient( + 180deg, #5b9bd1 0%, #406db8 100% ); @@ -486,8 +501,8 @@ margin: -9px 0 0 -16px; border-radius: 30px 30px 0 0; cursor: pointer; - background: url('../images/icon.delete.png') no-repeat center 2px, -*-linear-gradient( - 270deg, + background: url('../images/icon.delete.png') no-repeat center 2px, linear-gradient( + 180deg, #5b9bd1 0%, #5b9bd1 100% ); @@ -540,6 +555,9 @@ .page .combined_table .ctrlHolder > * { margin: 0 10px 0 0; } + .page .combined_table .ctrlHolder > .check { + margin-top: 6px; + } .page .combined_table .ctrlHolder .delete { display: none; diff --git a/couchpotato/templates/index.html b/couchpotato/templates/index.html index 1d61806..cf666a6 100644 --- a/couchpotato/templates/index.html +++ b/couchpotato/templates/index.html @@ -1,6 +1,9 @@ + + + {% for url in fireEvent('clientscript.get_styles', as_html = True, location = 'front', single = True) %} {% endfor %} {% for url in fireEvent('clientscript.get_scripts', as_html = True, location = 'front', single = True) %} @@ -17,6 +20,17 @@