- Hello from Hono + React!
+ Embed a Rocksky Scrobble
This is server-side rendered with Tailwind CSS
diff --git a/apps/embed/src/styles.css b/apps/embed/src/styles.css
index 6f4f1218..df46fe79 100644
--- a/apps/embed/src/styles.css
+++ b/apps/embed/src/styles.css
@@ -2,6 +2,12 @@
@tailwind components;
@tailwind utilities;
+@plugin "flyonui";
+@plugin "@iconify/tailwind4";
+
+@import "../node_modules/flyonui/variants.css";
+@source "../node_modules/flyonui/dist/index.js";
+
@font-face {
font-family: "RockfordSansLight";
src: url("/public/fonts/RockfordSans-Light.otf") format("opentype");
@@ -69,6 +75,7 @@
--font-rockford-light: "RockfordSansLight";
--color-text-muted: #42576ca6;
--color-genre: #3f03fb;
+ --color-avatar-background: #f4f0ff;
}
.dark {
diff --git a/apps/embed/src/types/album.ts b/apps/embed/src/types/album.ts
new file mode 100644
index 00000000..2e3a22e9
--- /dev/null
+++ b/apps/embed/src/types/album.ts
@@ -0,0 +1,11 @@
+export type Album = {
+ id: string;
+ title: string;
+ artist: string;
+ albumArt: string;
+ year: number | null;
+ uri: string;
+ artistUri: string;
+ playCount: number;
+ uniqueListeners: number;
+};
diff --git a/apps/embed/src/types/artist.ts b/apps/embed/src/types/artist.ts
index ce01d7ed..33c34d75 100644
--- a/apps/embed/src/types/artist.ts
+++ b/apps/embed/src/types/artist.ts
@@ -1,5 +1,9 @@
export type Artist = {
id: string;
name: string;
- tags: string[];
+ playCount: number;
+ uniqueListeners: number;
+ picture: string | null;
+ uri: string;
+ tags: string[] | null;
};
diff --git a/apps/embed/src/types/scrobble.ts b/apps/embed/src/types/scrobble.ts
new file mode 100644
index 00000000..95b8b776
--- /dev/null
+++ b/apps/embed/src/types/scrobble.ts
@@ -0,0 +1,16 @@
+export type Scrobble = {
+ id: string;
+ trackId: string;
+ title: string;
+ artist: string;
+ albumArtist: string;
+ albumArt: string;
+ album: string;
+ handle: string;
+ did: string;
+ uri: string;
+ trackUri: string;
+ artistUri: string;
+ albumUri: string;
+ createdAt: string;
+};
diff --git a/apps/embed/src/types/track.ts b/apps/embed/src/types/track.ts
index e2c4d4de..64e94f2f 100644
--- a/apps/embed/src/types/track.ts
+++ b/apps/embed/src/types/track.ts
@@ -1,11 +1,16 @@
export type Track = {
id: string;
title: string;
- album: string;
- albumArt: string;
- albumUri: string;
artist: string;
- artistUri: string;
albumArtist: string;
+ albumArt: string;
+ trackNumber: number;
+ duration: number;
+ discNumber: number;
uri: string;
+ artistUri: string;
+ albumUri: string;
+ album: string;
+ playCount: number;
+ uniqueListeners: number;
};
diff --git a/apps/embed/src/xrpc/getRecentScrobbles.ts b/apps/embed/src/xrpc/getRecentScrobbles.ts
new file mode 100644
index 00000000..992d92c5
--- /dev/null
+++ b/apps/embed/src/xrpc/getRecentScrobbles.ts
@@ -0,0 +1,18 @@
+import { ROCKSKY_API_URL } from "../consts";
+import type { Scrobble } from "../types/scrobble";
+
+export default async function getRecentScrobbles(handle: string) {
+ const url = new URL(
+ `${ROCKSKY_API_URL}/xrpc/app.rocksky.actor.getActorScrobbles`,
+ );
+ url.searchParams.append("did", handle);
+ url.searchParams.append("limit", "20");
+ const res = await fetch(url);
+
+ if (!res.ok) {
+ return { scrobbles: [], ok: res.ok };
+ }
+
+ const { scrobbles } = (await res.json()) as { scrobbles: Scrobble[] };
+ return { scrobbles, ok: res.ok };
+}
diff --git a/apps/embed/src/xrpc/getTopAlbums.ts b/apps/embed/src/xrpc/getTopAlbums.ts
new file mode 100644
index 00000000..d7759585
--- /dev/null
+++ b/apps/embed/src/xrpc/getTopAlbums.ts
@@ -0,0 +1,18 @@
+import { ROCKSKY_API_URL } from "../consts";
+import type { Album } from "../types/album";
+
+export default async function getTopAlbums(handle: string) {
+ const url = new URL(
+ `${ROCKSKY_API_URL}/xrpc/app.rocksky.actor.getActorAlbums`,
+ );
+ url.searchParams.append("did", handle);
+ url.searchParams.append("limit", "20");
+ const res = await fetch(url);
+
+ if (!res.ok) {
+ return { topAlbums: [], ok: res.ok };
+ }
+
+ const { albums } = (await res.json()) as { albums: Album[] };
+ return { topAlbums: albums, ok: res.ok };
+}
diff --git a/apps/embed/src/xrpc/getTopArtists.ts b/apps/embed/src/xrpc/getTopArtists.ts
new file mode 100644
index 00000000..1d69e13e
--- /dev/null
+++ b/apps/embed/src/xrpc/getTopArtists.ts
@@ -0,0 +1,23 @@
+import { ROCKSKY_API_URL } from "../consts";
+import getLastDays from "../lib/getLastDays";
+import type { Artist } from "../types/artist";
+
+export default async function getTopArtists(handle: string) {
+ const [start, end] = getLastDays(7);
+ const url = new URL(
+ `${ROCKSKY_API_URL}/xrpc/app.rocksky.actor.getActorArtists`,
+ );
+ url.searchParams.append("did", handle);
+ url.searchParams.append("startDate", start.toISOString());
+ url.searchParams.append("endDate", end.toISOString());
+ url.searchParams.append("limit", "20");
+
+ const res = await fetch(url);
+
+ if (!res.ok) {
+ return { topArtists: [], ok: res.ok };
+ }
+
+ const { artists } = (await res.json()) as { artists: Artist[] };
+ return { topArtists: artists, ok: res.ok };
+}
diff --git a/apps/embed/src/xrpc/getTopTracks.ts b/apps/embed/src/xrpc/getTopTracks.ts
new file mode 100644
index 00000000..439a8d56
--- /dev/null
+++ b/apps/embed/src/xrpc/getTopTracks.ts
@@ -0,0 +1,19 @@
+import { ROCKSKY_API_URL } from "../consts";
+import type { Track } from "../types/track";
+
+export default async function getTopAlbums(handle: string) {
+ const url = new URL(
+ `${ROCKSKY_API_URL}/xrpc/app.rocksky.actor.getActorSongs`,
+ );
+ url.searchParams.append("did", handle);
+ url.searchParams.append("limit", "20");
+
+ const res = await fetch(url);
+
+ if (!res.ok) {
+ return { topTracks: [], ok: res.ok };
+ }
+
+ const { tracks } = (await res.json()) as { tracks: Track[] };
+ return { topTracks: tracks, ok: res.ok };
+}