From 2dc2f4788add40c2fe7c8fdede76189b54259155 Mon Sep 17 00:00:00 2001 Message-Id: <2dc2f4788add40c2fe7c8fdede76189b54259155.1713542952.git.mdw@distorted.org.uk> From: Mark Wooding Date: Mon, 27 Oct 2008 19:52:24 +0000 Subject: [PATCH] Add a new 'origin' field to queue entries. This records the origin of the track independently of its current state (the 'state' field mixes up the two concepts in an unhelpful way). A few things use this but not yet all. Organization: Straylight/Edgeware From: Richard Kettlewell Old queues will produce confusion if you upgrade to this version. Nothing's yet been done to fix this; there'll have to be a version field. Fixed a few bits of template documentation too. --- cgi/actions.c | 2 +- cgi/macros-disorder.c | 24 ++++++++++++++++++---- clients/disorder.c | 13 ++++++++++-- lib/macros-builtin.c | 24 +++++++++++----------- lib/queue.c | 33 +++++++++++++++++++++++++++++- lib/queue.h | 44 +++++++++++++++++++++++++++++++++++++++- server/disorder-server.h | 2 +- server/play.c | 11 +++++----- server/queue-ops.c | 5 +++-- server/schedule.c | 2 +- server/server-queue.c | 10 +++++++-- server/server.c | 2 +- 12 files changed, 139 insertions(+), 33 deletions(-) diff --git a/cgi/actions.c b/cgi/actions.c index f63e519..b5c516c 100644 --- a/cgi/actions.c +++ b/cgi/actions.c @@ -87,7 +87,7 @@ static void act_playing(void) { } if(!dcgi_playing && ((dcgi_queue - && dcgi_queue->state != playing_random) + && dcgi_queue->origin != origin_random) || dcgi_random_enabled) && dcgi_enabled) { /* no track playing but playing is enabled and there is something coming diff --git a/cgi/macros-disorder.c b/cgi/macros-disorder.c index 6348f47..c0eee5d 100644 --- a/cgi/macros-disorder.c +++ b/cgi/macros-disorder.c @@ -486,7 +486,7 @@ static int exp_isqueue(int attribute((unused)) nargs, return mx_bool_result(output, !!dcgi_queue); } -/*$ @isrecent@ +/*$ @isrecent * * Expands to "true" if there the recently played list is nonempty, otherwise * "false". @@ -581,7 +581,7 @@ static int exp_transform(int nargs, return sink_writes(output, cgi_sgmlquote(t)) < 0 ? -1 : 0; } -/*$ @enabled@ +/*$ @enabled * * Expands to "true" if playing is enabled, otherwise "false". */ @@ -680,7 +680,7 @@ static int exp_paused(int attribute((unused)) nargs, && dcgi_playing->state == playing_paused)); } -/*$ @state{ID}@ +/*$ @state{ID} * * Expands to the current state of track ID. */ @@ -695,7 +695,22 @@ static int exp_state(int attribute((unused)) nargs, return 0; } -/*$ @right{RIGHT}{WITH-RIGHT}{WITHOUT-RIGHT}@ +/*$ @origin{ID} + * + * Expands to the current origin of track ID. + */ +static int exp_origin(int attribute((unused)) nargs, + char **args, + struct sink *output, + void attribute((unused)) *u) { + struct queue_entry *q = dcgi_findtrack(args[0]); + + if(q) + return sink_writes(output, track_origins[q->origin]) < 0 ? -1 : 0; + return 0; +} + +/*$ @right{RIGHT}{WITH-RIGHT}{WITHOUT-RIGHT} * * Expands to WITH-RIGHT if the current user has right RIGHT, otherwise to * WITHOUT-RIGHT. The WITHOUT-RIGHT argument may be left out. @@ -999,6 +1014,7 @@ void dcgi_expansions(void) { mx_register("label", 1, 1, exp_label); mx_register("length", 1, 1, exp_length); mx_register("movable", 1, 2, exp_movable); + mx_register("origin", 1, 1, exp_origin); mx_register("part", 2, 3, exp_part); mx_register("paused", 0, 0, exp_paused); mx_register("pref", 2, 2, exp_pref); diff --git a/clients/disorder.c b/clients/disorder.c index 6ac27bc..4afc568 100644 --- a/clients/disorder.c +++ b/clients/disorder.c @@ -99,8 +99,17 @@ static void cf_version(char attribute((unused)) **argv) { static void print_queue_entry(const struct queue_entry *q) { if(q->track) xprintf("track %s\n", nullcheck(utf82mb(q->track))); if(q->id) xprintf(" id %s\n", nullcheck(utf82mb(q->id))); - if(q->submitter) xprintf(" submitted by %s at %s", - nullcheck(utf82mb(q->submitter)), ctime(&q->when)); + switch(q->origin) { + case origin_adopted: + case origin_picked: + case origin_scheduled: + xprintf(" %s by %s at %s", + track_origins[q->origin], + nullcheck(utf82mb(q->submitter)), ctime(&q->when)); + break; + default: + break; + } if(q->played) xprintf(" played at %s", ctime(&q->played)); if(q->state == playing_started || q->state == playing_paused) xprintf(" %lds so far", q->sofar); diff --git a/lib/macros-builtin.c b/lib/macros-builtin.c index 8c88f72..3d49c25 100644 --- a/lib/macros-builtin.c +++ b/lib/macros-builtin.c @@ -99,7 +99,7 @@ char *mx_find(const char *name, int report) { return path; } -/*$ @include{TEMPLATE}@ +/*$ @include{TEMPLATE} * * Includes TEMPLATE. * @@ -154,7 +154,7 @@ static int exp_include(int attribute((unused)) nargs, return 0; } -/*$ @include{COMMAND}@ +/*$ @include{COMMAND} * * Executes COMMAND via the shell (using "sh -c") and copies its * standard output to the template output. The shell command output @@ -203,7 +203,7 @@ static int exp_shell(int attribute((unused)) nargs, return 0; } -/*$ @if{CONDITION}{IF-TRUE}{IF-FALSE}@ +/*$ @if{CONDITION}{IF-TRUE}{IF-FALSE} * * If CONDITION is "true" then evaluates to IF-TRUE. Otherwise * evaluates to IF-FALSE. The IF-FALSE part is optional. @@ -225,7 +225,7 @@ static int exp_if(int nargs, return 0; } -/*$ @and{BRANCH}{BRANCH}...@ +/*$ @and{BRANCH}{BRANCH}... * * Expands to "true" if all the branches are "true" otherwise to "false". If * there are no brances then the result is "true". Only as many branches as @@ -251,7 +251,7 @@ static int exp_and(int nargs, return mx_bool_result(output, result); } -/*$ @or{BRANCH}{BRANCH}...@ +/*$ @or{BRANCH}{BRANCH}... * * Expands to "true" if any of the branches are "true" otherwise to "false". * If there are no brances then the result is "false". Only as many branches @@ -277,7 +277,7 @@ static int exp_or(int nargs, return mx_bool_result(output, result); } -/*$ @not{CONDITION}@ +/*$ @not{CONDITION} * * Expands to "true" unless CONDITION is "true" in which case "false". */ @@ -288,7 +288,7 @@ static int exp_not(int attribute((unused)) nargs, return mx_bool_result(output, !mx_str2bool(args[0])); } -/*$ @#{...}@ +/*$ @#{...} * * Expands to nothing. The argument(s) are not fully evaluated, and no side * effects occur. @@ -300,7 +300,7 @@ static int exp_comment(int attribute((unused)) nargs, return 0; } -/*$ @urlquote{STRING}@ +/*$ @urlquote{STRING} * * URL-quotes a string, i.e. replaces any characters not safe to use unquoted * in a URL with %-encoded form. @@ -315,7 +315,7 @@ static int exp_urlquote(int attribute((unused)) nargs, return 0; } -/*$ @eq{S1}{S2}...@ +/*$ @eq{S1}{S2}... * * Expands to "true" if all the arguments are identical, otherwise to "false" * (i.e. if any pair of arguments differs). @@ -339,7 +339,7 @@ static int exp_eq(int nargs, return mx_bool_result(output, result); } -/*$ @ne{S1}{S2}...@ +/*$ @ne{S1}{S2}... * * Expands to "true" if all of the arguments differ from one another, otherwise * to "false" (i.e. if any value appears more than once). @@ -363,7 +363,7 @@ static int exp_ne(int nargs, return mx_bool_result(output, result); } -/*$ @discard{...}@ +/*$ @discard{...} * * Expands to nothing. Unlike the comment expansion @#{...}, side effects of * arguments are not suppressed. So this can be used to surround a collection @@ -376,7 +376,7 @@ static int exp_discard(int attribute((unused)) nargs, return 0; } -/*$ @define{NAME}{ARG1 ARG2...}{DEFINITION}@ +/*$ @define{NAME}{ARG1 ARG2...}{DEFINITION} * * Define a macro. The macro will be called NAME and will act like an * expansion. When it is expanded, the expansion is replaced by DEFINITION, diff --git a/lib/queue.c b/lib/queue.c index 71d85aa..333fe7d 100644 --- a/lib/queue.c +++ b/lib/queue.c @@ -31,7 +31,7 @@ #include "table.h" #include "printf.h" -const char *playing_states[] = { +const char *const playing_states[] = { "failed", "isscratch", "no_player", @@ -44,6 +44,15 @@ const char *playing_states[] = { "unplayed" }; +/** @brief String values for @c origin field */ +const char *const track_origins[] = { + "adopted", + "picked", + "random", + "scheduled", + "scratch", +}; + #define VALUE(q, offset, type) *(type *)((char *)q + offset) /* add new entry @n@ to a doubly linked list just after @b@ */ @@ -139,10 +148,31 @@ static int unmarshall_state(char *data, struct queue_entry *q, return 0; } +static int unmarshall_origin(char *data, struct queue_entry *q, + size_t offset, + void (*error_handler)(const char *, void *), + void *u) { + int n; + + if((n = table_find(track_origins, 0, sizeof (char *), + sizeof track_origins / sizeof *track_origins, + data)) < 0) { + D(("origin=[%s] n=%d", data, n)); + error_handler("invalid origin", u); + return -1; + } + VALUE(q, offset, enum track_origin) = n; + return 0; +} + static const char *marshall_state(const struct queue_entry *q, size_t offset) { return playing_states[VALUE(q, offset, enum playing_state)]; } +static const char *marshall_origin(const struct queue_entry *q, size_t offset) { + return track_origins[VALUE(q, offset, enum track_origin)]; +} + #define F(n, h) { #n, offsetof(struct queue_entry, n), marshall_##h, unmarshall_##h } static const struct field { @@ -156,6 +186,7 @@ static const struct field { /* Keep this table sorted. */ F(expected, time_t), F(id, string), + F(origin, origin), F(played, time_t), F(scratched, string), F(sofar, long), diff --git a/lib/queue.h b/lib/queue.h index a084502..7ca9b4f 100644 --- a/lib/queue.h +++ b/lib/queue.h @@ -40,7 +40,48 @@ enum playing_state { playing_unplayed /* haven't played this track yet */ }; -extern const char *playing_states[]; +extern const char *const playing_states[]; + +/** @brief Possible track origins + * + * This is a newly introduced field. The aim is ultimately to separate the + * concepts of the track origin and its current state. NB that both are + * potentially mutable! + */ +enum track_origin { + /** @brief Track was picked at random and then adopted by a user + * + * @c submitter identifies who adopted it. This isn't implemented + * yet. + */ + origin_adopted, + + /** @brief Track was picked by a user + * + * @c submitter identifies who picked it + */ + origin_picked, + + /** @brief Track was picked at random + * + * @c submitter will be NULL + */ + origin_random, + + /** @brief Track was scheduled by a user + * + * @c submitter identifies who picked it + */ + origin_scheduled, + + /** @brief Track is a scratch + * + * @c submitter identifies who did the scratching + */ + origin_scratch +}; + +extern const char *const track_origins[]; /* queue entries form a circular doubly-linked list */ struct queue_entry { @@ -51,6 +92,7 @@ struct queue_entry { time_t when; /* time submitted */ time_t played; /* when played */ enum playing_state state; /* state */ + enum track_origin origin; /* where track came from */ long wstat; /* wait status */ const char *scratched; /* scratched by */ const char *id; /* queue entry ID */ diff --git a/server/disorder-server.h b/server/disorder-server.h index 09eafbc..1a52e25 100644 --- a/server/disorder-server.h +++ b/server/disorder-server.h @@ -121,7 +121,7 @@ void recent_write(void); /* write the recently played list out. Calls @fatal@ on error. */ struct queue_entry *queue_add(const char *track, const char *submitter, - int where); + int where, enum track_origin origin); #define WHERE_START 0 /* Add to head of queue */ #define WHERE_END 1 /* Add to end of queue */ #define WHERE_BEFORE_RANDOM 2 /* End, or before random track */ diff --git a/server/play.c b/server/play.c index f657d6f..9a04a98 100644 --- a/server/play.c +++ b/server/play.c @@ -491,7 +491,7 @@ static void chosen_random_track(ev_source *ev, if(!track) return; /* Add the track to the queue */ - q = queue_add(track, 0, WHERE_END); + q = queue_add(track, 0, WHERE_END, origin_random); q->state = playing_random; D(("picked %p (%s) at random", (void *)q, q->track)); queue_write(); @@ -534,9 +534,10 @@ void play(ev_source *ev) { } /* There must be at least one track in the queue. */ q = qhead.next; - /* If random play is disabled but the track is a random one then don't play - * it. play() will be called again when random play is re-enabled. */ - if(!random_enabled && q->state == playing_random) + /* If random play is disabled but the track is a non-adopted random one + * then don't play it. play() will be called again when random play is + * re-enabled. */ + if(!random_enabled && q->origin == origin_random) return; D(("taken %p (%s) from queue", (void *)q, q->track)); /* Try to start playing. */ @@ -642,7 +643,7 @@ void scratch(const char *who, const char *id) { * bother if playing is disabled) */ if(playing_is_enabled() && config->scratch.n) { int r = rand() * (double)config->scratch.n / (RAND_MAX + 1.0); - q = queue_add(config->scratch.s[r], who, WHERE_START); + q = queue_add(config->scratch.s[r], who, WHERE_START, origin_scratch); q->state = playing_isscratch; } notify_scratch(playing->track, playing->submitter, who, diff --git a/server/queue-ops.c b/server/queue-ops.c index 01987a2..7dfcdb1 100644 --- a/server/queue-ops.c +++ b/server/queue-ops.c @@ -49,13 +49,14 @@ static void queue_id(struct queue_entry *q) { } struct queue_entry *queue_add(const char *track, const char *submitter, - int where) { + int where, enum track_origin origin) { struct queue_entry *q, *beforeme; q = xmalloc(sizeof *q); q->track = xstrdup(track); q->submitter = submitter ? xstrdup(submitter) : 0; q->state = playing_unplayed; + q->origin = origin; queue_id(q); time(&q->when); switch(where) { @@ -70,7 +71,7 @@ struct queue_entry *queue_add(const char *track, const char *submitter, * at the end. */ beforeme = &qhead; while(beforeme->prev != &qhead - && beforeme->prev->state == playing_random) + && beforeme->prev->origin == origin_random) beforeme = beforeme->prev; queue_insert_entry(beforeme->prev, q); break; diff --git a/server/schedule.c b/server/schedule.c index 088229e..ec7eddc 100644 --- a/server/schedule.c +++ b/server/schedule.c @@ -374,7 +374,7 @@ static void schedule_play(ev_source *ev, return; } info("scheduled event %s: %s play %s", id, who, track); - q = queue_add(track, who, WHERE_START); + q = queue_add(track, who, WHERE_START, origin_scheduled); queue_write(); if(q == qhead.next && playing) prepare(ev, q); diff --git a/server/server-queue.c b/server/server-queue.c index a1fa181..c65bf6d 100644 --- a/server/server-queue.c +++ b/server/server-queue.c @@ -18,11 +18,17 @@ #include "disorder-server.h" /* the head of the queue is played next, so normally we add to the tail */ -struct queue_entry qhead = { &qhead, &qhead, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +struct queue_entry qhead = { + .next = &qhead, + .prev = &qhead +}; /* the head of the recent list is the oldest thing, the tail the most recently * played */ -struct queue_entry phead = { &phead, &phead, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +struct queue_entry phead = { + .next = &phead, + .prev = &phead +}; long pcount; diff --git a/server/server.c b/server/server.c index 9c9db68..0b2458d 100644 --- a/server/server.c +++ b/server/server.c @@ -188,7 +188,7 @@ static int c_play(struct conn *c, char **vec, sink_writes(ev_writer_sink(c->w), "550 cannot resolve track\n"); return 1; } - q = queue_add(track, c->who, WHERE_BEFORE_RANDOM); + q = queue_add(track, c->who, WHERE_BEFORE_RANDOM, origin_picked); queue_write(); /* If we added the first track, and something is playing, then prepare the * new track. If nothing is playing then we don't bother as it wouldn't gain -- [mdw]