Files
Assclaw b0f8c89db7
Magent CI/CD / verify (push) Successful in 10m54s
Magent CI/CD / deploy-prod (push) Skipped
Magent CI/CD / deploy-beta (push) Successful in 1m14s
Add Grizzlyflix newsletters with curated editions and weekly delivery
2026-09-09 23:42:52 +12:00

75 lines
5.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 &#8599;</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='Whats 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}