diff options
-rw-r--r-- | callbacks.c | 74 | ||||
-rw-r--r-- | callbacks.h | 12 | ||||
-rw-r--r-- | cdumb.c | 3 | ||||
-rw-r--r-- | cflac.c | 3 | ||||
-rw-r--r-- | cgme.c | 3 | ||||
-rw-r--r-- | cmp3.c | 3 | ||||
-rw-r--r-- | codec.h | 1 | ||||
-rw-r--r-- | csid.cpp | 3 | ||||
-rw-r--r-- | cvorbis.c | 3 | ||||
-rw-r--r-- | deadbeef.glade | 39 | ||||
-rw-r--r-- | gtkplaylist.c | 46 | ||||
-rw-r--r-- | gtkplaylist.h | 2 | ||||
-rw-r--r-- | interface.c | 33 | ||||
-rw-r--r-- | playlist.c | 307 | ||||
-rw-r--r-- | playlist.h | 8 |
15 files changed, 460 insertions, 80 deletions
diff --git a/callbacks.c b/callbacks.c index c4d98b9c..830ef250 100644 --- a/callbacks.c +++ b/callbacks.c @@ -64,7 +64,7 @@ main_playlist_init (GtkWidget *widget) { main_playlist.header = lookup_widget (mainwin, "header"); main_playlist.scrollbar = lookup_widget (mainwin, "playscroll"); main_playlist.pcurr = &playlist_current_ptr; - main_playlist.count = &pl_count; + main_playlist.pcount = &pl_count; main_playlist.iterator = PL_MAIN; main_playlist.multisel = 1; main_playlist.scrollpos = 0; @@ -94,7 +94,7 @@ search_playlist_init (GtkWidget *widget) { assert (search_playlist.header); assert (search_playlist.scrollbar); // main_playlist.pcurr = &search_current; - search_playlist.count = &search_count; + search_playlist.pcount = &search_count; search_playlist.multisel = 0; search_playlist.iterator = PL_SEARCH; search_playlist.scrollpos = 0; @@ -691,3 +691,73 @@ on_searchlist_realize (GtkWidget *widget, + +void +on_playlist_load_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + GtkWidget *dlg = gtk_file_chooser_dialog_new ("Load Playlist", GTK_WINDOW (mainwin), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL); + + GtkFileFilter* flt; + flt = gtk_file_filter_new (); + gtk_file_filter_set_name (flt, "DeaDBeeF playlist files (*.dbpl)"); + gtk_file_filter_add_pattern (flt, "*.dbpl"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dlg), flt); + + if (gtk_dialog_run (GTK_DIALOG (dlg)) == GTK_RESPONSE_OK) + { + gchar *fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg)); + gtk_widget_destroy (dlg); + if (fname) { + int res = pl_load (fname); + printf ("load result: %d\n", res); + g_free (fname); + gtkplaylist_t *ps = &main_playlist; + gtkpl_setup_scrollbar (ps); + gtkpl_draw_playlist (ps, 0, 0, ps->playlist->allocation.width, ps->playlist->allocation.height); + gtkpl_expose (ps, 0, 0, ps->playlist->allocation.width, ps->playlist->allocation.height); + search_refresh (); + } + } + else { + gtk_widget_destroy (dlg); + } +} + + +void +on_playlist_save_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + +} + + +void +on_playlist_save_as_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + GtkWidget *dlg = gtk_file_chooser_dialog_new ("Save Playlist As", GTK_WINDOW (mainwin), GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_OK, NULL); + + GtkFileFilter* flt; + flt = gtk_file_filter_new (); + gtk_file_filter_set_name (flt, "DeaDBeeF playlist files (*.dbpl)"); + gtk_file_filter_add_pattern (flt, "*.dbpl"); + gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dlg), flt); + + if (gtk_dialog_run (GTK_DIALOG (dlg)) == GTK_RESPONSE_OK) + { + gchar *fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg)); + gtk_widget_destroy (dlg); + + if (fname) { + int res = pl_save (fname); + printf ("save res: %d\n", res); + g_free (fname); + } + } + else { + gtk_widget_destroy (dlg); + } +} + diff --git a/callbacks.h b/callbacks.h index 8fbb5668..1daf45ed 100644 --- a/callbacks.h +++ b/callbacks.h @@ -391,3 +391,15 @@ on_playlist_motion_notify_event (GtkWidget *widget, void on_playscroll_value_changed (GtkRange *range, gpointer user_data); + +void +on_playlist_load_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_playlist_save_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_playlist_save_as_activate (GtkMenuItem *menuitem, + gpointer user_data); @@ -799,6 +799,7 @@ codec_t cdumb = { .read = cdumb_read, .seek = cdumb_seek, .insert = cdumb_insert, - .getexts = cdumb_getexts + .getexts = cdumb_getexts, + .id = "stddumb" }; @@ -426,5 +426,6 @@ codec_t cflac = { .read = cflac_read, .seek = cflac_seek, .insert = cflac_insert, - .getexts = cflac_getexts + .getexts = cflac_getexts, + .id = "stdflac" }; @@ -191,6 +191,7 @@ codec_t cgme = { .insert = cgme_insert, .getexts = cgme_getexts, .numvoices = cgme_numvoices, - .mutevoice = cgme_mutevoice + .mutevoice = cgme_mutevoice, + .id = "stdgme" }; @@ -1321,7 +1321,8 @@ codec_t cmp3 = { .read = cmp3_read, .seek = cmp3_seek, .insert = cmp3_insert, - .getexts = cmp3_getexts + .getexts = cmp3_getexts, + .id = "stdmp3" }; @@ -40,6 +40,7 @@ typedef struct codec_s { const char ** (*getexts) (void); int (*numvoices) (void); void (*mutevoice) (int voice, int mute); + const char *id; // codec id used for playlist serialization fileinfo_t info; } codec_t; @@ -507,6 +507,7 @@ codec_t csid = { csid_insert, csid_getexts, csid_numvoices, - csid_mutevoice + csid_mutevoice, + "stdsid" }; @@ -178,6 +178,7 @@ codec_t cvorbis = { .read = cvorbis_read, .seek = cvorbis_seek, .insert = cvorbis_insert, - .getexts = cvorbis_getexts + .getexts = cvorbis_getexts, + .id = "stdogg" }; diff --git a/deadbeef.glade b/deadbeef.glade index f4147637..f0e12741 100644 --- a/deadbeef.glade +++ b/deadbeef.glade @@ -190,6 +190,33 @@ <widget class="GtkMenu" id="playlist1_menu"> <child> + <widget class="GtkMenuItem" id="playlist_load"> + <property name="visible">True</property> + <property name="label" translatable="yes">Load</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_playlist_load_activate" last_modification_time="Sun, 09 Aug 2009 19:04:52 GMT"/> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="playlist_save"> + <property name="visible">True</property> + <property name="label" translatable="yes">Save</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_playlist_save_activate" last_modification_time="Sun, 09 Aug 2009 19:04:52 GMT"/> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="playlist_save_as"> + <property name="visible">True</property> + <property name="label" translatable="yes">Save As</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_playlist_save_as_activate" last_modification_time="Sun, 09 Aug 2009 19:04:52 GMT"/> + </widget> + </child> + + <child> <widget class="GtkMenuItem" id="order1"> <property name="visible">True</property> <property name="label" translatable="yes">Order</property> @@ -203,7 +230,7 @@ <property name="visible">True</property> <property name="label" translatable="yes">Linear</property> <property name="use_underline">True</property> - <property name="active">False</property> + <property name="active">True</property> <signal name="activate" handler="on_order_linear_activate" last_modification_time="Sat, 08 Aug 2009 12:26:33 GMT"/> </widget> </child> @@ -213,7 +240,7 @@ <property name="visible">True</property> <property name="label" translatable="yes">Shuffle</property> <property name="use_underline">True</property> - <property name="active">False</property> + <property name="active">True</property> <property name="group">order_linear</property> <signal name="activate" handler="on_order_shuffle_activate" last_modification_time="Sat, 08 Aug 2009 12:26:33 GMT"/> </widget> @@ -224,7 +251,7 @@ <property name="visible">True</property> <property name="label" translatable="yes">Random</property> <property name="use_underline">True</property> - <property name="active">False</property> + <property name="active">True</property> <property name="group">order_linear</property> <signal name="activate" handler="on_order_random_activate" last_modification_time="Sat, 08 Aug 2009 12:26:33 GMT"/> </widget> @@ -248,7 +275,7 @@ <property name="visible">True</property> <property name="label" translatable="yes">Loop All</property> <property name="use_underline">True</property> - <property name="active">False</property> + <property name="active">True</property> <signal name="activate" handler="on_loop_all_activate" last_modification_time="Sat, 08 Aug 2009 12:26:33 GMT"/> </widget> </child> @@ -258,7 +285,7 @@ <property name="visible">True</property> <property name="label" translatable="yes">Loop Single Song</property> <property name="use_underline">True</property> - <property name="active">False</property> + <property name="active">True</property> <property name="group">loop_all</property> <signal name="activate" handler="on_loop_single_activate" last_modification_time="Sat, 08 Aug 2009 12:26:33 GMT"/> </widget> @@ -269,7 +296,7 @@ <property name="visible">True</property> <property name="label" translatable="yes">Don't Loop</property> <property name="use_underline">True</property> - <property name="active">False</property> + <property name="active">True</property> <property name="group">loop_all</property> <signal name="activate" handler="on_loop_disable_activate" last_modification_time="Sat, 08 Aug 2009 12:26:33 GMT"/> </widget> diff --git a/gtkplaylist.c b/gtkplaylist.c index a633ee9b..63eb0306 100644 --- a/gtkplaylist.c +++ b/gtkplaylist.c @@ -133,7 +133,7 @@ void gtkpl_setup_scrollbar (gtkplaylist_t *ps) { GtkWidget *playlist = ps->playlist; int h = playlist->allocation.height / rowheight; - int size = (*ps->count); + int size = (*ps->pcount); if (h >= size) { size = 0; } @@ -241,14 +241,13 @@ gtkpl_draw_pl_row (gtkplaylist_t *ps, cairo_t *cr, int row, playItem_t *it) { } cairo_set_font_size (cr, rowheight-4); // draw as columns - char dur[10]; - if (it->duration < 0) { // means not initialized yet - strcpy (dur, "-:--"); - } - else { - int min = (int)it->duration/60; - int sec = (int)(it->duration-min*60); - snprintf (dur, 10, "%d:%02d", min, sec); + char dur[10] = "-:--"; + if (it) { + if (it->duration >= 0) { + int min = (int)it->duration/60; + int sec = (int)(it->duration-min*60); + snprintf (dur, 10, "%d:%02d", min, sec); + } } const char *columns[pl_ncolumns] = { "", @@ -323,13 +322,13 @@ gtkpl_draw_playlist (gtkplaylist_t *ps, int x, int y, int w, int h) { int row2; int row2_full; row1 = max (0, y / rowheight + ps->scrollpos); - row2 = min ((*ps->count), (y+h) / rowheight + ps->scrollpos + 1); + row2 = min ((*ps->pcount), (y+h) / rowheight + ps->scrollpos + 1); row2_full = (y+h) / rowheight + ps->scrollpos + 1; - //printf ("drawing row %d (nvis=%d)\n", row2_full, ps->nvisiblerows); // draw background playItem_t *it = gtkpl_get_for_idx (ps, ps->scrollpos); playItem_t *it_copy = it; for (row = row1; row < row2_full; row++) { + //printf ("drawing row %d (nvis=%d), row1=%d, row2=%d, row2_full=%d\n", row, ps->nvisiblerows, row1, row2, row2_full); gtkpl_draw_pl_row_back (ps, cr, row, it); if (it) { it = it->next[ps->iterator]; @@ -338,6 +337,7 @@ gtkpl_draw_playlist (gtkplaylist_t *ps, int x, int y, int w, int h) { it = it_copy; int idx = 0; for (row = row1; row < row2; row++, idx++) { + //printf ("drawing row %d (nvis=%d), row1=%d, row2=%d, row2_full=%d\n", row, ps->nvisiblerows, row1, row2, row2_full); gtkpl_draw_pl_row (ps, cr, row, it); it = it->next[ps->iterator]; } @@ -431,7 +431,7 @@ static int shift_sel_anchor = -1; void gtkpl_mouse1_pressed (gtkplaylist_t *ps, int state, int ex, int ey, double time) { // cursor must be set here, but selection must be handled in keyrelease - if ((*ps->count) == 0) { + if ((*ps->pcount) == 0) { return; } GtkWidget *widget = ps->playlist; @@ -440,7 +440,7 @@ gtkpl_mouse1_pressed (gtkplaylist_t *ps, int state, int ex, int ey, double time) ps->lastpos[1] = ey; // select item int y = ey/rowheight + ps->scrollpos; - if (y < 0 || y >= (*ps->count)) { + if (y < 0 || y >= (*ps->pcount)) { y = -1; } @@ -463,7 +463,7 @@ gtkpl_mouse1_pressed (gtkplaylist_t *ps, int state, int ex, int ey, double time) int sel = y; if (y == -1) { - y = (*ps->count) - 1; + y = (*ps->pcount) - 1; } int prev = ps->row; ps->row = y; @@ -627,7 +627,7 @@ gtkpl_handle_scroll_event (gtkplaylist_t *ps, int direction) { GtkWidget *range = ps->scrollbar;; GtkWidget *playlist = ps->playlist; int h = playlist->allocation.height / rowheight; - int size = (*ps->count); + int size = (*ps->pcount); if (h >= size) { size = 0; } @@ -777,8 +777,8 @@ gtkpl_keypress (gtkplaylist_t *ps, int keyval, int state) { } else if (keyval == GDK_Delete) { pl_delete_selected (); - if (ps->row >= (*ps->count)) { - ps->row = (*ps->count) - 1; + if (ps->row >= (*ps->pcount)) { + ps->row = (*ps->pcount) - 1; } gtkpl_setup_scrollbar (ps); gtkpl_draw_playlist (ps, 0, 0, widget->allocation.width, widget->allocation.height); @@ -786,7 +786,7 @@ gtkpl_keypress (gtkplaylist_t *ps, int keyval, int state) { search_refresh (); return; } - else if (keyval == GDK_Down && ps->row < (*ps->count) - 1) { + else if (keyval == GDK_Down && ps->row < (*ps->pcount) - 1) { ps->row++; if (ps->row > ps->scrollpos + widget->allocation.height / rowheight - 1) { newscroll = ps->row - widget->allocation.height / rowheight + 1; @@ -798,10 +798,10 @@ gtkpl_keypress (gtkplaylist_t *ps, int keyval, int state) { newscroll = ps->row; } } - else if (keyval == GDK_Page_Down && ps->row < (*ps->count) - 1) { + else if (keyval == GDK_Page_Down && ps->row < (*ps->pcount) - 1) { ps->row += 10; - if (ps->row >= (*ps->count)) { - ps->row = (*ps->count) - 1; + if (ps->row >= (*ps->pcount)) { + ps->row = (*ps->pcount) - 1; } if (ps->row > ps->scrollpos + widget->allocation.height / rowheight - 1) { newscroll = ps->row - widget->allocation.height / rowheight + 1; @@ -816,8 +816,8 @@ gtkpl_keypress (gtkplaylist_t *ps, int keyval, int state) { newscroll = ps->row; } } - else if (keyval == GDK_End && ps->row != (*ps->count) - 1) { - ps->row = (*ps->count) - 1; + else if (keyval == GDK_End && ps->row != (*ps->pcount) - 1) { + ps->row = (*ps->pcount) - 1; if (ps->row > ps->scrollpos + widget->allocation.height / rowheight - 1) { newscroll = ps->row - widget->allocation.height / rowheight + 1; } diff --git a/gtkplaylist.h b/gtkplaylist.h index e886c6af..d913f01a 100644 --- a/gtkplaylist.h +++ b/gtkplaylist.h @@ -42,7 +42,7 @@ typedef struct { GdkPixmap *backbuf; // parameters playItem_t **pcurr; // pointer to current item - int *count; // pointer to count of items in list + int *pcount; // pointer to count of items in list int iterator; // index into next array of playItem_t struct int lastpos[2]; // last mouse position (for playlist widget) int multisel; // if it uses multiple selection diff --git a/interface.c b/interface.c index ea3e68bc..b3dad19d 100644 --- a/interface.c +++ b/interface.c @@ -53,6 +53,9 @@ create_mainwin (void) GtkWidget *crop1; GtkWidget *playlist1; GtkWidget *playlist1_menu; + GtkWidget *playlist_load; + GtkWidget *playlist_save; + GtkWidget *playlist_save_as; GtkWidget *order1; GtkWidget *order1_menu; GSList *order_linear_group = NULL; @@ -199,6 +202,18 @@ create_mainwin (void) playlist1_menu = gtk_menu_new (); gtk_menu_item_set_submenu (GTK_MENU_ITEM (playlist1), playlist1_menu); + playlist_load = gtk_menu_item_new_with_mnemonic ("Load"); + gtk_widget_show (playlist_load); + gtk_container_add (GTK_CONTAINER (playlist1_menu), playlist_load); + + playlist_save = gtk_menu_item_new_with_mnemonic ("Save"); + gtk_widget_show (playlist_save); + gtk_container_add (GTK_CONTAINER (playlist1_menu), playlist_save); + + playlist_save_as = gtk_menu_item_new_with_mnemonic ("Save As"); + gtk_widget_show (playlist_save_as); + gtk_container_add (GTK_CONTAINER (playlist1_menu), playlist_save_as); + order1 = gtk_menu_item_new_with_mnemonic ("Order"); gtk_widget_show (order1); gtk_container_add (GTK_CONTAINER (playlist1_menu), order1); @@ -210,16 +225,19 @@ create_mainwin (void) order_linear_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (order_linear)); gtk_widget_show (order_linear); gtk_container_add (GTK_CONTAINER (order1_menu), order_linear); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (order_linear), TRUE); order_shuffle = gtk_radio_menu_item_new_with_mnemonic (order_linear_group, "Shuffle"); order_linear_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (order_shuffle)); gtk_widget_show (order_shuffle); gtk_container_add (GTK_CONTAINER (order1_menu), order_shuffle); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (order_shuffle), TRUE); order_random = gtk_radio_menu_item_new_with_mnemonic (order_linear_group, "Random"); order_linear_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (order_random)); gtk_widget_show (order_random); gtk_container_add (GTK_CONTAINER (order1_menu), order_random); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (order_random), TRUE); looping1 = gtk_menu_item_new_with_mnemonic ("Looping"); gtk_widget_show (looping1); @@ -232,16 +250,19 @@ create_mainwin (void) loop_all_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (loop_all)); gtk_widget_show (loop_all); gtk_container_add (GTK_CONTAINER (looping1_menu), loop_all); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (loop_all), TRUE); loop_single = gtk_radio_menu_item_new_with_mnemonic (loop_all_group, "Loop Single Song"); loop_all_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (loop_single)); gtk_widget_show (loop_single); gtk_container_add (GTK_CONTAINER (looping1_menu), loop_single); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (loop_single), TRUE); loop_disable = gtk_radio_menu_item_new_with_mnemonic (loop_all_group, "Don't Loop"); loop_all_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (loop_disable)); gtk_widget_show (loop_disable); gtk_container_add (GTK_CONTAINER (looping1_menu), loop_disable); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (loop_disable), TRUE); menuitem4 = gtk_menu_item_new_with_mnemonic ("_Help"); gtk_widget_show (menuitem4); @@ -444,6 +465,15 @@ create_mainwin (void) g_signal_connect ((gpointer) crop1, "activate", G_CALLBACK (on_crop1_activate), NULL); + g_signal_connect ((gpointer) playlist_load, "activate", + G_CALLBACK (on_playlist_load_activate), + NULL); + g_signal_connect ((gpointer) playlist_save, "activate", + G_CALLBACK (on_playlist_save_activate), + NULL); + g_signal_connect ((gpointer) playlist_save_as, "activate", + G_CALLBACK (on_playlist_save_as_activate), + NULL); g_signal_connect ((gpointer) order_linear, "activate", G_CALLBACK (on_order_linear_activate), NULL); @@ -599,6 +629,9 @@ create_mainwin (void) GLADE_HOOKUP_OBJECT (mainwin, crop1, "crop1"); GLADE_HOOKUP_OBJECT (mainwin, playlist1, "playlist1"); GLADE_HOOKUP_OBJECT (mainwin, playlist1_menu, "playlist1_menu"); + GLADE_HOOKUP_OBJECT (mainwin, playlist_load, "playlist_load"); + GLADE_HOOKUP_OBJECT (mainwin, playlist_save, "playlist_save"); + GLADE_HOOKUP_OBJECT (mainwin, playlist_save_as, "playlist_save_as"); GLADE_HOOKUP_OBJECT (mainwin, order1, "order1"); GLADE_HOOKUP_OBJECT (mainwin, order1_menu, "order1_menu"); GLADE_HOOKUP_OBJECT (mainwin, order_linear, "order_linear"); @@ -38,6 +38,10 @@ #include "messages.h" #include "playback.h" +codec_t *codecs[] = { + &cdumb, &cvorbis, &cflac, &cgme, &cmp3, &csid, NULL +}; + #define SKIP_BLANK_CUE_TRACKS 1 playItem_t *playlist_head[PL_MAX_ITERATORS]; @@ -265,9 +269,6 @@ pl_insert_file (playItem_t *after, const char *fname, int (*cb)(playItem_t *it, eol++; // match by codec - codec_t *codecs[] = { - &cdumb, &cvorbis, &cflac, &cgme, &cmp3, &csid, NULL - }; for (int i = 0; codecs[i]; i++) { if (codecs[i]->getexts && codecs[i]->insert) { const char **exts = codecs[i]->getexts (); @@ -339,43 +340,6 @@ pl_add_file (const char *fname, int (*cb)(playItem_t *it, void *data), void *use return 0; } return -1; -// {{{ original pl_add_file -#if 0 - if (!fname) { - return -1; - } - // detect codec - codec_t *codec = NULL; - const char *eol = fname + strlen (fname) - 1; - while (eol > fname && *eol != '.') { - eol--; - } - eol++; - - // match by codec - codec_t *codecs[] = { - &cdumb, &cvorbis, &cflac, &cgme, &cmp3, &csid, NULL - }; - playItem_t *after = playlist_tail; - for (int i = 0; codecs[i]; i++) { - if (codecs[i]->getexts && codecs[i]->insert) { - const char **exts = codecs[i]->getexts (); - if (exts) { - for (int e = 0; exts[e]; e++) { - if (!strcasecmp (exts[e], eol)) { - playItem_t *inserted = NULL; - if ((inserted = codecs[i]->insert (after, fname)) != NULL) { - return 0; - } - } - } - } - } - } - - return -1; -#endif -// }}} } int @@ -916,3 +880,266 @@ pl_set_loop_mode (int mode) { pl_loop_mode = mode; } +int +pl_save (const char *fname) { + const char magic[] = "DBPL"; + uint8_t majorver = 1; + uint8_t minorver = 0; + FILE *fp = fopen (fname, "w+b"); + if (!fp) { + return -1; + } + if (fwrite (magic, 1, 4, fp) != 4) { + goto save_fail; + } + if (fwrite (&majorver, 1, 1, fp) != 1) { + goto save_fail; + } + if (fwrite (&minorver, 1, 1, fp) != 1) { + goto save_fail; + } + uint32_t cnt = pl_count; + if (fwrite (&cnt, 1, 4, fp) != 4) { + goto save_fail; + } + for (playItem_t *it = playlist_head[PL_MAIN]; it; it = it->next[PL_MAIN]) { + uint16_t l; + uint8_t ll; + l = strlen (it->fname); + if (fwrite (&l, 1, 2, fp) != 2) { + goto save_fail; + } + if (fwrite (it->fname, 1, l, fp) != l) { + goto save_fail; + } + ll = strlen (it->codec->id); + if (fwrite (&ll, 1, 1, fp) != 1) { + goto save_fail; + } + if (fwrite (it->codec->id, 1, ll, fp) != ll) { + goto save_fail; + } + l = it->tracknum; + if (fwrite (&l, 1, 2, fp) != 2) { + goto save_fail; + } + if (fwrite (&it->timestart, 1, 4, fp) != 4) { + goto save_fail; + } + if (fwrite (&it->timeend, 1, 4, fp) != 4) { + goto save_fail; + } + if (fwrite (&it->duration, 1, 4, fp) != 4) { + goto save_fail; + } +#if 0 + uint8_t ft = it->filetype ? strlen (it->filetype) : 0; + if (fwrite (&ft, 1, 1, fp) != 1) { + goto save_fail; + } + if (ft) { + if (fwrite (it->filetype, 1, ft, fp) != ft) { + goto save_fail; + } + } +#endif + int16_t nm = 0; + metaInfo_t *m; + for (m = it->meta; m; m = m->next) { + nm++; + } + if (fwrite (&nm, 1, 2, fp) != 2) { + goto save_fail; + } + for (m = it->meta; m; m = m->next) { + l = strlen (m->key); + if (fwrite (&l, 1, 2, fp) != 2) { + goto save_fail; + } + if (l) { + if (fwrite (m->key, 1, l, fp) != l) { + goto save_fail; + } + } + l = strlen (m->value); + if (fwrite (&l, 1, 2, fp) != 2) { + goto save_fail; + } + if (l) { + if (fwrite (m->value, 1, l, fp) != l) { + goto save_fail; + } + } + } + } + fclose (fp); + return 0; +save_fail: + fclose (fp); + unlink (fname); + return -1; +} + +int +pl_load (const char *fname) { + pl_free (); + uint8_t majorver = 1; + uint8_t minorver = 0; + FILE *fp = fopen (fname, "rb"); + if (!fp) { + return -1; + } + char magic[4]; + if (fread (magic, 1, 4, fp) != 4) { + goto load_fail; + } + if (strncmp (magic, "DBPL", 4)) { + goto load_fail; + } + if (fread (&majorver, 1, 1, fp) != 1) { + goto load_fail; + } + if (majorver != 1) { + goto load_fail; + } + if (fread (&minorver, 1, 1, fp) != 1) { + goto load_fail; + } + if (minorver != 0) { + goto load_fail; + } + uint32_t cnt; + if (fread (&cnt, 1, 4, fp) != 4) { + goto load_fail; + } + playItem_t *it = NULL; + for (uint32_t i = 0; i < cnt; i++) { + it = malloc (sizeof (playItem_t)); + if (!it) { + goto load_fail; + } + memset (it, 0, sizeof (playItem_t)); + uint16_t l; + // fname + if (fread (&l, 1, 2, fp) != 2) { + goto load_fail; + } + it->fname = malloc (l+1); + if (fread (it->fname, 1, l, fp) != l) { + goto load_fail; + } + it->fname[l] = 0; + // codec + uint8_t ll; + if (fread (&ll, 1, 1, fp) != 1) { + goto load_fail; + } + if (ll >= 20) { + goto load_fail; + } + char codec[20]; + if (fread (codec, 1, ll, fp) != ll) { + goto load_fail; + } + codec[ll] = 0; + for (int c = 0; codecs[c]; c++) { + if (!strcmp (codec, codecs[c]->id)) { + it->codec = codecs[c]; + } + } + if (!it->codec) { + goto load_fail; + } + // tracknum + if (fread (&l, 1, 2, fp) != 2) { + goto load_fail; + } + it->tracknum = l; + // timestart + if (fread (&it->timestart, 1, 4, fp) != 4) { + goto load_fail; + } + // timeend + if (fread (&it->timeend, 1, 4, fp) != 4) { + goto load_fail; + } + // duration + if (fread (&it->duration, 1, 4, fp) != 4) { + goto load_fail; + } +#if 0 + // filetype + uint8_t ft; + if (fread (&ft, 1, 1, fp) != 1) { + goto load_fail; + } + if (ft) { + it->filetype = malloc (ft+1) + if (fread (it->filetype, 1, ft, fp) != ft) { + goto load_fail; + } + it->filetype[ft] = 0; + } +#endif + // printf ("loading file %s\n", it->fname); + int16_t nm = 0; + if (fread (&nm, 1, 2, fp) != 2) { + goto load_fail; + } + for (int i = 0; i < nm; i++) { + char key[1024]; + char value[1024]; + const char *valid_keys[] = { + "title", + "artist", + "album", + "vendor", + "year", + "genre", + "comment", + "track", + "band", + NULL + }; + + if (fread (&l, 1, 2, fp) != 2) { + goto load_fail; + } + if (!l || l >= 1024) { + goto load_fail; + } + if (fread (key, 1, l, fp) != l) { + goto load_fail; + } + key[l] = 0; + if (fread (&l, 1, 2, fp) != 2) { + goto load_fail; + } + if (!l || l >= 1024) { + goto load_fail; + } + if (fread (value, 1, l, fp) != l) { + goto load_fail; + } + value[l] = 0; + //printf ("%s=%s\n", key, value); + for (int n = 0; valid_keys[n]; n++) { + if (!strcmp (valid_keys[n], key)) { + pl_add_meta (it, valid_keys[n], value); + break; + } + } + } + pl_insert_item (playlist_tail[PL_MAIN], it); + } + fclose (fp); + return 0; +load_fail: + fclose (fp); + if (it) { + pl_item_free (it); + } + pl_free (); + return -1; +} + @@ -39,8 +39,6 @@ typedef struct playItem_s { const char *filetype; // e.g. MP3 or OGG struct playItem_s *next[PL_MAX_ITERATORS]; // next item in linked list struct playItem_s *prev[PL_MAX_ITERATORS]; // prev item in linked list -// struct playItem_s *shufflenext; // next item in shuffle list -// struct playItem_s *searchnext; // next in search results list struct metaInfo_s *meta; // linked list storing metainfo unsigned selected : 1; } playItem_t; @@ -130,4 +128,10 @@ pl_set_order (int order); void pl_set_loop_mode (int mode); +int +pl_save (const char *fname); + +int +pl_load (const char *fname); + #endif // __PLAYLIST_H |