~ubuntu-branches/ubuntu/utopic/rhythmbox/utopic-proposed

« back to all changes in this revision

Viewing changes to lib/rb-playlist.c

Tags: upstream-0.9.2
ImportĀ upstreamĀ versionĀ 0.9.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* 
2
 
   arch-tag: Implementation of Rhythmbox playlist parser
3
 
 
4
 
   Copyright (C) 2002, 2003 Bastien Nocera
5
 
   Copyright (C) 2003 Colin Walters <walters@rhythmbox.org>
6
 
 
7
 
   The Gnome Library is free software; you can redistribute it and/or
8
 
   modify it under the terms of the GNU Library General Public License as
9
 
   published by the Free Software Foundation; either version 2 of the
10
 
   License, or (at your option) any later version.
11
 
 
12
 
   The Gnome Library is distributed in the hope that it will be useful,
13
 
   but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 
   Library General Public License for more details.
16
 
 
17
 
   You should have received a copy of the GNU Library General Public
18
 
   License along with the Gnome Library; see the file COPYING.LIB.  If not,
19
 
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20
 
   Boston, MA 02111-1307, USA.
21
 
 
22
 
   Author: Bastien Nocera <hadess@hadess.net>
23
 
 */
24
 
 
25
 
#include "config.h"
26
 
#include "rb-playlist.h"
27
 
 
28
 
#include "rb-marshal.h"
29
 
#include "rb-file-helpers.h"
30
 
 
31
 
#include <glib.h>
32
 
#include <gtk/gtk.h>
33
 
#include <libxml/tree.h>
34
 
#include <libxml/parser.h>
35
 
#include <glade/glade.h>
36
 
#include <gconf/gconf-client.h>
37
 
#if HAVE_LIBGNOME_DESKTOP
38
 
#include <libgnome/gnome-desktop-item.h>
39
 
#endif
40
 
#include <libgnomevfs/gnome-vfs.h>
41
 
#include <libgnomevfs/gnome-vfs-mime-utils.h>
42
 
#include <string.h>
43
 
#include <libgnome/gnome-i18n.h>
44
 
 
45
 
static gboolean
46
 
rb_playlist_parse_recurse (RBPlaylist *playlist, const char *uri,
47
 
                           gint recurse_level);
48
 
 
49
 
#define READ_CHUNK_SIZE 8192
50
 
#define MIME_READ_CHUNK_SIZE 1024
51
 
 
52
 
typedef gboolean (*PlaylistCallback) (RBPlaylist *playlist, const char *url,
53
 
                                      guint recurse_level, gpointer data);
54
 
 
55
 
typedef struct {
56
 
        char *mimetype;
57
 
        PlaylistCallback func;
58
 
} PlaylistTypes;
59
 
 
60
 
struct RBPlaylistPrivate
61
 
{
62
 
        GladeXML *xml;
63
 
 
64
 
        int x, y;
65
 
};
66
 
 
67
 
/* Signals */
68
 
enum {
69
 
        ENTRY,
70
 
        LAST_SIGNAL
71
 
};
72
 
 
73
 
static int rb_playlist_table_signals[LAST_SIGNAL] = { 0 };
74
 
 
75
 
static GObjectClass *parent_class = NULL;
76
 
 
77
 
static void rb_playlist_class_init (RBPlaylistClass *class);
78
 
static void rb_playlist_init       (RBPlaylist      *playlist);
79
 
static void rb_playlist_finalize   (GObject *object);
80
 
 
81
 
const char * my_gnome_vfs_get_mime_type_with_data (const char *uri, gpointer *data);
82
 
 
83
 
 
84
 
GtkType
85
 
rb_playlist_get_type (void)
86
 
{
87
 
        static GtkType rb_playlist_type = 0;
88
 
 
89
 
        if (!rb_playlist_type) {
90
 
                static const GTypeInfo rb_playlist_info = {
91
 
                        sizeof (RBPlaylistClass),
92
 
                        (GBaseInitFunc) NULL,
93
 
                        (GBaseFinalizeFunc) NULL,
94
 
                        (GClassInitFunc) rb_playlist_class_init,
95
 
                        (GClassFinalizeFunc) NULL,
96
 
                        NULL /* class_data */,
97
 
                        sizeof (RBPlaylist),
98
 
                        0 /* n_preallocs */,
99
 
                        (GInstanceInitFunc) rb_playlist_init,
100
 
                };
101
 
 
102
 
                rb_playlist_type = g_type_register_static (G_TYPE_OBJECT,
103
 
                                                           "RBPlaylist", &rb_playlist_info,
104
 
                                                           (GTypeFlags)0);
105
 
        }
106
 
 
107
 
        return rb_playlist_type;
108
 
}
109
 
 
110
 
static void
111
 
rb_playlist_class_init (RBPlaylistClass *klass)
112
 
{
113
 
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
114
 
 
115
 
        parent_class = g_type_class_peek_parent (klass);
116
 
 
117
 
        object_class->finalize = rb_playlist_finalize;
118
 
 
119
 
        /* Signals */
120
 
        rb_playlist_table_signals[ENTRY] =
121
 
                g_signal_new ("entry",
122
 
                              G_TYPE_FROM_CLASS (klass),
123
 
                              G_SIGNAL_RUN_LAST,
124
 
                              G_STRUCT_OFFSET (RBPlaylistClass, entry),
125
 
                              NULL, NULL,
126
 
                              rb_marshal_VOID__STRING_STRING_STRING,
127
 
                              G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
128
 
}
129
 
 
130
 
GQuark
131
 
rb_playlist_error_quark (void)
132
 
{
133
 
        static GQuark quark;
134
 
        if (!quark)
135
 
                quark = g_quark_from_static_string ("rb_playlist_error");
136
 
 
137
 
        return quark;
138
 
}
139
 
 
140
 
RBPlaylist *
141
 
rb_playlist_new (void)
142
 
{
143
 
        return RB_PLAYLIST (g_object_new (RB_TYPE_PLAYLIST, NULL));
144
 
}
145
 
 
146
 
const char *
147
 
my_gnome_vfs_get_mime_type_with_data (const char *uri, gpointer *data)
148
 
{
149
 
        GnomeVFSResult result;
150
 
        GnomeVFSHandle *handle;
151
 
        char *buffer;
152
 
        const char *mimetype;
153
 
        GnomeVFSFileSize total_bytes_read;
154
 
        GnomeVFSFileSize bytes_read;
155
 
 
156
 
        *data = NULL;
157
 
 
158
 
        /* Open the file. */
159
 
        result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
160
 
        if (result != GNOME_VFS_OK)
161
 
                return NULL;
162
 
 
163
 
        /* Read the whole thing. */
164
 
        buffer = NULL;
165
 
        total_bytes_read = 0;
166
 
        do {
167
 
                buffer = g_realloc (buffer, total_bytes_read
168
 
                                + MIME_READ_CHUNK_SIZE);
169
 
                result = gnome_vfs_read (handle,
170
 
                                buffer + total_bytes_read,
171
 
                                MIME_READ_CHUNK_SIZE,
172
 
                                &bytes_read);
173
 
                if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_EOF) {
174
 
                        g_free (buffer);
175
 
                        gnome_vfs_close (handle);
176
 
                        return NULL;
177
 
                }
178
 
 
179
 
                /* Check for overflow. */
180
 
                if (total_bytes_read + bytes_read < total_bytes_read) {
181
 
                        g_free (buffer);
182
 
                        gnome_vfs_close (handle);
183
 
                        return NULL;
184
 
                }
185
 
 
186
 
                total_bytes_read += bytes_read;
187
 
        } while (result == GNOME_VFS_OK
188
 
                        && total_bytes_read < MIME_READ_CHUNK_SIZE);
189
 
 
190
 
        /* Close the file. */
191
 
        result = gnome_vfs_close (handle);
192
 
        if (result != GNOME_VFS_OK) {
193
 
                g_free (buffer);
194
 
                return NULL;
195
 
        }
196
 
 
197
 
        /* Return the file. */
198
 
        *data = g_realloc (buffer, total_bytes_read);
199
 
        mimetype = gnome_vfs_get_mime_type_for_data (*data, total_bytes_read);
200
 
 
201
 
        return mimetype;
202
 
}
203
 
 
204
 
static gboolean
205
 
write_string (GnomeVFSHandle *handle, const char *buf, GError **error)
206
 
{
207
 
        GnomeVFSResult res;
208
 
        GnomeVFSFileSize written;
209
 
        int len;
210
 
 
211
 
        len = strlen (buf);
212
 
        res = gnome_vfs_write (handle, buf, len, &written);
213
 
        if (res != GNOME_VFS_OK || written < len) {
214
 
                g_set_error (error,
215
 
                             RB_PLAYLIST_ERROR,
216
 
                             RB_PLAYLIST_ERROR_VFS_WRITE,
217
 
                             _("Couldn't write playlist: %s"),
218
 
                             gnome_vfs_result_to_string (res));
219
 
                gnome_vfs_close (handle);
220
 
                return FALSE;
221
 
        }
222
 
 
223
 
        return TRUE;
224
 
}
225
 
 
226
 
gboolean
227
 
rb_playlist_write (RBPlaylist *playlist, GtkTreeModel *model,
228
 
                   RBPlaylistIterFunc func, const char *output, GError **error)
229
 
{
230
 
        GnomeVFSHandle *handle;
231
 
        GnomeVFSResult res;
232
 
        int num_entries, i;
233
 
        char *buf;
234
 
        gboolean success;
235
 
 
236
 
        num_entries = gtk_tree_model_iter_n_children (model, NULL);
237
 
        res = gnome_vfs_open (&handle, output, GNOME_VFS_OPEN_WRITE);
238
 
        if (res == GNOME_VFS_ERROR_NOT_FOUND) {
239
 
                res = gnome_vfs_create (&handle, output,
240
 
                                GNOME_VFS_OPEN_WRITE, FALSE,
241
 
                                GNOME_VFS_PERM_USER_WRITE
242
 
                                | GNOME_VFS_PERM_USER_READ
243
 
                                | GNOME_VFS_PERM_GROUP_READ);
244
 
        }
245
 
 
246
 
        if (res != GNOME_VFS_OK) {
247
 
                g_set_error(error,
248
 
                            RB_PLAYLIST_ERROR,
249
 
                            RB_PLAYLIST_ERROR_VFS_OPEN,
250
 
                            _("Couldn't open playlist: %s"),
251
 
                            gnome_vfs_result_to_string (res));
252
 
                return FALSE;
253
 
        }
254
 
 
255
 
        buf = g_strdup ("[playlist]\n");
256
 
        success = write_string (handle, buf, error);
257
 
        g_free (buf);
258
 
        if (success == FALSE)
259
 
                return FALSE;
260
 
 
261
 
        buf = g_strdup_printf ("numberofentries=%d\n", num_entries);
262
 
        success = write_string (handle, buf, error);
263
 
        g_free (buf);
264
 
        if (success == FALSE)
265
 
                return FALSE;
266
 
 
267
 
        for (i = 1; i <= num_entries; i++) {
268
 
                GtkTreeIter iter;
269
 
                char *path, *url, *title;
270
 
 
271
 
                path = g_strdup_printf ("%d", i - 1);
272
 
                gtk_tree_model_get_iter_from_string (model, &iter, path);
273
 
                g_free (path);
274
 
                
275
 
                func (model, &iter, &url, &title);
276
 
 
277
 
                buf = g_strdup_printf ("file%d=%s\n", i, url);
278
 
                success = write_string (handle, buf, error);
279
 
                g_free (buf);
280
 
                g_free (url);
281
 
                if (success == FALSE)
282
 
                {
283
 
                        g_free (title);
284
 
                        return FALSE;
285
 
                }
286
 
 
287
 
                buf = g_strdup_printf ("title%d=%s\n", i, title);
288
 
                success = write_string (handle, buf, error);
289
 
                g_free (buf);
290
 
                g_free (title);
291
 
                if (success == FALSE)
292
 
                        return FALSE;
293
 
        }
294
 
 
295
 
        gnome_vfs_close (handle);
296
 
        return TRUE;
297
 
}
298
 
 
299
 
static GnomeVFSResult
300
 
my_eel_read_entire_file (const char *uri,
301
 
                int *file_size,
302
 
                char **file_contents)
303
 
{
304
 
        GnomeVFSResult result;
305
 
        GnomeVFSHandle *handle;
306
 
        char *buffer;
307
 
        GnomeVFSFileSize total_bytes_read;
308
 
        GnomeVFSFileSize bytes_read;
309
 
 
310
 
        *file_size = 0;
311
 
        *file_contents = NULL;
312
 
 
313
 
        /* Open the file. */
314
 
        result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
315
 
        if (result != GNOME_VFS_OK) {
316
 
                return result;
317
 
        }
318
 
 
319
 
        /* Read the whole thing. */
320
 
        buffer = NULL;
321
 
        total_bytes_read = 0;
322
 
        do {
323
 
                buffer = g_realloc (buffer, total_bytes_read + READ_CHUNK_SIZE);
324
 
                result = gnome_vfs_read (handle,
325
 
                                buffer + total_bytes_read,
326
 
                                READ_CHUNK_SIZE,
327
 
                                &bytes_read);
328
 
                if (result != GNOME_VFS_OK && result != GNOME_VFS_ERROR_EOF) {
329
 
                        g_free (buffer);
330
 
                        gnome_vfs_close (handle);
331
 
                        return result;
332
 
                }
333
 
 
334
 
                /* Check for overflow. */
335
 
                if (total_bytes_read + bytes_read < total_bytes_read) {
336
 
                        g_free (buffer);
337
 
                        gnome_vfs_close (handle);
338
 
                        return GNOME_VFS_ERROR_TOO_BIG;
339
 
                }
340
 
 
341
 
                total_bytes_read += bytes_read;
342
 
        } while (result == GNOME_VFS_OK);
343
 
 
344
 
        /* Close the file. */
345
 
        result = gnome_vfs_close (handle);
346
 
        if (result != GNOME_VFS_OK) {
347
 
                g_free (buffer);
348
 
                return result;
349
 
        }
350
 
 
351
 
        /* Return the file. */
352
 
        *file_size = total_bytes_read;
353
 
        *file_contents = g_realloc (buffer, total_bytes_read);
354
 
 
355
 
        return GNOME_VFS_OK;
356
 
}
357
 
 
358
 
static char*
359
 
rb_playlist_base_url (const char *url)
360
 
{
361
 
        /* Yay, let's reconstruct the base by hand */
362
 
        GnomeVFSURI *uri, *parent;
363
 
        char *base;
364
 
 
365
 
        uri = gnome_vfs_uri_new (url);
366
 
        parent = gnome_vfs_uri_get_parent (uri);
367
 
        base = gnome_vfs_uri_to_string (parent, 0);
368
 
 
369
 
        gnome_vfs_uri_unref (uri);
370
 
        gnome_vfs_uri_unref (parent);
371
 
 
372
 
        return base;
373
 
}
374
 
 
375
 
static int
376
 
read_ini_line_int (char **lines, const char *key)
377
 
{
378
 
        int retval = -1;
379
 
        int i;
380
 
 
381
 
        if (lines == NULL || key == NULL)
382
 
                return -1;
383
 
 
384
 
        for (i = 0; (lines[i] != NULL && retval == -1); i++) {
385
 
                if (g_ascii_strncasecmp (lines[i], key, strlen (key)) == 0) {
386
 
                        char **bits;
387
 
 
388
 
                        bits = g_strsplit (lines[i], "=", 2);
389
 
                        if (bits[0] == NULL || bits [1] == NULL) {
390
 
                                g_strfreev (bits);
391
 
                                return -1;
392
 
                        }
393
 
 
394
 
                        retval = (gint) g_strtod (bits[1], NULL);
395
 
                        g_strfreev (bits);
396
 
                }
397
 
        }
398
 
 
399
 
        return retval;
400
 
}
401
 
 
402
 
static char*
403
 
read_ini_line_string (char **lines, const char *key, gboolean dos_mode)
404
 
{
405
 
        char *retval = NULL;
406
 
        int i;
407
 
 
408
 
        if (lines == NULL || key == NULL)
409
 
                return NULL;
410
 
 
411
 
        for (i = 0; (lines[i] != NULL && retval == NULL); i++) {
412
 
                if (g_ascii_strncasecmp (lines[i], key, strlen (key)) == 0) {
413
 
                        char **bits;
414
 
                        ssize_t len;
415
 
 
416
 
                        bits = g_strsplit (lines[i], "=", 2);
417
 
                        if (bits[0] == NULL || bits [1] == NULL) {
418
 
                                g_strfreev (bits);
419
 
                                return NULL;
420
 
                        }
421
 
 
422
 
                        retval = g_strdup (bits[1]);
423
 
                        len = strlen (retval);
424
 
                        if (dos_mode && len >= 2 && retval[len-2] == '\r') {
425
 
                                retval[len-2] = '\n';
426
 
                                retval[len-1] = '\0';
427
 
                        }
428
 
                            
429
 
                        g_strfreev (bits);
430
 
                }
431
 
        }
432
 
 
433
 
        return retval;
434
 
}
435
 
 
436
 
static void
437
 
rb_playlist_init (RBPlaylist *playlist)
438
 
{
439
 
        playlist->priv = g_new0 (RBPlaylistPrivate, 1);
440
 
}
441
 
 
442
 
static void
443
 
rb_playlist_finalize (GObject *object)
444
 
{
445
 
        RBPlaylist *playlist = RB_PLAYLIST (object);
446
 
 
447
 
        g_return_if_fail (object != NULL);
448
 
        g_return_if_fail (playlist->priv != NULL);
449
 
 
450
 
        if (G_OBJECT_CLASS (parent_class)->finalize != NULL) {
451
 
                (* G_OBJECT_CLASS (parent_class)->finalize) (object);
452
 
        }
453
 
}
454
 
 
455
 
static void
456
 
rb_playlist_add_one_url (RBPlaylist *playlist, const char *url, const char *title)
457
 
{
458
 
        
459
 
        g_signal_emit (G_OBJECT (playlist), rb_playlist_table_signals[ENTRY],
460
 
                       0, url, title, NULL);
461
 
}
462
 
 
463
 
static void
464
 
rb_playlist_add_one_url_ext (RBPlaylist *playlist, const char *url, const char *title,
465
 
                             const char *genre)
466
 
{
467
 
        g_signal_emit (G_OBJECT (playlist), rb_playlist_table_signals[ENTRY],
468
 
                       0, url, title, genre);
469
 
}
470
 
 
471
 
static gboolean
472
 
rb_playlist_add_ram (RBPlaylist *playlist, const char *url,
473
 
                         guint recurse_level, gpointer data)
474
 
{
475
 
        gboolean retval = FALSE;
476
 
        char *contents, **lines;
477
 
        int size, i;
478
 
        const char *split_char;
479
 
 
480
 
        if (my_eel_read_entire_file (url, &size, &contents) != GNOME_VFS_OK)
481
 
                return FALSE;
482
 
 
483
 
        contents = g_realloc (contents, size + 1);
484
 
        contents[size] = '\0';
485
 
 
486
 
        /* figure out whether we're a unix or dos RAM file */
487
 
        if (strstr(contents,"\x0d") == NULL)
488
 
                split_char = "\n";
489
 
        else
490
 
                split_char = "\x0d\n";
491
 
 
492
 
        lines = g_strsplit (contents, split_char, 0);
493
 
        g_free (contents);
494
 
 
495
 
        for (i = 0; lines[i] != NULL; i++) {
496
 
                if (strcmp (lines[i], "") == 0)
497
 
                        continue;
498
 
 
499
 
                /* Either it's a URI, or it has a proper path ... */
500
 
                if (strstr(lines[i], "://") != NULL
501
 
                                || lines[i][0] == G_DIR_SEPARATOR) {
502
 
                        /* .ram files can contain .smil entries */
503
 
                        rb_playlist_parse_recurse (playlist, lines[i],
504
 
                                                   recurse_level);
505
 
                        rb_playlist_add_one_url (playlist, lines[i], NULL);
506
 
                } else if (strcmp (lines[i], "--stop--") == 0) {
507
 
                        /* For Real Media playlists, handle the stop command */
508
 
                        break;
509
 
                } else {
510
 
                        char *fullpath, *base;
511
 
 
512
 
                        /* Try with a base */
513
 
                        base = rb_playlist_base_url (url);
514
 
 
515
 
                        fullpath = g_strdup_printf ("%s/%s", base, lines[i]);
516
 
                        if (rb_playlist_parse_recurse (playlist, fullpath,
517
 
                                                       recurse_level) == TRUE)
518
 
                                retval = TRUE;
519
 
 
520
 
                        g_free (fullpath);
521
 
                        g_free (base);
522
 
                }
523
 
        }
524
 
 
525
 
        g_strfreev (lines);
526
 
 
527
 
        return retval;
528
 
}
529
 
 
530
 
static const char *
531
 
rb_playlist_get_extinfo_title (gboolean extinfo, char **lines, int i)
532
 
{
533
 
        const char *retval;
534
 
 
535
 
        if (extinfo == FALSE)
536
 
                return NULL;
537
 
 
538
 
        if (i == 0)
539
 
                return NULL;
540
 
 
541
 
        retval = strstr (lines[i-1], "#EXTINF:");
542
 
        retval = strstr (retval, ",");
543
 
        if (retval == NULL || retval[0] == '\0')
544
 
                return NULL;
545
 
 
546
 
        retval++;
547
 
 
548
 
        return retval;
549
 
}
550
 
 
551
 
static gboolean
552
 
rb_playlist_add_m3u (RBPlaylist *playlist, const char *url,
553
 
                     guint recurse_level, gpointer data)
554
 
{
555
 
        gboolean retval = FALSE;
556
 
        char *contents, **lines;
557
 
        int size, i;
558
 
        char *split_char;
559
 
        gboolean extinfo;
560
 
 
561
 
        if (my_eel_read_entire_file (url, &size, &contents) != GNOME_VFS_OK)
562
 
                return FALSE;
563
 
 
564
 
        contents = g_realloc (contents, size + 1);
565
 
        contents[size] = '\0';
566
 
 
567
 
        /* is TRUE if there's an EXTINF on the previous line */
568
 
        extinfo = FALSE;
569
 
 
570
 
        /* figure out whether we're a unix m3u or dos m3u */
571
 
        if (strstr(contents,"\x0d") == NULL)
572
 
                split_char = "\n";
573
 
        else
574
 
                split_char = "\x0d\n";
575
 
 
576
 
        lines = g_strsplit (contents, split_char, 0);
577
 
        g_free (contents);
578
 
 
579
 
        for (i = 0; lines[i] != NULL; i++) {
580
 
                if (lines[i][0] == '\0')
581
 
                        continue;
582
 
 
583
 
                /* Ignore comments, but mark it if we have extra info */
584
 
                if (lines[i][0] == '#') {
585
 
                        if (strstr (lines[i], "#EXTINF") != NULL)
586
 
                                extinfo = TRUE;
587
 
                        continue;
588
 
                }
589
 
 
590
 
                /* Either it's a URI, or it has a proper path ... */
591
 
                if (strstr(lines[i], "://") != NULL
592
 
                                || lines[i][0] == G_DIR_SEPARATOR) {
593
 
                        rb_playlist_parse_recurse (playlist, lines[i],
594
 
                                                   recurse_level);
595
 
                        rb_playlist_add_one_url (playlist, lines[i],
596
 
                                                 rb_playlist_get_extinfo_title (extinfo, lines, i));
597
 
                        retval = TRUE;
598
 
                        extinfo = FALSE;
599
 
                } else if (lines[i][0] == '\\' && lines[i][1] == '\\') {
600
 
                        /* ... Or it's in the windows smb form
601
 
                         * (\\machine\share\filename), Note drive names
602
 
                         * (C:\ D:\ etc) are unhandled (unknown base for
603
 
                         * drive letters) */
604
 
                        char *tmpurl;
605
 
 
606
 
                        lines[i] = g_strdelimit (lines[i], "\\", '/');
607
 
                        tmpurl = g_strjoin (NULL, "smb:", lines[i], NULL);
608
 
 
609
 
                        rb_playlist_add_one_url (playlist, tmpurl, NULL);
610
 
                        retval = TRUE;
611
 
                        extinfo = FALSE;
612
 
 
613
 
                        g_free (tmpurl);
614
 
                } else {
615
 
                        /* Try with a base */
616
 
                        char *fullpath, *base, sep;
617
 
 
618
 
                        retval = TRUE;                  
619
 
                        
620
 
                        base = rb_playlist_base_url (url);
621
 
                        sep = (split_char[0] == '\n' ? '/' : '\\');
622
 
                        if (sep == '\\')
623
 
                                lines[i] = g_strdelimit (lines[i], "\\", '/');
624
 
                        fullpath = g_strdup_printf ("%s/%s", base, lines[i]);
625
 
                        if (!rb_playlist_parse_recurse (playlist, fullpath,
626
 
                                                        recurse_level))
627
 
                                rb_playlist_add_one_url (playlist, fullpath, NULL);
628
 
                                
629
 
                        g_free (fullpath);
630
 
                        g_free (base);
631
 
                }
632
 
        }
633
 
 
634
 
        g_strfreev (lines);
635
 
 
636
 
        return retval;
637
 
}
638
 
 
639
 
static gboolean
640
 
rb_playlist_add_asf_parser (RBPlaylist *playlist, const char *url,
641
 
                              guint recurse_level, gpointer data)
642
 
{
643
 
        gboolean retval = FALSE;
644
 
        char *contents, **lines, *ref;
645
 
        int size;
646
 
 
647
 
        if (my_eel_read_entire_file (url, &size, &contents) != GNOME_VFS_OK)
648
 
                return FALSE;
649
 
 
650
 
        contents = g_realloc (contents, size + 1);
651
 
        contents[size] = '\0';
652
 
 
653
 
        lines = g_strsplit (contents, "\n", 0);
654
 
        g_free (contents);
655
 
 
656
 
        ref = read_ini_line_string (lines, "Ref1", FALSE);
657
 
 
658
 
        if (ref == NULL)
659
 
                goto bail;
660
 
 
661
 
        /* change http to mms, thanks Microsoft */
662
 
        if (strncmp ("http", ref, 4) == 0)
663
 
                memcpy(ref, "mmsh", 4);
664
 
 
665
 
        rb_playlist_add_one_url (playlist, ref, NULL);
666
 
        retval = TRUE;
667
 
        g_free (ref);
668
 
 
669
 
bail:
670
 
        g_strfreev (lines);
671
 
 
672
 
        return retval;
673
 
}
674
 
 
675
 
static gboolean
676
 
rb_playlist_add_pls (RBPlaylist *playlist, const char *url,
677
 
                     guint recurse_level, gpointer data)
678
 
{
679
 
        gboolean retval = FALSE;
680
 
        char *contents, **lines;
681
 
        int size, i, num_entries;
682
 
        char *split_char;
683
 
        gboolean dos_mode = FALSE;
684
 
 
685
 
        if (my_eel_read_entire_file (url, &size, &contents) != GNOME_VFS_OK)
686
 
                return FALSE;
687
 
 
688
 
        contents = g_realloc (contents, size + 1);
689
 
        contents[size] = '\0';
690
 
 
691
 
        /* figure out whether we're a unix pls or dos pls */
692
 
        if (strstr(contents,"\x0d") == NULL)
693
 
                split_char = "\n";
694
 
        else {
695
 
                split_char = "\x0d\n";
696
 
                dos_mode = TRUE;
697
 
        }
698
 
        lines = g_strsplit (contents, split_char, 0);
699
 
        g_free (contents);
700
 
 
701
 
        /* [playlist] */
702
 
        if (!lines[0]
703
 
            || g_ascii_strncasecmp (lines[0], "[playlist]",
704
 
                                (gsize)strlen ("[playlist]")) != 0)
705
 
                goto bail;
706
 
 
707
 
        /* numberofentries=? */
708
 
        num_entries = read_ini_line_int (lines, "numberofentries");
709
 
        if (num_entries == -1)
710
 
                goto bail;
711
 
 
712
 
        for (i = 1; i <= num_entries; i++) {
713
 
                char *file, *title, *genre;
714
 
                char *file_key, *title_key, *genre_key;
715
 
 
716
 
                file_key = g_strdup_printf ("file%d", i);
717
 
                title_key = g_strdup_printf ("title%d", i);
718
 
                /* Genre is our own little extension */
719
 
                genre_key = g_strdup_printf ("genre%d", i);
720
 
 
721
 
                file = read_ini_line_string (lines, (const char*)file_key, dos_mode);
722
 
                title = read_ini_line_string (lines, (const char*)title_key, dos_mode);
723
 
                genre = read_ini_line_string (lines, (const char*)genre_key, dos_mode);
724
 
 
725
 
                g_free (file_key);
726
 
                g_free (title_key);
727
 
                g_free (genre_key);
728
 
 
729
 
                if (file != NULL) {
730
 
                        rb_playlist_add_one_url_ext (playlist, file, title, genre);
731
 
                        retval = TRUE;
732
 
                } 
733
 
 
734
 
                g_free (file);
735
 
                g_free (title);
736
 
                g_free (genre);
737
 
        }
738
 
 
739
 
bail:
740
 
        g_strfreev (lines);
741
 
 
742
 
        return retval;
743
 
}
744
 
 
745
 
static gboolean
746
 
parse_asx_entry (RBPlaylist *playlist, guint recurse_level,
747
 
                 char *base, xmlDocPtr doc, xmlNodePtr parent)
748
 
{
749
 
        xmlNodePtr node;
750
 
        char *title, *url;
751
 
        gboolean retval = FALSE;
752
 
 
753
 
        title = NULL;
754
 
        url = NULL;
755
 
 
756
 
        for (node = parent->children; node != NULL; node = node->next) {
757
 
                if (node->name == NULL)
758
 
                        continue;
759
 
 
760
 
                /* ENTRY should only have one ref and one title nodes */
761
 
                if (g_ascii_strcasecmp (node->name, "ref") == 0) {
762
 
                        url = xmlGetProp (node, "href");
763
 
                        continue;
764
 
                }
765
 
 
766
 
                if (g_ascii_strcasecmp (node->name, "title") == 0)
767
 
                        title = xmlNodeListGetString (doc, node->children, 1);
768
 
        }
769
 
 
770
 
        if (url == NULL) {
771
 
                g_free (title);
772
 
                return FALSE;
773
 
        }
774
 
 
775
 
        if (strstr (url, "://") != NULL || url[0] == '/') {
776
 
                rb_playlist_add_one_url (playlist, url, title);
777
 
                retval = TRUE;
778
 
        } else {
779
 
                char *fullpath;
780
 
 
781
 
                fullpath = g_strdup_printf ("%s/%s", base, url);
782
 
                /* .asx files can contain references to other .asx files */
783
 
                rb_playlist_parse_recurse (playlist, fullpath, recurse_level);
784
 
 
785
 
                g_free (fullpath);
786
 
        }
787
 
 
788
 
        g_free (title);
789
 
        g_free (url);
790
 
 
791
 
        return retval;
792
 
}
793
 
 
794
 
static gboolean
795
 
parse_asx_entries (RBPlaylist *playlist, guint recurse_level,
796
 
                   char *base, xmlDocPtr doc, xmlNodePtr parent)
797
 
{
798
 
        xmlNodePtr node;
799
 
        gboolean retval = FALSE;
800
 
 
801
 
        for (node = parent->children; node != NULL; node = node->next) {
802
 
                if (node->name == NULL)
803
 
                        continue;
804
 
 
805
 
                if (g_ascii_strcasecmp (node->name, "entry") == 0) {
806
 
                        /* Whee found an entry here, find the REF and TITLE */
807
 
                        if (parse_asx_entry (playlist, recurse_level, base, doc, node) == TRUE)
808
 
                                retval = TRUE;
809
 
                }
810
 
        }
811
 
 
812
 
        return retval;
813
 
}
814
 
 
815
 
static gboolean
816
 
rb_playlist_add_asx (RBPlaylist *playlist, const char *url,
817
 
                     guint recurse_level, gpointer data)
818
 
{
819
 
        xmlDocPtr doc;
820
 
        xmlNodePtr node;
821
 
        char *contents = NULL, *base;
822
 
        int size;
823
 
        gboolean retval = FALSE;
824
 
 
825
 
        if (my_eel_read_entire_file (url, &size, &contents) != GNOME_VFS_OK)
826
 
                return FALSE;
827
 
 
828
 
        contents = g_realloc (contents, size + 1);
829
 
        contents[size] = '\0';
830
 
 
831
 
        doc = xmlParseMemory (contents, size);
832
 
        if (doc == NULL)
833
 
                doc = xmlRecoverMemory (contents, size);
834
 
        g_free (contents);
835
 
 
836
 
        /* If the document has no root, or no name */
837
 
        if(!doc || !doc->children || !doc->children->name) {
838
 
                if (doc != NULL)
839
 
                        xmlFreeDoc(doc);
840
 
                return FALSE;
841
 
        }
842
 
 
843
 
        base = rb_playlist_base_url (url);
844
 
 
845
 
        for (node = doc->children; node != NULL; node = node->next)
846
 
                if (parse_asx_entries (playlist, recurse_level, base, doc, node) == TRUE)
847
 
                        retval = TRUE;
848
 
 
849
 
        g_free (base);
850
 
        xmlFreeDoc(doc);
851
 
        return retval;
852
 
}
853
 
 
854
 
static gboolean
855
 
rb_playlist_add_ra (RBPlaylist *playlist, const char *url,
856
 
                    guint recurse_level, gpointer data)
857
 
{
858
 
        if (data == NULL
859
 
                        || (strncmp (data, "http://", strlen ("http://")) != 0
860
 
                        && strncmp (data, "rtsp://", strlen ("rtsp://")) != 0
861
 
                            && strncmp (data, "pnm://", strlen ("pnm://")) != 0)) {
862
 
                rb_playlist_add_one_url (playlist, url, NULL);
863
 
                return TRUE;
864
 
        }
865
 
 
866
 
        return rb_playlist_add_ram (playlist, url, recurse_level, NULL);
867
 
}
868
 
 
869
 
static gboolean
870
 
parse_smil_video_entry (RBPlaylist *playlist, char *base,
871
 
                        char *url, char *title)
872
 
{
873
 
        if (strstr (url, "://") != NULL || url[0] == '/') {
874
 
                rb_playlist_add_one_url (playlist, url, title);
875
 
        } else {
876
 
                char *fullpath;
877
 
 
878
 
                fullpath = g_strdup_printf ("%s/%s", base, url);
879
 
                rb_playlist_add_one_url (playlist, fullpath, title);
880
 
 
881
 
                g_free (fullpath);
882
 
        }
883
 
 
884
 
        return TRUE;
885
 
}
886
 
 
887
 
static gboolean
888
 
parse_smil_entry (RBPlaylist *playlist, guint recurse_level, char *base,
889
 
                  xmlDocPtr doc, xmlNodePtr parent)
890
 
{
891
 
        xmlNodePtr node;
892
 
        char *title, *url;
893
 
        gboolean retval = FALSE;
894
 
 
895
 
        title = NULL;
896
 
        url = NULL;
897
 
 
898
 
        if (recurse_level > 5)
899
 
                return FALSE;
900
 
 
901
 
        for (node = parent->children; node != NULL; node = node->next) {
902
 
                if (node->name == NULL)
903
 
                        continue;
904
 
 
905
 
                /* ENTRY should only have one ref and one title nodes */
906
 
                if (g_ascii_strcasecmp (node->name, "video") == 0) {
907
 
                        url = xmlGetProp (node, "src");
908
 
                        title = xmlGetProp (node, "title");
909
 
 
910
 
                        if (url != NULL) {
911
 
                                if (parse_smil_video_entry (playlist,
912
 
                                                            base, url, title) == TRUE)
913
 
                                        retval = TRUE;
914
 
                        }
915
 
 
916
 
                        g_free (title);
917
 
                        g_free (url);
918
 
                } else {
919
 
                        if (parse_smil_entry (playlist, recurse_level+1,
920
 
                                              base, doc, node) == TRUE)
921
 
                                retval = TRUE;
922
 
                }
923
 
        }
924
 
 
925
 
        return retval;
926
 
}
927
 
 
928
 
static gboolean
929
 
parse_smil_entries (RBPlaylist *playlist, guint recurse_level,
930
 
                    char *base, xmlDocPtr doc, xmlNodePtr parent)
931
 
{
932
 
        xmlNodePtr node;
933
 
        gboolean retval = FALSE;
934
 
 
935
 
        for (node = parent->children; node != NULL; node = node->next) {
936
 
                if (node->name == NULL)
937
 
                        continue;
938
 
 
939
 
                if (g_ascii_strcasecmp (node->name, "body") == 0) {
940
 
                        if (parse_smil_entry (playlist, recurse_level,
941
 
                                              base, doc, node) == TRUE)
942
 
                                retval = TRUE;
943
 
                }
944
 
 
945
 
        }
946
 
 
947
 
        return retval;
948
 
}
949
 
 
950
 
static gboolean
951
 
rb_playlist_add_smil (RBPlaylist *playlist, const char *url,
952
 
                      guint recurse_level, gpointer data)
953
 
{
954
 
        xmlDocPtr doc;
955
 
        xmlNodePtr node;
956
 
        char *contents = NULL, *base;
957
 
        int size;
958
 
        gboolean retval = FALSE;
959
 
 
960
 
        if (my_eel_read_entire_file (url, &size, &contents) != GNOME_VFS_OK)
961
 
                return FALSE;
962
 
 
963
 
        contents = g_realloc (contents, size + 1);
964
 
        contents[size] = '\0';
965
 
 
966
 
        doc = xmlParseMemory (contents, size);
967
 
        if (doc == NULL)
968
 
                doc = xmlRecoverMemory (contents, size);
969
 
        g_free (contents);
970
 
 
971
 
        /* If the document has no root, or no name */
972
 
        if(!doc || !doc->children
973
 
                        || !doc->children->name
974
 
                        || g_ascii_strcasecmp (doc->children->name,
975
 
                                "smil") != 0) {
976
 
                if (doc != NULL)
977
 
                        xmlFreeDoc(doc);
978
 
                return FALSE;
979
 
        }
980
 
 
981
 
        base = rb_playlist_base_url (url);
982
 
 
983
 
        for (node = doc->children; node != NULL; node = node->next)
984
 
                if (parse_smil_entries (playlist, recurse_level, base, doc, node) == TRUE)
985
 
                        retval = TRUE;
986
 
 
987
 
        return FALSE;
988
 
}
989
 
 
990
 
static gboolean
991
 
rb_playlist_add_asf (RBPlaylist *playlist, const char *url,
992
 
                     guint recurse_level, gpointer data)
993
 
{
994
 
        if (data == NULL) {
995
 
                rb_playlist_add_one_url (playlist, url, NULL);
996
 
                return TRUE;
997
 
        }
998
 
 
999
 
        if (strncmp (data, "[Reference]", strlen ("[Reference]")) != 0) {
1000
 
                rb_playlist_add_one_url (playlist, url, NULL);
1001
 
                return TRUE;
1002
 
        }
1003
 
 
1004
 
        return rb_playlist_add_asf_parser (playlist, url, recurse_level, data);
1005
 
}
1006
 
 
1007
 
#if HAVE_LIBGNOME_DESKTOP
1008
 
static gboolean
1009
 
rb_playlist_add_desktop (RBPlaylist *playlist, const char *url,
1010
 
                         guint recurse_level, gpointer data)
1011
 
{
1012
 
        GnomeDesktopItem *ditem;
1013
 
        int type;
1014
 
        gboolean retval;
1015
 
        const char *path, *display_name;
1016
 
 
1017
 
        ditem = gnome_desktop_item_new_from_file (url, 0, NULL);
1018
 
        if (ditem == NULL)
1019
 
                return FALSE;
1020
 
 
1021
 
        type = gnome_desktop_item_get_entry_type (ditem);
1022
 
        if (type != GNOME_DESKTOP_ITEM_TYPE_LINK) {
1023
 
                gnome_desktop_item_unref (ditem);
1024
 
                return FALSE;
1025
 
        }
1026
 
 
1027
 
        path = gnome_desktop_item_get_string (ditem, "URL");
1028
 
        if (path == NULL) {
1029
 
                gnome_desktop_item_unref (ditem);
1030
 
                return FALSE;
1031
 
        }
1032
 
        display_name = gnome_desktop_item_get_localestring (ditem, "Name");
1033
 
        retval = rb_playlist_add_url (playlist, path, display_name);
1034
 
        gnome_desktop_item_unref (ditem);
1035
 
 
1036
 
        return retval;
1037
 
}
1038
 
#endif
1039
 
 
1040
 
/* These ones need a special treatment, mostly playlist formats */
1041
 
static PlaylistTypes special_types[] = {
1042
 
        { "audio/x-mpegurl", rb_playlist_add_m3u },
1043
 
        { "audio/x-ms-asx", rb_playlist_add_asx },
1044
 
        { "audio/x-scpls", rb_playlist_add_pls },
1045
 
        { "application/x-smil", rb_playlist_add_smil },
1046
 
#if HAVE_LIBGNOME_DESKTOP
1047
 
        { "application/x-gnome-app-info", rb_playlist_add_desktop },
1048
 
#endif  
1049
 
        { "video/x-ms-wvx", rb_playlist_add_asx },
1050
 
        { "video/x-ms-wax", rb_playlist_add_asx },
1051
 
};
1052
 
 
1053
 
/* These ones are "dual" types, might be a video, might be a playlist */
1054
 
static PlaylistTypes dual_types[] = {
1055
 
        { "audio/x-real-audio", rb_playlist_add_ra },
1056
 
        { "audio/x-pn-realaudio", rb_playlist_add_ra },
1057
 
        { "application/vnd.rn-realmedia", rb_playlist_add_ra },
1058
 
        { "audio/x-pn-realaudio-plugin", rb_playlist_add_ra },
1059
 
        { "text/plain", rb_playlist_add_ra },
1060
 
        { "video/x-ms-asf", rb_playlist_add_asf },
1061
 
        { "video/x-ms-wmv", rb_playlist_add_asf },
1062
 
};
1063
 
 
1064
 
static gboolean
1065
 
rb_playlist_add_url_from_data (RBPlaylist *playlist, const char *url,
1066
 
                               guint recurse_level)
1067
 
{
1068
 
        const char *mimetype;
1069
 
        gboolean retval;
1070
 
        gpointer data;
1071
 
        int i;
1072
 
 
1073
 
        mimetype = my_gnome_vfs_get_mime_type_with_data (url, &data);
1074
 
        if (mimetype == NULL)
1075
 
                return FALSE;
1076
 
 
1077
 
        for (i = 0; i < G_N_ELEMENTS(special_types); i++) {
1078
 
                if (mimetype == NULL)
1079
 
                        break;
1080
 
                if (strcmp (special_types[i].mimetype, mimetype) == 0) {
1081
 
                        retval = (* special_types[i].func) (playlist, url,
1082
 
                                                            recurse_level, data);
1083
 
                        g_free (data);
1084
 
                        return retval;
1085
 
                }
1086
 
        }
1087
 
 
1088
 
        for (i = 0; i < G_N_ELEMENTS(dual_types); i++) {
1089
 
                if (strcmp (dual_types[i].mimetype, mimetype) == 0) {
1090
 
                        retval = (* dual_types[i].func) (playlist, url,
1091
 
                                                         recurse_level, data);
1092
 
                        g_free (data);
1093
 
                        return retval;
1094
 
                }
1095
 
        }
1096
 
 
1097
 
        g_free (data);
1098
 
 
1099
 
        return FALSE;
1100
 
}
1101
 
 
1102
 
static gboolean
1103
 
rb_playlist_parse_recurse (RBPlaylist *playlist, const char *uri,
1104
 
                           gint recurse_level)
1105
 
{
1106
 
        char *mimetype;
1107
 
        int i;
1108
 
 
1109
 
        g_return_val_if_fail (uri != NULL, FALSE);
1110
 
 
1111
 
        if (recurse_level > 3)
1112
 
                return FALSE;
1113
 
 
1114
 
        if (recurse_level > 1 && rb_uri_is_iradio (uri)) {
1115
 
                rb_playlist_add_one_url (playlist, uri, NULL);
1116
 
                return TRUE;
1117
 
        }
1118
 
 
1119
 
        recurse_level++;
1120
 
 
1121
 
        mimetype = gnome_vfs_get_mime_type (uri);
1122
 
 
1123
 
        if (mimetype == NULL)
1124
 
                return rb_playlist_add_url_from_data (playlist, uri, recurse_level);
1125
 
 
1126
 
        for (i = 0; i < G_N_ELEMENTS(special_types); i++) {
1127
 
                if (strcmp (special_types[i].mimetype, mimetype) == 0) {
1128
 
                        g_free (mimetype);
1129
 
                        return (* special_types[i].func) (playlist, uri, recurse_level, NULL);
1130
 
                }
1131
 
        }
1132
 
 
1133
 
        for (i = 0; i < G_N_ELEMENTS(dual_types); i++) {
1134
 
                if (strcmp (dual_types[i].mimetype, mimetype) == 0) {
1135
 
                        g_free (mimetype);
1136
 
                        return rb_playlist_add_url_from_data (playlist, uri, recurse_level);
1137
 
                }
1138
 
        }
1139
 
 
1140
 
        return FALSE;
1141
 
}
1142
 
                
1143
 
 
1144
 
gboolean
1145
 
rb_playlist_parse (RBPlaylist *playlist, const char *url)
1146
 
{
1147
 
        return rb_playlist_parse_recurse (playlist, url, 1);
1148
 
}
1149
 
 
1150
 
gboolean
1151
 
rb_playlist_can_handle (const char *url)
1152
 
{
1153
 
        const char *mimetype;
1154
 
        int i;
1155
 
 
1156
 
        g_return_val_if_fail (url != NULL, FALSE);
1157
 
 
1158
 
        mimetype = gnome_vfs_get_mime_type (url);
1159
 
 
1160
 
        if (mimetype == NULL)
1161
 
                return FALSE;
1162
 
 
1163
 
        for (i = 0; i < G_N_ELEMENTS(special_types); i++)
1164
 
                if (strcmp (special_types[i].mimetype, mimetype) == 0)
1165
 
                        return TRUE;
1166
 
 
1167
 
        for (i = 0; i < G_N_ELEMENTS(dual_types); i++)
1168
 
                if (strcmp (dual_types[i].mimetype, mimetype) == 0)
1169
 
                        return TRUE;
1170
 
 
1171
 
        return FALSE;
1172
 
}