コード例 #1
0
def register(search, search_movie, search_episode):
    try:
        payload = json.loads(base64.b64decode(sys.argv[1]))
    except:
        notify(ADDON.getLocalizedString(30102).encode('utf-8'), time=1000)
        return

    results = ()
    method = {
        "search": search,
        "search_movie": search_movie,
        "search_episode": search_episode,
    }.get(payload["method"]) or (lambda *a, **kw: [])
    try:
        results = ()
        try:
            objects = method(payload["search_object"])
            if objects is not None:
                results = tuple(objects)
        except Exception as e:
            notify("%s: %s" % (ADDON.getLocalizedString(30224).encode('utf-8'), e))
            map(log.error, traceback.format_exc().split("\n"))
    finally:
        try:
            req = urllib2.Request(payload["callback_url"], data=json.dumps(results))
            with closing(urllib2.urlopen(req)) as response:
                log.info("%s" % repr(response))
        except Exception as e:
            notify("%s: %s" % (ADDON.getLocalizedString(30224).encode('utf-8'), e))
            map(log.error, traceback.format_exc().split("\n"))
コード例 #2
0
def run(url_suffix=""):
    if not os.path.exists(os.path.join(xbmc.translatePath(ADDON.getAddonInfo("path")), ".firstrun")):
        notify(ADDON.getLocalizedString(30101).encode('utf-8'))
        system_information()
        return

    socket.setdefaulttimeout(300)
    urllib2.install_opener(urllib2.build_opener(NoRedirectHandler()))

    url = sys.argv[0].replace("plugin://%s" % ADDON_ID, QUASARD_HOST + url_suffix) + sys.argv[2]
    log.info("Requesting %s" % url)

    try:
        data = _json(url)
    except Exception as e:
        map(log.error, traceback.format_exc().split("\n"))
        notify("%s: %s" % (ADDON.getLocalizedString(30225).encode('utf-8'), e))
        return

    if not data:
        return

    if data["content_type"]:
        xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_UNSORTED)
        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, data["content_type"])

    listitems = range(len(data["items"]))
    for i, item in enumerate(data["items"]):
        # Translate labels
        if item["label"][0:8] == "LOCALIZE":
            item["label"] = GetLocalizedString(item["label"])
        if item["label2"][0:8] == "LOCALIZE":
            item["label2"] = GetLocalizedString(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"])
        if item.get("context_menu"):
            # Translate context menus
            for m, menu in enumerate(item["context_menu"]):
                if menu[0][0:8] == "LOCALIZE":
                    menu[0] = GetLocalizedString(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))
    xbmcplugin.endOfDirectory(HANDLE, succeeded=True, updateListing=False, cacheToDisc=True)
コード例 #3
0
ファイル: rpc.py プロジェクト: ShlomiD83/plugin.video.quasar
 def Reset(self):
     for i in self._objects:
         try:
             self._objects[i].hide()
         except:
             pass
     log.info("Resetting RPC objects...")
     self._objects = {}
コード例 #4
0
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")
コード例 #5
0
ファイル: rpc.py プロジェクト: ShlomiD83/plugin.video.quasar
def server_thread():
    try:
        s = bjsonrpc.createserver(port=JSONRPC_EXT_PORT, handler_factory=QuasarRPCServer)
        log.info("quasar: starting jsonrpc service")
        s.serve()
        log.info("quasar: exiting jsonrpc service")
    except Exception:
        import traceback
        map(log.error, traceback.format_exc().split("\n"))
        raise
コード例 #6
0
ファイル: util.py プロジェクト: ditheo/plugin.video.quasar
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
コード例 #7
0
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
コード例 #8
0
ファイル: rpc.py プロジェクト: Casti9l/plugin.video.quasar
 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
コード例 #9
0
def run():
    # Make sure the XBMC jsonrpc server is started.
    xbmc.startServer(xbmc.SERVER_JSONRPCSERVER, True)

    # Make the monitor
    monitor = QuasarMonitor()

    threads = [
        threading.Thread(target=server_thread),  # JSONRPC thread
        threading.Thread(target=quasard_thread, args=[monitor]),  # Quasard thread
        threading.Thread(target=library_thread),  # Library thread
    ]
    for t in threads:
        t.daemon = True
        t.start()

    # XBMC loop
    while not xbmc.abortRequested:
        xbmc.sleep(1000)

    log.info("quasar: exiting quasard")
コード例 #10
0
def get_quasar_binary():
    binary = "quasar" + (PLATFORM["os"] == "windows" and ".exe" or "")

    platform = PLATFORM.copy()
    if platform["os"] == "darwin":  # 64 bits anyway on Darwin
        platform["arch"] = "x64"
    # elif platform["os"] == "windows":  # 32 bits anyway on Windows
    #     platform["arch"] = "x86"

    binary_dir = os.path.join(ADDON.getAddonInfo("path"), "resources", "bin", "%(os)s_%(arch)s" % platform)
    if platform["os"] == "android":
        binary_dir = binary_dir.replace("/storage/emulated/0", "/storage/emulated/legacy")
        app_id = android_get_current_appid()
        xbmc_data_path = os.path.join("/data", "data", app_id)
        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")), "bin", "%(os)s_%(arch)s" % platform)

    try:
        binary_dir = binary_dir.decode("latin1")
        dest_binary_dir = dest_binary_dir.decode("latin1")
    except UnicodeEncodeError:
        log.info("Unable to decode: binary_dir=%s dest_binary_dir=%s" % (repr(binary_dir), repr(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(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:
            pass
        shutil.copytree(binary_dir, dest_binary_dir)

    # 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)
コード例 #11
0
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
コード例 #12
0
ファイル: daemon.py プロジェクト: trisk/plugin.video.quasar
def quasard_thread(monitor):
    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
                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:
                        log.info(proc.stdout.readline())
                        continue
                    except IOError:
                        time.sleep(1)  # nothing to read, sleep

            if proc.returncode == 0 or xbmc.abortRequested:
                break

            notify(ADDON.getLocalizedString(30100).encode('utf-8'), time=3000)
            xbmc.executebuiltin("Dialog.Close(all, true)")
            system_information()
            time.sleep(3)

    except Exception as e:
        notify("%s: %s" % (ADDON.getLocalizedString(30226).encode('utf-8'), e))
        map(log.error, traceback.format_exc().split("\n"))
        raise
コード例 #13
0
ファイル: daemon.py プロジェクト: jswxjj/addon
def install_app(APP_PARAMS):
    import traceback
    import requests
    import xbmc
    import time

    try:
        user_params = {}
        apk_OK = False
        ANDROID_STORAGE = os.getenv('ANDROID_STORAGE')
        if not ANDROID_STORAGE: ANDROID_STORAGE = '/storage'
        LOCAL_DOWNLOAD_PATH = os.path.join(ANDROID_STORAGE, 'emulated', '0',
                                           'Download')
        USER_APP_PATH = os.path.join(ANDROID_STORAGE, 'emulated', '0',
                                     'Android', 'data')

        for user_addon, user_params in list(APP_PARAMS.items()):
            if not user_params['ACTIVE']: continue

            for apk_path in user_params['USER_APK']:
                if apk_path.endswith('.apk'):
                    download_path = LOCAL_DOWNLOAD_PATH
                elif user_params['USER_ADDON_STATUS']:
                    download_path = user_params['USER_ADDON_USERDATA']
                else:
                    continue

                if apk_path.startswith('http'):
                    try:
                        apk_body = requests.get(apk_path, timeout=10)
                    except Exception as e:
                        apk_body = requests.Response()
                        apk_body.status_code = str(e)
                    if apk_body.status_code != 200:
                        log.error(
                            "## Install_app: Invalid app requests response: %s"
                            % (apk_body.status_code))
                        apk_OK = False
                        continue
                    with open(os.path.join(download_path, \
                            os.path.basename(apk_path)), "wb") as f:
                        f.write(apk_body.content)
                    apk_OK = True

                else:
                    if os.path.exists(apk_path):
                        shutil.copy(apk_path, download_path)
                        apk_OK = True
                    else:
                        continue
                if not apk_OK:
                    break

            if apk_OK:
                log.info("## Install_app: Installing the APK from: %s" %
                         LOCAL_DOWNLOAD_PATH)
                notify('Install your Assistant %s from folder %s' % \
                            (os.path.basename(user_params['USER_APK'][0]), \
                            LOCAL_DOWNLOAD_PATH))
                cmd_android = 'StartAndroidActivity("%s", "", "%s", "%s")' % (
                    user_params['USER_APP'], 'open', 'about:blank')
                cmd_android_permissions = 'StartAndroidActivity("%s", "", "%s", "%s")' % (
                    user_params['USER_APP'], 'checkPermissions', 'about:blank')
                cmd_android_close = 'StartAndroidActivity("%s", "", "%s", "%s")' % (
                    user_params['USER_APP'], 'terminate', 'about:blank')
                xbmc.executebuiltin(cmd_android)
                time.sleep(1)

                # Lets give the user 5 minutes to install the app an retry automatically
                for x in range(300):
                    if os.path.exists(
                            os.path.join(USER_APP_PATH,
                                         user_params['USER_APP'])):
                        log.info("## Install_app: APP installed: %s" %
                                 user_params['USER_APP'])
                        notify('Accept Assistant permissions')
                        log.info("## Install_app: Requesting permissions: %s" %
                                 user_params['USER_APP'])
                        time.sleep(5)
                        xbmc.executebuiltin(cmd_android_permissions)
                        time.sleep(15)
                        log.info("## Install_app: closing APP: %s" %
                                 user_params['USER_APP'])
                        notify('Accept Assistant permissions')
                        xbmc.executebuiltin(cmd_android_close)
                        log.info("## Install_app: APP closed: %s" %
                                 user_params['USER_APP'])
                        time.sleep(10)
                        return user_params

                    xbmc.executebuiltin(cmd_android)
                    time.sleep(1)
                break

    except:
        log.info(traceback.format_exc())
        user_params = {}
    return user_params
コード例 #14
0
ファイル: daemon.py プロジェクト: jswxjj/addon
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:
                if not PY3:
                    library_lockfile = os.path.join(
                        translatePath(
                            ADDON.getAddonInfo("profile")).decode('utf-8'),
                        "library.db.lock")
                else:
                    library_lockfile = os.path.join(
                        translatePath(ADDON.getAddonInfo("profile")),
                        "library.db.lock")
                    if isinstance(library_lockfile, bytes):
                        library_lockfile = library_lockfile.decode("utf8")
                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 call_binary('openBinary', args, **kwargs)
コード例 #15
0
ファイル: daemon.py プロジェクト: jswxjj/addon
def quasard_thread(monitor):
    crash_count = 0
    try:
        monitor_abort = xbmc.Monitor()  # For Kodi >= 14
        while not monitor_abort.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()

            try:
                if proc.app:
                    assistant = True
                else:
                    assistant = False
            except:
                assistant = False

            def polling_assistant(proc, monitor):
                while proc.poll() is None and not monitor_abort.abortRequested(
                ):
                    time.sleep(1)

            if assistant:
                threading.Thread(target=polling_assistant,
                                 args=[proc, monitor]).start()

            if PLATFORM["os"] == "windows":
                while proc.poll() is None:
                    log.info(proc.stdout.readline())

            elif assistant:
                while proc.poll() is None and not monitor_abort.abortRequested(
                ):
                    for line in iter(proc.stdout.readline,
                                     proc.stdout.read(0)):
                        if PY3 and isinstance(line, bytes):
                            line = line.decode()
                        log.info(line)
                    time.sleep(1)

            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
                    except IOError:
                        time.sleep(1)  # nothing to read, sleep

            log.info("quasard: proc.return code: %s" % str(proc.returncode))
            if proc.returncode == 0 or proc.returncode == -9 or monitor_abort.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
        list(map(log.error, traceback.format_exc().split("\n")))
        if not PY3:
            notify("%s: %s" %
                   (getLocalizedString(30226), repr(e).encode('utf-8')))
        else:
            notify("%s: %s" % (getLocalizedString(30226), repr(e)))
        raise
コード例 #16
0
def get_quasar_binary():
    binary = "quasar" + (PLATFORM["os"] == "windows" and ".exe" or "")

    binary_dir = os.path.join(ADDON.getAddonInfo("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)
        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")), "bin", "%(os)s_%(arch)s" % PLATFORM)

    try:
        binary_dir = binary_dir.decode("latin1")
        dest_binary_dir = dest_binary_dir.decode("latin1")
    except UnicodeEncodeError:
        log.info("Unable to decode: binary_dir=%s dest_binary_dir=%s" % (repr(binary_dir), repr(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(ADDON.getLocalizedString(30103).encode('utf-8'))
        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 OSError 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 OSError as e:
            log.error("Unable to copy to destination path for update: %s" % e)
            notify(ADDON.getLocalizedString(30227).encode('utf-8'))
            system_information()
            pass

    # 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)
コード例 #17
0
def wait_for_abortRequested(proc, monitor):
    monitor.closing.wait()
    log.info("quasard: exiting quasard daemon")
    proc.terminate()
    log.info("quasard: quasard daemon exited")
コード例 #18
0
ファイル: daemon.py プロジェクト: jswxjj/addon
def call_binary(function, cmd, retry=False, p=None, **kwargs):
    import xbmc
    import xbmcaddon
    import traceback
    import base64
    import requests
    import time
    import json

    # Lets try first the traditional way
    if not p:
        try:
            p = subprocess.Popen(cmd, **kwargs)
            log.info('## Executing Popen.CMD: %s - PID: %s' % (cmd, p.pid))
            return p
        except Exception as e:
            if not PY3:
                e = unicode(str(e), "utf8", errors="replace").encode("utf8")
            elif PY3 and isinstance(e, bytes):
                e = e.decode("utf8")
            log.error('Exception Popen ERROR: %s, %s' % (str(cmd), str(e)))

            if PLATFORM["os"] == "android" and ('Errno 13' in str(e)
                                                or 'Errno 2' in str(e)):
                p = None
            else:
                return p

    # The traditional way did not work, so most probably we hit the SDK 29 problem
    APP_PARAMS = {
        'Quasar': {
            'ACTIVE':
            0,
            'USER_ADDON':
            'plugin.video.quasar',
            'USER_ADDON_STATUS':
            xbmc.getCondVisibility('System.HasAddon("plugin.video.quasar")'),
            'USER_ADDON_USERDATA':
            os.path.join(xbmc.translatePath('special://masterprofile/'),
                         'addon_data', 'plugin.video.quasar'),
            'USER_APK': [
                os.path.join(xbmc.translatePath('special://xbmcbinaddons/'),
                             'plugin.video.quasar', 'resources', 'apk',
                             'quasar-mobile-assistant.apk')
            ],
            'USER_APP':
            'com.quasar.quasarmobileassistant',
            'USER_APP_CONTROL':
            'quasar-mobile-assistant.version',
            'USER_APP_URL':
            'http://127.0.0.1',
            'USER_APP_PORT':
            '66666',
            'USER_APP_PORT_ALT':
            '66667'
        },
        'Alfa': {
            'ACTIVE':
            1,
            'USER_ADDON':
            'plugin.video.alfa',
            'USER_ADDON_STATUS':
            xbmc.getCondVisibility('System.HasAddon("plugin.video.alfa")'),
            'USER_ADDON_USERDATA':
            os.path.join(xbmc.translatePath('special://masterprofile/'),
                         'addon_data', 'plugin.video.alfa'),
            'USER_APK': [
                'https://github.com/alfa-addon/alfa-repo/raw/master/downloads/assistant/alfa-mobile-assistant.apk',
                'https://github.com/alfa-addon/alfa-repo/raw/master/downloads/assistant/alfa-mobile-assistant.version'
            ],
            'USER_APP':
            'com.alfa.alfamobileassistant',
            'USER_APP_CONTROL':
            'alfa-mobile-assistant.version',
            'USER_APP_URL':
            'http://127.0.0.1',
            'USER_APP_PORT':
            '48885',
            'USER_APP_PORT_ALT':
            '48886'
        }
    }

    QUASAR_ADDON_SETTING = ADDON
    QUASAR_ADDON_USERDATA = translatePath(
        QUASAR_ADDON_SETTING.getAddonInfo("profile"))
    if xbmc.getCondVisibility('System.HasAddon("plugin.video.torrest")'):
        TORREST_ADDON = True
    else:
        TORREST_ADDON = False
    USER_APP_URL = ''
    USER_APP_URL_ALT = ''
    USER_ADDON = ''
    USER_ADDON_STATUS = False
    ANDROID_STORAGE = os.getenv('ANDROID_STORAGE')
    if not ANDROID_STORAGE: ANDROID_STORAGE = '/storage'
    USER_APP = ''
    USER_APP_PATH = os.path.join(ANDROID_STORAGE, 'emulated', '0', 'Android',
                                 'data')
    USER_APP_STATUS = False

    for user_addon, user_params in list(APP_PARAMS.items()):
        if not user_params['ACTIVE'] or not user_params['USER_ADDON_STATUS']:
            continue

        if user_addon == 'Quasar':
            try:
                # Quasar add-on and Quasar Assistant installed
                USER_ADDON = user_params['USER_ADDON']
                USER_ADDON_STATUS = True
                if os.path.exists(
                        os.path.join(USER_APP_PATH, user_params['USER_APP'])):
                    USER_APP = user_params['USER_APP']
                    USER_APP_STATUS = True
                    USER_APP_URL = "%s:%s" % (user_params['USER_APP_URL'],
                                              user_params['USER_APP_PORT'])
                    USER_APP_URL_ALT = "%s:%s" % (
                        user_params['USER_APP_URL'],
                        user_params['USER_APP_PORT_ALT'])
                if USER_APP_STATUS: break
            except:
                log.error(traceback.format_exc())

        if user_addon == 'Alfa':
            try:
                try:
                    # Alfa add-on and Alfa Assistant installed
                    USER_ADDON_SETTING = xbmcaddon.Addon(
                        id="%s" % user_params['USER_ADDON'])
                    USER_ADDON = user_params['USER_ADDON']
                    USER_ADDON_STATUS = True
                    if USER_ADDON_SETTING.getSetting('assistant_mode') == 'este' and \
                            os.path.exists(os.path.join(USER_APP_PATH, user_params['USER_APP'])):
                        USER_APP = user_params['USER_APP']
                        USER_APP_STATUS = True
                        if USER_ADDON_SETTING.getSetting(
                                'assistant_custom_address'):
                            USER_APP_URL_base = "http://%s" % USER_ADDON_SETTING.getSetting(
                                'assistant_custom_address')
                        else:
                            USER_APP_URL_base = user_params['USER_APP_URL']
                        USER_APP_URL = "%s:%s" % (USER_APP_URL_base,
                                                  user_params['USER_APP_PORT'])
                        USER_APP_URL_ALT = "%s:%s" % (
                            USER_APP_URL_base,
                            user_params['USER_APP_PORT_ALT'])
                except:
                    # Only Alfa Assistant installed and not Alfa
                    if os.path.exists(
                            os.path.join(USER_APP_PATH,
                                         user_params['USER_APP'])):
                        USER_APP_STATUS = True
                if USER_APP_STATUS: break
            except:
                log.error(traceback.format_exc())

    if not USER_APP_STATUS:
        user_params = install_app(APP_PARAMS)
        if not user_params.get('USER_APP', '') or not user_params.get('USER_APP_URL', '') \
                        or not user_params.get('USER_APP_PORT', '') or not user_params.get('USER_APP_PORT_ALT', ''):
            raise ValueError("No app: One MUST be installed")
        else:
            USER_APP_STATUS = True
            USER_APP = user_params['USER_APP']
            USER_APP_URL = "%s:%s" % (user_params['USER_APP_URL'],
                                      user_params['USER_APP_PORT'])
            USER_APP_URL_ALT = "%s:%s" % (user_params['USER_APP_URL'],
                                          user_params['USER_APP_PORT_ALT'])

    if USER_APP_STATUS:
        try:
            """
            Assistant APP acts as a CONSOLE for binaries management in Android 10+ and Kodi 19+
            
            Syntax StartAndroidActivity("USER_APP", "", "function", "cmd|arg| ... |arg|||dict{env} in format |key=value|... "):
                  
                  - cmd: binary name in format '$PWD/lib'binary_name'.so'
                  - 'open':                                                     START the Assitant
                  - 'terminate':                                                CLOSE Assistant
                  - "OpenBinary", "$PWD/libBINARY.so|-port|61235|-settings|/storage/emulated/.../settings.json|||
                                    (kwargs[env]): |key=value|etc=etc":         CALL binary
            
            Syntax Http requests: http://127.0.0.1:48884/command?option=value
                  
                  - /openBinary?cmd=base64OfFullCommand($PWD/libBINARY.so|-port|61235| 
                                    -settings|/storage/emulated/.../settings.json|||
                                    (kwargs[env]): |key=value|etc=etc):         CALL binary
                          - returns: {
                                      "pid": 999,
                                      "output": "Base64encoded",
                                      "error": "Base64encoded",
                                      "startDate": "Base64encoded(2021-12-13 14:00:12)",
                                      "endDate": "Base64encoded([2021-12-13 14:00:12])",    If ended
                                      "retCode": "0|1|number|None"                          None if not ended
                                      "cmd": "Base64encoded(command *args **kwargs)"        Full command as sent to the app
                                      "finalCmd": "Base64encoded($PWD/command *args)"       Actual command executed vy the app
                                      
                  - /openBinary?cmd=base64OfFullCommand([[ANY Android/Linux command: killall libquasar.so (kills all Quasar binaries)]])
                          - returns: {As in /openBinary?cmd}
                  
                  - /getBinaryStatus?pid=999:                                   Request binary STATUS by PID
                          - returns: {As in /openBinary?cmd}
                  
                  - /killBinary?pid=999:                                        Request KILL binary PID
                          - returns: {As in /openBinary?cmd}

                  - /getBinaryList:                                             Return a /getBinaryStatus per binary launched during app session
                          - returns: {As in /openBinary?cmd}
                  - /terminate:                                                 Close Assistant
            """

            # Lets start the Assistant app
            separator = '|'
            separator_escaped = '\|'
            separator_kwargs = '|||'
            command = []
            status_code = 0
            cmd_app = ''

            url = ''
            url_open = USER_APP_URL + '/openBinary?cmd='
            url_killall = url_open + base64.b64encode(
                str('killall%slibquasar.so' %
                    separator).encode('utf8')).decode('utf8')
            if isinstance(p, int):
                url_killall = USER_APP_URL + '/killBinary?pid=%s' % p
            cmd_android = 'StartAndroidActivity("%s", "", "%s", "%s")' % (
                USER_APP, 'open', 'about:blank')
            cmd_android_close = 'StartAndroidActivity("%s", "", "%s", "%s")' % (
                USER_APP, 'terminate', 'about:blank')
            if TORREST_ADDON:
                time.sleep(10)  # let Torrest starts first
            xbmc.executebuiltin(cmd_android)
            time.sleep(3)

            # Build the command & params
            if isinstance(cmd, list):
                command.append(cmd)
                command.append(kwargs)
                # Convert Args to APP format
                cmd_bis = cmd[:]
                cmd_bis[0] = '$PWD/libquasar.so'
                for args in cmd_bis:
                    cmd_app += args.replace(separator,
                                            separator_escaped) + separator
                cmd_app = cmd_app.rstrip(separator)
                # Convert Kwargs to APP format
                if kwargs.get('env', {}):
                    cmd_app += separator_kwargs
                for key, value in list(kwargs.get('env', {}).items()):
                    if key == 'LD_LIBRARY_PATH':
                        # The app will replace $PWD by the binary/lib path
                        value = '$PWD'
                    if key == 'PATH':
                        # The app will replace $PWD by the binary/lib path
                        value = '$PWD:%s' % value
                    cmd_app += '%s=%s%s' % (key.replace(separator, separator_escaped), \
                                value.replace(separator, separator_escaped), separator)
                cmd_app = cmd_app.rstrip(separator)
                command_base64 = base64.b64encode(
                    cmd_app.encode('utf8')).decode('utf8')
            else:
                command_base64 = cmd
                cmd = p.args_
                kwargs = p.kwargs_
                command.append(cmd)
                command.append(kwargs)

            # Launch the Binary
            launch_status = True
            try:
                session = requests.Session()
                # First, cancel existing Binary sessions
                url = url_killall
                log.info('## Killing Quasar from Assistant App: %s' % url)
                resp = session.get(url, timeout=5)
                status_code = resp.status_code
                if status_code != 200:
                    log.info('## ERROR Killing Quasar from Assistant App: %s' %
                             resp.content)
                time.sleep(1)
                # Now lets launch the Binary
                log.info(
                    '## Calling Quasar from Assistant App: %s - Retry = %s' %
                    (cmd, retry))
                url = url_open + command_base64
                resp = session.get(url, timeout=5)
            except Exception as e:
                resp = requests.Response()
                resp.status_code = str(e)
            status_code = resp.status_code
            if status_code != 200 and not retry:
                log.error(
                    "## Calling/Killing Quasar: Invalid app requests response: %s.  Closing Assistant (1)"
                    % status_code)
                if TORREST_ADDON:
                    time.sleep(10)  # let Torrest starts first
                else:
                    xbmc.executebuiltin(cmd_android_close)
                    time.sleep(5)
                return call_binary(function, cmd, retry=True, **kwargs)
            elif status_code != 200 and retry:
                log.error(
                    "## Calling/Killing Quasar: Invalid app requests response: %s.  Closing Assistant (1)"
                    % status_code)
                launch_status = False
                xbmc.executebuiltin(cmd_android_close)
                time.sleep(5)
            try:
                app_response = resp.content
                if launch_status:
                    if PY3 and isinstance(app_response, bytes):
                        app_response = app_response.decode()
                    app_response = re.sub('\n|\r|\t', '', app_response)
                    app_response = json.loads(app_response)
            except:
                status_code = resp.content
                launch_status = False
                log.info("## Calling Quasar: Invalid app response: %s" %
                         resp.content)

            # Simulate the response from subprocess.Popen
            pipeout, pipein = os.pipe()

            class Proc:
                pid = 999999
                stdout = os.fdopen(pipeout, 'rb')
                stdin = os.fdopen(pipein, 'wb')
                stderr = stdout
                returncode = None
                startDate = ''
                endDate = ''
                poll = ''
                terminate = ''
                communicate = ''
                app = USER_APP
                url_app = USER_APP_URL
                url_app_alt = USER_APP_URL_ALT
                cmd_app = command_base64
                finalCmd = ''
                args_ = cmd
                kwargs_ = kwargs
                sess = session
                monitor = xbmc.Monitor()
                binary_time = time.time()
                binary_awake = 0
                torrest = TORREST_ADDON

            p = Proc()

            def redirect_terminate(p=p, action='killBinary'):
                return binary_stat(p, action)

            def redirect_poll(p=p, action='poll'):
                return binary_stat(p, action)

            def redirect_communicate(p=p, action='communicate'):
                return binary_stat(p, action)

            p.poll = redirect_poll
            p.terminate = redirect_terminate
            p.communicate = redirect_communicate

            # If something went wrong on the binary launch, lets return the error so it is recovered from the standard code
            if not launch_status:
                p.returncode = 999
                raise ValueError("No app response:  error code: %s" %
                                 status_code)

            try:
                p.pid = int(app_response['pid'])
            except:
                raise ValueError("No valid PID returned:  PID code: %s" %
                                 resp.content)

            # Handle initial messages
            time.sleep(2)
            if app_response.get('output') or app_response.get('error'):
                res = binary_stat(p, 'poll', app_response=app_response)
            else:
                for x in range(20):
                    res = binary_stat(p, 'poll', init=True)
                    if res: break
                    time.sleep(1)
                else:
                    # Is the binary hung?  Lets restart it
                    return call_binary(function,
                                       command_base64,
                                       retry=True,
                                       kwargs={})

            log.info('## Assistant executing CMD: %s - PID: %s' %
                     (command[0], p.pid))
            #log.warning('## Assistant executing CMD **kwargs: %s' % command[1])
        except:
            log.info('## Assistant ERROR %s in CMD: %s%s' %
                     (status_code, url, command))
            log.error(traceback.format_exc())

    return p
コード例 #19
0
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)'):
        if wait_counter == 1:
            log.info('Add-on settings currently opened, waiting before starting...')
        if wait_counter > 300:
            break
        time.sleep(1)

    return subprocess.Popen(args, **kwargs)
コード例 #20
0
def run(url_suffix=""):
    if not os.path.exists(
            os.path.join(xbmc.translatePath(ADDON.getAddonInfo("path")),
                         ".firstrun")):
        notify(getLocalizedString(30101))
        system_information()
        return

    socket.setdefaulttimeout(300)
    urllib2.install_opener(urllib2.build_opener(NoRedirectHandler()))

    url = sys.argv[0].replace("plugin://%s" % ADDON_ID,
                              QUASARD_HOST + url_suffix) + sys.argv[2]
    log.info("Requesting %s from %s" % (url, repr(sys.argv)))

    try:
        data = _json(url)
    except Exception as e:
        map(log.error, traceback.format_exc().split("\n"))
        notify("%s: %s" % (getLocalizedString(30225), repr(e).encode('utf-8')))
        return

    if not data:
        return

    if data["content_type"]:
        xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_UNSORTED)
        if data["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, data["content_type"])

    listitems = range(len(data["items"]))
    for i, item in enumerate(data["items"]):
        # Translate labels
        if item["label"][0:8] == "LOCALIZE":
            item["label"] = getLocalizedLabel(item["label"])
        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"])
        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"])
        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)
コード例 #21
0
ファイル: daemon.py プロジェクト: jswxjj/addon
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
        app_id = android_get_current_appid()
        xbmc_data_path = translatePath("special://xbmcbin/").replace(
            'user/0', 'data').replace('cache/apk/assets', 'files/quasar')
        log.info("Trying binary Kodi folder: %s" % xbmc_data_path)

        try:  #Test if there is any permisions problem
            if not os.path.exists(xbmc_data_path):
                os.makedirs(xbmc_data_path)
        except Exception as e:
            log.info("ERROR %s in binary Kodi folder: %s" %
                     (str(e), xbmc_data_path))

        if not os.path.exists(xbmc_data_path):
            xbmc_data_path = translatePath("special://xbmcbin/").replace(
                'cache/apk/assets', 'files/quasar')
            log.info("Trying alternative binary Kodi folder: %s" %
                     xbmc_data_path)

            try:  #Test if there is any permisions problem
                if not os.path.exists(xbmc_data_path):
                    os.makedirs(xbmc_data_path)
            except Exception as e:
                log.info("ERROR %s in alternative binary Kodi folder: %s" %
                         (str(e), xbmc_data_path))

        dest_binary_dir = xbmc_data_path
    else:
        if not PY3:
            dest_binary_dir = os.path.join(
                translatePath(ADDON.getAddonInfo("profile")).decode('utf-8'),
                "bin", "%(os)s_%(arch)s" % PLATFORM)
        else:
            dest_binary_dir = os.path.join(
                translatePath(ADDON.getAddonInfo("profile")), "bin",
                "%(os)s_%(arch)s" % PLATFORM)

    if PY3 and isinstance(dest_binary_dir, bytes):
        dest_binary_dir = dest_binary_dir.decode("utf8")
    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()
            #return False, False
        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
    if os.path.exists(dest_binary_dir):
        try:
            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)
        except:
            pass

    return dest_binary_dir, ensure_exec_perms(dest_binary_path)