summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Viktor Semykin <thesame.ml@gmail.com>2010-05-25 03:37:44 +0300
committerGravatar Viktor Semykin <thesame.ml@gmail.com>2010-05-25 03:37:44 +0300
commit8c012917f5530d947c1a31adfc6aba4b0cf3853c (patch)
treef9183ee845fd497006dde0c5f80049714a15d9a4
parentbe0f4b1926f7e77c7e03eae7b70ec12f1b905025 (diff)
Misc plugin actions
-rw-r--r--configure.ac11
-rw-r--r--deadbeef.h14
-rw-r--r--plugins/gtkui/plcommon.c48
-rw-r--r--plugins/lastfm/lastfm.c62
-rw-r--r--plugins/shellexec/Makefile.am9
-rw-r--r--plugins/shellexec/shellexec.c125
6 files changed, 262 insertions, 7 deletions
diff --git a/configure.ac b/configure.ac
index d10f8ed2..51113954 100644
--- a/configure.ac
+++ b/configure.ac
@@ -74,6 +74,7 @@ AC_ARG_ENABLE(cdda, [ --disable-cdda disable CD-Audio plugin (defa
AC_ARG_ENABLE(gme, [ --disable-gme disable Game Music Emu plugin for NSF, AY, etc (default: enabled)], [enable_gme=$enableval], [enable_gme=yes])
AC_ARG_ENABLE(dumb, [ --disable-dumb disable D.U.M.B. plugin for MOD, S3M and other tracker formats (default: enabled)], [enable_dumb=$enableval], [enable_dumb=yes])
AC_ARG_ENABLE(notify, [ --disable-notify disable notification-daemon support plugin (default: enabled)], [enable_notify=$enableval], [enable_notify=yes])
+AC_ARG_ENABLE(shellexec, [ --disable-notify disable notification-daemon support plugin (default: enabled)], [enable_notify=$enableval], [enable_notify=yes])
PKG_CHECK_MODULES(DEPS, samplerate)
@@ -290,6 +291,11 @@ if test "x$enable_lfm" != "xno" ; then
fi
fi
+dnl shellexec plugin
+if test "x$enable_shellexec" != "xno" ; then
+ HAVE_SHELLEXEC=yes
+fi
+
if test "x$enable_artwork" != "xno" ; then
if test "x$HAVE_CURL" = "xyes" && test "x$HAVE_VFS_CURL" = "xyes" ; then
HAVE_ARTWORK=yes
@@ -336,7 +342,7 @@ if test "x$HAVE_DBUS" = "xyes" && test "x$enable_notify" != "xno" ; then
AC_SUBST(NOTIFY_CFLAGS)
fi
-PLUGINS_DIRS="plugins/lastfm plugins/mpgmad plugins/vorbis plugins/flac plugins/wavpack plugins/sndfile plugins/vfs_curl plugins/cdda plugins/gtkui plugins/alsa plugins/ffmpeg plugins/hotkeys plugins/oss plugins/artwork plugins/adplug plugins/ffap plugins/sid plugins/nullout plugins/supereq plugins/vtx plugins/gme plugins/dumb plugins/pulse plugins/notify"
+PLUGINS_DIRS="plugins/lastfm plugins/mpgmad plugins/vorbis plugins/flac plugins/wavpack plugins/sndfile plugins/vfs_curl plugins/cdda plugins/gtkui plugins/alsa plugins/ffmpeg plugins/hotkeys plugins/oss plugins/artwork plugins/adplug plugins/ffap plugins/sid plugins/nullout plugins/supereq plugins/vtx plugins/gme plugins/dumb plugins/pulse plugins/notify plugins/shellexec"
AM_CONDITIONAL(HAVE_VORBIS, test "x$HAVE_VORBISPLUGIN" = "xyes")
AM_CONDITIONAL(HAVE_FLAC, test "x$HAVE_FLACPLUGIN" = "xyes")
@@ -362,6 +368,7 @@ AM_CONDITIONAL(HAVE_MPGMAD, test "x$HAVE_MPGMAD" = "xyes")
AM_CONDITIONAL(HAVE_OSS, test "x$HAVE_OSS" = "xyes")
AM_CONDITIONAL(HAVE_SUPEREQ, test "x$HAVE_SUPEREQ" = "xyes")
AM_CONDITIONAL(HAVE_NOTIFY, test "x$HAVE_NOTIFY" = "xyes")
+AM_CONDITIONAL(HAVE_SHELLEXEC, test "x$HAVE_SHELLEXEC" = "xyes")
AC_SUBST(PLUGINS_DIRS)
@@ -414,6 +421,7 @@ PRINT_PLUGIN_INFO([pulse],[PulseAudio output plugin],[test "x$HAVE_PULSEPLUGIN"
PRINT_PLUGIN_INFO([artwork],[Cover art plugin],[test "x$HAVE_ARTWORK" = "xyes"])
PRINT_PLUGIN_INFO([supereq],[Equalizer based on Super EQ library by Naoki Shibata],[test "x$HAVE_SUPEREQ" = "xyes"])
PRINT_PLUGIN_INFO([notify],[notification-daemon support plugin],[test "x$HAVE_NOTIFY" = "xyes"])
+PRINT_PLUGIN_INFO([shellexec],[shell commands plugin],[test "x$HAVE_SHELLEXEC" = "xyes"])
echo
@@ -445,6 +453,7 @@ plugins/pulse/Makefile
plugins/artwork/Makefile
plugins/supereq/Makefile
plugins/notify/Makefile
+plugins/shellexec/Makefile
deadbeef.desktop
])
diff --git a/deadbeef.h b/deadbeef.h
index bbbecb48..0afd47f4 100644
--- a/deadbeef.h
+++ b/deadbeef.h
@@ -646,12 +646,26 @@ typedef struct DB_dsp_s {
int (*enabled) (void);
} DB_dsp_t;
+typedef struct
+{
+ const char *title;
+ int (*callback) (DB_playItem_t *it, void *data);
+ void *data;
+} DB_single_action_t;
+
// misc plugin
// purpose is to provide extra services
// e.g. scrobbling, converting, tagging, custom gui, etc.
// misc plugins should be mostly event driven, so no special entry points in them
typedef struct {
DB_plugin_t plugin;
+ /* Returns actions available for this track
+ it - track
+ actions - array of actions
+ size - on input, size of array in pointers; on output - count of actions
+ returns 0 on error, nonzero on success
+ */
+ int (*get_single_actions) (DB_playItem_t *it, DB_single_action_t *actions[], int *size);
} DB_misc_t;
// vfs plugin
diff --git a/plugins/gtkui/plcommon.c b/plugins/gtkui/plcommon.c
index 96ddff69..3b72b901 100644
--- a/plugins/gtkui/plcommon.c
+++ b/plugins/gtkui/plcommon.c
@@ -346,6 +346,14 @@ on_remove_from_disk_activate (GtkMenuItem *menuitem,
}
void
+actionitem_activate (GtkMenuItem *menuitem,
+ DB_single_action_t *action)
+{
+ DB_playItem_t *it = deadbeef->pl_get_for_idx_and_iter (clicked_idx, PL_MAIN);
+ action->callback (it, action->data);
+}
+
+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);
@@ -399,6 +407,46 @@ list_context_menu (DdbListview *listview, DdbListviewIter it, int idx) {
gtk_container_add (GTK_CONTAINER (playlist_menu), separator8);
gtk_widget_set_sensitive (separator8, FALSE);
+ ///
+ int count;
+ int i, j;
+ GtkWidget *actionitem;
+ DB_single_action_t *actions[4];
+ DB_plugin_t **plugins = deadbeef->plug_get_list();
+ for (i = 0; plugins[i]; i++)
+ {
+ if (plugins[i]->type != DB_PLUGIN_MISC)
+ continue;
+
+ DB_misc_t *misc = (DB_misc_t*)plugins[i];
+ if (!misc->get_single_actions)
+ continue;
+
+ count = 4;
+ if (!misc->get_single_actions (it, actions, &count))
+ continue;
+
+ if (count == 0)
+ continue;
+
+ for (j = 0; j < count; j++)
+ {
+ actionitem = gtk_menu_item_new_with_mnemonic (actions[j]->title);
+ gtk_widget_show (actionitem);
+ gtk_container_add (GTK_CONTAINER (playlist_menu), actionitem);
+ g_object_set_data (G_OBJECT (actionitem), "ps", listview);
+
+ g_signal_connect ((gpointer) actionitem, "activate",
+ G_CALLBACK (actionitem_activate),
+ actions[j]);
+ }
+ separator8 = gtk_separator_menu_item_new ();
+ gtk_widget_show (separator8);
+ gtk_container_add (GTK_CONTAINER (playlist_menu), separator8);
+ gtk_widget_set_sensitive (separator8, FALSE);
+ }
+ ///
+
properties1 = gtk_menu_item_new_with_mnemonic ("Properties");
gtk_widget_show (properties1);
gtk_container_add (GTK_CONTAINER (playlist_menu), properties1);
diff --git a/plugins/lastfm/lastfm.c b/plugins/lastfm/lastfm.c
index 3ba8def8..02dff618 100644
--- a/plugins/lastfm/lastfm.c
+++ b/plugins/lastfm/lastfm.c
@@ -334,26 +334,31 @@ lfm_fetch_song_info (DB_playItem_t *song, const char **a, const char **t, const
static int
lfm_uri_encode (char *out, int outl, const char *str) {
int l = outl;
- static const char echars[] = " ;/?:@=#&+";
//trace ("lfm_uri_encode %p %d %s\n", out, outl, str);
while (*str) {
if (outl <= 1) {
//trace ("no space left for 1 byte in buffer\n");
return -1;
}
- if (strchr (echars, *str)) {
+
+ if (!(
+ (*str >= '0' && *str <= '9') ||
+ (*str >= 'a' && *str <= 'z') ||
+ (*str >= 'A' && *str <= 'Z') ||
+ (*str == ' ')
+ ))
+ {
if (outl <= 3) {
//trace ("no space left for 3 bytes in the buffer\n");
return -1;
}
- //trace ("adding escaped value for %c\n", *str);
- snprintf (out, outl, "%%%02x", (int)*str);
+ snprintf (out, outl, "%%%02x", (uint8_t)*str);
outl -= 3;
str++;
out += 3;
}
else {
- *out = *str;
+ *out = *str == ' ' ? '+' : *str;
out++;
str++;
outl--;
@@ -819,6 +824,50 @@ lastfm_stop (void) {
return 0;
}
+static int
+lfm_action_lookup (DB_playItem_t *it, void *data)
+{
+ const char *artist = deadbeef->pl_find_meta (it, "artist");
+ const char *title = deadbeef->pl_find_meta (it, "title");
+
+ if (!title || !artist)
+ return 0;
+
+ char eartist [strlen (artist) * 3 + 1];
+ char etitle [strlen (title) * 3 + 1];
+
+ if (-1 == lfm_uri_encode (eartist, sizeof (eartist), artist))
+ return 0;
+
+ if (-1 == lfm_uri_encode (etitle, sizeof (etitle), title))
+ return 0;
+
+ char *command = NULL;
+ if (-1 == asprintf (&command, "xdg-open http://www.last.fm/music/%s/_/%s", eartist, etitle))
+ return 0;
+ system (command);
+ free (command);
+}
+
+static DB_single_action_t lookup_action = {
+ .title = "Lookup at Last.fm",
+ .callback = lfm_action_lookup
+};
+
+static int
+lfm_get_single_actions (DB_playItem_t *it, DB_single_action_t *actions[], int *size)
+{
+ if (deadbeef->pl_find_meta (it, "artist") &&
+ deadbeef->pl_find_meta (it, "title"))
+ {
+ actions[0] = &lookup_action;
+ *size = 1;
+ }
+ else
+ *size = 0;
+ return 1;
+}
+
static const char settings_dlg[] =
"property \"Enable scrobbler\" checkbox lastfm.enable 0;"
"property \"Disable nowplaying\" checkbox lastfm.disable_np 0;"
@@ -840,5 +889,6 @@ static DB_misc_t plugin = {
.plugin.website = "http://deadbeef.sf.net",
.plugin.start = lastfm_start,
.plugin.stop = lastfm_stop,
- .plugin.configdialog = settings_dlg
+ .plugin.configdialog = settings_dlg,
+ .get_single_actions = lfm_get_single_actions
};
diff --git a/plugins/shellexec/Makefile.am b/plugins/shellexec/Makefile.am
new file mode 100644
index 00000000..65d6a5b8
--- /dev/null
+++ b/plugins/shellexec/Makefile.am
@@ -0,0 +1,9 @@
+if HAVE_SHELLEXEC
+shxdir = $(libdir)/$(PACKAGE)
+pkglib_LTLIBRARIES = shellexec.la
+shellexec_la_SOURCES = shellexec.c
+shellexec_la_LDFLAGS = -module
+
+shellexec_la_LIBADD = $(LDADD) $(HOTKEYS_LIBS)
+AM_CFLAGS = $(CFLAGS) -std=c99
+endif
diff --git a/plugins/shellexec/shellexec.c b/plugins/shellexec/shellexec.c
new file mode 100644
index 00000000..39e7f4da
--- /dev/null
+++ b/plugins/shellexec/shellexec.c
@@ -0,0 +1,125 @@
+/*
+ Shellexec plugin for DeaDBeeF
+ Copyright (C) 2009 Viktor Semykin <thesame.ml@gmail.com>
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+#include <stdio.h>
+#include <string.h>
+
+#include "../../deadbeef.h"
+
+#define MAX_COMMANDS 128
+
+#define trace(...) { fprintf(stderr, __VA_ARGS__); }
+//#define trace(fmt,...)
+
+static DB_misc_t plugin;
+static DB_functions_t *deadbeef;
+
+DB_single_action_t shx_actions [MAX_COMMANDS];
+static int single_action_count;
+
+DB_plugin_t *
+shellexec_load (DB_functions_t *api) {
+ deadbeef = api;
+ return DB_PLUGIN (&plugin);
+}
+
+static int
+shx_get_single_actions (DB_playItem_t *it, DB_single_action_t *actions[], int *size)
+{
+ if (*size < single_action_count)
+ return 0;
+
+ int i;
+ *size = single_action_count;
+ trace ("Shellexec: %d actions\n", single_action_count);
+ for (i=0; i < single_action_count; i++)
+ actions[i] = &shx_actions[i];
+ return 1;
+}
+
+static int
+shx_callback (DB_playItem_t *it, void *data)
+{
+ char fmt[1024]; //FIXME: possible stack corruption
+ deadbeef->pl_format_title (it, -1, fmt, sizeof (fmt), -1, data);
+ printf ("%s\n", fmt);
+ return 0;
+}
+
+static char*
+trim (char* s)
+{
+ char *h, *t;
+
+ for (h = s; *h == ' ' || *h == '\t'; h++);
+ for (t = s + strlen (s); *t == ' ' || *t == '\t'; t--);
+ * (t+1) = 0;
+ return h;
+}
+
+static int
+shellexec_start (void)
+{
+ trace ("Starting shellexec\n");
+ single_action_count = 0;
+ DB_conf_item_t *item = deadbeef->conf_find ("shellexec.", NULL);
+ while (item)
+ {
+ if (single_action_count == MAX_COMMANDS)
+ {
+ fprintf (stdout, "Shellexec: max number of commands (%d) exceeded\n", MAX_COMMANDS);
+ break;
+ }
+ size_t l = strlen (item->value) + 1;
+ char tmp[l];
+ strcpy (tmp, item->value);
+ trace ("Shellexec: %s\n", tmp);
+
+ char *semicolon = strchr (tmp, ':');
+ if (!semicolon)
+ {
+ fprintf (stdout, "Shellexec: wrong option <%s>\n", tmp);
+ continue;
+ }
+
+ *semicolon = 0;
+
+ shx_actions[single_action_count].title = strdup (trim (semicolon + 1));
+ shx_actions[single_action_count].callback = shx_callback;
+ shx_actions[single_action_count].data = strdup (trim (tmp));
+
+ item = deadbeef->conf_find ("shellexec.", item);
+ single_action_count++;
+ }
+}
+
+// define plugin interface
+static DB_misc_t plugin = {
+ .plugin.api_vmajor = DB_API_VERSION_MAJOR,
+ .plugin.api_vminor = DB_API_VERSION_MINOR,
+ .plugin.type = DB_PLUGIN_MISC,
+ .plugin.id = "shellexec",
+ .plugin.name = "Shell commands for tracks",
+ .plugin.descr = "Executes configurable shell commands for tracks",
+ .plugin.author = "Viktor Semykin",
+ .plugin.email = "thesame.ml@gmail.com",
+ .plugin.website = "http://deadbeef.sf.net",
+ .plugin.start = shellexec_start,
+
+ .get_single_actions = shx_get_single_actions
+};
+