chiark / gitweb /
Add a new 'origin' field to queue entries. This records the origin of
authorRichard Kettlewell <rjk@greenend.org.uk>
Mon, 27 Oct 2008 19:52:24 +0000 (19:52 +0000)
committerRichard Kettlewell <rjk@greenend.org.uk>
Mon, 27 Oct 2008 19:52:24 +0000 (19:52 +0000)
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.

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.

12 files changed:
cgi/actions.c
cgi/macros-disorder.c
clients/disorder.c
lib/macros-builtin.c
lib/queue.c
lib/queue.h
server/disorder-server.h
server/play.c
server/queue-ops.c
server/schedule.c
server/server-queue.c
server/server.c

index f63e519..b5c516c 100644 (file)
@@ -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
index 6348f47..c0eee5d 100644 (file)
@@ -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);
index 6ac27bc..4afc568 100644 (file)
@@ -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);
index 8c88f72..3d49c25 100644 (file)
@@ -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,
index 71d85aa..333fe7d 100644 (file)
@@ -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),
index a084502..7ca9b4f 100644 (file)
@@ -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 */
index 09eafbc..1a52e25 100644 (file)
@@ -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 */
index f657d6f..9a04a98 100644 (file)
@@ -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,
index 01987a2..7dfcdb1 100644 (file)
@@ -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;
index 088229e..ec7eddc 100644 (file)
@@ -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);
index a1fa181..c65bf6d 100644 (file)
 #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;
 
index 9c9db68..0b2458d 100644 (file)
@@ -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