75 lines
5.6 KiB
Python
75 lines
5.6 KiB
Python
import base64
|
||
import html
|
||
from urllib.parse import urlencode
|
||
|
||
from .recap_email import document
|
||
|
||
|
||
def description(entry):
|
||
if entry['type'] == 'movie':
|
||
return f"Movie · {entry['year']}" if entry.get('year') else 'Movie'
|
||
seasons = sorted({item['season'] for item in entry['items'] if isinstance(item.get('season'), int)})
|
||
count = len(entry['items'])
|
||
labels = ', '.join('Specials' if value == 0 else str(value) for value in seasons[:8])
|
||
suffix = f" · {'Season' if len(seasons) == 1 else 'Seasons'} {labels}" if labels else ''
|
||
return f"{count} new {'episode' if count == 1 else 'episodes'}{suffix}"
|
||
|
||
|
||
def render_confirmation(username, url):
|
||
intro = f"Hi {username}, confirm your email to receive new arrivals, featured picks and announcements from Grizzlyflix."
|
||
return {'subject': 'Confirm your Grizzlyflix newsletter subscription',
|
||
'body_text': f'{intro}\n\nConfirm newsletter subscription: {url}\n\nThis link expires in 24 hours. If you did not request this, ignore this email.',
|
||
'body_html': document(title='Your next watch starts here.', intro=intro,
|
||
content='<p style="color:#bdb6c3;font-size:14px;line-height:1.7">A weekly look at new movies and TV updates, with posters and links to watch.</p>',
|
||
action='Confirm newsletter subscription', url=url, kicker='NEW ON GRIZZLYFLIX',
|
||
footer='This link expires in 24 hours. If you did not request this, ignore this email.')}
|
||
|
||
|
||
def render(content, images, public_url, playback_url, unsubscribe_url, *, preview=False, test=False):
|
||
esc = html.escape
|
||
titles = [entry for entry in content['titles'] if entry['selected']]
|
||
body, lines, attachments = [], [], []
|
||
intro = str(content.get('intro') or '').strip()
|
||
if intro:
|
||
body.append(f'<p style="font-size:15px;line-height:1.8;color:#e5e1e4;overflow-wrap:anywhere">{esc(intro).replace(chr(10), "<br>")}</p>')
|
||
lines += [intro, '']
|
||
sections = [('Featured picks', [entry for entry in titles if entry['featured']]),
|
||
('New movies', [entry for entry in titles if not entry['featured'] and entry['type'] == 'movie']),
|
||
('Fresh episodes', [entry for entry in titles if not entry['featured'] and entry['type'] == 'series'])]
|
||
for heading, entries in sections:
|
||
if not entries:
|
||
continue
|
||
body.append(f'<h2 style="font-size:20px;margin:28px 0 8px;color:#e5e1e4">{heading}</h2>')
|
||
lines += [heading, '']
|
||
for entry in entries:
|
||
watch = playback_url + '/web/index.html#!/details?' + urlencode({'id': entry['id'], 'serverId': content['server_id']})
|
||
image_data = images.get(entry['id'])
|
||
cid = f"newsletter-{entry['id']}@magent"
|
||
if image_data:
|
||
source = 'data:image/jpeg;base64,' + base64.b64encode(image_data).decode() if preview else 'cid:' + cid
|
||
poster = f'<img src="{source}" width="80" alt="{esc(entry["title"], quote=True)}" style="display:block;width:80px;height:auto;border-radius:7px;border:0">'
|
||
if not preview:
|
||
attachments.append({'cid': cid, 'data': image_data})
|
||
else:
|
||
poster = f'<div style="width:80px;height:112px;line-height:112px;background:#353039;color:#c7bdff;text-align:center;border-radius:7px;font-size:11px">{"TV" if entry["type"] == "series" else "MOVIE"}</div>'
|
||
details = description(entry)
|
||
overview = str(entry.get('overview') or '')[:180]
|
||
copy = f'<p style="margin:8px 0;font-size:12px;line-height:1.6;color:#bdb6c3">{esc(overview)}</p>' if overview and entry['featured'] else ''
|
||
body.append(f'''<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="table-layout:fixed;border-bottom:1px solid #363338"><tr>
|
||
<td width="92" valign="top" style="padding:18px 12px 18px 0">{poster}</td><td valign="top" style="padding:18px 0;overflow-wrap:anywhere">
|
||
<h3 style="margin:0 0 8px;font-size:16px;line-height:1.4;color:#eee8f2">{esc(entry['title'])}</h3><p style="font-size:12px;line-height:1.6;color:#a69fac;margin:0 0 10px">{esc(details)}</p>{copy}
|
||
<a href="{esc(watch, quote=True)}" style="display:inline-block;padding:8px 0;color:#c7bdff;text-decoration:none;font-size:13px;font-weight:bold">Watch on Grizzlyflix ↗</a></td></tr></table>''')
|
||
lines += [entry['title'], details, watch, '']
|
||
if not titles:
|
||
body.append('<p style="font-size:14px;line-height:1.7;color:#bdb6c3">Your next discovery is waiting in Grizzlyflix.</p>')
|
||
period = f"{content['period_start'][:10]} to {content['period_end'][:10]} · UTC"
|
||
footer = f'You subscribed to the Grizzlyflix newsletter.<br>Arrivals recorded by Jellyfin · {esc(period)}<br><a href="{esc(unsubscribe_url, quote=True)}" style="color:#c7bdff">Unsubscribe from newsletters</a> · <a href="{esc(public_url + "/profile#newsletters", quote=True)}" style="color:#c7bdff">Email preferences</a>'
|
||
subject = ('[Test] ' if test else '') + content['subject']
|
||
return {'subject': subject, 'body_text': '\n'.join([subject, '', *lines, f'Browse Grizzlyflix: {playback_url}', '',
|
||
f'Arrivals recorded by Jellyfin: {period}', f'Unsubscribe from newsletters: {unsubscribe_url}',
|
||
f'Email preferences: {public_url}/profile#newsletters']),
|
||
'body_html': document(title='What’s new on Grizzlyflix',
|
||
intro=('This is your test edition. ' if test else '') + 'New stories for your watchlist. Find your next movie or catch up on fresh episodes.',
|
||
content=''.join(body), action='Explore Grizzlyflix', url=playback_url, footer=footer, kicker='YOUR NEXT WATCH'),
|
||
'inline_images': attachments}
|