|
|
@ -1,11 +1,14 @@ |
|
|
|
import pprint |
|
|
|
import re |
|
|
|
from couchpotato import get_session, Env |
|
|
|
from couchpotato.core.event import addEvent, fireEvent |
|
|
|
from couchpotato.core.helpers.variable import getTitle |
|
|
|
from couchpotato.core.helpers.encoding import simplifyString |
|
|
|
from couchpotato.core.helpers.variable import getTitle, tryInt, possibleTitles |
|
|
|
from couchpotato.core.logger import CPLog |
|
|
|
from couchpotato.core.media._base.searcher.main import SearchSetupError |
|
|
|
from couchpotato.core.plugins.base import Plugin |
|
|
|
from couchpotato.core.settings.model import Media |
|
|
|
from couchpotato.core.settings.model import Media, Library |
|
|
|
from caper import Caper |
|
|
|
|
|
|
|
log = CPLog(__name__) |
|
|
|
|
|
|
@ -14,6 +17,15 @@ class ShowSearcher(Plugin): |
|
|
|
|
|
|
|
in_progress = False |
|
|
|
|
|
|
|
# TODO come back to this later, think this could be handled better |
|
|
|
quality_map = { |
|
|
|
'webdl_1080p': {'resolution': ['1080p'], 'source': ['webdl']}, |
|
|
|
'webdl_720p': {'resolution': ['720p'], 'source': ['webdl']}, |
|
|
|
|
|
|
|
'hdtv_720p': {'resolution': ['720p'], 'source': ['hdtv']}, |
|
|
|
'hdtv_sd': {'resolution': ['480p', None], 'source': ['hdtv']}, |
|
|
|
} |
|
|
|
|
|
|
|
def __init__(self): |
|
|
|
super(ShowSearcher, self).__init__() |
|
|
|
|
|
|
@ -21,10 +33,12 @@ class ShowSearcher(Plugin): |
|
|
|
addEvent('searcher.correct_release', self.correctRelease) |
|
|
|
addEvent('searcher.get_search_title', self.getSearchTitle) |
|
|
|
|
|
|
|
self.caper = Caper() |
|
|
|
|
|
|
|
def _lookupMedia(self, media): |
|
|
|
db = get_session() |
|
|
|
|
|
|
|
media_library = db.query(Media).filter_by(id = media['id']).first().library |
|
|
|
media_library = db.query(Library).filter_by(id = media['library_id']).first() |
|
|
|
|
|
|
|
show = None |
|
|
|
season = None |
|
|
@ -45,6 +59,8 @@ class ShowSearcher(Plugin): |
|
|
|
return show, season, episode |
|
|
|
|
|
|
|
def single(self, media, search_protocols = None): |
|
|
|
pprint.pprint(media) |
|
|
|
|
|
|
|
if media['type'] == 'show': |
|
|
|
# TODO handle show searches (scan all seasons) |
|
|
|
return |
|
|
@ -70,7 +86,7 @@ class ShowSearcher(Plugin): |
|
|
|
found_releases = [] |
|
|
|
too_early_to_search = [] |
|
|
|
|
|
|
|
default_title = getTitle(media['library']) |
|
|
|
default_title = self.getSearchTitle(media['library']) |
|
|
|
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) |
|
|
@ -126,14 +142,118 @@ class ShowSearcher(Plugin): |
|
|
|
if not fireEvent('searcher.correct_words', release['name'], media, single = True): |
|
|
|
return False |
|
|
|
|
|
|
|
preferred_quality = fireEvent('quality.single', identifier = quality['identifier'], single = True) |
|
|
|
#pprint.pprint(release) |
|
|
|
|
|
|
|
show, season, episode = self._lookupMedia(media) |
|
|
|
if show is None or season is None: |
|
|
|
log.error('Unable to find show or season library in database, missing required data for searching') |
|
|
|
return |
|
|
|
|
|
|
|
release_info = self.caper.parse(release['name']) |
|
|
|
if len(release_info.chains) < 1: |
|
|
|
log.info2('Wrong: %s, unable to parse release name (no chains)', release['name']) |
|
|
|
return False |
|
|
|
|
|
|
|
# TODO look at all chains |
|
|
|
chain = release_info.chains[0] |
|
|
|
|
|
|
|
if not self.correctQuality(chain, quality['identifier']): |
|
|
|
log.info('Wrong: %s, quality does not match', release['name']) |
|
|
|
return False |
|
|
|
|
|
|
|
if not self.correctIdentifier(chain, media): |
|
|
|
log.info('Wrong: %s, identifier does not match', release['name']) |
|
|
|
return False |
|
|
|
|
|
|
|
#print chain.weight |
|
|
|
#pprint.pprint(chain.info) |
|
|
|
|
|
|
|
if 'show_name' not in chain.info or not len(chain.info['show_name']): |
|
|
|
log.info('Wrong: %s, missing show name in parsed result', release['name']) |
|
|
|
return False |
|
|
|
|
|
|
|
chain_words = [x.lower() for x in chain.info['show_name']] |
|
|
|
chain_title = ' '.join(chain_words) |
|
|
|
|
|
|
|
library_title = None |
|
|
|
|
|
|
|
# Check show titles match |
|
|
|
for raw_title in show.titles: |
|
|
|
for valid_words in [x.split(' ') for x in possibleTitles(raw_title.title)]: |
|
|
|
if not library_title: |
|
|
|
library_title = ' '.join(valid_words) |
|
|
|
|
|
|
|
if valid_words == chain_words: |
|
|
|
return True |
|
|
|
|
|
|
|
log.info("Wrong: title '%s', undetermined show naming. Looking for '%s (%s)'", (chain_title, library_title, media['library']['year'])) |
|
|
|
return False |
|
|
|
|
|
|
|
# Contains lower quality string |
|
|
|
if fireEvent('searcher.contains_other_quality', release, preferred_quality = preferred_quality, single = True): |
|
|
|
log.info2('Wrong: %s, looking for %s', (release['name'], quality['label'])) |
|
|
|
def correctQuality(self, chain, quality_identifier): |
|
|
|
if quality_identifier not in self.quality_map: |
|
|
|
log.info2('Wrong: unknown preferred quality %s for TV searching', quality_identifier) |
|
|
|
return False |
|
|
|
|
|
|
|
pprint.pprint(release) |
|
|
|
if 'video' not in chain.info: |
|
|
|
log.info2('Wrong: no video tags found') |
|
|
|
return False |
|
|
|
|
|
|
|
video_tags = self.quality_map[quality_identifier] |
|
|
|
|
|
|
|
if not self.chainMatches(chain, 'video', video_tags): |
|
|
|
log.info2('Wrong: %s tags not in chain', video_tags) |
|
|
|
return False |
|
|
|
|
|
|
|
return True |
|
|
|
|
|
|
|
def correctIdentifier(self, chain, media): |
|
|
|
required_id = self.getIdentifier(media['library'], 'season_number', 'episode_number') |
|
|
|
|
|
|
|
if 'identifier' not in chain.info: |
|
|
|
return False |
|
|
|
|
|
|
|
# TODO could be handled better? |
|
|
|
if len(chain.info['identifier']) != 1: |
|
|
|
return False |
|
|
|
identifier = chain.info['identifier'][0] |
|
|
|
|
|
|
|
# TODO air by date episodes |
|
|
|
release_id = self.getIdentifier(identifier, 'season', 'episode') |
|
|
|
|
|
|
|
if required_id != release_id: |
|
|
|
log.info2('Wrong: required identifier %s does not match release identifier %s', (str(required_id), str(release_id))) |
|
|
|
return False |
|
|
|
|
|
|
|
return True |
|
|
|
|
|
|
|
def getIdentifier(self, d, episode_key, season_key): |
|
|
|
return ( |
|
|
|
tryInt(d.get(season_key), None) if season_key in d else None, |
|
|
|
tryInt(d.get(episode_key), None) if episode_key in d else None |
|
|
|
) |
|
|
|
|
|
|
|
def chainMatches(self, chain, group, tags): |
|
|
|
found_tags = [] |
|
|
|
|
|
|
|
for match in chain.info[group]: |
|
|
|
for ck, cv in match.items(): |
|
|
|
if ck in tags and self.cleanMatchValue(cv) in tags[ck]: |
|
|
|
found_tags.append(ck) |
|
|
|
|
|
|
|
|
|
|
|
if set(tags.keys()) == set(found_tags): |
|
|
|
return True |
|
|
|
|
|
|
|
return set([key for key, value in tags.items() if value]) == set(found_tags) |
|
|
|
|
|
|
|
def cleanMatchValue(self, value): |
|
|
|
value = value.lower() |
|
|
|
value = value.strip() |
|
|
|
|
|
|
|
for ch in [' ', '-', '.']: |
|
|
|
value = value.replace(ch, '') |
|
|
|
|
|
|
|
return value |
|
|
|
|
|
|
|
def getSearchTitle(self, media): |
|
|
|
show, season, episode = self._lookupMedia(media) |
|
|
|