class Engine:
    POLL_DELAY = 0.1
    ADDON_KEY = 'bjUxTHZRb1RsSnpOR2FGeHNlUkstdXZudlgtc0Q0Vm01QXh3bWM0VWNvRC1qcnV4bUtzdUphSDBl\nVmdF\n'

    DEFAULT_HOST = "127.0.0.1"
    DEFAULT_PORT = 62062

    def __init__(self,
                 host=DEFAULT_HOST,
                 port=DEFAULT_PORT,
                 save_path=None,
                 save_encrypted=False,
                 log=None,
                 on_playback_resumed=None,
                 on_playback_paused=None,
                 on_poll=None):
        self.log = log or logging.getLogger(__name__)
        self.state = None
        self.host = host
        self.port = port
        self.sink = None
        self.duration = None
        self.link = None
        self.is_ad = False
        self.is_live = False
        self.can_save = []
        self.files = None
        self.key = None
        self.version = None
        self.status = None
        self.progress = 0
        self.down_speed = 0
        self.up_speed = 0
        self.peers = 0
        self.download = 0
        self.upload = 0
        self.save_path = save_path
        self.save_indexes = []
        self.save_encrypted = save_encrypted
        self.saved_files = {}
        self.on_playback_resumed = on_playback_resumed
        self.on_playback_paused = on_playback_paused
        self.error = False
        self.error_msg = None
        self.last_event = None
        self.last_event_params = None
        self.auth_level = None
        self.infohash = None
        self.ready = False
        self.on_poll = on_poll or time.sleep

    def on_start(self, duration):
        self.duration = duration * 1000
        self.sink.send("DUR %s %s" % (self.link, self.duration))
        self.sink.send("PLAYBACK %s 0" % self.link)

    def on_pause(self):
        self.sink.send("EVENT pause")

    def on_resume(self):
        self.sink.send("EVENT play")

    def on_stop(self):
        self.sink.send("EVENT stop")
        self.sink.send("STOP")

    def on_seek(self, _time):
        self.sink.send("EVENT seek position=%s" % int(_time / 1000))

    def on_end(self):
        self.sink.send("PLAYBACK %s 100" % self.link)
        self.sink.send("EVENT stop")
        self.sink.send("STOP")

    def close(self):
        self.log.info("Closing AceStream engine")
        if self.sink:
            if self.state > 0:
                self.on_stop()
            self.sink.send("SHUTDOWN")

    def shutdown(self):
        if self.sink:
            self.sink.end()

    def _start(self):
        if not self._is_local():
            return True
        if self.save_path and not os.path.isdir(self.save_path):
            raise Error("Invalid download path (%s)" % self.save_path,
                        Error.INVALID_DOWNLOAD_PATH)
        if sys.platform.startswith("win"):
            return self._start_windows()
        else:
            try:
                self._start_linux()
            except Error:
                self._start_android()

    def connect(self, timeout=20):
        if not self.sink:
            self.sink = Sink(self.host, on_receive=self.track_sink_event)
        start = time.time()
        connected = False
        started = False
        error = None
        while time.time() - start < timeout and not self.is_ready():
            if not connected:
                try:
                    self.sink.connect(self._get_ace_port())
                    self.sink.send("HELLOBG")
                    connected = True
                except Error as error:
                    if not started:
                        self._start()
                        started = True
            self.on_poll(self.POLL_DELAY)
        if not self.is_ready():
            self.sink = None
            raise error

    @staticmethod
    def _unique_id():
        import random
        return random.randint(0, 0x7fffffff)

    def _files(self, timeout=20):
        start = time.time()
        while time.time(
        ) - start < timeout and self.files is None and not self.error:
            self.on_poll(self.POLL_DELAY)
        if self.error:
            raise Error(self.error_msg, Error.CANT_LOAD_TORRENT)
        elif self.files is not None:
            return self.files
        else:
            raise Error("Timeout while getting files list", Error.TIMEOUT)

    def load_torrent(self,
                     url,
                     developer_id=0,
                     offiliate_id=0,
                     zone_id=0,
                     timeout=20):
        self.sink.send(
            "LOADASYNC %d TORRENT %s %d %d %d" %
            (self._unique_id(), url, developer_id, offiliate_id, zone_id))
        return self._files(timeout)

    def load_infohash(self,
                      infohash,
                      developer_id=0,
                      offiliate_id=0,
                      zone_id=0,
                      timeout=20):
        self.sink.send(
            "LOADASYNC %d INFOHASH %s %d %d %d" %
            (self._unique_id(), infohash, developer_id, offiliate_id, zone_id))
        return self._files(timeout)

    def load_data(self,
                  data,
                  developer_id=0,
                  offiliate_id=0,
                  zone_id=0,
                  timeout=20):
        import base64
        self.sink.send("LOADASYNC %d RAW %s %d %d %d" %
                       (self._unique_id(), base64.b64encode(data),
                        developer_id, offiliate_id, zone_id))
        return self._files(timeout)

    def load_pid(self, content_id, timeout=20):
        self.sink.send("LOADASYNC %d PID %d" % (self._unique_id(), content_id))
        return self._files(timeout)

    def _start_play(self, timeout=20):
        start = time.time()
        while time.time(
        ) - start < timeout and not self.state and not self.error:
            self.on_poll(self.POLL_DELAY)
        if self.error:
            raise Error(self.error_msg, Error.CANT_PLAY_TORRENT)
        elif self.state:
            return
        else:
            raise Error("Timeout while starting playback", Error.TIMEOUT)

    def play_torrent(self,
                     url,
                     indexes=None,
                     developer_id=0,
                     offiliate_id=0,
                     zone_id=0,
                     stream_id=0,
                     timeout=20):
        indexes = indexes or [0]
        self.saved_files = {}
        self.save_indexes = indexes
        self.sink.send("START TORRENT %s %s %d %d %d %d" %
                       (url, ",".join(str(index) for index in indexes),
                        developer_id, offiliate_id, zone_id, stream_id))
        self._start_play(timeout)

    def play_infohash(self,
                      infohash,
                      indexes=None,
                      developer_id=0,
                      offiliate_id=0,
                      zone_id=0,
                      timeout=20):
        indexes = indexes or [0]
        self.saved_files = {}
        self.save_indexes = indexes
        self.sink.send("START INFOHASH %s %s %d %d %d" % (infohash, ",".join(
            str(index)
            for index in indexes), developer_id, offiliate_id, zone_id))
        self._start_play(timeout)

    def play_pid(self, content_id, indexes=None, timeout=20):
        indexes = indexes or [0]
        self.saved_files = {}
        self.save_indexes = indexes
        self.sink.send("START PID %d %s" %
                       (content_id, ",".join(str(index) for index in indexes)))
        self._start_play(timeout)

    def play_data(self,
                  data,
                  indexes=None,
                  developer_id=0,
                  offiliate_id=0,
                  zone_id=0,
                  timeout=20):
        indexes = indexes or [0]
        self.saved_files = {}
        self.save_indexes = indexes
        import base64
        self.sink.send(
            "START RAW %s %s %d %d %d" % (base64.b64encode(data), ",".join(
                str(index)
                for index in indexes), developer_id, offiliate_id, zone_id))
        self._start_play(timeout)

    def play_url(self,
                 url,
                 indexes=None,
                 developer_id=0,
                 offiliate_id=0,
                 zone_id=0,
                 timeout=20):
        indexes = indexes or [0]
        self.saved_files = {}
        self.save_indexes = indexes
        self.sink.send("START URL %s %s %d %d %d" % (url, ",".join(
            str(index)
            for index in indexes), developer_id, offiliate_id, zone_id))
        self._start_play(timeout)

    def play_efile(self, url, timeout=20):
        self.sink.send("START EFILE %s" % url)
        self._start_play(timeout)

    def get_status(self):
        return Status(state=self.state,
                      status=self.status,
                      progress=self.progress,
                      down_speed=self.down_speed,
                      up_speed=self.up_speed,
                      peers=self.peers,
                      url=self.link,
                      error=self.error_msg,
                      download=self.download,
                      upload=self.upload)

    def _start_windows(self):
        import _winreg
        try:
            key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER,
                                  "Software\\AceStream")
            path = _winreg.QueryValueEx(key, "EnginePath")[0]
        except _winreg.error:
            # trying previous version
            try:
                import _winreg
                key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER,
                                      "Software\\TorrentStream")
                path = _winreg.QueryValueEx(key, "EnginePath")[0]
            except _winreg.error:
                raise Error("Can't find AceStream executable",
                            Error.CANT_FIND_EXECUTABLE)
        try:
            self.log.info("Starting AceStream engine for Windows: %s" % path)
            os.startfile(path)
        except:
            raise Error("Can't start AceStream engine",
                        Error.CANT_START_ENGINE)

    def _start_linux(self):
        import subprocess
        try:
            self.log.info("Starting AceStream engine for Linux")
            subprocess.Popen(["acestreamengine", "--client-console"])
        except OSError:
            try:
                subprocess.Popen('acestreamengine-client-console')
            except OSError:
                raise Error("AceStream engine is not installed",
                            Error.CANT_START_ENGINE)

    def _start_android(self):
        try:
            import xbmc
            self.log.info("Starting AceStream engine for Android")
            xbmc.executebuiltin(
                'XBMC.StartAndroidActivity("org.acestream.engine")')
        except:
            raise Error("AceStream engine is not installed",
                        Error.CANT_START_ENGINE)

    def _get_ace_port(self):
        if not self._is_local() or not sys.platform.startswith("win"):
            return self.port
        import _winreg
        try:
            key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER,
                                  "Software\\AceStream")
            engine_exe = _winreg.QueryValueEx(key, "EnginePath")[0]
            path = engine_exe.replace("ace_engine.exe", "")
        except _winreg.error:
            # trying previous version
            try:
                key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER,
                                      "Software\\TorrentStream")
                engine_exe = _winreg.QueryValueEx(key, "EnginePath")[0]
                path = engine_exe.replace("tsengine.exe", "")
            except _winreg.error:
                return self.port
        pfile = os.path.join(path, "acestream.port")
        try:
            hfile = open(pfile, "r")
            return int(hfile.read())
        except IOError:
            return self.port

    def is_ready(self):
        return self.state >= 0

    def track_sink_event(self, event, params):
        self.last_event = event
        self.last_event_params = params

        if event == "STATUS":
            self._update_status(params)

        elif event == "START" or event == "PLAY":
            parts = params.split(" ")
            self.link = parts[0].replace(self.DEFAULT_HOST, self.host)

            if len(parts) > 1:
                if "ad=1" in parts:
                    self.is_ad = True
                    self.sink.send("PLAYBACK %s 100" % self.link)
                    self.link = None
                else:
                    self.is_ad = False

                if "stream=1" in parts:
                    self.is_live = True

        elif event == "AUTH":
            self.auth_level = int(params)
            self.state = State.IDLE

        elif event == "STATE":
            self.state = int(params)

        elif event == "EVENT":
            parts = params.split(" ")
            if len(parts) > 1 and "cansave" in parts:
                if self.save_path:
                    self._save_file(int(parts[1].split("=")[1]),
                                    parts[2].split("=")[1],
                                    parts[3].split("=")[1])

        elif event == "RESUME":
            if self.on_playback_resumed is not None:
                self.on_playback_resumed()

        elif event == "PAUSE":
            if self.on_playback_paused is not None:
                self.on_playback_paused()

        elif event == "HELLOTS":
            parts = params.split(" ")
            for part in parts:
                k, v = part.split("=")
                if k == "key":
                    self.key = v
                elif k == "version":
                    self.version = v
            cmd = "READY"
            if self.key:
                import hashlib
                sha1 = hashlib.sha1()
                addon_key = base64.decodestring(self.ADDON_KEY)
                sha1.update(self.key + addon_key)
                cmd = "READY key=%s-%s" % (addon_key.split("-")[0],
                                           sha1.hexdigest())

            self.sink.send(cmd)

        elif event == "LOADRESP":
            import json
            resp_json = json.loads(params[params.find("{"):len(params)])
            if resp_json["status"] == 100:
                self.error_msg = resp_json["message"]
                self.error = True
            else:
                self.infohash = resp_json["infohash"]
                if resp_json["status"] > 0:
                    self.files = OrderedDict((f[1], urllib.unquote(f[0]))
                                             for f in resp_json["files"])
                else:
                    self.files = OrderedDict()
        elif event == "SHUTDOWN":
            self.shutdown()

    def _save_file(self, index, infohash, _format):
        import urllib

        index = int(index)
        if self.save_path and index in self.save_indexes:
            filename = self.files and self.files[index] or infohash
            if _format == "encrypted":
                if not self.save_encrypted:
                    return
                filename += ".acemedia"
            path = os.path.join(self.save_path, filename)
            if not os.path.exists(path):
                dir_name = os.path.dirname(path)
                if not os.path.exists(dir_name):
                    os.mkdir(dir_name)
                self.sink.send("SAVE infohash=%s index=%s path=%s" %
                               (infohash, index, urllib.quote(path)))
            self.saved_files[index] = path

    def _update_status(self, status_string):
        import re

        ss = re.compile("main:[a-z]+", re.S)
        s1 = re.findall(ss, status_string)[0]
        self.status = s1.split(":")[1]

        if self.status == "starting":
            self.progress = 0
        if self.status == "loading":
            self.progress = 0
        if self.status == "prebuf":
            parts = status_string.split(";")
            self.progress = int(parts[1])
            self.down_speed = int(parts[5])
            self.up_speed = int(parts[7])
            self.peers = int(parts[8])
            self.download = int(parts[10])
            self.upload = int(parts[12])
        if self.status == "buf":
            parts = status_string.split(";")
            self.progress = int(parts[1])
            self.down_speed = int(parts[5])
            self.up_speed = int(parts[7])
            self.peers = int(parts[8])
            self.download = int(parts[10])
            self.upload = int(parts[12])
        if self.status == "dl":
            parts = status_string.split(";")
            self.progress = int(parts[1])
            self.down_speed = int(parts[3])
            self.up_speed = int(parts[5])
            self.peers = int(parts[6])
            self.download = int(parts[8])
            self.upload = int(parts[10])
        if self.status == "check":
            self.progress = int(status_string.split(";")[1])
        if self.status == "idle":
            self.progress = 0
        if self.status == "wait":
            self.progress = 0
        if self.status == "err":
            parts = status_string.split(";")
            self.error = True
            self.error_id = parts[1]
            self.error_msg = parts[2]

    def _is_local(self):
        return self.host == self.DEFAULT_HOST
class Engine:
    POLL_DELAY = 0.1
    ADDON_KEY = "n51LvQoTlJzNGaFxseRK-uvnvX-sD4Vm5Axwmc4UcoD-jruxmKsuJaH0eVgE"

    DEFAULT_HOST = "127.0.0.1"
    DEFAULT_PORT = 62062

    def __init__(self, host=DEFAULT_HOST, port=DEFAULT_PORT, save_path=None, save_encrypted=False,
                 log=None, on_playback_resumed=None, on_playback_paused=None, on_poll=None):
        self.log = log or logging.getLogger(__name__)
        self.state = None
        self.host = host
        self.port = port
        self.sink = None
        self.duration = None
        self.link = None
        self.is_ad = False
        self.is_live = False
        self.can_save = []
        self.files = None
        self.key = None
        self.version = None
        self.status = None
        self.progress = 0
        self.down_speed = 0
        self.up_speed = 0
        self.peers = 0
        self.download = 0
        self.upload = 0
        self.save_path = save_path
        self.save_indexes = []
        self.save_encrypted = save_encrypted
        self.saved_files = {}
        self.on_playback_resumed = on_playback_resumed
        self.on_playback_paused = on_playback_paused
        self.error = False
        self.error_msg = None
        self.last_event = None
        self.last_event_params = None
        self.auth_level = None
        self.infohash = None
        self.ready = False
        self.on_poll = on_poll or time.sleep

    def on_start(self, duration):
        self.duration = duration * 1000
        self.sink.send("DUR %s %s" % (self.link, self.duration))
        self.sink.send("PLAYBACK %s 0" % self.link)

    def on_pause(self):
        self.sink.send("EVENT pause")

    def on_resume(self):
        self.sink.send("EVENT play")

    def on_stop(self):
        self.sink.send("EVENT stop")
        self.sink.send("STOP")

    def on_seek(self, _time):
        self.sink.send("EVENT seek position=%s" % int(_time / 1000))

    def on_end(self):
        self.sink.send("PLAYBACK %s 100" % self.link)
        self.sink.send("EVENT stop")
        self.sink.send("STOP")

    def close(self):
        self.log.info("Closing AceStream engine")
        if self.sink:
            if self.state > 0:
                self.on_stop()
            self.sink.send("SHUTDOWN")

    def shutdown(self):
        if self.sink:
            self.sink.end()

    def _start(self):
        if not self._is_local():
            return True
        if self.save_path and not os.path.isdir(self.save_path):
            raise Error("Invalid download path (%s)" % self.save_path, Error.INVALID_DOWNLOAD_PATH)
        if sys.platform.startswith("win"):
            return self._start_windows()
        else:
            try:
                self._start_linux()
            except Error:
                self._start_android()

    def connect(self, timeout=20):
        if not self.sink:
            self.sink = Sink(self.host, on_receive=self.track_sink_event)
        start = time.time()
        connected = False
        started = False
        error = None
        while time.time() - start < timeout and not self.is_ready():
            if not connected:
                try:
                    self.sink.connect(self._get_ace_port())
                    self.sink.send("HELLOBG")
                    connected = True
                except Error as error:
                    if not started:
                        self._start()
                        started = True
            self.on_poll(self.POLL_DELAY)
        if not self.is_ready():
            self.sink = None
            raise error

    @staticmethod
    def _unique_id():
        import random
        return random.randint(0, 0x7fffffff)

    def _files(self, timeout=20):
        start = time.time()
        while time.time() - start < timeout and self.files is None and not self.error:
            self.on_poll(self.POLL_DELAY)
        if self.error:
            raise Error(self.error_msg, Error.CANT_LOAD_TORRENT)
        elif self.files is not None:
            return self.files
        else:
            raise Error("Timeout while getting files list", Error.TIMEOUT)

    def load_torrent(self, url, developer_id=0, offiliate_id=0, zone_id=0, timeout=20):
        self.sink.send("LOADASYNC %d TORRENT %s %d %d %d" % (self._unique_id(), url,
                                                             developer_id, offiliate_id, zone_id))
        return self._files(timeout)

    def load_infohash(self, infohash, developer_id=0, offiliate_id=0, zone_id=0, timeout=20):
        self.sink.send("LOADASYNC %d INFOHASH %s %d %d %d" % (self._unique_id(), infohash,
                                                              developer_id, offiliate_id, zone_id))
        return self._files(timeout)

    def load_data(self, data, developer_id=0, offiliate_id=0, zone_id=0, timeout=20):
        import base64
        self.sink.send("LOADASYNC %d RAW %s %d %d %d" % (self._unique_id(), base64.b64encode(data),
                                                         developer_id, offiliate_id, zone_id))
        return self._files(timeout)

    def load_pid(self, content_id, timeout=20):
        self.sink.send("LOADASYNC %d PID %d" % (self._unique_id(), content_id))
        return self._files(timeout)

    def _start_play(self, timeout=20):
        start = time.time()
        while time.time() - start < timeout and not self.state and not self.error:
            self.on_poll(self.POLL_DELAY)
        if self.error:
            raise Error(self.error_msg, Error.CANT_PLAY_TORRENT)
        elif self.state:
            return
        else:
            raise Error("Timeout while starting playback", Error.TIMEOUT)

    def play_torrent(self, url, indexes=None, developer_id=0, offiliate_id=0, zone_id=0, stream_id=0, timeout=20):
        indexes = indexes or [0]
        self.saved_files = {}
        self.save_indexes = indexes
        self.sink.send("START TORRENT %s %s %d %d %d %d" % (url, ",".join(str(index) for index in indexes),
                                                            developer_id, offiliate_id, zone_id, stream_id))
        self._start_play(timeout)

    def play_infohash(self, infohash, indexes=None, developer_id=0, offiliate_id=0, zone_id=0, timeout=20):
        indexes = indexes or [0]
        self.saved_files = {}
        self.save_indexes = indexes
        self.sink.send("START INFOHASH %s %s %d %d %d" % (infohash, ",".join(str(index) for index in indexes),
                                                          developer_id, offiliate_id, zone_id))
        self._start_play(timeout)

    def play_pid(self, content_id, indexes=None, timeout=20):
        indexes = indexes or [0]
        self.saved_files = {}
        self.save_indexes = indexes
        self.sink.send("START PID %d %s" % (content_id, ",".join(str(index) for index in indexes)))
        self._start_play(timeout)

    def play_data(self, data, indexes=None, developer_id=0, offiliate_id=0, zone_id=0, timeout=20):
        indexes = indexes or [0]
        self.saved_files = {}
        self.save_indexes = indexes
        import base64
        self.sink.send("START RAW %s %s %d %d %d" % (base64.b64encode(data), ",".join(str(index) for index in indexes),
                                                     developer_id, offiliate_id, zone_id))
        self._start_play(timeout)

    def play_url(self, url, indexes=None, developer_id=0, offiliate_id=0, zone_id=0, timeout=20):
        indexes = indexes or [0]
        self.saved_files = {}
        self.save_indexes = indexes
        self.sink.send("START URL %s %s %d %d %d" % (url, ",".join(str(index) for index in indexes),
                                                     developer_id, offiliate_id, zone_id))
        self._start_play(timeout)

    def play_efile(self, url, timeout=20):
        self.sink.send("START EFILE %s" % url)
        self._start_play(timeout)

    def get_status(self):
        return Status(state=self.state, status=self.status, progress=self.progress, down_speed=self.down_speed,
                      up_speed=self.up_speed, peers=self.peers, url=self.link, error=self.error_msg,
                      download=self.download, upload=self.upload)

    def _start_windows(self):
        import _winreg
        try:
            key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, "Software\\AceStream")
            path = _winreg.QueryValueEx(key, "EnginePath")[0]
        except _winreg.error:
            # trying previous version
            try:
                import _winreg
                key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, "Software\\TorrentStream")
                path = _winreg.QueryValueEx(key, "EnginePath")[0]
            except _winreg.error:
                raise Error("Can't find AceStream executable", Error.CANT_FIND_EXECUTABLE)
        try:
            self.log.info("Starting AceStream engine for Windows: %s" % path)
            os.startfile(path)
        except:
            raise Error("Can't start AceStream engine", Error.CANT_START_ENGINE)

    def _start_linux(self):
        import subprocess
        try:
            self.log.info("Starting AceStream engine for Linux")
            subprocess.Popen(["acestreamengine", "--client-console"])
        except OSError:
            try:
                subprocess.Popen('acestreamengine-client-console')
            except OSError:
                raise Error("AceStream engine is not installed", Error.CANT_START_ENGINE)

    def _start_android(self):
        try:
            import xbmc
            self.log.info("Starting AceStream engine for Android")
            xbmc.executebuiltin('XBMC.StartAndroidActivity("org.acestream.engine")')
        except:
            raise Error("AceStream engine is not installed", Error.CANT_START_ENGINE)

    def _get_ace_port(self):
        if not self._is_local() or not sys.platform.startswith("win"):
            return self.port
        import _winreg
        try:
            key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, "Software\\AceStream")
            engine_exe = _winreg.QueryValueEx(key, "EnginePath")[0]
            path = engine_exe.replace("ace_engine.exe", "")
        except _winreg.error:
            # trying previous version
            try:
                key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, "Software\\TorrentStream")
                engine_exe = _winreg.QueryValueEx(key, "EnginePath")[0]
                path = engine_exe.replace("tsengine.exe", "")
            except _winreg.error:
                return self.port
        pfile = os.path.join(path, "acestream.port")
        try:
            hfile = open(pfile, "r")
            return int(hfile.read())
        except IOError:
            return self.port

    def is_ready(self):
        return self.state >= 0

    def track_sink_event(self, event, params):
        self.last_event = event
        self.last_event_params = params

        if event == "STATUS":
            self._update_status(params)

        elif event == "START" or event == "PLAY":
            parts = params.split(" ")
            self.link = parts[0].replace(self.DEFAULT_HOST, self.host)

            if len(parts) > 1:
                if "ad=1" in parts:
                    self.is_ad = True
                    self.sink.send("PLAYBACK %s 100" % self.link)
                    self.link = None
                else:
                    self.is_ad = False

                if "stream=1" in parts:
                    self.is_live = True

        elif event == "AUTH":
            self.auth_level = int(params)
            self.state = State.IDLE

        elif event == "STATE":
            self.state = int(params)

        elif event == "EVENT":
            parts = params.split(" ")
            if len(parts) > 1 and "cansave" in parts:
                if self.save_path:
                    self._save_file(int(parts[1].split("=")[1]), parts[2].split("=")[1], parts[3].split("=")[1])

        elif event == "RESUME":
            if self.on_playback_resumed is not None:
                self.on_playback_resumed()

        elif event == "PAUSE":
            if self.on_playback_paused is not None:
                self.on_playback_paused()

        elif event == "HELLOTS":
            parts = params.split(" ")
            for part in parts:
                k, v = part.split("=")
                if k == "key":
                    self.key = v
                elif k == "version":
                    self.version = v
            cmd = "READY"
            if self.key:
                import hashlib
                sha1 = hashlib.sha1()

                sha1.update(self.key + self.ADDON_KEY)
                cmd = "READY key=%s-%s" % (self.ADDON_KEY.split("-")[0], sha1.hexdigest())

            self.sink.send(cmd)

        elif event == "LOADRESP":
            import json
            resp_json = json.loads(params[params.find("{"):len(params)])
            if resp_json["status"] == 100:
                self.error_msg = resp_json["message"]
                self.error = True
            else:
                self.infohash = resp_json["infohash"]
                if resp_json["status"] > 0:
                    self.files = OrderedDict((f[1], urllib.unquote(f[0])) for f in resp_json["files"])
                else:
                    self.files = OrderedDict()
        elif event == "SHUTDOWN":
            self.shutdown()

    def _save_file(self, index, infohash, _format):
        import urllib

        index = int(index)
        if self.save_path and index in self.save_indexes:
            filename = self.files and self.files[index] or infohash
            if _format == "encrypted":
                if not self.save_encrypted:
                    return
                filename += ".acemedia"
            path = os.path.join(self.save_path, filename)
            if not os.path.exists(path):
                dir_name = os.path.dirname(path)
                if not os.path.exists(dir_name):
                    os.mkdir(dir_name)
                self.sink.send("SAVE infohash=%s index=%s path=%s" % (infohash, index, urllib.quote(path)))
            self.saved_files[index] = path

    def _update_status(self, status_string):
        import re

        ss = re.compile("main:[a-z]+", re.S)
        s1 = re.findall(ss, status_string)[0]
        self.status = s1.split(":")[1]

        if self.status == "starting":
            self.progress = 0
        if self.status == "loading":
            self.progress = 0
        if self.status == "prebuf":
            parts = status_string.split(";")
            self.progress = int(parts[1])
            self.down_speed = int(parts[5])
            self.up_speed = int(parts[7])
            self.peers = int(parts[8])
            self.download = int(parts[10])
            self.upload = int(parts[12])
        if self.status == "buf":
            parts = status_string.split(";")
            self.progress = int(parts[1])
            self.down_speed = int(parts[5])
            self.up_speed = int(parts[7])
            self.peers = int(parts[8])
            self.download = int(parts[10])
            self.upload = int(parts[12])
        if self.status == "dl":
            parts = status_string.split(";")
            self.progress = int(parts[1])
            self.down_speed = int(parts[3])
            self.up_speed = int(parts[5])
            self.peers = int(parts[6])
            self.download = int(parts[8])
            self.upload = int(parts[10])
        if self.status == "check":
            self.progress = int(status_string.split(";")[1])
        if self.status == "idle":
            self.progress = 0
        if self.status == "wait":
            self.progress = 0
        if self.status == "err":
            parts = status_string.split(";")
            self.error = True
            self.error_id = parts[1]
            self.error_msg = parts[2]

    def _is_local(self):
        return self.host == self.DEFAULT_HOST