add timestamp to favorite entries

+ convert useBisection into a function
This commit is contained in:
mungai-njoroge
2024-03-10 17:21:22 +03:00
committed by Mungai Njoroge
parent fb635ff35f
commit 766eb388b2
9 changed files with 95 additions and 52 deletions
+2 -2
View File
@@ -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
View File
@@ -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})
+4 -2
View File
@@ -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
+2 -1
View File
@@ -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 (
+4 -1
View File
@@ -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
] ]
] ]
+34
View File
@@ -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 -3
View File
@@ -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
View File
@@ -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
+22 -29
View File
@@ -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