This commit is contained in:
parent
9ea97c4ac6
commit
5f0ac7efdb
7 changed files with 154 additions and 64 deletions
1
global.d.ts
vendored
1
global.d.ts
vendored
|
@ -1,5 +1,4 @@
|
||||||
declare global {
|
declare global {
|
||||||
let __BLOG_POSTS__: string[]
|
let __BLOG_POSTS__: string[]
|
||||||
let __STICKERS__: string[]
|
|
||||||
}
|
}
|
||||||
export { };
|
export { };
|
||||||
|
|
|
@ -15,9 +15,6 @@ export default class Variables extends Plugin {
|
||||||
this.variables["__BLOG_POSTS__"] = JSON.stringify(
|
this.variables["__BLOG_POSTS__"] = JSON.stringify(
|
||||||
fs.readdirSync("./website/blogs").map((z) => z.replace(".md", ""))
|
fs.readdirSync("./website/blogs").map((z) => z.replace(".md", ""))
|
||||||
);
|
);
|
||||||
this.variables["__STICKERS__"] = JSON.stringify(
|
|
||||||
fs.readdirSync("./website/assets/stickers")
|
|
||||||
);
|
|
||||||
const templatePath = path.resolve(__dirname, "../../website/templates");
|
const templatePath = path.resolve(__dirname, "../../website/templates");
|
||||||
if (fs.existsSync(templatePath)) {
|
if (fs.existsSync(templatePath)) {
|
||||||
for (const file of fs.readdirSync(templatePath)) {
|
for (const file of fs.readdirSync(templatePath)) {
|
||||||
|
|
|
@ -18,9 +18,10 @@ a {
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"details skills"
|
"details skills"
|
||||||
"details donations"
|
"details donations"
|
||||||
|
"details lastfm"
|
||||||
"webrings webrings"
|
"webrings webrings"
|
||||||
"status binkies"
|
"status binkies"
|
||||||
"stickers blog";
|
"blog blog";
|
||||||
grid-gap: 5px;
|
grid-gap: 5px;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
grid-template-rows: repeat(25vh, 6);
|
grid-template-rows: repeat(25vh, 6);
|
||||||
|
@ -57,14 +58,18 @@ a {
|
||||||
background-position: 20px 20px;
|
background-position: 20px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.paper > h1,
|
.paper > :nth-child(1) {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
margin: 0;
|
text-shadow: 0px 0px 4px white;
|
||||||
padding: 0;
|
|
||||||
}
|
}
|
||||||
.details {
|
.details {
|
||||||
grid-area: details;
|
grid-area: details;
|
||||||
|
@ -72,20 +77,19 @@ h6 {
|
||||||
.donations {
|
.donations {
|
||||||
grid-area: donations;
|
grid-area: donations;
|
||||||
}
|
}
|
||||||
.status {
|
.lastfm {
|
||||||
grid-area: status;
|
grid-area: lastfm;
|
||||||
}
|
|
||||||
.stickers {
|
|
||||||
grid-area: stickers;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: row;
|
||||||
max-height: 75vh;
|
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
}
|
}
|
||||||
.stickers > img,
|
.lastfm > div {
|
||||||
.stickers > video {
|
display: flex;
|
||||||
object-fit: scale-down;
|
flex-direction: column;
|
||||||
height: 100%;
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.status {
|
||||||
|
grid-area: status;
|
||||||
}
|
}
|
||||||
.skills {
|
.skills {
|
||||||
grid-area: skills;
|
grid-area: skills;
|
||||||
|
@ -103,7 +107,7 @@ h6 {
|
||||||
}
|
}
|
||||||
.blog {
|
.blog {
|
||||||
grid-area: blog;
|
grid-area: blog;
|
||||||
max-height: 75vh;
|
min-height: 75vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
webring-container {
|
webring-container {
|
||||||
|
@ -117,20 +121,16 @@ webring-container {
|
||||||
"status"
|
"status"
|
||||||
"skills "
|
"skills "
|
||||||
"donations "
|
"donations "
|
||||||
|
"lastfm"
|
||||||
"webrings"
|
"webrings"
|
||||||
"blog "
|
|
||||||
"blog "
|
|
||||||
"binkies"
|
"binkies"
|
||||||
"stickers";
|
"blog "
|
||||||
|
"blog ";
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
scrollbar-width: auto !important;
|
scrollbar-width: auto !important;
|
||||||
}
|
}
|
||||||
.stickers {
|
|
||||||
flex-direction: row;
|
|
||||||
max-height: auto;
|
|
||||||
}
|
|
||||||
.blog {
|
.blog {
|
||||||
max-height: unset;
|
max-height: unset;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,10 @@
|
||||||
__TEMPLATE_HEAD__
|
__TEMPLATE_HEAD__
|
||||||
|
|
||||||
|
<script type="module" src="scripts/util.js"></script>
|
||||||
<script type="module" src="scripts/blog.js"></script>
|
<script type="module" src="scripts/blog.js"></script>
|
||||||
<script type="module" src="scripts/status_cafe.js"></script>
|
<script type="module" src="scripts/status_cafe.js"></script>
|
||||||
<script type="module" src="scripts/particles.js"></script>
|
<script type="module" src="scripts/particles.js"></script>
|
||||||
|
<script type="module" src="scripts/lastfm.js"></script>
|
||||||
<script>
|
|
||||||
window.addEventListener("load", () => {
|
|
||||||
const stickers = document.getElementById("stickers");
|
|
||||||
const binkies = document.getElementById("binkies");
|
|
||||||
if (!stickers) return;
|
|
||||||
|
|
||||||
for (const sticker of __STICKERS__) {
|
|
||||||
const img = document.createElement("img");
|
|
||||||
img.src = "assets/stickers/" + sticker;
|
|
||||||
stickers.appendChild(img);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="assets/style.css">
|
<link rel="stylesheet" href="assets/style.css">
|
||||||
</head>
|
</head>
|
||||||
|
@ -90,7 +78,6 @@ __TEMPLATE_HEAD__
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="paper blog" id="root"></div>
|
<div class="paper blog" id="root"></div>
|
||||||
<div class="paper stickers" id="stickers"></div>
|
|
||||||
<div class="paper status" id="status"></div>
|
<div class="paper status" id="status"></div>
|
||||||
<div class="paper binkies">
|
<div class="paper binkies">
|
||||||
<a href="https://sad.ovh">
|
<a href="https://sad.ovh">
|
||||||
|
@ -118,7 +105,9 @@ __TEMPLATE_HEAD__
|
||||||
</webring-container>
|
</webring-container>
|
||||||
|
|
||||||
<iframe id="bucket-webring" style="width: 100%; height: 3rem; border: none;margin-top:5px;" src="https://webring.bucketfish.me/embed.html?name=☹️☹️☹️.ovh&lightmode=true"></iframe>
|
<iframe id="bucket-webring" style="width: 100%; height: 3rem; border: none;margin-top:5px;" src="https://webring.bucketfish.me/embed.html?name=☹️☹️☹️.ovh&lightmode=true"></iframe>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="paper lastfm" id="lastfm">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
104
website/scripts/lastfm.ts
Normal file
104
website/scripts/lastfm.ts
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
import { timeAgo } from "./util";
|
||||||
|
|
||||||
|
interface GetRecentTracksType {
|
||||||
|
recenttracks: {
|
||||||
|
track: Array<{
|
||||||
|
artist: {
|
||||||
|
mbid: string;
|
||||||
|
"#text": string;
|
||||||
|
};
|
||||||
|
streamable: string;
|
||||||
|
image: Array<{
|
||||||
|
size: string;
|
||||||
|
"#text": string;
|
||||||
|
}>;
|
||||||
|
mbid: string;
|
||||||
|
album: {
|
||||||
|
mbid: string;
|
||||||
|
"#text": string;
|
||||||
|
};
|
||||||
|
name: string;
|
||||||
|
"@attr"?: {
|
||||||
|
nowplaying: string;
|
||||||
|
};
|
||||||
|
url: string;
|
||||||
|
date?: {
|
||||||
|
uts: string;
|
||||||
|
"#text": string;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
"@attr": {
|
||||||
|
user: string;
|
||||||
|
totalPages: string;
|
||||||
|
page: string;
|
||||||
|
perPage: string;
|
||||||
|
total: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
//https://ws.audioscrobbler.com/2.0/
|
||||||
|
//?api_key=816cfe50ddeeb73c9987b85de5c19e71
|
||||||
|
//&method=User.getrecenttracks
|
||||||
|
//&user=yourfriendoss
|
||||||
|
//&format=json
|
||||||
|
//&limit=1
|
||||||
|
|
||||||
|
const lastFM = {
|
||||||
|
apiKey: "816cfe50ddeeb73c9987b85de5c19e71",
|
||||||
|
constructUrl: (method: string, parameters: Record<string, any>) => {
|
||||||
|
const defaultParams = {
|
||||||
|
format: "json",
|
||||||
|
api_key: lastFM.apiKey,
|
||||||
|
method
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(parameters, defaultParams);
|
||||||
|
|
||||||
|
return "https://ws.audioscrobbler.com/2.0/?" + new URLSearchParams(parameters).toString()
|
||||||
|
},
|
||||||
|
getRecentTracks: async (user: string): Promise<GetRecentTracksType> => {
|
||||||
|
const request = await fetch(lastFM.constructUrl("User.getrecenttracks", {user, limit: 1}));
|
||||||
|
return await request.json();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const recentTracks = await lastFM.getRecentTracks("yourfriendoss");
|
||||||
|
const track = recentTracks.recenttracks.track[0];
|
||||||
|
if(!track) return;
|
||||||
|
const lastFMElement = document.getElementById("lastfm")!
|
||||||
|
lastFMElement.innerHTML = "";
|
||||||
|
const img = document.createElement("img");
|
||||||
|
img.width = 150
|
||||||
|
img.height = 150
|
||||||
|
img.src = track.image.at(-1)?.["#text"]!;
|
||||||
|
lastFMElement?.appendChild(img);
|
||||||
|
|
||||||
|
const div = document.createElement("div");
|
||||||
|
const h1 = document.createElement("h1");
|
||||||
|
const spanArtist = document.createElement("span");
|
||||||
|
const spanTitle = document.createElement("span");
|
||||||
|
|
||||||
|
h1.innerText = track["@attr"]?.nowplaying ? "listening to Now" : "listened to " + timeAgo((+track.date?.uts!)*1000)
|
||||||
|
h1.style.margin = "0";
|
||||||
|
|
||||||
|
spanArtist.style.fontSize = "xx-large";
|
||||||
|
spanTitle.style.fontSize = "x-large";
|
||||||
|
|
||||||
|
spanArtist.innerText = track.artist["#text"];
|
||||||
|
spanTitle.innerText = track.name;
|
||||||
|
|
||||||
|
div.appendChild(h1)
|
||||||
|
div.appendChild(spanArtist)
|
||||||
|
div.appendChild(spanTitle)
|
||||||
|
lastFMElement?.appendChild(div);
|
||||||
|
|
||||||
|
|
||||||
|
/* <img src="https://lastfm.freetls.fastly.net/i/u/300x300/400560416eb0c37bbc407cd4279c7899.jpg" width="150" height="150">
|
||||||
|
<div>
|
||||||
|
<h1>listening to now</h1>
|
||||||
|
<span style="font-size:x-large">Bladee</span>
|
||||||
|
<span style="font-size:x-large">Open your Eyes (hymn)</span>
|
||||||
|
</div>*/
|
||||||
|
|
||||||
|
})();
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { timeAgo } from "./util";
|
||||||
|
|
||||||
interface LatestStatus {
|
interface LatestStatus {
|
||||||
username: string;
|
username: string;
|
||||||
status: string;
|
status: string;
|
||||||
|
@ -7,28 +9,6 @@ interface LatestStatus {
|
||||||
}
|
}
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
function timeAgo(input: number | Date) {
|
|
||||||
const date = input instanceof Date ? input : new Date(input);
|
|
||||||
const formatter = new Intl.RelativeTimeFormat("en");
|
|
||||||
const ranges = [
|
|
||||||
["years", 3600 * 24 * 365],
|
|
||||||
["months", 3600 * 24 * 30],
|
|
||||||
["weeks", 3600 * 24 * 7],
|
|
||||||
["days", 3600 * 24],
|
|
||||||
["hours", 3600],
|
|
||||||
["minutes", 60],
|
|
||||||
["seconds", 1],
|
|
||||||
] as const;
|
|
||||||
const secondsElapsed = (date.getTime() - Date.now()) / 1000;
|
|
||||||
|
|
||||||
for (const [rangeType, rangeVal] of ranges) {
|
|
||||||
if (rangeVal < Math.abs(secondsElapsed)) {
|
|
||||||
const delta = secondsElapsed / rangeVal;
|
|
||||||
return formatter.format(Math.round(delta), rangeType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const req = await fetch("https://status.cafe/users/sophie.atom");
|
const req = await fetch("https://status.cafe/users/sophie.atom");
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
const doc = parser.parseFromString(await req.text(), "text/xml");
|
const doc = parser.parseFromString(await req.text(), "text/xml");
|
||||||
|
|
21
website/scripts/util.ts
Normal file
21
website/scripts/util.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
export function timeAgo(input: number | Date) {
|
||||||
|
const date = input instanceof Date ? input : new Date(input);
|
||||||
|
const formatter = new Intl.RelativeTimeFormat("en");
|
||||||
|
const ranges = [
|
||||||
|
["years", 3600 * 24 * 365],
|
||||||
|
["months", 3600 * 24 * 30],
|
||||||
|
["weeks", 3600 * 24 * 7],
|
||||||
|
["days", 3600 * 24],
|
||||||
|
["hours", 3600],
|
||||||
|
["minutes", 60],
|
||||||
|
["seconds", 1],
|
||||||
|
] as const;
|
||||||
|
const secondsElapsed = (date.getTime() - Date.now()) / 1000;
|
||||||
|
|
||||||
|
for (const [rangeType, rangeVal] of ranges) {
|
||||||
|
if (rangeVal < Math.abs(secondsElapsed)) {
|
||||||
|
const delta = secondsElapsed / rangeVal;
|
||||||
|
return formatter.format(Math.round(delta), rangeType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue