Merge pull request #548 from lopezvg/master

Kodi 18: Quasar: Sistema de actualización desde Alfa
This commit is contained in:
Alfa
2019-02-07 08:41:05 -05:00
committed by GitHub
8 changed files with 773 additions and 5 deletions

View File

@@ -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():

View File

@@ -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()

View File

@@ -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

View File

@@ -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)

View File

@@ -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()

View File

@@ -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

View File

@@ -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<aciones de Alfa
alfa_addon_updates = filetools.join(config.get_runtime_path(), filetools.join("lib", addon_name))
#Path de destino en addon externo
__settings__ = xbmcaddon.Addon(id="plugin.video." + addon_name)
if addon_name.lower() in ['quasar', 'elementum']:
addon_path = filetools.join(xbmc.translatePath(__settings__.getAddonInfo('Path')), filetools.join("resources", filetools.join("site-packages", addon_name)))
else:
addon_path = ''
#Hay modificaciones en Alfa? Las copiamos al addon
if filetools.exists(alfa_addon_updates) and filetools.exists(addon_path):
for root, folders, files in os.walk(alfa_addon_updates):
for file in files:
input_file = filetools.join(root, file)
output_file = input_file.replace(alfa_addon_updates, addon_path)
if filetools.copy(input_file, output_file, silent=True) == False:
logger.error('Error en la copia: Input: %s o Output: %s' % (input_file, output_file))
return False
return True
else:
logger.error('Alguna carpeta no existe: Alfa: %s o %s: %s' % (alfa_addon_updates, addon_name, addon_path))
return False

View File

@@ -124,9 +124,13 @@
<setting type="sep"/>
<setting label="Gestión de actualizaciones urgentes de módulos de Alfa (Quick Fixes):" type="lsep"/>
<setting id="addon_update_timer" type="labelenum" values="0|6|12|24" label="Intervalo entre actualizaciones automáticas (horas)" default="12"/>
<setting id="addon_update_message" type="bool" label="Quiere ver mensajes de las actualizaciones" default="false"/>
<setting id="addon_update_message" type="bool" label="¿Quiere ver mensajes de las actualizaciones?" default="false"/>
<setting label="Lista activa" type="text" id="lista_activa" default="alfavorites-default.json" visible="false"/>
<setting type="sep"/>
<setting label="Gestión de actualizaciones de otros addon relacionados con Alfa:" type="lsep"/>
<setting id="addon_quasar_update" type="bool" label="¿Quiere actualizar Quasar para evitar errores?" default="false"/>
</category>
</settings>