hythmbox-2.98/plugins/generic-player/rb-generic-player-source.c

Location Tool Test ID Function Issue
rb-generic-player-source.c:831:3 clang-analyzer Value stored to 'is_song' is never read
   1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
   2  *
   3  *  Copyright (C) 2004 James Livingston  <doclivingston@gmail.com>
   4  *
   5  *  This program is free software; you can redistribute it and/or modify
   6  *  it under the terms of the GNU General Public License as published by
   7  *  the Free Software Foundation; either version 2 of the License, or
   8  *  (at your option) any later version.
   9  *
  10  *  The Rhythmbox authors hereby grant permission for non-GPL compatible
  11  *  GStreamer plugins to be used and distributed together with GStreamer
  12  *  and Rhythmbox. This permission is above and beyond the permissions granted
  13  *  by the GPL license by which Rhythmbox is covered. If you modify this code
  14  *  you may extend this exception to your version of the code, but you are not
  15  *  obligated to do so. If you do not wish to do so, delete this exception
  16  *  statement from your version.
  17  *
  18  *  This program is distributed in the hope that it will be useful,
  19  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  20  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21  *  GNU General Public License for more details.
  22  *
  23  *  You should have received a copy of the GNU General Public License
  24  *  along with this program; if not, write to the Free Software
  25  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
  26  *
  27  */
  28 
  29 #define __EXTENSIONS__
  30 
  31 #include "config.h"
  32 
  33 #include <string.h>
  34 
  35 #include <gtk/gtk.h>
  36 #include <glib/gi18n.h>
  37 
  38 #include <totem-pl-parser.h>
  39 
  40 #include "mediaplayerid.h"
  41 
  42 #include "rb-generic-player-source.h"
  43 #include "rb-generic-player-playlist-source.h"
  44 #include "rb-removable-media-manager.h"
  45 #include "rb-transfer-target.h"
  46 #include "rb-device-source.h"
  47 #include "rb-debug.h"
  48 #include "rb-util.h"
  49 #include "rb-file-helpers.h"
  50 #include "rhythmdb.h"
  51 #include "rb-dialog.h"
  52 #include "rhythmdb-import-job.h"
  53 #include "rb-import-errors-source.h"
  54 #include "rb-builder-helpers.h"
  55 #include "rb-gst-media-types.h"
  56 #include "rb-sync-settings.h"
  57 #include "rb-missing-plugins.h"
  58 
  59 static void rb_generic_player_device_source_init (RBDeviceSourceInterface *interface);
  60 static void rb_generic_player_source_transfer_target_init (RBTransferTargetInterface *interface);
  61 
  62 static void impl_constructed (GObject *object);
  63 static void impl_dispose (GObject *object);
  64 static void impl_set_property (GObject *object,
  65 			       guint prop_id,
  66 			       const GValue *value,
  67 			       GParamSpec *pspec);
  68 static void impl_get_property (GObject *object,
  69 			       guint prop_id,
  70 			       GValue *value,
  71 			       GParamSpec *pspec);
  72 
  73 static void load_songs (RBGenericPlayerSource *source);
  74 
  75 static gboolean impl_show_popup (RBDisplayPage *page);
  76 static void impl_delete_thyself (RBDisplayPage *page);
  77 static void impl_get_status (RBDisplayPage *page, char **text, char **progress_text, float *progress);
  78 static void impl_selected (RBDisplayPage *page);
  79 
  80 static gboolean impl_can_paste (RBSource *source);
  81 static RBTrackTransferBatch *impl_paste (RBSource *source, GList *entries);
  82 static gboolean impl_can_delete (RBSource *source);
  83 static void impl_delete (RBSource *source);
  84 
  85 static char* impl_build_dest_uri (RBTransferTarget *target,
  86 				  RhythmDBEntry *entry,
  87 				  const char *media_type,
  88 				  const char *extension);
  89 static guint64 impl_get_capacity (RBMediaPlayerSource *source);
  90 static guint64 impl_get_free_space (RBMediaPlayerSource *source);
  91 static void impl_get_entries (RBMediaPlayerSource *source, const char *category, GHashTable *map);
  92 static void impl_delete_entries (RBMediaPlayerSource *source,
  93 				 GList *entries,
  94 				 RBMediaPlayerSourceDeleteCallback callback,
  95 				 gpointer data,
  96 				 GDestroyNotify destroy_data);
  97 static void impl_show_properties (RBMediaPlayerSource *source, GtkWidget *info_box, GtkWidget *notebook);
  98 static void impl_add_playlist (RBMediaPlayerSource *source, char *name, GList *entries);
  99 static void impl_remove_playlists (RBMediaPlayerSource *source);
 100 
 101 static char *default_get_mount_path (RBGenericPlayerSource *source);
 102 static void default_load_playlists (RBGenericPlayerSource *source);
 103 static char * default_uri_from_playlist_uri (RBGenericPlayerSource *source,
 104 					     const char *uri);
 105 static char * default_uri_to_playlist_uri (RBGenericPlayerSource *source,
 106 					   const char *uri,
 107 					   TotemPlParserType playlist_type);
 108 
 109 enum
 110 {
 111 	PROP_0,
 112 	PROP_MOUNT,
 113 	PROP_IGNORE_ENTRY_TYPE,
 114 	PROP_ERROR_ENTRY_TYPE,
 115 	PROP_DEVICE_INFO
 116 };
 117 
 118 typedef struct
 119 {
 120 	RhythmDB *db;
 121 
 122 	gboolean loaded;
 123 	RhythmDBImportJob *import_job;
 124 	gint load_playlists_id;
 125 	GList *playlists;
 126 	RBSource *import_errors;
 127 
 128 	char *mount_path;
 129 
 130 	/* entry types */
 131 	RhythmDBEntryType *ignore_type;
 132 	RhythmDBEntryType *error_type;
 133 
 134 	/* information derived from volume */
 135 	gboolean read_only;
 136 
 137 	MPIDDevice *device_info;
 138 	GMount *mount;
 139 
 140 } RBGenericPlayerSourcePrivate;
 141 
 142 G_DEFINE_DYNAMIC_TYPE_EXTENDED (
 143 	RBGenericPlayerSource,
 144 	rb_generic_player_source,
 145 	RB_TYPE_MEDIA_PLAYER_SOURCE,
 146 	0,
 147 	G_IMPLEMENT_INTERFACE_DYNAMIC (RB_TYPE_DEVICE_SOURCE, rb_generic_player_device_source_init)
 148 	G_IMPLEMENT_INTERFACE_DYNAMIC (RB_TYPE_TRANSFER_TARGET, rb_generic_player_source_transfer_target_init))
 149 
 150 #define GET_PRIVATE(o)   (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_GENERIC_PLAYER_SOURCE, RBGenericPlayerSourcePrivate))
 151 
 152 static void
 153 rb_generic_player_source_class_init (RBGenericPlayerSourceClass *klass)
 154 {
 155 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 156 	RBDisplayPageClass *page_class = RB_DISPLAY_PAGE_CLASS (klass);
 157 	RBSourceClass *source_class = RB_SOURCE_CLASS (klass);
 158 	RBMediaPlayerSourceClass *mps_class = RB_MEDIA_PLAYER_SOURCE_CLASS (klass);
 159 
 160 	object_class->set_property = impl_set_property;
 161 	object_class->get_property = impl_get_property;
 162 	object_class->constructed = impl_constructed;
 163 	object_class->dispose = impl_dispose;
 164 
 165 	page_class->show_popup = impl_show_popup;
 166 	page_class->delete_thyself = impl_delete_thyself;
 167 	page_class->get_status = impl_get_status;
 168 	page_class->selected = impl_selected;
 169 
 170 	source_class->impl_can_delete = impl_can_delete;
 171 	source_class->impl_delete = impl_delete;
 172 	source_class->impl_can_move_to_trash = (RBSourceFeatureFunc) rb_false_function;
 173 	source_class->impl_can_paste = impl_can_paste;
 174 	source_class->impl_paste = impl_paste;
 175 	source_class->impl_want_uri = rb_device_source_want_uri;
 176 	source_class->impl_uri_is_source = rb_device_source_uri_is_source;
 177 
 178 	mps_class->impl_get_entries = impl_get_entries;
 179 	mps_class->impl_get_capacity = impl_get_capacity;
 180 	mps_class->impl_get_free_space = impl_get_free_space;
 181 	mps_class->impl_delete_entries = impl_delete_entries;
 182 	mps_class->impl_show_properties = impl_show_properties;
 183 	mps_class->impl_add_playlist = impl_add_playlist;
 184 	mps_class->impl_remove_playlists = impl_remove_playlists;
 185 
 186 	klass->impl_get_mount_path = default_get_mount_path;
 187 	klass->impl_load_playlists = default_load_playlists;
 188 	klass->impl_uri_from_playlist_uri = default_uri_from_playlist_uri;
 189 	klass->impl_uri_to_playlist_uri = default_uri_to_playlist_uri;
 190 
 191 	g_object_class_install_property (object_class,
 192 					 PROP_ERROR_ENTRY_TYPE,
 193 					 g_param_spec_object ("error-entry-type",
 194 							      "Error entry type",
 195 							      "Entry type to use for import error entries added by this source",
 196 							      RHYTHMDB_TYPE_ENTRY_TYPE,
 197 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 198 	g_object_class_install_property (object_class,
 199 					 PROP_IGNORE_ENTRY_TYPE,
 200 					 g_param_spec_object ("ignore-entry-type",
 201 							      "Ignore entry type",
 202 							      "Entry type to use for ignore entries added by this source",
 203 							      RHYTHMDB_TYPE_ENTRY_TYPE,
 204 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 205 	g_object_class_install_property (object_class,
 206 					 PROP_DEVICE_INFO,
 207 					 g_param_spec_object ("device-info",
 208 							      "device info",
 209 							      "device information object",
 210 							      MPID_TYPE_DEVICE,
 211 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 212 	g_object_class_install_property (object_class,
 213 					 PROP_MOUNT,
 214 					 g_param_spec_object ("mount",
 215 							      "mount",
 216 							      "GMount object",
 217 							      G_TYPE_MOUNT,
 218 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 219 
 220 	g_type_class_add_private (klass, sizeof (RBGenericPlayerSourcePrivate));
 221 }
 222 
 223 static void
 224 rb_generic_player_device_source_init (RBDeviceSourceInterface *interface)
 225 {
 226 	/* nothing */
 227 }
 228 
 229 static void
 230 rb_generic_player_source_transfer_target_init (RBTransferTargetInterface *interface)
 231 {
 232 	interface->build_dest_uri = impl_build_dest_uri;
 233 }
 234 
 235 static void
 236 rb_generic_player_source_class_finalize (RBGenericPlayerSourceClass *klass)
 237 {
 238 }
 239 
 240 static void
 241 rb_generic_player_source_init (RBGenericPlayerSource *source)
 242 {
 243 
 244 }
 245 
 246 static void
 247 impl_constructed (GObject *object)
 248 {
 249 	RBGenericPlayerSource *source;
 250 	RBGenericPlayerSourcePrivate *priv;
 251 	RhythmDBEntryType *entry_type;
 252 	char **playlist_formats;
 253 	char **output_formats;
 254 	char *mount_name;
 255 	RBShell *shell;
 256 	GFile *root;
 257 	GFileInfo *info;
 258 	GError *error = NULL;
 259 
 260 	RB_CHAIN_GOBJECT_METHOD (rb_generic_player_source_parent_class, constructed, object);
 261 	source = RB_GENERIC_PLAYER_SOURCE (object);
 262 
 263 	priv = GET_PRIVATE (source);
 264 
 265 	rb_device_source_set_display_details (RB_DEVICE_SOURCE (source));
 266 
 267 	g_object_get (source,
 268 		      "shell", &shell,
 269 		      "entry-type", &entry_type,
 270 		      NULL);
 271 
 272 	g_object_get (shell, "db", &priv->db, NULL);
 273 
 274 	priv->import_errors = rb_import_errors_source_new (shell,
 275 							   priv->error_type,
 276 							   entry_type,
 277 							   priv->ignore_type);
 278 
 279 	g_object_unref (shell);
 280 
 281 	root = g_mount_get_root (priv->mount);
 282 	mount_name = g_mount_get_name (priv->mount);
 283 
 284 	info = g_file_query_filesystem_info (root, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, NULL, &error);
 285 	if (error != NULL) {
 286 		rb_debug ("error querying filesystem info for %s: %s", mount_name, error->message);
 287 		g_error_free (error);
 288 		priv->read_only = FALSE;
 289 	} else {
 290 		priv->read_only = g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY);
 291 		g_object_unref (info);
 292 	}
 293 
 294 	g_free (mount_name);
 295 	g_object_unref (root);
 296 
 297 	g_object_get (priv->device_info, "playlist-formats", &playlist_formats, NULL);
 298 	if (playlist_formats != NULL && g_strv_length (playlist_formats) > 0) {
 299 		g_object_set (entry_type, "has-playlists", TRUE, NULL);
 300 	}
 301 	g_strfreev (playlist_formats);
 302 	g_object_unref (entry_type);
 303 
 304 	g_object_get (priv->device_info, "output-formats", &output_formats, NULL);
 305 	if (output_formats != NULL) {
 306 		GstEncodingTarget *target;
 307 		int i;
 308 
 309 		target = gst_encoding_target_new ("generic-player", "device", "", NULL);
 310 		for (i = 0; output_formats[i] != NULL; i++) {
 311 			const char *media_type = rb_gst_mime_type_to_media_type (output_formats[i]);
 312 			if (media_type != NULL) {
 313 				GstEncodingProfile *profile;
 314 				profile = rb_gst_get_encoding_profile (media_type);
 315 				if (profile != NULL) {
 316 					gst_encoding_target_add_profile (target, profile);
 317 				}
 318 			}
 319 		}
 320 		g_object_set (source, "encoding-target", target, NULL);
 321 	}
 322 	g_strfreev (output_formats);
 323 
 324 }
 325 
 326 static void
 327 impl_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
 328 {
 329 	RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (object);
 330 
 331 	switch (prop_id) {
 332 	case PROP_IGNORE_ENTRY_TYPE:
 333 		priv->ignore_type = g_value_get_object (value);
 334 		break;
 335 	case PROP_ERROR_ENTRY_TYPE:
 336 		priv->error_type = g_value_get_object (value);
 337 		break;
 338 	case PROP_DEVICE_INFO:
 339 		priv->device_info = g_value_dup_object (value);
 340 		break;
 341 	case PROP_MOUNT:
 342 		priv->mount = g_value_dup_object (value);
 343 		break;
 344 	default:
 345 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 346 		break;
 347 	}
 348 }
 349 
 350 static void
 351 impl_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
 352 {
 353 	RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (object);
 354 
 355 	switch (prop_id) {
 356 	case PROP_IGNORE_ENTRY_TYPE:
 357 		g_value_set_object (value, priv->ignore_type);
 358 		break;
 359 	case PROP_ERROR_ENTRY_TYPE:
 360 		g_value_set_object (value, priv->error_type);
 361 		break;
 362 	case PROP_DEVICE_INFO:
 363 		g_value_set_object (value, priv->device_info);
 364 		break;
 365 	case PROP_MOUNT:
 366 		g_value_set_object (value, priv->mount);
 367 		break;
 368 	default:
 369 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 370 		break;
 371 	}
 372 }
 373 
 374 static void
 375 impl_dispose (GObject *object)
 376 {
 377 	RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (object);
 378 
 379 	if (priv->load_playlists_id != 0) {
 380 		g_source_remove (priv->load_playlists_id);
 381 		priv->load_playlists_id = 0;
 382 	}
 383 
 384 	if (priv->db != NULL) {
 385 		if (priv->ignore_type != NULL) {
 386 			rhythmdb_entry_delete_by_type (priv->db, priv->ignore_type);
 387 			g_object_unref (priv->ignore_type);
 388 			priv->ignore_type = NULL;
 389 		}
 390 		if (priv->error_type != NULL) {
 391 			rhythmdb_entry_delete_by_type (priv->db, priv->error_type);
 392 			g_object_unref (priv->error_type);
 393 			priv->error_type = NULL;
 394 		}
 395 
 396 		g_object_unref (priv->db);
 397 		priv->db = NULL;
 398 	}
 399 
 400 	if (priv->import_job != NULL) {
 401 		rhythmdb_import_job_cancel (priv->import_job);
 402 		g_object_unref (priv->import_job);
 403 		priv->import_job = NULL;
 404 	}
 405 
 406 	if (priv->device_info != NULL) {
 407 		g_object_unref (priv->device_info);
 408 		priv->device_info = NULL;
 409 	}
 410 
 411 	if (priv->mount != NULL) {
 412 		g_object_unref (priv->mount);
 413 		priv->mount = NULL;
 414 	}
 415 
 416 	G_OBJECT_CLASS (rb_generic_player_source_parent_class)->dispose (object);
 417 }
 418 
 419 RBSource *
 420 rb_generic_player_source_new (GObject *plugin, RBShell *shell, GMount *mount, MPIDDevice *device_info)
 421 {
 422 	RBGenericPlayerSource *source;
 423 	RhythmDBEntryType *entry_type;
 424 	RhythmDBEntryType *error_type;
 425 	RhythmDBEntryType *ignore_type;
 426 	RhythmDB *db;
 427 	GVolume *volume;
 428 	GSettings *settings;
 429 	char *name;
 430 	char *path;
 431 
 432 	volume = g_mount_get_volume (mount);
 433 
 434 	g_object_get (shell, "db", &db, NULL);
 435 	path = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
 436 
 437 	name = g_strdup_printf ("generic audio player: %s", path);
 438 	entry_type = g_object_new (RHYTHMDB_TYPE_ENTRY_TYPE,
 439 				   "db", db,
 440 				   "name", name,
 441 				   "save-to-disk", FALSE,
 442 				   "category", RHYTHMDB_ENTRY_NORMAL,
 443 				   NULL);
 444 	rhythmdb_register_entry_type (db, entry_type);
 445 	g_free (name);
 446 
 447 	name = g_strdup_printf ("generic audio player (ignore): %s", path);
 448 	ignore_type = g_object_new (RHYTHMDB_TYPE_ENTRY_TYPE,
 449 				    "db", db,
 450 				    "name", name,
 451 				    "save-to-disk", FALSE,
 452 				    "category", RHYTHMDB_ENTRY_VIRTUAL,
 453 				    NULL);
 454 	rhythmdb_register_entry_type (db, ignore_type);
 455 	g_free (name);
 456 
 457 	name = g_strdup_printf ("generic audio player (errors): %s", path);
 458 	error_type = g_object_new (RHYTHMDB_TYPE_ENTRY_TYPE,
 459 				   "db", db,
 460 				   "name", name,
 461 				   "save-to-disk", FALSE,
 462 				   "category", RHYTHMDB_ENTRY_VIRTUAL,
 463 				   NULL);
 464 	rhythmdb_register_entry_type (db, error_type);
 465 	g_free (name);
 466 
 467 	g_object_unref (db);
 468 	g_object_unref (volume);
 469 	g_free (path);
 470 
 471 	settings = g_settings_new ("org.gnome.rhythmbox.plugins.generic-player");
 472 	source = RB_GENERIC_PLAYER_SOURCE (g_object_new (RB_TYPE_GENERIC_PLAYER_SOURCE,
 473 							 "plugin", plugin,
 474 							 "entry-type", entry_type,
 475 							 "ignore-entry-type", ignore_type,
 476 							 "error-entry-type", error_type,
 477 							 "mount", mount,
 478 							 "shell", shell,
 479 							 "device-info", device_info,
 480 							 "load-status", RB_SOURCE_LOAD_STATUS_LOADING,
 481 							 "settings", g_settings_get_child (settings, "source"),
 482 							 "toolbar-path", "/GenericPlayerSourceToolBar",
 483 							 NULL));
 484 	g_object_unref (settings);
 485 
 486 	rb_shell_register_entry_type_for_source (shell, RB_SOURCE (source), entry_type);
 487 
 488 	return RB_SOURCE (source);
 489 }
 490 
 491 static void
 492 impl_delete_thyself (RBDisplayPage *page)
 493 {
 494 	GList *pl;
 495 	GList *p;
 496 	RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (page);
 497 
 498 	/* take a copy of the list first, as playlist_deleted_cb modifies priv->playlists */
 499 	pl = g_list_copy (priv->playlists);
 500 	for (p = pl; p != NULL; p = p->next) {
 501 		RBDisplayPage *playlist_page = RB_DISPLAY_PAGE (p->data);
 502 		rb_display_page_delete_thyself (playlist_page);
 503 	}
 504 	g_list_free (priv->playlists);
 505 	g_list_free (pl);
 506 	priv->playlists = NULL;
 507 
 508 	if (priv->import_errors != NULL) {
 509 		rb_display_page_delete_thyself (RB_DISPLAY_PAGE (priv->import_errors));
 510 		priv->import_errors = NULL;
 511 	}
 512 
 513 	RB_DISPLAY_PAGE_CLASS (rb_generic_player_source_parent_class)->delete_thyself (page);
 514 }
 515 
 516 static gboolean
 517 ensure_loaded (RBGenericPlayerSource *source)
 518 {
 519 	RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
 520 	RBSourceLoadStatus status;
 521 
 522 	if (priv->loaded) {
 523 		g_object_get (source, "load-status", &status, NULL);
 524 		return (status == RB_SOURCE_LOAD_STATUS_LOADED);
 525 	} else {
 526 		priv->loaded = TRUE;
 527 		rb_media_player_source_load (RB_MEDIA_PLAYER_SOURCE (source));
 528 		load_songs (source);
 529 		return FALSE;
 530 	}
 531 }
 532 
 533 static void
 534 impl_selected (RBDisplayPage *page)
 535 {
 536 	ensure_loaded (RB_GENERIC_PLAYER_SOURCE (page));
 537 }
 538 
 539 static void
 540 import_complete_cb (RhythmDBImportJob *job, int total, RBGenericPlayerSource *source)
 541 {
 542 	RBGenericPlayerSourceClass *klass = RB_GENERIC_PLAYER_SOURCE_GET_CLASS (source);
 543 	RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
 544 	RBShell *shell;
 545 
 546 	GDK_THREADS_ENTER ();
 547 
 548 	g_object_get (source, "shell", &shell, NULL);
 549 	rb_shell_append_display_page (shell, RB_DISPLAY_PAGE (priv->import_errors), RB_DISPLAY_PAGE (source));
 550 	g_object_unref (shell);
 551 
 552 	if (klass->impl_load_playlists)
 553 		klass->impl_load_playlists (source);
 554 
 555 	g_object_unref (priv->import_job);
 556 	priv->import_job = NULL;
 557 
 558 	rb_display_page_notify_status_changed (RB_DISPLAY_PAGE (source));
 559 	g_object_set (source, "load-status", RB_SOURCE_LOAD_STATUS_LOADED, NULL);
 560 
 561 	rb_transfer_target_transfer (RB_TRANSFER_TARGET (source), NULL, FALSE);
 562 
 563 	GDK_THREADS_LEAVE ();
 564 }
 565 
 566 static void
 567 import_status_changed_cb (RhythmDBImportJob *job, int total, int imported, RBGenericPlayerSource *source)
 568 {
 569 	rb_display_page_notify_status_changed (RB_DISPLAY_PAGE (source));
 570 }
 571 
 572 static void
 573 load_songs (RBGenericPlayerSource *source)
 574 {
 575 	RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
 576 	RhythmDBEntryType *entry_type;
 577 	char **audio_folders;
 578 	char *mount_path;
 579 
 580 	mount_path = rb_generic_player_source_get_mount_path (source);
 581 	g_object_get (source, "entry-type", &entry_type, NULL);
 582 
 583 	/* if we have a set of folders on the device containing audio files,
 584 	 * load only those folders, otherwise add the whole volume.
 585 	 */
 586 	priv->import_job = rhythmdb_import_job_new (priv->db, entry_type, priv->ignore_type, priv->error_type);
 587 
 588 	g_signal_connect_object (priv->import_job, "complete", G_CALLBACK (import_complete_cb), source, 0);
 589 	g_signal_connect_object (priv->import_job, "status-changed", G_CALLBACK (import_status_changed_cb), source, 0);
 590 
 591 	g_object_get (priv->device_info, "audio-folders", &audio_folders, NULL);
 592 	if (audio_folders != NULL && g_strv_length (audio_folders) > 0) {
 593 		int af;
 594 		for (af=0; audio_folders[af] != NULL; af++) {
 595 			char *path;
 596 			path = rb_uri_append_path (mount_path, audio_folders[af]);
 597 			rb_debug ("loading songs from device audio folder %s", path);
 598 			rhythmdb_import_job_add_uri (priv->import_job, path);
 599 			g_free (path);
 600 		}
 601 	} else {
 602 		rb_debug ("loading songs from device mount path %s", mount_path);
 603 		rhythmdb_import_job_add_uri (priv->import_job, mount_path);
 604 	}
 605 	g_strfreev (audio_folders);
 606 
 607 	rhythmdb_import_job_start (priv->import_job);
 608 
 609 	g_object_unref (entry_type);
 610 	g_free (mount_path);
 611 }
 612 
 613 char *
 614 rb_generic_player_source_get_mount_path (RBGenericPlayerSource *source)
 615 {
 616 	RBGenericPlayerSourceClass *klass = RB_GENERIC_PLAYER_SOURCE_GET_CLASS (source);
 617 
 618 	return klass->impl_get_mount_path (source);
 619 }
 620 
 621 static char *
 622 default_get_mount_path (RBGenericPlayerSource *source)
 623 {
 624 	RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
 625 
 626 	if (priv->mount_path == NULL) {
 627 		GFile *root;
 628 
 629 		root = g_mount_get_root (priv->mount);
 630 		if (root != NULL) {
 631 			priv->mount_path = g_file_get_uri (root);
 632 			g_object_unref (root);
 633 		}
 634 	}
 635 
 636 	return g_strdup (priv->mount_path);
 637 }
 638 
 639 gboolean
 640 rb_generic_player_is_mount_player (GMount *mount, MPIDDevice *device_info)
 641 {
 642 	char **protocols;
 643 	gboolean result = FALSE;
 644 	int i;
 645 
 646 	/* claim anything with 'storage' as an access protocol */
 647 	g_object_get (device_info, "access-protocols", &protocols, NULL);
 648 	if (protocols != NULL) {
 649 		for (i = 0; protocols[i] != NULL; i++) {
 650 			if (g_str_equal (protocols[i], "storage")) {
 651 				result = TRUE;
 652 				break;
 653 			}
 654 		}
 655 		g_strfreev (protocols);
 656 	}
 657 
 658 	return result;
 659 }
 660 
 661 static gboolean
 662 impl_show_popup (RBDisplayPage *page)
 663 {
 664 	_rb_display_page_show_popup (page, "/GenericPlayerSourcePopup");
 665 	return TRUE;
 666 }
 667 
 668 static void
 669 impl_get_status (RBDisplayPage *page, char **text, char **progress_text, float *progress)
 670 {
 671 	RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (page);
 672 
 673 	/* get default status text first */
 674 	RB_DISPLAY_PAGE_CLASS (rb_generic_player_source_parent_class)->get_status (page, text, progress_text, progress);
 675 
 676 	/* override with bits of import status */
 677 	if (priv->import_job != NULL) {
 678 		_rb_source_set_import_status (RB_SOURCE (page), priv->import_job, progress_text, progress);
 679 	}
 680 }
 681 
 682 /* code for playlist loading */
 683 
 684 static void
 685 playlist_deleted_cb (RBSource *playlist, RBGenericPlayerSource *source)
 686 {
 687 	RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
 688 	GList *p;
 689 
 690 	p = g_list_find (priv->playlists, playlist);
 691 	if (p != NULL) {
 692 		priv->playlists = g_list_delete_link (priv->playlists, p);
 693 		g_object_unref (playlist);
 694 	}
 695 }
 696 
 697 void
 698 rb_generic_player_source_add_playlist (RBGenericPlayerSource *source,
 699 				       RBShell *shell,
 700 				       RBSource *playlist)
 701 {
 702 	RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
 703 	g_object_ref (playlist);
 704 	priv->playlists = g_list_prepend (priv->playlists, playlist);
 705 
 706 	g_signal_connect_object (playlist, "deleted", G_CALLBACK (playlist_deleted_cb), source, 0);
 707 
 708 	rb_shell_append_display_page (shell, RB_DISPLAY_PAGE (playlist), RB_DISPLAY_PAGE (source));
 709 }
 710 
 711 
 712 
 713 static char *
 714 default_uri_from_playlist_uri (RBGenericPlayerSource *source, const char *uri)
 715 {
 716 	char *mount_uri;
 717 	char *full_uri;
 718 
 719 	mount_uri = rb_generic_player_source_get_mount_path (source);
 720 	if (g_str_has_prefix (uri, mount_uri)) {
 721 		return g_strdup (uri);
 722 	}
 723 
 724 	full_uri = rb_uri_append_uri (mount_uri, uri);
 725 	g_free (mount_uri);
 726 
 727 	rb_debug ("%s => %s", uri, full_uri);
 728 	return full_uri;
 729 }
 730 
 731 static char *
 732 default_uri_to_playlist_uri (RBGenericPlayerSource *source, const char *uri, TotemPlParserType playlist_type)
 733 {
 734 	char *mount_uri;
 735 	char *playlist_uri;
 736 
 737 	switch (playlist_type) {
 738 	case TOTEM_PL_PARSER_IRIVER_PLA:
 739 		/* we need absolute paths within the device filesystem for this format */
 740 		mount_uri = rb_generic_player_source_get_mount_path (source);
 741 		if (g_str_has_prefix (uri, mount_uri) == FALSE) {
 742 			rb_debug ("uri %s is not under device mount uri %s", uri, mount_uri);
 743 			return NULL;
 744 		}
 745 
 746 		playlist_uri = g_strdup_printf ("file://%s", uri + strlen (mount_uri));
 747 		return playlist_uri;
 748 
 749 	case TOTEM_PL_PARSER_M3U_DOS:
 750 	case TOTEM_PL_PARSER_M3U:
 751 	case TOTEM_PL_PARSER_PLS:
 752 	default:
 753 		/* leave the URI as-is, so we end up with relative paths in the playlist file */
 754 		return g_strdup (uri);
 755 	}
 756 }
 757 
 758 char *
 759 rb_generic_player_source_uri_from_playlist_uri (RBGenericPlayerSource *source, const char *uri)
 760 {
 761 	RBGenericPlayerSourceClass *klass = RB_GENERIC_PLAYER_SOURCE_GET_CLASS (source);
 762 
 763 	return klass->impl_uri_from_playlist_uri (source, uri);
 764 }
 765 
 766 char *
 767 rb_generic_player_source_uri_to_playlist_uri (RBGenericPlayerSource *source, const char *uri, TotemPlParserType playlist_type)
 768 {
 769 	RBGenericPlayerSourceClass *klass = RB_GENERIC_PLAYER_SOURCE_GET_CLASS (source);
 770 
 771 	return klass->impl_uri_to_playlist_uri (source, uri, playlist_type);
 772 }
 773 
 774 static void
 775 load_playlist_file (RBGenericPlayerSource *source,
 776 		    const char *playlist_path,
 777 		    const char *rel_path)
 778 {
 779 	RhythmDBEntryType *entry_type;
 780 	RBGenericPlayerPlaylistSource *playlist;
 781 	RBShell *shell;
 782 	char *mount_path;
 783 
 784 	g_object_get (source,
 785 		      "shell", &shell,
 786 		      "entry-type", &entry_type,
 787 		      NULL);
 788 
 789 	mount_path = rb_generic_player_source_get_mount_path (source);
 790 	rb_debug ("loading playlist %s", playlist_path);
 791 	playlist = RB_GENERIC_PLAYER_PLAYLIST_SOURCE (
 792 			rb_generic_player_playlist_source_new (shell,
 793 							       source,
 794 							       playlist_path,
 795 							       mount_path,
 796 							       entry_type));
 797 
 798 	if (playlist != NULL) {
 799 		rb_generic_player_source_add_playlist (source, shell, RB_SOURCE (playlist));
 800 	}
 801 
 802 	g_object_unref (entry_type);
 803 	g_object_unref (shell);
 804 	g_free (mount_path);
 805 }
 806 
 807 static gboolean
 808 visit_playlist_dirs (GFile *file,
 809 		     gboolean dir,
 810 		     RBGenericPlayerSource *source)
 811 {
 812 	char *basename;
 813 	char *uri;
 814 	RhythmDBEntry *entry;
 815 	RhythmDBEntryType *entry_type;
 816 	RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source);
 817 
 818 	if (dir) {
 819 		return TRUE;
 820 	}
 821 
 822 	/* check if we've already got an entry 
 823 	 * for this file, just to save some i/o.
 824 	 */
 825 	uri = g_file_get_uri (file);
 826 	entry = rhythmdb_entry_lookup_by_location (priv->db, uri);
 827 	g_free (uri);
 828 	if (entry != NULL) {
 829 		gboolean is_song;
 830 
 831 		is_song = FALSE;
Value stored to 'is_song' is never read
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

832 833 g_object_get (source, "entry-type", &entry_type, NULL); 834 is_song = (rhythmdb_entry_get_entry_type (entry) == entry_type); 835 g_object_unref (entry_type); 836 837 if (is_song) { 838 rb_debug ("%s was loaded as a song", 839 rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION)); 840 return TRUE; 841 } 842 } 843 844 basename = g_file_get_basename (file); 845 if (strcmp (basename, ".is_audio_player") != 0) { 846 char *playlist_path; 847 playlist_path = g_file_get_path (file); 848 load_playlist_file (source, playlist_path, basename); 849 g_free (playlist_path); 850 } 851 852 g_free (basename); 853 854 return TRUE; 855 } 856 857 static void 858 default_load_playlists (RBGenericPlayerSource *source) 859 { 860 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source); 861 char *mount_path; 862 char *playlist_path; 863 char *full_playlist_path; 864 char **playlist_formats; 865 866 mount_path = rb_generic_player_source_get_mount_path (source); 867 868 playlist_path = rb_generic_player_source_get_playlist_path (RB_GENERIC_PLAYER_SOURCE (source)); 869 if (playlist_path) { 870 871 /* If the device only supports a single playlist, just load that */ 872 if (g_str_has_suffix (playlist_path, ".m3u") || 873 g_str_has_suffix (playlist_path, ".pls")) { 874 full_playlist_path = rb_uri_append_path (mount_path, playlist_path); 875 if (rb_uri_exists (full_playlist_path)) { 876 load_playlist_file (source, full_playlist_path, playlist_path); 877 } 878 879 g_free (full_playlist_path); 880 g_free (playlist_path); 881 return; 882 } 883 884 /* Otherwise, limit the search to the device's playlist folder */ 885 full_playlist_path = rb_uri_append_path (mount_path, playlist_path); 886 rb_debug ("constructed playlist search path %s", full_playlist_path); 887 } else { 888 full_playlist_path = g_strdup (mount_path); 889 } 890 891 /* only try to load playlists if the device has at least one playlist format */ 892 g_object_get (priv->device_info, "playlist-formats", &playlist_formats, NULL); 893 if (playlist_formats != NULL && g_strv_length (playlist_formats) > 0) { 894 rb_debug ("searching for playlists in %s", playlist_path); 895 rb_uri_handle_recursively (full_playlist_path, 896 NULL, 897 (RBUriRecurseFunc) visit_playlist_dirs, 898 source); 899 } 900 g_strfreev (playlist_formats); 901 902 g_free (playlist_path); 903 g_free (full_playlist_path); 904 g_free (mount_path); 905 } 906 907 static gboolean 908 impl_can_paste (RBSource *source) 909 { 910 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source); 911 912 return (priv->read_only == FALSE); 913 } 914 915 static RBTrackTransferBatch * 916 impl_paste (RBSource *source, GList *entries) 917 { 918 gboolean defer; 919 920 defer = (ensure_loaded (RB_GENERIC_PLAYER_SOURCE (source)) == FALSE); 921 return rb_transfer_target_transfer (RB_TRANSFER_TARGET (source), entries, defer); 922 } 923 924 static gboolean 925 impl_can_delete (RBSource *source) 926 { 927 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source); 928 929 return (priv->read_only == FALSE); 930 } 931 932 static gboolean 933 can_delete_directory (RBGenericPlayerSource *source, GFile *dir) 934 { 935 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source); 936 gboolean result; 937 GMount *mount; 938 GFile *root; 939 char **audio_folders; 940 int i; 941 942 g_object_get (source, "mount", &mount, NULL); 943 root = g_mount_get_root (mount); 944 g_object_unref (mount); 945 946 /* can't delete the root dir */ 947 if (g_file_equal (dir, root)) { 948 rb_debug ("refusing to delete device root dir"); 949 g_object_unref (root); 950 return FALSE; 951 } 952 953 /* can't delete the device's audio folders */ 954 result = TRUE; 955 g_object_get (priv->device_info, "audio-folders", &audio_folders, NULL); 956 if (audio_folders != NULL && g_strv_length (audio_folders) > 0) { 957 for (i = 0; audio_folders[i] != NULL; i++) { 958 GFile *check; 959 960 check = g_file_resolve_relative_path (root, audio_folders[i]); 961 if (g_file_equal (dir, check)) { 962 rb_debug ("refusing to delete device audio folder %s", audio_folders[i]); 963 result = FALSE; 964 } 965 g_object_unref (check); 966 } 967 } 968 g_strfreev (audio_folders); 969 970 /* can delete anything else */ 971 g_object_unref (root); 972 return result; 973 } 974 975 void 976 rb_generic_player_source_delete_entries (RBGenericPlayerSource *source, GList *entries) 977 { 978 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source); 979 GList *tem; 980 981 if (priv->read_only != FALSE) 982 return; 983 984 for (tem = entries; tem != NULL; tem = tem->next) { 985 RhythmDBEntry *entry; 986 const char *uri; 987 GFile *file; 988 GFile *dir; 989 990 entry = tem->data; 991 uri = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION); 992 file = g_file_new_for_uri (uri); 993 g_file_delete (file, NULL, NULL); 994 995 /* now walk up the directory structure and delete empty dirs 996 * until we reach the root or one of the device's audio folders. 997 */ 998 dir = g_file_get_parent (file); 999 while (can_delete_directory (source, dir)) { 1000 GFile *parent; 1001 char *path; 1002 1003 path = g_file_get_path (dir); 1004 rb_debug ("trying to delete %s", path); 1005 g_free (path); 1006 1007 if (g_file_delete (dir, NULL, NULL) == FALSE) { 1008 break; 1009 } 1010 1011 parent = g_file_get_parent (dir); 1012 if (parent == NULL) { 1013 break; 1014 } 1015 g_object_unref (dir); 1016 dir = parent; 1017 } 1018 1019 g_object_unref (dir); 1020 g_object_unref (file); 1021 1022 rhythmdb_entry_delete (priv->db, entry); 1023 } 1024 1025 rhythmdb_commit (priv->db); 1026 } 1027 1028 static void 1029 impl_delete (RBSource *source) 1030 { 1031 RBEntryView *view; 1032 GList *sel; 1033 1034 view = rb_source_get_entry_view (source); 1035 sel = rb_entry_view_get_selected_entries (view); 1036 1037 rb_generic_player_source_delete_entries (RB_GENERIC_PLAYER_SOURCE (source), sel); 1038 g_list_foreach (sel, (GFunc)rhythmdb_entry_unref, NULL); 1039 g_list_free (sel); 1040 } 1041 1042 static char * 1043 sanitize_path (const char *str) 1044 { 1045 char *res = NULL; 1046 char *s; 1047 1048 /* Skip leading periods, otherwise files disappear... */ 1049 while (*str == '.') 1050 str++; 1051 1052 s = g_strdup (str); 1053 g_strdelimit (s, "/", '-'); 1054 res = g_uri_escape_string (s, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH_ELEMENT, TRUE); 1055 g_free (s); 1056 return res; 1057 } 1058 1059 static char * 1060 impl_build_dest_uri (RBTransferTarget *target, 1061 RhythmDBEntry *entry, 1062 const char *media_type, 1063 const char *extension) 1064 { 1065 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (target); 1066 const char *in_artist; 1067 char *artist, *album, *title; 1068 gulong track_number, disc_number; 1069 const char *folders; 1070 char **audio_folders; 1071 char *mount_path; 1072 char *number; 1073 char *file = NULL; 1074 char *path; 1075 char *ext; 1076 1077 rb_debug ("building dest uri for entry at %s", rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION)); 1078 1079 if (extension != NULL) { 1080 ext = g_strconcat (".", extension, NULL); 1081 } else { 1082 ext = g_strdup (""); 1083 } 1084 1085 in_artist = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM_ARTIST); 1086 if (in_artist[0] == '\0') { 1087 in_artist = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ARTIST); 1088 } 1089 artist = sanitize_path (in_artist); 1090 album = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_ALBUM)); 1091 title = sanitize_path (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_TITLE)); 1092 1093 /* we really do need to fix this so untagged entries actually have NULL rather than 1094 * a translated string. 1095 */ 1096 if (strcmp (artist, _("Unknown")) == 0 && strcmp (album, _("Unknown")) == 0 && 1097 g_str_has_suffix (rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION), title)) { 1098 /* file isn't tagged, so just use the filename as-is, replacing the extension */ 1099 char *p; 1100 1101 p = g_utf8_strrchr (title, -1, '.'); 1102 if (p != NULL) { 1103 *p = '\0'; 1104 } 1105 file = g_strdup_printf ("%s%s", title, ext); 1106 } 1107 1108 if (file == NULL) { 1109 int folder_depth; 1110 1111 track_number = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_TRACK_NUMBER); 1112 disc_number = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DISC_NUMBER); 1113 if (disc_number > 0) 1114 number = g_strdup_printf ("%.02u.%.02u", (guint)disc_number, (guint)track_number); 1115 else 1116 number = g_strdup_printf ("%.02u", (guint)track_number); 1117 1118 g_object_get (priv->device_info, "folder-depth", &folder_depth, NULL); 1119 switch (folder_depth) { 1120 case 0: 1121 /* artist - album - number - title */ 1122 file = g_strdup_printf ("%s - %s - %s - %s%s", 1123 artist, album, number, title, ext); 1124 break; 1125 1126 case 1: 1127 /* artist - album/number - title */ 1128 file = g_strdup_printf ("%s - %s" G_DIR_SEPARATOR_S "%s - %s%s", 1129 artist, album, number, title, ext); 1130 break; 1131 1132 default: /* use this for players that don't care */ 1133 case 2: 1134 /* artist/album/number - title */ 1135 file = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s" G_DIR_SEPARATOR_S "%s - %s%s", 1136 artist, album, number, title, ext); 1137 break; 1138 } 1139 g_free (number); 1140 } 1141 1142 g_free (artist); 1143 g_free (album); 1144 g_free (title); 1145 g_free (ext); 1146 1147 if (file == NULL) 1148 return NULL; 1149 1150 g_object_get (priv->device_info, "audio-folders", &audio_folders, NULL); 1151 if (audio_folders != NULL && g_strv_length (audio_folders) > 0) { 1152 folders = g_strdup (audio_folders[0]); 1153 } else { 1154 folders = ""; 1155 } 1156 g_strfreev (audio_folders); 1157 1158 mount_path = rb_generic_player_source_get_mount_path (RB_GENERIC_PLAYER_SOURCE (target)); 1159 path = g_build_filename (mount_path, folders, file, NULL); 1160 g_free (file); 1161 g_free (mount_path); 1162 1163 /* TODO: check for duplicates, or just overwrite by default? */ 1164 rb_debug ("dest file is %s", path); 1165 return path; 1166 } 1167 1168 static gboolean 1169 strv_contains (char **strv, const char *s) 1170 { 1171 int i; 1172 for (i = 0; strv[i] != NULL; i++) { 1173 if (g_str_equal (strv[i], s)) 1174 return TRUE; 1175 } 1176 return FALSE; 1177 } 1178 1179 void 1180 rb_generic_player_source_set_supported_formats (RBGenericPlayerSource *source, TotemPlParser *parser) 1181 { 1182 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source); 1183 char **playlist_formats; 1184 const char *check[] = { "audio/x-mpegurl", "audio/x-scpls", "audio/x-iriver-pla" }; 1185 1186 g_object_get (priv->device_info, "playlist-formats", &playlist_formats, NULL); 1187 if (playlist_formats != NULL && g_strv_length (playlist_formats) > 0) { 1188 int i; 1189 for (i = 0; i < G_N_ELEMENTS (check); i++) { 1190 if (strv_contains (playlist_formats, check[i]) == FALSE) { 1191 totem_pl_parser_add_ignored_mimetype (parser, check[i]); 1192 } 1193 } 1194 } 1195 g_strfreev (playlist_formats); 1196 1197 totem_pl_parser_add_ignored_mimetype (parser, "x-directory/normal"); 1198 } 1199 1200 TotemPlParserType 1201 rb_generic_player_source_get_playlist_format (RBGenericPlayerSource *source) 1202 { 1203 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source); 1204 TotemPlParserType result; 1205 char **playlist_formats; 1206 1207 g_object_get (priv->device_info, "playlist-formats", &playlist_formats, NULL); 1208 1209 if (playlist_formats == NULL || g_strv_length (playlist_formats) == 0 || strv_contains (playlist_formats, "audio/x-scpls")) { 1210 result = TOTEM_PL_PARSER_PLS; 1211 } else if (strv_contains (playlist_formats, "audio/x-mpegurl")) { 1212 result = TOTEM_PL_PARSER_M3U_DOS; 1213 } else if (strv_contains (playlist_formats, "audio/x-iriver-pla")) { 1214 result = TOTEM_PL_PARSER_IRIVER_PLA; 1215 } else { 1216 /* now what? */ 1217 result = TOTEM_PL_PARSER_PLS; 1218 } 1219 1220 g_strfreev (playlist_formats); 1221 return result; 1222 } 1223 1224 char * 1225 rb_generic_player_source_get_playlist_path (RBGenericPlayerSource *source) 1226 { 1227 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source); 1228 char *path; 1229 1230 g_object_get (priv->device_info, "playlist-path", &path, NULL); 1231 if (g_str_has_suffix (path, "%File")) { 1232 path[strlen (path) - strlen("%File")] = '\0'; 1233 } 1234 return path; 1235 } 1236 1237 static guint64 1238 get_fs_property (RBGenericPlayerSource *source, const char *attr) 1239 { 1240 char *mountpoint; 1241 GFile *root; 1242 GFileInfo *info; 1243 guint64 value = 0; 1244 1245 mountpoint = rb_generic_player_source_get_mount_path (source); 1246 root = g_file_new_for_uri (mountpoint); 1247 g_free (mountpoint); 1248 1249 info = g_file_query_filesystem_info (root, attr, NULL, NULL); 1250 g_object_unref (root); 1251 if (info != NULL) { 1252 if (g_file_info_has_attribute (info, attr)) { 1253 value = g_file_info_get_attribute_uint64 (info, attr); 1254 } 1255 g_object_unref (info); 1256 } 1257 return value; 1258 } 1259 static guint64 1260 impl_get_capacity (RBMediaPlayerSource *source) 1261 { 1262 return get_fs_property (RB_GENERIC_PLAYER_SOURCE (source), G_FILE_ATTRIBUTE_FILESYSTEM_SIZE); 1263 } 1264 1265 static guint64 1266 impl_get_free_space (RBMediaPlayerSource *source) 1267 { 1268 return get_fs_property (RB_GENERIC_PLAYER_SOURCE (source), G_FILE_ATTRIBUTE_FILESYSTEM_FREE); 1269 } 1270 1271 static void 1272 impl_get_entries (RBMediaPlayerSource *source, 1273 const char *category, 1274 GHashTable *map) 1275 { 1276 RhythmDBQueryModel *model; 1277 GtkTreeIter iter; 1278 gboolean podcast; 1279 1280 /* we don't have anything else to distinguish podcasts from regular 1281 * tracks, so just use the genre. 1282 */ 1283 podcast = (g_str_equal (category, SYNC_CATEGORY_PODCAST)); 1284 1285 g_object_get (source, "base-query-model", &model, NULL); 1286 if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter) == FALSE) { 1287 g_object_unref (model); 1288 return; 1289 } 1290 1291 do { 1292 RhythmDBEntry *entry; 1293 const char *genre; 1294 entry = rhythmdb_query_model_iter_to_entry (model, &iter); 1295 genre = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_GENRE); 1296 if (g_str_equal (genre, "Podcast") == podcast) { 1297 _rb_media_player_source_add_to_map (map, entry); 1298 } 1299 } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter)); 1300 1301 g_object_unref (model); 1302 } 1303 1304 static void 1305 impl_delete_entries (RBMediaPlayerSource *source, 1306 GList *entries, 1307 RBMediaPlayerSourceDeleteCallback callback, 1308 gpointer callback_data, 1309 GDestroyNotify destroy_data) 1310 { 1311 rb_generic_player_source_delete_entries (RB_GENERIC_PLAYER_SOURCE (source), entries); 1312 1313 if (callback) { 1314 callback (source, callback_data); 1315 } 1316 if (destroy_data) { 1317 destroy_data (callback_data); 1318 } 1319 } 1320 1321 1322 static void 1323 impl_show_properties (RBMediaPlayerSource *source, GtkWidget *info_box, GtkWidget *notebook) 1324 { 1325 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source); 1326 RhythmDBQueryModel *model; 1327 GtkBuilder *builder; 1328 GtkWidget *widget; 1329 GString *str; 1330 char *device_name; 1331 char *builder_file; 1332 char *vendor_name; 1333 char *model_name; 1334 char *serial_id; 1335 GObject *plugin; 1336 char *text; 1337 GList *output_formats; 1338 GList *t; 1339 1340 g_object_get (source, "plugin", &plugin, NULL); 1341 builder_file = rb_find_plugin_data_file (plugin, "generic-player-info.ui"); 1342 g_object_unref (plugin); 1343 1344 if (builder_file == NULL) { 1345 g_warning ("Couldn't find generic-player-info.ui"); 1346 return; 1347 } 1348 1349 builder = rb_builder_load (builder_file, NULL); 1350 g_free (builder_file); 1351 1352 if (builder == NULL) { 1353 rb_debug ("Couldn't load generic-player-info.ui"); 1354 return; 1355 } 1356 1357 /* 'basic' tab stuff */ 1358 1359 widget = GTK_WIDGET (gtk_builder_get_object (builder, "generic-player-basic-info")); 1360 gtk_box_pack_start (GTK_BOX (info_box), widget, TRUE, TRUE, 0); 1361 1362 widget = GTK_WIDGET (gtk_builder_get_object (builder, "entry-device-name")); 1363 g_object_get (source, "name", &device_name, NULL); 1364 gtk_entry_set_text (GTK_ENTRY (widget), device_name); 1365 g_free (device_name); 1366 /* don't think we can support this.. 1367 g_signal_connect (widget, "focus-out-event", 1368 (GCallback)rb_mtp_source_name_changed_cb, source); 1369 */ 1370 1371 g_object_get (source, "base-query-model", &model, NULL); 1372 widget = GTK_WIDGET (gtk_builder_get_object (builder, "num-tracks")); 1373 text = g_strdup_printf ("%d", gtk_tree_model_iter_n_children (GTK_TREE_MODEL (model), NULL)); 1374 gtk_label_set_text (GTK_LABEL (widget), text); 1375 g_free (text); 1376 g_object_unref (model); 1377 1378 widget = GTK_WIDGET (gtk_builder_get_object (builder, "num-playlists")); 1379 text = g_strdup_printf ("%d", g_list_length (priv->playlists)); 1380 gtk_label_set_text (GTK_LABEL (widget), text); 1381 g_free (text); 1382 1383 /* 'advanced' tab stuff */ 1384 widget = GTK_WIDGET (gtk_builder_get_object (builder, "generic-player-advanced-tab")); 1385 gtk_notebook_append_page (GTK_NOTEBOOK (notebook), widget, gtk_label_new (_("Advanced"))); 1386 1387 g_object_get (priv->device_info, 1388 "model", &model_name, 1389 "vendor", &vendor_name, 1390 "serial", &serial_id, 1391 NULL); 1392 1393 widget = GTK_WIDGET (gtk_builder_get_object (builder, "label-model-value")); 1394 gtk_label_set_text (GTK_LABEL (widget), model_name); 1395 1396 widget = GTK_WIDGET (gtk_builder_get_object (builder, "label-manufacturer-value")); 1397 gtk_label_set_text (GTK_LABEL (widget), vendor_name); 1398 1399 widget = GTK_WIDGET (gtk_builder_get_object (builder, "label-serial-number-value")); 1400 gtk_label_set_text (GTK_LABEL (widget), serial_id); 1401 1402 g_free (model_name); 1403 g_free (vendor_name); 1404 g_free (serial_id); 1405 1406 str = g_string_new (""); 1407 output_formats = rb_transfer_target_get_format_descriptions (RB_TRANSFER_TARGET (source)); 1408 for (t = output_formats; t != NULL; t = t->next) { 1409 if (t != output_formats) { 1410 g_string_append (str, "\n"); 1411 } 1412 g_string_append (str, t->data); 1413 } 1414 rb_list_deep_free (output_formats); 1415 1416 widget = GTK_WIDGET (gtk_builder_get_object (builder, "audio-format-list")); 1417 gtk_label_set_text (GTK_LABEL (widget), str->str); 1418 g_string_free (str, TRUE); 1419 1420 g_object_unref (builder); 1421 } 1422 1423 static void 1424 impl_add_playlist (RBMediaPlayerSource *source, char *name, GList *entries) 1425 { 1426 RBSource *playlist; 1427 RhythmDBEntryType *entry_type; 1428 RBShell *shell; 1429 GList *i; 1430 1431 g_object_get (source, 1432 "shell", &shell, 1433 "entry-type", &entry_type, 1434 NULL); 1435 1436 playlist = rb_generic_player_playlist_source_new (shell, RB_GENERIC_PLAYER_SOURCE (source), NULL, NULL, entry_type); 1437 g_object_unref (entry_type); 1438 1439 rb_generic_player_source_add_playlist (RB_GENERIC_PLAYER_SOURCE (source), 1440 shell, 1441 playlist); 1442 g_object_set (playlist, "name", name, NULL); 1443 1444 for (i = entries; i != NULL; i = i->next) { 1445 rb_static_playlist_source_add_entry (RB_STATIC_PLAYLIST_SOURCE (playlist), 1446 i->data, 1447 -1); 1448 } 1449 1450 g_object_unref (shell); 1451 } 1452 1453 static void 1454 impl_remove_playlists (RBMediaPlayerSource *source) 1455 { 1456 RBGenericPlayerSourcePrivate *priv = GET_PRIVATE (source); 1457 GList *playlists; 1458 GList *t; 1459 1460 playlists = g_list_copy (priv->playlists); 1461 for (t = playlists; t != NULL; t = t->next) { 1462 RBGenericPlayerPlaylistSource *p = RB_GENERIC_PLAYER_PLAYLIST_SOURCE (t->data); 1463 rb_generic_player_playlist_delete_from_player (p); 1464 rb_display_page_delete_thyself (RB_DISPLAY_PAGE (p)); 1465 } 1466 1467 g_list_free (playlists); 1468 } 1469 1470 void 1471 _rb_generic_player_source_register_type (GTypeModule *module) 1472 { 1473 rb_generic_player_source_register_type (module); 1474 }