285 lines
9.0 KiB
Python
285 lines
9.0 KiB
Python
# coding=utf-8
|
|
#
|
|
# Copyright (C) 2018 Dmitry Vinogradov
|
|
# https://github.com/kodi-iptv-addons
|
|
#
|
|
# This library is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU Library General Public
|
|
# License as published by the Free Software Foundation; either
|
|
# version 2 of the License, or (at your option) any later version.
|
|
#
|
|
# This library is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# Library General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Library General Public
|
|
# License along with this library; if not, write to the
|
|
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
|
# Boston, MA 02110-1301, USA.
|
|
#
|
|
import builtins
|
|
import calendar
|
|
import datetime
|
|
import math
|
|
import os
|
|
import platform
|
|
import sys
|
|
import threading
|
|
import time
|
|
from functools import wraps
|
|
|
|
import xbmc
|
|
import xbmcaddon
|
|
import importlib
|
|
|
|
if hasattr(builtins, 'addon_id') is False:
|
|
setattr(builtins, 'addon_id', os.path.basename(os.path.abspath(os.path.dirname(__file__))))
|
|
|
|
addon = xbmcaddon.Addon(id=getattr(builtins, 'addon_id'))
|
|
|
|
utc_local_offset = math.ceil(calendar.timegm(time.localtime()) - time.time())
|
|
|
|
importlib.reload(sys)
|
|
# noinspection PyUnresolvedReferences
|
|
# sys.setdefaultencoding('utf-8')
|
|
|
|
TENSECS = 10 # type: int
|
|
MIN = 60 # type: int
|
|
HALFHOUR = 1800 # type: int
|
|
HOUR = 3600 # type: int
|
|
DAY = 86400 # type: int
|
|
TWODAYS = 172800 # type: int
|
|
TREEDAYS = 259200 # type: int
|
|
WEEK = 604800 # type: int
|
|
TWOWEEKS = 1209600 # type: int
|
|
|
|
TEXT_SUBSCRIPTION_REQUIRED_ID = 30101 # type: int
|
|
TEXT_SET_CREDENTIALS_ID = 30102 # type: int
|
|
TEXT_AUTHENTICATION_FAILED_ID = 30103 # type: int
|
|
TEXT_CHECK_SETTINGS_ID = 30104 # type: int
|
|
TEXT_NOT_PLAYABLE_ID = 30105 # type: int
|
|
TEXT_SERVICE_ERROR_OCCURRED_ID = 30106 # type: int
|
|
TEXT_SURE_TO_EXIT_ID = 30107 # type: int
|
|
TEXT_ARCHIVE_NOT_AVAILABLE_YET_ID = 30108 # type: int
|
|
TEXT_JUMP_TO_ARCHIVE_ID = 30109 # type: int
|
|
TEXT_CHANNEL_HAS_NO_ARCHIVE_ID = 30110 # type: int
|
|
TEXT_LIVE_NO_FORWARD_SKIP_ID = 30111 # type: int
|
|
TEXT_IDLE_DIALOG_ID = 30112 # type: int
|
|
TEXT_IDLE_DIALOG_COUNTDOWN_ID = 30113 # type: int
|
|
TEXT_HTTP_REQUEST_ERROR_ID = 30114 # type: int
|
|
TEXT_PLEASE_RESTART_KODI_ID = 30115 # type: int
|
|
TEXT_INSTALL_EXTRA_RESOURCES_ID = 30116 # type: int
|
|
TEXT_PLEASE_CHECK_INTERNET_CONNECTION_ID = 30117 # type: int
|
|
TEXT_UNEXPECTED_RESPONSE_FROM_SERVICE_PROVIDER_ID = 30118 # type: int
|
|
TEXT_UNEXPECTED_ERROR_OCCURRED_ID = 30119 # type: int
|
|
|
|
TEXT_NO_INFO_AVAILABLE_ID = 30201 # type: int
|
|
TEXT_ABBR_MINUTES_ID = 30202 # type: int
|
|
TEXT_ABBR_SECONDS_ID = 30203 # type: int
|
|
TEXT_WEEKDAY_FULL_ID_PREFIX = 3030 # type: int
|
|
TEXT_WEEKDAY_ABBR_ID_PREFIX = 3031 # type: int
|
|
TEXT_MONTH_FULL_ID_PREFIX = 304 # type: int
|
|
TEXT_MONTH_ABBR_ID_PREFIX = 305 # type: int
|
|
|
|
|
|
# noinspection PyUnresolvedReferences
|
|
class WindowMixin(object):
|
|
is_closing = False # type: bool
|
|
|
|
def __init__(self, **kwargs):
|
|
self.is_closing = False
|
|
super(WindowMixin, self).__init__()
|
|
|
|
def close(self):
|
|
self.is_closing = True
|
|
super(WindowMixin, self).close()
|
|
|
|
def show_control(self, *control_ids):
|
|
for control_id in control_ids:
|
|
control = self.getControl(control_id)
|
|
if control:
|
|
control.setVisible(True)
|
|
|
|
def hide_control(self, *control_ids):
|
|
for control_id in control_ids:
|
|
control = self.getControl(control_id)
|
|
if control:
|
|
control.setVisible(False)
|
|
|
|
def set_control_image(self, control_id, image):
|
|
control = self.getControl(control_id)
|
|
if control:
|
|
control.setImage(image)
|
|
|
|
def setcontrol_label(self, control_id, label):
|
|
control = self.getControl(control_id)
|
|
if control and label:
|
|
control.setLabel(label)
|
|
|
|
def set_control_text(self, control_id, text):
|
|
control = self.getControl(control_id)
|
|
if control:
|
|
control.setText(text)
|
|
|
|
|
|
def get_string(id_):
|
|
return xbmcaddon.Addon(os.path.basename(os.path.abspath(os.path.dirname(__file__) + "/../../"))).getLocalizedString(id_)
|
|
|
|
|
|
def show_small_popup(title='', msg='', delay=5000, image=''):
|
|
xbmc.executebuiltin('XBMC.Notification("%s","%s",%d,"%s")' % (title, msg, delay, image))
|
|
|
|
|
|
def build_user_agent():
|
|
# type: () -> str
|
|
return 'KODI/%s (%s; %s %s; python %s) %s/%s ' % (
|
|
xbmc.getInfoLabel('System.BuildVersion').split(" ")[0],
|
|
xbmc.getInfoLabel('System.BuildVersion'),
|
|
platform.system(),
|
|
platform.release(),
|
|
platform.python_version(),
|
|
addon.getAddonInfo('id').replace('-DEV', ''),
|
|
addon.getAddonInfo('version')
|
|
)
|
|
|
|
|
|
def unique(s, t):
|
|
# type: (str, str) -> str
|
|
t = (t * ((len(s) // len(t)) + 1))[:len(s)]
|
|
if isinstance(s, str):
|
|
return "".join(chr(ord(a) ^ ord(b)) for a, b in zip(s, t))
|
|
else:
|
|
return str([a ^ b for a, b in zip(s, t)])
|
|
|
|
|
|
def secs_to_percent(length, played):
|
|
# type: (int, float) -> float
|
|
return (100 * played) / length
|
|
|
|
|
|
def percent_to_secs(length, percent):
|
|
# type: (int, float) -> int
|
|
return int((length * percent) / 100)
|
|
|
|
|
|
def format_secs(secs, id="time"):
|
|
# type: (int, str) -> str
|
|
if id == "time":
|
|
return "{:0>8}".format(str(datetime.timedelta(seconds=secs)))
|
|
if id == "skip":
|
|
prefix = "+"
|
|
if secs < 0:
|
|
prefix = "-"
|
|
secs *= -1
|
|
elif secs == 0:
|
|
prefix = ""
|
|
if secs > 60:
|
|
return "%s%s %s" % (prefix, secs / 60, get_string(TEXT_ABBR_MINUTES_ID))
|
|
return "%s%s %s" % (prefix, secs, get_string(TEXT_ABBR_SECONDS_ID))
|
|
|
|
|
|
def format_date(timestamp, id="dateshort", custom_format=None):
|
|
# type: (float, str, str) -> str
|
|
ids = {
|
|
"%A": (TEXT_WEEKDAY_FULL_ID_PREFIX, "%w"),
|
|
"%a": (TEXT_WEEKDAY_ABBR_ID_PREFIX, "%w"),
|
|
"%B": (TEXT_MONTH_FULL_ID_PREFIX, "%m"),
|
|
"%b": (TEXT_MONTH_ABBR_ID_PREFIX, "%m")
|
|
}
|
|
if timestamp:
|
|
if custom_format is not None:
|
|
dt = datetime.datetime.fromtimestamp(timestamp)
|
|
for k in ids.keys():
|
|
if k in custom_format:
|
|
v = get_string(int("%s%s" % (ids[k][0], dt.strftime(ids[k][1]))))
|
|
custom_format = custom_format.replace(k, v)
|
|
return dt.strftime(custom_format)
|
|
return datetime.datetime.fromtimestamp(timestamp).strftime(xbmc.getRegion(id))
|
|
return ''
|
|
|
|
|
|
def time_now():
|
|
# type: () -> float
|
|
return time.time()
|
|
|
|
|
|
def timestamp_to_midnight(timestamp):
|
|
# type: (float) -> int
|
|
# noinspection PyTypeChecker
|
|
return int(time.mktime(
|
|
datetime.datetime.combine(
|
|
datetime.datetime.fromtimestamp(timestamp),
|
|
datetime.datetime.min.time()
|
|
).timetuple()
|
|
))
|
|
|
|
|
|
def str_to_datetime(str_date, fmt):
|
|
# type: (str, str) -> datetime.datetime
|
|
try:
|
|
d = datetime.datetime.strptime(str_date, fmt)
|
|
except TypeError:
|
|
from time import strptime
|
|
d = datetime.datetime(*(strptime(str_date, fmt)[0:6]))
|
|
return d
|
|
|
|
|
|
def str_to_timestamp(str_date, fmt):
|
|
# type: (str, str) -> int
|
|
try:
|
|
return int(time.mktime(str_to_datetime(str_date, fmt).timetuple()))
|
|
except:
|
|
return 0
|
|
|
|
|
|
def run_async(func):
|
|
"""
|
|
Decorator to run a function in a separate thread
|
|
"""
|
|
|
|
@wraps(func)
|
|
def async_func(*args, **kwargs):
|
|
thread = threading.Thread(target=func, args=args, kwargs=kwargs)
|
|
thread.start()
|
|
return thread
|
|
|
|
return async_func
|
|
|
|
|
|
def log(msg, level=xbmc.LOGINFO):
|
|
if level == xbmc.LOGDEBUG:
|
|
import inspect
|
|
mod = inspect.getmodule(inspect.stack()[1][0])
|
|
calframe = inspect.getouterframes(inspect.currentframe(), 2)
|
|
msg = "------- [%s.%s] : %s" % (mod.__name__, calframe[1][3], msg)
|
|
xbmc.log('%s: %s' % (addon.getAddonInfo('name'), msg), level)
|
|
|
|
|
|
def normalize(text):
|
|
# type: (str) -> str
|
|
if type(text) is not str:
|
|
text = str(text)
|
|
symbols = ("абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ",
|
|
"abvgdeejzijklmnoprstufhzcss_y_euaABVGDEEJZIJKLMNOPRSTUFHZCSS_Y_EUA")
|
|
tr = dict([(ord(a), ord(b)) for (a, b) in zip(*symbols)])
|
|
import re
|
|
regs = [
|
|
'\s+\+[0-9]+', # time shift suffix, e.g. "RTL +7"
|
|
'\s+\[[a-zA-Z]+\]', # land code suffix, e.g. "RTL [de]"
|
|
'\s+(HQ)', # High quality suffix, e.g. "RTL (HQ)"
|
|
]
|
|
for reg in regs:
|
|
text = re.sub(reg, '', text)
|
|
return re.sub('[^0-9a-zA-Z+-]+', '', text.translate(tr)).upper()
|
|
|
|
|
|
x = lambda s: s.decode("hex")
|
|
z = lambda s: str.encode(s, "hex")
|
|
h1 = '3d37612b5542244c4e3952775a3f6b5a24367732426e583750'
|
|
h2 = '5543155b26780b63394e25593d50043d48535a532c0f344e24' \
|
|
'54541205362d49632d563e1b3f5c1f65505f130f172f750e66' \
|
|
'0b03541f65710978684f6f467c4b563f52531946640b3b0a2b' \
|
|
'4011044a6839596a2b556f0c27190e2c194d0a1421073c0a2b' \
|
|
'40113e162e3f'
|