""" Description: Create and share playlists based on Most Popular TV/Movies from Tautulli and Aired this day in history. Author: blacktwin Requires: requests, plexapi Create, share, and clean Playlists for users. optional arguments: -h, --help show this help message and exit --jbop Playlist selector. Choices: (todayInHistory, mostPopularTv, mostPopularMovies) --action Action selector. Choices: (add, remove, update) --users {] The Plex usernames to create/share to or delete from. Choices: (USERNAMES) --libraries [ ...] Space separated list of case sensitive names to process. Allowed names are: Choices: (LIBRARIES) --self Create playlist for admin. Default: False --days DAYS The time range to calculate statistics. Default: 30 --top TOP The number of top items to list. Default: 5 Example: Use with cron or task to schedule runs Create Aired Today Playlist from Movies and TV Shows libraries and share to users bob, Black Twin and admin user python playlist_manager.py --jbop todayInHistory --libraries Movies "TV Shows" --action add --user bob "Black Twin" --self Update previous Aired Today Playlist(s) from Movies and TV Shows libraries and share to users bob and Black Twin python playlist_manager.py --jbop todayInHistory --libraries Movies "TV Shows" --action update --user bob "Black Twin" Delete all previous Aired Today Playlist(s) from users bob and Black Twin python playlist_manager.py --jbop todayInHistory --action remove --user bob "Black Twin" Create 5 Most Popular TV Shows (30 days) Playlist and share to users bob and Black Twin python playlist_manager.py --jbop mostPopularTv --action add --user bob "Black Twin" Create 10 Most Popular Movies (60 days) Playlist and share to users bob and Black Twin python playlist_manager.py --jbop mostPopularTv --action add --user bob "Black Twin" --days 60 --top 10 """ import sys import requests import argparse import operator import datetime from plexapi.server import PlexServer, CONFIG ### EDIT SETTINGS ### PLEX_URL = '' PLEX_TOKEN = '' TAUTULLI_URL = '' TAUTULLI_APIKEY = '' ## CODE BELOW ## if not PLEX_URL: PLEX_URL = CONFIG.data['auth'].get('server_baseurl') if not PLEX_TOKEN: PLEX_TOKEN = CONFIG.data['auth'].get('server_token') if not TAUTULLI_URL: TAUTULLI_URL = CONFIG.data['auth'].get('tautulli_baseurl') if not TAUTULLI_APIKEY: TAUTULLI_APIKEY = CONFIG.data['auth'].get('tautulli_apikey') # Defaults DAYS = 30 TOP = 5 # Playlist Titles TODAY_PLAY_TITLE = 'Aired Today {month}-{day}' MOVIE_PLAYLIST = 'Most Popular Movies ({days} days)' TV_PLAYLIST = 'Most Popular TV Shows ({days} days)' SELECTOR = ['todayInHistory', 'mostPopularTv', 'mostPopularMovies'] ACTIONS = ['add', 'remove', 'update'] sess = requests.Session() # Ignore verifying the SSL certificate sess.verify = False # '/path/to/certfile' # 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()] + [plex.myPlexAccount().title] section_lst = [x.title for x in plex.library.sections()] today = datetime.datetime.now().date() def get_home_stats(time_range, stats_count): # Get the homepage watch statistics. payload = {'apikey': TAUTULLI_APIKEY, 'cmd': 'get_home_stats', 'time_range': time_range, 'stats_count': stats_count, 'stats_type': 0} # stats_type = plays try: r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=payload) response = r.json() res_data = response['response']['data'] return res_data except Exception as e: sys.stderr.write("Tautulli API 'get_home_stats' request failed: {0}.".format(e)) def find_air_dates(video): """Find what aired with today's month-day Parameters ---------- video video object to find air date Returns ------- list list of rating key and date aired """ try: ad_month = str(video.originallyAvailableAt.month) ad_day = str(video.originallyAvailableAt.day) if ad_month == str(today.month) and ad_day == str(today.day): return [[video.ratingKey] + [str(video.originallyAvailableAt)]] except Exception as e: # print(e) return def get_all_content(library_name): """Get all movies or episodes from LIBRARY_NAME Parameters ---------- library_name: list list of library objects Returns ------- list Sorted list of Movie and episodes that aired on today's date. """ child_lst = [] for library in library_name: for child in plex.library.section(library).all(): if child.type == 'movie': if find_air_dates(child): item_date = find_air_dates(child) child_lst += item_date elif child.type == 'show': for episode in child.episodes(): if find_air_dates(episode): item_date = find_air_dates(episode) child_lst += item_date else: pass # Sort by original air date, oldest first aired_lst = sorted(child_lst, key=operator.itemgetter(1)) # Remove date used for sorting play_lst = [x[0] for x in aired_lst] return play_lst def create_playlist(playlist_title, playlist_keys, server, user): """ Parameters ---------- playlist_title playlist_keys server user Returns ------- """ playlist_list = [] for key in playlist_keys: plex_obj = server.fetchItem(key) if plex_obj.type == 'show': for episode in plex_obj.episodes(): playlist_list.append(episode) else: playlist_list.append(plex_obj) server.createPlaylist(playlist_title, playlist_list) print("...Added {title} playlist to '{user}'.".format(title=playlist_title, user=user)) def delete_playlist(server, user, jbop): """ Parameters ---------- user_lst Returns ------- """ # Delete the old playlist try: for playlist in server.playlists(): if jbop == 'todayInHistory': if playlist.title.startswith('Aired Today'): playlist.delete() print("...Deleted {playlist.title} for '{user}'." .format(playlist=playlist, user=user)) elif jbop == 'mostPopularMovies': if playlist.title.startswith('Most Popular Movies'): playlist.delete() print("...Deleted {playlist.title} for '{user}'." .format(playlist=playlist, user=user)) elif jbop == 'mostPopularTv': if playlist.title.startswith('Most Popular TV'): playlist.delete() print("...Deleted {playlist.title} for '{user}'." .format(playlist=playlist, user=user)) except: # print("Playlist not found on '{user}' account".format(user=user)) pass if __name__ == "__main__": parser = argparse.ArgumentParser(description="Create, share, and clean Playlists for users.", formatter_class = argparse.RawTextHelpFormatter) parser.add_argument('--jbop', required=True, choices=SELECTOR, help='Playlist selector.\n' 'Choices: (%(choices)s)') parser.add_argument('--action', required=True, choices=ACTIONS, help='Action selector.\n' 'Choices: (%(choices)s)') parser.add_argument('--users', required=True, nargs='+', choices=user_lst, help='The Plex usernames to create/share to or delete from. Allowed names are: \n' 'Choices: %(choices)s') parser.add_argument('--libraries', nargs='+', choices=section_lst, metavar='', help='Space separated list of case sensitive names to process. Allowed names are: \n' 'Choices: %(choices)s') parser.add_argument('--self', default=False, action='store_true', help='Create playlist for admin. \n' 'Default: %(default)s') parser.add_argument('--days', type=str, default=DAYS, help='The time range to calculate statistics. \n' 'Default: %(default)s') parser.add_argument('--top', type=str, default=TOP, help='The number of top items to list. \n' 'Default: %(default)s') opts = parser.parse_args() # print(opts) plex_servers = [] # todo-me add allUsers and exclusions if opts.users: for user in opts.users: user_acct = account.user(user) plex_servers.append({ 'server': PlexServer(PLEX_URL, user_acct.get_token(plex.machineIdentifier)), 'user': user}) if opts.self: plex_servers.append({'server': plex, 'user': 'admin'}) else: plex_servers.append({'server': plex, 'user': 'admin'}) if opts.action == 'remove': print("Deleting the playlist(s)...") for x in plex_servers: delete_playlist(x['server'], x['user'], opts.jbop) else: # todo-me add more playlist types if opts.jbop == 'todayInHistory': keys_list = get_all_content(opts.libraries) title = TODAY_PLAY_TITLE.format(month=today.month, day=today.day) if opts.jbop == 'mostPopularTv': home_stats = get_home_stats(opts.days, opts.top) for stat in home_stats: if stat['stat_id'] == 'popular_tv': keys_list = [x['rating_key'] for x in stat['rows']] title = TV_PLAYLIST.format(days=opts.days) if opts.jbop == 'mostPopularMovies': home_stats = get_home_stats(opts.days, opts.top) for stat in home_stats: if stat['stat_id'] == 'popular_movies': keys_list = [x['rating_key'] for x in stat['rows']] title = MOVIE_PLAYLIST.format(days=opts.days) if opts.action == 'update': print("Deleting the playlist(s)...") for x in plex_servers: delete_playlist(x['server'], x['user'], opts.jbop) print('Creating playlist(s)...') for x in plex_servers: create_playlist(title, keys_list, x['server'], x['user']) # todo-me allow for update or another action to remove watched items # todo-me use Tautulli to imitate Smart Playlist if opts.action == 'add': print('Creating playlist(s)...') for x in plex_servers: create_playlist(title, keys_list, x['server'], x['user']) print("Done.")