Commit 11d7a44d by PLN (Algolia)

feat(parvagues): add Montreuil Algorave gig page

15-track collapsible list driven by metadata extracted from the .tidal
sources via the Pulsar HUD parser. Each row: name, BPM, style, expandable
ingredients (per-orbit sample/role/CC bindings) + git.nech.pl src link.
Six tracks have IG story loops (5s, 16:9 cropped from 9:16 originals,
~150 KB each) with a fixed-position hover loupe (480×270) that escapes
the row's clipping box; click opens the highlight album. Page-level SC
embed + Instagram album link in MediaSection. Full-length story MP4s
gitignored — only the loop previews + jpg thumbs ship.
parent 5b5a5dfa
...@@ -32,3 +32,7 @@ yarn-error.log* ...@@ -32,3 +32,7 @@ yarn-error.log*
# LLM exchanges # LLM exchanges
code2prompt.json code2prompt.json
.vercel .vercel
# IG highlight full-length MP4s — kept local for re-cropping; only loops + jpgs are shipped
public/images/parvagues/lives/ig-live-highlight/*.mp4
!public/images/parvagues/lives/ig-live-highlight/*_loop.mp4
---
title: "Montreuil Algorave"
date: "2026-05-22"
time: "21:00"
location: "Montreuil, France"
address: "Montreuil, FR"
description: "Algorave par @a_f_alfl aux Nouveaux Sauvages à Montreuil, avec @shipow aux visuels."
ctaURL: "https://soundcloud.com/parvagues/live-montreuil-algorave"
ctaText: "Écouter sur SoundCloud"
audio: "https://soundcloud.com/parvagues/live-montreuil-algorave"
instagram: "https://www.instagram.com/stories/highlights/18035324351004139/"
video: ""
archive: ""
tags: ["livecoding", "algorave", "technojazz", "montreuil"]
---
Algorave par [@a_f_alfl](https://instagram.com/a_f_alfl) aux Nouveaux Sauvages à Montreuil, avec [@shipow](https://instagram.com/shipow) aux visuels.
{
"gig": "montreuil-algorave",
"title": "Montreuil Algorave V3 — Mai Floral",
"date": "2026-05-22",
"bpmRange": [
60,
170
],
"styleDistribution": {
"ambient": 1,
"nujazz": 3,
"lounge": 1,
"dnb": 4,
"drill": 1,
"punk": 1,
"dance": 1,
"breaks": 1,
"techno": 2
},
"samplePacks": [
{
"name": "jungle_breaks",
"usage": "used in 10 orbits"
},
{
"name": "bassWarsaw",
"usage": "used in 6 orbits"
},
{
"name": "h2ogmhh",
"usage": "used in 6 orbits"
},
{
"name": "risers",
"usage": "used in 5 orbits"
},
{
"name": "take5",
"usage": "used in 4 orbits"
},
{
"name": "orage",
"usage": "used in 4 orbits"
},
{
"name": "xplosive",
"usage": "used in 3 orbits"
},
{
"name": "suns_guitar",
"usage": "used in 3 orbits"
},
{
"name": "like_sugar",
"usage": "used in 3 orbits"
},
{
"name": "airports",
"usage": "used in 2 orbits"
},
{
"name": "moogBass",
"usage": "used in 2 orbits"
},
{
"name": "vec1_claps",
"usage": "used in 2 orbits"
}
],
"instagramHighlight": "https://www.instagram.com/stories/highlights/18035324351004139/",
"tracks": [
{
"name": "Quand on décolle",
"file": "live/midi/nova/ambient/quand_on_decolle.tidal",
"bpm": 60,
"style": "ambient",
"duration": 362.0,
"ingredients": [
{
"type": "sample",
"code": "s \"bassWarsaw\"",
"description": "d4 — bass · CC crush, octersub, on"
},
{
"type": "pad",
"code": "s \"airports\"",
"description": "d5 — pad · CC delay, dt, on"
},
{
"type": "pad",
"code": "s \"airports\"",
"description": "d7 — pad · CC on"
},
{
"type": "sample",
"code": "s \"jungle_breaks:24\"",
"description": "BROKEN BEAT | BREAKS STACK · CC on"
},
{
"type": "effect",
"code": "s \"risers:8\"",
"description": "FIXME Other RISER"
},
{
"type": "sample",
"code": "s \"moogBass\"",
"description": "DRINK ME <3"
},
{
"type": "moment",
"code": "highlights",
"description": "breakdown×2 · transition×4 · buildup×3 · peak×1 · best cut @ 4:30"
}
]
},
{
"name": "Piment brésilien",
"file": "live/collab/raph/piment_bresilien.tidal",
"bpm": 124,
"style": "nujazz",
"duration": 354.0,
"ingredients": [
{
"type": "sample",
"code": "s \"[techno:0,808bd:2,909,kick:4]\"",
"description": "d1 — rhythm · CC off, on"
},
{
"type": "sample",
"code": "s \"vec1_claps\"",
"description": "d2 — rhythm · CC on"
},
{
"type": "sample",
"code": "s \"bassWarsaw\"",
"description": "d4 — bass · CC crush, octer, off"
},
{
"type": "synth",
"code": "s \"gfunk_lead:2\"",
"description": "LE PIMENT BRESILIEN · CC octer, octersub, on"
},
{
"type": "sample",
"code": "s \"jungle_breaks:45\"",
"description": "La Boite à breaks de ParVagues <3 · CC off, on"
},
{
"type": "effect",
"code": "d10",
"description": "Menace · CC crush"
},
{
"type": "moment",
"code": "highlights",
"description": "breakdown×5 · buildup×2 · transition×2 · drop×3 · peak×1 · best cut @ 4:00"
}
],
"loop": "/images/parvagues/lives/ig-live-highlight/18_montreuil26_3904124101644263299_loop.mp4"
},
{
"name": "Take 5 Drops",
"file": "live/midi/nova/lounge/take_5_drops.tidal",
"bpm": 124,
"style": "nujazz",
"duration": 418.0,
"ingredients": [
{
"type": "sample",
"code": "s \"bassWarsaw\"",
"description": "BASSLINE GENERATIVE · CC crush, octersub, on"
},
{
"type": "sample",
"code": "s \"moogBass\"",
"description": "CHOEUR GENERATIF ALONG THE BASS · CC crush, on, range"
},
{
"type": "sample",
"code": "s \"take5:0\"",
"description": "La Boite à breaks de ParVagues <3 · CC off, on"
},
{
"type": "effect",
"code": "s \"risers\"",
"description": "A FAIR RISER"
},
{
"type": "synth",
"code": "s \"take5\"",
"description": "THE FLUTIST"
},
{
"type": "synth",
"code": "s \"take5\"",
"description": "THE SLEEPWALKER"
},
{
"type": "moment",
"code": "highlights",
"description": "breakdown×2 · buildup×7 · drop×1 · transition×5 · peak×1 · best cut @ 0:52"
}
]
},
{
"name": "Super Sunny Side Up",
"file": "live/midi/nova/lounge/sunny_side_up.tidal",
"bpm": 120,
"style": "lounge",
"duration": 271.0,
"ingredients": [
{
"type": "sample",
"code": "s \"h2ogmhh:3\"",
"description": "Hats closed legers"
},
{
"type": "sample",
"code": "s \"sunny_bass\"",
"description": "La super sunny bass on the other side of the sunny street · CC crush, octersub, octersubsub"
},
{
"type": "synth",
"code": "s \"sunny_brass\"",
"description": "On the Sunny side of the sample · CC on"
},
{
"type": "sample",
"code": "s \"h2ogmhh:5*8\"",
"description": "Hats closed legers · CC off"
},
{
"type": "sample",
"code": "s \"acidOto3092\"",
"description": "LA ACID BASS ARE YOU SUNNY NOW ?? <3 · CC crush, on, width"
},
{
"type": "synth",
"code": "s \"nujazz_keys120:35/4\"",
"description": "Sunny doux piano c'est cadeau"
},
{
"type": "moment",
"code": "highlights",
"description": "transition×4 · breakdown×3 · buildup×3 · peak×1 · best cut @ 2:17"
}
]
},
{
"name": "W.I.P. W.A.P.",
"file": "live/midi/nova/dnb/wap.tidal",
"bpm": 133,
"style": "dnb",
"duration": 229.0,
"ingredients": [
{
"type": "sample",
"code": "s \"[kick:4]\"",
"description": "Kick · CC off, on"
},
{
"type": "sample",
"code": "s \"wap/2\"",
"description": "WAP BASS · CC crush, octer, octersubsub"
},
{
"type": "synth",
"code": "s \"wap:0\"",
"description": "WAP HOES v1 magic repeat · CC off, on"
},
{
"type": "sample",
"code": "s \"wap\"",
"description": "Les WAP DRUMS · CC off, on"
},
{
"type": "sample",
"code": "s \"jungle_breaks:84\"",
"description": "La stack de breaks · CC off, on"
},
{
"type": "sample",
"code": "s \"vec1_acid:25\"",
"description": "d9 — bass · CC crush, gain"
},
{
"type": "moment",
"code": "highlights",
"description": "breakdown×2 · buildup×5 · peak×1 · transition×1 · drop×1 · best cut @ 3:33"
}
],
"loop": "/images/parvagues/lives/ig-live-highlight/19_montreuil26_3904125516458554645_loop.mp4"
},
{
"name": "'Plosive",
"file": "live/midi/nova/dnb/plosive.tidal",
"bpm": 160,
"style": "dnb",
"duration": 178.71,
"ingredients": [
{
"type": "sample",
"code": "s \"[808bd:13,jazz,kicklinn]\"",
"description": "YRVHTECHNO KICK TO DNB · CC off, on"
},
{
"type": "sample",
"code": "s \"vec1_snare:4\"",
"description": "d2 — rhythm"
},
{
"type": "sample",
"code": "s \"bassWarsaw\"",
"description": "d4 — bass · CC crush, octersub, off"
},
{
"type": "synth",
"code": "s \"xplosive\"",
"description": "RIFFs <3"
},
{
"type": "sample",
"code": "s \"xplosive\"",
"description": "VOICES · CC off, on"
},
{
"type": "synth",
"code": "s \"xplosive:6\"",
"description": "d12 — lead · CC cyc"
},
{
"type": "moment",
"code": "highlights",
"description": "transition×5 · buildup×5 · peak×1 · best cut @ 0:46"
}
]
},
{
"name": "Jeudi Drill",
"file": "live/collab/raph/jeudrill.tidal",
"bpm": 140,
"style": "drill",
"duration": 240.0,
"ingredients": [
{
"type": "sample",
"code": "s \"giorgio_syn:1\"",
"description": "Bassline aqualgorithmique · CC crush, octersubsub"
},
{
"type": "synth",
"code": "s \"marimba1\"",
"description": "Marimba fondamental · CC off"
},
{
"type": "sample",
"code": "d7",
"description": "d7 — bass · CC crush, cyc, on"
},
{
"type": "sample",
"code": "s \"jungle_breaks:26\"",
"description": "Come on breaks! · CC off, on"
},
{
"type": "pad",
"code": "s \"bogdan_grime:3\"",
"description": "Bogdan the Grime sample provider"
},
{
"type": "effect",
"code": "s \"90s_synatm:10\"",
"description": "Menace · CC crush"
},
{
"type": "moment",
"code": "highlights",
"description": "breakdown×2 · transition×5 · buildup×3 · peak×1 · drop×1 · best cut @ 3:35"
}
],
"loop": "/images/parvagues/lives/ig-live-highlight/22_montreuil26_3904132082700135316_loop.mp4"
},
{
"name": "Aria Sans Serif",
"file": "live/collab/raph/aria_sans_serif.tidal",
"bpm": 160,
"style": "dnb",
"duration": 255.26,
"ingredients": [
{
"type": "sample",
"code": "s \"[jazz,clubkick]\"",
"description": "d1 — rhythm · CC off, on"
},
{
"type": "sample",
"code": "s \"jazz:4\"",
"description": "d2 — rhythm · CC off, on"
},
{
"type": "sample",
"code": "s \"h2ogmhh\"",
"description": "d3 — rhythm · CC off, on"
},
{
"type": "sample",
"code": "s \"moog:2\"",
"description": "d4 — bass · CC crush, octer, on"
},
{
"type": "sample",
"code": "s \"oil\"",
"description": "d5 — bass · CC off, on, squiz"
},
{
"type": "sample",
"code": "s \"acidOto3091\"",
"description": "d7 — bass · CC crush, octer, on"
},
{
"type": "moment",
"code": "highlights",
"description": "transition×4 · breakdown×2 · buildup×4 · peak×1 · best cut @ 0:56"
}
],
"loop": "/images/parvagues/lives/ig-live-highlight/23_montreuil26_3904132801972947604_loop.mp4"
},
{
"name": "PunkAChien",
"file": "live/collab/raph/punkachien.tidal",
"bpm": 170,
"style": "punk",
"duration": 287.74,
"ingredients": [
{
"type": "sample",
"code": "s \"[kick:4]\"",
"description": "Kick · CC off, on"
},
{
"type": "sample",
"code": "s \"[snare:39]\"",
"description": "Snare melancolie fromagere · CC off, on"
},
{
"type": "sample",
"code": "s \"d*[16 <12 <16 [12 24]>> . 8]\"",
"description": "d3 — rhythm · CC on"
},
{
"type": "sample",
"code": "s \"vec1_acid:11\"",
"description": "d4 — bass · CC crush, on, range"
},
{
"type": "sample",
"code": "s \"cpluck:1\"",
"description": "d5 — bass · CC crush, octer, octersub"
},
{
"type": "pad",
"code": "s \"moog\"",
"description": "d9 — pad · CC crush, on"
},
{
"type": "moment",
"code": "highlights",
"description": "breakdown×3 · buildup×8 · transition×1 · drop×1 · peak×1 · best cut @ 4:25"
}
],
"loop": "/images/parvagues/lives/ig-live-highlight/20_montreuil26_3904126116294333504_loop.mp4"
},
{
"name": "You My Sunshine",
"file": "live/midi/nova/dnb/liquid/you_my_sunshine.tidal",
"bpm": 144,
"style": "dnb",
"duration": 318.68,
"ingredients": [
{
"type": "sample",
"code": "s \"rampleS34:6\"",
"description": "Highest hats · CC on"
},
{
"type": "sample",
"code": "s \"bassWarsaw\"",
"description": "d4 — bass · CC crush, gain, off"
},
{
"type": "synth",
"code": "s \"no_sunshine\"",
"description": "d5 — lead · CC off, on"
},
{
"type": "sample",
"code": "s \"no_sunshine/2\"",
"description": "Guitar Sunshine · CC on"
},
{
"type": "sample",
"code": "s \"org_jungle_breaks:1\"",
"description": "JazzyBelle JazzyBreak · CC on"
},
{
"type": "pad",
"code": "s \"no_sunshine/4\"",
"description": "d9 — pad"
},
{
"type": "moment",
"code": "highlights",
"description": "breakdown×1 · buildup×5 · drop×1 · transition×2 · peak×1 · best cut @ 0:21"
}
]
},
{
"name": "Desire",
"file": "live/collab/raph/desire.tidal",
"bpm": 129,
"style": "dance",
"duration": 246.0,
"ingredients": [
{
"type": "sample",
"code": "s \"[techno:0,jazz]\"",
"description": "KICK: Sub thud, 4otf with flourish (NTO: deepens over time) · CC off, on, some"
},
{
"type": "sample",
"code": "s \"h2ogmcp\"",
"description": "CLAP: Dry quiet snap, contretemps (enters late) · CC off, on, some"
},
{
"type": "sample",
"code": "s \"h2ogmhh\"",
"description": "OH/CY: Subtle offbeat hats, ^44 morphs to cymbal shimmer · CC legato, on, some"
},
{
"type": "synth",
"code": "s \"desire\"",
"description": "d5 — lead · CC octersub, on"
},
{
"type": "sample",
"code": "s \"desire\"",
"description": "Voices · CC off, on"
},
{
"type": "sample",
"code": "s \"desire_synth/2\"",
"description": "Announcing SYNTH · CC sup"
},
{
"type": "moment",
"code": "highlights",
"description": "breakdown×3 · transition×6 · drop×1 · buildup×4 · peak×1 · best cut @ 1:11"
}
]
},
{
"name": "L'or Bleu",
"file": "live/collab/mousquetaires/blue_gold.tidal",
"bpm": 124,
"style": "breaks",
"duration": 258.0,
"ingredients": [
{
"type": "sample",
"code": "s \"~ h ~ h ~ h*<1 2> ~ h*<1!3 2>\"",
"description": "Highest hats · CC on"
},
{
"type": "sample",
"code": "s \"bassWarsaw\"",
"description": "BASSLINE VIOLENTE PAR VALENTIN <3 · CC crush, octer, octersub"
},
{
"type": "pad",
"code": "s \"suns_keys\"",
"description": "Suns of Gold Keys, samples · CC off, on, tremr"
},
{
"type": "pad",
"code": "s \"suns_guitar:0/2\"",
"description": "Suns of Gold Guitars, sampled and cut · CC crush, lpf, octersub"
},
{
"type": "pad",
"code": "s \"suns_voice:0\"",
"description": "Voice of the Father"
},
{
"type": "effect",
"code": "s \"[<~!3 rampleD5:3>,risers:5]\"",
"description": "CUES AND NOISES"
},
{
"type": "moment",
"code": "highlights",
"description": "breakdown×2 · buildup×8 · drop×1 · peak×1 · transition×1 · best cut @ 0:27"
}
]
},
{
"name": "Premier Septembre",
"file": "live/collab/baba/sept1.tidal",
"bpm": 120,
"style": "techno",
"duration": 313.65,
"ingredients": [
{
"type": "sample",
"code": "s \"cy\"",
"description": "Cymbales sol · CC off, on"
},
{
"type": "sample",
"code": "s \"cpluck\"",
"description": "Bassline faite main avec un violoncelle californien · CC crush, octer, on"
},
{
"type": "synth",
"code": "s \"rhadamanthe_melo:1\"",
"description": "Piano nostalgique, temps inexorable · CC crush, on"
},
{
"type": "sample",
"code": "s \"breaks165\"",
"description": "Je t'avais oublie toi <3"
},
{
"type": "sample",
"code": "s \"jungle_breaks:1\"",
"description": "Break multi-facette, assez complexe, comme chacun d'entre nous · CC on"
},
{
"type": "synth",
"code": "s \"nujazz_keys120:35/4\"",
"description": "Intro doux piano · CC delay"
},
{
"type": "moment",
"code": "highlights",
"description": "breakdown×2 · buildup×4 · transition×5 · peak×1 · best cut @ 2:47"
}
],
"loop": "/images/parvagues/lives/ig-live-highlight/24_montreuil26_3904133245638071567_loop.mp4"
},
{
"name": "Techno Orage",
"file": "live/midi/nova/techno/techno_orage.tidal",
"bpm": 104,
"style": "techno",
"duration": 424.07,
"ingredients": [
{
"type": "sample",
"code": "s \"orage\"",
"description": "BASS <3 · CC crush, octer"
},
{
"type": "synth",
"code": "s \"orage\"",
"description": "Rain piano <3 · CC crush, octer, octersub"
},
{
"type": "sample",
"code": "s \"orage\"",
"description": "Voices · CC on"
},
{
"type": "sample",
"code": "s \"jungle_breaks:12\"",
"description": "Break discret face à une telle idole · CC off, on"
},
{
"type": "sample",
"code": "s \"[acid1,acid2]\"",
"description": "ACID Doutes · CC gain, on, some"
},
{
"type": "effect",
"code": "s \"risers\"",
"description": "d10 — riser"
},
{
"type": "moment",
"code": "highlights",
"description": "breakdown×2 · buildup×4 · transition×3 · peak×1 · best cut @ 4:35"
}
]
},
{
"name": "La Révolution Sera Samplée",
"file": "live/midi/nova/jazz/the_revolution_will_be_sampled.tidal",
"bpm": 114,
"style": "nujazz",
"duration": 424.27,
"ingredients": [
{
"type": "sample",
"code": "s \"like_sugar:0\"",
"description": "d4 — bass · CC bandf, crush, off"
},
{
"type": "sample",
"code": "d7",
"description": "LEAD: Rhodes arp in D Dorian (i7 → IV7 → bIII → v) · CC cutoff, off, on"
},
{
"type": "pad",
"code": "s \"like_sugar/2\"",
"description": "SUGAR CHOIR"
},
{
"type": "effect",
"code": "s \"like_sugar:21\"",
"description": "SUGAR KEY SWEET TRIGGER"
},
{
"type": "synth",
"code": "s \"like_sugar/2\"",
"description": "SUGAR GLITCH: stuttered chops + bitcrush"
},
{
"type": "synth",
"code": "s \"like_sugar:20/4\"",
"description": "HEY HEY!"
},
{
"type": "moment",
"code": "highlights",
"description": "breakdown×3 · buildup×10 · transition×2 · peak×1 · drop×1 · best cut @ 6:13"
}
]
}
]
}
\ No newline at end of file
import { useState, useEffect } from 'react'; import { useState, useEffect, useRef } from 'react';
import Head from 'next/head'; import Head from 'next/head';
import Image from 'next/image'; import Image from 'next/image';
import { getAllLives, getLiveData, getLivesImages, getLiveTracks } from '@/lib/livesData'; import { getAllLives, getLiveData, getLivesImages, getLiveTracks } from '@/lib/livesData';
...@@ -13,6 +13,70 @@ import Prism from 'prismjs'; ...@@ -13,6 +13,70 @@ import Prism from 'prismjs';
import 'prismjs/components/prism-haskell'; import 'prismjs/components/prism-haskell';
import 'prismjs/themes/prism-tomorrow.css'; import 'prismjs/themes/prism-tomorrow.css';
const LOUPE_W = 480; // px, matches w-[480px]
const LOUPE_H = 270; // px, 16:9 aspect of 480w (loops cropped from 9:16 stories)
const LOUPE_MARGIN = 16;
function LoopThumb({ src, href }) {
const thumbRef = useRef(null);
const [pos, setPos] = useState(null);
const onEnter = () => {
if (!thumbRef.current) return;
const r = thumbRef.current.getBoundingClientRect();
const vw = window.innerWidth;
const vh = window.innerHeight;
// Center on thumb; clamp so the loupe always stays fully on-screen
const x = Math.max(
LOUPE_W / 2 + LOUPE_MARGIN,
Math.min(vw - LOUPE_W / 2 - LOUPE_MARGIN, r.left + r.width / 2)
);
const y = Math.max(
LOUPE_H / 2 + LOUPE_MARGIN,
Math.min(vh - LOUPE_H / 2 - LOUPE_MARGIN, r.top + r.height / 2)
);
setPos({ x, y });
};
const onLeave = () => setPos(null);
return (
<a
ref={thumbRef}
href={href || '#'}
target="_blank"
rel="noopener noreferrer"
onClick={(e) => e.stopPropagation()}
onMouseEnter={onEnter}
onMouseLeave={onLeave}
title="Voir sur Instagram"
className="flex-shrink-0"
>
<video
src={src}
autoPlay muted loop playsInline
className={`w-12 h-12 rounded-md object-cover border transition-colors ${pos ? 'border-[var(--neon-high)]/60' : 'border-white/10'}`}
/>
{pos && (
<div
className="fixed z-[60] pointer-events-none transition-opacity duration-150 hidden md:block"
style={{
left: pos.x,
top: pos.y,
width: LOUPE_W,
transform: 'translate(-50%, -50%)',
}}
>
<video
src={src}
autoPlay muted loop playsInline
className="w-full rounded-xl border-2 border-[var(--neon-high)]/50 shadow-2xl shadow-black/90"
/>
</div>
)}
</a>
);
}
function TrackIngredient({ ingredient }) { function TrackIngredient({ ingredient }) {
return ( return (
<div className="bg-black/30 rounded-lg p-3 border border-white/5"> <div className="bg-black/30 rounded-lg p-3 border border-white/5">
...@@ -61,6 +125,9 @@ function TracksSection({ tracks }) { ...@@ -61,6 +125,9 @@ function TracksSection({ tracks }) {
<details key={track.name} className="group bg-white/[0.02] border border-white/[0.06] rounded-xl overflow-hidden"> <details key={track.name} className="group bg-white/[0.02] border border-white/[0.06] rounded-xl overflow-hidden">
<summary className="flex items-center gap-4 px-5 py-4 cursor-pointer hover:bg-white/[0.03] transition-colors"> <summary className="flex items-center gap-4 px-5 py-4 cursor-pointer hover:bg-white/[0.03] transition-colors">
<span className="font-mono text-xs text-[var(--text-muted)] w-6">{String(i + 1).padStart(2, '0')}</span> <span className="font-mono text-xs text-[var(--text-muted)] w-6">{String(i + 1).padStart(2, '0')}</span>
{track.loop && (
<LoopThumb src={track.loop} href={tracks.instagramHighlight} />
)}
<span className="font-display font-semibold text-sm flex-grow">{track.name}</span> <span className="font-display font-semibold text-sm flex-grow">{track.name}</span>
{track.bpm && <span className="text-[11px] text-[var(--text-muted)] font-mono">{track.bpm} BPM</span>} {track.bpm && <span className="text-[11px] text-[var(--text-muted)] font-mono">{track.bpm} BPM</span>}
{track.style && <span className="text-[10px] tracking-wider uppercase text-[var(--text-muted)]">{track.style}</span>} {track.style && <span className="text-[10px] tracking-wider uppercase text-[var(--text-muted)]">{track.style}</span>}
...@@ -71,7 +138,15 @@ function TracksSection({ tracks }) { ...@@ -71,7 +138,15 @@ function TracksSection({ tracks }) {
))} ))}
{track.file && ( {track.file && (
<p className="text-[10px] text-[var(--text-muted)] font-mono mt-2"> <p className="text-[10px] text-[var(--text-muted)] font-mono mt-2">
src: {track.file} src:{' '}
<a
href={`https://git.nech.pl/pln/Tidal/blob/master/${track.file}`}
target="_blank"
rel="noopener noreferrer"
className="hover:text-[var(--neon-high)] transition-colors underline decoration-dotted decoration-white/20 underline-offset-2"
>
{track.file}
</a>
</p> </p>
)} )}
</div> </div>
...@@ -82,11 +157,12 @@ function TracksSection({ tracks }) { ...@@ -82,11 +157,12 @@ function TracksSection({ tracks }) {
); );
} }
function MediaSection({ audio, video, archive }) { function MediaSection({ audio, video, archive, instagram }) {
const links = [ const links = [
audio && { url: audio, label: detectPlatform(audio) }, audio && { url: audio, label: detectPlatform(audio) },
video && { url: video, label: detectPlatform(video) }, video && { url: video, label: detectPlatform(video) },
archive && { url: archive, label: detectPlatform(archive) }, archive && { url: archive, label: detectPlatform(archive) },
instagram && { url: instagram, label: detectPlatform(instagram) },
].filter(Boolean); ].filter(Boolean);
if (links.length === 0) return null; if (links.length === 0) return null;
...@@ -140,6 +216,7 @@ function detectPlatform(url) { ...@@ -140,6 +216,7 @@ function detectPlatform(url) {
if (url.includes('youtube')) return 'YouTube'; if (url.includes('youtube')) return 'YouTube';
if (url.includes('archive.org')) return 'Archive.org'; if (url.includes('archive.org')) return 'Archive.org';
if (url.includes('spotify')) return 'Spotify'; if (url.includes('spotify')) return 'Spotify';
if (url.includes('instagram')) return 'Instagram';
return 'Écouter'; return 'Écouter';
} }
...@@ -167,7 +244,7 @@ export default function LiveEvent({ live, images, tracks, isPostEvent: initialPo ...@@ -167,7 +244,7 @@ export default function LiveEvent({ live, images, tracks, isPostEvent: initialPo
const fm = live.frontmatter; const fm = live.frontmatter;
const eventDate = new Date((fm.date || '') + 'T00:00:00'); const eventDate = new Date((fm.date || '') + 'T00:00:00');
const dateStr = isNaN(eventDate.getTime()) ? null : format(eventDate, 'dd MMMM yyyy', { locale: fr }); const dateStr = isNaN(eventDate.getTime()) ? null : format(eventDate, 'dd MMMM yyyy', { locale: fr });
const hasMedia = fm.audio || fm.video || fm.archive; const hasMedia = fm.audio || fm.video || fm.archive || fm.instagram;
const title = fm.title || live.slug; const title = fm.title || live.slug;
const location = fm.location || ''; const location = fm.location || '';
...@@ -222,6 +299,7 @@ export default function LiveEvent({ live, images, tracks, isPostEvent: initialPo ...@@ -222,6 +299,7 @@ export default function LiveEvent({ live, images, tracks, isPostEvent: initialPo
audio={fm.audio} audio={fm.audio}
video={fm.video} video={fm.video}
archive={fm.archive} archive={fm.archive}
instagram={fm.instagram}
/> />
)} )}
......
{
"username": "parvagues.mp3",
"highlight_pk": "18035324351004139",
"highlight_title": "Live 🔊",
"highlight_url": "https://www.instagram.com/stories/highlights/18035324351004139/",
"fetched_at": "2026-05-24T16:22:18",
"items": [
{
"index": 1,
"gig": "ccc-2024",
"date_utc": "2024-10-06T13:34:22",
"mediaid": "3472897487859595026",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3472897487859595026/",
"files": [
"01_ccc-2024_3472897487859595026.jpg",
"01_ccc-2024_3472897487859595026.mp4"
],
"loop_file": "01_ccc-2024_3472897487859595026_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
},
{
"index": 2,
"gig": "lyon-algorave-2025",
"date_utc": "2025-05-24T21:52:44",
"mediaid": "3639846751595324022",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3639846751595324022/",
"files": [
"02_lyon-algorave-2025_3639846751595324022.jpg",
"02_lyon-algorave-2025_3639846751595324022.mp4"
],
"loop_file": "02_lyon-algorave-2025_3639846751595324022_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
},
{
"index": 3,
"gig": "lyon-algorave-2025",
"date_utc": "2025-05-25T14:16:02",
"mediaid": "3640341650342131394",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3640341650342131394/",
"files": [
"03_lyon-algorave-2025_3640341650342131394.jpg",
"03_lyon-algorave-2025_3640341650342131394.mp4"
],
"loop_file": "03_lyon-algorave-2025_3640341650342131394_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
},
{
"index": 4,
"gig": "cosmicfest-2025",
"date_utc": "2025-06-22T17:38:52",
"mediaid": "3660738103656884158",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3660738103656884158/",
"files": [
"04_cosmicfest-2025_3660738103656884158.jpg",
"04_cosmicfest-2025_3660738103656884158.mp4"
],
"loop_file": "04_cosmicfest-2025_3660738103656884158_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
},
{
"index": 5,
"gig": "cosmicfest-2025",
"date_utc": "2025-06-22T17:39:11",
"mediaid": "3660739426834044573",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3660739426834044573/",
"files": [
"05_cosmicfest-2025_3660739426834044573.jpg",
"05_cosmicfest-2025_3660739426834044573.mp4"
],
"loop_file": "05_cosmicfest-2025_3660739426834044573_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
},
{
"index": 6,
"gig": "cosmicfest-2025",
"date_utc": "2025-06-22T17:39:24",
"mediaid": "3660740062203926753",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3660740062203926753/",
"files": [
"06_cosmicfest-2025_3660740062203926753.jpg",
"06_cosmicfest-2025_3660740062203926753.mp4"
],
"loop_file": "06_cosmicfest-2025_3660740062203926753_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
},
{
"index": 7,
"gig": "cosmicfest-2025",
"date_utc": "2025-06-22T17:39:39",
"mediaid": "3660744267178404555",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3660744267178404555/",
"files": [
"07_cosmicfest-2025_3660744267178404555.jpg",
"07_cosmicfest-2025_3660744267178404555.mp4"
],
"loop_file": "07_cosmicfest-2025_3660744267178404555_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
},
{
"index": 8,
"gig": "cosmicfest-2025",
"date_utc": "2025-06-22T17:39:49",
"mediaid": "3660744648365088582",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3660744648365088582/",
"files": [
"08_cosmicfest-2025_3660744648365088582.jpg",
"08_cosmicfest-2025_3660744648365088582.mp4"
],
"loop_file": "08_cosmicfest-2025_3660744648365088582_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
},
{
"index": 9,
"gig": "cosmicfest-2025",
"date_utc": "2025-06-22T17:40:02",
"mediaid": "3660745080369921054",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3660745080369921054/",
"files": [
"09_cosmicfest-2025_3660745080369921054.jpg",
"09_cosmicfest-2025_3660745080369921054.mp4"
],
"loop_file": "09_cosmicfest-2025_3660745080369921054_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
},
{
"index": 10,
"gig": "cosmicfest-2025",
"date_utc": "2025-06-22T17:41:16",
"mediaid": "3660745549611922275",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3660745549611922275/",
"files": [
"10_cosmicfest-2025_3660745549611922275.jpg",
"10_cosmicfest-2025_3660745549611922275.mp4"
],
"loop_file": "10_cosmicfest-2025_3660745549611922275_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
},
{
"index": 11,
"gig": "cosmicfest-2025",
"date_utc": "2025-06-23T20:36:12",
"mediaid": "3661552020452984945",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3661552020452984945/",
"files": [
"11_cosmicfest-2025_3661552020452984945.jpg",
"11_cosmicfest-2025_3661552020452984945.mp4"
],
"loop_file": "11_cosmicfest-2025_3661552020452984945_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
},
{
"index": 12,
"gig": "opal25",
"date_utc": "2025-08-13T10:05:06",
"mediaid": "3698197458484126827",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3698197458484126827/",
"files": [
"12_opal25_3698197458484126827.jpg",
"12_opal25_3698197458484126827.mp4"
],
"loop_file": "12_opal25_3698197458484126827_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
},
{
"index": 13,
"gig": "opal25",
"date_utc": "2025-08-13T10:07:37",
"mediaid": "3698198749155237569",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3698198749155237569/",
"files": [
"13_opal25_3698198749155237569.jpg",
"13_opal25_3698198749155237569.mp4"
],
"loop_file": "13_opal25_3698198749155237569_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
},
{
"index": 14,
"gig": "opal25",
"date_utc": "2025-08-13T10:08:57",
"mediaid": "3698199638557546211",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3698199638557546211/",
"files": [
"14_opal25_3698199638557546211.jpg",
"14_opal25_3698199638557546211.mp4"
],
"loop_file": "14_opal25_3698199638557546211_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
},
{
"index": 15,
"gig": "standalone-2025-09",
"date_utc": "2025-09-26T09:32:53",
"mediaid": "3730071367052969862",
"typename": "GraphStoryImage",
"is_video": false,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3730071367052969862/",
"files": [
"15_standalone-2025-09_3730071367052969862.jpg"
]
},
{
"index": 16,
"gig": "montreuil26",
"date_utc": "2026-05-22T22:31:52",
"mediaid": "3902959909721651162",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3902959909721651162/",
"files": [
"16_montreuil26_3902959909721651162.jpg",
"16_montreuil26_3902959909721651162.mp4"
],
"loop_file": "16_montreuil26_3902959909721651162_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
},
{
"index": 17,
"gig": "montreuil26",
"date_utc": "2026-05-23T08:02:27",
"mediaid": "3903247087634803743",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3903247087634803743/",
"files": [
"17_montreuil26_3903247087634803743.jpg",
"17_montreuil26_3903247087634803743.mp4"
],
"loop_file": "17_montreuil26_3903247087634803743_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
},
{
"index": 18,
"gig": "montreuil26",
"date_utc": "2026-05-24T13:04:53",
"mediaid": "3904124101644263299",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3904124101644263299/",
"files": [
"18_montreuil26_3904124101644263299.jpg",
"18_montreuil26_3904124101644263299.mp4"
],
"loop_file": "18_montreuil26_3904124101644263299_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
},
{
"index": 19,
"gig": "montreuil26",
"date_utc": "2026-05-24T13:07:42",
"mediaid": "3904125516458554645",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3904125516458554645/",
"files": [
"19_montreuil26_3904125516458554645.jpg",
"19_montreuil26_3904125516458554645.mp4"
],
"loop_file": "19_montreuil26_3904125516458554645_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
},
{
"index": 20,
"gig": "montreuil26",
"date_utc": "2026-05-24T13:08:52",
"mediaid": "3904126116294333504",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3904126116294333504/",
"files": [
"20_montreuil26_3904126116294333504.jpg",
"20_montreuil26_3904126116294333504.mp4"
],
"loop_file": "20_montreuil26_3904126116294333504_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
},
{
"index": 21,
"gig": "montreuil26",
"date_utc": "2026-05-24T13:20:44",
"mediaid": "3904132182602628446",
"typename": "GraphStoryImage",
"is_video": false,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3904132182602628446/",
"files": [
"21_montreuil26_3904132182602628446.jpg"
]
},
{
"index": 22,
"gig": "montreuil26",
"date_utc": "2026-05-24T13:20:48",
"mediaid": "3904132082700135316",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3904132082700135316/",
"files": [
"22_montreuil26_3904132082700135316.jpg",
"22_montreuil26_3904132082700135316.mp4"
],
"loop_file": "22_montreuil26_3904132082700135316_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
},
{
"index": 23,
"gig": "montreuil26",
"date_utc": "2026-05-24T13:22:18",
"mediaid": "3904132801972947604",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3904132801972947604/",
"files": [
"23_montreuil26_3904132801972947604.jpg",
"23_montreuil26_3904132801972947604.mp4"
],
"loop_file": "23_montreuil26_3904132801972947604_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
},
{
"index": 24,
"gig": "montreuil26",
"date_utc": "2026-05-24T13:23:10",
"mediaid": "3904133245638071567",
"typename": "GraphStoryVideo",
"is_video": true,
"story_url": "https://www.instagram.com/stories/parvagues.mp3/3904133245638071567/",
"files": [
"24_montreuil26_3904133245638071567.jpg",
"24_montreuil26_3904133245638071567.mp4"
],
"loop_file": "24_montreuil26_3904133245638071567_loop.mp4",
"loop_params": {
"start_s": 1.0,
"duration_s": 5.0,
"width": 540,
"crf": 28,
"crop": "iw:iw*9/16:0:(ih-iw*9/16)/2"
}
}
]
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment