2
playlist: playlist logic
4
copyright 1995-2006 by the mpg123 project - free software under the terms of the LGPL 2.1
5
see COPYING and AUTHORS files in distribution or http://mpg123.de
6
initially written by Michael Hipp, outsourced/reorganized by Thomas Orgis
8
If we officially support Windows again, we should have this reworked to really cope with Windows paths, too.
13
#include "getlopt.h" /* for loptind */
15
#include "term.h" /* for term_restore */
20
/* increase linebuf in blocks of ... bytes */
21
#define LINEBUF_STEP 100
23
/* one global instance... add a pointer to this to every function definition and you have OO-style... */
27
functions to be called from outside and thus declared in the header:
29
void prepare_playlist(int argc, char** argv);
30
char *get_next_file(int argc, char **argv);
36
int add_next_file (int argc, char *argv[]);
37
void shuffle_playlist();
38
void print_playlist();
40
int add_copy_to_playlist(char* new_entry);
41
int add_to_playlist(char* new_entry, char freeit);
43
/* used to be init_input */
44
void prepare_playlist(int argc, char** argv)
47
fetch all playlist entries ... I don't consider playlists to be an endless stream.
48
If you want to intentionally hang mpg123 on some other prog that may take infinite time to produce the full list (perhaps load tracks on demand), then just use the remote control and let that program print "load filename" instead of "filename".
49
We may even provide a simple wrapper script that emulates the old playlist reading behaviour (for files and stdin, http playlists are actually a strong point on reading the list in _before_ starting playback since http connections don't last forever).
52
while (add_next_file(argc, argv)) {}
55
fprintf(stderr, "\nplaylist in normal order:\n");
57
fprintf(stderr, "\n");
59
if(param.shuffle == 1) shuffle_playlist();
60
/* Don't need these anymore, we have copies! */
61
free_stringbuf(&pl.linebuf);
62
free_stringbuf(&pl.dir);
69
if(pl.fill == 0) newfile = NULL;
71
/* normal order, just pick next thing */
74
if(pl.pos < pl.fill) newfile = pl.list[pl.pos].url;
78
/* randomly select files, with repeating */
79
else newfile = pl.list[ (size_t) rand() % pl.fill ].url;
84
/* It doesn't really matter on program exit, but anyway...
85
Make sure you don't free() an item of argv! */
90
debug("going to free() the playlist");
94
debug1("free()ing entry %lu", (unsigned long)pl.fill);
95
if(pl.list[pl.fill].freeit) free(pl.list[pl.fill].url);
100
debug("free()d the playlist");
102
free_stringbuf(&pl.linebuf);
103
free_stringbuf(&pl.dir);
106
/* the constructor... */
117
init_stringbuf(&pl.dir);
118
init_stringbuf(&pl.linebuf);
123
slightly modified find_next_file from mpg123.c
124
now doesn't return the next entry but adds it to playlist struct
125
returns 1 if it found something, 0 on end
127
int add_next_file (int argc, char *argv[])
131
/* hack for url that has been detected as track, not playlist */
132
if(pl.type == NO_LIST) return 0;
134
/* Get playlist dirname to append it to the files in playlist */
138
/* Oh, right... that doesn't look good for Windows... */
139
if ((slashpos=strrchr(param.listname, '/')))
141
/* up to and including /, with space for \0 */
142
if(resize_stringbuf(&pl.dir, 2 + slashpos - param.listname))
144
memcpy(pl.dir.p, param.listname, pl.dir.size-1);
145
pl.dir.p[pl.dir.size-1] = 0;
149
error("cannot allocate memory for list directory!");
155
if (param.listname || pl.file)
157
size_t line_offset = 0;
161
if (!*param.listname || !strcmp(param.listname, "-"))
164
param.listname = NULL;
167
else if (!strncmp(param.listname, "http://", 7))
170
char *listmime = NULL;
171
fd = http_open(param.listname, &listmime);
172
debug1("listmime: %p", (void*) listmime);
175
debug1("listmime value: %s", listmime);
176
if(!strcmp("audio/x-mpegurl", listmime)) pl.type = M3U;
177
else if(!strcmp("audio/x-scpls", listmime)) pl.type = PLS;
180
if(fd >= 0) close(fd);
181
if(!strcmp("audio/mpeg", listmime))
184
if(param.listentry < 0)
186
printf("#note you gave me a file url, no playlist, so...\n#entry 1\n%s\n", param.listname);
191
fprintf(stderr, "Note: MIME type indicates that this is no playlist but an mpeg audio file... reopening as such.\n");
192
add_to_playlist(param.listname, 0);
196
fprintf(stderr, "Error: unknown playlist MIME type %s; maybe "PACKAGE_NAME" can support it in future if you report this to the maintainer.\n", listmime);
203
param.listname = NULL;
205
fprintf(stderr, "Error: invalid playlist from http_open()!\n");
210
pl.file = fdopen(fd,"r");
213
else if (!(pl.file = fopen(param.listname, "rb")))
215
perror (param.listname);
224
debug("opened ordinary list file");
227
if (param.verbose && pl.file) fprintf (stderr, "Using playlist from %s ...\n", param.listname ? param.listname : "standard input");
228
firstline = 1; /* just opened */
230
/* reading the file line by line */
234
now read a string of arbitrary size...
235
read a chunk, see if lineend, realloc, read another chunk
237
fgets reads at most size-1 bytes and always appends the \0
242
/* have is the length of the string read, without the closing \0 */
243
if(pl.linebuf.size <= have+1)
245
if(!resize_stringbuf(&pl.linebuf, pl.linebuf.size+LINEBUF_STEP))
247
error("cannot increase line buffer");
251
/* I rely on fgets writing the \0 at the end! */
252
if(fgets(pl.linebuf.p+have, pl.linebuf.size-have, pl.file))
254
have += strlen(pl.linebuf.p+have);
255
debug2("have read %lu characters into linebuf: [%s]", (unsigned long)have, pl.linebuf.p);
259
debug("fgets failed to deliver something... file ended?");
262
} while(have && pl.linebuf.p[have-1] != '\r' && pl.linebuf.p[have-1] != '\n');
266
pl.linebuf.p[strcspn(pl.linebuf.p, "\t\n\r")] = '\0';
267
/* a bit of fuzzyness */
270
if(pl.type == UNKNOWN)
272
if(!strcmp("[playlist]", pl.linebuf.p))
274
fprintf(stderr, "Note: detected Shoutcast/Winamp PLS playlist\n");
280
(!strncasecmp("#M3U", pl.linebuf.p ,4))
282
(!strncasecmp("#EXTM3U", pl.linebuf.p ,7))
284
(param.listname != NULL && (strrchr(param.listname, '.')) != NULL && !strcasecmp(".m3u", strrchr(param.listname, '.')))
287
if(param.verbose) fprintf(stderr, "Note: detected M3U playlist type\n");
292
if(param.verbose) fprintf(stderr, "Note: guessed M3U playlist type\n");
300
fprintf(stderr, "Note: Interpreting as ");
303
case M3U: fprintf(stderr, "M3U"); break;
304
case PLS: fprintf(stderr, "PLS (Winamp/Shoutcast)"); break;
305
default: fprintf(stderr, "???");
307
fprintf(stderr, " playlist\n");
313
/* convert \ to / (from MS-like directory format) */
314
for (i=0;pl.linebuf.p[i]!='\0';i++)
316
if (pl.linebuf.p[i] == '\\') pl.linebuf.p[i] = '/';
319
if (pl.linebuf.p[0]=='\0') continue; /* skip empty lines... */
320
if (((pl.type == M3U) && (pl.linebuf.p[0]=='#')))
322
/* a comment line in m3u file */
323
if(param.listentry < 0) printf("%s\n", pl.linebuf.p);
327
/* real filename may start at an offset */
329
/* extract path out of PLS */
332
if(!strncasecmp("File", pl.linebuf.p, 4))
334
/* too lazy to really check for file number... would have to change logic to support unordered file entries anyway */
336
if((in_line = strchr(pl.linebuf.p+4, '=')) != NULL)
342
line_offset = (size_t) (in_line-pl.linebuf.p);
346
fprintf(stderr, "Warning: Invalid PLS line (empty filename) - corrupt playlist file?\n");
352
fprintf(stderr, "Warning: Invalid PLS line (no '=' after 'File') - corrupt playlist file?\n");
358
if(param.listentry < 0) printf("#metainfo %s\n", pl.linebuf.p);
363
/* make paths absolute */
364
/* Windows knows absolute paths with c: in front... should handle this if really supporting win32 again */
368
&& (pl.linebuf.p[line_offset]!='/')
369
&& (pl.linebuf.p[line_offset]!='\\')
370
&& strncmp(pl.linebuf.p+line_offset, "http://", 7)
374
need = pl.dir.size + strlen(pl.linebuf.p+line_offset);
375
if(pl.linebuf.size < need)
377
if(!resize_stringbuf(&pl.linebuf, need))
379
error("unable to enlarge linebuf for appending path! skipping");
383
/* move to have the space at beginning */
384
memmove(pl.linebuf.p+pl.dir.size-1, pl.linebuf.p+line_offset, strlen(pl.linebuf.p+line_offset)+1);
386
memcpy(pl.linebuf.p, pl.dir.p, pl.dir.size-1);
390
if(param.listentry < 0) printf("#entry %lu\n%s\n", (unsigned long)pl.entry,pl.linebuf.p+line_offset);
391
else if((param.listentry == 0) || (param.listentry == pl.entry))
393
add_copy_to_playlist(pl.linebuf.p+line_offset);
401
param.listname = NULL;
408
add_to_playlist(argv[loptind++], 0);
414
void shuffle_playlist()
420
for (loop = 0; loop < pl.fill; loop++)
424
rand gives integer 0 <= RAND_MAX
425
dividing this by (fill-1)*4 and taking the rest gives something 0 <= x < (fill-1)*4
426
now diving x by 4 gives 0 <= y < fill-1
427
then adding 1 if y >= loop index... makes 1 <= z <= fill-1
428
if y not >= loop index that means... what?
430
rannum = (rand() % (fill * 4 - 4)) / 4;
431
rannum += (rannum >= loop);
435
rannum = ( rand() % fill);
437
That directly results in a random number in the allowed range. I'm using this now until someone convinces me of the numerical benefits of the other.
439
rannum = (size_t) rand() % pl.fill;
441
Small test on your binary operation skills (^ is XOR):
446
pl.list[loop] ^= pl.list[rannum];
447
pl.list[rannum] ^= pl.list[loop];
448
pl.list[loop] ^= pl.list[rannum];
450
But since this is not allowed with pointers and any speed gain questionable (well, saving _some_ memory...), doing it the lame way:
452
tmp = pl.list[rannum];
453
pl.list[rannum] = pl.list[loop];
458
if(param.verbose > 1)
461
fprintf(stderr, "\nshuffled playlist:\n");
463
fprintf(stderr, "\n");
467
void print_playlist()
470
for (loop = 0; loop < pl.fill; loop++)
471
fprintf(stderr, "%s\n", pl.list[loop].url);
474
int add_copy_to_playlist(char* new_entry)
477
if((cop = (char*) malloc(strlen(new_entry)+1)) != NULL)
479
strcpy(cop, new_entry);
480
return add_to_playlist(cop, 1);
485
/* add new entry to playlist - no string copy, just the pointer! */
486
int add_to_playlist(char* new_entry, char freeit)
488
if(pl.fill == pl.size)
490
struct listitem* tmp = NULL;
491
/* enlarge the list */
492
tmp = (struct listitem*) realloc(pl.list, (pl.size + pl.alloc_step) * sizeof(struct listitem));
495
error("unable to allocate more memory for playlist");
502
pl.size += pl.alloc_step;
506
if(pl.fill < pl.size)
508
pl.list[pl.fill].freeit = freeit;
509
pl.list[pl.fill].url = new_entry;
514
error("playlist memory still too small?!");