def request(url, params={}, headers={}, data=None, method=None): if params: url = "".join([url, "?", urllib_parse.urlencode(params)]) req = urllib_request.Request(url) if method: req.get_method = lambda: method req.add_header("User-Agent", USER_AGENT) req.add_header("Accept-Encoding", "gzip") for k, v in headers.items(): req.add_header(k, v) if data: req.data = data try: with closing(urllib_request.urlopen(req)) as response: data = response.read() if response.headers.get("Content-Encoding", "") == "gzip": import zlib data = zlib.decompressobj(16 + zlib.MAX_WBITS).decompress(data) response.data = data response.json = lambda: parse_json(data) response.xml = lambda: parse_xml(data) return response except Exception as e: import traceback map(log.error, traceback.format_exc().split("\n")) notify("%s: %s" % (getLocalizedString(30224), repr(e).encode('utf-8'))) return None, None
def elementumd_thread(monitor): crash_count = 0 try: while not xbmc.abortRequested: log.info("elementumd: starting elementumd") proc = start_elementumd(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 Elementum 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 if proc.returncode == 5: notify(getLocalizedString(30332), time=3000) else: 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
def AddonFailure(self, addonId): if ADDON.getSetting("provider_disable_failing") == u"false": return 0 if addonId in self._failures: self._failures[addonId] += 1 else: self._failures[addonId] = 1 log.warning("Recorded failure %d for %s" % (self._failures[addonId], addonId)) if self._failures[addonId] > int(ADDON.getSetting("provider_allowed_failures")): try: time.sleep(10) notify(getLocalizedString(30111)) urllib_request.urlopen("%s/provider/%s/disable" % (ELEMENTUMD_HOST, addonId)) except: notify(getLocalizedString(30112)) return 0 return self._failures[addonId]
def register(search, search_movie, search_episode, search_season=None): try: payload = json.loads(base64.b64decode(sys.argv[1])) except: notify(getElementumLocalizedString(30102), time=1000) return results = () method = { "search": search, "search_movie": search_movie, "search_season": search_season, "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: import traceback map(log.error, traceback.format_exc().split("\n")) notify( py2_encode( "%s: %s" % (getElementumLocalizedString(30224), repr(e)), 'utf-8')) try: urllib_request.urlopen("%s/provider/%s/failure" % (ELEMENTUMD_HOST, ADDON_ID)) except: pass finally: try: req_data = json.dumps(results) if not PY2 and isinstance(req_data, str): req_data = req_data.encode() req = urllib_request.Request(payload["callback_url"], data=req_data) with closing(urllib_request.urlopen(req)) as response: log.debug("callback returned: %d" % response.getcode()) except Exception as e: import traceback map(log.error, traceback.format_exc().split("\n")) notify( py2_encode( "%s: %s" % (getElementumLocalizedString(30224), repr(e)), 'utf-8')) try: urllib_request.urlopen("%s/provider/%s/failure" % (ELEMENTUMD_HOST, ADDON_ID)) except: pass
def elementumd_thread(monitor): restart_count = 0 max_restart = 3 last_code = 0 try: monitor_abort = xbmc.Monitor() # For Kodi >= 14 while not monitor_abort.abortRequested(): # If we ran out of attempts of last exit code was '-9': we do not try to start it again. # So if you kill the binary with '-9': it will not be restarted by this monitor. if restart_count > max_restart or last_code == -9: if monitor.reboot(): log.debug("elementumd: resetting attempts") restart_count = 0 last_code = 0 monitor.reboot(False) else: time.sleep(5) continue log.info("elementumd: starting elementumd") proc = None if hasSubprocess: proc = start_elementumd(stdout=subprocess.PIPE, stderr=subprocess.STDOUT) if not proc: break else: log.info( "elementumd: current system is unable to run the binary") break threading.Thread(target=wait_for_abortRequested, args=[proc, monitor]).start() if not hasSubprocess: break if binary_platform["os"] == "windows": while proc.poll() is None: log.info(toUtf8(proc.stdout.readline())) else: # Kodi hangs on some Android (sigh...) systems when doing a blocking # read. We count on the fact that Elementum 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 try: log.info(toUtf8(line)) except TypeError: pass except IOError: time.sleep(1) # nothing to read, sleep last_code = proc.returncode if monitor_abort.abortRequested(): break if proc.returncode == 0 or proc.returncode == -9 or proc.returncode == -1: continue if proc.returncode == 5: restart_count = 0 notify(getLocalizedString(30332), time=3000) else: restart_count += 1 notify(getLocalizedString(30100), time=3000) xbmc.executebuiltin("Dialog.Close(all, true)") system_information() time.sleep(5) if restart_count >= max_restart: log.debug("elementumd: no attempts left") notify(getLocalizedString(30110), time=3000) continue 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 log.debug("elementumd: closing")
def Notify(self, header, message, image): return notify(getLocalizedLabel(message), header, 3000, image)
def run(url_suffix=""): socket.setdefaulttimeout(int(ADDON.getSetting("buffer_timeout"))) urllib2.install_opener(urllib2.build_opener(NoRedirectHandler())) # Pause currently playing Elementum file to avoid doubling requests if xbmc.Player().isPlaying() and ADDON_ID in xbmc.Player().getPlayingFile( ): xbmc.Player().pause() url = sys.argv[0].replace("plugin://%s" % ADDON_ID, ELEMENTUMD_HOST + url_suffix) + sys.argv[2] if len(sys.argv) > 3: app = sys.argv[3].replace(":", "=") # Replacing resume=false with resume=true if item is launched from main window title = xbmc.getInfoLabel('ListItem.Title') label = xbmc.getInfoLabel('ListItem.Label') if "resume" in app and not title and not label: app = app.replace("resume=false", "resume=true") if "?" in url: url += "&" + app else: url += "?" + app log.debug("Requesting %s from %s" % (url, repr(sys.argv))) try: data = _json(url) except urllib2.URLError as e: if 'Connection refused' in e.reason: notify(getLocalizedString(30116), time=7000) else: import traceback map(log.error, traceback.format_exc().split("\n")) notify(e.reason, time=7000) return except Exception as e: import traceback map(log.error, traceback.format_exc().split("\n")) try: msg = unicode(e) except: try: msg = str(e) except: msg = repr(e) notify(getLocalizedLabel(msg), time=7000) return if not data: return if data["content_type"]: content_type = data["content_type"] if data["content_type"].startswith("menus"): content_type = data["content_type"].split("_")[1] xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_UNSORTED) if content_type != "tvshows": xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE) else: xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_TITLE_IGNORE_THE) xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_DATE) xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_GENRE) xbmcplugin.setContent(HANDLE, content_type) listitems = range(len(data["items"])) for i, item in enumerate(data["items"]): # Translate labels if item["label"][0:8] == "LOCALIZE": item["label"] = unicode(getLocalizedLabel(item["label"]), 'utf-8') 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"]) elif ADDON.getSetting( 'default_fanart') == 'true' and item["label"] != unicode( getLocalizedString(30218), 'utf-8'): fanart = os.path.join(ADDON_PATH, "fanart.png") listItem.setArt({'fanart': fanart}) 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"]) if viewMode: 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)
def run(url_suffix="", retry=0): if '/restart/' in sys.argv[0]: restart_signal() return try: buffer_timeout = int(ADDON.getSetting("buffer_timeout")) if buffer_timeout < 60: buffer_timeout = 60 except: buffer_timeout = 60 buffer_timeout = buffer_timeout * 2 try: preload_timeout = int(ADDON.getSetting("preload_timeout")) if preload_timeout < 1: preload_timeout = 1 except: preload_timeout = 1 socket.setdefaulttimeout(buffer_timeout) opener = urllib_request.build_opener(NoRedirectHandler()) opener.addheaders = [('User-Agent', ADDON_ID)] urllib_request.install_opener(opener) # Pause currently playing Elementum file to avoid doubling requests try: if xbmc.Player().isPlaying() and ADDON_ID in xbmc.Player( ).getPlayingFile(): xbmc.Player().pause() except: pass url = sys.argv[0].replace("plugin://%s" % ADDON_ID, ELEMENTUMD_HOST + url_suffix) + sys.argv[2] query_add = "" if len(sys.argv) > 3: query_add = sys.argv[3].replace(":", "=") if query_add and "resume=" not in url: query_add = query_add.replace("resume=", "doresume=") if "?" in url: url += "&" + query_add else: url += "?" + query_add log.debug("Requesting %s from %s" % (url, repr(sys.argv))) try: data = _json(url) except PlayerException as e: redirect_url = e.__str__() log.debug("Launching player with %s" % (redirect_url)) xbmcplugin.endOfDirectory(HANDLE, succeeded=True) xbmc.sleep(500) xbmc.executeJSONRPC( '{"jsonrpc":"2.0","method":"Player.Open","params":{"item":{"file":"%s"}},"id":"1"}' % (redirect_url)) return except RedirectException as e: redirect_url = e.__str__() log.debug("Redirecting Kodi with %s" % (redirect_url)) xbmcplugin.endOfDirectory(HANDLE, succeeded=True) xbmc.sleep(500) if "keyboard=1" in sys.argv[0]: xbmc.executebuiltin('Container.Update(%s,replace)' % (redirect_url)) else: xbmc.executebuiltin('Container.Update(%s)' % (redirect_url)) return except urllib_error.URLError as e: # We can retry the request if connection is refused. # For example when plugin has not yet started but is requested by someone. if retry <= 2: time.sleep(preload_timeout) return run(retry=retry + 1) if isinstance(e.reason, IOError) or isinstance( e.reason, OSError) or 'Connection refused' in e.reason: notify(getLocalizedString(30116), time=7000) else: import traceback map(log.error, traceback.format_exc().split("\n")) notify(e.reason, time=7000) return except Exception as e: import traceback log.debug(traceback.print_exc()) map(log.error, traceback.format_exc().split("\n")) try: msg = six.ensure_text(e.__str__(), errors='ignore') except: try: msg = six.ensure_binary(e.__str__(), errors='ignore') except: msg = repr(e) notify(getLocalizedLabel(msg), time=7000) return if not data: return if data["content_type"]: content_type = data["content_type"] if data["content_type"].startswith("menus"): content_type = data["content_type"].split("_")[1] xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_UNSORTED) if content_type != "tvshows": xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_LABEL_IGNORE_THE) else: xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_TITLE_IGNORE_THE) xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_DATE) xbmcplugin.addSortMethod(HANDLE, xbmcplugin.SORT_METHOD_GENRE) xbmcplugin.setContent(HANDLE, content_type) listitems = list(range(len(data["items"]))) for i, item in enumerate(data["items"]): # Translate labels if "LOCALIZE" in item["label"]: if item["label"][0:8] == "LOCALIZE": item["label"] = getLocalizedLabel(item["label"]) else: item["label"] = getLocalizedText(item["label"]) if isinstance(item["label"], str): item["label"] = six.ensure_text(item["label"], 'utf-8') if "LOCALIZE" in item["label2"]: if item["label2"][0:8] == "LOCALIZE": item["label2"] = getLocalizedText(item["label2"]) else: item["label2"] = getLocalizedText(item["label2"]) if isinstance(item["label2"], str): item["label2"] = six.ensure_text(item["label2"], 'utf-8') if PLATFORM['kodi'] >= 19: listItem = xbmcgui.ListItem(label=item["label"], label2=item["label2"]) listItem.setArt({'icon': item["icon"]}) listItem.setArt({'thumb': item["thumbnail"]}) else: listItem = xbmcgui.ListItem(label=item["label"], label2=item["label2"], iconImage=item["icon"], thumbnailImage=item["thumbnail"]) try: if item.get("castmembers") and PLATFORM['kodi'] >= 17: listItem.setCast(item.get("castmembers")) if item.get("info"): item["info"] = normalizeLabels(item["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"): if "fanarts" in item.get("art") and item.get("art")["fanarts"]: try: start = 0 fanart_list = [] for fa in item.get("art")["fanarts"]: start += 1 item.get("art")["fanart{}".format(start)] = fa fanart_list.append({'image': fa}) if PLATFORM['kodi'] >= 18: listItem.setAvailableFanart(fanart_list) except Exception as e: log.warning( "Could not initialize ListItem.Art (%s): %s" % (repr(item.get("art")), repr(e))) pass del item.get("art")["fanarts"] listItem.setArt(item["art"]) elif ADDON.getSetting('default_fanart') == 'true' and item[ "label"] != six.ensure_text(getLocalizedString(30218), 'utf-8'): fanart = os.path.join(ADDON_PATH, "fanart.jpg") listItem.setArt({'fanart': fanart}) 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]) if PLATFORM['kodi'] >= 19 and menu[1][0:5] == "XBMC.": menu[1] = menu[1][5:] 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) except Exception as e: log.warning("Could not initialize ListItem (%s): %s" % (repr(item.get("info")), repr(e))) 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"]) if viewMode: 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)
def get_elementum_binary(): global binary_platform binary_platform = get_platform() binary = "elementum" + (binary_platform["os"] == "windows" and ".exe" or "") binary_dir = os.path.join(ADDON_PATH, "resources", "bin", "%(os)s_%(arch)s" % binary_platform) if binary_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 changed binary folder for Android: %s" % binary_dir) app_id = android_get_current_appid() xbmc_data_path = os.path.join("/data", "data", app_id) if not os.path.exists(xbmc_data_path): log.info("%s path does not exist, so using %s as xbmc_data_path" % (xbmc_data_path, translatePath("special://xbmcbin/"))) xbmc_data_path = translatePath("special://xbmcbin/") if not os.path.exists(xbmc_data_path): log.info( "%s path does not exist, so using %s as xbmc_data_path" % (xbmc_data_path, translatePath("special://masterprofile/"))) xbmc_data_path = translatePath("special://masterprofile/") dest_binary_dir = os.path.join(xbmc_data_path, "files", ADDON_ID, "bin", "%(os)s_%(arch)s" % binary_platform) else: try: dest_binary_dir = os.path.join( translatePath(ADDON.getAddonInfo("profile")).decode('utf-8'), "bin", "%(os)s_%(arch)s" % binary_platform) except Exception: dest_binary_dir = os.path.join( translatePath(ADDON.getAddonInfo("profile")), "bin", "%(os)s_%(arch)s" % binary_platform) binary_path = os.path.join(binary_dir, binary) dest_binary_path = os.path.join(dest_binary_dir, binary) installed_version = read_current_version(binary_dir) log.info("Binary detection. Version: %s, Source: %s, Destination: %s" % (installed_version, binary_path, dest_binary_path)) if not os.path.exists( binary_path ) or not installed_version or ADDON_VERSION > installed_version: try: os.makedirs(binary_dir) except OSError as e: if e.errno != errno.EEXIST: raise notify("LOCALIZE[30670];;" + "%(os)s_%(arch)s" % binary_platform) if not download_github_folder( repo, "%(os)s_%(arch)s" % binary_platform, binary_dir): log.error("Unable to download binary to destination path") system_information() return False, False version = download_current_version(repo) write_current_version(binary_dir, version) if not os.path.exists(binary_path): # notify((getLocalizedString(30103) + " %(os)s_%(arch)s" % PLATFORM), time=7000) dialog_ok("LOCALIZE[30347];;" + "%(os)s_%(arch)s" % binary_platform) 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 not os.path.exists( binary_path) or get_elementumd_checksum( dest_binary_path) != get_elementumd_checksum( binary_path) or not filecmp.cmp( dest_binary_path, binary_path, shallow=True): log.info("Updating elementum 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() pass 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 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) log.info("Binary detection: [ Source: %s, Destination: %s ]" % (binary_path, dest_binary_path)) return dest_binary_dir, ensure_exec_perms(dest_binary_path)