Handle streams separately in tree_add_track()
[cmus.git] / cmus.c
bloba8e6d1c56ff1fb03d9efa8f328f9bfe1b1ac5c6f
1 #include "cmus.h"
2 #include "job.h"
3 #include "lib.h"
4 #include "pl.h"
5 #include "player.h"
6 #include "input.h"
7 #include "play_queue.h"
8 #include "worker.h"
9 #include "cache.h"
10 #include "misc.h"
11 #include "file.h"
12 #include "utils.h"
13 #include "path.h"
14 #include "options.h"
15 #include "xmalloc.h"
16 #include "xstrjoin.h"
17 #include "debug.h"
18 #include "load_dir.h"
19 #include "ui_curses.h"
20 #include "cache.h"
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <dirent.h>
27 #include <stdlib.h>
28 #include <ctype.h>
30 static char **playable_exts;
31 static const char * const playlist_exts[] = { "m3u", "pl", "pls", NULL };
33 int cmus_init(void)
35 playable_exts = ip_get_supported_extensions();
36 cache_init();
37 worker_init();
38 play_queue_init();
39 return 0;
42 void cmus_exit(void)
44 worker_remove_jobs(JOB_TYPE_ANY);
45 worker_exit();
46 if (cache_close())
47 d_print("error: %s\n", strerror(errno));
50 void cmus_next(void)
52 struct track_info *info;
54 editable_lock();
55 info = play_queue_remove();
56 if (info == NULL) {
57 if (play_library) {
58 info = lib_set_next();
59 } else {
60 info = pl_set_next();
63 editable_unlock();
65 if (info)
66 player_set_file(info);
69 void cmus_prev(void)
71 struct track_info *info;
73 editable_lock();
74 if (play_library) {
75 info = lib_set_prev();
76 } else {
77 info = pl_set_prev();
79 editable_unlock();
81 if (info)
82 player_set_file(info);
85 void cmus_play_file(const char *filename)
87 struct track_info *ti;
89 if (is_url(filename)) {
90 ti = track_info_url_new(filename);
91 } else {
92 cache_lock();
93 ti = cache_get_ti(filename);
94 cache_unlock();
95 if (!ti) {
96 error_msg("Couldn't get file information for %s\n", filename);
97 return;
100 player_play_file(ti);
103 enum file_type cmus_detect_ft(const char *name, char **ret)
105 char *absolute;
106 struct stat st;
108 if (is_url(name)) {
109 *ret = xstrdup(name);
110 return FILE_TYPE_URL;
113 *ret = NULL;
114 absolute = path_absolute(name);
115 if (absolute == NULL)
116 return FILE_TYPE_INVALID;
118 /* stat follows symlinks, lstat does not */
119 if (stat(absolute, &st) == -1) {
120 free(absolute);
121 return FILE_TYPE_INVALID;
124 if (S_ISDIR(st.st_mode)) {
125 *ret = absolute;
126 return FILE_TYPE_DIR;
128 if (!S_ISREG(st.st_mode)) {
129 free(absolute);
130 errno = EINVAL;
131 return FILE_TYPE_INVALID;
134 *ret = absolute;
135 if (cmus_is_playlist(absolute))
136 return FILE_TYPE_PL;
138 /* NOTE: it could be FILE_TYPE_PL too! */
139 return FILE_TYPE_FILE;
142 void cmus_add(add_ti_cb add, const char *name, enum file_type ft, int jt)
144 struct add_data *data = xnew(struct add_data, 1);
146 data->add = add;
147 data->name = xstrdup(name);
148 data->type = ft;
149 worker_add_job(jt, do_add_job, free_add_job, data);
152 static int save_playlist_cb(void *data, struct track_info *ti)
154 int fd = *(int *)data;
155 const char nl = '\n';
156 int rc;
158 rc = write_all(fd, ti->filename, strlen(ti->filename));
159 if (rc == -1)
160 return -1;
161 rc = write_all(fd, &nl, 1);
162 if (rc == -1)
163 return -1;
164 return 0;
167 int cmus_save(for_each_ti_cb for_each_ti, const char *filename)
169 int fd, rc;
171 fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
172 if (fd == -1)
173 return -1;
174 rc = for_each_ti(save_playlist_cb, &fd);
175 close(fd);
176 return rc;
179 static int update_cb(void *data, struct track_info *ti)
181 struct update_data *d = data;
183 if (is_url(ti->filename))
184 return 0;
186 if (d->size == d->used) {
187 if (d->size == 0)
188 d->size = 16;
189 d->size *= 2;
190 d->ti = xrealloc(d->ti, d->size * sizeof(struct track_info *));
192 track_info_ref(ti);
193 d->ti[d->used++] = ti;
194 return 0;
197 void cmus_update_cache(void)
199 worker_add_job(JOB_TYPE_LIB, do_update_cache_job, free_update_cache_job, NULL);
202 void cmus_update_lib(void)
204 struct update_data *data;
206 data = xnew(struct update_data, 1);
207 data->size = 0;
208 data->used = 0;
209 data->ti = NULL;
211 editable_lock();
212 lib_for_each(update_cb, data);
213 editable_unlock();
215 worker_add_job(JOB_TYPE_LIB, do_update_job, free_update_job, data);
218 void cmus_update_tis(struct track_info **tis, int nr)
220 struct update_data *data;
222 data = xnew(struct update_data, 1);
223 data->size = nr;
224 data->used = nr;
225 data->ti = tis;
226 worker_add_job(JOB_TYPE_LIB, do_update_job, free_update_job, data);
229 static const char *get_ext(const char *filename)
231 const char *ext = strrchr(filename, '.');
233 if (ext)
234 ext++;
235 return ext;
238 static int str_in_array(const char *str, const char * const *array)
240 int i;
242 for (i = 0; array[i]; i++) {
243 if (strcasecmp(str, array[i]) == 0)
244 return 1;
246 return 0;
249 int cmus_is_playlist(const char *filename)
251 const char *ext = get_ext(filename);
253 return ext && str_in_array(ext, playlist_exts);
256 int cmus_is_playable(const char *filename)
258 const char *ext = get_ext(filename);
260 return ext && str_in_array(ext, (const char * const *)playable_exts);
263 int cmus_is_supported(const char *filename)
265 const char *ext = get_ext(filename);
267 return ext && (str_in_array(ext, (const char * const *)playable_exts) ||
268 str_in_array(ext, playlist_exts));
271 struct pl_data {
272 int (*cb)(void *data, const char *line);
273 void *data;
276 static int pl_handle_line(void *data, const char *line)
278 struct pl_data *d = data;
279 int i = 0;
281 while (isspace(line[i]))
282 i++;
283 if (line[i] == 0)
284 return 0;
286 if (line[i] == '#')
287 return 0;
289 return d->cb(d->data, line);
292 static int pls_handle_line(void *data, const char *line)
294 struct pl_data *d = data;
296 if (strncasecmp(line, "file", 4))
297 return 0;
298 line = strchr(line, '=');
299 if (line == NULL)
300 return 0;
301 return d->cb(d->data, line + 1);
304 int cmus_playlist_for_each(const char *buf, int size, int reverse,
305 int (*cb)(void *data, const char *line),
306 void *data)
308 struct pl_data d = { cb, data };
309 int (*handler)(void *, const char *);
311 handler = pl_handle_line;
312 if (size >= 10 && strncasecmp(buf, "[playlist]", 10) == 0)
313 handler = pls_handle_line;
315 if (reverse) {
316 buffer_for_each_line_reverse(buf, size, handler, &d);
317 } else {
318 buffer_for_each_line(buf, size, handler, &d);
320 return 0;