1 /* 2 * Copyright (c) 2014 Tim van der Molen <tim@kariliq.nl> 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 /* For FreeBSD. */ 18 #define _WITH_GETLINE 19 20 #include <pthread.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 25 #include "siren.h" 26 27 static int playlist_search_entry(const void *, const char *); 28 29 static pthread_mutex_t playlist_menu_mtx = PTHREAD_MUTEX_INITIALIZER; 30 static struct format *playlist_altformat; 31 static struct format *playlist_format; 32 static struct menu *playlist_menu; 33 static unsigned int playlist_duration; 34 static char *playlist_file; 35 36 void 37 playlist_activate_entry(void) 38 { 39 struct menu_entry *e; 40 struct track *t; 41 42 XPTHREAD_MUTEX_LOCK(&playlist_menu_mtx); 43 if ((e = menu_get_selected_entry(playlist_menu)) == NULL) 44 t = NULL; 45 else { 46 menu_activate_entry(playlist_menu, e); 47 t = menu_get_entry_data(e); 48 } 49 XPTHREAD_MUTEX_UNLOCK(&playlist_menu_mtx); 50 51 if (t != NULL) { 52 player_set_source(PLAYER_SOURCE_PLAYLIST); 53 player_play_track(t); 54 playlist_print(); 55 } 56 } 57 58 void 59 playlist_copy_entry(enum view_id view) 60 { 61 struct track *t; 62 63 XPTHREAD_MUTEX_LOCK(&playlist_menu_mtx); 64 if ((t = menu_get_selected_entry_data(playlist_menu)) != NULL) 65 view_add_track(view, t); 66 XPTHREAD_MUTEX_UNLOCK(&playlist_menu_mtx); 67 } 68 69 void 70 playlist_end(void) 71 { 72 menu_free(playlist_menu); 73 free(playlist_file); 74 } 75 76 static void 77 playlist_get_entry_text(const void *e, char *buf, size_t bufsize) 78 { 79 const struct track *t; 80 81 t = e; 82 format_track_snprintf(buf, bufsize, playlist_format, 83 playlist_altformat, t); 84 } 85 86 struct track * 87 playlist_get_next_track(void) 88 { 89 struct menu_entry *e; 90 struct track *t; 91 92 XPTHREAD_MUTEX_LOCK(&playlist_menu_mtx); 93 if ((e = menu_get_active_entry(playlist_menu)) == NULL) 94 t = NULL; 95 else { 96 if ((e = menu_get_next_entry(e)) == NULL && 97 option_get_boolean("repeat-all")) 98 e = menu_get_first_entry(playlist_menu); 99 100 if (e == NULL) 101 t = NULL; 102 else { 103 menu_activate_entry(playlist_menu, e); 104 t = menu_get_entry_data(e); 105 } 106 } 107 XPTHREAD_MUTEX_UNLOCK(&playlist_menu_mtx); 108 109 playlist_print(); 110 return t; 111 } 112 113 struct track * 114 playlist_get_prev_track(void) 115 { 116 struct menu_entry *e; 117 struct track *t; 118 119 XPTHREAD_MUTEX_LOCK(&playlist_menu_mtx); 120 if ((e = menu_get_active_entry(playlist_menu)) == NULL) 121 t = NULL; 122 else { 123 if ((e = menu_get_prev_entry(e)) == NULL && 124 option_get_boolean("repeat-all")) 125 e = menu_get_last_entry(playlist_menu); 126 127 if (e == NULL) 128 t = NULL; 129 else { 130 menu_activate_entry(playlist_menu, e); 131 t = menu_get_entry_data(e); 132 } 133 } 134 XPTHREAD_MUTEX_UNLOCK(&playlist_menu_mtx); 135 136 playlist_print(); 137 return t; 138 } 139 140 void 141 playlist_init(void) 142 { 143 playlist_menu = menu_init(NULL, playlist_get_entry_text, 144 playlist_search_entry); 145 } 146 147 void 148 playlist_load(const char *file) 149 { 150 struct track *t; 151 FILE *fp; 152 size_t size; 153 ssize_t len; 154 char *dir, *line, *path, *tmp; 155 156 if ((fp = fopen(file, "r")) == NULL) { 157 LOG_ERR("fopen: %s", file); 158 msg_err("Cannot open playlist: %s", file); 159 return; 160 } 161 162 XPTHREAD_MUTEX_LOCK(&playlist_menu_mtx); 163 164 menu_remove_all_entries(playlist_menu); 165 free(playlist_file); 166 playlist_duration = 0; 167 168 playlist_file = path_normalise(file); 169 dir = path_get_dirname(playlist_file); 170 171 line = NULL; 172 size = 0; 173 while ((len = getline(&line, &size, fp)) != -1) { 174 /* Strip both \n and \r\n EOLs. */ 175 if (len > 0 && line[len - 1] == '\n') { 176 if (len > 1 && line[len - 2] == '\r') 177 line[len - 2] = '\0'; 178 else 179 line[len - 1] = '\0'; 180 } 181 182 if (line[0] == '#' || line[0] == '\0') 183 continue; 184 185 if (line[0] == '/') 186 path = path_normalise(line); 187 else { 188 xasprintf(&tmp, "%s/%s", dir, line); 189 path = path_normalise(tmp); 190 free(tmp); 191 } 192 193 if ((t = track_require(path)) != NULL) { 194 menu_insert_tail(playlist_menu, t); 195 playlist_duration += t->duration; 196 } 197 198 free(path); 199 } 200 201 if (ferror(fp)) { 202 LOG_ERR("getline: %s", file); 203 msg_err("Cannot read playlist: %s", file); 204 } 205 206 free(line); 207 free(dir); 208 209 XPTHREAD_MUTEX_UNLOCK(&playlist_menu_mtx); 210 211 fclose(fp); 212 213 playlist_print(); 214 } 215 216 void 217 playlist_print(void) 218 { 219 if (view_get_id() != VIEW_ID_PLAYLIST) 220 return; 221 222 XPTHREAD_MUTEX_LOCK(&playlist_menu_mtx); 223 screen_view_title_printf("Playlist: %s (%u track%s, %u:%02u:%02u)", 224 playlist_file != NULL ? playlist_file : "None", 225 menu_get_nentries(playlist_menu), 226 menu_get_nentries(playlist_menu) == 1 ? "" : "s", 227 HOURS(playlist_duration), 228 HMINS(playlist_duration), 229 MSECS(playlist_duration)); 230 option_lock(); 231 playlist_format = option_get_format("playlist-format"); 232 playlist_altformat = option_get_format("playlist-format-alt"); 233 menu_print(playlist_menu); 234 option_unlock(); 235 XPTHREAD_MUTEX_UNLOCK(&playlist_menu_mtx); 236 } 237 238 void 239 playlist_scroll_down(enum menu_scroll scroll) 240 { 241 XPTHREAD_MUTEX_LOCK(&playlist_menu_mtx); 242 menu_scroll_down(playlist_menu, scroll); 243 XPTHREAD_MUTEX_UNLOCK(&playlist_menu_mtx); 244 playlist_print(); 245 } 246 247 void 248 playlist_scroll_up(enum menu_scroll scroll) 249 { 250 XPTHREAD_MUTEX_LOCK(&playlist_menu_mtx); 251 menu_scroll_up(playlist_menu, scroll); 252 XPTHREAD_MUTEX_UNLOCK(&playlist_menu_mtx); 253 playlist_print(); 254 } 255 256 static int 257 playlist_search_entry(const void *e, const char *search) 258 { 259 const struct track *t; 260 261 t = e; 262 return track_search(t, search); 263 } 264 265 void 266 playlist_search_next(const char *search) 267 { 268 XPTHREAD_MUTEX_LOCK(&playlist_menu_mtx); 269 menu_search_next(playlist_menu, search); 270 XPTHREAD_MUTEX_UNLOCK(&playlist_menu_mtx); 271 playlist_print(); 272 } 273 274 void 275 playlist_search_prev(const char *search) 276 { 277 XPTHREAD_MUTEX_LOCK(&playlist_menu_mtx); 278 menu_search_prev(playlist_menu, search); 279 XPTHREAD_MUTEX_UNLOCK(&playlist_menu_mtx); 280 playlist_print(); 281 } 282 283 void 284 playlist_select_active_entry(void) 285 { 286 XPTHREAD_MUTEX_LOCK(&playlist_menu_mtx); 287 menu_select_active_entry(playlist_menu); 288 XPTHREAD_MUTEX_UNLOCK(&playlist_menu_mtx); 289 playlist_print(); 290 } 291 292 void 293 playlist_select_first_entry(void) 294 { 295 XPTHREAD_MUTEX_LOCK(&playlist_menu_mtx); 296 menu_select_first_entry(playlist_menu); 297 XPTHREAD_MUTEX_UNLOCK(&playlist_menu_mtx); 298 playlist_print(); 299 } 300 301 void 302 playlist_select_last_entry(void) 303 { 304 XPTHREAD_MUTEX_LOCK(&playlist_menu_mtx); 305 menu_select_last_entry(playlist_menu); 306 XPTHREAD_MUTEX_UNLOCK(&playlist_menu_mtx); 307 playlist_print(); 308 } 309 310 void 311 playlist_select_next_entry(void) 312 { 313 XPTHREAD_MUTEX_LOCK(&playlist_menu_mtx); 314 menu_select_next_entry(playlist_menu); 315 XPTHREAD_MUTEX_UNLOCK(&playlist_menu_mtx); 316 playlist_print(); 317 } 318 319 void 320 playlist_select_prev_entry(void) 321 { 322 XPTHREAD_MUTEX_LOCK(&playlist_menu_mtx); 323 menu_select_prev_entry(playlist_menu); 324 XPTHREAD_MUTEX_UNLOCK(&playlist_menu_mtx); 325 playlist_print(); 326 } 327 328 /* Recalculate the duration. */ 329 void 330 playlist_update(void) 331 { 332 struct menu_entry *e; 333 struct track *t; 334 335 XPTHREAD_MUTEX_LOCK(&playlist_menu_mtx); 336 playlist_duration = 0; 337 MENU_FOR_EACH_ENTRY(playlist_menu, e) { 338 t = menu_get_entry_data(e); 339 playlist_duration += t->duration; 340 } 341 XPTHREAD_MUTEX_UNLOCK(&playlist_menu_mtx); 342 } 343