Base classes for matcher and library
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
from .main import Library
|
||||
|
||||
def autoload():
|
||||
return Library()
|
||||
|
||||
config = []
|
||||
@@ -0,0 +1,13 @@
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
|
||||
|
||||
class LibraryBase(Plugin):
|
||||
|
||||
_type = None
|
||||
|
||||
def initType(self):
|
||||
addEvent('library.types', self.getType)
|
||||
|
||||
def getType(self):
|
||||
return self._type
|
||||
@@ -0,0 +1,18 @@
|
||||
from couchpotato.core.event import addEvent, fireEvent
|
||||
from couchpotato.core.media._base.library.base import LibraryBase
|
||||
|
||||
|
||||
class Library(LibraryBase):
|
||||
def __init__(self):
|
||||
addEvent('library.title', self.title)
|
||||
|
||||
def title(self, library):
|
||||
return fireEvent(
|
||||
'library.query',
|
||||
library,
|
||||
|
||||
condense = False,
|
||||
include_year = False,
|
||||
include_identifier = False,
|
||||
single = True
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
from .main import Matcher
|
||||
|
||||
def autoload():
|
||||
return Matcher()
|
||||
|
||||
config = []
|
||||
@@ -0,0 +1,84 @@
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.helpers.encoding import simplifyString
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class MatcherBase(Plugin):
|
||||
type = None
|
||||
|
||||
def __init__(self):
|
||||
if self.type:
|
||||
addEvent('%s.matcher.correct' % self.type, self.correct)
|
||||
|
||||
def correct(self, chain, release, media, quality):
|
||||
raise NotImplementedError()
|
||||
|
||||
def flattenInfo(self, info):
|
||||
# Flatten dictionary of matches (chain info)
|
||||
if isinstance(info, dict):
|
||||
return dict([(key, self.flattenInfo(value)) for key, value in info.items()])
|
||||
|
||||
# Flatten matches
|
||||
result = None
|
||||
|
||||
for match in info:
|
||||
if isinstance(match, dict):
|
||||
if result is None:
|
||||
result = {}
|
||||
|
||||
for key, value in match.items():
|
||||
if key not in result:
|
||||
result[key] = []
|
||||
|
||||
result[key].append(value)
|
||||
else:
|
||||
if result is None:
|
||||
result = []
|
||||
|
||||
result.append(match)
|
||||
|
||||
return result
|
||||
|
||||
def constructFromRaw(self, match):
|
||||
if not match:
|
||||
return None
|
||||
|
||||
parts = [
|
||||
''.join([
|
||||
y for y in x[1:] if y
|
||||
]) for x in match
|
||||
]
|
||||
|
||||
return ''.join(parts)[:-1].strip()
|
||||
|
||||
def simplifyValue(self, value):
|
||||
if not value:
|
||||
return value
|
||||
|
||||
if isinstance(value, basestring):
|
||||
return simplifyString(value)
|
||||
|
||||
if isinstance(value, list):
|
||||
return [self.simplifyValue(x) for x in value]
|
||||
|
||||
raise ValueError("Unsupported value type")
|
||||
|
||||
def chainMatch(self, chain, group, tags):
|
||||
info = self.flattenInfo(chain.info[group])
|
||||
|
||||
found_tags = []
|
||||
for tag, accepted in tags.items():
|
||||
values = [self.simplifyValue(x) for x in info.get(tag, [None])]
|
||||
|
||||
if any([val in accepted for val in values]):
|
||||
found_tags.append(tag)
|
||||
|
||||
log.debug('tags found: %s, required: %s' % (found_tags, tags.keys()))
|
||||
|
||||
if set(tags.keys()) == set(found_tags):
|
||||
return True
|
||||
|
||||
return all([key in found_tags for key, value in tags.items()])
|
||||
@@ -0,0 +1,89 @@
|
||||
from couchpotato.core.event import addEvent, fireEvent
|
||||
from couchpotato.core.helpers.variable import possibleTitles
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.media._base.matcher.base import MatcherBase
|
||||
from caper import Caper
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class Matcher(MatcherBase):
|
||||
|
||||
def __init__(self):
|
||||
super(Matcher, self).__init__()
|
||||
|
||||
self.caper = Caper()
|
||||
|
||||
addEvent('matcher.parse', self.parse)
|
||||
addEvent('matcher.match', self.match)
|
||||
|
||||
addEvent('matcher.flatten_info', self.flattenInfo)
|
||||
addEvent('matcher.construct_from_raw', self.constructFromRaw)
|
||||
|
||||
addEvent('matcher.correct_title', self.correctTitle)
|
||||
addEvent('matcher.correct_quality', self.correctQuality)
|
||||
|
||||
def parse(self, name, parser='scene'):
|
||||
return self.caper.parse(name, parser)
|
||||
|
||||
def match(self, release, media, quality):
|
||||
match = fireEvent('matcher.parse', release['name'], single = True)
|
||||
|
||||
if len(match.chains) < 1:
|
||||
log.info2('Wrong: %s, unable to parse release name (no chains)', release['name'])
|
||||
return False
|
||||
|
||||
for chain in match.chains:
|
||||
if fireEvent('%s.matcher.correct' % media['type'], chain, release, media, quality, single = True):
|
||||
return chain
|
||||
|
||||
return False
|
||||
|
||||
def correctTitle(self, chain, media):
|
||||
root_library = media['library']['root_library']
|
||||
|
||||
if 'show_name' not in chain.info or not len(chain.info['show_name']):
|
||||
log.info('Wrong: missing show name in parsed result')
|
||||
return False
|
||||
|
||||
# Get the lower-case parsed show name from the chain
|
||||
chain_words = [x.lower() for x in chain.info['show_name']]
|
||||
|
||||
# Build a list of possible titles of the media we are searching for
|
||||
titles = root_library['info']['titles']
|
||||
|
||||
# Add year suffix titles (will result in ['<name_one>', '<name_one> <suffix_one>', '<name_two>', ...])
|
||||
suffixes = [None, root_library['info']['year']]
|
||||
|
||||
titles = [
|
||||
title + ((' %s' % suffix) if suffix else '')
|
||||
for title in titles
|
||||
for suffix in suffixes
|
||||
]
|
||||
|
||||
# Check show titles match
|
||||
# TODO check xem names
|
||||
for title in titles:
|
||||
for valid_words in [x.split(' ') for x in possibleTitles(title)]:
|
||||
|
||||
if valid_words == chain_words:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def correctQuality(self, chain, quality, quality_map):
|
||||
if quality['identifier'] not in quality_map:
|
||||
log.info2('Wrong: unknown preferred quality %s', quality['identifier'])
|
||||
return False
|
||||
|
||||
if 'video' not in chain.info:
|
||||
log.info2('Wrong: no video tags found')
|
||||
return False
|
||||
|
||||
video_tags = quality_map[quality['identifier']]
|
||||
|
||||
if not self.chainMatch(chain, 'video', video_tags):
|
||||
log.info2('Wrong: %s tags not in chain', video_tags)
|
||||
return False
|
||||
|
||||
return True
|
||||
Reference in New Issue
Block a user