[generators] Default per artist up to 4
Some checks failed
Some checks failed
This commit is contained in:
@ -25,6 +25,7 @@ class Extension(ext.Extension):
|
||||
schema["playlist_dir"] = config.String(optional=True)
|
||||
schema["max_tracks"] = config.Integer(optional=True, minimum=0)
|
||||
schema["max_per_album"] = config.Integer(optional=True, minimum=0)
|
||||
schema["max_per_artist"] = config.Integer(optional=True, minimum=0)
|
||||
return schema
|
||||
|
||||
def validate_environment(self) -> None:
|
||||
|
||||
@ -26,3 +26,5 @@ playlist_dir =
|
||||
max_tracks = 75
|
||||
# Maximum tracks per album in a mix (0 = no limit)
|
||||
max_per_album = 2
|
||||
# Maximum tracks per artist in a mix (0 = no limit)
|
||||
max_per_artist = 4
|
||||
|
||||
@ -34,22 +34,38 @@ def _search(
|
||||
|
||||
|
||||
def _mix_tracks(
|
||||
tracks: list[Track], max_tracks: int = 0, max_per_album: int = 0,
|
||||
tracks: list[Track], max_tracks: int = 0,
|
||||
max_per_album: int = 0, max_per_artist: int = 0,
|
||||
) -> list[Track]:
|
||||
if not tracks:
|
||||
return []
|
||||
if max_per_album > 0:
|
||||
random.shuffle(tracks)
|
||||
seen: dict[str, int] = {}
|
||||
result: list[Track] = []
|
||||
for t in tracks:
|
||||
key = t.album.uri if t.album and t.album.uri else str(id(t))
|
||||
count = seen.get(key, 0)
|
||||
if count >= max_per_album:
|
||||
random.shuffle(tracks)
|
||||
seen_album: dict[str, int] = {}
|
||||
seen_artist: dict[str, int] = {}
|
||||
result: list[Track] = []
|
||||
for t in tracks:
|
||||
if max_per_album > 0:
|
||||
album_key = t.album.uri if t.album and t.album.uri else str(id(t))
|
||||
if seen_album.get(album_key, 0) >= max_per_album:
|
||||
continue
|
||||
seen[key] = count + 1
|
||||
result.append(t)
|
||||
tracks = result
|
||||
if max_per_artist > 0:
|
||||
artists = t.artists or []
|
||||
artist_key = None
|
||||
for a in artists:
|
||||
if a.uri:
|
||||
artist_key = a.uri
|
||||
break
|
||||
if not artist_key:
|
||||
artist_name = (artists[0].name or "") if artists else ""
|
||||
artist_key = artist_name or str(id(t))
|
||||
if seen_artist.get(artist_key, 0) >= max_per_artist:
|
||||
continue
|
||||
if max_per_album > 0:
|
||||
seen_album[album_key] = seen_album.get(album_key, 0) + 1
|
||||
if max_per_artist > 0:
|
||||
seen_artist[artist_key] = seen_artist.get(artist_key, 0) + 1
|
||||
result.append(t)
|
||||
tracks = result
|
||||
if max_tracks > 0:
|
||||
tracks = tracks[:max_tracks]
|
||||
random.shuffle(tracks)
|
||||
@ -58,7 +74,7 @@ def _mix_tracks(
|
||||
|
||||
def build_decade_mix(
|
||||
core: CoreProxy, decade: str, uris: list[Uri] | None = None,
|
||||
max_tracks: int = 0, max_per_album: int = 0,
|
||||
max_tracks: int = 0, max_per_album: int = 0, max_per_artist: int = 0,
|
||||
) -> list[Track]:
|
||||
query = cast("Query[SearchField]", {"date": [parse_decade(decade)]})
|
||||
try:
|
||||
@ -66,23 +82,23 @@ def build_decade_mix(
|
||||
except Exception:
|
||||
logger.exception("Decade search failed for %s", decade)
|
||||
return []
|
||||
tracks = _mix_tracks(_extract_tracks(result), max_tracks, max_per_album)
|
||||
tracks = _mix_tracks(_extract_tracks(result), max_tracks, max_per_album, max_per_artist)
|
||||
logger.info("Found %d tracks for decade %s", len(tracks), decade)
|
||||
return tracks
|
||||
|
||||
|
||||
def build_genre_mix(
|
||||
core: CoreProxy, genre: str, uris: list[Uri] | None = None,
|
||||
max_tracks: int = 0, max_per_album: int = 0,
|
||||
max_tracks: int = 0, max_per_album: int = 0, max_per_artist: int = 0,
|
||||
) -> list[Track]:
|
||||
return _mix_tracks(_search(core, "genre", genre, uris=uris), max_tracks, max_per_album)
|
||||
return _mix_tracks(_search(core, "genre", genre, uris=uris), max_tracks, max_per_album, max_per_artist)
|
||||
|
||||
|
||||
def build_artist_mix(
|
||||
core: CoreProxy, artist: str, uris: list[Uri] | None = None,
|
||||
max_tracks: int = 0, max_per_album: int = 0,
|
||||
max_tracks: int = 0, max_per_album: int = 0, max_per_artist: int = 0,
|
||||
) -> list[Track]:
|
||||
return _mix_tracks(_search(core, "artist", artist, uris=uris), max_tracks, max_per_album)
|
||||
return _mix_tracks(_search(core, "artist", artist, uris=uris), max_tracks, max_per_album, max_per_artist)
|
||||
|
||||
|
||||
def build_album_mix(core: CoreProxy, album_uri: str) -> list[Track]:
|
||||
|
||||
@ -29,7 +29,7 @@ class DecadeMixHandler(tornado.web.RequestHandler):
|
||||
def initialize(
|
||||
self, core: CoreProxy, prefix: str, uris: list[Uri] | None = None,
|
||||
playlist_dir: str | None = None,
|
||||
max_tracks: int = 0, max_per_album: int = 0,
|
||||
max_tracks: int = 0, max_per_album: int = 0, max_per_artist: int = 0,
|
||||
) -> None:
|
||||
self.core = core
|
||||
self.prefix = prefix
|
||||
@ -37,6 +37,7 @@ class DecadeMixHandler(tornado.web.RequestHandler):
|
||||
self.playlist_dir = playlist_dir
|
||||
self.max_tracks = max_tracks
|
||||
self.max_per_album = max_per_album
|
||||
self.max_per_artist = max_per_artist
|
||||
|
||||
def post(self) -> None:
|
||||
data = json.loads(self.request.body)
|
||||
@ -48,6 +49,7 @@ class DecadeMixHandler(tornado.web.RequestHandler):
|
||||
tracks = build_decade_mix(
|
||||
self.core, decade, uris=self.uris,
|
||||
max_tracks=self.max_tracks, max_per_album=self.max_per_album,
|
||||
max_per_artist=self.max_per_artist,
|
||||
)
|
||||
if not tracks:
|
||||
self.write({"playlist": None, "tracks": 0})
|
||||
@ -71,7 +73,7 @@ class GenreMixHandler(tornado.web.RequestHandler):
|
||||
def initialize(
|
||||
self, core: CoreProxy, prefix: str, uris: list[Uri] | None = None,
|
||||
playlist_dir: str | None = None,
|
||||
max_tracks: int = 0, max_per_album: int = 0,
|
||||
max_tracks: int = 0, max_per_album: int = 0, max_per_artist: int = 0,
|
||||
) -> None:
|
||||
self.core = core
|
||||
self.prefix = prefix
|
||||
@ -79,6 +81,7 @@ class GenreMixHandler(tornado.web.RequestHandler):
|
||||
self.playlist_dir = playlist_dir
|
||||
self.max_tracks = max_tracks
|
||||
self.max_per_album = max_per_album
|
||||
self.max_per_artist = max_per_artist
|
||||
|
||||
def post(self) -> None:
|
||||
data = json.loads(self.request.body)
|
||||
@ -90,6 +93,7 @@ class GenreMixHandler(tornado.web.RequestHandler):
|
||||
tracks = build_genre_mix(
|
||||
self.core, genre, uris=self.uris,
|
||||
max_tracks=self.max_tracks, max_per_album=self.max_per_album,
|
||||
max_per_artist=self.max_per_artist,
|
||||
)
|
||||
if not tracks:
|
||||
self.write({"playlist": None, "tracks": 0})
|
||||
@ -113,7 +117,7 @@ class ArtistMixHandler(tornado.web.RequestHandler):
|
||||
def initialize(
|
||||
self, core: CoreProxy, prefix: str, uris: list[Uri] | None = None,
|
||||
playlist_dir: str | None = None,
|
||||
max_tracks: int = 0, max_per_album: int = 0,
|
||||
max_tracks: int = 0, max_per_album: int = 0, max_per_artist: int = 0,
|
||||
) -> None:
|
||||
self.core = core
|
||||
self.prefix = prefix
|
||||
@ -121,6 +125,7 @@ class ArtistMixHandler(tornado.web.RequestHandler):
|
||||
self.playlist_dir = playlist_dir
|
||||
self.max_tracks = max_tracks
|
||||
self.max_per_album = max_per_album
|
||||
self.max_per_artist = max_per_artist
|
||||
|
||||
def post(self) -> None:
|
||||
data = json.loads(self.request.body)
|
||||
@ -132,6 +137,7 @@ class ArtistMixHandler(tornado.web.RequestHandler):
|
||||
tracks = build_artist_mix(
|
||||
self.core, artist, uris=self.uris,
|
||||
max_tracks=self.max_tracks, max_per_album=self.max_per_album,
|
||||
max_per_artist=self.max_per_artist,
|
||||
)
|
||||
if not tracks:
|
||||
self.write({"playlist": None, "tracks": 0})
|
||||
@ -323,17 +329,21 @@ def app_factory(config: Config, core: CoreProxy) -> list[tuple]:
|
||||
playlist_dir = _parse_playlist_dir(config)
|
||||
max_tracks = _parse_int(config, "max_tracks", 0)
|
||||
max_per_album = _parse_int(config, "max_per_album", 0)
|
||||
max_per_artist = _parse_int(config, "max_per_artist", 0)
|
||||
|
||||
return [
|
||||
(r"/decade", DecadeMixHandler,
|
||||
{"core": core, "prefix": prefix, "uris": uris, "playlist_dir": playlist_dir,
|
||||
"max_tracks": max_tracks, "max_per_album": max_per_album}),
|
||||
"max_tracks": max_tracks, "max_per_album": max_per_album,
|
||||
"max_per_artist": max_per_artist}),
|
||||
(r"/genre", GenreMixHandler,
|
||||
{"core": core, "prefix": prefix, "uris": uris, "playlist_dir": playlist_dir,
|
||||
"max_tracks": max_tracks, "max_per_album": max_per_album}),
|
||||
"max_tracks": max_tracks, "max_per_album": max_per_album,
|
||||
"max_per_artist": max_per_artist}),
|
||||
(r"/artist", ArtistMixHandler,
|
||||
{"core": core, "prefix": prefix, "uris": uris, "playlist_dir": playlist_dir,
|
||||
"max_tracks": max_tracks, "max_per_album": max_per_album}),
|
||||
"max_tracks": max_tracks, "max_per_album": max_per_album,
|
||||
"max_per_artist": max_per_artist}),
|
||||
(r"/album", AlbumMixHandler,
|
||||
{"core": core, "prefix": prefix, "playlist_dir": playlist_dir}),
|
||||
(r"/instant-mix", InstantMixHandler,
|
||||
|
||||
Reference in New Issue
Block a user