Files
mediamark/ui/static/bg-space-pirate.svg
T
unkin-agent c129cb99fc
ci/woodpecker/pr/test Pipeline failed
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful
Add the initial mediamark app
Single Go binary serving the API and an embedded keyboard-first UI for
promoting fafflix titles into the cheeztv kids tree via hardlinks.

- internal/library: hardlink sync, idempotent re-runs, drift reporting,
  strict single-path-element name validation as the traversal guard
- internal/arr: minimal sonarr/radarr v3 client with a 60s list cache and
  a key-brokered poster proxy
- internal/auth: server-side Authentik group enforcement on every route
- internal/server: library JSON API, art proxy, health probes, SPA
- ui: two-tile landing page, fuzzy-filtered title list, detail panel
- Makefile, Dockerfile, .woodpecker pipelines, pre-commit config
2026-08-29 21:27:09 +10:00

224 lines
12 KiB
XML

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 960" preserveAspectRatio="xMidYMid slice" role="img" aria-label="Cartoon pirate ship sailing a cosmic sea through outer space">
<defs>
<radialGradient id="sky" cx="50%" cy="40%" r="80%">
<stop offset="0%" stop-color="#12213f"/>
<stop offset="55%" stop-color="#0c1730"/>
<stop offset="100%" stop-color="#070d1f"/>
</radialGradient>
<linearGradient id="wave" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#3aa6d6"/>
<stop offset="100%" stop-color="#2a3f8a"/>
</linearGradient>
<radialGradient id="ring" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#9a6bd6"/>
<stop offset="100%" stop-color="#6a3fae"/>
</radialGradient>
<radialGradient id="galaxy" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#e9d6ff"/>
<stop offset="40%" stop-color="#9a72d8"/>
<stop offset="100%" stop-color="#0c1730" stop-opacity="0"/>
</radialGradient>
<radialGradient id="moon" cx="38%" cy="35%" r="70%">
<stop offset="0%" stop-color="#f2a24a"/>
<stop offset="100%" stop-color="#c56a1f"/>
</radialGradient>
<radialGradient id="teal" cx="38%" cy="35%" r="70%">
<stop offset="0%" stop-color="#5fd6c0"/>
<stop offset="100%" stop-color="#2a9c86"/>
</radialGradient>
<linearGradient id="comet" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#ff8a2a" stop-opacity="0"/>
<stop offset="100%" stop-color="#ffd24a"/>
</linearGradient>
<linearGradient id="hull" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#a9743a"/>
<stop offset="100%" stop-color="#6e451d"/>
</linearGradient>
</defs>
<!-- space backdrop -->
<rect width="1280" height="960" fill="url(#sky)"/>
<!-- stars + sparkles -->
<g fill="#ffffff">
<circle cx="90" cy="80" r="2.4"/><circle cx="180" cy="150" r="1.6"/><circle cx="260" cy="60" r="2"/>
<circle cx="360" cy="120" r="1.4"/><circle cx="470" cy="70" r="2.2"/><circle cx="560" cy="140" r="1.5"/>
<circle cx="700" cy="90" r="1.8"/><circle cx="820" cy="150" r="2"/><circle cx="930" cy="70" r="1.5"/>
<circle cx="1040" cy="130" r="2.2"/><circle cx="1150" cy="80" r="1.6"/><circle cx="1220" cy="180" r="2"/>
<circle cx="60" cy="300" r="1.7"/><circle cx="150" cy="420" r="2.1"/><circle cx="40" cy="520" r="1.5"/>
<circle cx="110" cy="640" r="2"/><circle cx="1230" cy="360" r="1.8"/><circle cx="1180" cy="470" r="2.2"/>
<circle cx="1250" cy="600" r="1.6"/><circle cx="1200" cy="720" r="2"/><circle cx="640" cy="40" r="1.4"/>
<circle cx="380" cy="300" r="1.5"/><circle cx="900" cy="320" r="1.6"/><circle cx="1080" cy="260" r="1.5"/>
</g>
<g fill="#ffd24a">
<path d="M240 240 l4 10 10 4 -10 4 -4 10 -4 -10 -10 -4 10 -4z"/>
<path d="M1000 500 l4 10 10 4 -10 4 -4 10 -4 -10 -10 -4 10 -4z"/>
<path d="M470 470 l3 8 8 3 -8 3 -3 8 -3 -8 -8 -3 8 -3z"/>
<path d="M760 250 l3 8 8 3 -8 3 -3 8 -3 -8 -8 -3 8 -3z"/>
</g>
<!-- purple ringed planet (top-left) -->
<g transform="translate(150,180)">
<ellipse cx="0" cy="0" rx="120" ry="34" fill="none" stroke="#d7a24a" stroke-width="10" transform="rotate(-20)"/>
<circle cx="0" cy="0" r="62" fill="url(#ring)" stroke="#0a1226" stroke-width="4"/>
<ellipse cx="0" cy="0" rx="120" ry="34" fill="none" stroke="#d7a24a" stroke-width="10" transform="rotate(-20)" stroke-dasharray="150 400" stroke-dashoffset="150"/>
</g>
<!-- spiral galaxy (top-right) -->
<g transform="translate(1080,190) rotate(25)">
<ellipse cx="0" cy="0" rx="130" ry="70" fill="url(#galaxy)"/>
<path d="M0 0 Q60 -30 90 20 Q40 40 0 0" fill="#c9a8ef" opacity="0.7"/>
<path d="M0 0 Q-60 30 -90 -20 Q-40 -40 0 0" fill="#c9a8ef" opacity="0.7"/>
<circle cx="0" cy="0" r="10" fill="#ffffff"/>
</g>
<!-- teal planet (right) -->
<circle cx="1180" cy="360" r="46" fill="url(#teal)" stroke="#0a1226" stroke-width="4"/>
<ellipse cx="1165" cy="345" rx="16" ry="8" fill="#7fe6d2" opacity="0.6"/>
<!-- orange cratered moon (bottom-right) -->
<g transform="translate(1130,800)">
<circle r="90" fill="url(#moon)" stroke="#0a1226" stroke-width="5"/>
<circle cx="-30" cy="-20" r="16" fill="#a85417" opacity="0.6"/>
<circle cx="20" cy="10" r="22" fill="#a85417" opacity="0.5"/>
<circle cx="-10" cy="40" r="12" fill="#a85417" opacity="0.6"/>
<circle cx="40" cy="-30" r="9" fill="#a85417" opacity="0.6"/>
</g>
<!-- blue asteroid/planet (bottom-left) -->
<circle cx="90" cy="770" r="70" fill="#3f6fae" stroke="#0a1226" stroke-width="5"/>
<circle cx="65" cy="745" r="14" fill="#5f8fce" opacity="0.6"/>
<circle cx="110" cy="800" r="10" fill="#2f579a" opacity="0.6"/>
<!-- flaming comet (left) -->
<g transform="translate(210,360)">
<path d="M0 0 L-150 -40 L-150 40 Z" fill="url(#comet)"/>
<circle r="22" fill="#ffce54" stroke="#e07b1f" stroke-width="4"/>
<path d="M-6 -10 q10 -14 20 0 q10 -12 16 4 q-8 8 -20 6 q-14 4 -16 -10z" fill="#ff7a2a"/>
</g>
<!-- cartoon rocket (right, angled up) -->
<g transform="translate(1000,430) rotate(-30)">
<path d="M0 -46 q22 22 22 60 h-44 q0 -38 22 -60z" fill="#eef2f7" stroke="#0a1226" stroke-width="4"/>
<circle cx="0" cy="0" r="10" fill="#7fd0ff" stroke="#0a1226" stroke-width="3"/>
<path d="M-22 14 l-18 22 18 -4z" fill="#d64a3a" stroke="#0a1226" stroke-width="3"/>
<path d="M22 14 l18 22 -18 -4z" fill="#d64a3a" stroke="#0a1226" stroke-width="3"/>
<path d="M-10 20 q10 26 20 0z" fill="#ff9a2a"/>
<path d="M-6 24 q6 16 12 0z" fill="#ffd24a"/>
</g>
<!-- scattered asteroids -->
<g fill="#8a8f9c" stroke="#0a1226" stroke-width="3">
<path d="M330 640 q-18 -10 -6 -26 q16 -12 30 -2 q16 8 8 26 q-10 16 -32 2z"/>
<path d="M980 700 q-14 -8 -4 -22 q12 -10 24 -2 q12 6 6 22 q-8 12 -26 2z"/>
<path d="M640 820 q-16 -8 -6 -24 q14 -10 28 -2 q14 8 6 24 q-10 14 -28 2z"/>
</g>
<!-- ===== cosmic sea wave ===== -->
<path d="M0 720
q120 -60 240 -20 q120 40 240 0 q120 -40 240 0 q120 40 240 -10 q120 -50 240 0
q40 20 80 10 L1280 960 L0 960 Z"
fill="url(#wave)" stroke="#1a2c66" stroke-width="4"/>
<g fill="#bfe9ff" opacity="0.85">
<path d="M120 720 q30 -26 60 0 q-30 18 -60 0z"/>
<path d="M420 706 q28 -24 56 0 q-28 18 -56 0z"/>
<path d="M760 712 q30 -26 60 0 q-30 18 -60 0z"/>
<path d="M1040 700 q28 -24 56 0 q-28 18 -56 0z"/>
</g>
<!-- foam swirl -->
<path d="M0 760 q160 40 320 10 q160 -30 320 6 q160 36 320 0 q160 -36 320 8" fill="none" stroke="#dff3ff" stroke-width="6" stroke-linecap="round" opacity="0.8"/>
<!-- ===== pirate ship ===== -->
<g transform="translate(640,560)">
<!-- main mast + black skull sail -->
<rect x="-6" y="-260" width="12" height="230" fill="#7a5228" stroke="#0a1226" stroke-width="3"/>
<!-- pennant flag -->
<path d="M6 -258 l70 16 -70 16z" fill="#c0392b" stroke="#0a1226" stroke-width="3"/>
<!-- top small skull flag -->
<g transform="translate(0,-250)">
<rect x="-2" y="-8" width="4" height="10" fill="#7a5228"/>
</g>
<!-- big black sail -->
<path d="M-150 -230 q150 30 300 0 q-30 130 0 200 q-150 30 -300 0 q30 -110 0 -200z"
fill="#151515" stroke="#0a1226" stroke-width="4"/>
<!-- skull and crossbones -->
<g transform="translate(0,-120)" fill="#f5f5f5">
<path d="M-60 0 q60 -14 120 0 M-60 20 q60 14 120 0" stroke="#f5f5f5" stroke-width="12" fill="none" stroke-linecap="round"/>
<ellipse cx="0" cy="-8" rx="44" ry="40"/>
<path d="M-30 24 q30 18 60 0 l-6 16 -12 -6 -6 10 -6 -10 -12 6z"/>
<circle cx="-16" cy="-8" r="12" fill="#151515"/>
<circle cx="16" cy="-8" r="12" fill="#151515"/>
<path d="M-4 6 l4 -10 4 10z" fill="#151515"/>
</g>
<!-- hull -->
<path d="M-200 20 q0 90 200 90 q200 0 200 -90 q-30 26 -200 26 q-170 0 -200 -26z"
fill="url(#hull)" stroke="#0a1226" stroke-width="5"/>
<!-- deck trim -->
<rect x="-200" y="8" width="400" height="18" rx="8" fill="#c69a5a" stroke="#0a1226" stroke-width="4"/>
<!-- portholes -->
<g fill="#3a2410" stroke="#0a1226" stroke-width="3">
<circle cx="-120" cy="66" r="12"/><circle cx="-60" cy="76" r="12"/>
<circle cx="0" cy="80" r="12"/><circle cx="60" cy="76" r="12"/><circle cx="120" cy="66" r="12"/>
</g>
<!-- crow's nest with telescope pirate -->
<g transform="translate(150,-190)">
<path d="M-22 0 h44 l-6 26 h-32z" fill="#8a5f2c" stroke="#0a1226" stroke-width="3"/>
<circle cx="0" cy="-10" r="12" fill="#f0c39a" stroke="#0a1226" stroke-width="2"/>
<path d="M-12 -14 q12 -10 24 0 l-2 -10 -22 0z" fill="#c0392b"/>
<rect x="6" y="-16" width="26" height="6" rx="3" fill="#3a2410" transform="rotate(-20 6 -16)" stroke="#0a1226" stroke-width="2"/>
</g>
<!-- captain (center, cutlass) -->
<g transform="translate(0,-30)">
<path d="M-18 0 q18 -8 36 0 l-4 34 h-28z" fill="#c0392b" stroke="#0a1226" stroke-width="3"/>
<circle cx="0" cy="-16" r="17" fill="#f0c39a" stroke="#0a1226" stroke-width="3"/>
<path d="M-20 -22 q20 -18 40 0 q-6 -14 -20 -14 q-14 0 -20 14z" fill="#1b1b1b"/>
<path d="M-8 -20 h6" stroke="#1b1b1b" stroke-width="3"/>
<path d="M4 -20 h6" stroke="#1b1b1b" stroke-width="3"/>
<path d="M-4 -8 q4 4 8 0" stroke="#7a3b1b" stroke-width="3" fill="none"/>
<!-- cutlass -->
<path d="M18 -6 q40 -30 60 -50" stroke="#d9dde3" stroke-width="6" fill="none" stroke-linecap="round"/>
<rect x="14" y="-10" width="10" height="14" rx="3" fill="#c9a25a" stroke="#0a1226" stroke-width="2"/>
</g>
<!-- pirate with telescope (left, on deck) -->
<g transform="translate(-110,-24)">
<path d="M-16 0 q16 -6 32 0 l-4 26 h-24z" fill="#2f6fae" stroke="#0a1226" stroke-width="3"/>
<circle cx="0" cy="-14" r="14" fill="#f0c39a" stroke="#0a1226" stroke-width="3"/>
<path d="M-16 -18 q16 -8 30 -2" stroke="#c0392b" stroke-width="8" fill="none" stroke-linecap="round"/>
<rect x="8" y="-18" width="30" height="7" rx="3" fill="#3a2410" transform="rotate(-15 8 -18)" stroke="#0a1226" stroke-width="2"/>
</g>
<!-- third pirate (right) -->
<g transform="translate(80,-22)">
<path d="M-14 0 q14 -6 28 0 l-4 24 h-20z" fill="#6a4aa0" stroke="#0a1226" stroke-width="3"/>
<circle cx="0" cy="-14" r="13" fill="#f0c39a" stroke="#0a1226" stroke-width="3"/>
<path d="M-14 -18 q14 -8 28 0 l-2 -9 -24 0z" fill="#c0392b"/>
</g>
<!-- parrot on the rail -->
<g transform="translate(120,-58)">
<ellipse cx="0" cy="0" rx="12" ry="16" fill="#2ecc71" stroke="#0a1226" stroke-width="3"/>
<circle cx="0" cy="-14" r="8" fill="#2ecc71" stroke="#0a1226" stroke-width="3"/>
<path d="M6 -16 l12 4 -12 4z" fill="#f1c40f" stroke="#0a1226" stroke-width="2"/>
<circle cx="1" cy="-16" r="2" fill="#0a1226"/>
<path d="M-6 4 q-14 6 -18 20 q10 -6 20 -12z" fill="#e74c3c" stroke="#0a1226" stroke-width="2"/>
<path d="M-4 -2 q-16 4 -20 16 q10 -4 20 -8z" fill="#f39c12" stroke="#0a1226" stroke-width="2"/>
</g>
</g>
<!-- goofy fish/creature in the water -->
<g transform="translate(830,700)">
<ellipse cx="0" cy="0" rx="46" ry="30" fill="#e8b84a" stroke="#0a1226" stroke-width="4"/>
<path d="M40 0 l34 -22 v44z" fill="#e8b84a" stroke="#0a1226" stroke-width="4"/>
<circle cx="-18" cy="-8" r="12" fill="#ffffff" stroke="#0a1226" stroke-width="3"/>
<circle cx="6" cy="-8" r="12" fill="#ffffff" stroke="#0a1226" stroke-width="3"/>
<circle cx="-16" cy="-6" r="5" fill="#0a1226"/>
<circle cx="8" cy="-6" r="5" fill="#0a1226"/>
<path d="M-30 14 q18 16 44 0" stroke="#0a1226" stroke-width="4" fill="none" stroke-linecap="round"/>
<path d="M-2 14 q6 22 14 8z" fill="#c0392b" stroke="#0a1226" stroke-width="2"/>
</g>
</svg>