def Reset(self): for i in self._objects: try: self._objects[i].hide() except: pass log.info("Resetting RPC objects...") self._objects = {}
def wait_for_abortRequested(proc, monitor): monitor.closing.wait() log.info("da_inc: exiting da_inc daemon") try: if proc is not None: proc.terminate() except OSError: pass # Process already exited, nothing to terminate log.info("da_inc: da_inc daemon exited")
def server_thread(): try: bjsonrpc.bjsonrpc_options['threaded'] = True s = bjsonrpc.createserver(host="0.0.0.0", port=JSONRPC_EXT_PORT, handler_factory=da_incRPCServer) log.info("da_inc: starting jsonrpc service") s.serve() log.info("da_inc: exiting jsonrpc service") except Exception: import traceback map(log.error, traceback.format_exc().split("\n")) raise
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 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("da_inc", getLocalizedString(30199)) return False
def GetCurrentView(self): skinPath = xbmc.translatePath('special://skin/') xml = os.path.join(skinPath, 'addon.xml') f = xbmcvfs.File(xml) read = f.read() f.close() try: src = re.search('defaultresolution="([^"]+)', read, re.DOTALL).group(1) except: src = re.search('<res.+?folder="([^"]+)', read, re.DOTALL).group(1) src = os.path.join(skinPath, src, 'MyVideoNav.xml') f = xbmcvfs.File(src) read = f.read() f.close() match = re.search('<views>([^<]+)', read, re.DOTALL) if match: views = match.group(1) log.info("Skin's ViewModes: %s" % views) for view in views.split(','): if xbmc.getInfoLabel('Control.GetLabel(%s)' % view): return view
def dump_version(ret): try: p = platform.platform() except: p = "Could not detect" try: log.info("""Python version: %s dist: %s linux_distribution: %s system: %s machine: %s platform: %s uname: %s version: %s mac_ver: %s """ % (sys.version.split('\n'), str(platform.dist()), linux_distribution(), platform.system(), platform.machine(), p, platform.uname(), platform.version(), platform.mac_ver())) except: if ret is not None: log.info("Cannot write detection info. Ret: %s" % (repr(ret))) pass
def run(): # Make sure the XBMC jsonrpc server is started. xbmc.startServer(xbmc.SERVER_JSONRPCSERVER, True) # Make the monitor monitor = da_incMonitor() threads = [ threading.Thread(target=server_thread), # JSONRPC thread ] if not ONLY_CLIENT and PLATFORM["fork"]: threads.append(threading.Thread(target=da_inc_thread, args=[monitor])) # da_inc thread for t in threads: t.daemon = True t.start() # XBMC loop while not xbmc.abortRequested: xbmc.sleep(1000) log.info("da_inc: exiting da_inc")
def DialogProgressBG_Cleanup(self): log.info("Cleaning up dialogs") for hwnd in self._objects: if isinstance(self._objects[hwnd], xbmcgui.DialogProgressBG): self._objects[hwnd].close()
def get_platform(): try: binary_platform = ADDON.getSetting("binary_platform") except: binary_platform = "auto" pass build = xbmc.getInfoLabel("System.BuildVersion") kodi_version = int(build.split()[0][:2]) if binary_platform and "auto" not in binary_platform.lower(): custom = binary_platform.split('_') if len(custom) > 1: return { "os": custom[0], "arch": custom[1], "fork": True, "version": "", "kodi": kodi_version, "build": build } ret = { "auto_arch": sys.maxsize > 2**32 and "64-bit" or "32-bit", "arch": sys.maxsize > 2**32 and "x64" or "x86", "os": "", "version": "", "kodi": kodi_version, "build": build, "fork": True, "machine": "", "system": "", "platform": "" } try: ret["os"] = platform.release() except: pass try: ret["machine"] = platform.machine() except: # Default 'machine' for Android can be 'arm' if xbmc.getCondVisibility("system.platform.android"): ret["machine"] = "arm" pass try: ret["system"] = platform.system() except: pass try: ret["platform"] = platform.platform() except: pass if xbmc.getCondVisibility("system.platform.android"): ret["os"] = "android" if "arm" in ret["machine"].lower() or "aarch" in ret["machine"].lower( ): ret["arch"] = "arm" if "64" in ret["machine"] and ret["auto_arch"] == "64-bit": ret["arch"] = "arm64" elif xbmc.getCondVisibility("system.platform.linux"): ret["os"] = "linux" if "aarch" in ret["machine"].lower( ) or "arm64" in ret["machine"].lower(): 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"] = "armv6" else: ret["arch"] = "armv7" elif "armv7" in ret["machine"]: ret["arch"] = "armv7" elif "arm" in ret["machine"]: cpuarch = "" if "aarch" in ret["machine"].lower( ) or "arm" in ret["machine"].lower(): info = cpuinfo() for proc in info.keys(): log.info("CPU: %s=%s" % (proc, info[proc])) model = "" if "model name" in info[proc]: model = info[proc]["model name"].lower() elif "Processor" in info[proc]: model = info[proc]["Processor"].lower() if model: log.info("Exploring model: %s" % model) if "aarch" in model or "arm64" in model or "v8l" in model: cpuarch = "arm64" elif "armv7" in model or "v7l" in model: cpuarch = "armv7" break if cpuarch: log.info("Using CPU info arch: %s" % cpuarch) ret["arch"] = cpuarch else: ret["arch"] = "armv6" elif xbmc.getCondVisibility("system.platform.xbox"): ret["os"] = "windows" ret["arch"] = "x64" ret["fork"] = False elif xbmc.getCondVisibility("system.platform.windows"): ret["os"] = "windows" if ret["machine"].endswith('64'): ret["arch"] = "x64" elif ret["system"] == "Darwin": ret["os"] = "darwin" ret["arch"] = "x64" if "AppleTV" in ret["platform"]: ret["os"] = "ios" ret["arch"] = "armv7" ret["fork"] = False if "64bit" in ret["platform"]: ret["arch"] = "arm64" elif xbmc.getCondVisibility("system.platform.ios"): ret["os"] = "ios" ret["arch"] = "armv7" ret["fork"] = False if "64bit" in ret["platform"]: ret["arch"] = "arm64" # elif xbmc.getCondVisibility("system.platform.osx"): # ret["os"] = "darwin" # ret["arch"] = "x64" # elif xbmc.getCondVisibility("system.platform.ios"): # ret["os"] = "ios" # ret["arch"] = "armv7" return ret
def get_da_inc_binary(): global binary_platform binary_platform = get_platform() binary = "da_inc" + (binary_platform["os"] == "windows" and ".exe" or "") binary_dir = os.path.join(ADDON_PATH, "resources", "bin", "%(os)s_%(arch)s" % binary_platform) if binary_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 changed binary folder for Android: %s" % binary_dir) app_id = android_get_current_appid() xbmc_data_path = os.path.join("/data", "data", app_id) 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/") 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" % binary_platform) else: dest_binary_dir = os.path.join( xbmc.translatePath(ADDON.getAddonInfo("profile")), "bin", "%(os)s_%(arch)s" % binary_platform) binary_path = os.path.join(binary_dir, binary) dest_binary_path = os.path.join(dest_binary_dir, binary) log.info("Binary detection. Source: %s, Destination: %s" % (binary_path, dest_binary_path)) if not os.path.exists(binary_path): # notify((getLocalizedString(30103) + " %(os)s_%(arch)s" % PLATFORM), time=7000) dialog_ok("LOCALIZE[30347];;" + "%(os)s_%(arch)s" % binary_platform) 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 not os.path.exists( binary_path) or get_da_inc_checksum( dest_binary_path) != get_da_inc_checksum( binary_path) or not filecmp.cmp( dest_binary_path, binary_path, shallow=True): log.info("Updating da_inc 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) log.info("Binary detection: [ Source: %s, Destination: %s ]" % (binary_path, dest_binary_path)) return dest_binary_dir, ensure_exec_perms(dest_binary_path)
def da_inc_thread(monitor): crash_count = 0 try: while not xbmc.abortRequested: log.info("da_inc: starting da_inc") proc = None if hasSubprocess: proc = start_da_inc(stdout=subprocess.PIPE, stderr=subprocess.STDOUT) if not proc: break else: log.info("da_inc: current system is unable to run the binary") break threading.Thread(target=wait_for_abortRequested, args=[proc, monitor]).start() if not hasSubprocess: break if binary_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 da_inc 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 if proc.returncode == 5: notify(getLocalizedString(30332), time=3000) else: 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
def start_da_inc(**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) da_inc_dir, da_inc_binary = get_da_inc_binary() log.info("Binary dir: %s, item: %s " % (da_inc_dir, da_inc_binary)) if da_inc_dir is False or da_inc_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 OSError as e: if e.errno != 3: # Ignore: OSError: [Errno 3] No such process log.error(repr(e)) except Exception as e: log.error(repr(e)) if binary_platform["os"] == "windows": try: library_lockfile = os.path.join( xbmc.translatePath( ADDON.getAddonInfo("profile")).decode('utf-8'), "library.db.lock") log.warning("Removing library.db.lock file at %s ..." % library_lockfile) os.remove(library_lockfile) except Exception as e: log.error(repr(e)) SW_HIDE = 0 STARTF_USESHOWWINDOW = 1 args = [da_inc_binary] kwargs["cwd"] = da_inc_dir if binary_platform["os"] == "windows": args[0] = getWindowsShortPath(da_inc_binary) kwargs["cwd"] = getWindowsShortPath(da_inc_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" % (da_inc_dir, env.get("LD_LIBRARY_PATH", "")) kwargs["env"] = env kwargs["close_fds"] = True wait_counter = 1 log.debug("Checking for visible") 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 log.info("da_inc: start args: %s, kw: %s" % (args, kwargs)) if hasSubprocess: return subprocess.Popen(args, **kwargs) return False