~ubuntu-branches/ubuntu/trusty/mpd/trusty

« back to all changes in this revision

Viewing changes to src/playlist/xspf_playlist_plugin.c

  • Committer: Bazaar Package Importer
  • Author(s): Angel Abad
  • Date: 2011-02-02 12:26:30 UTC
  • mfrom: (1.5.11 upstream)
  • Revision ID: james.westby@ubuntu.com-20110202122630-bdyx8w4k94doz4fs
Tags: 0.16.1-1ubuntu1
* Merge from debian unstable. Remaining changes:
  - debian/control:
    + Don't build-depend on libmikmod2-dev (Debian bug #510675).
    + Move avahi-daemon from Suggests field to Recommends field.
  - debian/mpd.init.d:
    + Read mpd user from mpd.conf.
  - debian/control, debian/rules:
    + Add libmp3lame-dev to the build dependencies and enable lame.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2003-2010 The Music Player Daemon Project
 
3
 * http://www.musicpd.org
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation; either version 2 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License along
 
16
 * with this program; if not, write to the Free Software Foundation, Inc.,
 
17
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
18
 */
 
19
 
 
20
#include "config.h"
 
21
#include "playlist/xspf_playlist_plugin.h"
 
22
#include "playlist_plugin.h"
 
23
#include "input_stream.h"
 
24
#include "uri.h"
 
25
#include "song.h"
 
26
#include "tag.h"
 
27
 
 
28
#include <glib.h>
 
29
 
 
30
#include <assert.h>
 
31
#include <string.h>
 
32
 
 
33
#undef G_LOG_DOMAIN
 
34
#define G_LOG_DOMAIN "xspf"
 
35
 
 
36
/**
 
37
 * This is the state object for the GLib XML parser.
 
38
 */
 
39
struct xspf_parser {
 
40
        /**
 
41
         * The list of songs (in reverse order because that's faster
 
42
         * while adding).
 
43
         */
 
44
        GSList *songs;
 
45
 
 
46
        /**
 
47
         * The current position in the XML file.
 
48
         */
 
49
        enum {
 
50
                ROOT, PLAYLIST, TRACKLIST, TRACK,
 
51
                LOCATION,
 
52
        } state;
 
53
 
 
54
        /**
 
55
         * The current tag within the "track" element.  This is only
 
56
         * valid if state==TRACK.  TAG_NUM_OF_ITEM_TYPES means there
 
57
         * is no (known) tag.
 
58
         */
 
59
        enum tag_type tag;
 
60
 
 
61
        /**
 
62
         * The current song.  It is allocated after the "location"
 
63
         * element.
 
64
         */
 
65
        struct song *song;
 
66
};
 
67
 
 
68
static void
 
69
xspf_start_element(G_GNUC_UNUSED GMarkupParseContext *context,
 
70
                   const gchar *element_name,
 
71
                   G_GNUC_UNUSED const gchar **attribute_names,
 
72
                   G_GNUC_UNUSED const gchar **attribute_values,
 
73
                   gpointer user_data, G_GNUC_UNUSED GError **error)
 
74
{
 
75
        struct xspf_parser *parser = user_data;
 
76
 
 
77
        switch (parser->state) {
 
78
        case ROOT:
 
79
                if (strcmp(element_name, "playlist") == 0)
 
80
                        parser->state = PLAYLIST;
 
81
 
 
82
                break;
 
83
 
 
84
        case PLAYLIST:
 
85
                if (strcmp(element_name, "trackList") == 0)
 
86
                        parser->state = TRACKLIST;
 
87
 
 
88
                break;
 
89
 
 
90
        case TRACKLIST:
 
91
                if (strcmp(element_name, "track") == 0) {
 
92
                        parser->state = TRACK;
 
93
                        parser->song = NULL;
 
94
                        parser->tag = TAG_NUM_OF_ITEM_TYPES;
 
95
                }
 
96
 
 
97
                break;
 
98
 
 
99
        case TRACK:
 
100
                if (strcmp(element_name, "location") == 0)
 
101
                        parser->state = LOCATION;
 
102
                else if (strcmp(element_name, "title") == 0)
 
103
                        parser->tag = TAG_TITLE;
 
104
                else if (strcmp(element_name, "creator") == 0)
 
105
                        /* TAG_COMPOSER would be more correct
 
106
                           according to the XSPF spec */
 
107
                        parser->tag = TAG_ARTIST;
 
108
                else if (strcmp(element_name, "annotation") == 0)
 
109
                        parser->tag = TAG_COMMENT;
 
110
                else if (strcmp(element_name, "album") == 0)
 
111
                        parser->tag = TAG_ALBUM;
 
112
                else if (strcmp(element_name, "trackNum") == 0)
 
113
                        parser->tag = TAG_TRACK;
 
114
 
 
115
                break;
 
116
 
 
117
        case LOCATION:
 
118
                break;
 
119
        }
 
120
}
 
121
 
 
122
static void
 
123
xspf_end_element(G_GNUC_UNUSED GMarkupParseContext *context,
 
124
                 const gchar *element_name,
 
125
                 gpointer user_data, G_GNUC_UNUSED GError **error)
 
126
{
 
127
        struct xspf_parser *parser = user_data;
 
128
 
 
129
        switch (parser->state) {
 
130
        case ROOT:
 
131
                break;
 
132
 
 
133
        case PLAYLIST:
 
134
                if (strcmp(element_name, "playlist") == 0)
 
135
                        parser->state = ROOT;
 
136
 
 
137
                break;
 
138
 
 
139
        case TRACKLIST:
 
140
                if (strcmp(element_name, "tracklist") == 0)
 
141
                        parser->state = PLAYLIST;
 
142
 
 
143
                break;
 
144
 
 
145
        case TRACK:
 
146
                if (strcmp(element_name, "track") == 0) {
 
147
                        if (parser->song != NULL)
 
148
                                parser->songs = g_slist_prepend(parser->songs,
 
149
                                                                parser->song);
 
150
 
 
151
                        parser->state = TRACKLIST;
 
152
                } else
 
153
                        parser->tag = TAG_NUM_OF_ITEM_TYPES;
 
154
 
 
155
                break;
 
156
 
 
157
        case LOCATION:
 
158
                parser->state = TRACK;
 
159
                break;
 
160
        }
 
161
}
 
162
 
 
163
static void
 
164
xspf_text(G_GNUC_UNUSED GMarkupParseContext *context,
 
165
          const gchar *text, gsize text_len,
 
166
          gpointer user_data, G_GNUC_UNUSED GError **error)
 
167
{
 
168
        struct xspf_parser *parser = user_data;
 
169
 
 
170
        switch (parser->state) {
 
171
        case ROOT:
 
172
        case PLAYLIST:
 
173
        case TRACKLIST:
 
174
                break;
 
175
 
 
176
        case TRACK:
 
177
                if (parser->song != NULL &&
 
178
                    parser->tag != TAG_NUM_OF_ITEM_TYPES) {
 
179
                        if (parser->song->tag == NULL)
 
180
                                parser->song->tag = tag_new();
 
181
                        tag_add_item_n(parser->song->tag, parser->tag,
 
182
                                       text, text_len);
 
183
                }
 
184
 
 
185
                break;
 
186
 
 
187
        case LOCATION:
 
188
                if (parser->song == NULL) {
 
189
                        char *uri = g_strndup(text, text_len);
 
190
                        parser->song = song_remote_new(uri);
 
191
                        g_free(uri);
 
192
                }
 
193
 
 
194
                break;
 
195
        }
 
196
}
 
197
 
 
198
static const GMarkupParser xspf_parser = {
 
199
        .start_element = xspf_start_element,
 
200
        .end_element = xspf_end_element,
 
201
        .text = xspf_text,
 
202
};
 
203
 
 
204
static void
 
205
song_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
 
206
{
 
207
        struct song *song = data;
 
208
 
 
209
        song_free(song);
 
210
}
 
211
 
 
212
static void
 
213
xspf_parser_destroy(gpointer data)
 
214
{
 
215
        struct xspf_parser *parser = data;
 
216
 
 
217
        if (parser->state >= TRACK && parser->song != NULL)
 
218
                song_free(parser->song);
 
219
 
 
220
        g_slist_foreach(parser->songs, song_free_callback, NULL);
 
221
        g_slist_free(parser->songs);
 
222
}
 
223
 
 
224
/*
 
225
 * The playlist object
 
226
 *
 
227
 */
 
228
 
 
229
struct xspf_playlist {
 
230
        struct playlist_provider base;
 
231
 
 
232
        GSList *songs;
 
233
};
 
234
 
 
235
static struct playlist_provider *
 
236
xspf_open_stream(struct input_stream *is)
 
237
{
 
238
        struct xspf_parser parser = {
 
239
                .songs = NULL,
 
240
                .state = ROOT,
 
241
        };
 
242
        struct xspf_playlist *playlist;
 
243
        GMarkupParseContext *context;
 
244
        char buffer[1024];
 
245
        size_t nbytes;
 
246
        bool success;
 
247
        GError *error = NULL;
 
248
 
 
249
        /* parse the XSPF XML file */
 
250
 
 
251
        context = g_markup_parse_context_new(&xspf_parser,
 
252
                                             G_MARKUP_TREAT_CDATA_AS_TEXT,
 
253
                                             &parser, xspf_parser_destroy);
 
254
 
 
255
        while (true) {
 
256
                nbytes = input_stream_read(is, buffer, sizeof(buffer), &error);
 
257
                if (nbytes == 0) {
 
258
                        if (error != NULL) {
 
259
                                g_markup_parse_context_free(context);
 
260
                                g_warning("%s", error->message);
 
261
                                g_error_free(error);
 
262
                                return NULL;
 
263
                        }
 
264
 
 
265
                        break;
 
266
                }
 
267
 
 
268
                success = g_markup_parse_context_parse(context, buffer, nbytes,
 
269
                                                       &error);
 
270
                if (!success) {
 
271
                        g_warning("XML parser failed: %s", error->message);
 
272
                        g_error_free(error);
 
273
                        g_markup_parse_context_free(context);
 
274
                        return NULL;
 
275
                }
 
276
        }
 
277
 
 
278
        success = g_markup_parse_context_end_parse(context, &error);
 
279
        if (!success) {
 
280
                g_warning("XML parser failed: %s", error->message);
 
281
                g_error_free(error);
 
282
                g_markup_parse_context_free(context);
 
283
                return NULL;
 
284
        }
 
285
 
 
286
        /* create a #xspf_playlist object from the parsed song list */
 
287
 
 
288
        playlist = g_new(struct xspf_playlist, 1);
 
289
        playlist_provider_init(&playlist->base, &xspf_playlist_plugin);
 
290
        playlist->songs = g_slist_reverse(parser.songs);
 
291
        parser.songs = NULL;
 
292
 
 
293
        g_markup_parse_context_free(context);
 
294
 
 
295
        return &playlist->base;
 
296
}
 
297
 
 
298
static void
 
299
xspf_close(struct playlist_provider *_playlist)
 
300
{
 
301
        struct xspf_playlist *playlist = (struct xspf_playlist *)_playlist;
 
302
 
 
303
        g_slist_foreach(playlist->songs, song_free_callback, NULL);
 
304
        g_slist_free(playlist->songs);
 
305
        g_free(playlist);
 
306
}
 
307
 
 
308
static struct song *
 
309
xspf_read(struct playlist_provider *_playlist)
 
310
{
 
311
        struct xspf_playlist *playlist = (struct xspf_playlist *)_playlist;
 
312
        struct song *song;
 
313
 
 
314
        if (playlist->songs == NULL)
 
315
                return NULL;
 
316
 
 
317
        song = playlist->songs->data;
 
318
        playlist->songs = g_slist_remove(playlist->songs, song);
 
319
 
 
320
        return song;
 
321
}
 
322
 
 
323
static const char *const xspf_suffixes[] = {
 
324
        "xspf",
 
325
        NULL
 
326
};
 
327
 
 
328
static const char *const xspf_mime_types[] = {
 
329
        "application/xspf+xml",
 
330
        NULL
 
331
};
 
332
 
 
333
const struct playlist_plugin xspf_playlist_plugin = {
 
334
        .name = "xspf",
 
335
 
 
336
        .open_stream = xspf_open_stream,
 
337
        .close = xspf_close,
 
338
        .read = xspf_read,
 
339
 
 
340
        .suffixes = xspf_suffixes,
 
341
        .mime_types = xspf_mime_types,
 
342
};