Skip to content

Commit

Permalink
[service.subtitles.rvm.addic7ed] 3.2.2
Browse files Browse the repository at this point in the history
  • Loading branch information
romanvm committed Oct 6, 2024
1 parent 4190487 commit b2a0472
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 36 deletions.
39 changes: 19 additions & 20 deletions service.subtitles.rvm.addic7ed/addic7ed/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import xbmcplugin

from addic7ed import parser
from addic7ed.addon import ADDON, PROFILE, ICON, get_ui_string
from addic7ed.addon import ADDON, PROFILE, ICON, GettextEmulator
from addic7ed.exceptions import NoSubtitlesReturned, ParseError, SubsSearchError, \
Add7ConnectionError
from addic7ed.parser import parse_filename, normalize_showname, get_languages
Expand All @@ -35,9 +35,11 @@

__all__ = ['router']

_ = GettextEmulator.gettext

logger = logging.getLogger(__name__)

TEMP_DIR = os.path.join(PROFILE, 'temp')
TEMP_DIR = PROFILE / 'temp'
HANDLE = int(sys.argv[1])


Expand Down Expand Up @@ -135,24 +137,24 @@ def download_subs(link, referrer, filename):
label - the download location for subs.
"""
# Re-create a download location in a temporary folder
if not os.path.exists(PROFILE):
os.mkdir(PROFILE)
if os.path.exists(TEMP_DIR):
shutil.rmtree(TEMP_DIR)
os.mkdir(TEMP_DIR)
if not PROFILE.exists():
PROFILE.mkdir()
if TEMP_DIR.exists():
shutil.rmtree(str(TEMP_DIR))
TEMP_DIR.mkdir()
# Combine a path where to download the subs
filename = os.path.splitext(filename)[0] + '.srt'
subspath = os.path.join(TEMP_DIR, filename)
subspath = str(TEMP_DIR / filename)
# Download the subs from addic7ed.com
try:
Session().download_subs(link, referrer, subspath)
except Add7ConnectionError:
logger.error('Unable to connect to addic7ed.com')
DIALOG.notification(get_ui_string(32002), get_ui_string(32005), 'error')
DIALOG.notification(_('Error!'), _('Unable to connect to addic7ed.com.'), 'error')
except NoSubtitlesReturned:
DIALOG.notification(get_ui_string(32002), get_ui_string(32003), 'error',
DIALOG.notification(_('Error!'), _('Exceeded daily limit for subs downloads.'), 'error',
3000)
logger.error('Exceeded daily limit for subs downloads.')
logger.error('A HTML page returned instead of subtitles for link: %s', link)
else:
# Create a ListItem for downloaded subs and pass it
# to the Kodi subtitles engine to move the downloaded subs file
Expand All @@ -165,8 +167,7 @@ def download_subs(link, referrer, filename):
url=subspath,
listitem=list_item,
isFolder=False)
DIALOG.notification(get_ui_string(32000), get_ui_string(32001), ICON,
3000, False)
DIALOG.notification(_('Success!'), _('Subtitles downloaded.'), ICON, 3000, False)
logger.info('Subs downloaded.')


Expand Down Expand Up @@ -197,7 +198,7 @@ def extract_episode_data():
showname, season, episode = parse_filename(filename)
except ParseError:
logger.error('Unable to determine episode data for %s', filename)
DIALOG.notification(get_ui_string(32002), get_ui_string(32006),
DIALOG.notification(_('Error!'), _('Unable to determine episode data.'),
'error', 3000)
raise
else:
Expand Down Expand Up @@ -241,24 +242,22 @@ def search_subs(params):
results = parser.search_episode(query, languages)
except Add7ConnectionError:
logger.error('Unable to connect to addic7ed.com')
DIALOG.notification(
get_ui_string(32002), get_ui_string(32005), 'error'
)
DIALOG.notification(_('Error!'), _('Unable to connect to addic7ed.com.'), 'error')
except SubsSearchError:
logger.info('No subs for "%s" found.', query)
else:
if isinstance(results, list):
logger.info('Multiple episodes found:\n%s', results)
i = DIALOG.select(
get_ui_string(32008), [item.title for item in results]
_('Select episode'), [item.title for item in results]
)
if i >= 0:
try:
results = parser.get_episode(results[i].link, languages)
except Add7ConnectionError:
logger.error('Unable to connect to addic7ed.com')
DIALOG.notification(get_ui_string(32002),
get_ui_string(32005), 'error')
DIALOG.notification(_('Error!'),
_('Unable to connect to addic7ed.com.'), 'error')
return
except SubsSearchError:
logger.info('No subs found.')
Expand Down
101 changes: 90 additions & 11 deletions service.subtitles.rvm.addic7ed/addic7ed/addon.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,105 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

import os
import hashlib
import json
import re
from pathlib import Path

import xbmcaddon
from xbmcvfs import translatePath

__all__ = ['ADDON_ID', 'ADDON', 'ADDON_VERSION', 'PATH', 'PROFILE', 'ICON', 'get_ui_string']
__all__ = ['ADDON_ID', 'ADDON', 'ADDON_VERSION', 'PATH', 'PROFILE', 'ICON', 'GettextEmulator']

ADDON = xbmcaddon.Addon()
ADDON_ID = ADDON.getAddonInfo('id')
ADDON_VERSION = ADDON.getAddonInfo('version')
PATH = translatePath(ADDON.getAddonInfo('path'))
PROFILE = translatePath(ADDON.getAddonInfo('profile'))
ICON = os.path.join(PATH, 'icon.png')

PATH = Path(translatePath(ADDON.getAddonInfo('path')))
PROFILE = Path(translatePath(ADDON.getAddonInfo('profile')))
ICON = str(PATH / 'icon.png')

def get_ui_string(string_id):
"""
Get language string by ID

:param string_id: UI string ID
:return: UI string
class GettextEmulator:
"""
Emulate GNU Gettext by mapping resource.language.en_gb UI strings to their numeric string IDs
"""
return ADDON.getLocalizedString(string_id)
_instance = None

class LocalizationError(Exception): # pylint: disable=missing-docstring
pass

def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance

def __init__(self):
self._en_gb_string_po_path = (PATH / 'resources' / 'language' /
'resource.language.en_gb' / 'strings.po')
if not self._en_gb_string_po_path.exists():
raise self.LocalizationError(
'Missing resource.language.en_gb strings.po localization file')
if not PROFILE.exists():
PROFILE.mkdir()
self._string_mapping_path = PROFILE / 'strings-map.json'
self.strings_mapping = self._load_strings_mapping()

def _load_strings_po(self): # pylint: disable=missing-docstring
with self._en_gb_string_po_path.open('r', encoding='utf-8') as fo:
return fo.read()

def _load_strings_mapping(self):
"""
Load mapping of resource.language.en_gb UI strings to their IDs
If a mapping file is missing or resource.language.en_gb strins.po file has been updated,
a new mapping file is created.
:return: UI strings mapping
"""
strings_po = self._load_strings_po()
strings_po_md5 = hashlib.md5(strings_po.encode('utf-8')).hexdigest()
try:
with self._string_mapping_path.open('r', encoding='utf-8') as fo:
mapping = json.load(fo)
if mapping['md5'] != strings_po_md5:
raise IOError('resource.language.en_gb strings.po has been updated')
except (IOError, ValueError):
strings_mapping = self._parse_strings_po(strings_po)
mapping = {
'strings': strings_mapping,
'md5': strings_po_md5,
}
with self._string_mapping_path.open('w', encoding='utf-8') as fo:
json.dump(mapping, fo)
return mapping['strings']

@staticmethod
def _parse_strings_po(strings_po):
"""
Parse resource.language.en_gb strings.po file contents into a mapping of UI strings
to their numeric IDs.
:param strings_po: the content of strings.po file as a text string
:return: UI strings mapping
"""
id_string_pairs = re.findall(r'^msgctxt "#(\d+?)"\r?\nmsgid "(.*)"\r?$', strings_po, re.M)
return {string: int(string_id) for string_id, string in id_string_pairs if string}

@classmethod
def gettext(cls, en_string: str) -> str:
"""
Return a localized UI string by a resource.language.en_gb source string
:param en_string: resource.language.en_gb UI string
:return: localized UI string
"""
emulator = cls()
try:
string_id = emulator.strings_mapping[en_string]
except KeyError as exc:
raise cls.LocalizationError(
f'Unable to find "{en_string}" string in resource.language.en_gb/strings.po'
) from exc
return ADDON.getLocalizedString(string_id)
4 changes: 2 additions & 2 deletions service.subtitles.rvm.addic7ed/addic7ed/simple_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def headers(self) -> HTTPMessage:
@headers.setter
def headers(self, value: HTTPMessage):
charset = value.get_content_charset()
if charset is not None:
if charset:
self.encoding = charset
self._headers = value

Expand All @@ -124,7 +124,7 @@ def text(self) -> str:
if self._text is None:
try:
self._text = self.content.decode(self.encoding)
except UnicodeDecodeError:
except (UnicodeDecodeError, LookupError):
self._text = self.content.decode('utf-8', 'replace')
return self._text

Expand Down
7 changes: 4 additions & 3 deletions service.subtitles.rvm.addic7ed/addon.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<addon id="service.subtitles.rvm.addic7ed"
name="Addic7ed.com"
version="3.2.1"
version="3.2.2"
provider-name="Roman V.M.">
<requires>
<import addon="xbmc.python" version="3.0.0"/>
Expand Down Expand Up @@ -29,8 +29,9 @@
<icon>icon.png</icon>
<fanart>fanart.jpg</fanart>
</assets>
<news>3.2.1:
- Fixed issues with downloading subtitles.</news>
<news>3.2.2:
- Fixed the issue with an empty content-charset header.
- Internal changes.</news>
<reuselanguageinvoker>true</reuselanguageinvoker>
</extension>
</addon>

0 comments on commit b2a0472

Please sign in to comment.