upnp: Ensure services_discovery_sys_t destructor will be called
[vlc.git] / src / playlist / services_discovery.c
1 /*****************************************************************************
2  * services_discovery.c : Manage playlist services_discovery modules
3  *****************************************************************************
4  * Copyright (C) 1999-2004 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: ClĂ©ment Stenac <zorglub@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26 #include <assert.h>
27
28 #include <vlc_common.h>
29 #include <vlc_playlist.h>
30 #include <vlc_services_discovery.h>
31 #include "playlist_internal.h"
32
33 typedef struct vlc_sd_internal_t
34 {
35     /* the playlist items for category and onelevel */
36     playlist_item_t      *node;
37     services_discovery_t *sd; /**< Loaded service discovery modules */
38     struct vlc_list       siblings;
39     char name[];
40 } vlc_sd_internal_t;
41
42  /* A new item has been added to a certain sd */
43 static void playlist_sd_item_added(services_discovery_t *sd,
44                                    input_item_t *parent, input_item_t *p_input,
45                                    const char *psz_cat)
46 {
47     assert(parent == NULL || psz_cat == NULL);
48
49     vlc_sd_internal_t *sds = sd->owner.sys;
50     playlist_t *playlist = (playlist_t *)sd->obj.parent;
51     playlist_item_t *node;
52     const char *longname = (sd->description != NULL) ? sd->description : "?";
53
54     msg_Dbg(sd, "adding: %s", p_input->psz_name ? p_input->psz_name : "(null)");
55
56     playlist_Lock(playlist);
57     if (sds->node == NULL)
58         sds->node = playlist_NodeCreate(playlist, longname, &playlist->root,
59                                         PLAYLIST_END, PLAYLIST_RO_FLAG);
60
61     if (parent != NULL)
62         node = playlist_ItemGetByInput(playlist, parent);
63     else
64     if (psz_cat == NULL)
65         node = sds->node;
66     else
67     {   /* Parent is NULL (root) and category is specified.
68          * This is clearly a hack. TODO: remove this. */
69         node = playlist_ChildSearchName(sds->node, psz_cat);
70         if (node == NULL)
71             node = playlist_NodeCreate(playlist, psz_cat, sds->node,
72                                        PLAYLIST_END, PLAYLIST_RO_FLAG);
73     }
74
75     playlist_NodeAddInput(playlist, p_input, node, PLAYLIST_END);
76     playlist_Unlock(playlist);
77 }
78
79  /* A new item has been removed from a certain sd */
80 static void playlist_sd_item_removed(services_discovery_t *sd,
81                                      input_item_t *p_input)
82 {
83     vlc_sd_internal_t *sds = sd->owner.sys;
84     playlist_t *playlist = (playlist_t *)sd->obj.parent;
85     playlist_item_t *node, *item;
86
87     msg_Dbg(sd, "removing: %s", p_input->psz_name ? p_input->psz_name : "(null)");
88
89     playlist_Lock(playlist);
90     item = playlist_ItemGetByInput(playlist, p_input);
91     if (unlikely(item == NULL))
92     {
93         msg_Err(sd, "removing item not added"); /* SD plugin bug */
94         playlist_Unlock(playlist);
95         return;
96     }
97
98 #ifndef NDEBUG
99     /* Check that the item belonged to the SD */
100     for (playlist_item_t *i = item->p_parent; i != sds->node; i = i->p_parent)
101         assert(i != NULL);
102 #endif
103
104     node = item->p_parent;
105     /* if the item was added under a category and the category node
106        becomes empty, delete that node as well */
107     if (node != sds->node && node->i_children == 1)
108         item = node;
109     playlist_NodeDeleteExplicit(playlist, item,
110         PLAYLIST_DELETE_FORCE | PLAYLIST_DELETE_STOP_IF_CURRENT );
111     playlist_Unlock(playlist);
112 }
113
114 static const struct services_discovery_callbacks playlist_sd_cbs = {
115     .item_added = playlist_sd_item_added,
116     .item_removed = playlist_sd_item_removed,
117 };
118
119 int playlist_ServicesDiscoveryAdd(playlist_t *playlist, const char *chain)
120 {
121     vlc_sd_internal_t *sds = malloc(sizeof (*sds) + strlen(chain) + 1);
122     if (unlikely(sds == NULL))
123         return VLC_ENOMEM;
124
125     sds->node = NULL;
126
127     struct services_discovery_owner_t owner = {
128         &playlist_sd_cbs,
129         sds,
130     };
131
132     /* Perform the addition */
133     sds->sd = vlc_sd_Create(VLC_OBJECT(playlist), chain, &owner);
134     if (unlikely(sds->sd == NULL))
135     {
136         free(sds);
137         return VLC_ENOMEM;
138     }
139
140     strcpy(sds->name, chain);
141
142     playlist_Lock(playlist);
143     /* Backward compatibility with Qt UI: create the node even if the SD
144      * has not discovered any item. */
145     if (sds->node == NULL && sds->sd->description != NULL)
146         sds->node = playlist_NodeCreate(playlist, sds->sd->description,
147                                         &playlist->root, PLAYLIST_END,
148                                         PLAYLIST_RO_FLAG);
149
150     vlc_list_append(&sds->siblings, &pl_priv(playlist)->sds);
151     playlist_Unlock(playlist);
152     return VLC_SUCCESS;
153 }
154
155 static void playlist_ServicesDiscoveryInternalRemove(playlist_t *playlist,
156                                                      vlc_sd_internal_t *sds)
157 {
158     assert(sds->sd != NULL);
159     vlc_sd_Destroy(sds->sd);
160
161     /* Remove the sd playlist node if it exists */
162     playlist_Lock(playlist);
163     if (sds->node != NULL)
164         playlist_NodeDeleteExplicit(playlist, sds->node,
165             PLAYLIST_DELETE_FORCE | PLAYLIST_DELETE_STOP_IF_CURRENT );
166     playlist_Unlock(playlist);
167
168     free(sds);
169 }
170
171
172 int playlist_ServicesDiscoveryRemove(playlist_t *playlist, const char *name)
173 {
174     playlist_private_t *priv = pl_priv(playlist);
175     vlc_sd_internal_t *sds = NULL, *entry;
176
177     playlist_Lock(playlist);
178     vlc_list_foreach(entry, &priv->sds, siblings)
179         if (!strcmp(name, entry->name))
180         {
181             sds = entry;
182             vlc_list_remove(&sds->siblings);
183             break;
184         }
185     playlist_Unlock(playlist);
186
187     if (sds == NULL)
188     {
189         msg_Warn(playlist, "discovery %s is not loaded", name);
190         return VLC_EGENERIC;
191     }
192
193     playlist_ServicesDiscoveryInternalRemove(playlist, sds);
194     return VLC_SUCCESS;
195 }
196
197 bool playlist_IsServicesDiscoveryLoaded( playlist_t * playlist,
198                                          const char *psz_name )
199 {
200     playlist_private_t *priv = pl_priv( playlist );
201     vlc_sd_internal_t *sds;
202     bool found = false;
203
204     playlist_Lock(playlist);
205     vlc_list_foreach(sds, &priv->sds, siblings)
206         if (!strcmp(psz_name, sds->name))
207         {
208             found = true;
209             break;
210         }
211     playlist_Unlock(playlist);
212     return found;
213 }
214
215 int playlist_ServicesDiscoveryControl( playlist_t *playlist, const char *psz_name, int i_control, ... )
216 {
217     playlist_private_t *priv = pl_priv( playlist );
218     vlc_sd_internal_t *sds;
219     int i_ret = VLC_EGENERIC;
220
221     playlist_Lock(playlist);
222     vlc_list_foreach(sds, &priv->sds, siblings)
223         if (!strcmp(psz_name, sds->name))
224         {
225             va_list args;
226             va_start( args, i_control );
227             i_ret = vlc_sd_control(sds->sd, i_control, args );
228             va_end( args );
229             break;
230         }
231     playlist_Unlock(playlist);
232
233     return i_ret;
234 }
235
236 void playlist_ServicesDiscoveryKillAll(playlist_t *playlist)
237 {
238     playlist_private_t *priv = pl_priv(playlist);
239     vlc_sd_internal_t *sds;
240
241     vlc_list_foreach(sds, &priv->sds, siblings)
242         playlist_ServicesDiscoveryInternalRemove(playlist, sds);
243 }