예제 #1
0
def main():
    args = get_args()
    if args.debug:
        logger.setLevel(logging.DEBUG)
    if args.account:
        # ## Connect via Account
        account = MyPlexAccount(args.username, args.password)
        plex = account.resource(args.resource).connect()
    elif args.server:
        # ## Connect via Direct URL
        baseurl = args.baseurl
        token = args.token
        plex = PlexServer(baseurl, token)
    else:
        exit(1)

    all_shows = plex.library.section('TV Shows')
    # shows = get_unwatched_shows(all_shows.all())
    episodes = get_random_episodes(all_shows, n=args.number)
    for episode in episodes:
        season_episode = episode.seasonEpisode
        # skipped = skipped_missing(all_shows.get(title=episode.grandparentTitle), episode)

    # playlist = Playlist(plex, )
    plex.playlist(title=args.name).delete()
    Playlist.create(server=plex, title=args.name, items=episodes)
예제 #2
0
def sync_all():

    global user_plex
    plex = PlexServer(PLEX_URL, PLEX_TOKEN)
    plex_users = get_user_tokens(plex.machineIdentifier)
    user_token = plex_users.get(FROM_USER)
    playlists_to_sync = get_playlists_from_user(user_token)
    for playlist in playlists_to_sync:
        playlist_items = playlist.items()
        for user in TO_USERS:
            user_token = plex_users.get(user)
            user_plex = PlexServer(PLEX_URL, user_token)
            # Delete the old playlist
            try:
                logging.info(
                    f'Syncing Playlist {playlist.title} to user {user}')
                user_playlist = user_plex.playlist(playlist.title)
                user_playlist.delete()
            except Exception as e:
                logging.error(e)
            try:
                user_plex.createPlaylist(title=playlist.title,
                                         items=playlist_items)
            except Exception as e:
                logging.error(e)
예제 #3
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
예제 #4
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
예제 #5
0
def createPlaylist(plex: PlexServer, sp: spotipy.Spotify, playlist: []):
    playlistName = playlist['owner']['display_name'] + " - " + playlist['name']
    logging.info('Starting playlist %s' % playlistName)
    plexTracks = getPlexTracks(plex, getSpotifyTracks(sp, playlist))
    if len(plexTracks) > 0:
        try:
            plexPlaylist = plex.playlist(playlistName)
            logging.info('Updating playlist %s' % playlistName)
            plexPlaylist.addItems(plexTracks)
        except:
            logging.info("Creating playlist %s" % playlistName)
            plex.createPlaylist(playlistName, plexTracks)
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
def main():
    config = load_config()

    account = MyPlexAccount(config.get('DEFAULT', 'username'),
                            config.get('DEFAULT', 'password'))

    admin_server = account.resource(config.get('DEFAULT',
                                               'server name')).connect()

    user = account.user(config.get('DEFAULT', 'user'))

    # Get the token for the machine.
    token = user.get_token(admin_server.machineIdentifier)

    # Get the user server. We access the base URL by requesting a URL for a
    # blank key.
    user_server = PlexServer(admin_server.url(''), token=token)

    albums = {a.key: a for a in user_server.library.section('Music').albums()}
    playlist = user_server.playlist(config.get('DEFAULT', 'playlist name'))
    items = playlist.items()

    sort_structure = []

    for track in items:
        album = albums[track.parentKey]

        key = get_track_sort_key(track, album)

        sort_structure.append((key, track))

    sort_structure.sort(key=lambda item: item[0])

    items = [item[1] for item in sort_structure]

    for item in items:
        playlist.removeItem(item)

    playlist.addItems(items)
예제 #8
0
class SpotiPlex:
    def __init__(self, spotifyInfo, plexInfo):
        self.credManager = SpotifyClientCredentials(
            client_id=spotifyInfo['clientId'],
            client_secret=spotifyInfo['clientSecret'])
        self.user = spotifyInfo['user']
        self.sp = spotipy.Spotify(client_credentials_manager=self.credManager)
        self.plex = PlexServer(plexInfo['url'], plexInfo['token'])

    def getSpotifyPlaylist(self, plId):
        'Generate and return a list of tracks of the Spotify playlist'

        #playlist = self.sp.user_playlist(self.user, plId)
        playlist = self.sp.user_playlist_tracks(self.user, playlist_id=plId)

        tracks = playlist['items']
        while playlist['next']:
            playlist = self.sp.next(playlist)
            tracks.extend(playlist['items'])

        items = []
        for item in tracks:
            items.append({
                'title': item['track']['name'],
                'album': item['track']['album']['name'],
                'artist': item['track']['artists'][0]['name'],
                'isrc': item['track']['external_ids']['isrc']
                #'number': item['track']['track_number'],
                #'img': item['track']['album']['images'][0]['url']
            })

        return items

    def checkPlexFiles(self, playlist):
        'Check if the songs in the playlist are present on the Plex server. Returns list of found and missing items'

        tracks = []
        missing = []

        for item in playlist:
            results = self.plex.search(item['title'], mediatype='track')
            if not results:
                missing.append(item)
                continue

            for result in results:
                if type(result) != plexapi.audio.Track:
                    continue
                else:
                    if result.grandparentTitle.lower() == item['artist'].lower(
                    ):  # and result.parentTitle == item['album']:
                        tracks.append(result)
                        break
                    else:
                        if result == results[-1]:
                            missing.append(item)
                            break

        return tracks, missing

    def checkForPlexPlaylist(self, name):
        'Check if a playlist with this name exists in Plex. Returns the playlist if valid, else None'

        try:
            return self.plex.playlist(name)
        except plexapi.exceptions.NotFound:
            return None

    def comparePlaylists(self, sPlaylist, pPlaylist):
        'Compares the extracted Spotify playlist with the existing Plex playlist. Returns list of tracks to create the new playlist version from and missing songs in Plex'

        tracksToAdd = sPlaylist
        plexTracks = pPlaylist.items()
        plexOnlyItems = []
        temp = []
        for track in plexTracks:
            # remove any tracks from Spotify list that are already in Plex
            lastLen = len(temp)
            temp = list(
                filter(lambda item: not item['title'] == track.title,
                       tracksToAdd))
            if not len(temp) == lastLen:
                tracksToAdd = temp
            else:
                plexOnlyItems.append(track)

        return tracksToAdd, plexOnlyItems

    def createPlexPlaylist(self, name, playlist=None):
        'Create the playlist on the Plex server from given name and a item list'

        newPlaylist = self.plex.createPlaylist(name, items=playlist)
        return

    def addToPlexPlaylist(self, plexPlaylist, newItems):
        'Add more items to a Plex playlist'

        return plexPlaylist.addItems(newItems)

    def removeFromPlexPlaylist(self, plexPlaylist, itemsToRemove):
        'Remove given items from a Plex playlist'

        ## Seems not to work properly yet

        for item in itemsToRemove:
            plexPlaylist.removeItem(item)
def main():
    """Main script"""
    num_cores = multiprocessing.cpu_count()

    l = Library(FILEPATH)
    playlists = l.getPlaylistNames()

    PLEX = PlexServer(PLEX_URL, PLEX_TOKEN)
    PLEX_USERS = get_user_tokens(PLEX.machineIdentifier)
    PLEX_MUSIC = PLEX.library.section('Music')
    PLEX_TRACK_LIST = PLEX_MUSIC.searchTracks()
    PLEX_ARTIST_LIST = PLEX_MUSIC.searchArtists()

    for playlist in playlists:
        playlist_items = []
        DATA_FILE = playlist + '.pickle'

        if playlist not in PLAYLISTS:
            continue

        # Check if .pickle exists
        try:
            print("Loading the '{title}' playlist from disk...".format(title=playlist))

            with open(DATA_FILE, 'rb') as fp:
                playlist_items = pickle.load(fp)
                fp.close()

            # HACK
            playlist_items = [playlist_item for playlist_item in playlist_items if playlist_item]

        except FileNotFoundError:
            print("Building the '{title}' playlist...".format(title=playlist))

            PLAYLIST_TRACKS = l.getPlaylist(playlist).tracks

            # Multiprocessing implementation
            # playlist_items = Parallel(n_jobs=num_cores, prefer='processes')(
            #     delayed(match_track)(PLAYLIST_TRACK, PLEX_MUSIC, PLEX_ARTIST_LIST, PLEX_TRACK_LIST) for PLAYLIST_TRACK in PLAYLIST_TRACKS)

            # Standard implementation
            for PLAYLIST_TRACK in PLAYLIST_TRACKS:
                track_match = match_track(PLAYLIST_TRACK, PLEX_MUSIC, PLEX_ARTIST_LIST, PLEX_TRACK_LIST)
                if track_match:
                    playlist_items.append(track_match)

            # Save data (just in case)
            with open(DATA_FILE, 'wb') as fp:
                pickle.dump(playlist_items, fp)
                fp.close()

        # Create playlist (per user)
        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
예제 #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):
        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)
예제 #11
0
    ep_list = []
    to_remove = ''

    if opts.user:
        user_acct = account.user(opts.user)
        plex_server = PlexServer(PLEX_URL,
                                 user_acct.get_token(plex.machineIdentifier))
    else:
        plex_server = plex

    if opts.shows and not opts.playlist:
        to_remove = opts.shows
    elif not opts.shows and opts.playlist:
        to_remove = [
            x.grandparentTitle
            for x in plex_server.playlist(opts.playlist).items()
        ]

    if opts.action == 'deck':
        if not to_remove:
            print('The following shows are On Deck...')
            on_deck = get_on_deck(plex_server)['on_deck']
            for item in on_deck:
                print('{}: S{:02}E{:02} {}'.format(
                    item.grandparentTitle.encode('UTF-8'),
                    int(item.parentIndex), int(item.index),
                    item.title.encode('UTF-8')))

        else:
            print('Finding listed shows On Deck...')
            while True:
예제 #12
0
파일: bot.py 프로젝트: jarulsamy/Plex-Bot
class Plex(commands.Cog):
    """
    Discord commands pertinent to interacting with Plex

    Contains user commands such as play, pause, resume, stop, etc.
    Grabs, and parses all data from plex database.
    """

    # pylint: disable=too-many-instance-attributes
    # All are necessary to detect global interactions
    # within the bot.

    def __init__(self, bot, **kwargs):
        """
        Initializes Plex resources

        Connects to Plex library and sets up
        all asyncronous communications.

        Args:
            bot: discord.ext.command.Bot, bind for cogs
            base_url: str url to Plex server
            plex_token: str X-Token of Plex server
            lib_name: str name of Plex library to search through

        Raises:
            plexapi.exceptions.Unauthorized: Invalid Plex token

        Returns:
            None
        """

        self.bot = bot
        self.base_url = kwargs["base_url"]
        self.plex_token = kwargs["plex_token"]
        self.library_name = kwargs["lib_name"]
        self.bot_prefix = bot.command_prefix

        if kwargs["lyrics_token"]:
            self.genius = lyricsgenius.Genius(kwargs["lyrics_token"])
        else:
            plex_log.warning("No lyrics token specified, lyrics disabled")
            self.genius = None

        # Log fatal invalid plex token
        try:
            self.pms = PlexServer(self.base_url, self.plex_token)
        except Unauthorized:
            plex_log.fatal("Invalid Plex token, stopping...")
            raise Unauthorized("Invalid Plex token")

        self.music = self.pms.library.section(self.library_name)
        plex_log.debug("Connected to plex library: %s", self.library_name)

        # Initialize necessary vars
        self.voice_channel = None
        self.current_track = None
        self.np_message_id = None
        self.ctx = None

        # Initialize events
        self.play_queue = asyncio.Queue()
        self.play_next_event = asyncio.Event()

        bot_log.info("Started bot successfully")
        self.bot.loop.create_task(self._audio_player_task())

    def _search_tracks(self, title: str):
        """
        Search the Plex music db for track

        Args:
            title: str title of song to search for

        Returns:
            plexapi.audio.Track pointing to best matching title

        Raises:
            MediaNotFoundError: Title of track can't be found in plex db
        """
        results = self.music.searchTracks(title=title, maxresults=1)
        try:
            return results[0]
        except IndexError:
            raise MediaNotFoundError("Track cannot be found")

    def _search_albums(self, title: str):
        """
        Search the Plex music db for album

        Args:
            title: str title of album to search for

        Returns:
            plexapi.audio.Album pointing to best matching title

        Raises:
            MediaNotFoundError: Title of album can't be found in plex db
        """
        results = self.music.searchAlbums(title=title, maxresults=1)
        try:
            return results[0]
        except IndexError:
            raise MediaNotFoundError("Album cannot be found")

    def _search_playlists(self, title: str):
        """
        Search the Plex music db for playlist

        Args:
            title: str title of playlist to search for

        Returns:
            plexapi.playlist pointing to best matching title

        Raises:
            MediaNotFoundError: Title of playlist can't be found in plex db
        """
        try:
            return self.pms.playlist(title)
        except NotFound:
            raise MediaNotFoundError("Playlist cannot be found")

    async def _play(self):
        """
        Heavy lifting of playing songs

        Grabs the appropiate streaming URL, sends the `now playing`
        message, and initiates playback in the vc.

        Args:
            None

        Returns:
            None

        Raises:
            None
        """
        track_url = self.current_track.getStreamURL()
        audio_stream = FFmpegPCMAudio(track_url)

        while self.voice_channel.is_playing():
            asyncio.sleep(2)

        self.voice_channel.play(audio_stream, after=self._toggle_next)

        plex_log.debug("%s - URL: %s", self.current_track, track_url)

        embed, img = self._build_embed_track(self.current_track)
        self.np_message_id = await self.ctx.send(embed=embed, file=img)

    async def _audio_player_task(self):
        """
        Coroutine to handle playback and queuing

        Always-running function awaiting new songs to be added.
        Auto disconnects from VC if idle for > 15 seconds.
        Handles auto deletion of now playing song notifications.

        Args:
            None

        Returns:
            None

        Raises:
            None
        """
        while True:
            self.play_next_event.clear()
            if self.voice_channel:
                try:
                    # Disconnect after 15 seconds idle
                    async with timeout(15):
                        self.current_track = await self.play_queue.get()
                except asyncio.TimeoutError:
                    await self.voice_channel.disconnect()
                    self.voice_channel = None

            if not self.current_track:
                self.current_track = await self.play_queue.get()

            await self._play()
            await self.play_next_event.wait()
            await self.np_message_id.delete()

    def _toggle_next(self, error=None):
        """
        Callback for vc playback

        Clears current track, then activates _audio_player_task
        to play next in queue or disconnect.

        Args:
            error: Optional parameter required for discord.py callback

        Returns:
            None

        Raises:
            None
        """
        self.current_track = None
        self.bot.loop.call_soon_threadsafe(self.play_next_event.set)

    @staticmethod
    def _build_embed_track(track, type_="play"):
        """
        Creates a pretty embed card for tracks

        Builds a helpful status embed with the following info:
        Status, song title, album, artist and album art. All
        pertitent information is grabbed dynamically from the Plex db.

        Args:
            track: plexapi.audio.Track object of song
            type_: Type of card to make (play, queue).

        Returns:
            embed: discord.embed fully constructed payload.
            thumb_art: io.BytesIO of album thumbnail img.

        Raises:
            ValueError: Unsupported type of embed {type_}
        """
        # Grab the relevant thumbnail
        img_stream = requests.get(track.thumbUrl, stream=True).raw
        img = io.BytesIO(img_stream.read())

        # Attach to discord embed
        art_file = discord.File(img, filename="image0.png")
        # Get appropiate status message
        if type_ == "play":
            title = f"Now Playing - {track.title}"
        elif type_ == "queue":
            title = f"Added to queue - {track.title}"
        else:
            raise ValueError(f"Unsupported type of embed {type_}")

        # Include song details
        descrip = f"{track.album().title} - {track.artist().title}"

        # Build the actual embed
        embed = discord.Embed(
            title=title, description=descrip, colour=discord.Color.red()
        )
        embed.set_author(name="Plex")
        # Point to file attached with ctx object.
        embed.set_thumbnail(url="attachment://image0.png")

        bot_log.debug("Built embed for track - %s", track.title)

        return embed, art_file

    @staticmethod
    def _build_embed_album(album):
        """
        Creates a pretty embed card for albums

        Builds a helpful status embed with the following info:
        album, artist, and album art. All pertitent information
        is grabbed dynamically from the Plex db.

        Args:
            album: plexapi.audio.Album object of album

        Returns:
            embed: discord.embed fully constructed payload.
            thumb_art: io.BytesIO of album thumbnail img.

        Raises:
            None
        """
        # Grab the relevant thumbnail
        img_stream = requests.get(album.thumbUrl, stream=True).raw
        img = io.BytesIO(img_stream.read())

        # Attach to discord embed
        art_file = discord.File(img, filename="image0.png")
        title = "Added album to queue"
        descrip = f"{album.title} - {album.artist().title}"

        embed = discord.Embed(
            title=title, description=descrip, colour=discord.Color.red()
        )
        embed.set_author(name="Plex")
        embed.set_thumbnail(url="attachment://image0.png")
        bot_log.debug("Built embed for album - %s", album.title)

        return embed, art_file

    @staticmethod
    def _build_embed_playlist(self, playlist):
        """
        Creates a pretty embed card for playlists

        Builds a helpful status embed with the following info:
        playlist art. All pertitent information
        is grabbed dynamically from the Plex db.

        Args:
            playlist: plexapi.playlist object of playlist

        Returns:
            embed: discord.embed fully constructed payload.
            thumb_art: io.BytesIO of playlist thumbnail img.

        Raises:
            None
        """
        # Grab the relevant thumbnail
        img_stream = requests.get(self.pms.url(playlist.composite, True), stream=True).raw
        img = io.BytesIO(img_stream.read())

        # Attach to discord embed
        art_file = discord.File(img, filename="image0.png")
        title = "Added playlist to queue"
        descrip = f"{playlist.title}"

        embed = discord.Embed(
            title=title, description=descrip, colour=discord.Color.red()
        )
        embed.set_author(name="Plex")
        embed.set_thumbnail(url="attachment://image0.png")
        bot_log.debug("Built embed for playlist - %s", playlist.title)

        return embed, art_file

    async def _validate(self, ctx):
        """
        Ensures user is in a vc

        Args:
            ctx: discord.ext.commands.Context message context from command

        Returns:
            None

        Raises:
            VoiceChannelError: Author not in voice channel
        """
        # Fail if user not in vc
        if not ctx.author.voice:
            await ctx.send("Join a voice channel first!")
            bot_log.debug("Failed to play, requester not in voice channel")
            raise VoiceChannelError

        # Connect to voice if not already
        if not self.voice_channel:
            self.voice_channel = await ctx.author.voice.channel.connect()
            bot_log.debug("Connected to vc.")

    @command()
    async def play(self, ctx, *args):
        """
        User command to play song

        Searchs plex db and either, initiates playback, or
        adds to queue. Handles invalid usage from the user.

        Args:
            ctx: discord.ext.commands.Context message context from command
            *args: Title of song to play

        Returns:
            None

        Raises:
            None
        """
        # Save the context to use with async callbacks
        self.ctx = ctx
        title = " ".join(args)

        try:
            track = self._search_tracks(title)
        except MediaNotFoundError:
            await ctx.send(f"Can't find song: {title}")
            bot_log.debug("Failed to play, can't find song - %s", title)
            return

        try:
            await self._validate(ctx)
        except VoiceChannelError:
            pass

        # Specific add to queue message
        if self.voice_channel.is_playing():
            bot_log.debug("Added to queue - %s", title)
            embed, img = self._build_embed_track(track, type_="queue")
            await ctx.send(embed=embed, file=img)

        # Add the song to the async queue
        await self.play_queue.put(track)

    @command()
    async def album(self, ctx, *args):
        """
        User command to play song

        Searchs plex db and either, initiates playback, or
        adds to queue. Handles invalid usage from the user.

        Args:
            ctx: discord.ext.commands.Context message context from command
            *args: Title of song to play

        Returns:
            None

        Raises:
            None
        """
        # Save the context to use with async callbacks
        self.ctx = ctx
        title = " ".join(args)

        try:
            album = self._search_albums(title)
        except MediaNotFoundError:
            await ctx.send(f"Can't find album: {title}")
            bot_log.debug("Failed to queue album, can't find - %s", title)
            return

        try:
            await self._validate(ctx)
        except VoiceChannelError:
            pass

        bot_log.debug("Added to queue - %s", title)
        embed, img = self._build_embed_album(album)
        await ctx.send(embed=embed, file=img)

        for track in album.tracks():
            await self.play_queue.put(track)

    @command()
    async def playlist(self, ctx, *args):
        """
        User command to play playlist

        Searchs plex db and either, initiates playback, or
        adds to queue. Handles invalid usage from the user.

        Args:
            ctx: discord.ext.commands.Context message context from command
            *args: Title of playlist to play

        Returns:
            None

        Raises:
            None
        """
        # Save the context to use with async callbacks
        self.ctx = ctx
        title = " ".join(args)

        try:
            playlist = self._search_playlists(title)
        except MediaNotFoundError:
            await ctx.send(f"Can't find playlist: {title}")
            bot_log.debug("Failed to queue playlist, can't find - %s", title)
            return

        try:
            await self._validate(ctx)
        except VoiceChannelError:
            pass

        bot_log.debug("Added to queue - %s", title)
        embed, img = self._build_embed_playlist(self, playlist)
        await ctx.send(embed=embed, file=img)

        for item in playlist.items():
            if (item.TYPE == "track"):
                await self.play_queue.put(item)

    @command()
    async def stop(self, ctx):
        """
        User command to stop playback

        Stops playback and disconnects from vc.

        Args:
            ctx: discord.ext.commands.Context message context from command

        Returns:
            None

        Raises:
            None
        """
        if self.voice_channel:
            self.voice_channel.stop()
            await self.voice_channel.disconnect()
            self.voice_channel = None
            self.ctx = None
            bot_log.debug("Stopped")
            await ctx.send(":stop_button: Stopped")

    @command()
    async def pause(self, ctx):
        """
        User command to pause playback

        Pauses playback, but doesn't reset anything
        to allow playback resuming.

        Args:
            ctx: discord.ext.commands.Context message context from command

        Returns:
            None

        Raises:
            None
        """
        if self.voice_channel:
            self.voice_channel.pause()
            bot_log.debug("Paused")
            await ctx.send(":play_pause: Paused")

    @command()
    async def resume(self, ctx):
        """
        User command to resume playback

        Args:
            ctx: discord.ext.commands.Context message context from command

        Returns:
            None

        Raises:
            None
        """
        if self.voice_channel:
            self.voice_channel.resume()
            bot_log.debug("Resumed")
            await ctx.send(":play_pause: Resumed")

    @command()
    async def skip(self, ctx):
        """
        User command to skip song in queue

        Skips currently playing song. If no other songs in
        queue, stops playback, otherwise moves to next song.

        Args:
            ctx: discord.ext.commands.Context message context from command

        Returns:
            None

        Raises:
            None
        """
        bot_log.debug("Skip")
        if self.voice_channel:
            self.voice_channel.stop()
            bot_log.debug("Skipped")
            self._toggle_next()

    @command(name="np")
    async def now_playing(self, ctx):
        """
        User command to get currently playing song.

        Deletes old `now playing` status message,
        Creates a new one with up to date information.

        Args:
            ctx: discord.ext.commands.Context message context from command

        Returns:
            None

        Raises:
            None
        """
        if self.current_track:
            embed, img = self._build_embed_track(self.current_track)
            bot_log.debug("Now playing")
            if self.np_message_id:
                await self.np_message_id.delete()
                bot_log.debug("Deleted old np status")

            bot_log.debug("Created np status")
            self.np_message_id = await ctx.send(embed=embed, file=img)

    @command()
    async def clear(self, ctx):
        """
        User command to clear play queue.

        Args:
            ctx: discord.ext.commands.Context message context from command

        Returns:
            None

        Raises:
            None
        """
        self.play_queue = asyncio.Queue()
        bot_log.debug("Cleared queue")
        await ctx.send(":boom: Queue cleared.")

    @command()
    async def lyrics(self, ctx):
        """
        User command to get lyrics of a song.

        Args:
            ctx: discord.ext.commands.Context message context from command

            Returns:
                None

            Raises:
                None
        """
        if not self.current_track:
            plex_log.info("No song currently playing")
            return

        if self.genius:
            plex_log.info(
                "Searching for %s, %s",
                self.current_track.title,
                self.current_track.artist().title,
            )
            try:
                song = self.genius.search_song(
                    self.current_track.title, self.current_track.artist().title
                )
            except TypeError:
                self.genius = None
                plex_log.error("Invalid genius token, disabling lyrics")
                return

            try:
                lyrics = song.lyrics
                # Split into 1950 char chunks
                # Discord max message length is 2000
                lines = [(lyrics[i : i + 1950]) for i in range(0, len(lyrics), 1950)]

                for i in lines:
                    if i == "":
                        continue
                    # Apply code block format
                    i = f"```{i}```"
                    await ctx.send(i)

            except (IndexError, TypeError):
                plex_log.info("Could not find lyrics")
                await ctx.send("Can't find lyrics for this song.")
        else:
            plex_log.warning("Attempted lyrics without valid token")
예제 #13
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()
예제 #14
0
    opts = parser.parse_args()

    ep_list = []
    to_remove = ''

    if opts.user:
        user_acct = account.user(opts.user)
        plex_server = PlexServer(PLEX_URL, user_acct.get_token(plex.machineIdentifier))
    else:
        plex_server = plex

    if opts.shows and not opts.playlist:
        to_remove = opts.shows
    elif not opts.shows and opts.playlist:
        to_remove = [x.grandparentTitle for x in plex_server.playlist(opts.playlist).items()]

    if opts.action == 'deck':
        if not to_remove:
            print('The following shows are On Deck...')
            on_deck = get_on_deck(plex_server)['on_deck']
            for item in on_deck:
                print('{}: S{:02}E{:02} {}'.format(
                    item.grandparentTitle.encode('UTF-8'),
                    int(item.parentIndex), int(item.index),
                    item.title.encode('UTF-8')))

        else:
            print('Finding listed shows On Deck...')
            while True:
                off_deck = get_on_deck(plex_server, to_remove)
            print('finished refresing')
            notifier.stop()


server = PlexServer(url, token)
library = server.library
section = library.section(section_title)
notifier = server.startAlertListener(callback=cb)

if section.refreshing:
    print('already refrehsing')
else:
    print('start refrehsing')
    section.update()

while notifier.isAlive():
    sleep(0.1)

print('addig item to playlist')
items = section.searchTracks(title=item_title,
                             sort='addedAt:desc',
                             maxresults=1)
print('items', items)
if items:
    playlist = server.playlist(playlist_title)
    print('playlist', playlist)
    playlist.addItems(items)
    print('item added to playlist')
else:
    print('no items found')