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