mirror of
https://github.com/morpheus65535/bazarr.git
synced 2025-01-06 06:52:07 +08:00
708fbfcd8e
Currently only configurable via manual `data/config/config.ini` text edition. New configurable values are `series_scores` and `movie_scores`. For each config section, the sum of the config values (except hash) must be equal to the hash value plus one (1), otherwise default values will be used (notified via debug log). Hash values are not meant to be modified; the value is shown in `config.ini` for reference. Modifying hash values would imply breaking Bazarr's score logic.
197 lines
6 KiB
Python
197 lines
6 KiB
Python
# coding=utf-8
|
|
|
|
from __future__ import absolute_import
|
|
import logging
|
|
|
|
from subliminal.video import Episode, Movie
|
|
from subliminal.score import get_scores
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
FPS_EQUALITY = ((23.976, 23.98, 24.0),)
|
|
|
|
|
|
def framerate_equal(source, check):
|
|
if source == check:
|
|
return True
|
|
|
|
source = float(source)
|
|
check = float(check)
|
|
if source == check:
|
|
return True
|
|
|
|
for equal_fps_tuple in FPS_EQUALITY:
|
|
if check in equal_fps_tuple and source in equal_fps_tuple:
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
DEFAULT_SCORES = {
|
|
"episode": {
|
|
"hash": 359,
|
|
"series": 180,
|
|
"year": 90,
|
|
"season": 30,
|
|
"episode": 30,
|
|
"release_group": 14,
|
|
"source": 7,
|
|
"audio_codec": 3,
|
|
"resolution": 2,
|
|
"video_codec": 2,
|
|
"streaming_service": 1,
|
|
"hearing_impaired": 1,
|
|
},
|
|
"movie": {
|
|
"hash": 119,
|
|
"title": 60,
|
|
"year": 30,
|
|
"release_group": 13,
|
|
"source": 7,
|
|
"audio_codec": 3,
|
|
"resolution": 2,
|
|
"video_codec": 2,
|
|
"streaming_service": 1,
|
|
"edition": 1,
|
|
"hearing_impaired": 1,
|
|
},
|
|
}
|
|
|
|
|
|
def _check_hash_sum(scores: dict):
|
|
hash_val = scores["hash"]
|
|
rest_sum = sum(val for key, val in scores.items() if key != "hash")
|
|
logger.debug("Hash value: %s -> Rest sum: %s", hash_val, rest_sum)
|
|
return rest_sum - 1 == hash_val
|
|
|
|
|
|
class ComputeScore:
|
|
def __init__(self, scores=None):
|
|
if scores:
|
|
valid = True
|
|
for val in scores.values():
|
|
if not _check_hash_sum(val):
|
|
logger.debug("Scores sum - 1 is not equal to hash. Using defaults")
|
|
self._scores = DEFAULT_SCORES
|
|
valid = False
|
|
break
|
|
|
|
if valid is True:
|
|
self._scores = scores
|
|
else:
|
|
self._scores = DEFAULT_SCORES
|
|
|
|
# Hash values should be the same. Update from defaults to ensure it
|
|
for key in self._scores.keys():
|
|
self._scores[key]["hash"] = DEFAULT_SCORES[key]["hash"]
|
|
|
|
def __call__(self, matches, subtitle, video, hearing_impaired=None):
|
|
scores = self._scores[video.__class__.__name__.lower()]
|
|
logger.debug("Scores to use for %s: %s", video, scores)
|
|
|
|
is_episode = isinstance(video, Episode)
|
|
is_movie = isinstance(video, Movie)
|
|
|
|
episode_hash_valid_if = {"series", "season", "episode", "source"}
|
|
movie_hash_valid_if = {"video_codec", "source"}
|
|
|
|
orig_matches = matches.copy()
|
|
|
|
# on hash match, discard everything else
|
|
if subtitle.hash_verifiable and "hash" in matches:
|
|
# hash is error-prone, try to fix that
|
|
hash_valid_if = episode_hash_valid_if if is_episode else movie_hash_valid_if
|
|
|
|
# don't validate hashes of specials, as season and episode tend to be wrong
|
|
if is_movie or not video.is_special:
|
|
if hash_valid_if <= set(matches):
|
|
# series, season and episode matched, hash is valid
|
|
logger.debug(
|
|
"%r: Using valid hash, as %s are correct (%r) and (%r)",
|
|
subtitle,
|
|
hash_valid_if,
|
|
matches,
|
|
video,
|
|
)
|
|
matches &= {"hash"}
|
|
else:
|
|
# no match, invalidate hash
|
|
logger.debug(
|
|
"%r: Ignoring hash as other matches are wrong (missing: %r) and (%r)",
|
|
subtitle,
|
|
hash_valid_if - matches,
|
|
video,
|
|
)
|
|
matches -= {"hash"}
|
|
elif "hash" in matches:
|
|
logger.debug(
|
|
"%r: Hash not verifiable for this provider. Keeping it", subtitle
|
|
)
|
|
matches &= {"hash"}
|
|
|
|
# handle equivalent matches
|
|
eq_matches = set()
|
|
if is_episode:
|
|
_episode_checks(video, eq_matches, matches)
|
|
elif is_movie and "imdb_id" in matches:
|
|
logger.debug("Adding imdb_id match equivalents")
|
|
eq_matches |= {"title", "year"}
|
|
|
|
matches |= eq_matches
|
|
|
|
# handle hearing impaired
|
|
if (
|
|
hearing_impaired is not None
|
|
and subtitle.hearing_impaired == hearing_impaired
|
|
):
|
|
logger.debug("Matched hearing_impaired")
|
|
matches.add("hearing_impaired")
|
|
orig_matches.add("hearing_impaired")
|
|
|
|
# compute the score
|
|
score = sum((scores.get(match, 0) for match in matches))
|
|
logger.info(
|
|
"%r: Computed score %r with final matches %r", subtitle, score, matches
|
|
)
|
|
|
|
score_without_hash = sum(
|
|
(
|
|
scores.get(match, 0)
|
|
for match in orig_matches | eq_matches
|
|
if match != "hash"
|
|
)
|
|
)
|
|
|
|
return score, score_without_hash
|
|
|
|
|
|
def _episode_checks(video, eq_matches, matches):
|
|
if "title" in matches:
|
|
logger.debug("Adding title match equivalent")
|
|
eq_matches.add("episode")
|
|
if "series_imdb_id" in matches:
|
|
logger.debug("Adding series_imdb_id match equivalent")
|
|
eq_matches |= {"series", "year"}
|
|
if "imdb_id" in matches:
|
|
logger.debug("Adding imdb_id match equivalents")
|
|
eq_matches |= {"series", "year", "season", "episode"}
|
|
if "tvdb_id" in matches:
|
|
logger.debug("Adding tvdb_id match equivalents")
|
|
eq_matches |= {"series", "year", "season", "episode", "title"}
|
|
if "series_tvdb_id" in matches:
|
|
logger.debug("Adding series_tvdb_id match equivalents")
|
|
eq_matches |= {"series", "year"}
|
|
|
|
# specials
|
|
if (
|
|
video.is_special
|
|
and "title" in matches
|
|
and "series" in matches
|
|
and "year" in matches
|
|
):
|
|
logger.debug("Adding special title match equivalent")
|
|
eq_matches |= {"season", "episode"}
|
|
|
|
|
|
compute_score = ComputeScore()
|