Skip to content

Commit 40b8a57

Browse files
committed
New Features and Bug Fixes
##Features * Can choose between episode, season and show art work for the TV episodes section. * Can filter (include/exclude) based on the title of the item in all libraries. Useful if you want to only send emails to a specific user when a specific show is added. * Added the ability to show the air date for movies, TV shows and TV episodes. ##Bug Fixes * Fixed an issue with the MIME type when attaching images directly to the email.
1 parent 1051d38 commit 40b8a57

File tree

3 files changed

+107
-12
lines changed

3 files changed

+107
-12
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,17 @@ The config file is in the scripts folder. Before first run of the script, pleas
124124
#####Filtering
125125
* filter_include_plex_web_link - True to add hyperlinks to the images and titles to go to Plex Web for the specific title
126126
* filter_show_movies - True to show recently added movies
127+
* filter_movies_include - List of movies to include (Anything that does not match perfectly will not be included)
128+
* filter_movies_excluse - List of movies to exclude (Only titles that match perfectly will be excluded)
127129
* filter_show_shows - True to show recently added TV shows
130+
* filter_shows_include - List of shows to include (Anything that does not match perfectly will not be included)
131+
* filter_shows_excluse - List of shows to exclude (Only titles that match perfectly will be excluded)
128132
* filter_show_seasons - True to show recently added TV seasons
133+
* filter_seasons_include - List of seasons to include (Anything that does not match perfectly will not be included)
134+
* filter_seasons_excluse - List of seasons to exclude (Only titles that match perfectly will be excluded)
129135
* filter_show_episodes - True to show recently added TV episodes
136+
* filter_episodes_include - List of episodes to include (Anything that does not match perfectly will not be included)
137+
* filter_episodes_excluse - List of episodes to exclude (Only titles that match perfectly will be excluded)
130138
* filter_show_email_images - True to show images in the email
131139
* filter_libraries - A list of library names to filter out - ['Home Videos', 'Private']
132140
* filter_sections_movies - Movie specific filters
@@ -138,6 +146,7 @@ The config file is in the scripts folder. Before first run of the script, pleas
138146
* postText - The text that should be added after this field for each title
139147
* include - A list of values that are each title must match at least one to be shown
140148
* exclude - A list of values that if the title matches any of, will not be shown
149+
* format - Date format to be applied (Only use this for air_date)
141150

142151
#####Messages
143152
* msg_notice - Used for special notices to the users. This can also be done through the command line using the -n flag.

scripts/config.conf

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,20 @@ email_skip_if_no_additions = False
5757
##Filtering
5858
filter_include_plex_web_link = True
5959
filter_show_movies = True
60+
filter_movies_include = []
61+
filter_movies_exclude = []
6062
filter_show_shows = True
63+
filter_shows_include = []
64+
filter_shows_exclude = []
6165
filter_show_seasons = True
66+
filter_seasons_include = []
67+
filter_seasons_exclude = []
6268
filter_show_episodes = True
69+
filter_episodes_include = []
70+
filter_episodes_exclude = []
6371
filter_show_email_images = True
72+
#Type of image to show for new TV Episodes - "episode", "season" or "show" (Defaults to episode)
73+
filter_episode_thumbnail_type = 'episode'
6474
#Name of libraries to filter out - ['Home Videos', 'Private']
6575
filter_libraries = []
6676
#The sections to include and ordering - tagline, summary, content_rating, duration, year, rating, studio, tags_genre, tags_director, tags_star
@@ -144,6 +154,15 @@ filter_sections_movies = {
144154
'postText':'%',
145155
'include':[],
146156
'exclude':[]
157+
},
158+
'air_date':{
159+
'order':11,
160+
'show': False,
161+
'preText':'Release Date: ',
162+
'postText':'',
163+
'include':[],
164+
'exclude':[],
165+
'format': '%B %d, %Y'
147166
}
148167
}
149168
filter_sections_TV = {
@@ -226,6 +245,15 @@ filter_sections_TV = {
226245
'postText':'%',
227246
'include':[],
228247
'exclude':[]
248+
},
249+
'air_date':{
250+
'order':11,
251+
'show': True,
252+
'preText':'Air Date: ',
253+
'postText':'',
254+
'include':[],
255+
'exclude':[],
256+
'format': '%B %d, %Y'
229257
}
230258
}
231259

scripts/plexEmail.py

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import cloudinary.uploader
1313
import cloudinary.api
1414
import imghdr
15+
import time
1516
from base64 import b64encode
1617
from collections import OrderedDict
1718
from datetime import date, timedelta
@@ -24,6 +25,33 @@
2425

2526
def replaceConfigTokens():
2627
## The below code is for backwards compatibility
28+
if ('filter_movies_include' not in config):
29+
config['filter_movies_include'] = []
30+
31+
if ('filter_movies_exclude' not in config):
32+
config['filter_movies_exclude'] = []
33+
34+
if ('filter_shows_include' not in config):
35+
config['filter_movies_include'] = []
36+
37+
if ('filter_shows_exclude' not in config):
38+
config['filter_movies_exclude'] = []
39+
40+
if ('filter_seasons_include' not in config):
41+
config['filter_movies_include'] = []
42+
43+
if ('filter_seasons_exclude' not in config):
44+
config['filter_movies_exclude'] = []
45+
46+
if ('filter_episodes_include' not in config):
47+
config['filter_movies_include'] = []
48+
49+
if ('filter_episodes_exclude' not in config):
50+
config['filter_movies_exclude'] = []
51+
52+
if ('filter_episode_thumbnail_type' not in config):
53+
config['filter_episode_thumbnail_type'] = 'episode'
54+
2755
if ('email_unsubscribe' not in config):
2856
config['email_unsubscribe'] = []
2957

@@ -357,7 +385,7 @@ def sendMail(email):
357385
#Create image headers
358386
for image in imgNames:
359387
fp = open(imgNames[image], 'rb')
360-
msgImage = MIMEImage(fp.read())
388+
msgImage = MIMEImage(fp.read(), _subtype="jpg")
361389
fp.close()
362390
msgImage.add_header('Content-ID', '<' + image + '>')
363391
msg.attach(msgImage)
@@ -697,11 +725,11 @@ def createWebHTML():
697725
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\')'
698726

699727
cur = con.cursor()
700-
cur.execute("SELECT MD.id, MD.parent_id, MD.metadata_type, MD.title, MD.title_sort, MD.original_title, MD.rating, MD.tagline, MD.summary, MD.content_rating, MD.duration, MD.user_thumb_url, MD.tags_genre, MD.tags_director, MD.tags_star, MD.year, MD.hash, MD.[index], MD.studio, ME.duration FROM metadata_items MD LEFT OUTER JOIN media_items ME ON MD.id = ME.metadata_item_id WHERE added_at >= " + dateSearch + " AND metadata_type >= 1 AND metadata_type <= 4 " + libraryFilter + " ORDER BY title_sort;")
728+
cur.execute("SELECT MD.id, MD.parent_id, MD.metadata_type, MD.title, MD.title_sort, MD.original_title, MD.rating, MD.tagline, MD.summary, MD.content_rating, MD.duration, MD.user_thumb_url, MD.tags_genre, MD.tags_director, MD.tags_star, MD.year, MD.hash, MD.[index], MD.studio, ME.duration, MD.originally_available_at FROM metadata_items MD LEFT OUTER JOIN media_items ME ON MD.id = ME.metadata_item_id WHERE added_at >= " + dateSearch + " AND metadata_type >= 1 AND metadata_type <= 4 " + libraryFilter + " ORDER BY title_sort;")
701729

702730
response = {};
703731
for row in cur:
704-
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], 'real_duration': row[19]}
732+
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], 'real_duration': row[19], 'air_date': row[20]}
705733

706734
emailNotice = ''
707735
htmlNotice = ''
@@ -802,12 +830,20 @@ def createWebHTML():
802830
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)):
803831
skipItem = True
804832
if (sections[section[0]]['show'] and movies[movie][section[0]] and movies[movie][section[0]] != ''):
805-
emailText += '<p class="lead">' + sections[section[0]]['preText'].decode('utf-8') + str(movies[movie][section[0]]).decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + '</p>'
806-
htmlText += '<p class="lead">' + sections[section[0]]['preText'].decode('utf-8') + str(movies[movie][section[0]]).decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + '</p>'
833+
displayText = str(movies[movie][section[0]])
834+
if ('format' in sections[section[0]] and sections[section[0]]['format'] != ''):
835+
displayText = time.strftime(sections[section[0]]['format'], time.strptime(displayText, '%Y-%m-%d %H:%M:%S'))
836+
emailText += '<p class="lead">' + sections[section[0]]['preText'].decode('utf-8') + displayText.decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + '</p>'
837+
htmlText += '<p class="lead">' + sections[section[0]]['preText'].decode('utf-8') + displayText.decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + '</p>'
807838

808839
emailText += '</td></tr></table><br/>&nbsp;<br/>&nbsp;'
809840
htmlText += '</div></div><br/>&nbsp;<br/>&nbsp;'
810841

842+
titleFilter = []
843+
844+
if (movies[movie]['title'] in config['filter_movies_exclude'] or len(set(titleFilter).intersection(config['filter_movies_exclude'])) > 0 or (config['filter_movies_include'] and movies[movie]['title'] not in config['filter_movies_include'] and len(set(titleFilter).intersection(config['filter_movies_include'])) == 0)):
845+
skipItem = True
846+
811847
if (not skipItem):
812848
movieCount += 1
813849
emailMovies += emailText
@@ -852,12 +888,20 @@ def createWebHTML():
852888
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)):
853889
skipItem = True
854890
if (sections[section[0]]['show'] and tvShows[show][section[0]] and tvShows[show][section[0]] != ''):
855-
emailText += '<p class="lead">' + sections[section[0]]['preText'].decode('utf-8') + str(tvShows[show][section[0]]).decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + '</p>'
856-
htmlText += '<p class="lead">' + sections[section[0]]['preText'].decode('utf-8') + str(tvShows[show][section[0]]).decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + '</p>'
891+
displayText = str(tvShows[show][section[0]])
892+
if ('format' in sections[section[0]] and sections[section[0]]['format'] != ''):
893+
displayText = time.strftime(sections[section[0]]['format'], time.strptime(displayText, '%Y-%m-%d %H:%M:%S'))
894+
emailText += '<p class="lead">' + sections[section[0]]['preText'].decode('utf-8') + displayText.decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + '</p>'
895+
htmlText += '<p class="lead">' + sections[section[0]]['preText'].decode('utf-8') + displayText.decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + '</p>'
857896

858897
emailText += '</td></tr></table><br/>&nbsp;<br/>&nbsp;'
859898
htmlText += '</div></div><br/>&nbsp;<br/>&nbsp;'
860899

900+
titleFilter = []
901+
902+
if (tvShows[show]['title'] in config['filter_shows_exclude'] or len(set(titleFilter).intersection(config['filter_shows_exclude'])) > 0 or (config['filter_shows_include'] and tvShows[show]['title'] not in config['filter_shows_include'] and len(set(titleFilter).intersection(config['filter_shows_include'])) == 0)):
903+
skipItem = True
904+
861905
if (not skipItem):
862906
showCount += 1
863907
emailTVShows += emailText
@@ -935,6 +979,11 @@ def createWebHTML():
935979
emailText += '</td></tr></table><br/>&nbsp;<br/>&nbsp;'
936980
htmlText += '</div></div><br/>&nbsp;<br/>&nbsp;'
937981

982+
titleFilter = []
983+
984+
if (tvSeasons[season]['title'] in config['filter_seasons_exclude'] or len(set(titleFilter).intersection(config['filter_seasons_exclude'])) > 0 or (config['filter_seasons_include'] and tvSeasons[season]['title'] not in config['filter_seasons_include'] and len(set(titleFilter).intersection(config['filter_seasons_include'])) == 0)):
985+
skipItem = True
986+
938987
if (not skipItem):
939988
seasonCount += 1
940989
emailTVSeasons += emailText
@@ -984,13 +1033,14 @@ def createWebHTML():
9841033
title += tvEpisodes[episode]['title']
9851034
hash = str(tvEpisodes[episode]['hash'])
9861035
imageInfo = {}
987-
if (tvEpisodes[episode]['user_thumb_url'] != ''):
1036+
imageTypeToUse = 'show' if (tvEpisodes[episode]['show_thumb_url'] != '' and config['filter_episode_thumbnail_type'] == 'show') else 'season' if (tvEpisodes[episode]['season_thumb_url'] != '' and config['filter_episode_thumbnail_type'] == 'season') else 'episode' if (tvEpisodes[episode]['user_thumb_url'] != '') else ''
1037+
if (imageTypeToUse == 'episode'):
9881038
imageInfo['thumb'] = tvEpisodes[episode]['user_thumb_url']
9891039
imageInfo = processImage(hash, imageInfo['thumb'], 'episode', tvEpisodes[episode]['season_index'], tvEpisodes[episode]['index'])
990-
elif (tvEpisodes[episode]['season_thumb_url'] != ''):
1040+
elif (imageTypeToUse == 'season'):
9911041
imageInfo['thumb'] = tvEpisodes[episode]['season_thumb_url']
9921042
imageInfo = processImage(hash, imageInfo['thumb'], 'season', tvEpisodes[episode]['season_index'], 0)
993-
elif (tvEpisodes[episode]['show_thumb_url'] != ''):
1043+
elif (imageTypeToUse == 'show'):
9941044
imageInfo['thumb'] = tvEpisodes[episode]['show_thumb_url']
9951045
imageInfo = processImage(hash, imageInfo['thumb'], 'show', 0, 0)
9961046

@@ -1017,12 +1067,20 @@ def createWebHTML():
10171067
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)):
10181068
skipItem = True
10191069
if (sections[section[0]]['show'] and tvEpisodes[episode][section[0]] and tvEpisodes[episode][section[0]] != ''):
1020-
emailText += '<p class="lead">' + sections[section[0]]['preText'].decode('utf-8') + str(tvEpisodes[episode][section[0]]).decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + '</p>'
1021-
htmlText += '<p class="lead">' + sections[section[0]]['preText'].decode('utf-8') + str(tvEpisodes[episode][section[0]]).decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + '</p>'
1070+
displayText = str(tvEpisodes[episode][section[0]])
1071+
if ('format' in sections[section[0]] and sections[section[0]]['format'] != ''):
1072+
displayText = time.strftime(sections[section[0]]['format'], time.strptime(displayText, '%Y-%m-%d %H:%M:%S'))
1073+
emailText += '<p class="lead">' + sections[section[0]]['preText'].decode('utf-8') + displayText.decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + '</p>'
1074+
htmlText += '<p class="lead">' + sections[section[0]]['preText'].decode('utf-8') + displayText.decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + '</p>'
10221075

10231076
emailText += '</td></tr></table><br/>&nbsp;<br/>&nbsp;'
10241077
htmlText += '</div></div><br/>&nbsp;<br/>&nbsp;'
10251078

1079+
titleFilter = []
1080+
1081+
if (tvEpisodes[episode]['show_title'] in config['filter_episodes_exclude'] or len(set(titleFilter).intersection(config['filter_episodes_exclude'])) > 0 or (config['filter_episodes_include'] and tvEpisodes[episode]['show_title'] not in config['filter_episodes_include'] and len(set(titleFilter).intersection(config['filter_episodes_include'])) == 0)):
1082+
skipItem = True
1083+
10261084
if (not skipItem):
10271085
episodeCount += 1
10281086
emailTVEpisodes += emailText

0 commit comments

Comments
 (0)