comparison src/xspf/xspf.c @ 892:d0b558bcf704 trunk

[svn] - initial support for writing relative path.
author yaz
date Thu, 22 Mar 2007 22:19:41 -0700
parents 502222ed5dd4
children 40c1ffbe34d0
comparison
equal deleted inserted replaced
891:502222ed5dd4 892:d0b558bcf704
48 48
49 #define TMP_BUF_LEN 128 49 #define TMP_BUF_LEN 128
50 50
51 gchar *base = NULL; 51 gchar *base = NULL;
52 52
53 static gboolean is_uri(gchar *uri)
54 {
55 if(strstr(uri, "://"))
56 return TRUE;
57 else
58 return FALSE;
59 }
60
61 static gboolean is_remote(gchar *uri)
62 {
63 if(strstr(uri, "file://"))
64 return FALSE;
65
66 if(strstr(uri, "://"))
67 return TRUE;
68 else
69 return FALSE;
70 }
71
53 // this function is taken from libxml2-2.6.27. 72 // this function is taken from libxml2-2.6.27.
54 static xmlChar *audPathToURI(const xmlChar *path) 73 static xmlChar *audPathToURI(const xmlChar *path)
55 { 74 {
56 xmlURIPtr uri; 75 xmlURIPtr uri;
57 xmlURI temp; 76 xmlURI temp;
58 xmlChar *ret, *cal; 77 xmlChar *ret, *cal;
59 78
60 if(path == NULL) 79 if(path == NULL)
61 return (NULL); 80 return NULL;
62 81
63 if((uri = xmlParseURI((const char *)path)) != NULL) { 82 if((uri = xmlParseURI((const char *)path)) != NULL) {
64 xmlFreeURI(uri); 83 xmlFreeURI(uri);
65 return xmlStrdup(path); 84 return xmlStrdup(path);
66 } 85 }
67 cal = xmlCanonicPath(path); 86 cal = xmlCanonicPath(path);
68 if(cal == NULL) 87 if(cal == NULL)
69 return (NULL); 88 return NULL;
70 memset(&temp, 0, sizeof(temp)); 89 memset(&temp, 0, sizeof(temp));
71 temp.path = (char *)cal; 90 temp.path = (char *)cal;
72 ret = xmlSaveUri(&temp); 91 ret = xmlSaveUri(&temp);
73 xmlFree(cal); 92 xmlFree(cal);
74 return (ret); 93 return ret;
75 } 94 }
76 95
77 static void add_file(xmlNode *track, const gchar *filename, gint pos) 96 static void add_file(xmlNode *track, const gchar *filename, gint pos)
78 { 97 {
79 xmlNode *nptr; 98 xmlNode *nptr;
91 if(nptr->type == XML_ELEMENT_NODE 110 if(nptr->type == XML_ELEMENT_NODE
92 && !xmlStrcmp(nptr->name, (xmlChar *)"location")) { 111 && !xmlStrcmp(nptr->name, (xmlChar *)"location")) {
93 gchar *str = (gchar *)xmlNodeGetContent(nptr); 112 gchar *str = (gchar *)xmlNodeGetContent(nptr);
94 gchar *tmp; 113 gchar *tmp;
95 114
96 if(strncmp(str, "http://", 7) && strncmp(str, "https://", 8) && strncmp(str, "mms://", 6)) { /* not a streaming */ 115 if(!is_remote(str)) { /* local file */
97 tmp = (gchar *)xmlURIUnescapeString(str, -1, NULL); 116 tmp = (gchar *)xmlURIUnescapeString(str, -1, NULL);
98 } 117 }
99 else { /* streaming */ 118 else { /* streaming */
100 tmp = g_strdup(str); 119 tmp = g_strdup(str);
101 } 120 }
243 // find trackList 262 // find trackList
244 for(nptr = doc->children; nptr != NULL; nptr = nptr->next) { 263 for(nptr = doc->children; nptr != NULL; nptr = nptr->next) {
245 if(nptr->type == XML_ELEMENT_NODE 264 if(nptr->type == XML_ELEMENT_NODE
246 && !xmlStrcmp(nptr->name, (xmlChar *)"playlist")) { 265 && !xmlStrcmp(nptr->name, (xmlChar *)"playlist")) {
247 base = (gchar *)xmlNodeGetBase(doc, nptr); 266 base = (gchar *)xmlNodeGetBase(doc, nptr);
248 if(!strcmp(base, filename)) { 267 #ifdef DEBUG
268 printf("load: base = %s\n", base);
269 #endif
270 {
271 gchar *tmp = xmlURIUnescapeString(base, -1, NULL);
272 if(tmp) {
273 if(strstr(tmp, "file://")) {
274 gchar *tmp2 = g_strdup(tmp + 7);
275 g_free(tmp);
276 tmp = tmp2;
277 }
278 g_free(base);
279 base = tmp;
280 }
281 }
282
283 if(!strcmp(base, filename)) { // filename is specified as a base URI. ignore.
249 xmlFree(base); 284 xmlFree(base);
250 base = NULL; 285 base = NULL;
251 } 286 }
252 287
253 for(nptr2 = nptr->children; nptr2 != NULL; nptr2 = nptr2->next) { 288 for(nptr2 = nptr->children; nptr2 != NULL; nptr2 = nptr2->next) {
265 300
266 if(nptr2->type == XML_ELEMENT_NODE 301 if(nptr2->type == XML_ELEMENT_NODE
267 && !xmlStrcmp(nptr2->name, (xmlChar *)"trackList")) { 302 && !xmlStrcmp(nptr2->name, (xmlChar *)"trackList")) {
268 find_track(nptr2, filename, pos); 303 find_track(nptr2, filename, pos);
269 } 304 }
270 305 }
271 } 306 }
272 307 }
273 }
274 }
275
276 xmlFreeDoc(doc); 308 xmlFreeDoc(doc);
277
278 } 309 }
279 310
280 static void playlist_save_xspf(const gchar *filename, gint pos) 311 static void playlist_save_xspf(const gchar *filename, gint pos)
281 { 312 {
282 xmlDocPtr doc; 313 xmlDocPtr doc;
283 xmlNodePtr rootnode, tmp, tracklist; 314 xmlNodePtr rootnode, tmp, tracklist;
284 GList *node; 315 GList *node;
316 gint baselen = 0;
285 Playlist *playlist = playlist_get_active(); 317 Playlist *playlist = playlist_get_active();
318
319 xmlFree(base);
320 base = NULL;
286 321
287 doc = xmlNewDoc((xmlChar *)"1.0"); 322 doc = xmlNewDoc((xmlChar *)"1.0");
288 323
289 doc->charset = XML_CHAR_ENCODING_UTF8; 324 doc->charset = XML_CHAR_ENCODING_UTF8;
290 doc->encoding = xmlStrdup((xmlChar *)"UTF-8"); 325 doc->encoding = xmlStrdup((xmlChar *)"UTF-8");
291 326
292 rootnode = xmlNewNode(NULL, (xmlChar *)XSPF_ROOT_NODE_NAME); 327 rootnode = xmlNewNode(NULL, (xmlChar *)XSPF_ROOT_NODE_NAME);
328 xmlSetProp(rootnode, (xmlChar *)"version", (xmlChar *)"1");
293 xmlSetProp(rootnode, (xmlChar *)"xmlns", (xmlChar *)XSPF_XMLNS); 329 xmlSetProp(rootnode, (xmlChar *)"xmlns", (xmlChar *)XSPF_XMLNS);
294 xmlSetProp(rootnode, (xmlChar *)"version", (xmlChar *)"1"); 330
331 PLAYLIST_LOCK(playlist->mutex);
332 if(playlist->attribute & PLAYLIST_USE_RELATIVE) {
333 /* prescan to determine base uri */
334 for(node = playlist->entries; node != NULL; node = g_list_next(node)) {
335 gchar *ptr1, *ptr2;
336 PlaylistEntry *entry = PLAYLIST_ENTRY(node->data);
337 gchar *tmp;
338 gint tmplen = 0;
339
340 if(!is_uri(entry->filename)) {
341 gchar *tmp2;
342 tmp2 = g_path_get_dirname(entry->filename);
343 tmp = g_strdup_printf("%s/", tmp2);
344 g_free(tmp2);
345 tmp2 = NULL;
346 }
347 else {
348 tmp = g_strdup(entry->filename);
349 }
350
351 if(!base) {
352 base = strdup(tmp);
353 baselen = strlen(base);
354 }
355 ptr1 = base;
356 ptr2 = tmp;
357
358 while(ptr1 && ptr2 && *ptr1 && *ptr2 && *ptr1 == *ptr2) {
359 ptr1++;
360 ptr2++;
361 }
362 *ptr2 = '\0'; //terminate
363 tmplen = ptr2 - tmp;
364
365 if(tmplen <= baselen) {
366 g_free(base);
367 base = tmp;
368 baselen = tmplen;
369 #ifdef DEBUG
370 printf("base = \"%s\" baselen = %d\n", base, baselen);
371 #endif
372 }
373 else {
374 g_free(tmp);
375 tmp = NULL;
376 }
377 }
378 /* set base URI */
379 if(base) {
380 gchar *tmp;
381 if(!is_uri(base)) {
382 tmp = (gchar *)audPathToURI((xmlChar *)base);
383 if(tmp) {
384 g_free(base);
385 base = tmp;
386 }
387 }
388 // xmlNodeSetBase(rootnode, base); // it blindly escapes characters.
389
390 if(!is_uri(base)) {
391 tmp = g_strdup_printf("file://%s", base);
392 xmlSetProp(rootnode, (xmlChar *)"xml:base", (xmlChar *)tmp);
393 g_free(tmp);
394 tmp = NULL;
395 }
396 else
397 xmlSetProp(rootnode, (xmlChar *)"xml:base", (xmlChar *)base);
398 }
399 } /* USE_RELATIVE */
400
401
295 xmlDocSetRootElement(doc, rootnode); 402 xmlDocSetRootElement(doc, rootnode);
296 403
297 tmp = xmlNewNode(NULL, (xmlChar *)"creator"); 404 tmp = xmlNewNode(NULL, (xmlChar *)"creator");
298 xmlAddChild(tmp, xmlNewText((xmlChar *)PACKAGE "-" VERSION)); 405 xmlAddChild(tmp, xmlNewText((xmlChar *)PACKAGE "-" VERSION));
299 xmlAddChild(rootnode, tmp); 406 xmlAddChild(rootnode, tmp);
313 } 420 }
314 421
315 tracklist = xmlNewNode(NULL, (xmlChar *)"trackList"); 422 tracklist = xmlNewNode(NULL, (xmlChar *)"trackList");
316 xmlAddChild(rootnode, tracklist); 423 xmlAddChild(rootnode, tracklist);
317 424
318 PLAYLIST_LOCK(playlist->mutex); 425 // PLAYLIST_LOCK(playlist->mutex);
319 426
320 for(node = playlist->entries; node != NULL; node = g_list_next(node)) { 427 for(node = playlist->entries; node != NULL; node = g_list_next(node)) {
321 PlaylistEntry *entry = PLAYLIST_ENTRY(node->data); 428 PlaylistEntry *entry = PLAYLIST_ENTRY(node->data);
322 xmlNodePtr track, location; 429 xmlNodePtr track, location;
323 gchar *filename = NULL; 430 gchar *filename = NULL;
324 431
325 track = xmlNewNode(NULL, (xmlChar *)"track"); 432 track = xmlNewNode(NULL, (xmlChar *)"track");
326 location = xmlNewNode(NULL, (xmlChar *)"location"); 433 location = xmlNewNode(NULL, (xmlChar *)"location");
327 434
328 /* uri escape entry->filename */ 435 if(is_uri(entry->filename)) { /* remote uri */
329 if(!strncasecmp("http://", entry->filename, 7) || !strncasecmp("https://", entry->filename, 8) || !strncasecmp("mms://", entry->filename, 6)) { /* streaming */ 436 gchar *tmp = NULL;
330 gchar *tmp = (gchar *)xmlURIEscape((xmlChar *)entry->filename); 437 #ifdef DEBUG
331 filename = g_strdup(tmp ? tmp : entry->filename); 438 printf("filename is uri\n");
439 #endif
440 tmp = (gchar *)xmlURIEscape((xmlChar *)entry->filename);
441 filename = g_strdup(entry->filename + baselen);
332 g_free(tmp); 442 g_free(tmp);
443 tmp = NULL;
333 } 444 }
334 else { /* local file */ 445 else { /* local file */
335 446 gchar *tmp =
336 gchar *tmp = (gchar *)audPathToURI((const xmlChar *)entry->filename); 447 (gchar *)audPathToURI((const xmlChar *)entry->filename + baselen);
337 filename = g_strdup_printf("file://%s", tmp); 448 if(base) {
449 filename = g_strdup_printf("%s", tmp);
450 }
451 else {
452 #ifdef DEBUG
453 printf("absolule, local\n");
454 #endif
455 filename = g_strdup_printf("file://%s", tmp);
456 }
338 g_free(tmp); 457 g_free(tmp);
458 tmp = NULL;
339 } 459 }
340 460
341 if(!g_utf8_validate(filename, -1, NULL)) 461 if(!g_utf8_validate(filename, -1, NULL))
342 continue; 462 continue;
343 463
345 xmlAddChild(track, location); 465 xmlAddChild(track, location);
346 xmlAddChild(tracklist, track); 466 xmlAddChild(tracklist, track);
347 467
348 /* do we have a tuple? */ 468 /* do we have a tuple? */
349 if(entry->tuple != NULL) { 469 if(entry->tuple != NULL) {
470
350 if(entry->tuple->track_name != NULL && 471 if(entry->tuple->track_name != NULL &&
351 g_utf8_validate(entry->tuple->track_name, -1, NULL)) { 472 g_utf8_validate(entry->tuple->track_name, -1, NULL)) {
352 tmp = xmlNewNode(NULL, (xmlChar *)"title"); 473 tmp = xmlNewNode(NULL, (xmlChar *)"title");
353 xmlAddChild(tmp, xmlNewText((xmlChar *)entry->tuple->track_name)); 474 xmlAddChild(tmp, xmlNewText((xmlChar *)entry->tuple->track_name));
354 xmlAddChild(track, tmp); 475 xmlAddChild(track, tmp);
380 str = g_malloc(TMP_BUF_LEN); 501 str = g_malloc(TMP_BUF_LEN);
381 tmp = xmlNewNode(NULL, (xmlChar *)"trackNum"); 502 tmp = xmlNewNode(NULL, (xmlChar *)"trackNum");
382 sprintf(str, "%d", entry->tuple->track_number); 503 sprintf(str, "%d", entry->tuple->track_number);
383 xmlAddChild(tmp, xmlNewText((xmlChar *)str)); 504 xmlAddChild(tmp, xmlNewText((xmlChar *)str));
384 g_free(str); 505 g_free(str);
506 str = NULL;
385 xmlAddChild(track, tmp); 507 xmlAddChild(track, tmp);
386 } 508 }
387 509
388 if(entry->tuple->length > 0) { 510 if(entry->tuple->length > 0) {
389 gchar *str; 511 gchar *str;
390 str = g_malloc(TMP_BUF_LEN); 512 str = g_malloc(TMP_BUF_LEN);
391 tmp = xmlNewNode(NULL, (xmlChar *)"duration"); 513 tmp = xmlNewNode(NULL, (xmlChar *)"duration");
392 sprintf(str, "%d", entry->tuple->length); 514 sprintf(str, "%d", entry->tuple->length);
393 xmlAddChild(tmp, xmlNewText((xmlChar *)str)); 515 xmlAddChild(tmp, xmlNewText((xmlChar *)str));
394 g_free(str); 516 g_free(str);
517 str = NULL;
395 xmlAddChild(track, tmp); 518 xmlAddChild(track, tmp);
396 } 519 }
397 520
398 // 521 //
399 // additional metadata 522 // additional metadata
400 // 523 //
401 // year, date, genre, formatter, mtime 524 // year, date, genre, formatter, mtime
402 // 525 //
526
403 if(entry->tuple->year != 0) { 527 if(entry->tuple->year != 0) {
404 gchar *str; 528 gchar *str;
405 str = g_malloc(TMP_BUF_LEN); 529 str = g_malloc(TMP_BUF_LEN);
406 tmp = xmlNewNode(NULL, (xmlChar *)"meta"); 530 tmp = xmlNewNode(NULL, (xmlChar *)"meta");
407 xmlSetProp(tmp, (xmlChar *)"rel", (xmlChar *)"year"); 531 xmlSetProp(tmp, (xmlChar *)"rel", (xmlChar *)"year");
408 sprintf(str, "%d", entry->tuple->year); 532 sprintf(str, "%d", entry->tuple->year);
409 xmlAddChild(tmp, xmlNewText((xmlChar *)str)); 533 xmlAddChild(tmp, xmlNewText((xmlChar *)str));
410 xmlAddChild(track, tmp); 534 xmlAddChild(track, tmp);
411 g_free(str); 535 g_free(str);
536 str = NULL;
412 } 537 }
413 538
414 if(entry->tuple->date != NULL && 539 if(entry->tuple->date != NULL &&
415 g_utf8_validate(entry->tuple->date, -1, NULL)) { 540 g_utf8_validate(entry->tuple->date, -1, NULL)) {
416 tmp = xmlNewNode(NULL, (xmlChar *)"meta"); 541 tmp = xmlNewNode(NULL, (xmlChar *)"meta");
435 xmlAddChild(track, tmp); 560 xmlAddChild(track, tmp);
436 } 561 }
437 562
438 // mtime: write mtime unconditionally to support staticlist. 563 // mtime: write mtime unconditionally to support staticlist.
439 { 564 {
440 gchar *str; 565 gchar *str = g_malloc(TMP_BUF_LEN);
441 str = g_malloc(TMP_BUF_LEN);
442 tmp = xmlNewNode(NULL, (xmlChar *)"meta"); 566 tmp = xmlNewNode(NULL, (xmlChar *)"meta");
443 xmlSetProp(tmp, (xmlChar *)"rel", (xmlChar *)"mtime"); 567 xmlSetProp(tmp, (xmlChar *)"rel", (xmlChar *)"mtime");
444 sprintf(str, "%ld", (long)entry->tuple->mtime); 568 sprintf(str, "%ld", (long)entry->tuple->mtime);
445 569
446 xmlAddChild(tmp, xmlNewText((xmlChar *)str)); 570 xmlAddChild(tmp, xmlNewText((xmlChar *)str));
447 xmlAddChild(track, tmp); 571 xmlAddChild(track, tmp);
448 g_free(str); 572 g_free(str);
449 } 573 str = NULL;
450 } 574 }
575
576 } /* tuple */
577 else {
578
579 if(entry->title != NULL && g_utf8_validate(entry->title, -1, NULL)) {
580 tmp = xmlNewNode(NULL, (xmlChar *)"title");
581 xmlAddChild(tmp, xmlNewText((xmlChar *)entry->title));
582 xmlAddChild(track, tmp);
583 }
584
585 if(entry->length > 0) {
586 gchar *str;
587 str = g_malloc(TMP_BUF_LEN);
588 tmp = xmlNewNode(NULL, (xmlChar *)"duration");
589 sprintf(str, "%d", entry->length);
590 xmlAddChild(tmp, xmlNewText((xmlChar *)str));
591 g_free(str);
592 str = NULL;
593 xmlAddChild(track, tmp);
594 }
595
596 /* add mtime of -1 */
597 {
598 gchar *str = g_malloc(TMP_BUF_LEN);
599 tmp = xmlNewNode(NULL, (xmlChar *)"meta");
600 xmlSetProp(tmp, (xmlChar *)"rel", (xmlChar *)"mtime");
601 sprintf(str, "%ld", -1L);
602
603 xmlAddChild(tmp, xmlNewText((xmlChar *)str));
604 xmlAddChild(track, tmp);
605 g_free(str);
606 str = NULL;
607 }
608
609 } /* no tuple */
610
451 g_free(filename); 611 g_free(filename);
452 filename = NULL; 612 filename = NULL;
453 } 613 }
454 614
455 PLAYLIST_UNLOCK(playlist->mutex); 615 PLAYLIST_UNLOCK(playlist->mutex);
456 616
457 xmlSaveFormatFile(filename, doc, 1); 617 xmlSaveFormatFile(filename, doc, 1);
458 xmlFreeDoc(doc); 618 xmlFreeDoc(doc);
619 doc = NULL;
620
621 xmlFree(base);
622 base = NULL;
459 } 623 }
460 624
461 PlaylistContainer plc_xspf = { 625 PlaylistContainer plc_xspf = {
462 .name = "XSPF Playlist Format", 626 .name = "XSPF Playlist Format",
463 .ext = "xspf", 627 .ext = "xspf",