diff --git a/README.md b/README.md index 1eef704..ee2e516 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,16 @@ The config file is in the scripts folder. Before first run of the script, pleas * filter_show_shows - True to show recently added TV shows * filter_show_seasons - True to show recently added TV seasons * filter_show_episodes - True to show recently added TV episodes +* filter_libraries - A list of library names to filter out - ['Home Videos', 'Private'] +* filter_sections_movies - Movie specific filters +* filter_sections_TV - TV specific filters + * Possible fields: tagline, summary, content_rating, duration, year, rating, studio, tags_genre, tags_director, tags_star + * order - The order this field should appear for each title + * show - Whether or not this field should be shown for each title + * preText - The text that should be added before this field for each title + * postText - The text that should be added after this field for each title + * include - A list of values that are each title must match at least one to be shown + * exclude - A list of values that if the title matches any of, will not be shown #####Messages * msg_email_teaser - Teaser text on the email diff --git a/scripts/config.conf b/scripts/config.conf index a59295c..24161e8 100644 --- a/scripts/config.conf +++ b/scripts/config.conf @@ -1,3 +1,4 @@ +# This Python file uses the following encoding: utf-8 ##Folder Paths # Windows Example: 'C:\\Users\\{User Name}\\AppData\\Local\\' # Linux Example: '/var/lib/plexmediaserver/Library/Application Support/' @@ -22,6 +23,8 @@ upload_cloudinary_api_secret = '' ##Web web_enabled = True +#This will not create the index.html file, but will still save the images to the images folder. (Useful for when you only want to send an email) +web_only_save_images = False web_domain = '' web_path = 'plexemail' web_delete_previous_images = False @@ -47,6 +50,173 @@ filter_show_movies = True filter_show_shows = True filter_show_seasons = True filter_show_episodes = True +#Name of libraries to filter out - ['Home Videos', 'Private'] +filter_libraries = [] +#The sections to include and ordering - tagline, summary, content_rating, duration, year, rating, studio, tags_genre, tags_director, tags_star +filter_sections_movies = { + 'tagline':{ + 'order':1, + 'show': True, + 'preText':'', + 'postText':'', + 'include':[], + 'exclude':[] + }, + 'summary':{ + 'order':2, + 'show': True, + 'preText':'', + 'postText':'', + 'include':[], + 'exclude':[] + }, + 'tags_genre':{ + 'order':3, + 'show': True, + 'preText':'Genre(s): ', + 'postText':'', + 'include':[], + 'exclude':[] + }, + 'tags_director':{ + 'order':4, + 'show': False, + 'preText':'Director: ', + 'postText':'', + 'include':[], + 'exclude':[] + }, + 'tags_star':{ + 'order':5, + 'show': True, + 'preText':'Star(s): ', + 'postText':'', + 'include':[], + 'exclude':[] + }, + 'content_rating':{ + 'order':6, + 'show': False, + 'preText':'Content Rating: ', + 'postText':'', + 'include':[], + 'exclude':[] + }, + 'duration':{ + 'order':7, + 'show': True, + 'preText':'Runtime: ', + 'postText':' minutes', + 'include':[], + 'exclude':[] + }, + 'year':{ + 'order':8, + 'show': True, + 'preText':'Year: ', + 'postText':'', + 'include':[], + 'exclude':[] + }, + 'studio':{ + 'order':9, + 'show': False, + 'preText':'Studio: ', + 'postText':'', + 'include':[], + 'exclude':[] + }, + 'rating':{ + 'order':10, + 'show': True, + 'preText':'Rating: ', + 'postText':'%', + 'include':[], + 'exclude':[] + } + } +filter_sections_TV = { + 'tagline':{ + 'order':1, + 'show': True, + 'preText':'', + 'postText':'', + 'include':[], + 'exclude':[] + }, + 'summary':{ + 'order':2, + 'show': True, + 'preText':'', + 'postText':'', + 'include':[], + 'exclude':[] + }, + 'tags_genre':{ + 'order':3, + 'show': False, + 'preText':'Genre(s): ', + 'postText':'', + 'include':[], + 'exclude':[] + }, + 'tags_director':{ + 'order':4, + 'show': False, + 'preText':'Director: ', + 'postText':'', + 'include':[], + 'exclude':[] + }, + 'tags_star':{ + 'order':5, + 'show': False, + 'preText':'Star(s): ', + 'postText':'', + 'include':[], + 'exclude':[] + }, + 'content_rating':{ + 'order':6, + 'show': False, + 'preText':'Content Rating: ', + 'postText':'', + 'include':[], + 'exclude':[] + }, + 'duration':{ + 'order':7, + 'show': True, + 'preText':'Runtime: ', + 'postText':' minutes', + 'include':[], + 'exclude':[] + }, + 'year':{ + 'order':8, + 'show': False, + 'preText':'Year: ', + 'postText':'', + 'include':[], + 'exclude':[] + }, + 'studio':{ + 'order':9, + 'show': True, + 'preText':'Network: ', + 'postText':'', + 'include':[], + 'exclude':[] + }, + 'rating':{ + 'order':10, + 'show': False, + 'preText':'Rating: ', + 'postText':'%', + 'include':[], + 'exclude':[] + } + } ##Messages msg_email_teaser = 'Check out this week\'s releases from Plex: {website} - {past_day} - {current_day}' diff --git a/scripts/plexEmail.py b/scripts/plexEmail.py index 6103e43..09182bf 100644 --- a/scripts/plexEmail.py +++ b/scripts/plexEmail.py @@ -22,6 +22,15 @@ def replaceConfigTokens(): ## The below code is for backwards compatibility + if ('filter_sections_movies' not in config): + config['filter_include_sections_movies'] = {'tagline':{'order':1,'show':True,'preText':'','postText':'','include':[],'exclude':[]},'summary':{'order':2,'show':True,'preText':'','postText':'','include':[],'exclude':[]},'tags_genre':{'order':3,'show':True,'preText':'Genre(s): ','postText':'','include':[],'exclude':[]},'tags_director':{'order':4,'show':False,'preText':'Director: ','postText':'','include':[],'exclude':[]},'tags_star':{'order':5,'show':True,'preText':'Star(s): ','postText':'','include':[],'exclude':[]},'content_rating':{'order':6,'show':False,'preText':'ContentRating: ','postText':'','include':[],'exclude':[]},'duration':{'order':7,'show':True,'preText':'Runtime: ','postText':' minutes','include':[],'exclude':[]},'year':{'order':8,'show':True,'preText':'Year: ','postText':'','include':[],'exclude':[]},'studio':{'order':9,'show':False,'preText':'Studio: ','postText':'','include':[],'exclude':[]},'rating':{'order':10,'show':True,'preText':'Rating: ','postText':'%','include':[],'exclude':[]}} + + if ('filter_sections_TV' not in config): + config['filter_include_sections_TV'] = {'tagline':{'order':1,'show':True,'preText':'','postText':'','include':[],'exclude':[]},'summary':{'order':2,'show':True,'preText':'','postText':'','include':[],'exclude':[]},'tags_genre':{'order':3,'show':False,'preText':'Genre(s): ','postText':'','include':[],'exclude':[]},'tags_director':{'order':4,'show':False,'preText':'Director: ','postText':'','include':[],'exclude':[]},'tags_star':{'order':5,'show':False,'preText':'Star(s): ','postText':'','include':[],'exclude':[]},'content_rating':{'order':6,'show':False,'preText':'ContentRating: ','postText':'','include':[],'exclude':[]},'duration':{'order':7,'show':True,'preText':'Runtime: ','postText':' minutes','include':[],'exclude':[]},'year':{'order':8,'show':False,'preText':'Year: ','postText':'','include':[],'exclude':[]},'studio':{'order':9,'show':True,'preText':'Network: ','postText':'','include':[],'exclude':[]},'rating':{'order':10,'show':False,'preText':'Rating: ','postText':'%','include':[],'exclude':[]}} + + if ('web_only_save_images' not in config): + config['web_only_save_images'] = False + if ('filter_show_movies' not in config): config['filter_show_movies'] = True @@ -113,11 +122,26 @@ def replaceConfigTokens(): if (config['web_folder'].rfind(os.path.sep) < len(config['web_folder']) - len(os.path.sep)): config['web_folder'] = config['web_folder'] + os.path.sep + +def convertToHumanReadable(valuesToConvert): + convertedValues = {} + for value in valuesToConvert: + convertedValues[value] = valuesToConvert[value] + convertedValues[value + '_filter'] = [valuesToConvert[value]] + if (not valuesToConvert[value]): + continue + if (value == 'duration'): + convertedValues[value] = str(valuesToConvert[value] // 1000 // 60) + elif (value == 'rating'): + convertedValues[value] = str(int(valuesToConvert[value] * 10)) + elif (value == 'tags_genre' or value == 'tags_star' or value == 'tags_director'): + convertedValues[value + '_filter'] = valuesToConvert[value].split('|') + convertedValues[value] = valuesToConvert[value].replace('|', ', ') + return convertedValues def deleteImages(): folder = config['web_folder'] + config['web_path'] + os.path.sep + 'images' + os.path.sep for file in os.listdir(folder): - print folder, file if (file.endswith('.jpg')): os.remove(folder + file) @@ -500,10 +524,15 @@ def createWebHTML(): parser = argparse.ArgumentParser(description='This script aggregates all new TV and movie releases for the past x days then writes to your web directory and sends out an email.') parser.add_argument('-c','--configfile', help='The path to a config file to be used in the running of this instance of the script.', default=os.path.dirname(os.path.realpath(sys.argv[0])) + os.path.sep + 'config.conf', required=False) +parser.add_argument('-t','--test', help='Run this script in test mode - Sends email only to sender', action='store_true') args = vars(parser.parse_args()) + +testMode = False if ('configfile' in args): configFile = args['configfile'] +if ('test' in args): + testMode = args['test'] if (not os.path.isfile(configFile)): print configFile + ' does not exist' @@ -529,21 +558,29 @@ def createWebHTML(): con = sqlite3.connect(DATABASE_FILE) con.text_factory = str -with con: - - #if ('date_use_hours' in config and config['date_use_hours']): - #dateSearch = 'datetime(\'now\', \'localtime\', \'-' + str(config['date_hours_back_to_search']) + ' hours\')' - #else: - #dateSearch = 'datetime(\'now\', \'localtime\', \'-' + str(config['date_days_back_to_search']) + ' days\')' +with con: + libraryFilter = '' + if (config['filter_libraries']): + cur = con.cursor() + cur.execute('SELECT id, name FROM library_sections;') + for row in cur: + if (row[1].lower() in (library.lower() for library in config['filter_libraries'])): + if (libraryFilter == ''): + libraryFilter = ' AND (' + libraryFilter += 'library_section_id != ' + str(row[0]) + else: + libraryFilter += ' OR library_section_id != ' + str(row[0]) + if (libraryFilter != ''): + libraryFilter += ') ' dateSearch = 'datetime(\'now\', \'localtime\', \'-' + str(config['date_days_back_to_search']) + ' days\', \'-' + str(config['date_hours_back_to_search']) + ' hours\', \'-' + str(config['date_minutes_back_to_search']) + ' minutes\')' cur = con.cursor() - cur.execute("SELECT id, parent_id, metadata_type, title, title_sort, original_title, rating, tagline, summary, content_rating, duration, user_thumb_url, tags_genre, tags_director, tags_star, year, hash, [index], studio FROM metadata_items WHERE added_at >= " + dateSearch + " AND metadata_type >= 1 AND metadata_type <= 4 ORDER BY title_sort;") + cur.execute("SELECT id, parent_id, metadata_type, title, title_sort, original_title, rating, tagline, summary, content_rating, duration, user_thumb_url, tags_genre, tags_director, tags_star, year, hash, [index], studio FROM metadata_items WHERE added_at >= " + dateSearch + " AND metadata_type >= 1 AND metadata_type <= 4 " + libraryFilter + " ORDER BY title_sort;") response = {}; for row in cur: - response[row[0]] = {'id': row[0], 'parent_id': row[1], 'metadata_type': row[2], 'title': row[3], 'title_sort': row[4], 'original_title': row[5], 'rating': row[6], 'tagline': row[7], 'summary': row[8], 'content_rating': row[9], 'duration': row[10], 'user_thumb_url': row[11], 'tags_genre': row[12], 'tags_director': row[13], 'tags_star': row[14], 'year': row[15], 'hash': row[16], 'index': row[17], 'studio': row[18]} + response[row[0]] = {'id': row[0], 'parent_id': row[1], 'metadata_type': row[2], 'title': row[3], 'title_sort': row[4], 'original_title': row[5], 'rating': row[6], 'tagline': row[7], 'summary': row[8], 'content_rating': row[9], 'duration': row[10], 'user_thumb_url': row[11], 'tags_genre': row[12], 'tags_director': row[13], 'tags_star': row[14], 'year': row[15], 'hash': row[16], 'index': row[17], 'studio': row[18]} emailMovies = """
' - emailMovies += '' - emailMovies += ' | ' - emailMovies += '' + title.decode('utf-8') + '' - if (movies[movie]['tagline'] != ''): - emailMovies += '' + movies[movie]['tagline'].decode('utf-8') + ' ' - emailMovies += '' + movies[movie]['summary'].decode('utf-8') + ' ' - if (movies[movie]['duration']): - emailMovies += 'Runtime: ' + str(movies[movie]['duration'] // 1000 // 60) + ' minutes ' - if (movies[movie]['year']): - emailMovies += 'Release Year: ' + str(movies[movie]['year']) + ' ' - if (movies[movie]['rating']): - emailMovies += 'Rating: ' + str(int(movies[movie]['rating'] * 10)) + '% ' - emailMovies += ' |
' + emailText += '' + emailText += ' | ' + emailText += '' + title.decode('utf-8') + '' + htmlText += ''
+ htmlText += ''
+ htmlText += ' ' + title.decode('utf-8') + '' + + sections = config['filter_sections_movies'] + for section in sorted(sections.iteritems(), key=lambda t: t[1]['order']): + if (movies[movie][section[0]] in sections[section[0]]['exclude'] or len(set(movies[movie][section[0] + '_filter']).intersection(sections[section[0]]['exclude'])) > 0 or (sections[section[0]]['include'] and movies[movie][section[0]] not in sections[section[0]]['include'] and len(set(movies[movie][section[0] + '_filter']).intersection(sections[section[0]]['include'])) == 0)): + skipItem = True + if (sections[section[0]]['show'] and movies[movie][section[0]] and movies[movie][section[0]] != ''): + emailText += '' + sections[section[0]]['preText'].decode('utf-8') + str(movies[movie][section[0]]).decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + ' ' + htmlText += '' + sections[section[0]]['preText'].decode('utf-8') + str(movies[movie][section[0]]).decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + ' ' - htmlMovies += ''
- htmlMovies += ''
- htmlMovies += ' ' + title.decode('utf-8') + '' - if (movies[movie]['tagline'] != ''): - htmlMovies += '' + movies[movie]['tagline'].decode('utf-8') + ' ' - htmlMovies += '' + movies[movie]['summary'].decode('utf-8') + ' ' - if (movies[movie]['duration']): - htmlMovies += 'Runtime: ' + str(movies[movie]['duration'] // 1000 // 60) + ' minutes ' - if (movies[movie]['year']): - htmlMovies += 'Release Year: ' + str(movies[movie]['year']) + ' ' - if (movies[movie]['rating']): - htmlMovies += 'Rating: ' + str(int(movies[movie]['rating'] * 10)) + '% ' - htmlMovies += '' + emailText += ' |
' - emailTVShows += ' | ' + title.decode('utf-8') + '' - if (tvShows[show]['tagline'] != ''): - emailTVShows += '' + tvShows[show]['tagline'].decode('utf-8') + ' ' - emailTVShows += '' + tvShows[show]['summary'].decode('utf-8') + ' ' - if (tvShows[show]['studio'] != ''): - emailTVShows += 'Network: ' + tvShows[show]['studio'].decode('utf-8') + ' ' - emailTVShows += ' |
' + emailText += ' | ' + title.decode('utf-8') + '' + htmlText += ''
+ htmlText += ''
+ htmlText += ' ' + title.decode('utf-8') + '' - htmlTVShows += ''
- htmlTVShows += ''
- htmlTVShows += ' ' + title.decode('utf-8') + '' - if (tvShows[show]['tagline'] != ''): - htmlTVShows += '' + tvShows[show]['tagline'].decode('utf-8') + ' ' - htmlTVShows += '' + tvShows[show]['summary'].decode('utf-8') + ' ' - if (tvShows[show]['studio'] != ''): - htmlTVShows += 'Network: ' + tvShows[show]['studio'].decode('utf-8') + ' ' - htmlTVShows += '' + sections = config['filter_sections_TV'] + for section in sorted(sections.iteritems(), key=lambda t: t[1]['order']): + if (tvShows[show][section[0]] in sections[section[0]]['exclude'] or len(set(tvShows[show][section[0] + '_filter']).intersection(sections[section[0]]['exclude'])) > 0 or (sections[section[0]]['include'] and tvShows[show][section[0]] not in sections[section[0]]['include'] and len(set(tvShows[show][section[0] + '_filter']).intersection(sections[section[0]]['include'])) == 0)): + skipItem = True + if (sections[section[0]]['show'] and tvShows[show][section[0]] and tvShows[show][section[0]] != ''): + emailText += ' ' + sections[section[0]]['preText'].decode('utf-8') + str(tvShows[show][section[0]]).decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + ' ' + htmlText += '' + sections[section[0]]['preText'].decode('utf-8') + str(tvShows[show][section[0]]).decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + ' ' + + emailText += ' |
' - emailTVSeasons += ' | ' + title.decode('utf-8') + '' - emailTVSeasons += 'Season ' + str(tvSeasons[season]['index']) + ' ' - if (tvSeasons[season]['tagline'] != ''): - emailTVSeasons += '' + tvSeasons[season]['tagline'].decode('utf-8') + ' ' - emailTVSeasons += '' + tvSeasons[season]['summary'].decode('utf-8') + ' ' - if (tvSeasons[season]['studio'] != ''): - emailTVSeasons += 'Network: ' + tvSeasons[season]['studio'].decode('utf-8') + ' ' - emailTVSeasons += ' |
' + emailText += ' | ' + title.decode('utf-8') + '' + emailText += 'Season ' + str(tvSeasons[season]['index']) + ' ' + htmlText += ''
+ htmlText += ''
+ htmlText += ' ' + title.decode('utf-8') + '' + htmlText += 'Season ' + str(tvSeasons[season]['index']) + ' ' + + sections = config['filter_sections_TV'] + for section in sorted(sections.iteritems(), key=lambda t: t[1]['order']): + if (tvSeasons[season][section[0]] in sections[section[0]]['exclude'] or len(set(tvSeasons[season][section[0] + '_filter']).intersection(sections[section[0]]['exclude'])) > 0 or (sections[section[0]]['include'] and tvSeasons[season][section[0]] not in sections[section[0]]['include'] and len(set(tvSeasons[season][section[0] + '_filter']).intersection(sections[section[0]]['include'])) == 0)): + skipItem = True + if (sections[section[0]]['show'] and tvSeasons[season][section[0]] and tvSeasons[season][section[0]] != ''): + emailText += '' + sections[section[0]]['preText'].decode('utf-8') + str(tvSeasons[season][section[0]]).decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + ' ' + htmlText += '' + sections[section[0]]['preText'].decode('utf-8') + str(tvSeasons[season][section[0]]).decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + ' ' + + emailText += ' |
Season ' + str(tvSeasons[season]['index']) + '
' - if (tvSeasons[season]['tagline'] != ''): - htmlTVSeasons += '' + tvSeasons[season]['tagline'].decode('utf-8') + '
' - htmlTVSeasons += '' + tvSeasons[season]['summary'].decode('utf-8') + '
' - if (tvSeasons[season]['studio'] != ''): - htmlTVSeasons += 'Network: ' + tvSeasons[season]['studio'].decode('utf-8') + '
' - htmlTVSeasons += '' - emailTVEpisodes += ' | ' + showTitle.decode('utf-8') + '' - emailTVEpisodes += 'S' + str(tvEpisodes[episode]['season_index']) + ' E' + str(tvEpisodes[episode]['index']) + ': ' + title.decode('utf-8') + ' ' - if (tvEpisodes[episode]['tagline'] != ''): - emailTVEpisodes += '' + tvEpisodes[episode]['tagline'].decode('utf-8') + ' ' - emailTVEpisodes += '' + tvEpisodes[episode]['summary'].decode('utf-8') + ' ' - if (tvEpisodes[episode]['studio'] != ''): - emailTVEpisodes += 'Network: ' + tvEpisodes[episode]['studio'].decode('utf-8') + ' ' - emailTVEpisodes += ' |
' + emailText += ' | ' + showTitle.decode('utf-8') + '' + emailText += 'S' + str(tvEpisodes[episode]['season_index']) + ' E' + str(tvEpisodes[episode]['index']) + ': ' + title.decode('utf-8') + ' ' + htmlText += ''
+ htmlText += ''
+ htmlText += ' ' + showTitle.decode('utf-8') + '' + htmlText += 'S' + str(tvEpisodes[episode]['season_index']) + ' E' + str(tvEpisodes[episode]['index']) + ': ' + title.decode('utf-8') + ' ' + + sections = config['filter_sections_TV'] + for section in sorted(sections.iteritems(), key=lambda t: t[1]['order']): + if (tvEpisodes[episode][section[0]] in sections[section[0]]['exclude'] or len(set(tvEpisodes[episode][section[0] + '_filter']).intersection(sections[section[0]]['exclude'])) > 0 or (sections[section[0]]['include'] and tvEpisodes[episode][section[0]] not in sections[section[0]]['include'] and len(set(tvEpisodes[episode][section[0] + '_filter']).intersection(sections[section[0]]['include'])) == 0)): + skipItem = True + if (sections[section[0]]['show'] and tvEpisodes[episode][section[0]] and tvEpisodes[episode][section[0]] != ''): + emailText += '' + sections[section[0]]['preText'].decode('utf-8') + str(tvEpisodes[episode][section[0]]).decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + ' ' + htmlText += '' + sections[section[0]]['preText'].decode('utf-8') + str(tvEpisodes[episode][section[0]]).decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + ' ' - htmlTVEpisodes += ''
- htmlTVEpisodes += ''
- htmlTVEpisodes += ' ' + showTitle.decode('utf-8') + '' - htmlTVEpisodes += 'S' + str(tvEpisodes[episode]['season_index']) + ' E' + str(tvEpisodes[episode]['index']) + ': ' + title.decode('utf-8') + ' ' - if (tvEpisodes[episode]['tagline'] != ''): - htmlTVEpisodes += '' + tvEpisodes[episode]['tagline'].decode('utf-8') + ' ' - htmlTVEpisodes += '' + tvEpisodes[episode]['summary'].decode('utf-8') + ' ' - if (tvEpisodes[episode]['studio'] != ''): - htmlTVEpisodes += 'Network: ' + tvEpisodes[episode]['studio'].decode('utf-8') + ' ' - htmlTVEpisodes += '' + emailText += ' |