Merge branch 'release/0.7.0'
84
CHANGES.md
|
@ -1,8 +1,92 @@
|
|||
### 0.7.0 (2015-03-04 06:00:00 UTC)
|
||||
|
||||
* Fix slow database operations (port from midgetspy/sickbeard)
|
||||
* Add TVRage network name standardization
|
||||
* Remove recent and backlog search at start up options from GUI
|
||||
* Change recent and backlog search at start up default value to false
|
||||
* Change recent search to occur 5 minutes after start up
|
||||
* Change backlog search to occur 10 minutes after start up
|
||||
* Change UI footer to display time left until a backlog search
|
||||
* Remove obsolete tvtorrents search provider
|
||||
* Change light and dark theme css to only hold color information
|
||||
* Fix incorrect class names in a couple of templates
|
||||
* Change anime release groups to in memory storage for lowered latency
|
||||
* Change adjust menu delay and hover styling
|
||||
* Fix provider list color
|
||||
* Add handling of exceptional case with missing network name (NoneType) in Episode View
|
||||
* Fix black and white list initialization on new show creation
|
||||
* Add select all and clear all buttons to testRename template
|
||||
* Fix displayShow topmenu variable to point to a valid menu item
|
||||
* Change displayShow scene exception separator to a comma for neater appearance
|
||||
* Remove non english subtitle providers
|
||||
* Fix rename of excluded metadata
|
||||
* Change corrected spelling & better clarified various log messages
|
||||
* Change minor PEP8 tweaks in sab.py
|
||||
* Add api disabled error code for newznab providers
|
||||
* Add support for a proxy host PAC url on the General Config/Advanced Settings page
|
||||
* Add proxy request url parsing to enforce netloc only matching which prevents false positives when url query parts contain FQDNs
|
||||
* Add scroll into view buttons when overdues shows are available on the Episodes page/DayByDay layout
|
||||
* Add scroll into view buttons when future shows are available on the Episodes page/DayByDay layout
|
||||
* Add qTips to episode names on the Episodes page/DayByDay layout
|
||||
* Change Episodes page/List layout qtips to prepend show title to episode plot
|
||||
* Change Episodes page/DayByDay layout qtips to prepend show title to episode plot
|
||||
* Change Episodes page/DayByDay layout cards to display show title in a qtip when there is no plot
|
||||
* Change position of "[paused]" text to top right of a card on the Episodes page/DayByDay layout
|
||||
* Add "On Air until" text and overdue/on air colour bars to show episode states on the Episodes page/DayByDay layout
|
||||
* Change The Pirate Bay url back as it's now back up and oldpiratebay hasn't been updated for weeks
|
||||
* Remove duplicate thepiratebay icon
|
||||
* Change to ensure uTorrent API parameters are ordered for uT 2.2.1 compatibility
|
||||
* Remove defunct boxcar notifier
|
||||
* Add sound selection for boxcar2 notifier
|
||||
* Change boxcar2 notifier to use updated api scheme
|
||||
* Update the Plex notifier from a port at midgetspy/sickbeard
|
||||
* Add support for multiple server hosts to the updated Plex server notifier
|
||||
* Change Plex Media Server settings section for multi server(s) and improve the layout in the config/notifications page
|
||||
* Add logic to Plex notifier to update a single server where its TV section path matches the downloaded show. All server
|
||||
libraries are updated if no single server has a download path match.
|
||||
* Change the ui notifications to show the Plex Media Server(s) actioned for library updating
|
||||
* Fix issue where PMS text wasn't initialised on the config/notifications page and added info about Plex clients
|
||||
* Add ability to test Plex Server(s) on config/notifications page
|
||||
* Add percentage of episodes downloaded to footer and remove double spaces in text
|
||||
* Fix SSL authentication on Synology stations
|
||||
* Change IPT urls to reduce 301 redirection
|
||||
* Add detection of file-system having no support for link creation (e.g. Unraid shares)
|
||||
* Add catch exceptions when unable to cache a requests response
|
||||
* Update PNotify to latest master (2014-12-25) for desktop notifications
|
||||
* Add desktop notifications
|
||||
* Change the AniDB provider image for a sharper looking version
|
||||
* Change to streamline iCal function and make it handle missing network names
|
||||
* Change when picking a best result to only test items that have a size specifier against the failed history
|
||||
* Add anime release groups to add new show options page
|
||||
* Add setting "Update shows during hour" to General Config/Misc
|
||||
* Add max-width to prevent ui glitch on Pull request and Branch Version selectors on config/General/Advanced and change <input> tags to html5
|
||||
* Change order of some settings on Config/General/Interface/Web Interface and tweak texts
|
||||
* Change overhaul UI of editShow and anime release groups, refactor and simplify code
|
||||
* Change list order of option on the right of the displayShow page to be mostly inline with the order of options on editShow
|
||||
* Change legend wording and text colour on the displayShow page
|
||||
* Add output message if no release group results are available
|
||||
* 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
|
||||
* Change SCC URLs to remove redirection overhead
|
||||
* Change TorrentBytes login parameter in line with site change
|
||||
* Change FreshOnTv login parameter and use secure URLs, add logging of Cloudflare blocking and prevent vacant cookie tracebacks
|
||||
* Change TPB webproxy list and add SSL variants
|
||||
* Add YTV network logo
|
||||
* Remove defunct Fanzub provider
|
||||
|
||||
|
||||
### 0.6.4 (2015-02-10 20:20:00 UTC)
|
||||
|
||||
* Fix issue where setting the status for an episode that doesn't need a db update fails
|
||||
|
||||
|
||||
### 0.6.3 (2015-02-10 05:30:00 UTC)
|
||||
|
||||
* Change KickAssTorrents URL
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
|
@ -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()
|
||||
|
||||
|
|
BIN
gui/slick/css/fonts/glyphicons-halflings-regular.eot
Normal file
229
gui/slick/css/fonts/glyphicons-halflings-regular.svg
Normal file
|
@ -0,0 +1,229 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata></metadata>
|
||||
<defs>
|
||||
<font id="glyphicons_halflingsregular" horiz-adv-x="1200" >
|
||||
<font-face units-per-em="1200" ascent="960" descent="-240" />
|
||||
<missing-glyph horiz-adv-x="500" />
|
||||
<glyph />
|
||||
<glyph />
|
||||
<glyph unicode="
" />
|
||||
<glyph unicode=" " />
|
||||
<glyph unicode="*" d="M100 500v200h259l-183 183l141 141l183 -183v259h200v-259l183 183l141 -141l-183 -183h259v-200h-259l183 -183l-141 -141l-183 183v-259h-200v259l-183 -183l-141 141l183 183h-259z" />
|
||||
<glyph unicode="+" d="M0 400v300h400v400h300v-400h400v-300h-400v-400h-300v400h-400z" />
|
||||
<glyph unicode=" " />
|
||||
<glyph unicode=" " horiz-adv-x="652" />
|
||||
<glyph unicode=" " horiz-adv-x="1304" />
|
||||
<glyph unicode=" " horiz-adv-x="652" />
|
||||
<glyph unicode=" " horiz-adv-x="1304" />
|
||||
<glyph unicode=" " horiz-adv-x="434" />
|
||||
<glyph unicode=" " horiz-adv-x="326" />
|
||||
<glyph unicode=" " horiz-adv-x="217" />
|
||||
<glyph unicode=" " horiz-adv-x="217" />
|
||||
<glyph unicode=" " horiz-adv-x="163" />
|
||||
<glyph unicode=" " horiz-adv-x="260" />
|
||||
<glyph unicode=" " horiz-adv-x="72" />
|
||||
<glyph unicode=" " horiz-adv-x="260" />
|
||||
<glyph unicode=" " horiz-adv-x="326" />
|
||||
<glyph unicode="€" d="M100 500l100 100h113q0 47 5 100h-218l100 100h135q37 167 112 257q117 141 297 141q242 0 354 -189q60 -103 66 -209h-181q0 55 -25.5 99t-63.5 68t-75 36.5t-67 12.5q-24 0 -52.5 -10t-62.5 -32t-65.5 -67t-50.5 -107h379l-100 -100h-300q-6 -46 -6 -100h406l-100 -100 h-300q9 -74 33 -132t52.5 -91t62 -54.5t59 -29t46.5 -7.5q29 0 66 13t75 37t63.5 67.5t25.5 96.5h174q-31 -172 -128 -278q-107 -117 -274 -117q-205 0 -324 158q-36 46 -69 131.5t-45 205.5h-217z" />
|
||||
<glyph unicode="−" d="M200 400h900v300h-900v-300z" />
|
||||
<glyph unicode="◼" horiz-adv-x="500" d="M0 0z" />
|
||||
<glyph unicode="☁" d="M-14 494q0 -80 56.5 -137t135.5 -57h750q120 0 205 86.5t85 207.5t-85 207t-205 86q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5z" />
|
||||
<glyph unicode="✉" d="M0 100l400 400l200 -200l200 200l400 -400h-1200zM0 300v600l300 -300zM0 1100l600 -603l600 603h-1200zM900 600l300 300v-600z" />
|
||||
<glyph unicode="✏" d="M-13 -13l333 112l-223 223zM187 403l214 -214l614 614l-214 214zM887 1103l214 -214l99 92q13 13 13 32.5t-13 33.5l-153 153q-15 13 -33 13t-33 -13z" />
|
||||
<glyph unicode="" d="M0 1200h1200l-500 -550v-550h300v-100h-800v100h300v550z" />
|
||||
<glyph unicode="" d="M14 84q18 -55 86 -75.5t147 5.5q65 21 109 69t44 90v606l600 155v-521q-64 16 -138 -7q-79 -26 -122.5 -83t-25.5 -111q18 -55 86 -75.5t147 4.5q70 23 111.5 63.5t41.5 95.5v881q0 10 -7 15.5t-17 2.5l-752 -193q-10 -3 -17 -12.5t-7 -19.5v-689q-64 17 -138 -7 q-79 -25 -122.5 -82t-25.5 -112z" />
|
||||
<glyph unicode="" d="M23 693q0 200 142 342t342 142t342 -142t142 -342q0 -142 -78 -261l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233z" />
|
||||
<glyph unicode="" d="M100 784q0 64 28 123t73 100.5t104.5 64t119 20.5t120 -38.5t104.5 -104.5q48 69 109.5 105t121.5 38t118.5 -20.5t102.5 -64t71 -100.5t27 -123q0 -57 -33.5 -117.5t-94 -124.5t-126.5 -127.5t-150 -152.5t-146 -174q-62 85 -145.5 174t-149.5 152.5t-126.5 127.5 t-94 124.5t-33.5 117.5z" />
|
||||
<glyph unicode="" d="M-72 800h479l146 400h2l146 -400h472l-382 -278l145 -449l-384 275l-382 -275l146 447zM168 71l2 1z" />
|
||||
<glyph unicode="" d="M-72 800h479l146 400h2l146 -400h472l-382 -278l145 -449l-384 275l-382 -275l146 447zM168 71l2 1zM237 700l196 -142l-73 -226l192 140l195 -141l-74 229l193 140h-235l-77 211l-78 -211h-239z" />
|
||||
<glyph unicode="" d="M0 0v143l400 257v100q-37 0 -68.5 74.5t-31.5 125.5v200q0 124 88 212t212 88t212 -88t88 -212v-200q0 -51 -31.5 -125.5t-68.5 -74.5v-100l400 -257v-143h-1200z" />
|
||||
<glyph unicode="" d="M0 0v1100h1200v-1100h-1200zM100 100h100v100h-100v-100zM100 300h100v100h-100v-100zM100 500h100v100h-100v-100zM100 700h100v100h-100v-100zM100 900h100v100h-100v-100zM300 100h600v400h-600v-400zM300 600h600v400h-600v-400zM1000 100h100v100h-100v-100z M1000 300h100v100h-100v-100zM1000 500h100v100h-100v-100zM1000 700h100v100h-100v-100zM1000 900h100v100h-100v-100z" />
|
||||
<glyph unicode="" d="M0 50v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5zM0 650v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400 q-21 0 -35.5 14.5t-14.5 35.5zM600 50v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5zM600 650v400q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-400 q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||
<glyph unicode="" d="M0 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM0 450v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200 q-21 0 -35.5 14.5t-14.5 35.5zM0 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5 t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 450v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5 v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 450v200q0 21 14.5 35.5t35.5 14.5h200 q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM800 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||
<glyph unicode="" d="M0 50v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM0 450q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v200q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5 t-14.5 -35.5v-200zM0 850v200q0 21 14.5 35.5t35.5 14.5h200q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5zM400 50v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5 t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5zM400 450v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5zM400 850v200q0 21 14.5 35.5t35.5 14.5h700q21 0 35.5 -14.5t14.5 -35.5 v-200q0 -21 -14.5 -35.5t-35.5 -14.5h-700q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||
<glyph unicode="" d="M29 454l419 -420l818 820l-212 212l-607 -607l-206 207z" />
|
||||
<glyph unicode="" d="M106 318l282 282l-282 282l212 212l282 -282l282 282l212 -212l-282 -282l282 -282l-212 -212l-282 282l-282 -282z" />
|
||||
<glyph unicode="" d="M23 693q0 200 142 342t342 142t342 -142t142 -342q0 -142 -78 -261l300 -300q7 -8 7 -18t-7 -18l-109 -109q-8 -7 -18 -7t-18 7l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 693q0 -136 97 -233t234 -97t233.5 96.5t96.5 233.5t-96.5 233.5t-233.5 96.5 t-234 -97t-97 -233zM300 600v200h100v100h200v-100h100v-200h-100v-100h-200v100h-100z" />
|
||||
<glyph unicode="" d="M23 694q0 200 142 342t342 142t342 -142t142 -342q0 -141 -78 -262l300 -299q7 -7 7 -18t-7 -18l-109 -109q-8 -8 -18 -8t-18 8l-300 300q-119 -78 -261 -78q-200 0 -342 142t-142 342zM176 694q0 -136 97 -233t234 -97t233.5 97t96.5 233t-96.5 233t-233.5 97t-234 -97 t-97 -233zM300 601h400v200h-400v-200z" />
|
||||
<glyph unicode="" d="M23 600q0 183 105 331t272 210v-166q-103 -55 -165 -155t-62 -220q0 -177 125 -302t302 -125t302 125t125 302q0 120 -62 220t-165 155v166q167 -62 272 -210t105 -331q0 -118 -45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5 zM500 750q0 -21 14.5 -35.5t35.5 -14.5h100q21 0 35.5 14.5t14.5 35.5v400q0 21 -14.5 35.5t-35.5 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-400z" />
|
||||
<glyph unicode="" d="M100 1h200v300h-200v-300zM400 1v500h200v-500h-200zM700 1v800h200v-800h-200zM1000 1v1200h200v-1200h-200z" />
|
||||
<glyph unicode="" d="M26 601q0 -33 6 -74l151 -38l2 -6q14 -49 38 -93l3 -5l-80 -134q45 -59 105 -105l133 81l5 -3q45 -26 94 -39l5 -2l38 -151q40 -5 74 -5q27 0 74 5l38 151l6 2q46 13 93 39l5 3l134 -81q56 44 104 105l-80 134l3 5q24 44 39 93l1 6l152 38q5 40 5 74q0 28 -5 73l-152 38 l-1 6q-16 51 -39 93l-3 5l80 134q-44 58 -104 105l-134 -81l-5 3q-45 25 -93 39l-6 1l-38 152q-40 5 -74 5q-27 0 -74 -5l-38 -152l-5 -1q-50 -14 -94 -39l-5 -3l-133 81q-59 -47 -105 -105l80 -134l-3 -5q-25 -47 -38 -93l-2 -6l-151 -38q-6 -48 -6 -73zM385 601 q0 88 63 151t152 63t152 -63t63 -151q0 -89 -63 -152t-152 -63t-152 63t-63 152z" />
|
||||
<glyph unicode="" d="M100 1025v50q0 10 7.5 17.5t17.5 7.5h275v100q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5v-100h275q10 0 17.5 -7.5t7.5 -17.5v-50q0 -11 -7 -18t-18 -7h-1050q-11 0 -18 7t-7 18zM200 100v800h900v-800q0 -41 -29.5 -71t-70.5 -30h-700q-41 0 -70.5 30 t-29.5 71zM300 100h100v700h-100v-700zM500 100h100v700h-100v-700zM500 1100h300v100h-300v-100zM700 100h100v700h-100v-700zM900 100h100v700h-100v-700z" />
|
||||
<glyph unicode="" d="M1 601l656 644l644 -644h-200v-600h-300v400h-300v-400h-300v600h-200z" />
|
||||
<glyph unicode="" d="M100 25v1150q0 11 7 18t18 7h475v-500h400v-675q0 -11 -7 -18t-18 -7h-850q-11 0 -18 7t-7 18zM700 800v300l300 -300h-300z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM500 500v400h100 v-300h200v-100h-300z" />
|
||||
<glyph unicode="" d="M-100 0l431 1200h209l-21 -300h162l-20 300h208l431 -1200h-538l-41 400h-242l-40 -400h-539zM488 500h224l-27 300h-170z" />
|
||||
<glyph unicode="" d="M0 0v400h490l-290 300h200v500h300v-500h200l-290 -300h490v-400h-1100zM813 200h175v100h-175v-100z" />
|
||||
<glyph unicode="" d="M1 600q0 122 47.5 233t127.5 191t191 127.5t233 47.5t233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233zM188 600q0 -170 121 -291t291 -121t291 121t121 291t-121 291t-291 121 t-291 -121t-121 -291zM350 600h150v300h200v-300h150l-250 -300z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM350 600l250 300 l250 -300h-150v-300h-200v300h-150z" />
|
||||
<glyph unicode="" d="M0 25v475l200 700h800l199 -700l1 -475q0 -11 -7 -18t-18 -7h-1150q-11 0 -18 7t-7 18zM200 500h200l50 -200h300l50 200h200l-97 500h-606z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM500 397v401 l297 -200z" />
|
||||
<glyph unicode="" d="M23 600q0 -118 45.5 -224.5t123 -184t184 -123t224.5 -45.5t224.5 45.5t184 123t123 184t45.5 224.5h-150q0 -177 -125 -302t-302 -125t-302 125t-125 302t125 302t302 125q136 0 246 -81l-146 -146h400v400l-145 -145q-157 122 -355 122q-118 0 -224.5 -45.5t-184 -123 t-123 -184t-45.5 -224.5z" />
|
||||
<glyph unicode="" d="M23 600q0 118 45.5 224.5t123 184t184 123t224.5 45.5q198 0 355 -122l145 145v-400h-400l147 147q-112 80 -247 80q-177 0 -302 -125t-125 -302h-150zM100 0v400h400l-147 -147q112 -80 247 -80q177 0 302 125t125 302h150q0 -118 -45.5 -224.5t-123 -184t-184 -123 t-224.5 -45.5q-198 0 -355 122z" />
|
||||
<glyph unicode="" d="M100 0h1100v1200h-1100v-1200zM200 100v900h900v-900h-900zM300 200v100h100v-100h-100zM300 400v100h100v-100h-100zM300 600v100h100v-100h-100zM300 800v100h100v-100h-100zM500 200h500v100h-500v-100zM500 400v100h500v-100h-500zM500 600v100h500v-100h-500z M500 800v100h500v-100h-500z" />
|
||||
<glyph unicode="" d="M0 100v600q0 41 29.5 70.5t70.5 29.5h100v200q0 82 59 141t141 59h300q82 0 141 -59t59 -141v-200h100q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-900q-41 0 -70.5 29.5t-29.5 70.5zM400 800h300v150q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-150z" />
|
||||
<glyph unicode="" d="M100 0v1100h100v-1100h-100zM300 400q60 60 127.5 84t127.5 17.5t122 -23t119 -30t110 -11t103 42t91 120.5v500q-40 -81 -101.5 -115.5t-127.5 -29.5t-138 25t-139.5 40t-125.5 25t-103 -29.5t-65 -115.5v-500z" />
|
||||
<glyph unicode="" d="M0 275q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 127 70.5 231.5t184.5 161.5t245 57t245 -57t184.5 -161.5t70.5 -231.5v-300q0 -11 7 -18t18 -7h50q11 0 18 7t7 18v300q0 116 -49.5 227t-131 192.5t-192.5 131t-227 49.5t-227 -49.5t-192.5 -131t-131 -192.5 t-49.5 -227v-300zM200 20v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14zM800 20v460q0 8 6 14t14 6h160q8 0 14 -6t6 -14v-460q0 -8 -6 -14t-14 -6h-160q-8 0 -14 6t-6 14z" />
|
||||
<glyph unicode="" d="M0 400h300l300 -200v800l-300 -200h-300v-400zM688 459l141 141l-141 141l71 71l141 -141l141 141l71 -71l-141 -141l141 -141l-71 -71l-141 141l-141 -141z" />
|
||||
<glyph unicode="" d="M0 400h300l300 -200v800l-300 -200h-300v-400zM700 857l69 53q111 -135 111 -310q0 -169 -106 -302l-67 54q86 110 86 248q0 146 -93 257z" />
|
||||
<glyph unicode="" d="M0 401v400h300l300 200v-800l-300 200h-300zM702 858l69 53q111 -135 111 -310q0 -170 -106 -303l-67 55q86 110 86 248q0 145 -93 257zM889 951l7 -8q123 -151 123 -344q0 -189 -119 -339l-7 -8l81 -66l6 8q142 178 142 405q0 230 -144 408l-6 8z" />
|
||||
<glyph unicode="" d="M0 0h500v500h-200v100h-100v-100h-200v-500zM0 600h100v100h400v100h100v100h-100v300h-500v-600zM100 100v300h300v-300h-300zM100 800v300h300v-300h-300zM200 200v100h100v-100h-100zM200 900h100v100h-100v-100zM500 500v100h300v-300h200v-100h-100v-100h-200v100 h-100v100h100v200h-200zM600 0v100h100v-100h-100zM600 1000h100v-300h200v-300h300v200h-200v100h200v500h-600v-200zM800 800v300h300v-300h-300zM900 0v100h300v-100h-300zM900 900v100h100v-100h-100zM1100 200v100h100v-100h-100z" />
|
||||
<glyph unicode="" d="M0 200h100v1000h-100v-1000zM100 0v100h300v-100h-300zM200 200v1000h100v-1000h-100zM500 0v91h100v-91h-100zM500 200v1000h200v-1000h-200zM700 0v91h100v-91h-100zM800 200v1000h100v-1000h-100zM900 0v91h200v-91h-200zM1000 200v1000h200v-1000h-200z" />
|
||||
<glyph unicode="" d="M0 700l1 475q0 10 7.5 17.5t17.5 7.5h474l700 -700l-500 -500zM148 953q0 -42 29 -71q30 -30 71.5 -30t71.5 30q29 29 29 71t-29 71q-30 30 -71.5 30t-71.5 -30q-29 -29 -29 -71z" />
|
||||
<glyph unicode="" d="M1 700l1 475q0 11 7 18t18 7h474l700 -700l-500 -500zM148 953q0 -42 30 -71q29 -30 71 -30t71 30q30 29 30 71t-30 71q-29 30 -71 30t-71 -30q-30 -29 -30 -71zM701 1200h100l700 -700l-500 -500l-50 50l450 450z" />
|
||||
<glyph unicode="" d="M100 0v1025l175 175h925v-1000l-100 -100v1000h-750l-100 -100h750v-1000h-900z" />
|
||||
<glyph unicode="" d="M200 0l450 444l450 -443v1150q0 20 -14.5 35t-35.5 15h-800q-21 0 -35.5 -15t-14.5 -35v-1151z" />
|
||||
<glyph unicode="" d="M0 100v700h200l100 -200h600l100 200h200v-700h-200v200h-800v-200h-200zM253 829l40 -124h592l62 124l-94 346q-2 11 -10 18t-18 7h-450q-10 0 -18 -7t-10 -18zM281 24l38 152q2 10 11.5 17t19.5 7h500q10 0 19.5 -7t11.5 -17l38 -152q2 -10 -3.5 -17t-15.5 -7h-600 q-10 0 -15.5 7t-3.5 17z" />
|
||||
<glyph unicode="" d="M0 200q0 -41 29.5 -70.5t70.5 -29.5h1000q41 0 70.5 29.5t29.5 70.5v600q0 41 -29.5 70.5t-70.5 29.5h-150q-4 8 -11.5 21.5t-33 48t-53 61t-69 48t-83.5 21.5h-200q-41 0 -82 -20.5t-70 -50t-52 -59t-34 -50.5l-12 -20h-150q-41 0 -70.5 -29.5t-29.5 -70.5v-600z M356 500q0 100 72 172t172 72t172 -72t72 -172t-72 -172t-172 -72t-172 72t-72 172zM494 500q0 -44 31 -75t75 -31t75 31t31 75t-31 75t-75 31t-75 -31t-31 -75zM900 700v100h100v-100h-100z" />
|
||||
<glyph unicode="" d="M53 0h365v66q-41 0 -72 11t-49 38t1 71l92 234h391l82 -222q16 -45 -5.5 -88.5t-74.5 -43.5v-66h417v66q-34 1 -74 43q-18 19 -33 42t-21 37l-6 13l-385 998h-93l-399 -1006q-24 -48 -52 -75q-12 -12 -33 -25t-36 -20l-15 -7v-66zM416 521l178 457l46 -140l116 -317h-340 z" />
|
||||
<glyph unicode="" d="M100 0v89q41 7 70.5 32.5t29.5 65.5v827q0 28 -1 39.5t-5.5 26t-15.5 21t-29 14t-49 14.5v71l471 -1q120 0 213 -88t93 -228q0 -55 -11.5 -101.5t-28 -74t-33.5 -47.5t-28 -28l-12 -7q8 -3 21.5 -9t48 -31.5t60.5 -58t47.5 -91.5t21.5 -129q0 -84 -59 -156.5t-142 -111 t-162 -38.5h-500zM400 200h161q89 0 153 48.5t64 132.5q0 90 -62.5 154.5t-156.5 64.5h-159v-400zM400 700h139q76 0 130 61.5t54 138.5q0 82 -84 130.5t-239 48.5v-379z" />
|
||||
<glyph unicode="" d="M200 0v57q77 7 134.5 40.5t65.5 80.5l173 849q10 56 -10 74t-91 37q-6 1 -10.5 2.5t-9.5 2.5v57h425l2 -57q-33 -8 -62 -25.5t-46 -37t-29.5 -38t-17.5 -30.5l-5 -12l-128 -825q-10 -52 14 -82t95 -36v-57h-500z" />
|
||||
<glyph unicode="" d="M-75 200h75v800h-75l125 167l125 -167h-75v-800h75l-125 -167zM300 900v300h150h700h150v-300h-50q0 29 -8 48.5t-18.5 30t-33.5 15t-39.5 5.5t-50.5 1h-200v-850l100 -50v-100h-400v100l100 50v850h-200q-34 0 -50.5 -1t-40 -5.5t-33.5 -15t-18.5 -30t-8.5 -48.5h-49z " />
|
||||
<glyph unicode="" d="M33 51l167 125v-75h800v75l167 -125l-167 -125v75h-800v-75zM100 901v300h150h700h150v-300h-50q0 29 -8 48.5t-18 30t-33.5 15t-40 5.5t-50.5 1h-200v-650l100 -50v-100h-400v100l100 50v650h-200q-34 0 -50.5 -1t-39.5 -5.5t-33.5 -15t-18.5 -30t-8 -48.5h-50z" />
|
||||
<glyph unicode="" d="M0 50q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 350q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5 v-100zM0 650q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1000q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 950q0 -20 14.5 -35t35.5 -15h600q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-600q-21 0 -35.5 -14.5 t-14.5 -35.5v-100z" />
|
||||
<glyph unicode="" d="M0 50q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM0 650q0 -20 14.5 -35t35.5 -15h1100q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5 v-100zM200 350q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM200 950q0 -20 14.5 -35t35.5 -15h700q21 0 35.5 15t14.5 35v100q0 21 -14.5 35.5t-35.5 14.5h-700q-21 0 -35.5 -14.5 t-14.5 -35.5v-100z" />
|
||||
<glyph unicode="" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM100 650v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1000q-21 0 -35.5 15 t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM500 950v100q0 21 14.5 35.5t35.5 14.5h600q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-600 q-21 0 -35.5 15t-14.5 35z" />
|
||||
<glyph unicode="" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100q-21 0 -35.5 15t-14.5 35zM0 950v100q0 21 14.5 35.5t35.5 14.5h1100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-1100 q-21 0 -35.5 15t-14.5 35z" />
|
||||
<glyph unicode="" d="M0 50v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM0 350v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15 t-14.5 35zM0 650v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15t-14.5 35zM0 950v100q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-100q-21 0 -35.5 15 t-14.5 35zM300 50v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM300 350v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800 q-21 0 -35.5 15t-14.5 35zM300 650v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15h-800q-21 0 -35.5 15t-14.5 35zM300 950v100q0 21 14.5 35.5t35.5 14.5h800q21 0 35.5 -14.5t14.5 -35.5v-100q0 -20 -14.5 -35t-35.5 -15 h-800q-21 0 -35.5 15t-14.5 35z" />
|
||||
<glyph unicode="" d="M-101 500v100h201v75l166 -125l-166 -125v75h-201zM300 0h100v1100h-100v-1100zM500 50q0 -20 14.5 -35t35.5 -15h600q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 350q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35 v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 650q0 -20 14.5 -35t35.5 -15h500q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM500 950q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35v100 q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100z" />
|
||||
<glyph unicode="" d="M1 50q0 -20 14.5 -35t35.5 -15h600q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-600q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 350q0 -20 14.5 -35t35.5 -15h300q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-300q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 650 q0 -20 14.5 -35t35.5 -15h500q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-500q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM1 950q0 -20 14.5 -35t35.5 -15h100q20 0 35 15t15 35v100q0 21 -15 35.5t-35 14.5h-100q-21 0 -35.5 -14.5t-14.5 -35.5v-100zM801 0v1100h100v-1100 h-100zM934 550l167 -125v75h200v100h-200v75z" />
|
||||
<glyph unicode="" d="M0 275v650q0 31 22 53t53 22h750q31 0 53 -22t22 -53v-650q0 -31 -22 -53t-53 -22h-750q-31 0 -53 22t-22 53zM900 600l300 300v-600z" />
|
||||
<glyph unicode="" d="M0 44v1012q0 18 13 31t31 13h1112q19 0 31.5 -13t12.5 -31v-1012q0 -18 -12.5 -31t-31.5 -13h-1112q-18 0 -31 13t-13 31zM100 263l247 182l298 -131l-74 156l293 318l236 -288v500h-1000v-737zM208 750q0 56 39 95t95 39t95 -39t39 -95t-39 -95t-95 -39t-95 39t-39 95z " />
|
||||
<glyph unicode="" d="M148 745q0 124 60.5 231.5t165 172t226.5 64.5q123 0 227 -63t164.5 -169.5t60.5 -229.5t-73 -272q-73 -114 -166.5 -237t-150.5 -189l-57 -66q-10 9 -27 26t-66.5 70.5t-96 109t-104 135.5t-100.5 155q-63 139 -63 262zM342 772q0 -107 75.5 -182.5t181.5 -75.5 q107 0 182.5 75.5t75.5 182.5t-75.5 182t-182.5 75t-182 -75.5t-75 -181.5z" />
|
||||
<glyph unicode="" d="M1 600q0 122 47.5 233t127.5 191t191 127.5t233 47.5t233 -47.5t191 -127.5t127.5 -191t47.5 -233t-47.5 -233t-127.5 -191t-191 -127.5t-233 -47.5t-233 47.5t-191 127.5t-127.5 191t-47.5 233zM173 600q0 -177 125.5 -302t301.5 -125v854q-176 0 -301.5 -125 t-125.5 -302z" />
|
||||
<glyph unicode="" d="M117 406q0 94 34 186t88.5 172.5t112 159t115 177t87.5 194.5q21 -71 57.5 -142.5t76 -130.5t83 -118.5t82 -117t70 -116t50 -125.5t18.5 -136q0 -89 -39 -165.5t-102 -126.5t-140 -79.5t-156 -33.5q-114 6 -211.5 53t-161.5 139t-64 210zM243 414q14 -82 59.5 -136 t136.5 -80l16 98q-7 6 -18 17t-34 48t-33 77q-15 73 -14 143.5t10 122.5l9 51q-92 -110 -119.5 -185t-12.5 -156z" />
|
||||
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5q366 -6 397 -14l-186 -186h-311q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v125l200 200v-225q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5 t-117.5 282.5zM436 341l161 50l412 412l-114 113l-405 -405zM995 1015l113 -113l113 113l-21 85l-92 28z" />
|
||||
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h261l2 -80q-133 -32 -218 -120h-145q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5l200 153v-53q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5 zM423 524q30 38 81.5 64t103 35.5t99 14t77.5 3.5l29 -1v-209l360 324l-359 318v-216q-7 0 -19 -1t-48 -8t-69.5 -18.5t-76.5 -37t-76.5 -59t-62 -88t-39.5 -121.5z" />
|
||||
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q61 0 127 -23l-178 -177h-349q-41 0 -70.5 -29.5t-29.5 -70.5v-500q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v69l200 200v-169q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5 t-117.5 282.5zM342 632l283 -284l567 567l-137 137l-430 -431l-146 147z" />
|
||||
<glyph unicode="" d="M0 603l300 296v-198h200v200h-200l300 300l295 -300h-195v-200h200v198l300 -296l-300 -300v198h-200v-200h195l-295 -300l-300 300h200v200h-200v-198z" />
|
||||
<glyph unicode="" d="M200 50v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-1100l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||
<glyph unicode="" d="M0 50v1000q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-437l500 487v-487l500 487v-1100l-500 488v-488l-500 488v-438q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5z" />
|
||||
<glyph unicode="" d="M136 550l564 550v-487l500 487v-1100l-500 488v-488z" />
|
||||
<glyph unicode="" d="M200 0l900 550l-900 550v-1100z" />
|
||||
<glyph unicode="" d="M200 150q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v800q0 21 -14.5 35.5t-35.5 14.5h-200q-21 0 -35.5 -14.5t-14.5 -35.5v-800zM600 150q0 -21 14.5 -35.5t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v800q0 21 -14.5 35.5t-35.5 14.5h-200 q-21 0 -35.5 -14.5t-14.5 -35.5v-800z" />
|
||||
<glyph unicode="" d="M200 150q0 -20 14.5 -35t35.5 -15h800q21 0 35.5 15t14.5 35v800q0 21 -14.5 35.5t-35.5 14.5h-800q-21 0 -35.5 -14.5t-14.5 -35.5v-800z" />
|
||||
<glyph unicode="" d="M0 0v1100l500 -487v487l564 -550l-564 -550v488z" />
|
||||
<glyph unicode="" d="M0 0v1100l500 -487v487l500 -487v437q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438l-500 -488v488z" />
|
||||
<glyph unicode="" d="M300 0v1100l500 -487v437q0 21 14.5 35.5t35.5 14.5h100q21 0 35.5 -14.5t14.5 -35.5v-1000q0 -21 -14.5 -35.5t-35.5 -14.5h-100q-21 0 -35.5 14.5t-14.5 35.5v438z" />
|
||||
<glyph unicode="" d="M100 250v100q0 21 14.5 35.5t35.5 14.5h1000q21 0 35.5 -14.5t14.5 -35.5v-100q0 -21 -14.5 -35.5t-35.5 -14.5h-1000q-21 0 -35.5 14.5t-14.5 35.5zM100 500h1100l-550 564z" />
|
||||
<glyph unicode="" d="M185 599l592 -592l240 240l-353 353l353 353l-240 240z" />
|
||||
<glyph unicode="" d="M272 194l353 353l-353 353l241 240l572 -571l21 -22l-1 -1v-1l-592 -591z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM300 500h200v-200h200v200h200v200h-200v200h-200v-200h-200v-200z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM300 500h600v200h-600v-200z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM246 459l213 -213l141 142l141 -142l213 213l-142 141l142 141l-213 212l-141 -141l-141 142l-212 -213l141 -141 z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM270 551l276 -277l411 411l-175 174l-236 -236l-102 102z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM364 700h143q4 0 11.5 -1t11 -1t6.5 3t3 9t1 11t3.5 8.5t3.5 6t5.5 4t6.5 2.5t9 1.5t9 0.5h11.5h12.5 q19 0 30 -10t11 -26q0 -22 -4 -28t-27 -22q-5 -1 -12.5 -3t-27 -13.5t-34 -27t-26.5 -46t-11 -68.5h200q5 3 14 8t31.5 25.5t39.5 45.5t31 69t14 94q0 51 -17.5 89t-42 58t-58.5 32t-58.5 15t-51.5 3q-50 0 -90.5 -12t-75 -38.5t-53.5 -74.5t-19 -114zM500 300h200v100h-200 v-100z" />
|
||||
<glyph unicode="" d="M3 600q0 162 80 299.5t217.5 217.5t299.5 80t299.5 -80t217.5 -217.5t80 -299.5t-80 -299.5t-217.5 -217.5t-299.5 -80t-299.5 80t-217.5 217.5t-80 299.5zM400 300h400v100h-100v300h-300v-100h100v-200h-100v-100zM500 800h200v100h-200v-100z" />
|
||||
<glyph unicode="" d="M0 500v200h195q31 125 98.5 199.5t206.5 100.5v200h200v-200q54 -20 113 -60t112.5 -105.5t71.5 -134.5h203v-200h-203q-25 -102 -116.5 -186t-180.5 -117v-197h-200v197q-140 27 -208 102.5t-98 200.5h-194zM290 500q24 -73 79.5 -127.5t130.5 -78.5v206h200v-206 q149 48 201 206h-201v200h200q-25 74 -75.5 127t-124.5 77v-204h-200v203q-75 -23 -130 -77t-79 -126h209v-200h-210z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM356 465l135 135 l-135 135l109 109l135 -135l135 135l109 -109l-135 -135l135 -135l-109 -109l-135 135l-135 -135z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM322 537l141 141 l87 -87l204 205l142 -142l-346 -345z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -115 62 -215l568 567q-100 62 -216 62q-171 0 -292.5 -121.5t-121.5 -292.5zM391 245q97 -59 209 -59q171 0 292.5 121.5t121.5 292.5 q0 112 -59 209z" />
|
||||
<glyph unicode="" d="M0 547l600 453v-300h600v-300h-600v-301z" />
|
||||
<glyph unicode="" d="M0 400v300h600v300l600 -453l-600 -448v301h-600z" />
|
||||
<glyph unicode="" d="M204 600l450 600l444 -600h-298v-600h-300v600h-296z" />
|
||||
<glyph unicode="" d="M104 600h296v600h300v-600h298l-449 -600z" />
|
||||
<glyph unicode="" d="M0 200q6 132 41 238.5t103.5 193t184 138t271.5 59.5v271l600 -453l-600 -448v301q-95 -2 -183 -20t-170 -52t-147 -92.5t-100 -135.5z" />
|
||||
<glyph unicode="" d="M0 0v400l129 -129l294 294l142 -142l-294 -294l129 -129h-400zM635 777l142 -142l294 294l129 -129v400h-400l129 -129z" />
|
||||
<glyph unicode="" d="M34 176l295 295l-129 129h400v-400l-129 130l-295 -295zM600 600v400l129 -129l295 295l142 -141l-295 -295l129 -130h-400z" />
|
||||
<glyph unicode="" d="M23 600q0 118 45.5 224.5t123 184t184 123t224.5 45.5t224.5 -45.5t184 -123t123 -184t45.5 -224.5t-45.5 -224.5t-123 -184t-184 -123t-224.5 -45.5t-224.5 45.5t-184 123t-123 184t-45.5 224.5zM456 851l58 -302q4 -20 21.5 -34.5t37.5 -14.5h54q20 0 37.5 14.5 t21.5 34.5l58 302q4 20 -8 34.5t-32 14.5h-207q-21 0 -33 -14.5t-8 -34.5zM500 300h200v100h-200v-100z" />
|
||||
<glyph unicode="" d="M0 800h100v-200h400v300h200v-300h400v200h100v100h-111q1 1 1 6.5t-1.5 15t-3.5 17.5l-34 172q-11 39 -41.5 63t-69.5 24q-32 0 -61 -17l-239 -144q-22 -13 -40 -35q-19 24 -40 36l-238 144q-33 18 -62 18q-39 0 -69.5 -23t-40.5 -61l-35 -177q-2 -8 -3 -18t-1 -15v-6 h-111v-100zM100 0h400v400h-400v-400zM200 900q-3 0 14 48t36 96l18 47l213 -191h-281zM700 0v400h400v-400h-400zM731 900l202 197q5 -12 12 -32.5t23 -64t25 -72t7 -28.5h-269z" />
|
||||
<glyph unicode="" d="M0 -22v143l216 193q-9 53 -13 83t-5.5 94t9 113t38.5 114t74 124q47 60 99.5 102.5t103 68t127.5 48t145.5 37.5t184.5 43.5t220 58.5q0 -189 -22 -343t-59 -258t-89 -181.5t-108.5 -120t-122 -68t-125.5 -30t-121.5 -1.5t-107.5 12.5t-87.5 17t-56.5 7.5l-99 -55z M238.5 300.5q19.5 -6.5 86.5 76.5q55 66 367 234q70 38 118.5 69.5t102 79t99 111.5t86.5 148q22 50 24 60t-6 19q-7 5 -17 5t-26.5 -14.5t-33.5 -39.5q-35 -51 -113.5 -108.5t-139.5 -89.5l-61 -32q-369 -197 -458 -401q-48 -111 -28.5 -117.5z" />
|
||||
<glyph unicode="" d="M111 408q0 -33 5 -63q9 -56 44 -119.5t105 -108.5q31 -21 64 -16t62 23.5t57 49.5t48 61.5t35 60.5q32 66 39 184.5t-13 157.5q79 -80 122 -164t26 -184q-5 -33 -20.5 -69.5t-37.5 -80.5q-10 -19 -14.5 -29t-12 -26t-9 -23.5t-3 -19t2.5 -15.5t11 -9.5t19.5 -5t30.5 2.5 t42 8q57 20 91 34t87.5 44.5t87 64t65.5 88.5t47 122q38 172 -44.5 341.5t-246.5 278.5q22 -44 43 -129q39 -159 -32 -154q-15 2 -33 9q-79 33 -120.5 100t-44 175.5t48.5 257.5q-13 -8 -34 -23.5t-72.5 -66.5t-88.5 -105.5t-60 -138t-8 -166.5q2 -12 8 -41.5t8 -43t6 -39.5 t3.5 -39.5t-1 -33.5t-6 -31.5t-13.5 -24t-21 -20.5t-31 -12q-38 -10 -67 13t-40.5 61.5t-15 81.5t10.5 75q-52 -46 -83.5 -101t-39 -107t-7.5 -85z" />
|
||||
<glyph unicode="" d="M-61 600l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5t145.5 -23.5t132.5 -59t116.5 -83.5t97 -90t74.5 -85.5t49 -63.5t20 -30l26 -40l-26 -40q-6 -10 -20 -30t-49 -63.5t-74.5 -85.5t-97 -90t-116.5 -83.5t-132.5 -59t-145.5 -23.5 t-145.5 23.5t-132.5 59t-116.5 83.5t-97 90t-74.5 85.5t-49 63.5t-20 30zM120 600q7 -10 40.5 -58t56 -78.5t68 -77.5t87.5 -75t103 -49.5t125 -21.5t123.5 20t100.5 45.5t85.5 71.5t66.5 75.5t58 81.5t47 66q-1 1 -28.5 37.5t-42 55t-43.5 53t-57.5 63.5t-58.5 54 q49 -74 49 -163q0 -124 -88 -212t-212 -88t-212 88t-88 212q0 85 46 158q-102 -87 -226 -258zM377 656q49 -124 154 -191l105 105q-37 24 -75 72t-57 84l-20 36z" />
|
||||
<glyph unicode="" d="M-61 600l26 40q6 10 20 30t49 63.5t74.5 85.5t97 90t116.5 83.5t132.5 59t145.5 23.5q61 0 121 -17l37 142h148l-314 -1200h-148l37 143q-82 21 -165 71.5t-140 102t-109.5 112t-72 88.5t-29.5 43zM120 600q210 -282 393 -336l37 141q-107 18 -178.5 101.5t-71.5 193.5 q0 85 46 158q-102 -87 -226 -258zM377 656q49 -124 154 -191l47 47l23 87q-30 28 -59 69t-44 68l-14 26zM780 161l38 145q22 15 44.5 34t46 44t40.5 44t41 50.5t33.5 43.5t33 44t24.5 34q-97 127 -140 175l39 146q67 -54 131.5 -125.5t87.5 -103.5t36 -52l26 -40l-26 -40 q-7 -12 -25.5 -38t-63.5 -79.5t-95.5 -102.5t-124 -100t-146.5 -79z" />
|
||||
<glyph unicode="" d="M-97.5 34q13.5 -34 50.5 -34h1294q37 0 50.5 35.5t-7.5 67.5l-642 1056q-20 34 -48 36.5t-48 -29.5l-642 -1066q-21 -32 -7.5 -66zM155 200l445 723l445 -723h-345v100h-200v-100h-345zM500 600l100 -300l100 300v100h-200v-100z" />
|
||||
<glyph unicode="" d="M100 262v41q0 20 11 44.5t26 38.5l363 325v339q0 62 44 106t106 44t106 -44t44 -106v-339l363 -325q15 -14 26 -38.5t11 -44.5v-41q0 -20 -12 -26.5t-29 5.5l-359 249v-263q100 -91 100 -113v-64q0 -20 -13 -28.5t-32 0.5l-94 78h-222l-94 -78q-19 -9 -32 -0.5t-13 28.5 v64q0 22 100 113v263l-359 -249q-17 -12 -29 -5.5t-12 26.5z" />
|
||||
<glyph unicode="" d="M0 50q0 -20 14.5 -35t35.5 -15h1000q21 0 35.5 15t14.5 35v750h-1100v-750zM0 900h1100v150q0 21 -14.5 35.5t-35.5 14.5h-150v100h-100v-100h-500v100h-100v-100h-150q-21 0 -35.5 -14.5t-14.5 -35.5v-150zM100 100v100h100v-100h-100zM100 300v100h100v-100h-100z M100 500v100h100v-100h-100zM300 100v100h100v-100h-100zM300 300v100h100v-100h-100zM300 500v100h100v-100h-100zM500 100v100h100v-100h-100zM500 300v100h100v-100h-100zM500 500v100h100v-100h-100zM700 100v100h100v-100h-100zM700 300v100h100v-100h-100zM700 500 v100h100v-100h-100zM900 100v100h100v-100h-100zM900 300v100h100v-100h-100zM900 500v100h100v-100h-100z" />
|
||||
<glyph unicode="" d="M0 200v200h259l600 600h241v198l300 -295l-300 -300v197h-159l-600 -600h-341zM0 800h259l122 -122l141 142l-181 180h-341v-200zM678 381l141 142l122 -123h159v198l300 -295l-300 -300v197h-241z" />
|
||||
<glyph unicode="" d="M0 400v600q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-596l-304 -300v300h-100q-41 0 -70.5 29.5t-29.5 70.5z" />
|
||||
<glyph unicode="" d="M100 600v200h300v-250q0 -113 6 -145q17 -92 102 -117q39 -11 92 -11q37 0 66.5 5.5t50 15.5t36 24t24 31.5t14 37.5t7 42t2.5 45t0 47v25v250h300v-200q0 -42 -3 -83t-15 -104t-31.5 -116t-58 -109.5t-89 -96.5t-129 -65.5t-174.5 -25.5t-174.5 25.5t-129 65.5t-89 96.5 t-58 109.5t-31.5 116t-15 104t-3 83zM100 900v300h300v-300h-300zM800 900v300h300v-300h-300z" />
|
||||
<glyph unicode="" d="M-30 411l227 -227l352 353l353 -353l226 227l-578 579z" />
|
||||
<glyph unicode="" d="M70 797l580 -579l578 579l-226 227l-353 -353l-352 353z" />
|
||||
<glyph unicode="" d="M-198 700l299 283l300 -283h-203v-400h385l215 -200h-800v600h-196zM402 1000l215 -200h381v-400h-198l299 -283l299 283h-200v600h-796z" />
|
||||
<glyph unicode="" d="M18 939q-5 24 10 42q14 19 39 19h896l38 162q5 17 18.5 27.5t30.5 10.5h94q20 0 35 -14.5t15 -35.5t-15 -35.5t-35 -14.5h-54l-201 -961q-2 -4 -6 -10.5t-19 -17.5t-33 -11h-31v-50q0 -20 -14.5 -35t-35.5 -15t-35.5 15t-14.5 35v50h-300v-50q0 -20 -14.5 -35t-35.5 -15 t-35.5 15t-14.5 35v50h-50q-21 0 -35.5 15t-14.5 35q0 21 14.5 35.5t35.5 14.5h535l48 200h-633q-32 0 -54.5 21t-27.5 43z" />
|
||||
<glyph unicode="" d="M0 0v800h1200v-800h-1200zM0 900v100h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-100h-1200z" />
|
||||
<glyph unicode="" d="M1 0l300 700h1200l-300 -700h-1200zM1 400v600h200q0 41 29.5 70.5t70.5 29.5h300q41 0 70.5 -29.5t29.5 -70.5h500v-200h-1000z" />
|
||||
<glyph unicode="" d="M302 300h198v600h-198l298 300l298 -300h-198v-600h198l-298 -300z" />
|
||||
<glyph unicode="" d="M0 600l300 298v-198h600v198l300 -298l-300 -297v197h-600v-197z" />
|
||||
<glyph unicode="" d="M0 100v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM31 400l172 739q5 22 23 41.5t38 19.5h672q19 0 37.5 -22.5t23.5 -45.5l172 -732h-1138zM800 100h100v100h-100v-100z M1000 100h100v100h-100v-100z" />
|
||||
<glyph unicode="" d="M-101 600v50q0 24 25 49t50 38l25 13v-250l-11 5.5t-24 14t-30 21.5t-24 27.5t-11 31.5zM100 500v250v8v8v7t0.5 7t1.5 5.5t2 5t3 4t4.5 3.5t6 1.5t7.5 0.5h200l675 250v-850l-675 200h-38l47 -276q2 -12 -3 -17.5t-11 -6t-21 -0.5h-8h-83q-20 0 -34.5 14t-18.5 35 q-55 337 -55 351zM1100 200v850q0 21 14.5 35.5t35.5 14.5q20 0 35 -14.5t15 -35.5v-850q0 -20 -15 -35t-35 -15q-21 0 -35.5 15t-14.5 35z" />
|
||||
<glyph unicode="" d="M74 350q0 21 13.5 35.5t33.5 14.5h18l117 173l63 327q15 77 76 140t144 83l-18 32q-6 19 3 32t29 13h94q20 0 29 -10.5t3 -29.5q-18 -36 -18 -37q83 -19 144 -82.5t76 -140.5l63 -327l118 -173h17q20 0 33.5 -14.5t13.5 -35.5q0 -20 -13 -40t-31 -27q-8 -3 -23 -8.5 t-65 -20t-103 -25t-132.5 -19.5t-158.5 -9q-125 0 -245.5 20.5t-178.5 40.5l-58 20q-18 7 -31 27.5t-13 40.5zM497 110q12 -49 40 -79.5t63 -30.5t63 30.5t39 79.5q-48 -6 -102 -6t-103 6z" />
|
||||
<glyph unicode="" d="M21 445l233 -45l-78 -224l224 78l45 -233l155 179l155 -179l45 233l224 -78l-78 224l234 45l-180 155l180 156l-234 44l78 225l-224 -78l-45 233l-155 -180l-155 180l-45 -233l-224 78l78 -225l-233 -44l179 -156z" />
|
||||
<glyph unicode="" d="M0 200h200v600h-200v-600zM300 275q0 -75 100 -75h61q124 -100 139 -100h250q46 0 83 57l238 344q29 31 29 74v100q0 44 -30.5 84.5t-69.5 40.5h-328q28 118 28 125v150q0 44 -30.5 84.5t-69.5 40.5h-50q-27 0 -51 -20t-38 -48l-96 -198l-145 -196q-20 -26 -20 -63v-400z M400 300v375l150 213l100 212h50v-175l-50 -225h450v-125l-250 -375h-214l-136 100h-100z" />
|
||||
<glyph unicode="" d="M0 400v600h200v-600h-200zM300 525v400q0 75 100 75h61q124 100 139 100h250q46 0 83 -57l238 -344q29 -31 29 -74v-100q0 -44 -30.5 -84.5t-69.5 -40.5h-328q28 -118 28 -125v-150q0 -44 -30.5 -84.5t-69.5 -40.5h-50q-27 0 -51 20t-38 48l-96 198l-145 196 q-20 26 -20 63zM400 525l150 -212l100 -213h50v175l-50 225h450v125l-250 375h-214l-136 -100h-100v-375z" />
|
||||
<glyph unicode="" d="M8 200v600h200v-600h-200zM308 275v525q0 17 14 35.5t28 28.5l14 9l362 230q14 6 25 6q17 0 29 -12l109 -112q14 -14 14 -34q0 -18 -11 -32l-85 -121h302q85 0 138.5 -38t53.5 -110t-54.5 -111t-138.5 -39h-107l-130 -339q-7 -22 -20.5 -41.5t-28.5 -19.5h-341 q-7 0 -90 81t-83 94zM408 289l100 -89h293l131 339q6 21 19.5 41t28.5 20h203q16 0 25 15t9 36q0 20 -9 34.5t-25 14.5h-457h-6.5h-7.5t-6.5 0.5t-6 1t-5 1.5t-5.5 2.5t-4 4t-4 5.5q-5 12 -5 20q0 14 10 27l147 183l-86 83l-339 -236v-503z" />
|
||||
<glyph unicode="" d="M-101 651q0 72 54 110t139 38l302 -1l-85 121q-11 16 -11 32q0 21 14 34l109 113q13 12 29 12q11 0 25 -6l365 -230q7 -4 17 -10.5t26.5 -26t16.5 -36.5v-526q0 -13 -86 -93.5t-94 -80.5h-341q-16 0 -29.5 20t-19.5 41l-130 339h-107q-84 0 -139 39t-55 111zM-1 601h222 q15 0 28.5 -20.5t19.5 -40.5l131 -339h293l107 89v502l-343 237l-87 -83l145 -184q10 -11 10 -26q0 -11 -5 -20q-1 -3 -3.5 -5.5l-4 -4t-5 -2.5t-5.5 -1.5t-6.5 -1t-6.5 -0.5h-7.5h-6.5h-476v-100zM1000 201v600h200v-600h-200z" />
|
||||
<glyph unicode="" d="M97 719l230 -363q4 -6 10.5 -15.5t26 -25t36.5 -15.5h525q13 0 94 83t81 90v342q0 15 -20 28.5t-41 19.5l-339 131v106q0 84 -39 139t-111 55t-110 -53.5t-38 -138.5v-302l-121 84q-15 12 -33.5 11.5t-32.5 -13.5l-112 -110q-22 -22 -6 -53zM172 739l83 86l183 -146 q22 -18 47 -5q3 1 5.5 3.5l4 4t2.5 5t1.5 5.5t1 6.5t0.5 6.5v7.5v6.5v456q0 22 25 31t50 -0.5t25 -30.5v-202q0 -16 20 -29.5t41 -19.5l339 -130v-294l-89 -100h-503zM400 0v200h600v-200h-600z" />
|
||||
<glyph unicode="" d="M2 585q-16 -31 6 -53l112 -110q13 -13 32 -13.5t34 10.5l121 85q0 -51 -0.5 -153.5t-0.5 -148.5q0 -84 38.5 -138t110.5 -54t111 55t39 139v106l339 131q20 6 40.5 19.5t20.5 28.5v342q0 7 -81 90t-94 83h-525q-17 0 -35.5 -14t-28.5 -28l-10 -15zM77 565l236 339h503 l89 -100v-294l-340 -130q-20 -6 -40 -20t-20 -29v-202q0 -22 -25 -31t-50 0t-25 31v456v14.5t-1.5 11.5t-5 12t-9.5 7q-24 13 -46 -5l-184 -146zM305 1104v200h600v-200h-600z" />
|
||||
<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM298 701l2 -201h300l-2 -194l402 294l-402 298v-197h-300z" />
|
||||
<glyph unicode="" d="M0 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t231.5 47.5q122 0 232.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-218 -217.5t-300 -80t-299.5 80t-217.5 217.5t-80 299.5zM200 600l402 -294l-2 194h300l2 201h-300v197z" />
|
||||
<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM300 600h200v-300h200v300h200l-300 400z" />
|
||||
<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q162 0 299.5 -80t217.5 -218t80 -300t-80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM300 600l300 -400l300 400h-200v300h-200v-300h-200z" />
|
||||
<glyph unicode="" d="M5 597q0 122 47.5 232.5t127.5 190.5t190.5 127.5t232.5 47.5q121 0 231.5 -47.5t190.5 -127.5t127.5 -190.5t47.5 -232.5q0 -162 -80 -299.5t-217.5 -217.5t-299.5 -80t-300 80t-218 217.5t-80 299.5zM254 780q-8 -33 5.5 -92.5t7.5 -87.5q0 -9 17 -44t16 -60 q12 0 23 -5.5t23 -15t20 -13.5q24 -12 108 -42q22 -8 53 -31.5t59.5 -38.5t57.5 -11q8 -18 -15 -55t-20 -57q42 -71 87 -80q0 -6 -3 -15.5t-3.5 -14.5t4.5 -17q104 -3 221 112q30 29 47 47t34.5 49t20.5 62q-14 9 -37 9.5t-36 7.5q-14 7 -49 15t-52 19q-9 0 -39.5 -0.5 t-46.5 -1.5t-39 -6.5t-39 -16.5q-50 -35 -66 -12q-4 2 -3.5 25.5t0.5 25.5q-6 13 -26.5 17t-24.5 7q2 22 -2 41t-16.5 28t-38.5 -20q-23 -25 -42 4q-19 28 -8 58q6 16 22 22q6 -1 26 -1.5t33.5 -4t19.5 -13.5q12 -19 32 -37.5t34 -27.5l14 -8q0 3 9.5 39.5t5.5 57.5 q-4 23 14.5 44.5t22.5 31.5q5 14 10 35t8.5 31t15.5 22.5t34 21.5q-6 18 10 37q8 0 23.5 -1.5t24.5 -1.5t20.5 4.5t20.5 15.5q-10 23 -30.5 42.5t-38 30t-49 26.5t-43.5 23q11 39 2 44q31 -13 58 -14.5t39 3.5l11 4q7 36 -16.5 53.5t-64.5 28.5t-56 23q-19 -3 -37 0 q-15 -12 -36.5 -21t-34.5 -12t-44 -8t-39 -6q-15 -3 -45.5 0.5t-45.5 -2.5q-21 -7 -52 -26.5t-34 -34.5q-3 -11 6.5 -22.5t8.5 -18.5q-3 -34 -27.5 -90.5t-29.5 -79.5zM518 916q3 12 16 30t16 25q10 -10 18.5 -10t14 6t14.5 14.5t16 12.5q0 -24 17 -66.5t17 -43.5 q-9 2 -31 5t-36 5t-32 8t-30 14zM692 1003h1h-1z" />
|
||||
<glyph unicode="" d="M0 164.5q0 21.5 15 37.5l600 599q-33 101 6 201.5t135 154.5q164 92 306 -9l-259 -138l145 -232l251 126q13 -175 -151 -267q-123 -70 -253 -23l-596 -596q-15 -16 -36.5 -16t-36.5 16l-111 110q-15 15 -15 36.5z" />
|
||||
<glyph unicode="" horiz-adv-x="1220" d="M0 196v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM0 596v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000 q-41 0 -70.5 29.5t-29.5 70.5zM0 996v100q0 41 29.5 70.5t70.5 29.5h1000q41 0 70.5 -29.5t29.5 -70.5v-100q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM600 596h500v100h-500v-100zM800 196h300v100h-300v-100zM900 996h200v100h-200v-100z" />
|
||||
<glyph unicode="" d="M100 1100v100h1000v-100h-1000zM150 1000h900l-350 -500v-300l-200 -200v500z" />
|
||||
<glyph unicode="" d="M0 200v200h1200v-200q0 -41 -29.5 -70.5t-70.5 -29.5h-1000q-41 0 -70.5 29.5t-29.5 70.5zM0 500v400q0 41 29.5 70.5t70.5 29.5h300v100q0 41 29.5 70.5t70.5 29.5h200q41 0 70.5 -29.5t29.5 -70.5v-100h300q41 0 70.5 -29.5t29.5 -70.5v-400h-500v100h-200v-100h-500z M500 1000h200v100h-200v-100z" />
|
||||
<glyph unicode="" d="M0 0v400l129 -129l200 200l142 -142l-200 -200l129 -129h-400zM0 800l129 129l200 -200l142 142l-200 200l129 129h-400v-400zM729 329l142 142l200 -200l129 129v-400h-400l129 129zM729 871l200 200l-129 129h400v-400l-129 129l-200 -200z" />
|
||||
<glyph unicode="" d="M0 596q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM182 596q0 -172 121.5 -293t292.5 -121t292.5 121t121.5 293q0 171 -121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM291 655 q0 23 15.5 38.5t38.5 15.5t39 -16t16 -38q0 -23 -16 -39t-39 -16q-22 0 -38 16t-16 39zM400 850q0 22 16 38.5t39 16.5q22 0 38 -16t16 -39t-16 -39t-38 -16q-23 0 -39 16.5t-16 38.5zM514 609q0 32 20.5 56.5t51.5 29.5l122 126l1 1q-9 14 -9 28q0 22 16 38.5t39 16.5 q22 0 38 -16t16 -39t-16 -39t-38 -16q-14 0 -29 10l-55 -145q17 -22 17 -51q0 -36 -25.5 -61.5t-61.5 -25.5t-61.5 25.5t-25.5 61.5zM800 655q0 22 16 38t39 16t38.5 -15.5t15.5 -38.5t-16 -39t-38 -16q-23 0 -39 16t-16 39z" />
|
||||
<glyph unicode="" d="M-40 375q-13 -95 35 -173q35 -57 94 -89t129 -32q63 0 119 28q33 16 65 40.5t52.5 45.5t59.5 64q40 44 57 61l394 394q35 35 47 84t-3 96q-27 87 -117 104q-20 2 -29 2q-46 0 -78.5 -16.5t-67.5 -51.5l-389 -396l-7 -7l69 -67l377 373q20 22 39 38q23 23 50 23 q38 0 53 -36q16 -39 -20 -75l-547 -547q-52 -52 -125 -52q-55 0 -100 33t-54 96q-5 35 2.5 66t31.5 63t42 50t56 54q24 21 44 41l348 348q52 52 82.5 79.5t84 54t107.5 26.5q25 0 48 -4q95 -17 154 -94.5t51 -175.5q-7 -101 -98 -192l-252 -249l-253 -256l7 -7l69 -60 l517 511q67 67 95 157t11 183q-16 87 -67 154t-130 103q-69 33 -152 33q-107 0 -197 -55q-40 -24 -111 -95l-512 -512q-68 -68 -81 -163z" />
|
||||
<glyph unicode="" d="M80 784q0 131 98.5 229.5t230.5 98.5q143 0 241 -129q103 129 246 129q129 0 226 -98.5t97 -229.5q0 -46 -17.5 -91t-61 -99t-77 -89.5t-104.5 -105.5q-197 -191 -293 -322l-17 -23l-16 23q-43 58 -100 122.5t-92 99.5t-101 100q-71 70 -104.5 105.5t-77 89.5t-61 99 t-17.5 91zM250 784q0 -27 30.5 -70t61.5 -75.5t95 -94.5l22 -22q93 -90 190 -201q82 92 195 203l12 12q64 62 97.5 97t64.5 79t31 72q0 71 -48 119.5t-105 48.5q-74 0 -132 -83l-118 -171l-114 174q-51 80 -123 80q-60 0 -109.5 -49.5t-49.5 -118.5z" />
|
||||
<glyph unicode="" d="M57 353q0 -95 66 -159l141 -142q68 -66 159 -66q93 0 159 66l283 283q66 66 66 159t-66 159l-141 141q-8 9 -19 17l-105 -105l212 -212l-389 -389l-247 248l95 95l-18 18q-46 45 -75 101l-55 -55q-66 -66 -66 -159zM269 706q0 -93 66 -159l141 -141q7 -7 19 -17l105 105 l-212 212l389 389l247 -247l-95 -96l18 -17q47 -49 77 -100l29 29q35 35 62.5 88t27.5 96q0 93 -66 159l-141 141q-66 66 -159 66q-95 0 -159 -66l-283 -283q-66 -64 -66 -159z" />
|
||||
<glyph unicode="" d="M200 100v953q0 21 30 46t81 48t129 38t163 15t162 -15t127 -38t79 -48t29 -46v-953q0 -41 -29.5 -70.5t-70.5 -29.5h-600q-41 0 -70.5 29.5t-29.5 70.5zM300 300h600v700h-600v-700zM496 150q0 -43 30.5 -73.5t73.5 -30.5t73.5 30.5t30.5 73.5t-30.5 73.5t-73.5 30.5 t-73.5 -30.5t-30.5 -73.5z" />
|
||||
<glyph unicode="" d="M0 0l303 380l207 208l-210 212h300l267 279l-35 36q-15 14 -15 35t15 35q14 15 35 15t35 -15l283 -282q15 -15 15 -36t-15 -35q-14 -15 -35 -15t-35 15l-36 35l-279 -267v-300l-212 210l-208 -207z" />
|
||||
<glyph unicode="" d="M295 433h139q5 -77 48.5 -126.5t117.5 -64.5v335q-6 1 -15.5 4t-11.5 3q-46 14 -79 26.5t-72 36t-62.5 52t-40 72.5t-16.5 99q0 92 44 159.5t109 101t144 40.5v78h100v-79q38 -4 72.5 -13.5t75.5 -31.5t71 -53.5t51.5 -84t24.5 -118.5h-159q-8 72 -35 109.5t-101 50.5 v-307l64 -14q34 -7 64 -16.5t70 -31.5t67.5 -52t47.5 -80.5t20 -112.5q0 -139 -89 -224t-244 -96v-77h-100v78q-152 17 -237 104q-40 40 -52.5 93.5t-15.5 139.5zM466 889q0 -29 8 -51t16.5 -34t29.5 -22.5t31 -13.5t38 -10q7 -2 11 -3v274q-61 -8 -97.5 -37.5t-36.5 -102.5 zM700 237q170 18 170 151q0 64 -44 99.5t-126 60.5v-311z" />
|
||||
<glyph unicode="" d="M100 600v100h166q-24 49 -44 104q-10 26 -14.5 55.5t-3 72.5t25 90t68.5 87q97 88 263 88q129 0 230 -89t101 -208h-153q0 52 -34 89.5t-74 51.5t-76 14q-37 0 -79 -14.5t-62 -35.5q-41 -44 -41 -101q0 -28 16.5 -69.5t28 -62.5t41.5 -72h241v-100h-197q8 -50 -2.5 -115 t-31.5 -94q-41 -59 -99 -113q35 11 84 18t70 7q33 1 103 -16t103 -17q76 0 136 30l50 -147q-41 -25 -80.5 -36.5t-59 -13t-61.5 -1.5q-23 0 -128 33t-155 29q-39 -4 -82 -17t-66 -25l-24 -11l-55 145l16.5 11t15.5 10t13.5 9.5t14.5 12t14.5 14t17.5 18.5q48 55 54 126.5 t-30 142.5h-221z" />
|
||||
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM602 900l298 300l298 -300h-198v-900h-200v900h-198z" />
|
||||
<glyph unicode="" d="M2 300h198v900h200v-900h198l-298 -300zM700 0v200h100v-100h200v-100h-300zM700 400v100h300v-200h-99v-100h-100v100h99v100h-200zM700 700v500h300v-500h-100v100h-100v-100h-100zM801 900h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M2 300h198v900h200v-900h198l-298 -300zM700 0v500h300v-500h-100v100h-100v-100h-100zM700 700v200h100v-100h200v-100h-300zM700 1100v100h300v-200h-99v-100h-100v100h99v100h-200zM801 200h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM800 100v400h300v-500h-100v100h-200zM800 1100v100h200v-500h-100v400h-100zM901 200h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM800 400v100h200v-500h-100v400h-100zM800 800v400h300v-500h-100v100h-200zM901 900h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM700 100v200h500v-200h-500zM700 400v200h400v-200h-400zM700 700v200h300v-200h-300zM700 1000v200h200v-200h-200z" />
|
||||
<glyph unicode="" d="M2 300l298 -300l298 300h-198v900h-200v-900h-198zM700 100v200h200v-200h-200zM700 400v200h300v-200h-300zM700 700v200h400v-200h-400zM700 1000v200h500v-200h-500z" />
|
||||
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q162 0 281 -118.5t119 -281.5v-300q0 -165 -118.5 -282.5t-281.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500z" />
|
||||
<glyph unicode="" d="M0 400v300q0 163 119 281.5t281 118.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-163 0 -281.5 117.5t-118.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM400 300l333 250l-333 250v-500z" />
|
||||
<glyph unicode="" d="M0 400v300q0 163 117.5 281.5t282.5 118.5h300q163 0 281.5 -119t118.5 -281v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-300q-165 0 -282.5 117.5t-117.5 282.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM300 700l250 -333l250 333h-500z" />
|
||||
<glyph unicode="" d="M0 400v300q0 165 117.5 282.5t282.5 117.5h300q165 0 282.5 -117.5t117.5 -282.5v-300q0 -162 -118.5 -281t-281.5 -119h-300q-165 0 -282.5 118.5t-117.5 281.5zM200 300q0 -41 29.5 -70.5t70.5 -29.5h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5 h-500q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM300 400h500l-250 333z" />
|
||||
<glyph unicode="" d="M0 400v300h300v200l400 -350l-400 -350v200h-300zM500 0v200h500q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-500v200h400q165 0 282.5 -117.5t117.5 -282.5v-300q0 -165 -117.5 -282.5t-282.5 -117.5h-400z" />
|
||||
<glyph unicode="" d="M217 519q8 -19 31 -19h302q-155 -438 -160 -458q-5 -21 4 -32l9 -8h9q14 0 26 15q11 13 274.5 321.5t264.5 308.5q14 19 5 36q-8 17 -31 17l-301 -1q1 4 78 219.5t79 227.5q2 15 -5 27l-9 9h-9q-15 0 -25 -16q-4 -6 -98 -111.5t-228.5 -257t-209.5 -237.5q-16 -19 -6 -41 z" />
|
||||
<glyph unicode="" d="M0 400q0 -165 117.5 -282.5t282.5 -117.5h300q47 0 100 15v185h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h500v185q-14 4 -114 7.5t-193 5.5l-93 2q-165 0 -282.5 -117.5t-117.5 -282.5v-300zM600 400v300h300v200l400 -350l-400 -350v200h-300z " />
|
||||
<glyph unicode="" d="M0 400q0 -165 117.5 -282.5t282.5 -117.5h300q163 0 281.5 117.5t118.5 282.5v98l-78 73l-122 -123v-148q0 -41 -29.5 -70.5t-70.5 -29.5h-500q-41 0 -70.5 29.5t-29.5 70.5v500q0 41 29.5 70.5t70.5 29.5h156l118 122l-74 78h-100q-165 0 -282.5 -117.5t-117.5 -282.5 v-300zM496 709l353 342l-149 149h500v-500l-149 149l-342 -353z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM406 600 q0 80 57 137t137 57t137 -57t57 -137t-57 -137t-137 -57t-137 57t-57 137z" />
|
||||
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 800l445 -500l450 500h-295v400h-300v-400h-300zM900 150h100v50h-100v-50z" />
|
||||
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 700h300v-300h300v300h295l-445 500zM900 150h100v50h-100v-50z" />
|
||||
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 705l305 -305l596 596l-154 155l-442 -442l-150 151zM900 150h100v50h-100v-50z" />
|
||||
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM100 988l97 -98l212 213l-97 97zM200 400l697 1l3 699l-250 -239l-149 149l-212 -212l149 -149zM900 150h100v50h-100v-50z" />
|
||||
<glyph unicode="" d="M0 0v275q0 11 7 18t18 7h1048q11 0 19 -7.5t8 -17.5v-275h-1100zM200 612l212 -212l98 97l-213 212zM300 1200l239 -250l-149 -149l212 -212l149 148l249 -237l-1 697zM900 150h100v50h-100v-50z" />
|
||||
<glyph unicode="" d="M23 415l1177 784v-1079l-475 272l-310 -393v416h-392zM494 210l672 938l-672 -712v-226z" />
|
||||
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-850q0 -21 -15 -35.5t-35 -14.5h-150v400h-700v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 1000h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-218l-276 -275l-120 120l-126 -127h-378v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM581 306l123 123l120 -120l353 352l123 -123l-475 -476zM600 1000h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-269l-103 -103l-170 170l-298 -298h-329v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 1000h100v200h-100v-200zM700 133l170 170l-170 170l127 127l170 -170l170 170l127 -128l-170 -169l170 -170 l-127 -127l-170 170l-170 -170z" />
|
||||
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-300h-400v-200h-500v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 300l300 -300l300 300h-200v300h-200v-300h-200zM600 1000v200h100v-200h-100z" />
|
||||
<glyph unicode="" d="M0 150v1000q0 20 14.5 35t35.5 15h250v-300h500v300h100l200 -200v-402l-200 200l-298 -298h-402v-400h-150q-21 0 -35.5 14.5t-14.5 35.5zM600 300h200v-300h200v300h200l-300 300zM600 1000v200h100v-200h-100z" />
|
||||
<glyph unicode="" d="M0 250q0 -21 14.5 -35.5t35.5 -14.5h1100q21 0 35.5 14.5t14.5 35.5v550h-1200v-550zM0 900h1200v150q0 21 -14.5 35.5t-35.5 14.5h-1100q-21 0 -35.5 -14.5t-14.5 -35.5v-150zM100 300v200h400v-200h-400z" />
|
||||
<glyph unicode="" d="M0 400l300 298v-198h400v-200h-400v-198zM100 800v200h100v-200h-100zM300 800v200h100v-200h-100zM500 800v200h400v198l300 -298l-300 -298v198h-400zM800 300v200h100v-200h-100zM1000 300h100v200h-100v-200z" />
|
||||
<glyph unicode="" d="M100 700v400l50 100l50 -100v-300h100v300l50 100l50 -100v-300h100v300l50 100l50 -100v-400l-100 -203v-447q0 -21 -14.5 -35.5t-35.5 -14.5h-200q-21 0 -35.5 14.5t-14.5 35.5v447zM800 597q0 -29 10.5 -55.5t25 -43t29 -28.5t25.5 -18l10 -5v-397q0 -21 14.5 -35.5 t35.5 -14.5h200q21 0 35.5 14.5t14.5 35.5v1106q0 31 -18 40.5t-44 -7.5l-276 -116q-25 -17 -43.5 -51.5t-18.5 -65.5v-359z" />
|
||||
<glyph unicode="" d="M100 0h400v56q-75 0 -87.5 6t-12.5 44v394h500v-394q0 -38 -12.5 -44t-87.5 -6v-56h400v56q-4 0 -11 0.5t-24 3t-30 7t-24 15t-11 24.5v888q0 22 25 34.5t50 13.5l25 2v56h-400v-56q75 0 87.5 -6t12.5 -44v-394h-500v394q0 38 12.5 44t87.5 6v56h-400v-56q4 0 11 -0.5 t24 -3t30 -7t24 -15t11 -24.5v-888q0 -22 -25 -34.5t-50 -13.5l-25 -2v-56z" />
|
||||
<glyph unicode="" d="M0 300q0 -41 29.5 -70.5t70.5 -29.5h300q41 0 70.5 29.5t29.5 70.5v500q0 41 -29.5 70.5t-70.5 29.5h-300q-41 0 -70.5 -29.5t-29.5 -70.5v-500zM100 100h400l200 200h105l295 98v-298h-425l-100 -100h-375zM100 300v200h300v-200h-300zM100 600v200h300v-200h-300z M100 1000h400l200 -200v-98l295 98h105v200h-425l-100 100h-375zM700 402v163l400 133v-163z" />
|
||||
<glyph unicode="" d="M16.5 974.5q0.5 -21.5 16 -90t46.5 -140t104 -177.5t175 -208q103 -103 207.5 -176t180 -103.5t137 -47t92.5 -16.5l31 1l163 162q17 18 13.5 41t-22.5 37l-192 136q-19 14 -45 12t-42 -19l-118 -118q-142 101 -268 227t-227 268l118 118q17 17 20 41.5t-11 44.5 l-139 194q-14 19 -36.5 22t-40.5 -14l-162 -162q-1 -11 -0.5 -32.5z" />
|
||||
<glyph unicode="" d="M0 50v212q0 20 10.5 45.5t24.5 39.5l365 303v50q0 4 1 10.5t12 22.5t30 28.5t60 23t97 10.5t97 -10t60 -23.5t30 -27.5t12 -24l1 -10v-50l365 -303q14 -14 24.5 -39.5t10.5 -45.5v-212q0 -21 -14.5 -35.5t-35.5 -14.5h-1100q-20 0 -35 14.5t-15 35.5zM0 712 q0 -21 14.5 -33.5t34.5 -8.5l202 33q20 4 34.5 21t14.5 38v146q141 24 300 24t300 -24v-146q0 -21 14.5 -38t34.5 -21l202 -33q20 -4 34.5 8.5t14.5 33.5v200q-6 8 -19 20.5t-63 45t-112 57t-171 45t-235 20.5q-92 0 -175 -10.5t-141.5 -27t-108.5 -36.5t-81.5 -40 t-53.5 -36.5t-31 -27.5l-9 -10v-200z" />
|
||||
<glyph unicode="" d="M100 0v100h1100v-100h-1100zM175 200h950l-125 150v250l100 100v400h-100v-200h-100v200h-200v-200h-100v200h-200v-200h-100v200h-100v-400l100 -100v-250z" />
|
||||
<glyph unicode="" d="M100 0h300v400q0 41 -29.5 70.5t-70.5 29.5h-100q-41 0 -70.5 -29.5t-29.5 -70.5v-400zM500 0v1000q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-1000h-300zM900 0v700q0 41 29.5 70.5t70.5 29.5h100q41 0 70.5 -29.5t29.5 -70.5v-700h-300z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v300h-200v100h200v100h-300v-300h200v-100h-200v-100zM600 300h200v100h100v300h-100v100h-200v-500 zM700 400v300h100v-300h-100z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h100v200h100v-200h100v500h-100v-200h-100v200h-100v-500zM600 300h200v100h100v300h-100v100h-200v-500 zM700 400v300h100v-300h-100z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v100h-200v300h200v100h-300v-500zM600 300h300v100h-200v300h200v100h-300v-500z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 550l300 -150v300zM600 400l300 150l-300 150v-300z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300v500h700v-500h-700zM300 400h130q41 0 68 42t27 107t-28.5 108t-66.5 43h-130v-300zM575 549 q0 -65 27 -107t68 -42h130v300h-130q-38 0 -66.5 -43t-28.5 -108z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v300h-200v100h200v100h-300v-300h200v-100h-200v-100zM601 300h100v100h-100v-100zM700 700h100 v-400h100v500h-200v-100z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 300h300v400h-200v100h-100v-500zM301 400v200h100v-200h-100zM601 300h100v100h-100v-100zM700 700h100 v-400h100v500h-200v-100z" />
|
||||
<glyph unicode="" d="M-100 300v500q0 124 88 212t212 88h700q124 0 212 -88t88 -212v-500q0 -124 -88 -212t-212 -88h-700q-124 0 -212 88t-88 212zM100 200h900v700h-900v-700zM200 700v100h300v-300h-99v-100h-100v100h99v200h-200zM201 300v100h100v-100h-100zM601 300v100h100v-100h-100z M700 700v100h200v-500h-100v400h-100z" />
|
||||
<glyph unicode="" d="M4 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM186 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM400 500v200 l100 100h300v-100h-300v-200h300v-100h-300z" />
|
||||
<glyph unicode="" d="M0 600q0 162 80 299t217 217t299 80t299 -80t217 -217t80 -299t-80 -299t-217 -217t-299 -80t-299 80t-217 217t-80 299zM182 600q0 -171 121.5 -292.5t292.5 -121.5t292.5 121.5t121.5 292.5t-121.5 292.5t-292.5 121.5t-292.5 -121.5t-121.5 -292.5zM400 400v400h300 l100 -100v-100h-100v100h-200v-100h200v-100h-200v-100h-100zM700 400v100h100v-100h-100z" />
|
||||
<glyph unicode="" d="M-14 494q0 -80 56.5 -137t135.5 -57h222v300h400v-300h128q120 0 205 86.5t85 207.5t-85 207t-205 86q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5zM300 200h200v300h200v-300h200 l-300 -300z" />
|
||||
<glyph unicode="" d="M-14 494q0 -80 56.5 -137t135.5 -57h8l414 414l403 -403q94 26 154.5 104.5t60.5 178.5q0 120 -85 206.5t-205 86.5q-46 0 -90 -14q-44 97 -134.5 156.5t-200.5 59.5q-152 0 -260 -107.5t-108 -260.5q0 -25 2 -37q-66 -14 -108.5 -67.5t-42.5 -122.5zM300 200l300 300 l300 -300h-200v-300h-200v300h-200z" />
|
||||
<glyph unicode="" d="M100 200h400v-155l-75 -45h350l-75 45v155h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170z" />
|
||||
<glyph unicode="" d="M121 700q0 -53 28.5 -97t75.5 -65q-4 -16 -4 -38q0 -74 52.5 -126.5t126.5 -52.5q56 0 100 30v-306l-75 -45h350l-75 45v306q46 -30 100 -30q74 0 126.5 52.5t52.5 126.5q0 24 -9 55q50 32 79.5 83t29.5 112q0 90 -61.5 155.5t-150.5 71.5q-26 89 -99.5 145.5 t-167.5 56.5q-116 0 -197.5 -81.5t-81.5 -197.5q0 -4 1 -11.5t1 -11.5q-14 2 -23 2q-74 0 -126.5 -52.5t-52.5 -126.5z" />
|
||||
</font>
|
||||
</defs></svg>
|
After Width: | Height: | Size: 62 KiB |
BIN
gui/slick/css/fonts/glyphicons-halflings-regular.ttf
Normal file
BIN
gui/slick/css/fonts/glyphicons-halflings-regular.woff
Normal file
|
@ -139,7 +139,6 @@ fonts
|
|||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
/* =======================================================================
|
||||
inc_top.tmpl
|
||||
========================================================================== */
|
||||
|
@ -158,124 +157,124 @@ inc_top.tmpl
|
|||
|
||||
[class^="icon-"],
|
||||
[class*=" icon-"] {
|
||||
background-image: url("../images/glyphicons-halflings.png");
|
||||
background-image: url("../images/glyphicons-halflings.png");
|
||||
}
|
||||
|
||||
.icon-white {
|
||||
background-image: url("../images/glyphicons-halflings-white.png");
|
||||
background-image: url("../images/glyphicons-halflings-white.png");
|
||||
}
|
||||
|
||||
.dropdown-menu li > a:hover > [class^="menu-icon-"],
|
||||
.dropdown-menu li > a:hover > [class*=" menu-icon-"] {
|
||||
background-image: url("../images/menu/menu-icons-white.png");
|
||||
.dropdown-menu li > a:hover > [class^="menu-icon-"],
|
||||
.dropdown-menu li > a:hover > [class*=" menu-icon-"] {
|
||||
background-image: url("../images/menu/menu-icons-white.png");
|
||||
}
|
||||
|
||||
.ui-autocomplete-loading {
|
||||
background: white url("../images/loading16.gif") right center no-repeat;
|
||||
background: white url("../images/loading16.gif") right center no-repeat;
|
||||
}
|
||||
|
||||
.browserDialog.busy .ui-dialog-buttonpane {
|
||||
background: url("../images/loading.gif") 10px 50% no-repeat !important;
|
||||
.browserDialog.busy .ui-dialog-buttonpane {
|
||||
background: url("../images/loading.gif") 10px 50% no-repeat !important;
|
||||
}
|
||||
|
||||
.ui-progressbar .ui-progressbar-overlay {
|
||||
background: url("../css/lib/images/animated-overlay.gif");
|
||||
.ui-progressbar .ui-progressbar-overlay {
|
||||
background: url("../css/lib/images/animated-overlay.gif");
|
||||
}
|
||||
|
||||
.ui-dialog,
|
||||
.ui-dialog-buttonpane {
|
||||
background: #eceadf url("../css/lib/images/ui-bg_fine-grain_10_eceadf_60x60.png") 50% 50% repeat !important;
|
||||
.ui-dialog-buttonpane {
|
||||
background: #eceadf url("../css/lib/images/ui-bg_fine-grain_10_eceadf_60x60.png") 50% 50% repeat !important;
|
||||
}
|
||||
|
||||
.ui-accordion-content,
|
||||
.ui-accordion-content,
|
||||
.ui-tabs-panel {
|
||||
background: #ededed !important;
|
||||
background-image: none !important;
|
||||
background: #ededed !important;
|
||||
background-image: none !important;
|
||||
}
|
||||
|
||||
.ui-widget-content {
|
||||
background: #dcdcdc url("../css/lib/images/ui-bg_highlight-soft_75_dcdcdc_1x100.png") 50% top repeat-x;
|
||||
background: #dcdcdc url("../css/lib/images/ui-bg_highlight-soft_75_dcdcdc_1x100.png") 50% top repeat-x;
|
||||
}
|
||||
|
||||
.ui-widget-content a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.ui-widget-header {
|
||||
background: #ffffff url("../css/lib/images/ui-bg_flat_0_ffffff_40x100.png") 50% 50% repeat-x;
|
||||
.ui-widget-header {
|
||||
background: #ffffff url("../css/lib/images/ui-bg_flat_0_ffffff_40x100.png") 50% 50% repeat-x;
|
||||
}
|
||||
|
||||
.ui-state-default,
|
||||
.ui-widget-content .ui-state-default,
|
||||
.ui-widget-header .ui-state-default {
|
||||
background: #ffffff;
|
||||
.ui-widget-content .ui-state-default,
|
||||
.ui-widget-header .ui-state-default {
|
||||
background: #ffffff;
|
||||
border: 1px solid #CCCCCC;
|
||||
}
|
||||
|
||||
.ui-state-hover,
|
||||
.ui-widget-content .ui-state-hover,
|
||||
.ui-widget-header .ui-state-hover,
|
||||
.ui-state-focus,
|
||||
.ui-widget-content .ui-state-focus,
|
||||
.ui-widget-header .ui-state-focus {
|
||||
background: #ffffff;
|
||||
.ui-widget-content .ui-state-hover,
|
||||
.ui-widget-header .ui-state-hover,
|
||||
.ui-state-focus,
|
||||
.ui-widget-content .ui-state-focus,
|
||||
.ui-widget-header .ui-state-focus {
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.ui-state-active,
|
||||
.ui-widget-content .ui-state-active,
|
||||
.ui-widget-header .ui-state-active {
|
||||
.ui-widget-content .ui-state-active,
|
||||
.ui-widget-header .ui-state-active {
|
||||
background: #F7F7F7;
|
||||
}
|
||||
|
||||
.ui-state-highlight,
|
||||
.ui-widget-content .ui-state-highlight,
|
||||
.ui-widget-header .ui-state-highlight {
|
||||
background: #fbf9ee url("../css/lib/images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x;
|
||||
.ui-widget-content .ui-state-highlight,
|
||||
.ui-widget-header .ui-state-highlight {
|
||||
background: #fbf9ee url("../css/lib/images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x;
|
||||
}
|
||||
|
||||
.ui-state-error,
|
||||
.ui-widget-content .ui-state-error,
|
||||
.ui-widget-header .ui-state-error {
|
||||
background: #fef1ec url("../css/lib/images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x;
|
||||
.ui-widget-content .ui-state-error,
|
||||
.ui-widget-header .ui-state-error {
|
||||
background: #fef1ec url("../css/lib/images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x;
|
||||
}
|
||||
|
||||
.ui-icon,
|
||||
.ui-widget-content .ui-icon {
|
||||
background-image: url("../css/lib/images/ui-icons_222222_256x240.png");
|
||||
.ui-icon,
|
||||
.ui-widget-content .ui-icon {
|
||||
background-image: url("../css/lib/images/ui-icons_222222_256x240.png");
|
||||
}
|
||||
|
||||
.ui-widget-header .ui-icon {
|
||||
background-image: url("../css/lib/images/ui-icons_222222_256x240.png");
|
||||
.ui-widget-header .ui-icon {
|
||||
background-image: url("../css/lib/images/ui-icons_222222_256x240.png");
|
||||
}
|
||||
|
||||
.ui-state-default .ui-icon {
|
||||
background-image: url("../css/lib/images/ui-icons_8c291d_256x240.png");
|
||||
.ui-state-default .ui-icon {
|
||||
background-image: url("../css/lib/images/ui-icons_8c291d_256x240.png");
|
||||
}
|
||||
|
||||
.ui-state-hover .ui-icon,
|
||||
.ui-state-focus .ui-icon {
|
||||
background-image: url("../css/lib/images/ui-icons_222222_256x240.png");
|
||||
.ui-state-hover .ui-icon,
|
||||
.ui-state-focus .ui-icon {
|
||||
background-image: url("../css/lib/images/ui-icons_222222_256x240.png");
|
||||
}
|
||||
|
||||
.ui-state-active .ui-icon {
|
||||
background-image: url("../css/lib/images/ui-icons_8c291d_256x240.png");
|
||||
.ui-state-active .ui-icon {
|
||||
background-image: url("../css/lib/images/ui-icons_8c291d_256x240.png");
|
||||
}
|
||||
|
||||
.ui-state-highlight .ui-icon {
|
||||
background-image: url("../css/lib/images/ui-icons_2e83ff_256x240.png");
|
||||
.ui-state-highlight .ui-icon {
|
||||
background-image: url("../css/lib/images/ui-icons_2e83ff_256x240.png");
|
||||
}
|
||||
|
||||
.ui-state-error .ui-icon,
|
||||
.ui-state-error-text .ui-icon {
|
||||
background-image: url("../css/lib/images/ui-icons_cd0a0a_256x240.png");
|
||||
.ui-state-error .ui-icon,
|
||||
.ui-state-error-text .ui-icon {
|
||||
background-image: url("../css/lib/images/ui-icons_cd0a0a_256x240.png");
|
||||
}
|
||||
|
||||
.ui-widget-overlay {
|
||||
background: #aaaaaa url("../css/lib/images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;
|
||||
.ui-widget-overlay {
|
||||
background: #aaaaaa url("../css/lib/images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;
|
||||
}
|
||||
|
||||
.ui-widget-shadow {
|
||||
background: #000000 url("../css/lib/images/ui-bg_flat_0_000000_40x100.png") 50% 50% repeat-x;
|
||||
.ui-widget-shadow {
|
||||
background: #000000 url("../css/lib/images/ui-bg_flat_0_000000_40x100.png") 50% 50% repeat-x;
|
||||
}
|
||||
|
||||
.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited {
|
||||
|
@ -302,7 +301,7 @@ inc_top.tmpl
|
|||
border-radius: 0;
|
||||
}
|
||||
|
||||
.ui-tabs .ui-tabs-panel {
|
||||
.ui-tabs .ui-tabs-panel {
|
||||
background-color: #F7F7F7 !important;
|
||||
border: 1px solid #CCCCCC !important;
|
||||
}
|
||||
|
@ -312,7 +311,7 @@ inc_top.tmpl
|
|||
border-top-right-radius: 5px;
|
||||
}
|
||||
|
||||
.ui-tabs-nav > :not(.ui-tabs-active){
|
||||
.ui-tabs-nav > :not(.ui-tabs-active){
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
}
|
||||
|
@ -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
|
||||
========================================================================== */
|
||||
|
@ -726,7 +745,7 @@ home.tmpl
|
|||
}
|
||||
|
||||
.show-table {
|
||||
text-align:center;
|
||||
text-align:center;
|
||||
vertical-align:middle;
|
||||
width: 33%
|
||||
}
|
||||
|
@ -764,7 +783,7 @@ home.tmpl
|
|||
|
||||
#sort-by {
|
||||
display: inline;
|
||||
list-style-type: none;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
@ -910,6 +929,51 @@ div.formpaginate {
|
|||
margin-left: 10px
|
||||
}
|
||||
|
||||
.stepDiv #searchResults div {
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.stepDiv #searchResults #searchingAnim {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.stepone-result-title {
|
||||
font-weight: 600;
|
||||
margin-left: 10px
|
||||
}
|
||||
|
||||
.stepone-result-date,
|
||||
.stepone-result-db,
|
||||
.stepone-result-overview {
|
||||
margin-left: 5px
|
||||
}
|
||||
|
||||
.stepone-result-db img {
|
||||
margin-top: 3px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
#newShowPortal #displayText .show-name,
|
||||
#newShowPortal #displayText .show-dest,
|
||||
#newShowPortal #displayText p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#newShowPortal #displayText .show-name,
|
||||
#newShowPortal #displayText .show-dest {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#addRootDirTable td label .filepath {
|
||||
font-weight: 900
|
||||
}
|
||||
|
||||
#addShowForm #blackwhitelist,
|
||||
#addShowForm #blackwhitelist h4,
|
||||
#addShowForm #blackwhitelist p {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
home_addExistingShow.tmpl
|
||||
========================================================================== */
|
||||
|
@ -985,9 +1049,9 @@ home_trendingShows.tmpl
|
|||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
border-bottom: 1px solid #111;
|
||||
background-image: url(../images/poster-dark.jpg);
|
||||
}
|
||||
|
||||
|
||||
/* =======================================================================
|
||||
home_postprocess.tmpl
|
||||
========================================================================== */
|
||||
|
@ -1038,7 +1102,7 @@ h1.title {
|
|||
}
|
||||
|
||||
.displayspecials {
|
||||
position: relative;
|
||||
position: relative;
|
||||
top: -24px;
|
||||
}
|
||||
|
||||
|
@ -1066,7 +1130,8 @@ span.imdbstars, span.imdbstars > * {
|
|||
height: 12px;
|
||||
width: 120px;
|
||||
display: inline-block;
|
||||
font-size:10px
|
||||
font-size:10px;
|
||||
background: url(../images/rating.png) 0 -12px repeat-x;
|
||||
}
|
||||
|
||||
#showinfo .flag {
|
||||
|
@ -1227,6 +1292,7 @@ span.snatched b {
|
|||
text-align: center;
|
||||
border: none;
|
||||
empty-cells: show;
|
||||
color: #000;
|
||||
}
|
||||
.sickbeardTable.display_show {
|
||||
clear:both
|
||||
|
@ -1326,6 +1392,17 @@ td.col-search {
|
|||
padding-right: 6px;
|
||||
padding-bottom: 1px;
|
||||
width: 150px;
|
||||
vertical-align: top;
|
||||
}
|
||||
.options-on-right {
|
||||
width:180px;
|
||||
float: right;
|
||||
vertical-align: middle;
|
||||
height: 100%;
|
||||
}
|
||||
.options-on-right .showLegendRight {
|
||||
padding-right: 6px;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
.input-scene {
|
||||
|
@ -1335,10 +1412,11 @@ td.col-search {
|
|||
}
|
||||
|
||||
#editShow {
|
||||
width: 700px;
|
||||
padding-top: 10px;
|
||||
/*width: 700px;
|
||||
padding-top: 10px;*/
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
padding: 15px 0 0
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
|
@ -1360,6 +1438,7 @@ episodeView.tmpl
|
|||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.carousel-indicators li.listing-soon,
|
||||
.listing-default {
|
||||
background-color: #f5f1e4;
|
||||
}
|
||||
|
@ -1368,14 +1447,17 @@ episodeView.tmpl
|
|||
background-color: #dfd;
|
||||
}
|
||||
|
||||
.carousel-indicators li.listing-overdue,
|
||||
.listing-overdue {
|
||||
background-color: #fdd;
|
||||
}
|
||||
|
||||
.carousel-indicators li.listing-default,
|
||||
.listing-toofar {
|
||||
background-color: #bedeed;
|
||||
}
|
||||
|
||||
.carousel-indicators li.listing-soon,
|
||||
span.listing-default {
|
||||
color: #826f30;
|
||||
border: 1px solid #826f30;
|
||||
|
@ -1386,11 +1468,13 @@ span.listing-current {
|
|||
border: 1px solid #295730;
|
||||
}
|
||||
|
||||
.carousel-indicators li.listing-overdue,
|
||||
span.listing-overdue {
|
||||
color: #890000;
|
||||
border: 1px solid #890000;
|
||||
}
|
||||
|
||||
.carousel-indicators li.listing-default,
|
||||
span.listing-toofar {
|
||||
color: #1d5068;
|
||||
border: 1px solid #1d5068;
|
||||
|
@ -1495,7 +1579,7 @@ h2.day, h2.network {
|
|||
.daybydayWrapper {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 0 /*3px*/
|
||||
padding: 0; /*3px*/
|
||||
}
|
||||
|
||||
.day-of-week {
|
||||
|
@ -1538,6 +1622,23 @@ h2.day, h2.network {
|
|||
width: 100%
|
||||
}
|
||||
|
||||
.daybyday-show .state {
|
||||
height: 3px;
|
||||
}
|
||||
|
||||
.daybyday-show .listing-default {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.carousel-indicators li.listing-overdue,
|
||||
.daybyday-show .listing-overdue {
|
||||
background-color: #ffb0b0;
|
||||
}
|
||||
|
||||
.daybyday-show .listing-current {
|
||||
background-color: #aaffaa;
|
||||
}
|
||||
|
||||
.day-of-week .poster img {
|
||||
border: 1px solid;
|
||||
border-radius: 5px;
|
||||
|
@ -1575,6 +1676,111 @@ h2.day, h2.network {
|
|||
font-size: 12px;
|
||||
}
|
||||
|
||||
.day-of-week .text .episode {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.time-am-pm {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
#content.episodeview-banner .time-am-pm,
|
||||
#content.episodeview-poster .time-am-pm {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.over-layer0 {
|
||||
filter: alpha(opacity=60);
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
.over-layer1 {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.over-layer0,
|
||||
.over-layer1 {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
font-size: 10px;
|
||||
padding: 4px 6px 2px 2px;
|
||||
}
|
||||
|
||||
.on-air0,
|
||||
.on-air1 {
|
||||
text-align: right;
|
||||
}
|
||||
.on-air0 {
|
||||
background-color: #dfd !important;
|
||||
filter: alpha(opacity=75);
|
||||
opacity: .75;
|
||||
}
|
||||
.on-air1 {
|
||||
color: #295730 !important;
|
||||
border-left: 1px solid #295730 !important;
|
||||
border-bottom: 1px solid #295730 !important;
|
||||
}
|
||||
|
||||
.daybydayCarouselContainer {
|
||||
min-height: 20px;
|
||||
margin: 19px 0;
|
||||
}
|
||||
|
||||
.controlsBlock {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 180px;
|
||||
margin: 0 auto;
|
||||
height: 35px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.carousel-control {
|
||||
background: none !important;
|
||||
text-align: center;
|
||||
opacity: 0.75;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
top:1px
|
||||
}
|
||||
|
||||
.carousel-indicators {
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.carousel-indicators li {
|
||||
border-radius: 0;
|
||||
width: 12px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.carousel-indicators .active {
|
||||
width: 14px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.carousel-control.right {
|
||||
right: 4px
|
||||
}
|
||||
|
||||
.carousel-control .glyphicon-chevron-left {
|
||||
margin-left: -10px;
|
||||
}
|
||||
.carousel-control .glyphicon-chevron-right {
|
||||
margin-right: -10px;
|
||||
}
|
||||
.carousel-control .glyphicon-chevron-left,
|
||||
.carousel-control .glyphicon-chevron-right {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-top: 0;
|
||||
font-size: 20px;
|
||||
top:0;
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
config*.tmpl
|
||||
========================================================================== */
|
||||
|
@ -1693,6 +1899,15 @@ select .selected {
|
|||
line-height: 24px;
|
||||
}
|
||||
|
||||
#editShow .field-pair #SceneException h4,
|
||||
#editShow .field-pair #customQuality h4 {
|
||||
font-size: 13px !important;
|
||||
margin-bottom: 10px
|
||||
}
|
||||
#editShow .field-pair #customQuality h4 {
|
||||
margin-bottom:1px;
|
||||
}
|
||||
|
||||
.testNotification {
|
||||
padding: 5px;
|
||||
margin-bottom: 10px;
|
||||
|
@ -1867,6 +2082,34 @@ config_postProcessing.tmpl
|
|||
top: 2px;
|
||||
}
|
||||
|
||||
#failed-guide,
|
||||
#failed-guide .title,
|
||||
#failed-guide li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#failed-guide .title {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
#failed-guide li {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.icon-info-sign {
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin: 2px 5px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.pp .component-group-list.right,
|
||||
.pp .field-pair.right {
|
||||
margin: 0 0 0 250px;
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
config_notifications.tmpl
|
||||
========================================================================== */
|
||||
|
@ -1982,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
|
||||
|
@ -2091,47 +2353,81 @@ option.flag {
|
|||
}
|
||||
|
||||
#Anime div.component-group-desc p {
|
||||
margin-bottom: 0.4em;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
margin-top: 0.4em;
|
||||
margin: 0.4em 0;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
div.blackwhitelist h4 {
|
||||
margin-top:0
|
||||
}
|
||||
div.blackwhitelist{
|
||||
float:left;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.blackwhitelist input {
|
||||
margin: 5px 5px;
|
||||
}
|
||||
|
||||
div.blackwhitelist.pool select{
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
div.blackwhitelist.pool {
|
||||
margin:5px;
|
||||
}
|
||||
|
||||
div.blackwhitelist.white select, div.blackwhitelist.black select {
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
div.blackwhitelist.white, div.blackwhitelist.black {
|
||||
margin:5px;
|
||||
}
|
||||
|
||||
div.blackwhitelist span {
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.blackwhitelist.anidb, div.blackwhitelist.manual {
|
||||
div.blackwhitelist.white input,
|
||||
div.blackwhitelist.black input,
|
||||
div.blackwhitelist.pool input {
|
||||
margin: 5px 0 !important;
|
||||
}
|
||||
div.blackwhitelist select {
|
||||
margin : 0 !important
|
||||
}
|
||||
|
||||
div.blackwhitelist .inuse {
|
||||
margin-right: 5px;
|
||||
width: 243px;
|
||||
float: left
|
||||
}
|
||||
|
||||
div.blackwhitelist.white,
|
||||
div.blackwhitelist.black {
|
||||
width: 243px;
|
||||
}
|
||||
div.blackwhitelist.white select,
|
||||
div.blackwhitelist.black select{
|
||||
margin:0;
|
||||
width: 215px;
|
||||
/* clear:both*/
|
||||
}
|
||||
div.blackwhitelist.white select,
|
||||
div.blackwhitelist.black select {
|
||||
height: 110px;
|
||||
}
|
||||
div.blackwhitelist.pool,
|
||||
div.blackwhitelist.pool select{
|
||||
width: 330px;
|
||||
height: 265px;
|
||||
float:right
|
||||
}
|
||||
|
||||
div.blackwhitelist span {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div#blackwhitelist,
|
||||
div.blackwhitelist.manual {
|
||||
margin: 7px 0;
|
||||
}
|
||||
|
||||
.boldest {
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
.clear-left {
|
||||
clear: left;
|
||||
}
|
||||
|
||||
.nextline-block {
|
||||
display:block;
|
||||
}
|
||||
|
||||
.padbottom {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.max300 {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
bootstrap Overrides
|
||||
|
@ -3123,83 +3419,6 @@ span.token-input-delete-token {
|
|||
margin: 0 1px;
|
||||
}
|
||||
|
||||
.stepDiv #searchResults div {
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.stepDiv #searchResults #searchingAnim {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.stepone-result-title {
|
||||
font-weight: 600;
|
||||
margin-left: 10px
|
||||
}
|
||||
|
||||
.stepone-result-date,
|
||||
.stepone-result-db,
|
||||
.stepone-result-overview {
|
||||
margin-left: 5px
|
||||
}
|
||||
.stepone-result-db img {
|
||||
margin-top: 3px;
|
||||
vertical-align: top;
|
||||
}
|
||||
#newShowPortal #displayText .show-name,
|
||||
#newShowPortal #displayText .show-dest,
|
||||
#newShowPortal #displayText p {
|
||||
margin: 0;
|
||||
}
|
||||
#newShowPortal #displayText .show-name,
|
||||
#newShowPortal #displayText .show-dest {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#addRootDirTable td label .filepath {
|
||||
font-weight: 900
|
||||
}
|
||||
|
||||
.boldest {font-weight: 900}
|
||||
.red-text {color:#d33}
|
||||
.clear-left {clear:left}
|
||||
.float-left {float:left}
|
||||
.nextline-block {display:block}
|
||||
|
||||
#failed-guide,
|
||||
#failed-guide .title,
|
||||
#failed-guide li {margin:0; padding:0}
|
||||
#failed-guide .title {list-style-type: none}
|
||||
#failed-guide li {margin-left:15px}
|
||||
|
||||
.icon-info-sign {
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin: 2px 5px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.pp .component-group-list.right,
|
||||
.pp .field-pair.right {
|
||||
margin: 0 0 0 250px;
|
||||
}
|
||||
|
||||
.trakt-image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 0;
|
||||
background-image: url(../images/poster-dark.jpg)
|
||||
}
|
||||
|
||||
.time-am-pm {
|
||||
margin-left: 2px;
|
||||
}
|
||||
#content.episodeview-banner .time-am-pm,
|
||||
#content.episodeview-poster .time-am-pm {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
/* =======================================================================
|
||||
jquery.confirm.css
|
||||
========================================================================== */
|
||||
|
@ -3306,4 +3525,5 @@ pnotify.css
|
|||
.ui-pnotify-closer {
|
||||
margin-top: -12px;
|
||||
margin-right: -10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.6 KiB |
BIN
gui/slick/images/network/ytv.png
Normal file
After Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 101 B |
Before Width: | Height: | Size: 703 B |
BIN
gui/slick/images/sickgear-large.png
Normal file
After Width: | Height: | Size: 24 KiB |
33
gui/slick/interfaces/default/404.tmpl
Normal file
|
@ -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')
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>SickGear - BRANCH:[$sickbeard.BRANCH] - $title</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/bootstrap.css?$sbPID"/>
|
||||
<link rel="stylesheet" type="text/css" href="$sbRoot/css/style.css?$sbPID"/>
|
||||
<link rel="stylesheet" type="text/css" href="$sbRoot/css/${sickbeard.THEME_NAME}.css?$sbPID" />
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="error-404">
|
||||
<h1>404</h1>
|
||||
<h2>Page Not Found</h2>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" width="500px" height="100px" viewBox="0 0 50 10" enable-background="new 0 0 50 10" xml:space="preserve"><g><path d="M2.364 7.785H2.027C1.405 7 1.1 6.1 1.1 5.275c0-0.871 0.31-1.708 0.932-2.514h0.337 C1.843 3.6 1.6 4.4 1.6 5.275C1.582 6.1 1.8 7 2.4 7.785z"/><path d="M5.563 2.569c0 0.854-0.292 1.57-0.874 2.147c-0.577 0.57-1.288 0.855-2.131 0.855V4.931c0.645 0 1.199-0.232 1.662-0.697 c0.463-0.465 0.694-1.02 0.694-1.665H5.563z"/><path d="M8.987 3.372c0.199 0 0.4 0.1 0.5 0.213c0.142 0.1 0.2 0.3 0.2 0.511c0 0.201-0.071 0.372-0.213 0.5 S9.184 4.8 9 4.824c-0.199 0-0.369-0.071-0.511-0.213S8.262 4.3 8.3 4.096c0-0.2 0.071-0.372 0.212-0.513 C8.615 3.4 8.8 3.4 9 3.372z M8.987 4.541c0.121 0 0.224-0.043 0.31-0.131c0.087-0.087 0.13-0.192 0.13-0.314 S9.383 3.9 9.3 3.785C9.21 3.7 9.1 3.7 9 3.657c-0.122 0-0.226 0.042-0.312 0.128C8.59 3.9 8.5 4 8.5 4.1 S8.59 4.3 8.7 4.41C8.763 4.5 8.9 4.5 9 4.541z"/><path d="M10.438 4.787h2.47v2.47h-2.47V4.787z M10.637 4.986v2.072h2.072V4.986H10.637z"/><path d="M14.365 3.372c0.199 0 0.4 0.1 0.5 0.213c0.142 0.1 0.2 0.3 0.2 0.511c0 0.201-0.071 0.372-0.213 0.5 s-0.312 0.213-0.509 0.213c-0.199 0-0.369-0.071-0.511-0.213c-0.143-0.143-0.213-0.313-0.213-0.514c0-0.2 0.07-0.372 0.212-0.513 C13.993 3.4 14.2 3.4 14.4 3.372z M14.365 4.541c0.121 0 0.224-0.043 0.31-0.131c0.086-0.087 0.129-0.192 0.129-0.314 s-0.043-0.226-0.129-0.311c-0.086-0.085-0.189-0.128-0.31-0.128c-0.122 0-0.226 0.042-0.312 0.1 c-0.085 0.085-0.128 0.189-0.128 0.311s0.043 0.2 0.1 0.314C14.141 4.5 14.2 4.5 14.4 4.541z"/><path d="M17.158 5.195c0 0.897-0.303 1.709-0.91 2.438h-0.513V7.612c0.286-0.265 0.519-0.59 0.699-0.971 c0.213-0.455 0.318-0.938 0.318-1.447c0-0.506-0.105-0.985-0.318-1.438c-0.18-0.382-0.413-0.705-0.699-0.97V2.757h0.513 C16.855 3.5 17.2 4.3 17.2 5.195z"/><path d="M23.797 2.569c0 0.854-0.292 1.57-0.874 2.147c-0.577 0.57-1.288 0.855-2.131 0.855V4.931c0.645 0 1.199-0.232 1.662-0.697 c0.463-0.465 0.695-1.02 0.695-1.665H23.797z"/><path d="M31.286 7.646c-0.226-0.278-0.549-0.512-0.971-0.701c-0.47-0.21-0.964-0.315-1.483-0.315s-1.015 0.105-1.485 0.3 c-0.424 0.189-0.748 0.423-0.974 0.701h-0.021V7.188c0.294-0.294 0.679-0.524 1.153-0.69c0.437-0.149 0.879-0.224 1.326-0.224 c0.448 0 0.9 0.1 1.3 0.224c0.473 0.2 0.9 0.4 1.2 0.69v0.458H31.286z"/><path d="M38.286 5.561h-5.357V4.92h2.352V2.569h0.644V4.92h2.362V5.561z"/><path d="M43.638 5.566h-5.352V4.925h5.352V5.566z"/><path d="M49 5.561h-5.357V4.92h2.352V2.569h0.644V4.92H49V5.561z"/></g></svg>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -57,7 +57,7 @@
|
|||
<label for="launch_browser">
|
||||
<span class="component-title">Launch browser</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="launch_browser" id="launch_browser" #if $sickbeard.LAUNCH_BROWSER then 'checked="checked"' else ''#/>
|
||||
<input type="checkbox" name="launch_browser" id="launch_browser" #if $sickbeard.LAUNCH_BROWSER then 'checked="checked"' else ''#>
|
||||
<p>open the SickGear home page on startup</p>
|
||||
</span>
|
||||
</label>
|
||||
|
@ -67,21 +67,31 @@
|
|||
<label for="update_shows_on_start">
|
||||
<span class="component-title">Update shows on startup</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="update_shows_on_start" id="update_shows_on_start" #if $sickbeard.UPDATE_SHOWS_ON_START then 'checked="checked"' else ''#/>
|
||||
<p>with information such as next air dates, show ended, etc. Disable for a faster startup as show info is scheduled to update in the background anyway</p>
|
||||
<input type="checkbox" name="update_shows_on_start" id="update_shows_on_start" #if $sickbeard.UPDATE_SHOWS_ON_START then 'checked="checked"' else ''#>
|
||||
<p>with show data; episode plot, images, air and end dates, etc. Disable for a quicker startup. Show data is scheduled to update during hour <span class="show_update_hour_value">$sickbeard.SHOW_UPDATE_HOUR</span>.</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="show_update_hour">
|
||||
<span class="component-title">Update shows during hour</span>
|
||||
<span class="component-desc">
|
||||
<input type="number" name="show_update_hour" id="show_update_hour" value="$sickbeard.SHOW_UPDATE_HOUR" class="form-control input-sm input75">
|
||||
<p>(0 ... 23) with show data; episode plot, images, air and end dates, etc.</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<span class="component-title">Send to trash for actions</span>
|
||||
<span class="component-desc">
|
||||
<label for="trash_remove_show" class="nextline-block">
|
||||
<input type="checkbox" name="trash_remove_show" id="trash_remove_show" #if $sickbeard.TRASH_REMOVE_SHOW then 'checked="checked"' else ''#/>
|
||||
<input type="checkbox" name="trash_remove_show" id="trash_remove_show" #if $sickbeard.TRASH_REMOVE_SHOW then 'checked="checked"' else ''#>
|
||||
<p>when using show "Remove" and delete files</p>
|
||||
</label>
|
||||
<label for="trash_rotate_logs" class="nextline-block">
|
||||
<input type="checkbox" name="trash_rotate_logs" id="trash_rotate_logs" #if $sickbeard.TRASH_ROTATE_LOGS then 'checked="checked"' else ''#/>
|
||||
<input type="checkbox" name="trash_rotate_logs" id="trash_rotate_logs" #if $sickbeard.TRASH_ROTATE_LOGS then 'checked="checked"' else ''#>
|
||||
<p>on scheduled deletes of the oldest log files</p>
|
||||
</label>
|
||||
<div class="clear-left"><p>selected actions use trash (recycle bin) instead of the default permanent delete</p></div>
|
||||
|
@ -92,7 +102,7 @@
|
|||
<label for="log_dir">
|
||||
<span class="component-title">Log file folder location</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="log_dir" id="log_dir" value="$sickbeard.ACTUAL_LOG_DIR" class="form-control input-sm input350" />
|
||||
<input type="text" name="log_dir" id="log_dir" value="$sickbeard.ACTUAL_LOG_DIR" class="form-control input-sm input350">
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -116,7 +126,7 @@
|
|||
<label for="indexer_timeout">
|
||||
<span class="component-title">Timeout show indexer at</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="indexer_timeout" id="indexer_timeout" value="$sickbeard.INDEXER_TIMEOUT" class="form-control input-sm input75" />
|
||||
<input type="text" name="indexer_timeout" id="indexer_timeout" value="$sickbeard.INDEXER_TIMEOUT" class="form-control input-sm input75">
|
||||
<p>seconds of inactivity when finding new shows (default:10)</p>
|
||||
</span>
|
||||
</label>
|
||||
|
@ -132,7 +142,7 @@
|
|||
</label>
|
||||
</div>
|
||||
|
||||
<input type="submit" class="btn config_submitter" value="Save Changes" />
|
||||
<input type="submit" class="btn config_submitter" value="Save Changes">
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="component-group">
|
||||
|
@ -147,7 +157,7 @@
|
|||
<label for="version_notify">
|
||||
<span class="component-title">Check software updates</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="version_notify" id="version_notify" #if $sickbeard.VERSION_NOTIFY then 'checked="checked"' else ''#/>
|
||||
<input type="checkbox" name="version_notify" id="version_notify" #if $sickbeard.VERSION_NOTIFY then 'checked="checked"' else ''#>
|
||||
<p>and display notifications when updates are available.
|
||||
Checks are run on startup and at the frequency set below*</p>
|
||||
</span>
|
||||
|
@ -158,7 +168,7 @@
|
|||
<label for="auto_update">
|
||||
<span class="component-title">Automatically update</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="auto_update" id="auto_update" #if $sickbeard.AUTO_UPDATE then 'checked="checked"' else ''#/>
|
||||
<input type="checkbox" name="auto_update" id="auto_update" #if $sickbeard.AUTO_UPDATE then 'checked="checked"' else ''#>
|
||||
<p>fetch and install software updates.
|
||||
Updates are run on startup and in the background at the frequency set below*</p>
|
||||
</span>
|
||||
|
@ -169,7 +179,7 @@
|
|||
<label>
|
||||
<span class="component-title">Check the server every*</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="update_frequency" id="update_frequency" value="$sickbeard.UPDATE_FREQUENCY" class="form-control input-sm input75" />
|
||||
<input type="text" name="update_frequency" id="update_frequency" value="$sickbeard.UPDATE_FREQUENCY" class="form-control input-sm input75">
|
||||
<p>hours for software updates (default:12)</p>
|
||||
</span>
|
||||
</label>
|
||||
|
@ -179,13 +189,13 @@
|
|||
<label for="notify_on_update">
|
||||
<span class="component-title">Notify on software update</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="notify_on_update" id="notify_on_update" #if $sickbeard.NOTIFY_ON_UPDATE then 'checked="checked"' else ''#/>
|
||||
<input type="checkbox" name="notify_on_update" id="notify_on_update" #if $sickbeard.NOTIFY_ON_UPDATE then 'checked="checked"' else ''#>
|
||||
<p>send a message to all enabled notifiers when SickGear has been updated</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<input type="submit" class="btn config_submitter" value="Save Changes" />
|
||||
<input type="submit" class="btn config_submitter" value="Save Changes">
|
||||
</fieldset>
|
||||
|
||||
</div>
|
||||
|
@ -219,7 +229,7 @@
|
|||
<label for="home_search_focus">
|
||||
<span class="component-title">Give show list search focus</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="home_search_focus" id="home_search_focus" #if $sickbeard.HOME_SEARCH_FOCUS then 'checked="checked"' else ''#/>
|
||||
<input type="checkbox" name="home_search_focus" id="home_search_focus" #if $sickbeard.HOME_SEARCH_FOCUS then 'checked="checked"' else ''#>
|
||||
<p>page refresh on "Show List" will start search box focused</p>
|
||||
</span>
|
||||
</label>
|
||||
|
@ -229,7 +239,7 @@
|
|||
<label for="sort_article">
|
||||
<span class="component-title">Sort with "The", "A", "An"</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="sort_article" id="sort_article" #if $sickbeard.SORT_ARTICLE then 'checked="checked"' else ''#/>
|
||||
<input type="checkbox" name="sort_article" id="sort_article" #if $sickbeard.SORT_ARTICLE then 'checked="checked"' else ''#>
|
||||
<p>include articles ("The", "A", "An") when sorting show lists</p>
|
||||
</span>
|
||||
</label>
|
||||
|
@ -239,7 +249,7 @@
|
|||
<label for="fuzzy_dating">
|
||||
<span class="component-title">Display fuzzy dates</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="fuzzy_dating" id="fuzzy_dating" class="viewIf datePresets" #if $sickbeard.FUZZY_DATING == True then 'checked="checked"' else ''#/>
|
||||
<input type="checkbox" name="fuzzy_dating" id="fuzzy_dating" class="viewIf datePresets" #if $sickbeard.FUZZY_DATING == True then 'checked="checked"' else ''#>
|
||||
<p>move absolute dates into tooltips and display e.g. "Last Thu", "On Tue"</p>
|
||||
</span>
|
||||
</label>
|
||||
|
@ -248,7 +258,7 @@
|
|||
<label for="trim_zero">
|
||||
<span class="component-title">Trim date and time</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="trim_zero" id="trim_zero" #if True == $sickbeard.TRIM_ZERO then 'checked="checked"' else ''#/>
|
||||
<input type="checkbox" name="trim_zero" id="trim_zero" #if True == $sickbeard.TRIM_ZERO then 'checked="checked"' else ''#>
|
||||
<p>display minimalist date and time i.e. <del>02:00</del> = 2:00, <del>02:00pm</del> = 2pm, <del>03 Jan</del> = 3 Jan</p>
|
||||
</span>
|
||||
</label>
|
||||
|
@ -293,16 +303,16 @@
|
|||
<span class="component-title">Timezone:</span>
|
||||
<span class="component-desc">
|
||||
<label for="local" class="space-right">
|
||||
<input type="radio" name="timezone_display" id="local" value="local" #if "local" == $sickbeard.TIMEZONE_DISPLAY then 'checked="checked"' else ''# />local
|
||||
<input type="radio" name="timezone_display" id="local" value="local" #if "local" == $sickbeard.TIMEZONE_DISPLAY then 'checked="checked"' else ''#>local
|
||||
</label>
|
||||
<label for="network">
|
||||
<input type="radio" name="timezone_display" id="network" value="network" #if "network" == $sickbeard.TIMEZONE_DISPLAY then 'checked="checked"' else ''# />network
|
||||
<input type="radio" name="timezone_display" id="network" value="network" #if "network" == $sickbeard.TIMEZONE_DISPLAY then 'checked="checked"' else ''#>network
|
||||
</label>
|
||||
<div class="clear-left"><p>display dates and times in either your timezone or the shows network timezone</p></div>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<input type="submit" class="btn config_submitter" value="Save Changes" />
|
||||
<input type="submit" class="btn config_submitter" value="Save Changes">
|
||||
|
||||
</fieldset>
|
||||
|
||||
|
@ -313,88 +323,91 @@
|
|||
<div class="component-group-desc">
|
||||
<h3>Web Interface</h3>
|
||||
<p>It is recommended that you enable a username and password to secure SickGear from being tampered with remotely.</p>
|
||||
<p><b>These options require a manual restart to take effect.</b></p>
|
||||
<p><b class="boldest">These options require a manual restart to take effect.</b></p>
|
||||
</div>
|
||||
|
||||
<fieldset class="component-group-list">
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="use_api">
|
||||
<span class="component-title">Enable API</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="use_api" class="enabler" id="use_api" #if $sickbeard.USE_API then 'checked="checked"' else ''#/>
|
||||
<p>allow the use of the SickGear API</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div id="content_use_api">
|
||||
<div class="field-pair">
|
||||
<label for="api_key">
|
||||
<span class="component-title">API key</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="api_key" id="api_key" value="$sickbeard.API_KEY" class="form-control input-sm input300" readonly="readonly" />
|
||||
<input class="btn btn-inline" type="button" id="generate_new_apikey" value="Generate">
|
||||
<div class="clear-left"><p>used to give 3rd party programs limited access to SickGear</p></div>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="web_log">
|
||||
<span class="component-title">HTTP logs</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="web_log" id="web_log" #if $sickbeard.WEB_LOG then 'checked="checked"' else ''#/>
|
||||
<p>enable logs from the internal Tornado web server</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="web_username">
|
||||
<span class="component-title">HTTP username</span>
|
||||
<span class="component-title">Username</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="web_username" id="web_username" value="$sickbeard.WEB_USERNAME" class="form-control input-sm input300" />
|
||||
<p>set blank for no login</p>
|
||||
<input type="text" name="web_username" id="web_username" value="$sickbeard.WEB_USERNAME" class="form-control input-sm input300">
|
||||
<p>blank for none</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="web_password">
|
||||
<span class="component-title">HTTP password</span>
|
||||
<span class="component-title">Password</span>
|
||||
<span class="component-desc">
|
||||
<input type="password" name="web_password" id="web_password" value="$sickbeard.WEB_PASSWORD" class="form-control input-sm input300" />
|
||||
<p>blank = no authentication</span>
|
||||
<input type="password" name="web_password" id="web_password" value="$sickbeard.WEB_PASSWORD" class="form-control input-sm input300">
|
||||
<p>blank for none</p>
|
||||
<span class="clear-left">check autoProcessTV.cfg is set up for external apps to use post processing scripts
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="calendar_unprotected">
|
||||
<span class="component-title">Unprotected calendar</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="calendar_unprotected" id="calendar_unprotected" #if $sickbeard.CALENDAR_UNPROTECTED then 'checked="checked"' else ''#>
|
||||
<p>permit subscribing to the calendar without username and password.
|
||||
Some services like Google Calendar will only work with <b class="boldest">no</b> authentication</p>
|
||||
</span>
|
||||
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="use_api">
|
||||
<span class="component-title">API enable</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="use_api" class="enabler" id="use_api" #if $sickbeard.USE_API then 'checked="checked"' else ''#>
|
||||
<p>permit the use of the SickGear (SickBeard) API</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<div id="content_use_api">
|
||||
<div class="field-pair">
|
||||
<label for="api_key">
|
||||
<span class="component-title">API key</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="api_key" id="api_key" value="$sickbeard.API_KEY" class="form-control input-sm input300" readonly="readonly">
|
||||
<input class="btn btn-inline" type="button" id="generate_new_apikey" value="Generate">
|
||||
<div class="clear-left"><p>used to give 3rd party programs limited access to SickGear</p></div>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="web_port">
|
||||
<span class="component-title">HTTP port</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="web_port" id="web_port" value="$sickbeard.WEB_PORT" class="form-control input-sm input100" />
|
||||
<p>web port to browse and access SickGear (default:8081)</p>
|
||||
<input type="text" name="web_port" id="web_port" value="$sickbeard.WEB_PORT" class="form-control input-sm input100">
|
||||
<p>web port to access and browse SickGear (default:8081)</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="web_ipv6">
|
||||
<span class="component-title">Listen on IPv6</span>
|
||||
<label for="web_log">
|
||||
<span class="component-title">HTTP logs</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="web_ipv6" id="web_ipv6" #if $sickbeard.WEB_IPV6 then 'checked="checked"' else ''#/>
|
||||
<p>attempt binding to any available IPv6 address</p>
|
||||
<input type="checkbox" name="web_log" id="web_log" #if $sickbeard.WEB_LOG then 'checked="checked"' else ''#>
|
||||
<p>enable logs from the internal web server</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="enable_https">
|
||||
<span class="component-title">Enable HTTPS</span>
|
||||
<span class="component-title">SSL enable</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="enable_https" class="enabler" id="enable_https" #if $sickbeard.ENABLE_HTTPS then 'checked="checked"' else ''#/>
|
||||
<p>enable access to the web interface using a HTTPS address</p>
|
||||
<input type="checkbox" name="enable_https" class="enabler" id="enable_https" #if $sickbeard.ENABLE_HTTPS then 'checked="checked"' else ''#>
|
||||
<p>use a HTTPS address to access the web interface</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -403,8 +416,8 @@
|
|||
<label for="https_cert">
|
||||
<span class="component-title">HTTPS certificate</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="https_cert" id="https_cert" value="$sickbeard.HTTPS_CERT" class="form-control input-sm input300" />
|
||||
<div class="clear-left"><p>file name or path to HTTPS certificate</p></div>
|
||||
<input type="text" name="https_cert" id="https_cert" value="$sickbeard.HTTPS_CERT" class="form-control input-sm input300">
|
||||
<div class="clear-left"><p>file name or path to a <b class="boldest">server.crt</b> certificate file</p></div>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -412,24 +425,34 @@
|
|||
<label for="https_key">
|
||||
<span class="component-title">HTTPS key</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="https_key" id="https_key" value="$sickbeard.HTTPS_KEY" class="form-control input-sm input300" />
|
||||
<div class="clear-left"><p>file name or path to HTTPS key</p></div>
|
||||
<input type="text" name="https_key" id="https_key" value="$sickbeard.HTTPS_KEY" class="form-control input-sm input300">
|
||||
<div class="clear-left"><p>file name or path to a <b class="boldest">server.key</b> file</p></div>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="web_ipv6">
|
||||
<span class="component-title">Listen on IPv6</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="web_ipv6" id="web_ipv6" #if $sickbeard.WEB_IPV6 then 'checked="checked"' else ''#>
|
||||
<p>attempt binding to any available IPv6 address</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="handle_reverse_proxy">
|
||||
<span class="component-title">Reverse proxy headers</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="handle_reverse_proxy" id="handle_reverse_proxy" #if $sickbeard.HANDLE_REVERSE_PROXY then 'checked="checked"' else ''#/>
|
||||
<input type="checkbox" name="handle_reverse_proxy" id="handle_reverse_proxy" #if $sickbeard.HANDLE_REVERSE_PROXY then 'checked="checked"' else ''#>
|
||||
<p>accept the following reverse proxy headers (advanced)...<br />(X-Forwarded-For, X-Forwarded-Host, and X-Forwarded-Proto)</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<input type="submit" class="btn config_submitter" value="Save Changes" />
|
||||
<input type="submit" class="btn config_submitter" value="Save Changes">
|
||||
|
||||
</fieldset>
|
||||
|
||||
|
@ -450,7 +473,7 @@
|
|||
<label>
|
||||
<span class="component-title">Branch version:</span>
|
||||
<span class="component-desc">
|
||||
<select id="branchVersion" class="form-control form-control-inline input-sm pull-left">
|
||||
<select id="branchVersion" class="form-control form-control-inline input-sm pull-left max300">
|
||||
#for $cur_branch in $sickbeard.versionCheckScheduler.action.list_remote_branches():
|
||||
<option value="$cur_branch" #if $cur_branch == $sickbeard.BRANCH then 'selected="selected"' else ''#>$cur_branch</option>
|
||||
#end for
|
||||
|
@ -467,7 +490,7 @@
|
|||
<label>
|
||||
<span class="component-title">Pull request:</span>
|
||||
<span class="component-desc">
|
||||
<select id="pullRequestVersion" class="form-control form-control-inline input-sm pull-left">
|
||||
<select id="pullRequestVersion" class="form-control form-control-inline input-sm pull-left max300">
|
||||
#for $cur_branch in $pulls:
|
||||
<option value="$cur_branch.fetch_name()" #if $cur_branch == $sickbeard.BRANCH then 'selected="selected"' else ''#>$cur_branch</option>
|
||||
#end for
|
||||
|
@ -483,7 +506,7 @@
|
|||
<label for="git_remote">
|
||||
<span class="component-title">Git remote for branch</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="git_remote" id="git_remote" value="$sickbeard.GIT_REMOTE" class="form-control input-sm input300" />
|
||||
<input type="text" name="git_remote" id="git_remote" value="$sickbeard.GIT_REMOTE" class="form-control input-sm input300">
|
||||
<div class="clear-left"><p>default:origin. Access repo configured remotes (save then refresh browser)</p></div>
|
||||
</span>
|
||||
</label>
|
||||
|
@ -493,7 +516,7 @@
|
|||
<label>
|
||||
<span class="component-title">Git executable path</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="git_path" value="$sickbeard.GIT_PATH" class="form-control input-sm input300" />
|
||||
<input type="text" name="git_path" value="$sickbeard.GIT_PATH" class="form-control input-sm input300">
|
||||
<div class="clear-left"><p>only needed if OS is unable to locate git from env</p></div>
|
||||
</span>
|
||||
</label>
|
||||
|
@ -517,7 +540,7 @@
|
|||
<label>
|
||||
<span class="component-title">Anonymous redirect</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="anon_redirect" value="$sickbeard.ANON_REDIRECT" class="form-control input-sm input300" />
|
||||
<input type="text" name="anon_redirect" value="$sickbeard.ANON_REDIRECT" class="form-control input-sm input300">
|
||||
<div class="clear-left"><p>backlink protection via anonymizer service, must end in "?"</p></div>
|
||||
</span>
|
||||
</label>
|
||||
|
@ -527,31 +550,20 @@
|
|||
<label for="encryption_version">
|
||||
<span class="component-title">Encrypt passwords</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="encryption_version" id="encryption_version" #if $sickbeard.ENCRYPTION_VERSION then 'checked="checked"' else ''#/>
|
||||
<input type="checkbox" name="encryption_version" id="encryption_version" #if $sickbeard.ENCRYPTION_VERSION then 'checked="checked"' else ''#>
|
||||
<p>in the <code>config.ini</code> file.
|
||||
<b>Warning:</b> Passwords must only contain <a target="_blank" href="<%= anon_url('http://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters') %>">ASCII characters</a></p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="calendar_unprotected">
|
||||
<span class="component-title">Unprotected calendar</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="calendar_unprotected" id="calendar_unprotected" #if $sickbeard.CALENDAR_UNPROTECTED then 'checked="checked"' else ''#/>
|
||||
<p>allow subscribing to the calendar without user and password.
|
||||
Some services like Google Calendar only work this way</p>
|
||||
</span>
|
||||
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label>
|
||||
<span class="component-title">Proxy host</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="proxy_setting" value="$sickbeard.PROXY_SETTING" class="form-control input-sm input300" />
|
||||
<div class="clear-left"><p>blank to disable or proxy to use when connecting to providers</p></div>
|
||||
<input type="text" name="proxy_setting" value="$sickbeard.PROXY_SETTING" class="form-control input-sm input300">
|
||||
<p>blank to disable</p>
|
||||
<div class="clear-left"><p>proxy address for connecting to providers (use 'PAC:Url' for PAC support)</p></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
@ -559,19 +571,19 @@
|
|||
<label for="proxy_indexers">
|
||||
<span class="component-title">Use proxy for indexers</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="proxy_indexers" id="proxy_indexers" #if True == $sickbeard.PROXY_INDEXERS then 'checked="checked"' else ''#/>
|
||||
<input type="checkbox" name="proxy_indexers" id="proxy_indexers" #if True == $sickbeard.PROXY_INDEXERS then 'checked="checked"' else ''#>
|
||||
<p>use proxy host for connecting to indexers (thetvdb, tvrage)</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<input type="submit" class="btn config_submitter" value="Save Changes" />
|
||||
<input type="submit" class="btn config_submitter" value="Save Changes">
|
||||
</fieldset>
|
||||
</div><!-- /component-group3 //-->
|
||||
|
||||
<br/>
|
||||
<h6 class="pull-right"><b class="boldest">All non-absolute folder locations are relative to <span class="path">$sickbeard.DATA_DIR</span></b></h6>
|
||||
<input type="submit" class="btn pull-left config_submitter button" value="Save Changes" />
|
||||
<input type="submit" class="btn pull-left config_submitter button" value="Save Changes">
|
||||
|
||||
</div><!-- /config-components -->
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="tv_download_dir" id="tv_download_dir" value="$sickbeard.TV_DOWNLOAD_DIR" class="form-control input-sm input350" />
|
||||
<div class="float-left">
|
||||
<div class="pull-left">
|
||||
<p>folder where download clients save <b><em class="boldest">completed</em></b> downloads.
|
||||
<b>note:</b> only use if not using SABnzbd post processing <em>or</em> if SABnzbd is on a different PC to SickGear</p>
|
||||
</div>
|
||||
|
@ -1177,14 +1177,14 @@
|
|||
<div class="metadata_options">
|
||||
<label for="${cur_id}_show_metadata"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_show_metadata" #if $cur_metadata_inst.show_metadata then $checked else ''#/> Show Metadata</label>
|
||||
<label for="${cur_id}_episode_metadata"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_episode_metadata" #if $cur_metadata_inst.episode_metadata then $checked else ''#/> Episode Metadata</label>
|
||||
<label for="${cur_id}_fanart"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_fanart" #if $cur_metadata_inst.fanart then $checked else ''#/> Show Fanart</label>
|
||||
<label for="${cur_id}_poster"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_poster" #if $cur_metadata_inst.poster then $checked else ''#/> Show Poster</label>
|
||||
<label for="${cur_id}_banner"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_banner" #if $cur_metadata_inst.banner then $checked else ''#/> Show Banner</label>
|
||||
<label for="${cur_id}_episode_thumbnails"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_episode_thumbnails" #if $cur_metadata_inst.episode_thumbnails then $checked else ''#/> Episode Thumbnails</label>
|
||||
<label for="${cur_id}_season_posters"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_season_posters" #if $cur_metadata_inst.season_posters then $checked else ''#/> Season Posters</label>
|
||||
<label for="${cur_id}_season_banners"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_season_banners" #if $cur_metadata_inst.season_banners then $checked else ''#/> Season Banners</label>
|
||||
<label for="${cur_id}_season_all_poster"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_season_all_poster" #if $cur_metadata_inst.season_all_poster then $checked else ''#/> Season All Poster</label>
|
||||
<label for="${cur_id}_season_all_banner"><input type="checkbox" class="float-left metadata_checkbox" id="${cur_id}_season_all_banner" #if $cur_metadata_inst.season_all_banner then $checked else ''#/> Season All Banner</label>
|
||||
<label for="${cur_id}_fanart"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_fanart" #if $cur_metadata_inst.fanart then $checked else ''#/> Show Fanart</label>
|
||||
<label for="${cur_id}_poster"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_poster" #if $cur_metadata_inst.poster then $checked else ''#/> Show Poster</label>
|
||||
<label for="${cur_id}_banner"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_banner" #if $cur_metadata_inst.banner then $checked else ''#/> Show Banner</label>
|
||||
<label for="${cur_id}_episode_thumbnails"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_episode_thumbnails" #if $cur_metadata_inst.episode_thumbnails then $checked else ''#/> Episode Thumbnails</label>
|
||||
<label for="${cur_id}_season_posters"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_season_posters" #if $cur_metadata_inst.season_posters then $checked else ''#/> Season Posters</label>
|
||||
<label for="${cur_id}_season_banners"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_season_banners" #if $cur_metadata_inst.season_banners then $checked else ''#/> Season Banners</label>
|
||||
<label for="${cur_id}_season_all_poster"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_season_all_poster" #if $cur_metadata_inst.season_all_poster then $checked else ''#/> Season All Poster</label>
|
||||
<label for="${cur_id}_season_all_banner"><input type="checkbox" class="metadata_checkbox" id="${cur_id}_season_all_banner" #if $cur_metadata_inst.season_all_banner then $checked else ''#/> Season All Banner</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="metadata_example_wrapper">
|
||||
|
|
|
@ -571,39 +571,6 @@
|
|||
</div>
|
||||
#end if
|
||||
|
||||
#if $hasattr($curTorrentProvider, 'options'):
|
||||
<br>
|
||||
<input type="hidden" id="tvtorrents_option_string" />
|
||||
<fieldset>
|
||||
<legend id="seed_options">Advanced options</legend>
|
||||
<div class="field-pair">
|
||||
<label >
|
||||
<span class="component-title">Seeding ratio(%) goal:</span>
|
||||
<input type="text" id="tvtorrents_seed_ratio" class="seed_option form-control input-sm input75" />
|
||||
</label>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label>
|
||||
<span class="component-title">Seeding time(h) goal:</span>
|
||||
<input type="text" id="tvtorrents_seed_time" class="seed_option form-control input-sm input75" />
|
||||
</label>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label>
|
||||
<span class="component-title">Process method:</span>
|
||||
<select id="tvtorrents_process_method" class="seed_option form-control input-sm" >
|
||||
#set $process_method_text = {'': "", 'copy': "Copy", 'move': "Move", 'hardlink': "Hard Link", 'symlink' : "Symbolic Link"}
|
||||
#for $curAction in ('', 'copy', 'move', 'hardlink', 'symlink'):
|
||||
#set $process_method = ''
|
||||
<option class="seed_option" value="$curAction" $process_method>$process_method_text[$curAction]</option>
|
||||
#end for
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
<br>
|
||||
#end if
|
||||
|
||||
</div>
|
||||
#end for
|
||||
<!-- end div for editing providers -->
|
||||
|
|
|
@ -110,20 +110,20 @@
|
|||
|
||||
<div class="field-pair">
|
||||
<label>
|
||||
<span class="component-title">Ignore words</span>
|
||||
<span class="component-title">Ignore result with any word</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="ignore_words" value="$sickbeard.IGNORE_WORDS" class="form-control input-sm input350" />
|
||||
<div class="clear-left">results containing any word in the comma separated word list will be ignored</div>
|
||||
<div class="clear-left">ignore search result <em class="grey-text">if its title contains any</em> of these comma seperated words</div>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label>
|
||||
<span class="component-title">Require words</span>
|
||||
<span class="component-title">Require at least one word</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="require_words" value="$sickbeard.REQUIRE_WORDS" class="form-control input-sm input350" />
|
||||
<div class="clear-left">results not containing all words in the comma separated word list will be ignored</div>
|
||||
<div class="clear-left">ignore search result <em class="grey-text">unless its title contains one</em> of these comma seperated words</div>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -138,26 +138,6 @@
|
|||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="recentsearch_startup">
|
||||
<span class="component-title">Recent search on startup</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="recentsearch_startup" id="recentsearch_startup" <%= html_checked if sickbeard.RECENTSEARCH_STARTUP == True else '' %>/>
|
||||
<p>start recent search on startup of SickGear</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="backlog_startup">
|
||||
<span class="component-title">Run backlog on startup</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="backlog_startup" id="backlog_startup" <%= html_checked if sickbeard.BACKLOG_STARTUP == True else '' %>/>
|
||||
<p>start processing backlogged episodes on startup of SickGear</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<input type="submit" class="btn config_submitter" value="Save Changes" />
|
||||
|
||||
</fieldset>
|
||||
|
|
|
@ -7,11 +7,10 @@
|
|||
#import os.path, os
|
||||
#import datetime
|
||||
|
||||
#set global $title=$show.name
|
||||
##set global $header = '<a></a>' %
|
||||
#set global $topmenu="manageShows"#
|
||||
#set $exceptions_string = " | ".join($show.exceptions)
|
||||
#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
|
||||
#set global $title = $show.name
|
||||
#set global $topmenu = 'home'
|
||||
#set $exceptions_string = ', '.join($show.exceptions)
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/lib/jquery.bookmarkscroll.js?$sbPID"></script>
|
||||
|
||||
|
@ -197,68 +196,69 @@
|
|||
<div id="summary">
|
||||
<table class="summaryTable pull-left">
|
||||
#if $show.network and $show.airs:
|
||||
<tr><td class="showLegend">Originally Airs: </td><td>$show.airs #if not $network_timezones.test_timeformat($show.airs) then " <font color='#FF0000'><b>(invalid Timeformat)</b></font> " else ""# on $show.network</td></tr>
|
||||
<tr><td class="showLegend grey-text">Originally airs</td><td>$show.airs #if not $network_timezones.test_timeformat($show.airs) then ' <font color="#FF0000"><b>(invalid Timeformat)</b></font> ' else ''# on $show.network</td></tr>
|
||||
#else if $show.network:
|
||||
<tr><td class="showLegend">Originally Airs: </td><td>$show.network</td></tr>
|
||||
<tr><td class="showLegend grey-text">Originally airs</td><td>$show.network</td></tr>
|
||||
#else if $show.airs:
|
||||
<tr><td class="showLegend">Originally Airs: </td><td>>$show.airs #if not $network_timezones.test_timeformat($show.airs) then " <font color='#FF0000'><b>(invalid Timeformat)</b></font> " else ""#</td></tr>
|
||||
<tr><td class="showLegend grey-text">Originally airs</td><td>$show.airs #if not $network_timezones.test_timeformat($show.airs) then ' <font color="#FF0000"><b>(invalid Timeformat)</b></font> ' else ''#</td></tr>
|
||||
#end if
|
||||
<tr><td class="showLegend">Status: </td><td>$show.status</td></tr>
|
||||
<tr><td class="showLegend grey-text">Status</td><td>$show.status</td></tr>
|
||||
#if $showLoc[1]:
|
||||
<tr><td class="showLegend">Location: </td><td>$showLoc[0]</td></tr>
|
||||
<tr><td class="showLegend grey-text">Location</td><td>$showLoc[0]</td></tr>
|
||||
#else:
|
||||
<tr><td class="showLegend"><span style="color: red;">Location: </span></td><td><span style="color: red;">$showLoc[0]</span> (dir is missing)</td></tr>
|
||||
<tr><td class="showLegend grey-text"><span style="color: red;">Location</span></td><td><span style="color: red;">$showLoc[0]</span> (dir is missing)</td></tr>
|
||||
#end if
|
||||
#set $anyQualities, $bestQualities = $Quality.splitQuality(int($show.quality))
|
||||
<tr><td class="showLegend">Quality: </td><td>
|
||||
<tr><td class="showLegend grey-text">Quality</td><td>
|
||||
#if $show.quality in $qualityPresets:
|
||||
<span class="quality $qualityPresetStrings[$show.quality]">$qualityPresetStrings[$show.quality]</span>
|
||||
#else:
|
||||
#if $anyQualities:
|
||||
<i>Initial:</i> <%=", ".join([Quality.qualityStrings[x] for x in sorted(anyQualities)])%> #if $bestQualities then " </br> " else ""#
|
||||
<i class="grey-text">Initial ...</i> <%= ', '.join([Quality.qualityStrings[x] for x in sorted(anyQualities)])%> #if $bestQualities then " </br> " else ""#
|
||||
#end if
|
||||
#if $bestQualities:
|
||||
<i>Replace with:</i> <%=", ".join([Quality.qualityStrings[x] for x in sorted(bestQualities)])%>
|
||||
<i class="grey-text">Replace with ...</i> <%= ', '.join([Quality.qualityStrings[x] for x in sorted(bestQualities)])%>
|
||||
#end if
|
||||
#end if
|
||||
<tr><td class="showLegend">Scene Name:</td><td>#if $show.exceptions then $exceptions_string else $show.name#</td></tr>
|
||||
<tr><td class="showLegend grey-text">Scene name</td><td>#if $show.exceptions then $exceptions_string else $show.name#</td></tr>
|
||||
|
||||
#if $show.rls_require_words:
|
||||
<tr><td class="showLegend">Required Words: </td><td>#echo $show.rls_require_words#</td></tr>
|
||||
#end if
|
||||
#if $show.rls_ignore_words:
|
||||
<tr><td class="showLegend">Ignored Words: </td><td>#echo $show.rls_ignore_words#</td></tr>
|
||||
<tr><td class="showLegend grey-text">Ignore with any of</td><td>#echo $show.rls_ignore_words#</td></tr>
|
||||
#end if
|
||||
#if $bwl and $bwl.get_white_keywords_for("release_group"):
|
||||
<tr><td class="showLegend">Wanted Group#if len($bwl.get_white_keywords_for("release_group"))>1 then "s" else ""#:</td>
|
||||
<td>#echo ', '.join($bwl.get_white_keywords_for("release_group"))#</td>
|
||||
#if $show.rls_require_words:
|
||||
<tr><td class="showLegend grey-text">Require one of</td><td>#echo $show.rls_require_words#</td></tr>
|
||||
#end if
|
||||
|
||||
#if $bwl and $bwl.whitelist:
|
||||
<tr><td class="showLegend grey-text">Whitelist group#if len($bwl.whitelist)>1 then 's' else ''#</td>
|
||||
<td>#echo ', '.join($bwl.whitelist)#</td>
|
||||
</tr>
|
||||
#end if
|
||||
#if $bwl and $bwl.get_black_keywords_for("release_group"):
|
||||
<tr><td class="showLegend">Unwanted Group#if len($bwl.get_black_keywords_for("release_group"))>1 then "s" else ""#:</td>
|
||||
<td>#echo ', '.join($bwl.get_black_keywords_for("release_group"))#</td>
|
||||
#if $bwl and $bwl.blacklist:
|
||||
<tr><td class="showLegend grey-text">Blacklist group#if len($bwl.blacklist)>1 then 's' else ''#</td>
|
||||
<td>#echo ', '.join($bwl.blacklist)#</td>
|
||||
</tr>
|
||||
#end if
|
||||
|
||||
<tr><td class="showLegend">Size:</td><td>$sickbeard.helpers.human(sickbeard.helpers.get_size($showLoc[0]))</td></tr>
|
||||
<tr><td class="showLegend grey-text">Size</td><td>$sickbeard.helpers.human(sickbeard.helpers.get_size($showLoc[0]))</td></tr>
|
||||
|
||||
</table>
|
||||
|
||||
<table style="width:180px; float: right; vertical-align: middle; height: 100%;">
|
||||
<tr><td class="showLegend">Info Language:</td><td><img src="$sbRoot/images/flags/${show.lang}.png" width="16" height="11" alt="$show.lang" title="$show.lang" /></td></tr>
|
||||
#if $sickbeard.USE_SUBTITLES
|
||||
<tr><td class="showLegend">Subtitles: </td><td><img src="$sbRoot/images/#if int($show.subtitles) == 1 then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr>
|
||||
#end if
|
||||
<tr><td class="showLegend">Flat Folders: </td><td><img src="$sbRoot/images/#if $show.flatten_folders == 1 or $sickbeard.NAMING_FORCE_FOLDERS then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr>
|
||||
<tr><td class="showLegend">Paused: </td><td><img src="$sbRoot/images/#if int($show.paused) == 1 then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr>
|
||||
<tr><td class="showLegend">Air-by-Date: </td><td><img src="$sbRoot/images/#if int($show.air_by_date) == 1 then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr>
|
||||
<tr><td class="showLegend">Sports: </td><td><img src="$sbRoot/images/#if int($show.is_sports) == 1 then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr>
|
||||
<tr><td class="showLegend">Anime: </td><td><img src="$sbRoot/images/#if int($show.is_anime) == 1 then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr>
|
||||
<tr><td class="showLegend">DVD Order: </td><td><img src="$sbRoot/images/#if int($show.dvdorder) == 1 then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr>
|
||||
<tr><td class="showLegend">Scene Numbering: </td><td><img src="$sbRoot/images/#if int($show.scene) == 1 then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr>
|
||||
<table class="options-on-right">
|
||||
<tr><td class="showLegendRight grey-text">Paused</td><td><img src="$sbRoot/images/#if int($show.paused) == 1 then 'yes16.png" title="Yes" alt="Yes' else 'no16.png" title="No" alt="No'#" width="16" height="16" /></td></tr>
|
||||
#if $anyQualities + $bestQualities
|
||||
<tr><td class="showLegend">Archive First Match: </td><td><img src="$sbRoot/images/#if int($show.archive_firstmatch) == 1 then "yes16.png\" alt=\"Y" else "no16.png\" alt=\"N"#" width="16" height="16" /></td></tr>
|
||||
<tr><td class="showLegendRight grey-text">Archive on first match</td><td><img src="$sbRoot/images/#if int($show.archive_firstmatch) == 1 then 'yes16.png" title="Yes" alt="Yes' else 'no16.png" title="No" alt="No'#" width="16" height="16" /></td></tr>
|
||||
#end if
|
||||
<tr><td class="showLegendRight grey-text">Flat folder structure</td><td><img src="$sbRoot/images/#if $show.flatten_folders == 1 or $sickbeard.NAMING_FORCE_FOLDERS then 'yes16.png" title="Yes" alt="Yes' else 'no16.png" title="No" alt="No'#" width="16" height="16" /></td></tr>
|
||||
<tr><td class="showLegendRight grey-text">Air by date naming</td><td><img src="$sbRoot/images/#if int($show.air_by_date) == 1 then 'yes16.png" title="Yes" alt="Yes' else 'no16.png" title="No" alt="No'#" width="16" height="16" /></td></tr>
|
||||
<tr><td class="showLegendRight grey-text">Use DVD order</td><td><img src="$sbRoot/images/#if int($show.dvdorder) == 1 then 'yes16.png" title="Yes" alt="Yes' else 'no16.png" title="No" alt="No'#" width="16" height="16" /></td></tr>
|
||||
<tr><td class="showLegendRight grey-text">Scene numbering</td><td><img src="$sbRoot/images/#if int($show.scene) == 1 then 'yes16.png" title="Yes" alt="Yes' else 'no16.png" title="No" alt="No'#" width="16" height="16" /></td></tr>
|
||||
#if $sickbeard.USE_SUBTITLES
|
||||
<tr><td class="showLegendRight grey-text">Subtitles</td><td><img src="$sbRoot/images/#if int($show.subtitles) == 1 then 'yes16.png" title="Yes" alt="Yes' else 'no16.png" title="No" alt="No'#" width="16" height="16" /></td></tr>
|
||||
#end if
|
||||
<tr><td class="showLegendRight grey-text">Show is sports</td><td><img src="$sbRoot/images/#if int($show.is_sports) == 1 then 'yes16.png" title="Yes" alt="Yes' else 'no16.png" title="No" alt="No'#" width="16" height="16" /></td></tr>
|
||||
<tr><td class="showLegendRight grey-text">Show is anime</td><td><img src="$sbRoot/images/#if int($show.is_anime) == 1 then 'yes16.png" title="Yes" alt="Yes' else 'no16.png" title="No" alt="No'#" width="16" height="16" /></td></tr>
|
||||
<tr><td class="showLegendRight grey-text">Info language</td><td><img src="$sbRoot/images/flags/${show.lang}.png" width="16" height="11" alt="$show.lang" title="$show.lang" /></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
@ -490,4 +490,4 @@
|
|||
|
||||
</table>
|
||||
|
||||
#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
||||
|
|
|
@ -3,271 +3,303 @@
|
|||
#from sickbeard import common
|
||||
#from sickbeard import exceptions
|
||||
#from sickbeard import scene_exceptions
|
||||
#from sickbeard.blackandwhitelist import *
|
||||
#set global $title="Edit " + $show.name
|
||||
#set global $header="Edit " + $show.name
|
||||
#import sickbeard.blackandwhitelist
|
||||
|
||||
#set global $sbPath=".."
|
||||
#set global $title = 'Edit ' + $show.name
|
||||
#set global $header = 'Edit ' + $show.name
|
||||
|
||||
#set global $topmenu="home"
|
||||
#set global $sbPath = '..'
|
||||
#set html_checked = ' checked="checked"'
|
||||
#set html_disabled = ' disabled="disabled"'
|
||||
|
||||
#set global $topmenu = 'home'
|
||||
#import os.path
|
||||
#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
|
||||
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
#else
|
||||
<h1 class="title">$title</h1>
|
||||
#end if
|
||||
|
||||
<div id="editShow">
|
||||
<script type="text/javascript" src="$sbRoot/js/qualityChooser.js?$sbPID"></script>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
<!--
|
||||
\$(document).ready(function(){
|
||||
|
||||
\$.getJSON('$sbRoot/home/addShows/getIndexerLanguages', {}, function(data) {
|
||||
var resultStr = '';
|
||||
\$.getJSON('$sbRoot/home/addShows/getIndexerLanguages', {}, function(data) {
|
||||
var resultStr = '';
|
||||
|
||||
if (data.results.length == 0) {
|
||||
flag = ' class="flag" style="background-image:url($sbRoot/images/flags/${show.lang}.png)"';
|
||||
resultStr = '<option value="$show.lang" selected="selected" + flag>$show.lang</option>';
|
||||
} else {
|
||||
var current_lang_added = false;
|
||||
\$.each(data.results, function(index, obj) {
|
||||
if (data.results.length == 0) {
|
||||
flag = ' class="flag" style="background-image:url($sbRoot/images/flags/${show.lang}.png)"';
|
||||
resultStr = '<option value="$show.lang" selected="selected" + flag>$show.lang</option>';
|
||||
} else {
|
||||
var current_lang_added = false;
|
||||
\$.each(data.results, function(index, obj) {
|
||||
|
||||
if (obj == "$show.lang") {
|
||||
selected = ' selected="selected"';
|
||||
current_lang_added = true;
|
||||
}
|
||||
else {
|
||||
selected = '';
|
||||
}
|
||||
if (obj == '$show.lang') {
|
||||
selected = ' selected="selected"';
|
||||
current_lang_added = true;
|
||||
}
|
||||
else {
|
||||
selected = '';
|
||||
}
|
||||
|
||||
flag = ' class="flag" style="background-image:url($sbRoot/images/flags/' + obj + '.png);"';
|
||||
resultStr += '<option value="' + obj + '"' + selected + flag + '>' + obj + '</option>';
|
||||
});
|
||||
if (!current_lang_added)
|
||||
resultStr += '<option value="$show.lang" selected="selected">$show.lang</option>';
|
||||
flag = ' class="flag" style="background-image:url($sbRoot/images/flags/' + obj + '.png);"';
|
||||
resultStr += '<option value="' + obj + '"' + selected + flag + '>' + obj + '</option>';
|
||||
});
|
||||
|
||||
}
|
||||
\$('#indexerLangSelect').html(resultStr)
|
||||
|
||||
});
|
||||
if (!current_lang_added)
|
||||
resultStr += '<option value="$show.lang" selected="selected">$show.lang</option>';
|
||||
|
||||
}
|
||||
\$('#indexerLangSelectEdit').html(resultStr)
|
||||
});
|
||||
});
|
||||
//-->
|
||||
</script>
|
||||
|
||||
<br>
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
#else
|
||||
<h1 class="title">$title</h1>
|
||||
#end if
|
||||
|
||||
<form action="editShow" method="post">
|
||||
<input type="hidden" name="show" value="$show.indexerid" />
|
||||
<b>Location:</b> <input type="text" name="location" id="location" value="$show._location" class="form-control form-control-inline input-sm input350" /><br />
|
||||
<br />
|
||||
<form action="editShow" method="post" id="addShowForm">
|
||||
<input type="hidden" name="show" value="$show.indexerid">
|
||||
|
||||
<div id="editShow" class="stepDiv">
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="paused">
|
||||
<span class="component-title">Paused</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="paused" id="paused"#if 1 == $show.paused then $html_checked else ''#>
|
||||
<p>enable to pause searching providers for show episodes</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
#set $qualities = $common.Quality.splitQuality(int($show.quality))
|
||||
#set global $anyQualities = $qualities[0]
|
||||
#set global $bestQualities = $qualities[1]
|
||||
#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_qualityChooser.tmpl")
|
||||
<br />
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_qualityChooser.tmpl')
|
||||
|
||||
#if $anyQualities + $bestQualities
|
||||
<b>Archive on first match: </b>
|
||||
<input type="checkbox" name="archive_firstmatch" #if $show.archive_firstmatch == 1 then "checked=\"checked\"" else ""# /><br>
|
||||
(check this to have the episode archived after the first best match is found from your archive quality list)
|
||||
<br />
|
||||
<br />
|
||||
<div class="field-pair">
|
||||
<label for="archive_firstmatch">
|
||||
<span class="component-title">Archive on first match</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="archive_firstmatch" id="archive_firstmatch"#if $show.archive_firstmatch == 1 then $html_checked else ''#>
|
||||
<p>enable to have the episode archived after the first best match is found from your archive quality list</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
#end if
|
||||
</div>
|
||||
|
||||
<b>Scene Exception:</b>
|
||||
<input type="text" id="SceneName" class="form-control form-control-inline input-sm input200">
|
||||
<input class="btn btn-inline" type="button" value="Add" id="addSceneName"><br />
|
||||
<div class="field-pair">
|
||||
<label for="SceneName">
|
||||
<span class="component-title input">Scene exception</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" id="SceneName" class="form-control form-control-inline input-sm input200">
|
||||
<input class="btn btn-inline" type="button" value="Add" id="addSceneName">
|
||||
<p class="clear-left">add alternative release names found on search providers for <b class="boldest grey-text">$show.name</b></p>
|
||||
</span>
|
||||
<span class="component-desc">
|
||||
<div id="SceneException">
|
||||
<h4>Exceptions list (multi-selectable)</h4>
|
||||
<select id="exceptions_list" name="exceptions_list" multiple="multiple" class="input200" style="min-height:90px; float:left" >
|
||||
#for $cur_exception in $show.exceptions:
|
||||
<option value="$cur_exception">$cur_exception</option>
|
||||
#end for
|
||||
</select>
|
||||
<span><p>this list overrides the original name<br />to search, it doesn't append to it</p></span>
|
||||
<div>
|
||||
<input id="removeSceneName" value="Remove" class="btn pull-left" type="button" style="margin-top: 10px;"/>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</label>
|
||||
<div style="clear:right"> </div>
|
||||
</div>
|
||||
|
||||
<div id="SceneException" >
|
||||
<div class="field-pair">
|
||||
<label for="rls_ignore_words">
|
||||
<span class="component-title input">Ignore result with any word</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="rls_ignore_words" id="rls_ignore_words" value="$show.rls_ignore_words" class="form-control form-control-inline input-sm input350">
|
||||
<p>e.g. [word1,word2, ... ,word_n]</p>
|
||||
<span><p>ignore search result <em class="grey-text">if its title contains any</em> of these comma seperated words</p></span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p>This will <b>affect the episode show search</b> on nzb and torrent provider.<br />
|
||||
This list overrides the original name, it doesn't append to it.<br />
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="pull-left" style="text-align:center;">
|
||||
<h4>Exceptions List</h4>
|
||||
<select id="exceptions_list" name="exceptions_list" multiple="multiple" style="min-width:10em;" >
|
||||
#for $cur_exception in $show.exceptions:
|
||||
<option value="$cur_exception">$cur_exception</option>
|
||||
#end for
|
||||
</select>
|
||||
<div>
|
||||
<input id="removeSceneName" value="Remove" class="btn float-left" type="button" style="margin-top: 10px;"/>
|
||||
</div>
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
<br />
|
||||
<div class="field-pair">
|
||||
<label for="rls_require_words">
|
||||
<span class="component-title input">Require at least one word</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="rls_require_words" id="rls_require_words" value="$show.rls_require_words" class="form-control form-control-inline input-sm input350">
|
||||
<p>e.g. [word1,word2, ... ,word_n]</p>
|
||||
<span><p>ignore search result <em class="grey-text">unless its title contains one</em> of these comma seperated words</p></span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<b>Info Language:</b> <select name="indexerLang" id="indexerLangSelect" class="form-control form-control-inline input-sm"></select><br />
|
||||
Note: This will only affect the language of the retrieved metadata file contents and episode filenames.<br />
|
||||
This <b>DOES NOT</b> allow SickGear to download non-english TV episodes!<br />
|
||||
<br />
|
||||
<div class="field-pair">
|
||||
<label for="location">
|
||||
<span class="component-title input">Location for files</span>
|
||||
<span class="component-desc">
|
||||
<input type="text" name="location" id="location" value="$show._location" class="form-control form-control-inline input-sm input350">
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<b>Flatten files (no folders):</b> <input type="checkbox" name="flatten_folders" #if $show.flatten_folders == 1 and not $sickbeard.NAMING_FORCE_FOLDERS then "checked=\"checked\"" else ""# #if $sickbeard.NAMING_FORCE_FOLDERS then "disabled=\"disabled\"" else ""#/><br /><br />
|
||||
<b>Paused:</b> <input type="checkbox" name="paused" #if $show.paused == 1 then "checked=\"checked\"" else ""# /><br /><br />
|
||||
<b>Subtitles:</b> <input type="checkbox" name="subtitles"#if $show.subtitles == 1 and $sickbeard.USE_SUBTITLES then " checked=\"checked\"" else ""##if not $sickbeard.USE_SUBTITLES then " disabled=\"disabled\"" else ""#/><br /><br />
|
||||
<div class="field-pair">
|
||||
<label for="flatten_folders">
|
||||
<span class="component-title">Flat folder structure</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="flatten_folders" id="flatten_folders"#if 1 == $show.flatten_folders and not $sickbeard.NAMING_FORCE_FOLDERS then $html_checked else ''##if $sickbeard.NAMING_FORCE_FOLDERS then $html_disabled else ''#>
|
||||
<p>enable to prevent creating the folders normally used to group seasons</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<b>Scene Numbering: </b>
|
||||
<input type="checkbox" name="scene" #if $show.scene == 1 then "checked=\"checked\"" else ""# /><br/>
|
||||
(check this if you wish to search by scene numbering, uncheck to search by indexer numbering)
|
||||
<br/><br/>
|
||||
<b>Air by date: </b>
|
||||
<input type="checkbox" name="air_by_date" #if $show.air_by_date == 1 then "checked=\"checked\"" else ""# /><br />
|
||||
(check this if the show is released as Show.03.02.2010 rather than Show.S02E03)
|
||||
<br /><br />
|
||||
<b>Sports: </b>
|
||||
<input type="checkbox" name="sports" #if $show.sports == 1 then "checked=\"checked\"" else ""# /><br />
|
||||
(check this if the show is a sporting or MMA event)
|
||||
<br /><br />
|
||||
<b>Anime: </b>
|
||||
<input type="checkbox" name="anime" #if $show.is_anime then "CHECKED" else ""#><br />
|
||||
(check this if the show is released as Show.265 rather than Show.S02E03, this show is an anime)
|
||||
<br /><br />
|
||||
<b>DVD Order: </b>
|
||||
<input type="checkbox" name="dvdorder" #if $show.dvdorder == 1 then "checked=\"checked\"" else ""# /><br/>
|
||||
(check this if you wish to use the DVD order instead of the Airing order)
|
||||
<br/><br/>
|
||||
<div class="field-pair">
|
||||
<label for="air_by_date">
|
||||
<span class="component-title">Air by date episode names</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="air_by_date" id="air_by_date"#if 1 == $show.air_by_date then $html_checked else ''#>
|
||||
<p>enable if episode releases are named ... <em class="grey-text">Show.03.02.2010</em> instead of <em class="grey-text">Show.S02E03</em></p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<b>Ignored Words:</b> <input type="text" name="rls_ignore_words" id="rls_ignore_words" value="$show.rls_ignore_words" class="form-control form-control-inline input-sm input350" /><br />
|
||||
Results with any of these words in the title will be filtered out <br />
|
||||
Separate words with a comma, e.g. "word1,word2,word3"
|
||||
<br /><br />
|
||||
<div class="field-pair">
|
||||
<label for="dvdorder">
|
||||
<span class="component-title">Use DVD order</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="dvdorder" id="dvdorder"#if 1 == $show.dvdorder then $html_checked else ''#>
|
||||
<p>for episode titles, numbering etc. instead of the order the show aired on the network</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<b>Required Words:</b> <input type="text" name="rls_require_words" id="rls_require_words" value="$show.rls_require_words" class="form-control form-control-inline input-sm input350" /><br />
|
||||
Results without one of these words in the title will be filtered out <br />
|
||||
Separate words with a comma, e.g. "word1,word2,word3"
|
||||
<br /><br />
|
||||
<div class="field-pair">
|
||||
<label for="scene">
|
||||
<span class="component-title">Scene numbering</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="scene" id="scene"#if $show.scene == 1 then $html_checked else ''#>
|
||||
<p>search for episodes that are numbered by scene groups instead of by the TV network</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair" style="margin-bottom:10px">
|
||||
<label for="indexerLangSelectEdit">
|
||||
<span class="component-title input">Info language</span>
|
||||
<span class="component-desc">
|
||||
<select name="indexerLang" id="indexerLangSelectEdit" class="form-control form-control-inline input-sm"></select>
|
||||
<span>attempt to fetch show data and episode filenames in this language</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="subtitles">
|
||||
<span class="component-title">Subtitles</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="subtitles" id="subtitles"#if 1 == $show.subtitles and $sickbeard.USE_SUBTITLES then $html_checked else ''##if not $sickbeard.USE_SUBTITLES then $html_disabled else ''#>
|
||||
<p#if not $sickbeard.USE_SUBTITLES then ' class="grey-text"><del' else ''#>attempt to download episode subtitles for this show#if not $sickbeard.USE_SUBTITLES then '</del> ... (<span class="red-text">note: first <a href="%s/config/subtitles/">enable the subtitle system here</a></span>)' % $sbRoot else ''#</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="sports">
|
||||
<span class="component-title">Show is sports</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="sports" id="sports"#if 1 == $show.sports then $html_checked else ''#>
|
||||
<p>enable to treat this show as a sporting or MMA event</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label for="anime">
|
||||
<span class="component-title">Show is anime</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="anime" id="anime"#if $show.is_anime then $html_checked else ''#>
|
||||
<p>enable if this show is anime and episode releases are named ... <em class="grey-text">Show.265</em> instead of <em class="grey-text">Show.S02E03</em></p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
#if $show.is_anime:
|
||||
#from sickbeard.blackandwhitelist import *
|
||||
#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_blackwhitelist.tmpl")
|
||||
#import sickbeard.blackandwhitelist
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_blackwhitelist.tmpl')
|
||||
<script type="text/javascript" src="$sbRoot/js/blackwhite.js?$sbPID"></script>
|
||||
#end if
|
||||
|
||||
<input type="hidden" name="whitelist" id="whitelist"/>
|
||||
<input type="hidden" name="blacklist" id="blacklist"/>
|
||||
|
||||
<input type="submit" id="submit" value="Submit" class="btn btn-primary" />
|
||||
</form>
|
||||
<input type="submit" id="submit" value="Submit" class="btn btn-primary" />
|
||||
</form>
|
||||
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
<!--
|
||||
var all_exceptions = new Array;
|
||||
var all_exceptions = new Array;
|
||||
|
||||
jQuery('#location').fileBrowser({ title: 'Select Show Location' });
|
||||
|
||||
\$('#submit').click(function(){
|
||||
jQuery('#location').fileBrowser({ title: 'Select Show Location' });
|
||||
|
||||
\$('#submit').click(function(){
|
||||
all_exceptions = []
|
||||
|
||||
\$("#exceptions_list option").each ( function() {
|
||||
\$('#exceptions_list option').each ( function() {
|
||||
all_exceptions.push( \$(this).val() );
|
||||
});
|
||||
|
||||
\$("#exceptions_list").val(all_exceptions);
|
||||
|
||||
var realvalues = [];
|
||||
|
||||
\$('#white option').each(function(i, selected) {
|
||||
realvalues[i] = \$(selected).val();
|
||||
});
|
||||
\$("#whitelist").val(realvalues.join(","));
|
||||
|
||||
realvalues = [];
|
||||
\$('#black option').each(function(i, selected) {
|
||||
realvalues[i] = \$(selected).val();
|
||||
});
|
||||
\$("#blacklist").val(realvalues.join(","));
|
||||
\$('#exceptions_list').val(all_exceptions);
|
||||
#if $show.is_anime:
|
||||
generate_bwlist()
|
||||
#end if
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
\$('#addSceneName').click(function() {
|
||||
var scene_ex = \$('#SceneName').val()
|
||||
var option = \$("<option>")
|
||||
\$('#addSceneName').click(function() {
|
||||
var scene_ex = \$('#SceneName').val()
|
||||
var option = \$('<option>')
|
||||
all_exceptions = []
|
||||
|
||||
\$("#exceptions_list option").each ( function() {
|
||||
all_exceptions.push( \$(this).val() )
|
||||
});
|
||||
|
||||
\$('#SceneName').val('')
|
||||
|
||||
if (jQuery.inArray(scene_ex, all_exceptions) > -1 || (scene_ex == ''))
|
||||
return
|
||||
\$('#exceptions_list option').each ( function() {
|
||||
all_exceptions.push( \$(this).val() )
|
||||
});
|
||||
|
||||
\$("#SceneException").show()
|
||||
\$('#SceneName').val('')
|
||||
|
||||
option.attr("value",scene_ex)
|
||||
option.html(scene_ex)
|
||||
return option.appendTo('#exceptions_list');
|
||||
});
|
||||
|
||||
\$('#removeSceneName').click(function() {
|
||||
if (jQuery.inArray(scene_ex, all_exceptions) > -1 || (scene_ex == ''))
|
||||
return
|
||||
|
||||
\$('#SceneException').show()
|
||||
|
||||
option.attr('value',scene_ex)
|
||||
option.html(scene_ex)
|
||||
return option.appendTo('#exceptions_list');
|
||||
});
|
||||
|
||||
\$('#removeSceneName').click(function() {
|
||||
\$('#exceptions_list option:selected').remove();
|
||||
|
||||
\$(this).toggle_SceneException()
|
||||
});
|
||||
|
||||
$.fn.toggle_SceneException = function() {
|
||||
all_exceptions = []
|
||||
|
||||
\$("#exceptions_list option").each ( function() {
|
||||
});
|
||||
|
||||
$.fn.toggle_SceneException = function() {
|
||||
all_exceptions = []
|
||||
|
||||
\$('#exceptions_list option').each ( function() {
|
||||
all_exceptions.push( \$(this).val() );
|
||||
});
|
||||
|
||||
if (all_exceptions == '')
|
||||
\$("#SceneException").hide();
|
||||
});
|
||||
|
||||
if ('' == all_exceptions)
|
||||
\$('#SceneException').hide();
|
||||
else
|
||||
\$("#SceneException").show();
|
||||
\$('#SceneException').show();
|
||||
}
|
||||
|
||||
\$(this).toggle_SceneException();
|
||||
|
||||
\$('#removeW').click(function() {
|
||||
return !\$('#white option:selected').remove().appendTo('#pool');
|
||||
});
|
||||
\$('#addW').click(function() {
|
||||
return !\$('#pool option:selected').remove().appendTo('#white');
|
||||
});
|
||||
\$('#addB').click(function() {
|
||||
return !\$('#pool option:selected').remove().appendTo('#black');
|
||||
});
|
||||
\$('#removeP').click(function() {
|
||||
return !\$('#pool option:selected').remove();
|
||||
});
|
||||
\$('#removeB').click(function() {
|
||||
return !\$('#black option:selected').remove().appendTo('#pool');
|
||||
});
|
||||
|
||||
\$('#addToWhite').click(function() {
|
||||
var group = \$('#addToPoolText').attr("value")
|
||||
if(group == "")
|
||||
return
|
||||
\$('#addToPoolText').attr("value", "")
|
||||
var option = \$("<option>")
|
||||
option.attr("value",group)
|
||||
option.html(group)
|
||||
return option.appendTo('#white');
|
||||
});
|
||||
\$('#addToBlack').click(function() {
|
||||
var group = \$('#addToPoolText').attr("value")
|
||||
if(group == "")
|
||||
return
|
||||
\$('#addToPoolText').attr("value", "")
|
||||
var option = \$("<option>")
|
||||
option.attr("value",group)
|
||||
option.html(group)
|
||||
return option.appendTo('#black');
|
||||
});
|
||||
|
||||
//-->
|
||||
</script>
|
||||
</div>
|
||||
#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
||||
|
|
|
@ -108,6 +108,19 @@
|
|||
\$(this).removeClass(sortdir).addClass(newdir);
|
||||
uiSortBy(\$('#sort').val());
|
||||
});
|
||||
|
||||
\$('.carousel').on('slide.bs.carousel', function () {
|
||||
imagesLoaded('.daybyday-show', function() {
|
||||
jQuery.each(\$container, function(j) {
|
||||
this.isotope('layout');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
\$('div[title!=""], span[title!=""]').qtip({style: {classes: 'qtip-rounded qtip-shadow'},
|
||||
position: {viewport: \$(window), my: 'left center', adjust: {y: -10, x: 0}},
|
||||
show: {solo: true}
|
||||
});
|
||||
});
|
||||
//-->
|
||||
</script>
|
||||
|
@ -116,7 +129,7 @@
|
|||
<style type="text/css">
|
||||
#SubMenu {display:none}
|
||||
#if 'daybyday' == $layout:
|
||||
.caret {
|
||||
.ep-caret {
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
margin-right: 2px;
|
||||
|
@ -133,7 +146,7 @@
|
|||
</style>
|
||||
|
||||
<div class="h2footer pull-right">
|
||||
<span>Layout:
|
||||
<span>Layout
|
||||
<select name="layout" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
|
||||
<option value="$sbRoot/setEpisodeViewLayout/?layout=banner" #if 'banner' == $sickbeard.EPISODE_VIEW_LAYOUT then 'selected="selected"' else ''#>Banner</option>
|
||||
<option value="$sbRoot/setEpisodeViewLayout/?layout=daybyday" #if 'daybyday' == $sickbeard.EPISODE_VIEW_LAYOUT then 'selected="selected"' else ''#>Day by Day</option>
|
||||
|
@ -144,7 +157,7 @@
|
|||
|
||||
<span>Sort
|
||||
#if 'daybyday' == $layout:
|
||||
<span id="sort-dir" data-sort-dir="asc" class="caret asc" title="Click to sort descending"> </span>
|
||||
<span id="sort-dir" data-sort-dir="asc" class="caret ep-caret asc" title="Click to sort descending"> </span>
|
||||
#end if
|
||||
By
|
||||
#if 'daybyday' == $layout:
|
||||
|
@ -161,7 +174,7 @@
|
|||
</select>
|
||||
</span>
|
||||
|
||||
<span>View Paused:
|
||||
<span>View Paused
|
||||
<select name="viewpaused" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;">
|
||||
<option value="$sbRoot/toggleEpisodeViewDisplayPaused"<%= (' selected="selected"', '')[True == sickbeard.EPISODE_VIEW_DISPLAY_PAUSED] %>>Hidden</option>
|
||||
<option value="$sbRoot/toggleEpisodeViewDisplayPaused"<%= ('', ' selected="selected"')[True == sickbeard.EPISODE_VIEW_DISPLAY_PAUSED] %>>Shown</option>
|
||||
|
@ -313,8 +326,9 @@
|
|||
#end if
|
||||
#end if
|
||||
|
||||
#set $show_id = '%s_%sx%s' % (str($cur_result['showid']), str($cur_result['season']), str($cur_result['episode']))
|
||||
<!-- start $cur_result['show_name'] //-->
|
||||
<tr class="$show_div">
|
||||
<tr id="show-${show_id}" class="$show_div" data-rawname="$cur_result['show_name']">
|
||||
## forced to use a div to wrap airdate, the column sort went crazy with a span
|
||||
<td align="center" class="nowrap">
|
||||
<div class="${fuzzydate}">$sbdatetime.sbdatetime.sbfdatetime($cur_result['localtime']).decode($sickbeard.SYS_ENCODING)</div><span class="sort_data">$time.mktime($cur_result['localtime'].timetuple())</span>
|
||||
|
@ -332,7 +346,7 @@
|
|||
|
||||
<td>
|
||||
#if $cur_result['description']:
|
||||
<img alt="" src="$sbRoot/images/info32.png" height="16" width="16" class="plotInfo" id="plot_info_<%= '%s_%s_%s' % (str(cur_result['showid']), str(cur_result['season']), str(cur_result['episode'])) %>" />
|
||||
<img alt="" src="$sbRoot/images/info32.png" height="16" width="16" class="plotInfo" id="plot-${show_id}" />
|
||||
#else:
|
||||
<img alt="" src="$sbRoot/images/info32.png" width="16" height="16" class="plotInfoNone" />
|
||||
#end if
|
||||
|
@ -590,96 +604,186 @@
|
|||
<!-- end non list view //-->
|
||||
#end if
|
||||
|
||||
|
||||
|
||||
#if 'daybyday' == $layout:
|
||||
|
||||
#set $today = datetime.date.today()
|
||||
#set $dates = [$today + datetime.timedelta(days = $i) for $i in range(7)]
|
||||
#set $tbl_day = 0
|
||||
#set $shows_overdue = []
|
||||
#set $shows_soon = []
|
||||
#set $shows_future = []
|
||||
|
||||
#set $state_overdue = 'listing-overdue'
|
||||
#set $state_current = 'listing-current'
|
||||
#set $state_soon = 'listing-soon'
|
||||
#set $state_future = 'listing-default'
|
||||
|
||||
#for $cur_result in $sql_results:
|
||||
#if int($cur_result['paused']) and not $sickbeard.EPISODE_VIEW_DISPLAY_PAUSED:
|
||||
#continue
|
||||
#end if
|
||||
#if $cur_result['runtime']:
|
||||
#set $air_date = $cur_result['localtime'].date()
|
||||
#set $end_datetime = $cur_result['localtime'] + datetime.timedelta(minutes = $cur_result['runtime'])
|
||||
#if $air_date >= $next_week.date():
|
||||
#set $cur_result['state'] = ''
|
||||
$shows_future.append($cur_result)
|
||||
#elif $cur_result['localtime'] > $today
|
||||
#set $cur_result['state'] = ''
|
||||
$shows_soon.append($cur_result)
|
||||
#elif $end_datetime > $today
|
||||
#set $cur_result['state'] = $state_current
|
||||
#set $cur_result['state-title'] = 'Currently On Air'
|
||||
$shows_soon.append($cur_result)
|
||||
#elif $air_date == $today.date():
|
||||
#set $cur_result['state'] = $state_overdue
|
||||
#set $cur_result['state-title'] = 'Overdue'
|
||||
$shows_soon.append($cur_result)
|
||||
#else
|
||||
#set $cur_result['state'] = $state_overdue
|
||||
#set $cur_result['state-title'] = 'Overdue'
|
||||
$shows_overdue.append($cur_result)
|
||||
#end if
|
||||
#else
|
||||
#set $cur_result['state'] = $state_soon
|
||||
$shows_soon.append($cur_result)
|
||||
#end if
|
||||
#end for
|
||||
|
||||
##set $state_init = [int(bool($shows_overdue)), ($state_soon, $state_overdue)[0 < len($shows_overdue)]] ## default overdue
|
||||
#set $state_init = [int(bool($shows_overdue)), $state_soon] ## default soon
|
||||
|
||||
#set $dates_future = sorted({$i['localtime'].date():$i for $i in $shows_future})
|
||||
#set $rounded_week = len($dates_future)/7*7 + int(bool(len($dates_future)%7))*7
|
||||
#set $dates_future += [$dates_future[-1] + datetime.timedelta(days = 1 + $i) for $i in range($rounded_week - len($dates_future))]
|
||||
#set $num_weeks = $rounded_week/7
|
||||
|
||||
<input type="hidden" id="sbRoot" value="$sbRoot" />
|
||||
|
||||
<br>
|
||||
<br>
|
||||
<div class="daybydayWrapper"> <!-- style="width:1600px" -->
|
||||
#for $day in $dates
|
||||
#set $tbl_day += 1
|
||||
<div class="daybydayCarouselContainer">
|
||||
<div id="Carousel" class="carousel slide">
|
||||
|
||||
#set $col_class = ''
|
||||
#if 1 == $tbl_day
|
||||
#set $col_class = 'today'
|
||||
#end if
|
||||
#set $col_class = '%s %s' % ($col_class, ('even', 'odd')[1 == tbl_day % 2])
|
||||
<div class="controlsBlock">
|
||||
<a class="left carousel-control" href="#Carousel" data-slide="prev"><i class="glyphicon glyphicon-chevron-left"></i></a>
|
||||
<a class="right carousel-control" href="#Carousel" data-slide="next"><i class="glyphicon glyphicon-chevron-right"></i></a>
|
||||
<div class="carousel-indicators">
|
||||
|
||||
<div class="day-of-week $col_class">
|
||||
<div class="day-number">
|
||||
<div class="number">$sbdatetime.sbdatetime.sbfdate($day, ' %d').decode($sickbeard.SYS_ENCODING).replace(' 0', ' ')</div>
|
||||
<div class="day">
|
||||
<span class="visible-lg">$sbdatetime.sbdatetime.sbfdate($day, '%A').decode($sickbeard.SYS_ENCODING).capitalize()</span>
|
||||
<span class="hidden-lg">$sbdatetime.sbdatetime.sbfdate($day, '%a').decode($sickbeard.SYS_ENCODING).capitalize()</span>
|
||||
</div>
|
||||
<div class="month">
|
||||
<span class="visible-lg">$sbdatetime.sbdatetime.sbfdate($day, '%B').decode($sickbeard.SYS_ENCODING).capitalize()</span>
|
||||
<span class="hidden-lg">$sbdatetime.sbdatetime.sbfdate($day, '%b').decode($sickbeard.SYS_ENCODING).capitalize()</span>
|
||||
#set $slide_id = 0
|
||||
#if len($shows_overdue)
|
||||
<li data-target="#Carousel" data-slide-to="$slide_id" class="$state_overdue#if $state_init[1] == $state_overdue then ' active' else ''#"></li>
|
||||
#set $slide_id = 1
|
||||
#end if
|
||||
<li data-target="#Carousel" data-slide-to="$slide_id" class="$state_soon#if $state_init[1] == $state_soon then ' active' else ''#"></li>
|
||||
#set $slide_id += 1
|
||||
|
||||
#for $i in range($slide_id, $slide_id + $num_weeks)
|
||||
<li data-target="#Carousel" data-slide-to="${i}" class="$state_future#if $state_init[1] == $state_future and $state_init[0] == $i then ' active' else ''#"></li>
|
||||
#end for
|
||||
</div>
|
||||
</div>
|
||||
<div id="$sbdatetime.sbdatetime.sbfdate($day, 'day%w')">
|
||||
|
||||
#set $day_has_show = False
|
||||
#for $cur_result in $sql_results:
|
||||
#if int($cur_result['paused']) and not $sickbeard.EPISODE_VIEW_DISPLAY_PAUSED:
|
||||
#continue
|
||||
#end if
|
||||
|
||||
#set $cur_indexer = int($cur_result['indexer'])
|
||||
#set $runtime = $cur_result['runtime']
|
||||
#set $airday = $cur_result['localtime'].date()
|
||||
<div class="carousel-inner">
|
||||
|
||||
#if $airday == $day:
|
||||
#set $day_has_show = True
|
||||
#set $airtime = $sbdatetime.sbdatetime.sbftime($cur_result['localtime'], markup=True).decode($sickbeard.SYS_ENCODING)
|
||||
#set $img_tag = '<img'
|
||||
#set $plot_class = 'class="img-responsive'
|
||||
#set $title_text = ''
|
||||
#if $cur_result['description']:
|
||||
#set $img_tag += ' id="plot_info_%s_%s_%s"' % (str($cur_result['showid']), str($cur_result['season']), str($cur_result['episode']))
|
||||
#set $plot_class += ' plot-daybyday'
|
||||
#else
|
||||
#set $title_text = $cur_result['show_name']
|
||||
#for $shows, $state in [[$shows_overdue, $state_overdue], [$shows_soon, $state_soon], [$shows_future, $state_future]]
|
||||
#if 0 == len($shows) and ($state_overdue == $state or $state_future == $state)
|
||||
#continue
|
||||
#end if
|
||||
#set $week_num = 0
|
||||
#set $num_weeks = 1
|
||||
#while ($num_weeks)
|
||||
#if $state_future == $state
|
||||
#set $dates = $dates_future[$week_num*7:$week_num*7+7]
|
||||
#if 0 == $week_num
|
||||
#set $num_weeks = $rounded_week/7
|
||||
#end if
|
||||
#set $week_num += 1
|
||||
#else
|
||||
#set $dates = [($today + datetime.timedelta(days = ($i, -7+$i)[$state_overdue == $state])).date() for $i in range(7)]
|
||||
#end if
|
||||
#set $num_weeks -= 1
|
||||
|
||||
<div id="show-$cur_result['showid']" class="daybyday-show" data-name="$cur_result['data_show_name']" data-season="$cur_result['season']" data-episode="$cur_result['episode']" data-network="$cur_result['data_network']" data-time="$time.mktime($cur_result['localtime'].timetuple())">
|
||||
<div class="poster">
|
||||
<a title="${title_text}" href="$sbRoot/home/displayShow?show=${cur_result['showid']}">
|
||||
${img_tag} ${plot_class}" alt="" src="$sbRoot/showPoster/?show=${cur_result['showid']}&which=poster_thumb" /></a>
|
||||
</div>
|
||||
<div class="text">
|
||||
<div class="airtime">
|
||||
<span class="time">${airtime}</span> <span class="network pull-right grey-text">$cur_result['network']</span>
|
||||
<div class="item#if $state_init[1] == $state then ' active' else ''#"> <!-- start $state -->
|
||||
<div class="daybydayWrapper">
|
||||
|
||||
#set $tbl_day = 0
|
||||
#for $day in $dates
|
||||
|
||||
#set $tbl_day += 1
|
||||
|
||||
#set $col_class = ''
|
||||
#if 1 == $tbl_day and $state_soon == $state
|
||||
#set $col_class = 'today'
|
||||
#end if
|
||||
#set $col_class = '%s %s' % ($col_class, ('even', 'odd')[1 == tbl_day % 2])
|
||||
|
||||
<div class="day-of-week $col_class">
|
||||
<div class="day-number">
|
||||
<div class="number">$sbdatetime.sbdatetime.sbfdate($day, ' %d').decode($sickbeard.SYS_ENCODING).replace(' 0', ' ')</div>
|
||||
<div class="day">
|
||||
<span class="visible-lg">$sbdatetime.sbdatetime.sbfdate($day, '%A').decode($sickbeard.SYS_ENCODING).capitalize()</span>
|
||||
<span class="hidden-lg">$sbdatetime.sbdatetime.sbfdate($day, '%a').decode($sickbeard.SYS_ENCODING).capitalize()</span>
|
||||
</div>
|
||||
<div class="month">
|
||||
<span class="visible-lg">$sbdatetime.sbdatetime.sbfdate($day, '%B').decode($sickbeard.SYS_ENCODING).capitalize()</span>
|
||||
<span class="hidden-lg">$sbdatetime.sbdatetime.sbfdate($day, '%b').decode($sickbeard.SYS_ENCODING).capitalize()</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="episode" title="$cur_result['name']">
|
||||
<span class="season"><%= '%i' % int(cur_result['season']) %></span>x<span class="number"><%= '%02i' % int(cur_result['episode']) %></span>
|
||||
<span class="name">$cur_result['name']</span>
|
||||
#if int($cur_result['paused']):
|
||||
<span class="pause">[paused]</span>
|
||||
|
||||
<div id="$sbdatetime.sbdatetime.sbfdate($day, 'day%j')">
|
||||
|
||||
#set $day_has_show = False
|
||||
#for $cur_result in $shows:
|
||||
#if $day == $cur_result['localtime'].date():
|
||||
#set $day_has_show = True
|
||||
#set $airtime = $sbdatetime.sbdatetime.sbftime($cur_result['localtime'], markup=True).decode($sickbeard.SYS_ENCODING)
|
||||
#set $img_id = ''
|
||||
#set $plot_class = ''
|
||||
#set $title_text = ''
|
||||
#set $show_id = '%s_%sx%s' % (str($cur_result['showid']), str($cur_result['season']), str($cur_result['episode']))
|
||||
#set $img_id = ' id="plot-%s"' % $show_id
|
||||
#set $plot_class = ' plot-daybyday'
|
||||
|
||||
<div id="show-${show_id}" class="daybyday-show" data-name="$cur_result['data_show_name']" data-season="$cur_result['season']" data-episode="$cur_result['episode']" data-network="$cur_result['data_network']" data-time="$time.mktime($cur_result['localtime'].timetuple())" data-rawname="$cur_result['show_name']">
|
||||
<div class="poster">
|
||||
<a${title_text} href="$sbRoot/home/displayShow?show=${cur_result['showid']}">
|
||||
<img${img_id} class="img-responsive${plot_class}" alt="" src="$sbRoot/showPoster/?show=${cur_result['showid']}&which=poster_thumb" /></a>
|
||||
</div>
|
||||
<div class="state#if len($cur_result['state']) then ' %s" title="%s"' % ($cur_result['state'], $cur_result['state-title']) else '"' #></div>
|
||||
<div class="text">
|
||||
<div class="airtime">
|
||||
<span class="time">${airtime}</span> <span class="network pull-right grey-text">$cur_result['network']</span>
|
||||
</div>
|
||||
<div class="episode" title="$cur_result['name']">
|
||||
<span class="season"><%= '%i' % int(cur_result['season']) %></span>x<span class="number"><%= '%02i' % int(cur_result['episode']) %></span>
|
||||
<span class="name">$cur_result['name']</span>
|
||||
</div>
|
||||
</div>
|
||||
#if int($cur_result['paused']):
|
||||
<span class="over-layer0">[paused]</span>
|
||||
<span class="over-layer1">[paused]</span>
|
||||
#elif $state_current == $cur_result['state']
|
||||
#set $endtime = $sbdatetime.sbdatetime.sbftime($cur_result['localtime'] + datetime.timedelta(minutes = $cur_result['runtime']), markup=True).decode($sickbeard.SYS_ENCODING)
|
||||
<span class="over-layer0 on-air0">On Air until<br />$endtime</span>
|
||||
<span class="over-layer1 on-air1">On Air until<br />$endtime</span>
|
||||
#end if
|
||||
</div><!-- end show-$cur_result['showid'] //-->
|
||||
|
||||
#end if
|
||||
#end for
|
||||
#if not $day_has_show:
|
||||
<div class="daybyday-show">
|
||||
#set $theday = ('this ', 'to')[1 == $tbl_day and $state_soon == $state]
|
||||
<span class="episode-blank">No shows ${theday}day</span>
|
||||
</div>
|
||||
#end if
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- end show-$cur_result['showid'] //-->
|
||||
#end for
|
||||
</div> <!-- end daybydayWrapper //-->
|
||||
</div> <!-- end $state //-->
|
||||
#end while
|
||||
#end for
|
||||
|
||||
#end if
|
||||
|
||||
#end for
|
||||
#if not $day_has_show:
|
||||
<div class="daybyday-show">
|
||||
<span class="episode-blank">No shows for this day</span>
|
||||
</div>
|
||||
#end if
|
||||
</div>
|
||||
</div>
|
||||
#end for
|
||||
</div>
|
||||
</div> <!-- end carouselinner //-->
|
||||
</div> <!-- end Carousel //-->
|
||||
</div> <!-- end daybydayCarouselContainer //-->
|
||||
|
||||
<!-- end calender view //-->
|
||||
#end if
|
||||
|
@ -689,6 +793,20 @@
|
|||
<script type="text/javascript" charset="utf-8">
|
||||
<!--
|
||||
window.setInterval('location.reload(true)', 30*60000); // Refresh every xx minutes
|
||||
|
||||
\$('#Carousel').carousel({
|
||||
interval: 0
|
||||
});
|
||||
|
||||
\$(document).bind('keyup', function(e) {
|
||||
if(e.which == 39){
|
||||
\$('.carousel').carousel('next');
|
||||
}
|
||||
else if(e.which == 37){
|
||||
\$('.carousel').carousel('prev');
|
||||
}
|
||||
});
|
||||
|
||||
//-->
|
||||
</script>
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
<input class="btn btn-inline" type="button" id="searchName" value="Search" />
|
||||
</span>
|
||||
<br />
|
||||
<p style="margin:5px 0 15px"><b>*</b> SickGear supports english episodes. The language choice is used for fetching metadata and episode filenames</p>
|
||||
<p style="margin:5px 0 15px"><b>*</b> SickGear supports english episodes. The language choice is used for fetching show data and episode filenames</p>
|
||||
|
||||
<div id="searchResults" style="height: 100%"></div>
|
||||
#end if
|
||||
|
@ -111,6 +111,7 @@
|
|||
</div>
|
||||
|
||||
<script type="text/javascript" src="$sbRoot/js/rootDirs.js?$sbPID"></script>
|
||||
<script type="text/javascript" src="$sbRoot/js/blackwhite.js?$sbPID"></script>
|
||||
|
||||
</div></div>
|
||||
|
||||
|
|
|
@ -150,7 +150,7 @@
|
|||
<p style="line-height: 1.5; padding: 2px 5px 3px" title="<%= '%s added' % ('TVRage', 'theTVDB')['1' == cur_show['show_id'][:1]] %>">In library</p>
|
||||
#else
|
||||
#set $encoded_show_title = urllib.quote($cur_show['title'].encode("utf-8"))
|
||||
<a href="$sbRoot/home/addTraktShow?indexer_id=${cur_show['show_id']}&showName=${encoded_show_title}" class="btn btn-xs">Add Show</a>
|
||||
<a href="$sbRoot/home/addShows/addTraktShow?indexer_id=${cur_show['show_id']}&showName=${encoded_show_title}" class="btn btn-xs">Add Show</a>
|
||||
#end if
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -20,6 +20,33 @@
|
|||
<p class="grey-text">Tip: The following options are <span style="font-weight:800">edit</span>able later in the detail view of the show</p>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
#set $qualities = $Quality.splitQuality($sickbeard.QUALITY_DEFAULT)
|
||||
#set global $anyQualities = $qualities[0]
|
||||
#set global $bestQualities = $qualities[1]
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_qualityChooser.tmpl')
|
||||
</div>
|
||||
|
||||
<div class="field-pair alt">
|
||||
<label for="flatten_folders">
|
||||
<span class="component-title">Flat folder structure</span>
|
||||
<span class="component-desc">
|
||||
<input class="cb" type="checkbox" name="flatten_folders" id="flatten_folders" #if $sickbeard.FLATTEN_FOLDERS_DEFAULT then "checked=\"checked\"" else ""# />
|
||||
<p>do not create sub folders</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair alt">
|
||||
<label for="scene">
|
||||
<span class="component-title">Scene numbering</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="scene" id="scene" #if $sickbeard.SCENE_DEFAULT then "checked=\"checked\"" else ""# />
|
||||
<p>search for episodes that are numbered by scene groups instead of by the TV network</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
#if $sickbeard.USE_SUBTITLES:
|
||||
<div class="field-pair alt">
|
||||
<label for="subtitles">
|
||||
|
@ -32,42 +59,17 @@
|
|||
</div>
|
||||
#end if
|
||||
|
||||
<div class="field-pair alt">
|
||||
<label for="flatten_folders">
|
||||
<span class="component-title">Flatten folders</span>
|
||||
<span class="component-desc">
|
||||
<input class="cb" type="checkbox" name="flatten_folders" id="flatten_folders" #if $sickbeard.FLATTEN_FOLDERS_DEFAULT then "checked=\"checked\"" else ""# />
|
||||
<p>do not create sub folders</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair alt">
|
||||
<label for="anime">
|
||||
<span class="component-title">Anime</span>
|
||||
<span class="component-title">Show is anime</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="anime" id="anime" #if $sickbeard.ANIME_DEFAULT then "checked=\"checked\"" else ""# />
|
||||
<p>use anime processing for this show</p>
|
||||
<p>enable if this show is anime and episode releases are named ... <em class="grey-text">Show.265</em> instead of <em class="grey-text">Show.S02E03</em></p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="field-pair alt">
|
||||
<label for="scene">
|
||||
<span class="component-title">Scene numbering</span>
|
||||
<span class="component-desc">
|
||||
<input type="checkbox" name="scene" id="scene" #if $sickbeard.SCENE_DEFAULT then "checked=\"checked\"" else ""# />
|
||||
<p>enable if episodes are numbered by scene releases and not by the TV network</p>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
#set $qualities = $Quality.splitQuality($sickbeard.QUALITY_DEFAULT)
|
||||
#set global $anyQualities = $qualities[0]
|
||||
#set global $bestQualities = $qualities[1]
|
||||
#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_qualityChooser.tmpl")
|
||||
|
||||
<div class="field-pair alt" style="margin-top:30px">
|
||||
<div class="field-pair alt" style="margin-top:20px">
|
||||
<label for="saveDefaultsButton">
|
||||
<span class="component-title">Save options as defaults</span>
|
||||
<span class="component-desc">
|
||||
|
@ -76,3 +78,6 @@
|
|||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
#import sickbeard.blackandwhitelist
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_blackwhitelist.tmpl')
|
|
@ -1,53 +1,68 @@
|
|||
<b>Fansub Groups:</b>
|
||||
<div >
|
||||
<p>Select your preferred fansub groups from the <b>Available Groups</b> and add them to the <b>Whitelist</b>. Add groups to the <b>Blacklist</b> to ignore them.</p>
|
||||
<p>The <b>Whitelist</b> is checked <i>before</i> the <b>Blacklist</b>.</p>
|
||||
<p>Groups are shown as <b>Name</b> | <b>Rating</b> | <b>Number of subbed episodes</b>.</p>
|
||||
<p>You may also add any fansub group not listed to either list manually.</p>
|
||||
</div>
|
||||
<div class="bwlWrapper" id="Anime">
|
||||
<div class="blackwhitelist all">
|
||||
<div class="blackwhitelist anidb">
|
||||
<div class="blackwhitelist white">
|
||||
<span><h4>Whitelist</h4></span>
|
||||
<select id="white" multiple="multiple" size="12">
|
||||
#for $keyword in $whitelist:
|
||||
<option value="$keyword">$keyword</option>
|
||||
#end for
|
||||
</select>
|
||||
<br/>
|
||||
<input class="btn" id="removeW" value="Remove" type="button"/>
|
||||
</div>
|
||||
<div class="blackwhitelist pool">
|
||||
<span><h4>Available Groups</h4></span>
|
||||
<select id="pool" multiple="multiple" size="12">
|
||||
#for $group in $groups
|
||||
#if $group not in $whitelist and $group['name'] not in $blacklist:
|
||||
<option value="$group['name']">$group['name'] | $group['rating'] | $group['range']</option>
|
||||
#end if
|
||||
#end for
|
||||
</select>
|
||||
<br/>
|
||||
<input class="btn" id="addW" value="Add to Whitelist" type="button"/>
|
||||
<input class="btn" id="addB" value="Add to Blacklist" type="button"/>
|
||||
</div>
|
||||
<div class="blackwhitelist black">
|
||||
<span><h4>Blacklist</h4></span>
|
||||
<select id="black" multiple="multiple" size="12">
|
||||
#for $keyword in $blacklist:
|
||||
<option value="$keyword">$keyword</option>
|
||||
#end for
|
||||
</select>
|
||||
<br/>
|
||||
<input class="btn" id="removeB" value="Remove" type="button"/>
|
||||
<div class="field-pair alt" id="blackwhitelist">
|
||||
<input type="hidden" name="whitelist" id="whitelist">
|
||||
<input type="hidden" name="blacklist" id="blacklist">
|
||||
|
||||
<span class="component-title">Fansub groups</span>
|
||||
<span class="component-desc">
|
||||
|
||||
<div class="bwlWrapper">
|
||||
<div class="blackwhitelist all">
|
||||
|
||||
<div class="blackwhitelist anidb">
|
||||
<div class="inuse" style="padding:0">
|
||||
<div class="blackwhitelist white" style="margin-bottom:10px">
|
||||
<h4 style="margin:0 41px 6px 0">Whitelist<br /><span class="grey-text">Only snatch releases by group(s)</span></h4>
|
||||
<div style="width:243px; height:110px">
|
||||
<select style="width:202px" class="pull-left" id="white" multiple="multiple" size="12">
|
||||
#for $keyword in sorted($whitelist):
|
||||
<option value="$keyword">$keyword</option>
|
||||
#end for
|
||||
</select>
|
||||
<div style="position:relative; width:36px; height:64px; margin: -32px 0 0; top: 50%;" class="pull-right">
|
||||
<input style="margin:0 0 10px !important" class="btn" id="add-white" value="<<" type="button">
|
||||
<input style="margin:0 !important" class="btn clear:right" id="remove-white" value=">>" type="button">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="blackwhitelist black" style="position:relative; bottom: -1px">
|
||||
<h4 style="margin:0 41px 6px 0">Blacklist<br /><span class="grey-text">Ignore releases by group(s)</span></h4>
|
||||
<div style="width:243px; height:110px">
|
||||
<select style="width:202px" class="pull-left" id="black" multiple="multiple" size="12">
|
||||
#for $keyword in sorted($blacklist):
|
||||
<option value="$keyword">$keyword</option>
|
||||
#end for
|
||||
</select>
|
||||
<div style="position:relative; width:36px; height:64px; margin: -32px 0 0; top: 50%;" class="pull-right">
|
||||
<input style="margin:0 0 10px !important" class="btn" id="add-black" value="<<" type="button">
|
||||
<input style="margin:0 !important" class="btn clear:right" id="remove-black" value=">>" type="button">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="blackwhitelist pool">
|
||||
<h4 style="margin:0 0 6px 0">Available groups<br /><span class="grey-text">Name (Rating) Number of subbed episodes</span></h4>
|
||||
<select id="pool" multiple="multiple" size="12">
|
||||
#for $group in sorted($groups)
|
||||
#if $group not in $whitelist and $group['name'] not in $blacklist:
|
||||
<option value="$group['name']">$group['name'] ($group['rating']) $group['range']</option>
|
||||
#end if
|
||||
#end for
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="clear:both"> </div>
|
||||
<div class="blackwhitelist manual">
|
||||
<div class="pull-left">
|
||||
<input type="text" id="addToPoolText" class="form-control form-control-inline input-sm input200" style="width:202px">
|
||||
<input class="btn btn-inline" type="button" value="Add to Whitelist" id="new-white">
|
||||
<input style="margin-right:0" class="btn btn-inline" type="button" value="Add to Blacklist" id="new-black">
|
||||
</div>
|
||||
<span class="pull-left">add a custom item to either the whitelist or blacklist</span>
|
||||
<div style="clear:both"> </div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br style="clear:both" />
|
||||
<div class="blackwhitelist manual">
|
||||
<input type="text" id="addToPoolText" class="form-control form-control-inline input-sm input250" />
|
||||
<input class="btn btn-inline" type="button" value="Add to Whitelist" id="addToWhite">
|
||||
<input class="btn btn-inline" type="button" value="Add to Blacklist" id="addToBlack">
|
||||
</div>
|
||||
</div>
|
||||
<br style="clear:both" />
|
||||
</div>
|
||||
</span>
|
||||
</div><!-- /blackwhitelist -->
|
|
@ -10,24 +10,23 @@
|
|||
<div class="footer clearfix">
|
||||
#set $my_db = $db.DBConnection()
|
||||
#set $today = str($datetime.date.today().toordinal())
|
||||
#set status_quality = '(' + ','.join([str(quality) for quality in $Quality.SNATCHED + $Quality.SNATCHED_PROPER]) + ')'
|
||||
#set status_download = '(' + ','.join([str(quality) for quality in $Quality.DOWNLOADED + [$ARCHIVED]]) + ')'
|
||||
|
||||
#set $sql_statement = 'SELECT '
|
||||
|
||||
#set $sql_statement += '(SELECT COUNT(*) FROM tv_episodes WHERE season > 0 AND episode > 0 AND airdate > 1 AND status IN ' + $status_quality + ') AS ep_snatched, '
|
||||
#set $sql_statement += '(SELECT COUNT(*) FROM tv_episodes WHERE season > 0 AND episode > 0 AND airdate > 1 AND status IN ' + $status_download + ') AS ep_downloaded, '
|
||||
|
||||
#set $sql_statement += '(SELECT COUNT(*) FROM tv_episodes WHERE season > 0 AND episode > 0 AND airdate > 1 '
|
||||
#set $sql_statement += ' AND ((airdate <= ' + $today + ' AND (status = ' + str($SKIPPED) + ' OR status = ' + str($WANTED) + ')) '
|
||||
#set $sql_statement += ' OR (status IN ' + status_quality + ') OR (status IN ' + status_download + '))) AS ep_total '
|
||||
|
||||
#set $sql_statement += ' FROM tv_episodes tv_eps LIMIT 1'
|
||||
#set status_quality = '(%s)' % ','.join([str(quality) for quality in $Quality.SNATCHED + $Quality.SNATCHED_PROPER])
|
||||
#set status_download = '(%s)' % ','.join([str(quality) for quality in $Quality.DOWNLOADED + [$ARCHIVED]])
|
||||
#set $sql_statement = 'SELECT '\
|
||||
+ '(SELECT COUNT(*) FROM tv_episodes WHERE season > 0 AND episode > 0 AND airdate > 1 AND status IN %s) AS ep_snatched, '\
|
||||
% $status_quality\
|
||||
+ '(SELECT COUNT(*) FROM tv_episodes WHERE season > 0 AND episode > 0 AND airdate > 1 AND status IN %s) AS ep_downloaded, '\
|
||||
% $status_download\
|
||||
+ '(SELECT COUNT(*) FROM tv_episodes WHERE season > 0 AND episode > 0 AND airdate > 1 '\
|
||||
+ ' AND ((airdate <= %s AND (status = %s OR status = %s)) '\
|
||||
% ($today, str($SKIPPED), str($WANTED))\
|
||||
+ ' OR (status IN %s) OR (status IN %s))) AS ep_total FROM tv_episodes tv_eps LIMIT 1'\
|
||||
% ($status_quality, $status_download)
|
||||
|
||||
#set $sql_result = $my_db.select($sql_statement)
|
||||
|
||||
#set $shows_total = len($sickbeard.showList)
|
||||
#set $shows_active = len([show for show in $sickbeard.showList if show.paused == 0 and show.status != "Ended"])
|
||||
#set $shows_active = len([show for show in $sickbeard.showList if 0 == show.paused and 'Ended' != show.status])
|
||||
|
||||
#if $sql_result:
|
||||
#set $ep_snatched = $sql_result[0]['ep_snatched']
|
||||
|
@ -38,6 +37,8 @@
|
|||
#set $ep_downloaded = 0
|
||||
#set $ep_total = 0
|
||||
#end if
|
||||
#set $ep_percentage = '' if $ep_total == 0 else '(<span class="footerhighlight">{:.1%}</span>)'.format(float($ep_downloaded)/float($ep_total))
|
||||
|
||||
#try
|
||||
#set $localRoot = $sbRoot
|
||||
#except NotFound
|
||||
|
@ -50,20 +51,19 @@
|
|||
#end try
|
||||
|
||||
<span class="footerhighlight">$shows_total</span> shows (<span class="footerhighlight">$shows_active</span> active)
|
||||
| <span class="footerhighlight"><%= ep_downloaded %></span>
|
||||
<%= (
|
||||
'',\
|
||||
' (<span class="footerhighlight">+%s</span> snatched)' % \
|
||||
(
|
||||
str(ep_snatched),
|
||||
'<a href="%s/manage/episodeStatuses?whichStatus=2" title="View overview of snatched episodes">%s</a>' % \
|
||||
(localRoot, str(ep_snatched))
|
||||
)['Episode Overview' != localheader]
|
||||
)[0 < ep_snatched]
|
||||
%>
|
||||
/ <span class="footerhighlight">$ep_total</span> episodes downloaded
|
||||
| <span class="footerhighlight">$ep_downloaded</span><%=
|
||||
(
|
||||
'',
|
||||
' (<span class="footerhighlight">+%s</span> snatched)'\
|
||||
% (
|
||||
str(ep_snatched),
|
||||
'<a href="%s/manage/episodeStatuses?whichStatus=2" title="View overview of snatched episodes">%s</a>'\
|
||||
% (localRoot, str(ep_snatched))
|
||||
)['Episode Overview' != localheader]
|
||||
)[0 < ep_snatched]
|
||||
%> / <span class="footerhighlight">$ep_total</span> episodes downloaded $ep_percentage
|
||||
| recent search: <span class="footerhighlight"><%= str(sickbeard.recentSearchScheduler.timeLeft()).split('.')[0] %></span>
|
||||
| backlog search: <span class="footerhighlight">$sbdatetime.sbdatetime.sbfdate($sickbeard.backlogSearchScheduler.nextRun())</span>
|
||||
| backlog search: <span class="footerhighlight"><%= str(sickbeard.backlogSearchScheduler.timeLeft()).split('.')[0] %></span>
|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
<span class="component-desc">
|
||||
<div style="float:left; padding-right: 40px">
|
||||
<h4>Initial</h4>
|
||||
<h4 class="jumbo">Initial</h4>
|
||||
#set $anyQualityList = filter(lambda x: x > $Quality.NONE, $Quality.qualityStrings)
|
||||
<select id="anyQualities" name="anyQualities" multiple="multiple" size="$len($anyQualityList)" class="form-control form-control-inline input-sm">
|
||||
#for $curQuality in sorted($anyQualityList):
|
||||
|
@ -36,7 +36,7 @@
|
|||
</div>
|
||||
|
||||
<div style="float:left">
|
||||
<h4>Archive</h4>
|
||||
<h4 class="jumbo">Archive</h4>
|
||||
#set $bestQualityList = filter(lambda x: x > $Quality.SDTV and x < $Quality.UNKNOWN, $Quality.qualityStrings)
|
||||
<select id="bestQualities" name="bestQualities" multiple="multiple" size="$len($bestQualityList)" class="form-control form-control-inline input-sm">
|
||||
#for $curQuality in sorted($bestQualityList):
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
\$("#SubMenu a[href$='/errorlogs/clearerrors/']").addClass('btn').html('<span class="ui-icon ui-icon-trash pull-left"></span> Clear Errors');
|
||||
\$("#SubMenu a:contains('Re-scan')").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> Re-scan');
|
||||
\$("#SubMenu a:contains('Backlog Overview')").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> Backlog Overview');
|
||||
\$("#SubMenu a[href$='/home/updatePLEX/']").addClass('btn').html('<span class="ui-icon ui-icon-refresh pull-left"></span> Update PLEX');
|
||||
\$("#SubMenu a[href$='/home/updatePLEX/']").addClass('btn').html('<span class="submenu-icon-plex pull-left"></span> Update PLEX');
|
||||
\$("#SubMenu a:contains('Force')").addClass('btn').html('<span class="ui-icon ui-icon-transfer-e-w pull-left"></span> Force Full Update');
|
||||
\$("#SubMenu a:contains('Rename')").addClass('btn').html('<span class="ui-icon ui-icon-tag pull-left"></span> Preview Rename');
|
||||
\$("#SubMenu a[href$='/config/subtitles/']").addClass('btn').html('<span class="ui-icon ui-icon-comment pull-left"></span> Search Subtitles');
|
||||
|
@ -142,7 +142,7 @@
|
|||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li id="NAVhome" class="dropdown">
|
||||
<a href="$sbRoot/home/" class="dropdown-toggle" data-toggle="dropdown" tabindex="$tab#set $tab += 1#">Shows <b class="caret"></b></a>
|
||||
<a href="$sbRoot/home/" class="dropdown-toggle" data-toggle="dropdown" data-delay="0" tabindex="$tab#set $tab += 1#">Shows <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="$sbRoot/home/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-home"></i> Show List</a></li>
|
||||
<li><a href="$sbRoot/home/addShows/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-addshow"></i> Add Shows</a></li>
|
||||
|
@ -159,14 +159,14 @@
|
|||
</li>
|
||||
|
||||
<li id="NAVmanage" class="dropdown">
|
||||
<a href="$sbRoot/manage/" class="dropdown-toggle" data-toggle="dropdown" tabindex="$tab#set $tab += 1#">Manage <b class="caret"></b></a>
|
||||
<a href="$sbRoot/manage/" class="dropdown-toggle" data-toggle="dropdown" data-delay="0" tabindex="$tab#set $tab += 1#">Manage <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="$sbRoot/manage/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-manage"></i> Mass Update</a></li>
|
||||
<li><a href="$sbRoot/manage/backlogOverview/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-backlog-view"></i> Backlog Overview</a></li>
|
||||
<li><a href="$sbRoot/manage/manageSearches/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-manage-searches"></i> Manage Searches</a></li>
|
||||
<li><a href="$sbRoot/manage/episodeStatuses/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-backlog"></i> Episode Status Management</a></li>
|
||||
#if $sickbeard.USE_PLEX and $sickbeard.PLEX_SERVER_HOST != "":
|
||||
<li><a href="$sbRoot/home/updatePLEX/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-backlog-view"></i> Update PLEX</a></li>
|
||||
<li><a href="$sbRoot/home/updatePLEX/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-plex"></i> Update PLEX</a></li>
|
||||
#end if
|
||||
#if $sickbeard.USE_XBMC and $sickbeard.XBMC_HOST != "":
|
||||
<li><a href="$sbRoot/home/updateXBMC/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-xbmc"></i> Update XBMC</a></li>
|
||||
|
@ -186,7 +186,7 @@
|
|||
</li>
|
||||
|
||||
<li id="NAVerrorlogs" class="dropdown">
|
||||
<a href="$sbRoot/errorlogs/" class="dropdown-toggle" data-toggle="dropdown" tabindex="$tab#set $tab += 1#">$logPageTitle <b class="caret"></b></a>
|
||||
<a href="$sbRoot/errorlogs/" class="dropdown-toggle" data-toggle="dropdown" data-delay="0" tabindex="$tab#set $tab += 1#">$logPageTitle <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="$sbRoot/errorlogs/"><i class="menu-icon-viewlog-errors" tabindex="$tab#set $tab += 1#"></i> View Log (Errors)</a></li>
|
||||
<li><a href="$sbRoot/errorlogs/viewlog/"><i class="menu-icon-viewlog" tabindex="$tab#set $tab += 1#"></i> View Log</a></li>
|
||||
|
@ -194,7 +194,7 @@
|
|||
</li>
|
||||
|
||||
<li id="NAVconfig" class="dropdown">
|
||||
<a href="$sbRoot/config/" class="dropdown-toggle" data-toggle="dropdown" tabindex="$tab#set $tab += 1#"><img src="$sbRoot/images/menu/system18.png" class="navbaricon hidden-xs" /><b class="caret hidden-xs"></b><span class="visible-xs">Config <b class="caret"></b></span></a>
|
||||
<a href="$sbRoot/config/" class="dropdown-toggle" data-toggle="dropdown" data-delay="0" tabindex="$tab#set $tab += 1#"><img src="$sbRoot/images/menu/system18.png" class="navbaricon hidden-xs" /><b class="caret hidden-xs"></b><span class="visible-xs">Config <b class="caret"></b></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="$sbRoot/config/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-help"></i> Help & Info</a></li>
|
||||
<li><a href="$sbRoot/config/general/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-config"></i> General</a></li>
|
||||
|
@ -208,9 +208,12 @@
|
|||
</li>
|
||||
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" tabindex="$tab#set $tab += 1#"><img src="$sbRoot/images/menu/system18-2.png" class="navbaricon hidden-xs" /><b class="caret hidden-xs"></b><span class="visible-xs">System <b class="caret"></b></span></a>
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" data-delay="0" tabindex="$tab#set $tab += 1#"><img src="$sbRoot/images/menu/system18-2.png" class="navbaricon hidden-xs" /><b class="caret hidden-xs"></b><span class="visible-xs">System <b class="caret"></b></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="$sbRoot/manage/manageSearches/forceVersionCheck" tabindex="$tab#set $tab += 1#"><i class="menu-icon-update"></i> Force Version Check</a></li>
|
||||
#if $sickbeard.WEB_USERNAME or $sickbeard.WEB_PASSWORD:
|
||||
<li><a href="$sbRoot/logout" class="confirm logout" tabindex="$tab#set $tab += 1#"><i class="menu-icon-logout"></i> Logout</a></li>
|
||||
#end if
|
||||
<li><a href="$sbRoot/home/restart/?pid=$sbPID" class="confirm restart" tabindex="$tab#set $tab += 1#"><i class="menu-icon-restart"></i> Restart</a></li>
|
||||
<li><a href="$sbRoot/home/shutdown/?pid=$sbPID" class="confirm shutdown" tabindex="$tab#set $tab += 1#"><i class="menu-icon-shutdown"></i> Shutdown</a></li>
|
||||
</ul>
|
||||
|
|
52
gui/slick/interfaces/default/login.tmpl
Normal file
|
@ -57,7 +57,7 @@
|
|||
<span class="listing-key qual">Low Quality: <b>$totalQual</b></span>
|
||||
</div><br/>
|
||||
|
||||
<div class="float-left">
|
||||
<div class="pull-left">
|
||||
Jump to Show
|
||||
<select id="pickShow" class="form-control form-control-inline input-sm">
|
||||
#for $curShow in sorted($sickbeard.showList, key = operator.attrgetter('name')):
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
#import sickbeard
|
||||
#from sickbeard import common
|
||||
#from sickbeard import exceptions
|
||||
#set global $title="Test Rename"
|
||||
#set global $title = 'Test Rename ' + $show.name
|
||||
#set global $header = '<a href="' + $sbRoot + '/home/displayShow?show=%d">%s</a>' % ($show.indexerid, $show.name)
|
||||
#set global $sbPath=".."
|
||||
#set global $sbPath = '..'
|
||||
|
||||
#set global $topmenu="home"#
|
||||
#set global $topmenu = 'home'
|
||||
#import os.path
|
||||
#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
|
||||
|
||||
#if $varExists('header')
|
||||
<h1 class="header">$header</h1>
|
||||
|
@ -32,6 +32,14 @@
|
|||
|
||||
#set $curSeason = -1
|
||||
#set $odd = False
|
||||
|
||||
<div class="clearfix padbottom">
|
||||
<button class="btn seriesCheck">Select All Episodes</button>
|
||||
<button class="btn clearAll">Clear All</button>
|
||||
</div>
|
||||
|
||||
<input type="submit" value="Rename Selected" class="btn btn-success"> <a href="/home/displayShow?show=$show.indexerid" class="btn btn-danger">Cancel Rename</a>
|
||||
|
||||
<table id="testRenameTable" class="sickbeardTable" cellspacing="1" border="0" cellpadding="0">
|
||||
|
||||
#for $cur_ep_obj in $ep_obj_list:
|
||||
|
@ -43,7 +51,6 @@
|
|||
<thead>
|
||||
<tr class="seasonheader" id="season-$cur_ep_obj.season">
|
||||
<td colspan="4">
|
||||
<br/>
|
||||
<h2>#if int($cur_ep_obj.season) == 0 then "Specials" else "Season "+str($cur_ep_obj.season)#</h2>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -84,4 +91,4 @@
|
|||
#end for
|
||||
</table><br />
|
||||
<input type="submit" value="Rename Selected" class="btn btn-success"> <a href="/home/displayShow?show=$show.indexerid" class="btn btn-danger">Cancel Rename</a>
|
||||
#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_bottom.tmpl")
|
||||
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
var search_status_url = sbRoot + '/getManualSearchStatus';
|
||||
var search_status_url = sbRoot + '/home/getManualSearchStatus';
|
||||
PNotify.prototype.options.maxonscreen = 5;
|
||||
|
||||
$.fn.manualSearches = [];
|
||||
|
|
30
gui/slick/js/blackwhite.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
function generate_bwlist() {
|
||||
$.each(['white', 'black'], function(i, list) {
|
||||
var group_list = [];
|
||||
|
||||
$('#' + list + ' option').each(function(i, option) {
|
||||
group_list.push($(option).val());
|
||||
});
|
||||
|
||||
$('#' + list + 'list').val(group_list.join(','));
|
||||
});
|
||||
}
|
||||
|
||||
$('#add-white, #add-black').click(function() {
|
||||
!$('#pool option:selected').remove().appendTo('#' + $(this).attr('id').replace(/add[-]/i, ''));
|
||||
});
|
||||
|
||||
$('#remove-white, #remove-black').click(function() {
|
||||
!$('#' + $(this).attr('id').replace(/remove[-]/i, '') + ' option:selected').remove().appendTo('#pool');
|
||||
});
|
||||
|
||||
$('#new-white, #new-black').click(function() {
|
||||
var group = $('#addToPoolText').attr('value');
|
||||
if ('' != group) {
|
||||
var option = $('<option>');
|
||||
option.attr('value', group);
|
||||
option.html(group);
|
||||
option.appendTo('#' + $(this).attr('id').replace(/new[-]/i, ''));
|
||||
$('#addToPoolText').attr('value', '');
|
||||
}
|
||||
});
|
|
@ -92,14 +92,15 @@ $(document).ready(function(){
|
|||
$(this).after('<span><img src="' + sbRoot + '/images/loading16' + themeSpinner + '.gif"> Saving...</span>');
|
||||
$(this).hide();
|
||||
});
|
||||
$('.show_update_hour_value').text($('#show_update_hour').val())
|
||||
},
|
||||
success: function(){
|
||||
setTimeout('config_success()', 2000)
|
||||
success: function(response){
|
||||
setTimeout(function(){config_success(response)}, 2000);
|
||||
}
|
||||
});
|
||||
|
||||
$('#api_key').click(function(){ $('#api_key').select() });
|
||||
$("#generate_new_apikey").click(function(){
|
||||
$('#generate_new_apikey').click(function(){
|
||||
$.get(sbRoot + '/config/general/generateKey',
|
||||
function(data){
|
||||
if (data.error != undefined) {
|
||||
|
@ -120,11 +121,14 @@ $(document).ready(function(){
|
|||
|
||||
});
|
||||
|
||||
function config_success(){
|
||||
function config_success(response){
|
||||
if (response == 'reload'){
|
||||
window.location.reload(true);
|
||||
}
|
||||
$('.config_submitter').each(function(){
|
||||
$(this).removeAttr('disabled');
|
||||
$(this).next().remove();
|
||||
$(this).show();
|
||||
});
|
||||
$('#email_show').trigger('notify');
|
||||
}
|
||||
}
|
|
@ -1,119 +1,122 @@
|
|||
$(document).ready(function(){
|
||||
var loading = '<img src="' + sbRoot + '/images/loading16' + themeSpinner + '.gif" height="16" width="16" />';
|
||||
var loading = '<img src="' + sbRoot + '/images/loading16' + themeSpinner + '.gif" height="16" width="16" />';
|
||||
|
||||
$('#testGrowl').click(function () {
|
||||
var growl_host = $.trim($('#growl_host').val());
|
||||
var growl_password = $.trim($('#growl_password').val());
|
||||
if (!growl_host) {
|
||||
$('#testGrowl-result').html('Please fill out the necessary fields above.');
|
||||
$('#testGrowl').click(function () {
|
||||
var growl_host = $.trim($('#growl_host').val());
|
||||
var growl_password = $.trim($('#growl_password').val());
|
||||
if (!growl_host) {
|
||||
$('#testGrowl-result').html('Please fill out the necessary fields above.');
|
||||
$('#growl_host').addClass('warning');
|
||||
return;
|
||||
}
|
||||
$('#growl_host').removeClass('warning');
|
||||
return;
|
||||
}
|
||||
$('#growl_host').removeClass('warning');
|
||||
$(this).prop('disabled', true);
|
||||
$('#testGrowl-result').html(loading);
|
||||
$.get(sbRoot + '/home/testGrowl', {'host': growl_host, 'password': growl_password})
|
||||
.done(function (data) {
|
||||
$('#testGrowl-result').html(data);
|
||||
$('#testGrowl').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
$('#testGrowl-result').html(loading);
|
||||
$.get(sbRoot + '/home/testGrowl', {'host': growl_host, 'password': growl_password})
|
||||
.done(function (data) {
|
||||
$('#testGrowl-result').html(data);
|
||||
$('#testGrowl').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
$('#testProwl').click(function () {
|
||||
var prowl_api = $.trim($('#prowl_api').val());
|
||||
var prowl_priority = $('#prowl_priority').val();
|
||||
if (!prowl_api) {
|
||||
$('#testProwl-result').html('Please fill out the necessary fields above.');
|
||||
$('#testProwl').click(function () {
|
||||
var prowl_api = $.trim($('#prowl_api').val());
|
||||
var prowl_priority = $('#prowl_priority').val();
|
||||
if (!prowl_api) {
|
||||
$('#testProwl-result').html('Please fill out the necessary fields above.');
|
||||
$('#prowl_api').addClass('warning');
|
||||
return;
|
||||
}
|
||||
$('#prowl_api').removeClass('warning');
|
||||
return;
|
||||
}
|
||||
$('#prowl_api').removeClass('warning');
|
||||
$(this).prop('disabled', true);
|
||||
$('#testProwl-result').html(loading);
|
||||
$.get(sbRoot + '/home/testProwl', {'prowl_api': prowl_api, 'prowl_priority': prowl_priority})
|
||||
.done(function (data) {
|
||||
$('#testProwl-result').html(data);
|
||||
$('#testProwl').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
$('#testXBMC').click(function () {
|
||||
var xbmc_host = $.trim($('#xbmc_host').val());
|
||||
var xbmc_username = $.trim($('#xbmc_username').val());
|
||||
var xbmc_password = $.trim($('#xbmc_password').val());
|
||||
if (!xbmc_host) {
|
||||
$('#testXBMC-result').html('Please fill out the necessary fields above.');
|
||||
$('#testProwl-result').html(loading);
|
||||
$.get(sbRoot + '/home/testProwl', {'prowl_api': prowl_api, 'prowl_priority': prowl_priority})
|
||||
.done(function (data) {
|
||||
$('#testProwl-result').html(data);
|
||||
$('#testProwl').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
$('#testXBMC').click(function () {
|
||||
var xbmc_host = $.trim($('#xbmc_host').val());
|
||||
var xbmc_username = $.trim($('#xbmc_username').val());
|
||||
var xbmc_password = $.trim($('#xbmc_password').val());
|
||||
if (!xbmc_host) {
|
||||
$('#testXBMC-result').html('Please fill out the necessary fields above.');
|
||||
$('#xbmc_host').addClass('warning');
|
||||
return;
|
||||
}
|
||||
$('#xbmc_host').removeClass('warning');
|
||||
return;
|
||||
}
|
||||
$('#xbmc_host').removeClass('warning');
|
||||
$(this).prop('disabled', true);
|
||||
$('#testXBMC-result').html(loading);
|
||||
$.get(sbRoot + '/home/testXBMC', {'host': xbmc_host, 'username': xbmc_username, 'password': xbmc_password})
|
||||
.done(function (data) {
|
||||
$('#testXBMC-result').html(data);
|
||||
$('#testXBMC').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
$('#testXBMC-result').html(loading);
|
||||
$.get(sbRoot + '/home/testXBMC', {'host': xbmc_host, 'username': xbmc_username, 'password': xbmc_password})
|
||||
.done(function (data) {
|
||||
$('#testXBMC-result').html(data);
|
||||
$('#testXBMC').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
$('#testPLEX').click(function () {
|
||||
var plex_host = $.trim($('#plex_host').val());
|
||||
var plex_username = $.trim($('#plex_username').val());
|
||||
var plex_password = $.trim($('#plex_password').val());
|
||||
if (!plex_host) {
|
||||
$('#testPLEX-result').html('Please fill out the necessary fields above.');
|
||||
$('#testPMC').click(function () {
|
||||
var plex_host = $.trim($('#plex_host').val());
|
||||
var plex_username = $.trim($('#plex_username').val());
|
||||
var plex_password = $.trim($('#plex_password').val());
|
||||
if (!plex_host) {
|
||||
$('#testPMC-result').html('Please fill out the necessary fields above.');
|
||||
$('#plex_host').addClass('warning');
|
||||
return;
|
||||
}
|
||||
$('#plex_host').removeClass('warning');
|
||||
return;
|
||||
}
|
||||
$('#plex_host').removeClass('warning');
|
||||
$(this).prop('disabled', true);
|
||||
$('#testPLEX-result').html(loading);
|
||||
$.get(sbRoot + '/home/testPLEX', {'host': plex_host, 'username': plex_username, 'password': plex_password})
|
||||
.done(function (data) {
|
||||
$('#testPLEX-result').html(data);
|
||||
$('#testPLEX').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
$('#testPMC-result').html(loading);
|
||||
$.get(sbRoot + '/home/testPMC', {'host': plex_host, 'username': plex_username, 'password': plex_password})
|
||||
.done(function (data) {
|
||||
$('#testPMC-result').html(data);
|
||||
$('#testPMC').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
$('#testBoxcar').click(function() {
|
||||
var boxcar_username = $.trim($('#boxcar_username').val());
|
||||
if (!boxcar_username) {
|
||||
$('#testBoxcar-result').html('Please fill out the necessary fields above.');
|
||||
$('#boxcar_username').addClass('warning');
|
||||
return;
|
||||
}
|
||||
$('#boxcar_username').removeClass('warning');
|
||||
$('#testPMS').click(function () {
|
||||
var plex_server_host = $.trim($('#plex_server_host').val());
|
||||
var plex_username = $.trim($('#plex_username').val());
|
||||
var plex_password = $.trim($('#plex_password').val());
|
||||
if (!plex_server_host) {
|
||||
$('#testPMS-result').html('Please fill out the necessary fields above.');
|
||||
$('#plex_server_host').addClass('warning');
|
||||
return;
|
||||
}
|
||||
$('#plex_server_host').removeClass('warning');
|
||||
$(this).prop('disabled', true);
|
||||
$('#testBoxcar-result').html(loading);
|
||||
$.get(sbRoot + '/home/testBoxcar', {'username': boxcar_username})
|
||||
.done(function (data) {
|
||||
$('#testBoxcar-result').html(data);
|
||||
$('#testBoxcar').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
$('#testBoxcar2').click(function () {
|
||||
var boxcar2_accesstoken = $.trim($('#boxcar2_accesstoken').val());
|
||||
if (!boxcar2_accesstoken) {
|
||||
$('#testBoxcar2-result').html('Please fill out the necessary fields above.');
|
||||
$('#testPMS-result').html(loading);
|
||||
$.get(sbRoot + '/home/testPMS', {'host': plex_server_host, 'username': plex_username, 'password': plex_password})
|
||||
.done(function (data) {
|
||||
$('#testPMS-result').html(data);
|
||||
$('#testPMS').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
$('#testBoxcar2').click(function () {
|
||||
var boxcar2_accesstoken = $.trim($('#boxcar2_accesstoken').val());
|
||||
var boxcar2_sound = $('#boxcar2_sound').val() || 'default';
|
||||
if (!boxcar2_accesstoken) {
|
||||
$('#testBoxcar2-result').html('Please fill out the necessary fields above.');
|
||||
$('#boxcar2_accesstoken').addClass('warning');
|
||||
return;
|
||||
}
|
||||
$('#boxcar2_accesstoken').removeClass('warning');
|
||||
return;
|
||||
}
|
||||
$('#boxcar2_accesstoken').removeClass('warning');
|
||||
$(this).prop('disabled', true);
|
||||
$('#testBoxcar2-result').html(loading);
|
||||
$.get(sbRoot + '/home/testBoxcar2', {'accessToken': boxcar2_accesstoken})
|
||||
.done(function (data) {
|
||||
$('#testBoxcar2-result').html(data);
|
||||
$('#testBoxcar2').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
$('#testBoxcar2-result').html(loading);
|
||||
$.get(sbRoot + '/home/testBoxcar2', {'accesstoken': boxcar2_accesstoken, 'sound': boxcar2_sound})
|
||||
.done(function (data) {
|
||||
$('#testBoxcar2-result').html(data);
|
||||
$('#testBoxcar2').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
$('#testPushover').click(function () {
|
||||
var pushover_userkey = $('#pushover_userkey').val();
|
||||
var pushover_apikey = $('#pushover_apikey').val();
|
||||
if (!pushover_userkey || !pushover_apikey) {
|
||||
$('#testPushover-result').html('Please fill out the necessary fields above.');
|
||||
$('#testPushover').click(function () {
|
||||
var pushover_userkey = $('#pushover_userkey').val();
|
||||
var pushover_apikey = $('#pushover_apikey').val();
|
||||
if (!pushover_userkey || !pushover_apikey) {
|
||||
$('#testPushover-result').html('Please fill out the necessary fields above.');
|
||||
if (!pushover_userkey) {
|
||||
$('#pushover_userkey').addClass('warning');
|
||||
} else {
|
||||
|
@ -124,108 +127,108 @@ $(document).ready(function(){
|
|||
} else {
|
||||
$('#pushover_apikey').removeClass('warning');
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
$('#pushover_userkey,#pushover_apikey').removeClass('warning');
|
||||
$(this).prop('disabled', true);
|
||||
$('#testPushover-result').html(loading);
|
||||
$.get(sbRoot + '/home/testPushover', {'userKey': pushover_userkey, 'apiKey': pushover_apikey})
|
||||
.done(function (data) {
|
||||
$('#testPushover-result').html(data);
|
||||
$('#testPushover').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
$('#testPushover-result').html(loading);
|
||||
$.get(sbRoot + '/home/testPushover', {'userKey': pushover_userkey, 'apiKey': pushover_apikey})
|
||||
.done(function (data) {
|
||||
$('#testPushover-result').html(data);
|
||||
$('#testPushover').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
$('#testLibnotify').click(function() {
|
||||
$('#testLibnotify-result').html(loading);
|
||||
$.get(sbRoot + '/home/testLibnotify',
|
||||
function (data) { $('#testLibnotify-result').html(data); });
|
||||
});
|
||||
$('#testLibnotify').click(function() {
|
||||
$('#testLibnotify-result').html(loading);
|
||||
$.get(sbRoot + '/home/testLibnotify',
|
||||
function (data) { $('#testLibnotify-result').html(data); });
|
||||
});
|
||||
|
||||
$('#twitterStep1').click(function() {
|
||||
$('#testTwitter-result').html(loading);
|
||||
$.get(sbRoot + '/home/twitterStep1', function (data) {window.open(data); })
|
||||
.done(function () { $('#testTwitter-result').html('<b>Step1:</b> Confirm Authorization'); });
|
||||
});
|
||||
$('#twitterStep1').click(function() {
|
||||
$('#testTwitter-result').html(loading);
|
||||
$.get(sbRoot + '/home/twitterStep1', function (data) {window.open(data); })
|
||||
.done(function () { $('#testTwitter-result').html('<b>Step1:</b> Confirm Authorization'); });
|
||||
});
|
||||
|
||||
$('#twitterStep2').click(function () {
|
||||
var twitter_key = $.trim($('#twitter_key').val());
|
||||
if (!twitter_key) {
|
||||
$('#testTwitter-result').html('Please fill out the necessary fields above.');
|
||||
$('#twitterStep2').click(function () {
|
||||
var twitter_key = $.trim($('#twitter_key').val());
|
||||
if (!twitter_key) {
|
||||
$('#testTwitter-result').html('Please fill out the necessary fields above.');
|
||||
$('#twitter_key').addClass('warning');
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
$('#twitter_key').removeClass('warning');
|
||||
$('#testTwitter-result').html(loading);
|
||||
$.get(sbRoot + '/home/twitterStep2', {'key': twitter_key},
|
||||
function (data) { $('#testTwitter-result').html(data); });
|
||||
});
|
||||
$('#testTwitter-result').html(loading);
|
||||
$.get(sbRoot + '/home/twitterStep2', {'key': twitter_key},
|
||||
function (data) { $('#testTwitter-result').html(data); });
|
||||
});
|
||||
|
||||
$('#testTwitter').click(function() {
|
||||
$.get(sbRoot + '/home/testTwitter',
|
||||
function (data) { $('#testTwitter-result').html(data); });
|
||||
});
|
||||
$('#testTwitter').click(function() {
|
||||
$.get(sbRoot + '/home/testTwitter',
|
||||
function (data) { $('#testTwitter-result').html(data); });
|
||||
});
|
||||
|
||||
$('#settingsNMJ').click(function() {
|
||||
if (!$('#nmj_host').val()) {
|
||||
alert('Please fill in the Popcorn IP address');
|
||||
$('#nmj_host').focus();
|
||||
return;
|
||||
}
|
||||
$('#testNMJ-result').html(loading);
|
||||
var nmj_host = $('#nmj_host').val();
|
||||
|
||||
$.get(sbRoot + '/home/settingsNMJ', {'host': nmj_host},
|
||||
function (data) {
|
||||
if (data === null) {
|
||||
$('#nmj_database').removeAttr('readonly');
|
||||
$('#nmj_mount').removeAttr('readonly');
|
||||
}
|
||||
var JSONData = $.parseJSON(data);
|
||||
$('#testNMJ-result').html(JSONData.message);
|
||||
$('#nmj_database').val(JSONData.database);
|
||||
$('#nmj_mount').val(JSONData.mount);
|
||||
$('#settingsNMJ').click(function() {
|
||||
if (!$('#nmj_host').val()) {
|
||||
alert('Please fill in the Popcorn IP address');
|
||||
$('#nmj_host').focus();
|
||||
return;
|
||||
}
|
||||
$('#testNMJ-result').html(loading);
|
||||
var nmj_host = $('#nmj_host').val();
|
||||
|
||||
if (JSONData.database) {
|
||||
$('#nmj_database').attr('readonly', true);
|
||||
} else {
|
||||
$('#nmj_database').removeAttr('readonly');
|
||||
}
|
||||
if (JSONData.mount) {
|
||||
$('#nmj_mount').attr('readonly', true);
|
||||
} else {
|
||||
$('#nmj_mount').removeAttr('readonly');
|
||||
}
|
||||
});
|
||||
});
|
||||
$.get(sbRoot + '/home/settingsNMJ', {'host': nmj_host},
|
||||
function (data) {
|
||||
if (data === null) {
|
||||
$('#nmj_database').removeAttr('readonly');
|
||||
$('#nmj_mount').removeAttr('readonly');
|
||||
}
|
||||
var JSONData = $.parseJSON(data);
|
||||
$('#testNMJ-result').html(JSONData.message);
|
||||
$('#nmj_database').val(JSONData.database);
|
||||
$('#nmj_mount').val(JSONData.mount);
|
||||
|
||||
$('#testNMJ').click(function () {
|
||||
var nmj_host = $.trim($('#nmj_host').val());
|
||||
var nmj_database = $('#nmj_database').val();
|
||||
var nmj_mount = $('#nmj_mount').val();
|
||||
if (!nmj_host) {
|
||||
$('#testNMJ-result').html('Please fill out the necessary fields above.');
|
||||
if (JSONData.database) {
|
||||
$('#nmj_database').attr('readonly', true);
|
||||
} else {
|
||||
$('#nmj_database').removeAttr('readonly');
|
||||
}
|
||||
if (JSONData.mount) {
|
||||
$('#nmj_mount').attr('readonly', true);
|
||||
} else {
|
||||
$('#nmj_mount').removeAttr('readonly');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#testNMJ').click(function () {
|
||||
var nmj_host = $.trim($('#nmj_host').val());
|
||||
var nmj_database = $('#nmj_database').val();
|
||||
var nmj_mount = $('#nmj_mount').val();
|
||||
if (!nmj_host) {
|
||||
$('#testNMJ-result').html('Please fill out the necessary fields above.');
|
||||
$('#nmj_host').addClass('warning');
|
||||
return;
|
||||
}
|
||||
$('#nmj_host').removeClass('warning');
|
||||
return;
|
||||
}
|
||||
$('#nmj_host').removeClass('warning');
|
||||
$(this).prop('disabled', true);
|
||||
$('#testNMJ-result').html(loading);
|
||||
$.get(sbRoot + '/home/testNMJ', {'host': nmj_host, 'database': nmj_database, 'mount': nmj_mount})
|
||||
.done(function (data) {
|
||||
$('#testNMJ-result').html(data);
|
||||
$('#testNMJ').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
$('#testNMJ-result').html(loading);
|
||||
$.get(sbRoot + '/home/testNMJ', {'host': nmj_host, 'database': nmj_database, 'mount': nmj_mount})
|
||||
.done(function (data) {
|
||||
$('#testNMJ-result').html(data);
|
||||
$('#testNMJ').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
$('#settingsNMJv2').click(function() {
|
||||
if (!$('#nmjv2_host').val()) {
|
||||
alert('Please fill in the Popcorn IP address');
|
||||
$('#nmjv2_host').focus();
|
||||
return;
|
||||
}
|
||||
$('#testNMJv2-result').html(loading);
|
||||
var nmjv2_host = $('#nmjv2_host').val();
|
||||
if (!$('#nmjv2_host').val()) {
|
||||
alert('Please fill in the Popcorn IP address');
|
||||
$('#nmjv2_host').focus();
|
||||
return;
|
||||
}
|
||||
$('#testNMJv2-result').html(loading);
|
||||
var nmjv2_host = $('#nmjv2_host').val();
|
||||
var nmjv2_dbloc;
|
||||
var radios = document.getElementsByName('nmjv2_dbloc');
|
||||
for (var i = 0; i < radios.length; i++) {
|
||||
|
@ -235,46 +238,46 @@ $(document).ready(function(){
|
|||
}
|
||||
}
|
||||
|
||||
var nmjv2_dbinstance=$('#NMJv2db_instance').val();
|
||||
$.get(sbRoot + '/home/settingsNMJv2', {'host': nmjv2_host,'dbloc': nmjv2_dbloc,'instance': nmjv2_dbinstance},
|
||||
function (data){
|
||||
if (data == null) {
|
||||
$('#nmjv2_database').removeAttr('readonly');
|
||||
}
|
||||
var JSONData = $.parseJSON(data);
|
||||
$('#testNMJv2-result').html(JSONData.message);
|
||||
$('#nmjv2_database').val(JSONData.database);
|
||||
|
||||
if (JSONData.database)
|
||||
$('#nmjv2_database').attr('readonly', true);
|
||||
else
|
||||
$('#nmjv2_database').removeAttr('readonly');
|
||||
});
|
||||
});
|
||||
var nmjv2_dbinstance=$('#NMJv2db_instance').val();
|
||||
$.get(sbRoot + '/home/settingsNMJv2', {'host': nmjv2_host,'dbloc': nmjv2_dbloc,'instance': nmjv2_dbinstance},
|
||||
function (data){
|
||||
if (data == null) {
|
||||
$('#nmjv2_database').removeAttr('readonly');
|
||||
}
|
||||
var JSONData = $.parseJSON(data);
|
||||
$('#testNMJv2-result').html(JSONData.message);
|
||||
$('#nmjv2_database').val(JSONData.database);
|
||||
|
||||
$('#testNMJv2').click(function () {
|
||||
var nmjv2_host = $.trim($('#nmjv2_host').val());
|
||||
if (!nmjv2_host) {
|
||||
$('#testNMJv2-result').html('Please fill out the necessary fields above.');
|
||||
if (JSONData.database)
|
||||
$('#nmjv2_database').attr('readonly', true);
|
||||
else
|
||||
$('#nmjv2_database').removeAttr('readonly');
|
||||
});
|
||||
});
|
||||
|
||||
$('#testNMJv2').click(function () {
|
||||
var nmjv2_host = $.trim($('#nmjv2_host').val());
|
||||
if (!nmjv2_host) {
|
||||
$('#testNMJv2-result').html('Please fill out the necessary fields above.');
|
||||
$('#nmjv2_host').addClass('warning');
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
$('#nmjv2_host').removeClass('warning');
|
||||
$(this).prop('disabled', true);
|
||||
$('#testNMJv2-result').html(loading);
|
||||
$.get(sbRoot + '/home/testNMJv2', {'host': nmjv2_host})
|
||||
.done(function (data) {
|
||||
$('#testNMJv2-result').html(data);
|
||||
$('#testNMJv2').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
$('#testTrakt').click(function () {
|
||||
var trakt_api = $.trim($('#trakt_api').val());
|
||||
var trakt_username = $.trim($('#trakt_username').val());
|
||||
var trakt_password = $.trim($('#trakt_password').val());
|
||||
if (!trakt_api || !trakt_username || !trakt_password) {
|
||||
$('#testTrakt-result').html('Please fill out the necessary fields above.');
|
||||
$(this).prop('disabled', true);
|
||||
$('#testNMJv2-result').html(loading);
|
||||
$.get(sbRoot + '/home/testNMJv2', {'host': nmjv2_host})
|
||||
.done(function (data) {
|
||||
$('#testNMJv2-result').html(data);
|
||||
$('#testNMJv2').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
$('#testTrakt').click(function () {
|
||||
var trakt_api = $.trim($('#trakt_api').val());
|
||||
var trakt_username = $.trim($('#trakt_username').val());
|
||||
var trakt_password = $.trim($('#trakt_password').val());
|
||||
if (!trakt_api || !trakt_username || !trakt_password) {
|
||||
$('#testTrakt-result').html('Please fill out the necessary fields above.');
|
||||
if (!trakt_api) {
|
||||
$('#trakt_api').addClass('warning');
|
||||
} else {
|
||||
|
@ -290,189 +293,189 @@ $(document).ready(function(){
|
|||
} else {
|
||||
$('#trakt_password').removeClass('warning');
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
$('#trakt_api,#trakt_username,#trakt_password').removeClass('warning');
|
||||
$(this).prop('disabled', true);
|
||||
$('#testTrakt-result').html(loading);
|
||||
$.get(sbRoot + '/home/testTrakt', {'api': trakt_api, 'username': trakt_username, 'password': trakt_password})
|
||||
.done(function (data) {
|
||||
$('#testTrakt-result').html(data);
|
||||
$('#testTrakt').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
$('#testEmail').click(function () {
|
||||
var status, host, port, tls, from, user, pwd, err, to;
|
||||
status = $('#testEmail-result');
|
||||
status.html(loading);
|
||||
host = $('#email_host').val();
|
||||
host = host.length > 0 ? host : null;
|
||||
port = $('#email_port').val();
|
||||
port = port.length > 0 ? port : null;
|
||||
tls = $('#email_tls').attr('checked') !== undefined ? 1 : 0;
|
||||
from = $('#email_from').val();
|
||||
from = from.length > 0 ? from : 'root@localhost';
|
||||
user = $('#email_user').val().trim();
|
||||
pwd = $('#email_password').val();
|
||||
err = '';
|
||||
if (host === null) {
|
||||
err += '<li style="color: red;">You must specify an SMTP hostname!</li>';
|
||||
}
|
||||
if (port === null) {
|
||||
err += '<li style="color: red;">You must specify an SMTP port!</li>';
|
||||
} else if (port.match(/^\d+$/) === null || parseInt(port, 10) > 65535) {
|
||||
err += '<li style="color: red;">SMTP port must be between 0 and 65535!</li>';
|
||||
}
|
||||
if (err.length > 0) {
|
||||
err = '<ol>' + err + '</ol>';
|
||||
status.html(err);
|
||||
} else {
|
||||
to = prompt('Enter an email address to send the test to:', null);
|
||||
if (to === null || to.length === 0 || to.match(/.*@.*/) === null) {
|
||||
status.html('<p style="color: red;">You must provide a recipient email address!</p>');
|
||||
} else {
|
||||
$.get(sbRoot + '/home/testEmail', {host: host, port: port, smtp_from: from, use_tls: tls, user: user, pwd: pwd, to: to},
|
||||
function (msg) { $('#testEmail-result').html(msg); });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$('#testNMA').click(function () {
|
||||
var nma_api = $.trim($('#nma_api').val());
|
||||
var nma_priority = $('#nma_priority').val();
|
||||
if (!nma_api) {
|
||||
$('#testNMA-result').html('Please fill out the necessary fields above.');
|
||||
$('#nma_api').addClass('warning');
|
||||
return;
|
||||
}
|
||||
$('#nma_api').removeClass('warning');
|
||||
$(this).prop('disabled', true);
|
||||
$('#testNMA-result').html(loading);
|
||||
$.get(sbRoot + '/home/testNMA', {'nma_api': nma_api, 'nma_priority': nma_priority})
|
||||
.done(function (data) {
|
||||
$('#testNMA-result').html(data);
|
||||
$('#testNMA').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
$('#testPushalot').click(function () {
|
||||
var pushalot_authorizationtoken = $.trim($('#pushalot_authorizationtoken').val());
|
||||
if (!pushalot_authorizationtoken) {
|
||||
$('#testPushalot-result').html('Please fill out the necessary fields above.');
|
||||
$('#pushalot_authorizationtoken').addClass('warning');
|
||||
return;
|
||||
}
|
||||
$('#pushalot_authorizationtoken').removeClass('warning');
|
||||
$(this).prop('disabled', true);
|
||||
$('#testPushalot-result').html(loading);
|
||||
$.get(sbRoot + '/home/testPushalot', {'authorizationToken': pushalot_authorizationtoken})
|
||||
.done(function (data) {
|
||||
$('#testPushalot-result').html(data);
|
||||
$('#testPushalot').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
$('#testPushbullet').click(function () {
|
||||
var pushbullet_api = $.trim($('#pushbullet_api').val());
|
||||
if (!pushbullet_api) {
|
||||
$('#testPushbullet-result').html('Please fill out the necessary fields above.');
|
||||
$('#pushbullet_api').addClass('warning');
|
||||
return;
|
||||
}
|
||||
$('#pushbullet_api').removeClass('warning');
|
||||
$(this).prop('disabled', true);
|
||||
$('#testPushbullet-result').html(loading);
|
||||
$.get(sbRoot + '/home/testPushbullet', {'api': pushbullet_api})
|
||||
.done(function (data) {
|
||||
$('#testPushbullet-result').html(data);
|
||||
$('#testPushbullet').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
function get_pushbullet_devices(msg){
|
||||
|
||||
if(msg){
|
||||
$('#testPushbullet-result').html(loading);
|
||||
}
|
||||
|
||||
var pushbullet_api = $("#pushbullet_api").val();
|
||||
|
||||
if(!pushbullet_api) {
|
||||
$('#testPushbullet-result').html("You didn't supply a Pushbullet api key");
|
||||
$("#pushbullet_api").focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
var current_pushbullet_device = $("#pushbullet_device").val();
|
||||
$.get(sbRoot + "/home/getPushbulletDevices", {'api': pushbullet_api},
|
||||
function (data) {
|
||||
var devices = jQuery.parseJSON(data).devices;
|
||||
$("#pushbullet_device_list").html('');
|
||||
for (var i = 0; i < devices.length; i++) {
|
||||
if(devices[i].active == true) {
|
||||
if(current_pushbullet_device == devices[i].iden) {
|
||||
$("#pushbullet_device_list").append('<option value="'+devices[i].iden+'" selected>' + devices[i].nickname + '</option>')
|
||||
} else {
|
||||
$("#pushbullet_device_list").append('<option value="'+devices[i].iden+'">' + devices[i].nickname + '</option>')
|
||||
}
|
||||
}
|
||||
}
|
||||
if(msg) {
|
||||
$('#testPushbullet-result').html(msg);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$("#pushbullet_device_list").change(function(){
|
||||
$("#pushbullet_device").val($("#pushbullet_device_list").val());
|
||||
$('#testPushbullet-result').html("Don't forget to save your new pushbullet settings.");
|
||||
});
|
||||
};
|
||||
|
||||
$('#getPushbulletDevices').click(function(){
|
||||
get_pushbullet_devices("Device list updated. Please choose a device to push to.");
|
||||
});
|
||||
|
||||
// we have to call this function on dom ready to create the devices select
|
||||
get_pushbullet_devices();
|
||||
|
||||
$('#email_show').change(function () {
|
||||
var key = parseInt($('#email_show').val(), 10);
|
||||
$('#email_show_list').val(key >= 0 ? notify_data[key.toString()].list : '');
|
||||
$(this).prop('disabled', true);
|
||||
$('#testTrakt-result').html(loading);
|
||||
$.get(sbRoot + '/home/testTrakt', {'api': trakt_api, 'username': trakt_username, 'password': trakt_password})
|
||||
.done(function (data) {
|
||||
$('#testTrakt-result').html(data);
|
||||
$('#testTrakt').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
// Update the internal data struct anytime settings are saved to the server
|
||||
$('#email_show').bind('notify', function () { load_show_notify_lists(); });
|
||||
$('#testEmail').click(function () {
|
||||
var status, host, port, tls, from, user, pwd, err, to;
|
||||
status = $('#testEmail-result');
|
||||
status.html(loading);
|
||||
host = $('#email_host').val();
|
||||
host = host.length > 0 ? host : null;
|
||||
port = $('#email_port').val();
|
||||
port = port.length > 0 ? port : null;
|
||||
tls = $('#email_tls').attr('checked') !== undefined ? 1 : 0;
|
||||
from = $('#email_from').val();
|
||||
from = from.length > 0 ? from : 'root@localhost';
|
||||
user = $('#email_user').val().trim();
|
||||
pwd = $('#email_password').val();
|
||||
err = '';
|
||||
if (host === null) {
|
||||
err += '<li style="color: red;">You must specify an SMTP hostname!</li>';
|
||||
}
|
||||
if (port === null) {
|
||||
err += '<li style="color: red;">You must specify an SMTP port!</li>';
|
||||
} else if (port.match(/^\d+$/) === null || parseInt(port, 10) > 65535) {
|
||||
err += '<li style="color: red;">SMTP port must be between 0 and 65535!</li>';
|
||||
}
|
||||
if (err.length > 0) {
|
||||
err = '<ol>' + err + '</ol>';
|
||||
status.html(err);
|
||||
} else {
|
||||
to = prompt('Enter an email address to send the test to:', null);
|
||||
if (to === null || to.length === 0 || to.match(/.*@.*/) === null) {
|
||||
status.html('<p style="color: red;">You must provide a recipient email address!</p>');
|
||||
} else {
|
||||
$.get(sbRoot + '/home/testEmail', {host: host, port: port, smtp_from: from, use_tls: tls, user: user, pwd: pwd, to: to},
|
||||
function (msg) { $('#testEmail-result').html(msg); });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function load_show_notify_lists() {
|
||||
$.get(sbRoot + "/home/loadShowNotifyLists", function (data) {
|
||||
var list, html, s;
|
||||
list = $.parseJSON(data);
|
||||
notify_data = list;
|
||||
if (list._size === 0) {
|
||||
return;
|
||||
}
|
||||
html = '<option value="-1">-- Select --</option>';
|
||||
for (s in list) {
|
||||
if (s.charAt(0) !== '_') {
|
||||
html += '<option value="' + list[s].id + '">' + $('<div/>').text(list[s].name).html() + '</option>';
|
||||
}
|
||||
}
|
||||
$('#email_show').html(html);
|
||||
$('#email_show_list').val('');
|
||||
});
|
||||
}
|
||||
// Load the per show notify lists everytime this page is loaded
|
||||
load_show_notify_lists();
|
||||
$('#testNMA').click(function () {
|
||||
var nma_api = $.trim($('#nma_api').val());
|
||||
var nma_priority = $('#nma_priority').val();
|
||||
if (!nma_api) {
|
||||
$('#testNMA-result').html('Please fill out the necessary fields above.');
|
||||
$('#nma_api').addClass('warning');
|
||||
return;
|
||||
}
|
||||
$('#nma_api').removeClass('warning');
|
||||
$(this).prop('disabled', true);
|
||||
$('#testNMA-result').html(loading);
|
||||
$.get(sbRoot + '/home/testNMA', {'nma_api': nma_api, 'nma_priority': nma_priority})
|
||||
.done(function (data) {
|
||||
$('#testNMA-result').html(data);
|
||||
$('#testNMA').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
// show instructions for plex when enabled
|
||||
$('#use_plex').click(function() {
|
||||
if ( $(this).is(':checked') ) {
|
||||
$('.plexinfo').removeClass('hide');
|
||||
} else {
|
||||
$('.plexinfo').addClass('hide');
|
||||
}
|
||||
});
|
||||
$('#testPushalot').click(function () {
|
||||
var pushalot_authorizationtoken = $.trim($('#pushalot_authorizationtoken').val());
|
||||
if (!pushalot_authorizationtoken) {
|
||||
$('#testPushalot-result').html('Please fill out the necessary fields above.');
|
||||
$('#pushalot_authorizationtoken').addClass('warning');
|
||||
return;
|
||||
}
|
||||
$('#pushalot_authorizationtoken').removeClass('warning');
|
||||
$(this).prop('disabled', true);
|
||||
$('#testPushalot-result').html(loading);
|
||||
$.get(sbRoot + '/home/testPushalot', {'authorizationToken': pushalot_authorizationtoken})
|
||||
.done(function (data) {
|
||||
$('#testPushalot-result').html(data);
|
||||
$('#testPushalot').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
$('#testPushbullet').click(function () {
|
||||
var pushbullet_api = $.trim($('#pushbullet_api').val());
|
||||
if (!pushbullet_api) {
|
||||
$('#testPushbullet-result').html('Please fill out the necessary fields above.');
|
||||
$('#pushbullet_api').addClass('warning');
|
||||
return;
|
||||
}
|
||||
$('#pushbullet_api').removeClass('warning');
|
||||
$(this).prop('disabled', true);
|
||||
$('#testPushbullet-result').html(loading);
|
||||
$.get(sbRoot + '/home/testPushbullet', {'api': pushbullet_api})
|
||||
.done(function (data) {
|
||||
$('#testPushbullet-result').html(data);
|
||||
$('#testPushbullet').prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
function get_pushbullet_devices(msg){
|
||||
|
||||
if(msg){
|
||||
$('#testPushbullet-result').html(loading);
|
||||
}
|
||||
|
||||
var pushbullet_api = $("#pushbullet_api").val();
|
||||
|
||||
if(!pushbullet_api) {
|
||||
$('#testPushbullet-result').html("You didn't supply a Pushbullet api key");
|
||||
$("#pushbullet_api").focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
var current_pushbullet_device = $("#pushbullet_device").val();
|
||||
$.get(sbRoot + "/home/getPushbulletDevices", {'api': pushbullet_api},
|
||||
function (data) {
|
||||
var devices = jQuery.parseJSON(data).devices;
|
||||
$("#pushbullet_device_list").html('');
|
||||
for (var i = 0; i < devices.length; i++) {
|
||||
if(devices[i].active == true) {
|
||||
if(current_pushbullet_device == devices[i].iden) {
|
||||
$("#pushbullet_device_list").append('<option value="'+devices[i].iden+'" selected>' + devices[i].nickname + '</option>')
|
||||
} else {
|
||||
$("#pushbullet_device_list").append('<option value="'+devices[i].iden+'">' + devices[i].nickname + '</option>')
|
||||
}
|
||||
}
|
||||
}
|
||||
if(msg) {
|
||||
$('#testPushbullet-result').html(msg);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
$("#pushbullet_device_list").change(function(){
|
||||
$("#pushbullet_device").val($("#pushbullet_device_list").val());
|
||||
$('#testPushbullet-result').html("Don't forget to save your new pushbullet settings.");
|
||||
});
|
||||
};
|
||||
|
||||
$('#getPushbulletDevices').click(function(){
|
||||
get_pushbullet_devices("Device list updated. Please choose a device to push to.");
|
||||
});
|
||||
|
||||
// we have to call this function on dom ready to create the devices select
|
||||
get_pushbullet_devices();
|
||||
|
||||
$('#email_show').change(function () {
|
||||
var key = parseInt($('#email_show').val(), 10);
|
||||
$('#email_show_list').val(key >= 0 ? notify_data[key.toString()].list : '');
|
||||
});
|
||||
|
||||
// Update the internal data struct anytime settings are saved to the server
|
||||
$('#email_show').bind('notify', function () { load_show_notify_lists(); });
|
||||
|
||||
function load_show_notify_lists() {
|
||||
$.get(sbRoot + "/home/loadShowNotifyLists", function (data) {
|
||||
var list, html, s;
|
||||
list = $.parseJSON(data);
|
||||
notify_data = list;
|
||||
if (list._size === 0) {
|
||||
return;
|
||||
}
|
||||
html = '<option value="-1">-- Select --</option>';
|
||||
for (s in list) {
|
||||
if (s.charAt(0) !== '_') {
|
||||
html += '<option value="' + list[s].id + '">' + $('<div/>').text(list[s].name).html() + '</option>';
|
||||
}
|
||||
}
|
||||
$('#email_show').html(html);
|
||||
$('#email_show_list').val('');
|
||||
});
|
||||
}
|
||||
// Load the per show notify lists everytime this page is loaded
|
||||
load_show_notify_lists();
|
||||
|
||||
// show instructions for plex when enabled
|
||||
$('#use_plex').click(function() {
|
||||
if ( $(this).is(':checked') ) {
|
||||
$('.plexinfo').removeClass('hide');
|
||||
} else {
|
||||
$('.plexinfo').addClass('hide');
|
||||
}
|
||||
});
|
||||
if ($('input[id="use_plex"]').is(':checked')) {$('.plexinfo').removeClass('hide')}
|
||||
});
|
||||
|
|
|
@ -1,5 +1,26 @@
|
|||
$(document).ready(function () {
|
||||
$('a.shutdown').bind("click",function(e) {
|
||||
$('a.logout').bind('click',function(e) {
|
||||
e.preventDefault();
|
||||
var target = $( this ).attr('href');
|
||||
$.confirm({
|
||||
'title' : 'Logout',
|
||||
'message' : 'Are you sure you want to Logout from SickGear ?',
|
||||
'buttons' : {
|
||||
'Yes' : {
|
||||
'class' : 'green',
|
||||
'action': function(){
|
||||
location.href = target;
|
||||
}
|
||||
},
|
||||
'No' : {
|
||||
'class' : 'red',
|
||||
'action': function(){} // Nothing to do in this case. You can as well omit the action property.
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('a.shutdown').bind('click',function(e) {
|
||||
e.preventDefault();
|
||||
var target = $( this ).attr('href');
|
||||
$.confirm({
|
||||
|
@ -20,7 +41,7 @@ $(document).ready(function () {
|
|||
});
|
||||
});
|
||||
|
||||
$('a.restart').bind("click",function(e) {
|
||||
$('a.restart').bind('click',function(e) {
|
||||
e.preventDefault();
|
||||
var target = $( this ).attr('href');
|
||||
$.confirm({
|
||||
|
@ -41,10 +62,10 @@ $(document).ready(function () {
|
|||
});
|
||||
});
|
||||
|
||||
$('a.remove').bind("click",function(e) {
|
||||
$('a.remove').bind('click',function(e) {
|
||||
e.preventDefault();
|
||||
var target = $( this ).attr('href');
|
||||
var showname = document.getElementById("showtitle").getAttribute('data-showname');
|
||||
var showname = document.getElementById('showtitle').getAttribute('data-showname');
|
||||
$.confirm({
|
||||
'title' : 'Remove Show',
|
||||
'message' : 'Are you sure you want to remove <span class="footerhighlight">' + showname + '</span> from the database ?<br /><br /><input type="checkbox" id="deleteFiles"> <span class="red-text">Check to delete files as well. IRREVERSIBLE</span></input>',
|
||||
|
@ -64,7 +85,7 @@ $(document).ready(function () {
|
|||
});
|
||||
});
|
||||
|
||||
$('a.clearhistory').bind("click",function(e) {
|
||||
$('a.clearhistory').bind('click',function(e) {
|
||||
e.preventDefault();
|
||||
var target = $( this ).attr('href');
|
||||
$.confirm({
|
||||
|
@ -85,7 +106,7 @@ $(document).ready(function () {
|
|||
});
|
||||
});
|
||||
|
||||
$('a.trimhistory').bind("click",function(e) {
|
||||
$('a.trimhistory').bind('click',function(e) {
|
||||
e.preventDefault();
|
||||
var target = $( this ).attr('href');
|
||||
$.confirm({
|
||||
|
|
43
gui/slick/js/lib/pnotify.custom.min.js
vendored
|
@ -1,6 +1,6 @@
|
|||
$(document).ready(function () {
|
||||
|
||||
function populateSelect() {
|
||||
function populateLangSelect() {
|
||||
if (!$('#nameToSearch').length)
|
||||
return;
|
||||
|
||||
|
@ -30,6 +30,16 @@ $(document).ready(function () {
|
|||
}
|
||||
}
|
||||
|
||||
function cleanseText(text, toDisplay) {
|
||||
return (!0 == toDisplay
|
||||
? text
|
||||
.replace(/["]/g, '"')
|
||||
: text
|
||||
.replace(/Pokémon/, 'Pokemon')
|
||||
.replace(/(?:["]|")/g, '')
|
||||
);
|
||||
}
|
||||
|
||||
var searchRequestXhr = null;
|
||||
|
||||
function searchIndexers() {
|
||||
|
@ -42,15 +52,17 @@ $(document).ready(function () {
|
|||
searchRequestXhr.abort();
|
||||
|
||||
var elTvDatabase = $('#providedIndexer'),
|
||||
elIndexerLang = $('#indexerLangSelect'),
|
||||
searchingFor = elNameToSearch.val() + ' on ' + elTvDatabase.find('option:selected').text() + ' in ' + elIndexerLang.val();
|
||||
elIndexerLang = $('#indexerLangSelect');
|
||||
|
||||
$('#searchResults').empty().html('<img id="searchingAnim" src="' + sbRoot + '/images/loading32' + themeSpinner + '.gif" height="32" width="32" /> searching ' + searchingFor + '...');
|
||||
$('#searchResults').empty().html('<img id="searchingAnim" src="' + sbRoot + '/images/loading32' + themeSpinner + '.gif" height="32" width="32" />'
|
||||
+ ' searching <span class="boldest">' + cleanseText(elNameToSearch.val(), !0) + '</span>'
|
||||
+ ' on ' + elTvDatabase.find('option:selected').text() + ' in ' + elIndexerLang.val()
|
||||
+ '...');
|
||||
|
||||
searchRequestXhr = $.ajax({
|
||||
url: sbRoot + '/home/addShows/searchIndexersForShowName',
|
||||
data: {
|
||||
'search_term': elNameToSearch.val(),
|
||||
'search_term': cleanseText(elNameToSearch.val(), !1),
|
||||
'lang': elIndexerLang.val(),
|
||||
'indexer': elTvDatabase.val()
|
||||
},
|
||||
|
@ -70,7 +82,8 @@ $(document).ready(function () {
|
|||
rowType = (0 == row % 2 ? '' : ' class="alt"');
|
||||
row++;
|
||||
|
||||
var whichSeries = obj.join('|'),
|
||||
var whichSeries = cleanseText(obj.join('|'), !0),
|
||||
display_show_name = cleanseText(obj[4], !0),
|
||||
showstartdate = '';
|
||||
|
||||
if (null !== obj[5]) {
|
||||
|
@ -84,17 +97,17 @@ $(document).ready(function () {
|
|||
resultStr += '<div' + rowType + '>'
|
||||
+ '<input id="whichSeries" type="radio"'
|
||||
+ ' class="stepone-result-radio"'
|
||||
+ ' title="Add show <span style=\'color: rgb(66, 139, 202)\'>' + obj[4] + '</span>"'
|
||||
+ ' title="Add show <span style=\'color: rgb(66, 139, 202)\'>' + display_show_name + '</span>"'
|
||||
+ ' name="whichSeries"'
|
||||
+ ' value="' + whichSeries + '"'
|
||||
+ checked
|
||||
+ ' />'
|
||||
+ '<a'
|
||||
+ ' class="stepone-result-title"'
|
||||
+ ' title="View detail for <span style=\'color: rgb(66, 139, 202)\'>' + obj[4] + '</span>"'
|
||||
+ ' title="View detail for <span style=\'color: rgb(66, 139, 202)\'>' + display_show_name + '</span>"'
|
||||
+ ' href="' + anonURL + obj[2] + obj[3] + ((data.langid && '' != data.langid) ? '&lid=' + data.langid : '') + '"'
|
||||
+ ' onclick="window.open(this.href, \'_blank\'); return false;"'
|
||||
+ '>' + obj[4] + '</a>'
|
||||
+ '>' + display_show_name + '</a>'
|
||||
+ showstartdate
|
||||
+ (null == obj[0] ? ''
|
||||
: ' <span class="stepone-result-db grey-text">' + '[' + obj[0] + ']' + '</span>')
|
||||
|
@ -131,6 +144,7 @@ $(document).ready(function () {
|
|||
alert('You must choose a show to continue');
|
||||
return false;
|
||||
}
|
||||
generate_bwlist();
|
||||
$('#addShowForm').submit();
|
||||
});
|
||||
|
||||
|
@ -154,7 +168,7 @@ $(document).ready(function () {
|
|||
formid: 'addShowForm',
|
||||
revealfx: ['slide', 500],
|
||||
oninit: function () {
|
||||
populateSelect();
|
||||
populateLangSelect();
|
||||
updateSampleText();
|
||||
if ($('input:hidden[name="whichSeries"]').length && $('#fullShowPath').length) {
|
||||
goToStep(3);
|
||||
|
@ -192,8 +206,8 @@ $(document).ready(function () {
|
|||
} else {
|
||||
show_name = '';
|
||||
}
|
||||
|
||||
var sample_text = '<p>Adding show <span class="show-name">' + show_name + '</span>'
|
||||
update_bwlist(show_name);
|
||||
var sample_text = '<p>Adding show <span class="show-name">' + cleanseText(show_name, !0) + '</span>'
|
||||
+ ('' == show_name ? 'into<br />' : '<br />into')
|
||||
+ ' <span class="show-dest">';
|
||||
|
||||
|
@ -224,7 +238,7 @@ $(document).ready(function () {
|
|||
|
||||
// if we have a show name then sanitize and use it for the dir name
|
||||
if (show_name.length) {
|
||||
$.get(sbRoot + '/home/addShows/sanitizeFileName', {name: show_name}, function (data) {
|
||||
$.get(sbRoot + '/home/addShows/sanitizeFileName', {name: cleanseText(show_name, !1)}, function (data) {
|
||||
$('#displayText').html(sample_text.replace('||', data));
|
||||
});
|
||||
// if not then it's unknown
|
||||
|
@ -275,4 +289,59 @@ $(document).ready(function () {
|
|||
});
|
||||
});
|
||||
|
||||
$('#anime').change (function () {
|
||||
updateSampleText();
|
||||
myform.loadsection(2);
|
||||
});
|
||||
|
||||
function add_option_to_pool (text) {
|
||||
var groupvalue = '', groupview = text,
|
||||
option = $('<option>'),
|
||||
match = /^(.*?)#<3SG#(.*)$/m.exec(text);
|
||||
|
||||
if (match != null) {
|
||||
groupvalue = match[1];
|
||||
groupview = groupvalue + match[2];
|
||||
}
|
||||
option.attr('value', groupvalue);
|
||||
option.html(groupview);
|
||||
option.appendTo('#pool');
|
||||
}
|
||||
|
||||
function update_bwlist (show_name) {
|
||||
|
||||
$('#black, #white, #pool').children().remove();
|
||||
|
||||
if ($('#anime').prop('checked')) {
|
||||
$('#blackwhitelist').show();
|
||||
if (show_name) {
|
||||
$.getJSON(sbRoot + '/home/fetch_releasegroups', {'show_name': cleanseText(show_name, !1)}, function (data) {
|
||||
if ('success' == data['result']) {
|
||||
var groups = [];
|
||||
$.each(data.groups, function (i, group) {
|
||||
if ('' != group.name) {
|
||||
groups.push(group.name + '#<3SG#' + ' (' + group.rating + ') ' + group.range)
|
||||
}
|
||||
});
|
||||
if (0 < groups.length) {
|
||||
groups.sort();
|
||||
$.each(groups, function (i, text) {
|
||||
add_option_to_pool(text);
|
||||
});
|
||||
} else {
|
||||
add_option_to_pool('No groups returned from AniDB');
|
||||
}
|
||||
} else if ('fail' == data['result']) {
|
||||
if ('connect' == data['resp']) {
|
||||
add_option_to_pool('Fail:AniDB connect. Restart sg else check debug log');
|
||||
} else if ('init' == data['resp']) {
|
||||
add_option_to_pool('Did not initialise AniDB. Check debug log if reqd.');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
$('#blackwhitelist').hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
$(function () {
|
||||
$('.plotInfo, .plot-daybyday').each(function () {
|
||||
var match = $(this).attr('id').match(/^plot_info_(\d+)_(\d+)_(\d+)$/);
|
||||
var match = $(this).attr('id').match(/^plot(?:_info_|-)((\d+)_(\d+)[_x](\d+))$/);
|
||||
var showName = $('#show-' + match[1]).attr('data-rawname');
|
||||
$(this).qtip({
|
||||
content: {
|
||||
text: function(event, api) {
|
||||
|
@ -9,14 +10,16 @@ $(function () {
|
|||
url: $('#sbRoot').val() + '/home/plotDetails',
|
||||
type: 'GET',
|
||||
data: {
|
||||
show: match[1],
|
||||
episode: match[3],
|
||||
season: match[2]
|
||||
show: match[2],
|
||||
episode: match[4],
|
||||
season: match[3]
|
||||
}
|
||||
})
|
||||
.then(function(content) {
|
||||
// Set the tooltip content upon successful retrieval
|
||||
api.set('content.text', content);
|
||||
api.set('content.text', ('undefined' === typeof(showName) ? ''
|
||||
: ('' !== content ? '<b class="boldest">' + showName + '</b>' : showName))
|
||||
+ ('' !== content ? ' ' + content : ''));
|
||||
}, function(xhr, status, error) {
|
||||
// Upon failure... set the tooltip content to the status and error value
|
||||
api.set('content.text', status + ': ' + error);
|
||||
|
@ -32,7 +35,7 @@ $(function () {
|
|||
my: 'left center',
|
||||
adjust: {
|
||||
y: -10,
|
||||
x: 2
|
||||
x: 0
|
||||
}
|
||||
},
|
||||
style: {
|
||||
|
|
|
@ -12,6 +12,20 @@ $(document).ready(function(){
|
|||
});
|
||||
});
|
||||
|
||||
// selects all visible episode checkboxes
|
||||
$('.seriesCheck').click(function () {
|
||||
$('.epCheck:visible, .seasonCheck:visible').each(function () {
|
||||
this.checked = true
|
||||
});
|
||||
});
|
||||
|
||||
// clears all visible episode checkboxes and the season selectors
|
||||
$('.clearAll').click(function () {
|
||||
$('.epCheck:visible, .seasonCheck:visible').each(function () {
|
||||
this.checked = false
|
||||
});
|
||||
});
|
||||
|
||||
$('input[type=submit]').click(function(){
|
||||
var epArr = new Array()
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
from requests.adapters import HTTPAdapter
|
||||
from lib.requests.adapters import HTTPAdapter
|
||||
|
||||
from .controller import CacheController
|
||||
from .cache import DictCache
|
||||
|
||||
|
||||
class CacheControlAdapter(HTTPAdapter):
|
||||
invalidating_methods = set(['PUT', 'DELETE'])
|
||||
|
||||
|
@ -58,7 +59,11 @@ class CacheControlAdapter(HTTPAdapter):
|
|||
response = cached_response
|
||||
else:
|
||||
# try to cache the response
|
||||
self.controller.cache_response(request, response)
|
||||
try:
|
||||
self.controller.cache_response(request, response)
|
||||
except Exception as e:
|
||||
# Failed to cache the results
|
||||
pass
|
||||
|
||||
resp = super(CacheControlAdapter, self).build_response(
|
||||
request, response
|
||||
|
|
|
@ -6,7 +6,7 @@ import calendar
|
|||
import time
|
||||
import datetime
|
||||
|
||||
from requests.structures import CaseInsensitiveDict
|
||||
from lib.requests.structures import CaseInsensitiveDict
|
||||
|
||||
from .cache import DictCache
|
||||
from .compat import parsedate_tz
|
||||
|
@ -21,7 +21,7 @@ def parse_uri(uri):
|
|||
(scheme, authority, path, query, fragment) = parse_uri(uri)
|
||||
"""
|
||||
groups = URI.match(uri).groups()
|
||||
return (groups[1], groups[3], groups[4], groups[6], groups[8])
|
||||
return groups[1], groups[3], groups[4], groups[6], groups[8]
|
||||
|
||||
|
||||
class CacheController(object):
|
||||
|
|
|
@ -2,22 +2,25 @@ from __future__ import absolute_import
|
|||
|
||||
import time
|
||||
import os
|
||||
import errno
|
||||
|
||||
from . import (LockBase, LockFailed, NotLocked, NotMyLock, LockTimeout,
|
||||
AlreadyLocked)
|
||||
|
||||
class LinkLockFile(LockBase):
|
||||
"""Lock access to a file using atomic property of link(2).
|
||||
|
||||
>>> lock = LinkLockFile('somefile')
|
||||
>>> lock = LinkLockFile('somefile', threaded=False)
|
||||
class LinkLockFile(LockBase):
|
||||
"""
|
||||
Lock access to a file using atomic property of link(2).
|
||||
|
||||
lock = LinkLockFile('somefile'[, threaded=False[, timeout=None]])
|
||||
"""
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
def acquire(self, timeout=None):
|
||||
try:
|
||||
open(self.unique_name, "wb").close()
|
||||
open(self.unique_name, 'wb').close()
|
||||
except IOError:
|
||||
raise LockFailed("failed to create %s" % self.unique_name)
|
||||
raise LockFailed('failed to create %s' % self.unique_name)
|
||||
|
||||
timeout = timeout is not None and timeout or self.timeout
|
||||
end_time = time.time()
|
||||
|
@ -28,7 +31,10 @@ class LinkLockFile(LockBase):
|
|||
# Try and create a hard link to it.
|
||||
try:
|
||||
os.link(self.unique_name, self.lock_file)
|
||||
except OSError:
|
||||
except OSError as e:
|
||||
if errno.ENOSYS == e.errno:
|
||||
raise LockFailed('%s' % e.strerror)
|
||||
|
||||
# Link creation failed. Maybe we've double-locked?
|
||||
nlinks = os.stat(self.unique_name).st_nlink
|
||||
if nlinks == 2:
|
||||
|
@ -40,22 +46,20 @@ class LinkLockFile(LockBase):
|
|||
if timeout is not None and time.time() > end_time:
|
||||
os.unlink(self.unique_name)
|
||||
if timeout > 0:
|
||||
raise LockTimeout("Timeout waiting to acquire"
|
||||
" lock for %s" %
|
||||
self.path)
|
||||
raise LockTimeout('Timeout waiting to acquire lock for %s' % self.path)
|
||||
else:
|
||||
raise AlreadyLocked("%s is already locked" %
|
||||
self.path)
|
||||
time.sleep(timeout is not None and timeout/10 or 0.1)
|
||||
raise AlreadyLocked('%s is already locked' % self.path)
|
||||
|
||||
time.sleep(timeout is not None and (timeout / 10) or 0.1)
|
||||
else:
|
||||
# Link creation succeeded. We're good to go.
|
||||
return
|
||||
|
||||
def release(self):
|
||||
if not self.is_locked():
|
||||
raise NotLocked("%s is not locked" % self.path)
|
||||
raise NotLocked('%s is not locked' % self.path)
|
||||
elif not os.path.exists(self.unique_name):
|
||||
raise NotMyLock("%s is locked, but not by me" % self.path)
|
||||
raise NotMyLock('%s is locked, but not by me' % self.path)
|
||||
os.unlink(self.unique_name)
|
||||
os.unlink(self.lock_file)
|
||||
|
||||
|
@ -70,4 +74,3 @@ class LinkLockFile(LockBase):
|
|||
def break_lock(self):
|
||||
if os.path.exists(self.lock_file):
|
||||
os.unlink(self.lock_file)
|
||||
|
||||
|
|
|
@ -32,8 +32,7 @@ __all__ = ['SERVICES', 'LANGUAGE_INDEX', 'SERVICE_INDEX', 'SERVICE_CONFIDENCE',
|
|||
'create_list_tasks', 'create_download_tasks', 'consume_task', 'matching_confidence',
|
||||
'key_subtitles', 'group_by_video']
|
||||
logger = logging.getLogger("subliminal")
|
||||
SERVICES = ['opensubtitles', 'subswiki', 'subtitulos', 'thesubdb', 'addic7ed', 'tvsubtitles', 'itasa',
|
||||
'usub', 'subscenter']
|
||||
SERVICES = ['opensubtitles', 'thesubdb', 'addic7ed', 'tvsubtitles']
|
||||
LANGUAGE_INDEX, SERVICE_INDEX, SERVICE_CONFIDENCE, MATCHING_CONFIDENCE = range(4)
|
||||
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import datetime as dt
|
|||
import requests
|
||||
import requests.exceptions
|
||||
import xmltodict
|
||||
from sickbeard.network_timezones import standardize_network
|
||||
|
||||
try:
|
||||
import xml.etree.cElementTree as ElementTree
|
||||
|
@ -443,7 +444,9 @@ class TVRage:
|
|||
if value:
|
||||
if isinstance(value, dict):
|
||||
if key == 'network':
|
||||
value = value['#text']
|
||||
network = value['#text']
|
||||
country = value['@country']
|
||||
value = standardize_network(network, country)
|
||||
if key == 'genre':
|
||||
value = value['genre']
|
||||
if not value:
|
||||
|
|
|
@ -29,14 +29,16 @@ from threading import Lock
|
|||
# apparently py2exe won't build these unless they're imported somewhere
|
||||
import sys
|
||||
import os.path
|
||||
import uuid
|
||||
import base64
|
||||
sys.path.append(os.path.abspath('../lib'))
|
||||
from sickbeard import providers, metadata, config, webserveInit
|
||||
from sickbeard.providers.generic import GenericProvider
|
||||
from providers import ezrss, tvtorrents, btn, newznab, womble, thepiratebay, torrentleech, kat, iptorrents, \
|
||||
omgwtfnzbs, scc, hdtorrents, torrentday, hdbits, nextgen, speedcd, nyaatorrents, fanzub, torrentbytes, \
|
||||
from providers import ezrss, btn, newznab, womble, thepiratebay, torrentleech, kat, iptorrents, \
|
||||
omgwtfnzbs, scc, hdtorrents, torrentday, hdbits, nextgen, speedcd, nyaatorrents, torrentbytes, \
|
||||
freshontv, bitsoup, tokyotoshokan
|
||||
from sickbeard.config import CheckSection, check_setting_int, check_setting_str, check_setting_float, ConfigMigrator, \
|
||||
naming_ep_type
|
||||
naming_ep_type, minimax
|
||||
from sickbeard import searchBacklog, showUpdater, versionChecker, properFinder, autoPostProcesser, \
|
||||
subtitles, traktChecker
|
||||
from sickbeard import helpers, db, exceptions, show_queue, search_queue, scheduler, show_name_helpers
|
||||
|
@ -58,7 +60,7 @@ CFG = None
|
|||
CONFIG_FILE = None
|
||||
|
||||
# This is the version of the config we EXPECT to find
|
||||
CONFIG_VERSION = 7
|
||||
CONFIG_VERSION = 8
|
||||
|
||||
# Default encryption version (0 for None)
|
||||
ENCRYPTION_VERSION = 0
|
||||
|
@ -91,6 +93,8 @@ traktCheckerScheduler = None
|
|||
|
||||
showList = None
|
||||
loadingShowList = None
|
||||
UPDATE_SHOWS_ON_START = False
|
||||
SHOW_UPDATE_HOUR = 3
|
||||
|
||||
providerList = []
|
||||
newznabProviderList = []
|
||||
|
@ -144,7 +148,6 @@ LAUNCH_BROWSER = False
|
|||
CACHE_DIR = None
|
||||
ACTUAL_CACHE_DIR = None
|
||||
ROOT_DIRS = None
|
||||
UPDATE_SHOWS_ON_START = False
|
||||
TRASH_REMOVE_SHOW = False
|
||||
TRASH_ROTATE_LOGS = False
|
||||
HOME_SEARCH_FOCUS = True
|
||||
|
@ -309,19 +312,12 @@ TWITTER_USERNAME = None
|
|||
TWITTER_PASSWORD = None
|
||||
TWITTER_PREFIX = None
|
||||
|
||||
USE_BOXCAR = False
|
||||
BOXCAR_NOTIFY_ONSNATCH = False
|
||||
BOXCAR_NOTIFY_ONDOWNLOAD = False
|
||||
BOXCAR_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
BOXCAR_USERNAME = None
|
||||
BOXCAR_PASSWORD = None
|
||||
BOXCAR_PREFIX = None
|
||||
|
||||
USE_BOXCAR2 = False
|
||||
BOXCAR2_NOTIFY_ONSNATCH = False
|
||||
BOXCAR2_NOTIFY_ONDOWNLOAD = False
|
||||
BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD = False
|
||||
BOXCAR2_ACCESSTOKEN = None
|
||||
BOXCAR2_SOUND = None
|
||||
|
||||
USE_PUSHOVER = False
|
||||
PUSHOVER_NOTIFY_ONSNATCH = False
|
||||
|
@ -456,6 +452,8 @@ CALENDAR_UNPROTECTED = False
|
|||
TMDB_API_KEY = 'edc5f123313769de83a71e157758030b'
|
||||
TRAKT_API_KEY = 'abd806c54516240c76e4ebc9c5ccf394'
|
||||
|
||||
COOKIE_SECRET = base64.b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes)
|
||||
|
||||
__INITIALIZED__ = False
|
||||
|
||||
def get_backlog_cycle_time():
|
||||
|
@ -475,7 +473,7 @@ def initialize(consoleLogging=True):
|
|||
USE_TRAKT, TRAKT_USERNAME, TRAKT_PASSWORD, TRAKT_API, TRAKT_REMOVE_WATCHLIST, TRAKT_USE_WATCHLIST, TRAKT_METHOD_ADD, TRAKT_START_PAUSED, traktCheckerScheduler, TRAKT_USE_RECOMMENDED, TRAKT_SYNC, TRAKT_DEFAULT_INDEXER, TRAKT_REMOVE_SERIESLIST, \
|
||||
USE_PLEX, PLEX_NOTIFY_ONSNATCH, PLEX_NOTIFY_ONDOWNLOAD, PLEX_NOTIFY_ONSUBTITLEDOWNLOAD, PLEX_UPDATE_LIBRARY, \
|
||||
PLEX_SERVER_HOST, PLEX_HOST, PLEX_USERNAME, PLEX_PASSWORD, DEFAULT_BACKLOG_FREQUENCY, MIN_BACKLOG_FREQUENCY, BACKLOG_STARTUP, SKIP_REMOVED_FILES, \
|
||||
showUpdateScheduler, __INITIALIZED__, LAUNCH_BROWSER, UPDATE_SHOWS_ON_START, TRASH_REMOVE_SHOW, TRASH_ROTATE_LOGS, HOME_SEARCH_FOCUS, SORT_ARTICLE, showList, loadingShowList, \
|
||||
showUpdateScheduler, __INITIALIZED__, LAUNCH_BROWSER, TRASH_REMOVE_SHOW, TRASH_ROTATE_LOGS, HOME_SEARCH_FOCUS, SORT_ARTICLE, showList, loadingShowList, UPDATE_SHOWS_ON_START, SHOW_UPDATE_HOUR, \
|
||||
NEWZNAB_DATA, NZBS, NZBS_UID, NZBS_HASH, INDEXER_DEFAULT, INDEXER_TIMEOUT, USENET_RETENTION, TORRENT_DIR, \
|
||||
QUALITY_DEFAULT, FLATTEN_FOLDERS_DEFAULT, SUBTITLES_DEFAULT, STATUS_DEFAULT, RECENTSEARCH_STARTUP, \
|
||||
GROWL_NOTIFY_ONSNATCH, GROWL_NOTIFY_ONDOWNLOAD, GROWL_NOTIFY_ONSUBTITLEDOWNLOAD, TWITTER_NOTIFY_ONSNATCH, TWITTER_NOTIFY_ONDOWNLOAD, TWITTER_NOTIFY_ONSUBTITLEDOWNLOAD, \
|
||||
|
@ -491,8 +489,7 @@ def initialize(consoleLogging=True):
|
|||
RENAME_EPISODES, AIRDATE_EPISODES, properFinderScheduler, PROVIDER_ORDER, autoPostProcesserScheduler, \
|
||||
WOMBLE, OMGWTFNZBS, OMGWTFNZBS_USERNAME, OMGWTFNZBS_APIKEY, providerList, newznabProviderList, torrentRssProviderList, \
|
||||
EXTRA_SCRIPTS, USE_TWITTER, TWITTER_USERNAME, TWITTER_PASSWORD, TWITTER_PREFIX, RECENTSEARCH_FREQUENCY, \
|
||||
USE_BOXCAR, BOXCAR_USERNAME, BOXCAR_PASSWORD, BOXCAR_NOTIFY_ONDOWNLOAD, BOXCAR_NOTIFY_ONSUBTITLEDOWNLOAD, BOXCAR_NOTIFY_ONSNATCH, \
|
||||
USE_BOXCAR2, BOXCAR2_ACCESSTOKEN, BOXCAR2_NOTIFY_ONDOWNLOAD, BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD, BOXCAR2_NOTIFY_ONSNATCH, \
|
||||
USE_BOXCAR2, BOXCAR2_ACCESSTOKEN, BOXCAR2_NOTIFY_ONDOWNLOAD, BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD, BOXCAR2_NOTIFY_ONSNATCH, BOXCAR2_SOUND, \
|
||||
USE_PUSHOVER, PUSHOVER_USERKEY, PUSHOVER_APIKEY, PUSHOVER_NOTIFY_ONDOWNLOAD, PUSHOVER_NOTIFY_ONSUBTITLEDOWNLOAD, PUSHOVER_NOTIFY_ONSNATCH, \
|
||||
USE_LIBNOTIFY, LIBNOTIFY_NOTIFY_ONSNATCH, LIBNOTIFY_NOTIFY_ONDOWNLOAD, LIBNOTIFY_NOTIFY_ONSUBTITLEDOWNLOAD, USE_NMJ, NMJ_HOST, NMJ_DATABASE, NMJ_MOUNT, USE_NMJv2, NMJv2_HOST, NMJv2_DATABASE, NMJv2_DBLOC, USE_SYNOINDEX, \
|
||||
USE_SYNOLOGYNOTIFIER, SYNOLOGYNOTIFIER_NOTIFY_ONSNATCH, SYNOLOGYNOTIFIER_NOTIFY_ONDOWNLOAD, SYNOLOGYNOTIFIER_NOTIFY_ONSUBTITLEDOWNLOAD, \
|
||||
|
@ -506,7 +503,8 @@ def initialize(consoleLogging=True):
|
|||
USE_FAILED_DOWNLOADS, DELETE_FAILED, ANON_REDIRECT, LOCALHOST_IP, TMDB_API_KEY, DEBUG, PROXY_SETTING, PROXY_INDEXERS, \
|
||||
AUTOPOSTPROCESSER_FREQUENCY, DEFAULT_AUTOPOSTPROCESSER_FREQUENCY, MIN_AUTOPOSTPROCESSER_FREQUENCY, \
|
||||
ANIME_DEFAULT, NAMING_ANIME, ANIMESUPPORT, USE_ANIDB, ANIDB_USERNAME, ANIDB_PASSWORD, ANIDB_USE_MYLIST, \
|
||||
ANIME_SPLIT_HOME, SCENE_DEFAULT, BACKLOG_DAYS, ANIME_TREAT_AS_HDTV
|
||||
ANIME_SPLIT_HOME, SCENE_DEFAULT, BACKLOG_DAYS, ANIME_TREAT_AS_HDTV, \
|
||||
COOKIE_SECRET
|
||||
|
||||
if __INITIALIZED__:
|
||||
return False
|
||||
|
@ -521,7 +519,6 @@ def initialize(consoleLogging=True):
|
|||
CheckSection(CFG, 'Growl')
|
||||
CheckSection(CFG, 'Prowl')
|
||||
CheckSection(CFG, 'Twitter')
|
||||
CheckSection(CFG, 'Boxcar')
|
||||
CheckSection(CFG, 'Boxcar2')
|
||||
CheckSection(CFG, 'NMJ')
|
||||
CheckSection(CFG, 'NMJv2')
|
||||
|
@ -603,11 +600,13 @@ def initialize(consoleLogging=True):
|
|||
ANON_REDIRECT = check_setting_str(CFG, 'General', 'anon_redirect', 'http://dereferer.org/?')
|
||||
PROXY_SETTING = check_setting_str(CFG, 'General', 'proxy_setting', '')
|
||||
PROXY_INDEXERS = bool(check_setting_int(CFG, 'General', 'proxy_indexers', 1))
|
||||
# attempt to help prevent users from breaking links by using a bad url
|
||||
# attempt to help prevent users from breaking links by using a bad url
|
||||
if not ANON_REDIRECT.endswith('?'):
|
||||
ANON_REDIRECT = ''
|
||||
|
||||
UPDATE_SHOWS_ON_START = bool(check_setting_int(CFG, 'General', 'update_shows_on_start', 0))
|
||||
SHOW_UPDATE_HOUR = check_setting_int(CFG, 'General', 'show_update_hour', 3)
|
||||
SHOW_UPDATE_HOUR = minimax(SHOW_UPDATE_HOUR, 3, 0, 23)
|
||||
TRASH_REMOVE_SHOW = bool(check_setting_int(CFG, 'General', 'trash_remove_show', 0))
|
||||
TRASH_ROTATE_LOGS = bool(check_setting_int(CFG, 'General', 'trash_rotate_logs', 0))
|
||||
|
||||
|
@ -674,8 +673,8 @@ def initialize(consoleLogging=True):
|
|||
|
||||
ALLOW_HIGH_PRIORITY = bool(check_setting_int(CFG, 'General', 'allow_high_priority', 1))
|
||||
|
||||
RECENTSEARCH_STARTUP = bool(check_setting_int(CFG, 'General', 'recentsearch_startup', 1))
|
||||
BACKLOG_STARTUP = bool(check_setting_int(CFG, 'General', 'backlog_startup', 1))
|
||||
RECENTSEARCH_STARTUP = bool(check_setting_int(CFG, 'General', 'recentsearch_startup', 0))
|
||||
BACKLOG_STARTUP = bool(check_setting_int(CFG, 'General', 'backlog_startup', 0))
|
||||
SKIP_REMOVED_FILES = bool(check_setting_int(CFG, 'General', 'skip_removed_files', 0))
|
||||
|
||||
USENET_RETENTION = check_setting_int(CFG, 'General', 'usenet_retention', 500)
|
||||
|
@ -793,18 +792,13 @@ def initialize(consoleLogging=True):
|
|||
TWITTER_PASSWORD = check_setting_str(CFG, 'Twitter', 'twitter_password', '')
|
||||
TWITTER_PREFIX = check_setting_str(CFG, 'Twitter', 'twitter_prefix', 'SickGear')
|
||||
|
||||
USE_BOXCAR = bool(check_setting_int(CFG, 'Boxcar', 'use_boxcar', 0))
|
||||
BOXCAR_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'Boxcar', 'boxcar_notify_onsnatch', 0))
|
||||
BOXCAR_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'Boxcar', 'boxcar_notify_ondownload', 0))
|
||||
BOXCAR_NOTIFY_ONSUBTITLEDOWNLOAD = bool(check_setting_int(CFG, 'Boxcar', 'boxcar_notify_onsubtitledownload', 0))
|
||||
BOXCAR_USERNAME = check_setting_str(CFG, 'Boxcar', 'boxcar_username', '')
|
||||
|
||||
USE_BOXCAR2 = bool(check_setting_int(CFG, 'Boxcar2', 'use_boxcar2', 0))
|
||||
BOXCAR2_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'Boxcar2', 'boxcar2_notify_onsnatch', 0))
|
||||
BOXCAR2_NOTIFY_ONDOWNLOAD = bool(check_setting_int(CFG, 'Boxcar2', 'boxcar2_notify_ondownload', 0))
|
||||
BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD = bool(
|
||||
check_setting_int(CFG, 'Boxcar2', 'boxcar2_notify_onsubtitledownload', 0))
|
||||
BOXCAR2_ACCESSTOKEN = check_setting_str(CFG, 'Boxcar2', 'boxcar2_accesstoken', '')
|
||||
BOXCAR2_SOUND = check_setting_str(CFG, 'Boxcar2', 'boxcar2_sound', 'default')
|
||||
|
||||
USE_PUSHOVER = bool(check_setting_int(CFG, 'Pushover', 'use_pushover', 0))
|
||||
PUSHOVER_NOTIFY_ONSNATCH = bool(check_setting_int(CFG, 'Pushover', 'pushover_notify_onsnatch', 0))
|
||||
|
@ -1052,7 +1046,7 @@ def initialize(consoleLogging=True):
|
|||
if hasattr(curNzbProvider, 'enable_recentsearch'):
|
||||
curNzbProvider.enable_recentsearch = bool(check_setting_int(CFG, curNzbProvider.getID().upper(),
|
||||
curNzbProvider.getID() + '_enable_recentsearch',
|
||||
1))
|
||||
1))
|
||||
if hasattr(curNzbProvider, 'enable_backlog'):
|
||||
curNzbProvider.enable_backlog = bool(check_setting_int(CFG, curNzbProvider.getID().upper(),
|
||||
curNzbProvider.getID() + '_enable_backlog',
|
||||
|
@ -1115,7 +1109,7 @@ def initialize(consoleLogging=True):
|
|||
showUpdateScheduler = scheduler.Scheduler(showUpdater.ShowUpdater(),
|
||||
cycleTime=datetime.timedelta(hours=1),
|
||||
threadName="SHOWUPDATER",
|
||||
start_time=datetime.time(hour=3)) # 3 AM
|
||||
start_time=datetime.time(hour=SHOW_UPDATE_HOUR)) # 3 AM
|
||||
|
||||
# searchers
|
||||
searchQueueScheduler = scheduler.Scheduler(search_queue.SearchQueue(),
|
||||
|
@ -1127,14 +1121,14 @@ def initialize(consoleLogging=True):
|
|||
cycleTime=update_interval,
|
||||
threadName="RECENTSEARCHER",
|
||||
run_delay=update_now if RECENTSEARCH_STARTUP
|
||||
else update_interval)
|
||||
else datetime.timedelta(minutes=5))
|
||||
|
||||
update_interval = datetime.timedelta(minutes=BACKLOG_FREQUENCY)
|
||||
backlogSearchScheduler = searchBacklog.BacklogSearchScheduler(searchBacklog.BacklogSearcher(),
|
||||
cycleTime=update_interval,
|
||||
threadName="BACKLOG",
|
||||
run_delay=update_now if BACKLOG_STARTUP
|
||||
else update_interval)
|
||||
else datetime.timedelta(minutes=10))
|
||||
|
||||
search_intervals = {'15m': 15, '45m': 45, '90m': 90, '4h': 4 * 60, 'daily': 24 * 60}
|
||||
if CHECK_PROPERS_INTERVAL in search_intervals:
|
||||
|
@ -1428,6 +1422,7 @@ def save_config():
|
|||
new_config['General']['naming_anime'] = int(NAMING_ANIME)
|
||||
new_config['General']['launch_browser'] = int(LAUNCH_BROWSER)
|
||||
new_config['General']['update_shows_on_start'] = int(UPDATE_SHOWS_ON_START)
|
||||
new_config['General']['show_update_hour'] = int(SHOW_UPDATE_HOUR)
|
||||
new_config['General']['trash_remove_show'] = int(TRASH_REMOVE_SHOW)
|
||||
new_config['General']['trash_rotate_logs'] = int(TRASH_ROTATE_LOGS)
|
||||
new_config['General']['home_search_focus'] = int(HOME_SEARCH_FOCUS)
|
||||
|
@ -1641,19 +1636,13 @@ def save_config():
|
|||
new_config['Twitter']['twitter_password'] = helpers.encrypt(TWITTER_PASSWORD, ENCRYPTION_VERSION)
|
||||
new_config['Twitter']['twitter_prefix'] = TWITTER_PREFIX
|
||||
|
||||
new_config['Boxcar'] = {}
|
||||
new_config['Boxcar']['use_boxcar'] = int(USE_BOXCAR)
|
||||
new_config['Boxcar']['boxcar_notify_onsnatch'] = int(BOXCAR_NOTIFY_ONSNATCH)
|
||||
new_config['Boxcar']['boxcar_notify_ondownload'] = int(BOXCAR_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Boxcar']['boxcar_notify_onsubtitledownload'] = int(BOXCAR_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Boxcar']['boxcar_username'] = BOXCAR_USERNAME
|
||||
|
||||
new_config['Boxcar2'] = {}
|
||||
new_config['Boxcar2']['use_boxcar2'] = int(USE_BOXCAR2)
|
||||
new_config['Boxcar2']['boxcar2_notify_onsnatch'] = int(BOXCAR2_NOTIFY_ONSNATCH)
|
||||
new_config['Boxcar2']['boxcar2_notify_ondownload'] = int(BOXCAR2_NOTIFY_ONDOWNLOAD)
|
||||
new_config['Boxcar2']['boxcar2_notify_onsubtitledownload'] = int(BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD)
|
||||
new_config['Boxcar2']['boxcar2_accesstoken'] = BOXCAR2_ACCESSTOKEN
|
||||
new_config['Boxcar2']['boxcar2_sound'] = BOXCAR2_SOUND
|
||||
|
||||
new_config['Pushover'] = {}
|
||||
new_config['Pushover']['use_pushover'] = int(USE_PUSHOVER)
|
||||
|
|
|
@ -16,197 +16,102 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from sickbeard import db, logger
|
||||
import sickbeard
|
||||
from sickbeard import db, logger, helpers
|
||||
import urllib
|
||||
|
||||
class BlackAndWhiteList(object):
|
||||
_tableBlack = "blacklist"
|
||||
_tableWhite = "whitelist"
|
||||
blackList = []
|
||||
whiteList = []
|
||||
blackDict = {}
|
||||
whiteDict = {}
|
||||
|
||||
last_black_valid_result = None
|
||||
last_white_valid_result = None
|
||||
blacklist = []
|
||||
whitelist = []
|
||||
|
||||
def __init__(self, show_id):
|
||||
if not show_id:
|
||||
raise BlackWhitelistNoShowIDException()
|
||||
self.show_id = show_id
|
||||
self.refresh()
|
||||
self.load()
|
||||
|
||||
def refresh(self):
|
||||
logger.log(u"Building black and white list for " + str(self.show_id), logger.DEBUG)
|
||||
def load(self):
|
||||
logger.log(u'Building black and white list for ' + str(self.show_id), logger.DEBUG)
|
||||
self.blacklist = self._load_list('blacklist')
|
||||
self.whitelist = self._load_list('whitelist')
|
||||
|
||||
(self.blackList, self.blackDict) = self.load_blacklist()
|
||||
(self.whiteList, self.whiteDict) = self.load_whitelist()
|
||||
|
||||
def load_blacklist(self):
|
||||
return self._load_list(self._tableBlack)
|
||||
|
||||
def load_whitelist(self):
|
||||
return self._load_list(self._tableWhite)
|
||||
|
||||
def get_black_keywords_for(self, range):
|
||||
if range in self.blackDict:
|
||||
return self.blackDict[range]
|
||||
else:
|
||||
return []
|
||||
|
||||
def get_white_keywords_for(self, range):
|
||||
if range in self.whiteDict:
|
||||
return self.whiteDict[range]
|
||||
else:
|
||||
return []
|
||||
|
||||
def set_black_keywords(self, range, values):
|
||||
self._del_all_black_keywords()
|
||||
self._add_keywords(self._tableBlack, range, values)
|
||||
|
||||
def set_white_keywords(self, range, values):
|
||||
self._del_all_white_keywords()
|
||||
self._add_keywords(self._tableWhite, range, values)
|
||||
|
||||
def set_black_keywords_for(self, range, values):
|
||||
self._del_all_black_keywords_for(range)
|
||||
self._add_keywords(self._tableBlack, range, values)
|
||||
|
||||
def set_white_keywords_for(self, range, values):
|
||||
self._del_all_white_keywords_for(range)
|
||||
self._add_keywords(self._tableWhite, range, values)
|
||||
|
||||
def add_black_keyword(self, range, value):
|
||||
self._add_keywords(self._tableBlack, range, [value])
|
||||
|
||||
def add_white_keyword(self, range, value):
|
||||
self._add_keywords(self._tableWhite, range, [value])
|
||||
|
||||
def get_last_result_msg(self):
|
||||
blackResult = whiteResult = "Untested"
|
||||
if self.last_black_valid_result == True:
|
||||
blackResult = "Valid"
|
||||
elif self.last_black_valid_result == False:
|
||||
blackResult = "Invalid"
|
||||
|
||||
if self.last_white_valid_result == True:
|
||||
whiteResult = "Valid"
|
||||
elif self.last_white_valid_result == False:
|
||||
whiteResult = "Invalid"
|
||||
|
||||
return "Blacklist: " + blackResult + ", Whitelist: " + whiteResult
|
||||
|
||||
def _add_keywords(self, table, range, values):
|
||||
def _add_keywords(self, table, values):
|
||||
myDB = db.DBConnection()
|
||||
for value in values:
|
||||
myDB.action("INSERT INTO [" + table + "] (show_id, range , keyword) VALUES (?,?,?)", [self.show_id, range, value])
|
||||
myDB.action('INSERT INTO [' + table + '] (show_id, keyword) VALUES (?,?)', [self.show_id, value])
|
||||
|
||||
self.refresh()
|
||||
def set_black_keywords(self, values):
|
||||
self._del_all_keywords('blacklist')
|
||||
self._add_keywords('blacklist', values)
|
||||
self.blacklist = values
|
||||
logger.log('Blacklist set to: %s' % self.blacklist, logger.DEBUG)
|
||||
|
||||
def _del_all_black_keywords(self):
|
||||
self._del_all_keywords(self._tableBlack)
|
||||
|
||||
def _del_all_white_keywords(self):
|
||||
self._del_all_keywords(self._tableWhite)
|
||||
|
||||
def _del_all_black_keywords_for(self, range):
|
||||
self._del_all_keywords_for(self._tableBlack, range)
|
||||
|
||||
def _del_all_white_keywords_for(self, range):
|
||||
self._del_all_keywords_for(self._tableWhite, range)
|
||||
def set_white_keywords(self, values):
|
||||
self._del_all_keywords('whitelist')
|
||||
self._add_keywords('whitelist', values)
|
||||
self.whitelist = values
|
||||
logger.log('Whitelist set to: %s' % self.whitelist, logger.DEBUG)
|
||||
|
||||
def _del_all_keywords(self, table):
|
||||
logger.log(u"Deleting all " + table + " keywords for " + str(self.show_id), logger.DEBUG)
|
||||
myDB = db.DBConnection()
|
||||
myDB.action("DELETE FROM [" + table + "] WHERE show_id = ?", [self.show_id])
|
||||
self.refresh()
|
||||
|
||||
def _del_all_keywords_for(self, table, range):
|
||||
logger.log(u"Deleting all " + range + " " + table + " keywords for " + str(self.show_id), logger.DEBUG)
|
||||
myDB = db.DBConnection()
|
||||
myDB.action("DELETE FROM [" + table + "] WHERE show_id = ? and range = ?", [self.show_id, range])
|
||||
self.refresh()
|
||||
myDB.action('DELETE FROM [' + table + '] WHERE show_id = ?', [self.show_id])
|
||||
|
||||
def _load_list(self, table):
|
||||
myDB = db.DBConnection()
|
||||
sqlResults = myDB.select("SELECT range,keyword FROM [" + table + "] WHERE show_id = ? ", [self.show_id])
|
||||
sqlResults = myDB.select('SELECT keyword FROM [' + table + '] WHERE show_id = ?', [self.show_id])
|
||||
if not sqlResults or not len(sqlResults):
|
||||
return ([], {})
|
||||
return []
|
||||
|
||||
list, dict = self._build_keyword_dict(sqlResults)
|
||||
logger.log("BWL: " + str(self.show_id) + " loaded keywords from " + table + ": " + str(dict), logger.DEBUG)
|
||||
return list, dict
|
||||
groups = []
|
||||
for result in sqlResults:
|
||||
groups.append(result['keyword'])
|
||||
|
||||
def _build_keyword_dict(self, sql_result):
|
||||
list = []
|
||||
dict = {}
|
||||
for row in sql_result:
|
||||
list.append(row["keyword"])
|
||||
if row["range"] in dict:
|
||||
dict[row["range"]].append(row["keyword"])
|
||||
else:
|
||||
dict[row["range"]] = [row["keyword"]]
|
||||
logger.log('BWL: ' + str(self.show_id) + ' loaded keywords from ' + table + ': ' + str(groups), logger.DEBUG)
|
||||
|
||||
return (list, dict)
|
||||
return groups
|
||||
|
||||
def is_valid_for_black(self, haystack):
|
||||
logger.log(u"BWL: " + str(self.show_id) + " is valid black", logger.DEBUG)
|
||||
result = self._is_valid_for(self.blackDict, False, haystack)
|
||||
self.last_black_valid_result = result
|
||||
return result
|
||||
def is_valid(self, result):
|
||||
|
||||
def is_valid_for_white(self, haystack):
|
||||
logger.log(u"BWL: " + str(self.show_id) + " is valid white", logger.DEBUG)
|
||||
result = self._is_valid_for(self.whiteDict, True, haystack)
|
||||
self.last_white_valid_result = result
|
||||
return result
|
||||
if not result.release_group:
|
||||
logger.log('Failed to detect release group, invalid result', logger.DEBUG)
|
||||
return False
|
||||
|
||||
def is_valid(self, haystack):
|
||||
return self.is_valid_for_black(haystack) and self.is_valid_for_white(haystack)
|
||||
|
||||
def _is_valid_for(self, list, mood, haystack):
|
||||
if not len(list):
|
||||
return True
|
||||
|
||||
results = []
|
||||
for range in list:
|
||||
for keyword in list[range]:
|
||||
string = None
|
||||
if range == "global":
|
||||
string = haystack.name
|
||||
elif range in haystack.__dict__:
|
||||
string = haystack.__dict__[range]
|
||||
elif not range in haystack.__dict__:
|
||||
results.append((not mood))
|
||||
else:
|
||||
results.append(False)
|
||||
|
||||
if string:
|
||||
results.append(self._is_keyword_in_string(string, keyword) == mood)
|
||||
|
||||
# black: mood = False
|
||||
# white: mood = True
|
||||
if mood in results:
|
||||
return mood
|
||||
if result.release_group.lower() in [x.lower() for x in self.whitelist] or not self.whitelist:
|
||||
white_result = True
|
||||
else:
|
||||
return (not mood)
|
||||
white_result = False
|
||||
|
||||
def _is_keyword_in_string(self, fromPost, fromBWList):
|
||||
"""
|
||||
will return true if fromBWList is found in fromPost
|
||||
for now a basic find is used
|
||||
"""
|
||||
fromPost = fromPost.lower()
|
||||
fromBWList = fromBWList.lower()
|
||||
logger.log(u"BWL: " + str(self.show_id) + " comparing fromPost: " + fromPost + " vs fromBWlist: " + fromBWList, logger.DEBUG)
|
||||
return (fromPost.find(fromBWList) >= 0)
|
||||
if result.release_group.lower() in [x.lower() for x in self.blacklist]:
|
||||
black_result = False
|
||||
else:
|
||||
black_result = True
|
||||
|
||||
class BlackWhiteKeyword(object):
|
||||
range = ""
|
||||
value = []
|
||||
logger.log('Whitelist check passed: %s. Blacklist check passed: %s' % (white_result, black_result), logger.DEBUG)
|
||||
|
||||
if white_result and black_result:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def __init__(self, range, values):
|
||||
self.range = range # "global" or a parser group
|
||||
self.value = values # a list of values may contain only one item (still a list)
|
||||
|
||||
class BlackWhitelistNoShowIDException(Exception):
|
||||
"No show_id was given"
|
||||
"""
|
||||
No show_id was given
|
||||
"""
|
||||
|
||||
|
||||
def short_group_names(groups):
|
||||
group_list = groups.split(',')
|
||||
short_group_list = []
|
||||
if helpers.set_up_anidb_connection():
|
||||
for group_name in group_list:
|
||||
adba_result = sickbeard.ADBA_CONNECTION.group(gname=group_name) # no such group is returned for utf8 groups like interrobang
|
||||
for line in adba_result.datalines:
|
||||
if line['shortname']:
|
||||
short_group_list.append(line['shortname'])
|
||||
else:
|
||||
if group_name not in short_group_list:
|
||||
short_group_list.append(group_name)
|
||||
else:
|
||||
short_group_list = group_list
|
||||
return short_group_list
|
|
@ -22,7 +22,7 @@ import string
|
|||
from tornado.httputil import HTTPHeaders
|
||||
from tornado.web import RequestHandler
|
||||
from sickbeard import encodingKludge as ek
|
||||
from sickbeard import logger
|
||||
from sickbeard import logger, webserve
|
||||
|
||||
# use the built-in if it's available (python 2.6), if not use the included library
|
||||
try:
|
||||
|
@ -107,7 +107,7 @@ def foldersAtPath(path, includeParent=False, includeFiles=False):
|
|||
return entries
|
||||
|
||||
|
||||
class WebFileBrowser(RequestHandler):
|
||||
class WebFileBrowser(webserve.MainHandler):
|
||||
def index(self, path='', includeFiles=False, *args, **kwargs):
|
||||
self.set_header("Content-Type", "application/json")
|
||||
return json.dumps(foldersAtPath(path, True, bool(int(includeFiles))))
|
||||
|
|
|
@ -36,7 +36,7 @@ class DownloadStationAPI(GenericClient):
|
|||
auth_url = self.host + 'webapi/auth.cgi?api=SYNO.API.Auth&version=2&method=login&account=' + self.username + '&passwd=' + self.password + '&session=DownloadStation&format=sid'
|
||||
|
||||
try:
|
||||
self.response = self.session.get(auth_url)
|
||||
self.response = self.session.get(auth_url, verify=False)
|
||||
self.auth = self.response.json()['data']['sid']
|
||||
except:
|
||||
return None
|
||||
|
|
|
@ -62,7 +62,7 @@ class GenericClient(object):
|
|||
logger.log(self.name + u': Connection Timeout ' + ex(e), logger.ERROR)
|
||||
return False
|
||||
except Exception, e:
|
||||
logger.log(self.name + u': Unknown exception raised when send torrent to ' + self.name + ': ' + ex(e),
|
||||
logger.log(self.name + u': Unknown exception raised when sending torrent to ' + self.name + ': ' + ex(e),
|
||||
logger.ERROR)
|
||||
return False
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import re
|
|||
import sickbeard
|
||||
from sickbeard import logger
|
||||
from sickbeard.clients.generic import GenericClient
|
||||
import urllib
|
||||
|
||||
|
||||
class uTorrentAPI(GenericClient):
|
||||
|
@ -32,8 +33,11 @@ class uTorrentAPI(GenericClient):
|
|||
|
||||
def _request(self, method='get', params={}, files=None):
|
||||
|
||||
params.update({'token': self.auth})
|
||||
return super(uTorrentAPI, self)._request(method=method, params=params, files=files)
|
||||
return super(uTorrentAPI, self)._request(
|
||||
method=method,
|
||||
params='token={0:s}&{1:s}'.format(self.auth, '&'.join(
|
||||
['%s' % urllib.urlencode(dict([[key, str(value)]])) for key, value in params.iteritems()])) if any(params) else params,
|
||||
files=files)
|
||||
|
||||
def _get_auth(self):
|
||||
|
||||
|
|
|
@ -740,3 +740,8 @@ class ConfigMigrator():
|
|||
sickbeard.EPISODE_VIEW_SORT = 'time'
|
||||
sickbeard.EPISODE_VIEW_DISPLAY_PAUSED = bool(check_setting_int(self.config_obj, 'GUI', 'coming_eps_display_paused', 0))
|
||||
sickbeard.EPISODE_VIEW_MISSED_RANGE = check_setting_int(self.config_obj, 'GUI', 'coming_eps_missed_range', 7)
|
||||
|
||||
def _migrate_v8(self):
|
||||
# removing settings from gui and making it a hidden debug option
|
||||
sickbeard.RECENTSEARCH_STARTUP = False
|
||||
sickbeard.BACKLOG_STARTUP = False
|
||||
|
|
|
@ -88,4 +88,15 @@ class AddSceneExceptionsRefresh(AddSceneExceptionsCustom):
|
|||
|
||||
def execute(self):
|
||||
self.connection.action(
|
||||
"CREATE TABLE scene_exceptions_refresh (list TEXT PRIMARY KEY, last_refreshed INTEGER)")
|
||||
"CREATE TABLE scene_exceptions_refresh (list TEXT PRIMARY KEY, last_refreshed INTEGER)")
|
||||
|
||||
|
||||
class AddNetworkConversions(AddSceneExceptionsRefresh):
|
||||
def test(self):
|
||||
return self.hasTable('network_conversions')
|
||||
|
||||
def execute(self):
|
||||
self.connection.action('CREATE TABLE network_conversions (tvdb_network TEXT PRIMARY KEY, tvrage_network TEXT,'
|
||||
' tvrage_country TEXT)')
|
||||
|
||||
self.connection.action('CREATE INDEX tvrage_idx on network_conversions (tvrage_network, tvrage_country)')
|
||||
|
|
|
@ -27,7 +27,7 @@ from sickbeard import encodingKludge as ek
|
|||
from sickbeard.name_parser.parser import NameParser, InvalidNameException, InvalidShowException
|
||||
|
||||
MIN_DB_VERSION = 9 # oldest db version we support migrating from
|
||||
MAX_DB_VERSION = 20000
|
||||
MAX_DB_VERSION = 20001
|
||||
|
||||
|
||||
class MainSanityCheck(db.DBSanityCheck):
|
||||
|
@ -483,7 +483,7 @@ class AddShowidTvdbidIndex(db.SchemaUpgrade):
|
|||
def execute(self):
|
||||
backup_database(self.checkDBVersion())
|
||||
|
||||
logger.log(u'Check for duplicate shows before adding unique index.')
|
||||
logger.log(u'Checking for duplicate shows before adding unique index.')
|
||||
MainSanityCheck(self.connection).fix_duplicate_shows('tvdb_id')
|
||||
|
||||
logger.log(u'Adding index on tvdb_id (tv_shows) and showid (tv_episodes) to speed up searches/queries.')
|
||||
|
@ -949,6 +949,16 @@ class SickGearDatabaseVersion(db.SchemaUpgrade):
|
|||
self.setDBVersion(20000)
|
||||
return self.checkDBVersion()
|
||||
|
||||
# 20000 -> 20001
|
||||
class DBIncreaseTo20001(db.SchemaUpgrade):
|
||||
def execute(self):
|
||||
backup_database(self.checkDBVersion())
|
||||
|
||||
logger.log('Bumping database version to force a backup before new database code')
|
||||
|
||||
self.setDBVersion(20001)
|
||||
return self.checkDBVersion()
|
||||
|
||||
|
||||
# 10001 -> 10000
|
||||
class RemoveDefaultEpStatusFromTvShows(db.SchemaUpgrade):
|
||||
|
|
105
sickbeard/db.py
|
@ -48,61 +48,14 @@ def dbFilename(filename="sickbeard.db", suffix=None):
|
|||
|
||||
class DBConnection(object):
|
||||
def __init__(self, filename="sickbeard.db", suffix=None, row_type=None):
|
||||
|
||||
self.filename = filename
|
||||
self.suffix = suffix
|
||||
self.row_type = row_type
|
||||
self.connection = None
|
||||
self.connection = sqlite3.connect(dbFilename(filename), 20)
|
||||
|
||||
try:
|
||||
self.reconnect()
|
||||
except Exception as e:
|
||||
logger.log(u"DB error: " + ex(e), logger.ERROR)
|
||||
raise
|
||||
|
||||
def reconnect(self):
|
||||
"""Closes the existing database connection and re-opens it."""
|
||||
self.close()
|
||||
self.connection = sqlite3.connect(dbFilename(self.filename, self.suffix), 20, check_same_thread=False)
|
||||
self.connection.isolation_level = None
|
||||
|
||||
if self.row_type == "dict":
|
||||
if row_type == "dict":
|
||||
self.connection.row_factory = self._dict_factory
|
||||
else:
|
||||
self.connection.row_factory = sqlite3.Row
|
||||
|
||||
def __del__(self):
|
||||
self.close()
|
||||
|
||||
def _cursor(self):
|
||||
"""Returns the cursor; reconnects if disconnected."""
|
||||
if self.connection is None: self.reconnect()
|
||||
return self.connection.cursor()
|
||||
|
||||
def execute(self, query, args=None, fetchall=False, fetchone=False):
|
||||
"""Executes the given query, returning the lastrowid from the query."""
|
||||
cursor = self._cursor()
|
||||
|
||||
try:
|
||||
if fetchall:
|
||||
return self._execute(cursor, query, args).fetchall()
|
||||
elif fetchone:
|
||||
return self._execute(cursor, query, args).fetchone()
|
||||
else:
|
||||
return self._execute(cursor, query, args)
|
||||
finally:
|
||||
cursor.close()
|
||||
|
||||
def _execute(self, cursor, query, args):
|
||||
try:
|
||||
if args == None:
|
||||
return cursor.execute(query)
|
||||
return cursor.execute(query, args)
|
||||
except sqlite3.OperationalError as e:
|
||||
logger.log(u"DB error: " + ex(e), logger.ERROR)
|
||||
self.close()
|
||||
raise
|
||||
|
||||
def checkDBVersion(self):
|
||||
|
||||
result = None
|
||||
|
@ -118,13 +71,11 @@ class DBConnection(object):
|
|||
else:
|
||||
return 0
|
||||
|
||||
def mass_action(self, querylist, logTransaction=False, fetchall=False):
|
||||
def mass_action(self, querylist, logTransaction=False):
|
||||
|
||||
with db_lock:
|
||||
# remove None types
|
||||
querylist = [i for i in querylist if i != None]
|
||||
|
||||
if querylist == None:
|
||||
if querylist is None:
|
||||
return
|
||||
|
||||
sqlResult = []
|
||||
|
@ -135,17 +86,15 @@ class DBConnection(object):
|
|||
for qu in querylist:
|
||||
if len(qu) == 1:
|
||||
if logTransaction:
|
||||
logger.log(qu[0], logger.DEBUG)
|
||||
sqlResult.append(self.execute(qu[0], fetchall=fetchall))
|
||||
logger.log(qu[0], logger.DB)
|
||||
sqlResult.append(self.connection.execute(qu[0]).fetchall())
|
||||
elif len(qu) > 1:
|
||||
if logTransaction:
|
||||
logger.log(qu[0] + " with args " + str(qu[1]), logger.DEBUG)
|
||||
sqlResult.append(self.execute(qu[0], qu[1], fetchall=fetchall))
|
||||
|
||||
logger.log(u"Transaction with " + str(len(querylist)) + u" queries executed", logger.DEBUG)
|
||||
|
||||
# finished
|
||||
break
|
||||
logger.log(qu[0] + " with args " + str(qu[1]), logger.DB)
|
||||
sqlResult.append(self.connection.execute(qu[0], qu[1]).fetchall())
|
||||
self.connection.commit()
|
||||
logger.log(u"Transaction with " + str(len(querylist)) + u" query's executed", logger.DEBUG)
|
||||
return sqlResult
|
||||
except sqlite3.OperationalError, e:
|
||||
sqlResult = []
|
||||
if self.connection:
|
||||
|
@ -164,15 +113,13 @@ class DBConnection(object):
|
|||
logger.log(u"Fatal error executing query: " + ex(e), logger.ERROR)
|
||||
raise
|
||||
|
||||
#time.sleep(0.02)
|
||||
|
||||
return sqlResult
|
||||
|
||||
def action(self, query, args=None, fetchall=False, fetchone=False):
|
||||
def action(self, query, args=None):
|
||||
|
||||
with db_lock:
|
||||
|
||||
if query == None:
|
||||
if query is None:
|
||||
return
|
||||
|
||||
sqlResult = None
|
||||
|
@ -180,13 +127,13 @@ class DBConnection(object):
|
|||
|
||||
while attempt < 5:
|
||||
try:
|
||||
if args == None:
|
||||
if args is None:
|
||||
logger.log(self.filename + ": " + query, logger.DB)
|
||||
sqlResult = self.connection.execute(query)
|
||||
else:
|
||||
logger.log(self.filename + ": " + query + " with args " + str(args), logger.DB)
|
||||
|
||||
sqlResult = self.execute(query, args, fetchall=fetchall, fetchone=fetchone)
|
||||
|
||||
sqlResult = self.connection.execute(query, args)
|
||||
self.connection.commit()
|
||||
# get out of the connection attempt loop since we were successful
|
||||
break
|
||||
except sqlite3.OperationalError, e:
|
||||
|
@ -201,27 +148,17 @@ class DBConnection(object):
|
|||
logger.log(u"Fatal error executing query: " + ex(e), logger.ERROR)
|
||||
raise
|
||||
|
||||
#time.sleep(0.02)
|
||||
|
||||
return sqlResult
|
||||
|
||||
def select(self, query, args=None):
|
||||
|
||||
sqlResults = self.action(query, args, fetchall=True)
|
||||
sqlResults = self.action(query, args).fetchall()
|
||||
|
||||
if sqlResults == None:
|
||||
if sqlResults is None:
|
||||
return []
|
||||
|
||||
return sqlResults
|
||||
|
||||
def selectOne(self, query, args=None):
|
||||
|
||||
sqlResults = self.action(query, args, fetchone=True)
|
||||
|
||||
if sqlResults == None:
|
||||
return []
|
||||
|
||||
return sqlResults
|
||||
|
||||
def upsert(self, tableName, valueDict, keyDict):
|
||||
|
||||
|
@ -480,9 +417,9 @@ def MigrationCode(myDB):
|
|||
41: sickbeard.mainDB.Migrate41,
|
||||
|
||||
10000: sickbeard.mainDB.SickGearDatabaseVersion,
|
||||
10001: sickbeard.mainDB.RemoveDefaultEpStatusFromTvShows
|
||||
10001: sickbeard.mainDB.RemoveDefaultEpStatusFromTvShows,
|
||||
|
||||
#20000: sickbeard.mainDB.AddCoolSickGearFeature1,
|
||||
20000: sickbeard.mainDB.DBIncreaseTo20001,
|
||||
#20001: sickbeard.mainDB.AddCoolSickGearFeature2,
|
||||
#20002: sickbeard.mainDB.AddCoolSickGearFeature3,
|
||||
}
|
||||
|
|
|
@ -416,7 +416,7 @@ def make_dirs(path):
|
|||
# Windows, create all missing folders
|
||||
if os.name == 'nt' or os.name == 'ce':
|
||||
try:
|
||||
logger.log(u"Folder " + path + " didn't exist, creating it", logger.DEBUG)
|
||||
logger.log(u"Folder " + path + " doesn't exist, creating it", logger.DEBUG)
|
||||
ek.ek(os.makedirs, path)
|
||||
except (OSError, IOError), e:
|
||||
logger.log(u"Failed creating " + path + " : " + ex(e), logger.ERROR)
|
||||
|
@ -436,7 +436,7 @@ def make_dirs(path):
|
|||
continue
|
||||
|
||||
try:
|
||||
logger.log(u"Folder " + sofar + " didn't exist, creating it", logger.DEBUG)
|
||||
logger.log(u"Folder " + sofar + " doesn't exist, creating it", logger.DEBUG)
|
||||
ek.ek(os.mkdir, sofar)
|
||||
# use normpath to remove end separator, otherwise checks permissions against itself
|
||||
chmodAsParent(ek.ek(os.path.normpath, sofar))
|
||||
|
@ -1082,22 +1082,29 @@ def validateShow(show, season=None, episode=None):
|
|||
|
||||
def set_up_anidb_connection():
|
||||
if not sickbeard.USE_ANIDB:
|
||||
logger.log(u"Usage of anidb disabled. Skiping", logger.DEBUG)
|
||||
logger.log(u'Usage of anidb disabled. Skipping', logger.DEBUG)
|
||||
return False
|
||||
|
||||
if not sickbeard.ANIDB_USERNAME and not sickbeard.ANIDB_PASSWORD:
|
||||
logger.log(u"anidb username and/or password are not set. Aborting anidb lookup.", logger.DEBUG)
|
||||
logger.log(u'anidb username and/or password are not set. Aborting anidb lookup.', logger.DEBUG)
|
||||
return False
|
||||
|
||||
if not sickbeard.ADBA_CONNECTION:
|
||||
anidb_logger = lambda x: logger.log("ANIDB: " + str(x), logger.DEBUG)
|
||||
anidb_logger = lambda x: logger.log('ANIDB: ' + str(x), logger.DEBUG)
|
||||
sickbeard.ADBA_CONNECTION = adba.Connection(keepAlive=True, log=anidb_logger)
|
||||
|
||||
if not sickbeard.ADBA_CONNECTION.authed():
|
||||
auth = False
|
||||
try:
|
||||
auth = sickbeard.ADBA_CONNECTION.authed()
|
||||
except Exception, e:
|
||||
logger.log(u'exception msg: ' + str(e))
|
||||
pass
|
||||
|
||||
if not auth:
|
||||
try:
|
||||
sickbeard.ADBA_CONNECTION.auth(sickbeard.ANIDB_USERNAME, sickbeard.ANIDB_PASSWORD)
|
||||
except Exception, e:
|
||||
logger.log(u"exception msg: " + str(e))
|
||||
logger.log(u'exception msg: ' + str(e))
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
@ -1236,6 +1243,62 @@ def _getTempDir():
|
|||
|
||||
return os.path.join(tempfile.gettempdir(), "SickGear-%s" % (uid))
|
||||
|
||||
|
||||
def proxy_setting(proxy_setting, request_url, force=False):
|
||||
"""
|
||||
Returns a list of a) proxy_setting address value or a PAC is fetched and parsed if proxy_setting
|
||||
starts with "PAC:" (case-insensitive) and b) True/False if "PAC" is found in the proxy_setting.
|
||||
|
||||
The PAC data parser is crude, javascript is not eval'd. The first "PROXY URL" found is extracted with a list
|
||||
of "url_a_part.url_remaining", "url_b_part.url_remaining", "url_n_part.url_remaining" and so on.
|
||||
Also, PAC data items are escaped for matching therefore regular expression items will not match a request_url.
|
||||
|
||||
If force is True or request_url contains a PAC parsed data item then the PAC proxy address is returned else False.
|
||||
None is returned in the event of an error fetching PAC data.
|
||||
|
||||
"""
|
||||
|
||||
# check for "PAC" usage
|
||||
match = re.search(r'^\s*PAC:\s*(.*)', proxy_setting, re.I)
|
||||
if not match:
|
||||
return proxy_setting, False
|
||||
pac_url = match.group(1)
|
||||
|
||||
# prevent a recursive test with existing proxy setting when fetching PAC url
|
||||
proxy_setting_backup = sickbeard.PROXY_SETTING
|
||||
sickbeard.PROXY_SETTING = ''
|
||||
|
||||
resp = ''
|
||||
try:
|
||||
resp = getURL(pac_url)
|
||||
except:
|
||||
pass
|
||||
sickbeard.PROXY_SETTING = proxy_setting_backup
|
||||
|
||||
if not resp:
|
||||
return None, False
|
||||
|
||||
proxy_address = None
|
||||
request_url_match = False
|
||||
parsed_url = urlparse.urlparse(request_url)
|
||||
netloc = (parsed_url.path, parsed_url.netloc)['' != parsed_url.netloc]
|
||||
for pac_data in re.finditer(r"""(?:[^'"]*['"])([^\.]+\.[^'"]*)(?:['"])""", resp, re.I):
|
||||
data = re.search(r"""PROXY\s+([^'"]+)""", pac_data.group(1), re.I)
|
||||
if data:
|
||||
if force:
|
||||
return data.group(1), True
|
||||
proxy_address = (proxy_address, data.group(1))[None is proxy_address]
|
||||
elif re.search(re.escape(pac_data.group(1)), netloc, re.I):
|
||||
request_url_match = True
|
||||
if None is not proxy_address:
|
||||
break
|
||||
|
||||
if None is proxy_address:
|
||||
return None, True
|
||||
|
||||
return (False, proxy_address)[request_url_match], True
|
||||
|
||||
|
||||
def getURL(url, post_data=None, params=None, headers=None, timeout=30, session=None, json=False):
|
||||
"""
|
||||
Returns a byte-string retrieved from the url provider.
|
||||
|
@ -1265,11 +1328,17 @@ def getURL(url, post_data=None, params=None, headers=None, timeout=30, session=N
|
|||
|
||||
# request session proxies
|
||||
if sickbeard.PROXY_SETTING:
|
||||
logger.log("Using proxy for url: " + url, logger.DEBUG)
|
||||
session.proxies = {
|
||||
"http": sickbeard.PROXY_SETTING,
|
||||
"https": sickbeard.PROXY_SETTING,
|
||||
}
|
||||
(proxy_address, pac_found) = proxy_setting(sickbeard.PROXY_SETTING, url)
|
||||
msg = '%sproxy for url: %s' % (('', 'PAC parsed ')[pac_found], url)
|
||||
if None is proxy_address:
|
||||
logger.log('Proxy error, aborted the request using %s' % msg, logger.DEBUG)
|
||||
return
|
||||
elif proxy_address:
|
||||
logger.log('Using %s' % msg, logger.DEBUG)
|
||||
session.proxies = {
|
||||
'http': proxy_address,
|
||||
'https': proxy_address
|
||||
}
|
||||
|
||||
# decide if we get or post data to server
|
||||
if post_data:
|
||||
|
@ -1300,6 +1369,7 @@ def getURL(url, post_data=None, params=None, headers=None, timeout=30, session=N
|
|||
|
||||
return resp.content
|
||||
|
||||
|
||||
def download_file(url, filename, session=None):
|
||||
# create session
|
||||
cache_dir = sickbeard.CACHE_DIR or _getTempDir()
|
||||
|
@ -1316,11 +1386,17 @@ def download_file(url, filename, session=None):
|
|||
|
||||
# request session proxies
|
||||
if sickbeard.PROXY_SETTING:
|
||||
logger.log("Using proxy for url: " + url, logger.DEBUG)
|
||||
session.proxies = {
|
||||
"http": sickbeard.PROXY_SETTING,
|
||||
"https": sickbeard.PROXY_SETTING,
|
||||
}
|
||||
(proxy_address, pac_found) = proxy_setting(sickbeard.PROXY_SETTING, url)
|
||||
msg = '%sproxy for url: %s' % (('', 'PAC parsed ')[pac_found], url)
|
||||
if None is proxy_address:
|
||||
logger.log('Proxy error, aborted the request using %s' % msg, logger.DEBUG)
|
||||
return
|
||||
elif proxy_address:
|
||||
logger.log('Using %s' % msg, logger.DEBUG)
|
||||
session.proxies = {
|
||||
'http': proxy_address,
|
||||
'https': proxy_address
|
||||
}
|
||||
|
||||
try:
|
||||
resp = session.get(url)
|
||||
|
@ -1432,6 +1508,8 @@ def get_size(start_path='.'):
|
|||
def remove_article(text=''):
|
||||
return re.sub(r'(?i)^(?:(?:A(?!\s+to)n?)|The)\s(\w)', r'\1', text)
|
||||
|
||||
def build_dict(seq, key):
|
||||
return dict((d[key], dict(d, index=index)) for (index, d) in enumerate(seq))
|
||||
|
||||
def client_host(server_host):
|
||||
'''Extracted from cherrypy libs
|
||||
|
@ -1462,7 +1540,7 @@ def wait_for_free_port(host, port):
|
|||
else:
|
||||
return
|
||||
|
||||
raise IOError("Port %r not free on %r" % (port, host))
|
||||
raise IOError("Port %r is not free on %r" % (port, host))
|
||||
|
||||
|
||||
def check_port(host, port, timeout=1.0):
|
||||
|
|
|
@ -188,11 +188,11 @@ class ImageCache:
|
|||
|
||||
# make sure the cache folder exists before we try copying to it
|
||||
if not ek.ek(os.path.isdir, self._cache_dir()):
|
||||
logger.log(u"Image cache dir didn't exist, creating it at " + str(self._cache_dir()))
|
||||
logger.log(u"Image cache directory doesn't exist, creating it at " + str(self._cache_dir()))
|
||||
ek.ek(os.makedirs, self._cache_dir())
|
||||
|
||||
if not ek.ek(os.path.isdir, self._thumbnails_dir()):
|
||||
logger.log(u"Thumbnails cache dir didn't exist, creating it at " + str(self._thumbnails_dir()))
|
||||
logger.log(u"Thumbnails cache directory doesn't exist, creating it at " + str(self._thumbnails_dir()))
|
||||
ek.ek(os.makedirs, self._thumbnails_dir())
|
||||
|
||||
logger.log(u"Copying from " + image_path + " to " + dest_path)
|
||||
|
@ -276,12 +276,12 @@ class ImageCache:
|
|||
|
||||
if cur_file_type in need_images and need_images[cur_file_type]:
|
||||
logger.log(
|
||||
u"Found an image in the show dir that doesn't exist in the cache, caching it: " + cur_file_name + ", type " + str(
|
||||
u"Found an image in the show directory that doesn't exist in the cache, caching it: " + cur_file_name + ", type " + str(
|
||||
cur_file_type), logger.DEBUG)
|
||||
self._cache_image_from_file(cur_file_name, cur_file_type, show_obj.indexerid)
|
||||
need_images[cur_file_type] = False
|
||||
except exceptions.ShowDirNotFoundException:
|
||||
logger.log(u"Unable to search for images in show dir because it doesn't exist", logger.WARNING)
|
||||
logger.log(u"Unable to search for images in show directory because it doesn't exist", logger.WARNING)
|
||||
|
||||
# download from indexer for missing ones
|
||||
for cur_image_type in [self.POSTER, self.BANNER, self.POSTER_THUMB, self.BANNER_THUMB]:
|
||||
|
|
|
@ -19,7 +19,7 @@ import os
|
|||
import sickbeard
|
||||
|
||||
from indexer_config import initConfig, indexerConfig
|
||||
|
||||
from sickbeard.helpers import proxy_setting
|
||||
|
||||
class indexerApi(object):
|
||||
def __init__(self, indexerID=None):
|
||||
|
@ -49,7 +49,9 @@ class indexerApi(object):
|
|||
if sickbeard.CACHE_DIR:
|
||||
indexerConfig[self.indexerID]['api_params']['cache'] = os.path.join(sickbeard.CACHE_DIR, 'indexers', self.name)
|
||||
if sickbeard.PROXY_SETTING and sickbeard.PROXY_INDEXERS:
|
||||
indexerConfig[self.indexerID]['api_params']['proxy'] = sickbeard.PROXY_SETTING
|
||||
(proxy_address, pac_found) = proxy_setting(sickbeard.PROXY_SETTING, indexerConfig[self.indexerID]['base_url'], force=True)
|
||||
if proxy_address:
|
||||
indexerConfig[self.indexerID]['api_params']['proxy'] = proxy_address
|
||||
|
||||
return indexerConfig[self.indexerID]['api_params']
|
||||
|
||||
|
|
|
@ -140,6 +140,7 @@ def _update_zoneinfo():
|
|||
def update_network_dict():
|
||||
_remove_old_zoneinfo()
|
||||
_update_zoneinfo()
|
||||
load_network_conversions()
|
||||
|
||||
d = {}
|
||||
|
||||
|
@ -278,3 +279,62 @@ def test_timeformat(t):
|
|||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def standardize_network(network, country):
|
||||
myDB = db.DBConnection('cache.db')
|
||||
sqlResults = myDB.select('SELECT * FROM network_conversions WHERE tvrage_network = ? and tvrage_country = ?',
|
||||
[network, country])
|
||||
if len(sqlResults) == 1:
|
||||
return sqlResults[0]['tvdb_network']
|
||||
else:
|
||||
return network
|
||||
|
||||
|
||||
def load_network_conversions():
|
||||
|
||||
conversions = []
|
||||
|
||||
# network conversions are stored on github pages
|
||||
url = 'https://raw.githubusercontent.com/prinz23/sg_network_conversions/master/conversions.txt'
|
||||
|
||||
url_data = helpers.getURL(url)
|
||||
if url_data is None:
|
||||
# When urlData is None, trouble connecting to github
|
||||
logger.log(u'Updating network conversions failed, this can happen from time to time. URL: %s' % url, logger.WARNING)
|
||||
return
|
||||
|
||||
try:
|
||||
for line in url_data.splitlines():
|
||||
(tvdb_network, tvrage_network, tvrage_country) = line.decode('utf-8').strip().rsplit(u'::', 2)
|
||||
if not (tvdb_network and tvrage_network and tvrage_country):
|
||||
continue
|
||||
conversions.append({'tvdb_network': tvdb_network, 'tvrage_network': tvrage_network, 'tvrage_country': tvrage_country})
|
||||
except (IOError, OSError):
|
||||
pass
|
||||
|
||||
my_db = db.DBConnection('cache.db')
|
||||
|
||||
old_d = my_db.select('SELECT * FROM network_conversions')
|
||||
old_d = helpers.build_dict(old_d, 'tvdb_network')
|
||||
|
||||
# list of sql commands to update the network_conversions table
|
||||
cl = []
|
||||
|
||||
for n_w in conversions:
|
||||
cl.append(['INSERT OR REPLACE INTO network_conversions (tvdb_network, tvrage_network, tvrage_country)'
|
||||
'VALUES (?,?,?)', [n_w['tvdb_network'], n_w['tvrage_network'], n_w['tvrage_country']]])
|
||||
try:
|
||||
del old_d[n_w['tvdb_network']]
|
||||
except:
|
||||
pass
|
||||
|
||||
# remove deleted records
|
||||
if len(old_d) > 0:
|
||||
old_items = list(va for va in old_d)
|
||||
cl.append(['DELETE FROM network_conversions WHERE tvdb_network'
|
||||
' IN (%s)' % ','.join(['?'] * len(old_items)), old_items])
|
||||
|
||||
# change all network conversion info at once (much faster)
|
||||
if len(cl) > 0:
|
||||
my_db.mass_action(cl)
|
|
@ -30,7 +30,6 @@ import growl
|
|||
import prowl
|
||||
from . import libnotify
|
||||
import pushover
|
||||
import boxcar
|
||||
import boxcar2
|
||||
import nma
|
||||
import pushalot
|
||||
|
@ -55,7 +54,6 @@ growl_notifier = growl.GrowlNotifier()
|
|||
prowl_notifier = prowl.ProwlNotifier()
|
||||
libnotify_notifier = libnotify.LibnotifyNotifier()
|
||||
pushover_notifier = pushover.PushoverNotifier()
|
||||
boxcar_notifier = boxcar.BoxcarNotifier()
|
||||
boxcar2_notifier = boxcar2.Boxcar2Notifier()
|
||||
nma_notifier = nma.NMA_Notifier()
|
||||
pushalot_notifier = pushalot.PushalotNotifier()
|
||||
|
@ -77,7 +75,6 @@ notifiers = [
|
|||
growl_notifier,
|
||||
prowl_notifier,
|
||||
pushover_notifier,
|
||||
boxcar_notifier,
|
||||
boxcar2_notifier,
|
||||
nma_notifier,
|
||||
pushalot_notifier,
|
||||
|
|
|
@ -1,155 +0,0 @@
|
|||
# Author: Marvin Pinto <me@marvinp.ca>
|
||||
# Author: Dennis Lutter <lad1337@gmail.com>
|
||||
# URL: http://code.google.com/p/sickbeard/
|
||||
#
|
||||
# This file is part of SickGear.
|
||||
#
|
||||
# SickGear is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# SickGear is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import urllib, urllib2
|
||||
import time
|
||||
|
||||
import sickbeard
|
||||
|
||||
from sickbeard import logger
|
||||
from sickbeard.common import notifyStrings, NOTIFY_SNATCH, NOTIFY_DOWNLOAD, NOTIFY_SUBTITLE_DOWNLOAD, NOTIFY_GIT_UPDATE, NOTIFY_GIT_UPDATE_TEXT
|
||||
from sickbeard.exceptions import ex
|
||||
|
||||
API_URL = "https://boxcar.io/devices/providers/fWc4sgSmpcN6JujtBmR6/notifications"
|
||||
|
||||
|
||||
class BoxcarNotifier:
|
||||
def test_notify(self, boxcar_username):
|
||||
return self._notify("This is a test notification from Sick Beard", "Test", boxcar_username, force=True)
|
||||
|
||||
def _sendBoxcar(self, msg, title, email, subscribe=False):
|
||||
"""
|
||||
Sends a boxcar notification to the address provided
|
||||
|
||||
msg: The message to send (unicode)
|
||||
title: The title of the message
|
||||
email: The email address to send the message to (or to subscribe with)
|
||||
subscribe: If true then instead of sending a message this function will send a subscription notification (optional, default is False)
|
||||
|
||||
returns: True if the message succeeded, False otherwise
|
||||
"""
|
||||
|
||||
# build up the URL and parameters
|
||||
msg = msg.strip()
|
||||
curUrl = API_URL
|
||||
|
||||
# if this is a subscription notification then act accordingly
|
||||
if subscribe:
|
||||
data = urllib.urlencode({'email': email})
|
||||
curUrl = curUrl + "/subscribe"
|
||||
|
||||
# for normal requests we need all these parameters
|
||||
else:
|
||||
data = urllib.urlencode({
|
||||
'email': email,
|
||||
'notification[from_screen_name]': title,
|
||||
'notification[message]': msg.encode('utf-8'),
|
||||
'notification[from_remote_service_id]': int(time.time())
|
||||
})
|
||||
|
||||
|
||||
# send the request to boxcar
|
||||
try:
|
||||
req = urllib2.Request(curUrl)
|
||||
handle = urllib2.urlopen(req, data)
|
||||
handle.close()
|
||||
|
||||
except urllib2.HTTPError, e:
|
||||
# if we get an error back that doesn't have an error code then who knows what's really happening
|
||||
if not hasattr(e, 'code'):
|
||||
logger.log("Boxcar notification failed. Error code: " + ex(e), logger.ERROR)
|
||||
return False
|
||||
else:
|
||||
logger.log("Boxcar notification failed. Error code: " + str(e.code), logger.WARNING)
|
||||
|
||||
# HTTP status 404 if the provided email address isn't a Boxcar user.
|
||||
if e.code == 404:
|
||||
logger.log("Username is wrong/not a boxcar email. Boxcar will send an email to it", logger.WARNING)
|
||||
return False
|
||||
|
||||
# For HTTP status code 401's, it is because you are passing in either an invalid token, or the user has not added your service.
|
||||
elif e.code == 401:
|
||||
|
||||
# If the user has already added your service, we'll return an HTTP status code of 401.
|
||||
if subscribe:
|
||||
logger.log("Already subscribed to service", logger.ERROR)
|
||||
# i dont know if this is true or false ... its neither but i also dont know how we got here in the first place
|
||||
return False
|
||||
|
||||
#HTTP status 401 if the user doesn't have the service added
|
||||
else:
|
||||
subscribeNote = self._sendBoxcar(msg, title, email, True)
|
||||
if subscribeNote:
|
||||
logger.log("Subscription send", logger.DEBUG)
|
||||
return True
|
||||
else:
|
||||
logger.log("Subscription could not be send", logger.ERROR)
|
||||
return False
|
||||
|
||||
# If you receive an HTTP status code of 400, it is because you failed to send the proper parameters
|
||||
elif e.code == 400:
|
||||
logger.log("Wrong data sent to boxcar", logger.ERROR)
|
||||
return False
|
||||
|
||||
logger.log("Boxcar notification successful.", logger.MESSAGE)
|
||||
return True
|
||||
|
||||
def notify_snatch(self, ep_name, title=notifyStrings[NOTIFY_SNATCH]):
|
||||
if sickbeard.BOXCAR_NOTIFY_ONSNATCH:
|
||||
self._notifyBoxcar(title, ep_name)
|
||||
|
||||
|
||||
def notify_download(self, ep_name, title=notifyStrings[NOTIFY_DOWNLOAD]):
|
||||
if sickbeard.BOXCAR_NOTIFY_ONDOWNLOAD:
|
||||
self._notifyBoxcar(title, ep_name)
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang, title=notifyStrings[NOTIFY_SUBTITLE_DOWNLOAD]):
|
||||
if sickbeard.BOXCAR_NOTIFY_ONSUBTITLEDOWNLOAD:
|
||||
self._notifyBoxcar(title, ep_name + ": " + lang)
|
||||
|
||||
def notify_git_update(self, new_version = "??"):
|
||||
if sickbeard.USE_BOXCAR:
|
||||
update_text=notifyStrings[NOTIFY_GIT_UPDATE_TEXT]
|
||||
title=notifyStrings[NOTIFY_GIT_UPDATE]
|
||||
self._notifyBoxcar(title, update_text + new_version)
|
||||
|
||||
def _notifyBoxcar(self, title, message, username=None, force=False):
|
||||
"""
|
||||
Sends a boxcar notification based on the provided info or SB config
|
||||
|
||||
title: The title of the notification to send
|
||||
message: The message string to send
|
||||
username: The username to send the notification to (optional, defaults to the username in the config)
|
||||
force: If True then the notification will be sent even if Boxcar is disabled in the config
|
||||
"""
|
||||
|
||||
if not sickbeard.USE_BOXCAR and not force:
|
||||
logger.log("Notification for Boxcar not enabled, skipping this notification", logger.DEBUG)
|
||||
return False
|
||||
|
||||
# if no username was given then use the one from the config
|
||||
if not username:
|
||||
username = sickbeard.BOXCAR_USERNAME
|
||||
|
||||
logger.log("Sending notification for " + message, logger.DEBUG)
|
||||
|
||||
return self._sendBoxcar(message, title, username)
|
||||
|
||||
|
||||
notifier = BoxcarNotifier
|
|
@ -18,7 +18,8 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import urllib, urllib2
|
||||
import urllib
|
||||
import urllib2
|
||||
import time
|
||||
|
||||
import sickbeard
|
||||
|
@ -27,103 +28,105 @@ from sickbeard import logger
|
|||
from sickbeard.common import notifyStrings, NOTIFY_SNATCH, NOTIFY_DOWNLOAD, NOTIFY_SUBTITLE_DOWNLOAD, NOTIFY_GIT_UPDATE, NOTIFY_GIT_UPDATE_TEXT
|
||||
from sickbeard.exceptions import ex
|
||||
|
||||
API_URL = "https://new.boxcar.io/api/notifications"
|
||||
API_URL = 'https://new.boxcar.io/api/notifications'
|
||||
|
||||
|
||||
class Boxcar2Notifier:
|
||||
def test_notify(self, accesstoken, title="SickGear : Test"):
|
||||
return self._sendBoxcar2("This is a test notification from SickGear", title, accesstoken)
|
||||
|
||||
def _sendBoxcar2(self, msg, title, accesstoken):
|
||||
def _sendBoxcar2(self, title, msg, accesstoken, sound):
|
||||
"""
|
||||
Sends a boxcar2 notification to the address provided
|
||||
|
||||
msg: The message to send
|
||||
title: The title of the message
|
||||
accesstoken: to send to this device
|
||||
|
||||
|
||||
returns: True if the message succeeded, False otherwise
|
||||
"""
|
||||
|
||||
# build up the URL and parameters
|
||||
#more info goes here - https://boxcar.uservoice.com/knowledgebase/articles/306788-how-to-send-your-boxcar-account-a-notification
|
||||
msg = msg.strip()
|
||||
curUrl = API_URL
|
||||
# more info goes here - https://boxcar.uservoice.com/knowledgebase/articles/306788-how-to-send-your-boxcar-account-a-notification
|
||||
msg = msg.strip().encode('utf-8')
|
||||
|
||||
data = urllib.urlencode({
|
||||
'user_credentials': accesstoken,
|
||||
'notification[title]': "SickGear : " + title + ' : ' + msg,
|
||||
'notification[title]': title + ' - ' + msg,
|
||||
'notification[long_message]': msg,
|
||||
'notification[sound]': "notifier-2"
|
||||
'notification[sound]': sound,
|
||||
'notification[source_name]': 'SickGear',
|
||||
'notification[icon_url]': 'https://cdn.rawgit.com/SickGear/SickGear/master/gui/slick/images/ico/apple-touch-icon-60x60.png'
|
||||
})
|
||||
|
||||
# send the request to boxcar2
|
||||
try:
|
||||
req = urllib2.Request(curUrl)
|
||||
req = urllib2.Request(API_URL)
|
||||
handle = urllib2.urlopen(req, data)
|
||||
handle.close()
|
||||
|
||||
except urllib2.HTTPError, e:
|
||||
except urllib2.URLError, e:
|
||||
# if we get an error back that doesn't have an error code then who knows what's really happening
|
||||
if not hasattr(e, 'code'):
|
||||
logger.log("Boxcar2 notification failed." + ex(e), logger.ERROR)
|
||||
return False
|
||||
logger.log(u'BOXCAR2: Notification failed.' + ex(e), logger.ERROR)
|
||||
else:
|
||||
logger.log("Boxcar2 notification failed. Error code: " + str(e.code), logger.WARNING)
|
||||
logger.log(u'BOXCAR2: Notification failed. Error code: ' + str(e.code), logger.ERROR)
|
||||
|
||||
# HTTP status 404
|
||||
if e.code == 404:
|
||||
logger.log("Access token is invalid. Check it.", logger.WARNING)
|
||||
return False
|
||||
|
||||
# If you receive an HTTP status code of 400, it is because you failed to send the proper parameters
|
||||
logger.log(u'BOXCAR2: Access token is wrong/not associated to a device.', logger.ERROR)
|
||||
elif e.code == 401:
|
||||
logger.log(u'BOXCAR2: Access token not recognized.', logger.ERROR)
|
||||
elif e.code == 400:
|
||||
logger.log("Wrong data send to boxcar2", logger.ERROR)
|
||||
return False
|
||||
logger.log(u'BOXCAR2: Wrong data sent to boxcar.', logger.ERROR)
|
||||
elif e.code == 503:
|
||||
logger.log(u'BOXCAR2: Boxcar server to busy to handle the request at this time.', logger.WARNING)
|
||||
return False
|
||||
|
||||
logger.log("Boxcar2 notification successful.", logger.DEBUG)
|
||||
logger.log(u'BOXCAR2: Notification successful.', logger.MESSAGE)
|
||||
return True
|
||||
|
||||
def notify_snatch(self, ep_name, title=notifyStrings[NOTIFY_SNATCH]):
|
||||
def _notifyBoxcar2(self, title, message, accesstoken=None, sound=None, force=False):
|
||||
"""
|
||||
Sends a boxcar2 notification based on the provided info or SG config
|
||||
|
||||
title: The title of the notification to send
|
||||
message: The message string to send
|
||||
accesstoken: to send to this device
|
||||
force: If True then the notification will be sent even if Boxcar is disabled in the config
|
||||
"""
|
||||
|
||||
# suppress notifications if the notifier is disabled but the notify options are checked
|
||||
if not sickbeard.USE_BOXCAR2 and not force:
|
||||
logger.log(u'BOXCAR2: Notifications are not enabled, skipping this notification', logger.DEBUG)
|
||||
return False
|
||||
|
||||
# fill in omitted parameters
|
||||
if not accesstoken:
|
||||
accesstoken = sickbeard.BOXCAR2_ACCESSTOKEN
|
||||
if not sound:
|
||||
sound = sickbeard.BOXCAR2_SOUND
|
||||
|
||||
logger.log(u'BOXCAR2: Sending notification for ' + message, logger.DEBUG)
|
||||
|
||||
self._sendBoxcar2(title, message, accesstoken, sound)
|
||||
return True
|
||||
|
||||
def test_notify(self, accesstoken, sound, force=True):
|
||||
return self._sendBoxcar2('Test', 'This is a test notification from SickGear', accesstoken, sound)
|
||||
|
||||
def notify_snatch(self, ep_name):
|
||||
if sickbeard.BOXCAR2_NOTIFY_ONSNATCH:
|
||||
self._notifyBoxcar2(title, ep_name)
|
||||
self._notifyBoxcar2(notifyStrings[NOTIFY_SNATCH], ep_name)
|
||||
|
||||
|
||||
def notify_download(self, ep_name, title=notifyStrings[NOTIFY_DOWNLOAD]):
|
||||
def notify_download(self, ep_name):
|
||||
if sickbeard.BOXCAR2_NOTIFY_ONDOWNLOAD:
|
||||
self._notifyBoxcar2(title, ep_name)
|
||||
self._notifyBoxcar2(notifyStrings[NOTIFY_DOWNLOAD], ep_name)
|
||||
|
||||
def notify_subtitle_download(self, ep_name, lang, title=notifyStrings[NOTIFY_SUBTITLE_DOWNLOAD]):
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
if sickbeard.BOXCAR2_NOTIFY_ONSUBTITLEDOWNLOAD:
|
||||
self._notifyBoxcar2(title, ep_name + ": " + lang)
|
||||
self._notifyBoxcar2(notifyStrings[NOTIFY_SUBTITLE_DOWNLOAD], ep_name + ': ' + lang)
|
||||
|
||||
def notify_git_update(self, new_version = "??"):
|
||||
def notify_git_update(self, new_version = '??'):
|
||||
if sickbeard.USE_BOXCAR2:
|
||||
update_text=notifyStrings[NOTIFY_GIT_UPDATE_TEXT]
|
||||
title=notifyStrings[NOTIFY_GIT_UPDATE]
|
||||
self._notifyBoxcar2(title, update_text + new_version)
|
||||
|
||||
def _notifyBoxcar2(self, title, message, accesstoken=None):
|
||||
"""
|
||||
Sends a boxcar2 notification based on the provided info or SB config
|
||||
|
||||
title: The title of the notification to send
|
||||
message: The message string to send
|
||||
accesstoken: to send to this device
|
||||
"""
|
||||
|
||||
if not sickbeard.USE_BOXCAR2:
|
||||
logger.log("Notification for Boxcar2 not enabled, skipping this notification", logger.DEBUG)
|
||||
return False
|
||||
|
||||
# if no username was given then use the one from the config
|
||||
if not accesstoken:
|
||||
accesstoken = sickbeard.BOXCAR2_ACCESSTOKEN
|
||||
|
||||
logger.log("Sending notification for " + message, logger.DEBUG)
|
||||
|
||||
self._sendBoxcar2(message, title, accesstoken)
|
||||
return True
|
||||
|
||||
|
||||
notifier = Boxcar2Notifier
|
||||
|
|
|
@ -56,7 +56,7 @@ class EmailNotifier:
|
|||
show = self._parseEp(ep_name)
|
||||
to = self._generate_recepients(show)
|
||||
if len(to) == 0:
|
||||
logger.log('Skipping email notify because there are no configured recepients', logger.WARNING)
|
||||
logger.log('Skipping email notification because there are no configured recepients', logger.WARNING)
|
||||
else:
|
||||
try:
|
||||
msg = MIMEMultipart('alternative')
|
||||
|
@ -91,7 +91,7 @@ class EmailNotifier:
|
|||
show = self._parseEp(ep_name)
|
||||
to = self._generate_recepients(show)
|
||||
if len(to) == 0:
|
||||
logger.log('Skipping email notify because there are no configured recepients', logger.WARNING)
|
||||
logger.log('Skipping email notification because there are no configured recepients', logger.WARNING)
|
||||
else:
|
||||
try:
|
||||
msg = MIMEMultipart('alternative')
|
||||
|
@ -126,7 +126,7 @@ class EmailNotifier:
|
|||
show = self._parseEp(ep_name)
|
||||
to = self._generate_recepients(show)
|
||||
if len(to) == 0:
|
||||
logger.log('Skipping email notify because there are no configured recepients', logger.WARNING)
|
||||
logger.log('Skipping email notification because there are no configured recepients', logger.WARNING)
|
||||
else:
|
||||
try:
|
||||
msg = MIMEMultipart('alternative')
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
import urllib
|
||||
import urllib2
|
||||
import base64
|
||||
import re
|
||||
|
||||
import sickbeard
|
||||
|
||||
|
@ -56,7 +57,7 @@ class PLEXNotifier:
|
|||
password = sickbeard.PLEX_PASSWORD
|
||||
|
||||
if not host:
|
||||
logger.log(u"PLEX: No host specified, check your settings", logger.ERROR)
|
||||
logger.log(u'PLEX: No host specified, check your settings', logger.ERROR)
|
||||
return False
|
||||
|
||||
for key in command:
|
||||
|
@ -64,7 +65,7 @@ class PLEXNotifier:
|
|||
command[key] = command[key].encode('utf-8')
|
||||
|
||||
enc_command = urllib.urlencode(command)
|
||||
logger.log(u"PLEX: Encoded API command: " + enc_command, logger.DEBUG)
|
||||
logger.log(u'PLEX: Encoded API command: ' + enc_command, logger.DEBUG)
|
||||
|
||||
url = 'http://%s/xbmcCmds/xbmcHttp/?%s' % (host, enc_command)
|
||||
try:
|
||||
|
@ -72,26 +73,26 @@ class PLEXNotifier:
|
|||
# if we have a password, use authentication
|
||||
if password:
|
||||
base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
|
||||
authheader = "Basic %s" % base64string
|
||||
req.add_header("Authorization", authheader)
|
||||
logger.log(u"PLEX: Contacting (with auth header) via url: " + url, logger.DEBUG)
|
||||
authheader = 'Basic %s' % base64string
|
||||
req.add_header('Authorization', authheader)
|
||||
logger.log(u'PLEX: Contacting (with auth header) via url: ' + url, logger.DEBUG)
|
||||
else:
|
||||
logger.log(u"PLEX: Contacting via url: " + url, logger.DEBUG)
|
||||
logger.log(u'PLEX: Contacting via url: ' + url, logger.DEBUG)
|
||||
|
||||
response = urllib2.urlopen(req)
|
||||
|
||||
result = response.read().decode(sickbeard.SYS_ENCODING)
|
||||
response.close()
|
||||
|
||||
logger.log(u"PLEX: HTTP response: " + result.replace('\n', ''), logger.DEBUG)
|
||||
logger.log(u'PLEX: HTTP response: ' + result.replace('\n', ''), logger.DEBUG)
|
||||
# could return result response = re.compile('<html><li>(.+\w)</html>').findall(result)
|
||||
return 'OK'
|
||||
|
||||
except (urllib2.URLError, IOError), e:
|
||||
logger.log(u"PLEX: Warning: Couldn't contact Plex at " + fixStupidEncodings(url) + " " + ex(e), logger.WARNING)
|
||||
logger.log(u'PLEX: Warning: Couldn\'t contact Plex at ' + fixStupidEncodings(url) + ' ' + ex(e), logger.WARNING)
|
||||
return False
|
||||
|
||||
def _notify(self, message, title="SickGear", host=None, username=None, password=None, force=False):
|
||||
def _notify_pmc(self, message, title='SickGear', host=None, username=None, password=None, force=False):
|
||||
"""Internal wrapper for the notify_snatch and notify_download functions
|
||||
|
||||
Args:
|
||||
|
@ -121,13 +122,13 @@ class PLEXNotifier:
|
|||
password = sickbeard.PLEX_PASSWORD
|
||||
|
||||
result = ''
|
||||
for curHost in [x.strip() for x in host.split(",")]:
|
||||
logger.log(u"PLEX: Sending notification to '" + curHost + "' - " + message, logger.MESSAGE)
|
||||
for curHost in [x.strip() for x in host.split(',')]:
|
||||
logger.log(u'PLEX: Sending notification to \'%s\' - %s' % (curHost, message), logger.MESSAGE)
|
||||
|
||||
command = {'command': 'ExecBuiltIn', 'parameter': 'Notification(' + title.encode("utf-8") + ',' + message.encode("utf-8") + ')'}
|
||||
notifyResult = self._send_to_plex(command, curHost, username, password)
|
||||
if notifyResult:
|
||||
result += curHost + ':' + str(notifyResult)
|
||||
command = {'command': 'ExecBuiltIn', 'parameter': 'Notification(%s,%s)' % (title.encode('utf-8'), message.encode('utf-8'))}
|
||||
notify_result = self._send_to_plex(command, curHost, username, password)
|
||||
if notify_result:
|
||||
result += '%s:%s' % (curHost, str(notify_result))
|
||||
|
||||
return result
|
||||
|
||||
|
@ -145,89 +146,124 @@ class PLEXNotifier:
|
|||
|
||||
def notify_subtitle_download(self, ep_name, lang):
|
||||
if sickbeard.PLEX_NOTIFY_ONSUBTITLEDOWNLOAD:
|
||||
self._notify_pmc(ep_name + ": " + lang, common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD])
|
||||
self._notify_pmc(ep_name + ': ' + lang, common.notifyStrings[common.NOTIFY_SUBTITLE_DOWNLOAD])
|
||||
|
||||
def notify_git_update(self, new_version="??"):
|
||||
def notify_git_update(self, new_version='??'):
|
||||
if sickbeard.USE_PLEX:
|
||||
update_text = common.notifyStrings[common.NOTIFY_GIT_UPDATE_TEXT]
|
||||
title = common.notifyStrings[common.NOTIFY_GIT_UPDATE]
|
||||
self._notify_pmc(update_text + new_version, title)
|
||||
|
||||
def test_notify(self, host, username, password):
|
||||
return self._notify("This is a test notification from SickGear", "Test", host, username, password, force=True)
|
||||
def test_notify_pmc(self, host, username, password):
|
||||
return self._notify_pmc('This is a test notification from SickGear', 'Test', host, username, password, force=True)
|
||||
|
||||
def update_library(self, ep_obj=None, host=None, username=None, password=None):
|
||||
def test_notify_pms(self, host, username, password):
|
||||
return self.update_library(host=host, username=username, password=password, force=False)
|
||||
|
||||
def update_library(self, ep_obj=None, host=None, username=None, password=None, force=True):
|
||||
"""Handles updating the Plex Media Server host via HTTP API
|
||||
|
||||
Plex Media Server currently only supports updating the whole video library and not a specific path.
|
||||
|
||||
Returns:
|
||||
Returns True or False
|
||||
Returns None for no issue, else a string of host with connection issues
|
||||
|
||||
"""
|
||||
|
||||
# fill in omitted parameters
|
||||
if not host:
|
||||
host = sickbeard.PLEX_SERVER_HOST
|
||||
if not username:
|
||||
username = sickbeard.PLEX_USERNAME
|
||||
if not password:
|
||||
password = sickbeard.PLEX_PASSWORD
|
||||
|
||||
if sickbeard.USE_PLEX and sickbeard.PLEX_UPDATE_LIBRARY:
|
||||
|
||||
if not sickbeard.PLEX_SERVER_HOST:
|
||||
logger.log(u"PLEX: No Plex Media Server host specified, check your settings", logger.DEBUG)
|
||||
logger.log(u'PLEX: No Plex Media Server host specified, check your settings', logger.DEBUG)
|
||||
return False
|
||||
|
||||
logger.log(u"PLEX: Updating library for the Plex Media Server host: " + host, logger.MESSAGE)
|
||||
if not host:
|
||||
host = sickbeard.PLEX_SERVER_HOST
|
||||
if not username:
|
||||
username = sickbeard.PLEX_USERNAME
|
||||
if not password:
|
||||
password = sickbeard.PLEX_PASSWORD
|
||||
|
||||
# if username and password were provided, fetch the auth token from plex.tv
|
||||
token_arg = ""
|
||||
token_arg = ''
|
||||
if username and password:
|
||||
|
||||
logger.log(u"PLEX: fetching credentials for Plex user: " + username, logger.DEBUG)
|
||||
req = urllib2.Request("https://plex.tv/users/sign_in.xml", data="")
|
||||
base64string = base64.encodestring('%s:%s' % (username, password))[:-1]
|
||||
authheader = "Basic %s" % base64string
|
||||
req.add_header("Authorization", authheader)
|
||||
req.add_header("X-Plex-Client-Identifier", "Sick-Beard-Notifier")
|
||||
|
||||
logger.log(u'PLEX: fetching plex.tv credentials for user: ' + username, logger.DEBUG)
|
||||
req = urllib2.Request('https://plex.tv/users/sign_in.xml', data='')
|
||||
authheader = 'Basic %s' % base64.encodestring('%s:%s' % (username, password))[:-1]
|
||||
req.add_header('Authorization', authheader)
|
||||
req.add_header('X-Plex-Device-Name', 'SickGear')
|
||||
req.add_header('X-Plex-Product', 'SickGear Notifier')
|
||||
req.add_header('X-Plex-Client-Identifier', '5f48c063eaf379a565ff56c9bb2b401e')
|
||||
req.add_header('X-Plex-Version', '1.0')
|
||||
|
||||
try:
|
||||
response = urllib2.urlopen(req)
|
||||
except urllib2.URLError, e:
|
||||
logger.log(u"PLEX: Error fetching credentials from from plex.tv for user %s: %s" % (username, ex(e)), logger.MESSAGE)
|
||||
return False
|
||||
|
||||
try:
|
||||
auth_tree = etree.parse(response)
|
||||
token = auth_tree.findall(".//authentication-token")[0].text
|
||||
token_arg = "?X-Plex-Token=" + token
|
||||
token = auth_tree.findall('.//authentication-token')[0].text
|
||||
token_arg = '?X-Plex-Token=' + token
|
||||
|
||||
except urllib2.URLError as e:
|
||||
logger.log(u'PLEX: Error fetching credentials from from plex.tv for user %s: %s' % (username, ex(e)), logger.MESSAGE)
|
||||
|
||||
except (ValueError, IndexError) as e:
|
||||
logger.log(u"PLEX: Error parsing plex.tv response: " + ex(e), logger.MESSAGE)
|
||||
return False
|
||||
logger.log(u'PLEX: Error parsing plex.tv response: ' + ex(e), logger.MESSAGE)
|
||||
|
||||
url = "http://%s/library/sections%s" % (sickbeard.PLEX_SERVER_HOST, token_arg)
|
||||
try:
|
||||
xml_tree = etree.parse(urllib.urlopen(url))
|
||||
media_container = xml_tree.getroot()
|
||||
except IOError, e:
|
||||
logger.log(u"PLEX: Error while trying to contact Plex Media Server: " + ex(e), logger.ERROR)
|
||||
return False
|
||||
file_location = '' if None is ep_obj else ep_obj.location
|
||||
host_list = [x.strip() for x in host.split(',')]
|
||||
hosts_all = {}
|
||||
hosts_match = {}
|
||||
hosts_failed = []
|
||||
for cur_host in host_list:
|
||||
|
||||
sections = media_container.findall('.//Directory')
|
||||
if not sections:
|
||||
logger.log(u"PLEX: Plex Media Server not running on: " + sickbeard.PLEX_SERVER_HOST, logger.MESSAGE)
|
||||
return False
|
||||
url = 'http://%s/library/sections%s' % (cur_host, token_arg)
|
||||
try:
|
||||
xml_tree = etree.parse(urllib.urlopen(url))
|
||||
media_container = xml_tree.getroot()
|
||||
except IOError, e:
|
||||
logger.log(u'PLEX: Error while trying to contact Plex Media Server: ' + ex(e), logger.ERROR)
|
||||
hosts_failed.append(cur_host)
|
||||
continue
|
||||
|
||||
for section in sections:
|
||||
if section.attrib['type'] == "show":
|
||||
url = "http://%s/library/sections/%s/refresh%s" % (sickbeard.PLEX_SERVER_HOST, section.attrib['key'], token_arg)
|
||||
try:
|
||||
urllib.urlopen(url)
|
||||
except Exception, e:
|
||||
logger.log(u"PLEX: Error updating library section for Plex Media Server: " + ex(e), logger.ERROR)
|
||||
return False
|
||||
sections = media_container.findall('.//Directory')
|
||||
if not sections:
|
||||
logger.log(u'PLEX: Plex Media Server not running on: ' + cur_host, logger.MESSAGE)
|
||||
hosts_failed.append(cur_host)
|
||||
continue
|
||||
|
||||
return True
|
||||
for section in sections:
|
||||
if 'show' == section.attrib['type']:
|
||||
|
||||
keyed_host = [(str(section.attrib['key']), cur_host)]
|
||||
hosts_all.update(keyed_host)
|
||||
if not file_location:
|
||||
continue
|
||||
|
||||
for section_location in section.findall('.//Location'):
|
||||
section_path = re.sub(r'[/\\]+', '/', section_location.attrib['path'].lower())
|
||||
section_path = re.sub(r'^(.{,2})[/\\]', '', section_path)
|
||||
location_path = re.sub(r'[/\\]+', '/', file_location.lower())
|
||||
location_path = re.sub(r'^(.{,2})[/\\]', '', location_path)
|
||||
|
||||
if section_path in location_path:
|
||||
hosts_match.update(keyed_host)
|
||||
|
||||
hosts_try = (hosts_all.copy(), hosts_match.copy())[len(hosts_match)]
|
||||
host_list = []
|
||||
for section_key, cur_host in hosts_try.items():
|
||||
|
||||
url = 'http://%s/library/sections/%s/refresh%s' % (cur_host, section_key, token_arg)
|
||||
try:
|
||||
force and urllib.urlopen(url)
|
||||
host_list.append(cur_host)
|
||||
except Exception, e:
|
||||
logger.log(u'PLEX: Error updating library section for Plex Media Server: ' + ex(e), logger.ERROR)
|
||||
hosts_failed.append(cur_host)
|
||||
|
||||
if len(hosts_match):
|
||||
logger.log(u'PLEX: Updating hosts where TV section paths match the downloaded show: ' + ', '.join(set(host_list)), logger.MESSAGE)
|
||||
else:
|
||||
logger.log(u'PLEX: Updating all hosts with TV sections: ' + ', '.join(set(host_list)), logger.MESSAGE)
|
||||
|
||||
return (', '.join(set(hosts_failed)), None)[not len(hosts_failed)]
|
||||
|
||||
notifier = PLEXNotifier
|
||||
|
|
|
@ -98,7 +98,7 @@ class ProwlNotifier:
|
|||
logger.log(u"Prowl notifications sent.", logger.MESSAGE)
|
||||
return True
|
||||
elif request_status == 401:
|
||||
logger.log(u"Prowl auth failed: %s" % response.reason, logger.ERROR)
|
||||
logger.log(u"Prowl authentication failed: %s" % response.reason, logger.ERROR)
|
||||
return False
|
||||
else:
|
||||
logger.log(u"Prowl notification failed.", logger.ERROR)
|
||||
|
|
|
@ -88,7 +88,7 @@ class PushalotNotifier:
|
|||
logger.log(u"Pushalot notifications sent.", logger.DEBUG)
|
||||
return True
|
||||
elif request_status == 410:
|
||||
logger.log(u"Pushalot auth failed: %s" % response.reason, logger.ERROR)
|
||||
logger.log(u"Pushalot authentication failed: %s" % response.reason, logger.ERROR)
|
||||
return False
|
||||
else:
|
||||
logger.log(u"Pushalot notification failed.", logger.ERROR)
|
||||
|
|
|
@ -114,7 +114,7 @@ class PushbulletNotifier:
|
|||
logger.log(u"Pushbullet notifications sent.", logger.DEBUG)
|
||||
return True
|
||||
elif request_status == 410:
|
||||
logger.log(u"Pushbullet auth failed: %s" % response.reason, logger.ERROR)
|
||||
logger.log(u"Pushbullet authentication failed: %s" % response.reason, logger.ERROR)
|
||||
return False
|
||||
else:
|
||||
logger.log(u"Pushbullet notification failed.", logger.ERROR)
|
||||
|
|
|
@ -52,10 +52,10 @@ def sendNZB(nzb, proper=False):
|
|||
|
||||
nzbGetRPC = xmlrpclib.ServerProxy(url)
|
||||
try:
|
||||
if nzbGetRPC.writelog("INFO", "SickGear connected to drop of %s any moment now." % (nzb.name + ".nzb")):
|
||||
logger.log(u"Successful connected to NZBget", logger.DEBUG)
|
||||
if nzbGetRPC.writelog("INFO", "SickGear connected to drop off %s any moment now." % (nzb.name + ".nzb")):
|
||||
logger.log(u"Successfully connected to NZBget", logger.DEBUG)
|
||||
else:
|
||||
logger.log(u"Successful connected to NZBget, but unable to send a message", logger.ERROR)
|
||||
logger.log(u"Successfully connected to NZBget, but unable to send a message", logger.ERROR)
|
||||
|
||||
except httplib.socket.error:
|
||||
logger.log(
|
||||
|
@ -94,8 +94,8 @@ def sendNZB(nzb, proper=False):
|
|||
data = nzb.extraInfo[0]
|
||||
nzbcontent64 = standard_b64encode(data)
|
||||
|
||||
logger.log(u"Sending NZB to NZBget")
|
||||
logger.log(u"URL: " + url, logger.DEBUG)
|
||||
logger.log(u"Sending NZB to NZBGet: %s" % nzb.name)
|
||||
logger.log(u"NZBGet URL: " + url, logger.DEBUG)
|
||||
|
||||
try:
|
||||
# Find out if nzbget supports priority (Version 9.0+), old versions beginning with a 0.x will use the old command
|
||||
|
|
|
@ -1013,7 +1013,7 @@ class PostProcessor(object):
|
|||
notifiers.xbmc_notifier.update_library(ep_obj.show.name)
|
||||
|
||||
# do the library update for Plex
|
||||
notifiers.plex_notifier.update_library()
|
||||
notifiers.plex_notifier.update_library(ep_obj)
|
||||
|
||||
# do the library update for NMJ
|
||||
# nmj_notifier kicks off its library update when the notify_download is issued (inside notifiers)
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
__all__ = ['ezrss',
|
||||
'tvtorrents',
|
||||
'womble',
|
||||
'btn',
|
||||
'thepiratebay',
|
||||
|
@ -32,7 +31,6 @@ __all__ = ['ezrss',
|
|||
'nextgen',
|
||||
'speedcd',
|
||||
'nyaatorrents',
|
||||
'fanzub',
|
||||
'torrentbytes',
|
||||
'freshontv',
|
||||
'bitsoup',
|
||||
|
|
|
@ -98,7 +98,7 @@ class BitSoupProvider(generic.TorrentProvider):
|
|||
return False
|
||||
|
||||
if re.search('Username or password incorrect', response.text):
|
||||
logger.log(u'Invalid username or password for ' + self.name + ' Check your settings', logger.ERROR)
|
||||
logger.log(u'Your authentication credentials for ' + self.name + ' are incorrect, check your config.', logger.ERROR)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
@ -180,7 +180,7 @@ class BitSoupProvider(generic.TorrentProvider):
|
|||
|
||||
#Continue only if one Release is found
|
||||
if len(torrent_rows) < 2:
|
||||
logger.log(u"The Data returned from " + self.name + " do not contains any torrent",
|
||||
logger.log(u"The data returned from " + self.name + " does not contain any torrents",
|
||||
logger.DEBUG)
|
||||
continue
|
||||
|
||||
|
|
|
@ -1,150 +0,0 @@
|
|||
# Author: Nic Wolfe <nic@wolfeden.ca>
|
||||
# URL: http://code.google.com/p/sickbeard/
|
||||
#
|
||||
# This file is part of Sick Beard.
|
||||
#
|
||||
# Sick Beard is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Sick Beard is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Sick Beard. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import urllib
|
||||
import datetime
|
||||
|
||||
import sickbeard
|
||||
import generic
|
||||
|
||||
from sickbeard import classes, show_name_helpers, helpers
|
||||
|
||||
from sickbeard import exceptions, logger
|
||||
from sickbeard.common import *
|
||||
from sickbeard import tvcache
|
||||
from lib.dateutil.parser import parse as parseDate
|
||||
|
||||
|
||||
class Fanzub(generic.NZBProvider):
|
||||
|
||||
def __init__(self):
|
||||
|
||||
generic.NZBProvider.__init__(self, "Fanzub")
|
||||
|
||||
self.supportsBacklog = False
|
||||
self.supportsAbsoluteNumbering = True
|
||||
self.anime_only = True
|
||||
|
||||
self.enabled = False
|
||||
|
||||
self.cache = FanzubCache(self)
|
||||
|
||||
self.url = 'https://fanzub.com/'
|
||||
|
||||
def isEnabled(self):
|
||||
return self.enabled
|
||||
|
||||
def imageName(self):
|
||||
return 'fanzub.gif'
|
||||
|
||||
def _get_season_search_strings(self, ep_obj):
|
||||
return [x for x in show_name_helpers.makeSceneSeasonSearchString(self.show, ep_obj)]
|
||||
|
||||
def _get_episode_search_strings(self, ep_obj, add_string=''):
|
||||
return [x for x in show_name_helpers.makeSceneSearchString(self.show, ep_obj)]
|
||||
|
||||
def _doSearch(self, search_string, search_mode='eponly', epcount=0, age=0):
|
||||
if self.show and not self.show.is_anime:
|
||||
logger.log(u"" + str(self.show.name) + " is not an anime skiping ...")
|
||||
return []
|
||||
|
||||
params = {
|
||||
"cat": "anime",
|
||||
"q": search_string.encode('utf-8'),
|
||||
"max": "100"
|
||||
}
|
||||
|
||||
search_url = self.url + "rss?" + urllib.urlencode(params)
|
||||
|
||||
logger.log(u"Search url: " + search_url, logger.DEBUG)
|
||||
|
||||
data = self.cache.getRSSFeed(search_url)
|
||||
if not data:
|
||||
return []
|
||||
|
||||
if 'entries' in data:
|
||||
|
||||
items = data.entries
|
||||
results = []
|
||||
|
||||
for curItem in items:
|
||||
(title, url) = self._get_title_and_url(curItem)
|
||||
|
||||
if title and url:
|
||||
results.append(curItem)
|
||||
else:
|
||||
logger.log(
|
||||
u"The data returned from the " + self.name + " is incomplete, this result is unusable",
|
||||
logger.DEBUG)
|
||||
|
||||
return results
|
||||
|
||||
return []
|
||||
|
||||
def findPropers(self, date=None):
|
||||
|
||||
results = []
|
||||
|
||||
for item in self._doSearch("v2|v3|v4|v5"):
|
||||
|
||||
(title, url) = self._get_title_and_url(item)
|
||||
|
||||
if item.has_key('published_parsed') and item['published_parsed']:
|
||||
result_date = item.published_parsed
|
||||
if result_date:
|
||||
result_date = datetime.datetime(*result_date[0:6])
|
||||
else:
|
||||
logger.log(u"Unable to figure out the date for entry " + title + ", skipping it")
|
||||
continue
|
||||
|
||||
if not date or result_date > date:
|
||||
search_result = classes.Proper(title, url, result_date, self.show)
|
||||
results.append(search_result)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
class FanzubCache(tvcache.TVCache):
|
||||
|
||||
def __init__(self, provider):
|
||||
|
||||
tvcache.TVCache.__init__(self, provider)
|
||||
|
||||
# only poll Fanzub every 20 minutes max
|
||||
self.minTime = 20
|
||||
|
||||
def _getRSSData(self):
|
||||
|
||||
params = {
|
||||
"cat": "anime".encode('utf-8'),
|
||||
"max": "100".encode('utf-8')
|
||||
}
|
||||
|
||||
rss_url = self.provider.url + 'rss?' + urllib.urlencode(params)
|
||||
|
||||
logger.log(self.provider.name + u" cache update URL: " + rss_url, logger.DEBUG)
|
||||
|
||||
data = self.getRSSFeed(rss_url)
|
||||
|
||||
if data and 'entries' in data:
|
||||
return data.entries
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
provider = Fanzub()
|
|
@ -19,10 +19,9 @@
|
|||
import re
|
||||
import traceback
|
||||
import datetime
|
||||
import urlparse
|
||||
import sickbeard
|
||||
import generic
|
||||
from sickbeard.common import Quality, cpu_presets
|
||||
from sickbeard.common import Quality
|
||||
from sickbeard import logger
|
||||
from sickbeard import tvcache
|
||||
from sickbeard import db
|
||||
|
@ -30,7 +29,6 @@ from sickbeard import classes
|
|||
from sickbeard import helpers
|
||||
from sickbeard import show_name_helpers
|
||||
from sickbeard.exceptions import ex, AuthException
|
||||
from sickbeard import clients
|
||||
from lib import requests
|
||||
from lib.requests import exceptions
|
||||
from sickbeard.bs4_parser import BS4Parser
|
||||
|
@ -39,16 +37,15 @@ from sickbeard.helpers import sanitizeSceneName
|
|||
|
||||
|
||||
class FreshOnTVProvider(generic.TorrentProvider):
|
||||
urls = {'base_url': 'http://freshon.tv/',
|
||||
'login': 'http://freshon.tv/login.php?action=makelogin',
|
||||
'detail': 'http://freshon.tv/details.php?id=%s',
|
||||
'search': 'http://freshon.tv/browse.php?incldead=%s&words=0&cat=0&search=%s',
|
||||
'download': 'http://freshon.tv/download.php?id=%s&type=torrent',
|
||||
}
|
||||
urls = {'base_url': 'https://freshon.tv/',
|
||||
'login': 'https://freshon.tv/login.php?action=makelogin',
|
||||
'detail': 'https://freshon.tv/details.php?id=%s',
|
||||
'search': 'https://freshon.tv/browse.php?incldead=%s&words=0&cat=0&search=%s',
|
||||
'download': 'https://freshon.tv/download.php?id=%s&type=torrent'}
|
||||
|
||||
def __init__(self):
|
||||
|
||||
generic.TorrentProvider.__init__(self, "FreshOnTV")
|
||||
generic.TorrentProvider.__init__(self, 'FreshOnTV')
|
||||
|
||||
self.supportsBacklog = True
|
||||
|
||||
|
@ -81,7 +78,7 @@ class FreshOnTVProvider(generic.TorrentProvider):
|
|||
def _checkAuth(self):
|
||||
|
||||
if not self.username or not self.password:
|
||||
raise AuthException("Your authentication credentials for " + self.name + " are missing, check your config.")
|
||||
raise AuthException('Your authentication credentials for %s are missing, check your config.' % self.name)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -91,13 +88,12 @@ class FreshOnTVProvider(generic.TorrentProvider):
|
|||
|
||||
if self._uid and self._hash:
|
||||
|
||||
requests.utils.add_dict_to_cookiejar(self.session.cookies, self.cookies)
|
||||
requests.utils.add_dict_to_cookiejar(self.session.cookies, self.cookies)
|
||||
|
||||
else:
|
||||
login_params = {'username': self.username,
|
||||
'password': self.password,
|
||||
'login': 'submit'
|
||||
}
|
||||
'login': 'Do it!'}
|
||||
|
||||
if not self.session:
|
||||
self.session = requests.Session()
|
||||
|
@ -105,24 +101,30 @@ class FreshOnTVProvider(generic.TorrentProvider):
|
|||
try:
|
||||
response = self.session.post(self.urls['login'], data=login_params, timeout=30, verify=False)
|
||||
except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e:
|
||||
logger.log(u'Unable to connect to ' + self.name + ' provider: ' + ex(e), logger.ERROR)
|
||||
logger.log(u'Unable to connect to %s provider: %s' % (self.name, ex(e)), logger.ERROR)
|
||||
return False
|
||||
|
||||
if re.search('Username does not exist in the userbase or the account is not confirmed yet.', response.text):
|
||||
logger.log(u'Invalid username or password for ' + self.name + ' Check your settings', logger.ERROR)
|
||||
return False
|
||||
logger.log(u'Invalid username or password for %s, check your config.' % self.name, logger.ERROR)
|
||||
return False
|
||||
|
||||
if requests.utils.dict_from_cookiejar(self.session.cookies)['uid'] and requests.utils.dict_from_cookiejar(self.session.cookies)['pass']:
|
||||
if re.search('DDoS protection by CloudFlare', response.text):
|
||||
logger.log(u'Unable to login to %s due to CloudFlare DDoS javascript check.' % self.name, logger.ERROR)
|
||||
return False
|
||||
|
||||
try:
|
||||
if requests.utils.dict_from_cookiejar(self.session.cookies)['uid'] and requests.utils.dict_from_cookiejar(self.session.cookies)['pass']:
|
||||
self._uid = requests.utils.dict_from_cookiejar(self.session.cookies)['uid']
|
||||
self._hash = requests.utils.dict_from_cookiejar(self.session.cookies)['pass']
|
||||
|
||||
self.cookies = {'uid': self._uid,
|
||||
'pass': self._hash
|
||||
}
|
||||
'pass': self._hash}
|
||||
return True
|
||||
else:
|
||||
logger.log(u'Unable to obtain cookie for FreshOnTV', logger.ERROR)
|
||||
return False
|
||||
except:
|
||||
pass
|
||||
|
||||
logger.log(u'Unable to obtain cookie for FreshOnTV', logger.ERROR)
|
||||
return False
|
||||
|
||||
def _get_season_search_strings(self, ep_obj):
|
||||
|
||||
|
@ -131,7 +133,7 @@ class FreshOnTVProvider(generic.TorrentProvider):
|
|||
if ep_obj.show.air_by_date or ep_obj.show.sports:
|
||||
ep_string = show_name + '.' + str(ep_obj.airdate).split('-')[0]
|
||||
elif ep_obj.show.anime:
|
||||
ep_string = show_name + '.' + "%d" % ep_obj.scene_absolute_number
|
||||
ep_string = show_name + '.' + '%d' % ep_obj.scene_absolute_number
|
||||
else:
|
||||
ep_string = show_name + '.S%02d' % int(ep_obj.scene_season) #1) showName SXX
|
||||
|
||||
|
@ -149,18 +151,18 @@ class FreshOnTVProvider(generic.TorrentProvider):
|
|||
if self.show.air_by_date:
|
||||
for show_name in set(show_name_helpers.allPossibleShowNames(self.show)):
|
||||
ep_string = sanitizeSceneName(show_name) + ' ' + \
|
||||
str(ep_obj.airdate).replace('-', '|')
|
||||
str(ep_obj.airdate).replace('-', '|')
|
||||
search_string['Episode'].append(ep_string)
|
||||
elif self.show.sports:
|
||||
for show_name in set(show_name_helpers.allPossibleShowNames(self.show)):
|
||||
ep_string = sanitizeSceneName(show_name) + ' ' + \
|
||||
str(ep_obj.airdate).replace('-', '|') + '|' + \
|
||||
ep_obj.airdate.strftime('%b')
|
||||
str(ep_obj.airdate).replace('-', '|') + '|' + \
|
||||
ep_obj.airdate.strftime('%b')
|
||||
search_string['Episode'].append(ep_string)
|
||||
elif self.show.anime:
|
||||
for show_name in set(show_name_helpers.allPossibleShowNames(self.show)):
|
||||
ep_string = sanitizeSceneName(show_name) + ' ' + \
|
||||
"%i" % int(ep_obj.scene_absolute_number)
|
||||
'%i' % int(ep_obj.scene_absolute_number)
|
||||
search_string['Episode'].append(ep_string)
|
||||
else:
|
||||
for show_name in set(show_name_helpers.allPossibleShowNames(self.show)):
|
||||
|
@ -180,18 +182,18 @@ class FreshOnTVProvider(generic.TorrentProvider):
|
|||
freeleech = '3' if self.freeleech else '0'
|
||||
|
||||
if not self._doLogin():
|
||||
return []
|
||||
return results
|
||||
|
||||
for mode in search_params.keys():
|
||||
for search_string in search_params[mode]:
|
||||
|
||||
search_string, url = self._get_title_and_url([search_string, self.urls['search'], '', '', ''])
|
||||
if isinstance(search_string, unicode):
|
||||
search_string = unidecode(search_string)
|
||||
|
||||
|
||||
searchURL = self.urls['search'] % (freeleech, search_string)
|
||||
|
||||
logger.log(u"Search string: " + searchURL, logger.DEBUG)
|
||||
logger.log(u'Search string: ' + searchURL, logger.DEBUG)
|
||||
|
||||
# returns top 15 results by default, expandable in user profile to 100
|
||||
data = self.getURL(searchURL)
|
||||
|
@ -199,13 +201,15 @@ class FreshOnTVProvider(generic.TorrentProvider):
|
|||
continue
|
||||
|
||||
try:
|
||||
with BS4Parser(data, features=["html5lib", "permissive"]) as html:
|
||||
with BS4Parser(data, features=['html5lib', 'permissive']) as html:
|
||||
torrent_table = html.find('table', attrs={'class': 'frame'})
|
||||
torrent_rows = torrent_table.findChildren('tr') if torrent_table else []
|
||||
torrent_rows = []
|
||||
if torrent_table:
|
||||
torrent_rows = torrent_table.findChildren('tr')
|
||||
|
||||
#Continue only if one Release is found
|
||||
if len(torrent_rows) < 2:
|
||||
logger.log(u"The Data returned from " + self.name + " do not contains any torrent",
|
||||
# Continue only if one Release is found
|
||||
if 2 > len(torrent_rows):
|
||||
logger.log(u'The data returned from %s does not contain any torrents' % self.name,
|
||||
logger.DEBUG)
|
||||
continue
|
||||
|
||||
|
@ -213,14 +217,13 @@ class FreshOnTVProvider(generic.TorrentProvider):
|
|||
for result in torrent_rows[1:]:
|
||||
cells = result.findChildren('td')
|
||||
|
||||
link = cells[1].find('a', attrs = {'class': 'torrent_name_link'})
|
||||
#skip if torrent has been nuked due to poor quality
|
||||
if cells[1].find('img', alt='Nuked') != None:
|
||||
link = cells[1].find('a', attrs={'class': 'torrent_name_link'})
|
||||
# skip if torrent has been nuked due to poor quality
|
||||
if None is not cells[1].find('img', alt='Nuked'):
|
||||
continue
|
||||
|
||||
torrent_id = link['href'].replace('/details.php?id=', '')
|
||||
|
||||
|
||||
try:
|
||||
if link.has_key('title'):
|
||||
title = cells[1].find('a', {'class': 'torrent_name_link'})['title']
|
||||
|
@ -234,22 +237,22 @@ class FreshOnTVProvider(generic.TorrentProvider):
|
|||
except (AttributeError, TypeError):
|
||||
continue
|
||||
|
||||
#Filter unseeded torrent
|
||||
if mode != 'RSS' and (seeders < self.minseed or leechers < self.minleech):
|
||||
# Filter unseeded torrent
|
||||
if 'RSS' != mode and (self.minseed > seeders or self.minleech > leechers):
|
||||
continue
|
||||
|
||||
if not title or not download_url:
|
||||
continue
|
||||
|
||||
item = title, download_url, id, seeders, leechers
|
||||
logger.log(u"Found result: " + title + "(" + searchURL + ")", logger.DEBUG)
|
||||
logger.log(u'Found result: %s (%s)' % (title, searchURL), logger.DEBUG)
|
||||
|
||||
items[mode].append(item)
|
||||
|
||||
except Exception, e:
|
||||
logger.log(u"Failed parsing " + self.name + " Traceback: " + traceback.format_exc(), logger.ERROR)
|
||||
logger.log(u'Failed parsing %s Traceback: %s' % (self.name, traceback.format_exc()), logger.ERROR)
|
||||
|
||||
#For each search mode sort all the items by seeders
|
||||
# For each search mode sort all the items by seeders
|
||||
items[mode].sort(key=lambda tup: tup[3], reverse=True)
|
||||
|
||||
results += items[mode]
|
||||
|
@ -260,10 +263,14 @@ class FreshOnTVProvider(generic.TorrentProvider):
|
|||
|
||||
title, url, id, seeders, leechers = item
|
||||
|
||||
if title:
|
||||
title += u''
|
||||
title = re.sub(r'\s+', '.', title)
|
||||
|
||||
if url:
|
||||
url = str(url).replace('&', '&')
|
||||
|
||||
return (title, url)
|
||||
return title, url
|
||||
|
||||
def findPropers(self, search_date=datetime.datetime.today()):
|
||||
|
||||
|
@ -282,9 +289,9 @@ class FreshOnTVProvider(generic.TorrentProvider):
|
|||
return []
|
||||
|
||||
for sqlshow in sqlResults:
|
||||
self.show = helpers.findCertainShow(sickbeard.showList, int(sqlshow["showid"]))
|
||||
self.show = helpers.findCertainShow(sickbeard.showList, int(sqlshow['showid']))
|
||||
if self.show:
|
||||
curEp = self.show.getEpisode(int(sqlshow["season"]), int(sqlshow["episode"]))
|
||||
curEp = self.show.getEpisode(int(sqlshow['season']), int(sqlshow['episode']))
|
||||
|
||||
searchString = self._get_episode_search_strings(curEp, add_string='PROPER|REPACK')
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ class HDTorrentsProvider(generic.TorrentProvider):
|
|||
|
||||
if re.search('You need cookies enabled to log in.', response.text) \
|
||||
or response.status_code == 401:
|
||||
logger.log(u'Invalid username or password for ' + self.name + ' Check your settings', logger.ERROR)
|
||||
logger.log(u'Your authentication credentials for ' + self.name + ' are incorrect, check your config.', logger.ERROR)
|
||||
return False
|
||||
|
||||
self._uid = requests.utils.dict_from_cookiejar(self.session.cookies)['uid']
|
||||
|
@ -212,7 +212,7 @@ class HDTorrentsProvider(generic.TorrentProvider):
|
|||
continue
|
||||
|
||||
if not entries:
|
||||
logger.log(u"The Data returned from " + self.name + " do not contains any torrent",
|
||||
logger.log(u"The data returned from " + self.name + " does not contain any torrents",
|
||||
logger.DEBUG)
|
||||
continue
|
||||
|
||||
|
|
|
@ -40,9 +40,9 @@ from sickbeard.show_name_helpers import allPossibleShowNames
|
|||
|
||||
|
||||
class IPTorrentsProvider(generic.TorrentProvider):
|
||||
urls = {'base_url': 'https://www.iptorrents.com',
|
||||
'login': 'https://www.iptorrents.com/torrents/',
|
||||
'search': 'https://www.iptorrents.com/torrents/?%s%s&q=%s&qf=ti',
|
||||
urls = {'base_url': 'https://iptorrents.com',
|
||||
'login': 'https://iptorrents.com/torrents/',
|
||||
'search': 'https://iptorrents.com/torrents/?%s%s&q=%s&qf=ti',
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
|
@ -97,7 +97,7 @@ class IPTorrentsProvider(generic.TorrentProvider):
|
|||
if re.search('tries left', response.text) \
|
||||
or re.search('<title>IPT</title>', response.text) \
|
||||
or response.status_code == 401:
|
||||
logger.log(u'Invalid username or password for ' + self.name + ', Check your settings!', logger.ERROR)
|
||||
logger.log(u'Your authentication credentials for ' + self.name + ' are incorrect, check your config.', logger.ERROR)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
@ -189,7 +189,7 @@ class IPTorrentsProvider(generic.TorrentProvider):
|
|||
|
||||
#Continue only if one Release is found
|
||||
if len(torrents) < 2:
|
||||
logger.log(u"The Data returned from " + self.name + " do not contains any torrent",
|
||||
logger.log(u"The data returned from " + self.name + " does not contain any torrents",
|
||||
logger.WARNING)
|
||||
continue
|
||||
|
||||
|
|
|
@ -250,6 +250,9 @@ class NewznabProvider(generic.NZBProvider):
|
|||
elif code == '102':
|
||||
raise AuthException(
|
||||
"Your account isn't allowed to use the API on " + self.name + ", contact the administrator")
|
||||
elif code == '910':
|
||||
logger.log(u"" + self.name + " currently has their API disabled, please check with provider.", logger.WARNING)
|
||||
return False
|
||||
else:
|
||||
logger.log(u"Unknown error given from " + self.name + ": " + data.feed['error']['description'],
|
||||
logger.ERROR)
|
||||
|
@ -308,7 +311,7 @@ class NewznabProvider(generic.NZBProvider):
|
|||
results.append(item)
|
||||
else:
|
||||
logger.log(
|
||||
u"The data returned from the " + self.name + " is incomplete, this result is unusable",
|
||||
u"The data returned from " + self.name + " is incomplete, this result is unusable",
|
||||
logger.DEBUG)
|
||||
|
||||
# get total and offset attribs
|
||||
|
@ -336,7 +339,7 @@ class NewznabProvider(generic.NZBProvider):
|
|||
params['limit']) + " items.", logger.DEBUG)
|
||||
else:
|
||||
logger.log(str(
|
||||
total - int(params['offset'])) + " No more searches needed, could find anything I was looking for! " + str(
|
||||
total - int(params['offset'])) + " No more searches needed, couldn't find anything I was looking for! " + str(
|
||||
params['limit']) + " items.", logger.DEBUG)
|
||||
break
|
||||
|
||||
|
|
|
@ -208,7 +208,7 @@ class NextGenProvider(generic.TorrentProvider):
|
|||
resultsTable = html.find('div', attrs={'id': 'torrent-table-wrapper'})
|
||||
|
||||
if not resultsTable:
|
||||
logger.log(u"The Data returned from " + self.name + " do not contains any torrent",
|
||||
logger.log(u"The data returned from " + self.name + " does not contain any torrents",
|
||||
logger.DEBUG)
|
||||
continue
|
||||
|
||||
|
@ -253,7 +253,7 @@ class NextGenProvider(generic.TorrentProvider):
|
|||
items[mode].append(item)
|
||||
|
||||
else:
|
||||
logger.log(u"The Data returned from " + self.name + " do not contains any torrent",
|
||||
logger.log(u"The data returned from " + self.name + " does not contain any torrents",
|
||||
logger.WARNING)
|
||||
continue
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ class NyaaProvider(generic.TorrentProvider):
|
|||
results.append(curItem)
|
||||
else:
|
||||
logger.log(
|
||||
u"The data returned from the " + self.name + " is incomplete, this result is unusable",
|
||||
u"The data returned from " + self.name + " is incomplete, this result is unusable",
|
||||
logger.DEBUG)
|
||||
|
||||
return results
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
import re
|
||||
import traceback
|
||||
import datetime
|
||||
import urlparse
|
||||
import sickbeard
|
||||
import generic
|
||||
from sickbeard.common import Quality
|
||||
|
@ -31,7 +30,6 @@ from sickbeard import classes
|
|||
from sickbeard import helpers
|
||||
from sickbeard import show_name_helpers
|
||||
from sickbeard.exceptions import ex
|
||||
from sickbeard import clients
|
||||
from lib import requests
|
||||
from lib.requests import exceptions
|
||||
from sickbeard.bs4_parser import BS4Parser
|
||||
|
@ -42,17 +40,16 @@ from sickbeard.helpers import sanitizeSceneName
|
|||
class SCCProvider(generic.TorrentProvider):
|
||||
urls = {'base_url': 'https://sceneaccess.eu',
|
||||
'login': 'https://sceneaccess.eu/login',
|
||||
'detail': 'https://www.sceneaccess.eu/details?id=%s',
|
||||
'detail': 'https://sceneaccess.eu/details?id=%s',
|
||||
'search': 'https://sceneaccess.eu/browse?search=%s&method=1&%s',
|
||||
'nonscene': 'https://sceneaccess.eu/nonscene?search=%s&method=1&c44=44&c45=44',
|
||||
'foreign': 'https://sceneaccess.eu/foreign?search=%s&method=1&c34=34&c33=33',
|
||||
'archive': 'https://sceneaccess.eu/archive?search=%s&method=1&c26=26',
|
||||
'download': 'https://www.sceneaccess.eu/%s',
|
||||
}
|
||||
'download': 'https://sceneaccess.eu/%s'}
|
||||
|
||||
def __init__(self):
|
||||
|
||||
generic.TorrentProvider.__init__(self, "SceneAccess")
|
||||
generic.TorrentProvider.__init__(self, 'SceneAccess')
|
||||
|
||||
self.supportsBacklog = True
|
||||
|
||||
|
@ -67,7 +64,7 @@ class SCCProvider(generic.TorrentProvider):
|
|||
|
||||
self.url = self.urls['base_url']
|
||||
|
||||
self.categories = "c27=27&c17=17&c11=11"
|
||||
self.categories = 'c27=27&c17=17&c11=11'
|
||||
|
||||
def isEnabled(self):
|
||||
return self.enabled
|
||||
|
@ -84,21 +81,20 @@ class SCCProvider(generic.TorrentProvider):
|
|||
|
||||
login_params = {'username': self.username,
|
||||
'password': self.password,
|
||||
'submit': 'come on in',
|
||||
}
|
||||
'submit': 'come on in'}
|
||||
|
||||
self.session = requests.Session()
|
||||
|
||||
try:
|
||||
response = self.session.post(self.urls['login'], data=login_params, headers=self.headers, timeout=30, verify=False)
|
||||
except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e:
|
||||
logger.log(u'Unable to connect to ' + self.name + ' provider: ' + ex(e), logger.ERROR)
|
||||
logger.log(u'Unable to connect to %s provider: %s' % (self.name, ex(e)), logger.ERROR)
|
||||
return False
|
||||
|
||||
if re.search('Username or password incorrect', response.text) \
|
||||
or re.search('<title>SceneAccess \| Login</title>', response.text) \
|
||||
or response.status_code == 401:
|
||||
logger.log(u'Invalid username or password for ' + self.name + ' Check your settings', logger.ERROR)
|
||||
or 401 == response.status_code:
|
||||
logger.log(u'Your authentication credentials for %s are incorrect, check your config.' % self.name, logger.ERROR)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
@ -108,13 +104,13 @@ class SCCProvider(generic.TorrentProvider):
|
|||
search_string = {'Season': []}
|
||||
for show_name in set(show_name_helpers.allPossibleShowNames(self.show)):
|
||||
if ep_obj.show.air_by_date or ep_obj.show.sports:
|
||||
ep_string = show_name + ' ' + str(ep_obj.airdate).split('-')[0]
|
||||
ep_string = str(ep_obj.airdate).split('-')[0]
|
||||
elif ep_obj.show.anime:
|
||||
ep_string = show_name + ' ' + "%d" % ep_obj.scene_absolute_number
|
||||
ep_string = '%d' % ep_obj.scene_absolute_number
|
||||
else:
|
||||
ep_string = show_name + ' S%02d' % int(ep_obj.scene_season) #1) showName SXX
|
||||
ep_string = 'S%02d' % int(ep_obj.scene_season) # 1) showName SXX
|
||||
|
||||
search_string['Season'].append(ep_string)
|
||||
search_string['Season'].append('%s %s' % (show_name, ep_string))
|
||||
|
||||
return [search_string]
|
||||
|
||||
|
@ -128,24 +124,24 @@ class SCCProvider(generic.TorrentProvider):
|
|||
if self.show.air_by_date:
|
||||
for show_name in set(show_name_helpers.allPossibleShowNames(self.show)):
|
||||
ep_string = sanitizeSceneName(show_name) + ' ' + \
|
||||
str(ep_obj.airdate).replace('-', '|')
|
||||
str(ep_obj.airdate).replace('-', '|')
|
||||
search_string['Episode'].append(ep_string)
|
||||
elif self.show.sports:
|
||||
for show_name in set(show_name_helpers.allPossibleShowNames(self.show)):
|
||||
ep_string = sanitizeSceneName(show_name) + ' ' + \
|
||||
str(ep_obj.airdate).replace('-', '|') + '|' + \
|
||||
ep_obj.airdate.strftime('%b')
|
||||
str(ep_obj.airdate).replace('-', '|') + '|' + \
|
||||
ep_obj.airdate.strftime('%b')
|
||||
search_string['Episode'].append(ep_string)
|
||||
elif self.show.anime:
|
||||
for show_name in set(show_name_helpers.allPossibleShowNames(self.show)):
|
||||
ep_string = sanitizeSceneName(show_name) + ' ' + \
|
||||
"%i" % int(ep_obj.scene_absolute_number)
|
||||
'%i' % int(ep_obj.scene_absolute_number)
|
||||
search_string['Episode'].append(ep_string)
|
||||
else:
|
||||
for show_name in set(show_name_helpers.allPossibleShowNames(self.show)):
|
||||
ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \
|
||||
sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season,
|
||||
'episodenumber': ep_obj.scene_episode}
|
||||
sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season,
|
||||
'episodenumber': ep_obj.scene_episode}
|
||||
|
||||
search_string['Episode'].append(re.sub('\s+', ' ', ep_string))
|
||||
|
||||
|
@ -164,47 +160,49 @@ class SCCProvider(generic.TorrentProvider):
|
|||
items = {'Season': [], 'Episode': [], 'RSS': []}
|
||||
|
||||
if not self._doLogin():
|
||||
return []
|
||||
return results
|
||||
|
||||
for mode in search_params.keys():
|
||||
for search_string in search_params[mode]:
|
||||
|
||||
search_string, url = self._get_title_and_url([search_string, self.urls['search'], '', '', ''])
|
||||
if isinstance(search_string, unicode):
|
||||
search_string = unidecode(search_string)
|
||||
|
||||
nonsceneSearchURL = None
|
||||
foreignSearchURL = None
|
||||
if mode == 'Season':
|
||||
searchURL = self.urls['archive'] % (search_string)
|
||||
if 'Season' == mode:
|
||||
searchURL = self.urls['archive'] % search_string
|
||||
data = [self.getURL(searchURL)]
|
||||
else:
|
||||
searchURL = self.urls['search'] % (search_string, self.categories)
|
||||
nonsceneSearchURL = self.urls['nonscene'] % (search_string)
|
||||
foreignSearchURL = self.urls['foreign'] % (search_string)
|
||||
nonsceneSearchURL = self.urls['nonscene'] % search_string
|
||||
foreignSearchURL = self.urls['foreign'] % search_string
|
||||
data = [self.getURL(searchURL),
|
||||
self.getURL(nonsceneSearchURL),
|
||||
self.getURL(foreignSearchURL)]
|
||||
logger.log(u"Search string: " + nonsceneSearchURL, logger.DEBUG)
|
||||
logger.log(u"Search string: " + foreignSearchURL, logger.DEBUG)
|
||||
logger.log(u'Search string: ' + nonsceneSearchURL, logger.DEBUG)
|
||||
logger.log(u'Search string: ' + foreignSearchURL, logger.DEBUG)
|
||||
|
||||
logger.log(u"Search string: " + searchURL, logger.DEBUG)
|
||||
logger.log(u'Search string: ' + searchURL, logger.DEBUG)
|
||||
|
||||
if not data:
|
||||
continue
|
||||
|
||||
try:
|
||||
for dataItem in data:
|
||||
with BS4Parser(dataItem, features=["html5lib", "permissive"]) as html:
|
||||
with BS4Parser(dataItem, features=['html5lib', 'permissive']) as html:
|
||||
torrent_table = html.find('table', attrs={'id': 'torrents-table'})
|
||||
torrent_rows = torrent_table.find_all('tr') if torrent_table else []
|
||||
torrent_rows = []
|
||||
if torrent_table:
|
||||
torrent_rows = torrent_table.find_all('tr')
|
||||
|
||||
#Continue only if at least one Release is found
|
||||
if len(torrent_rows) < 2:
|
||||
# Continue only if at least one Release is found
|
||||
if 2 > len(torrent_rows):
|
||||
if html.title:
|
||||
source = self.name + " (" + html.title.string + ")"
|
||||
source = '%s (%s)' % (self.name, html.title.string)
|
||||
else:
|
||||
source = self.name
|
||||
logger.log(u"The Data returned from " + source + " does not contain any torrent", logger.DEBUG)
|
||||
logger.log(u'The data returned from %s does not contain any torrents' % source, logger.DEBUG)
|
||||
continue
|
||||
|
||||
for result in torrent_table.find_all('tr')[1:]:
|
||||
|
@ -220,7 +218,7 @@ class SCCProvider(generic.TorrentProvider):
|
|||
|
||||
title = link.string
|
||||
if re.search('\.\.\.', title):
|
||||
data = self.getURL(self.url + "/" + link['href'])
|
||||
data = self.getURL(self.url + '/' + link['href'])
|
||||
if data:
|
||||
with BS4Parser(data) as details_html:
|
||||
title = re.search('(?<=").+(?<!")', details_html.title.string).group(0)
|
||||
|
@ -231,7 +229,7 @@ class SCCProvider(generic.TorrentProvider):
|
|||
except (AttributeError, TypeError):
|
||||
continue
|
||||
|
||||
if mode != 'RSS' and (seeders < self.minseed or leechers < self.minleech):
|
||||
if 'RSS' != mode and (self.minseed > seeders or self.minleech > leechers):
|
||||
continue
|
||||
|
||||
if not title or not download_url:
|
||||
|
@ -240,18 +238,18 @@ class SCCProvider(generic.TorrentProvider):
|
|||
item = title, download_url, id, seeders, leechers
|
||||
|
||||
if self._isSection('Non-Scene', dataItem):
|
||||
logger.log(u"Found result: " + title + "(" + nonsceneSearchURL + ")", logger.DEBUG)
|
||||
logger.log(u'Found result: %s (%s)' % (title, nonsceneSearchURL), logger.DEBUG)
|
||||
elif self._isSection('Foreign', dataItem):
|
||||
logger.log(u"Found result: " + title + "(" + foreignSearchURL + ")", logger.DEBUG)
|
||||
logger.log(u'Found result: %s (%s)' % (title, foreignSearchURL), logger.DEBUG)
|
||||
else:
|
||||
logger.log(u"Found result: " + title + "(" + searchURL + ")", logger.DEBUG)
|
||||
logger.log(u'Found result: %s (%s)' % (title, searchURL), logger.DEBUG)
|
||||
|
||||
items[mode].append(item)
|
||||
|
||||
except Exception, e:
|
||||
logger.log(u"Failed parsing " + self.name + " Traceback: " + traceback.format_exc(), logger.ERROR)
|
||||
logger.log(u'Failed parsing %s Traceback: %s' % (self.name, traceback.format_exc()), logger.ERROR)
|
||||
|
||||
#For each search mode sort all the items by seeders
|
||||
# For each search mode sort all the items by seeders
|
||||
items[mode].sort(key=lambda tup: tup[3], reverse=True)
|
||||
|
||||
results += items[mode]
|
||||
|
@ -263,13 +261,13 @@ class SCCProvider(generic.TorrentProvider):
|
|||
title, url, id, seeders, leechers = item
|
||||
|
||||
if title:
|
||||
title = u'' + title
|
||||
title = title.replace(' ', '.')
|
||||
title += u''
|
||||
title = re.sub(r'\s+', '.', title)
|
||||
|
||||
if url:
|
||||
url = str(url).replace('&', '&')
|
||||
|
||||
return (title, url)
|
||||
return title, url
|
||||
|
||||
def findPropers(self, search_date=datetime.datetime.today()):
|
||||
|
||||
|
@ -288,9 +286,9 @@ class SCCProvider(generic.TorrentProvider):
|
|||
return []
|
||||
|
||||
for sqlshow in sqlResults:
|
||||
self.show = helpers.findCertainShow(sickbeard.showList, int(sqlshow["showid"]))
|
||||
self.show = helpers.findCertainShow(sickbeard.showList, int(sqlshow['showid']))
|
||||
if self.show:
|
||||
curEp = self.show.getEpisode(int(sqlshow["season"]), int(sqlshow["episode"]))
|
||||
curEp = self.show.getEpisode(int(sqlshow['season']), int(sqlshow['episode']))
|
||||
|
||||
searchString = self._get_episode_search_strings(curEp, add_string='PROPER|REPACK')
|
||||
|
||||
|
@ -317,5 +315,4 @@ class SCCCache(tvcache.TVCache):
|
|||
return self.provider._doSearch(search_params)
|
||||
|
||||
|
||||
|
||||
provider = SCCProvider()
|
||||
|
|
|
@ -91,7 +91,7 @@ class SpeedCDProvider(generic.TorrentProvider):
|
|||
|
||||
if re.search('Incorrect username or Password. Please try again.', response.text) \
|
||||
or response.status_code == 401:
|
||||
logger.log(u'Invalid username or password for ' + self.name + ' Check your settings', logger.ERROR)
|
||||
logger.log(u'Your authentication credentials for ' + self.name + ' are incorrect, check your config.', logger.ERROR)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
|
|
@ -18,36 +18,28 @@
|
|||
|
||||
from __future__ import with_statement
|
||||
|
||||
import time
|
||||
import re
|
||||
import urllib, urllib2, urlparse
|
||||
import sys
|
||||
import urllib
|
||||
import os
|
||||
import datetime
|
||||
|
||||
import sickbeard
|
||||
import generic
|
||||
from sickbeard.common import Quality, cpu_presets
|
||||
from sickbeard.common import Quality
|
||||
from sickbeard.name_parser.parser import NameParser, InvalidNameException, InvalidShowException
|
||||
from sickbeard import db
|
||||
from sickbeard import classes
|
||||
from sickbeard import logger
|
||||
from sickbeard import tvcache
|
||||
from sickbeard import helpers
|
||||
from sickbeard import clients
|
||||
from sickbeard.show_name_helpers import allPossibleShowNames, sanitizeSceneName
|
||||
from sickbeard.common import Overview
|
||||
from sickbeard.exceptions import ex
|
||||
from sickbeard import encodingKludge as ek
|
||||
from lib import requests
|
||||
from lib.requests import exceptions
|
||||
from lib.unidecode import unidecode
|
||||
|
||||
|
||||
class ThePirateBayProvider(generic.TorrentProvider):
|
||||
def __init__(self):
|
||||
|
||||
generic.TorrentProvider.__init__(self, "ThePirateBay")
|
||||
generic.TorrentProvider.__init__(self, 'The Pirate Bay')
|
||||
|
||||
self.supportsBacklog = True
|
||||
|
||||
|
@ -61,11 +53,11 @@ class ThePirateBayProvider(generic.TorrentProvider):
|
|||
|
||||
self.proxy = ThePirateBayWebproxy()
|
||||
|
||||
self.url = 'http://oldpiratebay.org/'
|
||||
self.url = 'https://thepiratebay.se/'
|
||||
|
||||
self.searchurl = self.url + 'search.php?q=%s&Torrent_sort=seeders.desc' # order by seed
|
||||
self.searchurl = self.url + 'search/%s/0/7/200' # order by seed
|
||||
|
||||
self.re_title_url = 'href=["\'](?P<url>magnet:.*?)&.*?/torrent/(?P<id>\d+)/(?P<title>.*?)".+?seeders-row sy">(?P<seeders>\d+)</td>.+?leechers-row ly">(?P<leechers>\d+)</td>'
|
||||
self.re_title_url = '/torrent/(?P<id>\d+)/(?P<title>.*?)//1".+?(?P<url>magnet.*?)//1".+?(?P<seeders>\d+)</td>.+?(?P<leechers>\d+)</td>'
|
||||
|
||||
def isEnabled(self):
|
||||
return self.enabled
|
||||
|
@ -82,23 +74,23 @@ class ThePirateBayProvider(generic.TorrentProvider):
|
|||
|
||||
quality_string = ''
|
||||
|
||||
if quality == Quality.SDTV:
|
||||
if Quality.SDTV == quality:
|
||||
quality_string = 'HDTV x264'
|
||||
if quality == Quality.SDDVD:
|
||||
if Quality.SDDVD == quality:
|
||||
quality_string = 'DVDRIP'
|
||||
elif quality == Quality.HDTV:
|
||||
elif Quality.HDTV == quality:
|
||||
quality_string = '720p HDTV x264'
|
||||
elif quality == Quality.FULLHDTV:
|
||||
elif Quality.FULLHDTV == quality:
|
||||
quality_string = '1080p HDTV x264'
|
||||
elif quality == Quality.RAWHDTV:
|
||||
elif Quality.RAWHDTV == quality:
|
||||
quality_string = '1080i HDTV mpeg2'
|
||||
elif quality == Quality.HDWEBDL:
|
||||
elif Quality.HDWEBDL == quality:
|
||||
quality_string = '720p WEB-DL h264'
|
||||
elif quality == Quality.FULLHDWEBDL:
|
||||
elif Quality.FULLHDWEBDL == quality:
|
||||
quality_string = '1080p WEB-DL h264'
|
||||
elif quality == Quality.HDBLURAY:
|
||||
elif Quality.HDBLURAY == quality:
|
||||
quality_string = '720p Bluray x264'
|
||||
elif quality == Quality.FULLHDBLURAY:
|
||||
elif Quality.FULLHDBLURAY == quality:
|
||||
quality_string = '1080p Bluray x264'
|
||||
|
||||
return quality_string
|
||||
|
@ -108,7 +100,7 @@ class ThePirateBayProvider(generic.TorrentProvider):
|
|||
|
||||
mediaExtensions = ['avi', 'mkv', 'wmv', 'divx',
|
||||
'vob', 'dvr-ms', 'wtv', 'ts'
|
||||
'ogv', 'rar', 'zip', 'mp4']
|
||||
'ogv', 'rar', 'zip', 'mp4']
|
||||
|
||||
quality = Quality.UNKNOWN
|
||||
|
||||
|
@ -126,31 +118,31 @@ class ThePirateBayProvider(generic.TorrentProvider):
|
|||
filesList = re.findall('<td.+>(.*?)</td>', data)
|
||||
|
||||
if not filesList:
|
||||
logger.log(u"Unable to get the torrent file list for " + title, logger.ERROR)
|
||||
logger.log(u'Unable to get the torrent file list for ' + title, logger.ERROR)
|
||||
|
||||
videoFiles = filter(lambda x: x.rpartition(".")[2].lower() in mediaExtensions, filesList)
|
||||
videoFiles = filter(lambda x: x.rpartition('.')[2].lower() in mediaExtensions, filesList)
|
||||
|
||||
#Filtering SingleEpisode/MultiSeason Torrent
|
||||
if len(videoFiles) < ep_number or len(videoFiles) > float(ep_number * 1.1):
|
||||
logger.log(
|
||||
u"Result " + title + " have " + str(ep_number) + " episode and episodes retrived in torrent are " + str(
|
||||
len(videoFiles)), logger.DEBUG)
|
||||
logger.log(u"Result " + title + " Seem to be a Single Episode or MultiSeason torrent, skipping result...",
|
||||
logger.DEBUG)
|
||||
# Filtering SingleEpisode/MultiSeason Torrent
|
||||
if ep_number > len(videoFiles) or float(ep_number * 1.1) < len(videoFiles):
|
||||
logger.log(u'Result %s has episode %s and total episodes retrieved in torrent are %s'
|
||||
% (title, str(ep_number), str(len(videoFiles))), logger.DEBUG)
|
||||
logger.log(u'Result %s seems to be a single episode or multiseason torrent, skipping result...'
|
||||
% title, logger.DEBUG)
|
||||
return None
|
||||
|
||||
if Quality.sceneQuality(title) != Quality.UNKNOWN:
|
||||
if Quality.UNKNOWN != Quality.sceneQuality(title):
|
||||
return title
|
||||
|
||||
for fileName in videoFiles:
|
||||
quality = Quality.sceneQuality(os.path.basename(fileName))
|
||||
if quality != Quality.UNKNOWN: break
|
||||
if Quality.UNKNOWN != quality:
|
||||
break
|
||||
|
||||
if fileName is not None and quality == Quality.UNKNOWN:
|
||||
if None is not fileName and Quality.UNKNOWN == quality:
|
||||
quality = Quality.assumeQuality(os.path.basename(fileName))
|
||||
|
||||
if quality == Quality.UNKNOWN:
|
||||
logger.log(u"Unable to obtain a Season Quality for " + title, logger.DEBUG)
|
||||
if Quality.UNKNOWN == quality:
|
||||
logger.log(u'Unable to obtain a Season Quality for ' + title, logger.DEBUG)
|
||||
return None
|
||||
|
||||
try:
|
||||
|
@ -159,11 +151,12 @@ class ThePirateBayProvider(generic.TorrentProvider):
|
|||
except (InvalidNameException, InvalidShowException):
|
||||
return None
|
||||
|
||||
logger.log(u"Season quality for " + title + " is " + Quality.qualityStrings[quality], logger.DEBUG)
|
||||
logger.log(u'Season quality for %s is %s' % (title, Quality.qualityStrings[quality]), logger.DEBUG)
|
||||
|
||||
if parse_result.series_name and parse_result.season_number:
|
||||
title = parse_result.series_name + ' S%02d' % int(parse_result.season_number) + ' ' + self._reverseQuality(
|
||||
quality)
|
||||
title = '%s S%02d %s' % (parse_result.series_name,
|
||||
int(parse_result.season_number),
|
||||
self._reverseQuality(quality))
|
||||
|
||||
return title
|
||||
|
||||
|
@ -177,12 +170,12 @@ class ThePirateBayProvider(generic.TorrentProvider):
|
|||
ep_string = show_name + ' Season ' + str(ep_obj.airdate).split('-')[0]
|
||||
search_string['Season'].append(ep_string)
|
||||
elif ep_obj.show.anime:
|
||||
ep_string = show_name + ' ' + "%02d" % ep_obj.scene_absolute_number
|
||||
ep_string = show_name + ' ' + '%02d' % ep_obj.scene_absolute_number
|
||||
search_string['Season'].append(ep_string)
|
||||
else:
|
||||
ep_string = show_name + ' S%02d' % int(ep_obj.scene_season)
|
||||
search_string['Season'].append(ep_string)
|
||||
ep_string = show_name + ' Season ' + str(ep_obj.scene_season) + ' -Ep*'
|
||||
ep_string = show_name + ' Season %s -Ep*' % str(ep_obj.scene_season)
|
||||
search_string['Season'].append(ep_string)
|
||||
|
||||
search_string['Season'].append(ep_string)
|
||||
|
@ -196,26 +189,26 @@ class ThePirateBayProvider(generic.TorrentProvider):
|
|||
if self.show.air_by_date:
|
||||
for show_name in set(allPossibleShowNames(self.show)):
|
||||
ep_string = sanitizeSceneName(show_name) + ' ' + \
|
||||
str(ep_obj.airdate).replace('-', ' ')
|
||||
str(ep_obj.airdate).replace('-', ' ')
|
||||
search_string['Episode'].append(ep_string)
|
||||
elif self.show.sports:
|
||||
for show_name in set(allPossibleShowNames(self.show)):
|
||||
ep_string = sanitizeSceneName(show_name) + ' ' + \
|
||||
str(ep_obj.airdate).replace('-', '|') + '|' + \
|
||||
ep_obj.airdate.strftime('%b')
|
||||
str(ep_obj.airdate).replace('-', '|') + '|' + \
|
||||
ep_obj.airdate.strftime('%b')
|
||||
search_string['Episode'].append(ep_string)
|
||||
elif self.show.anime:
|
||||
for show_name in set(allPossibleShowNames(self.show)):
|
||||
ep_string = sanitizeSceneName(show_name) + ' ' + \
|
||||
"%02i" % int(ep_obj.scene_absolute_number)
|
||||
'%02i' % int(ep_obj.scene_absolute_number)
|
||||
search_string['Episode'].append(ep_string)
|
||||
else:
|
||||
for show_name in set(allPossibleShowNames(self.show)):
|
||||
ep_string = sanitizeSceneName(show_name) + ' ' + \
|
||||
sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season,
|
||||
'episodenumber': ep_obj.scene_episode} + '|' + \
|
||||
sickbeard.config.naming_ep_type[0] % {'seasonnumber': ep_obj.scene_season,
|
||||
'episodenumber': ep_obj.scene_episode} + ' %s' % add_string
|
||||
sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season,
|
||||
'episodenumber': ep_obj.scene_episode} + '|' + \
|
||||
sickbeard.config.naming_ep_type[0] % {'seasonnumber': ep_obj.scene_season,
|
||||
'episodenumber': ep_obj.scene_episode} + ' %s' % add_string
|
||||
search_string['Episode'].append(re.sub('\s+', ' ', ep_string))
|
||||
|
||||
return [search_string]
|
||||
|
@ -230,13 +223,16 @@ class ThePirateBayProvider(generic.TorrentProvider):
|
|||
|
||||
for mode in search_params.keys():
|
||||
for search_string in search_params[mode]:
|
||||
search_string, url = self._get_title_and_url([search_string, '', '', '', ''])
|
||||
if isinstance(search_string, unicode):
|
||||
search_string = unidecode(search_string)
|
||||
|
||||
if mode != 'RSS':
|
||||
searchURL = self.proxy._buildURL(self.searchurl % (urllib.quote(unidecode(search_string))))
|
||||
if 'RSS' != mode:
|
||||
searchURL = self.proxy._buildURL(self.searchurl % (urllib.quote(search_string)))
|
||||
else:
|
||||
searchURL = self.proxy._buildURL(self.url + 'tv/latest/')
|
||||
|
||||
logger.log(u"Search string: " + searchURL, logger.DEBUG)
|
||||
logger.log(u'Search string: ' + searchURL, logger.DEBUG)
|
||||
|
||||
data = self.getURL(searchURL)
|
||||
if not data:
|
||||
|
@ -244,29 +240,28 @@ class ThePirateBayProvider(generic.TorrentProvider):
|
|||
|
||||
re_title_url = self.proxy._buildRE(self.re_title_url)
|
||||
|
||||
#Extracting torrent information from data returned by searchURL
|
||||
# Extracting torrent information from data returned by searchURL
|
||||
match = re.compile(re_title_url, re.DOTALL).finditer(urllib.unquote(data))
|
||||
for torrent in match:
|
||||
|
||||
title = torrent.group('title').replace('_',
|
||||
'.') #Do not know why but SickBeard skip release with '_' in name
|
||||
title = torrent.group('title').replace('_', '.') # Do not know why but SickBeard skip release with '_' in name
|
||||
url = torrent.group('url')
|
||||
id = int(torrent.group('id'))
|
||||
seeders = int(torrent.group('seeders'))
|
||||
leechers = int(torrent.group('leechers'))
|
||||
|
||||
#Filter unseeded torrent
|
||||
if mode != 'RSS' and (seeders < self.minseed or leechers < self.minleech):
|
||||
# Filter unseeded torrent
|
||||
if 'RSS' != mode and (self.minseed > seeders or self.minleech > leechers):
|
||||
continue
|
||||
|
||||
#Accept Torrent only from Good People for every Episode Search
|
||||
# Accept Torrent only from Good People for every Episode Search
|
||||
if self.confirmed and re.search('(VIP|Trusted|Helper|Moderator)', torrent.group(0)) is None:
|
||||
logger.log(u"ThePirateBay Provider found result " + torrent.group(
|
||||
'title') + " but that doesn't seem like a trusted result so I'm ignoring it", logger.DEBUG)
|
||||
logger.log(u'ThePirateBay Provider found result ' + torrent.group(
|
||||
'title') + ' but that doesn\'t seem like a trusted result so I\'m ignoring it', logger.DEBUG)
|
||||
continue
|
||||
|
||||
#Check number video files = episode in season and find the real Quality for full season torrent analyzing files in torrent
|
||||
if mode == 'Season' and search_mode == 'sponly':
|
||||
# Check number video files = episode in season and find the real Quality for full season torrent analyzing files in torrent
|
||||
if 'Season' == mode and 'sponly' == search_mode:
|
||||
ep_number = int(epcount / len(set(allPossibleShowNames(self.show))))
|
||||
title = self._find_season_quality(title, id, ep_number)
|
||||
|
||||
|
@ -277,7 +272,7 @@ class ThePirateBayProvider(generic.TorrentProvider):
|
|||
|
||||
items[mode].append(item)
|
||||
|
||||
#For each search mode sort all the items by seeders
|
||||
# For each search mode sort all the items by seeders
|
||||
items[mode].sort(key=lambda tup: tup[3], reverse=True)
|
||||
|
||||
results += items[mode]
|
||||
|
@ -289,12 +284,13 @@ class ThePirateBayProvider(generic.TorrentProvider):
|
|||
title, url, id, seeders, leechers = item
|
||||
|
||||
if title:
|
||||
title = u'' + title.replace(' ', '.')
|
||||
title += u''
|
||||
title = re.sub(r'\s+', '.', title)
|
||||
|
||||
if url:
|
||||
url = url.replace('&', '&')
|
||||
|
||||
return (title, url)
|
||||
return title, url
|
||||
|
||||
def findPropers(self, search_date=datetime.datetime.today()):
|
||||
|
||||
|
@ -310,13 +306,13 @@ class ThePirateBayProvider(generic.TorrentProvider):
|
|||
)
|
||||
|
||||
if not sqlResults:
|
||||
return []
|
||||
return results
|
||||
|
||||
for sqlshow in sqlResults:
|
||||
self.show = helpers.findCertainShow(sickbeard.showList, int(sqlshow["showid"]))
|
||||
self.show = helpers.findCertainShow(sickbeard.showList, int(sqlshow['showid']))
|
||||
|
||||
if self.show:
|
||||
curEp = self.show.getEpisode(int(sqlshow["season"]), int(sqlshow["episode"]))
|
||||
curEp = self.show.getEpisode(int(sqlshow['season']), int(sqlshow['episode']))
|
||||
|
||||
searchString = self._get_episode_search_strings(curEp, add_string='PROPER|REPACK')
|
||||
|
||||
|
@ -353,14 +349,13 @@ class ThePirateBayWebproxy:
|
|||
|
||||
self.urls = {
|
||||
'Getprivate.eu (NL)': 'http://getprivate.eu/',
|
||||
'15bb51.info (US)': 'http://15bb51.info/',
|
||||
'Hideme.nl (NL)': 'http://hideme.nl/',
|
||||
'Proxite.eu (DE)': 'http://proxite.eu/',
|
||||
'Webproxy.cz (CZ)': 'http://webproxy.cz/',
|
||||
'2me2u (CZ)': 'http://2me2u.me/',
|
||||
'Interproxy.net (EU)': 'http://interproxy.net/',
|
||||
'Unblockersurf.info (DK)': 'http://unblockersurf.info/',
|
||||
'Hiload.org (NL)': 'http://hiload.org/',
|
||||
'Hiload.org (NL) SSL': 'https://hiload.org/',
|
||||
'Interproxy.net (EU)': 'http://interproxy.net/',
|
||||
'Interproxy.net (EU) SSL': 'https://interproxy.net/',
|
||||
'Proxite.eu (DE)': 'http://proxite.eu/',
|
||||
'Proxite.eu (DE) SSL ': 'https://proxite.eu/',
|
||||
}
|
||||
|
||||
def isEnabled(self):
|
||||
|
|
|
@ -43,12 +43,11 @@ class TorrentBytesProvider(generic.TorrentProvider):
|
|||
'login': 'https://www.torrentbytes.net/takelogin.php',
|
||||
'detail': 'https://www.torrentbytes.net/details.php?id=%s',
|
||||
'search': 'https://www.torrentbytes.net/browse.php?search=%s%s',
|
||||
'download': 'https://www.torrentbytes.net/download.php?id=%s&name=%s',
|
||||
}
|
||||
'download': 'https://www.torrentbytes.net/download.php?id=%s&SSL=1&name=%s'}
|
||||
|
||||
def __init__(self):
|
||||
|
||||
generic.TorrentProvider.__init__(self, "TorrentBytes")
|
||||
generic.TorrentProvider.__init__(self, 'TorrentBytes')
|
||||
|
||||
self.supportsBacklog = True
|
||||
|
||||
|
@ -63,7 +62,7 @@ class TorrentBytesProvider(generic.TorrentProvider):
|
|||
|
||||
self.url = self.urls['base_url']
|
||||
|
||||
self.categories = "&c41=1&c33=1&c38=1&c32=1&c37=1"
|
||||
self.categories = '&c41=1&c33=1&c38=1&c32=1&c37=1'
|
||||
|
||||
def isEnabled(self):
|
||||
return self.enabled
|
||||
|
@ -80,8 +79,7 @@ class TorrentBytesProvider(generic.TorrentProvider):
|
|||
|
||||
login_params = {'username': self.username,
|
||||
'password': self.password,
|
||||
'login': 'submit'
|
||||
}
|
||||
'login': 'Log in!'}
|
||||
|
||||
self.session = requests.Session()
|
||||
|
||||
|
@ -92,7 +90,7 @@ class TorrentBytesProvider(generic.TorrentProvider):
|
|||
return False
|
||||
|
||||
if re.search('Username or password incorrect', response.text):
|
||||
logger.log(u'Invalid username or password for ' + self.name + ' Check your settings', logger.ERROR)
|
||||
logger.log(u'Your authentication credentials for ' + self.name + ' are incorrect, check your config.', logger.ERROR)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
@ -104,9 +102,9 @@ class TorrentBytesProvider(generic.TorrentProvider):
|
|||
if ep_obj.show.air_by_date or ep_obj.show.sports:
|
||||
ep_string = show_name + '.' + str(ep_obj.airdate).split('-')[0]
|
||||
elif ep_obj.show.anime:
|
||||
ep_string = show_name + '.' + "%d" % ep_obj.scene_absolute_number
|
||||
ep_string = show_name + '.' + '%d' % ep_obj.scene_absolute_number
|
||||
else:
|
||||
ep_string = show_name + '.S%02d' % int(ep_obj.scene_season) #1) showName SXX
|
||||
ep_string = show_name + '.S%02d' % int(ep_obj.scene_season) # 1) showName SXX
|
||||
|
||||
search_string['Season'].append(ep_string)
|
||||
|
||||
|
@ -122,24 +120,24 @@ class TorrentBytesProvider(generic.TorrentProvider):
|
|||
if self.show.air_by_date:
|
||||
for show_name in set(show_name_helpers.allPossibleShowNames(self.show)):
|
||||
ep_string = sanitizeSceneName(show_name) + ' ' + \
|
||||
str(ep_obj.airdate).replace('-', '|')
|
||||
str(ep_obj.airdate).replace('-', '|')
|
||||
search_string['Episode'].append(ep_string)
|
||||
elif self.show.sports:
|
||||
for show_name in set(show_name_helpers.allPossibleShowNames(self.show)):
|
||||
ep_string = sanitizeSceneName(show_name) + ' ' + \
|
||||
str(ep_obj.airdate).replace('-', '|') + '|' + \
|
||||
ep_obj.airdate.strftime('%b')
|
||||
str(ep_obj.airdate).replace('-', '|') + '|' + \
|
||||
ep_obj.airdate.strftime('%b')
|
||||
search_string['Episode'].append(ep_string)
|
||||
elif self.show.anime:
|
||||
for show_name in set(show_name_helpers.allPossibleShowNames(self.show)):
|
||||
ep_string = sanitizeSceneName(show_name) + ' ' + \
|
||||
"%i" % int(ep_obj.scene_absolute_number)
|
||||
'%i' % int(ep_obj.scene_absolute_number)
|
||||
search_string['Episode'].append(ep_string)
|
||||
else:
|
||||
for show_name in set(show_name_helpers.allPossibleShowNames(self.show)):
|
||||
ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \
|
||||
sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season,
|
||||
'episodenumber': ep_obj.scene_episode}
|
||||
sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season,
|
||||
'episodenumber': ep_obj.scene_episode}
|
||||
|
||||
search_string['Episode'].append(re.sub('\s+', ' ', ep_string))
|
||||
|
||||
|
@ -155,26 +153,28 @@ class TorrentBytesProvider(generic.TorrentProvider):
|
|||
|
||||
for mode in search_params.keys():
|
||||
for search_string in search_params[mode]:
|
||||
|
||||
search_string, url = self._get_title_and_url([search_string, self.urls['search'], '', '', ''])
|
||||
if isinstance(search_string, unicode):
|
||||
search_string = unidecode(search_string)
|
||||
|
||||
searchURL = self.urls['search'] % (search_string, self.categories)
|
||||
|
||||
logger.log(u"Search string: " + searchURL, logger.DEBUG)
|
||||
logger.log(u'Search string: ' + searchURL, logger.DEBUG)
|
||||
|
||||
data = self.getURL(searchURL)
|
||||
if not data:
|
||||
continue
|
||||
|
||||
try:
|
||||
with BS4Parser(data, features=["html5lib", "permissive"]) as html:
|
||||
with BS4Parser(data, features=['html5lib', 'permissive']) as html:
|
||||
torrent_table = html.find('table', attrs={'border': '1'})
|
||||
torrent_rows = torrent_table.find_all('tr') if torrent_table else []
|
||||
torrent_rows = []
|
||||
if torrent_table:
|
||||
torrent_rows = torrent_table.find_all('tr')
|
||||
|
||||
#Continue only if one Release is found
|
||||
# Continue only if one Release is found
|
||||
if len(torrent_rows) < 2:
|
||||
logger.log(u"The Data returned from " + self.name + " do not contains any torrent",
|
||||
logger.log(u'The data returned from ' + self.name + ' does not contain any torrents',
|
||||
logger.DEBUG)
|
||||
continue
|
||||
|
||||
|
@ -184,7 +184,7 @@ class TorrentBytesProvider(generic.TorrentProvider):
|
|||
link = cells[1].find('a', attrs={'class': 'index'})
|
||||
|
||||
full_id = link['href'].replace('details.php?id=', '')
|
||||
torrent_id = full_id.split("&")[0]
|
||||
torrent_id = full_id.split('&')[0]
|
||||
|
||||
try:
|
||||
if link.has_key('title'):
|
||||
|
@ -198,22 +198,22 @@ class TorrentBytesProvider(generic.TorrentProvider):
|
|||
except (AttributeError, TypeError):
|
||||
continue
|
||||
|
||||
#Filter unseeded torrent
|
||||
if mode != 'RSS' and (seeders < self.minseed or leechers < self.minleech):
|
||||
# Filter unseeded torrent
|
||||
if 'RSS' != mode and (seeders < self.minseed or leechers < self.minleech):
|
||||
continue
|
||||
|
||||
if not title or not download_url:
|
||||
continue
|
||||
|
||||
item = title, download_url, id, seeders, leechers
|
||||
logger.log(u"Found result: " + title + "(" + searchURL + ")", logger.DEBUG)
|
||||
logger.log(u'Found result: ' + title + '(' + searchURL + ')', logger.DEBUG)
|
||||
|
||||
items[mode].append(item)
|
||||
|
||||
except Exception, e:
|
||||
logger.log(u"Failed parsing " + self.name + " Traceback: " + traceback.format_exc(), logger.ERROR)
|
||||
logger.log(u'Failed parsing ' + self.name + ' Traceback: ' + traceback.format_exc(), logger.ERROR)
|
||||
|
||||
#For each search mode sort all the items by seeders
|
||||
# For each search mode sort all the items by seeders
|
||||
items[mode].sort(key=lambda tup: tup[3], reverse=True)
|
||||
|
||||
results += items[mode]
|
||||
|
@ -225,14 +225,15 @@ class TorrentBytesProvider(generic.TorrentProvider):
|
|||
title, url, id, seeders, leechers = item
|
||||
|
||||
if title:
|
||||
title = u'' + title
|
||||
title = title.replace(' ', '.').replace(u'\xa0', '')
|
||||
title += u''
|
||||
title = re.sub(r'\s+', '.', title)
|
||||
title = title.replace(u'\xa0', '')
|
||||
|
||||
if url:
|
||||
url = url.replace(u'\xa0', '')
|
||||
url = str(url).replace('&', '&')
|
||||
|
||||
return (title, url)
|
||||
return title, url
|
||||
|
||||
def findPropers(self, search_date=datetime.datetime.today()):
|
||||
|
||||
|
@ -251,9 +252,9 @@ class TorrentBytesProvider(generic.TorrentProvider):
|
|||
return []
|
||||
|
||||
for sqlshow in sqlResults:
|
||||
self.show = helpers.findCertainShow(sickbeard.showList, int(sqlshow["showid"]))
|
||||
self.show = helpers.findCertainShow(sickbeard.showList, int(sqlshow['showid']))
|
||||
if self.show:
|
||||
curEp = self.show.getEpisode(int(sqlshow["season"]), int(sqlshow["episode"]))
|
||||
curEp = self.show.getEpisode(int(sqlshow['season']), int(sqlshow['episode']))
|
||||
|
||||
searchString = self._get_episode_search_strings(curEp, add_string='PROPER|REPACK')
|
||||
|
||||
|
|
|
@ -101,11 +101,11 @@ class TorrentDayProvider(generic.TorrentProvider):
|
|||
return False
|
||||
|
||||
if re.search('You tried too often', response.text):
|
||||
logger.log(u'Too many login access for ' + self.name + ', can''t retrive any data', logger.ERROR)
|
||||
logger.log(u'Too many login attempts for ' + self.name + ', can\'t retrive any data', logger.ERROR)
|
||||
return False
|
||||
|
||||
if response.status_code == 401:
|
||||
logger.log(u'Invalid username or password for ' + self.name + ', Check your settings!', logger.ERROR)
|
||||
logger.log(u'Your authentication credentials for ' + self.name + ' are incorrect, check your config.', logger.ERROR)
|
||||
return False
|
||||
|
||||
if requests.utils.dict_from_cookiejar(self.session.cookies)['uid'] and requests.utils.dict_from_cookiejar(self.session.cookies)['pass']:
|
||||
|
@ -118,7 +118,7 @@ class TorrentDayProvider(generic.TorrentProvider):
|
|||
return True
|
||||
|
||||
else:
|
||||
logger.log(u'Unable to obtain cookie for TorrentDay', logger.ERROR)
|
||||
logger.log(u'Unable to obtain a cookie for TorrentDay', logger.ERROR)
|
||||
return False
|
||||
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ class TorrentLeechProvider(generic.TorrentProvider):
|
|||
if re.search('Invalid Username/password', response.text) \
|
||||
or re.search('<title>Login :: TorrentLeech.org</title>', response.text) \
|
||||
or response.status_code == 401:
|
||||
logger.log(u'Invalid username or password for ' + self.name + ' Check your settings', logger.ERROR)
|
||||
logger.log(u'Your authentication credentials for ' + self.name + ' are incorrect, check your config.', logger.ERROR)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
@ -182,7 +182,7 @@ class TorrentLeechProvider(generic.TorrentProvider):
|
|||
|
||||
#Continue only if one Release is found
|
||||
if len(torrent_rows) < 2:
|
||||
logger.log(u"The Data returned from " + self.name + " do not contains any torrent",
|
||||
logger.log(u"The data returned from " + self.name + " does not contain any torrents",
|
||||
logger.DEBUG)
|
||||
continue
|
||||
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
# Author: Nic Wolfe <nic@wolfeden.ca>
|
||||
# URL: http://code.google.com/p/sickbeard/
|
||||
#
|
||||
# This file is part of SickGear.
|
||||
#
|
||||
# SickGear is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# SickGear is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
try:
|
||||
import xml.etree.cElementTree as etree
|
||||
except ImportError:
|
||||
import elementtree.ElementTree as etree
|
||||
|
||||
import sickbeard
|
||||
import generic
|
||||
|
||||
from sickbeard.exceptions import ex, AuthException
|
||||
from sickbeard import helpers
|
||||
from sickbeard import logger
|
||||
from sickbeard import tvcache
|
||||
|
||||
|
||||
class TvTorrentsProvider(generic.TorrentProvider):
|
||||
def __init__(self):
|
||||
|
||||
generic.TorrentProvider.__init__(self, "TvTorrents")
|
||||
|
||||
self.supportsBacklog = False
|
||||
|
||||
self.enabled = False
|
||||
self.hash = None
|
||||
self.digest = None
|
||||
self.ratio = None
|
||||
self.options = None
|
||||
|
||||
self.cache = TvTorrentsCache(self)
|
||||
|
||||
self.url = 'https://www.tvtorrents.com/'
|
||||
|
||||
def isEnabled(self):
|
||||
return self.enabled
|
||||
|
||||
def imageName(self):
|
||||
return 'tvtorrents.png'
|
||||
|
||||
def _checkAuth(self):
|
||||
|
||||
if not self.digest or not self.hash:
|
||||
raise AuthException("Your authentication credentials for " + self.name + " are missing, check your config.")
|
||||
|
||||
return True
|
||||
|
||||
def _checkAuthFromData(self, data):
|
||||
if data.feed.title:
|
||||
description_text = data.feed.title
|
||||
|
||||
if "User can't be found" in description_text or "Invalid Hash" in description_text:
|
||||
logger.log(u"Incorrect authentication credentials for " + self.name + " : " + str(description_text),
|
||||
logger.DEBUG)
|
||||
raise AuthException(
|
||||
u"Your authentication credentials for " + self.name + " are incorrect, check your config")
|
||||
|
||||
return True
|
||||
|
||||
def seedRatio(self):
|
||||
return self.ratio
|
||||
|
||||
|
||||
class TvTorrentsCache(tvcache.TVCache):
|
||||
def __init__(self, provider):
|
||||
tvcache.TVCache.__init__(self, provider)
|
||||
|
||||
# only poll TvTorrents every 15 minutes max
|
||||
self.minTime = 15
|
||||
|
||||
def _getRSSData(self):
|
||||
# These will be ignored on the serverside.
|
||||
ignore_regex = "all.month|month.of|season[\s\d]*complete"
|
||||
|
||||
rss_url = self.provider.url + 'RssServlet?digest=' + provider.digest + '&hash=' + provider.hash + '&fname=true&exclude=(' + ignore_regex + ')'
|
||||
logger.log(self.provider.name + u" cache update URL: " + rss_url, logger.DEBUG)
|
||||
|
||||
data = self.getRSSFeed(rss_url)
|
||||
|
||||
if not self.provider._checkAuthFromData(data):
|
||||
return []
|
||||
|
||||
if data and 'entries' in data:
|
||||
return data['entries']
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
|
||||
provider = TvTorrentsProvider()
|
|
@ -17,14 +17,15 @@
|
|||
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
|
||||
import urllib, httplib
|
||||
import urllib
|
||||
import httplib
|
||||
import datetime
|
||||
|
||||
import sickbeard
|
||||
|
||||
from lib import MultipartPostHandler
|
||||
import urllib2, cookielib
|
||||
import urllib2
|
||||
import cookielib
|
||||
|
||||
try:
|
||||
import json
|
||||
|
@ -45,13 +46,13 @@ def sendNZB(nzb):
|
|||
|
||||
# set up a dict with the URL params in it
|
||||
params = {}
|
||||
if sickbeard.SAB_USERNAME != None:
|
||||
if sickbeard.SAB_USERNAME is not None:
|
||||
params['ma_username'] = sickbeard.SAB_USERNAME
|
||||
if sickbeard.SAB_PASSWORD != None:
|
||||
if sickbeard.SAB_PASSWORD is not None:
|
||||
params['ma_password'] = sickbeard.SAB_PASSWORD
|
||||
if sickbeard.SAB_APIKEY != None:
|
||||
if sickbeard.SAB_APIKEY is not None:
|
||||
params['apikey'] = sickbeard.SAB_APIKEY
|
||||
if sickbeard.SAB_CATEGORY != None:
|
||||
if sickbeard.SAB_CATEGORY is not None:
|
||||
params['cat'] = sickbeard.SAB_CATEGORY
|
||||
|
||||
# use high priority if specified (recently aired episode)
|
||||
|
@ -64,7 +65,7 @@ def sendNZB(nzb):
|
|||
if nzb.provider.getID() == 'newzbin':
|
||||
id = nzb.provider.getIDFromURL(nzb.url)
|
||||
if not id:
|
||||
logger.log("Unable to send NZB to sab, can't find ID in URL " + str(nzb.url), logger.ERROR)
|
||||
logger.log("Unable to send NZB to SABnzbd, can't find ID in URL " + str(nzb.url), logger.ERROR)
|
||||
return False
|
||||
params['mode'] = 'addid'
|
||||
params['name'] = id
|
||||
|
@ -79,8 +80,8 @@ def sendNZB(nzb):
|
|||
|
||||
url = sickbeard.SAB_HOST + "api?" + urllib.urlencode(params)
|
||||
|
||||
logger.log(u"Sending NZB to SABnzbd")
|
||||
logger.log(u"URL: " + url, logger.DEBUG)
|
||||
logger.log(u"Sending NZB to SABnzbd: %s" % nzb.name)
|
||||
logger.log(u"SABnzbd URL: " + url, logger.DEBUG)
|
||||
|
||||
try:
|
||||
# if we have the URL to an NZB then we've built up the SAB API URL already so just call it
|
||||
|
@ -99,15 +100,15 @@ def sendNZB(nzb):
|
|||
f = opener.open(req)
|
||||
|
||||
except (EOFError, IOError), e:
|
||||
logger.log(u"Unable to connect to SAB: " + ex(e), logger.ERROR)
|
||||
logger.log(u"Unable to connect to SABnzbd: " + ex(e), logger.ERROR)
|
||||
return False
|
||||
|
||||
except httplib.InvalidURL, e:
|
||||
logger.log(u"Invalid SAB host, check your config: " + ex(e), logger.ERROR)
|
||||
logger.log(u"Invalid SABnzbd host, check your config: " + ex(e), logger.ERROR)
|
||||
return False
|
||||
|
||||
# this means we couldn't open the connection or something just as bad
|
||||
if f == None:
|
||||
if f is None:
|
||||
logger.log(u"No data returned from SABnzbd, NZB not sent", logger.ERROR)
|
||||
return False
|
||||
|
||||
|
@ -115,7 +116,7 @@ def sendNZB(nzb):
|
|||
try:
|
||||
result = f.readlines()
|
||||
except Exception, e:
|
||||
logger.log(u"Error trying to get result from SAB, NZB not sent: " + ex(e), logger.ERROR)
|
||||
logger.log(u"Error trying to get result from SABnzbd, NZB not sent: " + ex(e), logger.ERROR)
|
||||
return False
|
||||
|
||||
# SAB shouldn't return a blank result, this most likely (but not always) means that it timed out and didn't recieve the NZB
|
||||
|
@ -126,17 +127,17 @@ def sendNZB(nzb):
|
|||
# massage the result a little bit
|
||||
sabText = result[0].strip()
|
||||
|
||||
logger.log(u"Result text from SAB: " + sabText, logger.DEBUG)
|
||||
logger.log(u"Result text from SABnzbd: " + sabText, logger.DEBUG)
|
||||
|
||||
# do some crude parsing of the result text to determine what SAB said
|
||||
if sabText == "ok":
|
||||
logger.log(u"NZB sent to SAB successfully", logger.DEBUG)
|
||||
logger.log(u"NZB sent to SABnzbd successfully", logger.DEBUG)
|
||||
return True
|
||||
elif sabText == "Missing authentication":
|
||||
logger.log(u"Incorrect username/password sent to SAB, NZB not sent", logger.ERROR)
|
||||
logger.log(u"Incorrect username/password sent to SABnzbd, NZB not sent", logger.ERROR)
|
||||
return False
|
||||
else:
|
||||
logger.log(u"Unknown failure sending NZB to sab. Return text is: " + sabText, logger.ERROR)
|
||||
logger.log(u"Unknown failure sending NZB to SABnzbd. Return text is: " + sabText, logger.ERROR)
|
||||
return False
|
||||
|
||||
|
||||
|
@ -144,12 +145,12 @@ def _checkSabResponse(f):
|
|||
try:
|
||||
result = f.readlines()
|
||||
except Exception, e:
|
||||
logger.log(u"Error trying to get result from SAB" + ex(e), logger.ERROR)
|
||||
return False, "Error from SAB"
|
||||
logger.log(u"Error trying to get result from SABnzbd" + ex(e), logger.ERROR)
|
||||
return False, "Error from SABnzbd"
|
||||
|
||||
if len(result) == 0:
|
||||
logger.log(u"No data returned from SABnzbd, NZB not sent", logger.ERROR)
|
||||
return False, "No data from SAB"
|
||||
return False, "No data from SABnzbd"
|
||||
|
||||
sabText = result[0].strip()
|
||||
sabJson = {}
|
||||
|
@ -159,8 +160,8 @@ def _checkSabResponse(f):
|
|||
pass
|
||||
|
||||
if sabText == "Missing authentication":
|
||||
logger.log(u"Incorrect username/password sent to SAB", logger.ERROR)
|
||||
return False, "Incorrect username/password sent to SAB"
|
||||
logger.log(u"Incorrect username/password sent to SABnzbd", logger.ERROR)
|
||||
return False, "Incorrect username/password sent to SABnzbd"
|
||||
elif 'error' in sabJson:
|
||||
logger.log(sabJson['error'], logger.ERROR)
|
||||
return False, sabJson['error']
|
||||
|
@ -172,12 +173,12 @@ def _sabURLOpenSimple(url):
|
|||
try:
|
||||
f = urllib.urlopen(url)
|
||||
except (EOFError, IOError), e:
|
||||
logger.log(u"Unable to connect to SAB: " + ex(e), logger.ERROR)
|
||||
logger.log(u"Unable to connect to SABnzbd: " + ex(e), logger.ERROR)
|
||||
return False, "Unable to connect"
|
||||
except httplib.InvalidURL, e:
|
||||
logger.log(u"Invalid SAB host, check your config: " + ex(e), logger.ERROR)
|
||||
return False, "Invalid SAB host"
|
||||
if f == None:
|
||||
logger.log(u"Invalid SABnzbd host, check your config: " + ex(e), logger.ERROR)
|
||||
return False, "Invalid SABnzbd host"
|
||||
if f is None:
|
||||
logger.log(u"No data returned from SABnzbd", logger.ERROR)
|
||||
return False, "No data returned from SABnzbd"
|
||||
else:
|
||||
|
|
|
@ -218,6 +218,7 @@ def retrieve_exceptions():
|
|||
|
||||
# write all the exceptions we got off the net into the database
|
||||
myDB = db.DBConnection('cache.db')
|
||||
cl = []
|
||||
for cur_indexer_id in exception_dict:
|
||||
|
||||
# get a list of the existing exceptions for this ID
|
||||
|
@ -236,10 +237,12 @@ def retrieve_exceptions():
|
|||
if not isinstance(cur_exception, unicode):
|
||||
cur_exception = unicode(cur_exception, 'utf-8', 'replace')
|
||||
|
||||
myDB.action("INSERT INTO scene_exceptions (indexer_id, show_name, season) VALUES (?,?,?)",
|
||||
[cur_indexer_id, cur_exception, curSeason])
|
||||
cl.append(["INSERT INTO scene_exceptions (indexer_id, show_name, season) VALUES (?,?,?)",
|
||||
[cur_indexer_id, cur_exception, curSeason]])
|
||||
changed_exceptions = True
|
||||
|
||||
myDB.mass_action(cl)
|
||||
|
||||
# since this could invalidate the results of the cache we clear it out after updating
|
||||
if changed_exceptions:
|
||||
logger.log(u"Updated scene exceptions")
|
||||
|
|
|
@ -481,7 +481,7 @@ def xem_refresh(indexer_id, indexer, force=False):
|
|||
|
||||
if refresh or force:
|
||||
logger.log(
|
||||
u'Looking up XEM scene mapping using for show %s on %s' % (indexer_id, sickbeard.indexerApi(indexer).name,),
|
||||
u'Looking up XEM scene mapping for show %s on %s' % (indexer_id, sickbeard.indexerApi(indexer).name,),
|
||||
logger.DEBUG)
|
||||
|
||||
# mark refreshed
|
||||
|
@ -493,7 +493,7 @@ def xem_refresh(indexer_id, indexer, force=False):
|
|||
try:
|
||||
parsedJSON = sickbeard.helpers.getURL(url, json=True)
|
||||
if not parsedJSON or parsedJSON == '':
|
||||
logger.log(u'No XEN data for show "%s on %s"' % (indexer_id, sickbeard.indexerApi(indexer).name,), logger.MESSAGE)
|
||||
logger.log(u'No XEM data for show "%s on %s"' % (indexer_id, sickbeard.indexerApi(indexer).name,), logger.MESSAGE)
|
||||
return
|
||||
|
||||
if 'success' in parsedJSON['result']:
|
||||
|
|
|
@ -198,23 +198,14 @@ def filter_release_name(name, filter_words):
|
|||
def pickBestResult(results, show, quality_list=None):
|
||||
logger.log(u"Picking the best result out of " + str([x.name for x in results]), logger.DEBUG)
|
||||
|
||||
# build the black And white list
|
||||
bwl = None
|
||||
if show:
|
||||
if show.is_anime:
|
||||
bwl = BlackAndWhiteList(show.indexerid)
|
||||
else:
|
||||
logger.log("Could not create black and white list no show was given", logger.DEBUG)
|
||||
|
||||
# find the best result for the current episode
|
||||
bestResult = None
|
||||
for cur_result in results:
|
||||
|
||||
logger.log("Quality of " + cur_result.name + " is " + Quality.qualityStrings[cur_result.quality])
|
||||
|
||||
if bwl:
|
||||
if not bwl.is_valid(cur_result):
|
||||
logger.log(cur_result.name+" does not match the blacklist or the whitelist, rejecting it. Result: " + bwl.get_last_result_msg(), logger.MESSAGE)
|
||||
if show.is_anime:
|
||||
if not show.release_groups.is_valid(cur_result):
|
||||
continue
|
||||
|
||||
if quality_list and cur_result.quality not in quality_list:
|
||||
|
@ -231,8 +222,9 @@ def pickBestResult(results, show, quality_list=None):
|
|||
logger.MESSAGE)
|
||||
continue
|
||||
|
||||
if sickbeard.USE_FAILED_DOWNLOADS and failed_history.hasFailed(cur_result.name, cur_result.size,
|
||||
cur_result.provider.name):
|
||||
cur_size = getattr(cur_result, 'size', None)
|
||||
if sickbeard.USE_FAILED_DOWNLOADS and None is not cur_size and failed_history.hasFailed(
|
||||
cur_result.name, cur_size, cur_result.provider.name):
|
||||
logger.log(cur_result.name + u" has previously failed, rejecting it")
|
||||
continue
|
||||
|
||||
|
@ -269,9 +261,7 @@ def isFinalResult(result):
|
|||
|
||||
show_obj = result.episodes[0].show
|
||||
|
||||
bwl = None
|
||||
if show_obj.is_anime:
|
||||
bwl = BlackAndWhiteList(show_obj.indexerid)
|
||||
|
||||
|
||||
any_qualities, best_qualities = Quality.splitQuality(show_obj.quality)
|
||||
|
||||
|
@ -280,7 +270,7 @@ def isFinalResult(result):
|
|||
return False
|
||||
|
||||
# if it does not match the shows black and white list its no good
|
||||
elif bwl and not bwl.is_valid(result):
|
||||
elif show_obj.is_anime and show_obj.release_groups.is_valid(result):
|
||||
return False
|
||||
|
||||
# if there's no redownload that's higher (above) and this is the highest initial download then we're good
|
||||
|
@ -307,7 +297,7 @@ def isFirstBestMatch(result):
|
|||
Checks if the given result is a best quality match and if we want to archive the episode on first match.
|
||||
"""
|
||||
|
||||
logger.log(u"Checking if we should archive our first best quality match for for episode " + result.name,
|
||||
logger.log(u"Checking if we should archive our first best quality match for episode " + result.name,
|
||||
logger.DEBUG)
|
||||
|
||||
show_obj = result.episodes[0].show
|
||||
|
@ -561,7 +551,7 @@ def searchProviders(show, episodes, manualSearch=False):
|
|||
# if we need every ep in the season and there's nothing better then just download this and be done with it (unless single episodes are preferred)
|
||||
if allWanted and bestSeasonResult.quality == highest_quality_overall:
|
||||
logger.log(
|
||||
u"Every ep in this season is needed, downloading the whole " + bestSeasonResult.provider.providerType + " " + bestSeasonResult.name)
|
||||
u"Every episode in this season is needed, downloading the whole " + bestSeasonResult.provider.providerType + " " + bestSeasonResult.name)
|
||||
epObjs = []
|
||||
for curEpNum in allEps:
|
||||
epObjs.append(show.getEpisode(season, curEpNum))
|
||||
|
@ -571,7 +561,7 @@ def searchProviders(show, episodes, manualSearch=False):
|
|||
|
||||
elif not anyWanted:
|
||||
logger.log(
|
||||
u"No eps from this season are wanted at this quality, ignoring the result of " + bestSeasonResult.name,
|
||||
u"No episodes from this season are wanted at this quality, ignoring the result of " + bestSeasonResult.name,
|
||||
logger.DEBUG)
|
||||
|
||||
else:
|
||||
|
@ -601,7 +591,7 @@ def searchProviders(show, episodes, manualSearch=False):
|
|||
|
||||
# Season result from Torrent Provider must be a full-season torrent, creating multi-ep result for it.
|
||||
logger.log(
|
||||
u"Adding multi-ep result for full-season torrent. Set the episodes you don't want to 'don't download' in your torrent client if desired!")
|
||||
u"Adding multi episode result for full season torrent. Set the episodes you don't want to 'don't download' in your torrent client if desired!")
|
||||
epObjs = []
|
||||
for curEpNum in allEps:
|
||||
epObjs.append(show.getEpisode(season, curEpNum))
|
||||
|
@ -618,11 +608,11 @@ def searchProviders(show, episodes, manualSearch=False):
|
|||
if MULTI_EP_RESULT in foundResults[curProvider.name]:
|
||||
for multiResult in foundResults[curProvider.name][MULTI_EP_RESULT]:
|
||||
|
||||
logger.log(u"Seeing if we want to bother with multi-episode result " + multiResult.name, logger.DEBUG)
|
||||
logger.log(u"Seeing if we want to bother with multi episode result " + multiResult.name, logger.DEBUG)
|
||||
|
||||
if sickbeard.USE_FAILED_DOWNLOADS and failed_history.hasFailed(multiResult.name, multiResult.size,
|
||||
multiResult.provider.name):
|
||||
logger.log(multiResult.name + u" has previously failed, rejecting this multi-ep result")
|
||||
logger.log(multiResult.name + u" has previously failed, rejecting this multi episode result")
|
||||
continue
|
||||
|
||||
# see how many of the eps that this result covers aren't covered by single results
|
||||
|
@ -637,11 +627,11 @@ def searchProviders(show, episodes, manualSearch=False):
|
|||
notNeededEps.append(epNum)
|
||||
|
||||
logger.log(
|
||||
u"Single-ep check result is neededEps: " + str(neededEps) + ", notNeededEps: " + str(notNeededEps),
|
||||
u"Single episode check result is needed episodes: " + str(neededEps) + ", not needed episodes: " + str(notNeededEps),
|
||||
logger.DEBUG)
|
||||
|
||||
if not notNeededEps:
|
||||
logger.log(u"All of these episodes were covered by single episode results, ignoring this multi-episode result", logger.DEBUG)
|
||||
logger.log(u"All of these episodes were covered by single episode results, ignoring this multi episode result", logger.DEBUG)
|
||||
continue
|
||||
|
||||
# check if these eps are already covered by another multi-result
|
||||
|
@ -655,12 +645,12 @@ def searchProviders(show, episodes, manualSearch=False):
|
|||
multiNeededEps.append(epNum)
|
||||
|
||||
logger.log(
|
||||
u"Multi-ep check result is multiNeededEps: " + str(multiNeededEps) + ", multiNotNeededEps: " + str(
|
||||
u"Multi episode check result is multi needed episodes: " + str(multiNeededEps) + ", multi not needed episodes: " + str(
|
||||
multiNotNeededEps), logger.DEBUG)
|
||||
|
||||
if not multiNeededEps:
|
||||
logger.log(
|
||||
u"All of these episodes were covered by another multi-episode nzbs, ignoring this multi-ep result",
|
||||
u"All of these episodes were covered by another multi episode nzb, ignoring this multi episode result",
|
||||
logger.DEBUG)
|
||||
continue
|
||||
|
||||
|
@ -673,8 +663,8 @@ def searchProviders(show, episodes, manualSearch=False):
|
|||
epNum = epObj.episode
|
||||
if epNum in foundResults[curProvider.name]:
|
||||
logger.log(
|
||||
u"A needed multi-episode result overlaps with a single-episode result for ep #" + str(
|
||||
epNum) + ", removing the single-episode results from the list", logger.DEBUG)
|
||||
u"A needed multi episode result overlaps with a single episode result for episode #" + str(
|
||||
epNum) + ", removing the single episode results from the list", logger.DEBUG)
|
||||
del foundResults[curProvider.name][epNum]
|
||||
|
||||
# of all the single ep results narrow it down to the best one for each episode
|
||||
|
|
|
@ -38,6 +38,8 @@ class BacklogSearchScheduler(scheduler.Scheduler):
|
|||
def nextRun(self):
|
||||
if self.action._lastBacklog <= 1:
|
||||
return datetime.date.today()
|
||||
elif (self.action._lastBacklog + self.action.cycleTime) < datetime.date.today().toordinal():
|
||||
return datetime.date.today()
|
||||
else:
|
||||
return datetime.date.fromordinal(self.action._lastBacklog + self.action.cycleTime)
|
||||
|
||||
|
|
|
@ -32,9 +32,6 @@ from name_parser.parser import NameParser, InvalidNameException, InvalidShowExce
|
|||
from lib.unidecode import unidecode
|
||||
from sickbeard.blackandwhitelist import BlackAndWhiteList
|
||||
|
||||
resultFilters = ["sub(bed|ed|pack|s)", "(dk|fin|heb|kor|nor|nordic|pl|swe)sub(bed|ed|s)?",
|
||||
"(dir|sample|sub|nfo)fix", "sample", "(dvd)?extras",
|
||||
"dub(bed)?"]
|
||||
|
||||
def filterBadReleases(name, parse=True):
|
||||
"""
|
||||
|
@ -56,6 +53,10 @@ def filterBadReleases(name, parse=True):
|
|||
logger.log(u"Unable to parse the filename " + name + " into a valid show", logger.DEBUG)
|
||||
return False
|
||||
|
||||
resultFilters = ['sub(bed|ed|pack|s)', '(dk|fin|heb|kor|nor|nordic|pl|swe)sub(bed|ed|s)?',
|
||||
'(dir|sample|sub|nfo)fix', 'sample', '(dvd)?extras',
|
||||
'dub(bed)?']
|
||||
|
||||
# if any of the bad strings are in the name then say no
|
||||
if sickbeard.IGNORE_WORDS:
|
||||
resultFilters.extend(sickbeard.IGNORE_WORDS.split(','))
|
||||
|
@ -164,7 +165,6 @@ def makeSceneSeasonSearchString(show, ep_obj, extraSearchType=None):
|
|||
numseasons = int(numseasonsSQlResult[0][0])
|
||||
seasonStrings = ["S%02d" % int(ep_obj.scene_season)]
|
||||
|
||||
bwl = BlackAndWhiteList(show.indexerid)
|
||||
showNames = set(makeSceneShowSearchStrings(show, ep_obj.scene_season))
|
||||
|
||||
toReturn = []
|
||||
|
@ -179,8 +179,8 @@ def makeSceneSeasonSearchString(show, ep_obj, extraSearchType=None):
|
|||
# for providers that don't allow multiple searches in one request we only search for Sxx style stuff
|
||||
else:
|
||||
for cur_season in seasonStrings:
|
||||
if len(bwl.whiteList) > 0:
|
||||
for keyword in bwl.whiteList:
|
||||
if len(show.release_groups.whitelist) > 0:
|
||||
for keyword in show.release_groups.whitelist:
|
||||
toReturn.append(keyword + '.' + curShow+ "." + cur_season)
|
||||
else:
|
||||
toReturn.append(curShow + "." + cur_season)
|
||||
|
@ -210,15 +210,14 @@ def makeSceneSearchString(show, ep_obj):
|
|||
if numseasons == 1 and not ep_obj.show.is_anime:
|
||||
epStrings = ['']
|
||||
|
||||
bwl = BlackAndWhiteList(ep_obj.show.indexerid)
|
||||
showNames = set(makeSceneShowSearchStrings(show, ep_obj.scene_season))
|
||||
|
||||
toReturn = []
|
||||
|
||||
for curShow in showNames:
|
||||
for curEpString in epStrings:
|
||||
if len(bwl.whiteList) > 0:
|
||||
for keyword in bwl.whiteList:
|
||||
if len(ep_obj.show.release_groups.whitelist) > 0:
|
||||
for keyword in ep_obj.show.release_groups.whitelist:
|
||||
toReturn.append(keyword + '.' + curShow + '.' + curEpString)
|
||||
else:
|
||||
toReturn.append(curShow + '.' + curEpString)
|
||||
|
|
|
@ -29,7 +29,7 @@ from sickbeard import exceptions, logger, ui, db
|
|||
from sickbeard import generic_queue
|
||||
from sickbeard import name_cache
|
||||
from sickbeard.exceptions import ex
|
||||
|
||||
from sickbeard.blackandwhitelist import BlackAndWhiteList
|
||||
|
||||
class ShowQueue(generic_queue.GenericQueue):
|
||||
def __init__(self):
|
||||
|
@ -105,7 +105,7 @@ class ShowQueue(generic_queue.GenericQueue):
|
|||
|
||||
if (self.isBeingUpdated(show) or self.isInUpdateQueue(show)) and not force:
|
||||
logger.log(
|
||||
u"A refresh was attempted but there is already an update queued or in progress. Since updates do a refres at the end anyway I'm skipping this request.",
|
||||
u"A refresh was attempted but there is already an update queued or in progress. Since updates do a refresh at the end anyway I'm skipping this request.",
|
||||
logger.DEBUG)
|
||||
return
|
||||
|
||||
|
@ -132,9 +132,9 @@ class ShowQueue(generic_queue.GenericQueue):
|
|||
return queueItemObj
|
||||
|
||||
def addShow(self, indexer, indexer_id, showDir, default_status=None, quality=None, flatten_folders=None,
|
||||
lang="en", subtitles=None, anime=None, scene=None, paused=None):
|
||||
lang="en", subtitles=None, anime=None, scene=None, paused=None, blacklist=None, whitelist=None):
|
||||
queueItemObj = QueueItemAdd(indexer, indexer_id, showDir, default_status, quality, flatten_folders, lang,
|
||||
subtitles, anime, scene, paused)
|
||||
subtitles, anime, scene, paused, blacklist, whitelist)
|
||||
|
||||
self.add_item(queueItemObj)
|
||||
|
||||
|
@ -191,7 +191,7 @@ class ShowQueueItem(generic_queue.QueueItem):
|
|||
|
||||
class QueueItemAdd(ShowQueueItem):
|
||||
def __init__(self, indexer, indexer_id, showDir, default_status, quality, flatten_folders, lang, subtitles, anime,
|
||||
scene, paused):
|
||||
scene, paused, blacklist, whitelist):
|
||||
|
||||
self.indexer = indexer
|
||||
self.indexer_id = indexer_id
|
||||
|
@ -204,6 +204,8 @@ class QueueItemAdd(ShowQueueItem):
|
|||
self.anime = anime
|
||||
self.scene = scene
|
||||
self.paused = paused
|
||||
self.blacklist = blacklist
|
||||
self.whitelist = whitelist
|
||||
|
||||
self.show = None
|
||||
|
||||
|
@ -293,6 +295,13 @@ class QueueItemAdd(ShowQueueItem):
|
|||
self.show.scene = self.scene if self.scene != None else sickbeard.SCENE_DEFAULT
|
||||
self.show.paused = self.paused if self.paused != None else False
|
||||
|
||||
if self.show.anime:
|
||||
self.show.release_groups = BlackAndWhiteList(self.show.indexerid)
|
||||
if self.blacklist:
|
||||
self.show.release_groups.set_black_keywords(self.blacklist)
|
||||
if self.whitelist:
|
||||
self.show.release_groups.set_white_keywords(self.whitelist)
|
||||
|
||||
# be smartish about this
|
||||
if self.show.genre and "talk show" in self.show.genre.lower():
|
||||
self.show.air_by_date = 1
|
||||
|
@ -332,7 +341,7 @@ class QueueItemAdd(ShowQueueItem):
|
|||
self.show.loadIMDbInfo()
|
||||
except imdb_exceptions.IMDbError, e:
|
||||
#todo Insert UI notification
|
||||
logger.log(u" Something wrong on IMDb api: " + ex(e), logger.WARNING)
|
||||
logger.log(u"Something is wrong with IMDb api: " + ex(e), logger.WARNING)
|
||||
except Exception, e:
|
||||
logger.log(u"Error loading IMDb info: " + ex(e), logger.ERROR)
|
||||
|
||||
|
@ -361,7 +370,7 @@ class QueueItemAdd(ShowQueueItem):
|
|||
try:
|
||||
self.show.loadEpisodesFromDir()
|
||||
except Exception, e:
|
||||
logger.log(u"Error searching dir for episodes: " + ex(e), logger.ERROR)
|
||||
logger.log(u"Error searching directory for episodes: " + ex(e), logger.ERROR)
|
||||
logger.log(traceback.format_exc(), logger.DEBUG)
|
||||
|
||||
# if they gave a custom status then change all the eps to it
|
||||
|
@ -446,7 +455,7 @@ class QueueItemRename(ShowQueueItem):
|
|||
try:
|
||||
show_loc = self.show.location
|
||||
except exceptions.ShowDirNotFoundException:
|
||||
logger.log(u"Can't perform rename on " + self.show.name + " when the show dir is missing.", logger.WARNING)
|
||||
logger.log(u"Can't perform rename on " + self.show.name + " when the show directory is missing.", logger.WARNING)
|
||||
return
|
||||
|
||||
ep_obj_rename_list = []
|
||||
|
@ -515,7 +524,7 @@ class QueueItemUpdate(ShowQueueItem):
|
|||
try:
|
||||
self.show.loadIMDbInfo()
|
||||
except imdb_exceptions.IMDbError, e:
|
||||
logger.log(u" Something wrong on IMDb api: " + ex(e), logger.WARNING)
|
||||
logger.log(u"Something is wrong with IMDb api: " + ex(e), logger.WARNING)
|
||||
except Exception, e:
|
||||
logger.log(u"Error loading IMDb info: " + ex(e), logger.ERROR)
|
||||
logger.log(traceback.format_exc(), logger.DEBUG)
|
||||
|
|