From fe647b8749874bde5102988b7942b4f2202f01ef Mon Sep 17 00:00:00 2001 From: sakrecoer Date: Sun, 16 Aug 2026 11:17:10 +0200 Subject: [PATCH] add playlist partial --- fetch-playlist.sh | 214 ++++++++++++++++++ themes/one-pager/assets/css/style.css | 184 +++++++++++++++ .../one-pager/layouts/partials/playlists.html | 90 ++++++++ 3 files changed, 488 insertions(+) create mode 100755 fetch-playlist.sh create mode 100644 themes/one-pager/layouts/partials/playlists.html diff --git a/fetch-playlist.sh b/fetch-playlist.sh new file mode 100755 index 0000000..d71dbed --- /dev/null +++ b/fetch-playlist.sh @@ -0,0 +1,214 @@ +#!/bin/bash + +# ============================================ +# Nostr Playlist Fetcher for Hugo +# ============================================ + +# Configuration +PUBKEY="3f11abb2e235da2d4dda5d6deb2f123173476a745a3ca56895a1d0f632a42f40" +RELAY="wss://drops.basspistol.org" +DATA_DIR="data/playlists" +NAK_BIN="nak" +JQ_BIN="jq" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +mkdir -p "$DATA_DIR" + +log() { echo -e "${BLUE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1"; } +log_success() { echo -e "${GREEN}✅ $1${NC}"; } +log_warning() { echo -e "${YELLOW}⚠️ $1${NC}"; } +log_error() { echo -e "${RED}❌ $1${NC}"; } + +# ============================================ +# Step 1: Fetch playlist events +# ============================================ + +log "Fetching playlist events (kind 34139) from $RELAY..." + +PLAYLISTS_JSON=$(mktemp) +$NAK_BIN req -k 34139 -a "$PUBKEY" "$RELAY" | $JQ_BIN -s '.' > "$PLAYLISTS_JSON" + +if [ ! -s "$PLAYLISTS_JSON" ]; then + log_error "No playlist data received." + rm -f "$PLAYLISTS_JSON" + exit 1 +fi + +PLAYLIST_COUNT=$(cat "$PLAYLISTS_JSON" | $JQ_BIN 'length') +log_success "Found $PLAYLIST_COUNT playlist(s)" + +# ============================================ +# Step 2: Fetch ALL tracks +# ============================================ + +log "Fetching ALL tracks (kind 36787) from $PUBKEY..." + +ALL_TRACKS_JSON=$(mktemp) +$NAK_BIN req -k 36787 -a "$PUBKEY" "$RELAY" | $JQ_BIN -s '.' > "$ALL_TRACKS_JSON" + +TRACK_COUNT=$(cat "$ALL_TRACKS_JSON" | $JQ_BIN 'length' 2>/dev/null || echo "0") +log_success "Found $TRACK_COUNT total tracks from pubkey" + +# ============================================ +# Step 3: Build playlist data +# ============================================ + +log "Building playlist data structure..." + +OUTPUT_FILE="$DATA_DIR/playlists.json" +FLATTENED_OUTPUT="$DATA_DIR/tracks.json" + +# Create a file with track lookup by d-tag +TRACK_LOOKUP=$(mktemp) +cat "$ALL_TRACKS_JSON" | $JQ_BIN -r ' + .[] | + . as $track | + (.tags[] | select(.[0] == "d") | .[1]) as $d_tag | + "\($d_tag)|\($track | tojson)" +' > "$TRACK_LOOKUP" + +# Build the playlists JSON +echo '{"playlists": []}' > "$OUTPUT_FILE" + +# Process each playlist +cat "$PLAYLISTS_JSON" | $JQ_BIN -c '.[]' | while read -r playlist; do + # Extract playlist metadata + PLAYLIST_ID=$(echo "$playlist" | $JQ_BIN -r '.id') + PLAYLIST_CREATED=$(echo "$playlist" | $JQ_BIN -r '.created_at') + + # FIX: Extract name from "title" tag (not "name") + PLAYLIST_NAME=$(echo "$playlist" | $JQ_BIN -r '.tags[] | select(.[0] == "title") | .[1] // ""') + PLAYLIST_DESC=$(echo "$playlist" | $JQ_BIN -r '.tags[] | select(.[0] == "description") | .[1] // ""') + PLAYLIST_IMAGE=$(echo "$playlist" | $JQ_BIN -r '.tags[] | select(.[0] == "image") | .[1] // ""') + + # Debug: show what was extracted + echo " Playlist: '$PLAYLIST_NAME'" >&2 + + # Get track references from 'a' tags + TRACK_REFS=$(echo "$playlist" | $JQ_BIN -r '.tags[] | select(.[0] == "a") | .[1]') + + # Build tracks array for this playlist + TRACKS_JSON="[]" + + while read -r ref; do + if [ -n "$ref" ]; then + # Extract identifier (the part after the second colon) + IDENTIFIER=$(echo "$ref" | cut -d':' -f3-) + + # Find the track in the lookup + TRACK_DATA=$(grep "^$IDENTIFIER|" "$TRACK_LOOKUP" | head -1 | cut -d'|' -f2-) + + if [ -n "$TRACK_DATA" ] && [ "$TRACK_DATA" != "null" ]; then + # Extract track fields + TRACK_TITLE=$(echo "$TRACK_DATA" | $JQ_BIN -r '.tags[] | select(.[0] == "title") | .[1] // "Untitled"') + TRACK_ARTIST=$(echo "$TRACK_DATA" | $JQ_BIN -r '.tags[] | select(.[0] == "artist") | .[1] // "Unknown Artist"') + TRACK_ALBUM=$(echo "$TRACK_DATA" | $JQ_BIN -r '.tags[] | select(.[0] == "album") | .[1] // ""') + TRACK_URL=$(echo "$TRACK_DATA" | $JQ_BIN -r '.tags[] | select(.[0] == "url") | .[1] // ""') + TRACK_IMAGE=$(echo "$TRACK_DATA" | $JQ_BIN -r '.tags[] | select(.[0] == "image") | .[1] // ""') + TRACK_DURATION=$(echo "$TRACK_DATA" | $JQ_BIN -r '.tags[] | select(.[0] == "duration") | .[1] // ""') + TRACK_ID=$(echo "$TRACK_DATA" | $JQ_BIN -r '.id') + TRACK_CREATED=$(echo "$TRACK_DATA" | $JQ_BIN -r '.created_at') + TRACK_TAGS=$(echo "$TRACK_DATA" | $JQ_BIN -r '[.tags[] | select(.[0] == "t") | .[1]] | join(",")') + + # Build track JSON object + TRACK_JSON=$(cat < "${OUTPUT_FILE}.tmp" + mv "${OUTPUT_FILE}.tmp" "$OUTPUT_FILE" +done + +# ============================================ +# Step 4: Flattened tracks file +# ============================================ + +log "Generating flattened tracks file..." + +cat "$ALL_TRACKS_JSON" | $JQ_BIN ' + .[] | { + id: .id, + created_at: .created_at, + title: (.tags[] | select(.[0] == "title") | .[1] // "Untitled"), + artist: (.tags[] | select(.[0] == "artist") | .[1] // "Unknown Artist"), + album: (.tags[] | select(.[0] == "album") | .[1] // ""), + url: (.tags[] | select(.[0] == "url") | .[1] // ""), + image: (.tags[] | select(.[0] == "image") | .[1] // ""), + duration: (.tags[] | select(.[0] == "duration") | .[1] // ""), + tags: [.tags[] | select(.[0] == "t") | .[1]], + d_tag: (.tags[] | select(.[0] == "d") | .[1] // "") + } +' | $JQ_BIN -s '.' > "$FLATTENED_OUTPUT" + +# ============================================ +# Step 5: Save raw data +# ============================================ + +cp "$PLAYLISTS_JSON" "$DATA_DIR/playlists-raw.json" +cp "$ALL_TRACKS_JSON" "$DATA_DIR/tracks-raw.json" + +# ============================================ +# Step 6: Cleanup +# ============================================ + +rm -f "$PLAYLISTS_JSON" "$ALL_TRACKS_JSON" "$TRACK_LOOKUP" + +# ============================================ +# Step 7: Summary +# ============================================ + +echo "" +echo -e "${BLUE}========================================${NC}" +echo -e "${GREEN}📊 Playlist Summary${NC}" +echo -e "${BLUE}========================================${NC}" +echo -e " 📀 Playlists: $PLAYLIST_COUNT" +echo -e " 🎵 Total tracks found: $TRACK_COUNT" +echo -e " 📁 Data saved to: $DATA_DIR/" +echo -e "${BLUE}========================================${NC}" + +# Show playlist names +echo "" +echo -e "${BLUE}📝 Playlist Names:${NC}" +cat "$OUTPUT_FILE" | $JQ_BIN -r '.playlists[].name' 2>/dev/null | while read -r name; do + if [ -n "$name" ]; then + echo " 🎵 $name" + else + echo " ⚠️ (unnamed)" + fi +done \ No newline at end of file diff --git a/themes/one-pager/assets/css/style.css b/themes/one-pager/assets/css/style.css index 7cc3ad4..a4186f1 100644 --- a/themes/one-pager/assets/css/style.css +++ b/themes/one-pager/assets/css/style.css @@ -2093,3 +2093,187 @@ button { } /* ============ END CYBERPUNK FOOTER ============ */ + +/* ============================================ + * PLAYLISTS + * ============================================ */ + +.playlists-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); + gap: 2.5rem; +} + +.playlist-card { + background: var(--card-bg); + border: 2px solid var(--accent-grey); + padding: 1.8rem; + transition: all 0.3s ease; + transform: skewX(-2deg); + box-shadow: 8px 8px 0 var(--accent-grey); +} + +.playlist-card:hover { + transform: skewX(0deg) translate(-4px, -4px); + box-shadow: 12px 12px 0 var(--accent-yellow); +} + +.playlist-image { + width: 100%; + height: 180px; + object-fit: cover; + border: 2px solid var(--accent-grey); + margin-bottom: 1rem; + filter: grayscale(30%); + transition: all 0.3s ease; +} + +.playlist-card:hover .playlist-image { + filter: grayscale(0%); +} + +.playlist-info h3 { + font-family: 'Poppins', sans-serif; + font-weight: 700; + font-size: 1.3rem; + letter-spacing: -1px; + margin-bottom: 0.3rem; + color: var(--text-primary); +} + +.playlist-description { + color: var(--text-secondary); + font-size: 0.9rem; + margin-bottom: 0.5rem; +} + +.playlist-track-count { + display: inline-block; + background: var(--accent-yellow); + color: var(--accent-black); + padding: 0.2rem 0.8rem; + font-size: 0.7rem; + font-weight: 700; + letter-spacing: 1px; + border-radius: 4px; + margin-top: 0.5rem; +} + +.playlist-tracks { + margin-top: 1.5rem; + border-top: 2px solid var(--accent-grey); + padding-top: 1rem; +} + +.playlist-tracks h4 { + font-family: 'Poppins', sans-serif; + font-weight: 600; + font-size: 1rem; + color: var(--accent-yellow); + margin-bottom: 1rem; + letter-spacing: 1px; +} + +.tracks-list { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.track-item { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.8rem; + padding: 0.8rem; + background: var(--bg-secondary); + border-radius: 8px; + border-left: 3px solid var(--accent-yellow); + transition: all 0.3s ease; +} + +.track-item:hover { + background: var(--bg-primary); + border-left-color: var(--accent-yellow); +} + +.track-thumbnail { + width: 50px; + height: 50px; + object-fit: cover; + border-radius: 4px; + border: 1px solid var(--accent-grey); +} + +.track-info { + flex: 1; + min-width: 150px; +} + +.track-title { + display: block; + font-family: 'Poppins', sans-serif; + font-weight: 600; + font-size: 0.9rem; + color: var(--text-primary); +} + +.track-artist { + display: block; + font-size: 0.75rem; + color: var(--accent-yellow); +} + +.track-album { + display: block; + font-size: 0.65rem; + color: var(--accent-grey); +} + +.track-duration { + font-size: 0.65rem; + color: var(--accent-grey); + background: var(--tag-bg); + padding: 0.1rem 0.5rem; + border-radius: 3px; +} + +.track-player { + width: 100%; + margin-top: 0.5rem; + border: 1px solid var(--accent-grey); + border-radius: 4px; +} + +.track-item .track-tags { + display: flex; + flex-wrap: wrap; + gap: 0.3rem; + margin-top: 0.3rem; +} + +/* Responsive */ +@media (max-width: 768px) { + .playlists-grid { + grid-template-columns: 1fr; + } + + .playlist-card { + transform: skewX(0deg); + } + + .playlist-card:hover { + transform: translate(-2px, -2px); + } + + .track-item { + flex-direction: column; + align-items: flex-start; + } + + .track-thumbnail { + width: 100%; + height: 100px; + } +} +/* //////// END PLAYLISTS ////////// */ diff --git a/themes/one-pager/layouts/partials/playlists.html b/themes/one-pager/layouts/partials/playlists.html new file mode 100644 index 0000000..8f5c4c9 --- /dev/null +++ b/themes/one-pager/layouts/partials/playlists.html @@ -0,0 +1,90 @@ +{{ $playlistsData := index hugo.Data "playlists" }} +{{ $playlists := index $playlistsData "playlists" }} + +{{ if $playlists }} +
+

Playlists

+ +
+ {{ range $playlistIndex, $playlist := $playlists }} + {{/* Debug: show the type and data */}} + {{/* +
+ Playlist {{ $playlistIndex }}: Type = {{ printf "%T" $playlist }} +
+ */}} + + {{/* Get the actual playlist object - it's the first element of the array */}} + {{ $playlistObj := index $playlist 0 }} + + {{ if $playlistObj }} + {{ $name := index $playlistObj "name" | default "Untitled Playlist" }} + {{ $description := index $playlistObj "description" | default "" }} + {{ $image := index $playlistObj "image" | default "" }} + {{ $tracks := index $playlistObj "tracks" | default slice }} + +
+ {{ if $image }} + {{ $name }} + {{ end }} + +
+

{{ $name }}

+ {{ if $description }} +

{{ $description }}

+ {{ end }} + {{ if $tracks }} + {{ len $tracks }} tracks + {{ end }} +
+ + {{ if $tracks }} +
+

Tracks

+
+ {{ range $track := $tracks }} + {{ $trackTitle := index $track "title" | default "Untitled" }} + {{ $trackArtist := index $track "artist" | default "Unknown Artist" }} + {{ $trackAlbum := index $track "album" | default "" }} + {{ $trackImage := index $track "image" | default "" }} + {{ $trackUrl := index $track "url" | default "" }} + {{ $trackDuration := index $track "duration" | default "" }} + {{ $trackTags := index $track "tags" | default slice }} + +
+ {{ if $trackImage }} + {{ $trackTitle }} + {{ end }} +
+ {{ $trackTitle }} + {{ $trackArtist }} + {{ if $trackAlbum }} + {{ $trackAlbum }} + {{ end }} + {{ if $trackDuration }} + {{ $trackDuration }}s + {{ end }} +
+ {{ if $trackUrl }} + + {{ end }} + {{ if $trackTags }} +
+ {{ range $trackTags }} + {{ . }} + {{ end }} +
+ {{ end }} +
+ {{ end }} +
+
+ {{ end }} +
+ {{ end }} + {{ end }} +
+
+{{ end }}