Skip to content

Commit

Permalink
release: v0.0.23
Browse files Browse the repository at this point in the history
  • Loading branch information
newt-sc committed May 7, 2020
1 parent 1ebc686 commit 0a16b69
Show file tree
Hide file tree
Showing 15 changed files with 141 additions and 51 deletions.
1 change: 1 addition & 0 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ ignore =
F401,
W503,
W504,
C901,

exclude =
.git,
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
* [v0.0.23](https://github.com/newt-sc/a4kSubtitles/releases/tag/service.subtitles.a4ksubtitles%2Fservice.subtitles.a4ksubtitles-0.0.23):
* Add progress dialog
* Support cancellation

* [v0.0.22](https://github.com/newt-sc/a4kSubtitles/releases/tag/service.subtitles.a4ksubtitles%2Fservice.subtitles.a4ksubtitles-0.0.22):
* Fix meta string conversion issue

Expand Down
9 changes: 9 additions & 0 deletions a4kSubtitles/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,23 @@
api_mode_enabled = True
handle = None

progress_dialog = None
progress_text = ''

def main(handle, paramstring): # pragma: no cover
core.api_mode_enabled = False
core.handle = handle

core.progress_dialog = kodi.get_progress_dialog()
core.progress_text = ''

params = dict(utils.parse_qsl(paramstring))
if params['action'] in ('search', 'manualsearch'):
search(core, params)
elif params['action'] == 'download':
params['action_args'] = json.loads(params['action_args'])
download(core, params)

core.progress_dialog.close()
core.progress_dialog = None
kodi.xbmcplugin.endOfDirectory(handle)
38 changes: 36 additions & 2 deletions a4kSubtitles/lib/kodi.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import os
import sys
import json
import re
import importlib

kodi = sys.modules[__name__]
Expand Down Expand Up @@ -40,7 +41,7 @@
addon_version = addon.getAddonInfo('version')
addon_profile = xbmc.translatePath(addon.getAddonInfo('profile'))

def json_rpc(method, params, log_error=True):
def json_rpc(method, params, log_error=True): # pragma: no cover
try:
result = xbmc.executeJSONRPC(
json.dumps({
Expand All @@ -57,9 +58,42 @@ def json_rpc(method, params, log_error=True):
except KeyError:
return None

def get_kodi_setting(setting, log_error=True):
def get_kodi_setting(setting, log_error=True): # pragma: no cover
return json_rpc('Settings.GetSettingValue', {"setting": setting}, log_error)

def get_progress_dialog(): # pragma: no cover
progress_dialog = xbmcgui.DialogProgress()
progress_dialog.create(addon_name, 'Searching...')
return progress_dialog

def update_progress(core): # pragma: no cover
if core.progress_dialog is None or core.progress_dialog.iscanceled():
return

text = re.sub(r'\|+', '|', core.progress_text).strip('|')
total = core.progress_text.count('|') + 1
count = text.count('|') + 1 if text != '' else 0
progress = int(float(total - count) / total * 100)
core.progress_dialog.update(progress, text.replace('|', ' | '))

def parse_language(language): # pragma: no cover
if language == 'original':
audio_streams = xbmc.Player().getAvailableAudioStreams()
if len(audio_streams) == 0:
return None
return xbmc.convertLanguage(
audio_streams[0],
xbmc.ENGLISH_NAME
)
elif language == 'default':
return xbmc.getLanguage()
elif language == 'none':
return None
elif language == 'forced_only':
return parse_language(get_kodi_setting("locale.audiolanguage"))
else:
return language

def create_listitem(item): # pragma: no cover
if item['lang'] == 'Brazilian':
item['lang'] = 'Portuguese (Brazil)'
Expand Down
2 changes: 2 additions & 0 deletions a4kSubtitles/lib/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
from urlparse import unquote, parse_qsl
from urllib import quote_plus
from StringIO import StringIO
import Queue as queue
except ImportError:
from urllib.parse import quote_plus, unquote, parse_qsl
from io import StringIO
import queue
unicode = None

__url_regex = r'(([a-z0-9][a-z0-9-]{1,5}[a-z0-9]\.[a-z0-9]{2,20})|(opensubtitles))\.[a-z]{2,5}'
Expand Down
93 changes: 55 additions & 38 deletions a4kSubtitles/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@ def __query_service(core, service_name, meta, request, results):

results.extend(service_results)

core.progress_text = core.progress_text.replace(service.display_name, '')
core.kodi.update_progress(core)

core.logger.debug(lambda: core.json.dumps({
'url': request['url'],
'count': len(service_results),
'status_code': response.status_code
}, indent=2))

def __add_results(core, results):
def __add_results(core, results): # pragma: no cover
for item in results:
listitem = core.kodi.create_listitem(item)

Expand Down Expand Up @@ -94,26 +97,28 @@ def __apply_limit(core, all_results, meta):

return results[:limit]

def __prepare_results(core, meta, results):
results = __apply_language_filter(meta, results)
results = __sanitize_results(core, meta, results)

sorter = lambda x: (
not x['lang'] == meta.preferredlanguage,
meta.languages.index(x['lang']),
not x['sync'] == 'true',
-core.difflib.SequenceMatcher(None, x['name'], meta.filename).ratio(),
-x['rating'],
not x['impaired'] == 'true',
x['service'],
)

results = sorted(results, key=sorter)
results = __apply_limit(core, results, meta)
results = sorted(results, key=sorter)

return results

def __parse_languages(core, languages):
return list({language for language in (__parse_language(core, x) for x in languages) if language is not None})

def __parse_language(core, language):
if language == 'original':
audio_streams = core.kodi.xbmc.Player().getAvailableAudioStreams()
if len(audio_streams) == 0:
return None
return core.kodi.xbmc.convertLanguage(
audio_streams[0],
core.kodi.xbmc.ENGLISH_NAME
)
elif language == 'default':
return core.kodi.xbmc.getLanguage()
elif language == 'none':
return None
elif language == 'forced_only':
return __parse_language(core, core.kodi.get_kodi_setting("locale.audiolanguage"))
else:
return language
return list({language for language in (core.kodi.parse_language(x) for x in languages) if language is not None})

def __chain_auth_and_search_threads(core, auth_thread, search_thread):
auth_thread.start()
Expand Down Expand Up @@ -153,7 +158,7 @@ def __search(core, service_name, meta, results):
def search(core, params):
meta = core.video.get_meta()
meta.languages = __parse_languages(core, core.utils.unquote(params['languages']).split(','))
meta.preferredlanguage = __parse_language(core, params['preferredlanguage'])
meta.preferredlanguage = core.kodi.parse_language(params['preferredlanguage'])
core.logger.debug(lambda: core.json.dumps(meta, default=lambda o: '', indent=2))

if meta.imdb_id == '':
Expand All @@ -174,6 +179,7 @@ def search(core, params):
continue

service = core.services[service_name]
core.progress_text += service.display_name + '|'
auth_thread = None

auth_request = service.build_auth_request(core, service_name)
Expand All @@ -187,23 +193,34 @@ def search(core, params):
if len(threads) == 0:
return __complete_search(core, last_query_results)

__wait_threads(core, threads)
results = __apply_language_filter(meta, results)
results = __sanitize_results(core, meta, results)
core.progress_text = core.progress_text[:-1]
core.kodi.update_progress(core)

sorter = lambda x: (
not x['lang'] == meta.preferredlanguage,
meta.languages.index(x['lang']),
not x['sync'] == 'true',
-core.difflib.SequenceMatcher(None, x['name'], meta.filename).ratio(),
-x['rating'],
not x['impaired'] == 'true',
x['service'],
)
ready_queue = core.utils.queue.Queue()
cancellation_token = lambda: None
cancellation_token.iscanceled = False

results = sorted(results, key=sorter)
results = __apply_limit(core, results, meta)
results = sorted(results, key=sorter)
__save_results(core, meta, results)
def check_cancellation(): # pragma: no cover
dialog = core.progress_dialog
while (dialog is not None and not cancellation_token.iscanceled):
if not dialog.iscanceled():
core.time.sleep(1)
continue

cancellation_token.iscanceled = True
final_results = __prepare_results(core, meta, results)
ready_queue.put(__complete_search(core, final_results))
break

def wait_all_results():
__wait_threads(core, threads)
if cancellation_token.iscanceled:
return
final_results = __prepare_results(core, meta, results)
__save_results(core, meta, final_results)
ready_queue.put(__complete_search(core, final_results))

core.threading.Thread(target=check_cancellation).start()
core.threading.Thread(target=wait_all_results).start()

return __complete_search(core, results)
return ready_queue.get()
12 changes: 10 additions & 2 deletions a4kSubtitles/services/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,25 @@
import importlib
from a4kSubtitles.lib import utils

__all__ = utils.get_all_relative_py_files(__file__)
__all = utils.get_all_relative_py_files(__file__)
__display_names = {
'bsplayer': 'BSPlayer',
'opensubtitles': 'OpenSubtitles',
'podnadpisi': 'Podnadpisi',
'subdb': 'SubDB',
'subscene': 'Subscene',
}

def __set_fn_if_missing(service, fn_name, fn):
if not getattr(service, fn_name, None):
setattr(service, fn_name, fn)

services = {}
for service_name in __all__:
for service_name in __all:
service = services[service_name] = importlib.import_module('a4kSubtitles.services.%s' % service_name)

service.context = utils.DictAsObject({})
service.display_name = __display_names[service_name]

__set_fn_if_missing(service, 'build_auth_request', lambda _, __: None)

Expand Down
3 changes: 2 additions & 1 deletion a4kSubtitles/services/bsplayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ def build_search_requests(core, service_name, meta):
def parse_search_response(core, service_name, meta, response):
__logout(core, service_name)

service = core.services[service_name]
response = __parse_response(core, service_name, response.text)
if response is None:
return []
Expand All @@ -170,7 +171,7 @@ def map_result(result):

return {
'service_name': service_name,
'service': 'BSPlayer',
'service': service.display_name,
'lang': lang,
'name': name,
'rating': int(round(float(rating) / 2)) if rating else 0,
Expand Down
4 changes: 3 additions & 1 deletion a4kSubtitles/services/opensubtitles.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,12 @@ def parse_search_response(core, service_name, meta, response):
core.logger.error('%s - %s' % (service_name, exc))
return []

service = core.services[service_name]

def map_result(result):
return {
'service_name': service_name,
'service': 'OpenSubtitles',
'service': service.display_name,
'lang': result['LanguageName'],
'name': result['SubFileName'],
'rating': int(round(float(result['SubRating']) / 2)),
Expand Down
3 changes: 2 additions & 1 deletion a4kSubtitles/services/podnadpisi.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def parse_search_response(core, service_name, meta, response):
core.logger.error('%s - %s' % (service_name, exc))
return []

service = core.services[service_name]
lang_ids = core.utils.get_lang_ids(meta.languages, core.kodi.xbmc.ISO_639_1)

def map_result(result):
Expand All @@ -56,7 +57,7 @@ def map_result(result):

return {
'service_name': service_name,
'service': 'Podnadpisi',
'service': service.display_name,
'lang': lang,
'name': name,
'rating': 0,
Expand Down
4 changes: 3 additions & 1 deletion a4kSubtitles/services/subdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ def build_search_requests(core, service_name, meta):
return [request]

def parse_search_response(core, service_name, meta, response):
service = core.services[service_name]

lang_ids = core.utils.get_lang_ids(meta.languages, core.kodi.xbmc.ISO_639_1)
name = '%s.srt' % meta.filename_without_ext
results = []
Expand All @@ -30,7 +32,7 @@ def parse_search_response(core, service_name, meta, response):

results.append({
'service_name': service_name,
'service': 'SubDB',
'service': service.display_name,
'lang': lang,
'name': name,
'rating': 0,
Expand Down
5 changes: 3 additions & 2 deletions a4kSubtitles/services/subscene.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ def build_search_requests(core, service_name, meta):
return [request]

def parse_search_response(core, service_name, meta, response):
title_href = core.services[service_name].context.title_href
service = core.services[service_name]
title_href = service.context.title_href
any_regex = r'.*?'

results_regex = (
Expand Down Expand Up @@ -73,7 +74,7 @@ def map_result(result):

return {
'service_name': service_name,
'service': 'Subscene',
'service': service.display_name,
'lang': lang,
'name': name_with_ext,
'rating': 0,
Expand Down
6 changes: 5 additions & 1 deletion addon.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="service.subtitles.a4ksubtitles"
name="a4kSubtitles"
version="0.0.22"
version="0.0.23"
provider-name="Unknown">
<requires>
<import addon="xbmc.python" version="2.25.0"/>
Expand All @@ -24,6 +24,10 @@ Supports: OpenSubtitles, BSPlayer, Podnadpisi.NET, SubDB, Subscene
<icon>icon.png</icon>
</assets>
<news>
[v0.0.23]:
* Add progress dialog
* Support cancellation

[v0.0.22]:
* Fix meta string conversion issue

Expand Down
Loading

0 comments on commit 0a16b69

Please sign in to comment.