From 6d8e79c78a612b141c7cd9a79b0c2830e722767a Mon Sep 17 00:00:00 2001 From: nielsenj Date: Mon, 30 Jun 2014 22:48:49 -0700 Subject: [PATCH 1/9] Adds custom RSS provider ratio setting. Adds ratio setting to the custom RSS provider. --- sickbeard/providers/rsstorrent.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sickbeard/providers/rsstorrent.py b/sickbeard/providers/rsstorrent.py index 3aac4d8d..b3e7c709 100644 --- a/sickbeard/providers/rsstorrent.py +++ b/sickbeard/providers/rsstorrent.py @@ -41,6 +41,7 @@ class TorrentRssProvider(generic.TorrentProvider): self.url = re.sub('\/$', '', url) self.url = url self.enabled = True + self.ratio = None self.supportsBacklog = False self.search_mode = search_mode @@ -167,6 +168,8 @@ class TorrentRssProvider(generic.TorrentProvider): logger.log(u"Saved custom_torrent html dump " + dumpName + " ", logger.MESSAGE) return True + def seedRatio(self): + return self.ratio class TorrentRssCache(tvcache.TVCache): def __init__(self, provider): @@ -192,4 +195,4 @@ class TorrentRssCache(tvcache.TVCache): return None logger.log(u"Attempting to add item to cache: " + title, logger.DEBUG) - return self._addCacheEntry(title, url) \ No newline at end of file + return self._addCacheEntry(title, url) From bf52b882d4aba582193904b6a88d62363d996c8f Mon Sep 17 00:00:00 2001 From: e5e4eaeacd39c5cfba4d7c852c48277ae50331e6 Date: Sat, 5 Jul 2014 16:14:55 +1000 Subject: [PATCH 2/9] Minor GUI and console fixes --- gui/slick/interfaces/default/home_newShow.tmpl | 4 ++-- sickbeard/dailysearcher.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gui/slick/interfaces/default/home_newShow.tmpl b/gui/slick/interfaces/default/home_newShow.tmpl index 60ce3e43..964978e5 100644 --- a/gui/slick/interfaces/default/home_newShow.tmpl +++ b/gui/slick/interfaces/default/home_newShow.tmpl @@ -39,7 +39,7 @@ #else: - + * @@ -50,7 +50,7 @@ #end for -

+

* This will only affect the language of the retrieved metadata file contents and episode filenames.
This DOES NOT allow SickRage to download non-english TV episodes!
diff --git a/sickbeard/dailysearcher.py b/sickbeard/dailysearcher.py index ac528d1e..60ce9cff 100644 --- a/sickbeard/dailysearcher.py +++ b/sickbeard/dailysearcher.py @@ -44,7 +44,7 @@ class DailySearcher(): for curProviderCount, curProvider in enumerate(providers): try: - logger.log(u"Updating [" + curProvider.name + "} RSS cache ...") + logger.log(u"Updating [" + curProvider.name + "] RSS cache ...") curProvider.cache.updateCache() except exceptions.AuthException, e: logger.log(u"Authentication error: " + ex(e), logger.ERROR) From e6eb1256bf6b8e08ba8c4ac76f4ca5009188a189 Mon Sep 17 00:00:00 2001 From: adam Date: Mon, 7 Jul 2014 19:23:59 +0800 Subject: [PATCH 3/9] Fix for 'add to my list' option defaulting to on upon a restart. --- sickbeard/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py index 218466b8..e1527183 100644 --- a/sickbeard/__init__.py +++ b/sickbeard/__init__.py @@ -872,7 +872,7 @@ def initialize(consoleLogging=True): USE_ANIDB = check_setting_str(CFG, 'ANIDB', 'use_anidb', '') ANIDB_USERNAME = check_setting_str(CFG, 'ANIDB', 'anidb_username', '') ANIDB_PASSWORD = check_setting_str(CFG, 'ANIDB', 'anidb_password', '') - ANIDB_USE_MYLIST = check_setting_str(CFG, 'ANIDB', 'anidb_use_mylist', '') + ANIDB_USE_MYLIST = bool(check_setting_int(CFG, 'ANIDB', 'anidb_use_mylist', 0)) ANIME_SPLIT_HOME = bool(check_setting_int(CFG, 'ANIME', 'anime_split_home', 0)) METADATA_XBMC = check_setting_str(CFG, 'General', 'metadata_xbmc', '0|0|0|0|0|0|0|0|0|0') From c8d899ad669ea8111bffecd482b5e0f3bde644eb Mon Sep 17 00:00:00 2001 From: echel0n Date: Mon, 7 Jul 2014 14:30:27 -0700 Subject: [PATCH 4/9] Removed maintance schedualer and moved the routines from it to happen before a search is started to ensure things are up to date and to stop waking up synology devices, regexes also made less greedy. --- SickBeard.py | 12 +++++++- sickbeard/__init__.py | 22 ++------------ sickbeard/maintenance.py | 53 --------------------------------- sickbeard/name_cache.py | 6 ++-- sickbeard/name_parser/parser.py | 4 +-- sickbeard/search_queue.py | 8 ++--- sickbeard/showUpdater.py | 7 +++++ 7 files changed, 31 insertions(+), 81 deletions(-) delete mode 100644 sickbeard/maintenance.py diff --git a/SickBeard.py b/SickBeard.py index 00d0a84d..4bd57cb3 100755 --- a/SickBeard.py +++ b/SickBeard.py @@ -60,7 +60,7 @@ import sickbeard from sickbeard import db from sickbeard.tv import TVShow -from sickbeard import logger +from sickbeard import logger, network_timezones, failed_history, name_cache from sickbeard.webserveInit import SRWebServer from sickbeard.version import SICKBEARD_VERSION from sickbeard.databases.mainDB import MIN_DB_VERSION @@ -313,6 +313,16 @@ class SickRage(object): # Fire up all our threads sickbeard.start() + # Build internal name cache + name_cache.buildNameCache() + + # refresh network timezones + network_timezones.update_network_dict() + + # sure, why not? + if sickbeard.USE_FAILED_DOWNLOADS: + failed_history.trimHistory() + # Start an update if we're supposed to if self.forceUpdate or sickbeard.UPDATE_SHOWS_ON_START: sickbeard.showUpdateScheduler.action.run(force=True) # @UndefinedVariable diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py index 218466b8..28a5c0fb 100644 --- a/sickbeard/__init__.py +++ b/sickbeard/__init__.py @@ -41,7 +41,6 @@ from sickbeard import helpers, db, exceptions, show_queue, search_queue, schedul from sickbeard import logger from sickbeard import naming from sickbeard import dailysearcher -from sickbeard import maintenance from sickbeard import scene_numbering, scene_exceptions, name_cache from indexers.indexer_api import indexerApi from indexers.indexer_exceptions import indexer_shownotfound, indexer_exception, indexer_error, indexer_episodenotfound, \ @@ -76,7 +75,6 @@ PIDFILE = '' DAEMON = None NO_RESIZE = False -maintenanceScheduler = None dailySearchScheduler = None backlogSearchScheduler = None showUpdateScheduler = None @@ -479,7 +477,7 @@ def initialize(consoleLogging=True): USE_FAILED_DOWNLOADS, DELETE_FAILED, ANON_REDIRECT, LOCALHOST_IP, TMDB_API_KEY, DEBUG, PROXY_SETTING, \ AUTOPOSTPROCESSER_FREQUENCY, DEFAULT_AUTOPOSTPROCESSER_FREQUENCY, MIN_AUTOPOSTPROCESSER_FREQUENCY, \ ANIME_DEFAULT, NAMING_ANIME, ANIMESUPPORT, USE_ANIDB, ANIDB_USERNAME, ANIDB_PASSWORD, ANIDB_USE_MYLIST, \ - ANIME_SPLIT_HOME, maintenanceScheduler, SCENE_DEFAULT + ANIME_SPLIT_HOME, SCENE_DEFAULT if __INITIALIZED__: return False @@ -957,10 +955,6 @@ def initialize(consoleLogging=True): threadName="CHECKVERSION", silent=False) - maintenanceScheduler = scheduler.Scheduler(maintenance.Maintenance(), - cycleTime=datetime.timedelta(hours=1), - threadName="MAINTENANCE") - showQueueScheduler = scheduler.Scheduler(show_queue.ShowQueue(), cycleTime=datetime.timedelta(seconds=3), threadName="SHOWQUEUE") @@ -1123,7 +1117,7 @@ def initialize(consoleLogging=True): def start(): - global __INITIALIZED__, maintenanceScheduler, backlogSearchScheduler, \ + global __INITIALIZED__, backlogSearchScheduler, \ showUpdateScheduler, versionCheckScheduler, showQueueScheduler, \ properFinderScheduler, autoPostProcesserScheduler, searchQueueScheduler, \ subtitlesFinderScheduler, USE_SUBTITLES,traktCheckerScheduler, \ @@ -1133,9 +1127,6 @@ def start(): if __INITIALIZED__: - # start the maintenance scheduler - maintenanceScheduler.thread.start() - # start the daily search scheduler dailySearchScheduler.thread.start() @@ -1171,7 +1162,7 @@ def start(): def halt(): - global __INITIALIZED__, maintenanceScheduler, backlogSearchScheduler, \ + global __INITIALIZED__, backlogSearchScheduler, \ showUpdateScheduler, versionCheckScheduler, showQueueScheduler, \ properFinderScheduler, autoPostProcesserScheduler, searchQueueScheduler, \ subtitlesFinderScheduler, traktCheckerScheduler, \ @@ -1185,13 +1176,6 @@ def halt(): # abort all the threads - maintenanceScheduler.abort = True - logger.log(u"Waiting for the MAINTENANCE scheduler thread to exit") - try: - maintenanceScheduler.thread.join(10) - except: - pass - dailySearchScheduler.abort = True logger.log(u"Waiting for the DAILYSEARCH thread to exit") try: diff --git a/sickbeard/maintenance.py b/sickbeard/maintenance.py deleted file mode 100644 index ccc3f871..00000000 --- a/sickbeard/maintenance.py +++ /dev/null @@ -1,53 +0,0 @@ -# Author: Nic Wolfe -# URL: http://code.google.com/p/sickbeard/ -# -# This file is part of SickRage. -# -# SickRage is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# SickRage is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with SickRage. If not, see . - -from __future__ import with_statement -import threading -import sickbeard - -from sickbeard import scene_exceptions -from sickbeard import failed_history -from sickbeard import network_timezones -from sickbeard import name_cache - -class Maintenance(): - def __init__(self): - self.lock = threading.Lock() - - self.amActive = False - - def run(self, force=False): - self.amActive = True - - # clear internal name cache - name_cache.clearCache() - - # get and update scene exceptions lists - scene_exceptions.retrieve_exceptions() - - # build internal name cache for searches and parsing - name_cache.buildNameCache() - - # refresh network timezones - network_timezones.update_network_dict() - - # sure, why not? - if sickbeard.USE_FAILED_DOWNLOADS: - failed_history.trimHistory() - - self.amActive = False \ No newline at end of file diff --git a/sickbeard/name_cache.py b/sickbeard/name_cache.py index 6661b89d..401db5dc 100644 --- a/sickbeard/name_cache.py +++ b/sickbeard/name_cache.py @@ -96,7 +96,10 @@ def buildNameCache(): # clear internal name cache clearCache() - logger.log(u"Updating internal name cache", logger.MESSAGE) + # update scene exception names + sickbeard.scene_exceptions.retrieve_exceptions() + + logger.log(u"Building internal name cache", logger.MESSAGE) cacheDB = db.DBConnection('cache.db') cache_results = cacheDB.select("SELECT * FROM scene_names") @@ -111,5 +114,4 @@ def buildNameCache(): for name in sickbeard.scene_exceptions.get_scene_exceptions(show.indexerid, season=curSeason): nameCache[sickbeard.helpers.full_sanitizeSceneName(name)] = show.indexerid - logger.log(u"Updated internal name cache", logger.MESSAGE) logger.log(u"Internal name cache set to: " + str(nameCache), logger.DEBUG) \ No newline at end of file diff --git a/sickbeard/name_parser/parser.py b/sickbeard/name_parser/parser.py index 74f163a8..63a012f9 100644 --- a/sickbeard/name_parser/parser.py +++ b/sickbeard/name_parser/parser.py @@ -123,8 +123,8 @@ class NameParser(object): if not self.showObj and not self.naming_pattern: # Regex pattern to return the Show / Series Name regardless of the file pattern tossed at it, matched 53 show name examples from regexes.py show_patterns = [ - '''^(?P.*?)\W+(?:(?:S\d[\dE._ -])|(?:\d\d?x)|(?:\d{4}\W\d\d\W\d\d)|(?:(?:part|pt)[\._ -]?(\d|[ivx]))|Season\W+\d+\W+|E\d+\W+|(?:\d{1,3}.+\d{1,}[a-zA-Z]{2}\W+[a-zA-Z]{3,}\W+\d{4}.+))''', - '''^((\[.*?\])|(\d+[\.-]))*[ _\.]*(?P.*?)(([ ._-]+\d+)|([ ._-]+s\d{2})).*''' + '''^(?P.*)\W+(?:(?:S\d[\dE._ -])|(?:\d\d?x)|(?:\d{4}\W\d\d\W\d\d)|(?:(?:part|pt)[\._ -]?(\d|[ivx]))|Season\W+\d+\W+|E\d+\W+|(?:\d{1,3}.+\d{1,}[a-zA-Z]{2}\W+[a-zA-Z]{3,}\W+\d{4}.+))''', + '''^((\[.*?\])|(\d+[\.-]))*[ _\.]*(?P.*)(([ ._-]+\d+)|([ ._-]+s\d{2})).*''' ] # find show object diff --git a/sickbeard/search_queue.py b/sickbeard/search_queue.py index c1968440..dfd4f863 100644 --- a/sickbeard/search_queue.py +++ b/sickbeard/search_queue.py @@ -43,9 +43,6 @@ class SearchQueue(generic_queue.GenericQueue): generic_queue.GenericQueue.__init__(self) self.queue_name = "SEARCHQUEUE" - def __del__(self): - pass - def is_in_queue(self, show, segment): for cur_item in self.queue: if isinstance(cur_item, BacklogQueueItem) and cur_item.show == show and cur_item.segment == segment: @@ -77,17 +74,20 @@ class SearchQueue(generic_queue.GenericQueue): def add_item(self, item): if isinstance(item, DailySearchQueueItem) and not self.is_in_queue(item.show, item.segment): + sickbeard.name_cache.buildNameCache() generic_queue.GenericQueue.add_item(self, item) elif isinstance(item, BacklogQueueItem) and not self.is_in_queue(item.show, item.segment): + sickbeard.name_cache.buildNameCache() generic_queue.GenericQueue.add_item(self, item) elif isinstance(item, ManualSearchQueueItem) and not self.is_in_queue(item.show, item.segment): + sickbeard.name_cache.buildNameCache() generic_queue.GenericQueue.add_item(self, item) elif isinstance(item, FailedQueueItem) and not self.is_in_queue(item.show, item.segment): + sickbeard.name_cache.buildNameCache() generic_queue.GenericQueue.add_item(self, item) else: logger.log(u"Not adding item, it's already in the queue", logger.DEBUG) - class DailySearchQueueItem(generic_queue.QueueItem): def __init__(self, show, segment): generic_queue.QueueItem.__init__(self, 'Daily Search', DAILY_SEARCH) diff --git a/sickbeard/showUpdater.py b/sickbeard/showUpdater.py index 8d371af3..46c0734c 100644 --- a/sickbeard/showUpdater.py +++ b/sickbeard/showUpdater.py @@ -37,6 +37,13 @@ class ShowUpdater(): update_datetime = datetime.datetime.now() update_date = update_datetime.date() + # refresh network timezones + network_timezones.update_network_dict() + + # sure, why not? + if sickbeard.USE_FAILED_DOWNLOADS: + failed_history.trimHistory() + logger.log(u"Doing full update on all shows") # clean out cache directory, remove everything > 12 hours old From 79a1b1c31e419d0e6ed584a056f1806b9a21951d Mon Sep 17 00:00:00 2001 From: echel0n Date: Mon, 7 Jul 2014 20:27:24 -0700 Subject: [PATCH 5/9] Fix for root dir location not being saved or set correctly for shows. Fix for mass update edits of shows, bwlist no longer gets updated during this process as its not required. You can not change your root dir even if location does not exist so long as you have create_missing_show_dirs=1 in your config set so that there be auto-created during the next post-processing of a episode. --- sickbeard/tv.py | 9 ++-- sickbeard/webserve.py | 99 ++++++++++++++++++++++--------------------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/sickbeard/tv.py b/sickbeard/tv.py index 3b9beb32..57ac05bb 100644 --- a/sickbeard/tv.py +++ b/sickbeard/tv.py @@ -66,7 +66,6 @@ class TVShow(object): self._indexerid = int(indexerid) self._indexer = int(indexer) self._name = "" - self._location = "" self._imdbid = "" self._network = "" self._genre = "" @@ -93,6 +92,7 @@ class TVShow(object): self.dirty = True + self._location = "" self.lock = threading.Lock() self.isDirGood = False self.episodes = {} @@ -103,9 +103,6 @@ class TVShow(object): self.loadFromDB() - def __del__(self): - pass - name = property(lambda self: self._name, dirty_setter("_name")) indexerid = property(lambda self: self._indexerid, dirty_setter("_indexerid")) indexer = property(lambda self: self._indexer, dirty_setter("_indexer")) @@ -172,7 +169,7 @@ class TVShow(object): logger.log(u"Setter sets location to " + newLocation, logger.DEBUG) # Don't validate dir if user wants to add shows without creating a dir if sickbeard.ADD_SHOWS_WO_DIR or ek.ek(os.path.isdir, newLocation): - self._location = newLocation + dirty_setter("_location")(self, newLocation) self._isDirGood = True else: raise exceptions.NoNFOException("Invalid folder for the show!") @@ -834,7 +831,7 @@ class TVShow(object): self.flatten_folders = int(sqlResults[0]["flatten_folders"]) self.paused = int(sqlResults[0]["paused"]) - self._location = sqlResults[0]["location"] + self.location = sqlResults[0]["location"] if not self.lang: self.lang = sqlResults[0]["lang"] diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 2ef84235..8923bad8 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -487,7 +487,8 @@ class MainHandler(RequestHandler): class PageTemplate(Template): def __init__(self, headers, *args, **KWs): - KWs['file'] = os.path.join(sickbeard.PROG_DIR, "gui/" + sickbeard.GUI_NAME + "/interfaces/default/",KWs['file']) + KWs['file'] = os.path.join(sickbeard.PROG_DIR, "gui/" + sickbeard.GUI_NAME + "/interfaces/default/", + KWs['file']) super(PageTemplate, self).__init__(*args, **KWs) self.sbRoot = sickbeard.WEB_ROOT @@ -531,6 +532,7 @@ class PageTemplate(Template): kwargs['cacheDirForModuleFiles'] = os.path.join(sickbeard.CACHE_DIR, 'cheetah') return super(PageTemplate, self).compile(*args, **kwargs) + class IndexerWebUI(MainHandler): def __init__(self, config, log=None): self.config = config @@ -3643,8 +3645,6 @@ class Home(MainHandler): else: return self._genericMessage("Error", errString) - showObj.exceptions = scene_exceptions.get_scene_exceptions(showObj.indexerid) - if not location and not anyQualities and not bestQualities and not flatten_folders: t = PageTemplate(headers=self.request.headers, file="editShow.tmpl") t.submenu = HomeMenu() @@ -3727,54 +3727,56 @@ class Home(MainHandler): else: do_update_exceptions = True - bwl = BlackAndWhiteList(showObj.indexerid) - if whitelist: - whitelist = whitelist.split(",") - shortWhiteList = [] - if helpers.set_up_anidb_connection(): - for groupName in whitelist: - group = sickbeard.ADBA_CONNECTION.group(gname=groupName) - for line in group.datalines: - if line["shortname"]: - shortWhiteList.append(line["shortname"]) - else: - if not groupName in shortWhiteList: - shortWhiteList.append(groupName) + # If directCall from mass_edit_update no scene exceptions handling + if not directCall: + bwl = BlackAndWhiteList(showObj.indexerid) + if whitelist: + whitelist = whitelist.split(",") + shortWhiteList = [] + if helpers.set_up_anidb_connection(): + for groupName in whitelist: + group = sickbeard.ADBA_CONNECTION.group(gname=groupName) + for line in group.datalines: + if line["shortname"]: + shortWhiteList.append(line["shortname"]) + else: + if not groupName in shortWhiteList: + shortWhiteList.append(groupName) + else: + shortWhiteList = whitelist + bwl.set_white_keywords_for("release_group", shortWhiteList) else: - shortWhiteList = whitelist - bwl.set_white_keywords_for("release_group", shortWhiteList) - else: - bwl.set_white_keywords_for("release_group", []) + bwl.set_white_keywords_for("release_group", []) - if blacklist: - blacklist = blacklist.split(",") - shortBlacklist = [] - if helpers.set_up_anidb_connection(): - for groupName in blacklist: - group = sickbeard.ADBA_CONNECTION.group(gname=groupName) - for line in group.datalines: - if line["shortname"]: - shortBlacklist.append(line["shortname"]) - else: - if not groupName in shortBlacklist: - shortBlacklist.append(groupName) + if blacklist: + blacklist = blacklist.split(",") + shortBlacklist = [] + if helpers.set_up_anidb_connection(): + for groupName in blacklist: + group = sickbeard.ADBA_CONNECTION.group(gname=groupName) + for line in group.datalines: + if line["shortname"]: + shortBlacklist.append(line["shortname"]) + else: + if not groupName in shortBlacklist: + shortBlacklist.append(groupName) + else: + shortBlacklist = blacklist + bwl.set_black_keywords_for("release_group", shortBlacklist) else: - shortBlacklist = blacklist - bwl.set_black_keywords_for("release_group", shortBlacklist) - else: - bwl.set_black_keywords_for("release_group", []) + bwl.set_black_keywords_for("release_group", []) - if whiteWords: - whiteWords = [x.strip() for x in whiteWords.split(",")] - bwl.set_white_keywords_for("global", whiteWords) - else: - bwl.set_white_keywords_for("global", []) + if whiteWords: + whiteWords = [x.strip() for x in whiteWords.split(",")] + bwl.set_white_keywords_for("global", whiteWords) + else: + bwl.set_white_keywords_for("global", []) - if blackWords: - blackWords = [x.strip() for x in blackWords.split(",")] - bwl.set_black_keywords_for("global", blackWords) - else: - bwl.set_black_keywords_for("global", []) + if blackWords: + blackWords = [x.strip() for x in blackWords.split(",")] + bwl.set_black_keywords_for("global", blackWords) + else: + bwl.set_black_keywords_for("global", []) errors = [] with showObj.lock: @@ -3825,14 +3827,14 @@ class Home(MainHandler): # if we change location clear the db of episodes, change it, write to db, and rescan if os.path.normpath(showObj._location) != os.path.normpath(location): logger.log(os.path.normpath(showObj._location) + " != " + os.path.normpath(location), logger.DEBUG) - if not ek.ek(os.path.isdir, location): + if not ek.ek(os.path.isdir, location) and not sickbeard.CREATE_MISSING_SHOW_DIRS: errors.append("New location %s does not exist" % location) # don't bother if we're going to update anyway elif not do_update: # change it try: - showObj.location = location + showObj._location = location try: sickbeard.showQueueScheduler.action.refreshShow(showObj) # @UndefinedVariable except exceptions.CantRefreshException, e: @@ -3858,6 +3860,7 @@ class Home(MainHandler): if do_update_exceptions: try: scene_exceptions.update_scene_exceptions(showObj.indexerid, exceptions_list) # @UndefinedVariable + showObj.exceptions = scene_exceptions.get_scene_exceptions(showObj.indexerid) time.sleep(cpu_presets[sickbeard.CPU_PRESET]) except exceptions.CantUpdateException, e: errors.append("Unable to force an update on scene exceptions of the show.") From 257181ee4fb04a694c188f570c597b1e48e4bb9a Mon Sep 17 00:00:00 2001 From: adam Date: Tue, 8 Jul 2014 20:57:14 +0800 Subject: [PATCH 6/9] Fixes web-dl quality detection for some episode naming patterns --- sickbeard/common.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sickbeard/common.py b/sickbeard/common.py index dcc466f5..2e532750 100644 --- a/sickbeard/common.py +++ b/sickbeard/common.py @@ -222,13 +222,9 @@ class Quality: return Quality.RAWHDTV elif checkName(["1080p", "hdtv", "x264"], all): return Quality.FULLHDTV - elif checkName(["720p", "web.dl", "h.?264"], all) or checkName(["720p", "itunes", "h.?264"], all): + elif checkName(["720p", "web.dl|webrip"], all) or checkName(["720p", "itunes", "h.?264"], all): return Quality.HDWEBDL - elif checkName(["1080p", "web.dl", "h.?264"], all) or checkName(["1080p", "itunes", "h.?264"], all): - return Quality.FULLHDWEBDL - elif checkName(["720p", "webrip", "x264"], all): - return Quality.HDWEBDL - elif checkName(["1080p", "webrip", "x264"], all): + elif checkName(["1080p", "web.dl|webrip"], all) or checkName(["1080p", "itunes", "h.?264"], all): return Quality.FULLHDWEBDL elif checkName(["720p", "bluray|hddvd|b[r|d]rip", "x264"], all): return Quality.HDBLURAY From 00bad06fad405bfdb02788f638ab57b2403702d8 Mon Sep 17 00:00:00 2001 From: adam Date: Tue, 8 Jul 2014 21:52:29 +0800 Subject: [PATCH 7/9] Fixes downloads column sorting order for shows with all episodes ignored --- gui/slick/interfaces/default/home.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/slick/interfaces/default/home.tmpl b/gui/slick/interfaces/default/home.tmpl index 952732bc..3ac9a474 100644 --- a/gui/slick/interfaces/default/home.tmpl +++ b/gui/slick/interfaces/default/home.tmpl @@ -229,7 +229,7 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name)) #set $den = $curShowAll[0] #end if #else - #set $dlStat = "0" + #set $dlStat = "0 / 0" #set $nom = 0 #set $den = 1 #end if From 4d3bcf6b521d1eabe37a28c40790ef00ba537f32 Mon Sep 17 00:00:00 2001 From: adam Date: Wed, 9 Jul 2014 00:35:27 +0800 Subject: [PATCH 8/9] Adds network logos for bs11 and niconico --- gui/slick/images/network/bs11.png | Bin 0 -> 1220 bytes gui/slick/images/network/niconico.png | Bin 0 -> 932 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 gui/slick/images/network/bs11.png create mode 100644 gui/slick/images/network/niconico.png diff --git a/gui/slick/images/network/bs11.png b/gui/slick/images/network/bs11.png new file mode 100644 index 0000000000000000000000000000000000000000..24b921996424e74eef4401b4f3813b45b825411d GIT binary patch literal 1220 zcmV;#1UvhQP)<|;+@O$P-M|LvERpyTQgKFwC$r3bO}I}`s#OCGX!?sBoW3DxWu&n{Pygg*Dzv{ySYQXo&yk0nmzq4 zqFQNWX3*7@ePuO)1WAu2_dg^M!S+e_)U-)^HohQYYps?A0MflHMDEhd=bWi|X%p+F zl$=~h@^Dcd<}gP`o#|(ea+rhYBkL;B!u~hDtRyY!2xqZR4C7g z6(PP_Y5|Ik#GCr|vmc;1h+_;50BAse_t2t@=sq`|6B$p=bd_tU(G@glpSq({#LzkR zafndVyJzrfhoRRhxX@eP7rNU*)=u}VR@^bVe)FmT1C*l4?G-Wi6b*jsWoW}5>NtB$ zq@9$K&M5QzW>>*PRzCi`vA*$S-Vy+6c;ce1;DVt6E!Y;mBr=ovmRz5@6kKK!u(E^g ztR=etyQsiAhXDlu7ykZ>s~#4)PL82`s4i|ZlR&&Z%WkYKhDVEu5jyrZRIV1dgM*|T zajB*fxIvJJXhTalVY|_!P8h;Fs=ZsE5?(4k9OtJwOzea?&Fc24Z{K$5u5o>ea)&q9+)3(;k5liSEGFstuwweuMKD z2(grRN|m0xb!r-fmK8eE#eT`{f1iLH??e`4@3W;$dT6=4Wat&e@7JOw`XH^Do3@?a z=0f2;F{&2l2PP57p-&M?i`g0 z1i?h(^g<=sBzkLt$cG{hb&)aV9GR0r0C8rH57*Kby7ldQylA&k#A|Qf-_?1ZJfHuZ zbHK*N#>VFVrd`0JzS`B5ZFTj4Xp(CdE z{B9Z=o5zoiBeD3A%I?D(K*Uo?sF1$=^Zn7{bWcUz!QqGMTdy9R%G7h=Y3@5FLfhl7 z8QrIzgii}z8BOIaU3~6RQ{nMT&Qwmvzz(tS8y1kI2&~1dFFu${Xj5 zz4Kp)$iKG*pPqow`>`$F7f-&?K5v1~b743BnCUWlRRX}$J-ZeS_g~PWuCJ5@QoHep zWcCsCM+)e1r)W(rFP03m<^tqCk(t^rwIw z)N1;uB0p%E3jahKkOG`Z1fWR4^#>(~f1By}p@690ee~u5?l=)Eb7ZZUHJ?aWE<`6f z0@x%(d?_h+j-$z3LmWd!oRB+lxgaY|%w03bxCxvQ{YnB7&`rw&BMb|{o!qBXzMgR0 z{%t6LF|&Qsv&*OET;f{d`BVEux?eJ|O{mj;Oy zboo`y7fZeGI*^DFlt2Ljg#-l}ly;1qJ#wXZcD2VB?%y8nEVk~-c&?NC$d#}S9z-f%s@ zX!YGMO|@hMaOQpV3?Jmw>`IiS9qz$_SM7h;#>U3R2L1v1DIi*H*caLW0000 Date: Tue, 8 Jul 2014 15:17:34 -0700 Subject: [PATCH 9/9] New event queue system in place, currently handles shutdown and restart calls. Fixed ctrl-c issues with new event queue system. Added a sleep timer to the NameParser class to help lower cpu usage spikes. --- SickBeard.py | 108 ++++++++++++++++---------------- sickbeard/__init__.py | 44 ++++--------- sickbeard/event_queue.py | 45 +++++++++++++ sickbeard/name_parser/parser.py | 5 ++ sickbeard/versionChecker.py | 2 +- sickbeard/webapi.py | 4 +- sickbeard/webserve.py | 6 +- sickbeard/webserveInit.py | 7 --- 8 files changed, 123 insertions(+), 98 deletions(-) create mode 100644 sickbeard/event_queue.py diff --git a/SickBeard.py b/SickBeard.py index 4bd57cb3..947ed781 100755 --- a/SickBeard.py +++ b/SickBeard.py @@ -21,6 +21,7 @@ from __future__ import with_statement import time +import signal import sys import shutil import subprocess @@ -52,12 +53,12 @@ if sys.hexversion >= 0x020600F0: import locale import datetime import threading -import signal import traceback import getopt import sickbeard +from sickbeard.event_queue import Events from sickbeard import db from sickbeard.tv import TVShow from sickbeard import logger, network_timezones, failed_history, name_cache @@ -68,14 +69,16 @@ from sickbeard.databases.mainDB import MAX_DB_VERSION from lib.configobj import ConfigObj +throwaway = datetime.datetime.strptime('20110101', '%Y%m%d') + signal.signal(signal.SIGINT, sickbeard.sig_handler) signal.signal(signal.SIGTERM, sickbeard.sig_handler) -throwaway = datetime.datetime.strptime('20110101', '%Y%m%d') - class SickRage(object): def __init__(self): + sickbeard.events = Events(self.shutdown) + self.webserver = None self.runAsDaemon = False self.CREATEPID = False @@ -330,7 +333,8 @@ class SickRage(object): if sickbeard.LAUNCH_BROWSER and not (self.noLaunch or self.runAsDaemon): sickbeard.launchBrowser(self.startPort) - while(sickbeard.started): + # main loop + while(True): time.sleep(1) def daemonize(self): @@ -431,56 +435,54 @@ class SickRage(object): except: return False + def shutdown(self, type): + if sickbeard.started: + # stop all tasks + sickbeard.halt() + + # shutdown web server + if self.webserver: + self.webserver.shutDown() + self.webserver = None + + # save all shows to DB + sickbeard.saveAll() + + # if run as daemon delete the pidfile + if self.runAsDaemon and self.CREATEPID: + self.remove_pid_file(self.PIDFILE) + + if type == sickbeard.events.SystemEvent.RESTART: + install_type = sickbeard.versionCheckScheduler.action.install_type + + popen_list = [] + + if install_type in ('git', 'source'): + popen_list = [sys.executable, sickbeard.MY_FULLNAME] + elif install_type == 'win': + if hasattr(sys, 'frozen'): + # c:\dir\to\updater.exe 12345 c:\dir\to\sickbeard.exe + popen_list = [os.path.join(sickbeard.PROG_DIR, 'updater.exe'), str(sickbeard.PID), sys.executable] + else: + logger.log(u"Unknown SB launch method, please file a bug report about this", logger.ERROR) + popen_list = [sys.executable, os.path.join(sickbeard.PROG_DIR, 'updater.py'), str(sickbeard.PID), + sys.executable, + sickbeard.MY_FULLNAME] + + if popen_list: + popen_list += sickbeard.MY_ARGS + if '--nolaunch' not in popen_list: + popen_list += ['--nolaunch'] + logger.log(u"Restarting SickRage with " + str(popen_list)) + logger.close() + subprocess.Popen(popen_list, cwd=os.getcwd()) + + # system exit + os._exit(0) + if __name__ == "__main__": if sys.hexversion >= 0x020600F0: freeze_support() - sr = None - try: - # init sickrage - sr = SickRage() - - # start sickrage - sr.start() - - # shutdown web server - sr.webserver.shutDown() - sr.webserver.join() - sr.webserver = None - - # if run as daemon delete the pidfile - if sr.runAsDaemon and sr.CREATEPID: - sr.remove_pid_file(sr.PIDFILE) - - if not sickbeard.shutdown: - install_type = sickbeard.versionCheckScheduler.action.install_type - - popen_list = [] - - if install_type in ('git', 'source'): - popen_list = [sys.executable, sickbeard.MY_FULLNAME] - elif install_type == 'win': - if hasattr(sys, 'frozen'): - # c:\dir\to\updater.exe 12345 c:\dir\to\sickbeard.exe - popen_list = [os.path.join(sickbeard.PROG_DIR, 'updater.exe'), str(sickbeard.PID), sys.executable] - else: - logger.log(u"Unknown SB launch method, please file a bug report about this", logger.ERROR) - popen_list = [sys.executable, os.path.join(sickbeard.PROG_DIR, 'updater.py'), str(sickbeard.PID), sys.executable, - sickbeard.MY_FULLNAME] - - if popen_list: - popen_list += sickbeard.MY_ARGS - if '--nolaunch' not in popen_list: - popen_list += ['--nolaunch'] - logger.log(u"Restarting SickRage with " + str(popen_list)) - logger.close() - subprocess.Popen(popen_list, cwd=os.getcwd()) - - # exit process - os._exit(0) - except: - if sr: - logger.log(traceback.format_exc(), logger.ERROR) - else: - print(traceback.format_exc()) - sys.exit(1) \ No newline at end of file + # start sickrage + SickRage().start() \ No newline at end of file diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py index 8cdc796c..ce787008 100644 --- a/sickbeard/__init__.py +++ b/sickbeard/__init__.py @@ -49,7 +49,6 @@ from sickbeard.common import SD, SKIPPED, NAMING_REPEAT from sickbeard.databases import mainDB, cache_db, failed_db from lib.configobj import ConfigObj -from tornado.ioloop import IOLoop import xml.etree.ElementTree as ElementTree PID = None @@ -75,6 +74,9 @@ PIDFILE = '' DAEMON = None NO_RESIZE = False +# system events +events = None + dailySearchScheduler = None backlogSearchScheduler = None showUpdateScheduler = None @@ -103,7 +105,6 @@ CUR_COMMIT_HASH = None INIT_LOCK = Lock() started = False -shutdown = False ACTUAL_LOG_DIR = None LOG_DIR = None @@ -1166,7 +1167,7 @@ def halt(): showUpdateScheduler, versionCheckScheduler, showQueueScheduler, \ properFinderScheduler, autoPostProcesserScheduler, searchQueueScheduler, \ subtitlesFinderScheduler, traktCheckerScheduler, \ - dailySearchScheduler, started + dailySearchScheduler, events, started with INIT_LOCK: @@ -1174,7 +1175,12 @@ def halt(): logger.log(u"Aborting all threads") - # abort all the threads + events.alive = False + logger.log(u"Waiting for the EVENTS thread to exit") + try: + events.join(10) + except: + pass dailySearchScheduler.abort = True logger.log(u"Waiting for the DAILYSEARCH thread to exit") @@ -1261,7 +1267,7 @@ def halt(): def sig_handler(signum=None, frame=None): if type(signum) != type(None): logger.log(u"Signal %i caught, saving and exiting..." % int(signum)) - saveAndShutdown() + events.put(events.SystemEvent.SHUTDOWN) def saveAll(): global showList @@ -1275,32 +1281,6 @@ def saveAll(): logger.log(u"Saving config file to disk") save_config() -def saveAndShutdown(restart=False): - global shutdown, started - - # flag restart/shutdown - if not restart: - shutdown = True - - # proceed with shutdown - started = False - -def invoke_command(to_call, *args, **kwargs): - - def delegate(): - to_call(*args, **kwargs) - - logger.log(u"Placed invoked command: " + repr(delegate) + " for " + repr(to_call) + " with " + repr( - args) + " and " + repr(kwargs), logger.DEBUG) - - IOLoop.current().add_callback(delegate) - -def invoke_restart(soft=True): - invoke_command(restart, soft=soft) - -def invoke_shutdown(): - invoke_command(saveAndShutdown, False) - def restart(soft=True): if soft: halt() @@ -1308,7 +1288,7 @@ def restart(soft=True): logger.log(u"Re-initializing all data") initialize() else: - saveAndShutdown(True) + events.put(events.SystemEvent.RESTART) def save_config(): diff --git a/sickbeard/event_queue.py b/sickbeard/event_queue.py new file mode 100644 index 00000000..1b32558a --- /dev/null +++ b/sickbeard/event_queue.py @@ -0,0 +1,45 @@ +from threading import Thread +from Queue import Queue, Empty +from tornado.ioloop import IOLoop + +class Event: + def __init__(self, type): + self._type = type + + @property + def type(self): + return self._type + +class Events(Thread): + def __init__(self, callback): + super(Events, self).__init__() + self.queue = Queue() + self.daemon = True + self.alive = True + self.callback = callback + self.name = "EVENT-QUEUE" + + # auto-start + self.start() + + def put(self, type): + self.queue.put_nowait(type) + + def run(self): + while(self.alive): + try: + # get event type + type = self.queue.get(True, 1) + + # perform callback if we got a event type + self.callback(type) + + # event completed + self.queue.task_done() + except Empty: + type = None + + # System Events + class SystemEvent(Event): + RESTART = "RESTART" + SHUTDOWN = "SHUTDOWN" \ No newline at end of file diff --git a/sickbeard/name_parser/parser.py b/sickbeard/name_parser/parser.py index 63a012f9..a77eaae7 100644 --- a/sickbeard/name_parser/parser.py +++ b/sickbeard/name_parser/parser.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with SickRage. If not, see . +import time import re import datetime import os.path @@ -133,6 +134,8 @@ class NameParser(object): if self.showObj: break else: + time.sleep(0.05) + raise InvalidShowException( "Unable to parse " + name.encode(sickbeard.SYS_ENCODING, 'xmlcharrefreplace')) @@ -251,6 +254,8 @@ class NameParser(object): result.score += 1 matches.append(result) + time.sleep(0.05) + if len(matches): result = max(sorted(matches, reverse=True, key=lambda x: x.which_regex), key=lambda x: x.score) diff --git a/sickbeard/versionChecker.py b/sickbeard/versionChecker.py index 4e9e60dc..4b45a355 100644 --- a/sickbeard/versionChecker.py +++ b/sickbeard/versionChecker.py @@ -64,7 +64,7 @@ class CheckVersion(): if sickbeard.versionCheckScheduler.action.update(): logger.log(u"Update was successful!") ui.notifications.message('Update was successful') - threading.Timer(2, sickbeard.invoke_restart, [False]).start() + sickbeard.events.put(sickbeard.events.SystemEvent.RESTART) def find_install_type(self): """ diff --git a/sickbeard/webapi.py b/sickbeard/webapi.py index 4ec6d45b..7ded782f 100644 --- a/sickbeard/webapi.py +++ b/sickbeard/webapi.py @@ -1524,7 +1524,7 @@ class CMD_SickBeardRestart(ApiCall): def run(self): """ restart sickbeard """ - threading.Timer(2, sickbeard.invoke_restart, [False]).start() + sickbeard.events.put(sickbeard.events.SystemEvent.RESTART) return _responds(RESULT_SUCCESS, msg="SickRage is restarting...") @@ -1701,7 +1701,7 @@ class CMD_SickBeardShutdown(ApiCall): def run(self): """ shutdown sickbeard """ - threading.Timer(2, sickbeard.invoke_shutdown).start() + sickbeard.events.put(sickbeard.events.SystemEvent.SHUTDOWN) return _responds(RESULT_SUCCESS, msg="SickRage is shutting down...") diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 8923bad8..b83e9615 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -3434,7 +3434,7 @@ class Home(MainHandler): if str(pid) != str(sickbeard.PID): redirect("/home/") - threading.Timer(2, sickbeard.invoke_shutdown).start() + sickbeard.events.put(sickbeard.events.SystemEvent.SHUTDOWN) title = "Shutting down" message = "SickRage is shutting down..." @@ -3450,7 +3450,7 @@ class Home(MainHandler): t.submenu = HomeMenu() # restart - threading.Timer(5, sickbeard.invoke_restart, [False]).start() + sickbeard.events.put(sickbeard.events.SystemEvent.RESTART) return _munge(t) @@ -3462,7 +3462,7 @@ class Home(MainHandler): updated = sickbeard.versionCheckScheduler.action.update() # @UndefinedVariable if updated: # do a hard restart - threading.Timer(2, sickbeard.invoke_restart, [False]).start() + sickbeard.events.put(sickbeard.events.SystemEvent.RESTART) t = PageTemplate(headers=self.request.headers, file="restart_bare.tmpl") return _munge(t) diff --git a/sickbeard/webserveInit.py b/sickbeard/webserveInit.py index 2dd33df1..ee95b01a 100644 --- a/sickbeard/webserveInit.py +++ b/sickbeard/webserveInit.py @@ -122,13 +122,6 @@ class SRWebServer(threading.Thread): try: self.io_loop.start() self.io_loop.close(True) - - # stop all tasks - sickbeard.halt() - - # save all shows to DB - sickbeard.saveAll() - except ValueError: # Ignore errors like "ValueError: I/O operation on closed kqueue fd". These might be thrown during a reload. pass