# -*- coding: utf-8 -*-
from __future__ import absolute_import
import logging
import io
from requests import Session
from guessit import guessit
from subliminal_patch.providers import Provider
from subliminal_patch.subtitle import Subtitle
from subliminal.utils import sanitize_release_group
from subliminal.subtitle import guess_matches
from subliminal.video import Episode, Movie
from subzero.language import Language
import gzip
import random
from time import sleep
from xml.etree import ElementTree
logger = logging.getLogger(__name__)
class BSPlayerSubtitle(Subtitle):
"""BSPlayer Subtitle."""
provider_name = 'bsplayer'
hash_verifiable = True
def __init__(self, language, filename, subtype, video, link):
super(BSPlayerSubtitle, self).__init__(language)
self.language = language
self.filename = filename
self.page_link = link
self.subtype = subtype
self.video = video
@property
def id(self):
return self.page_link
@property
def release_info(self):
return self.filename
def get_matches(self, video):
matches = set()
matches |= guess_matches(video, guessit(self.filename))
subtitle_filename = self.filename
# episode
if isinstance(video, Episode):
# already matched in search query
matches.update(['title', 'series', 'season', 'episode', 'year'])
# movie
elif isinstance(video, Movie):
# already matched in search query
matches.update(['title', 'year'])
# release_group
if video.release_group and video.release_group.lower() in subtitle_filename:
matches.add('release_group')
# resolution
if video.resolution and video.resolution.lower() in subtitle_filename:
matches.add('resolution')
# format
formats = []
if video.format:
formats = [video.format.lower()]
if formats[0] == "web-dl":
formats.append("webdl")
formats.append("webrip")
formats.append("web ")
for frmt in formats:
if frmt.lower() in subtitle_filename:
matches.add('format')
break
# video_codec
if video.video_codec:
video_codecs = [video.video_codec.lower()]
if video_codecs[0] == "h264":
formats.append("x264")
elif video_codecs[0] == "h265":
formats.append("x265")
for vc in formats:
if vc.lower() in subtitle_filename:
matches.add('video_codec')
break
matches.add('hash')
return matches
class BSPlayerProvider(Provider):
"""BSPlayer Provider."""
languages = {Language('por', 'BR')} | {Language(l) for l in [
'ara', 'bul', 'ces', 'dan', 'deu', 'ell', 'eng', 'fin', 'fra', 'hun', 'ita', 'jpn', 'kor', 'nld', 'pol', 'por',
'ron', 'rus', 'spa', 'swe', 'tur', 'ukr', 'zho'
]}
SEARCH_THROTTLE = 8
hash_verifiable = True
# batantly based on kodi's bsplayer plugin
# also took from BSPlayer-Subtitles-Downloader
def __init__(self):
self.initialize()
def initialize(self):
self.session = Session()
self.search_url = self.get_sub_domain()
self.token = None
self.login()
def terminate(self):
self.session.close()
self.logout()
def api_request(self, func_name='logIn', params='', tries=5):
headers = {
'User-Agent': 'BSPlayer/2.x (1022.12360)',
'Content-Type': 'text/xml; charset=utf-8',
'Connection': 'close',
'SOAPAction': '"http://api.bsplayer-subtitles.com/v1.php#{func_name}"'.format(func_name=func_name)
}
data = (
'\n'
''
''
'{params}'
).format(search_url=self.search_url, func_name=func_name, params=params)
logger.info('Sending request: %s.' % func_name)
for i in iter(range(tries)):
try:
self.session.headers.update(headers.items())
res = self.session.post(self.search_url, data)
return ElementTree.fromstring(res.text)
except Exception as ex:
logger.info("ERROR: %s." % ex)
if func_name == 'logIn':
self.search_url = self.get_sub_domain()
sleep(1)
logger.info('ERROR: Too many tries (%d)...' % tries)
raise Exception('Too many tries...')
def login(self):
# If already logged in
if self.token:
return True
root = self.api_request(
func_name='logIn',
params=(''
''
'BSPlayer v2.67')
)
res = root.find('.//return')
if res.find('status').text == 'OK':
self.token = res.find('data').text
logger.info("Logged In Successfully.")
return True
return False
def logout(self):
# If already logged out / not logged in
if not self.token:
return True
root = self.api_request(
func_name='logOut',
params='{token}'.format(token=self.token)
)
res = root.find('.//return')
self.token = None
if res.find('status').text == 'OK':
logger.info("Logged Out Successfully.")
return True
return False
def query(self, video, video_hash, language):
if not self.login():
return []
if isinstance(language, (tuple, list, set)):
# language_ids = ",".join(language)
# language_ids = 'spa'
language_ids = ','.join(sorted(l.opensubtitles for l in language))
if video.imdb_id is None:
imdbId = '*'
else:
imdbId = video.imdb_id
sleep(self.SEARCH_THROTTLE)
root = self.api_request(
func_name='searchSubtitles',
params=(
'{token}'
'{movie_hash}'
'{movie_size}'
'{language_ids}'
'{imdbId}'
).format(token=self.token, movie_hash=video_hash,
movie_size=video.size, language_ids=language_ids, imdbId=imdbId)
)
res = root.find('.//return/result')
if res.find('status').text != 'OK':
return []
items = root.findall('.//return/data/item')
subtitles = []
if items:
logger.info("Subtitles Found.")
for item in items:
subID = item.find('subID').text
subDownloadLink = item.find('subDownloadLink').text
subLang = Language.fromopensubtitles(item.find('subLang').text)
subName = item.find('subName').text
subFormat = item.find('subFormat').text
subtitles.append(
BSPlayerSubtitle(subLang, subName, subFormat, video, subDownloadLink)
)
return subtitles
def list_subtitles(self, video, languages):
return self.query(video, video.hashes['bsplayer'], languages)
def get_sub_domain(self):
# s1-9, s101-109
SUB_DOMAINS = ['s1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9',
's101', 's102', 's103', 's104', 's105', 's106', 's107', 's108', 's109']
API_URL_TEMPLATE = "http://{sub_domain}.api.bsplayer-subtitles.com/v1.php"
sub_domains_end = len(SUB_DOMAINS) - 1
return API_URL_TEMPLATE.format(sub_domain=SUB_DOMAINS[random.randint(0, sub_domains_end)])
def download_subtitle(self, subtitle):
session = Session()
_addheaders = {
'User-Agent': 'Mozilla/4.0 (compatible; Synapse)'
}
session.headers.update(_addheaders)
res = session.get(subtitle.page_link)
if res:
if res.text == '500':
raise ValueError('Error 500 on server')
with gzip.GzipFile(fileobj=io.BytesIO(res.content)) as gf:
subtitle.content = gf.read()
subtitle.normalize()
return subtitle
raise ValueError('Problems conecting to the server')