diff options
author | Alexey Yakovenko <wakeroid@gmail.com> | 2010-02-28 19:04:15 +0100 |
---|---|---|
committer | Alexey Yakovenko <wakeroid@gmail.com> | 2010-02-28 19:04:15 +0100 |
commit | dc16ab2da7065f82230087a84654167aec5444dc (patch) | |
tree | 469d9b9da96de74fc27c261112fe68b9c6c0d9eb /plugins | |
parent | 515d5e66bb81bdf1fc63b28e8f50ee558d767630 (diff) |
cover-art WIP
Diffstat (limited to 'plugins')
-rw-r--r-- | plugins/artwork/Makefile.am | 8 | ||||
-rw-r--r-- | plugins/artwork/albumartorg.c | 54 | ||||
-rw-r--r-- | plugins/artwork/albumartorg.h | 25 | ||||
-rw-r--r-- | plugins/artwork/artwork.c | 233 | ||||
-rw-r--r-- | plugins/artwork/artwork.h | 24 | ||||
-rw-r--r-- | plugins/artwork/lastfm.c | 40 | ||||
-rw-r--r-- | plugins/artwork/lastfm.h | 25 | ||||
-rw-r--r-- | plugins/gtkui/Makefile.am | 3 | ||||
-rw-r--r-- | plugins/gtkui/coverart.c | 70 | ||||
-rw-r--r-- | plugins/gtkui/coverart.h | 36 | ||||
-rw-r--r-- | plugins/gtkui/gtkui.c | 15 | ||||
-rw-r--r-- | plugins/gtkui/mainplaylist.c | 14 |
12 files changed, 540 insertions, 7 deletions
diff --git a/plugins/artwork/Makefile.am b/plugins/artwork/Makefile.am new file mode 100644 index 00000000..de137844 --- /dev/null +++ b/plugins/artwork/Makefile.am @@ -0,0 +1,8 @@ +artworkdir = $(libdir)/$(PACKAGE) +pkglib_LTLIBRARIES = artwork.la +artwork_la_SOURCES = artwork.h albumartorg.c artwork.c lastfm.c + +artwork_la_LDFLAGS = -module + +artwork_la_LIBADD = $(LDADD) $(ARTWORK_DEPS_LIBS) $(CURL_LIBS) +AM_CFLAGS = -std=c99 $(ARTWORK_DEPS_CFLAGS) diff --git a/plugins/artwork/albumartorg.c b/plugins/artwork/albumartorg.c new file mode 100644 index 00000000..06247f74 --- /dev/null +++ b/plugins/artwork/albumartorg.c @@ -0,0 +1,54 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include <stdlib.h> +#include <curl/curl.h> +#include <string.h> +#include "artwork.h" + +int +fetch_from_albumart_org (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), "http://www.albumart.org/index.php?srchkey=%s+%s&itempage=1&newsearch=1&searchindex=Music", artist_url, album_url); + 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; + } + char *end = strstr (img, "._SL160_"); + if (!end) + { + free (response); + return 0; + } + strcpy (end, ".jpg"); + + int res = fetch_to_file (img, dest); + free (response); + return res; +} + diff --git a/plugins/artwork/albumartorg.h b/plugins/artwork/albumartorg.h new file mode 100644 index 00000000..4d1d494a --- /dev/null +++ b/plugins/artwork/albumartorg.h @@ -0,0 +1,25 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#ifndef __ALBUMARTORG_H +#define __ALBUMARTORG_H + +int +fetch_from_albumart_org (const char *artist, const char *album, const char *dest); + +#endif diff --git a/plugins/artwork/artwork.c b/plugins/artwork/artwork.c new file mode 100644 index 00000000..a136d29e --- /dev/null +++ b/plugins/artwork/artwork.c @@ -0,0 +1,233 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <curl/curl.h> +#include <errno.h> +#include <dirent.h> +#include "../../deadbeef.h" +#include "artwork.h" +#include "lastfm.h" +#include "albumartorg.h" + +//#define trace(...) { fprintf(stderr, __VA_ARGS__); } +#define trace(...) + +static DB_artwork_plugin_t plugin; +DB_functions_t *deadbeef; + +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) + { + fprintf (stderr, "Could not open %s for writing\n", temp); + return 0; + } + ret = fetch_to_stream (url, stream); + if (ret != 0) + printf ("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); + } + 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) +{ + char *tmp = strdup (dir); + char *slash = tmp; + struct stat stat_buf; + do + { + slash = strstr (slash+1, "/"); + if (slash) + *slash = 0; +// trace ("Checking %s\n", tmp); + if (-1 == stat (tmp, &stat_buf)) + { + if (0 != mkdir (tmp, mode)) + { + trace ("Failed to create %s (%d)\n", tmp, errno); + free (tmp); + return 0; + } + } + if (slash) + *slash = '/'; + } + while (slash); + free (tmp); + return 1; +} + +typedef struct +{ + const char *artist; + const char *album; + artwork_callback callback; +} fetcher_thread_param_t; + +static void +fetcher_thread (fetcher_thread_param_t *param) +{ + char path [1024]; + + snprintf (path, sizeof (path), "%s/artcache/%s", deadbeef->get_config_dir (), param->artist); + if (!check_dir (path, 0755)) + goto finalize; + + snprintf (path, sizeof (path), "%s/artcache/%s/%s.jpg", deadbeef->get_config_dir (), param->artist, param->album); + + if (!fetch_from_lastfm (param->artist, param->album, path)) + if (!fetch_from_albumart_org (param->artist, param->album, path)) + goto finalize; + + if (param->callback) + param->callback (param->artist, param->album); + +finalize: + free (param); + return; +} + +static int +filter_jpg (const struct dirent *f) +{ + const char *ext = strrchr (f->d_name, '.'); + if (!ext) + return 0; + if (0 == strcasecmp (ext, ".jpg") || + 0 == strcasecmp (ext, ".jpeg")) + return 1; + return 0; +} + +char* +get_album_art (DB_playItem_t *track, artwork_callback callback) +{ + char path [1024]; + struct dirent **files; + int files_count; + + const char *album = deadbeef->pl_find_meta (track, "album"); + const char *artist = deadbeef->pl_find_meta (track, "artist"); + + /* Searching in track diectory */ + strncpy (path, track->fname, sizeof (path)); + *(strrchr (path, '/')) = 0; //Supposing at least one slash exist +// trace ("Scanning: %s\n", path); + files_count = scandir (path, &files, filter_jpg, alphasort); + +/* if (files_count < 0) + trace ("Failed to scan dir"); + if (files_count == 0) + trace ("No jpg's found in directoy");*/ + if (files_count > 0) + { + strcat (path, "/"); + strcat (path, files[0]->d_name); + char *res = strdup (path); + trace ("Found: %s\n", res); + int i; + for (i = 0; i < files_count; i++) + free (files [i]); + return res; + } + + /* Searching in cache */ + if (!artist || !*artist || !album || !*album) + { + //give up + return PREFIX "/share/deadbeef/pixmaps/blank_cd.jpg"; + } + + snprintf (path, sizeof (path), "%s/artcache/%s/%s.jpg", deadbeef->get_config_dir (), artist, album); + + struct stat stat_buf; + if (0 == stat (path, &stat_buf)) + { + char *res = strdup (path); +// printf ("Found in cache: %s\n", res); + return res; + } + + /* Downloading from internet */ + trace ("Downloading: %s %s\n", artist, album); + fetcher_thread_param_t *param = malloc (sizeof (fetcher_thread_param_t)); + param->artist = artist; + param->album = album; + param->callback = callback; + deadbeef->thread_start ((void (*)(void *))fetcher_thread, param); + return PREFIX "/share/deadbeef/pixmaps/blank_cd.jpg"; +} + +DB_plugin_t * +artwork_load (DB_functions_t *api) { + deadbeef = api; + return DB_PLUGIN (&plugin); +} + +static int +artwork_plugin_start (void) +{ +} + +static int +artwork_plugin_stop (void) +{ +} + +// define plugin interface +static DB_artwork_plugin_t plugin = { + .plugin.plugin.api_vmajor = DB_API_VERSION_MAJOR, + .plugin.plugin.api_vminor = DB_API_VERSION_MINOR, + .plugin.plugin.type = DB_PLUGIN_MISC, + .plugin.plugin.id = "cover_loader", + .plugin.plugin.name = "Album Artwork", + .plugin.plugin.descr = "Loads album artwork either from local directories or from internet", + .plugin.plugin.author = "Viktor Semykin", + .plugin.plugin.email = "thesame.ml@gmail.com", + .plugin.plugin.website = "http://deadbeef.sf.net", + .plugin.plugin.start = artwork_plugin_start, + .plugin.plugin.stop = artwork_plugin_stop, + .get_album_art = get_album_art, +}; diff --git a/plugins/artwork/artwork.h b/plugins/artwork/artwork.h new file mode 100644 index 00000000..816ca349 --- /dev/null +++ b/plugins/artwork/artwork.h @@ -0,0 +1,24 @@ +#ifndef __ARTWORK_H +#define __ARTWORK_H + +#include "../../deadbeef.h" + +typedef void (*artwork_callback) (const char *artist, const char *album); + +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 + char* (*get_album_art) (DB_playItem_t *track, artwork_callback callback); +} DB_artwork_plugin_t; + +#endif /*__ARTWORK_H*/ + diff --git a/plugins/artwork/lastfm.c b/plugins/artwork/lastfm.c new file mode 100644 index 00000000..e64af10b --- /dev/null +++ b/plugins/artwork/lastfm.c @@ -0,0 +1,40 @@ +#include <stdio.h> +#include <string.h> +#include <curl/curl.h> +#include <stdlib.h> + +#include "artwork.h" + +#define BASE_URL "http://ws.audioscrobbler.com/2.0/" +#define API_KEY "b25b959554ed76058ac220b7b2e0a026" + +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 ); + 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; + } + img += 25; + char *end = strstr (img, "</image>"); + if (!end || (end == img)) + { + free (response); + return 0; + } + *end = 0; + int res = fetch_to_file (img, dest); + free (response); + return res; +} diff --git a/plugins/artwork/lastfm.h b/plugins/artwork/lastfm.h new file mode 100644 index 00000000..09ec817c --- /dev/null +++ b/plugins/artwork/lastfm.h @@ -0,0 +1,25 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#ifndef __LASTFM_H +#define __LASTFM_H + +int +fetch_from_lastfm (const char *artist, const char *album, const char *dest); + +#endif diff --git a/plugins/gtkui/Makefile.am b/plugins/gtkui/Makefile.am index eeeabce9..ed8d1c24 100644 --- a/plugins/gtkui/Makefile.am +++ b/plugins/gtkui/Makefile.am @@ -13,7 +13,8 @@ gtkui_la_SOURCES = gtkui.c gtkui.h\ parser.c parser.h\ ddbtabstrip.c ddbtabstrip.h\ ddbvolumebar.c ddbvolumebar.h\ - trkproperties.c trkproperties.h + trkproperties.c trkproperties.h\ + coverart.c coverart.h gtkui_la_LDFLAGS = -module diff --git a/plugins/gtkui/coverart.c b/plugins/gtkui/coverart.c new file mode 100644 index 00000000..7d042e5f --- /dev/null +++ b/plugins/gtkui/coverart.c @@ -0,0 +1,70 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include <gtk/gtk.h> +#include "coverart.h" +#include "../artwork/artwork.h" + +extern DB_artwork_plugin_t *coverart_plugin; + +void +cover_avail_callback (const char *artist, const char *album) { +} + +static GdkPixbuf * +get_pixbuf (const char *fname, int width) { + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (fname, NULL); + if (!pixbuf) { + return NULL; + } + int w, h; + w = gdk_pixbuf_get_width (pixbuf); + h = gdk_pixbuf_get_height (pixbuf); + if (w == width) { + return pixbuf; + } + int height; + if (w > h) { + height = width * h / w; + } + else if (h > w) { + height = width; + width = height * w / h; + } + else { + height = width; + } + printf ("width=%d/%d, height=%d/%d\n", width, w, height, h); + + GdkPixbuf *scaled = gdk_pixbuf_scale_simple (pixbuf, width, height, GDK_INTERP_BILINEAR); + g_object_unref (pixbuf); + return scaled; +} + +GdkPixbuf * +get_cover_art (DB_playItem_t *it, int width) { + if (!coverart_plugin) { + return NULL; + } + const char *fname = coverart_plugin->get_album_art (it, cover_avail_callback); + if (fname) { + printf ("loading %s\n", fname); + return get_pixbuf (fname, width); + } + return NULL; +} diff --git a/plugins/gtkui/coverart.h b/plugins/gtkui/coverart.h new file mode 100644 index 00000000..0f1238ea --- /dev/null +++ b/plugins/gtkui/coverart.h @@ -0,0 +1,36 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#ifndef __COVERART_H +#define __COVERART_H + +#include <gtk/gtk.h> +#include "../../deadbeef.h" + +// this function puts request for cover art into queue, or returns default image +// of specific size +// +// if the image is not available immediately -- callback will be called later, +// which will redraw all tracks matching the query +// +// if cover art loader plugin is not available -- NULL will be returned +GdkPixbuf * +get_cover_art (DB_playItem_t *it, int width); + +#endif + diff --git a/plugins/gtkui/gtkui.c b/plugins/gtkui/gtkui.c index acde4de8..316a237f 100644 --- a/plugins/gtkui/gtkui.c +++ b/plugins/gtkui/gtkui.c @@ -39,6 +39,7 @@ #include "parser.h" #include "drawing.h" #include "trkproperties.h" +#include "../artwork/artwork.h" //#define trace(...) { fprintf(stderr, __VA_ARGS__); } #define trace(fmt,...) @@ -48,6 +49,9 @@ DB_functions_t *deadbeef; static intptr_t gtk_tid; +// cover art loading plugin +DB_artwork_plugin_t *coverart_plugin = NULL; + // main widgets GtkWidget *mainwin; GtkWidget *searchwin; @@ -825,6 +829,17 @@ gtkui_thread (void *ctx) { static int gtkui_start (void) { + // find coverart plugin + DB_plugin_t **plugins = deadbeef->plug_get_list (); + for (int i = 0; plugins[i]; i++) { + DB_plugin_t *p = plugins[i]; + if (p->id && !strcmp (p->id, "cover_loader")) { + fprintf (stderr, "gtkui: found cover-art loader plugin\n"); + coverart_plugin = (DB_artwork_plugin_t *)p; + break; + } + } + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_ACTIVATE, DB_CALLBACK (gtkui_on_activate), 0); deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_SONGCHANGED, DB_CALLBACK (gtkui_on_songchanged), 0); deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_TRACKINFOCHANGED, DB_CALLBACK (gtkui_on_trackinfochanged), 0); diff --git a/plugins/gtkui/mainplaylist.c b/plugins/gtkui/mainplaylist.c index 3405e4fb..61fbef50 100644 --- a/plugins/gtkui/mainplaylist.c +++ b/plugins/gtkui/mainplaylist.c @@ -26,8 +26,7 @@ #include "support.h" #include "drawing.h" #include "trkproperties.h" - -#pragma GCC optimize("O0") +#include "coverart.h" //#define trace(...) { fprintf(stderr, __VA_ARGS__); } #define trace(fmt,...) @@ -295,12 +294,15 @@ void main_draw_column_data (DdbListview *listview, GdkDrawable *drawable, DdbLis int h = cwidth - group_y; h = min (height, h); gdk_draw_rectangle (drawable, GTK_WIDGET (listview)->style->white_gc, TRUE, x, y, width, h); - GdkPixbuf *pixbuf = gdk_pixbuf_scale_simple ((GdkPixbuf *)play16_pixbuf, width, width, GDK_INTERP_BILINEAR); - gdk_draw_pixbuf (drawable, GTK_WIDGET (listview)->style->white_gc, pixbuf, 0, group_y, x, y, width, h, GDK_RGB_DITHER_NONE, 0, 0); - g_object_unref (pixbuf); +// GdkPixbuf *pixbuf = gdk_pixbuf_scale_simple ((GdkPixbuf *)play16_pixbuf, width, width, GDK_INTERP_BILINEAR); + if (it) { + GdkPixbuf *pixbuf = get_cover_art (it, width); + gdk_draw_pixbuf (drawable, GTK_WIDGET (listview)->style->white_gc, pixbuf, 0, group_y, x, y, width, h, GDK_RGB_DITHER_NONE, 0, 0); + g_object_unref (pixbuf); + } } } - else if (it == deadbeef->streamer_get_playing_track () && cinf->id == DB_COLUMN_PLAYING) { + else if (it && it == deadbeef->streamer_get_playing_track () && cinf->id == DB_COLUMN_PLAYING) { int paused = deadbeef->get_output ()->state () == OUTPUT_STATE_PAUSED; int buffering = !deadbeef->streamer_ok_to_read (-1); uintptr_t pixbuf; |