Add log message for not found on indexer when adding a new show.

Fix upgrade once ARCHIVED setting by postProcessor.
Fix determination of is_first_best_match.
Change improve smart selection of categories in manual and failed search modes.
Change refactor wantedQuality into own function that can be used in multiple places.
Change improve error resistance in neededQualities class.
Add log warning message if wantedQuality or eps_aired_in_season is missing for search.
Add check backlogitem for wantedQuality and add if missing.
Add use wantedQuality list in wantEpisode.
Change don't use wantedQualities for multipart.
This commit is contained in:
Prinz23 2017-12-27 03:14:20 +00:00 committed by JackDandy
parent 8d97f2664a
commit 6da32a5ed0
15 changed files with 270 additions and 131 deletions

View file

@ -1,10 +1,11 @@
### 0.14.0 (2018-xx-xx xx:xx:xx UTC)
### 0.13.7 (2017-12-27 03:00:00 UTC)
* Add log message for not found on indexer when adding a new show
* Fix upgrade once ARCHIVED setting by postProcessor
* Fix determination of is_first_best_match
* Fix BTScene and Lime
* Add ETTV torrent provider
* Add PotUK torrent provider
* Fix BTScene and Lime
[develop changelog]
### 0.13.6 (2017-12-13 01:50:00 UTC)

View file

@ -340,7 +340,7 @@
#if $show.paused
<span class="label label-paused">Paused</span>
#end if
#if ($anyQualities + $bestQualities) and int($show.archive_firstmatch)
#if ($anyQualities + $bestQualities) and int($show.upgrade_once)
<span class="label">Upgrade once</span>
#end if
#if $show.exceptions

View file

@ -152,10 +152,10 @@
#if $anyQualities + $bestQualities
<div class="field-pair show-if-quality-custom" style="display:none">
<label for="archive_firstmatch">
<label for="upgrade-once">
<span class="component-title">Upgrade once</span>
<span class="component-desc">
<input type="checkbox" name="archive_firstmatch" id="archive_firstmatch"#echo ('', $html_checked)[$show.archive_firstmatch]#>
<input type="checkbox" name="upgrade_once" id="upgrade-once"#echo ('', $html_checked)[$show.upgrade_once]#>
<p>stop upgrading after matching the first best <em>Upgrade to</em> quality</p>
</span>
</label>

View file

@ -88,11 +88,11 @@
#set $isSelected = ' selected="selected"'
#set $isEnabled = $isSelected
#set $isDisabled = $isSelected
#if $archive_firstmatch_value##set $isDisabled = ''##else##set $isEnabled = ''##end if#
#if $upgrade_once_value##set $isDisabled = ''##else##set $isEnabled = ''##end if#
<div class="optionWrapper clearfix">
<span class="selectTitle">Upgrade once</span>
<div class="selectChoices">
<select id="edit_archive_firstmatch" name="archive_firstmatch" class="form-control form-control-inline input-sm">
<select id="edit_upgrade-once" name="upgrade_once" class="form-control form-control-inline input-sm">
<option value="keep">&lt; keep &gt;</option>
<option value="enable"${isEnabled}>enable</option>
<option value="disable"${isDisabled}>disable</option>

View file

@ -507,6 +507,8 @@ IGNORE_WORDS = 'core2hd, hevc, MrLss, reenc, x265, danish, deutsch, dutch, flemi
'german, italian, nordic, norwegian, portuguese, spanish, swedish, turkish'
REQUIRE_WORDS = ''
WANTEDLIST_CACHE = None
CALENDAR_UNPROTECTED = False
TMDB_API_KEY = 'edc5f123313769de83a71e157758030b'
@ -555,7 +557,7 @@ def initialize(console_logging=True):
global __INITIALIZED__, showList, providerList, newznabProviderList, torrentRssProviderList, \
WEB_HOST, WEB_ROOT, ACTUAL_CACHE_DIR, CACHE_DIR, ZONEINFO_DIR, ADD_SHOWS_WO_DIR, CREATE_MISSING_SHOW_DIRS, \
RECENTSEARCH_STARTUP, NAMING_FORCE_FOLDERS, SOCKET_TIMEOUT, DEBUG, INDEXER_DEFAULT, CONFIG_FILE, \
REMOVE_FILENAME_CHARS, IMPORT_DEFAULT_CHECKED_SHOWS
REMOVE_FILENAME_CHARS, IMPORT_DEFAULT_CHECKED_SHOWS, WANTEDLIST_CACHE
# Schedulers
# global traktCheckerScheduler
global recentSearchScheduler, backlogSearchScheduler, showUpdateScheduler, \
@ -677,6 +679,8 @@ def initialize(console_logging=True):
update_config = False
WANTEDLIST_CACHE = common.wantedQualities()
# wanted branch
BRANCH = check_setting_str(CFG, 'General', 'branch', '')

View file

@ -377,6 +377,62 @@ class Quality:
FAILED = None
class wantedQualities(dict):
wantedlist = 1
bothlists = 2
upgradelist = 3
def __init__(self, **kwargs):
super(wantedQualities, self).__init__(**kwargs)
def _generate_wantedlist(self, qualities):
initial_qualities, upgrade_qualities = Quality.splitQuality(qualities)
max_initial_quality = max(initial_qualities or [Quality.NONE])
self[qualities] = {0: {self.bothlists: False, self.wantedlist: initial_qualities, self.upgradelist: False}}
for q in Quality.qualityStrings:
if 0 >= q:
continue
if q not in upgrade_qualities and q in initial_qualities:
# quality is only in initial_qualities
self[qualities][q] = {self.bothlists: False,
self.wantedlist: [i for i in upgrade_qualities if q < i], self.upgradelist: False}
elif q in upgrade_qualities and q in initial_qualities:
# quality is in initial_qualities and upgrade_qualities
self[qualities][q] = {self.bothlists: True,
self.wantedlist: [i for i in upgrade_qualities if q < i], self.upgradelist: True}
elif q in upgrade_qualities:
# quality is only in upgrade_qualities
self[qualities][q] = {self.bothlists: False,
self.wantedlist: [i for i in upgrade_qualities if q < i], self.upgradelist: True}
else:
# quality is not in any selected quality for the show
only_upgrade = q >= max_initial_quality
self[qualities][q] = {self.bothlists: False,
self.wantedlist:
[i for i in (initial_qualities, upgrade_qualities)[only_upgrade] if q < i],
self.upgradelist: only_upgrade}
def __getitem__(self, k):
if k not in self:
self._generate_wantedlist(k)
return super(wantedQualities, self).__getitem__(k)
def get(self, k, *args, **kwargs):
if k not in self:
self._generate_wantedlist(k)
return super(wantedQualities, self).get(k, *args, **kwargs)
def get_wantedlist(self, qualities, upgradeonce, quality, status, unaired=False, manual=False):
if not manual and status in [ARCHIVED, IGNORED, SKIPPED] + ([UNAIRED], [])[unaired]:
return []
if upgradeonce:
if status == SNATCHED_BEST or \
(not self[qualities][quality][self.bothlists] and self[qualities][quality][self.upgradelist] and
status in (DOWNLOADED, SNATCHED, SNATCHED_BEST, SNATCHED_PROPER)):
return []
return self[qualities][quality][self.wantedlist]
Quality.SNATCHED = [Quality.compositeStatus(SNATCHED, x) for x in Quality.qualityStrings.keys()]
Quality.SNATCHED_PROPER = [Quality.compositeStatus(SNATCHED_PROPER, x) for x in Quality.qualityStrings.keys()]
Quality.SNATCHED_BEST = [Quality.compositeStatus(SNATCHED_BEST, x) for x in Quality.qualityStrings.keys()]

View file

@ -939,12 +939,17 @@ class PostProcessor(object):
cur_ep.release_name = self.release_name or ''
any_qualities, best_qualities = common.Quality.splitQuality(cur_ep.show.quality)
cur_status, cur_quality = common.Quality.splitCompositeStatus(cur_ep.status)
cur_ep.status = common.Quality.compositeStatus(
**({'status': common.DOWNLOADED, 'quality': new_ep_quality},
{'status': common.ARCHIVED, 'quality': new_ep_quality})
[ep_obj.status in common.Quality.SNATCHED_BEST or
(cur_ep.show.archive_firstmatch and new_ep_quality in best_qualities)])
(cur_ep.show.upgrade_once and
(new_ep_quality in best_qualities and
(new_ep_quality not in any_qualities or (cur_status in
(common.SNATCHED, common.SNATCHED_BEST, common.SNATCHED_PROPER, common.DOWNLOADED) and
cur_quality != new_ep_quality))))])
cur_ep.release_group = self.release_group or ''

View file

@ -64,6 +64,9 @@ class BTSceneProvider(generic.TorrentProvider):
url = self.url
response = self.get_url(url)
if not response:
return results
form = re.findall('(?is)(<form[^>]+)', response)
response = any(form) and form[0] or response
action = re.findall('<form[^>]+action=[\'"]([^\'"]*)', response)[0]

View file

@ -35,6 +35,7 @@ from io import BytesIO
from lib.dateutil import parser
from sickbeard.network_timezones import sb_timezone
from sickbeard.helpers import tryInt
from sickbeard.search import get_wanted_qualities, get_aired_in_season
try:
from lxml import etree
@ -473,18 +474,30 @@ class NewznabProvider(generic.NZBProvider):
return show_obj
def choose_search_mode(self, episodes, ep_obj, hits_per_page=100):
if not hasattr(ep_obj, 'eps_aired_in_season'):
return None, neededQualities(need_all_qualities=True), hits_per_page
searches = [e for e in episodes if (not ep_obj.show.is_scene and e.season == ep_obj.season) or
(ep_obj.show.is_scene and e.scene_season == ep_obj.scene_season)]
needed = neededQualities()
needed.check_needed_types(ep_obj.show)
for s in searches:
if needed.all_qualities_needed:
break
if not s.show.is_anime and not s.show.is_sports:
if not getattr(s, 'wantedQuality', None):
# this should not happen, the creation is missing for the search in this case
logger.log('wantedQuality property was missing for search, creating it', logger.WARNING)
ep_status, ep_quality = Quality.splitCompositeStatus(ep_obj.status)
s.wantedQuality = get_wanted_qualities(ep_obj, ep_status, ep_quality, unaired=True)
needed.check_needed_qualities(s.wantedQuality)
if not hasattr(ep_obj, 'eps_aired_in_season'):
# this should not happen, the creation is missing for the search in this case
logger.log('eps_aired_in_season property was missing for search, creating it', logger.WARNING)
ep_count, ep_count_scene = get_aired_in_season(ep_obj.show)
ep_obj.eps_aired_in_season = ep_count.get(ep_obj.season, 0)
ep_obj.eps_aired_in_scene_season = ep_count_scene.get(ep_obj.scene_season, 0) if ep_obj.show.is_scene else \
ep_obj.eps_aired_in_season
per_ep, limit_per_ep = 0, 0
if needed.need_sd and not needed.need_hd:
per_ep, limit_per_ep = 10, 25
@ -500,15 +513,26 @@ class NewznabProvider(generic.NZBProvider):
rel_per_ep, limit_per_ep = 5, 10
else:
rel_per_ep = per_ep
rel = int(ceil((ep_obj.eps_aired_in_scene_season if ep_obj.show.is_scene else
ep_obj.eps_aired_in_season * rel_per_ep) / hits_per_page))
rel_limit = int(ceil((ep_obj.eps_aired_in_scene_season if ep_obj.show.is_scene else
ep_obj.eps_aired_in_season * limit_per_ep) / hits_per_page))
rel = max(1, int(ceil((ep_obj.eps_aired_in_scene_season if ep_obj.show.is_scene else
ep_obj.eps_aired_in_season * rel_per_ep) / hits_per_page)))
rel_limit = max(1, int(ceil((ep_obj.eps_aired_in_scene_season if ep_obj.show.is_scene else
ep_obj.eps_aired_in_season * limit_per_ep) / hits_per_page)))
season_search = rel < (len(searches) * 100 // hits_per_page)
if not season_search:
needed = neededQualities()
needed.check_needed_types(ep_obj.show)
if not ep_obj.show.is_anime and not ep_obj.show.is_sports:
if not getattr(ep_obj, 'wantedQuality', None):
ep_status, ep_quality = Quality.splitCompositeStatus(ep_obj.status)
ep_obj.wantedQuality = get_wanted_qualities(ep_obj, ep_status, ep_quality, unaired=True)
needed.check_needed_qualities(ep_obj.wantedQuality)
else:
if not ep_obj.show.is_anime and not ep_obj.show.is_sports:
for ep in episodes:
if not getattr(ep, 'wantedQuality', None):
ep_status, ep_quality = Quality.splitCompositeStatus(ep.status)
ep.wantedQuality = get_wanted_qualities(ep, ep_status, ep_quality, unaired=True)
needed.check_needed_qualities(ep.wantedQuality)
return (season_search, needed,
(hits_per_page * 100 // hits_per_page * 2, hits_per_page * int(ceil(rel_limit * 1.5)))[season_search])

View file

@ -26,7 +26,7 @@ import traceback
import sickbeard
from common import SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, Quality, SEASON_RESULT, MULTI_EP_RESULT
from common import SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, DOWNLOADED, Quality, SEASON_RESULT, MULTI_EP_RESULT
from sickbeard import logger, db, show_name_helpers, exceptions, helpers
from sickbeard import sab
@ -41,6 +41,7 @@ from sickbeard import failed_history
from sickbeard.exceptions import ex
from sickbeard.providers.generic import GenericProvider
from sickbeard import common
from sickbeard.tv import TVEpisode
def _download_result(result):
@ -170,7 +171,7 @@ def snatch_episode(result, end_status=SNATCHED):
update_imdb_data = True
for cur_ep_obj in result.episodes:
with cur_ep_obj.lock:
if is_first_best_match(result):
if is_first_best_match(cur_ep_obj.status, result):
cur_ep_obj.status = Quality.compositeStatus(SNATCHED_BEST, result.quality)
else:
cur_ep_obj.status = Quality.compositeStatus(end_status, result.quality)
@ -294,7 +295,7 @@ def is_final_result(result):
return False
def is_first_best_match(result):
def is_first_best_match(ep_status, result):
"""
Checks if the given result is a best quality match and if we want to archive the episode on first match.
"""
@ -303,21 +304,41 @@ def is_first_best_match(result):
result.name, logger.DEBUG)
show_obj = result.episodes[0].show
cur_status, cur_quality = Quality.splitCompositeStatus(ep_status)
any_qualities, best_qualities = Quality.splitQuality(show_obj.quality)
# if there is a redownload that's a match to one of our best qualities and
# we want to archive the episode then we are done
if best_qualities and show_obj.archive_firstmatch and result.quality in best_qualities:
if best_qualities and show_obj.upgrade_once and \
(result.quality in best_qualities and
(cur_status in (SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, DOWNLOADED) or
result.quality not in any_qualities)):
return True
return False
def wanted_episodes(show, from_date, make_dict=False, unaired=False):
initial_qualities, upgrade_qualities = common.Quality.splitQuality(show.quality)
all_qualities = list(set(initial_qualities + upgrade_qualities))
def set_wanted_aired(ep_obj, unaired, ep_count, ep_count_scene, manual=False):
ep_status, ep_quality = common.Quality.splitCompositeStatus(ep_obj.status)
ep_obj.wantedQuality = get_wanted_qualities(ep_obj, ep_status, ep_quality, unaired=unaired, manual=manual)
ep_obj.eps_aired_in_season = ep_count.get(ep_obj.season, 0)
ep_obj.eps_aired_in_scene_season = ep_count_scene.get(
ep_obj.scene_season, 0) if ep_obj.scene_season else ep_obj.eps_aired_in_season
def get_wanted_qualities(ep_obj, cur_status, cur_quality, unaired=False, manual=False):
if isinstance(ep_obj, TVEpisode):
return sickbeard.WANTEDLIST_CACHE.get_wantedlist(ep_obj.show.quality, ep_obj.show.upgrade_once,
cur_quality, cur_status, unaired, manual)
return []
def get_aired_in_season(show, return_sql=False):
ep_count = {}
ep_count_scene = {}
tomorrow = (datetime.date.today() + datetime.timedelta(days=1)).toordinal()
my_db = db.DBConnection()
if show.air_by_date:
@ -331,9 +352,6 @@ def wanted_episodes(show, from_date, make_dict=False, unaired=False):
'WHERE showid = ? AND indexer = ? AND season > 0'
sql_results = my_db.select(sql_string, [show.indexerid, show.indexer])
ep_count = {}
ep_count_scene = {}
tomorrow = (datetime.date.today() + datetime.timedelta(days=1)).toordinal()
for result in sql_results:
if 1 < helpers.tryInt(result['airdate']) <= tomorrow:
cur_season = helpers.tryInt(result['season'])
@ -342,76 +360,57 @@ def wanted_episodes(show, from_date, make_dict=False, unaired=False):
if -1 != cur_scene_season:
ep_count_scene[cur_scene_season] = ep_count.setdefault(cur_scene_season, 0) + 1
if return_sql:
return ep_count, ep_count_scene, sql_results
return ep_count, ep_count_scene
def wanted_episodes(show, from_date, make_dict=False, unaired=False):
ep_count, ep_count_scene, sql_results_org = get_aired_in_season(show, return_sql=True)
from_date_ord = from_date.toordinal()
if unaired:
status_list = [common.WANTED, common.FAILED, common.UNAIRED]
sql_string += ' AND ( airdate > ? OR airdate = 1 )'
sql_results = [s for s in sql_results_org if s['airdate'] > from_date_ord or s['airdate'] == 1]
else:
status_list = [common.WANTED, common.FAILED]
sql_string += ' AND airdate > ?'
sql_results = [s for s in sql_results_org if s['airdate'] > from_date_ord]
sql_results = my_db.select(sql_string, [show.indexerid, show.indexer, from_date.toordinal()])
# check through the list of statuses to see if we want any
if make_dict:
wanted = {}
else:
wanted = []
total_wanted = total_replacing = total_unaired = 0
downloaded_status_list = common.SNATCHED_ANY + [common.DOWNLOADED]
for result in sql_results:
not_downloaded = True
cur_composite_status = int(result['status'])
cur_status, cur_quality = common.Quality.splitCompositeStatus(cur_composite_status)
cur_snatched = cur_status in downloaded_status_list
if show.archive_firstmatch and cur_snatched and cur_quality in upgrade_qualities:
total_wanted = total_replacing = total_unaired = 0
if 0 < len(sql_results) and 2 < len(sql_results) - len(show.episodes):
myDB = db.DBConnection()
show_ep_sql = myDB.select('SELECT * FROM tv_episodes WHERE showid = ? AND indexer = ?',
[show.indexerid, show.indexer])
else:
show_ep_sql = None
for result in sql_results:
ep_obj = show.getEpisode(int(result['season']), int(result['episode']), ep_sql=show_ep_sql)
cur_status, cur_quality = common.Quality.splitCompositeStatus(ep_obj.status)
ep_obj.wantedQuality = get_wanted_qualities(ep_obj, cur_status, cur_quality, unaired=unaired)
if not ep_obj.wantedQuality:
continue
# special case: already downloaded quality is not in any of the upgrade to Qualities
other_quality_downloaded = False
if len(upgrade_qualities) and cur_snatched and cur_quality not in all_qualities:
other_quality_downloaded = True
wanted_qualities = all_qualities
ep_obj.eps_aired_in_season = ep_count.get(helpers.tryInt(result['season']), 0)
ep_obj.eps_aired_in_scene_season = ep_count_scene.get(
helpers.tryInt(result['scene_season']), 0) if result['scene_season'] else ep_obj.eps_aired_in_season
if make_dict:
wanted.setdefault(ep_obj.scene_season if ep_obj.show.is_scene else ep_obj.season, []).append(ep_obj)
else:
wanted_qualities = upgrade_qualities
wanted.append(ep_obj)
if upgrade_qualities:
highest_wanted_quality = max(wanted_qualities)
if cur_status in (common.WANTED, common.FAILED):
total_wanted += 1
elif cur_status in (common.UNAIRED, common.SKIPPED, common.IGNORED, common.UNKNOWN):
total_unaired += 1
else:
if other_quality_downloaded:
highest_wanted_quality = max(initial_qualities)
else:
highest_wanted_quality = 0
# if we need a better one then say yes
if (cur_snatched and cur_quality < highest_wanted_quality) \
or cur_status in status_list \
or (sickbeard.SEARCH_UNAIRED and 1 == result['airdate']
and cur_status in (common.SKIPPED, common.IGNORED, common.UNAIRED, common.UNKNOWN, common.FAILED)):
if cur_status in (common.WANTED, common.FAILED):
total_wanted += 1
elif cur_status in (common.UNAIRED, common.SKIPPED, common.IGNORED, common.UNKNOWN):
total_unaired += 1
else:
total_replacing += 1
not_downloaded = False
ep_obj = show.getEpisode(int(result['season']), int(result['episode']))
ep_obj.wantedQuality = [i for i in (wanted_qualities, initial_qualities)[not_downloaded]
if cur_quality < i]
# in case we don't want any quality for this episode, skip the episode
if 0 == len(ep_obj.wantedQuality):
logger.log('Dropped episode, no wanted quality for %sx%s: [%s]' % (
ep_obj.season, ep_obj.episode, ep_obj.show.name), logger.ERROR)
continue
ep_obj.eps_aired_in_season = ep_count.get(helpers.tryInt(result['season']), 0)
ep_obj.eps_aired_in_scene_season = ep_count_scene.get(
helpers.tryInt(result['scene_season']), 0) if result['scene_season'] else ep_obj.eps_aired_in_season
if make_dict:
wanted.setdefault(ep_obj.scene_season if ep_obj.show.is_scene else ep_obj.season, []).append(ep_obj)
else:
wanted.append(ep_obj)
total_replacing += 1
if 0 < total_wanted + total_replacing + total_unaired:
actions = []

View file

@ -25,8 +25,7 @@ import datetime
import sickbeard
from sickbeard import db, logger, common, exceptions, helpers, network_timezones, generic_queue, search, \
failed_history, history, ui, properFinder
from sickbeard.search import wanted_episodes
from sickbeard.common import Quality
from sickbeard.search import wanted_episodes, get_aired_in_season, set_wanted_aired
search_queue_lock = threading.Lock()
@ -369,6 +368,9 @@ class ManualSearchQueueItem(generic_queue.QueueItem):
logger.log(u'Beginning manual search for: [%s]' % self.segment.prettyName())
self.started = True
ep_count, ep_count_scene = get_aired_in_season(self.show)
set_wanted_aired(self.segment, True, ep_count, ep_count_scene, manual=True)
search_result = search.search_providers(self.show, [self.segment], True, try_other_searches=True)
if search_result:
@ -415,6 +417,11 @@ class BacklogQueueItem(generic_queue.QueueItem):
is_error = False
try:
if not self.standard_backlog:
ep_count, ep_count_scene = get_aired_in_season(self.show)
for ep_obj in self.segment:
set_wanted_aired(ep_obj, True, ep_count, ep_count_scene)
logger.log(u'Beginning backlog search for: [%s]' % self.show.name)
search_result = search.search_providers(
self.show, self.segment, False,
@ -454,12 +461,11 @@ class FailedQueueItem(generic_queue.QueueItem):
self.started = True
try:
ep_count, ep_count_scene = get_aired_in_season(self.show)
for ep_obj in self.segment:
logger.log(u'Marking episode as bad: [%s]' % ep_obj.prettyName())
cur_status = ep_obj.status
failed_history.set_episode_failed(ep_obj)
(release, provider) = failed_history.find_release(ep_obj)
failed_history.revert_episode(ep_obj)
@ -469,8 +475,9 @@ class FailedQueueItem(generic_queue.QueueItem):
logger.log(u'Beginning failed download search for: [%s]' % ep_obj.prettyName())
search_result = search.search_providers(
self.show, self.segment, True, try_other_searches=True, old_status=cur_status)
set_wanted_aired(ep_obj, True, ep_count, ep_count_scene)
search_result = search.search_providers(self.show, self.segment, True, try_other_searches=True)
if search_result:
for result in search_result:

View file

@ -177,10 +177,10 @@ class ShowQueue(generic_queue.GenericQueue):
def addShow(self, indexer, indexer_id, showDir, default_status=None, quality=None, flatten_folders=None,
lang='en', subtitles=None, anime=None, scene=None, paused=None, blacklist=None, whitelist=None,
wanted_begin=None, wanted_latest=None, tag=None, new_show=False):
wanted_begin=None, wanted_latest=None, tag=None, new_show=False, show_name=None):
queueItemObj = QueueItemAdd(indexer, indexer_id, showDir, default_status, quality, flatten_folders, lang,
subtitles, anime, scene, paused, blacklist, whitelist,
wanted_begin, wanted_latest, tag, new_show=new_show)
wanted_begin, wanted_latest, tag, new_show=new_show, show_name=show_name)
self.add_item(queueItemObj)
@ -238,7 +238,7 @@ class ShowQueueItem(generic_queue.QueueItem):
class QueueItemAdd(ShowQueueItem):
def __init__(self, indexer, indexer_id, showDir, default_status, quality, flatten_folders, lang, subtitles, anime,
scene, paused, blacklist, whitelist, default_wanted_begin, default_wanted_latest, tag,
scheduled_update=False, new_show=False):
scheduled_update=False, new_show=False, show_name=None):
self.indexer = indexer
self.indexer_id = indexer_id
@ -257,6 +257,7 @@ class QueueItemAdd(ShowQueueItem):
self.whitelist = whitelist
self.tag = tag
self.new_show = new_show
self.showname = show_name
self.show = None
@ -270,7 +271,9 @@ class QueueItemAdd(ShowQueueItem):
Returns the show name if there is a show object created, if not returns
the dir that the show is being added to.
"""
if self.show == None:
if None is not self.showname:
return self.showname
if None is self.show:
return self.showDir
return self.show.name
@ -304,6 +307,12 @@ class QueueItemAdd(ShowQueueItem):
t = sickbeard.indexerApi(self.indexer).indexer(**lINDEXER_API_PARMS)
s = t[self.indexer_id, False]
if getattr(t, 'show_not_found', False):
logger.log('Show %s was not found on %s, maybe show was deleted' %
(self.show_name, sickbeard.indexerApi(self.indexer).name), logger.ERROR)
self._finishEarly()
return
# 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:
logger.log('Show in %s has no name on %s, probably the wrong language used to search with.' %

View file

@ -109,7 +109,7 @@ class TVShow(object):
self._air_by_date = 0
self._subtitles = int(sickbeard.SUBTITLES_DEFAULT if sickbeard.SUBTITLES_DEFAULT else 0)
self._dvdorder = 0
self._archive_firstmatch = 0
self._upgrade_once = 0
self._lang = lang
self._last_update_indexer = 1
self._sports = 0
@ -156,7 +156,7 @@ class TVShow(object):
air_by_date = property(lambda self: self._air_by_date, dirty_setter('_air_by_date'))
subtitles = property(lambda self: self._subtitles, dirty_setter('_subtitles'))
dvdorder = property(lambda self: self._dvdorder, dirty_setter('_dvdorder'))
archive_firstmatch = property(lambda self: self._archive_firstmatch, dirty_setter('_archive_firstmatch'))
upgrade_once = property(lambda self: self._upgrade_once, dirty_setter('_upgrade_once'))
lang = property(lambda self: self._lang, dirty_setter('_lang'))
last_update_indexer = property(lambda self: self._last_update_indexer, dirty_setter('_last_update_indexer'))
sports = property(lambda self: self._sports, dirty_setter('_sports'))
@ -358,7 +358,8 @@ class TVShow(object):
return ep_list
def getEpisode(self, season=None, episode=None, file=None, noCreate=False, absolute_number=None, forceUpdate=False):
def getEpisode(self, season=None, episode=None, file=None, noCreate=False, absolute_number=None, forceUpdate=False,
ep_sql=None):
# if we get an anime get the real season and episode
if self.is_anime and absolute_number and not season and not episode:
@ -392,9 +393,9 @@ class TVShow(object):
(self.indexerid, season, episode), logger.DEBUG)
if file:
ep = TVEpisode(self, season, episode, file)
ep = TVEpisode(self, season, episode, file, show_sql=ep_sql)
else:
ep = TVEpisode(self, season, episode)
ep = TVEpisode(self, season, episode, show_sql=ep_sql)
if ep != None:
self.episodes[season][episode] = ep
@ -932,9 +933,9 @@ class TVShow(object):
if not self.dvdorder:
self.dvdorder = 0
self.archive_firstmatch = sqlResults[0]['archive_firstmatch']
if not self.archive_firstmatch:
self.archive_firstmatch = 0
self.upgrade_once = sqlResults[0]['archive_firstmatch']
if not self.upgrade_once:
self.upgrade_once = 0
self.quality = int(sqlResults[0]['quality'])
self.flatten_folders = int(sqlResults[0]['flatten_folders'])
@ -1407,7 +1408,7 @@ class TVShow(object):
'sports': self.sports,
'subtitles': self.subtitles,
'dvdorder': self.dvdorder,
'archive_firstmatch': self.archive_firstmatch,
'archive_firstmatch': self.upgrade_once,
'startyear': self.startyear,
'lang': self.lang,
'imdb_id': self.imdbid,
@ -1451,6 +1452,30 @@ class TVShow(object):
logger.log('Checking if found %sepisode %sx%s is wanted at quality %s' %
(('', 'multi-part ')[multi_ep], season, episode, Quality.qualityStrings[quality]), logger.DEBUG)
if not multi_ep:
try:
wq = getattr(self.episodes.get(season, {}).get(episode, {}), 'wantedQuality', None)
if None is not wq:
if quality in wq:
curStatus, curQuality = Quality.splitCompositeStatus(self.episodes[season][episode].status)
if curStatus in (WANTED, UNAIRED, SKIPPED, FAILED):
logger.log('Existing episode status is wanted/unaired/skipped/failed, getting found episode',
logger.DEBUG)
return True
elif manualSearch:
logger.log('Usually ignoring found episode, but forced search allows the quality, getting found'
' episode', logger.DEBUG)
return True
elif quality > curQuality:
logger.log(
'Episode already exists but the found episode has better quality, getting found episode',
logger.DEBUG)
return True
logger.log('None of the conditions were met, ignoring found episode', logger.DEBUG)
return False
except (StandardError, Exception):
pass
# if the quality isn't one we want under any circumstances then just say no
initialQualities, archiveQualities = Quality.splitQuality(self.quality)
allQualities = list(set(initialQualities + archiveQualities))
@ -1542,7 +1567,7 @@ class TVShow(object):
min_best, max_best = min(best_qualities), max(best_qualities)
if quality >= max_best \
or (self.archive_firstmatch and
or (self.upgrade_once and
(quality in best_qualities or (None is not min_best and quality > min_best))):
return Overview.GOOD
return Overview.QUAL
@ -1558,7 +1583,7 @@ class TVShow(object):
class TVEpisode(object):
def __init__(self, show, season, episode, file=''):
def __init__(self, show, season, episode, file='', show_sql=None):
self._name = ''
self._season = season
self._episode = episode
@ -1593,7 +1618,7 @@ class TVEpisode(object):
self.lock = threading.Lock()
self.specifyEpisode(self.season, self.episode)
self.specifyEpisode(self.season, self.episode, show_sql)
self.relatedEps = []
@ -1738,9 +1763,9 @@ class TVEpisode(object):
# if either setting has changed return true, if not return false
return oldhasnfo != self.hasnfo or oldhastbn != self.hastbn
def specifyEpisode(self, season, episode):
def specifyEpisode(self, season, episode, show_sql=None):
sqlResult = self.loadFromDB(season, episode)
sqlResult = self.loadFromDB(season, episode, show_sql)
if not sqlResult:
# only load from NFO if we didn't load from DB
@ -1764,13 +1789,17 @@ class TVEpisode(object):
raise exceptions.EpisodeNotFoundException(
'Couldn\'t find episode %sx%s' % (season, episode))
def loadFromDB(self, season, episode):
def loadFromDB(self, season, episode, show_sql=None):
logger.log('%s: Loading episode details from DB for episode %sx%s' % (self.show.indexerid, season, episode),
logger.DEBUG)
myDB = db.DBConnection()
sql_results = myDB.select('SELECT * FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?',
[self.show.indexerid, season, episode])
sql_results = None
if show_sql:
sql_results = [s for s in show_sql if episode == s['episode'] and season == s['season']]
if not sql_results:
myDB = db.DBConnection()
sql_results = myDB.select('SELECT * FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?',
[self.show.indexerid, season, episode])
if len(sql_results) > 1:
raise exceptions.MultipleDBEpisodesException('Your DB has two records for the same show somehow.')

View file

@ -1678,7 +1678,7 @@ class Home(MainHandler):
def editShow(self, show=None, location=None, anyQualities=[], bestQualities=[], exceptions_list=[],
flatten_folders=None, paused=None, directCall=False, air_by_date=None, sports=None, dvdorder=None,
indexerLang=None, subtitles=None, archive_firstmatch=None, rls_ignore_words=None,
indexerLang=None, subtitles=None, upgrade_once=None, rls_ignore_words=None,
rls_require_words=None, anime=None, blacklist=None, whitelist=None,
scene=None, tag=None, quality_preset=None, reset_fanart=None, **kwargs):
@ -1757,7 +1757,7 @@ class Home(MainHandler):
flatten_folders = config.checkbox_to_value(flatten_folders)
dvdorder = config.checkbox_to_value(dvdorder)
archive_firstmatch = config.checkbox_to_value(archive_firstmatch)
upgrade_once = config.checkbox_to_value(upgrade_once)
paused = config.checkbox_to_value(paused)
air_by_date = config.checkbox_to_value(air_by_date)
scene = config.checkbox_to_value(scene)
@ -1820,7 +1820,7 @@ class Home(MainHandler):
with showObj.lock:
newQuality = Quality.combineQualities(map(int, anyQualities), map(int, bestQualities))
showObj.quality = newQuality
showObj.archive_firstmatch = archive_firstmatch
showObj.upgrade_once = upgrade_once
# reversed for now
if bool(showObj.flatten_folders) != bool(flatten_folders):
@ -3717,7 +3717,8 @@ class NewHomeAddShows(Home):
sickbeard.showQueueScheduler.action.addShow(indexer, indexer_id, show_dir, int(defaultStatus), newQuality,
flatten_folders, indexerLang, subtitles, anime,
scene, None, blacklist, whitelist,
wanted_begin, wanted_latest, tag, new_show=new_show) # @UndefinedVariable
wanted_begin, wanted_latest, tag, new_show=new_show,
show_name=show_name) # @UndefinedVariable
# ui.notifications.message('Show added', 'Adding the specified show into ' + show_dir)
return finishAddShow()
@ -3788,7 +3789,8 @@ class NewHomeAddShows(Home):
flatten_folders=sickbeard.FLATTEN_FOLDERS_DEFAULT,
subtitles=sickbeard.SUBTITLES_DEFAULT,
anime=sickbeard.ANIME_DEFAULT,
scene=sickbeard.SCENE_DEFAULT)
scene=sickbeard.SCENE_DEFAULT,
show_name=show_name)
num_added += 1
if num_added:
@ -4131,8 +4133,8 @@ class Manage(MainHandler):
if showObj:
showList.append(showObj)
archive_firstmatch_all_same = True
last_archive_firstmatch = None
upgrade_once_all_same = True
last_upgrade_once = None
flatten_folders_all_same = True
last_flatten_folders = None
@ -4169,12 +4171,12 @@ class Manage(MainHandler):
if cur_root_dir not in root_dir_list:
root_dir_list.append(cur_root_dir)
if archive_firstmatch_all_same:
if upgrade_once_all_same:
# if we had a value already and this value is different then they're not all the same
if last_archive_firstmatch not in (None, curShow.archive_firstmatch):
archive_firstmatch_all_same = False
if last_upgrade_once not in (None, curShow.upgrade_once):
upgrade_once_all_same = False
else:
last_archive_firstmatch = curShow.archive_firstmatch
last_upgrade_once = curShow.upgrade_once
# if we know they're not all the same then no point even bothering
if paused_all_same:
@ -4235,7 +4237,7 @@ class Manage(MainHandler):
last_air_by_date = curShow.air_by_date
t.showList = toEdit
t.archive_firstmatch_value = last_archive_firstmatch if archive_firstmatch_all_same else None
t.upgrade_once_value = last_upgrade_once if upgrade_once_all_same else None
t.paused_value = last_paused if paused_all_same else None
t.tag_value = last_tag if tag_all_same else None
t.anime_value = last_anime if anime_all_same else None
@ -4249,7 +4251,7 @@ class Manage(MainHandler):
return t.respond()
def massEditSubmit(self, archive_firstmatch=None, paused=None, anime=None, sports=None, scene=None,
def massEditSubmit(self, upgrade_once=None, paused=None, anime=None, sports=None, scene=None,
flatten_folders=None, quality_preset=False, subtitles=None, air_by_date=None, anyQualities=[],
bestQualities=[], toEdit=None, tag=None, *args, **kwargs):
@ -4285,11 +4287,11 @@ class Manage(MainHandler):
else:
new_show_dir = showObj._location
if archive_firstmatch == 'keep':
new_archive_firstmatch = showObj.archive_firstmatch
if upgrade_once == 'keep':
new_upgrade_once = showObj.upgrade_once
else:
new_archive_firstmatch = True if archive_firstmatch == 'enable' else False
new_archive_firstmatch = 'on' if new_archive_firstmatch else 'off'
new_upgrade_once = True if 'enable' == upgrade_once else False
new_upgrade_once = 'on' if new_upgrade_once else 'off'
if paused == 'keep':
new_paused = showObj.paused
@ -4348,7 +4350,7 @@ class Manage(MainHandler):
curErrors += Home(self.application, self.request).editShow(curShow, new_show_dir, anyQualities,
bestQualities, exceptions_list,
archive_firstmatch=new_archive_firstmatch,
upgrade_once=new_upgrade_once,
flatten_folders=new_flatten_folders,
paused=new_paused, sports=new_sports,
subtitles=new_subtitles, anime=new_anime,

View file

@ -110,7 +110,7 @@ mainDB.sickbeard.save_config = _dummy_saveConfig
# the real one tries to contact tvdb just stop it from getting more info on the ep
def _fake_specifyEP(self, season, episode):
def _fake_specifyEP(self, season, episode, show_sql=None):
pass
sickbeard.tv.TVEpisode.specifyEpisode = _fake_specifyEP