fix playlist closing
[omnplay] / src / library.c
1 /*
2 * playlist.c -- GTK+ 2 omnplay
3 * Copyright (C) 2011 Maksym Veremeyenko <verem@m1stereo.tv>
4 *
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.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
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 Foundation,
17 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #ifndef _GNU_SOURCE
25 #define _GNU_SOURCE
26 #endif
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <gtk/gtk.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <pthread.h>
34 #include <string.h>
35
36 #include "omnplay.h"
37 #include "ui.h"
38 #include "timecode.h"
39
40 playlist_item_t* omnplay_library_find(omnplay_instance_t* app, char* id)
41 {
42 int i;
43 playlist_item_t* item = NULL;
44
45 pthread_mutex_lock(&app->library.lock);
46
47 for(i = 0; i < app->library.count && !item; i++)
48 if(!strcasecmp(id, app->library.item[i].id))
49 item = &app->library.item[i];
50
51 pthread_mutex_unlock(&app->library.lock);
52
53 return item;
54 };
55
56 int omnplay_library_normalize_item(omnplay_instance_t* app, playlist_item_t* item)
57 {
58 int r = 0;
59 playlist_item_t* lib;
60 playlist_item_t prev;
61
62 pthread_mutex_lock(&app->library.lock);
63
64 prev = *item;
65
66 lib = omnplay_library_find(app, item->id);
67
68 item->error = 0;
69
70 if(lib)
71 {
72 if(!item->title[0])
73 {
74 strcpy(item->title, lib->title);
75 g_warning("%s: [%s] title=[%s]->[%s]", __FUNCTION__, item->id, prev.title, item->title);
76 r++;
77 };
78
79 if(item->in < lib->in || item->in >= (lib->in + lib->dur))
80 {
81 g_warning("%s: [%s] (item->in=[%d] < lib->in=[%d] || item->in=[%d] >= (lib->in=[%d] + lib->dur=[%d]))",
82 __FUNCTION__, item->id,
83 item->in, lib->in, item->in, lib->in, lib->dur);
84
85 item->in = lib->in;
86
87 g_warning("%s: [%s] in=[%d]->[%d]", __FUNCTION__, item->id, prev.in, item->in);
88
89 r++;
90 };
91
92 if(!item->dur || (item->in + item->dur) > (lib->in + lib->dur))
93 {
94 g_warning("%s: [%s] (!item->dur=[%d] || (item->in=[%d] + item->dur=[%d]) > (lib->in=[%d] + lib->dur=[%d])",
95 __FUNCTION__, item->id,
96 item->dur, item->in, item->dur, lib->in, lib->dur);
97
98 item->dur = lib->in + lib->dur - item->in;
99
100 g_warning("%s: [%s] dur=[%d]->[%d]", __FUNCTION__, item->id, prev.dur, item->dur);
101
102 r++;
103 };
104 }
105 else
106 {
107 r = 1;
108 item->error = PLAYLIST_ITEM_ERROR_LIB;
109 };
110
111 pthread_mutex_unlock(&app->library.lock);
112
113 return r;
114 };
115
116 int omnplay_library_relink_item(omnplay_instance_t* app, playlist_item_t* item)
117 {
118 int r = 0;
119 playlist_item_t* lib;
120
121 pthread_mutex_lock(&app->library.lock);
122
123 lib = omnplay_library_find(app, item->id);
124
125 item->error = 0;
126
127 if(lib)
128 {
129 r = 1;
130 strcpy(item->title, lib->title);
131 item->dur = lib->dur;
132 item->in = lib->in;
133 }
134 else
135 {
136 r = 1;
137 item->error = PLAYLIST_ITEM_ERROR_LIB;
138 };
139
140 pthread_mutex_unlock(&app->library.lock);
141
142 return r;
143 };
144
145 void omnplay_library_sort(omnplay_instance_t* app)
146 {
147 int i, j, m;
148 playlist_item_t item;
149
150 for(i = 0; i < app->library.count; i++)
151 {
152 /* find max */
153 for(j = i + 1, m = i; j < app->library.count; j++)
154 if(strcasecmp(app->library.item[j].id, app->library.item[m].id) < 0)
155 m = j;
156
157 if(m != i)
158 {
159 item = app->library.item[i];
160 app->library.item[i] = app->library.item[m];
161 app->library.item[m] = item;
162 };
163 };
164 };
165
166 int omnplay_library_load_file(playlist_item_t* items, int *pcount, char* filename)
167 {
168 int i, c = 0, r = 0;
169 FILE* f;
170 char *l;
171 int limit = *pcount;
172 playlist_item_t item;
173
174 /* allocate space for strings and items */
175 l = malloc(PATH_MAX);
176
177 *pcount = 0;
178
179 /* open and process file */
180 if((f = fopen(filename, "rt")))
181 {
182 while(!feof(f) && c < limit)
183 {
184 char *s, *sp_r, *sp_b;
185
186 /* load string */
187 memset(l, 0, PATH_MAX);
188 fgets(l, PATH_MAX, f);
189
190 /* remove newlines */
191 if( (s = strchr(l, '\n')) ) *s = 0;
192 if( (s = strchr(l, '\r')) ) *s = 0;
193
194 /* check for empty line */
195 if(l[0] && l[0] != '#' && l[0] != '|')
196 {
197 memset(&item, 0, sizeof(playlist_item_t));
198
199 g_warning("%s: [%s]", __FUNCTION__, l);
200
201 for(i = 0, sp_b = l; (NULL != (sp_r = strtok(sp_b, "\t"))); i++, sp_b = NULL)
202 {
203 g_warning("%s: [%d]=[%s]", __FUNCTION__, i, sp_r);
204
205 switch(i)
206 {
207 case 0: strncpy(item.id, sp_r, PATH_MAX); break;
208 case 1: tc2frames(sp_r, 25.0, &item.in); break;
209 case 2: tc2frames(sp_r, 25.0, &item.dur); break;
210 case 3: strncpy(item.title, sp_r, PATH_MAX); break;
211 };
212 };
213
214 /* insert item */
215 items[c++] = item;
216 g_warning("%s: id=[%s], in=[%d], dur=[%d]", __FUNCTION__, item.id, item.in, item.dur);
217 }
218 else
219 g_warning("omnplay_library_load_file: ignored line [%s]\n", l);
220 }
221
222 fclose(f);
223 }
224 else
225 r = -1;
226
227 /* free data */
228 free(l);
229
230 *pcount = c;
231
232 g_warning("omnplay_library_load_file: loaded [%d] items from [%s] file, limit [%d]\n", c, filename, limit);
233
234 return r;
235 };
236
237 void omnplay_library_load(omnplay_instance_t* app)
238 {
239 pthread_mutex_lock(&app->library.lock);
240
241 if(app->library.filename[0])
242 {
243 app->library.count = MAX_LIBRARY_ITEMS;
244 omnplay_library_load_file(app->library.item, &app->library.count, app->library.filename);
245 };
246
247 omnplay_library_sort(app);
248
249 pthread_mutex_unlock(&app->library.lock);
250
251 omnplay_library_draw(app);
252 };
253
254 static void omnplay_library_save_file(playlist_item_t* item, int count, char* filename)
255 {
256 int i;
257 FILE* f;
258
259 if((f = fopen(filename, "wt")))
260 {
261 char tc_in[32], tc_dur[32];
262
263 for(i = 0; i < count; i++)
264 fprintf(f, "%s\t%s\t%s\t%s\n",
265 item[i].id,
266 frames2tc(item[i].in, 25.0, tc_in),
267 frames2tc(item[i].dur, 25.0, tc_dur),
268 item[i].title);
269 fclose(f);
270 g_warning("omnplay_library_save_file: written [%d] lines to file [%s]\n", count, filename);
271 };
272 };
273
274 void omnplay_library_save(omnplay_instance_t* app)
275 {
276 pthread_mutex_lock(&app->library.lock);
277
278 if(app->library.filename[0])
279 omnplay_library_save_file(app->library.item, app->library.count,
280 app->library.filename);
281
282 pthread_mutex_unlock(&app->library.lock);
283 };
284
285 static void omnplay_get_content_cb(omnplay_instance_t* app, playlist_item_t* item, void* data)
286 {
287 if(!(app->library.id_display_idx % app->library.id_display_rate))
288 omnplay_set_status(app, item->id);
289 app->library.id_display_idx++;
290 };
291
292 static void* omnplay_library_refresh_proc(void* data)
293 {
294 omnplay_instance_t* app = (omnplay_instance_t*)data;
295 int count, i;
296 playlist_item_t* items;
297
298 gdk_threads_enter();
299 gtk_widget_set_sensitive(app->window, FALSE);
300 gdk_flush();
301 gdk_threads_leave();
302
303 omnplay_set_status(app, "Updating library...");
304
305 items = (playlist_item_t*)malloc(sizeof(playlist_item_t) * MAX_LIBRARY_ITEMS);
306
307 count = omnplay_get_content(app, items, MAX_LIBRARY_ITEMS, omnplay_get_content_cb, NULL);
308
309 if(count > 0)
310 {
311 omnplay_set_status(app, "Quering whois...");
312
313 if(app->library.whois[0])
314 omnplay_whois_list(app, items, &count);
315
316 omnplay_set_status(app, "Setting library...");
317
318 pthread_mutex_lock(&app->library.lock);
319
320 for(i = 0; i < count; i++)
321 app->library.item[i] = items[i];
322
323 app->library.count = count;
324
325 omnplay_library_sort(app);
326
327 pthread_mutex_unlock(&app->library.lock);
328
329 gdk_threads_enter();
330 omnplay_library_draw(app);
331 gdk_flush();
332 gdk_threads_leave();
333 };
334
335 omnplay_set_status(app, "Normalizing playlist...");
336
337 free(items);
338
339 gdk_threads_enter();
340 omnplay_playlist_normalize(app);
341 gdk_flush();
342 gdk_threads_leave();
343
344 omnplay_set_status(app, "");
345
346 gdk_threads_enter();
347 gtk_widget_set_sensitive(app->window, TRUE);
348 gdk_flush();
349 gdk_threads_leave();
350
351
352 return NULL;
353 };
354
355 void omnplay_library_refresh(omnplay_instance_t* app)
356 {
357 if(app->library.refresh_thread)
358 g_thread_join(app->library.refresh_thread);
359
360 app->library.refresh_thread = g_thread_create(
361 omnplay_library_refresh_proc, app, TRUE, NULL);
362 };
363
364 void omnplay_library_draw(omnplay_instance_t* app)
365 {
366 int i;
367 char tc[12];
368 GtkListStore *list_store;
369 GtkTreeIter iter;
370
371 list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(app->library_grid)));
372 gtk_list_store_clear(list_store);
373
374 pthread_mutex_lock(&app->library.lock);
375
376 for(i = 0;i < app->library.count; i++)
377 {
378 gtk_list_store_append(list_store, &iter);
379
380 gtk_list_store_set(list_store, &iter,
381 0, app->library.item[i].id,
382 1, frames2tc(app->library.item[i].dur, 25.0, tc),
383 2, app->library.item[i].title,
384 3, i,
385 4, FALSE,
386 5, "red",
387 -1 );
388 }
389
390 pthread_mutex_unlock(&app->library.lock);
391 };
392
393 static void get_selected_idx_library_proc(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
394 {
395 int idx, *list = (int*)data;
396 gtk_tree_model_get(model, iter, 3, &idx, -1);
397 list[list[0] + 1] = idx;
398 list[0] = list[0] + 1;
399 };
400
401 static int* get_selected_idx_library(omnplay_instance_t* app)
402 {
403 int* list = NULL;
404 GtkTreeSelection *selection;
405
406 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(app->library_grid));
407 if(selection)
408 {
409 list = (int*)malloc(sizeof(int) * (MAX_LIBRARY_ITEMS + 1));
410 memset(list, 0, sizeof(int) * (MAX_LIBRARY_ITEMS + 1));
411
412 gtk_tree_selection_selected_foreach(
413 selection,
414 get_selected_idx_library_proc,
415 list);
416
417 if(!list[0])
418 {
419 free(list);
420 list = NULL;
421 };
422 };
423
424 return list;
425 };
426
427
428 playlist_item_t* omnplay_library_get_selected(omnplay_instance_t* app, int *count)
429 {
430 int* idxs;
431 playlist_item_t* items = NULL;
432
433 pthread_mutex_lock(&app->library.lock);
434
435 *count = 0;
436
437 idxs = get_selected_idx_library(app);
438
439 if(idxs)
440 {
441 int i;
442
443 /* alloc items */
444 items = (playlist_item_t*)malloc(sizeof(playlist_item_t) * (idxs[0] + 1));
445
446 /* clear last item */
447 memset(&items[idxs[0]], 0, sizeof(playlist_item_t));
448
449 /* copy items */
450 for(i = 0; i < idxs[0]; i++)
451 items[i] = app->library.item[idxs[i + 1]];
452
453 *count = idxs[0];
454 free(idxs);
455 };
456
457 pthread_mutex_unlock(&app->library.lock);
458
459 return items;
460 };
461
462 void omnplay_library_search(omnplay_instance_t* app, int next)
463 {
464 int idx = 0, i;
465 int* idxs;
466 const char *search;
467 GtkTreePath* path;
468
469 pthread_mutex_lock(&app->library.lock);
470
471 idxs = get_selected_idx_library(app);
472 if(idxs) idx = idxs[1];
473 free(idxs);
474
475 if(!next) idx = 0;
476 else idx++;
477
478 search = gtk_entry_get_text(GTK_ENTRY(app->library.search));
479
480 if(search[0])
481 {
482 for(i = idx; i < app->library.count; i++)
483 if( strcasestr(app->library.item[i].id, search) ||
484 strcasestr(app->library.item[i].title, search))
485 break;
486
487 if(i < app->library.count)
488 {
489 g_warning("found at pos=%d\n", i);
490
491 /* select */
492 path = gtk_tree_path_new_from_indices(i, -1);
493 gtk_tree_selection_select_path(gtk_tree_view_get_selection(
494 GTK_TREE_VIEW(app->library_grid)), path);
495 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app->library_grid), path, NULL, FALSE);
496 gtk_tree_path_free(path);
497 };
498 };
499
500 pthread_mutex_unlock(&app->library.lock);
501 };