UPnP discovery: add support for SAT>IP servers (closes #11929, closes #15540)
[vlc.git] / modules / notify / growl.m
1 /*****************************************************************************
2  * growl.m : growl notification plugin
3  *****************************************************************************
4  * VLC specific code:
5  *
6  * Copyright © 2008,2011,2012 the VideoLAN team
7  * $Id$
8  *
9  * Authors: Rafaël Carré <funman@videolanorg>
10  *          Felix Paul Kühne <fkuehne@videolan.org
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *
26  * Growl specific code, ripped from growlnotify:
27  *
28  * Copyright (c) The Growl Project, 2004-2005
29  * All rights reserved.
30  *
31  * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
32  *
33  * 1. Redistributions of source code must retain the above copyright
34  notice, this list of conditions and the following disclaimer.
35  * 2. Redistributions in binary form must reproduce the above copyright
36  notice, this list of conditions and the following disclaimer in the
37  documentation and/or other materials provided with the distribution.
38  * 3. Neither the name of Growl nor the names of its contributors
39  may be used to endorse or promote products derived from this software
40  without specific prior written permission.
41  *
42  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43  *
44  *****************************************************************************/
45
46 /*****************************************************************************
47  * Preamble
48  *****************************************************************************/
49
50 #ifdef HAVE_CONFIG_H
51 # include "config.h"
52 #endif
53
54 #import <Foundation/Foundation.h>
55 #import <Growl/Growl.h>
56
57 #define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
58 #include <vlc_common.h>
59 #include <vlc_plugin.h>
60 #include <vlc_playlist.h>
61 #include <vlc_input.h>
62 #include <vlc_meta.h>
63 #include <vlc_interface.h>
64 #include <vlc_url.h>
65
66
67 /*****************************************************************************
68  * intf_sys_t, VLCGrowlDelegate
69  *****************************************************************************/
70 @interface VLCGrowlDelegate : NSObject <GrowlApplicationBridgeDelegate>
71 {
72     NSString *o_applicationName;
73     NSString *o_notificationType;
74     NSMutableDictionary *o_registrationDictionary;
75 }
76
77 - (void)registerToGrowl;
78 - (void)notifyWithDescription: (const char *)psz_desc
79                        artUrl: (const char *)psz_arturl;
80 @end
81
82 struct intf_sys_t
83 {
84     VLCGrowlDelegate *o_growl_delegate;
85     int             i_id;
86     int             i_item_changes;
87 };
88
89 /*****************************************************************************
90  * Local prototypes
91  *****************************************************************************/
92 static int  Open    ( vlc_object_t * );
93 static void Close   ( vlc_object_t * );
94
95 static int ItemChange( vlc_object_t *, const char *,
96                       vlc_value_t, vlc_value_t, void * );
97
98 /*****************************************************************************
99  * Module descriptor
100  ****************************************************************************/
101
102 vlc_module_begin ()
103 set_category( CAT_INTERFACE )
104 set_subcategory( SUBCAT_INTERFACE_CONTROL )
105 set_shortname( "Growl" )
106 set_description( N_("Growl Notification Plugin") )
107 set_capability( "interface", 0 )
108 set_callbacks( Open, Close )
109 vlc_module_end ()
110
111 /*****************************************************************************
112  * Open: initialize and create stuff
113  *****************************************************************************/
114 static int Open( vlc_object_t *p_this )
115 {
116     intf_thread_t *p_intf = (intf_thread_t *)p_this;
117     playlist_t *p_playlist;
118     intf_sys_t    *p_sys;
119
120     p_sys = p_intf->p_sys = calloc( 1, sizeof(intf_sys_t) );
121     if( !p_sys )
122         return VLC_ENOMEM;
123
124     p_sys->o_growl_delegate = [[VLCGrowlDelegate alloc] init];
125     if( !p_sys->o_growl_delegate )
126       return VLC_ENOMEM;
127
128     p_playlist = pl_Get( p_intf );
129     var_AddCallback( p_playlist, "item-change", ItemChange, p_intf );
130     var_AddCallback( p_playlist, "input-current", ItemChange, p_intf );
131
132     [p_sys->o_growl_delegate registerToGrowl];
133     return VLC_SUCCESS;
134 }
135
136 /*****************************************************************************
137  * Close: destroy interface stuff
138  *****************************************************************************/
139 static void Close( vlc_object_t *p_this )
140 {
141     intf_thread_t *p_intf = (intf_thread_t *)p_this;
142     playlist_t *p_playlist = pl_Get( p_intf );
143     intf_sys_t *p_sys = p_intf->p_sys;
144
145     var_DelCallback( p_playlist, "item-change", ItemChange, p_intf );
146     var_DelCallback( p_playlist, "input-current", ItemChange, p_intf );
147
148     [p_sys->o_growl_delegate release];
149     free( p_sys );
150 }
151
152 /*****************************************************************************
153  * ItemChange: Playlist item change callback
154  *****************************************************************************/
155 static int ItemChange( vlc_object_t *p_this, const char *psz_var,
156                       vlc_value_t oldval, vlc_value_t newval, void *param )
157 {
158     VLC_UNUSED(oldval);
159
160     intf_thread_t *p_intf = (intf_thread_t *)param;
161     char *psz_tmp           = NULL;
162     char *psz_title         = NULL;
163     char *psz_artist        = NULL;
164     char *psz_album         = NULL;
165     input_item_t *p_item = newval.p_address;
166
167     bool b_is_item_current = !strcmp( "input-current", psz_var );
168
169     /* Don't update each time an item has been preparsed */
170     if( b_is_item_current )
171     { /* stores the current input item id */
172         input_thread_t *p_input = newval.p_address;
173         if( !p_input )
174             return VLC_SUCCESS;
175
176         p_item = input_GetItem( p_input );
177         if( p_intf->p_sys->i_id != p_item->i_id )
178         {
179             p_intf->p_sys->i_id = p_item->i_id;
180             p_intf->p_sys->i_item_changes = 0;
181         }
182
183         return VLC_SUCCESS;
184     }
185     /* ignore items which weren't pre-parsed yet */
186     else if( !input_item_IsPreparsed(p_item) )
187         return VLC_SUCCESS;
188     else
189     {   /* "item-change" */
190         
191         if( p_item->i_id != p_intf->p_sys->i_id )
192             return VLC_SUCCESS;
193
194         /* Some variable bitrate inputs call "item-change" callbacks each time
195          * their length is updated, that is several times per second.
196          * We'll limit the number of changes to 1 per input. */
197         if( p_intf->p_sys->i_item_changes > 0 )
198             return VLC_SUCCESS;
199
200         p_intf->p_sys->i_item_changes++;
201     }
202
203     /* Playing something ... */
204     if( input_item_GetNowPlayingFb( p_item ) )
205         psz_title = input_item_GetNowPlayingFb( p_item );
206     else
207         psz_title = input_item_GetTitleFbName( p_item );
208     if( EMPTY_STR( psz_title ) )
209     {
210         free( psz_title );
211         return VLC_SUCCESS;
212     }
213
214     psz_artist = input_item_GetArtist( p_item );
215     if( EMPTY_STR( psz_artist ) ) FREENULL( psz_artist );
216     psz_album = input_item_GetAlbum( p_item ) ;
217     if( EMPTY_STR( psz_album ) ) FREENULL( psz_album );
218
219     int i_ret;
220     if( psz_artist && psz_album )
221         i_ret = asprintf( &psz_tmp, "%s\n%s [%s]",
222                          psz_title, psz_artist, psz_album );
223     else if( psz_artist )
224         i_ret = asprintf( &psz_tmp, "%s\n%s", psz_title, psz_artist );
225     else
226         i_ret = asprintf(&psz_tmp, "%s", psz_title );
227
228     if( i_ret == -1 )
229     {
230         free( psz_title );
231         free( psz_artist );
232         free( psz_album );
233         return VLC_ENOMEM;
234     }
235
236     char *psz_arturl = input_item_GetArtURL( p_item );
237     if( psz_arturl )
238     {
239         char *psz = make_path( psz_arturl );
240         free( psz_arturl );
241         psz_arturl = psz;
242     }
243
244     [p_intf->p_sys->o_growl_delegate notifyWithDescription: psz_tmp artUrl: psz_arturl];
245
246     free( psz_title );
247     free( psz_artist );
248     free( psz_album );
249     free( psz_arturl );
250     free( psz_tmp );
251
252     return VLC_SUCCESS;
253 }
254
255 /*****************************************************************************
256  * VLCGrowlDelegate
257  *****************************************************************************/
258 @implementation VLCGrowlDelegate
259
260 - (id)init
261 {
262     if( !( self = [super init] ) )
263         return nil;
264
265     o_applicationName = nil;
266     o_notificationType = nil;
267     o_registrationDictionary = nil;
268
269     return self;
270 }
271
272 - (void)dealloc
273 {
274     [o_applicationName release];
275     [o_notificationType release];
276     [o_registrationDictionary release];
277     [super dealloc];
278 }
279
280 - (void)registerToGrowl
281 {
282     @autoreleasepool {
283         o_applicationName = [[NSString alloc] initWithUTF8String: _( "VLC media player" )];
284         o_notificationType = [[NSString alloc] initWithUTF8String: _( "New input playing" )];
285
286         NSArray *o_defaultAndAllNotifications = [NSArray arrayWithObject: o_notificationType];
287         o_registrationDictionary = [[NSMutableDictionary alloc] init];
288         [o_registrationDictionary setObject: o_defaultAndAllNotifications
289                                      forKey: GROWL_NOTIFICATIONS_ALL];
290         [o_registrationDictionary setObject: o_defaultAndAllNotifications
291                                      forKey: GROWL_NOTIFICATIONS_DEFAULT];
292
293         [GrowlApplicationBridge setGrowlDelegate: self];
294     }
295 }
296
297 - (void)notifyWithDescription: (const char *)psz_desc artUrl: (const char *)psz_arturl
298 {
299     @autoreleasepool {
300         NSData *o_art = nil;
301
302         if( psz_arturl )
303             o_art = [NSData dataWithContentsOfFile: [NSString stringWithUTF8String: psz_arturl]];
304
305         [GrowlApplicationBridge notifyWithTitle: [NSString stringWithUTF8String: _( "Now playing" )]
306                                     description: [NSString stringWithUTF8String: psz_desc]
307                                notificationName: o_notificationType
308                                        iconData: o_art
309                                        priority: 0
310                                        isSticky: NO
311                                    clickContext: nil];
312     }
313 }
314
315 /*****************************************************************************
316  * Delegate methods
317  *****************************************************************************/
318 - (NSDictionary *)registrationDictionaryForGrowl
319 {
320     return o_registrationDictionary;
321 }
322
323 - (NSString *)applicationNameForGrowl
324 {
325     return o_applicationName;
326 }
327 @end