diff --git a/app/components/Artist/Card.vue b/app/components/Artist/Card.vue new file mode 100644 index 0000000..cfaa111 --- /dev/null +++ b/app/components/Artist/Card.vue @@ -0,0 +1,41 @@ + + + + + + + {{ props.artist.name }} + + + + + diff --git a/app/pages/library/artists.vue b/app/pages/library/artists.vue index b47c9c9..6eb7763 100644 --- a/app/pages/library/artists.vue +++ b/app/pages/library/artists.vue @@ -1,5 +1,63 @@ - + + + Artists + + + Loading your artists… + + + Your artists could not be loaded. + Try again + + + No artists in your saved songs yet. + + + + + + + + diff --git a/server/api/user/artists.get.ts b/server/api/user/artists.get.ts new file mode 100644 index 0000000..bc56821 --- /dev/null +++ b/server/api/user/artists.get.ts @@ -0,0 +1,41 @@ +import { setResponseStatus } from "h3" +import { Artist } from "#server/models/artist" +import { Track } from "#server/models/track" +import { User } from "#server/models/user" +import { defineAuthenticatedEventHandler, requireAuthenticatedUser } from "#server/utils/auth" + +export default defineAuthenticatedEventHandler(async (event) => { + const authenticatedUser = await requireAuthenticatedUser(event) + const user = await User.findById(authenticatedUser._id).select("savedTracks") + if (!user) { + setResponseStatus(event, 404) + return { message: "User not found" } + } + + if (!user.savedTracks.length) return [] + + const tracks = await Track.find({ _id: { $in: user.savedTracks } }) + .select("_id artists") + .exec() + const artistsByTrack = new Map( + tracks.map((track: any) => [ + track._id.toString(), + (track.artists ?? []).map((artist: any) => artist.toString()), + ]), + ) + const artistIds: string[] = [] + const seen = new Set() + for (const savedTrack of user.savedTracks) { + for (const artistId of artistsByTrack.get(savedTrack.toString()) ?? []) { + if (!seen.has(artistId)) { + seen.add(artistId) + artistIds.push(artistId) + } + } + } + if (!artistIds.length) return [] + + const artists = await Artist.find({ _id: { $in: artistIds } }).exec() + const artistsById = new Map(artists.map((artist: any) => [artist._id.toString(), artist])) + return artistIds.map((id) => artistsById.get(id)).filter((artist) => artist != null) +}) diff --git a/server/utils/openapi.ts b/server/utils/openapi.ts index 6a9febe..39710d0 100644 --- a/server/utils/openapi.ts +++ b/server/utils/openapi.ts @@ -39,6 +39,7 @@ const operations: ApiOperation[] = [ { method: "get", path: "/all/tracks", tag: "All", summary: "Get all tracks", auth: true, response: "TrackList" }, { method: "get", path: "/search", tag: "Search", summary: "Search tracks, albums and artists", auth: true, query: ["q", "type", "limit"], response: "SearchResults" }, { method: "get", path: "/user", tag: "User", summary: "Get the current user", auth: true, response: "User" }, + { method: "get", path: "/user/artists", tag: "User", summary: "Get artists represented by saved tracks", auth: true, response: "ArtistList" }, { method: "get", path: "/user/tracks", tag: "User", summary: "Get saved tracks", auth: true, response: "TrackList" }, { method: "patch", path: "/user/tracks", tag: "User", summary: "Save a track", auth: true, query: ["id"], response: "EmptyObject" }, { method: "delete", path: "/user/tracks", tag: "User", summary: "Remove saved tracks", auth: true, query: ["id", "ids"], response: "EmptyObject" },
{{ props.artist.name }}
+ Loading your artists… +
Your artists could not be loaded.
+ No artists in your saved songs yet. +