XMMS2

src/xmms/xform.c

Go to the documentation of this file.
00001 /*  XMMS2 - X Music Multiplexer System
00002  *  Copyright (C) 2003-2009 XMMS2 Team
00003  *
00004  *  PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
00005  *
00006  *  This library is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU Lesser General Public
00008  *  License as published by the Free Software Foundation; either
00009  *  version 2.1 of the License, or (at your option) any later version.
00010  *
00011  *  This library is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  *  Lesser General Public License for more details.
00015  */
00016 
00017 /**
00018  * @file
00019  * xforms
00020  */
00021 
00022 #include <string.h>
00023 
00024 #include "xmmspriv/xmms_plugin.h"
00025 #include "xmmspriv/xmms_xform.h"
00026 #include "xmmspriv/xmms_streamtype.h"
00027 #include "xmmspriv/xmms_medialib.h"
00028 #include "xmmspriv/xmms_utils.h"
00029 #include "xmms/xmms_ipc.h"
00030 #include "xmms/xmms_log.h"
00031 #include "xmms/xmms_object.h"
00032 
00033 struct xmms_xform_object_St {
00034     xmms_object_t obj;
00035 };
00036 
00037 struct xmms_xform_St {
00038     xmms_object_t obj;
00039     struct xmms_xform_St *prev;
00040 
00041     const xmms_xform_plugin_t *plugin;
00042     xmms_medialib_entry_t entry;
00043 
00044     gboolean inited;
00045 
00046     void *priv;
00047 
00048     xmms_stream_type_t *out_type;
00049 
00050     GList *goal_hints;
00051 
00052     gboolean eos;
00053     gboolean error;
00054 
00055     char *buffer;
00056     gint buffered;
00057     gint buffersize;
00058 
00059     gboolean metadata_collected;
00060 
00061     gboolean metadata_changed;
00062     GHashTable *metadata;
00063 
00064     GHashTable *privdata;
00065     GQueue *hotspots;
00066 
00067     GList *browse_list;
00068     xmmsv_t *browse_dict;
00069     gint browse_index;
00070 
00071     /** used for line reading */
00072     struct {
00073         gchar buf[XMMS_XFORM_MAX_LINE_SIZE];
00074         gchar *bufend;
00075     } lr;
00076 };
00077 
00078 typedef struct xmms_xform_hotspot_St {
00079     guint pos;
00080     gchar *key;
00081     xmmsv_t *obj;
00082 } xmms_xform_hotspot_t;
00083 
00084 #define READ_CHUNK 4096
00085 
00086 struct xmms_xform_plugin_St {
00087     xmms_plugin_t plugin;
00088 
00089     xmms_xform_methods_t methods;
00090 
00091     GList *in_types;
00092 };
00093 
00094 xmms_xform_t *xmms_xform_find (xmms_xform_t *prev, xmms_medialib_entry_t entry,
00095                                GList *goal_hints);
00096 const char *xmms_xform_shortname (xmms_xform_t *xform);
00097 static xmms_xform_t *add_effects (xmms_xform_t *last,
00098                                   xmms_medialib_entry_t entry,
00099                                   GList *goal_formats);
00100 static xmms_xform_t *xmms_xform_new_effect (xmms_xform_t* last,
00101                                             xmms_medialib_entry_t entry,
00102                                             GList *goal_formats,
00103                                             const gchar *name);
00104 static void xmms_xform_destroy (xmms_object_t *object);
00105 static void effect_callbacks_init (void);
00106 
00107 void
00108 xmms_xform_browse_add_entry_property_str (xmms_xform_t *xform,
00109                                           const gchar *key,
00110                                           const gchar *value)
00111 {
00112     xmmsv_t *val = xmmsv_new_string (value);
00113     xmms_xform_browse_add_entry_property (xform, key, val);
00114     xmmsv_unref (val);
00115 }
00116 
00117 
00118 void
00119 xmms_xform_browse_add_entry_property_int (xmms_xform_t *xform,
00120                                           const gchar *key,
00121                                           gint value)
00122 {
00123     xmmsv_t *val = xmmsv_new_int (value);
00124     xmms_xform_browse_add_entry_property (xform, key, val);
00125     xmmsv_unref (val);
00126 }
00127 
00128 void
00129 xmms_xform_browse_add_symlink_args (xmms_xform_t *xform, const gchar *basename,
00130                                     const gchar *url, gint nargs, gchar **args)
00131 {
00132     GString *s;
00133     gchar *eurl;
00134     gchar bname[32];
00135     gint i;
00136 
00137     if (!basename) {
00138         g_snprintf (bname, sizeof (bname), "%d", xform->browse_index++);
00139         basename = bname;
00140     }
00141 
00142     xmms_xform_browse_add_entry (xform, basename, 0);
00143     eurl = xmms_medialib_url_encode (url);
00144     s = g_string_new (eurl);
00145 
00146     for (i = 0; i < nargs; i++) {
00147         g_string_append_c (s, i == 0 ? '?' : '&');
00148         g_string_append (s, args[i]);
00149     }
00150 
00151     xmms_xform_browse_add_entry_property_str (xform, "realpath", s->str);
00152 
00153     g_free (eurl);
00154     g_string_free (s, TRUE);
00155 }
00156 
00157 void
00158 xmms_xform_browse_add_symlink (xmms_xform_t *xform, const gchar *basename,
00159                                const gchar *url)
00160 {
00161     xmms_xform_browse_add_symlink_args (xform, basename, url, 0, NULL);
00162 }
00163 
00164 void
00165 xmms_xform_browse_add_entry_property (xmms_xform_t *xform, const gchar *key,
00166                                       xmmsv_t *val)
00167 {
00168     g_return_if_fail (xform);
00169     g_return_if_fail (xform->browse_dict);
00170     g_return_if_fail (key);
00171     g_return_if_fail (val);
00172 
00173     xmmsv_dict_set (xform->browse_dict, key, val);
00174 }
00175 
00176 void
00177 xmms_xform_browse_add_entry (xmms_xform_t *xform, const gchar *filename,
00178                              guint32 flags)
00179 {
00180     xmmsv_t *val;
00181     const gchar *url;
00182     gchar *efile, *eurl, *t;
00183     gint l, isdir;
00184 
00185     g_return_if_fail (filename);
00186 
00187     t = strchr (filename, '/');
00188     g_return_if_fail (!t); /* filenames can't contain '/', can they? */
00189 
00190     url = xmms_xform_get_url (xform);
00191     g_return_if_fail (url);
00192 
00193     xform->browse_dict = xmmsv_new_dict ();
00194 
00195     eurl = xmms_medialib_url_encode (url);
00196     efile = xmms_medialib_url_encode (filename);
00197 
00198     /* can't use g_build_filename as we need to preserve
00199        slashes stuff like file:/// */
00200     l = strlen (url);
00201     if (l && url[l - 1] == '/') {
00202         t = g_strdup_printf ("%s%s", eurl, efile);
00203     } else {
00204         t = g_strdup_printf ("%s/%s", eurl, efile);
00205     }
00206 
00207     isdir = !!(flags & XMMS_XFORM_BROWSE_FLAG_DIR);
00208     xmms_xform_browse_add_entry_property_str (xform, "path", t);
00209     xmms_xform_browse_add_entry_property_int (xform, "isdir", isdir);
00210 
00211     val = xform->browse_dict;
00212     xform->browse_list = g_list_prepend (xform->browse_list, val);
00213 
00214     g_free (t);
00215     g_free (efile);
00216     g_free (eurl);
00217 }
00218 
00219 static gint
00220 xmms_browse_list_sortfunc (gconstpointer a, gconstpointer b)
00221 {
00222     int r1, r2;
00223     xmmsv_t *val1, *val2, *tmp1, *tmp2;
00224     const gchar *s1, *s2;
00225 
00226     val1 = (xmmsv_t *) a;
00227     val2 = (xmmsv_t *) b;
00228 
00229     g_return_val_if_fail (xmmsv_get_type (val1) == XMMSV_TYPE_DICT, 0);
00230     g_return_val_if_fail (xmmsv_get_type (val2) == XMMSV_TYPE_DICT, 0);
00231 
00232     r1 = xmmsv_dict_get (val1, "intsort", &tmp1);
00233     r2 = xmmsv_dict_get (val2, "intsort", &tmp2);
00234 
00235     if (r1 && r2) {
00236         gint i1, i2;
00237 
00238         if (!xmmsv_get_int (tmp1, &i1))
00239             return 0;
00240         if (!xmmsv_get_int (tmp2, &i2))
00241             return 0;
00242         return i1 > i2;
00243     }
00244 
00245     if (!xmmsv_dict_get (val1, "path", &tmp1))
00246         return 0;
00247     if (!xmmsv_dict_get (val2, "path", &tmp2))
00248         return 0;
00249 
00250     if (!xmmsv_get_string (tmp1, &s1))
00251         return 0;
00252     if (!xmmsv_get_string (tmp2, &s2))
00253         return 0;
00254 
00255     return xmms_natcmp (s1, s2);
00256 }
00257 
00258 GList *
00259 xmms_xform_browse_method (xmms_xform_t *xform, const gchar *url,
00260                           xmms_error_t *error)
00261 {
00262     GList *list = NULL;
00263 
00264     if (xform->plugin->methods.browse) {
00265         if (!xform->plugin->methods.browse (xform, url, error)) {
00266             return NULL;
00267         }
00268         list = xform->browse_list;
00269         xform->browse_list = NULL;
00270         list = g_list_sort (list, xmms_browse_list_sortfunc);
00271     } else {
00272         xmms_error_set (error, XMMS_ERROR_GENERIC, "Couldn't handle that URL");
00273     }
00274 
00275     return list;
00276 }
00277 
00278 GList *
00279 xmms_xform_browse (const gchar *url, xmms_error_t *error)
00280 {
00281     GList *list = NULL;
00282     gchar *durl;
00283     xmms_xform_t *xform = NULL;
00284     xmms_xform_t *xform2 = NULL;
00285 
00286     xform = xmms_xform_new (NULL, NULL, 0, NULL);
00287 
00288     durl = g_strdup (url);
00289     xmms_medialib_decode_url (durl);
00290     XMMS_DBG ("url = %s", durl);
00291 
00292     xmms_xform_outdata_type_add (xform,
00293                                  XMMS_STREAM_TYPE_MIMETYPE,
00294                                  "application/x-url",
00295                                  XMMS_STREAM_TYPE_URL,
00296                                  durl,
00297                                  XMMS_STREAM_TYPE_END);
00298 
00299     xform2 = xmms_xform_find (xform, 0, NULL);
00300     if (xform2) {
00301         XMMS_DBG ("found xform %s", xmms_xform_shortname (xform2));
00302     } else {
00303         xmms_error_set (error, XMMS_ERROR_GENERIC, "Couldn't handle that URL");
00304         xmms_object_unref (xform);
00305         g_free (durl);
00306         return NULL;
00307     }
00308 
00309     list = xmms_xform_browse_method (xform2, durl, error);
00310 
00311     xmms_object_unref (xform);
00312     xmms_object_unref (xform2);
00313 
00314     g_free (durl);
00315 
00316     return list;
00317 }
00318 
00319 static GList *
00320 xmms_xform_client_browse (xmms_xform_object_t *obj, const gchar *url,
00321                           xmms_error_t *error)
00322 {
00323     return xmms_xform_browse (url, error);
00324 }
00325 XMMS_CMD_DEFINE (browse, xmms_xform_client_browse, xmms_xform_object_t *,
00326                  LIST, STRING, NONE);
00327 
00328 static void
00329 xmms_xform_object_destroy (xmms_object_t *obj)
00330 {
00331     xmms_ipc_object_unregister (XMMS_IPC_OBJECT_XFORM);
00332 }
00333 
00334 xmms_xform_object_t *
00335 xmms_xform_object_init (void)
00336 {
00337     xmms_xform_object_t *obj;
00338 
00339     obj = xmms_object_new (xmms_xform_object_t, xmms_xform_object_destroy);
00340 
00341     xmms_ipc_object_register (XMMS_IPC_OBJECT_XFORM, XMMS_OBJECT (obj));
00342 
00343     xmms_object_cmd_add (XMMS_OBJECT (obj), XMMS_IPC_CMD_BROWSE,
00344                          XMMS_CMD_FUNC (browse));
00345 
00346     effect_callbacks_init ();
00347 
00348     return obj;
00349 }
00350 
00351 static void
00352 xmms_xform_destroy (xmms_object_t *object)
00353 {
00354     xmms_xform_t *xform = (xmms_xform_t *)object;
00355 
00356     XMMS_DBG ("Freeing xform '%s'", xmms_xform_shortname (xform));
00357 
00358     /* The 'destroy' method is not mandatory */
00359     if (xform->plugin && xform->plugin->methods.destroy && xform->inited) {
00360         xform->plugin->methods.destroy (xform);
00361     }
00362 
00363     g_hash_table_destroy (xform->metadata);
00364 
00365     g_hash_table_destroy (xform->privdata);
00366     g_queue_free (xform->hotspots);
00367 
00368     g_free (xform->buffer);
00369 
00370     xmms_object_unref (xform->out_type);
00371     xmms_object_unref (xform->plugin);
00372 
00373     if (xform->prev) {
00374         xmms_object_unref (xform->prev);
00375     }
00376 
00377 }
00378 
00379 xmms_xform_t *
00380 xmms_xform_new (xmms_xform_plugin_t *plugin, xmms_xform_t *prev,
00381                 xmms_medialib_entry_t entry, GList *goal_hints)
00382 {
00383     xmms_xform_t *xform;
00384 
00385     xform = xmms_object_new (xmms_xform_t, xmms_xform_destroy);
00386 
00387     xmms_object_ref (plugin);
00388     xform->plugin = plugin;
00389     xform->entry = entry;
00390     xform->goal_hints = goal_hints;
00391     xform->lr.bufend = &xform->lr.buf[0];
00392 
00393     if (prev) {
00394         xmms_object_ref (prev);
00395         xform->prev = prev;
00396     }
00397 
00398     xform->metadata = g_hash_table_new_full (g_str_hash, g_str_equal,
00399                                              g_free,
00400                                              (GDestroyNotify) xmmsv_unref);
00401 
00402     xform->privdata = g_hash_table_new_full (g_str_hash, g_str_equal,
00403                                              g_free,
00404                                              (GDestroyNotify) xmmsv_unref);
00405     xform->hotspots = g_queue_new ();
00406 
00407     if (plugin && entry) {
00408         if (!plugin->methods.init (xform)) {
00409             xmms_object_unref (xform);
00410             return NULL;
00411         }
00412         xform->inited = TRUE;
00413         g_return_val_if_fail (xform->out_type, NULL);
00414     }
00415 
00416     xform->buffer = g_malloc (READ_CHUNK);
00417     xform->buffersize = READ_CHUNK;
00418 
00419     return xform;
00420 }
00421 
00422 xmms_medialib_entry_t
00423 xmms_xform_entry_get (xmms_xform_t *xform)
00424 {
00425     return xform->entry;
00426 }
00427 
00428 gpointer
00429 xmms_xform_private_data_get (xmms_xform_t *xform)
00430 {
00431     return xform->priv;
00432 }
00433 
00434 void
00435 xmms_xform_private_data_set (xmms_xform_t *xform, gpointer data)
00436 {
00437     xform->priv = data;
00438 }
00439 
00440 void
00441 xmms_xform_outdata_type_add (xmms_xform_t *xform, ...)
00442 {
00443     va_list ap;
00444     va_start (ap, xform);
00445     xform->out_type = xmms_stream_type_parse (ap);
00446     va_end (ap);
00447 }
00448 
00449 void
00450 xmms_xform_outdata_type_set (xmms_xform_t *xform, xmms_stream_type_t *type)
00451 {
00452     xmms_object_ref (type);
00453     xform->out_type = type;
00454 }
00455 
00456 void
00457 xmms_xform_outdata_type_copy (xmms_xform_t *xform)
00458 {
00459     xmms_object_ref (xform->prev->out_type);
00460     xform->out_type = xform->prev->out_type;
00461 }
00462 
00463 const char *
00464 xmms_xform_indata_find_str (xmms_xform_t *xform, xmms_stream_type_key_t key)
00465 {
00466     const gchar *r;
00467     r = xmms_stream_type_get_str (xform->prev->out_type, key);
00468     if (r) {
00469         return r;
00470     } else if (xform->prev) {
00471         return xmms_xform_indata_find_str (xform->prev, key);
00472     }
00473     return NULL;
00474 }
00475 
00476 const char *
00477 xmms_xform_indata_get_str (xmms_xform_t *xform, xmms_stream_type_key_t key)
00478 {
00479     return xmms_stream_type_get_str (xform->prev->out_type, key);
00480 }
00481 
00482 gint
00483 xmms_xform_indata_get_int (xmms_xform_t *xform, xmms_stream_type_key_t key)
00484 {
00485     return xmms_stream_type_get_int (xform->prev->out_type, key);
00486 }
00487 
00488 xmms_stream_type_t *
00489 xmms_xform_outtype_get (xmms_xform_t *xform)
00490 {
00491     return xform->out_type;
00492 }
00493 
00494 xmms_stream_type_t *
00495 xmms_xform_intype_get (xmms_xform_t *xform)
00496 {
00497     return xmms_xform_outtype_get (xform->prev);
00498 }
00499 
00500 
00501 
00502 const char *
00503 xmms_xform_outtype_get_str (xmms_xform_t *xform, xmms_stream_type_key_t key)
00504 {
00505     return xmms_stream_type_get_str (xform->out_type, key);
00506 }
00507 
00508 gint
00509 xmms_xform_outtype_get_int (xmms_xform_t *xform, xmms_stream_type_key_t key)
00510 {
00511     return xmms_stream_type_get_int (xform->out_type, key);
00512 }
00513 
00514 
00515 void
00516 xmms_xform_metadata_set_int (xmms_xform_t *xform, const char *key, int val)
00517 {
00518     XMMS_DBG ("Setting '%s' to %d", key, val);
00519     g_hash_table_insert (xform->metadata, g_strdup (key),
00520                          xmmsv_new_int (val));
00521     xform->metadata_changed = TRUE;
00522 }
00523 
00524 void
00525 xmms_xform_metadata_set_str (xmms_xform_t *xform, const char *key,
00526                              const char *val)
00527 {
00528     const char *old;
00529 
00530     if (!g_utf8_validate (val, -1, NULL)) {
00531         xmms_log_error ("xform '%s' tried to set property '%s' to a NON UTF-8 string!", xmms_xform_shortname (xform), key);
00532         return;
00533     }
00534 
00535     if (xmms_xform_metadata_get_str (xform, key, &old)) {
00536         if (strcmp (old, val) == 0) {
00537             return;
00538         }
00539     }
00540 
00541     g_hash_table_insert (xform->metadata, g_strdup (key),
00542                          xmmsv_new_string (val));
00543 
00544     xform->metadata_changed = TRUE;
00545 }
00546 
00547 static const xmmsv_t *
00548 xmms_xform_metadata_get_val (xmms_xform_t *xform, const char *key)
00549 {
00550     xmmsv_t *val = NULL;
00551 
00552     for (; xform; xform = xform->prev) {
00553         val = g_hash_table_lookup (xform->metadata, key);
00554         if (val) {
00555             break;
00556         }
00557     }
00558 
00559     return val;
00560 }
00561 
00562 gboolean
00563 xmms_xform_metadata_has_val (xmms_xform_t *xform, const gchar *key)
00564 {
00565     return !!xmms_xform_metadata_get_val (xform, key);
00566 }
00567 
00568 gboolean
00569 xmms_xform_metadata_get_int (xmms_xform_t *xform, const char *key,
00570                              gint32 *val)
00571 {
00572     const xmmsv_t *obj;
00573     gboolean ret = FALSE;
00574 
00575     obj = xmms_xform_metadata_get_val (xform, key);
00576     if (obj && xmmsv_get_type (obj) == XMMSV_TYPE_INT32) {
00577         xmmsv_get_int (obj, val);
00578         ret = TRUE;
00579     }
00580 
00581     return ret;
00582 }
00583 
00584 gboolean
00585 xmms_xform_metadata_get_str (xmms_xform_t *xform, const char *key,
00586                              const gchar **val)
00587 {
00588     const xmmsv_t *obj;
00589     gboolean ret = FALSE;
00590 
00591     obj = xmms_xform_metadata_get_val (xform, key);
00592     if (obj && xmmsv_get_type (obj) == XMMSV_TYPE_STRING) {
00593         xmmsv_get_string (obj, val);
00594         ret = TRUE;
00595     }
00596 
00597     return ret;
00598 }
00599 
00600 typedef struct {
00601     xmms_medialib_session_t *session;
00602     xmms_medialib_entry_t entry;
00603     guint32 source;
00604 } metadata_festate_t;
00605 
00606 static void
00607 add_metadatum (gpointer _key, gpointer _value, gpointer user_data)
00608 {
00609     xmmsv_t *value = (xmmsv_t *) _value;
00610     gchar *key = (gchar *) _key;
00611     metadata_festate_t *st = (metadata_festate_t *) user_data;
00612 
00613     if (xmmsv_get_type (value) == XMMSV_TYPE_STRING) {
00614         const gchar *s;
00615         xmmsv_get_string (value, &s);
00616         xmms_medialib_entry_property_set_str_source (st->session,
00617                                                      st->entry,
00618                                                      key,
00619                                                      s,
00620                                                      st->source);
00621     } else if (xmmsv_get_type (value) == XMMSV_TYPE_INT32) {
00622         gint i;
00623         xmmsv_get_int (value, &i);
00624         xmms_medialib_entry_property_set_int_source (st->session,
00625                                                      st->entry,
00626                                                      key,
00627                                                      i,
00628                                                      st->source);
00629     } else {
00630         XMMS_DBG ("Unknown type?!?");
00631     }
00632 }
00633 
00634 static void
00635 xmms_xform_metadata_collect_one (xmms_xform_t *xform, metadata_festate_t *info)
00636 {
00637     gchar src[XMMS_PLUGIN_SHORTNAME_MAX_LEN + 8];
00638 
00639     XMMS_DBG ("Collecting metadata from %s", xmms_xform_shortname (xform));
00640 
00641     g_snprintf (src, sizeof (src), "plugin/%s",
00642                 xmms_xform_shortname (xform));
00643 
00644     info->source = xmms_medialib_source_to_id (info->session, src);
00645     g_hash_table_foreach (xform->metadata, add_metadatum, info);
00646 
00647     xform->metadata_changed = FALSE;
00648 }
00649 
00650 static void
00651 xmms_xform_metadata_collect_r (xmms_xform_t *xform, metadata_festate_t *info,
00652                                GString *namestr)
00653 {
00654     if (xform->prev) {
00655         xmms_xform_metadata_collect_r (xform->prev, info, namestr);
00656     }
00657 
00658     if (xform->plugin) {
00659         if (namestr->len) {
00660             g_string_append_c (namestr, ':');
00661         }
00662         g_string_append (namestr, xmms_xform_shortname (xform));
00663     }
00664 
00665     if (xform->metadata_changed) {
00666         xmms_xform_metadata_collect_one (xform, info);
00667     }
00668 
00669     xform->metadata_collected = TRUE;
00670 }
00671 
00672 static void
00673 xmms_xform_metadata_collect (xmms_xform_t *start, GString *namestr, gboolean rehashing)
00674 {
00675     metadata_festate_t info;
00676     gint times_played;
00677     gint last_started;
00678     GTimeVal now;
00679 
00680     info.entry = start->entry;
00681     info.session = xmms_medialib_begin_write ();
00682 
00683     times_played = xmms_medialib_entry_property_get_int (info.session,
00684                                                          info.entry,
00685                                                          XMMS_MEDIALIB_ENTRY_PROPERTY_TIMESPLAYED);
00686 
00687     /* times_played == -1 if we haven't played this entry yet. so after initial
00688      * metadata collection the mlib would have timesplayed = -1 if we didn't do
00689      * the following */
00690     if (times_played < 0) {
00691         times_played = 0;
00692     }
00693 
00694     last_started = xmms_medialib_entry_property_get_int (info.session,
00695                                                          info.entry,
00696                                                          XMMS_MEDIALIB_ENTRY_PROPERTY_LASTSTARTED);
00697 
00698     xmms_medialib_entry_cleanup (info.session, info.entry);
00699 
00700     xmms_xform_metadata_collect_r (start, &info, namestr);
00701 
00702     xmms_medialib_entry_property_set_str (info.session, info.entry,
00703                                           XMMS_MEDIALIB_ENTRY_PROPERTY_CHAIN,
00704                                           namestr->str);
00705 
00706     xmms_medialib_entry_property_set_int (info.session, info.entry,
00707                                           XMMS_MEDIALIB_ENTRY_PROPERTY_TIMESPLAYED,
00708                                           times_played + (rehashing ? 0 : 1));
00709 
00710     if (!rehashing || (rehashing && last_started)) {
00711         g_get_current_time (&now);
00712 
00713         xmms_medialib_entry_property_set_int (info.session, info.entry,
00714                                               XMMS_MEDIALIB_ENTRY_PROPERTY_LASTSTARTED,
00715                                               (rehashing ? last_started : now.tv_sec));
00716     }
00717 
00718     xmms_medialib_entry_status_set (info.session, info.entry,
00719                                     XMMS_MEDIALIB_ENTRY_STATUS_OK);
00720 
00721     xmms_medialib_end (info.session);
00722     xmms_medialib_entry_send_update (info.entry);
00723 }
00724 
00725 static void
00726 xmms_xform_metadata_update (xmms_xform_t *xform)
00727 {
00728     metadata_festate_t info;
00729 
00730     info.entry = xform->entry;
00731     info.session = xmms_medialib_begin_write ();
00732 
00733     xmms_xform_metadata_collect_one (xform, &info);
00734 
00735     xmms_medialib_end (info.session);
00736     xmms_medialib_entry_send_update (info.entry);
00737 }
00738 
00739 static void
00740 xmms_xform_auxdata_set_val (xmms_xform_t *xform, char *key, xmmsv_t *val)
00741 {
00742     xmms_xform_hotspot_t *hs;
00743 
00744     hs = g_new0 (xmms_xform_hotspot_t, 1);
00745     hs->pos = xform->buffered;
00746     hs->key = key;
00747     hs->obj = val;
00748 
00749     g_queue_push_tail (xform->hotspots, hs);
00750 }
00751 
00752 void
00753 xmms_xform_auxdata_barrier (xmms_xform_t *xform)
00754 {
00755     xmmsv_t *val = xmmsv_new_none ();
00756     xmms_xform_auxdata_set_val (xform, NULL, val);
00757 }
00758 
00759 void
00760 xmms_xform_auxdata_set_int (xmms_xform_t *xform, const char *key, int intval)
00761 {
00762     xmmsv_t *val = xmmsv_new_int (intval);
00763     xmms_xform_auxdata_set_val (xform, g_strdup (key), val);
00764 }
00765 
00766 void
00767 xmms_xform_auxdata_set_str (xmms_xform_t *xform, const gchar *key,
00768                             const gchar *strval)
00769 {
00770     xmmsv_t *val;
00771     const char *old;
00772 
00773     if (xmms_xform_auxdata_get_str (xform, key, &old)) {
00774         if (strcmp (old, strval) == 0) {
00775             return;
00776         }
00777     }
00778 
00779     val = xmmsv_new_string (strval);
00780     xmms_xform_auxdata_set_val (xform, g_strdup (key), val);
00781 }
00782 
00783 void
00784 xmms_xform_auxdata_set_bin (xmms_xform_t *xform, const gchar *key,
00785                             gpointer data, gssize len)
00786 {
00787     xmmsv_t *val;
00788 
00789     val = xmmsv_new_bin (data, len);
00790     xmms_xform_auxdata_set_val (xform, g_strdup (key), val);
00791 }
00792 
00793 static const xmmsv_t *
00794 xmms_xform_auxdata_get_val (xmms_xform_t *xform, const gchar *key)
00795 {
00796     guint i;
00797     xmms_xform_hotspot_t *hs;
00798     xmmsv_t *val = NULL;
00799 
00800     /* privdata is always got from the previous xform */
00801     xform = xform->prev;
00802 
00803     /* check if we have unhandled current (pos 0) hotspots for this key */
00804     for (i=0; (hs = g_queue_peek_nth (xform->hotspots, i)) != NULL; i++) {
00805         if (hs->pos != 0) {
00806             break;
00807         } else if (hs->key && !strcmp (key, hs->key)) {
00808             val = hs->obj;
00809         }
00810     }
00811 
00812     if (!val) {
00813         val = g_hash_table_lookup (xform->privdata, key);
00814     }
00815 
00816     return val;
00817 }
00818 
00819 gboolean
00820 xmms_xform_auxdata_has_val (xmms_xform_t *xform, const gchar *key)
00821 {
00822     return !!xmms_xform_auxdata_get_val (xform, key);
00823 }
00824 
00825 gboolean
00826 xmms_xform_auxdata_get_int (xmms_xform_t *xform, const gchar *key, gint32 *val)
00827 {
00828     const xmmsv_t *obj;
00829 
00830     obj = xmms_xform_auxdata_get_val (xform, key);
00831     if (obj && xmmsv_get_type (obj) == XMMSV_TYPE_INT32) {
00832         xmmsv_get_int (obj, val);
00833         return TRUE;
00834     }
00835 
00836     return FALSE;
00837 }
00838 
00839 gboolean
00840 xmms_xform_auxdata_get_str (xmms_xform_t *xform, const gchar *key,
00841                             const gchar **val)
00842 {
00843     const xmmsv_t *obj;
00844 
00845     obj = xmms_xform_auxdata_get_val (xform, key);
00846     if (obj && xmmsv_get_type (obj) == XMMSV_TYPE_STRING) {
00847         xmmsv_get_string (obj, val);
00848         return TRUE;
00849     }
00850 
00851     return FALSE;
00852 }
00853 
00854 gboolean
00855 xmms_xform_auxdata_get_bin (xmms_xform_t *xform, const gchar *key,
00856                             const guchar **data, gsize *datalen)
00857 {
00858     const xmmsv_t *obj;
00859 
00860     obj = xmms_xform_auxdata_get_val (xform, key);
00861     if (obj && xmmsv_get_type (obj) == XMMSV_TYPE_BIN) {
00862         xmmsv_get_bin (obj, data, datalen);
00863         return TRUE;
00864     }
00865 
00866     return FALSE;
00867 }
00868 
00869 const char *
00870 xmms_xform_shortname (xmms_xform_t *xform)
00871 {
00872     return (xform->plugin)
00873            ? xmms_plugin_shortname_get ((xmms_plugin_t *) xform->plugin)
00874            : "unknown";
00875 }
00876 
00877 static gint
00878 xmms_xform_this_peek (xmms_xform_t *xform, gpointer buf, gint siz,
00879                       xmms_error_t *err)
00880 {
00881     while (xform->buffered < siz) {
00882         gint res;
00883 
00884         if (xform->buffered + READ_CHUNK > xform->buffersize) {
00885             xform->buffersize *= 2;
00886             xform->buffer = g_realloc (xform->buffer, xform->buffersize);
00887         }
00888 
00889         res = xform->plugin->methods.read (xform,
00890                                            &xform->buffer[xform->buffered],
00891                                            READ_CHUNK, err);
00892 
00893         if (res < -1) {
00894             XMMS_DBG ("Read method of %s returned bad value (%d) - BUG IN PLUGIN",
00895                       xmms_xform_shortname (xform), res);
00896             res = -1;
00897         }
00898 
00899         if (res == 0) {
00900             xform->eos = TRUE;
00901             break;
00902         } else if (res == -1) {
00903             xform->error = TRUE;
00904             return -1;
00905         } else {
00906             xform->buffered += res;
00907         }
00908     }
00909 
00910     /* might have eosed */
00911     siz = MIN (siz, xform->buffered);
00912     memcpy (buf, xform->buffer, siz);
00913     return siz;
00914 }
00915 
00916 static void
00917 xmms_xform_hotspot_callback (gpointer data, gpointer user_data)
00918 {
00919     xmms_xform_hotspot_t *hs = data;
00920     gint *read = user_data;
00921 
00922     hs->pos -= *read;
00923 }
00924 
00925 static gint
00926 xmms_xform_hotspots_update (xmms_xform_t *xform)
00927 {
00928     xmms_xform_hotspot_t *hs;
00929     gint ret = -1;
00930 
00931     hs = g_queue_peek_head (xform->hotspots);
00932     while (hs != NULL && hs->pos == 0) {
00933         g_queue_pop_head (xform->hotspots);
00934         if (hs->key) {
00935             g_hash_table_insert (xform->privdata, hs->key, hs->obj);
00936         }
00937         hs = g_queue_peek_head (xform->hotspots);
00938     }
00939 
00940     if (hs != NULL) {
00941         ret = hs->pos;
00942     }
00943 
00944     return ret;
00945 }
00946 
00947 gint
00948 xmms_xform_this_read (xmms_xform_t *xform, gpointer buf, gint siz,
00949                       xmms_error_t *err)
00950 {
00951     gint read = 0;
00952     gint nexths;
00953 
00954     if (xform->error) {
00955         xmms_error_set (err, XMMS_ERROR_GENERIC, "Read on errored xform");
00956         return -1;
00957     }
00958 
00959     /* update hotspots */
00960     nexths = xmms_xform_hotspots_update (xform);
00961     if (nexths >= 0) {
00962         siz = MIN (siz, nexths);
00963     }
00964 
00965     if (xform->buffered) {
00966         read = MIN (siz, xform->buffered);
00967         memcpy (buf, xform->buffer, read);
00968         xform->buffered -= read;
00969 
00970         /* buffer edited, update hotspot positions */
00971         g_queue_foreach (xform->hotspots, &xmms_xform_hotspot_callback, &read);
00972 
00973         if (xform->buffered) {
00974             /* unless we are _peek:ing often
00975                this should be fine */
00976             memmove (xform->buffer, &xform->buffer[read], xform->buffered);
00977         }
00978     }
00979 
00980     if (xform->eos) {
00981         return read;
00982     }
00983 
00984     while (read < siz) {
00985         gint res;
00986 
00987         res = xform->plugin->methods.read (xform, buf + read, siz - read, err);
00988         if (xform->metadata_collected && xform->metadata_changed)
00989             xmms_xform_metadata_update (xform);
00990 
00991         if (res < -1) {
00992             XMMS_DBG ("Read method of %s returned bad value (%d) - BUG IN PLUGIN", xmms_xform_shortname (xform), res);
00993             res = -1;
00994         }
00995 
00996         if (res == 0) {
00997             xform->eos = TRUE;
00998             break;
00999         } else if (res == -1) {
01000             xform->error = TRUE;
01001             return -1;
01002         } else {
01003             if (read == 0)
01004                 xmms_xform_hotspots_update (xform);
01005 
01006             if (!g_queue_is_empty (xform->hotspots)) {
01007                 if (xform->buffered + res > xform->buffersize) {
01008                     xform->buffersize = MAX (xform->buffersize * 2,
01009                                              xform->buffersize + res);
01010                     xform->buffer = g_realloc (xform->buffer,
01011                                                xform->buffersize);
01012                 }
01013 
01014                 g_memmove (xform->buffer + xform->buffered, buf + read, res);
01015                 xform->buffered += res;
01016                 break;
01017             }
01018             read += res;
01019         }
01020     }
01021 
01022     return read;
01023 }
01024 
01025 gint64
01026 xmms_xform_this_seek (xmms_xform_t *xform, gint64 offset,
01027                       xmms_xform_seek_mode_t whence, xmms_error_t *err)
01028 {
01029     gint64 res;
01030 
01031     if (xform->error) {
01032         xmms_error_set (err, XMMS_ERROR_GENERIC, "Seek on errored xform");
01033         return -1;
01034     }
01035 
01036     if (!xform->plugin->methods.seek) {
01037         XMMS_DBG ("Seek not implemented in '%s'", xmms_xform_shortname (xform));
01038         xmms_error_set (err, XMMS_ERROR_GENERIC, "Seek not implemented");
01039         return -1;
01040     }
01041 
01042     if (xform->buffered && whence == XMMS_XFORM_SEEK_CUR) {
01043         offset -= xform->buffered;
01044     }
01045 
01046     res = xform->plugin->methods.seek (xform, offset, whence, err);
01047     if (res != -1) {
01048         xmms_xform_hotspot_t *hs;
01049 
01050         xform->eos = FALSE;
01051         xform->buffered = 0;
01052 
01053         /* flush the hotspot queue on seek */
01054         while ((hs = g_queue_pop_head (xform->hotspots)) != NULL) {
01055             g_free (hs->key);
01056             xmmsv_unref (hs->obj);
01057             g_free (hs);
01058         }
01059     }
01060 
01061     return res;
01062 }
01063 
01064 gint
01065 xmms_xform_peek (xmms_xform_t *xform, gpointer buf, gint siz,
01066                  xmms_error_t *err)
01067 {
01068     g_return_val_if_fail (xform->prev, -1);
01069     return xmms_xform_this_peek (xform->prev, buf, siz, err);
01070 }
01071 
01072 gchar *
01073 xmms_xform_read_line (xmms_xform_t *xform, gchar *line, xmms_error_t *err)
01074 {
01075     gchar *p;
01076 
01077     g_return_val_if_fail (xform, NULL);
01078     g_return_val_if_fail (line, NULL);
01079 
01080     p = strchr (xform->lr.buf, '\n');
01081 
01082     if (!p) {
01083         gint l, r;
01084 
01085         l = (XMMS_XFORM_MAX_LINE_SIZE - 1) - (xform->lr.bufend - xform->lr.buf);
01086         if (l) {
01087             r = xmms_xform_read (xform, xform->lr.bufend, l, err);
01088             if (r < 0) {
01089                 return NULL;
01090             }
01091             xform->lr.bufend += r;
01092         }
01093         if (xform->lr.bufend <= xform->lr.buf)
01094             return NULL;
01095 
01096         *(xform->lr.bufend) = '\0';
01097         p = strchr (xform->lr.buf, '\n');
01098         if (!p) {
01099             p = xform->lr.bufend;
01100         }
01101     }
01102 
01103     if (p > xform->lr.buf && *(p-1) == '\r') {
01104         *(p-1) = '\0';
01105     } else {
01106         *p = '\0';
01107     }
01108 
01109     strcpy (line, xform->lr.buf);
01110     memmove (xform->lr.buf, p + 1, xform->lr.bufend - p);
01111     xform->lr.bufend -= (p - xform->lr.buf) + 1;
01112     *xform->lr.bufend = '\0';
01113 
01114     return line;
01115 }
01116 
01117 gint
01118 xmms_xform_read (xmms_xform_t *xform, gpointer buf, gint siz, xmms_error_t *err)
01119 {
01120     g_return_val_if_fail (xform->prev, -1);
01121     return xmms_xform_this_read (xform->prev, buf, siz, err);
01122 }
01123 
01124 gint64
01125 xmms_xform_seek (xmms_xform_t *xform, gint64 offset,
01126                  xmms_xform_seek_mode_t whence, xmms_error_t *err)
01127 {
01128     g_return_val_if_fail (xform->prev, -1);
01129     return xmms_xform_this_seek (xform->prev, offset, whence, err);
01130 }
01131 
01132 const gchar *
01133 xmms_xform_get_url (xmms_xform_t *xform)
01134 {
01135     const gchar *url = NULL;
01136     xmms_xform_t *x;
01137     x = xform;
01138 
01139     while (!url && x) {
01140         url = xmms_xform_indata_get_str (x, XMMS_STREAM_TYPE_URL);
01141         x = x->prev;
01142     }
01143 
01144     return url;
01145 }
01146 
01147 static void
01148 xmms_xform_plugin_destroy (xmms_object_t *obj)
01149 {
01150     xmms_xform_plugin_t *plugin = (xmms_xform_plugin_t *)obj;
01151 
01152     while (plugin->in_types) {
01153         xmms_object_unref (plugin->in_types->data);
01154 
01155         plugin->in_types = g_list_delete_link (plugin->in_types,
01156                                                plugin->in_types);
01157     }
01158 
01159     xmms_plugin_destroy ((xmms_plugin_t *)obj);
01160 }
01161 
01162 xmms_plugin_t *
01163 xmms_xform_plugin_new (void)
01164 {
01165     xmms_xform_plugin_t *res;
01166 
01167     res = xmms_object_new (xmms_xform_plugin_t, xmms_xform_plugin_destroy);
01168 
01169     return (xmms_plugin_t *)res;
01170 }
01171 
01172 void
01173 xmms_xform_plugin_methods_set (xmms_xform_plugin_t *plugin,
01174                                xmms_xform_methods_t *methods)
01175 {
01176 
01177     g_return_if_fail (plugin);
01178     g_return_if_fail (plugin->plugin.type == XMMS_PLUGIN_TYPE_XFORM);
01179 
01180     XMMS_DBG ("Registering xform '%s'",
01181               xmms_plugin_shortname_get ((xmms_plugin_t *) plugin));
01182 
01183     memcpy (&plugin->methods, methods, sizeof (xmms_xform_methods_t));
01184 }
01185 
01186 gboolean
01187 xmms_xform_plugin_verify (xmms_plugin_t *_plugin)
01188 {
01189     xmms_xform_plugin_t *plugin = (xmms_xform_plugin_t *) _plugin;
01190 
01191     g_return_val_if_fail (plugin, FALSE);
01192     g_return_val_if_fail (plugin->plugin.type == XMMS_PLUGIN_TYPE_XFORM, FALSE);
01193 
01194     /* more checks */
01195 
01196     return TRUE;
01197 }
01198 
01199 void
01200 xmms_xform_plugin_indata_add (xmms_xform_plugin_t *plugin, ...)
01201 {
01202     xmms_stream_type_t *t;
01203     va_list ap;
01204     gchar *config_key, config_value[32];
01205     gint priority;
01206 
01207     va_start (ap, plugin);
01208     t = xmms_stream_type_parse (ap);
01209     va_end (ap);
01210 
01211     config_key = g_strconcat ("priority.",
01212                               xmms_stream_type_get_str (t, XMMS_STREAM_TYPE_NAME),
01213                               NULL);
01214     priority = xmms_stream_type_get_int (t, XMMS_STREAM_TYPE_PRIORITY);
01215     g_snprintf (config_value, sizeof (config_value), "%d", priority);
01216     xmms_xform_plugin_config_property_register (plugin, config_key,
01217                                                 config_value, NULL, NULL);
01218     g_free (config_key);
01219 
01220     plugin->in_types = g_list_prepend (plugin->in_types, t);
01221 }
01222 
01223 static gboolean
01224 xmms_xform_plugin_supports (xmms_xform_plugin_t *plugin, xmms_stream_type_t *st,
01225                             gint *priority)
01226 {
01227     GList *t;
01228 
01229     for (t = plugin->in_types; t; t = g_list_next (t)) {
01230         if (xmms_stream_type_match (t->data, st)) {
01231             if (priority) {
01232                 gchar *config_key;
01233                 xmms_config_property_t *conf_priority;
01234 
01235                 config_key = g_strconcat ("priority.",
01236                                           xmms_stream_type_get_str (t->data, XMMS_STREAM_TYPE_NAME),
01237                                           NULL);
01238                 conf_priority = xmms_plugin_config_lookup ((xmms_plugin_t *)plugin,
01239                                                            config_key);
01240                 g_free (config_key);
01241 
01242                 if (conf_priority) {
01243                     *priority = xmms_config_property_get_int (conf_priority);
01244                 } else {
01245                     *priority = XMMS_STREAM_TYPE_PRIORITY_DEFAULT;
01246                 }
01247             }
01248             return TRUE;
01249         }
01250     }
01251     return FALSE;
01252 }
01253 
01254 typedef struct match_state_St {
01255     xmms_xform_plugin_t *match;
01256     xmms_stream_type_t *out_type;
01257     gint priority;
01258 } match_state_t;
01259 
01260 static gboolean
01261 xmms_xform_match (xmms_plugin_t *_plugin, gpointer user_data)
01262 {
01263     xmms_xform_plugin_t *plugin = (xmms_xform_plugin_t *)_plugin;
01264     match_state_t *state = (match_state_t *)user_data;
01265     gint priority;
01266 
01267     g_assert (_plugin->type == XMMS_PLUGIN_TYPE_XFORM);
01268 
01269     if (!plugin->in_types) {
01270         XMMS_DBG ("Skipping plugin '%s'", xmms_plugin_shortname_get (_plugin));
01271         return TRUE;
01272     }
01273 
01274     XMMS_DBG ("Trying plugin '%s'", xmms_plugin_shortname_get (_plugin));
01275     if (xmms_xform_plugin_supports (plugin, state->out_type, &priority)) {
01276         XMMS_DBG ("Plugin '%s' matched (priority %d)",
01277                   xmms_plugin_shortname_get (_plugin), priority);
01278         if (priority > state->priority) {
01279             if (state->match) {
01280                 XMMS_DBG ("Using plugin '%s' (priority %d) instead of '%s' (priority %d)",
01281                           xmms_plugin_shortname_get (_plugin), priority,
01282                           xmms_plugin_shortname_get ((xmms_plugin_t *)state->match),
01283                           state->priority);
01284             }
01285 
01286             state->match = plugin;
01287             state->priority = priority;
01288         }
01289     }
01290 
01291     return TRUE;
01292 }
01293 
01294 xmms_xform_t *
01295 xmms_xform_find (xmms_xform_t *prev, xmms_medialib_entry_t entry,
01296                  GList *goal_hints)
01297 {
01298     match_state_t state;
01299     xmms_xform_t *xform = NULL;
01300 
01301     state.out_type = prev->out_type;
01302     state.match = NULL;
01303     state.priority = -1;
01304 
01305     xmms_plugin_foreach (XMMS_PLUGIN_TYPE_XFORM, xmms_xform_match, &state);
01306 
01307     if (state.match) {
01308         xform = xmms_xform_new (state.match, prev, entry, goal_hints);
01309     } else {
01310         XMMS_DBG ("Found no matching plugin...");
01311     }
01312 
01313     return xform;
01314 }
01315 
01316 gboolean
01317 xmms_xform_iseos (xmms_xform_t *xform)
01318 {
01319     gboolean ret = TRUE;
01320 
01321     if (xform->prev) {
01322         ret = xform->prev->eos;
01323     }
01324 
01325     return ret;
01326 }
01327 
01328 const xmms_stream_type_t *
01329 xmms_xform_get_out_stream_type (xmms_xform_t *xform)
01330 {
01331     return xform->out_type;
01332 }
01333 
01334 const GList *
01335 xmms_xform_goal_hints_get (xmms_xform_t *xform)
01336 {
01337     return xform->goal_hints;
01338 }
01339 
01340 
01341 static gboolean
01342 has_goalformat (xmms_xform_t *xform, GList *goal_formats)
01343 {
01344     const xmms_stream_type_t *current;
01345     gboolean ret = FALSE;
01346     GList *n;
01347 
01348     current = xmms_xform_get_out_stream_type (xform);
01349 
01350     for (n = goal_formats; n; n = g_list_next (n)) {
01351         xmms_stream_type_t *goal_type = n->data;
01352         if (xmms_stream_type_match (goal_type, current)) {
01353             ret = TRUE;
01354             break;
01355         }
01356 
01357     }
01358 
01359     if (!ret) {
01360         XMMS_DBG ("Not in one of %d goal-types", g_list_length (goal_formats));
01361     }
01362 
01363     return ret;
01364 }
01365 
01366 static void
01367 outdata_type_metadata_collect (xmms_xform_t *xform)
01368 {
01369     gint val;
01370     const char *mime;
01371     xmms_stream_type_t *type;
01372 
01373     type = xform->out_type;
01374     mime = xmms_stream_type_get_str (type, XMMS_STREAM_TYPE_MIMETYPE);
01375     if (strcmp (mime, "audio/pcm") != 0) {
01376         return;
01377     }
01378 
01379     val = xmms_stream_type_get_int (type, XMMS_STREAM_TYPE_FMT_FORMAT);
01380     if (val != -1) {
01381         const gchar *name = xmms_sample_name_get ((xmms_sample_format_t) val);
01382         xmms_xform_metadata_set_str (xform,
01383                                      XMMS_MEDIALIB_ENTRY_PROPERTY_SAMPLE_FMT,
01384                                      name);
01385     }
01386 
01387     val = xmms_stream_type_get_int (type, XMMS_STREAM_TYPE_FMT_SAMPLERATE);
01388     if (val != -1) {
01389         xmms_xform_metadata_set_int (xform,
01390                                      XMMS_MEDIALIB_ENTRY_PROPERTY_SAMPLERATE,
01391                                      val);
01392     }
01393 
01394     val = xmms_stream_type_get_int (type, XMMS_STREAM_TYPE_FMT_CHANNELS);
01395     if (val != -1) {
01396         xmms_xform_metadata_set_int (xform,
01397                                      XMMS_MEDIALIB_ENTRY_PROPERTY_CHANNELS,
01398                                      val);
01399     }
01400 }
01401 
01402 static xmms_xform_t *
01403 chain_setup (xmms_medialib_entry_t entry, const gchar *url, GList *goal_formats)
01404 {
01405     xmms_xform_t *xform, *last;
01406     gchar *durl, *args;
01407 
01408     if (!entry) {
01409         entry = 1; /* FIXME: this is soooo ugly, don't do this */
01410     }
01411 
01412     xform = xmms_xform_new (NULL, NULL, 0, goal_formats);
01413 
01414     durl = g_strdup (url);
01415 
01416     args = strchr (durl, '?');
01417     if (args) {
01418         gchar **params;
01419         gint i;
01420         *args = 0;
01421         args++;
01422         xmms_medialib_decode_url (args);
01423 
01424         params = g_strsplit (args, "&", 0);
01425 
01426         for (i = 0; params && params[i]; i++) {
01427             gchar *v;
01428             v = strchr (params[i], '=');
01429             if (v) {
01430                 *v = 0;
01431                 v++;
01432                 xmms_xform_metadata_set_str (xform, params[i], v);
01433             } else {
01434                 xmms_xform_metadata_set_int (xform, params[i], 1);
01435             }
01436         }
01437         g_strfreev (params);
01438     }
01439     xmms_medialib_decode_url (durl);
01440 
01441     xmms_xform_outdata_type_add (xform, XMMS_STREAM_TYPE_MIMETYPE,
01442                                  "application/x-url", XMMS_STREAM_TYPE_URL,
01443                                  durl, XMMS_STREAM_TYPE_END);
01444 
01445     g_free (durl);
01446 
01447     last = xform;
01448 
01449     do {
01450         xform = xmms_xform_find (last, entry, goal_formats);
01451         if (!xform) {
01452             xmms_log_error ("Couldn't set up chain for '%s' (%d)",
01453                             url, entry);
01454             xmms_object_unref (last);
01455 
01456             return NULL;
01457         }
01458         xmms_object_unref (last);
01459         last = xform;
01460     } while (!has_goalformat (xform, goal_formats));
01461 
01462     outdata_type_metadata_collect (last);
01463 
01464     return last;
01465 }
01466 
01467 static void
01468 chain_finalize (xmms_xform_t *xform, xmms_medialib_entry_t entry,
01469                 const gchar *url, gboolean rehashing)
01470 {
01471     GString *namestr;
01472 
01473     namestr = g_string_new ("");
01474     xmms_xform_metadata_collect (xform, namestr, rehashing);
01475     xmms_log_info ("Successfully setup chain for '%s' (%d) containing %s",
01476                    url, entry, namestr->str);
01477 
01478     g_string_free (namestr, TRUE);
01479 }
01480 
01481 static gchar *
01482 get_url_for_entry (xmms_medialib_entry_t entry)
01483 {
01484     xmms_medialib_session_t *session;
01485     gchar *url = NULL;
01486 
01487     session = xmms_medialib_begin ();
01488     url = xmms_medialib_entry_property_get_str (session, entry,
01489                                                 XMMS_MEDIALIB_ENTRY_PROPERTY_URL);
01490     xmms_medialib_end (session);
01491 
01492     if (!url) {
01493         xmms_log_error ("Couldn't get url for entry (%d)", entry);
01494     }
01495 
01496     return url;
01497 }
01498 
01499 xmms_xform_t *
01500 xmms_xform_chain_setup (xmms_medialib_entry_t entry, GList *goal_formats,
01501                         gboolean rehash)
01502 {
01503     gchar *url;
01504     xmms_xform_t *xform;
01505 
01506     if (!(url = get_url_for_entry (entry))) {
01507         return NULL;
01508     }
01509 
01510     xform = xmms_xform_chain_setup_url (entry, url, goal_formats, rehash);
01511     g_free (url);
01512 
01513     return xform;
01514 }
01515 
01516 xmms_xform_t *
01517 xmms_xform_chain_setup_url (xmms_medialib_entry_t entry, const gchar *url,
01518                             GList *goal_formats, gboolean rehash)
01519 {
01520     xmms_xform_t *last;
01521     xmms_plugin_t *plugin;
01522     xmms_xform_plugin_t *xform_plugin;
01523     gboolean add_segment = FALSE;
01524 
01525     last = chain_setup (entry, url, goal_formats);
01526     if (!last) {
01527         return NULL;
01528     }
01529 
01530     /* first check that segment plugin is available in the system */
01531     plugin = xmms_plugin_find (XMMS_PLUGIN_TYPE_XFORM, "segment");
01532     xform_plugin = (xmms_xform_plugin_t *) plugin;
01533 
01534     /* if segment plugin input is the same as current output, include it
01535      * for collecting additional duration metadata on audio entries */
01536     if (xform_plugin) {
01537         add_segment = xmms_xform_plugin_supports (xform_plugin,
01538                                                   last->out_type,
01539                                                   NULL);
01540         xmms_object_unref (plugin);
01541     }
01542 
01543     /* add segment plugin to the chain if it can be added */
01544     if (add_segment) {
01545         last = xmms_xform_new_effect (last, entry, goal_formats, "segment");
01546         if (!last) {
01547             return NULL;
01548         }
01549     }
01550 
01551     /* if not rehashing, also initialize all the effect plugins */
01552     if (!rehash) {
01553         last = add_effects (last, entry, goal_formats);
01554         if (!last) {
01555             return NULL;
01556         }
01557     }
01558 
01559     chain_finalize (last, entry, url, rehash);
01560     return last;
01561 }
01562 
01563 xmms_config_property_t *
01564 xmms_xform_plugin_config_property_register (xmms_xform_plugin_t *xform_plugin,
01565                                             const gchar *name,
01566                                             const gchar *default_value,
01567                                             xmms_object_handler_t cb,
01568                                             gpointer userdata)
01569 {
01570     xmms_plugin_t *plugin = (xmms_plugin_t *) xform_plugin;
01571 
01572     return xmms_plugin_config_property_register (plugin, name,
01573                                                  default_value,
01574                                                  cb, userdata);
01575 }
01576 
01577 xmms_config_property_t *
01578 xmms_xform_config_lookup (xmms_xform_t *xform, const gchar *path)
01579 {
01580     g_return_val_if_fail (xform->plugin, NULL);
01581 
01582     return xmms_plugin_config_lookup ((xmms_plugin_t *) xform->plugin, path);
01583 }
01584 
01585 static xmms_xform_t *
01586 add_effects (xmms_xform_t *last, xmms_medialib_entry_t entry,
01587              GList *goal_formats)
01588 {
01589     gint effect_no;
01590 
01591     for (effect_no = 0; TRUE; effect_no++) {
01592         xmms_config_property_t *cfg;
01593         gchar key[64];
01594         const gchar *name;
01595 
01596         g_snprintf (key, sizeof (key), "effect.order.%i", effect_no);
01597 
01598         cfg = xmms_config_lookup (key);
01599         if (!cfg) {
01600             break;
01601         }
01602 
01603         name = xmms_config_property_get_string (cfg);
01604 
01605         if (!name[0]) {
01606             continue;
01607         }
01608 
01609         last = xmms_xform_new_effect (last, entry, goal_formats, name);
01610     }
01611 
01612     return last;
01613 }
01614 
01615 static xmms_xform_t *
01616 xmms_xform_new_effect (xmms_xform_t *last, xmms_medialib_entry_t entry,
01617                        GList *goal_formats, const gchar *name)
01618 {
01619     xmms_plugin_t *plugin;
01620     xmms_xform_plugin_t *xform_plugin;
01621     xmms_xform_t *xform;
01622 
01623     plugin = xmms_plugin_find (XMMS_PLUGIN_TYPE_XFORM, name);
01624     if (!plugin) {
01625         xmms_log_error ("Couldn't find any effect named '%s'", name);
01626         return last;
01627     }
01628 
01629     xform_plugin = (xmms_xform_plugin_t *) plugin;
01630     if (!xmms_xform_plugin_supports (xform_plugin, last->out_type, NULL)) {
01631         xmms_log_info ("Effect '%s' doesn't support format, skipping",
01632                        xmms_plugin_shortname_get (plugin));
01633         xmms_object_unref (plugin);
01634         return last;
01635     }
01636 
01637     xform = xmms_xform_new (xform_plugin, last, entry, goal_formats);
01638 
01639     if (xform) {
01640         xmms_object_unref (last);
01641         last = xform;
01642     } else {
01643         xmms_log_info ("Effect '%s' failed to initialize, skipping",
01644                        xmms_plugin_shortname_get (plugin));
01645     }
01646     xmms_xform_plugin_config_property_register (xform_plugin,
01647                                                 "enabled", "0",
01648                                                 NULL, NULL);
01649     xmms_object_unref (plugin);
01650     return last;
01651 }
01652 
01653 static void
01654 update_effect_properties (xmms_object_t *object, xmmsv_t *data,
01655                           gpointer userdata)
01656 {
01657     gint effect_no = GPOINTER_TO_INT (userdata);
01658     const gchar *name;
01659 
01660     xmms_config_property_t *cfg;
01661     xmms_xform_plugin_t *xform_plugin;
01662     xmms_plugin_t *plugin;
01663     gchar key[64];
01664 
01665     name = xmms_config_property_get_string ((xmms_config_property_t *) object);
01666 
01667     if (name[0]) {
01668         plugin = xmms_plugin_find (XMMS_PLUGIN_TYPE_XFORM, name);
01669         if (!plugin) {
01670             xmms_log_error ("Couldn't find any effect named '%s'", name);
01671         } else {
01672             xform_plugin = (xmms_xform_plugin_t *) plugin;
01673             xmms_xform_plugin_config_property_register (xform_plugin, "enabled",
01674                                                         "1", NULL, NULL);
01675             xmms_object_unref (plugin);
01676         }
01677 
01678         /* setup new effect.order.n */
01679         g_snprintf (key, sizeof (key), "effect.order.%i", effect_no + 1);
01680 
01681         cfg = xmms_config_lookup (key);
01682         if (!cfg) {
01683             xmms_config_property_register (key, "", update_effect_properties,
01684                                            GINT_TO_POINTER (effect_no + 1));
01685         }
01686     }
01687 }
01688 
01689 static void
01690 effect_callbacks_init (void)
01691 {
01692     gint effect_no;
01693 
01694     xmms_config_property_t *cfg;
01695     xmms_xform_plugin_t *xform_plugin;
01696     xmms_plugin_t *plugin;
01697     gchar key[64];
01698     const gchar *name;
01699 
01700     for (effect_no = 0; ; effect_no++) {
01701         g_snprintf (key, sizeof (key), "effect.order.%i", effect_no);
01702 
01703         cfg = xmms_config_lookup (key);
01704         if (!cfg) {
01705             break;
01706         }
01707         xmms_config_property_callback_set (cfg, update_effect_properties,
01708                                            GINT_TO_POINTER (effect_no));
01709 
01710         name = xmms_config_property_get_string (cfg);
01711         if (!name[0]) {
01712             continue;
01713         }
01714 
01715         plugin = xmms_plugin_find (XMMS_PLUGIN_TYPE_XFORM, name);
01716         if (!plugin) {
01717             xmms_log_error ("Couldn't find any effect named '%s'", name);
01718             continue;
01719         }
01720 
01721         xform_plugin = (xmms_xform_plugin_t *) plugin;
01722         xmms_xform_plugin_config_property_register (xform_plugin, "enabled",
01723                                                     "1", NULL, NULL);
01724 
01725         xmms_object_unref (plugin);
01726     }
01727 
01728     /* the name stored in the last present property was not "" or there was no
01729        last present property */
01730     if ((!effect_no) || name[0]) {
01731             xmms_config_property_register (key, "", update_effect_properties,
01732                                            GINT_TO_POINTER (effect_no));
01733     }
01734 }
01735