chiark / gitweb /
More reliable gapless play.
[disorder] / server / queue-ops.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2004-2008 Richard Kettlewell
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 /** @file server/queue-ops.c
19  * @brief Track queues (server-specific code)
20  */
21 #include "disorder-server.h"
22
23 static int find_in_list(struct queue_entry *needle,
24                         int nqs, struct queue_entry **qs) {
25   int n;
26
27   for(n = 0; n < nqs; ++n)
28     if(qs[n] == needle)
29       return 1;
30   return 0;
31 }
32
33 static int id_in_use(const char *id) {
34   struct queue_entry *q;
35
36   for(q = qhead.next; q != &qhead; q = q->next)
37     if(!strcmp(id, q->id))
38       return 1;
39   return 0;
40 }
41
42 static void queue_id(struct queue_entry *q) {
43   const char *id;
44
45   id = random_id();
46   while(id_in_use(id))
47     id = random_id();
48   q->id = id;
49 }
50
51 struct queue_entry *queue_add(const char *track, const char *submitter,
52                               int where, enum track_origin origin) {
53   struct queue_entry *q, *beforeme;
54
55   q = xmalloc(sizeof *q);
56   q->track = xstrdup(track);
57   q->submitter = submitter ? xstrdup(submitter) : 0;
58   q->state = playing_unplayed;
59   q->origin = origin;
60   queue_id(q);
61   time(&q->when);
62   switch(where) {
63   case WHERE_START:
64     queue_insert_entry(&qhead, q);
65     break;
66   case WHERE_END:
67     queue_insert_entry(qhead.prev, q);
68     break;
69   case WHERE_BEFORE_RANDOM:
70     /* We want to find the point in the queue before the block of random tracks
71      * at the end. */
72     beforeme = &qhead;
73     while(beforeme->prev != &qhead
74           && beforeme->prev->origin == origin_random)
75       beforeme = beforeme->prev;
76     queue_insert_entry(beforeme->prev, q);
77     break;
78   }
79   /* submitter will be a null pointer for a scratch */
80   if(submitter)
81     notify_queue(track, submitter);
82   eventlog_raw("queue", queue_marshall(q), (const char *)0);
83   return q;
84 }
85
86 int queue_move(struct queue_entry *q, int delta, const char *who) {
87   int moved = 0;
88   char buffer[20];
89
90   /* not the most efficient approach but hopefuly relatively comprehensible:
91    * the idea is that for each step we determine which nodes are affected, and
92    * fill in all the links starting at the 'prev' end and moving towards the
93    * 'next' end. */
94   
95   while(delta > 0 && q->prev != &qhead) {
96     struct queue_entry *n, *p, *pp;
97
98     n = q->next;
99     p = q->prev;
100     pp = p->prev;
101     pp->next = q;
102     q->prev = pp;
103     q->next = p;
104     p->prev = q;
105     p->next = n;
106     n->prev = p;
107     --delta;
108     ++moved;
109   }
110
111   while(delta < 0 && q->next != &qhead) {
112     struct queue_entry *n, *p, *nn;
113
114     p = q->prev;
115     n = q->next;
116     nn = n->next;
117     p->next = n;
118     n->prev = p;
119     n->next = q;
120     q->prev = n;
121     q->next = nn;
122     nn->prev = q;
123     ++delta;
124     --moved;
125   }
126
127   if(moved) {
128     info("user %s moved %s", who, q->id);
129     notify_queue_move(q->track, who);
130     sprintf(buffer, "%d", moved);
131     eventlog("moved", who, (char *)0);
132   }
133   
134   return delta;
135 }
136
137 void queue_moveafter(struct queue_entry *target,
138                      int nqs, struct queue_entry **qs,
139                      const char *who) {
140   struct queue_entry *q;
141   int n;
142
143   /* Normalize */
144   if(!target)
145     target = &qhead;
146   else
147     while(find_in_list(target, nqs, qs))
148       target = target->prev;
149   /* Do the move */
150   for(n = 0; n < nqs; ++n) {
151     q = qs[n];
152     queue_delete_entry(q);
153     queue_insert_entry(target, q);
154     target = q;
155     /* Log the individual tracks */
156     info("user %s moved %s", who, q->id);
157     notify_queue_move(q->track, who);
158   }
159   /* Report that the queue changed to the event log */
160   eventlog("moved", who, (char *)0);
161 }
162
163 void queue_remove(struct queue_entry *which, const char *who) {
164   if(who) {
165     info("user %s removed %s", who, which->id);
166     notify_queue_move(which->track, who);
167   }
168   eventlog("removed", which->id, who, (const char *)0);
169   queue_delete_entry(which);
170 }
171
172 void queue_played(struct queue_entry *q) {
173   while(pcount && pcount >= config->history) {
174     eventlog("recent_removed", phead.next->id, (char *)0);
175     queue_delete_entry(phead.next);
176     pcount--;
177   }
178   if(config->history) {
179     eventlog_raw("recent_added", queue_marshall(q), (char *)0);
180     queue_insert_entry(phead.prev, q);
181     ++pcount;
182   }
183 }
184
185 /*
186 Local Variables:
187 c-basic-offset:2
188 comment-column:40
189 fill-column:79
190 indent-tabs-mode:nil
191 End:
192 */