mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-03 20:13:02 +00:00
add api docs for artist routes
+ extract hash and limit schemas
This commit is contained in:
committed by
Mungai Njoroge
parent
9e50eb4395
commit
7d064a8562
+16
-4
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user