2 * This file is part of DisOrder.
3 * Copyright (C) 2004-2008 Richard Kettlewell
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 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
35 #include "server-queue.h"
39 #include "configuration.h"
40 #include "inputline.h"
43 /* the head of the queue is played next, so normally we add to the tail */
44 struct queue_entry qhead
= { &qhead
, &qhead
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
46 /* the head of the recent list is the oldest thing, the tail the most recently
48 struct queue_entry phead
= { &phead
, &phead
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
52 /* add new entry @n@ to a doubly linked list just after @b@ */
53 static void l_add(struct queue_entry
*b
, struct queue_entry
*n
) {
60 /* remove an entry from a doubly-linked list */
61 static void l_remove(struct queue_entry
*node
) {
62 node
->next
->prev
= node
->prev
;
63 node
->prev
->next
= node
->next
;
66 void queue_fix_sofar(struct queue_entry
*q
) {
69 /* Fake up SOFAR field for currently-playing tracks that don't have it filled
70 * in by the speaker process. XXX this horrible bodge should go away when we
71 * have a more general implementation of pausing as that field will always
72 * have to be right for the playing track. */
73 if((q
->state
== playing_started
74 || q
->state
== playing_paused
)
75 && q
->type
& DISORDER_PLAYER_PAUSES
76 && (q
->type
& DISORDER_PLAYER_TYPEMASK
) != DISORDER_PLAYER_RAW
) {
78 if(q
->uptopause
== -1) /* Don't know how far thru. */
80 else if(q
->lastresumed
) /* Has been paused and resumed. */
81 sofar
= q
->uptopause
+ time(0) - q
->lastresumed
;
82 else /* Currently paused. */
84 } else /* Never been paused. */
85 sofar
= time(0) - q
->played
;
90 static void queue_read_error(const char *msg
,
92 fatal(0, "error parsing queue %s: %s", (const char *)u
, msg
);
95 static void queue_do_read(struct queue_entry
*head
, const char *path
) {
98 struct queue_entry
*q
;
100 if(!(fp
= fopen(path
, "r"))) {
102 return; /* no queue */
103 fatal(errno
, "error opening %s", path
);
105 head
->next
= head
->prev
= head
;
106 while(!inputline(path
, fp
, &buffer
, '\n')) {
107 q
= xmalloc(sizeof *q
);
108 queue_unmarshall(q
, buffer
, queue_read_error
, (void *)path
);
112 fatal(0, "incomplete queue entry in %s", path
);
113 l_add(head
->prev
, q
);
115 if(ferror(fp
)) fatal(errno
, "error reading %s", path
);
119 void queue_read(void) {
120 queue_do_read(&qhead
, config_get_file("queue"));
123 void recent_read(void) {
124 struct queue_entry
*q
;
126 queue_do_read(&phead
, config_get_file("recent"));
127 /* reset pcount after loading */
136 static void queue_do_write(const struct queue_entry
*head
, const char *path
) {
139 struct queue_entry
*q
;
141 byte_xasprintf(&tmp
, "%s.new", path
);
142 if(!(fp
= fopen(tmp
, "w"))) fatal(errno
, "error opening %s", tmp
);
143 for(q
= head
->next
; q
!= head
; q
= q
->next
)
144 if(fprintf(fp
, "%s\n", queue_marshall(q
)) < 0)
145 fatal(errno
, "error writing %s", tmp
);
146 if(fclose(fp
) < 0) fatal(errno
, "error closing %s", tmp
);
147 if(rename(tmp
, path
) < 0) fatal(errno
, "error replacing %s", path
);
150 void queue_write(void) {
151 queue_do_write(&qhead
, config_get_file("queue"));
154 void recent_write(void) {
155 queue_do_write(&phead
, config_get_file("recent"));
158 static int id_in_use(const char *id
) {
159 struct queue_entry
*q
;
161 for(q
= qhead
.next
; q
!= &qhead
; q
= q
->next
)
162 if(!strcmp(id
, q
->id
))
167 static void queue_id(struct queue_entry
*q
) {
176 struct queue_entry
*queue_add(const char *track
, const char *submitter
,
178 struct queue_entry
*q
, *beforeme
;
180 q
= xmalloc(sizeof *q
);
181 q
->track
= xstrdup(track
);
182 q
->submitter
= submitter ?
xstrdup(submitter
) : 0;
183 q
->state
= playing_unplayed
;
191 l_add(qhead
.prev
, q
);
193 case WHERE_BEFORE_RANDOM
:
194 /* We want to find the point in the queue before the block of random tracks
197 while(beforeme
->prev
!= &qhead
198 && beforeme
->prev
->state
== playing_random
)
199 beforeme
= beforeme
->prev
;
200 l_add(beforeme
->prev
, q
);
203 /* submitter will be a null pointer for a scratch */
205 notify_queue(track
, submitter
);
206 eventlog_raw("queue", queue_marshall(q
), (const char *)0);
210 int queue_move(struct queue_entry
*q
, int delta
, const char *who
) {
214 /* not the most efficient approach but hopefuly relatively comprehensible:
215 * the idea is that for each step we determine which nodes are affected, and
216 * fill in all the links starting at the 'prev' end and moving towards the
219 while(delta
> 0 && q
->prev
!= &qhead
) {
220 struct queue_entry
*n
, *p
, *pp
;
235 while(delta
< 0 && q
->next
!= &qhead
) {
236 struct queue_entry
*n
, *p
, *nn
;
252 info("user %s moved %s", who
, q
->id
);
253 notify_queue_move(q
->track
, who
);
254 sprintf(buffer
, "%d", moved
);
255 eventlog("moved", who
, (char *)0);
261 static int find_in_list(struct queue_entry
*needle
,
262 int nqs
, struct queue_entry
**qs
) {
265 for(n
= 0; n
< nqs
; ++n
)
271 void queue_moveafter(struct queue_entry
*target
,
272 int nqs
, struct queue_entry
**qs
,
274 struct queue_entry
*q
;
281 while(find_in_list(target
, nqs
, qs
))
282 target
= target
->prev
;
284 for(n
= 0; n
< nqs
; ++n
) {
289 /* Log the individual tracks */
290 info("user %s moved %s", who
, q
->id
);
291 notify_queue_move(q
->track
, who
);
293 /* Report that the queue changed to the event log */
294 eventlog("moved", who
, (char *)0);
297 void queue_remove(struct queue_entry
*which
, const char *who
) {
299 info("user %s removed %s", who
, which
->id
);
300 notify_queue_move(which
->track
, who
);
302 eventlog("removed", which
->id
, who
, (const char *)0);
306 struct queue_entry
*queue_find(const char *key
) {
307 struct queue_entry
*q
;
310 q
!= &qhead
&& strcmp(q
->track
, key
) && strcmp(q
->id
, key
);
313 return q
!= &qhead ? q
: 0;
316 void queue_played(struct queue_entry
*q
) {
317 while(pcount
&& pcount
>= config
->history
) {
318 eventlog("recent_removed", phead
.next
->id
, (char *)0);
319 l_remove(phead
.next
);
322 if(config
->history
) {
323 eventlog_raw("recent_added", queue_marshall(q
), (char *)0);
324 l_add(phead
.prev
, q
);