From 1bd73696a3c8484729460cad8d8e6a44e3649c16 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Sun, 4 Apr 2010 19:10:55 +0200 Subject: switched artwork downloading to use vfs_curl --- plugins/artwork/albumartorg.c | 72 +++++++++++++++++++++++++++++++------- plugins/artwork/artwork.c | 64 ++-------------------------------- plugins/artwork/artwork.h | 9 ----- plugins/artwork/lastfm.c | 81 ++++++++++++++++++++++++++++++++++--------- plugins/gtkui/coverart.c | 9 +++-- 5 files changed, 133 insertions(+), 102 deletions(-) diff --git a/plugins/artwork/albumartorg.c b/plugins/artwork/albumartorg.c index 06247f74..74e58d6d 100644 --- a/plugins/artwork/albumartorg.c +++ b/plugins/artwork/albumartorg.c @@ -19,8 +19,14 @@ #include #include #include +#include #include "artwork.h" +extern DB_functions_t *deadbeef; + +#define trace(...) { fprintf(stderr, __VA_ARGS__); } +//#define trace(...) + int fetch_from_albumart_org (const char *artist, const char *album, const char *dest) { @@ -31,24 +37,64 @@ fetch_from_albumart_org (const char *artist, const char *album, const char *dest curl_free (artist_url); curl_free (album_url); - char *response = fetch (url); -// printf ("%s\n", response); - char *img = strstr (response, "http://ecx.images-amazon.com/images/I/"); - if (!img) - { - free (response); - return 0; + DB_FILE *fp = deadbeef->fopen (url); + if (!fp) { + trace ("fetch_from_albumart_org: failed to open %s\n", url); + return -1; + } + const char searchstr[] = "http://ecx.images-amazon.com/images/I/"; + char buffer[10000]; + char *img = NULL; + int size = deadbeef->fread (buffer, 1, sizeof (buffer), fp); + if (size > 0) { + img = strstr (buffer, searchstr); } + deadbeef->fclose (fp); + + if (!img) { + trace ("fetch_from_albumart_org: image url not found in response from %s (%d bytes)\n", url, size); + return -1; + } + char *end = strstr (img, "._SL160_"); - if (!end) + if (!end || end == img) { - free (response); - return 0; + trace ("fetch_from_albumart_org: bad xml from %s\n", url); + return -1; } strcpy (end, ".jpg"); - int res = fetch_to_file (img, dest); - free (response); - return res; + fp = deadbeef->fopen (img); + if (!fp) { + trace ("fetch_from_albumart_org: failed to open %s\n", img); + return -1; + } + + FILE *out = fopen (dest, "w+b"); + if (!out) { + trace ("fetch_from_albumart_org: failed to open %s for writing\n", dest); + deadbeef->fclose (fp); + return -1; + } + + char *writebuffer[4096]; + int len; + int error = 0; + while ((len = deadbeef->fread (writebuffer, 1, sizeof (writebuffer), fp)) > 0) { + if (fwrite (writebuffer, 1, len, out) != len) { + trace ("fetch_from_albumart_org: failed to write to %s\n", dest); + error = 1; + break; + } + } + + fclose (out); + deadbeef->fclose (fp); + + if (error) { + unlink (dest); + return -1; + } + return 0; } diff --git a/plugins/artwork/artwork.c b/plugins/artwork/artwork.c index e2151069..284a4754 100644 --- a/plugins/artwork/artwork.c +++ b/plugins/artwork/artwork.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -124,63 +123,6 @@ queue_pop (void) { deadbeef->mutex_unlock (mutex); } -int -fetch_to_stream (const char *url, FILE *stream) -{ - CURL *curl = curl_easy_init(); - curl_easy_setopt (curl, CURLOPT_URL, url); - curl_easy_setopt (curl, CURLOPT_WRITEDATA, stream); - curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1); - CURLcode ret = curl_easy_perform (curl); - curl_easy_cleanup (curl); - return ret; -} - -int -fetch_to_file (const char *url, const char *filename) -{ - /** - * Downloading files directly to its locations can cause - * cachehits of semi-downloaded files. That's why I use - * temporary files - */ - char temp [1024]; - int ret; - snprintf (temp, sizeof (temp), "%s.part", filename); - - FILE *stream = fopen (temp, "wb"); - if (!stream) - { - trace ("Could not open %s for writing\n", temp); - return 0; - } - ret = fetch_to_stream (url, stream); - if (ret != 0) { - trace ("Failed to fetch %s\n", url); - } - fclose (stream); - if (0 == ret) - { - ret = (0 == rename (temp, filename)); - if (!ret) { - trace ("Could not move %s to %s: %d\n", temp, filename, errno); - unlink (temp); - } - } - return ret; -} - -char* -fetch (const char *url) -{ - char *data; - size_t size; - FILE *stream = open_memstream (&data, &size); - fetch_to_stream (url, stream); - fclose (stream); - return data; -} - static int check_dir (const char *dir, mode_t mode) { @@ -462,10 +404,10 @@ fetcher_thread (void *none) make_cache_path (path, sizeof (path), param->album, param->artist); - if (deadbeef->conf_get_int ("artwork.enable_lastfm", 0) && fetch_from_lastfm (param->artist, param->album, path)) { + if (deadbeef->conf_get_int ("artwork.enable_lastfm", 0) && !fetch_from_lastfm (param->artist, param->album, path)) { trace ("art found on last.fm for %s %s\n", param->album, param->artist); } - else if (deadbeef->conf_get_int ("artwork.enable_albumartorg", 0) && fetch_from_albumart_org (param->artist, param->album, path)) { + else if (deadbeef->conf_get_int ("artwork.enable_albumartorg", 0) && !fetch_from_albumart_org (param->artist, param->album, path)) { trace ("art found on albumart.org for %s %s\n", param->album, param->artist); } else { @@ -503,7 +445,7 @@ fetcher_thread (void *none) char* get_album_art (const char *fname, const char *artist, const char *album, artwork_callback callback, void *user_data) { - trace ("get_album_art: %s (%s - %s)\n", fname, artist, album); +// trace ("get_album_art: %s (%s - %s)\n", fname, artist, album); char path [1024]; if (!album) { diff --git a/plugins/artwork/artwork.h b/plugins/artwork/artwork.h index 35b29fb1..c6fd9177 100644 --- a/plugins/artwork/artwork.h +++ b/plugins/artwork/artwork.h @@ -5,15 +5,6 @@ typedef void (*artwork_callback) (const char *fname, const char *artist, const char *album, void *user_data); -char* -fetch (const char *url); - -int -fetch_to_file (const char *url, const char *filename); - -int -fetch_to_stream (const char *url, FILE *stream); - typedef struct { DB_misc_t plugin; // returns filename of cached image, or NULL diff --git a/plugins/artwork/lastfm.c b/plugins/artwork/lastfm.c index e64af10b..df08c2b9 100644 --- a/plugins/artwork/lastfm.c +++ b/plugins/artwork/lastfm.c @@ -2,39 +2,88 @@ #include #include #include +#include #include "artwork.h" #define BASE_URL "http://ws.audioscrobbler.com/2.0/" #define API_KEY "b25b959554ed76058ac220b7b2e0a026" +#define trace(...) { fprintf(stderr, __VA_ARGS__); } +//#define trace(...) + +extern DB_functions_t *deadbeef; + int fetch_from_lastfm (const char *artist, const char *album, const char *dest) { char url [1024]; char *artist_url = curl_easy_escape (NULL, artist, 0); char *album_url = curl_easy_escape (NULL, album, 0); - snprintf (url, sizeof (url), BASE_URL "?method=album.getinfo&api_key=" API_KEY "&artist=%s&album=%s", artist_url, album_url ); + snprintf (url, sizeof (url), BASE_URL "?method=album.getinfo&api_key=" API_KEY "&artist=%s&album=%s", artist_url, album_url); curl_free (artist_url); curl_free (album_url); - char *response = fetch (url); -// printf ("%s\n", response); - char *img = strstr (response, ""); - if (!img) - { - free (response); - return 0; + DB_FILE *fp = deadbeef->fopen (url); + if (!fp) { + trace ("fetch_from_lastfm: failed to open %s\n", url); + return -1; } - img += 25; + + const char searchstr[] = ""; + char buffer[1000]; + char *img = NULL; + int size = deadbeef->fread (buffer, 1, sizeof (buffer), fp); + if (size > 0) { + img = strstr (buffer, searchstr); + } + deadbeef->fclose (fp); + + if (!img) { + trace ("fetch_from_lastfm: image url not found in response from %s\n", url); + return -1; + } + + img += sizeof (searchstr)-1; + char *end = strstr (img, ""); - if (!end || (end == img)) - { - free (response); - return 0; + if (!end || end == img) { + trace ("fetch_from_lastfm: bad xml from %s\n", url); + return -1; } + *end = 0; - int res = fetch_to_file (img, dest); - free (response); - return res; + + fp = deadbeef->fopen (img); + if (!fp) { + trace ("fetch_from_lastfm: failed to open %s\n", img); + return -1; + } + + FILE *out = fopen (dest, "w+b"); + if (!out) { + trace ("fetch_from_lastfm: failed to open %s for writing\n", dest); + deadbeef->fclose (fp); + return -1; + } + + char *writebuffer[4096]; + int len; + int error = 0; + while ((len = deadbeef->fread (writebuffer, 1, sizeof (writebuffer), fp)) > 0) { + if (fwrite (writebuffer, 1, len, out) != len) { + trace ("fetch_from_lastfm: failed to write to %s\n", dest); + error = 1; + break; + } + } + + fclose (out); + deadbeef->fclose (fp); + + if (error) { + unlink (dest); + return -1; + } + return 0; } diff --git a/plugins/gtkui/coverart.c b/plugins/gtkui/coverart.c index 2cf880ff..c86f447c 100644 --- a/plugins/gtkui/coverart.c +++ b/plugins/gtkui/coverart.c @@ -25,10 +25,10 @@ #include "../artwork/artwork.h" #include "gtkui.h" -#pragma GCC optimize("O0") +#define DEFAULT_COVER_PATH (PREFIX "/share/deadbeef/pixmaps/noartwork.jpg") -#define trace(...) { fprintf(stderr, __VA_ARGS__); } -//#define trace(...) +//#define trace(...) { fprintf(stderr, __VA_ARGS__); } +#define trace(...) extern DB_artwork_plugin_t *coverart_plugin; @@ -145,6 +145,9 @@ loading_thread (void *none) { GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (queue->fname, NULL); if (!pixbuf) { trace ("GDK failed to load pixbuf from file %s\n", queue->fname); + pixbuf = gdk_pixbuf_new_from_file (DEFAULT_COVER_PATH, NULL); + } + if (!pixbuf) { // make default empty image pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, 2, 2); } -- cgit v1.2.3 From 69f04b15c899d16b4f9ae3d9fe9ccb4f7da46ea4 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Sun, 4 Apr 2010 20:04:15 +0200 Subject: dependency-friendly unloading of plugins; fixed crashes in artwork loader at shutdown --- plugins.c | 15 ++++++++++----- plugins/artwork/albumartorg.c | 1 + plugins/artwork/artwork.c | 7 ++++--- plugins/artwork/lastfm.c | 1 + plugins/vfs_curl/vfs_curl.c | 6 ++++++ 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/plugins.c b/plugins.c index 429fac61..e91d8eff 100644 --- a/plugins.c +++ b/plugins.c @@ -722,13 +722,18 @@ plug_load_all (void) { void plug_unload_all (void) { + fprintf (stderr, "plug_unload_all\n"); + plugin_t *p; + for (p = plugins; p; p = p->next) { + if (p->plugin->stop) { + fprintf (stderr, "stopping %s...\n", p->plugin->name); + fflush (stderr); + p->plugin->stop (); + } + } + fprintf (stderr, "stopped all plugins\n"); while (plugins) { plugin_t *next = plugins->next; - if (plugins->plugin->stop) { - fprintf (stderr, "stopping %s...", plugins->plugin->name); - plugins->plugin->stop (); - fprintf (stderr, " [OK]\n"); - } if (plugins->handle) { dlclose (plugins->handle); } diff --git a/plugins/artwork/albumartorg.c b/plugins/artwork/albumartorg.c index 74e58d6d..cc10f235 100644 --- a/plugins/artwork/albumartorg.c +++ b/plugins/artwork/albumartorg.c @@ -44,6 +44,7 @@ fetch_from_albumart_org (const char *artist, const char *album, const char *dest } const char searchstr[] = "http://ecx.images-amazon.com/images/I/"; char buffer[10000]; + memset (buffer, 0, sizeof (buffer)); char *img = NULL; int size = deadbeef->fread (buffer, 1, sizeof (buffer), fp); if (size > 0) { diff --git a/plugins/artwork/artwork.c b/plugins/artwork/artwork.c index 284a4754..ef0c9113 100644 --- a/plugins/artwork/artwork.c +++ b/plugins/artwork/artwork.c @@ -11,15 +11,15 @@ #include "albumartorg.h" #define min(x,y) ((x)<(y)?(x):(y)) -#define trace(...) { fprintf(stderr, __VA_ARGS__); } -//#define trace(...) + +//#define trace(...) { fprintf(stderr, __VA_ARGS__); } +#define trace(...) #define DEFAULT_COVER_PATH (PREFIX "/share/deadbeef/pixmaps/noartwork.jpg") static DB_artwork_plugin_t plugin; DB_functions_t *deadbeef; - typedef struct cover_query_s { char *fname; char *artist; @@ -535,6 +535,7 @@ static int artwork_plugin_stop (void) { if (tid) { + printf ("terminate artwork plugin\n"); terminate = 1; deadbeef->cond_signal (cond); deadbeef->thread_join (tid); diff --git a/plugins/artwork/lastfm.c b/plugins/artwork/lastfm.c index df08c2b9..6f429bf7 100644 --- a/plugins/artwork/lastfm.c +++ b/plugins/artwork/lastfm.c @@ -32,6 +32,7 @@ fetch_from_lastfm (const char *artist, const char *album, const char *dest) const char searchstr[] = ""; char buffer[1000]; + memset (buffer, 0, sizeof (buffer)); char *img = NULL; int size = deadbeef->fread (buffer, 1, sizeof (buffer), fp); if (size > 0) { diff --git a/plugins/vfs_curl/vfs_curl.c b/plugins/vfs_curl/vfs_curl.c index 517d55ce..c376b2ed 100644 --- a/plugins/vfs_curl/vfs_curl.c +++ b/plugins/vfs_curl/vfs_curl.c @@ -76,6 +76,7 @@ static char http_err[CURL_ERROR_SIZE]; static int vfs_curl_abort; static int vfs_curl_count; +static int allow_new_streams; static size_t http_content_header_handler (void *ptr, size_t size, size_t nmemb, void *stream); @@ -518,6 +519,9 @@ http_start_streamer (HTTP_FILE *fp) { static DB_FILE * http_open (const char *fname) { + if (!allow_new_streams) { + return NULL; + } trace ("http_open\n"); HTTP_FILE *fp = malloc (sizeof (HTTP_FILE)); memset (fp, 0, sizeof (HTTP_FILE)); @@ -805,12 +809,14 @@ vfs_curl_on_abort (DB_event_t *ev, uintptr_t data) { static int vfs_curl_start (void) { deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_ABORTREAD, DB_CALLBACK (vfs_curl_on_abort), 0); + allow_new_streams = 1; return 0; } static int vfs_curl_stop (void) { deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_ABORTREAD, DB_CALLBACK (vfs_curl_on_abort), 0); + allow_new_streams = 0; vfs_curl_on_abort (NULL, 0); return 0; } -- cgit v1.2.3 From 1dfd6d2399b20a72aae401fa8b4893ee1558dd03 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Mon, 5 Apr 2010 13:13:59 +0200 Subject: FLAC tag editing/reloading; added support for (non-standard) TRACKTOTAL field to FLAC plugin; --- plugins/flac/flac.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/plugins/flac/flac.c b/plugins/flac/flac.c index 9281b62e..44db4c22 100644 --- a/plugins/flac/flac.c +++ b/plugins/flac/flac.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "../../deadbeef.h" static DB_decoder_t plugin; @@ -511,6 +512,9 @@ cflac_init_metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__Str else if (!strncasecmp (s, "TRACKNUMBER=", 12)) { deadbeef->pl_add_meta (it, "track", s + 12); } + else if (!strncasecmp (s, "TRACKTOTAL=", 11)) { + deadbeef->pl_add_meta (it, "numtracks", s + 11); + } else if (!strncasecmp (s, "DATE=", 5)) { deadbeef->pl_add_meta (it, "year", s + 5); } @@ -722,6 +726,139 @@ cflac_insert_fail: return NULL; } + +static const char *metainfo[] = { + "ARTIST", "artist", + "TITLE", "title", + "ALBUM", "album", + "TRACKNUMBER", "track", + "DATE", "year", + "GENRE", "genre", + "COMMENT", "comment", + "PERFORMER", "performer", + "ENSEMBLE", "band", + "COMPOSER", "composer", + "ENCODED-BY", "vendor", + "DISCNUMBER", "disc", + "COPYRIGHT", "copyright", + "TRACKTOTAL", "numtracks", + NULL +}; + +int +cflac_read_metadata (DB_playItem_t *it) { + deadbeef->pl_delete_all_meta (it); + int err = -1; + FLAC__Metadata_Chain *chain = NULL; + FLAC__Metadata_Iterator *iter = NULL; + + chain = FLAC__metadata_chain_new (); + if (!chain) { + trace ("cflac_read_metadata: FLAC__metadata_chain_new failed\n"); + return -1; + } + FLAC__bool res = FLAC__metadata_chain_read (chain, it->fname); + if (!res) { + trace ("cflac_read_metadata: FLAC__metadata_chain_read failed\n"); + goto error; + } + FLAC__metadata_chain_merge_padding (chain); + + iter = FLAC__metadata_iterator_new (); + if (!iter) { + trace ("cflac_read_metadata: FLAC__metadata_iterator_new failed\n"); + goto error; + } + FLAC__metadata_iterator_init (iter, chain); + do { + FLAC__StreamMetadata *data = FLAC__metadata_iterator_get_block (iter); + if (data && data->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { + // delete all crap + for (int m = 0; metainfo[m]; m += 2) { + int offs = 0; + do { + offs = FLAC__metadata_object_vorbiscomment_find_entry_from (data, offs, metainfo[m]); + if (offs != -1) { + FLAC__StreamMetadata_VorbisComment_Entry *comm = &data->data.vorbis_comment.comments[offs]; + deadbeef->pl_append_meta (it, metainfo[m+1], comm->entry + strlen (metainfo[m])+1); + offs++; + } + } while (offs != -1); + } + } + } while (FLAC__metadata_iterator_next (iter)); + + FLAC__metadata_iterator_delete (iter); + err = 0; +error: + if (chain) { + FLAC__metadata_chain_delete (chain); + } + + return err; +} + +int +cflac_write_metadata (DB_playItem_t *it) { + int err = -1; + FLAC__Metadata_Chain *chain = NULL; + FLAC__Metadata_Iterator *iter = NULL; + + chain = FLAC__metadata_chain_new (); + if (!chain) { + trace ("cflac_write_metadata: FLAC__metadata_chain_new failed\n"); + return -1; + } + FLAC__bool res = FLAC__metadata_chain_read (chain, it->fname); + if (!res) { + trace ("cflac_write_metadata: FLAC__metadata_chain_read failed\n"); + goto error; + } + FLAC__metadata_chain_merge_padding (chain); + + iter = FLAC__metadata_iterator_new (); + if (!iter) { + trace ("cflac_write_metadata: FLAC__metadata_iterator_new failed\n"); + goto error; + } + + FLAC__metadata_iterator_init (iter, chain); + do { + FLAC__StreamMetadata *data = FLAC__metadata_iterator_get_block (iter); + if (data && data->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { + // delete all crap + for (int m = 0; metainfo[m]; m += 2) { + const char *val = deadbeef->pl_find_meta (it, metainfo[m+1]); + if (val && *val) { + char s[1024]; + snprintf (s, sizeof (s), "%s=%s", metainfo[m], val); + FLAC__StreamMetadata_VorbisComment_Entry ent = { + .length = strlen (s), + .entry = (FLAC__byte*)s + }; + FLAC__metadata_object_vorbiscomment_replace_comment (data, ent, 1, 1); + } + else { + FLAC__metadata_object_vorbiscomment_remove_entry_matching (data, metainfo[m]); + } + } + } + } while (FLAC__metadata_iterator_next (iter)); + + FLAC__metadata_iterator_delete (iter); + if (!FLAC__metadata_chain_write (chain, 1, 0)) { + trace ("cflac_write_metadata: FLAC__metadata_chain_write failed\n"); + goto error; + } + err = 0; +error: + if (chain) { + FLAC__metadata_chain_delete (chain); + } + + return err; +} + static const char *exts[] = { "flac", "ogg", "oga", NULL }; static const char *filetypes[] = { "FLAC", "OggFLAC", NULL }; @@ -745,6 +882,8 @@ static DB_decoder_t plugin = { .seek = cflac_seek, .seek_sample = cflac_seek_sample, .insert = cflac_insert, + .read_metadata = cflac_read_metadata, + .write_metadata = cflac_write_metadata, .exts = exts, .filetypes = filetypes }; -- cgit v1.2.3 From 3f2e3471d49abd1db57709a119a341c2a8a64204 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Mon, 5 Apr 2010 15:21:51 +0200 Subject: added ogg vorbis tag editing --- plugins/vorbis/Makefile.am | 2 +- plugins/vorbis/i18n.h | 18 + plugins/vorbis/vcedit.c | 880 +++++++++++++++++++++++++++++++++++++++++++++ plugins/vorbis/vcedit.h | 69 ++++ plugins/vorbis/vceditaux.h | 9 + plugins/vorbis/vorbis.c | 221 ++++++++++-- 6 files changed, 1176 insertions(+), 23 deletions(-) create mode 100644 plugins/vorbis/i18n.h create mode 100644 plugins/vorbis/vcedit.c create mode 100644 plugins/vorbis/vcedit.h create mode 100644 plugins/vorbis/vceditaux.h diff --git a/plugins/vorbis/Makefile.am b/plugins/vorbis/Makefile.am index 0526b8f7..a316d3b3 100644 --- a/plugins/vorbis/Makefile.am +++ b/plugins/vorbis/Makefile.am @@ -2,7 +2,7 @@ if HAVE_VORBISFILE if HAVE_VORBIS vorbisdir = $(libdir)/$(PACKAGE) pkglib_LTLIBRARIES = vorbis.la -vorbis_la_SOURCES = vorbis.c +vorbis_la_SOURCES = vorbis.c vcedit.c vcedit.h vceditaux.h i18n.h vorbis_la_LDFLAGS = -module vorbis_la_LIBADD = $(LDADD) $(VORBIS_LIBS) -lc diff --git a/plugins/vorbis/i18n.h b/plugins/vorbis/i18n.h new file mode 100644 index 00000000..86248307 --- /dev/null +++ b/plugins/vorbis/i18n.h @@ -0,0 +1,18 @@ +#ifndef VORBIS_TOOLS_I18N_H +#define VORBIS_TOOLS_I18N_H + +#ifdef ENABLE_NLS +#include +#define _(X) gettext(X) +#else +#define _(X) (X) +#define textdomain(X) +#define bindtextdomain(X, Y) +#endif +#ifdef gettext_noop +#define N_(X) gettext_noop(X) +#else +#define N_(X) (X) +#endif + +#endif diff --git a/plugins/vorbis/vcedit.c b/plugins/vorbis/vcedit.c new file mode 100644 index 00000000..2300b3da --- /dev/null +++ b/plugins/vorbis/vcedit.c @@ -0,0 +1,880 @@ +/* This program is licensed under the GNU Library General Public License, version 2, + * a copy of which is included with this program (LICENCE.LGPL). + * + * (c) 2000-2001 Michael Smith + * + * + * Comment editing backend, suitable for use by nice frontend interfaces. + * + * last modified: $Id: vcedit.c 16826 2010-01-27 04:16:24Z xiphmont $ + */ + +/* Handle muxed streams and the Vorbis renormalization without having + * to understand remuxing: + * Linked list of buffers (buffer_chain). Start a link and whenever + * you encounter an unknown page from the current stream (ie we found + * its bos in the bos section) push it onto the current buffer. Whenever + * you encounter the stream being renormalized create a new link in the + * chain. + * On writing, write the contents of the first link before every Vorbis + * page written, and move to the next link. Assuming the Vorbis pages + * in match vorbis pages out, the order of pages from different logical + * streams will be unchanged. + * Special case: header. After writing the vorbis headers, and before + * starting renormalization, flush accumulated links (takes care of + * situations where number of secondary vorbis header pages changes due + * to remuxing. Similarly flush links at the end of renormalization + * and before the start of the next chain is written. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "vcedit.h" +#include "vceditaux.h" +#include "i18n.h" + +#define CHUNKSIZE 4096 +#define BUFFERCHUNK CHUNKSIZE + +/* Helper function, shouldn't need to call directly */ +static int page_buffer_push(vcedit_buffer_chain *bufferlink, ogg_page *og) { + int result=0; + char *tmp; + vcedit_page_buffer *buffer; + + buffer = &bufferlink->buffer; + tmp = realloc(buffer->data, + buffer->data_len + og->header_len + og->body_len); + if(tmp) { + buffer->data = tmp; + memcpy(buffer->data + buffer->data_len, og->header, + og->header_len); + buffer->data_len += og->header_len; + memcpy(buffer->data + buffer->data_len, og->body, + og->body_len); + result = 1; + buffer->data_len += og->body_len; + } else { + result = -1; + } + + return result; +} + +/* Write and free the first link using callbacks */ +static int buffer_chain_writelink(vcedit_state *state, void *out) { + int result = 0; + vcedit_buffer_chain *tmpchain; + vcedit_page_buffer *tmpbuffer; + + tmpchain = state->sidebuf; + tmpbuffer = &tmpchain->buffer; + if(tmpbuffer->data_len) + { + if(state->write(tmpbuffer->data,1,tmpbuffer->data_len, out) != + (size_t) tmpbuffer->data_len) + result = -1; + else + result = 1; + } + + free(tmpbuffer->data); + state->sidebuf = tmpchain->next; + free(tmpchain); + return result; +} + + +static int buffer_chain_newlink(vcedit_state *state) { + int result = 1; + vcedit_buffer_chain *bufferlink; + + if(!state->sidebuf) { + state->sidebuf = malloc (sizeof *state->sidebuf); + if(state->sidebuf) { + bufferlink = state->sidebuf; + } else { + result = -1; + } + } else { + bufferlink=state->sidebuf; + while(bufferlink->next) { + bufferlink = bufferlink->next; + } + bufferlink->next = malloc (sizeof *bufferlink->next); + if(bufferlink->next) { + bufferlink = bufferlink->next; + } else { + result = -1; + } + } + + if(result > 0 ) { + bufferlink->next = 0; + bufferlink->buffer.data = 0; + bufferlink->buffer.data_len = 0; + } + else + state->lasterror = + _("Couldn't get enough memory for input buffering."); + + return result; +} + + +/* Push page onto the end of the buffer chain */ +static int buffer_chain_push(vcedit_state *state, ogg_page *og) { + /* If there is no sidebuffer yet we need to create one, otherwise + * traverse to the last buffer and push the new page onto it. */ + int result=1; + vcedit_buffer_chain *bufferlink; + if(!state->sidebuf) { + result = buffer_chain_newlink(state); + } + + if(result > 0) { + bufferlink = state->sidebuf; + while(bufferlink->next) { + bufferlink = bufferlink->next; + } + result = page_buffer_push(bufferlink, og); + } + + if(result < 0) + state->lasterror = + _("Couldn't get enough memory for input buffering."); + + return result; +} + + + +static int vcedit_supported_stream(vcedit_state *state, ogg_page *og) { + ogg_stream_state os; + vorbis_info vi; + vorbis_comment vc; + ogg_packet header; + int result = 0; + + ogg_stream_init(&os, ogg_page_serialno(og)); + vorbis_info_init(&vi); + vorbis_comment_init(&vc); + + if( !ogg_page_bos(og) ) + result = -1; + + if(result >= 0 && ogg_stream_pagein(&os, og) < 0) + { + state->lasterror = + _("Error reading first page of Ogg bitstream."); + result = -1; + } + + if(result >= 0 && ogg_stream_packetout(&os, &header) != 1) + { + state->lasterror = _("Error reading initial header packet."); + result = -1; + } + + if(result >= 0 && vorbis_synthesis_headerin(&vi, &vc, &header) >= 0) + { + result = 1; + } else { + /* Not vorbis, may eventually become a chain of checks (Speex, + * Theora), but for the moment return 0, bos scan will push + * the current page onto the buffer. + */ + } + + ogg_stream_clear(&os); + vorbis_info_clear(&vi); + vorbis_comment_clear(&vc); + return result; +} + + +static int vcedit_contains_serial (vcedit_state *state, int serialno) { + int result = 0; + size_t count; + for( count=0; count < state->serials.streams_len; count++ ) { + if ( *(state->serials.streams + count ) == serialno ) + result = 1; + } + + return result; +} + + +static int vcedit_add_serial (vcedit_state *state, long serial) { + int result = 0; + long *tmp; + + + if( vcedit_contains_serial(state, serial) ) + { + result = 1; + } else { + tmp = realloc(state->serials.streams, + (state->serials.streams_len + 1) * sizeof *tmp); + if(tmp) { + state->serials.streams = tmp; + *(state->serials.streams + + state->serials.streams_len) = serial; + state->serials.streams_len += 1; + result = 1; + } else { + state->lasterror = + _("Couldn't get enough memory to register new stream serial number."); + result = -1; + } + } + return result; +} + + +/* For the benefit of the secondary header read only. Quietly creates + * newlinks and pushes pages onto the buffer in the right way */ +static int vcedit_target_pageout (vcedit_state *state, ogg_page *og) { + int result = 0; + int pageout_result; + pageout_result = ogg_sync_pageout(state->oy, og); + if(pageout_result > 0) + { + if(state->serial == ogg_page_serialno(og)) + result = buffer_chain_newlink(state); + else + result = buffer_chain_push(state, og); + } else if (pageout_result < 0) { + /* Vorbis comment traditionally ignores the not-synced + * error from pageout, so give it a different code. */ + result = -2; + } + return result; +} + + +/* (I'm paranoid about memset(x,0,len) not giving null pointers */ +vcedit_state *vcedit_new_state(void) { + vcedit_state *state = malloc(sizeof(vcedit_state)); + if(state) { + memset(state, 0, sizeof(vcedit_state)); + state->sidebuf = 0; + state->serials.streams = 0; + state->serials.streams_len = 0; + } + return state; +} + +char *vcedit_error(vcedit_state *state) { + return state->lasterror; +} + +vorbis_comment *vcedit_comments(vcedit_state *state) { + return state->vc; +} + +static void vcedit_clear_internals(vcedit_state *state) { + char *tmp; + if(state->vc) { + vorbis_comment_clear(state->vc); + free(state->vc); + } + if(state->os) { + ogg_stream_clear(state->os); + free(state->os); + } + if(state->oy) { + ogg_sync_clear(state->oy); + free(state->oy); + } + if(state->serials.streams_len) { + free(state->serials.streams); + state->serials.streams_len = 0; + state->serials.streams = 0; + } + while(state->sidebuf) { + vcedit_buffer_chain *tmpbuffer; + tmpbuffer = state->sidebuf; + state->sidebuf = tmpbuffer->next; + free(tmpbuffer->buffer.data); + free(tmpbuffer); + } + if(state->vendor) + free(state->vendor); + if(state->mainbuf) + free(state->mainbuf); + if(state->bookbuf) + free(state->bookbuf); + if(state->vi) { + vorbis_info_clear(state->vi); + free(state->vi); + } + + tmp = state->lasterror; + memset(state, 0, sizeof(*state)); + state->lasterror = tmp; +} + +void vcedit_clear(vcedit_state *state) +{ + if(state) + { + vcedit_clear_internals(state); + free(state); + } +} + +/* Next two functions pulled straight from libvorbis, apart from one change + * - we don't want to overwrite the vendor string. + */ +static void _v_writestring(oggpack_buffer *o,char *s, int len) +{ + while(len--) + { + oggpack_write(o,*s++,8); + } +} + +static int _commentheader_out(vorbis_comment *vc, char *vendor, ogg_packet *op) +{ + oggpack_buffer opb; + + oggpack_writeinit(&opb); + + /* preamble */ + oggpack_write(&opb,0x03,8); + _v_writestring(&opb,"vorbis", 6); + + /* vendor */ + oggpack_write(&opb,strlen(vendor),32); + _v_writestring(&opb,vendor, strlen(vendor)); + + /* comments */ + oggpack_write(&opb,vc->comments,32); + if(vc->comments){ + int i; + for(i=0;icomments;i++){ + if(vc->user_comments[i]){ + oggpack_write(&opb,vc->comment_lengths[i],32); + _v_writestring(&opb,vc->user_comments[i], + vc->comment_lengths[i]); + }else{ + oggpack_write(&opb,0,32); + } + } + } + oggpack_write(&opb,1,1); + + op->packet = malloc(oggpack_bytes(&opb)); + memcpy(op->packet, opb.buffer, oggpack_bytes(&opb)); + + op->bytes=oggpack_bytes(&opb); + op->b_o_s=0; + op->e_o_s=0; + op->granulepos=0; + + oggpack_writeclear(&opb); + return 0; +} + +static int _blocksize(vcedit_state *s, ogg_packet *p) +{ + int this = vorbis_packet_blocksize(s->vi, p); + int ret = (this + s->prevW)/4; + + if(!s->prevW) + { + s->prevW = this; + return 0; + } + + s->prevW = this; + return ret; +} + +static int _fetch_next_packet(vcedit_state *s, ogg_packet *p, ogg_page *page) +{ + int result; + char *buffer; + int bytes; + int serialno; + + result = ogg_stream_packetout(s->os, p); + + if(result > 0) + return 1; + else { + while(1) { + if(s->eosin) + return 0; + + while(ogg_sync_pageout(s->oy, page) <= 0) + { + buffer = ogg_sync_buffer(s->oy, CHUNKSIZE); + bytes = s->read(buffer,1, CHUNKSIZE, s->in); + ogg_sync_wrote(s->oy, bytes); + if(bytes == 0) + return 0; + } + + serialno = ogg_page_serialno(page); + if(ogg_page_serialno(page) != s->serial) + { + if(vcedit_contains_serial(s, serialno)) { + result = buffer_chain_push(s, page); + if(result < 0) + return result; + } + else + { + s->eosin = 1; + s->extrapage = 1; + return 0; + } + } + else + { + ogg_stream_pagein(s->os, page); + result = buffer_chain_newlink(s); + if (result < 0) + return result; + + if(ogg_page_eos(page)) + s->eosin = 1; + } + result = ogg_stream_packetout(s->os, p); + if(result > 0) + return 1; + } + /* Here == trouble */ + return 0; + } +} + +int vcedit_open(vcedit_state *state, FILE *in) +{ + return vcedit_open_callbacks(state, (void *)in, + (vcedit_read_func)fread, (vcedit_write_func)fwrite); +} + +int vcedit_open_callbacks(vcedit_state *state, void *in, + vcedit_read_func read_func, vcedit_write_func write_func) +{ + + char *buffer; + int bytes,i; + int chunks = 0; + int read_bos, test_supported, page_pending; + int have_vorbis; + ogg_packet *header; + ogg_packet header_main; + ogg_packet header_comments; + ogg_packet header_codebooks; + ogg_page og; + + state->in = in; + state->read = read_func; + state->write = write_func; + + state->oy = malloc(sizeof(ogg_sync_state)); + ogg_sync_init(state->oy); + + while(1) + { + buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); + bytes = state->read(buffer, 1, CHUNKSIZE, state->in); + + ogg_sync_wrote(state->oy, bytes); + + if(ogg_sync_pageout(state->oy, &og) == 1) + break; + + if(chunks++ >= 10) /* Bail if we don't find data in the first 40 kB */ + { + if(byteslasterror = _("Input truncated or empty."); + else + state->lasterror = _("Input is not an Ogg bitstream."); + goto err; + } + } + + /* BOS loop, starting with a loaded ogg page. */ + if(buffer_chain_newlink(state) < 0) + goto err; + + for( read_bos = 1, have_vorbis = 0 ; read_bos; ) + { + test_supported = vcedit_supported_stream(state, &og); + if(test_supported < 0) + { + goto err; + } + else if (test_supported == 0 || have_vorbis ) + { + if(vcedit_add_serial ( state, ogg_page_serialno(&og)) < 0) + goto err; + if( buffer_chain_push(state, &og) < 0) + goto err; + } + else if (test_supported > 0) + { + if(buffer_chain_newlink(state) < 0) + goto err; + state->serial = ogg_page_serialno(&og); + if(vcedit_add_serial ( state, ogg_page_serialno(&og)) < 0) + goto err; + + state->os = malloc(sizeof(ogg_stream_state)); + ogg_stream_init(state->os, state->serial); + + state->vi = malloc(sizeof(vorbis_info)); + vorbis_info_init(state->vi); + + state->vc = malloc(sizeof(vorbis_comment)); + vorbis_comment_init(state->vc); + + if(ogg_stream_pagein(state->os, &og) < 0) + { + state->lasterror = + _("Error reading first page of Ogg bitstream."); + goto err; + } + + if(ogg_stream_packetout(state->os, &header_main) != 1) + { + state->lasterror = + _("Error reading initial header packet."); + goto err; + } + + if(vorbis_synthesis_headerin(state->vi, state->vc, + &header_main) < 0) + { + state->lasterror = + _("Ogg bitstream does not contain Vorbis data."); + goto err; + } + have_vorbis = 1; + } + while(1) + { + buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); + bytes = state->read(buffer, 1, CHUNKSIZE, state->in); + + if(bytes == 0) + { + state->lasterror = + _("EOF before recognised stream."); + goto err; + } + + ogg_sync_wrote(state->oy, bytes); + + if(ogg_sync_pageout(state->oy, &og) == 1) + break; + } + if(!ogg_page_bos(&og)) { + read_bos = 0; + page_pending = 1; + } + } + + if(!state->os) { + state->lasterror = _("Ogg bitstream does not contain a supported data-type."); + goto err; + } + + state->mainlen = header_main.bytes; + state->mainbuf = malloc(state->mainlen); + memcpy(state->mainbuf, header_main.packet, header_main.bytes); + + if(ogg_page_serialno(&og) == state->serial) + { + if(buffer_chain_newlink(state) < 0) + goto err; + } + + else + { + if(buffer_chain_push(state, &og) < 0) + goto err; + page_pending = 0; + } + + i = 0; + header = &header_comments; + while(i<2) { + while(i<2) { + int result; + if(!page_pending) + result = vcedit_target_pageout(state, &og); + else + { + result = 1; + page_pending = 0; + } + if(result == 0 || result == -2) break; /* Too little data so far */ + else if(result == -1) goto err; + else if(result == 1) + { + ogg_stream_pagein(state->os, &og); + while(i<2) + { + result = ogg_stream_packetout(state->os, header); + if(result == 0) break; + if(result == -1) + { + state->lasterror = _("Corrupt secondary header."); + goto err; + } + vorbis_synthesis_headerin(state->vi, state->vc, header); + if(i==1) + { + state->booklen = header->bytes; + state->bookbuf = malloc(state->booklen); + memcpy(state->bookbuf, header->packet, + header->bytes); + } + i++; + header = &header_codebooks; + } + } + } + + buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); + bytes = state->read(buffer, 1, CHUNKSIZE, state->in); + if(bytes == 0 && i < 2) + { + state->lasterror = _("EOF before end of Vorbis headers."); + goto err; + } + ogg_sync_wrote(state->oy, bytes); + } + + /* Copy the vendor tag */ + state->vendor = malloc(strlen(state->vc->vendor) +1); + strcpy(state->vendor, state->vc->vendor); + + /* Headers are done! */ + return 0; + +err: + vcedit_clear_internals(state); + return -1; +} + +int vcedit_write(vcedit_state *state, void *out) +{ + ogg_stream_state streamout; + ogg_packet header_main; + ogg_packet header_comments; + ogg_packet header_codebooks; + + ogg_page ogout, ogin; + ogg_packet op; + ogg_int64_t granpos = 0; + int result; + char *buffer; + int bytes; + int needflush=0, needout=0; + + state->eosin = 0; + state->extrapage = 0; + + header_main.bytes = state->mainlen; + header_main.packet = state->mainbuf; + header_main.b_o_s = 1; + header_main.e_o_s = 0; + header_main.granulepos = 0; + + header_codebooks.bytes = state->booklen; + header_codebooks.packet = state->bookbuf; + header_codebooks.b_o_s = 0; + header_codebooks.e_o_s = 0; + header_codebooks.granulepos = 0; + + ogg_stream_init(&streamout, state->serial); + + _commentheader_out(state->vc, state->vendor, &header_comments); + + ogg_stream_packetin(&streamout, &header_main); + ogg_stream_packetin(&streamout, &header_comments); + ogg_stream_packetin(&streamout, &header_codebooks); + + while((result = ogg_stream_flush(&streamout, &ogout))) + { + if(state->sidebuf && buffer_chain_writelink(state, out) < 0) + goto cleanup; + if(state->write(ogout.header,1,ogout.header_len, out) != + (size_t) ogout.header_len) + goto cleanup; + if(state->write(ogout.body,1,ogout.body_len, out) != + (size_t) ogout.body_len) + goto cleanup; + } + + while(state->sidebuf) { + if(buffer_chain_writelink(state, out) < 0) + goto cleanup; + } + if(buffer_chain_newlink(state) < 0) + goto cleanup; + + while(_fetch_next_packet(state, &op, &ogin)) + { + int size; + size = _blocksize(state, &op); + granpos += size; + + if(needflush) + { + if(ogg_stream_flush(&streamout, &ogout)) + { + if(state->sidebuf && + buffer_chain_writelink(state, out) < 0) + goto cleanup; + if(state->write(ogout.header,1,ogout.header_len, + out) != (size_t) ogout.header_len) + goto cleanup; + if(state->write(ogout.body,1,ogout.body_len, + out) != (size_t) ogout.body_len) + goto cleanup; + } + } + else if(needout) + { + if(ogg_stream_pageout(&streamout, &ogout)) + { + if(state->sidebuf && + buffer_chain_writelink(state, out) < 0) + goto cleanup; + if(state->write(ogout.header,1,ogout.header_len, + out) != (size_t) ogout.header_len) + goto cleanup; + if(state->write(ogout.body,1,ogout.body_len, + out) != (size_t) ogout.body_len) + goto cleanup; + } + } + + needflush=needout=0; + + if(op.granulepos == -1) + { + op.granulepos = granpos; + ogg_stream_packetin(&streamout, &op); + } + else /* granulepos is set, validly. Use it, and force a flush to + account for shortened blocks (vcut) when appropriate */ + { + if(granpos > op.granulepos) + { + granpos = op.granulepos; + ogg_stream_packetin(&streamout, &op); + needflush=1; + } + else + { + ogg_stream_packetin(&streamout, &op); + needout=1; + } + } + } + + streamout.e_o_s = 1; + while(ogg_stream_flush(&streamout, &ogout)) + { + if(state->sidebuf && buffer_chain_writelink(state, out) < 0) + goto cleanup; + if(state->write(ogout.header,1,ogout.header_len, + out) != (size_t) ogout.header_len) + goto cleanup; + if(state->write(ogout.body,1,ogout.body_len, + out) != (size_t) ogout.body_len) + goto cleanup; + } + + if (state->extrapage) + { + /* This is the first page of a new chain, get rid of the + * sidebuffer */ + while(state->sidebuf) + if(buffer_chain_writelink(state, out) < 0) + goto cleanup; + if(state->write(ogin.header,1,ogin.header_len, + out) != (size_t) ogin.header_len) + goto cleanup; + if (state->write(ogin.body,1,ogin.body_len, out) != + (size_t) ogin.body_len) + goto cleanup; + } + + state->eosin=0; /* clear it, because not all paths to here do */ + while(!state->eosin) /* We reached eos, not eof */ + { + /* We copy the rest of the stream (other logical streams) + * through, a page at a time. */ + while(1) + { + result = ogg_sync_pageout(state->oy, &ogout); + if(result==0) + break; + if(result<0) + state->lasterror = _("Corrupt or missing data, continuing..."); + else + { + /* Don't bother going through the rest, we can just + * write the page out now */ + if(state->write(ogout.header,1,ogout.header_len, + out) != (size_t) ogout.header_len) { + goto cleanup; + } + if(state->write(ogout.body,1,ogout.body_len, out) != + (size_t) ogout.body_len) { + goto cleanup; + } + } + } + buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); + bytes = state->read(buffer,1, CHUNKSIZE, state->in); + ogg_sync_wrote(state->oy, bytes); + if(bytes == 0) + { + state->eosin = 1; + break; + } + } + + +cleanup: + ogg_stream_clear(&streamout); + + /* We don't ogg_packet_clear() this, because the memory was allocated in + _commentheader_out(), so we mirror that here */ + _ogg_free(header_comments.packet); + + free(state->mainbuf); + free(state->bookbuf); + state->mainbuf = state->bookbuf = NULL; + + if(!state->eosin) + { + state->lasterror = + _("Error writing stream to output. " + "Output stream may be corrupted or truncated."); + return -1; + } + + return 0; +} diff --git a/plugins/vorbis/vcedit.h b/plugins/vorbis/vcedit.h new file mode 100644 index 00000000..173876f3 --- /dev/null +++ b/plugins/vorbis/vcedit.h @@ -0,0 +1,69 @@ +/* This program is licensed under the GNU Library General Public License, version 2, + * a copy of which is included with this program (with filename LICENSE.LGPL). + * + * (c) 2000-2001 Michael Smith + * + * VCEdit header. + * + * last modified: $ID:$ + */ + +#ifndef __VCEDIT_H +#define __VCEDIT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +typedef size_t (*vcedit_read_func)(void *, size_t, size_t, void *); +typedef size_t (*vcedit_write_func)(const void *, size_t, size_t, void *); + +typedef struct { + long *streams; + size_t streams_len; +} vcedit_serial_nos; + +typedef struct { + ogg_sync_state *oy; + ogg_stream_state *os; + + vorbis_comment *vc; + vorbis_info *vi; + + vcedit_read_func read; + vcedit_write_func write; + + void *in; + int serial; + vcedit_serial_nos serials; + unsigned char *mainbuf; + unsigned char *bookbuf; + int mainlen; + int booklen; + char *lasterror; + char *vendor; + int prevW; + int extrapage; + int eosin; + struct vcedit_buffer_chain *sidebuf; +} vcedit_state; + +extern vcedit_state * vcedit_new_state(void); +extern void vcedit_clear(vcedit_state *state); +extern vorbis_comment * vcedit_comments(vcedit_state *state); +extern int vcedit_open(vcedit_state *state, FILE *in); +extern int vcedit_open_callbacks(vcedit_state *state, void *in, + vcedit_read_func read_func, vcedit_write_func write_func); +extern int vcedit_write(vcedit_state *state, void *out); +extern char * vcedit_error(vcedit_state *state); + +#ifdef __cplusplus +} +#endif + +#endif /* __VCEDIT_H */ + diff --git a/plugins/vorbis/vceditaux.h b/plugins/vorbis/vceditaux.h new file mode 100644 index 00000000..bb40eaeb --- /dev/null +++ b/plugins/vorbis/vceditaux.h @@ -0,0 +1,9 @@ +typedef struct vcedit_page_buffer { + char *data; + size_t data_len; +} vcedit_page_buffer; + +typedef struct vcedit_buffer_chain { + struct vcedit_buffer_chain *next; + struct vcedit_page_buffer buffer; +} vcedit_buffer_chain; diff --git a/plugins/vorbis/vorbis.c b/plugins/vorbis/vorbis.c index 4532eef9..45533651 100644 --- a/plugins/vorbis/vorbis.c +++ b/plugins/vorbis/vorbis.c @@ -20,16 +20,19 @@ #include #include #include +#include +#include #ifdef HAVE_CONFIG_H # include #endif #include "../../deadbeef.h" +#include "vcedit.h" #define min(x,y) ((x)<(y)?(x):(y)) #define max(x,y) ((x)>(y)?(x):(y)) -//#define trace(...) { fprintf (stderr, __VA_ARGS__); } -#define trace(fmt,...) +#define trace(...) { fprintf (stderr, __VA_ARGS__); } +//#define trace(fmt,...) static DB_decoder_t plugin; static DB_functions_t *deadbeef; @@ -50,7 +53,7 @@ typedef struct { static size_t cvorbis_fread (void *ptr, size_t size, size_t nmemb, void *datasource) { size_t ret = deadbeef->fread (ptr, size, nmemb, datasource); - trace ("cvorbis_fread %d %d %d\n", size, nmemb, ret); +// trace ("cvorbis_fread %d %d %d\n", size, nmemb, ret); return ret; } @@ -77,19 +80,22 @@ update_vorbis_comments (DB_playItem_t *it, vorbis_comment *vc) { deadbeef->pl_delete_all_meta (it); deadbeef->pl_add_meta (it, "vendor", vc->vendor); for (int i = 0; i < vc->comments; i++) { - if (!strncasecmp (vc->user_comments[i], "artist=", 7)) { + if (!strncasecmp (vc->user_comments[i], "ARTIST=", 7)) { deadbeef->pl_add_meta (it, "artist", vc->user_comments[i] + 7); } - else if (!strncasecmp (vc->user_comments[i], "album=", 6)) { + else if (!strncasecmp (vc->user_comments[i], "ALBUM=", 6)) { deadbeef->pl_add_meta (it, "album", vc->user_comments[i] + 6); } - else if (!strncasecmp (vc->user_comments[i], "title=", 6)) { + else if (!strncasecmp (vc->user_comments[i], "TITLE=", 6)) { deadbeef->pl_add_meta (it, "title", vc->user_comments[i] + 6); } - else if (!strncasecmp (vc->user_comments[i], "tracknumber=", 12)) { + else if (!strncasecmp (vc->user_comments[i], "TRACKNUMBER=", 12)) { deadbeef->pl_add_meta (it, "track", vc->user_comments[i] + 12); } - else if (!strncasecmp (vc->user_comments[i], "date=", 5)) { + else if (!strncasecmp (vc->user_comments[i], "TRACKTOTAL=", 11)) { + deadbeef->pl_add_meta (it, "numtracks", vc->user_comments[i] + 11); + } + else if (!strncasecmp (vc->user_comments[i], "DATE=", 5)) { deadbeef->pl_add_meta (it, "year", vc->user_comments[i] + 5); } else if (!strncasecmp (vc->user_comments[i], "COMMENT=", 8)) { @@ -254,7 +260,7 @@ cvorbis_read (DB_fileinfo_t *_info, char *bytes, int size) { if (!info->file->vfs->streaming) { if (info->currentsample + size / (2 * _info->channels) > info->endsample) { size = (info->endsample - info->currentsample + 1) * 2 * _info->channels; - trace ("size truncated to %d bytes, cursample=%d, info->endsample=%d, totalsamples=%d\n", size, info->currentsample, info->endsample, ov_pcm_total (&vorbis_file, -1)); + trace ("size truncated to %d bytes, cursample=%d, info->endsample=%d, totalsamples=%d\n", size, info->currentsample, info->endsample, ov_pcm_total (&info->vorbis_file, -1)); if (size <= 0) { return 0; } @@ -320,7 +326,7 @@ cvorbis_read (DB_fileinfo_t *_info, char *bytes, int size) { } } _info->readpos = (float)(ov_pcm_tell(&info->vorbis_file)-info->startsample)/info->vi->rate; - trace ("cvorbis_read got %d bytes, readpos %f, info->currentsample %d, ret %d\n", initsize-size, _info->readpos, info->currentsample, ret); + //trace ("cvorbis_read got %d bytes, readpos %f, info->currentsample %d, ret %d\n", initsize-size, _info->readpos, info->currentsample, ret); deadbeef->streamer_set_bitrate (ov_bitrate_instant (&info->vorbis_file)/1000); return initsize - size; } @@ -431,28 +437,197 @@ cvorbis_insert (DB_playItem_t *after, const char *fname) { return after; } -// that won't be needed with refcounting - -//static int -//vorbis_trackdeleted (DB_event_track_t *ev, uintptr_t data) { -// if (ev->track == info->ptrack) { -// info->ptrack = NULL; -// } -// return 0; -//} - static int vorbis_start (void) { -// deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_TRACKDELETED, DB_CALLBACK (vorbis_trackdeleted), 0); return 0; } static int vorbis_stop (void) { -// deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_TRACKDELETED, DB_CALLBACK (vorbis_trackdeleted), 0); return 0; } +static const char *metainfo[] = { + "ARTIST", "artist", + "TITLE", "title", + "ALBUM", "album", + "TRACKNUMBER", "track", + "DATE", "year", + "GENRE", "genre", + "COMMENT", "comment", + "PERFORMER", "performer", + "ENSEMBLE", "band", + "COMPOSER", "composer", + "ENCODED-BY", "vendor", + "DISCNUMBER", "disc", + "COPYRIGHT", "copyright", + "TRACKTOTAL", "numtracks", + NULL +}; + +int +cvorbis_read_metadata (DB_playItem_t *it) { + int err = -1; + DB_FILE *fp = NULL; + OggVorbis_File vorbis_file; + vorbis_info *vi = NULL; + + fp = deadbeef->fopen (it->fname); + if (!fp) { + trace ("cvorbis_read_metadata: failed to fopen %s\n", it->fname); + return -1; + } + if (fp->vfs->streaming) { + trace ("cvorbis_read_metadata: failed to fopen %s\n", it->fname); + goto error; + } + ov_callbacks ovcb = { + .read_func = cvorbis_fread, + .seek_func = cvorbis_fseek, + .close_func = cvorbis_fclose, + .tell_func = cvorbis_ftell + }; + int res = ov_open_callbacks (fp, &vorbis_file, NULL, 0, ovcb); + if (res != 0) { + trace ("cvorbis_read_metadata: ov_open_callbacks returned %d\n", res); + goto error; + } + vi = ov_info (&vorbis_file, -1); + if (!vi) { // not a vorbis stream + trace ("cvorbis_read_metadata: failed to ov_open %s\n", it->fname); + goto error; + } + + // metainfo + vorbis_comment *vc = ov_comment (&vorbis_file, -1); + if (vc) { + update_vorbis_comments (it, vc); + } + + err = 0; +error: + if (fp) { + ov_clear (&vorbis_file); + } + return err; + +} + +int +cvorbis_write_metadata (DB_playItem_t *it) { + vcedit_state *state = NULL; + vorbis_comment *vc = NULL; + FILE *fp = NULL; + FILE *out = NULL; + int err = -1; + char outname[PATH_MAX] = ""; + + struct field { + struct field *next; + int size; + uint8_t data[0]; + }; + + struct field *preserved_fields = NULL; + + state = vcedit_new_state (); + if (!state) { + trace ("cvorbis_write_metadata: vcedit_new_state failed\n"); + return -1; + } + fp = fopen (it->fname, "rb"); + if (!fp) { + trace ("cvorbis_write_metadata: failed to read metadata from %s\n", it->fname); + goto error; + } + if (vcedit_open (state, fp) != 0) { + trace ("cvorbis_write_metadata: vcedit_open failed, error: %s\n", vcedit_error (state)); + goto error; + } + + vc = vcedit_comments (state); + if (!vc) { + trace ("cvorbis_write_metadata: vcedit_comments failed, error: %s\n", vcedit_error (state)); + goto error; + } + + // copy all unknown fields to separate buffer + for (int i = 0; i < vc->comments; i++) { + int m; + for (m = 0; metainfo[m]; m += 2) { + int l = strlen (metainfo[m]); + if (l+1 <= vc->comment_lengths[i] && !strncasecmp (vc->user_comments[i], metainfo[m], vc->comment_lengths[i]) && vc->user_comments[i][l] == '=') { + break; + } + } + if (!metainfo[m]) { + // unknown field + struct field *f = malloc (sizeof (struct field) + vc->comment_lengths[i]); + memset (f, 0, sizeof (struct field)); + memcpy (f->data, vc->user_comments[i], vc->comment_lengths[i]); + f->size = vc->comment_lengths[i]; + f->next = preserved_fields; + preserved_fields = f; + } + } + + vorbis_comment_clear(vc); + vorbis_comment_init(vc); + + // add known fields + for (int m = 0; metainfo[m]; m += 2) { + const char *val = deadbeef->pl_find_meta (it, metainfo[m+1]); + if (val && *val) { + char s[1024]; + snprintf (s, sizeof (s), "%s=%s", metainfo[m], val); + vorbis_comment_add (vc, s); + } + } + + // add preserved fields + for (struct field *f = preserved_fields; f; f = f->next) { + vorbis_comment_add (vc, f->data); + } + + snprintf (outname, sizeof (outname), "%s.temp.ogg", it->fname); + + out = fopen (outname, "w+b"); + if (!fp) { + trace ("cvorbis_write_metadata: failed to open %s for writing\n", it->fname); + goto error; + } + + if (vcedit_write (state, out) < 0) { + trace ("cvorbis_write_metadata: failed to write tags to %s, error: %s\n", it->fname, vcedit_error (state)); + goto error; + } + + err = 0; +error: + if (out) { + fclose (fp); + } + if (state) { + vcedit_clear (state); + } + + while (preserved_fields) { + struct field *next = preserved_fields->next; + free (preserved_fields); + preserved_fields = next; + } + + if (!err) { + rename (outname, it->fname); + } + else if (*outname) { + unlink (outname); + } + + return err; +} + + static const char * exts[] = { "ogg", "ogx", NULL }; static const char *filetypes[] = { "OggVorbis", NULL }; @@ -478,6 +653,8 @@ static DB_decoder_t plugin = { .seek = cvorbis_seek, .seek_sample = cvorbis_seek_sample, .insert = cvorbis_insert, + .read_metadata = cvorbis_read_metadata, + .write_metadata = cvorbis_write_metadata, .exts = exts, .filetypes = filetypes }; -- cgit v1.2.3 From 65d8f6b0a9a8334bbd46843216fbdf96a02d6e50 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Mon, 5 Apr 2010 16:08:16 +0200 Subject: added tag type metadata support to flac plugin --- plugins/flac/flac.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/flac/flac.c b/plugins/flac/flac.c index 44db4c22..fad3be29 100644 --- a/plugins/flac/flac.c +++ b/plugins/flac/flac.c @@ -566,6 +566,7 @@ cflac_init_metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__Str if (!title_added) { deadbeef->pl_add_meta (it, "title", NULL); } + deadbeef->pl_add_meta (it, "tags", "VorbisComments"); } } @@ -726,7 +727,6 @@ cflac_insert_fail: return NULL; } - static const char *metainfo[] = { "ARTIST", "artist", "TITLE", "title", @@ -790,6 +790,8 @@ cflac_read_metadata (DB_playItem_t *it) { FLAC__metadata_iterator_delete (iter); err = 0; + deadbeef->pl_add_meta (it, "title", NULL); + deadbeef->pl_add_meta (it, "tags", "VorbisComments"); error: if (chain) { FLAC__metadata_chain_delete (chain); -- cgit v1.2.3 From 392c7057f23fbec6827283d0d95cbddc1deed61a Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Mon, 5 Apr 2010 16:08:47 +0200 Subject: added tag type metadata support to vorbis plugin --- plugins/vorbis/vorbis.c | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/vorbis/vorbis.c b/plugins/vorbis/vorbis.c index 45533651..4667d1eb 100644 --- a/plugins/vorbis/vorbis.c +++ b/plugins/vorbis/vorbis.c @@ -140,6 +140,7 @@ update_vorbis_comments (DB_playItem_t *it, vorbis_comment *vc) { } } deadbeef->pl_add_meta (it, "title", NULL); + deadbeef->pl_add_meta (it, "tags", "VorbisComments"); } static DB_fileinfo_t * -- cgit v1.2.3 From eaee7c805cfd491ae5b6fb11a8431e5e9ced6972 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Mon, 5 Apr 2010 16:08:59 +0200 Subject: rebuild/redraw playlist after editing track metadata --- plugins/gtkui/trkproperties.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/gtkui/trkproperties.c b/plugins/gtkui/trkproperties.c index 481cdc8c..61df69f0 100644 --- a/plugins/gtkui/trkproperties.c +++ b/plugins/gtkui/trkproperties.c @@ -19,6 +19,7 @@ #include #include #include +#include "ddblistview.h" #include "trkproperties.h" #include "interface.h" #include "support.h" @@ -211,6 +212,7 @@ on_write_tags_clicked (GtkButton *button, GtkTreeModel *model = GTK_TREE_MODEL (gtk_tree_view_get_model (tree)); gtk_tree_model_foreach (model, set_metadata_cb, track); dec->write_metadata (track); + ddb_listview_refresh (DDB_LISTVIEW (lookup_widget (mainwin, "playlist")), DDB_REFRESH_LIST); } break; } -- cgit v1.2.3 From d3138fa6c8602437e70e5b6592188238b5102de1 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Mon, 5 Apr 2010 16:54:35 +0200 Subject: reduced amount of copy-pasted iconv code in junklib; added junk_iconv to DB_functions_t --- deadbeef.h | 3 +- junklib.c | 157 +++++++------------------------------------------------------ junklib.h | 5 +- plugins.c | 1 + 4 files changed, 25 insertions(+), 141 deletions(-) diff --git a/deadbeef.h b/deadbeef.h index a0053cdd..80508b8d 100644 --- a/deadbeef.h +++ b/deadbeef.h @@ -450,7 +450,8 @@ typedef struct { int (*junk_get_leading_size_stdio) (FILE *fp); void (*junk_copy) (DB_playItem_t *from, DB_playItem_t *first, DB_playItem_t *last); const char * (*junk_detect_charset) (const char *s); - void (*junk_recode) (const char *in, int inlen, char *out, int outlen, const char *cs); + int (*junk_recode) (const char *in, int inlen, char *out, int outlen, const char *cs); + int (*junk_iconv) (const char *in, int inlen, char *out, int outlen, const char *cs_in, const char *cs_out); // vfs DB_FILE* (*fopen) (const char *fname); void (*fclose) (DB_FILE *f); diff --git a/junklib.c b/junklib.c index 34bdfbe3..b103ff4e 100644 --- a/junklib.c +++ b/junklib.c @@ -37,8 +37,8 @@ #define UTF8 "utf-8" -//#define trace(...) { fprintf(stderr, __VA_ARGS__); } -#define trace(fmt,...) +#define trace(...) { fprintf(stderr, __VA_ARGS__); } +//#define trace(fmt,...) #define min(x,y) ((x)<(y)?(x):(y)) #define max(x,y) ((x)>(y)?(x):(y)) @@ -312,67 +312,10 @@ can_be_russian (const signed char *str) { return 0; } -#if 0 -static char * -convstr_id3v2_2to3 (const unsigned char* str, int sz) { - static char out[2048]; - const char *enc = "iso8859-1"; - char *ret = out; - - // hack to add limited cp1251 recoding support - if (*str == 1) { - if (str[1] == 0xff && str[2] == 0xfe) { - enc = "UCS-2LE"; - str += 2; - sz -= 2; - } - else if (str[2] == 0xff && str[1] == 0xfe) { - enc = "UCS-2BE"; - str += 2; - sz -= 2; - } - else { - trace ("invalid ucs-2 signature %x %x\n", (int)str[1], (int)str[2]); - return NULL; - } - } - else { - if (can_be_russian (&str[1])) { - enc = "cp1251"; - } - } - str++; - sz--; - iconv_t cd = iconv_open (UTF8, enc); - if (cd == (iconv_t)-1) { - trace ("iconv can't recoode from %s to utf8", enc); - return strdup ("-"); - } - else { - size_t inbytesleft = sz; - size_t outbytesleft = 2047; -#ifdef __linux__ - char *pin = (char*)str; -#else - const char *pin = str; -#endif - char *pout = out; - memset (out, 0, sizeof (out)); - /*size_t res = */iconv (cd, &pin, &inbytesleft, &pout, &outbytesleft); - iconv_close (cd); - ret = out; - } - return strdup (ret); -} -#endif - static char * convstr_id3v2 (int version, uint8_t encoding, const unsigned char* str, int sz) { static char out[2048]; const char *enc = "iso8859-1"; - char *ret = out; - - // hack to add limited cp1251 recoding support if (version == 4 && encoding == 3) { // utf8 @@ -415,32 +358,20 @@ convstr_id3v2 (int version, uint8_t encoding, const unsigned char* str, int sz) enc = "iso8859-1"; } #endif - else { + else if (encoding == 0) { + // hack to add limited cp1251 recoding support if (can_be_russian (str)) { enc = "cp1251"; } } - iconv_t cd = iconv_open (UTF8, enc); - if (cd == (iconv_t)-1) { - trace ("iconv can't recode from %s to utf8\n", enc); - return strdup ("-"); - } else { - size_t inbytesleft = sz; - size_t outbytesleft = 2047; -#ifdef __linux__ - char *pin = (char*)str; -#else - const char *pin = str; -#endif - char *pout = out; - memset (out, 0, sizeof (out)); - /*size_t res = */iconv (cd, &pin, &inbytesleft, &pout, &outbytesleft); - iconv_close (cd); - ret = out; - } -// trace ("decoded %s\n", out+3); - return strdup (ret); + return 0; // invalid encoding + } + if (junk_iconv (str, sz, out, sizeof (out), enc, UTF8) > 0) { + trace ("converted: %s\n", out); + return strdup (out); + } + return NULL; } static const char * @@ -458,52 +389,18 @@ convstr_id3v1 (const char* str, int sz) { } // check for utf8 (hack) - iconv_t cd; - cd = iconv_open (UTF8, UTF8); - if (cd == (iconv_t)-1) { - trace ("iconv doesn't support utf8\n"); - return str; - } - size_t inbytesleft = sz; - size_t outbytesleft = 2047; -#ifdef __linux__ - char *pin = (char*)str; -#else - const char *pin = str; -#endif - char *pout = out; - memset (out, 0, sizeof (out)); - size_t res = iconv (cd, &pin, &inbytesleft, &pout, &outbytesleft); - iconv_close (cd); - if (res == 0) { - strncpy (out, str, 2047); - out[min (sz, 2047)] = 0; + if (junk_iconv (str, sz, out, sizeof (out), UTF8, UTF8) > 0) { return out; } - const char *enc = "iso8859-1"; if (can_be_russian (str)) { enc = "cp1251"; } - cd = iconv_open (UTF8, enc); - if (cd == (iconv_t)-1) { - trace ("iconv can't recode from %s to utf8\n", enc); - return str; - } - else { - size_t inbytesleft = sz; - size_t outbytesleft = 2047; -#ifdef __linux__ - char *pin = (char*)str; -#else - const char *pin = str; -#endif - char *pout = out; - memset (out, 0, sizeof (out)); - /*size_t res = */iconv (cd, &pin, &inbytesleft, &pout, &outbytesleft); - iconv_close (cd); + + if (junk_iconv (str, sz, out, sizeof (out), enc, UTF8) > 0) { + return out; } - return out; + return NULL; } static void @@ -2849,26 +2746,8 @@ junk_detect_charset (const char *s) { return "iso8859-1"; } -void +int junk_recode (const char *in, int inlen, char *out, int outlen, const char *cs) { - iconv_t cd = iconv_open (UTF8, cs); - if (cd == (iconv_t)-1) { - trace ("iconv can't recode from %s to utf8\n", cs); - memcpy (out, in, min(inlen, outlen)); - return; - } - else { - size_t inbytesleft = inlen; - size_t outbytesleft = outlen; -#ifdef __linux__ - char *pin = (char*)in; -#else - const char *pin = in; -#endif - char *pout = out; - memset (out, 0, outlen); - size_t res = iconv (cd, &pin, &inbytesleft, &pout, &outbytesleft); - iconv_close (cd); - } + return junk_iconv (in, inlen, out, outlen, cs, UTF8); } diff --git a/junklib.h b/junklib.h index cde8f078..b9ac948d 100644 --- a/junklib.h +++ b/junklib.h @@ -98,7 +98,10 @@ junk_get_leading_size (DB_FILE *fp); const char * junk_detect_charset (const char *s); -void +int +junk_iconv (const char *in, int inlen, char *out, int outlen, const char *cs_in, const char *cs_out); + +int junk_recode (const char *in, int inlen, char *out, int outlen, const char *cs); #endif // __JUNKLIB_H diff --git a/plugins.c b/plugins.c index e91d8eff..53e5ecf4 100644 --- a/plugins.c +++ b/plugins.c @@ -208,6 +208,7 @@ static DB_functions_t deadbeef_api = { .junk_get_leading_size_stdio = junk_get_leading_size_stdio, .junk_detect_charset = junk_detect_charset, .junk_recode = junk_recode, + .junk_iconv = junk_iconv, // vfs .fopen = vfs_fopen, .fclose = vfs_fclose, -- cgit v1.2.3 From 036c4ee543da711c0a601c84d56cc5e2be93d2b0 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Mon, 5 Apr 2010 17:05:57 +0200 Subject: fixed cross-plaftorm issue in junk_iconv --- junklib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/junklib.c b/junklib.c index b103ff4e..b781f7cf 100644 --- a/junklib.c +++ b/junklib.c @@ -113,7 +113,7 @@ junk_iconv (const char *in, int inlen, char *out, int outlen, const char *cs_in, #ifdef __linux__ char *pin = (char*)in; #else - const char *pin = value; + const char *pin = in; #endif size_t inbytesleft = inlen; -- cgit v1.2.3 From b53e7fc73342b76fac27cb7197e4ee6f3d3fa0d0 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Mon, 5 Apr 2010 17:07:08 +0200 Subject: disabled warning level cflags in configure.ac --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 369becdf..e55240a6 100644 --- a/configure.ac +++ b/configure.ac @@ -35,7 +35,7 @@ esac test "x$prefix" = xNONE && prefix=$ac_default_prefix -INSANE_CFLAGS="-Wcomment -Wchar-subscripts -Wunused-function -Wunused-value -Wuninitialized -Wtype-limits -Wbad-function-cast" +dnl INSANE_CFLAGS="-Wcomment -Wchar-subscripts -Wunused-function -Wunused-value -Wuninitialized -Wtype-limits -Wbad-function-cast" dnl INSANE_CXXFLAGS="-Wcomment -Wchar-subscripts -Wunused-function -Wunused-value -Wuninitialized -Wtype-limits" AC_SUBST(INSANE_CFLAGS) -- cgit v1.2.3 From fea077efa41959daee6e091a9029293210eaf9d1 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Mon, 5 Apr 2010 17:23:43 +0200 Subject: restrict pthread_setschedprio to linux --- threading_pthread.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/threading_pthread.c b/threading_pthread.c index 4f7cba1a..e6b9df9c 100644 --- a/threading_pthread.c +++ b/threading_pthread.c @@ -48,6 +48,7 @@ thread_start (void (*fn)(void *ctx), void *ctx) { intptr_t thread_start_low_priority (void (*fn)(void *ctx), void *ctx) { +#ifdef __linux__ pthread_t tid; pthread_attr_t attr; int s = pthread_attr_init (&attr); @@ -82,6 +83,9 @@ thread_start_low_priority (void (*fn)(void *ctx), void *ctx) { return 0; } return tid; +#else + return thread_start (fn, ctx); +#endif } int -- cgit v1.2.3 From 982a91aaf02f65bea0db8ef0e7bf03f60663f111 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Mon, 5 Apr 2010 17:24:10 +0200 Subject: use junklib iconv functions instead of directly calling iconv --- cdumb.c | 60 ++++++------------------------------------------------------ 1 file changed, 6 insertions(+), 54 deletions(-) diff --git a/cdumb.c b/cdumb.c index 289d2fb5..83c07d31 100644 --- a/cdumb.c +++ b/cdumb.c @@ -18,8 +18,6 @@ #include #include #include -#define LIBICONV_PLUG -#include #include "dumb/dumb-kode54/include/dumb.h" #include "dumb/dumb-kode54/include/internal/it.h" #include "deadbeef.h" @@ -691,62 +689,16 @@ static const char *convstr (const char* str, int sz) { } // check for utf8 (hack) - iconv_t cd; - cd = iconv_open ("utf-8", "utf-8"); - if (cd == (iconv_t)-1) { - trace ("iconv doesn't support utf8\n"); - return str; - } - size_t inbytesleft = sz; - size_t outbytesleft = 2047; -#ifdef __linux__ - char *pin = (char*)str; -#else - const char *pin = str; -#endif - char *pout = out; - memset (out, 0, sizeof (out)); - size_t res = iconv (cd, &pin, &inbytesleft, &pout, &outbytesleft); - iconv_close (cd); - if (res == 0) { - strncpy (out, str, 2047); - out[min (sz, 2047)] = 0; + if (deadbeef->junk_iconv (str, sz, out, sizeof (out), "utf-8", "utf-8") >= 0) { return out; } - const char *enc = "iso8859-1"; -#if 0 - int latin = 0; - int rus = 0; - for (int i = 0; i < sz; i++) { - if ((str[i] >= 'A' && str[i] <= 'Z') - || str[i] >= 'a' && str[i] <= 'z') { - latin++; - } - else if (str[i] < 0) { - rus++; - } - } - if (rus > latin/2) { - // might be russian - enc = "cp1251"; - } -#endif - cd = iconv_open ("utf-8", enc); - if (cd == (iconv_t)-1) { - trace ("iconv can't recode from %s to utf8\n", enc); - return str; - } - else { - size_t inbytesleft = sz; - size_t outbytesleft = 2047; - char *pin = (char*)str; - char *pout = out; - memset (out, 0, sizeof (out)); - /*size_t res = */iconv (cd, &pin, &inbytesleft, &pout, &outbytesleft); - iconv_close (cd); + if (deadbeef->junk_iconv (str, sz, out, sizeof (out), "utf-8", "iso8859-1") >= 0) { + return out; } - return out; + + trace ("cdumb: failed to detect charset\n"); + return NULL; } static DB_playItem_t * -- cgit v1.2.3 From dcd098ef53a71e2137fab62e0bdc5a9efc8ded75 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Mon, 5 Apr 2010 17:59:03 +0200 Subject: added CONTRIBUTING file --- CONTRIBUTING | 1 + 1 file changed, 1 insertion(+) create mode 100644 CONTRIBUTING diff --git a/CONTRIBUTING b/CONTRIBUTING new file mode 100644 index 00000000..a990ffca --- /dev/null +++ b/CONTRIBUTING @@ -0,0 +1 @@ +see following link: http://contributing.appspot.com/deadbeef -- cgit v1.2.3 From 4de59f6b45b6e71df9fda01ae9d1ed356d3eae98 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Mon, 5 Apr 2010 17:59:24 +0200 Subject: fixed iconv linking regression on BSD systems --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 91d16383..aea1d4cd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -52,7 +52,7 @@ deadbeef_SOURCES =\ sdkdir = $(pkgincludedir) sdk_HEADERS = deadbeef.h -deadbeef_LDADD = $(LDADD) $(DEPS_LIBS) gme/Game_Music_Emu-0.5.2/gme/libgme.a dumb/libdumb.a -lstdc++ -lm +deadbeef_LDADD = $(LDADD) $(DEPS_LIBS) $(ICONV_LIB) gme/Game_Music_Emu-0.5.2/gme/libgme.a dumb/libdumb.a -lstdc++ -lm AM_CFLAGS = $(DEPS_CFLAGS) -I$(gmepath) -std=c99 AM_CPPFLAGS = $(DEPS_CFLAGS) -- cgit v1.2.3 From fb8fc73d1012451a91df0e68435b30b68fb8020a Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Mon, 5 Apr 2010 19:59:33 +0200 Subject: added multiline id3v2 frames support --- junklib.c | 69 ++++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 44 insertions(+), 25 deletions(-) diff --git a/junklib.c b/junklib.c index b781f7cf..ce7d0149 100644 --- a/junklib.c +++ b/junklib.c @@ -314,21 +314,31 @@ can_be_russian (const signed char *str) { static char * convstr_id3v2 (int version, uint8_t encoding, const unsigned char* str, int sz) { - static char out[2048]; - const char *enc = "iso8859-1"; + char out[2048] = ""; + const char *enc = NULL; - if (version == 4 && encoding == 3) { - // utf8 - trace ("utf8\n"); - strncpy (out, str, 2047); - out[min (sz, 2047)] = 0; - return strdup (out); - } - else if (version == 4 && encoding == 2) { + // detect encoding + if (version == 4 && encoding == 2) { trace ("utf16be\n"); enc = "UTF-16BE"; } - else if (encoding == 1) { + else if (version == 4 && encoding == 3) { + enc = UTF8; + } + else if (encoding == 0) { + // hack to add limited cp1251 recoding support + if (can_be_russian (str)) { + enc = "cp1251"; + } + } + else if (encoding != 1 && !(version == 4 && encoding == 3)){ + return NULL; // invalid encoding + } + + if (encoding == 1) { // detect kind of unicode used + if (sz < 2) { + return NULL; + } if (version < 4) { if (str[0] == 0xff && str[1] == 0xfe) { enc = "UCS-2LE"; @@ -350,32 +360,34 @@ convstr_id3v2 (int version, uint8_t encoding, const unsigned char* str, int sz) enc = "UTF-16"; } } -#if 0 - // NOTE: some dumb taggers put non-iso8859-1 text with enc=0 - else if (*str == 0) { - // iso8859-1 - trace ("iso8859-1\n"); - enc = "iso8859-1"; - } -#endif else if (encoding == 0) { // hack to add limited cp1251 recoding support if (can_be_russian (str)) { enc = "cp1251"; } + else { + enc = "iso8859-1"; + } } - else { - return 0; // invalid encoding + + int converted_sz = 0; + + if ((converted_sz = junk_iconv (str, sz, out, sizeof (out), enc, UTF8)) < 0) { + return NULL; } - if (junk_iconv (str, sz, out, sizeof (out), enc, UTF8) > 0) { - trace ("converted: %s\n", out); - return strdup (out); + else { + for (int n = 0; n < converted_sz; n++) { + if (out[n] == 0 && n != converted_sz-1) { + out[n] = '\n'; + } + } } - return NULL; + return strdup (out); } static const char * convstr_id3v1 (const char* str, int sz) { + trace ("convstr_id3v1\n"); static char out[2048]; int i; for (i = 0; i < sz; i++) { @@ -2441,7 +2453,14 @@ junk_id3v2_read_full (playItem_t *it, DB_id3v2_tag_t *tag_store, DB_FILE *fp) { trace ("frame %s is too big, discard\n", frameid); break; } + char *text = convstr_id3v2 (version_major, readptr[0], readptr+1, synched_size-1); + + // couple of simple tests + //char *text = convstr_id3v2 (4, 3, "текст1\0текст2", strlen ("текст1")*2+2); + //const char ucstext[] = { 0x42, 0x04, 0x35, 0x04, 0x3a, 0x04, 0x41, 0x04, 0x42, 0x04, 0x31, 0x00, 0x00, 0x00, 0x42, 0x04, 0x35, 0x04, 0x3a, 0x04, 0x41, 0x04, 0x42, 0x04, 0x32, 0x00 }; + //char *text = convstr_id3v2 (4, 1, ucstext, sizeof (ucstext)); + if (text && *text && text_holders[f]) { if (*text_holders[f]) { // append -- cgit v1.2.3 From 9d920d33780718bc77b55449382882aa244d0301 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Mon, 5 Apr 2010 19:59:52 +0200 Subject: small website update --- web/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/index.html b/web/index.html index abdcbf2c..e4f8cb87 100644 --- a/web/index.html +++ b/web/index.html @@ -183,8 +183,8 @@

Reporting Bugs And Requesting Features

Trackers for reporting bugs, requesting features, etc are here

Developer Information

-

if you want to contribute a patch, simply clone git repository, make new feature or fix a bug, and use "git format-patch" to create patch. then you can send it to me over email.

-

see project site for details.

+

How to contribute: http://contributing.appspot.com/deadbeef

+

Also, see project site.

Contacts

official IRC channel (for english speaking people) is: #deadbeefplayer @ freenode

official jabber conference for russian speaking users is here: deadbeef-ru@conference.jabber.ru

-- cgit v1.2.3 From a0565f661fdacfd50f543bc2bee085fd06860a24 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Mon, 5 Apr 2010 22:06:17 +0200 Subject: fixed embedded cuesheed reading from apev2 --- junklib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/junklib.c b/junklib.c index ce7d0149..6d8b0ccb 100644 --- a/junklib.c +++ b/junklib.c @@ -32,6 +32,7 @@ #endif #define MAX_TEXT_FRAME_SIZE 1024 +#define MAX_CUESHEET_FRAME_SIZE 10000 #define MAX_APEV2_FRAME_SIZE 100000 #define MAX_ID3V2_FRAME_SIZE 100000 @@ -779,7 +780,7 @@ junk_apev2_read_full (playItem_t *it, DB_apev2_tag_t *tag_store, DB_FILE *fp) { int valuetype = ((itemflags >> 1) & 3); // add metainfo only if it's textual - if (valuetype == 0 && itemsize < MAX_TEXT_FRAME_SIZE) { + if (valuetype == 0 && (itemsize < MAX_TEXT_FRAME_SIZE || (!strcasecmp (key, "cuesheet") && itemsize < MAX_CUESHEET_FRAME_SIZE))) { if (!u8_valid (value, itemsize, NULL)) { trace ("junk_read_ape_full: bad encoding in text frame %s\n", key); continue; -- cgit v1.2.3 From 0b207476b362f6453fec73432416268317454425 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Mon, 5 Apr 2010 22:06:49 +0200 Subject: copy tags frame from origin track into all cue tracks; add cuesheet tag type to tags list of all cue tracks --- playlist.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/playlist.c b/playlist.c index 09e656f0..abaa4d7d 100644 --- a/playlist.c +++ b/playlist.c @@ -779,6 +779,7 @@ pl_insert_cue_from_buffer (playItem_t *after, playItem_t *origin, const uint8_t } // copy metadata from embedded tags playItem_t *first = ins ? ins->next[PL_MAIN] : playlist->head[PL_MAIN]; + pl_append_meta (origin, "tags", "cuesheet"); pl_items_copy_junk (origin, first, after); UNLOCK; return after; @@ -2758,6 +2759,7 @@ pl_items_copy_junk (playItem_t *from, playItem_t *first, playItem_t *last) { const char *copyright = pl_find_meta (from, "copyright"); const char *vendor = pl_find_meta (from, "vendor"); const char *comment = pl_find_meta (from, "comment"); + const char *tags = pl_find_meta (from, "tags"); playItem_t *i; for (i = first; i; i = i->next[PL_MAIN]) { if (year) { @@ -2775,6 +2777,9 @@ pl_items_copy_junk (playItem_t *from, playItem_t *first, playItem_t *last) { if (comment) { pl_add_meta (i, "comment", comment); } + if (tags) { + pl_add_meta (i, "tags", tags); + } if (i == last) { break; } -- cgit v1.2.3 From e4550ae4025c64122f1e79a4b2de8d4271738b4a Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Mon, 5 Apr 2010 22:08:10 +0200 Subject: added tag reloading to wavpack plugin; added some experimental code to wavpack plugin --- plugins/wavpack/wavpack.c | 83 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/plugins/wavpack/wavpack.c b/plugins/wavpack/wavpack.c index 931a9430..ffbd1bd1 100644 --- a/plugins/wavpack/wavpack.c +++ b/plugins/wavpack/wavpack.c @@ -41,30 +41,30 @@ typedef struct { } wvctx_t; int32_t wv_read_bytes(void *id, void *data, int32_t bcount) { - trace ("wv_read_bytes\n"); +// trace ("wv_read_bytes\n"); return deadbeef->fread (data, 1, bcount, id); } uint32_t wv_get_pos(void *id) { - trace ("wv_get_pos\n"); +// trace ("wv_get_pos\n"); return deadbeef->ftell (id); } int wv_set_pos_abs(void *id, uint32_t pos) { - trace ("wv_set_pos_abs\n"); +// trace ("wv_set_pos_abs\n"); return deadbeef->fseek (id, pos, SEEK_SET); } int wv_set_pos_rel(void *id, int32_t delta, int mode) { - trace ("wv_set_pos_rel\n"); +// trace ("wv_set_pos_rel\n"); return deadbeef->fseek (id, delta, SEEK_CUR); } int wv_push_back_byte(void *id, int c) { - trace ("wv_push_back_byte\n"); +// trace ("wv_push_back_byte\n"); deadbeef->fseek (id, -1, SEEK_CUR); return deadbeef->ftell (id); } uint32_t wv_get_length(void *id) { - trace ("wv_get_length\n"); +// trace ("wv_get_length\n"); size_t pos = deadbeef->ftell (id); deadbeef->fseek (id, 0, SEEK_END); size_t sz = deadbeef->ftell (id); @@ -72,7 +72,7 @@ uint32_t wv_get_length(void *id) { return sz; } int wv_can_seek(void *id) { - trace ("wv_can_seek\n"); +// trace ("wv_can_seek\n"); return 1; } @@ -233,7 +233,6 @@ wv_insert (DB_playItem_t *after, const char *fname) { } int totalsamples = WavpackGetNumSamples (ctx); int samplerate = WavpackGetSampleRate (ctx); - WavpackCloseFile (ctx); float duration = (float)totalsamples / samplerate; DB_playItem_t *it = deadbeef->pl_item_alloc (); @@ -241,9 +240,19 @@ wv_insert (DB_playItem_t *after, const char *fname) { it->fname = strdup (fname); it->filetype = "wv"; deadbeef->pl_set_item_duration (it, duration); - trace ("wv: totalsamples=%d, samplerate=%d, duration=%f\n", totalsamples, samplerate, duration); +#if 0 + int num = WavpackGetNumTagItems (ctx); + trace ("num tag items: %d\n", num); + + for (int i = 0; i < num; i++) { + char str[1024]; + WavpackGetTagItemIndexed (ctx, i, str, sizeof (str)); + trace ("tag item: %s\n", str); + } + +#endif int apeerr = deadbeef->junk_apev2_read (it, fp); if (!apeerr) { trace ("wv: ape tag found\n"); @@ -252,30 +261,75 @@ wv_insert (DB_playItem_t *after, const char *fname) { if (!v1err) { trace ("wv: id3v1 tag found\n"); } - deadbeef->fclose (fp); - DB_playItem_t *cue_after = deadbeef->pl_insert_cue (after, it, totalsamples, samplerate); - if (cue_after) { - return cue_after; +#if 0 + // embedded cue + char *emb_cuesheet; + int len = WavpackGetTagItem (ctx, "cuesheet", NULL, 0); + if (len) { + emb_cuesheet = malloc (len); + if (emb_cuesheet) { + WavpackGetTagItem (ctx, "Cuesheet", emb_cuesheet, len); + trace ("got cuesheet\n%s\n", emb_cuesheet); + DB_playItem_t *last = deadbeef->pl_insert_cue_from_buffer (after, it, emb_cuesheet, strlen (emb_cuesheet), totalsamples, samplerate); + free (emb_cuesheet); + if (last) { + deadbeef->pl_item_unref (it); + deadbeef->fclose (fp); + WavpackCloseFile (ctx); + return last; + } + trace ("pl_insert_cue_from_buffer failed!\n"); + } } +#endif // embedded cue const char *cuesheet = deadbeef->pl_find_meta (it, "cuesheet"); if (cuesheet) { + trace ("found cuesheet: %s\n", cuesheet); DB_playItem_t *last = deadbeef->pl_insert_cue_from_buffer (after, it, cuesheet, strlen (cuesheet), totalsamples, samplerate); if (last) { + deadbeef->fclose (fp); + WavpackCloseFile (ctx); deadbeef->pl_item_unref (it); return last; } } + // cue file on disc + DB_playItem_t *cue_after = deadbeef->pl_insert_cue (after, it, totalsamples, samplerate); + if (cue_after) { + deadbeef->fclose (fp); + WavpackCloseFile (ctx); + return cue_after; + } - deadbeef->pl_add_meta (it, "title", NULL); after = deadbeef->pl_insert_item (after, it); deadbeef->pl_item_unref (it); + deadbeef->fclose (fp); + WavpackCloseFile (ctx); return after; } +int +wv_read_metadata (DB_playItem_t *it) { + DB_FILE *fp = deadbeef->fopen (it->fname); + if (!fp) { + return -1; + } + int apeerr = deadbeef->junk_apev2_read (it, fp); + if (!apeerr) { + trace ("wv: ape tag found\n"); + } + int v1err = deadbeef->junk_id3v1_read (it, fp); + if (!v1err) { + trace ("wv: id3v1 tag found\n"); + } + deadbeef->fclose (fp); + return 0; +} + static const char * exts[] = { "wv", NULL }; static const char *filetypes[] = { "wv", NULL }; @@ -298,6 +352,7 @@ static DB_decoder_t plugin = { .seek = wv_seek, .seek_sample = wv_seek_sample, .insert = wv_insert, + .read_metadata = wv_read_metadata, .exts = exts, .filetypes = filetypes }; -- cgit v1.2.3 From ddbca9d6a76f38b8951eb5ac5da19ce9f2b340cc Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 6 Apr 2010 12:46:07 +0200 Subject: fixed id3v2 txx parser to respect encoding --- junklib.c | 64 +++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/junklib.c b/junklib.c index 6d8b0ccb..5a99eb27 100644 --- a/junklib.c +++ b/junklib.c @@ -2187,40 +2187,44 @@ junk_load_comm_frame (int version_major, playItem_t *it, uint8_t *readptr, int s int junk_id3v2_load_txx (int version_major, playItem_t *it, uint8_t *readptr, int synched_size) { - uint8_t *p = readptr; - uint8_t encoding = *p; - p++; - uint8_t *desc = p; - int desc_sz = 0; - while (*p && p - readptr < synched_size) { - p++; - desc_sz++; - } - p++; - if (p - readptr >= synched_size) { - trace ("bad TXXX frame, skipped\n"); + char *txx = convstr_id3v2 (version_major, *readptr, readptr+1, synched_size-1); + if (!txx) { return -1; } - // FIXME: decode properly using frame encoding - char *desc_s = desc; - char *value_s = p; - //trace ("value=%s\n", value_s); - if (!strcasecmp (desc_s, "replaygain_album_gain")) { - it->replaygain_album_gain = atof (value_s); - trace ("%s=%s (%f)\n", desc_s, value_s, it->replaygain_album_gain); - } - else if (!strcasecmp (desc_s, "replaygain_album_peak")) { - it->replaygain_album_peak = atof (value_s); - trace ("%s=%s (%f)\n", desc_s, value_s, it->replaygain_album_peak); - } - else if (!strcasecmp (desc_s, "replaygain_track_gain")) { - it->replaygain_track_gain = atof (value_s); - trace ("%s=%s (%f)\n", desc_s, value_s, it->replaygain_track_gain); + + char *val = NULL; + if (txx) { + char *p; + for (p = txx; *p; p++) { + if (*p == '\n') { + *p = 0; + val = p+1; + break; + } + } } - else if (!strcasecmp (desc_s, "replaygain_track_peak")) { - it->replaygain_track_peak = atof (value_s); - trace ("%s=%s (%f)\n", desc_s, value_s, it->replaygain_track_peak); + + if (val) { + if (!strcasecmp (txx, "replaygain_album_gain")) { + it->replaygain_album_gain = atof (val); + trace ("%s=%s (%f)\n", txx, val, it->replaygain_album_gain); + } + else if (!strcasecmp (txx, "replaygain_album_peak")) { + it->replaygain_album_peak = atof (val); + trace ("%s=%s (%f)\n", txx, val, it->replaygain_album_peak); + } + else if (!strcasecmp (txx, "replaygain_track_gain")) { + it->replaygain_track_gain = atof (val); + trace ("%s=%s (%f)\n", txx, val, it->replaygain_track_gain); + } + else if (!strcasecmp (txx, "replaygain_track_peak")) { + it->replaygain_track_peak = atof (val); + trace ("%s=%s (%f)\n", txx, val, it->replaygain_track_peak); + } } + free (txx); + + return 0; } int -- cgit v1.2.3 From 0bfe67e0add4c8aa0d6920e5f8867ea64cceaf9b Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 6 Apr 2010 13:19:58 +0200 Subject: don't draw seekbar if window is not visible --- plugins/gtkui/gtkui.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/gtkui/gtkui.c b/plugins/gtkui/gtkui.c index ab26084f..143a3307 100644 --- a/plugins/gtkui/gtkui.c +++ b/plugins/gtkui/gtkui.c @@ -328,6 +328,10 @@ redraw_queued_tracks (DdbListview *pl, int list) { static gboolean redraw_queued_tracks_cb (gpointer nothing) { + int iconified = gdk_window_get_state(mainwin->window) & GDK_WINDOW_STATE_ICONIFIED; + if (!GTK_WIDGET_VISIBLE (mainwin) || iconified) { + return FALSE; + } redraw_queued_tracks (DDB_LISTVIEW (lookup_widget (mainwin, "playlist")), PL_MAIN); redraw_queued_tracks (DDB_LISTVIEW (lookup_widget (searchwin, "searchlist")), PL_SEARCH); return FALSE; @@ -656,6 +660,10 @@ update_win_title_idle (gpointer data) { static gboolean redraw_seekbar_cb (gpointer nothing) { + int iconified = gdk_window_get_state(mainwin->window) & GDK_WINDOW_STATE_ICONIFIED; + if (!GTK_WIDGET_VISIBLE (mainwin) || iconified) { + return FALSE; + } seekbar_redraw (); return FALSE; } -- cgit v1.2.3 From 5972f25cc2efd09afaab2f82bf236049285ba3e3 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 6 Apr 2010 13:26:21 +0200 Subject: don't put text with line breaks into window title --- plugins/gtkui/gtkui.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/plugins/gtkui/gtkui.c b/plugins/gtkui/gtkui.c index 143a3307..60c996cf 100644 --- a/plugins/gtkui/gtkui.c +++ b/plugins/gtkui/gtkui.c @@ -374,12 +374,10 @@ static void current_track_changed (DB_playItem_t *it) { char str[600]; if (it) { - char dname[512]; - deadbeef->pl_format_item_display_name (it, dname, 512); - snprintf (str, sizeof (str), "DeaDBeeF - %s", dname); + deadbeef->pl_format_title (it, -1, str, sizeof (str), -1, "DeaDBeeF-" VERSION " - %a - %t"); } else { - strcpy (str, "DeaDBeeF"); + strcpy (str, "DeaDBeeF-" VERSION); } gtk_window_set_title (GTK_WINDOW (mainwin), str); set_tray_tooltip (str); @@ -649,8 +647,8 @@ update_win_title_idle (gpointer data) { } } else { - gtk_window_set_title (GTK_WINDOW (mainwin), "DeaDBeeF"); - set_tray_tooltip ("DeaDBeeF"); + gtk_window_set_title (GTK_WINDOW (mainwin), "DeaDBeeF-" VERSION); + set_tray_tooltip ("DeaDBeeF-" VERSION); } } // update playlist view @@ -842,6 +840,7 @@ gtkui_thread (void *ctx) { // playlist_refresh (); // ddb_listview_set_vscroll (main_playlist, scroll); + gtk_window_set_title (GTK_WINDOW (mainwin), "DeaDBeeF-" VERSION); gtk_initialized = 1; gtk_main (); cover_art_free (); -- cgit v1.2.3 From 8eb57ca535ea9a07c46d8d3bf3e824a4e678f535 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 6 Apr 2010 13:29:17 +0200 Subject: removed obsolete function pl_format_item_display_name --- playlist.c | 15 --------------- playlist.h | 3 --- plugins.c | 1 - 3 files changed, 19 deletions(-) diff --git a/playlist.c b/playlist.c index abaa4d7d..ccc4646c 100644 --- a/playlist.c +++ b/playlist.c @@ -1536,21 +1536,6 @@ pl_replace_meta (playItem_t *it, const char *key, const char *value) { UNLOCK; } -void -pl_format_item_display_name (playItem_t *it, char *str, int len) { - LOCK; - const char *artist = pl_find_meta (it, "artist"); - const char *title = pl_find_meta (it, "title"); - if (!artist) { - artist = "Unknown artist"; - } - if (!title) { - title = "Unknown title"; - } - snprintf (str, len, "%s - %s", artist, title); - UNLOCK; -} - const char * pl_find_meta (playItem_t *it, const char *key) { DB_metaInfo_t *m = it->meta; diff --git a/playlist.h b/playlist.h index b6c8403f..26655f5f 100644 --- a/playlist.h +++ b/playlist.h @@ -195,9 +195,6 @@ pl_find_meta (playItem_t *it, const char *key); void pl_replace_meta (playItem_t *it, const char *key, const char *value); -void -pl_format_item_display_name (playItem_t *it, char *str, int len); - void pl_delete_all_meta (playItem_t *it); diff --git a/plugins.c b/plugins.c index 53e5ecf4..03246303 100644 --- a/plugins.c +++ b/plugins.c @@ -155,7 +155,6 @@ static DB_functions_t deadbeef_api = { .pl_get_next = (DB_playItem_t *(*) (DB_playItem_t *, int))pl_get_next, .pl_get_prev = (DB_playItem_t *(*) (DB_playItem_t *, int))pl_get_prev, .pl_format_title = (int (*) (DB_playItem_t *it, int idx, char *s, int size, int id, const char *fmt))pl_format_title, - .pl_format_item_display_name = (void (*) (DB_playItem_t *it, char *str, int len))pl_format_item_display_name, .pl_move_items = (void (*) (int iter, DB_playItem_t *drop_before, uint32_t *indexes, int count))pl_move_items, .pl_copy_items = (void (*) (int iter, int plt_from, DB_playItem_t *before, uint32_t *indices, int cnt))pl_copy_items, .pl_search_reset = pl_search_reset, -- cgit v1.2.3 From e84b9cbebdd0be7e2c20b03474ae09330e1ea4a1 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 6 Apr 2010 13:33:44 +0200 Subject: added Escape shortcut for Deselect all; fixed couple of potential keyboard handling bugs --- plugins/gtkui/callbacks.c | 2 +- plugins/gtkui/ddblistview.c | 2 +- plugins/gtkui/deadbeef.glade | 19 ++++----- plugins/gtkui/interface.c | 93 +++++++++++++++++++++++--------------------- 4 files changed, 60 insertions(+), 56 deletions(-) diff --git a/plugins/gtkui/callbacks.c b/plugins/gtkui/callbacks.c index cacba4b0..fffda154 100644 --- a/plugins/gtkui/callbacks.c +++ b/plugins/gtkui/callbacks.c @@ -292,7 +292,7 @@ on_mainwin_key_press_event (GtkWidget *widget, gpointer user_data) { - if (event->keyval == GDK_n && !(event->state&(GDK_SHIFT_MASK|GDK_CONTROL_MASK|GDK_MOD1_MASK))) { + if (event->keyval == GDK_n && !event->state) { // button for that one is not in toolbar anymore, so handle it manually deadbeef->sendmessage (M_PLAYRANDOM, 0, 0, 0); } diff --git a/plugins/gtkui/ddblistview.c b/plugins/gtkui/ddblistview.c index 441e126b..33840b86 100644 --- a/plugins/gtkui/ddblistview.c +++ b/plugins/gtkui/ddblistview.c @@ -1725,7 +1725,7 @@ ddb_listview_handle_keypress (DdbListview *ps, int keyval, int state) { GtkWidget *range = ps->scrollbar; GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (range)); - if (state & (GDK_CONTROL_MASK|GDK_MOD1_MASK)) { + if (state & ~(GDK_SHIFT_MASK)) { return 0; } diff --git a/plugins/gtkui/deadbeef.glade b/plugins/gtkui/deadbeef.glade index bd0ec65d..03eb8cf4 100644 --- a/plugins/gtkui/deadbeef.glade +++ b/plugins/gtkui/deadbeef.glade @@ -58,7 +58,7 @@ - + True gtk-open 1 @@ -85,7 +85,7 @@ - + True gtk-add 1 @@ -106,7 +106,7 @@ - + True gtk-add 1 @@ -127,7 +127,7 @@ - + True gtk-add 1 @@ -207,7 +207,7 @@ - + True gtk-quit 1 @@ -241,7 +241,7 @@ - + True gtk-clear 1 @@ -270,6 +270,7 @@ Deselect all True + @@ -299,7 +300,7 @@ - + True gtk-remove 1 @@ -558,7 +559,7 @@ - + True gtk-help 1 @@ -618,7 +619,7 @@ - + True gtk-about 1 diff --git a/plugins/gtkui/interface.c b/plugins/gtkui/interface.c index ef00fa83..f6fce1d2 100644 --- a/plugins/gtkui/interface.c +++ b/plugins/gtkui/interface.c @@ -35,14 +35,14 @@ create_mainwin (void) GtkWidget *menuitem1; GtkWidget *menuitem1_menu; GtkWidget *open; - GtkWidget *image368; + GtkWidget *image376; GtkWidget *separator2; GtkWidget *add_files; - GtkWidget *image369; + GtkWidget *image377; GtkWidget *add_folders; - GtkWidget *image370; + GtkWidget *image378; GtkWidget *add_audio_cd; - GtkWidget *image371; + GtkWidget *image379; GtkWidget *add_location1; GtkWidget *separatormenuitem1; GtkWidget *new_playlist1; @@ -51,18 +51,18 @@ create_mainwin (void) GtkWidget *playlist_save_as; GtkWidget *separator8; GtkWidget *quit; - GtkWidget *image372; + GtkWidget *image380; GtkWidget *edit1; GtkWidget *edit1_menu; GtkWidget *clear1; - GtkWidget *image373; + GtkWidget *image381; GtkWidget *select_all1; GtkWidget *deselect_all1; GtkWidget *invert_selection1; GtkWidget *selection1; GtkWidget *selection1_menu; GtkWidget *remove1; - GtkWidget *image374; + GtkWidget *image382; GtkWidget *crop1; GtkWidget *find1; GtkWidget *separator5; @@ -93,14 +93,14 @@ create_mainwin (void) GtkWidget *menuitem4; GtkWidget *menuitem4_menu; GtkWidget *help1; - GtkWidget *image375; + GtkWidget *image383; GtkWidget *changelog1; GtkWidget *separator10; GtkWidget *gpl1; GtkWidget *lgpl1; GtkWidget *separator9; GtkWidget *about1; - GtkWidget *image376; + GtkWidget *image384; GtkWidget *hbox2; GtkWidget *hbox3; GtkWidget *stopbtn; @@ -150,9 +150,9 @@ create_mainwin (void) GDK_O, (GdkModifierType) GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); - image368 = gtk_image_new_from_stock ("gtk-open", GTK_ICON_SIZE_MENU); - gtk_widget_show (image368); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (open), image368); + image376 = gtk_image_new_from_stock ("gtk-open", GTK_ICON_SIZE_MENU); + gtk_widget_show (image376); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (open), image376); separator2 = gtk_separator_menu_item_new (); gtk_widget_show (separator2); @@ -163,25 +163,25 @@ create_mainwin (void) gtk_widget_show (add_files); gtk_container_add (GTK_CONTAINER (menuitem1_menu), add_files); - image369 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU); - gtk_widget_show (image369); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_files), image369); + image377 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU); + gtk_widget_show (image377); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_files), image377); add_folders = gtk_image_menu_item_new_with_mnemonic ("Add folder(s)"); gtk_widget_show (add_folders); gtk_container_add (GTK_CONTAINER (menuitem1_menu), add_folders); - image370 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU); - gtk_widget_show (image370); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_folders), image370); + image378 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU); + gtk_widget_show (image378); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_folders), image378); add_audio_cd = gtk_image_menu_item_new_with_mnemonic ("Add Audio CD"); gtk_widget_show (add_audio_cd); gtk_container_add (GTK_CONTAINER (menuitem1_menu), add_audio_cd); - image371 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU); - gtk_widget_show (image371); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_audio_cd), image371); + image379 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_MENU); + gtk_widget_show (image379); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (add_audio_cd), image379); add_location1 = gtk_menu_item_new_with_mnemonic ("Add location"); gtk_widget_show (add_location1); @@ -223,9 +223,9 @@ create_mainwin (void) GDK_Q, (GdkModifierType) GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); - image372 = gtk_image_new_from_stock ("gtk-quit", GTK_ICON_SIZE_MENU); - gtk_widget_show (image372); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (quit), image372); + image380 = gtk_image_new_from_stock ("gtk-quit", GTK_ICON_SIZE_MENU); + gtk_widget_show (image380); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (quit), image380); edit1 = gtk_menu_item_new_with_mnemonic ("_Edit"); gtk_widget_show (edit1); @@ -238,9 +238,9 @@ create_mainwin (void) gtk_widget_show (clear1); gtk_container_add (GTK_CONTAINER (edit1_menu), clear1); - image373 = gtk_image_new_from_stock ("gtk-clear", GTK_ICON_SIZE_MENU); - gtk_widget_show (image373); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (clear1), image373); + image381 = gtk_image_new_from_stock ("gtk-clear", GTK_ICON_SIZE_MENU); + gtk_widget_show (image381); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (clear1), image381); select_all1 = gtk_menu_item_new_with_mnemonic ("Select all"); gtk_widget_show (select_all1); @@ -252,6 +252,9 @@ create_mainwin (void) deselect_all1 = gtk_menu_item_new_with_mnemonic ("Deselect all"); gtk_widget_show (deselect_all1); gtk_container_add (GTK_CONTAINER (edit1_menu), deselect_all1); + gtk_widget_add_accelerator (deselect_all1, "activate", accel_group, + GDK_Escape, (GdkModifierType) 0, + GTK_ACCEL_VISIBLE); invert_selection1 = gtk_menu_item_new_with_mnemonic ("Invert selection"); gtk_widget_show (invert_selection1); @@ -268,9 +271,9 @@ create_mainwin (void) gtk_widget_show (remove1); gtk_container_add (GTK_CONTAINER (selection1_menu), remove1); - image374 = gtk_image_new_from_stock ("gtk-remove", GTK_ICON_SIZE_MENU); - gtk_widget_show (image374); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (remove1), image374); + image382 = gtk_image_new_from_stock ("gtk-remove", GTK_ICON_SIZE_MENU); + gtk_widget_show (image382); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (remove1), image382); crop1 = gtk_menu_item_new_with_mnemonic ("Crop"); gtk_widget_show (crop1); @@ -399,9 +402,9 @@ create_mainwin (void) gtk_widget_show (help1); gtk_container_add (GTK_CONTAINER (menuitem4_menu), help1); - image375 = gtk_image_new_from_stock ("gtk-help", GTK_ICON_SIZE_MENU); - gtk_widget_show (image375); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (help1), image375); + image383 = gtk_image_new_from_stock ("gtk-help", GTK_ICON_SIZE_MENU); + gtk_widget_show (image383); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (help1), image383); changelog1 = gtk_menu_item_new_with_mnemonic ("_ChangeLog"); gtk_widget_show (changelog1); @@ -429,9 +432,9 @@ create_mainwin (void) gtk_widget_show (about1); gtk_container_add (GTK_CONTAINER (menuitem4_menu), about1); - image376 = gtk_image_new_from_stock ("gtk-about", GTK_ICON_SIZE_MENU); - gtk_widget_show (image376); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (about1), image376); + image384 = gtk_image_new_from_stock ("gtk-about", GTK_ICON_SIZE_MENU); + gtk_widget_show (image384); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (about1), image384); hbox2 = gtk_hbox_new (FALSE, 0); gtk_widget_show (hbox2); @@ -708,14 +711,14 @@ create_mainwin (void) GLADE_HOOKUP_OBJECT (mainwin, menuitem1, "menuitem1"); GLADE_HOOKUP_OBJECT (mainwin, menuitem1_menu, "menuitem1_menu"); GLADE_HOOKUP_OBJECT (mainwin, open, "open"); - GLADE_HOOKUP_OBJECT (mainwin, image368, "image368"); + GLADE_HOOKUP_OBJECT (mainwin, image376, "image376"); GLADE_HOOKUP_OBJECT (mainwin, separator2, "separator2"); GLADE_HOOKUP_OBJECT (mainwin, add_files, "add_files"); - GLADE_HOOKUP_OBJECT (mainwin, image369, "image369"); + GLADE_HOOKUP_OBJECT (mainwin, image377, "image377"); GLADE_HOOKUP_OBJECT (mainwin, add_folders, "add_folders"); - GLADE_HOOKUP_OBJECT (mainwin, image370, "image370"); + GLADE_HOOKUP_OBJECT (mainwin, image378, "image378"); GLADE_HOOKUP_OBJECT (mainwin, add_audio_cd, "add_audio_cd"); - GLADE_HOOKUP_OBJECT (mainwin, image371, "image371"); + GLADE_HOOKUP_OBJECT (mainwin, image379, "image379"); GLADE_HOOKUP_OBJECT (mainwin, add_location1, "add_location1"); GLADE_HOOKUP_OBJECT (mainwin, separatormenuitem1, "separatormenuitem1"); GLADE_HOOKUP_OBJECT (mainwin, new_playlist1, "new_playlist1"); @@ -724,18 +727,18 @@ create_mainwin (void) GLADE_HOOKUP_OBJECT (mainwin, playlist_save_as, "playlist_save_as"); GLADE_HOOKUP_OBJECT (mainwin, separator8, "separator8"); GLADE_HOOKUP_OBJECT (mainwin, quit, "quit"); - GLADE_HOOKUP_OBJECT (mainwin, image372, "image372"); + GLADE_HOOKUP_OBJECT (mainwin, image380, "image380"); GLADE_HOOKUP_OBJECT (mainwin, edit1, "edit1"); GLADE_HOOKUP_OBJECT (mainwin, edit1_menu, "edit1_menu"); GLADE_HOOKUP_OBJECT (mainwin, clear1, "clear1"); - GLADE_HOOKUP_OBJECT (mainwin, image373, "image373"); + GLADE_HOOKUP_OBJECT (mainwin, image381, "image381"); GLADE_HOOKUP_OBJECT (mainwin, select_all1, "select_all1"); GLADE_HOOKUP_OBJECT (mainwin, deselect_all1, "deselect_all1"); GLADE_HOOKUP_OBJECT (mainwin, invert_selection1, "invert_selection1"); GLADE_HOOKUP_OBJECT (mainwin, selection1, "selection1"); GLADE_HOOKUP_OBJECT (mainwin, selection1_menu, "selection1_menu"); GLADE_HOOKUP_OBJECT (mainwin, remove1, "remove1"); - GLADE_HOOKUP_OBJECT (mainwin, image374, "image374"); + GLADE_HOOKUP_OBJECT (mainwin, image382, "image382"); GLADE_HOOKUP_OBJECT (mainwin, crop1, "crop1"); GLADE_HOOKUP_OBJECT (mainwin, find1, "find1"); GLADE_HOOKUP_OBJECT (mainwin, separator5, "separator5"); @@ -764,14 +767,14 @@ create_mainwin (void) GLADE_HOOKUP_OBJECT (mainwin, menuitem4, "menuitem4"); GLADE_HOOKUP_OBJECT (mainwin, menuitem4_menu, "menuitem4_menu"); GLADE_HOOKUP_OBJECT (mainwin, help1, "help1"); - GLADE_HOOKUP_OBJECT (mainwin, image375, "image375"); + GLADE_HOOKUP_OBJECT (mainwin, image383, "image383"); GLADE_HOOKUP_OBJECT (mainwin, changelog1, "changelog1"); GLADE_HOOKUP_OBJECT (mainwin, separator10, "separator10"); GLADE_HOOKUP_OBJECT (mainwin, gpl1, "gpl1"); GLADE_HOOKUP_OBJECT (mainwin, lgpl1, "lgpl1"); GLADE_HOOKUP_OBJECT (mainwin, separator9, "separator9"); GLADE_HOOKUP_OBJECT (mainwin, about1, "about1"); - GLADE_HOOKUP_OBJECT (mainwin, image376, "image376"); + GLADE_HOOKUP_OBJECT (mainwin, image384, "image384"); GLADE_HOOKUP_OBJECT (mainwin, hbox2, "hbox2"); GLADE_HOOKUP_OBJECT (mainwin, hbox3, "hbox3"); GLADE_HOOKUP_OBJECT (mainwin, stopbtn, "stopbtn"); -- cgit v1.2.3 From f1da2ccca93bd28e7ee71247bdf05018782cb1a0 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 6 Apr 2010 13:42:49 +0200 Subject: changed from ? to Unknown * to display absent metadata --- playlist.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/playlist.c b/playlist.c index ccc4646c..1aa78bd9 100644 --- a/playlist.c +++ b/playlist.c @@ -2212,17 +2212,17 @@ pl_format_title (playItem_t *it, int idx, char *s, int size, int id, const char case DB_COLUMN_ARTIST_ALBUM: { char artistalbum[1024]; - artist = pl_get_meta_cached (it, "artist", artist, "?"); - album = pl_get_meta_cached (it, "album", album, "?"); + artist = pl_get_meta_cached (it, "artist", artist, "Unknown artist"); + album = pl_get_meta_cached (it, "album", album, "Unknown album"); snprintf (artistalbum, sizeof (artistalbum), "%s - %s", artist, album); text = artistalbum; } break; case DB_COLUMN_ARTIST: - text = (artist = pl_get_meta_cached (it, "artist", artist, "?")); + text = (artist = pl_get_meta_cached (it, "artist", artist, "Unknown artist")); break; case DB_COLUMN_ALBUM: - text = (album = pl_get_meta_cached (it, "album", artist, "?")); + text = (album = pl_get_meta_cached (it, "album", artist, "Unknown album")); break; case DB_COLUMN_TITLE: text = (title = pl_get_meta_cached (it, "title", artist, "?")); @@ -2275,13 +2275,13 @@ pl_format_title (playItem_t *it, int idx, char *s, int size, int id, const char break; } else if (*fmt == 'a') { - meta = (artist = pl_get_meta_cached (it, "artist", artist, "?")); + meta = (artist = pl_get_meta_cached (it, "artist", artist, "Unknown artist")); } else if (*fmt == 't') { meta = (title = pl_get_meta_cached (it, "title", title, "?")); } else if (*fmt == 'b') { - meta = (album = pl_get_meta_cached (it, "album", album, "?")); + meta = (album = pl_get_meta_cached (it, "album", album, "Unknown album")); } else if (*fmt == 'n') { meta = (track = pl_get_meta_cached (it, "track", track, "")); -- cgit v1.2.3 From bdc72d79ac75d88aedff946c778153492dadf471 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 6 Apr 2010 13:56:28 +0200 Subject: fixed duplicating columns in track properties --- plugins/gtkui/trkproperties.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/plugins/gtkui/trkproperties.c b/plugins/gtkui/trkproperties.c index 61df69f0..5a9c5fb9 100644 --- a/plugins/gtkui/trkproperties.c +++ b/plugins/gtkui/trkproperties.c @@ -106,10 +106,30 @@ show_track_properties_dlg (DB_playItem_t *it) { deadbeef->pl_item_ref (it); } track = it; - + GtkTreeView *tree; + GtkListStore *store; if (!trackproperties) { trackproperties = create_trackproperties (); gtk_window_set_transient_for (GTK_WINDOW (trackproperties), GTK_WINDOW (mainwin)); + tree = GTK_TREE_VIEW (lookup_widget (trackproperties, "metalist")); + store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING); + GtkCellRenderer *rend_text = gtk_cell_renderer_text_new (); + GtkCellRenderer *rend_text2 = gtk_cell_renderer_text_new (); + g_object_set (G_OBJECT (rend_text2), "editable", TRUE, NULL); + g_signal_connect ((gpointer)rend_text2, "edited", + G_CALLBACK (on_metadata_edited), + store); + GtkTreeViewColumn *col1 = gtk_tree_view_column_new_with_attributes ("Key", rend_text, "text", 0, NULL); + GtkTreeViewColumn *col2 = gtk_tree_view_column_new_with_attributes ("Value", rend_text2, "text", 1, NULL); + gtk_tree_view_append_column (tree, col1); + gtk_tree_view_append_column (tree, col2); + } + else { + tree = GTK_TREE_VIEW (lookup_widget (trackproperties, "metalist")); + store = GTK_LIST_STORE (gtk_tree_view_get_model (tree)); + + // remove everything from store + gtk_list_store_clear (store); } GtkWidget *widget = trackproperties; GtkWidget *w; @@ -118,18 +138,6 @@ show_track_properties_dlg (DB_playItem_t *it) { w = lookup_widget (widget, "location"); gtk_entry_set_text (GTK_ENTRY (w), it->fname); - GtkTreeView *tree = GTK_TREE_VIEW (lookup_widget (widget, "metalist")); - GtkListStore *store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING); - GtkCellRenderer *rend_text = gtk_cell_renderer_text_new (); - GtkCellRenderer *rend_text2 = gtk_cell_renderer_text_new (); - g_object_set (G_OBJECT (rend_text2), "editable", TRUE, NULL); - g_signal_connect ((gpointer)rend_text2, "edited", - G_CALLBACK (on_metadata_edited), - store); - GtkTreeViewColumn *col1 = gtk_tree_view_column_new_with_attributes ("Key", rend_text, "text", 0, NULL); - GtkTreeViewColumn *col2 = gtk_tree_view_column_new_with_attributes ("Value", rend_text2, "text", 1, NULL); - gtk_tree_view_append_column (tree, col1); - gtk_tree_view_append_column (tree, col2); deadbeef->pl_lock (); int i = 0; -- cgit v1.2.3 From e41f4453e2c5311a0534643c4eeddfc4bb56dfe9 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 6 Apr 2010 14:32:31 +0200 Subject: added is_local_file routine to plugin API --- deadbeef.h | 2 ++ plugins.c | 17 +++++++++++++++++ plugins.h | 3 +++ plugins/artwork/artwork.c | 18 ++---------------- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/deadbeef.h b/deadbeef.h index 80508b8d..f2e774ab 100644 --- a/deadbeef.h +++ b/deadbeef.h @@ -483,6 +483,8 @@ typedef struct { int (*plug_activate) (struct DB_plugin_s *p, int activate); const char * (*plug_get_decoder_id) (const char *id); void (*plug_remove_decoder_id) (const char *id); + // misc utilities + int (*is_local_file) (const char *fname); // returns 1 for local filename, 0 otherwise } DB_functions_t; // base plugin interface diff --git a/plugins.c b/plugins.c index 03246303..805cc6ee 100644 --- a/plugins.c +++ b/plugins.c @@ -238,6 +238,8 @@ static DB_functions_t deadbeef_api = { .plug_activate = plug_activate, .plug_get_decoder_id = plug_get_decoder_id, .plug_remove_decoder_id = plug_remove_decoder_id, + // misc utilities + .is_local_file = plug_is_local_file, }; DB_functions_t *deadbeef = &deadbeef_api; @@ -920,3 +922,18 @@ plug_get_decoder_for_id (const char *id) { } return NULL; } + +int +plug_is_local_file (const char *fname) { + if (!strncasecmp (fname, "file://", 7)) { + return 1; + } + + for (; *fname; fname++) { + if (!strncmp (fname, "://", 3)) { + return 0; + } + } + + return 1; +} diff --git a/plugins.h b/plugins.h index 7f19ceb3..3f3db11c 100644 --- a/plugins.h +++ b/plugins.h @@ -133,4 +133,7 @@ plug_free_decoder_ids (void); DB_decoder_t * plug_get_decoder_for_id (const char *id); +int +plug_is_local_file (const char *fname); + #endif // __PLUGINS_H diff --git a/plugins/artwork/artwork.c b/plugins/artwork/artwork.c index ef0c9113..aa4fc64e 100644 --- a/plugins/artwork/artwork.c +++ b/plugins/artwork/artwork.c @@ -211,20 +211,6 @@ filter_jpg (const struct dirent *f) return 0; } -static int -is_local_file (const char *fname) { - if (!strncasecmp (fname, "file://", 7)) { - return 1; - } - for (; *fname; fname++) { - if (!strncmp (fname, "://", 3)) { - return 0; - } - } - - return 1; -} - static uint8_t * id3v2_skip_str (int enc, uint8_t *ptr, uint8_t *end) { if (enc == 0 || enc == 3) { @@ -276,7 +262,7 @@ fetcher_thread (void *none) trace ("fetching cover for %s %s\n", param->album, param->artist); // try to load embedded from id3v2 - if (is_local_file (param->fname)) { + if (deadbeef->is_local_file (param->fname)) { if (deadbeef->conf_get_int ("artwork.enable_embedded", 1)) { trace ("trying to load artwork from id3v2 tag for %s\n", param->fname); DB_id3v2_tag_t tag; @@ -461,7 +447,7 @@ get_album_art (const char *fname, const char *artist, const char *album, artwork return strdup (DEFAULT_COVER_PATH); } - if (!is_local_file (fname)) { + if (!deadbeef->is_local_file (fname)) { return strdup (DEFAULT_COVER_PATH); } -- cgit v1.2.3 From 3348d1c4e5897a9b1d2ae63bf170fa7edf390435 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 6 Apr 2010 14:40:56 +0200 Subject: added delete files from disk context menu item --- plugins/gtkui/plcommon.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/plugins/gtkui/plcommon.c b/plugins/gtkui/plcommon.c index 79ca1994..9890f303 100644 --- a/plugins/gtkui/plcommon.c +++ b/plugins/gtkui/plcommon.c @@ -18,6 +18,7 @@ */ #include #include +#include #include "gtkui.h" #include "plcommon.h" #include "coverart.h" @@ -294,6 +295,42 @@ on_remove2_activate (GtkMenuItem *menuitem, search_refresh (); } +void +on_remove_from_disk_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + GtkWidget *widget = GTK_WIDGET (menuitem); + + GtkWidget *dlg = gtk_message_dialog_new (GTK_WINDOW (mainwin), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, "Delete files from disk"); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dlg), "Files will be lost. Proceed?"); + + int response = gtk_dialog_run (GTK_DIALOG (dlg)); + gtk_widget_destroy (dlg); + if (response != GTK_RESPONSE_YES) { + return; + } + + deadbeef->pl_lock (); + deadbeef->plt_lock (); + + DB_playItem_t *it = deadbeef->pl_get_first (PL_MAIN); + while (it) { + if (deadbeef->pl_is_selected (it) && deadbeef->is_local_file (it->fname)) { + unlink (it->fname); + } + DB_playItem_t *next = deadbeef->pl_get_next (it, PL_MAIN); + deadbeef->pl_item_unref (it); + it = next; + } + + int cursor = deadbeef->pl_delete_selected (); + deadbeef->plt_unlock (); + deadbeef->pl_unlock (); + + main_refresh (); + search_refresh (); +} + void list_context_menu (DdbListview *listview, DdbListviewIter it, int idx) { clicked_idx = deadbeef->pl_get_idx_of (it); @@ -303,6 +340,7 @@ list_context_menu (DdbListview *listview, DdbListviewIter it, int idx) { GtkWidget *remove_from_playback_queue1; GtkWidget *separator9; GtkWidget *remove2; + GtkWidget *remove_from_disk; GtkWidget *separator8; GtkWidget *properties1; GtkWidget *reload_metadata; @@ -337,6 +375,11 @@ list_context_menu (DdbListview *listview, DdbListviewIter it, int idx) { gtk_container_add (GTK_CONTAINER (playlist_menu), remove2); gtk_object_set_data (GTK_OBJECT (remove2), "ps", listview); + remove_from_disk = gtk_menu_item_new_with_mnemonic ("Remove from disk"); + gtk_widget_show (remove_from_disk); + gtk_container_add (GTK_CONTAINER (playlist_menu), remove_from_disk); + gtk_object_set_data (GTK_OBJECT (remove_from_disk), "ps", listview); + separator8 = gtk_separator_menu_item_new (); gtk_widget_show (separator8); gtk_container_add (GTK_CONTAINER (playlist_menu), separator8); @@ -359,6 +402,9 @@ list_context_menu (DdbListview *listview, DdbListviewIter it, int idx) { g_signal_connect ((gpointer) remove2, "activate", G_CALLBACK (on_remove2_activate), NULL); + g_signal_connect ((gpointer) remove_from_disk, "activate", + G_CALLBACK (on_remove_from_disk_activate), + NULL); g_signal_connect ((gpointer) properties1, "activate", G_CALLBACK (main_properties_activate), NULL); -- cgit v1.2.3 From 580d19bec4656c404d384bebd8b5161b8bf3d0ea Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 6 Apr 2010 14:50:16 +0200 Subject: disallow editing/writing metadata in non-local streams --- plugins/gtkui/trkproperties.c | 65 +++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/plugins/gtkui/trkproperties.c b/plugins/gtkui/trkproperties.c index 5a9c5fb9..73f55061 100644 --- a/plugins/gtkui/trkproperties.c +++ b/plugins/gtkui/trkproperties.c @@ -28,12 +28,14 @@ static GtkWidget *trackproperties; static DB_playItem_t *track; +static GtkCellRenderer *rend_text2; gboolean on_trackproperties_delete_event (GtkWidget *widget, GdkEvent *event, gpointer user_data) { + rend_text2 = NULL; trackproperties = NULL; if (track) { deadbeef->pl_item_unref (track); @@ -48,11 +50,7 @@ on_trackproperties_key_press_event (GtkWidget *widget, gpointer user_data) { if (event->keyval == GDK_Escape) { - trackproperties = NULL; - if (track) { - deadbeef->pl_item_unref (track); - track = NULL; - } + on_trackproperties_delete_event (NULL, NULL, NULL); gtk_widget_destroy (widget); } return FALSE; @@ -63,12 +61,9 @@ on_closebtn_clicked (GtkButton *button, gpointer user_data) { if (trackproperties) { - if (track) { - deadbeef->pl_item_unref (track); - track = NULL; - } - gtk_widget_destroy (trackproperties); - trackproperties = NULL; + GtkWidget *w = trackproperties; + on_trackproperties_delete_event (NULL, NULL, NULL); + gtk_widget_destroy (w); } } @@ -106,6 +101,28 @@ show_track_properties_dlg (DB_playItem_t *it) { deadbeef->pl_item_ref (it); } track = it; + + + int allow_editing = 0; + + if (deadbeef->is_local_file (it->fname)) { + // get decoder plugin by id + DB_decoder_t *dec = NULL; + if (it->decoder_id) { + DB_decoder_t **decoders = deadbeef->plug_get_decoder_list (); + for (int i = 0; decoders[i]; i++) { + if (!strcmp (decoders[i]->plugin.id, it->decoder_id)) { + dec = decoders[i]; + break; + } + } + } + + if (dec && dec->write_metadata && deadbeef->conf_get_int ("enable_tag_writing", 0)) { + allow_editing = 1; + } + } + GtkTreeView *tree; GtkListStore *store; if (!trackproperties) { @@ -114,8 +131,7 @@ show_track_properties_dlg (DB_playItem_t *it) { tree = GTK_TREE_VIEW (lookup_widget (trackproperties, "metalist")); store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING); GtkCellRenderer *rend_text = gtk_cell_renderer_text_new (); - GtkCellRenderer *rend_text2 = gtk_cell_renderer_text_new (); - g_object_set (G_OBJECT (rend_text2), "editable", TRUE, NULL); + rend_text2 = gtk_cell_renderer_text_new (); g_signal_connect ((gpointer)rend_text2, "edited", G_CALLBACK (on_metadata_edited), store); @@ -131,6 +147,14 @@ show_track_properties_dlg (DB_playItem_t *it) { // remove everything from store gtk_list_store_clear (store); } + + if (allow_editing) { + g_object_set (G_OBJECT (rend_text2), "editable", TRUE, NULL); + } + else { + g_object_set (G_OBJECT (rend_text2), "editable", FALSE, NULL); + } + GtkWidget *widget = trackproperties; GtkWidget *w; const char *meta; @@ -138,7 +162,6 @@ show_track_properties_dlg (DB_playItem_t *it) { w = lookup_widget (widget, "location"); gtk_entry_set_text (GTK_ENTRY (w), it->fname); - deadbeef->pl_lock (); int i = 0; while (types[i]) { @@ -154,19 +177,7 @@ show_track_properties_dlg (DB_playItem_t *it) { } deadbeef->pl_unlock (); - // get decoder plugin by id - DB_decoder_t *dec = NULL; - if (it->decoder_id) { - DB_decoder_t **decoders = deadbeef->plug_get_decoder_list (); - for (int i = 0; decoders[i]; i++) { - if (!strcmp (decoders[i]->plugin.id, it->decoder_id)) { - dec = decoders[i]; - break; - } - } - } - - if (dec && dec->write_metadata && deadbeef->conf_get_int ("enable_tag_writing", 0)) { + if (allow_editing) { gtk_widget_set_sensitive (lookup_widget (widget, "write_tags"), TRUE); } else { -- cgit v1.2.3 From 4418d5f81f26fe30df9f065c7d1a2313340b8799 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 6 Apr 2010 14:52:32 +0200 Subject: set window title for delete files from disk confirmation dialog --- plugins/gtkui/plcommon.c | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/gtkui/plcommon.c b/plugins/gtkui/plcommon.c index 9890f303..e1a18363 100644 --- a/plugins/gtkui/plcommon.c +++ b/plugins/gtkui/plcommon.c @@ -303,6 +303,7 @@ on_remove_from_disk_activate (GtkMenuItem *menuitem, GtkWidget *dlg = gtk_message_dialog_new (GTK_WINDOW (mainwin), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, "Delete files from disk"); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dlg), "Files will be lost. Proceed?"); + gtk_window_set_title (GTK_WINDOW (dlg), "Warning"); int response = gtk_dialog_run (GTK_DIALOG (dlg)); gtk_widget_destroy (dlg); -- cgit v1.2.3 From e909f3a3def16141682485dc83fb1a7cfb075c64 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 6 Apr 2010 16:34:04 +0200 Subject: fixed hang after using search window --- playlist.c | 2 +- plugins/gtkui/search.c | 41 ----------------------------------------- 2 files changed, 1 insertion(+), 42 deletions(-) diff --git a/playlist.c b/playlist.c index 1aa78bd9..417dbf7a 100644 --- a/playlist.c +++ b/playlist.c @@ -2646,7 +2646,7 @@ pl_search_process (const char *text) { } } } - GLOBAL_LOCK; + GLOBAL_UNLOCK; } int diff --git a/plugins/gtkui/search.c b/plugins/gtkui/search.c index cfffa284..a938cd5f 100644 --- a/plugins/gtkui/search.c +++ b/plugins/gtkui/search.c @@ -456,46 +456,5 @@ search_playlist_init (GtkWidget *widget) { } } lock_column_config = 0; -#if 0 - extern GtkWidget *searchwin; - // init playlist control structure, and put it into widget user-data - memset (&search_playlist, 0, sizeof (search_playlist)); - search_playlist.title = "search"; - search_playlist.playlist = widget; - search_playlist.header = lookup_widget (searchwin, "searchheader"); - search_playlist.scrollbar = lookup_widget (searchwin, "searchscroll"); - search_playlist.hscrollbar = lookup_widget (searchwin, "searchhscroll"); - assert (search_playlist.header); - assert (search_playlist.scrollbar); -// search_playlist.pcurr = &search_current; -// search_playlist.pcount = &search_count; - search_playlist.get_count = search_get_count; -// search_playlist.multisel = 0; - search_playlist.iterator = PL_SEARCH; - search_playlist.scrollpos = 0; - search_playlist.hscrollpos = 0; -// search_playlist.row = -1; - search_playlist.clicktime = -1; - search_playlist.nvisiblerows = 0; - - // create default set of columns - DB_conf_item_t *col = deadbeef->conf_find ("search.column.", NULL); - if (!col) { - gtkpl_column_append (&search_playlist, gtkpl_column_alloc ("Artist / Album", 150, DB_COLUMN_ARTIST_ALBUM, NULL, 0)); - gtkpl_column_append (&search_playlist, gtkpl_column_alloc ("Track №", 50, DB_COLUMN_TRACK, NULL, 1)); - gtkpl_column_append (&search_playlist, gtkpl_column_alloc ("Title / Track Artist", 150, DB_COLUMN_TITLE, NULL, 0)); - gtkpl_column_append (&search_playlist, gtkpl_column_alloc ("Duration", 50, DB_COLUMN_DURATION, NULL, 0)); - } - else { - while (col) { - gtkpl_append_column_from_textdef (&search_playlist, col->value); - col = deadbeef->conf_find ("search.column.", col); - } - } - gtk_object_set_data (GTK_OBJECT (search_playlist.playlist), "ps", &search_playlist); - gtk_object_set_data (GTK_OBJECT (search_playlist.header), "ps", &search_playlist); - gtk_object_set_data (GTK_OBJECT (search_playlist.scrollbar), "ps", &search_playlist); - gtk_object_set_data (GTK_OBJECT (search_playlist.hscrollbar), "ps", &search_playlist); -#endif } -- cgit v1.2.3 From 574c5123520c587f8a0c21a01e634f31752692bf Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 6 Apr 2010 16:59:48 +0200 Subject: fixed selection bugs in search window --- plugins/gtkui/search.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/gtkui/search.c b/plugins/gtkui/search.c index a938cd5f..399cef1e 100644 --- a/plugins/gtkui/search.c +++ b/plugins/gtkui/search.c @@ -381,7 +381,7 @@ void search_handle_doubleclick (DdbListview *listview, DdbListviewIter iter, int } void search_selection_changed (DdbListviewIter it, int idx) { - DdbListview *main = DDB_LISTVIEW (lookup_widget (searchwin, "searchlist")); + DdbListview *main = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); ddb_listview_draw_row (main, main_get_idx ((DB_playItem_t *)it), it); } -- cgit v1.2.3 From e53621d5da78de3d100821a67cfef27676b52f54 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 6 Apr 2010 17:27:32 +0200 Subject: FLAC: added support for multiline metadata --- plugins/flac/flac.c | 127 +++++++++++++++++----------------------------------- 1 file changed, 42 insertions(+), 85 deletions(-) diff --git a/plugins/flac/flac.c b/plugins/flac/flac.c index fad3be29..0de7c7c6 100644 --- a/plugins/flac/flac.c +++ b/plugins/flac/flac.c @@ -473,6 +473,24 @@ cflac_init_cue_metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC_ } #endif +static const char *metainfo[] = { + "ARTIST", "artist", + "TITLE", "title", + "ALBUM", "album", + "TRACKNUMBER", "track", + "DATE", "year", + "GENRE", "genre", + "COMMENT", "comment", + "PERFORMER", "performer", + "ENSEMBLE", "band", + "COMPOSER", "composer", + "ENCODED-BY", "vendor", + "DISCNUMBER", "disc", + "COPYRIGHT", "copyright", + "TRACKTOTAL", "numtracks", + NULL +}; + static void cflac_init_metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) { flac_info_t *info = (flac_info_t *)client_data; @@ -492,80 +510,37 @@ cflac_init_metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__Str } else if (metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { const FLAC__StreamMetadata_VorbisComment *vc = &metadata->data.vorbis_comment; - int title_added = 0; for (int i = 0; i < vc->num_comments; i++) { const FLAC__StreamMetadata_VorbisComment_Entry *c = &vc->comments[i]; if (c->length > 0) { - char s[c->length+1]; - s[c->length] = 0; - memcpy (s, c->entry, c->length); - if (!strncasecmp (s, "ARTIST=", 7)) { - deadbeef->pl_add_meta (it, "artist", s + 7); - } - else if (!strncasecmp (s, "TITLE=", 6)) { - deadbeef->pl_add_meta (it, "title", s + 6); - title_added = 1; - } - else if (!strncasecmp (s, "ALBUM=", 6)) { - deadbeef->pl_add_meta (it, "album", s + 6); - } - else if (!strncasecmp (s, "TRACKNUMBER=", 12)) { - deadbeef->pl_add_meta (it, "track", s + 12); - } - else if (!strncasecmp (s, "TRACKTOTAL=", 11)) { - deadbeef->pl_add_meta (it, "numtracks", s + 11); - } - else if (!strncasecmp (s, "DATE=", 5)) { - deadbeef->pl_add_meta (it, "year", s + 5); - } - else if (!strncasecmp (s, "GENRE=", 6)) { - deadbeef->pl_add_meta (it, "genre", s + 6); - } - else if (!strncasecmp (s, "COMMENT=", 8)) { - deadbeef->pl_add_meta (it, "comment", s + 8); - } - else if (!strncasecmp (s, "PERFORMER=", 10)) { - deadbeef->pl_add_meta (it, "performer", s + 10); - } - else if (!strncasecmp (s, "ENSEMBLE=", 9)) { - deadbeef->pl_add_meta (it, "band", s + 9); - } - else if (!strncasecmp (s, "COMPOSER=", 9)) { - deadbeef->pl_add_meta (it, "composer", s + 9); - } - else if (!strncasecmp (s, "ENCODED-BY=", 11)) { - deadbeef->pl_add_meta (it, "vendor", s + 11); - } - else if (!strncasecmp (s, "DISCNUMBER=", 11)) { - deadbeef->pl_add_meta (it, "disc", s + 11); - } - else if (!strncasecmp (s, "COPYRIGHT=", 10)) { - deadbeef->pl_add_meta (it, "copyright", s + 10); - } - else if (!strncasecmp (s, "CUESHEET=", 9)) { - deadbeef->pl_add_meta (it, "cuesheet", s + 9); -// info->last = deadbeef->pl_insert_cue_from_buffer (info->after, info->fname, s+9, c->length-9, &plugin, "FLAC", info->totalsamples, info->samplerate); - } - else if (!strncasecmp (s, "replaygain_album_gain=", 22)) { - it->replaygain_album_gain = atof (s + 22); - } - else if (!strncasecmp (s, "replaygain_album_peak=", 22)) { - it->replaygain_album_peak = atof (s + 22); - } - else if (!strncasecmp (s, "replaygain_track_gain=", 22)) { - it->replaygain_track_gain = atof (s + 22); - } - else if (!strncasecmp (s, "replaygain_track_peak=", 22)) { - it->replaygain_track_peak = atof (s + 22); + char *s = c->entry; + int m; + for (m = 0; metainfo[m]; m += 2) { + int l = strlen (metainfo[m]); + if (c->length > l && !strncasecmp (metainfo[m], s, l) && s[l] == '=') { + deadbeef->pl_append_meta (it, metainfo[m+1], s + l + 1); + } } - else { - trace ("found flac meta: %s\n", s); + if (!metainfo[m]) { + if (!strncasecmp (s, "CUESHEET=", 9)) { + deadbeef->pl_add_meta (it, "cuesheet", s + 9); + } + else if (!strncasecmp (s, "replaygain_album_gain=", 22)) { + it->replaygain_album_gain = atof (s + 22); + } + else if (!strncasecmp (s, "replaygain_album_peak=", 22)) { + it->replaygain_album_peak = atof (s + 22); + } + else if (!strncasecmp (s, "replaygain_track_gain=", 22)) { + it->replaygain_track_gain = atof (s + 22); + } + else if (!strncasecmp (s, "replaygain_track_peak=", 22)) { + it->replaygain_track_peak = atof (s + 22); + } } } } - if (!title_added) { - deadbeef->pl_add_meta (it, "title", NULL); - } + deadbeef->pl_add_meta (it, "title", NULL); deadbeef->pl_add_meta (it, "tags", "VorbisComments"); } } @@ -727,24 +702,6 @@ cflac_insert_fail: return NULL; } -static const char *metainfo[] = { - "ARTIST", "artist", - "TITLE", "title", - "ALBUM", "album", - "TRACKNUMBER", "track", - "DATE", "year", - "GENRE", "genre", - "COMMENT", "comment", - "PERFORMER", "performer", - "ENSEMBLE", "band", - "COMPOSER", "composer", - "ENCODED-BY", "vendor", - "DISCNUMBER", "disc", - "COPYRIGHT", "copyright", - "TRACKTOTAL", "numtracks", - NULL -}; - int cflac_read_metadata (DB_playItem_t *it) { deadbeef->pl_delete_all_meta (it); -- cgit v1.2.3 From ad088a54da363ebdb39ba91de54452ad06b86762 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 6 Apr 2010 17:36:45 +0200 Subject: added multiline comment support to vorbis plugin --- plugins/vorbis/vorbis.c | 115 +++++++++++++++++------------------------------- 1 file changed, 41 insertions(+), 74 deletions(-) diff --git a/plugins/vorbis/vorbis.c b/plugins/vorbis/vorbis.c index 4667d1eb..45f14731 100644 --- a/plugins/vorbis/vorbis.c +++ b/plugins/vorbis/vorbis.c @@ -74,68 +74,54 @@ cvorbis_ftell (void *datasource) { return deadbeef->ftell (datasource); } +static const char *metainfo[] = { + "ARTIST", "artist", + "TITLE", "title", + "ALBUM", "album", + "TRACKNUMBER", "track", + "DATE", "year", + "GENRE", "genre", + "COMMENT", "comment", + "PERFORMER", "performer", + "ENSEMBLE", "band", + "COMPOSER", "composer", + "ENCODED-BY", "vendor", + "DISCNUMBER", "disc", + "COPYRIGHT", "copyright", + "TRACKTOTAL", "numtracks", + NULL +}; + static void update_vorbis_comments (DB_playItem_t *it, vorbis_comment *vc) { if (vc) { deadbeef->pl_delete_all_meta (it); deadbeef->pl_add_meta (it, "vendor", vc->vendor); for (int i = 0; i < vc->comments; i++) { - if (!strncasecmp (vc->user_comments[i], "ARTIST=", 7)) { - deadbeef->pl_add_meta (it, "artist", vc->user_comments[i] + 7); - } - else if (!strncasecmp (vc->user_comments[i], "ALBUM=", 6)) { - deadbeef->pl_add_meta (it, "album", vc->user_comments[i] + 6); - } - else if (!strncasecmp (vc->user_comments[i], "TITLE=", 6)) { - deadbeef->pl_add_meta (it, "title", vc->user_comments[i] + 6); - } - else if (!strncasecmp (vc->user_comments[i], "TRACKNUMBER=", 12)) { - deadbeef->pl_add_meta (it, "track", vc->user_comments[i] + 12); - } - else if (!strncasecmp (vc->user_comments[i], "TRACKTOTAL=", 11)) { - deadbeef->pl_add_meta (it, "numtracks", vc->user_comments[i] + 11); - } - else if (!strncasecmp (vc->user_comments[i], "DATE=", 5)) { - deadbeef->pl_add_meta (it, "year", vc->user_comments[i] + 5); - } - else if (!strncasecmp (vc->user_comments[i], "COMMENT=", 8)) { - deadbeef->pl_add_meta (it, "comment", vc->user_comments[i] + 8); - } - else if (!strncasecmp (vc->user_comments[i], "PERFORMER=", 10)) { - deadbeef->pl_add_meta (it, "performer", vc->user_comments[i] + 10); - } - else if (!strncasecmp (vc->user_comments[i], "ENSEMBLE=", 9)) { - deadbeef->pl_add_meta (it, "band", vc->user_comments[i] + 9); - } - else if (!strncasecmp (vc->user_comments[i], "COMPOSER=", 9)) { - deadbeef->pl_add_meta (it, "composer", vc->user_comments[i] + 9); - } - else if (!strncasecmp (vc->user_comments[i], "ENCODED-BY=", 11)) { - deadbeef->pl_add_meta (it, "vendor", vc->user_comments[i] + 11); - } - else if (!strncasecmp (vc->user_comments[i], "DISCNUMBER=", 11)) { - deadbeef->pl_add_meta (it, "disc", vc->user_comments[i] + 11); - } - else if (!strncasecmp (vc->user_comments[i], "genre=", 6)) { - deadbeef->pl_add_meta (it, "genre", vc->user_comments[i] + 6); - } - else if (!strncasecmp (vc->user_comments[i], "copyright=", 10)) { - deadbeef->pl_add_meta (it, "copyright", vc->user_comments[i] + 10); - } - else if (!strncasecmp (vc->user_comments[i], "cuesheet=", 9)) { - deadbeef->pl_add_meta (it, "cuesheet", vc->user_comments[i] + 9); - } - else if (!strncasecmp (vc->user_comments[i], "replaygain_album_gain=", 22)) { - it->replaygain_album_gain = atof (vc->user_comments[i] + 22); - } - else if (!strncasecmp (vc->user_comments[i], "replaygain_album_peak=", 22)) { - it->replaygain_album_peak = atof (vc->user_comments[i] + 22); - } - else if (!strncasecmp (vc->user_comments[i], "replaygain_track_gain=", 22)) { - it->replaygain_track_gain = atof (vc->user_comments[i] + 22); + char *s = vc->user_comments[i]; + int m; + for (m = 0; metainfo[m]; m += 2) { + int l = strlen (metainfo[m]); + if (vc->comment_lengths[i] > l && !strncasecmp (metainfo[m], s, l) && s[l] == '=') { + deadbeef->pl_append_meta (it, metainfo[m+1], s + l + 1); + } } - else if (!strncasecmp (vc->user_comments[i], "replaygain_track_peak=", 22)) { - it->replaygain_track_peak = atof (vc->user_comments[i] + 22); + if (!metainfo[m]) { + if (!strncasecmp (s, "cuesheet=", 9)) { + deadbeef->pl_add_meta (it, "cuesheet", s + 9); + } + else if (!strncasecmp (s, "replaygain_album_gain=", 22)) { + it->replaygain_album_gain = atof (s + 22); + } + else if (!strncasecmp (s, "replaygain_album_peak=", 22)) { + it->replaygain_album_peak = atof (s + 22); + } + else if (!strncasecmp (s, "replaygain_track_gain=", 22)) { + it->replaygain_track_gain = atof (s + 22); + } + else if (!strncasecmp (s, "replaygain_track_peak=", 22)) { + it->replaygain_track_peak = atof (s + 22); + } } } } @@ -447,25 +433,6 @@ static int vorbis_stop (void) { return 0; } - -static const char *metainfo[] = { - "ARTIST", "artist", - "TITLE", "title", - "ALBUM", "album", - "TRACKNUMBER", "track", - "DATE", "year", - "GENRE", "genre", - "COMMENT", "comment", - "PERFORMER", "performer", - "ENSEMBLE", "band", - "COMPOSER", "composer", - "ENCODED-BY", "vendor", - "DISCNUMBER", "disc", - "COPYRIGHT", "copyright", - "TRACKTOTAL", "numtracks", - NULL -}; - int cvorbis_read_metadata (DB_playItem_t *it) { int err = -1; -- cgit v1.2.3 From a9f223af03e491c49f3e127a626f2861882e7134 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 6 Apr 2010 17:45:06 +0200 Subject: fixed ogg vorbis ENCODER comment support; fixed few ogg metadata writer bugs --- plugins/vorbis/vorbis.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/vorbis/vorbis.c b/plugins/vorbis/vorbis.c index 45f14731..6e589c2e 100644 --- a/plugins/vorbis/vorbis.c +++ b/plugins/vorbis/vorbis.c @@ -85,7 +85,7 @@ static const char *metainfo[] = { "PERFORMER", "performer", "ENSEMBLE", "band", "COMPOSER", "composer", - "ENCODED-BY", "vendor", + "ENCODER", "vendor", "DISCNUMBER", "disc", "COPYRIGHT", "copyright", "TRACKTOTAL", "numtracks", @@ -96,7 +96,6 @@ static void update_vorbis_comments (DB_playItem_t *it, vorbis_comment *vc) { if (vc) { deadbeef->pl_delete_all_meta (it); - deadbeef->pl_add_meta (it, "vendor", vc->vendor); for (int i = 0; i < vc->comments; i++) { char *s = vc->user_comments[i]; int m; @@ -524,11 +523,12 @@ cvorbis_write_metadata (DB_playItem_t *it) { int m; for (m = 0; metainfo[m]; m += 2) { int l = strlen (metainfo[m]); - if (l+1 <= vc->comment_lengths[i] && !strncasecmp (vc->user_comments[i], metainfo[m], vc->comment_lengths[i]) && vc->user_comments[i][l] == '=') { + if (vc->comment_lengths[i] > l && !strncasecmp (vc->user_comments[i], metainfo[m], l) && vc->user_comments[i][l] == '=') { break; } } if (!metainfo[m]) { + printf ("preserved field: %s\n", vc->user_comments[i]); // unknown field struct field *f = malloc (sizeof (struct field) + vc->comment_lengths[i]); memset (f, 0, sizeof (struct field)); -- cgit v1.2.3 From 40522448588f63d6d72ef775e0340b7fae548a05 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 6 Apr 2010 17:59:11 +0200 Subject: support for multiline apev2 frames; improved apev2 parser; improved cuesheet metadata interoperability --- junklib.c | 119 +++++++++++++++++++++++++++++++------------------------------ playlist.c | 38 ++++++-------------- 2 files changed, 71 insertions(+), 86 deletions(-) diff --git a/junklib.c b/junklib.c index 5a99eb27..887e243b 100644 --- a/junklib.c +++ b/junklib.c @@ -721,6 +721,25 @@ junk_apev2_read_full (playItem_t *it, DB_apev2_tag_t *tag_store, DB_FILE *fp) { trace ("failed to seek to tag start (-%d)\n", size); return -1; } + + // this code ensures that APEv2 frames don't get appended to + // existing metainfo, but all APEv2 frames with the same key are + // appended + const char *metainfo[] = { + "artist", "artist", it ? pl_find_meta (it, "artist") : NULL, + "album artist", "band", it ? pl_find_meta (it, "band") : NULL, + "title", "title", it ? pl_find_meta (it, "title") : NULL, + "album", "album", it ? pl_find_meta (it, "album") : NULL, + "year", "year", it ? pl_find_meta (it, "year") : NULL, + "genre", "genre", it ? pl_find_meta (it, "genre") : NULL, + "composer", "composer", it ? pl_find_meta (it, "composer") : NULL, +// "performer", "performer", it ? pl_find_meta (it, "performer") : NULL, + "comment", "comment", it ? pl_find_meta (it, "comment") : NULL, + "copyright", "copyright", it ? pl_find_meta (it, "copyright") : NULL, + "totaltracks", "numtracks", it ? pl_find_meta (it, "numtracks") : NULL, + NULL + }; + int i; for (i = 0; i < numitems; i++) { uint8_t buffer[8]; @@ -778,66 +797,50 @@ junk_apev2_read_full (playItem_t *it, DB_apev2_tag_t *tag_store, DB_FILE *fp) { tail = frm; } - int valuetype = ((itemflags >> 1) & 3); - // add metainfo only if it's textual - if (valuetype == 0 && (itemsize < MAX_TEXT_FRAME_SIZE || (!strcasecmp (key, "cuesheet") && itemsize < MAX_CUESHEET_FRAME_SIZE))) { - if (!u8_valid (value, itemsize, NULL)) { - trace ("junk_read_ape_full: bad encoding in text frame %s\n", key); - continue; - } + if (it) { + int valuetype = ((itemflags >> 1) & 3); + // add metainfo only if it's textual + if (valuetype == 0 && (itemsize < MAX_TEXT_FRAME_SIZE || (!strcasecmp (key, "cuesheet") && itemsize < MAX_CUESHEET_FRAME_SIZE))) { + printf ("APEv2 %s=%s\n", key, value); - if (!strcasecmp (key, "artist")) { - pl_add_meta (it, "artist", value); - } - else if (!strcasecmp (key, "title")) { - pl_add_meta (it, "title", value); - } - else if (!strcasecmp (key, "album")) { - pl_add_meta (it, "album", value); - } - else if (!strcasecmp (key, "track")) { - char *slash = strchr (value, '/'); - if (slash) { - // split into track/number - *slash = 0; - slash++; - pl_add_meta (it, "numtracks", slash); + if (!u8_valid (value, itemsize, NULL)) { + trace ("junk_read_ape_full: bad encoding in text frame %s\n", key); + continue; + } + + int m; + for (m = 0; metainfo[m]; m+=3) { + if (!metainfo[m+2] && !strcasecmp (key, metainfo[m])) { + printf ("adding %s=%s as %s\n", key, value, metainfo[m+1]); + pl_append_meta (it, metainfo[m+1], value); + break; + } + } + + if (!metainfo[m]) { + if (!strcasecmp (key, "track")) { + pl_add_meta (it, "track", value); + } + else if (!strcasecmp (key, "cuesheet")) { + pl_add_meta (it, "cuesheet", value); + } + else if (!strncasecmp (key, "replaygain_album_gain", 21)) { + it->replaygain_album_gain = atof (value); + trace ("album_gain=%s\n", value); + } + else if (!strncasecmp (key, "replaygain_album_peak", 21)) { + it->replaygain_album_peak = atof (value); + trace ("album_peak=%s\n", value); + } + else if (!strncasecmp (key, "replaygain_track_gain", 21)) { + it->replaygain_track_gain = atof (value); + trace ("track_gain=%s\n", value); + } + else if (!strncasecmp (key, "replaygain_track_peak", 21)) { + it->replaygain_track_peak = atof (value); + trace ("track_peak=%s\n", value); + } } - pl_add_meta (it, "track", value); - } - else if (!strcasecmp (key, "year")) { - pl_add_meta (it, "year", value); - } - else if (!strcasecmp (key, "genre")) { - pl_add_meta (it, "genre", value); - } - else if (!strcasecmp (key, "composer")) { - pl_add_meta (it, "composer", value); - } - else if (!strcasecmp (key, "comment")) { - pl_add_meta (it, "comment", value); - } - else if (!strcasecmp (key, "copyright")) { - pl_add_meta (it, "copyright", value); - } - else if (!strcasecmp (key, "cuesheet")) { - pl_add_meta (it, "cuesheet", value); - } - else if (!strncasecmp (key, "replaygain_album_gain", 21)) { - it->replaygain_album_gain = atof (value); - trace ("album_gain=%s\n", value); - } - else if (!strncasecmp (key, "replaygain_album_peak", 21)) { - it->replaygain_album_peak = atof (value); - trace ("album_peak=%s\n", value); - } - else if (!strncasecmp (key, "replaygain_track_gain", 21)) { - it->replaygain_track_gain = atof (value); - trace ("track_gain=%s\n", value); - } - else if (!strncasecmp (key, "replaygain_track_peak", 21)) { - it->replaygain_track_peak = atof (value); - trace ("track_peak=%s\n", value); } } free (value); diff --git a/playlist.c b/playlist.c index 417dbf7a..62629303 100644 --- a/playlist.c +++ b/playlist.c @@ -2739,34 +2739,16 @@ pl_playqueue_getcount (void) { void pl_items_copy_junk (playItem_t *from, playItem_t *first, playItem_t *last) { LOCK; - const char *year = pl_find_meta (from, "year"); - const char *genre = pl_find_meta (from, "genre"); - const char *copyright = pl_find_meta (from, "copyright"); - const char *vendor = pl_find_meta (from, "vendor"); - const char *comment = pl_find_meta (from, "comment"); - const char *tags = pl_find_meta (from, "tags"); - playItem_t *i; - for (i = first; i; i = i->next[PL_MAIN]) { - if (year) { - pl_add_meta (i, "year", year); - } - if (genre) { - pl_add_meta (i, "genre", genre); - } - if (copyright) { - pl_add_meta (i, "copyright", copyright); - } - if (vendor) { - pl_add_meta (i, "vendor", vendor); - } - if (comment) { - pl_add_meta (i, "comment", comment); - } - if (tags) { - pl_add_meta (i, "tags", tags); - } - if (i == last) { - break; + const char *metainfo[] = { + "year", "genre", "copyright", "vendor", "comment", "tags", "numtracks", "band", "performer", "composer", "disc", NULL + }; + for (int m = 0; metainfo[m]; m++) { + const char *data = pl_find_meta (from, metainfo[m]); + if (data) { + playItem_t *i; + for (i = first; i != last; i = i->next[PL_MAIN]) { + pl_add_meta (i, metainfo[m], data); + } } } UNLOCK; -- cgit v1.2.3 From 80f7aa0cfe21fc2715af029cc5bea6e8b4e18a5f Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 6 Apr 2010 19:27:21 +0200 Subject: added auto-saving config/playlist files --- deadbeef.h | 3 +++ main.c | 4 +++- playlist.c | 40 ++++++++++++++++++++++++++++++++++++++-- playlist.h | 3 +++ plugins.c | 9 +++++++++ plugins/gtkui/ddbtabstrip.c | 8 ++++++++ plugins/gtkui/fileman.c | 3 ++- 7 files changed, 66 insertions(+), 4 deletions(-) diff --git a/deadbeef.h b/deadbeef.h index f2e774ab..430f2e90 100644 --- a/deadbeef.h +++ b/deadbeef.h @@ -357,6 +357,8 @@ typedef struct { void (*pl_clear) (void); int (*pl_load) (const char *name); int (*pl_save) (const char *name); + int (*pl_save_current) (void); + int (*pl_save_all) (void); void (*pl_select_all) (void); void (*pl_crop_selected) (void); int (*pl_getselcount) (void); @@ -475,6 +477,7 @@ typedef struct { void (*conf_set_float) (const char *key, float val); DB_conf_item_t * (*conf_find) (const char *group, DB_conf_item_t *prev); void (*conf_remove_items) (const char *key); + int (*conf_save) (void); // plugin communication struct DB_decoder_s **(*plug_get_decoder_list) (void); struct DB_output_s **(*plug_get_output_list) (void); diff --git a/main.c b/main.c index b53a2205..a273d5fb 100644 --- a/main.c +++ b/main.c @@ -344,6 +344,7 @@ player_mainloop (void) { switch (msg) { case M_REINIT_SOUND: plug_reinit_sound (); + conf_save (); break; case M_TERMINATE: return; @@ -387,10 +388,11 @@ player_mainloop (void) { streamer_move_to_randomsong (); break; case M_PLAYLISTREFRESH: + pl_save_current (); plug_trigger_event_playlistchanged (); break; case M_CONFIGCHANGED: - //plug_get_output ()->configchanged (); + conf_save (); streamer_configchanged (); plug_trigger_event (DB_EV_CONFIGCHANGED, 0); break; diff --git a/playlist.c b/playlist.c index 62629303..84831149 100644 --- a/playlist.c +++ b/playlist.c @@ -269,6 +269,14 @@ plt_add (int before, const char *title) { if (!playlist) { playlist = plt; + if (!plt_loading) { + // need to delete old playlist file if exists + char path[PATH_MAX]; + if (snprintf (path, sizeof (path), "%s/playlists/%d.dbpl", dbconfdir, playlists_count-1) <= sizeof (path)) { + unlink (path); + } + pl_save_current (); + } } PLT_UNLOCK; @@ -351,6 +359,8 @@ plt_set_curr (int plt) { playlist = p; if (!plt_loading) { plug_trigger_event (DB_EV_PLAYLISTSWITCH, 0); + conf_set_int ("playlist.current", plt_get_curr ()); + conf_save (); } } PLT_UNLOCK; @@ -1713,9 +1723,35 @@ save_fail: return -1; } +int +pl_save_current (void) { + char path[PATH_MAX]; + if (snprintf (path, sizeof (path), "%s/playlists", dbconfdir) > sizeof (path)) { + fprintf (stderr, "error: failed to make path string for playlists folder\n"); + return -1; + } + // make folder + mkdir (path, 0755); + + PLT_LOCK; + int curr = plt_get_curr (); + int err = 0; + + plt_loading = 1; + if (snprintf (path, sizeof (path), "%s/playlists/%d.dbpl", dbconfdir, curr) > sizeof (path)) { + fprintf (stderr, "error: failed to make path string for playlist file\n"); + PLT_UNLOCK; + return -1; + } + err = pl_save (path); + plt_loading = 0; + PLT_UNLOCK; + return err; +} + int pl_save_all (void) { - char path[1024]; + char path[PATH_MAX]; if (snprintf (path, sizeof (path), "%s/playlists", dbconfdir) > sizeof (path)) { fprintf (stderr, "error: failed to make path string for playlists folder\n"); return -1; @@ -1734,7 +1770,7 @@ pl_save_all (void) { for (i = 0; i < cnt; i++, p = p->next) { plt_set_curr (i); if (snprintf (path, sizeof (path), "%s/playlists/%d.dbpl", dbconfdir, i) > sizeof (path)) { - fprintf (stderr, "error: failed to make path string for playlists folder\n"); + fprintf (stderr, "error: failed to make path string for playlist file\n"); err = -1; break; } diff --git a/playlist.h b/playlist.h index 26655f5f..74434cf5 100644 --- a/playlist.h +++ b/playlist.h @@ -208,6 +208,9 @@ pl_crop_selected (void); int pl_save (const char *fname); +int +pl_save_current (void); + int pl_save_all (void); diff --git a/plugins.c b/plugins.c index 805cc6ee..6f5d57ba 100644 --- a/plugins.c +++ b/plugins.c @@ -147,6 +147,8 @@ static DB_functions_t deadbeef_api = { .pl_clear = pl_clear, .pl_load = pl_load, .pl_save = pl_save, + .pl_save_current = pl_save_current, + .pl_save_all = pl_save_all, .pl_select_all = pl_select_all, .pl_crop_selected = pl_crop_selected, .pl_getselcount = pl_getselcount, @@ -230,6 +232,7 @@ static DB_functions_t deadbeef_api = { .conf_set_float = conf_set_float, .conf_find = conf_find, .conf_remove_items = conf_remove_items, + .conf_save = conf_save, // plugin communication .plug_get_decoder_list = plug_get_decoder_list, .plug_get_output_list = plug_get_output_list, @@ -422,11 +425,17 @@ plug_event_call (DB_event_t *ev) { ev->time = time (NULL); // printf ("plug_event_call enter %d\n", ev->event); mutex_lock (mutex); + for (int i = 0; i < MAX_HANDLERS; i++) { if (handlers[ev->event][i].plugin && !handlers[ev->event][i].plugin->inactive) { handlers[ev->event][i].callback (ev, handlers[ev->event][i].data); } } +// if (ev->event == DB_EV_PLAYLISTSWITCH) { +// printf ("DB_EV_PLAYLISTSWITCH %d %d\n", plt_get_curr (), conf_get_int ("playlist.current", 0)); +// pl_save_current (); +// } + mutex_unlock (mutex); // printf ("plug_event_call leave %d\n", ev->event); } diff --git a/plugins/gtkui/ddbtabstrip.c b/plugins/gtkui/ddbtabstrip.c index 8d9b6675..98ec3f1a 100644 --- a/plugins/gtkui/ddbtabstrip.c +++ b/plugins/gtkui/ddbtabstrip.c @@ -201,6 +201,8 @@ static int tab_overlap_size = 0; // widget_height/2 static int tabs_left_margin = 4; static int min_tab_size = 80; +static int tab_moved = 0; + void ddb_tabstrip_draw_tab (GtkWidget *widget, GdkDrawable *drawable, int selected, int x, int y, int w, int h) { GdkPoint points_filled[] = { @@ -452,6 +454,7 @@ on_tabstrip_button_press_event (GtkWidget *widget, ts->prepare = 1; ts->dragging = tab_clicked; ts->prev_x = event->x; + tab_moved = 0; } else if (event->button == 3) { GtkWidget *menu = create_plmenu (); @@ -481,6 +484,10 @@ on_tabstrip_button_release_event (GtkWidget *widget, ts->prepare = 0; tabstrip_render (ts); tabstrip_expose (ts, 0, 0, widget->allocation.width, widget->allocation.height); + if (tab_moved) { + deadbeef->pl_save_all (); + deadbeef->conf_save (); + } } } return FALSE; @@ -558,6 +565,7 @@ on_tabstrip_motion_notify_event (GtkWidget *widget, snprintf (str2, sizeof (str2), "playlist.scroll.%d", inspos); pos2 = deadbeef->conf_get_int (str2, 0); deadbeef->plt_move (ts->dragging, inspos); + tab_moved = 1; deadbeef->conf_set_int (str1, pos2); deadbeef->conf_set_int (str2, pos1); ts->dragging = inspos; diff --git a/plugins/gtkui/fileman.c b/plugins/gtkui/fileman.c index 1ae8b4aa..8ef81238 100644 --- a/plugins/gtkui/fileman.c +++ b/plugins/gtkui/fileman.c @@ -32,7 +32,8 @@ gtkpl_add_file_info_cb (DB_playItem_t *it, void *data) { static gboolean progress_hide_idle (gpointer data) { progress_hide (); - playlist_refresh (); + deadbeef->sendmessage (M_PLAYLISTREFRESH, 0, 0, 0); + //playlist_refresh (); return FALSE; } -- cgit v1.2.3 From 6f2b3a82236d9de0b8926c3d24e98c7ef9c320a5 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 6 Apr 2010 19:51:51 +0200 Subject: added option to enable/disable lastfm plugin; disabled lastfm plugin temporarily to avoid ban --- plugins/lastfm/lastfm.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/plugins/lastfm/lastfm.c b/plugins/lastfm/lastfm.c index 79946d14..6f52018b 100644 --- a/plugins/lastfm/lastfm.c +++ b/plugins/lastfm/lastfm.c @@ -28,7 +28,7 @@ #define LFM_TESTMODE 0 #define LFM_IGNORE_RULES 0 -#define LFM_NOSEND 0 +#define LFM_NOSEND 1 static DB_misc_t plugin; static DB_functions_t *deadbeef; @@ -465,6 +465,9 @@ lfm_format_uri (int subm, DB_playItem_t *song, char *out, int outl) { static int lastfm_songstarted (DB_event_track_t *ev, uintptr_t data) { + if (!deadbeef->conf_get_int ("lastfm.enable", 0)) { + return 0; + } deadbeef->mutex_lock (lfm_mutex); if (lfm_format_uri (-1, ev->track, lfm_nowplaying, sizeof (lfm_nowplaying)) < 0) { lfm_nowplaying[0] = 0; @@ -480,7 +483,10 @@ lastfm_songstarted (DB_event_track_t *ev, uintptr_t data) { static int lastfm_songfinished (DB_event_track_t *ev, uintptr_t data) { - trace ("lfm songfinished\n"); + trace ("lfm songfinished %s\n", ev->track->fname); + if (!deadbeef->conf_get_int ("lastfm.enable", 0)) { + return 0; + } #if !LFM_IGNORE_RULES // check submission rules // duration must be >= 30 sec @@ -663,6 +669,9 @@ lfm_thread (void *ctx) { trace ("cond signalled!\n"); deadbeef->mutex_unlock (lfm_mutex); + if (!deadbeef->conf_get_int ("lastfm.enable", 0)) { + continue; + } trace ("lfm sending nowplaying...\n"); // try to send nowplaying if (lfm_nowplaying[0] && !deadbeef->conf_get_int ("lastfm.disable_np", 0)) { @@ -798,10 +807,11 @@ lastfm_stop (void) { } static const char settings_dlg[] = + "property \"Enable scrobbler\" checkbox lastfm.enable 0;" + "property \"Disable nowplaying\" checkbox lastfm.disable_np 0;" "property Username entry lastfm.login \"\";\n" "property Password password lastfm.password \"\";" "property \"Scrobble URL\" entry lastfm.scrobbler_url \""SCROBBLER_URL_LFM"\";" - "property \"Disable nowplaying\" checkbox lastfm.disable_np 0;" ; // define plugin interface -- cgit v1.2.3 From e62b0f28ff08c7913463855cba8082e1b9dff867 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 6 Apr 2010 20:45:20 +0200 Subject: fixed double-play in shuffle mode; fixed lastfm submission/nowplaying --- plugins/lastfm/lastfm.c | 6 +++--- streamer.c | 12 ++++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/plugins/lastfm/lastfm.c b/plugins/lastfm/lastfm.c index 6f52018b..e8d3428b 100644 --- a/plugins/lastfm/lastfm.c +++ b/plugins/lastfm/lastfm.c @@ -28,7 +28,7 @@ #define LFM_TESTMODE 0 #define LFM_IGNORE_RULES 0 -#define LFM_NOSEND 1 +#define LFM_NOSEND 0 static DB_misc_t plugin; static DB_functions_t *deadbeef; @@ -99,7 +99,6 @@ lastfm_curl_res (void *ptr, size_t size, size_t nmemb, void *stream) static int lfm_curl_control (void *stream, double dltotal, double dlnow, double ultotal, double ulnow) { - trace ("lfm_curl_control\n"); if (lfm_stopthread) { trace ("lfm: aborting current request\n"); return -1; @@ -465,6 +464,7 @@ lfm_format_uri (int subm, DB_playItem_t *song, char *out, int outl) { static int lastfm_songstarted (DB_event_track_t *ev, uintptr_t data) { + trace ("lfm songfinished %s\n", ev->track->fname); if (!deadbeef->conf_get_int ("lastfm.enable", 0)) { return 0; } @@ -673,11 +673,11 @@ lfm_thread (void *ctx) { continue; } trace ("lfm sending nowplaying...\n"); + lfm_send_submissions (); // try to send nowplaying if (lfm_nowplaying[0] && !deadbeef->conf_get_int ("lastfm.disable_np", 0)) { lfm_send_nowplaying (); } - lfm_send_submissions (); } } diff --git a/streamer.c b/streamer.c index 67a79e05..fe7c0ed4 100644 --- a/streamer.c +++ b/streamer.c @@ -436,7 +436,11 @@ streamer_set_current (playItem_t *it) { playing_track = it; if (playing_track) { pl_item_ref (playing_track); + playing_track->played = 1; + trace ("sending songstarted to plugins [2] current playtrack: %s\n", playing_track->fname); + plug_trigger_event (DB_EV_SONGSTARTED, 0); } + bytes_until_next_song = -1; } // code below breaks seekbar drawing during transition between tracks @@ -532,9 +536,9 @@ streamer_set_current (playItem_t *it) { } return -1; } - if (bytes_until_next_song == -1) { - bytes_until_next_song = 0; - } +// if (bytes_until_next_song == -1) { +// bytes_until_next_song = 0; +// } success: messagepump_push (M_TRACKCHANGED, 0, to, 0); return 0; @@ -718,7 +722,7 @@ streamer_thread (void *ctx) { playlist_track = playing_track; // that is needed for playlist drawing // plugin will get pointer to new str_playing_song - trace ("sending songstarted to plugins\ncurrent playtrack: %s\n", playing_track->fname); + trace ("sending songstarted to plugins [1] current playtrack: %s\n", playing_track->fname); plug_trigger_event (DB_EV_SONGSTARTED, 0); playpos = 0; -- cgit v1.2.3 From 9c6e2108adbcedccee5a2984b1d8ffc57e6b1087 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 6 Apr 2010 21:14:09 +0200 Subject: added ability to enable/disable dsp plugins --- deadbeef.h | 2 ++ plugins/supereq/supereq.c | 43 ++++++++++++++++++++++++++++++++++++++++++- streamer.c | 8 ++++++-- 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/deadbeef.h b/deadbeef.h index 430f2e90..87d7deaf 100644 --- a/deadbeef.h +++ b/deadbeef.h @@ -615,6 +615,8 @@ typedef struct DB_dsp_s { // stereo sample is counted as 1 sample int (*process_int16) (int16_t *samples, int nsamples, int nch, int bps, int srate); void (*reset) (void); + void (*enable) (int e); + int (*enabled) (void); } DB_dsp_t; // misc plugin diff --git a/plugins/supereq/supereq.c b/plugins/supereq/supereq.c index 368bcda3..cf56f349 100644 --- a/plugins/supereq/supereq.c +++ b/plugins/supereq/supereq.c @@ -22,6 +22,7 @@ #include "supereq.h" static DB_functions_t *deadbeef; +static DB_supereq_dsp_t plugin; void *paramlist_alloc (void); void paramlist_free (void *); @@ -31,6 +32,8 @@ void equ_clearbuf(int bps,int srate); void equ_init(int wb); void equ_quit(void); +void supereq_reset (void); + static float last_srate = 0; static int last_nch = 0, last_bps = 0; static float lbands[18] = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0}; @@ -40,9 +43,24 @@ static void *paramsroot; static int params_changed = 0; static intptr_t tid = 0; static uintptr_t mutex = 0; +static int enabled = 0; + +static int +supereq_on_configchanged (DB_event_t *ev, uintptr_t data) { + int e = deadbeef->conf_get_int ("supereq.enable", 0); + if (e != enabled) { + if (e) { + supereq_reset (); + } + enabled = e; + } + + return 0; +} int supereq_plugin_start (void) { + enabled = deadbeef->conf_get_int ("supereq.enable", 0); // load bands from config for (int i = 0; i < 18; i++) { char key[100]; @@ -58,11 +76,13 @@ supereq_plugin_start (void) { equ_makeTable (lbands, rbands, paramsroot, last_srate); equ_clearbuf (last_bps,last_srate); mutex = deadbeef->mutex_create (); + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_CONFIGCHANGED, DB_CALLBACK (supereq_on_configchanged), 0); return 0; } int supereq_plugin_stop (void) { + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_CONFIGCHANGED, DB_CALLBACK (supereq_on_configchanged), 0); if (tid) { deadbeef->thread_join (tid); deadbeef->mutex_free (mutex); @@ -144,6 +164,25 @@ supereq_reset (void) { deadbeef->mutex_unlock (mutex); } +void +supereq_enable (int e) { + deadbeef->conf_set_int ("supereq.enable", e); + + if (e && !enabled) { + supereq_reset (); + } + enabled = e; +} + +int +supereq_enabled (void) { + return enabled; +} + +static const char settings_dlg[] = + "property \"Enable\" checkbox supereq.enable 0;\n" +; + static DB_supereq_dsp_t plugin = { .dsp.plugin.api_vmajor = DB_API_VERSION_MAJOR, .dsp.plugin.api_vminor = DB_API_VERSION_MINOR, @@ -156,9 +195,11 @@ static DB_supereq_dsp_t plugin = { .dsp.plugin.website = "http://deadbeef.sf.net", .dsp.plugin.start = supereq_plugin_start, .dsp.plugin.stop = supereq_plugin_stop, -// .dsp.plugin.configdialog = settings_dlg, + .dsp.plugin.configdialog = settings_dlg, .dsp.process_int16 = supereq_process_int16, .dsp.reset = supereq_reset, + .dsp.enable = supereq_enable, + .dsp.enabled = supereq_enabled, .get_band = supereq_get_band, .set_band = supereq_set_band, }; diff --git a/streamer.c b/streamer.c index fe7c0ed4..949ef383 100644 --- a/streamer.c +++ b/streamer.c @@ -976,7 +976,9 @@ streamer_reset (int full) { // must be called when current song changes by exter DB_dsp_t **dsp = deadbeef->plug_get_dsp_list (); int srate = p_get_rate (); for (int i = 0; dsp[i]; i++) { - dsp[i]->reset (); + if (dsp[i]->enabled ()) { + dsp[i]->reset (); + } } src_unlock (); } @@ -1356,7 +1358,9 @@ streamer_read_async (char *bytes, int size) { DB_dsp_t **dsp = deadbeef->plug_get_dsp_list (); int srate = p_get_rate (); for (int i = 0; dsp[i]; i++) { - dsp[i]->process_int16 ((int16_t *)bytes, bytesread/4, 2, 16, srate); + if (dsp[i]->enabled ()) { + dsp[i]->process_int16 ((int16_t *)bytes, bytesread/4, 2, 16, srate); + } } mutex_unlock (decodemutex); bytes += bytesread; -- cgit v1.2.3