Пример #1
0
    def handle_crashes(self, max_crashes=5, max_consecutive_crash_time=20):
        crash_count = 0
        last_crash = 0

        while not self.waitForAbort(1):
            # Initial check to avoid using the lock most of the time
            if self._daemon.daemon_poll() is None:
                continue

            with self._lock:
                if self._enabled and self._daemon.daemon_poll() is not None:
                    logging.info("Deamon crashed")
                    kodi.notification(kodi.translate(30105))
                    self._stop()

                    crash_time = time.time()
                    time_between_crashes = crash_time - last_crash
                    if 0 < max_consecutive_crash_time < time_between_crashes:
                        crash_count = 1
                    else:
                        crash_count += 1

                    if last_crash > 0:
                        logging.info("%s seconds passed since last crash",
                                     time_between_crashes)
                    last_crash = crash_time

                    if crash_count <= max_crashes:
                        logging.info("Re-starting daemon - %s/%s", crash_count,
                                     max_crashes)
                        self._start()
                        self._wait(timeout=get_daemon_timeout(),
                                   notification=True)
Пример #2
0
def wait_for_metadata(info_hash):
    close_busy_dialog()
    percent = 0
    timeout = get_metadata_timeout()
    start_time = time.time()
    monitor = Monitor()
    progress = DialogProgress()
    progress.create(ADDON_NAME, translate(30237))

    try:
        while not api.torrent_status(info_hash).has_metadata:
            if monitor.waitForAbort(0.5):
                raise PlayError("Abort requested")
            passed_time = time.time() - start_time
            if 0 < timeout:
                if timeout < passed_time:
                    notification(translate(30238))
                    raise PlayError("No metadata after timeout")
                percent = int(100 * passed_time / timeout)
            else:
                percent = 0 if percent == 100 else (percent + 5)
            progress.update(percent)
            if progress.iscanceled():
                raise CanceledError("User canceled metadata", info_hash)
    finally:
        progress.close()
Пример #3
0
def play_info_hash(info_hash, buffer=True):
    if not api.torrent_status(info_hash).has_metadata:
        wait_for_metadata(info_hash)

    files = api.files(info_hash, status=False)
    min_candidate_size = get_min_candidate_size() * 1024 * 1024
    candidate_files = [
        f for f in files if is_video(f.path) and f.length >= min_candidate_size
    ]
    if not candidate_files:
        notification(translate(30239))
        raise PlayError("No candidate files found for {}".format(info_hash))
    elif len(candidate_files) == 1:
        chosen_file = candidate_files[0]
    else:
        sort_files(candidate_files)
        chosen_index = Dialog().select(translate(30240),
                                       [f.name for f in candidate_files])
        if chosen_index < 0:
            raise PlayError("User canceled dialog select")
        chosen_file = candidate_files[chosen_index]

    if buffer:
        buffer_and_play(info_hash, chosen_file.id)
    else:
        play(info_hash, chosen_file.id)
Пример #4
0
def clear_entries():
    entries = Entries()
    if entries.length() == 0:
        notification(translate(30010))
    else:
        entries.clear()
        entries.save()
        update_repository()
        notification(translate(30011))
Пример #5
0
def import_entries():
    path = str_to_unicode(
        xbmc.translatePath(xbmcgui.Dialog().browse(1, translate(30002),
                                                   "files", ".json|.zip")))
    if path:
        entries = Entries()
        entries.add_entries_from_file(path)
        entries.save()
        update_repository()
        notification(translate(30012))
Пример #6
0
def dialog_insert():
    window = DialogInsert("DialogInsert.xml", ADDON_PATH, "Default")
    window.doModal()
    if window.type == DialogInsert.TYPE_PATH:
        api.add_torrent(window.ret_val, download=download_after_insert())
    elif window.type == DialogInsert.TYPE_URL:
        api.add_magnet(window.ret_val, download=download_after_insert())
    else:
        return
    notification(translate(30243), time=2000)
Пример #7
0
 def _wait(self, timeout=-1, notification=False):
     start = time.time()
     while not 0 < timeout < time.time() - start:
         try:
             self._request("get", "")
             if notification:
                 kodi.notification(kodi.translate(30104))
             return
         except requests.exceptions.ConnectionError:
             if self.waitForAbort(0.5):
                 raise AbortRequestedError("Abort requested")
     raise DaemonTimeoutError("Timeout reached")
Пример #8
0
    def handle_crashes(self, max_crashes=5, max_consecutive_crash_time=20):
        crash_count = 0
        last_crash = 0

        while not self.waitForAbort(1):
            # Initial check to avoid using the lock most of the time
            if self._daemon.daemon_poll() is None:
                continue

            with self._lock:
                if self._enabled and self._daemon.daemon_poll() is not None:
                    logging.warning("Deamon crashed")
                    kodi.notification(kodi.translate(30105))
                    self._stop()

                    if os.path.exists(self._log_path):
                        path = os.path.join(
                            kodi.ADDON_DATA,
                            time.strftime("%Y%m%d_%H%M%S.") + self.log_name)
                        shutil.copy(self._log_path, path)

                    crash_time = time.time()
                    time_between_crashes = crash_time - last_crash
                    if 0 < max_consecutive_crash_time < time_between_crashes:
                        crash_count = 1
                    else:
                        crash_count += 1

                    if last_crash > 0:
                        logging.info("%.2f seconds passed since last crash",
                                     time_between_crashes)
                    last_crash = crash_time

                    if crash_count <= max_crashes:
                        logging.info("Re-starting daemon - %s/%s", crash_count,
                                     max_crashes)

                        if crash_count > 1 and os.path.exists(
                                self._settings_path):
                            logging.info("Removing old settings file")
                            os.remove(self._settings_path)

                        self._start()

                        try:
                            self._wait(timeout=get_daemon_timeout(),
                                       notification=True)
                            self._update_daemon_settings()
                        except DaemonTimeoutError:
                            logging.error("Timed out waiting for daemon")
                            last_crash = time.time()
                    else:
                        logging.info("Max crashes (%d) reached", max_crashes)
Пример #9
0
def delete_entries():
    entries = Entries()
    if entries.length() == 0:
        notification(translate(30010))
    else:
        selected = xbmcgui.Dialog().multiselect(translate(30003), entries.ids)
        if selected:
            for index in selected:
                entries.remove(index)
            entries.save()
            update_repository()
            notification(translate(30011))
Пример #10
0
def play_info_hash(info_hash, timeout=30, buffer=True):
    start_time = time.time()
    monitor = Monitor()
    progress = DialogProgress()
    progress.create(ADDON_NAME, translate(30237))

    try:
        while not api.torrent_status(info_hash).has_metadata:
            if monitor.waitForAbort(0.5):
                raise PlayError("Abort requested")
            passed_time = time.time() - start_time
            if 0 < timeout < passed_time:
                notification(translate(30238))
                raise PlayError("No metadata after timeout")
            progress.update(int(100 * passed_time / timeout))
            if progress.iscanceled():
                raise PlayError("User canceled metadata")
    finally:
        progress.close()

    files = api.files(info_hash, status=False)
    min_candidate_size = get_min_candidate_size() * 1024 * 1024
    candidate_files = [
        f for f in files if is_video(f.path) and f.length >= min_candidate_size
    ]
    if not candidate_files:
        notification(translate(30239))
        raise PlayError("No candidate files found for {}".format(info_hash))
    elif len(candidate_files) == 1:
        chosen_file = candidate_files[0]
    else:
        sort_files(candidate_files)
        chosen_index = Dialog().select(translate(30240),
                                       [f.name for f in candidate_files])
        if chosen_index < 0:
            raise PlayError("User canceled dialog select")
        chosen_file = candidate_files[chosen_index]

    if buffer:
        buffer_and_play(info_hash, chosen_file.id)
    else:
        play(info_hash, chosen_file.id)
Пример #11
0
def wait_for_buffering_completion(info_hash, file_id):
    close_busy_dialog()
    info = api.file_info(info_hash, file_id)
    of = translate(30244)
    timeout = get_buffering_timeout()
    last_time = last_done = 0
    start_time = time.time()

    monitor = Monitor()
    progress = DialogProgress()
    progress.create(ADDON_NAME)

    try:
        while True:
            current_time = time.time()
            status = api.file_status(info_hash, file_id)
            if status.buffering_progress >= 100:
                break

            total_done = status.buffering_total * status.buffering_progress / 100
            speed = float(total_done - last_done) / (current_time - last_time)
            last_time = current_time
            last_done = total_done
            progress.update(
                int(status.buffering_progress),
                "{} - {:.2f}%\n{} {} {} - {}/s\n{}\n".format(
                    get_state_string(status.state), status.buffering_progress,
                    sizeof_fmt(total_done), of,
                    sizeof_fmt(status.buffering_total), sizeof_fmt(speed),
                    info.name))

            if progress.iscanceled():
                raise CanceledError("User canceled buffering", info_hash)
            if 0 < timeout < current_time - start_time:
                notification(translate(30236))
                raise PlayError("Buffering timeout reached")
            if monitor.waitForAbort(1):
                raise PlayError("Abort requested")
    finally:
        progress.close()
Пример #12
0
def buffer_and_play(info_hash, file_id):
    api.download_file(info_hash, file_id, buffer=True)

    monitor = Monitor()
    progress = DialogProgress()
    progress.create(ADDON_NAME)

    try:
        timeout = get_buffering_timeout()
        start_time = time.time()
        last_time = 0
        last_done = 0
        while True:
            current_time = time.time()
            status = api.file_status(info_hash, file_id)
            if status.buffering_progress >= 100:
                break

            total_done = status.buffering_total * status.buffering_progress / 100
            speed = float(total_done - last_done) / (current_time - last_time)
            last_time = current_time
            last_done = total_done
            progress.update(
                int(status.buffering_progress),
                "{} - {:.2f}%\n{} {} {} - {}/s\n\n".format(
                    get_state_string(status.state), status.buffering_progress,
                    sizeof_fmt(total_done), translate(30244),
                    sizeof_fmt(status.buffering_total), sizeof_fmt(speed)))

            if progress.iscanceled():
                raise PlayError("User canceled buffering")
            if 0 < timeout < current_time - start_time:
                notification(translate(30236))
                raise PlayError("Buffering timeout reached")
            if monitor.waitForAbort(1):
                raise PlayError("Abort requested")
    finally:
        progress.close()

    play(info_hash, file_id)
Пример #13
0
def install_app(APP_PARAMS):
    import traceback
    import requests
    import xbmc
    import time
    from lib import kodi
    
    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:
                        logging.info("## 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:
                logging.info("## Install_app: Installing the APK from: %s", LOCAL_DOWNLOAD_PATH)
                kodi.notification('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'])):
                        logging.info("## Install_app: APP installed: %s", user_params['USER_APP'])
                        kodi.notification('Accept Assistant permissions')
                        logging.info("## Install_app: Requesting permissions: %s", user_params['USER_APP'])
                        time.sleep(5)
                        xbmc.executebuiltin(cmd_android_permissions)
                        time.sleep(15)
                        logging.info("## Install_app: closing APP: %s", user_params['USER_APP'])
                        kodi.notification('Accept Assistant permissions')
                        xbmc.executebuiltin(cmd_android_close)
                        logging.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:
        logging.info(traceback.format_exc())
        user_params = {}
    return user_params
Пример #14
0
def torrent_status(info_hash):
    status = api.torrent_status(info_hash)
    notification("{:s} ({:.2f}%)".format(get_state_string(status.state),
                                         status.progress),
                 api.torrent_info(info_hash).name,
                 sound=False)
Пример #15
0
def update_repository(notify=False):
    get_request("http://127.0.0.1:{}/update".format(get_repository_port()),
                timeout=2)
    if notify:
        notification(translate(30013))
Пример #16
0
def run():
    try:
        plugin.run()
    except Exception as e:
        logging.error("Caught exception:", exc_info=True)
        notification(str(e))
Пример #17
0
def binary_stat(p, action, retry=False, init=False, app_response={}):
    if init: logging.info('## Binary_stat: action: %s; PID: %s; retry: %s; init: %s; awake: %s; app_r: %s', \
                        action, p.pid, retry, init, p.binary_awake, app_response)
    import traceback
    import base64
    import requests
    import json
    import time
    import xbmc
    
    try:
        if action in ['poll', 'communicate']:
            url = p.url_app + '/getBinaryStatus?pid=%s&flushAfterRead=true' % str(p.pid)
            url_alt = p.url_app_alt + '/getBinaryStatus?pid=%s&flushAfterRead=true' % str(p.pid)

        if action == 'killBinary':
            url = p.url_app + '/killBinary?pid=%s' % str(p.pid)
            url_alt = p.url_app_alt + '/killBinary?pid=%s' % str(p.pid)

        url_close = p.url_app + '/terminate'
        cmd_android = 'StartAndroidActivity("%s", "", "%s", "%s")' % (p.app, 'open', 'about:blank')
        cmd_android_quit = 'StartAndroidActivity("%s", "", "%s", "%s")' % (p.app, 'quit', 'about:blank')
        cmd_android_close = 'StartAndroidActivity("%s", "", "%s", "%s")' % (p.app, 'terminate', 'about:blank')
        cmd_android_permissions = 'StartAndroidActivity("%s", "", "%s", "%s")' % (p.app, 'checkPermissions', 'about:blank')

        finished = False
        retry_req = False
        retry_app = False
        stdout_acum = ''
        stderr_acum = ''
        msg = ''
        binary_awake = 0
        binary_awake_safe = 300*1000
        while not finished:
            if not app_response.get('retCode', 0) >= 999:
                try:
                    resp = p.sess.get(url, timeout=5)
                except Exception as e:
                    resp = requests.Response()
                    resp.status_code = str(e)
                
                if resp.status_code != 200 and not retry_req:
                    if action == 'killBinary' or p.monitor.abortRequested():
                        app_response = {'pid': p.pid, 'retCode': 998}
                    else:
                        logging.info("## Binary_stat: Invalid app requests response for PID: %s: %s - retry: %s - awake: %s", \
                                    p.pid, resp.status_code, retry_req, p.binary_awake)
                        retry_req = True
                        url = url_alt
                        msg += str(resp.status_code)
                        stdout_acum += str(resp.status_code)
                        xbmc.executebuiltin(cmd_android)
                        binary_awake = (int(time.time()) - int(p.binary_time)) * 1000 - binary_awake_safe
                        if binary_awake < binary_awake_safe:
                            binary_awake = 0
                        logging.info('## Time.awake: %s; binary_awake: %s; p.binary_awake: %s', \
                                    (int(time.time()) - int(p.binary_time))*1000, binary_awake, p.binary_awake)
                        time.sleep(5)
                        continue
                if resp.status_code != 200 and retry_req and app_response.get('retCode', 0) != 999:
                    logging.info("## Binary_stat: Invalid app requests response for PID: %s: %s - retry: %s - awake: %s.  Closing Assistant", \
                                    p.pid, resp.status_code, retry_req, p.binary_awake)
                    msg += str(resp.status_code)
                    stdout_acum += str(resp.status_code)
                    app_response = {'pid': p.pid, 'retCode': 999}
                    xbmc.executebuiltin(cmd_android_close)
                    time.sleep(5)
                    xbmc.executebuiltin(cmd_android)
                    binary_awake = (int(time.time()) - int(p.binary_time)) * 1000 - binary_awake_safe
                    if binary_awake > binary_awake_safe:
                        if p.binary_awake:
                            if binary_awake < p.binary_awake: p.binary_awake = binary_awake
                        else:
                            p.binary_awake = binary_awake
                        time.sleep(5)
                        logging.info('## Time.awake: %s; binary_awake: %s; p.binary_awake: %s; awakingInterval: True', \
                                    (int(time.time()) - int(p.binary_time))*1000, binary_awake, p.binary_awake)
                        try:
                            if not 'awakingInterval' in url: 
                                url += '&awakingInterval=%s' % p.binary_awake
                                resp = p.sess.get(url, timeout=5)
                        except:
                            pass
                        time.sleep(1)
                        continue
                    time.sleep(5)
                    continue

                if resp.status_code == 200:
                    try:
                        app_response = resp.content
                        if init: logging.debug(app_response)
                        if PY3 and isinstance(app_response, bytes):
                            app_response = app_response.decode()
                        app_response_save = app_response
                        app_response = re.sub('\n|\r|\t', '', app_response)
                        app_response = json.loads(app_response)
                        test_json = app_response["pid"]
                    except:
                        status_code = resp.content
                        logging.info("## Binary_stat: Invalid app response for PID: %s: %s - retry: %s - awake: %s", \
                                    p.pid, resp.content, retry_app, binary_awake)
                        if retry_app:
                            app_response = {'pid': p.pid}
                            app_response['retCode'] = 999
                            msg += app_response_save
                            stdout_acum += app_response_save
                        else:
                            retry_app = True
                            app_response = {}
                            if not 'awakingInterval' in url and (binary_awake > 0 or p.binary_awake > 0):
                                if p.binary_awake:
                                    if binary_awake and binary_awake < p.binary_awake: p.binary_awake = binary_awake
                                else:
                                    p.binary_awake = binary_awake
                                url += '&awakingInterval=%s' % p.binary_awake
                                logging.info('## Time.awake: %s; binary_awake: %s; p.binary_awake: %s; awakingInterval: True', \
                                            (int(time.time()) - int(p.binary_time))*1000, binary_awake, p.binary_awake)
                            time.sleep(1)
                            continue
                        
            if app_response.get("pid", 0):
                if app_response.get('output'):
                    stdout_acum += base64.b64decode(app_response['output']).decode('utf-8')
                    msg += base64.b64decode(app_response['output']).decode('utf-8')
                if app_response.get('error'): 
                    stderr_acum += base64.b64decode(app_response['error']).decode('utf-8')
                    msg += base64.b64decode(app_response['error']).decode('utf-8')
                if app_response.get('startDate'): 
                    p.startDate = base64.b64decode(app_response['startDate']).decode('utf-8')
                if app_response.get('endDate'): 
                    p.endDate = base64.b64decode(app_response['endDate']).decode('utf-8')
                if app_response.get('cmd'): 
                    p.cmd_app = base64.b64decode(app_response['cmd']).decode('utf-8')
                if app_response.get('finalCmd'): 
                    p.finalCmd = base64.b64decode(app_response['finalCmd']).decode('utf-8')

                # If still app permissions not allowed, give it a retry
                if 'permission denied' in msg:
                    from lib import kodi
                    kodi.notification('Accept Assitant permissions', time=15000)
                    time.sleep(5)
                    xbmc.executebuiltin(cmd_android_permissions)
                    time.sleep(10)
                    xbmc.executebuiltin(cmd_android_quit)
                    time.sleep(3)
                
                if msg:
                    try:
                        for line in msg.split('\n'):
                            line += '\n'
                            if PY3 and not isinstance(line, (bytes, bytearray)):
                                line = line.encode('utf-8')
                            p.stdin.write(line)
                            p.stdin.flush()
                    except:
                        pass
            
            p.returncode = None
            if action == 'killBinary' and not app_response.get('retCode', ''):
                app_response['retCode'] = 137
            if app_response.get('retCode', '') or action == 'killBinary' or \
                            (action == 'communicate' and app_response.get('retCode', '') != ''):
                try:
                    p.stdin.flush()
                    p.stdin.close()
                except:
                    pass
                try:
                    p.returncode = int(app_response['retCode'])
                except:
                    p.returncode = app_response['retCode']
                
            if action == 'communicate' and p.returncode is not None:
                logging.info("## Binary_stat: communicate Torrest: %s - Returncode: %s", p.pid, p.returncode)
                return stdout_acum, stderr_acum
            
            elif action == 'poll':
                if init and msg:
                    logging.debug('## Binary_stat: Torrest initial response: %s', msg)
                    return True
                return p.returncode
            
            elif action == 'killBinary':
                logging.info("## Binary_stat: killBinary Torrest: %s - Returncode: %s", p.pid, p.returncode)
                try:
                    if p.monitor.abortRequested():
                        try:
                            resp_t = p.sess.get(url_close, timeout=1)
                        except:
                            pass
                    elif p.returncode == 998:
                        xbmc.executebuiltin(cmd_android_close)
                        time.sleep(5)
                except:
                    logging.info(traceback.format_exc())
                    time.sleep(1)
                    xbmc.executebuiltin(cmd_android_close)
                    time.sleep(2)
                return p
            
            time.sleep(5)
            msg = ''
            app_response = {}

    except:
        logging.info(traceback.format_exc())
    return None