mirror of
https://github.com/SickGear/SickGear.git
synced 2025-01-05 17:43:37 +00:00
Add parse media content to determine quality.
Determine quality before making final assumptions during re-scan, force update, pp and other processes. Add a postprocess folder name validation.
This commit is contained in:
parent
92bf6bb6d6
commit
70a8b47b44
5 changed files with 141 additions and 87 deletions
|
@ -17,6 +17,8 @@
|
|||
* Update Beautiful Soup 4.3.2 to 4.4.0 (r390)
|
||||
* Update Hachoir library 1.3.3 to 1.3.4 (r1383)
|
||||
* Change configure quiet option in Hachoir to suppress warnings (add ref:hacks.txt)
|
||||
* Add parse media content to determine quality before making final assumptions during re-scan, update, pp
|
||||
* Add a postprocess folder name validation
|
||||
|
||||
|
||||
### 0.10.0 (2015-08-06 11:05:00 UTC)
|
||||
|
|
|
@ -230,6 +230,45 @@ class Quality:
|
|||
else:
|
||||
return Quality.UNKNOWN
|
||||
|
||||
@staticmethod
|
||||
def fileQuality(filename):
|
||||
|
||||
from sickbeard import encodingKludge as ek
|
||||
if ek.ek(os.path.isfile, filename):
|
||||
|
||||
from hachoir_parser import createParser
|
||||
from hachoir_metadata import extractMetadata
|
||||
from hachoir_core.stream import InputStreamError
|
||||
|
||||
parser = height = None
|
||||
try:
|
||||
parser = createParser(filename)
|
||||
except InputStreamError as e:
|
||||
logger.log('Hachoir can\'t parse file content quality because it found error: %s' % e.text, logger.WARNING)
|
||||
|
||||
if parser:
|
||||
extract = extractMetadata(parser)
|
||||
if extract:
|
||||
try:
|
||||
height = extract.get('height')
|
||||
except (AttributeError, ValueError):
|
||||
try:
|
||||
for metadata in extract.iterGroups():
|
||||
if re.search('(?i)video', metadata.header):
|
||||
height = metadata.get('height')
|
||||
break
|
||||
except (AttributeError, ValueError):
|
||||
pass
|
||||
|
||||
parser.stream._input.close()
|
||||
|
||||
tolerance = lambda value, percent: int(round(value - (value * percent / 100.0)))
|
||||
if height >= tolerance(352, 5):
|
||||
if height <= tolerance(720, 2):
|
||||
return Quality.SDTV
|
||||
return (Quality.HDTV, Quality.FULLHDTV)[height >= tolerance(1080, 1)]
|
||||
return Quality.UNKNOWN
|
||||
|
||||
@staticmethod
|
||||
def assumeQuality(name):
|
||||
if name.lower().endswith(('.avi', '.mp4')):
|
||||
|
@ -262,10 +301,19 @@ class Quality:
|
|||
@staticmethod
|
||||
def statusFromName(name, assume=True, anime=False):
|
||||
quality = Quality.nameQuality(name, anime)
|
||||
if assume and quality == Quality.UNKNOWN:
|
||||
if assume and Quality.UNKNOWN == quality:
|
||||
quality = Quality.assumeQuality(name)
|
||||
return Quality.compositeStatus(DOWNLOADED, quality)
|
||||
|
||||
@staticmethod
|
||||
def statusFromNameOrFile(file_path, assume=True, anime=False):
|
||||
quality = Quality.nameQuality(file_path, anime)
|
||||
if Quality.UNKNOWN == quality:
|
||||
quality = Quality.fileQuality(file_path)
|
||||
if assume and Quality.UNKNOWN == quality:
|
||||
quality = Quality.assumeQuality(file_path)
|
||||
return Quality.compositeStatus(DOWNLOADED, quality)
|
||||
|
||||
DOWNLOADED = None
|
||||
SNATCHED = None
|
||||
SNATCHED_PROPER = None
|
||||
|
|
|
@ -662,7 +662,7 @@ class PostProcessor(object):
|
|||
continue
|
||||
|
||||
ep_quality = common.Quality.nameQuality(cur_name, ep_obj.show.is_anime)
|
||||
quality_log = u' "%s" quality from the %s %s' % (common.Quality.qualityStrings[ep_quality], thing, cur_name)
|
||||
quality_log = u' "%s" quality parsed from the %s %s' % (common.Quality.qualityStrings[ep_quality], thing, cur_name)
|
||||
|
||||
# if we find a good one then use it
|
||||
if common.Quality.UNKNOWN != ep_quality:
|
||||
|
@ -671,6 +671,12 @@ class PostProcessor(object):
|
|||
else:
|
||||
self._log(u'Found' + quality_log, logger.DEBUG)
|
||||
|
||||
ep_quality = common.Quality.fileQuality(self.file_path)
|
||||
if common.Quality.UNKNOWN != ep_quality:
|
||||
self._log(u'Using "%s" quality parsed from the metadata file content of %s'
|
||||
% (common.Quality.qualityStrings[ep_quality], self.file_name), logger.DEBUG)
|
||||
return ep_quality
|
||||
|
||||
# Try guessing quality from the file name
|
||||
ep_quality = common.Quality.assumeQuality(self.file_name)
|
||||
self._log(u'Using guessed "%s" quality from the file name %s'
|
||||
|
|
|
@ -434,6 +434,10 @@ class ProcessTVShow(object):
|
|||
try_scene_exceptions=True,
|
||||
convert=True).parse(
|
||||
dir_name, cache_result=False)
|
||||
# check we parsed id, ep and season
|
||||
if not (0 < len(parse_result.episode_numbers) and isinstance(parse_result.show.indexerid, int)
|
||||
and isinstance(parse_result.season_number, int)):
|
||||
return False
|
||||
except (InvalidNameException, InvalidShowException):
|
||||
# If the filename doesn't parse, then return false as last
|
||||
# resort. We can assume that unparseable filenames are not
|
||||
|
|
164
sickbeard/tv.py
164
sickbeard/tv.py
|
@ -613,141 +613,137 @@ class TVShow(object):
|
|||
def makeEpFromFile(self, file):
|
||||
|
||||
if not ek.ek(os.path.isfile, file):
|
||||
logger.log(str(self.indexerid) + u": That isn't even a real file dude... " + file)
|
||||
logger.log(u'%s: Not a real file... %s' % (self.indexerid, file))
|
||||
return None
|
||||
|
||||
logger.log(str(self.indexerid) + u": Creating episode object from " + file, logger.DEBUG)
|
||||
logger.log(u'%s: Creating episode object from %s' % (self.indexerid, file), logger.DEBUG)
|
||||
|
||||
try:
|
||||
myParser = NameParser(showObj=self, try_indexers=True)
|
||||
parse_result = myParser.parse(file)
|
||||
my_parser = NameParser(showObj=self, try_indexers=True)
|
||||
parse_result = my_parser.parse(file)
|
||||
except InvalidNameException:
|
||||
logger.log(u"Unable to parse the filename " + file + " into a valid episode", logger.DEBUG)
|
||||
logger.log(u'Unable to parse the filename %s into a valid episode' % file, logger.DEBUG)
|
||||
return None
|
||||
except InvalidShowException:
|
||||
logger.log(u"Unable to parse the filename " + file + " into a valid show", logger.DEBUG)
|
||||
logger.log(u'Unable to parse the filename %s into a valid show' % file, logger.DEBUG)
|
||||
return None
|
||||
|
||||
if not len(parse_result.episode_numbers):
|
||||
logger.log("parse_result: " + str(parse_result))
|
||||
logger.log(u"No episode number found in " + file + ", ignoring it", logger.ERROR)
|
||||
logger.log(u'parse_result: %s' % parse_result)
|
||||
logger.log(u'No episode number found in %s, ignoring it' % file, logger.ERROR)
|
||||
return None
|
||||
|
||||
# for now lets assume that any episode in the show dir belongs to that show
|
||||
season = parse_result.season_number if parse_result.season_number != None else 1
|
||||
season = parse_result.season_number if None is not parse_result.season_number else 1
|
||||
episodes = parse_result.episode_numbers
|
||||
rootEp = None
|
||||
root_ep = None
|
||||
|
||||
sql_l = []
|
||||
for curEpNum in episodes:
|
||||
for cur_ep_num in episodes:
|
||||
|
||||
episode = int(curEpNum)
|
||||
episode = int(cur_ep_num)
|
||||
|
||||
logger.log(
|
||||
str(self.indexerid) + ": " + file + " parsed to " + self.name + " " + str(season) + "x" + str(episode),
|
||||
logger.DEBUG)
|
||||
logger.log(u'%s: %s parsed to %s %sx%s' % (self.indexerid, file, self.name, season, episode), logger.DEBUG)
|
||||
|
||||
checkQualityAgain = False
|
||||
check_quality_again = False
|
||||
same_file = False
|
||||
curEp = self.getEpisode(season, episode)
|
||||
cur_ep = self.getEpisode(season, episode)
|
||||
|
||||
if curEp == None:
|
||||
if None is cur_ep:
|
||||
try:
|
||||
curEp = self.getEpisode(season, episode, file)
|
||||
cur_ep = self.getEpisode(season, episode, file)
|
||||
except exceptions.EpisodeNotFoundException:
|
||||
logger.log(str(self.indexerid) + u": Unable to figure out what this file is, skipping",
|
||||
logger.ERROR)
|
||||
logger.log(u'%s: Unable to figure out what this file is, skipping' % self.indexerid, logger.ERROR)
|
||||
continue
|
||||
|
||||
else:
|
||||
# if there is a new file associated with this ep then re-check the quality
|
||||
if curEp.location and ek.ek(os.path.normpath, curEp.location) != ek.ek(os.path.normpath, file):
|
||||
if cur_ep.location and ek.ek(os.path.normpath, cur_ep.location) != ek.ek(os.path.normpath, file):
|
||||
logger.log(
|
||||
u"The old episode had a different file associated with it, I will re-check the quality based on the new filename " + file,
|
||||
u'The old episode had a different file associated with it, re-checking the quality based on the new filename ' + file,
|
||||
logger.DEBUG)
|
||||
checkQualityAgain = True
|
||||
check_quality_again = True
|
||||
|
||||
with curEp.lock:
|
||||
old_size = curEp.file_size
|
||||
curEp.location = file
|
||||
with cur_ep.lock:
|
||||
old_size = cur_ep.file_size
|
||||
cur_ep.location = file
|
||||
# if the sizes are the same then it's probably the same file
|
||||
if old_size and curEp.file_size == old_size:
|
||||
if old_size and cur_ep.file_size == old_size:
|
||||
same_file = True
|
||||
else:
|
||||
same_file = False
|
||||
|
||||
curEp.checkForMetaFiles()
|
||||
cur_ep.checkForMetaFiles()
|
||||
|
||||
if rootEp == None:
|
||||
rootEp = curEp
|
||||
if None is root_ep:
|
||||
root_ep = cur_ep
|
||||
else:
|
||||
if curEp not in rootEp.relatedEps:
|
||||
rootEp.relatedEps.append(curEp)
|
||||
if cur_ep not in root_ep.relatedEps:
|
||||
root_ep.relatedEps.append(cur_ep)
|
||||
|
||||
# if it's a new file then
|
||||
if not same_file:
|
||||
curEp.release_name = ''
|
||||
cur_ep.release_name = ''
|
||||
|
||||
# if they replace a file on me I'll make some attempt at re-checking the quality unless I know it's the same file
|
||||
if checkQualityAgain and not same_file:
|
||||
newQuality = Quality.nameQuality(file, self.is_anime)
|
||||
logger.log(u"Since this file has been renamed, I checked " + file + " and found quality " +
|
||||
Quality.qualityStrings[newQuality], logger.DEBUG)
|
||||
if newQuality != Quality.UNKNOWN:
|
||||
curEp.status = Quality.compositeStatus(DOWNLOADED, newQuality)
|
||||
|
||||
if check_quality_again and not same_file:
|
||||
new_quality = Quality.nameQuality(file, self.is_anime)
|
||||
if Quality.UNKNOWN == new_quality:
|
||||
new_quality = Quality.fileQuality(file)
|
||||
logger.log(u'Since this file was renamed, file %s was checked and quality "%s" found'
|
||||
% (file, Quality.qualityStrings[new_quality]), logger.DEBUG)
|
||||
if Quality.UNKNOWN != new_quality:
|
||||
cur_ep.status = Quality.compositeStatus(DOWNLOADED, new_quality)
|
||||
|
||||
# check for status/quality changes as long as it's a new file
|
||||
elif not same_file and sickbeard.helpers.isMediaFile(file) and curEp.status not in Quality.DOWNLOADED + [
|
||||
ARCHIVED, IGNORED]:
|
||||
elif not same_file and sickbeard.helpers.isMediaFile(file)\
|
||||
and cur_ep.status not in Quality.DOWNLOADED + [ARCHIVED, IGNORED]:
|
||||
|
||||
oldStatus, oldQuality = Quality.splitCompositeStatus(curEp.status)
|
||||
newQuality = Quality.nameQuality(file, self.is_anime)
|
||||
if newQuality == Quality.UNKNOWN:
|
||||
newQuality = Quality.assumeQuality(file)
|
||||
old_status, old_quality = Quality.splitCompositeStatus(cur_ep.status)
|
||||
new_quality = Quality.nameQuality(file, self.is_anime)
|
||||
if Quality.UNKNOWN == new_quality:
|
||||
new_quality = Quality.fileQuality(file)
|
||||
if Quality.UNKNOWN == new_quality:
|
||||
new_quality = Quality.assumeQuality(file)
|
||||
|
||||
newStatus = None
|
||||
new_status = None
|
||||
|
||||
# if it was snatched and now exists then set the status correctly
|
||||
if oldStatus == SNATCHED and oldQuality <= newQuality:
|
||||
logger.log(u"STATUS: this episode used to be snatched with quality " + Quality.qualityStrings[
|
||||
oldQuality] + u" but a file exists with quality " + Quality.qualityStrings[
|
||||
newQuality] + u" so I'm setting the status to DOWNLOADED", logger.DEBUG)
|
||||
newStatus = DOWNLOADED
|
||||
if SNATCHED == old_status and old_quality <= new_quality:
|
||||
logger.log(u'STATUS: this episode used to be snatched with quality %s but a file exists with quality %s so setting the status to DOWNLOADED'
|
||||
% (Quality.qualityStrings[old_quality], Quality.qualityStrings[new_quality]), logger.DEBUG)
|
||||
new_status = DOWNLOADED
|
||||
|
||||
# if it was snatched proper and we found a higher quality one then allow the status change
|
||||
elif oldStatus == SNATCHED_PROPER and oldQuality < newQuality:
|
||||
logger.log(u"STATUS: this episode used to be snatched proper with quality " + Quality.qualityStrings[
|
||||
oldQuality] + u" but a file exists with quality " + Quality.qualityStrings[
|
||||
newQuality] + u" so I'm setting the status to DOWNLOADED", logger.DEBUG)
|
||||
newStatus = DOWNLOADED
|
||||
elif SNATCHED_PROPER == old_status and old_quality < new_quality:
|
||||
logger.log(u'STATUS: this episode used to be snatched proper with quality %s but a file exists with quality %s so setting the status to DOWNLOADED'
|
||||
% (Quality.qualityStrings[old_quality], Quality.qualityStrings[new_quality]), logger.DEBUG)
|
||||
new_status = DOWNLOADED
|
||||
|
||||
elif oldStatus not in (SNATCHED, SNATCHED_PROPER):
|
||||
newStatus = DOWNLOADED
|
||||
elif old_status not in (SNATCHED, SNATCHED_PROPER):
|
||||
new_status = DOWNLOADED
|
||||
|
||||
if newStatus != None:
|
||||
with curEp.lock:
|
||||
logger.log(u"STATUS: we have an associated file, so setting the status from " + str(
|
||||
curEp.status) + u" to DOWNLOADED/" + str(Quality.statusFromName(file, anime=self.is_anime)),
|
||||
logger.DEBUG)
|
||||
curEp.status = Quality.compositeStatus(newStatus, newQuality)
|
||||
if None is not new_status:
|
||||
with cur_ep.lock:
|
||||
logger.log(u'STATUS: we have an associated file, so setting the status from %s to DOWNLOADED/%s'
|
||||
% (cur_ep.status, Quality.compositeStatus(Quality.DOWNLOADED, new_quality)), logger.DEBUG)
|
||||
cur_ep.status = Quality.compositeStatus(new_status, new_quality)
|
||||
|
||||
with curEp.lock:
|
||||
result = curEp.get_sql()
|
||||
with cur_ep.lock:
|
||||
result = cur_ep.get_sql()
|
||||
if None is not result:
|
||||
sql_l.append(result)
|
||||
|
||||
if 0 < len(sql_l):
|
||||
myDB = db.DBConnection()
|
||||
myDB.mass_action(sql_l)
|
||||
|
||||
my_db = db.DBConnection()
|
||||
my_db.mass_action(sql_l)
|
||||
|
||||
# creating metafiles on the root should be good enough
|
||||
if sickbeard.USE_FAILED_DOWNLOADS and rootEp is not None:
|
||||
with rootEp.lock:
|
||||
rootEp.createMetaFiles()
|
||||
if sickbeard.USE_FAILED_DOWNLOADS and root_ep is not None:
|
||||
with root_ep.lock:
|
||||
root_ep.createMetaFiles()
|
||||
|
||||
return rootEp
|
||||
return root_ep
|
||||
|
||||
def loadFromDB(self, skipNFO=False):
|
||||
|
||||
|
@ -1812,14 +1808,13 @@ class TVEpisode(object):
|
|||
elif sickbeard.helpers.isMediaFile(self.location):
|
||||
# leave propers alone, you have to either post-process them or manually change them back
|
||||
if self.status not in Quality.SNATCHED_PROPER + Quality.DOWNLOADED + Quality.SNATCHED + [ARCHIVED]:
|
||||
logger.log(
|
||||
u"5 Status changes from " + str(self.status) + " to " + str(Quality.statusFromName(self.location)),
|
||||
logger.DEBUG)
|
||||
self.status = Quality.statusFromName(self.location, anime=self.show.is_anime)
|
||||
status_quality = Quality.statusFromNameOrFile(self.location, anime=self.show.is_anime)
|
||||
logger.log(u'(1) Status changes from %s to %s' % (self.status, status_quality), logger.DEBUG)
|
||||
self.status = status_quality
|
||||
|
||||
# shouldn't get here probably
|
||||
else:
|
||||
logger.log(u"6 Status changes from " + str(self.status) + " to " + str(UNKNOWN), logger.DEBUG)
|
||||
logger.log(u"(2) Status changes from " + str(self.status) + " to " + str(UNKNOWN), logger.DEBUG)
|
||||
self.status = UNKNOWN
|
||||
|
||||
def loadFromNFO(self, location):
|
||||
|
@ -1837,11 +1832,10 @@ class TVEpisode(object):
|
|||
|
||||
if self.location != "":
|
||||
|
||||
if self.status == UNKNOWN:
|
||||
if sickbeard.helpers.isMediaFile(self.location):
|
||||
logger.log(u"7 Status changes from " + str(self.status) + " to " + str(
|
||||
Quality.statusFromName(self.location, anime=self.show.is_anime)), logger.DEBUG)
|
||||
self.status = Quality.statusFromName(self.location, anime=self.show.is_anime)
|
||||
if UNKNOWN == self.status and sickbeard.helpers.isMediaFile(self.location):
|
||||
status_quality = Quality.statusFromNameOrFile(self.location, anime=self.show.is_anime)
|
||||
logger.log(u'(3) Status changes from %s to %s' % (self.status, status_quality), logger.DEBUG)
|
||||
self.status = status_quality
|
||||
|
||||
nfoFile = sickbeard.helpers.replaceExtension(self.location, "nfo")
|
||||
logger.log(str(self.show.indexerid) + u": Using NFO name " + nfoFile, logger.DEBUG)
|
||||
|
|
Loading…
Reference in a new issue