add because you listened to artist artists

+ add artists you might like
This commit is contained in:
cwilvx
2024-11-27 10:55:11 +03:00
parent e42ec3afb5
commit 809415ddb4
6 changed files with 138 additions and 9 deletions
+1 -1
View File
@@ -18,9 +18,9 @@ def start_cron_jobs():
RecentlyAdded()
# Initialized CRON jobs
Mixes()
TopArtists()
TopArtists(duration="week")
Mixes()
# Trigger all CRON jobs when the app is started.
schedule.run_all()
+5
View File
@@ -1,5 +1,6 @@
from app.crons.cron import CronJob
from app.lib.recipes.artistmixes import ArtistMixes
from app.lib.recipes.because import BecauseYouListened
class Mixes(CronJob):
@@ -19,3 +20,7 @@ class Mixes(CronJob):
"""
print("⭐⭐⭐⭐ Mixes cron job running")
ArtistMixes()
# INFO: Because you listened to artist items are generated using
# the artist mixes, so run them after the artist mixes are created.
BecauseYouListened()
+2 -2
View File
@@ -184,7 +184,7 @@ def get_recently_played(
return items
def recover_recently_played_items(items: list[dict]):
def recover_items(items: list[dict]):
custom_playlists = [
{"name": "recentlyadded", "handler": get_recently_added_playlist},
{"name": "recentlyplayed", "handler": get_recently_played_playlist},
@@ -235,7 +235,7 @@ def recover_recently_played_items(items: list[dict]):
},
}
elif item["type"] == "playlist":
if item["is_custom"]:
if item.get("is_custom"):
playlist, _ = next(
i["handler"]()
for i in custom_playlists
+37
View File
@@ -0,0 +1,37 @@
from pprint import pprint
from app.db.userdata import UserTable
from app.lib.recipes import HomepageRoutine
from app.lib.recipes.artistmixes import ArtistMixes
from app.models.mix import Mix
from app.plugins.mixes import MixesPlugin
from app.store.homepage import HomepageStore
class BecauseYouListened(HomepageRoutine):
store_keys = ["because_you_listened_to_artist", "artists_you_might_like"]
@property
def is_valid(self):
return MixesPlugin().enabled
def run(self):
users = UserTable.get_all()
for user in users:
entry: dict[str, Mix] = HomepageStore.entries.get(
ArtistMixes.store_key
).items.get(user.id) # type: ignore
if not entry:
continue
because_you_listened_to_artist, artists_you_might_like = (
MixesPlugin().get_because_items(list(entry.values()))
)
HomepageStore.entries[self.store_keys[0]].items[
user.id
] = because_you_listened_to_artist
HomepageStore.entries[self.store_keys[1]].items[
user.id
] = artists_you_might_like
+60 -1
View File
@@ -1,4 +1,5 @@
import datetime
from gettext import ngettext
import json
from pprint import pprint
import random
@@ -136,7 +137,7 @@ class MixesPlugin(Plugin):
# try to balance the mix
trackmatches = balance_mix(trackmatches)
return trackmatches, results["albums"], results["albums"]
return trackmatches, results["albums"], results["artists"]
@plugin_method
def get_artist_mix(self, artisthash: str):
@@ -419,3 +420,61 @@ class MixesPlugin(Plugin):
TODO: Implement this!
"""
pass
def get_because_items(self, mixes: list[Mix]):
"""
Given a list of mixes, returns a list of artists that are similar to the
artists in the mixes.
"""
artists: dict[str, list[dict[str, str | int]]] = {}
for mix in mixes:
mix_artisthash = mix.extra["artisthash"]
artists.setdefault(mix_artisthash, [])
for artisthash in mix.extra["artists"]:
artist = ArtistStore.artistmap.get(artisthash)
if not artist:
continue
artists[mix_artisthash].append(
{
"type": "artist",
"trackcount": artist.artist.trackcount,
"hash": artisthash,
"help_text": str(artist.artist.trackcount)
+ ngettext(" track", " tracks", artist.artist.trackcount),
}
)
# INFO: Sort artists by trackcount
artists[mix_artisthash] = sorted(
artists[mix_artisthash],
key=lambda x: x["trackcount"],
reverse=True,
)
artisthash = mixes[0].extra["artisthash"]
because_you_listened_to_artist = {
"title": "Because you listened to "
+ ArtistStore.artistmap[artisthash].artist.name,
"items": artists[artisthash][:15],
}
# Flatten list of artists and remove duplicates by artisthash
all_artists = []
seen = set()
for artist_list in artists.values():
for artist in artist_list:
if artist["hash"] not in seen:
all_artists.append(artist)
seen.add(artist["hash"])
artists_you_might_like = {
"title": "Artists you might like",
"items": random.sample(all_artists, k=min(15, len(all_artists))),
}
return because_you_listened_to_artist, artists_you_might_like
+33 -5
View File
@@ -1,7 +1,8 @@
from abc import ABC
from dataclasses import dataclass
import pprint
from typing import Any
from app.lib.home.recentlyplayed import recover_recently_played_items
from app.lib.home.recentlyplayed import recover_items
from app.models.mix import Mix
from app.utils.auth import get_current_userid
@@ -78,7 +79,7 @@ class RecentlyPlayedHomepageEntry(HomepageEntry):
return {
"title": self.title,
"description": self.description,
"items": recover_recently_played_items(items),
"items": recover_items(items),
}
@@ -91,7 +92,7 @@ class RecentlyAddedHomepageEntry(RecentlyPlayedHomepageEntry):
return super().get_items(0, limit)
class TopStreamedHomepageEntry(RecentlyPlayedHomepageEntry):
class GenericRecoverableEntry(RecentlyPlayedHomepageEntry):
"""
A homepage entry for top streamed.
"""
@@ -101,6 +102,25 @@ class TopStreamedHomepageEntry(RecentlyPlayedHomepageEntry):
pass
class BecauseYouListenedToArtistHomepageEntry(RecentlyPlayedHomepageEntry):
"""
A homepage entry for because you listened to artist.
"""
# SHAPE: {userid: {title: str, items: list[RecoverableItem]}}
items: dict[int, dict[str, Any]]
def get_items(self, userid: int, limit: int | None = None):
pprint.pprint(self.items)
title = self.items.get(userid, {}).get("title")
items = self.items.get(userid, {}).get("items", [])[:limit]
return {
"title": title,
"items": recover_items(items),
}
class HomepageStore:
"""
Stores the homepage items.
@@ -114,14 +134,22 @@ class HomepageStore:
title="Artist mixes for you",
description="Based on artists you have been listening to",
),
"top_streamed_weekly_artists": TopStreamedHomepageEntry(
"top_streamed_weekly_artists": GenericRecoverableEntry(
title="Top artists this week",
description="Your most played artists since Monday",
),
"top_streamed_monthly_artists": TopStreamedHomepageEntry(
"top_streamed_monthly_artists": GenericRecoverableEntry(
title="Top artists this month",
description="Your most played artists since the start of the month",
),
"because_you_listened_to_artist": BecauseYouListenedToArtistHomepageEntry(
title="",
description="Artists similar to the artist you listened to",
),
"artists_you_might_like": BecauseYouListenedToArtistHomepageEntry(
title="Artists you might like",
description="Artists similar to the artists you have listened to",
),
"recently_added": RecentlyAddedHomepageEntry(
title="Recently added",
description="New music added to your library",