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