static void write_album_to_device (RBMtpThread *thread, LIBMTP_album_t *album, gboolean new_album) { if (new_album) { if (LIBMTP_Create_New_Album (thread->device, album) != 0) { LIBMTP_destroy_album_t (album); rb_debug ("LIBMTP_Create_New_Album failed.."); rb_mtp_thread_report_errors (thread, FALSE); } } else { if (LIBMTP_Update_Album (thread->device, album) != 0) { rb_debug ("LIBMTP_Update_Album failed.."); rb_mtp_thread_report_errors (thread, FALSE); } } }
/* this callback runs on the device handling thread, so it can call libmtp directly */ static void mtp_device_open_cb (LIBMTP_mtpdevice_t *device, RBMtpSource *source) { RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source); DeviceOpenedData *data; if (device == NULL) { /* can't delete the source on this thread, so move it to the main thread */ g_idle_add ((GSourceFunc) device_open_failed_idle, g_object_ref (source)); return; } /* set the source name to match the device, ignoring some * particular broken device names. */ data = g_new0 (DeviceOpenedData, 1); data->source = g_object_ref (source); data->name = LIBMTP_Get_Friendlyname (device); if (data->name == NULL || strcmp (data->name, "?????") == 0) { g_free (data->name); data->name = LIBMTP_Get_Modelname (device); } if (data->name == NULL) { data->name = g_strdup (_("Digital Audio Player")); } /* get some other device information that doesn't change */ priv->manufacturer = LIBMTP_Get_Manufacturername (device); priv->device_version = LIBMTP_Get_Deviceversion (device); priv->model_name = LIBMTP_Get_Modelname (device); priv->serial = LIBMTP_Get_Serialnumber (device); /* calculate the device capacity */ priv->capacity = 0; if (LIBMTP_Get_Storage (device, LIBMTP_STORAGE_SORTBY_NOTSORTED) == 0) { LIBMTP_devicestorage_t *storage; for (storage = device->storage; storage != NULL; storage = storage->next) { priv->capacity += storage->MaxCapacity; } } update_free_space_cb (device, RB_MTP_SOURCE (source)); /* figure out the set of formats supported by the device */ if (LIBMTP_Get_Supported_Filetypes (device, &data->types, &data->num_types) != 0) { rb_mtp_thread_report_errors (priv->device_thread, FALSE); } g_idle_add ((GSourceFunc) device_opened_idle, data); /* now get the track list */ rb_mtp_thread_get_track_list (priv->device_thread, (RBMtpTrackListCallback) mtp_tracklist_cb, g_object_ref (source), g_object_unref); }
static void remove_track_from_album (RBMtpThread *thread, RBMtpThreadTask *task) { LIBMTP_album_t *album; int i; album = g_hash_table_lookup (thread->albums, task->album); if (album == NULL) { rb_debug ("Couldn't find an album for %s", task->album); return; } for (i = 0; i < album->no_tracks; i++) { if (album->tracks[i] == task->track_id) { break; } } if (i == album->no_tracks) { rb_debug ("Couldn't find track %d in album %d", task->track_id, album->album_id); return; } memmove (album->tracks + i, album->tracks + i + 1, album->no_tracks - (i+1)); album->no_tracks--; if (album->no_tracks == 0) { rb_debug ("deleting empty album %d", album->album_id); if (LIBMTP_Delete_Object (thread->device, album->album_id) != 0) { rb_mtp_thread_report_errors (thread, FALSE); } g_hash_table_remove (thread->albums, task->album); } else { rb_debug ("updating album %d: %d tracks remaining", album->album_id, album->no_tracks); if (LIBMTP_Update_Album (thread->device, album) != 0) { rb_mtp_thread_report_errors (thread, FALSE); } } }
static void update_free_space_cb (LIBMTP_mtpdevice_t *device, RBMtpSource *source) { RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source); LIBMTP_devicestorage_t *storage; int ret; ret = LIBMTP_Get_Storage (device, LIBMTP_STORAGE_SORTBY_NOTSORTED); if (ret != 0) { rb_mtp_thread_report_errors (priv->device_thread, FALSE); } /* probably need a lock for this.. */ priv->free_space = 0; for (storage = device->storage; storage != NULL; storage = storage->next) { priv->free_space += storage->FreeSpaceInBytes; } }
static void set_album_image (RBMtpThread *thread, RBMtpThreadTask *task) { LIBMTP_filesampledata_t *albumart; LIBMTP_album_t *album; GError *error = NULL; char *image_data; gsize image_size; int ret; album = g_hash_table_lookup (thread->albums, task->album); if (album == NULL) { rb_debug ("Couldn't find an album for %s", task->album); return; } /* probably should scale the image down, since some devices have a size limit and they all have * tiny displays anyway. */ if (gdk_pixbuf_save_to_buffer (task->image, &image_data, &image_size, "jpeg", &error, NULL) == FALSE) { rb_debug ("unable to convert album art image to a JPEG buffer: %s", error->message); g_error_free (error); return; } albumart = LIBMTP_new_filesampledata_t (); albumart->filetype = LIBMTP_FILETYPE_JPEG; albumart->data = image_data; albumart->size = image_size; ret = LIBMTP_Send_Representative_Sample (thread->device, album->album_id, albumart); if (ret != 0) { rb_mtp_thread_report_errors (thread, TRUE); } else { rb_debug ("successfully set album art for %s (%" G_GSIZE_FORMAT " bytes)", task->album, image_size); } /* libmtp will try to free this if we don't clear the pointer */ albumart->data = NULL; LIBMTP_destroy_filesampledata_t (albumart); }
/* this callback runs on the device handling thread, so it can call libmtp directly */ static void mtp_device_open_cb (LIBMTP_mtpdevice_t *device, RBMtpSource *source) { RBMtpSourcePrivate *priv = MTP_SOURCE_GET_PRIVATE (source); gboolean has_audio = FALSE; DeviceOpenedData *data; if (device == NULL) { /* can't delete the source on this thread, so move it to the main thread */ g_idle_add ((GSourceFunc) device_open_failed_idle, g_object_ref (source)); return; } /* set the source name to match the device, ignoring some * particular broken device names. */ data = g_new0 (DeviceOpenedData, 1); data->source = g_object_ref (source); data->name = LIBMTP_Get_Friendlyname (device); if (data->name == NULL || strcmp (data->name, "?????") == 0) { g_free (data->name); data->name = LIBMTP_Get_Modelname (device); } if (data->name == NULL) { data->name = g_strdup (_("Digital Audio Player")); } /* get some other device information that doesn't change */ priv->manufacturer = LIBMTP_Get_Manufacturername (device); priv->device_version = LIBMTP_Get_Deviceversion (device); priv->model_name = LIBMTP_Get_Modelname (device); priv->serial = LIBMTP_Get_Serialnumber (device); /* calculate the device capacity */ priv->capacity = 0; if (LIBMTP_Get_Storage (device, LIBMTP_STORAGE_SORTBY_NOTSORTED) == 0) { LIBMTP_devicestorage_t *storage; for (storage = device->storage; storage != NULL; storage = storage->next) { priv->capacity += storage->MaxCapacity; } } update_free_space_cb (device, RB_MTP_SOURCE (source)); /* figure out the set of formats supported by the device, ensuring there's at least * one audio format aside from WAV. the purpose of this is to exclude cameras and other * MTP devices that aren't interesting to us. */ if (LIBMTP_Get_Supported_Filetypes (device, &data->types, &data->num_types) != 0) { rb_mtp_thread_report_errors (priv->device_thread, FALSE); } else { int i; for (i = 0; i < data->num_types; i++) { if (data->types[i] != LIBMTP_FILETYPE_WAV && LIBMTP_FILETYPE_IS_AUDIO (data->types[i])) { has_audio = TRUE; break; } } } if (has_audio == FALSE) { rb_debug ("device doesn't support any audio formats"); g_idle_add ((GSourceFunc) device_open_ignore_idle, data); return; } g_idle_add ((GSourceFunc) device_opened_idle, data); /* now get the track list */ rb_mtp_thread_get_track_list (priv->device_thread, (RBMtpTrackListCallback) mtp_tracklist_cb, g_object_ref (source), g_object_unref); }
static gboolean run_task (RBMtpThread *thread, RBMtpThreadTask *task) { char *name = task_name (task); rb_debug ("running task: %s", name); g_free (name); switch (task->task) { case OPEN_DEVICE: open_device (thread, task); break; case CLOSE_DEVICE: return TRUE; case SET_DEVICE_NAME: if (LIBMTP_Set_Friendlyname (thread->device, task->name)) { rb_mtp_thread_report_errors (thread, TRUE); } break; case THREAD_CALLBACK: { RBMtpThreadCallback cb = (RBMtpThreadCallback)task->callback; cb (thread->device, task->user_data); } break; case ADD_TO_ALBUM: add_track_to_album_and_update (thread, task); break; case REMOVE_FROM_ALBUM: remove_track_from_album (thread, task); break; case SET_ALBUM_IMAGE: set_album_image (thread, task); break; case GET_TRACK_LIST: get_track_list (thread, task); break; case DELETE_TRACK: if (LIBMTP_Delete_Object (thread->device, task->track_id)) { rb_mtp_thread_report_errors (thread, TRUE); } break; case UPLOAD_TRACK: upload_track (thread, task); break; case DOWNLOAD_TRACK: download_track (thread, task); break; default: g_assert_not_reached (); } return FALSE; }
static void get_track_list (RBMtpThread *thread, RBMtpThreadTask *task) { RBMtpTrackListCallback cb = task->callback; gboolean device_forgets_albums = TRUE; GHashTable *update_albums = NULL; LIBMTP_track_t *tracks = NULL; LIBMTP_album_t *albums; LIBMTP_album_t *album; /* get all the albums */ albums = LIBMTP_Get_Album_List (thread->device); rb_mtp_thread_report_errors (thread, FALSE); if (albums != NULL) { LIBMTP_album_t *album; for (album = albums; album != NULL; album = album->next) { if (album->name == NULL) continue; rb_debug ("album: %s, %d tracks", album->name, album->no_tracks); g_hash_table_insert (thread->albums, album->name, album); if (album->no_tracks != 0) { device_forgets_albums = FALSE; } } if (device_forgets_albums) { rb_debug ("stupid mtp device detected. will rebuild all albums."); } } else { rb_debug ("No albums"); device_forgets_albums = FALSE; } tracks = LIBMTP_Get_Tracklisting_With_Callback (thread->device, NULL, NULL); rb_mtp_thread_report_errors (thread, FALSE); if (tracks == NULL) { rb_debug ("no tracks on the device"); } else if (device_forgets_albums) { LIBMTP_track_t *track; rb_debug ("rebuilding albums"); update_albums = g_hash_table_new (g_direct_hash, g_direct_equal); for (track = tracks; track != NULL; track = track->next) { if (track->album != NULL) { gboolean new_album = FALSE; album = add_track_to_album (thread, track->album, track->item_id, track->storage_id, &new_album); g_hash_table_insert (update_albums, album, GINT_TO_POINTER (new_album)); } } rb_debug ("finished rebuilding albums"); } cb (tracks, task->user_data); /* the callback owns the tracklist */ if (device_forgets_albums) { GHashTableIter iter; gpointer album_ptr; gpointer new_album_ptr; rb_debug ("writing rebuilt albums back to the device"); g_hash_table_iter_init (&iter, update_albums); while (g_hash_table_iter_next (&iter, &album_ptr, &new_album_ptr)) { album = album_ptr; rb_debug ("writing album \"%s\"", album->name); write_album_to_device (thread, album, GPOINTER_TO_INT (new_album_ptr)); } g_hash_table_destroy (update_albums); rb_debug ("removing remaining empty albums"); g_hash_table_iter_init (&iter, thread->albums); while (g_hash_table_iter_next (&iter, NULL, &album_ptr)) { int ret; album = album_ptr; if (album->no_tracks == 0) { rb_debug ("pruning empty album \"%s\"", album->name); ret = LIBMTP_Delete_Object (thread->device, album->album_id); if (ret != 0) { rb_mtp_thread_report_errors (thread, FALSE); } g_hash_table_iter_remove (&iter); } } rb_debug ("finished updating albums on the device"); } }