Files
pivoine.art/layouts/tracks/single.html
Sebastian Krüger 3bccbdf82d feat: serve audio as local Hugo page resources instead of Jellyfin
Download all 20 track MP3s from Jellyfin and store them as page bundle
resources (track.mp3). Update templates to use .Resources.GetMatch
instead of .Params.audio, removing external API key dependencies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 22:51:30 +01:00

143 lines
4.8 KiB
HTML
Executable File

{{ define "main" }}
{{- $audio := "" -}}
{{- with .Resources.GetMatch "track.*" -}}
{{- $audio = .RelPermalink -}}
{{- end -}}
<article class="py-16">
<div class="container-wide">
{{/* Content area with offset */}}
<div class="md:mx-72">
{{/* Header */}}
<header class="mb-8">
{{/* Meta */}}
<div class="flex items-center gap-4 text-sm text-text-muted mb-3">
<time datetime="{{ .Date.Format "2006-01-02" }}">
{{ .Date.Format "January 2, 2006" }}
</time>
{{- with .Params.genre }}
<span class="px-2 py-1 bg-surface-2 rounded">{{ . }}</span>
{{- end }}
</div>
<h1 class="text-3xl md:text-4xl font-medium tracking-tight mb-2">
{{ .Title }}
</h1>
{{- with .Description }}
<p class="text-text-secondary">{{ . }}</p>
{{- end }}
</header>
{{/* Cover Image + Play Widget - 2 Column Layout */}}
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-12">
{{/* Cover Image - 1 column */}}
{{- with .Resources.GetMatch "cover.*" }}
{{- $img := .Resize "400x webp q90" }}
<figure
class="overflow-hidden rounded-lg group cursor-pointer relative aspect-square border border-border"
{{- if $audio }}
x-data
@click="
$store.audio.currentTrack = {
title: '{{ $.Title }}',
url: '{{ $audio }}',
image: '{{ with $.Resources.GetMatch "cover.*" }}{{ (.Resize "200x webp q85").RelPermalink }}{{ end }}'
};
window.__pivoine?.audioManager?.play('{{ $audio }}');
$store.audio.isPlaying = true;
"
{{- end }}
>
<img
src="{{ $img.RelPermalink }}"
alt="{{ $.Title }}"
class="w-full h-full object-cover grayscale group-hover:grayscale-0 transition-all duration-500"
loading="eager"
>
{{/* Video preview on hover */}}
{{- with $.Resources.GetMatch "preview.*" }}
<video
src="{{ .RelPermalink }}"
class="absolute inset-0 w-full h-full object-cover opacity-0 group-hover:opacity-100 transition-opacity duration-300"
muted
loop
playsinline
onmouseenter="this.play()"
onmouseleave="this.pause(); this.currentTime=0;"
></video>
{{- end }}
</figure>
{{- end }}
{{/* Play Widget - 2 columns */}}
{{- if $audio }}
<div
x-data
@click="
$store.audio.currentTrack = {
title: '{{ .Title }}',
url: '{{ $audio }}',
image: '{{ with .Resources.GetMatch "cover.*" }}{{ (.Resize "200x webp q85").RelPermalink }}{{ end }}'
};
window.__pivoine?.audioManager?.play('{{ $audio }}');
$store.audio.isPlaying = true;
"
class="md:col-span-2 flex items-center gap-4 cursor-pointer group"
>
<button
class="w-14 h-14 flex-shrink-0 flex items-center justify-center rounded-full bg-accent text-surface-0 group-hover:scale-110 transition-transform"
aria-label="Play {{ .Title }}"
>
<svg class="w-6 h-6 ml-0.5" fill="currentColor" viewBox="0 0 24 24">
<path d="M8 5v14l11-7z"/>
</svg>
</button>
<div>
<p class="font-medium group-hover:text-accent transition-colors">Play Track</p>
{{- with .Params.duration }}
<p class="text-sm text-text-muted tabular-nums">{{ . }}</p>
{{- end }}
</div>
</div>
{{- end }}
</div>
{{/* Content */}}
{{- if .Content }}
<div class="prose prose-invert prose-lg max-w-none">
{{ .Content }}
</div>
{{- end }}
{{/* Tags */}}
{{- with .GetTerms "tags" }}
<footer class="mt-12 pt-8 border-t border-border">
<div class="flex flex-wrap gap-2">
{{- range . }}
<a
href="{{ .Permalink }}"
class="px-3 py-1 text-sm bg-surface-2 hover:bg-surface-3 transition-colors rounded"
>
{{ .LinkTitle }}
</a>
{{- end }}
</div>
</footer>
{{- end }}
{{/* Related Tracks */}}
{{- $related := .Site.RegularPages.Related . | first 3 }}
{{- if $related }}
<aside class="mt-16 pt-12 border-t border-border">
<h2 class="text-xl font-medium mb-8">Related Tracks</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
{{- range $related }}
{{ partial "track-card.html" . }}
{{- end }}
</div>
</aside>
{{- end }}
</div>
</div>
</article>
{{ end }}