mirror of
https://github.com/clinton-hall/nzbToMedia.git
synced 2024-11-14 17:40:24 -08:00
f05b09f349
Updates rarfile to 3.1 Updates stevedore to 3.5.0 Updates appdirs to 1.4.4 Updates click to 8.1.3 Updates decorator to 5.1.1 Updates dogpile.cache to 1.1.8 Updates pbr to 5.11.0 Updates pysrt to 1.1.2 Updates pytz to 2022.6 Adds importlib-metadata version 3.1.1 Adds typing-extensions version 4.1.1 Adds zipp version 3.11.0
259 lines
9.5 KiB
Python
259 lines
9.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
This module provides the default implementation of the `compute_score` parameter in
|
|
:meth:`~subliminal.core.ProviderPool.download_best_subtitles` and :func:`~subliminal.core.download_best_subtitles`.
|
|
|
|
.. note::
|
|
|
|
To avoid unnecessary dependency on `sympy <http://www.sympy.org/>`_ and boost subliminal's import time, the
|
|
resulting scores are hardcoded here and manually updated when the set of equations change.
|
|
|
|
Available matches:
|
|
|
|
* hash
|
|
* title
|
|
* year
|
|
* country
|
|
* series
|
|
* season
|
|
* episode
|
|
* release_group
|
|
* streaming_service
|
|
* source
|
|
* audio_codec
|
|
* resolution
|
|
* hearing_impaired
|
|
* video_codec
|
|
* series_imdb_id
|
|
* imdb_id
|
|
* tvdb_id
|
|
|
|
"""
|
|
from __future__ import division, print_function
|
|
import logging
|
|
|
|
from .video import Episode, Movie
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
#: Scores for episodes
|
|
episode_scores = {'hash': 809, 'series': 405, 'year': 135, 'country': 135, 'season': 45, 'episode': 45,
|
|
'release_group': 15, 'streaming_service': 15, 'source': 7, 'audio_codec': 3, 'resolution': 2,
|
|
'video_codec': 2, 'hearing_impaired': 1}
|
|
|
|
#: Scores for movies
|
|
movie_scores = {'hash': 269, 'title': 135, 'year': 45, 'country': 45, 'release_group': 15, 'streaming_service': 15,
|
|
'source': 7, 'audio_codec': 3, 'resolution': 2, 'video_codec': 2, 'hearing_impaired': 1}
|
|
|
|
#: All scores names
|
|
score_keys = set([s for s in episode_scores.keys()] + [s for s in movie_scores.keys()])
|
|
|
|
#: Equivalent release groups
|
|
equivalent_release_groups = ({'LOL', 'DIMENSION'}, {'ASAP', 'IMMERSE', 'FLEET'}, {'AVS', 'SVA'})
|
|
|
|
|
|
def get_equivalent_release_groups(release_group):
|
|
"""Get all the equivalents of the given release group.
|
|
|
|
:param str release_group: the release group to get the equivalents of.
|
|
:return: the equivalent release groups.
|
|
:rtype: set
|
|
|
|
"""
|
|
for equivalent_release_group in equivalent_release_groups:
|
|
if release_group in equivalent_release_group:
|
|
return equivalent_release_group
|
|
|
|
return {release_group}
|
|
|
|
|
|
def get_scores(video):
|
|
"""Get the scores dict for the given `video`.
|
|
|
|
This will return either :data:`episode_scores` or :data:`movie_scores` based on the type of the `video`.
|
|
|
|
:param video: the video to compute the score against.
|
|
:type video: :class:`~subliminal.video.Video`
|
|
:return: the scores dict.
|
|
:rtype: dict
|
|
|
|
"""
|
|
if isinstance(video, Episode):
|
|
return episode_scores
|
|
elif isinstance(video, Movie):
|
|
return movie_scores
|
|
|
|
raise ValueError('video must be an instance of Episode or Movie')
|
|
|
|
|
|
def compute_score(subtitle, video, hearing_impaired=None):
|
|
"""Compute the score of the `subtitle` against the `video` with `hearing_impaired` preference.
|
|
|
|
:func:`compute_score` uses the :meth:`Subtitle.get_matches <subliminal.subtitle.Subtitle.get_matches>` method and
|
|
applies the scores (either from :data:`episode_scores` or :data:`movie_scores`) after some processing.
|
|
|
|
:param subtitle: the subtitle to compute the score of.
|
|
:type subtitle: :class:`~subliminal.subtitle.Subtitle`
|
|
:param video: the video to compute the score against.
|
|
:type video: :class:`~subliminal.video.Video`
|
|
:param bool hearing_impaired: hearing impaired preference.
|
|
:return: score of the subtitle.
|
|
:rtype: int
|
|
|
|
"""
|
|
logger.info('Computing score of %r for video %r with %r', subtitle, video, dict(hearing_impaired=hearing_impaired))
|
|
|
|
# get the scores dict
|
|
scores = get_scores(video)
|
|
logger.debug('Using scores %r', scores)
|
|
|
|
# get the matches
|
|
matches = subtitle.get_matches(video)
|
|
logger.debug('Found matches %r', matches)
|
|
|
|
# on hash match, discard everything else
|
|
if 'hash' in matches:
|
|
logger.debug('Keeping only hash match')
|
|
matches &= {'hash'}
|
|
|
|
# handle equivalent matches
|
|
if isinstance(video, Episode):
|
|
if 'title' in matches:
|
|
logger.debug('Adding title match equivalent')
|
|
matches.add('episode')
|
|
if 'series_imdb_id' in matches:
|
|
logger.debug('Adding series_imdb_id match equivalent')
|
|
matches |= {'series', 'year', 'country'}
|
|
if 'imdb_id' in matches:
|
|
logger.debug('Adding imdb_id match equivalents')
|
|
matches |= {'series', 'year', 'country', 'season', 'episode'}
|
|
if 'tvdb_id' in matches:
|
|
logger.debug('Adding tvdb_id match equivalents')
|
|
matches |= {'series', 'year', 'country', 'season', 'episode'}
|
|
if 'series_tvdb_id' in matches:
|
|
logger.debug('Adding series_tvdb_id match equivalents')
|
|
matches |= {'series', 'year', 'country'}
|
|
elif isinstance(video, Movie):
|
|
if 'imdb_id' in matches:
|
|
logger.debug('Adding imdb_id match equivalents')
|
|
matches |= {'title', 'year', 'country'}
|
|
|
|
# handle hearing impaired
|
|
if hearing_impaired is not None and subtitle.hearing_impaired == hearing_impaired:
|
|
logger.debug('Matched hearing_impaired')
|
|
matches.add('hearing_impaired')
|
|
|
|
# compute the score
|
|
score = sum((scores.get(match, 0) for match in matches))
|
|
logger.info('Computed score %r with final matches %r', score, matches)
|
|
|
|
# ensure score is within valid bounds
|
|
assert 0 <= score <= scores['hash'] + scores['hearing_impaired']
|
|
|
|
return score
|
|
|
|
|
|
def solve_episode_equations():
|
|
from sympy import Eq, solve, symbols
|
|
|
|
hash, series, year, country, season, episode = symbols('hash series year country season episode')
|
|
release_group, streaming_service, source = symbols('release_group streaming_service source')
|
|
audio_codec, resolution, video_codec = symbols('audio_codec resolution video_codec')
|
|
hearing_impaired = symbols('hearing_impaired')
|
|
|
|
equations = [
|
|
# hash is best
|
|
Eq(hash, series + year + country + season + episode +
|
|
release_group + streaming_service + source + audio_codec + resolution + video_codec),
|
|
|
|
# series counts for the most part in the total score
|
|
Eq(series, year + country + season + episode + release_group + streaming_service + source +
|
|
audio_codec + resolution + video_codec + 1),
|
|
|
|
# year is the second most important part
|
|
Eq(year, season + episode + release_group + streaming_service + source +
|
|
audio_codec + resolution + video_codec + 1),
|
|
|
|
# year counts as much as country
|
|
Eq(year, country),
|
|
|
|
# season is important too
|
|
Eq(season, release_group + streaming_service + source + audio_codec + resolution + video_codec + 1),
|
|
|
|
# episode is equally important to season
|
|
Eq(episode, season),
|
|
|
|
# release group is the next most wanted match
|
|
Eq(release_group, source + audio_codec + resolution + video_codec + 1),
|
|
|
|
# streaming service counts as much as release group
|
|
Eq(release_group, streaming_service),
|
|
|
|
# source counts as much as audio_codec, resolution and video_codec
|
|
Eq(source, audio_codec + resolution + video_codec),
|
|
|
|
# audio_codec is more valuable than video_codec
|
|
Eq(audio_codec, video_codec + 1),
|
|
|
|
# resolution counts as much as video_codec
|
|
Eq(resolution, video_codec),
|
|
|
|
# video_codec is the least valuable match but counts more than the sum of all scoring increasing matches
|
|
Eq(video_codec, hearing_impaired + 1),
|
|
|
|
# hearing impaired is only used for score increasing, so put it to 1
|
|
Eq(hearing_impaired, 1),
|
|
]
|
|
|
|
return solve(equations, [hash, series, year, country, season, episode, release_group, streaming_service, source,
|
|
audio_codec, resolution, hearing_impaired, video_codec])
|
|
|
|
|
|
def solve_movie_equations():
|
|
from sympy import Eq, solve, symbols
|
|
|
|
hash, title, year, country, release_group = symbols('hash title year country release_group')
|
|
streaming_service, source, audio_codec, resolution = symbols('streaming_service source audio_codec resolution')
|
|
video_codec, hearing_impaired = symbols('video_codec hearing_impaired')
|
|
|
|
equations = [
|
|
# hash is best
|
|
Eq(hash, title + year + country + release_group + streaming_service +
|
|
source + audio_codec + resolution + video_codec),
|
|
|
|
# title counts for the most part in the total score
|
|
Eq(title, year + country + release_group + streaming_service +
|
|
source + audio_codec + resolution + video_codec + 1),
|
|
|
|
# year is the second most important part
|
|
Eq(year, release_group + streaming_service + source + audio_codec + resolution + video_codec + 1),
|
|
|
|
# year counts as much as country
|
|
Eq(year, country),
|
|
|
|
# release group is the next most wanted match
|
|
Eq(release_group, source + audio_codec + resolution + video_codec + 1),
|
|
|
|
# streaming service counts as much as release group
|
|
Eq(release_group, streaming_service),
|
|
|
|
# source counts as much as audio_codec, resolution and video_codec
|
|
Eq(source, audio_codec + resolution + video_codec),
|
|
|
|
# audio_codec is more valuable than video_codec
|
|
Eq(audio_codec, video_codec + 1),
|
|
|
|
# resolution counts as much as video_codec
|
|
Eq(resolution, video_codec),
|
|
|
|
# video_codec is the least valuable match but counts more than the sum of all scoring increasing matches
|
|
Eq(video_codec, hearing_impaired + 1),
|
|
|
|
# hearing impaired is only used for score increasing, so put it to 1
|
|
Eq(hearing_impaired, 1),
|
|
]
|
|
|
|
return solve(equations, [hash, title, year, country, release_group, streaming_service, source, audio_codec,
|
|
resolution, hearing_impaired, video_codec])
|