chiark / gitweb /
01987a2b7d3735a6e1d3e9c771b5e4f00e452789
[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) {
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   queue_id(q);
60   time(&q->when);
61   switch(where) {
62   case WHERE_START:
63     queue_insert_entry(&qhead, q);
64     break;
65   case WHERE_END:
66     queue_insert_entry(qhead.prev, q);
67     break;
68   case WHERE_BEFORE_RANDOM:
69     /* We want to find the point in the queue before the block of random tracks
70      * at the end. */
71     beforeme = &qhead;
72     while(beforeme->prev != &qhead
73           && beforeme->prev->state == playing_random)
74       beforeme = beforeme->prev;
75     queue_insert_entry(beforeme->prev, q);
76     break;
77   }
78   /* submitter will be a null pointer for a scratch */
79   if(submitter)
80     notify_queue(track, submitter);
81   eventlog_raw("queue", queue_marshall(q), (const char *)0);
82   return q;
83 }
84
85 int queue_move(struct queue_entry *q, int delta, const char *who) {
86   int moved = 0;
87   char buffer[20];
88
89   /* not the most efficient approach but hopefuly relatively comprehensible:
90    * the idea is that for each step we determine which nodes are affected, and
91    * fill in all the links starting at the 'prev' end and moving towards the
92    * 'next' end. */
93   
94   while(delta > 0 && q->prev != &qhead) {
95     struct queue_entry *n, *p, *pp;
96
97     n = q->next;
98     p = q->prev;
99     pp = p->prev;
100     pp->next = q;
101     q->prev = pp;
102     q->next = p;
103     p->prev = q;
104     p->next = n;
105     n->prev = p;
106     --delta;
107     ++moved;
108   }
109
110   while(delta < 0 && q->next != &qhead) {
111     struct queue_entry *n, *p, *nn;
112
113     p = q->prev;
114     n = q->next;
115     nn = n->next;
116     p->next = n;
117     n->prev = p;
118     n->next = q;
119     q->prev = n;
120     q->next = nn;
121     nn->prev = q;
122     ++delta;
123     --moved;
124   }
125
126   if(moved) {
127     info("user %s moved %s", who, q->id);
128     notify_queue_move(q->track, who);
129     sprintf(buffer, "%d", moved);
130     eventlog("moved", who, (char *)0);
131   }
132   
133   return delta;
134 }
135
136 void queue_moveafter(struct queue_entry *target,
137                      int nqs, struct queue_entry **qs,
138                      const char *who) {
139   struct queue_entry *q;
140   int n;
141
142   /* Normalize */
143   if(!target)
144     target = &qhead;
145   else
146     while(find_in_list(target, nqs, qs))
147       target = target->prev;
148   /* Do the move */
149   for(n = 0; n < nqs; ++n) {
150     q = qs[n];
151     queue_delete_entry(q);
152     queue_insert_entry(target, q);
153     target = q;
154     /* Log the individual tracks */
155     info("user %s moved %s", who, q->id);
156     notify_queue_move(q->track, who);
157   }
158   /* Report that the queue changed to the event log */
159   eventlog("moved", who, (char *)0);
160 }
161
162 void queue_remove(struct queue_entry *which, const char *who) {
163   if(who) {
164     info("user %s removed %s", who, which->id);
165     notify_queue_move(which->track, who);
166   }
167   eventlog("removed", which->id, who, (const char *)0);
168   queue_delete_entry(which);
169 }
170
171 void queue_played(struct queue_entry *q) {
172   while(pcount && pcount >= config->history) {
173     eventlog("recent_removed", phead.next->id, (char *)0);
174     queue_delete_entry(phead.next);
175     pcount--;
176   }
177   if(config->history) {
178     eventlog_raw("recent_added", queue_marshall(q), (char *)0);
179     queue_insert_entry(phead.prev, q);
180     ++pcount;
181   }
182 }
183
184 /*
185 Local Variables:
186 c-basic-offset:2
187 comment-column:40
188 fill-column:79
189 indent-tabs-mode:nil
190 End:
191 */