You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
196 lines
7.3 KiB
196 lines
7.3 KiB
from caper import Caper
|
|
|
|
from couchpotato.core.event import addEvent, fireEvent
|
|
from couchpotato.core.helpers.variable import getExt
|
|
from couchpotato.core.logger import CPLog
|
|
from couchpotato.core.media._base.quality.base import QualityBase
|
|
|
|
log = CPLog(__name__)
|
|
|
|
autoload = 'ShowQuality'
|
|
|
|
|
|
class ShowQuality(QualityBase):
|
|
type = 'show'
|
|
|
|
properties = {
|
|
'codec': [
|
|
{'identifier': 'mp2', 'label': 'MPEG-2/H.262', 'value': ['mpeg2']},
|
|
{'identifier': 'mp4-asp', 'label': 'MPEG-4 ASP', 'value': ['divx', 'xvid']},
|
|
{'identifier': 'mp4-avc', 'label': 'MPEG-4 AVC/H.264', 'value': ['avc', 'h264', 'x264', ('h', '264')]},
|
|
],
|
|
'container': [
|
|
{'identifier': 'avi', 'label': 'AVI', 'value': ['avi']},
|
|
{'identifier': 'mov', 'label': 'QuickTime Movie', 'value': ['mov']},
|
|
{'identifier': 'mpeg-4', 'label': 'MPEG-4', 'value': ['m4v', 'mp4']},
|
|
{'identifier': 'mpeg-ts', 'label': 'MPEG-TS', 'value': ['m2ts', 'ts']},
|
|
{'identifier': 'mkv', 'label': 'Matroska', 'value': ['mkv']},
|
|
{'identifier': 'wmv', 'label': 'Windows Media Video', 'value': ['wmv']}
|
|
],
|
|
'resolution': [
|
|
# TODO interlaced resolutions (auto-fill these options?)
|
|
{'identifier': 'sd'},
|
|
{'identifier': '480p', 'width': 853, 'height': 480},
|
|
{'identifier': '576p', 'width': 1024, 'height': 576},
|
|
{'identifier': '720p', 'width': 1280, 'height': 720},
|
|
{'identifier': '1080p', 'width': 1920, 'height': 1080}
|
|
],
|
|
'source': [
|
|
{'identifier': 'cam', 'label': 'Cam', 'value': ['camrip', 'hdcam']},
|
|
{'identifier': 'hdtv', 'label': 'HDTV', 'value': ['hdtv']},
|
|
{'identifier': 'screener', 'label': 'Screener', 'value': ['screener', 'dvdscr', 'ppvrip', 'dvdscreener', 'hdscr']},
|
|
{'identifier': 'web', 'label': 'Web', 'value': ['webrip', ('web', 'rip'), 'webdl', ('web', 'dl')]}
|
|
]
|
|
}
|
|
|
|
qualities = [
|
|
# TODO sizes will need to be adjusted for season packs
|
|
|
|
# resolutions
|
|
{'identifier': '1080p', 'label': '1080p', 'size': (1000, 25000), 'codec': ['mp4-avc'], 'container': ['mpeg-ts', 'mkv'], 'resolution': ['1080p']},
|
|
{'identifier': '720p', 'label': '720p', 'size': (1000, 5000), 'codec': ['mp4-avc'], 'container': ['mpeg-ts', 'mkv'], 'resolution': ['720p']},
|
|
{'identifier': '480p', 'label': '480p', 'size': (800, 5000), 'codec': ['mp4-avc'], 'container': ['mpeg-ts', 'mkv'], 'resolution': ['480p']},
|
|
|
|
# sources
|
|
{'identifier': 'cam', 'label': 'Cam', 'size': (800, 5000), 'source': ['cam']},
|
|
{'identifier': 'hdtv', 'label': 'HDTV', 'size': (800, 5000), 'source': ['hdtv']},
|
|
{'identifier': 'screener', 'label': 'Screener', 'size': (800, 5000), 'source': ['screener']},
|
|
{'identifier': 'web', 'label': 'Web', 'size': (800, 5000), 'source': ['web']},
|
|
]
|
|
|
|
def __init__(self):
|
|
super(ShowQuality, self).__init__()
|
|
|
|
addEvent('quality.guess', self.guess)
|
|
|
|
self.caper = Caper()
|
|
|
|
def guess(self, files, extra = None, size = None, types = None):
|
|
if types and self.type not in types:
|
|
return
|
|
|
|
log.debug('Trying to guess quality of: %s', files)
|
|
|
|
if not extra: extra = {}
|
|
|
|
# Create hash for cache
|
|
cache_key = str([f.replace('.' + getExt(f), '') if len(getExt(f)) < 4 else f for f in files])
|
|
cached = self.getCache(cache_key)
|
|
if cached and len(extra) == 0:
|
|
return cached
|
|
|
|
qualities = self.all()
|
|
|
|
# Score files against each quality
|
|
score = self.score(files, qualities = qualities)
|
|
|
|
if score is None:
|
|
return None
|
|
|
|
# Return nothing if all scores are <= 0
|
|
has_non_zero = 0
|
|
for s in score:
|
|
if score[s]['score'] > 0:
|
|
has_non_zero += 1
|
|
|
|
if not has_non_zero:
|
|
return None
|
|
|
|
heighest_quality = max(score, key = lambda p: score[p]['score'])
|
|
if heighest_quality:
|
|
for quality in qualities:
|
|
if quality.get('identifier') == heighest_quality:
|
|
quality['is_3d'] = False
|
|
if score[heighest_quality].get('3d'):
|
|
quality['is_3d'] = True
|
|
return self.setCache(cache_key, quality)
|
|
|
|
return None
|
|
|
|
def score(self, files, qualities = None, types = None):
|
|
if types and self.type not in types:
|
|
return None
|
|
|
|
if not qualities:
|
|
qualities = self.all()
|
|
|
|
qualities_expanded = [self.expand(q.copy()) for q in qualities]
|
|
|
|
# Start with 0
|
|
score = {}
|
|
for quality in qualities:
|
|
score[quality.get('identifier')] = {
|
|
'score': 0,
|
|
'3d': {}
|
|
}
|
|
|
|
for cur_file in files:
|
|
match = self.caper.parse(cur_file, 'scene')
|
|
|
|
if len(match.chains) < 1:
|
|
log.info2('Unable to parse "%s", ignoring file')
|
|
continue
|
|
|
|
chain = match.chains[0]
|
|
|
|
for quality in qualities_expanded:
|
|
property_score = self.propertyScore(quality, chain)
|
|
|
|
self.calcScore(score, quality, property_score)
|
|
|
|
return score
|
|
|
|
def propertyScore(self, quality, chain):
|
|
score = 0
|
|
|
|
if 'video' not in chain.info:
|
|
return 0
|
|
|
|
info = fireEvent('matcher.flatten_info', chain.info['video'], single = True)
|
|
|
|
for key in ['codec', 'resolution', 'source']:
|
|
if key not in quality:
|
|
# No specific property required
|
|
score += 5
|
|
continue
|
|
|
|
available = list(self.getInfo(info, key))
|
|
found = False
|
|
|
|
for property in quality[key]:
|
|
required = property['value'] if 'value' in property else [property['identifier']]
|
|
|
|
if set(available) & set(required):
|
|
score += 10
|
|
found = True
|
|
break
|
|
|
|
if not found:
|
|
score -= 10
|
|
|
|
return score
|
|
|
|
def getInfo(self, info, key):
|
|
for value in info.get(key, []):
|
|
if isinstance(value, list):
|
|
yield tuple([x.lower() for x in value])
|
|
else:
|
|
yield value.lower()
|
|
|
|
def calcScore(self, score, quality, add_score, threedscore = (0, None), penalty = True):
|
|
score[quality['identifier']]['score'] += add_score
|
|
|
|
# Set order for allow calculation (and cache)
|
|
if not self.cached_order:
|
|
self.cached_order = {}
|
|
for q in self.qualities:
|
|
self.cached_order[q.get('identifier')] = self.qualities.index(q)
|
|
|
|
if penalty and add_score != 0:
|
|
for allow in quality.get('allow', []):
|
|
score[allow]['score'] -= 40 if self.cached_order[allow] < self.cached_order[quality['identifier']] else 5
|
|
|
|
# Give panelty for all lower qualities
|
|
for q in self.qualities[self.order.index(quality.get('identifier'))+1:]:
|
|
if score.get(q.get('identifier')):
|
|
score[q.get('identifier')]['score'] -= 1
|
|
|