예제 #1
0
 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
예제 #2
0
 def checkPlexClientsActive(self):
     try:
         plex = PlexServer(self._baseurl, self._token)
         for client in plex.clients():
             if client.isPlayingMedia():
                 return True
     except:
         pass
     return False
예제 #3
0
파일: plex.py 프로젝트: lairdm/automommy
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()
예제 #4
0
 def checkPlexClientsActive(self):
     try:
         plex = PlexServer(self._baseurl, self._token)
         for client in plex.clients():
             if client.isPlayingMedia():
                 return True
     except:
         pass
     return False
예제 #5
0
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'))
예제 #6
0
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
예제 #7
0
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
예제 #9
0
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()
예제 #12
0
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')