Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

release: v1.4.0 #39

Merged
merged 1 commit into from
May 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
[run]
omit =
main.py
main_service.py
a4kSubtitles/lib/num2ordinal.py

[report]
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
* [v1.4.0](https://github.com/newt-sc/a4kSubtitles/releases/tag/service.subtitles.a4ksubtitles%2Fservice.subtitles.a4ksubtitles-1.4.0):
* Add option to auto open the search dialog when the video does not have subtitles

* [v1.3.0](https://github.com/newt-sc/a4kSubtitles/releases/tag/service.subtitles.a4ksubtitles%2Fservice.subtitles.a4ksubtitles-1.3.0):
* Improve tvshow year scraping

Expand Down
6 changes: 3 additions & 3 deletions a4kSubtitles/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def restore():
self.core.kodi.xbmcvfs.File.subdb_hash = default____
return restore

def __mock_settings(self, settings):
def mock_settings(self, settings):
default = self.core.kodi.addon.getSetting

def get_setting(id):
Expand All @@ -84,7 +84,7 @@ def search(self, params, settings=None, video_meta=None):

try:
if settings:
restore_settings = self.__mock_settings(settings)
restore_settings = self.mock_settings(settings)

if video_meta:
restore_video_meta = self.__mock_video_meta(video_meta)
Expand All @@ -101,7 +101,7 @@ def download(self, params, settings=None):

try:
if settings:
restore_settings = self.__mock_settings(settings)
restore_settings = self.mock_settings(settings)

return self.core.download(self.core, params)
finally:
Expand Down
6 changes: 6 additions & 0 deletions a4kSubtitles/lib/kodi_mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
xbmc.executeJSONRPC = lambda _: '{ "result": { "value": true } }'
xbmc.executebuiltin = lambda _: None
xbmc.getCleanMovieTitle = lambda t: t
xbmc.getCondVisibility = lambda _: False

xbmc.convertLanguage = lambda l, f: l[:f].lower()
xbmc.ISO_639_1 = 2
Expand All @@ -28,6 +29,11 @@
__player.getPlayingFile = lambda: ''
xbmc.Player = lambda: __player

__monitor = lambda: None
__monitor.abortRequested = lambda: False
__monitor.waitForAbort = lambda _: False
xbmc.Monitor = lambda: __monitor

def __log(msg, label):
print(msg)
xbmc.log = __log
Expand Down
29 changes: 29 additions & 0 deletions a4kSubtitles/service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-

def start(core):
monitor = core.kodi.xbmc.Monitor()
has_done_subs_check = False

while not monitor.abortRequested():
if monitor.waitForAbort(1):
break

if not core.kodi.get_bool_setting('general', 'auto_search'):
continue

has_video = core.kodi.xbmc.getCondVisibility('Player.HasVideo')
if not has_video and has_done_subs_check:
has_done_subs_check = False

if not has_video or has_done_subs_check:
continue

has_done_subs_check = True

has_subtitles_enabled = core.kodi.xbmc.getCondVisibility('VideoPlayer.SubtitlesEnabled')
has_subtitles = core.kodi.xbmc.getCondVisibility('VideoPlayer.HasSubtitles') and has_subtitles_enabled

if not has_subtitles:
core.time.sleep(0.5)
core.kodi.xbmc.executebuiltin('ActivateWindow(SubtitleSearch)')
continue
6 changes: 5 additions & 1 deletion addon.xml
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="service.subtitles.a4ksubtitles"
name="a4kSubtitles"
version="1.3.0"
version="1.4.0"
provider-name="Unknown">
<requires>
<import addon="xbmc.python" version="2.25.0"/>
<import addon="vfs.libarchive"/>
<import addon="script.module.requests"/>
</requires>
<extension point="xbmc.subtitle.module" library="main.py"/>
<extension point="xbmc.service" library="main_service.py" />
<extension point="xbmc.addon.metadata">
<summary lang="en">a4kSubtitles - Multi-Source Subtitles Addon</summary>
<description>
Expand All @@ -27,6 +28,9 @@ Supports: OpenSubtitles, BSPlayer, Podnadpisi.NET, SubDB, Subscene, Addic7ed
<screenshot>screenshot-03.png</screenshot>
</assets>
<news>
[v1.4.0]:
* Add option to auto open the search dialog when the video does not have subtitles

[v1.3.0]:
* Improve tvshow year scraping

Expand Down
10 changes: 10 additions & 0 deletions main_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-

import os
import importlib
from a4kSubtitles import api, service

if __name__ == '__main__':
os.environ.pop(api.api_mode_env_name, '')
core = importlib.import_module('a4kSubtitles.core')
service.start(core)
6 changes: 5 additions & 1 deletion packages/addons.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
<addons>
<addon id="service.subtitles.a4ksubtitles"
name="a4kSubtitles"
version="1.3.0"
version="1.4.0"
provider-name="Unknown">
<requires>
<import addon="xbmc.python" version="2.25.0"/>
<import addon="vfs.libarchive"/>
<import addon="script.module.requests"/>
</requires>
<extension point="xbmc.subtitle.module" library="main.py"/>
<extension point="xbmc.service" library="main_service.py" />
<extension point="xbmc.addon.metadata">
<summary lang="en">a4kSubtitles - Multi-Source Subtitles Addon</summary>
<description>
Expand All @@ -30,6 +31,9 @@ Supports: OpenSubtitles, BSPlayer, Podnadpisi.NET, SubDB, Subscene, Addic7ed
<screenshot>screenshot-03.png</screenshot>
</assets>
<news>
[v1.4.0]:
* Add option to auto open the search dialog when the video does not have subtitles

[v1.3.0]:
* Improve tvshow year scraping

Expand Down
2 changes: 1 addition & 1 deletion packages/addons.xml.crc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
61ee54c24b8a4b1ee015abe153168a80843b7fc8
6e36d33f6e61337b90cf18add8c7f42fabd29b2a
4 changes: 4 additions & 0 deletions resources/language/resource.language.en_gb/strings.po
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ msgctxt "#33102"
msgid "Results Limit"
msgstr ""

msgctxt "#33103"
msgid "Auto Search (Will not trigger if subs are available)"
msgstr ""

# Services
msgctxt "#33201"
msgid "OpenSubtitles"
Expand Down
1 change: 1 addition & 0 deletions resources/settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<category label="33001">
<setting id="general.timeout" label="33101" type="slider" default="15" option="int" range="5,20"/>
<setting id="general.results_limit" label="33102" type="slider" default="20" option="int" range="10,100"/>
<setting id="general.auto_search" label="33103" type="bool" default="false"/>
</category>
<!-- Services -->
<category label="33002">
Expand Down
23 changes: 23 additions & 0 deletions tests/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-

import sys
import os
import json
import re
import pytest
import time

dir_name = os.path.dirname(__file__)
main = os.path.join(dir_name, '..')
a4kSubtitles = os.path.join(main, '..', 'a4kSubtitles')
lib = os.path.join(a4kSubtitles, 'lib')
services = os.path.join(a4kSubtitles, 'services')

sys.path.append(dir_name)
sys.path.append(main)
sys.path.append(a4kSubtitles)
sys.path.append(lib)
sys.path.append(services)

from a4kSubtitles import api, service
from tests import utils
30 changes: 30 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-

from .common import pytest, api

def test_api_mocking():
def get_error_msg(e):
return str(e.value).replace('\'', '')

with pytest.raises(ImportError) as e:
api.A4kSubtitlesApi()
assert get_error_msg(e) == "No module named xbmc"

with pytest.raises(ImportError) as e:
api.A4kSubtitlesApi({'xbmc': True})
assert get_error_msg(e) == "No module named xbmcaddon"

with pytest.raises(ImportError) as e:
api.A4kSubtitlesApi({'xbmc': True, 'xbmcaddon': True})
assert get_error_msg(e) == "No module named xbmcplugin"

with pytest.raises(ImportError) as e:
api.A4kSubtitlesApi({'xbmc': True, 'xbmcaddon': True, 'xbmcplugin': True})
assert get_error_msg(e) == "No module named xbmcgui"

with pytest.raises(ImportError) as e:
api.A4kSubtitlesApi({'xbmc': True, 'xbmcaddon': True, 'xbmcplugin': True, 'xbmcgui': True})
assert get_error_msg(e) == "No module named xbmcvfs"

api.A4kSubtitlesApi({'xbmc': True, 'xbmcaddon': True, 'xbmcplugin': True, 'xbmcgui': True, 'xbmcvfs': True})
api.A4kSubtitlesApi({'kodi': True})
163 changes: 163 additions & 0 deletions tests/test_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# -*- coding: utf-8 -*-

from .common import api, service, utils

def __mock_monitor(api):
monitor = lambda: None
monitor.__check_num = 1
def __monitor_is_at_least_third_check():
if monitor.__check_num < 3:
monitor.__check_num += 1
return False
return True
monitor.abortRequested = lambda: __monitor_is_at_least_third_check()
monitor.waitForAbort = lambda _: False

default = api.core.kodi.xbmc.Monitor
api.core.kodi.xbmc.Monitor = lambda: monitor
def restore():
api.core.kodi.xbmc.Monitor = default
return restore

def __mock_get_cond_visibility(api, mock_data):
default = api.core.kodi.xbmc.getCondVisibility
api.core.kodi.xbmc.getCondVisibility = lambda v: mock_data.get(v, False)
def restore():
api.core.kodi.xbmc.getCondVisibility = default
return restore

def __mock_time_sleep(api):
default = api.core.time.sleep
api.core.time.sleep = lambda _: None
def restore():
api.core.time.sleep = default
return restore

def __mock(api, settings):
restore_monitor = __mock_monitor(api)
restore_time_sleep = __mock_time_sleep(api)
restore_settings = api.mock_settings(settings)

def restore():
restore_monitor()
restore_time_sleep()
restore_settings()
return restore

def test_service_start_when_disabled():
a4ksubtitles_api = api.A4kSubtitlesApi({'kodi': True})

restore = __mock(a4ksubtitles_api, {
'general.auto_search': 'false',
})
get_cond_visibility_spy = utils.spy_fn(a4ksubtitles_api.core.kodi.xbmc, 'getCondVisibility')

service.start(a4ksubtitles_api.core)

restore()
get_cond_visibility_spy.restore()

assert get_cond_visibility_spy.call_count == 0

def test_service_start_when_enabled():
a4ksubtitles_api = api.A4kSubtitlesApi({'kodi': True})

restore = __mock(a4ksubtitles_api, {
'general.auto_search': 'true',
})
get_cond_visibility_spy = utils.spy_fn(a4ksubtitles_api.core.kodi.xbmc, 'getCondVisibility')

service.start(a4ksubtitles_api.core)

restore()
get_cond_visibility_spy.restore()

assert get_cond_visibility_spy.call_count > 0

def test_service_when_video_has_subtitles():
a4ksubtitles_api = api.A4kSubtitlesApi({'kodi': True})

restore = __mock(a4ksubtitles_api, {
'general.auto_search': 'true',
})
restore_get_cond_visibility = __mock_get_cond_visibility(a4ksubtitles_api, {
'Player.HasVideo': True,
'VideoPlayer.HasSubtitles': True,
'VideoPlayer.SubtitlesEnabled': True,
})

executebuiltin_spy = utils.spy_fn(a4ksubtitles_api.core.kodi.xbmc, 'executebuiltin')

service.start(a4ksubtitles_api.core)

restore()
restore_get_cond_visibility()
executebuiltin_spy.restore()

assert executebuiltin_spy.call_count == 0

def test_service_when_video_does_not_have_subtitles():
a4ksubtitles_api = api.A4kSubtitlesApi({'kodi': True})

restore = __mock(a4ksubtitles_api, {
'general.auto_search': 'true',
})
restore_get_cond_visibility = __mock_get_cond_visibility(a4ksubtitles_api, {
'Player.HasVideo': True,
'VideoPlayer.HasSubtitles': False,
'VideoPlayer.SubtitlesEnabled': False,
})

executebuiltin_spy = utils.spy_fn(a4ksubtitles_api.core.kodi.xbmc, 'executebuiltin')

service.start(a4ksubtitles_api.core)

restore()
restore_get_cond_visibility()
executebuiltin_spy.restore()

assert executebuiltin_spy.call_count == 1

def test_service_when_video_has_disabled_subtitles():
a4ksubtitles_api = api.A4kSubtitlesApi({'kodi': True})

restore = __mock(a4ksubtitles_api, {
'general.auto_search': 'true',
})
restore_get_cond_visibility = __mock_get_cond_visibility(a4ksubtitles_api, {
'Player.HasVideo': True,
'VideoPlayer.HasSubtitles': True,
'VideoPlayer.SubtitlesEnabled': False,
})

executebuiltin_spy = utils.spy_fn(a4ksubtitles_api.core.kodi.xbmc, 'executebuiltin')

service.start(a4ksubtitles_api.core)

restore()
restore_get_cond_visibility()
executebuiltin_spy.restore()

assert executebuiltin_spy.call_count == 1

def test_service_when_subs_check_done():
a4ksubtitles_api = api.A4kSubtitlesApi({'kodi': True})

restore = __mock(a4ksubtitles_api, {
'general.auto_search': 'true',
})
restore_get_cond_visibility = __mock_get_cond_visibility(a4ksubtitles_api, {
'Player.HasVideo': True,
'VideoPlayer.HasSubtitles': True,
'VideoPlayer.SubtitlesEnabled': True,
})

executebuiltin_spy = utils.spy_fn(a4ksubtitles_api.core.kodi.xbmc, 'executebuiltin')

service.start(a4ksubtitles_api.core)

restore()
restore_get_cond_visibility()
executebuiltin_spy.restore()

assert executebuiltin_spy.call_count == 0
Loading