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

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