Mercurial > audlegacy-plugins
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", |