Skip to content

Commit 9e59584

Browse files
committed
Populate track data
1 parent 05ee50a commit 9e59584

File tree

19 files changed

+493
-160
lines changed

19 files changed

+493
-160
lines changed

backend/src/controllers/database.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,19 @@
88
update_album,
99
)
1010
from src.database.crud.genre import create_genre
11+
from src.database.crud.track import (
12+
all_tracks_have_artists,
13+
create_track_or_none,
14+
get_album_tracks,
15+
get_user_albums_with_no_tracks,
16+
)
1117
from src.database.crud.playlist import (
18+
add_playlist_album_index,
1219
create_playlist,
1320
get_playlist_albums,
1421
get_playlist_by_id_or_none,
1522
get_user_playlists,
23+
playlist_has_null_album_indexes,
1624
update_playlist_with_albums,
1725
)
1826
from src.database.crud.user import get_or_create_user
@@ -122,6 +130,74 @@ def populate_user_album_genres():
122130
populate_album_genres_by_user_id(user.id, musicbrainz)
123131
return make_response("User album genres populated", 201)
124132

133+
@database_controller.route("populate_album_tracks", methods=["GET"])
134+
def populate_album_tracks():
135+
access_token = request.cookies.get("spotify_access_token")
136+
user = spotify.get_current_user(access_token)
137+
albums = get_user_albums_with_no_tracks(user.id)
138+
for album in albums:
139+
existing_album_tracks = get_album_tracks(album)
140+
if existing_album_tracks != []:
141+
continue
142+
sleep(0.5)
143+
144+
album_tracks = spotify.get_album(
145+
access_token=access_token, id=album.id
146+
).tracks.items
147+
for track in album_tracks:
148+
create_track_or_none(track, album)
149+
150+
return make_response("User album genres populated", 201)
151+
152+
@database_controller.route("populate_playlist_album_indexes", methods=["GET"])
153+
def populate_playlist_album_indexes():
154+
access_token = request.cookies.get("spotify_access_token")
155+
user = spotify.get_current_user(access_token)
156+
simplified_playlists = spotify.get_all_playlists(
157+
user_id=user.id, access_token=access_token
158+
)
159+
160+
for simplified_playlist in simplified_playlists:
161+
if "Albums" in simplified_playlist.name:
162+
if not playlist_has_null_album_indexes(simplified_playlist.id):
163+
continue
164+
sleep(1)
165+
166+
[playlist, albums] = [
167+
spotify.get_playlist(
168+
access_token=access_token, id=simplified_playlist.id
169+
),
170+
spotify.get_playlist_album_info(
171+
access_token=access_token, id=simplified_playlist.id
172+
),
173+
]
174+
175+
for index, album in enumerate(albums):
176+
add_playlist_album_index(
177+
playlist_id=playlist.id, album_id=album.id, index=index
178+
)
179+
180+
return make_response("Playlist album indexes populated", 201)
181+
182+
@database_controller.route("populate_track_artists", methods=["GET"])
183+
def populate_track_artists():
184+
access_token = request.cookies.get("spotify_access_token")
185+
user = spotify.get_current_user(access_token)
186+
albums = get_user_albums(user.id)
187+
offset = 1800
188+
for index, album in enumerate(albums[offset:]):
189+
if all_tracks_have_artists(album.id):
190+
continue
191+
sleep(0.5)
192+
print(index + offset)
193+
album_tracks = spotify.get_album(
194+
access_token=access_token, id=album.id
195+
).tracks.items
196+
for track in album_tracks:
197+
create_track_or_none(track, album)
198+
199+
return make_response("User album genres populated", 201)
200+
125201
return database_controller
126202

127203

backend/src/controllers/music_data.py

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
1-
from logging import Logger
21
from flask import Blueprint, jsonify, make_response, request
32
from src.database.crud.playlist import (
4-
get_playlist_albums,
53
get_playlist_albums_with_genres,
64
get_playlist_by_id_or_none,
5+
get_playlist_duration,
6+
get_playlist_duration_up_to_track,
7+
get_playlist_track_list,
78
get_recent_user_playlists,
89
get_user_playlists,
910
update_playlist_info,
10-
update_playlist_with_albums,
1111
)
12-
from src.dataclasses.playback_info import PlaybackInfo
13-
from src.dataclasses.playback_request import StartPlaybackRequest
12+
from src.dataclasses.playback_info import PlaylistProgression
1413
from src.dataclasses.playlist import Playlist
1514
from src.spotify import SpotifyClient
16-
import sys
1715

1816

1917
def music_controller(spotify: SpotifyClient):
@@ -50,18 +48,22 @@ def recent_playlists():
5048

5149
return jsonify(
5250
get_recent_user_playlists(
53-
user_id=user_id,
54-
limit=limit,
55-
offset=offset,
56-
search=search
51+
user_id=user_id, limit=limit, offset=offset, search=search
5752
)
5853
)
5954

6055
@music_controller.route("playlist/<id>", methods=["GET"])
6156
def get_playlist(id):
6257
db_playlist = get_playlist_by_id_or_none(id)
6358
if db_playlist is not None:
64-
return jsonify(db_playlist.__data__)
59+
playlist_data = db_playlist.__data__
60+
tracks_data = get_playlist_track_list(id)
61+
return jsonify(
62+
{
63+
**playlist_data,
64+
"tracks": tracks_data,
65+
}
66+
)
6567
else:
6668
access_token = request.cookies.get("spotify_access_token")
6769
playlist = spotify.get_playlist(access_token=access_token, id=id)
@@ -96,6 +98,21 @@ def get_playlist_album_info(id):
9698
)
9799
]
98100

101+
@music_controller.route("playlist/<id>/tracks", methods=["GET"])
102+
def get_playlist_tracks(id):
103+
db_playlist = get_playlist_by_id_or_none(id)
104+
if db_playlist is not None:
105+
track_list = get_playlist_track_list(id)
106+
return jsonify(track_list)
107+
else:
108+
access_token = request.cookies.get("spotify_access_token")
109+
return [
110+
album.model_dump()
111+
for album in spotify.get_playlist_album_info(
112+
access_token=access_token, id=id
113+
)
114+
]
115+
99116
@music_controller.route("find_associated_playlists", methods=["POST"])
100117
def find_associated_playlists():
101118
access_token = request.cookies.get("spotify_access_token")
@@ -108,5 +125,25 @@ def find_associated_playlists():
108125
associated_playlist.model_dump()
109126
for associated_playlist in associated_playlists
110127
]
128+
129+
@music_controller.route("playback", methods=["GET"])
130+
def get_playback_info():
131+
access_token = request.cookies.get("spotify_access_token")
132+
playback_info = spotify.get_my_current_playback(access_token=access_token)
133+
if playback_info is None:
134+
return ("", 204)
135+
if playback_info.playlist_id is not None:
136+
playlist_duration = get_playlist_duration(playback_info.playlist_id)
137+
playlist_progress = get_playlist_duration_up_to_track(playback_info.playlist_id, playback_info.track_id) + playback_info.track_progress
138+
return PlaylistProgression.model_validate(
139+
{
140+
"playlist_id": playback_info.playlist_id,
141+
"playlist_title": playback_info.name,
142+
"playlist_progress": playlist_progress,
143+
"playlist_duration": playlist_duration,
144+
}
145+
)
146+
147+
return playback_info.model_dump_json()
111148

112149
return music_controller

backend/src/controllers/spotify.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def delete_playlist_by_id(id):
5353
spotify.delete_playlist(access_token=access_token, id=id)
5454
return make_response("playlist deleted", 200)
5555

56-
@spotify_controller.route("edit-playlist/<id>", methods=["GET"])
56+
@spotify_controller.route("playlist/<id>", methods=["GET"])
5757
def get_edit_playlist(id):
5858
access_token = request.cookies.get("spotify_access_token")
5959
playlist = spotify.get_playlist(access_token=access_token, id=id)
@@ -101,13 +101,12 @@ def get_playlist_progress():
101101
return ("", 204)
102102
return playlist_progression.model_dump_json()
103103

104-
@spotify_controller.route("find_associated_playlists", methods=["POST"])
105-
def find_associated_playlists():
104+
@spotify_controller.route("find_associated_playlists/<id>", methods=["GET"])
105+
def find_associated_playlists(playlist_id):
106106
access_token = request.cookies.get("spotify_access_token")
107107
user_id = request.cookies.get("user_id")
108-
playlist = Playlist.model_validate(request.json)
109108
associated_playlists = spotify.find_associated_playlists(
110-
user_id=user_id, access_token=access_token, playlist=playlist
109+
user_id=user_id, access_token=access_token, playlist_id=playlist_id
111110
)
112111
return [
113112
associated_playlist.model_dump()

backend/src/database/crud/playlist.py

Lines changed: 121 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
DbArtist,
88
DbGenre,
99
DbPlaylist,
10+
DbTrack,
1011
DbUser,
1112
PlaylistAlbumRelationship,
12-
peewee_model_to_dict,
13+
TrackArtistRelationship,
1314
)
1415
from src.dataclasses.album import Album
1516
from src.dataclasses.playlist import Playlist
1617
from peewee import fn
17-
import re
18-
from datetime import datetime
18+
from playhouse.shortcuts import model_to_dict
1919

2020

2121
def get_playlist_by_id_or_none(id: str):
@@ -180,6 +180,7 @@ def get_playlist_albums_with_genres(playlist_id: str) -> List[dict]:
180180
)
181181
.join(DbPlaylist, on=(PlaylistAlbumRelationship.playlist == DbPlaylist.id))
182182
.where(DbPlaylist.id == playlist_id)
183+
.order_by(PlaylistAlbumRelationship.album_index.asc())
183184
)
184185

185186
# Step 2: Retrieve genres and artists for each album
@@ -211,8 +212,124 @@ def get_playlist_albums_with_genres(playlist_id: str) -> List[dict]:
211212
"release_date": album.release_date,
212213
"total_tracks": album.total_tracks,
213214
"genres": [genre.name for genre in genres],
214-
"artists": [peewee_model_to_dict(artist) for artist in artists],
215+
"artists": [model_to_dict(artist) for artist in artists],
215216
}
216217
albums_with_details.append(album_details)
217218

218219
return albums_with_details
220+
221+
222+
def add_playlist_album_index(album_id: str, playlist_id: str, index: int):
223+
query = PlaylistAlbumRelationship.update(album_index=index).where(
224+
PlaylistAlbumRelationship.album == album_id,
225+
PlaylistAlbumRelationship.playlist == playlist_id,
226+
)
227+
query.execute()
228+
229+
230+
def playlist_has_null_album_indexes(playlist_id: str):
231+
query = PlaylistAlbumRelationship.select().where(
232+
(PlaylistAlbumRelationship.playlist_id == playlist_id)
233+
& (PlaylistAlbumRelationship.album_index.is_null(True))
234+
)
235+
236+
return query.exists()
237+
238+
239+
def get_playlist_track_list(playlist_id: str):
240+
query = (
241+
DbTrack.select(DbTrack, DbAlbum, PlaylistAlbumRelationship.album_index)
242+
.join(DbAlbum, on=(DbTrack.album == DbAlbum.id))
243+
.join(
244+
PlaylistAlbumRelationship,
245+
on=(PlaylistAlbumRelationship.album == DbAlbum.id),
246+
)
247+
.where(PlaylistAlbumRelationship.playlist == playlist_id)
248+
.order_by(
249+
PlaylistAlbumRelationship.album_index.asc(),
250+
DbTrack.disc_number.asc(),
251+
DbTrack.track_number.asc(),
252+
)
253+
)
254+
255+
# Preparing the result as a list of dictionaries
256+
results = []
257+
for track in query:
258+
track_dict = model_to_dict(track, recurse=False)
259+
album_dict = model_to_dict(track.album, recurse=False)
260+
261+
# Fetch all artists related to this track
262+
artists_query = (
263+
DbArtist.select()
264+
.join(
265+
TrackArtistRelationship,
266+
on=(DbArtist.id == TrackArtistRelationship.artist),
267+
)
268+
.where(TrackArtistRelationship.track == track.id)
269+
)
270+
271+
# Serialize the artists as a list of dictionaries
272+
artists_list = [
273+
model_to_dict(artist, recurse=False) for artist in artists_query
274+
]
275+
276+
# Adding the album and artist information to the track
277+
track_dict["album"] = album_dict
278+
track_dict["artists"] = artists_list
279+
280+
# Accessing the album_index from the PlaylistAlbumRelationship
281+
track_dict["album_index"] = track.album.playlistalbumrelationship.album_index
282+
283+
results.append(track_dict)
284+
285+
# Returning the results as JSON
286+
return results
287+
288+
289+
def get_playlist_duration(playlist_id):
290+
# Subquery to get all albums associated with the playlist
291+
albums_in_playlist = PlaylistAlbumRelationship.select(
292+
PlaylistAlbumRelationship.album
293+
).where(PlaylistAlbumRelationship.playlist == playlist_id)
294+
295+
# Query to sum the duration of all tracks in the albums from the playlist
296+
total_duration = (
297+
DbTrack.select(fn.SUM(DbTrack.duration_ms))
298+
.where(DbTrack.album.in_(albums_in_playlist))
299+
.scalar()
300+
) # .scalar() to get the result directly as a number
301+
302+
# If no tracks are found, return 0
303+
return total_duration or 0
304+
305+
306+
def get_playlist_duration_up_to_track(playlist_id, track_id):
307+
# Subquery to get albums in the playlist ordered by album_index
308+
albums_in_playlist = (
309+
PlaylistAlbumRelationship.select(PlaylistAlbumRelationship.album)
310+
.where(PlaylistAlbumRelationship.playlist == playlist_id)
311+
.order_by(PlaylistAlbumRelationship.album_index)
312+
)
313+
314+
# Query to get all tracks in those albums, ordered by album_index, disc_number, and track_number
315+
tracks_in_playlist = (
316+
DbTrack.select(DbTrack.id, DbTrack.duration_ms)
317+
.join(DbAlbum)
318+
.where(DbTrack.album.in_(albums_in_playlist))
319+
.order_by(
320+
PlaylistAlbumRelationship.album_index,
321+
DbTrack.disc_number,
322+
DbTrack.track_number,
323+
)
324+
)
325+
326+
# Variable to accumulate total duration
327+
total_duration = 0
328+
329+
# Loop through tracks and sum duration until we reach the given track_id
330+
for track in tracks_in_playlist:
331+
total_duration += track.duration_ms
332+
if track.id == track_id:
333+
break
334+
335+
return total_duration

0 commit comments

Comments
 (0)