ELementum Come Client di Default
This commit is contained in:
@@ -1,295 +0,0 @@
|
||||
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
|
||||
|
||||
log.info("quasard: proc.return code: %s" % str(proc.returncode))
|
||||
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
|
||||
Reference in New Issue
Block a user