예제 #1
0
def main():
    """Main script"""
    plex = PlexServer(PLEX_URL, PLEX_TOKEN)
    plex_users = get_user_tokens(plex.machineIdentifier)

    plex_playlists = {playlist.title: playlist.items() for playlist in plex.playlists()}

    for playlist in PLAYLISTS:
        playlist_items = plex_playlists.get(playlist)
        if not playlist_items:
            print("Playlist '{playlist}' not found on the server. Skipping.".format(playlist=playlist))
            continue

        print("Cloning the '{title}' playlist...".format(title=playlist))

        for user in USERS:
            user_token = plex_users.get(user)
            if not user_token:
                print("...User '{user}' not found in shared users. Skipping.".format(user=user))
                continue

            user_plex = PlexServer(PLEX_URL, user_token)

            # Delete the old playlist
            try:
                user_playlist = user_plex.playlist(playlist)
                user_playlist.delete()
            except:
                pass

            # Create a new playlist
            user_plex.createPlaylist(playlist, playlist_items)
            print("...Created playlist for '{user}'.".format(user=user))

    return
예제 #2
0
def change_playlist_content(plex: PlexServer, name: str,
                            tracks: List[Track]) -> Playlist:
    """
    Connects to the PlexServer, and fills the named playlist with the given track list. If the playlist does not yet
    exist, it will be created.
    :param plex: the PlexServer object
    :param name: the name of the playlist to replace the contents of
    :param tracks: list of Tracks that will be the contents of the playlist
    :return: the Playlist object
    """
    timer = Stopwatch()
    timer.start()
    logger = config.logger

    if not any([name == pl.title for pl in plex.playlists()]):
        playlist = plex.createPlaylist(name, tracks)
        logger.debug(f"Created new playlist {name} in {timer.click():.2f}s")
    else:
        playlist = plex.playlist(name)
        [playlist.removeItem(item) for item in playlist.items()]
        logger.debug(f"Emptied playlist {name} in {timer.click():.2f}s")
        playlist.addItems(tracks)
        logger.debug(
            f"Added {len(tracks)} track to the playlist {name} in {timer.click():.2f}s"
        )
    return playlist
def main():
    """Main script"""
    global PLAYLISTS

    plex_server = PlexServer(PLEX_URL, PLEX_TOKEN)
    plex_users = get_user_tokens(plex_server.machineIdentifier)
    plex_users[plex_server.myPlexUsername] = plex_server._token

    plex_user = PlexServer(PLEX_URL, plex_users[FROM_USER])
    plex_playlists = {
        playlist.title: playlist.items()
        for playlist in plex_user.playlists()
    }

    if not PLAYLISTS:
        PLAYLISTS = plex_playlists  # Default to all playlists
    for playlist in PLAYLISTS:
        if playlist in SKIP_PLAYLISTS:
            print("Skipping '{playlist}'...".format(playlist=playlist))
            continue
        playlist_items = plex_playlists.get(playlist)
        if not playlist_items:
            print("Playlist '{playlist}' not found on the server. Skipping.".
                  format(playlist=playlist))
            continue

        print("Cloning the '{title}' playlist...".format(title=playlist))

        for user in TO_USERS:
            user_token = plex_users.get(user)
            if not user_token:
                print("...User '{user}' not found in shared users. Skipping.".
                      format(user=user))
                continue

            user_plex = PlexServer(PLEX_URL, user_token)

            # Delete the old playlist
            try:
                user_playlist = user_plex.playlist(playlist)
                user_playlist.delete()
            except:
                pass

            # Create a new playlist
            user_plex.createPlaylist(playlist, playlist_items)
            print("...Created playlist for '{user}'.".format(user=user))

    return
예제 #4
0
def test_server_Server_session():
    from requests import Session
    from plexapi.server import PlexServer

    class MySession(Session):
        def __init__(self):
            super(self.__class__, self).__init__()
            self.plexapi_session_test = True

    plex = PlexServer('http://138.68.157.5:32400',
                      os.environ.get('PLEX_TEST_TOKEN'),
                      session=MySession())
    assert hasattr(plex.session, 'plexapi_session_test')
    pl = plex.playlists()
    assert hasattr(pl[0].server.session, 'plexapi_session_test')
예제 #5
0
def create_playlists():
    plex = PlexServer(BASEURL, TOKEN)

    plex_scenes = {}
    plex_playlists = {}

    for library_name in LIBRARIES:
        plex_library = plex.library.section(library_name)
        plex_playlists[plex_library] = [
            playlist for playlist in plex.playlists()
            if playlist.smart and playlist.items()
            and playlist.items()[0].section() == plex_library
        ]
        plex_scenes[plex_library] = plex_library.all()

    plex_actors = {}

    for library in plex_scenes:
        plex_actors[library] = []
        for scene in plex_scenes[library]:
            for actor in scene.actors:
                actor_name = actor.tag

                if actor_name and actor_name not in plex_actors[library]:
                    plex_actors[library].append(actor_name)

    for library in plex_scenes:
        playlist_titles = [
            playlist.title for playlist in plex_playlists[library]
        ]
        for actor_name in plex_actors[library]:
            if actor_name not in playlist_titles:
                Playlist.create(plex,
                                title=actor_name,
                                section=library,
                                smart=True,
                                actor=actor_name)
예제 #6
0
파일: plex.py 프로젝트: fretb/Plex2Jellyfin
class Plex:
    def __init__(self, url, token, server_name):
        self.url = url
        self.token = token
        self.server_name = server_name
        self.server = PlexServer(url, token)

    def get_users(self):
        return self.server.myPlexAccount().users()

    def user_has_server_access(self, user):
        for s in user.servers:
            if s.name == self.server_name:
                return True
        return False

    def get_playlists(self):
        return self.server.playlists()

    def get_library_sections(self):
        return self.server.library.sections()

    def get_all_section_items(self, section):
        return section.all()
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
예제 #8
0
    # Disable the warning that the request is insecure, we know that...
    import urllib3
    urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

plex = PlexServer(PLEX_URL, PLEX_TOKEN, session=sess)
account = plex.myPlexAccount()

user_lst = [
    x.title for x in plex.myPlexAccount().users() if x.servers and x.friend
]
sections = plex.library.sections()
sections_dict = {x.key: x.title for x in sections}
filters_lst = list(
    set([y.key for x in sections if x.type != 'photo'
         for y in x.listFields()]))
admin_playlist_lst = [x for x in plex.playlists()]
today = datetime.datetime.now().date()
weeknum = datetime.date(today.year, today.month, today.day).isocalendar()[1]


def actions():
    """
    add - create new playlist for admin or users
    remove - remove playlist type or name from admin or users
    update - remove playlist type and create new playlist type for admin or users
    show - show contents of playlist type or admin or users current playlists
    share - share existing playlist by title from admin to users
    export - export playlist by title from admin to users
    """
    return ['add', 'remove', 'update', 'show', 'share', 'export']
예제 #9
0
# If verify is set to a path to a directory,
# the directory must have been processed using the c_rehash utility supplied
# with OpenSSL.
if sess.verify is False:
    # Disable the warning that the request is insecure, we know that...
    import urllib3
    urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

plex = PlexServer(PLEX_URL, PLEX_TOKEN, session=sess)
account = plex.myPlexAccount()

user_lst = [x.title for x in plex.myPlexAccount().users()]
sections = plex.library.sections()
sections_lst = [x.title for x in sections]
filter_lst = list(set([y for x in sections if x.type != 'photo' for y in x.ALLOWED_FILTERS]))
playlist_lst = [x.title for x in plex.playlists()]
today = datetime.datetime.now().date()
weeknum = datetime.date(today.year, today.month, today.day).isocalendar()[1]

def actions():
    """
    add - create new playlist for admin or users
    remove - remove playlist type or name from admin or users
    update - remove playlist type and create new playlist type for admin or users
    show - show contents of playlist type or admin or users current playlists
    share - share existing playlist by title from admin to users
    """
    return ['add', 'remove', 'update', 'show', 'share']


def selectors():
예제 #10
0
class tizplexproxy(object):
    """A class that accesses Plex servers, retrieves track URLs and creates and
    manages a playback queue.

    """
    def __init__(self, base_url, token, section):
        self.base_url = base_url
        self.queue = list()
        self.queue_index = -1
        self.play_queue_order = list()
        self.play_modes = TizEnumeration(["NORMAL", "SHUFFLE"])
        self.current_play_mode = self.play_modes.NORMAL
        self.now_playing_track = None
        self._plex = PlexServer(base_url, token)
        self._music = self._plex.library.section(section)

    def set_play_mode(self, mode):
        """ Set the playback mode.

        :param mode: current valid values are "NORMAL" and "SHUFFLE"

        """
        self.current_play_mode = getattr(self.play_modes, mode)
        self._update_play_queue_order()

    def enqueue_audio_tracks(self, arg):
        """Search the Plex server for audio tracks and add them to the playback queue.

        :param arg: a search string

        """
        logging.info("arg : %s", arg)
        print_msg("[Plex] [Track search in server] : '{0}'. ".format(
            self.base_url))
        try:
            count = len(self.queue)

            try:
                tracks = self._music.searchTracks(title=arg)
                for track in tracks:
                    track_info = TrackInfo(track, track.artist(),
                                           track.album())
                    self._add_to_playback_queue(track_info)

            except (NotFound):
                pass

            if count == len(self.queue):
                tracks = self._music.search(libtype="track")
                for track in tracks:
                    track_name = track.title
                    if fuzz.partial_ratio(arg, track_name) > 60:
                        track_info = TrackInfo(track, track.artist(),
                                               track.album())
                        self._add_to_playback_queue(track_info)

            self._finalise_play_queue(count, arg)

        except ValueError:
            raise ValueError(str("Track not found : %s" % arg))

    def enqueue_audio_artist(self, arg):
        """Obtain an artist from the Plex server and add all the artist's audio tracks
        to the playback queue.

        :param arg: an artist search term

        """
        logging.info("arg : %s", arg)
        print_msg("[Plex] [Artist search in server] : '{0}'. ".format(
            self.base_url))
        try:
            count = len(self.queue)
            artist = None
            artist_name = ""

            try:
                artists = self._music.searchArtists(title=arg)
                for artist in artists:
                    artist_name = artist.title
                    print_wrn("[Plex] Playing '{0}'.".format(artist_name))
                    for album in artist.albums():
                        for track in album.tracks():
                            track_info = TrackInfo(track, artist, album)
                            self._add_to_playback_queue(track_info)

            except (NotFound):
                pass

            if count == len(self.queue):
                artist_dict = dict()
                artist_names = list()
                artists = self._music.search(libtype="artist")
                for art in artists:
                    artist_names.append(art.title)
                    artist_dict[art.title] = art

                if len(artist_names) > 1:
                    artist_name = process.extractOne(arg, artist_names)[0]
                    artist = artist_dict[artist_name]
                elif len(artist_names) == 1:
                    artist_name = artist_names[0]
                    artist = artist_dict[artist_name]

                if artist:
                    print_adv("[Plex] '{0}' not found. "
                              "Playing '{1}' instead.".format(
                                  arg, artist_name))
                    for album in artist.albums():
                        for track in album.tracks():
                            track_info = TrackInfo(track, artist, album)
                            self._add_to_playback_queue(track_info)

            self._finalise_play_queue(count, arg)

        except ValueError:
            raise ValueError(str("Artist not found : %s" % arg))

    def enqueue_audio_album(self, arg):
        """Obtain an album from the Plex server and add all its tracks to the playback
        queue.

        :param arg: an album search term

        """
        logging.info("arg : %s", arg)
        print_msg("[Plex] [Album search in server] : '{0}'. ".format(
            self.base_url))
        try:
            count = len(self.queue)
            album = None
            album_name = ""

            try:
                albums = self._music.searchAlbums(title=arg)
                for album in albums:
                    album_name = album.title
                    print_wrn("[Plex] Playing '{0}'.".format(album_name))
                    for track in album.tracks():
                        track_info = TrackInfo(track, track.artist(), album)
                        self._add_to_playback_queue(track_info)

            except (NotFound):
                pass

            if count == len(self.queue):
                album_dict = dict()
                album_names = list()
                albums = self._music.search(libtype="album")
                for alb in albums:
                    album_names.append(alb.title)
                    album_dict[alb.title] = alb

                if len(album_names) > 1:
                    album_name = process.extractOne(arg, album_names)[0]
                    album = album_dict[album_name]
                elif len(album_names) == 1:
                    album_name = album_names[0]
                    album = album_dict[album_name]

                if album:
                    print_adv("[Plex] '{0}' not found. "
                              "Playing '{1}' instead.".format(arg, album_name))
                    for track in album.tracks():
                        track_info = TrackInfo(track, album, album)
                        self._add_to_playback_queue(track_info)

            self._finalise_play_queue(count, arg)

        except ValueError:
            raise ValueError(str("Album not found : %s" % arg))

    def enqueue_audio_playlist(self, arg):
        """Add all audio tracks in a Plex playlist to the playback queue.

        :param arg: a playlist search term

        """
        logging.info("arg : %s", arg)
        print_msg("[Plex] [Playlist search in server] : '{0}'. ".format(
            self.base_url))
        try:
            count = len(self.queue)
            playlist_title = ""
            playlist = None

            try:
                playlist = self._plex.playlist(title=arg)
                if playlist:
                    playlist_title = playlist.title
                    print_wrn("[Plex] Playing '{0}'.".format(playlist_title))
                    for item in list(playlist.items()):
                        if item.TYPE == "track":
                            track = item
                            track_info = TrackInfo(track, track.artist(),
                                                   track.album())
                            self._add_to_playback_queue(track_info)
                        if count == len(self.queue):
                            print_wrn(
                                "[Plex] '{0}' No audio tracks found.".format(
                                    playlist_title))
                            raise ValueError

            except (NotFound):
                pass

            if count == len(self.queue):
                playlist_dict = dict()
                playlist_titles = list()
                playlists = self._plex.playlists()
                for pl in playlists:
                    playlist_titles.append(pl.title)
                    playlist_dict[pl.title] = pl

                if len(playlist_titles) > 1:
                    playlist_title = process.extractOne(arg,
                                                        playlist_titles)[0]
                    playlist = playlist_dict[playlist_title]
                elif len(playlist_titles) == 1:
                    playlist_title = playlist_titles[0]
                    playlist = playlist_dict[playlist_title]

                if playlist:
                    print_adv("[Plex] '{0}' not found. "
                              "Playing '{1}' instead.".format(
                                  arg, playlist_title))
                    for item in list(playlist.items()):
                        if item.TYPE == "track":
                            track = item
                            track_info = TrackInfo(track, track.artist(),
                                                   track.album())
                            self._add_to_playback_queue(track_info)
                        if count == len(self.queue):
                            print_wrn(
                                "[Plex] '{0}' No audio tracks found.".format(
                                    playlist_title))

            self._finalise_play_queue(count, arg)

        except (ValueError, NotFound):
            raise ValueError(
                str("Playlist not found or no audio tracks in playlist : %s" %
                    arg))

    def current_audio_track_title(self):
        """ Retrieve the current track's title.

        """
        logging.info("current_audio_track_title")
        track = self.now_playing_track
        title = ""
        if track:
            title = to_ascii(track.title)
        return title

    def current_audio_track_artist(self):
        """ Retrieve the current track's artist.

        """
        logging.info("current_audio_track_artist")
        track = self.now_playing_track
        artist = ""
        if track:
            artist = to_ascii(track.artist)
        return artist

    def current_audio_track_album(self):
        """ Retrieve the current track's album.

        """
        logging.info("current_audio_track_album")
        track = self.now_playing_track
        album = ""
        if track and track.album:
            album = to_ascii(track.album)
        return album

    def current_audio_track_year(self):
        """ Retrieve the current track's publication year.

        """
        logging.info("current_audio_track_year")
        track = self.now_playing_track
        year = 0
        if track:
            year = track.year
        return year

    def current_audio_track_file_size(self):
        """ Retrieve the current track's file size.

        """
        logging.info("current_audio_track_file_size")
        track = self.now_playing_track
        size = 0
        if track:
            size = track.size
        return size

    def current_audio_track_duration(self):
        """ Retrieve the current track's duration.

        """
        logging.info("current_audio_track_duration")
        track = self.now_playing_track
        duration = 0
        if track:
            duration = track.duration
        return duration

    def current_audio_track_bitrate(self):
        """ Retrieve the current track's bitrate.

        """
        logging.info("current_audio_track_bitrate")
        track = self.now_playing_track
        bitrate = 0
        if track:
            bitrate = track.bitrate
        return bitrate

    def current_audio_track_codec(self):
        """ Retrieve the current track's codec.

        """
        logging.info("current_audio_track_codec")
        track = self.now_playing_track
        codec = ""
        if track:
            codec = to_ascii(track.codec)
        return codec

    def current_audio_track_album_art(self):
        """ Retrieve the current track's album_art.

        """
        logging.info("current_audio_track_album_art")
        track = self.now_playing_track
        album_art = ""
        if track and track.thumb_url:
            album_art = to_ascii(track.thumb_url)
        return album_art

    def current_audio_track_queue_index_and_queue_length(self):
        """ Retrieve index in the queue (starting from 1) of the current track and the
        length of the playback queue.

        """
        logging.info("current_audio_track_queue_index_and_queue_length")
        return self.play_queue_order[self.queue_index] + 1, len(self.queue)

    def clear_queue(self):
        """ Clears the playback queue.

        """
        self.queue = list()
        self.queue_index = -1

    def print_queue(self):
        """ Print the contents of the playback queue.

        """

        for i in range(0, len(self.queue)):
            track = self.queue[self.play_queue_order[i]]
            order_num = str("#{:0{}d}".format(i + 1,
                                              len(str(len(self.queue)))))
            info_str = str("[Plex] [Track] [{0}] '{1}' [{2}] ({3})".format(
                order_num,
                to_ascii(track.title),
                to_ascii(track.artist),
                to_ascii(track.duration_str),
            ))
            print_nfo(info_str + ".")

        print_nfo("[Plex] [Tracks in queue] '{0}'.".format(len(self.queue)))

    def remove_current_url(self):
        """Remove the currently active url from the playback queue.

        """
        logging.info("")
        if len(self.queue) and self.queue_index:
            track = self.queue[self.queue_index]
            print_nfo("[Plex] [Track] '{0}' removed.".format(
                to_ascii(track["i"].title)))
            del self.queue[self.queue_index]
            self.queue_index -= 1
            if self.queue_index < 0:
                self.queue_index = 0
            self._update_play_queue_order()

    def next_url(self):
        """ Retrieve the url of the next track in the playback queue.

        """
        logging.info("")
        try:
            if len(self.queue):
                self.queue_index += 1
                if (self.queue_index < len(self.queue)) and (self.queue_index
                                                             >= 0):
                    next_track = self.queue[self.play_queue_order[
                        self.queue_index]]
                    return self._retrieve_track_url(next_track)
                else:
                    self.queue_index = -1
                    return self.next_url()
            else:
                return ""
        except (KeyError, AttributeError):
            # TODO: We don't remove this for now
            # del self.queue[self.queue_index]
            logging.info("exception")
            return self.next_url()

    def prev_url(self):
        """ Retrieve the url of the previous track in the playback queue.

        """
        logging.info("")
        try:
            if len(self.queue):
                self.queue_index -= 1
                if (self.queue_index < len(self.queue)) and (self.queue_index
                                                             >= 0):
                    prev_track = self.queue[self.play_queue_order[
                        self.queue_index]]
                    return self._retrieve_track_url(prev_track)
                else:
                    self.queue_index = len(self.queue)
                    return self.prev_url()
            else:
                return ""
        except (KeyError, AttributeError):
            # TODO: We don't remove this for now
            # del self.queue[self.queue_index]
            logging.info("exception")
            return self.prev_url()

    def get_url(self, position=None):
        """Retrieve the url on a particular position in the playback queue. If no
        position is given, the url at the current position of the playback is returned.

        """
        logging.info("get_url {}".format(position if position else "-1"))
        try:
            if len(self.queue):
                queue_pos = self.play_queue_order[self.queue_index]
                if position and position > 0 and position <= len(self.queue):
                    self.queue_index = position - 1
                    queue_pos = self.play_queue_order[self.queue_index]
                    logging.info("get_url : self.queue_index {}".format(
                        self.queue_index))
                logging.info("get_url : play_queue_order {}".format(
                    self.play_queue_order[self.queue_index]))
                track = self.queue[queue_pos]
                return self._retrieve_track_url(track)
            else:
                return ""
        except (KeyError, AttributeError):
            # TODO: We don't remove this for now
            # del self.queue[self.queue_index]
            logging.info("exception")
            return ""

    def _update_play_queue_order(self):
        """ Update the queue playback order.

        A sequential order is applied if the current play mode is "NORMAL" or a
        random order if current play mode is "SHUFFLE"

        """
        total_tracks = len(self.queue)
        if total_tracks:
            if not len(self.play_queue_order):
                # Create a sequential play order, if empty
                self.play_queue_order = list(range(total_tracks))
            if self.current_play_mode == self.play_modes.SHUFFLE:
                random.shuffle(self.play_queue_order)

    def _retrieve_track_url(self, track):
        """ Retrieve a track url

        """
        try:
            self.now_playing_track = track
            return track.url

        except AttributeError:
            logging.info("Could not retrieve the track url!")
            raise

    def _add_to_playback_queue(self, track):
        """ Add to the playback queue. """

        if not track:
            return

        self.queue.append(track)

    def _finalise_play_queue(self, count, arg):
        """ Helper function to grou the various actions needed to ready play
        queue.

        """

        if count == len(self.queue):
            logging.info("no tracks found arg : %s", arg)
            raise ValueError
        self._update_play_queue_order()
        self.print_queue()
예제 #11
0
class Plex(GObject.Object):
    __gsignals__ = {
        'login-status': (GObject.SignalFlags.RUN_FIRST, None, (bool, str)),
        'shows-latest': (GObject.SignalFlags.RUN_FIRST, None, (object, )),
        'shows-deck': (GObject.SignalFlags.RUN_FIRST, None, (object, )),
        'section-shows-deck':
        (GObject.SignalFlags.RUN_FIRST, None, (object, )),
        'download-cover': (GObject.SignalFlags.RUN_FIRST, None, (int, str)),
        'download-from-url': (GObject.SignalFlags.RUN_FIRST, None, (str, str)),
        'shows-retrieved':
        (GObject.SignalFlags.RUN_FIRST, None, (object, object)),
        'item-retrieved': (GObject.SignalFlags.RUN_FIRST, None, (object, )),
        'item-downloading':
        (GObject.SignalFlags.RUN_FIRST, None, (object, bool)),
        'sync-status': (GObject.SignalFlags.RUN_FIRST, None, (bool, )),
        'servers-retrieved': (GObject.SignalFlags.RUN_FIRST, None, (object, )),
        'sections-retrieved':
        (GObject.SignalFlags.RUN_FIRST, None, (object, )),
        'album-retrieved': (GObject.SignalFlags.RUN_FIRST, None, (object,
                                                                  object)),
        'artist-retrieved': (GObject.SignalFlags.RUN_FIRST, None, (object,
                                                                   object)),
        'playlists-retrieved': (GObject.SignalFlags.RUN_FIRST, None,
                                (object, )),
        'section-item-retrieved': (GObject.SignalFlags.RUN_FIRST, None,
                                   (object, )),
        'search-item-retrieved': (GObject.SignalFlags.RUN_FIRST, None,
                                  (str, object)),
        'connection-to-server': (GObject.SignalFlags.RUN_FIRST, None, ()),
        'logout': (GObject.SignalFlags.RUN_FIRST, None, ()),
        'loading': (GObject.SignalFlags.RUN_FIRST, None, (str, bool)),
        'sync-items': (GObject.SignalFlags.RUN_FIRST, None, (object, )),
    }

    _config = {}
    _search_provider_data = {}

    _server = None
    _account = None
    _library = None
    _sync_busy = False

    def __init__(self, config_dir, data_dir, player, **kwargs):
        super().__init__(**kwargs)
        self._settings = Gio.Settings("nl.g4d.Girens")
        self._config_dir = config_dir
        self._data_dir = data_dir
        self._player = player
        self._player.set_plex(self)
        self._config = self.__open_file(self._config_dir + '/config')
        self._search_provider_data = self.__open_file(self._config_dir +
                                                      '/search_provider_data')
        self._user_uuid = self._settings.get_string("user-uuid")
        self._token = self.get_token(self._user_uuid)
        self._server_uuid = self._settings.get_string("server-uuid")
        if (self._server_uuid is not ''):
            self._server_token = self.get_server_token(self._server_uuid)
            self._server_url = self._settings.get_string("server-url")
        else:
            self._server_token = None
            self._server_url = None

    def __open_file(self, file_path):
        if (os.path.isfile(file_path)):
            with open(file_path, 'r') as file:
                lines = file.readlines()
                return json.loads(lines[0])
        return {}

    def has_token(self):
        return self._token is not None

    def get_server_token(self, uuid):
        return Secret.password_lookup_sync(
            Secret.Schema.new("nl.g4d.Girens", Secret.SchemaFlags.NONE,
                              {'uuid': Secret.SchemaAttributeType.STRING}),
            {'uuid': uuid}, None)

    def get_token(self, uuid):
        return Secret.password_lookup_sync(
            Secret.Schema.new("nl.g4d.Girens", Secret.SchemaFlags.NONE,
                              {'uuid': Secret.SchemaAttributeType.STRING}),
            {'uuid': uuid}, None)

    def set_server_token(self, token, server_url, server_uuid, name):
        self._settings.set_string("server-url", self._server._baseurl)
        self._settings.set_string("server-uuid",
                                  self._server.machineIdentifier)
        Secret.password_store(
            Secret.Schema.new(
                "nl.g4d.Girens", Secret.SchemaFlags.NONE, {
                    'name': Secret.SchemaAttributeType.STRING,
                    'url': Secret.SchemaAttributeType.STRING,
                    'uuid': Secret.SchemaAttributeType.STRING
                }), {
                    'name': name,
                    'url': server_url,
                    'uuid': server_uuid
                }, Secret.COLLECTION_DEFAULT, 'Girens server token', token,
            None, None)

    def set_token(self, token, username, email, uuid):
        self._settings.set_string("user-uuid", uuid)
        Secret.password_store(
            Secret.Schema.new(
                "nl.g4d.Girens", Secret.SchemaFlags.NONE, {
                    'username': Secret.SchemaAttributeType.STRING,
                    'email': Secret.SchemaAttributeType.STRING,
                    'uuid': Secret.SchemaAttributeType.STRING
                }), {
                    'username': username,
                    'email': email,
                    'uuid': uuid
                }, Secret.COLLECTION_DEFAULT, 'Girens token', token, None,
            None)

    def has_url(self):
        return self._server_url is not None

    def login_token(self, token):
        try:
            self._account = MyPlexAccount(token=token)
            self.set_token(self._account._token, self._account.username,
                           self._account.email, self._account.uuid)
            self.emit('login-status', True, '')
        except:
            self.emit('login-status', False, 'Login failed')

    def login(self, username, password):
        try:
            self._account = MyPlexAccount(username, password)
            self.set_token(self._account._token, self._account.username,
                           self._account.email, self._account.uuid)
            self.emit('login-status', True, '')
        except:
            self.emit('login-status', False, 'Login failed')

    def login_with_url(self, baseurl, token):
        try:
            self.emit('loading', _('Connecting to ') + baseurl, True)
            self._server = PlexServer(baseurl, token)
            self._account = self._server.account()
            self._library = self._server.library
            self.set_server_token(self._server._token, self._server._baseurl,
                                  self._server.machineIdentifier,
                                  self._server.friendlyName)
            Secret.password_clear_sync(
                Secret.Schema.new("nl.g4d.Girens", Secret.SchemaFlags.NONE,
                                  {'uuid': Secret.SchemaAttributeType.STRING}),
                {'uuid': self._user_uuid}, None)
            self._user_uuid = None
            self._token = None
            self.emit('connection-to-server')
            self.emit('loading', 'Success', False)
            self.emit('login-status', True, '')
        except:
            self.emit('loading',
                      _('Connecting to ') + baseurl + _(' failed.'), True)
            self.emit('login-status', False, 'Login failed')
            print('connection failed (login with url)')

    def __save_config(self):
        with open(self._config_dir + '/config', 'w') as file:
            file.write(json.dumps(self._config))

    def __save_search_provider_data(self):
        with open(self._config_dir + '/search_provider_data', 'w') as file:
            file.write(json.dumps(self._search_provider_data))

    def logout(self):
        self._config = {}
        self._server = None
        self._account = None
        self._library = None
        self.__remove_login()
        self.emit('logout')

    def __remove_login(self):
        if (os.path.isfile(self._config_dir + '/config')):
            os.remove(self._config_dir + '/config')
        Secret.password_clear_sync(
            Secret.Schema.new("nl.g4d.Girens", Secret.SchemaFlags.NONE,
                              {'uuid': Secret.SchemaAttributeType.STRING}),
            {'uuid': self._server_uuid}, None)
        Secret.password_clear_sync(
            Secret.Schema.new("nl.g4d.Girens", Secret.SchemaFlags.NONE,
                              {'uuid': Secret.SchemaAttributeType.STRING}),
            {'uuid': self._user_uuid}, None)
        self._settings.set_string("server-url", '')
        self._settings.set_string("server-uuid", '')
        self._settings.set_string("user-uuid", '')

        self._user_uuid = None
        self._token = None
        self._server_uuid = None
        self._server_token = None
        self._server_url = None

    def get_latest(self):
        latest = self._library.recentlyAdded()
        self.emit('shows-latest', latest)

    def get_deck(self):
        deck = self._library.onDeck()
        self.emit('shows-deck', deck)

    def get_section_deck(self, section_id):
        deck = self._library.sectionByID(section_id).onDeck()
        self.emit('section-shows-deck', deck)

    def get_item(self, key):
        return self._server.fetchItem(int(key))

    def get_show(self, key):
        show = self._server.fetchItem(int(key))
        episodes = show.episodes()
        self.emit('shows-retrieved', show, episodes)

    def get_album(self, key):
        album = self._server.fetchItem(int(key))
        tracks = album.tracks()
        self.emit('album-retrieved', album, tracks)

    def get_artist(self, key):
        artist = self._server.fetchItem(int(key))
        albums = artist.albums()
        self.emit('artist-retrieved', artist, albums)

    def get_servers(self):
        servers = []
        if (self.has_token()):
            for resource in self._account.resources():
                if (resource.provides == 'server'):
                    servers.append(resource)
        else:
            servers.append(self._server)
        self.emit('servers-retrieved', servers)

    def get_playlists(self):
        playlists = self._server.playlists()
        self.emit('playlists-retrieved', playlists)

    def get_sections(self):
        sections = self._library.sections()
        self.emit('sections-retrieved', sections)

    def get_section_filter(self, section):
        if ('sections' in self._config
                and section.uuid in self._config['sections']
                and 'sort' in self._config['sections'][section.uuid]):
            return self._config['sections'][section.uuid]
        return None

    def get_section_items(self,
                          section,
                          container_start=0,
                          container_size=10,
                          sort=None,
                          sort_value=None):
        if (sort != None):
            if 'sections' not in self._config:
                self._config['sections'] = {}
            if section.uuid not in self._config['sections']:
                self._config['sections'][section.uuid] = {}
            self._config['sections'][section.uuid]['sort'] = sort
            self._config['sections'][section.uuid]['sort_value'] = sort_value
            self.__save_config()
            sort = sort + ':' + sort_value
        items = section.all(container_start=container_start,
                            container_size=container_size,
                            sort=sort)
        self.emit('section-item-retrieved', items)

    def reload_search_provider_data(self):
        #section = self._library.sectionByID('22')
        self._search_provider_data['sections'] = {}
        for section in self._library.sections():
            if (section.type not in ['photo', 'movie']):
                items = section.all()
                self._search_provider_data['sections'][section.uuid] = {
                    'key': section.key,
                    'server_machine_identifier':
                    self._server.machineIdentifier,
                    'title': section.title
                }
                self._search_provider_data['sections'][
                    section.uuid]['items'] = []
                for item in items:
                    self._search_provider_data['sections'][
                        section.uuid]['items'].append({
                            'title': item.title,
                            'titleSort': item.titleSort,
                            'ratingKey': item.ratingKey,
                            'type': item.type
                        })
                if (section.type == 'artist'):
                    for item in section.albums():
                        self._search_provider_data['sections'][
                            section.uuid]['items'].append({
                                'title': item.title,
                                'titleSort': item.titleSort,
                                'ratingKey': item.ratingKey,
                                'type': item.type
                            })
        self.__save_search_provider_data()

    def search_library(self, search, libtype=None):
        items = self._library.search(search, limit=10, libtype=libtype)
        self.emit('search-item-retrieved', search, items)

    def download_cover(self, key, thumb):
        url_image = self._server.transcodeImage(thumb, 300, 200)
        if (url_image is not None and url_image != ""):
            path = self.__download(url_image, 'thumb_' + str(key))
            self.emit('download-cover', key, path)

    def download_from_url(self, name_image, url_image):
        if (url_image is not None and url_image != ""):
            path = self.__download(url_image, 'thumb_' + name_image)
            self.emit('download-from-url', name_image, path)

    def play_item(self, item, shuffle=0, from_beginning=None, sort=None):
        if type(item) is str:
            item = self._server.fetchItem(item)
        parent_item = None
        if item.TYPE == "track":
            parent_item = item.album()
        playqueue = PlayQueue.create(self._server,
                                     item,
                                     shuffle=shuffle,
                                     continuous=1,
                                     parent=parent_item,
                                     sort=sort)
        self._player.set_playqueue(playqueue)
        GLib.idle_add(self.__play_item, from_beginning)

    def __play_item(self, from_beginning):
        self._player.start(from_beginning=from_beginning)

    def get_sync_items(self):
        if 'sync' in self._config:
            self.emit('sync-items', self._config['sync'])

    def remove_from_sync(self, item_key):
        if str(item_key) in self._config['sync']:
            del self._config['sync'][item_key]
            self.__save_config()
            self.get_sync_items()

    def add_to_sync(self,
                    item,
                    converted=False,
                    max_items=None,
                    only_unwatched=False):
        if 'sync' not in self._config:
            self._config['sync'] = {}
        if str(item.ratingKey) not in self._config['sync']:
            self._config['sync'][str(item.ratingKey)] = {}
            self._config['sync'][str(item.ratingKey)]['rating_key'] = str(
                item.ratingKey)
            self._config['sync'][str(item.ratingKey)]['converted'] = converted
            self._config['sync'][str(
                item.ratingKey)]['only_unwatched'] = only_unwatched
            if (max_items != None):
                self._config['sync'][str(
                    item.ratingKey)]['max_items'] = max_items
            self.__save_config()
            self.get_sync_items()
        self.sync()

    def sync(self):
        if (self._sync_busy == False):
            self.emit('sync-status', True)
            path_dir = self._data_dir + '/' + self._server.machineIdentifier
            download_files = []
            for file in os.listdir(path_dir):
                if file.startswith("item_"):
                    download_files.append(file)
            self._sync_busy = True
            if 'sync' in self._config:
                sync = self._config['sync'].copy()
                for item_keys in sync:
                    item = self._server.fetchItem(int(item_keys))

                    download_items = []
                    if (item.TYPE == 'movie' or item.TYPE == 'episode'):
                        download_items.append(item)
                    elif (item.TYPE == 'album' or item.TYPE == 'artist'):
                        download_items = item.tracks()
                    elif (item.TYPE == 'playlist'):
                        download_items = item.items()
                    elif (item.TYPE == 'show'):
                        download_items = item.episodes()
                    count = 0
                    for download_item in download_items:
                        sync_bool = False
                        if ('only_unwatched' not in sync[item_keys]):
                            sync_bool = True
                        elif (sync[item_keys]['only_unwatched'] == False):
                            sync_bool = True
                        elif (sync[item_keys]['only_unwatched'] == True
                              and (download_item.TYPE == 'movie'
                                   or download_item.TYPE == 'episode')
                              and not download_item.isWatched):
                            sync_bool = True

                        if (sync_bool == True):
                            count = count + 1
                            if ('max_items' in sync[item_keys] and
                                    count > int(sync[item_keys]['max_items'])):
                                break
                            if (self.get_item_download_path(download_item) ==
                                    None):
                                self.__download_item(
                                    download_item,
                                    converted=sync[item_keys]['converted'])
                            if ('item_' + str(download_item.ratingKey)
                                    in download_files):
                                download_files.remove(
                                    'item_' + str(download_item.ratingKey))
            for file in download_files:
                path_file = os.path.join(path_dir, file)
                if os.path.exists(path_file):
                    os.remove(path_file)
            self.emit('sync-status', False)
            self._sync_busy = False

    def __download_item(self, item, converted=False):
        path_dir = self._data_dir + '/' + self._server.machineIdentifier
        filename = 'item_' + str(item.ratingKey)
        filename_tmp = filename + '.tmp'
        path = path_dir + '/' + filename
        path_tmp = path_dir + '/' + filename_tmp

        self.emit('item-downloading', item, True)

        if not os.path.exists(path_dir):
            os.makedirs(path_dir)
        if not os.path.exists(path):
            if os.path.exists(path_tmp):
                os.remove(path_tmp)
            locations = [i for i in item.iterParts() if i]
            if (converted == False):
                download_url = self._server.url('%s?download=1' %
                                                locations[0].key)
            else:
                download_url = item.getStreamURL()
            utils.download(download_url,
                           self._server._token,
                           filename=filename_tmp,
                           savepath=path_dir,
                           session=self._server._session)
            os.rename(path_tmp, path)
            self.emit('item-downloading', item, False)

    def get_item_download_path(self, item):
        path_dir = self._data_dir + '/' + self._server.machineIdentifier
        filename = 'item_' + str(item.ratingKey)
        path = path_dir + '/' + filename
        if not os.path.exists(path):
            return None
        return path

    def mark_as_played(self, item):
        item.markWatched()
        item.reload()
        self.emit('item-retrieved', item)

    def mark_as_unplayed(self, item):
        item.markUnwatched()
        item.reload()
        self.emit('item-retrieved', item)

    def retrieve_item(self, item_key):
        item = self._server.fetchItem(int(item_key))
        self.emit('item-retrieved', item)

    def path_for_download(self, prefix):
        path_dir = self._data_dir + '/' + self._server.machineIdentifier
        path = path_dir + '/' + prefix
        return [path_dir, path]

    def __download(self, url_image, prefix):
        paths = self.path_for_download(prefix)
        path_dir = paths[0]
        path = paths[1]

        if not os.path.exists(path_dir):
            os.makedirs(path_dir)
        if not os.path.exists(path):
            parse = urllib.parse.urlparse(url_image)
            auth_user = parse.username
            auth_passwd = parse.password
            password_mgr = urllib.request.HTTPPasswordMgrWithDefaultRealm()
            password_mgr.add_password(None,
                                      parse.scheme + "://" + parse.hostname,
                                      auth_user, auth_passwd)
            handler = urllib.request.HTTPBasicAuthHandler(password_mgr)
            opener = urllib.request.build_opener(handler)
            port = ""
            if parse.port != None:
                port = ":" + str(parse.port)
            url_img_combined = parse.scheme + "://" + parse.hostname + port + parse.path + "?" + parse.query
            img_raw = opener.open(url_img_combined)
            with open(path, 'w+b') as file:
                file.write(img_raw.read())
            return path
        else:
            return path

    def connect_to_server(self):
        if (self._server_token is not None and self._server_url is not None):
            try:
                self.emit('loading',
                          _('Connecting to ') + self._server_url + '.', True)
                self._server = PlexServer(self._server_url, self._server_token)
                self._library = self._server.library
                self.set_server_token(self._server._token,
                                      self._server._baseurl,
                                      self._server.machineIdentifier,
                                      self._server.friendlyName)
                self.emit('connection-to-server')
                self.emit('loading', 'Success', False)
                return None
            except:
                self.emit(
                    'loading',
                    _('Connecting to ') + self._server_url + _(' failed.'),
                    True)
                print('custom url connection failed')

        servers_found = False
        for resource in self._account.resources():
            servers_found = True
            if ('server' in resource.provides.split(',')):
                if self.connect_to_resource(resource):
                    break

        if (servers_found == False):
            self.emit('loading', _('No servers found for this account.'), True)

    def connect_to_resource(self, resource):
        try:
            self.emit(
                'loading',
                _('Connecting to ') + resource.name + '.\n' + _('There are ') +
                str(len(resource.connections)) + _(' connection urls.') +
                '\n' + _('This may take a while'), True)
            self._server = resource.connect(ssl=self._account.secure)
            self._library = self._server.library
            self.set_server_token(self._server._token, self._server._baseurl,
                                  self._server.machineIdentifier,
                                  self._server.friendlyName)
            self.emit('connection-to-server')
            self.emit('loading', 'Success', False)
            return True
        except:
            self.emit('loading',
                      _('Connecting to ') + resource.name + _(' failed.'),
                      True)
            print('connection failed (when trying to connect to resource)')
            return False
예제 #12
0
import re

from plexapi.server import PlexServer

baseurl = 'http://plex.example.com:32400'
token = 'abcdefghijklmnopqrstuvwxyz'

plex = PlexServer(baseurl, token)

playlist_title = 'Treehouse of Horror'

for playlist in plex.playlists():
    if playlist.title == playlist_title:
        playlist.delete()
        print('{} already exists. Deleting. Will rebuild.'.format(playlist_title))

tv_shows = plex.library.section(title='TV Shows')

episode_list = []

for show in tv_shows.all():
    match = re.search(r'Simpsons', show.title)
    if match is not None:
        print('{}:'.format(show.title))
        for episode in show.episodes():
            match = re.search(r'Treehouse', episode.title)
            if match is not None:
                print('\t{}'.format(episode.title))
                episode_list += episode

print('Adding {} to playlist {}.'.format(len(episode_list), playlist_title))
예제 #13
0
def get_playlists_from_user(token):
    plex_user = PlexServer(PLEX_URL, token)
    user_playlists = plex_user.playlists()
    return user_playlists
예제 #14
0
class PlexBackend():

    def __init__(self, plexurl, token, libname, data_path, client_name):

        self.token = token
        self.plexurl = plexurl
        self.lib_name = libname
        self.data_path = data_path
        self.client_name = client_name
        self.plex = PlexServer(self.plexurl, self.token)
        self.music = self.plex.library.section(self.lib_name)
                       
    def down_plex_lib(self):
        songs = {}
        try:
            playlists = self.plex.playlists()
            songs["playlist"] = {}
            for p in playlists:
                p_name = p.title
                songs["playlist"][p_name] = []
                for track in p.items():
                    title = track.title
                    album = track.album().title
                    artist = track.artist().title
                    file_key = self.get_file(track)
                    file = self.get_tokenized_uri( file_key )
                    songs["playlist"][p_name].append([artist, album, title, file])
            root = self.music.all()
            artists = defaultdict(list)
            albums = defaultdict(list)
            titles = defaultdict(list)
            count = 0
            for artist in root:
                artist_title = artist.title
                songs[artist_title] = {}
                for album in artist.albums():
                    album_title = album.title
                    songs[artist_title][album_title] = []
                    for track in album.tracks():
                        title = track.title
                        key = track.key
                        file_key = self.get_file(track)
                        file = self.get_tokenized_uri( file_key )
                        try:
                            print("""%d 
            %s -- %s 
            %s
            %s
            %s

                            """ % (count, artist_title, album_title, title,file_key, key))
                            songs[artist_title][album_title].append([title, file, key])
                            count += 1
                        except Exception as ex:
                            print(ex)
            self.json_save(songs, self.data_path)
            print("done loading library")
        except Exception as e:
            print(e)
            return None

    def json_save(self, data, fname):
        with open(fname, 'w') as fp:
            dump(data, fp)

    def json_load(self, fname):
        with open(fname, 'r') as fp:
            return load(fp)
        
    def get_tokenized_uri(self, uri):
        return self.plexurl+uri+"?X-Plex-Token="+self.token

    def get_file(self,track):
        for media in track.media:
            for p in media.parts:
                return p.key

    def play_media(self, key, media_type):
        client = self.plex.client(self.client_name)
        item = self.plex.library.fetchItem(key)
        if media_type == "album":
            item = self.plex.library.fetchItem(item.parentKey)
            client.playMedia(item)
        elif media_type == "artist":
            item = self.plex.library.fetchItem(item.grandparentKey)
            queue = self.plex.createPlayQueue(item, shuffle = 1)
            client.playMedia(queue)
        else:
            client.playMedia(item)

    def pause(self):
        client = self.plex.client(self.client_name)
        client.pause("music")

    def next(self):
        client = self.plex.client(self.client_name)
        client.skipNext("music")

    def previous(self):
        client = self.plex.client(self.client_name)
        client.skipPrevious("music")

    def resume(self):
        client = self.plex.client(self.client_name)
        client.play("music")

    def stop(self):
        client = self.plex.client(self.client_name)
        client.stop("music")
예제 #15
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
예제 #16
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')
from plexapi.server import PlexServer
from plexapi.playlist import Playlist
import os
import sys

#PLEX INFO
url = "http://192.168.1.#:32400"
token = "##################"

if hasattr(__builtins__, 'raw_input'):
        input=raw_input

plex = PlexServer(url, token)
for i, playlist in enumerate(plex.playlists()):
        print("{position}) {playlist_title}".format(position=i+1, playlist_title=playlist.title))
choice = -1
while choice == -1:
        selection = input("Select playlist: ")
        try:
                selection = int(selection)
                if selection > 0 and selection <= i+1:
                        choice = selection - 1
                else:
                        print("Invalid selection")
        except:
                print("Invalid selection")
new_playlist_name = input("Enter new playlist name: ")
try:
    Playlist.create(plex, new_playlist_name, plex.playlists()[choice].items())
    print("{playlist} created".format(playlist=new_playlist_name))
except:
예제 #18
0
class tizplexproxy(object):
    """A class that accesses Plex servers, retrieves track URLs and creates and
    manages a playback queue.

    """
    def __init__(self, base_url, token):
        self.base_url = base_url
        self.queue = list()
        self.queue_index = -1
        self.play_queue_order = list()
        self.play_modes = TizEnumeration(["NORMAL", "SHUFFLE"])
        self.current_play_mode = self.play_modes.NORMAL
        self.now_playing_track = None
        self._plex = PlexServer(base_url, token)
        self._music = self._plex.library.section('Music')

    def set_play_mode(self, mode):
        """ Set the playback mode.

        :param mode: current valid values are "NORMAL" and "SHUFFLE"

        """
        self.current_play_mode = getattr(self.play_modes, mode)
        self.__update_play_queue_order()

    def enqueue_audio_tracks(self, arg):
        """Search the Plex server for audio tracks and add them to the playback queue.

        :param arg: a search string

        """
        logging.info('arg : %s', arg)
        print_msg("[Plex] [Track search in server] : '{0}'. " \
                  .format(self.base_url))
        try:
            count = len(self.queue)

            try:
                tracks = self._music.searchTracks(title=arg)
                for track in tracks:
                    track_info = TrackInfo(track, track.artist(),
                                           track.album())
                    self.add_to_playback_queue(track_info)

            except (NotFound):
                pass

            if count == len(self.queue):
                tracks = self._music.search(libtype='track')
                for track in tracks:
                    track_name = track.title
                    if fuzz.partial_ratio(arg, track_name) > 60:
                        track_info = TrackInfo(track, track.artist(),
                                               track.album())
                        self.add_to_playback_queue(track_info)

            if count == len(self.queue):
                raise ValueError

            self.__update_play_queue_order()

        except ValueError:
            raise ValueError(str("Track not found : %s" % arg))

    def enqueue_audio_artist(self, arg):
        """Obtain an artist from the Plex server and add all the artist's audio tracks
        to the playback queue.

        :param arg: an artist search term

        """
        logging.info('arg : %s', arg)
        print_msg("[Plex] [Artist search in server] : '{0}'. " \
                  .format(self.base_url))
        try:
            count = len(self.queue)
            artist = None
            artist_name = ''

            try:
                artists = self._music.searchArtists(title=arg)
                for artist in artists:
                    artist_name = artist.title
                    print_wrn("[Plex] Playing '{0}'." \
                              .format(artist_name.encode('utf-8')))
                    for album in artist.albums():
                        for track in album.tracks():
                            track_info = TrackInfo(track, artist, album)
                            self.add_to_playback_queue(track_info)

            except (NotFound):
                pass

            if count == len(self.queue):
                artist_dict = dict()
                artist_names = list()
                artists = self._music.search(libtype='artist')
                for art in artists:
                    artist_names.append(art.title)
                    artist_dict[art.title] = art

                if len(artist_names) > 1:
                    artist_name = process.extractOne(arg, artist_names)[0]
                    artist = artist_dict[artist_name]
                elif len(artist_names) == 1:
                    artist_name = artist_names[0]
                    artist = artist_dict[artist_name]

                if artist:
                    print_wrn("[Plex] '{0}' not found. " \
                              "Playing '{1}' instead." \
                              .format(arg.encode('utf-8'), \
                                      artist_name.encode('utf-8')))
                    for album in artist.albums():
                        for track in album.tracks():
                            track_info = TrackInfo(track, artist, album)
                            self.add_to_playback_queue(track_info)

            if count == len(self.queue):
                raise ValueError

            self.__update_play_queue_order()

        except ValueError:
            raise ValueError(str("Artist not found : %s" % arg))

    def enqueue_audio_album(self, arg):
        """Obtain an album from the Plex server and add all its tracks to the playback
        queue.

        :param arg: an album search term

        """
        logging.info('arg : %s', arg)
        print_msg("[Plex] [Album search in server] : '{0}'. " \
                  .format(self.base_url))
        try:
            count = len(self.queue)
            album = None
            album_name = ''

            try:
                albums = self._music.searchAlbums(title=arg)
                for album in albums:
                    album_name = album.title
                    print_wrn("[Plex] Playing '{0}'." \
                              .format(album_name.encode('utf-8')))
                    for track in album.tracks():
                        track_info = TrackInfo(track, track.artist(), album)
                        self.add_to_playback_queue(track_info)

            except (NotFound):
                pass

            if count == len(self.queue):
                album_dict = dict()
                album_names = list()
                albums = self._music.search(libtype='album')
                for alb in albums:
                    album_names.append(alb.title)
                    album_dict[alb.title] = alb

                if len(album_names) > 1:
                    album_name = process.extractOne(arg, album_names)[0]
                    album = album_dict[album_name]
                elif len(album_names) == 1:
                    album_name = album_names[0]
                    album = album_dict[album_name]

                if album:
                    print_wrn("[Plex] '{0}' not found. " \
                              "Playing '{1}' instead." \
                              .format(arg.encode('utf-8'), \
                                      album_name.encode('utf-8')))
                    for track in album.tracks():
                        track_info = TrackInfo(track, album, album)
                        self.add_to_playback_queue(track_info)

            if count == len(self.queue):
                raise ValueError

            self.__update_play_queue_order()

        except ValueError:
            raise ValueError(str("Album not found : %s" % arg))

    def enqueue_audio_playlist(self, arg):
        """Add all audio tracks in a Plex playlist to the playback queue.

        :param arg: a playlist search term

        """
        logging.info('arg : %s', arg)
        print_msg("[Plex] [Playlist search in server] : '{0}'. " \
                  .format(self.base_url))
        try:
            count = len(self.queue)
            playlist_title = ''
            playlist = None

            try:
                playlist = self._plex.playlist(title=arg)
                if playlist:
                    playlist_title = playlist.title
                    print_wrn("[Plex] Playing '{0}'." \
                              .format(playlist_title.encode('utf-8')))
                    for item in playlist.items():
                        if item.TYPE == 'track':
                            track = item
                            track_info = TrackInfo(track, track.artist(), \
                                                   track.album())
                            self.add_to_playback_queue(track_info)
                        if count == len(self.queue):
                            print_wrn("[Plex] '{0}' No audio tracks found." \
                                      .format(playlist_title.encode('utf-8')))
                            raise ValueError

            except (NotFound):
                pass

            if count == len(self.queue):
                playlist_dict = dict()
                playlist_titles = list()
                playlists = self._plex.playlists()
                for pl in playlists:
                    playlist_titles.append(pl.title)
                    playlist_dict[pl.title] = pl

                if len(playlist_titles) > 1:
                    playlist_title = process.extractOne(arg,
                                                        playlist_titles)[0]
                    playlist = playlist_dict[playlist_title]
                elif len(playlist_titles) == 1:
                    playlist_title = playlist_titles[0]
                    playlist = playlist_dict[playlist_title]

                if playlist:
                    print_wrn("[Plex] '{0}' not found. " \
                              "Playing '{1}' instead." \
                              .format(arg.encode('utf-8'), \
                                      playlist_title.encode('utf-8')))
                    for item in playlist.items():
                        if item.TYPE == 'track':
                            track = item
                            track_info = TrackInfo(track, track.artist(), \
                                                   track.album())
                            self.add_to_playback_queue(track_info)
                        if count == len(self.queue):
                            print_wrn("[Plex] '{0}' No audio tracks found." \
                                      .format(playlist_title.encode('utf-8')))

            if count == len(self.queue):
                raise ValueError

            self.__update_play_queue_order()

        except (ValueError, NotFound):
            raise ValueError(
                str("Playlist not found or no audio tracks in playlist : %s" %
                    arg))

    def current_audio_track_title(self):
        """ Retrieve the current track's title.

        """
        track = self.now_playing_track
        title = ''
        if track:
            title = to_ascii(track.title).encode("utf-8")
        return title

    def current_audio_track_artist(self):
        """ Retrieve the current track's artist.

        """
        track = self.now_playing_track
        artist = ''
        if track:
            artist = to_ascii(track.artist).encode("utf-8")
        return artist

    def current_audio_track_album(self):
        """ Retrieve the current track's album.

        """
        track = self.now_playing_track
        album = ''
        if track:
            album = to_ascii(track.album).encode("utf-8")
        return album

    def current_audio_track_year(self):
        """ Retrieve the current track's publication year.

        """
        track = self.now_playing_track
        year = 0
        if track:
            year = track.year
        return year

    def current_audio_track_file_size(self):
        """ Retrieve the current track's file size.

        """
        track = self.now_playing_track
        size = 0
        if track:
            size = track.size
        return size

    def current_audio_track_duration(self):
        """ Retrieve the current track's duration.

        """
        track = self.now_playing_track
        duration = 0
        if track:
            duration = track.duration
        return duration

    def current_audio_track_bitrate(self):
        """ Retrieve the current track's bitrate.

        """
        track = self.now_playing_track
        bitrate = 0
        if track:
            bitrate = track.bitrate
        return bitrate

    def current_audio_track_codec(self):
        """ Retrieve the current track's codec.

        """
        track = self.now_playing_track
        codec = ''
        if track:
            codec = to_ascii(track.codec).encode("utf-8")
        return codec

    def current_audio_track_album_art(self):
        """ Retrieve the current track's album_art.

        """
        track = self.now_playing_track
        album_art = ''
        if track:
            album_art = to_ascii(track.thumb_url).encode("utf-8")
        return album_art

    def current_audio_track_queue_index_and_queue_length(self):
        """ Retrieve index in the queue (starting from 1) of the current track and the
        length of the playback queue.

        """
        return self.play_queue_order[self.queue_index] + 1, len(self.queue)

    def clear_queue(self):
        """ Clears the playback queue.

        """
        self.queue = list()
        self.queue_index = -1

    def remove_current_url(self):
        """Remove the currently active url from the playback queue.

        """
        logging.info("")
        if len(self.queue) and self.queue_index:
            track = self.queue[self.queue_index]
            print_nfo("[Plex] [Track] '{0}' removed." \
                      .format(to_ascii(track['i'].title).encode("utf-8")))
            del self.queue[self.queue_index]
            self.queue_index -= 1
            if self.queue_index < 0:
                self.queue_index = 0
            self.__update_play_queue_order()

    def next_url(self):
        """ Retrieve the url of the next track in the playback queue.

        """
        logging.info("")
        try:
            if len(self.queue):
                self.queue_index += 1
                if (self.queue_index < len(self.queue)) \
                   and (self.queue_index >= 0):
                    next_track = self.queue[self.play_queue_order \
                                            [self.queue_index]]
                    return self.__retrieve_track_url(next_track)
                else:
                    self.queue_index = -1
                    return self.next_url()
            else:
                return ''
        except (KeyError, AttributeError):
            # TODO: We don't remove this for now
            # del self.queue[self.queue_index]
            logging.info("exception")
            return self.next_url()

    def prev_url(self):
        """ Retrieve the url of the previous track in the playback queue.

        """
        logging.info("")
        try:
            if len(self.queue):
                self.queue_index -= 1
                if (self.queue_index < len(self.queue)) \
                   and (self.queue_index >= 0):
                    prev_track = self.queue[self.play_queue_order \
                                            [self.queue_index]]
                    return self.__retrieve_track_url(prev_track)
                else:
                    self.queue_index = len(self.queue)
                    return self.prev_url()
            else:
                return ''
        except (KeyError, AttributeError):
            # TODO: We don't remove this for now
            # del self.queue[self.queue_index]
            logging.info("exception")
            return self.prev_url()

    def __update_play_queue_order(self):
        """ Update the queue playback order.

        A sequential order is applied if the current play mode is "NORMAL" or a
        random order if current play mode is "SHUFFLE"

        """
        total_tracks = len(self.queue)
        if total_tracks:
            if not len(self.play_queue_order):
                # Create a sequential play order, if empty
                self.play_queue_order = range(total_tracks)
            if self.current_play_mode == self.play_modes.SHUFFLE:
                random.shuffle(self.play_queue_order)
            print_nfo("[Plex] [Tracks in queue] '{0}'." \
                      .format(total_tracks))

    def __retrieve_track_url(self, track):
        """ Retrieve a track url

        """
        try:
            self.now_playing_track = track
            return track.url.encode("utf-8")

        except AttributeError:
            logging.info("Could not retrieve the track url!")
            raise

    def add_to_playback_queue(self, track):
        """ Add to the playback queue. """

        print_nfo("[Plex] [Track] '{0}' [{1}]." \
                  .format(to_ascii(track.title).encode("utf-8"), \
                          to_ascii(track.codec)))
        queue_index = len(self.queue)
        self.queue.append(track)
예제 #19
0
class PlexInstance:
    def __init__(self,
                 url: str,
                 token: str,
                 api,
                 server_name: str = None,
                 server_alt_name: str = None,
                 server_number: int = 0,
                 credentials_folder: str = None,
                 tautulli_info=None,
                 ombi_info=None,
                 libraries=None):
        # Server info
        self.url = url
        self.token = token
        if not url or not token:
            raise Exception("Must include Plex Media Server url and token")
        self._api = api
        self.server = PlexServer(self.url, self.token)
        self.name = server_name if server_name else self.server.friendlyName
        self.alt_name = server_alt_name if server_alt_name else self.server.friendlyName
        self.number = server_number
        self.id = self.server.machineIdentifier

        # Auth
        self.auth_header = {'X-Plex-Token': token}
        self.cloud_key = None

        # Crypt
        self.crypt = None
        if credentials_folder:
            self.crypt = Encryption(key_file=f'{credentials_folder}/key.txt', key_folder=credentials_folder)

        # Libraries
        self.shows = defaultdict(list)
        self.movies = defaultdict(list)
        self.libraries = libraries

        # Ombi
        self.use_ombi = False
        self.ombi = None
        if ombi_info:
            self.set_ombi_connection(ombi_info)

        # Tautulli
        self.use_tautulli = False
        self.tautulli = None
        if tautulli_info:
            self.set_tautulli_connection(tautulli_info)


    def set_tautulli_connection(self, tautulli_info):
        self.use_tautulli = tautulli_info.get('enable', False)
        if self.use_tautulli:
            self.tautulli = TautulliConnector(url=tautulli_info.get('url'), api_key=tautulli_info.get('api_key'))


    def set_ombi_connection(self, ombi_info):
        self.use_ombi = ombi_info.get('enable', False)
        if self.use_ombi:
            self.ombi = OmbiConnector(url=ombi_info.get('url'), api_key=ombi_info.get('api_key'))


    def get_user_creds(self, user_id) -> dict:
        if self.crypt:
            creds_dict = {'username': None, 'password': None}
            if self.crypt.exists(f"{user_id}.json"):
                creds = self.crypt.decrypt_file(f'{self.crypt.key_folder}/{user_id}.json').splitlines()
                creds_dict = {'username': creds[0], 'password': creds[1]}
            return creds_dict
        return {}

    def ping(self) -> bool:
        response = requests.get(f"{self.url}/identity", timeout=10)
        if response:
            return True
        return False

    def save_user_creds(self, user_id, username, password) -> bool:
        if self.crypt:
            text = '{}\n{}'.format(username, password)
            return self.crypt.encrypt_file(text=text, filename=f'{self.crypt.key_folder}/{user_id}.json')
        return False


    def get_media_item(self, title: str, rating_key=None, library_id=None):
        library = self.server.library
        if library_id:
            library = library.sectionByID(str(library_id))
        results = library.search(title=title)
        if results:
            if rating_key:  # find exact match
                for item in results:
                    if item.ratingKey == rating_key:
                        return item
            return results[0]  # assume first result is correct
        return None


    def get_watch_now_link(self, rating_key: str) -> str:
        return f"https://app.plex.tv/desktop#!/server/{self.id}//details?key=%2Flibrary%2Fmetadata%2F{rating_key}"


    def get_media_info(self, rating_key) -> Tuple[str, str]:
        r = requests.get(f'{self.url}/library/metadata/{rating_key}?X-Plex-Token={self.token}').content
        tree = ET.fromstring(r)
        return tree.get('librarySectionID'), tree[0].get('title')


    def get_rating_key(self, url = None) -> str:
        if not url:
            url = self.url
        return str(re.search('metadata%2F(\d*)', url).group(1))


    def find_url(self, text) -> str:
        pattern = '{}\S*'.format(self.url.replace('.', '\.'))
        return str(re.search(pattern, text).group(0))


    def get_playlist(self, playlist_name):
        for playlist in self.server.playlists():
            if playlist.title == playlist_name:
                return playlist
        return None


    def add_to_playlist(self, playlist_title, rating_key, item_to_add) -> str:
        playlist = self.get_playlist(playlist_name=playlist_title)
        if playlist:
            for item in playlist.items():
                if str(item.ratingKey) == str(rating_key):
                    return "That item is already on your {}list".format(
                        'play' if item_to_add.type in ['artist', 'track', 'album'] else 'watch')
            playlist.addItems([item_to_add])
            return "Item added to your {}list".format(
                'play' if item_to_add.type in ['artist', 'track', 'album'] else 'watch')
        else:
            self.server.createPlaylist(title=playlist_title, items=[item_to_add])
            return "New {}list created and item added.".format(
                'play' if item_to_add.type in ['artist', 'track', 'album'] else 'watch')


    def url_in_message(self, message) -> Union[str, None]:
        server_id = self.server.machineIdentifier
        if server_id in message.content and 'metadata%2F' in message.content:
            return self.find_url(text=message.content)
        if message.embeds:
            for embed in message.embeds:
                if server_id in embed.title and 'metadata%2F' in embed.title:
                    return self.find_url(text=embed.title)
                elif server_id in embed.description and 'metadata%2F' in embed.description:
                    return self.find_url(embed.description)
                elif server_id in embed.description and 'metadata%2F' in embed.url:
                    return self.find_url(embed.url)
            return None
        return None


    @property
    def sub_count(self) -> int:
        count = 0
        for user in self.server.myPlexAccount().users():
            for server in user.servers:
                if server.name in [self.name, self.alt_name]:
                    count += 1
                    break
        return count


    @property
    def users(self) -> List[MyPlexUser]:
        try:
            return self.server.myPlexAccount().users()
        except Exception as e:
            print(f"Error in getServerUsers: {e}")
        return []


    def get_user(self, username) -> Union[MyPlexUser, None]:
        try:
            return self.server.myPlexAccount().user(username=username)
        except Exception as e:
            print(f"Error in getServerUser: {e}")
        return None


    @property
    def plex_friends(self) -> List[MyPlexUser]:
        """
        # Returns all Plex Friends (access in + access out)
        """
        friends = []
        for user in self.users:
            if user.friend:
                friends.append(user)
        return friends

    def user_has_access(self, plex_username: str) -> bool:
        for user in self.users:
            if user.username == plex_username:
                return True
        return False


    def add_user(self, plex_username: str) -> utils.StatusResponse:
        try:
            self.server.myPlexAccount().inviteFriend(user=plex_username, server=self.server,
                                                     sections=None,
                                                     allowSync=False,
                                                     allowCameraUpload=False,
                                                     allowChannels=False,
                                                     filterMovies=None,
                                                     filterTelevision=None,
                                                     filterMusic=None)
            return utils.StatusResponse(success=True)
        except plex_exceptions.NotFound:
            return utils.StatusResponse(success=False, issue="Invalid Plex username")
        except Exception as e:
            return utils.StatusResponse(success=False, issue=e.__str__())


    def remove_user(self, plex_username: str) -> utils.StatusResponse:
        try:
            self.server.myPlexAccount().removeFriend(user=plex_username)
            return utils.StatusResponse(success=True)
        except plex_exceptions.NotFound:
            return utils.StatusResponse(success=False, issue="Invalid Plex username")
        except Exception as e:
            return utils.StatusResponse(success=False, issue=e.__str__())


    def refresh_tautulli_users(self) -> bool:
        if self.use_tautulli:
            return self.tautulli.refresh_users()
        return False


    def delete_user_from_tautulli(self, plex_username) -> bool:
        if self.use_tautulli:
            return self.tautulli.delete_user(plex_username=plex_username)
        return False


    def refresh_ombi_users(self) -> bool:
        if self.use_ombi:
            return self.ombi.refresh_users()
        return False


    def delete_user_from_ombi(self, username: str) -> bool:
        if self.use_ombi:
            return self.ombi.delete_user(plex_username=username)
        return False


    def get_live_tv_dvrs(self):
        data = self._get(hdr=self.auth_header, endpoint='/livetv/dvrs')
        if data:
            if data.get('MediaContainer').get('Dvr'):
                return [DVR(item) for item in data.get('MediaContainer').get('Dvr')]
        return None


    def get_cloud_key(self):
        if not self.cloud_key:
            data = self._get(hdr=self.auth_header, endpoint='/tv.plex.providers.epg.cloud')
            if data:
                self.cloud_key = data.get('MediaContainer').get('Directory')[1].get('title')
            else:
                return None
        return self.cloud_key


    def get_live_tv_sessions(self):
        data = self._get(hdr=self.auth_header, endpoint='/livetv/sessions')
        if data:
            if data.get('MediaContainer').get('Metadata'):
                return [TVSession(item) for item in data.get('MediaContainer').get('Metadata')]
        return None


    def get_hubs(self, identifier=None):
        data = self._get(hdr=self.auth_header, endpoint=f'/{self.get_cloud_key()}/hubs/discover')
        if data:
            if identifier:
                for hub in data['MediaContainer']['Hub']:
                    if hub['title'] == identifier:
                        return Hub(hub)
                return None
            return [Hub(hub) for hub in data['MediaContainer']['Hub']]
        return None


    def get_dvr_schedule(self):
        data = self._get(hdr=self.auth_header, endpoint='/media/subscriptions/scheduled')
        if data:
            return DVRSchedule(data.get('MediaContainer'))
        return None


    def get_dvr_items(self):
        data = self._get(hdr=self.auth_header, endpoint='/media/subscriptions')
        if data:
            return [DVRItem(item) for item in data.get('MediaContainer').get('MediaSubscription')]
        return None


    def delete_dvr_item(self, itemID):
        data = self._delete(hdr=self.auth_header, endpoint='/media/subscription/{}'.format(itemID))
        if str(data.status_code).startswith('2'):
            return True
        return False


    def get_homepage_items(self):
        data = self._get(hdr=self.auth_header, endpoint='/hubs')
        if data:
            return [Hub(item) for item in data.get('MediaContainer').get('Hub')]
        return None


    def _get(self, hdr, endpoint, data=None):
        """ Returns JSON """
        hdr = {'accept': 'application/json', **hdr}
        res = requests.get(f'{self.url}{endpoint}', headers=hdr, data=json.dumps(data)).json()
        return res


    def _post(self, hdr, endpoint, data=None) -> requests.Response:
        """ Returns response """
        hdr = {'accept': 'application/json', **hdr}
        res = requests.post(f'{self.url}{endpoint}', headers=hdr, data=json.dumps(data))
        return res


    def _delete(self, hdr, endpoint, data=None) -> requests.Response:
        """ Returns response """
        hdr = {'accept': 'application/json', **hdr}
        res = requests.delete(f'{self.url}{endpoint}', headers=hdr, data=json.dumps(data))
        return res


    def get_defined_libraries(self):
        names = [name for name in self.libraries.keys()]
        ids = []
        for _, vs in self.libraries.items():
            for v in vs:
                if v not in ids:
                    ids.append(str(v))
        return {'names': names, 'IDs': ids}


    def get_plex_share(self, share_name_or_number):
        if not self.libraries:
            return False
        if utils.is_positive_int(share_name_or_number):
            return [int(share_name_or_number)]
        else:
            for name, numbers in self.libraries.items():
                if name == share_name_or_number:
                    return numbers
            return False


    def _get_server_from_user_share(self, plex_user):
        try:
            return plex_user.server(self.name)
        except:
            return plex_user.server(self.alt_name)


    def get_user_restrictions(self, plex_username):
        user = self.get_user(username=plex_username)
        if user:
            if user.friend:
                try:
                    sections = self._get_server_from_user_share(plex_user=user).sections()
                except:
                    raise Exception("Could not load Plex user sections.")
                return {'allowSync': user.allowSync,
                        'filterMovies': user.filterMovies,
                        'filterShows': user.filterTelevision,
                        'sections': ([section.title for section in sections] if sections else [])
                        }
            else:
                raise Exception(f"Plex user {plex_username} is not a Plex Friend.")
        else:
            raise Exception(f"Could not locate Plex user: {plex_username}")


    def update_user_restrictions(self, plex_username, sections_to_share=[], rating_limit={}, allow_sync: bool = None) -> bool:
        """
        :param plex_username:
        :param sections_to_share:
        :param rating_limit: ex. {'Movie': 'PG-13', 'TV': 'TV-14'}
        :param allow_sync:
        :return:
        """
        try:
            sections = []
            for section in sections_to_share:
                section_numbers = self.get_plex_share(share_name_or_number=section)
                if section_numbers:
                    for number in section_numbers:
                        sections.append(str(number))
            allowed_movie_ratings = []
            allowed_tv_ratings = []
            if rating_limit:
                # add max rating and all below it to allowed ratings
                # if non_existent rating is used as limit, all ratings will be added
                if rating_limit.get('Movie') and rating_limit.get('Movie') in all_movie_ratings:
                    for rating in all_movie_ratings:
                        allowed_movie_ratings.append(rating)
                        if rating == rating_limit.get('Movie'):
                            break
                if rating_limit.get('TV') and rating_limit.get('TV') in all_tv_ratings:
                    for rating in all_tv_ratings:
                        allowed_tv_ratings.append(rating)
                        if rating == rating_limit.get('TV'):
                            break
            self.server.myPlexAccount().updateFriend(user=plex_username,
                                                     server=self.server,
                                                     sections=(sections if sections else None),
                                                     removeSections=False,
                                                     allowSync=allow_sync,
                                                     allowCameraUpload=None,
                                                     allowChannels=None,
                                                     filterMovies=(
                                                         {
                                                             'contentRating': allowed_movie_ratings} if allowed_movie_ratings else None
                                                     ),
                                                     filterTelevision=(
                                                         {
                                                             'contentRating': allowed_tv_ratings} if allowed_tv_ratings else None
                                                     ),
                                                     filterMusic=None)
            return True
        except:
            print(f"Could not update restrictions for Plex user: {plex_username}")
            return False
예제 #20
0
class tizplexproxy(object):
    """A class that accesses Plex servers, retrieves track URLs and creates and
    manages a playback queue.

    """

    def __init__(self, base_url, token):
        self.base_url = base_url
        self.queue = list()
        self.queue_index = -1
        self.play_queue_order = list()
        self.play_modes = TizEnumeration(["NORMAL", "SHUFFLE"])
        self.current_play_mode = self.play_modes.NORMAL
        self.now_playing_track = None
        self._plex = PlexServer(base_url, token)
        self._music = self._plex.library.section('Music')

    def set_play_mode(self, mode):
        """ Set the playback mode.

        :param mode: current valid values are "NORMAL" and "SHUFFLE"

        """
        self.current_play_mode = getattr(self.play_modes, mode)
        self.__update_play_queue_order()

    def enqueue_audio_tracks(self, arg):
        """Search the Plex server for audio tracks and add them to the playback queue.

        :param arg: a search string

        """
        logging.info('arg : %s', arg)
        print_msg("[Plex] [Track search in server] : '{0}'. " \
                  .format(self.base_url))
        try:
            count = len(self.queue)

            try:
                tracks = self._music.searchTracks(title=arg)
                for track in tracks:
                    track_info = TrackInfo(track, track.artist(), track.album())
                    self.add_to_playback_queue(track_info)

            except (NotFound):
                pass

            if count == len(self.queue):
                tracks = self._music.search(libtype='track')
                for track in tracks:
                    track_name = track.title
                    if fuzz.partial_ratio(arg, track_name) > 60:
                        track_info = TrackInfo(track, track.artist(), track.album())
                        self.add_to_playback_queue(track_info)

            if count == len(self.queue):
                raise ValueError

            self.__update_play_queue_order()

        except ValueError:
            raise ValueError(str("Track not found : %s" % arg))

    def enqueue_audio_artist(self, arg):
        """Obtain an artist from the Plex server and add all the artist's audio tracks
        to the playback queue.

        :param arg: an artist search term

        """
        logging.info('arg : %s', arg)
        print_msg("[Plex] [Artist search in server] : '{0}'. " \
                  .format(self.base_url))
        try:
            count = len(self.queue)
            artist = None
            artist_name = ''

            try:
                artists = self._music.searchArtists(title=arg)
                for artist in artists:
                    artist_name = artist.title
                    print_wrn("[Plex] Playing '{0}'." \
                              .format(artist_name.encode('utf-8')))
                    for album in artist.albums():
                        for track in album.tracks():
                            track_info = TrackInfo(track, artist, album)
                            self.add_to_playback_queue(track_info)

            except (NotFound):
                pass

            if count == len(self.queue):
                artist_dict = dict()
                artist_names = list()
                artists = self._music.search(libtype='artist')
                for art in artists:
                    artist_names.append(art.title)
                    artist_dict[art.title] = art

                if len(artist_names) > 1:
                    artist_name = process.extractOne(arg, artist_names)[0]
                    artist = artist_dict[artist_name]
                elif len(artist_names) == 1:
                    artist_name = artist_names[0]
                    artist = artist_dict[artist_name]

                if artist:
                    print_wrn("[Plex] '{0}' not found. " \
                              "Playing '{1}' instead." \
                              .format(arg.encode('utf-8'), \
                                      artist_name.encode('utf-8')))
                    for album in artist.albums():
                        for track in album.tracks():
                            track_info = TrackInfo(track, artist, album)
                            self.add_to_playback_queue(track_info)

            if count == len(self.queue):
                raise ValueError

            self.__update_play_queue_order()

        except ValueError:
            raise ValueError(str("Artist not found : %s" % arg))

    def enqueue_audio_album(self, arg):
        """Obtain an album from the Plex server and add all its tracks to the playback
        queue.

        :param arg: an album search term

        """
        logging.info('arg : %s', arg)
        print_msg("[Plex] [Album search in server] : '{0}'. " \
                  .format(self.base_url))
        try:
            count = len(self.queue)
            album = None
            album_name = ''

            try:
                albums = self._music.searchAlbums(title=arg)
                for album in albums:
                    album_name = album.title
                    print_wrn("[Plex] Playing '{0}'." \
                              .format(album_name.encode('utf-8')))
                    for track in album.tracks():
                        track_info = TrackInfo(track, track.artist(), album)
                        self.add_to_playback_queue(track_info)

            except (NotFound):
                pass

            if count == len(self.queue):
                album_dict = dict()
                album_names = list()
                albums = self._music.search(libtype='album')
                for alb in albums:
                    album_names.append(alb.title)
                    album_dict[alb.title] = alb

                if len(album_names) > 1:
                    album_name = process.extractOne(arg, album_names)[0]
                    album = album_dict[album_name]
                elif len(album_names) == 1:
                    album_name = album_names[0]
                    album = album_dict[album_name]

                if album:
                    print_wrn("[Plex] '{0}' not found. " \
                              "Playing '{1}' instead." \
                              .format(arg.encode('utf-8'), \
                                      album_name.encode('utf-8')))
                    for track in album.tracks():
                        track_info = TrackInfo(track, album, album)
                        self.add_to_playback_queue(track_info)

            if count == len(self.queue):
                raise ValueError

            self.__update_play_queue_order()

        except ValueError:
            raise ValueError(str("Album not found : %s" % arg))

    def enqueue_audio_playlist(self, arg):
        """Add all audio tracks in a Plex playlist to the playback queue.

        :param arg: a playlist search term

        """
        logging.info('arg : %s', arg)
        print_msg("[Plex] [Playlist search in server] : '{0}'. " \
                  .format(self.base_url))
        try:
            count = len(self.queue)
            playlist_title = ''
            playlist = None

            try:
                playlist = self._plex.playlist(title=arg)
                if playlist:
                    playlist_title = playlist.title
                    print_wrn("[Plex] Playing '{0}'." \
                              .format(playlist_title.encode('utf-8')))
                    for item in playlist.items():
                        if item.TYPE == 'track':
                            track = item
                            track_info = TrackInfo(track, track.artist(), \
                                                   track.album())
                            self.add_to_playback_queue(track_info)
                        if count == len(self.queue):
                            print_wrn("[Plex] '{0}' No audio tracks found." \
                                      .format(playlist_title.encode('utf-8')))
                            raise ValueError

            except (NotFound):
                pass

            if count == len(self.queue):
                playlist_dict = dict()
                playlist_titles = list()
                playlists = self._plex.playlists()
                for pl in playlists:
                    playlist_titles.append(pl.title)
                    playlist_dict[pl.title] = pl

                if len(playlist_titles) > 1:
                    playlist_title = process.extractOne(arg, playlist_titles)[0]
                    playlist = playlist_dict[playlist_title]
                elif len(playlist_titles) == 1:
                    playlist_title = playlist_titles[0]
                    playlist = playlist_dict[playlist_title]

                if playlist:
                    print_wrn("[Plex] '{0}' not found. " \
                              "Playing '{1}' instead." \
                              .format(arg.encode('utf-8'), \
                                      playlist_title.encode('utf-8')))
                    for item in playlist.items():
                        if item.TYPE == 'track':
                            track = item
                            track_info = TrackInfo(track, track.artist(), \
                                                   track.album())
                            self.add_to_playback_queue(track_info)
                        if count == len(self.queue):
                            print_wrn("[Plex] '{0}' No audio tracks found." \
                                      .format(playlist_title.encode('utf-8')))

            if count == len(self.queue):
                raise ValueError

            self.__update_play_queue_order()

        except (ValueError, NotFound):
            raise ValueError(str("Playlist not found or no audio tracks in playlist : %s" % arg))

    def current_audio_track_title(self):
        """ Retrieve the current track's title.

        """
        track = self.now_playing_track
        title = ''
        if track:
            title = to_ascii(track.title).encode("utf-8")
        return title

    def current_audio_track_artist(self):
        """ Retrieve the current track's artist.

        """
        track = self.now_playing_track
        artist = ''
        if track:
            artist = to_ascii(track.artist).encode("utf-8")
        return artist

    def current_audio_track_album(self):
        """ Retrieve the current track's album.

        """
        track = self.now_playing_track
        album = ''
        if track:
            album = to_ascii(track.album).encode("utf-8")
        return album

    def current_audio_track_year(self):
        """ Retrieve the current track's publication year.

        """
        track = self.now_playing_track
        year = 0
        if track:
            year = track.year
        return year

    def current_audio_track_file_size(self):
        """ Retrieve the current track's file size.

        """
        track = self.now_playing_track
        size = 0
        if track:
            size = track.size
        return size

    def current_audio_track_duration(self):
        """ Retrieve the current track's duration.

        """
        track = self.now_playing_track
        duration = 0
        if track:
            duration = track.duration
        return duration

    def current_audio_track_bitrate(self):
        """ Retrieve the current track's bitrate.

        """
        track = self.now_playing_track
        bitrate = 0
        if track:
            bitrate = track.bitrate
        return bitrate

    def current_audio_track_codec(self):
        """ Retrieve the current track's codec.

        """
        track = self.now_playing_track
        codec = ''
        if track:
            codec = to_ascii(track.codec).encode("utf-8")
        return codec

    def current_audio_track_album_art(self):
        """ Retrieve the current track's album_art.

        """
        track = self.now_playing_track
        album_art = ''
        if track:
            album_art = to_ascii(track.thumb_url).encode("utf-8")
        return album_art

    def current_audio_track_queue_index_and_queue_length(self):
        """ Retrieve index in the queue (starting from 1) of the current track and the
        length of the playback queue.

        """
        return self.play_queue_order[self.queue_index] + 1, len(self.queue)

    def clear_queue(self):
        """ Clears the playback queue.

        """
        self.queue = list()
        self.queue_index = -1

    def remove_current_url(self):
        """Remove the currently active url from the playback queue.

        """
        logging.info("")
        if len(self.queue) and self.queue_index:
            track = self.queue[self.queue_index]
            print_nfo("[Plex] [Track] '{0}' removed." \
                      .format(to_ascii(track['i'].title).encode("utf-8")))
            del self.queue[self.queue_index]
            self.queue_index -= 1
            if self.queue_index < 0:
                self.queue_index = 0
            self.__update_play_queue_order()

    def next_url(self):
        """ Retrieve the url of the next track in the playback queue.

        """
        logging.info("")
        try:
            if len(self.queue):
                self.queue_index += 1
                if (self.queue_index < len(self.queue)) \
                   and (self.queue_index >= 0):
                    next_track = self.queue[self.play_queue_order \
                                            [self.queue_index]]
                    return self.__retrieve_track_url(next_track)
                else:
                    self.queue_index = -1
                    return self.next_url()
            else:
                return ''
        except (KeyError, AttributeError):
            # TODO: We don't remove this for now
            # del self.queue[self.queue_index]
            logging.info("exception")
            return self.next_url()

    def prev_url(self):
        """ Retrieve the url of the previous track in the playback queue.

        """
        logging.info("")
        try:
            if len(self.queue):
                self.queue_index -= 1
                if (self.queue_index < len(self.queue)) \
                   and (self.queue_index >= 0):
                    prev_track = self.queue[self.play_queue_order \
                                            [self.queue_index]]
                    return self.__retrieve_track_url(prev_track)
                else:
                    self.queue_index = len(self.queue)
                    return self.prev_url()
            else:
                return ''
        except (KeyError, AttributeError):
            # TODO: We don't remove this for now
            # del self.queue[self.queue_index]
            logging.info("exception")
            return self.prev_url()

    def __update_play_queue_order(self):
        """ Update the queue playback order.

        A sequential order is applied if the current play mode is "NORMAL" or a
        random order if current play mode is "SHUFFLE"

        """
        total_tracks = len(self.queue)
        if total_tracks:
            if not len(self.play_queue_order):
                # Create a sequential play order, if empty
                self.play_queue_order = range(total_tracks)
            if self.current_play_mode == self.play_modes.SHUFFLE:
                random.shuffle(self.play_queue_order)
            print_nfo("[Plex] [Tracks in queue] '{0}'." \
                      .format(total_tracks))

    def __retrieve_track_url(self, track):
        """ Retrieve a track url

        """
        try:
            self.now_playing_track = track
            return track.url.encode("utf-8")

        except AttributeError:
            logging.info("Could not retrieve the track url!")
            raise

    def add_to_playback_queue(self, track):
        """ Add to the playback queue. """

        print_nfo("[Plex] [Track] '{0}' [{1}]." \
                  .format(to_ascii(track.title).encode("utf-8"), \
                          to_ascii(track.codec)))
        queue_index = len(self.queue)
        self.queue.append(track)
예제 #21
0
import os, sys
from plexapi.server import PlexServer, CONFIG
from plexapi import utils

## Edit ##
PLEX_URL = ''
PLEX_TOKEN = ''
PLEX_URL = CONFIG.data['auth'].get('server_baseurl', PLEX_URL)
PLEX_TOKEN = CONFIG.data['auth'].get('server_token', PLEX_TOKEN)

plex = PlexServer(PLEX_URL, PLEX_TOKEN)

playlists = [pl for pl in plex.playlists() if pl.isAudio]
playlist = None
if sys.argv[1]:
    for pl in playlists:
        if pl.title == sys.argv[1]:
            playlist = pl
    if playlist is None:
        print("Invalid playlist '{}'".format(sys.argv[1]))
        sys.exit()
else:
    playlist = utils.choose('Playlist to export', playlists,
                            lambda pl: '%s' % pl.title)

for song in playlist.items():
    print(song.media[0].parts[0].file)
예제 #22
0
class PlexBackend():
    def __init__(self, plexurl, token, libname, data_path):

        self.token = token
        self.plexurl = plexurl
        self.lib_name = libname
        self.data_path = data_path
        self.plex = PlexServer(self.plexurl, self.token)
        self.music = self.plex.library.section(self.lib_name)

    def down_plex_lib(self):
        songs = {}
        try:
            playlists = self.plex.playlists()
            songs["playlist"] = {}
            for p in playlists:
                p_name = p.title
                songs["playlist"][p_name] = []
                for track in p.items():
                    title = track.title
                    album = track.album().title
                    artist = track.artist().title
                    file_key = self.get_file(track)
                    file = self.get_tokenized_uri(file_key)
                    songs["playlist"][p_name].append(
                        [artist, album, title, file])
            root = self.music.all()
            artists = defaultdict(list)
            albums = defaultdict(list)
            titles = defaultdict(list)
            count = 0
            for artist in root:
                artist_title = artist.title
                songs[artist_title] = {}
                for album in artist.albums():
                    album_title = album.title
                    songs[artist_title][album_title] = []
                    for track in album.tracks():
                        title = track.title
                        file_key = self.get_file(track)
                        file = self.get_tokenized_uri(file_key)
                        try:
                            print("""%d 
            %s -- %s 
            %s
            %s

                            """ % (count, artist_title, album_title, title,
                                   file_key))
                            songs[artist_title][album_title].append(
                                [title, file])
                            count += 1
                        except Exception as ex:
                            print(ex)
            self.json_save(songs, self.data_path)
            print("done loading library")
        except Exception as e:
            print(e)
            return None

    def json_save(self, data, fname):
        with open(fname, 'w') as fp:
            dump(data, fp)

    def json_load(self, fname):
        with open(fname, 'r') as fp:
            return load(fp)

    def get_tokenized_uri(self, uri):
        return self.plexurl + uri + "?X-Plex-Token=" + self.token

    def get_file(self, track):
        for media in track.media:
            for p in media.parts:
                return p.key
예제 #23
0
import os
from plexapi.server import PlexServer
from plexapi import utils

baseurl = 'https://plx.w00t.cloud'
token = 'H6gqeSNE3yGthe72x1w7'
plex = PlexServer(baseurl, token)

playlists = [pl for pl in plex.playlists()]
playlist = utils.choose('Choose Playlist', playlists,
                        lambda pl: '%s' % pl.title)

print(len(playlist.items()))
for photo in playlist.items():
    photomediapart = photo.media[0].parts[0]
    print('Download File: %s' % photomediapart.file)
    url = plex.url('%s?download=1' % photomediapart.key)
    utils.download(url, token, os.path.basename(photomediapart.file))
예제 #24
0
# If verify is set to a path to a directory,
# the directory must have been processed using the c_rehash utility supplied
# with OpenSSL.
if sess.verify is False:
    # Disable the warning that the request is insecure, we know that...
    import urllib3
    urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

plex = PlexServer(PLEX_URL, PLEX_TOKEN, session=sess)
account = plex.myPlexAccount()

user_lst = [x.title for x in plex.myPlexAccount().users() if x.servers and x.friend]
sections = plex.library.sections()
sections_dict = {x.key: x.title for x in sections}
filters_lst = list(set([y for x in sections if x.type != 'photo' for y in x.ALLOWED_FILTERS]))
playlist_lst = [x.title for x in plex.playlists()]
today = datetime.datetime.now().date()
weeknum = datetime.date(today.year, today.month, today.day).isocalendar()[1]


def actions():
    """
    add - create new playlist for admin or users
    remove - remove playlist type or name from admin or users
    update - remove playlist type and create new playlist type for admin or users
    show - show contents of playlist type or admin or users current playlists
    share - share existing playlist by title from admin to users
    """
    return ['add', 'remove', 'update', 'show', 'share']

예제 #25
0
# Bulk Download of photos from PLEX playlist

import os
from plexapi.server import PlexServer
from plexapi import utils

baseurl = 'http://localhost:32400'
token = 'PLACE TOKEN HERE'
plex = PlexServer(baseurl, token)

playlists = [pl for pl in plex.playlists() if pl.isPhoto]
playlist = utils.choose('Choose Playlist', playlists,
                        lambda pl: '%s' % pl.title)

for photo in playlist.items():
    photomediapart = photo.media[0].parts[0]

    if photo.year == 2018 or photo.year == 2019:
        print('Downlod File: %s' % photomediapart.file)
        url = plex.url('%s?download=1' % photomediapart.key)
        utils.download(url, token, os.path.basename(photomediapart.file),
                       '%s' % photo.year)
    else:
        print('Skip File: %s | %s' % (photomediapart.file, photo.year))