6 #include <play_queue.h>
18 #include <sys/types.h>
26 static pthread_mutex_t track_db_mutex
= CMUS_MUTEX_INITIALIZER
;
27 static struct track_db
*track_db
;
28 static char **playable_exts
;
29 static const char * const playlist_exts
[] = { "m3u", "pl", "pls", NULL
};
31 #define track_db_lock() cmus_mutex_lock(&track_db_mutex)
32 #define track_db_unlock() cmus_mutex_unlock(&track_db_mutex)
34 /* add (worker job) {{{ */
42 static struct track_info
*track_info_url_new(const char *url
)
44 struct track_info
*ti
= track_info_new(url
);
45 ti
->comments
= xnew0(struct keyval
, 1);
51 static void add_url(add_ti_cb add
, const char *filename
)
53 struct track_info
*ti
;
55 ti
= track_info_url_new(filename
);
62 /* add file to the playlist
64 * @filename: absolute filename with extraneous slashes stripped
66 static void add_file(add_ti_cb add
, const char *filename
)
68 struct track_info
*ti
;
71 ti
= track_db_get_track(track_db
, filename
);
83 static char *fullname(const char *path
, const char *name
)
90 if (path
[l1
- 1] == '/')
92 full
= xnew(char, l1
+ 1 + l2
+ 1);
93 memcpy(full
, path
, l1
);
95 memcpy(full
+ l1
+ 1, name
, l2
+ 1);
99 static int dir_filter(const struct dirent
*d
)
101 if (d
->d_name
[0] == '.')
106 static void add_dir(add_ti_cb add
, const char *dirname
);
108 static void handle_dentry(add_ti_cb add
, const char *dirname
, const char *name
)
113 full
= fullname(dirname
, name
);
114 /* stat follows symlinks, lstat does not */
115 if (stat(full
, &s
) == 0) {
116 if (S_ISDIR(s
.st_mode
)) {
125 static void add_dir(add_ti_cb add
, const char *dirname
)
127 struct dirent
**dentries
;
130 num
= scandir(dirname
, &dentries
, dir_filter
, alphasort
);
132 d_print("error: scandir: %s\n", strerror(errno
));
135 if (add
== play_queue_prepend
) {
136 for (i
= num
- 1; i
>= 0; i
--) {
137 if (!worker_cancelling())
138 handle_dentry(add
, dirname
, dentries
[i
]->d_name
);
142 for (i
= 0; i
< num
; i
++) {
143 if (!worker_cancelling())
144 handle_dentry(add
, dirname
, dentries
[i
]->d_name
);
151 static int handle_line(void *data
, const char *line
)
153 add_ti_cb add
= data
;
155 if (worker_cancelling())
166 static void add_pl(add_ti_cb add
, const char *filename
)
171 buf
= mmap_file(filename
, &size
);
177 reverse
= add
== play_queue_prepend
;
179 cmus_playlist_for_each(buf
, size
, reverse
, handle_line
, add
);
184 static void do_add_job(void *data
)
186 struct add_data
*jd
= data
;
190 add_url(jd
->add
, jd
->name
);
193 add_pl(jd
->add
, jd
->name
);
196 add_dir(jd
->add
, jd
->name
);
199 add_file(jd
->add
, jd
->name
);
201 case FILE_TYPE_INVALID
:
206 static void free_add_job(void *data
)
208 struct add_data
*jd
= data
;
216 /* update (worker job) {{{ */
221 struct track_info
**ti
;
224 static void do_update_job(void *data
)
226 struct update_data
*d
= data
;
229 for (i
= 0; i
< d
->used
; i
++) {
230 struct track_info
*ti
= d
->ti
[i
];
233 /* stat follows symlinks, lstat does not */
234 if (stat(ti
->filename
, &s
) == -1) {
235 d_print("removing dead file %s\n", ti
->filename
);
239 } else if (ti
->mtime
!= s
.st_mtime
) {
240 d_print("mtime changed: %s\n", ti
->filename
);
245 cmus_add(lib_add_track
, ti
->filename
, FILE_TYPE_FILE
, JOB_TYPE_LIB
);
247 track_info_unref(ti
);
251 static void free_update_job(void *data
)
253 struct update_data
*d
= data
;
263 char *db_filename_base
;
265 playable_exts
= ip_get_supported_extensions();
267 db_filename_base
= xstrjoin(cmus_config_dir
, "/trackdb");
268 track_db
= track_db_new(db_filename_base
);
269 free(db_filename_base
);
279 worker_remove_jobs(JOB_TYPE_ANY
);
281 if (track_db_close(track_db
))
282 d_print("error: %s\n", strerror(errno
));
287 struct track_info
*info
;
290 info
= play_queue_remove();
293 info
= lib_set_next();
295 info
= pl_set_next();
301 player_set_file(info
->filename
);
302 track_info_unref(info
);
308 struct track_info
*info
;
312 info
= lib_set_prev();
314 info
= pl_set_prev();
319 player_set_file(info
->filename
);
320 track_info_unref(info
);
324 void cmus_play_file(const char *filename
)
326 player_play_file(filename
);
329 enum file_type
cmus_detect_ft(const char *name
, char **ret
)
335 *ret
= xstrdup(name
);
336 return FILE_TYPE_URL
;
340 absolute
= path_absolute(name
);
341 if (absolute
== NULL
)
342 return FILE_TYPE_INVALID
;
344 /* stat follows symlinks, lstat does not */
345 if (stat(absolute
, &st
) == -1) {
347 return FILE_TYPE_INVALID
;
350 if (S_ISDIR(st
.st_mode
)) {
352 return FILE_TYPE_DIR
;
354 if (!S_ISREG(st
.st_mode
)) {
357 return FILE_TYPE_INVALID
;
361 if (cmus_is_playlist(absolute
))
364 /* NOTE: it could be FILE_TYPE_PL too! */
365 return FILE_TYPE_FILE
;
368 void cmus_add(add_ti_cb add
, const char *name
, enum file_type ft
, int jt
)
370 struct add_data
*data
= xnew(struct add_data
, 1);
373 data
->name
= xstrdup(name
);
375 worker_add_job(jt
, do_add_job
, free_add_job
, data
);
378 static int save_playlist_cb(void *data
, struct track_info
*ti
)
380 int fd
= *(int *)data
;
381 const char nl
= '\n';
384 rc
= write_all(fd
, ti
->filename
, strlen(ti
->filename
));
387 rc
= write_all(fd
, &nl
, 1);
393 int cmus_save(for_each_ti_cb for_each_ti
, const char *filename
)
397 fd
= open(filename
, O_CREAT
| O_WRONLY
| O_TRUNC
, 0666);
400 rc
= for_each_ti(save_playlist_cb
, &fd
);
405 static int update_cb(void *data
, struct track_info
*ti
)
407 struct update_data
*d
= data
;
409 if (is_url(ti
->filename
))
412 if (d
->size
== d
->used
) {
416 d
->ti
= xrealloc(d
->ti
, d
->size
* sizeof(struct track_info
*));
419 d
->ti
[d
->used
++] = ti
;
423 void cmus_update_lib(void)
425 struct update_data
*data
;
427 data
= xnew(struct update_data
, 1);
433 lib_for_each(update_cb
, data
);
436 worker_add_job(JOB_TYPE_LIB
, do_update_job
, free_update_job
, data
);
439 void cmus_update_tis(struct track_info
**tis
, int nr
)
441 struct update_data
*data
;
443 data
= xnew(struct update_data
, 1);
447 worker_add_job(JOB_TYPE_LIB
, do_update_job
, free_update_job
, data
);
450 struct track_info
*cmus_get_track_info(const char *name
)
452 struct track_info
*ti
;
455 return track_info_url_new(name
);
458 ti
= track_db_get_track(track_db
, name
);
463 static const char *get_ext(const char *filename
)
465 const char *ext
= strrchr(filename
, '.');
472 static int str_in_array(const char *str
, const char * const * array
)
476 for (i
= 0; array
[i
]; i
++) {
477 if (strcasecmp(str
, array
[i
]) == 0)
483 int cmus_is_playlist(const char *filename
)
485 const char *ext
= get_ext(filename
);
487 return ext
&& str_in_array(ext
, playlist_exts
);
490 int cmus_is_playable(const char *filename
)
492 const char *ext
= get_ext(filename
);
494 return ext
&& str_in_array(ext
, (const char * const *)playable_exts
);
497 int cmus_is_supported(const char *filename
)
499 const char *ext
= get_ext(filename
);
501 return ext
&& (str_in_array(ext
, (const char * const *)playable_exts
) ||
502 str_in_array(ext
, playlist_exts
));
506 int (*cb
)(void *data
, const char *line
);
510 static int pl_handle_line(void *data
, const char *line
)
512 struct pl_data
*d
= data
;
515 while (isspace(line
[i
]))
523 return d
->cb(d
->data
, line
);
526 static int pls_handle_line(void *data
, const char *line
)
528 struct pl_data
*d
= data
;
530 if (strncasecmp(line
, "file", 4))
532 line
= strchr(line
, '=');
535 return d
->cb(d
->data
, line
+ 1);
538 int cmus_playlist_for_each(const char *buf
, int size
, int reverse
,
539 int (*cb
)(void *data
, const char *line
),
542 struct pl_data d
= { cb
, data
};
543 int (*handler
)(void *, const char *);
545 handler
= pl_handle_line
;
546 if (size
>= 10 && strncasecmp(buf
, "[playlist]", 10) == 0)
547 handler
= pls_handle_line
;
550 buffer_for_each_line_reverse(buf
, size
, handler
, &d
);
552 buffer_for_each_line(buf
, size
, handler
, &d
);