import sqlite3 import sys import argparse import os import json import operator import shutil import smtplib import requests import base64 import cloudinary import cloudinary.uploader import cloudinary.api import imghdr import time import logging import traceback from base64 import b64encode from collections import OrderedDict from datetime import date, timedelta from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.MIMEImage import MIMEImage from email.header import Header from email.utils import formataddr from xml.etree.ElementTree import XML SCRIPT_VERSION = 'v0.9.3' 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']))) config[value] = config[value].replace('{current_day}', str(date.today().strftime(config['date_format']))) config[value] = config[value].replace('{website}', config['web_domain'] + config['web_path']) config[value] = config[value].replace('{path_sep}', os.path.sep) 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: convertedValues[value] = valuesToConvert[value] convertedValues[value + '_filter'] = [valuesToConvert[value]] if (not valuesToConvert[value]): continue if (value == 'duration'): if (valuesToConvert['real_duration']): convertedValues[value] = str(valuesToConvert['real_duration'] // 1000 // 60) else: 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 getSharedUserEmails(): logging.info('getSharedUserEmails: begin') 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', '') headers = {'Authorization': base64string, 'X-Plex-Client-Identifier': 'plexEmail'} logging.debug('getSharedUserEmails: headers = ' + str(headers)) response = requests.post(url, headers=headers) logging.info('getSharedUserEmails: response = ' + str(response)) 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} logging.debug('getSharedUserEmails: headers = ' + str(headers)) 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 def deleteImages(): logging.info('deleteImages: begin') folder = config['web_folder'] + config['web_path'] + os.path.sep + 'images' + os.path.sep logging.info('deleteImages: deleting images from: ' + folder) for file in os.listdir(folder): if (file.endswith('.jpg')): 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)) thumbObj = {} imgLocation = '' if (not thumb or thumb == ''): logging.info('processImage: thumb is either null or empty, returning no image') 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 thumbObj['emailImgPath'] = thumb return thumbObj else: if (thumb.find('media://') >= 0): logging.info('processImage: thumb begins with media://') thumb = thumb[8:len(thumb)] imgName = thumb[thumb.rindex('/') + 1:thumb.rindex('.')] + hash imgLocation = config['plex_data_folder'] + 'Plex Media Server' + os.path.sep + 'Media' + os.path.sep + 'localhost' + os.path.sep + '' + thumb elif (thumb.find('upload://') >= 0): logging.info('processImage: thumb begins with upload://') thumb = thumb[9:len(thumb)] category = thumb[0:thumb.index('/')] imgName = thumb[thumb.rindex('/') + 1:len(thumb)] if (mediaType == 'movie'): type = "Movies" else: type = "TV Shows" imgLocation = config['plex_data_folder'] + 'Plex Media Server' + os.path.sep + 'Metadata' + os.path.sep + type + os.path.sep + imageHash[0] + os.path.sep + imageHash[1:len(imageHash)] + '.bundle' + os.path.sep + 'Uploads' + os.path.sep + category + os.path.sep + imgName else: thumb = thumb[11:len(thumb)] category = thumb[0:thumb.index('/')] imgName = thumb[thumb.index('_') + 1:len(thumb)] if (mediaType == 'movie'): indexer = thumb[thumb.index('/') + 1:thumb.index('_')] imgLocation = config['plex_data_folder'] + 'Plex Media Server' + os.path.sep + 'Metadata' + os.path.sep + 'Movies' + os.path.sep + imageHash[0] + os.path.sep + imageHash[1:len(imageHash)] + '.bundle' + os.path.sep + 'Contents' + os.path.sep + '_stored' + os.path.sep + thumb elif (mediaType == 'show'): indexer = thumb[thumb.index('/') + 1:thumb.index('_')] imgLocation = config['plex_data_folder'] + 'Plex Media Server' + os.path.sep + 'Metadata' + os.path.sep + 'TV Shows' + os.path.sep + imageHash[0] + os.path.sep + imageHash[1:len(imageHash)] + '.bundle' + os.path.sep + 'Contents' + os.path.sep + '_stored' + os.path.sep + thumb elif (mediaType == 'season'): indexer = thumb[thumb.index('posters/') + 8:thumb.index('_')] imgLocation = config['plex_data_folder'] + 'Plex Media Server' + os.path.sep + 'Metadata' + os.path.sep + 'TV Shows' + os.path.sep + imageHash[0] + os.path.sep + imageHash[1:len(imageHash)] + '.bundle' + os.path.sep + 'Contents' + os.path.sep + '_stored' + os.path.sep + thumb elif (mediaType == 'episode'): indexer = thumb[thumb.index('thumbs/') + 7:thumb.index('_')] imgLocation = config['plex_data_folder'] + 'Plex Media Server' + os.path.sep + 'Metadata' + os.path.sep + 'TV Shows' + os.path.sep + imageHash[0] + os.path.sep + imageHash[1:len(imageHash)] + '.bundle' + os.path.sep + 'Contents' + os.path.sep + '_stored' + os.path.sep + thumb elif (mediaType == 'artist'): indexer = thumb[thumb.index('posters/') + 8:thumb.index('_')] imgLocation = config['plex_data_folder'] + 'Plex Media Server' + os.path.sep + 'Metadata' + os.path.sep + 'Artists' + os.path.sep + imageHash[0] + os.path.sep + imageHash[1:len(imageHash)] + '.bundle' + os.path.sep + 'Contents' + os.path.sep + '_stored' + os.path.sep + thumb elif (mediaType == 'album'): indexer = thumb[thumb.index('posters/') + 8:thumb.index('_')] imgLocation = config['plex_data_folder'] + 'Plex Media Server' + os.path.sep + 'Metadata' + os.path.sep + 'Albums' + os.path.sep + imageHash[0] + os.path.sep + imageHash[1:len(imageHash)] + '.bundle' + os.path.sep + 'Contents' + os.path.sep + '_stored' + os.path.sep + thumb 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') thumbObj['emailImgPath'] = webImgFullPath #imgurURL = uploadToImgur(imgLocation, imgName) cloudinaryURL = uploadToCloudinary(imgLocation) elif (config['web_enabled'] and config['email_use_web_images']): logging.info('processImage: Hosting image on local web server') thumbObj['emailImgPath'] = webImgFullPath elif (os.path.isfile(imgLocation)): logging.info('processImage: Attaching images to email') imgNames['Image_' + imgName] = imgLocation thumbObj['emailImgPath'] = 'cid:Image_' + imgName else: logging.info('processImage: No email image') thumbObj['emailImgPath'] = '' if (cloudinaryURL != ''): logging.info('processImage: Setting image paths to cloudinary') thumbObj['webImgPath'] = cloudinaryURL thumbObj['emailImgPath'] = cloudinaryURL elif (os.path.isfile(imgLocation) and config['web_enabled']): logging.info('processImage: Setting image paths to local and copying image to web folder') try: shutil.copy(imgLocation, img) except EnvironmentError, e: logging.warning('processImage: Failed to copy image - ' + repr(e)) thumbObj['emailImgPath'] = '' thumbObj['webImgPath'] = '' else: 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, headers = headers, data = { 'image': b64encode(open(imgToUpload, 'rb').read()), 'type': 'base64', 'name': nameOfUpload + '.jpg', 'title': nameOfUpload } ) data = json.loads(response.text)['data']; return data['link'] else: return '' def uploadToCloudinary(imgToUpload): logging.info('uploadToCloudinary: begin') if (os.path.isfile(imgToUpload)): if (os.path.islink(imgToUpload)): imgToUpload = os.path.realpath(imgToUpload) if (imghdr.what(imgToUpload)): logging.info('uploadToCloudinary: start upload to cloudinary') response = cloudinary.uploader.upload(imgToUpload) logging.info('uploadToCloudinary: response = ' + str(response)) url = response['secure_url'] if (config['upload_cloudinary_use_https']) else response['url'] logging.info('uploadToCloudinary: url = ' + url) return url else: logging.info('uploadToCloudinary: not an image') return '' else: logging.info('uploadToCloudinary: file not located') return '' def containsnonasciicharacters(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 return message def sendMail(email): #First check if email is in the unsubscribe list if email != '' and email[0].upper() in (name.upper() for name in config['email_unsubscribe']): print email[0] + ' is in the unsubscribe list. Do not send an email.' return 0; gmail_user = config['email_username'] gmail_pwd = config['email_password'] smtp_address = config['email_smtp_address'] smtp_port = config['email_smtp_port'] use_ssl = config['email_use_ssl'] FROM = formataddr((str(Header(config['email_from_name'])), config['email_from'])) if ('email_from_name' in config) else config['email_from'] TO = email if (email != '') else config['email_to'] SUBJECT = config['msg_email_subject'] # Create message container - the correct MIME type is multipart/alternative. msg = MIMEMultipart('alternative') msg = addheader(msg, 'Subject', SUBJECT) msg['From'] = FROM # 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') if(containsnonasciicharacters(text)): plaintext = MIMEText(text,'plain','utf-8') else: plaintext = MIMEText(text,'plain') #Create image headers for image in imgNames: fp = open(imgNames[image], 'rb') msgImage = MIMEImage(fp.read(), _subtype="jpg") fp.close() msgImage.add_header('Content-ID', '<' + image + '>') msg.attach(msgImage) # Attach parts into message container. # According to RFC 2046, the last part of a multipart message, in this case # the HTML message, is best and preferred. msg.attach(plaintext) msg.attach(htmltext) if (not config['email_use_bcc']): msg['To'] = ", ".join(TO) if (not use_ssl): server = smtplib.SMTP(smtp_address, smtp_port) server.ehlo() server.starttls() server.login(gmail_user, gmail_pwd) server.sendmail(FROM, TO, msg.as_string()) server.close() else: server = smtplib.SMTP_SSL(smtp_address, smtp_port) server.ehlo() server.login(gmail_user, gmail_pwd) server.sendmail(FROM, TO, msg.as_string()) server.close() return 1; def createEmailHTML(): emailText = """
""" + config['msg_header1'] + """ |
""" + config['msg_header2'] + """ |
""" + config['msg_header3'] + """ |
' 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]] != ''): displayText = str(movies[movie][section[0]]) if ('format' in sections[section[0]] and sections[section[0]]['format'] != ''): 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 += ' | ' + title.decode('utf-8') + '' htmlText += ''
htmlText += ''
htmlText += ' ' + title.decode('utf-8') + '' 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]] != ''): displayText = str(tvShows[show][section[0]]) if ('format' in sections[section[0]] and sections[section[0]]['format'] != ''): 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 += ' | ' + 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]] != ''): displayText = str(tvSeasons[season][section[0]]) if ('format' in sections[section[0]] and sections[section[0]]['format'] != ''): 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 += ' | ' + showTitle.decode('utf-8') + '' emailText += 'S' + str(tvEpisodes[episode]['season_index']) + ' E' + str(tvEpisodes[episode]['index']) + ': ' + title.decode('utf-8') + ' ' htmlText += ''
if (len(imageInfo) != 0):
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]] != ''): displayText = str(tvEpisodes[episode][section[0]]) if ('format' in sections[section[0]] and sections[section[0]]['format'] != ''): 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 += ' | ' + title.decode('utf-8') + '' htmlText += ''
htmlText += ''
htmlText += ' ' + title.decode('utf-8') + '' sections = config['filter_sections_Music'] for section in sorted(sections.iteritems(), key=lambda t: t[1]['order']): if (section[0] != 'track_list'): if (artists[artist][section[0]] in sections[section[0]]['exclude'] or len(set(artists[artist][section[0] + '_filter']).intersection(sections[section[0]]['exclude'])) > 0 or (sections[section[0]]['include'] and artists[artist][section[0]] not in sections[section[0]]['include'] and len(set(artists[artist][section[0] + '_filter']).intersection(sections[section[0]]['include'])) == 0)): skipItem = True if (sections[section[0]]['show'] and artists[artist][section[0]] and artists[artist][section[0]] != ''): displayText = str(artists[artist][section[0]]) if ('format' in sections[section[0]] and sections[section[0]]['format'] != ''): 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 += ' | ' + title.decode('utf-8') + '' htmlText += ''
htmlText += ''
htmlText += ' ' + title.decode('utf-8') + '' tracklistEmailText = '' tracklistHTMLText = '' sections = config['filter_sections_Music'] trackCount = 0 for section in sorted(sections.iteritems(), key=lambda t: t[1]['order']): if (section[0] == 'track_list'): if (sections[section[0]]['show']): tracklistEmailText += '
' + 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 += ' | ' + showTitle.decode('utf-8') + '' # emailText += 'S' + str(songs[song]['season_index']) + ' E' + str(songs[song]['index']) + ': ' + title.decode('utf-8') + ' ' # htmlText += ''
# 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)): # skipItem = True # if (sections[section[0]]['show'] and songs[song][section[0]] and songs[song][section[0]] != ''): # displayText = str(songs[song][section[0]]) # if ('format' in sections[section[0]] and sections[section[0]]['format'] != ''): # 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 += ' |