mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-05 04:53:01 +00:00
modularize src
+ merge main.py and manage.py + move start logic to swingmusic/__main__.py + add a run.py on the project root
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
Recipes are a way to create mixes.
|
||||
"""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, List
|
||||
|
||||
class HomepageRoutine(ABC):
|
||||
"""
|
||||
A routine creates a row of homepage items.
|
||||
"""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def is_valid(self) -> bool: ...
|
||||
|
||||
def __init__(self) -> None:
|
||||
if not self.is_valid:
|
||||
return
|
||||
|
||||
self.run()
|
||||
|
||||
@abstractmethod
|
||||
def run(self) -> List[Any]:
|
||||
"""
|
||||
Creates the homepage items and saves them to the
|
||||
homepage store if self.is_valid is true.
|
||||
"""
|
||||
...
|
||||
@@ -0,0 +1,38 @@
|
||||
from swingmusic.db.userdata import UserTable
|
||||
from swingmusic.lib.recipes import HomepageRoutine
|
||||
from swingmusic.plugins.mixes import MixesPlugin
|
||||
from swingmusic.store.homepage import HomepageStore
|
||||
|
||||
|
||||
class ArtistMixes(HomepageRoutine):
|
||||
store_key = "artist_mixes"
|
||||
|
||||
@property
|
||||
def is_valid(self):
|
||||
return MixesPlugin().enabled
|
||||
|
||||
def run(self):
|
||||
users = UserTable.get_all()
|
||||
|
||||
for user in users:
|
||||
mix = MixesPlugin()
|
||||
mixes = mix.create_artist_mixes(user.id)
|
||||
|
||||
if not mixes:
|
||||
continue
|
||||
|
||||
HomepageStore.set_mixes(mixes, entrykey=self.store_key, userid=user.id)
|
||||
|
||||
custom_mixes = []
|
||||
for _mix in mixes:
|
||||
custom_mix = MixesPlugin.get_track_mix(_mix)
|
||||
|
||||
if custom_mix:
|
||||
custom_mixes.append(custom_mix)
|
||||
|
||||
HomepageStore.set_mixes(
|
||||
custom_mixes, entrykey="custom_mixes", userid=user.id
|
||||
)
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
@@ -0,0 +1,37 @@
|
||||
from pprint import pprint
|
||||
from swingmusic.db.userdata import UserTable
|
||||
from swingmusic.lib.recipes import HomepageRoutine
|
||||
from swingmusic.lib.recipes.artistmixes import ArtistMixes
|
||||
from swingmusic.models.mix import Mix
|
||||
from swingmusic.plugins.mixes import MixesPlugin
|
||||
from swingmusic.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
|
||||
@@ -0,0 +1,97 @@
|
||||
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
|
||||
from swingmusic.lib.recipes import HomepageRoutine
|
||||
from swingmusic.store.homepage import HomepageStore
|
||||
|
||||
|
||||
class RecentlyPlayed(HomepageRoutine):
|
||||
ITEM_LIMIT = 15
|
||||
store_key = "recently_played"
|
||||
|
||||
def __init__(self, userid: int | None = None) -> None:
|
||||
"""
|
||||
The userid is provided when we are running this routine
|
||||
outside a cron job. ie. when a user records a new scrobble.
|
||||
"""
|
||||
self.userids = [userid] if userid else [user.id for user in UserTable.get_all()]
|
||||
|
||||
# NOTE: When the userid is provided
|
||||
# we need to update the store for that userid only
|
||||
# using the last scrobble entry.
|
||||
self.update_only = userid is not None
|
||||
super().__init__()
|
||||
|
||||
@property
|
||||
def is_valid(self):
|
||||
return True
|
||||
|
||||
def run(self):
|
||||
if self.update_only:
|
||||
last_entry = ScrobbleTable.get_last_entry(self.userids[0])
|
||||
|
||||
if last_entry:
|
||||
items = get_recently_played(
|
||||
limit=self.ITEM_LIMIT, userid=self.userids[0], _entries=[last_entry]
|
||||
)
|
||||
|
||||
try:
|
||||
item = items[0]
|
||||
store_entry = HomepageStore.entries[self.store_key].items[
|
||||
self.userids[0]
|
||||
][0]
|
||||
except IndexError:
|
||||
store_entry = None
|
||||
item = None
|
||||
|
||||
if (
|
||||
store_entry
|
||||
and item
|
||||
and store_entry.get("type", "") + store_entry.get("hash", "")
|
||||
== item.get("type", "") + item.get("hash", "")
|
||||
):
|
||||
# If the item is the same as the one in the store
|
||||
# only update the timestamp
|
||||
HomepageStore.entries[self.store_key].items[self.userids[0]][0][
|
||||
"timestamp"
|
||||
] = item["timestamp"]
|
||||
else:
|
||||
# Otherwise, insert the new item
|
||||
# and remove the oldest item if there are more than 15 items
|
||||
HomepageStore.entries[self.store_key].items[self.userids[0]].insert(
|
||||
0, item
|
||||
)
|
||||
|
||||
if (
|
||||
len(
|
||||
HomepageStore.entries[self.store_key].items[self.userids[0]]
|
||||
)
|
||||
> self.ITEM_LIMIT
|
||||
):
|
||||
HomepageStore.entries[self.store_key].items[
|
||||
self.userids[0]
|
||||
].pop()
|
||||
|
||||
for userid in self.userids:
|
||||
items = get_recently_played(limit=self.ITEM_LIMIT, userid=userid)
|
||||
HomepageStore.entries[self.store_key].items[userid] = items
|
||||
|
||||
|
||||
class RecentlyAdded(HomepageRoutine):
|
||||
ITEM_LIMIT = 15
|
||||
store_key = "recently_added"
|
||||
|
||||
@property
|
||||
def is_valid(self):
|
||||
return True
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def run(self):
|
||||
items = get_recently_added_items(limit=self.ITEM_LIMIT)
|
||||
|
||||
# NOTE: Recently added is a global entry
|
||||
# So we don't need a userid
|
||||
HomepageStore.entries[self.store_key].items[0] = items
|
||||
@@ -0,0 +1,83 @@
|
||||
from gettext import ngettext
|
||||
import pendulum
|
||||
|
||||
from swingmusic.crons.cron import CronJob
|
||||
from swingmusic.db.userdata import UserTable
|
||||
from swingmusic.lib.recipes import HomepageRoutine
|
||||
from swingmusic.store.homepage import HomepageStore
|
||||
from swingmusic.utils.dates import get_date_range, seconds_to_time_string
|
||||
from swingmusic.utils.stats import get_artists_in_period
|
||||
|
||||
|
||||
class TopArtists(CronJob, HomepageRoutine):
|
||||
"""
|
||||
A routine to populate the top streamed artists/albums in the last week or month
|
||||
"""
|
||||
|
||||
hours = 1
|
||||
ITEM_LIMIT = 15
|
||||
|
||||
@property
|
||||
def is_valid(self):
|
||||
"""
|
||||
Only valid if it's the middle or last 2 days of this month.
|
||||
|
||||
When the duration is "week", it's valid on the weekend.
|
||||
"""
|
||||
if self.duration == "month":
|
||||
now = pendulum.now()
|
||||
middle_day = now.days_in_month // 2
|
||||
|
||||
return (
|
||||
now.day in range(middle_day, middle_day + 2)
|
||||
or now.day > now.days_in_month - 2
|
||||
)
|
||||
if self.duration == "week":
|
||||
return pendulum.now().isoweekday() in (5, 6, 7)
|
||||
|
||||
return False
|
||||
|
||||
def __init__(self, duration: str = "month") -> None:
|
||||
super().__init__()
|
||||
self.duration = duration
|
||||
|
||||
if not self.is_valid:
|
||||
return
|
||||
|
||||
def run(self):
|
||||
if not self.is_valid:
|
||||
self.destroy()
|
||||
return
|
||||
|
||||
self.userids = [user.id for user in UserTable.get_all()]
|
||||
|
||||
for userid in self.userids:
|
||||
date_range = get_date_range(self.duration)
|
||||
artists = get_artists_in_period(date_range[0], date_range[1], userid)[
|
||||
: self.ITEM_LIMIT
|
||||
]
|
||||
|
||||
artists = [
|
||||
{
|
||||
"type": "artist",
|
||||
"hash": artist["artisthash"],
|
||||
"help_text": seconds_to_time_string(artist["playduration"]),
|
||||
"secondary_text": str(artist["playcount"])
|
||||
+ " "
|
||||
+ ngettext("play", "plays", artist["playcount"]),
|
||||
}
|
||||
for artist in artists
|
||||
]
|
||||
|
||||
HomepageStore.entries[f"top_streamed_{self.duration}ly_artists"].items[
|
||||
userid
|
||||
] = artists
|
||||
|
||||
def destroy(self):
|
||||
"""
|
||||
Clear the top streamed entry from the homepage store.
|
||||
"""
|
||||
keys = [f"top_streamed_{self.duration}ly_artists"]
|
||||
|
||||
for key in keys:
|
||||
HomepageStore.entries[key].items = {}
|
||||
Reference in New Issue
Block a user