mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-05 13:03:02 +00:00
add timestamp to favorite entries
+ convert useBisection into a function
This commit is contained in:
committed by
Mungai Njoroge
parent
fb635ff35f
commit
766eb388b2
+2
-2
@@ -183,7 +183,7 @@ def get_all_artist_tracks(path: ArtistHashSchema):
|
|||||||
"""
|
"""
|
||||||
tracks = TrackStore.get_tracks_by_artisthash(path.artisthash)
|
tracks = TrackStore.get_tracks_by_artisthash(path.artisthash)
|
||||||
|
|
||||||
return {"tracks": serialize_tracks(tracks)}
|
return serialize_tracks(tracks)
|
||||||
|
|
||||||
|
|
||||||
@api.get("/<artisthash>/similar")
|
@api.get("/<artisthash>/similar")
|
||||||
@@ -208,7 +208,7 @@ def get_similar_artists(path: ArtistHashSchema, query: ArtistLimitSchema):
|
|||||||
if len(similar) > limit:
|
if len(similar) > limit:
|
||||||
similar = random.sample(similar, limit)
|
similar = random.sample(similar, limit)
|
||||||
|
|
||||||
return {"artists": similar[:limit]}
|
return similar[:limit]
|
||||||
|
|
||||||
|
|
||||||
# TODO: Rewrite this file using generators where possible
|
# TODO: Rewrite this file using generators where possible
|
||||||
|
|||||||
+22
-12
@@ -1,21 +1,26 @@
|
|||||||
|
from typing import List, TypeVar
|
||||||
from flask import Blueprint, request
|
from flask import Blueprint, request
|
||||||
|
|
||||||
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
|
|
||||||
from app.models import FavType
|
from app.models import FavType
|
||||||
from app.serializers.album import serialize_for_card, serialize_for_card_many
|
from app.utils.bisection import use_bisection
|
||||||
from app.serializers.artist import serialize_for_card as serialize_artist
|
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
|
||||||
from app.serializers.track import serialize_track, serialize_tracks
|
from app.serializers.track import serialize_track, serialize_tracks
|
||||||
from app.utils.bisection import UseBisection
|
from app.serializers.artist import serialize_for_card as serialize_artist
|
||||||
|
from app.serializers.album import serialize_for_card, serialize_for_card_many
|
||||||
|
|
||||||
from app.store.artists import ArtistStore
|
|
||||||
from app.store.albums import AlbumStore
|
from app.store.albums import AlbumStore
|
||||||
from app.store.tracks import TrackStore
|
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="/")
|
api = Blueprint("favorite", __name__, url_prefix="/")
|
||||||
|
|
||||||
|
|
||||||
def remove_none(items: list):
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
|
def remove_none(items: List[T]) -> List[T]:
|
||||||
return [i for i in items if i is not None]
|
return [i for i in items if i is not None]
|
||||||
|
|
||||||
|
|
||||||
@@ -76,7 +81,7 @@ def get_favorite_albums():
|
|||||||
|
|
||||||
src_albums = sorted(AlbumStore.albums, key=lambda x: x.albumhash)
|
src_albums = sorted(AlbumStore.albums, key=lambda x: x.albumhash)
|
||||||
|
|
||||||
fav_albums = UseBisection(src_albums, "albumhash", albumhashes)()
|
fav_albums = use_bisection(src_albums, "albumhash", albumhashes)
|
||||||
fav_albums = remove_none(fav_albums)
|
fav_albums = remove_none(fav_albums)
|
||||||
|
|
||||||
if limit == 0:
|
if limit == 0:
|
||||||
@@ -99,7 +104,7 @@ def get_favorite_tracks():
|
|||||||
trackhashes.reverse()
|
trackhashes.reverse()
|
||||||
src_tracks = sorted(TrackStore.tracks, key=lambda x: x.trackhash)
|
src_tracks = sorted(TrackStore.tracks, key=lambda x: x.trackhash)
|
||||||
|
|
||||||
tracks = UseBisection(src_tracks, "trackhash", trackhashes)()
|
tracks = use_bisection(src_tracks, "trackhash", trackhashes)
|
||||||
tracks = remove_none(tracks)
|
tracks = remove_none(tracks)
|
||||||
|
|
||||||
if limit == 0:
|
if limit == 0:
|
||||||
@@ -123,7 +128,7 @@ def get_favorite_artists():
|
|||||||
|
|
||||||
src_artists = sorted(ArtistStore.artists, key=lambda x: x.artisthash)
|
src_artists = sorted(ArtistStore.artists, key=lambda x: x.artisthash)
|
||||||
|
|
||||||
artists = UseBisection(src_artists, "artisthash", artisthashes)()
|
artists = use_bisection(src_artists, "artisthash", artisthashes)
|
||||||
artists = remove_none(artists)
|
artists = remove_none(artists)
|
||||||
|
|
||||||
if limit == 0:
|
if limit == 0:
|
||||||
@@ -169,6 +174,7 @@ def get_all_favorites():
|
|||||||
artist_master_hash = set(a.artisthash for a in ArtistStore.artists)
|
artist_master_hash = set(a.artisthash for a in ArtistStore.artists)
|
||||||
|
|
||||||
for fav in favs:
|
for fav in favs:
|
||||||
|
# INFO: hash is [1], type is [2], timestamp is [3]
|
||||||
hash = fav[1]
|
hash = fav[1]
|
||||||
if fav[2] == FavType.track:
|
if fav[2] == FavType.track:
|
||||||
tracks.append(hash) if hash in track_master_hash else None
|
tracks.append(hash) if hash in track_master_hash else None
|
||||||
@@ -189,9 +195,9 @@ def get_all_favorites():
|
|||||||
src_albums = sorted(AlbumStore.albums, key=lambda x: x.albumhash)
|
src_albums = sorted(AlbumStore.albums, key=lambda x: x.albumhash)
|
||||||
src_artists = sorted(ArtistStore.artists, key=lambda x: x.artisthash)
|
src_artists = sorted(ArtistStore.artists, key=lambda x: x.artisthash)
|
||||||
|
|
||||||
tracks = UseBisection(src_tracks, "trackhash", tracks, limit=track_limit)()
|
tracks = use_bisection(src_tracks, "trackhash", tracks, limit=track_limit)
|
||||||
albums = UseBisection(src_albums, "albumhash", albums, limit=album_limit)()
|
albums = use_bisection(src_albums, "albumhash", albums, limit=album_limit)
|
||||||
artists = UseBisection(src_artists, "artisthash", artists, limit=artist_limit)()
|
artists = use_bisection(src_artists, "artisthash", artists, limit=artist_limit)
|
||||||
|
|
||||||
tracks = remove_none(tracks)
|
tracks = remove_none(tracks)
|
||||||
albums = remove_none(albums)
|
albums = remove_none(albums)
|
||||||
@@ -201,6 +207,7 @@ def get_all_favorites():
|
|||||||
# first_n = favs
|
# first_n = favs
|
||||||
|
|
||||||
for fav in favs:
|
for fav in favs:
|
||||||
|
# INFO: hash is [1], type is [2], timestamp is [3]
|
||||||
if len(recents) >= largest:
|
if len(recents) >= largest:
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -212,6 +219,7 @@ def get_all_favorites():
|
|||||||
|
|
||||||
album = serialize_for_card(album)
|
album = serialize_for_card(album)
|
||||||
album["help_text"] = "album"
|
album["help_text"] = "album"
|
||||||
|
album["time"] = timestamp_to_time_passed(fav[3])
|
||||||
|
|
||||||
recents.append(
|
recents.append(
|
||||||
{
|
{
|
||||||
@@ -228,6 +236,7 @@ def get_all_favorites():
|
|||||||
|
|
||||||
artist = serialize_artist(artist)
|
artist = serialize_artist(artist)
|
||||||
artist["help_text"] = "artist"
|
artist["help_text"] = "artist"
|
||||||
|
artist["time"] = timestamp_to_time_passed(fav[3])
|
||||||
|
|
||||||
recents.append(
|
recents.append(
|
||||||
{
|
{
|
||||||
@@ -244,6 +253,7 @@ def get_all_favorites():
|
|||||||
|
|
||||||
track = serialize_track(track)
|
track = serialize_track(track)
|
||||||
track["help_text"] = "track"
|
track["help_text"] = "track"
|
||||||
|
track["time"] = timestamp_to_time_passed(fav[3])
|
||||||
|
|
||||||
recents.append({"type": "track", "item": track})
|
recents.append({"type": "track", "item": track})
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
from datetime import datetime
|
||||||
from app.models import FavType
|
from app.models import FavType
|
||||||
from .utils import SQLiteManager
|
from .utils import SQLiteManager
|
||||||
|
|
||||||
@@ -26,9 +27,10 @@ class SQLiteFavoriteMethods:
|
|||||||
if cls.check_is_favorite(fav_hash, fav_type):
|
if cls.check_is_favorite(fav_hash, fav_type):
|
||||||
return
|
return
|
||||||
|
|
||||||
sql = """INSERT INTO favorites(type, hash) VALUES(?,?)"""
|
sql = """INSERT INTO favorites(type, hash, timestamp) VALUES(?,?,?)"""
|
||||||
|
current_timestamp = datetime.now().timestamp()
|
||||||
with SQLiteManager(userdata_db=True) as cur:
|
with SQLiteManager(userdata_db=True) as cur:
|
||||||
cur.execute(sql, (fav_type, fav_hash))
|
cur.execute(sql, (fav_type, fav_hash, current_timestamp))
|
||||||
cur.close()
|
cur.close()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ CREATE TABLE IF NOT EXISTS playlists (
|
|||||||
CREATE TABLE IF NOT EXISTS favorites (
|
CREATE TABLE IF NOT EXISTS favorites (
|
||||||
id integer PRIMARY KEY,
|
id integer PRIMARY KEY,
|
||||||
hash text not null,
|
hash text not null,
|
||||||
type text not null
|
type text not null,
|
||||||
|
timestamp integer not null default 0
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS settings (
|
CREATE TABLE IF NOT EXISTS settings (
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ PS: Fuck that! Do what you want.
|
|||||||
|
|
||||||
from app.db.sqlite.migrations import MigrationManager
|
from app.db.sqlite.migrations import MigrationManager
|
||||||
from app.logger import log
|
from app.logger import log
|
||||||
from app.migrations import v1_3_0
|
from app.migrations import v1_3_0, v1_4_9
|
||||||
from app.migrations.base import Migration
|
from app.migrations.base import Migration
|
||||||
|
|
||||||
migrations: list[list[Migration]] = [
|
migrations: list[list[Migration]] = [
|
||||||
@@ -26,6 +26,9 @@ migrations: list[list[Migration]] = [
|
|||||||
v1_3_0.MovePlaylistsAndFavoritesTo10BitHashes,
|
v1_3_0.MovePlaylistsAndFavoritesTo10BitHashes,
|
||||||
v1_3_0.RemoveAllTracks,
|
v1_3_0.RemoveAllTracks,
|
||||||
v1_3_0.UpdateAppSettingsTable,
|
v1_3_0.UpdateAppSettingsTable,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
v1_4_9.AddTimestampToFavoritesTable
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
from app.db.sqlite.utils import SQLiteManager
|
||||||
|
from app.migrations.base import Migration
|
||||||
|
|
||||||
|
|
||||||
|
class AddTimestampToFavoritesTable(Migration):
|
||||||
|
"""
|
||||||
|
Adds a timestamp column to the favorites table.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def migrate():
|
||||||
|
# INFO: add timestamp column with automatic current timestamp
|
||||||
|
sql = f"ALTER TABLE favorites ADD COLUMN timestamp INTEGER NOT NULL DEFAULT 0"
|
||||||
|
|
||||||
|
# INFO: execute the sql
|
||||||
|
with SQLiteManager(userdata_db=True) as cur:
|
||||||
|
cur.execute(sql)
|
||||||
|
|
||||||
|
# INFO: Update the timestamp column with the current timestamp
|
||||||
|
cur.execute("UPDATE favorites SET timestamp = strftime('%s', 'now')")
|
||||||
|
cur.close()
|
||||||
|
|
||||||
|
|
||||||
|
class MoveHashesToSha1(Migration):
|
||||||
|
"""
|
||||||
|
Moves the 10 bit item hashes from sha256 to sha1 which is
|
||||||
|
faster and more lenient on less powerful devices.
|
||||||
|
|
||||||
|
Thanks to [@tcsenpai](https:github.com/tcsenpai) for the contribution.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
# INFO: Apparentlly, every single table is affected by this migration.
|
||||||
|
# NOTE: Use generators to avoid memory issues.
|
||||||
@@ -3,7 +3,7 @@ import json
|
|||||||
from app.db.sqlite.artistcolors import SQLiteArtistMethods as ardb
|
from app.db.sqlite.artistcolors import SQLiteArtistMethods as ardb
|
||||||
from app.lib.artistlib import get_all_artists
|
from app.lib.artistlib import get_all_artists
|
||||||
from app.models import Artist
|
from app.models import Artist
|
||||||
from app.utils.bisection import UseBisection
|
from app.utils.bisection import use_bisection
|
||||||
from app.utils.customlist import CustomList
|
from app.utils.customlist import CustomList
|
||||||
from app.utils.progressbar import tqdm
|
from app.utils.progressbar import tqdm
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ class ArtistStore:
|
|||||||
"""
|
"""
|
||||||
artists = sorted(cls.artists, key=lambda x: x.artisthash)
|
artists = sorted(cls.artists, key=lambda x: x.artisthash)
|
||||||
try:
|
try:
|
||||||
artist = UseBisection(artists, "artisthash", [artisthash])()[0]
|
artist = use_bisection(artists, "artisthash", [artisthash])[0]
|
||||||
return artist
|
return artist
|
||||||
except IndexError:
|
except IndexError:
|
||||||
return None
|
return None
|
||||||
@@ -83,7 +83,7 @@ class ArtistStore:
|
|||||||
Returns artists by their hashes.
|
Returns artists by their hashes.
|
||||||
"""
|
"""
|
||||||
artists = sorted(cls.artists, key=lambda x: x.artisthash)
|
artists = sorted(cls.artists, key=lambda x: x.artisthash)
|
||||||
artists = UseBisection(artists, "artisthash", artisthashes)()
|
artists = use_bisection(artists, "artisthash", artisthashes)
|
||||||
return [a for a in artists if a is not None]
|
return [a for a in artists if a is not None]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
+2
-2
@@ -3,7 +3,7 @@
|
|||||||
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
|
from app.db.sqlite.favorite import SQLiteFavoriteMethods as favdb
|
||||||
from app.db.sqlite.tracks import SQLiteTrackMethods as tdb
|
from app.db.sqlite.tracks import SQLiteTrackMethods as tdb
|
||||||
from app.models import Track
|
from app.models import Track
|
||||||
from app.utils.bisection import UseBisection
|
from app.utils.bisection import use_bisection
|
||||||
from app.utils.customlist import CustomList
|
from app.utils.customlist import CustomList
|
||||||
from app.utils.remove_duplicates import remove_duplicates
|
from app.utils.remove_duplicates import remove_duplicates
|
||||||
|
|
||||||
@@ -153,7 +153,7 @@ class TrackStore:
|
|||||||
Returns all tracks matching the given paths.
|
Returns all tracks matching the given paths.
|
||||||
"""
|
"""
|
||||||
tracks = sorted(cls.tracks, key=lambda x: x.filepath)
|
tracks = sorted(cls.tracks, key=lambda x: x.filepath)
|
||||||
tracks = UseBisection(tracks, "filepath", paths)()
|
tracks = use_bisection(tracks, "filepath", paths)
|
||||||
return [track for track in tracks if track is not None]
|
return [track for track in tracks if track is not None]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
+16
-23
@@ -1,52 +1,45 @@
|
|||||||
from app.models.track import Track
|
from typing import List, Optional, TypeVar
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
class UseBisection:
|
def use_bisection(
|
||||||
|
source: List[T], key: str, queries: List[str], limit: int = -1
|
||||||
|
) -> List[Optional[T]]:
|
||||||
"""
|
"""
|
||||||
Uses bisection to find a list of items in another list.
|
Uses bisection to find a list of items in another list.
|
||||||
|
|
||||||
returns a list of found items with `None` items being not found
|
Returns a list of found items with `None` items being not found items.
|
||||||
items.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def find(query: str):
|
||||||
self, source: list, search_from: str, queries: list[str], limit=-1
|
|
||||||
) -> None:
|
|
||||||
self.source_list = source
|
|
||||||
self.queries_list = queries
|
|
||||||
self.attr = search_from
|
|
||||||
self.limit = limit
|
|
||||||
|
|
||||||
def find(self, query: str):
|
|
||||||
left = 0
|
left = 0
|
||||||
right = len(self.source_list) - 1
|
right = len(source) - 1
|
||||||
|
|
||||||
while left <= right:
|
while left <= right:
|
||||||
mid = (left + right) // 2
|
mid = (left + right) // 2
|
||||||
if self.source_list[mid].__getattribute__(self.attr) == query:
|
if source[mid].__getattribute__(key) == query:
|
||||||
return self.source_list[mid]
|
return source[mid]
|
||||||
elif self.source_list[mid].__getattribute__(self.attr) > query:
|
elif source[mid].__getattribute__(key) > query:
|
||||||
right = mid - 1
|
right = mid - 1
|
||||||
else:
|
else:
|
||||||
left = mid + 1
|
left = mid + 1
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def __call__(self):
|
if len(source) == 0:
|
||||||
if len(self.source_list) == 0:
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
results: list[Track] = []
|
results = []
|
||||||
|
|
||||||
for query in self.queries_list:
|
for query in queries:
|
||||||
res = self.find(query)
|
res = find(query)
|
||||||
|
|
||||||
if res is None:
|
if res is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
results.append(res)
|
results.append(res)
|
||||||
|
|
||||||
if self.limit != -1 and len(results) >= self.limit:
|
if limit != -1 and len(results) >= limit:
|
||||||
break
|
break
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|||||||
Reference in New Issue
Block a user