/* * Pidgin-Rhythmbox plugin. * * Copyright (c) 2006 Jon Oberheide. * * 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., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #define DBUS_API_SUBJECT_TO_CHANGE 1 #include #include #include #include #include #include #include "gtkplugin.h" #include "util.h" #include "debug.h" #include "connection.h" #include "version.h" #define PIDGINRB_PLUGIN_ID "core-pidgin-rhythmbox" #define OPT_PIDGINRB "/plugins/pidgin_rhythmbox" #define OPT_PROCESS_STATUS OPT_PIDGINRB "/process_status" #define OPT_PROCESS_USERINFO OPT_PIDGINRB "/process_userinfo" #define OPT_LYRICS_LINK OPT_PIDGINRB "/lyrics_link" #define OPT_FORMAT_STRING OPT_PIDGINRB "/format_string" #define DBUS_RB_NAMESPACE "org.gnome.Rhythmbox" #define DBUS_RB_SHELL DBUS_RB_NAMESPACE ".Shell" #define DBUS_RB_PLAYER DBUS_RB_NAMESPACE ".Player" #define DBUS_RB_SHELL_PATH "/org/gnome/Rhythmbox/Shell" #define DBUS_RB_PLAYER_PATH "/org/gnome/Rhythmbox/Player" #define DBUS_RB_URI_SIGNAL "playingUriChanged" #define DBUS_RB_PLAYING_SIGNAL "playingChanged" #define RB_TOKEN "%rb" #define RB_START "" #define RB_END "" #define rb_debug(fmt, ...) purple_debug(PURPLE_DEBUG_INFO, "Pidgin-Rhythmbox", \ fmt, ## __VA_ARGS__); #define rb_error(fmt, ...) purple_debug(PURPLE_DEBUG_ERROR, "Pidgin-Rhythmbox", \ fmt, ## __VA_ARGS__); typedef struct { PurplePlugin *plugin; DBusGConnection *bus; DBusGProxy *shell; DBusGProxy *player; } pidginrb_t; pidginrb_t *pidginrb; static void rb_process_status(PurpleConnection *gc, gchar *rb_info) { gchar *new, *tmp1, *tmp2, *begin, *end; const gchar *old, *proto; PurpleAccount *account; PurplePresence *presence; PurplePlugin *prpl; PurplePluginProtocolInfo *prpl_info; PurpleStatus *status; account = purple_connection_get_account(gc); presence = purple_account_get_presence(account); proto = purple_account_get_protocol_id(account); prpl = purple_find_prpl(proto); g_return_if_fail(prpl != NULL); prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); g_return_if_fail(prpl_info != NULL && prpl_info->set_status != NULL); status = purple_presence_get_active_status(presence); g_return_if_fail(status != NULL); old = purple_status_get_attr_string(status, "message"); g_return_if_fail(old != NULL && strlen(old) != 0); g_return_if_fail(strstr(old, RB_TOKEN) || (strstr(old, RB_START) && strstr(old, RB_END))); /* strip lyrics link from info due to oscar's avail message length */ if (purple_presence_is_available(presence) && g_ascii_strcasecmp(proto, "prpl-oscar") == 0) { tmp1 = purple_markup_strip_html(rb_info); g_return_if_fail(tmp1 != NULL); begin = strstr(tmp1, " (http://jon.oberheide.org/projects/pidgin-rhythmbox/query.php"); if (begin) { end = strstr(begin, ")") + strlen(")"); if (end) { tmp2 = g_strndup(tmp1, strlen(tmp1) - strlen(begin)); rb_info = g_strconcat(tmp2, end, NULL); g_free(tmp2); } } g_free(tmp1); } /* construct our new status message */ tmp1 = purple_strreplace(old, RB_TOKEN, RB_START RB_END); g_return_if_fail(tmp1 != NULL); begin = strstr(tmp1, RB_START); end = strstr(tmp1, RB_END); tmp2 = g_strndup(tmp1, strlen(tmp1) - strlen(begin) + strlen(RB_START)); new = g_strconcat(tmp2, rb_info, end, NULL); g_free(tmp1); g_free(tmp2); /* only set the status message if the text has changed */ if (g_ascii_strcasecmp(old, new) != 0) { purple_status_set_attr_string(status, "message", new); prpl_info->set_status(account, status); } g_free(new); } static void rb_process_userinfo(PurpleConnection *gc, gchar *rb_info) { gchar *new; const gchar *old, *proto; PurpleAccount *account; PurplePlugin *prpl; PurplePluginProtocolInfo *prpl_info; account = purple_connection_get_account(gc); proto = purple_account_get_protocol_id(account); prpl = purple_find_prpl(proto); g_return_if_fail(prpl != NULL); prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(prpl); g_return_if_fail(prpl_info != NULL && prpl_info->set_status != NULL); /* retrieve the old user info */ old = purple_account_get_user_info(account); g_return_if_fail(old != NULL && strlen(old) != 0); g_return_if_fail(strstr(old, RB_TOKEN)); /* replace the %rb with our song info */ new = purple_strreplace(old, RB_TOKEN, rb_info); g_return_if_fail(new != NULL); /* only set the user info if the text has changed */ if (g_ascii_strcasecmp(old, new) != 0) { prpl_info->set_info(gc, new); } g_free(new); } static void rb_process(gchar *rb_info) { GList *l; PurpleConnection *gc; for (l = purple_connections_get_all(); l != NULL; l = l->next) { gc = (PurpleConnection *) l->data; /* make sure we're connected */ if (purple_connection_get_state(gc) != PURPLE_CONNECTED) { continue; } if (purple_prefs_get_bool(OPT_PROCESS_STATUS)) { rb_process_status(gc, rb_info); } if (purple_prefs_get_bool(OPT_PROCESS_USERINFO)) { rb_process_userinfo(gc, rb_info); } } } static void uri_signal_cb(DBusGProxy *player_proxy, const gchar *uri, gpointer data) { gint i; GValue *value; GHashTable *table = NULL; const gchar *artist = NULL; const gchar *album = NULL; const gchar *genre = NULL; const gchar *title = NULL; gchar *format, *rb_info = NULL; g_return_if_fail(uri != NULL); if (!dbus_g_proxy_call(pidginrb->shell, "getSongProperties", NULL, G_TYPE_STRING, uri, G_TYPE_INVALID, dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE), &table, G_TYPE_INVALID)) { return; } /* fetch values from hash table */ value = (GValue *) g_hash_table_lookup(table, "artist"); if (value != NULL && G_VALUE_HOLDS_STRING(value)) { artist = g_value_get_string(value); } value = (GValue *) g_hash_table_lookup(table, "album"); if (value != NULL && G_VALUE_HOLDS_STRING(value)) { album = g_value_get_string(value); } value = (GValue *) g_hash_table_lookup(table, "genre"); if (value != NULL && G_VALUE_HOLDS_STRING(value)) { genre = g_value_get_string(value); } value = (GValue *) g_hash_table_lookup(table, "title"); if (value != NULL && G_VALUE_HOLDS_STRING(value)) { title = g_value_get_string(value); } format = g_strdup(purple_prefs_get_string(OPT_FORMAT_STRING)); /* substitute in values */ if (artist && strstr(format, "%artist")) { rb_info = purple_strreplace(format, "%artist", artist); g_free(format); format = rb_info; } if (album && strstr(format, "%album")) { rb_info = purple_strreplace(format, "%album", album); g_free(format); format = rb_info; } if (genre && strstr(format, "%genre")) { rb_info = purple_strreplace(format, "%genre", genre); g_free(format); format = rb_info; } if (title && strstr(format, "%title")) { if (purple_prefs_get_bool(OPT_LYRICS_LINK)) { /* construct our google lyrics query */ gchar *tmp1, *tmp2; tmp1 = g_strdup_printf("lyrics \"%s\" \"%s\"", (artist ? artist : ""), title); tmp2 = g_strdup_printf("%s", purple_url_encode(tmp1), title); rb_info = purple_strreplace(format, "%title", tmp2); g_free(format); g_free(tmp1); g_free(tmp2); format = rb_info; } else { rb_info = purple_strreplace(format, "%title", title); g_free(format); format = rb_info; } } g_return_if_fail(rb_info != NULL); rb_process(rb_info); g_hash_table_destroy(table); g_free(rb_info); } static gchar * rb_get_playing_uri(void) { DBusConnection *conn; DBusMessage *msg, *reply; DBusMessageIter iter; gchar *uri; msg = dbus_message_new_method_call(DBUS_RB_NAMESPACE, DBUS_RB_PLAYER_PATH, DBUS_RB_PLAYER, "getPlayingUri"); if (!msg) { return NULL; } dbus_message_set_auto_start(msg, FALSE); conn = (DBusConnection *) dbus_g_connection_get_connection(pidginrb->bus); reply = dbus_connection_send_with_reply_and_block(conn, msg, 5000, NULL); dbus_message_unref(msg); if (!reply) { return NULL; } if (dbus_message_get_type(reply) != DBUS_MESSAGE_TYPE_METHOD_RETURN) { dbus_message_unref(reply); return NULL; } dbus_message_iter_init(reply, &iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) { dbus_message_unref(reply); return NULL; } dbus_message_iter_get_basic(&iter, &uri); dbus_message_unref(reply); return uri; } static void playing_signal_cb(DBusGProxy *player_proxy, gboolean playing, gpointer data) { gchar *uri; if (playing) { uri = rb_get_playing_uri(); if (uri && strlen(uri) != 0) { uri_signal_cb(NULL, uri, NULL); } else { rb_process(""); } } else { rb_process(""); } } static void signed_on_cb(PurpleConnection *gc, void *data) { playing_signal_cb(NULL, FALSE, NULL); } static gboolean load_plugin(PurplePlugin *plugin) { pidginrb = g_new0(pidginrb_t, 1); pidginrb->plugin = plugin; /* initialize dbus connection */ pidginrb->bus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); if (!pidginrb->bus) { rb_error("failed to connect to the dbus daemon\n"); return FALSE; } /* connect rhythmbox's dbus signals */ pidginrb->player = dbus_g_proxy_new_for_name(pidginrb->bus, DBUS_RB_NAMESPACE, DBUS_RB_PLAYER_PATH, DBUS_RB_PLAYER); pidginrb->shell = dbus_g_proxy_new_for_name(pidginrb->bus, DBUS_RB_NAMESPACE, DBUS_RB_SHELL_PATH, DBUS_RB_SHELL); dbus_g_proxy_add_signal(pidginrb->player, DBUS_RB_URI_SIGNAL, G_TYPE_STRING, G_TYPE_INVALID); dbus_g_proxy_connect_signal(pidginrb->player, DBUS_RB_URI_SIGNAL, G_CALLBACK(uri_signal_cb), NULL, NULL); dbus_g_proxy_add_signal(pidginrb->player, DBUS_RB_PLAYING_SIGNAL, G_TYPE_BOOLEAN, G_TYPE_INVALID); dbus_g_proxy_connect_signal(pidginrb->player, DBUS_RB_PLAYING_SIGNAL, G_CALLBACK(playing_signal_cb), NULL, NULL); purple_signal_connect(purple_connections_get_handle(), "signed-on", plugin, PURPLE_CALLBACK(signed_on_cb), NULL); playing_signal_cb(NULL, FALSE, NULL); return TRUE; } static gboolean unload_plugin(PurplePlugin *plugin) { if (pidginrb->shell) { g_object_unref(pidginrb->shell); } if (pidginrb->player) { g_object_unref(pidginrb->player); } if (pidginrb->bus) { dbus_g_connection_unref(pidginrb->bus); } g_free(pidginrb); return TRUE; } static PurplePluginPrefFrame * get_plugin_pref_frame(PurplePlugin *plugin) { PurplePluginPref *pref; PurplePluginPrefFrame *frame = purple_plugin_pref_frame_new(); /* create gtk elements for the plugin preferences */ pref = purple_plugin_pref_new_with_label("Pidgin-Rhythmbox Configuration"); purple_plugin_pref_frame_add(frame, pref); pref = purple_plugin_pref_new_with_name_and_label(OPT_PROCESS_STATUS, "Expand " RB_TOKEN " to song info in the status message"); purple_plugin_pref_frame_add(frame, pref); pref = purple_plugin_pref_new_with_name_and_label(OPT_PROCESS_USERINFO, "Expand " RB_TOKEN " to song info in the user info"); purple_plugin_pref_frame_add(frame, pref); pref = purple_plugin_pref_new_with_name_and_label(OPT_LYRICS_LINK, "Enable song title hyperlink for lyrics"); purple_plugin_pref_frame_add(frame, pref); pref = purple_plugin_pref_new_with_name_and_label(OPT_FORMAT_STRING, "Song format string"); purple_plugin_pref_frame_add(frame, pref); return frame; } static PurplePluginUiInfo pref_info = { get_plugin_pref_frame }; static PurplePluginInfo info = { PURPLE_PLUGIN_MAGIC, PURPLE_MAJOR_VERSION, PURPLE_MINOR_VERSION, PURPLE_PLUGIN_STANDARD, /**< type */ PIDGIN_PLUGIN_TYPE, /**< ui_req */ 0, /**< flags */ NULL, /**< deps */ PURPLE_PRIORITY_DEFAULT, /**< priority */ PIDGINRB_PLUGIN_ID, /**< id */ "Pidgin-Rhythmbox", /**< name */ VERSION, /**< version */ /** summary */ "Automatically updates your Pidgin status info with the currently " "playing music in Rhythmbox.", /** desc */ "Automatically updates your Pidgin status info with the currently " "playing music in Rhythmbox.", "Jon Oberheide ", /**< author */ /**< homepage */ "http://jon.oberheide.org/projects/pidgin-rhythmbox/", load_plugin, /**< load */ unload_plugin, /**< unload */ NULL, /**< destroy */ NULL, /**< ui_info */ NULL, /**< extra_info */ &pref_info, /**< pref info */ NULL }; static void init_plugin(PurplePlugin *plugin) { g_type_init(); /* add plugin preferences */ purple_prefs_add_none(OPT_PIDGINRB); purple_prefs_add_bool(OPT_PROCESS_STATUS, TRUE); purple_prefs_add_bool(OPT_PROCESS_USERINFO, TRUE); purple_prefs_add_bool(OPT_LYRICS_LINK, TRUE); purple_prefs_add_string(OPT_FORMAT_STRING, "'%title' by %artist"); } PURPLE_INIT_PLUGIN(pidgin_rhythmbox, init_plugin, info)