mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-07-12 08:16:06 -07:00
Add Other Video libraries to newsletters
This commit is contained in:
parent
77460f7617
commit
0d30df6853
3 changed files with 268 additions and 15 deletions
|
@ -368,6 +368,7 @@
|
|||
line-height: 1.2rem;
|
||||
font-size: 0.9rem;
|
||||
padding: 5px;
|
||||
max-width: 320px;
|
||||
}
|
||||
.card-info-title a {
|
||||
text-decoration: none;
|
||||
|
@ -952,6 +953,124 @@
|
|||
</td>
|
||||
</tr>
|
||||
% endif
|
||||
% if recently_added.get('other_video'):
|
||||
<tr>
|
||||
<td class="wrapper" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 14px;vertical-align: top;box-sizing: border-box;padding: 5px;overflow: auto;">
|
||||
<div class="sub-header-bar" style="margin-left: auto;margin-right: auto;font-size: 30px;text-align: center;width: 200px;border-top: 1px solid #E5A00D;margin-top: 15px;margin-bottom: 25px;"></div>
|
||||
<div class="sub-header-title" style="margin-left: auto;margin-right: auto;font-size: 30px;text-align: center;font-weight: lighter;">
|
||||
<img src="${(base_url_image + 'images/libraries/video.png') if base_url_image else 'https://tautulli.com/images/libraries/video.png'}" class="sub-header-icon" width="30" height="30" style="border: none;-ms-interpolation-mode: bicubic;max-width: 100%;height: 30px;width: 30px;vertical-align: middle;margin-right: 5px;margin-bottom: 5px;"> Recently Added Videos
|
||||
</div>
|
||||
<div class="sub-header-count" style="margin-left: auto;margin-right: auto;font-size: 30px;text-align: center;">
|
||||
<span class="count" style="color: #E5A00D;">${len(recently_added['other_video'])}</span> <span class="count-units" style="color: #aaaaaa;font-size: 20px;text-transform: uppercase;">video${'s' if len(recently_added['other_video']) > 1 else ''}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 14px;vertical-align: top;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate;mso-table-lspace: 0pt;mso-table-rspace: 0pt;width: 100%;">
|
||||
% for video_a, video_b in grouper(recently_added['other_video'], 2):
|
||||
<tr>
|
||||
<td style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 14px;vertical-align: top;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="border-collapse: separate;mso-table-lspace: 0pt;mso-table-rspace: 0pt;width: 100%;">
|
||||
<tr>
|
||||
% for video in (video_a, video_b):
|
||||
% if video:
|
||||
% if not video_b:
|
||||
<td align="center" valign="top" class="card-instance pad" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 12px;vertical-align: top;overflow: hidden;padding: 0 !important;width: 251px !important;min-width: 251px !important;max-width: 251px !important;"></td>
|
||||
% endif
|
||||
<td align="center" valign="top" class="card-instance movie" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 12px;vertical-align: top;overflow: hidden;padding: 3px;width: 502px;min-width: 502px;max-width: 502px;height: 233px;">
|
||||
<table border="0" cellpadding="0" cellspacing="3" width="100%" class="card-background" style="background-image: url(${(base_url_image + video['art_hash']) if base_url_image else video['art_url']});border-collapse: separate;mso-table-lspace: 0pt;mso-table-rspace: 0pt;width: 100%;background-color: #282828;background-position: center;background-size: cover;background-repeat: no-repeat;background-clip: padding-box;border: 1px solid rgba(255,255,255,.1);">
|
||||
<tr>
|
||||
<td class="card-poster-container" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 14px;vertical-align: top;width: 152px;min-width: 152px;height: 227px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="card-poster" style="background-image: url(${(base_url_image + video['thumb_hash']) if base_url_image else video['thumb_url']});border-collapse: separate;mso-table-lspace: 0pt;mso-table-rspace: 0pt;width: 100%;background-color: #3F4245;background-position: center;background-size: cover;background-repeat: no-repeat;background-clip: padding-box;border: 1px solid rgba(255,255,255,.1);">
|
||||
<tr>
|
||||
<td style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 14px;vertical-align: top;">
|
||||
<a href="${parameters['pms_web_url']}#!/server/${parameters['pms_identifier']}/details?key=%2Flibrary%2Fmetadata%2F${video['rating_key']}" title="${video['title']}" target="_blank" style="text-decoration: underline;">
|
||||
<img class="card-poster-overlay" src="${base_url_image + 'images/newsletter/view-on-plex-poster.png' if base_url_image else 'https://tautulli.com/images/newsletter/view-on-plex-poster.png'}" width="150" height="225" style="border: none;-ms-interpolation-mode: bicubic;max-width: 100%;display: block;">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<td class="card-info-container" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 14px;vertical-align: top;padding-left: 4px;text-align: left;height: 227px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="card-info-container-table" style="border-collapse: separate;mso-table-lspace: 0pt;mso-table-rspace: 0pt;width: 100%;height: 100%;">
|
||||
<tr>
|
||||
<td class="card-info-title nowrap" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 0.9rem;vertical-align: top;white-space: nowrap;text-overflow: ellipsis;overflow: hidden;border-bottom: 1px solid rgba(255, 255, 255, .1);line-height: 1.2rem;padding: 5px;max-width: 320px;">
|
||||
<a href="${parameters['pms_web_url']}#!/server/${parameters['pms_identifier']}/details?key=%2Flibrary%2Fmetadata%2F${video['rating_key']}" title="${video['title']}" target="_blank" style="text-decoration: none;color: #ffffff;">${video['title']}</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="card-info-body" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 0.75rem;vertical-align: top;padding: 5px;height: 100%;">
|
||||
% if video['tagline']:
|
||||
<p class="nowrap mb5" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-weight: 400;margin: 0;margin-bottom: 5px;white-space: nowrap;text-overflow: ellipsis;overflow: hidden;max-width: 325px;color: #ffffff;">
|
||||
<em>${video['tagline']}</em>
|
||||
</p>
|
||||
% endif
|
||||
<p style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-weight: 400;margin: 0;max-width: 325px;color: #ffffff;">
|
||||
${video['summary'][:450] + (video['summary'][450:] and '...')}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="card-info-footer nowrap" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 0.6rem;vertical-align: top;white-space: nowrap;text-overflow: ellipsis;overflow: hidden;padding-top: 0px;padding-right: 5px;padding-bottom: 5px;padding-left: 5px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate;mso-table-lspace: 0pt;mso-table-rspace: 0pt;width: 100%;">
|
||||
<tr>
|
||||
<td class="badge-container" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 14px;vertical-align: top;max-width: 260px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate;mso-table-lspace: 0pt;mso-table-rspace: 0pt;width: 100%;">
|
||||
<tr>
|
||||
% if video['year']:
|
||||
<td class="badge" title="${video['year']}" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 11px;vertical-align: middle;display: inline-block;min-width: 10px;margin-right: 4px;padding: 3px 7px;line-height: 1;text-align: center;white-space: nowrap;background-color: rgba(0, 0, 0, .25);border-radius: 2px;text-overflow: ellipsis;overflow: hidden;color: #ffffff;">${video['year']}</td>
|
||||
% endif
|
||||
% if video['duration']:
|
||||
<% duration = int(int(video['duration'])/60000) %>
|
||||
<td class="badge" title="${duration} mins" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 11px;vertical-align: middle;display: inline-block;min-width: 10px;margin-right: 4px;padding: 3px 7px;line-height: 1;text-align: center;white-space: nowrap;background-color: rgba(0, 0, 0, .25);border-radius: 2px;text-overflow: ellipsis;overflow: hidden;color: #ffffff;">${duration} mins</td>
|
||||
% endif
|
||||
% if video['genres']:
|
||||
% for genre in video['genres'][:]:
|
||||
<td class="badge" title="${genre}" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 11px;vertical-align: middle;display: inline-block;min-width: 10px;margin-right: 4px;padding: 3px 7px;line-height: 1;text-align: center;white-space: nowrap;background-color: rgba(0, 0, 0, .25);border-radius: 2px;text-overflow: ellipsis;overflow: hidden;color: #ffffff;">${genre}</td>
|
||||
% endfor
|
||||
% endif
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
% if video['rating']:
|
||||
<% rating = int(round(float(video['rating']) / 2)) %>
|
||||
<td class="star-rating-container" title="${int(float(video['rating'])/0.1)}%" align="right" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 14px;vertical-align: top;width: 65px;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate;mso-table-lspace: 0pt;mso-table-rspace: 0pt;width: 100%;">
|
||||
<tr>
|
||||
% for _ in range(rating):
|
||||
<td class="star-rating full" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 0.8rem;vertical-align: bottom;margin-left: 4px;line-height: 1rem;width: 0.5rem;display: inline-block;color: #E5A00D;">★</td>
|
||||
% endfor
|
||||
% for _ in range(5-rating):
|
||||
<td class="star-rating empty" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 0.8rem;vertical-align: bottom;margin-left: 4px;line-height: 1rem;width: 0.5rem;display: inline-block;color: #aaaaaa;">☆</td>
|
||||
% endfor
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
% endif
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
% if not video_b:
|
||||
<td align="center" valign="top" class="card-instance pad" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 12px;vertical-align: top;overflow: hidden;padding: 0 !important;width: 251px !important;min-width: 251px !important;max-width: 251px !important;"></td>
|
||||
% endif
|
||||
% endif
|
||||
% endfor
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
% endfor
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
% endif
|
||||
<tr>
|
||||
<td class="footer" style="font-family: 'Open Sans', Helvetica, Arial, sans-serif;font-size: 12px;vertical-align: top;clear: both;margin-top: 10px;text-align: center;width: 100%;">
|
||||
<div class="footer-bar" style="margin-left: auto;margin-right: auto;width: 200px;border-top: 1px solid #E5A00D;margin-top: 25px;"></div>
|
||||
|
|
|
@ -953,6 +953,124 @@
|
|||
</td>
|
||||
</tr>
|
||||
% endif
|
||||
% if recently_added.get('other_video'):
|
||||
<tr>
|
||||
<td class="wrapper">
|
||||
<div class="sub-header-bar"></div>
|
||||
<div class="sub-header-title">
|
||||
<img src="${(base_url_image + 'images/libraries/video.png') if base_url_image else 'https://tautulli.com/images/libraries/video.png'}" class="sub-header-icon" width="30" height="30"/> Recently Added Videos
|
||||
</div>
|
||||
<div class="sub-header-count">
|
||||
<span class="count">${len(recently_added['other_video'])}</span> <span class="count-units">video${'s' if len(recently_added['other_video']) > 1 else ''}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
% for video_a, video_b in grouper(recently_added['other_video'], 2):
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" cellpadding="0" cellspacing="0" width="100%">
|
||||
<tr>
|
||||
% for video in (video_a, video_b):
|
||||
% if video:
|
||||
% if not video_b:
|
||||
<td align="center" valign="top" class="card-instance pad"></td>
|
||||
% endif
|
||||
<td align="center" valign="top" class="card-instance movie">
|
||||
<table border="0" cellpadding="0" cellspacing="3" width="100%" class="card-background" style="background-image: url(${(base_url_image + video['art_hash']) if base_url_image else video['art_url']});">
|
||||
<tr>
|
||||
<td class="card-poster-container">
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="card-poster" style="background-image: url(${(base_url_image + video['thumb_hash']) if base_url_image else video['thumb_url']})">
|
||||
<tr>
|
||||
<td>
|
||||
<a href="${parameters['pms_web_url']}#!/server/${parameters['pms_identifier']}/details?key=%2Flibrary%2Fmetadata%2F${video['rating_key']}" title="${video['title']}" target="_blank">
|
||||
<img class="card-poster-overlay" src="${base_url_image + 'images/newsletter/view-on-plex-poster.png' if base_url_image else 'https://tautulli.com/images/newsletter/view-on-plex-poster.png'}" width="150" height="225">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<td class="card-info-container">
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="card-info-container-table">
|
||||
<tr>
|
||||
<td class="card-info-title nowrap">
|
||||
<a href="${parameters['pms_web_url']}#!/server/${parameters['pms_identifier']}/details?key=%2Flibrary%2Fmetadata%2F${video['rating_key']}" title="${video['title']}" target="_blank">${video['title']}</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="card-info-body">
|
||||
% if video['tagline']:
|
||||
<p class="nowrap mb5">
|
||||
<em>${video['tagline']}</em>
|
||||
</p>
|
||||
% endif
|
||||
<p>
|
||||
${video['summary'][:450] + (video['summary'][450:] and '...')}
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="card-info-footer nowrap">
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td class="badge-container">
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
% if video['year']:
|
||||
<td class="badge" title="${video['year']}">${video['year']}</td>
|
||||
% endif
|
||||
% if video['duration']:
|
||||
<% duration = int(int(video['duration'])/60000) %>
|
||||
<td class="badge" title="${duration} mins">${duration} mins</td>
|
||||
% endif
|
||||
% if video['genres']:
|
||||
% for genre in video['genres'][:]:
|
||||
<td class="badge" title="${genre}">${genre}</td>
|
||||
% endfor
|
||||
% endif
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
% if video['rating']:
|
||||
<% rating = int(round(float(video['rating']) / 2)) %>
|
||||
<td class="star-rating-container" title="${int(float(video['rating'])/0.1)}%" align="right">
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
% for _ in range(rating):
|
||||
<td class="star-rating full">★</td>
|
||||
% endfor
|
||||
% for _ in range(5-rating):
|
||||
<td class="star-rating empty">☆</td>
|
||||
% endfor
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
% endif
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
% if not video_b:
|
||||
<td align="center" valign="top" class="card-instance pad"></td>
|
||||
% endif
|
||||
% endif
|
||||
% endfor
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
% endfor
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
% endif
|
||||
<tr>
|
||||
<td class="footer">
|
||||
<div class="footer-bar"></div>
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
# along with Tautulli. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import arrow
|
||||
from collections import OrderedDict
|
||||
import json
|
||||
from itertools import groupby
|
||||
from mako.lookup import TemplateLookup
|
||||
|
@ -693,7 +694,7 @@ class RecentlyAdded(Newsletter):
|
|||
|
||||
recently_added.extend(filtered_items)
|
||||
|
||||
if media_type == 'movie':
|
||||
if media_type in ('movie', 'other_video'):
|
||||
movie_list = []
|
||||
for item in recently_added:
|
||||
# Filter included libraries
|
||||
|
@ -795,8 +796,13 @@ class RecentlyAdded(Newsletter):
|
|||
if not self.config['incl_libraries']:
|
||||
logger.warn(u"Tautulli Newsletters :: Failed to retrieve %s newsletter data: no libraries selected." % self.NAME)
|
||||
|
||||
media_types = {s['section_type'] for s in self._get_sections()
|
||||
if str(s['section_id']) in self.config['incl_libraries']}
|
||||
media_types = set()
|
||||
for s in self._get_sections():
|
||||
if str(s['section_id']) in self.config['incl_libraries']:
|
||||
if s['section_type'] == 'movie' and s['agent'] == 'com.plexapp.agents.none':
|
||||
media_types.add('other_video')
|
||||
else:
|
||||
media_types.add(s['section_type'])
|
||||
|
||||
recently_added = {}
|
||||
for media_type in media_types:
|
||||
|
@ -807,9 +813,10 @@ class RecentlyAdded(Newsletter):
|
|||
shows = recently_added.get('show', [])
|
||||
artists = recently_added.get('artist', [])
|
||||
albums = [a for artist in artists for a in artist['album']]
|
||||
other_video = recently_added.get('other_video', [])
|
||||
|
||||
if self.is_preview or helpers.get_img_service(include_self=True) == 'self-hosted':
|
||||
for item in movies + shows + albums:
|
||||
for item in movies + shows + albums + other_video:
|
||||
if item['media_type'] == 'album':
|
||||
height = 150
|
||||
fallback = 'cover'
|
||||
|
@ -833,7 +840,7 @@ class RecentlyAdded(Newsletter):
|
|||
|
||||
elif helpers.get_img_service():
|
||||
# Upload posters and art to image hosting service
|
||||
for item in movies + shows + albums:
|
||||
for item in movies + shows + albums + other_video:
|
||||
if item['media_type'] == 'album':
|
||||
height = 150
|
||||
fallback = 'cover'
|
||||
|
@ -858,7 +865,7 @@ class RecentlyAdded(Newsletter):
|
|||
item['poster_url'] = item['thumb_url'] # Keep for backwards compatibility
|
||||
|
||||
else:
|
||||
for item in movies + shows + albums:
|
||||
for item in movies + shows + albums + other_video:
|
||||
item['thumb_hash'] = ''
|
||||
item['art_hash'] = ''
|
||||
item['thumb_url'] = ''
|
||||
|
@ -871,10 +878,11 @@ class RecentlyAdded(Newsletter):
|
|||
|
||||
def _has_data(self):
|
||||
recently_added = self.data.get('recently_added')
|
||||
if recently_added and \
|
||||
recently_added.get('movie') or \
|
||||
recently_added.get('show') or \
|
||||
recently_added.get('artist'):
|
||||
if recently_added and (
|
||||
recently_added.get('movie') or
|
||||
recently_added.get('show') or
|
||||
recently_added.get('artist') or
|
||||
recently_added.get('other_video')):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
@ -883,18 +891,26 @@ class RecentlyAdded(Newsletter):
|
|||
return libraries.Libraries().get_sections()
|
||||
|
||||
def _get_sections_options(self):
|
||||
library_types = {'movie': 'Movie Libraries',
|
||||
'show': 'TV Show Libraries',
|
||||
'artist': 'Music Libraries'}
|
||||
sections = {}
|
||||
for s in self._get_sections():
|
||||
if s['section_type'] != 'photo':
|
||||
library_type = library_types[s['section_type']]
|
||||
if s['section_type'] == 'movie' and s['agent'] == 'com.plexapp.agents.none':
|
||||
library_type = 'other_video'
|
||||
else:
|
||||
library_type = s['section_type']
|
||||
group = sections.get(library_type, [])
|
||||
group.append({'value': s['section_id'],
|
||||
'text': s['section_name']})
|
||||
sections[library_type] = group
|
||||
return sections
|
||||
|
||||
groups = OrderedDict([(k, v) for k, v in [
|
||||
('Movie Libraries', sections.get('movie')),
|
||||
('TV Show Libraries', sections.get('show')),
|
||||
('Music Libraries', sections.get('artist')),
|
||||
('Other Video Libraries', sections.get('other_video'))
|
||||
] if v is not None])
|
||||
|
||||
return groups
|
||||
|
||||
def build_params(self):
|
||||
parameters = self._build_params()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue