fix: new user recently played data not being shown in homepage

+ fix: weird role labels on new users
+ remove system status tray
+ fix: tinytag removing b prefix on tags
This commit is contained in:
cwilvx
2025-05-31 16:55:42 +03:00
parent 20ebddfcff
commit 18fcd22827
12 changed files with 41 additions and 62 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ dependencies = [
"requests>=2.27.1", "requests>=2.27.1",
"colorgram.py>=1.2.0", "colorgram.py>=1.2.0",
"tqdm>=4.65.0", "tqdm>=4.65.0",
"tinytag>=2.0.0", "tinytag>=2.1.1",
"Unidecode>=1.3.6", "Unidecode>=1.3.6",
"psutil>=5.9.4", "psutil>=5.9.4",
"show-in-file-manager>=1.1.4", "show-in-file-manager>=1.1.4",
+1 -37
View File
@@ -2,11 +2,8 @@ import os
import sys import sys
import click import click
import pathlib import pathlib
import pystray
import multiprocessing import multiprocessing
from PIL import Image from PIL import Image
from typing import Callable
from pystray._base import Icon as PystrayIcon
from swingmusic.start_swingmusic import start_swingmusic from swingmusic.start_swingmusic import start_swingmusic
from swingmusic.utils.xdg_utils import get_xdg_config_dir from swingmusic.utils.xdg_utils import get_xdg_config_dir
@@ -14,30 +11,6 @@ from swingmusic.utils.filesystem import get_home_res_path
from swingmusic.arg_handler import handle_build, handle_password_reset from swingmusic.arg_handler import handle_build, handle_password_reset
class App:
def __init__(self, host: str, port: int, setup: Callable[[], None]):
self.host: str = host
self.port: int = port
self.icon: PystrayIcon = None
self.setup = setup
self.process = multiprocessing.Process(
target=self.setup, args=(self.host, self.port)
)
def start(self, icon: PystrayIcon):
self.icon = icon
self.icon.visible = True
self.process.start()
self.icon.run()
def stop(self):
print("\nShutting down ...", end=" ")
self.process.terminate()
self.process.join(timeout=1)
self.icon.stop()
print("bye! 👋")
def create_image(width, height, color1, color2): def create_image(width, height, color1, color2):
# Generate an image and draw a pattern # Generate an image and draw a pattern
padding = 7 padding = 7
@@ -132,16 +105,7 @@ def run(*args, **kwargs):
os.environ["SWINGMUSIC_XDG_CONFIG_DIR"] = str( os.environ["SWINGMUSIC_XDG_CONFIG_DIR"] = str(
pathlib.Path(kwargs["config"]).resolve() pathlib.Path(kwargs["config"]).resolve()
) )
start_swingmusic(kwargs["host"], kwargs["port"])
app = App(kwargs["host"], kwargs["port"], start_swingmusic)
icon = pystray.Icon(
"Swing Music",
icon=create_image(64, 64, "black", "white"),
menu=pystray.Menu(
pystray.MenuItem("Quit Swing Music", app.stop),
),
)
app.start(icon)
if __name__ == "__main__": if __name__ == "__main__":
+9 -5
View File
@@ -15,6 +15,7 @@ from flask_openapi3 import Tag
from flask_openapi3 import APIBlueprint from flask_openapi3 import APIBlueprint
from swingmusic.db.userdata import UserTable from swingmusic.db.userdata import UserTable
from swingmusic.store.homepage import HomepageStore
from swingmusic.utils.auth import check_password, hash_password from swingmusic.utils.auth import check_password, hash_password
from swingmusic.config import UserConfig from swingmusic.config import UserConfig
@@ -172,7 +173,6 @@ def update_profile(body: UpdateProfileBody):
if "admin" not in current_user["roles"]: if "admin" not in current_user["roles"]:
return {"msg": "Only admins can update roles"}, 403 return {"msg": "Only admins can update roles"}, 403
# all_users = authdb.get_all_users()
all_users = UserTable.get_all() all_users = UserTable.get_all()
if "admin" not in body.roles: if "admin" not in body.roles:
# check if we're removing the last admin # check if we're removing the last admin
@@ -187,7 +187,7 @@ def update_profile(body: UpdateProfileBody):
return {"msg": "Cannot update guest user"}, 400 return {"msg": "Cannot update guest user"}, 400
# finally, convert roles to json string # finally, convert roles to json string
user["roles"] = json.dumps(body.roles) user["roles"] = body.roles
if user["password"]: if user["password"]:
user["password"] = hash_password(user["password"]) user["password"] = hash_password(user["password"])
@@ -215,7 +215,7 @@ def create_user(body: UpdateProfileBody):
user = { user = {
"username": body.username, "username": body.username,
"password": hash_password(body.password), "password": hash_password(body.password),
"roles": json.dumps([]), "roles": [],
} }
# check if user already exists # check if user already exists
@@ -226,6 +226,7 @@ def create_user(body: UpdateProfileBody):
user = UserTable.get_by_username(user["username"]) user = UserTable.get_by_username(user["username"])
if user: if user:
HomepageStore.entries["recently_played"].add_new_user(user.id)
return user.todict() return user.todict()
return { return {
@@ -247,9 +248,12 @@ def create_guest_user():
"msg": "Guest user already exists", "msg": "Guest user already exists",
}, 400 }, 400
userid = UserTable.insert_guest_user() UserTable.insert_guest_user()
user = UserTable.get_by_username("guest")
if user:
HomepageStore.entries["recently_played"].add_new_user(user.id)
if userid:
return { return {
"msg": "Guest user created", "msg": "Guest user created",
} }
+3 -9
View File
@@ -1,5 +1,6 @@
from dataclasses import asdict from dataclasses import asdict
import datetime import datetime
import json
from typing import Any, Iterable, Literal from typing import Any, Iterable, Literal
from sqlalchemy import ( from sqlalchemy import (
JSON, JSON,
@@ -15,7 +16,7 @@ from sqlalchemy import (
update, update,
) )
from sqlalchemy.orm import Mapped, mapped_column, sessionmaker from sqlalchemy.orm import Mapped, mapped_column
from swingmusic.db.engine import DbEngine from swingmusic.db.engine import DbEngine
from swingmusic.db.utils import ( from swingmusic.db.utils import (
@@ -40,7 +41,7 @@ class UserTable(Base):
image: Mapped[str] = mapped_column(String(), nullable=True) image: Mapped[str] = mapped_column(String(), nullable=True)
password: Mapped[str] = mapped_column(String()) password: Mapped[str] = mapped_column(String())
username: Mapped[str] = mapped_column(String(), index=True) username: Mapped[str] = mapped_column(String(), index=True)
roles: Mapped[list[str]] = mapped_column(JSON(), default_factory=lambda: ["user"]) roles: Mapped[list[str]] = mapped_column(JSON(), default_factory=lambda: [])
extra: Mapped[dict[str, Any]] = mapped_column( extra: Mapped[dict[str, Any]] = mapped_column(
JSON(), nullable=True, default_factory=dict JSON(), nullable=True, default_factory=dict
) )
@@ -82,13 +83,6 @@ class UserTable(Base):
@classmethod @classmethod
def get_by_username(cls, username: str): def get_by_username(cls, username: str):
# with DbEngine.manager() as conn:
# result = conn.execute(select(cls).where(cls.username == username))
# res = result.fetchone()
# if res:
# return user_to_dataclass(res)
res = cls.execute(select(cls).where(cls.username == username)) res = cls.execute(select(cls).where(cls.username == username))
res = next(res).scalar() res = next(res).scalar()
+6 -1
View File
@@ -1,5 +1,6 @@
from datetime import datetime from datetime import datetime
from swingmusic.db.userdata import ScrobbleTable
from swingmusic.models.playlist import Playlist from swingmusic.models.playlist import Playlist
from swingmusic.lib.playlistlib import get_first_4_images from swingmusic.lib.playlistlib import get_first_4_images
from swingmusic.utils.dates import ( from swingmusic.utils.dates import (
@@ -20,7 +21,11 @@ def get_recently_played_playlist(limit: int = 100):
trackhashes=[], trackhashes=[],
) )
tracks = TrackStore.get_recently_played(limit) scrobbles = ScrobbleTable.get_all(None, 100)
tracks = TrackStore.get_tracks_by_trackhashes(
[scrobble.trackhash for scrobble in scrobbles]
)
date = datetime.fromtimestamp(tracks[0].lastplayed) date = datetime.fromtimestamp(tracks[0].lastplayed)
playlist._last_updated = date_string_to_time_passed(create_new_date(date)) playlist._last_updated = date_string_to_time_passed(create_new_date(date))
+1 -1
View File
@@ -1,4 +1,4 @@
from swingmusic.db.userdata import FavoritesTable, MixTable, PlaylistTable from swingmusic.db.userdata import FavoritesTable, PlaylistTable
from swingmusic.lib.home import find_mix from swingmusic.lib.home import find_mix
from swingmusic.lib.home.recentlyadded import get_recently_added_playlist from swingmusic.lib.home.recentlyadded import get_recently_added_playlist
from swingmusic.lib.home.recentlyplayed import get_recently_played_playlist from swingmusic.lib.home.recentlyplayed import get_recently_played_playlist
-1
View File
@@ -1,4 +1,3 @@
import pprint
from swingmusic.db.userdata import ScrobbleTable, UserTable from swingmusic.db.userdata import ScrobbleTable, UserTable
from swingmusic.lib.home.recentlyadded import get_recently_added_items from swingmusic.lib.home.recentlyadded import get_recently_added_items
from swingmusic.lib.home.get_recently_played import get_recently_played from swingmusic.lib.home.get_recently_played import get_recently_played
+7
View File
@@ -18,6 +18,13 @@ class User:
this_dict = asdict(self) this_dict = asdict(self)
del this_dict["password"] del this_dict["password"]
if type(this_dict["roles"]) is str:
# INFO: this is an attempt to fix string roles!
try:
this_dict["roles"] = json.loads(this_dict["roles"])
except json.JSONDecodeError:
this_dict["roles"] = []
return this_dict return this_dict
def todict_simplified(self): def todict_simplified(self):
+1
View File
@@ -18,6 +18,7 @@ class HomepageStore:
Stores the homepage items. Stores the homepage items.
""" """
# INFO: map of entry names to entry objects
entries: dict[str, HomepageEntry] = { entries: dict[str, HomepageEntry] = {
"recently_played": RecentlyPlayedHomepageEntry( "recently_played": RecentlyPlayedHomepageEntry(
title="Recently played", title="Recently played",
+7 -1
View File
@@ -4,6 +4,7 @@ from typing import Any
from swingmusic.lib.home.recover_items import recover_items from swingmusic.lib.home.recover_items import recover_items
from swingmusic.models.mix import Mix from swingmusic.models.mix import Mix
class HomepageEntry(ABC): class HomepageEntry(ABC):
""" """
Base class for all homepage entries. Base class for all homepage entries.
@@ -70,6 +71,12 @@ class RecentlyPlayedHomepageEntry(HomepageEntry):
super().__init__(title, description) super().__init__(title, description)
self.items = {} self.items = {}
def add_new_user(self, userid: int):
"""
Add a new user to the homepage entry.
"""
self.items[userid] = []
def get_items(self, userid: int, limit: int | None = None): def get_items(self, userid: int, limit: int | None = None):
items = self.items.get(userid, [])[:limit] items = self.items.get(userid, [])[:limit]
@@ -115,4 +122,3 @@ class BecauseYouListenedToArtistHomepageEntry(RecentlyPlayedHomepageEntry):
"title": title, "title": title,
"items": recover_items(items), "items": recover_items(items),
} }
+1 -2
View File
@@ -198,7 +198,6 @@ class TrackStore:
Returns a list of tracks by their hashes. Returns a list of tracks by their hashes.
""" """
hash_set = set(trackhashes) hash_set = set(trackhashes)
tracks: list[Track] = [] tracks: list[Track] = []
for trackhash in hash_set: for trackhash in hash_set:
@@ -209,7 +208,7 @@ class TrackStore:
tracks.append(track) tracks.append(track)
# sort the tracks in the order of the given trackhashes # sort the tracks in the order of the given trackhashes
if type(trackhashes) == list: if type(trackhashes) is list:
tracks.sort(key=lambda t: trackhashes.index(t.trackhash)) tracks.sort(key=lambda t: trackhashes.index(t.trackhash))
return tracks return tracks
Generated
+4 -4
View File
@@ -1047,7 +1047,7 @@ requires-dist = [
{ name = "sortedcontainers", specifier = ">=2.4.0" }, { name = "sortedcontainers", specifier = ">=2.4.0" },
{ name = "sqlalchemy", specifier = ">=2.0.31" }, { name = "sqlalchemy", specifier = ">=2.0.31" },
{ name = "tabulate", specifier = ">=0.9.0" }, { name = "tabulate", specifier = ">=0.9.0" },
{ name = "tinytag", specifier = ">=2.0.0" }, { name = "tinytag", specifier = ">=2.1.1" },
{ name = "tqdm", specifier = ">=4.65.0" }, { name = "tqdm", specifier = ">=4.65.0" },
{ name = "unidecode", specifier = ">=1.3.6" }, { name = "unidecode", specifier = ">=1.3.6" },
{ name = "watchdog", specifier = ">=4.0.0" }, { name = "watchdog", specifier = ">=4.0.0" },
@@ -1090,11 +1090,11 @@ wheels = [
[[package]] [[package]]
name = "tinytag" name = "tinytag"
version = "2.1.0" version = "2.1.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c1/44/9818c9272fffb67fa2bf4804ce7cda538726238d9424e006772518ebacd0/tinytag-2.1.0.tar.gz", hash = "sha256:9a1b2e37aa45723541621133004ae86416086a0b1922e600cff5bfca5ef93e55", size = 35803 } sdist = { url = "https://files.pythonhosted.org/packages/18/1e/90893a772800ef04ab337f5debf6adf976544d8e683bba9a26a376079814/tinytag-2.1.1.tar.gz", hash = "sha256:b417d480cf3b0c2d60a3afef705b29ac0080fc72d35b0b579b64184c54ee394c", size = 35860 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/00/6e/6d258b355f78047b3aa1abd4c5a12fd3cd4b3d45774651fa90d3aea2e5a4/tinytag-2.1.0-py3-none-any.whl", hash = "sha256:1261c25f5dc0a192eb182c4d9ac5facb1e86ba03a005a1322ec545d06a78d719", size = 31057 }, { url = "https://files.pythonhosted.org/packages/43/db/272d1b127d30107c1bb2724ba1d2aa7b75364824c7d203a84d8a8237fd6c/tinytag-2.1.1-py3-none-any.whl", hash = "sha256:b49da8f41dc0c457ecce32e6c5b2595aed8ac9314402192cd6cdb0fef67747eb", size = 31115 },
] ]
[[package]] [[package]]