Added the option to skip mismatched fps to Titulky

This commit is contained in:
Samuel Bartík 2024-10-09 01:38:31 +02:00 committed by GitHub
parent a4873fc0f5
commit f296ba5336
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 69 additions and 4 deletions

View file

@ -328,6 +328,7 @@ validators = [
Validator('titulky.username', must_exist=True, default='', is_type_of=str, cast=str), Validator('titulky.username', must_exist=True, default='', is_type_of=str, cast=str),
Validator('titulky.password', must_exist=True, default='', is_type_of=str, cast=str), Validator('titulky.password', must_exist=True, default='', is_type_of=str, cast=str),
Validator('titulky.approved_only', must_exist=True, default=False, is_type_of=bool), Validator('titulky.approved_only', must_exist=True, default=False, is_type_of=bool),
Validator('titulky.skip_wrong_fps', must_exist=True, default=False, is_type_of=bool),
# embeddedsubtitles section # embeddedsubtitles section
Validator('embeddedsubtitles.included_codecs', must_exist=True, default=[], is_type_of=list), Validator('embeddedsubtitles.included_codecs', must_exist=True, default=[], is_type_of=list),

View file

@ -284,6 +284,7 @@ def get_providers_auth():
'username': settings.titulky.username, 'username': settings.titulky.username,
'password': settings.titulky.password, 'password': settings.titulky.password,
'approved_only': settings.titulky.approved_only, 'approved_only': settings.titulky.approved_only,
'skip_wrong_fps': settings.titulky.skip_wrong_fps,
}, },
'titlovi': { 'titlovi': {
'username': settings.titlovi.username, 'username': settings.titlovi.username,

View file

@ -24,6 +24,8 @@ from subliminal_patch.providers.mixins import ProviderSubtitleArchiveMixin
from subliminal_patch.subtitle import Subtitle, guess_matches from subliminal_patch.subtitle import Subtitle, guess_matches
from subliminal_patch.score import framerate_equal
from dogpile.cache.api import NO_VALUE from dogpile.cache.api import NO_VALUE
from subzero.language import Language from subzero.language import Language
@ -53,6 +55,8 @@ class TitulkySubtitle(Subtitle):
approved, approved,
page_link, page_link,
download_link, download_link,
fps,
skip_wrong_fps,
asked_for_episode=None): asked_for_episode=None):
super().__init__(language, page_link=page_link) super().__init__(language, page_link=page_link)
@ -67,6 +71,8 @@ class TitulkySubtitle(Subtitle):
self.page_link = page_link self.page_link = page_link
self.uploader = uploader self.uploader = uploader
self.download_link = download_link self.download_link = download_link
self.fps = fps if skip_wrong_fps else None # This attribute should be ignored if skip_wrong_fps is false
self.skip_wrong_fps = skip_wrong_fps
self.asked_for_episode = asked_for_episode self.asked_for_episode = asked_for_episode
self.matches = None self.matches = None
@ -78,6 +84,10 @@ class TitulkySubtitle(Subtitle):
matches = set() matches = set()
media_type = 'movie' if isinstance(video, Movie) else 'episode' media_type = 'movie' if isinstance(video, Movie) else 'episode'
if self.skip_wrong_fps and video.fps and self.fps and not framerate_equal(video.fps, self.fps):
logger.debug(f"Titulky.com: Wrong FPS (expected: {video.fps}, got: {self.fps}, lowering score massively)")
return set()
if media_type == 'episode': if media_type == 'episode':
# match imdb_id of a series # match imdb_id of a series
if video.series_imdb_id and video.series_imdb_id == self.imdb_id: if video.series_imdb_id and video.series_imdb_id == self.imdb_id:
@ -120,16 +130,19 @@ class TitulkyProvider(Provider, ProviderSubtitleArchiveMixin):
def __init__(self, def __init__(self,
username=None, username=None,
password=None, password=None,
approved_only=None): approved_only=None,
skip_wrong_fps=None):
if not all([username, password]): if not all([username, password]):
raise ConfigurationError("Username and password must be specified!") raise ConfigurationError("Username and password must be specified!")
if type(approved_only) is not bool: if type(approved_only) is not bool:
raise ConfigurationError(f"Approved_only {approved_only} must be a boolean!") raise ConfigurationError(f"Approved_only {approved_only} must be a boolean!")
if type(skip_wrong_fps) is not bool:
raise ConfigurationError(f"Skip_wrong_fps {skip_wrong_fps} must be a boolean!")
self.username = username self.username = username
self.password = password self.password = password
self.approved_only = approved_only self.approved_only = approved_only
self.skip_wrong_fps = skip_wrong_fps
self.session = None self.session = None
@ -268,6 +281,48 @@ class TitulkyProvider(Provider, ProviderSubtitleArchiveMixin):
return result return result
# Retrieves the fps value given subtitles id from the details page and caches it.
def retrieve_subtitles_fps(self, subtitles_id):
cache_key = f"titulky_subs-{subtitles_id}_fps"
cached_fps_value = cache.get(cache_key)
if(cached_fps_value != NO_VALUE):
logger.debug(f"Titulky.com: Reusing cached fps value {cached_fps_value} for subtitles with id {subtitles_id}")
return cached_fps_value
params = {
'action': 'detail',
'id': subtitles_id
}
browse_url = self.build_url(params)
html_src = self.fetch_page(browse_url, allow_redirects=True)
browse_page_soup = ParserBeautifulSoup(html_src, ['lxml', 'html.parser'])
fps_container = browse_page_soup.select_one("div.ulozil:has(> img[src='img/ico/Movieroll.png'])")
if(fps_container is None):
logger.debug("Titulky.com: Could not manage to find the FPS container in the details page")
cache.set(cache_key, None)
return None
fps_text_components = fps_container.get_text(strip=True).split()
# Check if the container contains valid fps data
if(len(fps_text_components) < 2 or fps_text_components[1].lower() != "fps"):
logger.debug(f"Titulky.com: Could not determine FPS value for subtitles with id {subtitles_id}")
cache.set(cache_key, None)
return None
fps_text = fps_text_components[0].replace(",", ".") # Fix decimal comma to decimal point
try:
fps = float(fps_text)
logger.debug(f"Titulky.com: Retrieved FPS value {fps} from details page for subtitles with id {subtitles_id}")
cache.set(cache_key, fps)
return fps
except:
logger.debug(f"Titulky.com: There was an error parsing FPS value string for subtitles with id {subtitles_id}")
cache.set(cache_key, None)
return None
""" """
There are multiple ways to find substitles on Titulky.com, however we are There are multiple ways to find substitles on Titulky.com, however we are
going to utilize a page that lists all available subtitles for all episodes in a season going to utilize a page that lists all available subtitles for all episodes in a season
@ -377,7 +432,8 @@ class TitulkyProvider(Provider, ProviderSubtitleArchiveMixin):
'language': sub_language, 'language': sub_language,
'uploader': uploader, 'uploader': uploader,
'details_link': details_link, 'details_link': details_link,
'download_link': download_link 'download_link': download_link,
'fps': self.retrieve_subtitles_fps(sub_id) if skip_wrong_fps else None,
} }
# If this row contains the first subtitles to an episode number, # If this row contains the first subtitles to an episode number,
@ -413,7 +469,9 @@ class TitulkyProvider(Provider, ProviderSubtitleArchiveMixin):
sub_info['approved'], sub_info['approved'],
sub_info['details_link'], sub_info['details_link'],
sub_info['download_link'], sub_info['download_link'],
asked_for_episode=(media_type is SubtitlesType.EPISODE) sub_info['fps'],
self.skip_wrong_fps,
asked_for_episode=(media_type is SubtitlesType.EPISODE),
) )
subtitles.append(subtitle_instance) subtitles.append(subtitle_instance)

View file

@ -517,6 +517,11 @@ export const ProviderList: Readonly<ProviderInfo[]> = [
key: "approved_only", key: "approved_only",
name: "Skip unapproved subtitles", name: "Skip unapproved subtitles",
}, },
{
type: "switch",
key: "skip_wrong_fps",
name: "Skip subtitles with mismatched fps to video's",
},
], ],
}, },
{ {