Merge branch 'release/0.5.0'

This commit is contained in:
adam 2014-12-21 19:40:47 +08:00
commit 72e94116b7
68 changed files with 2818 additions and 3016 deletions

View file

@ -1,3 +1,45 @@
### 0.5.0 (2014-12-21 11:40:00 UTC)
* Fix searches freezing due to unescaped ignored or required words
* Add failed database to unit tests tear down function
* Fix purging of database files in tear down function during unit tests
* Add ability to auto focus Search Show box on Home page and control this option via General Config/Interface
* Change some provider images. Add a few new images
* Remove redundant Coming Eps template code used in the old UI
* Change update Plex notifier (port from SickBeard)
* Change Plex notifications to allow authenticated library updates (port from mmccurdy07/Sick-Beard)
* Change Config/Notifications/Plex logo and description (adapted port from mmccurdy07/Sick-Beard)
* Add ability for CSS/JS to target a specific page and layout
* Remove legacy sickbeard updater and build automation code
* Fix multiple instances of SG being able to start
* Fix garbled text appearing during startup in console
* Fix startup code order and general re-factoring (adapted from midgetspy/Sick-Beard)
* Add database migration code
* Change KickassTorrents provider URLs
* Fix missing Content-Type headers for posters and banners
* Remove config Backup & Restore
* Fix article removal for sorting on Display Show, and API pages
* Fix visual positioning of sprites on Config page
* Fix missing navbar gradients for all browsers
* Update qTip2 to v2.2.1
* Overhaul all Add Show pages
* Fix Display Show next/previous when show list is split
* Change Display Show next/previous when show list is not split to loop around
* Fix SQL statements that have dynamic table names to use proper syntax
* Fix port checking code preventing startup directly after a SG restart
* Add a link from the footer number of snatched to episode snatched overview page. The link to the
Episode Overview page is available on all pages except on the Episode Overview page
* Change the default state for all check boxes on the Episode Overview page to not checked
* Add validation to Go button to ensure at least one item is checked on Episode Overview page
* Add highlight to current status text in header on Episode Overview page
* Fix table alignment on homepage
* Fix duplicate entries in cache database
* Fix network sorting on home page
* Fix restart issue
* Fix to use new TorrentDay URLs
* Fix typo in menu item Manage/Update XBMC
### 0.4.0 (2014-12-04 10:50:00 UTC) ### 0.4.0 (2014-12-04 10:50:00 UTC)
* Change footer stats to not add newlines when copy/pasting from them * Change footer stats to not add newlines when copy/pasting from them

View file

@ -226,13 +226,13 @@ class SickGear(object):
if self.runAsDaemon: if self.runAsDaemon:
pid_dir = os.path.dirname(self.PIDFILE) pid_dir = os.path.dirname(self.PIDFILE)
if not os.access(pid_dir, os.F_OK): if not os.access(pid_dir, os.F_OK):
sys.exit("PID dir: " + pid_dir + " doesn't exist. Exiting.") sys.exit(u"PID dir: %s doesn't exist. Exiting." % pid_dir)
if not os.access(pid_dir, os.W_OK): if not os.access(pid_dir, os.W_OK):
sys.exit("PID dir: " + pid_dir + " must be writable (write permissions). Exiting.") sys.exit(u'PID dir: %s must be writable (write permissions). Exiting.' % pid_dir)
else: else:
if self.consoleLogging: if self.consoleLogging:
sys.stdout.write("Not running in daemon mode. PID file creation disabled.\n") print u'Not running in daemon mode. PID file creation disabled'
self.CREATEPID = False self.CREATEPID = False
@ -244,34 +244,28 @@ class SickGear(object):
if not os.access(sickbeard.DATA_DIR, os.F_OK): if not os.access(sickbeard.DATA_DIR, os.F_OK):
try: try:
os.makedirs(sickbeard.DATA_DIR, 0744) os.makedirs(sickbeard.DATA_DIR, 0744)
except os.error, e: except os.error:
raise SystemExit("Unable to create datadir '" + sickbeard.DATA_DIR + "'") sys.exit(u'Unable to create data directory: %s + Exiting.' % sickbeard.DATA_DIR)
# Make sure we can write to the data dir # Make sure we can write to the data dir
if not os.access(sickbeard.DATA_DIR, os.W_OK): if not os.access(sickbeard.DATA_DIR, os.W_OK):
raise SystemExit("Datadir must be writeable '" + sickbeard.DATA_DIR + "'") sys.exit(u'Data directory: %s must be writable (write permissions). Exiting.' % sickbeard.DATA_DIR)
# Make sure we can write to the config file # Make sure we can write to the config file
if not os.access(sickbeard.CONFIG_FILE, os.W_OK): if not os.access(sickbeard.CONFIG_FILE, os.W_OK):
if os.path.isfile(sickbeard.CONFIG_FILE): if os.path.isfile(sickbeard.CONFIG_FILE):
raise SystemExit("Config file '" + sickbeard.CONFIG_FILE + "' must be writeable.") sys.exit(u'Config file: %s must be writeable (write permissions). Exiting.' % sickbeard.CONFIG_FILE)
elif not os.access(os.path.dirname(sickbeard.CONFIG_FILE), os.W_OK): elif not os.access(os.path.dirname(sickbeard.CONFIG_FILE), os.W_OK):
raise SystemExit( sys.exit(u'Config file directory: %s must be writeable (write permissions). Exiting'
"Config file root dir '" + os.path.dirname(sickbeard.CONFIG_FILE) + "' must be writeable.") % os.path.dirname(sickbeard.CONFIG_FILE))
# Check if we need to perform a restore first
restoreDir = os.path.join(sickbeard.DATA_DIR, 'restore')
if os.path.exists(restoreDir):
if self.restore(restoreDir, sickbeard.DATA_DIR):
logger.log(u"Restore successful...")
else:
logger.log(u"Restore FAILED!", logger.ERROR)
os.chdir(sickbeard.DATA_DIR) os.chdir(sickbeard.DATA_DIR)
if self.consoleLogging:
print u'Starting up SickGear from %s' % sickbeard.CONFIG_FILE
# Load the config and publish it to the sickbeard package # Load the config and publish it to the sickbeard package
if not os.path.isfile(sickbeard.CONFIG_FILE): if not os.path.isfile(sickbeard.CONFIG_FILE):
logger.log(u"Unable to find '" + sickbeard.CONFIG_FILE + "' , all settings will be default!", logger.ERROR) print u'Unable to find "%s", all settings will be default!' % sickbeard.CONFIG_FILE
sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE) sickbeard.CFG = ConfigObj(sickbeard.CONFIG_FILE)
@ -279,15 +273,13 @@ class SickGear(object):
if CUR_DB_VERSION > 0: if CUR_DB_VERSION > 0:
if CUR_DB_VERSION < MIN_DB_VERSION: if CUR_DB_VERSION < MIN_DB_VERSION:
raise SystemExit("Your database version (" + str( print u'Your database version (%s) is too old to migrate from with this version of SickGear' \
CUR_DB_VERSION) + ") is too old to migrate from with this version of SickGear (" + str( % CUR_DB_VERSION
MIN_DB_VERSION) + ").\n" + \ sys.exit(u'Upgrade using a previous version of SG first, or start with no database file to begin fresh')
"Upgrade using a previous version of SB first, or start with no database file to begin fresh.")
if CUR_DB_VERSION > MAX_DB_VERSION: if CUR_DB_VERSION > MAX_DB_VERSION:
raise SystemExit("Your database version (" + str( print u'Your database version (%s) has been incremented past what this version of SickGear supports' \
CUR_DB_VERSION) + ") has been incremented past what this version of SickGear supports (" + str( % CUR_DB_VERSION
MAX_DB_VERSION) + ").\n" + \ sys.exit(u'If you have used other forks of SB, your database may be unusable due to their modifications')
"If you have used other forks of SB, your database may be unusable due to their modifications.")
# Initialize the config and our threads # Initialize the config and our threads
sickbeard.initialize(consoleLogging=self.consoleLogging) sickbeard.initialize(consoleLogging=self.consoleLogging)
@ -298,9 +290,6 @@ class SickGear(object):
# Get PID # Get PID
sickbeard.PID = os.getpid() sickbeard.PID = os.getpid()
# Build from the DB to start with
self.loadShowsFromDB()
if self.forcedPort: if self.forcedPort:
logger.log(u"Forcing web server to port " + str(self.forcedPort)) logger.log(u"Forcing web server to port " + str(self.forcedPort))
self.startPort = self.forcedPort self.startPort = self.forcedPort
@ -339,9 +328,12 @@ class SickGear(object):
# start web server # start web server
try: try:
# used to check if existing SG instances have been started
sickbeard.helpers.wait_for_free_port(self.web_options['host'], self.web_options['port'])
self.webserver = WebServer(self.web_options) self.webserver = WebServer(self.web_options)
self.webserver.start() self.webserver.start()
except IOError: except Exception:
logger.log(u"Unable to start web server, is something else running on port %d?" % self.startPort, logger.log(u"Unable to start web server, is something else running on port %d?" % self.startPort,
logger.ERROR) logger.ERROR)
if sickbeard.LAUNCH_BROWSER and not self.runAsDaemon: if sickbeard.LAUNCH_BROWSER and not self.runAsDaemon:
@ -349,8 +341,16 @@ class SickGear(object):
sickbeard.launchBrowser(self.startPort) sickbeard.launchBrowser(self.startPort)
os._exit(1) os._exit(1)
if self.consoleLogging: # Check if we need to perform a restore first
print "Starting up SickGear " + sickbeard.BRANCH + " from " + sickbeard.CONFIG_FILE restoreDir = os.path.join(sickbeard.DATA_DIR, 'restore')
if os.path.exists(restoreDir):
if self.restore(restoreDir, sickbeard.DATA_DIR):
logger.log(u"Restore successful...")
else:
logger.log_error_and_exit(u"Restore FAILED!", logger.ERROR)
# Build from the DB to start with
self.loadShowsFromDB()
# Fire up all our threads # Fire up all our threads
sickbeard.start() sickbeard.start()
@ -503,17 +503,6 @@ class SickGear(object):
if install_type in ('git', 'source'): if install_type in ('git', 'source'):
popen_list = [sys.executable, sickbeard.MY_FULLNAME] popen_list = [sys.executable, sickbeard.MY_FULLNAME]
elif install_type == 'win':
if hasattr(sys, 'frozen'):
# c:\dir\to\updater.exe 12345 c:\dir\to\sickbeard.exe
popen_list = [os.path.join(sickbeard.PROG_DIR, 'updater.exe'), str(sickbeard.PID),
sys.executable]
else:
logger.log(u"Unknown SB launch method, please file a bug report about this", logger.ERROR)
popen_list = [sys.executable, os.path.join(sickbeard.PROG_DIR, 'updater.py'),
str(sickbeard.PID),
sys.executable,
sickbeard.MY_FULLNAME]
if popen_list: if popen_list:
popen_list += sickbeard.MY_ARGS popen_list += sickbeard.MY_ARGS

View file

@ -1,24 +0,0 @@
2014-10-02
-Move the sql from the *.tmpl files
-Add anidb indexer
-Add supplemental data from anidb during postprocessing
-Fix grabbing non-propers as propers for certain air-by-date shows
-Add prefer torrents/usenet and allow custom delay between grabs from torrents/usenet
-Better postprocessing handling for torrents
2014-10-07
-Add release groups/require words/ignore words to add show page
-Add 'add show and add another show' button to add show page
-Change the hardcoded global ignore words to optional
2014-10-08
-Add login page for http auth as opposed to browser dialog box
2014-10-13
-Fix broken backlog
-Fix season searches
2014-10-21
-Remove qtip from config providers and go back to tab
-Find out why superfish & supersubs js breaks provider sorting
-Ability to save poster sorting and asc/desc in config

View file

@ -171,11 +171,6 @@ inc_top.tmpl
background: url("../images/ico/favicon-16x16.png") 0 0 no-repeat; background: url("../images/ico/favicon-16x16.png") 0 0 no-repeat;
} }
[class^="icon16-"],
[class*=" icon16-"] {
background-image: url("../images/glyphicons-config.png");
}
.ui-autocomplete-loading { .ui-autocomplete-loading {
background: white url("../images/loading16.gif") right center no-repeat; background: white url("../images/loading16.gif") right center no-repeat;
} }
@ -538,10 +533,6 @@ home.tmpl
margin-right: 5px; margin-right: 5px;
} }
.search {
margin-bottom: 10px;
}
.ui-progressbar { .ui-progressbar {
height: 20px; height: 20px;
line-height: 18px; line-height: 18px;
@ -782,122 +773,38 @@ td.tvShow a:hover {
/* ======================================================================= /* =======================================================================
home_addShows.tmpl home_addShows.tmpl
========================================================================== */ ========================================================================== */
#addShowPortal {
width: 700px;
padding: 10px 0;
margin-right: auto;
margin-left: auto;
}
#addShowPortal a {
padding: 10px;
}
div.button {
display: table-cell;
vertical-align: middle;
padding-left: 10px;
}
div.buttontext {
display: table-cell;
padding-left: 20px;
text-align: left;
white-space: normal;
}
div.buttontext h3 {
margin-top: 10px;
}
div.buttontext p {
font-size: 13px;
}
.icon-addnewshow { .icon-addnewshow {
background-image: url("../images/addshows/add-new32-white.png"); background-image: url("../images/addshows/add-new32-white.png");
width: 32px;
height: 32px;
} }
.icon-addtrendingshow { .icon-addtrendingshow {
background-image: url("../images/addshows/add-trending32-white.png"); background-image: url("../images/addshows/add-trending32-white.png");
width: 32px;
height: 32px;
} }
.icon-addrecommendedshow { .icon-addrecommendedshow {
background-image: url("../images/addshows/add-trakt32-white.png"); background-image: url("../images/addshows/add-trakt32-white.png");
width: 32px;
height: 32px;
} }
.icon-addexistingshow { .icon-addexistingshow {
background-image: url("../images/addshows/add-existing32-white.png"); background-image: url("../images/addshows/add-existing32-white.png");
width: 32px;
height: 32px;
} }
/* ======================================================================= /* =======================================================================
home_newShow.tmpl home_newShow.tmpl
========================================================================== */ ========================================================================== */
#addShowForm, #recommendedShowsForm {
margin-left: auto;
margin-right: auto;
}
#newShowPortal {
width: 800px;
padding: 10px 0;
margin-right: auto;
margin-left: auto;
}
#displayText { #displayText {
padding: 8px; background-color: rgb(17, 120, 179);
overflow: hidden; border: 0;
font-size: 14px;
background-color: #3d3d3d;
border: 1px solid #111;
}
#searchResults input[type="radio"] {
vertical-align: -2px;
} }
/* ======================================================================= /* =======================================================================
home_addExistingShow.tmpl home_addExistingShow.tmpl
========================================================================== */ ========================================================================== */
.existingtabs {
padding: 1em 1.4em;
}
ul#rootDirStaticList {
width: 90%;
margin-right: auto;
margin-left: auto;
text-align: left;
}
ul#rootDirStaticList li { ul#rootDirStaticList li {
padding: 4px 5px 4px 5px;
margin: 2px;
list-style: none outside none;
cursor: pointer;
background: #3d3d3d; background: #3d3d3d;
} }
ul#rootDirStaticList li label {
margin-top: 5px;
margin-bottom: 5px;
}
ul#rootDirStaticList li input[type="checkbox"] {
vertical-align: -2px;
}
/* ======================================================================= /* =======================================================================
home_trendingShows.tmpl home_trendingShows.tmpl
========================================================================== */ ========================================================================== */
@ -1541,32 +1448,6 @@ select .selected {
border-top: 1px dotted #666666; border-top: 1px dotted #666666;
} }
[class^="icon16-"], [class*=" icon16-"] {
background-image: url("../images/glyphicons-config.png");
background-repeat: no-repeat;
display: inline-block;
height: 16px;
vertical-align: text-top;
width: 16px;
margin-top: 1px;
}
.icon16-github {
background-position: 0 0;
}
.icon16-mirc {
background-position: -26px 0;
}
.icon16-sg {
background-position: -52px 0;
}
.icon16-web {
background-position: -78px 0;
}
.icon16-win {
background-position: -104px 0;
}
/* ======================================================================= /* =======================================================================
config_postProcessing.tmpl config_postProcessing.tmpl
========================================================================== */ ========================================================================== */
@ -1869,11 +1750,6 @@ body {
background-color: #222; background-color: #222;
} }
html * {
outline: 0 !important;
}
input[type="radio"] { input[type="radio"] {
margin: 2px 0px 0px; margin: 2px 0px 0px;
line-height: normal; line-height: normal;
@ -1897,8 +1773,12 @@ input, textarea, select, .uneditable-input {
.navbar-default { .navbar-default {
background-color: #15528F; background-color: #15528F;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#297AB8', endColorstr='#15528F'); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#297AB8', endColorstr='#15528F');
background: -webkit-gradient(linear, left top, left bottom, from(#297AB8), to(#15528F)); background-image: -ms-linear-gradient(top, #297AB8 0%, #15528F 100%);
background: -moz-linear-gradient(top, #297AB8, #15528F); background-image: -moz-linear-gradient(top, #297AB8 0%, #15528F 100%);
background-image: -o-linear-gradient(top, #297AB8 0%, #15528F 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #297AB8), color-stop(1, #15528F));
background-image: -webkit-linear-gradient(top, #297AB8 0%, #15528F 100%);
background-image: linear-gradient(to bottom, #297AB8 0%, #15528F 100%);
border-color: #3e3f3a; border-color: #3e3f3a;
} }
@ -2033,12 +1913,6 @@ fieldset[disabled] .navbar-default .btn-link:focus {
color: #000000; color: #000000;
} }
.form-control-inline {
min-width: 0;
width: auto;
display: inline;
}
.btn { .btn {
color: #fff; color: #fff;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.75); text-shadow: 0 1px 1px rgba(0, 0, 0, 0.75);
@ -2083,7 +1957,6 @@ fieldset[disabled] .navbar-default .btn-link:focus {
.btn:hover { .btn:hover {
color: #fff; color: #fff;
text-decoration: none;
background-color: #2672B6; background-color: #2672B6;
*background-color: #2672B6; *background-color: #2672B6;
background-position: 0 -150px; background-position: 0 -150px;
@ -2116,8 +1989,7 @@ fieldset[disabled] .navbar-default .btn-link:focus {
.btn.disabled, .btn.disabled,
.btn[disabled] { .btn[disabled] {
cursor: default; cursor: default;
background-color: #15528F; background: #15528F none;
background-image: none;
opacity: 0.65; opacity: 0.65;
filter: alpha(opacity=65); filter: alpha(opacity=65);
-webkit-box-shadow: none; -webkit-box-shadow: none;
@ -2125,19 +1997,6 @@ fieldset[disabled] .navbar-default .btn-link:focus {
box-shadow: none; box-shadow: none;
} }
.btn-large {
padding: 9px 14px;
font-size: 15px;
line-height: normal;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
.btn-large [class^="icon-"] {
margin-top: 1px;
}
.btn-small { .btn-small {
padding: 5px 9px; padding: 5px 9px;
font-size: 11px; font-size: 11px;
@ -2397,60 +2256,16 @@ pre {
/* ======================================================================= /* =======================================================================
input sizing (for config pages) input sizing (for config pages)
========================================================================== */ ========================================================================== */
#pickShow optgroup,
#editAProvider optgroup { #editAProvider optgroup {
color: #eee; color: #eee;
background-color: rgb(51, 51, 51); background-color: rgb(51, 51, 51);
} }
#pickShow optgroup option,
#editAProvider optgroup option { #editAProvider optgroup option {
color: #222; color: #222;
background-color: #fff; background-color: #fff;
}
#config select {
min-width: 0;
width: auto;
display: inline;
margin-top: -4px;
}
.btn-inline {
margin-top: -3px;
}
.input75 {
width: 75px;
margin-top: -4px;
}
.input100 {
width: 100px;
margin-top: -4px;
}
.input150 {
width: 150px;
margin-top: -4px;
}
.input200 {
width: 200px;
margin-top: -4px;
}
.input250 {
width: 250px;
margin-top: -4px;
}
.input300 {
width: 300px;
margin-top: -4px;
}
.input350 {
width: 350px;
margin-top: -4px;
} }
/* ======================================================================= /* =======================================================================
@ -2529,92 +2344,31 @@ browser.css
/* ======================================================================= /* =======================================================================
formWizard.css formWizard
========================================================================== */ ========================================================================== */
.step,
fieldset.sectionwrap {
width: 800px;
padding: 5px;
text-align: left;
border-width: 0;
}
legend.legendStep { legend.legendStep {
color: #ffffff; color: #ffffff;
margin-bottom: 0px;
}
div.stepsguide {
margin-bottom: 15px;
overflow: hidden;
text-align: left;
cursor: pointer;
}
div.stepsguide .step {
float: left;
width: 266px;
font: bold 24px Arial;
} }
div.stepsguide .step p { div.stepsguide .step p {
margin: 12px 0; border-color: #23AFDC;
border-bottom: 4px solid #23AFDC;
} }
div.stepsguide .disabledstep { .disabledstep {
color: #c4c4c4; color: #646464;
} }
div.stepsguide .disabledstep p { div.stepsguide .disabledstep p {
border-bottom: 4px solid #1178B3; border-color: #1178B3;
}
div.stepsguide .step .smalltext {
font-size: 13px;
font-weight: normal;
}
div.formpaginate {
width: 800px;
margin-top: 1em;
overflow: auto;
font-weight: bold;
text-align: center;
} }
div.formpaginate .prev, div.formpaginate .next { div.formpaginate .prev, div.formpaginate .next {
padding: 3px 6px;
color: #fff; color: #fff;
cursor: hand;
cursor: pointer;
background: #2265A1; background: #2265A1;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
border-radius: 6px;
} }
.stepDiv {
padding: 15px;
}
/* step 3 related */
#customQuality {
display: block;
padding: 10px 0;
overflow: hidden;
clear: both;
}
#customQualityWrapper div.component-group-desc {
float: left;
width: 165px;
}
#customQualityWrapper div.component-group-desc p { #customQualityWrapper div.component-group-desc p {
width: 85%;
margin: .8em 0;
font-size: 1.2em;
color: #666; color: #666;
} }
@ -2660,10 +2414,7 @@ tablesorter.css
.tablesorter th, .tablesorter th,
.tablesorter td { .tablesorter td {
padding: 4px; border-color: #222;
border-top: #222 1px solid;
border-left: #222 1px solid;
vertical-align: middle;
} }
/* remove extra border from left edge */ /* remove extra border from left edge */
@ -2674,31 +2425,22 @@ tablesorter.css
.tablesorter th { .tablesorter th {
color: #fff; color: #fff;
text-align: center;
text-shadow: -1px -1px 0 rgba(0,0,0,0.3); text-shadow: -1px -1px 0 rgba(0,0,0,0.3);
background-color: #15528F; background-color: #15528F;
border-collapse: collapse;
font-weight: normal;
} }
.tablesorter .tablesorter-header { .tablesorter .tablesorter-header {
padding: 4px 18px 4px 18px; background: #15528F url(data:image/gif;base64,R0lGODlhFQAJAIAAAP///////yH5BAEAAAEALAAAAAAVAAkAAAIXjI+AywnaYnhUMoqt3gZXPmVg94yJVQAAOw==) no-repeat center right;
cursor: pointer;
background-image: url(data:image/gif;base64,R0lGODlhFQAJAIAAAP///////yH5BAEAAAEALAAAAAAVAAkAAAIXjI+AywnaYnhUMoqt3gZXPmVg94yJVQAAOw==);
background-position: center right;
background-repeat: no-repeat;
/* background-image: url(../images/tablesorter/bg.gif); */ /* background-image: url(../images/tablesorter/bg.gif); */
} }
.tablesorter thead .tablesorter-headerDesc { .tablesorter thead .tablesorter-headerDesc {
background-color: #297AB8; background: #297AB8 url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7) no-repeat center right;
background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7);
/* background-image: url(../images/tablesorter/asc.gif); */ /* background-image: url(../images/tablesorter/asc.gif); */
} }
.tablesorter thead .tablesorter-headerAsc { .tablesorter thead .tablesorter-headerAsc {
background-color: #297AB8; background: #297AB8 url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7) no-repeat center right;
background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7);
/* background-image: url(../images/tablesorter/desc.gif); */ /* background-image: url(../images/tablesorter/desc.gif); */
} }
@ -2761,10 +2503,9 @@ thead.tablesorter-stickyHeader {
.tablesorter tfoot a { .tablesorter tfoot a {
color:#fff; color:#fff;
text-decoration: none;
} }
#showListTable tbody { #showListTable tbody {
color: #000; color: #000;
} }
@ -2903,6 +2644,25 @@ span.token-input-delete-token {
margin: 0 1px; margin: 0 1px;
} }
.step-one #searchResults .alt {
background-color: rgb(40, 40, 40);
}
#addRootDirTable td label .filepath,
.grey-text {
color:#999
}
#newShowPortal #displayText .show-name,
#newShowPortal #displayText .show-dest,
#newShowPortal #displayText p {
color: rgb(200, 200, 200)
}
#newShowPortal #displayText .show-name,
#newShowPortal #displayText .show-dest {
color: rgb(255, 255, 255)
}
/* ======================================================================= /* =======================================================================
jquery.confirm.css jquery.confirm.css
========================================================================== */ ========================================================================== */

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -167,12 +167,7 @@ inc_top.tmpl
background-image: url("../images/menu/menu-icons-white.png"); background-image: url("../images/menu/menu-icons-white.png");
} }
[class^="icon16-"], .ui-autocomplete-loading {
[class*=" icon16-"] {
background-image: url("../images/glyphicons-config.png");
}
.ui-autocomplete-loading {
background: white url("../images/loading16.gif") right center no-repeat; background: white url("../images/loading16.gif") right center no-repeat;
} }
@ -525,10 +520,6 @@ home.tmpl
margin-right: 5px; margin-right: 5px;
} }
.search {
margin-bottom: 10px;
}
.ui-progressbar { .ui-progressbar {
height: 20px; height: 20px;
line-height: 18px; line-height: 18px;
@ -769,122 +760,38 @@ td.tvShow a:hover {
/* ======================================================================= /* =======================================================================
home_addShows.tmpl home_addShows.tmpl
========================================================================== */ ========================================================================== */
#addShowPortal {
width: 700px;
padding: 10px 0;
margin-right: auto;
margin-left: auto;
}
#addShowPortal a {
padding: 10px;
}
div.button {
display: table-cell;
vertical-align: middle;
padding-left: 10px;
}
div.buttontext {
display: table-cell;
padding-left: 20px;
text-align: left;
white-space: normal;
}
div.buttontext h3 {
margin-top: 10px;
}
div.buttontext p {
font-size: 13px;
}
.icon-addnewshow { .icon-addnewshow {
background-image: url("../images/addshows/add-new32-black.png"); background-image: url("../images/addshows/add-new32-black.png");
width: 32px;
height: 32px;
} }
.icon-addtrendingshow { .icon-addtrendingshow {
background-image: url("../images/addshows/add-trending32-black.png"); background-image: url("../images/addshows/add-trending32-black.png");
width: 32px;
height: 32px;
} }
.icon-addrecommendedshow { .icon-addrecommendedshow {
background-image: url("../images/addshows/add-trakt32-black.png"); background-image: url("../images/addshows/add-trakt32-black.png");
width: 32px;
height: 32px;
} }
.icon-addexistingshow { .icon-addexistingshow {
background-image: url("../images/addshows/add-existing32-black.png"); background-image: url("../images/addshows/add-existing32-black.png");
width: 32px;
height: 32px;
} }
/* ======================================================================= /* =======================================================================
home_newShow.tmpl home_newShow.tmpl
========================================================================== */ ========================================================================== */
#addShowForm, #recommendedShowsForm {
margin-left: auto;
margin-right: auto;
}
#newShowPortal {
width: 800px;
padding: 10px 0;
margin-right: auto;
margin-left: auto;
}
#displayText { #displayText {
padding: 8px;
overflow: hidden;
font-size: 14px;
background-color: #efefef; background-color: #efefef;
border: 1px solid #dfdede; border-color: #dfdede;
}
#searchResults input[type="radio"] {
vertical-align: -2px;
} }
/* ======================================================================= /* =======================================================================
home_addExistingShow.tmpl home_addExistingShow.tmpl
========================================================================== */ ========================================================================== */
.existingtabs {
padding: 1em 1.4em;
}
ul#rootDirStaticList {
width: 90%;
margin-right: auto;
margin-left: auto;
text-align: left;
}
ul#rootDirStaticList li { ul#rootDirStaticList li {
padding: 4px 5px 4px 5px;
margin: 2px;
list-style: none outside none;
cursor: pointer;
background: url('../css/lib/images/ui-bg_highlight-soft_75_efefef_1x100.png') repeat-x scroll 50% 50% #EFEFEF; background: url('../css/lib/images/ui-bg_highlight-soft_75_efefef_1x100.png') repeat-x scroll 50% 50% #EFEFEF;
} }
ul#rootDirStaticList li label {
margin-top: 5px;
margin-bottom: 5px;
}
ul#rootDirStaticList li input[type="checkbox"] {
vertical-align: -2px;
}
/* ======================================================================= /* =======================================================================
home_trendingShows.tmpl home_trendingShows.tmpl
========================================================================== */ ========================================================================== */
@ -1518,32 +1425,6 @@ select .selected {
border-top: 1px dotted #666666; border-top: 1px dotted #666666;
} }
[class^="icon16-"], [class*=" icon16-"] {
background-image: url("../images/glyphicons-config.png");
background-repeat: no-repeat;
display: inline-block;
height: 16px;
vertical-align: text-top;
width: 16px;
margin-top: 1px;
}
.icon16-github {
background-position: 0 0;
}
.icon16-mirc {
background-position: -26px 0;
}
.icon16-sg {
background-position: -52px 0;
}
.icon16-web {
background-position: -78px 0;
}
.icon16-win {
background-position: -104px 0;
}
/* ======================================================================= /* =======================================================================
config_postProcessing.tmpl config_postProcessing.tmpl
========================================================================== */ ========================================================================== */
@ -1848,10 +1729,6 @@ body {
color: #000; color: #000;
} }
html * {
outline: 0 !important;
}
input[type="radio"] { input[type="radio"] {
margin: 2px 0px 0px; margin: 2px 0px 0px;
line-height: normal; line-height: normal;
@ -1875,8 +1752,12 @@ input, textarea, select, .uneditable-input {
.navbar-default { .navbar-default {
background-color: #333333; background-color: #333333;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#555555', endColorstr='#333333'); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#555555', endColorstr='#333333');
background: -webkit-gradient(linear, left top, left bottom, from(#555), to(#333)); background-image: -ms-linear-gradient(top, #555555 0%, #333333 100%);
background: -moz-linear-gradient(top, #555, #333); background-image: -moz-linear-gradient(top, #555555 0%, #333333 100%);
background-image: -o-linear-gradient(top, #555555 0%, #333333 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #555555), color-stop(1, #333333));
background-image: -webkit-linear-gradient(top, #555555 0%, #333333 100%);
background-image: linear-gradient(to bottom, #555555 0%, #333333 100%);
border-color: #3e3f3a; border-color: #3e3f3a;
} }
@ -2058,7 +1939,6 @@ fieldset[disabled] .navbar-default .btn-link:focus {
.btn:hover { .btn:hover {
color: #333333; color: #333333;
text-decoration: none;
background-color: #e6e6e6; background-color: #e6e6e6;
*background-color: #d9d9d9; *background-color: #d9d9d9;
background-position: 0 -15px; background-position: 0 -15px;
@ -2089,8 +1969,7 @@ fieldset[disabled] .navbar-default .btn-link:focus {
.btn.disabled, .btn.disabled,
.btn[disabled] { .btn[disabled] {
cursor: default; cursor: default;
background-color: #e6e6e6; background: #e6e6e6 none;
background-image: none;
opacity: 0.65; opacity: 0.65;
filter: alpha(opacity=65); filter: alpha(opacity=65);
-webkit-box-shadow: none; -webkit-box-shadow: none;
@ -2098,19 +1977,6 @@ fieldset[disabled] .navbar-default .btn-link:focus {
box-shadow: none; box-shadow: none;
} }
.btn-large {
padding: 9px 14px;
font-size: 15px;
line-height: normal;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
.btn-large [class^="icon-"] {
margin-top: 1px;
}
.btn-small { .btn-small {
padding: 5px 9px; padding: 5px 9px;
font-size: 11px; font-size: 11px;
@ -2370,60 +2236,16 @@ pre {
/* ======================================================================= /* =======================================================================
input sizing (for config pages) input sizing (for config pages)
========================================================================== */ ========================================================================== */
#pickShow optgroup,
#editAProvider optgroup { #editAProvider optgroup {
color: #eee; color: #eee;
background-color: #888; background-color: #888;
} }
#pickShow optgroup option,
#editAProvider optgroup option { #editAProvider optgroup option {
color: #222; color: #222;
background-color: #fff; background-color: #fff;
}
#config select {
min-width: 0;
width: auto;
display: inline;
margin-top: -4px;
}
.btn-inline {
margin-top: -3px;
}
.input75 {
width: 75px;
margin-top: -4px;
}
.input100 {
width: 100px;
margin-top: -4px;
}
.input150 {
width: 150px;
margin-top: -4px;
}
.input200 {
width: 200px;
margin-top: -4px;
}
.input250 {
width: 250px;
margin-top: -4px;
}
.input300 {
width: 300px;
margin-top: -4px;
}
.input350 {
width: 350px;
margin-top: -4px;
} }
/* ======================================================================= /* =======================================================================
@ -2501,92 +2323,31 @@ browser.css
/* ======================================================================= /* =======================================================================
formWizard.css formWizard
========================================================================== */ ========================================================================== */
.step,
fieldset.sectionwrap {
width: 800px;
padding: 5px;
text-align: left;
border-width: 0;
}
legend.legendStep { legend.legendStep {
color: #57442b; color: #57442b;
margin-bottom: 0px;
}
div.stepsguide {
margin-bottom: 15px;
overflow: hidden;
text-align: left;
cursor: pointer;
}
div.stepsguide .step {
float: left;
width: 266px;
font: bold 24px Arial;
} }
div.stepsguide .step p { div.stepsguide .step p {
margin: 12px 0; border-color: #57442b;
border-bottom: 4px solid #57442b;
} }
div.stepsguide .disabledstep { .disabledstep {
color: #c4c4c4; color: #c4c4c4;
} }
div.stepsguide .disabledstep p { div.stepsguide .disabledstep p {
border-bottom: 4px solid #8a775e; border-color: #8a775e;
}
div.stepsguide .step .smalltext {
font-size: 13px;
font-weight: normal;
}
div.formpaginate {
width: 800px;
margin-top: 1em;
overflow: auto;
font-weight: bold;
text-align: center;
} }
div.formpaginate .prev, div.formpaginate .next { div.formpaginate .prev, div.formpaginate .next {
padding: 3px 6px;
color: #fff; color: #fff;
cursor: hand;
cursor: pointer;
background: #57442b; background: #57442b;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
border-radius: 6px;
} }
.stepDiv {
padding: 15px;
}
/* step 3 related */
#customQuality {
display: block;
padding: 10px 0;
overflow: hidden;
clear: both;
}
#customQualityWrapper div.component-group-desc {
float: left;
width: 165px;
}
#customQualityWrapper div.component-group-desc p { #customQualityWrapper div.component-group-desc p {
width: 85%;
margin: .8em 0;
font-size: 1.2em;
color: #666; color: #666;
} }
@ -2606,10 +2367,7 @@ tablesorter.css
.tablesorter th, .tablesorter th,
.tablesorter td { .tablesorter td {
padding: 4px; border-color: #fff;
border-top: #fff 1px solid;
border-left: #fff 1px solid;
vertical-align: middle;
} }
/* remove extra border from left edge */ /* remove extra border from left edge */
@ -2620,32 +2378,23 @@ tablesorter.css
.tablesorter th { .tablesorter th {
color: #fff; color: #fff;
text-align: center;
text-shadow: -1px -1px 0 rgba(0,0,0,0.3); text-shadow: -1px -1px 0 rgba(0,0,0,0.3);
background-color: #333; background-color: #333;
border-collapse: collapse;
font-weight: normal;
} }
.tablesorter .tablesorter-header { .tablesorter .tablesorter-header {
padding: 4px 18px 4px 18px; background: #333 url(data:image/gif;base64,R0lGODlhFQAJAIAAAP///////yH5BAEAAAEALAAAAAAVAAkAAAIXjI+AywnaYnhUMoqt3gZXPmVg94yJVQAAOw==) no-repeat center right;
cursor: pointer;
background-image: url(data:image/gif;base64,R0lGODlhFQAJAIAAAP///////yH5BAEAAAEALAAAAAAVAAkAAAIXjI+AywnaYnhUMoqt3gZXPmVg94yJVQAAOw==);
background-position: center right;
background-repeat: no-repeat;
/* background-image: url(../images/tablesorter/bg.gif); */ /* background-image: url(../images/tablesorter/bg.gif); */
} }
.tablesorter thead .tablesorter-headerDesc { .tablesorter thead .tablesorter-headerDesc {
background-color: #555; background: #555 url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7) no-repeat center right;
background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7);
/* background-image: url(../images/tablesorter/asc.gif); */ /* background-image: url(../images/tablesorter/asc.gif); */
} }
.tablesorter thead .tablesorter-headerAsc { .tablesorter thead .tablesorter-headerAsc {
background-color: #555; background: #555 url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7) no-repeat center right;
background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7); /* background-image: url(../images/tablesorter/desc.gif); */
/* background-image: url(../images/tablesorter/desc.gif); */
} }
.tablesorter thead .sorter-false { .tablesorter thead .sorter-false {
@ -2707,7 +2456,6 @@ thead.tablesorter-stickyHeader {
.tablesorter tfoot a { .tablesorter tfoot a {
color:#fff; color:#fff;
text-decoration: none;
} }
/* ======================================================================= /* =======================================================================
@ -2844,6 +2592,25 @@ span.token-input-delete-token {
margin: 0 1px; margin: 0 1px;
} }
.step-one #searchResults .alt {
background-color: rgb(245, 245, 245);
}
#addRootDirTable td label .filepath,
.grey-text {
color:#666
}
#newShowPortal #displayText .show-name,
#newShowPortal #displayText .show-dest,
#newShowPortal #displayText p {
color: rgb(153, 153, 153);
}
#newShowPortal #displayText .show-name,
#newShowPortal #displayText .show-dest {
color: rgb(87, 68, 43);
}
/* ======================================================================= /* =======================================================================
jquery.confirm.css jquery.confirm.css
========================================================================== */ ========================================================================== */

View file

@ -167,12 +167,7 @@ inc_top.tmpl
background-image: url("../images/menu/menu-icons-white.png"); background-image: url("../images/menu/menu-icons-white.png");
} }
[class^="icon16-"], .ui-autocomplete-loading {
[class*=" icon16-"] {
background-image: url("../images/glyphicons-config.png");
}
.ui-autocomplete-loading {
background: white url("../images/loading16.gif") right center no-repeat; background: white url("../images/loading16.gif") right center no-repeat;
} }
@ -516,7 +511,6 @@ home.tmpl
border: 1px solid #ccc; border: 1px solid #ccc;
overflow: hidden; overflow: hidden;
height: 66px; height: 66px;
overflow: hidden;
border-radius: 8px; border-radius: 8px;
vertical-align: top; vertical-align: top;
width: 360px; width: 360px;
@ -535,9 +529,13 @@ home.tmpl
margin-right: 5px; margin-right: 5px;
} }
.search { #HomeLayout { margin-top: -35px; }
margin-bottom: 10px; #HomeLayout.not-poster { height: 75px }
} #HomeLayout div.not-poster { position:relative; top:38px; }
#HomeLayout span.not-poster { margin-top: -30px }
#HomeLayout.poster { margin-top: -35px; }
#HomeLayout span.poster { margin-bottom:10px }
#search_show_name { margin-top: 0 }
.ui-progressbar { .ui-progressbar {
height: 20px; height: 20px;
@ -793,31 +791,42 @@ home_addShows.tmpl
========================================================================== */ ========================================================================== */
#addShowPortal { #addShowPortal {
width: 700px; width: 750px;
padding: 10px 0; padding: 10px 0;
margin-right: auto; margin-right: auto;
margin-left: auto; margin-left: auto;
} }
#addShowPortal a { #addShowPortal a {
padding: 10px; /* padding: 10px;*/
padding: 0px 20px;
width: 360px;
float: left;
margin: 0 15px 15px 0;
} }
div.button { div.button {
display: table-cell; display: table-cell;
vertical-align: middle; vertical-align: middle;
padding-left: 10px; /* padding-left: 10px;*/
} }
div.buttontext { div.buttontext {
display: table-cell; display: table-cell;
/*
padding-left: 20px; padding-left: 20px;
padding: 10px 15px;
*/
padding: 10px 0px 10px 15px;
text-align: left; text-align: left;
white-space: normal; white-space: normal;
} }
div.buttontext p {
margin: 0
}
div.buttontext h3 { div.buttontext h3 {
margin-top: 10px; margin-top: 0px;
} }
div.buttontext p { div.buttontext p {
@ -851,13 +860,19 @@ div.buttontext p {
/* ======================================================================= /* =======================================================================
home_newShow.tmpl home_newShow.tmpl
========================================================================== */ ========================================================================== */
#addShowForm, #recommendedShowsForm { #addShowForm,
#newShowPortal,
fieldset.sectionwrap,
div.formpaginate {
width: 801px
}
#addShowForm {
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
} }
#newShowPortal { #newShowPortal {
width: 800px;
padding: 10px 0; padding: 10px 0;
margin-right: auto; margin-right: auto;
margin-left: auto; margin-left: auto;
@ -867,20 +882,44 @@ home_newShow.tmpl
padding: 8px; padding: 8px;
overflow: hidden; overflow: hidden;
font-size: 14px; font-size: 14px;
background-color: #efefef; border: 1px solid;
border: 1px solid #dfdede;
} }
#searchResults input[type="radio"] { #searchResults input[type="radio"] {
vertical-align: -2px; vertical-align: -2px;
} }
#addShowForm #promptForSettings,
#addShowForm #rootDirStaticList input,
#addShowForm div.field-pair input,
#addShowForm div.field-pair select {
margin-right: 6px;
}
#addShowForm #customQualityWrapper div.component-group-desc p {
font-size: 14px;
}
#addShowForm #nameToSearch {
width: 460px;
margin-top: 0
}
#addShowForm #providedIndexer,
#addShowForm #indexerLangSelect {
margin-left: 7px
}
#addShowForm #searchName {
margin-left: 10px
}
/* ======================================================================= /* =======================================================================
home_addExistingShow.tmpl home_addExistingShow.tmpl
========================================================================== */ ========================================================================== */
.existingtabs { .existingtabs {
padding: 1em 1.4em; padding: 20px;
} }
ul#rootDirStaticList { ul#rootDirStaticList {
@ -888,14 +927,15 @@ ul#rootDirStaticList {
margin-right: auto; margin-right: auto;
margin-left: auto; margin-left: auto;
text-align: left; text-align: left;
list-style-type: none;
padding: 0
} }
ul#rootDirStaticList li { ul#rootDirStaticList li {
padding: 4px 5px 4px 5px; padding: 4px 10px;
margin: 2px; margin: 2px 0;
list-style: none outside none; list-style: none outside none;
cursor: pointer; cursor: pointer;
background: url('../css/lib/images/ui-bg_highlight-soft_75_efefef_1x100.png') repeat-x scroll 50% 50% #EFEFEF;
} }
ul#rootDirStaticList li label { ul#rootDirStaticList li label {
@ -1538,15 +1578,27 @@ config*.tmpl
color: #666; color: #666;
} }
#config div.field-pair { .stepDiv #customQualityWrapper h4 {
padding: 12px 0px; margin-top: 6px;
padding: 0 0
} }
.stepDiv div.field-pair {
padding: 0 0 10px
}
#config div.field-pair {
padding: 12px 0
}
.stepDiv .component-desc select,
.stepDiv .component-desc input,
#config div.field-pair select, #config div.field-pair select,
#config div.field-pair input { #config div.field-pair input {
margin-right: 15px; margin-right: 15px;
} }
.stepDiv .component-desc input,
#config div.field-pair input { #config div.field-pair input {
float: left; float: left;
} }
@ -1555,6 +1607,11 @@ config*.tmpl
padding-left: 20px; padding-left: 20px;
} }
.stepDiv span.component-title.input {
line-height: 30px
}
.stepDiv span.component-title,
#config span.component-title { #config span.component-title {
float: left; float: left;
width: 172px; width: 172px;
@ -1563,6 +1620,11 @@ config*.tmpl
font-weight: bold; font-weight: bold;
} }
#addShowForm .stepDiv span.component-desc {
width:578px;
}
.stepDiv span.component-desc,
#config span.component-desc { #config span.component-desc {
font-size: 12px; font-size: 12px;
font-weight: normal; font-weight: normal;
@ -1684,14 +1746,14 @@ select .selected {
border-top: 1px dotted #666666; border-top: 1px dotted #666666;
} }
[class^="icon16-"], [class*=" icon16-"] { [class^="icon16-"],
background-image: url("../images/glyphicons-config.png"); [class*=" icon16-"] {
background-repeat: no-repeat; background: url('../images/glyphicons-config.png') no-repeat;
display: inline-block; display: block;
height: 16px; height: 16px;
vertical-align: text-top;
width: 16px; width: 16px;
margin-top: 1px; float: left;
margin-right: 5px;
} }
.icon16-github { .icon16-github {
@ -2298,9 +2360,9 @@ fieldset[disabled] .navbar-default .btn-link:focus {
border-radius: 5px; border-radius: 5px;
} }
.btn-large [class^="icon-"] { /*.btn-large [class^="icon-"] {
margin-top: 1px; margin-top: 1px;
} }*/
.btn-small { .btn-small {
padding: 5px 9px; padding: 5px 9px;
@ -2681,45 +2743,46 @@ browser.css
/* ======================================================================= /* =======================================================================
formWizard.css formWizard
========================================================================== */ ========================================================================== */
fieldset.sectionwrap { fieldset.sectionwrap {
width: 800px; padding: 5px 0;
padding: 5px;
text-align: left; text-align: left;
border-width: 0; border-width: 0;
} }
.step-one #searchResults legend.legendStep {
margin-top: 10px;
}
div.stepsguide .step,
legend.legendStep { legend.legendStep {
color: #57442b; margin-bottom: 0;
margin-bottom: 0px; }
legend.legendStep p {
padding-left: 15px;
} }
div.stepsguide { div.stepsguide {
margin-bottom: 15px;
overflow: hidden;
text-align: left; text-align: left;
cursor: pointer; cursor: pointer;
} }
div.stepsguide .step { div.stepsguide .step {
float: left; float: left;
width: 266px; width: 267px;
font: bold 24px Arial; font: bold 24px Arial;
} }
div.stepsguide .step p { div.stepsguide .step p {
margin: 12px 0; margin: 12px 0 0;
border-bottom: 4px solid #57442b; border-bottom: 5px solid;
}
div.stepsguide .disabledstep {
color: #c4c4c4;
} }
div.stepsguide .disabledstep p { div.stepsguide .disabledstep p {
border-bottom: 4px solid #8a775e; border-bottom: 2px solid;
} }
div.stepsguide .step .smalltext { div.stepsguide .step .smalltext {
@ -2728,56 +2791,41 @@ div.stepsguide .step .smalltext {
} }
div.formpaginate { div.formpaginate {
width: 800px;
margin-top: 1em; margin-top: 1em;
overflow: auto;
font-weight: bold; font-weight: bold;
text-align: center; text-align: center;
} }
.step-outer {
overflow: hidden
}
div.formpaginate .prev, div.formpaginate .next { div.formpaginate .prev, div.formpaginate .next {
padding: 3px 6px; padding: 3px 6px;
color: #fff;
cursor: hand; cursor: hand;
cursor: pointer; cursor: pointer;
background: #57442b;
-webkit-border-radius: 6px; -webkit-border-radius: 6px;
-moz-border-radius: 6px; -moz-border-radius: 6px;
border-radius: 6px; border-radius: 6px;
} }
.stepDiv { .stepDiv {
padding: 15px; padding: 15px;
} }
.stepDiv.parent-folder {
padding: 15px 0 0;
width: 430px;
margin: 0px auto;
}
#tabs .nocheck,
.stepDiv .nocheck { .stepDiv .nocheck {
padding-left: 16px; padding-left: 16px;
} }
#tabs label span.component-title,
.stepDiv label span.component-title {
padding-bottom: 6px;
float: left;
width: 172px;
margin-right: 10px;
font-size: 13px;
font-weight: bold;
}
#tabs label span.component-desc,
.stepDiv label span.component-desc {
padding-bottom: 6px;
font-size: 12px;
font-weight: normal;
display:inline-block;
width:475px;
}
/* step 3 related */ /* step 3 related */
#customQuality { #customQuality {
display: block; display: block;
padding: 10px 0; padding: 0 0 10px 0;
overflow: hidden; overflow: hidden;
clear: both; clear: both;
} }
@ -2791,7 +2839,6 @@ div.formpaginate .prev, div.formpaginate .next {
width: 85%; width: 85%;
margin: .8em 0; margin: .8em 0;
font-size: 1.2em; font-size: 1.2em;
color: #666;
} }
/* ======================================================================= /* =======================================================================
@ -2811,11 +2858,15 @@ tablesorter.css
.tablesorter th, .tablesorter th,
.tablesorter td { .tablesorter td {
padding: 4px; padding: 4px;
border-top: #fff 1px solid; border-top: 1px solid;
border-left: #fff 1px solid; border-left: 1px solid;
vertical-align: middle; vertical-align: middle;
} }
#addRootDirTable.tablesorter td {
vertical-align: baseline;
}
/* remove extra border from left edge */ /* remove extra border from left edge */
.tablesorter th:first-child, .tablesorter th:first-child,
.tablesorter td:first-child { .tablesorter td:first-child {
@ -2823,10 +2874,7 @@ tablesorter.css
} }
.tablesorter th { .tablesorter th {
color: #fff;
text-align: center; text-align: center;
text-shadow: -1px -1px 0 rgba(0,0,0,0.3);
background-color: #333;
border-collapse: collapse; border-collapse: collapse;
font-weight: normal; font-weight: normal;
} }
@ -2834,22 +2882,7 @@ tablesorter.css
.tablesorter .tablesorter-header { .tablesorter .tablesorter-header {
padding: 4px 18px 4px 18px; padding: 4px 18px 4px 18px;
cursor: pointer; cursor: pointer;
background-image: url(data:image/gif;base64,R0lGODlhFQAJAIAAAP///////yH5BAEAAAEALAAAAAAVAAkAAAIXjI+AywnaYnhUMoqt3gZXPmVg94yJVQAAOw==); vertical-align: middle;
background-position: center right;
background-repeat: no-repeat;
/* background-image: url(../images/tablesorter/bg.gif); */
}
.tablesorter thead .tablesorter-headerDesc {
background-color: #555;
background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjB+gC+jP2ptn0WskLQA7);
/* background-image: url(../images/tablesorter/asc.gif); */
}
.tablesorter thead .tablesorter-headerAsc {
background-color: #555;
background-image: url(data:image/gif;base64,R0lGODlhFQAEAIAAAP///////yH5BAEAAAEALAAAAAAVAAQAAAINjI8Bya2wnINUMopZAQA7);
/* background-image: url(../images/tablesorter/desc.gif); */
} }
.tablesorter thead .sorter-false { .tablesorter thead .sorter-false {
@ -2910,7 +2943,6 @@ thead.tablesorter-stickyHeader {
} }
.tablesorter tfoot a { .tablesorter tfoot a {
color:#fff;
text-decoration: none; text-decoration: none;
} }
@ -3053,6 +3085,42 @@ span.token-input-delete-token {
margin: 0 1px; 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} .boldest {font-weight: 900}
.red-text {color:#d33} .red-text {color:#d33}
.clear-left {clear:left} .clear-left {clear:left}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 B

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 B

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 338 B

After

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 977 B

After

Width:  |  Height:  |  Size: 204 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 268 B

After

Width:  |  Height:  |  Size: 213 B

View file

@ -1,102 +0,0 @@
#import os.path
#import datetime
#import locale
#import sickbeard
#from sickbeard.common import *
#from sickbeard.sbdatetime import *
#from sickbeard import config
#from sickbeard import metadata
#from sickbeard.metadata.generic import GenericMetadata
#set global $title = "Config - Backup/Restore"
#set global $header = "Backup/Restore"
#set global $sbPath="../.."
#set global $topmenu="config"#
#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_top.tmpl")
<script type="text/javascript" src="$sbRoot/js/configBackupRestore.js?$sbPID"></script>
#if $varExists('header')
<h1 class="header">$header</h1>
#else
<h1 class="title">$title</h1>
#end if
#set $indexer = 0
#if $sickbeard.INDEXER_DEFAULT
#set $indexer = $sickbeard.INDEXER_DEFAULT
#end if
<script type="text/javascript" src="$sbRoot/js/config.js?$sbPID"></script>
<div id="config">
<div id="config-content">
<form name="configForm" method="post" action="backuprestore">
<div id="config-components">
<ul>
<li><a href="#core-component-group1">Backup</a></li>
<li><a href="#core-component-group2">Restore</a></li>
</ul>
<div id="core-component-group1" class="component-group clearfix">
<div class="component-group-desc">
<h3>Backup</h3>
<p><b>Backup your main database file and config.</b></p>
</div>
<fieldset class="component-group-list">
<div class="field-pair">
Select the folder you wish to save your backup file to:
<br/><br/>
<input type="text" name="backupDir" id="backupDir" class="form-control input-sm input350" />
<input class="btn btn-inline" type="button" value="Backup" id="Backup" />
<br/>
</div>
<div class="Backup" id="Backup-result"></div>
</fieldset>
</div><!-- /component-group1 //-->
<div id="core-component-group2" class="component-group clearfix">
<div class="component-group-desc">
<h3>Restore</h3>
<p><b>Restore your main database file and config.</b></p>
</div>
<fieldset class="component-group-list">
<div class="field-pair">
Select the backup file you wish to restore:
<br/><br/>
<input type="text" name="backupFile" id="backupFile" class="form-control input-sm input350" />
<input class="btn btn-inline" type="button" value="Restore" id="Restore" />
<br/>
</div>
<div class="Restore" id="Restore-result"></div>
</fieldset>
</div><!-- /component-group2 //-->
</div><!-- /config-components -->
</form>
</div>
</div>
<div class="clearfix"></div>
<script type="text/javascript" charset="utf-8">
<!--
jQuery('#backupDir').fileBrowser({ title: 'Select backup folder to save to', key: 'backupPath' });
jQuery('#backupFile').fileBrowser({ title: 'Select backup files to restore', key: 'backupFile', includeFiles: 1 });
jQuery('#config-components').tabs();
//-->
</script>
#include $os.path.join($sickbeard.PROG_DIR,"gui/slick/interfaces/default/inc_bottom.tmpl")

View file

@ -215,6 +215,16 @@
</label> </label>
</div> </div>
<div class="field-pair">
<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 ''#/>
<p>page refresh on "Show List" will start search box focused</p>
</span>
</label>
</div>
<div class="field-pair"> <div class="field-pair">
<label for="sort_article"> <label for="sort_article">
<span class="component-title">Sort with "The", "A", "An"</span> <span class="component-title">Sort with "The", "A", "An"</span>

View file

@ -159,7 +159,7 @@
<div class="component-group-desc"> <div class="component-group-desc">
<img class="notifier-icon" src="$sbRoot/images/notifiers/plex.png" alt="" title="Plex Media Server" /> <img class="notifier-icon" src="$sbRoot/images/notifiers/plex.png" alt="" title="Plex Media Server" />
<h3><a href="<%= anon_url('http://www.plexapp.com/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Plex Media Server</a></h3> <h3><a href="<%= anon_url('http://www.plexapp.com/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">Plex Media Server</a></h3>
<p>Experience your media on a visually stunning, easy to use interface on your Mac connected to your TV. Your media library has never looked this good!</p> <p>Plex organizes all of your personal media, wherever you keep it, so you can enjoy it on any device</p>
<p class="plexinfo hide">For sending notifications to Plex Home Theater (PHT) clients, use the XBMC notifier with port <b>3005</b>.</p> <p class="plexinfo hide">For sending notifications to Plex Home Theater (PHT) clients, use the XBMC notifier with port <b>3005</b>.</p>
</div> </div>
<fieldset class="component-group-list"> <fieldset class="component-group-list">
@ -222,12 +222,12 @@
</div> </div>
<div class="field-pair"> <div class="field-pair">
<label for="plex_host"> <label for="plex_host">
<span class="component-title">Plex Client IP:Port</span> <span class="component-title">Plex client IP:Port</span>
<input type="text" name="plex_host" id="plex_host" value="$sickbeard.PLEX_HOST" class="form-control input-sm input350" /> <input type="text" name="plex_host" id="plex_host" value="$sickbeard.PLEX_HOST" class="form-control input-sm input350" />
</label> </label>
<label> <label>
<span class="component-title">&nbsp;</span> <span class="component-title">&nbsp;</span>
<span class="component-desc">host running Plex Client (eg. 192.168.1.100:3000)</span> <span class="component-desc">host running Plex client (eg. 192.168.1.100:3000)</span>
</label> </label>
<label> <label>
<span class="component-title">&nbsp;</span> <span class="component-title">&nbsp;</span>
@ -236,22 +236,22 @@
</div> </div>
<div class="field-pair"> <div class="field-pair">
<label for="plex_username"> <label for="plex_username">
<span class="component-title">Plex Client username</span> <span class="component-title">Plex client username</span>
<input type="text" name="plex_username" id="plex_username" value="$sickbeard.PLEX_USERNAME" class="form-control input-sm input250" /> <input type="text" name="plex_username" id="plex_username" value="$sickbeard.PLEX_USERNAME" class="form-control input-sm input250" />
</label> </label>
<label> <label>
<span class="component-title">&nbsp;</span> <span class="component-title">&nbsp;</span>
<span class="component-desc">username for your Plex client API (blank for none)</span> <span class="component-desc">needed if your client / server requires authentication (blank for none)</span>
</label> </label>
</div> </div>
<div class="field-pair"> <div class="field-pair">
<label for="plex_password"> <label for="plex_password">
<span class="component-title">Plex Client password</span> <span class="component-title">Plex client password</span>
<input type="password" name="plex_password" id="plex_password" value="$sickbeard.PLEX_PASSWORD" class="form-control input-sm input250" /> <input type="password" name="plex_password" id="plex_password" value="$sickbeard.PLEX_PASSWORD" class="form-control input-sm input250" />
</label> </label>
<label> <label>
<span class="component-title">&nbsp;</span> <span class="component-title">&nbsp;</span>
<span class="component-desc">password for your Plex client API (blank for none)</span> <span class="component-desc">needed if your client / server requires authentication (blank for none)</span>
</label> </label>
</div> </div>
<div class="testNotification" id="testPLEX-result">Click below to test.</div> <div class="testNotification" id="testPLEX-result">Click below to test.</div>

View file

@ -42,7 +42,7 @@
$(this).qtip({ $(this).qtip({
show: {solo:true}, show: {solo:true},
position: {viewport:$(window), my:'left center', adjust:{ y: -10, x: 2 }}, position: {viewport:$(window), my:'left center', adjust:{ y: -10, x: 2 }},
style: {tip:{corner:true, method:'polygon'}, classes:'qtip-rounded qtip-shadow ui-tooltip-sb'} style: {classes:'qtip-rounded qtip-shadow'}
}); });
}); });
#end raw #end raw
@ -52,7 +52,7 @@
}; };
\$('.imdbstars').generateStars(); \$('.imdbstars').generateStars();
TVShowList = [${tvshow_id_csv}]
}); });
//--> //-->
</script> </script>

View file

@ -65,7 +65,6 @@
<input type="hidden" name="show" value="$show.indexerid" /> <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 /> <b>Location:</b> <input type="text" name="location" id="location" value="$show._location" class="form-control form-control-inline input-sm input350" /><br />
<br /> <br />
<b>Quality:</b>
#set $qualities = $common.Quality.splitQuality(int($show.quality)) #set $qualities = $common.Quality.splitQuality(int($show.quality))
#set global $anyQualities = $qualities[0] #set global $anyQualities = $qualities[0]
#set global $bestQualities = $qualities[1] #set global $bestQualities = $qualities[1]

View file

@ -49,110 +49,112 @@
<!-- <!--
\$.tablesorter.addParser({ \$.tablesorter.addParser({
id: 'loadingNames', id: 'loadingNames',
is: function(s) { is: function(s) {
return false; return false;
}, },
format: function(s) { format: function(s) {
if (s.indexOf('Loading...') == 0) if (s.indexOf('Loading...') == 0)
return s.replace('Loading...','000'); return s.replace('Loading...','000');
else else
#if not $sickbeard.SORT_ARTICLE: #if not $sickbeard.SORT_ARTICLE:
return (s || '').replace(/^(?:(?:A(?!\s+to)n?)|The)\s(\w)/i, '$1'); return (s || '').replace(/^(?:(?:A(?!\s+to)n?)|The)\s(\w)/i, '$1');
#else: #else:
return (s || ''); return (s || '');
#end if #end if
}, },
type: 'text' type: 'text'
}); });
\$.tablesorter.addParser({ \$.tablesorter.addParser({
id: 'quality', id: 'quality',
is: function(s) { is: function(s) {
return false; return false;
}, },
format: function(s) { format: function(s) {
return s.replace('hd1080p',5).replace('hd720p',4).replace('hd',3).replace('sd',2).replace('any',1).replace('custom',7); return s.replace('hd1080p',5).replace('hd720p',4).replace('hd',3).replace('sd',2).replace('any',1).replace('custom',7);
}, },
type: 'numeric' type: 'numeric'
}); });
\$(document).ready(function(){ \$(document).ready(function(){
\$("img#network").on('error', function(){ \$("img#network").on('error', function(){
\$(this).parent().text(\$(this).attr('alt')); \$(this).parent().text(\$(this).attr('alt'));
\$(this).remove(); \$(this).remove();
}); });
\$("#showListTableShows:has(tbody tr)").tablesorter({ \$("#showListTableShows:has(tbody tr)").tablesorter({
sortList: [[5,1],[1,0]], sortList: [[5,1],[1,0]],
textExtraction: { textExtraction: {
0: function(node) { return \$(node).find("span").text().toLowerCase(); }, 0: function(node) { return \$(node).find("span").text().toLowerCase(); },
2: function(node) { return \$(node).find("span").text().toLowerCase(); },
3: function(node) { return \$(node).find("span").text().toLowerCase(); }, 3: function(node) { return \$(node).find("span").text().toLowerCase(); },
4: function(node) { return \$(node).find("span").text(); }, 4: function(node) { return \$(node).find("span").text(); },
5: function(node) { return \$(node).find("img").attr("alt"); } 5: function(node) { return \$(node).find("img").attr("alt"); }
}, },
widgets: ['saveSort', 'zebra', 'stickyHeaders', 'filter'], widgets: ['saveSort', 'zebra', 'stickyHeaders', 'filter'],
headers: { headers: {
0: { sorter: 'isoDate' }, 0: { sorter: 'isoDate' },
1: { sorter: 'loadingNames' }, 1: { sorter: 'loadingNames' },
3: { sorter: 'quality' }, 3: { sorter: 'quality' },
4: { sorter: 'eps' } 4: { sorter: 'eps' }
}, },
widgetOptions : { widgetOptions : {
filter_columnFilters: false, filter_columnFilters: false,
filter_reset: '.resetshows' filter_reset: '.resetshows'
}, },
sortStable: true sortStable: true
}); });
\$("#showListTableAnime:has(tbody tr)").tablesorter({ \$("#showListTableAnime:has(tbody tr)").tablesorter({
sortList: [[5,1],[1,0]], sortList: [[5,1],[1,0]],
textExtraction: { textExtraction: {
0: function(node) { return \$(node).find("span").text().toLowerCase(); }, 0: function(node) { return \$(node).find("span").text().toLowerCase(); },
2: function(node) { return \$(node).find("span").text().toLowerCase(); },
3: function(node) { return \$(node).find("span").text().toLowerCase(); }, 3: function(node) { return \$(node).find("span").text().toLowerCase(); },
4: function(node) { return \$(node).find("span").text(); }, 4: function(node) { return \$(node).find("span").text(); },
5: function(node) { return \$(node).find("img").attr("alt"); } 5: function(node) { return \$(node).find("img").attr("alt"); }
}, },
widgets: ['saveSort', 'zebra', 'stickyHeaders', 'filter'], widgets: ['saveSort', 'zebra', 'stickyHeaders', 'filter'],
headers: { headers: {
0: { sorter: 'isoDate' }, 0: { sorter: 'isoDate' },
1: { sorter: 'loadingNames' }, 1: { sorter: 'loadingNames' },
3: { sorter: 'quality' }, 3: { sorter: 'quality' },
4: { sorter: 'eps' } 4: { sorter: 'eps' }
}, },
widgetOptions : { widgetOptions : {
filter_columnFilters: false, filter_columnFilters: false,
filter_reset: '.resetanime' filter_reset: '.resetanime'
}, },
sortStable: true sortStable: true
}); });
\$.tablesorter.filter.bindSearch( "#showListTableShows", \$('.search') ); \$.tablesorter.filter.bindSearch( "#showListTableShows", \$('.search') );
#if $sickbeard.ANIME_SPLIT_HOME: #if $sickbeard.ANIME_SPLIT_HOME:
\$.tablesorter.filter.bindSearch( "#showListTableAnime", \$('.search') ); \$.tablesorter.filter.bindSearch( "#showListTableAnime", \$('.search') );
#end if #end if
#set $fuzzydate = 'airdate' #set $fuzzydate = 'airdate'
#if $sickbeard.FUZZY_DATING: #if $sickbeard.FUZZY_DATING:
fuzzyMoment({ fuzzyMoment({
dtInline : #if $layout == 'poster' then "true" else "false"#, dtInline : #if $layout == 'poster' then "true" else "false"#,
containerClass : '.${fuzzydate}', containerClass : '.${fuzzydate}',
dateHasTime : false, dateHasTime : false,
dateFormat : '${sickbeard.DATE_PRESET}', dateFormat : '${sickbeard.DATE_PRESET}',
timeFormat : '${sickbeard.TIME_PRESET}', timeFormat : '${sickbeard.TIME_PRESET}',
trimZero : #if $sickbeard.TRIM_ZERO then "true" else "false"# trimZero : #if $sickbeard.TRIM_ZERO then "true" else "false"#
}); });
#end if #end if
var \$container = [\$('#container'), \$('#container-anime')]; var \$container = [\$('#container'), \$('#container-anime')];
jQuery.each(\$container, function (j) { jQuery.each(\$container, function (j) {
this.isotope({ this.isotope({
itemSelector: '.show', itemSelector: '.show',
sortBy : '$sickbeard.POSTER_SORTBY', sortBy : '$sickbeard.POSTER_SORTBY',
sortAscending: $sickbeard.POSTER_SORTDIR, sortAscending: $sickbeard.POSTER_SORTDIR,
layoutMode: 'masonry', layoutMode: 'masonry',
masonry: { masonry: {
columnWidth: 12, columnWidth: 12,
@ -170,7 +172,7 @@
network: '[data-network]', network: '[data-network]',
date: function( itemElem ) { date: function( itemElem ) {
var date = \$( itemElem ).attr('data-date'); var date = \$( itemElem ).attr('data-date');
return date.length && parseInt( date, 10 ) || Number.POSITIVE_INFINITY; return date.length && parseInt( date, 10 ) || Number.POSITIVE_INFINITY;
}, },
progress: function( itemElem ) { progress: function( itemElem ) {
var progress = \$( itemElem ).attr('data-progress'); var progress = \$( itemElem ).attr('data-progress');
@ -184,7 +186,7 @@
var sortValue = this.value; var sortValue = this.value;
\$('#container').isotope({ sortBy: sortValue }); \$('#container').isotope({ sortBy: sortValue });
\$('#container-anime').isotope({ sortBy: sortValue }); \$('#container-anime').isotope({ sortBy: sortValue });
\$.get(this.options[this.selectedIndex].getAttribute('data-sort')); \$.get(this.options[this.selectedIndex].getAttribute('data-sort'));
}); });
\$('#postersortdirection').on( 'change', function() { \$('#postersortdirection').on( 'change', function() {
@ -192,57 +194,64 @@
sortDirection = sortDirection == 'true'; sortDirection = sortDirection == 'true';
\$('#container').isotope({ sortAscending: sortDirection }); \$('#container').isotope({ sortAscending: sortDirection });
\$('#container-anime').isotope({ sortAscending: sortDirection }); \$('#container-anime').isotope({ sortAscending: sortDirection });
\$.get(this.options[this.selectedIndex].getAttribute('data-sort')); \$.get(this.options[this.selectedIndex].getAttribute('data-sort'));
}); });
#if $sickbeard.HOME_SEARCH_FOCUS
\$('#search_show_name').focus();
#end if
}); });
//--> //-->
</script> </script>
#if $varExists('header') #if $varExists('header')
<h1 class="header">$header</h1> <h1 class="header" style="margin-bottom:0">$header</h1>
#else #else
<h1 class="title">$title</h1> <h1 class="title" style="margin-bottom:0">$title</h1>
#end if #end if
<div id="HomeLayout" class="pull-right" style="margin-top: -40px;"> #set $tab = 1
<span> Layout:
<select name="layout" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;"> #if $layout != 'poster':
<div id="HomeLayout" class="pull-right not-poster">
<div class="not-poster">
<input id="search_show_name" class="search form-control form-control-inline input-sm input200" type="search" data-column="1" placeholder="Search Show Name" tabindex="$tab#set $tab += 1#">
&nbsp;<button type="button" class="resetshows resetanime btn btn-inline" tabindex="$tab#set $tab += 1#">Reset Search</button>
</div>
<span class="pull-right not-poster"> Layout:
#else
<div id="HomeLayout" class="pull-right poster">
<span class="pull-right poster">Layout:
#end if
<select name="layout" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;" tabindex="$tab#set $tab += 1#">
<option value="$sbRoot/setHomeLayout/?layout=poster" #if $sickbeard.HOME_LAYOUT == "poster" then "selected=\"selected\"" else ""#>Poster</option> <option value="$sbRoot/setHomeLayout/?layout=poster" #if $sickbeard.HOME_LAYOUT == "poster" then "selected=\"selected\"" else ""#>Poster</option>
<option value="$sbRoot/setHomeLayout/?layout=small" #if $sickbeard.HOME_LAYOUT == "small" then "selected=\"selected\"" else ""#>Small Poster</option> <option value="$sbRoot/setHomeLayout/?layout=small" #if $sickbeard.HOME_LAYOUT == "small" then "selected=\"selected\"" else ""#>Small Poster</option>
<option value="$sbRoot/setHomeLayout/?layout=banner" #if $sickbeard.HOME_LAYOUT == "banner" then "selected=\"selected\"" else ""#>Banner</option> <option value="$sbRoot/setHomeLayout/?layout=banner" #if $sickbeard.HOME_LAYOUT == "banner" then "selected=\"selected\"" else ""#>Banner</option>
<option value="$sbRoot/setHomeLayout/?layout=simple" #if $sickbeard.HOME_LAYOUT == "simple" then "selected=\"selected\"" else ""#>Simple</option> <option value="$sbRoot/setHomeLayout/?layout=simple" #if $sickbeard.HOME_LAYOUT == "simple" then "selected=\"selected\"" else ""#>Simple</option>
</select> </select>
</span> </span>
#if $layout == 'poster': #if $layout == 'poster':
&nbsp; <span>Sort By:
<span> Sort By: <select id="postersort" class="form-control form-control-inline input-sm" tabindex="$tab#set $tab += 1#">
<select id="postersort" class="form-control form-control-inline input-sm"> <option value="name" data-sort="$sbRoot/setPosterSortBy/?sort=name" #if $sickbeard.POSTER_SORTBY == "name" then "selected=\"selected\"" else ""#>Name</option>
<option value="name" data-sort="$sbRoot/setPosterSortBy/?sort=name" #if $sickbeard.POSTER_SORTBY == "name" then "selected=\"selected\"" else ""#>Name</option> <option value="date" data-sort="$sbRoot/setPosterSortBy/?sort=date" #if $sickbeard.POSTER_SORTBY == "date" then "selected=\"selected\"" else ""#>Next Episode</option>
<option value="date" data-sort="$sbRoot/setPosterSortBy/?sort=date" #if $sickbeard.POSTER_SORTBY == "date" then "selected=\"selected\"" else ""#>Next Episode</option> <option value="network" data-sort="$sbRoot/setPosterSortBy/?sort=network" #if $sickbeard.POSTER_SORTBY == "network" then "selected=\"selected\"" else ""#>Network</option>
<option value="network" data-sort="$sbRoot/setPosterSortBy/?sort=network" #if $sickbeard.POSTER_SORTBY == "network" then "selected=\"selected\"" else ""#>Network</option> <option value="progress" data-sort="$sbRoot/setPosterSortBy/?sort=progress" #if $sickbeard.POSTER_SORTBY == "progress" then "selected=\"selected\"" else ""#>Progress</option>
<option value="progress" data-sort="$sbRoot/setPosterSortBy/?sort=progress" #if $sickbeard.POSTER_SORTBY == "progress" then "selected=\"selected\"" else ""#>Progress</option>
</select> </select>
</span> </span>
&nbsp; <span style="margin:0 0 0 5px">Sort Order:
<span> Sort Order: <select id="postersortdirection" class="form-control form-control-inline input-sm" tabindex="$tab#set $tab += 1#">
<select id="postersortdirection" class="form-control form-control-inline input-sm"> <option value="true" data-sort="$sbRoot/setPosterSortDir/?direction=1" #if $sickbeard.POSTER_SORTDIR == 1 then "selected=\"selected\"" else ""#>Asc</option>
<option value="true" data-sort="$sbRoot/setPosterSortDir/?direction=1" #if $sickbeard.POSTER_SORTDIR == 1 then "selected=\"selected\"" else ""#>Asc</option> <option value="false" data-sort="$sbRoot/setPosterSortDir/?direction=0" #if $sickbeard.POSTER_SORTDIR == 0 then "selected=\"selected\"" else ""#>Desc</option>
<option value="false" data-sort="$sbRoot/setPosterSortDir/?direction=0" #if $sickbeard.POSTER_SORTDIR == 0 then "selected=\"selected\"" else ""#>Desc</option>
</select> </select>
</span> </span>
&nbsp; &nbsp;
#end if #end if
</div> </div>
#if $layout != 'poster':
<div class="pull-right">
<input class="search form-control form-control-inline input-sm input200" type="search" data-column="1" placeholder="Search Show Name"> <button type="button" class="resetshows resetanime btn btn-inline">Reset Search</button>
</div>
#end if
#for $curShowlist in $showlists: #for $curShowlist in $showlists:
#set $curListType = $curShowlist[0] #set $curListType = $curShowlist[0]
@ -256,11 +265,11 @@
<div class="posterview"> <div class="posterview">
#for $curLoadingShow in $sickbeard.showQueueScheduler.action.loadingShowList: #for $curLoadingShow in $sickbeard.showQueueScheduler.action.loadingShowList:
#if $curLoadingShow.show != None and $curLoadingShow.show in $sickbeard.showList: #if $curLoadingShow.show != None and $curLoadingShow.show in $sickbeard.showList:
#continue #continue
#end if #end if
#if $curLoadingShow.show == None: #if $curLoadingShow.show == None:
<div class="show" data-name="0" data-date="010101" data-network="0" data-progress="101"> <div class="show" data-name="0" data-date="010101" data-network="0" data-progress="101">
<img alt="" title="$curLoadingShow.show_name" class="show-image" style="border-bottom: 1px solid #111;" src="$sbRoot/images/poster.png" /> <img alt="" title="$curLoadingShow.show_name" class="show-image" style="border-bottom: 1px solid #111;" src="$sbRoot/images/poster.png" />
<div class="show-details"> <div class="show-details">
@ -269,7 +278,7 @@
</div> </div>
#end if #end if
#end for #end for
$myShowList.sort(lambda x, y: cmp(x.name, y.name)) $myShowList.sort(lambda x, y: cmp(x.name, y.name))
@ -328,8 +337,8 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name))
#set $den = 1 #set $den = 1
#end if #end if
#set $progressbar_percent = $nom * 100 / $den #set $progressbar_percent = $nom * 100 / $den
#set $data_date = '6000000000.0' #set $data_date = '6000000000.0'
#if $cur_airs_next: #if $cur_airs_next:
#set $data_date = $time.mktime($sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($cur_airs_next,$curShow.airs,$curShow.network)).timetuple()) #set $data_date = $time.mktime($sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($cur_airs_next,$curShow.airs,$curShow.network)).timetuple())
@ -388,9 +397,9 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name))
#set $output_html = $display_status #set $output_html = $display_status
#end if #end if
#end if #end if
$output_html $output_html
#end if #end if
</div> </div>
<table width="100%" cellspacing="1" border="0" cellpadding="0"> <table width="100%" cellspacing="1" border="0" cellpadding="0">
<tr> <tr>
@ -399,29 +408,28 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name))
</td> </td>
<td class="show-table"> <td class="show-table">
#if $layout != 'simple': #if $layout != 'simple':
#if $curShow.network: #if $curShow.network:
<img class="show-network-image" src="$sbRoot/images/network/${curShow.network.replace(u"\u00C9",'e').lower()}.png" alt="$curShow.network" title="$curShow.network" /> <img class="show-network-image" src="$sbRoot/images/network/${curShow.network.replace(u"\u00C9",'e').lower()}.png" alt="$curShow.network" title="$curShow.network" />
#else: #else:
<img class="show-network-image" src="$sbRoot/images/network/nonetwork.png" alt="No Network" title="No Network" /> <img class="show-network-image" src="$sbRoot/images/network/nonetwork.png" alt="No Network" title="No Network" />
#end if #end if
#else: #else:
$curShow.network $curShow.network
#end if #end if
</td> </td>
<td class="show-table"> <td class="show-table">
#if $curShow.quality in $qualityPresets: #if $curShow.quality in $qualityPresets:
<span class="show-quality">$qualityPresetStrings[$curShow.quality]</span> <span class="show-quality">$qualityPresetStrings[$curShow.quality]</span>
#else: #else:
<span class="show-quality">Custom</span> <span class="show-quality">Custom</span>
#end if #end if
</td> </td>
</tr> </tr>
</table> </table>
</div> </div>
#end for #end for
@ -432,7 +440,7 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name))
<table id="showListTable$curListType" class="tablesorter" cellspacing="1" border="0" cellpadding="0"> <table id="showListTable$curListType" class="tablesorter" cellspacing="1" border="0" cellpadding="0">
<thead> <thead>
<tr> <tr>
<th class="nowrap">Next Ep</th> <th class="nowrap">Next Ep</th>
<th>Show</th> <th>Show</th>
@ -443,37 +451,37 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name))
<th>Status</th> <th>Status</th>
</tr> </tr>
</thead> </thead>
<tfoot> <tfoot>
<tr> <tr>
<th rowspan="1" colspan="1" align="center"><a href="$sbRoot/home/addShows/">Add Show</a></th> <th rowspan="1" colspan="1" align="center"><a href="$sbRoot/home/addShows/">Add Show</a></th>
<th rowspan="1" colspan="6"></th> <th rowspan="1" colspan="6"></th>
</tr> </tr>
</tfoot> </tfoot>
<tbody> <tbody>
#for $curLoadingShow in $sickbeard.showQueueScheduler.action.loadingShowList: #for $curLoadingShow in $sickbeard.showQueueScheduler.action.loadingShowList:
#if $curLoadingShow.show != None and $curLoadingShow.show in $sickbeard.showList: #if $curLoadingShow.show != None and $curLoadingShow.show in $sickbeard.showList:
#continue #continue
#end if
<tr>
<td align="center">(loading)</td>
<td></td>
<td>
#if $curLoadingShow.show == None:
Loading... ($curLoadingShow.show_name)
#else:
<a href="displayShow?show=$curLoadingShow.show.indexerid">$curLoadingShow.show.name</a>
#end if #end if
</td>
<td></td> <tr>
<td></td> <td align="center">(loading)</td>
<td></td> <td>
<td></td> #if $curLoadingShow.show == None:
</tr> Loading... ($curLoadingShow.show_name)
#else:
<a href="displayShow?show=$curLoadingShow.show.indexerid">$curLoadingShow.show.name</a>
#end if
</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
#end for #end for
$myShowList.sort(lambda x, y: cmp(x.name, y.name)) $myShowList.sort(lambda x, y: cmp(x.name, y.name))
@ -524,58 +532,59 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name))
#set $den = 1 #set $den = 1
#end if #end if
#set $progressbar_percent = $nom * 100 / $den #set $progressbar_percent = $nom * 100 / $den
<tr> <tr>
#if $cur_airs_next #if $cur_airs_next
#set $ldatetime = $sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($cur_airs_next,$curShow.airs,$curShow.network)) #set $ldatetime = $sbdatetime.sbdatetime.convert_to_setting($network_timezones.parse_date_time($cur_airs_next,$curShow.airs,$curShow.network))
<td align="center" class="nowrap"><div class="${fuzzydate}">$sbdatetime.sbdatetime.sbfdate($ldatetime)</div><span class="sort_data">$time.mktime($ldatetime.timetuple())</span></td> <td align="center" class="nowrap"><div class="${fuzzydate}">$sbdatetime.sbdatetime.sbfdate($ldatetime)</div><span class="sort_data">$time.mktime($ldatetime.timetuple())</span></td>
#else: #else:
<td align="center" class="nowrap"></td> <td align="center" class="nowrap"></td>
#end if #end if
#if $layout == 'small': #if $layout == 'small':
<td class="tvShow"> <td class="tvShow">
<div class="imgsmallposter $layout"> <div class="imgsmallposter $layout">
<a href="$sbRoot/showPoster/?show=$curShow.indexerid&amp;which=poster" rel="dialog" title="$curShow.name"> <a href="$sbRoot/showPoster/?show=$curShow.indexerid&amp;which=poster" rel="dialog" title="$curShow.name">
<img src="$sbRoot/showPoster/?show=$curShow.indexerid&amp;which=poster_thumb" class="$layout" alt="$curShow.indexerid"/> <img src="$sbRoot/showPoster/?show=$curShow.indexerid&amp;which=poster_thumb" class="$layout" alt="$curShow.indexerid"/>
</a> </a>
<a href="$sbRoot/home/displayShow?show=$curShow.indexerid" style="vertical-align: middle;">$curShow.name</a> <a href="$sbRoot/home/displayShow?show=$curShow.indexerid" style="vertical-align: middle;">$curShow.name</a>
</div> </div>
</td> </td>
#else if $layout == 'banner': #else if $layout == 'banner':
<td> <td>
<span style="display: none;">$curShow.name</span> <span style="display: none;">$curShow.name</span>
<div class="imgbanner $layout"> <div class="imgbanner $layout">
<a href="$sbRoot/home/displayShow?show=$curShow.indexerid"> <a href="$sbRoot/home/displayShow?show=$curShow.indexerid">
<img src="$sbRoot/showPoster/?show=$curShow.indexerid&amp;which=banner" class="$layout" alt="$curShow.indexerid" title="$curShow.name"/> <img src="$sbRoot/showPoster/?show=$curShow.indexerid&amp;which=banner" class="$layout" alt="$curShow.indexerid" title="$curShow.name"/>
</div> </div>
</td> </td>
#else if $layout == 'simple': #else if $layout == 'simple':
<td class="tvShow"><a href="$sbRoot/home/displayShow?show=$curShow.indexerid">$curShow.name</a></td> <td class="tvShow"><a href="$sbRoot/home/displayShow?show=$curShow.indexerid">$curShow.name</a></td>
#end if #end if
#if $layout != 'simple': #if $layout != 'simple':
<td align="center"> <td align="center">
#if $curShow.network: #if $curShow.network:
<img id="network" width="54" height="27" src="$sbRoot/images/network/${curShow.network.replace(u"\u00C9",'e').lower()}.png" alt="$curShow.network" title="$curShow.network" /> <span style="display: none;">$curShow.network</span>
#else: <img id="network" width="54" height="27" src="$sbRoot/images/network/${curShow.network.replace(u"\u00C9",'e').lower()}.png" alt="$curShow.network" title="$curShow.network" />
<img id="network" width="54" height="27" src="$sbRoot/images/network/nonetwork.png" alt="No Network" title="No Network" /> #else:
#end if <img id="network" width="54" height="27" src="$sbRoot/images/network/nonetwork.png" alt="No Network" title="No Network" />
#end if
</td> </td>
#else: #else:
<td> <td>
$curShow.network <span>$curShow.network</span>
</td> </td>
#end if #end if
#if $curShow.quality in $qualityPresets: #if $curShow.quality in $qualityPresets:
<td align="center"><span class="quality $qualityPresetStrings[$curShow.quality]">$qualityPresetStrings[$curShow.quality]</span></td> <td align="center"><span class="quality $qualityPresetStrings[$curShow.quality]">$qualityPresetStrings[$curShow.quality]</span></td>
#else: #else:
<td align="center"><span class="quality Custom">Custom</span></td> <td align="center"><span class="quality Custom">Custom</span></td>
#end if #end if
<td align="center"><span style="display: none;">$progressbar_percent</span><div id="progressbar$curShow.indexerid" style="position:relative;"></div> <td align="center"><span style="display: none;">$progressbar_percent</span><div id="progressbar$curShow.indexerid" style="position:relative;"></div>
<script type="text/javascript"> <script type="text/javascript">
<!-- <!--
@ -601,12 +610,12 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name))
//--> //-->
</script> </script>
</td> </td>
<td align="center"> <td align="center">
<img src="$sbRoot/images/#if int($curShow.paused) == 0 and $curShow.status != "Ended" then "yes16.png\" alt=\"Y\"" else "no16.png\" alt=\"N\""# width="16" height="16" /> <img src="$sbRoot/images/#if int($curShow.paused) == 0 and $curShow.status != "Ended" then "yes16.png\" alt=\"Y\"" else "no16.png\" alt=\"N\""# width="16" height="16" />
</td> </td>
<td align="center"> <td align="center">
#set $display_status = $curShow.status #set $display_status = $curShow.status
#if None is not $display_status #if None is not $display_status
@ -619,7 +628,7 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name))
$display_status $display_status
</td> </td>
</tr> </tr>
#end for #end for
</tbody> </tbody>

View file

@ -1,17 +1,15 @@
#import os.path #import os.path
#import sickbeard #import sickbeard
#from sickbeard.common import * #from sickbeard.common import *
#set global $title="Existing Show" #set global $title = 'Existing Show'
#set global $header="Existing Show" #set global $header = 'Existing Show'
#set global $sbPath="../.." #set global $sbPath = '../..'
#set global $statpath="../.."# #set global $statpath = '../..'
#set global $topmenu="home"# #set global $topmenu = 'home'
#import os.path #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')
<form id="addShowForm" method="post" action="$sbRoot/home/addShows/addNewShow" accept-charset="utf-8">
<script type="text/javascript" src="$sbRoot/js/qualityChooser.js?$sbPID"></script> <script type="text/javascript" src="$sbRoot/js/qualityChooser.js?$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/addExistingShow.js?$sbPID"></script> <script type="text/javascript" src="$sbRoot/js/addExistingShow.js?$sbPID"></script>
@ -21,7 +19,7 @@
<script type="text/javascript" charset="utf-8"> <script type="text/javascript" charset="utf-8">
<!-- <!--
\$(document).ready(function(){ \$(document).ready(function(){
\$( "#tabs" ).tabs({ \$( '#tabs' ).tabs({
collapsible: true, collapsible: true,
selected: #if $sickbeard.ROOT_DIRS then '-1' else '0'# selected: #if $sickbeard.ROOT_DIRS then '-1' else '0'#
}); });
@ -29,41 +27,55 @@
//--> //-->
</script> </script>
#if $varExists('header') #if $varExists('header')
<h1 class="header">$header</h1> <h1 class="header">$header</h1>
#else #else
<h1 class="title">$title</h1> <h1 class="title">$title</h1>
#end if #end if
<div id="tabs"> <image class="preload-image" style="position:absolute;top:-999px" src="$sbRoot/images/loading32<%= '-dark' if 'dark' == sickbeard.THEME_NAME else '' %>.gif" width="32" height="32" border="0">
<ul>
<li><a href="#tabs-1">Manage Directories</a></li>
<li><a href="#tabs-2">Customize Options</a></li>
</ul>
<div id="tabs-1" class="existingtabs">
#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_rootDirs.tmpl")
</div>
<div id="tabs-2" class="existingtabs">
#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_addShowOptions.tmpl")
</div>
</div>
<br />
<p>SickGear can add existing shows, using the current options, by using locally stored NFO/XML metadata to eliminate user interaction.<br /> <form id="addShowForm" method="post" action="$sbRoot/home/addShows/addNewShow" accept-charset="utf-8">
If you would rather have SickGear prompt you to customize each show, then use the checkbox below.</p>
<p><input type="checkbox" name="promptForSettings" id="promptForSettings" /> <label for="promptForSettings">Prompt me to set settings for each show</label></p> <p>Tip: shows are added quicker when usable show nfo and xml metadata is found</p>
<hr /> <p style="margin-top:15px">
<input type="checkbox" id="promptForSettings" name="promptForSettings" style="vertical-align: top;" />
<label for="promptForSettings">Enable to change the following options per show, otherwise use these options with all shows added below</label>
</p>
<h5><b>Displaying folders within these directories which aren't already added to SickGear:</b></h5> <div id="tabs">
<ul>
<li><a href="#tabs-1">Manage parent folders</a></li>
<li><a href="#tabs-2">Custom options</a></li>
</ul>
<div id="tabs-1" class="existingtabs">
<div style="width: 430px; margin: 0px auto">
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_rootDirs.tmpl')
</div>
</div>
<div id="tabs-2">
<div class="stepDiv">
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_addShowOptions.tmpl')
</div>
</div>
</div>
<ul id="rootDirStaticList"><li></li></ul> <br />
<hr />
<br /> <p>The following parent folder(s) are scanned for existing shows. Toggle a folder to display shows</p>
<div id="tableDiv"></div>
<br /> <ul id="rootDirStaticList">
<input class="btn btn-primary" type="button" value="Submit" id="submitShowDirs" /> <li></li>
</ul>
<p>shows <span class="boldest">not known</span> to SickGear are listed below...</p>
<div id="tableDiv"></div>
<br />
<input class="btn btn-primary" type="button" value="Submit" id="submitShowDirs" />
</form> </form>
#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')

View file

@ -1,16 +1,16 @@
#import os.path #import os.path
#import urllib #import urllib
#import sickbeard #import sickbeard
#set global $title="Add Show" #set global $title = 'Add Show'
#set global $header="Add Show" #set global $header = 'Add Show'
#set global $sbPath="../.." #set global $sbPath = '../..'
#set global $statpath="../.."# #set global $statpath = '../..'
#set global $topmenu="home"# #set global $topmenu = 'home'
#import os.path #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') #if $varExists('header')
<h1 class="header">$header</h1> <h1 class="header">$header</h1>
@ -18,46 +18,49 @@
<h1 class="title">$title</h1> <h1 class="title">$title</h1>
#end if #end if
<div id="addShowPortal"> <div id="addShowPortal">
<a href="$sbRoot/home/addShows/newShow/" id="btnNewShow" class="btn btn-large"> <a class="btn btn-large" href="$sbRoot/home/addShows/newShow/">
<div class="button"><div class="icon-addnewshow"></div></div> <div class="button"><div class="icon-addnewshow"></div></div>
<div class="buttontext"> <div class="buttontext">
<h3>Add New Show</h3> <h3>Add New Show</h3>
<p>For shows that you haven't downloaded yet, this option finds a show on theTVDB.com and TVRage.com, creates a directory for its episodes, and adds it to SickGear.</p> <p>Search a TV database for a show to add. A new folder will be created for episodes</p>
</div> </div>
</a> </a>
<br/><br/> <a class="btn btn-large" href="$sbRoot/home/addShows/trendingShows/">
<a href="$sbRoot/home/addShows/trendingShows/" id="btnNewShow" class="btn btn-large"> <div class="button"><div class="icon-addtrendingshow"></div></div>
<div class="button"><div class="icon-addtrendingshow"></div></div> <div class="buttontext">
<div class="buttontext"> <h3>Add From Trending</h3>
<h3>Add Trending Show</h3> <p>Browse a current trending show list to add from. A folder for episodes will be created</p>
<p>For shows that you haven't downloaded yet, this option lets you choose from a list of current trending shows with ratings to add, creates a directory for its episodes, and adds it to SickGear.</p> </div>
</div> </a>
</a>
<br/><br/> <a class="btn btn-large" href="$sbRoot/home/addShows/existingShows/">
#if $sickbeard.USE_TRAKT == True: <div class="button"><div class="icon-addexistingshow"></div></div>
<a href="$sbRoot/home/addShows/recommendedShows/" id="btnNewShow" class="btn btn-large"> <div class="buttontext">
<div class="button"><div class="icon-addrecommendedshow"></div></div> <h3>Add Existing Shows</h3>
<div class="buttontext"> <p>Scan parent folders for shows and episode metadata to import into SickGear</p>
<h3>Add Recommended Shows</h3> </div>
<p>For shows that you haven't downloaded yet, this option recommends shows to add based on your Trakt.tv show library, creates a directory for its episodes, and adds it to SickGear.</p> </a>
</div>
</a>
<br/><br/> #if True == $sickbeard.USE_TRAKT:
#end if <a class="btn btn-large" href="$sbRoot/home/addShows/recommendedShows/">
<a href="$sbRoot/home/addShows/existingShows/" id="btnExistingShow" class="btn btn-large"> <div class="button"><div class="icon-addrecommendedshow"></div></div>
<div class="button"><div class="icon-addexistingshow"></div></div> <div class="buttontext">
<div class="buttontext"> <h3>Add Recommended</h3>
<h3>Add Existing Shows</h3> <p>Browse recommendations based on your Trakt.tv show library to add to SickGear</p>
<p>Use this option to add shows that already have a folder created on your hard drive. SickGear will scan your existing metadata/episodes and add the show accordingly.</p> </div>
</div> </a>
</a> #else
<div class="buttontext" style="padding:10px 5px">
<p>There's more... unlock another button to browse<br />
recommended shows based on your Trakt.tv show<br />
library by enabling Trakt in Config/Notifications/Social</p>
</div>
#end if
</div>
</div> <div style="clear:both">&nbsp;</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")

View file

@ -2,10 +2,10 @@
#from sickbeard.helpers import anon_url #from sickbeard.helpers import anon_url
<table id="addRootDirTable" class="sickbeardTable tablesorter"> <table id="addRootDirTable" class="sickbeardTable tablesorter">
<thead><tr><th class="col-checkbox"><input type="checkbox" id="checkAll" checked=checked></th><th>Directory</th><th width="20%">Show Name (tvshow.nfo)<th width="20%">Indexer</td></tr></thead> <thead><tr><th class="col-checkbox"><input type="checkbox" id="checkAll" checked=checked></th><th>Parent\show folder</th><th width="20%">Show name<br />(tvshow.nfo)<th width="15%">TV database</td></tr></thead>
<tfoot> <tfoot>
<tr> <tr>
<th rowspan="1" colspan="4" align="left"><a href="#" style="padding-left: 10px;" class="showManage">Manage Directories</a></th> <th rowspan="1" colspan="4" align="left"><a href="#" class="showManage">Manage Directories</a></th>
</tr> </tr>
</tfoot> </tfoot>
<tbody> <tbody>
@ -31,7 +31,7 @@
<td class="col-checkbox"><input type="checkbox" id="$show_id" class="dirCheck" checked=checked></td> <td class="col-checkbox"><input type="checkbox" id="$show_id" class="dirCheck" checked=checked></td>
<td><label for="$show_id">$curDir['display_dir']</label></td> <td><label for="$show_id">$curDir['display_dir']</label></td>
#if $curDir['existing_info'][1] and $indexer > 0: #if $curDir['existing_info'][1] and $indexer > 0:
<td><a href="<%= anon_url(sickbeard.indexerApi(indexer).config['show_url'], curDir['existing_info'][0]) %>">$curDir['existing_info'][1]</a></td> <td><a href="<%= anon_url(sickbeard.indexerApi(indexer).config['show_url'], curDir['existing_info'][0]) %>" target="_new">$curDir['existing_info'][1]</a></td>
#else: #else:
<td>?</td> <td>?</td>
#end if #end if

View file

@ -24,15 +24,17 @@
<h1 class="title">$title</h1> <h1 class="title">$title</h1>
#end if #end if
<image class="preload-image" style="position:absolute;top:-999px" src="$sbRoot/images/loading32<%= '-dark' if 'dark' == sickbeard.THEME_NAME else '' %>.gif" width="32" height="32" border="0">
<div id="newShowPortal"> <div id="newShowPortal">
<div id="displayText">aoeu</div> <div id="displayText">aoeu</div>
<br /> <br />
<form id="addShowForm" method="post" action="$sbRoot/home/addShows/addNewShow" accept-charset="utf-8" style="width: 800px;"> <form id="addShowForm" method="post" action="$sbRoot/home/addShows/addNewShow" accept-charset="utf-8">
<fieldset class="sectionwrap"> <fieldset class="sectionwrap step-one">
<legend class="legendStep">Find a show on the TVDB or TVRAGE</legend> <legend class="legendStep"><p>Find show at a TV database</p></legend>
<div class="stepDiv"> <div class="stepDiv">
<input type="hidden" id="indexer_timeout" value="$sickbeard.INDEXER_TIMEOUT" /> <input type="hidden" id="indexer_timeout" value="$sickbeard.INDEXER_TIMEOUT" />
@ -48,10 +50,10 @@
#else #else
<input type="text" id="nameToSearch" value="$default_show_name" class="form-control form-control-inline input-sm input350" /> <input type="text" id="nameToSearch" value="$default_show_name" class="form-control form-control-inline input-sm input350" />
&nbsp; &nbsp;
<span style="float:right">
<select name="indexerLang" id="indexerLangSelect" class="form-control form-control-inline input-sm"> <select name="indexerLang" id="indexerLangSelect" class="form-control form-control-inline input-sm">
<option value="en" selected="selected">en</option> <option value="en" selected="selected">en</option>
</select><b>*</b> </select><b>&nbsp;*</b>
&nbsp;
<select name="providedIndexer" id="providedIndexer" class="form-control form-control-inline input-sm"> <select name="providedIndexer" id="providedIndexer" class="form-control form-control-inline input-sm">
<option value="0" #if $provided_indexer == 0 then "selected=\"selected\"" else ""#>All Indexers</option> <option value="0" #if $provided_indexer == 0 then "selected=\"selected\"" else ""#>All Indexers</option>
#for $indexer in $indexers #for $indexer in $indexers
@ -60,21 +62,21 @@
</select> </select>
&nbsp; &nbsp;
<input class="btn btn-inline" type="button" id="searchName" value="Search" /> <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>
<br /> <div id="searchResults" style="height: 100%"></div>
<b>*</b> 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 id="searchResults" style="height: 100%;"><br/></div>
#end if #end if
</div> </div>
<div style="clear:both">&nbsp;</div>
</fieldset> </fieldset>
<fieldset class="sectionwrap"> <fieldset class="sectionwrap step-two">
<legend class="legendStep">Pick the parent folder</legend> <legend class="legendStep"><p>Pick parent folder</p></legend>
<div class="stepDiv"> <div class="stepDiv parent-folder">
#if $provided_show_dir #if $provided_show_dir
Pre-chosen Destination Folder: <b>$provided_show_dir</b> <br /> Pre-chosen Destination Folder: <b>$provided_show_dir</b> <br />
<input type="hidden" id="fullShowPath" name="fullShowPath" value="$provided_show_dir" /><br /> <input type="hidden" id="fullShowPath" name="fullShowPath" value="$provided_show_dir" /><br />
@ -82,13 +84,15 @@
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_rootDirs.tmpl') #include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_rootDirs.tmpl')
#end if #end if
</div> </div>
<div style="clear:both">&nbsp;</div>
</fieldset> </fieldset>
<fieldset class="sectionwrap"> <fieldset class="sectionwrap step-three">
<legend class="legendStep">Customize options</legend> <legend class="legendStep"><p>Set custom options</p></legend>
<div class="stepDiv"> <div class="stepDiv">
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_addShowOptions.tmpl') #include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_addShowOptions.tmpl')
</div> </div>
<div style="clear:both">&nbsp;</div>
</fieldset> </fieldset>
#for $curNextDir in $other_shows #for $curNextDir in $other_shows

View file

@ -12,7 +12,6 @@
#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")
<link rel="stylesheet" type="text/css" href="$sbRoot/css/formwizard.css?$sbPID" />
<script type="text/javascript" src="$sbRoot/js/formwizard.js?$sbPID"></script> <script type="text/javascript" src="$sbRoot/js/formwizard.js?$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/qualityChooser.js?$sbPID"></script> <script type="text/javascript" src="$sbRoot/js/qualityChooser.js?$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/recommendedShows.js?$sbPID"></script> <script type="text/javascript" src="$sbRoot/js/recommendedShows.js?$sbPID"></script>
@ -24,35 +23,40 @@
<h1 class="title">$title</h1> <h1 class="title">$title</h1>
#end if #end if
<image class="preload-image" style="position:absolute;top:-999px" src="$sbRoot/images/loading32<%= '-dark' if 'dark' == sickbeard.THEME_NAME else '' %>.gif" width="32" height="32" border="0">
<div id="newShowPortal"> <div id="newShowPortal">
<div id="displayText"></div> <div id="displayText"></div>
<br /> <br />
<form id="recommendedShowsForm" method="post" action="$sbRoot/home/addShows/addRecommendedShow" accept-charset="utf-8" style="width: 800px;"> <form id="addShowForm" method="post" action="$sbRoot/home/addShows/addRecommendedShow" accept-charset="utf-8">
<fieldset class="sectionwrap"> <fieldset class="sectionwrap step-one">
<legend class="legendStep">Select a recommended show</legend> <legend class="legendStep"><p>Select a recommended show</p></legend>
<div class="stepDiv"> <div class="stepDiv">
<div id="searchResults" style="height: 100%;"><br/></div> <div id="searchResults" style="height: 100%;"><br/></div>
</div> </div>
<div style="clear:both">&nbsp;</div>
</fieldset> </fieldset>
<fieldset class="sectionwrap"> <fieldset class="sectionwrap step-two">
<legend class="legendStep">Pick the parent folder</legend> <legend class="legendStep"><p>Pick parent folder</p></legend>
<div class="stepDiv"> <div class="stepDiv parent-folder">
#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_rootDirs.tmpl") #include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_rootDirs.tmpl")
</div> </div>
<div style="clear:both">&nbsp;</div>
</fieldset> </fieldset>
<fieldset class="sectionwrap"> <fieldset class="sectionwrap step-three">
<legend class="legendStep">Customize options</legend> <legend class="legendStep"><p>Set custom options</p></legend>
<div class="stepDiv"> <div class="stepDiv">
#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_addShowOptions.tmpl") #include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_addShowOptions.tmpl")
</div> </div>
<div style="clear:both">&nbsp;</div>
</fieldset> </fieldset>
</form> </form>

View file

@ -2,62 +2,77 @@
#from sickbeard.common import * #from sickbeard.common import *
#from sickbeard import subtitles #from sickbeard import subtitles
#if $sickbeard.USE_SUBTITLES: <div class="field-pair">
<div class="field-pair alt"> <label for="statusSelect">
<input type="checkbox" name="subtitles" id="subtitles" #if $sickbeard.SUBTITLES_DEFAULT then "checked=\"checked\"" else ""# /> <span class="component-title input">Initial episode status</span>
<label for="subtitles" class="clearfix"> <span class="component-desc">
<span class="component-title">Subtitles</span> <select name="defaultStatus" id="statusSelect" class="form-control form-control-inline input-sm">
<span class="component-desc">Download subtitles for this show?</span> #for $curStatus in [$SKIPPED, $WANTED, $ARCHIVED, $IGNORED]:
</label> <option value="$curStatus" #if $sickbeard.STATUS_DEFAULT == $curStatus then 'selected="selected"' else ''#>$statusStrings[$curStatus]</option>
</div> #end for
#end if </select>
<span>set the initial status of missing episodes</span>
<div class="field-pair"> </span>
<label for="statusSelect" class="nocheck clearfix"> </label>
<span class="component-title"> </div>
<select name="defaultStatus" id="statusSelect" class="form-control form-control-inline input-sm">
#for $curStatus in [$SKIPPED, $WANTED, $ARCHIVED, $IGNORED]:
<option value="$curStatus" #if $sickbeard.STATUS_DEFAULT == $curStatus then 'selected="selected"' else ''#>$statusStrings[$curStatus]</option>
#end for
</select>
</span>
<span class="component-desc">Set the initial status of missing episodes</span>
</label>
</div>
<div class="field-pair alt">
<input class="cb" type="checkbox" name="flatten_folders" id="flatten_folders" #if $sickbeard.FLATTEN_FOLDERS_DEFAULT then "checked=\"checked\"" else ""# />
<label for="flatten_folders" class="clearfix">
<span class="component-title">Flatten Folders</span>
<span class="component-desc">Disregard sub-folders?</span>
</label>
</div>
<div class="field-pair alt"> <div class="field-pair alt">
<input type="checkbox" name="anime" id="anime" #if $sickbeard.ANIME_DEFAULT then "checked=\"checked\"" else ""# /> <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>
<label for="anime" class="clearfix"> </div>
<span class="component-title">Anime</span>
<span class="component-desc">Is this show an Anime?</span>
</label>
</div>
<div class="field-pair alt"> #if $sickbeard.USE_SUBTITLES:
<input type="checkbox" name="scene" id="scene" #if $sickbeard.SCENE_DEFAULT then "checked=\"checked\"" else ""# /> <div class="field-pair alt">
<label for="scene" class="clearfix"> <label for="subtitles">
<span class="component-title">Scene Numbering</span> <span class="component-title">Subtitles</span>
<span class="component-desc">Is this show scene numbered?</span> <span class="component-desc">
</label> <input type="checkbox" name="subtitles" id="subtitles" #if $sickbeard.SUBTITLES_DEFAULT then "checked=\"checked\"" else ""# />
</div> <p>download subtitles for this show</p>
</span>
</label>
</div>
#end if
#set $qualities = $Quality.splitQuality($sickbeard.QUALITY_DEFAULT) <div class="field-pair alt">
#set global $anyQualities = $qualities[0] <label for="flatten_folders">
#set global $bestQualities = $qualities[1] <span class="component-title">Flatten folders</span>
#include $os.path.join($sickbeard.PROG_DIR, "gui/slick/interfaces/default/inc_qualityChooser.tmpl") <span class="component-desc">
<input class="cb" type="checkbox" name="flatten_folders" id="flatten_folders" #if $sickbeard.FLATTEN_FOLDERS_DEFAULT then "checked=\"checked\"" else ""# />
<br> <p>do not create sub folders</p>
<div class="field-pair alt"> </span>
<label for="saveDefaultsButton" class="nocheck clearfix"> </label>
<span class="component-title"><input class="btn btn-inline" type="button" id="saveDefaultsButton" value="Save Defaults" disabled="disabled" /></span> </div>
<span class="component-desc">Persist current values as the defaults</span>
</label> <div class="field-pair alt">
</div> <label for="anime">
<span class="component-title">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>
</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">
<label for="saveDefaultsButton">
<span class="component-title">Save options as defaults</span>
<span class="component-desc">
<input class="btn btn-inline" type="button" id="saveDefaultsButton" value="Save Defaults" disabled="disabled" />
<p>reuse the above options when adding more shows</p>
</span>
</label>
</div>

View file

@ -38,9 +38,30 @@
#set $ep_downloaded = 0 #set $ep_downloaded = 0
#set $ep_total = 0 #set $ep_total = 0
#end if #end if
#try
#set $localRoot = $sbRoot
#except NotFound
#set $localRoot = ''
#end try
#try
#set $localheader = $header
#except NotFound
#set $localheader = ''
#end try
<span class="footerhighlight">$shows_total</span> shows (<span class="footerhighlight">$shows_active</span> active) <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))[ep_snatched > 0] %>&nbsp;/&nbsp;<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]
%>
&nbsp;/&nbsp;<span class="footerhighlight">$ep_total</span> episodes downloaded
| daily search: <span class="footerhighlight"><%= str(sickbeard.dailySearchScheduler.timeLeft()).split('.')[0] %></span> | daily search: <span class="footerhighlight"><%= str(sickbeard.dailySearchScheduler.timeLeft()).split('.')[0] %></span>
| backlog search: <span class="footerhighlight">$sbdatetime.sbdatetime.sbfdate($sickbeard.backlogSearchScheduler.nextRun())</span> | backlog search: <span class="footerhighlight">$sbdatetime.sbdatetime.sbfdate($sickbeard.backlogSearchScheduler.nextRun())</span>

View file

@ -2,46 +2,48 @@
#from sickbeard.common import Quality, qualityPresets, qualityPresetStrings #from sickbeard.common import Quality, qualityPresets, qualityPresetStrings
<div class="field-pair"> <div class="field-pair">
<label for="qualityPreset" class="nocheck clearfix"> <label for="qualityPreset" class="clearfix">
#set $overall_quality = $Quality.combineQualities($anyQualities, $bestQualities) #set $overall_quality = $Quality.combineQualities($anyQualities, $bestQualities)
<span class="component-title"> <span class="component-title input">Quality</span>
<span class="component-desc">
#set $selected = None #set $selected = None
<select id="qualityPreset" class="form-control form-control-inline input-sm"> <select id="qualityPreset" class="form-control form-control-inline input-sm">
<option value="0">Custom</option> <option value="0">Custom</option>
#for $curPreset in sorted($qualityPresets): #for $curPreset in sorted($qualityPresets):
<option value="$curPreset" #if $curPreset == $overall_quality then "selected=\"selected\"" else ""# #if $qualityPresetStrings[$curPreset].endswith("0p") then "style=\"padding-left: 15px;\"" else ""#>$qualityPresetStrings[$curPreset]</option> <option value="$curPreset" #if $curPreset == $overall_quality then "selected=\"selected\"" else ""# #if $qualityPresetStrings[$curPreset].endswith("0p") then "style=\"padding-left: 15px;\"" else ""#>$qualityPresetStrings[$curPreset]</option>
#end for #end for
</select> </select>
</span> <span>preferred episode quality to download</span>
<span class="component-desc">Preferred quality of episodes to be download</span> </span>
</label>
</label>
</div> </div>
<div id="customQualityWrapper"> <div id="customQualityWrapper">
<div id="customQuality"> <div id="customQuality">
<div class="component-group-desc"> <div class="component-group-desc">
<p>One of the <b>Initial</b> quality selections must be obtained before SickGear will attempt to process the <b>Archive</b> selections.</p> <p>One of the <em>Initial</em> quality selections must succeed before attempting to process <em>Archive</em> selections.</p>
</div> </div>
<div style="padding-right: 40px; text-align: center;" class="float-left"> <span class="component-desc">
<h4>Initial</h4> <div style="float:left; padding-right: 40px">
#set $anyQualityList = filter(lambda x: x > $Quality.NONE, $Quality.qualityStrings) <h4>Initial</h4>
<select id="anyQualities" name="anyQualities" multiple="multiple" size="$len($anyQualityList)" class="form-control form-control-inline input-sm"> #set $anyQualityList = filter(lambda x: x > $Quality.NONE, $Quality.qualityStrings)
#for $curQuality in sorted($anyQualityList): <select id="anyQualities" name="anyQualities" multiple="multiple" size="$len($anyQualityList)" class="form-control form-control-inline input-sm">
<option value="$curQuality" #if $curQuality in $anyQualities then "selected=\"selected\"" else ""#>$Quality.qualityStrings[$curQuality]</option> #for $curQuality in sorted($anyQualityList):
#end for <option value="$curQuality" #if $curQuality in $anyQualities then "selected=\"selected\"" else ""#>$Quality.qualityStrings[$curQuality]</option>
</select> #end for
</div> </select>
</div>
<div style="text-align: center;" class="float-left"> <div style="float:left">
<h4>Archive</h4> <h4>Archive</h4>
#set $bestQualityList = filter(lambda x: x > $Quality.SDTV and x < $Quality.UNKNOWN, $Quality.qualityStrings) #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"> <select id="bestQualities" name="bestQualities" multiple="multiple" size="$len($bestQualityList)" class="form-control form-control-inline input-sm">
#for $curQuality in sorted($bestQualityList): #for $curQuality in sorted($bestQualityList):
<option value="$curQuality" #if $curQuality in $bestQualities then "selected=\"selected\"" else ""#>$Quality.qualityStrings[$curQuality]</option> <option value="$curQuality" #if $curQuality in $bestQualities then "selected=\"selected\"" else ""#>$Quality.qualityStrings[$curQuality]</option>
#end for #end for
</select> </select>
</div> </div>
</div> </span>
</div>
</div> </div>

View file

@ -7,7 +7,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>SickGear - BRANCH:[$sickbeard.BRANCH] - $title</title> <title>SickGear - BRANCH:[$sickbeard.BRANCH] - $title</title>
<!--[if lt IE 9]> <!--[if lt IE 9]>
@ -37,7 +37,7 @@
<link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/bootstrap.css?$sbPID"/> <link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/bootstrap.css?$sbPID"/>
<link rel="stylesheet" type="text/css" href="$sbRoot/css/browser.css?$sbPID" /> <link rel="stylesheet" type="text/css" href="$sbRoot/css/browser.css?$sbPID" />
<link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/jquery-ui-1.10.4.custom.css?$sbPID" /> <link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/jquery-ui-1.10.4.custom.css?$sbPID" />
<link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/jquery.qtip-2.0.1.min.css?$sbPID"/> <link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/jquery.qtip-2.2.1.min.css?$sbPID"/>
<link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/pnotify.custom.min.css?$sbPID" /> <link rel="stylesheet" type="text/css" href="$sbRoot/css/lib/pnotify.custom.min.css?$sbPID" />
<link rel="stylesheet" type="text/css" href="$sbRoot/css/style.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" /> <link rel="stylesheet" type="text/css" href="$sbRoot/css/${sickbeard.THEME_NAME}.css?$sbPID" />
@ -52,7 +52,7 @@
<script type="text/javascript" src="$sbRoot/js/lib/jquery.selectboxes.min.js?$sbPID"></script> <script type="text/javascript" src="$sbRoot/js/lib/jquery.selectboxes.min.js?$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/lib/jquery.tablesorter-2.17.7.min.js?$sbPID"></script> <script type="text/javascript" src="$sbRoot/js/lib/jquery.tablesorter-2.17.7.min.js?$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/lib/jquery.tablesorter.widgets-2.17.7.min.js?$sbPID"></script> <script type="text/javascript" src="$sbRoot/js/lib/jquery.tablesorter.widgets-2.17.7.min.js?$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/lib/jquery.qtip-2.0.1.min.js?$sbPID"></script> <script type="text/javascript" src="$sbRoot/js/lib/jquery.qtip-2.2.1.min.js?$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/lib/pnotify.custom.min.js"></script> <script type="text/javascript" src="$sbRoot/js/lib/pnotify.custom.min.js"></script>
<script type="text/javascript" src="$sbRoot/js/lib/jquery.form-3.35.js?$sbPID"></script> <script type="text/javascript" src="$sbRoot/js/lib/jquery.form-3.35.js?$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/lib/jquery.ui.touch-punch-0.2.2.min.js?$sbPID"></script> <script type="text/javascript" src="$sbRoot/js/lib/jquery.ui.touch-punch-0.2.2.min.js?$sbPID"></script>
@ -61,10 +61,10 @@
<script type="text/javascript" src="$sbRoot/js/script.js?$sbPID"></script> <script type="text/javascript" src="$sbRoot/js/script.js?$sbPID"></script>
#if $sickbeard.FUZZY_DATING: #if $sickbeard.FUZZY_DATING:
<script type="text/javascript" src="$sbRoot/js/moment/moment.min.js?$sbPID"></script> <script type="text/javascript" src="$sbRoot/js/moment/moment.min.js?$sbPID"></script>
<script type="text/javascript" src="$sbRoot/js/fuzzyMoment.js?$sbPID"></script> <script type="text/javascript" src="$sbRoot/js/fuzzyMoment.js?$sbPID"></script>
#end if #end if
<script type="text/javascript" charset="utf-8"> <script type="text/javascript" charset="utf-8">
<!-- <!--
sbRoot = '$sbRoot'; // needed for browser.js & ajaxNotifications.js sbRoot = '$sbRoot'; // needed for browser.js & ajaxNotifications.js
@ -98,7 +98,6 @@
\$("#SubMenu a:contains('Anime')").addClass('btn').html('<span class="submenu-icon-anime pull-left"></span> Anime'); \$("#SubMenu a:contains('Anime')").addClass('btn').html('<span class="submenu-icon-anime pull-left"></span> Anime');
\$("#SubMenu a:contains('Settings')").addClass('btn').html('<span class="ui-icon ui-icon-search pull-left"></span> Search Settings'); \$("#SubMenu a:contains('Settings')").addClass('btn').html('<span class="ui-icon ui-icon-search pull-left"></span> Search Settings');
\$("#SubMenu a:contains('Provider')").addClass('btn').html('<span class="ui-icon ui-icon-search pull-left"></span> Search Providers'); \$("#SubMenu a:contains('Provider')").addClass('btn').html('<span class="ui-icon ui-icon-search pull-left"></span> Search Providers');
\$("#SubMenu a:contains('Backup/Restore')").addClass('btn').html('<span class="ui-icon ui-icon-gear pull-left"></span> Backup/Restore');
\$("#SubMenu a:contains('General')").addClass('btn').html('<span class="ui-icon ui-icon-gear pull-left"></span> General'); \$("#SubMenu a:contains('General')").addClass('btn').html('<span class="ui-icon ui-icon-gear pull-left"></span> General');
\$("#SubMenu a:contains('Episode Status')").addClass('btn').html('<span class="ui-icon ui-icon-transferthick-e-w pull-left"></span> Episode Status Management'); \$("#SubMenu a:contains('Episode Status')").addClass('btn').html('<span class="ui-icon ui-icon-transferthick-e-w pull-left"></span> Episode Status Management');
\$("#SubMenu a:contains('Missed Subtitle')").addClass('btn').html('<span class="ui-icon ui-icon-transferthick-e-w pull-left"></span> Missed Subtitles'); \$("#SubMenu a:contains('Missed Subtitle')").addClass('btn').html('<span class="ui-icon ui-icon-transferthick-e-w pull-left"></span> Missed Subtitles');
@ -119,13 +118,13 @@
\$("#NAV$topmenu").addClass("active"); \$("#NAV$topmenu").addClass("active");
\$('.dropdown-toggle').dropdownHover(); \$('.dropdown-toggle').dropdownHover();
}); });
//--> //-->
</script> </script>
<script type="text/javascript" src="$sbRoot/js/confirmations.js?$sbPID"></script> <script type="text/javascript" src="$sbRoot/js/confirmations.js?$sbPID"></script>
</head> </head>
#set $tab = 4
<body> <body>
<nav class="navbar navbar-default navbar-fixed-top" role="navigation"> <nav class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container-fluid"> <div class="container-fluid">
@ -136,84 +135,83 @@
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </button>
<a class="navbar-brand" href="$sbRoot/home/" title="SickGear"><img alt="SickGear" src="$sbRoot/images/sickgear.png" style="height: 50px;" class="img-responsive pull-left" /></a> <a href="$sbRoot/home/" class="navbar-brand" tabindex="-1" title="SickGear"><img alt="SickGear" src="$sbRoot/images/sickgear.png" style="height: 50px;" class="img-responsive pull-left" /></a>
</div> </div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
<li id="NAVhome" class="dropdown"> <li id="NAVhome" class="dropdown">
<a href="$sbRoot/home/" class="dropdown-toggle" data-toggle="dropdown">Shows <b class="caret"></b></a> <a href="$sbRoot/home/" class="dropdown-toggle" data-toggle="dropdown" tabindex="$tab#set $tab += 1#">Shows <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="$sbRoot/home/"><i class="menu-icon-home"></i>&nbsp;Show List</a></li>
<li><a href="$sbRoot/home/addShows/"><i class="menu-icon-addshow"></i>&nbsp;Add Shows</a></li>
<li><a href="$sbRoot/home/postprocess/"><i class="menu-icon-postprocess"></i>&nbsp;Manual Post-Processing</a></li>
</ul>
</li>
<li id="NAVcomingEpisodes">
<a href="$sbRoot/comingEpisodes/">Coming Episodes</a>
</li>
<li id="NAVhistory">
<a href="$sbRoot/history/">History</a>
</li>
<li id="NAVmanage" class="dropdown">
<a href="$sbRoot/manage/" class="dropdown-toggle" data-toggle="dropdown">Manage <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="$sbRoot/manage/"><i class="menu-icon-manage"></i>&nbsp;Mass Update</a></li>
<li><a href="$sbRoot/manage/backlogOverview/"><i class="menu-icon-backlog-view"></i>&nbsp;Backlog Overview</a></li>
<li><a href="$sbRoot/manage/manageSearches/"><i class="menu-icon-manage-searches"></i>&nbsp;Manage Searches</a></li>
<li><a href="$sbRoot/manage/episodeStatuses/"><i class="menu-icon-backlog"></i>&nbsp;Episode Status Management</a></li>
#if $sickbeard.USE_PLEX and $sickbeard.PLEX_SERVER_HOST != "":
<li><a href="$sbRoot/home/updatePLEX/"><i class="menu-icon-backlog-view"></i>&nbsp;Update PLEX</a></li>
#end if
#if $sickbeard.USE_XBMC and $sickbeard.XBMC_HOST != "":
<li><a href="$sbRoot/home/updateXBMC/"><i class="menu-icon-xbmc"></i>&nbsp;Update XBMC</a></li>
#end if
#if $sickbeard.USE_TORRENTS and $sickbeard.TORRENT_METHOD != 'blackhole' \
and ($sickbeard.ENABLE_HTTPS and $sickbeard.TORRENT_HOST[:5] == 'https' \
or not $sickbeard.ENABLE_HTTPS and $sickbeard.TORRENT_HOST[:5] == 'http:'):
<li><a href="$sbRoot/manage/manageTorrents/"><i class="menu-icon-bittorrent"></i>&nbsp;Manage Torrents</a></li>
#end if
#if $sickbeard.USE_FAILED_DOWNLOADS:
<li><a href="$sbRoot/manage/failedDownloads/"><i class="menu-icon-failed-download"></i>&nbsp;Failed Downloads</a></li>
#end if
#if $sickbeard.USE_SUBTITLES:
<li><a href="$sbRoot/manage/subtitleMissed/"><i class="menu-icon-backlog"></i>&nbsp;Missed Subtitle Management</a></li>
#end if
</ul>
</li>
<li id="NAVerrorlogs" class="dropdown">
<a href="$sbRoot/errorlogs/" class="dropdown-toggle" data-toggle="dropdown">$logPageTitle <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="$sbRoot/errorlogs/"><i class="menu-icon-viewlog-errors"></i>&nbsp;View Log (Errors)</a></li>
<li><a href="$sbRoot/errorlogs/viewlog/"><i class="menu-icon-viewlog"></i>&nbsp;View Log</a></li>
</ul>
</li>
<li id="NAVconfig" class="dropdown">
<a href="$sbRoot/config/" class="dropdown-toggle" data-toggle="dropdown"><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/"><i class="menu-icon-help"></i>&nbsp;Help &amp; Info</a></li>
<li><a href="$sbRoot/config/general/"><i class="menu-icon-config"></i>&nbsp;General</a></li>
<li><a href="$sbRoot/config/backuprestore/"><i class="menu-icon-config"></i>&nbsp;Backup &amp; Restore</a></li>
<li><a href="$sbRoot/config/search/"><i class="menu-icon-config"></i>&nbsp;Search Settings</a></li>
<li><a href="$sbRoot/config/providers/"><i class="menu-icon-config"></i>&nbsp;Search Providers</a></li>
<li><a href="$sbRoot/config/subtitles/"><i class="menu-icon-config"></i>&nbsp;Subtitles Settings</a></li>
<li><a href="$sbRoot/config/postProcessing/"><i class="menu-icon-config"></i>&nbsp;Post Processing</a></li>
<li><a href="$sbRoot/config/notifications/"><i class="menu-icon-config"></i>&nbsp;Notifications</a></li>
<li><a href="$sbRoot/config/anime/"><i class="menu-icon-config"></i>&nbsp;Anime</a></li>
</ul>
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><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"> <ul class="dropdown-menu">
<li><a href="$sbRoot/manage/manageSearches/forceVersionCheck"><i class="menu-icon-update"></i>&nbsp;Force Version Check</a></li> <li><a href="$sbRoot/home/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-home"></i>&nbsp;Show List</a></li>
<li><a href="$sbRoot/home/restart/?pid=$sbPID" class="confirm restart"><i class="menu-icon-restart"></i>&nbsp;Restart</a></li> <li><a href="$sbRoot/home/addShows/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-addshow"></i>&nbsp;Add Shows</a></li>
<li><a href="$sbRoot/home/shutdown/?pid=$sbPID" class="confirm shutdown"><i class="menu-icon-shutdown"></i>&nbsp;Shutdown</a></li> <li><a href="$sbRoot/home/postprocess/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-postprocess"></i>&nbsp;Manual Post-Processing</a></li>
</ul>
</li>
<li id="NAVcomingEpisodes">
<a href="$sbRoot/comingEpisodes/" tabindex="$tab#set $tab += 1#">Coming Episodes</a>
</li>
<li id="NAVhistory">
<a href="$sbRoot/history/" tabindex="$tab#set $tab += 1#">History</a>
</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>
<ul class="dropdown-menu">
<li><a href="$sbRoot/manage/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-manage"></i>&nbsp;Mass Update</a></li>
<li><a href="$sbRoot/manage/backlogOverview/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-backlog-view"></i>&nbsp;Backlog Overview</a></li>
<li><a href="$sbRoot/manage/manageSearches/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-manage-searches"></i>&nbsp;Manage Searches</a></li>
<li><a href="$sbRoot/manage/episodeStatuses/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-backlog"></i>&nbsp;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>&nbsp;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>&nbsp;Update XBMC</a></li>
#end if
#if $sickbeard.USE_TORRENTS and $sickbeard.TORRENT_METHOD != 'blackhole' \
and ($sickbeard.ENABLE_HTTPS and $sickbeard.TORRENT_HOST[:5] == 'https' \
or not $sickbeard.ENABLE_HTTPS and $sickbeard.TORRENT_HOST[:5] == 'http:'):
<li><a href="$sbRoot/manage/manageTorrents/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-bittorrent"></i>&nbsp;Manage Torrents</a></li>
#end if
#if $sickbeard.USE_FAILED_DOWNLOADS:
<li><a href="$sbRoot/manage/failedDownloads/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-failed-download"></i>&nbsp;Failed Downloads</a></li>
#end if
#if $sickbeard.USE_SUBTITLES:
<li><a href="$sbRoot/manage/subtitleMissed/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-backlog"></i>&nbsp;Missed Subtitle Management</a></li>
#end if
</ul>
</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>
<ul class="dropdown-menu">
<li><a href="$sbRoot/errorlogs/"><i class="menu-icon-viewlog-errors" tabindex="$tab#set $tab += 1#"></i>&nbsp;View Log (Errors)</a></li>
<li><a href="$sbRoot/errorlogs/viewlog/"><i class="menu-icon-viewlog" tabindex="$tab#set $tab += 1#"></i>&nbsp;View Log</a></li>
</ul>
</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>
<ul class="dropdown-menu">
<li><a href="$sbRoot/config/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-help"></i>&nbsp;Help &amp; Info</a></li>
<li><a href="$sbRoot/config/general/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-config"></i>&nbsp;General</a></li>
<li><a href="$sbRoot/config/search/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-config"></i>&nbsp;Search Settings</a></li>
<li><a href="$sbRoot/config/providers/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-config"></i>&nbsp;Search Providers</a></li>
<li><a href="$sbRoot/config/subtitles/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-config"></i>&nbsp;Subtitles Settings</a></li>
<li><a href="$sbRoot/config/postProcessing/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-config"></i>&nbsp;Post Processing</a></li>
<li><a href="$sbRoot/config/notifications/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-config"></i>&nbsp;Notifications</a></li>
<li><a href="$sbRoot/config/anime/" tabindex="$tab#set $tab += 1#"><i class="menu-icon-config"></i>&nbsp;Anime</a></li>
</ul>
</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>
<ul class="dropdown-menu">
<li><a href="$sbRoot/manage/manageSearches/forceVersionCheck" tabindex="$tab#set $tab += 1#"><i class="menu-icon-update"></i>&nbsp;Force Version Check</a></li>
<li><a href="$sbRoot/home/restart/?pid=$sbPID" class="confirm restart" tabindex="$tab#set $tab += 1#"><i class="menu-icon-restart"></i>&nbsp;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>&nbsp;Shutdown</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
@ -222,35 +220,47 @@
</div><!-- /.container-fluid --> </div><!-- /.container-fluid -->
</nav> </nav>
#if $varExists('submenu'): #if $varExists('submenu'):
<div id="SubMenu"> <div id="SubMenu">
<span> <span>
#set $first = True #set $first = True
#for $menuItem in $submenu: #for $menuItem in $submenu:
#if 'requires' not in $menuItem or $menuItem.requires(): #if 'requires' not in $menuItem or $menuItem.requires():
#if type($menuItem.path) == dict: #if type($menuItem.path) == dict:
#if $first then "" else "</span><span>"#<b>$menuItem.title</b> #if $first then "" else "</span><span>"#<b>$menuItem.title</b>
#set $first = False #set $first = False
#set $inner_first = True #set $inner_first = True
#for $cur_link in $menuItem.path: #for $cur_link in $menuItem.path:
#if $inner_first then "" else "&middot; "#<a class="inner" href="$sbRoot/$menuItem.path[$cur_link]">$cur_link</a> #if $inner_first then "" else "&middot; "#<a class="inner" href="$sbRoot/$menuItem.path[$cur_link]">$cur_link</a>
#set $inner_first = False #set $inner_first = False
#end for #end for
#else #else
#if $first then "" else ""#<a href="$sbRoot/$menuItem.path" #if 'confirm' in $menuItem then "class=\"confirm\"" else "" #>$menuItem.title</a> #if $first then "" else ""#<a href="$sbRoot/$menuItem.path" #if 'confirm' in $menuItem then "class=\"confirm\"" else "" # tabindex="$tab#set $tab += 1#">$menuItem.title</a>
#set $first = False #set $first = False
#end if #end if
#end if #end if
#end for #end for
</span> </span>
</div> </div>
#end if #end if
#if $sickbeard.NEWEST_VERSION_STRING: #if $sickbeard.NEWEST_VERSION_STRING:
<div class="alert alert-success upgrade-notification" role="alert"> <div class="alert alert-success upgrade-notification" role="alert">
<span>$sickbeard.NEWEST_VERSION_STRING</span> <span>$sickbeard.NEWEST_VERSION_STRING</span>
</div> </div>
#end if #end if
<div id="contentWrapper"> #set $items = []
<div id="content"> #try
$items.append($topmenu)
#except (NameError, NotFound):
#pass
#end try
#try
$items.append($layout)
#except (NameError, NotFound):
#pass
#end try
#set $page_class = ('', ' class="%s"' % '_'.join($items).lower().replace(' ', '-'))[0 < len($items)]
<div id="contentWrapper">
<div id="content"$page_class>

View file

@ -40,7 +40,7 @@ Manage episodes with status <select name="whichStatus" class="form-control form-
<form action="$sbRoot/manage/changeEpisodeStatuses" method="post"> <form action="$sbRoot/manage/changeEpisodeStatuses" method="post">
<input type="hidden" id="oldStatus" name="oldStatus" value="$whichStatus" /> <input type="hidden" id="oldStatus" name="oldStatus" value="$whichStatus" />
<h2>Shows containing $common.statusStrings[$int($whichStatus)] episodes</h2> <h2><span class="grey-text">Shows containing</span> $common.statusStrings[$int($whichStatus)] <span class="grey-text">episodes</span></h2>
<br /> <br />
@ -65,7 +65,7 @@ $statusList.append($common.FAILED)
<option value="$curStatus">$common.statusStrings[$curStatus]</option> <option value="$curStatus">$common.statusStrings[$curStatus]</option>
#end for #end for
</select> </select>
<input class="btn btn-inline" type="submit" value="Go" /> <input class="btn btn-inline go" type="submit" value="Go" />
<div> <div>
<button type="button" class="btn btn-xs selectAllShows">Select all</a></button> <button type="button" class="btn btn-xs selectAllShows">Select all</a></button>
@ -76,7 +76,7 @@ $statusList.append($common.FAILED)
<table class="sickbeardTable manageTable" cellspacing="1" border="0" cellpadding="0"> <table class="sickbeardTable manageTable" cellspacing="1" border="0" cellpadding="0">
#for $cur_indexer_id in $sorted_show_ids: #for $cur_indexer_id in $sorted_show_ids:
<tr id="$cur_indexer_id"> <tr id="$cur_indexer_id">
<th><input type="checkbox" class="allCheck" id="allCheck-$cur_indexer_id" name="$cur_indexer_id-all" checked="checked" /></th> <th><input type="checkbox" class="allCheck" id="allCheck-$cur_indexer_id" name="$cur_indexer_id-all" /></th>
<th colspan="2" style="width: 100%; text-align: left;"><a class="whitelink" href="$sbRoot/home/displayShow?show=$cur_indexer_id">$show_names[$cur_indexer_id]</a> ($ep_counts[$cur_indexer_id]) <input type="button" class="pull-right get_more_eps btn" id="$cur_indexer_id" value="Expand" /></th> <th colspan="2" style="width: 100%; text-align: left;"><a class="whitelink" href="$sbRoot/home/displayShow?show=$cur_indexer_id">$show_names[$cur_indexer_id]</a> ($ep_counts[$cur_indexer_id]) <input type="button" class="pull-right get_more_eps btn" id="$cur_indexer_id" value="Expand" /></th>
</tr> </tr>
#end for #end for

View file

@ -1,78 +1,93 @@
$(document).ready(function() { $(document).ready(function(){
$('#checkAll').live('click', function(){ var tableDiv = $('#tableDiv');
var seasCheck = this;
$('.dirCheck').each(function(){ tableDiv.on('click', '#checkAll', function(){
this.checked = seasCheck.checked;
});
});
$('#submitShowDirs').click(function(){ var cbToggle = this.checked;
var dirArr = new Array(); $('.dirCheck').each(function(){
this.checked = cbToggle;
});
});
$('.dirCheck').each(function(i,w) { $('#submitShowDirs').click(function(){
if (this.checked == true) {
var show = $(this).attr('id');
var indexer = $(this).closest('tr').find('select').val();
dirArr.push(encodeURIComponent(indexer + '|' + show));
}
});
if (dirArr.length == 0) var dirArr = [];
return false;
url = sbRoot+'/home/addShows/addExistingShows?promptForSettings='+ ($('#promptForSettings').prop('checked') ? 'on' : 'off'); $('.dirCheck').each(function(){
if (true == this.checked){
var show = $(this).attr('id');
var indexer = $(this).closest('tr').find('select').val();
dirArr.push(encodeURIComponent(indexer + '|' + show));
}
});
url += '&shows_to_add='+dirArr.join('&shows_to_add='); if (0 == dirArr.length)
return false;
window.location.href = url; window.location.href = sbRoot + '/home/addShows/addExistingShows'
}); + '?promptForSettings=' + ($('#promptForSettings').prop('checked') ? 'on' : 'off')
+ '&shows_to_add=' + dirArr.join('&shows_to_add=');
});
function loadContent() { function loadContent(){
var url = ''; var url = '';
$('.dir_check').each(function(i,w){ $('.dir_check').each(function(i, w){
if ($(w).is(':checked')) { if ($(w).is(':checked')){
if (url.length) url += (url.length ? '&' : '')
url += '&'; + 'rootDir=' + encodeURIComponent($(w).attr('id'));
url += 'rootDir=' + encodeURIComponent($(w).attr('id')); }
} });
});
$('#tableDiv').html('<img id="searchingAnim" src="' + sbRoot + '/images/loading32' + themeSpinner + '.gif" height="32" width="32" /> loading folders...'); $('#tableDiv').html('<img id="searchingAnim"'
$.get(sbRoot+'/home/addShows/massAddTable', url, function(data) { + ' style="margin-right:10px"'
$('#tableDiv').html(data); + ' src="' + sbRoot + '/images/loading32' + themeSpinner + '.gif"'
$("#addRootDirTable").tablesorter({ + ' height="32" width="32" />'
//sortList: [[1,0]], + ' scanning parent folders...');
widgets: ['zebra'],
headers: {
0: { sorter: false }
}
});
});
} $.get(sbRoot + '/home/addShows/massAddTable',
url,
function(data){
$('#tableDiv').html(data);
$('#addRootDirTable').tablesorter({
sortList: [[1,0]],
widgets: ['zebra'],
headers: {
0: { sorter: false }
}
});
});
}
var last_txt = ''; var last_txt = '', new_text = '', id;
$('#rootDirText').change(function() { $('#rootDirText').change(function(){
if (last_txt == $('#rootDirText').val()) if (last_txt == (new_text = $('#rootDirText').val()))
return false; return false;
else
last_txt = $('#rootDirText').val();
$('#rootDirStaticList').html('');
$('#rootDirs option').each(function(i, w) {
$('#rootDirStaticList').append('<li class="ui-state-default ui-corner-all"><input type="checkbox" class="dir_check" id="'+$(w).val()+'" checked=checked> <label for="'+$(w).val()+'" style="color:#09A2FF;"><b>'+$(w).val()+'</b></label></li>')
});
loadContent();
});
$('.dir_check').live('click', loadContent);
$('.showManage').live('click', function() { last_txt = new_text;
$( "#tabs" ).tabs( 'select', 0 ); $('#rootDirStaticList').html('');
}); $('#rootDirs').find('option').each(function(i, w){
id = $(w).val();
$('#rootDirStaticList').append('<li class="ui-state-default ui-corner-all">'
+ '<input id="' + id + '" type="checkbox"' + ' checked=checked'
+ ' class="dir_check"'
+ ' />'
+ ' <label for="' + id + '"'
+ ' style="color:#09A2FF">'
+ '<b>' + id + '</b></label>'
+ '</li>')
});
loadContent();
});
$('#rootDirStaticList').on('click', '.dir_check', loadContent);
tableDiv.on('click', '.showManage', function(event) {
event.preventDefault();
$('#tabs').tabs('option', 'active', 0);
$('html,body').animate({scrollTop: 0}, 1000);
});
}); });

View file

@ -1,24 +0,0 @@
$(document).ready(function(){
var loading = '<img src="' + sbRoot + '/images/loading16' + themeSpinner + '.gif" height="16" width="16" />';
$('#Backup').click(function() {
$("#Backup").attr("disabled", true);
$('#Backup-result').html(loading);
var backupDir = $("#backupDir").val();
$.get(sbRoot + "/config/backup", {'backupDir': backupDir})
.done(function (data) {
$('#Backup-result').html(data);
$("#Backup").attr("disabled", false);
});
});
$('#Restore').click(function() {
$("#Restore").attr("disabled", true);
$('#Restore-result').html(loading);
var backupFile = $("#backupFile").val();
$.get(sbRoot + "/config/restore", {'backupFile': backupFile})
.done(function (data) {
$('#Restore-result').html(data);
$("#Restore").attr("disabled", false);
});
});
});

View file

@ -17,7 +17,7 @@ $(document).ready(function () {
} else { } else {
$('#unpack').qtip('option', { $('#unpack').qtip('option', {
'content.text': 'Unrar Executable not found.', 'content.text': 'Unrar Executable not found.',
'style.classes': 'qtip-rounded qtip-shadow qtip-red' 'style.classes': 'qtip-red qtip-rounded qtip-shadow'
}); });
$('#unpack').qtip('toggle', true); $('#unpack').qtip('toggle', true);
$('#unpack').css('background-color', '#FFFFDD'); $('#unpack').css('background-color', '#FFFFDD');
@ -56,21 +56,21 @@ $(document).ready(function () {
if (data == "invalid") { if (data == "invalid") {
$('#naming_pattern').qtip('option', { $('#naming_pattern').qtip('option', {
'content.text': 'This pattern is invalid.', 'content.text': 'This pattern is invalid.',
'style.classes': 'qtip-rounded qtip-shadow qtip-red' 'style.classes': 'qtip-red qtip-rounded qtip-shadow'
}); });
$('#naming_pattern').qtip('toggle', true); $('#naming_pattern').qtip('toggle', true);
$('#naming_pattern').css('background-color', '#FFDDDD'); $('#naming_pattern').css('background-color', '#FFDDDD');
} else if (data == "seasonfolders") { } else if (data == "seasonfolders") {
$('#naming_pattern').qtip('option', { $('#naming_pattern').qtip('option', {
'content.text': 'This pattern would be invalid without the folders, using it will force "Flatten" off for all shows.', 'content.text': 'This pattern would be invalid without the folders, using it will force "Flatten" off for all shows.',
'style.classes': 'qtip-rounded qtip-shadow qtip-red' 'style.classes': 'qtip-red qtip-rounded qtip-shadow'
}); });
$('#naming_pattern').qtip('toggle', true); $('#naming_pattern').qtip('toggle', true);
$('#naming_pattern').css('background-color', '#FFFFDD'); $('#naming_pattern').css('background-color', '#FFFFDD');
} else { } else {
$('#naming_pattern').qtip('option', { $('#naming_pattern').qtip('option', {
'content.text': 'This pattern is valid.', 'content.text': 'This pattern is valid.',
'style.classes': 'qtip-rounded qtip-shadow qtip-green' 'style.classes': 'qtip-green qtip-rounded qtip-shadow'
}); });
$('#naming_pattern').qtip('toggle', false); $('#naming_pattern').qtip('toggle', false);
$('#naming_pattern').css('background-color', '#FFFFFF'); $('#naming_pattern').css('background-color', '#FFFFFF');
@ -97,21 +97,21 @@ $(document).ready(function () {
if (data == "invalid") { if (data == "invalid") {
$('#naming_abd_pattern').qtip('option', { $('#naming_abd_pattern').qtip('option', {
'content.text': 'This pattern is invalid.', 'content.text': 'This pattern is invalid.',
'style.classes': 'qtip-rounded qtip-shadow qtip-red' 'style.classes': 'qtip-red qtip-rounded qtip-shadow'
}); });
$('#naming_abd_pattern').qtip('toggle', true); $('#naming_abd_pattern').qtip('toggle', true);
$('#naming_abd_pattern').css('background-color', '#FFDDDD'); $('#naming_abd_pattern').css('background-color', '#FFDDDD');
} else if (data == "seasonfolders") { } else if (data == "seasonfolders") {
$('#naming_abd_pattern').qtip('option', { $('#naming_abd_pattern').qtip('option', {
'content.text': 'This pattern would be invalid without the folders, using it will force "Flatten" off for all shows.', 'content.text': 'This pattern would be invalid without the folders, using it will force "Flatten" off for all shows.',
'style.classes': 'qtip-rounded qtip-shadow qtip-red' 'style.classes': 'qtip-red qtip-rounded qtip-shadow'
}); });
$('#naming_abd_pattern').qtip('toggle', true); $('#naming_abd_pattern').qtip('toggle', true);
$('#naming_abd_pattern').css('background-color', '#FFFFDD'); $('#naming_abd_pattern').css('background-color', '#FFFFDD');
} else { } else {
$('#naming_abd_pattern').qtip('option', { $('#naming_abd_pattern').qtip('option', {
'content.text': 'This pattern is valid.', 'content.text': 'This pattern is valid.',
'style.classes': 'qtip-rounded qtip-shadow qtip-green' 'style.classes': 'qtip-green qtip-rounded qtip-shadow'
}); });
$('#naming_abd_pattern').qtip('toggle', false); $('#naming_abd_pattern').qtip('toggle', false);
$('#naming_abd_pattern').css('background-color', '#FFFFFF'); $('#naming_abd_pattern').css('background-color', '#FFFFFF');
@ -138,21 +138,21 @@ $(document).ready(function () {
if (data == "invalid") { if (data == "invalid") {
$('#naming_sports_pattern').qtip('option', { $('#naming_sports_pattern').qtip('option', {
'content.text': 'This pattern is invalid.', 'content.text': 'This pattern is invalid.',
'style.classes': 'qtip-rounded qtip-shadow qtip-red' 'style.classes': 'qtip-red qtip-rounded qtip-shadow'
}); });
$('#naming_sports_pattern').qtip('toggle', true); $('#naming_sports_pattern').qtip('toggle', true);
$('#naming_sports_pattern').css('background-color', '#FFDDDD'); $('#naming_sports_pattern').css('background-color', '#FFDDDD');
} else if (data == "seasonfolders") { } else if (data == "seasonfolders") {
$('#naming_sports_pattern').qtip('option', { $('#naming_sports_pattern').qtip('option', {
'content.text': 'This pattern would be invalid without the folders, using it will force "Flatten" off for all shows.', 'content.text': 'This pattern would be invalid without the folders, using it will force "Flatten" off for all shows.',
'style.classes': 'qtip-rounded qtip-shadow qtip-red' 'style.classes': 'qtip-red qtip-rounded qtip-shadow'
}); });
$('#naming_sports_pattern').qtip('toggle', true); $('#naming_sports_pattern').qtip('toggle', true);
$('#naming_sports_pattern').css('background-color', '#FFFFDD'); $('#naming_sports_pattern').css('background-color', '#FFFFDD');
} else { } else {
$('#naming_sports_pattern').qtip('option', { $('#naming_sports_pattern').qtip('option', {
'content.text': 'This pattern is valid.', 'content.text': 'This pattern is valid.',
'style.classes': 'qtip-rounded qtip-shadow qtip-green' 'style.classes': 'qtip-green qtip-rounded qtip-shadow'
}); });
$('#naming_sports_pattern').qtip('toggle', false); $('#naming_sports_pattern').qtip('toggle', false);
$('#naming_sports_pattern').css('background-color', '#FFFFFF'); $('#naming_sports_pattern').css('background-color', '#FFFFFF');
@ -179,21 +179,21 @@ $(document).ready(function () {
if (data == "invalid") { if (data == "invalid") {
$('#naming_sports_pattern').qtip('option', { $('#naming_sports_pattern').qtip('option', {
'content.text': 'This pattern is invalid.', 'content.text': 'This pattern is invalid.',
'style.classes': 'qtip-rounded qtip-shadow qtip-red' 'style.classes': 'qtip-red qtip-rounded qtip-shadow'
}); });
$('#naming_sports_pattern').qtip('toggle', true); $('#naming_sports_pattern').qtip('toggle', true);
$('#naming_sports_pattern').css('background-color', '#FFDDDD'); $('#naming_sports_pattern').css('background-color', '#FFDDDD');
} else if (data == "seasonfolders") { } else if (data == "seasonfolders") {
$('#naming_sports_pattern').qtip('option', { $('#naming_sports_pattern').qtip('option', {
'content.text': 'This pattern would be invalid without the folders, using it will force "Flatten" off for all shows.', 'content.text': 'This pattern would be invalid without the folders, using it will force "Flatten" off for all shows.',
'style.classes': 'qtip-rounded qtip-shadow qtip-red' 'style.classes': 'qtip-red qtip-rounded qtip-shadow'
}); });
$('#naming_sports_pattern').qtip('toggle', true); $('#naming_sports_pattern').qtip('toggle', true);
$('#naming_sports_pattern').css('background-color', '#FFFFDD'); $('#naming_sports_pattern').css('background-color', '#FFFFDD');
} else { } else {
$('#naming_sports_pattern').qtip('option', { $('#naming_sports_pattern').qtip('option', {
'content.text': 'This pattern is valid.', 'content.text': 'This pattern is valid.',
'style.classes': 'qtip-rounded qtip-shadow qtip-green' 'style.classes': 'qtip-green qtip-rounded qtip-shadow'
}); });
$('#naming_sports_pattern').qtip('toggle', false); $('#naming_sports_pattern').qtip('toggle', false);
$('#naming_sports_pattern').css('background-color', '#FFFFFF'); $('#naming_sports_pattern').css('background-color', '#FFFFFF');
@ -232,21 +232,21 @@ $(document).ready(function () {
if (data == "invalid") { if (data == "invalid") {
$('#naming_anime_pattern').qtip('option', { $('#naming_anime_pattern').qtip('option', {
'content.text': 'This pattern is invalid.', 'content.text': 'This pattern is invalid.',
'style.classes': 'qtip-rounded qtip-shadow qtip-red' 'style.classes': 'qtip-red qtip-rounded qtip-shadow'
}); });
$('#naming_anime_pattern').qtip('toggle', true); $('#naming_anime_pattern').qtip('toggle', true);
$('#naming_anime_pattern').css('background-color', '#FFDDDD'); $('#naming_anime_pattern').css('background-color', '#FFDDDD');
} else if (data == "seasonfolders") { } else if (data == "seasonfolders") {
$('#naming_anime_pattern').qtip('option', { $('#naming_anime_pattern').qtip('option', {
'content.text': 'This pattern would be invalid without the folders, using it will force "Flatten" off for all shows.', 'content.text': 'This pattern would be invalid without the folders, using it will force "Flatten" off for all shows.',
'style.classes': 'qtip-rounded qtip-shadow qtip-red' 'style.classes': 'qtip-red qtip-rounded qtip-shadow'
}); });
$('#naming_anime_pattern').qtip('toggle', true); $('#naming_anime_pattern').qtip('toggle', true);
$('#naming_anime_pattern').css('background-color', '#FFFFDD'); $('#naming_anime_pattern').css('background-color', '#FFFFDD');
} else { } else {
$('#naming_anime_pattern').qtip('option', { $('#naming_anime_pattern').qtip('option', {
'content.text': 'This pattern is valid.', 'content.text': 'This pattern is valid.',
'style.classes': 'qtip-rounded qtip-shadow qtip-green' 'style.classes': 'qtip-green qtip-rounded qtip-shadow'
}); });
$('#naming_anime_pattern').qtip('toggle', false); $('#naming_anime_pattern').qtip('toggle', false);
$('#naming_anime_pattern').css('background-color', '#FFFFFF'); $('#naming_anime_pattern').css('background-color', '#FFFFFF');
@ -485,35 +485,27 @@ $(document).ready(function () {
} }
$(this).refreshMetadataConfig(true); $(this).refreshMetadataConfig(true);
$('img[title]').qtip( { $('img[title]').qtip({
position: { position: {
viewport: $(window), viewport: $(window),
at: 'bottom center', my: 'top right',
my: 'top right' at: 'bottom center'
}, },
style: { style: {
tip: { classes: 'qtip-dark qtip-rounded qtip-shadow'
corner: true,
method: 'polygon'
},
classes: 'qtip-shadow qtip-dark'
} }
}); });
$('i[title]').qtip( { $('i[title]').qtip({
position: { position: {
viewport: $(window), viewport: $(window),
at: 'top center', my: 'bottom center',
my: 'bottom center' at: 'top center'
}, },
style: { style: {
tip: { classes: 'qtip-rounded qtip-shadow'
corner: true,
method: 'polygon'
},
classes: 'qtip-rounded qtip-shadow ui-tooltip-sb'
} }
}); });
$('.custom-pattern,#unpack').qtip( { $('.custom-pattern,#unpack').qtip({
content: 'validating...', content: 'validating...',
show: { show: {
event: false, event: false,
@ -522,15 +514,11 @@ $(document).ready(function () {
hide: false, hide: false,
position: { position: {
viewport: $(window), viewport: $(window),
at: 'center left', my: 'right center',
my: 'center right' at: 'left center'
}, },
style: { style: {
tip: { classes: 'qtip-red qtip-rounded qtip-shadow'
corner: true,
method: 'polygon'
},
classes: 'qtip-rounded qtip-shadow qtip-red'
} }
}); });

View file

@ -13,14 +13,14 @@ $(document).ready(function () {
$(this).val('jump'); $(this).val('jump');
}); });
$("#prevShow").click(function () { $('#prevShow, #nextShow').click(function () {
$('#pickShow option:selected').prev('option').attr('selected', 'selected'); var select$ = $('#pickShow'),
$("#pickShow").change(); index = $.inArray(select$.find('option:selected').val()*1, TVShowList);
}); select$.find('option[value="' + TVShowList[('nextShow' === $(this).attr('id')
? (index < TVShowList.length - 1 ? index + 1 : 0)
$("#nextShow").click(function () { : (0 < index ? index - 1 : TVShowList.length - 1))] + '"]').attr('selected', 'selected');
$('#pickShow option:selected').next('option').attr('selected', 'selected'); select$.change();
$("#pickShow").change(); return false;
}); });
$('#changeStatus').click(function () { $('#changeStatus').click(function () {

View file

@ -1,111 +1,136 @@
/*jQuery Form to Form Wizard (Initial: Oct 1st, 2010) /* jQuery Form to Form Wizard (Initial: Oct 1st, 2010)
* This notice must stay intact for usage * This notice must stay intact for usage
* Author: Dynamic Drive at http://www.dynamicdrive.com/ * Author: Dynamic Drive at http://www.dynamicdrive.com/
* Visit http://www.dynamicdrive.com/ for full source code * Visit http://www.dynamicdrive.com/ for full source code
*/ */
//Oct 21st, 2010: Script updated to v1.1, which adds basic form validation functionality, triggered each time the user goes from one page to the next, or tries to submit the form. // Oct 21st, 2010: Script updated to v1.1, which adds basic form validation functionality, triggered each time the user goes from one page to the next, or tries to submit the form.
// jQuery.noConflict()
//jQuery.noConflict()
function formtowizard(options){ function FormToWizard(options){
this.setting=jQuery.extend({persistsection:false, revealfx:['slide', 500], oninit:function(){}, onpagechangestart:function(){}}, options) this.setting = jQuery.extend({fieldsetborderwidth:2, persistsection:false, revealfx:['slide', 500],
this.currentsection=-1 oninit:function(){}, onpagechangestart:function(){}}, options);
this.currentsection = -1;
this.init(this.setting) this.init(this.setting)
} }
formtowizard.prototype={ FormToWizard.prototype = {
createfieldsets:function($theform, arr){ //reserved function for future version (dynamically wraps form elements with a fieldset element)
$theform.find('fieldset.sectionwrap').removeClass('sectionwrap') //make sure no fieldsets carry 'sectionwrap' before proceeding
var $startelement=$theform.find(':first-child') //reference first element inside form
for (var i=0; i<arr.length; i++){ //loop thru "break" elements
var $fieldsetelements=$startelement.nextUntil('#'+arr[i].breakafter+', *[name='+arr[i].breakafter+']').andSelf() //reference all elements from start element to break element (nextUntil() is jQuery 1.4 function)
$fieldsetelements.add($fieldsetelements.next()).wrapAll('<fieldset class="sectionwrap" />') //wrap these elements with fieldset element
$startelement=$theform.find('fieldset.sectionwrap').eq(i).prepend('<legend class="legendStep">'+arr[i].legend+'</legend>').next() //increment startelement to begin at the end of the just inserted fieldset element
}
},
loadsection:function(rawi, bypasshooks){ loadsection:function(rawi, bypasshooks){
var thiswizard=this
//doload Boolean checks to see whether to load next section (true if bypasshooks param is true or onpagechangestart() event handler doesn't return false) //doload Boolean checks to see whether to load next section (true if bypasshooks param is true or onpagechangestart() event handler doesn't return false)
var doload=bypasshooks || this.setting.onpagechangestart(jQuery, this.currentsection, this.sections.$sections.eq(this.currentsection)) var doload = bypasshooks || this.setting.onpagechangestart(jQuery, this.currentsection, this.sections.$sections.eq(this.currentsection)),
doload=(doload===false)? false : true //unless doload is explicitly false, set to true tabIndex,
if (!bypasshooks && this.setting.validate){ thiswizard = this;
var outcome=this.validate(this.currentsection)
if (outcome===false) doload = (doload !== false); //unless doload is explicitly false, set to true
doload=false if (!bypasshooks && this.setting.validate && false === this.validate(this.currentsection))
} doload = false;
var i=(rawi=="prev")? this.currentsection-1 : (rawi=="next")? this.currentsection+1 : parseInt(rawi) //get index of next section to show
i=(i<0)? this.sections.count-1 : (i>this.sections.count-1)? 0 : i //make sure i doesn't exceed min/max limit //get index of next section to show
if (i<this.sections.count && doload){ //if next section to show isn't the same as the current section shown tabIndex = ('prev' == rawi
this.$thesteps.eq(this.currentsection).addClass('disabledstep').end().eq(i).removeClass('disabledstep') //dull current "step" text then highlight next "step" text ? this.currentsection - 1
if (this.setting.revealfx[0]=="slide"){ : ('next' == rawi
this.sections.$sections.css("visibility", "visible") ? this.currentsection + 1
this.sections.$outerwrapper.stop().animate({height: this.sections.$sections.eq(i).outerHeight()}, this.setting.revealfx[1]) //animate fieldset wrapper's height to accomodate next section's height : parseInt(rawi)));
this.sections.$innerwrapper.stop().animate({left:-i*this.maxfieldsetwidth}, this.setting.revealfx[1], function(){ //slide next section into view
thiswizard.sections.$sections.each(function(thissec){ //don't exceed min/max limit
if (thissec!=i) //hide fieldset sections currently not in veiw, so tabbing doesn't go to elements within them (and mess up layout) tabIndex = (tabIndex < 0
thiswizard.sections.$sections.eq(thissec).css("visibility", "hidden") ? this.sections.count - 1
: (tabIndex > (this.sections.count - 1)
? 0
: tabIndex));
//if next section to show isn't the same as the current section shown
if (tabIndex < this.sections.count && doload){
//dull current 'step' text then highlight next 'step' text
this.$thesteps.eq(this.currentsection).addClass('disabledstep').end().eq(tabIndex).removeClass('disabledstep');
if ('slide' == this.setting.revealfx[0]) {
this.sections.$sections.css('visibility', 'visible');
//animate fieldset wrapper's height to accommodate next section's height
this.sections.$outerwrapper.stop().animate({height: this.sections.$sections.eq(tabIndex).outerHeight()}, this.setting.revealfx[1]);
//slide next section into view
this.sections.$innerwrapper.stop().animate({left: -tabIndex * this.maxfieldsetwidth}, this.setting.revealfx[1], function () {
thiswizard.sections.$sections.each(function (thissec) {
//hide fieldset sections currently not in view, so tabbing doesn't go to elements within them (and mess up layout)
if (tabIndex != thissec)
thiswizard.sections.$sections.eq(thissec).css('visibility', 'hidden')
}) })
}) })
} }
else if (this.setting.revealfx[0]=="fade"){ //if fx is "fade" else if ('fade' == this.setting.revealfx[0]) { //if fx is 'fade'
this.sections.$sections.eq(this.currentsection).hide().end().eq(i).fadeIn(this.setting.revealfx[1], function(){ this.sections.$sections.eq(this.currentsection).hide().end().eq(tabIndex).fadeIn(this.setting.revealfx[1], function () {
if (document.all && this.style && this.style.removeAttribute) if (document.all && this.style && this.style.removeAttribute)
this.style.removeAttribute('filter') //fix IE clearType problem //fix IE clearType problem
this.style.removeAttribute('filter');
}) })
} else {
this.sections.$sections.eq(this.currentsection).hide().end().eq(tabIndex).show()
} }
else{ //update current page status text
this.sections.$sections.eq(this.currentsection).hide().end().eq(i).show() this.paginatediv.$status.text('step ' + (tabIndex + 1) + ' / ' + this.sections.count);
} this.paginatediv.$navlinks.css('visibility', 'visible');
this.paginatediv.$status.text("Page "+(i+1)+" of "+this.sections.count) //update current page status text
this.paginatediv.$navlinks.css('visibility', 'visible') if (0 == tabIndex) //hide 'prev' link
if (i==0) //hide "prev" link this.paginatediv.$navlinks.eq(0).css('visibility', 'hidden');
this.paginatediv.$navlinks.eq(0).css('visibility', 'hidden') else if ((this.sections.count - 1) == tabIndex)
else if (i==this.sections.count-1) //hide "next" link //hide 'next' link
this.paginatediv.$navlinks.eq(1).css('visibility', 'hidden') this.paginatediv.$navlinks.eq(1).css('visibility', 'hidden');
if (this.setting.persistsection) //enable persistence? if (this.setting.persistsection) //enable persistence?
formtowizard.routines.setCookie(this.setting.formid+"_persist", i) FormToWizard.routines.setCookie(this.setting.formid + '_persist', tabIndex);
this.currentsection=i this.currentsection = tabIndex;
if(i === 0) { setTimeout(function() { $('#nameToSearch').focus(); }, 250); } if (0 === tabIndex) {
setTimeout(function () {
$('#nameToSearch').focus();
}, 250);
}
} }
}, },
addvalidatefields:function(){ addvalidatefields:function(){
var $=jQuery, setting=this.setting, theform=this.$theform.get(0), validatefields=[] var $ = jQuery,
var validatefields=setting.validate //array of form element ids to validate setting = this.setting,
for (var i=0; i<validatefields.length; i++){ theform = this.$theform.get(0),
var el=theform.elements[validatefields[i]] //reference form element validatefields = setting.validate; //array of form element ids to validate
if (el){ //if element is defined
var $section=$(el).parents('fieldset.sectionwrap:eq(0)') //find fieldset.sectionwrap this form element belongs to for (var i = 0; i < validatefields.length; i++){
if ($section.length==1){ //if element is within a fieldset.sectionwrap element var el = theform.elements[validatefields[i]]; //reference form element
$section.data('elements').push(el) //cache this element inside corresponding section if (el){
//find fieldset.sectionwrap this form element belongs to
var $section = $(el).parents('fieldset.sectionwrap:eq(0)');
//if element is within a fieldset.sectionwrap element
if ($section.length == 1){
//cache this element inside corresponding section
$section.data('elements').push(el);
} }
} }
} }
}, },
validate:function(section){ validate:function(section){
var elements=this.sections.$sections.eq(section).data('elements') //reference elements within this section that should be validated //reference elements within this section that should be validated
var validated=true, invalidtext=["Please fill out the following fields:\n"] var elements = this.sections.$sections.eq(section).data('elements');
var validated = true, invalidtext = ['Please fill out the following fields:' + "\n"];
function invalidate(el){ function invalidate(el){
validated=false validated = false;
invalidtext.push("- "+ (el.id || el.name)) invalidtext.push('- '+ (el.id || el.name))
} }
for (var i=0; i<elements.length; i++){ for (var i = 0; i < elements.length; i++){
if (/(text)/.test(elements[i].type) && elements[i].value==""){ //text and textarea elements if (/(text)/.test(elements[i].type) && elements[i].value == ''){
//text and textarea elements
invalidate(elements[i]) invalidate(elements[i])
} } else if (/(select)/.test(elements[i].type) && (elements[i].selectedIndex == -1 || elements[i].options[elements[i].selectedIndex].text == '')){
else if (/(select)/.test(elements[i].type) && (elements[i].selectedIndex==-1 || elements[i].options[elements[i].selectedIndex].text=="")){ //select elements //select elements
invalidate(elements[i]) invalidate(elements[i])
} } else if (undefined == elements[i].type && 0 < elements[i].length){
else if (elements[i].type==undefined && elements[i].length>0){ //radio and checkbox elements //radio and checkbox elements
var onechecked=false var onechecked = false;
for (var r=0; r<elements[i].length; r++){ for (var r = 0; r < elements[i].length; r++){
if (elements[i][r].checked==true){ if (elements[i][r].checked == true){
onechecked=true onechecked = true;
break break
} }
} }
@ -115,98 +140,154 @@ formtowizard.prototype={
} }
} }
if (!validated) if (!validated)
alert(invalidtext.join('\n')) alert(invalidtext.join("\n"));
return validated return validated
}, },
init:function(setting){ init:function(setting){
var thiswizard=this var thiswizard = this;
jQuery(function($){ //on document.ready jQuery(function($){ //on document.ready
var $theform=$('#'+setting.formid) var $theform = $('#' + setting.formid),
if ($theform.length==0) //if form with specified ID doesn't exist, try name attribute instead //create Steps Container to house the 'steps' text
$theform=$('form[name='+setting.formid+']') $stepsguide = $('<div class="stepsguide" />'),
if (setting.manualfieldsets && setting.manualfieldsets.length>0)
thiswizard.createfieldsets($theform, setting.manualfieldsets) //find all fieldsets within form and hide them initially
var $stepsguide=$('<div class="stepsguide" />') //create Steps Container to house the "steps" text $sections = $theform.find('fieldset.sectionwrap').hide(),
var $sections=$theform.find('fieldset.sectionwrap').hide() //find all fieldset elements within form and hide them initially $sectionswrapper = '',
if (setting.revealfx[0]=="slide"){ //create outer DIV that will house all the fieldset.sectionwrap elements $sectionswrapper_inner = '';
$sectionswrapper=$('<div style="position:relative;overflow:hidden;"></div>').insertBefore($sections.eq(0)) //add DIV above the first fieldset.sectionwrap element
$sectionswrapper_inner=$('<div style="position:absolute;left:0;top:0;"></div>') //create inner DIV of $sectionswrapper that will scroll to reveal a fieldset element if (0 == $theform.length)
//if form with specified ID doesn't exist, try name attribute instead
$theform = $('form[name=' + setting.formid + ']');
if ('slide' == setting.revealfx[0]) {
//create outer DIV that will house all the fieldset.sectionwrap elements
//add DIV above the first fieldset.sectionwrap element
$sectionswrapper = $('<div class="step-outer" style="position:relative"></div>').insertBefore($sections.eq(0));
//create inner DIV of $sectionswrapper that will scroll to reveal a fieldset element
$sectionswrapper_inner = $('<div class="step-inner" style="position:absolute; left:0"></div>');
} }
var maxfieldsetwidth=$sections.eq(0).outerWidth() //variable to get width of widest fieldset.sectionwrap
$sections.slice(1).each(function(i){ //loop through $sections (starting from 2nd one) //variable to get width of widest fieldset.sectionwrap
maxfieldsetwidth=Math.max($(this).outerWidth(), maxfieldsetwidth) var maxfieldsetwidth = $sections.eq(0).outerWidth();
})
maxfieldsetwidth+=2 //add 2px to final width to reveal fieldset border (if not removed via CSS) //loop through $sections (starting from 2nd one)
thiswizard.maxfieldsetwidth=maxfieldsetwidth $sections.slice(1).each(function(){
$sections.each(function(i){ //loop through $sections again maxfieldsetwidth = Math.max($(this).outerWidth(), maxfieldsetwidth)
var $section=$(this) });
if (setting.revealfx[0]=="slide"){
$section.data('page', i).css({position:'absolute', top:0, left:maxfieldsetwidth*i}).appendTo($sectionswrapper_inner) //set fieldset position to "absolute" and move it to inside sectionswrapper_inner DIV //add default 2px or param px to final width to reveal fieldset border (if not removed via CSS)
maxfieldsetwidth += setting.fieldsetborderwidth;
thiswizard.maxfieldsetwidth = maxfieldsetwidth;
//loop through $sections again
$sections.each(function(i){
var $section = $(this);
if ('slide' == setting.revealfx[0]) {
//set fieldset position to 'absolute' and move it to inside sectionswrapper_inner DIV
$section.data('page', i).css({position: 'absolute', left: maxfieldsetwidth * i}).appendTo($sectionswrapper_inner);
} }
$section.data('elements', []) //empty array to contain elements within this section that should be validated for data (applicable only if validate option is defined) //empty array to contain elements within this section that should be validated for data (applicable only if validate option is defined)
//create each "step" DIV and add it to main Steps Container: $section.data('elements', []);
var $thestep=$('<div class="step disabledstep" />').data('section', i).html('Step '+(i+1)+'<div class="smalltext">'+$section.find('legend:eq(0)').text()+'<p></p></div>').appendTo($stepsguide)
$thestep.click(function(){ //assign behavior to each step div //create each 'step' DIV and add it to main Steps Container:
var $stepwords = ['first', 'second', 'third'], $thestep = $('<div class="step disabledstep" />').data('section', i).html(($stepwords[i]
+ ' step') + '<div class="smalltext">' + $section.find('legend:eq(0)').text() + '<p></p></div>').appendTo($stepsguide);
//assign behavior to each step div
$thestep.click(function(){
thiswizard.loadsection($(this).data('section')) thiswizard.loadsection($(this).data('section'))
}) })
}) });
if (setting.revealfx[0]=="slide"){
$sectionswrapper.width(maxfieldsetwidth) //set fieldset wrapper to width of widest fieldset if ('slide' == setting.revealfx[0]) {
$sectionswrapper.append($sectionswrapper_inner) //add $sectionswrapper_inner as a child of $sectionswrapper $sectionswrapper.width(maxfieldsetwidth); //set fieldset wrapper to width of widest fieldset
$sectionswrapper.append($sectionswrapper_inner); //add $sectionswrapper_inner as a child of $sectionswrapper
$stepsguide.append('<div style="clear:both">&nbsp;</div>')
} }
$theform.prepend($stepsguide) //add $thesteps div to the beginning of the form
//add $thesteps div to the beginning of the form
$theform.prepend($stepsguide);
//$stepsguide.insertBefore($sectionswrapper) //add Steps Container before sectionswrapper container //$stepsguide.insertBefore($sectionswrapper) //add Steps Container before sectionswrapper container
var $thesteps=$stepsguide.find('div.step') var $thesteps = $stepsguide.find('div.step');
//create pagination DIV and add it to end of form: //create pagination DIV and add it to end of form:
var $paginatediv=$('<div class="formpaginate" style="overflow:hidden;"><span class="prev" style="float:left">Prev</span> <span class="status">Step 1 of </span> <span class="next" style="float:right">Next</span></div>') var $paginatediv = $('<div class="formpaginate">'
$theform.append($paginatediv) + '<span class="prev" style="float:left">Prev</span>'
thiswizard.$theform=$theform + ' <span class="status">step 1 of </span>'
if (setting.revealfx[0]=="slide"){ + ' <span class="next" style="float:right">Next</span>'
thiswizard.sections={$outerwrapper:$sectionswrapper, $innerwrapper:$sectionswrapper_inner, $sections:$sections, count:$sections.length} //remember various parts of section container + '</div>');
$theform.append($paginatediv);
thiswizard.$theform = $theform;
if ('slide' == setting.revealfx[0]) {
//remember various parts of section container
thiswizard.sections = {
$outerwrapper: $sectionswrapper,
$innerwrapper: $sectionswrapper_inner,
$sections: $sections,
count: $sections.length
};
thiswizard.sections.$sections.show() thiswizard.sections.$sections.show()
} else {
//remember various parts of section container
thiswizard.sections = {
$sections: $sections,
count: $sections.length
};
} }
else{ thiswizard.$thesteps = $thesteps;
thiswizard.sections={$sections:$sections, count:$sections.length} //remember various parts of section container
} //remember various parts of pagination DIV
thiswizard.$thesteps=$thesteps //remember this ref thiswizard.paginatediv = {
thiswizard.paginatediv={$main:$paginatediv, $navlinks:$paginatediv.find('span.prev, span.next'), $status:$paginatediv.find('span.status')} //remember various parts of pagination DIV $main: $paginatediv,
thiswizard.paginatediv.$main.click(function(e){ //assign behavior to pagination buttons $navlinks: $paginatediv.find('span.prev, span.next'),
$status: $paginatediv.find('span.status')
};
//assign behavior to pagination buttons
thiswizard.paginatediv.$main.click(function(e){
if (/(prev)|(next)/.test(e.target.className)) if (/(prev)|(next)/.test(e.target.className))
thiswizard.loadsection(e.target.className) thiswizard.loadsection(e.target.className)
}) });
var i=(setting.persistsection)? formtowizard.routines.getCookie(setting.formid+"_persist") : 0
thiswizard.loadsection(i||0, true) //show the first section var i = (setting.persistsection ? FormToWizard.routines.getCookie(setting.formid + '_persist') : 0);
thiswizard.setting.oninit($, i, $sections.eq(i)) //call oninit event handler
if (setting.validate){ //if validate array defined //show the first section
thiswizard.addvalidatefields() //seek out and cache form elements that should be validated thiswizard.loadsection(i||0, true);
//call oninit event handler
thiswizard.setting.oninit($, i, $sections.eq(i));
//if validate array defined
if (setting.validate){
//seek out and cache form elements that should be validated
thiswizard.addvalidatefields();
thiswizard.$theform.submit(function(){ thiswizard.$theform.submit(function(){
var returnval=true for (var i = 0; i < thiswizard.sections.count; i++){
for (var i=0; i<thiswizard.sections.count; i++){
if (!thiswizard.validate(i)){ if (!thiswizard.validate(i)){
thiswizard.loadsection(i, true) thiswizard.loadsection(i, true);
returnval=false return false;
break
} }
} }
return returnval //allow or disallow form submission return true;
}) })
} }
}) })
} }
} };
formtowizard.routines={ FormToWizard.routines = {
getCookie:function(Name){ getCookie:function(Name){
var re=new RegExp(Name+"=[^;]+", "i"); //construct RE to search for target name/value pair var re = new RegExp(Name + '=[^;]+', 'i'); //construct RE to search for target name/value pair
if (document.cookie.match(re)) //if cookie found if (document.cookie.match(re)) //if cookie found
return document.cookie.match(re)[0].split("=")[1] //return its value return document.cookie.match(re)[0].split('=')[1]; //return its value
return null return null
}, },
setCookie:function(name, value){ setCookie:function(name, value){
document.cookie = name+"=" + value + ";path=/" document.cookie = name + '=' + value + ';path=/';
} }
} };

View file

@ -54,11 +54,7 @@
} }
}, },
style: { style: {
tip: { classes: 'qtip-dark qtip-rounded qtip-shadow'
corner: true,
method: 'polygon'
},
classes: 'qtip-rounded qtip-dark qtip-shadow ui-tooltip-sb'
} }
}); });
}); });

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,34 +1,40 @@
$(document).ready(function() { $(document).ready(function() {
function make_row(indexer_id, season, episode, name, checked) { function make_row(indexer_id, season, episode, name, checked) {
if (checked) var checkedbox = (checked ? ' checked' : ''),
var checked = ' checked'; row_class = $('#row_class').val();
else
var checked = ''; return ' <tr class="' + row_class + '">'
+ ' <td class="tableleft" align="center">'
var row_class = $('#row_class').val(); + '<input type="checkbox"'
+ ' class="' + indexer_id + '-epcheck"'
var row = ''; + ' name="' + indexer_id + '-' + season + 'x' + episode + '"'
row += ' <tr class="'+row_class+'">'; + checkedbox+'></td>'
row += ' <td class="tableleft" align="center"><input type="checkbox" class="'+indexer_id+'-epcheck" name="'+indexer_id+'-'+season+'x'+episode+'"'+checked+'></td>'; + ' <td>' + season + 'x' + episode + '</td>'
row += ' <td>'+season+'x'+episode+'</td>'; + ' <td class="tableright" style="width: 100%">' + name + '</td>'
row += ' <td class="tableright" style="width: 100%">'+name+'</td>'; + ' </tr>';
row += ' </tr>'
return row;
} }
$('.go').click(function() {
var selected;
if (selected = (0 === $('input[class*="-epcheck"]:checked').length))
alert('Please select at least one episode');
return !selected
});
$('.allCheck').click(function(){ $('.allCheck').click(function(){
var indexer_id = $(this).attr('id').split('-')[1]; var indexer_id = $(this).attr('id').split('-')[1];
$('.'+indexer_id+'-epcheck').prop('checked', $(this).prop('checked')); $('.' + indexer_id + '-epcheck').prop('checked', $(this).prop('checked'));
}); });
$('.get_more_eps').click(function(){ $('.get_more_eps').click(function(){
var cur_indexer_id = $(this).attr('id'); var cur_indexer_id = $(this).attr('id');
var checked = $('#allCheck-'+cur_indexer_id).prop('checked'); var checked = $('#allCheck-' + cur_indexer_id).prop('checked');
var last_row = $('tr#'+cur_indexer_id); var last_row = $('tr#' + cur_indexer_id);
$.getJSON(sbRoot+'/manage/showEpisodeStatuses', $.getJSON(sbRoot + '/manage/showEpisodeStatuses',
{ {
indexer_id: cur_indexer_id, indexer_id: cur_indexer_id,
whichStatus: $('#oldStatus').val() whichStatus: $('#oldStatus').val()

View file

@ -1,227 +1,278 @@
$(document).ready(function () { $(document).ready(function () {
function populateSelect() { function populateSelect() {
if (!$('#nameToSearch').length) { if (!$('#nameToSearch').length)
return; return;
}
if ($('#indexerLangSelect option').length <= 1) { if (1 >= $('#indexerLangSelect').find('option').length) {
$.getJSON(sbRoot + '/home/addShows/getIndexerLanguages', {}, function (data) {
var selected, resultStr = '';
if (data.results.length === 0) { $.getJSON(sbRoot + '/home/addShows/getIndexerLanguages', {}, function (data) {
resultStr = '<option value="en" selected="selected">en</option>';
} else {
$.each(data.results, function (index, obj) {
if (resultStr == '') {
selected = ' selected="selected"';
} else {
selected = '';
}
resultStr += '<option value="' + obj + '"' + selected + '>' + obj + '</option>'; var resultStr = '',
}); selected = ' selected="selected"',
} elIndexerLang = $('#indexerLangSelect');
$('#indexerLangSelect').html(resultStr); if (0 === data.results.length) {
$('#indexerLangSelect').change(function () { searchIndexers(); }); resultStr = '<option value="en"' + selected + '>en</option>';
}); } else {
} $.each(data.results, function (index, obj) {
} resultStr += '<option value="' + obj + '"'
+ ('' == resultStr ? selected : '')
+ '>' + obj + '</option>';
});
}
var searchRequestXhr = null; elIndexerLang.html(resultStr);
elIndexerLang.change(function () {
searchIndexers();
});
});
}
}
function searchIndexers() { var searchRequestXhr = null;
if (!$('#nameToSearch').val().length) {
return;
}
if (searchRequestXhr) searchRequestXhr.abort(); function searchIndexers() {
var elNameToSearch = $('#nameToSearch');
var searchingFor = $('#nameToSearch').val() + ' on ' + $('#providedIndexer option:selected').text() + ' in ' + $('#indexerLangSelect').val(); if (!elNameToSearch.val().length)
$('#searchResults').empty().html('<img id="searchingAnim" src="' + sbRoot + '/images/loading32' + themeSpinner + '.gif" height="32" width="32" /> searching ' + searchingFor + '...'); return;
searchRequestXhr = $.ajax({ if (searchRequestXhr)
url: sbRoot + '/home/addShows/searchIndexersForShowName', searchRequestXhr.abort();
data: {'search_term': $('#nameToSearch').val(), 'lang': $('#indexerLangSelect').val(), 'indexer': $('#providedIndexer').val()},
timeout: parseInt($('#indexer_timeout').val(), 10) * 1000,
dataType: 'json',
error: function () {
$('#searchResults').empty().html('search timed out, try again or try another indexer');
},
success: function (data) {
var firstResult = true;
var resultStr = '<fieldset>\n<legend>Search Results:</legend>\n';
var checked = '';
if (data.results.length === 0) { var elTvDatabase = $('#providedIndexer'),
resultStr += '<b>No results found, try a different search.</b>'; elIndexerLang = $('#indexerLangSelect'),
} else { searchingFor = elNameToSearch.val() + ' on ' + elTvDatabase.find('option:selected').text() + ' in ' + elIndexerLang.val();
$.each(data.results, function (index, obj) {
if (firstResult) {
checked = ' checked';
firstResult = false;
} else {
checked = '';
}
var whichSeries = obj.join('|'); $('#searchResults').empty().html('<img id="searchingAnim" src="' + sbRoot + '/images/loading32' + themeSpinner + '.gif" height="32" width="32" /> searching ' + searchingFor + '...');
searchRequestXhr = $.ajax({
url: sbRoot + '/home/addShows/searchIndexersForShowName',
data: {
'search_term': elNameToSearch.val(),
'lang': elIndexerLang.val(),
'indexer': elTvDatabase.val()
},
timeout: parseInt($('#indexer_timeout').val(), 10) * 1000,
dataType: 'json',
error: function () {
$('#searchResults').empty().html('search timed out, try again or try another database');
},
success: function (data) {
var resultStr = '', checked = '', rowType, row = 0;
resultStr += '<input type="radio" id="whichSeries" name="whichSeries" value="' + whichSeries + '"' + checked + ' /> '; if (0 === data.results.length) {
if (data.langid && data.langid != "") { resultStr += '<span class="boldest">Sorry, no results found. Try a different search.</span>';
resultStr += '<a href="' + anonURL + obj[2] + obj[3] + '&lid=' + data.langid + '" onclick=\"window.open(this.href, \'_blank\'); return false;\" ><b>' + obj[4] + '</b></a>'; } else {
} else { $.each(data.results, function (index, obj) {
resultStr += '<a href="' + anonURL + obj[2] + obj[3] + '" onclick=\"window.open(this.href, \'_blank\'); return false;\" ><b>' + obj[4] + '</b></a>'; checked = (0 == row ? ' checked' : '');
} rowType = (0 == row % 2 ? '' : ' class="alt"');
row++;
if (obj[5] !== null) { var whichSeries = obj.join('|'),
var startDate = new Date(obj[5]); showstartdate = '';
var today = new Date();
if (startDate > today) {
resultStr += ' (will debut on ' + obj[5] + ')';
} else {
resultStr += ' (started on ' + obj[5] + ')';
}
}
if (obj[0] !== null) { if (null !== obj[5]) {
resultStr += ' [' + obj[0] + ']'; var startDate = new Date(obj[5]);
} var today = new Date();
showstartdate = '&nbsp;<span class="stepone-result-date">('
+ (startDate > today ? 'will debut' : 'started')
+ ' on ' + obj[5] + ')</span>';
}
resultStr += '<br />'; resultStr += '<div' + rowType + '>'
}); + '<input id="whichSeries" type="radio"'
resultStr += '</ul>'; + ' class="stepone-result-radio"'
} + ' title="Add show <span style=\'color: rgb(66, 139, 202)\'>' + obj[4] + '</span>"'
resultStr += '</fieldset>'; + ' name="whichSeries"'
$('#searchResults').html(resultStr); + ' value="' + whichSeries + '"'
updateSampleText(); + checked
myform.loadsection(0); + ' />'
} + '<a'
}); + ' class="stepone-result-title"'
} + ' title="View detail for <span style=\'color: rgb(66, 139, 202)\'>' + obj[4] + '</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>'
+ showstartdate
+ (null == obj[0] ? ''
: '&nbsp;<span class="stepone-result-db grey-text">' + '[' + obj[0] + ']' + '</span>')
+ '</div>' + "\n";
});
}
$('#searchResults').html(
'<fieldset>' + "\n" + '<legend class="legendStep" style="margin-bottom: 15px">'
+ (0 < row ? row : 'No')
+ ' search result' + (1 == row ? '' : 's') + '...</legend>' + "\n"
+ resultStr
+ '</fieldset>'
);
updateSampleText();
myform.loadsection(0);
$('.stepone-result-radio, .stepone-result-title').each(addQTip);
}
});
}
$('#searchName').click(function () { searchIndexers(); }); var elNameToSearch = $('#nameToSearch'),
elSearchName = $('#searchName');
if ($('#nameToSearch').length && $('#nameToSearch').val().length) { elSearchName.click(function () { searchIndexers(); });
$('#searchName').click();
}
$('#addShowButton').click(function () { if (elNameToSearch.length && elNameToSearch.val().length) {
// if they haven't picked a show don't let them submit elSearchName.click();
if (!$("input:radio[name='whichSeries']:checked").val() && !$("input:hidden[name='whichSeries']").val().length) { }
alert('You must choose a show to continue');
return false;
}
$('#addShowForm').submit(); $('#addShowButton').click(function () {
}); // if they haven't picked a show don't let them submit
if (!$('input:radio[name="whichSeries"]:checked').val()
&& !$('input:hidden[name="whichSeries"]').val().length) {
alert('You must choose a show to continue');
return false;
}
$('#addShowForm').submit();
});
$('#skipShowButton').click(function () { $('#skipShowButton').click(function () {
$('#skipShow').val('1'); $('#skipShow').val('1');
$('#addShowForm').submit(); $('#addShowForm').submit();
}); });
$('#qualityPreset').change(function () { $('#qualityPreset').change(function () {
myform.loadsection(2); myform.loadsection(2);
}); });
/*********************************************** /***********************************************
* jQuery Form to Form Wizard- (c) Dynamic Drive (www.dynamicdrive.com) * jQuery Form to Form Wizard- (c) Dynamic Drive (www.dynamicdrive.com)
* This notice MUST stay intact for legal use * This notice MUST stay intact for legal use
* Visit http://www.dynamicdrive.com/ for this script and 100s more. * Visit http://www.dynamicdrive.com/ for this script and 100s more.
***********************************************/ ***********************************************/
var myform = new formtowizard({ var myform = new FormToWizard({
formid: 'addShowForm', fieldsetborderwidth: 0,
revealfx: ['slide', 500], formid: 'addShowForm',
oninit: function () { revealfx: ['slide', 500],
populateSelect(); oninit: function () {
updateSampleText(); populateSelect();
if ($('input:hidden[name=whichSeries]').length && $('#fullShowPath').length) { updateSampleText();
goToStep(3); if ($('input:hidden[name="whichSeries"]').length && $('#fullShowPath').length) {
} goToStep(3);
} }
}); }
});
function goToStep(num) { function goToStep(num) {
$('.step').each(function () { $('.step').each(function () {
if ($.data(this, 'section') + 1 == num) { if ($.data(this, 'section') + 1 == num) {
$(this).click(); $(this).click();
} }
}); });
} }
$('#nameToSearch').focus(); elNameToSearch.focus();
function updateSampleText() { function updateSampleText() {
// if something's selected then we have some behavior to figure out // if something's selected then we have some behavior to figure out
var show_name, sep_char; var show_name,
// if they've picked a radio button then use that sep_char,
if ($('input:radio[name=whichSeries]:checked').length) { elRadio = $('input:radio[name="whichSeries"]:checked'),
show_name = $('input:radio[name=whichSeries]:checked').val().split('|')[4]; elInput = $('input:hidden[name="whichSeries"]'),
} elRootDirs = $('#rootDirs'),
// if we provided a show in the hidden field, use that elFullShowPath = $('#fullShowPath');
else if ($('input:hidden[name=whichSeries]').length && $('input:hidden[name=whichSeries]').val().length) {
show_name = $('#providedName').val();
} else {
show_name = '';
}
var sample_text = 'Adding show <b>' + show_name + '</b> into <b>'; // if they've picked a radio button then use that
if (elRadio.length) {
show_name = elRadio.val().split('|')[4];
}
// if we provided a show in the hidden field, use that
else if (elInput.length && elInput.val().length) {
show_name = $('#providedName').val();
} else {
show_name = '';
}
// if we have a root dir selected, figure out the path var sample_text = '<p>Adding show <span class="show-name">' + show_name + '</span>'
if ($("#rootDirs option:selected").length) { + ('' == show_name ? 'into<br />' : '<br />into')
var root_dir_text = $('#rootDirs option:selected').val(); + ' <span class="show-dest">';
if (root_dir_text.indexOf('/') >= 0) {
sep_char = '/';
} else if (root_dir_text.indexOf('\\') >= 0) {
sep_char = '\\';
} else {
sep_char = '';
}
if (root_dir_text.substr(sample_text.length - 1) != sep_char) { // if we have a root dir selected, figure out the path
root_dir_text += sep_char; if (elRootDirs.find('option:selected').length) {
} var root_dir_text = elRootDirs.find('option:selected').val();
root_dir_text += '<i>||</i>' + sep_char; if (root_dir_text.indexOf('/') >= 0) {
sep_char = '/';
} else if (root_dir_text.indexOf('\\') >= 0) {
sep_char = '\\';
} else {
sep_char = '';
}
sample_text += root_dir_text; if (root_dir_text.substr(sample_text.length - 1) != sep_char) {
} else if ($('#fullShowPath').length && $('#fullShowPath').val().length) { root_dir_text += sep_char;
sample_text += $('#fullShowPath').val(); }
} else { root_dir_text += '<i>||</i>' + sep_char;
sample_text += 'unknown dir.';
}
sample_text += '</b>'; sample_text += root_dir_text;
} else if (elFullShowPath.length && elFullShowPath.val().length) {
sample_text += elFullShowPath.val();
} else {
sample_text += 'unknown dir.';
}
// if we have a show name then sanitize and use it for the dir name sample_text += '</span></p>';
if (show_name.length) {
$.get(sbRoot + '/home/addShows/sanitizeFileName', {name: show_name}, function (data) {
$('#displayText').html(sample_text.replace('||', data));
});
// if not then it's unknown
} else {
$('#displayText').html(sample_text.replace('||', '??'));
}
// also toggle the add show button // if we have a show name then sanitize and use it for the dir name
if (($("#rootDirs option:selected").length || ($('#fullShowPath').length && $('#fullShowPath').val().length)) && if (show_name.length) {
($('input:radio[name=whichSeries]:checked').length) || ($('input:hidden[name=whichSeries]').length && $('input:hidden[name=whichSeries]').val().length)) { $.get(sbRoot + '/home/addShows/sanitizeFileName', {name: show_name}, function (data) {
$('#addShowButton').attr('disabled', false); $('#displayText').html(sample_text.replace('||', data));
} else { });
$('#addShowButton').attr('disabled', true); // if not then it's unknown
} } else {
} $('#displayText').html(sample_text.replace('||', '??'));
}
$('#rootDirText').change(updateSampleText); // also toggle the add show button
$('#whichSeries').live('change', updateSampleText); if ((elRootDirs.find('option:selected').length || (elFullShowPath.length && elFullShowPath.val().length)) &&
(elRadio.length) || (elInput.length && elInput.val().length)) {
$('#addShowButton').attr('disabled', false);
} else {
$('#addShowButton').attr('disabled', true);
}
}
$('#nameToSearch').keyup(function (event) { $('#rootDirText').change(updateSampleText);
if (event.keyCode == 13) {
$('#searchName').click(); $('#searchResults').on('click', '.stepone-result-radio', updateSampleText);
}
}); elNameToSearch.keyup(function (event) {
if (event.keyCode == 13) {
elSearchName.click();
}
});
var addQTip = (function() {
$(this).css('cursor', 'help');
$(this).qtip({
show: {
solo: true
},
position: {
viewport: $(window),
my: 'left center',
adjust: {
y: -10,
x: 2
}
},
style: {
tip: {
corner: true,
method: 'polygon'
},
classes: 'qtip-rounded qtip-bootstrap qtip-shadow ui-tooltip-sb'
}
});
});
}); });

View file

@ -1,20 +1,27 @@
$(function () { $(function () {
$('.plotInfo').each(function () { $('.plotInfo').each(function () {
match = $(this).attr("id").match(/^plot_info_(\d+)_(\d+)_(\d+)$/); var match = $(this).attr('id').match(/^plot_info_(\d+)_(\d+)_(\d+)$/);
$(this).qtip({ $(this).qtip({
content: { content: {
text: 'Loading...', text: function(event, api) {
ajax: { // deferred object ensuring the request is only made once
url: $("#sbRoot").val() + '/home/plotDetails', $.ajax({
type: 'GET', url: $('#sbRoot').val() + '/home/plotDetails',
data: { type: 'GET',
show: match[1], data: {
episode: match[3], show: match[1],
season: match[2] episode: match[3],
}, season: match[2]
success: function (data, status) { }
this.set('content.text', data); })
} .then(function(content) {
// Set the tooltip content upon successful retrieval
api.set('content.text', content);
}, function(xhr, status, error) {
// Upon failure... set the tooltip content to the status and error value
api.set('content.text', status + ': ' + error);
});
return 'Loading...'; // Set initial text
} }
}, },
show: { show: {
@ -29,11 +36,7 @@ $(function () {
} }
}, },
style: { style: {
tip: { classes: 'qtip-rounded qtip-shadow'
corner: true,
method: 'polygon'
},
classes: 'qtip-rounded qtip-shadow ui-tooltip-sb'
} }
}); });
}); });

View file

@ -1,36 +1,31 @@
function setFromPresets (preset) {
var elCustomQuality = $('#customQuality'),
selected = 'selected';
if (0 == preset) {
elCustomQuality.show();
return;
}
elCustomQuality.hide();
$('#anyQualities').find('option').each(function() {
var result = preset & $(this).val();
$(this).attr(selected, (0 < result ? selected : false));
});
$('#bestQualities').find('option').each(function() {
var result = preset & ($(this).val() << 16);
$(this).attr(selected, (result > 0 ? selected: false));
});
}
$(document).ready(function() { $(document).ready(function() {
function setFromPresets (preset) { var elQualityPreset = $('#qualityPreset'),
if (preset == 0) { selected = ':selected';
$('#customQuality').show();
return;
} else {
$('#customQuality').hide();
}
$('#anyQualities option').each(function(i) { elQualityPreset.change(function() {
var result = preset & $(this).val(); setFromPresets($('#qualityPreset').find(selected).val());
if (result > 0) { });
$(this).attr('selected', 'selected');
} else {
$(this).attr('selected', false);
}
});
$('#bestQualities option').each(function(i) { setFromPresets(elQualityPreset.find(selected).val());
var result = preset & ($(this).val() << 16); });
if (result > 0) {
$(this).attr('selected', 'selected');
} else {
$(this).attr('selected', false);
}
});
return;
}
$('#qualityPreset').change(function() {
setFromPresets($('#qualityPreset :selected').val());
});
setFromPresets($('#qualityPreset :selected').val());
});

View file

@ -15,15 +15,11 @@ $(function () {
at: 'center left', at: 'center left',
adjust: { adjust: {
y: 0, y: 0,
x: -6 x: -2
} }
}, },
style: { style: {
tip: { classes: 'qtip-rounded qtip-shadow'
corner: true,
method: 'polygon'
},
classes: 'qtip-rounded qtip-shadow ui-tooltip-sb'
} }
}); });
}); });

View file

@ -1,144 +1,211 @@
$(document).ready(function () { $(document).ready(function (){
function getRecommendedShows() {
$.getJSON(sbRoot + '/home/addShows/getRecommendedShows', {}, function (data) {
var firstResult = true;
var resultStr = '<fieldset>\n<legend>Recommended Shows:</legend>\n';
var checked = '';
if (data.results.length === 0) { function getRecommendedShows(){
resultStr += '<b>No recommended shows found, update your watched shows list on trakt.tv.</b>';
} else {
$.each(data.results, function (index, obj) {
if (firstResult) {
checked = ' checked';
firstResult = false;
} else {
checked = '';
}
var whichSeries = obj.join('|'); $('#searchResults').empty().html('<img id="searchingAnim"'
+ ' src="' + sbRoot + '/images/loading32' + themeSpinner + '.gif"'
+ ' height="32" width="32" />'
+ ' fetching recommendations...');
resultStr += '<input type="radio" id="whichSeries" name="whichSeries" value="' + whichSeries + '"' + checked + ' /> '; $.getJSON(sbRoot + '/home/addShows/getRecommendedShows',
resultStr += '<a href="' + anonURL + obj[1] + '" onclick="window.open(this.href, \'_blank\'); return false;"><b>' + obj[2] + '</b></a>'; {},
function (data){
var resultStr = '', checked = '', rowType, row = 0;
if (obj[4] !== null) { if (null === data || 0 === data.results.length){
var startDate = new Date(obj[4]); resultStr += '<p>Sorry, no recommended shows found, this can happen from time to time.</p>'
var today = new Date(); + '<p>However, if the issue persists, then try updating your watched shows list on trakt.tv</p>';
if (startDate > today) { } else {
resultStr += ' (will debut on ' + obj[4] + ')';
} else {
resultStr += ' (started on ' + obj[4] + ')';
}
}
if (obj[0] !== null) { $.each(data.results, function (index, obj){
resultStr += ' [' + obj[0] + ']'; checked = (0 == row ? ' checked' : '');
} rowType = (0 == row % 2 ? '' : ' class="alt"');
row++;
if (obj[3] !== null) { var whichSeries = obj[6] + '|' + obj[0] + '|' + obj[1] + '|' + obj[2] + '|' + obj[3],
resultStr += '<br />' + obj[3]; showstartdate = '';
}
resultStr += '<p /><br />'; if (null !== obj[3]){
}); var startDate = new Date(obj[3]);
resultStr += '</ul>'; var today = new Date();
} showstartdate = '&nbsp;<span class="stepone-result-date">('
resultStr += '</fieldset>'; + (startDate > today ? 'will debut' : 'started')
$('#searchResults').html(resultStr); + ' on ' + obj[3] + ')</span>';
updateSampleText(); }
myform.loadsection(0);
});
}
$('#addShowButton').click(function () { resultStr += '<div' + rowType + '>'
// if they haven't picked a show don't let them submit + '<input id="whichSeries" type="radio"'
if (!$("input:radio[name='whichSeries']:checked").val() && !$("input:hidden[name='whichSeries']").val().length) { + ' class="stepone-result-radio"'
alert('You must choose a show to continue'); + ' style="float:left;margin-top:4px"'
return false; + ' title="Add show <span style=\'color: rgb(66, 139, 202)\'>' + obj[1] + '</span>"'
} + ' name="whichSeries"'
+ ' value="' + whichSeries + '"'
+ checked
+ ' />'
+ '<div style="margin-left:20px">'
+ '<a'
+ ' class="stepone-result-title"'
+ ' style="margin-left:5px"'
+ ' title="View <span class=\'boldest\'>Trakt</span> detail for <span style=\'color: rgb(66, 139, 202)\'>' + obj[1] + '</span>"'
+ ' href="' + anonURL + obj[0] + '"'
+ ' onclick="window.open(this.href, \'_blank\'); return false;"'
+ '>' + obj[1] + '</a>'
+ showstartdate
+ (null == obj[6] ? ''
: '&nbsp;'
+ '<span class="stepone-result-db grey-text">'
+ '<a class="service" href="' + anonURL + obj[7] + '"'
+ ' onclick="window.open(this.href, \'_blank\'); return false;"'
+ ' title="View <span class=\'boldest\'>' + obj[4] + '</span> detail for <span style=\'color: rgb(66, 139, 202)\'>' + obj[1] + '</span>"'
+ '>'
+ '<img alt="' + obj[4] + '" height="16" width="16" src="' + sbRoot + '/images/' + obj[5] + '" />'
+ ''
+ '</a>'
+ '</span>'
)
+ (null == obj[10] ? ''
: '&nbsp;'
+ '<span class="stepone-result-db grey-text">'
+ '<a class="service" href="' + anonURL + obj[11] + '"'
+ ' onclick="window.open(this.href, \'_blank\'); return false;"'
+ ' title="View <span class=\'boldest\'>' + obj[8] + '</span> detail for <span style=\'color: rgb(66, 139, 202)\'>' + obj[1] + '</span>"'
+ '>'
+ '<img alt="' + obj[8] + '" height="16" width="16" src="' + sbRoot + '/images/' + obj[9] + '" />'
+ ''
+ '</a>'
+ '</span>'
)
+ (null == obj[2] ? ''
: '&nbsp;<div class="stepone-result-overview grey-text">' + obj[2] + '</div>')
+ '</div></div>';
});
}
$('#recommendedShowsForm').submit(); $('#searchResults').html(
}); '<fieldset>' + "\n" + '<legend class="legendStep" style="margin-bottom: 15px">'
+ (0 < row ? row : 'No')
+ ' recommended result' + (1 == row ? '' : 's') + '...</legend>' + "\n"
+ resultStr
+ '</fieldset>'
);
updateSampleText();
myform.loadsection(0);
$('.stepone-result-radio, .stepone-result-title, .service').each(addQTip);
}
);
}
$('#qualityPreset').change(function () { $('#addShowButton').click(function () {
myform.loadsection(2); // if they haven't picked a show don't let them submit
}); if (!$('input:radio[name="whichSeries"]:checked').val()
&& !$('input:hidden[name="whichSeries"]').val().length) {
alert('You must choose a show to continue');
return false;
}
$('#addShowForm').submit();
});
var myform = new formtowizard({ $('#qualityPreset').change(function (){
formid: 'recommendedShowsForm', myform.loadsection(2);
revealfx: ['slide', 500], });
oninit: function () {
getRecommendedShows();
updateSampleText();
}
});
function goToStep(num) { var myform = new FormToWizard({
$('.step').each(function () { fieldsetborderwidth: 0,
if ($.data(this, 'section') + 1 == num) { formid: 'addShowForm',
$(this).click(); revealfx: ['slide', 500],
} oninit: function (){
}); getRecommendedShows();
} updateSampleText();
}
});
function updateSampleText() { function goToStep(num){
// if something's selected then we have some behavior to figure out $('.step').each(function (){
if ($.data(this, 'section') + 1 == num){
$(this).click();
}
});
}
var show_name, sep_char; function updateSampleText(){
// if they've picked a radio button then use that // if something's selected then we have some behavior to figure out
if ($('input:radio[name=whichSeries]:checked').length) {
show_name = $('input:radio[name=whichSeries]:checked').val().split('|')[2];
} else {
show_name = '';
}
var sample_text = 'Adding show <b>' + show_name + '</b> into <b>'; var elRadio = $('input:radio[name="whichSeries"]:checked'),
elFullShowPath = $('#fullShowPath'),
sep_char = '',
root_dirs = $('#rootDirs'),
// if they've picked a radio button then use that
show_name = (elRadio.length ? elRadio.val().split('|')[2] : ''),
sample_text = '<p>Adding show <span class="show-name">' + show_name + '</span>'
+ ('' == show_name ? 'into<br />' : '<br />into')
+ ' <span class="show-dest">';
// if we have a root dir selected, figure out the path // if we have a root dir selected, figure out the path
if ($("#rootDirs option:selected").length) { if (root_dirs.find('option:selected').length){
var root_dir_text = $('#rootDirs option:selected').val(); var root_dir_text = root_dirs.find('option:selected').val();
if (root_dir_text.indexOf('/') >= 0) { if (0 <= root_dir_text.indexOf('/')){
sep_char = '/'; sep_char = '/';
} else if (root_dir_text.indexOf('\\') >= 0) { } else if (0 <= root_dir_text.indexOf('\\')){
sep_char = '\\'; sep_char = '\\';
} else { }
sep_char = '';
}
if (root_dir_text.substr(sample_text.length - 1) != sep_char) { root_dir_text += (sep_char != root_dir_text.substr(sample_text.length - 1)
root_dir_text += sep_char; ? sep_char : '')
} + '<i>||</i>' + sep_char;
root_dir_text += '<i>||</i>' + sep_char;
sample_text += root_dir_text; sample_text += root_dir_text;
} else if ($('#fullShowPath').length && $('#fullShowPath').val().length) { } else if (elFullShowPath.length && elFullShowPath.val().length){
sample_text += $('#fullShowPath').val(); sample_text += elFullShowPath.val();
} else { } else {
sample_text += 'unknown dir.'; sample_text += 'unknown dir.';
} }
sample_text += '</b>'; sample_text += '</span></p>';
// if we have a show name then sanitize and use it for the dir name // if we have a show name then sanitize and use it for the dir name
if (show_name.length) { if (show_name.length){
$.get(sbRoot + '/home/addShows/sanitizeFileName', {name: show_name}, function (data) { $.get(sbRoot + '/home/addShows/sanitizeFileName', {name: show_name}, function (data){
$('#displayText').html(sample_text.replace('||', data)); $('#displayText').html(sample_text.replace('||', data));
}); });
// if not then it's unknown // if not then it's unknown
} else { } else {
$('#displayText').html(sample_text.replace('||', '??')); $('#displayText').html(sample_text.replace('||', '??'));
} }
// also toggle the add show button // also toggle the add show button
if (($("#rootDirs option:selected").length || ($('#fullShowPath').length && $('#fullShowPath').val().length)) && $('#addShowButton').attr('disabled',
($('input:radio[name=whichSeries]:checked').length)) { ((root_dirs.find('option:selected').length
$('#addShowButton').attr('disabled', false); || (elFullShowPath.length && elFullShowPath.val().length))
} else { && elRadio.length
$('#addShowButton').attr('disabled', true); ? false : true));
} }
}
$('#rootDirText').change(updateSampleText); var addQTip = (function(){
$('#whichSeries').live('change', updateSampleText); $(this).css('cursor', 'help');
$(this).qtip({
show: {
solo: true
},
position: {
viewport: $(window),
my: 'left center',
adjust: {
y: -10,
x: 2
}
},
style: {
tip: {
corner: true,
method: 'polygon'
},
classes: 'qtip-rounded qtip-bootstrap qtip-shadow ui-tooltip-sb'
}
});
});
$('#rootDirText').change(updateSampleText);
$('#searchResults').on('click', '.stepone-result-radio', updateSampleText);
}); });

View file

@ -1,17 +1,25 @@
$(function () { $(function () {
$('.title span').each(function () { $('.title span').each(function () {
var match = $(this).parent().attr('id').match(/^scene_exception_(\d+)$/);
$(this).qtip({ $(this).qtip({
content: { content: {
text: 'Loading...', text: function(event, api) {
ajax: { // deferred object ensuring the request is only made once
url: $("#sbRoot").val() + '/home/sceneExceptions', $.ajax({
type: 'GET', url: $('#sbRoot').val() + '/home/sceneExceptions',
data: { type: 'GET',
show: match[1] data: {
}, show: match[1]
success: function (data, status) { }
this.set('content.text', data); })
} .then(function(content) {
// Set the tooltip content upon successful retrieval
api.set('content.text', content);
}, function(xhr, status, error) {
// Upon failure... set the tooltip content to the status and error value
api.set('content.text', status + ': ' + error);
});
return 'Loading...'; // Set initial text
} }
}, },
show: { show: {
@ -19,19 +27,15 @@ $(function () {
}, },
position: { position: {
viewport: $(window), viewport: $(window),
my: 'left middle', my: 'left center',
at: 'right middle', at: 'right center',
adjust: { adjust: {
y: 0, y: 0,
x: 10 x: 2
} }
}, },
style: { style: {
tip: { classes: 'qtip-rounded qtip-shadow'
corner: true,
method: 'polygon'
},
classes: 'qtip-rounded qtip-shadow ui-tooltip-sb'
} }
}); });
}); });

282
setup.py
View file

@ -1,282 +0,0 @@
import re
import urllib
import ConfigParser
import sys
import os
import shutil
import zipfile
import subprocess
import fnmatch
import googlecode_upload
from distutils.core import setup
try:
import py2exe
except:
print "The Python module py2exe is required"
sys.exit(1)
try:
import pygithub.github
except:
print "The Python module pyGitHub is required"
sys.exit(1)
# mostly stolen from the SABnzbd package.py file
name = 'SickGear'
version = '0.1'
release = name + '-' + version
Win32ConsoleName = 'SickBeard-console.exe'
Win32WindowName = 'SickBeard.exe'
def findLatestBuild():
regex = "http\://SickGear\.googlecode\.com/files/SickGear\-win32\-alpha\-build(\d+)(?:\.\d+)?\.zip"
svnFile = urllib.urlopen("http://code.google.com/p/SickGear/downloads/list")
for curLine in svnFile.readlines():
match = re.search(regex, curLine)
if match:
groups = match.groups()
return int(groups[0])
return None
def recursive_find_data_files(root_dir, allowed_extensions=('*')):
to_return = {}
for (dirpath, dirnames, filenames) in os.walk(root_dir):
if not filenames:
continue
for cur_filename in filenames:
matches_pattern = False
for cur_pattern in allowed_extensions:
if fnmatch.fnmatch(cur_filename, '*.' + cur_pattern):
matches_pattern = True
if not matches_pattern:
continue
cur_filepath = os.path.join(dirpath, cur_filename)
to_return.setdefault(dirpath, []).append(cur_filepath)
return sorted(to_return.items())
def find_all_libraries(root_dirs):
libs = []
for cur_root_dir in root_dirs:
for (dirpath, dirnames, filenames) in os.walk(cur_root_dir):
if '__init__.py' not in filenames:
continue
libs.append(dirpath.replace(os.sep, '.'))
return libs
def allFiles(dir):
files = []
for file in os.listdir(dir):
fullFile = os.path.join(dir, file)
if os.path.isdir(fullFile):
files += allFiles(fullFile)
else:
files.append(fullFile)
return files
# save the original arguments and replace them with the py2exe args
oldArgs = []
if len(sys.argv) > 1:
oldArgs = sys.argv[1:]
del sys.argv[1:]
sys.argv.append('py2exe')
# clear the dist dir
if os.path.isdir('dist'):
shutil.rmtree('dist')
# root source dir
compile_dir = os.path.dirname(os.path.normpath(os.path.abspath(sys.argv[0])))
if not 'nopull' in oldArgs:
# pull new source from git
print 'Updating source from git'
p = subprocess.Popen('git pull origin master', shell=True, cwd=compile_dir)
o, e = p.communicate()
# figure out what build this is going to be
latestBuild = findLatestBuild()
if 'test' in oldArgs:
currentBuildNumber = str(latestBuild) + 'a'
else:
currentBuildNumber = latestBuild + 1
# set up the compilation options
data_files = recursive_find_data_files('data', ['gif', 'png', 'jpg', 'ico', 'js', 'css', 'tmpl'])
options = dict(
name=name,
version=release,
author='SickGear',
author_email='SickGear@outlook.com',
description=name + ' ' + release,
scripts=['SickBeard.py'],
packages=find_all_libraries(['sickbeard', 'lib']),
)
# set up py2exe to generate the console app
program = [{'script': 'SickBeard.py'}]
options['options'] = {'py2exe':
{
'bundle_files': 3,
'packages': ['Cheetah'],
'excludes': ['Tkconstants', 'Tkinter', 'tcl'],
'optimize': 2,
'compressed': 0
}
}
options['zipfile'] = 'lib/SickGear.zip'
options['console'] = program
options['data_files'] = data_files
# compile sickbeard-console.exe
setup(**options)
# rename the exe to sickbeard-console.exe
try:
if os.path.exists("dist/%s" % Win32ConsoleName):
os.remove("dist/%s" % Win32ConsoleName)
os.rename("dist/%s" % Win32WindowName, "dist/%s" % Win32ConsoleName)
except:
print "Cannot create dist/%s" % Win32ConsoleName
# sys.exit(1)
# we don't need this stuff when we make the 2nd exe
del options['console']
del options['data_files']
options['windows'] = program
# compile sickbeard.exe
setup(**options)
# compile sabToSickbeard.exe using the existing setup.py script
auto_process_dir = os.path.join(compile_dir, 'autoProcessTV')
p = subprocess.Popen([sys.executable, os.path.join(auto_process_dir, 'setup.py')], cwd=auto_process_dir, shell=True)
o, e = p.communicate()
# copy autoProcessTV files to the dist dir
auto_process_files = ['autoProcessTV/sabToSickBeard.py',
'autoProcessTV/hellaToSickBeard.py',
'autoProcessTV/autoProcessTV.py',
'autoProcessTV/autoProcessTV.cfg.sample',
'autoProcessTV/sabToSickBeard.exe']
os.makedirs('dist/autoProcessTV')
for curFile in auto_process_files:
newFile = os.path.join('dist', curFile)
print "Copying file from", curFile, "to", newFile
shutil.copy(curFile, newFile)
# compile updater.exe
setup(
options={'py2exe': {'bundle_files': 1}},
zipfile=None,
console=['updater.py'], requires=['Cheetah']
)
if 'test' in oldArgs:
print "Ignoring changelog for test build"
else:
# start building the CHANGELOG.txt
print 'Creating changelog'
gh = github.GitHub()
# read the old changelog and find the last commit from that build
lastCommit = ""
try:
cl = open("CHANGELOG.txt", "r")
lastCommit = cl.readlines()[0].strip()
cl.close()
except:
print "I guess there's no changelog"
newestCommit = ""
changeString = ""
# cycle through all the git commits and save their commit messages
for curCommit in gh.commits.forBranch('SickGear', 'SickGear'):
if curCommit.id == lastCommit:
break
if newestCommit == "":
newestCommit = curCommit.id
changeString += curCommit.message + "\n\n"
# if we didn't find any changes don't make a changelog file
if newestCommit != "":
newChangelog = open("CHANGELOG.txt", "w")
newChangelog.write(newestCommit + "\n\n")
newChangelog.write("Changelog for build " + str(currentBuildNumber) + "\n\n")
newChangelog.write(changeString)
newChangelog.close()
else:
print "No changes found, keeping old changelog"
# put the changelog in the compile dir
if os.path.exists("CHANGELOG.txt"):
shutil.copy('CHANGELOG.txt', 'dist/')
# figure out what we're going to call the zip file
print 'Zipping files...'
zipFilename = 'SickGear-win32-alpha-build' + str(currentBuildNumber)
if os.path.isfile(zipFilename + '.zip'):
zipNum = 2
while os.path.isfile(zipFilename + '.{0:0>2}.zip'.format(str(zipNum))):
zipNum += 1
zipFilename = zipFilename + '.{0:0>2}'.format(str(zipNum))
# get a list of files to add to the zip
zipFileList = allFiles('dist/')
# add all files to the zip
z = zipfile.ZipFile(zipFilename + '.zip', 'w', zipfile.ZIP_DEFLATED)
for file in zipFileList:
z.write(file, file.replace('dist/', zipFilename + '/'))
z.close()
print "Created zip at", zipFilename
# i store my google code username/pw in a config so i can have this file in public source control
config = ConfigParser.ConfigParser()
configFilename = os.path.join(compile_dir, "gc.ini")
config.read(configFilename)
gc_username = config.get("GC", "username")
gc_password = config.get("GC", "password")
# upload to google code unless I tell it not to
if "noup" not in oldArgs and "test" not in oldArgs:
print "Uploading zip to google code"
googlecode_upload.upload(os.path.abspath(zipFilename + ".zip"), "SickGear", gc_username, gc_password,
"Win32 alpha build " + str(currentBuildNumber) + " (unstable/development release)",
["Featured", "Type-Executable", "OpSys-Windows"])
if 'nopush' not in oldArgs and 'test' not in oldArgs:
# tag commit as a new build and push changes to github
print 'Tagging commit and pushing'
p = subprocess.Popen('git tag -a "build-' + str(currentBuildNumber) + '" -m "Windows build ' + zipFilename + '"',
shell=True, cwd=compile_dir)
o, e = p.communicate()
p = subprocess.Popen('git push --tags origin windows_binaries', shell=True, cwd=compile_dir)
o, e = p.communicate()

View file

@ -147,6 +147,7 @@ ROOT_DIRS = None
UPDATE_SHOWS_ON_START = False UPDATE_SHOWS_ON_START = False
TRASH_REMOVE_SHOW = False TRASH_REMOVE_SHOW = False
TRASH_ROTATE_LOGS = False TRASH_ROTATE_LOGS = False
HOME_SEARCH_FOCUS = True
SORT_ARTICLE = False SORT_ARTICLE = False
DEBUG = False DEBUG = False
@ -474,7 +475,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_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, \ 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, \ 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, SORT_ARTICLE, showList, loadingShowList, \ showUpdateScheduler, __INITIALIZED__, LAUNCH_BROWSER, UPDATE_SHOWS_ON_START, TRASH_REMOVE_SHOW, TRASH_ROTATE_LOGS, HOME_SEARCH_FOCUS, SORT_ARTICLE, showList, loadingShowList, \
NEWZNAB_DATA, NZBS, NZBS_UID, NZBS_HASH, INDEXER_DEFAULT, INDEXER_TIMEOUT, USENET_RETENTION, TORRENT_DIR, \ NEWZNAB_DATA, NZBS, NZBS_UID, NZBS_HASH, INDEXER_DEFAULT, INDEXER_TIMEOUT, USENET_RETENTION, TORRENT_DIR, \
QUALITY_DEFAULT, FLATTEN_FOLDERS_DEFAULT, SUBTITLES_DEFAULT, STATUS_DEFAULT, DAILYSEARCH_STARTUP, \ QUALITY_DEFAULT, FLATTEN_FOLDERS_DEFAULT, SUBTITLES_DEFAULT, STATUS_DEFAULT, DAILYSEARCH_STARTUP, \
GROWL_NOTIFY_ONSNATCH, GROWL_NOTIFY_ONDOWNLOAD, GROWL_NOTIFY_ONSUBTITLEDOWNLOAD, TWITTER_NOTIFY_ONSNATCH, TWITTER_NOTIFY_ONDOWNLOAD, TWITTER_NOTIFY_ONSUBTITLEDOWNLOAD, \ GROWL_NOTIFY_ONSNATCH, GROWL_NOTIFY_ONDOWNLOAD, GROWL_NOTIFY_ONSUBTITLEDOWNLOAD, TWITTER_NOTIFY_ONSNATCH, TWITTER_NOTIFY_ONDOWNLOAD, TWITTER_NOTIFY_ONSUBTITLEDOWNLOAD, \
@ -610,6 +611,7 @@ def initialize(consoleLogging=True):
TRASH_REMOVE_SHOW = bool(check_setting_int(CFG, 'General', 'trash_remove_show', 0)) 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)) TRASH_ROTATE_LOGS = bool(check_setting_int(CFG, 'General', 'trash_rotate_logs', 0))
HOME_SEARCH_FOCUS = bool(check_setting_int(CFG, 'General', 'home_search_focus', HOME_SEARCH_FOCUS))
SORT_ARTICLE = bool(check_setting_int(CFG, 'General', 'sort_article', 0)) SORT_ARTICLE = bool(check_setting_int(CFG, 'General', 'sort_article', 0))
USE_API = bool(check_setting_int(CFG, 'General', 'use_api', 0)) USE_API = bool(check_setting_int(CFG, 'General', 'use_api', 0))
@ -1067,7 +1069,7 @@ def initialize(consoleLogging=True):
# initialize the main SB database # initialize the main SB database
myDB = db.DBConnection() myDB = db.DBConnection()
db.upgradeDatabase(myDB, mainDB.InitialSchema) db.MigrationCode(myDB)
# initialize the cache database # initialize the cache database
myDB = db.DBConnection('cache.db') myDB = db.DBConnection('cache.db')
@ -1430,6 +1432,7 @@ def save_config():
new_config['General']['update_shows_on_start'] = int(UPDATE_SHOWS_ON_START) new_config['General']['update_shows_on_start'] = int(UPDATE_SHOWS_ON_START)
new_config['General']['trash_remove_show'] = int(TRASH_REMOVE_SHOW) new_config['General']['trash_remove_show'] = int(TRASH_REMOVE_SHOW)
new_config['General']['trash_rotate_logs'] = int(TRASH_ROTATE_LOGS) new_config['General']['trash_rotate_logs'] = int(TRASH_ROTATE_LOGS)
new_config['General']['home_search_focus'] = int(HOME_SEARCH_FOCUS)
new_config['General']['sort_article'] = int(SORT_ARTICLE) new_config['General']['sort_article'] = int(SORT_ARTICLE)
new_config['General']['proxy_setting'] = PROXY_SETTING new_config['General']['proxy_setting'] = PROXY_SETTING
new_config['General']['proxy_indexers'] = int(PROXY_INDEXERS) new_config['General']['proxy_indexers'] = int(PROXY_INDEXERS)

View file

@ -98,7 +98,7 @@ class BlackAndWhiteList(object):
def _add_keywords(self, table, range, values): def _add_keywords(self, table, range, values):
myDB = db.DBConnection() myDB = db.DBConnection()
for value in values: 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, range , keyword) VALUES (?,?,?)", [self.show_id, range, value])
self.refresh() self.refresh()
@ -117,18 +117,18 @@ class BlackAndWhiteList(object):
def _del_all_keywords(self, table): def _del_all_keywords(self, table):
logger.log(u"Deleting all " + table + " keywords for " + str(self.show_id), logger.DEBUG) logger.log(u"Deleting all " + table + " keywords for " + str(self.show_id), logger.DEBUG)
myDB = db.DBConnection() myDB = db.DBConnection()
myDB.action("DELETE FROM " + table + " WHERE show_id = ?", [self.show_id]) myDB.action("DELETE FROM [" + table + "] WHERE show_id = ?", [self.show_id])
self.refresh() self.refresh()
def _del_all_keywords_for(self, table, range): def _del_all_keywords_for(self, table, range):
logger.log(u"Deleting all " + range + " " + table + " keywords for " + str(self.show_id), logger.DEBUG) logger.log(u"Deleting all " + range + " " + table + " keywords for " + str(self.show_id), logger.DEBUG)
myDB = db.DBConnection() myDB = db.DBConnection()
myDB.action("DELETE FROM " + table + " WHERE show_id = ? and range = ?", [self.show_id, range]) myDB.action("DELETE FROM [" + table + "] WHERE show_id = ? and range = ?", [self.show_id, range])
self.refresh() self.refresh()
def _load_list(self, table): def _load_list(self, table):
myDB = db.DBConnection() myDB = db.DBConnection()
sqlResults = myDB.select("SELECT range,keyword FROM " + table + " WHERE show_id = ? ", [self.show_id]) sqlResults = myDB.select("SELECT range,keyword FROM [" + table + "] WHERE show_id = ? ", [self.show_id])
if not sqlResults or not len(sqlResults): if not sqlResults or not len(sqlResults):
return ([], {}) return ([], {})

File diff suppressed because it is too large Load diff

View file

@ -229,20 +229,20 @@ class DBConnection(object):
genParams = lambda myDict: [x + " = ?" for x in myDict.keys()] genParams = lambda myDict: [x + " = ?" for x in myDict.keys()]
query = "UPDATE " + tableName + " SET " + ", ".join(genParams(valueDict)) + " WHERE " + " AND ".join( query = "UPDATE [" + tableName + "] SET " + ", ".join(genParams(valueDict)) + " WHERE " + " AND ".join(
genParams(keyDict)) genParams(keyDict))
self.action(query, valueDict.values() + keyDict.values()) self.action(query, valueDict.values() + keyDict.values())
if self.connection.total_changes == changesBefore: if self.connection.total_changes == changesBefore:
query = "INSERT INTO " + tableName + " (" + ", ".join(valueDict.keys() + keyDict.keys()) + ")" + \ query = "INSERT INTO [" + tableName + "] (" + ", ".join(valueDict.keys() + keyDict.keys()) + ")" + \
" VALUES (" + ", ".join(["?"] * len(valueDict.keys() + keyDict.keys())) + ")" " VALUES (" + ", ".join(["?"] * len(valueDict.keys() + keyDict.keys())) + ")"
self.action(query, valueDict.values() + keyDict.values()) self.action(query, valueDict.values() + keyDict.values())
def tableInfo(self, tableName): def tableInfo(self, tableName):
# FIXME ? binding is not supported here, but I cannot find a way to escape a string manually # FIXME ? binding is not supported here, but I cannot find a way to escape a string manually
sqlResult = self.select("PRAGMA table_info(%s)" % tableName) sqlResult = self.select("PRAGMA table_info([%s])" % tableName)
columns = {} columns = {}
for column in sqlResult: for column in sqlResult:
columns[column['name']] = {'type': column['type']} columns[column['name']] = {'type': column['type']}
@ -261,9 +261,17 @@ class DBConnection(object):
def hasColumn(self, tableName, column): def hasColumn(self, tableName, column):
return column in self.tableInfo(tableName) return column in self.tableInfo(tableName)
def hasIndex(self, tableName, index):
sqlResults = self.select('PRAGMA index_list([%s])' % tableName)
for result in sqlResults:
if result['name'] == index:
return True
return False
def addColumn(self, table, column, type="NUMERIC", default=0): def addColumn(self, table, column, type="NUMERIC", default=0):
self.action("ALTER TABLE %s ADD %s %s" % (table, column, type)) self.action("ALTER TABLE [%s] ADD %s %s" % (table, column, type))
self.action("UPDATE %s SET %s = ?" % (table, column), (default,)) self.action("UPDATE [%s] SET %s = ?" % (table, column), (default,))
def close(self): def close(self):
"""Close database connection""" """Close database connection"""
@ -353,8 +361,71 @@ class SchemaUpgrade(object):
return column in self.connection.tableInfo(tableName) return column in self.connection.tableInfo(tableName)
def addColumn(self, table, column, type="NUMERIC", default=0): def addColumn(self, table, column, type="NUMERIC", default=0):
self.connection.action("ALTER TABLE %s ADD %s %s" % (table, column, type)) self.connection.action("ALTER TABLE [%s] ADD %s %s" % (table, column, type))
self.connection.action("UPDATE %s SET %s = ?" % (table, column), (default,)) self.connection.action("UPDATE [%s] SET %s = ?" % (table, column), (default,))
def dropColumn(self, table, column):
# get old table columns and store the ones we want to keep
result = self.connection.select('pragma table_info([%s])' % table)
keptColumns = [c for c in result if c['name'] != column]
keptColumnsNames = []
final = []
pk = []
# copy the old table schema, column by column
for column in keptColumns:
keptColumnsNames.append(column['name'])
cl = []
cl.append(column['name'])
cl.append(column['type'])
'''
To be implemented if ever required
if column['dflt_value']:
cl.append(str(column['dflt_value']))
if column['notnull']:
cl.append(column['notnull'])
'''
if int(column['pk']) != 0:
pk.append(column['name'])
b = ' '.join(cl)
final.append(b)
# join all the table column creation fields
final = ', '.join(final)
keptColumnsNames = ', '.join(keptColumnsNames)
# generate sql for the new table creation
if len(pk) == 0:
sql = 'CREATE TABLE [%s_new] (%s)' % (table, final)
else:
pk = ', '.join(pk)
sql = 'CREATE TABLE [%s_new] (%s, PRIMARY KEY(%s))' % (table, final, pk)
# create new temporary table and copy the old table data across, barring the removed column
self.connection.action(sql)
self.connection.action('INSERT INTO [%s_new] SELECT %s FROM [%s]' % (table, keptColumnsNames, table))
# copy the old indexes from the old table
result = self.connection.select('SELECT sql FROM sqlite_master WHERE tbl_name=? and type="index"', [table])
# remove the old table and rename the new table to take it's place
self.connection.action('DROP TABLE [%s]' % table)
self.connection.action('ALTER TABLE [%s_new] RENAME TO [%s]' % (table, table))
# write any indexes to the new table
if len(result) > 0:
for index in result:
self.connection.action(index['sql'])
# vacuum the db as we will have a lot of space to reclaim after dropping tables
self.connection.action("VACUUM")
def checkDBVersion(self): def checkDBVersion(self):
return self.connection.checkDBVersion() return self.connection.checkDBVersion()
@ -363,3 +434,82 @@ class SchemaUpgrade(object):
new_version = self.checkDBVersion() + 1 new_version = self.checkDBVersion() + 1
self.connection.action("UPDATE db_version SET db_version = ?", [new_version]) self.connection.action("UPDATE db_version SET db_version = ?", [new_version])
return new_version return new_version
def setDBVersion(self, new_version):
self.connection.action("UPDATE db_version SET db_version = ?", [new_version])
return new_version
def MigrationCode(myDB):
schema = {
0: sickbeard.mainDB.InitialSchema, # 0->20000
9: sickbeard.mainDB.AddSizeAndSceneNameFields,
10: sickbeard.mainDB.RenameSeasonFolders,
11: sickbeard.mainDB.Add1080pAndRawHDQualities,
12: sickbeard.mainDB.AddShowidTvdbidIndex,
13: sickbeard.mainDB.AddLastUpdateTVDB,
14: sickbeard.mainDB.AddDBIncreaseTo15,
15: sickbeard.mainDB.AddIMDbInfo,
16: sickbeard.mainDB.AddProperNamingSupport,
17: sickbeard.mainDB.AddEmailSubscriptionTable,
18: sickbeard.mainDB.AddProperSearch,
19: sickbeard.mainDB.AddDvdOrderOption,
20: sickbeard.mainDB.AddSubtitlesSupport,
21: sickbeard.mainDB.ConvertTVShowsToIndexerScheme,
22: sickbeard.mainDB.ConvertTVEpisodesToIndexerScheme,
23: sickbeard.mainDB.ConvertIMDBInfoToIndexerScheme,
24: sickbeard.mainDB.ConvertInfoToIndexerScheme,
25: sickbeard.mainDB.AddArchiveFirstMatchOption,
26: sickbeard.mainDB.AddSceneNumbering,
27: sickbeard.mainDB.ConvertIndexerToInteger,
28: sickbeard.mainDB.AddRequireAndIgnoreWords,
29: sickbeard.mainDB.AddSportsOption,
30: sickbeard.mainDB.AddSceneNumberingToTvEpisodes,
31: sickbeard.mainDB.AddAnimeTVShow,
32: sickbeard.mainDB.AddAbsoluteNumbering,
33: sickbeard.mainDB.AddSceneAbsoluteNumbering,
34: sickbeard.mainDB.AddAnimeBlacklistWhitelist,
35: sickbeard.mainDB.AddSceneAbsoluteNumbering2,
36: sickbeard.mainDB.AddXemRefresh,
37: sickbeard.mainDB.AddSceneToTvShows,
38: sickbeard.mainDB.AddIndexerMapping,
39: sickbeard.mainDB.AddVersionToTvEpisodes,
40: sickbeard.mainDB.BumpDatabaseVersion,
41: sickbeard.mainDB.Migrate41,
10000: sickbeard.mainDB.SickGearDatabaseVersion,
10001: sickbeard.mainDB.RemoveDefaultEpStatusFromTvShows
#20000: sickbeard.mainDB.AddCoolSickGearFeature1,
#20001: sickbeard.mainDB.AddCoolSickGearFeature2,
#20002: sickbeard.mainDB.AddCoolSickGearFeature3,
}
db_version = myDB.checkDBVersion()
logger.log(u'Detected database version: v' + str(db_version), logger.DEBUG)
if not (db_version in schema):
if db_version == sickbeard.mainDB.MAX_DB_VERSION:
logger.log(u'Database schema is up-to-date, no upgrade required')
elif db_version < 10000:
logger.log_error_and_exit(u'SickGear does not currently support upgrading from this database version')
else:
logger.log_error_and_exit(u'Invalid database version')
else:
while db_version < sickbeard.mainDB.MAX_DB_VERSION:
try:
update = schema[db_version](myDB)
db_version = update.execute()
except Exception, e:
myDB.close()
logger.log(u'Failed to update database with error: ' + ex(e) + ' attempting recovery...', logger.ERROR)
if restoreDatabase(db_version):
# initialize the main SB database
logger.log_error_and_exit(u'Successfully restored database version:' + str(db_version))
else:
logger.log_error_and_exit(u'Failed to restore database version:' + str(db_version))

View file

@ -1430,4 +1430,75 @@ def get_size(start_path='.'):
def remove_article(text=''): def remove_article(text=''):
return re.sub(r'(?i)/^(?:(?:A(?!\s+to)n?)|The)\s(\w)', r'\1', text) return re.sub(r'(?i)^(?:(?:A(?!\s+to)n?)|The)\s(\w)', r'\1', text)
def client_host(server_host):
'''Extracted from cherrypy libs
Return the host on which a client can connect to the given listener.'''
if server_host == '0.0.0.0':
# 0.0.0.0 is INADDR_ANY, which should answer on localhost.
return '127.0.0.1'
if server_host in ('::', '::0', '::0.0.0.0'):
# :: is IN6ADDR_ANY, which should answer on localhost.
# ::0 and ::0.0.0.0 are non-canonical but common ways to write
# IN6ADDR_ANY.
return '::1'
return server_host
def wait_for_free_port(host, port):
'''Extracted from cherrypy libs
Wait for the specified port to become free (drop requests).'''
if not host:
raise ValueError("Host values of '' or None are not allowed.")
for trial in range(50):
try:
# we are expecting a free port, so reduce the timeout
check_port(host, port, timeout=0.1)
except IOError:
# Give the old server thread time to free the port.
time.sleep(0.1)
else:
return
raise IOError("Port %r not free on %r" % (port, host))
def check_port(host, port, timeout=1.0):
'''Extracted from cherrypy libs
Raise an error if the given port is not free on the given host.'''
if not host:
raise ValueError("Host values of '' or None are not allowed.")
host = client_host(host)
port = int(port)
import socket
# AF_INET or AF_INET6 socket
# Get the correct address family for our host (allows IPv6 addresses)
try:
info = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
socket.SOCK_STREAM)
except socket.gaierror:
if ':' in host:
info = [(socket.AF_INET6, socket.SOCK_STREAM, 0, "", (host, port, 0, 0))]
else:
info = [(socket.AF_INET, socket.SOCK_STREAM, 0, "", (host, port))]
for res in info:
af, socktype, proto, canonname, sa = res
s = None
try:
s = socket.socket(af, socktype, proto)
# See http://groups.google.com/group/cherrypy-users/
# browse_frm/thread/bbfe5eb39c904fe0
s.settimeout(timeout)
s.connect((host, port))
s.close()
raise IOError("Port %s is in use on %s; perhaps the previous "
"httpserver did not shut down properly." %
(repr(port), repr(host)))
except socket.error:
if s:
s.close()

View file

@ -27,33 +27,113 @@ from sickbeard import common
from sickbeard.exceptions import ex from sickbeard.exceptions import ex
from sickbeard.encodingKludge import fixStupidEncodings from sickbeard.encodingKludge import fixStupidEncodings
from sickbeard.notifiers.xbmc import XBMCNotifier try:
import xml.etree.cElementTree as etree
# TODO: switch over to using ElementTree except ImportError:
from xml.dom import minidom import elementtree.ElementTree as etree
class PLEXNotifier(XBMCNotifier): class PLEXNotifier:
def _notify_pmc(self, message, title="SickGear", host=None, username=None, password=None, force=False):
def _send_to_plex(self, command, host, username=None, password=None):
"""Handles communication to Plex hosts via HTTP API
Args:
command: Dictionary of field/data pairs, encoded via urllib and passed to the legacy xbmcCmds HTTP API
host: Plex host:port
username: Plex API username
password: Plex API password
Returns:
Returns 'OK' for successful commands or False if there was an error
"""
# fill in omitted parameters # fill in omitted parameters
if not host:
if sickbeard.PLEX_HOST:
host = sickbeard.PLEX_HOST # Use the default Plex host
else:
logger.log(u"No Plex host specified, check your settings", logger.DEBUG)
return False
if not username: if not username:
username = sickbeard.PLEX_USERNAME username = sickbeard.PLEX_USERNAME
if not password: if not password:
password = sickbeard.PLEX_PASSWORD password = sickbeard.PLEX_PASSWORD
# suppress notifications if the notifier is disabled but the notify options are checked if not host:
if not sickbeard.USE_PLEX and not force: logger.log(u"PLEX: No host specified, check your settings", logger.ERROR)
logger.log("Notification for Plex not enabled, skipping this notification", logger.DEBUG)
return False return False
return self._notify_xbmc(message=message, title=title, host=host, username=username, password=password, for key in command:
force=True) if type(command[key]) == unicode:
command[key] = command[key].encode('utf-8')
enc_command = urllib.urlencode(command)
logger.log(u"PLEX: Encoded API command: " + enc_command, logger.DEBUG)
url = 'http://%s/xbmcCmds/xbmcHttp/?%s' % (host, enc_command)
try:
req = urllib2.Request(url)
# 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)
else:
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)
# 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)
return False
def _notify(self, message, title="SickGear", host=None, username=None, password=None, force=False):
"""Internal wrapper for the notify_snatch and notify_download functions
Args:
message: Message body of the notice to send
title: Title of the notice to send
host: Plex Media Client(s) host:port
username: Plex username
password: Plex password
force: Used for the Test method to override config safety checks
Returns:
Returns a list results in the format of host:ip:result
The result will either be 'OK' or False, this is used to be parsed by the calling function.
"""
# suppress notifications if the notifier is disabled but the notify options are checked
if not sickbeard.USE_PLEX and not force:
return False
# fill in omitted parameters
if not host:
host = sickbeard.PLEX_HOST
if not username:
username = sickbeard.PLEX_USERNAME
if not password:
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)
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)
return result
##############################################################################
# Public functions
##############################################################################
def notify_snatch(self, ep_name): def notify_snatch(self, ep_name):
if sickbeard.PLEX_NOTIFY_ONSNATCH: if sickbeard.PLEX_NOTIFY_ONSNATCH:
@ -74,10 +154,9 @@ class PLEXNotifier(XBMCNotifier):
self._notify_pmc(update_text + new_version, title) self._notify_pmc(update_text + new_version, title)
def test_notify(self, host, username, password): def test_notify(self, host, username, password):
return self._notify_pmc("Testing Plex notifications from SickGear", "Test Notification", host, username, return self._notify("This is a test notification from SickGear", "Test", host, username, password, force=True)
password, force=True)
def update_library(self): def update_library(self, ep_obj=None, host=None, username=None, password=None):
"""Handles updating the Plex Media Server host via HTTP API """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. Plex Media Server currently only supports updating the whole video library and not a specific path.
@ -87,36 +166,68 @@ class PLEXNotifier(XBMCNotifier):
""" """
# 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 sickbeard.USE_PLEX and sickbeard.PLEX_UPDATE_LIBRARY:
if not sickbeard.PLEX_SERVER_HOST: if not sickbeard.PLEX_SERVER_HOST:
logger.log(u"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 return False
logger.log(u"Updating library for the Plex Media Server host: " + sickbeard.PLEX_SERVER_HOST, logger.log(u"PLEX: Updating library for the Plex Media Server host: " + host, logger.MESSAGE)
logger.MESSAGE)
url = "http://%s/library/sections" % sickbeard.PLEX_SERVER_HOST # if username and password were provided, fetch the auth token from plex.tv
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")
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
except (ValueError, IndexError) as e:
logger.log(u"PLEX: Error parsing plex.tv response: " + ex(e), logger.MESSAGE)
return False
url = "http://%s/library/sections%s" % (sickbeard.PLEX_SERVER_HOST, token_arg)
try: try:
xml_sections = minidom.parse(urllib.urlopen(url)) xml_tree = etree.parse(urllib.urlopen(url))
media_container = xml_tree.getroot()
except IOError, e: except IOError, e:
logger.log(u"Error while trying to contact Plex Media Server: " + ex(e), logger.ERROR) logger.log(u"PLEX: Error while trying to contact Plex Media Server: " + ex(e), logger.ERROR)
return False return False
sections = xml_sections.getElementsByTagName('Directory') sections = media_container.findall('.//Directory')
if not sections: if not sections:
logger.log(u"Plex Media Server not running on: " + sickbeard.PLEX_SERVER_HOST, logger.MESSAGE) logger.log(u"PLEX: Plex Media Server not running on: " + sickbeard.PLEX_SERVER_HOST, logger.MESSAGE)
return False return False
for s in sections: for section in sections:
if s.getAttribute('type') == "show": if section.attrib['type'] == "show":
url = "http://%s/library/sections/%s/refresh" % (sickbeard.PLEX_SERVER_HOST, s.getAttribute('key')) url = "http://%s/library/sections/%s/refresh%s" % (sickbeard.PLEX_SERVER_HOST, section.attrib['key'], token_arg)
try: try:
urllib.urlopen(url) urllib.urlopen(url)
except Exception, e: except Exception, e:
logger.log(u"Error updating library section for Plex Media Server: " + ex(e), logger.ERROR) logger.log(u"PLEX: Error updating library section for Plex Media Server: " + ex(e), logger.ERROR)
return False return False
return True return True
notifier = PLEXNotifier notifier = PLEXNotifier

View file

@ -61,8 +61,8 @@ class KATProvider(generic.TorrentProvider):
self.cache = KATCache(self) self.cache = KATCache(self)
self.urls = ['http://kickass.to/', 'http://katproxy.com/', 'http://www.kickmirror.com/'] self.urls = ['http://kickass.so/', 'http://katproxy.com/', 'http://www.kickmirror.com/']
self.url = 'https://kickass.to/' self.url = 'https://kickass.so/'
def isEnabled(self): def isEnabled(self):
return self.enabled return self.enabled

View file

@ -35,10 +35,10 @@ from sickbeard.helpers import sanitizeSceneName
class TorrentDayProvider(generic.TorrentProvider): class TorrentDayProvider(generic.TorrentProvider):
urls = {'base_url': 'http://www.torrentday.com', urls = {'base_url': 'https://torrentday.eu',
'login': 'http://www.torrentday.com/torrents/', 'login': 'https://torrentday.eu/torrents/',
'search': 'http://www.torrentday.com/V3/API/API.php', 'search': 'https://torrentday.eu/V3/API/API.php',
'download': 'http://www.torrentday.com/download.php/%s/%s' 'download': 'https://torrentday.eu/download.php/%s/%s'
} }
def __init__(self): def __init__(self):

View file

@ -59,20 +59,20 @@ def filterBadReleases(name, parse=True):
# if any of the bad strings are in the name then say no # if any of the bad strings are in the name then say no
if sickbeard.IGNORE_WORDS: if sickbeard.IGNORE_WORDS:
resultFilters.extend(sickbeard.IGNORE_WORDS.split(',')) resultFilters.extend(sickbeard.IGNORE_WORDS.split(','))
filters = [re.compile('(^|[\W_])%s($|[\W_])' % filter.strip(), re.I) for filter in resultFilters] filters = [re.compile('(^|[\W_])%s($|[\W_])' % re.escape(filter.strip()), re.I) for filter in resultFilters]
for regfilter in filters: for regfilter in filters:
if regfilter.search(name): if regfilter.search(name):
logger.log(u"Invalid scene release: " + name + " contains pattern: " + regfilter.pattern + ", ignoring it", logger.log(u"Invalid scene release: " + name + " contained: " + regfilter.pattern + ", ignoring it",
logger.DEBUG) logger.DEBUG)
return False return False
# if any of the good strings aren't in the name then say no # if any of the good strings aren't in the name then say no
if sickbeard.REQUIRE_WORDS: if sickbeard.REQUIRE_WORDS:
require_words = sickbeard.REQUIRE_WORDS.split(',') require_words = sickbeard.REQUIRE_WORDS.split(',')
filters = [re.compile('(^|[\W_])%s($|[\W_])' % filter.strip(), re.I) for filter in require_words] filters = [re.compile('(^|[\W_])%s($|[\W_])' % re.escape(filter.strip()), re.I) for filter in require_words]
for regfilter in filters: for regfilter in filters:
if not regfilter.search(name): if not regfilter.search(name):
logger.log(u"Invalid scene release: " + name + " doesn't contain pattern: " + regfilter.pattern + ", ignoring it", logger.log(u"Invalid scene release: " + name + " didn't contain: " + regfilter.pattern + ", ignoring it",
logger.DEBUG) logger.DEBUG)
return False return False

View file

@ -36,41 +36,44 @@ import itertools
class CacheDBConnection(db.DBConnection): class CacheDBConnection(db.DBConnection):
def __init__(self, providerName): def __init__(self, providerName):
db.DBConnection.__init__(self, "cache.db") db.DBConnection.__init__(self, 'cache.db')
# Create the table if it's not already there # Create the table if it's not already there
try: try:
if not self.hasTable(providerName): if not self.hasTable(providerName):
self.action( self.action(
"CREATE TABLE [" + providerName + "] (name TEXT, season NUMERIC, episodes TEXT, indexerid NUMERIC, url TEXT, time NUMERIC, quality TEXT, release_group TEXT)") 'CREATE TABLE [' + providerName + '] (name TEXT, season NUMERIC, episodes TEXT, indexerid NUMERIC, url TEXT, time NUMERIC, quality TEXT, release_group TEXT)')
else: self.action(
'CREATE UNIQUE INDEX IF NOT EXISTS [idx_' + providerName + '_url] ON [' + providerName + '] (url)')
elif not self.hasIndex(providerName, 'idx_%s_url' % providerName):
sqlResults = self.select( sqlResults = self.select(
"SELECT url, COUNT(url) as count FROM [" + providerName + "] GROUP BY url HAVING count > 1") 'SELECT url, COUNT(url) as count FROM [' + providerName + '] GROUP BY url HAVING count > 1')
for cur_dupe in sqlResults: for cur_dupe in sqlResults:
self.action("DELETE FROM [" + providerName + "] WHERE url = ?", [cur_dupe["url"]]) self.action('DELETE FROM [' + providerName + '] WHERE url = ?', [cur_dupe['url']])
self.action(
'CREATE UNIQUE INDEX IF NOT EXISTS [idx_' + providerName + '_url] ON [' + providerName + '] (url)')
# add unique index to prevent further dupes from happening if one does not exist
self.action("CREATE UNIQUE INDEX IF NOT EXISTS idx_url ON [" + providerName + "] (url)")
# add release_group column to table if missing # add release_group column to table if missing
if not self.hasColumn(providerName, 'release_group'): if not self.hasColumn(providerName, 'release_group'):
self.addColumn(providerName, 'release_group', "TEXT", "") self.addColumn(providerName, 'release_group', 'TEXT', '')
# add version column to table if missing # add version column to table if missing
if not self.hasColumn(providerName, 'version'): if not self.hasColumn(providerName, 'version'):
self.addColumn(providerName, 'version', "NUMERIC", "-1") self.addColumn(providerName, 'version', 'NUMERIC', '-1')
except Exception, e: except Exception, e:
if str(e) != "table [" + providerName + "] already exists": if str(e) != 'table [' + providerName + '] already exists':
raise raise
# Create the table if it's not already there # Create the table if it's not already there
try: try:
if not self.hasTable('lastUpdate'): if not self.hasTable('lastUpdate'):
self.action("CREATE TABLE lastUpdate (provider TEXT, time NUMERIC)") self.action('CREATE TABLE lastUpdate (provider TEXT, time NUMERIC)')
except Exception, e: except Exception, e:
if str(e) != "table lastUpdate already exists": if str(e) != 'table lastUpdate already exists':
raise raise
class TVCache(): class TVCache():
@ -91,7 +94,7 @@ class TVCache():
def _clearCache(self): def _clearCache(self):
if self.shouldClearCache(): if self.shouldClearCache():
myDB = self._getDB() myDB = self._getDB()
myDB.action("DELETE FROM [" + self.providerID + "] WHERE 1") myDB.action('DELETE FROM [' + self.providerID + '] WHERE 1')
def _get_title_and_url(self, item): def _get_title_and_url(self, item):
# override this in the provider if daily search has a different data layout to backlog searches # override this in the provider if daily search has a different data layout to backlog searches
@ -151,22 +154,22 @@ class TVCache():
title = self._translateTitle(title) title = self._translateTitle(title)
url = self._translateLinkURL(url) url = self._translateLinkURL(url)
logger.log(u"Attempting to add item to cache: " + title, logger.DEBUG) logger.log(u'Attempting to add item to cache: ' + title, logger.DEBUG)
return self._addCacheEntry(title, url) return self._addCacheEntry(title, url)
else: else:
logger.log( logger.log(
u"The data returned from the " + self.provider.name + " feed is incomplete, this result is unusable", u'The data returned from the ' + self.provider.name + ' feed is incomplete, this result is unusable',
logger.DEBUG) logger.DEBUG)
return None return None
def _getLastUpdate(self): def _getLastUpdate(self):
myDB = self._getDB() myDB = self._getDB()
sqlResults = myDB.select("SELECT time FROM lastUpdate WHERE provider = ?", [self.providerID]) sqlResults = myDB.select('SELECT time FROM lastUpdate WHERE provider = ?', [self.providerID])
if sqlResults: if sqlResults:
lastTime = int(sqlResults[0]["time"]) lastTime = int(sqlResults[0]['time'])
if lastTime > int(time.mktime(datetime.datetime.today().timetuple())): if lastTime > int(time.mktime(datetime.datetime.today().timetuple())):
lastTime = 0 lastTime = 0
else: else:
@ -176,10 +179,10 @@ class TVCache():
def _getLastSearch(self): def _getLastSearch(self):
myDB = self._getDB() myDB = self._getDB()
sqlResults = myDB.select("SELECT time FROM lastSearch WHERE provider = ?", [self.providerID]) sqlResults = myDB.select('SELECT time FROM lastSearch WHERE provider = ?', [self.providerID])
if sqlResults: if sqlResults:
lastTime = int(sqlResults[0]["time"]) lastTime = int(sqlResults[0]['time'])
if lastTime > int(time.mktime(datetime.datetime.today().timetuple())): if lastTime > int(time.mktime(datetime.datetime.today().timetuple())):
lastTime = 0 lastTime = 0
else: else:
@ -193,7 +196,7 @@ class TVCache():
toDate = datetime.datetime.today() toDate = datetime.datetime.today()
myDB = self._getDB() myDB = self._getDB()
myDB.upsert("lastUpdate", myDB.upsert('lastUpdate',
{'time': int(time.mktime(toDate.timetuple()))}, {'time': int(time.mktime(toDate.timetuple()))},
{'provider': self.providerID}) {'provider': self.providerID})
@ -202,7 +205,7 @@ class TVCache():
toDate = datetime.datetime.today() toDate = datetime.datetime.today()
myDB = self._getDB() myDB = self._getDB()
myDB.upsert("lastSearch", myDB.upsert('lastSearch',
{'time': int(time.mktime(toDate.timetuple()))}, {'time': int(time.mktime(toDate.timetuple()))},
{'provider': self.providerID}) {'provider': self.providerID})
@ -212,7 +215,7 @@ class TVCache():
def shouldUpdate(self): def shouldUpdate(self):
# if we've updated recently then skip the update # if we've updated recently then skip the update
if datetime.datetime.today() - self.lastUpdate < datetime.timedelta(minutes=self.minTime): if datetime.datetime.today() - self.lastUpdate < datetime.timedelta(minutes=self.minTime):
logger.log(u"Last update was too soon, using old cache: today()-" + str(self.lastUpdate) + "<" + str( logger.log(u'Last update was too soon, using old cache: today()-' + str(self.lastUpdate) + '<' + str(
datetime.timedelta(minutes=self.minTime)), logger.DEBUG) datetime.timedelta(minutes=self.minTime)), logger.DEBUG)
return False return False
@ -239,10 +242,10 @@ class TVCache():
myParser = NameParser(showObj=showObj, convert=True) myParser = NameParser(showObj=showObj, convert=True)
parse_result = myParser.parse(name) parse_result = myParser.parse(name)
except InvalidNameException: except InvalidNameException:
logger.log(u"Unable to parse the filename " + name + " into a valid episode", logger.DEBUG) logger.log(u'Unable to parse the filename ' + name + ' into a valid episode', logger.DEBUG)
return None return None
except InvalidShowException: except InvalidShowException:
logger.log(u"Unable to parse the filename " + name + " into a valid show", logger.DEBUG) logger.log(u'Unable to parse the filename ' + name + ' into a valid show', logger.DEBUG)
return None return None
if not parse_result or not parse_result.series_name: if not parse_result or not parse_result.series_name:
@ -254,7 +257,7 @@ class TVCache():
if season and episodes: if season and episodes:
# store episodes as a seperated string # store episodes as a seperated string
episodeText = "|" + "|".join(map(str, episodes)) + "|" episodeText = '|' + '|'.join(map(str, episodes)) + '|'
# get the current timestamp # get the current timestamp
curTimestamp = int(time.mktime(datetime.datetime.today().timetuple())) curTimestamp = int(time.mktime(datetime.datetime.today().timetuple()))
@ -271,10 +274,10 @@ class TVCache():
# get version # get version
version = parse_result.version version = parse_result.version
logger.log(u"Added RSS item: [" + name + "] to cache: [" + self.providerID + "]", logger.DEBUG) logger.log(u'Added RSS item: [' + name + '] to cache: [' + self.providerID + ']', logger.DEBUG)
return [ return [
"INSERT OR IGNORE INTO [" + self.providerID + "] (name, season, episodes, indexerid, url, time, quality, release_group, version) VALUES (?,?,?,?,?,?,?,?,?)", 'INSERT OR IGNORE INTO [' + self.providerID + '] (name, season, episodes, indexerid, url, time, quality, release_group, version) VALUES (?,?,?,?,?,?,?,?,?)',
[name, season, episodeText, parse_result.show.indexerid, url, curTimestamp, quality, release_group, version]] [name, season, episodeText, parse_result.show.indexerid, url, curTimestamp, quality, release_group, version]]
@ -285,12 +288,12 @@ class TVCache():
else: else:
return [] return []
def listPropers(self, date=None, delimiter="."): def listPropers(self, date=None, delimiter='.'):
myDB = self._getDB() myDB = self._getDB()
sql = "SELECT * FROM [" + self.providerID + "] WHERE name LIKE '%.PROPER.%' OR name LIKE '%.REPACK.%'" sql = "SELECT * FROM [" + self.providerID + "] WHERE name LIKE '%.PROPER.%' OR name LIKE '%.REPACK.%'"
if date != None: if date != None:
sql += " AND time >= " + str(int(time.mktime(date.timetuple()))) sql += ' AND time >= ' + str(int(time.mktime(date.timetuple())))
return filter(lambda x: x['indexerid'] != 0, myDB.select(sql)) return filter(lambda x: x['indexerid'] != 0, myDB.select(sql))
@ -302,14 +305,14 @@ class TVCache():
myDB = self._getDB() myDB = self._getDB()
if type(episode) != list: if type(episode) != list:
sqlResults = myDB.select( sqlResults = myDB.select(
"SELECT * FROM [" + self.providerID + "] WHERE indexerid = ? AND season = ? AND episodes LIKE ?", 'SELECT * FROM [' + self.providerID + '] WHERE indexerid = ? AND season = ? AND episodes LIKE ?',
[episode.show.indexerid, episode.season, "%|" + str(episode.episode) + "|%"]) [episode.show.indexerid, episode.season, '%|' + str(episode.episode) + '|%'])
else: else:
for epObj in episode: for epObj in episode:
cl.append([ cl.append([
"SELECT * FROM [" + self.providerID + "] WHERE indexerid = ? AND season = ? AND episodes LIKE ? " 'SELECT * FROM [' + self.providerID + '] WHERE indexerid = ? AND season = ? AND episodes LIKE ? '
"AND quality IN (" + ",".join([str(x) for x in epObj.wantedQuality]) + ")", 'AND quality IN (' + ','.join([str(x) for x in epObj.wantedQuality]) + ')',
[epObj.show.indexerid, epObj.season, "%|" + str(epObj.episode) + "|%"]]) [epObj.show.indexerid, epObj.season, '%|' + str(epObj.episode) + '|%']])
sqlResults = myDB.mass_action(cl, fetchall=True) sqlResults = myDB.mass_action(cl, fetchall=True)
sqlResults = list(itertools.chain(*sqlResults)) sqlResults = list(itertools.chain(*sqlResults))
@ -318,45 +321,45 @@ class TVCache():
for curResult in sqlResults: for curResult in sqlResults:
# skip non-tv crap # skip non-tv crap
if not show_name_helpers.filterBadReleases(curResult["name"], parse=False): if not show_name_helpers.filterBadReleases(curResult['name'], parse=False):
continue continue
# get the show object, or if it's not one of our shows then ignore it # get the show object, or if it's not one of our shows then ignore it
showObj = helpers.findCertainShow(sickbeard.showList, int(curResult["indexerid"])) showObj = helpers.findCertainShow(sickbeard.showList, int(curResult['indexerid']))
if not showObj: if not showObj:
continue continue
# skip if provider is anime only and show is not anime # skip if provider is anime only and show is not anime
if self.provider.anime_only and not showObj.is_anime: if self.provider.anime_only and not showObj.is_anime:
logger.log(u"" + str(showObj.name) + " is not an anime, skiping", logger.DEBUG) logger.log(u'' + str(showObj.name) + ' is not an anime, skiping', logger.DEBUG)
continue continue
# get season and ep data (ignoring multi-eps for now) # get season and ep data (ignoring multi-eps for now)
curSeason = int(curResult["season"]) curSeason = int(curResult['season'])
if curSeason == -1: if curSeason == -1:
continue continue
curEp = curResult["episodes"].split("|")[1] curEp = curResult['episodes'].split('|')[1]
if not curEp: if not curEp:
continue continue
curEp = int(curEp) curEp = int(curEp)
curQuality = int(curResult["quality"]) curQuality = int(curResult['quality'])
curReleaseGroup = curResult["release_group"] curReleaseGroup = curResult['release_group']
curVersion = curResult["version"] curVersion = curResult['version']
# if the show says we want that episode then add it to the list # if the show says we want that episode then add it to the list
if not showObj.wantEpisode(curSeason, curEp, curQuality, manualSearch): if not showObj.wantEpisode(curSeason, curEp, curQuality, manualSearch):
logger.log(u"Skipping " + curResult["name"] + " because we don't want an episode that's " + logger.log(u'Skipping ' + curResult['name'] + ' because we don\'t want an episode that\'s ' +
Quality.qualityStrings[curQuality], logger.DEBUG) Quality.qualityStrings[curQuality], logger.DEBUG)
continue continue
epObj = showObj.getEpisode(curSeason, curEp) epObj = showObj.getEpisode(curSeason, curEp)
# build a result object # build a result object
title = curResult["name"] title = curResult['name']
url = curResult["url"] url = curResult['url']
logger.log(u"Found result " + title + " at " + url) logger.log(u'Found result ' + title + ' at ' + url)
result = self.provider.getResult([epObj]) result = self.provider.getResult([epObj])
result.show = showObj result.show = showObj

View file

@ -61,6 +61,7 @@ from sickbeard.scene_numbering import get_scene_numbering, set_scene_numbering,
from sickbeard.blackandwhitelist import BlackAndWhiteList from sickbeard.blackandwhitelist import BlackAndWhiteList
from browser import WebFileBrowser from browser import WebFileBrowser
from mimetypes import MimeTypes
from lib.dateutil import tz from lib.dateutil import tz
from lib.unrar2 import RarFile from lib.unrar2 import RarFile
@ -234,7 +235,9 @@ class MainHandler(RequestHandler):
func = getattr(klass, 'index', None) func = getattr(klass, 'index', None)
if callable(func): if callable(func):
return func(**args) out = func(**args)
self._headers = klass._headers
return out
raise HTTPError(404) raise HTTPError(404)
@ -264,7 +267,7 @@ class MainHandler(RequestHandler):
else: else:
default_image_name = 'banner.png' default_image_name = 'banner.png'
default_image_path = ek.ek(os.path.join, sickbeard.PROG_DIR, 'gui', 'slick', 'images', default_image_name) image_path = ek.ek(os.path.join, sickbeard.PROG_DIR, 'gui', 'slick', 'images', default_image_name)
if show and sickbeard.helpers.findCertainShow(sickbeard.showList, int(show)): if show and sickbeard.helpers.findCertainShow(sickbeard.showList, int(show)):
cache_obj = image_cache.ImageCache() cache_obj = image_cache.ImageCache()
@ -279,10 +282,11 @@ class MainHandler(RequestHandler):
image_file_name = cache_obj.banner_thumb_path(show) image_file_name = cache_obj.banner_thumb_path(show)
if ek.ek(os.path.isfile, image_file_name): if ek.ek(os.path.isfile, image_file_name):
with file(image_file_name, 'rb') as img: image_path = image_file_name
return img.read()
with file(default_image_path, 'rb') as img: mime_type, encoding = MimeTypes().guess_type(image_path)
self.set_header('Content-Type', mime_type)
with file(image_path, 'rb') as img:
return img.read() return img.read()
def setHomeLayout(self, layout): def setHomeLayout(self, layout):
@ -425,25 +429,6 @@ class MainHandler(RequestHandler):
sql_results.sort(sorts[sickbeard.COMING_EPS_SORT]) sql_results.sort(sorts[sickbeard.COMING_EPS_SORT])
t = PageTemplate(headers=self.request.headers, file="comingEpisodes.tmpl") t = PageTemplate(headers=self.request.headers, file="comingEpisodes.tmpl")
# paused_item = { 'title': '', 'path': 'toggleComingEpsDisplayPaused' }
# paused_item['title'] = 'Hide Paused' if sickbeard.COMING_EPS_DISPLAY_PAUSED else 'Show Paused'
paused_item = {'title': 'View Paused:', 'path': {'': ''}}
paused_item['path'] = {'Hide': 'toggleComingEpsDisplayPaused'} if sickbeard.COMING_EPS_DISPLAY_PAUSED else {
'Show': 'toggleComingEpsDisplayPaused'}
t.submenu = [
{'title': 'Sort by:', 'path': {'Date': 'setComingEpsSort/?sort=date',
'Show': 'setComingEpsSort/?sort=show',
'Network': 'setComingEpsSort/?sort=network',
}},
{'title': 'Layout:', 'path': {'Banner': 'setComingEpsLayout/?layout=banner',
'Poster': 'setComingEpsLayout/?layout=poster',
'List': 'setComingEpsLayout/?layout=list',
'Calendar': 'setComingEpsLayout/?layout=calendar',
}},
paused_item,
]
t.next_week = datetime.datetime.combine(next_week_dt, datetime.time(tzinfo=network_timezones.sb_timezone)) t.next_week = datetime.datetime.combine(next_week_dt, datetime.time(tzinfo=network_timezones.sb_timezone))
t.today = datetime.datetime.now(network_timezones.sb_timezone) t.today = datetime.datetime.now(network_timezones.sb_timezone)
t.sql_results = sql_results t.sql_results = sql_results
@ -1467,7 +1452,6 @@ class History(MainHandler):
ConfigMenu = [ ConfigMenu = [
{'title': 'General', 'path': 'config/general/'}, {'title': 'General', 'path': 'config/general/'},
{'title': 'Backup/Restore', 'path': 'config/backuprestore/'},
{'title': 'Search Settings', 'path': 'config/search/'}, {'title': 'Search Settings', 'path': 'config/search/'},
{'title': 'Search Providers', 'path': 'config/providers/'}, {'title': 'Search Providers', 'path': 'config/providers/'},
{'title': 'Subtitles Settings', 'path': 'config/subtitles/'}, {'title': 'Subtitles Settings', 'path': 'config/subtitles/'},
@ -1544,7 +1528,7 @@ class ConfigGeneral(MainHandler):
update_shows_on_start=None, trash_remove_show=None, trash_rotate_logs=None, update_frequency=None, launch_browser=None, web_username=None, update_shows_on_start=None, trash_remove_show=None, trash_rotate_logs=None, update_frequency=None, launch_browser=None, web_username=None,
use_api=None, api_key=None, indexer_default=None, timezone_display=None, cpu_preset=None, use_api=None, api_key=None, indexer_default=None, timezone_display=None, cpu_preset=None,
web_password=None, version_notify=None, enable_https=None, https_cert=None, https_key=None, web_password=None, version_notify=None, enable_https=None, https_cert=None, https_key=None,
handle_reverse_proxy=None, sort_article=None, auto_update=None, notify_on_update=None, handle_reverse_proxy=None, home_search_focus=None, sort_article=None, auto_update=None, notify_on_update=None,
proxy_setting=None, proxy_indexers=None, anon_redirect=None, git_path=None, git_remote=None, calendar_unprotected=None, proxy_setting=None, proxy_indexers=None, anon_redirect=None, git_path=None, git_remote=None, calendar_unprotected=None,
fuzzy_dating=None, trim_zero=None, date_preset=None, date_preset_na=None, time_preset=None, fuzzy_dating=None, trim_zero=None, date_preset=None, date_preset_na=None, time_preset=None,
indexer_timeout=None, rootDir=None, theme_name=None): indexer_timeout=None, rootDir=None, theme_name=None):
@ -1563,6 +1547,7 @@ class ConfigGeneral(MainHandler):
sickbeard.TRASH_ROTATE_LOGS = config.checkbox_to_value(trash_rotate_logs) sickbeard.TRASH_ROTATE_LOGS = config.checkbox_to_value(trash_rotate_logs)
config.change_UPDATE_FREQUENCY(update_frequency) config.change_UPDATE_FREQUENCY(update_frequency)
sickbeard.LAUNCH_BROWSER = config.checkbox_to_value(launch_browser) sickbeard.LAUNCH_BROWSER = config.checkbox_to_value(launch_browser)
sickbeard.HOME_SEARCH_FOCUS = config.checkbox_to_value(home_search_focus)
sickbeard.SORT_ARTICLE = config.checkbox_to_value(sort_article) sickbeard.SORT_ARTICLE = config.checkbox_to_value(sort_article)
sickbeard.CPU_PRESET = cpu_preset sickbeard.CPU_PRESET = cpu_preset
sickbeard.ANON_REDIRECT = anon_redirect sickbeard.ANON_REDIRECT = anon_redirect
@ -1632,53 +1617,6 @@ class ConfigGeneral(MainHandler):
redirect("/config/general/") redirect("/config/general/")
class ConfigBackupRestore(MainHandler):
def index(self, *args, **kwargs):
t = PageTemplate(headers=self.request.headers, file="config_backuprestore.tmpl")
t.submenu = ConfigMenu
return _munge(t)
def backup(self, backupDir=None):
finalResult = ''
if backupDir:
source = [os.path.join(sickbeard.DATA_DIR, 'sickbeard.db'), sickbeard.CONFIG_FILE]
target = os.path.join(backupDir, 'SickGear-' + time.strftime('%Y%m%d%H%M%S') + '.zip')
if helpers.makeZip(source, target):
finalResult += "Successful backup to " + target
else:
finalResult += "Backup FAILED"
else:
finalResult += "You need to choose a folder to save your backup to!"
finalResult += "<br />\n"
return finalResult
def restore(self, backupFile=None):
finalResult = ''
if backupFile:
source = backupFile
target_dir = os.path.join(sickbeard.DATA_DIR, 'restore')
if helpers.extractZip(source, target_dir):
finalResult += "Successfully extracted restore files to " + target_dir
finalResult += "<br>Restart SickGear to complete the restore."
else:
finalResult += "Restore FAILED"
else:
finalResult += "You need to select a backup file to restore!"
finalResult += "<br />\n"
return finalResult
class ConfigSearch(MainHandler): class ConfigSearch(MainHandler):
def index(self, *args, **kwargs): def index(self, *args, **kwargs):
@ -2701,7 +2639,6 @@ class Config(MainHandler):
# map class names to urls # map class names to urls
general = ConfigGeneral general = ConfigGeneral
backuprestore = ConfigBackupRestore
search = ConfigSearch search = ConfigSearch
providers = ConfigProviders providers = ConfigProviders
subtitles = ConfigSubtitles subtitles = ConfigSubtitles
@ -2873,7 +2810,7 @@ class NewHomeAddShows(MainHandler):
cur_dir = { cur_dir = {
'dir': cur_path, 'dir': cur_path,
'display_dir': '<b>' + ek.ek(os.path.dirname, cur_path) + os.sep + '</b>' + ek.ek( 'display_dir': '<span class="filepath">' + ek.ek(os.path.dirname, cur_path) + os.sep + '</span>' + ek.ek(
os.path.basename, os.path.basename,
cur_path), cur_path),
} }
@ -2890,6 +2827,9 @@ class NewHomeAddShows(MainHandler):
indexer_id = show_name = indexer = None indexer_id = show_name = indexer = None
for cur_provider in sickbeard.metadata_provider_dict.values(): for cur_provider in sickbeard.metadata_provider_dict.values():
if indexer_id and show_name:
continue
(indexer_id, show_name, indexer) = cur_provider.retrieveShowMetadata(cur_path) (indexer_id, show_name, indexer) = cur_provider.retrieveShowMetadata(cur_path)
# default to TVDB if indexer was not detected # default to TVDB if indexer was not detected
@ -2917,6 +2857,10 @@ class NewHomeAddShows(MainHandler):
Display the new show page which collects a tvdb id, folder, and extra options and Display the new show page which collects a tvdb id, folder, and extra options and
posts them to addNewShow posts them to addNewShow
""" """
self.set_header('Cache-Control', 'no-cache, no-store, must-revalidate')
self.set_header('Pragma', 'no-cache')
self.set_header('Expires', '0')
t = PageTemplate(headers=self.request.headers, file="home_newShow.tmpl") t = PageTemplate(headers=self.request.headers, file="home_newShow.tmpl")
t.submenu = HomeMenu() t.submenu = HomeMenu()
@ -2962,6 +2906,10 @@ class NewHomeAddShows(MainHandler):
Display the new show page which collects a tvdb id, folder, and extra options and Display the new show page which collects a tvdb id, folder, and extra options and
posts them to addNewShow posts them to addNewShow
""" """
self.set_header('Cache-Control', 'no-cache, no-store, must-revalidate')
self.set_header('Pragma', 'no-cache')
self.set_header('Expires', '0')
t = PageTemplate(headers=self.request.headers, file="home_recommendedShows.tmpl") t = PageTemplate(headers=self.request.headers, file="home_recommendedShows.tmpl")
t.submenu = HomeMenu() t.submenu = HomeMenu()
@ -2983,11 +2931,21 @@ class NewHomeAddShows(MainHandler):
return return
map(final_results.append, map(final_results.append,
([int(show['tvdb_id'] or 0) if sickbeard.TRAKT_DEFAULT_INDEXER == 1 else int(show['tvdb_id'] or 0), ([show['url'],
show['url'], show['title'], show['overview'], show['title'],
datetime.date.fromtimestamp(int(show['first_aired']) / 1000.0).strftime('%Y%m%d')] for show in show['overview'],
recommendedlist if not helpers.findCertainShow(sickbeard.showList, indexerid=int(show['tvdb_id'])))) sbdatetime.sbdatetime.sbfdate(datetime.date.fromtimestamp(int(show['first_aired']))),
sickbeard.indexerApi(1).name,
sickbeard.indexerApi(1).config['icon'],
int(show['tvdb_id'] or 0),
'%s%s' % (sickbeard.indexerApi(1).config['show_url'], int(show['tvdb_id'] or 0)),
sickbeard.indexerApi(2).name,
sickbeard.indexerApi(2).config['icon'],
int(show['tvrage_id'] or 0),
'%s%s' % (sickbeard.indexerApi(2).config['show_url'], int(show['tvrage_id'] or 0))
] for show in recommendedlist if not helpers.findCertainShow(sickbeard.showList, indexerid=int(show['tvdb_id']))))
self.set_header('Content-Type', 'application/json')
return json.dumps({'results': final_results}) return json.dumps({'results': final_results})
def addRecommendedShow(self, whichSeries=None, indexerLang="en", rootDir=None, defaultStatus=None, def addRecommendedShow(self, whichSeries=None, indexerLang="en", rootDir=None, defaultStatus=None,
@ -3771,10 +3729,17 @@ class Home(MainHandler):
shows.append(show) shows.append(show)
t.sortedShowLists = [["Shows", sorted(shows, lambda x, y: cmp(titler(x.name), titler(y.name)))], t.sortedShowLists = [["Shows", sorted(shows, lambda x, y: cmp(titler(x.name), titler(y.name)))],
["Anime", sorted(anime, lambda x, y: cmp(titler(x.name), titler(y.name)))]] ["Anime", sorted(anime, lambda x, y: cmp(titler(x.name), titler(y.name)))]]
else: else:
t.sortedShowLists = [ t.sortedShowLists = [
["Shows", sorted(sickbeard.showList, lambda x, y: cmp(titler(x.name), titler(y.name)))]] ["Shows", sorted(sickbeard.showList, lambda x, y: cmp(titler(x.name), titler(y.name)))]]
tvshows = []
for tvshow_types in t.sortedShowLists:
for tvshow in tvshow_types[1]:
tvshows.append(tvshow.indexerid)
t.tvshow_id_csv = ','.join(str(x) for x in tvshows)
t.bwl = None t.bwl = None
if showObj.is_anime: if showObj.is_anime:
t.bwl = BlackAndWhiteList(showObj.indexerid) t.bwl = BlackAndWhiteList(showObj.indexerid)

View file

@ -136,6 +136,4 @@ class WebServer(threading.Thread):
def shutDown(self): def shutDown(self):
self.alive = False self.alive = False
if self.server: self.io_loop.stop()
self.server.stop()
self.io_loop.stop()

View file

@ -29,7 +29,7 @@ class DBBasicTests(test.SickbeardTestDBCase):
def test_select(self): def test_select(self):
self.db.select("SELECT * FROM tv_episodes WHERE showid = ? AND location != ''", [0000]) self.db.select("SELECT * FROM tv_episodes WHERE showid = ? AND location != ''", [0000])
self.db.close()
if __name__ == '__main__': if __name__ == '__main__':
print "==================" print "=================="

68
tests/migration_tests.py Normal file
View file

@ -0,0 +1,68 @@
import sys
import os.path
import glob
import unittest
import test_lib as test
import sickbeard
from time import sleep
from sickbeard import db
sys.path.append(os.path.abspath('..'))
sys.path.append(os.path.abspath('../lib'))
sickbeard.SYS_ENCODING = 'UTF-8'
class MigrationBasicTests(test.SickbeardTestDBCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_migrations(self):
schema = {
0: sickbeard.mainDB.InitialSchema,
31: sickbeard.mainDB.AddAnimeTVShow,
32: sickbeard.mainDB.AddAbsoluteNumbering,
33: sickbeard.mainDB.AddSceneAbsoluteNumbering,
34: sickbeard.mainDB.AddAnimeBlacklistWhitelist,
35: sickbeard.mainDB.AddSceneAbsoluteNumbering2,
36: sickbeard.mainDB.AddXemRefresh,
37: sickbeard.mainDB.AddSceneToTvShows,
38: sickbeard.mainDB.AddIndexerMapping,
39: sickbeard.mainDB.AddVersionToTvEpisodes,
41: AddDefaultEpStatusToTvShows,
}
count = 1
while count < len(schema.keys()):
myDB = db.DBConnection()
for version in sorted(schema.keys())[:count]:
update = schema[version](myDB)
update.execute()
sleep(0.1)
db.MigrationCode(myDB)
myDB.close()
for filename in glob.glob(os.path.join(test.TESTDIR, test.TESTDBNAME) +'*'):
os.remove(filename)
sleep(0.1)
count += 1
class AddDefaultEpStatusToTvShows(db.SchemaUpgrade):
def execute(self):
self.addColumn("tv_shows", "default_ep_status", "TEXT", "")
self.setDBVersion(41)
if __name__ == '__main__':
print "=================="
print "STARTING - MIGRATION TESTS"
print "=================="
print "######################################################################"
suite = unittest.TestLoader().loadTestsFromTestCase(MigrationBasicTests)
unittest.TextTestRunner(verbosity=2).run(suite)

View file

@ -20,11 +20,11 @@
from __future__ import with_statement from __future__ import with_statement
import unittest import unittest
import sqlite3 import sqlite3
import glob
import sys import sys
import os.path import os.path
sys.path.append(os.path.abspath('..')) sys.path.append(os.path.abspath('..'))
sys.path.append(os.path.abspath('../lib')) sys.path.append(os.path.abspath('../lib'))
@ -34,7 +34,7 @@ import shutil
from sickbeard import encodingKludge as ek, providers, tvcache from sickbeard import encodingKludge as ek, providers, tvcache
from sickbeard import db from sickbeard import db
from sickbeard.databases import mainDB from sickbeard.databases import mainDB
from sickbeard.databases import cache_db from sickbeard.databases import cache_db, failed_db
#================= #=================
# test globals # test globals
@ -42,7 +42,7 @@ from sickbeard.databases import cache_db
TESTDIR = os.path.abspath('.') TESTDIR = os.path.abspath('.')
TESTDBNAME = "sickbeard.db" TESTDBNAME = "sickbeard.db"
TESTCACHEDBNAME = "cache.db" TESTCACHEDBNAME = "cache.db"
TESTFAILEDDBNAME = "failed.db"
SHOWNAME = u"show name" SHOWNAME = u"show name"
SEASON = 4 SEASON = 4
@ -102,6 +102,7 @@ createTestCacheFolder()
#================= #=================
def _dummy_saveConfig(): def _dummy_saveConfig():
return True return True
# this overrides the sickbeard save_config which gets called during a db upgrade # this overrides the sickbeard save_config which gets called during a db upgrade
# this might be considered a hack # this might be considered a hack
mainDB.sickbeard.save_config = _dummy_saveConfig mainDB.sickbeard.save_config = _dummy_saveConfig
@ -165,7 +166,6 @@ class TestCacheDBConnection(TestDBConnection, object):
sickbeard.db.DBConnection = TestDBConnection sickbeard.db.DBConnection = TestDBConnection
sickbeard.tvcache.CacheDBConnection = TestCacheDBConnection sickbeard.tvcache.CacheDBConnection = TestCacheDBConnection
#================= #=================
# test functions # test functions
#================= #=================
@ -173,24 +173,31 @@ def setUp_test_db():
"""upgrades the db to the latest version """upgrades the db to the latest version
""" """
# upgrading the db # upgrading the db
db.upgradeDatabase(db.DBConnection(), mainDB.InitialSchema) db.MigrationCode(db.DBConnection())
# fix up any db problems # fix up any db problems
db.sanityCheckDatabase(db.DBConnection(), mainDB.MainSanityCheck) db.sanityCheckDatabase(db.DBConnection(), mainDB.MainSanityCheck)
#and for cache.b too # and for cachedb too
db.upgradeDatabase(db.DBConnection("cache.db"), cache_db.InitialSchema) db.upgradeDatabase(db.DBConnection("cache.db"), cache_db.InitialSchema)
# and for faileddb too
db.upgradeDatabase(db.DBConnection("failed.db"), failed_db.InitialSchema)
def tearDown_test_db(): def tearDown_test_db():
"""Deletes the test db """Deletes the test db
although this seams not to work on my system it leaves me with an zero kb file although this seams not to work on my system it leaves me with an zero kb file
""" """
# uncomment next line so leave the db intact between test and at the end # uncomment next line so leave the db intact between test and at the end
return False #return False
if os.path.exists(os.path.join(TESTDIR, TESTDBNAME)):
os.remove(os.path.join(TESTDIR, TESTDBNAME)) for filename in glob.glob(os.path.join(TESTDIR, TESTDBNAME) + '*'):
os.remove(filename)
if os.path.exists(os.path.join(TESTDIR, TESTCACHEDBNAME)): if os.path.exists(os.path.join(TESTDIR, TESTCACHEDBNAME)):
os.remove(os.path.join(TESTDIR, TESTCACHEDBNAME)) os.remove(os.path.join(TESTDIR, TESTCACHEDBNAME))
if os.path.exists(os.path.join(TESTDIR, TESTFAILEDDBNAME)):
os.remove(os.path.join(TESTDIR, TESTFAILEDDBNAME))
def setUp_test_episode_file(): def setUp_test_episode_file():

View file

@ -1,96 +0,0 @@
import subprocess, os, time, sys, os.path, shutil, re
try:
log_file = open('sb-update.log', 'w')
except:
print "Unable to open sb-update.log, not saving output"
log_file = None
def log(string):
if log_file:
log_file.write(string+'\n')
print string
def isProcRunning(pid):
"""See if a pid is running or not"""
tasklist_cmd = 'tasklist /FI "PID eq '+str(pid)+'" /FO CSV'
p = subprocess.Popen(tasklist_cmd, stdout=subprocess.PIPE)
out, err = p.communicate()
results = out.split('\r\n')
regex = '".*\\.exe","'+str(pid)+'",("[^"]*",?){3}'
for cur_line in results:
if re.match(regex, cur_line, re.I):
return True
return False
if len(sys.argv) < 3:
log("Invalid call.")
sys.exit()
try:
# this should be retrieved from sys.args
pid = sys.argv[1]
# process to re-launch
sb_executable = sys.argv[2:]
sb_closed = False
# try 15 times to make sure it's closed
for i in range(15):
isRunning = isProcRunning(pid)
if isRunning:
time.sleep(5)
continue
else:
sb_closed = True
break
if not sb_closed:
log("SickGear didn't close, unable to update. You'll have to manually restart it.")
sys.exit()
sb_root = os.path.dirname(sb_executable[0])
sb_update_dir = os.path.join(sb_root, 'sb-update')
# do the update if applicable
if os.path.isdir(sb_update_dir):
# find update dir name
update_dir_contents = os.listdir(sb_update_dir)
if len(update_dir_contents) != 1:
log("Invalid update data, update failed.")
sys.exit()
content_dir = os.path.join(sb_update_dir, update_dir_contents[0])
# copy everything from sb_update_dir to sb_root
for dirname, dirnames, filenames in os.walk(content_dir):
dirname = dirname[len(content_dir)+1:]
for curfile in filenames:
if curfile == 'updater.exe':
continue
old_path = os.path.join(content_dir, dirname, curfile)
new_path = os.path.join(sb_root, dirname, curfile)
if os.path.isfile(new_path):
os.remove(new_path)
os.renames(old_path, new_path)
if os.path.isdir(sb_update_dir):
shutil.rmtree(sb_update_dir)
# re-launch SB
p = subprocess.Popen(sb_executable, cwd=os.getcwd())
except Exception, e:
log("Exception while updating: "+str(e))
raise
if log_file:
log_file.close()