the home site for me: also iteration 3 or 4 of my site
1{% if config.extra.header_nav %}
2{% if page %}
3{% set active_path = page.path | trim_end_matches(pat="/") %}
4{% elif section %}
5{% set active_path = section.path | trim_end_matches(pat="/") %}
6{% elif current_path %}
7{% set active_path = current_path | trim_end_matches(pat="/") %}
8{% else %}
9{% set active_path = "" %}
10{% endif %}
11<div id="header-container">
12 <span id="now-playing"></span>
13 <nav id="nav-bar">
14 {% for nav_item in config.extra.header_nav %}
15 <a href="{{ nav_item.url }}"
16 class="{% if nav_item.url == active_path or (nav_item.url == '/' and active_path == '') %}active{% endif %}">
17 {{ nav_item.name }}
18 </a>
19 {% endfor %}
20 </nav>
21</div>
22<script>
23 async function resolveDidToPds(did) {
24 if (did.startsWith("did:plc:")) {
25 const res = await fetch(`https://plc.directory/${did}`);
26 const doc = await res.json();
27 return doc.service?.find(s => s.id === "#atproto_pds")?.serviceEndpoint;
28 } else if (did.startsWith("did:web:")) {
29 const domain = did.slice(8);
30 const res = await fetch(`https://${domain}/.well-known/did.json`);
31 const doc = await res.json();
32 return doc.service?.find(s => s.id === "#atproto_pds")?.serviceEndpoint;
33 }
34 return null;
35 }
36 async function fetchAtUriRecord(atUri) {
37 const match = atUri.match(/^at:\/\/([^/]+)\/([^/]+)\/([^/]+)$/);
38 if (!match) return null;
39 const [, repo, collection, rkey] = match;
40 const pds = await resolveDidToPds(repo);
41 if (!pds) return null;
42 const url = `${pds}/xrpc/com.atproto.repo.getRecord?repo=${encodeURIComponent(repo)}&collection=${encodeURIComponent(collection)}&rkey=${encodeURIComponent(rkey)}`;
43 const res = await fetch(url);
44 return res.ok ? res.json() : null;
45 }
46 let nowPlayingTimeout = null;
47 function fetchNowPlaying() {
48 if (nowPlayingTimeout) clearTimeout(nowPlayingTimeout);
49 fetchAtUriRecord("at://did:plc:krxbvxvis5skq7jj6eot23ul/fm.teal.alpha.actor.status/self")
50 .then((data) => {
51 const el = document.getElementById("now-playing");
52 if (!data?.value?.item) {
53 el.innerHTML = "";
54 nowPlayingTimeout = setTimeout(fetchNowPlaying, 60000);
55 return;
56 }
57 const item = data.value.item;
58 const expiry = new Date(data.value.expiry).getTime();
59 const now = Date.now();
60 if (now > expiry) {
61 el.innerHTML = "";
62 nowPlayingTimeout = setTimeout(fetchNowPlaying, 60000);
63 return;
64 }
65 el.innerHTML = `
66 <div class="now-playing-line"><span>🎵</span><a href="${item.originUrl || '#'}" target="_blank" rel="noopener"><span class="track-name">${item.trackName}</span></a></div>
67 <div class="now-playing-line artist-line">${item.artists?.[0]?.artistName || 'Unknown'}</div>
68 <div class="now-playing-line"><relative-time datetime="${item.playedTime}" threshold="P1D"></relative-time></div>
69 `;
70 const timeUntilExpiry = expiry - now + 5000;
71 nowPlayingTimeout = setTimeout(fetchNowPlaying, timeUntilExpiry);
72 })
73 .catch(() => {
74 nowPlayingTimeout = setTimeout(fetchNowPlaying, 60000);
75 });
76 }
77 document.addEventListener("DOMContentLoaded", fetchNowPlaying);
78</script>
79{% endif %}