madebydanny.uk written in html, css, and a lot of JavaScript I don't understand madebydanny.uk
html css javascript
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

git push

+249 -107
+5 -107
index.html
··· 3 3 <head> 4 4 <meta charset="UTF-8"> 5 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 - <title>Daniel Morrisey  - nadebydanny.uk</title> 6 + <title>Daniel Morrisey  - madebydanny.uk</title> 7 7 <script src="https://kit.fontawesome.com/0ca27f8db1.js" crossorigin="anonymous"></script> <link rel="icon" id="favicon" href="https://imrs.madebydanny.uk/?url=https://cloudflareisawesome.madebydanny.uk/madebydanny.uk/seo/favicon.webp"> 8 8 <meta name="description" id="meta-description" content="Posting everything into the endless jet stream of posts"> 9 - <meta property="og:title" content="Daniel Morrisey  - nadebydanny.uk"> 9 + <meta property="og:title" content="Daniel Morrisey  - madebydanny.uk"> 10 10 <meta property="og:description" id="og-description" content="Posting everything into the endless jet stream of posts"> 11 11 <meta property="og:type" content="website"> 12 12 <meta property="og:url" content="https://madebydanny.uk/"> 13 - <meta property="og:image" id="og-image" content="/media/ogimg.png"> 13 + <meta property="og:image" id="og-image" content="https://atimg.madebydanny.uk/?image=https://selfhosted.social%2Fxrpc%2Fcom.atproto.sync.getBlob%3Fdid%3Ddid%3Aplc%3Al37td5yhxl2irrzrgvei4qay%26cid%3Dbafkreidnipwcd2mzx2lcjpdxfu535dtm5nyyoo3i3drdzseqfon6muo7ya"> 14 14 <link rel="stylesheet" href="/css/index.css"> 15 15 </head> 16 16 <body> 17 17 <h1>Hi, I'm Daniel Morrisey </h1> 18 + <p><a href="https://guestbook.madebydanny.uk">Sign the Guestbook</a> ~ <a href="https://pdsls.dev/at://did:plc:l37td5yhxl2irrzrgvei4qay/fm.teal.alpha.feed.play">Recently played Music</a></p> 18 19 <p>I like to listen to Music <i>(Mainly Tate McRae and Taylor Swift)</i>, and post on Bluesky</p> 19 20 <p>I'm also on <a>Threads</a> and <a>Mastodon</a>, but active on <a>Bluesky</a>, becuase it's the best social media platform</p> 20 21 <div id="music-status-card"> ··· 47 48 <span class="label">Bluesky</span> 48 49 </a> 49 50 50 - <a class="social-btn btn-instagram" role="listitem" href="https://instagram.com/madebydanny.uk" target="_blank" rel="noopener noreferrer" aria-label="Instagram"> 51 - <i class="fa-brands fa-instagram"></i> 52 - <span class="label">Instagram</span> 53 - </a> 54 - 55 51 <a class="social-btn btn-tangled" role="listitem" href="https://tangled.org/madebydannyuk" target="_blank" rel="noopener noreferrer" aria-label="Tangled"> 56 52 <i class="fa-brands fa-git-alt"></i> 57 53 <span class="label">Tangled</span> ··· 69 65 </div> 70 66 <hr> 71 67 <p>&copy; 2024-26 Daniel Morrisey by <a href="/followonbsky.html?did=did:plc:l37td5yhxl2irrzrgvei4qay">@madebydanny.uk</a> - hosted on <a href="https://wisp.place" target="_blank">wisp.place</a></p> 72 - <p></p> 73 - 74 - <script> 75 - const BLUESKY_DID = 'did:plc:4qjpcixqrc3b2qmbce76sm7k'; 76 - const MUSIC_DID = 'did:plc:l37td5yhxl2irrzrgvei4qay'; 77 - 78 - async function fetchMusicStatus() { 79 - const url = `https://selfhosted.social/xrpc/com.atproto.repo.getRecord?repo=${MUSIC_DID}&collection=fm.teal.alpha.actor.status&rkey=self`; 80 - try { 81 - const response = await fetch(url); 82 - if (!response.ok) { 83 - console.error('Music API error:', response.status, response.statusText); 84 - throw new Error('Network response was not ok'); 85 - } 86 - const data = await response.json(); 87 - console.log('Music data:', data); 88 - 89 - if (data && data.value && data.value.item) { 90 - const item = data.value.item; 91 - const artists = item.artists.map(a => a.artistName).join(', '); 92 - const trackUrl = item.originUrl || '#'; 93 - 94 - document.getElementById('music-link-icon').innerHTML = `<a href="${trackUrl}" target="_blank">↗</a>`; 95 - document.getElementById('music-ui-content').innerHTML = ` 96 - <a href="${trackUrl}" target="_blank" class="bsky-trackname">${item.trackName}</a> 97 - <div class="bsky-artist">by ${artists}</div> 98 - `; 99 - document.getElementById('music-status-card').style.display = 'block'; 100 - } else { 101 - console.warn('No music data found'); 102 - } 103 - } catch (error) { 104 - console.error('Error fetching music:', error); 105 - // Show card anyway with error message 106 - document.getElementById('music-ui-content').innerHTML = '<span class="error-msg">No recent music activity</span>'; 107 - document.getElementById('music-status-card').style.display = 'block'; 108 - } 109 - } 110 - 111 - async function fetchLatestPost() { 112 - const url = `https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed?actor=${BLUESKY_DID}&limit=1`; 113 - try { 114 - console.log('Fetching post from:', url); 115 - const response = await fetch(url); 116 - 117 - if (!response.ok) { 118 - console.error('Post API error:', response.status, response.statusText); 119 - const errorText = await response.text(); 120 - console.error('Error response:', errorText); 121 - throw new Error(`HTTP ${response.status}`); 122 - } 123 - 124 - const data = await response.json(); 125 - console.log('Post data:', data); 126 - 127 - if (data.feed && data.feed.length > 0) { 128 - const latest = data.feed[0].post; 129 - const record = latest.record; 130 - const rkey = latest.uri.split('/').pop(); 131 - const postUrl = `https://bsky.app/profile/madebydanny.uk/post/${rkey}`; 132 - 133 - // Set Text and Date 134 - document.getElementById('post-content').innerText = record.text || ""; 135 - document.getElementById('post-date').innerText = new Date(record.createdAt).toLocaleString(); 136 - document.getElementById('post-link-icon').innerHTML = `<a href="${postUrl}" target="_blank">↗</a>`; 137 - 138 - // Handle Images (Embeds) 139 - const mediaContainer = document.getElementById('post-media'); 140 - mediaContainer.innerHTML = ''; // Clear previous 141 - 142 - if (record.embed && record.embed.$type === 'app.bsky.embed.images') { 143 - record.embed.images.forEach(img => { 144 - const imgRef = img.image.ref.$link; 145 - const imgSrc = `https://cdn.bsky.app/img/feed_fullsize/plain/${BLUESKY_DID}/${imgRef}@jpeg`; 146 - 147 - const imgElement = document.createElement('img'); 148 - imgElement.src = imgSrc; 149 - imgElement.alt = img.alt || "Bluesky post image"; 150 - imgElement.className = 'post-image'; 151 - mediaContainer.appendChild(imgElement); 152 - }); 153 - } 154 - 155 - document.getElementById('latest-post-card').style.display = 'block'; 156 - } else { 157 - console.warn('No posts found in feed'); 158 - document.getElementById('post-content').innerHTML = '<span class="error-msg">No posts found</span>'; 159 - document.getElementById('latest-post-card').style.display = 'block'; 160 - } 161 - } catch (error) { 162 - console.error('Error fetching post:', error); 163 - document.getElementById('post-content').innerHTML = `<span class="error-msg">Error loading post: ${error.message}</span>`; 164 - document.getElementById('latest-post-card').style.display = 'block'; 165 - } 166 - } 167 - 168 - fetchMusicStatus(); 169 - fetchLatestPost(); 170 - </script> 68 + <script src="/js/script.js"></script> 171 69 </body> 172 70 </html>
+147
js/music.js
··· 1 + (() => { 2 + const MUSIC_DID = 'did:plc:l37td5yhxl2irrzrgvei4qay'; 3 + const PDS_BASE = 'https://selfhosted.social'; 4 + 5 + const statusEl = document.getElementById('recent-status'); 6 + const listEl = document.getElementById('recent-list'); 7 + 8 + function setStatus(text) { 9 + if (statusEl) statusEl.textContent = text || ''; 10 + } 11 + 12 + function formatDate(iso) { 13 + if (!iso) return ''; 14 + try { 15 + const d = new Date(iso); 16 + return d.toLocaleString(undefined, { 17 + year: 'numeric', 18 + month: 'short', 19 + day: 'numeric', 20 + hour: '2-digit', 21 + minute: '2-digit' 22 + }); 23 + } catch { 24 + return iso; 25 + } 26 + } 27 + 28 + function buildArtworkUrl(play) { 29 + if (!play) return ''; 30 + // Prefer explicit artwork fields if they exist, else fall back to proxying the origin URL 31 + const fromField = 32 + play.artworkUrl || 33 + play.artworkUrl100 || 34 + (play.albumArt && play.albumArt.url); 35 + 36 + if (fromField) return fromField; 37 + 38 + if (play.originUrl) { 39 + return 'https://imrs.madebydanny.uk/?url=' + encodeURIComponent(play.originUrl); 40 + } 41 + return ''; 42 + } 43 + 44 + function renderPlays(plays) { 45 + if (!listEl) return; 46 + listEl.innerHTML = ''; 47 + 48 + if (!plays || !plays.length) { 49 + setStatus('No recent plays found.'); 50 + return; 51 + } 52 + 53 + setStatus(''); 54 + 55 + plays.forEach((rec) => { 56 + const value = rec.value || rec; // support both raw and listRecords-style 57 + const artistsArr = Array.isArray(value.artists) ? value.artists : []; 58 + const artists = artistsArr.map((a) => a.artistName || a.name).filter(Boolean).join(', '); 59 + const title = value.trackName || 'Unknown track'; 60 + const originUrl = value.originUrl || '#'; 61 + const when = formatDate(value.playedTime || rec.indexedAt); 62 + const artwork = buildArtworkUrl(value); 63 + 64 + const item = document.createElement('div'); 65 + item.className = 'recent-item'; 66 + 67 + if (artwork) { 68 + const img = document.createElement('img'); 69 + img.className = 'recent-artwork'; 70 + img.src = artwork; 71 + img.alt = `${title} artwork`; 72 + img.loading = 'lazy'; 73 + item.appendChild(img); 74 + } 75 + 76 + const meta = document.createElement('div'); 77 + meta.className = 'recent-meta'; 78 + 79 + const titleEl = document.createElement('div'); 80 + titleEl.className = 'recent-title'; 81 + if (originUrl && originUrl !== '#') { 82 + const link = document.createElement('a'); 83 + link.href = originUrl; 84 + link.target = '_blank'; 85 + link.rel = 'noopener'; 86 + link.textContent = title; 87 + titleEl.appendChild(link); 88 + } else { 89 + titleEl.textContent = title; 90 + } 91 + 92 + const artistEl = document.createElement('div'); 93 + artistEl.className = 'recent-artist'; 94 + artistEl.textContent = artists || 'Unknown artist'; 95 + 96 + const timeEl = document.createElement('div'); 97 + timeEl.className = 'recent-time'; 98 + timeEl.textContent = when; 99 + 100 + meta.appendChild(titleEl); 101 + meta.appendChild(artistEl); 102 + meta.appendChild(timeEl); 103 + 104 + item.appendChild(meta); 105 + listEl.appendChild(item); 106 + }); 107 + } 108 + 109 + async function fetchRecentPlays() { 110 + setStatus('Loading recent plays…'); 111 + 112 + try { 113 + const url = 114 + PDS_BASE + 115 + '/xrpc/com.atproto.repo.listRecords' + 116 + '?repo=' + 117 + encodeURIComponent(MUSIC_DID) + 118 + '&collection=' + 119 + encodeURIComponent('fm.teal.alpha.feed.play') + 120 + '&limit=50' + 121 + '&reverse=true'; 122 + 123 + const res = await fetch(url); 124 + if (!res.ok) { 125 + throw new Error('HTTP ' + res.status); 126 + } 127 + 128 + const data = await res.json(); 129 + const records = Array.isArray(data.records) ? data.records : []; 130 + 131 + // Sort by playedTime descending if present 132 + records.sort((a, b) => { 133 + const av = (a.value && a.value.playedTime) || a.indexedAt || ''; 134 + const bv = (b.value && b.value.playedTime) || b.indexedAt || ''; 135 + return (bv > av) ? 1 : (bv < av ? -1 : 0); 136 + }); 137 + 138 + renderPlays(records); 139 + } catch (err) { 140 + console.warn('Error loading recent plays', err); 141 + setStatus('Error loading recent plays. Please try again later.'); 142 + } 143 + } 144 + 145 + document.addEventListener('DOMContentLoaded', fetchRecentPlays); 146 + })(); 147 +
+97
js/script.js
··· 1 + document.addEventListener('DOMContentLoaded', () => { 2 + const BLUESKY_DID = 'did:plc:4qjpcixqrc3b2qmbce76sm7k'; 3 + const MUSIC_DID = 'did:plc:l37td5yhxl2irrzrgvei4qay'; 4 + 5 + async function fetchMusicStatus() { 6 + const url = `https://selfhosted.social/xrpc/com.atproto.repo.getRecord?repo=${MUSIC_DID}&collection=fm.teal.alpha.actor.status&rkey=self`; 7 + try { 8 + const response = await fetch(url); 9 + if (!response.ok) { 10 + console.error('Music API error:', response.status, response.statusText); 11 + throw new Error('Network response was not ok'); 12 + } 13 + const data = await response.json(); 14 + console.log('Music data:', data); 15 + 16 + if (data && data.value && data.value.item) { 17 + const item = data.value.item; 18 + const artists = item.artists.map(a => a.artistName).join(', '); 19 + const trackUrl = item.originUrl || '#'; 20 + 21 + document.getElementById('music-link-icon').innerHTML = `<a href="${trackUrl}" target="_blank">↗</a>`; 22 + document.getElementById('music-ui-content').innerHTML = ` 23 + <a href="${trackUrl}" target="_blank" class="bsky-trackname">${item.trackName}</a> 24 + <div class="bsky-artist">by ${artists}</div> 25 + `; 26 + document.getElementById('music-status-card').style.display = 'block'; 27 + } else { 28 + console.warn('No music data found'); 29 + } 30 + } catch (error) { 31 + console.error('Error fetching music:', error); 32 + // Show card anyway with error message 33 + document.getElementById('music-ui-content').innerHTML = '<span class="error-msg">No recent music activity</span>'; 34 + document.getElementById('music-status-card').style.display = 'block'; 35 + } 36 + } 37 + 38 + async function fetchLatestPost() { 39 + const url = `https://public.api.bsky.app/xrpc/app.bsky.feed.getAuthorFeed?actor=${BLUESKY_DID}&limit=1`; 40 + try { 41 + console.log('Fetching post from:', url); 42 + const response = await fetch(url); 43 + 44 + if (!response.ok) { 45 + console.error('Post API error:', response.status, response.statusText); 46 + const errorText = await response.text(); 47 + console.error('Error response:', errorText); 48 + throw new Error(`HTTP ${response.status}`); 49 + } 50 + 51 + const data = await response.json(); 52 + console.log('Post data:', data); 53 + 54 + if (data.feed && data.feed.length > 0) { 55 + const latest = data.feed[0].post; 56 + const record = latest.record; 57 + const rkey = latest.uri.split('/').pop(); 58 + const postUrl = `https://bsky.app/profile/madebydanny.uk/post/${rkey}`; 59 + 60 + // Set Text and Date 61 + document.getElementById('post-content').innerText = record.text || ""; 62 + document.getElementById('post-date').innerText = new Date(record.createdAt).toLocaleString(); 63 + document.getElementById('post-link-icon').innerHTML = `<a href="${postUrl}" target="_blank">↗</a>`; 64 + 65 + // Handle Images (Embeds) 66 + const mediaContainer = document.getElementById('post-media'); 67 + mediaContainer.innerHTML = ''; // Clear previous 68 + 69 + if (record.embed && record.embed.$type === 'app.bsky.embed.images') { 70 + record.embed.images.forEach(img => { 71 + const imgRef = img.image.ref.$link; 72 + const imgSrc = `https://cdn.bsky.app/img/feed_fullsize/plain/${BLUESKY_DID}/${imgRef}@jpeg`; 73 + 74 + const imgElement = document.createElement('img'); 75 + imgElement.src = imgSrc; 76 + imgElement.alt = img.alt || "Bluesky post image"; 77 + imgElement.className = 'post-image'; 78 + mediaContainer.appendChild(imgElement); 79 + }); 80 + } 81 + 82 + document.getElementById('latest-post-card').style.display = 'block'; 83 + } else { 84 + console.warn('No posts found in feed'); 85 + document.getElementById('post-content').innerHTML = '<span class="error-msg">No posts found</span>'; 86 + document.getElementById('latest-post-card').style.display = 'block'; 87 + } 88 + } catch (error) { 89 + console.error('Error fetching post:', error); 90 + document.getElementById('post-content').innerHTML = `<span class="error-msg">Error loading post: ${error.message}</span>`; 91 + document.getElementById('latest-post-card').style.display = 'block'; 92 + } 93 + } 94 + 95 + fetchMusicStatus(); 96 + fetchLatestPost(); 97 + });
media/ogimg.png

This is a binary file and will not be displayed.