Skip to content

Commit

Permalink
Populate track data
Browse files Browse the repository at this point in the history
  • Loading branch information
CalPinSW committed Sep 12, 2024
1 parent 05ee50a commit 9e59584
Show file tree
Hide file tree
Showing 19 changed files with 493 additions and 160 deletions.
76 changes: 76 additions & 0 deletions backend/src/controllers/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,19 @@
update_album,
)
from src.database.crud.genre import create_genre
from src.database.crud.track import (
all_tracks_have_artists,
create_track_or_none,
get_album_tracks,
get_user_albums_with_no_tracks,
)
from src.database.crud.playlist import (
add_playlist_album_index,
create_playlist,
get_playlist_albums,
get_playlist_by_id_or_none,
get_user_playlists,
playlist_has_null_album_indexes,
update_playlist_with_albums,
)
from src.database.crud.user import get_or_create_user
Expand Down Expand Up @@ -122,6 +130,74 @@ def populate_user_album_genres():
populate_album_genres_by_user_id(user.id, musicbrainz)
return make_response("User album genres populated", 201)

@database_controller.route("populate_album_tracks", methods=["GET"])
def populate_album_tracks():
access_token = request.cookies.get("spotify_access_token")
user = spotify.get_current_user(access_token)
albums = get_user_albums_with_no_tracks(user.id)
for album in albums:
existing_album_tracks = get_album_tracks(album)
if existing_album_tracks != []:
continue
sleep(0.5)

album_tracks = spotify.get_album(
access_token=access_token, id=album.id
).tracks.items
for track in album_tracks:
create_track_or_none(track, album)

return make_response("User album genres populated", 201)

@database_controller.route("populate_playlist_album_indexes", methods=["GET"])
def populate_playlist_album_indexes():
access_token = request.cookies.get("spotify_access_token")
user = spotify.get_current_user(access_token)
simplified_playlists = spotify.get_all_playlists(
user_id=user.id, access_token=access_token
)

for simplified_playlist in simplified_playlists:
if "Albums" in simplified_playlist.name:
if not playlist_has_null_album_indexes(simplified_playlist.id):
continue
sleep(1)

[playlist, albums] = [
spotify.get_playlist(
access_token=access_token, id=simplified_playlist.id
),
spotify.get_playlist_album_info(
access_token=access_token, id=simplified_playlist.id
),
]

for index, album in enumerate(albums):
add_playlist_album_index(
playlist_id=playlist.id, album_id=album.id, index=index
)

return make_response("Playlist album indexes populated", 201)

@database_controller.route("populate_track_artists", methods=["GET"])
def populate_track_artists():
access_token = request.cookies.get("spotify_access_token")
user = spotify.get_current_user(access_token)
albums = get_user_albums(user.id)
offset = 1800
for index, album in enumerate(albums[offset:]):
if all_tracks_have_artists(album.id):
continue
sleep(0.5)
print(index + offset)
album_tracks = spotify.get_album(
access_token=access_token, id=album.id
).tracks.items
for track in album_tracks:
create_track_or_none(track, album)

return make_response("User album genres populated", 201)

return database_controller


Expand Down
59 changes: 48 additions & 11 deletions backend/src/controllers/music_data.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
from logging import Logger
from flask import Blueprint, jsonify, make_response, request
from src.database.crud.playlist import (
get_playlist_albums,
get_playlist_albums_with_genres,
get_playlist_by_id_or_none,
get_playlist_duration,
get_playlist_duration_up_to_track,
get_playlist_track_list,
get_recent_user_playlists,
get_user_playlists,
update_playlist_info,
update_playlist_with_albums,
)
from src.dataclasses.playback_info import PlaybackInfo
from src.dataclasses.playback_request import StartPlaybackRequest
from src.dataclasses.playback_info import PlaylistProgression
from src.dataclasses.playlist import Playlist
from src.spotify import SpotifyClient
import sys


def music_controller(spotify: SpotifyClient):
Expand Down Expand Up @@ -50,18 +48,22 @@ def recent_playlists():

return jsonify(
get_recent_user_playlists(
user_id=user_id,
limit=limit,
offset=offset,
search=search
user_id=user_id, limit=limit, offset=offset, search=search
)
)

@music_controller.route("playlist/<id>", methods=["GET"])
def get_playlist(id):
db_playlist = get_playlist_by_id_or_none(id)
if db_playlist is not None:
return jsonify(db_playlist.__data__)
playlist_data = db_playlist.__data__
tracks_data = get_playlist_track_list(id)
return jsonify(
{
**playlist_data,
"tracks": tracks_data,
}
)
else:
access_token = request.cookies.get("spotify_access_token")
playlist = spotify.get_playlist(access_token=access_token, id=id)
Expand Down Expand Up @@ -96,6 +98,21 @@ def get_playlist_album_info(id):
)
]

@music_controller.route("playlist/<id>/tracks", methods=["GET"])
def get_playlist_tracks(id):
db_playlist = get_playlist_by_id_or_none(id)
if db_playlist is not None:
track_list = get_playlist_track_list(id)
return jsonify(track_list)
else:
access_token = request.cookies.get("spotify_access_token")
return [
album.model_dump()
for album in spotify.get_playlist_album_info(
access_token=access_token, id=id
)
]

@music_controller.route("find_associated_playlists", methods=["POST"])
def find_associated_playlists():
access_token = request.cookies.get("spotify_access_token")
Expand All @@ -108,5 +125,25 @@ def find_associated_playlists():
associated_playlist.model_dump()
for associated_playlist in associated_playlists
]

@music_controller.route("playback", methods=["GET"])
def get_playback_info():
access_token = request.cookies.get("spotify_access_token")
playback_info = spotify.get_my_current_playback(access_token=access_token)
if playback_info is None:
return ("", 204)
if playback_info.playlist_id is not None:
playlist_duration = get_playlist_duration(playback_info.playlist_id)
playlist_progress = get_playlist_duration_up_to_track(playback_info.playlist_id, playback_info.track_id) + playback_info.track_progress
return PlaylistProgression.model_validate(
{
"playlist_id": playback_info.playlist_id,
"playlist_title": playback_info.name,
"playlist_progress": playlist_progress,
"playlist_duration": playlist_duration,
}
)

return playback_info.model_dump_json()

return music_controller
9 changes: 4 additions & 5 deletions backend/src/controllers/spotify.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def delete_playlist_by_id(id):
spotify.delete_playlist(access_token=access_token, id=id)
return make_response("playlist deleted", 200)

@spotify_controller.route("edit-playlist/<id>", methods=["GET"])
@spotify_controller.route("playlist/<id>", methods=["GET"])
def get_edit_playlist(id):
access_token = request.cookies.get("spotify_access_token")
playlist = spotify.get_playlist(access_token=access_token, id=id)
Expand Down Expand Up @@ -101,13 +101,12 @@ def get_playlist_progress():
return ("", 204)
return playlist_progression.model_dump_json()

@spotify_controller.route("find_associated_playlists", methods=["POST"])
def find_associated_playlists():
@spotify_controller.route("find_associated_playlists/<id>", methods=["GET"])
def find_associated_playlists(playlist_id):
access_token = request.cookies.get("spotify_access_token")
user_id = request.cookies.get("user_id")
playlist = Playlist.model_validate(request.json)
associated_playlists = spotify.find_associated_playlists(
user_id=user_id, access_token=access_token, playlist=playlist
user_id=user_id, access_token=access_token, playlist_id=playlist_id
)
return [
associated_playlist.model_dump()
Expand Down
125 changes: 121 additions & 4 deletions backend/src/database/crud/playlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
DbArtist,
DbGenre,
DbPlaylist,
DbTrack,
DbUser,
PlaylistAlbumRelationship,
peewee_model_to_dict,
TrackArtistRelationship,
)
from src.dataclasses.album import Album
from src.dataclasses.playlist import Playlist
from peewee import fn
import re
from datetime import datetime
from playhouse.shortcuts import model_to_dict


def get_playlist_by_id_or_none(id: str):
Expand Down Expand Up @@ -180,6 +180,7 @@ def get_playlist_albums_with_genres(playlist_id: str) -> List[dict]:
)
.join(DbPlaylist, on=(PlaylistAlbumRelationship.playlist == DbPlaylist.id))
.where(DbPlaylist.id == playlist_id)
.order_by(PlaylistAlbumRelationship.album_index.asc())
)

# Step 2: Retrieve genres and artists for each album
Expand Down Expand Up @@ -211,8 +212,124 @@ def get_playlist_albums_with_genres(playlist_id: str) -> List[dict]:
"release_date": album.release_date,
"total_tracks": album.total_tracks,
"genres": [genre.name for genre in genres],
"artists": [peewee_model_to_dict(artist) for artist in artists],
"artists": [model_to_dict(artist) for artist in artists],
}
albums_with_details.append(album_details)

return albums_with_details


def add_playlist_album_index(album_id: str, playlist_id: str, index: int):
query = PlaylistAlbumRelationship.update(album_index=index).where(
PlaylistAlbumRelationship.album == album_id,
PlaylistAlbumRelationship.playlist == playlist_id,
)
query.execute()


def playlist_has_null_album_indexes(playlist_id: str):
query = PlaylistAlbumRelationship.select().where(
(PlaylistAlbumRelationship.playlist_id == playlist_id)
& (PlaylistAlbumRelationship.album_index.is_null(True))
)

return query.exists()


def get_playlist_track_list(playlist_id: str):
query = (
DbTrack.select(DbTrack, DbAlbum, PlaylistAlbumRelationship.album_index)
.join(DbAlbum, on=(DbTrack.album == DbAlbum.id))
.join(
PlaylistAlbumRelationship,
on=(PlaylistAlbumRelationship.album == DbAlbum.id),
)
.where(PlaylistAlbumRelationship.playlist == playlist_id)
.order_by(
PlaylistAlbumRelationship.album_index.asc(),
DbTrack.disc_number.asc(),
DbTrack.track_number.asc(),
)
)

# Preparing the result as a list of dictionaries
results = []
for track in query:
track_dict = model_to_dict(track, recurse=False)
album_dict = model_to_dict(track.album, recurse=False)

# Fetch all artists related to this track
artists_query = (
DbArtist.select()
.join(
TrackArtistRelationship,
on=(DbArtist.id == TrackArtistRelationship.artist),
)
.where(TrackArtistRelationship.track == track.id)
)

# Serialize the artists as a list of dictionaries
artists_list = [
model_to_dict(artist, recurse=False) for artist in artists_query
]

# Adding the album and artist information to the track
track_dict["album"] = album_dict
track_dict["artists"] = artists_list

# Accessing the album_index from the PlaylistAlbumRelationship
track_dict["album_index"] = track.album.playlistalbumrelationship.album_index

results.append(track_dict)

# Returning the results as JSON
return results


def get_playlist_duration(playlist_id):
# Subquery to get all albums associated with the playlist
albums_in_playlist = PlaylistAlbumRelationship.select(
PlaylistAlbumRelationship.album
).where(PlaylistAlbumRelationship.playlist == playlist_id)

# Query to sum the duration of all tracks in the albums from the playlist
total_duration = (
DbTrack.select(fn.SUM(DbTrack.duration_ms))
.where(DbTrack.album.in_(albums_in_playlist))
.scalar()
) # .scalar() to get the result directly as a number

# If no tracks are found, return 0
return total_duration or 0


def get_playlist_duration_up_to_track(playlist_id, track_id):
# Subquery to get albums in the playlist ordered by album_index
albums_in_playlist = (
PlaylistAlbumRelationship.select(PlaylistAlbumRelationship.album)
.where(PlaylistAlbumRelationship.playlist == playlist_id)
.order_by(PlaylistAlbumRelationship.album_index)
)

# Query to get all tracks in those albums, ordered by album_index, disc_number, and track_number
tracks_in_playlist = (
DbTrack.select(DbTrack.id, DbTrack.duration_ms)
.join(DbAlbum)
.where(DbTrack.album.in_(albums_in_playlist))
.order_by(
PlaylistAlbumRelationship.album_index,
DbTrack.disc_number,
DbTrack.track_number,
)
)

# Variable to accumulate total duration
total_duration = 0

# Loop through tracks and sum duration until we reach the given track_id
for track in tracks_in_playlist:
total_duration += track.duration_ms
if track.id == track_id:
break

return total_duration
Loading

0 comments on commit 9e59584

Please sign in to comment.