source: Klonkt/src/views/pages/paid-gate.ejs@ b4af6f3

main
Last change on this file since b4af6f3 was b4af6f3, checked in by Robin <roboburr@…>, 2 weeks ago

Open audio travels with a paid post, and plays on the site itself

A paid post federated a bare teaser and dropped every attachment. But its
fedi_open tracks federate on their own anyway, as public Audio objects with
their context pointing back at that post. So the early return kept no secret:
it withheld only the ORDER and the fact that this is one tape. In the hub a
mixtape fell apart into loose tracks under an empty teaser card
(boiert.eu/introducing-this-machine).

The wall belongs around the TEXT. fedi_open is a separate, one-way, per-track
choice by the owner, and buildMixtapeObject already builds from
playlistOpenTracks — the tape object can only ever carry open tracks. So the
paid branch now carries them, and the redaction of the body is untouched.

The same rule on the site itself: the paid gate now shows the player when the
post's audio is open. Without it the music was available everywhere except on
the site releasing it. Only when ALL of it is open — a shortcode renders its
whole list, and /audio/stream lets a same-origin fetch through, so half open
has to count as closed. The gate gets a post that is nothing but the audio
shortcodes, so the body cannot leak through it.

Extracted openAudioAttachments() and mixtapeAttachment() out of buildNote so
both branches use one implementation.

Co-Authored-By: Claude Opus 5 <noreply@…>

  • Property mode set to 100644
File size: 3.8 KB
Line 
1<div class="container pg-navwrap">
2 <%- include('../partials/post-nav', { newerPost: (typeof newerPost !== 'undefined' ? newerPost : null), olderPost: (typeof olderPost !== 'undefined' ? olderPost : null) }) %>
3</div>
4
5<article class="pg-page" id="pg-page">
6 <% if (typeof pgTitle !== 'undefined' && pgTitle) { %><h1 class="pg-title"><%= pgTitle %></h1><% } %>
7
8 <% if (typeof pgTeaser !== 'undefined' && pgTeaser) { %>
9 <div class="pg-teaser"><p><%= pgTeaser %></p></div>
10 <% } %>
11
12 <%# De opengezette muziek staat VOOR de poort en niet erachter: wie hem in een
13 hub of in Mastodon al kan afspelen, hoort hem ook hier te kunnen afspelen.
14 Alleen de tekst zit achter de poort, en die staat hier niet -- de route
15 geeft alleen de audio-shortcodes mee (paidOpenAudioHtml). %>
16 <% if (typeof pgAudio !== 'undefined' && pgAudio) { %>
17 <div class="pg-audio post-content"><%- pgAudio %></div>
18 <% } %>
19
20 <% var _hasPatron = (typeof pgPatronUrl !== 'undefined' && pgPatronUrl); %>
21 <section class="pg-card">
22 <div class="pg-lock">💶</div>
23 <h2 class="pg-h2"><%= t('pgate.h') %></h2>
24 <p class="pg-sub">
25 <% if (typeof pgCents !== 'undefined' && pgCents) { %><%= t('pgate.sub_cents', { eur: (pgCents/100).toFixed(2) }) %><% } else { %><%= t('pgate.sub') %><% } %>
26 </p>
27
28 <div class="pg-actions">
29 <% if (_hasPatron) { %>
30 <a class="pg-btn" href="<%= pgPatronUrl %>" target="_blank" rel="noopener"><%= t('pgate.join') %></a>
31 <button type="button" id="pg-unlock" class="pg-btn pg-btn-ghost"><%= t('pgate.unlock_have') %></button>
32 <% } else { %>
33 <button type="button" id="pg-unlock" class="pg-btn"><%= t('pgate.unlock') %></button>
34 <% } %>
35 </div>
36 <p id="pg-status" class="pg-status" hidden></p>
37 </section>
38</article>
39
40<script src="/assets/vendor/simplewebauthn-browser.umd.min.js"></script>
41<%# Het script staat in assets/js/mod/paid-gate.js; de gegevens via partials/page-data.ejs (shaer-bqr). %>
42
43<style>
44 .pg-navwrap { max-width: 720px; margin: 0.5rem auto 1.5rem; padding: 0 1rem; }
45 .pg-page { max-width: 720px; margin: 0 auto 3rem; padding: 0 1rem; }
46 .pg-title { font-family: var(--font-display, serif); font-size: clamp(1.6rem, 4vw, 2.2rem); margin: 0 0 1rem; }
47 .pg-teaser { font-family: var(--font-body, serif); font-size: 1.1rem; line-height: 1.7; color: var(--ink); opacity: .95;
48 -webkit-mask-image: linear-gradient(180deg, #000 55%, transparent); mask-image: linear-gradient(180deg, #000 55%, transparent); }
49 .pg-audio { margin: 1.5rem 0 0; }
50 .pg-card { margin: 1.5rem 0 0; border: 1px solid color-mix(in srgb, var(--ink, #000) 16%, transparent); border-radius: 18px; padding: 30px 26px; text-align: center; }
51 .pg-lock { font-size: 38px; margin-bottom: 6px; }
52 .pg-h2 { font-size: 22px; margin: 0 0 8px; }
53 .pg-sub { opacity: .85; line-height: 1.6; margin: 0 auto 12px; max-width: 34em; }
54 .pg-btn { display: inline-block; text-align: center; text-decoration: none; padding: 12px 24px; border: none; border-radius: 10px; font: inherit; font-weight: 600; cursor: pointer;
55 background: var(--accent, #6b8f71); color: #fff; }
56 .pg-btn:disabled { opacity: .6; cursor: default; }
57 .pg-btn:hover { filter: brightness(1.05); }
58 .pg-actions { display: flex; flex-direction: column; gap: 10px; align-items: stretch; max-width: 320px; margin: 0 auto; }
59 .pg-btn-ghost { background: transparent; color: var(--ink, #222); border: 1px solid color-mix(in srgb, var(--ink, #000) 22%, transparent); }
60 .pg-status { margin: 12px 0 0; opacity: .9; }
61 .pg-status.is-err { color: #c0392b; }
62</style>
63<%- include('../partials/page-data', { pageData: { base: (typeof siteUrlBase !== 'undefined' && siteUrlBase ? siteUrlBase : ''), slug: pgSlug, hasPatron: !!_hasPatron, i18n: { join: t('pgate.join_short'), confirm: t('pgate.confirm'), failed: t('pgate.failed'), error: t('pgate.error') } } }) %>
Note: See TracBrowser for help on using the repository browser.