Skip to content

Commit

Permalink
Merge branch 'main' into snyk-upgrade-7cb40877026085b7353d8ccaafb93efd
Browse files Browse the repository at this point in the history
  • Loading branch information
CalPinSW authored Oct 30, 2024
2 parents cbda9e0 + 2710acc commit 5777fc1
Show file tree
Hide file tree
Showing 75 changed files with 2,082 additions and 470 deletions.
3 changes: 3 additions & 0 deletions ansible/.ansible-secrets.template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ spotify_client_id: spotify_client_id
spotify_secret: spotify_secret
debug_mode: false
flask_secret_key: flask_secret_key
musicbrainz_url: musicbrainz_url
musicbrainz_user_agent: musicbrainz_user_agent
db_connection_string: db_connection_string
7 changes: 7 additions & 0 deletions backend/.env.j2
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,10 @@ SECRET_KEY={{flask_secret_key}}
SPOTIFY_CLIENT_ID={{spotify_client_id}}
SPOTIFY_SECRET={{spotify_secret}}
SPOTIFY_REDIRECT_URI="{{backend_url}}/auth/get-user-code"

# Musicbrainz Api
MUSICBRAINZ_URL={{musicbrainz_url}}
MUSICBRAINZ_USER_AGENT={{musicbrainz_user_agent}}

# Database Connection String
DB_CONNECTION_STRING={{db_connection_string}}
7 changes: 7 additions & 0 deletions backend/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,10 @@ SECRET_KEY="secret-key"
SPOTIFY_CLIENT_ID="https://developer.spotify.com/dashboard"
SPOTIFY_SECRET="https://developer.spotify.com/dashboard"
SPOTIFY_REDIRECT_URI="http://localhost:8080/spotify-redirect"

# Musicbrainz Api
MUSICBRAINZ_URL=https://musicbrainz.org/ws/2
MUSICBRAINZ_USER_AGENT="Application PlaylistManager/1.0 - Hobby project (put maintainers email here)"

# Database Connection String
DB_CONNECTION_STRING=postgresql://username:password@hostname:port/database
7 changes: 7 additions & 0 deletions backend/.env.test
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,10 @@ SECRET_KEY="secret-key"
SPOTIFY_CLIENT_ID="https://developer.spotify.com/dashboard"
SPOTIFY_SECRET="https://developer.spotify.com/dashboard"
SPOTIFY_REDIRECT_URI="http://localhost:8080/spotify-redirect"

# Musicbrainz Api
MUSICBRAINZ_URL=https://musicbrainz.org/ws/2
MUSICBRAINZ_USER_AGENT="Application PlaylistManager/1.0 - Hobby project (put maintainers email here)"

# Database Connection String
DB_CONNECTION_STRING=postgresql://username:password@hostname:5432/database
8 changes: 8 additions & 0 deletions backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,11 @@ If you now run [the frontend](../frontend/README.md) you should be able to commu
## Running Tests

Tests can be run with `poetry run pytest`


## Running DB Migrations

Load env variables with `source .env`
Run Python locally `python3`
Import migration file >>>: `from src.database.migrations.(migration_file_name) import up, down`
Run migration >>>: `up()` / `down()`
53 changes: 49 additions & 4 deletions backend/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ packages = [
python = "^3.10"
flask = "^3.0.3"
python-dotenv = "^1.0.0"
flask-cors = "^4.0.1"
flask-cors = "^5.0.0"
pydantic = "^2.6.4"
requests = "^2.32.3"
gunicorn = "^22.0.0"
peewee = "^3.17.6"
asyncio = "^3.4.3"
psycopg2 = "^2.9.9"

[tool.poetry.group.dev.dependencies]
pytest = "^8.1.1"
Expand Down
17 changes: 16 additions & 1 deletion backend/src/app.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
from flask import Flask, make_response
from flask_cors import CORS
from src.controllers.database import database_controller
from src.controllers.spotify import spotify_controller
from src.controllers.music_data import music_controller
from src.exceptions.Unauthorized import UnauthorizedException
from src.flask_config import Config
from src.musicbrainz import MusicbrainzClient
from src.spotify import SpotifyClient
from src.controllers.auth import auth_controller
from src.database.models import db_wrapper


def create_app():
app = Flask(__name__)
spotify = SpotifyClient()
musicbrainz = MusicbrainzClient()
app.config["DATABASE"] = Config().DB_CONNECTION_STRING
db_wrapper.init_app(app)

app.config.from_object(Config())
app.config["CORS_HEADERS"] = "Content-Type"

Expand All @@ -21,7 +29,7 @@ def create_app():
SESSION_COOKIE_SECURE="True",
)

cors = CORS(
CORS(
app,
resources={
r"/*": {
Expand All @@ -44,4 +52,11 @@ def handle_unauthorized_exception(_):

app.register_blueprint(auth_controller(spotify=spotify))
app.register_blueprint(spotify_controller(spotify=spotify))
app.register_blueprint(music_controller(spotify=spotify))
app.register_blueprint(
database_controller(
spotify=spotify, musicbrainz=musicbrainz, database=db_wrapper
)
)

return app
142 changes: 142 additions & 0 deletions backend/src/controllers/database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
from flask import Blueprint, make_response, request

from src.database.crud.album import (
add_genres_to_album,
get_album_artists,
get_album_genres,
get_user_albums,
get_user_albums_with_no_artists,
update_album,
)
from src.database.crud.genre import create_genre
from src.database.crud.playlist import (
create_playlist,
delete_playlist,
get_playlist_albums,
get_playlist_by_id_or_none,
)
from src.database.crud.user import get_or_create_user
from src.musicbrainz import MusicbrainzClient
from src.spotify import SpotifyClient
from playhouse.flask_utils import FlaskDB


def database_controller(
spotify: SpotifyClient, musicbrainz: MusicbrainzClient, database: FlaskDB
):
database_controller = Blueprint(
name="database_controller", import_name=__name__, url_prefix="/database"
)

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

for simplified_playlist in simplified_playlists:
with database.database.atomic():
if "Albums" in simplified_playlist.name:
db_playlist = get_playlist_by_id_or_none(simplified_playlist.id)
if (
db_playlist is None
or db_playlist.snapshot_id != simplified_playlist.snapshot_id
or db_playlist.name.startswith("Best Albums")
):
if db_playlist is not None:
delete_playlist(db_playlist.id)
playlist = spotify.get_playlist(
access_token=access_token, id=simplified_playlist.id
)
create_playlist(playlist, db_user)

return make_response("Playlist data populated", 201)

@database_controller.route("populate_playlist/<id>", methods=["GET"])
def populate_playlist(id):
access_token = request.cookies.get("spotify_access_token")
user = spotify.get_current_user(access_token)
(db_user, _) = get_or_create_user(user)
db_playlist = get_playlist_by_id_or_none(id)
if db_playlist is not None:
delete_playlist(db_playlist.id)
playlist = spotify.get_playlist(access_token=access_token, id=id)
create_playlist(playlist, db_user)
albums = get_playlist_albums(playlist.id)
batch_albums = split_list(albums, 20)
for album_chunk in batch_albums:
albums = spotify.get_multiple_albums(
access_token=access_token, ids=[album.id for album in album_chunk]
)
for db_album in albums:
with database.database.atomic():
db_album.genres = musicbrainz.get_album_genres(
db_album.artists[0].name, db_album.name
)
update_album(db_album)

return make_response("Playlist details populated", 201)

@database_controller.route("populate_additional_album_details", methods=["GET"])
def populate_additional_album_details():
access_token = request.cookies.get("spotify_access_token")
user = spotify.get_current_user(access_token)
albums = get_user_albums_with_no_artists(user.id)
batch_albums = split_list(albums, 20)
for album_chunk in batch_albums:
albums = spotify.get_multiple_albums(
access_token=access_token, ids=[album.id for album in album_chunk]
)
for db_album in albums:
with database.database.atomic():
db_album.genres = musicbrainz.get_album_genres(
db_album.artists[0].name, db_album.name
)
update_album(db_album)

return make_response("Playlist details populated", 201)

@database_controller.route("populate_universal_genre_list", methods=["GET"])
def populate_universal_genre_list():
genre_list = musicbrainz.get_genre_list()
[create_genre(genre) for genre in genre_list]
return make_response("Genre data populated", 201)

@database_controller.route("populate_user_album_genres", methods=["GET"])
def populate_user_album_genres():
access_token = request.cookies.get("spotify_access_token")
user = spotify.get_current_user(access_token)
populate_album_genres_by_user_id(user.id, musicbrainz)
return make_response("User album genres populated", 201)

return database_controller


def split_list(input_list, max_length=20):
return [
input_list[i : i + max_length] for i in range(0, len(input_list), max_length)
]


def populate_album_genres_by_user_id(
user_id: str, musicbrainz: MusicbrainzClient = MusicbrainzClient()
):
albums = get_user_albums(user_id=user_id)
print(f"processing album {0} of {len(albums)}")
skip_count = 0
for idx, db_album in enumerate(albums):
print("\033[A \033[A")
print(f"processing album {idx} of {len(albums)}, skipped {skip_count}")
if get_album_genres(db_album.id) != []:
skip_count += 1
continue
album_artists = get_album_artists(db_album)
genres = musicbrainz.get_album_genres(
artist_name=album_artists[0].name, album_title=db_album.name
)
add_genres_to_album(db_album, genres)
print("\033[A \033[A")
print(f"completed. Processed {len(albums)} albums. Skipped ")
Loading

0 comments on commit 5777fc1

Please sign in to comment.