Fixes to imobiledeviceconnection support
This commit is contained in:
parent
823f65f1ca
commit
6c77294a86
@ -20,6 +20,8 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QThread>
|
||||
#include <QFile>
|
||||
#include <QList>
|
||||
@ -28,6 +30,7 @@
|
||||
|
||||
#include "core/application.h"
|
||||
#include "core/utilities.h"
|
||||
#include "core/song.h"
|
||||
#include "afcdevice.h"
|
||||
#include "afcfile.h"
|
||||
#include "afctransfer.h"
|
||||
@ -35,13 +38,23 @@
|
||||
#include "gpodloader.h"
|
||||
#include "imobiledeviceconnection.h"
|
||||
|
||||
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_(nullptr)
|
||||
{
|
||||
}
|
||||
AfcDevice::AfcDevice(const QUrl &url, DeviceLister *lister, const QString &unique_id, DeviceManager *manager, Application *app, const int database_id, const bool first_time) : GPodDevice(url, lister, unique_id, manager, app, database_id, first_time), transfer_(nullptr) {}
|
||||
|
||||
AfcDevice::~AfcDevice() {
|
||||
|
||||
Utilities::RemoveRecursive(local_path_);
|
||||
|
||||
if (loader_) {
|
||||
loader_->deleteLater();
|
||||
loader_ = nullptr;
|
||||
}
|
||||
|
||||
if (loader_thread_) {
|
||||
loader_thread_->exit();
|
||||
loader_thread_->deleteLater();
|
||||
loader_thread_ = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool AfcDevice::Init() {
|
||||
@ -51,11 +64,16 @@ bool AfcDevice::Init() {
|
||||
InitBackendDirectory(local_path_, first_time_, false);
|
||||
model_->Init();
|
||||
|
||||
if (!loader_thread_) loader_thread_ = new QThread();
|
||||
|
||||
if (url_.isEmpty() || url_.path().isEmpty()) return false;
|
||||
|
||||
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();
|
||||
|
||||
@ -63,7 +81,7 @@ bool AfcDevice::Init() {
|
||||
|
||||
}
|
||||
|
||||
void AfcDevice::CopyFinished(bool success) {
|
||||
void AfcDevice::CopyFinished(const bool success) {
|
||||
|
||||
transfer_->deleteLater();
|
||||
transfer_ = nullptr;
|
||||
@ -76,12 +94,12 @@ void AfcDevice::CopyFinished(bool success) {
|
||||
// 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_->set_song_type(Song::FileType_Stream);
|
||||
loader_->moveToThread(loader_thread_);
|
||||
|
||||
connect(loader_, SIGNAL(Error(QString)), SIGNAL(Error(QString)));
|
||||
connect(loader_, SIGNAL(Error(QString)), SLOT(LoaderError(QString)));
|
||||
connect(loader_, SIGNAL(TaskStarted(int)), SIGNAL(TaskStarted(int)));
|
||||
connect(loader_, SIGNAL(LoadFinished(Itdb_iTunesDB*)), SLOT(LoadFinished(Itdb_iTunesDB*)));
|
||||
connect(loader_, SIGNAL(LoadFinished(Itdb_iTunesDB*, bool)), SLOT(LoadFinished(Itdb_iTunesDB*, bool)));
|
||||
QMetaObject::invokeMethod(loader_, "LoadDatabase");
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,8 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <gpod/itdb.h>
|
||||
|
||||
#include <QObject>
|
||||
@ -41,7 +43,7 @@ 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);
|
||||
Q_INVOKABLE AfcDevice(const QUrl &url, DeviceLister *lister, const QString &unique_id, DeviceManager *manager, Application *app, const int database_id, const bool first_time);
|
||||
~AfcDevice();
|
||||
|
||||
bool Init();
|
||||
@ -50,20 +52,20 @@ public:
|
||||
|
||||
bool StartCopy(QList<Song::FileType> *supported_types);
|
||||
bool CopyToStorage(const CopyJob &job);
|
||||
void FinishCopy(bool success);
|
||||
void FinishCopy(const bool success);
|
||||
|
||||
bool DeleteFromStorage(const DeleteJob &job);
|
||||
|
||||
protected:
|
||||
protected:
|
||||
void FinaliseDatabase();
|
||||
|
||||
private slots:
|
||||
private slots:
|
||||
void CopyFinished(bool success);
|
||||
|
||||
private:
|
||||
private:
|
||||
void RemoveRecursive(const QString &path);
|
||||
|
||||
private:
|
||||
private:
|
||||
AfcTransfer *transfer_;
|
||||
std::shared_ptr<iMobileDeviceConnection> connection_;
|
||||
|
||||
|
@ -45,9 +45,6 @@ AfcTransfer::AfcTransfer(const QString &uuid, const QString &local_destination,
|
||||
|
||||
}
|
||||
|
||||
AfcTransfer::~AfcTransfer() {
|
||||
}
|
||||
|
||||
void AfcTransfer::CopyFromDevice() {
|
||||
|
||||
int task_id = 0;
|
||||
@ -57,6 +54,7 @@ void AfcTransfer::CopyFromDevice() {
|
||||
}
|
||||
|
||||
// Connect to the device
|
||||
|
||||
iMobileDeviceConnection c(uuid_);
|
||||
|
||||
// Copy directories. If one fails we stop.
|
||||
|
@ -40,7 +40,6 @@ class AfcTransfer : public QObject {
|
||||
|
||||
public:
|
||||
explicit AfcTransfer(const QString &uuid, const QString &local_destination, TaskManager *task_manager, std::shared_ptr<ConnectedDevice> device);
|
||||
~AfcTransfer();
|
||||
|
||||
bool CopyToDevice(iMobileDeviceConnection *connection);
|
||||
|
||||
|
@ -78,7 +78,7 @@ ConnectedDevice::~ConnectedDevice() {
|
||||
backend_->deleteLater();
|
||||
}
|
||||
|
||||
void ConnectedDevice::InitBackendDirectory(const QString &mount_point, bool first_time, bool rewrite_path) {
|
||||
void ConnectedDevice::InitBackendDirectory(const QString &mount_point, const bool first_time, const bool rewrite_path) {
|
||||
|
||||
if (first_time || backend_->GetAllDirectories().isEmpty()) {
|
||||
backend_->AddDirectory(mount_point);
|
||||
|
@ -79,7 +79,7 @@ class ConnectedDevice : public QObject, public virtual MusicStorage, public std:
|
||||
void CloseFinished(const QString& id);
|
||||
|
||||
protected:
|
||||
void InitBackendDirectory(const QString &mount_point, bool first_time, bool rewrite_path = true);
|
||||
void InitBackendDirectory(const QString &mount_point, const bool first_time, const bool rewrite_path = true);
|
||||
|
||||
protected:
|
||||
Application *app_;
|
||||
|
@ -283,6 +283,10 @@ void GioLister::VolumeAdded(GVolume *volume) {
|
||||
|
||||
DeviceInfo info;
|
||||
info.ReadVolumeInfo(volume);
|
||||
if (info.volume_root_uri.startsWith("afc://") || info.volume_root_uri.startsWith("gphoto2://")) {
|
||||
// Handled by iLister.
|
||||
return;
|
||||
}
|
||||
#ifdef HAVE_AUDIOCD
|
||||
if (info.volume_root_uri.startsWith("cdda"))
|
||||
// Audio CD devices are already handled by CDDA lister
|
||||
@ -322,6 +326,10 @@ void GioLister::MountAdded(GMount *mount) {
|
||||
|
||||
DeviceInfo info;
|
||||
info.ReadVolumeInfo(g_mount_get_volume(mount));
|
||||
if (info.volume_root_uri.startsWith("afc://") || info.volume_root_uri.startsWith("gphoto2://")) {
|
||||
// Handled by iLister.
|
||||
return;
|
||||
}
|
||||
#ifdef HAVE_AUDIOCD
|
||||
if (info.volume_root_uri.startsWith("cdda"))
|
||||
// Audio CD devices are already handled by CDDA lister
|
||||
@ -566,7 +574,7 @@ void GioLister::UpdateDeviceFreeSpace(const QString &id) {
|
||||
bool GioLister::DeviceNeedsMount(const QString &id) {
|
||||
|
||||
QMutexLocker l(&mutex_);
|
||||
return devices_.contains(id) && !devices_[id].mount_ptr && !devices_[id].volume_root_uri.startsWith("mtp://");
|
||||
return devices_.contains(id) && !devices_[id].mount_ptr && !devices_[id].volume_root_uri.startsWith("mtp://") && !devices_[id].volume_root_uri.startsWith("gphoto2://");
|
||||
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@
|
||||
class DeviceLister;
|
||||
class DeviceManager;
|
||||
|
||||
GPodDevice::GPodDevice(const QUrl &url, DeviceLister *lister, const QString &unique_id, DeviceManager *manager, Application *app, int database_id, bool first_time)
|
||||
GPodDevice::GPodDevice(const QUrl &url, DeviceLister *lister, const QString &unique_id, DeviceManager *manager, Application *app, const int database_id, const bool first_time)
|
||||
: ConnectedDevice(url, lister, unique_id, manager, app, database_id, first_time),
|
||||
loader_(nullptr),
|
||||
loader_thread_(nullptr),
|
||||
@ -72,11 +72,15 @@ bool GPodDevice::Init() {
|
||||
}
|
||||
|
||||
GPodDevice::~GPodDevice() {
|
||||
|
||||
if (loader_) {
|
||||
loader_thread_->exit();
|
||||
loader_->deleteLater();
|
||||
loader_thread_->deleteLater();
|
||||
loader_ = nullptr;
|
||||
loader_thread_ = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GPodDevice::ConnectAsync() {
|
||||
|
@ -47,13 +47,7 @@ class GPodDevice : public ConnectedDevice, public virtual MusicStorage {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Q_INVOKABLE GPodDevice(
|
||||
const QUrl &url, DeviceLister *lister,
|
||||
const QString &unique_id,
|
||||
DeviceManager *manager,
|
||||
Application *app,
|
||||
int database_id,
|
||||
bool first_time);
|
||||
Q_INVOKABLE GPodDevice(const QUrl &url, DeviceLister *lister, const QString &unique_id, DeviceManager *manager, Application *app, const int database_id, const bool first_time);
|
||||
~GPodDevice();
|
||||
|
||||
bool Init();
|
||||
|
@ -52,7 +52,7 @@ class GPodLoader : public QObject {
|
||||
void LoadDatabase();
|
||||
|
||||
signals:
|
||||
void Error(const QString &message);
|
||||
void Error(QString message);
|
||||
void TaskStarted(int task_id);
|
||||
void LoadFinished(Itdb_iTunesDB *db, bool success);
|
||||
|
||||
|
@ -36,15 +36,17 @@ iLister::iLister() {}
|
||||
iLister::~iLister() {}
|
||||
|
||||
bool iLister::Init() {
|
||||
|
||||
idevice_event_subscribe(&EventCallback, reinterpret_cast<void*>(this));
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
void iLister::EventCallback(const idevice_event_t *event, void *context) {
|
||||
|
||||
iLister *me = reinterpret_cast<iLister*>(context);
|
||||
|
||||
const char *uuid = event->udid;
|
||||
QString uuid = QString::fromUtf8(event->udid);
|
||||
|
||||
switch (event->event) {
|
||||
case IDEVICE_DEVICE_ADD:
|
||||
@ -60,7 +62,7 @@ void iLister::EventCallback(const idevice_event_t *event, void *context) {
|
||||
}
|
||||
|
||||
|
||||
void iLister::DeviceAddedCallback(const char *uuid) {
|
||||
void iLister::DeviceAddedCallback(const QString uuid) {
|
||||
|
||||
DeviceInfo info = ReadDeviceInfo(uuid);
|
||||
if (!info.valid) return;
|
||||
@ -82,9 +84,10 @@ void iLister::DeviceAddedCallback(const char *uuid) {
|
||||
|
||||
}
|
||||
|
||||
void iLister::DeviceRemovedCallback(const char *uuid) {
|
||||
void iLister::DeviceRemovedCallback(const QString uuid) {
|
||||
|
||||
QString id = UniqueId(uuid);
|
||||
|
||||
{
|
||||
QMutexLocker l(&mutex_);
|
||||
if (!devices_.contains(id))
|
||||
@ -97,8 +100,8 @@ void iLister::DeviceRemovedCallback(const char *uuid) {
|
||||
|
||||
}
|
||||
|
||||
QString iLister::UniqueId(const char *uuid) {
|
||||
return "ithing/" + QString::fromUtf8(uuid);
|
||||
QString iLister::UniqueId(const QString uuid) {
|
||||
return "ithing/" + uuid;
|
||||
}
|
||||
|
||||
QStringList iLister::DeviceUniqueIDs() {
|
||||
@ -191,12 +194,13 @@ QList<QUrl> iLister::MakeDeviceUrls(const QString &id) {
|
||||
|
||||
}
|
||||
|
||||
iLister::DeviceInfo iLister::ReadDeviceInfo(const char *uuid) {
|
||||
iLister::DeviceInfo iLister::ReadDeviceInfo(const QString uuid) {
|
||||
|
||||
DeviceInfo ret;
|
||||
|
||||
iMobileDeviceConnection conn(uuid);
|
||||
if (!conn.is_valid()) return ret;
|
||||
|
||||
ret.valid = conn.is_valid();
|
||||
ret.uuid = uuid;
|
||||
ret.product_type = conn.GetProperty("ProductType").toString();
|
||||
|
@ -37,6 +37,7 @@
|
||||
|
||||
class iLister : public DeviceLister {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit iLister();
|
||||
~iLister();
|
||||
@ -58,7 +59,7 @@ class iLister : public DeviceLister {
|
||||
|
||||
private:
|
||||
struct DeviceInfo {
|
||||
DeviceInfo() : valid(false), free_bytes(0), total_bytes(0) {}
|
||||
DeviceInfo() : valid(false), free_bytes(0), total_bytes(0), password_protected(false) {}
|
||||
|
||||
bool valid;
|
||||
|
||||
@ -83,16 +84,16 @@ class iLister : public DeviceLister {
|
||||
|
||||
static void EventCallback(const idevice_event_t *event, void *context);
|
||||
|
||||
void DeviceAddedCallback(const char *uuid);
|
||||
void DeviceRemovedCallback(const char *uuid);
|
||||
void DeviceAddedCallback(const QString uuid);
|
||||
void DeviceRemovedCallback(const QString uuid);
|
||||
|
||||
DeviceInfo ReadDeviceInfo(const char *uuid);
|
||||
static QString UniqueId(const char *uuid);
|
||||
DeviceInfo ReadDeviceInfo(const QString uuid);
|
||||
static QString UniqueId(const QString uuid);
|
||||
|
||||
template <typename T>
|
||||
T LockAndGetDeviceInfo(const QString &id, T DeviceInfo::*field);
|
||||
|
||||
private:
|
||||
private:
|
||||
QMutex mutex_;
|
||||
QMap<QString, DeviceInfo> devices_;
|
||||
};
|
||||
|
@ -22,6 +22,11 @@
|
||||
|
||||
#include <plist/plist.h>
|
||||
|
||||
#include <libimobiledevice/afc.h>
|
||||
#include <libimobiledevice/libimobiledevice.h>
|
||||
#include <libimobiledevice/mobileactivation.h>
|
||||
#include <libimobiledevice/lockdown.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QByteArray>
|
||||
@ -29,15 +34,18 @@
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
#include <QtDebug>
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
# include <QRandomGenerator>
|
||||
#endif
|
||||
|
||||
#include "core/logging.h"
|
||||
#include "imobiledeviceconnection.h"
|
||||
|
||||
iMobileDeviceConnection::iMobileDeviceConnection(const QString &uuid) : device_(nullptr), afc_(nullptr) {
|
||||
iMobileDeviceConnection::iMobileDeviceConnection(const QString uuid) : device_(nullptr), afc_(nullptr) {
|
||||
|
||||
idevice_error_t err = idevice_new(&device_, uuid.toUtf8().constData());
|
||||
if (err != IDEVICE_E_SUCCESS) {
|
||||
qLog(Warning) << "idevice error:" << err;
|
||||
qLog(Warning) << "idevice_new error:" << err;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -47,27 +55,25 @@ iMobileDeviceConnection::iMobileDeviceConnection(const QString &uuid) : device_(
|
||||
lockdownd_client_t lockdown;
|
||||
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;
|
||||
qLog(Warning) << "lockdownd_client_new_with_handshake error:" << lockdown_err;
|
||||
return;
|
||||
}
|
||||
|
||||
lockdownd_service_descriptor_t lockdown_service_desc;
|
||||
lockdown_err = lockdownd_start_service(lockdown, "com.apple.afc", &lockdown_service_desc);
|
||||
if (lockdown_err != LOCKDOWN_E_SUCCESS) {
|
||||
qLog(Warning) << "lockdown error:" << lockdown_err;
|
||||
qLog(Warning) << "lockdownd_start_service error:" << lockdown_err;
|
||||
lockdownd_client_free(lockdown);
|
||||
return;
|
||||
}
|
||||
|
||||
afc_error_t afc_err = afc_client_new(device_, lockdown_service_desc, &afc_);
|
||||
if (afc_err != AFC_E_SUCCESS) {
|
||||
qLog(Warning) << "afc error:" << afc_err;
|
||||
lockdownd_service_descriptor_free(lockdown_service_desc);
|
||||
qLog(Warning) << "afc_client_new error:" << afc_err;
|
||||
lockdownd_client_free(lockdown);
|
||||
return;
|
||||
}
|
||||
|
||||
lockdownd_service_descriptor_free(lockdown_service_desc);
|
||||
lockdownd_client_free(lockdown);
|
||||
|
||||
}
|
||||
@ -187,6 +193,7 @@ QString iMobileDeviceConnection::GetFileInfo(const QString &path, const QString
|
||||
char **infolist = nullptr;
|
||||
afc_error_t err = afc_get_file_info(afc_, path.toUtf8().constData(), &infolist);
|
||||
if (err != AFC_E_SUCCESS || !infolist) {
|
||||
qLog(Debug) << "afc_get_file_info error:" << path << err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -232,7 +239,12 @@ QString iMobileDeviceConnection::GetUnusedFilename(Itdb_iTunesDB *itdb, const So
|
||||
}
|
||||
|
||||
// Pick one at random
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
|
||||
const int dir_num = QRandomGenerator::global()->bounded(total_musicdirs);
|
||||
#else
|
||||
const int dir_num = qrand() % total_musicdirs;
|
||||
#endif
|
||||
|
||||
QString dir = QString::asprintf("/iTunes_Control/Music/F%02d", dir_num);
|
||||
|
||||
if (!Exists(dir)) {
|
||||
|
@ -38,7 +38,7 @@
|
||||
|
||||
class iMobileDeviceConnection {
|
||||
public:
|
||||
explicit iMobileDeviceConnection(const QString &uuid);
|
||||
explicit iMobileDeviceConnection(const QString uuid);
|
||||
~iMobileDeviceConnection();
|
||||
|
||||
afc_client_t afc() { return afc_; }
|
||||
|
@ -51,7 +51,7 @@ class MtpDevice : public ConnectedDevice {
|
||||
Q_INVOKABLE MtpDevice(const QUrl &url, DeviceLister *lister, const QString &unique_id, DeviceManager *manager, Application *app, int database_id, bool first_time);
|
||||
~MtpDevice();
|
||||
|
||||
static QStringList url_schemes() { return QStringList() << "mtp" << "gphoto2"; }
|
||||
static QStringList url_schemes() { return QStringList() << "mtp"; }
|
||||
|
||||
bool Init();
|
||||
void ConnectAsync();
|
||||
|
Loading…
x
Reference in New Issue
Block a user