@ -1,5 +1,6 @@
import traceback
import re
from CodernityDB . database import RecordNotFound
from couchpotato import get_db
from couchpotato . api import addApiView
@ -21,11 +22,11 @@ class QualityPlugin(Plugin):
}
qualities = [
{ ' identifier ' : ' bd50 ' , ' hd ' : True , ' allow_3d ' : True , ' size ' : ( 15 000, 60000 ) , ' label ' : ' BR-Disk ' , ' alternative ' : [ ' bd25 ' ] , ' allow ' : [ ' 1080p ' ] , ' ext ' : [ ] , ' tags ' : [ ' bdmv ' , ' certificate ' , ( ' complete ' , ' bluray ' ) ] } ,
{ ' identifier ' : ' bd50 ' , ' hd ' : True , ' allow_3d ' : True , ' size ' : ( 20 000, 60000 ) , ' label ' : ' BR-Disk ' , ' alternative ' : [ ' bd25 ' ] , ' allow ' : [ ' 1080p ' ] , ' ext ' : [ ' iso ' , ' img ' ] , ' tags ' : [ ' bdmv ' , ' certificate ' , ( ' complete ' , ' bluray ' ) , ' avc ' , ' mvc ' ] } ,
{ ' identifier ' : ' 1080p ' , ' hd ' : True , ' allow_3d ' : True , ' size ' : ( 4000 , 20000 ) , ' label ' : ' 1080p ' , ' width ' : 1920 , ' height ' : 1080 , ' alternative ' : [ ] , ' allow ' : [ ] , ' ext ' : [ ' mkv ' , ' m2ts ' ] , ' tags ' : [ ' m2ts ' , ' x264 ' , ' h264 ' ] } ,
{ ' identifier ' : ' 720p ' , ' hd ' : True , ' allow_3d ' : True , ' size ' : ( 3000 , 10000 ) , ' label ' : ' 720p ' , ' width ' : 1280 , ' height ' : 720 , ' alternative ' : [ ] , ' allow ' : [ ] , ' ext ' : [ ' mkv ' , ' ts ' ] , ' tags ' : [ ' x264 ' , ' h264 ' ] } ,
{ ' identifier ' : ' brrip ' , ' hd ' : True , ' size ' : ( 700 , 7000 ) , ' label ' : ' BR-Rip ' , ' alternative ' : [ ' bdrip ' ] , ' allow ' : [ ' 720p ' , ' 1080p ' ] , ' ext ' : [ ] , ' tags ' : [ ' hdtv ' , ' hdrip ' , ' webdl ' , ( ' web ' , ' dl ' ) ] } ,
{ ' identifier ' : ' dvdr ' , ' size ' : ( 3000 , 10000 ) , ' label ' : ' DVD-R ' , ' alternative ' : [ ' br2dvd ' ] , ' allow ' : [ ] , ' ext ' : [ ' iso ' , ' img ' , ' vob ' ] , ' tags ' : [ ' pal ' , ' ntsc ' , ' video_ts ' , ' audio_ts ' , ( ' dvd ' , ' r ' ) ] } ,
{ ' identifier ' : ' brrip ' , ' hd ' : True , ' allow_3d ' : True , ' size' : ( 700 , 7000 ) , ' label ' : ' BR-Rip ' , ' alternative ' : [ ' bdrip ' ] , ' allow ' : [ ' 720p ' , ' 1080p ' ] , ' ext ' : [ ] , ' tags ' : [ ' hdtv ' , ' hdrip ' , ' webdl ' , ( ' web ' , ' dl ' ) ] } ,
{ ' identifier ' : ' dvdr ' , ' size ' : ( 3000 , 10000 ) , ' label ' : ' DVD-R ' , ' alternative ' : [ ' br2dvd ' ] , ' allow ' : [ ] , ' ext ' : [ ' iso ' , ' img ' , ' vob ' ] , ' tags ' : [ ' pal ' , ' ntsc ' , ' video_ts ' , ' audio_ts ' , ( ' dvd ' , ' r ' ) , ' dvd9 ' ] } ,
{ ' identifier ' : ' dvdrip ' , ' size ' : ( 600 , 2400 ) , ' label ' : ' DVD-Rip ' , ' width ' : 720 , ' alternative ' : [ ] , ' allow ' : [ ] , ' ext ' : [ ] , ' tags ' : [ ( ' dvd ' , ' rip ' ) , ( ' dvd ' , ' xvid ' ) , ( ' dvd ' , ' divx ' ) ] } ,
{ ' identifier ' : ' scr ' , ' size ' : ( 600 , 1600 ) , ' label ' : ' Screener ' , ' alternative ' : [ ' screener ' , ' dvdscr ' , ' ppvrip ' , ' dvdscreener ' , ' hdscr ' ] , ' allow ' : [ ' dvdr ' , ' dvdrip ' , ' 720p ' , ' 1080p ' ] , ' ext ' : [ ] , ' tags ' : [ ' webrip ' , ( ' web ' , ' rip ' ) ] } ,
{ ' identifier ' : ' r5 ' , ' size ' : ( 600 , 1000 ) , ' label ' : ' R5 ' , ' alternative ' : [ ' r6 ' ] , ' allow ' : [ ' dvdr ' ] , ' ext ' : [ ] } ,
@ -35,9 +36,9 @@ class QualityPlugin(Plugin):
]
pre_releases = [ ' cam ' , ' ts ' , ' tc ' , ' r5 ' , ' scr ' ]
threed_tags = {
' h sbs' : [ ( ' half ' , ' sbs ' ) ] ,
' fsbs ' : [ ( ' full ' , ' sbs ' ) ] ,
' 3d ' : [ ] ,
' sbs ' : [ ( ' half ' , ' sbs ' ) , ' hsbs ' , ( ' full ' , ' sbs ' ) , ' fsbs ' ] ,
' ou ' : [ ( ' half ' , ' ou ' ) , ' hou ' , ( ' full ' , ' ou ' ) , ' fou ' ] ,
' 3d ' : [ ' 2d3d ' , ' 3d2d ' , ' 3d ' ] ,
}
cached_qualities = None
@ -49,6 +50,9 @@ class QualityPlugin(Plugin):
addEvent ( ' quality.guess ' , self . guess )
addEvent ( ' quality.pre_releases ' , self . preReleases )
addEvent ( ' quality.order ' , self . getOrder )
addEvent ( ' quality.ishigher ' , self . isHigher )
addEvent ( ' quality.isfinish ' , self . isFinish )
addEvent ( ' quality.fill ' , self . fill )
addApiView ( ' quality.size.save ' , self . saveSize )
addApiView ( ' quality.list ' , self . allView , docs = {
@ -150,24 +154,31 @@ class QualityPlugin(Plugin):
order = 0
for q in self . qualities :
db . insert ( {
' _t ' : ' quality ' ,
' order ' : order ,
' identifier ' : q . get ( ' identifier ' ) ,
' size_min ' : tryInt ( q . get ( ' size ' ) [ 0 ] ) ,
' size_max ' : tryInt ( q . get ( ' size ' ) [ 1 ] ) ,
} )
log . info ( ' Creating profile: %s ' , q . get ( ' label ' ) )
db . insert ( {
' _t ' : ' profile ' ,
' order ' : order + 20 , # Make sure it goes behind other profiles
' core ' : True ,
' qualities ' : [ q . get ( ' identifier ' ) ] ,
' label ' : toUnicode ( q . get ( ' label ' ) ) ,
' finish ' : [ True ] ,
' wait_for ' : [ 0 ] ,
} )
existing = None
try :
existing = db . get ( ' quality ' , q . get ( ' identifier ' ) )
except RecordNotFound :
pass
if not existing :
db . insert ( {
' _t ' : ' quality ' ,
' order ' : order ,
' identifier ' : q . get ( ' identifier ' ) ,
' size_min ' : tryInt ( q . get ( ' size ' ) [ 0 ] ) ,
' size_max ' : tryInt ( q . get ( ' size ' ) [ 1 ] ) ,
} )
log . info ( ' Creating profile: %s ' , q . get ( ' label ' ) )
db . insert ( {
' _t ' : ' profile ' ,
' order ' : order + 20 , # Make sure it goes behind other profiles
' core ' : True ,
' qualities ' : [ q . get ( ' identifier ' ) ] ,
' label ' : toUnicode ( q . get ( ' label ' ) ) ,
' finish ' : [ True ] ,
' wait_for ' : [ 0 ] ,
} )
order + = 1
@ -177,7 +188,7 @@ class QualityPlugin(Plugin):
return False
def guess ( self , files , extra = None ) :
def guess ( self , files , extra = None , size = None ) :
if not extra : extra = { }
# Create hash for cache
@ -205,15 +216,27 @@ class QualityPlugin(Plugin):
self . calcScore ( score , quality , contains_score , threedscore )
# Try again with loose testing
size_scores = [ ]
for quality in qualities :
# Evaluate score based on size
size_score = self . guessSizeScore ( quality , size = size )
loose_score = self . guessLooseScore ( quality , extra = extra )
self . calcScore ( score , quality , loose_score )
# Return nothing if all scores are 0
if size_score > 0 :
size_scores . append ( quality )
self . calcScore ( score , quality , size_score + loose_score , penalty = False )
# Add additional size score if only 1 size validated
if len ( size_scores ) == 1 :
self . calcScore ( score , size_scores [ 0 ] , 10 , penalty = False )
del size_scores
# Return nothing if all scores are <= 0
has_non_zero = 0
for s in score :
if score [ s ] > 0 :
if score [ s ] [ ' score ' ] > 0 :
has_non_zero + = 1
if not has_non_zero :
@ -276,14 +299,14 @@ class QualityPlugin(Plugin):
tags = self . threed_tags . get ( key , [ ] )
for tag in tags :
if ( isinstance ( tag , tuple ) and ' . ' . join ( tag ) in ' . ' . join ( words ) ) or ( isinstance ( tag , ( str , unicode ) ) and ss ( tag . lower ( ) ) in cur_file . lower ( ) ) :
if isinstance ( tag , tuple ) :
if len ( set ( words ) & set ( tag ) ) == len ( tag ) :
log . debug ( ' Found %s in %s ' , ( tag , cur_file ) )
return 1 , key
elif tag in words :
log . debug ( ' Found %s in %s ' , ( tag , cur_file ) )
return 1 , key
if list ( set ( [ key ] ) & set ( words ) ) :
log . debug ( ' Found %s in %s ' , ( tag , cur_file ) )
return 1 , key
return 0 , None
def guessLooseScore ( self , quality , extra = None ) :
@ -308,7 +331,22 @@ class QualityPlugin(Plugin):
return score
def calcScore ( self , score , quality , add_score , threedscore = ( 0 , None ) ) :
def guessSizeScore ( self , quality , size = None ) :
score = 0
if size :
if tryInt ( quality [ ' size_min ' ] ) < = tryInt ( size ) < = tryInt ( quality [ ' size_max ' ] ) :
log . debug ( ' Found %s via release size: %s MB < %s MB < %s MB ' , ( quality [ ' identifier ' ] , quality [ ' size_min ' ] , size , quality [ ' size_max ' ] ) )
score + = 5
else :
score - = 5
return score
def calcScore ( self , score , quality , add_score , threedscore = ( 0 , None ) , penalty = True ) :
score [ quality [ ' identifier ' ] ] [ ' score ' ] + = add_score
@ -325,32 +363,85 @@ class QualityPlugin(Plugin):
for q in self . qualities :
self . cached_order [ q . get ( ' identifier ' ) ] = self . qualities . index ( q )
if add_score != 0 :
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
def isFinish ( self , quality , profile ) :
if not isinstance ( profile , dict ) or not profile . get ( ' qualities ' ) :
return False
try :
quality_order = [ i for i , identifier in enumerate ( profile [ ' qualities ' ] ) if identifier == quality [ ' identifier ' ] and bool ( profile [ ' 3d ' ] [ i ] if profile . get ( ' 3d ' ) else 0 ) == bool ( quality . get ( ' is_3d ' , 0 ) ) ] [ 0 ]
return profile [ ' finish ' ] [ quality_order ]
except :
return False
def isHigher ( self , quality , compare_with , profile = None ) :
if not isinstance ( profile , dict ) or not profile . get ( ' qualities ' ) :
profile = { ' qualities ' : self . order }
# Try to find quality in profile, if not found: a quality we do not want is lower than anything else
try :
quality_order = [ i for i , identifier in enumerate ( profile [ ' qualities ' ] ) if identifier == quality [ ' identifier ' ] and bool ( profile [ ' 3d ' ] [ i ] if profile . get ( ' 3d ' ) else 0 ) == bool ( quality . get ( ' is_3d ' , 0 ) ) ] [ 0 ]
except :
log . debug ( ' Quality %s not found in profile identifiers %s ' , ( quality [ ' identifier ' ] + ( ' 3D ' if quality . get ( ' is_3d ' , 0 ) else ' ' ) , \
[ identifier + ( ' 3D ' if ( profile [ ' 3d ' ] [ i ] if profile . get ( ' 3d ' ) else 0 ) else ' ' ) for i , identifier in enumerate ( profile [ ' qualities ' ] ) ] ) )
return ' lower '
# Try to find compare quality in profile, if not found: anything is higher than a not wanted quality
try :
compare_order = [ i for i , identifier in enumerate ( profile [ ' qualities ' ] ) if identifier == compare_with [ ' identifier ' ] and bool ( profile [ ' 3d ' ] [ i ] if profile . get ( ' 3d ' ) else 0 ) == bool ( compare_with . get ( ' is_3d ' , 0 ) ) ] [ 0 ]
except :
log . debug ( ' Compare quality %s not found in profile identifiers %s ' , ( compare_with [ ' identifier ' ] + ( ' 3D ' if compare_with . get ( ' is_3d ' , 0 ) else ' ' ) , \
[ identifier + ( ' 3D ' if ( profile [ ' 3d ' ] [ i ] if profile . get ( ' 3d ' ) else 0 ) else ' ' ) for i , identifier in enumerate ( profile [ ' qualities ' ] ) ] ) )
return ' higher '
# Note to self: a lower number means higher quality
if quality_order > compare_order :
return ' lower '
elif quality_order == compare_order :
return ' equal '
else :
return ' higher '
def doTest ( self ) :
tests = {
' Movie Name (1999)-DVD-Rip.avi ' : ' dvdrip ' ,
' Movie Name 1999 720p Bluray.mkv ' : ' 720p ' ,
' Movie Name 1999 BR-Rip 720p.avi ' : ' brrip ' ,
' Movie Name 1999 720p Web Rip.avi ' : ' scr ' ,
' Movie Name 1999 Web DL.avi ' : ' brrip ' ,
' Movie.Name.1999.1080p.WEBRip.H264-Group ' : ' scr ' ,
' Movie.Name.1999.DVDRip-Group ' : ' dvdrip ' ,
' Movie.Name.1999.DVD-Rip-Group ' : ' dvdrip ' ,
' Movie.Name.1999.DVD-R-Group ' : ' dvdr ' ,
' Movie.Name.Camelie.1999.720p.BluRay.x264-Group ' : ' 720p ' ,
' Movie.Name.2008.German.DL.AC3.1080p.BluRay.x264-Group ' : ' 1080p ' ,
' Movie.Name.2004.GERMAN.AC3D.DL.1080p.BluRay.x264-Group ' : ' 1080p ' ,
' Movie Name (1999)-DVD-Rip.avi ' : { ' size ' : 700 , ' quality ' : ' dvdrip ' } ,
' Movie Name 1999 720p Bluray.mkv ' : { ' size ' : 4200 , ' quality ' : ' 720p ' } ,
' Movie Name 1999 BR-Rip 720p.avi ' : { ' size ' : 1000 , ' quality ' : ' brrip ' } ,
' Movie Name 1999 720p Web Rip.avi ' : { ' size ' : 1200 , ' quality ' : ' scr ' } ,
' Movie Name 1999 Web DL.avi ' : { ' size ' : 800 , ' quality ' : ' brrip ' } ,
' Movie.Name.1999.1080p.WEBRip.H264-Group ' : { ' size ' : 1500 , ' quality ' : ' scr ' } ,
' Movie.Name.1999.DVDRip-Group ' : { ' size ' : 750 , ' quality ' : ' dvdrip ' } ,
' Movie.Name.1999.DVD-Rip-Group ' : { ' size ' : 700 , ' quality ' : ' dvdrip ' } ,
' Movie.Name.1999.DVD-R-Group ' : { ' size ' : 4500 , ' quality ' : ' dvdr ' } ,
' Movie.Name.Camelie.1999.720p.BluRay.x264-Group ' : { ' size ' : 5500 , ' quality ' : ' 720p ' } ,
' Movie.Name.2008.German.DL.AC3.1080p.BluRay.x264-Group ' : { ' size ' : 8500 , ' extra ' : { ' resolution_width ' : 1920 , ' resolution_height ' : 1080 } , ' quality ' : ' 1080p ' } ,
' Movie.Name.2004.GERMAN.AC3D.DL.1080p.BluRay.x264-Group ' : { ' size ' : 8000 , ' quality ' : ' 1080p ' } ,
' Movie.Name.2013.BR-Disk-Group.iso ' : { ' size ' : 48000 , ' quality ' : ' bd50 ' } ,
' Movie.Name.2013.2D+3D.BR-Disk-Group.iso ' : { ' size ' : 52000 , ' quality ' : ' bd50 ' , ' is_3d ' : True } ,
' Movie.Rising.Name.Girl.2011.NTSC.DVD9-GroupDVD ' : { ' size ' : 7200 , ' quality ' : ' dvdr ' } ,
' Movie Name (2013) 2D + 3D ' : { ' size ' : 49000 , ' quality ' : ' bd50 ' , ' is_3d ' : True } ,
' Movie Monuments 2013 BrRip 1080p ' : { ' size ' : 1800 , ' quality ' : ' brrip ' } ,
' Movie Monuments 2013 BrRip 720p ' : { ' size ' : 1300 , ' quality ' : ' brrip ' } ,
' The.Movie.2014.3D.1080p.BluRay.AVC.DTS-HD.MA.5.1-GroupName ' : { ' size ' : 30000 , ' quality ' : ' bd50 ' , ' is_3d ' : True } ,
' /home/namehou/Movie Monuments (2013)/Movie Monuments.mkv ' : { ' size ' : 4500 , ' quality ' : ' 1080p ' , ' is_3d ' : False } ,
' /home/namehou/Movie Monuments (2013)/Movie Monuments Full-OU.mkv ' : { ' size ' : 4500 , ' quality ' : ' 1080p ' , ' is_3d ' : True }
}
correct = 0
for name in tests :
success = self . guess ( [ name ] ) . get ( ' identifier ' ) == tests [ name ]
test_quality = self . guess ( files = [ name ] , extra = tests [ name ] . get ( ' extra ' , None ) , size = tests [ name ] . get ( ' size ' , None ) ) or { }
success = test_quality . get ( ' identifier ' ) == tests [ name ] [ ' quality ' ] and test_quality . get ( ' is_3d ' ) == tests [ name ] . get ( ' is_3d ' , False )
if not success :
log . error ( ' %s failed check, thinks it \' s %s ' , ( name , self . guess ( [ name ] ) . get ( ' identifier ' ) ) )
log . error ( ' %s failed check, thinks it \' s %s ' , ( name , test_quality . get ( ' identifier ' ) ) )
correct + = success