Remove all the soruce for WMDM devices. Fixes issue 3748
This commit is contained in:
parent
f245f7ed82
commit
eb20fb5945
@ -83,7 +83,6 @@ pkg_check_modules(TAGLIB REQUIRED taglib>=1.6)
|
||||
|
||||
if (WIN32)
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_library(MSWMDM_LIBRARIES mswmdm)
|
||||
find_library(QTSPARKLE_LIBRARIES qtsparkle)
|
||||
endif (WIN32)
|
||||
|
||||
|
38
dist/windows/clementine.nsi.in
vendored
38
dist/windows/clementine.nsi.in
vendored
@ -9,9 +9,6 @@
|
||||
!define PRODUCT_UNINST_ROOT_KEY "HKLM"
|
||||
!define PRODUCT_INSTALL_DIR "$PROGRAMFILES\Clementine"
|
||||
|
||||
!define WMDM_DIST_URL "http://clementine-player.googlecode.com/files/wmdmdist.exe"
|
||||
!define WMF_DIST_URL "http://clementine-player.googlecode.com/files/wmfdist.exe"
|
||||
|
||||
; Set Application Capabilities info
|
||||
!define CAPABILITIES_NAME "Clementine"
|
||||
!define CAPABILITIES_LOCAL_NAME "Clementine"
|
||||
@ -911,41 +908,6 @@ Section "Uninstaller"
|
||||
WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
|
||||
SectionEnd
|
||||
|
||||
Section "Windows Media Device Manager"
|
||||
GetDllVersion "$SYSDIR\mswmdm.dll" $R8 $R9
|
||||
IntOp $R7 $R8 / 0x00010000
|
||||
IntCmp $R7 9 end downloadwmf end # Major version >= 9
|
||||
|
||||
downloadwmf:
|
||||
StrCpy $1 "wmfdist.exe"
|
||||
NSISdl::download ${WMF_DIST_URL} "$TEMP\$1"
|
||||
Pop $R0
|
||||
StrCmp $R0 "success" downloadwmdm
|
||||
MessageBox MB_OK|MB_ICONSTOP "Error while downloading $1."
|
||||
Quit
|
||||
|
||||
downloadwmdm:
|
||||
StrCpy $1 "wmdmdist.exe"
|
||||
NSISdl::download ${WMDM_DIST_URL} "$TEMP\$1"
|
||||
Pop $R0
|
||||
StrCmp $R0 "success" install
|
||||
MessageBox MB_OK|MB_ICONSTOP "Error while downloading $1."
|
||||
Quit
|
||||
|
||||
install:
|
||||
DetailPrint "Installing the Windows Media Format 9 redistributable"
|
||||
ExecWait "$TEMP\wmfdist.exe /Q"
|
||||
|
||||
DetailPrint "Installing the Windows Media Device Manager 9 redistributable"
|
||||
ExecWait "$TEMP\wmdmdist.exe /Q"
|
||||
|
||||
Delete "$TEMP\wmfdist.exe"
|
||||
Delete "$TEMP\wmdmdist.exe"
|
||||
|
||||
end:
|
||||
DetailPrint "WMDM redistributable installed"
|
||||
SectionEnd
|
||||
|
||||
Section "Uninstall"
|
||||
; Kill clementine.exe if it's running
|
||||
; This calling convention is retarded...
|
||||
|
@ -1045,20 +1045,6 @@ optional_source(HAVE_LIBMTP
|
||||
devices/mtploader.h
|
||||
)
|
||||
|
||||
# Windows media lister
|
||||
optional_source(WIN32
|
||||
SOURCES
|
||||
devices/wmdmdevice.cpp
|
||||
devices/wmdmlister.cpp
|
||||
devices/wmdmloader.cpp
|
||||
devices/wmdmprogress.cpp
|
||||
devices/wmdmthread.cpp
|
||||
HEADERS
|
||||
devices/wmdmdevice.h
|
||||
devices/wmdmlister.h
|
||||
devices/wmdmloader.h
|
||||
)
|
||||
|
||||
# Moodbar support
|
||||
optional_source(HAVE_MOODBAR
|
||||
SOURCES
|
||||
@ -1297,7 +1283,6 @@ endif(HAVE_STATIC_SQLITE)
|
||||
if (WIN32)
|
||||
target_link_libraries(clementine_lib
|
||||
${ZLIB_LIBRARIES}
|
||||
${MSWMDM_LIBRARIES}
|
||||
${QTSPARKLE_LIBRARIES}
|
||||
tinysvcmdns
|
||||
qtwin
|
||||
|
@ -40,11 +40,6 @@
|
||||
|
||||
#include <id3v1genres.h>
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
# include <mswmdm.h>
|
||||
# include <QUuid>
|
||||
#endif // Q_OS_WIN32
|
||||
|
||||
#ifdef HAVE_LIBGPOD
|
||||
# include <gpod/itdb.h>
|
||||
#endif
|
||||
@ -726,233 +721,6 @@ void Song::InitFromLastFM(const lastfm::Track& track) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_WIN32)
|
||||
static void AddWmdmItem(IWMDMMetaData* metadata, const wchar_t* name,
|
||||
const QVariant& value) {
|
||||
switch (value.type()) {
|
||||
case QVariant::Int:
|
||||
case QVariant::UInt: {
|
||||
DWORD data = value.toUInt();
|
||||
metadata->AddItem(WMDM_TYPE_DWORD, name, (BYTE*)&data, sizeof(data));
|
||||
break;
|
||||
}
|
||||
case QVariant::String: {
|
||||
ScopedWCharArray data(value.toString());
|
||||
metadata->AddItem(WMDM_TYPE_STRING, name, (BYTE*)data.get(), data.bytes());
|
||||
break;
|
||||
}
|
||||
case QVariant::ByteArray: {
|
||||
QByteArray data = value.toByteArray();
|
||||
metadata->AddItem(WMDM_TYPE_BINARY, name, (BYTE*)data.constData(), data.size());
|
||||
break;
|
||||
}
|
||||
case QVariant::Bool: {
|
||||
int data = value.toBool();
|
||||
metadata->AddItem(WMDM_TYPE_BOOL, name, (BYTE*)&data, sizeof(data));
|
||||
break;
|
||||
}
|
||||
case QVariant::LongLong:
|
||||
case QVariant::ULongLong: {
|
||||
quint64 data = value.toULongLong();
|
||||
metadata->AddItem(WMDM_TYPE_QWORD, name, (BYTE*)&data, sizeof(data));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
qLog(Warning) << "Type" << value.type() << "not handled";
|
||||
Q_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static QVariant ReadWmdmValue(int type, uchar* data, uint length) {
|
||||
switch (type) {
|
||||
case WMDM_TYPE_DWORD:
|
||||
return QVariant::fromValue(uint(*reinterpret_cast<DWORD*>(data)));
|
||||
case WMDM_TYPE_WORD:
|
||||
return QVariant::fromValue(uint(*reinterpret_cast<WORD*>(data)));
|
||||
case WMDM_TYPE_QWORD:
|
||||
return QVariant::fromValue(qulonglong(*reinterpret_cast<quint64*>(data)));
|
||||
case WMDM_TYPE_STRING:
|
||||
return QString::fromWCharArray(reinterpret_cast<wchar_t*>(data), length/2);
|
||||
case WMDM_TYPE_BINARY:
|
||||
return QByteArray(reinterpret_cast<char*>(data), length);
|
||||
case WMDM_TYPE_BOOL:
|
||||
return bool(*reinterpret_cast<int*>(data));
|
||||
case WMDM_TYPE_GUID:
|
||||
return QUuid(*reinterpret_cast<GUID*>(data)).toString();
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void Song::InitFromWmdm(IWMDMMetaData* metadata) {
|
||||
bool non_consumable = false;
|
||||
int format = 0;
|
||||
|
||||
// How much metadata is there?
|
||||
uint count = 0;
|
||||
metadata->GetItemCount(&count);
|
||||
|
||||
for (int i=0 ; i<count ; ++i) {
|
||||
// Get this metadata item
|
||||
wchar_t* name = NULL;
|
||||
WMDM_TAG_DATATYPE type;
|
||||
BYTE* value = NULL;
|
||||
uint length = 0;
|
||||
|
||||
metadata->QueryByIndex(i, &name, &type, &value, &length);
|
||||
|
||||
QVariant item_value = ReadWmdmValue(type, value, length);
|
||||
|
||||
// Store it in the song if it's something we recognise
|
||||
if (wcscmp(name, g_wszWMDMTitle) == 0)
|
||||
d->title_ = item_value.toString();
|
||||
|
||||
else if (wcscmp(name, g_wszWMDMAuthor) == 0)
|
||||
d->artist_ = item_value.toString();
|
||||
|
||||
else if (wcscmp(name, g_wszWMDMDescription) == 0)
|
||||
d->comment_ = item_value.toString();
|
||||
|
||||
else if (wcscmp(name, g_wszWMDMAlbumTitle) == 0)
|
||||
d->album_ = item_value.toString();
|
||||
|
||||
else if (wcscmp(name, g_wszWMDMTrack) == 0)
|
||||
d->track_ = item_value.toInt();
|
||||
|
||||
else if (wcscmp(name, g_wszWMDMGenre) == 0)
|
||||
d->genre_ = item_value.toString();
|
||||
|
||||
else if (wcscmp(name, g_wszWMDMYear) == 0)
|
||||
d->year_ = item_value.toInt();
|
||||
|
||||
else if (wcscmp(name, g_wszWMDMComposer) == 0)
|
||||
d->composer_ = item_value.toString();
|
||||
|
||||
else if (wcscmp(name, g_wszWMDMBitrate) == 0)
|
||||
d->bitrate_ = item_value.toInt();
|
||||
|
||||
else if (wcscmp(name, g_wszWMDMFileName) == 0)
|
||||
d->url_ = QUrl::fromLocalFile(item_value.toString());
|
||||
|
||||
else if (wcscmp(name, g_wszWMDMDuration) == 0)
|
||||
set_length_nanosec(item_value.toULongLong() * 1e2);
|
||||
|
||||
else if (wcscmp(name, L"WMDM/FileSize") == 0)
|
||||
d->filesize_ = item_value.toULongLong();
|
||||
|
||||
else if (wcscmp(name, L"WMDM/NonConsumable") == 0)
|
||||
non_consumable = item_value.toBool();
|
||||
|
||||
else if (wcscmp(name, L"WMDM/FormatCode") == 0)
|
||||
format = item_value.toInt();
|
||||
|
||||
CoTaskMemFree(name);
|
||||
CoTaskMemFree(value);
|
||||
}
|
||||
|
||||
// Decide if this is music or not
|
||||
if (count == 0 || non_consumable)
|
||||
return;
|
||||
|
||||
switch (format) {
|
||||
case WMDM_FORMATCODE_AIFF:
|
||||
d->filetype_ = Song::Type_Aiff;
|
||||
break;
|
||||
|
||||
case WMDM_FORMATCODE_WAVE:
|
||||
d->filetype_ = Song::Type_Wav;
|
||||
break;
|
||||
|
||||
case WMDM_FORMATCODE_MP2:
|
||||
case WMDM_FORMATCODE_MP3:
|
||||
case WMDM_FORMATCODE_MPEG:
|
||||
d->filetype_ = Song::Type_Mpeg;
|
||||
break;
|
||||
|
||||
case WMDM_FORMATCODE_WMA:
|
||||
case WMDM_FORMATCODE_ASF:
|
||||
d->filetype_ = Song::Type_Asf;
|
||||
break;
|
||||
|
||||
case WMDM_FORMATCODE_OGG:
|
||||
d->filetype_ = Song::Type_OggVorbis;
|
||||
break;
|
||||
|
||||
case WMDM_FORMATCODE_AAC:
|
||||
case WMDM_FORMATCODE_MP4:
|
||||
d->filetype_ = Song::Type_Mp4;
|
||||
break;
|
||||
|
||||
case WMDM_FORMATCODE_FLAC:
|
||||
d->filetype_ = Song::Type_Flac;
|
||||
break;
|
||||
|
||||
case WMDM_FORMATCODE_AUDIBLE:
|
||||
case WMDM_FORMATCODE_UNDEFINEDAUDIO:
|
||||
d->filetype_ = Song::Type_Unknown;
|
||||
break;
|
||||
|
||||
case WMDM_FORMATCODE_UNDEFINED:
|
||||
// WMDM doesn't know what type of file it is, so we start guessing - first
|
||||
// check if any of the music metadata fields were defined. If they were,
|
||||
// there's a fairly good chance the file was music.
|
||||
if (!d->title_.isEmpty() || !d->artist_.isEmpty() ||
|
||||
!d->album_.isEmpty() || !d->comment_.isEmpty() ||
|
||||
!d->genre_.isEmpty() || d->track_ != -1 || d->year_ != -1 ||
|
||||
length_nanosec() != -1) {
|
||||
d->filetype_ = Song::Type_Unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
// Make a final guess based on the file extension
|
||||
{
|
||||
QString ext = d->url_.path().section('.', -1, -1).toLower();
|
||||
if (ext == "mp3" || ext == "wma" || ext == "flac" || ext == "ogg" ||
|
||||
ext == "spx" || ext == "mp4" || ext == "aac" || ext == "m4a")
|
||||
break;
|
||||
}
|
||||
return;
|
||||
|
||||
default:
|
||||
return; // It's not music
|
||||
}
|
||||
|
||||
d->valid_ = true;
|
||||
d->mtime_ = 0;
|
||||
d->ctime_ = 0;
|
||||
}
|
||||
|
||||
void Song::ToWmdm(IWMDMMetaData* metadata) const {
|
||||
AddWmdmItem(metadata, g_wszWMDMTitle, d->title_);
|
||||
AddWmdmItem(metadata, g_wszWMDMAuthor, d->artist_);
|
||||
AddWmdmItem(metadata, g_wszWMDMDescription, d->comment_);
|
||||
AddWmdmItem(metadata, g_wszWMDMAlbumTitle, d->album_);
|
||||
AddWmdmItem(metadata, g_wszWMDMTrack, d->track_);
|
||||
AddWmdmItem(metadata, g_wszWMDMGenre, d->genre_);
|
||||
AddWmdmItem(metadata, g_wszWMDMYear, QString::number(d->year_));
|
||||
AddWmdmItem(metadata, g_wszWMDMComposer, d->composer_);
|
||||
AddWmdmItem(metadata, g_wszWMDMBitrate, d->bitrate_);
|
||||
AddWmdmItem(metadata, g_wszWMDMFileName, d->basefilename_);
|
||||
AddWmdmItem(metadata, g_wszWMDMDuration, qint64(length_nanosec() / 1e2));
|
||||
AddWmdmItem(metadata, L"WMDM/FileSize", d->filesize_);
|
||||
|
||||
WMDM_FORMATCODE format;
|
||||
switch (d->filetype_) {
|
||||
case Type_Aiff: format = WMDM_FORMATCODE_AIFF; break;
|
||||
case Type_Wav: format = WMDM_FORMATCODE_WAVE; break;
|
||||
case Type_Mpeg: format = WMDM_FORMATCODE_MP3; break;
|
||||
case Type_Asf: format = WMDM_FORMATCODE_ASF; break;
|
||||
case Type_OggFlac:
|
||||
case Type_OggSpeex:
|
||||
case Type_OggVorbis: format = WMDM_FORMATCODE_OGG; break;
|
||||
case Type_Mp4: format = WMDM_FORMATCODE_MP4; break;
|
||||
case Type_Flac: format = WMDM_FORMATCODE_FLAC; break;
|
||||
default: format = WMDM_FORMATCODE_UNDEFINEDAUDIO; break;
|
||||
}
|
||||
AddWmdmItem(metadata, L"WMDM/FormatCode", format);
|
||||
}
|
||||
#endif // Q_OS_WIN32
|
||||
|
||||
void Song::MergeFromSimpleMetaBundle(const Engine::SimpleMetaBundle &bundle) {
|
||||
if (d->init_from_file_ || d->url_.scheme() == "file") {
|
||||
// This Song was already loaded using taglib. Our tags are probably better
|
||||
|
@ -44,10 +44,6 @@ class QUrl;
|
||||
struct LIBMTP_track_struct;
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_WIN32)
|
||||
struct IWMDMMetaData;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBLASTFM
|
||||
namespace lastfm {
|
||||
class Track;
|
||||
@ -126,11 +122,6 @@ class Song {
|
||||
void ToMTP(LIBMTP_track_struct* track) const;
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_WIN32)
|
||||
void InitFromWmdm(IWMDMMetaData* metadata);
|
||||
void ToWmdm(IWMDMMetaData* metadata) const;
|
||||
#endif
|
||||
|
||||
// Copies important statistics from the other song to this one, overwriting
|
||||
// any data that already exists. Useful when you want updated tags from disk
|
||||
// but you want to keep user stats.
|
||||
|
@ -50,10 +50,6 @@
|
||||
#ifdef Q_OS_DARWIN
|
||||
# include "macdevicelister.h"
|
||||
#endif
|
||||
#ifdef Q_OS_WIN32
|
||||
# include "wmdmlister.h"
|
||||
# include "wmdmdevice.h"
|
||||
#endif
|
||||
#ifdef HAVE_LIBGPOD
|
||||
# include "gpoddevice.h"
|
||||
#endif
|
||||
@ -202,10 +198,6 @@ DeviceManager::DeviceManager(Application* app, QObject *parent)
|
||||
#ifdef Q_OS_DARWIN
|
||||
AddLister(new MacDeviceLister);
|
||||
#endif
|
||||
#if defined(Q_OS_WIN32)
|
||||
AddLister(new WmdmLister);
|
||||
AddDeviceClass<WmdmDevice>();
|
||||
#endif
|
||||
|
||||
AddDeviceClass<FilesystemDevice>();
|
||||
|
||||
|
@ -1,292 +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 "devicemanager.h"
|
||||
#include "wmdmdevice.h"
|
||||
#include "wmdmlister.h"
|
||||
#include "wmdmloader.h"
|
||||
#include "wmdmprogress.h"
|
||||
#include "wmdmthread.h"
|
||||
#include "core/application.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/utilities.h"
|
||||
#include "library/librarybackend.h"
|
||||
#include "library/librarymodel.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QThread>
|
||||
|
||||
#include <boost/scoped_array.hpp>
|
||||
|
||||
#include <mswmdm.h>
|
||||
|
||||
WmdmDevice::WmdmDevice(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_(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
WmdmDevice::~WmdmDevice() {
|
||||
}
|
||||
|
||||
void WmdmDevice::Init() {
|
||||
InitBackendDirectory("/", first_time_, false);
|
||||
model_->Init();
|
||||
|
||||
loader_ = new WmdmLoader(app_->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();
|
||||
|
||||
db_busy_.lock();
|
||||
}
|
||||
|
||||
void WmdmDevice::LoadFinished() {
|
||||
loader_->deleteLater();
|
||||
loader_ = NULL;
|
||||
|
||||
db_busy_.unlock();
|
||||
}
|
||||
|
||||
bool WmdmDevice::StartCopy(QList<Song::FileType>* supported_types) {
|
||||
// Ensure only one "organise files" can be active at any one time
|
||||
db_busy_.lock();
|
||||
|
||||
// This initialises COM and gets a connection to the device
|
||||
thread_.reset(new WmdmThread);
|
||||
if (!thread_->manager())
|
||||
return false;
|
||||
|
||||
// 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();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
bool WmdmDevice::CopyToStorage(const CopyJob& job) {
|
||||
if (!storage_control_ || !storage_)
|
||||
return false;
|
||||
|
||||
// Create the song metadata
|
||||
IWMDMMetaData* metadata_iface = NULL;
|
||||
storage_->CreateEmptyMetadataObject(&metadata_iface);
|
||||
job.metadata_.ToWmdm(metadata_iface);
|
||||
|
||||
// Convert the filenames to wchars
|
||||
ScopedWCharArray source_filename(QDir::toNativeSeparators(job.source_));
|
||||
ScopedWCharArray dest_filename(job.metadata_.basefilename());
|
||||
|
||||
// Create the progress object
|
||||
WmdmProgress progress(job.progress_);
|
||||
|
||||
// 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
|
||||
&progress, // progress
|
||||
metadata_iface,
|
||||
NULL, // data
|
||||
&new_storage)) {
|
||||
qLog(Warning) << "Couldn't copy file to WMDM device";
|
||||
metadata_iface->Release();
|
||||
return false;
|
||||
}
|
||||
metadata_iface->Release();
|
||||
|
||||
if (!new_storage)
|
||||
return false;
|
||||
|
||||
// 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();
|
||||
|
||||
if (!new_metadata)
|
||||
return false;
|
||||
|
||||
// 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
|
||||
if (job.remove_original_) {
|
||||
if (!QFile::remove(job.source_))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
storage_->Release();
|
||||
storage_control_->Release();
|
||||
thread_.reset();
|
||||
|
||||
db_busy_.unlock();
|
||||
|
||||
ConnectedDevice::FinishCopy(success);
|
||||
}
|
||||
|
||||
void WmdmDevice::StartDelete() {
|
||||
StartCopy(NULL);
|
||||
}
|
||||
|
||||
bool WmdmDevice::DeleteFromStorage(const DeleteJob& job) {
|
||||
// Walk down the tree until we've found the file
|
||||
IWMDMStorage3* storage = storage_;
|
||||
storage->AddRef();
|
||||
|
||||
const QStringList path_components =
|
||||
job.metadata_.url().path().split('/', QString::SkipEmptyParts);
|
||||
foreach (const QString& path_component, path_components) {
|
||||
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
|
||||
songs_to_remove_ << job.metadata_;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WmdmDevice::FinishDelete(bool success) {
|
||||
FinishCopy(success);
|
||||
}
|
||||
|
||||
bool WmdmDevice::GetSupportedFiletypes(QList<Song::FileType>* ret, IWMDMDevice* device) {
|
||||
// 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)) {
|
||||
qLog(Warning) << "Unable to get a list of supported formats for device";
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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")
|
||||
*ret << Song::Type_Mpeg;
|
||||
else if (type == "audio/x-ms-wma")
|
||||
*ret << Song::Type_Asf;
|
||||
else if (type == "audio/wav")
|
||||
*ret << Song::Type_Wav;
|
||||
else if (type == "audio/mp4")
|
||||
*ret << Song::Type_Mp4;
|
||||
else if (type == "audio/ogg" || type == "audio/vorbis")
|
||||
*ret << Song::Type_OggVorbis;
|
||||
}
|
||||
|
||||
CoTaskMemFree(formats);
|
||||
CoTaskMemFree(mime_types);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WmdmDevice::GetSupportedFiletypes(QList<Song::FileType>* ret) {
|
||||
QMutexLocker l(&db_busy_);
|
||||
|
||||
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;
|
||||
}
|
@ -1,75 +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 WMDMDEVICE_H
|
||||
#define WMDMDEVICE_H
|
||||
|
||||
#include "connecteddevice.h"
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
class WmdmLoader;
|
||||
class WmdmThread;
|
||||
|
||||
struct IWMDMDevice;
|
||||
struct IWMDMStorage3;
|
||||
struct IWMDMStorageControl3;
|
||||
|
||||
class WmdmDevice : public ConnectedDevice {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Q_INVOKABLE WmdmDevice(const QUrl& url, DeviceLister* lister,
|
||||
const QString& unique_id, DeviceManager* manager,
|
||||
Application* app,
|
||||
int database_id, bool first_time);
|
||||
~WmdmDevice();
|
||||
|
||||
static QStringList url_schemes() { return QStringList() << "wmdm"; }
|
||||
|
||||
void Init();
|
||||
|
||||
bool GetSupportedFiletypes(QList<Song::FileType>* ret);
|
||||
|
||||
bool StartCopy(QList<Song::FileType>* supported_types);
|
||||
bool CopyToStorage(const CopyJob& job);
|
||||
void FinishCopy(bool success);
|
||||
|
||||
void StartDelete();
|
||||
bool DeleteFromStorage(const DeleteJob& job);
|
||||
void FinishDelete(bool success);
|
||||
|
||||
private slots:
|
||||
void LoadFinished();
|
||||
|
||||
private:
|
||||
bool GetSupportedFiletypes(QList<Song::FileType>* ret, IWMDMDevice* device);
|
||||
|
||||
private:
|
||||
QThread* loader_thread_;
|
||||
WmdmLoader* loader_;
|
||||
|
||||
QMutex db_busy_;
|
||||
SongList songs_to_add_;
|
||||
SongList songs_to_remove_;
|
||||
|
||||
boost::scoped_ptr<WmdmThread> thread_;
|
||||
IWMDMStorage3* storage_;
|
||||
IWMDMStorageControl3* storage_control_;
|
||||
};
|
||||
|
||||
#endif // WMDMDEVICE_H
|
@ -1,528 +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/>.
|
||||
*/
|
||||
|
||||
#define _WIN32_WINNT 0x0501
|
||||
|
||||
#include "wmdmlister.h"
|
||||
#include "wmdmthread.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/utilities.h"
|
||||
|
||||
#include <objbase.h>
|
||||
#include <mswmdm_i.c>
|
||||
#include <winbase.h>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/scoped_array.hpp>
|
||||
|
||||
#include <QDir>
|
||||
#include <QPixmap>
|
||||
#include <QStringList>
|
||||
#include <QtDebug>
|
||||
|
||||
const QUuid WmdmLister::kDeviceProtocolMsc(
|
||||
0xa4d2c26c, 0xa881, 0x44bb, 0xbd, 0x5d, 0x1f, 0x70, 0x3c, 0x71, 0xf7, 0xa9);
|
||||
|
||||
QString WmdmLister::CanonicalNameToId(const QString& canonical_name) {
|
||||
return "wmdm/" + canonical_name;
|
||||
}
|
||||
|
||||
QString WmdmLister::DeviceInfo::unique_id() const {
|
||||
return WmdmLister::CanonicalNameToId(canonical_name_);
|
||||
}
|
||||
|
||||
WmdmLister::WmdmLister()
|
||||
: notification_cookie_(0)
|
||||
{
|
||||
}
|
||||
|
||||
WmdmLister::~WmdmLister() {
|
||||
Q_ASSERT(!thread_);
|
||||
}
|
||||
|
||||
void WmdmLister::Init() {
|
||||
qLog(Debug) << "Starting";
|
||||
|
||||
thread_.reset(new WmdmThread);
|
||||
if (!thread_->manager())
|
||||
return;
|
||||
|
||||
// Register for notifications
|
||||
qLog(Debug) << "Obtaining CP container";
|
||||
IConnectionPointContainer* cp_container = NULL;
|
||||
thread_->manager()->QueryInterface(IID_IConnectionPointContainer, (void**)&cp_container);
|
||||
|
||||
qLog(Debug) << "Obtaining CP";
|
||||
IConnectionPoint* cp = NULL;
|
||||
cp_container->FindConnectionPoint(IID_IWMDMNotification, &cp);
|
||||
|
||||
qLog(Debug) << "Registering for notifications";
|
||||
cp->Advise(this, ¬ification_cookie_);
|
||||
|
||||
cp->Release();
|
||||
cp_container->Release();
|
||||
|
||||
// Fetch the initial list of devices
|
||||
qLog(Debug) << "Fetching device list";
|
||||
IWMDMEnumDevice* device_it = NULL;
|
||||
if (thread_->manager()->EnumDevices2(&device_it)) {
|
||||
qLog(Warning) << "Error querying WMDM devices";
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate through the devices
|
||||
QMap<QString, DeviceInfo> devices;
|
||||
forever {
|
||||
IWMDMDevice* device = NULL;
|
||||
IWMDMDevice2* device2 = NULL;
|
||||
ULONG fetched = 0;
|
||||
if (device_it->Next(1, &device, &fetched) || fetched != 1)
|
||||
break;
|
||||
|
||||
qLog(Debug) << "Querying device";
|
||||
if (device->QueryInterface(IID_IWMDMDevice2, (void**)&device2)) {
|
||||
qLog(Warning) << "Error getting IWMDMDevice2 from device";
|
||||
device->Release();
|
||||
continue;
|
||||
}
|
||||
device->Release();
|
||||
|
||||
DeviceInfo info = ReadDeviceInfo(device2);
|
||||
if (info.is_suitable_)
|
||||
devices[info.unique_id()] = info;
|
||||
else
|
||||
device2->Release();
|
||||
}
|
||||
device_it->Release();
|
||||
|
||||
// Update the internal cache
|
||||
qLog(Debug) << "Updating device cache";
|
||||
{
|
||||
QMutexLocker l(&mutex_);
|
||||
devices_ = devices;
|
||||
}
|
||||
|
||||
// Notify about the changes
|
||||
foreach (const QString& id, devices.keys()) {
|
||||
emit DeviceAdded(id);
|
||||
}
|
||||
|
||||
qLog(Debug) << "Startup complete";
|
||||
}
|
||||
|
||||
void WmdmLister::ReallyShutdown() {
|
||||
// Unregister for notifications
|
||||
IConnectionPointContainer* cp_container;
|
||||
thread_->manager()->QueryInterface(IID_IConnectionPointContainer, (void**)&cp_container);
|
||||
|
||||
IConnectionPoint* cp;
|
||||
cp_container->FindConnectionPoint(IID_IWMDMNotification, &cp);
|
||||
|
||||
cp->Release();
|
||||
cp_container->Release();
|
||||
|
||||
thread_.reset();
|
||||
}
|
||||
|
||||
void WmdmLister::ShutDown() {
|
||||
// COM shutdown must be done in the original thread.
|
||||
metaObject()->invokeMethod(this, "ReallyShutdown", Qt::BlockingQueuedConnection);
|
||||
}
|
||||
|
||||
WmdmLister::DeviceInfo WmdmLister::ReadDeviceInfo(IWMDMDevice2* device) {
|
||||
qLog(Debug) << "Reading device info";
|
||||
|
||||
DeviceInfo ret;
|
||||
ret.device_ = device;
|
||||
|
||||
// Get text strings
|
||||
const int max_size = 512;
|
||||
wchar_t buf[max_size];
|
||||
device->GetName(buf, max_size);
|
||||
ret.name_ = QString::fromWCharArray(buf).trimmed();
|
||||
|
||||
device->GetManufacturer(buf, max_size);
|
||||
ret.manufacturer_ = QString::fromWCharArray(buf).trimmed();
|
||||
|
||||
device->GetCanonicalName(buf, max_size);
|
||||
ret.canonical_name_ = QString::fromWCharArray(buf).toLower();
|
||||
|
||||
qLog(Debug) << "Read device strings:" << ret.name_ << ret.manufacturer_ << ret.canonical_name_;
|
||||
|
||||
// Upgrade to a device3
|
||||
IWMDMDevice3* device3 = NULL;
|
||||
device->QueryInterface(IID_IWMDMDevice3, (void**)&device3);
|
||||
|
||||
// Get the device protocol so we can figure out whether the device is MSC
|
||||
PROPVARIANT protocol;
|
||||
if (device3) {
|
||||
device3->GetProperty(g_wszWMDMDeviceProtocol, &protocol);
|
||||
device3->Release();
|
||||
}
|
||||
|
||||
// Get the type and check whether it has storage
|
||||
DWORD type = 0;
|
||||
device->GetType(&type);
|
||||
if (type & WMDM_DEVICE_TYPE_STORAGE)
|
||||
ret.is_suitable_ = true;
|
||||
|
||||
// Get the icon
|
||||
HICON icon;
|
||||
if (device->GetDeviceIcon((ULONG*)&icon) == S_OK) {
|
||||
// Extra check for whether the icon is valid (see issue 1417)
|
||||
|
||||
ICONINFO iconinfo;
|
||||
if (GetIconInfo(icon, &iconinfo)) {
|
||||
ret.icon_ = QPixmap::fromWinHICON(icon);
|
||||
DestroyIcon(icon);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the main (first) storage for the device
|
||||
IWMDMEnumStorage* storage_it = NULL;
|
||||
if (device->EnumStorage(&storage_it) == S_OK && storage_it) {
|
||||
ULONG storage_fetched = 0;
|
||||
IWMDMStorage* storage;
|
||||
|
||||
if (storage_it->Next(1, &storage, &storage_fetched) == S_OK) {
|
||||
if (storage->QueryInterface(IID_IWMDMStorage2, (void**)&ret.storage_)) {
|
||||
qLog(Warning) << "Error getting IWMDMStorage2 from storage";
|
||||
} else {
|
||||
// Get free space information
|
||||
UpdateFreeSpace(&ret);
|
||||
}
|
||||
storage->Release();
|
||||
}
|
||||
storage_it->Release();
|
||||
}
|
||||
|
||||
// There doesn't seem to be a way to get the drive letter of MSC devices, so
|
||||
// try parsing the device's name to extract it.
|
||||
if (!device3 || QUuid(*protocol.puuid) == kDeviceProtocolMsc)
|
||||
GuessDriveLetter(&ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void WmdmLister::GuessDriveLetter(DeviceInfo* info) {
|
||||
qLog(Debug) << "Guessing drive letter for" << info->name_;
|
||||
|
||||
// Windows XP puts the drive letter in brackets at the end of the name
|
||||
QRegExp drive_letter("\\(([A-Z]:)\\)$");
|
||||
if (drive_letter.indexIn(info->name_) != -1) {
|
||||
qLog(Debug) << "Looks like an XP drive" << drive_letter.cap(1);
|
||||
CheckDriveLetter(info, drive_letter.cap(1));
|
||||
return;
|
||||
}
|
||||
|
||||
// Windows 7 sometimes has the drive letter as the whole name
|
||||
drive_letter = QRegExp("^([A-Z]:)\\\\$");
|
||||
if (drive_letter.indexIn(info->name_) != -1) {
|
||||
qLog(Debug) << "Looks like a win7 drive" << drive_letter.cap(1);
|
||||
CheckDriveLetter(info, drive_letter.cap(1));
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise Windows 7 uses the drive's DOS label as its whole name.
|
||||
// Let's enumerate all the volumes on the system and find one with that
|
||||
// label, then get its drive letter. Yay!
|
||||
wchar_t volume_name[MAX_PATH + 1];
|
||||
HANDLE handle = FindFirstVolumeW(volume_name, MAX_PATH);
|
||||
|
||||
forever {
|
||||
// QueryDosDeviceW doesn't allow a trailing backslash, so remove it.
|
||||
int length = wcslen(volume_name);
|
||||
volume_name[length - 1] = L'\0';
|
||||
|
||||
wchar_t device_name[MAX_PATH + 1];
|
||||
QueryDosDeviceW(&volume_name[4], device_name, MAX_PATH);
|
||||
|
||||
volume_name[length - 1] = L'\\';
|
||||
|
||||
// Don't do cd-roms or floppies
|
||||
if (QString::fromWCharArray(device_name).contains("HarddiskVolume")) {
|
||||
wchar_t volume_path[MAX_PATH + 1];
|
||||
DWORD volume_path_length = MAX_PATH;
|
||||
GetVolumePathNamesForVolumeNameW(
|
||||
volume_name, volume_path, volume_path_length, &volume_path_length);
|
||||
|
||||
if (wcslen(volume_path) == 3) {
|
||||
ScopedWCharArray name(QString(MAX_PATH + 1, '\0'));
|
||||
ScopedWCharArray type(QString(MAX_PATH + 1, '\0'));
|
||||
DWORD serial = 0;
|
||||
|
||||
if (!GetVolumeInformationW(volume_path, name, MAX_PATH,
|
||||
&serial, NULL, NULL, type, MAX_PATH)) {
|
||||
qLog(Warning) << "Error getting volume information for" <<
|
||||
QString::fromWCharArray(volume_path);
|
||||
} else {
|
||||
if (name.ToString() == info->name_ && name.characters() != 0) {
|
||||
// We found it!
|
||||
qLog(Debug) << "Looks like a win7 drive name" << QString::fromWCharArray(volume_path);
|
||||
if (CheckDriveLetter(info, QString::fromWCharArray(volume_path))) {
|
||||
info->device_name_ = QString::fromWCharArray(device_name);
|
||||
info->volume_name_ = QString::fromWCharArray(volume_name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!FindNextVolumeW(handle, volume_name, MAX_PATH))
|
||||
break;
|
||||
}
|
||||
FindVolumeClose(handle);
|
||||
}
|
||||
|
||||
bool WmdmLister::CheckDriveLetter(DeviceInfo* info, const QString& drive) {
|
||||
// Sanity check to make sure there really is a drive there
|
||||
ScopedWCharArray path(drive.endsWith('\\') ? drive : (drive + "\\"));
|
||||
ScopedWCharArray name(QString(MAX_PATH + 1, '\0'));
|
||||
ScopedWCharArray type(QString(MAX_PATH + 1, '\0'));
|
||||
DWORD serial = 0;
|
||||
|
||||
if (!GetVolumeInformationW(
|
||||
path,
|
||||
name, MAX_PATH,
|
||||
&serial,
|
||||
NULL, // max component length
|
||||
NULL, // flags
|
||||
type, MAX_PATH // fat or ntfs
|
||||
)) {
|
||||
qLog(Warning) << "Error getting volume information for" << drive;
|
||||
return false;
|
||||
} else {
|
||||
qLog(Debug) << "Validated drive letter" << drive;
|
||||
info->mount_point_ = path.ToString();
|
||||
info->fs_name_ = name.ToString();
|
||||
info->fs_type_ = type.ToString();
|
||||
info->fs_serial_ = serial;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
QStringList WmdmLister::DeviceUniqueIDs() {
|
||||
QMutexLocker l(&mutex_);
|
||||
return devices_.keys();
|
||||
}
|
||||
|
||||
QVariantList WmdmLister::DeviceIcons(const QString& id) {
|
||||
QPixmap pixmap = LockAndGetDeviceInfo(id, &DeviceInfo::icon_);
|
||||
|
||||
if (pixmap.isNull())
|
||||
return QVariantList();
|
||||
return QVariantList() << pixmap;
|
||||
}
|
||||
|
||||
QString WmdmLister::DeviceManufacturer(const QString& id) {
|
||||
return LockAndGetDeviceInfo(id, &DeviceInfo::manufacturer_);
|
||||
}
|
||||
|
||||
QString WmdmLister::DeviceModel(const QString& id) {
|
||||
return LockAndGetDeviceInfo(id, &DeviceInfo::name_);
|
||||
}
|
||||
|
||||
quint64 WmdmLister::DeviceCapacity(const QString& id) {
|
||||
return LockAndGetDeviceInfo(id, &DeviceInfo::total_bytes_);
|
||||
}
|
||||
|
||||
quint64 WmdmLister::DeviceFreeSpace(const QString& id) {
|
||||
return LockAndGetDeviceInfo(id, &DeviceInfo::free_bytes_);
|
||||
}
|
||||
|
||||
QVariantMap WmdmLister::DeviceHardwareInfo(const QString& id) {
|
||||
QVariantMap ret;
|
||||
|
||||
QMutexLocker l(&mutex_);
|
||||
if (!devices_.contains(id))
|
||||
return ret;
|
||||
|
||||
const DeviceInfo& info = devices_[id];
|
||||
|
||||
ret[tr("Drive letter")] = QDir::toNativeSeparators(info.mount_point_);
|
||||
ret[tr("Filesystem type")] = info.fs_type_;
|
||||
ret[tr("Filesystem name")] = info.fs_name_;
|
||||
ret[tr("Device name")] = info.device_name_;
|
||||
ret[tr("Volume name")] = info.volume_name_;
|
||||
|
||||
if (info.fs_serial_ != 0)
|
||||
ret[tr("Filesystem serial number")] = info.fs_serial_;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString WmdmLister::MakeFriendlyName(const QString& id) {
|
||||
QMutexLocker l(&mutex_);
|
||||
if (!devices_.contains(id))
|
||||
return QString();
|
||||
|
||||
const DeviceInfo& info = devices_[id];
|
||||
if (info.manufacturer_.isEmpty() || info.manufacturer_ == "Unknown")
|
||||
return info.name_;
|
||||
|
||||
return info.manufacturer_ + " " + info.name_;
|
||||
}
|
||||
|
||||
QList<QUrl> WmdmLister::MakeDeviceUrls(const QString& id) {
|
||||
QList<QUrl> ret;
|
||||
|
||||
QString mount_point = LockAndGetDeviceInfo(id, &DeviceInfo::mount_point_);
|
||||
if (!mount_point.isEmpty()) {
|
||||
ret << MakeUrlFromLocalPath(mount_point);
|
||||
}
|
||||
|
||||
QUrl wmdm_url;
|
||||
wmdm_url.setScheme("wmdm");
|
||||
wmdm_url.setPath(id);
|
||||
ret << wmdm_url;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void WmdmLister::UnmountDevice(const QString& id) {
|
||||
}
|
||||
|
||||
void WmdmLister::UpdateDeviceFreeSpace(const QString& id) {
|
||||
// This needs to be done in the lister's thread where we already have COM
|
||||
// initialised
|
||||
metaObject()->invokeMethod(this, "DoUpdateDriveFreeSpace",
|
||||
Qt::BlockingQueuedConnection, Q_ARG(QString, id));
|
||||
}
|
||||
|
||||
void WmdmLister::DoUpdateDriveFreeSpace(const QString& id) {
|
||||
{
|
||||
QMutexLocker l(&mutex_);
|
||||
if (!devices_.contains(id))
|
||||
return;
|
||||
|
||||
UpdateFreeSpace(&devices_[id]);
|
||||
}
|
||||
|
||||
emit DeviceChanged(id);
|
||||
}
|
||||
|
||||
namespace {
|
||||
qint64 GetSpaceValue(
|
||||
IWMDMStorageGlobals* globals,
|
||||
LONG (IWMDMStorageGlobals::*f)(DWORD*,DWORD*)) {
|
||||
DWORD low, high;
|
||||
((globals)->*(f))(&low, &high);
|
||||
|
||||
return (qint64)high << 32 | (qint64)low;
|
||||
}
|
||||
}
|
||||
|
||||
void WmdmLister::UpdateFreeSpace(DeviceInfo* info) {
|
||||
IWMDMStorageGlobals* globals;
|
||||
info->storage_->GetStorageGlobals(&globals);
|
||||
|
||||
DWORD low, high;
|
||||
|
||||
globals->GetTotalSize(&low, &high);
|
||||
info->total_bytes_ = (qint64)high << 32 | (qint64)low;
|
||||
|
||||
globals->GetTotalFree(&low, &high);
|
||||
info->free_bytes_ = (qint64)high << 32 | (qint64)low;
|
||||
|
||||
globals->Release();
|
||||
}
|
||||
|
||||
HRESULT WmdmLister::WMDMMessage(DWORD message_type, LPCWSTR name) {
|
||||
qLog(Debug) << "WMDM message" << message_type << name;
|
||||
|
||||
QString canonical_name = QString::fromWCharArray(name).toLower();
|
||||
|
||||
switch (message_type) {
|
||||
case WMDM_MSG_DEVICE_ARRIVAL: WMDMDeviceAdded(canonical_name); break;
|
||||
case WMDM_MSG_DEVICE_REMOVAL: WMDMDeviceRemoved(canonical_name); break;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void WmdmLister::WMDMDeviceAdded(const QString& canonical_name) {
|
||||
ScopedWCharArray name(canonical_name);
|
||||
|
||||
IWMDMDevice* device = NULL;
|
||||
if (thread_->manager()->GetDeviceFromCanonicalName(name, &device)) {
|
||||
qLog(Warning) << "Error in GetDeviceFromCanonicalName for" << canonical_name;
|
||||
return;
|
||||
}
|
||||
|
||||
IWMDMDevice2* device2 = NULL;
|
||||
if (device->QueryInterface(IID_IWMDMDevice2, (void**) &device2)) {
|
||||
qLog(Warning) << "Error getting IWMDMDevice2 from device";
|
||||
device->Release();
|
||||
return;
|
||||
}
|
||||
device->Release();
|
||||
|
||||
DeviceInfo info = ReadDeviceInfo(device2);
|
||||
if (info.is_suitable_) {
|
||||
QString id = info.unique_id();
|
||||
{
|
||||
QMutexLocker l(&mutex_);
|
||||
devices_[id] = info;
|
||||
}
|
||||
emit DeviceAdded(id);
|
||||
} else {
|
||||
device2->Release();
|
||||
}
|
||||
}
|
||||
|
||||
void WmdmLister::WMDMDeviceRemoved(const QString& canonical_name) {
|
||||
QString id = CanonicalNameToId(canonical_name);
|
||||
{
|
||||
QMutexLocker l(&mutex_);
|
||||
if (!devices_.contains(id))
|
||||
return;
|
||||
|
||||
devices_[id].device_->Release();
|
||||
devices_[id].storage_->Release();
|
||||
|
||||
devices_.remove(id);
|
||||
}
|
||||
|
||||
emit DeviceRemoved(id);
|
||||
}
|
||||
|
||||
LONG WmdmLister::QueryInterface(REFIID riid, void** object) {
|
||||
*object = 0;
|
||||
|
||||
if (riid == IID_IUnknown)
|
||||
*object = (IUnknown*) this;
|
||||
else if (riid == IID_IWMDMNotification)
|
||||
*object = (IWMDMNotification*) this;
|
||||
else
|
||||
return E_NOINTERFACE;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
ULONG WmdmLister::AddRef() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ULONG WmdmLister::Release() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
QString WmdmLister::DeviceCanonicalName(const QString& id) {
|
||||
return LockAndGetDeviceInfo(id, &DeviceInfo::canonical_name_);
|
||||
}
|
||||
|
@ -1,141 +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 WMDMLISTER_H
|
||||
#define WMDMLISTER_H
|
||||
|
||||
#include "devicelister.h"
|
||||
|
||||
#include <QMap>
|
||||
#include <QMutex>
|
||||
#include <QPixmap>
|
||||
#include <QUuid>
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
|
||||
#include <mswmdm.h>
|
||||
#include <sac_shim.h>
|
||||
#undef LoadIcon
|
||||
|
||||
class WmdmThread;
|
||||
|
||||
class WmdmLister : public DeviceLister, public IWMDMNotification {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WmdmLister();
|
||||
~WmdmLister();
|
||||
|
||||
// DeviceLister
|
||||
virtual void Init();
|
||||
|
||||
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);
|
||||
|
||||
// IWMDMNotification
|
||||
// The __stdcall is *really* important
|
||||
virtual HRESULT __stdcall WMDMMessage(DWORD message_type, LPCWSTR name);
|
||||
virtual LONG __stdcall QueryInterface(const IID& riid, void** object);
|
||||
virtual ULONG __stdcall AddRef();
|
||||
virtual ULONG __stdcall Release();
|
||||
|
||||
// Called by WmdmLister
|
||||
QString DeviceCanonicalName(const QString& id);
|
||||
|
||||
public slots:
|
||||
virtual void UpdateDeviceFreeSpace(const QString& id);
|
||||
virtual void ShutDown();
|
||||
|
||||
private slots:
|
||||
virtual void DoUpdateDriveFreeSpace(const QString& id);
|
||||
virtual void ReallyShutdown();
|
||||
|
||||
private:
|
||||
struct DeviceInfo {
|
||||
DeviceInfo() : device_(NULL), storage_(NULL), is_suitable_(false),
|
||||
total_bytes_(0), free_bytes_(0) {}
|
||||
|
||||
QString unique_id() const;
|
||||
|
||||
IWMDMDevice2* device_;
|
||||
IWMDMStorage2* storage_;
|
||||
|
||||
bool is_suitable_;
|
||||
|
||||
QString name_;
|
||||
QString manufacturer_;
|
||||
QString canonical_name_;
|
||||
|
||||
QPixmap icon_;
|
||||
|
||||
quint64 total_bytes_;
|
||||
quint64 free_bytes_;
|
||||
|
||||
// Only valid for filesystem devices
|
||||
QString mount_point_;
|
||||
QString fs_name_;
|
||||
QString fs_type_;
|
||||
int fs_serial_;
|
||||
|
||||
// Information we get by querying win7-style FS devices
|
||||
QString device_name_;
|
||||
QString volume_name_;
|
||||
};
|
||||
|
||||
static const QUuid kDeviceProtocolMsc;
|
||||
|
||||
DeviceInfo ReadDeviceInfo(IWMDMDevice2* device);
|
||||
|
||||
template <typename T>
|
||||
T LockAndGetDeviceInfo(const QString& id, T DeviceInfo::*field);
|
||||
|
||||
void UpdateFreeSpace(DeviceInfo* info);
|
||||
void GuessDriveLetter(DeviceInfo* info);
|
||||
bool CheckDriveLetter(DeviceInfo* info, const QString& drive);
|
||||
|
||||
static QString CanonicalNameToId(const QString& canonical_name);
|
||||
void WMDMDeviceAdded(const QString& canonical_name);
|
||||
void WMDMDeviceRemoved(const QString& canonical_name);
|
||||
|
||||
private:
|
||||
boost::scoped_ptr<WmdmThread> thread_;
|
||||
|
||||
SacHandle sac_;
|
||||
DWORD notification_cookie_;
|
||||
|
||||
QMutex mutex_;
|
||||
QMap<QString, DeviceInfo> devices_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
T WmdmLister::LockAndGetDeviceInfo(const QString& id, T DeviceInfo::*field) {
|
||||
QMutexLocker l(&mutex_);
|
||||
if (!devices_.contains(id))
|
||||
return T();
|
||||
|
||||
return devices_[id].*field;
|
||||
}
|
||||
|
||||
#endif // WMDMLISTER_H
|
@ -1,133 +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 "devicelister.h"
|
||||
#include "wmdmdevice.h"
|
||||
#include "wmdmlister.h"
|
||||
#include "wmdmloader.h"
|
||||
#include "wmdmthread.h"
|
||||
#include "core/taskmanager.h"
|
||||
#include "library/librarybackend.h"
|
||||
|
||||
#include <boost/scoped_array.hpp>
|
||||
|
||||
#include <cwchar>
|
||||
#include <mswmdm.h>
|
||||
|
||||
#include <QUuid>
|
||||
|
||||
WmdmLoader::WmdmLoader(TaskManager* task_manager, LibraryBackend* backend,
|
||||
boost::shared_ptr<ConnectedDevice> device)
|
||||
: QObject(NULL),
|
||||
device_(device),
|
||||
task_manager_(task_manager),
|
||||
backend_(backend)
|
||||
{
|
||||
original_thread_ = thread();
|
||||
}
|
||||
|
||||
WmdmLoader::~WmdmLoader() {
|
||||
}
|
||||
|
||||
void WmdmLoader::LoadDatabase() {
|
||||
int task_id = task_manager_->StartTask(tr("Loading Windows Media device"));
|
||||
emit TaskStarted(task_id);
|
||||
|
||||
boost::scoped_ptr<WmdmThread> thread(new WmdmThread);
|
||||
|
||||
// Get the device's canonical name
|
||||
boost::shared_ptr<WmdmDevice> connected_device =
|
||||
boost::static_pointer_cast<WmdmDevice>(device_);
|
||||
WmdmLister* lister = static_cast<WmdmLister*>(connected_device->lister());
|
||||
QString canonical_name = lister->DeviceCanonicalName(connected_device->unique_id());
|
||||
|
||||
IWMDMStorage* storage = thread->GetRootStorage(canonical_name);
|
||||
QStringList path_components;
|
||||
RecursiveExploreStorage(storage, &path_components);
|
||||
storage->Release();
|
||||
|
||||
thread.reset();
|
||||
|
||||
// Need to remove all the existing songs in the database first
|
||||
backend_->DeleteSongs(backend_->FindSongsInDirectory(1));
|
||||
|
||||
// Add the songs we've just loaded
|
||||
backend_->AddOrUpdateSongs(songs_);
|
||||
|
||||
task_manager_->SetTaskFinished(task_id);
|
||||
emit LoadFinished();
|
||||
}
|
||||
|
||||
void WmdmLoader::RecursiveExploreStorage(IWMDMStorage* parent, QStringList* path_components) {
|
||||
IWMDMEnumStorage* child_it = NULL;
|
||||
parent->EnumStorage(&child_it);
|
||||
|
||||
IWMDMStorage* child = NULL;
|
||||
ULONG num_retreived = 0;
|
||||
while (child_it->Next(1, &child, &num_retreived) == S_OK && num_retreived == 1) {
|
||||
const int kMaxLen = 255;
|
||||
wchar_t name[kMaxLen];
|
||||
child->GetName(name, kMaxLen);
|
||||
|
||||
DWORD attributes = 0;
|
||||
_WAVEFORMATEX audio_format;
|
||||
child->GetAttributes(&attributes, &audio_format);
|
||||
|
||||
path_components->append(QString::fromWCharArray(name));
|
||||
if (attributes & WMDM_FILE_ATTR_FILE) {
|
||||
LoadFile(child, path_components);
|
||||
} else if (attributes & WMDM_FILE_ATTR_FOLDER) {
|
||||
RecursiveExploreStorage(child, path_components);
|
||||
}
|
||||
path_components->removeLast();
|
||||
|
||||
child->Release();
|
||||
}
|
||||
child_it->Release();
|
||||
}
|
||||
|
||||
void WmdmLoader::LoadFile(IWMDMStorage* file, const QStringList* path_components) {
|
||||
// Convert to a IWMDMStorage3 so we can get metadata
|
||||
IWMDMStorage3* storage3 = NULL;
|
||||
if (file->QueryInterface(IID_IWMDMStorage3, (void**) &storage3))
|
||||
return;
|
||||
|
||||
// Get the metadata interface
|
||||
IWMDMMetaData* metadata = NULL;
|
||||
if (storage3->GetMetadata(&metadata)) {
|
||||
storage3->Release();
|
||||
return;
|
||||
}
|
||||
storage3->Release();
|
||||
|
||||
QUrl url;
|
||||
url.setScheme("wmdm");
|
||||
url.setPath(path_components->join("/"));
|
||||
|
||||
// Store the metadata in here
|
||||
Song song;
|
||||
song.InitFromWmdm(metadata);
|
||||
song.set_directory_id(1);
|
||||
song.set_url(url);
|
||||
|
||||
metadata->Release();
|
||||
|
||||
if (song.is_valid())
|
||||
songs_ << song;
|
||||
}
|
||||
|
||||
|
@ -1,63 +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 WMDMLOADER_H
|
||||
#define WMDMLOADER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "core/song.h"
|
||||
|
||||
class ConnectedDevice;
|
||||
class LibraryBackend;
|
||||
class TaskManager;
|
||||
|
||||
struct IWMDMStorage;
|
||||
|
||||
class WmdmLoader : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
WmdmLoader(TaskManager* task_manager, LibraryBackend* backend,
|
||||
boost::shared_ptr<ConnectedDevice> device);
|
||||
~WmdmLoader();
|
||||
|
||||
public slots:
|
||||
void LoadDatabase();
|
||||
|
||||
signals:
|
||||
void Error(const QString& message);
|
||||
void TaskStarted(int task_id);
|
||||
void LoadFinished();
|
||||
|
||||
private:
|
||||
void RecursiveExploreStorage(IWMDMStorage* parent, QStringList* path_components);
|
||||
void LoadFile(IWMDMStorage* file, const QStringList* path_components);
|
||||
|
||||
private:
|
||||
boost::shared_ptr<ConnectedDevice> device_;
|
||||
QThread* original_thread_;
|
||||
|
||||
TaskManager* task_manager_;
|
||||
LibraryBackend* backend_;
|
||||
|
||||
SongList songs_;
|
||||
};
|
||||
|
||||
#endif // WMDMLOADER_H
|
@ -1,82 +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 "wmdmprogress.h"
|
||||
|
||||
#include <QtDebug>
|
||||
|
||||
WmdmProgress::WmdmProgress(const MusicStorage::ProgressFunction& f)
|
||||
: f_(f),
|
||||
estimated_(0)
|
||||
{
|
||||
}
|
||||
|
||||
LONG WmdmProgress::QueryInterface(const IID& riid, void** object) {
|
||||
*object = 0;
|
||||
|
||||
if (riid == IID_IUnknown)
|
||||
*object = (IUnknown*) this;
|
||||
else if (riid == IID_IWMDMProgress)
|
||||
*object = (IWMDMProgress*) this;
|
||||
else if (riid == IID_IWMDMProgress2)
|
||||
*object = (IWMDMProgress2*) this;
|
||||
else if (riid == IID_IWMDMProgress3)
|
||||
*object = (IWMDMProgress3*) this;
|
||||
else
|
||||
return E_NOINTERFACE;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
ULONG WmdmProgress::AddRef() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ULONG WmdmProgress::Release() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
HRESULT WmdmProgress::Begin(DWORD estimated_ticks) {
|
||||
return Begin3(EVENT_WMDM_CONTENT_TRANSFER, estimated_ticks, NULL);
|
||||
}
|
||||
|
||||
HRESULT WmdmProgress::End() {
|
||||
return End3(EVENT_WMDM_CONTENT_TRANSFER, S_OK, NULL);
|
||||
}
|
||||
|
||||
HRESULT WmdmProgress::Progress(DWORD transpired_ticks) {
|
||||
return Progress3(EVENT_WMDM_CONTENT_TRANSFER, transpired_ticks, NULL);
|
||||
}
|
||||
|
||||
HRESULT WmdmProgress::End2(HRESULT completion_code) {
|
||||
return End3(EVENT_WMDM_CONTENT_TRANSFER, completion_code, NULL);
|
||||
}
|
||||
|
||||
HRESULT WmdmProgress::Begin3(GUID, DWORD estimated_ticks, OPAQUECOMMAND*) {
|
||||
estimated_ = estimated_ticks;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT WmdmProgress::End3(GUID, HRESULT, OPAQUECOMMAND*) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT WmdmProgress::Progress3(GUID, DWORD transpired_ticks, OPAQUECOMMAND*) {
|
||||
if (estimated_ != 0)
|
||||
f_(float(transpired_ticks) / estimated_);
|
||||
return S_OK;
|
||||
}
|
@ -1,58 +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 WMDMPROGRESS_H
|
||||
#define WMDMPROGRESS_H
|
||||
|
||||
#include <mswmdm.h>
|
||||
|
||||
#include "core/musicstorage.h"
|
||||
|
||||
class WmdmProgress : public IWMDMProgress3 {
|
||||
public:
|
||||
WmdmProgress(const MusicStorage::ProgressFunction& f =
|
||||
MusicStorage::ProgressFunction());
|
||||
|
||||
// IUnknown
|
||||
// The __stdcall is *really* important
|
||||
virtual LONG __stdcall QueryInterface(const IID& riid, void** object);
|
||||
virtual ULONG __stdcall AddRef();
|
||||
virtual ULONG __stdcall Release();
|
||||
|
||||
// IWMDMProgress
|
||||
virtual HRESULT __stdcall Begin(DWORD estimated_ticks);
|
||||
virtual HRESULT __stdcall End();
|
||||
virtual HRESULT __stdcall Progress(DWORD transpired_ticks);
|
||||
|
||||
// IWMDMProgress2
|
||||
virtual HRESULT __stdcall End2(HRESULT completion_code);
|
||||
|
||||
// IWMDMProgress3
|
||||
virtual HRESULT __stdcall Begin3(GUID event_id, DWORD estimated_ticks,
|
||||
OPAQUECOMMAND* context);
|
||||
virtual HRESULT __stdcall End3(GUID event_id, HRESULT completion_code,
|
||||
OPAQUECOMMAND* context);
|
||||
virtual HRESULT __stdcall Progress3(GUID event_id, DWORD transpired_ticks,
|
||||
OPAQUECOMMAND* context);
|
||||
|
||||
private:
|
||||
MusicStorage::ProgressFunction f_;
|
||||
|
||||
DWORD estimated_;
|
||||
};
|
||||
|
||||
#endif // WMDMPROGRESS_H
|
@ -1,157 +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 "wmdmthread.h"
|
||||
#include "core/logging.h"
|
||||
#include "core/utilities.h"
|
||||
|
||||
#include <mswmdm.h>
|
||||
|
||||
#include <boost/scoped_array.hpp>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QLibrary>
|
||||
#include <QMutexLocker>
|
||||
#include <QtDebug>
|
||||
|
||||
BYTE abPVK[] = {0x00};
|
||||
BYTE abCert[] = {0x00};
|
||||
|
||||
bool WmdmThread::sIsLoaded = false;
|
||||
|
||||
decltype(&CSecureChannelClient_New) WmdmThread::_CSecureChannelClient_New;
|
||||
decltype(&CSecureChannelClient_Free) WmdmThread::_CSecureChannelClient_Free;
|
||||
decltype(&CSecureChannelClient_SetCertificate) WmdmThread::_CSecureChannelClient_SetCertificate;
|
||||
decltype(&CSecureChannelClient_SetInterface) WmdmThread::_CSecureChannelClient_SetInterface;
|
||||
decltype(&CSecureChannelClient_Authenticate) WmdmThread::_CSecureChannelClient_Authenticate;
|
||||
|
||||
|
||||
WmdmThread::WmdmThread()
|
||||
: device_manager_(NULL),
|
||||
sac_(NULL)
|
||||
{
|
||||
if (!sIsLoaded) {
|
||||
return;
|
||||
}
|
||||
// Initialise COM
|
||||
CoInitialize(0);
|
||||
|
||||
// Authenticate with WMDM
|
||||
IComponentAuthenticate* auth;
|
||||
if (CoCreateInstance(CLSID_MediaDevMgr, NULL, CLSCTX_ALL,
|
||||
IID_IComponentAuthenticate, (void**) &auth)) {
|
||||
qLog(Warning) << "Error creating the IComponentAuthenticate interface";
|
||||
return;
|
||||
}
|
||||
|
||||
sac_ = _CSecureChannelClient_New();
|
||||
if (_CSecureChannelClient_SetCertificate(
|
||||
sac_, SAC_CERT_V1, abCert, sizeof(abCert), abPVK, sizeof(abPVK))) {
|
||||
qLog(Warning) << "Error setting SAC certificate";
|
||||
return;
|
||||
}
|
||||
|
||||
_CSecureChannelClient_SetInterface(sac_, auth);
|
||||
if (_CSecureChannelClient_Authenticate(sac_, SAC_PROTOCOL_V1)) {
|
||||
qLog(Warning) << "Error authenticating with SAC";
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the device manager
|
||||
if (auth->QueryInterface(IID_IWMDeviceManager2, (void**)&device_manager_)) {
|
||||
qLog(Warning) << "Error creating WMDM device manager";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
WmdmThread::~WmdmThread() {
|
||||
if (device_manager_) {
|
||||
// Release the device manager
|
||||
device_manager_->Release();
|
||||
}
|
||||
|
||||
if (sac_) {
|
||||
// SAC
|
||||
_CSecureChannelClient_Free(sac_);
|
||||
}
|
||||
|
||||
// Uninitialise COM
|
||||
CoUninitialize();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
T Resolve(QLibrary* library, const char* name) {
|
||||
return reinterpret_cast<T>(library->resolve(name));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool WmdmThread::StaticInit() {
|
||||
if (!sIsLoaded) {
|
||||
QLibrary library(QCoreApplication::applicationDirPath() + "/sac_shim.dll");
|
||||
if (!library.load()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_CSecureChannelClient_New = Resolve<decltype(_CSecureChannelClient_New)>(
|
||||
&library, "CSecureChannelClient_New");
|
||||
_CSecureChannelClient_Free = Resolve<decltype(_CSecureChannelClient_Free)>(
|
||||
&library, "CSecureChannelClient_Free");
|
||||
_CSecureChannelClient_SetCertificate = Resolve<decltype(_CSecureChannelClient_SetCertificate)>(
|
||||
&library, "CSecureChannelClient_SetCertificate");
|
||||
_CSecureChannelClient_SetInterface = Resolve<decltype(_CSecureChannelClient_SetInterface)>(
|
||||
&library, "CSecureChannelClient_SetInterface");
|
||||
if (_CSecureChannelClient_New &&
|
||||
_CSecureChannelClient_Free &&
|
||||
_CSecureChannelClient_SetCertificate &&
|
||||
_CSecureChannelClient_SetInterface) {
|
||||
sIsLoaded = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
IWMDMDevice* WmdmThread::GetDeviceByCanonicalName(const QString& device_name) {
|
||||
ScopedWCharArray name(device_name);
|
||||
|
||||
IWMDMDevice* device = NULL;
|
||||
if (device_manager_->GetDeviceFromCanonicalName(name, &device)) {
|
||||
qLog(Warning) << "Error in GetDeviceFromCanonicalName for" << device_name;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
IWMDMStorage* WmdmThread::GetRootStorage(const QString& device_name) {
|
||||
IWMDMDevice* device = GetDeviceByCanonicalName(device_name);
|
||||
|
||||
IWMDMEnumStorage* storage_it = NULL;
|
||||
device->EnumStorage(&storage_it);
|
||||
|
||||
ULONG storage_fetched = 0;
|
||||
IWMDMStorage* storage = NULL;
|
||||
storage_it->Next(1, &storage, &storage_fetched);
|
||||
|
||||
storage_it->Release();
|
||||
device->Release();
|
||||
|
||||
return storage;
|
||||
}
|
@ -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 WMDMTHREAD_H
|
||||
#define WMDMTHREAD_H
|
||||
|
||||
#include <QMutex>
|
||||
#include <QtGlobal>
|
||||
|
||||
#include <sac_shim.h>
|
||||
|
||||
struct IWMDeviceManager2;
|
||||
struct IWMDMDevice;
|
||||
struct IWMDMStorage;
|
||||
|
||||
class WmdmThread {
|
||||
public:
|
||||
WmdmThread();
|
||||
~WmdmThread();
|
||||
|
||||
IWMDeviceManager2* manager() const { return device_manager_; }
|
||||
|
||||
IWMDMDevice* GetDeviceByCanonicalName(const QString& device_name);
|
||||
IWMDMStorage* GetRootStorage(const QString& device_name);
|
||||
|
||||
static bool StaticInit();
|
||||
private:
|
||||
|
||||
Q_DISABLE_COPY(WmdmThread);
|
||||
|
||||
IWMDeviceManager2* device_manager_;
|
||||
SacHandle sac_;
|
||||
|
||||
static decltype(&CSecureChannelClient_New) _CSecureChannelClient_New;
|
||||
static decltype(&CSecureChannelClient_Free) _CSecureChannelClient_Free;
|
||||
static decltype(&CSecureChannelClient_SetCertificate) _CSecureChannelClient_SetCertificate;
|
||||
static decltype(&CSecureChannelClient_SetInterface) _CSecureChannelClient_SetInterface;
|
||||
static decltype(&CSecureChannelClient_Authenticate) _CSecureChannelClient_Authenticate;
|
||||
|
||||
static bool sIsLoaded;
|
||||
};
|
||||
|
||||
#endif // WMDMTHREAD_H
|
@ -101,7 +101,6 @@ using boost::scoped_ptr;
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
# include <qtsparkle/Updater>
|
||||
# include "devices/wmdmthread.h"
|
||||
#endif
|
||||
|
||||
// Load sqlite plugin on windows and mac.
|
||||
@ -447,12 +446,6 @@ int main(int argc, char *argv[]) {
|
||||
mpris::Mpris mpris(&app);
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN32
|
||||
if (!WmdmThread::StaticInit()) {
|
||||
qLog(Warning) << "Failed to initialise SAC shim";
|
||||
}
|
||||
#endif
|
||||
|
||||
// Window
|
||||
MainWindow w(&app, tray_icon.get(), &osd);
|
||||
#ifdef Q_OS_DARWIN
|
||||
|
Loading…
x
Reference in New Issue
Block a user