def active(self): try: plex = PlexServer(self._baseurl, self._token) except NotFound: return False for client in plex.clients(): if client.isPlayingMedia(): return True return False
def checkPlexClientsActive(self): try: plex = PlexServer(self._baseurl, self._token) for client in plex.clients(): if client.isPlayingMedia(): return True except: pass return False
class PlexController(): plex = None plexClient = None plexLibrary = None def __init__(self): self.plex = PlexServer(secrets.plexBaseUrl, secrets.token) self.plexLibrary = self.plex.library.section(secrets.librarySection) def initializeRoku(self): self.plexClient = self.plex.client(secrets.rokuClientName) def printClients(self): print("Available plex clients:") for client in self.plex.clients(): print(client.title) # Get a show by name def getShow(self, showName): shows = self.plexLibrary.search(title=showName) # We're just going to return the first if shows: return shows[0] return None def getEpisodeCount(self, showName): show = self.getShow(showName) return len(show.episodes()) if show else None def getUnwatchedEpisodeCount(self, showName): show = self.getShow(showName) return len(list(filter(lambda x: not x.isWatched, show.episodes()))) def getNextEpisode(self, showName): show = self.getShow(showName) for episode in show.episodes(): if episode.isWatched: continue return episode return None def playEpisode(self, episode): self.plexClient.playMedia(episode) def resetWatched(self, showName): show = self.getShow(showName) for episode in show.episodes(): episode.markUnwatched()
def data(): baseurl = 'http://' + SERVER_IP + ':' + PORT plex = PlexServer(baseurl, TOKEN) account = plex.myPlexAccount() player = request.form.get('player') for client in plex.clients(): if (player == client.name): media = plex.search(request.form.get('title')) player.playMedia(media) return render_template('success.html', art=request.form.get('art'), player=player, title=request.form.get('title')) return render_template('notfound.html', art=request.form.get('art'))
class PlexConnector: def __init__(self, url: str, token: str, server_name: str, library_list: dict, tautulli_url: str, tautulli_key: str, analytics: GoogleAnalytics, database: PlexContentDatabase): self.name = server_name self.server = PlexServer(baseurl=url, token=token) self.analytics = analytics info("Connected to Plex.") self.library_config = library_list self.tautulli = tautulli.TautulliConnector(url=tautulli_url, api_key=tautulli_key, analytics=analytics) info("Connected to Tautulli.") self.database = database info("Connected to database.") self.initialize_libraries() self.owner_players = [] def get_section_ids_for_media_type(self, media_type: str): if media_type not in self.library_config.keys(): return [] return self.library_config[media_type] def _error_and_analytics(self, error_message: str, function_name: str): error(message=error_message) self.analytics.event(event_category="Error", event_action=function_name, random_uuid_if_needed=True) def initialize_libraries(self): for name, numbers in self.library_config.items(): for number in numbers: self.database.add_library(name=name, plex_id=number) info(f"Added library {name} with number {number} to database.") def get_random_media_item(self, library_id: int = None, media_type: str = None): if library_id: return self.database.get_random_contents_for_library( library_section_id=library_id)[0] else: return self.database.get_random_contents_of_type( media_type=media_type)[0] def clean_libraries(self): self.database.purge() def _populate_library(self, library_name: str): if library_name not in self.library_config.keys(): return for library_number in self.library_config[library_name]: tautulli_data = self.tautulli.get_library( library_number=library_number) if not tautulli_data: self._error_and_analytics( f"Could not find library {library_number} on Tautulli", "_populate_library") continue bar = Bar('Populating library', max=tautulli_data.count) library_section = self.server.library.sectionByID(library_number) if not library_section: self._error_and_analytics( f"Could not find library {library_number} on Plex", "_populate_library") continue for item in library_section.all(): external_ids = [] try: external_ids = [guid.id for guid in item.guids] except AttributeError: pass small_media_item = SmallMediaItem( title=item.title, year=(None if library_section.type == 'artist' else item.year), rating_key=item.ratingKey, library_section_id=item.librarySectionID, media_type=item.type, external_ids=external_ids) small_media_item.add_to_database(database=self.database) bar.next() bar.finish() def populate_libraries(self): self.clean_libraries() for group_name in self.library_config.keys(): self._populate_library(library_name=group_name) def get_user_history(self, username, section_ids): return self.tautulli.get_user_history(username=username, section_ids=section_ids) def get_available_players(self, media_type): self.owner_players = [] players = self.server.clients() if not players: return None, 0 num = 0 players_list = "" for player in players[:5]: num = num + 1 players_list += f'\n{num}:{player.title}' self.owner_players.append(player) return f'{players_list}\nReact with which player you want to start this {media_type} on.', num def play_media(self, player_number, media_item): self.owner_players[player_number].goToMedia(media_item) def get_library_section(self, section_id): return self.server.library.sectionByID(f"{section_id}") def get_full_media_item(self, content_media_item: Content = None, small_media_item: SmallMediaItem = None, match_keys: bool = True): library_section = self.get_library_section( section_id=content_media_item.LibraryID if content_media_item else small_media_item.library_section_id) if not library_section: return None for item in get_possible_matching_items( library_section=library_section, title=content_media_item.Title if content_media_item else small_media_item.title, year=content_media_item.Year if content_media_item else None, external_ids=self.database.get_external_ids_for_content( content_id=content_media_item.ID) if content_media_item else small_media_item.external_ids): if match_keys: if item.ratingKey == (content_media_item.RatingKey if content_media_item else small_media_item.rating_key): return item else: return item # go with the first item in the list return None @property def server_id(self): return self.server.machineIdentifier def is_on_plex(self, title: str, year: int, external_ids: List[str] = None, section_id: int = None, section_name: str = None, match_rating_keys: bool = False): sections_ids_to_check = [] if section_id: sections_ids_to_check.append(section_id) elif section_name: section = self.server.library.section(title=section_name) if section: sections_ids_to_check.append(section.key) if not sections_ids_to_check: for _, numbers in self.library_config.items(): for number in numbers: sections_ids_to_check.append(number) for s_id in sections_ids_to_check: temp_media_item = SmallMediaItem(title=title, year=year, rating_key=None, library_section_id=s_id, media_type=None, external_ids=external_ids) possible_match = self.get_full_media_item( small_media_item=temp_media_item, match_keys=match_rating_keys) if possible_match: return possible_match return False
class PlexConnector: def __init__(self, url, token, server_name, library_list, tautulli_url, tautulli_key, analytics): self.url = url self.token = token self.name = server_name self.server = PlexServer(self.url, self.token) self.server_id = self.server.machineIdentifier self.analytics = analytics info("Connected to Plex.") self.tautulli = None self.tautulli_url = tautulli_url self.tautulli_key = tautulli_key self.make_tautulli_connector() info("Connected to Tautulli.") self.libraries = {} self.initialize_library(library_list=library_list) self.owner_players = [] def _error_and_analytics(self, error_message, function_name): error(error_message) self.analytics.event(event_category="Error", event_action=function_name, random_uuid_if_needed=True) def make_tautulli_connector(self): self.tautulli = tautulli.TautulliConnector(url=self.tautulli_url, api_key=self.tautulli_key, analytics=self.analytics) def initialize_library(self, library_list): for name, numbers in library_list.items(): self.libraries[name] = [numbers, []] info(f"Libraries: {self.libraries}") def clean_libraries(self): for groupName, items in self.libraries.items(): items[1].clear() def make_library(self, libraryName, attempts: int = 0): try: if not self.libraries[libraryName][1]: for libraryNumber in self.libraries[libraryName][0]: json_data = self.tautulli.api_call_get( "get_library", f"section_id={libraryNumber}") if json_data: count = json_data['response']['data']['count'] bar = Bar( f'Loading {libraryName} (Library section {libraryNumber})', max=int(count)) librarySection = self.server.library.sectionByID( f"{libraryNumber}") for item in librarySection.all(): try: self.libraries[libraryName][1].append( SmallMediaItem( title=item.title, year=(None if librarySection.type == 'artist' else item.year), ratingKey=item.ratingKey, librarySectionID=item.librarySectionID, mediaType=item.type)) except plex_exceptions.PlexApiException as e: self._error_and_analytics( error_message= f"Could not create SmallMediaItem for Plex library item: {e}", function_name= 'make_library (smallMediaItem internal)') bar.next() bar.finish() return True else: self._error_and_analytics( error_message= f"Could not get JSON data to build {libraryName} library.", function_name='make_library (JSONError)') except KeyError as e: self._error_and_analytics( error_message= f"Could not get section {libraryNumber} ({libraryName}) from the Plex Server: {e}", function_name='make_library (KeyError)') except plex_exceptions.PlexApiException as e: self._error_and_analytics( error_message= f"Could not create SmallMediaItem from Plex library item: {e}", function_name='make_library (PlexApiException)') except Exception as e: self._error_and_analytics( error_message=f'Error in makeLibrary: {e}', function_name='make_library (general)') if attempts < 5: # for generic errors, retry making the library return self.make_library(libraryName=libraryName, attempts=attempts + 1) return False def make_libraries(self): if not self.tautulli: self.make_tautulli_connector() self.clean_libraries() for groupName in self.libraries.keys(): self.make_library(libraryName=groupName) def get_user_history(self, username, sectionIDs): return self.tautulli.get_user_history(username=username, sectionIDs=sectionIDs) def get_available_players(self, mediaType): self.owner_players = [] players = self.server.clients() if not players: return None, 0 num = 0 players_list = "" for player in players[:5]: num = num + 1 players_list += f'\n{num}:{player.title}' self.owner_players.append(player) return f'{players_list}\nReact with which player you want to start this {mediaType} on.', num def playMedia(self, playerNumber, mediaItem): self.owner_players[playerNumber].goToMedia(mediaItem) def get_library_section(self, section_id): return self.server.library.sectionByID(f"{section_id}") def getFullMediaItem(self, mediaItem, external_ids=None, match_keys: bool = True): if external_ids is None: external_ids = [] librarySection = self.get_library_section( section_id=mediaItem.librarySectionID) for item in search_for_item(library_section=librarySection, title=mediaItem.title, year=mediaItem.year, external_ids=external_ids): if match_keys: if item.ratingKey == mediaItem.ratingKey: return item else: return item # go with the first item in the list return None def get_server_id(self): return self.server.machineIdentifier def is_on_plex(self, title, year, external_ids=None, section_id=None, section_name=None, match_rating_keys: bool = False): sections_ids_to_check = [] if section_id: sections_ids_to_check.append(section_id) elif section_name: section = self.server.library.section(title=section_name) if section: sections_ids_to_check.append(section.key) if not sections_ids_to_check: for name, ids in self.libraries.items(): for libraryNumber in ids[0]: sections_ids_to_check.append(libraryNumber) for s_id in sections_ids_to_check: temp_media_item = SmallMediaItem(title=title, year=year, ratingKey=None, librarySectionID=s_id, mediaType=None, external_ids=external_ids) possible_match = self.getFullMediaItem( mediaItem=temp_media_item, match_keys=match_rating_keys) if possible_match: return possible_match return False
class PlexAttributes(): def __init__(self, opts): self.opts = opts # command line options self.clsnames = [c for c in opts.clsnames.split(',') if c] # list of clsnames to report (blank=all) self.account = MyPlexAccount() # MyPlexAccount instance self.plex = PlexServer() # PlexServer instance self.total = 0 # Total objects parsed self.attrs = defaultdict(dict) # Attrs result set def run(self): starttime = time.time() self._parse_myplex() self._parse_server() self._parse_search() self._parse_library() self._parse_audio() self._parse_photo() self._parse_movie() self._parse_show() self._parse_client() self._parse_playlist() self._parse_sync() self.runtime = round((time.time() - starttime) / 60.0, 1) return self def _parse_myplex(self): self._load_attrs(self.account, 'myplex') self._load_attrs(self.account.devices(), 'myplex') for resource in self.account.resources(): self._load_attrs(resource, 'myplex') self._load_attrs(resource.connections, 'myplex') self._load_attrs(self.account.users(), 'myplex') def _parse_server(self): self._load_attrs(self.plex, 'serv') self._load_attrs(self.plex.account(), 'serv') self._load_attrs(self.plex.history()[:50], 'hist') self._load_attrs(self.plex.history()[50:], 'hist') self._load_attrs(self.plex.sessions(), 'sess') def _parse_search(self): for search in ('cre', 'ani', 'mik', 'she', 'bea'): self._load_attrs(self.plex.search(search), 'hub') def _parse_library(self): cat = 'lib' self._load_attrs(self.plex.library, cat) # self._load_attrs(self.plex.library.all()[:50], 'all') self._load_attrs(self.plex.library.onDeck()[:50], 'deck') self._load_attrs(self.plex.library.recentlyAdded()[:50], 'add') for search in ('cat', 'dog', 'rat', 'gir', 'mou'): self._load_attrs(self.plex.library.search(search)[:50], 'srch') # TODO: Implement section search (remove library search?) # TODO: Implement section search filters def _parse_audio(self): cat = 'lib' for musicsection in self.plex.library.sections(): if musicsection.TYPE == library.MusicSection.TYPE: self._load_attrs(musicsection, cat) for artist in musicsection.all(): self._load_attrs(artist, cat) for album in artist.albums(): self._load_attrs(album, cat) for track in album.tracks(): self._load_attrs(track, cat) def _parse_photo(self): cat = 'lib' for photosection in self.plex.library.sections(): if photosection.TYPE == library.PhotoSection.TYPE: self._load_attrs(photosection, cat) for photoalbum in photosection.all(): self._load_attrs(photoalbum, cat) for photo in photoalbum.photos(): self._load_attrs(photo, cat) def _parse_movie(self): cat = 'lib' for moviesection in self.plex.library.sections(): if moviesection.TYPE == library.MovieSection.TYPE: self._load_attrs(moviesection, cat) for movie in moviesection.all(): self._load_attrs(movie, cat) def _parse_show(self): cat = 'lib' for showsection in self.plex.library.sections(): if showsection.TYPE == library.ShowSection.TYPE: self._load_attrs(showsection, cat) for show in showsection.all(): self._load_attrs(show, cat) for season in show.seasons(): self._load_attrs(season, cat) for episode in season.episodes(): self._load_attrs(episode, cat) def _parse_client(self): for device in self.account.devices(): client = self._safe_connect(device) if client is not None: self._load_attrs(client, 'myplex') for client in self.plex.clients(): self._safe_connect(client) self._load_attrs(client, 'client') def _parse_playlist(self): for playlist in self.plex.playlists(): self._load_attrs(playlist, 'pl') for item in playlist.items(): self._load_attrs(item, 'pl') playqueue = PlayQueue.create(self.plex, playlist) self._load_attrs(playqueue, 'pq') def _parse_sync(self): # TODO: Get plexattrs._parse_sync() working. pass def _load_attrs(self, obj, cat=None): if isinstance(obj, (list, tuple)): return [self._parse_objects(item, cat) for item in obj] self._parse_objects(obj, cat) def _parse_objects(self, obj, cat=None): clsname = '%s.%s' % (obj.__module__, obj.__class__.__name__) clsname = clsname.replace('plexapi.', '') if self.clsnames and clsname not in self.clsnames: return None self._print_the_little_dot() if clsname not in self.attrs: self.attrs[clsname] = copy.deepcopy(NAMESPACE) self.attrs[clsname]['total'] += 1 self._load_xml_attrs(clsname, obj._data, self.attrs[clsname]['xml'], self.attrs[clsname]['examples'], self.attrs[clsname]['categories'], cat) self._load_obj_attrs(clsname, obj, self.attrs[clsname]['obj'], self.attrs[clsname]['docs']) def _print_the_little_dot(self): self.total += 1 if not self.total % 100: sys.stdout.write('.') if not self.total % 8000: sys.stdout.write('\n') sys.stdout.flush() def _load_xml_attrs(self, clsname, elem, attrs, examples, categories, cat): if elem is None: return None for attr in sorted(elem.attrib.keys()): attrs[attr] += 1 if cat: categories[attr].add(cat) if elem.attrib[attr] and len(examples[attr]) <= self.opts.examples: examples[attr].add(elem.attrib[attr]) for subelem in elem: attrname = TAGATTRS.get(subelem.tag, '%ss' % subelem.tag.lower()) attrs['%s[]' % attrname] += 1 def _load_obj_attrs(self, clsname, obj, attrs, docs): if clsname in STOP_RECURSING_AT: return None if isinstance(obj, PlexObject) and clsname not in DONT_RELOAD: self._safe_reload(obj) alldocs = '\n\n'.join(self._all_docs(obj.__class__)) for attr, value in obj.__dict__.items(): if value is None or isinstance(value, (str, bool, float, int, datetime)): if not attr.startswith('_') and attr not in IGNORES.get( clsname, []): attrs[attr] += 1 if re.search('\s{8}%s\s\(.+?\)\:' % attr, alldocs) is not None: docs[attr] += 1 if isinstance(value, list): if not attr.startswith('_') and attr not in IGNORES.get( clsname, []): if value and isinstance(value[0], PlexObject): attrs['%s[]' % attr] += 1 [self._parse_objects(obj) for obj in value] def _all_docs(self, cls, docs=None): import inspect docs = docs or [] if cls.__doc__ is not None: docs.append(cls.__doc__) for parent in inspect.getmro(cls): if parent != cls: docs += self._all_docs(parent) return docs def print_report(self): total_attrs = 0 for clsname in sorted(self.attrs.keys()): if self._clsname_match(clsname): meta = self.attrs[clsname] count = meta['total'] print(_('\n%s (%s)\n%s' % (clsname, count, '-' * 30), 'yellow')) attrs = sorted( set(list(meta['xml'].keys()) + list(meta['obj'].keys()))) for attr in attrs: state = self._attr_state(clsname, attr, meta) count = meta['xml'].get(attr, 0) categories = ','.join(meta['categories'].get(attr, ['--'])) examples = '; '.join( list(meta['examples'].get(attr, ['--']))[:3])[:80] print('%7s %3s %-30s %-20s %s' % (count, state, attr, categories, examples)) total_attrs += count print(_('\nSUMMARY\n%s' % ('-' * 30), 'yellow')) print('%7s %3s %3s %3s %-20s %s' % ('total', 'new', 'old', 'doc', 'categories', 'clsname')) for clsname in sorted(self.attrs.keys()): if self._clsname_match(clsname): print('%7s %12s %12s %12s %s' % (self.attrs[clsname]['total'], _(self.attrs[clsname]['new'] or '', 'cyan'), _(self.attrs[clsname]['old'] or '', 'red'), _(self.attrs[clsname]['doc'] or '', 'purple'), clsname)) print('\nPlex Version %s' % self.plex.version) print('PlexAPI Version %s' % plexapi.VERSION) print('Total Objects %s' % sum([x['total'] for x in self.attrs.values()])) print('Runtime %s min\n' % self.runtime) def _clsname_match(self, clsname): if not self.clsnames: return True for cname in self.clsnames: if cname.lower() in clsname.lower(): return True return False def _attr_state(self, clsname, attr, meta): if attr in meta['xml'].keys() and attr not in meta['obj'].keys(): self.attrs[clsname]['new'] += 1 return _('new', 'blue') if attr not in meta['xml'].keys() and attr in meta['obj'].keys(): self.attrs[clsname]['old'] += 1 return _('old', 'red') if attr not in meta['docs'].keys() and attr in meta['obj'].keys(): self.attrs[clsname]['doc'] += 1 return _('doc', 'purple') return _(' ', 'green') def _safe_connect(self, elem): try: return elem.connect() except: return None def _safe_reload(self, elem): try: elem.reload() except: pass
class PlexAttributes(): def __init__(self, opts): self.opts = opts # command line options self.clsnames = [c for c in opts.clsnames.split(',') if c] # list of clsnames to report (blank=all) self.account = MyPlexAccount() # MyPlexAccount instance self.plex = PlexServer() # PlexServer instance self.total = 0 # Total objects parsed self.attrs = defaultdict(dict) # Attrs result set def run(self): starttime = time.time() self._parse_myplex() self._parse_server() self._parse_search() self._parse_library() self._parse_audio() self._parse_photo() self._parse_movie() self._parse_show() self._parse_client() self._parse_playlist() self._parse_sync() self.runtime = round((time.time() - starttime) / 60.0, 1) return self def _parse_myplex(self): self._load_attrs(self.account, 'myplex') self._load_attrs(self.account.devices(), 'myplex') for resource in self.account.resources(): self._load_attrs(resource, 'myplex') self._load_attrs(resource.connections, 'myplex') self._load_attrs(self.account.users(), 'myplex') def _parse_server(self): self._load_attrs(self.plex, 'serv') self._load_attrs(self.plex.account(), 'serv') self._load_attrs(self.plex.history()[:50], 'hist') self._load_attrs(self.plex.history()[50:], 'hist') self._load_attrs(self.plex.sessions(), 'sess') def _parse_search(self): for search in ('cre', 'ani', 'mik', 'she', 'bea'): self._load_attrs(self.plex.search(search), 'hub') def _parse_library(self): cat = 'lib' self._load_attrs(self.plex.library, cat) # self._load_attrs(self.plex.library.all()[:50], 'all') self._load_attrs(self.plex.library.onDeck()[:50], 'deck') self._load_attrs(self.plex.library.recentlyAdded()[:50], 'add') for search in ('cat', 'dog', 'rat', 'gir', 'mou'): self._load_attrs(self.plex.library.search(search)[:50], 'srch') # TODO: Implement section search (remove library search?) # TODO: Implement section search filters def _parse_audio(self): cat = 'lib' for musicsection in self.plex.library.sections(): if musicsection.TYPE == library.MusicSection.TYPE: self._load_attrs(musicsection, cat) for artist in musicsection.all(): self._load_attrs(artist, cat) for album in artist.albums(): self._load_attrs(album, cat) for track in album.tracks(): self._load_attrs(track, cat) def _parse_photo(self): cat = 'lib' for photosection in self.plex.library.sections(): if photosection.TYPE == library.PhotoSection.TYPE: self._load_attrs(photosection, cat) for photoalbum in photosection.all(): self._load_attrs(photoalbum, cat) for photo in photoalbum.photos(): self._load_attrs(photo, cat) def _parse_movie(self): cat = 'lib' for moviesection in self.plex.library.sections(): if moviesection.TYPE == library.MovieSection.TYPE: self._load_attrs(moviesection, cat) for movie in moviesection.all(): self._load_attrs(movie, cat) def _parse_show(self): cat = 'lib' for showsection in self.plex.library.sections(): if showsection.TYPE == library.ShowSection.TYPE: self._load_attrs(showsection, cat) for show in showsection.all(): self._load_attrs(show, cat) for season in show.seasons(): self._load_attrs(season, cat) for episode in season.episodes(): self._load_attrs(episode, cat) def _parse_client(self): for device in self.account.devices(): client = self._safe_connect(device) if client is not None: self._load_attrs(client, 'myplex') for client in self.plex.clients(): self._safe_connect(client) self._load_attrs(client, 'client') def _parse_playlist(self): for playlist in self.plex.playlists(): self._load_attrs(playlist, 'pl') for item in playlist.items(): self._load_attrs(item, 'pl') playqueue = PlayQueue.create(self.plex, playlist) self._load_attrs(playqueue, 'pq') def _parse_sync(self): # TODO: Get plexattrs._parse_sync() working. pass def _load_attrs(self, obj, cat=None): if isinstance(obj, (list, tuple)): return [self._parse_objects(item, cat) for item in obj] self._parse_objects(obj, cat) def _parse_objects(self, obj, cat=None): clsname = '%s.%s' % (obj.__module__, obj.__class__.__name__) clsname = clsname.replace('plexapi.', '') if self.clsnames and clsname not in self.clsnames: return None self._print_the_little_dot() if clsname not in self.attrs: self.attrs[clsname] = copy.deepcopy(NAMESPACE) self.attrs[clsname]['total'] += 1 self._load_xml_attrs(clsname, obj._data, self.attrs[clsname]['xml'], self.attrs[clsname]['examples'], self.attrs[clsname]['categories'], cat) self._load_obj_attrs(clsname, obj, self.attrs[clsname]['obj'], self.attrs[clsname]['docs']) def _print_the_little_dot(self): self.total += 1 if not self.total % 100: sys.stdout.write('.') if not self.total % 8000: sys.stdout.write('\n') sys.stdout.flush() def _load_xml_attrs(self, clsname, elem, attrs, examples, categories, cat): if elem is None: return None for attr in sorted(elem.attrib.keys()): attrs[attr] += 1 if cat: categories[attr].add(cat) if elem.attrib[attr] and len(examples[attr]) <= self.opts.examples: examples[attr].add(elem.attrib[attr]) for subelem in elem: attrname = TAGATTRS.get(subelem.tag, '%ss' % subelem.tag.lower()) attrs['%s[]' % attrname] += 1 def _load_obj_attrs(self, clsname, obj, attrs, docs): if clsname in STOP_RECURSING_AT: return None if isinstance(obj, PlexObject) and clsname not in DONT_RELOAD: self._safe_reload(obj) alldocs = '\n\n'.join(self._all_docs(obj.__class__)) for attr, value in obj.__dict__.items(): if value is None or isinstance(value, (str, bool, float, int, datetime)): if not attr.startswith('_') and attr not in IGNORES.get(clsname, []): attrs[attr] += 1 if re.search('\s{8}%s\s\(.+?\)\:' % attr, alldocs) is not None: docs[attr] += 1 if isinstance(value, list): if not attr.startswith('_') and attr not in IGNORES.get(clsname, []): if value and isinstance(value[0], PlexObject): attrs['%s[]' % attr] += 1 [self._parse_objects(obj) for obj in value] def _all_docs(self, cls, docs=None): import inspect docs = docs or [] if cls.__doc__ is not None: docs.append(cls.__doc__) for parent in inspect.getmro(cls): if parent != cls: docs += self._all_docs(parent) return docs def print_report(self): total_attrs = 0 for clsname in sorted(self.attrs.keys()): if self._clsname_match(clsname): meta = self.attrs[clsname] count = meta['total'] print(_('\n%s (%s)\n%s' % (clsname, count, '-' * 30), 'yellow')) attrs = sorted(set(list(meta['xml'].keys()) + list(meta['obj'].keys()))) for attr in attrs: state = self._attr_state(clsname, attr, meta) count = meta['xml'].get(attr, 0) categories = ','.join(meta['categories'].get(attr, ['--'])) examples = '; '.join(list(meta['examples'].get(attr, ['--']))[:3])[:80] print('%7s %3s %-30s %-20s %s' % (count, state, attr, categories, examples)) total_attrs += count print(_('\nSUMMARY\n%s' % ('-' * 30), 'yellow')) print('%7s %3s %3s %3s %-20s %s' % ('total', 'new', 'old', 'doc', 'categories', 'clsname')) for clsname in sorted(self.attrs.keys()): if self._clsname_match(clsname): print('%7s %12s %12s %12s %s' % (self.attrs[clsname]['total'], _(self.attrs[clsname]['new'] or '', 'cyan'), _(self.attrs[clsname]['old'] or '', 'red'), _(self.attrs[clsname]['doc'] or '', 'purple'), clsname)) print('\nPlex Version %s' % self.plex.version) print('PlexAPI Version %s' % plexapi.VERSION) print('Total Objects %s' % sum([x['total'] for x in self.attrs.values()])) print('Runtime %s min\n' % self.runtime) def _clsname_match(self, clsname): if not self.clsnames: return True for cname in self.clsnames: if cname.lower() in clsname.lower(): return True return False def _attr_state(self, clsname, attr, meta): if attr in meta['xml'].keys() and attr not in meta['obj'].keys(): self.attrs[clsname]['new'] += 1 return _('new', 'blue') if attr not in meta['xml'].keys() and attr in meta['obj'].keys(): self.attrs[clsname]['old'] += 1 return _('old', 'red') if attr not in meta['docs'].keys() and attr in meta['obj'].keys(): self.attrs[clsname]['doc'] += 1 return _('doc', 'purple') return _(' ', 'green') def _safe_connect(self, elem): try: return elem.connect() except: return None def _safe_reload(self, elem): try: elem.reload() except: pass
class PlexClientSensor(Entity): """Representation of a Plex now playing sensor.""" def __init__(self, name, plex_url, plex_user, plex_password, plex_server, plex_token, plex_machine_id): """Initialize the sensor.""" from plexapi.myplex import MyPlexAccount from plexapi.server import PlexServer self._name = name self._media_attrs = {} self._plex_machine_id = plex_machine_id self._player_state = 'idle' self._machineIdentifier = plex_machine_id self._device = None self._is_player_active = False self._is_player_available = False self._player = None self._make = 'AppleTV' self._session = None self._session_type = None self._session_username = None self._state = STATE_IDLE self._entity_picture = None self._plex_url = plex_url self._plex_token = plex_token self._media_content_id = None self._media_content_rating = None self._media_content_type = None self._media_duration = None self._media_image_url = None self._media_title = None self._media_ratio = None self._media_episode = None self._media_season = None self._media_series_title = None if plex_token: self._server = PlexServer(plex_url, plex_token) elif plex_user and plex_password: user = MyPlexAccount(plex_user, plex_password) server = plex_server if plex_server else user.resources()[0].name self._server = user.resource(server).connect() else: self._server = PlexServer(plex_url) @Throttle(MIN_TIME_BETWEEN_UPDATES) def update(self): """Update method for Plex sensor.""" # new data refresh self._clear_media_details() devices = self._server.clients() for device in devices: if device.machineIdentifier == self._machineIdentifier: self._device = device sessions = self._server.sessions() for sess in sessions: if sess.players[0].machineIdentifier == self._machineIdentifier: self._session = sess self._is_player_available = True else: self._is_player_available = False if self._session is not None: self._player = self._session.players[0] self._player_state = self._player.state self._session_username = self._session.usernames[0] self._make = self._player.device self._media_ratio = self._session.media[0].aspectRatio self._media_content_id = self._session.ratingKey self._media_content_rating = getattr( self._session, 'contentRating', None) else: self._is_player_available = False self._player_state = None self._entity_picture = '/local/icon/plex.png' self._set_player_state() if self._is_player_active and self._session is not None: self._session_type = self._session.type self._media_duration = self._session.duration # title (movie name, tv episode name, music song name) self._media_title = self._session.title # media type self._set_media_type() self._set_media_image() # calculate duration duration_min = self._media_duration / 60000 hours = int (duration_min / 60) mins = duration_min - hours * 60 length = "%d:%02d" % (hours, mins) media_attrs = {} media_attrs['type'] = self._session_type media_attrs['ratio'] = self._media_ratio media_attrs['rating'] = self._media_content_rating media_attrs['duration'] = length media_attrs['user'] = self._session_username self._entity_picture = self._media_image_url if self._session_type == 'episode': media_attrs['title'] = self._media_series_title + " S" + self._media_season + "E" + self._media_episode + " - " + self._media_title else: media_attrs['title'] = self._media_title self._media_attrs = media_attrs else: self._session_type = None self._media_attrs = {"type": "None", "title": "None", "ratio": "None", "rating": "None", "duration": "None", "user": "******"} def _clear_media_details(self): self._device = None self._session = None """Set all Media Items to None.""" # General self._media_content_id = None self._media_content_rating = None self._media_content_type = None self._media_duration = None self._media_image_url = None self._media_title = None self._media_ratio = None self._entity_picture = None # TV Show self._media_episode = None self._media_season = None self._media_series_title = None def _set_player_state(self): if self._player_state == 'playing': self._is_player_active = True self._state = STATE_PLAYING elif self._player_state == 'paused': self._is_player_active = True self._state = STATE_PAUSED elif self.device: self._is_player_active = False self._state = STATE_IDLE else: self._is_player_active = False self._state = STATE_OFF def _set_media_image(self): thumb_url = self._session.thumbUrl if (self.media_content_type is MEDIA_TYPE_TVSHOW): thumb_url = self._session.url(self._session.grandparentThumb) if thumb_url is None: thumb_url = self._session.url(self._session.art) self._media_image_url = thumb_url def _set_media_type(self): if self._session_type in ['clip', 'episode']: self._media_content_type = MEDIA_TYPE_TVSHOW # season number (00) if callable(self._session.season): self._media_season = str( (self._session.season()).index).zfill(2) elif self._session.parentIndex is not None: self._media_season = self._session.parentIndex.zfill(2) else: self._media_season = None # show name self._media_series_title = self._session.grandparentTitle # episode number (00) if self._session.index is not None: self._media_episode = str(self._session.index).zfill(2) elif self._session_type == 'movie': self._media_content_type = MEDIA_TYPE_VIDEO if self._session.year is not None and \ self._media_title is not None: self._media_title += ' (' + str(self._session.year) + ')' @property def name(self): """Return the name of the sensor.""" return self._name @property def state(self): """Return the state of the sensor.""" return self._state @property def device_state_attributes(self): """Return the state attributes.""" return self._media_attrs @property def entity_picture(self): """Return the entity picture of the sensor.""" return self._entity_picture @property def unique_id(self): """Return the id of this plex client.""" return self._machineIdentifier @property def machine_identifier(self): """Return the machine identifier of the device.""" return self._machineIdentifier @property def device(self): """Return the device, if any.""" return self._device @property def session(self): """Return the session, if any.""" return self._session @property def media_content_id(self): """Return the content ID of current playing media.""" return self._media_content_id @property def media_content_type(self): """Return the content type of current playing media.""" if self._session_type == 'clip': return MEDIA_TYPE_TVSHOW elif self._session_type == 'episode': return MEDIA_TYPE_TVSHOW elif self._session_type == 'movie': return MEDIA_TYPE_VIDEO return None @property def media_duration(self): """Return the duration of current playing media in seconds.""" return self._media_duration @property def media_ratio(self): """Return the aspect ratio of current playing media in seconds.""" return self._media_ratio @property def media_image_url(self): """Return the image URL of current playing media.""" return self._media_image_url @property def media_title(self): """Return the title of current playing media.""" return self._media_title @property def media_season(self): """Return the season of current playing media (TV Show only).""" return self._media_season @property def media_series_title(self): """Return the title of the series of current playing media.""" return self._media_series_title @property def media_episode(self): """Return the episode of current playing media (TV Show only).""" return self._media_episode @property def make(self): """Return the make of the device (ex. SHIELD Android TV).""" return self._make
cur.execute('INSERT INTO settings VALUES(?,?)', ('PLEXSVR', PLEXSVR)) sql.commit() cur.execute('SELECT setting FROM settings WHERE item LIKE \'PLEXSERVERIP\'') if not cur.fetchone(): cur.execute('INSERT INTO settings VALUES(?,?)', ('PLEXSERVERIP', PLEXSERVERIP)) sql.commit() cur.execute('SELECT setting FROM settings WHERE item LIKE \'PLEXSERVERPORT\'') if not cur.fetchone(): cur.execute('INSERT INTO settings VALUES(?,?)', ('PLEXSERVERPORT', PLEXSERVERPORT)) sql.commit() cur.execute('SELECT setting FROM settings WHERE item LIKE \'PLEXCLIENT\'') if not cur.fetchone(): daclients = [] for client in plex.clients(): daclients.append(client.title) print("Select a Client. The Following Clients are available:") counter = 1 for client in daclients: print(str(counter) + "- " + client.strip() + "\n") counter = counter + 1 choice = int(input('New Client: ')) try: client = daclients[choice - 1].strip() cur.execute('DELETE FROM settings WHERE item LIKE \'PLEXCLIENT\'') sql.commit() cur.execute('INSERT INTO settings VALUES(?,?)', ('PLEXCLIENT', client)) sql.commit() cur.execute("SELECT * FROM settings WHERE item LIKE \'PLEXCLIENT\'") test = cur.fetchone()
class PlexAttributes(): def __init__(self, opts): self.opts = opts # command line options self.clsnames = [c for c in opts.clsnames.split(',') if c] # list of clsnames to report (blank=all) self.account = MyPlexAccount.signin() # MyPlexAccount instance self.plex = PlexServer() # PlexServer instance self.attrs = defaultdict(dict) # Attrs result set def run(self): # MyPlex self._load_attrs(self.account) self._load_attrs(self.account.devices()) for resource in self.account.resources(): self._load_attrs(resource) self._load_attrs(resource.connections) self._load_attrs(self.account.users()) # Server self._load_attrs(self.plex) self._load_attrs(self.plex.account()) self._load_attrs(self.plex.history()[:20]) self._load_attrs(self.plex.playlists()) for search in ('cre', 'ani', 'mik', 'she'): self._load_attrs(self.plex.search('cre')) self._load_attrs(self.plex.sessions()) # Library self._load_attrs(self.plex.library) self._load_attrs(self.plex.library.sections()) self._load_attrs(self.plex.library.all()[:20]) self._load_attrs(self.plex.library.onDeck()[:20]) self._load_attrs(self.plex.library.recentlyAdded()[:20]) for search in ('cat', 'dog', 'rat'): self._load_attrs(self.plex.library.search(search)[:20]) # Client self._load_attrs(self.plex.clients()) return self def _load_attrs(self, obj): if isinstance(obj, (list, tuple)): return [self._parse_objects(x) for x in obj] return self._parse_objects(obj) def _parse_objects(self, obj): clsname = '%s.%s' % (obj.__module__, obj.__class__.__name__) clsname = clsname.replace('plexapi.', '') if self.clsnames and clsname not in self.clsnames: return None sys.stdout.write('.') sys.stdout.flush() if clsname not in self.attrs: self.attrs[clsname] = copy.deepcopy(NAMESPACE) self.attrs[clsname]['total'] += 1 self._load_xml_attrs(clsname, obj._data, self.attrs[clsname]['xml'], self.attrs[clsname]['examples']) self._load_obj_attrs(clsname, obj, self.attrs[clsname]['obj']) def _load_xml_attrs(self, clsname, elem, attrs, examples): if elem in (None, NA): return None for attr in sorted(elem.attrib.keys()): attrs[attr] += 1 if elem.attrib[attr] and len(examples[attr]) <= self.opts.examples: examples[attr].add(elem.attrib[attr]) def _load_obj_attrs(self, clsname, obj, attrs): for attr, value in obj.__dict__.items(): if value in (None, NA) or isinstance( value, (str, bool, float, int, datetime)): if not attr.startswith('_') and attr not in IGNORES.get( clsname, []): attrs[attr] += 1 def print_report(self): total_attrs = 0 for clsname in sorted(self.attrs.keys()): meta = self.attrs[clsname] count = meta['total'] print( _('\n%s (%s)\n%s' % (clsname, count, '-' * (len(clsname) + 8)), 'yellow')) attrs = sorted( set(list(meta['xml'].keys()) + list(meta['obj'].keys()))) for attr in attrs: state = self._attr_state(attr, meta) count = meta['xml'].get(attr, 0) example = list(meta['examples'].get(attr, ['--']))[0][:80] print('%-4s %4s %-30s %s' % (state, count, attr, example)) total_attrs += count print(_('\nSUMMARY\n------------', 'yellow')) print('Plex Version %s' % self.plex.version) print('PlexAPI Version %s' % plexapi.VERSION) print('Total Objects %s\n' % sum([x['total'] for x in self.attrs.values()])) for clsname in sorted(self.attrs.keys()): print('%-34s %s' % (clsname, self.attrs[clsname]['total'])) print() def _attr_state(self, attr, meta): if attr in meta['xml'].keys() and attr not in meta['obj'].keys(): return _('new', 'blue') if attr not in meta['xml'].keys() and attr in meta['obj'].keys(): return _('old', 'red') return _(' ', 'green')