diff --git a/CHANGES.md b/CHANGES.md index 4446ffc221b0d80ab1aa6dbf2559eb93ad270339..f6093aaadeeba0fe8db98284f7b1e181d7ad36e2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,35 @@ +### 4.0.30 (2015-07-09) + +[full changelog](https://github.com/SiCKRAGETV/SickRage/compare/v4.0.29...v4.0.30) + +* Fixed: Misc. Trakt problems +* Fixed: Search strings with airdate +* Fixed: PP issues +* Fixed: Glob issue +* Fixed: Kodi single show library update +* Fixed: HDTorrents provider (and some others) +* Fixed: Maxage not honored for NZB, resulting in dead snatches +* Fixed: Encoding problems and unicode conversion errors +* Fixed: Proxy and session +* Fixed: Manage of unaired episode according to trakt rolling download if enabled +* Added: API methods to check for update and run potential update +* Added: BTDigg, FNT, BlueTigers torrent providers +* Added: Babelfish (Dependency of Guessit) +* Added: Device parameter for pushover notifications +* Added: Log message when ignoring result from provider +* Added: itorrents.org torrent caching website +* Removed: www from HDT +* Change: Torrent caching site urls are now selected at random +* Change: Enable SSL certificate verification +* Changed default episode status,it is auto set to wanted after add to show, change it to whatever you want then. +* Changed errors to warnings in updater, when commit is newer than master or network unavailable +* Updated: Provider icons +* Updated: Subliminal to 0.8.0 +* Updated: Guessit +* Updated: Cachecontrol +* Updated: Enzyme +* Updated: Subtitle provider icons + ### 4.0.29 (2015-06-26) [full changelog](https://github.com/SiCKRAGETV/SickRage/compare/v4.0.28...v4.0.29) diff --git a/gui/slick/images/providers/freshontv.png b/gui/slick/images/providers/freshontv.png old mode 100755 new mode 100644 diff --git a/gui/slick/images/providers/morethantv.png b/gui/slick/images/providers/morethantv.png old mode 100755 new mode 100644 diff --git a/gui/slick/interfaces/default/config_search.tmpl b/gui/slick/interfaces/default/config_search.tmpl old mode 100755 new mode 100644 diff --git a/lib/scene_exceptions/__init__.py b/lib/scene_exceptions/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a0ceb39b11fbd73963b90cc128348e520dbc14b9 --- /dev/null +++ b/lib/scene_exceptions/__init__.py @@ -0,0 +1,3 @@ +from os.path import dirname, abspath +TVDB_EXCEPTIONS = dirname(abspath(__file__)) + '/tvdb/exceptions.txt' +TVRAGE_EXCEPTIONS = dirname(abspath(__file__)) + '/tvrage/exceptions.txt' diff --git a/lib/trakt/trakt.py b/lib/trakt/trakt.py index d9f9069ac6e7697fb7b5ae3d93ec36e66dcf1f87..08808bf2a786cb5009b7f9eadec1838e6241544e 100644 --- a/lib/trakt/trakt.py +++ b/lib/trakt/trakt.py @@ -95,11 +95,11 @@ class TraktAPI(): # This is pretty much a fatal error if there is no status_code # It means there basically was no response at all raise traktException(e) - elif code == 502: + elif code is 502: # Retry the request, cloudflare had a proxying issue logger.log(u'Retrying trakt api request: %s' % path, logger.WARNING) return self.traktRequest(path, data, headers, url, method) - elif code == 401: + elif code is 401: logger.log(u'Unauthorized. Please check your Trakt settings', logger.WARNING) if self.traktToken(refresh=True, count=count): return self.traktRequest(path, data, headers, url, method) @@ -108,6 +108,9 @@ class TraktAPI(): #http://docs.trakt.apiary.io/#introduction/status-codes logger.log(u'Trakt may have some issues and it\'s unavailable. Try again later please', logger.WARNING) return {} + elif code is 404: + logger.log(u'Trakt error (404) the resource does not exist: %s' % url + path, logger.WARNING) + return {} else: raise traktException(e) diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py old mode 100755 new mode 100644 diff --git a/sickbeard/clients/transmission.py b/sickbeard/clients/transmission.py old mode 100755 new mode 100644 diff --git a/sickbeard/config.py b/sickbeard/config.py index b43aa65d330c420bfe1b57810ef655769b60b1ed..60a53756012661e0ff9d45cf49c1618aae8ec364 100644 --- a/sickbeard/config.py +++ b/sickbeard/config.py @@ -511,7 +511,7 @@ def check_setting_str(config, cfg_name, item_name, def_val, silent=True, censor_ config[cfg_name] = {} config[cfg_name][item_name] = helpers.encrypt(my_val, encryption_version) - if censor_log or (cfg_name, item_name) in logger.censoredItems.items(): + if censor_log or (cfg_name, item_name) in logger.censoredItems.iteritems(): logger.censoredItems[cfg_name, item_name] = my_val if not silent: diff --git a/sickbeard/dailysearcher.py b/sickbeard/dailysearcher.py index ebf305ac020a8ab691023c7f0c3cc6aac5de1ff9..bd72ab250f5fc0360b3137154496ce01bc5f68fc 100644 --- a/sickbeard/dailysearcher.py +++ b/sickbeard/dailysearcher.py @@ -40,6 +40,8 @@ class DailySearcher(): self.amActive = False def run(self, force=False): + if self.amActive: + return self.amActive = True diff --git a/sickbeard/databases/cache_db.py b/sickbeard/databases/cache_db.py index 8cfe5a4167c00eea6387823db1a1188d28b0e3b9..9734be453c48afe4e03f25d0a6028bfb60a447b6 100644 --- a/sickbeard/databases/cache_db.py +++ b/sickbeard/databases/cache_db.py @@ -24,15 +24,14 @@ class InitialSchema(db.SchemaUpgrade): return self.hasTable("db_version") def execute(self): - queries = [ - ("CREATE TABLE db_version (db_version INTEGER);",), ("CREATE TABLE lastUpdate (provider TEXT, time NUMERIC);",), ("CREATE TABLE lastSearch (provider TEXT, time NUMERIC);",), ("CREATE TABLE scene_exceptions (exception_id INTEGER PRIMARY KEY, indexer_id INTEGER KEY, show_name TEXT, season NUMERIC DEFAULT -1, custom NUMERIC DEFAULT 0);",), ("CREATE TABLE scene_names (indexer_id INTEGER, name TEXT);",), ("CREATE TABLE network_timezones (network_name TEXT PRIMARY KEY, timezone TEXT);",), ("CREATE TABLE scene_exceptions_refresh (list TEXT PRIMARY KEY, last_refreshed INTEGER);",), + ("CREATE TABLE db_version (db_version INTEGER);",), ("INSERT INTO db_version(db_version) VALUES (1);",), ] for query in queries: @@ -48,14 +47,14 @@ class AddSceneExceptions(InitialSchema): def execute(self): self.connection.action( - "CREATE TABLE scene_exceptions (exception_id INTEGER PRIMARY KEY, indexer_id INTEGER KEY, show_name TEXT)") + "CREATE TABLE scene_exceptions (exception_id INTEGER PRIMARY KEY, indexer_id INTEGER KEY, show_name TEXT);") class AddSceneNameCache(AddSceneExceptions): def test(self): return self.hasTable("scene_names") def execute(self): - self.connection.action("CREATE TABLE scene_names (indexer_id INTEGER, name TEXT)") + self.connection.action("CREATE TABLE scene_names (indexer_id INTEGER, name TEXT);") class AddNetworkTimezones(AddSceneNameCache): @@ -63,16 +62,16 @@ class AddNetworkTimezones(AddSceneNameCache): return self.hasTable("network_timezones") def execute(self): - self.connection.action("CREATE TABLE network_timezones (network_name TEXT PRIMARY KEY, timezone TEXT)") + self.connection.action("CREATE TABLE network_timezones (network_name TEXT PRIMARY KEY, timezone TEXT);") class AddLastSearch(AddNetworkTimezones): def test(self): return self.hasTable("lastSearch") def execute(self): - self.connection.action("CREATE TABLE lastSearch (provider TEXT, time NUMERIC)") + self.connection.action("CREATE TABLE lastSearch (provider TEXT, time NUMERIC);") -class AddSceneExceptionsSeasons(AddSceneNameCache): +class AddSceneExceptionsSeasons(AddLastSearch): def test(self): return self.hasColumn("scene_exceptions", "season") @@ -92,4 +91,4 @@ class AddSceneExceptionsRefresh(AddSceneExceptionsCustom): def execute(self): self.connection.action( - "CREATE TABLE scene_exceptions_refresh (list TEXT PRIMARY KEY, last_refreshed INTEGER)") + "CREATE TABLE scene_exceptions_refresh (list TEXT PRIMARY KEY, last_refreshed INTEGER);") diff --git a/sickbeard/databases/mainDB.py b/sickbeard/databases/mainDB.py index 2c9aefbc7ae3a992cb5c410ce8ee803cac8fbccd..2742bc70bf916ddd7f79dcf58a183a5a86198d93 100644 --- a/sickbeard/databases/mainDB.py +++ b/sickbeard/databases/mainDB.py @@ -162,7 +162,7 @@ class MainSanityCheck(db.DBSanityCheck): '': 'Unknown', } - for old_status, new_status in status_map.items(): + for old_status, new_status in status_map.iteritems(): self.connection.action("UPDATE tv_shows SET status = ? WHERE LOWER(status) = ?", [new_status, old_status]) def fix_episode_statuses(self): diff --git a/sickbeard/generic_queue.py b/sickbeard/generic_queue.py index f97e348a73cf20023aced3b45d21477a91d43ee3..166c8aaa434afc79692ae55b4be6bcc01aef623a 100644 --- a/sickbeard/generic_queue.py +++ b/sickbeard/generic_queue.py @@ -50,47 +50,50 @@ class GenericQueue(object): self.min_priority = 0 def add_item(self, item): - item.added = datetime.datetime.now() - self.queue.append(item) + with self.lock: + item.added = datetime.datetime.now() + self.queue.append(item) - return item + return item def run(self, force=False): - - # only start a new task if one isn't already going - if self.currentItem is None or not self.currentItem.isAlive(): - - # if the thread is dead then the current item should be finished - if self.currentItem: - self.currentItem.finish() - self.currentItem = None - - # if there's something in the queue then run it in a thread and take it out of the queue - if len(self.queue) > 0: - - # sort by priority - def sorter(x, y): - """ - Sorts by priority descending then time ascending - """ - if x.priority == y.priority: - if y.added == x.added: - return 0 - elif y.added < x.added: - return 1 - elif y.added > x.added: - return -1 - else: - return y.priority - x.priority - - self.queue.sort(cmp=sorter) - if self.queue[0].priority < self.min_priority: - return - - # launch the queue item in a thread - self.currentItem = self.queue.pop(0) - self.currentItem.name = self.queue_name + '-' + self.currentItem.name - self.currentItem.start() + with self.lock: + # only start a new task if one isn't already going + if self.currentItem is None or not self.currentItem.isAlive(): + + # if the thread is dead then the current item should be finished + if self.currentItem: + self.currentItem.finish() + self.currentItem = None + + # if there's something in the queue then run it in a thread and take it out of the queue + if len(self.queue) > 0: + + # sort by priority + def sorter(x, y): + """ + Sorts by priority descending then time ascending + """ + if x.priority == y.priority: + if y.added == x.added: + return 0 + elif y.added < x.added: + return 1 + elif y.added > x.added: + return -1 + else: + return y.priority - x.priority + + self.queue.sort(cmp=sorter) + if self.queue[0].priority < self.min_priority: + return + + # launch the queue item in a thread + self.currentItem = self.queue.pop(0) + self.currentItem.name = self.queue_name + '-' + self.currentItem.name + self.currentItem.start() + + self.amActive = False class QueueItem(threading.Thread): def __init__(self, name, action_id=0): diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py index b9f50e394e5e42884a1d836d38a6e2968568979f..e8fde7c714da8a639911be11c20f2f11416dc0af 100644 --- a/sickbeard/helpers.py +++ b/sickbeard/helpers.py @@ -134,6 +134,7 @@ def remove_non_release_groups(name): '-NZBGEEK$': 'searchre', '-RP$': 'searchre', '-20-40$': 'searchre', + '\[NO-RAR\] - \[ www\.torrentday\.com \]$': 'searchre', '^\[ www\.TorrentDay\.com \] - ': 'searchre', '^\[ www\.Cpasbien\.pw \] ': 'searchre', } diff --git a/sickbeard/indexers/indexer_config.py b/sickbeard/indexers/indexer_config.py index 35c15e9771e9dd2eb9cb493c174389afd5501d0f..71e4ddc86371275ab303e1f8979d923e3d1b3a2b 100644 --- a/sickbeard/indexers/indexer_config.py +++ b/sickbeard/indexers/indexer_config.py @@ -1,5 +1,6 @@ from lib.tvdb_api.tvdb_api import Tvdb from lib.tvrage_api.tvrage_api import TVRage +from scene_exceptions import TVDB_EXCEPTIONS, TVRAGE_EXCEPTIONS import requests INDEXER_TVDB = 1 @@ -43,7 +44,7 @@ indexerConfig[INDEXER_TVRAGE] = { indexerConfig[INDEXER_TVDB]['trakt_id'] = 'tvdb_id' indexerConfig[INDEXER_TVDB]['xem_origin'] = 'tvdb' indexerConfig[INDEXER_TVDB]['icon'] = 'thetvdb16.png' -indexerConfig[INDEXER_TVDB]['scene_loc'] = '../lib/scene_exceptions/tvdb/exceptions.txt' +indexerConfig[INDEXER_TVDB]['scene_loc'] = TVDB_EXCEPTIONS 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'] @@ -51,6 +52,6 @@ indexerConfig[INDEXER_TVDB]['base_url'] = 'http://thetvdb.com/api/%(apikey)s/ser indexerConfig[INDEXER_TVRAGE]['trakt_id'] = 'tvrage_id' indexerConfig[INDEXER_TVRAGE]['xem_origin'] = 'rage' indexerConfig[INDEXER_TVRAGE]['icon'] = 'tvrage16.png' -indexerConfig[INDEXER_TVRAGE]['scene_loc'] = '../lib/scene_exceptions/tvrage/exceptions.txt' +indexerConfig[INDEXER_TVRAGE]['scene_loc'] = TVRAGE_EXCEPTIONS 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/logger.py b/sickbeard/logger.py index f5e0d92ffbbda5e0fd300a9ff53922cd24fe0155..b991c9f6be4e3e7c30b5189c03709f4e819bb0bb 100644 --- a/sickbeard/logger.py +++ b/sickbeard/logger.py @@ -58,7 +58,7 @@ class CensoredFormatter(logging.Formatter, object): def format(self, record): msg = super(CensoredFormatter, self).format(record) - for k, v in censoredItems.items(): + for k, v in censoredItems.iteritems(): if v and len(v) > 0 and v in msg: msg = msg.replace(v, len(v) * '*') # Needed because Newznab apikey isn't stored as key=value in a section. diff --git a/sickbeard/name_cache.py b/sickbeard/name_cache.py index eefa235285508082b4d69ce997915c7c1ae27c88..a349a8da6589ffc4a5521f634501c77b03e144d8 100644 --- a/sickbeard/name_cache.py +++ b/sickbeard/name_cache.py @@ -77,7 +77,7 @@ def clearCache(indexerid=0): def saveNameCacheToDb(): cacheDB = db.DBConnection('cache.db') - for name, indexer_id in nameCache.items(): + for name, indexer_id in nameCache.iteritems(): cacheDB.action("INSERT OR REPLACE INTO scene_names (indexer_id, name) VALUES (?, ?)", [indexer_id, name]) diff --git a/sickbeard/network_timezones.py b/sickbeard/network_timezones.py index 6a1805760f6be3d32c6a5ace9f87a01355ecc32e..b01d9a3a072bdb1a6ab3f4c6e7c3eafba81cea68 100644 --- a/sickbeard/network_timezones.py +++ b/sickbeard/network_timezones.py @@ -171,22 +171,22 @@ def update_network_dict(): my_db = db.DBConnection('cache.db') - network_list = dict(my_db.select('SELECT * FROM network_timezones')) + network_list = dict(my_db.select('SELECT * FROM network_timezones;')) queries = [] for network, timezone in d.iteritems(): existing = network_list.has_key(network) if not existing: - queries.append(['INSERT OR IGNORE INTO network_timezones VALUES (?,?)', [network, timezone]]) + queries.append(['INSERT OR IGNORE INTO network_timezones VALUES (?,?);', [network, timezone]]) elif network_list[network] is not timezone: - queries.append(['UPDATE OR IGNORE network_timezones SET timezone = ? WHERE network_name = ?', [timezone, network]]) + queries.append(['UPDATE OR IGNORE network_timezones SET timezone = ? WHERE network_name = ?;', [timezone, network]]) if existing: del network_list[network] if network_list: purged = list(x for x in network_list) - queries.append(['DELETE FROM network_timezones WHERE network_name IN (%s)' % ','.join(['?'] * len(purged)), purged]) + queries.append(['DELETE FROM network_timezones WHERE network_name IN (%s);' % ','.join(['?'] * len(purged)), purged]) if queries: my_db.mass_action(queries) @@ -197,10 +197,10 @@ def update_network_dict(): def load_network_dict(): try: my_db = db.DBConnection('cache.db') - cur_network_list = my_db.select('SELECT * FROM network_timezones') + cur_network_list = my_db.select('SELECT * FROM network_timezones;') if cur_network_list is None or len(cur_network_list) < 1: update_network_dict() - cur_network_list = my_db.select('SELECT * FROM network_timezones') + cur_network_list = my_db.select('SELECT * FROM network_timezones;') d = dict(cur_network_list) except: d = {} diff --git a/sickbeard/notifiers/boxcar2.py b/sickbeard/notifiers/boxcar2.py old mode 100755 new mode 100644 diff --git a/sickbeard/notifiers/plex.py b/sickbeard/notifiers/plex.py index c67aa8f119dbed59dd70dcf551471e748ad874ad..de496dfef0211a8b4e526c6f2f835b8903f663e1 100644 --- a/sickbeard/notifiers/plex.py +++ b/sickbeard/notifiers/plex.py @@ -258,7 +258,7 @@ class PLEXNotifier: hosts_try = (hosts_all.copy(), hosts_match.copy())[len(hosts_match)] host_list = [] - for section_key, cur_host in hosts_try.items(): + for section_key, cur_host in hosts_try.iteritems(): url = 'http://%s/library/sections/%s/refresh%s' % (cur_host, section_key, token_arg) try: diff --git a/sickbeard/providers/__init__.py b/sickbeard/providers/__init__.py index 79fb54854f3efd8169401f459f7e226e60b29e31..a6e634812303ecfb1cc084315aa5e1d93a31ae13 100644 --- a/sickbeard/providers/__init__.py +++ b/sickbeard/providers/__init__.py @@ -68,6 +68,11 @@ def sortedProviderList(randomize=False): if curModule in providerDict: newList.append(providerDict[curModule]) + # add all enabled providers first + for curModule in providerDict: + if providerDict[curModule] not in newList and providerDict[curModule].isEnabled(): + newList.append(providerDict[curModule]) + # add any modules that are missing from that list for curModule in providerDict: if providerDict[curModule] not in newList: diff --git a/sickbeard/providers/btn.py b/sickbeard/providers/btn.py index 3bbab68f48389d69409053de284aad35c1e90613..5470838b4dbe407df2e252fc54d02576a870a32d 100644 --- a/sickbeard/providers/btn.py +++ b/sickbeard/providers/btn.py @@ -353,7 +353,7 @@ class BTNProvider(generic.TorrentProvider): else: items[quality].append(item) - itemList = list(itertools.chain(*[v for (k, v) in sorted(items.items(), reverse=True)])) + itemList = list(itertools.chain(*[v for (k, v) in sorted(items.iteritems(), reverse=True)])) itemList += itemsUnknown if itemsUnknown else [] # filter results diff --git a/sickbeard/providers/generic.py b/sickbeard/providers/generic.py index 86398aa4c378cdb90cb62dbec35617e4b61ec522..a183d7d2d68274988f379ccfb509aaa109895770 100644 --- a/sickbeard/providers/generic.py +++ b/sickbeard/providers/generic.py @@ -62,6 +62,8 @@ class GenericProvider: self.search_mode = None self.search_fallback = False + + self.enabled = False self.enable_daily = False self.enable_backlog = False @@ -375,7 +377,7 @@ class GenericProvider: else: items[quality].append(item) - itemList = list(itertools.chain(*[v for (k, v) in sorted(items.items(), reverse=True)])) + itemList = list(itertools.chain(*[v for (k, v) in sorted(items.iteritems(), reverse=True)])) itemList += itemsUnknown if itemsUnknown else [] # filter results diff --git a/sickbeard/providers/iptorrents.py b/sickbeard/providers/iptorrents.py index 485459f3acbac95d36cf837b1583b663200d9b44..f2574442a79669e0db2c7008ca1d3d3912954cc0 100644 --- a/sickbeard/providers/iptorrents.py +++ b/sickbeard/providers/iptorrents.py @@ -186,7 +186,7 @@ class IPTorrentsProvider(generic.TorrentProvider): else: items[quality].append(item) - itemList = list(itertools.chain(*[v for (k, v) in sorted(items.items(), reverse=True)])) + itemList = list(itertools.chain(*[v for (k, v) in sorted(items.iteritems(), reverse=True)])) itemList += itemsUnknown if itemsUnknown else [] # filter results diff --git a/sickbeard/providers/newznab.py b/sickbeard/providers/newznab.py index 85db5e81c84cc5b77b9d7a091a38ed6a728c3bb8..58206969fb4e2b308454fbfcefc000c84cbf7c2a 100644 --- a/sickbeard/providers/newznab.py +++ b/sickbeard/providers/newznab.py @@ -107,9 +107,9 @@ class NewznabProvider(generic.NZBProvider): data = self.cache.getRSSFeed("%s/api?%s" % (self.url, urllib.urlencode(params))) except: logger.log(u"Error getting html for [%s]" % - ("%s/api?%s" % (self.url, '&'.join("%s=%s" % (x,y) for x,y in params.items())) ), logger.DEBUG) + ("%s/api?%s" % (self.url, '&'.join("%s=%s" % (x,y) for x,y in params.iteritems())) ), logger.DEBUG) return (False, return_categories, "Error getting html for [%s]" % - ("%s/api?%s" % (self.url, '&'.join("%s=%s" % (x,y) for x,y in params.items()) ))) + ("%s/api?%s" % (self.url, '&'.join("%s=%s" % (x,y) for x,y in params.iteritems()) ))) if not self._checkAuthFromData(data): logger.log(u"Error parsing xml for [%s]" % (self.name), logger.DEBUG) @@ -198,7 +198,7 @@ class NewznabProvider(generic.NZBProvider): if add_string: params['q'] += ' ' + add_string - to_return.append(params) + to_return.append(dict(params)) if ep_obj.show.anime: paramsNoEp = params.copy() diff --git a/sickbeard/providers/tntvillage.py b/sickbeard/providers/tntvillage.py index d8b2b9aa2feb969e0fad7c8eade4c17a9349e9b4..84d07f15510bc8df422173d8473946f07293456f 100644 --- a/sickbeard/providers/tntvillage.py +++ b/sickbeard/providers/tntvillage.py @@ -302,6 +302,9 @@ class TNTVillageProvider(generic.TorrentProvider): name = str(span_tag) + if not name: + return 0 + subFound=0 for sub in self.sub_string: diff --git a/sickbeard/scene_exceptions.py b/sickbeard/scene_exceptions.py index ae1e920d0b63e122c4633d0b9e54b1d37c18e805..063ab210671150c89f3c0aad6845de97674f4e28 100644 --- a/sickbeard/scene_exceptions.py +++ b/sickbeard/scene_exceptions.py @@ -223,30 +223,22 @@ def retrieve_exceptions(): else: exception_dict[anidb_ex] = anidb_exception_dict[anidb_ex] - changed_exceptions = False - - # write all the exceptions we got off the net into the database + queries = [] myDB = db.DBConnection('cache.db') for cur_indexer_id in exception_dict: - - # get a list of the existing exceptions for this ID - existing_exceptions = [x["show_name"] for x in - myDB.select("SELECT * FROM scene_exceptions WHERE indexer_id = ?", [cur_indexer_id])] - + sql_ex = myDB.select("SELECT * FROM scene_exceptions WHERE indexer_id = ?;", [cur_indexer_id]) + existing_exceptions = [x["show_name"] for x in sql_ex] if not cur_indexer_id in exception_dict: continue for cur_exception_dict in exception_dict[cur_indexer_id]: - cur_exception, curSeason = cur_exception_dict.items()[0] - - # if this exception isn't already in the DB then add it - if cur_exception not in existing_exceptions: - myDB.action("INSERT INTO scene_exceptions (indexer_id, show_name, season) VALUES (?,?,?)", - [cur_indexer_id, cur_exception, curSeason]) - changed_exceptions = True - - # since this could invalidate the results of the cache we clear it out after updating - if changed_exceptions: + for ex in cur_exception_dict.iteritems(): + cur_exception, curSeason = ex + if cur_exception not in existing_exceptions: + queries.append(["INSERT OR IGNORE INTO scene_exceptions (indexer_id, show_name, season) VALUES (?,?,?);", + [cur_indexer_id, cur_exception, curSeason]]) + if queries: + myDB.mass_action(queries) logger.log(u"Updated scene exceptions", logger.DEBUG) else: logger.log(u"No scene exceptions update needed", logger.DEBUG) @@ -316,7 +308,7 @@ def _xem_exceptions_fetcher(): if parsedJSON['result'] == 'failure': continue - for indexerid, names in parsedJSON['data'].items(): + for indexerid, names in parsedJSON['data'].iteritems(): try: xem_exception_dict[int(indexerid)] = names except Exception as e: diff --git a/sickbeard/search.py b/sickbeard/search.py index 5c3b8e8f7325a22c7a6a10f8daecc5821a8f1027..d56bfab67dc6d3ccf8ad88b90fdf40c8b7a64a03 100644 --- a/sickbeard/search.py +++ b/sickbeard/search.py @@ -321,19 +321,14 @@ def isFirstBestMatch(result): return False def wantedEpisodes(show, fromDate): + anyQualities, bestQualities = common.Quality.splitQuality(show.quality) # @UnusedVariable allQualities = list(set(anyQualities + bestQualities)) logger.log(u"Seeing if we need anything from " + show.name, logger.DEBUG) myDB = db.DBConnection() - if show.air_by_date: - sqlResults = myDB.select( - "SELECT ep.status, ep.season, ep.episode FROM tv_episodes ep, tv_shows show WHERE season != 0 AND ep.showid = show.indexer_id AND show.paused = 0 AND ep.airdate > ? AND ep.showid = ? AND show.air_by_date = 1", - [fromDate.toordinal(), show.indexerid]) - else: - sqlResults = myDB.select( - "SELECT status, season, episode FROM tv_episodes WHERE showid = ? AND season > 0 and airdate > ?", + sqlResults = myDB.select("SELECT status, season, episode FROM tv_episodes WHERE showid = ? AND season > 0 and airdate > ?", [show.indexerid, fromDate.toordinal()]) # check through the list of statuses to see if we want any diff --git a/sickbeard/searchBacklog.py b/sickbeard/searchBacklog.py index fc50b66bfc517327f096a2d5306ed3f99a1ba023..0c36135bb080712f271c0235a726857bdd07cd1e 100644 --- a/sickbeard/searchBacklog.py +++ b/sickbeard/searchBacklog.py @@ -74,6 +74,9 @@ class BacklogSearcher: logger.log(u"Backlog is still running, not starting it again", logger.DEBUG) return + self.amActive = True + self.amPaused = False + if which_shows: show_list = which_shows else: @@ -84,13 +87,10 @@ class BacklogSearcher: curDate = datetime.date.today().toordinal() fromDate = datetime.date.fromordinal(1) - if not which_shows and not curDate - self._lastBacklog >= self.cycleTime: + if not which_shows and not ((curDate - self._lastBacklog) >= self.cycleTime): logger.log(u"Running limited backlog on missed episodes " + str(sickbeard.BACKLOG_DAYS) + " day(s) and older only") fromDate = datetime.date.today() - datetime.timedelta(days=sickbeard.BACKLOG_DAYS) - self.amActive = True - self.amPaused = False - # go through non air-by-date shows and see if they need any episodes for curShow in show_list: @@ -99,7 +99,7 @@ class BacklogSearcher: segments = self._get_segments(curShow, fromDate) - for season, segment in segments.items(): + for season, segment in segments.iteritems(): self.currentSearchInfo = {'title': curShow.name + " Season " + str(season)} backlog_queue_item = search_queue.BacklogQueueItem(curShow, segment) @@ -135,19 +135,17 @@ class BacklogSearcher: return self._lastBacklog def _get_segments(self, show, fromDate): + if show.paused: + logger.log(u"Skipping backlog for {show_name} because the show is paused".format(show_name=show.name), logger.DEBUG) + return {} + anyQualities, bestQualities = common.Quality.splitQuality(show.quality) # @UnusedVariable logger.log(u"Seeing if we need anything from {show_name}".format(show_name=show.name), logger.DEBUG) myDB = db.DBConnection() - if show.air_by_date: - sqlResults = myDB.select( - "SELECT ep.status, ep.season, ep.episode FROM tv_episodes ep, tv_shows show WHERE season != 0 AND ep.showid = show.indexer_id AND show.paused = 0 AND ep.airdate > ? AND ep.showid = ? AND show.air_by_date = 1", + sqlResults = myDB.select("SELECT status, season, episode FROM tv_episodes WHERE season > 0 AND airdate > ? AND showid = ?", [fromDate.toordinal(), show.indexerid]) - else: - sqlResults = myDB.select( - "SELECT status, season, episode FROM tv_episodes WHERE showid = ? AND season > 0 and airdate > ?", - [show.indexerid, fromDate.toordinal()]) # check through the list of statuses to see if we want any wanted = {} diff --git a/sickbeard/tv.py b/sickbeard/tv.py index 57306807dfb86ce2c5cfaf21e75eae72ab595c8a..7e1b1c103ed1a4d0d81497d460cc98e281cc8c32 100644 --- a/sickbeard/tv.py +++ b/sickbeard/tv.py @@ -965,7 +965,7 @@ class TVShow(object): # Rename dict keys without spaces for DB upsert self.imdb_info = dict( - (k.replace(' ', '_'), k(v) if hasattr(v, 'keys') else v) for k, v in imdb_info.items()) + (k.replace(' ', '_'), k(v) if hasattr(v, 'keys') else v) for k, v in imdb_info.iteritems()) logger.log(str(self.indexerid) + u": Obtained info from IMDb ->" + str(self.imdb_info), logger.DEBUG) def nextEpisode(self): @@ -1453,46 +1453,17 @@ class TVEpisode(object): providers = sickbeard.subtitles.getEnabledServiceList() vname = self.location - create_link = False - if self.release_name: - try: - dir, name = ek.ek(os.path.split, self.location) - tmp = ek.ek(os.path.join, dir, self.release_name) - if ek.ek(os.path.splitext, tmp)[-1] is not ek.ek(os.path.splitext, self.location)[-1]: - tmp += ek.ek(os.path.splitext, self.location)[-1] - - create_link = not ek.ek(os.path.exists, tmp) - if create_link: - ek.ek(helpers.link, self.location, tmp) - vname = tmp - except Exception: - create_link = False - vname = self.location - pass - else: - logger.log(u'%s: No release name for S%02dE%02d, using existing file name' % - (self.show.indexerid, self.season, self.episode), logger.DEBUG) - video = None try: video = subliminal.scan_video(vname, subtitles=not force, embedded_subtitles=not sickbeard.EMBEDDED_SUBTITLES_ALL or not force) except Exception: logger.log(u'%s: Exception caught in subliminal.scan_video for S%02dE%02d' % (self.show.indexerid, self.season, self.episode), logger.DEBUG) - if create_link and vname is not self.location: - ek.ek(os.unlink, vname) return pass - if create_link and vname is not self.location: - ek.ek(os.unlink, vname) - - #video.tvdb_id = self.show.indexerid - #video.imdb_id = self.show.imdbid - #if not video.title and self.name: - # video.title = self.name - #if not video.release_group and self.release_group: - # video.release_group = self.release_group + if not video: + return # TODO: Add gui option for hearing_impaired parameter ? foundSubs = subliminal.download_best_subtitles([video], languages=languages, providers=providers, single=not sickbeard.SUBTITLES_MULTI, hearing_impaired=False) @@ -1504,35 +1475,20 @@ class TVEpisode(object): # absolute path (GUI 'Browse' button, or typed absolute path) - sillyness? if ek.ek(os.path.exists, sickbeard.SUBTITLES_DIR): subs_new_path = ek.ek(os.path.join, sickbeard.SUBTITLES_DIR, self.show.name) - dir_exists = True else: # relative to the folder the episode is in - sillyness? - subs_new_path = ek.ek(os.path.join, os.path.dirname(self.location), sickbeard.SUBTITLES_DIR) + subs_new_path = ek.ek(os.path.join, ek.ek(os.path.dirname, self.location), sickbeard.SUBTITLES_DIR) dir_exists = helpers.makeDir(subs_new_path) - if not dir_exists: - logger.log(u'Unable to create subtitles folder ' + subs_new_path, logger.ERROR) - else: - helpers.chmodAsParent(subs_new_path) + if not dir_exists: + logger.log(u'Unable to create subtitles folder ' + subs_new_path, logger.ERROR) + else: + helpers.chmodAsParent(subs_new_path) else: - # let subliminal save the subtitles next to the episode subs_new_path = None subliminal.save_subtitles(foundSubs, directory=subs_new_path, single=not sickbeard.SUBTITLES_MULTI) - # rename the subtitles if needed - if create_link: - for video, subs in foundSubs.items(): - for sub in subs: - path = subliminal.subtitle.get_subtitle_path(video.name, sub.language if sickbeard.SUBTITLES_MULTI else None) - if subs_new_path: - path = ek.ek(os.path.join, subs_new_path, ek.ek(os.path.split, path)[1]) - - new_path = path.replace(ek.ek(os.path.splitext, vname)[0], - ek.ek(os.path.splitext, ek.ek(os.path.basename, self.location))[0]) - if ek.ek(os.path.exists, path) and not ek.ek(os.path.exists, new_path): - ek.ek(os.rename, path, new_path) - except Exception as e: logger.log("Error occurred when downloading subtitles: " + traceback.format_exc(), logger.ERROR) return @@ -1555,7 +1511,7 @@ class TVEpisode(object): (self.show.indexerid, self.season, self.episode), logger.DEBUG) if sickbeard.SUBTITLES_HISTORY: - for video, subs in foundSubs.items(): + for video, subs in foundSubs.iteritems(): for sub in subs: logger.log(u'history.logSubtitle %s, %s' % (sub.provider_name, sub.language.alpha3), logger.DEBUG) history.logSubtitle(self.show.indexerid, self.season, self.episode, self.status, sub) diff --git a/sickbeard/webapi.py b/sickbeard/webapi.py index 0c62e0d4c7e72a95cd060ffaece781774df2abd2..c3c0eba0edd92afbd5ba2dc612b0f58168a113a8 100644 --- a/sickbeard/webapi.py +++ b/sickbeard/webapi.py @@ -85,7 +85,7 @@ class ApiHandler(RequestHandler): def get(self, *args, **kwargs): kwargs = self.request.arguments - for arg, value in kwargs.items(): + for arg, value in kwargs.iteritems(): if len(value) == 1: kwargs[arg] = value[0] @@ -1025,7 +1025,7 @@ class CMD_EpisodeSetStatus(ApiCall): extra_msg = "" if start_backlog: - for season, segment in segments.items(): + for season, segment in segments.iteritems(): cur_backlog_queue_item = search_queue.BacklogQueueItem(showObj, segment) sickbeard.searchQueueScheduler.action.add_item(cur_backlog_queue_item) # @UndefinedVariable diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index d769133549867c3dc0de7f4714d80faec068e3f2..144dcf24dc67a413402ab645fe134caad6390169 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -262,7 +262,7 @@ class WebHandler(BaseHandler): def async_call(self, function): try: kwargs = self.request.arguments - for arg, value in kwargs.items(): + for arg, value in kwargs.iteritems(): if len(value) == 1: kwargs[arg] = value[0] @@ -1784,7 +1784,7 @@ class Home(WebRoot): msg = "Backlog was automatically started for the following seasons of <b>" + showObj.name + "</b>:<br />" msg += '<ul>' - for season, segment in segments.items(): + for season, segment in segments.iteritems(): cur_backlog_queue_item = search_queue.BacklogQueueItem(showObj, segment) sickbeard.searchQueueScheduler.action.add_item(cur_backlog_queue_item) @@ -1803,7 +1803,7 @@ class Home(WebRoot): msg = "Retrying Search was automatically started for the following season of <b>" + showObj.name + "</b>:<br />" msg += '<ul>' - for season, segment in segments.items(): + for season, segment in segments.iteritems(): cur_failed_queue_item = search_queue.FailedQueueItem(showObj, segment) sickbeard.searchQueueScheduler.action.add_item(cur_failed_queue_item) @@ -2033,12 +2033,10 @@ class Home(WebRoot): newSubtitles = frozenset(ep_obj.subtitles).difference(previous_subtitles) if newSubtitles: newLangs = [subtitles.fromietf(newSub) for newSub in newSubtitles] - status = 'New subtitles downloaded: %s' % ' '.join([ - "<img src='" + sickbeard.WEB_ROOT + "/images/flags/" + newLang.alpha3 + - ".png' alt='" + newLang.name + "'/>" for newLang in newLangs]) + status = 'New subtitles downloaded: %s' % ', '.join([newLang.name for newLang in newLangs]) else: status = 'No subtitles downloaded' - ui.notifications.message('Subtitles Search', status) + ui.notifications.message(ep_obj.show.name, status) return json.dumps({'result': status, 'subtitles': ','.join(ep_obj.subtitles)}) def setSceneNumbering(self, show, indexer, forSeason=None, forEpisode=None, forAbsolute=None, sceneSeason=None, @@ -2235,7 +2233,7 @@ class HomeAddShows(Home): map(final_results.extend, ([[sickbeard.indexerApi(id).name, id, sickbeard.indexerApi(id).config["show_url"], int(show['id']), - show['seriesname'], show['firstaired']] for show in shows] for id, shows in results.items())) + show['seriesname'], show['firstaired']] for show in shows] for id, shows in results.iteritems())) lang_id = sickbeard.indexerApi().config['langabbv_to_id'][lang] return json.dumps({'results': final_results, 'langid': lang_id}) @@ -4406,6 +4404,7 @@ class ConfigProviders(Config): if curProvider.getID() not in finishedNames: sickbeard.torrentRssProviderList.remove(curProvider) + disabled_list = [] # do the enable/disable for curProviderStr in provider_str_list: curProvider, curEnabled = curProviderStr.split(':') @@ -4416,12 +4415,18 @@ class ConfigProviders(Config): if curProvObj: curProvObj[0].enabled = bool(curEnabled) - provider_list.append(curProvider) + if curEnabled: + provider_list.append(curProvider) + else: + disabled_list.append(curProvider) + if curProvider in newznabProviderDict: newznabProviderDict[curProvider].enabled = bool(curEnabled) elif curProvider in torrentRssProviderDict: torrentRssProviderDict[curProvider].enabled = bool(curEnabled) + provider_list = provider_list + disabled_list + # dynamically load provider settings for curTorrentProvider in [curProvider for curProvider in sickbeard.providers.sortedProviderList() if curProvider.providerType == sickbeard.GenericProvider.TORRENT]: