mirror of
https://github.com/skidoodle/albert.lol.git
synced 2026-04-28 03:07:40 +02:00
cleanup
This commit is contained in:
@@ -1 +0,0 @@
|
|||||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 640 512" class="fill-current transition-transform duration-300 ease-in-out hover:text-[#ad87ed] hover:scale-105 focus:outline-none" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M524.531,69.836a1.5,1.5,0,0,0-.764-.7A485.065,485.065,0,0,0,404.081,32.03a1.816,1.816,0,0,0-1.923.91,337.461,337.461,0,0,0-14.9,30.6,447.848,447.848,0,0,0-134.426,0,309.541,309.541,0,0,0-15.135-30.6,1.89,1.89,0,0,0-1.924-.91A483.689,483.689,0,0,0,116.085,69.137a1.712,1.712,0,0,0-.788.676C39.068,183.651,18.186,294.69,28.43,404.354a2.016,2.016,0,0,0,.765,1.375A487.666,487.666,0,0,0,176.02,479.918a1.9,1.9,0,0,0,2.063-.676A348.2,348.2,0,0,0,208.12,430.4a1.86,1.86,0,0,0-1.019-2.588,321.173,321.173,0,0,1-45.868-21.853,1.885,1.885,0,0,1-.185-3.126c3.082-2.309,6.166-4.711,9.109-7.137a1.819,1.819,0,0,1,1.9-.256c96.229,43.917,200.41,43.917,295.5,0a1.812,1.812,0,0,1,1.924.233c2.944,2.426,6.027,4.851,9.132,7.16a1.884,1.884,0,0,1-.162,3.126,301.407,301.407,0,0,1-45.89,21.83,1.875,1.875,0,0,0-1,2.611,391.055,391.055,0,0,0,30.014,48.815,1.864,1.864,0,0,0,2.063.7A486.048,486.048,0,0,0,610.7,405.729a1.882,1.882,0,0,0,.765-1.352C623.729,277.594,590.933,167.465,524.531,69.836ZM222.491,337.58c-28.972,0-52.844-26.587-52.844-59.239S193.056,219.1,222.491,219.1c29.665,0,53.306,26.82,52.843,59.239C275.334,310.993,251.924,337.58,222.491,337.58Zm195.38,0c-28.971,0-52.843-26.587-52.843-59.239S388.437,219.1,417.871,219.1c29.667,0,53.307,26.82,52.844,59.239C470.715,310.993,447.538,337.58,417.871,337.58Z"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 496 512" class="fill-current transition-transform duration-300 ease-in-out hover:text-[#ad87ed] hover:scale-105 focus:outline-none" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 512 512" class="fill-current transition-transform duration-300 ease-in-out hover:text-[#ad87ed] hover:scale-105 focus:outline-none" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M502.3 190.8c3.9-3.1 9.7-.2 9.7 4.7V400c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V195.6c0-5 5.7-7.8 9.7-4.7 22.4 17.4 52.1 39.5 154.1 113.6 21.1 15.4 56.7 47.8 92.2 47.6 35.7.3 72-32.8 92.3-47.6 102-74.1 131.6-96.3 154-113.7zM256 320c23.2.4 56.6-29.2 73.4-41.4 132.7-96.3 142.8-104.7 173.4-128.7 5.8-4.5 9.2-11.5 9.2-18.9v-19c0-26.5-21.5-48-48-48H48C21.5 64 0 85.5 0 112v19c0 7.4 3.4 14.3 9.2 18.9 30.6 23.9 40.7 32.4 173.4 128.7 16.8 12.2 50.2 41.8 73.4 41.4z"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 777 B |
@@ -1 +0,0 @@
|
|||||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 496 512" class="fill-current transition-transform duration-300 ease-in-out hover:text-[#ad87ed] hover:scale-105 focus:outline-none" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M496 256c0 137-111.2 248-248.4 248-113.8 0-209.6-76.3-239-180.4l95.2 39.3c6.4 32.1 34.9 56.4 68.9 56.4 39.2 0 71.9-32.4 70.2-73.5l84.5-60.2c52.1 1.3 95.8-40.9 95.8-93.5 0-51.6-42-93.5-93.7-93.5s-93.7 42-93.7 93.5v1.2L176.6 279c-15.5-.9-30.7 3.4-43.5 12.1L0 236.1C10.2 108.4 117.1 8 247.6 8 384.8 8 496 119 496 256zM155.7 384.3l-30.5-12.6a52.79 52.79 0 0 0 27.2 25.8c26.9 11.2 57.8-1.6 69-28.4 5.4-13 5.5-27.3.1-40.3-5.4-13-15.5-23.2-28.5-28.6-12.9-5.4-26.7-5.2-38.9-.6l31.5 13c19.8 8.2 29.2 30.9 20.9 50.7-8.3 19.9-31 29.2-50.8 21zm173.8-129.9c-34.4 0-62.4-28-62.4-62.3s28-62.3 62.4-62.3 62.4 28 62.4 62.3-27.9 62.3-62.4 62.3zm.1-15.6c25.9 0 46.9-21 46.9-46.8 0-25.9-21-46.8-46.9-46.8s-46.9 21-46.9 46.8c.1 25.8 21.1 46.8 46.9 46.8z"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -1,83 +0,0 @@
|
|||||||
class SpotifyClient {
|
|
||||||
constructor(url, elementId) {
|
|
||||||
this.url = url;
|
|
||||||
this.elementId = elementId;
|
|
||||||
this.element = document.getElementById(this.elementId);
|
|
||||||
|
|
||||||
this.ws = null;
|
|
||||||
this.reconnectTimeout = null;
|
|
||||||
this.reconnectAttempts = 0;
|
|
||||||
|
|
||||||
this.RECONNECT_BASE_DELAY = 1000;
|
|
||||||
this.RECONNECT_MAX_DELAY = 30000;
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
|
||||||
if (!this.element) {
|
|
||||||
console.error(`Spotify-WS: Element with ID "${this.elementId}" not found.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
connect() {
|
|
||||||
if (this.ws) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Spotify-WS: Connecting...");
|
|
||||||
this.ws = new WebSocket(this.url);
|
|
||||||
|
|
||||||
this.ws.onopen = () => {
|
|
||||||
console.log("Spotify-WS: Connection established.");
|
|
||||||
this.reconnectAttempts = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.ws.onmessage = (event) => {
|
|
||||||
this.updateDOM(event.data);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.ws.onclose = (event) => {
|
|
||||||
if (!event.wasClean) {
|
|
||||||
console.warn("Spotify-WS: Connection closed unexpectedly. Attempting to reconnect...");
|
|
||||||
this.reconnect();
|
|
||||||
} else {
|
|
||||||
console.log("Spotify-WS: Connection closed cleanly.");
|
|
||||||
}
|
|
||||||
this.ws = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.ws.onerror = (error) => {
|
|
||||||
console.error("Spotify-WS: An error occurred:", error);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
reconnect() {
|
|
||||||
const delay = Math.min(
|
|
||||||
this.RECONNECT_MAX_DELAY,
|
|
||||||
this.RECONNECT_BASE_DELAY * Math.pow(2, this.reconnectAttempts)
|
|
||||||
) * (0.8 + Math.random() * 0.4);
|
|
||||||
|
|
||||||
console.log(`Spotify-WS: Reconnecting in ${Math.round(delay / 1000)}s...`);
|
|
||||||
|
|
||||||
this.reconnectAttempts++;
|
|
||||||
this.reconnectTimeout = setTimeout(() => this.connect(), delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDOM(data) {
|
|
||||||
try {
|
|
||||||
const payload = JSON.parse(data);
|
|
||||||
if (payload.is_playing && payload.item) {
|
|
||||||
const artists = payload.item.artists.map(a => a.name).join(", ");
|
|
||||||
this.element.textContent = `${payload.item.name} - ${artists}`;
|
|
||||||
} else {
|
|
||||||
this.element.textContent = "";
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Spotify-WS: Failed to parse message data.", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const spotifyClient = new SpotifyClient("wss://ws.albert.lol", "nowplaying");
|
|
||||||
spotifyClient.start();
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
interface Props {
|
||||||
|
name: 'github' | 'steam' | 'mail' | 'discord'
|
||||||
|
}
|
||||||
|
|
||||||
|
const { name } = Astro.props
|
||||||
|
|
||||||
|
const paths = {
|
||||||
|
discord:
|
||||||
|
'M524.531,69.836a1.5,1.5,0,0,0-.764-.7A485.065,485.065,0,0,0,404.081,32.03a1.816,1.816,0,0,0-1.923.91,337.461,337.461,0,0,0-14.9,30.6,447.848,447.848,0,0,0-134.426,0,309.541,309.541,0,0,0-15.135-30.6,1.89,1.89,0,0,0-1.924-.91A483.689,483.689,0,0,0,116.085,69.137a1.712,1.712,0,0,0-.788.676C39.068,183.651,18.186,294.69,28.43,404.354a2.016,2.016,0,0,0,.765,1.375A487.666,487.666,0,0,0,176.02,479.918a1.9,1.9,0,0,0,2.063-.676A348.2,348.2,0,0,0,208.12,430.4a1.86,1.86,0,0,0-1.019-2.588,321.173,321.173,0,0,1-45.868-21.853,1.885,1.885,0,0,1-.185-3.126c3.082-2.309,6.166-4.711,9.109-7.137a1.819,1.819,0,0,1,1.9-.256c96.229,43.917,200.41,43.917,295.5,0a1.812,1.812,0,0,1,1.924.233c2.944,2.426,6.027,4.851,9.132,7.16a1.884,1.884,0,0,1-.162,3.126,301.407,301.407,0,0,1-45.89,21.83,1.875,1.875,0,0,0-1,2.611,391.055,391.055,0,0,0,30.014,48.815,1.864,1.864,0,0,0,2.063.7A486.048,486.048,0,0,0,610.7,405.729a1.882,1.882,0,0,0,.765-1.352C623.729,277.594,590.933,167.465,524.531,69.836ZM222.491,337.58c-28.972,0-52.844-26.587-52.844-59.239S193.056,219.1,222.491,219.1c29.665,0,53.306,26.82,52.843,59.239C275.334,310.993,251.924,337.58,222.491,337.58Zm195.38,0c-28.971,0-52.843-26.587-52.843-59.239S388.437,219.1,417.871,219.1c29.667,0,53.307,26.82,52.844,59.239C470.715,310.993,447.538,337.58,417.871,337.58Z',
|
||||||
|
github:
|
||||||
|
'M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z',
|
||||||
|
mail: 'M502.3 190.8c3.9-3.1 9.7-.2 9.7 4.7V400c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V195.6c0-5 5.7-7.8 9.7-4.7 22.4 17.4 52.1 39.5 154.1 113.6 21.1 15.4 56.7 47.8 92.2 47.6 35.7.3 72-32.8 92.3-47.6 102-74.1 131.6-96.3 154-113.7zM256 320c23.2.4 56.6-29.2 73.4-41.4 132.7-96.3 142.8-104.7 173.4-128.7 5.8-4.5 9.2-11.5 9.2-18.9v-19c0-26.5-21.5-48-48-48H48C21.5 64 0 85.5 0 112v19c0 7.4 3.4 14.3 9.2 18.9 30.6 23.9 40.7 32.4 173.4 128.7 16.8 12.2 50.2 41.8 73.4 41.4z',
|
||||||
|
steam:
|
||||||
|
'M496 256c0 137-111.2 248-248.4 248-113.8 0-209.6-76.3-239-180.4l95.2 39.3c6.4 32.1 34.9 56.4 68.9 56.4 39.2 0 71.9-32.4 70.2-73.5l84.5-60.2c52.1 1.3 95.8-40.9 95.8-93.5 0-51.6-42-93.5-93.7-93.5s-93.7 42-93.7 93.5v1.2L176.6 279c-15.5-.9-30.7 3.4-43.5 12.1L0 236.1C10.2 108.4 117.1 8 247.6 8 384.8 8 496 119 496 256zM155.7 384.3l-30.5-12.6a52.79 52.79 0 0 0 27.2 25.8c26.9 11.2 57.8-1.6 69-28.4 5.4-13 5.5-27.3.1-40.3-5.4-13-15.5-23.2-28.5-28.6-12.9-5.4-26.7-5.2-38.9-.6l31.5 13c19.8 8.2 29.2 30.9 20.9 50.7-8.3 19.9-31 29.2-50.8 21zm173.8-129.9c-34.4 0-62.4-28-62.4-62.3s28-62.3 62.4-62.3 62.4 28 62.4 62.3-27.9 62.3-62.4 62.3zm.1-15.6c25.9 0 46.9-21 46.9-46.8 0-25.9-21-46.8-46.9-46.8s-46.9 21-46.9 46.8c.1 25.8 21.1 46.8 46.9 46.8z',
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewBox =
|
||||||
|
name === 'discord'
|
||||||
|
? '0 0 640 512'
|
||||||
|
: name === 'github' || name === 'steam'
|
||||||
|
? '0 0 496 512'
|
||||||
|
: '0 0 512 512'
|
||||||
|
---
|
||||||
|
|
||||||
|
<svg
|
||||||
|
viewBox={viewBox}
|
||||||
|
width='1.5em'
|
||||||
|
height='1.5em'
|
||||||
|
fill='currentColor'
|
||||||
|
aria-hidden='true'>
|
||||||
|
<path d={paths[name]}></path>
|
||||||
|
</svg>
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
<span id="nowplaying"></span>
|
<span id='nowplaying'></span>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { SpotifyClient } from '../scripts/spotify.js';
|
import { SpotifyClient } from '../lib/spotify'
|
||||||
const spotifyClient = new SpotifyClient("wss://ws.albert.lol", "nowplaying");
|
new SpotifyClient('wss://ws.albert.lol', 'nowplaying').start()
|
||||||
spotifyClient.start();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -17,5 +16,6 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
min-height: 1em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,99 +1,86 @@
|
|||||||
---
|
---
|
||||||
const projects = [
|
const projects = [
|
||||||
{ name: "spotify-ws", url: "https://github.com/skidoodle/spotify-ws", desc: "Gets now-playing-song from Spotify with WebSockets." },
|
{
|
||||||
{ name: "ipinfo", url: "https://github.com/skidoodle/ipinfo", desc: "Shows details about any IP address or ASN." },
|
name: 'spotify-ws',
|
||||||
{ name: "hostinfo", url: "https://github.com/skidoodle/hostinfo", desc: "Browser extension showing website origin details." },
|
url: 'https://github.com/skidoodle/spotify-ws',
|
||||||
{ name: "mediaproxy", url: "https://github.com/skidoodle/mediaproxy", desc: "Proxy for caching and optimizing media." },
|
desc: 'Gets now-playing-song from Spotify with WebSockets.',
|
||||||
{ name: "pastebin", url: "https://github.com/skidoodle/pastebin", desc: "Another simple pastebin app." },
|
},
|
||||||
{ name: "albert.lol", url: "https://github.com/skidoodle/albert.lol", desc: "You're looking at it, built with Astro." },
|
{
|
||||||
{ name: "budgetable", url: "https://github.com/skidoodle/budgetable", desc: "Tracks items to buy later." },
|
name: 'ipinfo',
|
||||||
{ name: "watch-together", url: "https://github.com/skidoodle/watch-together", desc: "Watch YouTube together with your friends." },
|
url: 'https://github.com/skidoodle/ipinfo',
|
||||||
{ name: "erettsegi-browser", url: "https://github.com/skidoodle/erettsegi-browser", desc: "Finds previous Hungarian graduation exams." },
|
desc: 'Shows details about any IP address or ASN.',
|
||||||
{ name: "ncore-stats", url: "https://github.com/skidoodle/ncore-stats", desc: "Tracks profile activity on nCore." },
|
},
|
||||||
{ name: "ncore-leaderboard", url: "https://github.com/skidoodle/ncore-leaderboard", desc: "Scrapes and sorts profiles from nCore." },
|
{
|
||||||
{ name: "iphistory", url: "https://github.com/skidoodle/iphistory", desc: "Monitors and records public IP address history." },
|
name: 'hostinfo',
|
||||||
];
|
url: 'https://github.com/skidoodle/hostinfo',
|
||||||
|
desc: 'Browser extension showing website origin details.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mediaproxy',
|
||||||
|
url: 'https://github.com/skidoodle/mediaproxy',
|
||||||
|
desc: 'Proxy for caching and optimizing media.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'pastebin',
|
||||||
|
url: 'https://github.com/skidoodle/pastebin',
|
||||||
|
desc: 'Another simple pastebin app.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'albert.lol',
|
||||||
|
url: 'https://github.com/skidoodle/albert.lol',
|
||||||
|
desc: "You're looking at it, built with Astro.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'budgetable',
|
||||||
|
url: 'https://github.com/skidoodle/budgetable',
|
||||||
|
desc: 'Tracks items to buy later.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'watch-together',
|
||||||
|
url: 'https://github.com/skidoodle/watch-together',
|
||||||
|
desc: 'Watch YouTube together with your friends.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'erettsegi-browser',
|
||||||
|
url: 'https://github.com/skidoodle/erettsegi-browser',
|
||||||
|
desc: 'Finds previous Hungarian graduation exams.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ncore-stats',
|
||||||
|
url: 'https://github.com/skidoodle/ncore-stats',
|
||||||
|
desc: 'Tracks profile activity on nCore.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ncore-leaderboard',
|
||||||
|
url: 'https://github.com/skidoodle/ncore-leaderboard',
|
||||||
|
desc: 'Scrapes and sorts profiles from nCore.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'iphistory',
|
||||||
|
url: 'https://github.com/skidoodle/iphistory',
|
||||||
|
desc: 'Monitors and records public IP address history.',
|
||||||
|
},
|
||||||
|
]
|
||||||
---
|
---
|
||||||
|
|
||||||
<section class="projects">
|
<section class='projects details-list'>
|
||||||
<details>
|
<details>
|
||||||
<summary>~/projects</summary>
|
<summary>~/projects</summary>
|
||||||
<div>
|
<div>
|
||||||
<dl>
|
<dl>
|
||||||
{projects.map((project) => (
|
{
|
||||||
|
projects.map(project => (
|
||||||
<>
|
<>
|
||||||
<dt>
|
<dt>
|
||||||
<a href={project.url} target="_blank" rel="noopener noreferrer">
|
<a href={project.url} target='_blank' rel='noopener noreferrer'>
|
||||||
{project.name}
|
{project.name}
|
||||||
</a>
|
</a>
|
||||||
</dt>
|
</dt>
|
||||||
<dd set:html={project.desc} />
|
<dd set:html={project.desc} />
|
||||||
</>
|
</>
|
||||||
))}
|
))
|
||||||
|
}
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style>
|
|
||||||
a {
|
|
||||||
color: var(--primary-color);
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: var(--hover-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
dt {
|
|
||||||
color: var(--primary-color);
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 1.17em;
|
|
||||||
margin-inline-start: var(--spacing-md);
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
dt:first-of-type {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
dd {
|
|
||||||
margin-inline-start: var(--spacing-lg);
|
|
||||||
}
|
|
||||||
|
|
||||||
details {
|
|
||||||
font-family: "JetBrains Mono", monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
summary {
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
color: var(--primary-color);
|
|
||||||
font-size: 1.3em;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-block: 1.5rem 0.5rem;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
summary::-webkit-details-marker {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
summary::after {
|
|
||||||
content: ' ▼';
|
|
||||||
font-size: 0.6em;
|
|
||||||
opacity: 0.7;
|
|
||||||
margin-inline-start: 0.3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
details[open] > summary::after {
|
|
||||||
content: ' ▲';
|
|
||||||
}
|
|
||||||
|
|
||||||
details > div {
|
|
||||||
margin-top: 1rem;
|
|
||||||
margin-inline-start: var(--spacing-md);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<section class="setup">
|
<section class='setup details-list'>
|
||||||
<details>
|
<details>
|
||||||
<summary>~/setup</summary>
|
<summary>~/setup</summary>
|
||||||
<div>
|
<div>
|
||||||
@@ -9,9 +9,9 @@
|
|||||||
<dd><b>GPU:</b> RX 6700</dd>
|
<dd><b>GPU:</b> RX 6700</dd>
|
||||||
<dd><b>RAM:</b> 48 GB</dd>
|
<dd><b>RAM:</b> 48 GB</dd>
|
||||||
<dd>
|
<dd>
|
||||||
<div class="storage-layout">
|
<div class='storage-layout'>
|
||||||
<p><b>Storage:</b></p>
|
<p><b>Storage:</b></p>
|
||||||
<div class="storage-items">
|
<div class='storage-items'>
|
||||||
<p>Samsung 990 Pro 1TB</p>
|
<p>Samsung 990 Pro 1TB</p>
|
||||||
<p>Crucial P1 1TB</p>
|
<p>Crucial P1 1TB</p>
|
||||||
<p>Seagate Barracuda 2TB</p>
|
<p>Seagate Barracuda 2TB</p>
|
||||||
@@ -24,9 +24,9 @@
|
|||||||
<dd><b>CPU:</b> Dual Xeon E5-2680 v4</dd>
|
<dd><b>CPU:</b> Dual Xeon E5-2680 v4</dd>
|
||||||
<dd><b>RAM:</b> 128 GB</dd>
|
<dd><b>RAM:</b> 128 GB</dd>
|
||||||
<dd>
|
<dd>
|
||||||
<div class="storage-layout">
|
<div class='storage-layout'>
|
||||||
<p><b>Storage:</b></p>
|
<p><b>Storage:</b></p>
|
||||||
<div class="storage-items">
|
<div class='storage-items'>
|
||||||
<p>2x WD Black SN770 1TB (MIRROR)</p>
|
<p>2x WD Black SN770 1TB (MIRROR)</p>
|
||||||
<p>8x Toshiba Enterprise 6TB (RAID-Z2)</p>
|
<p>8x Toshiba Enterprise 6TB (RAID-Z2)</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -50,67 +50,6 @@
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
a {
|
|
||||||
color: var(--primary-color);
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
color: var(--hover-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
dt {
|
|
||||||
color: var(--primary-color);
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 1.17em;
|
|
||||||
margin-inline-start: var(--spacing-md);
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
dt:first-of-type {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
dd {
|
|
||||||
margin-inline-start: var(--spacing-lg);
|
|
||||||
}
|
|
||||||
|
|
||||||
details {
|
|
||||||
font-family: "JetBrains Mono", monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
summary {
|
|
||||||
cursor: pointer;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
color: var(--primary-color);
|
|
||||||
font-size: 1.3em;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-block: 1.5rem 0.5rem;
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
summary::-webkit-details-marker {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
summary::after {
|
|
||||||
content: ' ▼';
|
|
||||||
font-size: 0.6em;
|
|
||||||
opacity: 0.7;
|
|
||||||
margin-inline-start: 0.3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
details[open] > summary::after {
|
|
||||||
content: ' ▲';
|
|
||||||
}
|
|
||||||
|
|
||||||
details > div {
|
|
||||||
margin-top: 1rem;
|
|
||||||
margin-inline-start: var(--spacing-md);
|
|
||||||
}
|
|
||||||
|
|
||||||
.storage-layout {
|
.storage-layout {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|||||||
@@ -1,17 +1,33 @@
|
|||||||
<section class="socials">
|
---
|
||||||
|
import Icon from './icon.astro'
|
||||||
|
---
|
||||||
|
|
||||||
|
<section class='socials'>
|
||||||
<h2>~/socials</h2>
|
<h2>~/socials</h2>
|
||||||
<nav>
|
<nav>
|
||||||
<a href="https://github.com/skidoodle" target="_blank" rel="noopener noreferrer" aria-label="GitHub">
|
<a
|
||||||
<img src="/static/icons/github.svg" alt="GitHub Profile">
|
href='https://github.com/skidoodle'
|
||||||
|
target='_blank'
|
||||||
|
rel='noopener noreferrer'
|
||||||
|
aria-label='GitHub'>
|
||||||
|
<Icon name='github' />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://steamcommunity.com/id/_albert" target="_blank" rel="noopener noreferrer" aria-label="Steam">
|
<a
|
||||||
<img src="/static/icons/steam.svg" alt="Steam Profile">
|
href='https://steamcommunity.com/id/_albert'
|
||||||
|
target='_blank'
|
||||||
|
rel='noopener noreferrer'
|
||||||
|
aria-label='Steam'>
|
||||||
|
<Icon name='steam' />
|
||||||
</a>
|
</a>
|
||||||
<a href="mailto:contact@albert.lol" aria-label="Email">
|
<a href='mailto:contact@albert.lol' aria-label='Email'>
|
||||||
<img src="/static/icons/mail.svg" alt="Email Contact">
|
<Icon name='mail' />
|
||||||
</a>
|
</a>
|
||||||
<a href="https://discord.com/users/637745537369767936" target="_blank" rel="noopener noreferrer" aria-label="Discord">
|
<a
|
||||||
<img src="/static/icons/discord.svg" alt="Discord Profile">
|
href='https://discord.com/users/637745537369767936'
|
||||||
|
target='_blank'
|
||||||
|
rel='noopener noreferrer'
|
||||||
|
aria-label='Discord'>
|
||||||
|
<Icon name='discord' />
|
||||||
</a>
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
</section>
|
</section>
|
||||||
@@ -31,17 +47,14 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0.3em;
|
padding: 0.3em;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
transition: opacity 0.2s, box-shadow 0.2s;
|
color: var(--text-color);
|
||||||
|
transition:
|
||||||
|
color 0.2s,
|
||||||
|
transform 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.socials a:hover {
|
.socials a:hover {
|
||||||
opacity: 0.7;
|
color: var(--primary-color);
|
||||||
}
|
transform: scale(1.1);
|
||||||
|
|
||||||
.socials img {
|
|
||||||
display: block;
|
|
||||||
width: 1.5em;
|
|
||||||
height: 1.5em;
|
|
||||||
filter: brightness(0) saturate(100%) invert(1);
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,12 +1,24 @@
|
|||||||
---
|
---
|
||||||
const birthDate = new Date('2004-07-22');
|
const birthDate = new Date('2004-07-22')
|
||||||
const today = new Date();
|
const today = new Date()
|
||||||
const age = Math.floor((today.getTime() - birthDate.getTime()) / 31557600000);
|
|
||||||
|
let age = today.getFullYear() - birthDate.getFullYear()
|
||||||
|
const monthDiff = today.getMonth() - birthDate.getMonth()
|
||||||
|
|
||||||
|
if (
|
||||||
|
monthDiff < 0 ||
|
||||||
|
(monthDiff === 0 && today.getDate() < birthDate.getDate())
|
||||||
|
) {
|
||||||
|
age--
|
||||||
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
<section class="whoami">
|
<section class='whoami'>
|
||||||
<h2>~/whoami</h2>
|
<h2>~/whoami</h2>
|
||||||
<p>I'm a <span id="age">{age}</span>-year-old developer and tech enthusiast. I enjoy working on my homelab and coding in TypeScript and Go.</p>
|
<p>
|
||||||
|
I'm a <span id='age'>{age}</span>-year-old developer and tech enthusiast. I
|
||||||
|
enjoy working on my homelab and coding in TypeScript and Go.
|
||||||
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
+65
-43
@@ -1,62 +1,84 @@
|
|||||||
---
|
---
|
||||||
import '../styles/global.css';
|
import '../styles/global.css'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string;
|
title: string
|
||||||
description: string;
|
description: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const { title, description } = Astro.props;
|
const { title, description } = Astro.props
|
||||||
|
|
||||||
const schema = JSON.stringify({
|
const schema = JSON.stringify({
|
||||||
"@context": "https://schema.org",
|
'@context': 'https://schema.org',
|
||||||
"@type": "Person",
|
'@type': 'Person',
|
||||||
"name": "Albert",
|
name: 'Albert',
|
||||||
"alternateName": "skidoodle",
|
alternateName: 'skidoodle',
|
||||||
"url": "https://albert.lol/",
|
url: 'https://albert.lol/',
|
||||||
"image": "https://albert.lol/static/preview.png",
|
image: 'https://albert.lol/static/preview.png',
|
||||||
"jobTitle": "Developer and Tech Enthusiast",
|
jobTitle: 'Developer and Tech Enthusiast',
|
||||||
"description": "21-year-old developer and tech enthusiast. I enjoy working on my homelab and coding in TypeScript and Go.",
|
description: description,
|
||||||
"knowsAbout": ["Web Development", "TypeScript", "Go", "Homelab", "Linux", "Open Source Projects"],
|
knowsAbout: [
|
||||||
"sameAs": [
|
'Web Development',
|
||||||
"https://github.com/skidoodle",
|
'TypeScript',
|
||||||
"https://steamcommunity.com/id/_albert",
|
'Go',
|
||||||
"https://discord.com/users/637745537369767936"
|
'Homelab',
|
||||||
|
'Linux',
|
||||||
|
'Open Source Projects',
|
||||||
],
|
],
|
||||||
"worksFor": {
|
sameAs: [
|
||||||
"@type": "Organization",
|
'https://github.com/skidoodle',
|
||||||
"name": "Personal Projects / Open Source"
|
'https://steamcommunity.com/id/_albert',
|
||||||
}
|
'https://discord.com/users/637745537369767936',
|
||||||
});
|
],
|
||||||
|
worksFor: {
|
||||||
|
'@type': 'Organization',
|
||||||
|
name: 'Personal Projects / Open Source',
|
||||||
|
},
|
||||||
|
})
|
||||||
---
|
---
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang='en'>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset='UTF-8' />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
<link rel="icon" href="/favicon.ico" type="image/x-icon">
|
<link rel='icon' href='/favicon.ico' type='image/x-icon' />
|
||||||
<meta name="theme-color" content="#bb86fc">
|
<meta name='theme-color' content='#bb86fc' />
|
||||||
<meta name="description" content={description}>
|
<meta name='description' content={description} />
|
||||||
|
|
||||||
<meta property="og:title" content={title}>
|
<meta property='og:title' content={title} />
|
||||||
<meta property="og:description" content={description}>
|
<meta property='og:description' content={description} />
|
||||||
<meta property="og:type" content="website">
|
<meta property='og:type' content='website' />
|
||||||
<meta property="og:url" content="https://albert.lol/">
|
<meta property='og:url' content='https://albert.lol/' />
|
||||||
|
<meta property='og:image' content='https://albert.lol/static/preview.png' />
|
||||||
|
|
||||||
<meta name="twitter:card" content="summary">
|
<meta name='twitter:card' content='summary_large_image' />
|
||||||
<meta name="twitter:title" content={title}>
|
<meta name='twitter:title' content={title} />
|
||||||
<meta name="twitter:description" content={description}>
|
<meta name='twitter:description' content={description} />
|
||||||
|
<meta
|
||||||
|
name='twitter:image'
|
||||||
|
content='https://albert.lol/static/preview.png'
|
||||||
|
/>
|
||||||
|
|
||||||
<link rel="preload" href="/static/fonts/jetbrains.woff2" as="font" type="font/woff2" crossorigin>
|
<link
|
||||||
|
rel='preload'
|
||||||
|
href='/static/fonts/jetbrains.woff2'
|
||||||
|
as='font'
|
||||||
|
type='font/woff2'
|
||||||
|
crossorigin
|
||||||
|
/>
|
||||||
|
|
||||||
<script is:inline defer src="https://analytics.albert.lol/script.js" data-website-id="2c900d5e-c577-4824-ad37-0cdf68383c42"></script>
|
<script
|
||||||
<script is:inline type="application/ld+json" set:html={schema} />
|
is:inline
|
||||||
</head>
|
defer
|
||||||
<body>
|
src='https://analytics.albert.lol/script.js'
|
||||||
|
data-website-id='2c900d5e-c577-4824-ad37-0cdf68383c42'></script>
|
||||||
|
<script is:inline type='application/ld+json' set:html={schema} />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
<main>
|
<main>
|
||||||
<slot />
|
<slot />
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
interface SpotifyArtist {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SpotifyItem {
|
||||||
|
name: string;
|
||||||
|
artists: SpotifyArtist[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SpotifyMessage {
|
||||||
|
is_playing: boolean;
|
||||||
|
item: SpotifyItem | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SpotifyClient {
|
||||||
|
private readonly url: string;
|
||||||
|
private readonly elementId: string;
|
||||||
|
private element: HTMLElement | null = null;
|
||||||
|
private ws: WebSocket | null = null;
|
||||||
|
private reconnectTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
private reconnectAttempts: number = 0;
|
||||||
|
|
||||||
|
private readonly RECONNECT_BASE_DELAY = 1000;
|
||||||
|
private readonly RECONNECT_MAX_DELAY = 30000;
|
||||||
|
|
||||||
|
constructor(url: string, elementId: string) {
|
||||||
|
this.url = url;
|
||||||
|
this.elementId = elementId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public start(): void {
|
||||||
|
this.element = document.getElementById(this.elementId);
|
||||||
|
|
||||||
|
if (!this.element) {
|
||||||
|
console.warn(`Spotify-WS: Element #${this.elementId} not found. Retrying in 1s...`);
|
||||||
|
setTimeout(() => this.start(), 1000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
private connect(): void {
|
||||||
|
if (this.ws && (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("Spotify-WS: Connecting...");
|
||||||
|
this.ws = new WebSocket(this.url);
|
||||||
|
|
||||||
|
this.ws.onopen = () => {
|
||||||
|
console.log("Spotify-WS: Connected");
|
||||||
|
this.reconnectAttempts = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.ws.onmessage = (event: MessageEvent) => {
|
||||||
|
this.updateDOM(event.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.ws.onclose = (event: CloseEvent) => {
|
||||||
|
this.ws = null;
|
||||||
|
if (!event.wasClean) {
|
||||||
|
this.scheduleReconnect();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.ws.onerror = (error: Event) => {
|
||||||
|
console.error("Spotify-WS Error:", error);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private scheduleReconnect(): void {
|
||||||
|
if (this.reconnectTimeout) {
|
||||||
|
clearTimeout(this.reconnectTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
const delay = Math.min(
|
||||||
|
this.RECONNECT_MAX_DELAY,
|
||||||
|
this.RECONNECT_BASE_DELAY * Math.pow(2, this.reconnectAttempts)
|
||||||
|
) * (0.8 + Math.random() * 0.4);
|
||||||
|
|
||||||
|
console.log(`Spotify-WS: Reconnecting in ${Math.round(delay / 1000)}s...`);
|
||||||
|
|
||||||
|
this.reconnectAttempts++;
|
||||||
|
this.reconnectTimeout = setTimeout(() => this.connect(), delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateDOM(data: string): void {
|
||||||
|
if (!this.element) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const payload: SpotifyMessage = JSON.parse(data);
|
||||||
|
|
||||||
|
if (payload.is_playing && payload.item) {
|
||||||
|
const artists = payload.item.artists.map((a) => a.name).join(", ");
|
||||||
|
const text = `${payload.item.name} - ${artists}`;
|
||||||
|
|
||||||
|
if (this.element.textContent !== text) {
|
||||||
|
this.element.textContent = text;
|
||||||
|
this.element.title = `Playing: ${payload.item.name}`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.element.textContent = "";
|
||||||
|
this.element.title = "";
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Spotify-WS: Invalid JSON received", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
-9
@@ -1,19 +1,20 @@
|
|||||||
---
|
---
|
||||||
import Layout from '../layouts/layout.astro';
|
import Layout from '../layouts/layout.astro'
|
||||||
import Nowplaying from '../components/nowplaying.astro';
|
import Nowplaying from '../components/nowplaying.astro'
|
||||||
import Whoami from '../components/whoami.astro';
|
import Whoami from '../components/whoami.astro'
|
||||||
import Socials from '../components/socials.astro';
|
import Socials from '../components/socials.astro'
|
||||||
import Projects from '../components/projects.astro';
|
import Projects from '../components/projects.astro'
|
||||||
import Setup from '../components/setup.astro';
|
import Setup from '../components/setup.astro'
|
||||||
|
|
||||||
const title = "albert";
|
const title = 'albert'
|
||||||
const description = "A 21-year-old developer and tech enthusiast. I enjoy working on my homelab and coding in TypeScript and Go.";
|
const description =
|
||||||
|
'A 21-year-old developer and tech enthusiast. I enjoy working on my homelab and coding in TypeScript and Go.'
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout title={title} description={description}>
|
<Layout title={title} description={description}>
|
||||||
<h1>[{title}]</h1>
|
<h1>[{title}]</h1>
|
||||||
<Nowplaying />
|
<Nowplaying />
|
||||||
<hr>
|
<hr />
|
||||||
<Whoami />
|
<Whoami />
|
||||||
<Socials />
|
<Socials />
|
||||||
<Projects />
|
<Projects />
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
export class SpotifyClient {
|
|
||||||
constructor(url, elementId) {
|
|
||||||
this.url = url;
|
|
||||||
this.elementId = elementId;
|
|
||||||
this.element = document.getElementById(this.elementId);
|
|
||||||
|
|
||||||
this.ws = null;
|
|
||||||
this.reconnectTimeout = null;
|
|
||||||
this.reconnectAttempts = 0;
|
|
||||||
|
|
||||||
this.RECONNECT_BASE_DELAY = 1000;
|
|
||||||
this.RECONNECT_MAX_DELAY = 30000;
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
|
||||||
if (!this.element) {
|
|
||||||
console.error(`Spotify-WS: Element with ID "${this.elementId}" not found.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
connect() {
|
|
||||||
if (this.ws) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Spotify-WS: Connecting...");
|
|
||||||
this.ws = new WebSocket(this.url);
|
|
||||||
|
|
||||||
this.ws.onopen = () => {
|
|
||||||
console.log("Spotify-WS: Connection established.");
|
|
||||||
this.reconnectAttempts = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.ws.onmessage = (event) => {
|
|
||||||
this.updateDOM(event.data);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.ws.onclose = (event) => {
|
|
||||||
if (!event.wasClean) {
|
|
||||||
console.warn("Spotify-WS: Connection closed unexpectedly. Attempting to reconnect...");
|
|
||||||
this.reconnect();
|
|
||||||
} else {
|
|
||||||
console.log("Spotify-WS: Connection closed cleanly.");
|
|
||||||
}
|
|
||||||
this.ws = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.ws.onerror = (error) => {
|
|
||||||
console.error("Spotify-WS: An error occurred:", error);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
reconnect() {
|
|
||||||
const delay = Math.min(
|
|
||||||
this.RECONNECT_MAX_DELAY,
|
|
||||||
this.RECONNECT_BASE_DELAY * Math.pow(2, this.reconnectAttempts)
|
|
||||||
) * (0.8 + Math.random() * 0.4);
|
|
||||||
|
|
||||||
console.log(`Spotify-WS: Reconnecting in ${Math.round(delay / 1000)}s...`);
|
|
||||||
|
|
||||||
this.reconnectAttempts++;
|
|
||||||
this.reconnectTimeout = setTimeout(() => this.connect(), delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDOM(data) {
|
|
||||||
try {
|
|
||||||
const payload = JSON.parse(data);
|
|
||||||
if (payload.is_playing && payload.item) {
|
|
||||||
const artists = payload.item.artists.map(a => a.name).join(", ");
|
|
||||||
this.element.textContent = `${payload.item.name} - ${artists}`;
|
|
||||||
} else {
|
|
||||||
this.element.textContent = "";
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Spotify-WS: Failed to parse message data.", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -53,6 +53,16 @@ h2 {
|
|||||||
margin-block: 1.5rem 0.5rem;
|
margin-block: 1.5rem 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: var(--hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
a:focus-visible {
|
a:focus-visible {
|
||||||
outline: none;
|
outline: none;
|
||||||
box-shadow: 0 0 0 3px var(--focus-shadow-color);
|
box-shadow: 0 0 0 3px var(--focus-shadow-color);
|
||||||
@@ -64,6 +74,58 @@ hr {
|
|||||||
border-top: 4px solid var(--primary-color);
|
border-top: 4px solid var(--primary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.details-list details {
|
||||||
|
font-family: "JetBrains Mono", monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-list summary {
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
color: var(--primary-color);
|
||||||
|
font-size: 1.3em;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-block: 1.5rem 0.5rem;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-list summary::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-list summary::after {
|
||||||
|
content: ' ▼';
|
||||||
|
font-size: 0.6em;
|
||||||
|
opacity: 0.7;
|
||||||
|
margin-inline-start: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-list details[open]>summary::after {
|
||||||
|
content: ' ▲';
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-list details>div {
|
||||||
|
margin-top: 1rem;
|
||||||
|
margin-inline-start: var(--spacing-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-list dt {
|
||||||
|
color: var(--primary-color);
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.17em;
|
||||||
|
margin-inline-start: var(--spacing-md);
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-list dt:first-of-type {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-list dd {
|
||||||
|
margin-inline-start: var(--spacing-lg);
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
body {
|
body {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user