Compare commits

...

26 Commits

Author SHA1 Message Date
panni c48aa2b255 release 2.6.5.3152 2019-10-05 04:14:28 +02:00
panni 66859802f9 update readme 2019-10-05 04:13:50 +02:00
panni 433c8e987b back from dev 2019-10-05 04:07:52 +02:00
panni aa477ca48c Merge branch 'develop-2.6' 2019-10-05 04:07:33 +02:00
pannal 65b502afa4 bump dev 2019-09-21 16:25:33 +02:00
pannal 06c0b44589 fix Dicked.get 2019-09-21 16:24:21 +02:00
pannal d651f2cbb7 bump dev 2019-09-20 18:24:16 +02:00
pannal 8b5be8ea4b #676 improve 2019-09-20 18:14:14 +02:00
pannal f4e82c560d core: fix default values of opensubtitles-skip-wrong-fps, use_https; fix #676 2019-09-20 18:10:16 +02:00
panni c23b3e93a6 bump dev 2019-08-31 14:33:04 +02:00
panni de447d2d0b core: fix for determining whether to search under certain circumstances; fixes #666 2019-08-31 14:32:42 +02:00
panni 95b1272018 explicit language=None check 2019-08-25 06:12:48 +02:00
pannal 11d111da7c Update README.md 2019-08-24 04:51:26 +02:00
pannal 638dec0f04 Update README.md 2019-08-24 04:45:45 +02:00
panni a0ab6e406a providers: titlovi: raise ConfigurationError if credentials aren't given 2019-08-22 15:46:38 +02:00
panni 23242c0f52 bump dev 2019-08-22 15:34:45 +02:00
pannal 48bf70e825 Merge pull request #660 from viking1304/develop-2.6
New implementation of Titlovi using API
2019-08-22 15:32:43 +02:00
panni ada0b96872 #664 fix missing language processing of multiple videos refreshed at once 2019-08-22 14:58:28 +02:00
panni 0e4917bba9 #661 further improvements 2019-08-13 18:06:06 +02:00
panni 8169d31e86 #661 fix bad condition 2019-08-13 13:01:05 +02:00
panni 75b83aa163 #661 fix match strictness when determining preexisting external subtitles 2019-08-13 12:56:08 +02:00
viking1304 d2022de970 Removed titlovi from AntiCaptcha lablel 2019-08-12 21:02:51 +02:00
viking1304 8db1cdacb4 Revert "Disable provider Titlovi if user and password are not set"
This reverts commit 527d171a6a.
2019-08-09 19:01:08 +02:00
viking1304 527d171a6a Disable provider Titlovi if user and password are not set 2019-08-09 18:54:35 +02:00
viking1304 20620cfa7e New implentation of Titlovi using API 2019-08-09 18:22:42 +02:00
panni 4d03ca078d back to dev 2019-08-09 03:38:54 +02:00
15 changed files with 248 additions and 212 deletions
+18
View File
@@ -1,3 +1,21 @@
2.6.5.3124
subscene, addic7ed and titlovi
- either of those providers might impose a reCAPTCHA verification. In order to use those providers, please create an account at an AntiCaptcha service ([anti-captcha.com](http://getcaptchasolution.com/kkvviom7nh) or [deathbycaptcha.com](http://deathbycaptcha.com)), add funds, then supply your credentials/apikey in the configuration
Changelog
- core: http: fallback to default DNS when normal resolving fails; fixes #657
- core: extract embedded/menu: fix detection of unknown streams; don't use unknown streams if a known language was previously found
- core: language: use replacement map from bazarr
- providers: titlovi: fix matching
- providers: subscene: fix unknown language code error when "empty" result is returned
- providers: subscene: add support for pt-BR (based on Diaoul/subliminal@b22cf08)
- providers: subscene: explicitly set account filters for languages
- providers: subscene: limit alternative searches to 3; set throttle to 8
- providers: subscene: move login/cookies to initialization sequence
- submod: generic: en: fix ";='s
2.6.5.3109
subscene, addic7ed and titlovi
+6 -5
View File
@@ -843,11 +843,8 @@ class Config(object):
providers = property(get_providers)
def get_provider_settings(self):
os_use_https = self.advanced.providers.opensubtitles.use_https \
if self.advanced.providers.opensubtitles.use_https is not None else True
os_skip_wrong_fps = self.advanced.providers.opensubtitles.skip_wrong_fps \
if self.advanced.providers.opensubtitles.skip_wrong_fps is not None else True
os_use_https = self.advanced.providers.opensubtitles.get("use_https", True)
os_skip_wrong_fps = self.advanced.providers.opensubtitles.get("skip_wrong_fps", True)
provider_settings = {'addic7ed': {'username': Prefs['provider.addic7ed.username'],
'password': Prefs['provider.addic7ed.password'],
@@ -866,6 +863,10 @@ class Config(object):
'only_foreign': self.forced_only,
'also_foreign': self.forced_also,
},
'titlovi': {
'username': Prefs['provider.titlovi.username'],
'password': Prefs['provider.titlovi.password'],
},
'napisy24': {
'username': Prefs['provider.napisy24.username'],
'password': Prefs['provider.napisy24.password'],
+12 -11
View File
@@ -33,14 +33,14 @@ def get_missing_languages(video, part):
alpha3_map = {}
if config.ietf_as_alpha3:
for language in languages:
if language.country:
if language and language.country:
alpha3_map[language.alpha3] = language.country
language.country = None
have_languages = video.subtitle_languages.copy()
if config.ietf_as_alpha3:
for language in have_languages:
if language.country:
if language and language.country:
alpha3_map[language.alpha3] = language.country
language.country = None
@@ -53,14 +53,14 @@ def get_missing_languages(video, part):
filter(lambda l: not l.forced, video.subtitle_languages)
if langs:
Log.Debug("We have at least one subtitle for any configured language.")
return False
return set()
elif "External subtitle" in config.any_language_is_enough:
langs = video.subtitle_languages if not not_in_forced else \
langs = video.external_subtitle_languages if not not_in_forced else \
filter(lambda l: not l.forced, video.external_subtitle_languages)
if langs:
Log.Debug("We have at least one external subtitle for any configured language.")
return False
return set()
# all languages are found if we either really have subs for all languages or we only want to have exactly one language
# and we've only found one (the case for a selected language, Prefs['subtitles.only_one'] (one found sub matches any language))
@@ -70,7 +70,7 @@ def get_missing_languages(video, part):
Log.Debug('Only one language was requested, and we\'ve got a subtitle for %s', video)
else:
Log.Debug('All languages %r exist for %s', languages, video)
return False
return set()
# re-add country codes to the missing languages, in case we've removed them above
if config.ietf_as_alpha3:
@@ -106,21 +106,22 @@ def language_hook(provider):
def download_best_subtitles(video_part_map, min_score=0, throttle_time=None, providers=None):
hearing_impaired = Prefs['subtitles.search.hearingImpaired']
languages = set([Language.rebuild(l) for l in config.lang_list])
missing_languages = []
if not languages:
return
use_videos = []
missing_languages = set()
for video, part in video_part_map.iteritems():
if not video.ignore_all:
missing_languages = get_missing_languages(video, part)
p_missing_languages = get_missing_languages(video, part)
else:
missing_languages = languages
p_missing_languages = languages
if missing_languages:
Log.Info(u"%s has missing languages: %s", os.path.basename(video.name), missing_languages)
if p_missing_languages:
Log.Info(u"%s has missing languages: %s", os.path.basename(video.name), p_missing_languages)
refine_video(video, refiner_settings=config.refiner_settings)
use_videos.append(video)
missing_languages.update(p_missing_languages)
# prepare blacklist
blacklist = get_blacklist_from_part_map(video_part_map, languages)
+2 -1
View File
@@ -7,6 +7,7 @@ import helpers
import subtitlehelpers
from config import config as sz_config
from subzero.language import ENDSWITH_LANGUAGECODE_RE
SECONDARY_TAGS = ['forced', 'normal', 'default', 'embedded', 'embedded-forced', 'custom', 'hi', 'cc', 'sdh']
@@ -125,7 +126,7 @@ def find_subtitles(part, ignore_parts_cleanup=None):
root = split_tag[0]
# get associated media file name without language
sub_fn = subtitlehelpers.ENDSWITH_LANGUAGECODE_RE.sub("", root)
sub_fn = ENDSWITH_LANGUAGECODE_RE.sub("", root)
# subtitle basename and basename without possible language tag not found in collected
# media files? kill.
+2 -1
View File
@@ -127,7 +127,8 @@ def prepare_video(pms_video_info, ignore_all=False, hints=None, rating_key=None,
set_existing_languages(video, pms_video_info, external_subtitles=external_subtitles,
embedded_subtitles=embedded_subtitles, known_embedded=known_embedded,
stored_subs=stored_subs, languages=config.lang_list,
only_one=config.only_one, known_metadata_subs=known_metadata_subs)
only_one=config.only_one, known_metadata_subs=known_metadata_subs,
match_strictness=config.ext_match_strictness)
# add video fps info
video.fps = plex_part.fps
+2 -14
View File
@@ -5,6 +5,7 @@ import helpers
from config import config, SUBTITLE_EXTS, TEXT_SUBTITLE_EXTS
from bs4 import UnicodeDammit
from subzero.language import match_ietf_language
class SubtitleHelper(object):
@@ -85,19 +86,6 @@ class VobSubSubtitleHelper(SubtitleHelper):
#####################################################################################################################
IETF_MATCH = ".+\.([^-.]+)(?:-[A-Za-z]+)?$"
ENDSWITH_LANGUAGECODE_RE = re.compile("\.([^-.]{2,3})(?:-[A-Za-z]{2,})?$")
def match_ietf_language(s):
language_match = re.match(".+\.([^\.]+)$" if not helpers.cast_bool(Prefs["subtitles.language.ietf_display"])
else IETF_MATCH, s)
if language_match and len(language_match.groups()) == 1:
language = language_match.groups()[0]
return language
return s
class DefaultSubtitleHelper(SubtitleHelper):
@classmethod
def is_helper_for(cls, filename):
@@ -133,7 +121,7 @@ class DefaultSubtitleHelper(SubtitleHelper):
# Attempt to extract the language from the filename (e.g. Avatar (2009).eng)
# IETF support thanks to
# https://github.com/hpsbranco/LocalMedia.bundle/commit/4fad9aefedece78a1fa96401304351347f644369
lang_part = match_ietf_language(file)
lang_part = match_ietf_language(file, ietf=helpers.cast_bool(Prefs["subtitles.language.ietf_display"]))
if lang_part != file:
language = Locale.Language.Match(lang_part)
elif config.only_one:
+16 -2
View File
@@ -288,7 +288,7 @@
},
{
"id": "anticaptcha.service",
"label": "AntiCaptcha-Service (needs paid account; enables Addic7ed, titlovi)",
"label": "AntiCaptcha-Service (needs paid account; enables Addic7ed)",
"type": "enum",
"values": [
"none",
@@ -409,10 +409,24 @@
},
{
"id": "provider.titlovi.enabled",
"label": "Provider: Enable Titlovi.com (might need AntiCaptcha)",
"label": "Provider: Enable Titlovi.com (User and Password required)",
"type": "bool",
"default": "true"
},
{
"id": "provider.titlovi.username",
"label": "Titlovi Username",
"type": "text",
"default": ""
},
{
"id": "provider.titlovi.password",
"label": "Titlovi Password",
"type": "text",
"option": "hidden",
"default": "",
"secure": "true"
},
{
"id": "provider.legendastv.enabled",
"label": "Provider: Enable Legendas TV (mostly pt-BR; UNRAR NEEDED)",
+2 -2
View File
@@ -13,7 +13,7 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>2.6.5.3124</string>
<string>2.6.5.3152</string>
<key>PlexFrameworkVersion</key>
<string>2</string>
<key>PlexPluginClass</key>
@@ -32,7 +32,7 @@
&lt;h1&gt;Sub-Zero for Plex&lt;/h1&gt;&lt;i&gt;Subtitles done right&lt;/i&gt;
Version 2.6.5.3124
Version 2.6.5.3152
Originally based on @bramwalet's awesome &lt;a href=&quot;https://github.com/bramwalet/Subliminal.bundle&quot;&gt;Subliminal.bundle&lt;/a&gt;
@@ -27,16 +27,6 @@ class TitloviConverter(LanguageReverseConverter):
}
self.codes = set(self.from_titlovi.keys())
# temporary fix, should be removed as soon as API is used
self.lang_from_countrycode = {'ba': ('bos',),
'en': ('eng',),
'hr': ('hrv',),
'mk': ('mkd',),
'rs': ('srp',),
'rsc': ('srp', None, 'Cyrl'),
'si': ('slv',)
}
def convert(self, alpha3, country=None, script=None):
if (alpha3, country, script) in self.to_titlovi:
return self.to_titlovi[(alpha3, country, script)]
@@ -49,9 +39,5 @@ class TitloviConverter(LanguageReverseConverter):
if titlovi in self.from_titlovi:
return self.from_titlovi[titlovi]
# temporary fix, should be removed as soon as API is used
if titlovi in self.lang_from_countrycode:
return self.lang_from_countrycode[titlovi]
raise ConfigurationError('Unsupported language number for titlovi: %s' % titlovi)
@@ -30,7 +30,7 @@ from subliminal.core import guessit, ProviderPool, io, is_windows_special_path,
ThreadPoolExecutor, check_video
from subliminal_patch.exceptions import TooManyRequests, APIThrottled
from subzero.language import Language
from subzero.language import Language, ENDSWITH_LANGUAGECODE_RE
from scandir import scandir, scandir_generic as _scandir_generic
logger = logging.getLogger(__name__)
@@ -574,12 +574,14 @@ def scan_video(path, dont_use_actual_file=False, hints=None, providers=None, ski
return video
def _search_external_subtitles(path, languages=None, only_one=False, scandir_generic=False):
def _search_external_subtitles(path, languages=None, only_one=False, scandir_generic=False, match_strictness="strict"):
dirpath, filename = os.path.split(path)
dirpath = dirpath or '.'
fileroot, fileext = os.path.splitext(filename)
fn_no_ext, fileext = os.path.splitext(filename)
fn_no_ext_lower = fn_no_ext.lower()
subtitles = {}
_scandir = _scandir_generic if scandir_generic else scandir
for entry in _scandir(dirpath):
if (not entry.name or entry.name in ('\x0c', '$', ',', '\x7f')) and not scandir_generic:
logger.debug('Could not determine the name of the file, retrying with scandir_generic')
@@ -590,9 +592,11 @@ def _search_external_subtitles(path, languages=None, only_one=False, scandir_gen
p = entry.name
# keep only valid subtitle filenames
if not p.lower().startswith(fileroot.lower()) or not p.lower().endswith(SUBTITLE_EXTENSIONS):
if not p.lower().endswith(SUBTITLE_EXTENSIONS):
continue
# not p.lower().startswith(fileroot.lower()) or not
p_root, p_ext = os.path.splitext(p)
if not INCLUDE_EXOTIC_SUBS and p_ext not in (".srt", ".ass", ".ssa", ".vtt"):
continue
@@ -611,7 +615,19 @@ def _search_external_subtitles(path, languages=None, only_one=False, scandir_gen
forced = "forced" in adv_tag
# extract the potential language code
language_code = p_root[len(fileroot):].replace('_', '-')[1:]
language_code = p_root.rsplit(".", 1)[1].replace('_', '-')
# remove possible language code for matching
p_root_bare = ENDSWITH_LANGUAGECODE_RE.sub("", p_root)
p_root_lower = p_root_bare.lower()
filename_matches = p_root_lower == fn_no_ext_lower
filename_contains = p_root_lower in fn_no_ext_lower
if not filename_matches:
if match_strictness == "strict" or (match_strictness == "loose" and not filename_contains):
continue
# default language is undefined
language = Language('und')
@@ -635,7 +651,7 @@ def _search_external_subtitles(path, languages=None, only_one=False, scandir_gen
return subtitles
def search_external_subtitles(path, languages=None, only_one=False):
def search_external_subtitles(path, languages=None, only_one=False, match_strictness="strict"):
"""
wrap original search_external_subtitles function to search multiple paths for one given video
# todo: cleanup and merge with _search_external_subtitles
@@ -656,10 +672,11 @@ def search_external_subtitles(path, languages=None, only_one=False):
if os.path.isdir(os.path.dirname(abspath)):
try:
subtitles.update(_search_external_subtitles(abspath, languages=languages,
only_one=only_one))
only_one=only_one, match_strictness=match_strictness))
except OSError:
subtitles.update(_search_external_subtitles(abspath, languages=languages,
only_one=only_one, scandir_generic=True))
only_one=only_one, match_strictness=match_strictness,
scandir_generic=True))
logger.debug("external subs: found %s", subtitles)
return subtitles
@@ -2,42 +2,35 @@
import io
import logging
import math
import re
import time
from datetime import datetime
import dateutil.parser
import rarfile
from bs4 import BeautifulSoup
from zipfile import ZipFile, is_zipfile
from rarfile import RarFile, is_rarfile
from babelfish import language_converters, Script
from requests import RequestException
from requests import RequestException, codes as request_codes
from guessit import guessit
from subliminal_patch.http import RetryingCFSession
from subliminal_patch.providers import Provider
from subliminal_patch.providers.mixins import ProviderSubtitleArchiveMixin
from subliminal_patch.subtitle import Subtitle
from subliminal_patch.utils import sanitize, fix_inconsistent_naming as _fix_inconsistent_naming
from subliminal.exceptions import ProviderError
from subliminal.exceptions import ProviderError, AuthenticationError, ConfigurationError
from subliminal.score import get_equivalent_release_groups
from subliminal.utils import sanitize_release_group
from subliminal.subtitle import guess_matches
from subliminal.video import Episode, Movie
from subliminal.subtitle import fix_line_ending
from subliminal_patch.pitcher import pitchers, load_verification, store_verification
from subzero.language import Language
from random import randint
from .utils import FIRST_THOUSAND_OR_SO_USER_AGENTS as AGENT_LIST
from subzero.language import Language
from dogpile.cache.api import NO_VALUE
from subliminal.cache import region
# parsing regex definitions
title_re = re.compile(r'(?P<title>(?:.+(?= [Aa][Kk][Aa] ))|.+)(?:(?:.+)(?P<altitle>(?<= [Aa][Kk][Aa] ).+))?')
lang_re = re.compile(r'(?<=flags/)(?P<lang>.{2})(?:.)(?P<script>c?)(?:.+)')
season_re = re.compile(r'Sezona (?P<season>\d+)')
episode_re = re.compile(r'Epizoda (?P<episode>\d+)')
year_re = re.compile(r'(?P<year>\d+)')
fps_re = re.compile(r'fps: (?P<fps>.+)')
def fix_inconsistent_naming(title):
@@ -51,6 +44,7 @@ def fix_inconsistent_naming(title):
return _fix_inconsistent_naming(title, {"DC's Legends of Tomorrow": "Legends of Tomorrow",
"Marvel's Jessica Jones": "Jessica Jones"})
logger = logging.getLogger(__name__)
# Configure :mod:`rarfile` to use the same path separator as :mod:`zipfile`
@@ -62,9 +56,9 @@ language_converters.register('titlovi = subliminal_patch.converters.titlovi:Titl
class TitloviSubtitle(Subtitle):
provider_name = 'titlovi'
def __init__(self, language, page_link, download_link, sid, releases, title, alt_title=None, season=None,
episode=None, year=None, fps=None, asked_for_release_group=None, asked_for_episode=None):
super(TitloviSubtitle, self).__init__(language, page_link=page_link)
def __init__(self, language, download_link, sid, releases, title, alt_title=None, season=None,
episode=None, year=None, rating=None, download_count=None, asked_for_release_group=None, asked_for_episode=None):
super(TitloviSubtitle, self).__init__(language)
self.sid = sid
self.releases = self.release_info = releases
self.title = title
@@ -73,11 +67,21 @@ class TitloviSubtitle(Subtitle):
self.episode = episode
self.year = year
self.download_link = download_link
self.fps = fps
self.rating = rating
self.download_count = download_count
self.matches = None
self.asked_for_release_group = asked_for_release_group
self.asked_for_episode = asked_for_episode
def __repr__(self):
if self.season and self.episode:
return '<%s "%s (%r)" s%.2de%.2d [%s:%s] ID:%r R:%.2f D:%r>' % (
self.__class__.__name__, self.title, self.year, self.season, self.episode, self.language, self._guessed_encoding, self.sid,
self.rating, self.download_count)
else:
return '<%s "%s (%r)" [%s:%s] ID:%r R:%.2f D:%r>' % (
self.__class__.__name__, self.title, self.year, self.language, self._guessed_encoding, self.sid, self.rating, self.download_count)
@property
def id(self):
return self.sid
@@ -134,20 +138,62 @@ class TitloviSubtitle(Subtitle):
class TitloviProvider(Provider, ProviderSubtitleArchiveMixin):
subtitle_class = TitloviSubtitle
languages = {Language.fromtitlovi(l) for l in language_converters['titlovi'].codes} | {Language.fromietf('sr-Latn')}
server_url = 'https://titlovi.com'
search_url = server_url + '/titlovi/?'
download_url = server_url + '/download/?type=1&mediaid='
api_url = 'https://kodi.titlovi.com/api/subtitles'
api_gettoken_url = api_url + '/gettoken'
api_search_url = api_url + '/search'
def __init__(self, username=None, password=None):
if not all((username, password)):
raise ConfigurationError('Username and password must be specified')
self.username = username
self.password = password
self.session = None
self.user_id = None
self.login_token = None
self.token_exp = None
def initialize(self):
self.session = RetryingCFSession()
#load_verification("titlovi", self.session)
token = region.get("titlovi_token")
if token is not NO_VALUE:
self.user_id, self.login_token, self.token_exp = token
if datetime.now() > self.token_exp:
logger.debug('Token expired')
self.log_in()
else:
logger.debug('Use cached token')
else:
logger.debug('Token not found in cache')
self.log_in()
def log_in(self):
login_params = dict(username=self.username, password=self.password, json=True)
try:
response = self.session.post(self.api_gettoken_url, params=login_params)
if response.status_code == request_codes.ok:
resp_json = response.json()
self.login_token = resp_json.get('Token')
self.user_id = resp_json.get('UserId')
self.token_exp = dateutil.parser.parse(resp_json.get('ExpirationDate'))
region.set("titlovi_token", [self.user_id, self.login_token, self.token_exp])
logger.debug('New token obtained')
elif response.status_code == request_codes.unauthorized:
raise AuthenticationError('Login failed')
except RequestException as e:
logger.error(e)
def terminate(self):
self.session.close()
def query(self, languages, title, season=None, episode=None, year=None, video=None):
items_per_page = 10
current_page = 1
def query(self, languages, title, season=None, episode=None, year=None, imdb_id=None, video=None):
search_params = dict()
used_languages = languages
lang_strings = [str(lang) for lang in used_languages]
@@ -162,135 +208,73 @@ class TitloviProvider(Provider, ProviderSubtitleArchiveMixin):
langs = '|'.join(map(str, [l.titlovi for l in used_languages]))
# set query params
params = {'prijevod': title, 'jezik': langs}
search_params['query'] = title
search_params['lang'] = langs
is_episode = False
if season and episode:
is_episode = True
params['s'] = season
params['e'] = episode
if year:
params['g'] = year
search_params['season'] = season
search_params['episode'] = episode
#if year:
# search_params['year'] = year
if imdb_id:
search_params['imdbID'] = imdb_id
# loop through paginated results
logger.info('Searching subtitles %r', params)
logger.info('Searching subtitles %r', search_params)
subtitles = []
query_results = []
while True:
# query the server
try:
r = self.session.get(self.search_url, params=params, timeout=10)
r.raise_for_status()
except RequestException as e:
logger.exception('RequestException %s', e)
break
try:
search_params['token'] = self.login_token
search_params['userid'] = self.user_id
search_params['json'] = True
response = self.session.get(self.api_search_url, params=search_params)
resp_json = response.json()
if resp_json['SubtitleResults']:
query_results.extend(resp_json['SubtitleResults'])
except Exception as e:
logger.error(e)
for sub in query_results:
# title and alternate title
match = title_re.search(sub.get('Title'))
if match:
_title = match.group('title')
alt_title = match.group('altitle')
else:
try:
soup = BeautifulSoup(r.content, 'lxml')
continue
# number of results
result_count = int(soup.select_one('.results_count b').string)
except:
result_count = None
# handle movies and series separately
if is_episode:
subtitle = self.subtitle_class(Language.fromtitlovi(sub.get('Lang')), sub.get('Link'), sub.get('Id'), sub.get('Release'), _title,
alt_title=alt_title, season=sub.get('Season'), episode=sub.get('Episode'),
year=sub.get('Year'), rating=sub.get('Rating'),
download_count=sub.get('DownloadCount'),
asked_for_release_group=video.release_group,
asked_for_episode=episode)
else:
subtitle = self.subtitle_class(Language.fromtitlovi(sub.get('Lang')), sub.get('Link'), sub.get('Id'), sub.get('Release'), _title,
alt_title=alt_title, year=sub.get('Year'), rating=sub.get('Rating'),
download_count=sub.get('DownloadCount'),
asked_for_release_group=video.release_group)
logger.debug('Found subtitle %r', subtitle)
# exit if no results
if not result_count:
if not subtitles:
logger.debug('No subtitles found')
else:
logger.debug("No more subtitles found")
break
# prime our matches so we can use the values later
subtitle.get_matches(video)
# number of pages with results
pages = int(math.ceil(result_count / float(items_per_page)))
# get current page
if 'pg' in params:
current_page = int(params['pg'])
try:
sublist = soup.select('section.titlovi > ul.titlovi > li.subtitleContainer.canEdit')
for sub in sublist:
# subtitle id
sid = sub.find(attrs={'data-id': True}).attrs['data-id']
# get download link
download_link = self.download_url + sid
# title and alternate title
match = title_re.search(sub.a.string)
if match:
_title = match.group('title')
alt_title = match.group('altitle')
else:
continue
# page link
page_link = self.server_url + sub.a.attrs['href']
# subtitle language
_lang = sub.select_one('.lang')
match = lang_re.search(_lang.attrs.get('src', _lang.attrs.get('data-cfsrc', '')))
if match:
try:
# decode language
lang = Language.fromtitlovi(match.group('lang')+match.group('script'))
except ValueError:
continue
# relase year or series start year
match = year_re.search(sub.find(attrs={'data-id': True}).parent.i.string)
if match:
r_year = int(match.group('year'))
# fps
match = fps_re.search(sub.select_one('.fps').string)
if match:
fps = match.group('fps')
# releases
releases = str(sub.select_one('.fps').parent.contents[0].string)
# handle movies and series separately
if is_episode:
# season and episode info
sxe = sub.select_one('.s0xe0y').string
r_season = None
r_episode = None
if sxe:
match = season_re.search(sxe)
if match:
r_season = int(match.group('season'))
match = episode_re.search(sxe)
if match:
r_episode = int(match.group('episode'))
subtitle = self.subtitle_class(lang, page_link, download_link, sid, releases, _title,
alt_title=alt_title, season=r_season, episode=r_episode,
year=r_year, fps=fps,
asked_for_release_group=video.release_group,
asked_for_episode=episode)
else:
subtitle = self.subtitle_class(lang, page_link, download_link, sid, releases, _title,
alt_title=alt_title, year=r_year, fps=fps,
asked_for_release_group=video.release_group)
logger.debug('Found subtitle %r', subtitle)
# prime our matches so we can use the values later
subtitle.get_matches(video)
# add found subtitles
subtitles.append(subtitle)
finally:
soup.decompose()
# stop on last page
if current_page >= pages:
break
# increment current page
params['pg'] = current_page + 1
logger.debug('Getting page %d', params['pg'])
# add found subtitles
subtitles.append(subtitle)
return subtitles
def list_subtitles(self, video, languages):
season = episode = None
if isinstance(video, Episode):
title = video.series
season = video.season
@@ -300,6 +284,7 @@ class TitloviProvider(Provider, ProviderSubtitleArchiveMixin):
return [s for s in
self.query(languages, fix_inconsistent_naming(title), season=season, episode=episode, year=video.year,
imdb_id=video.imdb_id,
video=video)]
def download_subtitle(self, subtitle):
@@ -337,10 +322,12 @@ class TitloviProvider(Provider, ProviderSubtitleArchiveMixin):
sub_to_extract = None
for sub_name in subs_in_archive:
if not ('.cyr' in sub_name or '.cir' in sub_name):
_sub_name = sub_name.lower()
if not ('.cyr' in _sub_name or '.cir' in _sub_name or 'cyr)' in _sub_name):
sr_lat_subs.append(sub_name)
if ('.cyr' in sub_name or '.cir' in sub_name) and not '.lat' in sub_name:
if ('.cyr' in sub_name or '.cir' in _sub_name) and not '.lat' in _sub_name.lower():
sr_cyr_subs.append(sub_name)
if subtitle.language == 'sr':
@@ -1,5 +1,6 @@
# coding=utf-8
import types
import re
from babelfish.exceptions import LanguageError
from babelfish import Language as Language_, basestr
@@ -134,3 +135,16 @@ class Language(Language_):
return Language(*Language_.fromietf(s).__getstate__())
return Language(*Language_.fromalpha3b(s).__getstate__())
IETF_MATCH = ".+\.([^-.]+)(?:-[A-Za-z]+)?$"
ENDSWITH_LANGUAGECODE_RE = re.compile("\.([^-.]{2,3})(?:-[A-Za-z]{2,})?$")
def match_ietf_language(s, ietf=False):
language_match = re.match(".+\.([^\.]+)$" if not ietf
else IETF_MATCH, s)
if language_match and len(language_match.groups()) == 1:
language = language_match.groups()[0]
return language
return s
@@ -107,6 +107,12 @@ class Dicked(object):
for key, value in entries.iteritems():
self.__dict__[key] = (Dicked(**value) if isinstance(value, dict) else value)
def has(self, key):
return self._entries is not None and key in self._entries
def get(self, key, default=None):
return self._entries.get(key, default) if self._entries else default
def __repr__(self):
return str(self)
+4 -2
View File
@@ -17,7 +17,8 @@ def has_external_subtitle(part_id, stored_subs, language):
def set_existing_languages(video, video_info, external_subtitles=False, embedded_subtitles=False, known_embedded=None,
stored_subs=None, languages=None, only_one=False, known_metadata_subs=None):
stored_subs=None, languages=None, only_one=False, known_metadata_subs=None,
match_strictness="strict"):
logger.debug(u"Determining existing subtitles for %s", video.name)
external_langs_found = set()
@@ -27,7 +28,8 @@ def set_existing_languages(video, video_info, external_subtitles=False, embedded
external_langs_found = known_metadata_subs
external_langs_found.update(set(search_external_subtitles(video.name, languages=languages,
only_one=only_one).values()))
only_one=only_one,
match_strictness=match_strictness).values()))
# found external subtitles should be considered?
if external_subtitles:
+13 -13
View File
@@ -14,13 +14,17 @@ Check out **[the Sub-Zero Wiki](https://github.com/pannal/Sub-Zero.bundle/wiki)*
---
**[Kitana is now required to have a UI](https://github.com/pannal/Kitana)**
---
**[The future of Sub-Zero](https://www.reddit.com/r/PleX/comments/9n9qjl/subzero_the_future/)**
---
## Helping development
If you like this, buy me a beer: <br>[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=G9VKR2B8PMNKG) <br>or become a Patreon starting at **1 $ / month** <br><a href="https://www.patreon.com/subzero_plex" target="_blank"><img src="http://www.wenspencer.com/wp-content/uploads/2017/02/patreon-button.png" height="42" /></a> <br>or use the OpenSubtitles Sub-Zero affiliate link to become VIP <br>**10€/year, ad-free subs, 1000 subs/day, no-cache *VIP* server**<br><a href="http://v.ht/osvip" target="_blank"><img src="https://static.opensubtitles.org/gfx/logo.gif" height="50" /></a>
If you like this, buy me a beer: <br>[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=G9VKR2B8PMNKG) <br>or become a Patreon starting at **1 $ / month** <br><a href="https://www.patreon.com/subzero_plex" target="_blank"><img src="https://i0.wp.com/tablecakes.com/wp-content/uploads/2018/08/become-a-patron-button.png" height="54" /></a> <br>or use the OpenSubtitles Sub-Zero affiliate link to become VIP <br>**10€/year, ad-free subs, 1000 subs/day, no-cache *VIP* server**<br><a href="http://v.ht/osvip" target="_blank"><img src="https://static.opensubtitles.org/gfx/logo.gif" height="50" /></a>
If you register with an anti-captcha service and you decide to use [Anti-Captcha.com](http://getcaptchasolution.com/kkvviom7nh), you can use [this affiliate link](http://getcaptchasolution.com/kkvviom7nh) to help development.
@@ -91,22 +95,18 @@ the.vbm, mmgoodnow, Vertig0ne, thliu78, tattoomees, ostman, count_confucius, ehe
## Changelog
2.6.5.3124
2.6.5.3152
subscene, addic7ed and titlovi
subscene, addic7ed
- either of those providers might impose a reCAPTCHA verification. In order to use those providers, please create an account at an AntiCaptcha service ([anti-captcha.com](http://getcaptchasolution.com/kkvviom7nh) or [deathbycaptcha.com](http://deathbycaptcha.com)), add funds, then supply your credentials/apikey in the configuration
Changelog
- core: http: fallback to default DNS when normal resolving fails; fixes #657
- core: extract embedded/menu: fix detection of unknown streams; don't use unknown streams if a known language was previously found
- core: language: use replacement map from bazarr
- providers: titlovi: fix matching
- providers: subscene: fix unknown language code error when "empty" result is returned
- providers: subscene: add support for pt-BR (based on Diaoul/subliminal@b22cf08)
- providers: subscene: explicitly set account filters for languages
- providers: subscene: limit alternative searches to 3; set throttle to 8
- providers: subscene: move login/cookies to initialization sequence
- submod: generic: en: fix ";='s
- core: fix core issue possibly impacting results on OpenSubtitles in certain conditions
- core: fix default values of opensubtitles-skip-wrong-fps, use_https; fix #676
- core: fix for determining whether to search under certain circumstances; fixes #666
- core: #664 fix missing language processing of multiple videos refreshed at once
- core: #661 fix match strictness when determining preexisting external subtitles
- providers: titlovi: New implementation of Titlovi using API (thanks @viking1304)
[older changes](CHANGELOG.md)