XMMS2
|
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 * Controls playlist 00020 */ 00021 00022 #include <stdio.h> 00023 #include <unistd.h> 00024 #include <stdlib.h> 00025 #include <string.h> 00026 #include <glib.h> 00027 #include <math.h> 00028 #include <ctype.h> 00029 00030 #include "xmmspriv/xmms_playlist.h" 00031 #include "xmms/xmms_ipc.h" 00032 #include "xmms/xmms_config.h" 00033 #include "xmmspriv/xmms_medialib.h" 00034 #include "xmmspriv/xmms_collection.h" 00035 #include "xmms/xmms_log.h" 00036 /* 00037 #include "xmms/plsplugins.h" 00038 #include "xmms/util.h" 00039 #include "xmms/signal_xmms.h" 00040 #include "xmms/ipc.h" 00041 #include "xmms/mediainfo.h" 00042 #include "xmms/magic.h" 00043 */ 00044 static void xmms_playlist_destroy (xmms_object_t *object); 00045 static void xmms_playlist_client_shuffle (xmms_playlist_t *playlist, const gchar *plname, xmms_error_t *err); 00046 static void xmms_playlist_client_clear (xmms_playlist_t *playlist, const gchar *plname, xmms_error_t *err); 00047 static void xmms_playlist_client_sort (xmms_playlist_t *playlist, const gchar *plname, xmmsv_t *property, xmms_error_t *err); 00048 static GList * xmms_playlist_client_list_entries (xmms_playlist_t *playlist, const gchar *plname, xmms_error_t *err); 00049 static gchar *xmms_playlist_client_current_active (xmms_playlist_t *playlist, xmms_error_t *err); 00050 static void xmms_playlist_destroy (xmms_object_t *object); 00051 00052 static void xmms_playlist_client_add_id (xmms_playlist_t *playlist, const gchar *plname, xmms_medialib_entry_t file, xmms_error_t *error); 00053 static void xmms_playlist_client_add_url (xmms_playlist_t *playlist, const gchar *plname, const gchar *nurl, xmms_error_t *err); 00054 static void xmms_playlist_client_add_idlist (xmms_playlist_t *playlist, const gchar *plname, xmmsv_coll_t *coll, xmms_error_t *err); 00055 static void xmms_playlist_client_add_collection (xmms_playlist_t *playlist, const gchar *plname, xmmsv_coll_t *coll, xmmsv_t *order, xmms_error_t *err); 00056 static GTree * xmms_playlist_client_current_pos (xmms_playlist_t *playlist, const gchar *plname, xmms_error_t *err); 00057 static gint xmms_playlist_client_set_current_position (xmms_playlist_t *playlist, gint32 pos, xmms_error_t *error); 00058 static void xmms_playlist_client_remove (xmms_playlist_t *playlist, const gchar *plname, gint32 pos, xmms_error_t *err); 00059 static gboolean xmms_playlist_remove_unlocked (xmms_playlist_t *playlist, const gchar *plname, xmmsv_coll_t *plcoll, guint pos, xmms_error_t *err); 00060 static void xmms_playlist_client_move (xmms_playlist_t *playlist, const gchar *plname, gint32 pos, gint32 newpos, xmms_error_t *err); 00061 static gint xmms_playlist_client_set_current_position_rel (xmms_playlist_t *playlist, gint32 pos, xmms_error_t *error); 00062 static gint xmms_playlist_set_current_position_do (xmms_playlist_t *playlist, guint32 pos, xmms_error_t *err); 00063 00064 static void xmms_playlist_client_insert_url (xmms_playlist_t *playlist, const gchar *plname, gint32 pos, const gchar *url, xmms_error_t *error); 00065 static void xmms_playlist_client_insert_id (xmms_playlist_t *playlist, const gchar *plname, gint32 pos, xmms_medialib_entry_t file, xmms_error_t *error); 00066 static void xmms_playlist_client_insert_collection (xmms_playlist_t *playlist, const gchar *plname, gint32 pos, xmmsv_coll_t *coll, xmmsv_t *order, xmms_error_t *error); 00067 static void xmms_playlist_client_radd (xmms_playlist_t *playlist, const gchar *plname, const gchar *path, xmms_error_t *error); 00068 static void xmms_playlist_client_rinsert (xmms_playlist_t *playlist, const gchar *plname, gint32 pos, const gchar *path, xmms_error_t *error); 00069 00070 static void xmms_playlist_client_load (xmms_playlist_t *, const gchar *, xmms_error_t *); 00071 00072 static xmmsv_coll_t *xmms_playlist_get_coll (xmms_playlist_t *playlist, const gchar *plname, xmms_error_t *error); 00073 static const gchar *xmms_playlist_canonical_name (xmms_playlist_t *playlist, const gchar *plname); 00074 static gint xmms_playlist_coll_get_currpos (xmmsv_coll_t *plcoll); 00075 static gint xmms_playlist_coll_get_size (xmmsv_coll_t *plcoll); 00076 00077 static void xmms_playlist_update_queue (xmms_playlist_t *playlist, const gchar *plname, xmmsv_coll_t *coll); 00078 static void xmms_playlist_update_partyshuffle (xmms_playlist_t *playlist, const gchar *plname, xmmsv_coll_t *coll); 00079 00080 static void xmms_playlist_current_pos_msg_send (xmms_playlist_t *playlist, GTree *dict); 00081 static GTree * xmms_playlist_current_pos_msg_new (xmms_playlist_t *playlist, guint32 pos, const gchar *plname); 00082 00083 XMMS_CMD_DEFINE (load, xmms_playlist_client_load, xmms_playlist_t *, NONE, STRING, NONE); 00084 XMMS_CMD_DEFINE3 (insert_url, xmms_playlist_client_insert_url, xmms_playlist_t *, NONE, STRING, INT32, STRING); 00085 XMMS_CMD_DEFINE3 (insert_id, xmms_playlist_client_insert_id, xmms_playlist_t *, NONE, STRING, INT32, INT32); 00086 XMMS_CMD_DEFINE4 (insert_coll, xmms_playlist_client_insert_collection, xmms_playlist_t *, NONE, STRING, INT32, COLL, LIST); 00087 XMMS_CMD_DEFINE (shuffle, xmms_playlist_client_shuffle, xmms_playlist_t *, NONE, STRING, NONE); 00088 XMMS_CMD_DEFINE (remove, xmms_playlist_client_remove, xmms_playlist_t *, NONE, STRING, INT32); 00089 XMMS_CMD_DEFINE3 (move, xmms_playlist_client_move, xmms_playlist_t *, NONE, STRING, INT32, INT32); 00090 XMMS_CMD_DEFINE (add_url, xmms_playlist_client_add_url, xmms_playlist_t *, NONE, STRING, STRING); 00091 XMMS_CMD_DEFINE (add_id, xmms_playlist_client_add_id, xmms_playlist_t *, NONE, STRING, INT32); 00092 XMMS_CMD_DEFINE (add_idlist, xmms_playlist_client_add_idlist, xmms_playlist_t *, NONE, STRING, COLL); 00093 XMMS_CMD_DEFINE3 (add_coll, xmms_playlist_client_add_collection, xmms_playlist_t *, NONE, STRING, COLL, LIST); 00094 XMMS_CMD_DEFINE (clear, xmms_playlist_client_clear, xmms_playlist_t *, NONE, STRING, NONE); 00095 XMMS_CMD_DEFINE (sort, xmms_playlist_client_sort, xmms_playlist_t *, NONE, STRING, LIST); 00096 XMMS_CMD_DEFINE (list_entries, xmms_playlist_client_list_entries, xmms_playlist_t *, LIST, STRING, NONE); 00097 XMMS_CMD_DEFINE (current_pos, xmms_playlist_client_current_pos, xmms_playlist_t *, DICT, STRING, NONE); 00098 XMMS_CMD_DEFINE (current_active, xmms_playlist_client_current_active, xmms_playlist_t *, STRING, NONE, NONE); 00099 XMMS_CMD_DEFINE (set_pos, xmms_playlist_client_set_current_position, xmms_playlist_t *, INT32, INT32, NONE); 00100 XMMS_CMD_DEFINE (set_pos_rel, xmms_playlist_client_set_current_position_rel, xmms_playlist_t *, INT32, INT32, NONE); 00101 XMMS_CMD_DEFINE (radd, xmms_playlist_client_radd, xmms_playlist_t *, NONE, STRING, STRING); 00102 XMMS_CMD_DEFINE3 (rinsert, xmms_playlist_client_rinsert, xmms_playlist_t *, NONE, STRING, INT32, STRING); 00103 00104 #define XMMS_PLAYLIST_CHANGED_MSG(type, id, name) xmms_playlist_changed_msg_send (playlist, xmms_playlist_changed_msg_new (playlist, type, id, name)) 00105 #define XMMS_PLAYLIST_CURRPOS_MSG(pos, name) xmms_playlist_current_pos_msg_send (playlist, xmms_playlist_current_pos_msg_new (playlist, pos, name)) 00106 00107 00108 /** @defgroup Playlist Playlist 00109 * @ingroup XMMSServer 00110 * @brief This is the playlist control. 00111 * 00112 * A playlist is a central thing in the XMMS server, it 00113 * tells us what to do after we played the following entry 00114 * @{ 00115 */ 00116 00117 /** Playlist structure */ 00118 struct xmms_playlist_St { 00119 xmms_object_t object; 00120 00121 /* playlists are in the collection DAG */ 00122 xmms_coll_dag_t *colldag; 00123 00124 gboolean repeat_one; 00125 gboolean repeat_all; 00126 00127 GMutex *mutex; 00128 00129 xmms_mediainfo_reader_t *mediainfordr; 00130 00131 gboolean update_flag; 00132 xmms_medialib_t *medialib; 00133 }; 00134 00135 00136 static void 00137 on_playlist_r_all_changed (xmms_object_t *object, xmmsv_t *_data, 00138 gpointer udata) 00139 { 00140 xmms_playlist_t *playlist = udata; 00141 gint value; 00142 00143 value = xmms_config_property_get_int ((xmms_config_property_t *) object); 00144 00145 g_mutex_lock (playlist->mutex); 00146 playlist->repeat_all = !!value; 00147 g_mutex_unlock (playlist->mutex); 00148 } 00149 00150 static void 00151 on_playlist_r_one_changed (xmms_object_t *object, xmmsv_t *_data, 00152 gpointer udata) 00153 { 00154 xmms_playlist_t *playlist = udata; 00155 gint value; 00156 00157 value = xmms_config_property_get_int ((xmms_config_property_t *) object); 00158 00159 g_mutex_lock (playlist->mutex); 00160 playlist->repeat_one = !!value; 00161 g_mutex_unlock (playlist->mutex); 00162 } 00163 00164 00165 static void 00166 on_playlist_updated (xmms_object_t *object, const gchar *plname) 00167 { 00168 xmmsv_coll_t *plcoll; 00169 xmms_playlist_t *playlist = (xmms_playlist_t*)object; 00170 00171 /* Already in an update process, quit */ 00172 if (playlist->update_flag) { 00173 return; 00174 } 00175 00176 plcoll = xmms_playlist_get_coll (playlist, plname, NULL); 00177 if (plcoll == NULL) { 00178 return; 00179 } else { 00180 /* Run the update function if appropriate */ 00181 switch (xmmsv_coll_get_type (plcoll)) { 00182 case XMMS_COLLECTION_TYPE_QUEUE: 00183 xmms_playlist_update_queue (playlist, plname, plcoll); 00184 break; 00185 00186 case XMMS_COLLECTION_TYPE_PARTYSHUFFLE: 00187 xmms_playlist_update_partyshuffle (playlist, plname, plcoll); 00188 break; 00189 00190 default: 00191 break; 00192 } 00193 } 00194 } 00195 00196 static void 00197 on_playlist_updated_pos (xmms_object_t *object, xmmsv_t *val, gpointer udata) 00198 { 00199 XMMS_DBG ("PLAYLIST: updated pos!"); 00200 on_playlist_updated (object, XMMS_ACTIVE_PLAYLIST); 00201 } 00202 00203 static void 00204 on_playlist_updated_chg (xmms_object_t *object, xmmsv_t *val, gpointer udata) 00205 { 00206 const gchar *plname = NULL; 00207 xmmsv_t *pl_val; 00208 00209 XMMS_DBG ("PLAYLIST: updated chg!"); 00210 00211 xmmsv_dict_get (val, "name", &pl_val); 00212 if (pl_val != NULL) { 00213 xmmsv_get_string (pl_val, &plname); 00214 } else { 00215 /* FIXME: occurs? */ 00216 XMMS_DBG ("PLAYLIST: updated_chg, NULL playlist!"); 00217 g_assert_not_reached (); 00218 } 00219 00220 on_playlist_updated (object, plname); 00221 } 00222 00223 static void 00224 xmms_playlist_update_queue (xmms_playlist_t *playlist, const gchar *plname, 00225 xmmsv_coll_t *coll) 00226 { 00227 gint history, currpos; 00228 00229 XMMS_DBG ("PLAYLIST: update-queue!"); 00230 00231 if (!xmms_collection_get_int_attr (coll, "history", &history)) { 00232 history = 0; 00233 } 00234 00235 playlist->update_flag = TRUE; 00236 currpos = xmms_playlist_coll_get_currpos (coll); 00237 while (currpos > history) { 00238 xmms_playlist_remove_unlocked (playlist, plname, coll, 0, NULL); 00239 currpos = xmms_playlist_coll_get_currpos (coll); 00240 } 00241 playlist->update_flag = FALSE; 00242 } 00243 00244 static void 00245 xmms_playlist_update_partyshuffle (xmms_playlist_t *playlist, 00246 const gchar *plname, xmmsv_coll_t *coll) 00247 { 00248 gint history, upcoming, currpos, size; 00249 xmmsv_coll_t *src; 00250 xmmsv_t *tmp; 00251 00252 XMMS_DBG ("PLAYLIST: update-partyshuffle!"); 00253 00254 if (!xmms_collection_get_int_attr (coll, "history", &history)) { 00255 history = 0; 00256 } 00257 00258 if (!xmms_collection_get_int_attr (coll, "upcoming", &upcoming)) { 00259 upcoming = XMMS_DEFAULT_PARTYSHUFFLE_UPCOMING; 00260 } 00261 00262 playlist->update_flag = TRUE; 00263 currpos = xmms_playlist_coll_get_currpos (coll); 00264 while (currpos > history) { 00265 xmms_playlist_remove_unlocked (playlist, plname, coll, 0, NULL); 00266 currpos = xmms_playlist_coll_get_currpos (coll); 00267 } 00268 00269 if (!xmmsv_list_get (xmmsv_coll_operands_get (coll), 0, &tmp)) { 00270 XMMS_DBG ("Cannot find party shuffle operand!"); 00271 return; 00272 } 00273 xmmsv_get_coll (tmp, &src); 00274 00275 currpos = xmms_playlist_coll_get_currpos (coll); 00276 size = xmms_playlist_coll_get_size (coll); 00277 while (size < currpos + 1 + upcoming) { 00278 xmms_medialib_entry_t randentry; 00279 randentry = xmms_collection_get_random_media (playlist->colldag, src); 00280 if (randentry == 0) { 00281 break; /* No media found in the collection, give up */ 00282 } 00283 /* FIXME: add_collection might yield better perf here. */ 00284 xmms_playlist_add_entry_unlocked (playlist, plname, coll, randentry, NULL); 00285 00286 currpos = xmms_playlist_coll_get_currpos (coll); 00287 size = xmms_playlist_coll_get_size (coll); 00288 } 00289 playlist->update_flag = FALSE; 00290 } 00291 00292 00293 /** 00294 * Initializes a new xmms_playlist_t. 00295 */ 00296 xmms_playlist_t * 00297 xmms_playlist_init (void) 00298 { 00299 xmms_playlist_t *ret; 00300 xmms_config_property_t *val; 00301 00302 ret = xmms_object_new (xmms_playlist_t, xmms_playlist_destroy); 00303 ret->mutex = g_mutex_new (); 00304 00305 xmms_ipc_object_register (XMMS_IPC_OBJECT_PLAYLIST, XMMS_OBJECT (ret)); 00306 00307 xmms_ipc_broadcast_register (XMMS_OBJECT (ret), 00308 XMMS_IPC_SIGNAL_PLAYLIST_CHANGED); 00309 xmms_ipc_broadcast_register (XMMS_OBJECT (ret), 00310 XMMS_IPC_SIGNAL_PLAYLIST_CURRENT_POS); 00311 xmms_ipc_broadcast_register (XMMS_OBJECT (ret), 00312 XMMS_IPC_SIGNAL_PLAYLIST_LOADED); 00313 00314 val = xmms_config_property_register ("playlist.repeat_one", "0", 00315 on_playlist_r_one_changed, ret); 00316 ret->repeat_one = xmms_config_property_get_int (val); 00317 00318 val = xmms_config_property_register ("playlist.repeat_all", "0", 00319 on_playlist_r_all_changed, ret); 00320 ret->repeat_all = xmms_config_property_get_int (val); 00321 00322 ret->update_flag = FALSE; 00323 00324 xmms_object_connect (XMMS_OBJECT (ret), 00325 XMMS_IPC_SIGNAL_PLAYLIST_CHANGED, 00326 on_playlist_updated_chg, ret); 00327 00328 xmms_object_connect (XMMS_OBJECT (ret), 00329 XMMS_IPC_SIGNAL_PLAYLIST_CURRENT_POS, 00330 on_playlist_updated_pos, ret); 00331 00332 00333 xmms_object_cmd_add (XMMS_OBJECT (ret), 00334 XMMS_IPC_CMD_CURRENT_POS, 00335 XMMS_CMD_FUNC (current_pos)); 00336 00337 xmms_object_cmd_add (XMMS_OBJECT (ret), 00338 XMMS_IPC_CMD_CURRENT_ACTIVE, 00339 XMMS_CMD_FUNC (current_active)); 00340 00341 xmms_object_cmd_add (XMMS_OBJECT (ret), 00342 XMMS_IPC_CMD_LOAD, 00343 XMMS_CMD_FUNC (load)); 00344 00345 xmms_object_cmd_add (XMMS_OBJECT (ret), 00346 XMMS_IPC_CMD_SHUFFLE, 00347 XMMS_CMD_FUNC (shuffle)); 00348 00349 xmms_object_cmd_add (XMMS_OBJECT (ret), 00350 XMMS_IPC_CMD_SET_POS, 00351 XMMS_CMD_FUNC (set_pos)); 00352 00353 xmms_object_cmd_add (XMMS_OBJECT (ret), 00354 XMMS_IPC_CMD_SET_POS_REL, 00355 XMMS_CMD_FUNC (set_pos_rel)); 00356 00357 xmms_object_cmd_add (XMMS_OBJECT (ret), 00358 XMMS_IPC_CMD_ADD_URL, 00359 XMMS_CMD_FUNC (add_url)); 00360 00361 xmms_object_cmd_add (XMMS_OBJECT (ret), 00362 XMMS_IPC_CMD_ADD_ID, 00363 XMMS_CMD_FUNC (add_id)); 00364 00365 xmms_object_cmd_add (XMMS_OBJECT (ret), 00366 XMMS_IPC_CMD_ADD_IDLIST, 00367 XMMS_CMD_FUNC (add_idlist)); 00368 00369 xmms_object_cmd_add (XMMS_OBJECT (ret), 00370 XMMS_IPC_CMD_ADD_COLL, 00371 XMMS_CMD_FUNC (add_coll)); 00372 00373 xmms_object_cmd_add (XMMS_OBJECT (ret), 00374 XMMS_IPC_CMD_REMOVE_ENTRY, 00375 XMMS_CMD_FUNC (remove)); 00376 00377 xmms_object_cmd_add (XMMS_OBJECT (ret), 00378 XMMS_IPC_CMD_MOVE_ENTRY, 00379 XMMS_CMD_FUNC (move)); 00380 00381 xmms_object_cmd_add (XMMS_OBJECT (ret), 00382 XMMS_IPC_CMD_CLEAR, 00383 XMMS_CMD_FUNC (clear)); 00384 00385 xmms_object_cmd_add (XMMS_OBJECT (ret), 00386 XMMS_IPC_CMD_SORT, 00387 XMMS_CMD_FUNC (sort)); 00388 00389 xmms_object_cmd_add (XMMS_OBJECT (ret), 00390 XMMS_IPC_CMD_LIST, 00391 XMMS_CMD_FUNC (list_entries)); 00392 00393 xmms_object_cmd_add (XMMS_OBJECT (ret), 00394 XMMS_IPC_CMD_INSERT_URL, 00395 XMMS_CMD_FUNC (insert_url)); 00396 00397 xmms_object_cmd_add (XMMS_OBJECT (ret), 00398 XMMS_IPC_CMD_INSERT_ID, 00399 XMMS_CMD_FUNC (insert_id)); 00400 00401 xmms_object_cmd_add (XMMS_OBJECT (ret), 00402 XMMS_IPC_CMD_INSERT_COLL, 00403 XMMS_CMD_FUNC (insert_coll)); 00404 00405 xmms_object_cmd_add (XMMS_OBJECT (ret), 00406 XMMS_IPC_CMD_RADD, 00407 XMMS_CMD_FUNC (radd)); 00408 00409 xmms_object_cmd_add (XMMS_OBJECT (ret), 00410 XMMS_IPC_CMD_RINSERT, 00411 XMMS_CMD_FUNC (rinsert)); 00412 00413 ret->medialib = xmms_medialib_init (ret); 00414 ret->colldag = xmms_collection_init (ret); 00415 ret->mediainfordr = xmms_mediainfo_reader_start (); 00416 00417 return ret; 00418 } 00419 00420 static gboolean 00421 xmms_playlist_advance_do (xmms_playlist_t *playlist) 00422 { 00423 gint size, currpos; 00424 gboolean ret = TRUE; 00425 xmmsv_coll_t *plcoll; 00426 char *jumplist; 00427 xmms_error_t err; 00428 xmms_playlist_t *buffer = playlist; 00429 guint newpos; 00430 00431 xmms_error_reset (&err); 00432 00433 plcoll = xmms_playlist_get_coll (playlist, XMMS_ACTIVE_PLAYLIST, NULL); 00434 if (plcoll == NULL) { 00435 ret = FALSE; 00436 } else if ((size = xmms_playlist_coll_get_size (plcoll)) == 0) { 00437 if (xmmsv_coll_attribute_get (plcoll, "jumplist", &jumplist)) { 00438 xmms_playlist_client_load (buffer, jumplist, &err); 00439 if (xmms_error_isok (&err)) { 00440 ret = xmms_playlist_advance_do (playlist); 00441 } else { 00442 ret = FALSE; 00443 } 00444 } else { 00445 ret = FALSE; 00446 } 00447 } else if (!playlist->repeat_one) { 00448 currpos = xmms_playlist_coll_get_currpos (plcoll); 00449 currpos++; 00450 00451 if (currpos == size && !playlist->repeat_all && 00452 xmmsv_coll_attribute_get (plcoll, "jumplist", &jumplist)) { 00453 00454 xmms_collection_set_int_attr (plcoll, "position", 0); 00455 XMMS_PLAYLIST_CURRPOS_MSG (0, XMMS_ACTIVE_PLAYLIST); 00456 00457 xmms_playlist_client_load (buffer, jumplist, &err); 00458 if (xmms_error_isok (&err)) { 00459 ret = xmms_playlist_advance_do (playlist); 00460 } else { 00461 ret = FALSE; 00462 } 00463 } else { 00464 newpos = currpos%size; 00465 xmms_collection_set_int_attr (plcoll, "position", newpos); 00466 XMMS_PLAYLIST_CURRPOS_MSG (newpos, XMMS_ACTIVE_PLAYLIST); 00467 ret = (currpos != size) || playlist->repeat_all; 00468 } 00469 } 00470 00471 return ret; 00472 } 00473 00474 /** 00475 * Go to next song in playlist according to current playlist mode. 00476 * xmms_playlist_current_entry is to be used to retrieve the entry. 00477 * 00478 * @sa xmms_playlist_current_entry 00479 * 00480 * @returns FALSE if end of playlist is reached, TRUE otherwise. 00481 */ 00482 gboolean 00483 xmms_playlist_advance (xmms_playlist_t *playlist) 00484 { 00485 gboolean ret; 00486 00487 g_return_val_if_fail (playlist, FALSE); 00488 00489 g_mutex_lock (playlist->mutex); 00490 ret = xmms_playlist_advance_do (playlist); 00491 g_mutex_unlock (playlist->mutex); 00492 00493 return ret; 00494 } 00495 00496 /** 00497 * Retrieve the currently active xmms_medialib_entry_t. 00498 * 00499 */ 00500 xmms_medialib_entry_t 00501 xmms_playlist_current_entry (xmms_playlist_t *playlist) 00502 { 00503 gint size, currpos; 00504 xmmsv_coll_t *plcoll; 00505 xmms_medialib_entry_t ent = 0; 00506 00507 g_return_val_if_fail (playlist, 0); 00508 00509 g_mutex_lock (playlist->mutex); 00510 00511 plcoll = xmms_playlist_get_coll (playlist, XMMS_ACTIVE_PLAYLIST, NULL); 00512 if (plcoll == NULL) { 00513 /* FIXME: What happens? */ 00514 g_mutex_unlock (playlist->mutex); 00515 return 0; 00516 } 00517 00518 currpos = xmms_playlist_coll_get_currpos (plcoll); 00519 size = xmms_playlist_coll_get_size (plcoll); 00520 00521 if (currpos == -1 && (size > 0)) { 00522 currpos = 0; 00523 xmms_collection_set_int_attr (plcoll, "position", currpos); 00524 XMMS_PLAYLIST_CURRPOS_MSG (0, XMMS_ACTIVE_PLAYLIST); 00525 } 00526 00527 if (currpos < size) { 00528 guint *idlist; 00529 idlist = xmmsv_coll_get_idlist (plcoll); 00530 ent = idlist[currpos]; 00531 } else { 00532 ent = 0; 00533 } 00534 00535 g_mutex_unlock (playlist->mutex); 00536 00537 return ent; 00538 } 00539 00540 00541 /** 00542 * Retrieve the position of the currently active xmms_medialib_entry_t 00543 * 00544 */ 00545 GTree * 00546 xmms_playlist_client_current_pos (xmms_playlist_t *playlist, const gchar *plname, 00547 xmms_error_t *err) 00548 { 00549 guint32 pos; 00550 xmmsv_coll_t *plcoll; 00551 GTree *dict; 00552 00553 g_return_val_if_fail (playlist, 0); 00554 00555 g_mutex_lock (playlist->mutex); 00556 00557 plcoll = xmms_playlist_get_coll (playlist, plname, err); 00558 if (plcoll == NULL) { 00559 g_mutex_unlock (playlist->mutex); 00560 xmms_error_set (err, XMMS_ERROR_INVAL, "no such playlist"); 00561 return 0; 00562 } 00563 00564 pos = xmms_playlist_coll_get_currpos (plcoll); 00565 if (pos == -1) { 00566 xmms_error_set (err, XMMS_ERROR_GENERIC, "no current entry"); 00567 } 00568 00569 g_mutex_unlock (playlist->mutex); 00570 00571 dict = xmms_playlist_current_pos_msg_new (playlist, pos, plname); 00572 00573 return dict; 00574 } 00575 00576 /** 00577 * Retrieve a copy of the name of the currently active playlist. 00578 * 00579 */ 00580 static gchar * 00581 xmms_playlist_client_current_active (xmms_playlist_t *playlist, xmms_error_t *err) 00582 { 00583 gchar *name = NULL; 00584 xmmsv_coll_t *active_coll; 00585 00586 g_return_val_if_fail (playlist, 0); 00587 00588 g_mutex_lock (playlist->mutex); 00589 00590 active_coll = xmms_playlist_get_coll (playlist, XMMS_ACTIVE_PLAYLIST, err); 00591 if (active_coll != NULL) { 00592 const gchar *alias; 00593 00594 alias = xmms_collection_find_alias (playlist->colldag, 00595 XMMS_COLLECTION_NSID_PLAYLISTS, 00596 active_coll, XMMS_ACTIVE_PLAYLIST); 00597 if (alias == NULL) { 00598 xmms_error_set (err, XMMS_ERROR_GENERIC, "active playlist not referenced!"); 00599 } else { 00600 name = g_strdup (alias); 00601 } 00602 } else { 00603 xmms_error_set (err, XMMS_ERROR_GENERIC, "no active playlist"); 00604 } 00605 00606 g_mutex_unlock (playlist->mutex); 00607 00608 return name; 00609 } 00610 00611 00612 static void 00613 xmms_playlist_client_load (xmms_playlist_t *playlist, const gchar *name, xmms_error_t *err) 00614 { 00615 xmmsv_coll_t *plcoll, *active_coll; 00616 00617 if (strcmp (name, XMMS_ACTIVE_PLAYLIST) == 0) { 00618 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid playlist to load"); 00619 return; 00620 } 00621 00622 active_coll = xmms_playlist_get_coll (playlist, XMMS_ACTIVE_PLAYLIST, err); 00623 if (active_coll == NULL) { 00624 xmms_error_set (err, XMMS_ERROR_GENERIC, "no active playlist"); 00625 return; 00626 } 00627 00628 plcoll = xmms_playlist_get_coll (playlist, name, err); 00629 if (plcoll == NULL) { 00630 xmms_error_set (err, XMMS_ERROR_NOENT, "no such playlist"); 00631 return; 00632 } 00633 00634 if (active_coll == plcoll) { 00635 XMMS_DBG ("Not loading %s playlist, already active!", name); 00636 return; 00637 } 00638 00639 XMMS_DBG ("Loading new playlist! %s", name); 00640 xmms_collection_update_pointer (playlist->colldag, XMMS_ACTIVE_PLAYLIST, 00641 XMMS_COLLECTION_NSID_PLAYLISTS, plcoll); 00642 00643 xmms_object_emit_f (XMMS_OBJECT (playlist), 00644 XMMS_IPC_SIGNAL_PLAYLIST_LOADED, 00645 XMMSV_TYPE_STRING, 00646 name); 00647 } 00648 00649 static inline void 00650 swap_entries (xmmsv_coll_t *coll, gint i, gint j) 00651 { 00652 guint32 tmp, tmp2; 00653 00654 xmmsv_coll_idlist_get_index (coll, i, &tmp); 00655 xmmsv_coll_idlist_get_index (coll, j, &tmp2); 00656 00657 xmmsv_coll_idlist_set_index (coll, i, tmp2); 00658 xmmsv_coll_idlist_set_index (coll, j, tmp); 00659 } 00660 00661 00662 /** 00663 * Shuffle the playlist. 00664 * 00665 */ 00666 static void 00667 xmms_playlist_client_shuffle (xmms_playlist_t *playlist, const gchar *plname, 00668 xmms_error_t *err) 00669 { 00670 guint j,i; 00671 gint len, currpos; 00672 xmmsv_coll_t *plcoll; 00673 00674 g_return_if_fail (playlist); 00675 00676 g_mutex_lock (playlist->mutex); 00677 00678 plcoll = xmms_playlist_get_coll (playlist, plname, err); 00679 if (plcoll == NULL) { 00680 xmms_error_set (err, XMMS_ERROR_NOENT, "no such playlist"); 00681 g_mutex_unlock (playlist->mutex); 00682 return; 00683 } 00684 00685 currpos = xmms_playlist_coll_get_currpos (plcoll); 00686 len = xmms_playlist_coll_get_size (plcoll); 00687 if (len > 1) { 00688 /* put current at top and exclude from shuffling */ 00689 if (currpos != -1) { 00690 swap_entries (plcoll, 0, currpos); 00691 currpos = 0; 00692 xmms_collection_set_int_attr (plcoll, "position", currpos); 00693 } 00694 00695 /* knuth <3 */ 00696 for (i = currpos + 1; i < len; i++) { 00697 j = g_random_int_range (i, len); 00698 00699 if (i != j) { 00700 swap_entries (plcoll, i, j); 00701 } 00702 } 00703 00704 } 00705 00706 XMMS_PLAYLIST_CHANGED_MSG (XMMS_PLAYLIST_CHANGED_SHUFFLE, 0, plname); 00707 XMMS_PLAYLIST_CURRPOS_MSG (currpos, plname); 00708 00709 g_mutex_unlock (playlist->mutex); 00710 } 00711 00712 static gboolean 00713 xmms_playlist_remove_unlocked (xmms_playlist_t *playlist, const gchar *plname, 00714 xmmsv_coll_t *plcoll, guint pos, xmms_error_t *err) 00715 { 00716 gint currpos; 00717 GTree *dict; 00718 00719 g_return_val_if_fail (playlist, FALSE); 00720 00721 currpos = xmms_playlist_coll_get_currpos (plcoll); 00722 00723 if (!xmmsv_coll_idlist_remove (plcoll, pos)) { 00724 if (err) xmms_error_set (err, XMMS_ERROR_NOENT, "Entry was not in list!"); 00725 return FALSE; 00726 } 00727 00728 dict = xmms_playlist_changed_msg_new (playlist, XMMS_PLAYLIST_CHANGED_REMOVE, 0, plname); 00729 g_tree_insert (dict, (gpointer) "position", xmmsv_new_int (pos)); 00730 xmms_playlist_changed_msg_send (playlist, dict); 00731 00732 /* decrease currentpos if removed entry was before or if it's 00733 * the current entry, but only if currentpos is a valid entry. 00734 */ 00735 if (currpos != -1 && pos <= currpos) { 00736 currpos--; 00737 xmms_collection_set_int_attr (plcoll, "position", currpos); 00738 XMMS_PLAYLIST_CURRPOS_MSG (currpos, plname); 00739 } 00740 00741 return TRUE; 00742 } 00743 00744 typedef struct { 00745 xmms_playlist_t *pls; 00746 xmms_medialib_entry_t entry; 00747 } playlist_remove_info_t; 00748 00749 static void 00750 remove_from_playlist (gpointer key, gpointer value, gpointer udata) 00751 { 00752 playlist_remove_info_t *rminfo = (playlist_remove_info_t *) udata; 00753 guint32 i, val; 00754 gint size; 00755 xmmsv_coll_t *plcoll = (xmmsv_coll_t *) value; 00756 00757 size = xmms_playlist_coll_get_size (plcoll); 00758 for (i = 0; i < size; i++) { 00759 if (xmmsv_coll_idlist_get_index (plcoll, i, &val) && val == rminfo->entry) { 00760 XMMS_DBG ("removing entry on pos %d in %s", i, (gchar *)key); 00761 xmms_playlist_remove_unlocked (rminfo->pls, (gchar *)key, plcoll, i, NULL); 00762 i--; /* reset it */ 00763 } 00764 } 00765 } 00766 00767 00768 00769 /** 00770 * Remove all additions of entry in the playlist 00771 * 00772 * @param playlist the playlist to remove entries from 00773 * @param entry the playlist entry to remove 00774 * 00775 * @sa xmms_playlist_remove 00776 */ 00777 gboolean 00778 xmms_playlist_remove_by_entry (xmms_playlist_t *playlist, 00779 xmms_medialib_entry_t entry) 00780 { 00781 playlist_remove_info_t rminfo; 00782 g_return_val_if_fail (playlist, FALSE); 00783 00784 g_mutex_lock (playlist->mutex); 00785 00786 rminfo.pls = playlist; 00787 rminfo.entry = entry; 00788 00789 xmms_collection_foreach_in_namespace (playlist->colldag, 00790 XMMS_COLLECTION_NSID_PLAYLISTS, 00791 remove_from_playlist, &rminfo); 00792 00793 g_mutex_unlock (playlist->mutex); 00794 00795 return TRUE; 00796 } 00797 00798 /** 00799 * Remove an entry from playlist. 00800 * 00801 */ 00802 void 00803 xmms_playlist_client_remove (xmms_playlist_t *playlist, const gchar *plname, 00804 gint32 pos, xmms_error_t *err) 00805 { 00806 gboolean ret = FALSE; 00807 xmmsv_coll_t *plcoll; 00808 00809 g_return_if_fail (playlist); 00810 00811 g_mutex_lock (playlist->mutex); 00812 plcoll = xmms_playlist_get_coll (playlist, plname, err); 00813 if (plcoll != NULL) { 00814 ret = xmms_playlist_remove_unlocked (playlist, plname, plcoll, pos, err); 00815 } 00816 g_mutex_unlock (playlist->mutex); 00817 } 00818 00819 00820 /** 00821 * Move an entry in playlist 00822 * 00823 */ 00824 static void 00825 xmms_playlist_client_move (xmms_playlist_t *playlist, const gchar *plname, gint32 pos, 00826 gint32 newpos, xmms_error_t *err) 00827 { 00828 GTree *dict; 00829 guint32 id; 00830 gint currpos, size; 00831 gint64 ipos, inewpos; 00832 xmmsv_coll_t *plcoll; 00833 00834 g_return_if_fail (playlist); 00835 00836 XMMS_DBG ("Moving %d, to %d", pos, newpos); 00837 00838 g_mutex_lock (playlist->mutex); 00839 00840 plcoll = xmms_playlist_get_coll (playlist, plname, err); 00841 if (plcoll == NULL) { 00842 /* FIXME: happens ? */ 00843 g_mutex_unlock (playlist->mutex); 00844 return; 00845 } 00846 00847 currpos = xmms_playlist_coll_get_currpos (plcoll); 00848 size = xmms_playlist_coll_get_size (plcoll); 00849 00850 if (size == 0 || newpos > (size - 1)) { 00851 xmms_error_set (err, XMMS_ERROR_NOENT, 00852 "Cannot move entry outside playlist"); 00853 g_mutex_unlock (playlist->mutex); 00854 return; 00855 } 00856 00857 if (!xmmsv_coll_idlist_move (plcoll, pos, newpos)) { 00858 xmms_error_set (err, XMMS_ERROR_NOENT, "Entry was not in list!"); 00859 g_mutex_unlock (playlist->mutex); 00860 return; 00861 } 00862 00863 /* Update the current position pointer */ 00864 ipos = pos; 00865 inewpos = newpos; 00866 if (inewpos <= currpos && ipos > currpos) 00867 currpos++; 00868 else if (inewpos >= currpos && ipos < currpos) 00869 currpos--; 00870 else if (ipos == currpos) 00871 currpos = inewpos; 00872 00873 xmms_collection_set_int_attr (plcoll, "position", currpos); 00874 00875 xmmsv_coll_idlist_get_index (plcoll, newpos, &id); 00876 00877 dict = xmms_playlist_changed_msg_new (playlist, XMMS_PLAYLIST_CHANGED_MOVE, id, plname); 00878 g_tree_insert (dict, (gpointer) "position", xmmsv_new_int (pos)); 00879 g_tree_insert (dict, (gpointer) "newposition", xmmsv_new_int (newpos)); 00880 xmms_playlist_changed_msg_send (playlist, dict); 00881 00882 XMMS_PLAYLIST_CURRPOS_MSG (currpos, plname); 00883 00884 g_mutex_unlock (playlist->mutex); 00885 00886 return; 00887 00888 } 00889 00890 /** 00891 * Insert an entry into the playlist at given position. 00892 * Creates a #xmms_medialib_entry for you and insert it 00893 * in the list. 00894 * 00895 * @param playlist the playlist to add it URL to. 00896 * @param pos the position where the entry is inserted. 00897 * @param url the URL to add. 00898 * @param err an #xmms_error_t that should be defined upon error. 00899 * @return TRUE on success and FALSE otherwise. 00900 * 00901 */ 00902 static void 00903 xmms_playlist_client_insert_url (xmms_playlist_t *playlist, const gchar *plname, 00904 gint32 pos, const gchar *url, xmms_error_t *err) 00905 { 00906 xmms_medialib_entry_t entry = 0; 00907 xmms_medialib_session_t *session = xmms_medialib_begin_write (); 00908 00909 entry = xmms_medialib_entry_new_encoded (session, url, err); 00910 xmms_medialib_end (session); 00911 00912 if (!entry) { 00913 return; 00914 } 00915 00916 xmms_playlist_client_insert_id (playlist, plname, pos, entry, err); 00917 } 00918 00919 /** 00920 * Convenient function for inserting a directory at a given position 00921 * in the playlist, It will dive down the URL you feed it and 00922 * recursivly insert all files. 00923 * 00924 * @param playlist the playlist to add it URL to. 00925 * @param plname the name of the playlist to modify. 00926 * @param pos a position in the playlist. 00927 * @param nurl the URL of an directory you want to add 00928 * @param err an #xmms_error_t that should be defined upon error. 00929 */ 00930 static void 00931 xmms_playlist_client_rinsert (xmms_playlist_t *playlist, const gchar *plname, gint32 pos, 00932 const gchar *path, xmms_error_t *err) 00933 { 00934 /* we actually just call the medialib function, but keep 00935 * the ipc method here for not confusing users / developers 00936 */ 00937 xmms_medialib_insert_recursive (playlist->medialib, plname, pos, path, err); 00938 } 00939 00940 /** 00941 * Insert an xmms_medialib_entry to the playlist at given position. 00942 * 00943 * @param playlist the playlist to add the entry to. 00944 * @param pos the position where the entry is inserted. 00945 * @param file the #xmms_medialib_entry to add. 00946 * @param error Upon error this will be set. 00947 * @returns TRUE on success and FALSE otherwise. 00948 */ 00949 static void 00950 xmms_playlist_client_insert_id (xmms_playlist_t *playlist, const gchar *plname, 00951 gint32 pos, xmms_medialib_entry_t file, 00952 xmms_error_t *err) 00953 { 00954 if (!xmms_medialib_check_id (file)) { 00955 xmms_error_set (err, XMMS_ERROR_NOENT, 00956 "That is not a valid medialib id!"); 00957 } 00958 00959 xmms_playlist_insert_entry (playlist, plname, pos, file, err); 00960 } 00961 00962 static void 00963 xmms_playlist_client_insert_collection (xmms_playlist_t *playlist, const gchar *plname, 00964 gint32 pos, xmmsv_coll_t *coll, 00965 xmmsv_t *order, xmms_error_t *err) 00966 { 00967 GList *res; 00968 00969 res = xmms_collection_query_ids (playlist->colldag, coll, 0, 0, order, err); 00970 00971 while (res) { 00972 xmmsv_t *val = (xmmsv_t*) res->data; 00973 gint id; 00974 xmmsv_get_int (val, &id); 00975 xmms_playlist_client_insert_id (playlist, plname, pos, id, err); 00976 xmmsv_unref (val); 00977 00978 res = g_list_delete_link (res, res); 00979 pos++; 00980 } 00981 00982 } 00983 00984 /** 00985 * Insert an entry at a given position in the playlist without 00986 * validating it. 00987 * 00988 * @internal 00989 */ 00990 void 00991 xmms_playlist_insert_entry (xmms_playlist_t *playlist, const gchar *plname, 00992 guint32 pos, xmms_medialib_entry_t file, 00993 xmms_error_t *err) 00994 { 00995 GTree *dict; 00996 gint currpos; 00997 gint len; 00998 xmmsv_coll_t *plcoll; 00999 01000 g_mutex_lock (playlist->mutex); 01001 01002 plcoll = xmms_playlist_get_coll (playlist, plname, err); 01003 if (plcoll == NULL) { 01004 /* FIXME: happens ? */ 01005 g_mutex_unlock (playlist->mutex); 01006 return; 01007 } 01008 01009 len = xmms_playlist_coll_get_size (plcoll); 01010 if (pos > len) { 01011 xmms_error_set (err, XMMS_ERROR_GENERIC, 01012 "Could not insert entry outside of playlist!"); 01013 g_mutex_unlock (playlist->mutex); 01014 return; 01015 } 01016 xmmsv_coll_idlist_insert (plcoll, pos, file); 01017 01018 /** propagate the MID ! */ 01019 dict = xmms_playlist_changed_msg_new (playlist, XMMS_PLAYLIST_CHANGED_INSERT, file, plname); 01020 g_tree_insert (dict, (gpointer) "position", xmmsv_new_int (pos)); 01021 xmms_playlist_changed_msg_send (playlist, dict); 01022 01023 /** update position once client is familiar with the new item. */ 01024 currpos = xmms_playlist_coll_get_currpos (plcoll); 01025 if (pos <= currpos) { 01026 currpos++; 01027 xmms_collection_set_int_attr (plcoll, "position", currpos); 01028 XMMS_PLAYLIST_CURRPOS_MSG (currpos, plname); 01029 } 01030 01031 g_mutex_unlock (playlist->mutex); 01032 } 01033 01034 /** 01035 * Convenient function for adding a URL to the playlist, 01036 * Creates a #xmms_medialib_entry_t for you and adds it 01037 * to the list. 01038 * 01039 * @param playlist the playlist to add it URL to. 01040 * @param plname the name of the playlist to modify. 01041 * @param nurl the URL to add 01042 * @param err an #xmms_error_t that should be defined upon error. 01043 * @return TRUE on success and FALSE otherwise. 01044 */ 01045 void 01046 xmms_playlist_client_add_url (xmms_playlist_t *playlist, const gchar *plname, 01047 const gchar *nurl, xmms_error_t *err) 01048 { 01049 xmms_medialib_entry_t entry = 0; 01050 xmms_medialib_session_t *session = xmms_medialib_begin_write (); 01051 01052 entry = xmms_medialib_entry_new_encoded (session, nurl, err); 01053 xmms_medialib_end (session); 01054 01055 if (entry) { 01056 xmms_playlist_add_entry (playlist, plname, entry, err); 01057 } 01058 01059 } 01060 01061 /** 01062 * Convenient function for adding a directory to the playlist, 01063 * It will dive down the URL you feed it and recursivly add 01064 * all files there. 01065 * 01066 * @param playlist the playlist to add it URL to. 01067 * @param plname the name of the playlist to modify. 01068 * @param nurl the URL of an directory you want to add 01069 * @param err an #xmms_error_t that should be defined upon error. 01070 */ 01071 static void 01072 xmms_playlist_client_radd (xmms_playlist_t *playlist, const gchar *plname, 01073 const gchar *path, xmms_error_t *err) 01074 { 01075 /* we actually just call the medialib function, but keep 01076 * the ipc method here for not confusing users / developers 01077 */ 01078 xmms_medialib_add_recursive (playlist->medialib, plname, path, err); 01079 } 01080 01081 /** Adds a xmms_medialib_entry to the playlist. 01082 * 01083 * This will append or prepend the entry according to 01084 * the option. 01085 * This function will wake xmms_playlist_wait. 01086 * 01087 * @param playlist the playlist to add the entry to. 01088 * @param plname the name of the playlist to modify. 01089 * @param file the #xmms_medialib_entry_t to add 01090 * @param err Upon error this will be set. 01091 * @returns TRUE on success 01092 */ 01093 01094 void 01095 xmms_playlist_client_add_id (xmms_playlist_t *playlist, const gchar *plname, 01096 xmms_medialib_entry_t file, xmms_error_t *err) 01097 { 01098 if (!xmms_medialib_check_id (file)) { 01099 xmms_error_set (err, XMMS_ERROR_NOENT, 01100 "That is not a valid medialib id!"); 01101 return; 01102 } 01103 01104 xmms_playlist_add_entry (playlist, plname, file, err); 01105 } 01106 01107 void 01108 xmms_playlist_client_add_idlist (xmms_playlist_t *playlist, const gchar *plname, 01109 xmmsv_coll_t *coll, xmms_error_t *err) 01110 { 01111 uint32_t *idlist; 01112 01113 for (idlist = xmmsv_coll_get_idlist (coll); *idlist; idlist++) { 01114 if (!xmms_medialib_check_id (*idlist)) { 01115 xmms_error_set (err, XMMS_ERROR_NOENT, 01116 "Idlist contains invalid medialib id!"); 01117 return; 01118 } 01119 } 01120 01121 for (idlist = xmmsv_coll_get_idlist (coll); *idlist; idlist++) { 01122 xmms_playlist_add_entry (playlist, plname, *idlist, err); 01123 } 01124 01125 } 01126 01127 void 01128 xmms_playlist_client_add_collection (xmms_playlist_t *playlist, const gchar *plname, 01129 xmmsv_coll_t *coll, xmmsv_t *order, 01130 xmms_error_t *err) 01131 { 01132 GList *res; 01133 01134 res = xmms_collection_query_ids (playlist->colldag, coll, 0, 0, order, err); 01135 01136 while (res) { 01137 xmmsv_t *val = (xmmsv_t*) res->data; 01138 gint id; 01139 xmmsv_get_int (val, &id); 01140 xmms_playlist_add_entry (playlist, plname, id, err); 01141 xmmsv_unref (val); 01142 01143 res = g_list_delete_link (res, res); 01144 } 01145 01146 } 01147 01148 /** 01149 * Add an entry to the playlist without validating it. 01150 * 01151 * @internal 01152 */ 01153 void 01154 xmms_playlist_add_entry (xmms_playlist_t *playlist, const gchar *plname, 01155 xmms_medialib_entry_t file, xmms_error_t *err) 01156 { 01157 xmmsv_coll_t *plcoll; 01158 01159 g_mutex_lock (playlist->mutex); 01160 01161 plcoll = xmms_playlist_get_coll (playlist, plname, err); 01162 if (plcoll != NULL) { 01163 xmms_playlist_add_entry_unlocked (playlist, plname, plcoll, file, err); 01164 } 01165 01166 g_mutex_unlock (playlist->mutex); 01167 01168 } 01169 01170 /** 01171 * Add an entry to the playlist without locking the mutex. 01172 */ 01173 void 01174 xmms_playlist_add_entry_unlocked (xmms_playlist_t *playlist, 01175 const gchar *plname, 01176 xmmsv_coll_t *plcoll, 01177 xmms_medialib_entry_t file, 01178 xmms_error_t *err) 01179 { 01180 gint prev_size; 01181 GTree *dict; 01182 01183 prev_size = xmms_playlist_coll_get_size (plcoll); 01184 xmmsv_coll_idlist_append (plcoll, file); 01185 01186 /** propagate the MID ! */ 01187 dict = xmms_playlist_changed_msg_new (playlist, XMMS_PLAYLIST_CHANGED_ADD, file, plname); 01188 g_tree_insert (dict, (gpointer) "position", xmmsv_new_int (prev_size)); 01189 xmms_playlist_changed_msg_send (playlist, dict); 01190 } 01191 01192 /** Clear the playlist */ 01193 static void 01194 xmms_playlist_client_clear (xmms_playlist_t *playlist, const gchar *plname, 01195 xmms_error_t *err) 01196 { 01197 xmmsv_coll_t *plcoll; 01198 01199 g_return_if_fail (playlist); 01200 01201 g_mutex_lock (playlist->mutex); 01202 01203 plcoll = xmms_playlist_get_coll (playlist, plname, err); 01204 if (plcoll == NULL) { 01205 g_mutex_unlock (playlist->mutex); 01206 return; 01207 } 01208 01209 xmmsv_coll_idlist_clear (plcoll); 01210 xmms_collection_set_int_attr (plcoll, "position", -1); 01211 01212 XMMS_PLAYLIST_CHANGED_MSG (XMMS_PLAYLIST_CHANGED_CLEAR, 0, plname); 01213 g_mutex_unlock (playlist->mutex); 01214 01215 } 01216 01217 01218 /** Set the nextentry pointer in the playlist. 01219 * 01220 * This will set the pointer for the next entry to be 01221 * returned by xmms_playlist_advance. This function 01222 * will also wake xmms_playlist_wait 01223 */ 01224 01225 static gint 01226 xmms_playlist_set_current_position_do (xmms_playlist_t *playlist, guint32 pos, 01227 xmms_error_t *err) 01228 { 01229 gint size; 01230 guint mid; 01231 guint *idlist; 01232 xmmsv_coll_t *plcoll; 01233 char *jumplist; 01234 01235 g_return_val_if_fail (playlist, FALSE); 01236 01237 plcoll = xmms_playlist_get_coll (playlist, XMMS_ACTIVE_PLAYLIST, err); 01238 if (plcoll == NULL) { 01239 return 0; 01240 } 01241 01242 size = xmms_playlist_coll_get_size (plcoll); 01243 01244 if (pos == size && 01245 xmmsv_coll_attribute_get (plcoll, "jumplist", &jumplist)) { 01246 01247 xmms_collection_set_int_attr (plcoll, "position", 0); 01248 XMMS_PLAYLIST_CURRPOS_MSG (0, XMMS_ACTIVE_PLAYLIST); 01249 01250 xmms_playlist_client_load (playlist, jumplist, err); 01251 if (xmms_error_iserror (err)) { 01252 return 0; 01253 } 01254 01255 plcoll = xmms_playlist_get_coll (playlist, XMMS_ACTIVE_PLAYLIST, err); 01256 if (plcoll == NULL) { 01257 return 0; 01258 } 01259 } else if (pos < size) { 01260 XMMS_DBG ("newpos! %d", pos); 01261 xmms_collection_set_int_attr (plcoll, "position", pos); 01262 XMMS_PLAYLIST_CURRPOS_MSG (pos, XMMS_ACTIVE_PLAYLIST); 01263 } else { 01264 xmms_error_set (err, XMMS_ERROR_INVAL, 01265 "Can't set pos outside the current playlist!"); 01266 return 0; 01267 } 01268 01269 idlist = xmmsv_coll_get_idlist (plcoll); 01270 mid = idlist[pos]; 01271 01272 return mid; 01273 } 01274 01275 gint 01276 xmms_playlist_client_set_current_position (xmms_playlist_t *playlist, gint32 pos, 01277 xmms_error_t *err) 01278 { 01279 guint mid; 01280 g_return_val_if_fail (playlist, FALSE); 01281 01282 g_mutex_lock (playlist->mutex); 01283 mid = xmms_playlist_set_current_position_do (playlist, pos, err); 01284 g_mutex_unlock (playlist->mutex); 01285 01286 return mid; 01287 } 01288 01289 static gint 01290 xmms_playlist_client_set_current_position_rel (xmms_playlist_t *playlist, gint32 pos, 01291 xmms_error_t *err) 01292 { 01293 gint currpos, newpos; 01294 guint mid = 0; 01295 xmmsv_coll_t *plcoll; 01296 01297 g_return_val_if_fail (playlist, FALSE); 01298 01299 g_mutex_lock (playlist->mutex); 01300 01301 plcoll = xmms_playlist_get_coll (playlist, XMMS_ACTIVE_PLAYLIST, err); 01302 if (plcoll != NULL) { 01303 currpos = xmms_playlist_coll_get_currpos (plcoll); 01304 01305 if (playlist->repeat_all) { 01306 newpos = (pos+currpos) % (gint)xmmsv_coll_idlist_get_size (plcoll); 01307 01308 if (newpos < 0) { 01309 newpos += xmmsv_coll_idlist_get_size (plcoll); 01310 } 01311 01312 mid = xmms_playlist_set_current_position_do (playlist, newpos, err); 01313 } else { 01314 if (currpos + pos >= 0) { 01315 mid = xmms_playlist_set_current_position_do (playlist, 01316 currpos + pos, 01317 err); 01318 } else { 01319 xmms_error_set (err, XMMS_ERROR_INVAL, 01320 "Can't set pos outside the current playlist!"); 01321 } 01322 } 01323 } 01324 01325 g_mutex_unlock (playlist->mutex); 01326 01327 return mid; 01328 } 01329 01330 typedef struct { 01331 guint id; 01332 guint position; 01333 GList *val; /* List of (xmmsv_t *) prop values */ 01334 gboolean current; 01335 } sortdata_t; 01336 01337 01338 /** 01339 * Sort helper function. 01340 * Performs a case insesitive comparation between two entries. 01341 * We compare each pair of values in the list of prop values. 01342 */ 01343 static gint 01344 xmms_playlist_entry_compare (gconstpointer a, gconstpointer b, gpointer user_data) 01345 { 01346 GList *n1, *n2; 01347 xmmsv_t *val1, *val2, *properties, *propval; 01348 xmmsv_list_iter_t *propit; 01349 sortdata_t *data1 = (sortdata_t *) a; 01350 sortdata_t *data2 = (sortdata_t *) b; 01351 int s1, s2, res; 01352 const gchar *propstr, *str1, *str2; 01353 01354 properties = (xmmsv_t *) user_data; 01355 for (n1 = data1->val, n2 = data2->val, xmmsv_get_list_iter (properties, &propit); 01356 n1 && n2 && xmmsv_list_iter_valid (propit); 01357 n1 = n1->next, n2 = n2->next, xmmsv_list_iter_next (propit)) { 01358 01359 xmmsv_list_iter_entry (propit, &propval); 01360 xmmsv_get_string (propval, &propstr); 01361 if (propstr[0] == '-') { 01362 val2 = n1->data; 01363 val1 = n2->data; 01364 } else { 01365 val1 = n1->data; 01366 val2 = n2->data; 01367 } 01368 01369 if (!val1) { 01370 if (!val2) 01371 continue; 01372 else 01373 return -1; 01374 } 01375 01376 if (!val2) { 01377 return 1; 01378 } 01379 01380 if (xmmsv_get_type (val1) == XMMSV_TYPE_STRING && 01381 xmmsv_get_type (val2) == XMMSV_TYPE_STRING) { 01382 xmmsv_get_string (val1, &str1); 01383 xmmsv_get_string (val2, &str2); 01384 res = g_utf8_collate (str1, str2); 01385 /* keep comparing next pair if equal */ 01386 if (res == 0) 01387 continue; 01388 else 01389 return res; 01390 } 01391 01392 if (xmmsv_get_type (val1) == XMMSV_TYPE_INT32 && 01393 xmmsv_get_type (val2) == XMMSV_TYPE_INT32) 01394 { 01395 xmmsv_get_int (val1, &s1); 01396 xmmsv_get_int (val2, &s2); 01397 01398 if (s1 < s2) 01399 return -1; 01400 else if (s1 > s2) 01401 return 1; 01402 else 01403 continue; /* equal, compare next pair of properties */ 01404 } 01405 01406 XMMS_DBG ("Types in compare function differ to much"); 01407 01408 return 0; 01409 } 01410 01411 /* all pairs matched, really equal! */ 01412 return 0; 01413 } 01414 01415 /** 01416 * Unwind helper function. 01417 * Frees the sortdata elements. 01418 */ 01419 static void 01420 xmms_playlist_sorted_free (gpointer data, gpointer userdata) 01421 { 01422 GList *n; 01423 sortdata_t *sorted = (sortdata_t *) data; 01424 01425 for (n = sorted->val; n; n = n->next) { 01426 if (n->data) { 01427 xmmsv_unref (n->data); 01428 } 01429 } 01430 g_list_free (sorted->val); 01431 g_free (sorted); 01432 } 01433 01434 /** 01435 * Unwind helper function. 01436 * Fills the playlist with the new sorted data. 01437 */ 01438 static void 01439 xmms_playlist_sorted_unwind (gpointer data, gpointer userdata) 01440 { 01441 gint size; 01442 sortdata_t *sorted = (sortdata_t *) data; 01443 xmmsv_coll_t *playlist = (xmmsv_coll_t *)userdata; 01444 01445 xmmsv_coll_idlist_append (playlist, sorted->id); 01446 01447 if (sorted->current) { 01448 size = xmmsv_coll_idlist_get_size (playlist); 01449 xmms_collection_set_int_attr (playlist, "position", size - 1); 01450 } 01451 01452 xmms_playlist_sorted_free (sorted, NULL); 01453 } 01454 01455 /** Sorts the playlist by properties. 01456 * 01457 * This will sort the list. 01458 * @param playlist The playlist to sort. 01459 * @param properties Tells xmms_playlist_sort which properties it 01460 * should use when sorting. 01461 * @param err An #xmms_error_t - needed since xmms_playlist_sort is an ipc 01462 * method handler. 01463 */ 01464 01465 static void 01466 xmms_playlist_client_sort (xmms_playlist_t *playlist, const gchar *plname, 01467 xmmsv_t *properties, xmms_error_t *err) 01468 { 01469 guint32 i; 01470 GList *tmp = NULL, *n; 01471 sortdata_t *data; 01472 const gchar *str; 01473 xmmsv_t *val; 01474 xmms_medialib_session_t *session; 01475 gboolean list_changed = FALSE; 01476 xmmsv_coll_t *plcoll; 01477 gint currpos, size; 01478 xmmsv_t *valstr; 01479 xmmsv_list_iter_t *propit; 01480 01481 g_return_if_fail (playlist); 01482 g_return_if_fail (properties); 01483 01484 g_mutex_lock (playlist->mutex); 01485 01486 plcoll = xmms_playlist_get_coll (playlist, plname, err); 01487 if (plcoll == NULL) { 01488 xmms_error_set (err, XMMS_ERROR_NOENT, "no such playlist!"); 01489 g_mutex_unlock (playlist->mutex); 01490 return; 01491 } 01492 01493 /* check for invalid property strings */ 01494 if (!check_string_list (properties)) { 01495 xmms_error_set (err, XMMS_ERROR_NOENT, 01496 "invalid list of properties to sort by!"); 01497 g_mutex_unlock (playlist->mutex); 01498 return; 01499 } 01500 01501 if (xmmsv_list_get_size (properties) < 1) { 01502 xmms_error_set (err, XMMS_ERROR_NOENT, 01503 "empty list of properties to sort by!"); 01504 g_mutex_unlock (playlist->mutex); 01505 return; 01506 } 01507 01508 /* in debug, show the first ordering property */ 01509 xmmsv_list_get (properties, 0, &valstr); 01510 xmmsv_get_string (valstr, &str); 01511 XMMS_DBG ("Sorting on %s (and maybe more)", str); 01512 01513 currpos = xmms_playlist_coll_get_currpos (plcoll); 01514 size = xmms_playlist_coll_get_size (plcoll); 01515 01516 /* check whether we need to do any sorting at all */ 01517 if (size < 2) { 01518 g_mutex_unlock (playlist->mutex); 01519 return; 01520 } 01521 01522 session = xmms_medialib_begin (); 01523 01524 xmmsv_get_list_iter (properties, &propit); 01525 for (i = 0; i < size; i++) { 01526 data = g_new (sortdata_t, 1); 01527 01528 xmmsv_coll_idlist_get_index (plcoll, i, &data->id); 01529 data->position = i; 01530 01531 /* save the list of values corresponding to the list of sort props */ 01532 data->val = NULL; 01533 for (xmmsv_list_iter_first (propit); 01534 xmmsv_list_iter_valid (propit); 01535 xmmsv_list_iter_next (propit)) { 01536 01537 xmmsv_list_iter_entry (propit, &valstr); 01538 xmmsv_get_string (valstr, &str); 01539 if (str[0] == '-') 01540 str++; 01541 01542 val = xmms_medialib_entry_property_get_value (session, 01543 data->id, 01544 str); 01545 01546 if (val && xmmsv_get_type (val) == XMMSV_TYPE_STRING) { 01547 gchar *casefold; 01548 /* replace val by casefolded-string (beware of new/free order)*/ 01549 xmmsv_get_string (val, &str); 01550 casefold = g_utf8_casefold (str, strlen (str)); 01551 xmmsv_unref (val); 01552 01553 val = xmmsv_new_string (casefold); 01554 g_free (casefold); 01555 } 01556 01557 data->val = g_list_prepend (data->val, val); 01558 } 01559 data->val = g_list_reverse (data->val); 01560 01561 data->current = (currpos == i); 01562 01563 tmp = g_list_prepend (tmp, data); 01564 } 01565 01566 xmms_medialib_end (session); 01567 01568 tmp = g_list_reverse (tmp); 01569 tmp = g_list_sort_with_data (tmp, xmms_playlist_entry_compare, properties); 01570 01571 /* check whether there was any change */ 01572 for (i = 0, n = tmp; n; i++, n = g_list_next (n)) { 01573 if (((sortdata_t*)n->data)->position != i) { 01574 list_changed = TRUE; 01575 break; 01576 } 01577 } 01578 01579 if (!list_changed) { 01580 g_list_foreach (tmp, xmms_playlist_sorted_free, NULL); 01581 g_list_free (tmp); 01582 g_mutex_unlock (playlist->mutex); 01583 return; 01584 } 01585 01586 xmmsv_coll_idlist_clear (plcoll); 01587 g_list_foreach (tmp, xmms_playlist_sorted_unwind, plcoll); 01588 01589 g_list_free (tmp); 01590 01591 XMMS_PLAYLIST_CHANGED_MSG (XMMS_PLAYLIST_CHANGED_SORT, 0, plname); 01592 XMMS_PLAYLIST_CURRPOS_MSG (currpos, plname); 01593 01594 g_mutex_unlock (playlist->mutex); 01595 } 01596 01597 01598 /** List a playlist */ 01599 static GList * 01600 xmms_playlist_client_list_entries (xmms_playlist_t *playlist, const gchar *plname, 01601 xmms_error_t *err) 01602 { 01603 GList *entries = NULL; 01604 xmmsv_coll_t *plcoll; 01605 guint *idlist; 01606 gint i; 01607 01608 g_return_val_if_fail (playlist, NULL); 01609 01610 g_mutex_lock (playlist->mutex); 01611 01612 plcoll = xmms_playlist_get_coll (playlist, plname, err); 01613 if (plcoll == NULL) { 01614 g_mutex_unlock (playlist->mutex); 01615 return NULL; 01616 } 01617 01618 idlist = xmmsv_coll_get_idlist (plcoll); 01619 01620 for (i = 0; idlist[i] != 0; i++) { 01621 entries = g_list_prepend (entries, xmmsv_new_int (idlist[i])); 01622 } 01623 01624 g_mutex_unlock (playlist->mutex); 01625 01626 entries = g_list_reverse (entries); 01627 01628 return entries; 01629 } 01630 01631 /** returns pointer to mediainfo reader. */ 01632 xmms_mediainfo_reader_t * 01633 xmms_playlist_mediainfo_reader_get (xmms_playlist_t *playlist) 01634 { 01635 g_return_val_if_fail (playlist, NULL); 01636 01637 return playlist->mediainfordr; 01638 } 01639 01640 /** @} */ 01641 01642 /** Free the playlist and other memory in the xmms_playlist_t 01643 * 01644 * This will free all entries in the list! 01645 */ 01646 01647 static void 01648 xmms_playlist_destroy (xmms_object_t *object) 01649 { 01650 xmms_config_property_t *val; 01651 xmms_playlist_t *playlist = (xmms_playlist_t *)object; 01652 01653 g_return_if_fail (playlist); 01654 01655 g_mutex_free (playlist->mutex); 01656 01657 val = xmms_config_lookup ("playlist.repeat_one"); 01658 xmms_config_property_callback_remove (val, on_playlist_r_one_changed, playlist); 01659 val = xmms_config_lookup ("playlist.repeat_all"); 01660 xmms_config_property_callback_remove (val, on_playlist_r_all_changed, playlist); 01661 01662 xmms_object_unref (playlist->colldag); 01663 xmms_object_unref (playlist->mediainfordr); 01664 01665 xmms_ipc_broadcast_unregister (XMMS_IPC_SIGNAL_PLAYLIST_CHANGED); 01666 xmms_ipc_broadcast_unregister (XMMS_IPC_SIGNAL_PLAYLIST_CURRENT_POS); 01667 xmms_ipc_object_unregister (XMMS_IPC_OBJECT_PLAYLIST); 01668 } 01669 01670 01671 static xmmsv_coll_t * 01672 xmms_playlist_get_coll (xmms_playlist_t *playlist, const gchar *plname, 01673 xmms_error_t *error) 01674 { 01675 xmmsv_coll_t *coll; 01676 coll = xmms_collection_get_pointer (playlist->colldag, plname, 01677 XMMS_COLLECTION_NSID_PLAYLISTS); 01678 01679 if (coll == NULL && error != NULL) { 01680 xmms_error_set (error, XMMS_ERROR_INVAL, "invalid playlist name"); 01681 } 01682 01683 return coll; 01684 } 01685 01686 /** 01687 * Retrieve the canonical name of a playlist. Assumes the playlist 01688 * name is valid. 01689 */ 01690 static const gchar * 01691 xmms_playlist_canonical_name (xmms_playlist_t *playlist, const gchar *plname) 01692 { 01693 const gchar *fullname; 01694 01695 if (strcmp (plname, XMMS_ACTIVE_PLAYLIST) == 0) { 01696 xmmsv_coll_t *coll; 01697 coll = xmms_collection_get_pointer (playlist->colldag, plname, 01698 XMMS_COLLECTION_NSID_PLAYLISTS); 01699 fullname = xmms_collection_find_alias (playlist->colldag, 01700 XMMS_COLLECTION_NSID_PLAYLISTS, 01701 coll, plname); 01702 } else { 01703 fullname = plname; 01704 } 01705 01706 return fullname; 01707 } 01708 01709 /** Get the current position in the given playlist (set to -1 if absent). */ 01710 static gint 01711 xmms_playlist_coll_get_currpos (xmmsv_coll_t *plcoll) 01712 { 01713 gint currpos; 01714 01715 /* If absent, set to -1 and save it */ 01716 if (!xmms_collection_get_int_attr (plcoll, "position", &currpos)) { 01717 currpos = -1; 01718 xmms_collection_set_int_attr (plcoll, "position", currpos); 01719 } 01720 01721 return currpos; 01722 } 01723 01724 /** Get the size of the given playlist (compute and update it if absent). */ 01725 static gint 01726 xmms_playlist_coll_get_size (xmmsv_coll_t *plcoll) 01727 { 01728 return xmmsv_coll_idlist_get_size (plcoll); 01729 } 01730 01731 01732 GTree * 01733 xmms_playlist_changed_msg_new (xmms_playlist_t *playlist, 01734 xmms_playlist_changed_actions_t type, 01735 guint32 id, const gchar *plname) 01736 { 01737 GTree *dict; 01738 const gchar *tmp; 01739 01740 dict = g_tree_new_full ((GCompareDataFunc) strcmp, NULL, 01741 NULL, (GDestroyNotify) xmmsv_unref); 01742 01743 g_tree_insert (dict, (gpointer) "type", xmmsv_new_int (type)); 01744 01745 if (id) { 01746 g_tree_insert (dict, (gpointer) "id", xmmsv_new_int (id)); 01747 } 01748 01749 tmp = xmms_playlist_canonical_name (playlist, plname); 01750 g_tree_insert (dict, (gpointer) "name", xmmsv_new_string (tmp)); 01751 01752 return dict; 01753 } 01754 01755 GTree * 01756 xmms_playlist_current_pos_msg_new (xmms_playlist_t *playlist, 01757 guint32 pos, const gchar *plname) 01758 { 01759 GTree *dict; 01760 const gchar *tmp; 01761 01762 dict = g_tree_new_full ((GCompareDataFunc) strcmp, NULL, 01763 NULL, (GDestroyNotify) xmmsv_unref); 01764 01765 g_tree_insert (dict, (gpointer) "position", xmmsv_new_int (pos)); 01766 01767 tmp = xmms_playlist_canonical_name (playlist, plname); 01768 g_tree_insert (dict, (gpointer) "name", xmmsv_new_string (tmp)); 01769 01770 return dict; 01771 } 01772 01773 void 01774 xmms_playlist_changed_msg_send (xmms_playlist_t *playlist, GTree *dict) 01775 { 01776 xmmsv_t *type_val; 01777 xmmsv_t *pl_val; 01778 gint type; 01779 const gchar *plname; 01780 01781 g_return_if_fail (playlist); 01782 g_return_if_fail (dict); 01783 01784 /* If local playlist change, trigger a COLL_CHANGED signal */ 01785 type_val = g_tree_lookup (dict, "type"); 01786 pl_val = g_tree_lookup (dict, "name"); 01787 if (type_val != NULL && xmmsv_get_int (type_val, &type) && 01788 type != XMMS_PLAYLIST_CHANGED_UPDATE && 01789 pl_val != NULL && xmmsv_get_string (pl_val, &plname)) { 01790 XMMS_COLLECTION_PLAYLIST_CHANGED_MSG (playlist->colldag, plname); 01791 } 01792 01793 xmms_object_emit_f (XMMS_OBJECT (playlist), 01794 XMMS_IPC_SIGNAL_PLAYLIST_CHANGED, 01795 XMMSV_TYPE_DICT, 01796 dict); 01797 01798 g_tree_destroy (dict); 01799 } 01800 01801 static void 01802 xmms_playlist_current_pos_msg_send (xmms_playlist_t *playlist, 01803 GTree *dict) 01804 { 01805 g_return_if_fail (playlist); 01806 01807 g_return_if_fail (dict); 01808 01809 xmms_object_emit_f (XMMS_OBJECT (playlist), 01810 XMMS_IPC_SIGNAL_PLAYLIST_CURRENT_POS, 01811 XMMSV_TYPE_DICT, 01812 dict); 01813 01814 g_tree_destroy (dict); 01815 }