Mercurial > audlegacy
annotate Plugins/Container/xspf/xspf.c @ 1584:2229b67f1b89 trunk
[svn] - xspf stores all meta data in tuples now.
- at load time, tuple for each entry in a playlist can be constructed from xspf. it means no read access to meta data in each data file is needed.
author | yaz |
---|---|
date | Sat, 19 Aug 2006 12:28:56 -0700 |
parents | d60e3dbe1a48 |
children | c4b78bc50412 |
rev | line source |
---|---|
1568 | 1 /* |
2 * Audacious: A cross-platform multimedia player | |
3 * Copyright (c) 2006 William Pitcock, Tony Vroon, George Averill, | |
4 * Giacomo Lozito, Derek Pomery and Yoshiki Yazawa. | |
5 * | |
6 * This program is free software; you can redistribute it and/or modify | |
7 * it under the terms of the GNU General Public License as published by | |
8 * the Free Software Foundation; either version 2 of the License, or | |
9 * (at your option) any later version. | |
10 * | |
11 * This program is distributed in the hope that it will be useful, | |
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 * GNU General Public License for more details. | |
15 * | |
16 * You should have received a copy of the GNU General Public License | |
17 * along with this program; if not, write to the Free Software | |
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
19 */ | |
20 | |
21 #include <glib.h> | |
22 #include <string.h> | |
23 #include <glib.h> | |
24 #include <glib/gprintf.h> | |
25 #include <stdlib.h> | |
26 #include <string.h> | |
27 #include <time.h> | |
28 | |
29 #include <unistd.h> | |
30 #include <sys/types.h> | |
31 #include <sys/stat.h> | |
32 #include <sys/errno.h> | |
33 | |
34 #include "audacious/main.h" | |
35 #include "audacious/util.h" | |
36 #include "audacious/playlist.h" | |
37 #include "audacious/playlist_container.h" | |
38 #include "audacious/plugin.h" | |
39 | |
40 #include <libxml/tree.h> | |
41 #include <libxml/parser.h> | |
42 #include <libxml/xmlreader.h> | |
43 #include <libxml/xpath.h> | |
44 #include <libxml/xpathInternals.h> | |
45 | |
46 static void | |
1584 | 47 add_file(xmlNode *track, const gchar *filename, gint pos) |
48 { | |
49 xmlNode *nptr; | |
50 TitleInput *tuple; | |
51 gchar *locale_uri = NULL; | |
52 | |
53 XMMS_NEW_TITLEINPUT(tuple); | |
54 | |
55 // creator, album, title, duration, trackNum, annotation, image, | |
56 for(nptr = track->children; nptr != NULL; nptr = nptr->next){ | |
57 if(nptr->type == XML_ELEMENT_NODE && !strcmp(nptr->name, "location")){ | |
58 xmlChar *str = xmlNodeGetContent(nptr); | |
59 locale_uri = g_locale_from_utf8(str,-1,NULL,NULL,NULL); | |
60 if(!locale_uri){ | |
61 /* try ISO-8859-1 for last resort */ | |
62 locale_uri = g_convert(str, -1, "ISO-8859-1", "UTF-8", NULL, NULL, NULL); | |
63 } | |
64 xmlFree(str); | |
65 if(locale_uri){ | |
66 tuple->file_name = g_path_get_basename(locale_uri); | |
67 tuple->file_path = g_strdup(locale_uri); | |
68 } | |
69 } | |
70 else if(nptr->type == XML_ELEMENT_NODE && !strcmp(nptr->name, "creator")){ | |
71 tuple->performer = (gchar *)xmlNodeGetContent(nptr); | |
72 } | |
73 else if(nptr->type == XML_ELEMENT_NODE && !strcmp(nptr->name, "album")){ | |
74 tuple->album_name = (gchar *)xmlNodeGetContent(nptr); | |
75 } | |
76 else if(nptr->type == XML_ELEMENT_NODE && !strcmp(nptr->name, "title")){ | |
77 tuple->track_name = (gchar *)xmlNodeGetContent(nptr); | |
78 } | |
79 else if(nptr->type == XML_ELEMENT_NODE && !strcmp(nptr->name, "duration")){ | |
80 xmlChar *str = xmlNodeGetContent(nptr); | |
81 tuple->length = atol(str); | |
82 xmlFree(str); | |
83 } | |
84 else if(nptr->type == XML_ELEMENT_NODE && !strcmp(nptr->name, "trackNum")){ | |
85 xmlChar *str = xmlNodeGetContent(nptr); | |
86 tuple->track_number = atol(str); | |
87 xmlFree(str); | |
88 } | |
89 | |
90 // | |
91 // additional metadata | |
92 // | |
93 // year, date, genre, comment, file_ext, file_path, formatter | |
94 // | |
95 else if(nptr->type == XML_ELEMENT_NODE && !strcmp(nptr->name, "meta")){ | |
96 xmlChar *rel = NULL; | |
97 | |
98 rel = xmlGetProp(nptr, "rel"); | |
99 | |
100 if(!strcmp(rel, "year")){ | |
101 xmlChar *cont = xmlNodeGetContent(nptr); | |
102 tuple->year = atol(cont); | |
103 xmlFree(cont); | |
104 continue; | |
105 } | |
106 else if(!strcmp(rel, "date")){ | |
107 tuple->date = (gchar *)xmlNodeGetContent(nptr); | |
108 continue; | |
109 } | |
110 else if(!strcmp(rel, "genre")){ | |
111 tuple->genre = (gchar *)xmlNodeGetContent(nptr); | |
112 continue; | |
113 } | |
114 else if(!strcmp(rel, "comment")){ | |
115 tuple->comment = (gchar *)xmlNodeGetContent(nptr); | |
116 continue; | |
117 } | |
118 else if(!strcmp(rel, "file_ext")){ | |
119 tuple->file_ext = (gchar *)xmlNodeGetContent(nptr); | |
120 continue; | |
121 } | |
122 else if(!strcmp(rel, "file_path")){ | |
123 tuple->file_path = (gchar *)xmlNodeGetContent(nptr); | |
124 continue; | |
125 } | |
126 else if(!strcmp(rel, "formatter")){ | |
127 tuple->formatter = (gchar *)xmlNodeGetContent(nptr); | |
128 continue; | |
129 } | |
130 xmlFree(rel); | |
131 rel = NULL; | |
132 } | |
133 | |
134 } | |
135 // add file to playlist | |
136 playlist_load_ins_file_tuple(locale_uri, filename, pos, tuple); | |
137 pos++; | |
138 if(locale_uri) { | |
139 g_free(locale_uri); | |
140 locale_uri = NULL; | |
141 } | |
142 } | |
143 | |
144 static void | |
145 find_track(xmlNode *tracklist, const gchar *filename, gint pos) | |
146 { | |
147 xmlNode *nptr; | |
148 for(nptr = tracklist->children; nptr != NULL; nptr = nptr->next){ | |
149 if(nptr->type == XML_ELEMENT_NODE && !strcmp(nptr->name, "track")){ | |
150 add_file(nptr, filename, pos); | |
151 } | |
152 } | |
153 } | |
154 | |
155 static void | |
1568 | 156 playlist_load_xspf(const gchar * filename, gint pos) |
157 { | |
1584 | 158 xmlDocPtr doc; |
159 xmlNode *nptr, *nptr2; | |
1568 | 160 |
1570
4023a295db39
[svn] - wma: use posix_memalign() instead of memalign() in some more spots
nenolod
parents:
1569
diff
changeset
|
161 g_return_if_fail(filename != NULL); |
4023a295db39
[svn] - wma: use posix_memalign() instead of memalign() in some more spots
nenolod
parents:
1569
diff
changeset
|
162 |
1568 | 163 doc = xmlParseFile(filename); |
164 if (doc == NULL) | |
165 return; | |
166 | |
1584 | 167 // find trackList |
168 for(nptr = doc->children; nptr != NULL; nptr = nptr->next) { | |
169 if(nptr->type == XML_ELEMENT_NODE && !strcmp(nptr->name, "playlist")){ | |
170 for(nptr2 = nptr->children; nptr2 != NULL; nptr2 = nptr2->next){ | |
171 if(nptr2->type == XML_ELEMENT_NODE && !strcmp(nptr2->name, "trackList")){ | |
172 find_track(nptr2, filename, pos); | |
173 } | |
174 } | |
1573 | 175 |
1584 | 176 } |
1574 | 177 } |
1568 | 178 |
1584 | 179 xmlFreeDoc(doc); |
1571
0b69bc0eb2d2
[svn] - if uri == NULL, then continue looking for URIs in the XSPF playlist
nenolod
parents:
1570
diff
changeset
|
180 |
1584 | 181 } |
1568 | 182 |
183 | |
184 #define XSPF_ROOT_NODE_NAME "playlist" | |
185 #define XSPF_XMLNS "http://xspf.org/ns/0/" | |
186 | |
187 static void | |
188 playlist_save_xspf(const gchar *filename, gint pos) | |
189 { | |
190 xmlDocPtr doc; | |
191 xmlNodePtr rootnode, tmp, tracklist; | |
192 GList *node; | |
193 | |
194 doc = xmlNewDoc("1.0"); | |
195 | |
196 rootnode = xmlNewNode(NULL, XSPF_ROOT_NODE_NAME); | |
197 xmlSetProp(rootnode, "xmlns", XSPF_XMLNS); | |
198 xmlSetProp(rootnode, "version", "1"); | |
199 xmlDocSetRootElement(doc, rootnode); | |
200 | |
201 tmp = xmlNewNode(NULL, "creator"); | |
202 xmlAddChild(tmp, xmlNewText(PACKAGE "-" VERSION)); | |
203 xmlAddChild(rootnode, tmp); | |
204 | |
205 tracklist = xmlNewNode(NULL, "trackList"); | |
206 xmlAddChild(rootnode, tracklist); | |
207 | |
208 for (node = playlist_get(); node != NULL; node = g_list_next(node)) | |
209 { | |
210 PlaylistEntry *entry = PLAYLIST_ENTRY(node->data); | |
211 xmlNodePtr track, location; | |
1575
e6fce20309f9
[svn] xspf.c needs round trip encoding conversion between locale encoding and utf8.
yaz
parents:
1574
diff
changeset
|
212 gchar *utf_filename = NULL; |
1568 | 213 |
214 track = xmlNewNode(NULL, "track"); | |
215 location = xmlNewNode(NULL, "location"); | |
216 | |
1579
d60e3dbe1a48
[svn] - better handling for file name which can't be represented correctly in current locale encoding.
yaz
parents:
1575
diff
changeset
|
217 /* try locale encoding first */ |
d60e3dbe1a48
[svn] - better handling for file name which can't be represented correctly in current locale encoding.
yaz
parents:
1575
diff
changeset
|
218 utf_filename = g_locale_to_utf8(entry->filename, -1, NULL, NULL, NULL); |
1584 | 219 |
1579
d60e3dbe1a48
[svn] - better handling for file name which can't be represented correctly in current locale encoding.
yaz
parents:
1575
diff
changeset
|
220 if (!utf_filename) { |
d60e3dbe1a48
[svn] - better handling for file name which can't be represented correctly in current locale encoding.
yaz
parents:
1575
diff
changeset
|
221 /* if above fails, try to guess */ |
d60e3dbe1a48
[svn] - better handling for file name which can't be represented correctly in current locale encoding.
yaz
parents:
1575
diff
changeset
|
222 utf_filename = str_to_utf8(entry->filename); |
d60e3dbe1a48
[svn] - better handling for file name which can't be represented correctly in current locale encoding.
yaz
parents:
1575
diff
changeset
|
223 } |
1584 | 224 |
225 if(!g_utf8_validate(utf_filename, -1, NULL)) | |
226 continue; | |
1575
e6fce20309f9
[svn] xspf.c needs round trip encoding conversion between locale encoding and utf8.
yaz
parents:
1574
diff
changeset
|
227 xmlAddChild(location, xmlNewText(utf_filename)); |
1568 | 228 xmlAddChild(track, location); |
229 xmlAddChild(tracklist, track); | |
230 | |
231 /* do we have a tuple? */ | |
232 if (entry->tuple != NULL) | |
233 { | |
1584 | 234 if (entry->tuple->performer != NULL && |
235 g_utf8_validate(entry->tuple->performer, -1, NULL)) | |
1568 | 236 { |
237 tmp = xmlNewNode(NULL, "creator"); | |
238 xmlAddChild(tmp, xmlNewText(entry->tuple->performer)); | |
239 xmlAddChild(track, tmp); | |
240 } | |
241 | |
1584 | 242 if (entry->tuple->album_name != NULL && |
243 g_utf8_validate(entry->tuple->album_name, -1, NULL)) | |
1568 | 244 { |
245 tmp = xmlNewNode(NULL, "album"); | |
246 xmlAddChild(tmp, xmlNewText(entry->tuple->album_name)); | |
247 xmlAddChild(track, tmp); | |
248 } | |
249 | |
1584 | 250 if (entry->tuple->track_name != NULL && |
251 g_utf8_validate(entry->tuple->track_name, -1, NULL)) | |
1568 | 252 { |
253 tmp = xmlNewNode(NULL, "title"); | |
254 xmlAddChild(tmp, xmlNewText(entry->tuple->track_name)); | |
255 xmlAddChild(track, tmp); | |
256 } | |
1584 | 257 |
258 if (entry->tuple->length != 0) | |
259 { | |
260 gchar *str; | |
261 str = g_malloc(128); // XXX fix me. | |
262 tmp = xmlNewNode(NULL, "duration"); | |
263 sprintf(str, "%d", entry->tuple->length); | |
264 xmlAddChild(tmp, xmlNewText(str)); | |
265 g_free(str); | |
266 xmlAddChild(track, tmp); | |
267 } | |
268 | |
269 if (entry->tuple->track_number != 0) | |
270 { | |
271 gchar *str; | |
272 str = g_malloc(128); // XXX fix me. | |
273 tmp = xmlNewNode(NULL, "trackNum"); | |
274 sprintf(str, "%d", entry->tuple->track_number); | |
275 xmlAddChild(tmp, xmlNewText(str)); | |
276 g_free(str); | |
277 xmlAddChild(track, tmp); | |
278 } | |
279 | |
280 // | |
281 // additional metadata | |
282 // | |
283 // year, date, genre, comment, file_ext, file_path, formatter | |
284 // | |
285 if (entry->tuple->year != 0) | |
286 { | |
287 gchar *str; | |
288 str = g_malloc(128); // XXX fix me. | |
289 tmp = xmlNewNode(NULL, "meta"); | |
290 xmlSetProp(tmp, "rel", "year"); | |
291 sprintf(str, "%d", entry->tuple->year); | |
292 xmlAddChild(tmp, xmlNewText(str)); | |
293 xmlAddChild(track, tmp); | |
294 g_free(str); | |
295 } | |
296 | |
297 if (entry->tuple->date != NULL && | |
298 g_utf8_validate(entry->tuple->date, -1, NULL)) | |
299 { | |
300 tmp = xmlNewNode(NULL, "meta"); | |
301 xmlSetProp(tmp, "rel", "date"); | |
302 xmlAddChild(tmp, xmlNewText(entry->tuple->date)); | |
303 xmlAddChild(track, tmp); | |
304 } | |
305 | |
306 if (entry->tuple->genre != NULL && | |
307 g_utf8_validate(entry->tuple->genre, -1, NULL)) | |
308 { | |
309 tmp = xmlNewNode(NULL, "meta"); | |
310 xmlSetProp(tmp, "rel", "genre"); | |
311 xmlAddChild(tmp, xmlNewText(entry->tuple->genre)); | |
312 xmlAddChild(track, tmp); | |
313 } | |
314 | |
315 if (entry->tuple->comment != NULL && | |
316 g_utf8_validate(entry->tuple->comment, -1, NULL)) | |
317 { | |
318 tmp = xmlNewNode(NULL, "meta"); | |
319 xmlSetProp(tmp, "rel", "comment"); | |
320 xmlAddChild(tmp, xmlNewText(entry->tuple->comment)); | |
321 xmlAddChild(track, tmp); | |
322 } | |
323 | |
324 if (entry->tuple->file_ext != NULL && | |
325 g_utf8_validate(entry->tuple->file_ext, -1, NULL)) | |
326 { | |
327 tmp = xmlNewNode(NULL, "meta"); | |
328 xmlSetProp(tmp, "rel", "file_ext"); | |
329 xmlAddChild(tmp, xmlNewText(entry->tuple->file_ext)); | |
330 xmlAddChild(track, tmp); | |
331 } | |
332 | |
333 if (entry->tuple->file_path != NULL && | |
334 g_utf8_validate(entry->tuple->file_path, -1, NULL)) | |
335 { | |
336 tmp = xmlNewNode(NULL, "meta"); | |
337 xmlSetProp(tmp, "rel", "file_path"); | |
338 xmlAddChild(tmp, xmlNewText(entry->tuple->file_path)); | |
339 xmlAddChild(track, tmp); | |
340 } | |
341 | |
342 if (entry->tuple->formatter != NULL && | |
343 g_utf8_validate(entry->tuple->formatter, -1, NULL)) | |
344 { | |
345 tmp = xmlNewNode(NULL, "meta"); | |
346 xmlSetProp(tmp, "rel", "formatter"); | |
347 xmlAddChild(tmp, xmlNewText(entry->tuple->formatter)); | |
348 xmlAddChild(track, tmp); | |
349 } | |
350 | |
1568 | 351 } |
1575
e6fce20309f9
[svn] xspf.c needs round trip encoding conversion between locale encoding and utf8.
yaz
parents:
1574
diff
changeset
|
352 if(utf_filename) { |
e6fce20309f9
[svn] xspf.c needs round trip encoding conversion between locale encoding and utf8.
yaz
parents:
1574
diff
changeset
|
353 g_free(utf_filename); |
e6fce20309f9
[svn] xspf.c needs round trip encoding conversion between locale encoding and utf8.
yaz
parents:
1574
diff
changeset
|
354 utf_filename = NULL; |
e6fce20309f9
[svn] xspf.c needs round trip encoding conversion between locale encoding and utf8.
yaz
parents:
1574
diff
changeset
|
355 } |
1568 | 356 } |
357 | |
358 xmlSaveFile(filename, doc); | |
359 xmlFreeDoc(doc); | |
360 } | |
361 | |
362 PlaylistContainer plc_xspf = { | |
363 .name = "XSPF Playlist Format", | |
364 .ext = "xspf", | |
365 .plc_read = playlist_load_xspf, | |
366 .plc_write = playlist_save_xspf, | |
367 }; | |
368 | |
369 static void init(void) | |
370 { | |
371 playlist_container_register(&plc_xspf); | |
372 } | |
373 | |
374 static void cleanup(void) | |
375 { | |
376 playlist_container_unregister(&plc_xspf); | |
377 } | |
378 | |
379 LowlevelPlugin llp_xspf = { | |
380 NULL, | |
381 NULL, | |
382 "XSPF Playlist Format", | |
383 init, | |
384 cleanup, | |
385 }; | |
386 | |
387 LowlevelPlugin *get_lplugin_info(void) | |
388 { | |
389 return &llp_xspf; | |
390 } |