diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000000000000000000000000000000..df8e3477e4471cd8eddb1ad533c0e5fae1abd139 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "contrib/nzbToMedia"] + path = contrib/nzbToMedia + url = https://github.com/clinton-hall/nzbToMedia diff --git a/SickBeard.py b/SickBeard.py index 7337435ec6908a8fa5cdc67a081d3dc429e328d3..4a012f45f48fdf178664f588bf7f3c7d1830aa70 100755 --- a/SickBeard.py +++ b/SickBeard.py @@ -41,32 +41,9 @@ if sys.version_info < (2, 7): print "Sorry, requires Python 2.7.x" sys.exit(1) -# We only need this for compiling an EXE and I will just always do that on 2.7+ -if sys.hexversion >= 0x020600F0: - from multiprocessing import freeze_support # @UnresolvedImport - -import certifi -for env_cert_var in ['REQUESTS_CA_BUNDLE', 'CURL_CA_BUNDLE']: - ca_cert_loc = os.environ.get(env_cert_var) - if (not isinstance(ca_cert_loc, basestring)) or (not os.path.isfile(ca_cert_loc)): - os.environ[env_cert_var] = certifi.where() - if sys.version_info >= (2, 7, 9): import ssl ssl._create_default_https_context = ssl._create_unverified_context -else: - try: - import cryptography - except ImportError: - try: - from OpenSSL.version import __version__ as pyOpenSSL_Version - if int(pyOpenSSL_Version.replace('.', '')[:3]) > 13: - raise ImportError - except ImportError: - print('\nSNI is disabled with pyOpenSSL >= 0.14 when the cryptography module is missing,\n' + - 'you will encounter SSL errors with HTTPS! To fix this issue:\n' + - 'pip install pyopenssl==0.13.1 (easy) or pip install cryptography (pita)') - import locale import datetime @@ -77,7 +54,6 @@ import sickbeard from sickbeard import db, logger, network_timezones, failed_history, name_cache from sickbeard.tv import TVShow from sickbeard.webserveInit import SRWebServer -from sickbeard.databases.mainDB import MIN_DB_VERSION, MAX_DB_VERSION from sickbeard.event_queue import Events from configobj import ConfigObj from sickbeard import encodingKludge as ek @@ -518,7 +494,7 @@ class SickRage(object): if self.runAsDaemon and self.CREATEPID: self.remove_pid_file(self.PIDFILE) - if type == sickbeard.events.SystemEvent.RESTART: + if type == sickbeard.event_queue.Events.SystemEvent.RESTART: install_type = sickbeard.versionCheckScheduler.action.install_type popen_list = [] @@ -542,8 +518,5 @@ class SickRage(object): if __name__ == "__main__": - if sys.hexversion >= 0x020600F0: - freeze_support() - # start sickrage SickRage().start() diff --git a/autoProcessTV/autoProcessTV.py b/autoProcessTV/autoProcessTV.py index d8d56e3f0f936912cdf94b95806b1eda5bc411c0..5d2c817c8a58ec8c616c9b7b8ab7ed824a49717e 100755 --- a/autoProcessTV/autoProcessTV.py +++ b/autoProcessTV/autoProcessTV.py @@ -4,6 +4,11 @@ # URL: http://code.google.com/p/sickbeard/ # # This file is part of SickRage. +# DEPRECATION NOTICE: autoProcessTV is deprecated and will be removed +# from SickRage at 31-10-2015. +# +# Please switch to nzbToMedia from Clinton Hall, which is included in +# the contrib folder # # SickRage is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/autoProcessTV/hellaToSickBeard.py b/autoProcessTV/hellaToSickBeard.py index 4458cb8c5fd7f65ad7c62f938a88a92029639b1b..b57ddef1cdaedc1c1ca45399b9d7995d61f34d98 100755 --- a/autoProcessTV/hellaToSickBeard.py +++ b/autoProcessTV/hellaToSickBeard.py @@ -4,6 +4,11 @@ # URL: http://code.google.com/p/sickbeard/ # # This file is part of SickRage. +# DEPRECATION NOTICE: autoProcessTV is deprecated and will be removed +# from SickRage at 31-10-2015. +# +# Please switch to nzbToMedia from Clinton Hall, which is included in +# the contrib folder # # SickRage is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/autoProcessTV/mediaToSickbeard.py b/autoProcessTV/mediaToSickbeard.py index 289563af5a6511a067bcc7d40d1c31ecf9e0efb4..a24ffaf657a2561b0c20aa1c4627dc8d4b866e8c 100755 --- a/autoProcessTV/mediaToSickbeard.py +++ b/autoProcessTV/mediaToSickbeard.py @@ -1,4 +1,10 @@ #!/usr/bin/env python2 +# DEPRECATION NOTICE: autoProcessTV is deprecated and will be removed +# from SickRage at 31-10-2015. +# +# Please switch to nzbToMedia from Clinton Hall, which is included in +# the contrib folder + import sys import os import time @@ -138,6 +144,9 @@ def blackhole(): def main(): scriptlogger.info(u'Starting external PostProcess script ' + __file__) + scriptlogger.info(u'DEPRECATION NOTICE: autoProcessTV is deprecated and will be removed from SickRage at 31-10-2015. ' + __file__) + scriptlogger.info(u'Please switch to nzbToMedia from Clinton Hall, which is included in the contrib folder' + __file__) + host = config.get("General", "web_host") port = config.get("General", "web_port") diff --git a/autoProcessTV/sabToSickBeard.py b/autoProcessTV/sabToSickBeard.py index 5e4b85280630687238c5e39d983dee9364c84de3..6419bb9a56050b2e281bc0cc0c0382c36b8894bb 100755 --- a/autoProcessTV/sabToSickBeard.py +++ b/autoProcessTV/sabToSickBeard.py @@ -4,6 +4,11 @@ # URL: http://code.google.com/p/sickbeard/ # # This file is part of SickRage. +# DEPRECATION NOTICE: autoProcessTV is deprecated and will be removed +# from SickRage at 31-10-2015. +# +# Please switch to nzbToMedia from Clinton Hall, which is included in +# the contrib folder # # SickRage is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/contrib/nzbToMedia b/contrib/nzbToMedia new file mode 160000 index 0000000000000000000000000000000000000000..1cd64053b0da1d351bb6af392e1ebb904c6b6ce2 --- /dev/null +++ b/contrib/nzbToMedia @@ -0,0 +1 @@ +Subproject commit 1cd64053b0da1d351bb6af392e1ebb904c6b6ce2 diff --git a/contrib/readme.md b/contrib/readme.md new file mode 100644 index 0000000000000000000000000000000000000000..f4340d621fed01a1c2af66effe9b1118e5b13229 --- /dev/null +++ b/contrib/readme.md @@ -0,0 +1,10 @@ +SickRage Contrib +===== +Stuff contributed to SickRage, or included of users leisure + +## What will you find in here? + - Scripts + - Links to other repositories we think make your life better + - Custom themes + - Other things that may come in handy + diff --git a/gui/slick/css/lib/bootstrap.min.css b/gui/slick/css/lib/bootstrap.min.css index d65c66b1ba297eeb3b5976b71c64c736b41bb763..6dc17a6ab2052a25d92d460e255d70d8688c6f25 100644 Binary files a/gui/slick/css/lib/bootstrap.min.css and b/gui/slick/css/lib/bootstrap.min.css differ diff --git a/gui/slick/css/style.css b/gui/slick/css/style.css index 00ade317f9583a7cc70ea6938c8e8c43a5cd6920..7e9ccc96a97a2bb27a7a2630a9175d58a8845151 100644 --- a/gui/slick/css/style.css +++ b/gui/slick/css/style.css @@ -195,6 +195,11 @@ inc_top.mako background-image: none !important; } +.ui-widget { + font-family: inherit; + font-size: 1em; +} + .ui-widget-content { background: #dcdcdc url("../css/lib/images/ui-bg_highlight-soft_75_dcdcdc_1x100.png") 50% top repeat-x; } @@ -304,6 +309,7 @@ inc_top.mako .ui-tabs .ui-tabs-panel { background-color: #F7F7F7 !important; border: 1px solid #CCCCCC !important; + padding: 1em; } .ui-tabs .ui-tabs-nav li.ui-tabs-active { diff --git a/gui/slick/images/network/abc (au).png b/gui/slick/images/network/abc (au).png new file mode 100644 index 0000000000000000000000000000000000000000..ed50dcaf717c1c2b9b4ed87678d499e9d37edfeb Binary files /dev/null and b/gui/slick/images/network/abc (au).png differ diff --git a/gui/slick/images/network/s4/c.png b/gui/slick/images/network/s4/c.png new file mode 100644 index 0000000000000000000000000000000000000000..2eee369ebbc3f80cf373872e688739cae415b19e Binary files /dev/null and b/gui/slick/images/network/s4/c.png differ diff --git a/gui/slick/images/network/sbs (au).png b/gui/slick/images/network/sbs (au).png new file mode 100644 index 0000000000000000000000000000000000000000..2973a9cb37b3e33e102835c9297b1c049d6b61ae Binary files /dev/null and b/gui/slick/images/network/sbs (au).png differ diff --git a/gui/slick/images/network/toon disney.png b/gui/slick/images/network/toon disney.png new file mode 100644 index 0000000000000000000000000000000000000000..af2479a52778d9877533ab62217b714b486e47fc Binary files /dev/null and b/gui/slick/images/network/toon disney.png differ diff --git a/gui/slick/images/providers/spotweb.png b/gui/slick/images/providers/spotweb.png new file mode 100644 index 0000000000000000000000000000000000000000..b2d709be95e5c81164609ff22f5d9f0b39a89ece Binary files /dev/null and b/gui/slick/images/providers/spotweb.png differ diff --git a/gui/slick/images/providers/spotweb_be.png b/gui/slick/images/providers/spotweb_be.png new file mode 100644 index 0000000000000000000000000000000000000000..b2d709be95e5c81164609ff22f5d9f0b39a89ece Binary files /dev/null and b/gui/slick/images/providers/spotweb_be.png differ diff --git a/gui/slick/interfaces/default/genericMessage.mako b/gui/slick/interfaces/default/genericMessage.mako deleted file mode 100644 index c7835dd385f359ccc802ed96db5ca7c38d5db7e9..0000000000000000000000000000000000000000 --- a/gui/slick/interfaces/default/genericMessage.mako +++ /dev/null @@ -1,4 +0,0 @@ -<%include file="/inc_top.mako"/> -<h2>${subject}</h2> -${message} -<%include file="/inc_bottom.mako"/> diff --git a/gui/slick/interfaces/default/markdown.mako b/gui/slick/interfaces/default/markdown.mako deleted file mode 100644 index 0e462d0bf55be0363644500f78905c0ccab50a80..0000000000000000000000000000000000000000 --- a/gui/slick/interfaces/default/markdown.mako +++ /dev/null @@ -1,3 +0,0 @@ -<%include file="/inc_top.mako"/> -${data} -<%include file="/inc_bottom.mako"/> diff --git a/gui/slick/interfaces/default/restart.mako b/gui/slick/interfaces/default/restart.mako deleted file mode 100644 index a09eabf15c2e7efa71bf0c709d4f50ac620a3ebd..0000000000000000000000000000000000000000 --- a/gui/slick/interfaces/default/restart.mako +++ /dev/null @@ -1,3 +0,0 @@ -<%include file="/inc_top.mako"/> -<%include file="/restart_bare.mako"/> -<%include file="/inc_bottom.mako"/> diff --git a/gui/slick/js/configNotifications.js b/gui/slick/js/configNotifications.js index 338a452281eff62ccd6acf81063d0030ae906fdf..a8a8ca327a1ee245cbbec7f49d6fa1f199188f7c 100644 --- a/gui/slick/js/configNotifications.js +++ b/gui/slick/js/configNotifications.js @@ -343,16 +343,16 @@ $(document).ready(function(){ }); }); - $('#TraktGetPin').click(function () { + $('#TraktGetPin').click(function () { var trakt_pin_url = $('#trakt_pin_url').val(); var w; w = window.open(trakt_pin_url, "popUp", "toolbar=no, scrollbars=no, resizable=no, top=200, left=200, width=650, height=550"); $('#trakt_pin').removeClass('hide'); - }); - + }); + $('#trakt_pin').change(function() { var trakt_pin = $('#trakt_pin').val(); - + if (trakt_pin.length !=0) { $('#TraktGetPin').addClass('hide'); $('#authTrakt').removeClass('hide'); @@ -362,10 +362,10 @@ $(document).ready(function(){ $('#authTrakt').addClass('hide'); } }); - + $('#trakt_pin').keyup(function () { var trakt_pin = $('#trakt_pin').val(); - + if (trakt_pin.length !=0) { $('#TraktGetPin').addClass('hide'); $('#authTrakt').removeClass('hide'); @@ -375,7 +375,7 @@ $(document).ready(function(){ $('#authTrakt').addClass('hide'); } }); - + $('#authTrakt').click(function() { var trakt_pin = $('#trakt_pin').val(); if (trakt_pin.length !=0) { @@ -383,16 +383,15 @@ $(document).ready(function(){ .done(function (data) { $('#testTrakt-result').html(data); $('#authTrakt').addClass('hide'); - $('#trakt_pin').addClass('hide'); - $('#TraktGetPin').addClass('hide'); - }); + $('#trakt_pin').addClass('hide'); + $('#TraktGetPin').addClass('hide'); + }); } }); - + $('#testTrakt').click(function () { var trakt_username = $.trim($('#trakt_username').val()); var trakt_trending_blacklist = $.trim($('#trakt_blacklist_name').val()); - var trakt_disable_ssl_verify = $('#trakt_disable_ssl_verify').is(':checked'); if (!trakt_username) { $('#testTrakt-result').html('Please fill out the necessary fields above.'); if (!trakt_username) { @@ -412,7 +411,7 @@ $(document).ready(function(){ $('#trakt_blacklist_name').removeClass('warning'); $(this).prop('disabled', true); $('#testTrakt-result').html(loading); - $.get(sbRoot + '/home/testTrakt', {'username': trakt_username, 'disable_ssl': trakt_disable_ssl_verify, 'blacklist_name': trakt_trending_blacklist}) + $.get(sbRoot + '/home/testTrakt', {'username': trakt_username, 'blacklist_name': trakt_trending_blacklist}) .done(function (data) { $('#testTrakt-result').html(data); $('#testTrakt').prop('disabled', false); @@ -603,5 +602,5 @@ $(document).ready(function(){ $('.plexinfo').addClass('hide'); } }); - + }); diff --git a/gui/slick/interfaces/default/IRC.mako b/gui/slick/views/IRC.mako similarity index 80% rename from gui/slick/interfaces/default/IRC.mako rename to gui/slick/views/IRC.mako index ea91fd94c806f810863af42b78994276dae3c286..5d4cd6230b659f8a6211f6f6423e973dfb678b54 100644 --- a/gui/slick/interfaces/default/IRC.mako +++ b/gui/slick/views/IRC.mako @@ -1,7 +1,8 @@ -<%include file="/inc_top.mako"/> +<%inherit file="/layouts/main.mako"/> +<%block name="content"> <% from sickbeard import GIT_USERNAME username = ("SickRageUI|?", GIT_USERNAME)[bool(GIT_USERNAME)] %> <iframe id="extFrame" src="https://kiwiirc.com/client/irc.freenode.net/?nick=${username}&theme=basic#sickrage" width="100%" height="500" frameBorder="0" style="border: 1px black solid;"></iframe> -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/apiBuilder.mako b/gui/slick/views/apiBuilder.mako similarity index 100% rename from gui/slick/interfaces/default/apiBuilder.mako rename to gui/slick/views/apiBuilder.mako diff --git a/gui/slick/interfaces/default/comingEpisodes.mako b/gui/slick/views/comingEpisodes.mako similarity index 98% rename from gui/slick/interfaces/default/comingEpisodes.mako rename to gui/slick/views/comingEpisodes.mako index 5eb8c52c862324f1f76a7ef98909bfec2da74db1..62c956b209ff5a98851e94b44321813b1a0599d6 100644 --- a/gui/slick/interfaces/default/comingEpisodes.mako +++ b/gui/slick/views/comingEpisodes.mako @@ -1,3 +1,4 @@ +<%inherit file="/layouts/main.mako"/> <%! import sickbeard from sickbeard.helpers import anon_url @@ -6,63 +7,11 @@ import time import re %> -<% - sort = sickbeard.COMING_EPS_SORT -%> -<%namespace file="/inc_defs.mako" import="renderQualityPill"/> -<%include file="/inc_top.mako"/> +<%block name="scripts"> +<% fuzzydate = 'airdate' %> +<% sort = sickbeard.COMING_EPS_SORT %> <script type="text/javascript" src="${sbRoot}/js/ajaxEpSearch.js?${sbPID}"></script> -<h1 class="header">${header}</h1> -<style type="text/css"> -#SubMenu {display:none;} -#contentWrapper {padding-top:30px;} -</style> - -<div class="h2footer pull-right"> - <span>Layout: - <select name="layout" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;"> - <option value="${sbRoot}/setComingEpsLayout/?layout=poster" ${('', 'selected="selected"')[sickbeard.COMING_EPS_LAYOUT == 'poster']} >Poster</option> - <option value="${sbRoot}/setComingEpsLayout/?layout=calendar" ${('', 'selected="selected"')[sickbeard.COMING_EPS_LAYOUT == 'calendar']} >Calendar</option> - <option value="${sbRoot}/setComingEpsLayout/?layout=banner" ${('', 'selected="selected"')[sickbeard.COMING_EPS_LAYOUT == 'banner']} >Banner</option> - <option value="${sbRoot}/setComingEpsLayout/?layout=list" ${('', 'selected="selected"')[sickbeard.COMING_EPS_LAYOUT == 'list']} >List</option> - </select> - </span> - - - <span>Sort By: - <select name="sort" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;"> - <option value="${sbRoot}/setComingEpsSort/?sort=date" ${('', 'selected="selected"')[sickbeard.COMING_EPS_SORT == 'date']} >Date</option> - <option value="${sbRoot}/setComingEpsSort/?sort=network" ${('', 'selected="selected"')[sickbeard.COMING_EPS_SORT == 'network']} >Network</option> - <option value="${sbRoot}/setComingEpsSort/?sort=show" ${('', 'selected="selected"')[sickbeard.COMING_EPS_SORT == 'show']} >Show</option> - </select> - </span> - - - <span>View Paused: - <select name="viewpaused" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;"> - <option value="${sbRoot}/toggleComingEpsDisplayPaused" ${('', 'selected="selected"')[not bool(sickbeard.COMING_EPS_DISPLAY_PAUSED)]}>Hidden</option> - <option value="${sbRoot}/toggleComingEpsDisplayPaused" ${('', 'selected="selected"')[bool(sickbeard.COMING_EPS_DISPLAY_PAUSED)]}>Shown</option> - </select> - </span> -</div> - -<div class="key pull-right"> -% if 'calendar' != layout: - <b>Key:</b> - <span class="listing-key listing-overdue">Missed</span> - <span class="listing-key listing-current">Current</span> - <span class="listing-key listing-default">Future</span> - <span class="listing-key listing-toofar">Distant</span> -% endif - <a class="btn btn-inline forceBacklog" href="webcal://${sbHost}:${sbHttpPort}/calendar"> - <i class="icon-calendar icon-white"></i>Subscribe</a> -</div> - -<br> - % if 'list' == layout: -<!-- start list view //--> - <script type="text/javascript" src="${sbRoot}/js/plotTooltip.js?${sbPID}"></script> <script type="text/javascript" charset="utf-8"> $.tablesorter.addParser({ @@ -145,7 +94,93 @@ $(document).ready(function(){ }); </script> +% elif layout in ['banner', 'poster']: +<script type="text/javascript" charset="utf-8"> +$(document).ready(function(){ + $('#sbRoot').ajaxEpSearch({'size': 16, 'loadingImage': 'loading16' + themeSpinner + '.gif'}); + $('.ep_summary').hide(); + $('.ep_summaryTrigger').click(function() { + $(this).next('.ep_summary').slideToggle('normal', function() { + $(this).prev('.ep_summaryTrigger').attr('src', function(i, src) { + return $(this).next('.ep_summary').is(':visible') ? src.replace('plus','minus') : src.replace('minus','plus') + }); + }); + }); + % if sickbeard.FUZZY_DATING: + fuzzyMoment({ + dtInline : true, + dtGlue : ' at ', + containerClass : '.${fuzzydate}', + dateHasTime : true, + dateFormat : '${sickbeard.DATE_PRESET}', + timeFormat : '${sickbeard.TIME_PRESET}', + trimZero : ${('false', 'true')[bool(sickbeard.TRIM_ZERO)]} + }); + % endif + +}); +</script> +% endif +<script type="text/javascript" charset="utf-8"> +window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes +</script> +</%block> +<%block name="css"> +<style type="text/css"> +#SubMenu {display:none;} +#contentWrapper {padding-top:30px;} +</style> +</%block> +<%block name="content"> +<% sort = sickbeard.COMING_EPS_SORT %> +<% fuzzydate = 'airdate' %> +<%namespace file="/inc_defs.mako" import="renderQualityPill"/> +<h1 class="header">${header}</h1> +<div class="h2footer pull-right"> + <span>Layout: + <select name="layout" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;"> + <option value="${sbRoot}/setComingEpsLayout/?layout=poster" ${('', 'selected="selected"')[sickbeard.COMING_EPS_LAYOUT == 'poster']} >Poster</option> + <option value="${sbRoot}/setComingEpsLayout/?layout=calendar" ${('', 'selected="selected"')[sickbeard.COMING_EPS_LAYOUT == 'calendar']} >Calendar</option> + <option value="${sbRoot}/setComingEpsLayout/?layout=banner" ${('', 'selected="selected"')[sickbeard.COMING_EPS_LAYOUT == 'banner']} >Banner</option> + <option value="${sbRoot}/setComingEpsLayout/?layout=list" ${('', 'selected="selected"')[sickbeard.COMING_EPS_LAYOUT == 'list']} >List</option> + </select> + </span> + + + <span>Sort By: + <select name="sort" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;"> + <option value="${sbRoot}/setComingEpsSort/?sort=date" ${('', 'selected="selected"')[sickbeard.COMING_EPS_SORT == 'date']} >Date</option> + <option value="${sbRoot}/setComingEpsSort/?sort=network" ${('', 'selected="selected"')[sickbeard.COMING_EPS_SORT == 'network']} >Network</option> + <option value="${sbRoot}/setComingEpsSort/?sort=show" ${('', 'selected="selected"')[sickbeard.COMING_EPS_SORT == 'show']} >Show</option> + </select> + </span> + + + <span>View Paused: + <select name="viewpaused" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;"> + <option value="${sbRoot}/toggleComingEpsDisplayPaused" ${('', 'selected="selected"')[not bool(sickbeard.COMING_EPS_DISPLAY_PAUSED)]}>Hidden</option> + <option value="${sbRoot}/toggleComingEpsDisplayPaused" ${('', 'selected="selected"')[bool(sickbeard.COMING_EPS_DISPLAY_PAUSED)]}>Shown</option> + </select> + </span> +</div> + +<div class="key pull-right"> +% if 'calendar' != layout: + <b>Key:</b> + <span class="listing-key listing-overdue">Missed</span> + <span class="listing-key listing-current">Current</span> + <span class="listing-key listing-default">Future</span> + <span class="listing-key listing-toofar">Distant</span> +% endif + <a class="btn btn-inline forceBacklog" href="webcal://${sbHost}:${sbHttpPort}/calendar"> + <i class="icon-calendar icon-white"></i>Subscribe</a> +</div> + +<br> + +% if 'list' == layout: +<!-- start list view //--> <% show_div = 'listing-default' %> <input type="hidden" id="sbRoot" value="${sbRoot}" /> @@ -251,34 +286,6 @@ $(document).ready(function(){ % elif layout in ['banner', 'poster']: <!-- start non list view //--> -<script type="text/javascript" charset="utf-8"> -$(document).ready(function(){ - $('#sbRoot').ajaxEpSearch({'size': 16, 'loadingImage': 'loading16' + themeSpinner + '.gif'}); - $('.ep_summary').hide(); - $('.ep_summaryTrigger').click(function() { - $(this).next('.ep_summary').slideToggle('normal', function() { - $(this).prev('.ep_summaryTrigger').attr('src', function(i, src) { - return $(this).next('.ep_summary').is(':visible') ? src.replace('plus','minus') : src.replace('minus','plus') - }); - }); - }); - - <% fuzzydate = 'airdate' %> - % if sickbeard.FUZZY_DATING: - fuzzyMoment({ - dtInline : true, - dtGlue : ' at ', - containerClass : '.${fuzzydate}', - dateHasTime : true, - dateFormat : '${sickbeard.DATE_PRESET}', - timeFormat : '${sickbeard.TIME_PRESET}', - trimZero : ${('false', 'true')[bool(sickbeard.TRIM_ZERO)]} - }); - % endif - -}); -</script> - <% cur_segment = None too_late_header = False @@ -396,7 +403,6 @@ $(document).ready(function(){ </tr> <tr> % endif - <td class="next_episode"> <div class="clearfix"> <span class="tvshowTitle"> @@ -516,9 +522,4 @@ $(document).ready(function(){ % endif <div class="clearfix"></div> - -<script type="text/javascript" charset="utf-8"> -window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes -</script> - -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/config.mako b/gui/slick/views/config.mako similarity index 98% rename from gui/slick/interfaces/default/config.mako rename to gui/slick/views/config.mako index 82b327f83b5bec3c6e18b0b9d5deb4c0b047a506..9d3cacc0ab4a5c7759dfccf91c30e6d5afadad90 100644 --- a/gui/slick/interfaces/default/config.mako +++ b/gui/slick/views/config.mako @@ -1,10 +1,11 @@ +<%inherit file="/layouts/main.mako"/> <%! import sickbeard from sickbeard import db from sickbeard.helpers import anon_url import sys, os %> -<%include file="/inc_top.mako"/> +<%block name="content"> % if not header is UNDEFINED: <h1 class="header">${header}</h1> % else: @@ -68,5 +69,4 @@ <tr><td class="infoTableHeader"><i class="icon16-mirc"></i> Internet Relay Chat</td><td class="infoTableCell"><a href="irc://irc.freenode.net/#sickrage" rel="noreferrer"><i>#sickrage</i> on <i>irc.freenode.net</i></a></td></tr> </table> </div> - -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/config_anime.mako b/gui/slick/views/config_anime.mako similarity index 98% rename from gui/slick/interfaces/default/config_anime.mako rename to gui/slick/views/config_anime.mako index 8ba6a04677e6ad842864e23c0343be93a9ccebf3..08008cc050cb55ca8c42427efdd4d23ca8243a48 100644 --- a/gui/slick/interfaces/default/config_anime.mako +++ b/gui/slick/views/config_anime.mako @@ -1,12 +1,14 @@ +<%inherit file="/layouts/main.mako"/> <%! import sickbeard from sickbeard.helpers import anon_url %> -<%include file="/inc_top.mako"/> - +<%block name="scripts"> <script type="text/javascript" src="${sbRoot}/js/configAnime.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/config.js?${sbPID}"></script> +</%block> +<%block name="content"> <div id="content960"> <h1 class="header">${header}</h1> <div id="config"> @@ -96,6 +98,4 @@ </form> </div> </div> - - -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/config_backuprestore.mako b/gui/slick/views/config_backuprestore.mako similarity index 97% rename from gui/slick/interfaces/default/config_backuprestore.mako rename to gui/slick/views/config_backuprestore.mako index 6eaeb3344650cb5c4e57a624bf2e697c61f5f9b0..793d998a860cf0176df2afa9aa5882da6baab5db 100644 --- a/gui/slick/interfaces/default/config_backuprestore.mako +++ b/gui/slick/views/config_backuprestore.mako @@ -1,3 +1,4 @@ +<%inherit file="/layouts/main.mako"/> <%! import datetime import locale @@ -9,9 +10,15 @@ from sickbeard import metadata from sickbeard.metadata.generic import GenericMetadata %> -<%include file="/inc_top.mako"/> +<%block name="scripts"> <script type="text/javascript" src="${sbRoot}/js/configBackupRestore.js?${sbPID}"></script> - +<script type="text/javascript" charset="utf-8"> + $('#backupDir').fileBrowser({ title: 'Select backup folder to save to', key: 'backupPath' }); + $('#backupFile').fileBrowser({ title: 'Select backup files to restore', key: 'backupFile', includeFiles: 1 }); + $('#config-components').tabs(); +</script> +</%block> +<%block name="content"> % if not header is UNDEFINED: <h1 class="header">${header}</h1> % else: @@ -85,11 +92,4 @@ </div> <div class="clearfix"></div> - -<script type="text/javascript" charset="utf-8"> - $('#backupDir').fileBrowser({ title: 'Select backup folder to save to', key: 'backupPath' }); - $('#backupFile').fileBrowser({ title: 'Select backup files to restore', key: 'backupFile', includeFiles: 1 }); - $('#config-components').tabs(); -</script> - -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/config_general.mako b/gui/slick/views/config_general.mako similarity index 99% rename from gui/slick/interfaces/default/config_general.mako rename to gui/slick/views/config_general.mako index d1914035d639e3c7e33b01df5add48e3a58ce3ca..ddba429bc05e201ae75328411fb3def7f7031462 100644 --- a/gui/slick/interfaces/default/config_general.mako +++ b/gui/slick/views/config_general.mako @@ -1,3 +1,4 @@ +<%inherit file="/layouts/main.mako"/> <%! import datetime import locale @@ -10,22 +11,10 @@ from sickbeard.metadata.generic import GenericMetadata from sickbeard.helpers import anon_url %> -<%include file="/inc_top.mako"/> -% if not header is UNDEFINED: - <h1 class="header">${header}</h1> -% else: - <h1 class="title">${title}</h1> -% endif - -<% indexer = 0 %> -% if sickbeard.INDEXER_DEFAULT: - <% indexer = sickbeard.INDEXER_DEFAULT %> -% endif - +<%block name="scripts"> <script type="text/javascript" src="${sbRoot}/js/config.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/rootDirs.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/lib/bootstrap-formhelpers.min-2.3.0.js?${sbPID}"></script> - <script type="text/javascript" charset="utf-8"> $(document).ready(function(){ if ($("input[name='proxy_setting']").val().length == 0) { @@ -42,7 +31,22 @@ } }); }); + + $('#log_dir').fileBrowser({ title: 'Select log file folder location' }); + $('#config-components').tabs(); </script> +</%block> +<%block name="content"> +% if not header is UNDEFINED: + <h1 class="header">${header}</h1> +% else: + <h1 class="title">${title}</h1> +% endif + +<% indexer = 0 %> +% if sickbeard.INDEXER_DEFAULT: + <% indexer = sickbeard.INDEXER_DEFAULT %> +% endif <div id="config"> <div id="config-content"> @@ -614,7 +618,7 @@ <span class="component-title">Use proxy for indexers</span> <span class="component-desc"> <input type="checkbox" name="proxy_indexers" id="proxy_indexers" ${('', 'checked="checked"')[bool(sickbeard.PROXY_INDEXERS)]}/> - <p>use proxy host for connecting to indexers (thetvdb, tvrage)</p> + <p>use proxy host for connecting to indexers (thetvdb)</p> </span> </label> </div> @@ -763,10 +767,4 @@ </div> <div></div> - -<script type="text/javascript" charset="utf-8"> - $('#log_dir').fileBrowser({ title: 'Select log file folder location' }); - $('#config-components').tabs(); -</script> - -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/config_notifications.mako b/gui/slick/views/config_notifications.mako similarity index 99% rename from gui/slick/interfaces/default/config_notifications.mako rename to gui/slick/views/config_notifications.mako index f7b9a98344c7090ee766f5b4af31ad1670c7b368..d299139a4cb986e54b1f248379ed3969b7fd556f 100644 --- a/gui/slick/interfaces/default/config_notifications.mako +++ b/gui/slick/views/config_notifications.mako @@ -1,3 +1,4 @@ +<%inherit file="/layouts/main.mako"/> <% import sickbeard import re @@ -5,10 +6,15 @@ from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED from sickbeard.common import Quality, qualityPresets, statusStrings, qualityPresetStrings, cpu_presets, multiEpStrings %> -<%include file="/inc_top.mako"/> + +<%block name="scripts"> <script type="text/javascript" src="${sbRoot}/js/configNotifications.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/config.js?${sbPID}"></script> - +<script type="text/javascript" charset="utf-8"> + $('#config-components').tabs(); +</script> +</%block> +<%block name="content"> % if not header is UNDEFINED: <h1 class="header">${header}</h1> % else: @@ -1833,7 +1839,4 @@ </div> <div class="clearfix"></div> -<script type="text/javascript" charset="utf-8"> - $('#config-components').tabs(); -</script> -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/config_postProcessing.mako b/gui/slick/views/config_postProcessing.mako similarity index 99% rename from gui/slick/interfaces/default/config_postProcessing.mako rename to gui/slick/views/config_postProcessing.mako index 1b509883add04b01edbeed973427b9445a3e9b53..fd2240da6fa8c1a677744d3c66fe263db489b916 100644 --- a/gui/slick/interfaces/default/config_postProcessing.mako +++ b/gui/slick/views/config_postProcessing.mako @@ -1,4 +1,5 @@ -<% +<%inherit file="/layouts/main.mako"/> +<%! import os.path import sickbeard from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED @@ -8,9 +9,16 @@ from sickbeard.metadata.generic import GenericMetadata from sickbeard import naming %> -<%include file="/inc_top.mako"/> + +<%block name="scripts"> <script type="text/javascript" src="${sbRoot}/js/configPostProcessing.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/config.js?${sbPID}"></script> +<script type="text/javascript"> + $('#config-components').tabs(); + $('#tv_download_dir').fileBrowser({ title: 'Select TV Download Directory' }); +</script> +</%block> +<%block name="content"> <div id="content960"> % if not header is UNDEFINED: <h1 class="header">${header}</h1> @@ -1129,10 +1137,4 @@ </div> <div class="clearfix"></div> - -<script type="text/javascript" charset="utf-8"> - $('#config-components').tabs(); - $('#tv_download_dir').fileBrowser({ title: 'Select TV Download Directory' }); -</script> - -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/config_providers.mako b/gui/slick/views/config_providers.mako similarity index 97% rename from gui/slick/interfaces/default/config_providers.mako rename to gui/slick/views/config_providers.mako index a88ab938b147e4dcb2ab6d32563af09238b4320c..f62699fa5a8e7b92ec6cdf6605ab387647aeb2e8 100644 --- a/gui/slick/interfaces/default/config_providers.mako +++ b/gui/slick/views/config_providers.mako @@ -1,3 +1,4 @@ +<%inherit file="/layouts/main.mako"/> <%! import sickbeard from sickbeard.providers.generic import GenericProvider @@ -5,35 +6,32 @@ from sickbeard.helpers import anon_url %> -<%include file="/inc_top.mako"/> -% if not header is UNDEFINED: - <h1 class="header">${header}</h1> -% else: - <h1 class="title">${title}</h1> -% endif +<%block name="scripts"> <script type="text/javascript" src="${sbRoot}/js/configProviders.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/config.js?${sbPID}"></script> -% if sickbeard.USE_NZBS: -<script type="text/javascript" charset="utf-8"> +<script type="text/javascript"> $(document).ready(function(){ - var show_nzb_providers = ${("false", "true")[bool(sickbeard.USE_NZBS)]}; - % for curNewznabProvider in sickbeard.newznabProviderList: - $(this).addProvider('${curNewznabProvider.getID()}', '${curNewznabProvider.name}', '${curNewznabProvider.url}', '${curNewznabProvider.key}', '${curNewznabProvider.catIDs}', ${int(curNewznabProvider.default)}, show_nzb_providers); - % endfor -}); -</script> -% endif - -% if sickbeard.USE_TORRENTS: -<script type="text/javascript" charset="utf-8"> -$(document).ready(function(){ -% for curTorrentRssProvider in sickbeard.torrentRssProviderList: - $(this).addTorrentRssProvider('${curTorrentRssProvider.getID()}', '${curTorrentRssProvider.name}', '${curTorrentRssProvider.url}', '${curTorrentRssProvider.cookies}', '${curTorrentRssProvider.titleTAG}'); -% endfor + % if sickbeard.USE_NZBS: + var show_nzb_providers = ${("false", "true")[bool(sickbeard.USE_NZBS)]}; + % for curNewznabProvider in sickbeard.newznabProviderList: + $(this).addProvider('${curNewznabProvider.getID()}', '${curNewznabProvider.name}', '${curNewznabProvider.url}', '${curNewznabProvider.key}', '${curNewznabProvider.catIDs}', ${int(curNewznabProvider.default)}, show_nzb_providers); + % endfor + % endif + % if sickbeard.USE_TORRENTS: + % for curTorrentRssProvider in sickbeard.torrentRssProviderList: + $(this).addTorrentRssProvider('${curTorrentRssProvider.getID()}', '${curTorrentRssProvider.name}', '${curTorrentRssProvider.url}', '${curTorrentRssProvider.cookies}', '${curTorrentRssProvider.titleTAG}'); + % endfor + % endif }); +$('#config-components').tabs(); </script> +</%block> +<%block name="content"> +% if not header is UNDEFINED: + <h1 class="header">${header}</h1> +% else: + <h1 class="title">${title}</h1> % endif - <div id="config"> <div id="config-content"> @@ -732,8 +730,4 @@ $(document).ready(function(){ </form> </div> </div> - -<script type="text/javascript" charset="utf-8"> - $('#config-components').tabs(); -</script> -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/config_search.mako b/gui/slick/views/config_search.mako similarity index 99% rename from gui/slick/interfaces/default/config_search.mako rename to gui/slick/views/config_search.mako index fa888fff141e57cf6bba85fa275d2e77bb920ba7..814d374eff34705e6297cd2cbd4ecfa11de0dbcd 100644 --- a/gui/slick/interfaces/default/config_search.mako +++ b/gui/slick/views/config_search.mako @@ -1,12 +1,20 @@ -<% +<%inherit file="/layouts/main.mako"/> +<%! import sickbeard from sickbeard import clients %> -<%include file="/inc_top.mako"/> - +<%block name="scripts"> <script type="text/javascript" src="${sbRoot}/js/configSearch.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/config.js?${sbPID}"></script> - +<script type="text/javascript" charset="utf-8"> + $('#config-components').tabs(); + $('#nzb_dir').fileBrowser({ title: 'Select .nzb black hole/watch location' }); + $('#torrent_dir').fileBrowser({ title: 'Select .torrent black hole/watch location' }); + $('#torrent_path').fileBrowser({ title: 'Select .torrent download location' }); + $('#tv_download_dir').fileBrowser({ title: 'Select TV download location' }); +</script> +</%block> +<%block name="content"> % if not header is UNDEFINED: <h1 class="header">${header}</h1> % else: @@ -573,13 +581,4 @@ </div> <div></div> - -<script type="text/javascript" charset="utf-8"> - $('#config-components').tabs(); - $('#nzb_dir').fileBrowser({ title: 'Select .nzb black hole/watch location' }); - $('#torrent_dir').fileBrowser({ title: 'Select .torrent black hole/watch location' }); - $('#torrent_path').fileBrowser({ title: 'Select .torrent download location' }); - $('#tv_download_dir').fileBrowser({ title: 'Select TV download location' }); -</script> - -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/config_subtitles.mako b/gui/slick/views/config_subtitles.mako similarity index 98% rename from gui/slick/interfaces/default/config_subtitles.mako rename to gui/slick/views/config_subtitles.mako index 9e2b3b49aae1fe347d5694fbcaed93bce6c0741a..93104adf0d2cb523dd105543cf93ca385de3e071 100644 --- a/gui/slick/interfaces/default/config_subtitles.mako +++ b/gui/slick/views/config_subtitles.mako @@ -1,15 +1,15 @@ +<%inherit file="/layouts/main.mako"/> <%! from sickbeard import subtitles import sickbeard from sickbeard.helpers import anon_url %> -<%include file="/inc_top.mako"/> +<%block name="scripts"> <script type="text/javascript" src="${sbRoot}/js/configSubtitles.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/config.js"></script> <script type="text/javascript" src="${sbRoot}/js/lib/jquery.tokeninput.js"></script> - <script type="text/javascript"> - $(document).ready(function() { + $(document).ready(function() { $("#subtitles_languages").tokenInput( [${",\r\n".join("{id: \"" + lang.opensubtitles + "\", name: \"" + lang.name + "\"}" for lang in subtitles.subtitleLanguageFilter())}], { @@ -20,8 +20,11 @@ } ); }); + $('#config-components').tabs(); + $('#subtitles_dir').fileBrowser({ title: 'Select Subtitles Download Directory' }); </script> - +</%block> +<%block name="content"> % if not header is UNDEFINED: <h1 class="header">${header}</h1> % else: @@ -179,8 +182,4 @@ </div> <div class="clearfix"></div> -<script type="text/javascript" charset="utf-8"> - $('#config-components').tabs(); - $('#subtitles_dir').fileBrowser({ title: 'Select Subtitles Download Directory' }); -</script> -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/displayShow.mako b/gui/slick/views/displayShow.mako similarity index 98% rename from gui/slick/interfaces/default/displayShow.mako rename to gui/slick/views/displayShow.mako index c08b5fb4c5f46a2fa50694091c5fc5c049d4201f..453a77b3fa9a844a2a63348d5de894256375e375 100644 --- a/gui/slick/interfaces/default/displayShow.mako +++ b/gui/slick/views/displayShow.mako @@ -1,3 +1,4 @@ +<%inherit file="/layouts/main.mako"/> <%! import datetime import urllib @@ -10,23 +11,19 @@ from sickbeard.common import Quality, qualityPresets, statusStrings, Overview from sickbeard.helpers import anon_url + + fuzzydate = 'airdate' %> -<%namespace file="/inc_defs.mako" import="renderQualityPill"/> -<%include file="/inc_top.mako"/> +<%block name="scripts"> <script type="text/javascript" src="${sbRoot}/js/lib/jquery.bookmarkscroll.js?${sbPID}"></script> - -<input type="hidden" id="sbRoot" value="${sbRoot}" /> - <script type="text/javascript" src="${sbRoot}/js/displayShow.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/plotTooltip.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/sceneExceptionsTooltip.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/ratingTooltip.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/ajaxEpSearch.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/ajaxEpSubtitles.js?${sbPID}"></script> -##<script type="text/javascript" src="${sbRoot}/js/lib/jquery.collapser.min.js?${sbPID}"></script> <script type="text/javascript" charset="utf-8"> $(document).ready(function(){ - <% fuzzydate = 'airdate' %> % if sickbeard.FUZZY_DATING: fuzzyMoment({ containerClass : '.${fuzzydate}', @@ -71,6 +68,10 @@ $(document).ready(function(){ }); }); </script> +</%block> +<%block name="content"> +<%namespace file="/inc_defs.mako" import="renderQualityPill"/> +<input type="hidden" id="sbRoot" value="${sbRoot}" /> <div class="pull-left form-inline"> Change Show: <div class="navShow"><img id="prevShow" src="${sbRoot}/images/prev.png" alt="<<" title="Prev Show" /></div> @@ -140,8 +141,9 @@ $(document).ready(function(){ % endif </span> - % endif </div> + % endif + <div class="clearfix"></div> @@ -279,7 +281,11 @@ $(document).ready(function(){ <div class="pull-left" > Change selected episodes to:</br> <select id="statusSelect" class="form-control form-control-inline input-sm"> - % for curStatus in [WANTED, SKIPPED, ARCHIVED, IGNORED, FAILED] + sorted(Quality.DOWNLOADED): + <% availableStatus = [WANTED, SKIPPED, IGNORED, FAILED] %> + % if not sickbeard.USE_FAILED_DOWNLOADS: + <% availableStatus.remove(FAILED) %> + % endif + % for curStatus in availableStatus + sorted(Quality.DOWNLOADED) + sorted(Quality.ARCHIVED): % if curStatus != DOWNLOADED: <option value="${curStatus}">${statusStrings[curStatus]}</option> % endif @@ -622,4 +628,4 @@ $(document).ready(function(){ <!--End - Bootstrap Modal--> -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/editShow.mako b/gui/slick/views/editShow.mako similarity index 98% rename from gui/slick/interfaces/default/editShow.mako rename to gui/slick/views/editShow.mako index be62433ab15b71668ecc0a1b6ba772aa3e8fd306..d8e68c7c16e87886a648e37539bad19b898c571f 100644 --- a/gui/slick/interfaces/default/editShow.mako +++ b/gui/slick/views/editShow.mako @@ -1,3 +1,4 @@ +<%inherit file="/layouts/main.mako"/> <%! import sickbeard import adba @@ -7,7 +8,71 @@ from sickbeard import exceptions from sickbeard import scene_exceptions %> -<%include file="/inc_top.mako"/> +<%block name="scripts"> +<script type="text/javascript" src="${sbRoot}/js/qualityChooser.js?${sbPID}"></script> +<script type="text/javascript" src="${sbRoot}/js/lib/bootstrap-formhelpers.min-2.3.0.js?${sbPID}"></script> +<script type="text/javascript" charset="utf-8"> + var all_exceptions = new Array; + + $('#location').fileBrowser({ title: 'Select Show Location' }); + + $('#submit').click(function(){ + all_exceptions = [] + + $("#exceptions_list option").each ( function() { + all_exceptions.push( $(this).val() ); + }); + + $("#exceptions_list").val(all_exceptions); + + % if show.is_anime: + generate_bwlist() + % endif + }); + $('#addSceneName').click(function() { + var scene_ex = $('#SceneName').val() + var option = $("<option>") + all_exceptions = [] + + $("#exceptions_list option").each ( function() { + all_exceptions.push( $(this).val() ) + }); + + $('#SceneName').val('') + + if ($.inArray(scene_ex, all_exceptions) > -1 || (scene_ex == '')) + return + + $("#SceneException").show() + + option.attr("value",scene_ex) + option.html(scene_ex) + return option.appendTo('#exceptions_list'); + }); + + $('#removeSceneName').click(function() { + $('#exceptions_list option:selected').remove(); + + $(this).toggle_SceneException() + }); + + $.fn.toggle_SceneException = function() { + all_exceptions = [] + + $("#exceptions_list option").each ( function() { + all_exceptions.push( $(this).val() ); + }); + + if (all_exceptions == '') + $("#SceneException").hide(); + else + $("#SceneException").show(); + } + + $(this).toggle_SceneException(); +</script> +</%block> +<%block name="content"> % if not header is UNDEFINED: <h1 class="header">${header}</h1> % else: @@ -15,9 +80,6 @@ % endif <div id="editShow"> -<script type="text/javascript" src="${sbRoot}/js/qualityChooser.js?${sbPID}"></script> -<script type="text/javascript" src="${sbRoot}/js/lib/bootstrap-formhelpers.min-2.3.0.js?${sbPID}"></script> - <form action="editShow" method="post"> <input type="hidden" name="show" value="${show.indexerid}" /> <b>Location:</b></br> @@ -132,66 +194,5 @@ Separate words with a comma, e.g. "word1,word2,word3"<br /> <input type="submit" id="submit" value="Submit" class="btn btn-primary" /> </form> - -<script type="text/javascript" charset="utf-8"> - var all_exceptions = new Array; - - $('#location').fileBrowser({ title: 'Select Show Location' }); - - $('#submit').click(function(){ - all_exceptions = [] - - $("#exceptions_list option").each ( function() { - all_exceptions.push( $(this).val() ); - }); - - $("#exceptions_list").val(all_exceptions); - - % if show.is_anime: - generate_bwlist() - % endif - }); - $('#addSceneName').click(function() { - var scene_ex = $('#SceneName').val() - var option = $("<option>") - all_exceptions = [] - - $("#exceptions_list option").each ( function() { - all_exceptions.push( $(this).val() ) - }); - - $('#SceneName').val('') - - if ($.inArray(scene_ex, all_exceptions) > -1 || (scene_ex == '')) - return - - $("#SceneException").show() - - option.attr("value",scene_ex) - option.html(scene_ex) - return option.appendTo('#exceptions_list'); - }); - - $('#removeSceneName').click(function() { - $('#exceptions_list option:selected').remove(); - - $(this).toggle_SceneException() - }); - - $.fn.toggle_SceneException = function() { - all_exceptions = [] - - $("#exceptions_list option").each ( function() { - all_exceptions.push( $(this).val() ); - }); - - if (all_exceptions == '') - $("#SceneException").hide(); - else - $("#SceneException").show(); - } - - $(this).toggle_SceneException(); -</script> </div> -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/errorlogs.mako b/gui/slick/views/errorlogs.mako similarity index 83% rename from gui/slick/interfaces/default/errorlogs.mako rename to gui/slick/views/errorlogs.mako index 5131bdd8f8c61e7b817bd8aa93793b498787266a..70b3f02b8eb41a70c5498aa8fc7a4f8754ffd634 100644 --- a/gui/slick/interfaces/default/errorlogs.mako +++ b/gui/slick/views/errorlogs.mako @@ -1,9 +1,15 @@ -<%include file="/inc_top.mako"/> +<%inherit file="/layouts/main.mako"/> <%! import sickbeard from sickbeard import classes from sickbeard.logger import reverseNames %> +<%block name="scripts"> +<script type="text/javascript" charset="utf-8"> +window.setInterval( "location.reload(true)", 600000); // Refresh every 10 minutes +</script> +</%block> +<%block name="content"> <h1 class="header">${title}</h1> <div class="align-left"><pre> % if classes.ErrorViewer.errors: @@ -13,9 +19,4 @@ % endif </pre> </div> - -<script type="text/javascript" charset="utf-8"> -window.setInterval( "location.reload(true)", 600000); // Refresh every 10 minutes -</script> - -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/views/genericMessage.mako b/gui/slick/views/genericMessage.mako new file mode 100644 index 0000000000000000000000000000000000000000..6212becde8ed21a2758f4e0c3f31f9834f02daa5 --- /dev/null +++ b/gui/slick/views/genericMessage.mako @@ -0,0 +1,5 @@ +<%inherit file="/layouts/main.mako"/> +<%block name="content"> +<h2>${subject}</h2> +${message} +</%block> diff --git a/gui/slick/interfaces/default/history.mako b/gui/slick/views/history.mako similarity index 88% rename from gui/slick/interfaces/default/history.mako rename to gui/slick/views/history.mako index bdba48fd79a112f9957dcd7cec8ffe5c6aa8633d..875b93ac441b36c28a193e8dc0358c00458748c4 100644 --- a/gui/slick/interfaces/default/history.mako +++ b/gui/slick/views/history.mako @@ -1,3 +1,4 @@ +<%inherit file="/layouts/main.mako"/> <%! import sickbeard import os.path @@ -5,25 +6,26 @@ import re import time - from sickbeard import history from sickbeard import providers from sickbeard import sbdatetime from sickbeard.providers import generic from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED, DOWNLOADED, SUBTITLED from sickbeard.common import Quality, statusStrings, Overview -%> -<% + + from sickrage.show.History import History + layout = sickbeard.HISTORY_LAYOUT history_limit = sickbeard.HISTORY_LIMIT -%> -<%namespace file="/inc_defs.mako" import="renderQualityPill"/> -<%include file="/inc_top.mako"/> + fuzzydate = 'airdate' +%> +<%block name="css"> <style type="text/css"> .sort_data {display:none;} </style> - +</%block> +<%block name="scripts"> <script type="text/javascript"> $.tablesorter.addParser({ id: 'cDate', @@ -63,12 +65,11 @@ $(document).ready(function(){ } }); - $('#limit').change(function(){ - url = '${sbRoot}/history/?limit='+$(this).val() + $('#history_limit').on('change', function() { + var url = '${sbRoot}/history/?limit=' + $(this).val() window.location.href = url }); - <% fuzzydate = 'airdate' %> % if sickbeard.FUZZY_DATING: fuzzyMoment({ containerClass : '.${fuzzydate}', @@ -82,17 +83,20 @@ $(document).ready(function(){ }); </script> +</%block> +<%block name="content"> +<%namespace file="/inc_defs.mako" import="renderQualityPill"/> % if not header is UNDEFINED: - <h1 class="header">${header}</h1> + <h1 class="header">${header}</h1> % else: - <h1 class="title">${title}</h1> + <h1 class="title">${title}</h1> % endif <div class="h2footer pull-right"><b>Limit:</b> - <select name="history_limit" id="history_limit" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value;"> - <option value="${sbRoot}/setHistoryLimit/?history_limit=100" ${('', 'selected="selected"')[history_limit == 100]}>100</option> - <option value="${sbRoot}/setHistoryLimit/?history_limit=250" ${('', 'selected="selected"')[history_limit == 250]}>250</option> - <option value="${sbRoot}/setHistoryLimit/?history_limit=500" ${('', 'selected="selected"')[history_limit == 500]}>500</option> - <option value="${sbRoot}/setHistoryLimit/?history_limit=0" ${('', 'selected="selected"')[history_limit == 0 ]}>All</option> + <select name="history_limit" id="history_limit" class="form-control form-control-inline input-sm"> + <option value="100" ${('', 'selected="selected"')[limit == 100]}>100</option> + <option value="250" ${('', 'selected="selected"')[limit == 250]}>250</option> + <option value="500" ${('', 'selected="selected"')[limit == 500]}>500</option> + <option value="0" ${('', 'selected="selected"')[limit == 0 ]}>All</option> </select> <span> Layout: @@ -126,9 +130,9 @@ $(document).ready(function(){ % for hItem in historyResults: <% curStatus, curQuality = Quality.splitCompositeStatus(int(hItem["action"])) %> <tr> - <% curdatetime = datetime.datetime.strptime(str(hItem["date"]), history.dateFormat) %> + <% curdatetime = datetime.datetime.strptime(str(hItem["date"]), History.date_format) %> <td align="center"><div class="${fuzzydate}">${sbdatetime.sbdatetime.sbfdatetime(curdatetime, show_seconds=True)}</div><span class="sort_data">${time.mktime(curdatetime.timetuple())}</span></td> - <td class="tvShow" width="35%"><a href="${sbRoot}/home/displayShow?show=${hItem["showid"]}#season-${hItem["season"]}">${hItem["show_name"]} - ${"S%02i" % int(hItem["season"])}${"E%02i" % int(hItem["episode"])} ${('', '<span class="quality Proper">Proper</span>')["proper" in hItem["resource"].lower() or "repack" in hItem["resource"].lower()]}</a></td> + <td class="tvShow" width="35%"><a href="${sbRoot}/home/displayShow?show=${hItem["show_id"]}#season-${hItem["season"]}">${hItem["show_name"]} - ${"S%02i" % int(hItem["season"])}${"E%02i" % int(hItem["episode"])} ${('', '<span class="quality Proper">Proper</span>')["proper" in hItem["resource"].lower() or "repack" in hItem["resource"].lower()]}</a></td> <td align="center" ${('', 'class="subtitles_column"')[curStatus == SUBTITLED]}> % if curStatus == SUBTITLED: <img width="16" height="11" style="vertical-align:middle;" src="${sbRoot}/images/subtitles/flags/${hItem['resource']}.png" onError="this.onerror=null;this.src='${sbRoot}/images/flags/unknown.png';"> @@ -186,7 +190,7 @@ $(document).ready(function(){ <tbody> % for hItem in compactResults: <tr> - <% curdatetime = datetime.datetime.strptime(str(hItem["actions"][0]["time"]), history.dateFormat) %> + <% curdatetime = datetime.datetime.strptime(str(hItem["actions"][0]["time"]), History.date_format) %> <td align="center"><div class="${fuzzydate}">${sbdatetime.sbdatetime.sbfdatetime(curdatetime, show_seconds=True)}</div><span class="sort_data">${time.mktime(curdatetime.timetuple())}</span></td> <td class="tvShow" width="25%"> <span><a href="${sbRoot}/home/displayShow?show=${hItem["show_id"]}#season-${hItem["season"]}">${hItem["show_name"]} - ${"S%02i" % int(hItem["season"])}${"E%02i" % int(hItem["episode"])}${('', ' <span class="quality Proper">Proper</span>')['proper' in hItem["resource"].lower() or 'repack' in hItem["resource"].lower()]}</a></span> @@ -236,5 +240,4 @@ $(document).ready(function(){ </table> % endif - -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/home.mako b/gui/slick/views/home.mako similarity index 91% rename from gui/slick/interfaces/default/home.mako rename to gui/slick/views/home.mako index debfaecc8359cff4f6819c8977160dcd4a515ace..f508b3b6d1ef85b6323d63dba8a98bc7b5b2f2ee 100644 --- a/gui/slick/interfaces/default/home.mako +++ b/gui/slick/views/home.mako @@ -1,3 +1,4 @@ +<%inherit file="/layouts/main.mako"/> <%! import sickbeard import calendar @@ -6,13 +7,11 @@ from sickbeard import db, sbdatetime, network_timezones import datetime import re -%> -<%namespace file="/inc_defs.mako" import="renderQualityPill"/> -<%include file="/inc_top.mako"/> -<% + myDB = db.DBConnection() today = str(datetime.date.today().toordinal()) layout = sickbeard.HOME_LAYOUT + fuzzydate = 'airdate' status_quality = '(' + ','.join([str(x) for x in Quality.SNATCHED + Quality.SNATCHED_PROPER]) + ')' status_download = '(' + ','.join([str(x) for x in Quality.DOWNLOADED + [ARCHIVED]]) + ')' @@ -41,7 +40,7 @@ max_download_count = max_download_count * 100 %> - +<%block name="scripts"> <script type="text/javascript" charset="utf-8"> $.tablesorter.addParser({ id: 'loadingNames', @@ -105,13 +104,12 @@ $.tablesorter.addParser({ type: 'numeric' }); - +function aload(t){"use strict";t=t||window.document.querySelectorAll("[data-aload]"),void 0===t.length&&(t=[t]);var a,e=0,r=t.length;for(e;r>e;e+=1)a=t[e],a["LINK"!==a.tagName?"src":"href"]=a.getAttribute("data-aload"),a.removeAttribute("data-aload");return t} +// Onload +window.onload = function () { + aload(); +}; $(document).ready(function(){ - - $('[data-src]').each(function(){ - $(this).attr('src', $(this).data('src')); - }); - // This needs to be refined to work a little faster. $('.progressbar').each(function(progressbar){ var showId = $(this).data('show-id'); @@ -128,14 +126,15 @@ $(document).ready(function(){ }); $("#showListTableShows:has(tbody tr)").tablesorter({ - sortList: [[6,1],[2,0]], + sortList: [[7,1],[2,0]], textExtraction: { 0: function(node) { return $(node).find("span").text().toLowerCase(); }, 1: function(node) { return $(node).find("span").text().toLowerCase(); }, 3: function(node) { return $(node).find("span").prop("title").toLowerCase(); }, 4: function(node) { return $(node).find("span").text().toLowerCase(); }, 5: function(node) { return $(node).find("span:first").text(); }, - 6: function(node) { return $(node).find("img").attr("alt"); } + 6: function(node) { return $(node).data('show-size'); }, + 7: function(node) { return $(node).find("img").attr("alt"); } }, widgets: ['saveSort', 'zebra', 'stickyHeaders', 'filter', 'columnSelector'], headers: { @@ -145,7 +144,7 @@ $(document).ready(function(){ 4: { sorter: 'quality' }, 5: { sorter: 'eps' }, % if sickbeard.FILTER_ROW: - 6: { filter : 'parsed' } + 7: { filter : 'parsed' } % endif }, widgetOptions : { @@ -219,14 +218,15 @@ $(document).ready(function(){ }); $("#showListTableAnime:has(tbody tr)").tablesorter({ - sortList: [[6,1],[2,0]], + sortList: [[7,1],[2,0]], textExtraction: { 0: function(node) { return $(node).find("span").text().toLowerCase(); }, 1: function(node) { return $(node).find("span").text().toLowerCase(); }, 3: function(node) { return $(node).find("span").prop("title").toLowerCase(); }, 4: function(node) { return $(node).find("span").text().toLowerCase(); }, 5: function(node) { return $(node).find("span:first").text(); }, - 6: function(node) { return $(node).find("img").attr("alt"); } + 6: function(node) { return $(node).data('show-size'); }, + 7: function(node) { return $(node).find("img").attr("alt"); } }, widgets: ['saveSort', 'zebra', 'stickyHeaders', 'filter', 'columnSelector'], headers: { @@ -236,7 +236,7 @@ $(document).ready(function(){ 4: { sorter: 'quality' }, 5: { sorter: 'eps' }, % if sickbeard.FILTER_ROW: - 6: { filter : 'parsed' } + 7: { filter : 'parsed' } % endif }, widgetOptions : { @@ -317,7 +317,6 @@ $(document).ready(function(){ $.tablesorter.filter.bindSearch( "#showListTableAnime", $('.search') ); % endif - <% fuzzydate = 'airdate' %> % if sickbeard.FUZZY_DATING: fuzzyMoment({ dtInline : ${('true', 'false')[layout == 'poster']}, @@ -395,7 +394,9 @@ $(document).ready(function(){ }); </script> - +</%block> +<%block name="content"> +<%namespace file="/inc_defs.mako" import="renderQualityPill"/> % if not header is UNDEFINED: <h1 class="header">${header}</h1> % else: @@ -454,7 +455,7 @@ $(document).ready(function(){ % for curLoadingShow in sickbeard.showQueueScheduler.action.loadingShowList: % if curLoadingShow.show == None: <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;" data-src="${sbRoot}/images/poster.png" /> + <img alt="" title="${curLoadingShow.show_name}" class="show-image" style="border-bottom: 1px solid #111;" data-aload="${sbRoot}/images/poster.png" /> <div class="show-details"> <div class="show-add">Loading... (${curLoadingShow.show_name})</div> </div> @@ -528,7 +529,7 @@ $(document).ready(function(){ %> <div class="show" id="show${curShow.indexerid}" data-name="${curShow.name}" data-date="${data_date}" data-network="${curShow.network}" data-progress="${progressbar_percent}"> <div class="show-image"> - <a href="${sbRoot}/home/displayShow?show=${curShow.indexerid}"><img alt="" class="show-image" data-src="${sbRoot}/showPoster/?show=${curShow.indexerid}&which=poster_thumb" /></a> + <a href="${sbRoot}/home/displayShow?show=${curShow.indexerid}"><img alt="" class="show-image" data-aload="${sbRoot}/showPoster/?show=${curShow.indexerid}&which=poster_thumb" /></a> </div> <div class="progressbar hidden-print" style="position:relative;" data-show-id="${curShow.indexerid}" data-progress-percentage="${progressbar_percent}"></div> @@ -553,6 +554,7 @@ $(document).ready(function(){ % else: <% output_html = '?' + display_status = curShow.status if None is not display_status: if 'nded' not in display_status and 1 == int(curShow.paused): output_html = 'Paused' @@ -572,9 +574,9 @@ $(document).ready(function(){ <td class="show-table"> % if layout != 'simple': % if curShow.network: - <span title="${curShow.network}"><img class="show-network-image" data-src="${sbRoot}/showNetworkLogo/?show=${curShow.indexerid}" alt="${curShow.network}" title="${curShow.network}" /></span> + <span title="${curShow.network}"><img class="show-network-image" data-aload="${sbRoot}/showPoster/?show=${curShow.indexerid}&which=network" alt="${curShow.network}" title="${curShow.network}" /></span> % else: - <span title="No Network"><img class="show-network-image" data-src="${sbRoot}/images/network/nonetwork.png" alt="No Network" title="No Network" /></span> + <span title="No Network"><img class="show-network-image" data-aload="${sbRoot}/images/network/nonetwork.png" alt="No Network" title="No Network" /></span> % endif % else: <span title="${curShow.network}">${curShow.network}</span> @@ -605,6 +607,7 @@ $(document).ready(function(){ <th>Network</th> <th>Quality</th> <th>Downloads</th> + <th>Size</th> <th>Active</th> <th>Status</th> </tr> @@ -620,6 +623,7 @@ $(document).ready(function(){ <th> </th> <th> </th> <th> </th> + <th> </th> </tr> </tfoot> @@ -654,7 +658,6 @@ $(document).ready(function(){ <% myShowList.sort(lambda x, y: cmp(x.name, y.name)) %> % for curShow in myShowList: - <% cur_airs_next = '' cur_airs_prev = '' @@ -700,7 +703,6 @@ $(document).ready(function(){ progressbar_percent = nom * 100 / den %> <tr> - % if cur_airs_next: <% ldatetime = sbdatetime.sbdatetime.convert_to_setting(network_timezones.parse_date_time(cur_airs_next, curShow.airs, curShow.network)) %> % try: @@ -737,7 +739,7 @@ $(document).ready(function(){ <td class="tvShow"> <div class="imgsmallposter ${layout}"> <a href="${sbRoot}/showPoster/?show=${curShow.indexerid}&which=${layout}" rel="dialog" title="${curShow.name}"> - <img data-src="${sbRoot}/showPoster/?show=${curShow.indexerid}&which=poster_thumb" class="${layout}" alt="${curShow.indexerid}"/> + <img data-aload="${sbRoot}/showPoster/?show=${curShow.indexerid}&which=poster_thumb" class="${layout}" alt="${curShow.indexerid}"/> </a> <a href="${sbRoot}/home/displayShow?show=${curShow.indexerid}" style="vertical-align: middle;">${curShow.name}</a> </div> @@ -747,7 +749,7 @@ $(document).ready(function(){ <span style="display: none;">${curShow.name}</span> <div class="imgbanner ${layout}"> <a href="${sbRoot}/home/displayShow?show=${curShow.indexerid}"> - <img data-src="${sbRoot}/showPoster/?show=${curShow.indexerid}&which=banner" class="${layout}" alt="${curShow.indexerid}" title="${curShow.name}"/> + <img data-aload="${sbRoot}/showPoster/?show=${curShow.indexerid}&which=banner" class="${layout}" alt="${curShow.indexerid}" title="${curShow.name}"/> </div> </td> % elif layout == 'simple': @@ -757,10 +759,10 @@ $(document).ready(function(){ % if layout != 'simple': <td align="center"> % if curShow.network: - <span title="${curShow.network}" class="hidden-print"><img id="network" width="54" height="27" data-src="${sbRoot}/showNetworkLogo/?show=${curShow.indexerid}" alt="${curShow.network}" title="${curShow.network}" /></span> + <span title="${curShow.network}" class="hidden-print"><img id="network" width="54" height="27" data-aload="${sbRoot}/showPoster/?show=${curShow.indexerid}&which=network" alt="${curShow.network}" title="${curShow.network}" /></span> <span class="visible-print-inline">${curShow.network}</span> % else: - <span title="No Network" class="hidden-print"><img id="network" width="54" height="27" data-src="${sbRoot}/images/network/nonetwork.png" alt="No Network" title="No Network" /></span> + <span title="No Network" class="hidden-print"><img id="network" width="54" height="27" data-aload="${sbRoot}/images/network/nonetwork.png" alt="No Network" title="No Network" /></span> <span class="visible-print-inline">No Network</span> % endif </td> @@ -779,30 +781,30 @@ $(document).ready(function(){ <span class="visible-print-inline">${download_stat}</span> </td> + <% show_size = sickbeard.helpers.get_size(curShow._location) %> + <td align="center" data-show-size="${show_size}">${sickbeard.helpers.pretty_filesize(show_size)}</td> + <td align="center"> - <img data-src="${sbRoot}/images/${('no16.png" alt="No"', 'yes16.png" alt="Yes"')[int(curShow.paused) == 0 and curShow.status == 'Continuing']} width="16" height="16" /> + <% paused = int(curShow.paused) == 0 and curShow.status == 'Continuing' %> + <img data-aload="${sbRoot}/images/${('no16.png', 'yes16.png')[bool(paused)]}" alt="${('No', 'Yes')[bool(paused)]}" width="16" height="16" /> </td> <td align="center"> -<% display_status = curShow.status %> -% if None is not display_status: - % if re.search(r'(?i)(?:new|returning)\s*series', curShow.status): - <% display_status = 'Continuing' %> - % elif re.search(r'(?i)(?:nded)', curShow.status): - <% display_status = 'Ended' %> - % endif -% endif - + <% + display_status = curShow.status + if None is not display_status: + if re.search('(?i)(?:new|returning)\s*series', curShow.status): + display_status = 'Continuing' + elif re.search('(?i)(?:nded)', curShow.status): + display_status = 'Ended' + %> ${display_status} - </td> - </tr> - % endfor </tbody> </table> % endif % endfor -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/home_addExistingShow.mako b/gui/slick/views/home_addExistingShow.mako similarity index 95% rename from gui/slick/interfaces/default/home_addExistingShow.mako rename to gui/slick/views/home_addExistingShow.mako index 4a1c068562b2945bec2a06bb97a2c8e658c76a44..1865363775befa5e872183ff2089ebc0b5ab50b5 100644 --- a/gui/slick/interfaces/default/home_addExistingShow.mako +++ b/gui/slick/views/home_addExistingShow.mako @@ -1,12 +1,12 @@ +<%inherit file="/layouts/main.mako"/> <%! import sickbeard %> -<%include file="/inc_top.mako"/> +<%block name="scripts"> <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/rootDirs.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/addShowOptions.js?${sbPID}"></script> - <script type="text/javascript" charset="utf-8"> $(document).ready(function(){ $( "#tabs" ).tabs({ @@ -15,7 +15,8 @@ $(document).ready(function(){ }); }); </script> - +</%block> +<%block name="content"> % if not header is UNDEFINED: <h1 class="header">${header}</h1> % else: @@ -65,4 +66,4 @@ $(document).ready(function(){ </div> </div> </div> -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/home_addShows.mako b/gui/slick/views/home_addShows.mako similarity index 93% rename from gui/slick/interfaces/default/home_addShows.mako rename to gui/slick/views/home_addShows.mako index cbc02afb208e4b57be0cee9d41c26857c29b714b..09b79ae5f2c00b8eadd51676d3e8425aad6e668a 100644 --- a/gui/slick/interfaces/default/home_addShows.mako +++ b/gui/slick/views/home_addShows.mako @@ -1,9 +1,10 @@ +<%inherit file="/layouts/main.mako"/> <%! import os.path import urllib import sickbeard %> -<%include file="/inc_top.mako"/> +<%block name="content"> % if not header is UNDEFINED: <h1 class="header">${header}</h1> % else: @@ -15,7 +16,7 @@ <div class="button"><div class="icon-addnewshow"></div></div> <div class="buttontext"> <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 SickRage.</p> + <p>For shows that you haven't downloaded yet, this option finds a show on theTVDB.com, creates a directory for it's episodes, and adds it to SickRage.</p> </div> </a> @@ -65,6 +66,4 @@ </a> </div> - - -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/home_massAddTable.mako b/gui/slick/views/home_massAddTable.mako similarity index 100% rename from gui/slick/interfaces/default/home_massAddTable.mako rename to gui/slick/views/home_massAddTable.mako diff --git a/gui/slick/interfaces/default/home_newShow.mako b/gui/slick/views/home_newShow.mako similarity index 90% rename from gui/slick/interfaces/default/home_newShow.mako rename to gui/slick/views/home_newShow.mako index 3916f9a55d317c19470de9c0c55101bde6030327..75f827b2898d70ecaa7017be07fe5920962671b9 100644 --- a/gui/slick/interfaces/default/home_newShow.mako +++ b/gui/slick/views/home_newShow.mako @@ -1,16 +1,18 @@ +<%inherit file="/layouts/main.mako"/> <%! import sickbeard from sickbeard.helpers import anon_url %> - -<%include file="/inc_top.mako"/> - +<%block name="scripts"> <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/newShow.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/addShowOptions.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/lib/bootstrap-formhelpers.min-2.3.0.js?${sbPID}"></script> - +<script type="text/javascript" src="${sbRoot}/js/rootDirs.js?${sbPID}"></script> +<script type="text/javascript" src="${sbRoot}/js/blackwhite.js?${sbPID}"></script> +</%block> +<%block name="content"> % if not header is UNDEFINED: <h1 class="header">${header}</h1> % else: @@ -31,7 +33,7 @@ <form id="addShowForm" method="post" action="${sbRoot}/home/addShows/addNewShow" accept-charset="utf-8"> <fieldset class="sectionwrap"> - <legend class="legendStep">Find a show on the TVDB or TVRAGE</legend> + <legend class="legendStep">Find a show on theTVDB</legend> <div class="stepDiv"> <input type="hidden" id="indexer_timeout" value="${sickbeard.INDEXER_TIMEOUT}" /> @@ -52,7 +54,7 @@ <option value="0" ${('', 'selected="selected"')[provided_indexer == 0]}>All Indexers</option> % for indexer in indexers: <option value="${indexer}" ${('', 'selected="selected"')[provided_indexer == indexer]}> - ${(indexers[indexer], ''.join((indexers[indexer], ' **')))[indexers[indexer] == 'TVRage']} + ${indexers[indexer]} </option> % endfor </select> @@ -61,9 +63,7 @@ <br /><br /> <b>*</b> This will only affect the language of the retrieved metadata file contents and episode filenames.<br /> - This <b>DOES NOT</b> allow SickRage to download non-english TV episodes!<br /> - <b>** IMPORTANT: </b> TVRAGE indexer implementation doesn't currently support <b>specials</b> and <b>banners/posters</b>.<br /> - <br /> + This <b>DOES NOT</b> allow SickRage to download non-english TV episodes!<br /><br /> <div id="searchResults" style="height: 100%;"><br/></div> % endif @@ -104,9 +104,5 @@ <input class="btn" type="button" id="skipShowButton" value="Skip Show" /> % endif </div> - -<script type="text/javascript" src="${sbRoot}/js/rootDirs.js?${sbPID}"></script> -<script type="text/javascript" src="${sbRoot}/js/blackwhite.js?${sbPID}"></script> - </div></div></div></div> -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/home_popularShows.mako b/gui/slick/views/home_popularShows.mako similarity index 94% rename from gui/slick/interfaces/default/home_popularShows.mako rename to gui/slick/views/home_popularShows.mako index 6e9f94988a848225fddb784a522ca89b14c7948b..41b496e8a89196325b534a07ab1de10ddb7fc84e 100644 --- a/gui/slick/interfaces/default/home_popularShows.mako +++ b/gui/slick/views/home_popularShows.mako @@ -1,7 +1,6 @@ +<%inherit file="/layouts/main.mako"/> <% from sickbeard.helpers import anon_url %> - -<%include file="/inc_top.mako"/> - +<%block name="content"> <h2>Popular Shows</h2> <br /> @@ -35,6 +34,4 @@ </div> % endfor % endif - - -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/home_postprocess.mako b/gui/slick/views/home_postprocess.mako similarity index 95% rename from gui/slick/interfaces/default/home_postprocess.mako rename to gui/slick/views/home_postprocess.mako index 4295d170cc28e9a4ef5b20d05a4f4b3e674a5f33..68faecceac061befbf41dcded1c7406c6faac44f 100644 --- a/gui/slick/interfaces/default/home_postprocess.mako +++ b/gui/slick/views/home_postprocess.mako @@ -1,7 +1,13 @@ -<% +<%inherit file="/layouts/main.mako"/> +<%! import sickbeard %> -<%include file="/inc_top.mako"/> +<%block name="scripts"> +<script type="text/javascript"> + $('#episodeDir').fileBrowser({ title: 'Select Unprocessed Episode Folder', key: 'postprocessPath' }); +</script> +</%block> +<%block name="content"> <div id="content800"> % if not header is UNDEFINED: <h1 class="header">${header}</h1> @@ -73,10 +79,5 @@ </table> <input id="submit" class="btn" type="submit" value="Process" /> </form> - -<script type="text/javascript" charset="utf-8"> - $('#episodeDir').fileBrowser({ title: 'Select Unprocessed Episode Folder', key: 'postprocessPath' }); -</script> </div> - -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/home_recommendedShows.mako b/gui/slick/views/home_recommendedShows.mako similarity index 96% rename from gui/slick/interfaces/default/home_recommendedShows.mako rename to gui/slick/views/home_recommendedShows.mako index afa0373b570c0564893afe17e4fa3c5a64705bb6..89688574820db62705db25f8adb9bde7bb283800 100644 --- a/gui/slick/interfaces/default/home_recommendedShows.mako +++ b/gui/slick/views/home_recommendedShows.mako @@ -1,11 +1,11 @@ +<%inherit file="/layouts/main.mako"/> <%! import sickbeard %> -<%include file="/inc_top.mako"/> +<%block name="scripts"> <script type="text/javascript" src="${sbRoot}/js/recommendedShows.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/rootDirs.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/plotTooltip.js?${sbPID}"></script> - <script type="text/javascript" charset="utf-8"> $(document).ready(function(){ $( "#tabs" ).tabs({ @@ -67,8 +67,11 @@ $(document).ready(function(){ $('#container').isotope({sortAscending: ('asc' == this.value)}); }); }); -</script> +window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes +</script> +</%block> +<%block name="content"> % if not header is UNDEFINED: <h1 class="header">${header}</h1> % else: @@ -107,9 +110,4 @@ $(document).ready(function(){ <br /> <div id="trendingShows"></div> <br /> - -<script type="text/javascript" charset="utf-8"> -window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes -</script> - -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/home_trendingShows.mako b/gui/slick/views/home_trendingShows.mako similarity index 95% rename from gui/slick/interfaces/default/home_trendingShows.mako rename to gui/slick/views/home_trendingShows.mako index 7091d17a20bc4a5fa88e4aa83eccb3e8b3f87841..d6aa8d55d5aa80798f429482d7e53a35fdf698da 100644 --- a/gui/slick/interfaces/default/home_trendingShows.mako +++ b/gui/slick/views/home_trendingShows.mako @@ -1,3 +1,4 @@ +<%inherit file="/layouts/main.mako"/> <%! import sickbeard import datetime @@ -7,13 +8,11 @@ from sickbeard import sbdatetime from sickbeard.helpers import anon_url %> -<%include file="/inc_top.mako"/> - +<%block name="scripts"> <script type="text/javascript" src="${sbRoot}/js/addTrendingShow.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/rootDirs.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/plotTooltip.js?${sbPID}"></script> - -<script type="text/javascript" charset="utf-8"> +<script type="text/javascript"> $(document).ready(function(){ $( "#tabs" ).tabs({ collapsible: true, @@ -74,8 +73,10 @@ $(document).ready(function(){ $('#container').isotope({sortAscending: ('asc' == this.value)}); }); }); +window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes </script> - +</%block> +<%block name="content"> % if not header is UNDEFINED: <h1 class="header">${header}</h1> % else: @@ -114,8 +115,4 @@ $(document).ready(function(){ <br /> <div id="trendingShows"></div> <br /> - -<script type="text/javascript" charset="utf-8"> -window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes -</script> -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/inc_addShowOptions.mako b/gui/slick/views/inc_addShowOptions.mako similarity index 100% rename from gui/slick/interfaces/default/inc_addShowOptions.mako rename to gui/slick/views/inc_addShowOptions.mako diff --git a/gui/slick/interfaces/default/inc_blackwhitelist.mako b/gui/slick/views/inc_blackwhitelist.mako similarity index 100% rename from gui/slick/interfaces/default/inc_blackwhitelist.mako rename to gui/slick/views/inc_blackwhitelist.mako diff --git a/gui/slick/interfaces/default/inc_bottom.mako b/gui/slick/views/inc_bottom.mako similarity index 79% rename from gui/slick/interfaces/default/inc_bottom.mako rename to gui/slick/views/inc_bottom.mako index 1e51091e352ec1a44e0a2cd1c3a85a8648e3d811..3b8681ddc5ba7c9aec28b900a5dca6ea13539762 100644 --- a/gui/slick/interfaces/default/inc_bottom.mako +++ b/gui/slick/views/inc_bottom.mako @@ -3,7 +3,8 @@ import datetime from sickbeard import db from sickbeard.common import Quality, SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST - import re + from time import time + import re, resource %> <% sbRoot = sickbeard.WEB_ROOT @@ -62,15 +63,27 @@ / <span class="footerhighlight">${ep_total}</span> Episodes Downloaded ${ep_percentage} | Daily Search: <span class="footerhighlight">${str(sickbeard.dailySearchScheduler.timeLeft()).split('.')[0]}</span> | Backlog Search: <span class="footerhighlight">${str(sickbeard.backlogSearchScheduler.timeLeft()).split('.')[0]}</span> + + % if sickbeard.DEVELOPER: + <div> + Memory used: <span class="footerhighlight">${sickbeard.helpers.pretty_filesize(resource.getrusage(resource.RUSAGE_SELF).ru_maxrss)}</span> | + Load time: <span class="footerhighlight">${"%.4f" % (time() - sbStartTime)}s</span> / Mako: <span class="footerhighlight">${"%.4f" % (time() - makoStartTime)}s</span> | + Branch: <span class="footerhighlight">${sickbeard.BRANCH}</span> + </div> + % endif </div> <!-- <ul style="display: table; margin: 0 auto; font-size: 12px; list-style-type: none; padding: 0; padding-top: 10px;"> <li><a href="${sbRoot}/manage/manageSearches/forceVersionCheck"><img src="${sbRoot}/images/menu/update16.png" alt="" width="16" height="16" style="vertical-align:middle;" />Force Version Check</a></li> - <li><a href="${sbRoot}/home/restart/?pid=${sbPID}" class="confirm"><img src="${sbRoot}/images/menu/restart16.png" alt="" width="16" height="16" style="vertical-align:middle;" />Restart</a></li> - <li><a href="${sbRoot}/home/shutdown/?pid=${sbPID}" class="confirm"><img src="${sbRoot}/images/menu/shutdown16.png" alt="" width="16" height="16" style="vertical-align:middle;" />Shutdown</a></li> + <li><a href="${sbRoot}/home/restart/?pid=${sbPID}" class="confirm"><img src="${sbRoot}/images/menu/restart16.png" alt="" width="16" height="16" style="vertical-align:middle;" /><i class="menu-icon-restart"></i> Restart</a></li> + <li><a href="${sbRoot}/home/shutdown/?pid=${sbPID}" class="confirm"><img src="${sbRoot}/images/menu/shutdown16.png" alt="" width="16" height="16" style="vertical-align:middle;" /><i class="menu-icon-shutdown"></i> Shutdown</a></li> </ul> --> </footer> - + <script type="text/javascript"> + $(document).ready(function() { + $('.dropdown-toggle').dropdownHover(); + }); + </script> </body> </html> diff --git a/gui/slick/interfaces/default/inc_defs.mako b/gui/slick/views/inc_defs.mako similarity index 100% rename from gui/slick/interfaces/default/inc_defs.mako rename to gui/slick/views/inc_defs.mako diff --git a/gui/slick/interfaces/default/inc_qualityChooser.mako b/gui/slick/views/inc_qualityChooser.mako similarity index 100% rename from gui/slick/interfaces/default/inc_qualityChooser.mako rename to gui/slick/views/inc_qualityChooser.mako diff --git a/gui/slick/interfaces/default/inc_rootDirs.mako b/gui/slick/views/inc_rootDirs.mako similarity index 100% rename from gui/slick/interfaces/default/inc_rootDirs.mako rename to gui/slick/views/inc_rootDirs.mako diff --git a/gui/slick/interfaces/default/inc_top.mako b/gui/slick/views/inc_top.mako similarity index 75% rename from gui/slick/interfaces/default/inc_top.mako rename to gui/slick/views/inc_top.mako index d8bcb92a2770778fa297bfa1efcfeb3bf90e542e..2fefcac40d6a51033e8e0ff41d3724e33a10bc06 100644 --- a/gui/slick/interfaces/default/inc_top.mako +++ b/gui/slick/views/inc_top.mako @@ -55,7 +55,18 @@ <link rel="stylesheet" type="text/css" href="${sbRoot}/css/country-flags.css?${sbPID}"/> % endif - <script type="text/javascript" src="${sbRoot}/js/lib/jquery-1.11.2.min.js?${sbPID}"></script> + <!--[if !IE]><!--> + <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script> + <!--<![endif]--> + + <!--[if lte IE 8]> + <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> + <![endif]--> + + <!--[if gt IE 8]> + <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script> + <![endif]--> + <script type="text/javascript" src="${sbRoot}/js/lib/bootstrap.min.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/lib/bootstrap-hover-dropdown.min.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/lib/jquery-ui-1.10.4.custom.min.js?${sbPID}"></script> @@ -90,55 +101,7 @@ <script type="text/javascript" src="${sbRoot}/js/lib/jquery.scrolltopcontrol-1.1.js"></script> <script type="text/javascript" src="${sbRoot}/js/browser.js"></script> <script type="text/javascript" src="${sbRoot}/js/ajaxNotifications.js"></script> - <script type="text/javascript"> - function initActions() { - $("#SubMenu a[href*='/home/restart/']").addClass('restart').html('<span class="submenu-icon-restart"></span> Restart'); - $("#SubMenu a[href*='/home/shutdown/']").addClass('shutdown').html('<span class="submenu-icon-shutdown"></span> Shutdown'); - $("#SubMenu a[href*='/home/logout/']").html('<span class="ui-icon ui-icon-power"></span> Logout'); - $("#SubMenu a:contains('Edit')").html('<span class="ui-icon ui-icon-pencil"></span> Edit'); - $("#SubMenu a:contains('Remove')").addClass('remove').html('<span class="ui-icon ui-icon-trash"></span> Remove'); - $("#SubMenu a:contains('Clear History')").addClass('clearhistory').html('<span class="ui-icon ui-icon-trash"></span> Clear History'); - $("#SubMenu a:contains('Trim History')").addClass('trimhistory').html('<span class="ui-icon ui-icon-trash"></span> Trim History'); - $("#SubMenu a[href$='/errorlogs/clearerrors/']").html('<span class="ui-icon ui-icon-trash"></span> Clear Errors'); - $("#SubMenu a[href$='/errorlogs/submit_errors/']").html('<span class="ui-icon ui-icon-arrowreturnthick-1-n"></span> Submit Errors'); - $("#SubMenu a:contains('Re-scan')").html('<span class="ui-icon ui-icon-refresh"></span> Re-scan'); - $("#SubMenu a:contains('Backlog Overview')").html('<span class="ui-icon ui-icon-refresh"></span> Backlog Overview'); - $("#SubMenu a[href$='/home/updatePLEX/']").html('<span class="ui-icon ui-icon-refresh"></span> Update PLEX'); - $("#SubMenu a:contains('Force')").html('<span class="ui-icon ui-icon-transfer-e-w"></span> Force Full Update'); - $("#SubMenu a:contains('Rename')").html('<span class="ui-icon ui-icon-tag"></span> Preview Rename'); - $("#SubMenu a[href$='/config/subtitles/']").html('<span class="ui-icon ui-icon-comment"></span> Search Subtitles'); - $("#SubMenu a[href*='/home/subtitleShow']").html('<span class="ui-icon ui-icon-comment"></span> Download Subtitles'); - $("#SubMenu a:contains('Anime')").html('<span class="submenu-icon-anime"></span> Anime'); - $("#SubMenu a:contains('Settings')").html('<span class="ui-icon ui-icon-search"></span> Search Settings'); - $("#SubMenu a:contains('Provider')").html('<span class="ui-icon ui-icon-search"></span> Search Providers'); - $("#SubMenu a:contains('Backup/Restore')").html('<span class="ui-icon ui-icon-gear"></span> Backup/Restore'); - $("#SubMenu a:contains('General')").html('<span class="ui-icon ui-icon-gear"></span> General'); - $("#SubMenu a:contains('Episode Status')").html('<span class="ui-icon ui-icon-transferthick-e-w"></span> Episode Status Management'); - $("#SubMenu a:contains('Missed Subtitle')").html('<span class="ui-icon ui-icon-transferthick-e-w"></span> Missed Subtitles'); - $("#SubMenu a[href$='/home/addShows/']").html('<span class="ui-icon ui-icon-video"></span> Add Show'); - $("#SubMenu a:contains('Processing')").html('<span class="ui-icon ui-icon-folder-open"></span> Post-Processing'); - $("#SubMenu a:contains('Manage Searches')").html('<span class="ui-icon ui-icon-search"></span> Manage Searches'); - $("#SubMenu a:contains('Manage Torrents')").html('<span class="submenu-icon-bittorrent"></span> Manage Torrents'); - $("#SubMenu a[href$='/manage/failedDownloads/']").html('<span class="submenu-icon-failed-download"></span> Failed Downloads'); - $("#SubMenu a:contains('Notification')").html('<span class="ui-icon ui-icon-note"></span> Notifications'); - $("#SubMenu a:contains('Update show in KODI')").html('<span class="submenu-icon-kodi"></span> Update show in KODI'); - $("#SubMenu a[href$='/home/updateKODI/']").html('<span class="submenu-icon-kodi"></span> Update KODI'); - $("#SubMenu a:contains('Update show in Emby')").html('<span class="ui-icon ui-icon-refresh"></span> Update show in Emby'); - $("#SubMenu a[href$='/home/updateEMBY/']").html('<span class="ui-icon ui-icon-refresh"></span> Update Emby'); - $("#SubMenu a:contains('Pause')").html('<span class="ui-icon ui-icon-pause"></span> Pause'); - $("#SubMenu a:contains('Resume')").html('<span class="ui-icon ui-icon-play"></span> Resume'); - - $('#SubMenu a span').addClass('pull-left'); - - }; - - $(document).ready(function() { - initActions(); - - $('.dropdown-toggle').dropdownHover(); - }); - </script> - <script type="text/javascript" src="${sbRoot}/js/confirmations.js?${sbPID}"></script> + <script type="text/javascript" src="${sbRoot}/js/confirmations.js?${sbPID}"></script> % endif </head> @@ -204,7 +167,7 @@ </ul> </li> - <li id="NAVerrorlogs" class="dropdown" ${('', 'class="active"')[topmenu == 'errorlogs']}> + <li id="NAVerrorlogs" class="dropdown ${('', 'active')[topmenu == 'errorlogs']}"> <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> View Log (Errors)</a></li> @@ -212,7 +175,7 @@ </ul> </li> - <li id="NAVconfig" class="dropdown" ${('', 'class="active"')[topmenu == 'config']}> + <li id="NAVconfig" class="dropdown ${('', 'active')[topmenu == 'config']}"> <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> Help & Info</a></li> @@ -227,7 +190,7 @@ </ul> </li> - <li class="dropdown"> + <li class="dropdown ${('', 'active')[topmenu == 'system']}"> <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"> <li><a href="${sbRoot}/home/updateCheck?pid=${sbPID}"><i class="menu-icon-update"></i> Check For Updates</a></li> @@ -251,6 +214,7 @@ <% first = True %> % for menuItem in submenu: % if 'requires' not in menuItem or menuItem['requires']: + <% icon_class = '' if 'icon' not in menuItem else ' ' + menuItem['icon'] %> % if type(menuItem['path']) == dict: ${("</span><span>", "")[bool(first)]}<b>${menuItem['title']}</b> <% @@ -262,7 +226,7 @@ <% inner_first = False %> % endfor % else: - <a href="${sbRoot}/${menuItem['path']}" class="btn${('', ' confirm')['confirm' in menuItem]}">${menuItem['title']}</a> + <a href="${sbRoot}/${menuItem['path']}" class="btn${('', ' confirm')['confirm' in menuItem]}">${('', '<span class="pull-left ' + icon_class + '"></span> ')[bool(icon_class)]}${menuItem['title']}</a> <% first = False %> % endif % endif diff --git a/gui/slick/views/layouts/main.mako b/gui/slick/views/layouts/main.mako new file mode 100644 index 0000000000000000000000000000000000000000..ed22d06b9240f76698626398d440b8c0737d485a --- /dev/null +++ b/gui/slick/views/layouts/main.mako @@ -0,0 +1,319 @@ +<%! + import sickbeard + import datetime + from sickbeard import db + from sickbeard.common import Quality, SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED + from sickbeard.common import qualityPresets, qualityPresetStrings + import calendar + from time import time + import re, resource +%> +<% + sbRoot = sickbeard.WEB_ROOT +%> +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <meta name="robots" content="noindex, nofollow"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width"> + + <!-- These values come from css/dark.css and css/light.css --> + % if sickbeard.THEME_NAME == "dark": + <meta name="theme-color" content="#15528F"> + % elif sickbeard.THEME_NAME == "light": + <meta name="theme-color" content="#333333"> + % endif + + <title>SickRage - BRANCH:[${sickbeard.BRANCH}] - ${title}</title> + + <!--[if lt IE 9]> + <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> + <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> + <![endif]--> + <meta name="msapplication-TileColor" content="#FFFFFF"> + <meta name="msapplication-TileImage" content="${sbRoot}/images/ico/favicon-144.png"> + <meta name="msapplication-config" content="${sbRoot}/css/browserconfig.xml"> + + <link rel="shortcut icon" href="${sbRoot}/images/ico/favicon.ico"> + <link rel="icon" sizes="16x16 32x32 64x64" href="${sbRoot}/images/ico/favicon.ico"> + <link rel="icon" type="image/png" sizes="196x196" href="${sbRoot}/images/ico/favicon-196.png"> + <link rel="icon" type="image/png" sizes="160x160" href="${sbRoot}/images/ico/favicon-160.png"> + <link rel="icon" type="image/png" sizes="96x96" href="${sbRoot}/images/ico/favicon-96.png"> + <link rel="icon" type="image/png" sizes="64x64" href="${sbRoot}/images/ico/favicon-64.png"> + <link rel="icon" type="image/png" sizes="32x32" href="${sbRoot}/images/ico/favicon-32.png"> + <link rel="icon" type="image/png" sizes="16x16" href="${sbRoot}/images/ico/favicon-16.png"> + <link rel="apple-touch-icon" sizes="152x152" href="${sbRoot}/images/ico/favicon-152.png"> + <link rel="apple-touch-icon" sizes="144x144" href="${sbRoot}/images/ico/favicon-144.png"> + <link rel="apple-touch-icon" sizes="120x120" href="${sbRoot}/images/ico/favicon-120.png"> + <link rel="apple-touch-icon" sizes="114x114" href="${sbRoot}/images/ico/favicon-114.png"> + <link rel="apple-touch-icon" sizes="76x76" href="${sbRoot}/images/ico/favicon-76.png"> + <link rel="apple-touch-icon" sizes="72x72" href="${sbRoot}/images/ico/favicon-72.png"> + <link rel="apple-touch-icon" href="${sbRoot}/images/ico/favicon-57.png"> + + <link rel="stylesheet" type="text/css" href="${sbRoot}/css/lib/bootstrap.min.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.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/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/print.css?${sbPID}" /> + % if sbLogin: + <link rel="stylesheet" type="text/css" href="${sbRoot}/css/lib/pnotify.custom.min.css?${sbPID}" /> + <link rel="stylesheet" type="text/css" href="${sbRoot}/css/country-flags.css?${sbPID}"/> + % endif + <%block name="css" /> + </head> + + <body> + <nav class="navbar navbar-default navbar-fixed-top hidden-print" role="navigation"> + <div class="container-fluid"> + <div class="navbar-header"> + <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"> + <span class="sr-only">Toggle navigation</span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + </button> + <a class="navbar-brand" href="${sbRoot}/home/" title="SickRage"><img alt="SickRage" src="${sbRoot}/images/sickrage.png" style="height: 50px;" class="img-responsive pull-left" /></a> + </div> + + % if sbLogin: + <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> + <ul class="nav navbar-nav navbar-right"> + <li id="NAVnews" ${('', 'class="active"')[topmenu == 'news']}> + <a href="${sbRoot}/news/">News</a> + </li> + <li id="NAVirc" ${('', 'class="active"')[topmenu == 'irc']}> + <a href="${sbRoot}/IRC/">IRC</a> + </li> + <li id="NAVhome" ${('', 'class="active"')[topmenu == 'home']}> + <a href="${sbRoot}/home/">Shows</a> + </li> + + <li id="NAVcomingEpisodes" ${('', 'class="active"')[topmenu == 'comingEpisodes']}> + <a href="${sbRoot}/comingEpisodes/">Coming Episodes</a> + </li> + + <li id="NAVhistory" ${('', 'class="active"')[topmenu == 'history']}> + <a href="${sbRoot}/history/">History</a> + </li> + + <li id="NAVmanage" class="dropdown ${('', 'active')[topmenu == 'manage']}"> + <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> Mass Update</a></li> + <li><a href="${sbRoot}/manage/backlogOverview/"><i class="menu-icon-backlog-view"></i> Backlog Overview</a></li> + <li><a href="${sbRoot}/manage/manageSearches/"><i class="menu-icon-manage-searches"></i> Manage Searches</a></li> + <li><a href="${sbRoot}/manage/episodeStatuses/"><i class="menu-icon-backlog"></i> Episode Status Management</a></li> + % if sickbeard.USE_PLEX and sickbeard.PLEX_SERVER_HOST != "": + <li><a href="${sbRoot}/home/updatePLEX/"><i class="menu-icon-backlog-view"></i> Update PLEX</a></li> + % endif + % if sickbeard.USE_KODI and sickbeard.KODI_HOST != "": + <li><a href="${sbRoot}/home/updateKODI/"><i class="menu-icon-kodi"></i> Update KODI</a></li> + % endif + % if sickbeard.USE_EMBY and sickbeard.EMBY_HOST != "" and sickbeard.EMBY_APIKEY != "": + <li><a href="${sbRoot}/home/updateEMBY/"><i class="menu-icon-backlog-view"></i> Update Emby</a></li> + % endif + % 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> Manage Torrents</a></li> + % endif + % if sickbeard.USE_FAILED_DOWNLOADS: + <li><a href="${sbRoot}/manage/failedDownloads/"><i class="menu-icon-failed-download"></i> Failed Downloads</a></li> + % endif + % if sickbeard.USE_SUBTITLES: + <li><a href="${sbRoot}/manage/subtitleMissed/"><i class="menu-icon-backlog"></i> Missed Subtitle Management</a></li> + % endif + </ul> + </li> + + <li id="NAVerrorlogs" class="dropdown" ${('', 'class="active"')[topmenu == 'errorlogs']}> + <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> View Log (Errors)</a></li> + <li><a href="${sbRoot}/errorlogs/viewlog/"><i class="menu-icon-viewlog"></i> View Log</a></li> + </ul> + </li> + + <li id="NAVconfig" class="dropdown" ${('', 'class="active"')[topmenu == 'config']}> + <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> Help & Info</a></li> + <li><a href="${sbRoot}/config/general/"><i class="menu-icon-config"></i> General</a></li> + <li><a href="${sbRoot}/config/backuprestore/"><i class="menu-icon-config"></i> Backup & Restore</a></li> + <li><a href="${sbRoot}/config/search/"><i class="menu-icon-config"></i> Search Settings</a></li> + <li><a href="${sbRoot}/config/providers/"><i class="menu-icon-config"></i> Search Providers</a></li> + <li><a href="${sbRoot}/config/subtitles/"><i class="menu-icon-config"></i> Subtitles Settings</a></li> + <li><a href="${sbRoot}/config/postProcessing/"><i class="menu-icon-config"></i> Post Processing</a></li> + <li><a href="${sbRoot}/config/notifications/"><i class="menu-icon-config"></i> Notifications</a></li> + <li><a href="${sbRoot}/config/anime/"><i class="menu-icon-config"></i> 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"> + <li><a href="${sbRoot}/home/updateCheck?pid=${sbPID}"><i class="menu-icon-update"></i> Check For Updates</a></li> + <li><a href="${sbRoot}/changes"><i class="menu-icon-help"></i> Changelog</a></li> + <li><a href="${sbRoot}/home/restart/?pid=${sbPID}" class="confirm restart"><i class="menu-icon-restart"></i> Restart</a></li> + <li><a href="${sbRoot}/home/shutdown/?pid=${sbPID}" class="confirm shutdown"><i class="menu-icon-shutdown"></i> Shutdown</a></li> + <li><a href="${sbRoot}/logout" class="confirm logout"><i class="menu-icon-shutdown"></i> Logout</a></li> + <li><a href="${sbRoot}/home/status/"><i class="menu-icon-help"></i> Server Status</a></li> + </ul> + </li> + <li id="donate"><a href="https://github.com/SiCKRAGETV/SickRage/wiki/Donations" rel="noreferrer" onclick="window.open('${sickbeard.ANON_REDIRECT}' + this.href); return false;"><img src="${sbRoot}/images/donate.jpg" alt="[donate]" class="navbaricon hidden-xs" /></a></li> + </ul> + % endif + </div><!-- /.navbar-collapse --> + </div><!-- /.container-fluid --> + </nav> + + % if not submenu is UNDEFINED: + <div id="SubMenu" class="hidden-print"> + <span> + <% first = True %> + % for menuItem in submenu: + % if 'requires' not in menuItem or menuItem['requires']: + <% icon_class = '' if 'icon' not in menuItem else ' ' + menuItem['icon'] %> + % if type(menuItem['path']) == dict: + ${("</span><span>", "")[bool(first)]}<b>${menuItem['title']}</b> + <% + first = False + inner_first = True + %> + % for cur_link in menuItem['path']: + ${("· ", "")[bool(inner_first)]}<a class="inner" href="${sbRoot}/${menuItem['path'][cur_link]}">${cur_link}</a> + <% inner_first = False %> + % endfor + % else: + <a href="${sbRoot}/${menuItem['path']}" class="btn${('', ' confirm')['confirm' in menuItem]}">${('', '<span class="pull-left ' + icon_class + '"></span> ')[bool(icon_class)]}${menuItem['title']}</a> + <% first = False %> + % endif + % endif + % endfor + </span> + </div> + % endif + + % if sickbeard.BRANCH and sickbeard.BRANCH != 'master' and not sickbeard.DEVELOPER and sbLogin: + <div class="alert alert-danger upgrade-notification hidden-print" role="alert"> + <span>You're using the ${sickbeard.BRANCH} branch. Please use 'master' unless specifically asked</span> + </div> + % endif + + % if sickbeard.NEWEST_VERSION_STRING and sbLogin: + <div class="alert alert-success upgrade-notification hidden-print" role="alert"> + <span>${sickbeard.NEWEST_VERSION_STRING}</span> + </div> + % endif + + <div id="contentWrapper"> + <div id="content"> + <%block name="content" /> + </div> <!-- /content --> + </div> <!-- /contentWrapper --> + % if sbLogin: + <footer> + <div class="footer clearfix"> + <% + myDB = db.DBConnection() + today = str(datetime.date.today().toordinal()) + status_quality = '(%s)' % ','.join([str(quality) for quality in Quality.SNATCHED + Quality.SNATCHED_PROPER]) + status_download = '(%s)' % ','.join([str(quality) for quality in Quality.DOWNLOADED + [ARCHIVED]]) + + sql_statement = 'SELECT ' \ + + '(SELECT COUNT(*) FROM tv_episodes WHERE season > 0 AND episode > 0 AND airdate > 1 AND status IN %s) AS ep_snatched, ' % status_quality \ + + '(SELECT COUNT(*) FROM tv_episodes WHERE season > 0 AND episode > 0 AND airdate > 1 AND status IN %s) AS ep_downloaded, ' % status_download \ + + '(SELECT COUNT(*) FROM tv_episodes WHERE season > 0 AND episode > 0 AND airdate > 1 AND ((airdate <= %s AND (status = %s OR status = %s)) ' % (today, str(SKIPPED), str(WANTED)) \ + + ' OR (status IN %s) OR (status IN %s))) AS ep_total FROM tv_episodes tv_eps LIMIT 1' % (status_quality, status_download) + + sql_result = myDB.select(sql_statement) + + shows_total = len(sickbeard.showList) + shows_active = len([show for show in sickbeard.showList if show.paused == 0 and show.status == "Continuing"]) + + if sql_result: + ep_snatched = sql_result[0]['ep_snatched'] + ep_downloaded = sql_result[0]['ep_downloaded'] + ep_total = sql_result[0]['ep_total'] + else: + ep_snatched = 0 + ep_downloaded = 0 + ep_total = 0 + + ep_percentage = '' if ep_total == 0 else '(<span class="footerhighlight">%s%%</span>)' % re.sub(r'(\d+)(\.\d)\d+', r'\1\2', str((float(ep_downloaded)/float(ep_total))*100)) + + try: + localRoot = sbRoot + except NotFound: + localRoot = '' + + try: + localheader = header + except NotFound: + localheader = '' + %> + <span class="footerhighlight">${shows_total}</span> Shows (<span class="footerhighlight">${shows_active}</span> Active) + | <span class="footerhighlight">${ep_downloaded}</span> + + % if ep_snatched: + <span class="footerhighlight"><a href="${localRoot}/manage/episodeStatuses?whichStatus=2" title="View overview of snatched episodes">+${ep_snatched}</a></span> Snatched + % endif + + / <span class="footerhighlight">${ep_total}</span> Episodes Downloaded ${ep_percentage} + | Daily Search: <span class="footerhighlight">${str(sickbeard.dailySearchScheduler.timeLeft()).split('.')[0]}</span> + | Backlog Search: <span class="footerhighlight">${str(sickbeard.backlogSearchScheduler.timeLeft()).split('.')[0]}</span> + + % if not sickbeard.BRANCH or sickbeard.BRANCH != 'master' and sickbeard.DEVELOPER and sbLogin: + <div> + Memory used: <span class="footerhighlight">${sickbeard.helpers.pretty_filesize(resource.getrusage(resource.RUSAGE_SELF).ru_maxrss)}</span> | + Load time: <span class="footerhighlight">${"%.4f" % (time() - sbStartTime)}s</span> / Mako: <span class="footerhighlight">${"%.4f" % (time() - makoStartTime)}s</span> | + Branch: <span class="footerhighlight">${sickbeard.BRANCH}</span> + </div> + % endif + </div> + </footer> + <script type="text/javascript" src="${sbRoot}/js/lib/jquery-1.11.2.min.js?${sbPID}"></script> + <script type="text/javascript" src="${sbRoot}/js/lib/bootstrap.min.js?${sbPID}"></script> + <script type="text/javascript" src="${sbRoot}/js/lib/bootstrap-hover-dropdown.min.js?${sbPID}"></script> + <script type="text/javascript" src="${sbRoot}/js/lib/jquery-ui-1.10.4.custom.min.js?${sbPID}"></script> + <script type="text/javascript" src="${sbRoot}/js/lib/jquery.cookie.js?${sbPID}"></script> + <script type="text/javascript" src="${sbRoot}/js/lib/jquery.cookiejar.js?${sbPID}"></script> + <script type="text/javascript" src="${sbRoot}/js/lib/jquery.json-2.2.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.widgets-2.17.7.min.js?${sbPID}"></script> + <script type="text/javascript" src="${sbRoot}/js/lib/jquery.tablesorter.widget-columnSelector-2.17.7.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/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/isotope.pkgd.min.js?${sbPID}"></script> + <script type="text/javascript" src="${sbRoot}/js/lib/jquery.confirm.js?${sbPID}"></script> + <script type="text/javascript" src="${sbRoot}/js/script.js?${sbPID}"></script> + + % if sickbeard.FUZZY_DATING: + <script type="text/javascript" src="${sbRoot}/js/moment/moment.min.js?${sbPID}"></script> + <script type="text/javascript" src="${sbRoot}/js/fuzzyMoment.js?${sbPID}"></script> + % endif + <script type="text/javascript"> + sbRoot = '${sbRoot}'; // needed for browser.js & ajaxNotifications.js + //HTML for scrolltopcontrol, which is auto wrapped in DIV w/ ID="topcontrol" + top_image_html = '<img src="${sbRoot}/images/top.gif" width="31" height="11" alt="Jump to top" />'; + themeSpinner = '${('', '-dark')[sickbeard.THEME_NAME == 'dark']}'; + anonURL = '${sickbeard.ANON_REDIRECT}'; + </script> + <script type="text/javascript" src="${sbRoot}/js/lib/jquery.scrolltopcontrol-1.1.js"></script> + <script type="text/javascript" src="${sbRoot}/js/browser.js"></script> + <script type="text/javascript" src="${sbRoot}/js/ajaxNotifications.js"></script> + <script type="text/javascript" src="${sbRoot}/js/confirmations.js?${sbPID}"></script> + % endif + <script type="text/javascript"> + $(document).ready(function() { + $('.dropdown-toggle').dropdownHover(); + }); + </script> + <%block name="scripts" /> + </body> +</html> diff --git a/gui/slick/interfaces/default/login.mako b/gui/slick/views/login.mako similarity index 90% rename from gui/slick/interfaces/default/login.mako rename to gui/slick/views/login.mako index 011c9baee8a314e92743a7013148fe3fa8499afc..57a20f0cc9a353810446bacf5db65fd484b7c9f1 100644 --- a/gui/slick/interfaces/default/login.mako +++ b/gui/slick/views/login.mako @@ -1,4 +1,5 @@ -<%include file="/inc_top.mako"/> +<%inherit file="/layouts/main.mako"/> +<%block name="content"> <div class="login"> <form action="" method="post"> <h1>SickRage</h1> @@ -10,3 +11,4 @@ </div> </form> </div> +</%block> diff --git a/gui/slick/interfaces/default/manage.mako b/gui/slick/views/manage.mako similarity index 98% rename from gui/slick/interfaces/default/manage.mako rename to gui/slick/views/manage.mako index a444883f4ade65a635a27c1d55ee919e0d847779..eada903eecda9e518b8a5c86f1e70a183a3ae576 100644 --- a/gui/slick/interfaces/default/manage.mako +++ b/gui/slick/views/manage.mako @@ -1,10 +1,10 @@ +<%inherit file="/layouts/main.mako"/> <%! import sickbeard from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED from sickbeard.common import statusStrings %> -<%namespace file="/inc_defs.mako" import="renderQualityPill"/> -<%include file="/inc_top.mako"/> +<%block name="scripts"> <script type="text/javascript" src="${sbRoot}/js/lib/bootbox.min.js?${sbPID}"></script> <script type="text/javascript" charset="utf-8"> $.tablesorter.addParser({ @@ -32,8 +32,7 @@ $.tablesorter.addParser({ type: 'numeric' }); -$(document).ready(function() -{ +$(document).ready(function(){ $("#massUpdateTable:has(tbody tr)").tablesorter({ sortList: [[1,0]], textExtraction: { @@ -70,6 +69,9 @@ $(document).ready(function() }); </script> <script type="text/javascript" src="${sbRoot}/js/massUpdate.js?${sbPID}"></script> +</%block> +<%block name="content"> +<%namespace file="/inc_defs.mako" import="renderQualityPill"/> % if not header is UNDEFINED: <h1 class="header">${header}</h1> % else: @@ -181,11 +183,7 @@ $(document).ready(function() </tr> % endfor - </tbody> - </table> - </form> - -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/manage_backlogOverview.mako b/gui/slick/views/manage_backlogOverview.mako similarity index 96% rename from gui/slick/interfaces/default/manage_backlogOverview.mako rename to gui/slick/views/manage_backlogOverview.mako index 5ff215cc29eb06f5b6345967feac8c8501a2d485..4065b16b61d4dbf01f1b4d1316307896dddb007b 100644 --- a/gui/slick/interfaces/default/manage_backlogOverview.mako +++ b/gui/slick/views/manage_backlogOverview.mako @@ -1,11 +1,14 @@ -<% +<%inherit file="/layouts/main.mako"/> +<%! import sickbeard import datetime from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED from sickbeard.common import Overview, Quality, qualityPresets, qualityPresetStrings from sickbeard import sbdatetime, network_timezones + + fuzzydate = 'airdate' %> -<%include file="/inc_top.mako"/> +<%block name="scripts"> <script type="text/javascript"> $(document).ready(function(){ $('#pickShow').change(function(){ @@ -15,7 +18,6 @@ $(document).ready(function(){ } }); - <% fuzzydate = 'airdate' %> % if sickbeard.FUZZY_DATING: fuzzyMoment({ containerClass : '.${fuzzydate}', @@ -28,7 +30,8 @@ $(document).ready(function(){ }); </script> - +</%block> +<%block name="content"> <div id="content960"> % if not header is UNDEFINED: @@ -104,4 +107,4 @@ Jump to Show </table> </div> -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/manage_episodeStatuses.mako b/gui/slick/views/manage_episodeStatuses.mako similarity index 85% rename from gui/slick/interfaces/default/manage_episodeStatuses.mako rename to gui/slick/views/manage_episodeStatuses.mako index 2b0d025f9f3d0946be50560c0d7d638e4762709c..1b67d3cf86daca55d856bf7ed5d2ed24c4df8509 100644 --- a/gui/slick/interfaces/default/manage_episodeStatuses.mako +++ b/gui/slick/views/manage_episodeStatuses.mako @@ -1,10 +1,10 @@ +<%inherit file="/layouts/main.mako"/> <%! from sickbeard import common + import sickbeard %> -<%include file="/inc_top.mako"/> - +<%block name="content"> <div id="content960"> - % if not header is UNDEFINED: <h1 class="header">${header}</h1> % else: @@ -20,7 +20,7 @@ <form action="${sbRoot}/manage/episodeStatuses" method="get"> Manage episodes with status <select name="whichStatus" class="form-control form-control-inline input-sm"> -% for curStatus in [common.SKIPPED, common.SNATCHED, common.WANTED, common.ARCHIVED, common.IGNORED]: +% for curStatus in [common.SKIPPED, common.SNATCHED, common.WANTED, common.ARCHIVED, common.IGNORED] + common.Quality.ARCHIVED: <option value="${curStatus}">${common.statusStrings[curStatus]}</option> % endfor </select> @@ -39,7 +39,7 @@ Manage episodes with status <select name="whichStatus" class="form-control form- <br /> <% - if int(whichStatus) in [common.ARCHIVED, common.IGNORED, common.SNATCHED]: + if int(whichStatus) in [common.IGNORED, common.SNATCHED] + common.Quality.ARCHIVED: row_class = "good" else: row_class = common.Overview.overviewStrings[int(whichStatus)] @@ -49,11 +49,11 @@ Manage episodes with status <select name="whichStatus" class="form-control form- Set checked shows/episodes to <select name="newStatus" class="form-control form-control-inline input-sm"> <% - statusList = [common.SKIPPED, common.WANTED, common.ARCHIVED, common.IGNORED] + statusList = [common.SKIPPED, common.WANTED, common.IGNORED] + common.Quality.ARCHIVED if int(whichStatus) in statusList: statusList.remove(int(whichStatus)) - if int(whichStatus) in [common.SNATCHED, common.SNATCHED_PROPER, common.SNATCHED_BEST]: + if int(whichStatus) in [common.SNATCHED, common.SNATCHED_PROPER, common.SNATCHED_BEST] + common.Quality.DOWNLOADED and sickbeard.USE_FAILED_DOWNLOADS: statusList.append(common.FAILED) %> @@ -83,5 +83,4 @@ Set checked shows/episodes to <select name="newStatus" class="form-control form- % endif </div> - -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/manage_failedDownloads.mako b/gui/slick/views/manage_failedDownloads.mako similarity index 96% rename from gui/slick/interfaces/default/manage_failedDownloads.mako rename to gui/slick/views/manage_failedDownloads.mako index 6adde96bab4941a3cbb4730a22747625c2995bee..b7149ccf7a1a0a8974699503adc4e3120b7b491d 100644 --- a/gui/slick/interfaces/default/manage_failedDownloads.mako +++ b/gui/slick/views/manage_failedDownloads.mako @@ -1,3 +1,4 @@ +<%inherit file="/layouts/main.mako"/> <%! import sickbeard import os.path @@ -8,7 +9,7 @@ from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED from sickbeard.common import Quality, qualityPresets, qualityPresetStrings, statusStrings, Overview %> -<%include file="/inc_top.mako"/> +<%block name="scripts"> <script type="text/javascript"> $(document).ready(function(){ $("#failedTable:has(tbody tr)").tablesorter({ @@ -23,7 +24,8 @@ $(document).ready(function(){ }); </script> <script type="text/javascript" src="${sbRoot}/js/failedDownloads.js?${sbPID}"></script> - +</%block> +<%block name="content"> % if not header is UNDEFINED: <h1 class="header">${header}</h1> % else: @@ -80,4 +82,4 @@ $(document).ready(function(){ % endfor </tbody> </table> -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/manage_manageSearches.mako b/gui/slick/views/manage_manageSearches.mako similarity index 94% rename from gui/slick/interfaces/default/manage_manageSearches.mako rename to gui/slick/views/manage_manageSearches.mako index 030baefaa4b14cf1ed826c3ec8d9fc7538277369..42039f29c8d1a43db93ebf407944c8b9bc021458 100644 --- a/gui/slick/interfaces/default/manage_manageSearches.mako +++ b/gui/slick/views/manage_manageSearches.mako @@ -1,11 +1,14 @@ +<%inherit file="/layouts/main.mako"/> <%! import sickbeard import datetime from sickbeard.common import SKIPPED, WANTED, UNAIRED, ARCHIVED, IGNORED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, FAILED from sickbeard.common import Quality, qualityPresets, statusStrings, qualityPresetStrings, cpu_presets %> -<%include file="/inc_top.mako"/> +<%block name="scripts"> <script type="text/javascript" src="${sbRoot}/js/plotTooltip.js?${sbPID}"></script> +</%block> +<%block name="content"> <div id="content800"> % if not header is UNDEFINED: <h1 class="header">${header}</h1> @@ -46,4 +49,4 @@ Manual: <i>${queueLength['manual']} pending items</i></br> Failed: <i>${queueLength['failed']} pending items</i></br> </div> </div> -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/manage_massEdit.mako b/gui/slick/views/manage_massEdit.mako similarity index 98% rename from gui/slick/interfaces/default/manage_massEdit.mako rename to gui/slick/views/manage_massEdit.mako index 26e2df65cc2f3619824c2ac617123f96838f9ffd..d116ecf667883f3b0fa68b47fb126d5f4c354409 100644 --- a/gui/slick/interfaces/default/manage_massEdit.mako +++ b/gui/slick/views/manage_massEdit.mako @@ -1,3 +1,4 @@ +<%inherit file="/layouts/main.mako"/> <%! import sickbeard from sickbeard import common @@ -11,10 +12,14 @@ <% initial_quality = common.SD %> % endif <% anyQualities, bestQualities = common.Quality.splitQuality(initial_quality) %> -<%include file="/inc_top.mako"/> +<%block name="scripts"> <script type="text/javascript" src="${sbRoot}/js/qualityChooser.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/massEdit.js?${sbPID}"></script> - +<script type="text/javascript" charset="utf-8"> + $('#location').fileBrowser({ title: 'Select Show Location' }); +</script> +</%block> +<%block name="content"> <form action="massEditSubmit" method="post"> <input type="hidden" name="toEdit" value="${showList}" /> @@ -189,8 +194,4 @@ </form> <br /> - -<script type="text/javascript" charset="utf-8"> - $('#location').fileBrowser({ title: 'Select Show Location' }); -</script> -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/manage_subtitleMissed.mako b/gui/slick/views/manage_subtitleMissed.mako similarity index 95% rename from gui/slick/interfaces/default/manage_subtitleMissed.mako rename to gui/slick/views/manage_subtitleMissed.mako index 3874943f0a80e2f2828b4a501d55b4f38e8f693c..8a43b58d897f1b68c72e7341f09572bab9c2ae71 100644 --- a/gui/slick/interfaces/default/manage_subtitleMissed.mako +++ b/gui/slick/views/manage_subtitleMissed.mako @@ -1,10 +1,14 @@ +<%inherit file="/layouts/main.mako"/> <%! from sickbeard import subtitles import datetime import sickbeard from sickbeard import common %> -<%include file="/inc_top.mako"/> +<%block name="scripts"> +<script type="text/javascript" src="${sbRoot}/js/manageSubtitleMissed.js?${sbPID}"></script> +</%block> +<%block name="content"> <div id="content960"> % if not header is UNDEFINED: <h1 class="header">${header}</h1> @@ -34,8 +38,6 @@ subtitles </form> % else: - -<script type="text/javascript" src="${sbRoot}/js/manageSubtitleMissed.js?${sbPID}"></script> <input type="hidden" id="selectSubLang" name="selectSubLang" value="${whichSubs}" /> <form action="${sbRoot}/manage/downloadSubtitleMissed" method="post"> @@ -59,4 +61,4 @@ Download missed subtitles for selected episodes <input class="btn btn-inline" ty % endif </div> -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/manage_torrents.mako b/gui/slick/views/manage_torrents.mako similarity index 77% rename from gui/slick/interfaces/default/manage_torrents.mako rename to gui/slick/views/manage_torrents.mako index f4800653e916642807d05ce4682cb9acd00d45d7..4754c8cf968321f7a2d22eba1e38565c58364c6b 100644 --- a/gui/slick/interfaces/default/manage_torrents.mako +++ b/gui/slick/views/manage_torrents.mako @@ -1,5 +1,8 @@ -<%include file="/inc_top.mako"/> +<%inherit file="/layouts/main.mako"/> +<%block name="scripts"> <script type="text/javascript" src="${sbRoot}/js/plotTooltip.js?${sbPID}"></script> +</%block> +<%block name="content"> % if not header is UNDEFINED: <h1 class="header">${header}</h1> % else: @@ -8,5 +11,4 @@ ${info_download_station} <iframe id="extFrame" src="${webui_url}" width="100%" height="500" frameBorder="0" style="border: 1px black solid;"></iframe> - -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/views/markdown.mako b/gui/slick/views/markdown.mako new file mode 100644 index 0000000000000000000000000000000000000000..2ea437ad44983ccb6f4ba68b593a2f817567d76a --- /dev/null +++ b/gui/slick/views/markdown.mako @@ -0,0 +1,4 @@ +<%inherit file="/layouts/main.mako"/> +<%block name="content"> +${data} +</%block> diff --git a/gui/slick/interfaces/default/restart_bare.mako b/gui/slick/views/restart.mako similarity index 78% rename from gui/slick/interfaces/default/restart_bare.mako rename to gui/slick/views/restart.mako index f58307f771d46d8d1c788d9aef1a5c21af73465a..d072681a4e96dc2ce75081f18ea02f0d57d1324d 100644 --- a/gui/slick/interfaces/default/restart_bare.mako +++ b/gui/slick/views/restart.mako @@ -1,3 +1,5 @@ +<%inherit file="/layouts/main.mako"/> +<%block name="scripts"> <% try: curSBHost = sbHost @@ -20,10 +22,24 @@ sbHandleReverseProxy = "${curSBHandleReverseProxy}"; sbHost = "${curSBHost}"; sbDefaultPage = "${sbDefaultPage}"; </script> - <script type="text/javascript" src="${sbRoot}/js/lib/jquery-1.11.2.min.js?${sbPID}"></script> <script type="text/javascript" src="${sbRoot}/js/restart.js?${sbPID}&${sbDefaultPage}"></script> - +</%block> +<%block name="content"> +<% + try: + curSBHost = sbHost + curSBHttpPort = sbHttpPort + curSBHttpsEnabled = sbHttpsEnabled + curSBHandleReverseProxy = sbHandleReverseProxy + themeSpinner = sbThemeName + except NameMapper.NotFound: + curSBHost = "localhost" + curSBHttpPort = sickbeard.WEB_PORT + curSBHttpsEnabled = "False" + curSBHandleReverseProxy = "False" + themeSpinner = sickbeard.THEME_NAME +%> <% themeSpinner = ('', '-dark')['dark' == themeSpinner] %> <h2>Performing Restart</h2> <br /> @@ -48,3 +64,4 @@ Loading the default page: <div id="restart_fail_message" style="display: none;"> Error: The restart has timed out, perhaps something prevented SickRage from starting again? </div> +</%block> diff --git a/gui/slick/interfaces/default/status.mako b/gui/slick/views/status.mako similarity index 98% rename from gui/slick/interfaces/default/status.mako rename to gui/slick/views/status.mako index a49750673935d11e4eba78ae890095baf4f15813..9ef509c4b0901ad8d6e04b61d32132df9658143b 100644 --- a/gui/slick/interfaces/default/status.mako +++ b/gui/slick/views/status.mako @@ -1,10 +1,23 @@ +<%inherit file="/layouts/main.mako"/> <%! import sickbeard from sickbeard import helpers from sickbeard.show_queue import ShowQueueActions %> -<%include file="/inc_top.mako"/> - +<%block name="scripts"> +<script type="text/javascript"> + $(document).ready(function() { + $("#schedulerStatusTable").tablesorter({ + widgets: ['saveSort', 'zebra'] + }); + $("#queueStatusTable").tablesorter({ + widgets: ['saveSort', 'zebra'], + sortList: [[3,0], [4,0], [2,1]] + }); + }); +</script> +</%block> +<%block name="content"> % if not header is UNDEFINED: <h1 class="header">${header}</h1> % else: @@ -25,21 +38,6 @@ 'Trakt Checker': 'traktCheckerScheduler', } %> - -<script type="text/javascript"> - $(document).ready(function() { - $("#schedulerStatusTable").tablesorter({ - widgets: ['saveSort', 'zebra'] - }); - }); - $(document).ready(function() { - $("#queueStatusTable").tablesorter({ - widgets: ['saveSort', 'zebra'], - sortList: [[3,0], [4,0], [2,1]] - }); - }); -</script> - <div id="config-content"> <h2 class="header">Scheduler</h2> <table id="schedulerStatusTable" class="tablesorter" width="100%"> @@ -104,7 +102,7 @@ <td align="right">${service.start_time}</td> % else: <td align="right"></td> - % endif + % endif <% cycleTime = (service.cycleTime.microseconds + (service.cycleTime.seconds + service.cycleTime.days * 24 * 3600) * 10**6) / 10**6 %> <td align="right">${helpers.pretty_time_delta(cycleTime)}</td> % if service.enable: @@ -215,7 +213,7 @@ <td>${sickbeard.TV_DOWNLOAD_DIR}</td> <td>${tvdirFree} MB</td> </tr> - % endif + % endif <tr> <td rowspan=${len(rootDir)}>Media Root Directories</td> % for cur_dir in rootDir: @@ -226,3 +224,4 @@ </tbody> </table> </div> +</%block> diff --git a/gui/slick/interfaces/default/testRename.mako b/gui/slick/views/testRename.mako similarity index 97% rename from gui/slick/interfaces/default/testRename.mako rename to gui/slick/views/testRename.mako index 51e280d1c670b0bd2599ba1baac957ef6c6dfabe..08539495e69789a1f686d4bb661bee4cf417023d 100644 --- a/gui/slick/interfaces/default/testRename.mako +++ b/gui/slick/views/testRename.mako @@ -1,3 +1,4 @@ +<%inherit file="/layouts/main.mako"/> <%! import sickbeard import calendar @@ -7,7 +8,10 @@ import datetime import re %> -<%include file="/inc_top.mako"/> +<%block name="scripts"> +<script type="text/javascript" src="${sbRoot}/js/testRename.js"></script> +</%block> +<%block name="content"> % if not header is UNDEFINED: <h1 class="header">${header}</h1> % else: @@ -16,8 +20,6 @@ <input type="hidden" id="showID" value="${show.indexerid}" /> -<script type="text/javascript" src="${sbRoot}/js/testRename.js"></script> - <h3>Preview of the proposed name changes</h3> <blockquote> % if int(show.air_by_date) == 1 and sickbeard.NAMING_CUSTOM_ABD: @@ -98,4 +100,4 @@ if len(epList) > 1: % endfor </table><br /> <input type="submit" value="Rename Selected" class="btn btn-success"> <a href="/home/displayShow?show=${show.indexerid}" class="btn btn-danger">Cancel Rename</a> -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/gui/slick/interfaces/default/trendingShows.mako b/gui/slick/views/trendingShows.mako similarity index 92% rename from gui/slick/interfaces/default/trendingShows.mako rename to gui/slick/views/trendingShows.mako index ce1cc030347d1d665569f48da19c3161a7e590a8..6db8fe07f6ac6b50a11e3daf3c74f3af64c56e57 100644 --- a/gui/slick/interfaces/default/trendingShows.mako +++ b/gui/slick/views/trendingShows.mako @@ -1,3 +1,4 @@ +<%inherit file="/layouts/main.mako"/> <%! import sickbeard import datetime @@ -7,8 +8,8 @@ from sickbeard import sbdatetime from sickbeard.helpers import anon_url %> - -<script type="text/javascript" charset="utf-8"> +<%block name="scripts"> +<script type="text/javascript"> $(document).ready(function(){ // initialise combos for dirty page refreshes $('#showsort').val('original'); @@ -65,7 +66,8 @@ $(document).ready(function(){ }); }); </script> - +<%block> +<%block name="content"> <div id="container"> % if not trending_shows: <div class="trakt_show" style="width:100%; margin-top:20px"> @@ -89,10 +91,10 @@ $(document).ready(function(){ <p>${int(cur_show['show']['rating']*10)}% <img src="${sbRoot}/images/heart.png"></p> <i>${cur_show['show']['votes']} votes</i> <div class="traktShowTitleIcons"> - <a href="${sbRoot}/home/addShows/addTraktShow?indexer_id=${cur_show['show']['ids']['tvdb'] or cur_show['show']['ids']['tvrage']}&showName=${cur_show['show']['title']}" class="btn btn-xs">Add Show</a> -% if blacklist: + <a href="${sbRoot}/home/addShows/addTraktShow?indexer_id=${cur_show['show']['ids']['tvdb']}&showName=${cur_show['show']['title']}" class="btn btn-xs">Add Show</a> + % if blacklist: <a href="${sbRoot}/home/addShows/addShowToBlacklist?indexer_id=${cur_show['show']['ids']['tvdb'] or cur_show['show']['ids']['tvrage']}" class="btn btn-xs">Remove Show</a> -% endif + % endif </div> </div> </div> @@ -100,3 +102,4 @@ $(document).ready(function(){ % endfor % endif </div> +</%block> diff --git a/gui/slick/interfaces/default/viewlogs.mako b/gui/slick/views/viewlogs.mako similarity index 82% rename from gui/slick/interfaces/default/viewlogs.mako rename to gui/slick/views/viewlogs.mako index b4f107846b164753297f8f22b51714a989cf719c..26e0a720ced0cf145203b36fd95afae5bdc9e086 100644 --- a/gui/slick/interfaces/default/viewlogs.mako +++ b/gui/slick/views/viewlogs.mako @@ -1,30 +1,32 @@ -<%include file="/inc_top.mako"/> +<%inherit file="/layouts/main.mako"/> <%! import sickbeard from sickbeard import classes from sickbeard.logger import reverseNames %> -<script type="text/javascript" charset="utf-8"> +<%block name="scripts"> +<script type="text/javascript"> $(document).ready( - function(){ - $('#minLevel,#logFilter,#logSearch').change(function(){ - if ( $('#logSearch').val().length > 0 ) { + $('#minLevel,#logFilter,#logSearch').on('change', function(){ + if ($('#logSearch').val().length > 0){ $('#logSearch').prop('disabled', true); - $('#logFilter option[value=\\<NONE\\>]').prop('selected', true); + $('#logFilter option[value="<NONE>"]').prop('selected', true); $('#minLevel option[value=5]').prop('selected', true); } $('#minLevel').prop('disabled', true); $('#logFilter').prop('disabled', true); $('#logSearch').prop('disabled', true); - document.body.style.cursor='wait' url = '${sbRoot}/errorlogs/viewlog/?minLevel='+$('select[name=minLevel]').val()+'&logFilter='+$('select[name=logFilter]').val()+'&logSearch='+$('#logSearch').val() - window.location.href = url - + $.get(url, function(data){ + $('pre').html($(data).find('pre').html()); + $('#minLevel').removeProp('disabled'); + $('#logFilter').removeProp('disabled'); + $('#logSearch').removeProp('disabled'); + }); }); $(window).load(function(){ - if ( $('#logSearch').val().length == 0 ) { $('#minLevel').prop('disabled', false); $('#logFilter').prop('disabled', false); @@ -35,7 +37,7 @@ function(){ $('#logSearch').prop('disabled', false); } - document.body.style.cursor='default'; + document.body.style.cursor='default'; }); $('#logSearch').keyup(function() { @@ -52,8 +54,10 @@ function(){ } }); }); +window.setInterval( "location.reload(true)", 600000); // Refresh every 10 minutes </script> - +</%block> +<%block name="content"> % if not header is UNDEFINED: <h1 class="header">${header}</h1> % else: @@ -85,8 +89,4 @@ ${logLines} </pre> </div> <br /> -<script type="text/javascript" charset="utf-8"> -window.setInterval( "location.reload(true)", 600000); // Refresh every 10 minutes -</script> - -<%include file="/inc_bottom.mako"/> +</%block> diff --git a/lib/guessit/__version__.py b/lib/guessit/__version__.py index e24d8dea4851f5714d50f0c877d4b99f33108f6c..e841082f9a196a6d78b3b8cc54a8bbaba001f425 100644 --- a/lib/guessit/__version__.py +++ b/lib/guessit/__version__.py @@ -17,4 +17,4 @@ # You should have received a copy of the Lesser GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # -__version__ = '0.11.0.dev0' +__version__ = '0.11.1.dev0' diff --git a/lib/guessit/containers.py b/lib/guessit/containers.py index 30d7871fa15cfa888048bdb1f5415955c3df7abb..6e37020e55a9de977bebab81d320564df21f1577 100644 --- a/lib/guessit/containers.py +++ b/lib/guessit/containers.py @@ -135,8 +135,14 @@ class SameKeyValidator(object): self.validator_function = validator_function def validate(self, prop, string, node, match, entry_start, entry_end): + path_nodes = [path_node for path_node in node.ancestors if path_node.category == 'path'] + if path_nodes: + path_node = path_nodes[0] + else: + path_node = node.root + for key in prop.keys: - for same_value_leaf in node.root.leaves_containing(key): + for same_value_leaf in path_node.leaves_containing(key): ret = self.validator_function(same_value_leaf, key, prop, string, node, match, entry_start, entry_end) if ret is not None: return ret @@ -144,6 +150,9 @@ class SameKeyValidator(object): class OnlyOneValidator(SameKeyValidator): + """ + Check that there's only one occurence of key for current directory + """ def __init__(self): super(OnlyOneValidator, self).__init__(lambda same_value_leaf, key, prop, string, node, match, entry_start, entry_end: False) diff --git a/lib/guessit/guess.py b/lib/guessit/guess.py index 133c1769d3954a28c2e980cf0a4a2839fb0a0ed4..1eeb3554789b36ced0caa34479ad1546b0698833 100644 --- a/lib/guessit/guess.py +++ b/lib/guessit/guess.py @@ -287,10 +287,10 @@ def choose_int(g1, g2): if v1 == v2: return v1, 1 - (1 - c1) * (1 - c2) else: - if c1 > c2: - return v1, c1 - c2 + if c1 >= c2: + return v1, c1 - c2 / 2 else: - return v2, c2 - c1 + return v2, c2 - c1 / 2 def choose_string(g1, g2): @@ -308,7 +308,7 @@ def choose_string(g1, g2): prepended to it. >>> s(choose_string(('Hello', 0.75), ('World', 0.5))) - ('Hello', 0.25) + ('Hello', 0.5) >>> s(choose_string(('Hello', 0.5), ('hello', 0.5))) ('Hello', 0.75) @@ -354,10 +354,10 @@ def choose_string(g1, g2): # in case of conflict, return the one with highest confidence else: - if c1 > c2: - return v1, c1 - c2 + if c1 >= c2: + return v1, c1 - c2 / 2 else: - return v2, c2 - c1 + return v2, c2 - c1 / 2 def _merge_similar_guesses_nocheck(guesses, prop, choose): @@ -474,8 +474,8 @@ def merge_all(guesses, append=None): # delete very unlikely values for p in list(result.keys()): - if result.confidence(p) < 0.05: - del result[p] + if result.confidence(p) < 0.05: + del result[p] # make sure our appendable properties contain unique values for prop in append: @@ -503,13 +503,13 @@ def smart_merge(guesses): # 1- try to merge similar information together and give it a higher # confidence - for int_part in ('year', 'season'): + for int_part in ('year', 'season', 'episodeNumber'): merge_similar_guesses(guesses, int_part, choose_int) for string_part in ('title', 'series', 'container', 'format', 'releaseGroup', 'website', 'audioCodec', 'videoCodec', 'screenSize', 'episodeFormat', - 'audioChannels', 'idNumber'): + 'audioChannels', 'idNumber', 'container'): merge_similar_guesses(guesses, string_part, choose_string) # 2- merge the rest, potentially discarding information not properly diff --git a/lib/guessit/language.py b/lib/guessit/language.py index 0d7b6bc4cd130beab76d76615bd8121bd6107bad..d45a1ef23231c8671057db5d2ace1a72b57dd115 100644 --- a/lib/guessit/language.py +++ b/lib/guessit/language.py @@ -175,7 +175,7 @@ LNG_COMMON_WORDS = frozenset([ 'fry', 'cop', 'zen', 'gay', 'fat', 'one', 'cherokee', 'got', 'an', 'as', 'cat', 'her', 'be', 'hat', 'sun', 'may', 'my', 'mr', 'rum', 'pi', 'bb', 'bt', 'tv', 'aw', 'by', 'md', 'mp', 'cd', 'lt', 'gt', 'in', 'ad', 'ice', - 'ay', 'at', 'star', + 'ay', 'at', 'star', 'so', # french words 'bas', 'de', 'le', 'son', 'ne', 'ca', 'ce', 'et', 'que', 'mal', 'est', 'vol', 'or', 'mon', 'se', 'je', 'tu', 'me', @@ -210,6 +210,8 @@ subtitle_prefixes = ['sub', 'subs', 'st', 'vost', 'subforced', 'fansub', 'hardsu subtitle_suffixes = ['subforced', 'fansub', 'hardsub', 'sub', 'subs'] lang_prefixes = ['true'] +all_lang_prefixes_suffixes = subtitle_prefixes + subtitle_suffixes + lang_prefixes + def find_possible_languages(string, allowed_languages=None): """Find possible languages in the string diff --git a/lib/guessit/matcher.py b/lib/guessit/matcher.py index e1ed26f1b2fec36ea1c1653a7b3dc3d3027d3a4e..f27662b1811f6811c9fac66b422dacc567f79076 100644 --- a/lib/guessit/matcher.py +++ b/lib/guessit/matcher.py @@ -226,7 +226,12 @@ class GuessFinder(object): for node in nodes: self.process_node(node) - def process_node(self, node, iterative=True, partial_span=None): + def process_node(self, node, iterative=True, partial_span=None, skip_nodes=True): + if skip_nodes and not isinstance(skip_nodes, list): + skip_nodes = self.options.get('skip_nodes') + elif not isinstance(skip_nodes, list): + skip_nodes = [] + if partial_span: value = node.value[partial_span[0]:partial_span[1]] else: @@ -257,17 +262,29 @@ class GuessFinder(object): if partial_span: span = (span[0] + partial_span[0], span[1] + partial_span[0]) - skip_nodes = self.options.get('skip_nodes') if skip_nodes: + skip_nodes = [skip_node for skip_node in self.options.get('skip_nodes') if skip_node.parent.span[0] == node.span[0] or skip_node.parent.span[1] == node.span[1]] # if we guessed a node that we need to skip, recurse down the tree and ignore that node + indices = set() + skip_nodes_spans = [] + next_skip_nodes = [] for skip_node in skip_nodes: - skip_node_relative_span = (skip_node.span[0] - node.offset, skip_node.span[1] - node.offset) - if skip_node_relative_span == span: - partition_spans = [s for s in node.get_partition_spans(span) if s != skip_node.span] - for partition_span in partition_spans: - relative_span = (partition_span[0] - node.offset, partition_span[1] - node.offset) - self.process_node(node, partial_span=relative_span) - return + skip_for_next = False + skip_nodes_spans.append(skip_node.span) + if node.offset <= skip_node.span[0] <= node.span[1]: + indices.add(skip_node.span[0] - node.offset) + skip_for_next = True + if node.offset <= skip_node.span[1] <= node.span[1]: + indices.add(skip_node.span[1] - node.offset) + skip_for_next = True + if not skip_for_next: + next_skip_nodes.append(skip_node) + if indices: + partition_spans = [s for s in node.get_partition_spans(indices) if s not in skip_nodes_spans] + for partition_span in partition_spans: + relative_span = (partition_span[0] - node.offset, partition_span[1] - node.offset) + self.process_node(node, partial_span=relative_span, skip_nodes=next_skip_nodes) + return # restore sentinels compensation if isinstance(result, Guess): diff --git a/lib/guessit/matchtree.py b/lib/guessit/matchtree.py index dc4304ddebcbff186b4833f5f05d7a0432c0d723..3712f10cde777e38fad19eff83dc69656765347c 100644 --- a/lib/guessit/matchtree.py +++ b/lib/guessit/matchtree.py @@ -27,8 +27,7 @@ import guessit # @UnusedImport needed for doctests from guessit import UnicodeMixin, base_text_type from guessit.textutils import clean_default, str_fill from guessit.patterns import group_delimiters -from guessit.guess import (smart_merge, - Guess) +from guessit.guess import smart_merge, Guess log = logging.getLogger(__name__) diff --git a/lib/guessit/test/episodes.yaml b/lib/guessit/test/episodes.yaml index 55165ba3ceaa6e95e9671c2575cda3b4cd0acc26..dfab02b0cb8fe0d0bc0558082228c6cf1cfe9113 100644 --- a/lib/guessit/test/episodes.yaml +++ b/lib/guessit/test/episodes.yaml @@ -1773,10 +1773,10 @@ - 1 episodeNumber: 0 format: TV - subtitleLanguage: en releaseGroup: 2Maverick season: 14 series: Project Runway + subtitleLanguage: en videoCodec: h264 ? '[Hatsuyuki-Kaitou]_Fairy_Tail_2_-_16-20_[720p][10bit].torrent' @@ -1803,4 +1803,61 @@ episodeNumber: 16 releaseGroup: Hatsuyuki-Kaitou screenSize: 720p - series: Fairy Tail 2 \ No newline at end of file + series: Fairy Tail 2 + +? "Looney Tunes 1940x01 Porky's Last Stand.mkv" +: episodeNumber: 1 + season: 1940 + series: Looney Tunes + title: Porky's Last Stand + year: 1940 + +? The.Good.Wife.S06E01.E10.720p.WEB-DL.DD5.1.H.264-CtrlHD/The.Good.Wife.S06E09.Trust.Issues.720p.WEB-DL.DD5.1.H.264-CtrlHD.mkv +: audioChannels: '5.1' + audioCodec: DolbyDigital + episodeList: + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + episodeNumber: 9 + format: WEB-DL + releaseGroup: CtrlHD + screenSize: 720p + season: 6 + series: The Good Wife + title: Trust Issues + videoCodec: h264 + +? Fear the Walking Dead - 01x02 - So Close, Yet So Far.REPACK-KILLERS.French.C.updated.Addic7ed.com.mkv +: episodeNumber: 2 + language: fr + other: Proper + properCount: 1 + season: 1 + series: Fear the Walking Dead + title: So Close, Yet So Far + +? Fear the Walking Dead - 01x02 - En Close, Yet En Far.REPACK-KILLERS.French.C.updated.Addic7ed.com.mkv +: episodeNumber: 2 + language: fr + other: Proper + properCount: 1 + season: 1 + series: Fear the Walking Dead + title: En Close, Yet En Far + +? /av/unsorted/The.Daily.Show.2015.07.22.Jake.Gyllenhaal.720p.HDTV.x264-BATV.mkv +: date: 2015-07-22 + format: HDTV + releaseGroup: BATV + screenSize: 720p + series: The Daily Show + title: Jake Gyllenhaal + videoCodec: h264 diff --git a/lib/guessit/test/movies.yaml b/lib/guessit/test/movies.yaml index 75715c6ff1d7f20cafbc3e54e7bb8c90fd02dbd3..6255548d73d5edc9aa92b347b21a06bc7f0d1068 100644 --- a/lib/guessit/test/movies.yaml +++ b/lib/guessit/test/movies.yaml @@ -774,3 +774,6 @@ : title: 123 Angry Men year: 1957 +? "Looney Tunes 1444x866 Porky's Last Stand.mkv" +: screenSize: 1444x866 + title: Looney Tunes diff --git a/lib/guessit/transfo/guess_episode_info_from_position.py b/lib/guessit/transfo/guess_episode_info_from_position.py index 8706d0193941deb2d5b8aa732519f68d2d70c8c9..4ce6a1e88861d20721ac6d9b06e9482b8ae6d177 100644 --- a/lib/guessit/transfo/guess_episode_info_from_position.py +++ b/lib/guessit/transfo/guess_episode_info_from_position.py @@ -24,6 +24,8 @@ from guessit.plugins.transformers import Transformer, get_transformer from guessit.textutils import reorder_title from guessit.matcher import found_property +from guessit.patterns.list import all_separators +from guessit.language import all_lang_prefixes_suffixes class GuessEpisodeInfoFromPosition(Transformer): @@ -33,39 +35,49 @@ class GuessEpisodeInfoFromPosition(Transformer): def supported_properties(self): return ['title', 'series'] - def match_from_epnum_position(self, mtree, node, options): - epnum_idx = node.node_idx + @staticmethod + def excluded_word(*values): + for value in values: + if value.clean_value.lower() in (all_separators + all_lang_prefixes_suffixes): + return True + return False + + def match_from_epnum_position(self, path_node, ep_node, options): + epnum_idx = ep_node.node_idx # a few helper functions to be able to filter using high-level semantics def before_epnum_in_same_pathgroup(): - return [leaf for leaf in mtree.unidentified_leaves(lambda x: len(x.clean_value) > 1) + return [leaf for leaf in path_node.unidentified_leaves(lambda x: len(x.clean_value) > 1) if (leaf.node_idx[0] == epnum_idx[0] and - leaf.node_idx[1:] < epnum_idx[1:])] + leaf.node_idx[1:] < epnum_idx[1:] and + not GuessEpisodeInfoFromPosition.excluded_word(leaf))] def after_epnum_in_same_pathgroup(): - return [leaf for leaf in mtree.unidentified_leaves(lambda x: len(x.clean_value) > 1) + return [leaf for leaf in path_node.unidentified_leaves(lambda x: len(x.clean_value) > 1) if (leaf.node_idx[0] == epnum_idx[0] and - leaf.node_idx[1:] > epnum_idx[1:])] + leaf.node_idx[1:] > epnum_idx[1:] and + not GuessEpisodeInfoFromPosition.excluded_word(leaf))] def after_epnum_in_same_explicitgroup(): - return [leaf for leaf in mtree.unidentified_leaves(lambda x: len(x.clean_value) > 1) + return [leaf for leaf in path_node.unidentified_leaves(lambda x: len(x.clean_value) > 1) if (leaf.node_idx[:2] == epnum_idx[:2] and - leaf.node_idx[2:] > epnum_idx[2:])] + leaf.node_idx[2:] > epnum_idx[2:] and + not GuessEpisodeInfoFromPosition.excluded_word(leaf))] # epnumber is the first group and there are only 2 after it in same # path group # -> series title - episode title - title_candidates = self._filter_candidates(after_epnum_in_same_pathgroup(), options) + title_candidates = GuessEpisodeInfoFromPosition._filter_candidates(after_epnum_in_same_pathgroup(), options) - if ('title' not in mtree.info and # no title - 'series' in mtree.info and # series present + if ('title' not in path_node.info and # no title + 'series' in path_node.info and # series present before_epnum_in_same_pathgroup() == [] and # no groups before len(title_candidates) == 1): # only 1 group after found_property(title_candidates[0], 'title', confidence=0.4) return - if ('title' not in mtree.info and # no title + if ('title' not in path_node.info and # no title before_epnum_in_same_pathgroup() == [] and # no groups before len(title_candidates) == 2): # only 2 groups after @@ -77,17 +89,17 @@ class GuessEpisodeInfoFromPosition(Transformer): # probably the series name series_candidates = before_epnum_in_same_pathgroup() if len(series_candidates) >= 1: - found_property(series_candidates[0], 'series', confidence=0.7) + found_property(series_candidates[0], 'series', confidence=0.7) # only 1 group after (in the same path group) and it's probably the # episode title. - title_candidates = self._filter_candidates(after_epnum_in_same_pathgroup(), options) + title_candidates = GuessEpisodeInfoFromPosition._filter_candidates(after_epnum_in_same_pathgroup(), options) if len(title_candidates) == 1: found_property(title_candidates[0], 'title', confidence=0.5) return else: # try in the same explicit group, with lower confidence - title_candidates = self._filter_candidates(after_epnum_in_same_explicitgroup(), options) + title_candidates = GuessEpisodeInfoFromPosition._filter_candidates(after_epnum_in_same_explicitgroup(), options) if len(title_candidates) == 1: found_property(title_candidates[0], 'title', confidence=0.4) return @@ -96,7 +108,7 @@ class GuessEpisodeInfoFromPosition(Transformer): return # get the one with the longest value - title_candidates = self._filter_candidates(after_epnum_in_same_pathgroup(), options) + title_candidates = GuessEpisodeInfoFromPosition._filter_candidates(after_epnum_in_same_pathgroup(), options) if title_candidates: maxidx = -1 maxv = -1 @@ -104,7 +116,8 @@ class GuessEpisodeInfoFromPosition(Transformer): if len(c.clean_value) > maxv: maxidx = i maxv = len(c.clean_value) - found_property(title_candidates[maxidx], 'title', confidence=0.3) + if maxidx > -1: + found_property(title_candidates[maxidx], 'title', confidence=0.3) def should_process(self, mtree, options=None): options = options or {} @@ -114,9 +127,9 @@ class GuessEpisodeInfoFromPosition(Transformer): def _filter_candidates(candidates, options): episode_details_transformer = get_transformer('guess_episode_details') if episode_details_transformer: - return [n for n in candidates if not episode_details_transformer.container.find_properties(n.value, n, options, re_match=True)] - else: - return candidates + candidates = [n for n in candidates if not episode_details_transformer.container.find_properties(n.value, n, options, re_match=True)] + candidates = list(filter(lambda n: not GuessEpisodeInfoFromPosition.excluded_word(n), candidates)) + return candidates def process(self, mtree, options=None): """ @@ -130,14 +143,24 @@ class GuessEpisodeInfoFromPosition(Transformer): eps = sorted(eps, key=lambda ep: -ep.guess.confidence()) if eps: - self.match_from_epnum_position(mtree, eps[0], options) + performed_path_nodes = [] + for ep_node in eps: + # Perform only first episode node for each path node + path_node = [node for node in ep_node.ancestors if node.category == 'path'] + if len(path_node) > 0: + path_node = path_node[0] + else: + path_node = ep_node.root + if path_node not in performed_path_nodes: + self.match_from_epnum_position(path_node, ep_node, options) + performed_path_nodes.append(path_node) else: # if we don't have the episode number, but at least 2 groups in the # basename, then it's probably series - eptitle basename = list(filter(lambda x: x.category == 'path', mtree.nodes()))[-2] - title_candidates = self._filter_candidates(basename.unidentified_leaves(), options) + title_candidates = GuessEpisodeInfoFromPosition._filter_candidates(basename.unidentified_leaves(), options) if len(title_candidates) >= 2 and 'series' not in mtree.info: found_property(title_candidates[0], 'series', confidence=0.4) @@ -154,7 +177,7 @@ class GuessEpisodeInfoFromPosition(Transformer): except IndexError: series_candidates = [] - if len(series_candidates) == 1: + if len(series_candidates) == 1 and not GuessEpisodeInfoFromPosition.excluded_word(series_candidates[0]): found_property(series_candidates[0], 'series', confidence=0.3) # if there's a path group that only contains the season info, then the @@ -165,7 +188,7 @@ class GuessEpisodeInfoFromPosition(Transformer): if eps: previous = [node for node in mtree.unidentified_leaves() if node.node_idx[0] == eps[0].node_idx[0] - 1] - if len(previous) == 1: + if len(previous) == 1 and not GuessEpisodeInfoFromPosition.excluded_word(previous[0]): found_property(previous[0], 'series', confidence=0.5) # If we have found title without any serie name, replace it by the serie name. diff --git a/lib/guessit/transfo/guess_episodes_rexps.py b/lib/guessit/transfo/guess_episodes_rexps.py index f6fca1ab208ca3327c3903ebe05609a89e3f134c..9e5a88a3e6c5e3b37789fa50bf38cf9db0079829 100644 --- a/lib/guessit/transfo/guess_episodes_rexps.py +++ b/lib/guessit/transfo/guess_episodes_rexps.py @@ -67,7 +67,14 @@ class GuessEpisodesRexps(Transformer): class ResolutionCollisionValidator(object): @staticmethod def validate(prop, string, node, match, entry_start, entry_end): - return len(match.group(2)) < 3 # limit + # Invalidate when season or episode is more than 100. + try: + season_value = season_parser(match.group(2)) + episode_value = episode_parser_x(match.group(3)) + return season_value < 100 or episode_value < 100 + except: + # This may occur for 1xAll or patterns like this. + return True self.container.register_property(None, r'(' + season_words_re.pattern + sep + '?(?P<season>' + numeral + ')' + sep + '?' + season_words_re.pattern + '?)', confidence=1.0, formatter=parse_numeral) self.container.register_property(None, r'(' + season_words_re.pattern + sep + '?(?P<season>' + digital_numeral + '(?:' + sep + '?' + all_separators_re.pattern + sep + '?' + digital_numeral + ')*)' + sep + '?' + season_words_re.pattern + '?)' + sep, confidence=1.0, formatter={None: parse_numeral, 'season': season_parser}, validator=ChainedValidator(DefaultValidator(), FormatterValidator('season', lambda x: len(x) > 1 if hasattr(x, '__len__') else False))) diff --git a/lib/guessit/transfo/guess_language.py b/lib/guessit/transfo/guess_language.py index f045930a25ca2ade65a7fe5ff329336561f325bb..a1c6c3747b4117e6207cfc909a01b614d79ef3fb 100644 --- a/lib/guessit/transfo/guess_language.py +++ b/lib/guessit/transfo/guess_language.py @@ -43,6 +43,12 @@ class GuessLanguage(Transformer): allowed_languages = None if options and 'allowed_languages' in options: allowed_languages = options.get('allowed_languages') + + directory = list(filter(lambda x: x.category == 'path', node.ancestors))[0] + if len(directory.clean_value) <= 3: + # skip if we have a langage code as directory + return None + guess = search_language(string, allowed_languages) return guess diff --git a/lib/guessit/transfo/guess_movie_title_from_position.py b/lib/guessit/transfo/guess_movie_title_from_position.py index e93c67dc07dbc3414ba4a24fa8ab84ef01646af2..a55a410b826d26b9a58ccd0f2bbaaf9393c7e8cf 100644 --- a/lib/guessit/transfo/guess_movie_title_from_position.py +++ b/lib/guessit/transfo/guess_movie_title_from_position.py @@ -23,6 +23,8 @@ from __future__ import absolute_import, division, print_function, unicode_litera from guessit.plugins.transformers import Transformer from guessit.matcher import found_property from guessit import u +from guessit.patterns.list import all_separators +from guessit.language import all_lang_prefixes_suffixes class GuessMovieTitleFromPosition(Transformer): @@ -36,6 +38,13 @@ class GuessMovieTitleFromPosition(Transformer): options = options or {} return not options.get('skip_title') and not mtree.guess.get('type', '').startswith('episode') + @staticmethod + def excluded_word(*values): + for value in values: + if value.clean_value.lower() in all_separators + all_lang_prefixes_suffixes: + return True + return False + def process(self, mtree, options=None): """ try to identify the remaining unknown groups by looking at their @@ -63,7 +72,9 @@ class GuessMovieTitleFromPosition(Transformer): # specific cases: # if we find the same group both in the folder name and the filename, # it's a good candidate for title - if folder_leftover and basename_leftover and folder_leftover[0].clean_value == basename_leftover[0].clean_value: + if (folder_leftover and basename_leftover and + folder_leftover[0].clean_value == basename_leftover[0].clean_value and + not GuessMovieTitleFromPosition.excluded_word(folder_leftover[0])): found_property(folder_leftover[0], 'title', confidence=0.8) return @@ -91,7 +102,8 @@ class GuessMovieTitleFromPosition(Transformer): if (series.clean_value != title.clean_value and series.clean_value != film_number.clean_value and basename_leaves.index(film_number) == 0 and - basename_leaves.index(title) == 1): + basename_leaves.index(title) == 1 and + not GuessMovieTitleFromPosition.excluded_word(title, series)): found_property(title, 'title', confidence=0.6) found_property(series, 'filmSeries', confidence=0.6) @@ -105,8 +117,9 @@ class GuessMovieTitleFromPosition(Transformer): if groups_before: try: node = next(groups_before) - found_property(node, 'title', confidence=0.8) - return + if not GuessMovieTitleFromPosition.excluded_word(node): + found_property(node, 'title', confidence=0.8) + return except StopIteration: pass @@ -127,8 +140,10 @@ class GuessMovieTitleFromPosition(Transformer): # if they're all in the same group, take leftover info from there leftover = mtree.node_at((group_idx,)).unidentified_leaves() try: - found_property(next(leftover), 'title', confidence=0.7) - return + node = next(leftover) + if not GuessMovieTitleFromPosition.excluded_word(node): + found_property(node, 'title', confidence=0.7) + return except StopIteration: pass @@ -140,7 +155,8 @@ class GuessMovieTitleFromPosition(Transformer): # ex: Movies/Alice in Wonderland DVDRip.XviD-DiAMOND/dmd-aw.avi # ex: Movies/Somewhere.2010.DVDRip.XviD-iLG/i-smwhr.avi <-- TODO: gets caught here? if (basename_leftover[0].clean_value.count(' ') == 0 and - folder_leftover and folder_leftover[0].clean_value.count(' ') >= 2): + folder_leftover and folder_leftover[0].clean_value.count(' ') >= 2 and + not GuessMovieTitleFromPosition.excluded_word(folder_leftover[0])): found_property(folder_leftover[0], 'title', confidence=0.7) return @@ -150,17 +166,18 @@ class GuessMovieTitleFromPosition(Transformer): # ex: Movies/[阿维达].Avida.2006.FRENCH.DVDRiP.XViD-PROD.avi if basename_leftover[0].is_explicit(): for basename_leftover_elt in basename_leftover: - if not basename_leftover_elt.is_explicit(): + if not basename_leftover_elt.is_explicit() and not GuessMovieTitleFromPosition.excluded_word(basename_leftover_elt): found_property(basename_leftover_elt, 'title', confidence=0.8) return # if all else fails, take the first remaining unidentified group in the # basename as title - found_property(basename_leftover[0], 'title', confidence=0.6) - return + if not GuessMovieTitleFromPosition.excluded_word(basename_leftover[0]): + found_property(basename_leftover[0], 'title', confidence=0.6) + return # if there are no leftover groups in the basename, look in the folder name - if folder_leftover: + if folder_leftover and not GuessMovieTitleFromPosition.excluded_word(folder_leftover[0]): found_property(folder_leftover[0], 'title', confidence=0.5) return @@ -168,7 +185,9 @@ class GuessMovieTitleFromPosition(Transformer): # of the basename basename_leftover = basename.unidentified_leaves(valid=lambda leaf: True) try: - found_property(next(basename_leftover), 'title', confidence=0.4) - return + node = next(basename_leftover) + if not GuessMovieTitleFromPosition.excluded_word(node): + found_property(node, 'title', confidence=0.4) + return except StopIteration: pass diff --git a/lib/guessit/transfo/guess_properties.py b/lib/guessit/transfo/guess_properties.py index 136a216e641677b2ae671c1bdddc08f48b91a4f5..ad9722fbbf4425f634cd101fcc8abc835351efd0 100644 --- a/lib/guessit/transfo/guess_properties.py +++ b/lib/guessit/transfo/guess_properties.py @@ -61,7 +61,6 @@ class GuessProperties(Transformer): for canonical_form, quality in quality_dict.items(): self.qualities.register_quality(propname, canonical_form, quality) - register_property('container', {'mp4': ['MP4']}) # http://en.wikipedia.org/wiki/Pirated_movie_release_types register_property('format', {'VHS': ['VHS', 'VHS-Rip'], @@ -112,32 +111,13 @@ class GuessProperties(Transformer): }, validator=ChainedValidator(DefaultValidator(), OnlyOneValidator())) - class ResolutionValidator(object): - """Make sure our match is surrounded by separators, or by another entry""" - @staticmethod - def validate(prop, string, node, match, entry_start, entry_end): - """ - span = _get_span(prop, match) - span = _trim_span(span, string[span[0]:span[1]]) - start, end = span - - sep_start = start <= 0 or string[start - 1] in sep - sep_end = end >= len(string) or string[end] in sep - start_by_other = start in entry_end - end_by_other = end in entry_start - if (sep_start or start_by_other) and (sep_end or end_by_other): - return True - return False - """ - return True - _digits_re = re.compile('\d+') def resolution_formatter(value): digits = _digits_re.findall(value) return 'x'.join(digits) - self.container.register_property('screenSize', '\d{3,4}-?[x\*]-?\d{3,4}', canonical_from_pattern=False, formatter=resolution_formatter, validator=ChainedValidator(DefaultValidator(), ResolutionValidator())) + self.container.register_property('screenSize', '\d{3,4}-?[x\*]-?\d{3,4}', canonical_from_pattern=False, formatter=resolution_formatter) register_quality('screenSize', {'360p': -300, '368p': -200, diff --git a/lib/guessit/transfo/guess_release_group.py b/lib/guessit/transfo/guess_release_group.py index 08e8786ddba12ee2bccd3f95de2aed9b8bf06364..c0bbb642221c1f07034d4604e95d0b79569ac730 100644 --- a/lib/guessit/transfo/guess_release_group.py +++ b/lib/guessit/transfo/guess_release_group.py @@ -93,8 +93,12 @@ class GuessReleaseGroup(Transformer): return False if self.re_sep.match(val[-1]): val = val[:len(val)-1] + if not val: + return False if self.re_sep.match(val[0]): val = val[1:] + if not val: + return False guess['releaseGroup'] = val forbidden = False for forbidden_lambda in self._forbidden_groupname_lambda: diff --git a/lib/guessit/transfo/split_explicit_groups.py b/lib/guessit/transfo/split_explicit_groups.py index 263a5b443b08db186d08027a9ec6040c24b42e0f..77046ea58f002d552ce891c0ed1f7e19d0db0cb8 100644 --- a/lib/guessit/transfo/split_explicit_groups.py +++ b/lib/guessit/transfo/split_explicit_groups.py @@ -48,3 +48,23 @@ class SplitExplicitGroups(Transformer): # groups = functools.reduce(lambda l, x: l + x.split('-'), groups, []) c.split_on_components(groups, category='explicit') + + def post_process(self, mtree, options=None): + """ + Decrease confidence for properties found in explicit groups. + + :param mtree: + :param options: + :return: + """ + if not options.get('name_only'): + explicit_nodes = [node for node in mtree.nodes() if node.category == 'explicit' and node.is_explicit()] + + for explicit_node in explicit_nodes: + self.alter_confidence(explicit_node, 0.5) + + def alter_confidence(self, node, factor): + for guess in node.guesses: + for k in guess.keys(): + confidence = guess.confidence(k) + guess.set_confidence(k, confidence * factor) diff --git a/lib/guessit/transfo/split_path_components.py b/lib/guessit/transfo/split_path_components.py index 9ddda3f29823c8a6a56fd60710109135c766f841..da3a2cd7b794f837b2eb52b0297d6252129ad241 100644 --- a/lib/guessit/transfo/split_path_components.py +++ b/lib/guessit/transfo/split_path_components.py @@ -44,3 +44,29 @@ class SplitPathComponents(Transformer): mtree.split_on_components(components, category='path') else: mtree.split_on_components([mtree.value, ''], category='path') + + def post_process(self, mtree, options=None): + """ + Decrease confidence for properties found in directories, filename should always have priority. + + :param mtree: + :param options: + :return: + """ + if not options.get('name_only'): + path_nodes = [node for node in mtree.nodes() if node.category == 'path'] + + for path_node in path_nodes[:-2]: + self.alter_confidence(path_node, 0.3) + + try: + last_directory_node = path_nodes[-2] + self.alter_confidence(last_directory_node, 0.6) + except IndexError: + pass + + def alter_confidence(self, node, factor): + for guess in node.guesses: + for k in guess.keys(): + confidence = guess.confidence(k) + guess.set_confidence(k, confidence * factor) diff --git a/lib/subliminal/providers/opensubtitles.py b/lib/subliminal/providers/opensubtitles.py index 795799d2301d521a859322225ab813ba4fdd1bf7..f763837d5c192ee857e6224de42bf4a00928f6b9 100644 --- a/lib/subliminal/providers/opensubtitles.py +++ b/lib/subliminal/providers/opensubtitles.py @@ -58,6 +58,7 @@ class OpenSubtitlesSubtitle(Subtitle): if video.episode and self.series_episode == video.episode: matches.add('episode') # guess + logger.info('Trying to guess movie relese name: %r ', self.movie_release_name) matches |= compute_guess_matches(video, guessit.guess_episode_info(self.movie_release_name + '.mkv')) # movie elif isinstance(video, Movie) and self.movie_kind == 'movie': diff --git a/lib/tmdb_api/test_tmdb_api.py b/lib/tmdb_api/test_tmdb_api.py index 21f70a273a03b566d834872a735eb5097a2982af..e3e9dfb76d5cd657dbb0f3db156980a763626523 100644 --- a/lib/tmdb_api/test_tmdb_api.py +++ b/lib/tmdb_api/test_tmdb_api.py @@ -23,12 +23,11 @@ except ImportError: class TVCheck(unittest.TestCase): def testTVInfo(self): - id = 1396 - name = 'UFC' + name = u'Game of Thrones' tmdb = TMDB(TMDB_API_KEY) - find = tmdb.Find(23281) - response = find.info({'external_source': 'tvrage_id'}) - self.assertTrue(hasattr(response, name)) + find = tmdb.Find(121361) + response = find.info({'external_source': 'tvdb_id'}) + self.assertEqual(response['tv_results'][0]['name'], name) def testTVSearch(self): id = 1396 diff --git a/lib/tvrage_api/__init__.py b/lib/tvrage_api/__init__.py deleted file mode 100644 index fb885164122f148a54b0d6a323475490f19f792c..0000000000000000000000000000000000000000 --- a/lib/tvrage_api/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ - -__version__ = '1.0' -__author__ = 'echel0n' -__license__ = 'BSD' diff --git a/lib/tvrage_api/tvrage_api.py b/lib/tvrage_api/tvrage_api.py deleted file mode 100644 index 89923d11705cb3c99ec9978ca56e0b01a5c14f4c..0000000000000000000000000000000000000000 --- a/lib/tvrage_api/tvrage_api.py +++ /dev/null @@ -1,722 +0,0 @@ -# !/usr/bin/env python2 -# encoding:utf-8 -# author:echel0n -# project:tvrage_api -#repository:http://github.com/echel0n/tvrage_api -#license:unlicense (http://unlicense.org/) - -""" -Modified from http://github.com/dbr/tvrage_api -Simple-to-use Python interface to The TVRage's API (tvrage.com) -""" -from functools import wraps -import traceback - -__author__ = "echel0n" -__version__ = "1.0" - -import os -import re -import time -import getpass -import tempfile -import warnings -import logging -import datetime as dt -import requests -from requests import exceptions -import xmltodict - -try: - import xml.etree.cElementTree as ElementTree -except ImportError: - import xml.etree.ElementTree as ElementTree - -from dateutil.parser import parse -from cachecontrol import CacheControl, caches - -from tvrage_ui import BaseUI -from tvrage_exceptions import (tvrage_error, tvrage_userabort, tvrage_shownotfound, tvrage_showincomplete, - tvrage_seasonnotfound, tvrage_episodenotfound, tvrage_attributenotfound) - - -def log(): - return logging.getLogger("tvrage_api") - - -def retry(ExceptionToCheck, tries=4, delay=3, backoff=2, logger=None): - """Retry calling the decorated function using an exponential backoff. - - http://www.saltycrane.com/blog/2009/11/trying-out-retry-decorator-python/ - original from: http://wiki.python.org/moin/PythonDecoratorLibrary#Retry - - :param ExceptionToCheck: the exception to check. may be a tuple of - exceptions to check - :type ExceptionToCheck: Exception or tuple - :param tries: number of times to try (not retry) before giving up - :type tries: int - :param delay: initial delay between retries in seconds - :type delay: int - :param backoff: backoff multiplier e.g. value of 2 will double the delay - each retry - :type backoff: int - :param logger: logger to use. If None, print - :type logger: logging.Logger instance - """ - - def deco_retry(f): - - @wraps(f) - def f_retry(*args, **kwargs): - mtries, mdelay = tries, delay - while mtries > 1: - try: - return f(*args, **kwargs) - except ExceptionToCheck, e: - msg = "%s, Retrying in %d seconds..." % (str(e), mdelay) - if logger: - logger.warning(msg) - else: - print msg - time.sleep(mdelay) - mtries -= 1 - mdelay *= backoff - return f(*args, **kwargs) - - return f_retry # true decorator - - return deco_retry - - -class ShowContainer(dict): - """Simple dict that holds a series of Show instances - """ - - def __init__(self): - self._stack = [] - self._lastgc = time.time() - - def __setitem__(self, key, value): - self._stack.append(key) - - #keep only the 100th latest results - if time.time() - self._lastgc > 20: - for o in self._stack[:-100]: - del self[o] - - self._stack = self._stack[-100:] - - self._lastgc = time.time() - - super(ShowContainer, self).__setitem__(key, value) - - -class Show(dict): - """Holds a dict of seasons, and show data. - """ - - def __init__(self): - dict.__init__(self) - self.data = {} - - def __repr__(self): - return "<Show %s (containing %s seasons)>" % ( - self.data.get(u'seriesname', 'instance'), - len(self) - ) - - def __getattr__(self, key): - if key in self: - # Key is an episode, return it - return self[key] - - if key in self.data: - # Non-numeric request is for show-data - return self.data[key] - - raise AttributeError - - def __getitem__(self, key): - if key in self: - # Key is an episode, return it - return dict.__getitem__(self, key) - - if key in self.data: - # Non-numeric request is for show-data - return dict.__getitem__(self.data, key) - - # Data wasn't found, raise appropriate error - if isinstance(key, int) or key.isdigit(): - # Episode number x was not found - raise tvrage_seasonnotfound("Could not find season %s" % (repr(key))) - else: - # If it's not numeric, it must be an attribute name, which - # doesn't exist, so attribute error. - raise tvrage_attributenotfound("Cannot find attribute %s" % (repr(key))) - - def airedOn(self, date): - ret = self.search(str(date), 'firstaired') - if len(ret) == 0: - raise tvrage_episodenotfound("Could not find any episodes that aired on %s" % date) - return ret - - def search(self, term=None, key=None): - """ - Search all episodes in show. Can search all data, or a specific key (for - example, episodename) - - Always returns an array (can be empty). First index contains the first - match, and so on. - - Each array index is an Episode() instance, so doing - search_results[0]['episodename'] will retrieve the episode name of the - first match. - - Search terms are converted to lower case (unicode) strings. - """ - results = [] - for cur_season in self.values(): - searchresult = cur_season.search(term=term, key=key) - if len(searchresult) != 0: - results.extend(searchresult) - - return results - - -class Season(dict): - def __init__(self, show=None): - """The show attribute points to the parent show - """ - self.show = show - - def __repr__(self): - return "<Season instance (containing %s episodes)>" % ( - len(self.keys()) - ) - - def __getattr__(self, episode_number): - if episode_number in self: - return self[episode_number] - raise AttributeError - - def __getitem__(self, episode_number): - if episode_number not in self: - raise tvrage_episodenotfound("Could not find episode %s" % (repr(episode_number))) - else: - return dict.__getitem__(self, episode_number) - - def search(self, term=None, key=None): - """Search all episodes in season, returns a list of matching Episode - instances. - """ - results = [] - for ep in self.values(): - searchresult = ep.search(term=term, key=key) - if searchresult is not None: - results.append( - searchresult - ) - return results - - -class Episode(dict): - def __init__(self, season=None): - """The season attribute points to the parent season - """ - self.season = season - - def __repr__(self): - seasno = int(self.get(u'seasonnumber', 0)) - epno = int(self.get(u'episodenumber', 0)) - epname = self.get(u'episodename') - if epname is not None: - return "<Episode %02dx%02d - %s>" % (seasno, epno, epname) - else: - return "<Episode %02dx%02d>" % (seasno, epno) - - def __getattr__(self, key): - if key in self: - return self[key] - raise AttributeError - - def __getitem__(self, key): - try: - return dict.__getitem__(self, key) - except KeyError: - raise tvrage_attributenotfound("Cannot find attribute %s" % (repr(key))) - - def search(self, term=None, key=None): - """Search episode data for term, if it matches, return the Episode (self). - The key parameter can be used to limit the search to a specific element, - for example, episodename. - - This primarily for use use by Show.search and Season.search. - """ - if term == None: - raise TypeError("must supply string to search for (contents)") - - term = unicode(term).lower() - for cur_key, cur_value in self.items(): - cur_key, cur_value = unicode(cur_key).lower(), unicode(cur_value).lower() - if key is not None and cur_key != key: - # Do not search this key - continue - if cur_value.find(unicode(term).lower()) > -1: - return self - - -class TVRage: - """Create easy-to-use interface to name of season/episode name""" - - def __init__(self, - interactive=False, - select_first=False, - debug=False, - cache=True, - banners=False, - actors=False, - custom_ui=None, - language=None, - search_all_languages=False, - apikey=None, - forceConnect=False, - useZip=False, - dvdorder=False, - proxy=None): - - """ - cache (True/False/str/unicode/urllib2 opener): - Retrieved XML are persisted to to disc. If true, stores in - tvrage_api folder under your systems TEMP_DIR, if set to - str/unicode instance it will use this as the cache - location. If False, disables caching. Can also be passed - an arbitrary Python object, which is used as a urllib2 - opener, which should be created by urllib2.build_opener - - forceConnect (bool): - If true it will always try to connect to tvrage.com even if we - recently timed out. By default it will wait one minute before - trying again, and any requests within that one minute window will - return an exception immediately. - """ - - self.shows = ShowContainer() # Holds all Show classes - self.corrections = {} # Holds show-name to show_id mapping - - self.config = {} - - if apikey is not None: - self.config['apikey'] = apikey - else: - self.config['apikey'] = "Uhewg1Rr0o62fvZvUIZt" # tvdb_api's API key - - self.config['debug_enabled'] = debug # show debugging messages - - self.config['custom_ui'] = custom_ui - - self.config['proxy'] = proxy - - if cache is True: - self.config['cache_enabled'] = True - self.config['cache_location'] = self._getTempDir() - elif cache is False: - self.config['cache_enabled'] = False - elif isinstance(cache, basestring): - self.config['cache_enabled'] = True - self.config['cache_location'] = cache - else: - raise ValueError("Invalid value for Cache %r (type was %s)" % (cache, type(cache))) - - self.config['session'] = requests.Session() - - if self.config['debug_enabled']: - warnings.warn("The debug argument to tvrage_api.__init__ will be removed in the next version. " - "To enable debug messages, use the following code before importing: " - "import logging; logging.basicConfig(level=logging.DEBUG)") - logging.basicConfig(level=logging.DEBUG) - - - # List of language from http://tvrage.com/api/0629B785CE550C8D/languages.xml - # Hard-coded here as it is realtively static, and saves another HTTP request, as - # recommended on http://tvrage.com/wiki/index.php/API:languages.xml - self.config['valid_languages'] = [ - "da", "fi", "nl", "de", "it", "es", "fr", "pl", "hu", "el", "tr", - "ru", "he", "ja", "pt", "zh", "cs", "sl", "hr", "ko", "en", "sv", "no" - ] - - # tvrage.com should be based around numeric language codes, - # but to link to a series like http://tvrage.com/?tab=series&id=79349&lid=16 - # requires the language ID, thus this mapping is required (mainly - # for usage in tvrage_ui - internally tvrage_api will use the language abbreviations) - self.config['langabbv_to_id'] = {'el': 20, 'en': 7, 'zh': 27, - 'it': 15, 'cs': 28, 'es': 16, 'ru': 22, 'nl': 13, 'pt': 26, 'no': 9, - 'tr': 21, 'pl': 18, 'fr': 17, 'hr': 31, 'de': 14, 'da': 10, 'fi': 11, - 'hu': 19, 'ja': 25, 'he': 24, 'ko': 32, 'sv': 8, 'sl': 30} - - if language is None: - self.config['language'] = 'en' - else: - if language not in self.config['valid_languages']: - raise ValueError("Invalid language %s, options are: %s" % ( - language, self.config['valid_languages'] - )) - else: - self.config['language'] = language - - # The following url_ configs are based of the - # http://tvrage.com/wiki/index.php/Programmers_API - - self.config['base_url'] = "http://services.tvrage.com" - - self.config['url_getSeries'] = u"%(base_url)s/feeds/search.php" % self.config - self.config['params_getSeries'] = {"show": ""} - - self.config['url_epInfo'] = u"%(base_url)s/myfeeds/episode_list.php" % self.config - self.config['params_epInfo'] = {"key": self.config['apikey'], "sid": ""} - - self.config['url_seriesInfo'] = u"%(base_url)s/myfeeds/showinfo.php" % self.config - self.config['params_seriesInfo'] = {"key": self.config['apikey'], "sid": ""} - - self.config['url_updtes_all'] = u"%(base_url)s/myfeeds/currentshows.php" % self.config - - def _getTempDir(self): - """Returns the [system temp dir]/tvrage_api-u501 (or - tvrage_api-myuser) - """ - if hasattr(os, 'getuid'): - uid = "u%d" % (os.getuid()) - else: - # For Windows - try: - uid = getpass.getuser() - except ImportError: - return os.path.join(tempfile.gettempdir(), "tvrage_api") - - return os.path.join(tempfile.gettempdir(), "tvrage_api-%s" % (uid)) - - @retry(tvrage_error) - def _loadUrl(self, url, params=None): - try: - log().debug("Retrieving URL %s" % url) - - # get response from TVRage - if self.config['cache_enabled']: - session = CacheControl(sess=self.config['session'], cache=caches.FileCache(self.config['cache_location']), cache_etags=False) - if self.config['proxy']: - log().debug("Using proxy for URL: %s" % url) - session.proxies = { - "http": self.config['proxy'], - "https": self.config['proxy'], - } - - resp = session.get(url.strip(), params=params) - else: - resp = requests.get(url.strip(), params=params) - - resp.raise_for_status() - except requests.exceptions.HTTPError, e: - raise tvrage_error("HTTP error " + str(e.errno) + " while loading URL " + str(url)) - except requests.exceptions.ConnectionError, e: - raise tvrage_error("Connection error " + str(e.message) + " while loading URL " + str(url)) - except requests.exceptions.Timeout, e: - raise tvrage_error("Connection timed out " + str(e.message) + " while loading URL " + str(url)) - except Exception: - raise tvrage_error("Unknown exception while loading URL " + url + ": " + traceback.format_exc()) - - def remap_keys(path, key, value): - name_map = { - 'showid': 'id', - 'showname': 'seriesname', - 'name': 'seriesname', - 'summary': 'overview', - 'started': 'firstaired', - 'genres': 'genre', - 'airtime': 'airs_time', - 'airday': 'airs_dayofweek', - 'image': 'fanart', - 'epnum': 'absolute_number', - 'title': 'episodename', - 'airdate': 'firstaired', - 'screencap': 'filename', - 'seasonnum': 'episodenumber' - } - - status_map = { - 'returning series': 'Continuing', - 'canceled/ended': 'Ended', - 'tbd/on the bubble': 'Continuing', - 'in development': 'Continuing', - 'new series': 'Continuing', - 'never aired': 'Ended', - 'final season': 'Continuing', - 'on hiatus': 'Continuing', - 'pilot ordered': 'Continuing', - 'pilot rejected': 'Ended', - 'canceled': 'Ended', - 'ended': 'Ended', - '': 'Unknown', - } - - try: - key = name_map[key.lower()] - except (ValueError, TypeError, KeyError): - key = key.lower() - - # clean up value and do type changes - if value: - if isinstance(value, dict): - if key == 'status': - try: - value = status_map[str(value).lower()] - if not value: - raise - except: - value = 'Unknown' - - if key == 'network': - value = value['#text'] - - if key == 'genre': - value = value['genre'] - if not value: - value = [] - if not isinstance(value, list): - value = [value] - value = filter(None, value) - value = '|' + '|'.join(value) + '|' - - try: - if key == 'firstaired' and value in "0000-00-00": - new_value = str(dt.date.fromordinal(1)) - new_value = re.sub("([-]0{2}){1,}", "", new_value) - fixDate = parse(new_value, fuzzy=True).date() - value = fixDate.strftime("%Y-%m-%d") - elif key == 'firstaired': - value = parse(value, fuzzy=True).date() - value = value.strftime("%Y-%m-%d") - except: - pass - - return (key, value) - - try: - return xmltodict.parse(resp.content.decode('utf-8'), postprocessor=remap_keys) - except: - return dict([(u'data', None)]) - - def _getetsrc(self, url, params=None): - """Loads a URL using caching, returns an ElementTree of the source - """ - - try: - return self._loadUrl(url, params).values()[0] - except Exception, e: - raise tvrage_error(e) - - def _setItem(self, sid, seas, ep, attrib, value): - """Creates a new episode, creating Show(), Season() and - Episode()s as required. Called by _getShowData to populate show - - Since the nice-to-use tvrage[1][24]['name] interface - makes it impossible to do tvrage[1][24]['name] = "name" - and still be capable of checking if an episode exists - so we can raise tvrage_shownotfound, we have a slightly - less pretty method of setting items.. but since the API - is supposed to be read-only, this is the best way to - do it! - The problem is that calling tvrage[1][24]['episodename'] = "name" - calls __getitem__ on tvrage[1], there is no way to check if - tvrage.__dict__ should have a key "1" before we auto-create it - """ - if sid not in self.shows: - self.shows[sid] = Show() - if seas not in self.shows[sid]: - self.shows[sid][seas] = Season(show=self.shows[sid]) - if ep not in self.shows[sid][seas]: - self.shows[sid][seas][ep] = Episode(season=self.shows[sid][seas]) - self.shows[sid][seas][ep][attrib] = value - - def _setShowData(self, sid, key, value): - """Sets self.shows[sid] to a new Show instance, or sets the data - """ - if sid not in self.shows: - self.shows[sid] = Show() - self.shows[sid].data[key] = value - - def _cleanData(self, data): - """Cleans up strings returned by tvrage.com - - Issues corrected: - - Replaces & with & - - Trailing whitespace - """ - - if isinstance(data, basestring): - data = data.replace(u"&", u"&") - data = data.strip() - - return data - - def search(self, series): - """This searches tvrage.com for the series name - and returns the result list - """ - series = series.encode("utf-8") - log().debug("Searching for show %s" % series) - self.config['params_getSeries']['show'] = series - - results = self._getetsrc(self.config['url_getSeries'], self.config['params_getSeries']) - if not results: - return - - return results.values()[0] - - def _getSeries(self, series): - """This searches tvrage.com for the series name, - If a custom_ui UI is configured, it uses this to select the correct - series. If not, and interactive == True, ConsoleUI is used, if not - BaseUI is used to select the first result. - """ - allSeries = self.search(series) - if not allSeries: - log().debug('Series result returned zero') - raise tvrage_shownotfound("Show search returned zero results (cannot find show on TVRAGE)") - - if not isinstance(allSeries, list): - allSeries = [allSeries] - - if self.config['custom_ui'] is not None: - log().debug("Using custom UI %s" % (repr(self.config['custom_ui']))) - CustomUI = self.config['custom_ui'] - ui = CustomUI(config=self.config) - else: - log().debug('Auto-selecting first search result using BaseUI') - ui = BaseUI(config=self.config) - - return ui.selectSeries(allSeries) - - def _getShowData(self, sid, getEpInfo=False): - """Takes a series ID, gets the epInfo URL and parses the TVRAGE - XML file into the shows dict in layout: - shows[series_id][season_number][episode_number] - """ - - # Parse show information - log().debug('Getting all series data for %s' % (sid)) - self.config['params_seriesInfo']['sid'] = sid - seriesInfoEt = self._getetsrc( - self.config['url_seriesInfo'], - self.config['params_seriesInfo'] - ) - - if not seriesInfoEt: - log().debug('Series result returned zero') - raise tvrage_error("Series result returned zero") - - # get series data - for k, v in seriesInfoEt.items(): - if v is not None: - v = self._cleanData(v) - - self._setShowData(sid, k, v) - - # get episode data - if getEpInfo: - # Parse episode data - log().debug('Getting all episodes of %s' % (sid)) - self.config['params_epInfo']['sid'] = sid - epsEt = self._getetsrc(self.config['url_epInfo'], self.config['params_epInfo']) - - if not epsEt: - log().debug('Series results incomplete') - raise tvrage_showincomplete( - "Show search returned incomplete results (cannot find complete show on TVRAGE)") - - if 'episodelist' not in epsEt: - return False - - seasons = epsEt['episodelist']['season'] - if not isinstance(seasons, list): - seasons = [seasons] - - for season in seasons: - seas_no = int(season['@no']) - - episodes = season['episode'] - if not isinstance(episodes, list): - episodes = [episodes] - - for episode in episodes: - ep_no = int(episode['episodenumber']) - - for k, v in episode.items(): - k = k.lower() - - if v is not None: - if k == 'link': - v = v.rsplit('/', 1)[1] - k = 'id' - else: - v = self._cleanData(v) - - self._setItem(sid, seas_no, ep_no, k, v) - - return True - - def _nameToSid(self, name): - """Takes show name, returns the correct series ID (if the show has - already been grabbed), or grabs all episodes and returns - the correct SID. - """ - if name in self.corrections: - log().debug('Correcting %s to %s' % (name, self.corrections[name])) - return self.corrections[name] - else: - log().debug('Getting show %s' % (name)) - selected_series = self._getSeries(name) - if isinstance(selected_series, dict): - selected_series = [selected_series] - sids = list(int(x['id']) for x in selected_series if self._getShowData(int(x['id']))) - self.corrections.update(dict((x['seriesname'], int(x['id'])) for x in selected_series)) - return sids - - def __getitem__(self, key): - """Handles tvrage_instance['seriesname'] calls. - The dict index should be the show id - """ - if isinstance(key, (int, long)): - # Item is integer, treat as show id - if key not in self.shows: - self._getShowData(key, True) - return self.shows[key] - - key = str(key).lower() - self.config['searchterm'] = key - selected_series = self._getSeries(key) - if isinstance(selected_series, dict): - selected_series = [selected_series] - [[self._setShowData(show['id'], k, v) for k, v in show.items()] for show in selected_series] - return selected_series - #test = self._getSeries(key) - #sids = self._nameToSid(key) - #return list(self.shows[sid] for sid in sids) - - def __repr__(self): - return str(self.shows) - - -def main(): - """Simple example of using tvrage_api - it just - grabs an episode name interactively. - """ - import logging - - logging.basicConfig(level=logging.DEBUG) - - tvrage_instance = TVRage(cache=False) - print tvrage_instance['Lost']['seriesname'] - print tvrage_instance['Lost'][1][4]['episodename'] - - -if __name__ == '__main__': - main() diff --git a/lib/tvrage_api/tvrage_cache.py b/lib/tvrage_api/tvrage_cache.py deleted file mode 100644 index 9f58ff864e29c9f3a86a41e74b5b6a461eb93ad5..0000000000000000000000000000000000000000 --- a/lib/tvrage_api/tvrage_cache.py +++ /dev/null @@ -1,251 +0,0 @@ -#!/usr/bin/env python2 -#encoding:utf-8 -#author:echel0n -#project:tvrage_api -#repository:http://github.com/echel0n/tvrage_api -#license:unlicense (http://unlicense.org/) - -""" -urllib2 caching handler -Modified from http://code.activestate.com/recipes/491261/ -""" -from __future__ import with_statement - -__author__ = "echel0n" -__version__ = "1.0" - -import os -import time -import errno -import httplib -import urllib2 -import StringIO -from hashlib import md5 -from threading import RLock - -cache_lock = RLock() - -def locked_function(origfunc): - """Decorator to execute function under lock""" - def wrapped(*args, **kwargs): - cache_lock.acquire() - try: - return origfunc(*args, **kwargs) - finally: - cache_lock.release() - return wrapped - -def calculate_cache_path(cache_location, url): - """Checks if [cache_location]/[hash_of_url].headers and .body exist - """ - thumb = md5(url).hexdigest() - header = os.path.join(cache_location, thumb + ".headers") - body = os.path.join(cache_location, thumb + ".body") - return header, body - -def check_cache_time(path, max_age): - """Checks if a file has been created/modified in the [last max_age] seconds. - False means the file is too old (or doesn't exist), True means it is - up-to-date and valid""" - if not os.path.isfile(path): - return False - cache_modified_time = os.stat(path).st_mtime - time_now = time.time() - if cache_modified_time < time_now - max_age: - # Cache is old - return False - else: - return True - -@locked_function -def exists_in_cache(cache_location, url, max_age): - """Returns if header AND body cache file exist (and are up-to-date)""" - hpath, bpath = calculate_cache_path(cache_location, url) - if os.path.exists(hpath) and os.path.exists(bpath): - return( - check_cache_time(hpath, max_age) - and check_cache_time(bpath, max_age) - ) - else: - # File does not exist - return False - -@locked_function -def store_in_cache(cache_location, url, response): - """Tries to store response in cache.""" - hpath, bpath = calculate_cache_path(cache_location, url) - try: - outf = open(hpath, "wb") - headers = str(response.info()) - outf.write(headers) - outf.close() - - outf = open(bpath, "wb") - outf.write(response.read()) - outf.close() - except IOError: - return True - else: - return False - -@locked_function -def delete_from_cache(cache_location, url): - """Deletes a response in cache.""" - hpath, bpath = calculate_cache_path(cache_location, url) - try: - if os.path.exists(hpath): - os.remove(hpath) - if os.path.exists(bpath): - os.remove(bpath) - except IOError: - return True - else: - return False - -class CacheHandler(urllib2.BaseHandler): - """Stores responses in a persistant on-disk cache. - - If a subsequent GET request is made for the same URL, the stored - response is returned, saving time, resources and bandwidth - """ - @locked_function - def __init__(self, cache_location, max_age = 21600): - """The location of the cache directory""" - self.max_age = max_age - self.cache_location = cache_location - if not os.path.exists(self.cache_location): - try: - os.mkdir(self.cache_location) - except OSError, e: - if e.errno == errno.EEXIST and os.path.isdir(self.cache_location): - # File exists, and it's a directory, - # another process beat us to creating this dir, that's OK. - pass - else: - # Our target dir is already a file, or different error, - # relay the error! - raise - - def default_open(self, request): - """Handles GET requests, if the response is cached it returns it - """ - if request.get_method() != "GET": - return None # let the next handler try to handle the request - - if exists_in_cache( - self.cache_location, request.get_full_url(), self.max_age - ): - return CachedResponse( - self.cache_location, - request.get_full_url(), - set_cache_header = True - ) - else: - return None - - def http_response(self, request, response): - """Gets a HTTP response, if it was a GET request and the status code - starts with 2 (200 OK etc) it caches it and returns a CachedResponse - """ - if (request.get_method() == "GET" - and str(response.code).startswith("2") - ): - if 'x-local-cache' not in response.info(): - # Response is not cached - set_cache_header = store_in_cache( - self.cache_location, - request.get_full_url(), - response - ) - else: - set_cache_header = True - - return CachedResponse( - self.cache_location, - request.get_full_url(), - set_cache_header = set_cache_header - ) - else: - return response - -class CachedResponse(StringIO.StringIO): - """An urllib2.response-like object for cached responses. - - To determine if a response is cached or coming directly from - the network, check the x-local-cache header rather than the object type. - """ - - @locked_function - def __init__(self, cache_location, url, set_cache_header=True): - self.cache_location = cache_location - hpath, bpath = calculate_cache_path(cache_location, url) - - StringIO.StringIO.__init__(self, file(bpath, "rb").read()) - - self.url = url - self.code = 200 - self.msg = "OK" - headerbuf = file(hpath, "rb").read() - if set_cache_header: - headerbuf += "x-local-cache: %s\r\n" % (bpath) - self.headers = httplib.HTTPMessage(StringIO.StringIO(headerbuf)) - - def info(self): - """Returns headers - """ - return self.headers - - def geturl(self): - """Returns original URL - """ - return self.url - - @locked_function - def recache(self): - new_request = urllib2.urlopen(self.url) - set_cache_header = store_in_cache( - self.cache_location, - new_request.url, - new_request - ) - CachedResponse.__init__(self, self.cache_location, self.url, True) - - @locked_function - def delete_cache(self): - delete_from_cache( - self.cache_location, - self.url - ) - - -if __name__ == "__main__": - def main(): - """Quick test/example of CacheHandler""" - opener = urllib2.build_opener(CacheHandler("/tmp/")) - response = opener.open("http://google.com") - print response.headers - print "Response:", response.read() - - response.recache() - print response.headers - print "After recache:", response.read() - - # Test usage in threads - from threading import Thread - class CacheThreadTest(Thread): - lastdata = None - def run(self): - req = opener.open("http://google.com") - newdata = req.read() - if self.lastdata is None: - self.lastdata = newdata - assert self.lastdata == newdata, "Data was not consistent, uhoh" - req.recache() - threads = [CacheThreadTest() for x in range(50)] - print "Starting threads" - [t.start() for t in threads] - print "..done" - print "Joining threads" - [t.join() for t in threads] - print "..done" - main() diff --git a/lib/tvrage_api/tvrage_exceptions.py b/lib/tvrage_api/tvrage_exceptions.py deleted file mode 100644 index 69b918b6447bdfd8b0011ddd7f7bddfae38e51e4..0000000000000000000000000000000000000000 --- a/lib/tvrage_api/tvrage_exceptions.py +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env python2 -# encoding:utf-8 -#author:echel0n -#project:tvrage_api -#repository:http://github.com/echel0n/tvrage_api -#license:unlicense (http://unlicense.org/) - -"""Custom exceptions used or raised by tvrage_api""" - -__author__ = "echel0n" -__version__ = "1.0" - -__all__ = ["tvrage_error", "tvrage_userabort", "tvrage_shownotfound", "tvrage_showincomplete", - "tvrage_seasonnotfound", "tvrage_episodenotfound", "tvrage_attributenotfound"] - - -class tvrage_exception(Exception): - """Any exception generated by tvrage_api - """ - pass - - -class tvrage_error(tvrage_exception): - """An error with tvrage.com (Cannot connect, for example) - """ - pass - - -class tvrage_userabort(tvrage_exception): - """User aborted the interactive selection (via - the q command, ^c etc) - """ - pass - - -class tvrage_shownotfound(tvrage_exception): - """Show cannot be found on tvrage.com (non-existant show) - """ - pass - - -class tvrage_showincomplete(tvrage_exception): - """Show found but incomplete on tvrage.com (incomplete show) - """ - pass - - -class tvrage_seasonnotfound(tvrage_exception): - """Season cannot be found on tvrage.com - """ - pass - - -class tvrage_episodenotfound(tvrage_exception): - """Episode cannot be found on tvrage.com - """ - pass - - -class tvrage_attributenotfound(tvrage_exception): - """Raised if an episode does not have the requested - attribute (such as a episode name) - """ - pass diff --git a/lib/tvrage_api/tvrage_ui.py b/lib/tvrage_api/tvrage_ui.py deleted file mode 100644 index 64e54d6e0b2c75289f140c7469feaa290dc46ef1..0000000000000000000000000000000000000000 --- a/lib/tvrage_api/tvrage_ui.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env python2 -#encoding:utf-8 -#author:echel0n -#project:tvrage_api -#repository:http://github.com/echel0n/tvrage_api -#license:unlicense (http://unlicense.org/) - -"""Contains included user interface for TVRage show selection""" - -__author__ = "echel0n" -__version__ = "1.0" - -import logging -import warnings - -def log(): - return logging.getLogger(__name__) - -class BaseUI: - """Default non-interactive UI, which auto-selects first results - """ - def __init__(self, config, log = None): - self.config = config - if log is not None: - warnings.warn("the UI's log parameter is deprecated, instead use\n" - "use import logging; logging.getLogger('ui').info('blah')\n" - "The self.log attribute will be removed in the next version") - self.log = logging.getLogger(__name__) - - def selectSeries(self, allSeries): - return allSeries[0] \ No newline at end of file diff --git a/readme.md b/readme.md index 4779a9fc73dd58c2737f6d5695e6b7f7dc7edc4a..3c6d1fdb3f2de833a16e9905cc14aa407d32652f 100644 --- a/readme.md +++ b/readme.md @@ -12,11 +12,10 @@ Automatic Video Library Manager for TV Shows. It watches for new episodes of you - Automatic torrent/nzb searching, downloading, and processing at the qualities you want - Largest list of supported torrent and nzb providers, both public and private - Can notify Kodi, XBMC, Growl, Trakt, Twitter, and more when new episodes are available - - Searches TheTVDB.com, TVRage.com, and AniDB.net for shows, seasons, episodes, and metadata + - Searches TheTVDB.com and AniDB.net for shows, seasons, episodes, and metadata - Episode status management allows for mass failing seasons/episodes to force retrying - DVD Order numbering for returning the results in DVD order instead of Air-By-Date order - Allows you to choose which indexer to have SickRage search its show info from when importing - - SickRage can easily tell if info for an existing show comes from TheTVDB or TVRage when importing - Automatic XEM Scene Numbering/Naming for seasons/episodes - Available for any platform, uses a simple HTTP interface - Specials and multi-episode torrent/nzb support diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py index 83bb7398e301bf081d815abb94f000fd28919491..523e52e78e92c9bd4a6ae01e9c2919ac97103266 100644 --- a/sickbeard/__init__.py +++ b/sickbeard/__init__.py @@ -34,26 +34,33 @@ import sys from github import Github -from . import providers, metadata, config, webserveInit -from .providers.generic import GenericProvider -from .providers import btn, newznab, womble, thepiratebay, torrentleech, kat, iptorrents, \ +from sickbeard import metadata +from sickbeard import providers +from sickbeard.providers.generic import GenericProvider +from sickbeard.providers import btn, newznab, womble, thepiratebay, torrentleech, kat, iptorrents, \ omgwtfnzbs, scc, hdtorrents, torrentday, hdbits, hounddawgs, nextgen, speedcd, nyaatorrents, animenzb, bluetigers, cpasbien, fnt, xthor, torrentbytes, \ frenchtorrentdb, freshontv, titansoftv, libertalia, morethantv, bitsoup, t411, tokyotoshokan, shazbat, rarbg, alpharatio, tntvillage, binsearch, scenetime, btdigg -from .config import CheckSection, check_setting_int, check_setting_str, check_setting_float, ConfigMigrator, \ +from sickbeard.config import CheckSection, check_setting_int, check_setting_str, check_setting_float, ConfigMigrator, \ naming_ep_type -from . import searchBacklog, showUpdater, versionChecker, properFinder, autoPostProcesser, \ +from sickbeard import searchBacklog, showUpdater, versionChecker, properFinder, autoPostProcesser, \ subtitles, traktChecker -from . import helpers, db, exceptions, show_queue, search_queue, scheduler, show_name_helpers -from . import logger -from . import naming -from . import dailysearcher -from . import scene_numbering, scene_exceptions, name_cache -from .indexers.indexer_api import indexerApi -from .indexers.indexer_exceptions import indexer_shownotfound, indexer_showincomplete, indexer_exception, indexer_error, \ +from sickbeard import db +from sickbeard import helpers +from sickbeard import scheduler +from sickbeard import search_queue +from sickbeard import show_queue +from sickbeard import logger +from sickbeard import naming +from sickbeard import dailysearcher +from sickbeard.indexers.indexer_api import indexerApi +from sickbeard.indexers.indexer_exceptions import indexer_shownotfound, indexer_showincomplete, indexer_exception, indexer_error, \ indexer_episodenotfound, indexer_attributenotfound, indexer_seasonnotfound, indexer_userabort, indexerExcepts -from .common import SD, SKIPPED, WANTED, NAMING_REPEAT -from .databases import mainDB, cache_db, failed_db -from .helpers import ex +from sickbeard.common import SD +from sickbeard.common import SKIPPED +from sickbeard.common import WANTED +from sickbeard.databases import mainDB, cache_db, failed_db +from sickbeard.exceptions import ex +from sickrage.system.Shutdown import Shutdown from configobj import ConfigObj @@ -541,6 +548,7 @@ FANART_API_KEY = '9b3afaf26f6241bdb57d6cc6bd798da7' __INITIALIZED__ = False +NEWZNAB_DATA = None def get_backlog_cycle_time(): cycletime = DAILYSEARCH_FREQUENCY * 2 + 7 @@ -1568,9 +1576,9 @@ def halt(): def sig_handler(signum=None, frame=None): - if type(signum) != type(None): + if not isinstance(signum, type(None)): logger.log(u"Signal %i caught, saving and exiting..." % int(signum)) - events.put(events.SystemEvent.SHUTDOWN) + Shutdown.stop(PID) def saveAll(): @@ -1953,7 +1961,7 @@ def save_config(): new_config['Pushover']['pushover_userkey'] = PUSHOVER_USERKEY new_config['Pushover']['pushover_apikey'] = PUSHOVER_APIKEY new_config['Pushover']['pushover_device'] = PUSHOVER_DEVICE - new_config['Pushover']['pushover_sound]'] = PUSHOVER_SOUND + new_config['Pushover']['pushover_sound'] = PUSHOVER_SOUND new_config['Libnotify'] = {} new_config['Libnotify']['use_libnotify'] = int(USE_LIBNOTIFY) diff --git a/sickbeard/browser.py b/sickbeard/browser.py index 0df6491aaf98e342dcd70c9f0e5f913fddf452f1..2408923928f7e7015544fcee79f408eb634e90db 100644 --- a/sickbeard/browser.py +++ b/sickbeard/browser.py @@ -19,24 +19,14 @@ import os import string -from tornado.web import RequestHandler from sickbeard import encodingKludge as ek from sickbeard import logger -# use the built-in if it's available (python 2.6), if not use the included library -try: - import json -except ImportError: - import simplejson as json - -# this is for the drive letter code, it only works on windows -if os.name == 'nt': - from ctypes import windll - # adapted from http://stackoverflow.com/questions/827371/is-there-a-way-to-list-all-the-available-drive-letters-in-python/827490 def getWinDrives(): """ Return list of detected drives """ assert os.name == 'nt' + from ctypes import windll drives = [] bitmask = windll.kernel32.GetLogicalDrives() #@UndefinedVariable diff --git a/sickbeard/bs4_parser.py b/sickbeard/bs4_parser.py index 0c3bf6062801320c4c9c9a4ec09c7fa154a9b1f4..4d6887bdc4d1f08988e390c0569f8fd63c3c5acc 100644 --- a/sickbeard/bs4_parser.py +++ b/sickbeard/bs4_parser.py @@ -1,4 +1,3 @@ -import sickbeard from bs4 import BeautifulSoup class BS4Parser: diff --git a/sickbeard/classes.py b/sickbeard/classes.py index f46bcc9e17aa7f0a5653e37331c55b09940f35f9..0cc66e639364f15867ec6bd1c733e636153debd5 100644 --- a/sickbeard/classes.py +++ b/sickbeard/classes.py @@ -17,7 +17,6 @@ # along with SickRage. If not, see <http://www.gnu.org/licenses/>. import re import sys -import traceback import sickbeard diff --git a/sickbeard/clients/__init__.py b/sickbeard/clients/__init__.py index ce1b7bbddb4c1c7e7e8535b0090855af103f594b..7fe1f62f333d7a8e9f7a7136772bddc63d2c44d6 100644 --- a/sickbeard/clients/__init__.py +++ b/sickbeard/clients/__init__.py @@ -25,9 +25,7 @@ __all__ = ['utorrent', 'qbittorrent' ] -import sickbeard -from os import sys # Mapping error status codes to official W3C names http_error_code = { diff --git a/sickbeard/clients/deluged_client.py b/sickbeard/clients/deluged_client.py index 9c2e00b56189abd0c4a8023e62d0caa977a732c3..10988b6433266dc845f6ee12ec952f7a3c38b3fe 100644 --- a/sickbeard/clients/deluged_client.py +++ b/sickbeard/clients/deluged_client.py @@ -4,7 +4,6 @@ # This client script allows connection to Deluge Daemon directly, completely # circumventing the requirement to use the WebUI. -import json from base64 import b64encode import sickbeard @@ -84,21 +83,26 @@ class DelugeDAPI(GenericClient): return False if label: - if self.drpc.set_torrent_label(result.hash, label): - return True - + return self.drpc.set_torrent_label(result.hash, label) return False + def _set_torrent_ratio(self, result): + if result.ratio: + ratio = float(result.ratio) + return self.drpc.set_torrent_ratio(result.hash, ratio) + return False - return True + def _set_torrent_priority(self, result): + if result.priority == 1: + return self.drpc.set_torrent_priority(result.hash, True) + return False def _set_torrent_path(self, result): path = sickbeard.TORRENT_PATH if path: - if self.drpc.set_torrent_path(result.hash, path): - return True + return self.drpc.set_torrent_path(result.hash, path) return False def _set_torrent_pause(self, result): @@ -175,7 +179,6 @@ class DelugeRPC(object): self.connect() self.client.label.set_torrent(torrent_id, label).get() except Exception as err: - logger.log('DelugeD: Failed to set label for torrent: ' + err + ' ' + traceback.format_exc(), logger.ERROR) return False finally: if self.client: @@ -188,7 +191,30 @@ class DelugeRPC(object): self.client.core.set_torrent_move_completed_path(torrent_id, path).get() self.client.core.set_torrent_move_completed(torrent_id, 1).get() except Exception as err: - logger.log('DelugeD: Failed to set path for torrent: ' + err + ' ' + traceback.format_exc(), logger.ERROR) + return False + finally: + if self.client: + self.disconnect() + return True + + def set_torrent_priority(self, torrent_ids, priority): + try: + self.connect() + if priority: + self.client.core.queue_top([torrent_ids]).get() + except Exception, err: + return False + finally: + if self.client: + self.disconnect() + return True + + def set_torrent_ratio(self, torrent_ids, ratio): + try: + self.connect() + self.client.core.set_torrent_stop_at_ratio(torrent_ids, True).get() + self.client.core.set_torrent_stop_ratio(torrent_ids, ratio).get() + except Exception, err: return False finally: if self.client: @@ -200,7 +226,6 @@ class DelugeRPC(object): self.connect() self.client.core.pause_torrent(torrent_ids).get() except Exception as err: - logger.log('DelugeD: Failed to pause torrent: ' + err + ' ' + traceback.format_exc(), logger.ERROR) return False finally: if self.client: diff --git a/sickbeard/clients/generic.py b/sickbeard/clients/generic.py index 22bbf1ff7eb174ba0c21e9755d2b03dec2bd2463..21e42369e95e9b46cd5762b38d4dfe73a0c11fb3 100644 --- a/sickbeard/clients/generic.py +++ b/sickbeard/clients/generic.py @@ -5,11 +5,9 @@ from base64 import b16encode, b32decode import sickbeard from sickbeard import logger -from sickbeard.exceptions import ex from . import http_error_code from bencode import bencode, bdecode import requests -from requests import exceptions from bencode.BTL import BTFailure class GenericClient(object): diff --git a/sickbeard/clients/qbittorrent_client.py b/sickbeard/clients/qbittorrent_client.py index b07d827b320bb902fcbc53c3fc985b1ceb7e294e..7b39a3f09a6210882b3ede7a3b1bd441587d4b6b 100644 --- a/sickbeard/clients/qbittorrent_client.py +++ b/sickbeard/clients/qbittorrent_client.py @@ -17,9 +17,7 @@ # along with SickRage. If not, see <http://www.gnu.org/licenses/>. import sickbeard -from sickbeard import logger from .generic import GenericClient -import requests from requests.auth import HTTPDigestAuth class qbittorrentAPI(GenericClient): diff --git a/sickbeard/clients/rtorrent_client.py b/sickbeard/clients/rtorrent_client.py index 2b2ee37c7a020ce4a928c22f72a004cff0ed20c5..b6d6c1cc9b7afe30a78dd4cba0452af93598ea1b 100644 --- a/sickbeard/clients/rtorrent_client.py +++ b/sickbeard/clients/rtorrent_client.py @@ -16,14 +16,12 @@ # You should have received a copy of the GNU General Public License # along with SickRage. If not, see <http://www.gnu.org/licenses/>. -from base64 import b64encode import traceback import sickbeard from sickbeard import logger from .generic import GenericClient from rtorrent import RTorrent -from rtorrent.err import MethodError class rTorrentAPI(GenericClient): diff --git a/sickbeard/clients/transmission_client.py b/sickbeard/clients/transmission_client.py index ebc11dc65e22199ce26b02008bdfcf3d986f09d1..06fc75a546f50e1bda72b85acd522e00472558c1 100644 --- a/sickbeard/clients/transmission_client.py +++ b/sickbeard/clients/transmission_client.py @@ -21,7 +21,6 @@ import json from base64 import b64encode import sickbeard -from sickbeard import logger from .generic import GenericClient diff --git a/sickbeard/clients/utorrent_client.py b/sickbeard/clients/utorrent_client.py index c19161a5d44a405edeabfb218ec64981bc6235fc..c0462f8df1f9ba57996ed18d36a5c6fda8957a31 100644 --- a/sickbeard/clients/utorrent_client.py +++ b/sickbeard/clients/utorrent_client.py @@ -19,7 +19,6 @@ import re import sickbeard -from sickbeard import logger from .generic import GenericClient diff --git a/sickbeard/common.py b/sickbeard/common.py index ebed6f136983aa5d7e82944b8a9605829d29e9c4..c55da517216505d3a13cd8b67bd417afface042a 100644 --- a/sickbeard/common.py +++ b/sickbeard/common.py @@ -291,7 +291,6 @@ class Quality: parser = createParser(filename) except Exception: parser = None - pass if not parser: return Quality.UNKNOWN @@ -300,7 +299,6 @@ class Quality: metadata = extractMetadata(parser) except Exception: metadata = None - pass try: parser.stream._input.close() @@ -410,7 +408,7 @@ class StatusStrings: SNATCHED_BEST: "Snatched (Best)"} def __getitem__(self, name): - if name in Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_BEST: + if name in Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_BEST + Quality.ARCHIVED: status, quality = Quality.splitCompositeStatus(name) if quality == Quality.NONE: return self.statusStrings[status] diff --git a/sickbeard/config.py b/sickbeard/config.py index d9151c5c3e2509e7194058e7945b794e6421ce1e..1ffddc1c3c8b3cfae1e22a28001bdfd117b30d1c 100644 --- a/sickbeard/config.py +++ b/sickbeard/config.py @@ -22,7 +22,6 @@ import re import urlparse import sickbeard -from sickbeard import encodingKludge as ek from sickbeard import helpers from sickbeard import logger from sickbeard import naming diff --git a/sickbeard/dailysearcher.py b/sickbeard/dailysearcher.py index 2a4d2b3b22fd11d2fec10b6f70268dd52a1e3753..2b5421056f09fc3ba553fa8dfb2a717959b4404b 100644 --- a/sickbeard/dailysearcher.py +++ b/sickbeard/dailysearcher.py @@ -20,7 +20,6 @@ from __future__ import with_statement import datetime import threading -import traceback import sickbeard from sickbeard import logger @@ -29,9 +28,6 @@ from sickbeard import common from sickbeard import helpers from sickbeard import exceptions from sickbeard import network_timezones -from sickbeard.exceptions import ex -from sickbeard.common import SKIPPED -from common import Quality, qualityPresetStrings, statusStrings class DailySearcher(): diff --git a/sickbeard/databases/mainDB.py b/sickbeard/databases/mainDB.py index b329762722ff529d3d6816e4826afdc5cb60294b..b24555e6a689a878e005f1a7363b147e58a83e63 100644 --- a/sickbeard/databases/mainDB.py +++ b/sickbeard/databases/mainDB.py @@ -43,6 +43,48 @@ class MainSanityCheck(db.DBSanityCheck): self.fix_invalid_airdates() self.fix_subtitles_codes() self.fix_show_nfo_lang() + self.convert_tvrage_to_tvdb() + + def convert_tvrage_to_tvdb(self): + logger.log(u'Checking for shows with tvrage id\'s, since tvrage is gone') + from sickbeard.indexers.indexer_config import INDEXER_TVRAGE + from sickbeard.indexers.indexer_config import INDEXER_TVDB + + sqlResults = self.connection.select( + "SELECT indexer_id, show_name FROM tv_shows WHERE indexer = %i" % INDEXER_TVRAGE) + + if sqlResults: + logger.log(u'Found %i shows with TVRage ID\', FIXING!' % len(sqlResults), logger.WARNING) + + for tvrage_show in sqlResults: + mapping = self.connection.select( + "SELECT mindexer_id FROM indexer_mapping WHERE indexer_id=%i AND indexer=%i AND mindexer=%i" % + (tvrage_show['indexer_id'], INDEXER_TVRAGE, INDEXER_TVDB) + ) + + if len(mapping) != 1: + logger.log( + u'Error mapping show from tvrage to tvdb for %s, found %i results. This show will no longer update!' % + (tvrage_show['show_name'], len(mapping)), logger.WARNING + ) + + continue + + logger.log('Mapping %s to tvdb id %i' % (tvrage_show['show_name'], mapping[0]['mindexer_id'])) + + self.connection.action( + "UPDATE tv_shows SET indexer=%i, indexer_id=%i WHERE indexer_id=%i" % + (INDEXER_TVDB, mapping[0]['mindexer_id'], tvrage_show['indexer_id']) + ) + + logger.log(u'Relinking episodes to show') + self.connection.action( + "UPDATE tv_episodes SET indexer=%i, showid=%i, indexerid=0 WHERE showid=%i" % + (INDEXER_TVDB, mapping[0]['mindexer_id'], tvrage_show['indexer_id']) + ) + + logger.log('Please perform a full update on %s' % tvrage_show['show_name'], logger.WARNING) + def fix_duplicate_shows(self, column='indexer_id'): diff --git a/sickbeard/db.py b/sickbeard/db.py index 798a319b5e65f3d474eabcf93329592f4bc7050a..25ac95dbd42c5a83c834f13df2d3ae88451358ac 100644 --- a/sickbeard/db.py +++ b/sickbeard/db.py @@ -23,7 +23,6 @@ import re import sqlite3 import time import threading -import chardet import sickbeard from sickbeard import encodingKludge as ek diff --git a/sickbeard/failed_history.py b/sickbeard/failed_history.py index 0fda51eabb9fe1fab1ada2f3de9bf0f0084fd292..cd64abd60e36055834bf9f5667a9bb3d4af81026 100644 --- a/sickbeard/failed_history.py +++ b/sickbeard/failed_history.py @@ -23,16 +23,17 @@ import datetime from sickbeard import db from sickbeard import logger from sickbeard.exceptions import ex, EpisodeNotFoundException -from sickbeard.history import dateFormat from sickbeard.common import Quality from sickbeard.common import WANTED, FAILED from sickbeard import encodingKludge as ek +from sickrage.show.History import History + def prepareFailedName(release): """Standardizes release name for failed DB""" fixed = urllib.unquote(release) - if (fixed.endswith(".nzb")): + if fixed.endswith(".nzb"): fixed = fixed.rpartition(".")[0] fixed = re.sub("[\.\-\+\ ]", "_", fixed) @@ -151,7 +152,7 @@ def markFailed(epObj): def logSnatch(searchResult): - logDate = datetime.datetime.today().strftime(dateFormat) + logDate = datetime.datetime.today().strftime(History.date_format) release = prepareFailedName(searchResult.name) providerClass = searchResult.provider @@ -182,7 +183,7 @@ def deleteLoggedSnatch(release, size, provider): def trimHistory(): myDB = db.DBConnection('failed.db') myDB.action("DELETE FROM history WHERE date < " + str( - (datetime.datetime.today() - datetime.timedelta(days=30)).strftime(dateFormat))) + (datetime.datetime.today() - datetime.timedelta(days=30)).strftime(History.date_format))) def findRelease(epObj): diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py index e41f48b4bc43e41c4bea7289ba09736e60893b55..e5d4067dfc355f82a3c63096301ae4a5c9671900 100644 --- a/sickbeard/helpers.py +++ b/sickbeard/helpers.py @@ -40,7 +40,6 @@ import errno import ast import operator import platform -import sys from contextlib import closing import sickbeard @@ -48,9 +47,7 @@ import sickbeard import adba import requests import certifi -import xmltodict -import subprocess try: from io import BytesIO as _StringIO @@ -67,7 +64,9 @@ except ImportError: from sickbeard.exceptions import MultipleShowObjectsException, ex from sickbeard import logger, classes -from sickbeard.common import USER_AGENT, cpu_presets, mediaExtensions, subtitleExtensions +from sickbeard.common import USER_AGENT +from sickbeard.common import mediaExtensions +from sickbeard.common import subtitleExtensions from sickbeard import db from sickbeard import encodingKludge as ek from sickbeard import notifiers @@ -160,6 +159,8 @@ def remove_non_release_groups(name): '- \{ www\.SceneTime\.com \}$': 'searchre', '^\{ www\.SceneTime\.com \} - ': 'searchre', '^\[ www\.TorrentDay\.com \] - ': 'searchre', + '^\]\.\[ www\.tensiontorrent.com \] - ': 'searchre', + '^\]\.\[www\.tensiontorrent.com\] - ': 'searchre', '^\[ www\.Cpasbien\.pw \] ': 'searchre', '^\[ www\.Cpasbien\.com \] ': 'searchre', '^\[www\.Cpasbien\.com\] ': 'searchre', @@ -647,7 +648,6 @@ def chmodAsParent(childPath): logger.DEBUG) except OSError: logger.log(u"Failed to set permission for %s to %o" % (childPath, childMode), logger.DEBUG) - pass def fixSetGroupID(childPath): @@ -736,7 +736,7 @@ def get_all_episodes_from_absolute_number(show, absolute_numbers, indexer_id=Non def sanitizeSceneName(name, anime=False): """ Takes a show name and returns the "scenified" version of it. - + anime: Some show have a ' in their name(Kuroko's Basketball) and is needed for search. Returns: A string containing the scene version of the show name given. @@ -1030,18 +1030,18 @@ def get_show(name, tryIndexers=False, trySceneExceptions=False): if cache: fromCache = True showObj = findCertainShow(sickbeard.showList, int(cache)) - - #try indexers + + #try indexers if not showObj and tryIndexers: showObj = findCertainShow(sickbeard.showList, searchIndexerForShowID(full_sanitizeSceneName(name), ui=classes.ShowListUI)[2]) - + #try scene exceptions if not showObj and trySceneExceptions: ShowID = sickbeard.scene_exceptions.get_scene_exception_by_name(name)[0] if ShowID: showObj = findCertainShow(sickbeard.showList, int(ShowID)) - + # add show to cache if showObj and not fromCache: sickbeard.name_cache.addNameToCache(name, showObj.indexerid) @@ -1069,7 +1069,7 @@ def is_hidden_folder(folder): except (AttributeError, AssertionError): result = False return result - + if ek.ek(os.path.isdir, folder): if is_hidden(folder): return True @@ -1095,7 +1095,7 @@ def validateShow(show, season=None, episode=None): if show.dvdorder != 0: lINDEXER_API_PARMS['dvdorder'] = True - + t = sickbeard.indexerApi(show.indexer).indexer(**lINDEXER_API_PARMS) if season is None and episode is None: return t @@ -1284,7 +1284,6 @@ def touchFile(fname, atime=None): logger.log(u"File air date stamping failed(Permission denied). Check permissions for file: %s" % fname, logger.ERROR) else: logger.log(u"File air date stamping failed. The error is: %s." % ex(e), logger.ERROR) - pass return False @@ -1364,6 +1363,10 @@ def getURL(url, post_data=None, params={}, headers={}, timeout=30, session=None, try: # decide if we get or post data to server if post_data: + for param in post_data: + if isinstance(post_data[param], unicode): + post_data[param] = post_data[param].encode('utf-8') + session.headers.update({'Content-Type': 'application/x-www-form-urlencoded'}) resp = session.post(url, data=post_data, timeout=timeout, allow_redirects=True, verify=session.verify) else: @@ -1457,6 +1460,8 @@ def download_file(url, filename, session=None, headers={}): def get_size(start_path='.'): + if not ek.ek(os.path.isdir, start_path): + return -1 total_size = 0 for dirpath, dirnames, filenames in ek.ek(os.walk, start_path): @@ -1531,16 +1536,16 @@ def verify_freespace(src, dest, oldfile=None): oldfile = [oldfile] logger.log("Trying to determine free space on destination drive", logger.DEBUG) - + if hasattr(os, 'statvfs'): # POSIX def disk_usage(path): st = os.statvfs(path) free = st.f_bavail * st.f_frsize return free - + elif os.name == 'nt': # Windows import sys - + def disk_usage(path): _, total, free = ctypes.c_ulonglong(), ctypes.c_ulonglong(), \ ctypes.c_ulonglong() @@ -1560,20 +1565,20 @@ def verify_freespace(src, dest, oldfile=None): if not ek.ek(os.path.isfile, src): logger.log("A path to a file is required for the source. " + src + " is not a file.", logger.WARNING) return True - + try: diskfree = disk_usage(dest) except: logger.log("Unable to determine free space, so I will assume there is enough.", logger.WARNING) return True - + neededspace = ek.ek(os.path.getsize, src) - + if oldfile: for file in oldfile: if ek.ek(os.path.isfile, file.location): diskfree += ek.ek(os.path.getsize, file.location) - + if diskfree > neededspace: return True else: @@ -1606,7 +1611,7 @@ def isFileLocked(file, writeLockCheck=False): 1. Checks if the file even exists 2. Attempts to open the file for reading. This will determine if the file has a write lock. Write locks occur when the file is being edited or copied to, e.g. a file copy destination - 3. If the readLockCheck parameter is True, attempts to rename the file. If this fails the + 3. If the readLockCheck parameter is True, attempts to rename the file. If this fails the file is open by some other process for reading. The file can be read, but not written to or deleted. @param file: the file being checked @@ -1619,7 +1624,7 @@ def isFileLocked(file, writeLockCheck=False): f.close() except IOError: return True - + if(writeLockCheck): lockFile = file + ".lckchk" if ek.ek(os.path.exists, lockFile): @@ -1630,7 +1635,7 @@ def isFileLocked(file, writeLockCheck=False): ek.ek(os.rename, lockFile, file) except (OSError, IOError): return True - + return False def getDiskSpaceUsage(diskPath=None): diff --git a/sickbeard/history.py b/sickbeard/history.py index 1889af2fdcbf16e2d242440a986ecd6af7c03d07..bb49785c18492853c7030f3382dc3d8480accf00 100644 --- a/sickbeard/history.py +++ b/sickbeard/history.py @@ -21,13 +21,11 @@ import datetime from sickbeard.common import SNATCHED, SUBTITLED, FAILED, Quality from sickbeard import encodingKludge as ek - - -dateFormat = "%Y%m%d%H%M%S" +from sickrage.show.History import History def _logHistoryItem(action, showid, season, episode, quality, resource, provider, version=-1): - logDate = datetime.datetime.today().strftime(dateFormat) + logDate = datetime.datetime.today().strftime(History.date_format) resource = ek.ss(resource) myDB = db.DBConnection() diff --git a/sickbeard/indexers/indexer_config.py b/sickbeard/indexers/indexer_config.py index f19cd68cd004cf8cab602667614ad3e93af7383b..ee1d77a95f8bbf28d97823005d9afc37b1c69400 100644 --- a/sickbeard/indexers/indexer_config.py +++ b/sickbeard/indexers/indexer_config.py @@ -1,8 +1,9 @@ from tvdb_api.tvdb_api import Tvdb -from tvrage_api.tvrage_api import TVRage import requests INDEXER_TVDB = 1 + +#Must keep INDEXER_TVRAGE = 2 initConfig = {} @@ -10,13 +11,15 @@ indexerConfig = {} initConfig['valid_languages'] = [ "da", "fi", "nl", "de", "it", "es", "fr", "pl", "hu", "el", "tr", - "ru", "he", "ja", "pt", "zh", "cs", "sl", "hr", "ko", "en", "sv", "no"] + "ru", "he", "ja", "pt", "zh", "cs", "sl", "hr", "ko", "en", "sv", "no" +] initConfig['langabbv_to_id'] = { 'el': 20, 'en': 7, 'zh': 27, 'it': 15, 'cs': 28, 'es': 16, 'ru': 22, 'nl': 13, 'pt': 26, 'no': 9, 'tr': 21, 'pl': 18, 'fr': 17, 'hr': 31, 'de': 14, 'da': 10, 'fi': 11, - 'hu': 19, 'ja': 25, 'he': 24, 'ko': 32, 'sv': 8, 'sl': 30} + 'hu': 19, 'ja': 25, 'he': 24, 'ko': 32, 'sv': 8, 'sl': 30 +} indexerConfig[INDEXER_TVDB] = { 'id': INDEXER_TVDB, @@ -25,17 +28,7 @@ indexerConfig[INDEXER_TVDB] = { 'api_params': {'apikey': 'F9C450E78D99172E', 'language': 'en', 'useZip': True, - }, - 'session': requests.Session() -} - -indexerConfig[INDEXER_TVRAGE] = { - 'id': INDEXER_TVRAGE, - 'name': 'TVRage', - 'module': TVRage, - 'api_params': {'apikey': 'Uhewg1Rr0o62fvZvUIZt', - 'language': 'en', - }, + }, 'session': requests.Session() } @@ -46,11 +39,3 @@ indexerConfig[INDEXER_TVDB]['icon'] = 'thetvdb16.png' indexerConfig[INDEXER_TVDB]['scene_loc'] = 'http://sickragetv.github.io/sb_tvdb_scene_exceptions/exceptions.txt' indexerConfig[INDEXER_TVDB]['show_url'] = 'http://thetvdb.com/?tab=series&id=' indexerConfig[INDEXER_TVDB]['base_url'] = 'http://thetvdb.com/api/%(apikey)s/series/' % indexerConfig[INDEXER_TVDB]['api_params'] - -# TVRAGE Indexer Settings -indexerConfig[INDEXER_TVRAGE]['trakt_id'] = 'tvrage_id' -indexerConfig[INDEXER_TVRAGE]['xem_origin'] = 'rage' -indexerConfig[INDEXER_TVRAGE]['icon'] = 'tvrage16.png' -indexerConfig[INDEXER_TVRAGE]['scene_loc'] = 'http://sickragetv.github.io/sr_tvrage_scene_exceptions/exceptions.txt' -indexerConfig[INDEXER_TVRAGE]['show_url'] = 'http://tvrage.com/shows/id-' -indexerConfig[INDEXER_TVRAGE]['base_url'] = 'http://tvrage.com/showinfo.php?key=%(apikey)s&sid=' % indexerConfig[INDEXER_TVRAGE]['api_params'] diff --git a/sickbeard/indexers/indexer_exceptions.py b/sickbeard/indexers/indexer_exceptions.py index 1ba41a722f248c410e5f6233bc2418839b3c3bee..221f529661f32dcadc798725956de5bf23873b97 100644 --- a/sickbeard/indexers/indexer_exceptions.py +++ b/sickbeard/indexers/indexer_exceptions.py @@ -10,10 +10,6 @@ __author__ = "echel0n" __version__ = "1.0" -from tvrage_api.tvrage_exceptions import \ - tvrage_exception, tvrage_attributenotfound, tvrage_episodenotfound, tvrage_error, \ - tvrage_seasonnotfound, tvrage_shownotfound, tvrage_showincomplete, tvrage_userabort - from tvdb_api.tvdb_exceptions import \ tvdb_exception, tvdb_attributenotfound, tvdb_episodenotfound, tvdb_error, \ tvdb_seasonnotfound, tvdb_shownotfound, tvdb_showincomplete, tvdb_userabort @@ -25,15 +21,12 @@ indexerExcepts = ["indexer_exception", "indexer_error", "indexer_userabort", "in tvdbExcepts = ["tvdb_exception", "tvdb_error", "tvdb_userabort", "tvdb_shownotfound", "tvdb_showincomplete", "tvdb_seasonnotfound", "tvdb_episodenotfound", "tvdb_attributenotfound"] -tvrageExcepts = ["tvdb_exception", "tvrage_error", "tvrage_userabort", "tvrage_shownotfound", "tvrage_showincomplete", - "tvrage_seasonnotfound", "tvrage_episodenotfound", "tvrage_attributenotfound"] - # link API exceptions to our exception handler -indexer_exception = tvdb_exception, tvrage_exception -indexer_error = tvdb_error, tvrage_error -indexer_userabort = tvdb_userabort, tvrage_userabort -indexer_attributenotfound = tvdb_attributenotfound, tvrage_attributenotfound -indexer_episodenotfound = tvdb_episodenotfound, tvrage_episodenotfound -indexer_seasonnotfound = tvdb_seasonnotfound, tvrage_seasonnotfound -indexer_shownotfound = tvdb_shownotfound, tvrage_shownotfound -indexer_showincomplete = tvdb_showincomplete, tvrage_showincomplete +indexer_exception = tvdb_exception +indexer_error = tvdb_error +indexer_userabort = tvdb_userabort +indexer_attributenotfound = tvdb_attributenotfound +indexer_episodenotfound = tvdb_episodenotfound +indexer_seasonnotfound = tvdb_seasonnotfound +indexer_shownotfound = tvdb_shownotfound +indexer_showincomplete = tvdb_showincomplete diff --git a/sickbeard/metadata/generic.py b/sickbeard/metadata/generic.py index 672faa8264bd876e71f6e7833b7b94ba597ca951..7597fd4633cb3dc463ab1aa33713b944767a43e6 100644 --- a/sickbeard/metadata/generic.py +++ b/sickbeard/metadata/generic.py @@ -371,7 +371,7 @@ class GenericMetadata(): def _get_episode_thumb_url(self, ep_obj): """ Returns the URL to use for downloading an episode's thumbnail. Uses - theTVDB.com and TVRage.com data. + theTVDB.com data. ep_obj: a TVEpisode object for which to grab the thumb URL """ @@ -852,7 +852,7 @@ class GenericMetadata(): result[season] = {} - # find the correct season in the TVDB and TVRAGE object and just copy the dict into our result dict + # find the correct season in the TVDB object and just copy the dict into our result dict for seasonArtID in seasonsArtObj.keys(): if int(seasonsArtObj[seasonArtID]['season']) == season and seasonsArtObj[seasonArtID]['language'] == sickbeard.INDEXER_DEFAULT_LANGUAGE: result[season][seasonArtID] = seasonsArtObj[seasonArtID]['_bannerpath'] @@ -907,7 +907,7 @@ class GenericMetadata(): result[season] = {} - # find the correct season in the TVDB and TVRAGE object and just copy the dict into our result dict + # find the correct season in the TVDB object and just copy the dict into our result dict for seasonArtID in seasonsArtObj.keys(): if int(seasonsArtObj[seasonArtID]['season']) == season and seasonsArtObj[seasonArtID]['language'] == sickbeard.INDEXER_DEFAULT_LANGUAGE: result[season][seasonArtID] = seasonsArtObj[seasonArtID]['_bannerpath'] @@ -963,7 +963,8 @@ class GenericMetadata(): if 'thetvdb.com' in epg_url: indexer = 1 elif 'tvrage' in epg_url: - indexer = 2 + logger.log(u"Invalid Indexer ID (" + str(indexer_id) + "), not using metadata file because it has TVRage info", logger.WARNING) + return empty_return except Exception, e: diff --git a/sickbeard/name_cache.py b/sickbeard/name_cache.py index 8f00b35b077a3c0479120d02733129dd594161af..0429bff57805e5387ad20078fb0bc7f948ac7a12 100644 --- a/sickbeard/name_cache.py +++ b/sickbeard/name_cache.py @@ -26,9 +26,9 @@ nameCacheLock = threading.Lock() def addNameToCache(name, indexer_id=0): """ Adds the show & tvdb id to the scene_names table in cache.db. - + name: The show name to cache - indexer_id: the TVDB and TVRAGE id that this show should be cached with (can be None/0 for unknown) + indexer_id: the TVDB id that this show should be cached with (can be None/0 for unknown) """ global nameCache @@ -45,10 +45,10 @@ def addNameToCache(name, indexer_id=0): def retrieveNameFromCache(name): """ Looks up the given name in the scene_names table in cache.db. - + name: The show name to look up. - - Returns: the TVDB and TVRAGE id that resulted from the cache lookup or None if the show wasn't found in the cache + + Returns: the TVDB id that resulted from the cache lookup or None if the show wasn't found in the cache """ global nameCache diff --git a/sickbeard/name_parser/parser.py b/sickbeard/name_parser/parser.py index 205698e6a88f75d1b0938aafe886df9bc23f47c5..76b142d14d1ce5959588f9398cea6e758d7cd7b7 100644 --- a/sickbeard/name_parser/parser.py +++ b/sickbeard/name_parser/parser.py @@ -21,7 +21,6 @@ from __future__ import with_statement import os import time import re -import datetime import os.path import regexes import sickbeard diff --git a/sickbeard/naming.py b/sickbeard/naming.py index 026b9688b7794427f0b51e10634da0413b8ddfbe..693d72ecb8dbe7ef41e5f9515dd1ad709d45d4e6 100644 --- a/sickbeard/naming.py +++ b/sickbeard/naming.py @@ -24,7 +24,7 @@ from sickbeard import encodingKludge as ek from sickbeard import tv from sickbeard import common from sickbeard import logger -from sickbeard.name_parser.parser import NameParser, InvalidNameException +from sickbeard.name_parser.parser import NameParser from common import Quality, DOWNLOADED diff --git a/sickbeard/network_timezones.py b/sickbeard/network_timezones.py index e62139089a9ae3ee43e2148b84ce6c78683e01ef..7f7c7d70676111570197307f77f88f64f75fcdb5 100644 --- a/sickbeard/network_timezones.py +++ b/sickbeard/network_timezones.py @@ -23,7 +23,6 @@ from sickbeard import db from sickbeard import helpers from sickbeard import logger from sickbeard import encodingKludge as ek -from sickbeard.exceptions import ex from os.path import basename, join, isfile import os import re diff --git a/sickbeard/notifiers/__init__.py b/sickbeard/notifiers/__init__.py index 6df210bcc70d570fe64c219577eaeb1224fd65a1..a113129ccf2a28c03ecddba1c8cdc289c3ed499c 100644 --- a/sickbeard/notifiers/__init__.py +++ b/sickbeard/notifiers/__init__.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU General Public License # along with SickRage. If not, see <http://www.gnu.org/licenses/>. -import sickbeard import kodi import plex diff --git a/sickbeard/notifiers/boxcar.py b/sickbeard/notifiers/boxcar.py index a60f7f624d1afb030fd23f16a80ad19dab48c0ed..9bff9bf834a2b277c17078a2f95efdb10f7e6247 100644 --- a/sickbeard/notifiers/boxcar.py +++ b/sickbeard/notifiers/boxcar.py @@ -31,7 +31,7 @@ API_URL = "https://boxcar.io/devices/providers/fWc4sgSmpcN6JujtBmR6/notification class BoxcarNotifier: def test_notify(self, boxcar_username): - return self._notify("This is a test notification from Sick Beard", "Test", boxcar_username, force=True) + return self._notifyBoxcar("This is a test notification from Sick Beard", "Test", boxcar_username, force=True) def _sendBoxcar(self, msg, title, email, subscribe=False): """ diff --git a/sickbeard/notifiers/boxcar2.py b/sickbeard/notifiers/boxcar2.py index 61593cbc1b4dd93fbad9768ef52a50895037b2f1..bcd5f6083d290c2c16120fa6133146036e7908e8 100644 --- a/sickbeard/notifiers/boxcar2.py +++ b/sickbeard/notifiers/boxcar2.py @@ -19,7 +19,6 @@ # along with SickRage. If not, see <http://www.gnu.org/licenses/>. import urllib, urllib2 -import time import sickbeard diff --git a/sickbeard/notifiers/emailnotify.py b/sickbeard/notifiers/emailnotify.py index 7e95b4d3348735673b8ad4c451abb6ccdd4bd16c..41b32f8d0049d7eed84d908e81225be57afd61dc 100644 --- a/sickbeard/notifiers/emailnotify.py +++ b/sickbeard/notifiers/emailnotify.py @@ -29,10 +29,9 @@ import re import sickbeard -from sickbeard import logger, common +from sickbeard import logger from sickbeard import db from sickbeard import encodingKludge as ek -from sickbeard.exceptions import ex class EmailNotifier: diff --git a/sickbeard/notifiers/emby.py b/sickbeard/notifiers/emby.py index ebc3b5ce746fbfb5cf6c3d506be33632fc235727..238be4b0fc7926b4ec0ead9e6c1314fed6aa2c08 100644 --- a/sickbeard/notifiers/emby.py +++ b/sickbeard/notifiers/emby.py @@ -22,7 +22,6 @@ import urllib2 import sickbeard from sickbeard import logger -from sickbeard import common from sickbeard.exceptions import ex @@ -45,7 +44,7 @@ class EMBYNotifier: if not host: host = sickbeard.EMBY_HOST if not emby_apikey: - username = sickbeard.EMBY_APIKEY + emby_apikey = sickbeard.EMBY_APIKEY url = 'http://%s/emby/Notifications/Admin' % (host) values = {'Name': 'SickRage', 'Description': message, 'ImageUrl': 'https://raw.githubusercontent.com/SiCKRAGETV/SickRage/master/gui/slick/images/sickrage-shark-mascot.png'} @@ -92,7 +91,8 @@ class EMBYNotifier: if show.indexer == 1: provider = 'tvdb' elif show.indexer == 2: - provider = 'tvrage' + logger.log(u'EMBY: TVRage Provider no longer valid', logger.WARNING) + return False else: logger.log(u'EMBY: Provider unknown', logger.WARNING) return False diff --git a/sickbeard/notifiers/freemobile.py b/sickbeard/notifiers/freemobile.py index 102da870f534ed1795fcde8db690f89923fa32e9..bed5f40a7126a696b707aa7355fc0e819cbd683d 100644 --- a/sickbeard/notifiers/freemobile.py +++ b/sickbeard/notifiers/freemobile.py @@ -17,14 +17,11 @@ # # You should have received a copy of the GNU General Public License # along with SickRage. If not, see <http://www.gnu.org/licenses/>. -import httplib -import urllib, urllib2 -import time +import urllib2 import sickbeard from sickbeard import logger from sickbeard.common import notifyStrings, NOTIFY_SNATCH, NOTIFY_DOWNLOAD, NOTIFY_SUBTITLE_DOWNLOAD, NOTIFY_GIT_UPDATE, NOTIFY_GIT_UPDATE_TEXT -from sickbeard.exceptions import ex class FreeMobileNotifier: def test_notify(self, id=None, apiKey=None): diff --git a/sickbeard/notifiers/libnotify.py b/sickbeard/notifiers/libnotify.py index d4dc7953598ddf0851d7db3b3ecc36f84fc475c1..dc86055c72e4fd45f3930676ecba1f6793cdf967 100644 --- a/sickbeard/notifiers/libnotify.py +++ b/sickbeard/notifiers/libnotify.py @@ -71,15 +71,15 @@ class LibnotifyNotifier: logger.log(u"Unable to import Notify from gi.repository. libnotify notifications won't work.", logger.ERROR) return False try: - import gobject + from gi.repository import GObject except ImportError: - logger.log(u"Unable to import gobject. We can't catch a GError in display.", logger.ERROR) + logger.log(u"Unable to import GObject from gi.repository. We can't catch a GError in display.", logger.ERROR) return False if not Notify.init('SickRage'): logger.log(u"Initialization of Notify failed. libnotify notifications won't work.", logger.ERROR) return False self.Notify = Notify - self.gobject = gobject + self.gobject = GObject return True def notify_snatch(self, ep_name): diff --git a/sickbeard/notifiers/nmjv2.py b/sickbeard/notifiers/nmjv2.py index ba6cec2d329d03ac344343972ddd9421fdde2baf..7c02bca1151e14407a3545cea2f9847eb7e04788 100644 --- a/sickbeard/notifiers/nmjv2.py +++ b/sickbeard/notifiers/nmjv2.py @@ -17,11 +17,9 @@ # You should have received a copy of the GNU General Public License # along with SickRage. If not, see <http://www.gnu.org/licenses/>. -import urllib, urllib2, xml.dom.minidom +import urllib2 from xml.dom.minidom import parseString import sickbeard -import telnetlib -import re import time from sickbeard import logger diff --git a/sickbeard/notifiers/pushbullet.py b/sickbeard/notifiers/pushbullet.py index bb3a1f4e06edc3aecd255fd8b1ef030b67ba186b..275578d47179b613ef8af0345b779ecb589a3fa0 100644 --- a/sickbeard/notifiers/pushbullet.py +++ b/sickbeard/notifiers/pushbullet.py @@ -18,7 +18,6 @@ # along with SickRage. If not, see <http://www.gnu.org/licenses/>. import socket -import base64 from httplib import HTTPSConnection, HTTPException import json from ssl import SSLError @@ -98,7 +97,6 @@ class PushbulletNotifier: data = json.dumps(data) http_handler.request(method, uri, body=data, headers={'Content-Type': 'application/json', 'Authorization': 'Bearer %s' % pushbullet_api}) - pass except (SSLError, HTTPException, socket.error): return False diff --git a/sickbeard/notifiers/trakt.py b/sickbeard/notifiers/trakt.py index 0f18a1484594b15ba0426f18986ba2198246e784..57a5c14e0908020e671f2f4ca7b3e6862895af48 100644 --- a/sickbeard/notifiers/trakt.py +++ b/sickbeard/notifiers/trakt.py @@ -62,7 +62,7 @@ class TraktNotifier: } ] } - + if trakt_id == 'tvdb_id': data['shows'][0]['ids']['tvdb'] = ep_obj.show.indexerid else: @@ -74,17 +74,17 @@ class TraktNotifier: # Add Season and Episode + Related Episodes data['shows'][0]['seasons']=[{'number': ep_obj.season,'episodes': [] }] - + for relEp_Obj in [ep_obj] + ep_obj.relatedEps: data['shows'][0]['seasons'][0]['episodes'].append({'number': relEp_Obj.episode}) - + if sickbeard.TRAKT_SYNC_WATCHLIST: if sickbeard.TRAKT_REMOVE_WATCHLIST: trakt_api.traktRequest("sync/watchlist/remove", data, method='POST') - + # update library trakt_api.traktRequest("sync/collection", data, method='POST') - + except (traktException, traktAuthException, traktServerBusy) as e: logger.log(u"Could not connect to Trakt service: %s" % ex(e), logger.WARNING) @@ -154,7 +154,7 @@ class TraktNotifier: } season['season'][0].update(episode) - + data['shows'][0].update(season) trakt_url = "sync/watchlist" @@ -206,20 +206,19 @@ class TraktNotifier: return post_data - def test_notify(self, username, disable_ssl, blacklist_name=None): + def test_notify(self, username, blacklist_name=None): """ Sends a test notification to trakt with the given authentication info and returns a boolean representing success. api: The api string to use username: The username to use - password: The password to use blacklist_name: slug of trakt list used to hide not interested show Returns: True if the request succeeded, False otherwise """ try: - trakt_api = TraktAPI(disable_ssl, sickbeard.TRAKT_TIMEOUT) + trakt_api = TraktAPI(sickbeard.SSL_VERIFY, sickbeard.TRAKT_TIMEOUT) trakt_api.validateAccount() if blacklist_name and blacklist_name is not None: trakt_lists = trakt_api.traktRequest("users/" + username + "/lists") diff --git a/sickbeard/notifiers/tweet.py b/sickbeard/notifiers/tweet.py index befee7936d9181519d0d5584c02dcb3c155ac938..f699f55aaa6b3ff751f68b764299654e557b0846 100644 --- a/sickbeard/notifiers/tweet.py +++ b/sickbeard/notifiers/tweet.py @@ -162,7 +162,7 @@ class TwitterNotifier: return False if sickbeard.TWITTER_USEDM and sickbeard.TWITTER_DMTO: - return self._send_dm(self, sickbeard.TWITTER_DMTO, prefix + ": " + message) + return self._send_dm(prefix + ": " + message) else: return self._send_tweet(prefix + ": " + message) diff --git a/sickbeard/nzbget.py b/sickbeard/nzbget.py index a1cb5746338d4994900d502efe2c89768a898acc..e4d914e36baefacab535b38289559cc1e85bd4ef 100644 --- a/sickbeard/nzbget.py +++ b/sickbeard/nzbget.py @@ -20,7 +20,6 @@ import httplib import datetime -import re import sickbeard diff --git a/sickbeard/postProcessor.py b/sickbeard/postProcessor.py index 480a40a9f045a863f18cfce4c1a15715de06d327..b57a4e3dbddbf4bc2652546509b113f00b8efaf4 100644 --- a/sickbeard/postProcessor.py +++ b/sickbeard/postProcessor.py @@ -36,8 +36,6 @@ from sickbeard import logger from sickbeard import notifiers from sickbeard import show_name_helpers from sickbeard import failed_history -from sickbeard import name_cache -from sickbeard import subtitles from sickbeard import encodingKludge as ek from sickbeard.exceptions import ex @@ -300,7 +298,7 @@ class PostProcessor(object): # check if file have subtitles language if os.path.splitext(cur_extension)[1][1:] in common.subtitleExtensions: cur_lang = os.path.splitext(cur_extension)[0] - if cur_lang in subtitles.wantedSubtitles(): + if cur_lang in subtitles.wantedLanguages(): cur_extension = cur_lang + os.path.splitext(cur_extension)[1] # replace .nfo with .nfo-orig to avoid conflicts diff --git a/sickbeard/processTV.py b/sickbeard/processTV.py index aebf820a04efb90bf5d2a5070bd42d5b43058fe8..821992a42f879bedb14776ae5bc7e83283c1c6f8 100644 --- a/sickbeard/processTV.py +++ b/sickbeard/processTV.py @@ -32,7 +32,7 @@ from sickbeard import common from sickbeard import failedProcessor -from unrar2 import RarFile, RarInfo +from unrar2 import RarFile from unrar2.rar_exceptions import * import shutil @@ -457,7 +457,6 @@ def already_postprocessed(dirName, videofile, force, result): parse_result = np.parse(dirName) except: #ignore the exception, because we kind of expected it, but create parse_result anyway so we can perform a check on it. parse_result = False - pass search_sql = "SELECT tv_episodes.indexerid, history.resource FROM tv_episodes INNER JOIN history ON history.showid=tv_episodes.showid" #This part is always the same diff --git a/sickbeard/properFinder.py b/sickbeard/properFinder.py index 1d66ba6aaacbb70e33cc1e6f0a3d8ab368794459..2b98188476a078c9ab0084ef7b5aeeae6cb2a231 100644 --- a/sickbeard/properFinder.py +++ b/sickbeard/properFinder.py @@ -32,9 +32,9 @@ from sickbeard import exceptions from sickbeard.exceptions import ex from sickbeard import helpers, logger from sickbeard import search -from sickbeard import history from sickbeard.common import DOWNLOADED, SNATCHED, SNATCHED_PROPER, Quality, cpu_presets +from sickrage.show.History import History from name_parser.parser import NameParser, InvalidNameException, InvalidShowException @@ -213,7 +213,7 @@ class ProperFinder(): "WHERE showid = ? AND season = ? AND episode = ? AND quality = ? AND date >= ? " + "AND action IN (" + ",".join([str(x) for x in Quality.SNATCHED + Quality.DOWNLOADED]) + ")", [curProper.indexerid, curProper.season, curProper.episode, curProper.quality, - historyLimit.strftime(history.dateFormat)]) + historyLimit.strftime(History.date_format)]) # if we didn't download this episode in the first place we don't know what quality to use for the proper so we can't do it if len(historyResults) == 0: diff --git a/sickbeard/providers/__init__.py b/sickbeard/providers/__init__.py index 01bacf5557a26dc76c8416a45c94462bf5d106ef..6121ba7ee2bd6a4d0e6382236a1193e226a97635 100644 --- a/sickbeard/providers/__init__.py +++ b/sickbeard/providers/__init__.py @@ -55,7 +55,6 @@ __all__ = ['womble', ] import sickbeard -import generic from sickbeard import logger from os import sys diff --git a/sickbeard/providers/alpharatio.py b/sickbeard/providers/alpharatio.py index 5cb040fd6733417d70fb1a671404094a4e112338..8a3a43d4b0fbe7be2a3d6c4b846526df645c4134 100644 --- a/sickbeard/providers/alpharatio.py +++ b/sickbeard/providers/alpharatio.py @@ -19,23 +19,19 @@ import re import traceback import datetime -import urlparse import sickbeard import generic -from sickbeard.common import Quality, cpu_presets +from sickbeard.common import Quality from sickbeard import logger from sickbeard import tvcache from sickbeard import db from sickbeard import classes from sickbeard import helpers from sickbeard import show_name_helpers -from sickbeard.common import Overview from sickbeard.exceptions import ex -from sickbeard import clients import requests -from requests import exceptions from sickbeard.bs4_parser import BS4Parser from unidecode import unidecode from sickbeard.helpers import sanitizeSceneName @@ -89,7 +85,7 @@ class AlphaRatioProvider(generic.TorrentProvider): self.session = requests.Session() try: - response = self.session.post(self.urls['login'], data=login_params, timeout=30) + response = self.getURL(self.urls['login'], post_data=login_params, timeout=30) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: logger.log(u'Unable to connect to ' + self.name + ' provider: ' + ex(e), logger.ERROR) return False diff --git a/sickbeard/providers/animenzb.py b/sickbeard/providers/animenzb.py index 13f93517db8876eb0edaf8602b3758307390fc12..b6fac166607939f9152afa1265fb694c10792cda 100644 --- a/sickbeard/providers/animenzb.py +++ b/sickbeard/providers/animenzb.py @@ -19,15 +19,14 @@ import urllib import datetime -import sickbeard import generic -from sickbeard import classes, show_name_helpers, helpers +from sickbeard import classes +from sickbeard import show_name_helpers -from sickbeard import exceptions, logger +from sickbeard import logger from sickbeard.common import * from sickbeard import tvcache -from dateutil.parser import parse as parseDate class animenzb(generic.NZBProvider): diff --git a/sickbeard/providers/binsearch.py b/sickbeard/providers/binsearch.py index d9afe715b297358064dbd050c5052fb53ad14e93..6dba1f55289623bd46f8a89c5f6e4844e833a74f 100644 --- a/sickbeard/providers/binsearch.py +++ b/sickbeard/providers/binsearch.py @@ -16,15 +16,12 @@ # along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. import urllib import re -import time -import sickbeard import generic from sickbeard import logger from sickbeard import tvcache -from sickbeard.exceptions import AuthException class BinSearchProvider(generic.NZBProvider): def __init__(self): diff --git a/sickbeard/providers/bitsoup.py b/sickbeard/providers/bitsoup.py index f6f24bbe525e54b5ba11c066b97af36fcc0d4165..4aa30ea2de537544c0552e98c04e1b5c2813292b 100644 --- a/sickbeard/providers/bitsoup.py +++ b/sickbeard/providers/bitsoup.py @@ -22,7 +22,6 @@ import datetime import sickbeard import generic import requests -from requests import exceptions import urllib from sickbeard.common import Quality @@ -91,7 +90,7 @@ class BitSoupProvider(generic.TorrentProvider): self.session = requests.Session() try: - response = self.session.post(self.urls['login'], data=login_params, timeout=30) + response = self.getURL(self.urls['login'], post_data=login_params, timeout=30) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: logger.log(u'Unable to connect to ' + self.name + ' provider: ' + ex(e), logger.ERROR) return False diff --git a/sickbeard/providers/bluetigers.py b/sickbeard/providers/bluetigers.py index 4afa66425632f482190a52e5dc5ffc6817a3b4e9..d15d0a7e1f9648e0b6fa5479cce2aa5897592a31 100644 --- a/sickbeard/providers/bluetigers.py +++ b/sickbeard/providers/bluetigers.py @@ -20,13 +20,11 @@ import traceback import re import datetime -import time from requests.auth import AuthBase import sickbeard import generic import urllib import requests -from requests import exceptions from sickbeard.bs4_parser import BS4Parser from sickbeard.common import Quality from sickbeard import logger @@ -90,7 +88,7 @@ class BLUETIGERSProvider(generic.TorrentProvider): logger.log('Performing authentication to BLUETIGERS', logger.DEBUG) try: - response = self.session.post(self.urls['login'], data=login_params, timeout=30) + response = self.getURL(self.urls['login'], post_data=login_params, timeout=30) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: logger.log(u'Unable to connect to ' + self.name + ' provider: ' + ex(e), logger.ERROR) return False diff --git a/sickbeard/providers/btdigg.py b/sickbeard/providers/btdigg.py index 944d102b70ee5a0ba0a3dc27514339c4f31aa486..c31bf240616f622dd76ee71030483264089eb8ba 100644 --- a/sickbeard/providers/btdigg.py +++ b/sickbeard/providers/btdigg.py @@ -21,14 +21,11 @@ import datetime import generic -from sickbeard.common import Quality from sickbeard import logger from sickbeard import tvcache -from sickbeard import helpers from sickbeard import show_name_helpers from sickbeard import db from sickbeard.common import WANTED -from sickbeard.exceptions import ex from sickbeard.config import naming_ep_type from sickbeard.helpers import sanitizeSceneName diff --git a/sickbeard/providers/btn.py b/sickbeard/providers/btn.py index 2268c2dbe69d9582b408304c38bad40073268d1d..5d1680eae3aaef503e2808d49ceb8c85eb65aa7d 100644 --- a/sickbeard/providers/btn.py +++ b/sickbeard/providers/btn.py @@ -30,7 +30,8 @@ from sickbeard import logger from sickbeard import tvcache from sickbeard.helpers import sanitizeSceneName from sickbeard.exceptions import ex, AuthException -from sickbeard.common import MULTI_EP_RESULT, SEASON_RESULT, USER_AGENT +from sickbeard.common import MULTI_EP_RESULT +from sickbeard.common import SEASON_RESULT from sickbeard import db from sickbeard.name_parser.parser import NameParser, InvalidNameException, InvalidShowException from sickbeard.common import Quality, cpu_presets @@ -148,7 +149,7 @@ class BTNProvider(generic.TorrentProvider): except jsonrpclib.jsonrpc.ProtocolError, error: if error.message == 'Call Limit Exceeded': - logger.log(u"You have exceeded the limit of 150 calls per hour, per API key which is unique to your user account.", logger.WARNING) + logger.log(u"You have exceeded the limit of 150 calls per hour, per API key which is unique to your user account.", logger.WARNING) else: logger.log(u"JSON-RPC protocol error while accessing " + self.name + ": " + ex(error), logger.ERROR) parsedJSON = {'api-error': ex(error)} @@ -220,14 +221,11 @@ class BTNProvider(generic.TorrentProvider): if ep_obj.show.indexer == 1: current_params['tvdb'] = ep_obj.show.indexerid search_params.append(current_params) - elif ep_obj.show.indexer == 2: - current_params['tvrage'] = ep_obj.show.indexerid - search_params.append(current_params) else: name_exceptions = list( set(scene_exceptions.get_scene_exceptions(ep_obj.show.indexerid) + [ep_obj.show.name])) for name in name_exceptions: - # Search by name if we don't have tvdb or tvrage id + # Search by name if we don't have tvdb id current_params['series'] = sanitizeSceneName(name) search_params.append(current_params) @@ -258,9 +256,6 @@ class BTNProvider(generic.TorrentProvider): if ep_obj.show.indexer == 1: search_params['tvdb'] = ep_obj.show.indexerid to_return.append(search_params) - elif ep_obj.show.indexer == 2: - search_params['tvrage'] = ep_obj.show.indexerid - to_return.append(search_params) else: # add new query string for every exception name_exceptions = list( @@ -380,7 +375,7 @@ class BTNProvider(generic.TorrentProvider): addCacheEntry = False if not (showObj.air_by_date or showObj.sports): - if search_mode == 'sponly': + if search_mode == 'sponly': if len(parse_result.episode_numbers): logger.log( u"This is supposed to be a season pack search but the result " + title + " is not a valid season pack, skipping it", diff --git a/sickbeard/providers/cpasbien.py b/sickbeard/providers/cpasbien.py index 70bbc0244615d3f39b7c7cf887fb84988613b705..984428d5a9fd9ca66c057fa035d9dad78f575a9e 100644 --- a/sickbeard/providers/cpasbien.py +++ b/sickbeard/providers/cpasbien.py @@ -23,21 +23,16 @@ import datetime import sickbeard import generic -import requests -from requests import exceptions -from sickbeard.common import USER_AGENT, Quality, cpu_presets +from sickbeard.common import Quality from sickbeard import logger -from sickbeard import tvcache from sickbeard import show_name_helpers from sickbeard.bs4_parser import BS4Parser from sickbeard import db from sickbeard import helpers from sickbeard import classes -from sickbeard.helpers import sanitizeSceneName, arithmeticEval -from sickbeard.exceptions import ex +from sickbeard.helpers import sanitizeSceneName -import cookielib class CpasbienProvider(generic.TorrentProvider): diff --git a/sickbeard/providers/fnt.py b/sickbeard/providers/fnt.py index 28fa138c66ee52868c8cf90021a5b206e5846ed0..120f26ef3ceb60c4df0368067acbfa32c744db2c 100644 --- a/sickbeard/providers/fnt.py +++ b/sickbeard/providers/fnt.py @@ -20,13 +20,11 @@ import traceback import re import datetime -import time from requests.auth import AuthBase import sickbeard import generic import urllib import requests -from requests import exceptions from sickbeard.bs4_parser import BS4Parser from sickbeard.common import Quality from sickbeard import logger @@ -89,7 +87,7 @@ class FNTProvider(generic.TorrentProvider): logger.log('Performing authentication to FNT', logger.DEBUG) try: - response = self.session.post(self.urls['login'], data=login_params, timeout=30) + response = self.getURL(self.urls['login'], post_data=login_params, timeout=30) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: logger.log(u'Unable to connect to ' + self.name + ' provider: ' + ex(e), logger.ERROR) return False diff --git a/sickbeard/providers/frenchtorrentdb.py b/sickbeard/providers/frenchtorrentdb.py index 1611a3cf2d27352060c92f0d369117f6e329d581..27de073dc84d710c839b0e76c83dbf25d3bfd240 100644 --- a/sickbeard/providers/frenchtorrentdb.py +++ b/sickbeard/providers/frenchtorrentdb.py @@ -17,21 +17,17 @@ # You should have received a copy of the GNU General Public License # along with Sick Beard. If not, see <http://www.gnu.org/licenses/>. -import traceback import re import datetime -import time from requests.auth import AuthBase import sickbeard import generic import urllib import urllib2 -import requests import json import cookielib -from requests import exceptions from sickbeard.bs4_parser import BS4Parser from sickbeard.common import Quality from sickbeard import logger @@ -42,7 +38,6 @@ from sickbeard import helpers from sickbeard import classes from unidecode import unidecode from sickbeard.helpers import sanitizeSceneName -from sickbeard.exceptions import ex class FrenchTorrentDBProvider(generic.TorrentProvider): diff --git a/sickbeard/providers/freshontv.py b/sickbeard/providers/freshontv.py index 2f3a9b277ea19b979877b9f3fdb9a0696a158108..d2df9f91e39ad257b9d9de74e56d33dfad6fba32 100644 --- a/sickbeard/providers/freshontv.py +++ b/sickbeard/providers/freshontv.py @@ -20,10 +20,9 @@ import re import traceback import datetime import time -import urlparse import sickbeard import generic -from sickbeard.common import Quality, cpu_presets +from sickbeard.common import Quality from sickbeard import logger from sickbeard import tvcache from sickbeard import db @@ -31,9 +30,7 @@ from sickbeard import classes from sickbeard import helpers from sickbeard import show_name_helpers from sickbeard.exceptions import ex, AuthException -from sickbeard import clients import requests -from requests import exceptions from sickbeard.bs4_parser import BS4Parser from unidecode import unidecode from sickbeard.helpers import sanitizeSceneName @@ -104,7 +101,7 @@ class FreshOnTVProvider(generic.TorrentProvider): self.session = requests.Session() try: - response = self.session.post(self.urls['login'], data=login_params, timeout=30) + response = self.getURL(self.urls['login'], post_data=login_params, timeout=30) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError) as e: logger.log(u'Unable to connect to ' + self.name + ' provider: ' + ex(e), logger.ERROR) return False diff --git a/sickbeard/providers/hdbits.py b/sickbeard/providers/hdbits.py index 36e495a7e9da82d3a8cc620c7b058c9fc51ffd81..e0472c7f3b773cc06e41ef38a3f96477b259d5f7 100644 --- a/sickbeard/providers/hdbits.py +++ b/sickbeard/providers/hdbits.py @@ -16,7 +16,6 @@ import datetime import urllib -import sickbeard import generic from sickbeard import classes diff --git a/sickbeard/providers/hdtorrents.py b/sickbeard/providers/hdtorrents.py index 82900b55ca36c5bd5b2bc55b29fb321e4a423d9d..03582e952476b290caa579fd8134a465684723a3 100644 --- a/sickbeard/providers/hdtorrents.py +++ b/sickbeard/providers/hdtorrents.py @@ -18,12 +18,10 @@ # along with SickRage. If not, see <http://www.gnu.org/licenses/>. import re -import traceback -import urlparse import sickbeard import generic import urllib -from sickbeard.common import Quality, cpu_presets +from sickbeard.common import Quality from sickbeard import logger from sickbeard import tvcache from sickbeard import db @@ -31,13 +29,10 @@ from sickbeard import classes from sickbeard import helpers from sickbeard import show_name_helpers from sickbeard.exceptions import ex, AuthException -from sickbeard import clients import requests -from requests import exceptions from bs4 import BeautifulSoup as soup from unidecode import unidecode from sickbeard.helpers import sanitizeSceneName -from requests.auth import AuthBase from datetime import datetime class HDTorrentsProvider(generic.TorrentProvider): @@ -90,7 +85,7 @@ class HDTorrentsProvider(generic.TorrentProvider): 'submit': 'Confirm'} try: - response = self.session.post(self.urls['login'], data=login_params, timeout=30) + response = self.getURL(self.urls['login'], post_data=login_params, timeout=30) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: logger.log(u'Unable to connect to ' + self.name + ' provider: ' + ex(e), logger.ERROR) return False @@ -202,7 +197,7 @@ class HDTorrentsProvider(generic.TorrentProvider): # Skip torrents released before the episode aired (fakes) if re.match('..:..:.. ..:..:....', cells[6].text): if (datetime.strptime(cells[6].text, '%H:%M:%S %m/%d/%Y') - - datetime.combine(ep_obj.airdate, datetime.min.time())).days < 0: + datetime.combine(epObj.airdate, datetime.min.time())).days < 0: continue # Need size for failed downloads handling diff --git a/sickbeard/providers/hounddawgs.py b/sickbeard/providers/hounddawgs.py index 285a9e675120318a195a8f7c7752814e5b12cd7b..74205967035c0a0e4a10997674b77e64edf3e1c0 100644 --- a/sickbeard/providers/hounddawgs.py +++ b/sickbeard/providers/hounddawgs.py @@ -19,22 +19,18 @@ import re import traceback import datetime -import urlparse import sickbeard import generic import urllib -from sickbeard.common import Quality, cpu_presets +from sickbeard.common import Quality from sickbeard import logger from sickbeard import tvcache from sickbeard import db from sickbeard import classes from sickbeard import helpers from sickbeard import show_name_helpers -from sickbeard.common import Overview from sickbeard.exceptions import ex -from sickbeard import clients import requests -from requests import exceptions from sickbeard.bs4_parser import BS4Parser from unidecode import unidecode from sickbeard.helpers import sanitizeSceneName @@ -89,7 +85,7 @@ class HoundDawgsProvider(generic.TorrentProvider): try: self.session.get(self.urls['base_url'], timeout=30) - response = self.session.post(self.urls['login'], data=login_params, timeout=30) + response = self.getURL(self.urls['login'], post_data=login_params, timeout=30) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: logger.log(u'Unable to connect to ' + self.name + ' provider: ' + ex(e), logger.ERROR) return False diff --git a/sickbeard/providers/iptorrents.py b/sickbeard/providers/iptorrents.py index f81b994dea260a0cac5a81e511478d6eca2f9e3c..53f7ff6689d927aab80bd74db9a2f01d609ace4c 100644 --- a/sickbeard/providers/iptorrents.py +++ b/sickbeard/providers/iptorrents.py @@ -19,7 +19,6 @@ import re import traceback import datetime -import urlparse import itertools import sickbeard @@ -33,9 +32,7 @@ from sickbeard import classes from sickbeard import helpers from sickbeard import show_name_helpers from sickbeard.exceptions import ex, AuthException -from sickbeard import clients import requests -from requests import exceptions from sickbeard.bs4_parser import BS4Parser from unidecode import unidecode from sickbeard.helpers import sanitizeSceneName @@ -94,7 +91,7 @@ class IPTorrentsProvider(generic.TorrentProvider): try: self.session.get(self.urls['login'], timeout=30) - response = self.session.post(self.urls['login'], data=login_params, timeout=30) + response = self.getURL(self.urls['login'], post_data=login_params, timeout=30) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: logger.log(u'Unable to connect to ' + self.name + ' provider: ' + ex(e), logger.ERROR) return False diff --git a/sickbeard/providers/kat.py b/sickbeard/providers/kat.py index 80d85e8918e407cd3254adc57ef86a9bb941047e..9560fc580999d5fea34414d7e7a46abd8e8dcc25 100644 --- a/sickbeard/providers/kat.py +++ b/sickbeard/providers/kat.py @@ -19,7 +19,6 @@ from __future__ import with_statement -import os import traceback import urllib import re @@ -29,14 +28,12 @@ import sickbeard import generic from sickbeard.common import Quality -from sickbeard.name_parser.parser import NameParser, InvalidNameException, InvalidShowException from sickbeard import logger from sickbeard import tvcache from sickbeard import helpers from sickbeard import db from sickbeard import classes from sickbeard.show_name_helpers import allPossibleShowNames, sanitizeSceneName -from sickbeard.bs4_parser import BS4Parser from unidecode import unidecode diff --git a/sickbeard/providers/libertalia.py b/sickbeard/providers/libertalia.py index 9f558b22f0cb4982fd0488ab202a669476db1668..5b57592993f17cb4fbd80ec7fdb22ce23ddf8e35 100644 --- a/sickbeard/providers/libertalia.py +++ b/sickbeard/providers/libertalia.py @@ -19,24 +19,18 @@ # You should have received a copy of the GNU General Public License # along with SickRage. If not, see <http://www.gnu.org/licenses/>. -import traceback import re import datetime -import time -from requests.auth import AuthBase import sickbeard import generic import requests -import json import cookielib import urllib -from requests import exceptions from sickbeard.bs4_parser import BS4Parser from sickbeard.common import Quality from sickbeard import logger -from sickbeard import tvcache from sickbeard import show_name_helpers from sickbeard import db from sickbeard import helpers @@ -130,18 +124,15 @@ class LibertaliaProvider(generic.TorrentProvider): if any(requests.utils.dict_from_cookiejar(self.session.cookies).values()): return True - header = {'user-agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.8 (KHTML, like Gecko) Chrome/17.0.940.0 Safari/535.8'} + self.headers.update({'user-agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.8 (KHTML, like Gecko) Chrome/17.0.940.0 Safari/535.8'}) login_params = {'username': self.username, 'password': self.password } - if not self.session: - self.session = requests.Session() - logger.log('Performing authentication to Libertalia', logger.DEBUG) try: - response = self.session.post(self.url + '/login.php', data=login_params, timeout=30, headers=header) + response = self.getURL(self.url + '/login.php', post_data=login_params, timeout=30) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: logger.log(u'Unable to connect to ' + self.name + ' provider: ' + ex(e), logger.ERROR) return False diff --git a/sickbeard/providers/morethantv.py b/sickbeard/providers/morethantv.py index 1ca452a155ad7fcf835a43f7063c4a4d9aabba27..dab0d4c37d565e644965498c36f5becddb5d63aa 100644 --- a/sickbeard/providers/morethantv.py +++ b/sickbeard/providers/morethantv.py @@ -23,10 +23,9 @@ import re import traceback import datetime -import urlparse import sickbeard import generic -from sickbeard.common import Quality, cpu_presets +from sickbeard.common import Quality from sickbeard import logger from sickbeard import tvcache from sickbeard import db @@ -34,9 +33,7 @@ from sickbeard import classes from sickbeard import helpers from sickbeard import show_name_helpers from sickbeard.exceptions import ex, AuthException -from sickbeard import clients import requests -from requests import exceptions from sickbeard.bs4_parser import BS4Parser from unidecode import unidecode from sickbeard.helpers import sanitizeSceneName @@ -107,7 +104,7 @@ class MoreThanTVProvider(generic.TorrentProvider): self.session = requests.Session() try: - response = self.session.post(self.urls['login'], data=login_params, timeout=30) + response = self.getURL(self.urls['login'], post_data=login_params, timeout=30) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: logger.log(u'Unable to connect to ' + self.name + ' provider: ' + ex(e), logger.ERROR) return False diff --git a/sickbeard/providers/newznab.py b/sickbeard/providers/newznab.py index db73bd7e944d9e4419421c3834cbf2298682875c..838bedff56b435c348180d983ef2f902d1b920f8 100644 --- a/sickbeard/providers/newznab.py +++ b/sickbeard/providers/newznab.py @@ -145,13 +145,6 @@ class NewznabProvider(generic.NZBProvider): else: cur_params['season'] = str(ep_obj.scene_season) - # search - rid = helpers.mapIndexersToShow(ep_obj.show)[2] - if rid: - cur_params['rid'] = rid - cur_params['attrs'] = "rageid" - to_return.append(dict(cur_params)) - if 'rid' in cur_params: cur_params.pop('rid') cur_params.pop('attrs') @@ -186,13 +179,6 @@ class NewznabProvider(generic.NZBProvider): params['season'] = ep_obj.scene_season params['ep'] = ep_obj.scene_episode - # search - rid = helpers.mapIndexersToShow(ep_obj.show)[2] - if rid: - params['rid'] = rid - params['attrs'] = "rageid" - to_return.append(dict(params)) - if 'rid' in params: params.pop('rid') params.pop('attrs') @@ -416,10 +402,6 @@ class NewznabCache(tvcache.TVCache): return None tvrageid = 0 - for attr in item['newznab_attr'] if isinstance(item['newznab_attr'], list) else [item['newznab_attr']]: - if attr['name'] == 'tvrageid' or attr['name'] == 'rageid': - tvrageid = int(attr['value'] or 0) - break logger.log(u"Attempting to add item from RSS to cache: " + title, logger.DEBUG) return self._addCacheEntry(title, url, indexer_id=tvrageid) diff --git a/sickbeard/providers/nextgen.py b/sickbeard/providers/nextgen.py index 6bd9ef3c327ebf1caf5a3e40627899083dfd9b4d..c28d6a8c94851cf9135c97c384138434021a92d9 100644 --- a/sickbeard/providers/nextgen.py +++ b/sickbeard/providers/nextgen.py @@ -17,26 +17,20 @@ # along with SickRage. If not, see <http://www.gnu.org/licenses/>. import traceback -import urllib2 import urllib import time import re import datetime -import urlparse import sickbeard import generic -from sickbeard.common import Quality, cpu_presets +from sickbeard.common import Quality from sickbeard import logger from sickbeard import tvcache from sickbeard import db from sickbeard import classes from sickbeard import helpers from sickbeard import show_name_helpers -from sickbeard.common import Overview -from sickbeard.exceptions import ex -from sickbeard import clients import requests -from requests import exceptions from sickbeard.bs4_parser import BS4Parser from sickbeard.helpers import sanitizeSceneName @@ -121,7 +115,7 @@ class NextGenProvider(generic.TorrentProvider): data = self.session.get(self.urls['login_page']) with BS4Parser(data.content.decode('iso-8859-1')) as bs: csrfraw = bs.find('form', attrs={'id': 'login'})['action'] - output = self.session.post(self.urls['base_url'] + csrfraw, data=login_params) + output = self.getURL(self.urls['base_url'] + csrfraw, post_data=login_params) if self.loginSuccess(output): self.last_login_check = now diff --git a/sickbeard/providers/nyaatorrents.py b/sickbeard/providers/nyaatorrents.py index 2e8a4ebd6a3f8ec1eb9bb9ece6886988e4f18614..a49627bbe9180b3f70aacf94c8a6dec9c73b4870 100644 --- a/sickbeard/providers/nyaatorrents.py +++ b/sickbeard/providers/nyaatorrents.py @@ -19,7 +19,6 @@ import urllib import re -import sickbeard import generic from sickbeard import show_name_helpers diff --git a/sickbeard/providers/omgwtfnzbs.py b/sickbeard/providers/omgwtfnzbs.py index a1e5b5d89954ea3b906a5c4d4da9d4a9bdbb978f..71a7733214573399d6dafcf554f47c76984cc6ed 100644 --- a/sickbeard/providers/omgwtfnzbs.py +++ b/sickbeard/providers/omgwtfnzbs.py @@ -22,23 +22,12 @@ import sickbeard import generic from sickbeard import tvcache -from sickbeard import helpers from sickbeard import classes from sickbeard import logger -from sickbeard.exceptions import ex, AuthException +from sickbeard.exceptions import AuthException from sickbeard import show_name_helpers from datetime import datetime -try: - import xml.etree.cElementTree as etree -except ImportError: - import elementtree.ElementTree as etree - -try: - import json -except ImportError: - import simplejson as json - class OmgwtfnzbsProvider(generic.NZBProvider): def __init__(self): diff --git a/sickbeard/providers/rarbg.py b/sickbeard/providers/rarbg.py index 3a32bbaaeb8e292fdc0ef8cb5490e51b7a20461f..aecde2ee7e1d52ed5fe9bbb9f3ffbc84eba48dfd 100644 --- a/sickbeard/providers/rarbg.py +++ b/sickbeard/providers/rarbg.py @@ -20,28 +20,23 @@ import traceback import re import datetime -import urllib import generic import datetime import json import time -import requests -from requests import exceptions import sickbeard from sickbeard.common import Quality, USER_AGENT from sickbeard import logger from sickbeard import tvcache from sickbeard import show_name_helpers -from sickbeard.bs4_parser import BS4Parser from sickbeard import db from sickbeard import helpers from sickbeard import classes from sickbeard.exceptions import ex -from sickbeard.helpers import sanitizeSceneName from requests.exceptions import RequestException -from sickbeard.indexers.indexer_config import INDEXER_TVDB,INDEXER_TVRAGE +from sickbeard.indexers.indexer_config import INDEXER_TVDB class GetOutOfLoop(Exception): @@ -68,7 +63,6 @@ class RarbgProvider(generic.TorrentProvider): 'listing': u'http://torrentapi.org/pubapi_v2.php?mode=list&app_id=sickrage', 'search': u'http://torrentapi.org/pubapi_v2.php?mode=search&app_id=sickrage&search_string={search_string}', 'search_tvdb': u'http://torrentapi.org/pubapi_v2.php?mode=search&app_id=sickrage&search_tvdb={tvdb}&search_string={search_string}', - 'search_tvrage': u'http://torrentapi.org/pubapi_v2.php?mode=search&app_id=sickrage&search_tvrage={tvrage}&search_string={search_string}', 'api_spec': u'https://rarbg.com/pubapi/apidocs.txt', } @@ -205,15 +199,11 @@ class RarbgProvider(generic.TorrentProvider): elif mode == 'Season': if ep_indexer == INDEXER_TVDB: searchURL = self.urls['search_tvdb'].format(search_string=search_string, tvdb=ep_indexerid) + self.defaultOptions - elif ep_indexer == INDEXER_TVRAGE: - searchURL = self.urls['search_tvrage'].format(search_string=search_string, tvrage=ep_indexerid) + self.defaultOptions else: searchURL = self.urls['search'].format(search_string=search_string) + self.defaultOptions elif mode == 'Episode': if ep_indexer == INDEXER_TVDB: searchURL = self.urls['search_tvdb'].format(search_string=search_string, tvdb=ep_indexerid) + self.defaultOptions - elif ep_indexer == INDEXER_TVRAGE: - searchURL = self.urls['search_tvrage'].format(search_string=search_string, tvrage=ep_indexerid) + self.defaultOptions else: searchURL = self.urls['search'].format(search_string=search_string) + self.defaultOptions else: @@ -265,9 +255,6 @@ class RarbgProvider(generic.TorrentProvider): if re.search('Cant find search_tvdb in database. Are you sure this imdb exists?', data): logger.log(u'{name} no results found. Search tvdb id do not exist on server.'.format(name=self.name), logger.DEBUG) raise GetOutOfLoop - if re.search('Cant find search_tvrage in database. Are you sure this imdb exists?', data): - logger.log(u'{name} no results found. Search tvrage id do not exist on server.'.format(name=self.name), logger.DEBUG) - raise GetOutOfLoop if re.search('Invalid token. Use get_token for a new one!', data): logger.log(u'{name} Invalid token, retrieving new token'.format(name=self.name), logger.DEBUG) retry = retry - 1 diff --git a/sickbeard/providers/scc.py b/sickbeard/providers/scc.py index bbfe4c715998a1eddc5957dfd31a0c8f4b2b7dbb..5f92049006ec20c958863bc5c86649940a7ea502 100644 --- a/sickbeard/providers/scc.py +++ b/sickbeard/providers/scc.py @@ -18,11 +18,9 @@ # along with SickRage. If not, see <http://www.gnu.org/licenses/>. import re -import traceback import datetime import time -import urlparse import sickbeard import generic import urllib @@ -34,9 +32,7 @@ from sickbeard import classes from sickbeard import helpers from sickbeard import show_name_helpers from sickbeard.exceptions import ex -from sickbeard import clients import requests -from requests import exceptions from sickbeard.bs4_parser import BS4Parser from unidecode import unidecode from sickbeard.helpers import sanitizeSceneName @@ -91,10 +87,9 @@ class SCCProvider(generic.TorrentProvider): 'submit': 'come on in', } - self.session = requests.Session() try: - response = self.session.post(self.urls['login'], data=login_params, headers=self.headers, timeout=30) + response = self.getURL(self.urls['login'], post_data=login_params, timeout=30) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: logger.log(u'Unable to connect to ' + self.name + ' provider: ' + ex(e), logger.ERROR) return False diff --git a/sickbeard/providers/scenetime.py b/sickbeard/providers/scenetime.py index 6cd1511af7642073f35717a3ce88d12005d97f3a..aa3de5ce80c00482da38f1155cf7201987e1a9df 100644 --- a/sickbeard/providers/scenetime.py +++ b/sickbeard/providers/scenetime.py @@ -19,7 +19,6 @@ import re import traceback import datetime -import urlparse import sickbeard import generic import urllib @@ -31,9 +30,7 @@ from sickbeard import classes from sickbeard import helpers from sickbeard import show_name_helpers from sickbeard.exceptions import ex -from sickbeard import clients import requests -from requests import exceptions from sickbeard.bs4_parser import BS4Parser from unidecode import unidecode from sickbeard.helpers import sanitizeSceneName @@ -87,7 +84,7 @@ class SceneTimeProvider(generic.TorrentProvider): self.session = requests.Session() try: - response = self.session.post(self.urls['login'], data=login_params, timeout=30) + response = self.getURL(self.urls['login'], post_data=login_params, timeout=30) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: logger.log(u'Unable to connect to ' + self.name + ' provider: ' + ex(e), logger.ERROR) return False diff --git a/sickbeard/providers/shazbat.py b/sickbeard/providers/shazbat.py index d723a838278465ca483ec3559da790c458186c5c..69a62376008902bfc05ee337127ae6901566e719 100644 --- a/sickbeard/providers/shazbat.py +++ b/sickbeard/providers/shazbat.py @@ -16,16 +16,9 @@ # You should have received a copy of the GNU General Public License # along with SickRage. If not, see <http://www.gnu.org/licenses/>. -try: - import xml.etree.cElementTree as etree -except ImportError: - import elementtree.ElementTree as etree - -import sickbeard import generic -from sickbeard.exceptions import ex, AuthException -from sickbeard import helpers +from sickbeard.exceptions import AuthException from sickbeard import logger from sickbeard import tvcache diff --git a/sickbeard/providers/speedcd.py b/sickbeard/providers/speedcd.py index d7e5f37d82a356040d07620bbe952c571b7ed5b9..a805591e74d962c956f564c956e06c3df42e7d09 100644 --- a/sickbeard/providers/speedcd.py +++ b/sickbeard/providers/speedcd.py @@ -18,23 +18,18 @@ import re import datetime -import urlparse -import time import sickbeard import generic -from sickbeard.common import Quality, cpu_presets +from sickbeard.common import Quality from sickbeard import logger from sickbeard import tvcache from sickbeard import db from sickbeard import classes from sickbeard import helpers from sickbeard import show_name_helpers -from sickbeard.common import Overview from sickbeard.exceptions import ex -from sickbeard import clients import requests -from requests import exceptions from sickbeard.helpers import sanitizeSceneName @@ -85,7 +80,7 @@ class SpeedCDProvider(generic.TorrentProvider): } try: - response = self.session.post(self.urls['login'], data=login_params, timeout=30) + response = self.getURL(self.urls['login'], post_data=login_params, timeout=30) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: logger.log(u'Unable to connect to ' + self.name + ' provider: ' + ex(e), logger.ERROR) return False diff --git a/sickbeard/providers/t411.py b/sickbeard/providers/t411.py index 27e35160e8e8d51696258333f1446573493c25b4..11e9adabfcf844a0ea255a39af933417d55e24ae 100644 --- a/sickbeard/providers/t411.py +++ b/sickbeard/providers/t411.py @@ -26,7 +26,6 @@ import sickbeard import generic import requests -from requests import exceptions from sickbeard.common import Quality from sickbeard import logger diff --git a/sickbeard/providers/thepiratebay.py b/sickbeard/providers/thepiratebay.py index 3350ed2ae0edf70173760768cc5648074879d4ef..c00ec9a4bb8d24291cd38ae71b95b9488db68398 100644 --- a/sickbeard/providers/thepiratebay.py +++ b/sickbeard/providers/thepiratebay.py @@ -18,29 +18,19 @@ from __future__ import with_statement -import time import re -import urllib, urllib2, urlparse -import sys -import os +import urllib import datetime import sickbeard import generic -from sickbeard.common import Quality, cpu_presets -from sickbeard.name_parser.parser import NameParser, InvalidNameException, InvalidShowException +from sickbeard.common import Quality from sickbeard import db from sickbeard import classes from sickbeard import logger from sickbeard import tvcache from sickbeard import helpers -from sickbeard import clients from sickbeard.show_name_helpers import allPossibleShowNames, sanitizeSceneName -from sickbeard.common import Overview -from sickbeard.exceptions import ex -from sickbeard import encodingKludge as ek -import requests -from requests import exceptions from unidecode import unidecode diff --git a/sickbeard/providers/tntvillage.py b/sickbeard/providers/tntvillage.py index bd68139c9f96df7743a5fa07dafc955f50e6af40..3dd782d33f9d74784528a3adf4ef8f54040121dc 100644 --- a/sickbeard/providers/tntvillage.py +++ b/sickbeard/providers/tntvillage.py @@ -29,8 +29,6 @@ from sickbeard import classes from sickbeard import helpers from sickbeard import show_name_helpers from sickbeard.exceptions import ex, AuthException -from sickbeard import clients -import requests from requests.exceptions import RequestException from sickbeard.bs4_parser import BS4Parser from unidecode import unidecode @@ -153,7 +151,7 @@ class TNTVillageProvider(generic.TorrentProvider): } try: - response = self.session.post(self.urls['login'], data=login_params, timeout=30) + response = self.getURL(self.urls['login'], post_data=login_params, timeout=30) except RequestException as e: logger.log(u'Unable to connect to ' + self.name + ' provider: ' + ex(e), logger.ERROR) return False diff --git a/sickbeard/providers/tokyotoshokan.py b/sickbeard/providers/tokyotoshokan.py index 4d1c3680da956b9173c465b80b255a76f739c0f3..e6917e5f82765673418b0924cb7d674d1c0a7674 100644 --- a/sickbeard/providers/tokyotoshokan.py +++ b/sickbeard/providers/tokyotoshokan.py @@ -17,17 +17,15 @@ # along with SickRage. If not, see <http://www.gnu.org/licenses/>. import urllib -import re import traceback -import sickbeard import generic from sickbeard import show_name_helpers from sickbeard import logger from sickbeard.common import Quality from sickbeard import tvcache -from sickbeard import show_name_helpers, helpers +from sickbeard import show_name_helpers from sickbeard.bs4_parser import BS4Parser @@ -136,25 +134,6 @@ class TokyoToshokanCache(tvcache.TVCache): # only poll NyaaTorrents every 15 minutes max self.minTime = 15 - def _get_title_and_url(self, item): - """ - Retrieves the title and URL data from the item XML node - - item: An elementtree.ElementTree element representing the <item> tag of the RSS feed - - Returns: A tuple containing two strings representing title and URL respectively - """ - - title = item.title if item.title else None - if title: - title = self._clean_title_from_provider(title) - - url = item.link if item.link else None - if url: - url = url.replace('&', '&') - - return (title, url) - def _getRSSData(self): params = { "filter": '1', diff --git a/sickbeard/providers/torrentbytes.py b/sickbeard/providers/torrentbytes.py index 01df268d5177e1f34e9cf0448e49fe2aa5424f95..165fc2d476289c2697e03fee0d2bc4f986725068 100644 --- a/sickbeard/providers/torrentbytes.py +++ b/sickbeard/providers/torrentbytes.py @@ -19,7 +19,6 @@ import re import traceback import datetime -import urlparse import sickbeard import generic import urllib @@ -31,9 +30,7 @@ from sickbeard import classes from sickbeard import helpers from sickbeard import show_name_helpers from sickbeard.exceptions import ex -from sickbeard import clients import requests -from requests import exceptions from sickbeard.bs4_parser import BS4Parser from unidecode import unidecode from sickbeard.helpers import sanitizeSceneName @@ -88,7 +85,7 @@ class TorrentBytesProvider(generic.TorrentProvider): self.session = requests.Session() try: - response = self.session.post(self.urls['login'], data=login_params, timeout=30) + response = self.getURL(self.urls['login'], post_data=login_params, timeout=30) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: logger.log(u'Unable to connect to ' + self.name + ' provider: ' + ex(e), logger.ERROR) return False diff --git a/sickbeard/providers/torrentday.py b/sickbeard/providers/torrentday.py index 6cc32bea901ff3fca678b91e61c88413b4bbd194..fb3763b90438ff2cc68e9dccb05c66098c5dc6f5 100644 --- a/sickbeard/providers/torrentday.py +++ b/sickbeard/providers/torrentday.py @@ -17,7 +17,6 @@ import re import datetime -import urlparse import sickbeard import generic from sickbeard.common import Quality @@ -28,9 +27,7 @@ from sickbeard import classes from sickbeard import helpers from sickbeard import show_name_helpers from sickbeard.exceptions import ex -from sickbeard import clients import requests -from requests import exceptions from sickbeard.helpers import sanitizeSceneName @@ -97,7 +94,7 @@ class TorrentDayProvider(generic.TorrentProvider): self.session = requests.Session() try: - response = self.session.post(self.urls['login'], data=login_params, timeout=30) + response = self.getURL(self.urls['login'], post_data=login_params, timeout=30) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: logger.log(u'Unable to connect to ' + self.name + ' provider: ' + ex(e), logger.ERROR) return False diff --git a/sickbeard/providers/torrentleech.py b/sickbeard/providers/torrentleech.py index 4a94cacf865205a6c736392c74ad24acddb07779..90c2b96df523ced9b2ebd488e29be6d7e5425048 100644 --- a/sickbeard/providers/torrentleech.py +++ b/sickbeard/providers/torrentleech.py @@ -19,23 +19,18 @@ import re import traceback import datetime -import urlparse import urllib import sickbeard import generic -from sickbeard.common import Quality, cpu_presets +from sickbeard.common import Quality from sickbeard import logger from sickbeard import tvcache from sickbeard import db from sickbeard import classes from sickbeard import helpers from sickbeard import show_name_helpers -from sickbeard.common import Overview from sickbeard.exceptions import ex -from sickbeard import clients -import requests -from requests import exceptions from sickbeard.bs4_parser import BS4Parser from unidecode import unidecode from sickbeard.helpers import sanitizeSceneName @@ -89,17 +84,12 @@ class TorrentLeechProvider(generic.TorrentProvider): 'login': 'submit', } - self.session = requests.Session() - - try: - response = self.session.post(self.urls['login'], data=login_params, timeout=30) - except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: + response = self.getURL(self.urls['login'], post_data=login_params, timeout=30) + if not response: logger.log(u'Unable to connect to ' + self.name + ' provider: ' + ex(e), logger.ERROR) return False - if re.search('Invalid Username/password', response.text) \ - or re.search('<title>Login :: TorrentLeech.org</title>', response.text) \ - or response.status_code == 401: + if re.search('Invalid Username/password', response) or re.search('<title>Login :: TorrentLeech.org</title>', response): logger.log(u'Invalid username or password for ' + self.name + ' Check your settings', logger.ERROR) return False diff --git a/sickbeard/providers/womble.py b/sickbeard/providers/womble.py index 0349d3e878728eb41d7cbc4d3281e7e10fe36a13..5983a89e2445be1262b0d038dbeeed2110adec13 100644 --- a/sickbeard/providers/womble.py +++ b/sickbeard/providers/womble.py @@ -15,14 +15,11 @@ # # You should have received a copy of the GNU General Public License # along with SickRage. If not, see <http://www.gnu.org/licenses/>. -import time -import sickbeard import generic from sickbeard import logger from sickbeard import tvcache -from sickbeard.exceptions import AuthException class WombleProvider(generic.NZBProvider): diff --git a/sickbeard/providers/xthor.py b/sickbeard/providers/xthor.py index 4c5de0527c10ccd9ea7f243bac553b22fd239d0e..07c1afeb3cfff563bf01b9b97f8033063836a806 100644 --- a/sickbeard/providers/xthor.py +++ b/sickbeard/providers/xthor.py @@ -17,21 +17,16 @@ # You should have received a copy of the GNU General Public License # along with SickRage. If not, see <http://www.gnu.org/licenses/>. -import traceback import re import datetime -import time -from requests.auth import AuthBase import sickbeard import generic import cookielib import urllib import requests -from requests import exceptions from sickbeard.bs4_parser import BS4Parser from sickbeard.common import Quality from sickbeard import logger -from sickbeard import tvcache from sickbeard import show_name_helpers from sickbeard import db from sickbeard import helpers @@ -135,7 +130,7 @@ class XthorProvider(generic.TorrentProvider): if any(requests.utils.dict_from_cookiejar(self.session.cookies).values()): return True - header = {'user-agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.8 (KHTML, like Gecko) Chrome/17.0.940.0 Safari/535.8'} + self.headers.update({'user-agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.8 (KHTML, like Gecko) Chrome/17.0.940.0 Safari/535.8'}) login_params = {'username': self.username, 'password': self.password, @@ -148,7 +143,7 @@ class XthorProvider(generic.TorrentProvider): logger.log('Performing authentication to Xthor', logger.DEBUG) try: - response = self.session.post(self.url + '/takelogin.php', data=login_params, timeout=30, headers=header) + response = self.getURL(self.url + '/takelogin.php', post_data=login_params, timeout=30) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: logger.log(u'Unable to connect to ' + self.name + ' provider: ' + ex(e), logger.ERROR) return False diff --git a/sickbeard/sab.py b/sickbeard/sab.py index 01704fea758f111b838fa8006f43a807e818e001..639dfe8095e36a936705025bb5c2592aa9a5ef86 100644 --- a/sickbeard/sab.py +++ b/sickbeard/sab.py @@ -19,7 +19,6 @@ import urllib, httplib -import datetime import sickbeard diff --git a/sickbeard/scene_exceptions.py b/sickbeard/scene_exceptions.py index a6e49242782e9e00996bd05bb16fca4e995662e3..fc70d2f8539fd8d17431af033cd7f8061ab7ce4b 100644 --- a/sickbeard/scene_exceptions.py +++ b/sickbeard/scene_exceptions.py @@ -24,11 +24,8 @@ import datetime import sickbeard import adba from sickbeard import helpers -from sickbeard import name_cache from sickbeard import logger from sickbeard import db -from sickbeard import encodingKludge as ek -import os import requests exception_dict = {} diff --git a/sickbeard/scene_numbering.py b/sickbeard/scene_numbering.py index eac8cedee3a536968e7f4785dac3ebb777dd6f32..11f7a9cd2b5776b7f50c873a1886f35901c78894 100644 --- a/sickbeard/scene_numbering.py +++ b/sickbeard/scene_numbering.py @@ -25,13 +25,8 @@ import time import datetime import traceback -import sickbeard - -try: - import json -except ImportError: - import simplejson as json +import sickbeard from sickbeard import logger from sickbeard import db from sickbeard.exceptions import ex @@ -40,15 +35,15 @@ from sickbeard import helpers def get_scene_numbering(indexer_id, indexer, season, episode, fallback_to_xem=True): """ Returns a tuple, (season, episode), with the scene numbering (if there is one), - otherwise returns the xem numbering (if fallback_to_xem is set), otherwise - returns the TVDB and TVRAGE numbering. + otherwise returns the xem numbering (if fallback_to_xem is set), otherwise + returns the TVDB numbering. (so the return values will always be set) - + @param indexer_id: int @param season: int @param episode: int @param fallback_to_xem: bool If set (the default), check xem for matches if there is no local scene numbering - @return: (int, int) a tuple with (season, episode) + @return: (int, int) a tuple with (season, episode) """ if indexer_id is None or season is None or episode is None: return (season, episode) @@ -91,7 +86,7 @@ def get_scene_absolute_numbering(indexer_id, indexer, absolute_number, fallback_ """ Returns a tuple, (season, episode), with the scene numbering (if there is one), otherwise returns the xem numbering (if fallback_to_xem is set), otherwise - returns the TVDB and TVRAGE numbering. + returns the TVDB numbering. (so the return values will always be set) @param indexer_id: int @@ -141,7 +136,7 @@ def find_scene_absolute_numbering(indexer_id, indexer, absolute_number): def get_indexer_numbering(indexer_id, indexer, sceneSeason, sceneEpisode, fallback_to_xem=True): """ - Returns a tuple, (season, episode) with the TVDB and TVRAGE numbering for (sceneSeason, sceneEpisode) + Returns a tuple, (season, episode) with the TVDB numbering for (sceneSeason, sceneEpisode) (this works like the reverse of get_scene_numbering) """ if indexer_id is None or sceneSeason is None or sceneEpisode is None: @@ -165,7 +160,7 @@ def get_indexer_numbering(indexer_id, indexer, sceneSeason, sceneEpisode, fallba def get_indexer_absolute_numbering(indexer_id, indexer, sceneAbsoluteNumber, fallback_to_xem=True, scene_season=None): """ - Returns a tuple, (season, episode, absolute_number) with the TVDB and TVRAGE numbering for (sceneAbsoluteNumber) + Returns a tuple, (season, episode, absolute_number) with the TVDB absolute numbering for (sceneAbsoluteNumber) (this works like the reverse of get_absolute_numbering) """ if indexer_id is None or sceneAbsoluteNumber is None: @@ -197,7 +192,7 @@ def set_scene_numbering(indexer_id, indexer, season=None, episode=None, absolute """ Set scene numbering for a season/episode. To clear the scene numbering, leave both sceneSeason and sceneEpisode as None. - + """ if indexer_id is None: return @@ -232,7 +227,7 @@ def find_xem_numbering(indexer_id, indexer, season, episode): """ Returns the scene numbering, as retrieved from xem. Refreshes/Loads as needed. - + @param indexer_id: int @param season: int @param episode: int @@ -284,11 +279,11 @@ def find_xem_absolute_numbering(indexer_id, indexer, absolute_number): def get_indexer_numbering_for_xem(indexer_id, indexer, sceneSeason, sceneEpisode): """ Reverse of find_xem_numbering: lookup a tvdb season and episode using scene numbering - + @param indexer_id: int @param sceneSeason: int @param sceneEpisode: int - @return: (int, int) a tuple of (season, episode) + @return: (int, int) a tuple of (season, episode) """ if indexer_id is None or sceneSeason is None or sceneEpisode is None: return (sceneSeason, sceneEpisode) @@ -460,7 +455,7 @@ def get_xem_absolute_numbering_for_show(indexer_id, indexer): def xem_refresh(indexer_id, indexer, force=False): """ Refresh data from xem for a tv show - + @param indexer_id: int """ if not indexer_id or indexer_id < 1: @@ -553,46 +548,6 @@ def fix_xem_numbering(indexer_id, indexer): indexer_id = int(indexer_id) indexer = int(indexer) - # query = [{ - # "name": self.show.name, - # "seasons": [{ - # "episodes": [{ - # "episode_number": None, - # "name": None - # }], - # "season_number": None, - # }], - # "/tv/tv_program/number_of_seasons": [], - # "/tv/tv_program/number_of_episodes": [], - # "/tv/tv_program/thetvdb_id": [], - # "/tv/tv_program/tvrage_id": [], - # "type": "/tv/tv_program", - # }] - # - # - # url = 'https://www.googleapis.com/freebase/v1/mqlread' - # api_key = "AIzaSyCCHNp4dhVHxJYzbLiCE4y4a1rgTnX4fDE" - # params = { - # 'query': json.dumps(query), - # 'key': api_key - # } - # - # - # def get_from_api(url, params=None): - # """Build request and return results - # """ - # import xmltodict - # - # response = requests.get(url, params=params) - # if response.status_code == 200: - # try: - # return response.json() - # except ValueError: - # return xmltodict.parse(response.text)['Data'] - # - # # Get query results - # tmp = get_from_api(url, params=params)['result'] - myDB = db.DBConnection() rows = myDB.select( 'SELECT season, episode, absolute_number, scene_season, scene_episode, scene_absolute_number FROM tv_episodes WHERE indexer = ? and showid = ?', diff --git a/sickbeard/search.py b/sickbeard/search.py index 32c453fa27425185f2a818237f5f7e843bd93507..ac0ac51b425cc0ea2e90c56e9329406a57335459 100644 --- a/sickbeard/search.py +++ b/sickbeard/search.py @@ -40,7 +40,6 @@ from sickbeard import encodingKludge as ek from sickbeard import failed_history from sickbeard.exceptions import ex from sickbeard.providers.generic import GenericProvider -from sickbeard.blackandwhitelist import BlackAndWhiteList from sickbeard import common def _downloadResult(result): diff --git a/sickbeard/search_queue.py b/sickbeard/search_queue.py index 5f735e679bff2b312216c087d52651fb96b43e80..10cef186aab5b99c97bf27514a5f1b07ab5b51eb 100644 --- a/sickbeard/search_queue.py +++ b/sickbeard/search_queue.py @@ -23,12 +23,11 @@ import traceback import threading import sickbeard -from sickbeard import db, logger, common, exceptions, helpers -from sickbeard import generic_queue, scheduler +from sickbeard import common +from sickbeard import logger +from sickbeard import generic_queue from sickbeard import search, failed_history, history from sickbeard import ui -from sickbeard.exceptions import ex -from sickbeard.search import pickBestResult search_queue_lock = threading.Lock() diff --git a/sickbeard/showUpdater.py b/sickbeard/showUpdater.py index ad9001e40d41f9bf606af7aa014dde37012514fe..4485dfd7a738fdfc0b120f315487bdf76937ae81 100644 --- a/sickbeard/showUpdater.py +++ b/sickbeard/showUpdater.py @@ -17,7 +17,6 @@ # along with SickRage. If not, see <http://www.gnu.org/licenses/>. import datetime -import os import threading import sickbeard @@ -25,7 +24,6 @@ from sickbeard import logger from sickbeard import exceptions from sickbeard import ui from sickbeard.exceptions import ex -from sickbeard import encodingKludge as ek from sickbeard import db from sickbeard import network_timezones from sickbeard import failed_history diff --git a/sickbeard/show_name_helpers.py b/sickbeard/show_name_helpers.py index e6f3d90859950b4afe29b6dbc0430082d5bd49cd..c5097235e94481541b18f3e73010eb14d4a4dce3 100644 --- a/sickbeard/show_name_helpers.py +++ b/sickbeard/show_name_helpers.py @@ -30,8 +30,6 @@ from sickbeard import logger from sickbeard import db from sickbeard import encodingKludge as ek from name_parser.parser import NameParser, InvalidNameException, InvalidShowException -from unidecode import unidecode -from sickbeard.blackandwhitelist import BlackAndWhiteList resultFilters = ["sub(bed|ed|pack|s)", "(dk|fin|heb|kor|nor|nordic|pl|swe)sub(bed|ed|s)?", "(dir|sample|sub|nfo)fix", "sample", "(dvd)?extras", diff --git a/sickbeard/show_queue.py b/sickbeard/show_queue.py index 4d2ec47e43af2dfb548f65b4d30d64c696b8f2b4..a92af691fd873ce943e7a6509917e77d746a6433 100644 --- a/sickbeard/show_queue.py +++ b/sickbeard/show_queue.py @@ -23,15 +23,17 @@ import traceback import sickbeard from imdb import _exceptions as imdb_exceptions -from sickbeard.common import SKIPPED, WANTED +from sickbeard.common import WANTED from sickbeard.tv import TVShow -from sickbeard import exceptions, logger, ui, db, notifiers +from sickbeard import exceptions +from sickbeard import logger +from sickbeard import notifiers +from sickbeard import ui from sickbeard import generic_queue from sickbeard import name_cache from sickbeard.exceptions import ex -from sickbeard.blackandwhitelist import BlackAndWhiteList, short_group_names +from sickbeard.blackandwhitelist import BlackAndWhiteList from libtrakt import TraktAPI -from libtrakt.exceptions import traktException, traktServerBusy, traktAuthException class ShowQueue(generic_queue.GenericQueue): def __init__(self): @@ -306,10 +308,10 @@ class QueueItemAdd(ShowQueueItem): self.indexer).name) + " using ID " + str( self.indexer_id) + ", not using the NFO. Delete .nfo and try adding manually again.") - if sickbeard.USE_TRAKT and sickbeard.TRAKT_SYNC_WATCHLIST: + if sickbeard.USE_TRAKT: trakt_id = sickbeard.indexerApi(self.indexer).config['trakt_id'] - trakt_api = TraktAPI(sickbeard.TRAKT_DISABLE_SSL_VERIFY, sickbeard.TRAKT_TIMEOUT) + trakt_api = TraktAPI(sickbeard.SSL_VERIFY, sickbeard.TRAKT_TIMEOUT) title = self.showDir.split("/")[-1] data = { @@ -654,11 +656,10 @@ class QueueItemRemove(ShowQueueItem): logger.log(u"Removing %s" % self.show.name) self.show.deleteShow(full=self.full) - if sickbeard.USE_TRAKT and sickbeard.TRAKT_SYNC: + if sickbeard.USE_TRAKT: try: sickbeard.traktCheckerScheduler.action.removeShowFromTraktLibrary(self.show) except Exception as e: - logger.log(u"Unable to delete show from Trakt: %s. Error: %s" % (showObj.name, ex(e)),logger.WARNING) - pass + logger.log(u"Unable to delete show from Trakt: %s. Error: %s" % (self.show.name, ex(e)),logger.WARNING) self.finish() diff --git a/sickbeard/subtitles.py b/sickbeard/subtitles.py index 0b5477d3958f0872989fdda8794e9b1a22263bd0..f741ce655513d6f571a3c39aa5c6041c24dc77a7 100644 --- a/sickbeard/subtitles.py +++ b/sickbeard/subtitles.py @@ -16,16 +16,13 @@ # You should have received a copy of the GNU General Public License # along with SickRage. If not, see <http://www.gnu.org/licenses/>. -import time import datetime import sickbeard from sickbeard.common import * from sickbeard.exceptions import ex -from sickbeard import notifiers from sickbeard import logger from sickbeard import encodingKludge as ek from sickbeard import db -from sickbeard import history import subliminal import babelfish import subprocess diff --git a/sickbeard/traktChecker.py b/sickbeard/traktChecker.py index 6c3ab121def28934f200688b9087e0fb9eb9a2ce..9ebab41fdf3167351f2f9d1dede61deaba2c5754 100644 --- a/sickbeard/traktChecker.py +++ b/sickbeard/traktChecker.py @@ -19,7 +19,6 @@ import os import traceback import datetime -import json import sickbeard from sickbeard import encodingKludge as ek @@ -28,9 +27,11 @@ from sickbeard import logger from sickbeard import helpers from sickbeard import search_queue from sickbeard import db -from sickbeard import notifiers -from sickbeard.common import SNATCHED, SNATCHED_PROPER, DOWNLOADED, SKIPPED, UNAIRED, IGNORED, ARCHIVED, WANTED, UNKNOWN, FAILED -from common import Quality, qualityPresetStrings, statusStrings +from sickbeard.common import ARCHIVED +from sickbeard.common import SKIPPED +from sickbeard.common import UNKNOWN +from sickbeard.common import WANTED +from common import Quality from libtrakt import * from libtrakt.exceptions import traktException @@ -137,7 +138,6 @@ class TraktChecker(): self.trakt_api.traktRequest("sync/collection/remove", data, method='POST') except traktException as e: logger.log(u"Could not connect to Trakt service: %s" % ex(e), logger.WARNING) - pass def addShowToTraktLibrary(self, show_obj): """ diff --git a/sickbeard/tv.py b/sickbeard/tv.py index cb6f2ff3b9682e358d1b07c2e4312ebc72dee3fe..6c09350982efaa36c563c487473a56047d4e1e93 100644 --- a/sickbeard/tv.py +++ b/sickbeard/tv.py @@ -40,7 +40,6 @@ except ImportError: pass from imdb import imdb -import logging from sickbeard import db from sickbeard import helpers, exceptions, logger from sickbeard.exceptions import ex @@ -52,8 +51,8 @@ from sickbeard import history from sickbeard.blackandwhitelist import BlackAndWhiteList from sickbeard import sbdatetime from sickbeard import network_timezones +from sickbeard.indexers.indexer_config import INDEXER_TVRAGE from dateutil.tz import * -from subliminal.exceptions import Error as ServiceError from sickbeard import encodingKludge as ek @@ -66,7 +65,6 @@ from common import NAMING_DUPLICATE, NAMING_EXTEND, NAMING_LIMITED_EXTEND, NAMIN import shutil import shutil_custom -import babelfish shutil.copyfile = shutil_custom.copyfile_custom @@ -848,52 +846,55 @@ class TVShow(object): def loadFromIndexer(self, cache=True, tvapi=None, cachedSeason=None): - logger.log(str(self.indexerid) + u": Loading show info from " + sickbeard.indexerApi(self.indexer).name, logger.DEBUG) + if self.indexer is not INDEXER_TVRAGE: + logger.log(str(self.indexerid) + u": Loading show info from " + sickbeard.indexerApi(self.indexer).name, logger.DEBUG) - # There's gotta be a better way of doing this but we don't wanna - # change the cache value elsewhere - if tvapi is None: - lINDEXER_API_PARMS = sickbeard.indexerApi(self.indexer).api_params.copy() + # There's gotta be a better way of doing this but we don't wanna + # change the cache value elsewhere + if tvapi is None: + lINDEXER_API_PARMS = sickbeard.indexerApi(self.indexer).api_params.copy() - if not cache: - lINDEXER_API_PARMS['cache'] = False + if not cache: + lINDEXER_API_PARMS['cache'] = False - if self.lang: - lINDEXER_API_PARMS['language'] = self.lang + if self.lang: + lINDEXER_API_PARMS['language'] = self.lang - if self.dvdorder != 0: - lINDEXER_API_PARMS['dvdorder'] = True + if self.dvdorder != 0: + lINDEXER_API_PARMS['dvdorder'] = True - t = sickbeard.indexerApi(self.indexer).indexer(**lINDEXER_API_PARMS) + t = sickbeard.indexerApi(self.indexer).indexer(**lINDEXER_API_PARMS) - else: - t = tvapi + else: + t = tvapi - myEp = t[self.indexerid] + myEp = t[self.indexerid] - try: - self.name = myEp['seriesname'].strip() - except AttributeError: - raise sickbeard.indexer_attributenotfound( - "Found %s, but attribute 'seriesname' was empty." % (self.indexerid)) + try: + self.name = myEp['seriesname'].strip() + except AttributeError: + raise sickbeard.indexer_attributenotfound( + "Found %s, but attribute 'seriesname' was empty." % (self.indexerid)) - self.classification = getattr(myEp, 'classification', 'Scripted') - self.genre = getattr(myEp, 'genre', '') - self.network = getattr(myEp, 'network', '') - self.runtime = getattr(myEp, 'runtime', '') + self.classification = getattr(myEp, 'classification', 'Scripted') + self.genre = getattr(myEp, 'genre', '') + self.network = getattr(myEp, 'network', '') + self.runtime = getattr(myEp, 'runtime', '') - self.imdbid = getattr(myEp, 'imdb_id', '') + self.imdbid = getattr(myEp, 'imdb_id', '') - if getattr(myEp, 'airs_dayofweek', None) is not None and getattr(myEp, 'airs_time', None) is not None: - self.airs = myEp["airs_dayofweek"] + " " + myEp["airs_time"] + if getattr(myEp, 'airs_dayofweek', None) is not None and getattr(myEp, 'airs_time', None) is not None: + self.airs = myEp["airs_dayofweek"] + " " + myEp["airs_time"] - if self.airs is None: - self.airs = '' + if self.airs is None: + self.airs = '' - if getattr(myEp, 'firstaired', None) is not None: - self.startyear = int(str(myEp["firstaired"]).split('-')[0]) + if getattr(myEp, 'firstaired', None) is not None: + self.startyear = int(str(myEp["firstaired"]).split('-')[0]) - self.status = getattr(myEp, 'status', 'Unknown') + self.status = getattr(myEp, 'status', 'Unknown') + else: + logger.log(str(self.indexerid) + u": NOT loading info from " + sickbeard.indexerApi(self.indexer).name + " as it is temporarily disabled.", logger.WARNING) def loadIMDbInfo(self, imdbapi=None): @@ -1460,7 +1461,6 @@ class TVEpisode(object): logger.log(u'%s: Exception caught in subliminal.scan_video for S%02dE%02d' % (self.show.indexerid, self.season, self.episode), logger.DEBUG) return - pass if not video: return @@ -1557,7 +1557,6 @@ class TVEpisode(object): self.loadFromNFO(self.location) except exceptions.NoNFOException: logger.log(u"%s: There was an error loading the NFO for episode S%02dE%02d" % (self.show.indexerid, season, episode), logger.ERROR) - pass # if we tried loading it from NFO and didn't find the NFO, try the Indexers if not self.hasnfo: diff --git a/sickbeard/tvcache.py b/sickbeard/tvcache.py index b34f8d5b3dc991d94dfc37c9a6872fb02b95aa9c..974e78e66ae4fd05b4a113eb867c435331407d9c 100644 --- a/sickbeard/tvcache.py +++ b/sickbeard/tvcache.py @@ -21,7 +21,6 @@ from __future__ import with_statement import time import datetime import itertools -import traceback import urllib2 import sickbeard diff --git a/sickbeard/webapi.py b/sickbeard/webapi.py index 7d045c7e43978e51e2e7f536ab462f96b043b6f0..48fc149bc868a430214ac3ecdc82ba2eeae34a41 100644 --- a/sickbeard/webapi.py +++ b/sickbeard/webapi.py @@ -27,9 +27,16 @@ import re import traceback import sickbeard +from sickrage.media.ShowFanArt import ShowFanArt +from sickrage.media.ShowNetworkLogo import ShowNetworkLogo +from sickrage.media.ShowPoster import ShowPoster +from sickrage.media.ShowBanner import ShowBanner +from sickrage.show.History import History +from sickrage.system.Restart import Restart +from sickrage.system.Shutdown import Shutdown from versionChecker import CheckVersion -from sickbeard import db, logger, exceptions, history, ui, helpers +from sickbeard import db, logger, exceptions, ui, helpers from sickbeard import encodingKludge as ek from sickbeard import search_queue from sickbeard import image_cache @@ -37,8 +44,19 @@ from sickbeard import classes from sickbeard import processTV from sickbeard import network_timezones, sbdatetime from sickbeard.exceptions import ex -from sickbeard.common import Quality, Overview, qualityPresetStrings, statusStrings, SNATCHED, SNATCHED_PROPER, DOWNLOADED, SKIPPED, UNAIRED, IGNORED, ARCHIVED, WANTED, UNKNOWN -from sickbeard.webserve import WebRoot +from sickbeard.common import DOWNLOADED +from sickbeard.common import FAILED +from sickbeard.common import IGNORED +from sickbeard.common import Overview +from sickbeard.common import Quality +from sickbeard.common import SKIPPED +from sickbeard.common import SNATCHED +from sickbeard.common import SNATCHED_PROPER +from sickbeard.common import UNAIRED +from sickbeard.common import UNKNOWN +from sickbeard.common import WANTED +from sickbeard.common import qualityPresetStrings +from sickbeard.common import statusStrings import codecs try: @@ -46,12 +64,10 @@ try: except ImportError: import simplejson as json -import subliminal -import babelfish from tornado.web import RequestHandler -indexer_ids = ["indexerid", "tvdbid", "tvrageid"] +indexer_ids = ["indexerid", "tvdbid"] dateFormat = "%Y-%m-%d" dateTimeFormat = "%Y-%m-%d %H:%M" @@ -62,16 +78,18 @@ RESULT_FAILURE = 20 # only use inside the run methods RESULT_TIMEOUT = 30 # not used yet :( RESULT_ERROR = 40 # only use outside of the run methods ! RESULT_FATAL = 50 # only use in Api.default() ! this is the "we encountered an internal error" error -RESULT_DENIED = 60 # only use in Api.default() ! this is the acces denied error -result_type_map = {RESULT_SUCCESS: "success", - RESULT_FAILURE: "failure", - RESULT_TIMEOUT: "timeout", - RESULT_ERROR: "error", - RESULT_FATAL: "fatal", - RESULT_DENIED: "denied", +RESULT_DENIED = 60 # only use in Api.default() ! this is the access denied error +result_type_map = { + RESULT_SUCCESS: "success", + RESULT_FAILURE: "failure", + RESULT_TIMEOUT: "timeout", + RESULT_ERROR: "error", + RESULT_FATAL: "fatal", + RESULT_DENIED: "denied", } # basically everything except RESULT_SUCCESS / success is bad + class ApiHandler(RequestHandler): """ api class that returns json results """ version = 5 # use an int since float-point is unpredictable @@ -250,52 +268,6 @@ class ApiHandler(RequestHandler): return curArgs, curKwargs - def showPoster(self, show=None, which=None): - # Redirect initial poster/banner thumb to default images - if which[0:6] == 'poster': - default_image_name = 'poster.png' - elif which[0:6] == 'fanart': - default_image_name = 'fanart.png' - else: - default_image_name = 'banner.png' - - # image_path = ek.ek(os.path.join, sickbeard.PROG_DIR, 'gui', 'slick', 'images', default_image_name) - static_image_path = os.path.join('/images', default_image_name) - if show and sickbeard.helpers.findCertainShow(sickbeard.showList, int(show)): - cache_obj = image_cache.ImageCache() - - image_file_name = None - if which == 'poster': - image_file_name = cache_obj.poster_path(show) - if which == 'poster_thumb' or which == 'small': - image_file_name = cache_obj.poster_thumb_path(show) - if which == 'banner': - image_file_name = cache_obj.banner_path(show) - if which == 'banner_thumb': - image_file_name = cache_obj.banner_thumb_path(show) - if which == 'fanart': - if not cache_obj.has_fanart(show): - cache_obj.fill_cache(sickbeard.helpers.findCertainShow(sickbeard.showList, int(show))) - image_file_name = cache_obj.fanart_path(show) - - if ek.ek(os.path.isfile, image_file_name): - static_image_path = os.path.normpath(image_file_name.replace(sickbeard.CACHE_DIR, '/cache')) - - static_image_path = sickbeard.WEB_ROOT + static_image_path.replace('\\', '/') - return self.redirect(static_image_path) - - def showNetworkLogo(self, show=None): - show = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show)) - - if show: - image_file_name = show.network_logo_name - else: - image_file_name = 'nonetwork' - - static_image_path = '%s/images/network/%s.png' % (sickbeard.WEB_ROOT, image_file_name) - - return self.redirect(static_image_path) - class ApiCall(ApiHandler): _help = {"desc": "No help message available. Please tell the devs that a help msg is missing for this cmd"} @@ -369,8 +341,6 @@ class ApiCall(ApiHandler): if key in indexer_ids: if "tvdbid" in kwargs: key = "tvdbid" - elif "tvrageid" in kwargs: - key = "tvrageid" self.indexer = indexer_ids.index(key) @@ -578,29 +548,10 @@ def _ordinal_to_dateForm(ordinal): def _historyDate_to_dateTimeForm(timeString): - date = datetime.datetime.strptime(timeString, history.dateFormat) + date = datetime.datetime.strptime(timeString, History.date_format) return date.strftime(dateTimeFormat) -def _replace_statusStrings_with_statusCodes(statusStrings): - statusCodes = [] - if "snatched" in statusStrings: - statusCodes += Quality.SNATCHED - if "downloaded" in statusStrings: - statusCodes += Quality.DOWNLOADED - if "skipped" in statusStrings: - statusCodes.append(SKIPPED) - if "wanted" in statusStrings: - statusCodes.append(WANTED) - if "archived" in statusStrings: - statusCodes.append(ARCHIVED) - if "ignored" in statusStrings: - statusCodes.append(IGNORED) - if "unaired" in statusStrings: - statusCodes.append(UNAIRED) - return statusCodes - - def _mapQuality(showObj): quality_map = _getQualityMap() @@ -733,7 +684,7 @@ class CMD_ComingEpisodes(ApiCall): recently = (datetime.date.today() - datetime.timedelta(days=sickbeard.COMING_EPS_MISSED_RANGE)).toordinal() done_show_list = [] - qualList = Quality.DOWNLOADED + Quality.SNATCHED + [ARCHIVED, IGNORED] + qualList = Quality.DOWNLOADED + Quality.SNATCHED + Quality.ARCHIVED + [IGNORED] myDB = db.DBConnection(row_type="dict") sql_results = myDB.select( @@ -835,7 +786,6 @@ class CMD_Episode(ApiCall): }, "optionalParameters": { "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, "full_path": { "desc": "show the full absolute path (if valid) instead of a relative path for the episode location"} } @@ -901,7 +851,6 @@ class CMD_EpisodeSearch(ApiCall): }, "optionalParameters": { "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, } } @@ -948,13 +897,12 @@ class CMD_EpisodeSetStatus(ApiCall): "requiredParameters": { "indexerid": {"desc": "unique id of a show"}, "season": {"desc": "the season number"}, - "status": {"desc": "the status values: wanted, skipped, archived, ignored, failed"} + "status": {"desc": "the status values: wanted, skipped, ignored, failed"} }, "optionalParameters": { "episode": {"desc": "the episode number"}, "force": {"desc": "should we replace existing (downloaded) episodes or not"}, "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, } } @@ -964,7 +912,7 @@ class CMD_EpisodeSetStatus(ApiCall): self.indexerid, args = self.check_params(args, kwargs, "indexerid", None, True, "int", []) self.s, args = self.check_params(args, kwargs, "season", None, True, "int", []) self.status, args = self.check_params(args, kwargs, "status", None, True, "string", - ["wanted", "skipped", "archived", "ignored", "failed"]) + ["wanted", "skipped", "ignored", "failed"]) # optional self.e, args = self.check_params(args, kwargs, "episode", None, False, "int", []) self.force, args = self.check_params(args, kwargs, "force", 0, False, "bool", []) @@ -980,6 +928,7 @@ class CMD_EpisodeSetStatus(ApiCall): # convert the string status to a int for status in statusStrings.statusStrings: if str(statusStrings[status]).lower() == str(self.status).lower(): + self.status = status break else: # if we dont break out of the for loop we got here. @@ -1023,10 +972,14 @@ class CMD_EpisodeSetStatus(ApiCall): failure = True continue + if self.status == FAILED and not sickbeard.USE_FAILED_DOWNLOADS: + ep_results.append(_epResult(RESULT_FAILURE, epObj, "Refusing to change status to FAILED because failed download handling is disabled")) + failure = True + continue + # allow the user to force setting the status for an already downloaded episode if epObj.status in Quality.DOWNLOADED and not self.force: - ep_results.append(_epResult(RESULT_FAILURE, epObj, - "Refusing to change status because it is already marked as DOWNLOADED")) + ep_results.append(_epResult(RESULT_FAILURE, epObj, "Refusing to change status because it is already marked as DOWNLOADED")) failure = True continue @@ -1067,7 +1020,6 @@ class CMD_SubtitleSearch(ApiCall): }, "optionalParameters": { "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, } } @@ -1119,7 +1071,6 @@ class CMD_Exceptions(ApiCall): "optionalParameters": { "indexerid": {"desc": "unique id of a show"}, "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, } } @@ -1160,10 +1111,12 @@ class CMD_Exceptions(ApiCall): class CMD_History(ApiCall): - _help = {"desc": "display sickrage downloaded/snatched history", - "optionalParameters": {"limit": {"desc": "limit returned results"}, - "type": {"desc": "only show a specific type of results"}, - } + _help = { + "desc": "display SickRage downloaded/snatched history", + "optionalParameters": { + "limit": {"desc": "limit returned results"}, + "type": {"desc": "only show a specific type of results"}, + } } def __init__(self, args, kwargs): @@ -1172,48 +1125,34 @@ class CMD_History(ApiCall): self.limit, args = self.check_params(args, kwargs, "limit", 100, False, "int", []) self.type, args = self.check_params(args, kwargs, "type", None, False, "string", ["downloaded", "snatched"]) + self.type = self.type.lower() if isinstance(self.type, str) else '' + # super, missing, help ApiCall.__init__(self, args, kwargs) def run(self): - """ display sickrage downloaded/snatched history """ - - typeCodes = [] - if self.type == "downloaded": - self.type = "Downloaded" - typeCodes = Quality.DOWNLOADED - elif self.type == "snatched": - self.type = "Snatched" - typeCodes = Quality.SNATCHED - else: - typeCodes = Quality.SNATCHED + Quality.DOWNLOADED - - myDB = db.DBConnection(row_type="dict") - - ulimit = min(int(self.limit), 100) - if ulimit == 0: - sqlResults = myDB.select( - "SELECT h.*, show_name FROM history h, tv_shows s WHERE h.showid=s.indexer_id AND action in (" + ','.join( - ['?'] * len(typeCodes)) + ") ORDER BY date DESC", typeCodes) - else: - sqlResults = myDB.select( - "SELECT h.*, show_name FROM history h, tv_shows s WHERE h.showid=s.indexer_id AND action in (" + ','.join( - ['?'] * len(typeCodes)) + ") ORDER BY date DESC LIMIT ?", typeCodes + [ulimit]) - + """ display SickRage downloaded/snatched history """ + data = History().get(self.limit, self.type) results = [] - for row in sqlResults: + + for row in data: status, quality = Quality.splitCompositeStatus(int(row["action"])) status = _get_status_Strings(status) - if self.type and not status == self.type: + + if self.type and not status.lower() == self.type: continue + row["status"] = status row["quality"] = _get_quality_string(quality) row["date"] = _historyDate_to_dateTimeForm(str(row["date"])) + del row["action"] - _rename_element(row, "showid", "indexerid") + + _rename_element(row, "show_id", "indexerid") row["resource_path"] = os.path.dirname(row["resource"]) row["resource"] = os.path.basename(row["resource"]) - # Add tvdbid for backward compability + + # Add tvdbid for backward compatibility row['tvdbid'] = row['indexerid'] results.append(row) @@ -1221,8 +1160,7 @@ class CMD_History(ApiCall): class CMD_HistoryClear(ApiCall): - _help = {"desc": "clear sickrage's history", - } + _help = {"desc": "clear SickRage's history"} def __init__(self, args, kwargs): # required @@ -1231,16 +1169,14 @@ class CMD_HistoryClear(ApiCall): ApiCall.__init__(self, args, kwargs) def run(self): - """ clear sickrage's history """ - myDB = db.DBConnection() - myDB.action("DELETE FROM history WHERE 1=1") + """ clear SickRage's history """ + History().clear() return _responds(RESULT_SUCCESS, msg="History cleared") class CMD_HistoryTrim(ApiCall): - _help = {"desc": "trim sickrage's history by removing entries greater than 30 days old" - } + _help = {"desc": "trim SickRage's history by removing entries greater than 30 days old"} def __init__(self, args, kwargs): # required @@ -1249,12 +1185,11 @@ class CMD_HistoryTrim(ApiCall): ApiCall.__init__(self, args, kwargs) def run(self): - """ trim sickrage's history """ - myDB = db.DBConnection() - myDB.action("DELETE FROM history WHERE date < " + str( - (datetime.datetime.today() - datetime.timedelta(days=30)).strftime(history.dateFormat))) + """ trim SickRage's history """ + History().trim() + + return _responds(RESULT_SUCCESS, msg='Removed history entries older than 30 days') - return _responds(RESULT_SUCCESS, msg="Removed history entries greater than 30 days old") class CMD_Failed(ApiCall): _help = {"desc": "display failed downloads", @@ -1632,7 +1567,7 @@ class CMD_SickBeardGetMessages(ApiCall): def run(self): messages = [] - for cur_notification in ui.notifications.get_notifications(self.request.remote_ip): + for cur_notification in ui.notifications.get_notifications(self.rh.request.remote_ip): messages.append({"title": cur_notification.title, "message": cur_notification.message, "type": cur_notification.type}) @@ -1694,7 +1629,7 @@ class CMD_SickBeardPing(ApiCall): class CMD_SickBeardRestart(ApiCall): - _help = {"desc": "restart sickrage"} + _help = {"desc": "restart SickRage"} def __init__(self, args, kwargs): # required @@ -1703,8 +1638,10 @@ class CMD_SickBeardRestart(ApiCall): ApiCall.__init__(self, args, kwargs) def run(self): - """ restart sickrage """ - sickbeard.events.put(sickbeard.events.SystemEvent.RESTART) + """ restart SickRage """ + if not Restart.restart(sickbeard.PID): + return _responds(RESULT_FAILURE, msg='SickRage can not be restarted') + return _responds(RESULT_SUCCESS, msg="SickRage is restarting...") @@ -1713,7 +1650,6 @@ class CMD_SickBeardSearchIndexers(ApiCall): "optionalParameters": {"name": {"desc": "name of the show you want to search for"}, "indexerid": {"desc": "unique id of a show"}, "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, "lang": {"desc": "the 2 letter abbreviation lang id"} } } @@ -1812,17 +1748,21 @@ class CMD_SickBeardSearchTVDB(CMD_SickBeardSearchIndexers): class CMD_SickBeardSearchTVRAGE(CMD_SickBeardSearchIndexers): + """ + Deprecated, TVRage is no more. + """ + _help = {"desc": "search for show on TVRage with a given string and language", "optionalParameters": {"name": {"desc": "name of the show you want to search for"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, "lang": {"desc": "the 2 letter abbreviation lang id"} } } def __init__(self, args, kwargs): - CMD_SickBeardSearchIndexers.__init__(self, args, kwargs) - self.indexerid, args = self.check_params(args, kwargs, "tvrageid", None, False, "int", []) + ApiCall.__init__(self, args, kwargs) + def run(self): + return _responds(RESULT_FAILURE, msg="TVRage is no more, invalid result") class CMD_SickBeardSetDefaults(ApiCall): _help = {"desc": "set sickrage user defaults", @@ -1848,7 +1788,7 @@ class CMD_SickBeardSetDefaults(ApiCall): self.flatten_folders, args = self.check_params(args, kwargs, "flatten_folders", None, False, "bool", []) self.status, args = self.check_params(args, kwargs, "status", None, False, "string", - ["wanted", "skipped", "archived", "ignored"]) + ["wanted", "skipped", "ignored"]) # super, missing, help ApiCall.__init__(self, args, kwargs) @@ -1903,7 +1843,7 @@ class CMD_SickBeardSetDefaults(ApiCall): class CMD_SickBeardShutdown(ApiCall): - _help = {"desc": "shutdown sickrage"} + _help = {"desc": "shutdown SickRage"} def __init__(self, args, kwargs): # required @@ -1912,10 +1852,13 @@ class CMD_SickBeardShutdown(ApiCall): ApiCall.__init__(self, args, kwargs) def run(self): - """ shutdown sickrage """ - sickbeard.events.put(sickbeard.events.SystemEvent.SHUTDOWN) + """ shutdown SickRage """ + if not Shutdown.stop(sickbeard.PID): + return _responds(RESULT_FAILURE, msg='SickRage can not be shut down') + return _responds(RESULT_SUCCESS, msg="SickRage is shutting down...") + class CMD_SickBeardUpdate(ApiCall): _help = {"desc": "update SickRage to the latest version available"} @@ -1945,7 +1888,6 @@ class CMD_Show(ApiCall): }, "optionalParameters": { "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, } } @@ -2011,8 +1953,6 @@ class CMD_Show(ApiCall): showDict["indexerid"] = showObj.indexerid showDict["tvdbid"] = helpers.mapIndexersToShow(showObj)[1] - showDict["tvrage_id"] = helpers.mapIndexersToShow(showObj)[2] - showDict["tvrage_name"] = showObj.name showDict["imdbid"] = showObj.imdbid showDict["network"] = showObj.network @@ -2039,7 +1979,6 @@ class CMD_ShowAddExisting(ApiCall): }, "optionalParameters": { "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, "initial": {"desc": "initial quality for the show"}, "archive": {"desc": "archive quality for the show"}, "flatten_folders": {"desc": "flatten subfolders for the show"}, @@ -2130,7 +2069,6 @@ class CMD_ShowAddNew(ApiCall): }, "optionalParameters": { "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, "initial": {"desc": "initial quality for the show"}, "location": {"desc": "base path for where the show folder is to be created"}, "archive": {"desc": "archive quality for the show"}, @@ -2162,7 +2100,7 @@ class CMD_ShowAddNew(ApiCall): str(sickbeard.FLATTEN_FOLDERS_DEFAULT), False, "bool", []) self.status, args = self.check_params(args, kwargs, "status", None, False, "string", - ["wanted", "skipped", "archived", "ignored"]) + ["wanted", "skipped", "ignored"]) self.lang, args = self.check_params(args, kwargs, "lang", sickbeard.INDEXER_DEFAULT_LANGUAGE, False, "string", self.valid_languages.keys()) self.subtitles, args = self.check_params(args, kwargs, "subtitles", int(sickbeard.USE_SUBTITLES), @@ -2175,7 +2113,7 @@ class CMD_ShowAddNew(ApiCall): "int", []) self.future_status, args = self.check_params(args, kwargs, "future_status", None, False, "string", - ["wanted", "skipped", "archived", "ignored"]) + ["wanted", "skipped", "ignored"]) # super, missing, help ApiCall.__init__(self, args, kwargs) @@ -2236,7 +2174,7 @@ class CMD_ShowAddNew(ApiCall): if not self.status in statusStrings.statusStrings: raise ApiError("Invalid Status") # only allow the status options we want - if int(self.status) not in (WANTED, SKIPPED, ARCHIVED, IGNORED): + if int(self.status) not in (WANTED, SKIPPED, IGNORED): return _responds(RESULT_FAILURE, msg="Status prohibited") newStatus = self.status @@ -2252,7 +2190,7 @@ class CMD_ShowAddNew(ApiCall): if not self.future_status in statusStrings.statusStrings: raise ApiError("Invalid Status") # only allow the status options we want - if int(self.future_status) not in (WANTED, SKIPPED, ARCHIVED, IGNORED): + if int(self.future_status) not in (WANTED, SKIPPED, IGNORED): return _responds(RESULT_FAILURE, msg="Status prohibited") default_ep_status_after = self.future_status @@ -2301,7 +2239,6 @@ class CMD_ShowCache(ApiCall): }, "optionalParameters": { "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, } } @@ -2341,7 +2278,6 @@ class CMD_ShowDelete(ApiCall): }, "optionalParameters": { "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, "removefiles":{"desc": "Deletes the files, there is no going back!"}, } } @@ -2375,7 +2311,6 @@ class CMD_ShowGetQuality(ApiCall): }, "optionalParameters": { "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, } } @@ -2398,14 +2333,14 @@ class CMD_ShowGetQuality(ApiCall): class CMD_ShowGetPoster(ApiCall): - _help = {"desc": "get the poster stored for a show in sickrage", - "requiredParameters": { - "indexerid": {"desc": "unique id of a show"} - }, - "optionalParameters": { - "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, - } + _help = { + "desc": "get the poster stored for a show in sickrage", + "requiredParameters": { + "indexerid": {"desc": "unique id of a show"} + }, + "optionalParameters": { + "tvdbid": {"desc": "thetvdb.com unique id of a show"}, + } } def __init__(self, args, kwargs): @@ -2417,18 +2352,21 @@ class CMD_ShowGetPoster(ApiCall): def run(self): """ get the poster for a show in sickrage """ - return {'outputType': 'image', 'image': self.rh.showPoster(self.indexerid, 'poster')} + return { + 'outputType': 'image', + 'image': ShowPoster(self.indexerid).get_media() + } class CMD_ShowGetBanner(ApiCall): - _help = {"desc": "get the banner stored for a show in sickrage", - "requiredParameters": { - "indexerid": {"desc": "unique id of a show"} - }, - "optionalParameters": { - "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, - } + _help = { + "desc": "get the banner stored for a show in sickrage", + "requiredParameters": { + "indexerid": {"desc": "unique id of a show"} + }, + "optionalParameters": { + "tvdbid": {"desc": "thetvdb.com unique id of a show"}, + } } def __init__(self, args, kwargs): @@ -2440,7 +2378,10 @@ class CMD_ShowGetBanner(ApiCall): def run(self): """ get the banner for a show in sickrage """ - return {'outputType': 'image', 'image': self.rh.showPoster(self.indexerid, 'banner')} + return { + 'outputType': 'image', + 'image': ShowBanner(self.indexerid).get_media() + } class CMD_ShowGetNetworkLogo(ApiCall): @@ -2455,9 +2396,6 @@ class CMD_ShowGetNetworkLogo(ApiCall): "tvdbid": { "desc": "TheTVDB.com unique id of a show", }, - "tvrageid": { - "desc": "TVRage.con unique id of a show", - }, }, } @@ -2474,7 +2412,7 @@ class CMD_ShowGetNetworkLogo(ApiCall): """ return { 'outputType': 'image', - 'image': self.rh.showNetworkLogo(self.indexerid) + 'image': ShowNetworkLogo(self.indexerid).get_media() } @@ -2486,7 +2424,6 @@ class CMD_ShowGetFanArt(ApiCall): }, "optionalParameters": { "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, }, } @@ -2501,7 +2438,7 @@ class CMD_ShowGetFanArt(ApiCall): """ Get the fan art for a show in SickRage """ return { 'outputType': 'image', - 'image': self.rh.showPoster(self.indexerid, 'fanart') + 'image': ShowFanArt(self.indexerid).get_media() } @@ -2512,7 +2449,6 @@ class CMD_ShowPause(ApiCall): }, "optionalParameters": { "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, "pause": {"desc": "set the pause state of the show"} } } @@ -2547,7 +2483,6 @@ class CMD_ShowRefresh(ApiCall): }, "optionalParameters": { "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, } } @@ -2579,7 +2514,6 @@ class CMD_ShowSeasonList(ApiCall): }, "optionalParameters": { "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, "sort": {"desc": "change the sort order from descending to ascending"} } } @@ -2621,7 +2555,6 @@ class CMD_ShowSeasons(ApiCall): }, "optionalParameters": { "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, "season": {"desc": "the season number"}, } } @@ -2693,7 +2626,6 @@ class CMD_ShowSetQuality(ApiCall): }, "optionalParameters": { "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, "initial": {"desc": "initial quality for the show"}, "archive": {"desc": "archive quality for the show"} } @@ -2761,7 +2693,6 @@ class CMD_ShowStats(ApiCall): }, "optionalParameters": { "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, } } @@ -2870,7 +2801,6 @@ class CMD_ShowUpdate(ApiCall): }, "optionalParameters": { "tvdbid": {"desc": "thetvdb.com unique id of a show"}, - "tvrageid": {"desc": "tvrage.com unique id of a show"}, } } @@ -2930,8 +2860,6 @@ class CMD_Shows(ApiCall): "anime": curShow.anime, "indexerid": curShow.indexerid, "tvdbid": indexerShow[1], - "tvrage_id": indexerShow[2], - "tvrage_name": curShow.name, "network": curShow.network, "show_name": curShow.name, "status": curShow.status, @@ -2977,17 +2905,11 @@ class CMD_ShowsStats(ApiCall): stats["shows_active"] = len( [show for show in sickbeard.showList if show.paused == 0 and "Unknown" not in show.status and "Ended" not in show.status]) stats["ep_downloaded"] = myDB.select("SELECT COUNT(*) FROM tv_episodes WHERE status IN (" + ",".join( - [str(show) for show in - Quality.DOWNLOADED + [ARCHIVED]]) + ") AND season != 0 and episode != 0 AND airdate <= " + today + "")[0][ - 0] + [str(show) for show in Quality.DOWNLOADED + Quality.ARCHIVED]) + ") AND season != 0 and episode != 0 AND airdate <= " + today + "")[0][0] stats["ep_snatched"] = myDB.select("SELECT COUNT(*) FROM tv_episodes WHERE status IN (" + ",".join( - [str(show) for show in - Quality.SNATCHED + Quality.SNATCHED_PROPER]) + ") AND season != 0 and episode != 0 AND airdate <= " + today + "")[0][ - 0] - stats["ep_total"] = myDB.select( - "SELECT COUNT(*) FROM tv_episodes WHERE season != 0 AND episode != 0 AND (airdate != 1 OR status IN (" + ",".join( - [str(show) for show in (Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER) + [ - ARCHIVED]]) + ")) AND airdate <= " + today + " AND status != " + str(IGNORED) + "")[0][0] + [str(show) for show in Quality.SNATCHED + Quality.SNATCHED_PROPER]) + ") AND season != 0 and episode != 0 AND airdate <= " + today + "")[0][0] + stats["ep_total"] = myDB.select("SELECT COUNT(*) FROM tv_episodes WHERE season != 0 AND episode != 0 AND (airdate != 1 OR status IN (" + ",".join( + [str(show) for show in Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.ARCHIVED]) + ")) AND airdate <= " + today + " AND status != " + str(IGNORED) + "")[0][0] return _responds(RESULT_SUCCESS, stats) @@ -2995,55 +2917,56 @@ class CMD_ShowsStats(ApiCall): # this is reserved for cmd indexes used while cmd chaining # WARNING: never define a param name that contains a "." (dot) -# this is reserved for cmd namspaces used while cmd chaining -_functionMaper = {"help": CMD_Help, - "future": CMD_ComingEpisodes, - "episode": CMD_Episode, - "episode.search": CMD_EpisodeSearch, - "episode.setstatus": CMD_EpisodeSetStatus, - "episode.subtitlesearch": CMD_SubtitleSearch, - "exceptions": CMD_Exceptions, - "history": CMD_History, - "history.clear": CMD_HistoryClear, - "history.trim": CMD_HistoryTrim, - "failed": CMD_Failed, - "backlog": CMD_Backlog, - "logs": CMD_Logs, - "sb": CMD_SickBeard, - "postprocess": CMD_PostProcess, - "sb.addrootdir": CMD_SickBeardAddRootDir, - "sb.checkversion": CMD_SickBeardCheckVersion, - "sb.checkscheduler": CMD_SickBeardCheckScheduler, - "sb.deleterootdir": CMD_SickBeardDeleteRootDir, - "sb.getdefaults": CMD_SickBeardGetDefaults, - "sb.getmessages": CMD_SickBeardGetMessages, - "sb.getrootdirs": CMD_SickBeardGetRootDirs, - "sb.pausebacklog": CMD_SickBeardPauseBacklog, - "sb.ping": CMD_SickBeardPing, - "sb.restart": CMD_SickBeardRestart, - "sb.searchindexers": CMD_SickBeardSearchIndexers, - "sb.searchtvdb": CMD_SickBeardSearchTVDB, - "sb.searchtvrage": CMD_SickBeardSearchTVRAGE, - "sb.setdefaults": CMD_SickBeardSetDefaults, - "sb.update": CMD_SickBeardUpdate, - "sb.shutdown": CMD_SickBeardShutdown, - "show": CMD_Show, - "show.addexisting": CMD_ShowAddExisting, - "show.addnew": CMD_ShowAddNew, - "show.cache": CMD_ShowCache, - "show.delete": CMD_ShowDelete, - "show.getquality": CMD_ShowGetQuality, - "show.getposter": CMD_ShowGetPoster, - "show.getbanner": CMD_ShowGetBanner, - "show.getnetworklogo": CMD_ShowGetNetworkLogo, - "show.getfanart": CMD_ShowGetFanArt, - "show.pause": CMD_ShowPause, - "show.refresh": CMD_ShowRefresh, - "show.seasonlist": CMD_ShowSeasonList, - "show.seasons": CMD_ShowSeasons, - "show.setquality": CMD_ShowSetQuality, - "show.stats": CMD_ShowStats, - "show.update": CMD_ShowUpdate, - "shows": CMD_Shows, - "shows.stats": CMD_ShowsStats +# this is reserved for cmd namespaces used while cmd chaining +_functionMaper = { + "help": CMD_Help, + "future": CMD_ComingEpisodes, + "episode": CMD_Episode, + "episode.search": CMD_EpisodeSearch, + "episode.setstatus": CMD_EpisodeSetStatus, + "episode.subtitlesearch": CMD_SubtitleSearch, + "exceptions": CMD_Exceptions, + "history": CMD_History, + "history.clear": CMD_HistoryClear, + "history.trim": CMD_HistoryTrim, + "failed": CMD_Failed, + "backlog": CMD_Backlog, + "logs": CMD_Logs, + "sb": CMD_SickBeard, + "postprocess": CMD_PostProcess, + "sb.addrootdir": CMD_SickBeardAddRootDir, + "sb.checkversion": CMD_SickBeardCheckVersion, + "sb.checkscheduler": CMD_SickBeardCheckScheduler, + "sb.deleterootdir": CMD_SickBeardDeleteRootDir, + "sb.getdefaults": CMD_SickBeardGetDefaults, + "sb.getmessages": CMD_SickBeardGetMessages, + "sb.getrootdirs": CMD_SickBeardGetRootDirs, + "sb.pausebacklog": CMD_SickBeardPauseBacklog, + "sb.ping": CMD_SickBeardPing, + "sb.restart": CMD_SickBeardRestart, + "sb.searchindexers": CMD_SickBeardSearchIndexers, + "sb.searchtvdb": CMD_SickBeardSearchTVDB, + "sb.searchtvrage": CMD_SickBeardSearchTVRAGE, + "sb.setdefaults": CMD_SickBeardSetDefaults, + "sb.update": CMD_SickBeardUpdate, + "sb.shutdown": CMD_SickBeardShutdown, + "show": CMD_Show, + "show.addexisting": CMD_ShowAddExisting, + "show.addnew": CMD_ShowAddNew, + "show.cache": CMD_ShowCache, + "show.delete": CMD_ShowDelete, + "show.getquality": CMD_ShowGetQuality, + "show.getposter": CMD_ShowGetPoster, + "show.getbanner": CMD_ShowGetBanner, + "show.getnetworklogo": CMD_ShowGetNetworkLogo, + "show.getfanart": CMD_ShowGetFanArt, + "show.pause": CMD_ShowPause, + "show.refresh": CMD_ShowRefresh, + "show.seasonlist": CMD_ShowSeasonList, + "show.seasons": CMD_ShowSeasons, + "show.setquality": CMD_ShowSetQuality, + "show.stats": CMD_ShowStats, + "show.update": CMD_ShowUpdate, + "shows": CMD_Shows, + "shows.stats": CMD_ShowsStats } diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 5b1d5343a85b9d0855c8493aa12f5d22636280f1..7c65a2024e4f437cc038b72465a955db6c281730 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -27,12 +27,11 @@ import codecs import sickbeard from sickbeard import config, sab from sickbeard import clients -from sickbeard import history, notifiers, processTV +from sickbeard import notifiers, processTV from sickbeard import ui -from sickbeard import logger, helpers, exceptions, classes, db, scheduler, showUpdater +from sickbeard import logger, helpers, exceptions, classes, db from sickbeard import encodingKludge as ek from sickbeard import search_queue -from sickbeard import image_cache from sickbeard import naming from sickbeard import subtitles from sickbeard import network_timezones @@ -50,11 +49,18 @@ from sickbeard.scene_numbering import get_scene_numbering, set_scene_numbering, from imdbPopular import imdb_popular -from dateutil import tz, parser as dateutil_parser +from dateutil import tz from unrar2 import RarFile -import adba, subliminal +import adba from libtrakt import TraktAPI from libtrakt.exceptions import traktException +from sickrage.media.ShowBanner import ShowBanner +from sickrage.media.ShowFanArt import ShowFanArt +from sickrage.media.ShowNetworkLogo import ShowNetworkLogo +from sickrage.media.ShowPoster import ShowPoster +from sickrage.show.History import History as HistoryTool +from sickrage.system.Restart import Restart +from sickrage.system.Shutdown import Shutdown from versionChecker import CheckVersion import requests @@ -65,16 +71,11 @@ try: except ImportError: import simplejson as json -try: - import xml.etree.cElementTree as etree -except ImportError: - import xml.etree.ElementTree as etree - from mako.template import Template as MakoTemplate from mako.lookup import TemplateLookup from tornado.routes import route -from tornado.web import RequestHandler, HTTPError, authenticated, asynchronous +from tornado.web import RequestHandler, HTTPError, authenticated from tornado.gen import coroutine from tornado.ioloop import IOLoop from tornado.concurrent import run_on_executor @@ -84,6 +85,7 @@ route_locks = {} mako_path = mako_cache = mako_lookup = None + class _setupLookup(): def __init__(self, *args, **kwargs): global mako_path @@ -91,14 +93,16 @@ class _setupLookup(): global mako_lookup if not mako_path: - mako_path = os.path.join(sickbeard.PROG_DIR, "gui/" + sickbeard.GUI_NAME + "/interfaces/default/") + mako_path = os.path.join(sickbeard.PROG_DIR, "gui/" + sickbeard.GUI_NAME + "/views/") if not mako_cache: mako_cache = os.path.join(sickbeard.CACHE_DIR, 'mako') if not mako_lookup: mako_lookup = TemplateLookup(directories=[mako_path], module_directory=mako_cache, format_exceptions=True) + class PageTemplate(MakoTemplate): arguments = {} + def __init__(self, rh, file, *args, **kwargs): _setupLookup() kwargs['filename'] = os.path.join(mako_path, file) @@ -116,6 +120,7 @@ class PageTemplate(MakoTemplate): self.arguments['sbThemeName'] = sickbeard.THEME_NAME self.arguments['sbDefaultPage'] = sickbeard.DEFAULT_PAGE self.arguments['sbLogin'] = rh.get_current_user() + self.arguments['sbStartTime'] = rh.startTime if rh.request.headers['Host'][0] == '[': self.arguments['sbHost'] = re.match("^\[.*\]", rh.request.headers['Host'], re.X | re.M | re.S).group(0) @@ -153,10 +158,17 @@ class PageTemplate(MakoTemplate): if key not in kwargs: kwargs[key] = self.arguments[key] + kwargs['makoStartTime'] = time.time() + return super(PageTemplate, self).render(*args, **kwargs) + class BaseHandler(RequestHandler): + startTime = 0. + def __init__(self, *args, **kwargs): + self.startTime = time.time() + super(BaseHandler, self).__init__(*args, **kwargs) #def set_default_headers(self): @@ -369,48 +381,21 @@ class WebRoot(WebHandler): return t.render(title="Api Builder", header="Api Builder", sortedShowList=sortedShowList, seasonSQLResults=seasonSQLResults, episodeSQLResults=episodeSQLResults, apikey=apikey) def showPoster(self, show=None, which=None): - # Redirect initial poster/banner thumb to default images - if which[0:6] == 'poster': - default_image_name = 'poster.png' - elif which[0:6] == 'fanart': - default_image_name = 'fanart.png' - else: - default_image_name = 'banner.png' - - # image_path = ek.ek(os.path.join, sickbeard.PROG_DIR, 'gui', 'slick', 'images', default_image_name) - static_image_path = os.path.join('/images', default_image_name) - if show and sickbeard.helpers.findCertainShow(sickbeard.showList, int(show)): - cache_obj = image_cache.ImageCache() - - image_file_name = None - if which == 'poster': - image_file_name = cache_obj.poster_path(show) - if which == 'poster_thumb' or which == 'small': - image_file_name = cache_obj.poster_thumb_path(show) - if which == 'banner': - image_file_name = cache_obj.banner_path(show) - if which == 'banner_thumb': - image_file_name = cache_obj.banner_thumb_path(show) - if which == 'fanart': - image_file_name = cache_obj.fanart_path(show) - - if ek.ek(os.path.isfile, image_file_name): - static_image_path = os.path.normpath(image_file_name.replace(sickbeard.CACHE_DIR, '/cache')) - - static_image_path = static_image_path.replace('\\', '/') - return self.redirect(static_image_path, permanent=True) - - def showNetworkLogo(self, show=None): - show = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show)) + media_format = ('normal', 'thumb')[which in ('banner_thumb', 'poster_thumb', 'small')] - if show: - image_file_name = show.network_logo_name - else: - image_file_name = 'nonetwork' + if which[0:6] == 'banner': + return ShowBanner(show, media_format).get_media() - static_image_path = '%s/images/network/%s.png' % (sickbeard.WEB_ROOT, image_file_name) + if which[0:6] == 'fanart': + return ShowFanArt(show, media_format).get_media() + + if which[0:6] == 'poster': + return ShowPoster(show, media_format).get_media() - return self.redirect(static_image_path, permanent=True) + if which[0:7] == 'network': + return ShowNetworkLogo(show).get_media() + + return None def setHomeLayout(self, layout): @@ -443,15 +428,6 @@ class WebRoot(WebHandler): return self.redirect("/history/") - def setHistoryLimit(self, history_limit): - - if not int(history_limit): - history_limit = 100 - - sickbeard.HISTORY_LIMIT = history_limit - - return self.redirect("/history/") - def toggleDisplayShowSpecials(self, show): sickbeard.DISPLAY_SHOW_SPECIALS = not sickbeard.DISPLAY_SHOW_SPECIALS @@ -700,12 +676,12 @@ class Home(WebRoot): def HomeMenu(self): menu = [ - {'title': 'Add Shows', 'path': 'home/addShows/', }, - {'title': 'Manual Post-Processing', 'path': 'home/postprocess/'}, - {'title': 'Update KODI', 'path': 'home/updateKODI/', 'requires': self.haveKODI()}, - {'title': 'Update Plex', 'path': 'home/updatePLEX/', 'requires': self.havePLEX()}, - {'title': 'Update Emby', 'path': 'home/updateEMBY/', 'requires': self.haveEMBY()}, - {'title': 'Manage Torrents', 'path': 'manage/manageTorrents/', 'requires': self.haveTORRENT()}, + {'title': 'Add Shows', 'path': 'home/addShows/', 'icon': 'ui-icon ui-icon-video'}, + {'title': 'Manual Post-Processing', 'path': 'home/postprocess/', 'icon': 'ui-icon ui-icon-folder-open'}, + {'title': 'Update KODI', 'path': 'home/updateKODI/', 'requires': self.haveKODI(), 'icon': 'submenu-icon-kodi'}, + {'title': 'Update Plex', 'path': 'home/updatePLEX/', 'requires': self.havePLEX(), 'icon': 'ui-icon ui-icon-refresh'}, + {'title': 'Update Emby', 'path': 'home/updateEMBY/', 'requires': self.haveEMBY(), 'icon': 'ui-icon ui-icon-refresh'}, + {'title': 'Manage Torrents', 'path': 'manage/manageTorrents/', 'requires': self.haveTORRENT(), 'icon': 'submenu-icon-bittorrent'}, ] return menu @@ -1032,13 +1008,8 @@ class Home(WebRoot): return "Trakt Authorized" return "Trakt Not Authorized!" - def testTrakt(self, username=None, password=None, disable_ssl=None, blacklist_name=None): - # self.set_header('Cache-Control', 'max-age=0,no-cache,no-store') - if disable_ssl == 'true': - disable_ssl = True - else: - disable_ssl = False - return notifiers.trakt_notifier.test_notify(username, disable_ssl, blacklist_name) + def testTrakt(self, username=None, blacklist_name=None): + return notifiers.trakt_notifier.test_notify(username, blacklist_name) def loadShowNotifyLists(self): @@ -1063,7 +1034,7 @@ class Home(WebRoot): if myDB.action("UPDATE tv_shows SET notify_list = ? WHERE show_id = ?", [emails, show]): return 'OK' else: - return 'ERROR: %s' % myDB.last_err + return 'ERROR' def testEmail(self, host=None, port=None, smtp_from=None, use_tls=None, user=None, pwd=None, to=None): @@ -1120,10 +1091,8 @@ class Home(WebRoot): rootDir = {} if sickbeard.ROOT_DIRS: backend_pieces = sickbeard.ROOT_DIRS.split('|') - backend_default = 'rd-' + backend_pieces[0] backend_dirs = backend_pieces[1:] else: - backend_default = '' backend_dirs = [] if len(backend_dirs): @@ -1131,13 +1100,11 @@ class Home(WebRoot): rootDir[subject] = helpers.getDiskSpaceUsage(subject) t = PageTemplate(rh=self, file="status.mako") - return t.render(title='Status', header='Status', topmenu='home', submenu=self.HomeMenu(), tvdirFree=tvdirFree, rootDir=rootDir) + return t.render(title='Status', header='Status', topmenu='system', submenu=self.HomeMenu(), tvdirFree=tvdirFree, rootDir=rootDir) def shutdown(self, pid=None): - if str(pid) != str(sickbeard.PID): - return self.redirect('/' + sickbeard.DEFAULT_PAGE +'/') - - sickbeard.events.put(sickbeard.events.SystemEvent.SHUTDOWN) + if not Shutdown.stop(pid): + return self.redirect('/' + sickbeard.DEFAULT_PAGE + '/') title = "Shutting down" message = "SickRage is shutting down..." @@ -1145,15 +1112,12 @@ class Home(WebRoot): return self._genericMessage(title, message) def restart(self, pid=None): - if str(pid) != str(sickbeard.PID): - return self.redirect('/' + sickbeard.DEFAULT_PAGE +'/') + if not Restart.restart(pid): + return self.redirect('/' + sickbeard.DEFAULT_PAGE + '/') t = PageTemplate(rh=self, file="restart.mako") - # restart - sickbeard.events.put(sickbeard.events.SystemEvent.RESTART) - - return t.render(title="Home", header="Restarting SickRage", topmenu="home", submenu=self.HomeMenu()) + return t.render(title="Home", header="Restarting SickRage", topmenu="system", submenu=self.HomeMenu()) def updateCheck(self, pid=None): if str(pid) != str(sickbeard.PID): @@ -1161,7 +1125,7 @@ class Home(WebRoot): sickbeard.versionCheckScheduler.action.check_for_new_version(force=True) - return self.redirect('/' + sickbeard.DEFAULT_PAGE +'/') + return self.redirect('/' + sickbeard.DEFAULT_PAGE + '/') def update(self, pid=None): @@ -1234,7 +1198,7 @@ class Home(WebRoot): ) t = PageTemplate(rh=self, file="displayShow.mako") - submenu = [{'title': 'Edit', 'path': 'home/editShow?show=%d' % showObj.indexerid}] + submenu = [{'title': 'Edit', 'path': 'home/editShow?show=%d' % showObj.indexerid, 'icon': 'ui-icon ui-icon-pencil'}] try: showLoc = (showObj.location, True) @@ -1267,27 +1231,20 @@ class Home(WebRoot): if not sickbeard.showQueueScheduler.action.isBeingAdded(showObj): if not sickbeard.showQueueScheduler.action.isBeingUpdated(showObj): if showObj.paused: - submenu.append({'title': 'Resume', 'path': 'home/togglePause?show=%d' % showObj.indexerid}) + submenu.append({'title': 'Resume', 'path': 'home/togglePause?show=%d' % showObj.indexerid, 'icon': 'ui-icon ui-icon-play'}) else: - submenu.append({'title': 'Pause', 'path': 'home/togglePause?show=%d' % showObj.indexerid}) + submenu.append({'title': 'Pause', 'path': 'home/togglePause?show=%d' % showObj.indexerid, 'icon': 'ui-icon ui-icon-pause'}) - submenu.append( - {'title': 'Remove', 'path': 'home/deleteShow?show=%d' % showObj.indexerid, 'confirm': True}) - submenu.append({'title': 'Re-scan files', 'path': 'home/refreshShow?show=%d' % showObj.indexerid}) - submenu.append( - {'title': 'Force Full Update', 'path': 'home/updateShow?show=%d&force=1' % showObj.indexerid}) - submenu.append({'title': 'Update show in KODI', - 'path': 'home/updateKODI?show=%d' % showObj.indexerid, 'requires': self.haveKODI()}) - - submenu.append({'title': 'Update show in Emby', - 'path': 'home/updateEMBY?show=%d' % showObj.indexerid, 'requires': self.haveEMBY()}) - - submenu.append({'title': 'Preview Rename', 'path': 'home/testRename?show=%d' % showObj.indexerid}) + submenu.append({'title': 'Remove', 'path': 'home/deleteShow?show=%d' % showObj.indexerid, 'confirm': True, 'icon': 'ui-icon ui-icon-trash'}) + submenu.append({'title': 'Re-scan files', 'path': 'home/refreshShow?show=%d' % showObj.indexerid, 'icon': 'ui-icon ui-icon-refresh'}) + submenu.append({'title': 'Force Full Update', 'path': 'home/updateShow?show=%d&force=1' % showObj.indexerid, 'icon': 'ui-icon ui-icon-transfer-e-w'}) + submenu.append({'title': 'Update show in KODI','path': 'home/updateKODI?show=%d' % showObj.indexerid, 'requires': self.haveKODI(), 'icon': 'submenu-icon-kodi'}) + submenu.append({'title': 'Update show in Emby', 'path': 'home/updateEMBY?show=%d' % showObj.indexerid, 'requires': self.haveEMBY(), 'icon': 'ui-icon ui-icon-refresh'}) + submenu.append({'title': 'Preview Rename', 'path': 'home/testRename?show=%d' % showObj.indexerid, 'icon': 'ui-icon ui-icon-tag'}) if sickbeard.USE_SUBTITLES and not sickbeard.showQueueScheduler.action.isBeingSubtitled( showObj) and showObj.subtitles: - submenu.append( - {'title': 'Download Subtitles', 'path': 'home/subtitleShow?show=%d' % showObj.indexerid}) + submenu.append({'title': 'Download Subtitles', 'path': 'home/subtitleShow?show=%d' % showObj.indexerid, 'icon': 'ui-icon ui-icon-comment'}) epCounts = {} epCats = {} @@ -1906,7 +1863,7 @@ class Home(WebRoot): ep_obj_rename_list.reverse() t = PageTemplate(rh=self, file="testRename.mako") - submenu = [{'title': 'Edit', 'path': 'home/editShow?show=%d' % showObj.indexerid}] + submenu = [{'title': 'Edit', 'path': 'home/editShow?show=%d' % showObj.indexerid, 'icon': 'ui-icon ui-icon-pencil'}] return t.render(submenu=submenu, ep_obj_list=ep_obj_rename_list, show=showObj, title='Preview Rename', header='Preview Rename') @@ -2212,6 +2169,7 @@ class HomeNews(Home): return t.render(title="News", header="News", topmenu="news", data=data, submenu=self.HomeMenu()) + @route('/changes(/?.*)') class HomeChangeLog(Home): def __init__(self, *args, **kwargs): @@ -2227,7 +2185,8 @@ class HomeChangeLog(Home): t = PageTemplate(rh=self, file="markdown.mako") data = markdown2.markdown(changes if changes else "The was a problem connecting to github, please refresh and try again") - return t.render(title="Changelog", header="Changelog", topmenu="changes", data=data, submenu=self.HomeMenu()) + return t.render(title="Changelog", header="Changelog", topmenu="system", data=data, submenu=self.HomeMenu()) + @route('/home/postprocess(/?.*)') class HomePostProcess(Home): @@ -2482,9 +2441,7 @@ class HomeAddShows(Home): show = {} show['show']=show_detail try: - tvdb_id = int(show['show']['ids']['tvdb']) - tvrage_id = int(show['show']['ids']['tvrage'] or 0) - if not helpers.findCertainShow(sickbeard.showList, [tvdb_id, tvrage_id]): + if not helpers.findCertainShow(sickbeard.showList, [int(show['show']['ids']['tvdb'])]): if show['show']['ids']['tvdb'] not in (lshow['show']['ids']['tvdb'] for lshow in library_shows): if not_liked_show: if show['show']['ids']['tvdb'] not in (show['show']['ids']['tvdb'] for show in not_liked_show if show['type'] == 'show'): @@ -2538,9 +2495,7 @@ class HomeAddShows(Home): library_shows = trakt_api.traktRequest("sync/collection/shows?extended=full") or [] for show in shows: try: - tvdb_id = int(show['show']['ids']['tvdb']) - tvrage_id = int(show['show']['ids']['tvrage'] or 0) - if not helpers.findCertainShow(sickbeard.showList, [tvdb_id, tvrage_id]): + if not helpers.findCertainShow(sickbeard.showList, [int(show['show']['ids']['tvdb'])]): if show['show']['ids']['tvdb'] not in (lshow['show']['ids']['tvdb'] for lshow in library_shows): if not_liked_show: if show['show']['ids']['tvdb'] not in (show['show']['ids']['tvdb'] for show in not_liked_show if show['type'] == 'show'): @@ -2845,20 +2800,20 @@ class Manage(Home, WebRoot): def ManageMenu(self): menu = [ - {'title': 'Backlog Overview', 'path': 'manage/backlogOverview/'}, - {'title': 'Manage Searches', 'path': 'manage/manageSearches/'}, - {'title': 'Episode Status Management', 'path': 'manage/episodeStatuses/'}, ] + {'title': 'Backlog Overview', 'path': 'manage/backlogOverview/', 'icon': 'ui-icon ui-icon-refresh'}, + {'title': 'Manage Searches', 'path': 'manage/manageSearches/', 'icon': 'ui-icon ui-icon-search'}, + {'title': 'Episode Status Management', 'path': 'manage/episodeStatuses/', 'icon': 'ui-icon ui-icon-transferthick-e-w'}, ] 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:'): - menu.append({'title': 'Manage Torrents', 'path': 'manage/manageTorrents/'}) + menu.append({'title': 'Manage Torrents', 'path': 'manage/manageTorrents/', 'icon': 'submenu-icon-bittorrent'}) if sickbeard.USE_SUBTITLES: - menu.append({'title': 'Missed Subtitle Management', 'path': 'manage/subtitleMissed/'}) + menu.append({'title': 'Missed Subtitle Management', 'path': 'manage/subtitleMissed/', 'icon': 'ui-icon ui-icon-transferthick-e-w'}) if sickbeard.USE_FAILED_DOWNLOADS: - menu.append({'title': 'Failed Downloads', 'path': 'manage/failedDownloads/'}) + menu.append({'title': 'Failed Downloads', 'path': 'manage/failedDownloads/', 'icon': 'submenu-icon-failed-download'}) return menu @@ -3596,90 +3551,68 @@ class History(WebRoot): def __init__(self, *args, **kwargs): super(History, self).__init__(*args, **kwargs) - def index(self, limit=100): + self.history = HistoryTool() - # sqlResults = myDB.select("SELECT h.*, show_name, name FROM history h, tv_shows s, tv_episodes e WHERE h.showid=s.indexer_id AND h.showid=e.showid AND h.season=e.season AND h.episode=e.episode ORDER BY date DESC LIMIT "+str(numPerPage*(p-1))+", "+str(numPerPage)) - myDB = db.DBConnection() - if limit == "0": - sqlResults = myDB.select( - "SELECT h.*, show_name FROM history h, tv_shows s WHERE h.showid=s.indexer_id ORDER BY date DESC") - else: - sqlResults = myDB.select( - "SELECT h.*, show_name FROM history h, tv_shows s WHERE h.showid=s.indexer_id ORDER BY date DESC LIMIT ?", - [limit]) + def index(self, limit=100): + limit = int(limit) + sickbeard.HISTORY_LIMIT = limit - history = {'show_id': 0, 'season': 0, 'episode': 0, 'quality': 0, - 'actions': [{'time': '', 'action': '', 'provider': ''}]} compact = [] + data = self.history.get(limit) + + for row in data: + action = { + 'action': row['action'], + 'provider': row['provider'], + 'resource': row['resource'], + 'time': row['date'] + } + + if not any((history['show_id'] == row['show_id'] and + history['season'] == row['season'] and + history['episode'] == row['episode'] and + history['quality'] == row['quality']) for history in compact): + history = { + 'actions': [action], + 'episode': row['episode'], + 'quality': row['quality'], + 'resource': row['resource'], + 'season': row['season'], + 'show_id': row['show_id'], + 'show_name': row['show_name'] + } - for sql_result in sqlResults: - - if not any((history['show_id'] == sql_result['showid'] - and history['season'] == sql_result['season'] - and history['episode'] == sql_result['episode'] - and history['quality'] == sql_result['quality']) - for history in compact): - - history = {} - history['show_id'] = sql_result['showid'] - history['season'] = sql_result['season'] - history['episode'] = sql_result['episode'] - history['quality'] = sql_result['quality'] - history['show_name'] = sql_result['show_name'] - history['resource'] = sql_result['resource'] - - action = {} - history['actions'] = [] - - action['time'] = sql_result['date'] - action['action'] = sql_result['action'] - action['provider'] = sql_result['provider'] - action['resource'] = sql_result['resource'] - history['actions'].append(action) - history['actions'].sort(key=lambda x: x['time']) compact.append(history) else: - index = [i for i, dict in enumerate(compact) \ - if dict['show_id'] == sql_result['showid'] \ - and dict['season'] == sql_result['season'] \ - and dict['episode'] == sql_result['episode'] - and dict['quality'] == sql_result['quality']][0] - - action = {} + index = [i for i, item in enumerate(compact) + if item['show_id'] == row['show_id'] and + item['season'] == row['season'] and + item['episode'] == row['episode'] and + item['quality'] == row['quality']][0] history = compact[index] - - action['time'] = sql_result['date'] - action['action'] = sql_result['action'] - action['provider'] = sql_result['provider'] - action['resource'] = sql_result['resource'] history['actions'].append(action) history['actions'].sort(key=lambda x: x['time'], reverse=True) t = PageTemplate(rh=self, file="history.mako") submenu = [ - {'title': 'Clear History', 'path': 'history/clearHistory'}, - {'title': 'Trim History', 'path': 'history/trimHistory'}, + {'title': 'Clear History', 'path': 'history/clearHistory', 'icon': 'ui-icon ui-icon-trash', 'class': 'clearhistory'}, + {'title': 'Trim History', 'path': 'history/trimHistory', 'icon': 'ui-icon ui-icon-trash', 'class': 'trimhistory'}, ] - return t.render(historyResults=sqlResults, compactResults=compact, limit=limit, submenu=submenu, title='History', header='History', topmenu="history") - + return t.render(historyResults=data, compactResults=compact, limit=limit, submenu=submenu, title='History', header='History', topmenu="history") def clearHistory(self): - - myDB = db.DBConnection() - myDB.action("DELETE FROM history WHERE 1=1") + self.history.clear() ui.notifications.message('History cleared') - return self.redirect("/history/") + return self.redirect("/history/") def trimHistory(self): + self.history.trim() - myDB = db.DBConnection() - myDB.action("DELETE FROM history WHERE date < " + str( - (datetime.datetime.today() - datetime.timedelta(days=30)).strftime(history.dateFormat))) + ui.notifications.message('Removed history entries older than 30 days') - ui.notifications.message('Removed history entries greater than 30 days old') return self.redirect("/history/") @@ -3690,14 +3623,14 @@ class Config(WebRoot): def ConfigMenu(self): menu = [ - {'title': 'General', 'path': 'config/general/'}, - {'title': 'Backup/Restore', 'path': 'config/backuprestore/'}, - {'title': 'Search Settings', 'path': 'config/search/'}, - {'title': 'Search Providers', 'path': 'config/providers/'}, - {'title': 'Subtitles Settings', 'path': 'config/subtitles/'}, - {'title': 'Post Processing', 'path': 'config/postProcessing/'}, - {'title': 'Notifications', 'path': 'config/notifications/'}, - {'title': 'Anime', 'path': 'config/anime/'}, + {'title': 'General', 'path': 'config/general/', 'icon': 'ui-icon ui-icon-gear'}, + {'title': 'Backup/Restore', 'path': 'config/backuprestore/', 'icon': 'ui-icon ui-icon-gear'}, + {'title': 'Search Settings', 'path': 'config/search/', 'icon': 'ui-icon ui-icon-search'}, + {'title': 'Search Providers', 'path': 'config/providers/', 'icon': 'ui-icon ui-icon-search'}, + {'title': 'Subtitles Settings', 'path': 'config/subtitles/', 'icon': 'ui-icon ui-icon-comment'}, + {'title': 'Post Processing', 'path': 'config/postProcessing/', 'icon': 'ui-icon ui-icon-folder-open'}, + {'title': 'Notifications', 'path': 'config/notifications/', 'icon': 'ui-icon ui-icon-note'}, + {'title': 'Anime', 'path': 'config/anime/', 'icon': 'submenu-icon-anime'}, ] return menu @@ -3875,7 +3808,7 @@ class ConfigBackupRestore(Config): def index(self): t = PageTemplate(rh=self, file="config_backuprestore.mako") - return t.render(submenu=self.ConfigMenu(), title='Config - Backup/Restore', header='Backup/Restore', topmenu='comingEpisodes') + return t.render(submenu=self.ConfigMenu(), title='Config - Backup/Restore', header='Backup/Restore', topmenu='config') def backup(self, backupDir=None): @@ -3937,7 +3870,6 @@ class ConfigSearch(Config): return t.render(submenu=self.ConfigMenu(), title='Config - Episode Search', header='Search Settings', topmenu='config') - def saveSearch(self, use_nzbs=None, use_torrents=None, nzb_dir=None, sab_username=None, sab_password=None, sab_apikey=None, sab_category=None, sab_category_anime=None, sab_host=None, nzbget_username=None, nzbget_password=None, nzbget_category=None, nzbget_category_anime=None, nzbget_priority=None, @@ -4035,7 +3967,6 @@ class ConfigPostProcessing(Config): return t.render(submenu=self.ConfigMenu(), title='Config - Post Processing', header='Post Processing', topmenu='config') - def savePostProcessing(self, naming_pattern=None, naming_multi_ep=None, kodi_data=None, kodi_12plus_data=None, mediabrowser_data=None, sony_ps3_data=None, wdtv_data=None, tivo_data=None, mede8er_data=None, @@ -4225,7 +4156,6 @@ class ConfigProviders(Config): return t.render(submenu=self.ConfigMenu(), title='Config - Providers', header='Search Providers', topmenu='config') - def canAddNewznabProvider(self, name): if not name: @@ -4240,7 +4170,6 @@ class ConfigProviders(Config): else: return json.dumps({'success': tempProvider.getID()}) - def saveNewznabProvider(self, name, url, key=''): if not name or not url: @@ -4702,7 +4631,6 @@ class ConfigNotifications(Config): return t.render(submenu=self.ConfigMenu(), title='Config - Notifications', header='Notifications', topmenu='config') - def saveNotifications(self, use_kodi=None, kodi_always_on=None, kodi_notify_onsnatch=None, kodi_notify_ondownload=None, kodi_notify_onsubtitledownload=None, kodi_update_onlyfirst=None, @@ -4937,7 +4865,6 @@ class ConfigSubtitles(Config): return t.render(submenu=self.ConfigMenu(), title='Config - Subtitles', header='Subtitles', topmenu='config') - def saveSubtitles(self, use_subtitles=None, subtitles_plugins=None, subtitles_languages=None, subtitles_dir=None, service_order=None, subtitles_history=None, subtitles_finder_frequency=None, subtitles_multi=None, embedded_subtitles_all=None, subtitles_extra_scripts=None): @@ -4990,7 +4917,6 @@ class ConfigAnime(Config): return t.render(submenu=self.ConfigMenu(), title='Config - Anime', header='Anime', topmenu='config') - def saveAnime(self, use_anidb=None, anidb_username=None, anidb_password=None, anidb_use_mylist=None, split_home=None): @@ -5022,8 +4948,8 @@ class ErrorLogs(WebRoot): def ErrorLogsMenu(self): menu = [ - {'title': 'Clear Errors', 'path': 'errorlogs/clearerrors/'}, - {'title': 'Submit Errors', 'path': 'errorlogs/submit_errors/', 'requires': self.haveErrors(), 'confirm': True}, + {'title': 'Clear Errors', 'path': 'errorlogs/clearerrors/', 'icon': 'ui-icon ui-icon-trash'}, + {'title': 'Submit Errors', 'path': 'errorlogs/submit_errors/', 'requires': self.haveErrors(), 'confirm': True, 'icon': 'ui-icon ui-icon-arrowreturnthick-1-n'}, ] return menu diff --git a/sickbeard/webserveInit.py b/sickbeard/webserveInit.py index 2c44c9fd5d82612e53d7e3ebba1ffe00f9eff56b..c9636d67580e87192f6b0d5badec46b60efc66fc 100644 --- a/sickbeard/webserveInit.py +++ b/sickbeard/webserveInit.py @@ -1,6 +1,5 @@ import os import threading -import sys import sickbeard from sickbeard.webserve import LoginHandler, LogoutHandler, KeyHandler, CalendarHandler diff --git a/sickrage/__init__.py b/sickrage/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/sickrage/media/GenericMedia.py b/sickrage/media/GenericMedia.py new file mode 100644 index 0000000000000000000000000000000000000000..8e029052661a8ecc3706275e9211aff332677863 --- /dev/null +++ b/sickrage/media/GenericMedia.py @@ -0,0 +1,87 @@ +import sickbeard + +from abc import abstractmethod +from os.path import isfile, join, normpath +from sickbeard.encodingKludge import ek +from sickbeard.exceptions import MultipleShowObjectsException +from sickbeard.helpers import findCertainShow + + +class GenericMedia: + def __init__(self, indexer_id, media_format='normal'): + """ + :param indexer_id: The indexer id of the show + :param media_format: The format of the media to get. Must be either 'normal' or 'thumb' + """ + + if media_format in ('normal', 'thumb'): + self.media_format = media_format + else: + self.media_format = 'normal' + + try: + self.indexer_id = int(indexer_id) + except ValueError: + self.indexer_id = 0 + + @abstractmethod + def get_default_media_name(self): + """ + :return: The name of the file to use as a fallback if the show media file is missing + """ + + return '' + + def get_media(self): + """ + :return: The content of the desired media file + """ + + static_media_path = self.get_static_media_path() + + if ek(isfile, static_media_path): + with open(static_media_path, 'r') as content: + return content.read() + + return None + + @abstractmethod + def get_media_path(self): + """ + :return: The path to the media related to ``self.indexer_id`` + """ + + return '' + + @staticmethod + def get_media_root(): + """ + :return: The root folder containing the media + """ + + return sickbeard.DATA_DIR + '/gui/slick' + + def get_show(self): + """ + :return: The show object associated with ``self.indexer_id`` or ``None`` + """ + + try: + return findCertainShow(sickbeard.showList, self.indexer_id) + except MultipleShowObjectsException: + return None + + def get_static_media_path(self): + """ + :return: The full path to the media + """ + + if self.get_show(): + media_path = self.get_media_path() + + if ek(isfile, media_path): + return normpath(media_path) + + image_path = join(self.get_media_root(), 'images', self.get_default_media_name()) + + return image_path.replace('\\', '/') diff --git a/sickrage/media/ShowBanner.py b/sickrage/media/ShowBanner.py new file mode 100644 index 0000000000000000000000000000000000000000..e2ec5980f43a7fb7cafeb96e5932b611545b00d1 --- /dev/null +++ b/sickrage/media/ShowBanner.py @@ -0,0 +1,21 @@ +from sickbeard.image_cache import ImageCache +from sickrage.media.GenericMedia import GenericMedia + + +class ShowBanner(GenericMedia): + """ + Get the banner of a show + """ + + def get_default_media_name(self): + return 'banner.png' + + def get_media_path(self): + if self.get_show(): + if self.media_format == 'normal': + return ImageCache().banner_path(self.indexer_id) + + if self.media_format == 'thumb': + return ImageCache().banner_thumb_path(self.indexer_id) + + return '' diff --git a/sickrage/media/ShowFanArt.py b/sickrage/media/ShowFanArt.py new file mode 100644 index 0000000000000000000000000000000000000000..b027def87297fb991648b28a43585a681513e818 --- /dev/null +++ b/sickrage/media/ShowFanArt.py @@ -0,0 +1,17 @@ +from sickbeard.image_cache import ImageCache +from sickrage.media.GenericMedia import GenericMedia + + +class ShowFanArt(GenericMedia): + """ + Get the fan art of a show + """ + + def get_default_media_name(self): + return 'fanart.png' + + def get_media_path(self): + if self.get_show(): + return ImageCache().fanart_path(self.indexer_id) + + return '' diff --git a/sickrage/media/ShowNetworkLogo.py b/sickrage/media/ShowNetworkLogo.py new file mode 100644 index 0000000000000000000000000000000000000000..6aa00202f57c49c6708c181526d72e80cad449a1 --- /dev/null +++ b/sickrage/media/ShowNetworkLogo.py @@ -0,0 +1,18 @@ +from sickrage.media.GenericMedia import GenericMedia + + +class ShowNetworkLogo(GenericMedia): + """ + Get the network logo of a show + """ + + def get_default_media_name(self): + return 'network/nonetwork.png' + + def get_media_path(self): + show = self.get_show() + + if show: + return '%s/images/network/%s.png' % (self.get_media_root(), show.network_logo_name) + + return '' diff --git a/sickrage/media/ShowPoster.py b/sickrage/media/ShowPoster.py new file mode 100644 index 0000000000000000000000000000000000000000..f19d582651ff79f2aac93c0e88da1c9520ac7d39 --- /dev/null +++ b/sickrage/media/ShowPoster.py @@ -0,0 +1,21 @@ +from sickbeard.image_cache import ImageCache +from sickrage.media.GenericMedia import GenericMedia + + +class ShowPoster(GenericMedia): + """ + Get the poster of a show + """ + + def get_default_media_name(self): + return 'poster.png' + + def get_media_path(self): + if self.get_show(): + if self.media_format == 'normal': + return ImageCache().poster_path(self.indexer_id) + + if self.media_format == 'thumb': + return ImageCache().poster_thumb_path(self.indexer_id) + + return '' diff --git a/sickrage/media/__init__.py b/sickrage/media/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..cb64e75194e3d334bf711bbfd3fd3cc3dc5b6871 --- /dev/null +++ b/sickrage/media/__init__.py @@ -0,0 +1 @@ +__all__ = ['ShowBanner', 'ShowFanArt', 'ShowNetworkLogo', 'ShowPoster'] diff --git a/sickrage/show/History.py b/sickrage/show/History.py new file mode 100644 index 0000000000000000000000000000000000000000..9d1ccee5ba371b767a1c257c542af254ce9763e4 --- /dev/null +++ b/sickrage/show/History.py @@ -0,0 +1,73 @@ +from datetime import datetime +from datetime import timedelta +from sickbeard.common import Quality +from sickbeard.db import DBConnection + + +class History: + date_format = '%Y%m%d%H%M%S' + + def __init__(self): + self.db = DBConnection() + + def clear(self): + self.db.action( + 'DELETE ' + 'FROM history ' + 'WHERE 1 = 1' + ) + + def get(self, limit=100, action=None): + action = action.lower() if isinstance(action, str) else '' + limit = int(limit) + + if action == 'downloaded': + actions = Quality.DOWNLOADED + elif action == 'snatched': + actions = Quality.SNATCHED + else: + actions = Quality.SNATCHED + Quality.DOWNLOADED + + if limit == 0: + results = self.db.select( + 'SELECT h.*, show_name ' + 'FROM history h, tv_shows s ' + 'WHERE h.showid = s.indexer_id ' + 'AND action in (' + ','.join(['?'] * len(actions)) + ') ' + 'ORDER BY date DESC', + actions + ) + else: + results = self.db.select( + 'SELECT h.*, show_name ' + 'FROM history h, tv_shows s ' + 'WHERE h.showid = s.indexer_id ' + 'AND action in (' + ','.join(['?'] * len(actions)) + ') ' + 'ORDER BY date DESC ' + 'LIMIT ?', + actions + [limit] + ) + + data = [] + for result in results: + data.append({ + 'action': result['action'], + 'date': result['date'], + 'episode': result['episode'], + 'provider': result['provider'], + 'quality': result['quality'], + 'resource': result['resource'], + 'season': result['season'], + 'show_id': result['showid'], + 'show_name': result['show_name'] + }) + + return data + + def trim(self): + self.db.action( + 'DELETE ' + 'FROM history ' + 'WHERE date < ?', + [(datetime.today() - timedelta(days=30)).strftime(History.date_format)] + ) diff --git a/sickrage/show/__init__.py b/sickrage/show/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..38638bc46e7f39d9a00ec01e39f859e08a76df83 --- /dev/null +++ b/sickrage/show/__init__.py @@ -0,0 +1 @@ +__all__ = ['History'] diff --git a/sickrage/system/Restart.py b/sickrage/system/Restart.py new file mode 100644 index 0000000000000000000000000000000000000000..e8674d592ed53f32e5deb3de2b52c74ac6997e6f --- /dev/null +++ b/sickrage/system/Restart.py @@ -0,0 +1,17 @@ +import sickbeard + +from sickbeard.event_queue import Events + + +class Restart: + def __init__(self): + pass + + @staticmethod + def restart(pid): + if str(pid) != str(sickbeard.PID): + return False + + sickbeard.events.put(Events.SystemEvent.RESTART) + + return True diff --git a/sickrage/system/Shutdown.py b/sickrage/system/Shutdown.py new file mode 100644 index 0000000000000000000000000000000000000000..40d0231b51371b4f540876ce374e232289ba8ea7 --- /dev/null +++ b/sickrage/system/Shutdown.py @@ -0,0 +1,17 @@ +import sickbeard + +from sickbeard.event_queue import Events + + +class Shutdown: + def __init__(self): + pass + + @staticmethod + def stop(pid): + if str(pid) != str(sickbeard.PID): + return False + + sickbeard.events.put(Events.SystemEvent.SHUTDOWN) + + return True diff --git a/sickrage/system/__init__.py b/sickrage/system/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..55314582fc22f2d59c5261c56c83f8844e68f174 --- /dev/null +++ b/sickrage/system/__init__.py @@ -0,0 +1 @@ +__all__ = ['Restart', 'Shutdown']