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