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