summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Viktor Semykin <thesame.ml@gmail.com>2010-04-06 22:19:35 +0300
committerGravatar Viktor Semykin <thesame.ml@gmail.com>2010-04-06 22:19:35 +0300
commit823669afef8942b45960391f2f3d732ab1ad06e3 (patch)
tree067ad15823314a39d5c9d15878c9367b826bccac
parent6dbd7dd11bccc0ce7507e7b4b195517f123e8694 (diff)
parent9c6e2108adbcedccee5a2984b1d8ffc57e6b1087 (diff)
Merge branch 'devel' of git://deadbeef.git.sourceforge.net/gitroot/deadbeef/deadbeef into eq
-rw-r--r--CONTRIBUTING1
-rw-r--r--Makefile.am2
-rw-r--r--cdumb.c60
-rw-r--r--configure.ac2
-rw-r--r--deadbeef.h10
-rw-r--r--junklib.c394
-rw-r--r--junklib.h5
-rw-r--r--main.c4
-rw-r--r--playlist.c104
-rw-r--r--playlist.h6
-rw-r--r--plugins.c43
-rw-r--r--plugins.h3
-rw-r--r--plugins/artwork/albumartorg.c73
-rw-r--r--plugins/artwork/artwork.c89
-rw-r--r--plugins/artwork/artwork.h9
-rw-r--r--plugins/artwork/lastfm.c82
-rw-r--r--plugins/flac/flac.c226
-rw-r--r--plugins/gtkui/callbacks.c2
-rw-r--r--plugins/gtkui/coverart.c9
-rw-r--r--plugins/gtkui/ddblistview.c2
-rw-r--r--plugins/gtkui/ddbtabstrip.c8
-rw-r--r--plugins/gtkui/deadbeef.glade19
-rw-r--r--plugins/gtkui/fileman.c3
-rw-r--r--plugins/gtkui/gtkui.c19
-rw-r--r--plugins/gtkui/interface.c93
-rw-r--r--plugins/gtkui/plcommon.c47
-rw-r--r--plugins/gtkui/search.c43
-rw-r--r--plugins/gtkui/trkproperties.c95
-rw-r--r--plugins/lastfm/lastfm.c18
-rw-r--r--plugins/supereq/supereq.c43
-rw-r--r--plugins/vfs_curl/vfs_curl.c6
-rw-r--r--plugins/vorbis/Makefile.am2
-rw-r--r--plugins/vorbis/i18n.h18
-rw-r--r--plugins/vorbis/vcedit.c880
-rw-r--r--plugins/vorbis/vcedit.h69
-rw-r--r--plugins/vorbis/vceditaux.h9
-rw-r--r--plugins/vorbis/vorbis.c285
-rw-r--r--plugins/wavpack/wavpack.c83
-rw-r--r--streamer.c20
-rw-r--r--threading_pthread.c4
-rw-r--r--web/index.html4
41 files changed, 2109 insertions, 785 deletions
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
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)
diff --git a/cdumb.c b/cdumb.c
index 289d2fb5..83c07d31 100644
--- a/cdumb.c
+++ b/cdumb.c
@@ -18,8 +18,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#define LIBICONV_PLUG
-#include <iconv.h>
#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 *
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)
diff --git a/deadbeef.h b/deadbeef.h
index a0053cdd..87d7deaf 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);
@@ -450,7 +452,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);
@@ -474,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);
@@ -482,6 +486,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
@@ -609,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/junklib.c b/junklib.c
index 34bdfbe3..887e243b 100644
--- a/junklib.c
+++ b/junklib.c
@@ -32,13 +32,14 @@
#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
#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))
@@ -113,7 +114,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;
@@ -312,80 +313,33 @@ 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;
+convstr_id3v2 (int version, uint8_t encoding, const unsigned char* str, int sz) {
+ char out[2048] = "";
+ const char *enc = NULL;
- // 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;
- }
+ // detect encoding
+ if (version == 4 && encoding == 2) {
+ trace ("utf16be\n");
+ enc = "UTF-16BE";
}
- else {
- if (can_be_russian (&str[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";
}
}
- 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 if (encoding != 1 && !(version == 4 && encoding == 3)){
+ return NULL; // invalid encoding
}
- 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
- trace ("utf8\n");
- strncpy (out, str, 2047);
- out[min (sz, 2047)] = 0;
- return strdup (out);
- }
- else if (version == 4 && encoding == 2) {
- trace ("utf16be\n");
- enc = "UTF-16BE";
- }
- else if (encoding == 1) {
+ 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";
@@ -407,44 +361,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 {
+ else if (encoding == 0) {
+ // hack to add limited cp1251 recoding support
if (can_be_russian (str)) {
enc = "cp1251";
}
+ else {
+ enc = "iso8859-1";
+ }
}
- 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 ("-");
+
+ int converted_sz = 0;
+
+ if ((converted_sz = junk_iconv (str, sz, out, sizeof (out), enc, UTF8)) < 0) {
+ return NULL;
}
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);
+ for (int n = 0; n < converted_sz; n++) {
+ if (out[n] == 0 && n != converted_sz-1) {
+ out[n] = '\n';
+ }
+ }
+ }
+ 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++) {
@@ -458,52 +402,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
@@ -811,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];
@@ -868,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) {
- 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);
@@ -2277,40 +2190,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
@@ -2544,7 +2461,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
@@ -2849,26 +2773,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/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 09e656f0..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;
@@ -779,6 +789,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;
@@ -1535,21 +1546,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;
@@ -1728,8 +1724,34 @@ save_fail:
}
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;
@@ -1748,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;
}
@@ -2226,17 +2248,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, "?"));
@@ -2289,13 +2311,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, ""));
@@ -2660,7 +2682,7 @@ pl_search_process (const char *text) {
}
}
}
- GLOBAL_LOCK;
+ GLOBAL_UNLOCK;
}
int
@@ -2753,30 +2775,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");
- 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 (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;
diff --git a/playlist.h b/playlist.h
index b6c8403f..74434cf5 100644
--- a/playlist.h
+++ b/playlist.h
@@ -196,9 +196,6 @@ 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);
// returns index of 1st deleted item
@@ -212,6 +209,9 @@ int
pl_save (const char *fname);
int
+pl_save_current (void);
+
+int
pl_save_all (void);
int
diff --git a/plugins.c b/plugins.c
index 429fac61..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,
@@ -155,7 +157,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,
@@ -208,6 +209,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,
@@ -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,
@@ -238,6 +241,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;
@@ -420,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);
}
@@ -722,13 +733,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);
}
@@ -915,3 +931,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/albumartorg.c b/plugins/artwork/albumartorg.c
index 06247f74..cc10f235 100644
--- a/plugins/artwork/albumartorg.c
+++ b/plugins/artwork/albumartorg.c
@@ -19,8 +19,14 @@
#include <stdlib.h>
#include <curl/curl.h>
#include <string.h>
+#include <unistd.h>
#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,65 @@ 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];
+ memset (buffer, 0, sizeof (buffer));
+ 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..aa4fc64e 100644
--- a/plugins/artwork/artwork.c
+++ b/plugins/artwork/artwork.c
@@ -2,7 +2,6 @@
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
-#include <curl/curl.h>
#include <errno.h>
#include <dirent.h>
#include <unistd.h>
@@ -12,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;
@@ -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)
{
@@ -269,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) {
@@ -334,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;
@@ -462,10 +390,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 +431,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) {
@@ -519,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);
}
@@ -593,6 +521,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/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..6f429bf7 100644
--- a/plugins/artwork/lastfm.c
+++ b/plugins/artwork/lastfm.c
@@ -2,39 +2,89 @@
#include <string.h>
#include <curl/curl.h>
#include <stdlib.h>
+#include <unistd.h>
#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, "<image size=\"extralarge\">");
- 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[] = "<image size=\"extralarge\">";
+ char buffer[1000];
+ memset (buffer, 0, sizeof (buffer));
+ 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, "</image>");
- 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/flac/flac.c b/plugins/flac/flac.c
index 9281b62e..0de7c7c6 100644
--- a/plugins/flac/flac.c
+++ b/plugins/flac/flac.c
@@ -19,6 +19,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <FLAC/stream_decoder.h>
+#include <FLAC/metadata.h>
#include "../../deadbeef.h"
static DB_decoder_t plugin;
@@ -472,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;
@@ -491,77 +510,38 @@ 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, "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);
+ 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 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);
- }
- 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");
}
}
@@ -722,6 +702,122 @@ cflac_insert_fail:
return 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;
+ deadbeef->pl_add_meta (it, "title", NULL);
+ deadbeef->pl_add_meta (it, "tags", "VorbisComments");
+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 +841,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
};
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/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);
}
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/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/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 @@
<accelerator key="O" modifiers="GDK_CONTROL_MASK" signal="activate"/>
<child internal-child="image">
- <widget class="GtkImage" id="image368">
+ <widget class="GtkImage" id="image376">
<property name="visible">True</property>
<property name="stock">gtk-open</property>
<property name="icon_size">1</property>
@@ -85,7 +85,7 @@
<signal name="activate" handler="on_add_files_activate" last_modification_time="Sat, 04 Jul 2009 13:04:01 GMT"/>
<child internal-child="image">
- <widget class="GtkImage" id="image369">
+ <widget class="GtkImage" id="image377">
<property name="visible">True</property>
<property name="stock">gtk-add</property>
<property name="icon_size">1</property>
@@ -106,7 +106,7 @@
<signal name="activate" handler="on_add_folders_activate" last_modification_time="Sun, 06 Sep 2009 17:51:40 GMT"/>
<child internal-child="image">
- <widget class="GtkImage" id="image370">
+ <widget class="GtkImage" id="image378">
<property name="visible">True</property>
<property name="stock">gtk-add</property>
<property name="icon_size">1</property>
@@ -127,7 +127,7 @@
<signal name="activate" handler="on_add_audio_cd_activate" last_modification_time="Sat, 10 Oct 2009 15:29:22 GMT"/>
<child internal-child="image">
- <widget class="GtkImage" id="image371">
+ <widget class="GtkImage" id="image379">
<property name="visible">True</property>
<property name="stock">gtk-add</property>
<property name="icon_size">1</property>
@@ -207,7 +207,7 @@
<accelerator key="Q" modifiers="GDK_CONTROL_MASK" signal="activate"/>
<child internal-child="image">
- <widget class="GtkImage" id="image372">
+ <widget class="GtkImage" id="image380">
<property name="visible">True</property>
<property name="stock">gtk-quit</property>
<property name="icon_size">1</property>
@@ -241,7 +241,7 @@
<signal name="activate" handler="on_clear1_activate" last_modification_time="Sun, 06 Sep 2009 18:30:03 GMT"/>
<child internal-child="image">
- <widget class="GtkImage" id="image373">
+ <widget class="GtkImage" id="image381">
<property name="visible">True</property>
<property name="stock">gtk-clear</property>
<property name="icon_size">1</property>
@@ -270,6 +270,7 @@
<property name="label" translatable="yes">Deselect all</property>
<property name="use_underline">True</property>
<signal name="activate" handler="on_deselect_all1_activate" last_modification_time="Fri, 02 Apr 2010 11:30:31 GMT"/>
+ <accelerator key="Escape" modifiers="0" signal="activate"/>
</widget>
</child>
@@ -299,7 +300,7 @@
<signal name="activate" handler="on_remove1_activate" last_modification_time="Sun, 06 Sep 2009 18:30:03 GMT"/>
<child internal-child="image">
- <widget class="GtkImage" id="image374">
+ <widget class="GtkImage" id="image382">
<property name="visible">True</property>
<property name="stock">gtk-remove</property>
<property name="icon_size">1</property>
@@ -558,7 +559,7 @@
<signal name="activate" handler="on_help1_activate" last_modification_time="Tue, 08 Sep 2009 17:32:06 GMT"/>
<child internal-child="image">
- <widget class="GtkImage" id="image375">
+ <widget class="GtkImage" id="image383">
<property name="visible">True</property>
<property name="stock">gtk-help</property>
<property name="icon_size">1</property>
@@ -618,7 +619,7 @@
<signal name="activate" handler="on_about1_activate" last_modification_time="Sat, 04 Jul 2009 12:57:58 GMT"/>
<child internal-child="image">
- <widget class="GtkImage" id="image376">
+ <widget class="GtkImage" id="image384">
<property name="visible">True</property>
<property name="stock">gtk-about</property>
<property name="icon_size">1</property>
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;
}
diff --git a/plugins/gtkui/gtkui.c b/plugins/gtkui/gtkui.c
index ab26084f..60c996cf 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;
@@ -370,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);
@@ -645,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
@@ -656,6 +658,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;
}
@@ -834,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 ();
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");
diff --git a/plugins/gtkui/plcommon.c b/plugins/gtkui/plcommon.c
index 79ca1994..e1a18363 100644
--- a/plugins/gtkui/plcommon.c
+++ b/plugins/gtkui/plcommon.c
@@ -18,6 +18,7 @@
*/
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
#include "gtkui.h"
#include "plcommon.h"
#include "coverart.h"
@@ -295,6 +296,43 @@ on_remove2_activate (GtkMenuItem *menuitem,
}
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?");
+ gtk_window_set_title (GTK_WINDOW (dlg), "Warning");
+
+ 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);
int inqueue = deadbeef->pl_playqueue_test (it);
@@ -303,6 +341,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 +376,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 +403,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);
diff --git a/plugins/gtkui/search.c b/plugins/gtkui/search.c
index cfffa284..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);
}
@@ -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
}
diff --git a/plugins/gtkui/trkproperties.c b/plugins/gtkui/trkproperties.c
index 481cdc8c..73f55061 100644
--- a/plugins/gtkui/trkproperties.c
+++ b/plugins/gtkui/trkproperties.c
@@ -19,6 +19,7 @@
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <string.h>
+#include "ddblistview.h"
#include "trkproperties.h"
#include "interface.h"
#include "support.h"
@@ -27,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);
@@ -47,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;
@@ -62,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,10 +102,59 @@ show_track_properties_dlg (DB_playItem_t *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) {
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 ();
+ rend_text2 = gtk_cell_renderer_text_new ();
+ 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);
+ }
+
+ 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;
@@ -117,19 +162,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;
while (types[i]) {
@@ -145,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 {
@@ -211,6 +231,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;
}
diff --git a/plugins/lastfm/lastfm.c b/plugins/lastfm/lastfm.c
index 79946d14..e8d3428b 100644
--- a/plugins/lastfm/lastfm.c
+++ b/plugins/lastfm/lastfm.c
@@ -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,10 @@ 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;
+ }
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,12 +669,15 @@ 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");
+ 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 ();
}
}
@@ -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
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/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;
}
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 <libintl.h>
+#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 <msmith@xiph.org>
+ *
+ *
+ * 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 <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ogg/ogg.h>
+#include <vorbis/codec.h>
+
+#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;i<vc->comments;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(bytes<CHUNKSIZE)
+ state->lasterror = _("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 <msmith@xiph.org>
+ *
+ * VCEdit header.
+ *
+ * last modified: $ID:$
+ */
+
+#ifndef __VCEDIT_H
+#define __VCEDIT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include <ogg/ogg.h>
+#include <vorbis/codec.h>
+
+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..6e589c2e 100644
--- a/plugins/vorbis/vorbis.c
+++ b/plugins/vorbis/vorbis.c
@@ -20,16 +20,19 @@
#include <string.h>
#include <stdlib.h>
#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
#ifdef HAVE_CONFIG_H
# include <config.h>
#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;
}
@@ -71,69 +74,58 @@ 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",
+ "ENCODER", "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], "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);
+ }
}
}
}
deadbeef->pl_add_meta (it, "title", NULL);
+ deadbeef->pl_add_meta (it, "tags", "VorbisComments");
}
static DB_fileinfo_t *
@@ -254,7 +246,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 +312,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,27 +423,178 @@ 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;
}
+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 (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));
+ 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 +621,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
};
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
};
diff --git a/streamer.c b/streamer.c
index 67a79e05..949ef383 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;
@@ -972,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 ();
}
@@ -1352,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;
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
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 @@
<h1 id='bugs'>Reporting Bugs And Requesting Features</h1>
<p>Trackers for reporting bugs, requesting features, etc are <a href="https://sourceforge.net/projects/deadbeef/support">here</a></p>
<h1 id="developers">Developer Information</h1>
-<p>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.</p>
-<p>see <a href="http://sourceforge.net/projects/deadbeef">project site</a> for details.</p>
+<p>How to contribute: <a href="http://contributing.appspot.com/deadbeef">http://contributing.appspot.com/deadbeef</a></p>
+<p>Also, see <a href="http://sourceforge.net/projects/deadbeef">project site</a>.</p>
<h1 id='contacts'>Contacts</h1>
<p>official IRC channel (for english speaking people) is: #deadbeefplayer @ freenode</p>
<p>official jabber conference for russian speaking users is here: deadbeef-ru@conference.jabber.ru</p>