def handle_abort(self): # Wait for the format results to complete or an explicit abort event while True: if self.is_canceled() or self.abort_event.isSet(): self.abort_event.set() break kodi.sleep(50)
def handel_error(self, error, response, request_args, request_kwargs): if response.status_code == 401: traceback.print_stack() kodi.close_busy_dialog() raise githubException("Unauthorized: %s" % error) elif response.status_code == 403 and 'X-RateLimit-Reset' in response.headers: import time retry = int(response.headers['X-RateLimit-Reset']) - int( time.time()) for delay in range(retry, 0, -1): kodi.notify("API Rate limit exceeded", "Retry in %s seconds(s)" % delay, timeout=1000) kodi.sleep(1000) return self.request(*request_args, **request_kwargs) elif response.status_code == 422 and 'Only the first 1000' in response.text: kodi.handel_error('Result count exceeds API limit.', 'Try different search or result filter.') kodi.close_busy_dialog() traceback.print_stack() else: kodi.close_busy_dialog() traceback.print_stack() raise githubException("Status %s: %s" % (response.status_code, response.text))
def onInit(self): response = request_code() self.device_code = response['device_code'] self.user_code = response['user_code'] self.timeout = int(response['expires_in']) self.getControl(CONTROLS.CODE).setLabel(self.user_code) for tick in range(self.timeout, 0,-1): if tick == 0 or self._abort: break width = (float(tick) / self.timeout) * 596 self.getControl(CONTROLS.PROGRESS).setWidth(int(width)) if (tick % 5) == 0: r = poll_credentials(self.device_code) if r: client_id = r['client_id'] client_secret = r['client_secret'] token = request_token(client_id, client_secret, self.device_code) kodi.set_setting('realdebrid_client_id', client_id, addon_id='script.module.scrapecore') kodi.set_setting('realdebrid_client_secret', client_secret, addon_id='script.module.scrapecore') kodi.set_setting('realdebrid_token', token['access_token'], addon_id='script.module.scrapecore') kodi.set_setting('realdebrid_refresh_token', token['refresh_token'], addon_id='script.module.scrapecore') kodi.notify("RealDebrid Authorization", "Success!") self._close() return kodi.sleep(1000) self.close()
def onInit(self): response = request_code() self.device_code = response['device_code'] self.user_code = response['user_code'] self.timeout = int(response['expires_in']) self.getControl(CONTROLS.CODE).setLabel(self.user_code) for tick in range(self.timeout, 0, -1): if tick == 0 or self._abort: break width = (float(tick) / self.timeout) * 596 self.getControl(CONTROLS.PROGRESS).setWidth(int(width)) if (tick % 5) == 0: r = poll_credentials(self.device_code) if r: client_id = r['client_id'] client_secret = r['client_secret'] token = request_token(client_id, client_secret, self.device_code) kodi.set_setting('realdebrid_client_id', client_id, addon_id='script.module.scrapecore') kodi.set_setting('realdebrid_client_secret', client_secret, addon_id='script.module.scrapecore') kodi.set_setting('realdebrid_token', token['access_token'], addon_id='script.module.scrapecore') kodi.set_setting('realdebrid_refresh_token', token['refresh_token'], addon_id='script.module.scrapecore') kodi.notify("RealDebrid Authorization", "Success!") self._close() return kodi.sleep(1000) self.close()
def download(url, addon_id, destination, unzip=False, quiet=False): version = None filename = addon_id + '.zip' r = requests.get(url, stream=True) kodi.log("Download: %s" % url) if r.status_code == requests.codes.ok: temp_file = kodi.vfs.join(kodi.get_profile(), "downloads") if not kodi.vfs.exists(temp_file): kodi.vfs.mkdir(temp_file, recursive=True) temp_file = kodi.vfs.join(temp_file, filename) try: total_bytes = int(r.headers["Content-Length"]) except: total_bytes = 0 block_size = 1000 cached_bytes = 0 if not quiet: pb = xbmcgui.DialogProgress() pb.create("Downloading",filename,' ', ' ') kodi.sleep(150) start = time.time() with open(temp_file, 'wb') as f: for block in r.iter_content(chunk_size=block_size): if not block: break if not quiet and pb.iscanceled(): raise downloaderException('Download Aborted') return False cached_bytes += len(block) f.write(block) if total_bytes > 0: delta = int(time.time() - start) if delta: bs = int(cached_bytes / (delta)) else: bs = 0 if not quiet: percent = int(cached_bytes * 100 / total_bytes) pb.update(percent, "Downloading",filename, format_status(cached_bytes, total_bytes, bs)) if not quiet: pb.close() if unzip: zip_ref = zipfile.ZipFile(temp_file, 'r') zip_ref.extractall(destination) zip_ref.close() kodi.vfs.rm(temp_file, quiet=True) try: xml = kodi.vfs.read_file(kodi.vfs.join(destination, kodi.vfs.join(addon_id, 'addon.xml')), soup=True) version = get_version_by_xml(xml) if not version: version = get_version_by_name(filename) except: kodi.log("Unable to fine version from addon.xml for addon: %s" % addon_id) else: kodi.vfs.mv(temp_file, kodi.vfs.join(destination, filename)) else: kodi.close_busy_dialog() raise downloaderException(r.status_code) return version
def premiumize_resolver(raw_url): resolved_url = '' if kodi.get_setting('premiumize_enable', ADDON_ID) != 'true': return resolved_url attempt = 0 attempts = 5 try: response = premiumize.create_transfer(raw_url) id = response['id'] except: premiumize.clear_transfers() response = premiumize.create_transfer(raw_url) id = response['id'] try: while attempt < attempts: folder_id = False file_id = False target_folder_id = False kodi.log("Resolve Attempt %s" % attempt) temp = premiumize.list_transfers() for t in temp['transfers']: if t['id'] == id and t['status'] == 'finished': if 'target_folder_id' in t: target_folder_id = t['target_folder_id'] if 'folder_id' in t: folder_id = t['folder_id'] if 'file_id' in t: file_id = t['file_id'] break if file_id: response = premiumize.item_details(file_id) resolved_url = response['stream_link'] kodi.set_property('Playback.Resolver', 'premiumize') kodi.set_property('Playback.ID', file_id) return resolved_url if folder_id: response = premiumize.list_folder(folder_id) resolved_url = premiumize.get_folder_stream(response) kodi.set_property('Playback.Resolver', 'premiumize') kodi.set_property('Playback.ID', folder_id) return resolved_url if target_folder_id: response = premiumize.list_folder(target_folder_id) resolved_url = premiumize.get_folder_stream(response) kodi.set_property('Playback.Resolver', 'premiumize') kodi.set_property('Playback.ID', target_folder_id) return resolved_url attempt += 1 kodi.sleep(150) except: pass return resolved_url
def resolve_url(self, raw_url): resolved_url = '' kodi.open_busy_dialog() hash = self.get_hash_from_magnet(raw_url) if not hash: hash = self.get_hash_from_url(raw_url) if hash: kodi.set_property('Playback.Hash', hash) else: kodi.clear_property('Playback.Hash') attempt = 0 attempts = 5 try: response = premiumize.create_transfer(raw_url) id = response['id'] except: premiumize.clear_transfers() response = premiumize.create_transfer(raw_url) id = response['id'] try: while attempt < attempts: folder_id = False file_id = False target_folder_id = False kodi.log("Resolve Attempt %s" % attempt) temp = premiumize.list_transfers() for t in temp['transfers']: if t['id'] == id and t['status'] == 'finished': target_folder_id = t['target_folder_id'] folder_id = t['folder_id'] file_id = t['file_id'] break if folder_id: response = premiumize.list_folder(folder_id) resolved_url = premiumize.get_folder_stream(response) break if target_folder_id: response = premiumize.list_folder(target_folder_id) resolved_url = premiumize.get_folder_stream(response) break attempt += 1 kodi.sleep(150) except: kodi.close_busy_dialog() kodi.close_busy_dialog() return resolved_url
def process_response(self, url, response, cache_limit, request_args, request_kwargs): if 'page' in request_kwargs['query']: page = request_kwargs['query']['page'] + 1 else: page = 1 results = response.json() total_count = float(results['total_count']) page_count = int(math.ceil(total_count / page_limit)) if page_count > 1 and page == 1: results = response.json() for p in range(page+1, int(page_count+1)): kodi.sleep(500) request_kwargs['query']['page'] = p temp = self.request(*request_args, **request_kwargs) results['items'] += temp['items'] self.cache_response(url, json.dumps(results), cache_limit) return results self.cache_response(url, response.text, cache_limit) return self.get_content(self.get_response(response))
def handel_error(self, error, response, request_args, request_kwargs): if response.status_code == 401: traceback.print_stack() kodi.close_busy_dialog() raise githubException("Unauthorized: %s" % error) elif response.status_code == 403 and 'X-RateLimit-Reset' in response.headers: import time retry = int(response.headers['X-RateLimit-Reset']) - int(time.time()) for delay in range(retry, 0, -1): kodi.notify("API Rate limit exceeded", "Retry in %s seconds(s)" % delay, timeout=1000) kodi.sleep(1000) return self.request(*request_args, **request_kwargs) elif response.status_code == 422 and 'Only the first 1000' in response.text: kodi.handel_error('Result count exceeds API limit.', 'Try different search or result filter.') kodi.close_busy_dialog() traceback.print_stack() else: kodi.close_busy_dialog() traceback.print_stack() raise githubException("Status %s: %s" % (response.status_code, response.text))
def realdebrid_resolver(raw_url): resolved_url = '' if kodi.get_setting('realdebrid_enable', ADDON_ID) != 'true': return resolved_url response = realdebrid.add_torrent(raw_url) try: torrent_id = response['id'] info = realdebrid.get_torrent_info(torrent_id) file_id = realdebrid.get_stream_file(info['files']) if not file_id: return realdebrid.select_torrent_files(torrent_id, file_id) kodi.sleep(500) info = realdebrid.get_torrent_info(torrent_id) raw_url = info['links'][0] kodi.set_property('Playback.Resolver', 'realdebrid') kodi.set_property('Playback.ID', torrent_id) resolved_url = realdebrid.resolve_url(raw_url) except: pass return resolved_url
def format_results(self, results): if self.abort_event.is_set(): self.PB.close() return [] self.PB.update_subheading('Processing Results', '') self.PB.update_subheading('Processing Results', 'Formating Results...') regex_host = re.compile("\.\S+$") for r in results: service = r['service'].upper() host = format_color(regex_host.sub("", r['host'].upper()), 'darkred') #if r['cached']: host += " " + format_color('*', 'yellow') attribs = [service, host] if r['size']: r['size_sort'] = int(r['size']) else: r['size_sort'] = 0 if r['size']: attribs += [format_color(format_size(r['size']), 'blue')] if r['title']: attribs += [r['title']] display = ' | '.join(attribs) self.results[results.index(r)]['display'] = display kodi.sleep(250) self.PB.update_subheading('Processing Results', 'Removing Duplicates...') kodi.sleep(250) self.results = {v['raw_url']: v for v in results}.values() self.PB.update_subheading('Processing Results', 'Sorting...') kodi.sleep(250) results.sort(reverse=True, key=lambda k: (k['quality'], k['size_sort'])) self.PB.close() self.abort_event.set() return results
def start(self): enable_updates class Monitor(xbmc.Monitor): def onSettingsChanged(self): global enable_updates enable_updates = kodi.get_setting('enable_updates') == 'true' monitor = Monitor() kodi.log("Service Starting...") if is_depricated: while not xbmc.abortRequested: kodi.sleep(1000) self.update() else: while not monitor.abortRequested(): if monitor.waitForAbort(10): break self.update() self.shutdown()
def authorize(): PB = kodi.ProgressBar() PB.new("Authorize RealDebrid: https://real-debrid.com/device", 600) response = request_code() device_code = response['device_code'] user_code = response['user_code'] timeout = response['expires_in'] PB.update_subheading("Enter Code: %s" % user_code, "%s sec(s)" % 600) for tick in range(600, 0, -1): if PB.is_canceled(): return percent = int((tick / 600.0) * 100) PB.update_subheading("Enter Code: %s" % user_code, "%s sec(s) remaining" % tick, percent=percent) if (tick % 5) == 0: r = poll_credentials(device_code) if r: client_id = r['client_id'] client_secret = r['client_secret'] token = request_token(client_id, client_secret, device_code) kodi.set_setting('realdebrid_client_id', client_id, addon_id='script.module.scrapecore') kodi.set_setting('realdebrid_client_secret', client_secret, addon_id='script.module.scrapecore') kodi.set_setting('realdebrid_token', token['access_token'], addon_id='script.module.scrapecore') kodi.set_setting('realdebrid_refresh_token', token['refresh_token'], addon_id='script.module.scrapecore') PB.close() kodi.notify("RealDebrid Authorization", "Success!") return kodi.sleep(1000)
def process_response(self, url, response, cache_limit, request_args, request_kwargs): if "page" in request_kwargs["query"]: page = request_kwargs["query"]["page"] + 1 else: page = 1 results = response.json() try: total_count = float(results["total_count"]) page_count = int(math.ceil(total_count / page_limit)) except: total_count = page_count = 1 if page_count > 1 and page == 1: results = response.json() for p in range(page + 1, int(page_count + 1)): kodi.sleep(500) request_kwargs["query"]["page"] = p temp = self.request(*request_args, **request_kwargs) results["items"] += temp["items"] self.cache_response(url, json.dumps(results), cache_limit) return results self.cache_response(url, response.text, cache_limit) return self.get_content(self.get_response(response))
def __init__(self, addon_id, url, full_name, destination, master=False, quiet=False, installed_list=[], batch=False): self.installed_list = installed_list self.quiet = quiet self.batch=batch if not self.quiet: kodi.open_busy_dialog() v = kodi.get_kodi_version() # Grab a list of KNOWN addons from the database. Unfortunately Jarvis requires direct database access for the installed flag if v >= 17: response = kodi.kodi_json_request("Addons.GetAddons", { "installed": False, "properties": ["path", "dependencies"]}) for a in response['result']['addons']: self.available_addons += [a['addonid']] self.source_table[a['addonid']] = a['path'] else: from sqlite3 import dbapi2 dbf = kodi.vfs.join("special://profile/Database", "Addons20.db") with dbapi2.connect(dbf) as dbh: dbc = dbh.cursor() dbc.execute("SELECT addon.addonID, broken.addonID is Null AS enabled, addon.path FROM addon LEFT JOIN broken on addon.addonID=broken.addonID WHERE enabled=1") for a in dbc.fetchall(): self.available_addons += [a[0]] self.source_table[a[0]] = a[2] dbh.close() self._addon_id = addon_id self._url = url self._full_name = full_name self._user, self.repo = full_name.split("/") self._master = master self._destination = destination # Add the final addon target to the sources list with type of zip # Initiate install routine self.install_addon(addon_id, url, full_name, master) completed = list(reversed(self.completed)) if not quiet: pb = kodi.ProgressBar() pb.new('Enabling Addons', len(completed)+1) pb.update_subheading('Building Addon List') kodi.run_command("XBMC.UpdateLocalAddons()") kodi.sleep(500) for addon_id in completed: if not quiet: #percent = 100* (completed.index(addon_id) / len(completed)) #pb.update(percent, "Enabling Addons", addon_id, '') pb.next(addon_id) kodi.sleep(100) self.enable_addon(addon_id) if not quiet: pb.next("Looking for Updates", "") kodi.sleep(500) kodi.run_command('XBMC.UpdateAddonRepos') # Enable installed addons if not self.quiet: pb.close() kodi.close_busy_dialog() if self.install_error: kodi.notify("Install failed", self._addon_id) else: kodi.notify("Install complete", self._addon_id)
class FanartService(): def clear_art(self): kodi.log("Clearing Bad Art...") DB_FILE = kodi.vfs.join("special://database", 'Textures13.db') BAD_FILE = kodi.vfs.join(kodi.get_profile(), 'badart.log') bad_files = list(set(kodi.vfs.read_file(BAD_FILE).split("\n"))) with database.connect(DB_FILE, check_same_thread=False) as dbh: dbc = dbh.cursor() for bad_file in bad_files: if not bad_file: continue f = kodi.vfs.join(bad_file[0], bad_file) + '.jpg' dbc.execute("DELETE FROM texture WHERE cachedurl=?", [f]) f = kodi.vfs.join("special://thumbnails", f) kodi.vfs.rm(f, quiet=True) dbh.commit() kodi.vfs.write_file(BAD_FILE, '') def start(self): class Monitor(xbmc.Monitor): def onSettingsChanged(self): pass monitor = Monitor() kodi.log("Service Starting...") self.clear_art() if kodi.get_setting('enable_fanart_proxy') == 'true': CONTROL_PORT = int(kodi.get_setting('control_port')) if kodi.get_setting('network_bind') == 'Localhost': address = "127.0.0.1" else: address = "0.0.0.0" CONTROL_PROTO = kodi.get_setting('control_protocol') kodi.log("Launching Fanart WebInterface on: %s://%s:%s" % (CONTROL_PROTO, address, CONTROL_PORT)) kodi.log("Verify at: %s://%s:%s/api/up" % (CONTROL_PROTO, address, CONTROL_PORT)) try: if CONTROL_PROTO == 'https': from commoncore.webservice import HttpsServer certfile = kodi.vfs.join(kodi.get_path(), "resources/server.pem") self.httpd = HttpsServer(address, CONTROL_PORT, certfile, RequestHandler) self.webserver = Thread(target=self.httpd.serve_forever) else: from commoncore.webservice import HttpServer self.httpd = HttpServer(address, CONTROL_PORT, RequestHandler) self.webserver = Thread(target=self.httpd.serve_forever) except Exception, e: kodi.log(e) kodi.raise_error("Service Error: ", str(e)) sys.exit() self.webserver.start() if is_depricated: while not xbmc.abortRequested: kodi.sleep(1000) else: while not monitor.abortRequested(): if monitor.waitForAbort(1): break self.shutdown()
def download(url, addon_id, destination, unzip=False, quiet=False): version = None filename = addon_id + '.zip' r = requests.get(url, stream=True) kodi.log("Download: %s" % url) if r.status_code == requests.codes.ok: temp_file = kodi.vfs.join(kodi.get_profile(), "downloads") if not kodi.vfs.exists(temp_file): kodi.vfs.mkdir(temp_file, recursive=True) temp_file = kodi.vfs.join(temp_file, filename) try: total_bytes = int(r.headers["Content-Length"]) except: total_bytes = 0 block_size = 1000 cached_bytes = 0 if not quiet: pb = xbmcgui.DialogProgress() pb.create("Downloading", filename, ' ', ' ') kodi.sleep(150) start = time.time() with open(temp_file, 'wb') as f: for block in r.iter_content(chunk_size=block_size): if not block: break if not quiet and pb.iscanceled(): raise downloaderException('Download Aborted') return False cached_bytes += len(block) f.write(block) if total_bytes > 0: delta = int(time.time() - start) if delta: bs = int(cached_bytes / (delta)) else: bs = 0 if not quiet: percent = int(cached_bytes * 100 / total_bytes) pb.update(percent, "Downloading", filename, format_status(cached_bytes, total_bytes, bs)) if not quiet: pb.close() if unzip: zip_ref = zipfile.ZipFile(temp_file, 'r') zip_ref.extractall(destination) zip_ref.close() kodi.vfs.rm(temp_file, quiet=True) try: xml = kodi.vfs.read_file(kodi.vfs.join( destination, kodi.vfs.join(addon_id, 'addon.xml')), soup=True) version = get_version_by_xml(xml) if not version: version = get_version_by_name(filename) except: kodi.log( "Unable to fine version from addon.xml for addon: %s" % addon_id) else: kodi.vfs.mv(temp_file, kodi.vfs.join(destination, filename)) else: kodi.close_busy_dialog() raise downloaderException(r.status_code) return version
def prepair_request(self): kodi.sleep(random.randint(100, 250)) # random delay 50-250 ms
def install_batch(): import xbmcgui from libs import github from libs import github_installer if kodi.mode == 'install_batch': url = kodi.arg('url') xml, zip_ref = github.batch_installer(url) else: url = kodi.dialog_file_browser('Select a install file', mask='.zip') if not github.re_installer.search(url): return xml, zip_ref = github.batch_installer(url, True) if not kodi.dialog_confirm('Batch Installer?', "Click YES to proceed.", "This will install a list of addons.", "Some configuration files and settings may be overwritten."): return if not xml: return # Install each addon as instructed installed_list = [] count = 0 for a in xml.findAll('addon'): count +=1 PB = kodi.ProgressBar() PB.new('Batch Installer - Progress', count) for a in xml.findAll('addon'): addon_id = a.find('addon_id') username = a.find('username') if addon_id is None or username is None: continue username = username.text addon_id = addon_id.text PB.next(addon_id) if not kodi.get_condition_visiblity("System.HasAddon(%s)"% addon_id): if PB.is_canceled(): return kodi.log("Batch install " + addon_id) url, filename, full_name, version = github.find_zip(username, addon_id) installed_list += github_installer.GitHub_Installer(addon_id, url, full_name, kodi.vfs.join("special://home", "addons"), quiet=True, batch=True, installed_list=installed_list).installed_list kodi.sleep(1000) # Look for config files. # Need to add error checking for missing config files configs= xml.find('configs') if configs is not None and 'dir' in configs.attrs[0]: config_dir = configs['dir'] for config in configs.findAll('config'): source = config.find('source') destination = config.find('destination') if source is None or destination is None: continue source = source.text destination = destination.text if not kodi.vfs.exists(destination): kodi.vfs.mkdir(destination, True) kodi.vfs.write_file(kodi.vfs.join(destination, source), zip_ref.read(config_dir + '/' + source)) # Now look for individual setting key and value pairs # Set them as instructed settings= xml.find('settings') if settings is not None: for setting in settings.findAll('setting'): if 'addon_id' in setting.attrs[0]: addon_id = setting['addon_id'] k = setting.find('key') v = setting.find('value') if k is None or v is None: continue kodi.set_setting(k.text, v.text, addon_id) builtins= xml.find('builtins') if builtins is not None: for cmd in builtins.findAll('command'): cmd = cmd.text kodi.run_command(cmd) jsonrpc= xml.find('jsonrpc') if jsonrpc is not None: from ast import literal_eval for cmd in jsonrpc.findAll('command'): method = cmd.find('method').text params = literal_eval(cmd.find('params').text) id = cmd.find('id').text kodi.kodi_json_request(method, params, id) # Now clean up zip_ref.close() PB.close() r = kodi.dialog_confirm(kodi.get_name(), 'Click Continue to install more addons or', 'Restart button to finalize addon installation', yes='Restart', no='Continue') if r: import sys import xbmc if sys.platform in ['linux', 'linux2', 'win32']: xbmc.executebuiltin('RestartApp') else: xbmc.executebuiltin('ShutDown')
def download(url, full_name, addon_id, destination, unzip=False, quiet=False, verify_hash=True): version = None filename = addon_id + ".zip" r = requests.get(url, stream=True) kodi.log("Download: %s" % url) if r.status_code == requests.codes.ok: temp_file = kodi.vfs.join(kodi.get_profile(), "downloads") if not kodi.vfs.exists(temp_file): kodi.vfs.mkdir(temp_file, recursive=True) temp_file = kodi.vfs.join(temp_file, filename) try: total_bytes = int(r.headers["Content-Length"]) except: total_bytes = 0 block_size = 1000 cached_bytes = 0 if not quiet: pb = xbmcgui.DialogProgress() pb.create("Downloading", filename, " ", " ") kodi.sleep(150) start = time.time() is_64bit = sys.maxsize > 2**32 with open(temp_file, "wb") as f: for chunk in r.iter_content(chunk_size=block_size): if chunk: if not quiet and pb.iscanceled(): raise downloaderException("Download Aborted") return False cached_bytes += len(chunk) shutil.copyfileobj(functionIO(chunk), f, 8096) if total_bytes > 0: delta = int(time.time() - start) if delta: bs = int(cached_bytes / (delta)) else: bs = 0 if not quiet: percent = int(cached_bytes * 100 / total_bytes) pb.update( percent, "Downloading", filename, format_status(cached_bytes, total_bytes, bs), ) if not quiet: pb.close() if verify_hash: local_sha = hash_func(temp_file, "sha1") remote_sha = get_sha(full_name, url) if remote_sha != local_sha: kodi.close_busy_dialog() kodi.handel_error("Download Error", "Checksum mismatch!") if unzip: if is_64bit: zip_ref = zipfile.ZipFile(temp_file, "r") else: with open(temp_file, "rb") as zip_file: zip_ref = zip_file.ZipFile(functionIO(zip_file.read())) zip_ref.extractall(destination) zip_ref.close() kodi.vfs.rm(temp_file, quiet=True) try: xml = kodi.vfs.read_file( kodi.vfs.join(destination, kodi.vfs.join(addon_id, "addon.xml")), soup=True, ) version = get_version_by_xml(xml) if not version: version = get_version_by_name(filename) except: kodi.log( "Unable to fine version from addon.xml for addon: %s" % addon_id) else: kodi.vfs.mv(temp_file, kodi.vfs.join(destination, filename)) else: kodi.close_busy_dialog() raise downloaderException(r.status_code) return version