mirror of
https://github.com/tcsenpai/swingmusic.git
synced 2025-06-08 12:15:39 +00:00
add methods to get recently played items
This commit is contained in:
parent
5a420214f2
commit
ddfa7f1b03
@ -1,10 +1,11 @@
|
|||||||
from flask import Blueprint
|
from flask import Blueprint
|
||||||
from flask_restful import Api
|
from flask_restful import Api
|
||||||
|
|
||||||
from .recents import RecentlyAdded
|
from .recents import RecentlyAdded, RecentlyPlayed
|
||||||
|
|
||||||
api_bp = Blueprint("home", __name__, url_prefix="/home")
|
api_bp = Blueprint("home", __name__, url_prefix="/home")
|
||||||
api = Api(api_bp)
|
api = Api(api_bp)
|
||||||
|
|
||||||
|
|
||||||
api.add_resource(RecentlyAdded, "/recents/added")
|
api.add_resource(RecentlyAdded, "/recents/added")
|
||||||
|
api.add_resource(RecentlyPlayed, "/recents/played")
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from flask_restful import Resource, reqparse
|
from flask_restful import Resource, reqparse
|
||||||
|
|
||||||
from app.lib.home.recents import get_recent_items
|
from app.lib.home.recentlyadded import get_recent_items
|
||||||
|
from app.lib.home.recentlyplayed import get_recently_played
|
||||||
|
|
||||||
parser = reqparse.RequestParser()
|
parser = reqparse.RequestParser()
|
||||||
|
|
||||||
@ -15,3 +16,11 @@ class RecentlyAdded(Resource):
|
|||||||
limit = args["limit"]
|
limit = args["limit"]
|
||||||
|
|
||||||
return {"items": get_recent_items(cutoff, limit), "cutoff": cutoff}
|
return {"items": get_recent_items(cutoff, limit), "cutoff": cutoff}
|
||||||
|
|
||||||
|
|
||||||
|
class RecentlyPlayed(Resource):
|
||||||
|
def get(self):
|
||||||
|
args = parser.parse_args()
|
||||||
|
limit = args["limit"]
|
||||||
|
|
||||||
|
return {"items": get_recently_played(limit)}
|
||||||
|
@ -12,9 +12,7 @@ from app import models
|
|||||||
from app.db.sqlite.playlists import SQLitePlaylistMethods
|
from app.db.sqlite.playlists import SQLitePlaylistMethods
|
||||||
from app.lib import playlistlib
|
from app.lib import playlistlib
|
||||||
from app.lib.albumslib import sort_by_track_no
|
from app.lib.albumslib import sort_by_track_no
|
||||||
from app.lib.home.recents import get_recent_tracks
|
from app.serializers.playlist import serialize_for_card
|
||||||
from app.models.track import Track
|
|
||||||
from app.store.albums import AlbumStore
|
|
||||||
from app.store.tracks import TrackStore
|
from app.store.tracks import TrackStore
|
||||||
from app.utils.dates import create_new_date, date_string_to_time_passed
|
from app.utils.dates import create_new_date, date_string_to_time_passed
|
||||||
from app.utils.remove_duplicates import remove_duplicates
|
from app.utils.remove_duplicates import remove_duplicates
|
||||||
@ -25,47 +23,6 @@ api = Blueprint("playlist", __name__, url_prefix="/")
|
|||||||
PL = SQLitePlaylistMethods
|
PL = SQLitePlaylistMethods
|
||||||
|
|
||||||
|
|
||||||
def duplicate_images(images: list):
|
|
||||||
if len(images) == 1:
|
|
||||||
images *= 4
|
|
||||||
elif len(images) == 2:
|
|
||||||
images += list(reversed(images))
|
|
||||||
elif len(images) == 3:
|
|
||||||
images = images + images[:1]
|
|
||||||
|
|
||||||
return images
|
|
||||||
|
|
||||||
|
|
||||||
def get_first_4_images(
|
|
||||||
tracks: list[Track] = [], trackhashes: list[str] = []
|
|
||||||
) -> list[dict["str", str]]:
|
|
||||||
if len(trackhashes) > 0:
|
|
||||||
tracks = TrackStore.get_tracks_by_trackhashes(trackhashes)
|
|
||||||
|
|
||||||
albums = []
|
|
||||||
|
|
||||||
for track in tracks:
|
|
||||||
if track.albumhash not in albums:
|
|
||||||
albums.append(track.albumhash)
|
|
||||||
|
|
||||||
if len(albums) == 4:
|
|
||||||
break
|
|
||||||
|
|
||||||
albums = AlbumStore.get_albums_by_hashes(albums)
|
|
||||||
images = [
|
|
||||||
{
|
|
||||||
"image": album.image,
|
|
||||||
"color": "".join(album.colors),
|
|
||||||
}
|
|
||||||
for album in albums
|
|
||||||
]
|
|
||||||
|
|
||||||
if len(images) == 4:
|
|
||||||
return images
|
|
||||||
|
|
||||||
return duplicate_images(images)
|
|
||||||
|
|
||||||
|
|
||||||
@api.route("/playlists", methods=["GET"])
|
@api.route("/playlists", methods=["GET"])
|
||||||
def send_all_playlists():
|
def send_all_playlists():
|
||||||
"""
|
"""
|
||||||
@ -78,7 +35,9 @@ def send_all_playlists():
|
|||||||
|
|
||||||
for playlist in playlists:
|
for playlist in playlists:
|
||||||
if not no_images:
|
if not no_images:
|
||||||
playlist.images = get_first_4_images(trackhashes=playlist.trackhashes)
|
playlist.images = playlistlib.get_first_4_images(
|
||||||
|
trackhashes=playlist.trackhashes
|
||||||
|
)
|
||||||
playlist.images = [img["image"] for img in playlist.images]
|
playlist.images = [img["image"] for img in playlist.images]
|
||||||
|
|
||||||
playlist.clear_lists()
|
playlist.clear_lists()
|
||||||
@ -204,31 +163,27 @@ def get_playlist(playlistid: str):
|
|||||||
"""
|
"""
|
||||||
Gets a playlist by id, and if it exists, it gets all the tracks in the playlist and returns them.
|
Gets a playlist by id, and if it exists, it gets all the tracks in the playlist and returns them.
|
||||||
"""
|
"""
|
||||||
no_tracks = request.args.get("no_tracks", False)
|
no_tracks = request.args.get("no_tracks", "false")
|
||||||
no_tracks = no_tracks == "true"
|
no_tracks = no_tracks == "true"
|
||||||
|
|
||||||
is_recently_added = playlistid == "recentlyadded"
|
is_recently_added = playlistid == "recentlyadded"
|
||||||
|
|
||||||
if not is_recently_added:
|
if is_recently_added:
|
||||||
|
playlist, tracks = playlistlib.get_recently_added_playlist()
|
||||||
|
|
||||||
|
tracks = remove_duplicates(tracks)
|
||||||
|
duration = sum(t.duration for t in tracks)
|
||||||
|
|
||||||
|
playlist.set_duration(duration)
|
||||||
|
playlist = serialize_for_card(playlist)
|
||||||
|
|
||||||
|
return {"info": playlist, "tracks": tracks}
|
||||||
|
|
||||||
playlist = PL.get_playlist_by_id(int(playlistid))
|
playlist = PL.get_playlist_by_id(int(playlistid))
|
||||||
else:
|
|
||||||
playlist = models.Playlist(
|
|
||||||
id="recentlyadded",
|
|
||||||
name="Recently Added",
|
|
||||||
image=None,
|
|
||||||
last_updated="Now",
|
|
||||||
settings={},
|
|
||||||
trackhashes=[],
|
|
||||||
)
|
|
||||||
|
|
||||||
if playlist is None:
|
if playlist is None:
|
||||||
return {"msg": "Playlist not found"}, 404
|
return {"msg": "Playlist not found"}, 404
|
||||||
|
|
||||||
if is_recently_added:
|
|
||||||
tracks = get_recent_tracks(cutoff_days=14)
|
|
||||||
date = datetime.fromtimestamp(tracks[0].created_date)
|
|
||||||
playlist.last_updated = create_new_date(date)
|
|
||||||
else:
|
|
||||||
tracks = TrackStore.get_tracks_by_trackhashes(list(playlist.trackhashes))
|
tracks = TrackStore.get_tracks_by_trackhashes(list(playlist.trackhashes))
|
||||||
|
|
||||||
tracks = remove_duplicates(tracks)
|
tracks = remove_duplicates(tracks)
|
||||||
@ -239,7 +194,7 @@ def get_playlist(playlistid: str):
|
|||||||
playlist.set_count(len(tracks))
|
playlist.set_count(len(tracks))
|
||||||
|
|
||||||
if not playlist.has_image:
|
if not playlist.has_image:
|
||||||
playlist.images = get_first_4_images(tracks)
|
playlist.images = playlistlib.get_first_4_images(tracks)
|
||||||
|
|
||||||
playlist.clear_lists()
|
playlist.clear_lists()
|
||||||
|
|
||||||
@ -342,7 +297,7 @@ def remove_playlist_image(playlistid: str):
|
|||||||
playlist.settings["has_gif"] = False
|
playlist.settings["has_gif"] = False
|
||||||
playlist.has_image = False
|
playlist.has_image = False
|
||||||
|
|
||||||
playlist.images = get_first_4_images(trackhashes=playlist.trackhashes)
|
playlist.images = playlistlib.get_first_4_images(trackhashes=playlist.trackhashes)
|
||||||
playlist.last_updated = date_string_to_time_passed(playlist.last_updated)
|
playlist.last_updated = date_string_to_time_passed(playlist.last_updated)
|
||||||
|
|
||||||
return {"playlist": playlist}, 200
|
return {"playlist": playlist}, 200
|
||||||
@ -463,7 +418,7 @@ def save_item_as_playlist():
|
|||||||
PL.add_tracks_to_playlist(playlist.id, trackhashes)
|
PL.add_tracks_to_playlist(playlist.id, trackhashes)
|
||||||
playlist.set_count(len(trackhashes))
|
playlist.set_count(len(trackhashes))
|
||||||
|
|
||||||
images = get_first_4_images(trackhashes=trackhashes)
|
images = playlistlib.get_first_4_images(trackhashes=trackhashes)
|
||||||
playlist.images = [img["image"] for img in images]
|
playlist.images = [img["image"] for img in images]
|
||||||
|
|
||||||
return {"playlist": playlist}, 201
|
return {"playlist": playlist}, 201
|
||||||
|
@ -24,3 +24,17 @@ class SQLiteTrackLogger:
|
|||||||
lastrowid = cur.lastrowid
|
lastrowid = cur.lastrowid
|
||||||
|
|
||||||
return lastrowid
|
return lastrowid
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_all(cls):
|
||||||
|
"""
|
||||||
|
Returns all tracks from the database
|
||||||
|
"""
|
||||||
|
|
||||||
|
with SQLiteManager(userdata_db=True) as cur:
|
||||||
|
sql = """SELECT * FROM track_logger ORDER BY timestamp DESC"""
|
||||||
|
|
||||||
|
cur.execute(sql)
|
||||||
|
rows = cur.fetchall()
|
||||||
|
|
||||||
|
return rows
|
||||||
|
122
app/lib/home/recentlyplayed.py
Normal file
122
app/lib/home/recentlyplayed.py
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
from dataclasses import asdict
|
||||||
|
|
||||||
|
from tomlkit import item
|
||||||
|
|
||||||
|
from app.db.sqlite.logger.tracks import SQLiteTrackLogger as db
|
||||||
|
from app.db.sqlite.playlists import SQLitePlaylistMethods as pdb
|
||||||
|
from app.lib.playlistlib import get_first_4_images, get_recently_added_playlist
|
||||||
|
from app.models.logger import Track as TrackLog
|
||||||
|
from app.serializers.album import album_serializer
|
||||||
|
from app.serializers.artist import serialize_for_card
|
||||||
|
from app.serializers.playlist import serialize_for_card as serialize_playlist
|
||||||
|
from app.serializers.track import serialize_track
|
||||||
|
from app.store.albums import AlbumStore
|
||||||
|
from app.store.artists import ArtistStore
|
||||||
|
from app.store.tracks import TrackStore
|
||||||
|
|
||||||
|
|
||||||
|
def get_recently_played(limit=7):
|
||||||
|
entries = db.get_all()
|
||||||
|
items = []
|
||||||
|
added = set()
|
||||||
|
|
||||||
|
for entry in entries:
|
||||||
|
if len(items) >= limit:
|
||||||
|
break
|
||||||
|
|
||||||
|
entry = TrackLog(*entry)
|
||||||
|
|
||||||
|
if entry.source in added:
|
||||||
|
continue
|
||||||
|
|
||||||
|
added.add(entry.source)
|
||||||
|
|
||||||
|
if entry.type == "album":
|
||||||
|
album = AlbumStore.get_album_by_hash(entry.type_src)
|
||||||
|
|
||||||
|
if album is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
album = album_serializer(
|
||||||
|
album,
|
||||||
|
{
|
||||||
|
"genres",
|
||||||
|
"date",
|
||||||
|
"count",
|
||||||
|
"duration",
|
||||||
|
"albumartists_hashes",
|
||||||
|
"og_title",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
items.append({"type": "album", "item": album})
|
||||||
|
continue
|
||||||
|
|
||||||
|
if entry.type == "artist":
|
||||||
|
artist = ArtistStore.get_artist_by_hash(entry.type_src)
|
||||||
|
|
||||||
|
if artist is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
artist = serialize_for_card(artist)
|
||||||
|
|
||||||
|
items.append({"type": "artist", "item": artist})
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
if entry.type == "track":
|
||||||
|
try:
|
||||||
|
track = TrackStore.get_tracks_by_trackhashes([entry.trackhash])[0]
|
||||||
|
except IndexError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
track = serialize_track(track)
|
||||||
|
|
||||||
|
items.append({"type": "track", "item": track})
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
if entry.type == "folder":
|
||||||
|
count = len([t for t in TrackStore.tracks if t.folder == entry.type_src])
|
||||||
|
items.append(
|
||||||
|
{
|
||||||
|
"type": "folder",
|
||||||
|
"item": {
|
||||||
|
"path": entry.type_src,
|
||||||
|
"count": count,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if entry.type == "playlist":
|
||||||
|
is_recently_added = entry.type_src == "recentlyadded"
|
||||||
|
|
||||||
|
if is_recently_added:
|
||||||
|
playlist, _ = get_recently_added_playlist()
|
||||||
|
playlist.images = [i["image"] for i in playlist.images]
|
||||||
|
items.append(
|
||||||
|
{
|
||||||
|
"type": "playlist",
|
||||||
|
"item": serialize_playlist(
|
||||||
|
playlist, to_remove={"settings", "duration"}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
playlist = pdb.get_playlist_by_id(entry.type_src)
|
||||||
|
if playlist is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
tracks = TrackStore.get_tracks_by_trackhashes(playlist.trackhashes)
|
||||||
|
playlist.clear_lists()
|
||||||
|
|
||||||
|
if not playlist.has_image:
|
||||||
|
images = get_first_4_images(tracks)
|
||||||
|
images = [i["image"] for i in images]
|
||||||
|
playlist.images = images
|
||||||
|
|
||||||
|
items.append({"type": "playlist", "item": serialize_playlist(playlist)})
|
||||||
|
|
||||||
|
return items
|
@ -4,11 +4,18 @@ This library contains all the functions related to playlists.
|
|||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
|
from datetime import datetime
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from PIL import Image, ImageSequence
|
from PIL import Image, ImageSequence
|
||||||
|
|
||||||
from app import settings
|
from app import settings
|
||||||
|
from app.lib.home.recentlyadded import get_recent_tracks
|
||||||
|
from app.models.playlist import Playlist
|
||||||
|
from app.models.track import Track
|
||||||
|
from app.store.albums import AlbumStore
|
||||||
|
from app.store.tracks import TrackStore
|
||||||
|
from app.utils.dates import create_new_date
|
||||||
|
|
||||||
|
|
||||||
def create_thumbnail(image: Any, img_path: str) -> str:
|
def create_thumbnail(image: Any, img_path: str) -> str:
|
||||||
@ -86,33 +93,63 @@ def save_p_image(
|
|||||||
return filename
|
return filename
|
||||||
|
|
||||||
|
|
||||||
#
|
def duplicate_images(images: list):
|
||||||
# class ValidatePlaylistThumbs:
|
if len(images) == 1:
|
||||||
# """
|
images *= 4
|
||||||
# Removes all unused images in the images/playlists folder.
|
elif len(images) == 2:
|
||||||
# """
|
images += list(reversed(images))
|
||||||
#
|
elif len(images) == 3:
|
||||||
# def __init__(self) -> None:
|
images = images + images[:1]
|
||||||
# images = []
|
|
||||||
# playlists = Get.get_all_playlists()
|
return images
|
||||||
#
|
|
||||||
# log.info("Validating playlist thumbnails")
|
|
||||||
# for playlist in playlists:
|
|
||||||
# if playlist.image:
|
|
||||||
# img_path = playlist.image.split("/")[-1]
|
|
||||||
# thumb_path = playlist.thumb.split("/")[-1]
|
|
||||||
#
|
|
||||||
# images.append(img_path)
|
|
||||||
# images.append(thumb_path)
|
|
||||||
#
|
|
||||||
# p_path = os.path.join(settings.APP_DIR, "images", "playlists")
|
|
||||||
#
|
|
||||||
# for image in os.listdir(p_path):
|
|
||||||
# if image not in images:
|
|
||||||
# os.remove(os.path.join(p_path, image))
|
|
||||||
#
|
|
||||||
# log.info("Validating playlist thumbnails ... ✅")
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: Fix ValidatePlaylistThumbs
|
def get_first_4_images(
|
||||||
|
tracks: list[Track] = [], trackhashes: list[str] = []
|
||||||
|
) -> list[dict["str", str]]:
|
||||||
|
if len(trackhashes) > 0:
|
||||||
|
tracks = TrackStore.get_tracks_by_trackhashes(trackhashes)
|
||||||
|
|
||||||
|
albums = []
|
||||||
|
|
||||||
|
for track in tracks:
|
||||||
|
if track.albumhash not in albums:
|
||||||
|
albums.append(track.albumhash)
|
||||||
|
|
||||||
|
if len(albums) == 4:
|
||||||
|
break
|
||||||
|
|
||||||
|
albums = AlbumStore.get_albums_by_hashes(albums)
|
||||||
|
images = [
|
||||||
|
{
|
||||||
|
"image": album.image,
|
||||||
|
"color": "".join(album.colors),
|
||||||
|
}
|
||||||
|
for album in albums
|
||||||
|
]
|
||||||
|
|
||||||
|
if len(images) == 4:
|
||||||
|
return images
|
||||||
|
|
||||||
|
return duplicate_images(images)
|
||||||
|
|
||||||
|
|
||||||
|
def get_recently_added_playlist(cutoff: int = 14):
|
||||||
|
playlist = Playlist(
|
||||||
|
id="recentlyplayed",
|
||||||
|
name="Recently Added",
|
||||||
|
image=None,
|
||||||
|
last_updated="Now",
|
||||||
|
settings={},
|
||||||
|
trackhashes=[],
|
||||||
|
)
|
||||||
|
|
||||||
|
tracks = get_recent_tracks(cutoff)
|
||||||
|
date = datetime.fromtimestamp(tracks[0].created_date)
|
||||||
|
playlist.last_updated = create_new_date(date)
|
||||||
|
|
||||||
|
images = get_first_4_images(tracks=tracks)
|
||||||
|
playlist.images = images
|
||||||
|
playlist.set_count(len(tracks))
|
||||||
|
|
||||||
|
return playlist, tracks
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from attr import dataclass
|
from dataclasses import dataclass
|
||||||
|
from typing import Literal
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -6,6 +7,30 @@ class Track:
|
|||||||
"""
|
"""
|
||||||
Track play logger model
|
Track play logger model
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
id: int
|
||||||
trackhash: str
|
trackhash: str
|
||||||
duration: int
|
duration: int
|
||||||
timestamp: int
|
timestamp: int
|
||||||
|
source: str
|
||||||
|
userid: int
|
||||||
|
|
||||||
|
type = "track"
|
||||||
|
type_src = None
|
||||||
|
|
||||||
|
def __post_init__(self):
|
||||||
|
prefix_map = {
|
||||||
|
"al:": "album",
|
||||||
|
"ar:": "artist",
|
||||||
|
"pl:": "playlist",
|
||||||
|
"fo:": "folder",
|
||||||
|
}
|
||||||
|
|
||||||
|
for prefix, srctype in prefix_map.items():
|
||||||
|
if self.source.startswith(prefix):
|
||||||
|
try:
|
||||||
|
self.type_src = self.source.split(":", 1)[1]
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
self.type = srctype
|
||||||
|
break
|
||||||
|
@ -11,13 +11,13 @@ class Playlist:
|
|||||||
"""Creates playlist objects"""
|
"""Creates playlist objects"""
|
||||||
|
|
||||||
id: int
|
id: int
|
||||||
image: str
|
image: str | None
|
||||||
last_updated: str
|
last_updated: str
|
||||||
name: str
|
name: str
|
||||||
settings: str | dict
|
settings: str | dict
|
||||||
trackhashes: str | list[str]
|
trackhashes: str | list[str]
|
||||||
|
|
||||||
thumb: str = ""
|
thumb: str | None = ""
|
||||||
count: int = 0
|
count: int = 0
|
||||||
duration: int = 0
|
duration: int = 0
|
||||||
has_image: bool = False
|
has_image: bool = False
|
||||||
|
13
app/serializers/playlist.py
Normal file
13
app/serializers/playlist.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from dataclasses import asdict
|
||||||
|
from app.models.playlist import Playlist
|
||||||
|
|
||||||
|
|
||||||
|
def serialize_for_card(playlist: Playlist, to_remove=set()):
|
||||||
|
p_dict = asdict(playlist)
|
||||||
|
|
||||||
|
props = {"trackhashes"}.union(to_remove)
|
||||||
|
|
||||||
|
for key in props:
|
||||||
|
p_dict.pop(key, None)
|
||||||
|
|
||||||
|
return p_dict
|
Loading…
x
Reference in New Issue
Block a user