Ticket #506: rhythm.c

File rhythm.c, 7.8 kB (added by salinasv, 6 months ago)

Rhythm plugin

Line 
1/*
2 * Rhythm Plugin
3 *
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18 * 02111-1307, USA.
19 */
20
21#include <glib.h>
22
23#include <dbus/dbus.h>
24#include <dbus/dbus-glib-lowlevel.h>
25
26#include "account.h"
27#include "connection.h"
28#include "debug.h"
29#include "plugin.h"
30#include "status.h"
31#include "version.h"
32
33/* dbus info */
34#define DBUS_RB_NAMESPACE   "org.gnome.Rhythmbox"
35#define DBUS_RB_SHELL       DBUS_RB_NAMESPACE ".Shell"
36#define DBUS_RB_PLAYER      DBUS_RB_NAMESPACE ".Player"
37#define DBUS_RB_SHELL_PATH  "/org/gnome/Rhythmbox/Shell"
38#define DBUS_RB_PLAYER_PATH "/org/gnome/Rhythmbox/Player"
39#define DBUS_RB_URI_SIGNAL  "playingUriChanged"
40#define DBUS_RB_PLAYING_SIGNAL  "playingChanged"
41#define DBUS_RB_TIMEOUT     5000
42
43#define GET_PLAYING_STR     "getPlaying"
44#define GET_URI_STR         "getPlayingUri"
45
46#define GET_PLAYING 1
47#define GET_URI     2
48
49
50typedef struct {
51    PurplePlugin *plugin;
52    DBusGConnection *bus;
53    DBusGProxy *shell;
54    DBusGProxy *player;
55} Rhythm;
56
57typedef struct {
58    const gchar *artist;
59    const gchar *title;
60    const gchar *album;
61} RhythmSong;
62
63
64Rhythm *data;
65
66/* Dbus helpers */
67static gchar* get_dbus_iter(int call, gboolean *playing)
68{
69    DBusConnection *conn;
70    DBusMessage *msg, *reply;
71    DBusMessageIter iter;
72    gchar *uri = NULL;
73    gchar *call_str;
74
75    if (call == GET_PLAYING)
76        msg = dbus_message_new_method_call(DBUS_RB_NAMESPACE,
77                DBUS_RB_PLAYER_PATH,
78                DBUS_RB_PLAYER, GET_PLAYING_STR);
79    else
80        msg = dbus_message_new_method_call(DBUS_RB_NAMESPACE,
81                DBUS_RB_PLAYER_PATH,
82                DBUS_RB_PLAYER, GET_URI_STR);
83   
84    if (!msg) {
85        *playing = FALSE;
86        return NULL;
87    }
88
89    dbus_message_set_auto_start(msg, FALSE);
90    conn = (DBusConnection *) dbus_g_connection_get_connection(data->bus);
91
92    reply = dbus_connection_send_with_reply_and_block(conn, msg, DBUS_RB_TIMEOUT, NULL);
93    dbus_message_unref(msg);
94   
95    if (!reply) {
96        *playing = FALSE;
97        return NULL;
98    }
99
100    if (dbus_message_get_type(reply) != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
101        dbus_message_unref(reply);
102        *playing = FALSE;
103        return NULL;
104    }
105
106    dbus_message_iter_init(reply, &iter);
107    dbus_message_unref(reply);
108
109    if (call == GET_PLAYING) {
110        if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) {
111            *playing = FALSE;
112            return NULL;
113        }
114
115        dbus_message_iter_get_basic(&iter, &playing);
116    } else {
117        if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
118            return NULL;
119
120        dbus_message_iter_get_basic(&iter, &uri);
121    }
122   
123    return uri;
124}
125
126static gchar* get_playing_uri()
127{
128    gchar *uri;
129    gboolean nothing;
130
131    uri = get_dbus_iter(GET_URI, &nothing);
132
133    return uri;
134}
135
136static gboolean is_playing()
137{
138    gboolean playing;
139    gchar *dummy;
140
141    dummy = get_dbus_iter(GET_PLAYING, &playing);
142
143    return playing;
144}
145
146/* Purple update */
147static void rhythm_purple_update(RhythmSong *song)
148{
149    GList *all;
150    PurpleAccount *account;
151    PurplePresence *presence;
152    PurpleStatus *status;
153    PurpleStatus *active;
154
155    for ( all = purple_accounts_get_all(); all != NULL; all = all->next) {
156        account = (PurpleAccount*) all->data;
157
158        if (!purple_account_is_connected(account))
159            continue;
160
161        presence = purple_account_get_presence(account);
162        status = purple_presence_get_status(presence, "tune");
163
164        if (!status)
165            continue;
166
167        active = purple_presence_get_active_status(presence);
168
169        if (!song->title && !song->artist && !song->album
170                && (strcmp(purple_account_get_protocol_name(account),"XMPP")))
171            purple_status_set_active(status, FALSE);
172        else
173            purple_status_set_active(status, TRUE);
174        purple_status_set_attr_string(status, PURPLE_TUNE_ARTIST, song->artist);
175        purple_status_set_attr_string(status, PURPLE_TUNE_TITLE, song->title);
176        purple_status_set_attr_string(status, PURPLE_TUNE_ALBUM, song->album);
177
178        purple_prpl_change_account_status(account, active, status);
179    }
180    g_free(song);
181}
182
183/* processing the uri */
184static void uri_signal_cb(DBusGProxy *player_proxy, const gchar *uri, gpointer user_data)
185{
186    GValue *value;
187    GHashTable *table = NULL;
188    RhythmSong *song;
189    song = g_new0(RhythmSong, 1);
190
191    if (!dbus_g_proxy_call(data->shell, "getSongProperties", NULL,
192                G_TYPE_STRING, uri, G_TYPE_INVALID,
193                dbus_g_type_get_map("GHashTable",
194                G_TYPE_STRING, G_TYPE_VALUE),
195                &table, G_TYPE_INVALID)) {
196        return;
197    }
198
199    /* fetch values from hash table */
200    value = (GValue *) g_hash_table_lookup(table, "artist");
201
202    if (value != NULL && G_VALUE_HOLDS_STRING(value))
203        song->artist = g_value_get_string(value);
204   
205    value = (GValue *) g_hash_table_lookup(table, "album");
206
207    if (value != NULL && G_VALUE_HOLDS_STRING(value))
208        song->album = g_value_get_string(value);
209   
210    value = (GValue *) g_hash_table_lookup(table, "title");
211
212    if (value != NULL && G_VALUE_HOLDS_STRING(value))
213        song->title = g_value_get_string(value);
214   
215    purple_debug_info("rhythm", "song=%s, album=%s, artist=%s",
216            song->title, song->album, song->artist);
217
218    rhythm_purple_update(song);
219}
220
221/* No dbus methods */
222static void playing_signal_cb(DBusGProxy *player_proxy, gboolean playing,
223        gpointer data)
224{
225    gchar *uri;
226
227    if (!playing) {
228        RhythmSong *song;
229        song = g_new0(RhythmSong,1);
230        rhythm_purple_update(song);
231        return;
232    }
233
234    uri = get_playing_uri();
235
236    if (!uri)
237        return;
238
239    uri_signal_cb(NULL, uri, NULL);
240   
241}
242
243static void signed_on_cb(PurpleConnection *gc, void *data)
244{
245    playing_signal_cb(NULL, is_playing(), NULL);
246}
247
248static gboolean plugin_load(PurplePlugin *plugin)
249{
250    data = g_new0(Rhythm,1);
251    data->plugin = plugin;
252
253    /* Initialize dbus connection */
254    data->bus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
255
256    if (!data->bus) {
257        purple_debug_error("Rhythm", "Failed to connet to the dbus daemon\n");
258        return FALSE;
259    }
260
261    data->player = dbus_g_proxy_new_for_name(data->bus, DBUS_RB_NAMESPACE,
262            DBUS_RB_PLAYER_PATH, DBUS_RB_PLAYER);
263    data->shell = dbus_g_proxy_new_for_name(data->bus, DBUS_RB_NAMESPACE,
264            DBUS_RB_SHELL_PATH, DBUS_RB_SHELL);
265
266    dbus_g_proxy_add_signal(data->player, DBUS_RB_URI_SIGNAL,
267            G_TYPE_STRING, G_TYPE_INVALID);
268    dbus_g_proxy_connect_signal(data->player, DBUS_RB_URI_SIGNAL,
269            G_CALLBACK(uri_signal_cb), NULL, NULL);
270
271    dbus_g_proxy_add_signal(data->player, DBUS_RB_PLAYING_SIGNAL,
272            G_TYPE_BOOLEAN, G_TYPE_INVALID);
273    dbus_g_proxy_connect_signal(data->player, DBUS_RB_PLAYING_SIGNAL,
274            G_CALLBACK(playing_signal_cb), NULL, NULL);
275
276    purple_signal_connect(purple_connections_get_handle(), "signed-on",
277            plugin, PURPLE_CALLBACK(signed_on_cb), NULL);
278
279    playing_signal_cb(NULL, is_playing(), NULL);
280
281    return TRUE;
282}
283
284static gboolean plugin_unload(PurplePlugin *plugin)
285{
286    if (data->shell)
287        g_object_unref(data->shell);
288   
289    if (data->player)
290        g_object_unref(data->player);
291   
292    if (data->bus)
293        dbus_g_connection_unref(data->bus);
294
295    g_free(data);
296
297    return TRUE;
298}
299
300static void init_plugin(PurplePlugin *plugin)
301{
302}
303
304static PurplePluginInfo info = {
305    PURPLE_PLUGIN_MAGIC,
306    PURPLE_MAJOR_VERSION,
307    PURPLE_MINOR_VERSION,
308    PURPLE_PLUGIN_STANDARD,
309    NULL,
310    0,
311    NULL,
312    PURPLE_PRIORITY_DEFAULT,
313    "core-salinasv-rhythm",
314    "Rhythm",
315    "0.1",
316    "Get status from rhythmbox",
317    "Set the tune status on each account getting it from dbus - rhythmbox based on the original Pidgin-Rhythmbox plugin",
318    "Jorge Villaseñor (Masca), salinasv@gmail.com",
319    NULL,
320    plugin_load,
321    plugin_unload,
322    NULL,                   /** < destroy */
323    NULL,                   /** < ui_info */
324    NULL,                   /** < extra_info */
325    NULL,                   /** < pref_ui */
326    NULL,                   /** < plugin_actions */
327    /* Padding */
328    NULL,
329    NULL,
330    NULL,
331    NULL
332};
333
334PURPLE_INIT_PLUGIN(pidgin_rhythmbox, init_plugin, info)