From 81655aec49ebe33fd33ffebfe26e018874a39a37 Mon Sep 17 00:00:00 2001 From: Ruud Date: Wed, 27 May 2015 14:06:27 +0200 Subject: [PATCH 1/5] Email not creating body message properly. fix #4996 --- couchpotato/core/notifications/email_.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/couchpotato/core/notifications/email_.py b/couchpotato/core/notifications/email_.py index d1e8fd5..442f439 100644 --- a/couchpotato/core/notifications/email_.py +++ b/couchpotato/core/notifications/email_.py @@ -31,12 +31,12 @@ class Email(Notification): starttls = self.conf('starttls') # Make the basic message - message['Subject'] = '%s: %s' % (self.default_title, toUnicode(message)) - message = MIMEText(toUnicode(message), _charset = Env.get('encoding')) - message['From'] = from_address - message['To'] = to_address - message['Date'] = formatdate(localtime = 1) - message['Message-ID'] = make_msgid() + email = MIMEText(toUnicode(message), _charset = Env.get('encoding')) + email['Subject'] = '%s: %s' % (self.default_title, toUnicode(message)) + email['From'] = from_address + email['To'] = to_address + email['Date'] = formatdate(localtime = 1) + email['Message-ID'] = make_msgid() try: # Open the SMTP connection, via SSL if requested @@ -58,7 +58,7 @@ class Email(Notification): # Send the e-mail log.debug("Sending the email") - mailserver.sendmail(from_address, splitString(to_address), message.as_string()) + mailserver.sendmail(from_address, splitString(to_address), email.as_string()) # Close the SMTP connection mailserver.quit() From fc72de5228206d7a22ca06c442831c09493915a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Jagie=C5=82=C5=82o?= Date: Sun, 14 Jun 2015 20:57:45 +0200 Subject: [PATCH 2/5] Notifications for Pushbullet channels Making it possible to send notifications not only to devices, but also to channels. It is very useful when one wants to send notifications to other pushbullet users, i.e. flatmates. A flatmate can then subscribe to your channel and receive notifications on his/her devices. For some reason, a POST request sending notification to a channel returns an empty dictionary, so no counting of successful requests is performed for those notifications. --- couchpotato/core/notifications/pushbullet.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/couchpotato/core/notifications/pushbullet.py b/couchpotato/core/notifications/pushbullet.py index e9d4605..fc08292 100644 --- a/couchpotato/core/notifications/pushbullet.py +++ b/couchpotato/core/notifications/pushbullet.py @@ -19,14 +19,8 @@ class Pushbullet(Notification): def notify(self, message = '', data = None, listener = None): if not data: data = {} - devices = self.getDevices() - if devices is None: - return False - # Get all the device IDs linked to this user - if not len(devices): - devices = [None] - + devices = self.getDevices() or [None] successful = 0 for device in devices: response = self.request( @@ -43,11 +37,24 @@ class Pushbullet(Notification): else: log.error('Unable to push notification to Pushbullet device with ID %s' % device) + for channel in self.getChannels(): + response = self.request( + 'pushes', + cache = False, + channel_tag = channel, + type = 'note', + title = self.default_title, + body = toUnicode(message) + ) + return successful == len(devices) def getDevices(self): return splitString(self.conf('devices')) + def getChannels(self): + return splitString(self.conf('channels')) + def request(self, method, cache = True, **kwargs): try: base64string = base64.encodestring('%s:' % self.conf('api_key'))[:-1] @@ -94,6 +101,12 @@ config = [{ 'description': 'IDs of devices to send notifications to, empty = all devices' }, { + 'name': 'channels', + 'default': '', + 'advanced': True, + 'description': 'IDs of channels to send notifications to, empty = no channels' + }, + { 'name': 'on_snatch', 'default': 0, 'type': 'bool', From b5a878e6dc12bd1213459197d902c40b7e6ee27c Mon Sep 17 00:00:00 2001 From: keep3r Date: Tue, 16 Jun 2015 09:22:08 +0200 Subject: [PATCH 3/5] WDTV Live Metadata Support for WDTV Live metadata. Adds thumbnail and metadata in XML. --- .../core/media/movie/providers/metadata/wdtv.py | 221 +++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 couchpotato/core/media/movie/providers/metadata/wdtv.py diff --git a/couchpotato/core/media/movie/providers/metadata/wdtv.py b/couchpotato/core/media/movie/providers/metadata/wdtv.py new file mode 100644 index 0000000..6f577f4 --- /dev/null +++ b/couchpotato/core/media/movie/providers/metadata/wdtv.py @@ -0,0 +1,221 @@ +from xml.etree.ElementTree import Element, SubElement, tostring +import os +import re +import traceback +import xml.dom.minidom + +from couchpotato.core.media.movie.providers.metadata.base import MovieMetaData +from couchpotato.core.helpers.encoding import toUnicode +from couchpotato.core.helpers.variable import getTitle +from couchpotato.core.logger import CPLog + +autoload = 'WdtvLive' + +log = CPLog(__name__) + + +class WdtvLive(MovieMetaData): + + def getThumbnailName(self, name, root, i): + return self.createMetaName('%s.jpg', name, root) + + def createMetaName(self, basename, name, root): + return os.path.join(root, basename.replace('%s', name)) + + def getNfoName(self, name, root, i): + return self.createMetaName('%s.xml', name, root) + + def getNfo(self, movie_info=None, data=None, i=0): + if not data: data = {} + if not movie_info: movie_info = {} + + nfoxml = Element('details') + + # Title + try: + el = SubElement(nfoxml, 'title') + el.text = toUnicode(getTitle(data)) + except: + pass + + # IMDB id + try: + el = SubElement(nfoxml, 'id') + el.text = toUnicode(data['identifier']) + except: + pass + + # Runtime + try: + runtime = SubElement(nfoxml, 'runtime') + runtime.text = '%s min' % movie_info.get('runtime') + except: + pass + + # Other values + types = ['year', 'mpaa', 'originaltitle:original_title', 'outline', 'plot', 'tagline', 'premiered:released'] + for type in types: + + if ':' in type: + name, type = type.split(':') + else: + name = type + + try: + if movie_info.get(type): + el = SubElement(nfoxml, name) + el.text = toUnicode(movie_info.get(type, '')) + except: + pass + + # Rating + for rating_type in ['imdb', 'rotten', 'tmdb']: + try: + r, v = movie_info['rating'][rating_type] + rating = SubElement(nfoxml, 'rating') + rating.text = str(r) + votes = SubElement(nfoxml, 'votes') + votes.text = str(v) + break + except: + log.debug('Failed adding rating info from %s: %s', (rating_type, traceback.format_exc())) + + # Genre + for genre in movie_info.get('genres', []): + genres = SubElement(nfoxml, 'genre') + genres.text = toUnicode(genre) + + # Actors + for actor_name in movie_info.get('actor_roles', {}): + role_name = movie_info['actor_roles'][actor_name] + + actor = SubElement(nfoxml, 'actor') + name = SubElement(actor, 'name') + name.text = toUnicode(actor_name) + if role_name: + role = SubElement(actor, 'role') + role.text = toUnicode(role_name) + if movie_info['images']['actors'].get(actor_name): + thumb = SubElement(actor, 'thumb') + thumb.text = toUnicode(movie_info['images']['actors'].get(actor_name)) + + # Directors + for director_name in movie_info.get('directors', []): + director = SubElement(nfoxml, 'director') + director.text = toUnicode(director_name) + + # Writers + for writer in movie_info.get('writers', []): + writers = SubElement(nfoxml, 'credits') + writers.text = toUnicode(writer) + + # Sets or collections + collection_name = movie_info.get('collection') + if collection_name: + collection = SubElement(nfoxml, 'set') + collection.text = toUnicode(collection_name) + sorttitle = SubElement(nfoxml, 'sorttitle') + sorttitle.text = '%s %s' % (toUnicode(collection_name), movie_info.get('year')) + + # Images + for image_url in movie_info['images']['poster_original']: + image = SubElement(nfoxml, 'thumb') + image.text = toUnicode(image_url) + + image_types = [ + ('fanart', 'backdrop_original'), + ('banner', 'banner'), + ('discart', 'disc_art'), + ('logo', 'logo'), + ('clearart', 'clear_art'), + ('landscape', 'landscape'), + ('extrathumb', 'extra_thumbs'), + ('extrafanart', 'extra_fanart'), + ] + + for image_type in image_types: + sub, type = image_type + + sub_element = SubElement(nfoxml, sub) + for image_url in movie_info['images'][type]: + image = SubElement(sub_element, 'thumb') + image.text = toUnicode(image_url) + + # Add trailer if found + trailer_found = False + if data.get('renamed_files'): + for filename in data.get('renamed_files'): + if 'trailer' in filename: + trailer = SubElement(nfoxml, 'trailer') + trailer.text = toUnicode(filename) + trailer_found = True + if not trailer_found and data['files'].get('trailer'): + trailer = SubElement(nfoxml, 'trailer') + trailer.text = toUnicode(data['files']['trailer'][0]) + + # Add file metadata + fileinfo = SubElement(nfoxml, 'fileinfo') + streamdetails = SubElement(fileinfo, 'streamdetails') + + # Video data + if data['meta_data'].get('video'): + video = SubElement(streamdetails, 'video') + codec = SubElement(video, 'codec') + codec.text = toUnicode(data['meta_data']['video']) + aspect = SubElement(video, 'aspect') + aspect.text = str(data['meta_data']['aspect']) + width = SubElement(video, 'width') + width.text = str(data['meta_data']['resolution_width']) + height = SubElement(video, 'height') + height.text = str(data['meta_data']['resolution_height']) + + # Audio data + if data['meta_data'].get('audio'): + audio = SubElement(streamdetails, 'audio') + codec = SubElement(audio, 'codec') + codec.text = toUnicode(data['meta_data'].get('audio')) + channels = SubElement(audio, 'channels') + channels.text = toUnicode(data['meta_data'].get('audio_channels')) + + # Clean up the xml and return it + nfoxml = xml.dom.minidom.parseString(tostring(nfoxml)) + xml_string = nfoxml.toprettyxml(indent = ' ') + text_re = re.compile('>\n\s+([^<>\s].*?)\n\s+\g<1> Date: Tue, 16 Jun 2015 12:21:32 +0200 Subject: [PATCH 4/5] Cleanup wdtvlive PR indentation --- couchpotato/core/media/movie/providers/metadata/wdtv.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/couchpotato/core/media/movie/providers/metadata/wdtv.py b/couchpotato/core/media/movie/providers/metadata/wdtv.py index 6f577f4..a2062c7 100644 --- a/couchpotato/core/media/movie/providers/metadata/wdtv.py +++ b/couchpotato/core/media/movie/providers/metadata/wdtv.py @@ -18,13 +18,13 @@ class WdtvLive(MovieMetaData): def getThumbnailName(self, name, root, i): return self.createMetaName('%s.jpg', name, root) - + def createMetaName(self, basename, name, root): return os.path.join(root, basename.replace('%s', name)) - + def getNfoName(self, name, root, i): return self.createMetaName('%s.xml', name, root) - + def getNfo(self, movie_info=None, data=None, i=0): if not data: data = {} if not movie_info: movie_info = {} @@ -184,7 +184,7 @@ class WdtvLive(MovieMetaData): xml_string = text_re.sub('>\g<1> Date: Tue, 16 Jun 2015 12:43:36 +0200 Subject: [PATCH 5/5] Pushbullet, don't loop over "None" --- couchpotato/core/notifications/pushbullet.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/couchpotato/core/notifications/pushbullet.py b/couchpotato/core/notifications/pushbullet.py index fc08292..df37b7d 100644 --- a/couchpotato/core/notifications/pushbullet.py +++ b/couchpotato/core/notifications/pushbullet.py @@ -20,7 +20,7 @@ class Pushbullet(Notification): if not data: data = {} # Get all the device IDs linked to this user - devices = self.getDevices() or [None] + devices = self.getDevices() or [] successful = 0 for device in devices: response = self.request( @@ -60,7 +60,7 @@ class Pushbullet(Notification): base64string = base64.encodestring('%s:' % self.conf('api_key'))[:-1] headers = { - "Authorization": "Basic %s" % base64string + 'Authorization': 'Basic %s' % base64string } if cache: