diff --git a/CHANGES.md b/CHANGES.md index c82dfcdd..4e19ca0f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -68,6 +68,10 @@ * Add cleansing of text used in the processes to a add a show * Add sorting of AniDB available group results * Add error handling and related UI feedback to reflect result of AniDB communications +* Change replace HTTP auth with a login page +* Change to improve webserve code +* Add logout menu item with confirmation +* Add 404 error page [develop changelog] * Change uT params from unicode to str.format as magnet URLs worked but sending files in POST bodies failed diff --git a/autoProcessTV/autoProcessTV.py b/autoProcessTV/autoProcessTV.py index 0092f9cf..95e26623 100755 --- a/autoProcessTV/autoProcessTV.py +++ b/autoProcessTV/autoProcessTV.py @@ -23,6 +23,17 @@ from __future__ import with_statement import os.path import sys +sickbeardPath = os.path.split(os.path.split(sys.argv[0])[0])[0] +sys.path.append(os.path.join(sickbeardPath, 'lib')) +sys.path.append(sickbeardPath) + +try: + import requests +except ImportError: + print ('You need to install python requests library') + sys.exit(1) + + # Try importing Python 2 modules using new names try: import ConfigParser as configparser @@ -35,77 +46,63 @@ except ImportError: import urllib.request as urllib2 from urllib.parse import urlencode -# workaround for broken urllib2 in python 2.6.5: wrong credentials lead to an infinite recursion -if sys.version_info >= (2, 6, 5) and sys.version_info < (2, 6, 6): - class HTTPBasicAuthHandler(urllib2.HTTPBasicAuthHandler): - def retry_http_basic_auth(self, host, req, realm): - # don't retry if auth failed - if req.get_header(self.auth_header, None) is not None: - return None - - return urllib2.HTTPBasicAuthHandler.retry_http_basic_auth(self, host, req, realm) - -else: - HTTPBasicAuthHandler = urllib2.HTTPBasicAuthHandler - - def processEpisode(dir_to_process, org_NZB_name=None, status=None): # Default values - host = "localhost" - port = "8081" - username = "" - password = "" + host = 'localhost' + port = '8081' + username = '' + password = '' ssl = 0 - web_root = "/" + web_root = '/' - default_url = host + ":" + port + web_root + default_url = host + ':' + port + web_root if ssl: - default_url = "https://" + default_url + default_url = 'https://' + default_url else: - default_url = "http://" + default_url + default_url = 'http://' + default_url # Get values from config_file config = configparser.RawConfigParser() - config_filename = os.path.join(os.path.dirname(sys.argv[0]), "autoProcessTV.cfg") + config_filename = os.path.join(os.path.dirname(sys.argv[0]), 'autoProcessTV.cfg') if not os.path.isfile(config_filename): - print ("ERROR: " + config_filename + " doesn\'t exist") - print ("copy /rename " + config_filename + ".sample and edit\n") - print ("Trying default url: " + default_url + "\n") + print ('ERROR: ' + config_filename + " doesn't exist") + print ('copy /rename ' + config_filename + '.sample and edit\n') + print ('Trying default url: ' + default_url + '\n') else: try: - print ("Loading config from " + config_filename + "\n") + print ('Loading config from ' + config_filename + '\n') - with open(config_filename, "r") as fp: + with open(config_filename, 'r') as fp: config.readfp(fp) # Replace default values with config_file values - host = config.get("SickBeard", "host") - port = config.get("SickBeard", "port") - username = config.get("SickBeard", "username") - password = config.get("SickBeard", "password") + host = config.get('SickBeard', 'host') + port = config.get('SickBeard', 'port') + username = config.get('SickBeard', 'username') + password = config.get('SickBeard', 'password') try: - ssl = int(config.get("SickBeard", "ssl")) + ssl = int(config.get('SickBeard', 'ssl')) except (configparser.NoOptionError, ValueError): pass try: - web_root = config.get("SickBeard", "web_root") - if not web_root.startswith("/"): - web_root = "/" + web_root + web_root = config.get('SickBeard', 'web_root') + if not web_root.startswith('/'): + web_root = '/' + web_root - if not web_root.endswith("/"): - web_root = web_root + "/" + if not web_root.endswith('/'): + web_root = web_root + '/' except configparser.NoOptionError: pass except EnvironmentError: e = sys.exc_info()[1] - print ("Could not read configuration file: " + str(e)) + print ('Could not read configuration file: ' + str(e)) # There was a config_file, don't use default values but exit sys.exit(1) @@ -121,34 +118,33 @@ def processEpisode(dir_to_process, org_NZB_name=None, status=None): params['failed'] = status if ssl: - protocol = "https://" + protocol = 'https://' else: - protocol = "http://" + protocol = 'http://' - url = protocol + host + ":" + port + web_root + "home/postprocess/processEpisode?" + urlencode(params) + url = protocol + host + ':' + port + web_root + 'home/postprocess/processEpisode' + login_url = protocol + host + ':' + port + web_root + 'login' - print ("Opening URL: " + url) + print ('Opening URL: ' + url) try: - password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() - password_mgr.add_password(None, url, username, password) - handler = HTTPBasicAuthHandler(password_mgr) - opener = urllib2.build_opener(handler) - urllib2.install_opener(opener) - - result = opener.open(url).readlines() - - for line in result: - if line: - print (line.strip()) + sess = requests.Session() + sess.post(login_url, data={'username': username, 'password': password}, stream=True, verify=False) + result = sess.get(url, params=params, stream=True, verify=False) + if result.status_code == 401: + print 'Verify and use correct username and password in autoProcessTV.cfg' + else: + for line in result.iter_lines(): + if line: + print (line.strip()) except IOError: e = sys.exc_info()[1] - print ("Unable to open URL: " + str(e)) + print ('Unable to open URL: ' + str(e)) sys.exit(1) -if __name__ == "__main__": - print ("This module is supposed to be used as import in other scripts and not run standalone.") - print ("Use sabToSickBeard instead.") +if __name__ == '__main__': + print ('This module is supposed to be used as import in other scripts and not run standalone.') + print ('Use sabToSickBeard instead.') sys.exit(1) \ No newline at end of file diff --git a/autoProcessTV/mediaToSickbeard.py b/autoProcessTV/mediaToSickbeard.py index 12ee3e69..1329424e 100755 --- a/autoProcessTV/mediaToSickbeard.py +++ b/autoProcessTV/mediaToSickbeard.py @@ -6,20 +6,24 @@ import ConfigParser import logging sickbeardPath = os.path.split(os.path.split(sys.argv[0])[0])[0] -sys.path.append(os.path.join( sickbeardPath, 'lib')) +sys.path.append(os.path.join(sickbeardPath, 'lib')) sys.path.append(sickbeardPath) -configFilename = os.path.join(sickbeardPath, "config.ini") +configFilename = os.path.join(sickbeardPath, 'config.ini') -import requests +try: + import requests +except ImportError: + print ('You need to install python requests library') + sys.exit(1) config = ConfigParser.ConfigParser() try: - fp = open(configFilename, "r") + fp = open(configFilename, 'r') config.readfp(fp) fp.close() except IOError, e: - print "Could not find/read Sickbeard config.ini: " + str(e) + print 'Could not find/read Sickbeard config.ini: ' + str(e) print 'Possibly wrong mediaToSickbeard.py location. Ensure the file is in the autoProcessTV subdir of your Sickbeard installation' time.sleep(3) sys.exit(1) @@ -28,7 +32,7 @@ scriptlogger = logging.getLogger('mediaToSickbeard') formatter = logging.Formatter('%(asctime)s %(levelname)-8s MEDIATOSICKBEARD :: %(message)s', '%b-%d %H:%M:%S') # Get the log dir setting from SB config -logdirsetting = config.get("General", "log_dir") if config.get("General", "log_dir") else 'Logs' +logdirsetting = config.get('General', 'log_dir') if config.get('General', 'log_dir') else 'Logs' # put the log dir inside the SickBeard dir, unless an absolute path logdir = os.path.normpath(os.path.join(sickbeardPath, logdirsetting)) logfile = os.path.join(logdir, 'sickbeard.log') @@ -49,7 +53,7 @@ def utorrent(): # print 'Calling utorrent' if len(sys.argv) < 2: scriptlogger.error('No folder supplied - is this being called from uTorrent?') - print "No folder supplied - is this being called from uTorrent?" + print 'No folder supplied - is this being called from uTorrent?' time.sleep(3) sys.exit() @@ -69,7 +73,7 @@ def deluge(): if len(sys.argv) < 4: scriptlogger.error('No folder supplied - is this being called from Deluge?') - print "No folder supplied - is this being called from Deluge?" + print 'No folder supplied - is this being called from Deluge?' time.sleep(3) sys.exit() @@ -82,7 +86,7 @@ def blackhole(): if None != os.getenv('TR_TORRENT_DIR'): scriptlogger.debug('Processing script triggered by Transmission') - print "Processing script triggered by Transmission" + print 'Processing script triggered by Transmission' scriptlogger.debug(u'TR_TORRENT_DIR: ' + os.getenv('TR_TORRENT_DIR')) scriptlogger.debug(u'TR_TORRENT_NAME: ' + os.getenv('TR_TORRENT_NAME')) dirName = os.getenv('TR_TORRENT_DIR') @@ -90,7 +94,7 @@ def blackhole(): else: if len(sys.argv) < 2: scriptlogger.error('No folder supplied - Your client should invoke the script with a Dir and a Relese Name') - print "No folder supplied - Your client should invoke the script with a Dir and a Relese Name" + print 'No folder supplied - Your client should invoke the script with a Dir and a Release Name' time.sleep(3) sys.exit() @@ -99,50 +103,27 @@ def blackhole(): return (dirName, nzbName) -#def sabnzb(): -# if len(sys.argv) < 2: -# scriptlogger.error('No folder supplied - is this being called from SABnzbd?') -# print "No folder supplied - is this being called from SABnzbd?" -# sys.exit() -# elif len(sys.argv) >= 3: -# dirName = sys.argv[1] -# nzbName = sys.argv[2] -# else: -# dirName = sys.argv[1] -# -# return (dirName, nzbName) -# -#def hella(): -# if len(sys.argv) < 4: -# scriptlogger.error('No folder supplied - is this being called from HellaVCR?') -# print "No folder supplied - is this being called from HellaVCR?" -# sys.exit() -# else: -# dirName = sys.argv[3] -# nzbName = sys.argv[2] -# -# return (dirName, nzbName) def main(): scriptlogger.info(u'Starting external PostProcess script ' + __file__) - host = config.get("General", "web_host") - port = config.get("General", "web_port") - username = config.get("General", "web_username") - password = config.get("General", "web_password") + host = config.get('General', 'web_host') + port = config.get('General', 'web_port') + username = config.get('General', 'web_username') + password = config.get('General', 'web_password') try: - ssl = int(config.get("General", "enable_https")) + ssl = int(config.get('General', 'enable_https')) except (ConfigParser.NoOptionError, ValueError): ssl = 0 try: - web_root = config.get("General", "web_root") + web_root = config.get('General', 'web_root') except ConfigParser.NoOptionError: - web_root = "" + web_root = '' - tv_dir = config.get("General", "tv_download_dir") - use_torrents = int(config.get("General", "use_torrents")) - torrent_method = config.get("General", "torrent_method") + tv_dir = config.get('General', 'tv_download_dir') + use_torrents = int(config.get('General', 'use_torrents')) + torrent_method = config.get('General', 'torrent_method') if not use_torrents: scriptlogger.error(u'Enable Use Torrent on Sickbeard to use this Script. Aborting!') @@ -182,28 +163,31 @@ def main(): params['nzbName'] = nzbName if ssl: - protocol = "https://" + protocol = 'https://' else: - protocol = "http://" + protocol = 'http://' if host == '0.0.0.0': host = 'localhost' - url = protocol + host + ":" + port + web_root + "/home/postprocess/processEpisode" + url = protocol + host + ':' + port + web_root + '/home/postprocess/processEpisode' + login_url = protocol + host + ':' + port + web_root + '/login' - scriptlogger.debug("Opening URL: " + url + ' with params=' + str(params)) - print "Opening URL: " + url + ' with params=' + str(params) + scriptlogger.debug('Opening URL: ' + url + ' with params=' + str(params)) + print 'Opening URL: ' + url + ' with params=' + str(params) try: - response = requests.get(url, auth=(username, password), params=params, verify=False) + sess = requests.Session() + sess.post(login_url, data={'username': username, 'password': password}, stream=True, verify=False) + response = sess.get(url, auth=(username, password), params=params, verify=False, allow_redirects=False) except Exception, e: scriptlogger.error(u': Unknown exception raised when opening url: ' + str(e)) time.sleep(3) sys.exit() if response.status_code == 401: - scriptlogger.error(u'Invalid Sickbeard Username or Password, check your config') - print 'Invalid Sickbeard Username or Password, check your config' + scriptlogger.error(u'Verify and use correct username and password in autoProcessTV.cfg') + print 'Verify and use correct username and password in autoProcessTV.cfg' time.sleep(3) sys.exit() diff --git a/gui/slick/css/dark-login.css b/gui/slick/css/dark-login.css new file mode 100644 index 00000000..53deacb3 --- /dev/null +++ b/gui/slick/css/dark-login.css @@ -0,0 +1,98 @@ +/* ======================================================================= +login.tmpl +========================================================================== */ + +.login-remember span { + color: #aaa +} + +/* ======================================================================= +Global +========================================================================== */ + +.red-text { + color: #d33; +} + +/* ======================================================================= +bootstrap Overrides +========================================================================== */ + +body { + color: #fff; + background-color: #222; +} + +input, textarea, select, .uneditable-input { + width: auto; + color: #000; +} + +.btn { + color: #fff; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.75); + background-color: #2672B6; + *background-color: #2672B6; + background-image: -ms-linear-gradient(top, #297AB8, #15528F); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#297AB8), to(#15528F)); + background-image: -webkit-linear-gradient(top, #297AB8, #15528F); + background-image: -o-linear-gradient(top, #297AB8, #15528F); + background-image: linear-gradient(top, #297AB8, #15528F); + background-image: -moz-linear-gradient(top, #297AB8, #15528F); + border: 1px solid #111; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + border-color: #111 #111 #111; + border-bottom-color: #111; + filter: progid:dximagetransform.microsoft.gradient(startColorstr='#297AB8', endColorstr='#15528F', GradientType=0); + filter: progid:dximagetransform.microsoft.gradient(enabled=false); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.0), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.0), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.0), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn:hover, +.btn:active, +.btn.active, +.btn.disabled, +.btn[disabled] { + background-color: #2672B6; + *background-color: #2672B6; + color: #fff; +} + +.btn:active, +.btn.active { + background-color: #cccccc \9; + color: #fff; +} + +.btn:hover { + color: #fff; + background-color: #2672B6; + *background-color: #2672B6; + background-position: 0 -150px; + -webkit-transition: background-position 0.0s linear; + -moz-transition: background-position 0.0s linear; + -ms-transition: background-position 0.0s linear; + -o-transition: background-position 0.0s linear; + transition: background-position 0.0s linear; +} + +.btn:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; + color: #fff; +} + +.btn.active, +.btn:active { + background-color: #2672B6; + background-color: #2672B6 \9; + background-image: none; + color: #fff; + outline: 0; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} \ No newline at end of file diff --git a/gui/slick/css/dark.css b/gui/slick/css/dark.css index 230bc637..2b6a3f21 100644 --- a/gui/slick/css/dark.css +++ b/gui/slick/css/dark.css @@ -298,6 +298,18 @@ inc_top.tmpl background-position: -399px 0; } +.menu-icon-logout { + background-position: -420px 0; +} + +.menu-icon-kodi { + background-position: -441px 0; +} + +.menu-icon-plex { + background-position: -462px 0; +} + [class^="submenu-icon-"], [class*=" submenu-icon-"] { background: url("../images/menu/menu-icons-white.png"); height: 16px; @@ -328,6 +340,16 @@ inc_top.tmpl background-position: -378px 0; } +.submenu-icon-kodi { + background-position: -441px 0; +} + +.submenu-icon-plex { + background-position: -462px -2px; + margin-top: 2px; + height: 12px; +} + /* ======================================================================= inc_bottom.tmpl ========================================================================== */ @@ -776,6 +798,14 @@ a.whitelink { } +/* ======================================================================= +404.tmpl +========================================================================== */ + +#error-404 path { +fill: #fff; +} + /* ======================================================================= Global ========================================================================== */ diff --git a/gui/slick/css/light-login.css b/gui/slick/css/light-login.css new file mode 100644 index 00000000..f482c4c5 --- /dev/null +++ b/gui/slick/css/light-login.css @@ -0,0 +1,85 @@ +/* ======================================================================= +login.tmpl +========================================================================== */ + +.login-remember span { + color: #666 +} + +/* ======================================================================= +bootstrap Overrides +========================================================================== */ + +body { + color: #000; +} + +input, textarea, select, .uneditable-input { + width: auto; + color: #000; +} + +.btn { + color: #333333; + text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75); + background-color: #f5f5f5; + *background-color: #e6e6e6; + background-image: -ms-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6)); + background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6); + background-image: -o-linear-gradient(top, #ffffff, #e6e6e6); + background-image: linear-gradient(top, #ffffff, #e6e6e6); + background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6); + border: 1px solid #cccccc; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + border-color: #e6e6e6 #e6e6e6 #bfbfbf; + border-bottom-color: #b3b3b3; + filter: progid:dximagetransform.microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0); + filter: progid:dximagetransform.microsoft.gradient(enabled=false); + -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05); +} + +.btn:hover, +.btn:active, +.btn.active, +.btn.disabled, +.btn[disabled] { + background-color: #e6e6e6; + *background-color: #d9d9d9; +} + +.btn:active, +.btn.active { + background-color: #cccccc \9; +} + +.btn:hover { + color: #333333; + background-color: #e6e6e6; + *background-color: #d9d9d9; + background-position: 0 -15px; + -webkit-transition: background-position 0.1s linear; + -moz-transition: background-position 0.1s linear; + -ms-transition: background-position 0.1s linear; + -o-transition: background-position 0.1s linear; + transition: background-position 0.1s linear; +} + +.btn:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.btn.active, +.btn:active { + background-color: #e6e6e6; + background-color: #d9d9d9 \9; + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} \ No newline at end of file diff --git a/gui/slick/css/light.css b/gui/slick/css/light.css index 3dc56096..ab7a5f51 100644 --- a/gui/slick/css/light.css +++ b/gui/slick/css/light.css @@ -285,6 +285,18 @@ inc_top.tmpl background-position: -399px 0; } +.menu-icon-logout { + background-position: -420px 0; +} + +.menu-icon-kodi { + background-position: -441px 0; +} + +.menu-icon-plex { + background-position: -462px 0; +} + [class^="submenu-icon-"], [class*=" submenu-icon-"] { background: url("../images/menu/menu-icons-black.png"); height: 16px; @@ -315,6 +327,16 @@ inc_top.tmpl background-position: -378px 0; } +.submenu-icon-kodi { + background-position: -441px 0; +} + +.submenu-icon-plex { + background-position: -462px -2px; + margin-top: 2px; + height: 12px; +} + /* ======================================================================= inc_bottom.tmpl ========================================================================== */ @@ -738,6 +760,14 @@ a.whitelink { color: #fff; } +/* ======================================================================= +404.tmpl +========================================================================== */ + +#error-404 path { +fill: #000; +} + /* ======================================================================= Global ========================================================================== */ diff --git a/gui/slick/css/style-login.css b/gui/slick/css/style-login.css new file mode 100644 index 00000000..0c98ae39 --- /dev/null +++ b/gui/slick/css/style-login.css @@ -0,0 +1,163 @@ +/* ======================================================================= +fonts +========================================================================== */ +/* Open Sans */ +/* Regular */ +@font-face { + font-family: 'Open Sans'; + + 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-weight: 400; + font-style: normal; +} + +/* ======================================================================= +login.tmpl +========================================================================== */ + +.login { + position : absolute; + display: block; + height: 275px; + width: 300px; + margin-top: -137.5px; + margin-left: -150px; + top: 50%; + left: 50%; +} + +.login-img { + height: 128px; + width: 250px; + background-image: url('../images/sickgear-large.png'); + margin-bottom: 0; +} + +.login .glyphicon { + top: 0 +} + +.login-error { + height: 20px; + margin-bottom: 2px; + font-size: 13px; + text-align: center; +} + +.login-remember { + font-size: 12px; + display: inline-block; + padding: 2px 0; + line-height: 14px +} + +.login-remember input { + position: relative; + top: 2px +} + +.login-remember span { + margin-left: 5px; +} + +.error16 { + background: url('../images/error16.png') no-repeat 0 1px; + width: 16px; + display: inline-block; +} + +/* ======================================================================= +Global +========================================================================== */ + +.red-text { + color: #d33; +} + +/* ======================================================================= +bootstrap Overrides +========================================================================== */ + +body { + padding-top: 60px; + overflow-y: scroll; + font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #000; +} + +label { + font-weight: normal; +} + +.btn { + display: inline-block; + *display: inline; + padding: 4px 10px 4px; + margin-bottom: 0; + *margin-left: .3em; + font-size: 12px; + line-height: 16px; + *line-height: 20px; + text-align: center; + vertical-align: middle; + cursor: pointer; + background-repeat: repeat-x; + *border: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + *zoom: 1; +} + +.btn:hover, +.btn:active, +.btn.active, +.btn.disabled, +.btn[disabled] { + background-color: #e6e6e6; + *background-color: #d9d9d9; +} + +.btn:active, +.btn.active { + background-color: #cccccc \9; +} + +.btn:first-child { + *margin-left: 0; +} + +.btn:hover { + color: #333333; + text-decoration: none; + background-color: #e6e6e6; + *background-color: #d9d9d9; + background-position: 0 -15px; + -webkit-transition: background-position 0.1s linear; + -moz-transition: background-position 0.1s linear; + -ms-transition: background-position 0.1s linear; + -o-transition: background-position 0.1s linear; + transition: background-position 0.1s linear; +} + +.btn:focus { + outline: thin dotted #333; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; +} + +.btn.active, +.btn:active { + background-color: #e6e6e6; + background-color: #d9d9d9 \9; + background-image: none; + outline: 0; + -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05); +} \ No newline at end of file diff --git a/gui/slick/css/style.css b/gui/slick/css/style.css index 7f79fa44..7b430766 100644 --- a/gui/slick/css/style.css +++ b/gui/slick/css/style.css @@ -139,7 +139,6 @@ fonts font-style: normal; } - /* ======================================================================= inc_top.tmpl ========================================================================== */ @@ -444,6 +443,18 @@ inc_top.tmpl background-position: -399px 0; } +.menu-icon-logout { + background-position: -420px 0; +} + +.menu-icon-kodi { + background-position: -441px 0; +} + +.menu-icon-plex { + background-position: -462px 0; +} + [class^="submenu-icon-"], [class*=" submenu-icon-"] { background: url("../images/menu/menu-icons-black.png"); height: 16px; @@ -474,6 +485,14 @@ inc_top.tmpl background-position: -378px 0; } +.submenu-icon-kodi { + background-position: -441px 0; +} + +.submenu-icon-plex { + background-position: -462px 0; +} + /* ======================================================================= inc_bottom.tmpl ========================================================================== */ @@ -2206,6 +2225,25 @@ a.whitelink { color: #fff; } +/* ======================================================================= +404.tmpl +========================================================================== */ + +#error-404 { + text-align: center; +} + +#error-404 h1 { + font-size: 200px; + line-height: 200px; + font-weight: 900; +} + +#error-404 h2 { + text-transform: uppercase; + font-size: 50px; + font-weight: 900; +} /* ======================================================================= Global diff --git a/gui/slick/images/error16.png b/gui/slick/images/error16.png new file mode 100644 index 00000000..d7d19527 Binary files /dev/null and b/gui/slick/images/error16.png differ diff --git a/gui/slick/images/menu/menu-icons-black.png b/gui/slick/images/menu/menu-icons-black.png index 0e3cca99..43dd426c 100644 Binary files a/gui/slick/images/menu/menu-icons-black.png and b/gui/slick/images/menu/menu-icons-black.png differ diff --git a/gui/slick/images/menu/menu-icons-white.png b/gui/slick/images/menu/menu-icons-white.png index f301d602..0d867eca 100644 Binary files a/gui/slick/images/menu/menu-icons-white.png and b/gui/slick/images/menu/menu-icons-white.png differ diff --git a/gui/slick/images/sickgear-large.png b/gui/slick/images/sickgear-large.png new file mode 100644 index 00000000..857e2dc2 Binary files /dev/null and b/gui/slick/images/sickgear-large.png differ diff --git a/gui/slick/interfaces/default/404.tmpl b/gui/slick/interfaces/default/404.tmpl new file mode 100644 index 00000000..42da8b52 --- /dev/null +++ b/gui/slick/interfaces/default/404.tmpl @@ -0,0 +1,33 @@ +#import sickbeard + +#set global $title = '404' + +#set global $sbPath = '..' + +#set global $topmenu = '404' +#import os.path +#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl') + + + +
+ + + + +