Browse Source

Show searcher init

pull/3111/head
Ruud 11 years ago
parent
commit
342e61da48
  1. 8
      couchpotato/core/media/show/_base/main.py
  2. 6
      couchpotato/core/media/show/episode.py
  3. 6
      couchpotato/core/media/show/providers/info/thetvdb.py
  4. 203
      couchpotato/core/media/show/searcher.py
  5. 8
      couchpotato/core/media/show/season.py

8
couchpotato/core/media/show/_base/main.py

@ -165,12 +165,14 @@ class ShowBase(MediaBase):
for season_nr in seasons_info: for season_nr in seasons_info:
season_info = seasons_info[season_nr] season_info = seasons_info[season_nr]
season = fireEvent('show.season.add', media.get('_id'), season_info, single = True) episodes = season_info.get('episodes', {})
season = fireEvent('show.season.add', m.get('_id'), season_info, single = True)
# Add Episodes # Add Episodes
for episode_nr in season_info.get('episodes', {}): for episode_nr in episodes:
episode_info = season_info['episodes'][episode_nr] episode_info = episodes[episode_nr]
fireEvent('show.episode.add', season.get('_id'), episode_info, single = True) fireEvent('show.episode.add', season.get('_id'), episode_info, single = True)

6
couchpotato/core/media/show/episode.py

@ -1,6 +1,5 @@
from couchpotato import get_db from couchpotato import get_db
from couchpotato.core.event import addEvent, fireEvent, fireEventAsync from couchpotato.core.event import addEvent, fireEvent, fireEventAsync
from couchpotato.core.helpers.encoding import toUnicode
from couchpotato.core.logger import CPLog from couchpotato.core.logger import CPLog
from couchpotato.core.helpers.variable import tryInt from couchpotato.core.helpers.variable import tryInt
from couchpotato.core.media import MediaBase from couchpotato.core.media import MediaBase
@ -10,6 +9,7 @@ log = CPLog(__name__)
autoload = 'Episode' autoload = 'Episode'
class Episode(MediaBase): class Episode(MediaBase):
def __init__(self): def __init__(self):
@ -31,7 +31,7 @@ class Episode(MediaBase):
'_t': 'media', '_t': 'media',
'type': 'episode', 'type': 'episode',
'identifiers': identifiers, 'identifiers': identifiers,
'parent': parent_id, 'parent_id': parent_id,
'info': info, # Returned dict by providers 'info': info, # Returned dict by providers
} }
@ -54,7 +54,7 @@ class Episode(MediaBase):
return episode return episode
def update_info(self, media_id = None, info = None, force = False): def updateInfo(self, media_id = None, info = None, force = False):
if not info: info = {} if not info: info = {}
if self.shuttingDown(): if self.shuttingDown():

6
couchpotato/core/media/show/providers/info/thetvdb.py

@ -318,7 +318,7 @@ class TheTVDb(ShowProvider):
'identifiers': { 'identifiers': {
'thetvdb': show.get('seriesid') if show.get('seriesid') else show[number][1]['seasonid'] 'thetvdb': show.get('seriesid') if show.get('seriesid') else show[number][1]['seasonid']
}, },
'number': number, 'number': tryInt(number),
'images': { 'images': {
'poster': poster, 'poster': poster,
}, },
@ -369,8 +369,8 @@ class TheTVDb(ShowProvider):
poster = get('filename', []) poster = get('filename', [])
episode_data = { episode_data = {
'number': get('episodenumber'), 'number': tryInt(get('episodenumber')),
'absolute_number': get('absolute_number'), 'absolute_number': tryInt(get('absolute_number')),
'identifiers': { 'identifiers': {
'thetvdb': tryInt(episode['id']) 'thetvdb': tryInt(episode['id'])
}, },

203
couchpotato/core/media/show/searcher.py

@ -1,4 +1,5 @@
from couchpotato import Env import time
from couchpotato import Env, get_db
from couchpotato.core.event import addEvent, fireEvent from couchpotato.core.event import addEvent, fireEvent
from couchpotato.core.helpers.variable import getTitle, toIterable from couchpotato.core.helpers.variable import getTitle, toIterable
from couchpotato.core.logger import CPLog from couchpotato.core.logger import CPLog
@ -23,22 +24,24 @@ class ShowSearcher(SearcherBase, ShowTypeBase):
self.query_condenser = QueryCondenser() self.query_condenser = QueryCondenser()
for type in toIterable(self.type): addEvent('season.searcher.single', self.singleSeason)
addEvent('%s.searcher.single' % type, self.single) addEvent('episode.searcher.single', self.singleEpisode)
addEvent('searcher.correct_release', self.correctRelease) addEvent('searcher.correct_release', self.correctRelease)
def single(self, media, search_protocols = None, manual = False):
show, season, episode = self.getLibraries(media['library'])
if media['type'] == 'show': def test():
for library in season: time.sleep(.2)
# TODO ideally we shouldn't need to fetch the media for each season library here db = get_db()
m = db.query(Media).filter_by(library_id = library['library_id']).first() medias = db.get_many('media_by_type', 'show', with_doc = True)
for x in medias:
media = x['doc']
break
self.single(media)
fireEvent('season.searcher.single', m.to_dict(ShowBase.search_dict)) addEvent('app.load', test)
return def single(self, media, search_protocols = None, manual = False):
# Find out search type # Find out search type
try: try:
@ -47,78 +50,123 @@ class ShowSearcher(SearcherBase, ShowTypeBase):
except SearchSetupError: except SearchSetupError:
return return
done_status, available_status, ignored_status, failed_status = fireEvent('status.get', ['done', 'available', 'ignored', 'failed'], single = True) if not media['profile_id'] or media['status'] == 'done':
log.debug('Show doesn\'t have a profile or already done, assuming in manage tab.')
if not media['profile'] or media['status_id'] == done_status.get('id'):
log.debug('Episode doesn\'t have a profile or already done, assuming in manage tab.')
return return
#pre_releases = fireEvent('quality.pre_releases', single = True) show_title = fireEvent('media.search_query', media, condense = False, single = True)
found_releases = [] fireEvent('notify.frontend', type = 'show.searcher.started.%s' % media['_id'], data = True, message = 'Searching for "%s"' % show_title)
too_early_to_search = []
default_title = fireEvent('media.search_query', media['library'], condense = False, single=True) media = self.extendShow(media)
if not default_title:
log.error('No proper info found for episode, removing it from library to cause it from having more issues.')
#fireEvent('episode.delete', episode['id'], single = True)
return
if not show or not season: db = get_db()
log.error('Unable to find show or season library in database, missing required data for searching')
return
fireEvent('notify.frontend', type = 'show.searcher.started.%s' % media['id'], data = True, message = 'Searching for "%s"' % default_title) profile = db.get('id', media['profile_id'])
quality_order = fireEvent('quality.order', single = True)
ret = False seasons = media.get('seasons', {})
has_better_quality = None for sx in seasons:
season = seasons.get(sx)
# Check if full season can be downloaded TODO: add
season_success = self.singleSeason(season, media, profile)
# Do each episode seperately
if not season_success:
episodes = season.get('episodes', {})
for ex in episodes:
episode = episodes.get(ex)
self.singleEpisode(episode, season, media, profile, quality_order, search_protocols)
fireEvent('notify.frontend', type = 'show.searcher.ended.%s' % media['id'], data = True)
return ret
def singleSeason(self, media, show, profile):
# Check if any episode is already snatched
active = 0
episodes = media.get('episodes', {})
for ex in episodes:
episode = episodes.get(ex)
if episode.get('status') in ['active']:
active += 1
if active != len(episodes):
return False
for quality_type in media['profile']['types']: # Try and search for full season
# TODO check air date? # TODO:
return False
def singleEpisode(self, media, season, show, profile, quality_order, search_protocols = None, manual = False):
# TODO: check episode status
# TODO: check air date
#if not self.conf('always_search') and not self.couldBeReleased(quality_type['quality']['identifier'] in pre_releases, release_dates, movie['library']['year']): #if not self.conf('always_search') and not self.couldBeReleased(quality_type['quality']['identifier'] in pre_releases, release_dates, movie['library']['year']):
# too_early_to_search.append(quality_type['quality']['identifier']) # too_early_to_search.append(quality_type['quality']['identifier'])
# continue # return
ret = False
has_better_quality = None
found_releases = []
too_early_to_search = []
releases = fireEvent('release.for_media', media['_id'], single = True)
show_title = getTitle(show)
episode_identifier = '%s S%02d%s' % (show_title, season['info'].get('number', 0), "E%02d" % media['info'].get('number'))
index = 0
for q_identifier in profile.get('qualities'):
quality_custom = {
'quality': q_identifier,
'finish': profile['finish'][index],
'wait_for': profile['wait_for'][index],
'3d': profile['3d'][index] if profile.get('3d') else False
}
has_better_quality = 0 has_better_quality = 0
# See if better quality is available # See if better quality is available
for release in media['releases']: for release in releases:
if release['quality']['order'] <= quality_type['quality']['order'] and release['status_id'] not in [available_status.get('id'), ignored_status.get('id'), failed_status.get('id')]: if quality_order.index(release['quality']) <= quality_order.index(q_identifier) and release['status'] not in ['available', 'ignored', 'failed']:
has_better_quality += 1 has_better_quality += 1
# Don't search for quality lower then already available. # Don't search for quality lower then already available.
if has_better_quality is 0: if has_better_quality is 0:
log.info('Search for %s S%02d%s in %s', ( log.info('Searching for %s in %s', (episode_identifier, q_identifier))
getTitle(show), quality = fireEvent('quality.single', identifier = q_identifier, single = True)
season['season_number'], quality['custom'] = quality_custom
"E%02d" % episode['episode_number'] if episode and len(episode) == 1 else "",
quality_type['quality']['label'])
)
quality = fireEvent('quality.single', identifier = quality_type['quality']['identifier'], single = True)
results = fireEvent('searcher.search', search_protocols, media, quality, single = True) results = fireEvent('searcher.search', search_protocols, media, quality, single = True)
if len(results) == 0: if len(results) == 0:
log.debug('Nothing found for %s in %s', (default_title, quality_type['quality']['label'])) log.debug('Nothing found for %s in %s', (episode_identifier, q_identifier))
# Check if movie isn't deleted while searching
if not db.query(Media).filter_by(id = media.get('id')).first():
break
# Add them to this movie releases list # Add them to this movie releases list
found_releases += fireEvent('release.create_from_search', results, media, quality_type, single = True) found_releases += fireEvent('release.create_from_search', results, media, quality, single = True)
# Try find a valid result and download it # Try find a valid result and download it
if fireEvent('release.try_download_result', results, media, quality_type, manual, single = True): if fireEvent('release.try_download_result', results, media, quality, manual, single = True):
ret = True ret = True
# Remove releases that aren't found anymore # Remove releases that aren't found anymore
for release in media.get('releases', []): for release in releases:
if release.get('status_id') == available_status.get('id') and release.get('identifier') not in found_releases: if release.get('status') == 'available' and release.get('identifier') not in found_releases:
fireEvent('release.delete', release.get('id'), single = True) fireEvent('release.delete', release.get('id'), single = True)
else: else:
log.info('Better quality (%s) already available or snatched for %s', (quality_type['quality']['label'], default_title)) log.info('Better quality (%s) already available or snatched for %s', (q_identifier, episode_identifier))
fireEvent('media.restatus', media['id']) fireEvent('media.restatus', media['_id'])
break break
# Break if CP wants to shut down # Break if CP wants to shut down
@ -126,21 +174,7 @@ class ShowSearcher(SearcherBase, ShowTypeBase):
break break
if len(too_early_to_search) > 0: if len(too_early_to_search) > 0:
log.info2('Too early to search for %s, %s', (too_early_to_search, default_title)) log.info2('Too early to search for %s, %s', (too_early_to_search, episode_identifier))
elif media['type'] == 'season' and not ret and has_better_quality is 0:
# If nothing was found, start searching for episodes individually
log.info('No season pack found, starting individual episode search')
for library in episode:
# TODO ideally we shouldn't need to fetch the media for each episode library here
m = db.query(Media).filter_by(library_id = library['library_id']).first()
fireEvent('episode.searcher.single', m.to_dict(ShowBase.search_dict))
fireEvent('notify.frontend', type = 'show.searcher.ended.%s' % media['id'], data = True)
return ret
def correctRelease(self, release = None, media = None, quality = None, **kwargs): def correctRelease(self, release = None, media = None, quality = None, **kwargs):
@ -163,28 +197,29 @@ class ShowSearcher(SearcherBase, ShowTypeBase):
return False return False
def getLibraries(self, library): def extendShow(self, media):
if 'related_libraries' not in library:
log.warning("'related_libraries' missing from media library, unable to continue searching") db = get_db()
return None, None, None
seasons = db.get_many('media_children', media['_id'], with_doc = True)
media['seasons'] = {}
libraries = library['related_libraries'] for sx in seasons:
season = sx['doc']
# Show always collapses as there can never be any multiples # Add episode info
show = libraries.get('show', []) season['episodes'] = {}
show = show[0] if len(show) else None episodes = db.get_many('media_children', sx['_id'], with_doc = True)
# Season collapses if the subject is a season or episode for se in episodes:
season = libraries.get('season', []) episode = se['doc']
if library['type'] in ['season', 'episode']: season['episodes'][episode['info'].get('number')] = episode
season = season[0] if len(season) else None
# Episode collapses if the subject is a episode # Add season to show
episode = libraries.get('episode', []) media['seasons'][season['info'].get('number', 0)] = season
if library['type'] == 'episode':
episode = episode[0] if len(episode) else None
return show, season, episode return media
def searchAll(self): def searchAll(self):
pass pass

8
couchpotato/core/media/show/season.py

@ -17,7 +17,7 @@ class Season(MediaBase):
addEvent('media.identifier', self.identifier) addEvent('media.identifier', self.identifier)
addEvent('show.season.add', self.add) addEvent('show.season.add', self.add)
addEvent('show.season.update_info', self.update_info) addEvent('show.season.update_info', self.updateInfo)
def add(self, parent_id, info = None, update_after = True): def add(self, parent_id, info = None, update_after = True):
if not info: info = {} if not info: info = {}
@ -25,13 +25,15 @@ class Season(MediaBase):
identifiers = info.get('identifiers') identifiers = info.get('identifiers')
try: del info['identifiers'] try: del info['identifiers']
except: pass except: pass
try: del info['episodes']
except: pass
# Add Season # Add Season
season_info = { season_info = {
'_t': 'media', '_t': 'media',
'type': 'season', 'type': 'season',
'identifiers': identifiers, 'identifiers': identifiers,
'parent': parent_id, 'parent_id': parent_id,
'info': info, # Returned dict by providers 'info': info, # Returned dict by providers
} }
@ -54,7 +56,7 @@ class Season(MediaBase):
return season return season
def update_info(self, media_id = None, info = None, force = False): def updateInfo(self, media_id = None, info = None, force = False):
if not info: info = {} if not info: info = {}
if self.shuttingDown(): if self.shuttingDown():

Loading…
Cancel
Save