Skip to content

Commit 92a2a3e

Browse files
committed
Add functionality to search for albums across playlists
1 parent 75778ff commit 92a2a3e

File tree

4 files changed

+103
-9
lines changed

4 files changed

+103
-9
lines changed

backend/src/controllers/music_data.py

+21
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
get_recent_user_playlists,
1010
get_user_playlists,
1111
search_playlist_names,
12+
search_playlists_by_albums,
1213
update_playlist_info,
1314
)
1415
from src.dataclasses.playlist import Playlist
@@ -20,6 +21,26 @@ def music_controller(spotify: SpotifyClient):
2021
name="music_controller", import_name=__name__, url_prefix="/music"
2122
)
2223

24+
@music_controller.route("playlist_album_search")
25+
def album_search():
26+
user_id = request.cookies.get("user_id")
27+
limit = request.args.get("limit", type=int)
28+
offset = request.args.get("offset", type=int)
29+
search = request.args.get("search")
30+
sort_by = request.args.get("sort_by")
31+
desc = request.args.get("desc") == "True"
32+
return jsonify(
33+
search_playlists_by_albums(
34+
user_id=user_id,
35+
limit=limit,
36+
offset=offset,
37+
search=search,
38+
sort_by=sort_by,
39+
desc=desc,
40+
as_dicts=True,
41+
)
42+
)
43+
2344
@music_controller.route("playlists")
2445
def index():
2546
user_id = request.cookies.get("user_id")

backend/src/database/crud/playlist.py

+49
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,55 @@ def get_recent_user_playlists(
155155
return playlists
156156

157157

158+
def search_playlists_by_albums(
159+
user_id: str,
160+
limit: Optional[int] = None,
161+
offset: Optional[int] = None,
162+
search: Optional[str] = None,
163+
sort_by: Optional[str] = None,
164+
desc: bool = True,
165+
as_dicts: bool = False,
166+
) -> List[DbPlaylist]:
167+
# Subquery to find albums where the album name or artist name contains the search query
168+
albums_with_artists_query = (
169+
DbAlbum.select(DbAlbum.id).join(AlbumArtistRelationship).join(DbArtist)
170+
)
171+
172+
if search:
173+
albums_with_artists_query = albums_with_artists_query.where(
174+
(DbAlbum.name.contains(search)) | (DbArtist.name.contains(search))
175+
)
176+
177+
# Query to find playlists containing the matching albums
178+
playlists_with_albums_query = (
179+
DbPlaylist.select(DbPlaylist)
180+
.join(PlaylistAlbumRelationship)
181+
.join(DbAlbum)
182+
.where(DbAlbum.id.in_(albums_with_artists_query))
183+
)
184+
185+
if sort_by:
186+
sort_field = getattr(DbPlaylist, sort_by)
187+
if desc:
188+
playlists_with_albums_query = playlists_with_albums_query.order_by(
189+
sort_field.desc()
190+
)
191+
else:
192+
playlists_with_albums_query = playlists_with_albums_query.order_by(
193+
sort_field.asc()
194+
)
195+
196+
if limit is not None:
197+
playlists_with_albums_query = playlists_with_albums_query.limit(limit)
198+
if offset is not None:
199+
playlists_with_albums_query = playlists_with_albums_query.offset(offset)
200+
201+
if as_dicts:
202+
return list(playlists_with_albums_query.dicts())
203+
else:
204+
return list(playlists_with_albums_query.execute())
205+
206+
158207
def get_playlist_albums(playlist_id: str) -> List[DbAlbum]:
159208
query = (
160209
DbAlbum.select()

frontend/src/MainPage.tsx

+18-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { FC, useState } from "react";
22
import { useQuery } from "@tanstack/react-query";
3-
import { getPlaylists, getRecentPlaylists } from "./api";
3+
import { getRecentPlaylists, searchPlaylistsByAlbums } from "./api";
44
import { Playlist } from "./interfaces/Playlist";
55
import Box from "./components/Box";
66
import AddPlaylistForm from "./AddPlaylistForm";
@@ -16,26 +16,36 @@ interface PaginationState {
1616

1717
export const Index: FC = () => {
1818
const { isMobileView } = useWindowSize();
19-
const [searchRecent, setSearchRecent] = useState<string>("")
20-
const [search, setSearch] = useState<string>("")
19+
const [playlistSearch, setPlaylistSearch] = useState<string>("")
20+
const [albumSearch, setAlbumSearch] = useState<string>("")
2121
const [pagination, ] = useState<PaginationState>({
2222
pageIndex: 0,
2323
pageSize: isMobileView ? 8 : 8,
2424
});
2525

26+
const playlistQuery = useQuery<Playlist[]>({
27+
queryKey: ["playlists", pagination, playlistSearch],
28+
queryFn: () => {
29+
return getRecentPlaylists(playlistSearch, pagination.pageIndex, pagination.pageSize);
30+
},
31+
});
2632

27-
const recentQuery = useQuery<Playlist[]>({
28-
queryKey: ["playlists", pagination, search],
33+
const albumQuery = useQuery<Playlist[]>({
34+
queryKey: ["albums", pagination, albumSearch],
2935
queryFn: () => {
30-
return getRecentPlaylists(search, pagination.pageIndex, pagination.pageSize);
36+
return searchPlaylistsByAlbums(albumSearch, pagination.pageIndex, pagination.pageSize);
3137
},
3238
});
3339

3440
return (
3541
<div className="py-4 px-2 space-y-2">
3642
<Box className="space-y-2">
37-
<SearchBar search={searchRecent} setSearch={setSearchRecent}/>
38-
<Carousel slides={(recentQuery.data ?? createUndefinedArray(pagination.pageSize)).map(PlaylistSlide)} />
43+
<SearchBar search={playlistSearch} setSearch={setPlaylistSearch}/>
44+
<Carousel slides={(playlistQuery.data ?? createUndefinedArray(pagination.pageSize)).map(PlaylistSlide)} />
45+
</Box>
46+
<Box className="space-y-2">
47+
<SearchBar search={albumSearch} setSearch={setAlbumSearch}/>
48+
<Carousel slides={(albumQuery.data ?? createUndefinedArray(pagination.pageSize)).map(PlaylistSlide)} />
3949
</Box>
4050
<Box>
4151
<AddPlaylistForm />

frontend/src/api/index.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,20 @@ export const getCurrentUserDetails = async (): Promise<User> => {
4040
);
4141
};
4242

43+
export const searchPlaylistsByAlbums = async (
44+
search: string,
45+
offset: number,
46+
limit: number,
47+
): Promise<Playlist[]> => {
48+
const searchParams = new URLSearchParams();
49+
searchParams.append("limit", String(limit));
50+
searchParams.append("offset", String(offset));
51+
if (search !== "") {searchParams.append("search", search);}
52+
searchParams.toString();
53+
const endpoint = `music/playlist_album_search?${searchParams.toString()}`;
54+
return jsonRequest(endpoint, RequestMethod.GET);
55+
};
56+
4357
export const getRecentPlaylists = async (
4458
search: string,
4559
offset: number,
@@ -49,7 +63,7 @@ export const getRecentPlaylists = async (
4963
searchParams.append("limit", String(limit));
5064
searchParams.append("offset", String(offset));
5165
if (search !== "") {searchParams.append("search", search);}
52-
searchParams.toString(); // "type=all&query=coins"
66+
searchParams.toString();
5367
const endpoint = `music/playlists/recent?${searchParams.toString()}`;
5468
return jsonRequest(endpoint, RequestMethod.GET);
5569
};

0 commit comments

Comments
 (0)