diff --git a/README.md b/README.md
index ef15757bf..418c33ac4 100644
--- a/README.md
+++ b/README.md
@@ -45,8 +45,10 @@ If you need something that is not already part of Bazarr, feel free to create a
* Argenteam
* Assrt
* BetaSeries
+* BSPlayer
* GreekSubtitles
* Hosszupuska
+* LegendasDivx
* LegendasTV
* Napiprojekt
* Napisy24
diff --git a/bazarr.py b/bazarr.py
index ae5d88f13..f9228e2be 100644
--- a/bazarr.py
+++ b/bazarr.py
@@ -11,6 +11,7 @@ import os
import sys
import platform
import re
+import signal
from bazarr.get_args import args
@@ -39,15 +40,97 @@ check_python_version()
dir_name = os.path.dirname(__file__)
-def start_bazarr():
+class ProcessRegistry:
+
+ def register(self, process):
+ pass
+
+ def unregister(self, process):
+ pass
+
+
+class DaemonStatus(ProcessRegistry):
+
+ def __init__(self):
+ self.__should_stop = False
+ self.__processes = set()
+
+ def register(self, process):
+ self.__processes.add(process)
+
+ def unregister(self, process):
+ self.__processes.remove(process)
+
+ '''
+ Waits all the provided processes for the specified amount of time in seconds.
+ '''
+ @staticmethod
+ def __wait_for_processes(processes, timeout):
+ reference_ts = time.time()
+ elapsed = 0
+ remaining_processes = list(processes)
+ while elapsed < timeout and len(remaining_processes) > 0:
+ remaining_time = timeout - elapsed
+ for ep in list(remaining_processes):
+ if ep.poll() is not None:
+ remaining_processes.remove(ep)
+ else:
+ if remaining_time > 0:
+ if PY3:
+ try:
+ ep.wait(remaining_time)
+ remaining_processes.remove(ep)
+ except sp.TimeoutExpired:
+ pass
+ else:
+ '''
+ In python 2 there is no such thing as some mechanism to wait with a timeout.
+ '''
+ time.sleep(1)
+ elapsed = time.time() - reference_ts
+ remaining_time = timeout - elapsed
+ return remaining_processes
+
+ '''
+ Sends to every single of the specified processes the given signal and (if live_processes is not None) append to it processes which are still alive.
+ '''
+ @staticmethod
+ def __send_signal(processes, signal_no, live_processes=None):
+ for ep in processes:
+ if ep.poll() is None:
+ if live_processes is not None:
+ live_processes.append(ep)
+ try:
+ ep.send_signal(signal_no)
+ except Exception as e:
+ print('Failed sending signal %s to process %s because of an unexpected error: %s' % (signal_no, ep.pid, e))
+ return live_processes
+
+ '''
+ Flags this instance as should stop and terminates as smoothly as possible children processes.
+ '''
+ def stop(self):
+ self.__should_stop = True
+ live_processes = DaemonStatus.__send_signal(self.__processes, signal.SIGINT, list())
+ live_processes = DaemonStatus.__wait_for_processes(live_processes, 120)
+ DaemonStatus.__send_signal(live_processes, signal.SIGTERM)
+
+ def should_stop(self):
+ return self.__should_stop
+
+
+def start_bazarr(process_registry=ProcessRegistry()):
script = [sys.executable, "-u", os.path.normcase(os.path.join(dir_name, 'bazarr', 'main.py'))] + sys.argv[1:]
ep = sp.Popen(script, stdout=sp.PIPE, stderr=sp.STDOUT, stdin=sp.PIPE)
+ process_registry.register(ep)
print("Bazarr starting...")
try:
while True:
line = ep.stdout.readline()
if line == '' or not line:
+ # Process ended so let's unregister it
+ process_registry.unregister(ep)
break
if PY3:
sys.stdout.buffer.write(line)
@@ -73,7 +156,7 @@ if __name__ == '__main__':
pass
- def daemon():
+ def daemon(bazarr_runner = lambda: start_bazarr()):
if os.path.exists(stopfile):
try:
os.remove(stopfile)
@@ -89,12 +172,30 @@ if __name__ == '__main__':
except:
print('Unable to delete restart file.')
else:
- start_bazarr()
+ bazarr_runner()
- start_bazarr()
+ bazarr_runner = lambda: start_bazarr()
- # Keep the script running forever.
- while True:
- daemon()
+ should_stop = lambda: False
+
+ if PY3:
+ daemonStatus = DaemonStatus()
+
+ def shutdown():
+ # indicates that everything should stop
+ daemonStatus.stop()
+ # emulate a Ctrl C command on itself (bypasses the signal thing but, then, emulates the "Ctrl+C break")
+ os.kill(os.getpid(), signal.SIGINT)
+
+ signal.signal(signal.SIGTERM, lambda signal_no, frame: shutdown())
+
+ should_stop = lambda: daemonStatus.should_stop()
+ bazarr_runner = lambda: start_bazarr(daemonStatus)
+
+ bazarr_runner()
+
+ # Keep the script running forever until stop is requested through term or keyboard interrupt
+ while not should_stop():
+ daemon(bazarr_runner)
time.sleep(1)
diff --git a/bazarr/config.py b/bazarr/config.py
index ca61871e0..b64040ebf 100644
--- a/bazarr/config.py
+++ b/bazarr/config.py
@@ -102,6 +102,10 @@ defaults = {
'password': '',
'random_agents': 'True'
},
+ 'legendasdivx': {
+ 'username': '',
+ 'password': ''
+ },
'legendastv': {
'username': '',
'password': ''
diff --git a/bazarr/get_providers.py b/bazarr/get_providers.py
index c543f930d..d345b79ff 100644
--- a/bazarr/get_providers.py
+++ b/bazarr/get_providers.py
@@ -38,7 +38,7 @@ PROVIDER_THROTTLE_MAP = {
}
}
-PROVIDERS_FORCED_OFF = ["addic7ed", "tvsubtitles", "legendastv", "napiprojekt", "shooter", "hosszupuska",
+PROVIDERS_FORCED_OFF = ["addic7ed", "tvsubtitles", "legendasdivx", "legendastv", "napiprojekt", "shooter", "hosszupuska",
"supersubtitles", "titlovi", "argenteam", "assrt", "subscene"]
throttle_count = {}
@@ -114,6 +114,9 @@ def get_providers_auth():
'password': settings.subscene.password,
'only_foreign': False, # fixme
},
+ 'legendasdivx': {'username': settings.legendasdivx.username,
+ 'password': settings.legendasdivx.password,
+ },
'legendastv': {'username': settings.legendastv.username,
'password': settings.legendastv.password,
},
diff --git a/bazarr/get_series.py b/bazarr/get_series.py
index c909dda09..26d26f9bf 100644
--- a/bazarr/get_series.py
+++ b/bazarr/get_series.py
@@ -6,195 +6,193 @@ import os
import requests
import logging
from queueconfig import notifications
-from collections import OrderedDict
-import datetime
-from get_args import args
from config import settings, url_sonarr
from list_subtitles import list_missing_subtitles
from database import database, dict_converter
from utils import get_sonarr_version
-import six
from helper import path_replace
def update_series():
notifications.write(msg="Update series list from Sonarr is running...", queue='get_series')
apikey_sonarr = settings.sonarr.apikey
+ if apikey_sonarr is None:
+ return
+
sonarr_version = get_sonarr_version()
serie_default_enabled = settings.general.getboolean('serie_default_enabled')
serie_default_language = settings.general.serie_default_language
serie_default_hi = settings.general.serie_default_hi
serie_default_forced = settings.general.serie_default_forced
-
- if apikey_sonarr is None:
- pass
- else:
- audio_profiles = get_profile_list()
-
- # Get shows data from Sonarr
- url_sonarr_api_series = url_sonarr() + "/api/series?apikey=" + apikey_sonarr
- try:
- r = requests.get(url_sonarr_api_series, timeout=60, verify=False)
- r.raise_for_status()
- except requests.exceptions.HTTPError as errh:
- logging.exception("BAZARR Error trying to get series from Sonarr. Http error.")
- return
- except requests.exceptions.ConnectionError as errc:
- logging.exception("BAZARR Error trying to get series from Sonarr. Connection Error.")
- return
- except requests.exceptions.Timeout as errt:
- logging.exception("BAZARR Error trying to get series from Sonarr. Timeout Error.")
- return
- except requests.exceptions.RequestException as err:
- logging.exception("BAZARR Error trying to get series from Sonarr.")
- return
+ audio_profiles = get_profile_list()
+
+ # Get shows data from Sonarr
+ url_sonarr_api_series = url_sonarr() + "/api/series?apikey=" + apikey_sonarr
+ try:
+ r = requests.get(url_sonarr_api_series, timeout=60, verify=False)
+ r.raise_for_status()
+ except requests.exceptions.HTTPError:
+ logging.exception("BAZARR Error trying to get series from Sonarr. Http error.")
+ return
+ except requests.exceptions.ConnectionError:
+ logging.exception("BAZARR Error trying to get series from Sonarr. Connection Error.")
+ return
+ except requests.exceptions.Timeout:
+ logging.exception("BAZARR Error trying to get series from Sonarr. Timeout Error.")
+ return
+ except requests.exceptions.RequestException:
+ logging.exception("BAZARR Error trying to get series from Sonarr.")
+ return
+
+ # Get current shows in DB
+ current_shows_db = database.execute("SELECT sonarrSeriesId FROM table_shows")
+
+ current_shows_db_list = [x['sonarrSeriesId'] for x in current_shows_db]
+ current_shows_sonarr = []
+ series_to_update = []
+ series_to_add = []
+
+ series_list_length = len(r.json())
+ for i, show in enumerate(r.json(), 1):
+ notifications.write(msg="Getting series data from Sonarr...", queue='get_series', item=i,
+ length=series_list_length)
+
+ overview = show['overview'] if 'overview' in show else ''
+ poster = ''
+ fanart = ''
+ for image in show['images']:
+ if image['coverType'] == 'poster':
+ poster_big = image['url'].split('?')[0]
+ poster = os.path.splitext(poster_big)[0] + '-250' + os.path.splitext(poster_big)[1]
+
+ if image['coverType'] == 'fanart':
+ fanart = image['url'].split('?')[0]
+
+ alternate_titles = None
+ if show['alternateTitles'] is not None:
+ alternate_titles = str([item['title'] for item in show['alternateTitles']])
+
+ if sonarr_version.startswith('2'):
+ audio_language = profile_id_to_language(show['qualityProfileId'], audio_profiles)
else:
- # Get current shows in DB
- current_shows_db = database.execute("SELECT sonarrSeriesId FROM table_shows")
-
- current_shows_db_list = [x['sonarrSeriesId'] for x in current_shows_db]
- current_shows_sonarr = []
- series_to_update = []
- series_to_add = []
- altered_series = []
+ audio_language = profile_id_to_language(show['languageProfileId'], audio_profiles)
- seriesListLength = len(r.json())
- for i, show in enumerate(r.json(), 1):
- notifications.write(msg="Getting series data from Sonarr...", queue='get_series', item=i, length=seriesListLength)
- try:
- overview = six.text_type(show['overview'])
- except:
- overview = ""
- try:
- poster_big = show['images'][2]['url'].split('?')[0]
- poster = os.path.splitext(poster_big)[0] + '-250' + os.path.splitext(poster_big)[1]
- except:
- poster = ""
- try:
- fanart = show['images'][0]['url'].split('?')[0]
- except:
- fanart = ""
+ # Add shows in Sonarr to current shows list
+ current_shows_sonarr.append(show['id'])
- if show['alternateTitles'] != None:
- alternateTitles = str([item['title'] for item in show['alternateTitles']])
- else:
- alternateTitles = None
+ if show['id'] in current_shows_db_list:
+ series_to_update.append({'title': show["title"],
+ 'path': show["path"],
+ 'tvdbId': int(show["tvdbId"]),
+ 'sonarrSeriesId': int(show["id"]),
+ 'overview': overview,
+ 'poster': poster,
+ 'fanart': fanart,
+ 'audio_language': audio_language,
+ 'sortTitle': show['sortTitle'],
+ 'year': show['year'],
+ 'alternateTitles': alternate_titles})
+ else:
+ if serie_default_enabled is True:
+ series_to_add.append({'title': show["title"],
+ 'path': show["path"],
+ 'tvdbId': show["tvdbId"],
+ 'languages': serie_default_language,
+ 'hearing_impaired': serie_default_hi,
+ 'sonarrSeriesId': show["id"],
+ 'overview': overview,
+ 'poster': poster,
+ 'fanart': fanart,
+ 'audio_language': audio_language,
+ 'sortTitle': show['sortTitle'],
+ 'year': show['year'],
+ 'alternateTitles': alternate_titles,
+ 'forced': serie_default_forced})
+ else:
+ series_to_add.append({'title': show["title"],
+ 'path': show["path"],
+ 'tvdbId': show["tvdbId"],
+ 'sonarrSeriesId': show["id"],
+ 'overview': overview,
+ 'poster': poster,
+ 'fanart': fanart,
+ 'audio_language': audio_language,
+ 'sortTitle': show['sortTitle'],
+ 'year': show['year'],
+ 'alternateTitles': alternate_titles})
- # Add shows in Sonarr to current shows list
- current_shows_sonarr.append(show['id'])
-
- if show['tvdbId'] in current_shows_db_list:
- series_to_update.append({'title': six.text_type(show["title"]),
- 'path': six.text_type(show["path"]),
- 'tvdbId': int(show["tvdbId"]),
- 'sonarrSeriesId': int(show["id"]),
- 'overview': six.text_type(overview),
- 'poster': six.text_type(poster),
- 'fanart': six.text_type(fanart),
- 'audio_language': six.text_type(profile_id_to_language((show['qualityProfileId'] if get_sonarr_version().startswith('2') else show['languageProfileId']), audio_profiles)),
- 'sortTitle': six.text_type(show['sortTitle']),
- 'year': six.text_type(show['year']),
- 'alternateTitles': six.text_type(alternateTitles)})
- else:
- if serie_default_enabled is True:
- series_to_add.append({'title': show["title"],
- 'path': show["path"],
- 'tvdbId': show["tvdbId"],
- 'languages': serie_default_language,
- 'hearing_impaired': serie_default_hi,
- 'sonarrSeriesId': show["id"],
- 'overview': overview,
- 'poster': poster,
- 'fanart': fanart,
- 'audio_language': profile_id_to_language((show['qualityProfileId'] if sonarr_version.startswith('2') else show['languageProfileId']), audio_profiles),
- 'sortTitle': show['sortTitle'],
- 'year': show['year'],
- 'alternateTitles': alternateTitles,
- 'forced': serie_default_forced})
- else:
- series_to_add.append({'title': show["title"],
- 'path': show["path"],
- 'tvdbId': show["tvdbId"],
- 'sonarrSeriesId': show["id"],
- 'overview': overview,
- 'poster': poster,
- 'fanart': fanart,
- 'audio_language': profile_id_to_language((show['qualityProfileId'] if sonarr_version.startswith('2') else show['languageProfileId']), audio_profiles),
- 'sortTitle': show['sortTitle'],
- 'year': show['year'],
- 'alternateTitles': alternateTitles})
+ # Remove old series from DB
+ removed_series = list(set(current_shows_db_list) - set(current_shows_sonarr))
- # Remove old series from DB
- removed_series = list(set(current_shows_db_list) - set(current_shows_sonarr))
+ for series in removed_series:
+ database.execute("DELETE FROM table_shows WHERE sonarrSEriesId=?", (series,))
- for series in removed_series:
- database.execute("DELETE FROM table_shows WHERE sonarrSEriesId=?",(series,))
+ # Update existing series in DB
+ series_in_db_list = []
+ series_in_db = database.execute("SELECT title, path, tvdbId, sonarrSeriesId, overview, poster, fanart, "
+ "audio_language, sortTitle, year, alternateTitles FROM table_shows")
- # Update existing series in DB
- series_in_db_list = []
- series_in_db = database.execute("SELECT title, path, tvdbId, sonarrSeriesId, overview, poster, fanart, "
- "audio_language, sortTitle, year, alternateTitles FROM table_shows")
+ for item in series_in_db:
+ series_in_db_list.append(item)
- for item in series_in_db:
- series_in_db_list.append(item)
+ series_to_update_list = [i for i in series_to_update if i not in series_in_db_list]
- series_to_update_list = [i for i in series_to_update if i not in series_in_db_list]
+ for updated_series in series_to_update_list:
+ query = dict_converter.convert(updated_series)
+ database.execute('''UPDATE table_shows SET ''' + query.keys_update + ''' WHERE sonarrSeriesId = ?''',
+ query.values + (updated_series['sonarrSeriesId'],))
- for updated_series in series_to_update_list:
- query = dict_converter.convert(updated_series)
- database.execute('''UPDATE table_shows SET ''' + query.keys_update + ''' WHERE sonarrSeriesId = ?''',
- query.values + (updated_series['sonarrSeriesId'],))
+ # Insert new series in DB
+ for added_series in series_to_add:
+ query = dict_converter.convert(added_series)
+ result = database.execute(
+ '''INSERT OR IGNORE INTO table_shows(''' + query.keys_insert + ''') VALUES(''' +
+ query.question_marks + ''')''', query.values)
+ if result:
+ list_missing_subtitles(no=added_series['sonarrSeriesId'])
+ else:
+ logging.debug('BAZARR unable to insert this series into the database:',
+ path_replace(added_series['path']))
- # Insert new series in DB
- for added_series in series_to_add:
- query = dict_converter.convert(added_series)
- result = database.execute(
- '''INSERT OR IGNORE INTO table_shows(''' + query.keys_insert + ''') VALUES(''' +
- query.question_marks + ''')''', query.values)
- if result:
- list_missing_subtitles(no=added_series['sonarrSeriesId'])
- else:
- logging.debug('BAZARR unable to insert this series into the database:',
- path_replace(added_series['path']))
-
- logging.debug('BAZARR All series synced from Sonarr into database.')
+ logging.debug('BAZARR All series synced from Sonarr into database.')
def get_profile_list():
apikey_sonarr = settings.sonarr.apikey
sonarr_version = get_sonarr_version()
profiles_list = []
- # Get profiles data from Sonarr
+ # Get profiles data from Sonarr
if sonarr_version.startswith('2'):
url_sonarr_api_series = url_sonarr() + "/api/profile?apikey=" + apikey_sonarr
- elif sonarr_version.startswith('3'):
+ else:
url_sonarr_api_series = url_sonarr() + "/api/v3/languageprofile?apikey=" + apikey_sonarr
try:
profiles_json = requests.get(url_sonarr_api_series, timeout=60, verify=False)
- except requests.exceptions.ConnectionError as errc:
+ except requests.exceptions.ConnectionError:
logging.exception("BAZARR Error trying to get profiles from Sonarr. Connection Error.")
- except requests.exceptions.Timeout as errt:
+ return None
+ except requests.exceptions.Timeout:
logging.exception("BAZARR Error trying to get profiles from Sonarr. Timeout Error.")
- except requests.exceptions.RequestException as err:
+ return None
+ except requests.exceptions.RequestException:
logging.exception("BAZARR Error trying to get profiles from Sonarr.")
+ return None
+
+ # Parsing data returned from Sonarr
+ if sonarr_version.startswith('2'):
+ for profile in profiles_json.json():
+ profiles_list.append([profile['id'], profile['language'].capitalize()])
else:
- # Parsing data returned from Sonarr
- if sonarr_version.startswith('2'):
- for profile in profiles_json.json():
- profiles_list.append([profile['id'], profile['language'].capitalize()])
- elif sonarr_version.startswith('3'):
- for profile in profiles_json.json():
- profiles_list.append([profile['id'], profile['name'].capitalize()])
+ for profile in profiles_json.json():
+ profiles_list.append([profile['id'], profile['name'].capitalize()])
- return profiles_list
-
- return None
+ return profiles_list
-def profile_id_to_language(id, profiles):
+def profile_id_to_language(id_, profiles):
for profile in profiles:
- if id == profile[0]:
+ if id_ == profile[0]:
return profile[1]
diff --git a/bazarr/get_subtitle.py b/bazarr/get_subtitle.py
index 81d2e8a6e..8b078f439 100644
--- a/bazarr/get_subtitle.py
+++ b/bazarr/get_subtitle.py
@@ -40,6 +40,7 @@ from analytics import track_event
import six
from six.moves import range
from functools import reduce
+from locale import getpreferredencoding
def get_video(path, title, sceneName, use_scenename, providers=None, media_type="movie"):
@@ -234,34 +235,7 @@ def download_subtitle(path, language, hi, forced, providers, providers_auth, sce
command = pp_replace(postprocessing_cmd, path, downloaded_path, downloaded_language,
downloaded_language_code2, downloaded_language_code3,
subtitle.language.forced)
- try:
- if os.name == 'nt':
- codepage = subprocess.Popen("chcp", shell=True, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- # wait for the process to terminate
- out_codepage, err_codepage = codepage.communicate()
- encoding = out_codepage.split(':')[-1].strip()
-
- process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- # wait for the process to terminate
- out, err = process.communicate()
-
- if os.name == 'nt':
- out = out.decode(encoding)
-
- except:
- if out == "":
- logging.error(
- 'BAZARR Post-processing result for file ' + path + ' : Nothing returned from command execution')
- else:
- logging.error('BAZARR Post-processing result for file ' + path + ' : ' + out)
- else:
- if out == "":
- logging.info(
- 'BAZARR Post-processing result for file ' + path + ' : Nothing returned from command execution')
- else:
- logging.info('BAZARR Post-processing result for file ' + path + ' : ' + out)
+ postprocessing(command, path)
# fixme: support multiple languages at once
if media_type == 'series':
@@ -459,34 +433,7 @@ def manual_download_subtitle(path, language, hi, forced, subtitle, provider, pro
command = pp_replace(postprocessing_cmd, path, downloaded_path, downloaded_language,
downloaded_language_code2, downloaded_language_code3,
subtitle.language.forced)
- try:
- if os.name == 'nt':
- codepage = subprocess.Popen("chcp", shell=True, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- # wait for the process to terminate
- out_codepage, err_codepage = codepage.communicate()
- encoding = out_codepage.split(':')[-1].strip()
-
- process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- # wait for the process to terminate
- out, err = process.communicate()
-
- if os.name == 'nt':
- out = out.decode(encoding)
-
- except:
- if out == "":
- logging.error(
- 'BAZARR Post-processing result for file ' + path + ' : Nothing returned from command execution')
- else:
- logging.error('BAZARR Post-processing result for file ' + path + ' : ' + out)
- else:
- if out == "":
- logging.info(
- 'BAZARR Post-processing result for file ' + path + ' : Nothing returned from command execution')
- else:
- logging.info('BAZARR Post-processing result for file ' + path + ' : ' + out)
+ postprocessing(command, path)
if media_type == 'series':
reversed_path = path_replace_reverse(path)
@@ -1063,7 +1010,7 @@ def upgrade_subtitles():
except ValueError:
pass
else:
- if int(upgradable_movie['score']) < 360:
+ if int(upgradable_movie['score']) < 120:
upgradable_movies_not_perfect.append(upgradable_movie)
movies_to_upgrade = []
@@ -1078,6 +1025,8 @@ def upgrade_subtitles():
if settings.general.getboolean('use_sonarr'):
for i, episode in enumerate(episodes_to_upgrade, 1):
+ if episode['languages'] in [None, 'None', '[]']:
+ continue
providers = get_providers()
if not providers:
notifications.write(msg='BAZARR All providers are throttled', queue='get_subtitle', duration='long')
@@ -1127,6 +1076,8 @@ def upgrade_subtitles():
if settings.general.getboolean('use_radarr'):
for i, movie in enumerate(movies_to_upgrade, 1):
+ if movie['languages'] in [None, 'None', '[]']:
+ continue
providers = get_providers()
if not providers:
notifications.write(msg='BAZARR All providers are throttled', queue='get_subtitle', duration='long')
@@ -1173,3 +1124,41 @@ def upgrade_subtitles():
store_subtitles_movie(movie['video_path'], path_replace_movie(movie['video_path']))
history_log_movie(3, movie['radarrId'], message, path, language_code, provider, score)
send_notifications_movie(movie['radarrId'], message)
+
+
+def postprocessing(command, path):
+ try:
+ encoding = getpreferredencoding()
+ if os.name == 'nt':
+ if six.PY3:
+ codepage = subprocess.Popen("chcp", shell=True, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, encoding=getpreferredencoding())
+ else:
+ codepage = subprocess.Popen("chcp", shell=True, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ # wait for the process to terminate
+ out_codepage, err_codepage = codepage.communicate()
+ encoding = out_codepage.split(':')[-1].strip()
+
+ if six.PY3:
+ process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, encoding=encoding)
+ else:
+ process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ # wait for the process to terminate
+ out, err = process.communicate()
+
+ if six.PY2:
+ out = out.decode(encoding)
+
+ out = out.replace('\n', ' ').replace('\r', ' ')
+
+ except Exception as e:
+ logging.error('BAZARR Post-processing failed for file ' + path + ' : ' + repr(e))
+ else:
+ if out == "":
+ logging.info(
+ 'BAZARR Post-processing result for file ' + path + ' : Nothing returned from command execution')
+ else:
+ logging.info('BAZARR Post-processing result for file ' + path + ' : ' + out)
diff --git a/bazarr/main.py b/bazarr/main.py
index 696bc6f68..cac891428 100644
--- a/bazarr/main.py
+++ b/bazarr/main.py
@@ -1,6 +1,6 @@
# coding=utf-8
-bazarr_version = '0.8.4'
+bazarr_version = '0.8.4.1'
import os
os.environ["SZ_USER_AGENT"] = "Bazarr/1"
@@ -9,6 +9,7 @@ os.environ["BAZARR_VERSION"] = bazarr_version
import gc
import sys
import libs
+import io
import six
from six.moves import zip
@@ -209,11 +210,11 @@ def shutdown():
else:
database.close()
try:
- stop_file = open(os.path.join(args.config_dir, "bazarr.stop"), "w")
+ stop_file = io.open(os.path.join(args.config_dir, "bazarr.stop"), "w", encoding='UTF-8')
except Exception as e:
logging.error('BAZARR Cannot create bazarr.stop file.')
else:
- stop_file.write('')
+ stop_file.write(six.text_type(''))
stop_file.close()
sys.exit(0)
@@ -229,12 +230,12 @@ def restart():
else:
database.close()
try:
- restart_file = open(os.path.join(args.config_dir, "bazarr.restart"), "w")
+ restart_file = io.open(os.path.join(args.config_dir, "bazarr.restart"), "w", encoding='UTF-8')
except Exception as e:
logging.error('BAZARR Cannot create bazarr.restart file.')
else:
logging.info('Bazarr is being restarted...')
- restart_file.write('')
+ restart_file.write(six.text_type(''))
restart_file.close()
sys.exit(0)
@@ -398,6 +399,8 @@ def save_wizard():
settings.addic7ed.password = request.forms.get('settings_addic7ed_password')
settings.addic7ed.random_agents = text_type(settings_addic7ed_random_agents)
settings.assrt.token = request.forms.get('settings_assrt_token')
+ settings.legendasdivx.username = request.forms.get('settings_legendasdivx_username')
+ settings.legendasdivx.password = request.forms.get('settings_legendasdivx_password')
settings.legendastv.username = request.forms.get('settings_legendastv_username')
settings.legendastv.password = request.forms.get('settings_legendastv_password')
settings.opensubtitles.username = request.forms.get('settings_opensubtitles_username')
@@ -996,15 +999,18 @@ def historyseries():
else:
series_monitored_only_query_string = ''
- upgradable_episodes = database.execute("SELECT video_path, MAX(timestamp) as timestamp, score FROM table_history "
+ upgradable_episodes = database.execute("SELECT video_path, MAX(timestamp) as timestamp, score, table_shows.languages FROM table_history "
"INNER JOIN table_episodes on table_episodes.sonarrEpisodeId = "
- "table_history.sonarrEpisodeId WHERE action IN (" +
+ "table_history.sonarrEpisodeId JOIN table_shows on table_shows.sonarrSeriesId = "
+ "table_history.sonarrSeriesId WHERE action IN (" +
','.join(map(str, query_actions)) + ") AND timestamp > ? AND "
"score is not null" + series_monitored_only_query_string + " GROUP BY "
"table_history.video_path, table_history.language",
(minimum_timestamp,))
for upgradable_episode in upgradable_episodes:
+ if upgradable_episode['languages'] in [None, 'None', '[]']:
+ continue
if upgradable_episode['timestamp'] > minimum_timestamp:
try:
int(upgradable_episode['score'])
@@ -1073,15 +1079,17 @@ def historymovies():
else:
query_actions = [1, 3]
- upgradable_movies = database.execute("SELECT video_path, MAX(timestamp) as timestamp, score FROM table_history_movie "
- "INNER JOIN table_movies on table_movies.radarrId="
- "table_history_movie.radarrId WHERE action IN (" +
+ upgradable_movies = database.execute("SELECT video_path, MAX(timestamp) as timestamp, score, "
+ "table_movies.languages FROM table_history_movie INNER JOIN table_movies "
+ "on table_movies.radarrId=table_history_movie.radarrId WHERE action IN (" +
','.join(map(str, query_actions)) +
") AND timestamp > ? AND score is not NULL" +
movies_monitored_only_query_string + " GROUP BY video_path, language",
(minimum_timestamp,))
for upgradable_movie in upgradable_movies:
+ if upgradable_movie['languages'] in [None, 'None', '[]']:
+ continue
if upgradable_movie['timestamp'] > minimum_timestamp:
try:
int(upgradable_movie['score'])
@@ -1527,6 +1535,8 @@ def save_settings():
settings.addic7ed.password = request.forms.get('settings_addic7ed_password')
settings.addic7ed.random_agents = text_type(settings_addic7ed_random_agents)
settings.assrt.token = request.forms.get('settings_assrt_token')
+ settings.legendasdivx.username = request.forms.get('settings_legendasdivx_username')
+ settings.legendasdivx.password = request.forms.get('settings_legendasdivx_password')
settings.legendastv.username = request.forms.get('settings_legendastv_username')
settings.legendastv.password = request.forms.get('settings_legendastv_password')
settings.opensubtitles.username = request.forms.get('settings_opensubtitles_username')
@@ -1698,7 +1708,7 @@ def system():
throttled_providers = list_throttled_providers()
try:
- with open(os.path.join(args.config_dir, 'config', 'releases.txt'), 'r') as f:
+ with io.open(os.path.join(args.config_dir, 'config', 'releases.txt'), 'r', encoding='UTF-8') as f:
releases = ast.literal_eval(f.read())
except Exception as e:
releases = []
@@ -1724,7 +1734,7 @@ def system():
def get_logs():
authorize()
logs = []
- with open(os.path.join(args.config_dir, 'log', 'bazarr.log')) as file:
+ with io.open(os.path.join(args.config_dir, 'log', 'bazarr.log'), encoding='UTF-8') as file:
for line in file.readlines():
lin = []
lin = line.split('|')
@@ -1749,9 +1759,9 @@ def execute_task(taskid):
@custom_auth_basic(check_credentials)
def remove_subtitles():
authorize()
- episodePath = request.forms.get('episodePath')
+ episodePath = request.forms.episodePath
language = request.forms.get('language')
- subtitlesPath = request.forms.get('subtitlesPath')
+ subtitlesPath = request.forms.subtitlesPath
sonarrSeriesId = request.forms.get('sonarrSeriesId')
sonarrEpisodeId = request.forms.get('sonarrEpisodeId')
@@ -1768,9 +1778,9 @@ def remove_subtitles():
@custom_auth_basic(check_credentials)
def remove_subtitles_movie():
authorize()
- moviePath = request.forms.get('moviePath')
+ moviePath = request.forms.moviePath
language = request.forms.get('language')
- subtitlesPath = request.forms.get('subtitlesPath')
+ subtitlesPath = request.forms.subtitlesPath
radarrId = request.forms.get('radarrId')
try:
@@ -1788,14 +1798,14 @@ def get_subtitle():
authorize()
ref = request.environ['HTTP_REFERER']
- episodePath = request.forms.get('episodePath')
- sceneName = request.forms.get('sceneName')
+ episodePath = request.forms.episodePath
+ sceneName = request.forms.sceneName
language = request.forms.get('language')
hi = request.forms.get('hi')
forced = request.forms.get('forced')
sonarrSeriesId = request.forms.get('sonarrSeriesId')
sonarrEpisodeId = request.forms.get('sonarrEpisodeId')
- title = request.forms.get('title')
+ title = request.forms.title
providers_list = get_providers()
providers_auth = get_providers_auth()
@@ -1823,12 +1833,12 @@ def get_subtitle():
def manual_search_json():
authorize()
- episodePath = request.forms.get('episodePath')
- sceneName = request.forms.get('sceneName')
+ episodePath = request.forms.episodePath
+ sceneName = request.forms.sceneName
language = request.forms.get('language')
hi = request.forms.get('hi')
forced = request.forms.get('forced')
- title = request.forms.get('title')
+ title = request.forms.title
providers_list = get_providers()
providers_auth = get_providers_auth()
@@ -1843,16 +1853,16 @@ def manual_get_subtitle():
authorize()
ref = request.environ['HTTP_REFERER']
- episodePath = request.forms.get('episodePath')
- sceneName = request.forms.get('sceneName')
+ episodePath = request.forms.episodePath
+ sceneName = request.forms.sceneName
language = request.forms.get('language')
hi = request.forms.get('hi')
forced = request.forms.get('forced')
selected_provider = request.forms.get('provider')
- subtitle = request.forms.get('subtitle')
+ subtitle = request.forms.subtitle
sonarrSeriesId = request.forms.get('sonarrSeriesId')
sonarrEpisodeId = request.forms.get('sonarrEpisodeId')
- title = request.forms.get('title')
+ title = request.forms.title
providers_auth = get_providers_auth()
@@ -1881,14 +1891,14 @@ def perform_manual_upload_subtitle():
authorize()
ref = request.environ['HTTP_REFERER']
- episodePath = request.forms.get('episodePath')
- sceneName = request.forms.get('sceneName')
+ episodePath = request.forms.episodePath
+ sceneName = request.forms.sceneName
language = request.forms.get('language')
forced = True if request.forms.get('forced') == '1' else False
upload = request.files.get('upload')
sonarrSeriesId = request.forms.get('sonarrSeriesId')
sonarrEpisodeId = request.forms.get('sonarrEpisodeId')
- title = request.forms.get('title')
+ title = request.forms.title
_, ext = os.path.splitext(upload.filename)
@@ -1925,13 +1935,13 @@ def get_subtitle_movie():
authorize()
ref = request.environ['HTTP_REFERER']
- moviePath = request.forms.get('moviePath')
- sceneName = request.forms.get('sceneName')
+ moviePath = request.forms.moviePath
+ sceneName = request.forms.sceneName
language = request.forms.get('language')
hi = request.forms.get('hi')
forced = request.forms.get('forced')
radarrId = request.forms.get('radarrId')
- title = request.forms.get('title')
+ title = request.forms.title
providers_list = get_providers()
providers_auth = get_providers_auth()
@@ -1959,12 +1969,12 @@ def get_subtitle_movie():
def manual_search_movie_json():
authorize()
- moviePath = request.forms.get('moviePath')
- sceneName = request.forms.get('sceneName')
+ moviePath = request.forms.moviePath
+ sceneName = request.forms.sceneName
language = request.forms.get('language')
hi = request.forms.get('hi')
forced = request.forms.get('forced')
- title = request.forms.get('title')
+ title = request.forms.title
providers_list = get_providers()
providers_auth = get_providers_auth()
@@ -1979,15 +1989,15 @@ def manual_get_subtitle_movie():
authorize()
ref = request.environ['HTTP_REFERER']
- moviePath = request.forms.get('moviePath')
- sceneName = request.forms.get('sceneName')
+ moviePath = request.forms.moviePath
+ sceneName = request.forms.sceneName
language = request.forms.get('language')
hi = request.forms.get('hi')
forced = request.forms.get('forced')
- selected_provider = request.forms.get('provider')
- subtitle = request.forms.get('subtitle')
+ selected_provider = request.forms.provider
+ subtitle = request.forms.subtitle
radarrId = request.forms.get('radarrId')
- title = request.forms.get('title')
+ title = request.forms.title
providers_auth = get_providers_auth()
@@ -2015,13 +2025,13 @@ def perform_manual_upload_subtitle_movie():
authorize()
ref = request.environ['HTTP_REFERER']
- moviePath = request.forms.get('moviePath')
- sceneName = request.forms.get('sceneName')
+ moviePath = request.forms.moviePath
+ sceneName = request.forms.sceneName
language = request.forms.get('language')
forced = True if request.forms.get('forced') == '1' else False
upload = request.files.get('upload')
radarrId = request.forms.get('radarrId')
- title = request.forms.get('title')
+ title = request.forms.title
_, ext = os.path.splitext(upload.filename)
diff --git a/bazarr/notifier.py b/bazarr/notifier.py
index 57c9e4960..c9761ac98 100644
--- a/bazarr/notifier.py
+++ b/bazarr/notifier.py
@@ -2,23 +2,21 @@
from __future__ import absolute_import
import apprise
-import os
import logging
-from get_args import args
from database import database
def update_notifier():
# define apprise object
a = apprise.Apprise()
-
+
# Retrieve all of the details
results = a.details()
-
+
notifiers_new = []
notifiers_old = []
-
+
notifiers_current_db = database.execute("SELECT name FROM table_settings_notifier")
notifiers_current = []
@@ -31,66 +29,68 @@ def update_notifier():
logging.debug('Adding new notifier agent: ' + x['service_name'])
else:
notifiers_old.append([x['service_name']])
-
+
notifiers_to_delete = [item for item in notifiers_current if item not in notifiers_old]
- database.execute("INSERT INTO table_settings_notifier (name, enabled) VALUES (?, ?)", notifiers_new, execute_many=True)
-
+ database.execute("INSERT INTO table_settings_notifier (name, enabled) VALUES (?, ?)", notifiers_new,
+ execute_many=True)
+
database.execute("DELETE FROM table_settings_notifier WHERE name=?", notifiers_to_delete, execute_many=True)
def get_notifier_providers():
providers = database.execute("SELECT name, url FROM table_settings_notifier WHERE enabled=1")
+
return providers
-def get_series_name(sonarrSeriesId):
- data = database.execute("SELECT title FROM table_shows WHERE sonarrSeriesId=?", (sonarrSeriesId,), only_one=True)
-
+def get_series_name(sonarr_series_id):
+ data = database.execute("SELECT title FROM table_shows WHERE sonarrSeriesId=?", (sonarr_series_id,), only_one=True)
+
return data['title'] or None
-def get_episode_name(sonarrEpisodeId):
+def get_episode_name(sonarr_episode_id):
data = database.execute("SELECT title, season, episode FROM table_episodes WHERE sonarrEpisodeId=?",
- (sonarrEpisodeId,), only_one=True)
-
+ (sonarr_episode_id,), only_one=True)
+
return data['title'], data['season'], data['episode']
-def get_movies_name(radarrId):
- data = database.execute("SELECT title FROM table_movies WHERE radarrId=?", (radarrId,), only_one=True)
+def get_movies_name(radarr_id):
+ data = database.execute("SELECT title FROM table_movies WHERE radarrId=?", (radarr_id,), only_one=True)
return data['title']
-def send_notifications(sonarrSeriesId, sonarrEpisodeId, message):
+def send_notifications(sonarr_series_id, sonarr_episode_id, message):
providers = get_notifier_providers()
- series = get_series_name(sonarrSeriesId)
- episode = get_episode_name(sonarrEpisodeId)
-
+ series = get_series_name(sonarr_series_id)
+ episode = get_episode_name(sonarr_episode_id)
+
apobj = apprise.Apprise()
-
+
for provider in providers:
if provider['url'] is not None:
apobj.add(provider['url'])
-
+
apobj.notify(
title='Bazarr notification',
- body=(series + ' - S' + str(episode[1]).zfill(2) + 'E' + str(episode[2]).zfill(2) + ' - ' + episode[0] + ' : ' + message),
+ body="{} - S{:02d}E{:02d} - {} : {}".format(series, episode[1], episode[2], episode[0], message),
)
-def send_notifications_movie(radarrId, message):
+def send_notifications_movie(radarr_id, message):
providers = get_notifier_providers()
- movie = get_movies_name(radarrId)
-
+ movie = get_movies_name(radarr_id)
+
apobj = apprise.Apprise()
-
+
for provider in providers:
if provider['url'] is not None:
apobj.add(provider['url'])
-
+
apobj.notify(
title='Bazarr notification',
- body=movie + ' : ' + message,
+ body="{} : {}".format(movie, message),
)
diff --git a/bazarr/utils.py b/bazarr/utils.py
index aee63a298..2b436125e 100644
--- a/bazarr/utils.py
+++ b/bazarr/utils.py
@@ -4,7 +4,6 @@ from __future__ import absolute_import
import os
import time
import platform
-import sys
import logging
import requests
@@ -17,22 +16,20 @@ import datetime
import glob
-def history_log(action, sonarrSeriesId, sonarrEpisodeId, description, video_path=None, language=None, provider=None,
- score=None, forced=False):
+def history_log(action, sonarr_series_id, sonarr_episode_id, description, video_path=None, language=None, provider=None,
+ score=None):
from database import database
database.execute("INSERT INTO table_history (action, sonarrSeriesId, sonarrEpisodeId, timestamp, description,"
- "video_path, language, provider, score) VALUES (?,?,?,?,?,?,?,?,?)", (action, sonarrSeriesId,
- sonarrEpisodeId, time.time(),
- description, video_path,
- language, provider, score))
+ "video_path, language, provider, score) VALUES (?,?,?,?,?,?,?,?,?)",
+ (action, sonarr_series_id, sonarr_episode_id, time.time(), description, video_path, language,
+ provider, score))
-def history_log_movie(action, radarrId, description, video_path=None, language=None, provider=None, score=None,
- forced=False):
+def history_log_movie(action, radarr_id, description, video_path=None, language=None, provider=None, score=None):
from database import database
database.execute("INSERT INTO table_history_movie (action, radarrId, timestamp, description, video_path, language, "
- "provider, score) VALUES (?,?,?,?,?,?,?,?)", (action, radarrId, time.time(), description,
- video_path, language, provider, score))
+ "provider, score) VALUES (?,?,?,?,?,?,?,?)",
+ (action, radarr_id, time.time(), description, video_path, language, provider, score))
def get_binary(name):
@@ -46,10 +43,8 @@ def get_binary(name):
else:
if platform.system() == "Windows": # Windows
exe = os.path.abspath(os.path.join(binaries_dir, "Windows", "i386", name, "%s.exe" % name))
-
elif platform.system() == "Darwin": # MacOSX
exe = os.path.abspath(os.path.join(binaries_dir, "MacOSX", "i386", name, name))
-
elif platform.system() == "Linux": # Linux
exe = os.path.abspath(os.path.join(binaries_dir, "Linux", platform.machine(), name, name))
@@ -82,62 +77,52 @@ def cache_maintenance():
def get_sonarr_version():
- use_sonarr = settings.general.getboolean('use_sonarr')
- apikey_sonarr = settings.sonarr.apikey
- sv = url_sonarr() + "/api/system/status?apikey=" + apikey_sonarr
sonarr_version = ''
- if use_sonarr:
+ if settings.general.getboolean('use_sonarr'):
try:
+ sv = url_sonarr() + "/api/system/status?apikey=" + settings.sonarr.apikey
sonarr_version = requests.get(sv, timeout=60, verify=False).json()['version']
- except Exception as e:
+ except Exception:
logging.debug('BAZARR cannot get Sonarr version')
-
return sonarr_version
def get_sonarr_platform():
- use_sonarr = settings.general.getboolean('use_sonarr')
- apikey_sonarr = settings.sonarr.apikey
- sv = url_sonarr() + "/api/system/status?apikey=" + apikey_sonarr
sonarr_platform = ''
- if use_sonarr:
+ if settings.general.getboolean('use_sonarr'):
try:
- if requests.get(sv, timeout=60, verify=False).json()['isLinux'] or requests.get(sv, timeout=60, verify=False).json()['isOsx']:
+ sv = url_sonarr() + "/api/system/status?apikey=" + settings.sonarr.apikey
+ response = requests.get(sv, timeout=60, verify=False).json()
+ if response['isLinux'] or response['isOsx']:
sonarr_platform = 'posix'
- elif requests.get(sv, timeout=60, verify=False).json()['isWindows']:
+ elif response['isWindows']:
sonarr_platform = 'nt'
- except Exception as e:
- logging.DEBUG('BAZARR cannot get Sonarr platform')
-
+ except Exception:
+ logging.debug('BAZARR cannot get Sonarr platform')
return sonarr_platform
def get_radarr_version():
- use_radarr = settings.general.getboolean('use_radarr')
- apikey_radarr = settings.radarr.apikey
- rv = url_radarr() + "/api/system/status?apikey=" + apikey_radarr
radarr_version = ''
- if use_radarr:
+ if settings.general.getboolean('use_radarr'):
try:
+ rv = url_radarr() + "/api/system/status?apikey=" + settings.radarr.apikey
radarr_version = requests.get(rv, timeout=60, verify=False).json()['version']
- except Exception as e:
+ except Exception:
logging.debug('BAZARR cannot get Radarr version')
-
return radarr_version
def get_radarr_platform():
- use_radarr = settings.general.getboolean('use_radarr')
- apikey_radarr = settings.radarr.apikey
- rv = url_radarr() + "/api/system/status?apikey=" + apikey_radarr
radarr_platform = ''
- if use_radarr:
+ if settings.general.getboolean('use_radarr'):
try:
- if requests.get(rv, timeout=60, verify=False).json()['isLinux'] or requests.get(rv, timeout=60, verify=False).json()['isOsx']:
+ rv = url_radarr() + "/api/system/status?apikey=" + settings.radarr.apikey
+ response = requests.get(rv, timeout=60, verify=False).json()
+ if response['isLinux'] or response['isOsx']:
radarr_platform = 'posix'
- elif requests.get(rv, timeout=60, verify=False).json()['isWindows']:
+ elif response['isWindows']:
radarr_platform = 'nt'
- except Exception as e:
- logging.DEBUG('BAZARR cannot get Radarr platform')
-
+ except Exception:
+ logging.debug('BAZARR cannot get Radarr platform')
return radarr_platform
diff --git a/libs/subliminal_patch/core.py b/libs/subliminal_patch/core.py
index d4685c289..38a603eed 100644
--- a/libs/subliminal_patch/core.py
+++ b/libs/subliminal_patch/core.py
@@ -543,6 +543,10 @@ def scan_video(path, dont_use_actual_file=False, hints=None, providers=None, ski
if video.size > 10485760:
logger.debug('Size is %d', video.size)
osub_hash = None
+
+ if "bsplayer" in providers:
+ video.hashes['bsplayer'] = osub_hash = hash_opensubtitles(hash_path)
+
if "opensubtitles" in providers:
video.hashes['opensubtitles'] = osub_hash = hash_opensubtitles(hash_path)
diff --git a/libs/subliminal_patch/providers/bsplayer.py b/libs/subliminal_patch/providers/bsplayer.py
new file mode 100644
index 000000000..9839a0331
--- /dev/null
+++ b/libs/subliminal_patch/providers/bsplayer.py
@@ -0,0 +1,235 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import
+import logging
+import io
+import os
+
+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 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'
+
+ 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()
+
+ video_filename = video.name
+ video_filename = os.path.basename(video_filename)
+ video_filename, _ = os.path.splitext(video_filename)
+ video_filename = sanitize_release_group(video_filename)
+
+ subtitle_filename = self.filename
+ subtitle_filename = os.path.basename(subtitle_filename)
+ subtitle_filename, _ = os.path.splitext(subtitle_filename)
+ subtitle_filename = sanitize_release_group(subtitle_filename)
+
+
+ matches |= guess_matches(video, guessit(self.filename))
+
+ matches.add(id(self))
+ 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
+
+ # 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'
+ '