Remove iDevice support as it doesn't work well for modern devices and libgpod shows no signs of updating to support them.
Fixes issue #3683
This commit is contained in:
parent
cea41801db
commit
3d434f1c88
|
@ -71,18 +71,15 @@ pkg_check_modules(GSTREAMER_APP gstreamer-app-0.10)
|
|||
pkg_check_modules(GSTREAMER_BASE gstreamer-base-0.10)
|
||||
pkg_check_modules(GSTREAMER_CDDA gstreamer-cdda-0.10)
|
||||
pkg_check_modules(GSTREAMER_TAG gstreamer-tag-0.10)
|
||||
pkg_check_modules(IMOBILEDEVICE libimobiledevice-1.0)
|
||||
pkg_check_modules(INDICATEQT indicate-qt)
|
||||
pkg_check_modules(LIBGPOD libgpod-1.0>=0.7.92)
|
||||
pkg_check_modules(LIBMTP libmtp>=1.0)
|
||||
pkg_check_modules(LIBMYGPO_QT libmygpo-qt>=1.0.7)
|
||||
pkg_check_modules(LIBXML libxml-2.0)
|
||||
pkg_check_modules(PLIST libplist)
|
||||
pkg_check_modules(QCA qca2)
|
||||
pkg_check_modules(QJSON REQUIRED QJson)
|
||||
pkg_check_modules(SPOTIFY libspotify>=12.1.45)
|
||||
pkg_check_modules(TAGLIB REQUIRED taglib>=1.6)
|
||||
pkg_check_modules(USBMUXD libusbmuxd)
|
||||
|
||||
if (WIN32)
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
@ -233,13 +230,6 @@ optional_component(GIO ON "Devices: GIO device backend"
|
|||
DEPENDS "Linux or Windows" "NOT APPLE"
|
||||
)
|
||||
|
||||
optional_component(IMOBILEDEVICE ON "Devices: iPod Touch, iPhone, iPad support"
|
||||
DEPENDS "libimobiledevice" IMOBILEDEVICE_FOUND
|
||||
DEPENDS "libplist" PLIST_FOUND
|
||||
DEPENDS "libusbmuxd" USBMUXD_FOUND
|
||||
DEPENDS "iPod classic support" HAVE_LIBGPOD
|
||||
)
|
||||
|
||||
optional_component(LIBMTP ON "Devices: MTP support"
|
||||
DEPENDS "libmtp" LIBMTP_FOUND
|
||||
)
|
||||
|
@ -450,10 +440,6 @@ if(WITH_DEBIAN)
|
|||
add_subdirectory(debian)
|
||||
endif(WITH_DEBIAN)
|
||||
|
||||
if(IMOBILEDEVICE_FOUND AND PLIST_FOUND)
|
||||
add_subdirectory(gst/afcsrc)
|
||||
endif(IMOBILEDEVICE_FOUND AND PLIST_FOUND)
|
||||
|
||||
if(HAVE_BREAKPAD)
|
||||
add_subdirectory(3rdparty/google-breakpad)
|
||||
endif(HAVE_BREAKPAD)
|
||||
|
|
|
@ -110,6 +110,11 @@ Section "Delete old files" oldfiles
|
|||
Delete "$INSTDIR\avutil-50.dll"
|
||||
Delete "$INSTDIR\libcdio-12.dll"
|
||||
Delete "$INSTDIR\libgcc_s_dw2-1.dll"
|
||||
|
||||
; libimobiledevice removal
|
||||
Delete "$INSTDIR\libimobiledevice-1.dll"
|
||||
Delete "$INSTDIR\libplist.dll"
|
||||
Delete "$INSTDIR\libusbmuxd.dll"
|
||||
SectionEnd
|
||||
|
||||
Section "Clementine" Clementine
|
||||
|
@ -159,7 +164,6 @@ Section "Clementine" Clementine
|
|||
File "libgsttag-0.10-0.dll"
|
||||
File "libgthread-2.0-0.dll"
|
||||
File "libid3tag.dll"
|
||||
File "libimobiledevice-1.dll"
|
||||
File "liblastfm.dll"
|
||||
File "libmad.dll"
|
||||
File "libmms-0.dll"
|
||||
|
@ -178,7 +182,6 @@ Section "Clementine" Clementine
|
|||
File "libstdc++-6.dll"
|
||||
File "libtag.dll"
|
||||
File "libtasn1-3.dll"
|
||||
File "libusbmuxd.dll"
|
||||
File "libvorbis-0.dll"
|
||||
File "libvorbisenc-2.dll"
|
||||
File "libxml2-2.dll"
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
set(CMAKE_C_FLAGS "-Wall")
|
||||
set(CMAKE_CXX_FLAGS "-Woverloaded-virtual -Wall")
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
include_directories(${GLIB_INCLUDE_DIRS})
|
||||
include_directories(${GOBJECT_INCLUDE_DIRS})
|
||||
include_directories(${GSTREAMER_INCLUDE_DIRS})
|
||||
include_directories(${IMOBILEDEVICE_INCLUDE_DIRS})
|
||||
|
||||
set(SOURCES
|
||||
gstafcsrc.c
|
||||
)
|
||||
|
||||
add_library(gstafcsrc STATIC
|
||||
${SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(gstafcsrc
|
||||
${GOBJECT_LIBRARIES}
|
||||
${GLIB_LIBRARIES}
|
||||
${GSTREAMER_LIBRARIES}
|
||||
${GSTREAMER_BASE_LIBRARIES}
|
||||
${IMOBILEDEVICE_LIBRARIES}
|
||||
)
|
|
@ -1,368 +0,0 @@
|
|||
/* This file is part of Clementine.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gstafcsrc.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <gst/gst.h>
|
||||
|
||||
|
||||
// Signals
|
||||
enum {
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
// Properties
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_LOCATION,
|
||||
};
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC(gst_afc_src_debug);
|
||||
#define GST_CAT_DEFAULT gst_afc_src_debug
|
||||
|
||||
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("ANY")
|
||||
);
|
||||
|
||||
static void gst_afc_src_interface_init(GType type);
|
||||
static void gst_afc_src_uri_handler_init(gpointer iface, gpointer data);
|
||||
static void gst_afc_src_set_property(GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
|
||||
static void gst_afc_src_get_property(GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
|
||||
static void gst_afc_src_finalize(GObject* object);
|
||||
static gboolean gst_afc_src_start(GstBaseSrc* src);
|
||||
static gboolean gst_afc_src_stop(GstBaseSrc* src);
|
||||
static GstFlowReturn gst_afc_src_create(GstBaseSrc* src, guint64 offset, guint length, GstBuffer** buffer);
|
||||
static gboolean gst_afc_src_is_seekable(GstBaseSrc* src);
|
||||
static gboolean gst_afc_src_get_size(GstBaseSrc* src, guint64* size);
|
||||
|
||||
GST_BOILERPLATE_FULL(GstAfcSrc, gst_afc_src, GstBaseSrc, GST_TYPE_BASE_SRC, gst_afc_src_interface_init);
|
||||
|
||||
|
||||
static void gst_afc_src_interface_init(GType type) {
|
||||
static const GInterfaceInfo urihandler_info = {
|
||||
gst_afc_src_uri_handler_init,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (gst_afc_src_debug, "afcsrc", 0, "iPod/iPhone Source");
|
||||
|
||||
g_type_add_interface_static(type, GST_TYPE_URI_HANDLER, &urihandler_info);
|
||||
}
|
||||
|
||||
static void gst_afc_src_base_init(gpointer gclass) {
|
||||
GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
|
||||
|
||||
gst_element_class_set_details_simple(element_class,
|
||||
"iPod/iPhone Source",
|
||||
"Source/Afc",
|
||||
"Read using libimobiledevice",
|
||||
"David Sansome <me@davidsansome.com>");
|
||||
|
||||
gst_element_class_add_pad_template (element_class,
|
||||
gst_static_pad_template_get (&src_factory));
|
||||
}
|
||||
|
||||
static void gst_afc_src_class_init (GstAfcSrcClass* klass) {
|
||||
GObjectClass* gobject_class = (GObjectClass*) klass;
|
||||
GstBaseSrcClass* gstbasesrc_class = (GstBaseSrcClass*) klass;
|
||||
|
||||
gobject_class->set_property = gst_afc_src_set_property;
|
||||
gobject_class->get_property = gst_afc_src_get_property;
|
||||
gobject_class->finalize = gst_afc_src_finalize;
|
||||
|
||||
g_object_class_install_property(gobject_class, PROP_LOCATION,
|
||||
g_param_spec_string(
|
||||
"location", "URI",
|
||||
"The URI of the file to read, must be of the form afc://uuid/filename", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY));
|
||||
|
||||
gstbasesrc_class->start = gst_afc_src_start;
|
||||
gstbasesrc_class->stop = gst_afc_src_stop;
|
||||
gstbasesrc_class->create = gst_afc_src_create;
|
||||
gstbasesrc_class->is_seekable = gst_afc_src_is_seekable;
|
||||
gstbasesrc_class->get_size = gst_afc_src_get_size;
|
||||
}
|
||||
|
||||
static GstURIType gst_afc_src_uri_get_type() {
|
||||
return GST_URI_SRC;
|
||||
}
|
||||
|
||||
static gchar** gst_afc_src_uri_get_protocols() {
|
||||
static const gchar* protocols[] = { "afc", NULL };
|
||||
return (char**) protocols;
|
||||
}
|
||||
|
||||
static const gchar* gst_afc_src_uri_get_uri(GstURIHandler* handler) {
|
||||
GstAfcSrc* self = GST_AFCSRC(handler);
|
||||
return self->location_;
|
||||
}
|
||||
|
||||
static gboolean gst_afc_src_uri_set_uri(GstURIHandler* handler, const gchar* uri) {
|
||||
GstAfcSrc* self = GST_AFCSRC(handler);
|
||||
self->location_ = g_strdup(uri);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void gst_afc_src_uri_handler_init(gpointer g_iface, gpointer data) {
|
||||
GstURIHandlerInterface* iface = (GstURIHandlerInterface*) g_iface;
|
||||
|
||||
iface->get_type = gst_afc_src_uri_get_type;
|
||||
iface->get_protocols = gst_afc_src_uri_get_protocols;
|
||||
iface->set_uri = gst_afc_src_uri_set_uri;
|
||||
iface->get_uri = gst_afc_src_uri_get_uri;
|
||||
}
|
||||
|
||||
|
||||
static void gst_afc_src_init(GstAfcSrc* element, GstAfcSrcClass* gclass) {
|
||||
element->location_ = NULL;
|
||||
element->uuid_ = NULL;
|
||||
element->path_ = NULL;
|
||||
element->connected_ = FALSE;
|
||||
element->afc_ = NULL;
|
||||
element->afc_port_ = 0;
|
||||
element->device_ = NULL;
|
||||
element->file_handle_ = 0;
|
||||
element->buffer_ = NULL;
|
||||
element->buffer_is_valid_ = FALSE;
|
||||
element->buffer_length_ = 0;
|
||||
element->buffer_offset_ = 0;
|
||||
}
|
||||
|
||||
static void gst_afc_src_finalize(GObject* object) {
|
||||
GstAfcSrc* self = GST_AFCSRC(object);
|
||||
free(self->location_);
|
||||
free(self->uuid_);
|
||||
free(self->path_);
|
||||
free(self->buffer_);
|
||||
|
||||
if (self->file_handle_)
|
||||
afc_file_close(self->afc_, self->file_handle_);
|
||||
|
||||
if (self->afc_)
|
||||
afc_client_free(self->afc_);
|
||||
|
||||
if (self->device_)
|
||||
idevice_free(self->device_);
|
||||
|
||||
|
||||
G_OBJECT_CLASS(parent_class)->finalize(object);
|
||||
}
|
||||
|
||||
static void gst_afc_src_set_property(
|
||||
GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec) {
|
||||
GstAfcSrc* self = GST_AFCSRC(object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_LOCATION:
|
||||
self->location_ = g_strdup(g_value_get_string(value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void gst_afc_src_get_property(
|
||||
GObject* object, guint prop_id, GValue* value, GParamSpec* pspec) {
|
||||
GstAfcSrc* self = GST_AFCSRC(object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_LOCATION:
|
||||
g_value_set_string(value, self->location_);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean gst_afc_src_start(GstBaseSrc* src) {
|
||||
GstAfcSrc* self = GST_AFCSRC(src);
|
||||
|
||||
// Don't connect again
|
||||
if (self->connected_)
|
||||
return TRUE;
|
||||
|
||||
// Check that a URI has been passed
|
||||
if (!self->location_ || self->location_[0] == '\0') {
|
||||
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("No URI specified"), (NULL));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Parse the URI
|
||||
// HERE BE DRAGONS
|
||||
gchar* location = gst_uri_get_location(self->location_);
|
||||
char* path_pos = strstr(location, "/");
|
||||
|
||||
self->uuid_ = (char*) malloc(path_pos - location + 1);
|
||||
memcpy(self->uuid_, location, path_pos - location);
|
||||
self->uuid_[path_pos - location] = '\0';
|
||||
self->path_ = g_strdup(path_pos);
|
||||
|
||||
g_free(location);
|
||||
|
||||
// Open the device
|
||||
idevice_error_t err = idevice_new(&self->device_, self->uuid_);
|
||||
if (err != IDEVICE_E_SUCCESS) {
|
||||
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("idevice error: %d", err), (NULL));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
lockdownd_client_t lockdown;
|
||||
|
||||
lockdownd_error_t lockdown_err =
|
||||
lockdownd_client_new_with_handshake(self->device_, &lockdown, "GstAfcSrc");
|
||||
if (lockdown_err != LOCKDOWN_E_SUCCESS) {
|
||||
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("lockdown error: %d", lockdown_err), (NULL));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
lockdown_err = lockdownd_start_service(lockdown, "com.apple.afc", &self->afc_port_);
|
||||
if (lockdown_err != LOCKDOWN_E_SUCCESS) {
|
||||
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("lockdown error: %d", lockdown_err), (NULL));
|
||||
lockdownd_client_free(lockdown);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
afc_error_t afc_err = afc_client_new(self->device_, self->afc_port_, &self->afc_);
|
||||
if (afc_err != 0) {
|
||||
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("afc error: %d", afc_err), (NULL));
|
||||
lockdownd_client_free(lockdown);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
lockdownd_client_free(lockdown);
|
||||
|
||||
// Try opening the file
|
||||
afc_err = afc_file_open(self->afc_, self->path_, AFC_FOPEN_RDONLY, &self->file_handle_);
|
||||
if (afc_err != 0) {
|
||||
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("afc error: %d", afc_err), (NULL));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
self->connected_ = TRUE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean gst_afc_src_stop(GstBaseSrc* src) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstFlowReturn gst_afc_src_create(GstBaseSrc* src, guint64 offset, guint length, GstBuffer** buffer) {
|
||||
GstAfcSrc* self = GST_AFCSRC(src);
|
||||
|
||||
GstBuffer* buf = gst_buffer_try_new_and_alloc(length);
|
||||
if (buf == NULL && length > 0) {
|
||||
GST_ERROR_OBJECT(src, "Failed to allocate %u bytes", length);
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
|
||||
// Is this section within our cache?
|
||||
if (!self->buffer_is_valid_ || offset < self->buffer_offset_ ||
|
||||
offset + length > self->buffer_offset_ + self->buffer_length_) {
|
||||
// No - read from the device to fill our internal cache.
|
||||
// Always read twice the requested size so the next read(s) hit the cache too.
|
||||
if (self->buffer_length_ != length * 2) {
|
||||
self->buffer_ = (char*)realloc(self->buffer_, length * 2);
|
||||
self->buffer_is_valid_ = TRUE;
|
||||
self->buffer_length_ = length * 2;
|
||||
}
|
||||
self->buffer_offset_ = offset;
|
||||
|
||||
uint32_t bytes_read = 0;
|
||||
afc_error_t err;
|
||||
err = afc_file_seek(self->afc_, self->file_handle_, offset, SEEK_SET);
|
||||
if (err == AFC_E_SUCCESS) {
|
||||
err = afc_file_read(self->afc_, self->file_handle_, self->buffer_, length * 2, &bytes_read);
|
||||
}
|
||||
|
||||
if (err != AFC_E_SUCCESS) {
|
||||
gst_buffer_unref(buf);
|
||||
return GST_FLOW_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy from our internal buffer to the output buffer
|
||||
memcpy(GST_BUFFER_DATA(buf), self->buffer_ + offset - self->buffer_offset_, length);
|
||||
|
||||
*buffer = buf;
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static gboolean gst_afc_src_is_seekable(GstBaseSrc* src) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean gst_afc_src_get_size(GstBaseSrc* src, guint64* size) {
|
||||
GstAfcSrc* self = GST_AFCSRC(src);
|
||||
|
||||
char** infolist = NULL;
|
||||
afc_error_t err = afc_get_file_info(self->afc_, self->path_, &infolist);
|
||||
if (err != AFC_E_SUCCESS || !infolist) {
|
||||
GST_ELEMENT_ERROR(src, RESOURCE, NOT_FOUND, ("afc error: %d", err), (NULL));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean found_size = FALSE;
|
||||
|
||||
char** p = infolist;
|
||||
while (*p != NULL) {
|
||||
if (g_strcmp0(*p, "st_size") == 0) {
|
||||
*size = strtoll(*(p+1), NULL, 0);
|
||||
found_size = TRUE;
|
||||
}
|
||||
free(*p); ++p;
|
||||
free(*p); ++p;
|
||||
}
|
||||
free(infolist);
|
||||
|
||||
if (!found_size) {
|
||||
*size = 0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#define PACKAGE "Clementine"
|
||||
|
||||
static gboolean afcsrc_init(GstPlugin* afcsrc) {
|
||||
return gst_element_register(afcsrc, "afcsrc", GST_RANK_PRIMARY, GST_TYPE_AFCSRC);
|
||||
}
|
||||
|
||||
void afcsrc_register_static() {
|
||||
gst_plugin_register_static(
|
||||
GST_VERSION_MAJOR,
|
||||
GST_VERSION_MINOR,
|
||||
"afcsrc",
|
||||
"iPod/iPhone Source",
|
||||
afcsrc_init,
|
||||
"0.1",
|
||||
"GPL",
|
||||
"Clementine",
|
||||
"Clementine",
|
||||
"http://www.clementine-player.org/");
|
||||
}
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
/* This file is part of Clementine.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __GST_AFCSRC_H__
|
||||
#define __GST_AFCSRC_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstbasesrc.h>
|
||||
|
||||
#include <libimobiledevice/afc.h>
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void afcsrc_register_static();
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_AFCSRC \
|
||||
(gst_afc_src_get_type())
|
||||
#define GST_AFCSRC(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AFCSRC,GstAfcSrc))
|
||||
#define GST_AFCSRC_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AFCSRC,GstAfcSrcClass))
|
||||
#define GST_IS_AFCSRC(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AFCSRC))
|
||||
#define GST_IS_AFCSRC_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AFCSRC))
|
||||
|
||||
typedef struct _GstAfcSrc GstAfcSrc;
|
||||
typedef struct _GstAfcSrcClass GstAfcSrcClass;
|
||||
|
||||
struct _GstAfcSrc {
|
||||
GstBaseSrc element;
|
||||
|
||||
char* location_;
|
||||
char* uuid_;
|
||||
char* path_;
|
||||
|
||||
gboolean connected_;
|
||||
idevice_t device_;
|
||||
afc_client_t afc_;
|
||||
|
||||
uint16_t afc_port_;
|
||||
|
||||
uint64_t file_handle_;
|
||||
|
||||
gboolean buffer_is_valid_;
|
||||
guint64 buffer_offset_;
|
||||
guint buffer_length_;
|
||||
char* buffer_;
|
||||
};
|
||||
|
||||
struct _GstAfcSrcClass {
|
||||
GstBaseSrcClass parent_class;
|
||||
};
|
||||
|
||||
GType gst_afc_src_get_type (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
|
@ -26,10 +26,6 @@ if (QT_VERSION_MINOR GREATER 5)
|
|||
endif(QT_VERSION_MINOR GREATER 5)
|
||||
add_definitions(-DQT_NO_URL_CAST_FROM_STRING)
|
||||
|
||||
if(ENABLE_IMOBILEDEVICE AND IMOBILEDEVICE_VERSION VERSION_GREATER 1.1.1)
|
||||
set(IMOBILEDEVICE_USES_UDIDS ON)
|
||||
endif()
|
||||
|
||||
include_directories(${CMAKE_BINARY_DIR})
|
||||
include_directories(${GLIB_INCLUDE_DIRS})
|
||||
include_directories(${LIBXML_INCLUDE_DIRS})
|
||||
|
@ -1035,25 +1031,6 @@ optional_source(HAVE_AUDIOCD
|
|||
devices/cddalister.h
|
||||
)
|
||||
|
||||
# libimobiledevice backend and device
|
||||
optional_source(HAVE_IMOBILEDEVICE
|
||||
INCLUDE_DIRECTORIES
|
||||
${IMOBILEDEVICE_INCLUDE_DIRS}
|
||||
${PLIST_INCLUDE_DIRS}
|
||||
${PLISTPP_INCLUDE_DIRS}
|
||||
SOURCES
|
||||
devices/afcdevice.cpp
|
||||
devices/afcfile.cpp
|
||||
devices/afctransfer.cpp
|
||||
devices/ilister.cpp
|
||||
devices/imobiledeviceconnection.cpp
|
||||
HEADERS
|
||||
devices/afcdevice.h
|
||||
devices/afcfile.h
|
||||
devices/afctransfer.h
|
||||
devices/ilister.h
|
||||
)
|
||||
|
||||
# mtp device
|
||||
optional_source(HAVE_LIBMTP
|
||||
INCLUDE_DIRECTORIES ${LIBMTP_INCLUDE_DIRS}
|
||||
|
@ -1255,17 +1232,6 @@ if(HAVE_AUDIOCD)
|
|||
target_link_libraries(clementine_lib ${GSTREAMER_CDDA_LIBRARIES})
|
||||
endif(HAVE_AUDIOCD)
|
||||
|
||||
if(HAVE_IMOBILEDEVICE)
|
||||
target_link_libraries(clementine_lib
|
||||
${IMOBILEDEVICE_LIBRARIES}
|
||||
${PLIST_LIBRARIES}
|
||||
${USBMUXD_LIBRARIES}
|
||||
gstafcsrc
|
||||
)
|
||||
link_directories(${IMOBILEDEVICE_LIBRARY_DIRS})
|
||||
link_directories(${USBMUXD_LIBRARY_DIRS})
|
||||
endif(HAVE_IMOBILEDEVICE)
|
||||
|
||||
if(HAVE_MOODBAR)
|
||||
target_link_libraries(clementine_lib gstmoodbar)
|
||||
endif()
|
||||
|
|
|
@ -1,176 +0,0 @@
|
|||
/* This file is part of Clementine.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "afcdevice.h"
|
||||
#include "afcfile.h"
|
||||
#include "afctransfer.h"
|
||||
#include "devicemanager.h"
|
||||
#include "gpodloader.h"
|
||||
#include "imobiledeviceconnection.h"
|
||||
#include "core/application.h"
|
||||
#include "core/utilities.h"
|
||||
|
||||
#include <QThread>
|
||||
|
||||
AfcDevice::AfcDevice(
|
||||
const QUrl& url, DeviceLister* lister, const QString& unique_id,
|
||||
DeviceManager* manager, Application* app, int database_id, bool first_time)
|
||||
: GPodDevice(url, lister, unique_id, manager, app, database_id, first_time),
|
||||
transfer_(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
AfcDevice::~AfcDevice() {
|
||||
Utilities::RemoveRecursive(local_path_);
|
||||
}
|
||||
|
||||
void AfcDevice::Init() {
|
||||
// Make a new temporary directory for the iTunesDB. We copy it off the iPod
|
||||
// so that libgpod can have a local directory to use.
|
||||
local_path_ = Utilities::MakeTempDir();
|
||||
InitBackendDirectory(local_path_, first_time_, false);
|
||||
model_->Init();
|
||||
|
||||
transfer_ = new AfcTransfer(url_.host(), local_path_, app_->task_manager(),
|
||||
shared_from_this());
|
||||
transfer_->moveToThread(loader_thread_);
|
||||
|
||||
connect(transfer_, SIGNAL(TaskStarted(int)), SIGNAL(TaskStarted(int)));
|
||||
connect(transfer_, SIGNAL(CopyFinished(bool)), SLOT(CopyFinished(bool)));
|
||||
connect(loader_thread_, SIGNAL(started()), transfer_, SLOT(CopyFromDevice()));
|
||||
loader_thread_->start();
|
||||
}
|
||||
|
||||
void AfcDevice::CopyFinished(bool success) {
|
||||
transfer_->deleteLater();
|
||||
transfer_ = NULL;
|
||||
|
||||
if (!success) {
|
||||
app_->AddError(tr("An error occurred copying the iTunes database from the device"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Now load the songs from the local database
|
||||
loader_ = new GPodLoader(local_path_, app_->task_manager(), backend_,
|
||||
shared_from_this());
|
||||
loader_->set_music_path_prefix("afc://" + url_.host());
|
||||
loader_->set_song_type(Song::Type_Stream);
|
||||
loader_->moveToThread(loader_thread_);
|
||||
|
||||
connect(loader_, SIGNAL(Error(QString)), SIGNAL(Error(QString)));
|
||||
connect(loader_, SIGNAL(TaskStarted(int)), SIGNAL(TaskStarted(int)));
|
||||
connect(loader_, SIGNAL(LoadFinished(Itdb_iTunesDB*)), SLOT(LoadFinished(Itdb_iTunesDB*)));
|
||||
QMetaObject::invokeMethod(loader_, "LoadDatabase");
|
||||
}
|
||||
|
||||
bool AfcDevice::StartCopy(QList<Song::FileType>* supported_types) {
|
||||
GPodDevice::StartCopy(supported_types);
|
||||
connection_.reset(new iMobileDeviceConnection(url_.host()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AfcDevice::CopyToStorage(const CopyJob& job) {
|
||||
Q_ASSERT(db_);
|
||||
|
||||
Itdb_Track* track = AddTrackToITunesDb(job.metadata_);
|
||||
|
||||
// Get an unused filename on the device
|
||||
QString dest = connection_->GetUnusedFilename(db_, job.metadata_);
|
||||
if (dest.isEmpty()) {
|
||||
itdb_track_remove(track);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy the file
|
||||
{
|
||||
QFile source_file(job.source_);
|
||||
AfcFile dest_file(connection_.get(), dest);
|
||||
if (!Utilities::Copy(&source_file, &dest_file))
|
||||
return false;
|
||||
}
|
||||
|
||||
track->transferred = 1;
|
||||
|
||||
// Set the filetype_marker
|
||||
QString suffix = dest.section('.', -1, -1).toUpper();
|
||||
track->filetype_marker = 0;
|
||||
for (int i=0 ; i<4 ; ++i) {
|
||||
track->filetype_marker = track->filetype_marker << 8;
|
||||
if (i >= suffix.length())
|
||||
track->filetype_marker |= ' ';
|
||||
else
|
||||
track->filetype_marker |= suffix[i].toAscii();
|
||||
}
|
||||
|
||||
// Set the filename
|
||||
track->ipod_path = strdup(dest.toUtf8().constData());
|
||||
itdb_filename_fs2ipod(track->ipod_path);
|
||||
|
||||
AddTrackToModel(track, "afc://" + url_.host());
|
||||
|
||||
// Remove the original if it was requested
|
||||
if (job.remove_original_) {
|
||||
QFile::remove(job.source_);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AfcDevice::FinishCopy(bool success) {
|
||||
// Temporarily unset the GUID so libgpod doesn't lock the device for syncing
|
||||
itdb_device_set_sysinfo(db_->device, "FirewireGuid", NULL);
|
||||
|
||||
GPodDevice::FinishCopy(success);
|
||||
|
||||
// Close the connection to the device
|
||||
connection_.reset();
|
||||
}
|
||||
|
||||
void AfcDevice::FinaliseDatabase() {
|
||||
// Set the GUID again to lock the device for syncing
|
||||
itdb_device_set_sysinfo(db_->device, "FirewireGuid", url_.host().toUtf8().constData());
|
||||
|
||||
// Copy the files back to the iPod
|
||||
// No need to start another thread since we're already in the organiser thread
|
||||
AfcTransfer transfer(url_.host(), local_path_, NULL, shared_from_this());
|
||||
|
||||
itdb_start_sync(db_);
|
||||
bool success = transfer.CopyToDevice(connection_.get());
|
||||
itdb_stop_sync(db_);
|
||||
|
||||
if (!success) {
|
||||
app_->AddError(tr("An error occurred copying the iTunes database onto the device"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool AfcDevice::DeleteFromStorage(const DeleteJob& job) {
|
||||
const QString path = job.metadata_.url().toLocalFile();
|
||||
|
||||
if (!RemoveTrackFromITunesDb(path))
|
||||
return false;
|
||||
|
||||
// Remove the file
|
||||
if (afc_remove_path(connection_->afc(), path.toUtf8().constData()) != AFC_E_SUCCESS)
|
||||
return false;
|
||||
|
||||
// Remove it from our library model
|
||||
songs_to_remove_ << job.metadata_;
|
||||
|
||||
return true;
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
/* This file is part of Clementine.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef AFCDEVICE_H
|
||||
#define AFCDEVICE_H
|
||||
|
||||
#include "gpoddevice.h"
|
||||
|
||||
#include <QMutex>
|
||||
#include <QWaitCondition>
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
#include <gpod/itdb.h>
|
||||
|
||||
class AfcTransfer;
|
||||
class GPodLoader;
|
||||
class iMobileDeviceConnection;
|
||||
|
||||
class AfcDevice : public GPodDevice {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Q_INVOKABLE AfcDevice(const QUrl& url, DeviceLister* lister,
|
||||
const QString& unique_id, DeviceManager* manager,
|
||||
Application* app,
|
||||
int database_id, bool first_time);
|
||||
~AfcDevice();
|
||||
|
||||
void Init();
|
||||
|
||||
static QStringList url_schemes() { return QStringList() << "afc"; }
|
||||
|
||||
bool StartCopy(QList<Song::FileType>* supported_types);
|
||||
bool CopyToStorage(const CopyJob& job);
|
||||
void FinishCopy(bool success);
|
||||
|
||||
bool DeleteFromStorage(const DeleteJob& job);
|
||||
|
||||
protected:
|
||||
void FinaliseDatabase();
|
||||
|
||||
private slots:
|
||||
void CopyFinished(bool success);
|
||||
|
||||
private:
|
||||
void RemoveRecursive(const QString& path);
|
||||
|
||||
private:
|
||||
AfcTransfer* transfer_;
|
||||
boost::scoped_ptr<iMobileDeviceConnection> connection_;
|
||||
|
||||
QString local_path_;
|
||||
};
|
||||
|
||||
#endif // AFCDEVICE_H
|
|
@ -1,79 +0,0 @@
|
|||
#include "afcfile.h"
|
||||
#include "imobiledeviceconnection.h"
|
||||
|
||||
#include <libimobiledevice/afc.h>
|
||||
|
||||
AfcFile::AfcFile(iMobileDeviceConnection* connection, const QString& path, QObject* parent)
|
||||
: QIODevice(parent),
|
||||
connection_(connection),
|
||||
handle_(0),
|
||||
path_(path)
|
||||
{
|
||||
}
|
||||
|
||||
AfcFile::~AfcFile() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool AfcFile::open(QIODevice::OpenMode mode) {
|
||||
afc_file_mode_t afc_mode;
|
||||
switch (mode) {
|
||||
case ReadOnly:
|
||||
afc_mode = AFC_FOPEN_RDONLY;
|
||||
break;
|
||||
case WriteOnly:
|
||||
afc_mode = AFC_FOPEN_WRONLY;
|
||||
break;
|
||||
case ReadWrite:
|
||||
afc_mode = AFC_FOPEN_RW;
|
||||
break;
|
||||
|
||||
default:
|
||||
afc_mode = AFC_FOPEN_RW;
|
||||
}
|
||||
afc_error_t err = afc_file_open(
|
||||
connection_->afc(), path_.toUtf8().constData(), afc_mode, &handle_);
|
||||
if (err != AFC_E_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return QIODevice::open(mode);
|
||||
}
|
||||
|
||||
void AfcFile::close() {
|
||||
if (handle_) {
|
||||
afc_file_close(connection_->afc(), handle_);
|
||||
}
|
||||
QIODevice::close();
|
||||
}
|
||||
|
||||
bool AfcFile::seek(qint64 pos) {
|
||||
afc_error_t err = afc_file_seek(connection_->afc(), handle_, pos, SEEK_SET);
|
||||
if (err != AFC_E_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
QIODevice::seek(pos);
|
||||
return true;
|
||||
}
|
||||
|
||||
qint64 AfcFile::readData(char* data, qint64 max_size) {
|
||||
uint32_t bytes_read = 0;
|
||||
afc_error_t err = afc_file_read(connection_->afc(), handle_, data, max_size, &bytes_read);
|
||||
if (err != AFC_E_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
qint64 AfcFile::writeData(const char* data, qint64 max_size) {
|
||||
uint32_t bytes_written = 0;
|
||||
afc_error_t err = afc_file_write(connection_->afc(), handle_, data, max_size, &bytes_written);
|
||||
if (err != AFC_E_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
qint64 AfcFile::size() const {
|
||||
return connection_->GetFileInfo(path_, "st_size").toLongLong();
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
#ifndef AFCFILE_H
|
||||
#define AFCFILE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <QIODevice>
|
||||
|
||||
#include <libimobiledevice/afc.h>
|
||||
|
||||
class iMobileDeviceConnection;
|
||||
|
||||
class AfcFile : public QIODevice {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AfcFile(iMobileDeviceConnection* connection, const QString& path, QObject* parent = 0);
|
||||
~AfcFile();
|
||||
|
||||
// QIODevice
|
||||
void close();
|
||||
bool open(OpenMode mode);
|
||||
bool seek(qint64 pos);
|
||||
qint64 size() const;
|
||||
|
||||
private:
|
||||
// QIODevice
|
||||
qint64 readData(char* data, qint64 max_size);
|
||||
qint64 writeData(const char* data, qint64 max_size);
|
||||
|
||||
iMobileDeviceConnection* connection_;
|
||||
uint64_t handle_;
|
||||
|
||||
QString path_;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,135 +0,0 @@
|
|||
/* This file is part of Clementine.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "afcfile.h"
|
||||
#include "afctransfer.h"
|
||||
#include "imobiledeviceconnection.h"
|
||||
#include "core/taskmanager.h"
|
||||
#include "core/utilities.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QtDebug>
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
AfcTransfer::AfcTransfer(const QString& uuid, const QString& local_destination,
|
||||
TaskManager* task_manager, boost::shared_ptr<ConnectedDevice> device)
|
||||
: QObject(NULL),
|
||||
device_(device),
|
||||
task_manager_(task_manager),
|
||||
uuid_(uuid),
|
||||
local_destination_(local_destination)
|
||||
{
|
||||
original_thread_ = thread();
|
||||
|
||||
important_directories_ << "/iTunes_Control/Artwork";
|
||||
important_directories_ << "/iTunes_Control/Device";
|
||||
important_directories_ << "/iTunes_Control/iTunes";
|
||||
}
|
||||
|
||||
AfcTransfer::~AfcTransfer() {
|
||||
}
|
||||
|
||||
void AfcTransfer::CopyFromDevice() {
|
||||
int task_id = 0;
|
||||
if (task_manager_) {
|
||||
task_id = task_manager_->StartTask(tr("Copying iPod database"));
|
||||
emit TaskStarted(task_id);
|
||||
}
|
||||
|
||||
// Connect to the device
|
||||
iMobileDeviceConnection c(uuid_);
|
||||
|
||||
// Copy directories. If one fails we stop.
|
||||
bool success = true;
|
||||
foreach (const QString& dir, important_directories_) {
|
||||
if (!CopyDirFromDevice(&c, dir)) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (task_manager_) {
|
||||
moveToThread(original_thread_);
|
||||
task_manager_->SetTaskFinished(task_id);
|
||||
emit CopyFinished(success);
|
||||
}
|
||||
}
|
||||
|
||||
bool AfcTransfer::CopyToDevice(iMobileDeviceConnection* connection) {
|
||||
// Connect to the device
|
||||
if (!connection)
|
||||
connection = new iMobileDeviceConnection(uuid_);
|
||||
|
||||
foreach (const QString& dir, important_directories_)
|
||||
if (!CopyDirToDevice(connection, dir))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AfcTransfer::CopyDirFromDevice(iMobileDeviceConnection* c, const QString& path) {
|
||||
foreach (const QString& filename, c->ReadDirectory(path, QDir::Files | QDir::NoDotAndDotDot)) {
|
||||
if (!CopyFileFromDevice(c, path + "/" + filename))
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (const QString& dir, c->ReadDirectory(path, QDir::Dirs | QDir::NoDotAndDotDot)) {
|
||||
if (!CopyDirFromDevice(c, path + "/" + dir))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AfcTransfer::CopyDirToDevice(iMobileDeviceConnection* c, const QString& path) {
|
||||
QDir dir(local_destination_ + path);
|
||||
|
||||
if (!c->Exists(path)) {
|
||||
c->MkDir(path);
|
||||
}
|
||||
|
||||
foreach (const QString& filename, dir.entryList(QDir::Files | QDir::NoDotAndDotDot)) {
|
||||
if (!CopyFileToDevice(c, path + "/" + filename))
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (const QString& dir, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot)) {
|
||||
if (!CopyDirToDevice(c, path + "/" + dir))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AfcTransfer::CopyFileFromDevice(iMobileDeviceConnection *c, const QString &path) {
|
||||
QString local_filename = local_destination_ + path;
|
||||
QString local_dir = local_filename.section('/', 0, -2);
|
||||
|
||||
QDir d;
|
||||
d.mkpath(local_dir);
|
||||
|
||||
QFile dest(local_filename);
|
||||
AfcFile source(c, path);
|
||||
|
||||
return Utilities::Copy(&source, &dest);
|
||||
}
|
||||
|
||||
bool AfcTransfer::CopyFileToDevice(iMobileDeviceConnection *c, const QString &path) {
|
||||
QFile source(local_destination_ + path);
|
||||
AfcFile dest(c, path);
|
||||
|
||||
return Utilities::Copy(&source, &dest);
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
/* This file is part of Clementine.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef AFCTRANSFER_H
|
||||
#define AFCTRANSFER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
class ConnectedDevice;
|
||||
class iMobileDeviceConnection;
|
||||
class TaskManager;
|
||||
|
||||
class QIODevice;
|
||||
|
||||
class AfcTransfer : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AfcTransfer(const QString& uuid, const QString& local_destination,
|
||||
TaskManager* task_manager, boost::shared_ptr<ConnectedDevice> device);
|
||||
~AfcTransfer();
|
||||
|
||||
bool CopyToDevice(iMobileDeviceConnection* connection);
|
||||
|
||||
public slots:
|
||||
void CopyFromDevice();
|
||||
|
||||
signals:
|
||||
void TaskStarted(int task_id);
|
||||
void CopyFinished(bool success);
|
||||
|
||||
private:
|
||||
bool CopyDirFromDevice(iMobileDeviceConnection* c, const QString& path);
|
||||
bool CopyDirToDevice(iMobileDeviceConnection* c, const QString& path);
|
||||
bool CopyFileFromDevice(iMobileDeviceConnection* c, const QString& path);
|
||||
bool CopyFileToDevice(iMobileDeviceConnection* c, const QString& path);
|
||||
|
||||
private:
|
||||
boost::shared_ptr<ConnectedDevice> device_;
|
||||
QThread* original_thread_;
|
||||
|
||||
TaskManager* task_manager_;
|
||||
QString uuid_;
|
||||
QString local_destination_;
|
||||
|
||||
QStringList important_directories_;
|
||||
};
|
||||
|
||||
#endif // AFCTRANSFER_H
|
|
@ -60,10 +60,6 @@
|
|||
#ifdef HAVE_GIO
|
||||
# include "giolister.h"
|
||||
#endif
|
||||
#ifdef HAVE_IMOBILEDEVICE
|
||||
# include "afcdevice.h"
|
||||
# include "ilister.h"
|
||||
#endif
|
||||
#ifdef HAVE_LIBMTP
|
||||
# include "mtpdevice.h"
|
||||
#endif
|
||||
|
@ -210,10 +206,6 @@ DeviceManager::DeviceManager(Application* app, QObject *parent)
|
|||
AddLister(new WmdmLister);
|
||||
AddDeviceClass<WmdmDevice>();
|
||||
#endif
|
||||
#ifdef HAVE_IMOBILEDEVICE
|
||||
AddLister(new iLister);
|
||||
AddDeviceClass<AfcDevice>();
|
||||
#endif
|
||||
|
||||
AddDeviceClass<FilesystemDevice>();
|
||||
|
||||
|
|
|
@ -1,208 +0,0 @@
|
|||
/* This file is part of Clementine.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "ilister.h"
|
||||
#include "imobiledeviceconnection.h"
|
||||
|
||||
#include <QStringList>
|
||||
#include <QtDebug>
|
||||
|
||||
iLister::iLister() {
|
||||
}
|
||||
|
||||
iLister::~iLister() {
|
||||
}
|
||||
|
||||
void iLister::Init() {
|
||||
idevice_event_subscribe(&EventCallback, reinterpret_cast<void*>(this));
|
||||
}
|
||||
|
||||
void iLister::EventCallback(const idevice_event_t* event, void* context) {
|
||||
iLister* me = reinterpret_cast<iLister*>(context);
|
||||
|
||||
#ifdef IMOBILEDEVICE_USES_UDIDS
|
||||
const char* uuid = event->udid;
|
||||
#else
|
||||
const char* uuid = event->uuid;
|
||||
#endif
|
||||
|
||||
switch (event->event) {
|
||||
case IDEVICE_DEVICE_ADD:
|
||||
me->DeviceAddedCallback(uuid);
|
||||
break;
|
||||
|
||||
case IDEVICE_DEVICE_REMOVE:
|
||||
me->DeviceRemovedCallback(uuid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void iLister::DeviceAddedCallback(const char* uuid) {
|
||||
DeviceInfo info = ReadDeviceInfo(uuid);
|
||||
QString id = UniqueId(uuid);
|
||||
|
||||
QString name = MakeFriendlyName(id);
|
||||
if (info.product_type == "iPhone 3,1" || info.product_type.startsWith("iPad")) {
|
||||
// iPhone 4 and iPad unsupported by libgpod as of 0.7.94.
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
QMutexLocker l(&mutex_);
|
||||
devices_[id] = info;
|
||||
}
|
||||
|
||||
emit DeviceAdded(id);
|
||||
}
|
||||
|
||||
void iLister::DeviceRemovedCallback(const char* uuid) {
|
||||
QString id = UniqueId(uuid);
|
||||
{
|
||||
QMutexLocker l(&mutex_);
|
||||
if (!devices_.contains(id))
|
||||
return;
|
||||
|
||||
devices_.remove(id);
|
||||
}
|
||||
|
||||
emit DeviceRemoved(id);
|
||||
}
|
||||
|
||||
QString iLister::UniqueId(const char *uuid) {
|
||||
return "ithing/" + QString::fromUtf8(uuid);
|
||||
}
|
||||
|
||||
QStringList iLister::DeviceUniqueIDs() {
|
||||
return devices_.keys();
|
||||
}
|
||||
|
||||
QVariantList iLister::DeviceIcons(const QString& id) {
|
||||
return QVariantList() << "ipodtouchicon";
|
||||
}
|
||||
|
||||
QString iLister::DeviceManufacturer(const QString& id) {
|
||||
return "Apple";
|
||||
}
|
||||
|
||||
QString iLister::DeviceModel(const QString& id) {
|
||||
return LockAndGetDeviceInfo(id, &DeviceInfo::product_type);
|
||||
}
|
||||
|
||||
quint64 iLister::DeviceCapacity(const QString& id) {
|
||||
return LockAndGetDeviceInfo(id, &DeviceInfo::total_bytes);
|
||||
}
|
||||
|
||||
quint64 iLister::DeviceFreeSpace(const QString& id) {
|
||||
return LockAndGetDeviceInfo(id, &DeviceInfo::free_bytes);
|
||||
}
|
||||
|
||||
QVariantMap iLister::DeviceHardwareInfo(const QString& id) {
|
||||
QVariantMap ret;
|
||||
ret[tr("Color")] = LockAndGetDeviceInfo(id, &DeviceInfo::colour);
|
||||
ret["IMEI"] = LockAndGetDeviceInfo(id, &DeviceInfo::imei);
|
||||
ret[tr("Password Protected")] = LockAndGetDeviceInfo(id, &DeviceInfo::password_protected);
|
||||
ret[tr("Timezone")] = LockAndGetDeviceInfo(id, &DeviceInfo::timezone);
|
||||
ret[tr("WiFi MAC Address")] = LockAndGetDeviceInfo(id, &DeviceInfo::wifi_mac);
|
||||
ret[tr("Bluetooth MAC Address")] = LockAndGetDeviceInfo(id, &DeviceInfo::bt_mac);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString iLister::MakeFriendlyName(const QString& id) {
|
||||
QString name = LockAndGetDeviceInfo(id, &DeviceInfo::name);
|
||||
if (!name.isEmpty()) {
|
||||
return name;
|
||||
}
|
||||
|
||||
QString model_id = LockAndGetDeviceInfo(id, &DeviceInfo::product_type);
|
||||
|
||||
if (model_id.startsWith("iPhone")) {
|
||||
QString version = model_id.right(3);
|
||||
QChar major = version[0];
|
||||
QChar minor = version[2];
|
||||
if (major == '1' && minor == '1') {
|
||||
return "iPhone";
|
||||
}
|
||||
if (major == '1' && minor == '2') {
|
||||
return "iPhone 3G";
|
||||
}
|
||||
if (major == '2' && minor == '1') {
|
||||
return "iPhone 3GS";
|
||||
}
|
||||
if (major == '3' && minor == '1') {
|
||||
return "iPhone 4";
|
||||
}
|
||||
} else if (model_id.startsWith("iPod")) {
|
||||
return "iPod Touch";
|
||||
} else if (model_id.startsWith("iPad")) {
|
||||
return "iPad";
|
||||
}
|
||||
return model_id;
|
||||
}
|
||||
|
||||
QList<QUrl> iLister::MakeDeviceUrls(const QString& id) {
|
||||
QList<QUrl> ret;
|
||||
|
||||
QString uuid = LockAndGetDeviceInfo(id, &DeviceInfo::uuid);
|
||||
if (uuid.isEmpty())
|
||||
return ret;
|
||||
|
||||
ret << QUrl("afc://" + uuid + "/");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void iLister::UnmountDevice(const QString& id) { }
|
||||
|
||||
iLister::DeviceInfo iLister::ReadDeviceInfo(const char* uuid) {
|
||||
DeviceInfo ret;
|
||||
|
||||
iMobileDeviceConnection conn(uuid);
|
||||
ret.uuid = uuid;
|
||||
ret.product_type = conn.GetProperty("ProductType").toString();
|
||||
ret.free_bytes = conn.GetProperty("AmountDataAvailable", "com.apple.disk_usage").toULongLong();
|
||||
ret.total_bytes = conn.GetProperty("TotalDataCapacity", "com.apple.disk_usage").toULongLong();
|
||||
ret.name = conn.GetProperty("DeviceName").toString();
|
||||
|
||||
ret.colour = conn.GetProperty("DeviceColor").toString();
|
||||
ret.imei = conn.GetProperty("InternationalMobileEquipmentIdentity").toString();
|
||||
ret.hardware = conn.GetProperty("HardwareModel").toString();
|
||||
ret.password_protected = conn.GetProperty("PasswordProtected").toBool();
|
||||
ret.os_version = conn.GetProperty("ProductVersion").toString();
|
||||
ret.timezone = conn.GetProperty("TimeZone").toString();
|
||||
ret.wifi_mac = conn.GetProperty("WiFiAddress").toString();
|
||||
ret.bt_mac = conn.GetProperty("BluetoothAddress").toString();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void iLister::UpdateDeviceFreeSpace(const QString& id) {
|
||||
{
|
||||
QMutexLocker l(&mutex_);
|
||||
if (!devices_.contains(id))
|
||||
return;
|
||||
|
||||
DeviceInfo& info = devices_[id];
|
||||
iMobileDeviceConnection conn(info.uuid);
|
||||
|
||||
info.free_bytes = conn.GetProperty("AmountDataAvailable", "com.apple.disk_usage").toULongLong();
|
||||
}
|
||||
|
||||
emit DeviceChanged(id);
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
/* This file is part of Clementine.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ILISTER_H
|
||||
#define ILISTER_H
|
||||
|
||||
#include "devicelister.h"
|
||||
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
|
||||
#include <QMutex>
|
||||
|
||||
class iLister : public DeviceLister {
|
||||
Q_OBJECT
|
||||
public:
|
||||
iLister();
|
||||
~iLister();
|
||||
|
||||
int priority() const { return 120; }
|
||||
|
||||
virtual QStringList DeviceUniqueIDs();
|
||||
virtual QVariantList DeviceIcons(const QString& id);
|
||||
virtual QString DeviceManufacturer(const QString& id);
|
||||
virtual QString DeviceModel(const QString& id);
|
||||
virtual quint64 DeviceCapacity(const QString& id);
|
||||
virtual quint64 DeviceFreeSpace(const QString& id);
|
||||
virtual QVariantMap DeviceHardwareInfo(const QString& id);
|
||||
virtual QString MakeFriendlyName(const QString& id);
|
||||
virtual QList<QUrl> MakeDeviceUrls(const QString& id);
|
||||
virtual void UnmountDevice(const QString& id);
|
||||
|
||||
public slots:
|
||||
virtual void UpdateDeviceFreeSpace(const QString& id);
|
||||
|
||||
private:
|
||||
struct DeviceInfo {
|
||||
DeviceInfo() : free_bytes(0), total_bytes(0) {}
|
||||
|
||||
QString uuid;
|
||||
QString product_type;
|
||||
quint64 free_bytes;
|
||||
quint64 total_bytes;
|
||||
QString name; // Name given to the iDevice by the user.
|
||||
|
||||
// Extra information.
|
||||
QString colour;
|
||||
QString imei;
|
||||
QString hardware;
|
||||
bool password_protected;
|
||||
QString os_version;
|
||||
QString timezone;
|
||||
QString wifi_mac;
|
||||
QString bt_mac;
|
||||
};
|
||||
|
||||
virtual void Init();
|
||||
|
||||
static void EventCallback(const idevice_event_t* event, void* context);
|
||||
|
||||
void DeviceAddedCallback(const char* uuid);
|
||||
void DeviceRemovedCallback(const char* uuid);
|
||||
|
||||
DeviceInfo ReadDeviceInfo(const char* uuid);
|
||||
static QString UniqueId(const char* uuid);
|
||||
|
||||
template <typename T>
|
||||
T LockAndGetDeviceInfo(const QString& id, T DeviceInfo::*field);
|
||||
|
||||
private:
|
||||
QMutex mutex_;
|
||||
QMap<QString, DeviceInfo> devices_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
T iLister::LockAndGetDeviceInfo(const QString& id, T DeviceInfo::*field) {
|
||||
QMutexLocker l(&mutex_);
|
||||
if (!devices_.contains(id))
|
||||
return T();
|
||||
|
||||
return devices_[id].*field;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,237 +0,0 @@
|
|||
/* This file is part of Clementine.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "imobiledeviceconnection.h"
|
||||
#include "core/logging.h"
|
||||
|
||||
#include <plist/plist.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QUrl>
|
||||
#include <QtDebug>
|
||||
|
||||
iMobileDeviceConnection::iMobileDeviceConnection(const QString& uuid)
|
||||
: device_(NULL), afc_(NULL), afc_port_(0) {
|
||||
idevice_error_t err = idevice_new(&device_, uuid.toUtf8().constData());
|
||||
if (err != IDEVICE_E_SUCCESS) {
|
||||
qLog(Warning) << "idevice error:" << err;
|
||||
return;
|
||||
}
|
||||
|
||||
lockdownd_client_t lockdown;
|
||||
|
||||
QByteArray label_ascii = QCoreApplication::applicationName().toAscii();
|
||||
const char* label = label_ascii.constData();
|
||||
lockdownd_error_t lockdown_err =
|
||||
lockdownd_client_new_with_handshake(device_, &lockdown, label);
|
||||
if (lockdown_err != LOCKDOWN_E_SUCCESS) {
|
||||
qLog(Warning) << "lockdown error:" << lockdown_err;
|
||||
return;
|
||||
}
|
||||
|
||||
lockdown_err = lockdownd_start_service(lockdown, "com.apple.afc", &afc_port_);
|
||||
if (lockdown_err != LOCKDOWN_E_SUCCESS) {
|
||||
qLog(Warning) << "lockdown error:" << lockdown_err;
|
||||
lockdownd_client_free(lockdown);
|
||||
return;
|
||||
}
|
||||
|
||||
afc_error_t afc_err = afc_client_new(device_, afc_port_, &afc_);
|
||||
if (afc_err != 0) {
|
||||
qLog(Warning) << "afc error:" << afc_err;
|
||||
lockdownd_client_free(lockdown);
|
||||
return;
|
||||
}
|
||||
|
||||
lockdownd_client_free(lockdown);
|
||||
}
|
||||
|
||||
iMobileDeviceConnection::~iMobileDeviceConnection() {
|
||||
if (afc_) {
|
||||
afc_client_free(afc_);
|
||||
}
|
||||
if (device_) {
|
||||
idevice_free(device_);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename F>
|
||||
T GetPListValue(plist_t node, F f) {
|
||||
T ret;
|
||||
f(node, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
QVariant iMobileDeviceConnection::GetProperty(const QString& property, const QString& domain) {
|
||||
lockdownd_client_t lockdown;
|
||||
QByteArray label_ascii = QCoreApplication::applicationName().toAscii();
|
||||
const char* label = label_ascii.constData();
|
||||
|
||||
lockdownd_error_t lockdown_err =
|
||||
lockdownd_client_new_with_handshake(device_, &lockdown, label);
|
||||
if (lockdown_err != LOCKDOWN_E_SUCCESS) {
|
||||
qLog(Warning) << "lockdown error:" << lockdown_err;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
plist_t node = NULL;
|
||||
QByteArray domain_ascii = domain.toAscii();
|
||||
const char* d = domain_ascii.isEmpty() ? NULL : domain_ascii.constData();
|
||||
//const char* d = domain.isEmpty() ? NULL : "com.apple.disk_usage";
|
||||
lockdownd_get_value(lockdown, d, property.toAscii().constData(), &node);
|
||||
lockdownd_client_free(lockdown);
|
||||
|
||||
if (!node) {
|
||||
qLog(Warning) << "get_value failed" << property << domain;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
switch (plist_get_node_type(node)) {
|
||||
case PLIST_BOOLEAN:
|
||||
return bool(GetPListValue<quint8>(node, plist_get_bool_val));
|
||||
|
||||
case PLIST_UINT:
|
||||
return QVariant::fromValue(GetPListValue<uint64_t>(node, plist_get_uint_val));
|
||||
|
||||
case PLIST_STRING: {
|
||||
char* data = GetPListValue<char*>(node, plist_get_string_val);
|
||||
QString ret = QString::fromUtf8(data);
|
||||
free(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
default:
|
||||
qLog(Warning) << "Unhandled PList type";
|
||||
return QVariant();
|
||||
}
|
||||
}
|
||||
|
||||
QStringList iMobileDeviceConnection::ReadDirectory(const QString& path,
|
||||
QDir::Filters filters) {
|
||||
char** list = NULL;
|
||||
afc_error_t err = afc_read_directory(afc_, path.toUtf8().constData(), &list);
|
||||
if (err != AFC_E_SUCCESS || !list) {
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
QStringList ret;
|
||||
for (char** p = list ; *p != NULL ; ++p) {
|
||||
QString filename = QString::fromUtf8(*p);
|
||||
free(*p);
|
||||
|
||||
if (filters == QDir::NoFilter)
|
||||
ret << filename;
|
||||
else {
|
||||
if (filters & QDir::NoDotAndDotDot && (filename == "." || filename == ".."))
|
||||
continue;
|
||||
if (!(filters & QDir::Hidden) && filename.startsWith("."))
|
||||
continue;
|
||||
|
||||
QString filetype = GetFileInfo(path + "/" + filename, "st_ifmt");
|
||||
if ((filetype == "S_IFREG" && (filters & QDir::Files)) ||
|
||||
(filetype == "S_IFDIR" && (filters & QDir::Dirs)) ||
|
||||
(filetype == "S_IFLNK" && (!(filters & QDir::NoSymLinks))))
|
||||
ret << filename;
|
||||
}
|
||||
}
|
||||
free(list);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool iMobileDeviceConnection::MkDir(const QString& path) {
|
||||
afc_error_t err = afc_make_directory(afc_, path.toUtf8().constData());
|
||||
return err == AFC_E_SUCCESS;
|
||||
}
|
||||
|
||||
QString iMobileDeviceConnection::GetFileInfo(const QString& path, const QString& key) {
|
||||
QString ret;
|
||||
char** infolist = NULL;
|
||||
afc_error_t err = afc_get_file_info(afc_, path.toUtf8().constData(), &infolist);
|
||||
if (err != AFC_E_SUCCESS || !infolist) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString last_key;
|
||||
for (char** p = infolist ; *p != NULL ; ++p) {
|
||||
if (last_key.isNull()) {
|
||||
last_key = QString::fromUtf8(*p);
|
||||
} else {
|
||||
if (last_key == key)
|
||||
ret = QString::fromUtf8(*p);
|
||||
last_key = QString();
|
||||
}
|
||||
free(*p);
|
||||
}
|
||||
free(infolist);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool iMobileDeviceConnection::Exists(const QString& path) {
|
||||
return !GetFileInfo(path, "st_ifmt").isNull();
|
||||
}
|
||||
|
||||
QString iMobileDeviceConnection::GetUnusedFilename(
|
||||
Itdb_iTunesDB* itdb, const Song& metadata) {
|
||||
// This function does the same as itdb_cp_get_dest_filename, except it
|
||||
// accesses the device's filesystem through imobiledevice.
|
||||
|
||||
// Get the total number of F.. directories
|
||||
int total_musicdirs = 0;
|
||||
for ( ; ; ++total_musicdirs) {
|
||||
QString dir;
|
||||
dir.sprintf("/iTunes_Control/Music/F%02d", total_musicdirs);
|
||||
|
||||
if (!Exists(dir))
|
||||
break;
|
||||
}
|
||||
|
||||
if (total_musicdirs <= 0) {
|
||||
qLog(Warning) << "No 'F..'' directories found on iPod";
|
||||
return QString();
|
||||
}
|
||||
|
||||
// Pick one at random
|
||||
const int dir_num = qrand() % total_musicdirs;
|
||||
QString dir;
|
||||
dir.sprintf("/iTunes_Control/Music/F%02d", dir_num);
|
||||
|
||||
if (!Exists(dir)) {
|
||||
qLog(Warning) << "Music directory doesn't exist:" << dir;
|
||||
return QString();
|
||||
}
|
||||
|
||||
// Use the same file extension as the original file, default to mp3.
|
||||
QString extension = metadata.url().path().section('.', -1, -1).toLower();
|
||||
if (extension.isEmpty())
|
||||
extension = "mp3";
|
||||
|
||||
// Loop until we find an unused filename.
|
||||
// Use the same naming convention as libgpod, which is
|
||||
// "libgpod" + 6-digit random number
|
||||
static const int kRandMax = 999999;
|
||||
QString filename;
|
||||
forever {
|
||||
filename.sprintf("libgpod%06d", qrand() % kRandMax);
|
||||
filename += "." + extension;
|
||||
|
||||
if (!Exists(dir + "/" + filename))
|
||||
break;
|
||||
}
|
||||
|
||||
return dir + "/" + filename;
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/* This file is part of Clementine.
|
||||
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
|
||||
Clementine is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Clementine is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef IMOBILEDEVICECONNECTION_H
|
||||
#define IMOBILEDEVICECONNECTION_H
|
||||
|
||||
#include "core/song.h"
|
||||
|
||||
#include <libimobiledevice/afc.h>
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
#include <gpod/itdb.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QStringList>
|
||||
#include <QVariant>
|
||||
|
||||
class iMobileDeviceConnection {
|
||||
public:
|
||||
iMobileDeviceConnection(const QString& uuid);
|
||||
~iMobileDeviceConnection();
|
||||
|
||||
afc_client_t afc() { return afc_; }
|
||||
|
||||
QVariant GetProperty(const QString& property, const QString& domain = QString());
|
||||
QStringList ReadDirectory(const QString& path, QDir::Filters filters = QDir::NoFilter);
|
||||
bool MkDir(const QString& path);
|
||||
|
||||
QString GetFileInfo(const QString& path, const QString& key);
|
||||
bool Exists(const QString& path);
|
||||
|
||||
QString GetUnusedFilename(Itdb_iTunesDB* itdb, const Song& metadata);
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(iMobileDeviceConnection);
|
||||
|
||||
idevice_t device_;
|
||||
afc_client_t afc_;
|
||||
|
||||
uint16_t afc_port_;
|
||||
};
|
||||
|
||||
#endif // IMOBILEDEVICECONNECTION_H
|
|
@ -26,10 +26,6 @@
|
|||
#include "core/taskmanager.h"
|
||||
#include "core/utilities.h"
|
||||
|
||||
#ifdef HAVE_IMOBILEDEVICE
|
||||
# include "gst/afcsrc/gstafcsrc.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MOODBAR
|
||||
# include "gst/moodbar/spectrum.h"
|
||||
#endif
|
||||
|
@ -118,10 +114,6 @@ bool GstEngine::Init() {
|
|||
void GstEngine::InitialiseGstreamer() {
|
||||
gst_init(NULL, NULL);
|
||||
|
||||
#ifdef HAVE_IMOBILEDEVICE
|
||||
afcsrc_register_static();
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MOODBAR
|
||||
gstmoodbar_register_static();
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue