Renamed playlist "playlist-current" to "item-current".
[vlc.git] / modules / misc / notify / telepathy.c
1 /*****************************************************************************
2  * telepathy.c : changes Telepathy Presence information using MissionControl
3  *****************************************************************************
4  * Copyright © 2007 the VideoLAN team
5  * $Id$
6  *
7  * Author: Rafaël Carré <funman@videoanorg>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_interface.h>
35 #include <vlc_meta.h>
36 #include <vlc_playlist.h>
37 #include <vlc_strings.h>
38 #include <dbus/dbus.h>
39
40 /*****************************************************************************
41  * intf_sys_t: description and status of log interface
42  *****************************************************************************/
43 struct intf_sys_t
44 {
45     char            *psz_format;
46     DBusConnection  *p_conn;
47     int             i_id;
48     int             i_item_changes;
49 };
50
51 /*****************************************************************************
52  * Local prototypes
53  *****************************************************************************/
54 static int  Open    ( vlc_object_t * );
55 static void Close   ( vlc_object_t * );
56
57 static int ItemChange( vlc_object_t *, const char *,
58                        vlc_value_t, vlc_value_t, void * );
59 static int StateChange( vlc_object_t *, const char *,
60                         vlc_value_t, vlc_value_t, void * );
61 static int SendToTelepathy( intf_thread_t *, const char * );
62
63 /*****************************************************************************
64  * Module descriptor
65  *****************************************************************************/
66 #define FORMAT_DEFAULT "$a - $t"
67 #define FORMAT_TEXT N_("Title format string")
68 #define FORMAT_LONGTEXT N_("Format of the string to send to Telepathy." \
69 "Defaults to \"Artist - Title\" ($a - $t). " \
70 "You can use the following substitutions: " \
71 "$a Artist, $b Album, $c Copyright, $d Description, $e Encoder, $g Genre, " \
72 "$l Language, $n number, $p Now Playing, $r Rating, $s Subtitles language, " \
73 "$t Title, $u URL, $A Date, $B Bitrate, $C Chapter, $D Duration, $F URI, " \
74 "$I Video Title, $L Time Remaining, $N Name, $O Audio language, $P Position, " \
75 "$R Rate, $S Sample rate, $T Time elapsed, $U Publisher, $V Volume")
76
77 vlc_module_begin ()
78     set_category( CAT_INTERFACE )
79     set_subcategory( SUBCAT_INTERFACE_CONTROL )
80     set_shortname( "Telepathy" )
81     set_description( N_("Telepathy \"Now Playing\" (MissionControl)") )
82
83     add_string( "telepathy-format", FORMAT_DEFAULT, NULL,
84                 FORMAT_TEXT, FORMAT_LONGTEXT, false )
85
86     set_capability( "interface", 0 )
87     set_callbacks( Open, Close )
88 vlc_module_end ()
89
90 /*****************************************************************************
91  * Open: initialize and create stuff
92  *****************************************************************************/
93 static int Open( vlc_object_t *p_this )
94 {
95     intf_thread_t *p_intf = (intf_thread_t *)p_this;
96     playlist_t *p_playlist;
97     DBusConnection  *p_conn;
98     DBusError       error;
99
100     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
101     if( !p_intf->p_sys )
102         return VLC_ENOMEM;
103
104     /* connect to the session bus */
105     dbus_error_init( &error );
106     p_conn = dbus_bus_get( DBUS_BUS_SESSION, &error );
107     if( !p_conn )
108     {
109         msg_Err( p_this, "Failed to connect to the DBus session daemon: %s",
110                 error.message );
111         dbus_error_free( &error );
112         free( p_intf->p_sys );
113         return VLC_EGENERIC;
114     }
115     p_intf->p_sys->p_conn = p_conn;
116
117     p_intf->p_sys->psz_format = config_GetPsz( p_intf, "telepathy-format" );
118     if( !p_intf->p_sys->psz_format )
119     {
120         msg_Dbg( p_intf, "no format provided" );
121         p_intf->p_sys->psz_format = strdup( FORMAT_DEFAULT );
122     }
123     msg_Dbg( p_intf, "using format: %s", p_intf->p_sys->psz_format );
124
125     p_intf->p_sys->i_id = -1;
126
127     p_playlist = pl_Hold( p_intf );
128     var_AddCallback( p_playlist, "item-change", ItemChange, p_intf );
129     var_AddCallback( p_playlist, "item-current", ItemChange, p_intf );
130     pl_Release( p_intf );
131
132     return VLC_SUCCESS;
133 }
134
135 /*****************************************************************************
136  * Close: destroy interface stuff
137  *****************************************************************************/
138 static void Close( vlc_object_t *p_this )
139 {
140     intf_thread_t *p_intf = (intf_thread_t *)p_this;
141     playlist_t *p_playlist = pl_Hold( p_this );
142     input_thread_t *p_input = NULL;
143
144     var_DelCallback( p_playlist, "item-change", ItemChange, p_intf );
145     var_DelCallback( p_playlist, "item-current", ItemChange, p_intf );
146     if( (p_input = playlist_CurrentInput( p_playlist )) )
147     {
148         var_DelCallback( p_input, "state", StateChange, p_intf );
149         vlc_object_release( p_input );
150     }
151     pl_Release( p_this );
152
153     /* Clears the Presence message ... else it looks like we're still playing
154      * something although VLC (or the Telepathy plugin) is closed */
155
156     /* Do not check for VLC_ENOMEM as we're closing */
157     SendToTelepathy( p_intf, "" );
158
159     /* we won't use the DBus connection anymore */
160     dbus_connection_unref( p_intf->p_sys->p_conn );
161
162     /* Destroy structure */
163     free( p_intf->p_sys->psz_format );
164     free( p_intf->p_sys );
165 }
166
167 /*****************************************************************************
168  * ItemChange: Playlist item change callback
169  *****************************************************************************/
170 static int ItemChange( vlc_object_t *p_this, const char *psz_var,
171                        vlc_value_t oldval, vlc_value_t newval, void *param )
172 {
173     VLC_UNUSED(oldval);
174     intf_thread_t *p_intf = (intf_thread_t *)param;
175     playlist_t* p_playlist = (playlist_t*) p_this;
176     char *psz_buf = NULL;
177     input_thread_t *p_input;
178
179     /* Don't update Telepathy presence each time an item has been preparsed */
180     if( !strncmp( "item-current", psz_var, 16 ) )
181     { /* stores the current input item id */
182         p_intf->p_sys->i_id = newval.i_int;
183         p_intf->p_sys->i_item_changes = 0;
184     }
185     else
186     {
187         if( newval.i_int != p_intf->p_sys->i_id ) /* "item-change" */
188             return VLC_SUCCESS;
189         /* Some variable bitrate inputs call "item-change callbacks each time
190          * their length is updated, that is several times per second.
191          * We'll limit the number of changes to 10 per input. */
192         if( p_intf->p_sys->i_item_changes > 10 )
193             return VLC_SUCCESS;
194         p_intf->p_sys->i_item_changes++;
195     }
196
197     p_input = playlist_CurrentInput( p_playlist );
198
199     if( !p_input ) return VLC_SUCCESS;
200
201     if( p_input->b_dead || !input_GetItem(p_input)->psz_name )
202     {
203         vlc_object_release( p_input );
204         /* Not playing anything ... */
205         switch( SendToTelepathy( p_intf, "" ) )
206         {
207             case VLC_ENOMEM:
208                 return VLC_ENOMEM;
209             default:
210                 return VLC_SUCCESS;
211         }
212     }
213
214     if( !strncmp( "item-current", psz_var, 16 ) )
215         var_AddCallback( p_input, "state", StateChange, p_intf );
216
217     /* We format the string to be displayed */
218     psz_buf = str_format_meta( (vlc_object_t*) p_intf,
219             p_intf->p_sys->psz_format );
220
221     /* We don't need the input anymore */
222     vlc_object_release( p_input );
223
224     if( SendToTelepathy( p_intf, psz_buf ) == VLC_ENOMEM )
225     {
226         free( psz_buf );
227         return VLC_ENOMEM;
228     }
229     free( psz_buf );
230     return VLC_SUCCESS;
231 }
232
233 /*****************************************************************************
234  * StateChange: State change callback
235  *****************************************************************************/
236 static int StateChange( vlc_object_t *p_this, const char *psz_var,
237                        vlc_value_t oldval, vlc_value_t newval, void *param )
238 {
239     VLC_UNUSED(p_this); VLC_UNUSED(psz_var); VLC_UNUSED(oldval);
240     intf_thread_t *p_intf = (intf_thread_t *)param;
241     if( newval.i_int >= END_S )
242         return SendToTelepathy( p_intf, "" );
243     return VLC_SUCCESS;
244 }
245
246 /*****************************************************************************
247  * SendToTelepathy
248  *****************************************************************************/
249 static int SendToTelepathy( intf_thread_t *p_intf, const char *psz_msg )
250 {
251     DBusConnection *p_conn;
252     DBusMessage *p_msg;
253     DBusMessage *p_reply;
254     DBusMessageIter args;
255     DBusError error;
256     dbus_error_init( &error );
257     dbus_uint32_t i_status;
258
259     p_conn = p_intf->p_sys->p_conn;
260
261     /* first we need to get the actual status */
262     p_msg = dbus_message_new_method_call(
263             "org.freedesktop.Telepathy.MissionControl",
264            "/org/freedesktop/Telepathy/MissionControl",
265             "org.freedesktop.Telepathy.MissionControl",
266             "GetPresence" );
267     if( !p_msg )
268         return VLC_ENOMEM;
269
270     p_reply = dbus_connection_send_with_reply_and_block( p_conn, p_msg,
271         50, &error ); /* blocks 50ms maximum */
272
273     dbus_message_unref( p_msg );
274     if( p_reply == NULL )
275     {   /* MC is not active, or too slow. Better luck next time? */
276         return VLC_SUCCESS;
277     }
278
279     /* extract the status from the reply */
280     if( dbus_message_get_args( p_reply, &error,
281             DBUS_TYPE_UINT32, &i_status,
282             DBUS_TYPE_INVALID ) == FALSE )
283     {
284         return VLC_ENOMEM;
285     }
286
287     p_msg = dbus_message_new_method_call(
288             "org.freedesktop.Telepathy.MissionControl",
289            "/org/freedesktop/Telepathy/MissionControl",
290             "org.freedesktop.Telepathy.MissionControl",
291             "SetPresence" );
292     if( !p_msg )
293         return VLC_ENOMEM;
294
295     dbus_message_iter_init_append( p_msg, &args );
296
297     /* first argument is the status */
298     if( !dbus_message_iter_append_basic( &args, DBUS_TYPE_UINT32, &i_status ) )
299     {
300         dbus_message_unref( p_msg );
301         return VLC_ENOMEM;
302     }
303     /* second argument is the message */
304     if( !dbus_message_iter_append_basic( &args, DBUS_TYPE_STRING, &psz_msg ) )
305     {
306         dbus_message_unref( p_msg );
307         return VLC_ENOMEM;
308     }
309
310
311     if( !dbus_connection_send( p_conn, p_msg, NULL ) )
312         return VLC_ENOMEM;
313
314     dbus_connection_flush( p_conn );
315     dbus_message_unref( p_msg );
316
317     return VLC_SUCCESS;
318 }