comparison src/xspf/xspf.c @ 641:3f0a3c24f2b9 trunk

[svn] libxspf improvements: - now libxspf can parse relative path with xml:base notation. - backward compatibility support for base64 and locale_to_utf8 has been removed. - make use of libxml2's url encoding/decoding. - now mtime is always recorded even if the value is 0. - tentative support for "staticlist". -- if a mtime entry for each track is set to 0, metadata for corresponding track will not be updated automatically. -- if there is an element shown below in the playlist-element, all mtime entries in the playlist will be regarded as 0 so that whole metadata in the playlist will be conserved. staticlist element: <extension application="audacious"> <options staticlist="true"/> </extension>
author yaz
date Mon, 12 Feb 2007 20:02:17 -0800
parents 2d20bc58a290
children 2605c5515bab
comparison
equal deleted inserted replaced
640:0fde94f6285f 641:3f0a3c24f2b9
20 20
21 #include <config.h> 21 #include <config.h>
22 22
23 #include <glib.h> 23 #include <glib.h>
24 #include <string.h> 24 #include <string.h>
25 #include <glib.h>
26 #include <glib/gprintf.h>
27 #include <stdlib.h> 25 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h> 26 #include <time.h>
30 27
31 #include <unistd.h> 28 #include <unistd.h>
32 #include <sys/types.h> 29 #include <sys/types.h>
33 #include <sys/stat.h> 30 #include <sys/stat.h>
42 #include <libxml/tree.h> 39 #include <libxml/tree.h>
43 #include <libxml/parser.h> 40 #include <libxml/parser.h>
44 #include <libxml/xmlreader.h> 41 #include <libxml/xmlreader.h>
45 #include <libxml/xpath.h> 42 #include <libxml/xpath.h>
46 #include <libxml/xpathInternals.h> 43 #include <libxml/xpathInternals.h>
47 44 #include <libxml/uri.h>
48 #include "urlencode.h" 45
49 #include "base64.h" 46 #define XSPF_ROOT_NODE_NAME "playlist"
47 #define XSPF_XMLNS "http://xspf.org/ns/0/"
50 48
51 #define TMP_BUF_LEN 128 49 #define TMP_BUF_LEN 128
50
51 gchar *base = NULL;
52 gboolean override_mtime = FALSE;
52 53
53 static void 54 static void
54 add_file(xmlNode *track, const gchar *filename, gint pos) 55 add_file(xmlNode *track, const gchar *filename, gint pos)
55 { 56 {
56 xmlNode *nptr; 57 xmlNode *nptr;
57 TitleInput *tuple; 58 TitleInput *tuple;
58 gchar *location = NULL, *b64filename = NULL, *locale_uri = NULL; 59 gchar *location = NULL;
59 Playlist *playlist = playlist_get_active(); 60 Playlist *playlist = playlist_get_active();
60 61
61 tuple = bmp_title_input_new(); 62 tuple = bmp_title_input_new();
63
64 // staticlist hack
65 tuple->mtime = -1; // mark as uninitialized.
62 66
63 // creator, album, title, duration, trackNum, annotation, image, 67 // creator, album, title, duration, trackNum, annotation, image,
64 for(nptr = track->children; nptr != NULL; nptr = nptr->next){ 68 for(nptr = track->children; nptr != NULL; nptr = nptr->next){
65 if(nptr->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr->name, "location")){ 69 if(nptr->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr->name, (xmlChar *)"location")){
66 GError *err = NULL; 70 gchar *str = (gchar *)xmlNodeGetContent(nptr);
67 gchar *tmp = NULL, *tmp2 = NULL; 71 gchar *tmp = (gchar *)xmlURIUnescapeString(str, -1, NULL);
68 xmlChar *str = xmlNodeGetContent(nptr); 72
69 tmp = g_locale_from_utf8(str, -1, NULL, NULL, &err); //for backward compatibility 73 if(strstr(tmp, "file://")){
70 if(err != NULL) { 74 location = g_strdup_printf("%s%s", base ? base : "", tmp+7);
71 tmp2 = xspf_url_decode(str);
72 } 75 }
73 else { 76 else {
74 tmp2= xspf_url_decode(tmp); 77 location = g_strdup_printf("%s%s", base ? base : "", tmp);
75 } 78 }
76 if(strstr(tmp2, "file://")){ 79 xmlFree(str); g_free(tmp);
77 location = g_strdup(tmp2+7); 80 }
78 } 81 else if(nptr->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr->name, (xmlChar *)"title")){
79 else {
80 location = g_strdup(tmp2);
81 }
82 xmlFree(str);
83 g_free(tmp); g_free(err); g_free(tmp2);
84 }
85 else if(nptr->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr->name, "title")){
86 tuple->track_name = (gchar *)xmlNodeGetContent(nptr); 82 tuple->track_name = (gchar *)xmlNodeGetContent(nptr);
87 } 83 }
88 else if(nptr->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr->name, "creator")){ 84 else if(nptr->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr->name, (xmlChar *)"creator")){
89 tuple->performer = (gchar *)xmlNodeGetContent(nptr); 85 tuple->performer = (gchar *)xmlNodeGetContent(nptr);
90 } 86 }
91 else if(nptr->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr->name, "annotation")){ 87 else if(nptr->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr->name, (xmlChar *)"annotation")){
92 tuple->comment = (gchar *)xmlNodeGetContent(nptr); 88 tuple->comment = (gchar *)xmlNodeGetContent(nptr);
93 continue; 89 continue;
94 } 90 }
95 else if(nptr->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr->name, "album")){ 91 else if(nptr->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr->name, (xmlChar *)"album")){
96 tuple->album_name = (gchar *)xmlNodeGetContent(nptr); 92 tuple->album_name = (gchar *)xmlNodeGetContent(nptr);
97 } 93 }
98 else if(nptr->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr->name, "trackNum")){ 94 else if(nptr->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr->name, (xmlChar *)"trackNum")){
99 xmlChar *str = xmlNodeGetContent(nptr); 95 xmlChar *str = xmlNodeGetContent(nptr);
100 tuple->track_number = atol(str); 96 tuple->track_number = atol((char *)str);
101 xmlFree(str); 97 xmlFree(str);
102 } 98 }
103 else if(nptr->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr->name, "duration")){ 99 else if(nptr->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr->name, (xmlChar *)"duration")){
104 xmlChar *str = xmlNodeGetContent(nptr); 100 xmlChar *str = xmlNodeGetContent(nptr);
105 tuple->length = atol(str); 101 tuple->length = atol((char *)str);
106 xmlFree(str); 102 xmlFree(str);
107 } 103 }
108 104
109 // 105 //
110 // additional metadata 106 // additional metadata
111 // 107 //
112 // year, date, genre, formatter, mtime, b64filename 108 // year, date, genre, formatter, mtime
113 // 109 //
114 else if(nptr->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr->name, "meta")){ 110 else if(nptr->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr->name, (xmlChar *)"meta")){
115 xmlChar *rel = NULL; 111 xmlChar *rel = NULL;
116 112
117 rel = xmlGetProp(nptr, "rel"); 113 rel = xmlGetProp(nptr, (xmlChar *)"rel");
118 114
119 if(!xmlStrcmp(rel, "year")){ 115 if(!xmlStrcmp(rel, (xmlChar *)"year")){
120 xmlChar *cont = xmlNodeGetContent(nptr); 116 xmlChar *cont = xmlNodeGetContent(nptr);
121 tuple->year = atol(cont); 117 tuple->year = atol((char *)cont);
122 xmlFree(cont); 118 xmlFree(cont);
123 continue; 119 continue;
124 } 120 }
125 else if(!xmlStrcmp(rel, "date")){ 121 else if(!xmlStrcmp(rel, (xmlChar *)"date")){
126 tuple->date = (gchar *)xmlNodeGetContent(nptr); 122 tuple->date = (gchar *)xmlNodeGetContent(nptr);
127 continue; 123 continue;
128 } 124 }
129 else if(!xmlStrcmp(rel, "genre")){ 125 else if(!xmlStrcmp(rel, (xmlChar *)"genre")){
130 tuple->genre = (gchar *)xmlNodeGetContent(nptr); 126 tuple->genre = (gchar *)xmlNodeGetContent(nptr);
131 continue; 127 continue;
132 } 128 }
133 else if(!xmlStrcmp(rel, "formatter")){ 129 else if(!xmlStrcmp(rel, (xmlChar *)"formatter")){
134 tuple->formatter = (gchar *)xmlNodeGetContent(nptr); 130 tuple->formatter = (gchar *)xmlNodeGetContent(nptr);
135 continue; 131 continue;
136 } 132 }
137 else if(!xmlStrcmp(rel, "mtime")){ 133 else if(!xmlStrcmp(rel, (xmlChar *)"mtime")){
138 xmlChar *str = NULL; 134 xmlChar *str = NULL;
139 str = xmlNodeGetContent(nptr); 135 str = xmlNodeGetContent(nptr);
140 tuple->mtime = (time_t)atoll(str); 136 tuple->mtime = (time_t)atoll((char *)str);
141 xmlFree(str); 137 xmlFree(str);
142 continue; 138 continue;
143 } 139 }
144 else if(!xmlStrcmp(rel, "b64filename")){ //for backward compatibility
145 gchar *b64str = NULL;
146 b64str = (gchar *)xmlNodeGetContent(nptr);
147 b64filename = g_malloc0(strlen(b64str)*3/4+1);
148 from64tobits(b64filename, b64str);
149 g_free(b64str);
150 continue;
151 }
152 xmlFree(rel); 140 xmlFree(rel);
153 rel = NULL; 141 rel = NULL;
154 } 142 }
155 143
156 } 144 }
157 145
158 if (tuple->length == 0) { 146 if (tuple->length == 0) {
159 tuple->length = -1; 147 tuple->length = -1;
160 } 148 }
161 149
162 if (tuple->mtime == 0) { 150 if(override_mtime) {
163 tuple->mtime = -1; 151 tuple->mtime = 0; //when mtime=0, scanning will be skipped. --yaz
164 } 152 }
165 153
166 locale_uri = b64filename ? b64filename : location; 154 if(location){
167 155 tuple->file_name = g_path_get_basename(location);
168 if(locale_uri){ 156 tuple->file_path = g_path_get_dirname(location);
169 tuple->file_name = g_path_get_basename(locale_uri); 157 tuple->file_ext = g_strdup(strrchr(location, '.'));
170 tuple->file_path = g_path_get_dirname(locale_uri);
171 tuple->file_ext = g_strdup(strrchr(locale_uri, '.'));
172 // add file to playlist 158 // add file to playlist
173 playlist_load_ins_file_tuple(playlist, locale_uri, filename, pos, tuple); 159 playlist_load_ins_file_tuple(playlist, location, filename, pos, tuple);
174 pos++; 160 pos++;
175 } 161 }
176 162
177 g_free(location); g_free(b64filename); 163 g_free(location);
178 locale_uri = NULL; location = NULL; b64filename = NULL; 164 location = NULL;
179 } 165 }
180 166
181 static void 167 static void
182 find_track(xmlNode *tracklist, const gchar *filename, gint pos) 168 find_track(xmlNode *tracklist, const gchar *filename, gint pos)
183 { 169 {
184 xmlNode *nptr; 170 xmlNode *nptr;
185 for(nptr = tracklist->children; nptr != NULL; nptr = nptr->next){ 171 for(nptr = tracklist->children; nptr != NULL; nptr = nptr->next){
186 if(nptr->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr->name, "track")){ 172 if(nptr->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr->name, (xmlChar *)"track")){
187 add_file(nptr, filename, pos); 173 add_file(nptr, filename, pos);
174 }
175 }
176 }
177
178 static void
179 find_audoptions(xmlNode *tracklist, const gchar *filename, gint pos)
180 {
181 xmlNode *nptr;
182 for(nptr = tracklist->children; nptr != NULL; nptr = nptr->next){
183 if(nptr->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr->name, (xmlChar *)"options")){
184 xmlChar *opt = NULL;
185
186 opt = xmlGetProp(nptr, (xmlChar *)"staticlist");
187 if(!strcasecmp((char *)opt, "true")){
188 override_mtime = TRUE;
189 }
190 xmlFree(opt);
191 opt = NULL;
188 } 192 }
189 } 193 }
190 } 194 }
191 195
192 static void 196 static void
199 203
200 doc = xmlParseFile(filename); 204 doc = xmlParseFile(filename);
201 if (doc == NULL) 205 if (doc == NULL)
202 return; 206 return;
203 207
208 xmlFree(base);
209 base = NULL;
210
204 // find trackList 211 // find trackList
205 for(nptr = doc->children; nptr != NULL; nptr = nptr->next) { 212 for(nptr = doc->children; nptr != NULL; nptr = nptr->next) {
206 if(nptr->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr->name, "playlist")){ 213 if(nptr->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr->name, (xmlChar *)"playlist")){
207 for(nptr2 = nptr->children; nptr2 != NULL; nptr2 = nptr2->next){ 214 base = (gchar *)xmlNodeGetBase(doc, nptr);
208 if(nptr2->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr2->name, "trackList")){ 215 if(!strcmp(base, filename)) {
216 xmlFree(base);
217 base = NULL;
218 }
219
220 for(nptr2 = nptr->children; nptr2 != NULL; nptr2 = nptr2->next){
221
222 if(nptr2->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr2->name, (xmlChar *)"extension")){
223 //check if application is audacious
224 xmlChar *app = NULL;
225 app = xmlGetProp(nptr2, (xmlChar *)"application");
226 if(!xmlStrcmp(app, (xmlChar *)"audacious")) {
227 find_audoptions(nptr2, filename, pos);
228 }
229 xmlFree(app);
230 }
231
232 if(nptr2->type == XML_ELEMENT_NODE && !xmlStrcmp(nptr2->name, (xmlChar *)"trackList")){
209 find_track(nptr2, filename, pos); 233 find_track(nptr2, filename, pos);
210 } 234 }
235
211 } 236 }
212 237
213 } 238 }
214 } 239 }
215 240
216 xmlFreeDoc(doc); 241 xmlFreeDoc(doc);
217 242
218 } 243 }
219
220
221 #define XSPF_ROOT_NODE_NAME "playlist"
222 #define XSPF_XMLNS "http://xspf.org/ns/0/"
223 244
224 static void 245 static void
225 playlist_save_xspf(const gchar *filename, gint pos) 246 playlist_save_xspf(const gchar *filename, gint pos)
226 { 247 {
227 xmlDocPtr doc; 248 xmlDocPtr doc;
228 xmlNodePtr rootnode, tmp, tracklist; 249 xmlNodePtr rootnode, tmp, tracklist;
229 GList *node; 250 GList *node;
230 Playlist *playlist = playlist_get_active(); 251 Playlist *playlist = playlist_get_active();
231 252
232 doc = xmlNewDoc("1.0"); 253 doc = xmlNewDoc((xmlChar *)"1.0");
233 254
234 doc->charset = XML_CHAR_ENCODING_UTF8; 255 doc->charset = XML_CHAR_ENCODING_UTF8;
235 doc->encoding = xmlStrdup("UTF-8"); 256 doc->encoding = xmlStrdup((xmlChar *)"UTF-8");
236 257
237 rootnode = xmlNewNode(NULL, XSPF_ROOT_NODE_NAME); 258 rootnode = xmlNewNode(NULL, (xmlChar *)XSPF_ROOT_NODE_NAME);
238 xmlSetProp(rootnode, "xmlns", XSPF_XMLNS); 259 xmlSetProp(rootnode, (xmlChar *)"xmlns", (xmlChar *)XSPF_XMLNS);
239 xmlSetProp(rootnode, "version", "1"); 260 xmlSetProp(rootnode, (xmlChar *)"version", (xmlChar *)"1");
240 xmlDocSetRootElement(doc, rootnode); 261 xmlDocSetRootElement(doc, rootnode);
241 262
242 tmp = xmlNewNode(NULL, "creator"); 263 tmp = xmlNewNode(NULL, (xmlChar *)"creator");
243 xmlAddChild(tmp, xmlNewText(PACKAGE "-" VERSION)); 264 xmlAddChild(tmp, xmlNewText((xmlChar *)PACKAGE "-" VERSION));
244 xmlAddChild(rootnode, tmp); 265 xmlAddChild(rootnode, tmp);
245 266
246 tracklist = xmlNewNode(NULL, "trackList"); 267 tracklist = xmlNewNode(NULL, (xmlChar *)"trackList");
247 xmlAddChild(rootnode, tracklist); 268 xmlAddChild(rootnode, tracklist);
248 269
249 PLAYLIST_LOCK(playlist->mutex); 270 PLAYLIST_LOCK(playlist->mutex);
250 271
251 for (node = playlist->entries; node != NULL; node = g_list_next(node)) 272 for (node = playlist->entries; node != NULL; node = g_list_next(node))
252 { 273 {
253 PlaylistEntry *entry = PLAYLIST_ENTRY(node->data); 274 PlaylistEntry *entry = PLAYLIST_ENTRY(node->data);
254 xmlNodePtr track, location; 275 xmlNodePtr track, location;
255 gchar *filename = NULL; 276 gchar *filename = NULL;
256 277
257 track = xmlNewNode(NULL, "track"); 278 track = xmlNewNode(NULL, (xmlChar *)"track");
258 location = xmlNewNode(NULL, "location"); 279 location = xmlNewNode(NULL, (xmlChar *)"location");
259 280
260 /* url encode file name. exclude streaming for now. */ 281 /* url encode file name. exclude streaming for now. */
261 if (strncasecmp("http://", entry->filename, 7) && 282 if (strncasecmp("http://", entry->filename, 7) &&
262 strncasecmp("https://", entry->filename, 8)) { /* the rest */ 283 strncasecmp("https://", entry->filename, 8)) { /* the rest */
263 gchar *tmp = (gchar *)xspf_url_encode(entry->filename); 284 gchar *tmp = (gchar *)xmlPathToURI((const xmlChar *)entry->filename);
285 printf("xmlPathToURI = %s\n", tmp);
264 filename = g_strdup_printf("file://%s", tmp); 286 filename = g_strdup_printf("file://%s", tmp);
265 g_free(tmp); 287 g_free(tmp);
266 } 288 }
267 else { /* streaming */ 289 else { /* streaming */
268 filename = strdup(entry->filename); 290 filename = (gchar *)xmlURIEscape((xmlChar *)entry->filename);
269 } 291 }
270 292
271 if(!g_utf8_validate(filename, -1, NULL)) 293 if(!g_utf8_validate(filename, -1, NULL))
272 continue; 294 continue;
273 295
274 xmlAddChild(location, xmlNewText(filename)); 296 xmlAddChild(location, xmlNewText((xmlChar *)filename));
275 xmlAddChild(track, location); 297 xmlAddChild(track, location);
276 xmlAddChild(tracklist, track); 298 xmlAddChild(tracklist, track);
277 299
278 /* do we have a tuple? */ 300 /* do we have a tuple? */
279 if (entry->tuple != NULL) 301 if (entry->tuple != NULL)
280 { 302 {
281 if (entry->tuple->track_name != NULL && 303 if (entry->tuple->track_name != NULL &&
282 g_utf8_validate(entry->tuple->track_name, -1, NULL)) 304 g_utf8_validate(entry->tuple->track_name, -1, NULL))
283 { 305 {
284 tmp = xmlNewNode(NULL, "title"); 306 tmp = xmlNewNode(NULL, (xmlChar *)"title");
285 xmlAddChild(tmp, xmlNewText(entry->tuple->track_name)); 307 xmlAddChild(tmp, xmlNewText((xmlChar *)entry->tuple->track_name));
286 xmlAddChild(track, tmp); 308 xmlAddChild(track, tmp);
287 } 309 }
288 310
289 if (entry->tuple->performer != NULL && 311 if (entry->tuple->performer != NULL &&
290 g_utf8_validate(entry->tuple->performer, -1, NULL)) 312 g_utf8_validate(entry->tuple->performer, -1, NULL))
291 { 313 {
292 tmp = xmlNewNode(NULL, "creator"); 314 tmp = xmlNewNode(NULL, (xmlChar *)"creator");
293 xmlAddChild(tmp, xmlNewText(entry->tuple->performer)); 315 xmlAddChild(tmp, xmlNewText((xmlChar *)entry->tuple->performer));
294 xmlAddChild(track, tmp); 316 xmlAddChild(track, tmp);
295 } 317 }
296 318
297 if (entry->tuple->comment != NULL && 319 if (entry->tuple->comment != NULL &&
298 g_utf8_validate(entry->tuple->comment, -1, NULL)) 320 g_utf8_validate(entry->tuple->comment, -1, NULL))
299 { 321 {
300 tmp = xmlNewNode(NULL, "annotation"); 322 tmp = xmlNewNode(NULL, (xmlChar *)"annotation");
301 xmlAddChild(tmp, xmlNewText(entry->tuple->comment)); 323 xmlAddChild(tmp, xmlNewText((xmlChar *)entry->tuple->comment));
302 xmlAddChild(track, tmp); 324 xmlAddChild(track, tmp);
303 } 325 }
304 326
305 if (entry->tuple->album_name != NULL && 327 if (entry->tuple->album_name != NULL &&
306 g_utf8_validate(entry->tuple->album_name, -1, NULL)) 328 g_utf8_validate(entry->tuple->album_name, -1, NULL))
307 { 329 {
308 tmp = xmlNewNode(NULL, "album"); 330 tmp = xmlNewNode(NULL, (xmlChar *)"album");
309 xmlAddChild(tmp, xmlNewText(entry->tuple->album_name)); 331 xmlAddChild(tmp, xmlNewText((xmlChar *)entry->tuple->album_name));
310 xmlAddChild(track, tmp); 332 xmlAddChild(track, tmp);
311 } 333 }
312 334
313 if (entry->tuple->track_number != 0) 335 if (entry->tuple->track_number != 0)
314 { 336 {
315 gchar *str; 337 gchar *str;
316 str = g_malloc(TMP_BUF_LEN); 338 str = g_malloc(TMP_BUF_LEN);
317 tmp = xmlNewNode(NULL, "trackNum"); 339 tmp = xmlNewNode(NULL, (xmlChar *)"trackNum");
318 sprintf(str, "%d", entry->tuple->track_number); 340 sprintf(str, "%d", entry->tuple->track_number);
319 xmlAddChild(tmp, xmlNewText(str)); 341 xmlAddChild(tmp, xmlNewText((xmlChar *)str));
320 g_free(str); 342 g_free(str);
321 xmlAddChild(track, tmp); 343 xmlAddChild(track, tmp);
322 } 344 }
323 345
324 if (entry->tuple->length > 0) 346 if (entry->tuple->length > 0)
325 { 347 {
326 gchar *str; 348 gchar *str;
327 str = g_malloc(TMP_BUF_LEN); 349 str = g_malloc(TMP_BUF_LEN);
328 tmp = xmlNewNode(NULL, "duration"); 350 tmp = xmlNewNode(NULL, (xmlChar *)"duration");
329 sprintf(str, "%d", entry->tuple->length); 351 sprintf(str, "%d", entry->tuple->length);
330 xmlAddChild(tmp, xmlNewText(str)); 352 xmlAddChild(tmp, xmlNewText((xmlChar *)str));
331 g_free(str); 353 g_free(str);
332 xmlAddChild(track, tmp); 354 xmlAddChild(track, tmp);
333 } 355 }
334 356
335 // 357 //
339 // 361 //
340 if (entry->tuple->year != 0) 362 if (entry->tuple->year != 0)
341 { 363 {
342 gchar *str; 364 gchar *str;
343 str = g_malloc(TMP_BUF_LEN); 365 str = g_malloc(TMP_BUF_LEN);
344 tmp = xmlNewNode(NULL, "meta"); 366 tmp = xmlNewNode(NULL, (xmlChar *)"meta");
345 xmlSetProp(tmp, "rel", "year"); 367 xmlSetProp(tmp, (xmlChar *)"rel", (xmlChar *)"year");
346 sprintf(str, "%d", entry->tuple->year); 368 sprintf(str, "%d", entry->tuple->year);
347 xmlAddChild(tmp, xmlNewText(str)); 369 xmlAddChild(tmp, xmlNewText((xmlChar *)str));
348 xmlAddChild(track, tmp); 370 xmlAddChild(track, tmp);
349 g_free(str); 371 g_free(str);
350 } 372 }
351 373
352 if (entry->tuple->date != NULL && 374 if (entry->tuple->date != NULL &&
353 g_utf8_validate(entry->tuple->date, -1, NULL)) 375 g_utf8_validate(entry->tuple->date, -1, NULL))
354 { 376 {
355 tmp = xmlNewNode(NULL, "meta"); 377 tmp = xmlNewNode(NULL, (xmlChar *)"meta");
356 xmlSetProp(tmp, "rel", "date"); 378 xmlSetProp(tmp, (xmlChar *)"rel", (xmlChar *)"date");
357 xmlAddChild(tmp, xmlNewText(entry->tuple->date)); 379 xmlAddChild(tmp, xmlNewText((xmlChar *)entry->tuple->date));
358 xmlAddChild(track, tmp); 380 xmlAddChild(track, tmp);
359 } 381 }
360 382
361 if (entry->tuple->genre != NULL && 383 if (entry->tuple->genre != NULL &&
362 g_utf8_validate(entry->tuple->genre, -1, NULL)) 384 g_utf8_validate(entry->tuple->genre, -1, NULL))
363 { 385 {
364 tmp = xmlNewNode(NULL, "meta"); 386 tmp = xmlNewNode(NULL, (xmlChar *)"meta");
365 xmlSetProp(tmp, "rel", "genre"); 387 xmlSetProp(tmp, (xmlChar *)"rel", (xmlChar *)"genre");
366 xmlAddChild(tmp, xmlNewText(entry->tuple->genre)); 388 xmlAddChild(tmp, xmlNewText((xmlChar *)entry->tuple->genre));
367 xmlAddChild(track, tmp); 389 xmlAddChild(track, tmp);
368 } 390 }
369 391
370 if (entry->tuple->formatter != NULL && 392 if (entry->tuple->formatter != NULL &&
371 g_utf8_validate(entry->tuple->formatter, -1, NULL)) 393 g_utf8_validate(entry->tuple->formatter, -1, NULL))
372 { 394 {
373 tmp = xmlNewNode(NULL, "meta"); 395 tmp = xmlNewNode(NULL, (xmlChar *)"meta");
374 xmlSetProp(tmp, "rel", "formatter"); 396 xmlSetProp(tmp, (xmlChar *)"rel", (xmlChar *)"formatter");
375 xmlAddChild(tmp, xmlNewText(entry->tuple->formatter)); 397 xmlAddChild(tmp, xmlNewText((xmlChar *)entry->tuple->formatter));
376 xmlAddChild(track, tmp); 398 xmlAddChild(track, tmp);
377 } 399 }
378 400
379 if (entry->tuple->mtime) { 401 // mtime: write mtime unconditionally for staticlist hack.
402 // if (entry->tuple->mtime) {
380 gchar *str; 403 gchar *str;
381 str = g_malloc(TMP_BUF_LEN); 404 str = g_malloc(TMP_BUF_LEN);
382 tmp = xmlNewNode(NULL, "meta"); 405 tmp = xmlNewNode(NULL, (xmlChar *)"meta");
383 xmlSetProp(tmp, "rel", "mtime"); 406 xmlSetProp(tmp, (xmlChar *)"rel", (xmlChar *)"mtime");
384 sprintf(str, "%ld", (long) entry->tuple->mtime); 407 sprintf(str, "%ld", (long) entry->tuple->mtime);
385 xmlAddChild(tmp, xmlNewText(str)); 408 xmlAddChild(tmp, xmlNewText((xmlChar *)str));
386 xmlAddChild(track, tmp); 409 xmlAddChild(track, tmp);
387 g_free(str); 410 g_free(str);
388 } 411 // }
389 412
390 } 413 }
391 g_free(filename); 414 g_free(filename);
392 filename = NULL; 415 filename = NULL;
393 } 416 }