1 /*
2 Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Bastien Nocera
3 Copyright (C) 2003, 2004 Colin Walters <walters@rhythmbox.org>
4
5 The Gnome Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
9
10 The Gnome Library 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 GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with the Gnome Library; see the file COPYING.LIB. If not,
17 write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301 USA.
19
20 Author: Bastien Nocera <hadess@hadess.net>
21 */
22
23 #include "config.h"
24
25 #ifndef TOTEM_PL_PARSER_MINI
26 #include <string.h>
27 #include <glib.h>
28 #include <glib/gi18n-lib.h>
29
30 #include "totem-pl-parser.h"
31 #endif /* !TOTEM_PL_PARSER_MINI */
32
33 #include "totem-pl-parser-mini.h"
34 #include "totem-pl-parser-pls.h"
35 #include "totem-pl-parser-private.h"
36
37 #ifndef TOTEM_PL_PARSER_MINI
38 gboolean
totem_pl_parser_save_pls(TotemPlParser * parser,TotemPlPlaylist * playlist,GFile * output,const gchar * title,GCancellable * cancellable,GError ** error)39 totem_pl_parser_save_pls (TotemPlParser *parser,
40 TotemPlPlaylist *playlist,
41 GFile *output,
42 const gchar *title,
43 GCancellable *cancellable,
44 GError **error)
45 {
46 TotemPlPlaylistIter iter;
47 GFileOutputStream *stream;
48 int num_entries, i;
49 gboolean valid, success;
50 char *buf;
51
52 num_entries = totem_pl_parser_num_entries (parser, playlist);
53
54 stream = g_file_replace (output, NULL, FALSE, G_FILE_CREATE_NONE, cancellable, error);
55 if (stream == NULL)
56 return FALSE;
57
58 buf = g_strdup ("[playlist]\n");
59 success = totem_pl_parser_write_string (G_OUTPUT_STREAM (stream), buf, cancellable, error);
60 g_free (buf);
61 if (success == FALSE)
62 return FALSE;
63
64 if (title != NULL) {
65 buf = g_strdup_printf ("X-GNOME-Title=%s\n", title);
66 success = totem_pl_parser_write_string (G_OUTPUT_STREAM (stream), buf, cancellable, error);
67 g_free (buf);
68 if (success == FALSE)
69 return FALSE;
70 }
71
72 buf = g_strdup_printf ("NumberOfEntries=%d\n", num_entries);
73 success = totem_pl_parser_write_string (G_OUTPUT_STREAM (stream), buf, cancellable, error);
74 g_free (buf);
75 if (success == FALSE)
76 return FALSE;
77
78 valid = totem_pl_playlist_iter_first (playlist, &iter);
79 i = 0;
80
81 while (valid) {
82 gchar *uri, *entry_title, *relative;
83 GFile *file;
84
85 totem_pl_playlist_get (playlist, &iter,
86 TOTEM_PL_PARSER_FIELD_URI, &uri,
87 TOTEM_PL_PARSER_FIELD_TITLE, &entry_title,
88 NULL);
89
90 valid = totem_pl_playlist_iter_next (playlist, &iter);
91
92 if (!uri) {
93 g_free (entry_title);
94 continue;
95 }
96
97 file = g_file_new_for_uri (uri);
98
99 if (totem_pl_parser_scheme_is_ignored (parser, file)) {
100 g_object_unref (file);
101 g_free (uri);
102 g_free (entry_title);
103 continue;
104 }
105
106 g_object_unref (file);
107 i++;
108
109 relative = totem_pl_parser_relative (output, uri);
110 buf = g_strdup_printf ("File%d=%s\n", i, relative ? relative : uri);
111 g_free (relative);
112 g_free (uri);
113
114 success = totem_pl_parser_write_string (G_OUTPUT_STREAM (stream), buf, cancellable, error);
115 g_free (buf);
116
117 if (success == FALSE) {
118 g_free (entry_title);
119 return FALSE;
120 }
121
122 if (!entry_title) {
123 continue;
124 }
125
126 buf = g_strdup_printf ("Title%d=%s\n", i, entry_title);
127 success = totem_pl_parser_write_string (G_OUTPUT_STREAM (stream), buf, cancellable, error);
128 g_free (buf);
129 g_free (entry_title);
130
131 if (success == FALSE) {
132 return FALSE;
133 }
134 }
135
136 g_object_unref (stream);
137 return TRUE;
138 }
139
140 static char *
ensure_utf8_valid(char * input)141 ensure_utf8_valid (char *input)
142 {
143 char *utf8_valid;
144
145 utf8_valid = g_strdup (input);
146
147 if (!g_utf8_validate (utf8_valid, -1, NULL)) {
148 gint i;
149
150 for (i = 0; i < g_utf8_strlen (utf8_valid, -1); i++) {
151 gunichar c;
152 c = g_utf8_get_char_validated (&utf8_valid[i], -1);
153 if (c > 127) {
154 utf8_valid[i] = '?';
155 }
156 }
157 }
158 return utf8_valid;
159 }
160
161 TotemPlParserResult
totem_pl_parser_add_pls_with_contents(TotemPlParser * parser,GFile * file,GFile * _base_file,const char * contents,TotemPlParseData * parse_data)162 totem_pl_parser_add_pls_with_contents (TotemPlParser *parser,
163 GFile *file,
164 GFile *_base_file,
165 const char *contents,
166 TotemPlParseData *parse_data)
167 {
168 TotemPlParserResult retval = TOTEM_PL_PARSER_RESULT_UNHANDLED;
169 GFile *base_file;
170 char **lines;
171 guint i, num_entries;
172 char *playlist_title;
173 gboolean fallback;
174 GHashTable *entries;
175 guint found_entries;
176 char *uri;
177
178 lines = g_strsplit_set (contents, "\r\n", 0);
179
180 /* [playlist] */
181 i = 0;
182 num_entries = 0;
183
184 /* Ignore empty lines */
185 while (lines[i] != NULL && totem_pl_parser_line_is_empty (lines[i]) != FALSE)
186 i++;
187
188 if (lines[i] == NULL
189 || g_ascii_strncasecmp (lines[i], "[playlist]",
190 (gsize)strlen ("[playlist]")) != 0) {
191 g_strfreev (lines);
192 return retval;
193 }
194
195 playlist_title = totem_pl_parser_read_ini_line_string (lines,
196 "X-GNOME-Title");
197 totem_pl_parser_add_uri (parser,
198 TOTEM_PL_PARSER_FIELD_IS_PLAYLIST, TRUE,
199 TOTEM_PL_PARSER_FIELD_FILE, file,
200 TOTEM_PL_PARSER_FIELD_TITLE, playlist_title,
201 TOTEM_PL_PARSER_FIELD_CONTENT_TYPE, "audio/x-scpls",
202 NULL);
203 g_free (playlist_title);
204
205 /* Load the file in hash table to speed up the later processing */
206 entries = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
207 for (i = 0; lines[i] != NULL; i++) {
208 char **bits;
209 char *value;
210
211 if (totem_pl_parser_line_is_empty (lines[i]))
212 continue;
213
214 if (lines[i][0] == '#' || lines[i][0] == '[')
215 continue;
216
217 bits = g_strsplit (lines[i], "=", 2);
218 if (bits[0] == NULL || bits [1] == NULL) {
219 g_strfreev (bits);
220 continue;
221 }
222
223 if (g_ascii_strncasecmp (g_strchug (bits[0]), "file", strlen ("file")) == 0)
224 num_entries++;
225
226 value = g_strdup (bits[1]);
227
228 g_hash_table_insert (entries,
229 g_ascii_strdown (bits[0], strlen (bits[0])),
230 value);
231 g_strfreev (bits);
232 }
233 g_strfreev (lines);
234
235 /* Base? */
236 if (_base_file == NULL)
237 base_file = g_file_get_parent (file);
238 else
239 base_file = g_object_ref (_base_file);
240
241 retval = TOTEM_PL_PARSER_RESULT_SUCCESS;
242
243 found_entries = 0;
244 for (i = 1; found_entries < num_entries; i++) {
245 char *file_str, *title, *genre, *length;
246 char *file_key, *title_key, *genre_key, *length_key;
247 gint64 length_num;
248
249 file_key = g_strdup_printf ("file%d", i);
250 title_key = g_strdup_printf ("title%d", i);
251 length_key = g_strdup_printf ("length%d", i);
252 length_num = 0;
253 /* Genre is our own little extension */
254 genre_key = g_strdup_printf ("genre%d", i);
255
256 file_str = g_hash_table_lookup (entries, file_key);
257 title = g_hash_table_lookup (entries, title_key);
258 genre = g_hash_table_lookup (entries, genre_key);
259 length = g_hash_table_lookup (entries, length_key);
260
261 g_free (file_key);
262 g_free (title_key);
263 g_free (genre_key);
264 g_free (length_key);
265
266 if (file_str == NULL)
267 continue;
268 found_entries++;
269
270 fallback = parse_data->fallback;
271 if (parse_data->recurse)
272 parse_data->fallback = FALSE;
273
274 /* Get the length, if it's negative, that means that we have a stream
275 * and should push the entry straight away */
276 if (length != NULL)
277 length_num = totem_pl_parser_parse_duration (length, totem_pl_parser_is_debugging_enabled (parser));
278
279 if (strstr (file_str, "://") != NULL || file_str[0] == G_DIR_SEPARATOR) {
280 GFile *target;
281
282 target = g_file_new_for_commandline_arg (file_str);
283 if (length_num < 0 || totem_pl_parser_parse_internal (parser, target, NULL, parse_data) != TOTEM_PL_PARSER_RESULT_SUCCESS) {
284 totem_pl_parser_add_uri (parser,
285 TOTEM_PL_PARSER_FIELD_URI, file_str,
286 TOTEM_PL_PARSER_FIELD_TITLE, title,
287 TOTEM_PL_PARSER_FIELD_GENRE, genre,
288 TOTEM_PL_PARSER_FIELD_DURATION, length,
289 TOTEM_PL_PARSER_FIELD_BASE_FILE, base_file, NULL);
290 }
291 g_object_unref (target);
292 } else {
293 GFile *target;
294 char *utf8_filename;
295
296 utf8_filename = ensure_utf8_valid (file_str);
297 target = g_file_get_child_for_display_name (base_file, utf8_filename, NULL);
298 g_free (utf8_filename);
299
300 if (length_num < 0 || totem_pl_parser_parse_internal (parser, target, base_file, parse_data) != TOTEM_PL_PARSER_RESULT_SUCCESS) {
301 totem_pl_parser_add_uri (parser,
302 TOTEM_PL_PARSER_FIELD_FILE, target,
303 TOTEM_PL_PARSER_FIELD_TITLE, title,
304 TOTEM_PL_PARSER_FIELD_GENRE, genre,
305 TOTEM_PL_PARSER_FIELD_DURATION, length,
306 TOTEM_PL_PARSER_FIELD_BASE_FILE, base_file, NULL);
307 }
308
309 g_object_unref (target);
310 }
311
312 parse_data->fallback = fallback;
313 }
314
315 uri = g_file_get_uri (file);
316 totem_pl_parser_playlist_end (parser, uri);
317 g_free (uri);
318
319 g_object_unref (base_file);
320 g_hash_table_destroy (entries);
321
322 return retval;
323 }
324
325 TotemPlParserResult
totem_pl_parser_add_pls(TotemPlParser * parser,GFile * file,GFile * base_file,TotemPlParseData * parse_data,gpointer data)326 totem_pl_parser_add_pls (TotemPlParser *parser,
327 GFile *file,
328 GFile *base_file,
329 TotemPlParseData *parse_data,
330 gpointer data)
331 {
332 TotemPlParserResult retval = TOTEM_PL_PARSER_RESULT_UNHANDLED;
333 char *contents;
334 gsize size;
335
336 if (g_file_load_contents (file, NULL, &contents, &size, NULL, NULL) == FALSE)
337 return TOTEM_PL_PARSER_RESULT_ERROR;
338
339 if (size == 0) {
340 g_free (contents);
341 return TOTEM_PL_PARSER_RESULT_SUCCESS;
342 }
343
344 retval = totem_pl_parser_add_pls_with_contents (parser, file, base_file, contents, parse_data);
345 g_free (contents);
346
347 return retval;
348 }
349
350 #endif /* !TOTEM_PL_PARSER_MINI */
351
352