Fix mtp device support
This commit is contained in:
parent
e4cefeaa8f
commit
ea6cce7068
|
@ -1055,6 +1055,7 @@ void Song::ToItdb(Itdb_Track *track) const {
|
|||
void Song::InitFromMTP(const LIBMTP_track_t *track, const QString &host) {
|
||||
|
||||
d->valid_ = true;
|
||||
d->source_ = Source_Device;
|
||||
|
||||
set_title(QString::fromUtf8(track->title));
|
||||
set_artist(QString::fromUtf8(track->artist));
|
||||
|
@ -1063,7 +1064,7 @@ void Song::InitFromMTP(const LIBMTP_track_t *track, const QString &host) {
|
|||
d->composer_ = QString::fromUtf8(track->composer);
|
||||
d->track_ = track->tracknumber;
|
||||
|
||||
d->url_ = QUrl(QString("mtp://%1/%2").arg(host, track->item_id));
|
||||
d->url_ = QUrl(QString("mtp://%1/%2").arg(host, QString::number(track->item_id)));
|
||||
d->basefilename_ = QString::number(track->item_id);
|
||||
d->filesize_ = track->filesize;
|
||||
d->mtime_ = track->modificationdate;
|
||||
|
@ -1072,7 +1073,7 @@ void Song::InitFromMTP(const LIBMTP_track_t *track, const QString &host) {
|
|||
set_length_nanosec(track->duration * kNsecPerMsec);
|
||||
|
||||
d->samplerate_ = track->samplerate;
|
||||
d->bitdepth_ = 0; //track->bitdepth;
|
||||
d->bitdepth_ = 0;
|
||||
d->bitrate_ = track->bitrate;
|
||||
|
||||
d->playcount_ = track->usecount;
|
||||
|
@ -1087,11 +1088,12 @@ void Song::InitFromMTP(const LIBMTP_track_t *track, const QString &host) {
|
|||
case LIBMTP_FILETYPE_FLAC: d->filetype_ = FileType_OggFlac; break;
|
||||
case LIBMTP_FILETYPE_MP2: d->filetype_ = FileType_MPEG; break;
|
||||
case LIBMTP_FILETYPE_M4A: d->filetype_ = FileType_MP4; break;
|
||||
default: d->filetype_ = FileType_Unknown; break;
|
||||
default:
|
||||
d->filetype_ = FileType_Unknown;
|
||||
d->valid_ = false;
|
||||
break;
|
||||
}
|
||||
|
||||
d->source_ = Source_Device;
|
||||
|
||||
}
|
||||
|
||||
void Song::ToMTP(LIBMTP_track_t *track) const {
|
||||
|
@ -1101,14 +1103,18 @@ void Song::ToMTP(LIBMTP_track_t *track) const {
|
|||
track->storage_id = 0;
|
||||
|
||||
track->title = strdup(d->title_.toUtf8().constData());
|
||||
track->artist = strdup(d->artist_.toUtf8().constData());
|
||||
track->artist = strdup(effective_albumartist().toUtf8().constData());
|
||||
track->album = strdup(d->album_.toUtf8().constData());
|
||||
track->genre = strdup(d->genre_.toUtf8().constData());
|
||||
track->date = nullptr;
|
||||
track->tracknumber = d->track_;
|
||||
track->composer = strdup(d->composer_.toUtf8().constData());
|
||||
if (d->composer_.isEmpty())
|
||||
track->composer = nullptr;
|
||||
else
|
||||
track->composer = strdup(d->composer_.toUtf8().constData());
|
||||
|
||||
track->filename = strdup(d->basefilename_.toUtf8().constData());
|
||||
|
||||
track->filesize = d->filesize_;
|
||||
track->modificationdate = d->mtime_;
|
||||
|
||||
|
@ -1123,15 +1129,15 @@ void Song::ToMTP(LIBMTP_track_t *track) const {
|
|||
track->usecount = d->playcount_;
|
||||
|
||||
switch (d->filetype_) {
|
||||
case FileType_ASF: track->filetype = LIBMTP_FILETYPE_ASF; break;
|
||||
case FileType_MP4: track->filetype = LIBMTP_FILETYPE_MP4; break;
|
||||
case FileType_MPEG: track->filetype = LIBMTP_FILETYPE_MP3; break;
|
||||
case FileType_ASF: track->filetype = LIBMTP_FILETYPE_ASF; break;
|
||||
case FileType_MP4: track->filetype = LIBMTP_FILETYPE_MP4; break;
|
||||
case FileType_MPEG: track->filetype = LIBMTP_FILETYPE_MP3; break;
|
||||
case FileType_FLAC:
|
||||
case FileType_OggFlac: track->filetype = LIBMTP_FILETYPE_FLAC; break;
|
||||
case FileType_OggFlac: track->filetype = LIBMTP_FILETYPE_FLAC; break;
|
||||
case FileType_OggSpeex:
|
||||
case FileType_OggVorbis: track->filetype = LIBMTP_FILETYPE_OGG; break;
|
||||
case FileType_WAV: track->filetype = LIBMTP_FILETYPE_WAV; break;
|
||||
default: track->filetype = LIBMTP_FILETYPE_UNDEF_AUDIO; break;
|
||||
case FileType_OggVorbis: track->filetype = LIBMTP_FILETYPE_OGG; break;
|
||||
case FileType_WAV: track->filetype = LIBMTP_FILETYPE_WAV; break;
|
||||
default: track->filetype = LIBMTP_FILETYPE_UNDEF_AUDIO; break;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -40,9 +40,7 @@ class CollectionModel;
|
|||
class DeviceLister;
|
||||
class DeviceManager;
|
||||
|
||||
class ConnectedDevice : public QObject,
|
||||
public virtual MusicStorage,
|
||||
public std::enable_shared_from_this<ConnectedDevice> {
|
||||
class ConnectedDevice : public QObject, public virtual MusicStorage, public std::enable_shared_from_this<ConnectedDevice> {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
@ -50,6 +48,7 @@ class ConnectedDevice : public QObject,
|
|||
~ConnectedDevice();
|
||||
|
||||
virtual bool Init() = 0;
|
||||
virtual void NewConnection() {}
|
||||
virtual void ConnectAsync();
|
||||
// For some devices (e.g. CD devices) we don't have callbacks to be notified when something change:
|
||||
// we can call this method to refresh device's state
|
||||
|
|
|
@ -47,10 +47,12 @@ using std::placeholders::_3;
|
|||
|
||||
QString GioLister::DeviceInfo::unique_id() const {
|
||||
|
||||
if (!volume_root_uri.isEmpty()) return volume_root_uri;
|
||||
|
||||
if (mount)
|
||||
return QString("Gio/%1/%2/%3").arg(mount_uuid, filesystem_type).arg(filesystem_size);
|
||||
|
||||
return QString("Gio/unmounted/%1").arg((qulonglong)volume.get());
|
||||
else
|
||||
return QString("Gio/unmounted/%1").arg((qulonglong)volume.get());
|
||||
|
||||
}
|
||||
|
||||
|
@ -191,43 +193,65 @@ QVariantMap GioLister::DeviceHardwareInfo(const QString &id) {
|
|||
|
||||
QList<QUrl> GioLister::MakeDeviceUrls(const QString &id) {
|
||||
|
||||
QString volume_root_uri;
|
||||
QString mount_point;
|
||||
QString uri;
|
||||
QString mount_uri;
|
||||
QString unix_device;
|
||||
|
||||
{
|
||||
QMutexLocker l(&mutex_);
|
||||
volume_root_uri = devices_[id].volume_root_uri;
|
||||
mount_point = devices_[id].mount_path;
|
||||
uri = devices_[id].mount_uri;
|
||||
mount_uri = devices_[id].mount_uri;
|
||||
unix_device = devices_[id].volume_unix_device;
|
||||
}
|
||||
|
||||
// gphoto2 gives invalid hostnames with []:, characters in
|
||||
uri.replace(QRegExp("//\\[usb:(\\d+),(\\d+)\\]"), "//usb-\\1-\\2");
|
||||
QStringList uris;
|
||||
if (!volume_root_uri.isEmpty())
|
||||
uris << volume_root_uri;
|
||||
|
||||
QUrl url(uri);
|
||||
if (!mount_uri.isEmpty())
|
||||
uris << mount_uri;
|
||||
|
||||
QList<QUrl> ret;
|
||||
|
||||
if (url.isValid()) {
|
||||
QRegExp device_re("usb/(\\d+)/(\\d+)");
|
||||
if (device_re.indexIn(unix_device) >= 0) {
|
||||
QUrlQuery url_query(url);
|
||||
url_query.addQueryItem("busnum", device_re.cap(1));
|
||||
url_query.addQueryItem("devnum", device_re.cap(2));
|
||||
url.setQuery(url_query);
|
||||
}
|
||||
for (QString uri : uris) {
|
||||
|
||||
// Special case for file:// GIO URIs - we have to check whether they point to an ipod.
|
||||
if (url.scheme() == "file") {
|
||||
ret << MakeUrlFromLocalPath(url.path());
|
||||
// gphoto2 gives invalid hostnames with []:, characters in
|
||||
uri.replace(QRegExp("//\\[usb:(\\d+),(\\d+)\\]"), "//usb-\\1-\\2");
|
||||
|
||||
QUrl url;
|
||||
|
||||
if (uri.contains(QRegExp("..+:.*"))) {
|
||||
url = QUrl::fromEncoded(uri.toUtf8());
|
||||
}
|
||||
else {
|
||||
url = MakeUrlFromLocalPath(uri);
|
||||
}
|
||||
|
||||
if (url.isValid()) {
|
||||
|
||||
// Special case for file:// GIO URIs - we have to check whether they point to an ipod.
|
||||
if (url.isLocalFile() && IsIpod(url.path())) {
|
||||
url.setScheme("ipod");
|
||||
}
|
||||
|
||||
QRegExp device_re("usb/(\\d+)/(\\d+)");
|
||||
if (device_re.indexIn(unix_device) >= 0) {
|
||||
QUrlQuery url_query(url);
|
||||
url_query.addQueryItem("busnum", device_re.cap(1));
|
||||
url_query.addQueryItem("devnum", device_re.cap(2));
|
||||
url.setQuery(url_query);
|
||||
}
|
||||
|
||||
ret << url;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ret << MakeUrlFromLocalPath(mount_point);
|
||||
if (!mount_point.isEmpty()) {
|
||||
ret << MakeUrlFromLocalPath(mount_point);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
|
@ -473,6 +497,7 @@ void GioLister::DeviceInfo::ReadVolumeInfo(GVolume *volume) {
|
|||
}
|
||||
|
||||
void GioLister::DeviceInfo::ReadDriveInfo(GDrive *drive) {
|
||||
|
||||
this->drive.reset_without_add(drive);
|
||||
if (!drive) return;
|
||||
|
||||
|
@ -485,6 +510,7 @@ QString GioLister::FindUniqueIdByMount(GMount *mount) const {
|
|||
if (info.mount == mount) return info.unique_id();
|
||||
}
|
||||
return QString();
|
||||
|
||||
}
|
||||
|
||||
QString GioLister::FindUniqueIdByVolume(GVolume *volume) const {
|
||||
|
@ -509,7 +535,7 @@ void GioLister::MountUnmountFinished(GObject *object, GAsyncResult *result, gpoi
|
|||
void GioLister::UnmountDevice(const QString &id) {
|
||||
|
||||
QMutexLocker l(&mutex_);
|
||||
if (!devices_.contains(id)) return;
|
||||
if (!devices_.contains(id) || !devices_[id].mount || devices_[id].volume_root_uri.startsWith("mtp://")) return;
|
||||
|
||||
const DeviceInfo &info = devices_[id];
|
||||
|
||||
|
@ -537,7 +563,7 @@ void GioLister::UpdateDeviceFreeSpace(const QString &id) {
|
|||
|
||||
{
|
||||
QMutexLocker l(&mutex_);
|
||||
if (!devices_.contains(id)) return;
|
||||
if (!devices_.contains(id) || !devices_[id].mount || devices_[id].volume_root_uri.startsWith("mtp://")) return;
|
||||
|
||||
DeviceInfo &device_info = devices_[id];
|
||||
|
||||
|
@ -563,10 +589,11 @@ void GioLister::UpdateDeviceFreeSpace(const QString &id) {
|
|||
|
||||
bool GioLister::DeviceNeedsMount(const QString &id) {
|
||||
QMutexLocker l(&mutex_);
|
||||
return devices_.contains(id) && !devices_[id].mount;
|
||||
return devices_.contains(id) && !devices_[id].mount && !devices_[id].volume_root_uri.startsWith("mtp://");
|
||||
}
|
||||
|
||||
int GioLister::MountDevice(const QString &id) {
|
||||
|
||||
const int request_id = next_mount_request_id_++;
|
||||
metaObject()->invokeMethod(this, "DoMountDevice", Qt::QueuedConnection, Q_ARG(QString, id), Q_ARG(int, request_id));
|
||||
return request_id;
|
||||
|
|
|
@ -47,7 +47,7 @@ class DeviceManager;
|
|||
|
||||
GPodDevice::GPodDevice(const QUrl &url, DeviceLister *lister, const QString &unique_id, DeviceManager *manager, Application *app, int database_id, bool first_time)
|
||||
: ConnectedDevice(url, lister, unique_id, manager, app, database_id, first_time),
|
||||
loader_thread_(new QThread(this)),
|
||||
loader_thread_(new QThread()),
|
||||
loader_(nullptr),
|
||||
db_(nullptr) {}
|
||||
|
||||
|
@ -68,7 +68,13 @@ bool GPodDevice::Init() {
|
|||
|
||||
}
|
||||
|
||||
GPodDevice::~GPodDevice() {}
|
||||
GPodDevice::~GPodDevice() {
|
||||
if (loader_) {
|
||||
loader_thread_->exit();
|
||||
loader_->deleteLater();
|
||||
loader_thread_->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void GPodDevice::ConnectAsync() {
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ Itdb_iTunesDB *GPodLoader::TryLoad() {
|
|||
for (GList *tracks = db->tracks; tracks != nullptr; tracks = tracks->next) {
|
||||
Itdb_Track *track = static_cast<Itdb_Track*>(tracks->data);
|
||||
|
||||
Song song;
|
||||
Song song(Song::Source_Device);
|
||||
song.InitFromItdb(track, prefix);
|
||||
song.set_directory_id(1);
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2019, Jonas Kvinge <jonas@jkvinge.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -105,3 +106,42 @@ MtpConnection::~MtpConnection() {
|
|||
if (device_) LIBMTP_Release_Device(device_);
|
||||
}
|
||||
|
||||
bool MtpConnection::GetSupportedFiletypes(QList<Song::FileType> *ret) {
|
||||
|
||||
if (!device_) return false;
|
||||
|
||||
uint16_t *list = nullptr;
|
||||
uint16_t length = 0;
|
||||
|
||||
if (LIBMTP_Get_Supported_Filetypes(device_, &list, &length) || !list || !length)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < length; ++i) {
|
||||
switch (LIBMTP_filetype_t(list[i])) {
|
||||
case LIBMTP_FILETYPE_WAV: *ret << Song::FileType_WAV; break;
|
||||
case LIBMTP_FILETYPE_MP2:
|
||||
case LIBMTP_FILETYPE_MP3: *ret << Song::FileType_MPEG; break;
|
||||
case LIBMTP_FILETYPE_WMA: *ret << Song::FileType_ASF; break;
|
||||
case LIBMTP_FILETYPE_MP4:
|
||||
case LIBMTP_FILETYPE_M4A:
|
||||
case LIBMTP_FILETYPE_AAC: *ret << Song::FileType_MP4; break;
|
||||
case LIBMTP_FILETYPE_FLAC:
|
||||
*ret << Song::FileType_FLAC;
|
||||
*ret << Song::FileType_OggFlac;
|
||||
break;
|
||||
case LIBMTP_FILETYPE_OGG:
|
||||
*ret << Song::FileType_OggVorbis;
|
||||
*ret << Song::FileType_OggSpeex;
|
||||
*ret << Song::FileType_OggFlac;
|
||||
break;
|
||||
default:
|
||||
qLog(Error) << "Unknown MTP file format" << LIBMTP_Get_Filetype_Description(LIBMTP_filetype_t(list[i]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(list);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2019, Jonas Kvinge <jonas@jkvinge.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -24,18 +25,23 @@
|
|||
#include "config.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <memory>
|
||||
#include <libmtp.h>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QList>
|
||||
#include <QUrl>
|
||||
|
||||
class MtpConnection {
|
||||
#include "core/song.h"
|
||||
|
||||
class MtpConnection : public QObject, public std::enable_shared_from_this<MtpConnection> {
|
||||
public:
|
||||
MtpConnection(const QUrl &url);
|
||||
~MtpConnection();
|
||||
|
||||
bool is_valid() const { return device_; }
|
||||
LIBMTP_mtpdevice_t *device() const { return device_; }
|
||||
bool GetSupportedFiletypes(QList<Song::FileType> *ret);
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(MtpConnection);
|
||||
|
@ -43,4 +49,4 @@ private:
|
|||
LIBMTP_mtpdevice_t *device_;
|
||||
};
|
||||
|
||||
#endif // MTPCONNECTION_H
|
||||
#endif // MTPCONNECTION_H
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2019, Jonas Kvinge <jonas@jkvinge.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -50,7 +51,7 @@ class DeviceManager;
|
|||
bool MtpDevice::sInitialisedLibMTP = false;
|
||||
|
||||
MtpDevice::MtpDevice(const QUrl &url, DeviceLister *lister, const QString &unique_id, DeviceManager *manager, Application *app, int database_id, bool first_time)
|
||||
: ConnectedDevice(url, lister, unique_id, manager, app, database_id, first_time), loader_thread_(new QThread(this)), loader_(nullptr) {
|
||||
: ConnectedDevice(url, lister, unique_id, manager, app, database_id, first_time), loader_thread_(new QThread()), loader_(nullptr) {
|
||||
|
||||
if (!sInitialisedLibMTP) {
|
||||
LIBMTP_Init();
|
||||
|
@ -59,7 +60,15 @@ MtpDevice::MtpDevice(const QUrl &url, DeviceLister *lister, const QString &uniqu
|
|||
|
||||
}
|
||||
|
||||
MtpDevice::~MtpDevice() {}
|
||||
MtpDevice::~MtpDevice() {
|
||||
if (loader_) {
|
||||
loader_thread_->exit();
|
||||
loader_->deleteLater();
|
||||
loader_ = nullptr;
|
||||
db_busy_.unlock();
|
||||
loader_thread_->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
bool MtpDevice::Init() {
|
||||
|
||||
|
@ -79,6 +88,12 @@ bool MtpDevice::Init() {
|
|||
|
||||
}
|
||||
|
||||
void MtpDevice::NewConnection() {
|
||||
|
||||
connection_.reset(new MtpConnection(url_));
|
||||
|
||||
}
|
||||
|
||||
void MtpDevice::ConnectAsync() {
|
||||
|
||||
db_busy_.lock();
|
||||
|
@ -96,7 +111,9 @@ void MtpDevice::LoadFinished(bool success) {
|
|||
|
||||
}
|
||||
|
||||
void MtpDevice::LoaderError(const QString& message) { app_->AddError(message); }
|
||||
void MtpDevice::LoaderError(const QString& message) {
|
||||
app_->AddError(message);
|
||||
}
|
||||
|
||||
bool MtpDevice::StartCopy(QList<Song::FileType> *supported_types) {
|
||||
|
||||
|
@ -104,7 +121,8 @@ bool MtpDevice::StartCopy(QList<Song::FileType> *supported_types) {
|
|||
db_busy_.lock();
|
||||
|
||||
// Connect to the device
|
||||
connection_.reset(new MtpConnection(url_));
|
||||
if (!connection_.get() || !connection_->is_valid()) NewConnection();
|
||||
if (!connection_.get() || !connection_->is_valid()) return false;
|
||||
|
||||
// Did the caller want a list of supported types?
|
||||
if (supported_types) {
|
||||
|
@ -129,7 +147,7 @@ static int ProgressCallback(uint64_t const sent, uint64_t const total, void cons
|
|||
|
||||
bool MtpDevice::CopyToStorage(const CopyJob &job) {
|
||||
|
||||
if (!connection_->is_valid()) return false;
|
||||
if (!connection_.get() || !connection_->is_valid()) return false;
|
||||
|
||||
// Convert metadata
|
||||
LIBMTP_track_t track;
|
||||
|
@ -140,9 +158,11 @@ bool MtpDevice::CopyToStorage(const CopyJob &job) {
|
|||
if (ret != 0) return false;
|
||||
|
||||
// Add it to our CollectionModel
|
||||
Song metadata_on_device;
|
||||
Song metadata_on_device(Song::Source_Device);
|
||||
metadata_on_device.InitFromMTP(&track, url_.host());
|
||||
metadata_on_device.set_directory_id(1);
|
||||
metadata_on_device.set_artist(metadata_on_device.effective_albumartist());
|
||||
metadata_on_device.set_albumartist("");
|
||||
songs_to_add_ << metadata_on_device;
|
||||
|
||||
// Remove the original if requested
|
||||
|
@ -164,8 +184,6 @@ void MtpDevice::FinishCopy(bool success) {
|
|||
songs_to_add_.clear();
|
||||
songs_to_remove_.clear();
|
||||
|
||||
connection_.reset();
|
||||
|
||||
db_busy_.unlock();
|
||||
|
||||
ConnectedDevice::FinishCopy(success);
|
||||
|
@ -176,6 +194,8 @@ void MtpDevice::StartDelete() { StartCopy(nullptr); }
|
|||
|
||||
bool MtpDevice::DeleteFromStorage(const DeleteJob &job) {
|
||||
|
||||
if (!connection_.get() || !connection_->is_valid()) return false;
|
||||
|
||||
// Extract the ID from the song's URL
|
||||
QString filename = job.metadata_.url().path();
|
||||
filename.remove('/');
|
||||
|
@ -201,6 +221,7 @@ bool MtpDevice::GetSupportedFiletypes(QList<Song::FileType> *ret) {
|
|||
|
||||
QMutexLocker l(&db_busy_);
|
||||
MtpConnection connection(url_);
|
||||
|
||||
if (!connection.is_valid()) {
|
||||
qLog(Warning) << "Error connecting to MTP device, couldn't get list of supported filetypes";
|
||||
return false;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2019, Jonas Kvinge <jonas@jkvinge.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -40,6 +41,7 @@
|
|||
class Application;
|
||||
class DeviceLister;
|
||||
class DeviceManager;
|
||||
class DeviceConnection;
|
||||
class MtpConnection;
|
||||
class MtpLoader;
|
||||
struct LIBMTP_mtpdevice_struct;
|
||||
|
@ -54,6 +56,7 @@ class MtpDevice : public ConnectedDevice {
|
|||
static QStringList url_schemes() { return QStringList() << "mtp" << "gphoto2"; }
|
||||
|
||||
bool Init();
|
||||
void NewConnection();
|
||||
void ConnectAsync();
|
||||
|
||||
bool GetSupportedFiletypes(QList<Song::FileType>* ret);
|
||||
|
@ -68,6 +71,8 @@ class MtpDevice : public ConnectedDevice {
|
|||
bool DeleteFromStorage(const DeleteJob& job);
|
||||
void FinishDelete(bool success);
|
||||
|
||||
MtpConnection *connection() { return connection_.get(); }
|
||||
|
||||
private slots:
|
||||
void LoadFinished(bool success);
|
||||
void LoaderError(const QString& message);
|
||||
|
@ -87,7 +92,8 @@ class MtpDevice : public ConnectedDevice {
|
|||
SongList songs_to_add_;
|
||||
SongList songs_to_remove_;
|
||||
|
||||
std::unique_ptr<MtpConnection> connection_;
|
||||
std::shared_ptr<MtpConnection> connection_;
|
||||
|
||||
};
|
||||
|
||||
#endif // MTPDEVICE_H
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2019, Jonas Kvinge <jonas@jkvinge.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -27,17 +28,19 @@
|
|||
|
||||
#include "core/taskmanager.h"
|
||||
#include "core/song.h"
|
||||
#include "core/logging.h"
|
||||
#include "collection/collectionbackend.h"
|
||||
#include "connecteddevice.h"
|
||||
#include "mtpdevice.h"
|
||||
#include "mtpconnection.h"
|
||||
#include "mtploader.h"
|
||||
|
||||
MtpLoader::MtpLoader(const QUrl &url, TaskManager *task_manager, CollectionBackend *backend, std::shared_ptr<ConnectedDevice> device)
|
||||
: QObject(nullptr),
|
||||
device_(device),
|
||||
url_(url),
|
||||
task_manager_(task_manager),
|
||||
backend_(backend),
|
||||
connection_(nullptr) {
|
||||
device_(device) {
|
||||
original_thread_ = thread();
|
||||
}
|
||||
|
||||
|
@ -61,23 +64,28 @@ void MtpLoader::LoadDatabase() {
|
|||
|
||||
bool MtpLoader::TryLoad() {
|
||||
|
||||
MtpConnection dev(url_);
|
||||
if (!dev.is_valid()) {
|
||||
MtpDevice *device = dynamic_cast<MtpDevice*>(device_.get()); // FIXME
|
||||
|
||||
if (!device->connection() || !device->connection()->is_valid())
|
||||
device->NewConnection();
|
||||
|
||||
if (!device->connection() || !device->connection()->is_valid()) {
|
||||
emit Error(tr("Error connecting MTP device %1").arg(url_.toString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load the list of songs on the device
|
||||
SongList songs;
|
||||
LIBMTP_track_t* tracks = LIBMTP_Get_Tracklisting_With_Callback(dev.device(), nullptr, nullptr);
|
||||
LIBMTP_track_t* tracks = LIBMTP_Get_Tracklisting_With_Callback(device->connection()->device(), nullptr, nullptr);
|
||||
while (tracks) {
|
||||
LIBMTP_track_t *track = tracks;
|
||||
|
||||
Song song;
|
||||
Song song(Song::Source_Device);
|
||||
song.InitFromMTP(track, url_.host());
|
||||
song.set_directory_id(1);
|
||||
songs << song;
|
||||
|
||||
if (song.is_valid() && !song.artist().isEmpty() && !song.title().isEmpty()) {
|
||||
song.set_directory_id(1);
|
||||
songs << song;
|
||||
}
|
||||
tracks = tracks->next;
|
||||
LIBMTP_destroy_track_t(track);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Strawberry Music Player
|
||||
* This file was part of Clementine.
|
||||
* Copyright 2010, David Sansome <me@davidsansome.com>
|
||||
* Copyright 2019, Jonas Kvinge <jonas@jkvinge.net>
|
||||
*
|
||||
* Strawberry is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -34,6 +35,7 @@
|
|||
class TaskManager;
|
||||
class CollectionBackend;
|
||||
class ConnectedDevice;
|
||||
class MtpDevice;
|
||||
class MtpConnection;
|
||||
|
||||
class MtpLoader : public QObject {
|
||||
|
@ -57,13 +59,13 @@ class MtpLoader : public QObject {
|
|||
bool TryLoad();
|
||||
|
||||
private:
|
||||
std::shared_ptr<ConnectedDevice> device_;
|
||||
QThread *original_thread_;
|
||||
|
||||
QUrl url_;
|
||||
TaskManager *task_manager_;
|
||||
CollectionBackend *backend_;
|
||||
MtpConnection *connection_;
|
||||
std::shared_ptr<ConnectedDevice> device_;
|
||||
std::shared_ptr<MtpConnection> connection_;
|
||||
QThread *original_thread_;
|
||||
|
||||
};
|
||||
|
||||
#endif // MTPLOADER_H
|
||||
|
|
|
@ -125,10 +125,11 @@ QList<QUrl> Udisks2Lister::MakeDeviceUrls(const QString &id) {
|
|||
QList<QUrl> ret;
|
||||
if (!device_data_.contains(id)) return ret;
|
||||
// Special case for Apple
|
||||
if(id.contains("iPod")) {
|
||||
ret << MakeUrlFromLocalPath(device_data_[id].mount_paths.at(0));
|
||||
} else {
|
||||
ret << QUrl::fromLocalFile(device_data_[id].mount_paths.at(0));
|
||||
if (id.contains("iPod")) {
|
||||
ret << MakeUrlFromLocalPath(device_data_[id].mount_paths.at(0));
|
||||
}
|
||||
else {
|
||||
ret << QUrl::fromLocalFile(device_data_[id].mount_paths.at(0));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -79,6 +79,13 @@ Organise::Organise(TaskManager *task_manager, std::shared_ptr<MusicStorage> dest
|
|||
|
||||
}
|
||||
|
||||
Organise::~Organise() {
|
||||
if (thread_) {
|
||||
thread_->quit();
|
||||
thread_->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void Organise::Start() {
|
||||
|
||||
if (thread_) return;
|
||||
|
@ -95,6 +102,7 @@ void Organise::Start() {
|
|||
|
||||
moveToThread(thread_);
|
||||
thread_->start();
|
||||
|
||||
}
|
||||
|
||||
void Organise::ProcessSomeFiles() {
|
||||
|
|
|
@ -62,6 +62,7 @@ class Organise : public QObject {
|
|||
typedef QList<NewSongInfo> NewSongInfoList;
|
||||
|
||||
Organise(TaskManager *task_manager, std::shared_ptr<MusicStorage> destination, const OrganiseFormat &format, bool copy, bool overwrite, bool mark_as_listened, bool albumcover, const NewSongInfoList &songs, bool eject_after);
|
||||
~Organise();
|
||||
|
||||
static const int kBatchSize;
|
||||
#ifdef HAVE_GSTREAMER
|
||||
|
|
Loading…
Reference in New Issue