From 18fcd2282717fb33c93ba6468ad7e3777bed2fec Mon Sep 17 00:00:00 2001 From: cwilvx Date: Sat, 31 May 2025 16:55:42 +0300 Subject: [PATCH] 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 --- pyproject.toml | 2 +- swingmusic/__main__.py | 38 +-------------------------- swingmusic/api/auth.py | 14 ++++++---- swingmusic/db/userdata.py | 12 +++------ swingmusic/lib/home/recentlyplayed.py | 7 ++++- swingmusic/lib/home/recover_items.py | 2 +- swingmusic/lib/recipes/recents.py | 1 - swingmusic/models/user.py | 7 +++++ swingmusic/store/homepage.py | 1 + swingmusic/store/homepageentries.py | 8 +++++- swingmusic/store/tracks.py | 3 +-- uv.lock | 8 +++--- 12 files changed, 41 insertions(+), 62 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 470957ff..e49da204 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ dependencies = [ "requests>=2.27.1", "colorgram.py>=1.2.0", "tqdm>=4.65.0", - "tinytag>=2.0.0", + "tinytag>=2.1.1", "Unidecode>=1.3.6", "psutil>=5.9.4", "show-in-file-manager>=1.1.4", diff --git a/swingmusic/__main__.py b/swingmusic/__main__.py index fd0d45ef..234170af 100644 --- a/swingmusic/__main__.py +++ b/swingmusic/__main__.py @@ -2,11 +2,8 @@ import os import sys import click import pathlib -import pystray import multiprocessing from PIL import Image -from typing import Callable -from pystray._base import Icon as PystrayIcon from swingmusic.start_swingmusic import start_swingmusic 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 -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): # Generate an image and draw a pattern padding = 7 @@ -132,16 +105,7 @@ def run(*args, **kwargs): os.environ["SWINGMUSIC_XDG_CONFIG_DIR"] = str( pathlib.Path(kwargs["config"]).resolve() ) - - 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) + start_swingmusic(kwargs["host"], kwargs["port"]) if __name__ == "__main__": diff --git a/swingmusic/api/auth.py b/swingmusic/api/auth.py index 0c32500a..5a4c148f 100644 --- a/swingmusic/api/auth.py +++ b/swingmusic/api/auth.py @@ -15,6 +15,7 @@ from flask_openapi3 import Tag from flask_openapi3 import APIBlueprint from swingmusic.db.userdata import UserTable +from swingmusic.store.homepage import HomepageStore from swingmusic.utils.auth import check_password, hash_password from swingmusic.config import UserConfig @@ -172,7 +173,6 @@ def update_profile(body: UpdateProfileBody): if "admin" not in current_user["roles"]: return {"msg": "Only admins can update roles"}, 403 - # all_users = authdb.get_all_users() all_users = UserTable.get_all() if "admin" not in body.roles: # check if we're removing the last admin @@ -187,7 +187,7 @@ def update_profile(body: UpdateProfileBody): return {"msg": "Cannot update guest user"}, 400 # finally, convert roles to json string - user["roles"] = json.dumps(body.roles) + user["roles"] = body.roles if user["password"]: user["password"] = hash_password(user["password"]) @@ -215,7 +215,7 @@ def create_user(body: UpdateProfileBody): user = { "username": body.username, "password": hash_password(body.password), - "roles": json.dumps([]), + "roles": [], } # check if user already exists @@ -226,6 +226,7 @@ def create_user(body: UpdateProfileBody): user = UserTable.get_by_username(user["username"]) if user: + HomepageStore.entries["recently_played"].add_new_user(user.id) return user.todict() return { @@ -247,9 +248,12 @@ def create_guest_user(): "msg": "Guest user already exists", }, 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 { "msg": "Guest user created", } diff --git a/swingmusic/db/userdata.py b/swingmusic/db/userdata.py index 8d854647..525d7a33 100644 --- a/swingmusic/db/userdata.py +++ b/swingmusic/db/userdata.py @@ -1,5 +1,6 @@ from dataclasses import asdict import datetime +import json from typing import Any, Iterable, Literal from sqlalchemy import ( JSON, @@ -15,7 +16,7 @@ from sqlalchemy import ( 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.utils import ( @@ -40,7 +41,7 @@ class UserTable(Base): image: Mapped[str] = mapped_column(String(), nullable=True) password: Mapped[str] = mapped_column(String()) 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( JSON(), nullable=True, default_factory=dict ) @@ -82,13 +83,6 @@ class UserTable(Base): @classmethod 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 = next(res).scalar() diff --git a/swingmusic/lib/home/recentlyplayed.py b/swingmusic/lib/home/recentlyplayed.py index da4ea438..837f4c42 100644 --- a/swingmusic/lib/home/recentlyplayed.py +++ b/swingmusic/lib/home/recentlyplayed.py @@ -1,5 +1,6 @@ from datetime import datetime +from swingmusic.db.userdata import ScrobbleTable from swingmusic.models.playlist import Playlist from swingmusic.lib.playlistlib import get_first_4_images from swingmusic.utils.dates import ( @@ -20,7 +21,11 @@ def get_recently_played_playlist(limit: int = 100): 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) playlist._last_updated = date_string_to_time_passed(create_new_date(date)) diff --git a/swingmusic/lib/home/recover_items.py b/swingmusic/lib/home/recover_items.py index 202733e6..8f91113b 100644 --- a/swingmusic/lib/home/recover_items.py +++ b/swingmusic/lib/home/recover_items.py @@ -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.recentlyadded import get_recently_added_playlist from swingmusic.lib.home.recentlyplayed import get_recently_played_playlist diff --git a/swingmusic/lib/recipes/recents.py b/swingmusic/lib/recipes/recents.py index d49553a1..e99b4f11 100644 --- a/swingmusic/lib/recipes/recents.py +++ b/swingmusic/lib/recipes/recents.py @@ -1,4 +1,3 @@ -import pprint from swingmusic.db.userdata import ScrobbleTable, UserTable from swingmusic.lib.home.recentlyadded import get_recently_added_items from swingmusic.lib.home.get_recently_played import get_recently_played diff --git a/swingmusic/models/user.py b/swingmusic/models/user.py index 3aca9b95..ffdb6708 100644 --- a/swingmusic/models/user.py +++ b/swingmusic/models/user.py @@ -18,6 +18,13 @@ class User: this_dict = asdict(self) 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 def todict_simplified(self): diff --git a/swingmusic/store/homepage.py b/swingmusic/store/homepage.py index 7037a2cc..28c42e6c 100644 --- a/swingmusic/store/homepage.py +++ b/swingmusic/store/homepage.py @@ -18,6 +18,7 @@ class HomepageStore: Stores the homepage items. """ + # INFO: map of entry names to entry objects entries: dict[str, HomepageEntry] = { "recently_played": RecentlyPlayedHomepageEntry( title="Recently played", diff --git a/swingmusic/store/homepageentries.py b/swingmusic/store/homepageentries.py index df8d82f2..841e7ab4 100644 --- a/swingmusic/store/homepageentries.py +++ b/swingmusic/store/homepageentries.py @@ -4,6 +4,7 @@ from typing import Any from swingmusic.lib.home.recover_items import recover_items from swingmusic.models.mix import Mix + class HomepageEntry(ABC): """ Base class for all homepage entries. @@ -70,6 +71,12 @@ class RecentlyPlayedHomepageEntry(HomepageEntry): super().__init__(title, description) 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): items = self.items.get(userid, [])[:limit] @@ -115,4 +122,3 @@ class BecauseYouListenedToArtistHomepageEntry(RecentlyPlayedHomepageEntry): "title": title, "items": recover_items(items), } - diff --git a/swingmusic/store/tracks.py b/swingmusic/store/tracks.py index 2a5cf99b..322ae40f 100644 --- a/swingmusic/store/tracks.py +++ b/swingmusic/store/tracks.py @@ -198,7 +198,6 @@ class TrackStore: Returns a list of tracks by their hashes. """ hash_set = set(trackhashes) - tracks: list[Track] = [] for trackhash in hash_set: @@ -209,7 +208,7 @@ class TrackStore: tracks.append(track) # 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)) return tracks diff --git a/uv.lock b/uv.lock index 6374419b..20f910f9 100644 --- a/uv.lock +++ b/uv.lock @@ -1047,7 +1047,7 @@ requires-dist = [ { name = "sortedcontainers", specifier = ">=2.4.0" }, { name = "sqlalchemy", specifier = ">=2.0.31" }, { 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 = "unidecode", specifier = ">=1.3.6" }, { name = "watchdog", specifier = ">=4.0.0" }, @@ -1090,11 +1090,11 @@ wheels = [ [[package]] name = "tinytag" -version = "2.1.0" +version = "2.1.1" 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 = [ - { 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]]