Merge pull request #775 from JackDandy/feature/ChangeOptimiseTVDBv1

Change optimise TheTVDB processes, 40% to 66% saved adding new and ex…
This commit is contained in:
JackDandy 2016-09-24 02:17:11 +01:00 committed by GitHub
commit b44b11b3fc
9 changed files with 227 additions and 123 deletions

View file

@ -159,6 +159,11 @@
* Change show loaded log message at start up and include info source
* Change if episode has no airdate then set status to unaired (was skipped)
* Fix only replace initial quality releases from the upgrade to list
* Change optimise TheTVDB processes, 40% to 66% saved adding new and existing shows, 40% to 50% saved per show update
* Change improve shows with more episodes gain largest reductions in time spent processing
* Change when using "Add new show" reduce search time outs
* Change always allow incomplete show data
* Remove redundant config/general/"Allow incomplete show data"
[develop changelog]
* Change send nzb data to NZBGet for Anizb instead of url

View file

@ -88,18 +88,6 @@
</label>
</div>
#if hasattr($sickbeard, 'ALLOW_INCOMPLETE_SHOWDATA')
<div class="field-pair">
<label for="allow_incomplete_showdata">
<span class="component-title">Allow incomplete show data</span>
<span class="component-desc">
<input type="checkbox" name="allow_incomplete_showdata" id="allow_incomplete_showdata"#echo ('', $checked)[$sickbeard.ALLOW_INCOMPLETE_SHOWDATA]#>
<p>add partial show data for future updates to complete</p>
</span>
</label>
</div>
#end if
<div class="field-pair">
<span class="component-title">Send to trash for actions</span>
<span class="component-desc">

141
lib/etreetodict.py Normal file
View file

@ -0,0 +1,141 @@
try:
from lxml import ElementTree
except ImportError:
try:
import xml.etree.cElementTree as ElementTree
except ImportError:
import xml.etree.ElementTree as ElementTree
class XmlDictObject(dict):
"""
Adds object like functionality to the standard dictionary.
"""
def __init__(self, initdict=None):
if initdict is None:
initdict = {}
dict.__init__(self, initdict)
def __getattr__(self, item):
return self.__getitem__(item)
def __setattr__(self, item, value):
self.__setitem__(item, value)
def __str__(self):
if self.has_key('_text'):
return self.__getitem__('_text')
else:
return ''
@staticmethod
def Wrap(x):
"""
Static method to wrap a dictionary recursively as an XmlDictObject
"""
if isinstance(x, dict):
return XmlDictObject((k, XmlDictObject.Wrap(v)) for (k, v) in x.iteritems())
elif isinstance(x, list):
return [XmlDictObject.Wrap(v) for v in x]
else:
return x
@staticmethod
def _UnWrap(x):
if isinstance(x, dict):
return dict((k, XmlDictObject._UnWrap(v)) for (k, v) in x.iteritems())
elif isinstance(x, list):
return [XmlDictObject._UnWrap(v) for v in x]
else:
return x
def UnWrap(self):
"""
Recursively converts an XmlDictObject to a standard dictionary and returns the result.
"""
return XmlDictObject._UnWrap(self)
def _ConvertDictToXmlRecurse(parent, dictitem):
assert type(dictitem) is not type([])
if isinstance(dictitem, dict):
for (tag, child) in dictitem.iteritems():
if str(tag) == '_text':
parent.text = str(child)
elif type(child) is type([]):
# iterate through the array and convert
for listchild in child:
elem = ElementTree.Element(tag)
parent.append(elem)
_ConvertDictToXmlRecurse(elem, listchild)
else:
elem = ElementTree.Element(tag)
parent.append(elem)
_ConvertDictToXmlRecurse(elem, child)
else:
parent.text = str(dictitem)
def ConvertDictToXml(xmldict):
"""
Converts a dictionary to an XML ElementTree Element
"""
roottag = xmldict.keys()[0]
root = ElementTree.Element(roottag)
_ConvertDictToXmlRecurse(root, xmldict[roottag])
return root
def _ConvertXmlToDictRecurse(node, dictclass):
nodedict = dictclass()
if len(node.items()) > 0:
# if we have attributes, set them
nodedict.update(dict(node.items()))
for child in node:
# recursively add the element's children
newitem = _ConvertXmlToDictRecurse(child, dictclass)
if nodedict.has_key(child.tag):
# found duplicate tag, force a list
if type(nodedict[child.tag]) is type([]):
# append to existing list
nodedict[child.tag].append(newitem)
else:
# convert to list
nodedict[child.tag] = [nodedict[child.tag], newitem]
else:
# only one, directly set the dictionary
nodedict[child.tag] = newitem
if node.text is None:
text = ''
else:
text = node.text.strip()
if len(nodedict) > 0:
# if we have a dictionary add the text as a dictionary value (if there is any)
if len(text) > 0:
nodedict['_text'] = text
else:
# if we don't have child nodes or attributes, just set the text
nodedict = text
return nodedict
def ConvertXmlToDict(root, dictclass=XmlDictObject):
"""
Converts an XML String to a dictionary
"""
if isinstance(root, basestring):
root = ElementTree.fromstring(root)
elif not isinstance(root, ElementTree.Element):
raise TypeError, 'Expected string'
return dictclass({root.tag: _ConvertXmlToDictRecurse(root, dictclass)})

View file

@ -5,8 +5,8 @@
# repository:http://github.com/dbr/tvdb_api
# license:unlicense (http://unlicense.org/)
from functools import wraps
import traceback
from functools import wraps
__author__ = 'dbr/Ben'
__version__ = '1.9'
@ -21,12 +21,6 @@ import logging
import zipfile
import requests
import requests.exceptions
import xmltodict
try:
import xml.etree.cElementTree as ElementTree
except ImportError:
import xml.etree.ElementTree as ElementTree
try:
import gzip
@ -36,6 +30,7 @@ except ImportError:
from lib.dateutil.parser import parse
from lib.cachecontrol import CacheControl, caches
from lib.etreetodict import ConvertXmlToDict
from tvdb_ui import BaseUI, ConsoleUI
from tvdb_exceptions import (tvdb_error, tvdb_shownotfound,
tvdb_seasonnotfound, tvdb_episodenotfound, tvdb_attributenotfound)
@ -565,18 +560,17 @@ class Tvdb:
except Exception:
raise tvdb_error('Unknown exception while loading URL %s: %s' % (url, traceback.format_exc()))
def process(path, key, value):
key = key.lower()
# clean up value and do type changes
if value:
if 'firstaired' == key:
try:
value = parse(value, fuzzy=True).strftime('%Y-%m-%d')
except:
value = None
return key, value
def process_data(data):
te = ConvertXmlToDict(data)
if isinstance(te, dict) and 'Data' in te and isinstance(te['Data'], dict) and \
'Series' in te['Data'] and isinstance(te['Data']['Series'], dict) and \
'FirstAired' in te['Data']['Series']:
try:
value = parse(te['Data']['Series']['FirstAired'], fuzzy=True).strftime('%Y-%m-%d')
except:
value = None
te['Data']['Series']['firstaired'] = value
return te
if resp.ok:
if 'application/zip' in resp.headers.get('Content-Type', ''):
@ -586,12 +580,12 @@ class Tvdb:
zipdata = StringIO.StringIO()
zipdata.write(resp.content)
myzipfile = zipfile.ZipFile(zipdata)
return xmltodict.parse(myzipfile.read('%s.xml' % language), postprocessor=process)
return process_data(myzipfile.read('%s.xml' % language))
except zipfile.BadZipfile:
raise tvdb_error('Bad zip file received from thetvdb.com, could not read it')
else:
try:
return xmltodict.parse(resp.content.strip(), postprocessor=process)
return process_data(resp.content.strip())
except:
return dict([(u'data', None)])
@ -641,7 +635,7 @@ class Tvdb:
- Replaces &amp; with &
- Trailing whitespace
"""
return data if data is None else data.strip().replace(u'&amp;', u'&')
return data if not isinstance(data, basestring) else data.strip().replace(u'&amp;', u'&')
def search(self, series):
"""This searches TheTVDB.com for the series name
@ -654,6 +648,7 @@ class Tvdb:
try:
series_found = self._getetsrc(self.config['url_get_series'], self.config['params_get_series'])
if series_found:
series_found['Series'] = [{k.lower(): v for k, v in s.iteritems()} for s in series_found['Series']]
return series_found.values()[0]
except:
pass
@ -804,21 +799,21 @@ class Tvdb:
# Parse show information
log().debug('Getting all series data for %s' % sid)
url = self.config['url_epInfo%s' % ('', '_zip')[self.config['useZip']]] % (sid, language)
url = (self.config['url_seriesInfo'] % (sid, language), self.config['url_epInfo%s' % ('', '_zip')[self.config['useZip']]] % (sid, language))[get_ep_info]
show_data = self._getetsrc(url, language=get_show_in_language)
# check and make sure we have data to process and that it contains a series name
if not len(show_data) or (isinstance(show_data, dict) and 'seriesname' not in show_data['series']):
if not len(show_data) or (isinstance(show_data, dict) and 'SeriesName' not in show_data['Series']):
return False
for k, v in show_data['series'].items():
for k, v in show_data['Series'].iteritems():
if None is not v:
if k in ['banner', 'fanart', 'poster']:
v = self.config['url_artworkPrefix'] % v
else:
v = self._clean_data(v)
self._set_show_data(sid, k, v)
self._set_show_data(sid, k.lower(), v)
if get_ep_info:
# Parse banners
@ -832,24 +827,24 @@ class Tvdb:
# Parse episode data
log().debug('Getting all episodes of %s' % sid)
if 'episode' not in show_data:
if 'Episode' not in show_data:
return False
episodes = show_data['episode']
episodes = show_data['Episode']
if not isinstance(episodes, list):
episodes = [episodes]
for cur_ep in episodes:
if self.config['dvdorder']:
log().debug('Using DVD ordering.')
use_dvd = None is not cur_ep['dvd_season'] and None is not cur_ep['dvd_episodenumber']
use_dvd = None is not cur_ep['DVD_season'] and None is not cur_ep['DVD_episodenumber']
else:
use_dvd = False
if use_dvd:
elem_seasnum, elem_epno = cur_ep['dvd_season'], cur_ep['dvd_episodenumber']
elem_seasnum, elem_epno = cur_ep['DVD_season'], cur_ep['DVD_episodenumber']
else:
elem_seasnum, elem_epno = cur_ep['seasonnumber'], cur_ep['episodenumber']
elem_seasnum, elem_epno = cur_ep['SeasonNumber'], cur_ep['EpisodeNumber']
if None is elem_seasnum or None is elem_epno:
log().warning('An episode has incomplete season/episode number (season: %r, episode: %r)' % (
@ -895,10 +890,16 @@ class Tvdb:
"""Handles tvdb_instance['seriesname'] calls.
The dict index should be the show id
"""
arg = None
if isinstance(key, tuple) and 2 == len(key):
key, arg = key
if not isinstance(arg, bool):
arg = None
if isinstance(key, (int, long)):
# Item is integer, treat as show id
if key not in self.shows:
self._get_show_data(key, self.config['language'], True)
self._get_show_data(key, self.config['language'], (True, arg)[arg is not None])
return None if key not in self.shows else self.shows[key]
key = str(key).lower()

View file

@ -657,10 +657,16 @@ class TVRage:
"""Handles tvrage_instance['seriesname'] calls.
The dict index should be the show id
"""
arg = None
if isinstance(key, tuple) and 2 == len(key):
key, arg = key
if not isinstance(arg, bool):
arg = None
if isinstance(key, (int, long)):
# Item is integer, treat as show id
if key not in self.shows:
self._getShowData(key, True)
self._getShowData(key, (True, arg)[arg is not None])
return None if key not in self.shows else self.shows[key]
key = key.lower()

View file

@ -89,7 +89,6 @@ background_mapping_task = None
showList = None
UPDATE_SHOWS_ON_START = False
SHOW_UPDATE_HOUR = 3
ALLOW_INCOMPLETE_SHOWDATA = False
providerList = []
newznabProviderList = []
@ -514,7 +513,7 @@ def initialize(consoleLogging=True):
PLEX_UPDATE_LIBRARY, PLEX_SERVER_HOST, PLEX_HOST, PLEX_USERNAME, PLEX_PASSWORD, \
USE_TRAKT, TRAKT_CONNECTED_ACCOUNT, TRAKT_ACCOUNTS, TRAKT_MRU, TRAKT_VERIFY, TRAKT_REMOVE_WATCHLIST, TRAKT_TIMEOUT, TRAKT_USE_WATCHLIST, TRAKT_METHOD_ADD, TRAKT_START_PAUSED, traktCheckerScheduler, TRAKT_SYNC, TRAKT_DEFAULT_INDEXER, TRAKT_REMOVE_SERIESLIST, TRAKT_UPDATE_COLLECTION, \
BACKLOG_FREQUENCY, DEFAULT_BACKLOG_FREQUENCY, MIN_BACKLOG_FREQUENCY, MAX_BACKLOG_FREQUENCY, BACKLOG_STARTUP, SKIP_REMOVED_FILES, \
showUpdateScheduler, __INITIALIZED__, LAUNCH_BROWSER, TRASH_REMOVE_SHOW, TRASH_ROTATE_LOGS, HOME_SEARCH_FOCUS, SORT_ARTICLE, showList, loadingShowList, UPDATE_SHOWS_ON_START, SHOW_UPDATE_HOUR, ALLOW_INCOMPLETE_SHOWDATA, \
showUpdateScheduler, __INITIALIZED__, LAUNCH_BROWSER, TRASH_REMOVE_SHOW, TRASH_ROTATE_LOGS, HOME_SEARCH_FOCUS, SORT_ARTICLE, showList, loadingShowList, UPDATE_SHOWS_ON_START, SHOW_UPDATE_HOUR, \
NEWZNAB_DATA, INDEXER_DEFAULT, INDEXER_TIMEOUT, USENET_RETENTION, TORRENT_DIR, \
QUALITY_DEFAULT, FLATTEN_FOLDERS_DEFAULT, SUBTITLES_DEFAULT, STATUS_DEFAULT, WANTED_BEGIN_DEFAULT, WANTED_LATEST_DEFAULT, RECENTSEARCH_STARTUP, \
GROWL_NOTIFY_ONSNATCH, GROWL_NOTIFY_ONDOWNLOAD, GROWL_NOTIFY_ONSUBTITLEDOWNLOAD, TWITTER_NOTIFY_ONSNATCH, TWITTER_NOTIFY_ONDOWNLOAD, TWITTER_NOTIFY_ONSUBTITLEDOWNLOAD, \
@ -666,7 +665,6 @@ def initialize(consoleLogging=True):
UPDATE_SHOWS_ON_START = bool(check_setting_int(CFG, 'General', 'update_shows_on_start', 0))
SHOW_UPDATE_HOUR = check_setting_int(CFG, 'General', 'show_update_hour', 3)
SHOW_UPDATE_HOUR = minimax(SHOW_UPDATE_HOUR, 3, 0, 23)
ALLOW_INCOMPLETE_SHOWDATA = bool(check_setting_int(CFG, 'General', 'allow_incomplete_showdata', 0))
TRASH_REMOVE_SHOW = bool(check_setting_int(CFG, 'General', 'trash_remove_show', 0))
TRASH_ROTATE_LOGS = bool(check_setting_int(CFG, 'General', 'trash_rotate_logs', 0))
@ -1524,7 +1522,6 @@ def save_config():
new_config['General']['launch_browser'] = int(LAUNCH_BROWSER)
new_config['General']['update_shows_on_start'] = int(UPDATE_SHOWS_ON_START)
new_config['General']['show_update_hour'] = int(SHOW_UPDATE_HOUR)
new_config['General']['allow_incomplete_showdata'] = int(ALLOW_INCOMPLETE_SHOWDATA)
new_config['General']['trash_remove_show'] = int(TRASH_REMOVE_SHOW)
new_config['General']['trash_rotate_logs'] = int(TRASH_ROTATE_LOGS)
new_config['General']['home_search_focus'] = int(HOME_SEARCH_FOCUS)

View file

@ -296,7 +296,7 @@ class QueueItemAdd(ShowQueueItem):
logger.log(u'' + str(sickbeard.indexerApi(self.indexer).name) + ': ' + repr(lINDEXER_API_PARMS))
t = sickbeard.indexerApi(self.indexer).indexer(**lINDEXER_API_PARMS)
s = t[self.indexer_id]
s = t[self.indexer_id, False]
# this usually only happens if they have an NFO in their show dir which gave us a Indexer ID that has no proper english version of the show
if getattr(s, 'seriesname', None) is None:
@ -307,14 +307,6 @@ class QueueItemAdd(ShowQueueItem):
(self.showDir, sickbeard.indexerApi(self.indexer).name))
self._finishEarly()
return
# if the show has no episodes/seasons
if not sickbeard.ALLOW_INCOMPLETE_SHOWDATA and not s:
msg = 'Show %s is on %s but contains no season/episode data. Only the show folder was created.'\
% (s['seriesname'], sickbeard.indexerApi(self.indexer).name)
logger.log(msg, logger.ERROR)
ui.notifications.error('Unable to add show', msg)
self._finishEarly()
return
except Exception as e:
logger.log('Unable to find show ID:%s on Indexer: %s' % (self.indexer_id, sickbeard.indexerApi(self.indexer).name),
logger.ERROR)

View file

@ -559,6 +559,8 @@ class TVShow(object):
if self.dvdorder != 0:
lINDEXER_API_PARMS['dvdorder'] = True
logger.log('%s: Loading all episodes from %s..' % (self.indexerid, sickbeard.indexerApi(self.indexer).name))
try:
t = sickbeard.indexerApi(self.indexer).indexer(**lINDEXER_API_PARMS)
showObj = t[self.indexerid]
@ -567,8 +569,6 @@ class TVShow(object):
(sickbeard.indexerApi(self.indexer).name, sickbeard.indexerApi(self.indexer).name), logger.ERROR)
return None
logger.log('%s: Loading all episodes from %s..' % (self.indexerid, sickbeard.indexerApi(self.indexer).name))
scannedEps = {}
sql_l = []
@ -912,7 +912,7 @@ class TVShow(object):
else:
t = tvapi
myEp = t[self.indexerid]
myEp = t[self.indexerid, False]
if None is myEp:
logger.log('Show not found (maybe even removed?)', logger.WARNING)
return False
@ -931,7 +931,7 @@ class TVShow(object):
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"]
self.airs = ('%s %s' % (myEp['airs_dayofweek'], myEp['airs_time'])).strip()
if getattr(myEp, 'firstaired', None) is not None:
self.startyear = int(str(myEp["firstaired"]).split('-')[0])
@ -1076,15 +1076,15 @@ class TVShow(object):
for path, dirs, files in ek.ek(os.walk, image_cache_dir):
for filename in ek.ek(fnmatch.filter, files, '%s.*' % self.indexerid):
cache_file = ek.ek(os.path.join, path, filename)
logger.log('Attempt to %s cache file %s' % (action, cache_file))
try:
if sickbeard.TRASH_REMOVE_SHOW:
send2trash(cache_file)
else:
os.remove(cache_file)
logger.log('Attempt to %s cache file %s' % (action, cache_file))
try:
if sickbeard.TRASH_REMOVE_SHOW:
send2trash(cache_file)
else:
os.remove(cache_file)
except OSError as e:
logger.log('Unable to %s %s: %s / %s' % (action, cache_file, repr(e), str(e)), logger.WARNING)
except OSError as e:
logger.log('Unable to %s %s: %s / %s' % (action, cache_file, repr(e), str(e)), logger.WARNING)
# remove entire show folder
if full:
@ -1771,15 +1771,7 @@ class TVEpisode(object):
self.deleteEpisode()
return
if not sickbeard.ALLOW_INCOMPLETE_SHOWDATA and None is getattr(myEp, 'episodename', None):
logger.log('This episode (%s - %sx%s) has no name on %s' %
(self.show.name, season, episode, sickbeard.indexerApi(self.indexer).name))
# if I'm incomplete on TVDB but I once was complete then just delete myself from the DB for now
if -1 != self.indexerid:
self.deleteEpisode()
return False
if None is getattr(myEp, 'absolute_number', None):
if getattr(myEp, 'absolute_number', None) in (None, ''):
logger.log('This episode (%s - %sx%s) has no absolute number on %s' %
(self.show.name, season, episode, sickbeard.indexerApi(self.indexer).name), logger.DEBUG)
else:
@ -1827,8 +1819,7 @@ class TVEpisode(object):
self.indexerid = getattr(myEp, 'id', None)
if None is self.indexerid:
logger.log('Failed to retrieve ID from %s' % sickbeard.indexerApi(self.indexer).name, logger.ERROR)
if -1 != self.indexerid:
self.deleteEpisode()
self.deleteEpisode()
return False
# don't update show status if show dir is missing, unless it's missing on purpose
@ -2060,42 +2051,26 @@ class TVEpisode(object):
logger.log('%s: Not creating SQL queue - record is not dirty' % self.show.indexerid, logger.DEBUG)
return
myDB = db.DBConnection()
rows = myDB.select(
'SELECT episode_id FROM tv_episodes WHERE showid = ? AND indexer=? AND season = ? AND episode = ?',
[self.show.indexerid, self.show.indexer, self.season, self.episode])
epID = None
if rows:
epID = int(rows[0]['episode_id'])
self.dirty = False
if epID:
# use a custom update method to get the data into the DB for existing records.
return [
'UPDATE tv_episodes SET indexerid = ?, indexer = ?, name = ?, description = ?, subtitles = ?, '
'subtitles_searchcount = ?, subtitles_lastsearch = ?, airdate = ?, hasnfo = ?, hastbn = ?, status = ?, '
'location = ?, file_size = ?, release_name = ?, is_proper = ?, showid = ?, season = ?, episode = ?, '
'absolute_number = ?, version = ?, release_group = ? WHERE episode_id = ?',
[self.indexerid, self.indexer, self.name, self.description, ",".join([sub for sub in self.subtitles]),
self.subtitles_searchcount, self.subtitles_lastsearch, self.airdate.toordinal(), self.hasnfo,
self.hastbn,
self.status, self.location, self.file_size, self.release_name, self.is_proper, self.show.indexerid,
self.season, self.episode, self.absolute_number, self.version, self.release_group, epID]]
else:
# use a custom insert method to get the data into the DB.
return [
'INSERT OR IGNORE INTO tv_episodes (episode_id, indexerid, indexer, name, description, subtitles, '
'subtitles_searchcount, subtitles_lastsearch, airdate, hasnfo, hastbn, status, location, file_size, '
'release_name, is_proper, showid, season, episode, absolute_number, version, release_group) VALUES '
'((SELECT episode_id FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?)'
',?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);',
[self.show.indexerid, self.season, self.episode, self.indexerid, self.indexer, self.name,
self.description,
",".join([sub for sub in self.subtitles]), self.subtitles_searchcount, self.subtitles_lastsearch,
self.airdate.toordinal(), self.hasnfo, self.hastbn, self.status, self.location, self.file_size,
self.release_name, self.is_proper, self.show.indexerid, self.season, self.episode,
self.absolute_number, self.version, self.release_group]]
return [
'INSERT OR REPLACE INTO tv_episodes (episode_id, indexerid, indexer, name, description, subtitles, '
'subtitles_searchcount, subtitles_lastsearch, airdate, hasnfo, hastbn, status, location, file_size, '
'release_name, is_proper, showid, season, episode, absolute_number, version, release_group, '
'scene_absolute_number, scene_season, scene_episode) VALUES '
'((SELECT episode_id FROM tv_episodes WHERE indexer = ? AND showid = ? AND season = ? AND episode = ?)'
',?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,'
'(SELECT scene_absolute_number FROM tv_episodes WHERE indexer = ? AND showid = ? AND season = ? AND episode = ?),'
'(SELECT scene_season FROM tv_episodes WHERE indexer = ? AND showid = ? AND season = ? AND episode = ?),'
'(SELECT scene_episode FROM tv_episodes WHERE indexer = ? AND showid = ? AND season = ? AND episode = ?));',
[self.show.indexer, self.show.indexerid, self.season, self.episode, self.indexerid, self.indexer, self.name,
self.description,
",".join([sub for sub in self.subtitles]), self.subtitles_searchcount, self.subtitles_lastsearch,
self.airdate.toordinal(), self.hasnfo, self.hastbn, self.status, self.location, self.file_size,
self.release_name, self.is_proper, self.show.indexerid, self.season, self.episode,
self.absolute_number, self.version, self.release_group,
self.show.indexer, self.show.indexerid, self.season, self.episode,
self.show.indexer, self.show.indexerid, self.season, self.episode,
self.show.indexer, self.show.indexerid, self.season, self.episode]]
def saveToDB(self, forceSave=False):
"""

View file

@ -2368,11 +2368,11 @@ class NewHomeAddShows(Home):
logger.log('Fetching show using id: %s (%s) from tv datasource %s' % (
search_id, search_term, sickbeard.indexerApi(indexer).name), logger.DEBUG)
results.setdefault('tt' in search_id and 3 or indexer, []).extend(
[{'id': indexer_id, 'seriesname': t[indexer_id]['seriesname'],
'firstaired': t[indexer_id]['firstaired'], 'network': t[indexer_id]['network'],
'overview': t[indexer_id]['overview'],
'genres': '' if not t[indexer_id]['genre'] else
t[indexer_id]['genre'].lower().strip('|').replace('|', ', '),
[{'id': indexer_id, 'seriesname': t[indexer_id, False]['seriesname'],
'firstaired': t[indexer_id, False]['firstaired'], 'network': t[indexer_id, False]['network'],
'overview': t[indexer_id, False]['overview'],
'genres': '' if not t[indexer_id, False]['genre'] else
t[indexer_id, False]['genre'].lower().strip('|').replace('|', ', '),
}])
break
else:
@ -4305,7 +4305,7 @@ class ConfigGeneral(Config):
return m.hexdigest()
def saveGeneral(self, log_dir=None, web_port=None, web_log=None, encryption_version=None, web_ipv6=None,
update_shows_on_start=None, show_update_hour=None, allow_incomplete_showdata=None,
update_shows_on_start=None, show_update_hour=None,
trash_remove_show=None, trash_rotate_logs=None, update_frequency=None, launch_browser=None, web_username=None,
use_api=None, api_key=None, indexer_default=None, timezone_display=None, cpu_preset=None, file_logging_preset=None,
web_password=None, version_notify=None, enable_https=None, https_cert=None, https_key=None,
@ -4327,7 +4327,6 @@ class ConfigGeneral(Config):
sickbeard.UPDATE_SHOWS_ON_START = config.checkbox_to_value(update_shows_on_start)
sickbeard.SHOW_UPDATE_HOUR = config.minimax(show_update_hour, 3, 0, 23)
sickbeard.ALLOW_INCOMPLETE_SHOWDATA = config.checkbox_to_value(allow_incomplete_showdata)
sickbeard.TRASH_REMOVE_SHOW = config.checkbox_to_value(trash_remove_show)
sickbeard.TRASH_ROTATE_LOGS = config.checkbox_to_value(trash_rotate_logs)
config.change_UPDATE_FREQUENCY(update_frequency)