diff --git a/scripts/plexEmail.py b/scripts/plexEmail.py index 610fa53..1ecbf52 100644 --- a/scripts/plexEmail.py +++ b/scripts/plexEmail.py @@ -27,255 +27,258 @@ SCRIPT_VERSION = 'v0.9.3' +reload(sys) +sys.setdefaultencoding("utf-8") + def replaceConfigTokens(): ## The below code is for backwards compatibility if ('filter_include_original_title' not in config): config['filter_include_original_title'] = True - + if ('logging_enabled' not in config): config['logging_enabled'] = True - + if ('plex_web_server_guid' not in config): config['plex_web_server_guid'] = '' - + if ('upload_cloudinary_use_https' not in config): config['upload_cloudinary_use_https'] = True - + if ('logging_retain_previous_logs' not in config): config['logging_retain_previous_logs'] = True - + if ('logging_debug_level' not in config): config['logging_debug_level'] = 'INFO' - + if ('logging_file_location' not in config): config['logging_file_location'] = '' - + if ('upload_cloudinary_api_secret' not in config): config['upload_cloudinary_api_secret'] = True - + if ('artist_sort_1' not in config.keys() or config['artist_sort_1'] == ""): config['artist_sort_1'] = 'title_sort' - + if ('artist_sort_1_reverse' not in config.keys() or config['artist_sort_1_reverse'] == ""): config['artist_sort_1_reverse'] = False if ('artist_sort_2_reverse' not in config.keys() or config['artist_sort_2_reverse'] == ""): config['artist_sort_2_reverse'] = False if ('artist_sort_3_reverse' not in config.keys() or config['artist_sort_3_reverse'] == ""): config['artist_sort_3_reverse'] = False - + if ('album_sort_1' not in config.keys() or config['album_sort_1'] == ""): config['album_sort_1'] = 'title_sort' - + if ('album_sort_1_reverse' not in config.keys() or config['album_sort_1_reverse'] == ""): config['album_sort_1_reverse'] = False if ('album_sort_2_reverse' not in config.keys() or config['album_sort_2_reverse'] == ""): config['album_sort_2_reverse'] = False if ('album_sort_3_reverse' not in config.keys() or config['album_sort_3_reverse'] == ""): config['album_sort_3_reverse'] = False - + if ('msg_new_artists_header' not in config): config['msg_new_artists_header'] = 'New Artists' - + if ('msg_new_albums_header' not in config): config['msg_new_albums_header'] = 'New Albums' - + if ('msg_new_songs_header' not in config): config['msg_new_songs_header'] = 'New Songs' - + if ('msg_artists_link' not in config): config['msg_artists_link'] = 'Artists' - + if ('msg_albums_link' not in config): config['msg_albums_link'] = 'Albums' - + if ('msg_songs_link' not in config): config['msg_songs_link'] = 'Songs' - + if ('filter_show_artists' not in config): config['filter_show_artists'] = True - + if ('filter_show_albums' not in config): config['filter_show_albums'] = True - + if ('filter_show_songs' not in config): config['filter_show_songs'] = False - + if ('filter_sections_Music' not in config): config['filter_sections_Music'] = {'tagline':{'order':1,'show':False,'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':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':[]},'air_date':{'order':11,'show': True,'preText':'Release Date: ','postText':'','include':[],'exclude':[],'format': '%B %d, %Y'},'track_list':{'order':12,'show': True,'headerText':{'trackNumber':'Track#','songName':'Song Name','artistName':'Artist(s)','duration':'Duration'}}} - + if ('filter_artists_include' not in config): config['filter_artists_include'] = [] - + if ('filter_artists_exclude' not in config): config['filter_artists_exclude'] = [] - + if ('filter_albums_include' not in config): config['filter_albums_include'] = [] - + if ('filter_albums_exclude' not in config): config['filter_albums_exclude'] = [] - + if ('filter_songs_include' not in config): config['filter_songs_include'] = [] - + if ('filter_songs_exclude' not in config): config['filter_songs_exclude'] = [] - + if ('filter_song_thumbnail_type' not in config): config['filter_song_thumbnail_type'] = 'album' - + if ('filter_movies_include' not in config): config['filter_movies_include'] = [] - + if ('filter_movies_exclude' not in config): config['filter_movies_exclude'] = [] - + if ('filter_shows_include' not in config): config['filter_shows_include'] = [] - + if ('filter_shows_exclude' not in config): config['filter_shows_exclude'] = [] - + if ('filter_seasons_include' not in config): config['filter_seasons_include'] = [] - + if ('filter_seasons_exclude' not in config): config['filter_seasons_exclude'] = [] - + if ('filter_episodes_include' not in config): config['filter_episodes_include'] = [] - + if ('filter_episodes_exclude' not in config): config['filter_episodes_exclude'] = [] - + if ('filter_episode_thumbnail_type' not in config): config['filter_episode_thumbnail_type'] = 'episode' - + if ('email_unsubscribe' not in config): config['email_unsubscribe'] = [] - + if ('email_use_bcc' not in config): config['email_use_bcc'] = False - + if ('email_to_send_to_shared_users' not in config): config['email_to_send_to_shared_users'] = False - + if ('plex_username' not in config): config['plex_username'] = '' - + if ('plex_password' not in config): config['plex_password'] = '' - + if ('filter_show_email_images' not in config): config['filter_show_email_images'] = True - + if ('msg_notice' not in config): config['msg_notice'] = '' - + if ('email_use_ssl' not in config): config['email_use_ssl'] = False - + if ('filter_include_plex_web_link' not in config): config['filter_include_plex_web_link'] = True - + if ('filter_libraries' not in config): config['filter_libraries'] = [] - + 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 - + if ('filter_show_shows' not in config): config['filter_show_shows'] = True - + if ('filter_show_seasons' not in config): config['filter_show_seasons'] = True - + if ('filter_show_episodes' not in config): config['filter_show_episodes'] = True - + if ('date_minutes_back_to_search' not in config): config['date_minutes_back_to_search'] = 0 - + if ('msg_no_new_content' not in config): config['msg_no_new_content'] = 'There have been no new additions to Plex from {past_day} through {current_day}.' - + if ('email_skip_if_no_additions' not in config): config['email_skip_if_no_additions'] = False - + if ('web_skip_if_no_additions' not in config): config['web_skip_if_no_additions'] = False - + if ('date_days_back_to_search' not in config and 'days_back_to_search' in config): config['date_days_back_to_search'] = config['days_back_to_search'] - + if ('movie_sort_1' not in config.keys() or config['movie_sort_1'] == ""): config['movie_sort_1'] = 'rating' if ('movie_sort_2' not in config.keys() or config['movie_sort_2'] == ""): config['movie_sort_2'] = 'title_sort' - + if ('movie_sort_1_reverse' not in config.keys() or config['movie_sort_1_reverse'] == ""): config['movie_sort_1_reverse'] = True if ('movie_sort_2_reverse' not in config.keys() or config['movie_sort_2_reverse'] == ""): config['movie_sort_2_reverse'] = False if ('movie_sort_3_reverse' not in config.keys() or config['movie_sort_3_reverse'] == ""): config['movie_sort_3_reverse'] = False - + if ('show_sort_1' not in config.keys() or config['show_sort_1'] == ""): config['show_sort_1'] = 'title_sort' - + if ('show_sort_1_reverse' not in config.keys() or config['show_sort_1_reverse'] == ""): config['show_sort_1_reverse'] = False if ('show_sort_2_reverse' not in config.keys() or config['show_sort_2_reverse'] == ""): config['show_sort_2_reverse'] = False if ('show_sort_3_reverse' not in config.keys() or config['show_sort_3_reverse'] == ""): config['show_sort_3_reverse'] = False - + if ('season_sort_1' not in config.keys() or config['season_sort_1'] == ""): config['season_sort_1'] = 'title_sort' if ('season_sort_2' not in config.keys() or config['season_sort_2'] == ""): config['season_sort_2'] = 'index' - + if ('season_sort_1_reverse' not in config.keys() or config['season_sort_1_reverse'] == ""): config['season_sort_1_reverse'] = False if ('season_sort_2_reverse' not in config.keys() or config['season_sort_2_reverse'] == ""): config['season_sort_2_reverse'] = False if ('season_sort_3_reverse' not in config.keys() or config['season_sort_3_reverse'] == ""): config['season_sort_3_reverse'] = False - + if ('episode_sort_1' not in config.keys() or config['episode_sort_1'] == ""): config['episode_sort_1'] = 'show_title_sort' if ('episode_sort_2' not in config.keys() or config['episode_sort_2'] == ""): config['episode_sort_2'] = 'season_index' if ('episode_sort_3' not in config.keys() or config['episode_sort_3'] == ""): config['episode_sort_3'] = 'index' - + if ('episode_sort_1_reverse' not in config.keys() or config['episode_sort_1_reverse'] == ""): config['episode_sort_1_reverse'] = False if ('episode_sort_2_reverse' not in config.keys() or config['episode_sort_2_reverse'] == ""): config['episode_sort_2_reverse'] = False if ('episode_sort_3_reverse' not in config.keys() or config['episode_sort_3_reverse'] == ""): config['episode_sort_3_reverse'] = False - + # The below code is replacing tokens if (config['web_domain'].rfind('/') < len(config['web_domain']) - len('/')): config['web_domain'] = config['web_domain'] + '/' - + if (config['date_days_back_to_search'] == 31): now = datetime.datetime.now() if (now.month == 1): config['date_days_back_to_search'] = calendar.monthrange(now.year - 1, 12)[1] else: config['date_days_back_to_search'] = calendar.monthrange(now.year, now.month - 1)[1] - + for value in config: if (isinstance(config[value], str)): config[value] = config[value].replace('{past_day}', str((date.today() - timedelta(days=config['date_days_back_to_search'], hours=config['date_hours_back_to_search'], minutes=config['date_minutes_back_to_search'])).strftime(config['date_format']))) @@ -285,20 +288,20 @@ def replaceConfigTokens(): if (config['plex_data_folder'].rfind(os.path.sep) < len(config['plex_data_folder']) - len(os.path.sep)): config['plex_data_folder'] = config['plex_data_folder'] + os.path.sep - + if (config['plex_data_folder'].find('Plex Media Server') >= 0): config['plex_data_folder'] = config['plex_data_folder'][0:config['plex_data_folder'].find('Plex Media Server')] - + 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 - + if (config['web_domain'] != '' and config['web_domain'].rfind('/') < len(config['web_domain']) - len('/')): config['web_domain'] = config['web_domain'] + '/' - + if (config['logging_file_location'].rfind(os.path.sep) < len(config['logging_file_location']) - len(os.path.sep)): config['logging_file_location'] = config['logging_file_location'] + os.path.sep - - + + def convertToHumanReadable(valuesToConvert): convertedValues = {} for value in valuesToConvert: @@ -323,7 +326,7 @@ def getSharedUserEmails(): emails = [] if (config['plex_username'] == '' or config['plex_password'] == ''): return emails - + url = 'https://my.plexapp.com/users/sign_in.json' logging.info('getSharedUserEmails: url = ' + url) base64string = 'Basic ' + base64.encodestring('%s:%s' % (config['plex_username'], config['plex_password'])).replace('\n', '') @@ -334,7 +337,7 @@ def getSharedUserEmails(): logging.info('getSharedUserEmails: response = ' + str(response.text)) token = json.loads(response.text)['user']['authentication_token']; logging.info('getSharedUserEmails: token = ' + token) - + url = 'https://plex.tv/pms/friends/all' logging.info('getSharedUserEmails: url = ' + url) headers = {'Accept': 'application/json', 'X-Plex-Token': token} @@ -342,14 +345,14 @@ def getSharedUserEmails(): response = requests.get(url, headers=headers) logging.info('getSharedUserEmails: response = ' + str(response)) logging.info('getSharedUserEmails: response = ' + str(response.text.encode('ascii', 'ignore'))) - + parsed = XML(response.text.encode('ascii', 'ignore')) for elem in parsed: for name, value in sorted(elem.attrib.items()): if (name == 'email'): logging.info('getSharedUserEmails: adding email - ' + value.lower()) emails.append(value.lower()) - + logging.info('getSharedUserEmails: Returning shared emails') logging.debug('getSharedUserEmails: email list - ' + ' '.join(emails)) return emails @@ -363,7 +366,7 @@ def deleteImages(): logging.debug('deleteImages: deleting image: ' + folder + file) os.remove(folder + file) logging.info('deleteImages: end') - + def processImage(imageHash, thumb, mediaType, seasonIndex, episodeIndex): logging.info('processImage: begin') logging.info('processImage: imageHash = ' + imageHash + ' - thumb = ' + thumb + ' - mediaType = ' + mediaType + ' - seasonIndex = ' + str(seasonIndex) + ' - episodeIndex = ' + str(episodeIndex)) @@ -374,7 +377,7 @@ def processImage(imageHash, thumb, mediaType, seasonIndex, episodeIndex): thumbObj['webImgPath'] = '' thumbObj['emailImgPath'] = '' return thumbObj - + if (thumb.find('http://') >= 0 or thumb.find('https://') >= 0): logging.info('processImage: thumb is already an externally hosted image') thumbObj['webImgPath'] = thumb @@ -421,12 +424,12 @@ def processImage(imageHash, thumb, mediaType, seasonIndex, episodeIndex): imgName += '_' + imageHash webImgFullPath = config['web_domain'] + config['web_path'] + '/images/' + imgName + '.jpg' img = config['web_folder'] + config['web_path'] + os.path.sep + 'images' + os.path.sep + imgName + '.jpg' - + logging.info('processImage: imgLocation = ' + imgLocation) logging.info('processImage: webImgFullPath = ' + webImgFullPath) logging.info('processImage: img = ' + img) - - + + cloudinaryURL = '' if ('upload_use_cloudinary' in config and config['upload_use_cloudinary']): logging.info('processImage: Uploading to cloudinary') @@ -443,7 +446,7 @@ def processImage(imageHash, thumb, mediaType, seasonIndex, episodeIndex): else: logging.info('processImage: No email image') thumbObj['emailImgPath'] = '' - + if (cloudinaryURL != ''): logging.info('processImage: Setting image paths to cloudinary') thumbObj['webImgPath'] = cloudinaryURL @@ -460,16 +463,16 @@ def processImage(imageHash, thumb, mediaType, seasonIndex, episodeIndex): thumbObj['webImgPath'] = 'images/' + imgName + '.jpg' else: thumbObj['webImgPath'] = '' - + return thumbObj - + def uploadToImgur(imgToUpload, nameOfUpload): client_id = 'a0c7b089b62b220' headers = {"Authorization": "Client-ID " + client_id} url = "https://api.imgur.com/3/upload.json" if (os.path.isfile(imgToUpload)): response = requests.post( - url, + url, headers = headers, data = { 'image': b64encode(open(imgToUpload, 'rb').read()), @@ -482,7 +485,7 @@ def uploadToImgur(imgToUpload, nameOfUpload): return data['link'] else: return '' - + def uploadToCloudinary(imgToUpload): logging.info('uploadToCloudinary: begin') if (os.path.isfile(imgToUpload)): @@ -501,16 +504,16 @@ def uploadToCloudinary(imgToUpload): else: logging.info('uploadToCloudinary: file not located') return '' - + def containsnonasciicharacters(str): - return not all(ord(c) < 128 for c in str) + return not all(ord(c) < 128 for c in str) def addheader(message, headername, headervalue): if containsnonasciicharacters(headervalue): h = Header(headervalue, 'utf-8') message[headername] = h else: - message[headername] = headervalue + message[headername] = headervalue return message def sendMail(email): @@ -534,18 +537,18 @@ def sendMail(email): # Create the body of the message (a plain-text and an HTML version). text = config['msg_email_teaser'] - + # Record the MIME types of both parts - text/plain and text/html. if containsnonasciicharacters(emailHTML): htmltext = MIMEText(emailHTML, 'html','utf-8') else: - htmltext = MIMEText(emailHTML, 'html') + htmltext = MIMEText(emailHTML, 'html') if(containsnonasciicharacters(text)): - plaintext = MIMEText(text,'plain','utf-8') + plaintext = MIMEText(text,'plain','utf-8') else: plaintext = MIMEText(text,'plain') - + #Create image headers for image in imgNames: fp = open(imgNames[image], 'rb') @@ -574,9 +577,9 @@ def sendMail(email): server.login(gmail_user, gmail_pwd) server.sendmail(FROM, TO, msg.as_string()) server.close() - + return 1; - + def createEmailHTML(): emailText = """ @@ -596,7 +599,7 @@ def createEmailHTML(): - + @@ -604,18 +607,70 @@ def createEmailHTML(): @@ -638,7 +693,7 @@ def createEmailHTML():
""" - + emailText += emailNotice if (movieCount > 0 and config['filter_show_movies']): emailText += emailMovies + '
 ' @@ -654,18 +709,18 @@ def createEmailHTML(): emailText += emailAlbums + '
 ' if (songCount > 0 and config['filter_show_songs']): emailText += emailSongs - + if(not hasNewContent): emailText += """

""" + config['msg_no_new_content'] + """

""" - + emailText += """ -
@@ -795,7 +850,7 @@ def createWebHTML():
""" - + htmlText += htmlNotice if (movieCount > 0 and config['filter_show_movies']): htmlText += htmlMovies + '
 ' @@ -811,12 +866,12 @@ def createWebHTML(): htmlText += htmlAlbums + '
 ' if (songCount > 0 and config['filter_show_songs']): htmlText += htmlSongs - + if(not hasNewContent): htmlText += """

""" + config['msg_no_new_content'] + """

""" - + htmlText += """

 
 ' - + titleFilter = [] - + if (artists[artist]['title'] in config['filter_artists_exclude'] or len(set(titleFilter).intersection(config['filter_artists_exclude'])) > 0 or (config['filter_artists_include'] and artists[artist]['title'] not in config['filter_artists_include'] and len(set(titleFilter).intersection(config['filter_artists_include'])) == 0)): skipItem = True - + if (not skipItem): artistCount += 1 emailArtists += emailText htmlArtists += htmlText - + for album in albums: cur2 = con.cursor() cur2.execute("SELECT title, title_sort, original_title, rating, tagline, summary, content_rating, duration, tags_genre, tags_director, tags_star, year, hash, user_thumb_url, studio FROM metadata_items WHERE id = " + str(albums[album]['parent_id']) + ";") @@ -1490,13 +1562,13 @@ def exceptionHandler(type, value, tb): albums[album]['parent_hash'] = row[12] albums[album]['parent_thumb_url'] = row[13] albums[album]['studio'] = row[14] - + cur2.close() del cur2 - + cur2 = con.cursor() cur2.execute("SELECT MD.id, MD.title, MD.title_sort, MD.original_title, MD.[index], ME.duration, ME.audio_codec FROM metadata_items MD LEFT OUTER JOIN media_items ME ON MD.id = ME.metadata_item_id WHERE parent_id = " + str(albums[album]['id']) + ";") - + albums[album]['tracks'] = {} for row in cur2: duration = row[5] @@ -1512,7 +1584,7 @@ def exceptionHandler(type, value, tb): except TypeError: duration = 'N/A' albums[album]['tracks'][row[4]] = {'id': row[0], 'title': row[1], 'title_sort': row[2], 'original_title': row[3], 'index': row[4], 'duration': duration, 'codec': row[6]} - + cur2.close() del cur2 if ('album_sort_3' in config and config['album_sort_3'] != ''): @@ -1521,7 +1593,7 @@ def exceptionHandler(type, value, tb): albums = OrderedDict(sorted(albums.iteritems(), key=lambda t: t[1][config['album_sort_2']], reverse=config['album_sort_2_reverse'])) if ('album_sort_1' in config and config['album_sort_1'] != ''): albums = OrderedDict(sorted(albums.iteritems(), key=lambda t: t[1][config['album_sort_1']], reverse=config['album_sort_1_reverse'])) - + for album in albums: albums[album] = convertToHumanReadable(albums[album]) title = '' @@ -1543,7 +1615,7 @@ def exceptionHandler(type, value, tb): pwLink = plexWebLink + str(albums[album]['id']) else: pwLink = '' - + emailText += '' if (config['filter_show_email_images']): emailText += '' @@ -1551,7 +1623,7 @@ def exceptionHandler(type, value, tb): htmlText += '
' htmlText += '' htmlText += '

' + title.decode('utf-8') + '

' - + tracklistEmailText = '' tracklistHTMLText = '' sections = config['filter_sections_Music'] @@ -1576,19 +1648,19 @@ def exceptionHandler(type, value, tb): displayText = time.strftime(sections[section[0]]['format'], time.strptime(displayText, '%Y-%m-%d %H:%M:%S')) emailText += '

' + sections[section[0]]['preText'].decode('utf-8') + displayText.decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + '

' htmlText += '

' + sections[section[0]]['preText'].decode('utf-8') + displayText.decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + '

' - + emailText += '
' emailText += tracklistEmailText if (trackCount > 0) else '' emailText += '
 
 ' htmlText += '' htmlText += tracklistHTMLText if (trackCount > 0) else '' htmlText += '
 
 ' - + titleFilter = [] - + if (albums[album]['title'] in config['filter_albums_exclude'] or len(set(titleFilter).intersection(config['filter_albums_exclude'])) > 0 or (config['filter_albums_include'] and albums[album]['title'] not in config['filter_albums_include'] and len(set(titleFilter).intersection(config['filter_albums_include'])) == 0)): skipItem = True - + if (not skipItem): albumCount += 1 emailAlbums += emailText @@ -1602,10 +1674,10 @@ def exceptionHandler(type, value, tb): # songs[song]['season_thumb_url'] = row[0] # parent_id = row[1] # songs[song]['season_index'] = row[2] - + # cur3 = con.cursor() # cur3.execute("SELECT title, title_sort, original_title, content_rating, duration, tags_genre, tags_star, hash, user_thumb_url, studio FROM metadata_items WHERE id = " + str(parent_id) + ";") - + # for row2 in cur3: # songs[song]['show_title'] = row2[0] # songs[song]['show_title_sort'] = row2[1] @@ -1617,14 +1689,14 @@ def exceptionHandler(type, value, tb): # songs[song]['hash'] = row2[7] # songs[song]['show_thumb_url'] = row2[8] # songs[song]['studio'] = row2[9] - + # if ('song_sort_3' in config and config['song_sort_3'] != ''): # songs = OrderedDict(sorted(songs.iteritems(), key=lambda t: t[1][config['song_sort_3']], reverse=config['song_sort_3_reverse'])) # if ('song_sort_2' in config and config['song_sort_2'] != ''): # songs = OrderedDict(sorted(songs.iteritems(), key=lambda t: t[1][config['song_sort_2']], reverse=config['song_sort_2_reverse'])) # if ('song_sort_1' in config and config['song_sort_1'] != ''): # songs = OrderedDict(sorted(songs.iteritems(), key=lambda t: t[1][config['song_sort_1']], reverse=config['song_sort_1_reverse'])) - + # for song in songs: # if (songs[song]['parent_id'] not in tvSeasons): # songs[song] = convertToHumanReadable(songs[song]) @@ -1650,9 +1722,9 @@ def exceptionHandler(type, value, tb): # elif (imageTypeToUse == 'artist'): # imageInfo['thumb'] = songs[song]['show_thumb_url'] # imageInfo = processImage(hash, imageInfo['thumb'], 'show', 0, 0) - + # print imageInfo - # print + # print # skipItem = False # emailText = '' # htmlText = '' @@ -1660,7 +1732,7 @@ def exceptionHandler(type, value, tb): # pwLink = plexWebLink + str(songs[song]['id']) # else: # pwLink = '' - + # emailText += '' # if (config['filter_show_email_images']): # emailText += '' @@ -1670,7 +1742,7 @@ def exceptionHandler(type, value, tb): # htmlText += '' # htmlText += '

' + showTitle.decode('utf-8') + '

' # htmlText += '

S' + str(songs[song]['season_index']) + ' E' + str(songs[song]['index']) + ': ' + title.decode('utf-8') + '

' - + # sections = config['filter_sections_TV'] # for section in sorted(sections.iteritems(), key=lambda t: t[1]['order']): # if (songs[song][section[0]] in sections[section[0]]['exclude'] or len(set(songs[song][section[0] + '_filter']).intersection(sections[section[0]]['exclude'])) > 0 or (sections[section[0]]['include'] and songs[song][section[0]] not in sections[section[0]]['include'] and len(set(songs[song][section[0] + '_filter']).intersection(sections[section[0]]['include'])) == 0)): @@ -1681,25 +1753,25 @@ def exceptionHandler(type, value, tb): # displayText = time.strftime(sections[section[0]]['format'], time.strptime(displayText, '%Y-%m-%d %H:%M:%S')) # emailText += '

' + sections[section[0]]['preText'].decode('utf-8') + displayText.decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + '

' # htmlText += '

' + sections[section[0]]['preText'].decode('utf-8') + displayText.decode('utf-8') + sections[section[0]]['postText'].decode('utf-8') + '

' - + # emailText += '

 
 ' # htmlText += '
 
 ' - + # titleFilter = [] - + # if (songs[song]['show_title'] in config['filter_songs_exclude'] or len(set(titleFilter).intersection(config['filter_songs_exclude'])) > 0 or (config['filter_songs_include'] and songs[song]['show_title'] not in config['filter_songs_include'] and len(set(titleFilter).intersection(config['filter_songs_include'])) == 0)): # skipItem = True - + # if (not skipItem): # songCount += 1 # emailSongs += emailText # htmlSongs += htmlText - + if ((movieCount > 0 and config['filter_show_movies']) or (showCount > 0 and config['filter_show_shows']) or (seasonCount > 0 and config['filter_show_seasons']) or (episodeCount > 0 and config['filter_show_episodes']) or (artistCount > 0 and config['filter_show_artists']) or (albumCount > 0 and config['filter_show_albums']) or (songCount > 0 and config['filter_show_songs'])): hasNewContent = True else: hasNewContent = False - + emailHTML = createEmailHTML() webHTML = createWebHTML() @@ -1711,7 +1783,7 @@ def exceptionHandler(type, value, tb): print 'Web page was not created because the option is disabled in the config file.' else: print 'Web page was not created because there were no new additions in the timeframe specified.' - + if (config['email_enabled'] and (not config['email_skip_if_no_additions'] or hasNewContent)): # try: if (config['email_to_send_to_shared_users']): @@ -1720,7 +1792,7 @@ def exceptionHandler(type, value, tb): #Remove duplicates by converting to a set config['email_to'] = set(config['email_to']) - + emailCount = 0 if (testMode): success = sendMail([config['email_from']]) @@ -1742,4 +1814,4 @@ def exceptionHandler(type, value, tb): print 'Emails were not sent because there were no new additions in the timeframe specified.' con.close() -os.remove(DATABASE_FILE + '_plexemail') \ No newline at end of file +os.remove(DATABASE_FILE + '_plexemail') diff --git a/web/css/one-page-wonder.css b/web/css/one-page-wonder.css index e7ff0c0..dbc1a0f 100644 --- a/web/css/one-page-wonder.css +++ b/web/css/one-page-wonder.css @@ -40,6 +40,10 @@ body { margin: 80px 0; } +.featurette-divider:first-of-type { + margin: 0 !important; +} + .featurette { overflow: hidden; } @@ -59,6 +63,7 @@ body { footer { margin: 50px 0; + text-align: center; } .info { @@ -190,4 +195,44 @@ footer { margin-bottom: 10px; margin-left: 0; } +} + +@media(max-width:767px) { + .headline { + padding: 75px 0 75px 0 !important; + } + + .featurette { + text-align: justify; + } + + .featurette > div { + margin-left: 0 !important; + } + + .pull-left { + float: none !important; + } + + .pull-right { + float: none !important; + } + + .featurette-image { + margin-right: auto !important; + margin-bottom: 30px !important; + margin-left: auto !important; + } + + .featurette-heading { + text-align: center; + } + + .subtitle { + text-align: center; + } + + .lead:first-of-type { + text-align: center; + } } \ No newline at end of file