add api docs for artist routes

+ extract hash and limit schemas
This commit is contained in:
mungai-njoroge
2024-03-04 01:31:46 +03:00
committed by Mungai Njoroge
parent 9e50eb4395
commit 7d064a8562
4 changed files with 163 additions and 81 deletions
+16 -4
View File
@@ -2,6 +2,7 @@
This module combines all API blueprints into a single Flask app instance. This module combines all API blueprints into a single Flask app instance.
""" """
import datetime
from flask_cors import CORS from flask_cors import CORS
from flask_compress import Compress from flask_compress import Compress
@@ -28,6 +29,19 @@ from app.api import (
getall, getall,
) )
open_api_description = f"""
The REST API exposed by your Swing Music server
### Definition of terms:
#### 1. `limit`: The number of items to return.
In endpoints that request multiple lists of items, this represents the number of items to return for each list.
---
[MIT License](https://github.com/swing-opensource/swingmusic?tab=MIT-1-ov-file#MIT-1-ov-file) | Copyright (c) {datetime.datetime.now().year} [Mungai Njoroge](https://mungai.vercel.app)
"""
def create_api(): def create_api():
""" """
@@ -36,9 +50,7 @@ def create_api():
api_info = Info( api_info = Info(
title=f"Swing Music", title=f"Swing Music",
version=f"v{Keys.SWINGMUSIC_APP_VERSION}", version=f"v{Keys.SWINGMUSIC_APP_VERSION}",
license={"name": "MIT", "url": "https://github.com/swing-opensource/swingmusic?tab=MIT-1-ov-file#MIT-1-ov-file"}, description=open_api_description,
contact={"name": "Mungai Njoroge", "url": "https://mungai.vercel.app", "email": "geoffreymungai45@gmail.com"},
description="The REST API exposed by your Swing Music server",
) )
app = OpenAPI(__name__, info=api_info) app = OpenAPI(__name__, info=api_info)
@@ -52,7 +64,7 @@ def create_api():
with app.app_context(): with app.app_context():
app.register_api(album.api) app.register_api(album.api)
app.register_blueprint(artist.api) app.register_api(artist.api)
app.register_blueprint(send_file.api) app.register_blueprint(send_file.api)
app.register_blueprint(search.api) app.register_blueprint(search.api)
app.register_blueprint(folder.api) app.register_blueprint(folder.api)
+33 -48
View File
@@ -7,6 +7,7 @@ import random
from flask_openapi3 import Tag from flask_openapi3 import Tag
from flask_openapi3 import APIBlueprint from flask_openapi3 import APIBlueprint
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from app.api.apischemas import AlbumHashSchema, AlbumLimitSchema, ArtistHashSchema
from app.db.sqlite.albumcolors import SQLiteAlbumMethods as adb from app.db.sqlite.albumcolors import SQLiteAlbumMethods as adb
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
@@ -23,22 +24,15 @@ from app.utils.hashing import create_hash
get_albums_by_albumartist = adb.get_albums_by_albumartist get_albums_by_albumartist = adb.get_albums_by_albumartist
check_is_fav = favdb.check_is_favorite check_is_fav = favdb.check_is_favorite
book_tag = Tag(name="Album", description="Single album") bp_tag = Tag(name="Album", description="Single album")
api = APIBlueprint("album", __name__, url_prefix="", abp_tags=[book_tag]) api = APIBlueprint("album", __name__, url_prefix="", abp_tags=[bp_tag])
class GetAlbumBody(BaseModel): @api.post("/album")
albumhash: str = Field( def get_album_tracks_and_info(body: AlbumHashSchema):
description="The hash of the album to get",
example="49e4819273",
min_length=Defaults.HASH_LENGTH,
max_length=Defaults.HASH_LENGTH,
)
@api.post("/album", summary="Get album")
def get_album_tracks_and_info(body: GetAlbumBody):
""" """
Get album and tracks
Returns album info and tracks for the given albumhash. Returns album info and tracks for the given albumhash.
""" """
albumhash = body.albumhash albumhash = body.albumhash
@@ -84,43 +78,39 @@ def get_album_tracks_and_info(body: GetAlbumBody):
"info": album, "info": album,
} }
class GetAlbumTracksQuery(BaseModel):
albumhash: str = Field(
description="The hash of the album",
example="49e4819273",
min_length=Defaults.HASH_LENGTH,
max_length=Defaults.HASH_LENGTH,
)
@api.get("/album/<albumhash>/tracks", summary="Get album tracks") @api.get("/album/<albumhash>/tracks")
def get_album_tracks(query: GetAlbumTracksQuery): def get_album_tracks(path: AlbumHashSchema):
""" """
Get album tracks
Returns all the tracks in the given album, sorted by disc and track number. Returns all the tracks in the given album, sorted by disc and track number.
NOTE: No album info is returned.
""" """
tracks = TrackStore.get_tracks_by_albumhash(query.albumhash) tracks = TrackStore.get_tracks_by_albumhash(path.albumhash)
tracks = sort_by_track_no(tracks) tracks = sort_by_track_no(tracks)
return tracks return tracks
class GetMoreFromArtistsBody(BaseModel):
class GetMoreFromArtistsBody(AlbumLimitSchema):
albumartists: str = Field( albumartists: str = Field(
description="The artist hashes to get more albums from", description="The artist hashes to get more albums from",
example=Defaults.API_ARTISTHASH example=Defaults.API_ARTISTHASH,
)
limit: int = Field(
description="The maximum number of albums to return per artist",
example=7,
default=7,
) )
base_title: str = Field( base_title: str = Field(
description="The base title of the album to exclude from the results.", description="The base title of the album to exclude from the results.",
example=Defaults.API_ALBUMNAME, example=Defaults.API_ALBUMNAME,
default=None, default=None,
) )
@api.post("/album/from-artist", summary="More from artist")
@api.post("/album/from-artist")
def get_more_from_artist(body: GetMoreFromArtistsBody): def get_more_from_artist(body: GetMoreFromArtistsBody):
""" """
Get more from artist
Returns more albums from the given artist hashes. Returns more albums from the given artist hashes.
""" """
albumartists = body.albumartists albumartists = body.albumartists
@@ -151,7 +141,7 @@ def get_more_from_artist(body: GetMoreFromArtistsBody):
return albums return albums
class GetAlbumVersionsBody(BaseModel): class GetAlbumVersionsBody(ArtistHashSchema):
og_album_title: str = Field( og_album_title: str = Field(
description="The original album title (album.og_title)", description="The original album title (album.og_title)",
example=Defaults.API_ALBUMNAME, example=Defaults.API_ALBUMNAME,
@@ -160,14 +150,13 @@ class GetAlbumVersionsBody(BaseModel):
description="The base title of the album to exclude from the results.", description="The base title of the album to exclude from the results.",
example=Defaults.API_ALBUMNAME, example=Defaults.API_ALBUMNAME,
) )
artisthash: str = Field(
description="The artist hash",
example=Defaults.API_ARTISTHASH,
)
@api.post("/album/other-versions", summary="Get other versions")
@api.post("/album/other-versions")
def get_album_versions(body: GetAlbumVersionsBody): def get_album_versions(body: GetAlbumVersionsBody):
""" """
Get other versions
Returns other versions of the given album. Returns other versions of the given album.
""" """
og_album_title = body.og_album_title og_album_title = body.og_album_title
@@ -189,20 +178,16 @@ def get_album_versions(body: GetAlbumVersionsBody):
return albums return albums
class GetSimilarAlbumsQuery(BaseModel):
artisthash: str = Field(
description="The artist hash",
example=Defaults.API_ARTISTHASH,
)
limit: int = Field(
description="The maximum number of albums to return",
example=Defaults.API_CARD_LIMIT,
default=Defaults.API_CARD_LIMIT,
)
@api.get("/album/similar", summary="Get similar albums") class GetSimilarAlbumsQuery(ArtistHashSchema, AlbumLimitSchema):
pass
@api.get("/album/similar")
def get_similar_albums(query: GetSimilarAlbumsQuery): def get_similar_albums(query: GetSimilarAlbumsQuery):
""" """
Get similar albums
Returns similar albums to the given album. Returns similar albums to the given album.
""" """
artisthash = query.artisthash artisthash = query.artisthash
+83
View File
@@ -0,0 +1,83 @@
"""
Reusable Pydantic basic schemas for the API
"""
from pydantic import BaseModel, Field
from app.settings import Defaults
class AlbumHashSchema(BaseModel):
"""
Extending this class will give you a model with the `albumhash` field
"""
albumhash: str = Field(
description="The album hash",
example=Defaults.API_ALBUMHASH,
min_length=Defaults.HASH_LENGTH,
max_length=Defaults.HASH_LENGTH,
)
class ArtistHashSchema(BaseModel):
"""
Extending this class will give you a model with the `artisthash` field
"""
artisthash: str = Field(
description="The artist hash",
example=Defaults.API_ARTISTHASH,
min_length=Defaults.HASH_LENGTH,
max_length=Defaults.HASH_LENGTH,
)
class GenericLimitSchema(BaseModel):
"""
Extending this class will give you a model with the `limit` field
"""
limit: int = Field(
description="The number of items to return",
example=Defaults.API_CARD_LIMIT,
default=Defaults.API_CARD_LIMIT
)
# INFO: The following 3 classes are duplicated to specify the type of items
class TrackLimitSchema(BaseModel):
"""
Extending this class will give you a model with the `limit` field
"""
limit: int = Field(
description="The number of tracks to return",
example=Defaults.API_CARD_LIMIT,
default=Defaults.API_CARD_LIMIT
)
class AlbumLimitSchema(BaseModel):
"""
Extending this class will give you a model with the `limit` field
"""
limit: int = Field(
description="The number of albums to return",
example=Defaults.API_CARD_LIMIT,
default=Defaults.API_CARD_LIMIT
)
class ArtistLimitSchema(BaseModel):
"""
Extending this class will give you a model with the `limit` field
"""
limit: int = Field(
description="The number of artists to return",
example=Defaults.API_CARD_LIMIT,
default=Defaults.API_CARD_LIMIT
)
+31 -29
View File
@@ -1,11 +1,15 @@
""" """
Contains all the artist(s) routes. Contains all the artist(s) routes.
""" """
import math import math
import random import random
from datetime import datetime from datetime import datetime
from flask import Blueprint, request from flask import request
from flask_openapi3 import APIBlueprint, Tag
from pydantic import BaseModel, Field
from app.api.apischemas import AlbumLimitSchema, ArtistHashSchema, ArtistLimitSchema, TrackLimitSchema
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
from app.db.sqlite.lastfm.similar_artists import SQLiteLastFMSimilarArtists as fmdb from app.db.sqlite.lastfm.similar_artists import SQLiteLastFMSimilarArtists as fmdb
@@ -16,20 +20,19 @@ from app.store.albums import AlbumStore
from app.store.artists import ArtistStore from app.store.artists import ArtistStore
from app.store.tracks import TrackStore from app.store.tracks import TrackStore
api = Blueprint("artist", __name__, url_prefix="/") bp_tag = Tag(name="Artist", description="Single artist")
api = APIBlueprint("artist", __name__, url_prefix="/", abp_tags=[bp_tag])
@api.route("/artist/<artisthash>", methods=["GET"]) @api.get("/artist/<string:artisthash>")
def get_artist(artisthash: str): def get_artist(path: ArtistHashSchema, query: TrackLimitSchema):
""" """
Get artist data. Get artist data.
Returns artist data, tracks and genres for the given artisthash.
""" """
limit = request.args.get("limit") artisthash = path.artisthash
limit = query.limit
if limit is None:
limit = 6
limit = int(limit)
artist = ArtistStore.get_artist_by_hash(artisthash) artist = ArtistStore.get_artist_by_hash(artisthash)
@@ -79,16 +82,18 @@ def get_artist(artisthash: str):
} }
@api.route("/artist/<artisthash>/albums", methods=["GET"]) class GetArtistAlbumsQuery(AlbumLimitSchema):
def get_artist_albums(artisthash: str): all: bool = Field(
limit = request.args.get("limit") description="Whether to ignore limit and return all albums", default=False
)
if limit is None:
limit = 6
return_all = request.args.get("all") @api.get("/artist/<artisthash>/albums")
def get_artist_albums(path: ArtistHashSchema, query: GetArtistAlbumsQuery):
return_all = query.all
artisthash = path.artisthash
limit = int(limit) limit = query.limit
all_albums = AlbumStore.get_albums_by_artisthash(artisthash) all_albums = AlbumStore.get_albums_by_artisthash(artisthash)
@@ -170,29 +175,26 @@ def get_artist_albums(artisthash: str):
} }
@api.route("/artist/<artisthash>/tracks", methods=["GET"]) @api.get("/artist/<artisthash>/tracks")
def get_all_artist_tracks(artisthash: str): def get_all_artist_tracks(path: ArtistHashSchema):
""" """
Get all artist tracks
Returns all artists by a given artist. Returns all artists by a given artist.
""" """
tracks = TrackStore.get_tracks_by_artisthash(artisthash) tracks = TrackStore.get_tracks_by_artisthash(path.artisthash)
return {"tracks": serialize_tracks(tracks)} return {"tracks": serialize_tracks(tracks)}
@api.route("/artist/<artisthash>/similar", methods=["GET"]) @api.get("/artist/<artisthash>/similar")
def get_similar_artists(artisthash: str): def get_similar_artists(path: ArtistHashSchema, query: ArtistLimitSchema):
""" """
Returns similar artists. Returns similar artists.
""" """
limit = request.args.get("limit") limit = query.limit
if limit is None: artist = ArtistStore.get_artist_by_hash(path.artisthash)
limit = 6
limit = int(limit)
artist = ArtistStore.get_artist_by_hash(artisthash)
if artist is None: if artist is None:
return {"error": "Artist not found"}, 404 return {"error": "Artist not found"}, 404