2010-08-22 21:18:22 +02:00
|
|
|
/* This file is part of Clementine.
|
|
|
|
|
|
|
|
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 "devicemanager.h"
|
|
|
|
#include "wmdmdevice.h"
|
2010-08-23 21:13:27 +02:00
|
|
|
#include "wmdmlister.h"
|
2010-08-22 21:18:22 +02:00
|
|
|
#include "wmdmloader.h"
|
2010-08-28 16:33:23 +02:00
|
|
|
#include "wmdmprogress.h"
|
2010-08-23 21:13:27 +02:00
|
|
|
#include "wmdmthread.h"
|
|
|
|
#include "core/utilities.h"
|
2010-08-22 21:18:22 +02:00
|
|
|
#include "library/librarybackend.h"
|
|
|
|
#include "library/librarymodel.h"
|
|
|
|
|
2010-08-23 21:13:27 +02:00
|
|
|
#include <QDir>
|
2010-08-22 21:18:22 +02:00
|
|
|
#include <QThread>
|
|
|
|
|
2010-08-23 21:13:27 +02:00
|
|
|
#include <boost/scoped_array.hpp>
|
|
|
|
|
|
|
|
#include <mswmdm.h>
|
|
|
|
|
2010-08-22 21:18:22 +02:00
|
|
|
WmdmDevice::WmdmDevice(const QUrl& url, DeviceLister* lister,
|
|
|
|
const QString& unique_id, DeviceManager* manager,
|
|
|
|
int database_id, bool first_time)
|
|
|
|
: ConnectedDevice(url, lister, unique_id, manager, database_id, first_time),
|
|
|
|
loader_thread_(new QThread(this)),
|
|
|
|
loader_(NULL)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
WmdmDevice::~WmdmDevice() {
|
|
|
|
}
|
|
|
|
|
|
|
|
void WmdmDevice::Init() {
|
|
|
|
InitBackendDirectory("/", first_time_, false);
|
|
|
|
model_->Init();
|
|
|
|
|
|
|
|
loader_ = new WmdmLoader(manager_->task_manager(), backend_, shared_from_this());
|
|
|
|
loader_->moveToThread(loader_thread_);
|
|
|
|
|
|
|
|
connect(loader_, SIGNAL(Error(QString)), SIGNAL(Error(QString)));
|
|
|
|
connect(loader_, SIGNAL(TaskStarted(int)), SIGNAL(TaskStarted(int)));
|
|
|
|
connect(loader_, SIGNAL(LoadFinished()), SLOT(LoadFinished()));
|
|
|
|
connect(loader_thread_, SIGNAL(started()), loader_, SLOT(LoadDatabase()));
|
|
|
|
loader_thread_->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
void WmdmDevice::LoadFinished() {
|
|
|
|
loader_->deleteLater();
|
|
|
|
loader_ = NULL;
|
|
|
|
}
|
|
|
|
|
2010-08-30 14:22:15 +02:00
|
|
|
bool WmdmDevice::StartCopy(QList<Song::FileType>* supported_types) {
|
2010-08-22 21:18:22 +02:00
|
|
|
// Ensure only one "organise files" can be active at any one time
|
|
|
|
db_busy_.lock();
|
2010-08-23 21:13:27 +02:00
|
|
|
|
|
|
|
// This initialises COM and gets a connection to the device
|
|
|
|
thread_.reset(new WmdmThread);
|
|
|
|
|
|
|
|
// Find a place to put the files. We default to the root folder for now, but
|
|
|
|
// could look for a "Music" folder in the future?
|
|
|
|
WmdmLister* wmdm_lister = static_cast<WmdmLister*>(lister());
|
|
|
|
QString canonical_name = wmdm_lister->DeviceCanonicalName(unique_id());
|
|
|
|
IWMDMStorage* destination = thread_->GetRootStorage(canonical_name);
|
|
|
|
|
|
|
|
// Get the control interface
|
|
|
|
destination->QueryInterface(IID_IWMDMStorageControl3, (void**)&storage_control_);
|
|
|
|
|
|
|
|
// Get the storage3 interface for CreateEmptyMetadataObject later
|
|
|
|
destination->QueryInterface(IID_IWMDMStorage3, (void**)&storage_);
|
|
|
|
|
|
|
|
destination->Release();
|
2010-08-30 14:22:15 +02:00
|
|
|
|
|
|
|
// Did the caller want a list of supported filetypes?
|
|
|
|
if (supported_types) {
|
|
|
|
IWMDMDevice* device = thread_->GetDeviceByCanonicalName(canonical_name);
|
|
|
|
if (!GetSupportedFiletypes(supported_types, device)) {
|
|
|
|
device->Release();
|
|
|
|
FinishCopy(false);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
device->Release();
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2010-08-22 21:18:22 +02:00
|
|
|
}
|
|
|
|
|
2010-08-28 23:55:30 +02:00
|
|
|
bool WmdmDevice::CopyToStorage(const CopyJob& job) {
|
2010-08-23 21:13:27 +02:00
|
|
|
if (!storage_control_ || !storage_)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Create the song metadata
|
|
|
|
IWMDMMetaData* metadata_iface = NULL;
|
|
|
|
storage_->CreateEmptyMetadataObject(&metadata_iface);
|
2010-08-28 23:55:30 +02:00
|
|
|
job.metadata_.ToWmdm(metadata_iface);
|
2010-08-23 21:13:27 +02:00
|
|
|
|
|
|
|
// Convert the filenames to wchars
|
2010-08-28 23:55:30 +02:00
|
|
|
ScopedWCharArray source_filename(QDir::toNativeSeparators(job.source_));
|
|
|
|
ScopedWCharArray dest_filename(job.metadata_.basefilename());
|
2010-08-23 21:13:27 +02:00
|
|
|
|
2010-08-28 16:33:23 +02:00
|
|
|
// Create the progress object
|
2010-08-29 00:15:54 +02:00
|
|
|
WmdmProgress progress(job.progress_);
|
2010-08-28 16:33:23 +02:00
|
|
|
|
2010-08-23 21:13:27 +02:00
|
|
|
// Copy the file
|
|
|
|
IWMDMStorage* new_storage = NULL;
|
|
|
|
if (storage_control_->Insert3(
|
|
|
|
WMDM_MODE_BLOCK | WMDM_STORAGECONTROL_INSERTINTO |
|
|
|
|
WMDM_FILE_CREATE_OVERWRITE | WMDM_CONTENT_FILE,
|
|
|
|
WMDM_FILE_ATTR_FOLDER,
|
|
|
|
source_filename,
|
|
|
|
dest_filename,
|
|
|
|
NULL, // operation
|
2010-08-28 16:33:23 +02:00
|
|
|
&progress, // progress
|
2010-08-23 21:13:27 +02:00
|
|
|
metadata_iface,
|
|
|
|
NULL, // data
|
|
|
|
&new_storage)) {
|
|
|
|
qWarning() << "Couldn't copy file to WMDM device";
|
|
|
|
metadata_iface->Release();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
metadata_iface->Release();
|
|
|
|
|
2010-08-28 16:33:23 +02:00
|
|
|
if (!new_storage)
|
|
|
|
return false;
|
|
|
|
|
2010-08-23 21:13:27 +02:00
|
|
|
// Get the metadata from the newly copied file
|
|
|
|
IWMDMStorage3* new_storage3 = NULL;
|
|
|
|
IWMDMMetaData* new_metadata = NULL;
|
|
|
|
|
|
|
|
new_storage->QueryInterface(IID_IWMDMStorage3, (void**)&new_storage3);
|
|
|
|
new_storage3->GetMetadata(&new_metadata);
|
|
|
|
|
|
|
|
new_storage->Release();
|
|
|
|
new_storage3->Release();
|
|
|
|
|
2010-08-28 16:33:23 +02:00
|
|
|
if (!new_metadata)
|
|
|
|
return false;
|
|
|
|
|
2010-08-23 21:13:27 +02:00
|
|
|
// Add it to our LibraryModel
|
|
|
|
Song new_song;
|
|
|
|
new_song.InitFromWmdm(new_metadata);
|
|
|
|
new_song.set_directory_id(1);
|
|
|
|
songs_to_add_ << new_song;
|
|
|
|
|
|
|
|
new_metadata->Release();
|
|
|
|
|
|
|
|
// Remove the original if requested
|
2010-08-28 23:55:30 +02:00
|
|
|
if (job.remove_original_) {
|
|
|
|
if (!QFile::remove(job.source_))
|
2010-08-23 21:13:27 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2010-08-22 21:18:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void WmdmDevice::FinishCopy(bool success) {
|
|
|
|
if (success) {
|
|
|
|
if (!songs_to_add_.isEmpty())
|
|
|
|
backend_->AddOrUpdateSongs(songs_to_add_);
|
|
|
|
if (!songs_to_remove_.isEmpty())
|
|
|
|
backend_->DeleteSongs(songs_to_remove_);
|
|
|
|
}
|
|
|
|
|
|
|
|
songs_to_add_.clear();
|
|
|
|
songs_to_remove_.clear();
|
|
|
|
|
2010-08-23 21:13:27 +02:00
|
|
|
storage_->Release();
|
|
|
|
storage_control_->Release();
|
|
|
|
thread_.reset();
|
|
|
|
|
2010-08-22 21:18:22 +02:00
|
|
|
db_busy_.unlock();
|
2010-08-30 14:28:03 +02:00
|
|
|
|
|
|
|
ConnectedDevice::FinishCopy(success);
|
2010-08-22 21:18:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void WmdmDevice::StartDelete() {
|
|
|
|
StartCopy();
|
|
|
|
}
|
|
|
|
|
2010-08-28 23:55:30 +02:00
|
|
|
bool WmdmDevice::DeleteFromStorage(const DeleteJob& job) {
|
2010-08-28 16:56:53 +02:00
|
|
|
// Walk down the tree until we've found the file
|
|
|
|
IWMDMStorage3* storage = storage_;
|
|
|
|
storage->AddRef();
|
2010-08-28 23:55:30 +02:00
|
|
|
foreach (const QString& path_component, job.metadata_.filename().split('/')) {
|
2010-08-28 16:56:53 +02:00
|
|
|
ScopedWCharArray path_component_wchar(path_component);
|
|
|
|
|
|
|
|
IWMDMStorage* next_storage = NULL;
|
|
|
|
if (storage->GetStorage(path_component_wchar, &next_storage)) {
|
|
|
|
// Couldn't find it
|
|
|
|
storage->Release();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
storage->Release();
|
|
|
|
|
|
|
|
next_storage->QueryInterface(IID_IWMDMStorage3, (void**)&storage);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get a control interface on it
|
|
|
|
IWMDMStorageControl3* control = NULL;
|
|
|
|
storage->QueryInterface(IID_IWMDMStorageControl3, (void**)&control);
|
|
|
|
storage->Release();
|
|
|
|
|
|
|
|
// Delete it
|
|
|
|
WmdmProgress progress;
|
|
|
|
if (control->Delete(WMDM_MODE_BLOCK, &progress)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove it from our library model
|
2010-08-28 23:55:30 +02:00
|
|
|
songs_to_remove_ << job.metadata_;
|
2010-08-28 16:56:53 +02:00
|
|
|
|
|
|
|
return true;
|
2010-08-22 21:18:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void WmdmDevice::FinishDelete(bool success) {
|
|
|
|
FinishCopy(success);
|
|
|
|
}
|
2010-08-29 02:34:35 +02:00
|
|
|
|
2010-08-30 14:22:15 +02:00
|
|
|
bool WmdmDevice::GetSupportedFiletypes(QList<Song::FileType>* ret, IWMDMDevice* device) {
|
2010-08-29 02:34:35 +02:00
|
|
|
// Get a list of supported formats
|
|
|
|
uint32_t format_count = 0;
|
|
|
|
uint32_t mime_count = 0;
|
|
|
|
_WAVEFORMATEX* formats;
|
|
|
|
wchar_t** mime_types;
|
|
|
|
|
|
|
|
if (device->GetFormatSupport(
|
|
|
|
&formats, &format_count, &mime_types, &mime_count)) {
|
|
|
|
qWarning() << "Unable to get a list of supported formats for device" << canonical_name;
|
2010-08-30 14:22:15 +02:00
|
|
|
return false;
|
2010-08-29 02:34:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Find known mime types
|
|
|
|
for (int i=0 ; i<mime_count ; ++i) {
|
|
|
|
QString type = QString::fromWCharArray(mime_types[i]);
|
|
|
|
if (type == "audio/mp3" || type == "audio/mpeg")
|
2010-08-30 14:22:15 +02:00
|
|
|
*ret << Song::Type_Mpeg;
|
2010-08-29 02:34:35 +02:00
|
|
|
else if (type == "audio/x-ms-wma")
|
2010-08-30 14:22:15 +02:00
|
|
|
*ret << Song::Type_Asf;
|
2010-08-29 02:34:35 +02:00
|
|
|
else if (type == "audio/wav")
|
2010-08-30 14:22:15 +02:00
|
|
|
*ret << Song::Type_Wav;
|
2010-08-29 02:34:35 +02:00
|
|
|
else if (type == "audio/mp4")
|
2010-08-30 14:22:15 +02:00
|
|
|
*ret << Song::Type_Mp4;
|
2010-08-29 02:34:35 +02:00
|
|
|
else if (type == "audio/ogg" || type == "audio/vorbis")
|
2010-08-30 14:22:15 +02:00
|
|
|
*ret << Song::Type_OggVorbis;
|
2010-08-29 02:34:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CoTaskMemFree(formats);
|
|
|
|
CoTaskMemFree(mime_types);
|
|
|
|
|
2010-08-30 14:22:15 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool WmdmDevice::GetSupportedFiletypes(QList<Song::FileType>* ret) {
|
|
|
|
WmdmThread thread;
|
|
|
|
|
|
|
|
// Get the device
|
|
|
|
WmdmLister* wmdm_lister = static_cast<WmdmLister*>(lister());
|
|
|
|
QString canonical_name = wmdm_lister->DeviceCanonicalName(unique_id());
|
|
|
|
IWMDMDevice* device = thread.GetDeviceByCanonicalName(canonical_name);
|
|
|
|
|
|
|
|
bool success = GetSupportedFiletypes(ret, device);
|
|
|
|
device->Release();
|
|
|
|
|
|
|
|
return success;
|
2010-08-29 02:34:35 +02:00
|
|
|
}
|