def download_github_folder(repo, folder, destination): contents_url = "https://api.github.com/repos/%s/contents/%s" % (repo, folder) log.info("Downloading repo content for folder: %s, to folder: %s" % (contents_url, destination)) try: with requests.get(contents_url) as r: lists = json.loads(r.content, parse_int=str) downloaded = 0 for i in lists: if 'download_url' not in i: continue dest = os.path.join(destination, urllib_parse.unquote(i['download_url'].rsplit('/', 1)[1])) log.info("Downloading file '%s' to '%s'" % (i['download_url'], dest)) with requests.get(i['download_url'], stream=True) as rd: rd.raise_for_status() with open(dest, 'wb') as f: for chunk in rd.iter_content(chunk_size=8192): f.write(chunk) downloaded += 1 return downloaded > 0 except Exception as e: log.error("Could not get list of files from github: %s" % e) raise
def _dispatch_delete(self, objectname): try: self._objects[objectname]._shutdown() except Exception: _log.error("Error when shutting down the object %s:", type(self._objects[objectname])) _log.debug(traceback.format_exc()) del self._objects[objectname]
def doTraktAction(action): dbid = getDbId() mediatype = getMediaType() if action == "watched": heading = ADDON.getLocalizedString(32018) elif action == "unwatched": heading = ADDON.getLocalizedString(32019) else: heading = "Unsupported action" if not dbid.isdigit(): showtmdbid = xbmc.getInfoLabel('ListItem.Property(ShowTMDBId)') try: tmdbID = sys.listitem.getUniqueID('tmdb') except AttributeError: tmdbID = "" if tmdbID == "" or ((mediatype == 'season' or mediatype == 'episode') and showtmdbid == ""): log.error("Could not find TMDB id for %s" % dbid) xbmcgui.Dialog().notification(ADDON.getLocalizedString(32007), ADDON.getLocalizedString(32014), xbmcgui.NOTIFICATION_WARNING, 3000) return xbmcgui.Dialog().notification(heading, sys.listitem.getLabel(), xbmcgui.NOTIFICATION_INFO, 3000) if not dbid.isdigit(): log.info("Make %s non-library item: tmdbID=%s, MediaType=%s" % (action, tmdbID, mediatype)) else: log.info("Make %s library item: DBID=%s, MediaType=%s" % (action, dbid, mediatype)) if not dbid.isdigit(): if mediatype == 'movie': url = "plugin://plugin.video.elementum/movie/%s/%s" % (tmdbID, action) if mediatype == 'tvshow': url = "plugin://plugin.video.elementum/show/%s/%s" % (tmdbID, action) elif mediatype == 'season': season = xbmc.getInfoLabel('ListItem.Season') url = "plugin://plugin.video.elementum/show/%s/season/%s/%s" % ( showtmdbid, season, action) elif mediatype == 'episode': season = xbmc.getInfoLabel('ListItem.Season') episode = xbmc.getInfoLabel('ListItem.Episode') url = "plugin://plugin.video.elementum/show/%s/season/%s/episode/%s/%s" % ( showtmdbid, season, episode, action) else: url = "plugin://plugin.video.elementum/context/media/%s/%s/%s" % ( mediatype, dbid, action) log.info("Starting Elementum with: %s" % url) xbmc.Player().play(url)
def Wait_Window_Loaded(self): tries = 9 while self.Window_Is_Loading(): if tries == 0: log.error("Failed to wait till window has finished loading") return int(False) tries = tries - 1 time.sleep(0.3) log.debug("Finished waiting till window has finished loading") return int(True)
def getTMDBidFromElementumPath(path): """plugin://plugin.video.elementum/movie/628/links/Interview%20with%20the%20Vampire%20%281994%29""" result = re.search(r'plugin://plugin.video.elementum/[^/]+/(\d+)/.*', path) if result: tmdbID = result.group(1) return tmdbID else: log.error("Could not find TMDB id for %s" % path) xbmcgui.Dialog().notification(ADDON.getLocalizedString(32007), ADDON.getLocalizedString(32014), xbmcgui.NOTIFICATION_WARNING, 3000) return ""
def jsonrpc_enabled(notify=False): try: s = socket.socket() s.connect(('127.0.0.1', 9090)) s.close() log.info("Kodi's JSON-RPC service is available, starting up...") del s return True except Exception as e: log.error(repr(e)) if notify: xbmc.executebuiltin("ActivateWindow(ServiceSettings)") dialog = xbmcgui.Dialog() dialog.ok("Elementum", getLocalizedString(30199)) return False
def download_current_version(repo): contents_url = "https://api.github.com/repos/%s/git/refs/tags" % (repo) log.debug("Downloading repo releases: %s" % (contents_url)) try: with requests.get(contents_url) as r: lists = json.loads(r.content, parse_int=str) ref = lists[-1]["ref"] if ref.find('/'): return ref.rsplit('/', 1)[1] return "" except Exception as e: log.error("Could not get list of tags from github: %s" % e) raise
def clear_fd_inherit_flags(): # Ensure the spawned Elementum binary doesn't inherit open files from Kodi # which can break things like addon updates. [WINDOWS ONLY] try: from ctypes import windll HANDLE_RANGE = six.moves.xrange(0, 65536) HANDLE_FLAG_INHERIT = 1 FILE_TYPE_DISK = 1 for hd in HANDLE_RANGE: if windll.kernel32.GetFileType(hd) == FILE_TYPE_DISK: if not windll.kernel32.SetHandleInformation(hd, HANDLE_FLAG_INHERIT, 0): log.error("Error clearing inherit flag, disk file handle %x" % hd) except: pass
def getSeasonNumberFromElementumPath(path): """plugin://plugin.video.elementum/show/1622/season/15/episodes""" season_number = xbmc.getInfoLabel('ListItem.Season') if season_number: return season_number else: result = re.search( r'plugin://plugin.video.elementum/[^/]+/\d+/season/(\d+)/.*', path) if result: season_number = result.group(1) return season_number else: log.error("Could not find season number for %s" % path) xbmcgui.Dialog().notification(ADDON.getLocalizedString(32007), ADDON.getLocalizedString(32014), xbmcgui.NOTIFICATION_WARNING, 3000) return ""
def _format_exception(self, obj, method, args, kw, exc): etype, evalue, etb = exc funargs = ", ".join([repr(x) for x in args] + ["%s=%r" % (k, kw[k]) for k in kw]) if len(funargs) > 40: funargs = funargs[:37] + "..." _log.error("(%s) In Handler method %s.%s(%s) ", obj.__class__.__module__, obj.__class__.__name__, method, funargs) _log.debug("\n".join([ "%s::%s:%d %s" % (filename, fnname, lineno, srcline) for filename, lineno, fnname, srcline in traceback.extract_tb(etb)[1:] ])) _log.error("Unhandled error: %s: %s", etype.__name__, evalue) del etb return '%s: %s' % (etype.__name__, evalue)
def _readn(self): """ Internal function which reads from socket waiting for a newline """ streambuffer = self._buffer pos = streambuffer.find(b'\n') # _log.debug("read...") # retry = 0 while pos == -1: data = b'' try: data = self._sck.recv(2048) except IOError as inst: _log.debug("Read socket error: IOError%r (timeout: %r)", inst.args, self._sck.gettimeout()) if inst.errno in (errno.EAGAIN, errno.EWOULDBLOCK): if self._sck.gettimeout() == 0: # if it was too fast self._sck.settimeout(5) continue # time.sleep(0.5) # retry += 1 # if retry < 10: # _log.debug("Retry %s", retry) # continue # _log.debug(traceback.format_exc(0)) if inst.errno in self._SOCKET_COMM_ERRORS: raise EofError(len(streambuffer)) return b'' except socket.error as inst: _log.error("Read socket error: socket.error%r (timeout: %r)", inst.args, self._sck.gettimeout()) # _log.debug(traceback.format_exc(0)) return b'' except: raise if not data: raise EofError(len(streambuffer)) # _log.debug("readbuf+: %r", data) streambuffer += data pos = streambuffer.find(b'\n') self._buffer = streambuffer[pos + 1:] streambuffer = streambuffer[:pos] # _log.debug("read: %r", buffer) return streambuffer
def getEpisodeNumberFromElementumPath(path): """plugin://plugin.video.elementum/show/1622/season/15/episode/1/links/Supernatural%20S15E01""" episode_number = xbmc.getInfoLabel('ListItem.Episode') if episode_number: return episode_number else: result = re.search( r'plugin://plugin.video.elementum/[^/]+/\d+/season/\d+/episode/(\d+)/.*', path) if result: episode_number = result.group(1) return episode_number else: log.error("Could not find episode number for %s" % path) xbmcgui.Dialog().notification(ADDON.getLocalizedString(32007), ADDON.getLocalizedString(32014), xbmcgui.NOTIFICATION_WARNING, 3000) return ""
def _send(self, response): txtResponse = None try: txtResponse = json.dumps(response, self) except Exception as e: _log.error( "An unexpected error ocurred when trying to create the message: %r", e) response = { 'result': None, 'error': "InternalServerError: " + repr(e) } txtResponse = json.dumps(response, self) try: self.write(txtResponse) except TypeError: _log.debug("response was: %r", response) raise
def getLocalizedLabel(label): try: if "LOCALIZE" not in label: return py2_encode(label) if ";;" not in label and label.endswith(']'): return py2_encode(getLocalizedString(int(label[9:-1]))) else: parts = label.split(";;") translation = getLocalizedString(int(parts[0][9:14])) for i, part in enumerate(parts[1:]): if part[0:8] == "LOCALIZE": parts[i + 1] = getLocalizedString(int(part[9:14])) else: parts[i + 1] = py2_encode(parts[i + 1]) return py2_encode(translation % tuple(parts[1:]), 'utf-8', 'ignore') except Exception as e: log.error("Cannot decode the label: %s, Error: %s" % (label, e)) return label
def setresponse(self, value): """ Method used by Connection instance to tell Request that a Response is available to this request. Parameters: **value** Value (JSON decoded) received from socket. """ self.responses.put(value) for callback in self.callbacks: try: callback(self) except Exception as exc: _log.error("Error on callback: %r", exc) _log.debug(traceback.format_exc()) self.event_response.set() # helper for threads. if self.auto_close: self.close()
def close(self): """ Close the connection and the socket. """ if self.connection_status == "closed": return item = {'abort': True, 'event': threading.Event()} self.write_thread_queue.append(item) self.write_thread_semaphore.release() # notify new item. item['event'].wait(1) if not item['event'].isSet(): _log.warning("write thread doesn't process our abort command") try: self.handler._shutdown() except Exception: _log.error("Error when shutting down the handler: %s", traceback.format_exc()) try: self._sck.shutdown(socket.SHUT_RDWR) except socket.error: pass self._sck.close() self.connection_status = "closed"
def doAssign(): mediatype = getMediaType() try: path = sys.listitem.getfilename() except AttributeError: path = sys.listitem.getPath() use_tmdb_id = False if path.startswith("plugin://plugin.video.elementum"): """plugin://plugin.video.elementum/show/1622/season/15/episode/1/links/Supernatural%20S15E01 plugin://plugin.video.elementum/show/1622/season/15/episodes plugin://plugin.video.elementum/movie/628/links/Interview%20with%20the%20Vampire%20%281994%29""" use_tmdb_id = True result = re.search(r'plugin://plugin.video.elementum/[^/]+/(\d+)/.*', path) if result: tmdbID = result.group(1) else: log.error("Could not find TMDB id for %s" % path) xbmcgui.Dialog().notification(ADDON.getLocalizedString(32007), ADDON.getLocalizedString(32014), xbmcgui.NOTIFICATION_WARNING, 3000) return if mediatype == 'season': result = re.search( r'plugin://plugin.video.elementum/[^/]+/\d+/season/(\d+)/.*', path) if result: season_number = result.group(1) else: log.error("Could not find season number for %s" % path) xbmcgui.Dialog().notification(ADDON.getLocalizedString(32007), ADDON.getLocalizedString(32014), xbmcgui.NOTIFICATION_WARNING, 3000) return if mediatype == 'episode': result = re.search( r'plugin://plugin.video.elementum/[^/]+/\d+/season/(\d+)/episode/(\d+)/.*', path) if result: season_number = result.group(1) episode_number = result.group(2) else: log.error("Could not find season/episode number for %s" % path) xbmcgui.Dialog().notification(ADDON.getLocalizedString(32007), ADDON.getLocalizedString(32014), xbmcgui.NOTIFICATION_WARNING, 3000) return else: dbid = getDbId() if not dbid.isdigit(): log.error("Kodi library ID is wrong %s" % dbid) xbmcgui.Dialog().notification(ADDON.getLocalizedString(32007), ADDON.getLocalizedString(32016), xbmcgui.NOTIFICATION_WARNING, 3000) return # we also can use plugin://plugin.video.elementum/torrents/ file = xbmcgui.Dialog().browseSingle( 1, ADDON.getLocalizedString(32010), 'files', '', False, False, 'plugin://plugin.video.elementum/history/') if file == '': return try: parsed_url = urlparse(file) params = parse_qs(parsed_url.query) if 'infohash' in params: torrentid = params['infohash'][0] else: torrentid = params['resume'][0] except Exception as e: log.error("Could not get torrent info for %s: %s" % (file, e)) xbmcgui.Dialog().notification(ADDON.getLocalizedString(32007), ADDON.getLocalizedString(32015), xbmcgui.NOTIFICATION_WARNING, 3000) return if not use_tmdb_id: url = "plugin://plugin.video.elementum/context/torrents/assign/%s/kodi/%s/%s" % ( torrentid, mediatype, dbid) log.info("Assigning torrent %s for: DBID=%s, MediaType=%s" % (torrentid, dbid, mediatype)) else: if mediatype == 'movie': url = "plugin://plugin.video.elementum/context/torrents/assign/%s/tmdb/%s/%s" % ( torrentid, mediatype, tmdbID) elif mediatype == 'season': url = "plugin://plugin.video.elementum/context/torrents/assign/%s/tmdb/show/%s/%s/%s" % ( torrentid, tmdbID, mediatype, season_number) elif mediatype == 'episode': url = "plugin://plugin.video.elementum/context/torrents/assign/%s/tmdb/show/%s/season/%s/%s/%s" % ( torrentid, tmdbID, season_number, mediatype, episode_number) log.info("Assigning torrent %s for: TMDBID=%s, MediaType=%s" % (torrentid, tmdbID, mediatype)) xbmcgui.Dialog().notification(ADDON.getLocalizedString(32010), sys.listitem.getLabel(), xbmcgui.NOTIFICATION_INFO, 3000) log.info("Starting Elementum with: %s" % url) xbmc.Player().play(url)
def start_elementumd(**kwargs): jsonrpc_failures = 0 while jsonrpc_enabled() is False: jsonrpc_failures += 1 log.warning( "Unable to connect to Kodi's JSON-RPC service, retrying...") if jsonrpc_failures > 1: time.sleep(5) if not jsonrpc_enabled(notify=True): log.error( "Unable to reach Kodi's JSON-RPC service, aborting...") return False else: break time.sleep(3) elementum_dir, elementum_binary = get_elementum_binary() log.info("Binary dir: %s, item: %s " % (elementum_dir, elementum_binary)) if elementum_dir is False or elementum_binary is False: return False lockfile = os.path.join(ADDON_PATH, ".lockfile") if os.path.exists(lockfile): log.warning("Existing process found from lockfile, killing...") try: with open(lockfile) as lf: pid = int(lf.read().rstrip(" \t\r\n\0")) os.kill(pid, 9) except OSError as e: if e.errno != 3: # Ignore: OSError: [Errno 3] No such process log.error(repr(e)) except Exception as e: log.error(repr(e)) if binary_platform["os"] == "windows": try: library_lockfile = os.path.join( xbmc.translatePath( ADDON.getAddonInfo("profile")).decode('utf-8'), "library.db.lock") log.warning("Removing library.db.lock file at %s ..." % library_lockfile) os.remove(library_lockfile) except Exception as e: log.error(repr(e)) SW_HIDE = 0 STARTF_USESHOWWINDOW = 1 args = [elementum_binary] kwargs["cwd"] = elementum_dir if binary_platform["os"] == "windows": args[0] = getWindowsShortPath(elementum_binary) kwargs["cwd"] = getWindowsShortPath(elementum_dir) si = subprocess.STARTUPINFO() si.dwFlags = STARTF_USESHOWWINDOW si.wShowWindow = SW_HIDE clear_fd_inherit_flags() kwargs["startupinfo"] = si else: env = os.environ.copy() env["LD_LIBRARY_PATH"] = "%s:%s" % (elementum_dir, env.get("LD_LIBRARY_PATH", "")) kwargs["env"] = env kwargs["close_fds"] = True wait_counter = 1 log.debug("Checking for visible") while xbmc.getCondVisibility( 'Window.IsVisible(10140)') or xbmc.getCondVisibility( 'Window.IsActive(10140)'): if wait_counter == 1: log.info( 'Add-on settings currently opened, waiting before starting...') if wait_counter > 300: break time.sleep(1) wait_counter += 1 log.info("elementumd: start args: %s, kw: %s" % (args, kwargs)) if hasSubprocess: return subprocess.Popen(args, **kwargs) return False
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, xbmc.translatePath("special://xbmcbin/"))) xbmc_data_path = xbmc.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, xbmc.translatePath("special://masterprofile/"))) xbmc_data_path = xbmc.translatePath("special://masterprofile/") dest_binary_dir = os.path.join(xbmc_data_path, "files", ADDON_ID, "bin", "%(os)s_%(arch)s" % binary_platform) else: dest_binary_dir = os.path.join( xbmc.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) log.info("Binary detection. Source: %s, Destination: %s" % (binary_path, dest_binary_path)) 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)
def doAssign(): mediatype = getMediaType() try: path = sys.listitem.getfilename() except AttributeError: path = sys.listitem.getPath() use_elementum_path = False try: tmdbID = sys.listitem.getUniqueID('tmdb') except AttributeError: tmdbID = "" if tmdbID == "": if path.startswith("plugin://plugin.video.elementum"): use_elementum_path = True tmdbID = getTMDBidFromElementumPath(path) if not tmdbID: return if mediatype == 'season': season_number = getSeasonNumberFromElementumPath(path) if not season_number: return if mediatype == 'episode': season_number = getSeasonNumberFromElementumPath(path) episode_number = getEpisodeNumberFromElementumPath(path) if not season_number or not episode_number: return else: dbid = getDbId() if not dbid.isdigit(): log.error("Kodi library ID is wrong %s" % dbid) xbmcgui.Dialog().notification(ADDON.getLocalizedString(32007), ADDON.getLocalizedString(32016), xbmcgui.NOTIFICATION_WARNING, 3000) return # we also can use plugin://plugin.video.elementum/torrents/ file = xbmcgui.Dialog().browseSingle( 1, ADDON.getLocalizedString(32010), 'files', '', False, False, 'plugin://plugin.video.elementum/history/') if file == '': return try: parsed_url = urlparse(file) params = parse_qs(parsed_url.query) if 'infohash' in params: torrentid = params['infohash'][0] else: torrentid = params['resume'][0] except Exception as e: log.error("Could not get torrent info for %s: %s" % (file, e)) xbmcgui.Dialog().notification(ADDON.getLocalizedString(32007), ADDON.getLocalizedString(32015), xbmcgui.NOTIFICATION_WARNING, 3000) return if use_elementum_path: if mediatype == 'movie': url = "plugin://plugin.video.elementum/context/torrents/assign/%s/tmdb/%s/%s" % ( torrentid, mediatype, tmdbID) elif mediatype == 'season': url = "plugin://plugin.video.elementum/context/torrents/assign/%s/tmdb/show/%s/%s/%s" % ( torrentid, tmdbID, mediatype, season_number) elif mediatype == 'episode': url = "plugin://plugin.video.elementum/context/torrents/assign/%s/tmdb/show/%s/season/%s/%s/%s" % ( torrentid, tmdbID, season_number, mediatype, episode_number) log.info("Assigning torrent %s for: TMDBID=%s, MediaType=%s" % (torrentid, tmdbID, mediatype)) else: if tmdbID != "": url = "plugin://plugin.video.elementum/torrents/assign/%s/%s" % ( torrentid, tmdbID) log.info("Assigning torrent %s for: TMDBID=%s, MediaType=%s" % (torrentid, tmdbID, mediatype)) else: url = "plugin://plugin.video.elementum/context/torrents/assign/%s/kodi/%s/%s" % ( torrentid, mediatype, dbid) log.info("Assigning torrent %s for: DBID=%s, MediaType=%s" % (torrentid, dbid, mediatype)) xbmcgui.Dialog().notification(ADDON.getLocalizedString(32010), sys.listitem.getLabel(), xbmcgui.NOTIFICATION_INFO, 3000) log.info("Starting Elementum with: %s" % url) xbmc.Player().play(url)