def main(): try: addons.download_addons() except Exception as e: cli.message('Failed to load Exodus: %s' % str(e)) _exit(-1) _setup_paths() _inject_xbmc() _clear_log() args = ('plugin://plugin.video.exodus', 0, '') while True: t = threading.Thread(target=_run_exodus, args=args) t.daemon = True cli.message('Loading...') t.start() t.join() handle = args[1] while True: try: selected = _dir_select(handle) except KeyboardInterrupt: _exit(0) args = _determine_args(*selected) if args: break
def _download(id, b64url, zipmd5, version): '''Download one addon from base64 encoded url Checks md5 Loads zip contents into addons directory ''' version_file = path.join(config.addonsdir, id + '.version') dir = path.join(config.addonsdir, id) if path.isfile(version_file): try: with open(version_file, 'r') as f: curr_version = f.read( 30).strip() # version should not be more than 30 bytes except: # try to remove version file if failed to read from it remove(version_file) raise if curr_version == version and path.isdir(dir): return cli.message('Retrieving %s (%s)...' % (id, version)) if path.isdir(dir): shutil.rmtree(dir) url = base64.b64decode(b64url) resp = urllib2.urlopen(url) s = resp.read(5 * 1000 * 1000) # should not be larger than 5MB m = hashlib.md5(s) if m.hexdigest() != zipmd5: raise Exception('Invalid md5 for %s', id) f = StringIO(s) try: with zipfile.ZipFile(f) as z: z.extractall(config.addonsdir) except: # try to remove directory if anything went wrong if path.isdir(dir): shutil.rmtree(dir) raise try: with open(version_file, 'w+') as f: f.write(version) except: # try to remove version file if failed to write to it if path.isfile(version_file): remove(version_file) raise cli.message('Loaded %s' % id)
def executebuiltin(function): m = re.match(_re_notification, function) if m: header, message, time, image = m.groups() cli.message('%s: %s' % (header.strip(), message.strip())) try: t = int(time) cli.message('(resume in %s seconds)' % round(t / 1000.0)) except TypeError: t = 0 sleep(t) return m = re.match(_re_update, function) if m: # this is completely different from how Kodi deals with Container.Update # Kodi creates new addon process to run the url, here we're making assumptions # on how Exodus works, and run Exodus again in the same thread from the entry # point with the query string supplied from the Container.Update call url = m.group(1) parts = urlparse(url) if parts.scheme == 'plugin' and parts.netloc == config.exodus['id']: qs = parts.query kv = dict(parse_qsl(qs)) if kv['action'] in [ 'addItem', 'moviePersons', 'movies', 'tvPersons', 'tvshows' ]: exodus, _ = os.path.splitext(config.exodus['entryfile']) old_argv = sys.argv[:] sys.argv[2] = '?' + qs runpy.run_module(exodus) sys.argv = old_argv return raise NotImplementedError if function == 'Dialog.Close(virtualkeyboard)': return if function == 'Dialog.Close(yesnoDialog)': return if function == 'Dialog.Close(busydialog)': return if function == 'Container.SetViewMode(500)': return if function == 'Container.SetViewMode(504)': return raise NotImplementedError
def executebuiltin(function): m = re.match(_re_notification, function) if m: header, message, time, image = m.groups() cli.message('%s: %s' % (header.strip(), message.strip())) try: t = int(time) cli.message('(resume in %s seconds)' % round(t/1000.0)) except TypeError: t = 0 sleep(t) return m = re.match(_re_update, function) if m: # this is completely different from how Kodi deals with Container.Update # Kodi creates new addon process to run the url, here we're making assumptions # on how Exodus works, and run Exodus again in the same thread from the entry # point with the query string supplied from the Container.Update call url = m.group(1) parts = urlparse(url) if parts.scheme == 'plugin' and parts.netloc == config.exodus['id']: qs = parts.query kv = dict(parse_qsl(qs)) if kv['action'] in ['addItem', 'moviePersons', 'movies', 'tvPersons', 'tvshows']: exodus, _ = os.path.splitext(config.exodus['entryfile']) old_argv = sys.argv[:] sys.argv[2] = '?' + qs runpy.run_module(exodus) sys.argv = old_argv return raise NotImplementedError if function == 'Dialog.Close(virtualkeyboard)': return if function == 'Dialog.Close(yesnoDialog)': return if function == 'Dialog.Close(busydialog)': return if function == 'Container.SetViewMode(500)': return if function == 'Container.SetViewMode(504)': return raise NotImplementedError
def _determine_args(handle, index): '''Return arguments tuple (used for calling Exodus) based on selected item Return None if it is a special item or is unsupported ''' url, listitem, isfolder = xbmcplugin._dirs[handle][index] urlparts = urlparse(url) scheme = urlparts.scheme if scheme == 'plugin': base = '%s://%s' % (scheme, urlparts.netloc) query = '?%s' % urlparts.query return (base, handle + 1, query) elif scheme == 'browser': link = url[len('browser://'):] try: webbrowser.get().open(link) except webbrowser.Error: cli.message('Failed to open URL in browser') return None elif scheme == 'print': link = url[len('print://'):] cli.message(link) return None else: cli.message('Unsupported item') return None
def _exit(status): cli.message('\nBye\n') sys.exit(status)
def update(self, percent, line1='', line2='', line3=''): for l in [line1, line2, line3]: if l: cli.message('%s: %s' % (self.heading, l)) cli.message('%s: %s%%' % (self.heading, percent))
def create(self, heading, line1='', line2='', line3=''): self.heading = heading for l in [line1, line2, line3]: if l: cli.message('%s: %s' % (heading, l))