mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-30 11:38:36 -07:00
Initial newsletter support
This commit is contained in:
parent
b73d2ff1f7
commit
0f39201774
15 changed files with 2454 additions and 123 deletions
|
@ -620,6 +620,14 @@ def dbcheck():
|
|||
'custom_conditions TEXT, custom_conditions_logic TEXT)'
|
||||
)
|
||||
|
||||
# newsletters table :: This table keeps record of the newsletter settings
|
||||
c_db.execute(
|
||||
'CREATE TABLE IF NOT EXISTS newsletters (id INTEGER PRIMARY KEY AUTOINCREMENT, '
|
||||
'agent_id INTEGER, agent_name TEXT, agent_label TEXT, '
|
||||
'friendly_name TEXT, newsletter_config TEXT, '
|
||||
'cron TEXT NOT NULL DEFAULT "0 0 * * 0", active INTEGER DEFAULT 0)'
|
||||
)
|
||||
|
||||
# poster_urls table :: This table keeps record of the notification poster urls
|
||||
c_db.execute(
|
||||
'CREATE TABLE IF NOT EXISTS poster_urls (id INTEGER PRIMARY KEY AUTOINCREMENT, '
|
||||
|
|
|
@ -965,4 +965,12 @@ def get_plexpy_url(hostname=None):
|
|||
else:
|
||||
root = ''
|
||||
|
||||
return scheme + '://' + hostname + port + root
|
||||
return scheme + '://' + hostname + port + root
|
||||
|
||||
def momentjs_to_arrow(format, duration=False):
|
||||
invalid_formats = ['Mo', 'DDDo', 'do']
|
||||
if duration:
|
||||
invalid_formats += ['A', 'a']
|
||||
for f in invalid_formats:
|
||||
format = format.replace(f, '')
|
||||
return format
|
||||
|
|
537
plexpy/newsletters.py
Normal file
537
plexpy/newsletters.py
Normal file
|
@ -0,0 +1,537 @@
|
|||
# This file is part of Tautulli.
|
||||
#
|
||||
# Tautulli is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Tautulli is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import arrow
|
||||
import json
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
import email.utils
|
||||
from itertools import groupby
|
||||
from mako.lookup import TemplateLookup
|
||||
from mako import exceptions
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
|
||||
import plexpy
|
||||
import database
|
||||
import helpers
|
||||
import logger
|
||||
import notification_handler
|
||||
import pmsconnect
|
||||
import request
|
||||
|
||||
|
||||
AGENT_IDS = {
|
||||
'recently_added': 0
|
||||
}
|
||||
|
||||
|
||||
def available_newsletter_agents():
|
||||
agents = [
|
||||
{
|
||||
'label': 'Recently Added',
|
||||
'name': 'recently_added',
|
||||
'id': AGENT_IDS['recently_added']
|
||||
}
|
||||
]
|
||||
|
||||
return agents
|
||||
|
||||
|
||||
def get_agent_class(agent_id=None, config=None):
|
||||
if str(agent_id).isdigit():
|
||||
agent_id = int(agent_id)
|
||||
|
||||
if agent_id == 0:
|
||||
return RecentlyAdded(config=config)
|
||||
else:
|
||||
return Newsletter(config=config)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def get_newsletter_agents():
|
||||
return tuple(a['name'] for a in sorted(available_newsletter_agents(), key=lambda k: k['label']))
|
||||
|
||||
|
||||
def get_newsletters(newsletter_id=None):
|
||||
where = where_id = ''
|
||||
args = []
|
||||
|
||||
if newsletter_id:
|
||||
where = 'WHERE '
|
||||
if newsletter_id:
|
||||
where_id += 'id = ?'
|
||||
args.append(newsletter_id)
|
||||
where += ' AND '.join([w for w in [where_id] if w])
|
||||
|
||||
db = database.MonitorDatabase()
|
||||
result = db.select('SELECT id, agent_id, agent_name, agent_label, '
|
||||
'friendly_name, active FROM newsletters %s' % where, args=args)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def delete_newsletter(newsletter_id=None):
|
||||
db = database.MonitorDatabase()
|
||||
|
||||
if str(newsletter_id).isdigit():
|
||||
logger.debug(u"Tautulli Newsletters :: Deleting newsletter_id %s from the database."
|
||||
% newsletter_id)
|
||||
result = db.action('DELETE FROM newsletters WHERE id = ?', args=[newsletter_id])
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def get_newsletter_config(newsletter_id=None):
|
||||
if str(newsletter_id).isdigit():
|
||||
newsletter_id = int(newsletter_id)
|
||||
else:
|
||||
logger.error(u"Tautulli Newsletters :: Unable to retrieve newsletter config: invalid newsletter_id %s."
|
||||
% newsletter_id)
|
||||
return None
|
||||
|
||||
db = database.MonitorDatabase()
|
||||
result = db.select_single('SELECT * FROM newsletters WHERE id = ?', args=[newsletter_id])
|
||||
|
||||
if not result:
|
||||
return None
|
||||
|
||||
try:
|
||||
config = json.loads(result.pop('newsletter_config') or '{}')
|
||||
newsletter_agent = get_agent_class(agent_id=result['agent_id'], config=config)
|
||||
newsletter_config = newsletter_agent.return_config_options()
|
||||
except Exception as e:
|
||||
logger.error(u"Tautulli Newsletters :: Failed to get newsletter config options: %s." % e)
|
||||
return
|
||||
|
||||
result['config'] = config
|
||||
result['config_options'] = newsletter_config
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def add_newsletter_config(agent_id=None, **kwargs):
|
||||
if str(agent_id).isdigit():
|
||||
agent_id = int(agent_id)
|
||||
else:
|
||||
logger.error(u"Tautulli Newsletters :: Unable to add new newsletter: invalid agent_id %s."
|
||||
% agent_id)
|
||||
return False
|
||||
|
||||
agent = next((a for a in available_newsletter_agents() if a['id'] == agent_id), None)
|
||||
|
||||
if not agent:
|
||||
logger.error(u"Tautulli Newsletters :: Unable to retrieve new newsletter agent: invalid agent_id %s."
|
||||
% agent_id)
|
||||
return False
|
||||
|
||||
keys = {'id': None}
|
||||
values = {'agent_id': agent['id'],
|
||||
'agent_name': agent['name'],
|
||||
'agent_label': agent['label'],
|
||||
'friendly_name': '',
|
||||
'newsletter_config': json.dumps(get_agent_class(agent_id=agent['id']).config)
|
||||
}
|
||||
|
||||
db = database.MonitorDatabase()
|
||||
try:
|
||||
db.upsert(table_name='newsletters', key_dict=keys, value_dict=values)
|
||||
newsletter_id = db.last_insert_id()
|
||||
logger.info(u"Tautulli Newsletters :: Added new newsletter agent: %s (newsletter_id %s)."
|
||||
% (agent['label'], newsletter_id))
|
||||
return newsletter_id
|
||||
except Exception as e:
|
||||
logger.warn(u"Tautulli Newsletters :: Unable to add newsletter agent: %s." % e)
|
||||
return False
|
||||
|
||||
|
||||
def set_newsletter_config(newsletter_id=None, agent_id=None, **kwargs):
|
||||
if str(agent_id).isdigit():
|
||||
agent_id = int(agent_id)
|
||||
else:
|
||||
logger.error(u"Tautulli Newsletters :: Unable to set exisiting newsletter: invalid agent_id %s."
|
||||
% agent_id)
|
||||
return False
|
||||
|
||||
agent = next((a for a in available_newsletter_agents() if a['id'] == agent_id), None)
|
||||
|
||||
if not agent:
|
||||
logger.error(u"Tautulli Newsletters :: Unable to retrieve existing newsletter agent: invalid agent_id %s."
|
||||
% agent_id)
|
||||
return False
|
||||
|
||||
config_prefix = agent['name'] + '_'
|
||||
|
||||
newsletter_config = {k[len(config_prefix):]: kwargs.pop(k)
|
||||
for k in kwargs.keys() if k.startswith(config_prefix)}
|
||||
newsletter_config = get_agent_class(agent['id']).set_config(config=newsletter_config)
|
||||
|
||||
keys = {'id': newsletter_id}
|
||||
values = {'agent_id': agent['id'],
|
||||
'agent_name': agent['name'],
|
||||
'agent_label': agent['label'],
|
||||
'friendly_name': kwargs.get('friendly_name', ''),
|
||||
'newsletter_config': json.dumps(newsletter_config),
|
||||
'cron': kwargs.get('cron'),
|
||||
'active': kwargs.get('active')
|
||||
}
|
||||
|
||||
db = database.MonitorDatabase()
|
||||
try:
|
||||
db.upsert(table_name='newsletters', key_dict=keys, value_dict=values)
|
||||
logger.info(u"Tautulli Newsletters :: Updated newsletter agent: %s (newsletter_id %s)."
|
||||
% (agent['label'], newsletter_id))
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.warn(u"Tautulli Newsletters :: Unable to update newsletter agent: %s." % e)
|
||||
return False
|
||||
|
||||
|
||||
def send_newsletter(newsletter_id=None, newsletter_log_id=None, **kwargs):
|
||||
newsletter_config = get_newsletter_config(newsletter_id=newsletter_id)
|
||||
if newsletter_config:
|
||||
agent = get_agent_class(agent_id=newsletter_config['agent_id'],
|
||||
config=newsletter_config['config'])
|
||||
return agent.send(newsletter_log_id=newsletter_log_id, **kwargs)
|
||||
else:
|
||||
logger.debug(u"Tautulli Newsletters :: Notification requested but no newsletter_id received.")
|
||||
|
||||
|
||||
def serve_template(templatename, **kwargs):
|
||||
interface_dir = os.path.join(str(plexpy.PROG_DIR), 'data/interfaces/')
|
||||
template_dir = os.path.join(str(interface_dir), 'newsletters')
|
||||
|
||||
_hplookup = TemplateLookup(directories=[template_dir], default_filters=['unicode', 'h'])
|
||||
|
||||
try:
|
||||
template = _hplookup.get_template(templatename)
|
||||
return template.render(**kwargs)
|
||||
except:
|
||||
return exceptions.html_error_template().render()
|
||||
|
||||
|
||||
class Newsletter(object):
|
||||
NAME = ''
|
||||
_DEFAULT_CONFIG = {}
|
||||
|
||||
def __init__(self, config=None):
|
||||
self.config = {}
|
||||
self.set_config(config)
|
||||
|
||||
def set_config(self, config=None):
|
||||
self.config = self._validate_config(config)
|
||||
return self.config
|
||||
|
||||
def _validate_config(self, config=None):
|
||||
if config is None:
|
||||
return self._DEFAULT_CONFIG
|
||||
|
||||
new_config = {}
|
||||
for k, v in self._DEFAULT_CONFIG.iteritems():
|
||||
if isinstance(v, int):
|
||||
new_config[k] = helpers.cast_to_int(config.get(k, v))
|
||||
else:
|
||||
new_config[k] = config.get(k, v)
|
||||
|
||||
return new_config
|
||||
|
||||
def preview(self, **kwargs):
|
||||
pass
|
||||
|
||||
def send(self, **kwargs):
|
||||
pass
|
||||
|
||||
def make_request(self, url, method='POST', **kwargs):
|
||||
response, err_msg, req_msg = request.request_response2(url, method, **kwargs)
|
||||
|
||||
if response and not err_msg:
|
||||
logger.info(u"Tautulli Newsletters :: {name} notification sent.".format(name=self.NAME))
|
||||
return True
|
||||
|
||||
else:
|
||||
verify_msg = ""
|
||||
if response is not None and response.status_code >= 400 and response.status_code < 500:
|
||||
verify_msg = " Verify you notification newsletter agent settings are correct."
|
||||
|
||||
logger.error(u"Tautulli Newsletters :: {name} notification failed.{}".format(verify_msg, name=self.NAME))
|
||||
|
||||
if err_msg:
|
||||
logger.error(u"Tautulli Newsletters :: {}".format(err_msg))
|
||||
|
||||
if req_msg:
|
||||
logger.debug(u"Tautulli Newsletters :: Request response: {}".format(req_msg))
|
||||
|
||||
return False
|
||||
|
||||
def return_config_options(self):
|
||||
config_options = []
|
||||
return config_options
|
||||
|
||||
|
||||
class RecentlyAdded(Newsletter):
|
||||
"""
|
||||
Recently Added Newsletter
|
||||
"""
|
||||
NAME = 'Recently Added'
|
||||
_DEFAULT_CONFIG = {'last_days': 7,
|
||||
'incl_movies': 1,
|
||||
'incl_shows': 1,
|
||||
'incl_artists': 1
|
||||
}
|
||||
_TEMPLATE = 'recently_added.html'
|
||||
|
||||
def __init__(self, config=None):
|
||||
super(RecentlyAdded, self).__init__(config)
|
||||
|
||||
date_format = helpers.momentjs_to_arrow(plexpy.CONFIG.DATE_FORMAT)
|
||||
|
||||
self.end_time = int(time.time())
|
||||
self.start_time = self.end_time - self.config['last_days']*24*60*60
|
||||
self.end_date = arrow.get(self.end_time).format(date_format)
|
||||
self.start_date = arrow.get(self.start_time).format(date_format)
|
||||
|
||||
self.plexpy_config = {
|
||||
'pms_identifier': plexpy.CONFIG.PMS_IDENTIFIER,
|
||||
'pms_web_url': plexpy.CONFIG.PMS_WEB_URL
|
||||
}
|
||||
|
||||
self.recently_added = {}
|
||||
|
||||
def _get_recently_added(self, media_type=None):
|
||||
pms_connect = pmsconnect.PmsConnect()
|
||||
|
||||
recently_added = []
|
||||
done = False
|
||||
start = 0
|
||||
|
||||
while not done:
|
||||
recent_items = pms_connect.get_recently_added_details(start=str(start), count='10', type=media_type)
|
||||
filtered_items = [i for i in recent_items['recently_added']
|
||||
if helpers.cast_to_int(i['added_at']) > self.start_time]
|
||||
if len(filtered_items) < 10:
|
||||
done = True
|
||||
else:
|
||||
start += 10
|
||||
|
||||
recently_added.extend(filtered_items)
|
||||
|
||||
if media_type == 'show':
|
||||
shows_list = []
|
||||
show_rating_keys = []
|
||||
for item in recently_added:
|
||||
if item['media_type'] == 'show':
|
||||
show_rating_key = item['rating_key']
|
||||
elif item['media_type'] == 'season':
|
||||
show_rating_key = item['parent_rating_key']
|
||||
elif item['media_type'] == 'episode':
|
||||
show_rating_key = item['grandparent_rating_key']
|
||||
|
||||
if show_rating_key in show_rating_keys:
|
||||
continue
|
||||
|
||||
show_metadata = pms_connect.get_metadata_details(show_rating_key, media_info=False)
|
||||
children = pms_connect.get_item_children(show_rating_key, get_grandchildren=True)
|
||||
filtered_children = [i for i in children['children_list']
|
||||
if helpers.cast_to_int(i['added_at']) > self.start_time]
|
||||
filtered_children.sort(key=lambda x: x['parent_media_index'])
|
||||
|
||||
seasons = []
|
||||
for k, v in groupby(filtered_children, key=lambda x: x['parent_media_index']):
|
||||
episodes = list(v)
|
||||
num, num00 = notification_handler.format_group_index(
|
||||
[helpers.cast_to_int(d['media_index']) for d in episodes])
|
||||
|
||||
seasons.append({'media_index': k,
|
||||
'episode_range': num00,
|
||||
'episode_count': len(episodes),
|
||||
'episode': episodes})
|
||||
|
||||
num, num00 = notification_handler.format_group_index(
|
||||
[helpers.cast_to_int(d['media_index']) for d in seasons])
|
||||
|
||||
show_metadata['season_range'] = num00
|
||||
show_metadata['season_count'] = len(seasons)
|
||||
show_metadata['season'] = seasons
|
||||
|
||||
shows_list.append(show_metadata)
|
||||
show_rating_keys.append(show_rating_key)
|
||||
|
||||
recently_added = shows_list
|
||||
|
||||
if media_type == 'artist':
|
||||
artists_list = []
|
||||
artist_rating_keys = []
|
||||
for item in recently_added:
|
||||
if item['media_type'] == 'artist':
|
||||
artist_rating_key = item['rating_key']
|
||||
elif item['media_type'] == 'album':
|
||||
artist_rating_key = item['parent_rating_key']
|
||||
elif item['media_type'] == 'track':
|
||||
artist_rating_key = item['grandparent_rating_key']
|
||||
|
||||
if artist_rating_key in artist_rating_keys:
|
||||
continue
|
||||
|
||||
artist_metadata = pms_connect.get_metadata_details(artist_rating_key, media_info=False)
|
||||
children = pms_connect.get_item_children(artist_rating_key)
|
||||
filtered_children = [i for i in children['children_list']
|
||||
if helpers.cast_to_int(i['added_at']) > self.start_time]
|
||||
filtered_children.sort(key=lambda x: x['added_at'])
|
||||
|
||||
albums = []
|
||||
for a in filtered_children:
|
||||
album_metadata = pms_connect.get_metadata_details(a['rating_key'], media_info=False)
|
||||
album_metadata['track_count'] = helpers.cast_to_int(album_metadata['children_count'])
|
||||
albums.append(album_metadata)
|
||||
|
||||
artist_metadata['album_count'] = len(albums)
|
||||
artist_metadata['album'] = albums
|
||||
|
||||
artists_list.append(artist_metadata)
|
||||
artist_rating_keys.append(artist_rating_key)
|
||||
|
||||
recently_added = artists_list
|
||||
|
||||
return recently_added
|
||||
|
||||
def get_recently_added(self):
|
||||
if self.config['incl_movies']:
|
||||
self.recently_added['movie'] = self._get_recently_added('movie')
|
||||
if self.config['incl_shows']:
|
||||
self.recently_added['show'] = self._get_recently_added('show')
|
||||
if self.config['incl_artists']:
|
||||
self.recently_added['artist'] = self._get_recently_added('artist')
|
||||
|
||||
return self.recently_added
|
||||
|
||||
def preview(self, **kwargs):
|
||||
self.get_recently_added()
|
||||
|
||||
return serve_template(
|
||||
templatename=self._TEMPLATE,
|
||||
title=self.NAME,
|
||||
recently_added=self.recently_added,
|
||||
start_date=self.start_date,
|
||||
end_date=self.end_date,
|
||||
plexpy_config=self.plexpy_config
|
||||
)
|
||||
|
||||
def send(self, **kwargs):
|
||||
if not subject or not body:
|
||||
return
|
||||
|
||||
if self.config['incl_subject']:
|
||||
text = subject.encode('utf-8') + '\r\n' + body.encode("utf-8")
|
||||
else:
|
||||
text = body.encode("utf-8")
|
||||
|
||||
data = {'content': text}
|
||||
if self.config['username']:
|
||||
data['username'] = self.config['username']
|
||||
if self.config['avatar_url']:
|
||||
data['avatar_url'] = self.config['avatar_url']
|
||||
if self.config['tts']:
|
||||
data['tts'] = True
|
||||
|
||||
if self.config['incl_card'] and kwargs.get('parameters', {}).get('media_type'):
|
||||
# Grab formatted metadata
|
||||
pretty_metadata = PrettyMetadata(kwargs['parameters'])
|
||||
|
||||
if pretty_metadata.media_type == 'movie':
|
||||
provider = self.config['movie_provider']
|
||||
elif pretty_metadata.media_type in ('show', 'season', 'episode'):
|
||||
provider = self.config['tv_provider']
|
||||
elif pretty_metadata.media_type in ('artist', 'album', 'track'):
|
||||
provider = self.config['music_provider']
|
||||
else:
|
||||
provider = None
|
||||
|
||||
poster_url = pretty_metadata.get_poster_url()
|
||||
provider_name = pretty_metadata.get_provider_name(provider)
|
||||
provider_link = pretty_metadata.get_provider_link(provider)
|
||||
title = pretty_metadata.get_title('\xc2\xb7'.decode('utf8'))
|
||||
description = pretty_metadata.get_description()
|
||||
plex_url = pretty_metadata.get_plex_url()
|
||||
|
||||
# Build Discord post attachment
|
||||
attachment = {'title': title
|
||||
}
|
||||
|
||||
if self.config['color']:
|
||||
hex_match = re.match(r'^#([0-9a-fA-F]{3}){1,2}$', self.config['color'])
|
||||
if hex_match:
|
||||
hex = hex_match.group(0).lstrip('#')
|
||||
hex = ''.join(h * 2 for h in hex) if len(hex) == 3 else hex
|
||||
attachment['color'] = helpers.hex_to_int(hex)
|
||||
|
||||
if self.config['incl_thumbnail']:
|
||||
attachment['thumbnail'] = {'url': poster_url}
|
||||
else:
|
||||
attachment['image'] = {'url': poster_url}
|
||||
|
||||
if self.config['incl_description'] or pretty_metadata.media_type in ('artist', 'album', 'track'):
|
||||
attachment['description'] = description
|
||||
|
||||
fields = []
|
||||
if provider_link:
|
||||
attachment['url'] = provider_link
|
||||
fields.append({'name': 'View Details',
|
||||
'value': '[%s](%s)' % (provider_name, provider_link.encode('utf-8')),
|
||||
'inline': True})
|
||||
if self.config['incl_pmslink']:
|
||||
fields.append({'name': 'View Details',
|
||||
'value': '[Plex Web](%s)' % plex_url.encode('utf-8'),
|
||||
'inline': True})
|
||||
if fields:
|
||||
attachment['fields'] = fields
|
||||
|
||||
data['embeds'] = [attachment]
|
||||
|
||||
headers = {'Content-type': 'application/json'}
|
||||
params = {'wait': True}
|
||||
|
||||
return self.make_request(self.config['hook'], params=params, headers=headers, json=data)
|
||||
|
||||
def return_config_options(self):
|
||||
config_option = [{'label': 'Number of Days',
|
||||
'value': self.config['last_days'],
|
||||
'name': 'recently_added_last_days',
|
||||
'description': 'The past number of days to include in the newsletter.',
|
||||
'input_type': 'number'
|
||||
},
|
||||
{'label': 'Include Movies',
|
||||
'value': self.config['incl_movies'],
|
||||
'description': 'Include recently added movies in the newsletter.',
|
||||
'name': 'recently_added_incl_movies',
|
||||
'input_type': 'checkbox'
|
||||
},
|
||||
{'label': 'Include TV Shows',
|
||||
'value': self.config['incl_shows'],
|
||||
'description': 'Include recently added TV shows in the newsletter.',
|
||||
'name': 'recently_added_incl_shows',
|
||||
'input_type': 'checkbox'
|
||||
},
|
||||
{'label': 'Include Music',
|
||||
'value': self.config['incl_artists'],
|
||||
'description': 'Include recently added music in the newsletter.',
|
||||
'name': 'recently_added_incl_artists',
|
||||
'input_type': 'checkbox'
|
||||
}
|
||||
]
|
||||
|
||||
return config_option
|
|
@ -448,9 +448,9 @@ def set_notify_success(notification_id):
|
|||
|
||||
def build_media_notify_params(notify_action=None, session=None, timeline=None, manual_trigger=False, **kwargs):
|
||||
# Get time formats
|
||||
date_format = plexpy.CONFIG.DATE_FORMAT.replace('Do','')
|
||||
time_format = plexpy.CONFIG.TIME_FORMAT.replace('Do','')
|
||||
duration_format = plexpy.CONFIG.TIME_FORMAT.replace('Do','').replace('a','').replace('A','')
|
||||
date_format = helpers.momentjs_to_arrow(plexpy.CONFIG.DATE_FORMAT)
|
||||
time_format = helpers.momentjs_to_arrow(plexpy.CONFIG.TIME_FORMAT)
|
||||
duration_format = helpers.momentjs_to_arrow(plexpy.CONFIG.TIME_FORMAT, duration=True)
|
||||
|
||||
# Get metadata for the item
|
||||
if session:
|
||||
|
|
|
@ -338,7 +338,7 @@ def get_agent_class(agent_id=None, config=None):
|
|||
agent_id = int(agent_id)
|
||||
|
||||
if agent_id == 0:
|
||||
return GROWL(config=config,)
|
||||
return GROWL(config=config)
|
||||
elif agent_id == 1:
|
||||
return PROWL(config=config)
|
||||
elif agent_id == 2:
|
||||
|
@ -419,8 +419,8 @@ def get_notifiers(notifier_id=None, notify_action=None):
|
|||
|
||||
db = database.MonitorDatabase()
|
||||
result = db.select('SELECT id, agent_id, agent_name, agent_label, friendly_name, %s FROM notifiers %s'
|
||||
% (', '.join(notify_actions), where), args=args)
|
||||
|
||||
% (', '.join(notify_actions), where), args=args)
|
||||
|
||||
for item in result:
|
||||
item['active'] = int(any([item.pop(k) for k in item.keys() if k in notify_actions]))
|
||||
|
||||
|
@ -431,9 +431,9 @@ def delete_notifier(notifier_id=None):
|
|||
db = database.MonitorDatabase()
|
||||
|
||||
if str(notifier_id).isdigit():
|
||||
logger.debug(u"Tautulli Notifiers :: Deleting notifier_id %s from the database." % notifier_id)
|
||||
result = db.action('DELETE FROM notifiers WHERE id = ?',
|
||||
args=[notifier_id])
|
||||
logger.debug(u"Tautulli Notifiers :: Deleting notifier_id %s from the database."
|
||||
% notifier_id)
|
||||
result = db.action('DELETE FROM notifiers WHERE id = ?', args=[notifier_id])
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
@ -443,12 +443,13 @@ def get_notifier_config(notifier_id=None):
|
|||
if str(notifier_id).isdigit():
|
||||
notifier_id = int(notifier_id)
|
||||
else:
|
||||
logger.error(u"Tautulli Notifiers :: Unable to retrieve notifier config: invalid notifier_id %s." % notifier_id)
|
||||
logger.error(u"Tautulli Notifiers :: Unable to retrieve notifier config: invalid notifier_id %s."
|
||||
% notifier_id)
|
||||
return None
|
||||
|
||||
db = database.MonitorDatabase()
|
||||
result = db.select_single('SELECT * FROM notifiers WHERE id = ?',
|
||||
args=[notifier_id])
|
||||
result = db.select_single('SELECT * FROM notifiers WHERE id = ?', args=[notifier_id])
|
||||
|
||||
if not result:
|
||||
return None
|
||||
|
||||
|
@ -490,13 +491,15 @@ def add_notifier_config(agent_id=None, **kwargs):
|
|||
if str(agent_id).isdigit():
|
||||
agent_id = int(agent_id)
|
||||
else:
|
||||
logger.error(u"Tautulli Notifiers :: Unable to add new notifier: invalid agent_id %s." % agent_id)
|
||||
logger.error(u"Tautulli Notifiers :: Unable to add new notifier: invalid agent_id %s."
|
||||
% agent_id)
|
||||
return False
|
||||
|
||||
agent = next((a for a in available_notification_agents() if a['id'] == agent_id), None)
|
||||
|
||||
if not agent:
|
||||
logger.error(u"Tautulli Notifiers :: Unable to retrieve new notification agent: invalid agent_id %s." % agent_id)
|
||||
logger.error(u"Tautulli Notifiers :: Unable to retrieve new notification agent: invalid agent_id %s."
|
||||
% agent_id)
|
||||
return False
|
||||
|
||||
keys = {'id': None}
|
||||
|
@ -521,7 +524,8 @@ def add_notifier_config(agent_id=None, **kwargs):
|
|||
try:
|
||||
db.upsert(table_name='notifiers', key_dict=keys, value_dict=values)
|
||||
notifier_id = db.last_insert_id()
|
||||
logger.info(u"Tautulli Notifiers :: Added new notification agent: %s (notifier_id %s)." % (agent['label'], notifier_id))
|
||||
logger.info(u"Tautulli Notifiers :: Added new notification agent: %s (notifier_id %s)."
|
||||
% (agent['label'], notifier_id))
|
||||
blacklist_logger()
|
||||
return notifier_id
|
||||
except Exception as e:
|
||||
|
@ -533,13 +537,15 @@ def set_notifier_config(notifier_id=None, agent_id=None, **kwargs):
|
|||
if str(agent_id).isdigit():
|
||||
agent_id = int(agent_id)
|
||||
else:
|
||||
logger.error(u"Tautulli Notifiers :: Unable to set exisiting notifier: invalid agent_id %s." % agent_id)
|
||||
logger.error(u"Tautulli Notifiers :: Unable to set exisiting notifier: invalid agent_id %s."
|
||||
% agent_id)
|
||||
return False
|
||||
|
||||
agent = next((a for a in available_notification_agents() if a['id'] == agent_id), None)
|
||||
|
||||
if not agent:
|
||||
logger.error(u"Tautulli Notifiers :: Unable to retrieve existing notification agent: invalid agent_id %s." % agent_id)
|
||||
logger.error(u"Tautulli Notifiers :: Unable to retrieve existing notification agent: invalid agent_id %s."
|
||||
% agent_id)
|
||||
return False
|
||||
|
||||
notify_actions = get_notify_actions()
|
||||
|
@ -571,7 +577,8 @@ def set_notifier_config(notifier_id=None, agent_id=None, **kwargs):
|
|||
db = database.MonitorDatabase()
|
||||
try:
|
||||
db.upsert(table_name='notifiers', key_dict=keys, value_dict=values)
|
||||
logger.info(u"Tautulli Notifiers :: Updated notification agent: %s (notifier_id %s)." % (agent['label'], notifier_id))
|
||||
logger.info(u"Tautulli Notifiers :: Updated notification agent: %s (notifier_id %s)."
|
||||
% (agent['label'], notifier_id))
|
||||
blacklist_logger()
|
||||
|
||||
if agent['name'] == 'browser':
|
||||
|
@ -743,6 +750,7 @@ class Notifier(object):
|
|||
_DEFAULT_CONFIG = {}
|
||||
|
||||
def __init__(self, config=None):
|
||||
self.config = {}
|
||||
self.set_config(config)
|
||||
|
||||
def set_config(self, config=None):
|
||||
|
|
|
@ -139,6 +139,22 @@ class PmsConnect(object):
|
|||
|
||||
return request
|
||||
|
||||
def get_metadata_grandchildren(self, rating_key='', output_format=''):
|
||||
"""
|
||||
Return metadata for graandchildren of the request item.
|
||||
|
||||
Parameters required: rating_key { Plex ratingKey }
|
||||
Optional parameters: output_format { dict, json }
|
||||
|
||||
Output: array
|
||||
"""
|
||||
uri = '/library/metadata/' + rating_key + '/grandchildren'
|
||||
request = self.request_handler.make_request(uri=uri,
|
||||
request_type='GET',
|
||||
output_format=output_format)
|
||||
|
||||
return request
|
||||
|
||||
def get_recently_added(self, start='0', count='0', output_format=''):
|
||||
"""
|
||||
Return list of recently added items.
|
||||
|
@ -171,22 +187,6 @@ class PmsConnect(object):
|
|||
|
||||
return request
|
||||
|
||||
def get_children_list(self, rating_key='', output_format=''):
|
||||
"""
|
||||
Return list of children in requested library item.
|
||||
|
||||
Parameters required: rating_key { ratingKey of parent }
|
||||
Optional parameters: output_format { dict, json }
|
||||
|
||||
Output: array
|
||||
"""
|
||||
uri = '/library/metadata/' + rating_key + '/children'
|
||||
request = self.request_handler.make_request(uri=uri,
|
||||
request_type='GET',
|
||||
output_format=output_format)
|
||||
|
||||
return request
|
||||
|
||||
def get_children_list_related(self, rating_key='', output_format=''):
|
||||
"""
|
||||
Return list of related children in requested collection item.
|
||||
|
@ -470,59 +470,86 @@ class PmsConnect(object):
|
|||
output = {'recently_added': []}
|
||||
return output
|
||||
|
||||
recents_main = []
|
||||
if a.getElementsByTagName('Directory'):
|
||||
recents_main = a.getElementsByTagName('Directory')
|
||||
for item in recents_main:
|
||||
recent_items = {'media_type': helpers.get_xml_attr(item, 'type'),
|
||||
'rating_key': helpers.get_xml_attr(item, 'ratingKey'),
|
||||
'parent_rating_key': helpers.get_xml_attr(item, 'parentRatingKey'),
|
||||
'grandparent_rating_key': helpers.get_xml_attr(item, 'grandparentRatingKey'),
|
||||
'title': helpers.get_xml_attr(item, 'title'),
|
||||
'parent_title': helpers.get_xml_attr(item, 'parentTitle'),
|
||||
'grandparent_title': helpers.get_xml_attr(item, 'grandparentTitle'),
|
||||
'sort_title': helpers.get_xml_attr(item, 'titleSort'),
|
||||
'media_index': helpers.get_xml_attr(item, 'index'),
|
||||
'parent_media_index': helpers.get_xml_attr(item, 'parentIndex'),
|
||||
'section_id': section_id if section_id else helpers.get_xml_attr(item, 'librarySectionID'),
|
||||
'library_name': helpers.get_xml_attr(item, 'librarySectionTitle'),
|
||||
'year': helpers.get_xml_attr(item, 'year'),
|
||||
'thumb': helpers.get_xml_attr(item, 'thumb'),
|
||||
'parent_thumb': helpers.get_xml_attr(item, 'parentThumb'),
|
||||
'grandparent_thumb': helpers.get_xml_attr(item, 'grandparentThumb'),
|
||||
'added_at': helpers.get_xml_attr(item, 'addedAt'),
|
||||
'child_count': helpers.get_xml_attr(item, 'childCount')
|
||||
}
|
||||
recents_list.append(recent_items)
|
||||
|
||||
recents_main += a.getElementsByTagName('Directory')
|
||||
if a.getElementsByTagName('Video'):
|
||||
recents_main = a.getElementsByTagName('Video')
|
||||
for item in recents_main:
|
||||
recent_items = {'media_type': helpers.get_xml_attr(item, 'type'),
|
||||
'rating_key': helpers.get_xml_attr(item, 'ratingKey'),
|
||||
'parent_rating_key': helpers.get_xml_attr(item, 'parentRatingKey'),
|
||||
'grandparent_rating_key': helpers.get_xml_attr(item, 'grandparentRatingKey'),
|
||||
'title': helpers.get_xml_attr(item, 'title'),
|
||||
'parent_title': helpers.get_xml_attr(item, 'parentTitle'),
|
||||
'grandparent_title': helpers.get_xml_attr(item, 'grandparentTitle'),
|
||||
'sort_title': helpers.get_xml_attr(item, 'titleSort'),
|
||||
'media_index': helpers.get_xml_attr(item, 'index'),
|
||||
'parent_media_index': helpers.get_xml_attr(item, 'parentIndex'),
|
||||
'section_id': section_id if section_id else helpers.get_xml_attr(item, 'librarySectionID'),
|
||||
'library_name': helpers.get_xml_attr(item, 'librarySectionTitle'),
|
||||
'year': helpers.get_xml_attr(item, 'year'),
|
||||
'thumb': helpers.get_xml_attr(item, 'thumb'),
|
||||
'parent_thumb': helpers.get_xml_attr(item, 'parentThumb'),
|
||||
'grandparent_thumb': helpers.get_xml_attr(item, 'grandparentThumb'),
|
||||
'added_at': helpers.get_xml_attr(item, 'addedAt'),
|
||||
'child_count': helpers.get_xml_attr(item, 'childCount')
|
||||
}
|
||||
recents_list.append(recent_items)
|
||||
recents_main += a.getElementsByTagName('Video')
|
||||
|
||||
for m in recents_main:
|
||||
directors = []
|
||||
writers = []
|
||||
actors = []
|
||||
genres = []
|
||||
labels = []
|
||||
|
||||
if m.getElementsByTagName('Director'):
|
||||
for director in m.getElementsByTagName('Director'):
|
||||
directors.append(helpers.get_xml_attr(director, 'tag'))
|
||||
|
||||
if m.getElementsByTagName('Writer'):
|
||||
for writer in m.getElementsByTagName('Writer'):
|
||||
writers.append(helpers.get_xml_attr(writer, 'tag'))
|
||||
|
||||
if m.getElementsByTagName('Role'):
|
||||
for actor in m.getElementsByTagName('Role'):
|
||||
actors.append(helpers.get_xml_attr(actor, 'tag'))
|
||||
|
||||
if m.getElementsByTagName('Genre'):
|
||||
for genre in m.getElementsByTagName('Genre'):
|
||||
genres.append(helpers.get_xml_attr(genre, 'tag'))
|
||||
|
||||
if m.getElementsByTagName('Label'):
|
||||
for label in m.getElementsByTagName('Label'):
|
||||
labels.append(helpers.get_xml_attr(label, 'tag'))
|
||||
|
||||
recent_item = {'media_type': helpers.get_xml_attr(m, 'type'),
|
||||
'section_id': helpers.get_xml_attr(m, 'librarySectionID'),
|
||||
'library_name': helpers.get_xml_attr(m, 'librarySectionTitle'),
|
||||
'rating_key': helpers.get_xml_attr(m, 'ratingKey'),
|
||||
'parent_rating_key': helpers.get_xml_attr(m, 'parentRatingKey'),
|
||||
'grandparent_rating_key': helpers.get_xml_attr(m, 'grandparentRatingKey'),
|
||||
'title': helpers.get_xml_attr(m, 'title'),
|
||||
'parent_title': helpers.get_xml_attr(m, 'parentTitle'),
|
||||
'grandparent_title': helpers.get_xml_attr(m, 'grandparentTitle'),
|
||||
'sort_title': helpers.get_xml_attr(m, 'titleSort'),
|
||||
'media_index': helpers.get_xml_attr(m, 'index'),
|
||||
'parent_media_index': helpers.get_xml_attr(m, 'parentIndex'),
|
||||
'studio': helpers.get_xml_attr(m, 'studio'),
|
||||
'content_rating': helpers.get_xml_attr(m, 'contentRating'),
|
||||
'summary': helpers.get_xml_attr(m, 'summary'),
|
||||
'tagline': helpers.get_xml_attr(m, 'tagline'),
|
||||
'rating': helpers.get_xml_attr(m, 'rating'),
|
||||
'audience_rating': helpers.get_xml_attr(m, 'audienceRating'),
|
||||
'user_rating': helpers.get_xml_attr(m, 'userRating'),
|
||||
'duration': helpers.get_xml_attr(m, 'duration'),
|
||||
'year': helpers.get_xml_attr(m, 'year'),
|
||||
'thumb': helpers.get_xml_attr(m, 'thumb'),
|
||||
'parent_thumb': helpers.get_xml_attr(m, 'parentThumb'),
|
||||
'grandparent_thumb': helpers.get_xml_attr(m, 'grandparentThumb'),
|
||||
'art': helpers.get_xml_attr(m, 'art'),
|
||||
'banner': helpers.get_xml_attr(m, 'banner'),
|
||||
'originally_available_at': helpers.get_xml_attr(m, 'originallyAvailableAt'),
|
||||
'added_at': helpers.get_xml_attr(m, 'addedAt'),
|
||||
'updated_at': helpers.get_xml_attr(m, 'updatedAt'),
|
||||
'last_viewed_at': helpers.get_xml_attr(m, 'lastViewedAt'),
|
||||
'guid': helpers.get_xml_attr(m, 'guid'),
|
||||
'directors': directors,
|
||||
'writers': writers,
|
||||
'actors': actors,
|
||||
'genres': genres,
|
||||
'labels': labels,
|
||||
'full_title': helpers.get_xml_attr(m, 'title'),
|
||||
'child_count': helpers.get_xml_attr(m, 'childCount')
|
||||
}
|
||||
|
||||
recents_list.append(recent_item)
|
||||
|
||||
output = {'recently_added': sorted(recents_list, key=lambda k: k['added_at'], reverse=True)}
|
||||
|
||||
return output
|
||||
|
||||
def get_metadata_details(self, rating_key='', sync_id='', cache_key=None):
|
||||
def get_metadata_details(self, rating_key='', sync_id='', cache_key=None, media_info=True):
|
||||
"""
|
||||
Return processed and validated metadata list for requested item.
|
||||
|
||||
|
@ -662,7 +689,8 @@ class PmsConnect(object):
|
|||
'genres': genres,
|
||||
'labels': labels,
|
||||
'collections': collections,
|
||||
'full_title': helpers.get_xml_attr(metadata_main, 'title')
|
||||
'full_title': helpers.get_xml_attr(metadata_main, 'title'),
|
||||
'children_count': helpers.get_xml_attr(metadata_main, 'leafCount')
|
||||
}
|
||||
|
||||
elif metadata_type == 'show':
|
||||
|
@ -708,7 +736,8 @@ class PmsConnect(object):
|
|||
'genres': genres,
|
||||
'labels': labels,
|
||||
'collections': collections,
|
||||
'full_title': helpers.get_xml_attr(metadata_main, 'title')
|
||||
'full_title': helpers.get_xml_attr(metadata_main, 'title'),
|
||||
'children_count': helpers.get_xml_attr(metadata_main, 'leafCount')
|
||||
}
|
||||
|
||||
elif metadata_type == 'season':
|
||||
|
@ -752,7 +781,8 @@ class PmsConnect(object):
|
|||
'labels': show_details['labels'],
|
||||
'collections': show_details['collections'],
|
||||
'full_title': u'{} - {}'.format(helpers.get_xml_attr(metadata_main, 'parentTitle'),
|
||||
helpers.get_xml_attr(metadata_main, 'title'))
|
||||
helpers.get_xml_attr(metadata_main, 'title')),
|
||||
'children_count': helpers.get_xml_attr(metadata_main, 'leafCount')
|
||||
}
|
||||
|
||||
elif metadata_type == 'episode':
|
||||
|
@ -796,7 +826,8 @@ class PmsConnect(object):
|
|||
'labels': show_details['labels'],
|
||||
'collections': show_details['collections'],
|
||||
'full_title': u'{} - {}'.format(helpers.get_xml_attr(metadata_main, 'grandparentTitle'),
|
||||
helpers.get_xml_attr(metadata_main, 'title'))
|
||||
helpers.get_xml_attr(metadata_main, 'title')),
|
||||
'children_count': helpers.get_xml_attr(metadata_main, 'leafCount')
|
||||
}
|
||||
|
||||
elif metadata_type == 'artist':
|
||||
|
@ -837,7 +868,8 @@ class PmsConnect(object):
|
|||
'genres': genres,
|
||||
'labels': labels,
|
||||
'collections': collections,
|
||||
'full_title': helpers.get_xml_attr(metadata_main, 'title')
|
||||
'full_title': helpers.get_xml_attr(metadata_main, 'title'),
|
||||
'children_count': helpers.get_xml_attr(metadata_main, 'leafCount')
|
||||
}
|
||||
|
||||
elif metadata_type == 'album':
|
||||
|
@ -881,7 +913,8 @@ class PmsConnect(object):
|
|||
'labels': labels,
|
||||
'collections': collections,
|
||||
'full_title': u'{} - {}'.format(helpers.get_xml_attr(metadata_main, 'parentTitle'),
|
||||
helpers.get_xml_attr(metadata_main, 'title'))
|
||||
helpers.get_xml_attr(metadata_main, 'title')),
|
||||
'children_count': helpers.get_xml_attr(metadata_main, 'leafCount')
|
||||
}
|
||||
|
||||
elif metadata_type == 'track':
|
||||
|
@ -925,7 +958,8 @@ class PmsConnect(object):
|
|||
'labels': album_details['labels'],
|
||||
'collections': album_details['collections'],
|
||||
'full_title': u'{} - {}'.format(helpers.get_xml_attr(metadata_main, 'grandparentTitle'),
|
||||
helpers.get_xml_attr(metadata_main, 'title'))
|
||||
helpers.get_xml_attr(metadata_main, 'title')),
|
||||
'children_count': helpers.get_xml_attr(metadata_main, 'leafCount')
|
||||
}
|
||||
|
||||
elif metadata_type == 'photo_album':
|
||||
|
@ -966,7 +1000,8 @@ class PmsConnect(object):
|
|||
'genres': genres,
|
||||
'labels': labels,
|
||||
'collections': collections,
|
||||
'full_title': helpers.get_xml_attr(metadata_main, 'title')
|
||||
'full_title': helpers.get_xml_attr(metadata_main, 'title'),
|
||||
'children_count': helpers.get_xml_attr(metadata_main, 'leafCount')
|
||||
}
|
||||
|
||||
elif metadata_type == 'photo':
|
||||
|
@ -1010,7 +1045,8 @@ class PmsConnect(object):
|
|||
'labels': photo_album_details['labels'],
|
||||
'collections': photo_album_details['collections'],
|
||||
'full_title': u'{} - {}'.format(helpers.get_xml_attr(metadata_main, 'parentTitle'),
|
||||
helpers.get_xml_attr(metadata_main, 'title'))
|
||||
helpers.get_xml_attr(metadata_main, 'title')),
|
||||
'children_count': helpers.get_xml_attr(metadata_main, 'leafCount')
|
||||
}
|
||||
|
||||
elif metadata_type == 'collection':
|
||||
|
@ -1055,7 +1091,8 @@ class PmsConnect(object):
|
|||
'genres': genres,
|
||||
'labels': labels,
|
||||
'collections': collections,
|
||||
'full_title': helpers.get_xml_attr(metadata_main, 'title')
|
||||
'full_title': helpers.get_xml_attr(metadata_main, 'title'),
|
||||
'children_count': helpers.get_xml_attr(metadata_main, 'leafCount')
|
||||
}
|
||||
|
||||
elif metadata_type == 'clip':
|
||||
|
@ -1102,7 +1139,7 @@ class PmsConnect(object):
|
|||
else:
|
||||
return {}
|
||||
|
||||
if metadata:
|
||||
if metadata and media_info:
|
||||
medias = []
|
||||
media_items = metadata_main.getElementsByTagName('Media')
|
||||
for media in media_items:
|
||||
|
@ -1873,18 +1910,21 @@ class PmsConnect(object):
|
|||
else:
|
||||
return False
|
||||
|
||||
def get_item_children(self, rating_key=''):
|
||||
def get_item_children(self, rating_key='', get_grandchildren=False):
|
||||
"""
|
||||
Return processed and validated children list.
|
||||
|
||||
Output: array
|
||||
"""
|
||||
children_data = self.get_children_list(rating_key, output_format='xml')
|
||||
if get_grandchildren:
|
||||
children_data = self.get_metadata_grandchildren(rating_key, output_format='xml')
|
||||
else:
|
||||
children_data = self.get_metadata_children(rating_key, output_format='xml')
|
||||
|
||||
try:
|
||||
xml_head = children_data.getElementsByTagName('MediaContainer')
|
||||
except Exception as e:
|
||||
logger.warn(u"Tautulli Pmsconnect :: Unable to parse XML for get_children_list: %s." % e)
|
||||
logger.warn(u"Tautulli Pmsconnect :: Unable to parse XML for get_item_children: %s." % e)
|
||||
return []
|
||||
|
||||
children_list = []
|
||||
|
@ -1907,21 +1947,72 @@ class PmsConnect(object):
|
|||
if a.getElementsByTagName('Track'):
|
||||
result_data = a.getElementsByTagName('Track')
|
||||
|
||||
section_id = helpers.get_xml_attr(a, 'librarySectionID')
|
||||
|
||||
if result_data:
|
||||
for result in result_data:
|
||||
children_output = {'section_id': section_id,
|
||||
'rating_key': helpers.get_xml_attr(result, 'ratingKey'),
|
||||
'parent_rating_key': helpers.get_xml_attr(result, 'parentRatingKey'),
|
||||
'media_index': helpers.get_xml_attr(result, 'index'),
|
||||
'title': helpers.get_xml_attr(result, 'title'),
|
||||
'parent_title': helpers.get_xml_attr(result, 'parentTitle'),
|
||||
'year': helpers.get_xml_attr(result, 'year'),
|
||||
'thumb': helpers.get_xml_attr(result, 'thumb'),
|
||||
'parent_thumb': helpers.get_xml_attr(a, 'thumb'),
|
||||
'duration': helpers.get_xml_attr(result, 'duration')
|
||||
}
|
||||
for m in result_data:
|
||||
directors = []
|
||||
writers = []
|
||||
actors = []
|
||||
genres = []
|
||||
labels = []
|
||||
|
||||
if m.getElementsByTagName('Director'):
|
||||
for director in m.getElementsByTagName('Director'):
|
||||
directors.append(helpers.get_xml_attr(director, 'tag'))
|
||||
|
||||
if m.getElementsByTagName('Writer'):
|
||||
for writer in m.getElementsByTagName('Writer'):
|
||||
writers.append(helpers.get_xml_attr(writer, 'tag'))
|
||||
|
||||
if m.getElementsByTagName('Role'):
|
||||
for actor in m.getElementsByTagName('Role'):
|
||||
actors.append(helpers.get_xml_attr(actor, 'tag'))
|
||||
|
||||
if m.getElementsByTagName('Genre'):
|
||||
for genre in m.getElementsByTagName('Genre'):
|
||||
genres.append(helpers.get_xml_attr(genre, 'tag'))
|
||||
|
||||
if m.getElementsByTagName('Label'):
|
||||
for label in m.getElementsByTagName('Label'):
|
||||
labels.append(helpers.get_xml_attr(label, 'tag'))
|
||||
|
||||
children_output = {'media_type': helpers.get_xml_attr(m, 'type'),
|
||||
'section_id': helpers.get_xml_attr(m, 'librarySectionID'),
|
||||
'library_name': helpers.get_xml_attr(m, 'librarySectionTitle'),
|
||||
'rating_key': helpers.get_xml_attr(m, 'ratingKey'),
|
||||
'parent_rating_key': helpers.get_xml_attr(m, 'parentRatingKey'),
|
||||
'grandparent_rating_key': helpers.get_xml_attr(m, 'grandparentRatingKey'),
|
||||
'title': helpers.get_xml_attr(m, 'title'),
|
||||
'parent_title': helpers.get_xml_attr(m, 'parentTitle'),
|
||||
'grandparent_title': helpers.get_xml_attr(m, 'grandparentTitle'),
|
||||
'sort_title': helpers.get_xml_attr(m, 'titleSort'),
|
||||
'media_index': helpers.get_xml_attr(m, 'index'),
|
||||
'parent_media_index': helpers.get_xml_attr(m, 'parentIndex'),
|
||||
'studio': helpers.get_xml_attr(m, 'studio'),
|
||||
'content_rating': helpers.get_xml_attr(m, 'contentRating'),
|
||||
'summary': helpers.get_xml_attr(m, 'summary'),
|
||||
'tagline': helpers.get_xml_attr(m, 'tagline'),
|
||||
'rating': helpers.get_xml_attr(m, 'rating'),
|
||||
'audience_rating': helpers.get_xml_attr(m, 'audienceRating'),
|
||||
'user_rating': helpers.get_xml_attr(m, 'userRating'),
|
||||
'duration': helpers.get_xml_attr(m, 'duration'),
|
||||
'year': helpers.get_xml_attr(m, 'year'),
|
||||
'thumb': helpers.get_xml_attr(m, 'thumb'),
|
||||
'parent_thumb': helpers.get_xml_attr(m, 'parentThumb'),
|
||||
'grandparent_thumb': helpers.get_xml_attr(m, 'grandparentThumb'),
|
||||
'art': helpers.get_xml_attr(m, 'art'),
|
||||
'banner': helpers.get_xml_attr(m, 'banner'),
|
||||
'originally_available_at': helpers.get_xml_attr(m, 'originallyAvailableAt'),
|
||||
'added_at': helpers.get_xml_attr(m, 'addedAt'),
|
||||
'updated_at': helpers.get_xml_attr(m, 'updatedAt'),
|
||||
'last_viewed_at': helpers.get_xml_attr(m, 'lastViewedAt'),
|
||||
'guid': helpers.get_xml_attr(m, 'guid'),
|
||||
'directors': directors,
|
||||
'writers': writers,
|
||||
'actors': actors,
|
||||
'genres': genres,
|
||||
'labels': labels,
|
||||
'full_title': helpers.get_xml_attr(m, 'title')
|
||||
}
|
||||
children_list.append(children_output)
|
||||
|
||||
output = {'children_count': helpers.get_xml_attr(xml_head[0], 'size'),
|
||||
|
@ -2157,7 +2248,7 @@ class PmsConnect(object):
|
|||
if str(section_id).isdigit():
|
||||
library_data = self.get_library_list(str(section_id), list_type, count, sort_type, label_key, output_format='xml')
|
||||
elif str(rating_key).isdigit():
|
||||
library_data = self.get_children_list(str(rating_key), output_format='xml')
|
||||
library_data = self.get_metadata_children(str(rating_key), output_format='xml')
|
||||
else:
|
||||
logger.warn(u"Tautulli Pmsconnect :: get_library_children called by invalid section_id or rating_key provided.")
|
||||
return []
|
||||
|
|
|
@ -39,6 +39,7 @@ import http_handler
|
|||
import libraries
|
||||
import log_reader
|
||||
import logger
|
||||
import newsletters
|
||||
import mobile_app
|
||||
import notification_handler
|
||||
import notifiers
|
||||
|
@ -5285,3 +5286,238 @@ class WebInterface(object):
|
|||
@requireAuth()
|
||||
def get_plexpy_url(self, **kwargs):
|
||||
return helpers.get_plexpy_url()
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth()
|
||||
def newsletter(self, **kwargs):
|
||||
news_letter = newsletters.Newsletter()
|
||||
|
||||
config = {
|
||||
"pms_identifier": plexpy.CONFIG.PMS_IDENTIFIER,
|
||||
"pms_web_url": plexpy.CONFIG.PMS_WEB_URL
|
||||
}
|
||||
|
||||
return serve_template(templatename="newsletter_template.html",
|
||||
title="Newsletter",
|
||||
recently_added=news_letter.recently_added,
|
||||
start_date=news_letter.start_date,
|
||||
end_date=news_letter.end_date,
|
||||
config=config)
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
@requireAuth()
|
||||
def newsletter_raw(self, **kwargs):
|
||||
news_letter = newsletters.Newsletter()
|
||||
|
||||
if news_letter.recently_added:
|
||||
return news_letter.recently_added
|
||||
else:
|
||||
return None
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
@requireAuth(member_of("admin"))
|
||||
@addtoapi()
|
||||
def get_newsletters(self, **kwargs):
|
||||
""" Get a list of configured newsletters.
|
||||
|
||||
```
|
||||
Required parameters:
|
||||
None
|
||||
|
||||
Optional parameters:
|
||||
None
|
||||
|
||||
Returns:
|
||||
json:
|
||||
[{"id": 1,
|
||||
"agent_id": 13,
|
||||
"agent_name": "recently_added",
|
||||
"agent_label": "Recently Added",
|
||||
"friendly_name": "",
|
||||
"cron": "0 0 * * 1",
|
||||
"active": 1
|
||||
}
|
||||
]
|
||||
```
|
||||
"""
|
||||
result = newsletters.get_newsletters()
|
||||
return result
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth(member_of("admin"))
|
||||
def get_newsletters_table(self, **kwargs):
|
||||
result = newsletters.get_newsletters()
|
||||
return serve_template(templatename="newsletters_table.html", newsletters_list=result)
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
@requireAuth(member_of("admin"))
|
||||
@addtoapi()
|
||||
def delete_newsletter(self, newsletter_id=None, **kwargs):
|
||||
""" Remove a newsletter from the database.
|
||||
|
||||
```
|
||||
Required parameters:
|
||||
newsletter_id (int): The newsletter to delete
|
||||
|
||||
Optional parameters:
|
||||
None
|
||||
|
||||
Returns:
|
||||
None
|
||||
```
|
||||
"""
|
||||
result = newsletters.delete_newsletter(newsletter_id=newsletter_id)
|
||||
if result:
|
||||
return {'result': 'success', 'message': 'Newsletter deleted successfully.'}
|
||||
else:
|
||||
return {'result': 'error', 'message': 'Failed to delete newsletter.'}
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
@requireAuth(member_of("admin"))
|
||||
@addtoapi()
|
||||
def get_newsletter_config(self, newsletter_id=None, **kwargs):
|
||||
""" Get the configuration for an existing notification agent.
|
||||
|
||||
```
|
||||
Required parameters:
|
||||
newsletter_id (int): The newsletter config to retrieve
|
||||
|
||||
Optional parameters:
|
||||
None
|
||||
|
||||
Returns:
|
||||
json:
|
||||
{"id": 1,
|
||||
"agent_id": 13,
|
||||
"agent_name": "recently_added",
|
||||
"agent_label": "Recently Added",
|
||||
"friendly_name": "",
|
||||
"cron": "0 0 * * 1",
|
||||
"active": 1
|
||||
"config": {"last_days": 7,
|
||||
"incl_movies": 1,
|
||||
"incl_shows": 1,
|
||||
"incl_artists": 1,
|
||||
},
|
||||
"config_options": [{...}, ...]
|
||||
}
|
||||
```
|
||||
"""
|
||||
result = newsletters.get_newsletter_config(newsletter_id=newsletter_id)
|
||||
return result
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth(member_of("admin"))
|
||||
def get_newsletter_config_modal(self, newsletter_id=None, **kwargs):
|
||||
result = newsletters.get_newsletter_config(newsletter_id=newsletter_id)
|
||||
return serve_template(templatename="newsletter_config.html", newsletter=result)
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
@requireAuth(member_of("admin"))
|
||||
@addtoapi()
|
||||
def add_newsletter_config(self, agent_id=None, **kwargs):
|
||||
""" Add a new notification agent.
|
||||
|
||||
```
|
||||
Required parameters:
|
||||
agent_id (int): The newsletter type to add
|
||||
|
||||
Optional parameters:
|
||||
None
|
||||
|
||||
Returns:
|
||||
None
|
||||
```
|
||||
"""
|
||||
result = newsletters.add_newsletter_config(agent_id=agent_id, **kwargs)
|
||||
|
||||
if result:
|
||||
return {'result': 'success', 'message': 'Added newsletter.', 'newsletter_id': result}
|
||||
else:
|
||||
return {'result': 'error', 'message': 'Failed to add newsletter.'}
|
||||
|
||||
@cherrypy.expose
|
||||
@cherrypy.tools.json_out()
|
||||
@requireAuth(member_of("admin"))
|
||||
@addtoapi()
|
||||
def set_newsletter_config(self, newsletter_id=None, agent_id=None, **kwargs):
|
||||
""" Configure an exisitng notificaiton agent.
|
||||
|
||||
```
|
||||
Required parameters:
|
||||
newsletter_id (int): The newsletter config to update
|
||||
agent_id (int): The newsletter type of the newsletter
|
||||
|
||||
Optional parameters:
|
||||
Pass all the config options for the agent with the agent prefix:
|
||||
e.g. For Recently Added: recently_added_last_days
|
||||
recently_added_incl_movies
|
||||
recently_added_incl_shows
|
||||
recently_added_incl_artists
|
||||
|
||||
Returns:
|
||||
None
|
||||
```
|
||||
"""
|
||||
result = newsletters.set_newsletter_config(newsletter_id=newsletter_id,
|
||||
agent_id=agent_id,
|
||||
**kwargs)
|
||||
|
||||
if result:
|
||||
return {'result': 'success', 'message': 'Saved newsletter.'}
|
||||
else:
|
||||
return {'result': 'error', 'message': 'Failed to save newsletter.'}
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth(member_of("admin"))
|
||||
@addtoapi("notify")
|
||||
def send_newsletter(self, newsletter_id=None, test=False, **kwargs):
|
||||
""" Send a newsletter using Tautulli.
|
||||
|
||||
```
|
||||
Required parameters:
|
||||
newsletter_id (int): The ID number of the newsletter
|
||||
|
||||
Optional parameters:
|
||||
None
|
||||
|
||||
Returns:
|
||||
None
|
||||
```
|
||||
"""
|
||||
cherrypy.response.headers['Cache-Control'] = "max-age=0,no-cache,no-store"
|
||||
|
||||
test = 'test ' if test else ''
|
||||
|
||||
if newsletter_id:
|
||||
newsletter = newsletters.get_newsletter_config(newsletter_id=newsletter_id)
|
||||
|
||||
if newsletter:
|
||||
logger.debug(u"Sending %s%s newsletter." % (test, newsletter['agent_name']))
|
||||
if newsletter_handler.send(newsletter_id=newsletter_id,
|
||||
**kwargs):
|
||||
return "Newsletter sent."
|
||||
else:
|
||||
return "Newsletter failed."
|
||||
else:
|
||||
logger.debug(u"Unable to send %snewsletter, invalid newsletter_id %s." % (test, newsletter_id))
|
||||
return "Invalid newsletter id %s." % newsletter_id
|
||||
else:
|
||||
logger.debug(u"Unable to send %snotification, no newsletter_id received." % test)
|
||||
return "No newsletter id received."
|
||||
|
||||
@cherrypy.expose
|
||||
@requireAuth(member_of("admin"))
|
||||
def preview_newsletter(self, newsletter_id=None, **kwargs):
|
||||
if newsletter_id:
|
||||
newsletter = newsletters.get_newsletter_config(newsletter_id=newsletter_id)
|
||||
newsletter_agent = newsletters.get_agent_class(agent_id=newsletter['agent_id'], config=newsletter['config'])
|
||||
if newsletter_agent:
|
||||
return newsletter_agent.preview()
|
||||
|
||||
return
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue