diff --git a/data/interfaces/default/images/newsletter/view-on-plex-cover.png b/data/interfaces/default/images/newsletter/view-on-plex-cover.png new file mode 100644 index 00000000..506848b7 Binary files /dev/null and b/data/interfaces/default/images/newsletter/view-on-plex-cover.png differ diff --git a/data/interfaces/default/images/newsletter/view-on-plex-poster.png b/data/interfaces/default/images/newsletter/view-on-plex-poster.png new file mode 100644 index 00000000..40a3e2d1 Binary files /dev/null and b/data/interfaces/default/images/newsletter/view-on-plex-poster.png differ diff --git a/data/interfaces/newsletters/recently_added.html b/data/interfaces/newsletters/recently_added.html index e5f79219..43a62e8a 100644 --- a/data/interfaces/newsletters/recently_added.html +++ b/data/interfaces/newsletters/recently_added.html @@ -1,6 +1,7 @@ % if data: <% import plexpy + from helpers import grouper recently_added = data['recently_added'] if plexpy.CONFIG.NEWSLETTER_SELF_HOSTED and plexpy.CONFIG.NEWSLETTER_BASE_URL: @@ -73,7 +74,7 @@ box-sizing: border-box; display: block; margin: 0 auto; - max-width: 1042px; + max-width: 1037px; padding: 10px; } @@ -139,10 +140,8 @@ ul, ol { font-family: 'Open Sans', Helvetica, Arial, sans-serif; - font-size: 14px; font-weight: 400; margin: 0; - margin-bottom: 15px; } p li, @@ -245,6 +244,8 @@ .nowrap { white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; } .clear { clear: both; @@ -335,150 +336,135 @@ MEDIA CARDS ------------------------------------- */ .card-instance { - float: left; - width: 500px; - margin: 3px; - border: 1px solid rgba(255,255,255,.1); - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; font-size: 12px; overflow: hidden; - position: relative; + padding: 3px; + width: 502px; + min-width: 502px; + max-width: 502px; } .card-instance.movie, .card-instance.show { - height: 235px; + height: 233px; } .card-instance.album { - height: 160px; - } - .card-instance.odd { - float: none !important; - margin: 3px auto !important; + height: 158px; } .card-background { background-position: center; background-size: cover; - width: 100%; - height: 100%; + background-repeat: no-repeat; + background-clip: padding-box; + border: 1px solid rgba(255,255,255,.1); } .card-poster-container { - width: 150px; - margin: 3px; - border: 1px solid rgba(255,255,255,.1); - float: left; - position: relative; - z-index: 1; + width: 152px; + min-width: 152px; } .card-instance.movie .card-poster-container, .card-instance.show .card-poster-container{ - height: 225px; + height: 227px; } .card-instance.album .card-poster-container { - height: 150px; + height: 152px; } .card-poster { background-color: #3F4245; background-position: center; background-size: cover; - height: 100%; - width: 100%; + background-repeat: no-repeat; + background-clip: padding-box; + border: 1px solid rgba(255,255,255,.1); } .card-poster-overlay { - background-repeat: no-repeat; - background-position: bottom right; - width: 100%; - height: 100%; + display: block; } .card-info-container { - margin: 4px; - width: 332px; - float: left; - position: relative; - z-index: 1; + padding-left: 4px; } .card-instance.movie .card-info-container, .card-instance.show .card-info-container{ - height: 225px; + height: 227px; } .card-instance.album .card-info-container { - height: 150px; + height: 152px; } - .card-info-container > div { - padding: 5px; - text-overflow: ellipsis; - overflow: hidden; + .card-info-container > table { + height: 100%; } .card-info-title { border-bottom: 1px solid rgba(255, 255, 255, .1); - line-height: 20px; - font-size: 15px; + line-height: 1.2rem; + font-size: 0.9rem; + padding: 5px; } .card-info-title a { text-decoration: none; color: #ffffff; } - .card-info-body > div { - color: #ffffff; - font-size: 12px; - text-overflow: ellipsis; - overflow: hidden; + .card-info-body { + font-size: 0.75rem; + padding: 5px; + height: 100%; + } + .card-info-body > p { + max-width: 325px; } .card-instance.movie .card-info-body, .card-instance.show .card-info-body { - min-height: 157px; } .card-instance.album .card-info-body { + height: 82px; min-height: 82px; } .card-info-footer { - font-size: 10px; + font-size: 0.6rem; + padding-top: 0px; + padding-right: 5px; + padding-bottom: 5px; + padding-left: 5px; + } + .card-info-footer .star-rating-container { + vertical-align: bottom; + padding-right: 5px; } .card-info-footer .star-rating { - width: 60px; - font-size: 14px; - line-height: 17px; - float: right; - } - .star-rating-full { - color: #E5A00D; - direction: ltr; - padding: 0; - overflow: hidden; - float: left; - } - .star-rating-empty { - color: #aaaaaa; - direction: rtl; - padding: 0; - overflow: hidden; - float: left; - } - .star-rating span { + font-size: 0.8rem; + line-height: 1rem; + width: 0.5rem; display: inline-block; - text-align: center; - width: 12px; + } + .star-rating.full { + color: #E5A00D; + } + .star-rating.empty { + color: #aaaaaa; } .badge { display: inline-block; min-width: 10px; padding: 3px 7px; font-size: 11px; - font-style: normal; line-height: 1; - color: #fff; text-align: center; white-space: nowrap; vertical-align: middle; background-color: rgba(0, 0, 0, .25); border-radius: 2px; + max-width: 75px; + text-overflow: ellipsis; + overflow: hidden; } /* ------------------------------------- RESPONSIVE AND MOBILE FRIENDLY STYLES ------------------------------------- */ - @media only screen and (max-width: 620px) { + @media only screen and (max-width: 1040px) { + .card-instance { + display: block !important; + margin: 0 auto; + } + table[class=body] .header { background-position: center !important; } @@ -510,11 +496,6 @@ font-size: 14px !important; } - table[class=body] .card-instance { - margin: 3px auto; - float: none; - } - table[class=body] .content { padding: 0 !important; } @@ -573,12 +554,12 @@
-
+
- + - +
@@ -588,9 +569,9 @@
${parameters['start_date']} - ${parameters['end_date']}
+ % if recently_added.get('movie'): + + + + + % endif + % if recently_added.get('show'): + + + + % endif + % if recently_added.get('artist'): + + + + % endif + + diff --git a/data/interfaces/newsletters/recently_added_master.html b/data/interfaces/newsletters/recently_added_master.html index 6cfe1879..179aa5da 100644 --- a/data/interfaces/newsletters/recently_added_master.html +++ b/data/interfaces/newsletters/recently_added_master.html @@ -1,6 +1,7 @@ % if data: <% import plexpy + from helpers import grouper recently_added = data['recently_added'] if plexpy.CONFIG.NEWSLETTER_SELF_HOSTED and plexpy.CONFIG.NEWSLETTER_BASE_URL: @@ -73,7 +74,7 @@ box-sizing: border-box; display: block; margin: 0 auto; - max-width: 1042px; + max-width: 1037px; padding: 10px; } @@ -139,10 +140,8 @@ ul, ol { font-family: 'Open Sans', Helvetica, Arial, sans-serif; - font-size: 14px; font-weight: 400; margin: 0; - margin-bottom: 15px; } p li, @@ -245,6 +244,8 @@ .nowrap { white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; } .clear { clear: both; @@ -335,150 +336,135 @@ MEDIA CARDS ------------------------------------- */ .card-instance { - float: left; - width: 500px; - margin: 3px; - border: 1px solid rgba(255,255,255,.1); - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; font-size: 12px; overflow: hidden; - position: relative; + padding: 3px; + width: 502px; + min-width: 502px; + max-width: 502px; } .card-instance.movie, .card-instance.show { - height: 235px; + height: 233px; } .card-instance.album { - height: 160px; - } - .card-instance.odd { - float: none !important; - margin: 3px auto !important; + height: 158px; } .card-background { background-position: center; background-size: cover; - width: 100%; - height: 100%; + background-repeat: no-repeat; + background-clip: padding-box; + border: 1px solid rgba(255,255,255,.1); } .card-poster-container { - width: 150px; - margin: 3px; - border: 1px solid rgba(255,255,255,.1); - float: left; - position: relative; - z-index: 1; + width: 152px; + min-width: 152px; } .card-instance.movie .card-poster-container, .card-instance.show .card-poster-container{ - height: 225px; + height: 227px; } .card-instance.album .card-poster-container { - height: 150px; + height: 152px; } .card-poster { background-color: #3F4245; background-position: center; background-size: cover; - height: 100%; - width: 100%; + background-repeat: no-repeat; + background-clip: padding-box; + border: 1px solid rgba(255,255,255,.1); } .card-poster-overlay { - background-repeat: no-repeat; - background-position: bottom right; - width: 100%; - height: 100%; + display: block; } .card-info-container { - margin: 4px; - width: 332px; - float: left; - position: relative; - z-index: 1; + padding-left: 4px; } .card-instance.movie .card-info-container, .card-instance.show .card-info-container{ - height: 225px; + height: 227px; } .card-instance.album .card-info-container { - height: 150px; + height: 152px; } - .card-info-container > div { - padding: 5px; - text-overflow: ellipsis; - overflow: hidden; + .card-info-container > table { + height: 100%; } .card-info-title { border-bottom: 1px solid rgba(255, 255, 255, .1); - line-height: 20px; - font-size: 15px; + line-height: 1.2rem; + font-size: 0.9rem; + padding: 5px; } .card-info-title a { text-decoration: none; color: #ffffff; } - .card-info-body > div { - color: #ffffff; - font-size: 12px; - text-overflow: ellipsis; - overflow: hidden; + .card-info-body { + font-size: 0.75rem; + padding: 5px; + height: 100%; + } + .card-info-body > p { + max-width: 325px; } .card-instance.movie .card-info-body, .card-instance.show .card-info-body { - min-height: 157px; } .card-instance.album .card-info-body { + height: 82px; min-height: 82px; } .card-info-footer { - font-size: 10px; + font-size: 0.6rem; + padding-top: 0px; + padding-right: 5px; + padding-bottom: 5px; + padding-left: 5px; + } + .card-info-footer .star-rating-container { + vertical-align: bottom; + padding-right: 5px; } .card-info-footer .star-rating { - width: 60px; - font-size: 14px; - line-height: 17px; - float: right; - } - .star-rating-full { - color: #E5A00D; - direction: ltr; - padding: 0; - overflow: hidden; - float: left; - } - .star-rating-empty { - color: #aaaaaa; - direction: rtl; - padding: 0; - overflow: hidden; - float: left; - } - .star-rating span { + font-size: 0.8rem; + line-height: 1rem; + width: 0.5rem; display: inline-block; - text-align: center; - width: 12px; + } + .star-rating.full { + color: #E5A00D; + } + .star-rating.empty { + color: #aaaaaa; } .badge { display: inline-block; min-width: 10px; padding: 3px 7px; font-size: 11px; - font-style: normal; line-height: 1; - color: #fff; text-align: center; white-space: nowrap; vertical-align: middle; background-color: rgba(0, 0, 0, .25); border-radius: 2px; + max-width: 75px; + text-overflow: ellipsis; + overflow: hidden; } /* ------------------------------------- RESPONSIVE AND MOBILE FRIENDLY STYLES ------------------------------------- */ - @media only screen and (max-width: 620px) { + @media only screen and (max-width: 1040px) { + .card-instance { + display: block !important; + margin: 0 auto; + } + table[class=body] .header { background-position: center !important; } @@ -510,11 +496,6 @@ font-size: 14px !important; } - table[class=body] .card-instance { - margin: 3px auto; - float: none; - } - table[class=body] .content { padding: 0 !important; } @@ -576,9 +557,9 @@
- Tautulli card-backbro - ${title} + Tautulli Newsletter - ${title} -
- % if recently_added.get('movie'):
@@ -602,67 +583,106 @@
-
- % for movie in recently_added['movie']: - <% - if loop.index == len(recently_added['movie'])-1 and loop.index % 2 == 0: - clear = '
' - odd = 'odd' - else: - clear = odd = '' - %> - ${clear | n} -
-
- -
- -
- % if movie['tagline']: -
- ${movie['tagline']} -
- % endif -
- ${movie['summary'][:450] + (movie['summary'][450:] and '...')} -
-
- -
-
-
- % endfor -
- % endif - % if recently_added.get('show'): +
+ + + % for movie_a, movie_b in grouper(recently_added['movie'], 2): + + % for movie in (movie_a, movie_b): + % if movie: + + % else: + + % endif + % endfor + + % endfor + +
+ + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + + + +
+ ${movie['title']} +
+ % if movie['tagline']: +

+ ${movie['tagline']} +

+ % endif +

+ ${movie['summary'][:450] + (movie['summary'][450:] and '...')} +

+
+
+
+
@@ -676,101 +696,129 @@
-
- % for show in recently_added['show']: - <% - if show['season_count'] == 1 and show['season'][0]['episode_count'] == 1: - link_rating_key = show['season'][0]['episode'][0]['rating_key'] - link_title = show['title'] + " - " + show['season'][0]['episode'][0]['title'] - else: - link_rating_key = show['rating_key'] - link_title = show['title'] - - if loop.index == len(recently_added['show'])-1 and loop.index % 2 == 0: - clear = '
' - odd = 'odd' - else: - clear = odd = '' - %> - ${clear | n} -
-
- -
- -
-
- % if show['season_count'] > 1: - ${show['season_count']} seasons / - % endif - <% total_show_episodes = sum(s['episode_count'] for s in show['season']) %> - ${total_show_episodes} episode${'s' if total_show_episodes > 1 else ''} -
-
- % for i, season in enumerate(show['season'][:8]): - Season ${season['media_index']} · - % if season['episode_count'] == 1: - Episode ${season['episode'][0]['media_index']} - ${season['episode'][0]['title']} - % else: - Episodes ${season['episode_range']} - % endif - % if i < min(show['season_count'], 7): -
- % elif i == 7 and show['season_count'] > 8: - ...plus ${show['season_count'] - 8} more seasons! - % endif - % endfor -
-
- % if show['season_count'] == 1 and show['season'][0]['episode_count'] == 1: - ${show['season'][0]['episode'][0]['summary'][:350] + (show['season'][0]['episode'][0]['summary'][350:] and '...')} - % else: - <% length = max(0, 350 - 50 * (show['season_count'] - 1)) %> - % if length: - ${show['summary'][:length] + (show['summary'][length:] and '...')} - % endif - % endif -
-
- -
-
-
- % endfor -
- % endif - % if recently_added.get('artist'): + + + % for show_a, show_b in grouper(recently_added['show'], 2): + + % for show in (show_a, show_b): + % if show: + + % else: + + % endif + % endfor + + % endfor + +
+ + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + + + +
+ ${show['title']} +
+

+ % if show['season_count'] > 1: + ${show['season_count']} seasons / + % endif + <% total_show_episodes = sum(s['episode_count'] for s in show['season']) %> + ${total_show_episodes} episode${'s' if total_show_episodes > 1 else ''} +

+

+ % for i, season in enumerate(show['season'][:8]): + Season ${season['media_index']} · + % if season['episode_count'] == 1: + Episode ${season['episode'][0]['media_index']} - ${season['episode'][0]['title']} + % else: + Episodes ${season['episode_range']} + % endif + % if i < min(show['season_count'], 7): +
+ % elif i == 7 and show['season_count'] > 8: + ...plus ${show['season_count'] - 8} more seasons! + % endif + % endfor +

+

+ % if show['season_count'] == 1 and show['season'][0]['episode_count'] == 1: + ${show['season'][0]['episode'][0]['summary'][:350] + (show['season'][0]['episode'][0]['summary'][350:] and '...')} + % else: + <% length = max(0, 350 - 50 * (show['season_count'] - 1)) %> + % if length: + ${show['summary'][:length] + (show['summary'][length:] and '...')} + % endif + % endif +

+
+
+
+
@@ -784,71 +832,100 @@
-
- <% album_count = 0 %> - % for artist in recently_added['artist']: - % for album in artist['album']: - <% - album_count += 1 - - if album_count == total_albums and album_count % 2 == 1: - clear = '
' - odd = 'odd' - else: - clear = odd = '' - %> - ${clear | n} -
-
- -
- -
-
- ${artist['title']} · ${album['track_count']} track${'s' if album['track_count'] > 1 else ''} -
- % if artist['title'].lower() != 'various artists': -
- ${album['summary'][:200] + (album['summary'][200:] and '...')} -
- % endif -
- -
-
-
- % endfor - % endfor -
- % endif - + + + % for album_a, album_b in grouper([a for artist in recently_added['artist'] for a in artist['album']], 2): + + % for album in (album_a, album_b): + % if album: + + % else: + + % endif + % endfor + + % endfor + +
+ + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + + + +
+ ${album['title']} +
+

+ ${album['parent_title']} · ${album['track_count']} track${'s' if album['track_count'] > 1 else ''} +

+ % if artist['title'].lower() != 'various artists': +

+ ${album['summary'][:200] + (album['summary'][200:] and '...')} +

+ % endif +
+
+
+
-
+
@@ -588,9 +569,9 @@
${parameters['start_date']} - ${parameters['end_date']}
+ % if recently_added.get('movie'): + + + + + % endif + % if recently_added.get('show'): + + + + % endif + % if recently_added.get('artist'): + + + + % endif + + diff --git a/plexpy/helpers.py b/plexpy/helpers.py index 466d901d..175588c9 100644 --- a/plexpy/helpers.py +++ b/plexpy/helpers.py @@ -20,6 +20,7 @@ import geoip2.database, geoip2.errors import gzip import hashlib import imghdr +from itertools import izip_longest import ipwhois, ipwhois.exceptions, ipwhois.utils from IPy import IP import json @@ -974,3 +975,10 @@ def momentjs_to_arrow(format, duration=False): for f in invalid_formats: format = format.replace(f, '') return format + + +def grouper(iterable, n, fillvalue=None): + "Collect data into fixed-length chunks or blocks" + # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx + args = [iter(iterable)] * n + return izip_longest(fillvalue=fillvalue, *args) \ No newline at end of file diff --git a/plexpy/newsletters.py b/plexpy/newsletters.py index 95318a7a..ed822045 100644 --- a/plexpy/newsletters.py +++ b/plexpy/newsletters.py @@ -413,7 +413,7 @@ class Newsletter(object): os.makedirs(newsletter_folder) try: - with open(newsletter_file_fp, 'w') as n_file: + with open(newsletter_file_fp, 'wb') as n_file: n_file.write(self.newsletter.encode('utf-8')) logger.info(u"Tautulli Newsletters :: %s newsletter saved to %s" % (self.NAME, newsletter_file)) @@ -661,8 +661,8 @@ class RecentlyAdded(Newsletter): if self.is_preview or plexpy.CONFIG.NEWSLETTER_SELF_HOSTED: for item in movies + shows + albums: item['thumb_hash'] = set_hash_image_info(img=item['thumb'], - width=300, - height=450, + width=150, + height=225, fallback='poster') item['art_hash'] = set_hash_image_info(img=item['art'], width=500,
- % if recently_added.get('movie'):
@@ -602,67 +583,106 @@
-
- % for movie in recently_added['movie']: - <% - if loop.index == len(recently_added['movie'])-1 and loop.index % 2 == 0: - clear = '
' - odd = 'odd' - else: - clear = odd = '' - %> - ${clear | n} -
-
- -
- -
- % if movie['tagline']: -
- ${movie['tagline']} -
- % endif -
- ${movie['summary'][:450] + (movie['summary'][450:] and '...')} -
-
- -
-
-
- % endfor -
- % endif - % if recently_added.get('show'): +
+ + + % for movie_a, movie_b in grouper(recently_added['movie'], 2): + + % for movie in (movie_a, movie_b): + % if movie: + + % else: + + % endif + % endfor + + % endfor + +
+ + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + + + +
+ ${movie['title']} +
+ % if movie['tagline']: +

+ ${movie['tagline']} +

+ % endif +

+ ${movie['summary'][:450] + (movie['summary'][450:] and '...')} +

+
+
+
+
@@ -676,101 +696,129 @@
-
- % for show in recently_added['show']: - <% - if show['season_count'] == 1 and show['season'][0]['episode_count'] == 1: - link_rating_key = show['season'][0]['episode'][0]['rating_key'] - link_title = show['title'] + " - " + show['season'][0]['episode'][0]['title'] - else: - link_rating_key = show['rating_key'] - link_title = show['title'] - - if loop.index == len(recently_added['show'])-1 and loop.index % 2 == 0: - clear = '
' - odd = 'odd' - else: - clear = odd = '' - %> - ${clear | n} -
-
- -
- -
-
- % if show['season_count'] > 1: - ${show['season_count']} seasons / - % endif - <% total_show_episodes = sum(s['episode_count'] for s in show['season']) %> - ${total_show_episodes} episode${'s' if total_show_episodes > 1 else ''} -
-
- % for i, season in enumerate(show['season'][:8]): - Season ${season['media_index']} · - % if season['episode_count'] == 1: - Episode ${season['episode'][0]['media_index']} - ${season['episode'][0]['title']} - % else: - Episodes ${season['episode_range']} - % endif - % if i < min(show['season_count'], 7): -
- % elif i == 7 and show['season_count'] > 8: - ...plus ${show['season_count'] - 8} more seasons! - % endif - % endfor -
-
- % if show['season_count'] == 1 and show['season'][0]['episode_count'] == 1: - ${show['season'][0]['episode'][0]['summary'][:350] + (show['season'][0]['episode'][0]['summary'][350:] and '...')} - % else: - <% length = max(0, 350 - 50 * (show['season_count'] - 1)) %> - % if length: - ${show['summary'][:length] + (show['summary'][length:] and '...')} - % endif - % endif -
-
- -
-
-
- % endfor -
- % endif - % if recently_added.get('artist'): + + + % for show_a, show_b in grouper(recently_added['show'], 2): + + % for show in (show_a, show_b): + % if show: + + % else: + + % endif + % endfor + + % endfor + +
+ + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + + + +
+ ${show['title']} +
+

+ % if show['season_count'] > 1: + ${show['season_count']} seasons / + % endif + <% total_show_episodes = sum(s['episode_count'] for s in show['season']) %> + ${total_show_episodes} episode${'s' if total_show_episodes > 1 else ''} +

+

+ % for i, season in enumerate(show['season'][:8]): + Season ${season['media_index']} · + % if season['episode_count'] == 1: + Episode ${season['episode'][0]['media_index']} - ${season['episode'][0]['title']} + % else: + Episodes ${season['episode_range']} + % endif + % if i < min(show['season_count'], 7): +
+ % elif i == 7 and show['season_count'] > 8: + ...plus ${show['season_count'] - 8} more seasons! + % endif + % endfor +

+

+ % if show['season_count'] == 1 and show['season'][0]['episode_count'] == 1: + ${show['season'][0]['episode'][0]['summary'][:350] + (show['season'][0]['episode'][0]['summary'][350:] and '...')} + % else: + <% length = max(0, 350 - 50 * (show['season_count'] - 1)) %> + % if length: + ${show['summary'][:length] + (show['summary'][length:] and '...')} + % endif + % endif +

+
+
+
+
@@ -784,71 +832,100 @@
-
- <% album_count = 0 %> - % for artist in recently_added['artist']: - % for album in artist['album']: - <% - album_count += 1 - - if album_count == total_albums and album_count % 2 == 1: - clear = '
' - odd = 'odd' - else: - clear = odd = '' - %> - ${clear | n} -
-
- -
- -
-
- ${artist['title']} · ${album['track_count']} track${'s' if album['track_count'] > 1 else ''} -
- % if artist['title'].lower() != 'various artists': -
- ${album['summary'][:200] + (album['summary'][200:] and '...')} -
- % endif -
- -
-
-
- % endfor - % endfor -
- % endif - + + + % for album_a, album_b in grouper([a for artist in recently_added['artist'] for a in artist['album']], 2): + + % for album in (album_a, album_b): + % if album: + + % else: + + % endif + % endfor + + % endfor + +
+ + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + + + +
+ ${album['title']} +
+

+ ${album['parent_title']} · ${album['track_count']} track${'s' if album['track_count'] > 1 else ''} +

+ % if artist['title'].lower() != 'various artists': +

+ ${album['summary'][:200] + (album['summary'][200:] and '...')} +

+ % endif +
+
+
+
-