diff --git a/plugin.video.alfa/channels/newpct1.py b/plugin.video.alfa/channels/newpct1.py index a55f0871..5c89f60b 100644 --- a/plugin.video.alfa/channels/newpct1.py +++ b/plugin.video.alfa/channels/newpct1.py @@ -264,6 +264,7 @@ def submenu_novedades(item): itemlist = [] itemlist_alt = [] item.extra2 = '' + thumb_buscar = get_thumb("search.png") #Renombramos el canal al nombre de clone inicial desde la URL item.channel_host = host @@ -327,6 +328,9 @@ def submenu_novedades(item): item.post = "date=%s" % value itemlist.append(item.clone(action="listado_busqueda", title=title, url=item.url, post=item.post)) + itemlist.append( + Item(channel=item.channel, action="search", title="Buscar", url=item.channel_host + "buscar", thumbnail=thumb_buscar, category=item.category, channel_host=item.channel_host)) + itemlist.append(item.clone(action='', title="[COLOR yellow]Lo Último en la Categoría:[/COLOR]")) for value, title in matches: if value.isdigit(): diff --git a/plugin.video.alfa/channels/setting.py b/plugin.video.alfa/channels/setting.py index 78e02242..da6bb262 100644 --- a/plugin.video.alfa/channels/setting.py +++ b/plugin.video.alfa/channels/setting.py @@ -306,6 +306,8 @@ def submenu_tools(item): itemlist.append(Item(channel=CHANNELNAME, action="check_quickfixes", folder=False, title="Comprobar actualizaciones urgentes", plot="Versión actual: %s" % config.get_addon_version() )) + itemlist.append(Item(channel=CHANNELNAME, action="update_quasar", folder=False, + title="Actualizar addon externo Quasar")) itemlist.append(Item(channel=CHANNELNAME, action="", title="", folder=False, thumbnail=get_thumb("setting_0.png"))) @@ -336,6 +338,18 @@ def check_quickfixes(item): return updater.check_addon_updates(verbose=True) +def update_quasar(item): + logger.info() + + from platformcode import custom_code, platformtools + stat = False + stat = custom_code.update_external_addon("quasar") + if stat: + platformtools.dialog_notification("Actualización Quasar", "Realizada con éxito") + else: + platformtools.dialog_notification("Actualización Quasar", "Ha fallado. Consulte el log") + + def conf_tools(item): logger.info() diff --git a/plugin.video.alfa/lib/quasar/daemon.py b/plugin.video.alfa/lib/quasar/daemon.py new file mode 100644 index 00000000..3754d8d4 --- /dev/null +++ b/plugin.video.alfa/lib/quasar/daemon.py @@ -0,0 +1,294 @@ +import os +import stat +import time +import xbmc +import shutil +import socket +import urllib2 +import xbmcgui +import threading +import subprocess +from quasar.logger import log +from quasar.osarch import PLATFORM +from quasar.config import QUASARD_HOST +from quasar.addon import ADDON, ADDON_ID, ADDON_PATH +from quasar.util import notify, system_information, getLocalizedString, getWindowsShortPath + +def ensure_exec_perms(file_): + st = os.stat(file_) + os.chmod(file_, st.st_mode | stat.S_IEXEC) + return file_ + +def android_get_current_appid(): + with open("/proc/%d/cmdline" % os.getpid()) as fp: + return fp.read().rstrip("\0") + +def get_quasard_checksum(path): + try: + with open(path) as fp: + fp.seek(-40, os.SEEK_END) # we put a sha1 there + return fp.read() + except Exception: + return "" + +def get_quasar_binary(): + binary = "quasar" + (PLATFORM["os"] == "windows" and ".exe" or "") + + log.info("PLATFORM: %s" % str(PLATFORM)) + binary_dir = os.path.join(ADDON_PATH, "resources", "bin", "%(os)s_%(arch)s" % PLATFORM) + if PLATFORM["os"] == "android": + log.info("Detected binary folder: %s" % binary_dir) + binary_dir_legacy = binary_dir.replace("/storage/emulated/0", "/storage/emulated/legacy") + if os.path.exists(binary_dir_legacy): + binary_dir = binary_dir_legacy + log.info("Using binary folder: %s" % binary_dir) + app_id = android_get_current_appid() + xbmc_data_path = os.path.join("/data", "data", app_id) + + try: #Test if there is any permisions problem + f = open(os.path.join(xbmc_data_path, "test.txt"), "wb") + f.write("test") + f.close() + os.remove(os.path.join(xbmc_data_path, "test.txt")) + except: + xbmc_data_path = '' + + if not os.path.exists(xbmc_data_path): + log.info("%s path does not exist, so using %s as xbmc_data_path" % (xbmc_data_path, xbmc.translatePath("special://xbmcbin/"))) + xbmc_data_path = xbmc.translatePath("special://xbmcbin/") + + try: #Test if there is any permisions problem + f = open(os.path.join(xbmc_data_path, "test.txt"), "wb") + f.write("test") + f.close() + os.remove(os.path.join(xbmc_data_path, "test.txt")) + except: + xbmc_data_path = '' + + if not os.path.exists(xbmc_data_path): + log.info("%s path does not exist, so using %s as xbmc_data_path" % (xbmc_data_path, xbmc.translatePath("special://masterprofile/"))) + xbmc_data_path = xbmc.translatePath("special://masterprofile/") + dest_binary_dir = os.path.join(xbmc_data_path, "files", ADDON_ID, "bin", "%(os)s_%(arch)s" % PLATFORM) + else: + dest_binary_dir = os.path.join(xbmc.translatePath(ADDON.getAddonInfo("profile")).decode('utf-8'), "bin", "%(os)s_%(arch)s" % PLATFORM) + + log.info("Using destination binary folder: %s" % dest_binary_dir) + binary_path = os.path.join(binary_dir, binary) + dest_binary_path = os.path.join(dest_binary_dir, binary) + + if not os.path.exists(binary_path): + notify((getLocalizedString(30103) + " %(os)s_%(arch)s" % PLATFORM), time=7000) + system_information() + try: + log.info("Source directory (%s):\n%s" % (binary_dir, os.listdir(os.path.join(binary_dir, "..")))) + log.info("Destination directory (%s):\n%s" % (dest_binary_dir, os.listdir(os.path.join(dest_binary_dir, "..")))) + except Exception: + pass + return False, False + + if os.path.isdir(dest_binary_path): + log.warning("Destination path is a directory, expected previous binary file, removing...") + try: + shutil.rmtree(dest_binary_path) + except Exception as e: + log.error("Unable to remove destination path for update: %s" % e) + system_information() + return False, False + + if not os.path.exists(dest_binary_path) or get_quasard_checksum(dest_binary_path) != get_quasard_checksum(binary_path): + log.info("Updating quasar daemon...") + try: + os.makedirs(dest_binary_dir) + except OSError: + pass + try: + shutil.rmtree(dest_binary_dir) + except Exception as e: + log.error("Unable to remove destination path for update: %s" % e) + system_information() + pass + try: + shutil.copytree(binary_dir, dest_binary_dir) + except Exception as e: + log.error("Unable to copy to destination path for update: %s" % e) + system_information() + return False, False + + # Clean stale files in the directory, as this can cause headaches on + # Android when they are unreachable + dest_files = set(os.listdir(dest_binary_dir)) + orig_files = set(os.listdir(binary_dir)) + log.info("Deleting stale files %s" % (dest_files - orig_files)) + for file_ in (dest_files - orig_files): + path = os.path.join(dest_binary_dir, file_) + if os.path.isdir(path): + shutil.rmtree(path) + else: + os.remove(path) + + return dest_binary_dir, ensure_exec_perms(dest_binary_path) + +def clear_fd_inherit_flags(): + # Ensure the spawned quasar binary doesn't inherit open files from Kodi + # which can break things like addon updates. [WINDOWS ONLY] + from ctypes import windll + + HANDLE_RANGE = xrange(0, 65536) + HANDLE_FLAG_INHERIT = 1 + FILE_TYPE_DISK = 1 + + for hd in HANDLE_RANGE: + if windll.kernel32.GetFileType(hd) == FILE_TYPE_DISK: + if not windll.kernel32.SetHandleInformation(hd, HANDLE_FLAG_INHERIT, 0): + log.error("Error clearing inherit flag, disk file handle %x" % hd) + + +def jsonrpc_enabled(notify=False): + try: + s = socket.socket() + s.connect(('127.0.0.1', 9090)) + s.close() + log.info("Kodi's JSON-RPC service is available, starting up...") + del s + return True + except Exception as e: + log.error(repr(e)) + if notify: + xbmc.executebuiltin("ActivateWindow(ServiceSettings)") + dialog = xbmcgui.Dialog() + dialog.ok("Quasar", getLocalizedString(30199)) + return False + +def start_quasard(**kwargs): + jsonrpc_failures = 0 + while jsonrpc_enabled() is False: + jsonrpc_failures += 1 + log.warning("Unable to connect to Kodi's JSON-RPC service, retrying...") + if jsonrpc_failures > 1: + time.sleep(5) + if not jsonrpc_enabled(notify=True): + log.error("Unable to reach Kodi's JSON-RPC service, aborting...") + return False + else: + break + time.sleep(3) + + quasar_dir, quasar_binary = get_quasar_binary() + + if quasar_dir is False or quasar_binary is False: + return False + + lockfile = os.path.join(ADDON_PATH, ".lockfile") + if os.path.exists(lockfile): + log.warning("Existing process found from lockfile, killing...") + try: + with open(lockfile) as lf: + pid = int(lf.read().rstrip(" \t\r\n\0")) + os.kill(pid, 9) + except Exception as e: + log.error(repr(e)) + + if PLATFORM["os"] == "windows": + log.warning("Removing library.db.lock file...") + try: + library_lockfile = os.path.join(xbmc.translatePath(ADDON.getAddonInfo("profile")).decode('utf-8'), "library.db.lock") + os.remove(library_lockfile) + except Exception as e: + log.error(repr(e)) + + SW_HIDE = 0 + STARTF_USESHOWWINDOW = 1 + + args = [quasar_binary] + kwargs["cwd"] = quasar_dir + + if PLATFORM["os"] == "windows": + args[0] = getWindowsShortPath(quasar_binary) + kwargs["cwd"] = getWindowsShortPath(quasar_dir) + si = subprocess.STARTUPINFO() + si.dwFlags = STARTF_USESHOWWINDOW + si.wShowWindow = SW_HIDE + clear_fd_inherit_flags() + kwargs["startupinfo"] = si + else: + env = os.environ.copy() + env["LD_LIBRARY_PATH"] = "%s:%s" % (quasar_dir, env.get("LD_LIBRARY_PATH", "")) + kwargs["env"] = env + kwargs["close_fds"] = True + + wait_counter = 1 + while xbmc.getCondVisibility('Window.IsVisible(10140)') or xbmc.getCondVisibility('Window.IsActive(10140)'): + if wait_counter == 1: + log.info('Add-on settings currently opened, waiting before starting...') + if wait_counter > 300: + break + time.sleep(1) + wait_counter += 1 + + return subprocess.Popen(args, **kwargs) + +def shutdown(): + try: + urllib2.urlopen(QUASARD_HOST + "/shutdown") + except: + pass + +def wait_for_abortRequested(proc, monitor): + monitor.closing.wait() + log.info("quasard: exiting quasard daemon") + try: + proc.terminate() + except OSError: + pass # Process already exited, nothing to terminate + log.info("quasard: quasard daemon exited") + +def quasard_thread(monitor): + crash_count = 0 + try: + while not xbmc.abortRequested: + log.info("quasard: starting quasard") + proc = start_quasard(stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + if not proc: + break + threading.Thread(target=wait_for_abortRequested, args=[proc, monitor]).start() + + if PLATFORM["os"] == "windows": + while proc.poll() is None: + log.info(proc.stdout.readline()) + else: + # Kodi hangs on some Android (sigh...) systems when doing a blocking + # read. We count on the fact that Quasar daemon flushes its log + # output on \n, creating a pretty clean output + import fcntl + import select + fd = proc.stdout.fileno() + fl = fcntl.fcntl(fd, fcntl.F_GETFL) + fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) + while proc.poll() is None: + try: + to_read, _, _ = select.select([proc.stdout], [], []) + for ro in to_read: + line = ro.readline() + if line == "": # write end is closed + break + log.info(line) + except IOError: + time.sleep(1) # nothing to read, sleep + + if proc.returncode == 0 or xbmc.abortRequested: + break + + crash_count += 1 + notify(getLocalizedString(30100), time=3000) + xbmc.executebuiltin("Dialog.Close(all, true)") + system_information() + time.sleep(5) + if crash_count >= 3: + notify(getLocalizedString(30110), time=3000) + break + + except Exception as e: + import traceback + map(log.error, traceback.format_exc().split("\n")) + notify("%s: %s" % (getLocalizedString(30226), repr(e).encode('utf-8'))) + raise diff --git a/plugin.video.alfa/lib/quasar/navigation.py b/plugin.video.alfa/lib/quasar/navigation.py new file mode 100644 index 00000000..70658b6c --- /dev/null +++ b/plugin.video.alfa/lib/quasar/navigation.py @@ -0,0 +1,260 @@ +import os +import sys +import socket +import urllib2 +import urlparse +import xbmc +import xbmcgui +import xbmcplugin +from quasar.logger import log +from quasar.config import QUASARD_HOST +from quasar.addon import ADDON, ADDON_ID, ADDON_PATH +from quasar.util import notify, getLocalizedString, getLocalizedLabel, system_information + +try: + import simplejson as json +except ImportError: + import json + + +HANDLE = int(sys.argv[1]) + + +class InfoLabels(dict): + def __init__(self, *args, **kwargs): + self.update(*args, **kwargs) + + def __getitem__(self, key): + return dict.get(self, key.lower(), "") + + def __setitem__(self, key, val): + dict.__setitem__(self, key.lower(), val) + + def update(self, *args, **kwargs): + for k, v in dict(*args, **kwargs).iteritems(): + self[k] = v + + +class closing(object): + def __init__(self, thing): + self.thing = thing + + def __enter__(self): + return self.thing + + def __exit__(self, *exc_info): + self.thing.close() + + +class NoRedirectHandler(urllib2.HTTPRedirectHandler): + def http_error_302(self, req, fp, code, msg, headers): + import urllib + infourl = urllib.addinfourl(fp, headers, headers["Location"]) + infourl.status = code + infourl.code = code + return infourl + http_error_300 = http_error_302 + http_error_301 = http_error_302 + http_error_303 = http_error_302 + http_error_307 = http_error_302 + + +def getInfoLabels(): + id_list = [int(s) for s in sys.argv[0].split("/") if s.isdigit()] + tmdb_id = id_list[0] if id_list else None + + if not tmdb_id: + parsed_url = urlparse.urlparse(sys.argv[0] + sys.argv[2]) + query = urlparse.parse_qs(parsed_url.query) + log.debug("Parsed URL: %s, Query: %s", repr(parsed_url), repr(query)) + if 'tmdb' in query and 'show' not in query: + tmdb_id = query['tmdb'][0] + url = "%s/movie/%s/infolabels" % (QUASARD_HOST, tmdb_id) + elif 'show' in query: + tmdb_id = query['show'][0] + if 'season' in query and 'episode' in query: + url = "%s/show/%s/season/%s/episode/%s/infolabels" % (QUASARD_HOST, tmdb_id, query['season'][0], query['episode'][0]) + else: + url = "%s/show/%s/infolabels" % (QUASARD_HOST, tmdb_id) + else: + url = "%s/infolabels" % (QUASARD_HOST) + elif 'movie' in sys.argv[0]: + url = "%s/movie/%s/infolabels" % (QUASARD_HOST, tmdb_id) + elif ('episode' in sys.argv[0] or 'show' in sys.argv[0]) and len(id_list) > 2: + url = "%s/show/%s/season/%s/episode/%s/infolabels" % (QUASARD_HOST, tmdb_id, id_list[1], id_list[2]) + elif 'show' in sys.argv[0] and len(id_list) == 2: + url = "%s/show/%s/season/%s/episode/%s/infolabels" % (QUASARD_HOST, tmdb_id, id_list[1], 1) + else: + url = "%s/infolabels" % (QUASARD_HOST) + + log.debug("Resolving TMDB item by calling %s for %s" % (url, repr(sys.argv))) + + try: + with closing(urllib2.urlopen(url)) as response: + resolved = json.loads(response.read()) + if not resolved: + return {} + + if 'info' in resolved and resolved['info']: + resolved.update(resolved['info']) + + if 'art' in resolved and resolved['art']: + resolved['artbanner'] = '' + for k, v in resolved['art'].items(): + resolved['art' + k] = v + + if 'info' in resolved: + del resolved['info'] + if 'art' in resolved: + del resolved['art'] + if 'stream_info' in resolved: + del resolved['stream_info'] + + if 'dbtype' not in resolved: + resolved['dbtype'] = 'video' + if 'mediatype' not in resolved or resolved['mediatype'] == '': + resolved['Mediatype'] = resolved['dbtype'] + + return resolved + except: + log.debug("Could not resolve TMDB item: %s" % tmdb_id) + return {} + + +def _json(url): + with closing(urllib2.urlopen(url)) as response: + if response.code >= 300 and response.code <= 307: + # Pause currently playing Quasar file to avoid doubling requests + if xbmc.Player().isPlaying() and ADDON_ID in xbmc.Player().getPlayingFile(): + xbmc.Player().pause() + _infoLabels = InfoLabels(getInfoLabels()) + + item = xbmcgui.ListItem( + path=response.geturl(), + label=_infoLabels["label"], + label2=_infoLabels["label2"], + thumbnailImage=_infoLabels["thumbnail"]) + + item.setArt({ + "poster": _infoLabels["artposter"], + "banner": _infoLabels["artbanner"], + "fanart": _infoLabels["artfanart"] + }) + + item.setInfo(type='Video', infoLabels=_infoLabels) + xbmcplugin.setResolvedUrl(HANDLE, True, item) + return + + payload = response.read() + + try: + if payload: + return json.loads(payload) + except: + raise Exception(payload) + + +def run(url_suffix=""): + if not os.path.exists(os.path.join(ADDON_PATH, ".firstrun")): + notify(getLocalizedString(30101)) + system_information() + return + + donatePath = os.path.join(ADDON_PATH, ".donate") + if not os.path.exists(donatePath): + with open(donatePath, "w"): + os.utime(donatePath, None) + dialog = xbmcgui.Dialog() + dialog.ok("Quasar", getLocalizedString(30141)) + + socket.setdefaulttimeout(int(ADDON.getSetting("buffer_timeout"))) + urllib2.install_opener(urllib2.build_opener(NoRedirectHandler())) + + # Pause currently playing Quasar file to avoid doubling requests + if xbmc.Player().isPlaying() and ADDON_ID in xbmc.Player().getPlayingFile(): + xbmc.Player().pause() + + url = sys.argv[0].replace("plugin://%s" % ADDON_ID, QUASARD_HOST + url_suffix) + sys.argv[2] + log.debug("Requesting %s from %s" % (url, repr(sys.argv))) + + try: + data = _json(url) + except urllib2.URLError as e: + if 'Connection refused' in e.reason: + notify(getLocalizedString(30116), time=7000) + else: + import traceback + map(log.error, traceback.format_exc().split("\n")) + notify(e.reason, time=7000) + return + except Exception as e: + import traceback + map(log.error, traceback.format_exc().split("\n")) + try: + msg = unicode(e) + except: + try: + msg = str(e) + except: + msg = repr(e) + notify(getLocalizedLabel(msg), time=7000) + return + + if not data: + return + + if data["content_type"]: + content_type = data["content_type"] + if data["content_type"].startswith("menus"): + content_type = data["content_type"].split("_")[1] + + xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_UNSORTED) + if content_type != "tvshows": + xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE) + xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_DATE) + xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_GENRE) + xbmcplugin.setContent(HANDLE, content_type) + + listitems = range(len(data["items"])) + for i, item in enumerate(data["items"]): + # Translate labels + if item["label"][0:8] == "LOCALIZE": + item["label"] = unicode(getLocalizedLabel(item["label"]), 'utf-8') + if item["label2"][0:8] == "LOCALIZE": + item["label2"] = getLocalizedLabel(item["label2"]) + + listItem = xbmcgui.ListItem(label=item["label"], label2=item["label2"], iconImage=item["icon"], thumbnailImage=item["thumbnail"]) + if item.get("info"): + listItem.setInfo("video", item["info"]) + if item.get("stream_info"): + for type_, values in item["stream_info"].items(): + listItem.addStreamInfo(type_, values) + if item.get("art"): + listItem.setArt(item["art"]) + elif ADDON.getSetting('default_fanart') == 'true' and item["label"] != unicode(getLocalizedString(30218), 'utf-8'): + fanart = os.path.join(ADDON_PATH, "fanart.jpg") + listItem.setArt({'fanart': fanart}) + if item.get("context_menu"): + # Translate context menus + for m, menu in enumerate(item["context_menu"]): + if menu[0][0:8] == "LOCALIZE": + menu[0] = getLocalizedLabel(menu[0]) + listItem.addContextMenuItems(item["context_menu"]) + listItem.setProperty("isPlayable", item["is_playable"] and "true" or "false") + if item.get("properties"): + for k, v in item["properties"].items(): + listItem.setProperty(k, v) + listitems[i] = (item["path"], listItem, not item["is_playable"]) + + xbmcplugin.addDirectoryItems(HANDLE, listitems, totalItems=len(listitems)) + + # Set ViewMode + if data["content_type"]: + viewMode = ADDON.getSetting("viewmode_%s" % data["content_type"]) + if viewMode: + try: + xbmc.executebuiltin('Container.SetViewMode(%s)' % viewMode) + except Exception as e: + log.warning("Unable to SetViewMode(%s): %s" % (viewMode, repr(e))) + + xbmcplugin.endOfDirectory(HANDLE, succeeded=True, updateListing=False, cacheToDisc=True) diff --git a/plugin.video.alfa/lib/quasar/osarch.py b/plugin.video.alfa/lib/quasar/osarch.py new file mode 100644 index 00000000..5b580c28 --- /dev/null +++ b/plugin.video.alfa/lib/quasar/osarch.py @@ -0,0 +1,56 @@ +import xbmc +import sys +import platform + +def get_platform(): + build = xbmc.getInfoLabel("System.BuildVersion") + kodi_version = int(build.split()[0][:2]) + ret = { + "auto_arch": sys.maxsize > 2 ** 32 and "64-bit" or "32-bit", + "arch": sys.maxsize > 2 ** 32 and "x64" or "x86", + "os": "", + "version": platform.release(), + "kodi": kodi_version, + "build": build + } + if xbmc.getCondVisibility("system.platform.android"): + ret["os"] = "android" + if "arm" in platform.machine() or "aarch" in platform.machine(): + ret["arch"] = "arm" + if "64" in platform.machine() and ret["auto_arch"] == "64-bit": + ret["arch"] = "arm" + #ret["arch"] = "x64" #The binary is corrupted in install package + elif xbmc.getCondVisibility("system.platform.linux"): + ret["os"] = "linux" + if "aarch" in platform.machine() or "arm64" in platform.machine(): + if xbmc.getCondVisibility("system.platform.linux.raspberrypi"): + ret["arch"] = "armv7" + elif ret["auto_arch"] == "32-bit": + ret["arch"] = "armv7" + elif ret["auto_arch"] == "64-bit": + ret["arch"] = "arm64" + elif platform.architecture()[0].startswith("32"): + ret["arch"] = "arm" + else: + ret["arch"] = "arm64" + elif "armv7" in platform.machine(): + ret["arch"] = "armv7" + elif "arm" in platform.machine(): + ret["arch"] = "arm" + elif xbmc.getCondVisibility("system.platform.xbox"): + ret["os"] = "windows" + ret["arch"] = "x64" + elif xbmc.getCondVisibility("system.platform.windows"): + ret["os"] = "windows" + if platform.machine().endswith('64'): + ret["arch"] = "x64" + elif xbmc.getCondVisibility("system.platform.osx"): + ret["os"] = "darwin" + ret["arch"] = "x64" + elif xbmc.getCondVisibility("system.platform.ios"): + ret["os"] = "ios" + ret["arch"] = "arm" + return ret + + +PLATFORM = get_platform() diff --git a/plugin.video.alfa/lib/quasar/util.py b/plugin.video.alfa/lib/quasar/util.py new file mode 100644 index 00000000..c3c3b63b --- /dev/null +++ b/plugin.video.alfa/lib/quasar/util.py @@ -0,0 +1,72 @@ +import platform +import xbmc +import xbmcgui +from quasar.logger import log +from quasar.osarch import PLATFORM +from quasar.addon import ADDON, ADDON_NAME, ADDON_ICON + + +def notify(message, header=ADDON_NAME, time=5000, image=ADDON_ICON): + sound = ADDON.getSetting('do_not_disturb') == 'false' + dialog = xbmcgui.Dialog() + return dialog.notification(toUtf8(header), toUtf8(message), toUtf8(image), time, sound) + +def getLocalizedLabel(label): + try: + if "LOCALIZE" not in label: + return label + if ";;" not in label and label.endswith(']'): + return getLocalizedString(int(label[9:-1])) + else: + parts = label.split(";;") + translation = getLocalizedString(int(parts[0][9:14])) + for i, part in enumerate(parts[1:]): + if part[0:8] == "LOCALIZE": + parts[i + 1] = getLocalizedString(int(part[9:14])) + + return (translation.decode('utf-8', 'replace') % tuple(parts[1:])).encode('utf-8', 'ignore') + except: + return label + +def getLocalizedString(stringId): + try: + return ADDON.getLocalizedString(stringId).encode('utf-8', 'ignore') + except: + return stringId + +def toUtf8(string): + if isinstance(string, unicode): + return string.encode('utf-8', 'ignore') + return string + +def system_information(): + build = xbmc.getInfoLabel("System.BuildVersion") + log.info("System information: %(os)s_%(arch)s %(version)s" % PLATFORM) + log.info("Kodi build version: %s" % build) + log.info("OS type: %s" % platform.system()) + log.info("uname: %s" % repr(platform.uname())) + return PLATFORM + +def getShortPath(path): + if PLATFORM["os"] == "windows": + return getWindowsShortPath(path) + return path + +def getWindowsShortPath(path): + try: + import ctypes + import ctypes.wintypes + + ctypes.windll.kernel32.GetShortPathNameW.argtypes = [ + ctypes.wintypes.LPCWSTR, # lpszLongPath + ctypes.wintypes.LPWSTR, # lpszShortPath + ctypes.wintypes.DWORD # cchBuffer + ] + ctypes.windll.kernel32.GetShortPathNameW.restype = ctypes.wintypes.DWORD + + buf = ctypes.create_unicode_buffer(1024) # adjust buffer size, if necessary + ctypes.windll.kernel32.GetShortPathNameW(path, buf, len(buf)) + + return buf.value + except: + return path diff --git a/plugin.video.alfa/platformcode/custom_code.py b/plugin.video.alfa/platformcode/custom_code.py index af06d125..87dcbc97 100644 --- a/plugin.video.alfa/platformcode/custom_code.py +++ b/plugin.video.alfa/platformcode/custom_code.py @@ -5,8 +5,11 @@ import os import json +import traceback +import xbmc +import xbmcaddon -from platformcode import config, logger +from platformcode import config, logger, platformtools from core import jsontools from core import filetools @@ -54,6 +57,15 @@ def init(): """ try: + #QUASAR: Preguntamos si se hacen modificaciones a Quasar + if not filetools.exists(os.path.join(config.get_data_path(), "quasar.json")) and not config.get_setting('addon_quasar_update', default=False): + question_update_external_addon("quasar") + + #QUASAR: Hacemos las modificaciones a Quasar, si está permitido, y si está instalado + if config.get_setting('addon_quasar_update', default=False): + if not update_external_addon("quasar"): + platformtools.dialog_notification("Actualización Quasar", "Ha fallado. Consulte el log") + #Existe carpeta "custom_code" ? Si no existe se crea y se sale custom_code_dir = os.path.join(config.get_data_path(), 'custom_code') if os.path.exists(custom_code_dir) == False: @@ -70,7 +82,7 @@ def init(): #Se verifica si la versión del .json y del add-on son iguales. Si es así se sale. Si no se copia "custom_code" al add-on verify_copy_folders(custom_code_dir, custom_code_json_path) except: - pass + logger.error(traceback.format_exc()) def create_folder_structure(custom_code_dir): @@ -88,11 +100,11 @@ def create_folder_structure(custom_code_dir): return -def create_json(custom_code_json_path): +def create_json(custom_code_json_path, json_name=json_data_file_name): logger.info() #Guardamaos el json con la versión de Alfa vacía, para permitir hacer la primera copia - json_data_file = filetools.join(custom_code_json_path, json_data_file_name) + json_data_file = filetools.join(custom_code_json_path, json_name) json_file = open(json_data_file, "a+") json_file.write(json.dumps({"addon_version": ""})) json_file.close() @@ -123,3 +135,55 @@ def verify_copy_folders(custom_code_dir, custom_code_json_path): filetools.write(json_data_file, jsontools.dump(json_data)) return + + +def question_update_external_addon(addon_name): + logger.info(addon_name) + + #Verificamos que el addon está instalado + stat = False + if xbmc.getCondVisibility('System.HasAddon("plugin.video.%s")' % addon_name): + #Si es la primera vez que se pregunta por la actualización del addon externo, recogemos la respuesta, + # guardaos un .json en userdat/alfa para no volver a preguntar otra vez, y se actualiza el setting en Alfa. + stat = platformtools.dialog_yesno('Actualización de %s' % addon_name.capitalize(), '¿Quiere que actualicemos Quasar para que sea compatible con las últimas versiones de Kodi? (recomendado: SÍ)', '', 'Si actualiza Quasar, reinicie Kodi en un par de minutos') + + #Con la respuesta actualizamos la variable en Alfa settings.xml. Se puede cambiar en Ajustes de Alfa, Otros + if stat: + config.set_setting('addon_quasar_update', True) + else: + config.set_setting('addon_quasar_update', False) + + #Creamos un .json en userdata para no volver a preguntar otra vez + create_json(config.get_data_path(), "%s.json" % addon_name) + + return stat + +def update_external_addon(addon_name): + logger.info(addon_name) + + #Verificamos que el addon está instalado + if xbmc.getCondVisibility('System.HasAddon("plugin.video.%s")' % addon_name): + #Path de actuali - + + + + +