mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-03 20:13:02 +00:00
add docs for playlist endpoints
+ limit recently added tracks to 100
This commit is contained in:
committed by
Mungai Njoroge
parent
4edb3a5e7a
commit
ae031014a9
@@ -1,6 +1,7 @@
|
||||
# What's New?
|
||||
|
||||
- Hovering on recent favorite item will show how long ago it was ♥ed
|
||||
- Recently added playlist returns a max of 100 tracks, but without a cutoff period
|
||||
|
||||
# Development
|
||||
- API documentation on /openapi
|
||||
+4
-2
@@ -8,6 +8,8 @@ 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
|
||||
@@ -68,8 +70,8 @@ def create_api():
|
||||
app.register_api(artist.api)
|
||||
app.register_api(send_file.api)
|
||||
app.register_api(search.api)
|
||||
app.register_blueprint(folder.api)
|
||||
app.register_blueprint(playlist.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)
|
||||
|
||||
+6
-3
@@ -26,7 +26,7 @@ api = APIBlueprint("artist", __name__, url_prefix="/artist", abp_tags=[bp_tag])
|
||||
@api.get("/<string:artisthash>")
|
||||
def get_artist(path: ArtistHashSchema, query: TrackLimitSchema):
|
||||
"""
|
||||
Get artist data.
|
||||
Get artist
|
||||
|
||||
Returns artist data, tracks and genres for the given artisthash.
|
||||
"""
|
||||
@@ -89,6 +89,9 @@ class GetArtistAlbumsQuery(AlbumLimitSchema):
|
||||
|
||||
@api.get("/<artisthash>/albums")
|
||||
def get_artist_albums(path: ArtistHashSchema, query: GetArtistAlbumsQuery):
|
||||
"""
|
||||
Get artist albums.
|
||||
"""
|
||||
return_all = query.all
|
||||
artisthash = path.artisthash
|
||||
|
||||
@@ -177,7 +180,7 @@ def get_artist_albums(path: ArtistHashSchema, query: GetArtistAlbumsQuery):
|
||||
@api.get("/<artisthash>/tracks")
|
||||
def get_all_artist_tracks(path: ArtistHashSchema):
|
||||
"""
|
||||
Get all artist tracks
|
||||
Get artist tracks
|
||||
|
||||
Returns all artists by a given artist.
|
||||
"""
|
||||
@@ -189,7 +192,7 @@ def get_all_artist_tracks(path: ArtistHashSchema):
|
||||
@api.get("/<artisthash>/similar")
|
||||
def get_similar_artists(path: ArtistHashSchema, query: ArtistLimitSchema):
|
||||
"""
|
||||
Returns similar artists.
|
||||
Get similar artists.
|
||||
"""
|
||||
limit = query.limit
|
||||
|
||||
|
||||
+59
-37
@@ -1,11 +1,14 @@
|
||||
"""
|
||||
Contains all the folder routes.
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import psutil
|
||||
from flask import Blueprint, request
|
||||
from pydantic import BaseModel, Field
|
||||
from flask_openapi3 import Tag
|
||||
from flask_openapi3 import APIBlueprint
|
||||
from showinfm import show_in_file_manager
|
||||
|
||||
from app import settings
|
||||
@@ -15,24 +18,24 @@ from app.serializers.track import serialize_track
|
||||
from app.store.tracks import TrackStore as store
|
||||
from app.utils.wintools import is_windows, win_replace_slash
|
||||
|
||||
api = Blueprint("folder", __name__, url_prefix="")
|
||||
tag = Tag(name="Folders", description="Get folders and tracks in a directory")
|
||||
api = APIBlueprint("folder", __name__, url_prefix="/folder", abp_tags=[tag])
|
||||
|
||||
|
||||
@api.route("/folder", methods=["POST"])
|
||||
def get_folder_tree():
|
||||
class FolderTree(BaseModel):
|
||||
folder: str = Field("$home", description="The folder to things from")
|
||||
tracks_only: bool = Field(False, description="Whether to only get tracks")
|
||||
|
||||
|
||||
@api.post("")
|
||||
def get_folder_tree(body: FolderTree):
|
||||
"""
|
||||
Get folder
|
||||
|
||||
Returns a list of all the folders and tracks in the given folder.
|
||||
"""
|
||||
data = request.get_json()
|
||||
req_dir = "$home"
|
||||
|
||||
tracks_only = False
|
||||
if data is not None:
|
||||
try:
|
||||
req_dir: str = data["folder"]
|
||||
tracks_only: bool = data["tracks_only"]
|
||||
except KeyError:
|
||||
req_dir = "$home"
|
||||
req_dir = body.folder
|
||||
tracks_only = body.tracks_only
|
||||
|
||||
root_dirs = db.get_root_dirs()
|
||||
root_dirs.sort()
|
||||
@@ -92,18 +95,23 @@ def get_all_drives(is_win: bool = False):
|
||||
return drives
|
||||
|
||||
|
||||
@api.route("/folder/dir-browser", methods=["POST"])
|
||||
def list_folders():
|
||||
"""
|
||||
Returns a list of all the folders in the given folder.
|
||||
"""
|
||||
data = request.get_json()
|
||||
is_win = is_windows()
|
||||
class DirBrowserBody(BaseModel):
|
||||
folder: str = Field(
|
||||
"$root",
|
||||
description="The folder to list directories from",
|
||||
)
|
||||
|
||||
try:
|
||||
req_dir: str = data["folder"]
|
||||
except KeyError:
|
||||
req_dir = "$root"
|
||||
|
||||
@api.post("/folder/dir-browser")
|
||||
def list_folders(body: DirBrowserBody):
|
||||
"""
|
||||
List folders
|
||||
|
||||
Returns a list of all the folders in the given folder.
|
||||
Used when selecting root dirs.
|
||||
"""
|
||||
req_dir = body.folder
|
||||
is_win = is_windows()
|
||||
|
||||
if req_dir == "$root":
|
||||
return {
|
||||
@@ -131,26 +139,40 @@ def list_folders():
|
||||
}
|
||||
|
||||
|
||||
@api.route("/folder/show-in-files")
|
||||
def open_in_file_manager():
|
||||
path = request.args.get("path")
|
||||
class FolderOpenInFileManagerQuery(BaseModel):
|
||||
path: str = Field(
|
||||
description="The path to open in the file manager",
|
||||
)
|
||||
|
||||
if path is None:
|
||||
return {"error": "No path provided."}, 400
|
||||
|
||||
show_in_file_manager(path)
|
||||
@api.get("/folder/show-in-files")
|
||||
def open_in_file_manager(query: FolderOpenInFileManagerQuery):
|
||||
"""
|
||||
Open in file manager
|
||||
|
||||
Opens the given path in the file manager on the host machine.
|
||||
"""
|
||||
show_in_file_manager(query.path)
|
||||
|
||||
return {"success": True}
|
||||
|
||||
|
||||
@api.route("/folder/tracks/all")
|
||||
def get_tracks_in_path():
|
||||
path = request.args.get("path")
|
||||
class GetTracksInPathQuery(BaseModel):
|
||||
path: str = Field(
|
||||
description="The path to get tracks from",
|
||||
)
|
||||
|
||||
if path is None:
|
||||
return {"error": "No path provided."}, 400
|
||||
|
||||
tracks = store.get_tracks_in_path(path)
|
||||
@api.get("/folder/tracks/all")
|
||||
def get_tracks_in_path(query: GetTracksInPathQuery):
|
||||
"""
|
||||
Get tracks in path
|
||||
|
||||
Gets all (or a max of 300) tracks from the given path and its subdirectories.
|
||||
|
||||
Used when adding tracks to the queue.
|
||||
"""
|
||||
tracks = store.get_tracks_in_path(query.path)
|
||||
tracks = sorted(tracks, key=lambda i: i.last_mod)
|
||||
tracks = (serialize_track(t) for t in tracks if Path(t.filepath).exists())
|
||||
|
||||
|
||||
+118
-110
@@ -1,12 +1,15 @@
|
||||
"""
|
||||
All playlist-related routes.
|
||||
"""
|
||||
|
||||
import json
|
||||
from datetime import datetime
|
||||
import pathlib
|
||||
|
||||
from flask import Blueprint, request
|
||||
from PIL import UnidentifiedImageError, Image
|
||||
from pydantic import BaseModel, Field
|
||||
from flask_openapi3 import Tag
|
||||
from flask_openapi3 import APIBlueprint, FileStorage
|
||||
|
||||
from app import models
|
||||
from app.db.sqlite.playlists import SQLitePlaylistMethods
|
||||
@@ -18,23 +21,26 @@ from app.utils.dates import create_new_date, date_string_to_time_passed
|
||||
from app.utils.remove_duplicates import remove_duplicates
|
||||
from app.settings import Paths
|
||||
|
||||
api = Blueprint("playlist", __name__, url_prefix="/")
|
||||
tag = Tag(name="Playlists", description="Get and manage playlists")
|
||||
api = APIBlueprint("playlists", __name__, url_prefix="/playlists", abp_tags=[tag])
|
||||
|
||||
PL = SQLitePlaylistMethods
|
||||
|
||||
|
||||
@api.route("/playlists", methods=["GET"])
|
||||
def send_all_playlists():
|
||||
class SendAllPlaylistsQuery(BaseModel):
|
||||
no_images: bool = Field(False, description="Whether to include images")
|
||||
|
||||
|
||||
@api.get("", methods=["GET"])
|
||||
def send_all_playlists(query: SendAllPlaylistsQuery):
|
||||
"""
|
||||
Gets all the playlists.
|
||||
"""
|
||||
no_images = request.args.get("no_images", False)
|
||||
|
||||
playlists = PL.get_all_playlists()
|
||||
playlists = list(playlists)
|
||||
|
||||
for playlist in playlists:
|
||||
if not no_images:
|
||||
if not query.no_images:
|
||||
playlist.images = playlistlib.get_first_4_images(
|
||||
trackhashes=playlist.trackhashes
|
||||
)
|
||||
@@ -69,22 +75,23 @@ def insert_playlist(name: str, image: str = None):
|
||||
return PL.insert_one_playlist(playlist)
|
||||
|
||||
|
||||
@api.route("/playlist/new", methods=["POST"])
|
||||
def create_playlist():
|
||||
class CreatePlaylistBody(BaseModel):
|
||||
name: str = Field(..., description="The name of the playlist")
|
||||
|
||||
|
||||
@api.post("/new")
|
||||
def create_playlist(body: CreatePlaylistBody):
|
||||
"""
|
||||
New playlist
|
||||
|
||||
Creates a new playlist. Accepts POST method with a JSON body.
|
||||
"""
|
||||
data = request.get_json()
|
||||
|
||||
if data is None:
|
||||
return {"error": "Playlist name not provided"}, 400
|
||||
|
||||
existing_playlist_count = PL.count_playlist_by_name(data["name"])
|
||||
existing_playlist_count = PL.count_playlist_by_name(body.name)
|
||||
|
||||
if existing_playlist_count > 0:
|
||||
return {"error": "Playlist already exists"}, 409
|
||||
|
||||
playlist = insert_playlist(data["name"])
|
||||
playlist = insert_playlist(body.name)
|
||||
|
||||
if playlist is None:
|
||||
return {"error": "Playlist could not be created"}, 500
|
||||
@@ -119,25 +126,30 @@ def get_artist_trackhashes(artisthash: str):
|
||||
return [t.trackhash for t in tracks]
|
||||
|
||||
|
||||
@api.route("/playlist/<playlist_id>/add", methods=["POST"])
|
||||
def add_item_to_playlist(playlist_id: str):
|
||||
class PlaylistIDPath(BaseModel):
|
||||
# INFO: playlistid string examples: "recentlyadded"
|
||||
playlistid: int | str = Field(..., description="The ID of the playlist")
|
||||
|
||||
|
||||
class AddItemToPlaylistBody(BaseModel):
|
||||
itemtype: str = Field(
|
||||
default="tracks",
|
||||
description="The type of item to add",
|
||||
examples=["tracks", "folder", "album", "artist"],
|
||||
)
|
||||
itemhash: str = Field(..., description="The hash of the item to add")
|
||||
|
||||
|
||||
@api.post("/<playlistid>/add")
|
||||
def add_item_to_playlist(path: PlaylistIDPath, body: AddItemToPlaylistBody):
|
||||
"""
|
||||
Takes a playlist ID and a track hash, and adds the track to the playlist
|
||||
Add to playlist.
|
||||
|
||||
If itemtype is not "tracks", itemhash is expected to be a folder, album or artist hash.
|
||||
"""
|
||||
data = request.get_json()
|
||||
|
||||
if data is None:
|
||||
return {"error": "Track hash not provided"}, 400
|
||||
|
||||
try:
|
||||
itemtype = data["itemtype"]
|
||||
except KeyError:
|
||||
itemtype = None
|
||||
|
||||
try:
|
||||
itemhash: str = data["itemhash"]
|
||||
except KeyError:
|
||||
itemhash = None
|
||||
itemtype = body.itemtype
|
||||
itemhash = body.itemhash
|
||||
playlist_id = path.playlistid
|
||||
|
||||
if itemtype == "tracks":
|
||||
trackhashes = itemhash.split(",")
|
||||
@@ -158,13 +170,17 @@ def add_item_to_playlist(playlist_id: str):
|
||||
return {"msg": "Done"}, 200
|
||||
|
||||
|
||||
@api.route("/playlist/<playlistid>")
|
||||
def get_playlist(playlistid: str):
|
||||
class GetPlaylistQuery(BaseModel):
|
||||
no_tracks: bool = Field(False, description="Whether to include tracks")
|
||||
|
||||
|
||||
@api.get("/<playlistid>")
|
||||
def get_playlist(path: PlaylistIDPath, query: GetPlaylistQuery):
|
||||
"""
|
||||
Gets a playlist by id, and if it exists, it gets all the tracks in the playlist and returns them.
|
||||
Get playlist by id
|
||||
"""
|
||||
no_tracks = request.args.get("no_tracks", "false")
|
||||
no_tracks = no_tracks == "true"
|
||||
no_tracks = query.no_tracks
|
||||
playlistid = path.playlistid
|
||||
|
||||
is_recently_added = playlistid == "recentlyadded"
|
||||
|
||||
@@ -201,31 +217,40 @@ def get_playlist(playlistid: str):
|
||||
return {"info": playlist, "tracks": tracks if not no_tracks else []}
|
||||
|
||||
|
||||
@api.route("/playlist/<playlistid>/update", methods=["PUT"])
|
||||
def update_playlist_info(playlistid: str):
|
||||
if playlistid is None:
|
||||
return {"error": "Playlist ID not provided"}, 400
|
||||
class UpdatePlaylistForm(BaseModel):
|
||||
image: FileStorage = Field(None, description="The image file")
|
||||
name: str = Field(..., description="The name of the playlist")
|
||||
settings: str = Field(
|
||||
...,
|
||||
description="The settings of the playlist",
|
||||
example='{"has_gif": false, "banner_pos": 50, "square_img": false, "pinned": false}',
|
||||
)
|
||||
|
||||
db_playlist = PL.get_playlist_by_id(int(playlistid))
|
||||
|
||||
@api.put("/<playlistid>/update", methods=["PUT"])
|
||||
def update_playlist_info(path: PlaylistIDPath, form: UpdatePlaylistForm):
|
||||
"""
|
||||
Update playlist
|
||||
"""
|
||||
playlistid = path.playlistid
|
||||
db_playlist = PL.get_playlist_by_id(playlistid)
|
||||
|
||||
if db_playlist is None:
|
||||
return {"error": "Playlist not found"}, 404
|
||||
|
||||
image = None
|
||||
image = form.image
|
||||
|
||||
if "image" in request.files:
|
||||
image = request.files["image"]
|
||||
if form.image:
|
||||
image = form.image
|
||||
|
||||
data = request.form
|
||||
|
||||
settings = json.loads(data.get("settings"))
|
||||
settings = json.loads(form.settings)
|
||||
settings["has_gif"] = False
|
||||
|
||||
playlist = {
|
||||
"id": int(playlistid),
|
||||
"id": playlistid,
|
||||
"image": db_playlist.image,
|
||||
"last_updated": create_new_date(),
|
||||
"name": str(data.get("name")).strip(),
|
||||
"name": str(form.name).strip(),
|
||||
"settings": settings,
|
||||
"trackhashes": json.dumps([]),
|
||||
}
|
||||
@@ -247,7 +272,7 @@ def update_playlist_info(playlistid: str):
|
||||
|
||||
p_tuple = (*playlist.values(),)
|
||||
|
||||
PL.update_playlist(int(playlistid), playlist)
|
||||
PL.update_playlist(playlistid, playlist)
|
||||
|
||||
playlist = models.Playlist(*p_tuple)
|
||||
playlist.last_updated = date_string_to_time_passed(playlist.last_updated)
|
||||
@@ -257,12 +282,12 @@ def update_playlist_info(playlistid: str):
|
||||
}
|
||||
|
||||
|
||||
@api.route("/playlist/<playlistid>/pin_unpin", methods=["GET"])
|
||||
def pin_unpin_playlist(playlistid: str):
|
||||
@api.post("/<playlistid>/pin_unpin")
|
||||
def pin_unpin_playlist(path: PlaylistIDPath):
|
||||
"""
|
||||
Pins or unpins a playlist.
|
||||
Pin playlist.
|
||||
"""
|
||||
playlist = PL.get_playlist_by_id(int(playlistid))
|
||||
playlist = PL.get_playlist_by_id(path.playlistid)
|
||||
|
||||
if playlist is None:
|
||||
return {"error": "Playlist not found"}, 404
|
||||
@@ -274,23 +299,22 @@ def pin_unpin_playlist(playlistid: str):
|
||||
except KeyError:
|
||||
settings["pinned"] = True
|
||||
|
||||
PL.update_settings(int(playlistid), settings)
|
||||
PL.update_settings(path.playlistid, settings)
|
||||
|
||||
return {"msg": "Done"}, 200
|
||||
|
||||
|
||||
@api.route("/playlist/<playlistid>/remove-img", methods=["GET"])
|
||||
def remove_playlist_image(playlistid: str):
|
||||
@api.delete("/<playlistid>/remove-img")
|
||||
def remove_playlist_image(path: PlaylistIDPath):
|
||||
"""
|
||||
Removes the playlist image.
|
||||
Clear playlist image.
|
||||
"""
|
||||
pid = int(playlistid)
|
||||
playlist = PL.get_playlist_by_id(pid)
|
||||
playlist = PL.get_playlist_by_id(path.playlistid)
|
||||
|
||||
if playlist is None:
|
||||
return {"error": "Playlist not found"}, 404
|
||||
|
||||
PL.remove_banner(pid)
|
||||
PL.remove_banner(path.playlistid)
|
||||
|
||||
playlist.image = None
|
||||
playlist.thumb = None
|
||||
@@ -303,78 +327,62 @@ def remove_playlist_image(playlistid: str):
|
||||
return {"playlist": playlist}, 200
|
||||
|
||||
|
||||
@api.route("/playlist/delete", methods=["POST"])
|
||||
def remove_playlist():
|
||||
@api.delete("/<playlistid>/delete", methods=["DELETE"])
|
||||
def remove_playlist(path: PlaylistIDPath):
|
||||
"""
|
||||
Deletes a playlist by ID.
|
||||
Delete playlist
|
||||
"""
|
||||
message = {"error": "Playlist ID not provided"}
|
||||
data = request.get_json()
|
||||
|
||||
if data is None:
|
||||
return message, 400
|
||||
|
||||
try:
|
||||
pid = data["pid"]
|
||||
except KeyError:
|
||||
return message, 400
|
||||
|
||||
PL.delete_playlist(pid)
|
||||
PL.delete_playlist(path.playlistid)
|
||||
|
||||
return {"msg": "Done"}, 200
|
||||
|
||||
|
||||
@api.route("/playlist/<pid>/remove-tracks", methods=["POST"])
|
||||
def remove_tracks_from_playlist(pid: int):
|
||||
data = request.get_json()
|
||||
class RemoveTracksFromPlaylistBody(BaseModel):
|
||||
tracks: list[dict] = Field(..., description="A list of trackhashes to remove")
|
||||
|
||||
if data is None:
|
||||
return {"error": "Track index not provided"}, 400
|
||||
|
||||
@api.post("/<playlistid>/remove-tracks")
|
||||
def remove_tracks_from_playlist(
|
||||
path: PlaylistIDPath, body: RemoveTracksFromPlaylistBody
|
||||
):
|
||||
"""
|
||||
Remove track from playlist
|
||||
"""
|
||||
# A track looks like this:
|
||||
# {
|
||||
# trackhash: str;
|
||||
# index: int;
|
||||
# }
|
||||
|
||||
tracks = data["tracks"]
|
||||
PL.remove_tracks_from_playlist(pid, tracks)
|
||||
PL.remove_tracks_from_playlist(path.playlistid, body.tracks)
|
||||
|
||||
return {"msg": "Done"}, 200
|
||||
|
||||
|
||||
def playlist_exists(name: str) -> bool:
|
||||
def playlist_name_exists(name: str) -> bool:
|
||||
return PL.count_playlist_by_name(name) > 0
|
||||
|
||||
|
||||
@api.route("/playlist/save-item", methods=["POST"])
|
||||
def save_item_as_playlist():
|
||||
data = request.get_json()
|
||||
msg = {"error": "'itemtype', 'playlist_name' and 'itemhash' not provided"}, 400
|
||||
class SavePlaylistAsItemBody(BaseModel):
|
||||
itemtype: str = Field(..., description="The type of item", example="tracks")
|
||||
playlist_name: str = Field(..., description="The name of the playlist")
|
||||
itemhash: str = Field(..., description="The hash of the item to save")
|
||||
|
||||
if data is None:
|
||||
return msg
|
||||
|
||||
try:
|
||||
playlist_name = data["playlist_name"]
|
||||
except KeyError:
|
||||
playlist_name = None
|
||||
@api.post("/save-item")
|
||||
def save_item_as_playlist(body: SavePlaylistAsItemBody):
|
||||
"""
|
||||
Save as playlist
|
||||
|
||||
if playlist_exists(playlist_name):
|
||||
Saves a track, album, artist or folder as a playlist
|
||||
"""
|
||||
itemtype = body.itemtype
|
||||
playlist_name = body.playlist_name
|
||||
itemhash = body.itemhash
|
||||
|
||||
if playlist_name_exists(playlist_name):
|
||||
return {"error": "Playlist already exists"}, 409
|
||||
|
||||
try:
|
||||
itemtype = data["itemtype"]
|
||||
except KeyError:
|
||||
itemtype = None
|
||||
|
||||
try:
|
||||
itemhash: str = data["itemhash"]
|
||||
except KeyError:
|
||||
itemhash = None
|
||||
|
||||
if itemtype is None or playlist_name is None or itemhash is None:
|
||||
return msg
|
||||
|
||||
if itemtype == "tracks":
|
||||
trackhashes = itemhash.split(",")
|
||||
elif itemtype == "folder":
|
||||
|
||||
+9
-5
@@ -3,8 +3,8 @@ Contains all the search routes.
|
||||
"""
|
||||
|
||||
from flask import request
|
||||
from pydantic import BaseModel, Field
|
||||
from unidecode import unidecode
|
||||
from pydantic import BaseModel, Field
|
||||
from flask_openapi3 import Tag
|
||||
from flask_openapi3 import APIBlueprint
|
||||
|
||||
@@ -75,7 +75,7 @@ class SearchQuery(BaseModel):
|
||||
@api.get("/tracks")
|
||||
def search_tracks(query: SearchQuery):
|
||||
"""
|
||||
Searches for tracks
|
||||
Search tracks
|
||||
"""
|
||||
|
||||
query = query.q
|
||||
@@ -92,7 +92,7 @@ def search_tracks(query: SearchQuery):
|
||||
@api.get("/albums")
|
||||
def search_albums(query: SearchQuery):
|
||||
"""
|
||||
Searches for albums.
|
||||
Search albums.
|
||||
"""
|
||||
|
||||
query = query.q
|
||||
@@ -109,7 +109,7 @@ def search_albums(query: SearchQuery):
|
||||
@api.get("/artists")
|
||||
def search_artists(query: SearchQuery):
|
||||
"""
|
||||
Searches for artists.
|
||||
Search artists.
|
||||
"""
|
||||
|
||||
query = query.q
|
||||
@@ -134,7 +134,9 @@ class TopResultsQuery(SearchQuery):
|
||||
@api.get("/top")
|
||||
def get_top_results(query: TopResultsQuery):
|
||||
"""
|
||||
Returns the top results for the search query.
|
||||
Get top results
|
||||
|
||||
Returns the top results for the given query.
|
||||
"""
|
||||
|
||||
query = query.q
|
||||
@@ -156,6 +158,8 @@ class SearchLoadMoreQuery(SearchQuery):
|
||||
@api.get("/loadmore")
|
||||
def search_load_more(query: SearchLoadMoreQuery):
|
||||
"""
|
||||
Load more
|
||||
|
||||
Returns more songs, albums or artists from a search query.
|
||||
|
||||
NOTE: You must first initiate a search using the `/search` endpoint.
|
||||
|
||||
@@ -12,7 +12,7 @@ from app.serializers.artist import serialize_for_card
|
||||
|
||||
from itertools import groupby
|
||||
|
||||
from app.utils.dates import timestamp_from_days_ago, timestamp_to_time_passed
|
||||
from app.utils.dates import timestamp_to_time_passed
|
||||
|
||||
older_albums = set()
|
||||
older_artists = set()
|
||||
@@ -216,8 +216,6 @@ def get_recent_items(limit: int = 7):
|
||||
return recent_items
|
||||
|
||||
|
||||
def get_recent_tracks(cutoff_days: int):
|
||||
def get_recent_tracks(limit: int):
|
||||
tracks = sorted(TrackStore.tracks, key=lambda t: t.created_date, reverse=True)
|
||||
timestamp = timestamp_from_days_ago(cutoff_days)
|
||||
|
||||
return [t for t in tracks if t.created_date > timestamp]
|
||||
return tracks[:limit]
|
||||
@@ -1,6 +1,7 @@
|
||||
"""
|
||||
This library contains all the functions related to playlists.
|
||||
"""
|
||||
|
||||
import os
|
||||
import random
|
||||
import string
|
||||
@@ -62,7 +63,7 @@ def create_gif_thumbnail(image: Any, img_path: str):
|
||||
|
||||
|
||||
def save_p_image(
|
||||
img: Image, pid: str, content_type: str = None, filename: str = None
|
||||
img: Image, pid: int, content_type: str = None, filename: str = None
|
||||
) -> str:
|
||||
"""
|
||||
Saves a playlist banner image and returns the filepath.
|
||||
@@ -72,7 +73,7 @@ def save_p_image(
|
||||
random_str = "".join(random.choices(string.ascii_letters + string.digits, k=5))
|
||||
|
||||
if not filename:
|
||||
filename = pid + str(random_str) + ".webp"
|
||||
filename = str(pid) + str(random_str) + ".webp"
|
||||
|
||||
full_img_path = os.path.join(settings.Paths.get_playlist_img_path(), filename)
|
||||
|
||||
@@ -134,7 +135,7 @@ def get_first_4_images(
|
||||
return duplicate_images(images)
|
||||
|
||||
|
||||
def get_recently_added_playlist(cutoff: int = 14):
|
||||
def get_recently_added_playlist(limit: int = 100):
|
||||
playlist = Playlist(
|
||||
id="recentlyadded",
|
||||
name="Recently Added",
|
||||
@@ -144,8 +145,10 @@ def get_recently_added_playlist(cutoff: int = 14):
|
||||
trackhashes=[],
|
||||
)
|
||||
|
||||
tracks = get_recent_tracks(cutoff)
|
||||
tracks = get_recent_tracks(limit=limit)
|
||||
|
||||
try:
|
||||
# Create date to show as last updated
|
||||
date = datetime.fromtimestamp(tracks[0].created_date)
|
||||
except IndexError:
|
||||
return playlist, []
|
||||
|
||||
Reference in New Issue
Block a user