Calliope - antisocial music recommendations

I’m trying to get my feet wet with “all beets albums, ordered by $added”. I think beets/plugins can do this already but my idea is to build more functionality on top of it. I got this far:

beet ls --format '$added-$album-$track-$title'

Should I be looking in constraints.py ? calliope/select/constraints.py · main · Sam Thursfield / calliope · GitLab

beets? calliope/beets/__init__.py · main · Sam Thursfield / calliope · GitLab

This looks promising: examples/special-mix/special_mix.py · main · Sam Thursfield / calliope · GitLab

Is this something cpe can do now or do I have to edit the code? Perhaps the keys that are requested from beets?

Right now I miss a sql-like query functionality.

A metaphor like this might make sense:

  1. select columns/information that an algorithm will use
  2. the algorithm sorts the rows (deterministic) or semi-randomly picks rows (ai/linear solver) based on column values
  3. cpe resolves the rows to music files
  4. cpe pipes the result to a playlist, music file etc.

example:

query.sql

select
genre, artist, year, album, disc, track, plays, rating, days_since_last_play
from
beets
where
genre = 'jazz'

Then moving beyond the sql metaphor:

  1. I want 4 albums with rating >= 9/10
  2. 10 albums with no artist duplicated, and score them higher (make them more likely to be chosen by the solver) based on days_since_last_play.

It could look like this:

cpe select query.sql picklist.py

picklist_helpers.py:

def prefer(candidates, criterion):
    # see also: https://gitlab.com/samthursfield/calliope/-/blob/main/calliope/select/localsearch.py#L157
    '''do some non-deterministic magic to rank higher by `criterion` but don't just do a simple sort.
    for example, 2 is not always greater than 1 in this paradigm. (If I'm understanding the calliope docs.)'''

picklist.py (custom per ‘picklist’, I may have one for jazz and one for running and one for parties, etc.)

def picker(table):
    '''Choose tracks from table based on below constraints.'''
    # each line in picklist is its own distinct set of constraints, unrelated to other lines
    # each line is attempted to be solved until that line is fulfilled or the overall constraint (total runtime) is met
    picklist = '''n=4;rating>=9;runtime<20minutes;shuffle_albums;
    n=10;n_albumartists=1;last_played>7;prefer(last_played+);shuffle_albums''' # only 1 album per artist on this line
    output_playlist = []
    while sum(output_playlist['runtime']) < '60minutes':
        for list_item in picklist:
            new_items = parse_list_item(list_item)
            # allow for the while-loop to break partway through, if constraint is met
            for item in new_items:
                output_playlist.append(new_items)

Or maybe the picklist line format could be something like count, sort, select.

n=10; sort=shuffle(albums); select= rating>=9, runtime<20minutes