X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/bea6f6d52db537bf2c0dbe826aedcddaa8de482c..566145652162e02433c1cd461bf9b5e312abbc40:/server/choose.c diff --git a/server/choose.c b/server/choose.c index fa424ee..06c9601 100644 --- a/server/choose.c +++ b/server/choose.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "configuration.h" #include "log.h" @@ -51,6 +52,9 @@ #include "trackdb.h" #include "trackdb-int.h" #include "version.h" +#include "trackname.h" +#include "queue.h" +#include "server-queue.h" static DB_TXN *global_tid; @@ -101,6 +105,19 @@ static unsigned long long total_weight; /** @brief Count of tracks */ static long ntracks; +static char **required_tags; +static char **prohibited_tags; + +static int queue_contains(const struct queue_entry *head, + const char *track) { + const struct queue_entry *q; + + for(q = head->next; q != head; q = q->next) + if(!strcmp(q->track, track)) + return 1; + return 0; +} + /** @brief Compute the weight of a track * @param track Track name (UTF-8) * @param data Track data @@ -109,16 +126,63 @@ static long ntracks; * * Tracks to be excluded entirely are given a weight of 0. */ -static unsigned long compute_weight(const char attribute((unused)) *track, - struct kvp attribute((unused)) *data, +static unsigned long compute_weight(const char *track, + struct kvp *data, struct kvp *prefs) { const char *s; + char **track_tags; + time_t last, now; + + /* Reject tracks not in any collection (race between edit config and + * rescan) */ + if(!find_track_root(track)) { + info("found track not in any collection: %s", track); + return 0; + } - /* Firstly, tracks with random play disabled always have weight 0 and that's - * that */ + /* Reject aliases to avoid giving aliased tracks extra weight */ + if(kvp_get(data, "_alias_for")) + return 0; + + /* Reject tracks with random play disabled */ if((s = kvp_get(prefs, "pick_at_random")) && !strcmp(s, "0")) return 0; + + /* Reject tracks played within the last 8 hours */ + if((s = kvp_get(prefs, "played_time"))) { + last = atoll(s); + now = time(0); + if(now < last + config->replay_min) + return 0; + } + + /* Reject tracks currently in the queue or in the recent list */ + if(queue_contains(&qhead, track) + || queue_contains(&phead, track)) + return 0; + + /* We'll need tags for a number of things */ + track_tags = parsetags(kvp_get(prefs, "tags")); + + /* Reject tracks with prohibited tags */ + if(prohibited_tags && tag_intersection(track_tags, prohibited_tags)) + return 0; + + /* Reject tracks that lack required tags */ + if(*required_tags && !tag_intersection(track_tags, required_tags)) + return 0; + + /* Use the configured weight if available */ + if((s = kvp_get(prefs, "weight"))) { + long n; + errno = 0; + + n = strtol(s, 0, 10); + if((errno == 0 || errno == ERANGE) && n >= 0) + return n; + } + return 90000; } @@ -128,11 +192,15 @@ static int collect_tracks_callback(const char *track, struct kvp *prefs, void attribute((unused)) *u, DB_TXN attribute((unused)) *tid) { - const unsigned long weight = compute_weight(track, data, prefs); + unsigned long weight = compute_weight(track, data, prefs); if(weight) { struct weighted_track *const t = xmalloc(sizeof *t); + /* Clamp weight so that we can fit in billions of tracks when we do + * arithmetic in long long */ + if(weight > 0x7fffffff) + weight = 0x7fffffff; t->next = tracks; t->track = track; t->weight = weight; @@ -177,7 +245,8 @@ static void pick_track(void) { } int main(int argc, char **argv) { - int n, logsyslog = !isatty(2); + int n, logsyslog = !isatty(2), err; + const char *tags; set_progname(argv); mem_init(); @@ -199,10 +268,19 @@ int main(int argc, char **argv) { log_default = &log_syslog; } if(config_read(0)) fatal(0, "cannot read configuration"); + /* Find out current queue/recent list */ + queue_read(); + recent_read(); /* Generate the candidate track list */ trackdb_init(TRACKDB_NO_RECOVER); trackdb_open(TRACKDB_NO_UPGRADE|TRACKDB_READ_ONLY); global_tid = trackdb_begin_transaction(); + if((err = trackdb_get_global_tid("required-tags", global_tid, &tags))) + fatal(0, "error getting required-tags: %s", db_strerror(err)); + required_tags = parsetags(tags); + if((err = trackdb_get_global_tid("prohibited-tags", global_tid, &tags))) + fatal(0, "error getting prohibited-tags: %s", db_strerror(err)); + prohibited_tags = parsetags(tags); if(trackdb_scan(0, collect_tracks_callback, 0, global_tid)) exit(1); trackdb_commit_transaction(global_tid);