mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-03 20:13:02 +00:00
Finish documentation for all endpoints
+ fix #193 (settings https redirect) + fix open api docs on binary + fix git error on binary + remove flask-restful hopefully, I didn't break something 😩
This commit is contained in:
committed by
Mungai Njoroge
parent
99ec11565c
commit
0af1ae1d8e
+11
-12
@@ -8,8 +8,6 @@ from flask_compress import Compress
|
||||
|
||||
from flask_openapi3 import Info
|
||||
from flask_openapi3 import OpenAPI
|
||||
from pydantic import BaseModel, Field
|
||||
from flask_openapi3 import FileStorage
|
||||
|
||||
from app.settings import Keys
|
||||
from .plugins import lyrics as lyrics_plugin
|
||||
@@ -46,6 +44,7 @@ In endpoints that request multiple lists of items, this represents the number of
|
||||
[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():
|
||||
"""
|
||||
Creates the Flask instance, registers modules and registers all the API blueprints.
|
||||
@@ -72,23 +71,23 @@ def create_api():
|
||||
app.register_api(search.api)
|
||||
app.register_api(folder.api)
|
||||
app.register_api(playlist.api)
|
||||
app.register_blueprint(favorites.api)
|
||||
app.register_blueprint(imgserver.api)
|
||||
app.register_blueprint(settings.api)
|
||||
app.register_blueprint(colors.api)
|
||||
app.register_blueprint(lyrics.api)
|
||||
app.register_api(favorites.api)
|
||||
app.register_api(imgserver.api)
|
||||
app.register_api(settings.api)
|
||||
app.register_api(colors.api)
|
||||
app.register_api(lyrics.api)
|
||||
|
||||
# Plugins
|
||||
app.register_blueprint(plugins.api)
|
||||
app.register_blueprint(lyrics_plugin.api)
|
||||
app.register_api(plugins.api)
|
||||
app.register_api(lyrics_plugin.api)
|
||||
|
||||
# Logger
|
||||
app.register_blueprint(logger.api_bp)
|
||||
app.register_api(logger.api)
|
||||
|
||||
# Home
|
||||
app.register_blueprint(home.api_bp)
|
||||
app.register_api(home.api)
|
||||
|
||||
# Flask Restful
|
||||
app.register_blueprint(getall.api_bp)
|
||||
app.register_api(getall.api)
|
||||
|
||||
return app
|
||||
|
||||
+1
-2
@@ -98,7 +98,6 @@ def get_artist_albums(path: ArtistHashSchema, query: GetArtistAlbumsQuery):
|
||||
limit = query.limit
|
||||
|
||||
all_albums = AlbumStore.get_albums_by_artisthash(artisthash)
|
||||
|
||||
# start: check for missing albums. ie. compilations and features
|
||||
all_tracks = TrackStore.get_tracks_by_artisthash(artisthash)
|
||||
|
||||
@@ -163,7 +162,7 @@ def get_artist_albums(path: ArtistHashSchema, query: GetArtistAlbumsQuery):
|
||||
if artist is None:
|
||||
return {"error": "Artist not found"}, 404
|
||||
|
||||
if return_all is not None and return_all == "true":
|
||||
if return_all:
|
||||
limit = len(all_albums)
|
||||
|
||||
singles_and_eps = singles + eps
|
||||
|
||||
+11
-5
@@ -1,12 +1,18 @@
|
||||
from flask import Blueprint
|
||||
from flask_openapi3 import Tag
|
||||
from flask_openapi3 import APIBlueprint
|
||||
from app.api.apischemas import AlbumHashSchema
|
||||
from app.store.albums import AlbumStore as Store
|
||||
|
||||
api = Blueprint("colors", __name__, url_prefix="/colors")
|
||||
bp_tag = Tag(name="Colors", description="Get item colors")
|
||||
api = APIBlueprint("colors", __name__, url_prefix="/colors", abp_tags=[bp_tag])
|
||||
|
||||
|
||||
@api.route("/album/<albumhash>")
|
||||
def get_album_color(albumhash: str):
|
||||
album = Store.get_album_by_hash(albumhash)
|
||||
@api.get("/album/<albumhash>")
|
||||
def get_album_color(path: AlbumHashSchema):
|
||||
"""
|
||||
Get album color
|
||||
"""
|
||||
album = Store.get_album_by_hash(path.albumhash)
|
||||
|
||||
msg = {"color": ""}
|
||||
|
||||
|
||||
+77
-77
@@ -1,7 +1,12 @@
|
||||
from typing import List, TypeVar
|
||||
from flask import Blueprint, request
|
||||
|
||||
from flask_openapi3 import Tag
|
||||
from flask_openapi3 import APIBlueprint
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.api.apischemas import GenericLimitSchema
|
||||
from app.models import FavType
|
||||
from app.settings import Defaults
|
||||
from app.utils.bisection import use_bisection
|
||||
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
|
||||
from app.serializers.track import serialize_track, serialize_tracks
|
||||
@@ -13,8 +18,8 @@ from app.store.tracks import TrackStore
|
||||
from app.store.artists import ArtistStore
|
||||
from app.utils.dates import timestamp_to_time_passed
|
||||
|
||||
|
||||
api = Blueprint("favorite", __name__, url_prefix="/")
|
||||
bp_tag = Tag(name="Favorites", description="Your favorite items")
|
||||
api = APIBlueprint("favorites", __name__, url_prefix="/favorites", abp_tags=[bp_tag])
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
@@ -24,18 +29,23 @@ def remove_none(items: List[T]) -> List[T]:
|
||||
return [i for i in items if i is not None]
|
||||
|
||||
|
||||
@api.route("/favorite/add", methods=["POST"])
|
||||
def add_favorite():
|
||||
class FavoritesAddBody(BaseModel):
|
||||
hash: str = Field(
|
||||
description="The hash of the item",
|
||||
min_length=Defaults.HASH_LENGTH,
|
||||
max_length=Defaults.HASH_LENGTH,
|
||||
example=Defaults.API_ALBUMHASH,
|
||||
)
|
||||
type: str = Field(description="The type of the item", example=FavType.album)
|
||||
|
||||
|
||||
@api.post("/add")
|
||||
def add_favorite(body: FavoritesAddBody):
|
||||
"""
|
||||
Adds a favorite to the database.
|
||||
"""
|
||||
data = request.get_json()
|
||||
|
||||
if data is None:
|
||||
return {"error": "No data provided"}, 400
|
||||
|
||||
itemhash = data.get("hash")
|
||||
itemtype = data.get("type")
|
||||
itemhash = body.hash
|
||||
itemtype = body.type
|
||||
|
||||
favdb.insert_one_favorite(itemtype, itemhash)
|
||||
|
||||
@@ -45,18 +55,13 @@ def add_favorite():
|
||||
return {"msg": "Added to favorites"}
|
||||
|
||||
|
||||
@api.route("/favorite/remove", methods=["POST"])
|
||||
def remove_favorite():
|
||||
@api.post("/remove")
|
||||
def remove_favorite(body: FavoritesAddBody):
|
||||
"""
|
||||
Removes a favorite from the database.
|
||||
"""
|
||||
data = request.get_json()
|
||||
|
||||
if data is None:
|
||||
return {"error": "No data provided"}, 400
|
||||
|
||||
itemhash = data.get("hash")
|
||||
itemtype = data.get("type")
|
||||
itemhash = body.hash
|
||||
itemtype = body.type
|
||||
|
||||
favdb.delete_favorite(itemtype, itemhash)
|
||||
|
||||
@@ -66,15 +71,12 @@ def remove_favorite():
|
||||
return {"msg": "Removed from favorites"}
|
||||
|
||||
|
||||
@api.route("/albums/favorite")
|
||||
def get_favorite_albums():
|
||||
limit = request.args.get("limit")
|
||||
|
||||
if limit is None:
|
||||
limit = 6
|
||||
|
||||
limit = int(limit)
|
||||
|
||||
@api.get("/albums")
|
||||
def get_favorite_albums(query: GenericLimitSchema):
|
||||
"""
|
||||
Get favorite albums
|
||||
"""
|
||||
limit = query.limit
|
||||
albums = favdb.get_fav_albums()
|
||||
albumhashes = [a[1] for a in albums]
|
||||
albumhashes.reverse()
|
||||
@@ -90,15 +92,12 @@ def get_favorite_albums():
|
||||
return {"albums": serialize_for_card_many(fav_albums[:limit])}
|
||||
|
||||
|
||||
@api.route("/tracks/favorite")
|
||||
def get_favorite_tracks():
|
||||
limit = request.args.get("limit")
|
||||
|
||||
if limit is None:
|
||||
limit = 6
|
||||
|
||||
limit = int(limit)
|
||||
|
||||
@api.get("/tracks")
|
||||
def get_favorite_tracks(query: GenericLimitSchema):
|
||||
"""
|
||||
Get favorite tracks
|
||||
"""
|
||||
limit = query.limit
|
||||
tracks = favdb.get_fav_tracks()
|
||||
trackhashes = [t[1] for t in tracks]
|
||||
trackhashes.reverse()
|
||||
@@ -113,15 +112,12 @@ def get_favorite_tracks():
|
||||
return {"tracks": serialize_tracks(tracks[:limit])}
|
||||
|
||||
|
||||
@api.route("/artists/favorite")
|
||||
def get_favorite_artists():
|
||||
limit = request.args.get("limit")
|
||||
|
||||
if limit is None:
|
||||
limit = 6
|
||||
|
||||
limit = int(limit)
|
||||
|
||||
@api.get("/artists")
|
||||
def get_favorite_artists(query: GenericLimitSchema):
|
||||
"""
|
||||
Get favorite artists
|
||||
"""
|
||||
limit = query.limit
|
||||
artists = favdb.get_fav_artists()
|
||||
artisthashes = [a[1] for a in artists]
|
||||
artisthashes.reverse()
|
||||
@@ -137,27 +133,38 @@ def get_favorite_artists():
|
||||
return {"artists": artists[:limit]}
|
||||
|
||||
|
||||
@api.route("/favorites")
|
||||
def get_all_favorites():
|
||||
class GetAllFavoritesQuery(BaseModel):
|
||||
"""
|
||||
Extending this class will give you a model with the `limit` field
|
||||
"""
|
||||
|
||||
track_limit: int = Field(
|
||||
description="The number of tracks to return",
|
||||
example=Defaults.API_CARD_LIMIT,
|
||||
default=Defaults.API_CARD_LIMIT,
|
||||
)
|
||||
|
||||
album_limit: int = Field(
|
||||
description="The number of albums to return",
|
||||
example=Defaults.API_CARD_LIMIT,
|
||||
default=Defaults.API_CARD_LIMIT,
|
||||
)
|
||||
|
||||
artist_limit: int = Field(
|
||||
description="The number of artists to return",
|
||||
example=Defaults.API_CARD_LIMIT,
|
||||
default=Defaults.API_CARD_LIMIT,
|
||||
)
|
||||
|
||||
|
||||
@api.get("")
|
||||
def get_all_favorites(query: GetAllFavoritesQuery):
|
||||
"""
|
||||
Returns all the favorites in the database.
|
||||
"""
|
||||
track_limit = request.args.get("track_limit")
|
||||
album_limit = request.args.get("album_limit")
|
||||
artist_limit = request.args.get("artist_limit")
|
||||
|
||||
if track_limit is None:
|
||||
track_limit = 6
|
||||
|
||||
if album_limit is None:
|
||||
album_limit = 6
|
||||
|
||||
if artist_limit is None:
|
||||
artist_limit = 6
|
||||
|
||||
track_limit = int(track_limit)
|
||||
album_limit = int(album_limit)
|
||||
artist_limit = int(artist_limit)
|
||||
track_limit = query.track_limit
|
||||
album_limit = query.album_limit
|
||||
artist_limit = query.artist_limit
|
||||
|
||||
# largest is x2 to accound for broken hashes if any
|
||||
largest = max(track_limit, album_limit, artist_limit)
|
||||
@@ -266,20 +273,13 @@ def get_all_favorites():
|
||||
}
|
||||
|
||||
|
||||
@api.route("/favorites/check")
|
||||
def check_favorite():
|
||||
@api.get("/check")
|
||||
def check_favorite(query: FavoritesAddBody):
|
||||
"""
|
||||
Checks if a favorite exists in the database.
|
||||
"""
|
||||
itemhash = request.args.get("hash")
|
||||
itemtype = request.args.get("type")
|
||||
|
||||
if itemhash is None:
|
||||
return {"error": "No hash provided"}, 400
|
||||
|
||||
if itemtype is None:
|
||||
return {"error": "No type provided"}, 400
|
||||
|
||||
itemhash = query.hash
|
||||
itemtype = query.type
|
||||
exists = favdb.check_is_favorite(itemhash, itemtype)
|
||||
|
||||
return {"is_favorite": exists}
|
||||
|
||||
+120
-5
@@ -1,10 +1,125 @@
|
||||
from flask import Blueprint
|
||||
from flask_restful import Api
|
||||
|
||||
from .resources import Albums
|
||||
from flask_openapi3 import Tag
|
||||
from flask_openapi3 import APIBlueprint
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
api_bp = Blueprint("getall", __name__, url_prefix="/getall")
|
||||
api = Api(api_bp)
|
||||
from datetime import datetime
|
||||
from app.api.apischemas import GenericLimitSchema
|
||||
from app.store.albums import AlbumStore
|
||||
from app.store.artists import ArtistStore
|
||||
|
||||
from app.serializers.album import serialize_for_card as serialize_album
|
||||
from app.serializers.artist import serialize_for_card as serialize_artist
|
||||
from app.utils import format_number
|
||||
from app.utils.dates import (
|
||||
create_new_date,
|
||||
date_string_to_time_passed,
|
||||
seconds_to_time_string,
|
||||
)
|
||||
|
||||
bp_tag = Tag(name="Get all", description="List all items")
|
||||
api = APIBlueprint("getall", __name__, url_prefix="/getall", abp_tags=[bp_tag])
|
||||
|
||||
|
||||
api.add_resource(Albums, "/<itemtype>")
|
||||
class GetAllItemsBody(GenericLimitSchema):
|
||||
start: int = Field(
|
||||
description="The start index of the items to return",
|
||||
example=0,
|
||||
default=0,
|
||||
)
|
||||
sortby: str = Field(
|
||||
description="The key to sort items by",
|
||||
example="created_date",
|
||||
default="created_date",
|
||||
)
|
||||
|
||||
reverse: int = Field(
|
||||
description="Reverse the sort",
|
||||
example=1,
|
||||
default=1,
|
||||
)
|
||||
|
||||
|
||||
class GetAllItemsPath(BaseModel):
|
||||
itemtype: str = Field(
|
||||
description="The type of items to return (albums | artists)",
|
||||
example="albums",
|
||||
default="albums",
|
||||
)
|
||||
|
||||
|
||||
@api.get("/<itemtype>")
|
||||
def get_all_items(path: GetAllItemsPath, query: GetAllItemsBody):
|
||||
"""
|
||||
Get all items
|
||||
|
||||
Used to show all albums or artists in the library
|
||||
"""
|
||||
is_albums = path.itemtype == "albums"
|
||||
is_artists = path.itemtype == "artists"
|
||||
|
||||
items = AlbumStore.albums
|
||||
|
||||
if is_artists:
|
||||
items = ArtistStore.artists
|
||||
|
||||
start = query.start
|
||||
limit = query.limit
|
||||
sort = query.sortby
|
||||
reverse = query.reverse == 1
|
||||
|
||||
# if sort == "":
|
||||
# sort = "created_date"
|
||||
|
||||
sort_is_count = sort == "count"
|
||||
sort_is_duration = sort == "duration"
|
||||
sort_is_create_date = sort == "created_date"
|
||||
|
||||
sort_is_date = is_albums and sort == "date"
|
||||
sort_is_artist = is_albums and sort == "albumartists"
|
||||
|
||||
sort_is_artist_trackcount = is_artists and sort == "trackcount"
|
||||
sort_is_artist_albumcount = is_artists and sort == "albumcount"
|
||||
|
||||
lambda_sort = lambda x: getattr(x, sort)
|
||||
if sort_is_artist:
|
||||
lambda_sort = lambda x: getattr(x, sort)[0].name
|
||||
|
||||
sorted_items = sorted(items, key=lambda_sort, reverse=reverse)
|
||||
items = sorted_items[start : start + limit]
|
||||
|
||||
album_list = []
|
||||
|
||||
for item in items:
|
||||
item_dict = serialize_album(item) if is_albums else serialize_artist(item)
|
||||
|
||||
if sort_is_date:
|
||||
item_dict["help_text"] = item.date
|
||||
|
||||
if sort_is_create_date:
|
||||
date = create_new_date(datetime.fromtimestamp(item.created_date))
|
||||
timeago = date_string_to_time_passed(date)
|
||||
item_dict["help_text"] = timeago
|
||||
|
||||
if sort_is_count:
|
||||
item_dict["help_text"] = (
|
||||
f"{format_number(item.count)} track{'' if item.count == 1 else 's'}"
|
||||
)
|
||||
|
||||
if sort_is_duration:
|
||||
item_dict["help_text"] = seconds_to_time_string(item.duration)
|
||||
|
||||
if sort_is_artist_trackcount:
|
||||
item_dict["help_text"] = (
|
||||
f"{format_number(item.trackcount)} track{'' if item.trackcount == 1 else 's'}"
|
||||
)
|
||||
|
||||
if sort_is_artist_albumcount:
|
||||
item_dict["help_text"] = (
|
||||
f"{format_number(item.albumcount)} album{'' if item.albumcount == 1 else 's'}"
|
||||
)
|
||||
|
||||
album_list.append(item_dict)
|
||||
|
||||
return {"items": album_list, "total": len(sorted_items)}
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
from flask_restful import Resource, reqparse
|
||||
from datetime import datetime
|
||||
from app.store.albums import AlbumStore
|
||||
from app.store.artists import ArtistStore
|
||||
|
||||
from app.serializers.album import serialize_for_card as serialize_album
|
||||
from app.serializers.artist import serialize_for_card as serialize_artist
|
||||
from app.utils import format_number
|
||||
from app.utils.dates import (
|
||||
create_new_date,
|
||||
date_string_to_time_passed,
|
||||
seconds_to_time_string,
|
||||
)
|
||||
|
||||
parser = reqparse.RequestParser()
|
||||
|
||||
parser.add_argument("start", type=int, default=0, location="args")
|
||||
parser.add_argument("limit", type=int, default=20, location="args")
|
||||
parser.add_argument("sortby", type=str, default="created_date", location="args")
|
||||
parser.add_argument("reverse", type=str, default="1", location="args")
|
||||
|
||||
|
||||
class Albums(Resource):
|
||||
def get(self, itemtype: str):
|
||||
is_albums = itemtype == "albums"
|
||||
is_artists = itemtype == "artists"
|
||||
|
||||
items = AlbumStore.albums
|
||||
|
||||
if is_artists:
|
||||
items = ArtistStore.artists
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
start = args["start"]
|
||||
limit = args["limit"]
|
||||
sort = args["sortby"]
|
||||
reverse = args["reverse"] == "1"
|
||||
|
||||
if sort == "":
|
||||
sort = "created_date"
|
||||
|
||||
sort_is_count = sort == "count"
|
||||
sort_is_duration = sort == "duration"
|
||||
sort_is_create_date = sort == "created_date"
|
||||
|
||||
sort_is_date = is_albums and sort == "date"
|
||||
sort_is_artist = is_albums and sort == "albumartists"
|
||||
|
||||
sort_is_artist_trackcount = is_artists and sort == "trackcount"
|
||||
sort_is_artist_albumcount = is_artists and sort == "albumcount"
|
||||
|
||||
lambda_sort = lambda x: getattr(x, sort)
|
||||
if sort_is_artist:
|
||||
lambda_sort = lambda x: getattr(x, sort)[0].name
|
||||
|
||||
sorted_items = sorted(items, key=lambda_sort, reverse=reverse)
|
||||
items = sorted_items[start : start + limit]
|
||||
|
||||
album_list = []
|
||||
|
||||
for item in items:
|
||||
item_dict = serialize_album(item) if is_albums else serialize_artist(item)
|
||||
|
||||
if sort_is_date:
|
||||
item_dict["help_text"] = item.date
|
||||
|
||||
if sort_is_create_date:
|
||||
date = create_new_date(datetime.fromtimestamp(item.created_date))
|
||||
timeago = date_string_to_time_passed(date)
|
||||
item_dict["help_text"] = timeago
|
||||
|
||||
if sort_is_count:
|
||||
item_dict[
|
||||
"help_text"
|
||||
] = f"{format_number(item.count)} track{'' if item.count == 1 else 's'}"
|
||||
|
||||
if sort_is_duration:
|
||||
item_dict["help_text"] = seconds_to_time_string(item.duration)
|
||||
|
||||
if sort_is_artist_trackcount:
|
||||
item_dict[
|
||||
"help_text"
|
||||
] = f"{format_number(item.trackcount)} track{'' if item.trackcount == 1 else 's'}"
|
||||
|
||||
if sort_is_artist_albumcount:
|
||||
item_dict[
|
||||
"help_text"
|
||||
] = f"{format_number(item.albumcount)} album{'' if item.albumcount == 1 else 's'}"
|
||||
|
||||
album_list.append(item_dict)
|
||||
|
||||
return {"items": album_list, "total": len(sorted_items)}
|
||||
@@ -1,11 +1,25 @@
|
||||
from flask import Blueprint
|
||||
from flask_restful import Api
|
||||
from flask_openapi3 import Tag
|
||||
from flask_openapi3 import APIBlueprint
|
||||
|
||||
from .recents import RecentlyAdded, RecentlyPlayed
|
||||
from app.api.apischemas import GenericLimitSchema
|
||||
from app.lib.home.recentlyadded import get_recent_items
|
||||
from app.lib.home.recentlyplayed import get_recently_played
|
||||
|
||||
api_bp = Blueprint("home", __name__, url_prefix="/home")
|
||||
api = Api(api_bp)
|
||||
bp_tag = Tag(name="Home", description="Homepage items")
|
||||
api = APIBlueprint("home", __name__, url_prefix="/home", abp_tags=[bp_tag])
|
||||
|
||||
|
||||
api.add_resource(RecentlyAdded, "/recents/added")
|
||||
api.add_resource(RecentlyPlayed, "/recents/played")
|
||||
@api.get("/recents/added")
|
||||
def get_recently_added(query: GenericLimitSchema):
|
||||
"""
|
||||
Get recently added
|
||||
"""
|
||||
return {"items": get_recent_items(query.limit)}
|
||||
|
||||
|
||||
@api.get("/recents/played")
|
||||
def get_recent_plays(query: GenericLimitSchema):
|
||||
"""
|
||||
Get recently played
|
||||
"""
|
||||
return {"items": get_recently_played(query.limit)}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
from flask_restful import Resource, reqparse
|
||||
|
||||
from app.lib.home.recentlyadded import get_recent_items
|
||||
from app.lib.home.recentlyplayed import get_recently_played
|
||||
|
||||
parser = reqparse.RequestParser()
|
||||
|
||||
parser.add_argument("limit", type=int, required=False, default=7, location="args")
|
||||
|
||||
|
||||
class RecentlyAdded(Resource):
|
||||
def get(self):
|
||||
args = parser.parse_args()
|
||||
limit = args["limit"]
|
||||
|
||||
return {"items": get_recent_items(limit)}
|
||||
|
||||
|
||||
class RecentlyPlayed(Resource):
|
||||
def get(self):
|
||||
args = parser.parse_args()
|
||||
limit = args["limit"]
|
||||
|
||||
return {"items": get_recently_played(limit)}
|
||||
+76
-42
@@ -1,88 +1,122 @@
|
||||
from pathlib import Path
|
||||
from flask_openapi3 import Tag
|
||||
from flask_openapi3 import APIBlueprint
|
||||
from pydantic import BaseModel, Field
|
||||
from flask import send_from_directory
|
||||
|
||||
from flask import Blueprint, send_from_directory
|
||||
from app.settings import Defaults, Paths
|
||||
|
||||
from app.settings import Paths
|
||||
|
||||
api = Blueprint("imgserver", __name__, url_prefix="/img")
|
||||
|
||||
|
||||
@api.route("/")
|
||||
def hello():
|
||||
return "<h1>Image Server</h1>"
|
||||
bp_tag = Tag(
|
||||
name="Images", description="Image filenames are constructured as '{itemhash}.webp'"
|
||||
)
|
||||
api = APIBlueprint("imgserver", __name__, url_prefix="/img", abp_tags=[bp_tag])
|
||||
|
||||
|
||||
def send_fallback_img(filename: str = "default.webp"):
|
||||
path = Paths.get_assets_path()
|
||||
img = Path(path) / filename
|
||||
folder = Paths.get_assets_path()
|
||||
img = Path(folder) / filename
|
||||
|
||||
if not img.exists():
|
||||
return "", 404
|
||||
|
||||
return send_from_directory(path, filename)
|
||||
return send_from_directory(folder, filename)
|
||||
|
||||
|
||||
@api.route("/t/o/<imgpath>")
|
||||
def send_original_thumbnail(imgpath: str):
|
||||
path = Paths.get_original_thumb_path()
|
||||
fpath = Path(path) / imgpath
|
||||
class ImagePath(BaseModel):
|
||||
imgpath: str = Field(
|
||||
description="The image filename",
|
||||
example=Defaults.API_ALBUMHASH + ".webp",
|
||||
)
|
||||
|
||||
|
||||
@api.get("/t/o/<imgpath>")
|
||||
def send_original_thumbnail(path: ImagePath):
|
||||
"""
|
||||
Get original thumbnail
|
||||
"""
|
||||
folder = Paths.get_original_thumb_path()
|
||||
fpath = Path(folder) / path.imgpath
|
||||
|
||||
if fpath.exists():
|
||||
return send_from_directory(path, imgpath)
|
||||
return send_from_directory(folder, path.imgpath)
|
||||
|
||||
return send_fallback_img()
|
||||
|
||||
|
||||
@api.route("/t/<imgpath>")
|
||||
def send_lg_thumbnail(imgpath: str):
|
||||
path = Paths.get_lg_thumb_path()
|
||||
fpath = Path(path) / imgpath
|
||||
@api.get("/t/<imgpath>")
|
||||
def send_lg_thumbnail(path: ImagePath):
|
||||
"""
|
||||
Get large thumbnail (500 x 500)
|
||||
"""
|
||||
folder = Paths.get_lg_thumb_path()
|
||||
fpath = Path(folder) / path.imgpath
|
||||
|
||||
if fpath.exists():
|
||||
return send_from_directory(path, imgpath)
|
||||
return send_from_directory(folder, path.imgpath)
|
||||
|
||||
return send_fallback_img()
|
||||
|
||||
|
||||
@api.route("/t/s/<imgpath>")
|
||||
def send_sm_thumbnail(imgpath: str):
|
||||
path = Paths.get_sm_thumb_path()
|
||||
fpath = Path(path) / imgpath
|
||||
@api.get("/t/s/<imgpath>")
|
||||
def send_sm_thumbnail(path: ImagePath):
|
||||
"""
|
||||
Get small thumbnail (64 x 64)
|
||||
"""
|
||||
folder = Paths.get_sm_thumb_path()
|
||||
fpath = Path(folder) / path.imgpath
|
||||
|
||||
if fpath.exists():
|
||||
return send_from_directory(path, imgpath)
|
||||
return send_from_directory(folder, path.imgpath)
|
||||
|
||||
return send_fallback_img()
|
||||
|
||||
|
||||
@api.route("/a/<imgpath>")
|
||||
def send_lg_artist_image(imgpath: str):
|
||||
path = Paths.get_artist_img_lg_path()
|
||||
fpath = Path(path) / imgpath
|
||||
@api.get("/a/<imgpath>")
|
||||
def send_lg_artist_image(path: ImagePath):
|
||||
"""
|
||||
Get large artist image (500 x 500)
|
||||
"""
|
||||
folder = Paths.get_artist_img_lg_path()
|
||||
fpath = Path(folder) / path.imgpath
|
||||
|
||||
if fpath.exists():
|
||||
return send_from_directory(path, imgpath)
|
||||
return send_from_directory(folder, path.imgpath)
|
||||
|
||||
return send_fallback_img("artist.webp")
|
||||
|
||||
|
||||
@api.route("/a/s/<imgpath>")
|
||||
def send_sm_artist_image(imgpath: str):
|
||||
path = Paths.get_artist_img_sm_path()
|
||||
fpath = Path(path) / imgpath
|
||||
@api.get("/a/s/<imgpath>")
|
||||
def send_sm_artist_image(path: ImagePath):
|
||||
"""
|
||||
Get small artist image (64 x 64)
|
||||
"""
|
||||
folder = Paths.get_artist_img_sm_path()
|
||||
fpath = Path(folder) / path.imgpath
|
||||
|
||||
if fpath.exists():
|
||||
return send_from_directory(path, imgpath)
|
||||
return send_from_directory(folder, path.imgpath)
|
||||
|
||||
return send_fallback_img("artist.webp")
|
||||
|
||||
|
||||
@api.route("/p/<imgpath>")
|
||||
def send_playlist_image(imgpath: str):
|
||||
path = Paths.get_playlist_img_path()
|
||||
fpath = Path(path) / imgpath
|
||||
class PlaylistImagePath(BaseModel):
|
||||
imgpath: str = Field(
|
||||
description="The image path",
|
||||
example="1.webp",
|
||||
)
|
||||
|
||||
|
||||
@api.get("/p/<imgpath>")
|
||||
def send_playlist_image(path: PlaylistImagePath):
|
||||
"""
|
||||
Get playlist image
|
||||
|
||||
Images are constructed as '{playlist_id}.webp'
|
||||
"""
|
||||
folder = Paths.get_playlist_img_path()
|
||||
fpath = Path(folder) / path.imgpath
|
||||
|
||||
if fpath.exists():
|
||||
return send_from_directory(path, imgpath)
|
||||
return send_from_directory(folder, path.imgpath)
|
||||
|
||||
return send_fallback_img("playlist.svg")
|
||||
|
||||
@@ -1,11 +1,38 @@
|
||||
from flask import Blueprint
|
||||
from flask_restful import Api
|
||||
from flask_openapi3 import Tag
|
||||
from flask_openapi3 import APIBlueprint
|
||||
from pydantic import Field
|
||||
from app.api.apischemas import TrackHashSchema
|
||||
|
||||
from app.api.logger.tracks import LogTrack
|
||||
from app.db.sqlite.logger.tracks import SQLiteTrackLogger as db
|
||||
from app.settings import Defaults
|
||||
|
||||
bp_tag = Tag(name="Logger", description="Log item plays")
|
||||
api = APIBlueprint("logger", __name__, url_prefix="/logger", abp_tags=[bp_tag])
|
||||
|
||||
|
||||
api_bp = Blueprint("logger", __name__, url_prefix="/logger")
|
||||
api = Api(api_bp)
|
||||
class LogTrackBody(TrackHashSchema):
|
||||
timestamp: int = Field(description="The timestamp of the track", example=1622217600)
|
||||
duration: int = Field(
|
||||
description="The duration of the track in seconds", example=300
|
||||
)
|
||||
source: str = Field(
|
||||
description="The play source of the track",
|
||||
example=f"al:{Defaults.API_ALBUMHASH}",
|
||||
)
|
||||
|
||||
|
||||
api.add_resource(LogTrack, "/track/log")
|
||||
@api.post("/track/log")
|
||||
def log_track(body: LogTrackBody):
|
||||
"""
|
||||
Log a track play to the database.
|
||||
"""
|
||||
trackhash = body.trackhash
|
||||
timestamp = body.timestamp
|
||||
duration = body.duration
|
||||
source = body.source
|
||||
|
||||
last_row = db.insert_track(
|
||||
trackhash=trackhash, timestamp=timestamp, duration=duration, source=source
|
||||
)
|
||||
|
||||
return {"last_row": last_row}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
from flask_restful import Resource, reqparse
|
||||
from app.db.sqlite.logger.tracks import SQLiteTrackLogger as db
|
||||
|
||||
parser = reqparse.RequestParser()
|
||||
parser.add_argument("trackhash", type=str, required=True)
|
||||
parser.add_argument("timestamp", type=int, required=True)
|
||||
parser.add_argument("duration", type=int, required=True)
|
||||
parser.add_argument("source", type=str, required=True)
|
||||
|
||||
|
||||
class LogTrack(Resource):
|
||||
def post(self):
|
||||
args = parser.parse_args(strict=True)
|
||||
|
||||
last_row = db.insert_track(
|
||||
args["trackhash"], args["duration"], args["source"], args["timestamp"]
|
||||
)
|
||||
|
||||
return {"last_row": last_row}
|
||||
+24
-21
@@ -1,5 +1,8 @@
|
||||
from flask import Blueprint, request
|
||||
from flask_openapi3 import Tag
|
||||
from flask_openapi3 import APIBlueprint
|
||||
from pydantic import Field
|
||||
|
||||
from app.api.apischemas import TrackHashSchema
|
||||
from app.lib.lyrics import (
|
||||
get_lyrics,
|
||||
check_lyrics_file,
|
||||
@@ -7,21 +10,24 @@ from app.lib.lyrics import (
|
||||
get_lyrics_from_tags,
|
||||
)
|
||||
|
||||
api = Blueprint("lyrics", __name__, url_prefix="")
|
||||
bp_tag = Tag(name="Lyrics", description="Get lyrics")
|
||||
api = APIBlueprint("lyrics", __name__, url_prefix="/lyrics", abp_tags=[bp_tag])
|
||||
|
||||
|
||||
@api.route("/lyrics", methods=["POST"])
|
||||
def send_lyrics():
|
||||
class SendLyricsBody(TrackHashSchema):
|
||||
filepath: str = Field(
|
||||
description="The path to the file",
|
||||
example="/path/to/file.mp3",
|
||||
)
|
||||
|
||||
|
||||
@api.post("")
|
||||
def send_lyrics(body: SendLyricsBody):
|
||||
"""
|
||||
Returns the lyrics for a track
|
||||
"""
|
||||
data = request.get_json()
|
||||
|
||||
filepath = data.get("filepath", None)
|
||||
trackhash = data.get("trackhash", None)
|
||||
|
||||
if filepath is None or trackhash is None:
|
||||
return {"error": "No filepath or trackhash provided"}, 400
|
||||
filepath = body.filepath
|
||||
trackhash = body.trackhash
|
||||
|
||||
is_synced = True
|
||||
lyrics, copyright = get_lyrics(filepath)
|
||||
@@ -38,15 +44,13 @@ def send_lyrics():
|
||||
return {"lyrics": lyrics, "synced": is_synced, "copyright": copyright}, 200
|
||||
|
||||
|
||||
@api.route("/lyrics/check", methods=["POST"])
|
||||
def check_lyrics():
|
||||
data = request.get_json()
|
||||
|
||||
filepath = data.get("filepath", None)
|
||||
trackhash = data.get("trackhash", None)
|
||||
|
||||
if filepath is None or trackhash is None:
|
||||
return {"error": "No filepath or trackhash provided"}, 400
|
||||
@api.post("/check")
|
||||
def check_lyrics(body: SendLyricsBody):
|
||||
"""
|
||||
Checks if lyrics exist for a track
|
||||
"""
|
||||
filepath = body.filepath
|
||||
trackhash = body.trackhash
|
||||
|
||||
exists = check_lyrics_file(filepath, trackhash)
|
||||
|
||||
@@ -56,4 +60,3 @@ def check_lyrics():
|
||||
exists = get_lyrics_from_tags(filepath, just_check=True)
|
||||
|
||||
return {"exists": exists}, 200
|
||||
|
||||
|
||||
+38
-15
@@ -1,37 +1,60 @@
|
||||
from flask import Blueprint, request
|
||||
|
||||
from flask_openapi3 import Tag
|
||||
from flask_openapi3 import APIBlueprint
|
||||
from pydantic import BaseModel, Field
|
||||
from app.db.sqlite.plugins import PluginsMethods
|
||||
|
||||
|
||||
api = Blueprint("plugins", __name__, url_prefix="/plugins")
|
||||
bp_tag = Tag(name="Plugins", description="Manage plugins")
|
||||
api = APIBlueprint("plugins", __name__, url_prefix="/plugins", abp_tags=[bp_tag])
|
||||
|
||||
|
||||
@api.route("/", methods=["GET"])
|
||||
@api.get("/")
|
||||
def get_all_plugins():
|
||||
"""
|
||||
List all plugins
|
||||
"""
|
||||
plugins = PluginsMethods.get_all_plugins()
|
||||
|
||||
return {"plugins": plugins}
|
||||
|
||||
|
||||
@api.route("/setactive", methods=["GET"])
|
||||
def activate_deactivate_plugin():
|
||||
name = request.args.get("plugin", None)
|
||||
state = request.args.get("state", None)
|
||||
class PluginBody(BaseModel):
|
||||
plugin: str = Field(description="The plugin name", example="lyrics")
|
||||
|
||||
if not name or not state:
|
||||
return {"error": "Missing plugin or state"}, 400
|
||||
|
||||
PluginsMethods.plugin_set_active(name, int(state))
|
||||
class PluginActivateBody(PluginBody):
|
||||
active: bool = Field(
|
||||
description="New plugin active state", example=False, default=False
|
||||
)
|
||||
|
||||
|
||||
@api.post("/setactive")
|
||||
def activate_deactivate_plugin(body: PluginActivateBody):
|
||||
"""
|
||||
Activate/Deactivate plugin
|
||||
"""
|
||||
name = body.plugin
|
||||
active = 1 if body.active else 0
|
||||
|
||||
PluginsMethods.plugin_set_active(name, active)
|
||||
|
||||
return {"message": "OK"}, 200
|
||||
|
||||
|
||||
@api.route("/settings", methods=["POST"])
|
||||
def update_plugin_settings():
|
||||
data = request.get_json()
|
||||
class PluginSettingsBody(PluginBody):
|
||||
settings: dict = Field(
|
||||
description="The new plugin settings", example={"key": "value"}
|
||||
)
|
||||
|
||||
plugin = data.get("plugin", None)
|
||||
settings = data.get("settings", None)
|
||||
|
||||
@api.post("/settings")
|
||||
def update_plugin_settings(body: PluginSettingsBody):
|
||||
"""
|
||||
Update plugin settings
|
||||
"""
|
||||
plugin = body.plugin
|
||||
settings = body.settings
|
||||
|
||||
if not plugin or not settings:
|
||||
return {"error": "Missing plugin or settings"}, 400
|
||||
|
||||
+28
-11
@@ -1,24 +1,41 @@
|
||||
from flask import Blueprint, request
|
||||
from flask_openapi3 import Tag
|
||||
from flask_openapi3 import APIBlueprint
|
||||
from pydantic import Field
|
||||
from app.api.apischemas import TrackHashSchema
|
||||
from app.lib.lyrics import format_synced_lyrics
|
||||
|
||||
from app.plugins.lyrics import Lyrics
|
||||
from app.settings import Defaults
|
||||
from app.utils.hashing import create_hash
|
||||
|
||||
api = Blueprint("lyricsplugin", __name__, url_prefix="/plugins/lyrics")
|
||||
bp_tag = Tag(name="Lyrics Plugin", description="Musixmatch lyrics plugin")
|
||||
api = APIBlueprint(
|
||||
"lyricsplugin", __name__, url_prefix="/plugins/lyrics", abp_tags=[bp_tag]
|
||||
)
|
||||
|
||||
|
||||
@api.route("/search", methods=["POST"])
|
||||
def search_lyrics():
|
||||
data = request.get_json()
|
||||
class LyricsSearchBody(TrackHashSchema):
|
||||
title: str = Field(description="The track title ", example=Defaults.API_TRACKNAME)
|
||||
artist: str = Field(description="The track artist ", example=Defaults.API_ARTISTNAME)
|
||||
album: str = Field(description="The track track album ", example=Defaults.API_ALBUMNAME)
|
||||
filepath: str = Field(
|
||||
description="Track filepath to save the lyrics file relative to",
|
||||
example="/home/cwilvx/temp/crazy song.mp3",
|
||||
)
|
||||
|
||||
trackhash = data.get("trackhash", "")
|
||||
title = data.get("title", "")
|
||||
artist = data.get("artist", "")
|
||||
album = data.get("album", "")
|
||||
filepath = data.get("filepath", None)
|
||||
|
||||
@api.post("/search")
|
||||
def search_lyrics(body: LyricsSearchBody):
|
||||
"""
|
||||
Search for lyrics by title and artist
|
||||
"""
|
||||
title = body.title
|
||||
artist = body.artist
|
||||
album = body.album
|
||||
filepath = body.filepath
|
||||
trackhash = body.trackhash
|
||||
|
||||
finder = Lyrics()
|
||||
|
||||
data = finder.search_lyrics_by_title_and_artist(title, artist)
|
||||
|
||||
if not data:
|
||||
|
||||
+46
-26
@@ -1,4 +1,8 @@
|
||||
from flask import Blueprint, request
|
||||
from typing import Any
|
||||
from flask import request
|
||||
from flask_openapi3 import Tag
|
||||
from flask_openapi3 import APIBlueprint
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from app.db.sqlite.plugins import PluginsMethods as pdb
|
||||
from app.db.sqlite.settings import SettingsSQLMethods as sdb
|
||||
@@ -12,7 +16,8 @@ from app.store.tracks import TrackStore
|
||||
from app.utils.generators import get_random_str
|
||||
from app.utils.threading import background
|
||||
|
||||
api = Blueprint("settings", __name__, url_prefix="")
|
||||
bp_tag = Tag(name="Settings", description="Customize stuff")
|
||||
api = APIBlueprint("settings", __name__, url_prefix="/notsettings", abp_tags=[bp_tag])
|
||||
|
||||
|
||||
def get_child_dirs(parent: str, children: list[str]):
|
||||
@@ -77,23 +82,24 @@ def finalize(new_: list[str], removed_: list[str], db_dirs_: list[str]):
|
||||
rebuild_store(db_dirs_)
|
||||
|
||||
|
||||
@api.route("/settings/add-root-dirs", methods=["POST"])
|
||||
def add_root_dirs():
|
||||
class AddRootDirsBody(BaseModel):
|
||||
new_dirs: list[str] = Field(
|
||||
description="The new directories to add",
|
||||
example=["/home/user/Music", "/home/user/Downloads"],
|
||||
)
|
||||
removed: list[str] = Field(
|
||||
description="The directories to remove",
|
||||
example=["/home/user/Downloads"],
|
||||
)
|
||||
|
||||
|
||||
@api.post("/add-root-dirs")
|
||||
def add_root_dirs(body: AddRootDirsBody):
|
||||
"""
|
||||
Add custom root directories to the database.
|
||||
"""
|
||||
msg = {"msg": "Failed! No directories were given."}
|
||||
|
||||
data = request.get_json()
|
||||
|
||||
if data is None:
|
||||
return msg, 400
|
||||
|
||||
try:
|
||||
new_dirs: list[str] = data["new_dirs"]
|
||||
removed_dirs: list[str] = data["removed"]
|
||||
except KeyError:
|
||||
return msg, 400
|
||||
new_dirs = body.new_dirs
|
||||
removed_dirs = body.removed
|
||||
|
||||
db_dirs = sdb.get_root_dirs()
|
||||
_h = "$home"
|
||||
@@ -132,10 +138,10 @@ def add_root_dirs():
|
||||
return {"root_dirs": db_dirs}
|
||||
|
||||
|
||||
@api.route("/settings/get-root-dirs", methods=["GET"])
|
||||
@api.get("/get-root-dirs")
|
||||
def get_root_dirs():
|
||||
"""
|
||||
Get custom root directories from the database.
|
||||
Get root directories
|
||||
"""
|
||||
dirs = sdb.get_root_dirs()
|
||||
|
||||
@@ -154,10 +160,10 @@ mapp = {
|
||||
}
|
||||
|
||||
|
||||
@api.route("/settings/", methods=["GET"])
|
||||
@api.get("")
|
||||
def get_all_settings():
|
||||
"""
|
||||
Get all settings from the database.
|
||||
Get all settings
|
||||
"""
|
||||
|
||||
settings = sdb.get_all_settings()
|
||||
@@ -195,10 +201,24 @@ def reload_all_for_set_setting():
|
||||
reload_everything(get_random_str())
|
||||
|
||||
|
||||
@api.route("/settings/set", methods=["POST"])
|
||||
def set_setting():
|
||||
key = request.get_json().get("key")
|
||||
value = request.get_json().get("value")
|
||||
class SetSettingBody(BaseModel):
|
||||
key: str = Field(
|
||||
description="The setting key",
|
||||
example="artist_separators",
|
||||
)
|
||||
value: Any = Field(
|
||||
description="The setting value",
|
||||
example=",",
|
||||
)
|
||||
|
||||
|
||||
@api.post("/set")
|
||||
def set_setting(body: SetSettingBody):
|
||||
"""
|
||||
Set a setting.
|
||||
"""
|
||||
key = body.key
|
||||
value = body.value
|
||||
|
||||
if key is None or value is None or key == "root_dirs":
|
||||
return {"msg": "Invalid arguments!"}, 400
|
||||
@@ -235,10 +255,10 @@ def run_populate():
|
||||
populate.Populate(instance_key=get_random_str())
|
||||
|
||||
|
||||
@api.route("/settings/trigger-scan", methods=["GET"])
|
||||
@api.get("/trigger-scan")
|
||||
def trigger_scan():
|
||||
"""
|
||||
Triggers a scan.
|
||||
Triggers scan for new music
|
||||
"""
|
||||
run_populate()
|
||||
|
||||
|
||||
+11
-1
@@ -1,6 +1,7 @@
|
||||
"""
|
||||
Handles arguments passed to the program.
|
||||
"""
|
||||
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
@@ -9,6 +10,7 @@ import PyInstaller.__main__ as bundler
|
||||
from app import settings
|
||||
from app.logger import log
|
||||
from app.print_help import HELP_MESSAGE
|
||||
from app.utils.paths import getFlaskOpenApiPath
|
||||
from app.utils.xdg_utils import get_xdg_config_dir
|
||||
from app.utils.wintools import is_windows
|
||||
|
||||
@@ -45,6 +47,8 @@ class HandleArgs:
|
||||
|
||||
config_keys = [
|
||||
"SWINGMUSIC_APP_VERSION",
|
||||
"GIT_LATEST_COMMIT_HASH",
|
||||
"GIT_CURRENT_BRANCH",
|
||||
]
|
||||
|
||||
lines = []
|
||||
@@ -65,6 +69,8 @@ class HandleArgs:
|
||||
|
||||
_s = ";" if is_windows() else ":"
|
||||
|
||||
flask_openapi_path = getFlaskOpenApiPath()
|
||||
|
||||
bundler.run(
|
||||
[
|
||||
"manage.py",
|
||||
@@ -74,6 +80,7 @@ class HandleArgs:
|
||||
"--clean",
|
||||
f"--add-data=assets{_s}assets",
|
||||
f"--add-data=client{_s}client",
|
||||
f"--add-data={flask_openapi_path}/templates/static{_s}flask_openapi3/templates/static",
|
||||
f"--icon=assets/logo-fill.light.ico",
|
||||
"-y",
|
||||
]
|
||||
@@ -176,5 +183,8 @@ class HandleArgs:
|
||||
@staticmethod
|
||||
def handle_version():
|
||||
if any((a in ARGS for a in ALLARGS.version)):
|
||||
print(settings.Keys.SWINGMUSIC_APP_VERSION)
|
||||
print(f"VERSION: v{settings.Keys.SWINGMUSIC_APP_VERSION}")
|
||||
print(
|
||||
f"COMMIT#: {settings.Keys.GIT_CURRENT_BRANCH}/{settings.Keys.GIT_LATEST_COMMIT_HASH}"
|
||||
)
|
||||
sys.exit(0)
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
SWINGMUSIC_APP_VERSION = ""
|
||||
GIT_LATEST_COMMIT_HASH = ""
|
||||
GIT_CURRENT_BRANCH = ""
|
||||
|
||||
@@ -66,9 +66,9 @@ class PluginsMethods:
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def plugin_set_active(cls, name: str, state: int):
|
||||
def plugin_set_active(cls, name: str, active: int):
|
||||
with SQLiteManager(userdata_db=True) as cur:
|
||||
cur.execute("UPDATE plugins SET active=? WHERE name=?", (state, name))
|
||||
cur.execute("UPDATE plugins SET active=? WHERE name=?", (active, name))
|
||||
cur.close()
|
||||
|
||||
@classmethod
|
||||
|
||||
+33
-3
@@ -3,6 +3,7 @@ Contains default configs
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
@@ -103,6 +104,7 @@ class Defaults:
|
||||
API_TRACKHASH = "0853280a12"
|
||||
API_ALBUMNAME = "Rumours"
|
||||
API_ARTISTNAME = "girl in red"
|
||||
API_TRACKNAME = "Apartment 402"
|
||||
|
||||
API_CARD_LIMIT = 6
|
||||
|
||||
@@ -242,21 +244,49 @@ class TCOLOR:
|
||||
# credits: https://stackoverflow.com/a/287944
|
||||
|
||||
|
||||
def getLatestCommitHash():
|
||||
"""
|
||||
Returns the latest git commit hash for the current branch
|
||||
"""
|
||||
|
||||
try:
|
||||
hash = subprocess.check_output(["git", "rev-parse", "--short", "HEAD"])
|
||||
return hash.decode("utf-8").strip()
|
||||
except:
|
||||
return ""
|
||||
|
||||
|
||||
def getCurrentBranch():
|
||||
"""
|
||||
Returns the current git branch
|
||||
"""
|
||||
|
||||
try:
|
||||
branch = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"])
|
||||
return branch.decode("utf-8").strip()
|
||||
except:
|
||||
return ""
|
||||
|
||||
|
||||
class Keys:
|
||||
SWINGMUSIC_APP_VERSION = os.environ.get("SWINGMUSIC_APP_VERSION")
|
||||
GIT_LATEST_COMMIT_HASH = "<unset>"
|
||||
GIT_CURRENT_BRANCH = "<unset>"
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
if IS_BUILD:
|
||||
cls.SWINGMUSIC_APP_VERSION = configs.SWINGMUSIC_APP_VERSION
|
||||
cls.GIT_LATEST_COMMIT_HASH = configs.GIT_LATEST_COMMIT_HASH
|
||||
cls.GIT_CURRENT_BRANCH = configs.GIT_CURRENT_BRANCH
|
||||
else:
|
||||
cls.GIT_LATEST_COMMIT_HASH = getLatestCommitHash()
|
||||
cls.GIT_CURRENT_BRANCH = getCurrentBranch()
|
||||
|
||||
cls.verify_keys()
|
||||
|
||||
@classmethod
|
||||
def verify_keys(cls):
|
||||
# if not cls.LASTFM_API_KEY:
|
||||
# print("ERROR: LASTFM_API_KEY not set in environment")
|
||||
# sys.exit(0)
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import sys
|
||||
|
||||
|
||||
def getFlaskOpenApiPath():
|
||||
"""
|
||||
Used to retrieve the path to the flask_openapi3 package
|
||||
|
||||
See: https://github.com/luolingchun/flask-openapi3/issues/147
|
||||
"""
|
||||
site_packages_path = [p for p in sys.path if "site-packages" in p][0]
|
||||
|
||||
return f"{site_packages_path}/flask_openapi3"
|
||||
Generated
+1
-46
@@ -11,20 +11,6 @@ files = [
|
||||
{file = "altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aniso8601"
|
||||
version = "9.0.1"
|
||||
description = "A library for parsing ISO 8601 strings."
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f"},
|
||||
{file = "aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
dev = ["black", "coverage", "isort", "pre-commit", "pyenchant", "pylint"]
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
version = "0.6.0"
|
||||
@@ -592,26 +578,6 @@ dotenv = ["python-dotenv"]
|
||||
email = ["email-validator"]
|
||||
yaml = ["pyyaml"]
|
||||
|
||||
[[package]]
|
||||
name = "flask-restful"
|
||||
version = "0.3.10"
|
||||
description = "Simple framework for creating REST APIs"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "Flask-RESTful-0.3.10.tar.gz", hash = "sha256:fe4af2ef0027df8f9b4f797aba20c5566801b6ade995ac63b588abf1a59cec37"},
|
||||
{file = "Flask_RESTful-0.3.10-py2.py3-none-any.whl", hash = "sha256:1cf93c535172f112e080b0d4503a8d15f93a48c88bdd36dd87269bdaf405051b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
aniso8601 = ">=0.82"
|
||||
Flask = ">=0.8"
|
||||
pytz = "*"
|
||||
six = ">=1.3.0"
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx"]
|
||||
|
||||
[[package]]
|
||||
name = "gevent"
|
||||
version = "23.9.1"
|
||||
@@ -1697,17 +1663,6 @@ files = [
|
||||
[package.dependencies]
|
||||
six = ">=1.5"
|
||||
|
||||
[[package]]
|
||||
name = "pytz"
|
||||
version = "2023.3.post1"
|
||||
description = "World timezone definitions, modern and historical"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"},
|
||||
{file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pywin32"
|
||||
version = "306"
|
||||
@@ -2513,4 +2468,4 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.10,<3.12"
|
||||
content-hash = "7969ca61599f24a005909514cf11d35dd11b6f82f5958bb84c271d8156399755"
|
||||
content-hash = "feb13f92b7b3a909fcb851860a405b96579feac0e2dde7681ed0e9c381c4f6cd"
|
||||
|
||||
@@ -20,7 +20,6 @@ show-in-file-manager = "^1.1.4"
|
||||
flask-compress = "^1.13"
|
||||
tabulate = "^0.9.0"
|
||||
setproctitle = "^1.3.2"
|
||||
flask-restful = "^0.3.10"
|
||||
locust = "^2.20.1"
|
||||
waitress = "^2.1.2"
|
||||
watchdog = "^4.0.0"
|
||||
|
||||
Reference in New Issue
Block a user