Compare commits

1 Commits

Author SHA1 Message Date
f34b81700f Dateien nach "content/tracks" hochladen 2025-12-29 12:31:47 +01:00
22 changed files with 25 additions and 181 deletions

View File

@@ -13,8 +13,7 @@
"WebSearch", "WebSearch",
"WebFetch(domain:htmx.org)", "WebFetch(domain:htmx.org)",
"Bash(cat:*)", "Bash(cat:*)",
"Bash(git remote:*)", "Bash(git remote:*)"
"Bash(ls:*)"
], ],
"deny": [], "deny": [],
"ask": [] "ask": []

3
.gitignore vendored
View File

@@ -30,6 +30,3 @@ npm-debug.log*
# Cache # Cache
.cache/ .cache/
# Claude
.claude/

View File

@@ -16,53 +16,6 @@ class AudioManager {
this.source = null; this.source = null;
this.frequencyData = null; this.frequencyData = null;
this.isInitialized = false; this.isInitialized = false;
this.tracks = [];
this.autoplayEnabled = true;
}
async fetchTracks() {
if (this.tracks.length > 0) return this.tracks;
try {
const response = await fetch('/tracks/index.json');
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
this.tracks = data.tracks || [];
} catch (e) {
console.error('Failed to fetch tracks:', e);
this.tracks = [];
}
return this.tracks;
}
getRandomTrack(excludeUrl = null) {
const available = this.tracks.filter((t) => t.audio !== excludeUrl);
if (available.length === 0) return null;
return available[Math.floor(Math.random() * available.length)];
}
async playRandomTrack() {
await this.fetchTracks();
const currentUrl = window.Alpine?.store('audio')?.currentTrack?.url;
const nextTrack = this.getRandomTrack(currentUrl);
if (nextTrack) {
// Store pending track info for auto-play on page load
sessionStorage.setItem(
'pivoine-autoplay',
JSON.stringify({
title: nextTrack.title,
url: nextTrack.audio,
image: nextTrack.image
})
);
// Navigate to the new track page
window.location.href = nextTrack.url;
}
} }
async init() { async init() {
@@ -93,10 +46,6 @@ class AudioManager {
if (window.Alpine) { if (window.Alpine) {
Alpine.store('audio').isPlaying = false; Alpine.store('audio').isPlaying = false;
} }
// Auto-play next random track
if (this.autoplayEnabled) {
this.playRandomTrack();
}
}); });
this.audio.addEventListener('play', () => { this.audio.addEventListener('play', () => {
@@ -186,32 +135,6 @@ if (!window.__pivoine) {
logo: null logo: null
}; };
// Check for auto-play from shuffle/random track
const checkAutoplay = () => {
const autoplayData = sessionStorage.getItem('pivoine-autoplay');
if (autoplayData) {
sessionStorage.removeItem('pivoine-autoplay');
try {
const track = JSON.parse(autoplayData);
// Update Alpine store
if (window.Alpine) {
Alpine.store('audio').currentTrack = track;
}
// Start playback
audioManager.play(track.url);
} catch (e) {
console.error('Failed to auto-play track:', e);
}
}
};
// Run autoplay check after Alpine is ready
document.addEventListener('alpine:initialized', checkAutoplay);
// Fallback if Alpine is already initialized
if (window.Alpine) {
setTimeout(checkAutoplay, 100);
}
// Initialize WebGL components after DOM is ready // Initialize WebGL components after DOM is ready
const initWebGL = () => { const initWebGL = () => {
// Main visualizer (fullscreen background) // Main visualizer (fullscreen background)

View File

@@ -2,9 +2,6 @@ baseURL = "https://pivoine.art/"
languageCode = "en-us" languageCode = "en-us"
title = "Valknar's" title = "Valknar's"
[pagination]
pagerSize = 12
[permalinks] [permalinks]
tracks = "/tracks/:slug/" tracks = "/tracks/:slug/"
@@ -14,7 +11,7 @@ title = "Valknar's"
[outputs] [outputs]
home = ["HTML", "RSS"] home = ["HTML", "RSS"]
section = ["HTML", "RSS", "JSON"] section = ["HTML", "RSS"]
[sitemap] [sitemap]
changefreq = "weekly" changefreq = "weekly"

View File

@@ -7,9 +7,9 @@ description: "About Valknar and his music"
Technology and sound, creating massive beats to push the boundaries of audio perception. Technology and sound, creating massive beats to push the boundaries of audio perception.
### Dedication ### Equipment
The love of my life, Palina. Debian GNU/Linux 13
### Contact ### Contact

Binary file not shown.

View File

@@ -1,7 +1,7 @@
--- ---
title: "Bunker" title: "Bunker"
date: 2025-11-30 date: 2025-11-30
draft: false draft: true
description: "Djane" description: "Djane"
audio: "https://jellyfin.media.pivoine.art/Items/5e09032deaaeb222b4de117b2e0233af/Download?api_key=64d0a008577f49a4aa276d4bbe5c5d60" audio: "https://jellyfin.media.pivoine.art/Items/5e09032deaaeb222b4de117b2e0233af/Download?api_key=64d0a008577f49a4aa276d4bbe5c5d60"

View File

@@ -1,7 +1,7 @@
--- ---
title: "Changed Her Mind Again" title: "Changed Her Mind Again"
date: 2025-09-10 date: 2025-09-10
draft: false draft: true
description: "Again..." description: "Again..."
audio: "https://jellyfin.media.pivoine.art/Items/ebd4e9f45b9dda1cada560af5e6cb7a8/Download?api_key=64d0a008577f49a4aa276d4bbe5c5d60" audio: "https://jellyfin.media.pivoine.art/Items/ebd4e9f45b9dda1cada560af5e6cb7a8/Download?api_key=64d0a008577f49a4aa276d4bbe5c5d60"

View File

@@ -1,7 +1,7 @@
--- ---
title: "Latex" title: "Latex"
date: 2025-11-30 date: 2025-11-30
draft: false draft: true
description: "Posing" description: "Posing"
audio: "https://jellyfin.media.pivoine.art/Items/ed71ecad292dc60ea3475cf9029974c3/Download?api_key=64d0a008577f49a4aa276d4bbe5c5d60" audio: "https://jellyfin.media.pivoine.art/Items/ed71ecad292dc60ea3475cf9029974c3/Download?api_key=64d0a008577f49a4aa276d4bbe5c5d60"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

View File

@@ -1,17 +0,0 @@
---
title: "Listen To Your Heart"
date: 2025-12-31
draft: false
description: "Roxette"
audio: "https://jellyfin.media.pivoine.art/Items/7665d067afb622eef70db5fa65ab8829/Download?api_key=8db4f88966fd4feb9308dfff68e9eeea"
duration: "4:23"
artist: "Valknar"
genre: "Drum'n'Bass"
tags:
- liquid
- funk
- soul
---

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

View File

@@ -1,17 +0,0 @@
---
title: "Paule"
date: 2025-12-31
draft: false
description: "Du Bisch"
audio: "https://jellyfin.media.pivoine.art/Items/f1050f982226566de6ef11f4f2edbf33/Download?api_key=8db4f88966fd4feb9308dfff68e9eeea"
duration: "0:31"
artist: "Valknar"
genre: "HipHop"
tags:
- patriotism
- funk
- superior
---

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

View File

@@ -1,17 +0,0 @@
---
title: "Rome"
date: 2025-12-31
draft: false
description: "Honor Et Virtus"
audio: "https://jellyfin.media.pivoine.art/Items/9f4b3b6e523b3488c850e2d50fbe157f/Download?api_key=8db4f88966fd4feb9308dfff68e9eeea"
duration: "3:57"
artist: "Valknar"
genre: "Metal"
tags:
- metal
- strength
- honor
---

View File

@@ -1,7 +1,7 @@
--- ---
title: "The End Of All" title: "The End Of All"
date: 2025-11-16 date: 2025-11-16
draft: false draft: true
description: "The end of all is just the beginning" description: "The end of all is just the beginning"
audio: "https://jellyfin.media.pivoine.art/Items/60d39ab0aad880627e8fb85cf1ee7b40/Download?api_key=64d0a008577f49a4aa276d4bbe5c5d60" audio: "https://jellyfin.media.pivoine.art/Items/60d39ab0aad880627e8fb85cf1ee7b40/Download?api_key=64d0a008577f49a4aa276d4bbe5c5d60"

View File

@@ -1,7 +1,7 @@
--- ---
title: "The Moon" title: "The Moon"
date: 2025-08-19 date: 2025-08-19
draft: false draft: true
description: "Because we are the last" description: "Because we are the last"
audio: "https://jellyfin.media.pivoine.art/Items/d91333f9c7c4d8251174c86a81588cbd/Download?api_key=64d0a008577f49a4aa276d4bbe5c5d60" audio: "https://jellyfin.media.pivoine.art/Items/d91333f9c7c4d8251174c86a81588cbd/Download?api_key=64d0a008577f49a4aa276d4bbe5c5d60"

View File

@@ -1,12 +1,5 @@
{{/* Track Card Component */}} {{/* Track Card Component */}}
{{- $hasVideo := .Resources.GetMatch "preview.*" -}} <article class="track-card rounded-lg overflow-hidden group grayscale hover:grayscale-0">
<article
class="track-card rounded-lg overflow-hidden group transition-all duration-300"
:class="hovering ? '' : 'grayscale'"
x-data="{ hovering: false }"
@mouseenter="hovering = true; $refs.video?.play().catch(() => {})"
@mouseleave="hovering = false; if ($refs.video) { $refs.video.pause(); $refs.video.currentTime = 0; }"
>
{{/* Cover Image/Video */}} {{/* Cover Image/Video */}}
<a href="{{ .Permalink }}" class="block relative aspect-square overflow-hidden"> <a href="{{ .Permalink }}" class="block relative aspect-square overflow-hidden">
{{- with .Resources.GetMatch "cover.*" }} {{- with .Resources.GetMatch "cover.*" }}
@@ -14,8 +7,7 @@
<img <img
src="{{ $img.RelPermalink }}" src="{{ $img.RelPermalink }}"
alt="{{ $.Title }}" alt="{{ $.Title }}"
class="track-card__cover w-full h-full object-cover" class="track-card__cover w-full h-full"
:class="{ 'opacity-0': hovering && $refs.video }"
loading="lazy" loading="lazy"
decoding="async" decoding="async"
> >
@@ -30,19 +22,18 @@
{{/* Video preview on hover */}} {{/* Video preview on hover */}}
{{- with .Resources.GetMatch "preview.*" }} {{- with .Resources.GetMatch "preview.*" }}
<video <video
x-ref="video"
src="{{ .RelPermalink }}" src="{{ .RelPermalink }}"
class="absolute inset-0 w-full h-full object-cover transition-opacity duration-300" class="absolute inset-0 w-full h-full object-cover opacity-0 group-hover:opacity-100 transition-opacity duration-300"
:class="hovering ? 'opacity-100' : 'opacity-0'"
muted muted
loop loop
playsinline playsinline
preload="metadata" onmouseenter="this.play()"
onmouseleave="this.pause(); this.currentTime=0;"
></video> ></video>
{{- end }} {{- end }}
{{/* Play overlay - only show if NO video exists */}} {{/* Play overlay - hide if video exists */}}
{{- if not $hasVideo }} {{- if not (.Resources.GetMatch "preview.*") }}
<div class="absolute inset-0 bg-surface-0/60 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center"> <div class="absolute inset-0 bg-surface-0/60 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center">
<div class="w-14 h-14 rounded-full bg-accent flex items-center justify-center transform scale-90 group-hover:scale-100 transition-transform"> <div class="w-14 h-14 rounded-full bg-accent flex items-center justify-center transform scale-90 group-hover:scale-100 transition-transform">
<svg class="w-6 h-6 text-surface-0 ml-1" fill="currentColor" viewBox="0 0 24 24"> <svg class="w-6 h-6 text-surface-0 ml-1" fill="currentColor" viewBox="0 0 24 24">

View File

@@ -6,7 +6,7 @@
<p class="text-text-secondary mt-2">{{ .Description | default "All audio experiments" }}</p> <p class="text-text-secondary mt-2">{{ .Description | default "All audio experiments" }}</p>
</header> </header>
{{- $tracks := .Paginator.Pages -}} {{- $tracks := .Pages -}}
{{- if $tracks }} {{- if $tracks }}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6"> <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{{- range $tracks }} {{- range $tracks }}

View File

@@ -1,18 +0,0 @@
{{- $tracks := slice -}}
{{- range .Pages -}}
{{- $track := dict
"title" .Title
"url" .Permalink
"slug" .File.ContentBaseName
"audio" .Params.audio
"duration" .Params.duration
"genre" .Params.genre
"image" ""
-}}
{{- with .Resources.GetMatch "cover.*" -}}
{{- $img := .Resize "200x webp q85" -}}
{{- $track = merge $track (dict "image" $img.RelPermalink) -}}
{{- end -}}
{{- $tracks = $tracks | append $track -}}
{{- end -}}
{{- dict "tracks" $tracks | jsonify (dict "indent" " ") -}}

View File

@@ -1,2 +0,0 @@
onlyBuiltDependencies:
- '@parcel/watcher'

View File

@@ -907,6 +907,14 @@
} }
} }
} }
.hover\:grayscale-0 {
&:hover {
@media (hover: hover) {
--tw-grayscale: grayscale(0%);
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
}
}
}
.hover\:after\:w-full { .hover\:after\:w-full {
&:hover { &:hover {
@media (hover: hover) { @media (hover: hover) {