Get a list of songs from a WMDM device
This commit is contained in:
parent
bd4b6b2b50
commit
7821a48cb7
|
@ -540,8 +540,15 @@ endif(LIBMTP_FOUND)
|
|||
|
||||
# Windows media lister
|
||||
IF(WIN32)
|
||||
list(APPEND SOURCES devices/wmdmdevice.cpp)
|
||||
list(APPEND SOURCES devices/wmdmlister.cpp)
|
||||
list(APPEND SOURCES devices/wmdmloader.cpp)
|
||||
list(APPEND SOURCES devices/wmdmthread.cpp)
|
||||
|
||||
list(APPEND HEADERS devices/wmdmdevice.h)
|
||||
list(APPEND HEADERS devices/wmdmlister.h)
|
||||
list(APPEND HEADERS devices/wmdmloader.h)
|
||||
list(APPEND HEADERS devices/wmdmthread.h)
|
||||
ENDIF(WIN32)
|
||||
|
||||
# Mac specific startup stuff
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#endif
|
||||
#ifdef Q_OS_WIN32
|
||||
# include "wmdmlister.h"
|
||||
# include "wmdmdevice.h"
|
||||
#endif
|
||||
#ifdef HAVE_LIBGPOD
|
||||
# include "gpoddevice.h"
|
||||
|
@ -183,6 +184,7 @@ DeviceManager::DeviceManager(BackgroundThread<Database>* database,
|
|||
#endif
|
||||
#ifdef Q_OS_WIN32
|
||||
AddLister(new WmdmLister);
|
||||
AddDeviceClass<WmdmDevice>();
|
||||
#endif
|
||||
#ifdef HAVE_IMOBILEDEVICE
|
||||
AddLister(new iLister);
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
/* 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"
|
||||
#include "wmdmloader.h"
|
||||
#include "library/librarybackend.h"
|
||||
#include "library/librarymodel.h"
|
||||
|
||||
#include <QThread>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void WmdmDevice::StartCopy() {
|
||||
// Ensure only one "organise files" can be active at any one time
|
||||
db_busy_.lock();
|
||||
}
|
||||
|
||||
bool WmdmDevice::CopyToStorage(
|
||||
const QString& source, const QString&, const Song& metadata,
|
||||
bool, bool remove_original)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
db_busy_.unlock();
|
||||
}
|
||||
|
||||
void WmdmDevice::StartDelete() {
|
||||
StartCopy();
|
||||
}
|
||||
|
||||
bool WmdmDevice::DeleteFromStorage(const Song& metadata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void WmdmDevice::FinishDelete(bool success) {
|
||||
FinishCopy(success);
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef WMDMDEVICE_H
|
||||
#define WMDMDEVICE_H
|
||||
|
||||
#include "connecteddevice.h"
|
||||
|
||||
class WmdmLoader;
|
||||
|
||||
class WmdmDevice : public ConnectedDevice {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Q_INVOKABLE WmdmDevice(const QUrl& url, DeviceLister* lister,
|
||||
const QString& unique_id, DeviceManager* manager,
|
||||
int database_id, bool first_time);
|
||||
~WmdmDevice();
|
||||
|
||||
static QStringList url_schemes() { return QStringList() << "wmdm"; }
|
||||
|
||||
void Init();
|
||||
|
||||
void StartCopy();
|
||||
bool CopyToStorage(const QString& source, const QString& destination,
|
||||
const Song& metadata, bool overwrite, bool remove_original);
|
||||
void FinishCopy(bool success);
|
||||
|
||||
void StartDelete();
|
||||
bool DeleteFromStorage(const Song& metadata);
|
||||
void FinishDelete(bool success);
|
||||
|
||||
private slots:
|
||||
void LoadFinished();
|
||||
|
||||
private:
|
||||
QThread* loader_thread_;
|
||||
WmdmLoader* loader_;
|
||||
|
||||
QMutex db_busy_;
|
||||
SongList songs_to_add_;
|
||||
SongList songs_to_remove_;
|
||||
};
|
||||
|
||||
#endif // WMDMDEVICE_H
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
|
||||
#include "wmdmlister.h"
|
||||
#include "wmdmthread.h"
|
||||
|
||||
#include <icomponentauthenticate.h>
|
||||
#include <objbase.h>
|
||||
|
@ -27,9 +28,6 @@
|
|||
#include <QStringList>
|
||||
#include <QtDebug>
|
||||
|
||||
BYTE abPVK[] = {0x00};
|
||||
BYTE abCert[] = {0x00};
|
||||
|
||||
QString WmdmLister::CanonicalNameToId(const QString& canonical_name) {
|
||||
return "wmdm/" + canonical_name;
|
||||
}
|
||||
|
@ -39,45 +37,20 @@ QString WmdmLister::DeviceInfo::unique_id() const {
|
|||
}
|
||||
|
||||
WmdmLister::WmdmLister()
|
||||
: device_manager_(NULL),
|
||||
notification_cookie_(0)
|
||||
: notification_cookie_(0)
|
||||
{
|
||||
}
|
||||
|
||||
WmdmLister::~WmdmLister() {
|
||||
Q_ASSERT(!thread_);
|
||||
}
|
||||
|
||||
void WmdmLister::Init() {
|
||||
// Initialise COM
|
||||
CoInitialize(0);
|
||||
|
||||
// Authenticate with WMDM
|
||||
IComponentAuthenticate* auth;
|
||||
if (CoCreateInstance(CLSID_MediaDevMgr, NULL, CLSCTX_ALL,
|
||||
IID_IComponentAuthenticate, (void**) &auth)) {
|
||||
qWarning() << "Error creating the IComponentAuthenticate interface";
|
||||
return;
|
||||
}
|
||||
|
||||
sac_ = CSecureChannelClient_New();
|
||||
if (CSecureChannelClient_SetCertificate(
|
||||
sac_, SAC_CERT_V1, abCert, sizeof(abCert), abPVK, sizeof(abPVK))) {
|
||||
qWarning() << "Error setting SAC certificate";
|
||||
return;
|
||||
}
|
||||
|
||||
CSecureChannelClient_SetInterface(sac_, auth);
|
||||
if (CSecureChannelClient_Authenticate(sac_, SAC_PROTOCOL_V1)) {
|
||||
qWarning() << "Error authenticating with SAC";
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the device manager
|
||||
if (auth->QueryInterface(IID_IWMDeviceManager2, (void**)&device_manager_)) {
|
||||
qWarning() << "Error creating WMDM device manager";
|
||||
return;
|
||||
}
|
||||
thread_.reset(new WmdmThread);
|
||||
|
||||
// Register for notifications
|
||||
IConnectionPointContainer* cp_container = NULL;
|
||||
device_manager_->QueryInterface(IID_IConnectionPointContainer, (void**)&cp_container);
|
||||
thread_->manager()->QueryInterface(IID_IConnectionPointContainer, (void**)&cp_container);
|
||||
|
||||
IConnectionPoint* cp = NULL;
|
||||
cp_container->FindConnectionPoint(IID_IWMDMNotification, &cp);
|
||||
|
@ -89,7 +62,7 @@ void WmdmLister::Init() {
|
|||
|
||||
// Fetch the initial list of devices
|
||||
IWMDMEnumDevice* device_it = NULL;
|
||||
if (device_manager_->EnumDevices2(&device_it)) {
|
||||
if (thread_->manager()->EnumDevices2(&device_it)) {
|
||||
qWarning() << "Error querying WMDM devices";
|
||||
return;
|
||||
}
|
||||
|
@ -133,7 +106,7 @@ void WmdmLister::Init() {
|
|||
void WmdmLister::ShutDown() {
|
||||
// Unregister for notifications
|
||||
IConnectionPointContainer* cp_container;
|
||||
device_manager_->QueryInterface(IID_IConnectionPointContainer, (void**)&cp_container);
|
||||
thread_->manager()->QueryInterface(IID_IConnectionPointContainer, (void**)&cp_container);
|
||||
|
||||
IConnectionPoint* cp;
|
||||
cp_container->FindConnectionPoint(IID_IWMDMNotification, &cp);
|
||||
|
@ -141,14 +114,7 @@ void WmdmLister::ShutDown() {
|
|||
cp->Release();
|
||||
cp_container->Release();
|
||||
|
||||
// Release the device manager
|
||||
device_manager_->Release();
|
||||
|
||||
// SAC
|
||||
CSecureChannelClient_Free(sac_);
|
||||
|
||||
// Uninitialise COM
|
||||
CoUninitialize();
|
||||
thread_.reset();
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
|
@ -207,8 +173,8 @@ WmdmLister::DeviceInfo WmdmLister::ReadDeviceInfo(IWMDMDevice2* device) {
|
|||
ret.free_bytes_ -= GetSpaceValue(boost::bind(&IWMDMStorageGlobals::GetTotalBad, globals, _1, _2));
|
||||
|
||||
globals->Release();
|
||||
storage->Release();
|
||||
}
|
||||
storage->Release();
|
||||
}
|
||||
storage_it->Release();
|
||||
}
|
||||
|
@ -262,7 +228,11 @@ QString WmdmLister::MakeFriendlyName(const QString& id) {
|
|||
}
|
||||
|
||||
QList<QUrl> WmdmLister::MakeDeviceUrls(const QString& id) {
|
||||
return QList<QUrl>();
|
||||
QUrl url;
|
||||
url.setScheme("wmdm");
|
||||
url.setPath(id);
|
||||
|
||||
return QList<QUrl>() << url;
|
||||
}
|
||||
|
||||
void WmdmLister::UnmountDevice(const QString& id) {
|
||||
|
@ -288,7 +258,7 @@ void WmdmLister::WMDMDeviceAdded(const QString& canonical_name) {
|
|||
name[canonical_name.length()] = '\0';
|
||||
|
||||
IWMDMDevice* device = NULL;
|
||||
if (device_manager_->GetDeviceFromCanonicalName(name.get(), &device)) {
|
||||
if (thread_->manager()->GetDeviceFromCanonicalName(name.get(), &device)) {
|
||||
qWarning() << "Error in GetDeviceFromCanonicalName for" << canonical_name;
|
||||
return;
|
||||
}
|
||||
|
@ -321,6 +291,9 @@ void WmdmLister::WMDMDeviceRemoved(const QString& canonical_name) {
|
|||
if (!devices_.contains(id))
|
||||
return;
|
||||
|
||||
devices_[id].device_->Release();
|
||||
devices_[id].storage_->Release();
|
||||
|
||||
devices_.remove(id);
|
||||
}
|
||||
|
||||
|
@ -348,3 +321,7 @@ ULONG WmdmLister::Release() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
QString WmdmLister::DeviceCanonicalName(const QString& id) {
|
||||
return LockAndGetDeviceInfo(id, &DeviceInfo::canonical_name_);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,16 +23,20 @@
|
|||
#include <QMutex>
|
||||
#include <QPixmap>
|
||||
|
||||
#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();
|
||||
|
@ -55,6 +59,9 @@ public:
|
|||
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();
|
||||
|
@ -91,7 +98,8 @@ private:
|
|||
void WMDMDeviceRemoved(const QString& canonical_name);
|
||||
|
||||
private:
|
||||
IWMDeviceManager2* device_manager_;
|
||||
boost::scoped_ptr<WmdmThread> thread_;
|
||||
|
||||
SacHandle sac_;
|
||||
DWORD notification_cookie_;
|
||||
|
||||
|
|
|
@ -0,0 +1,348 @@
|
|||
/* 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 "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>
|
||||
|
||||
// Copied from the WMDM SDK 11, it doesn't seem to be included in SDK 9 for
|
||||
// some reason, even though the devices use it.
|
||||
typedef enum tagWMDM_FORMATCODE{
|
||||
WMDM_FORMATCODE_NOTUSED = 0x0000,
|
||||
WMDM_FORMATCODE_ALLIMAGES = 0xFFFFFFFF,
|
||||
WMDM_FORMATCODE_UNDEFINED = 0x3000,
|
||||
WMDM_FORMATCODE_ASSOCIATION = 0x3001,
|
||||
WMDM_FORMATCODE_SCRIPT = 0x3002,
|
||||
WMDM_FORMATCODE_EXECUTABLE = 0x3003,
|
||||
WMDM_FORMATCODE_TEXT = 0x3004,
|
||||
WMDM_FORMATCODE_HTML = 0x3005,
|
||||
WMDM_FORMATCODE_DPOF = 0x3006,
|
||||
WMDM_FORMATCODE_AIFF = 0x3007,
|
||||
WMDM_FORMATCODE_WAVE = 0x3008,
|
||||
WMDM_FORMATCODE_MP3 = 0x3009,
|
||||
WMDM_FORMATCODE_AVI = 0x300A,
|
||||
WMDM_FORMATCODE_MPEG = 0x300B,
|
||||
WMDM_FORMATCODE_ASF = 0x300C,
|
||||
WMDM_FORMATCODE_RESERVED_FIRST = 0x300D,
|
||||
WMDM_FORMATCODE_RESERVED_LAST = 0x37FF,
|
||||
WMDM_FORMATCODE_IMAGE_UNDEFINED = 0x3800,
|
||||
WMDM_FORMATCODE_IMAGE_EXIF = 0x3801,
|
||||
WMDM_FORMATCODE_IMAGE_TIFFEP = 0x3802,
|
||||
WMDM_FORMATCODE_IMAGE_FLASHPIX = 0x3803,
|
||||
WMDM_FORMATCODE_IMAGE_BMP = 0x3804,
|
||||
WMDM_FORMATCODE_IMAGE_CIFF = 0x3805,
|
||||
WMDM_FORMATCODE_IMAGE_GIF = 0x3807,
|
||||
WMDM_FORMATCODE_IMAGE_JFIF = 0x3808,
|
||||
WMDM_FORMATCODE_IMAGE_PCD = 0x3809,
|
||||
WMDM_FORMATCODE_IMAGE_PICT = 0x380A,
|
||||
WMDM_FORMATCODE_IMAGE_PNG = 0x380B,
|
||||
WMDM_FORMATCODE_IMAGE_TIFF = 0x380D,
|
||||
WMDM_FORMATCODE_IMAGE_TIFFIT = 0x380E,
|
||||
WMDM_FORMATCODE_IMAGE_JP2 = 0x380F,
|
||||
WMDM_FORMATCODE_IMAGE_JPX = 0x3810,
|
||||
WMDM_FORMATCODE_IMAGE_RESERVED_FIRST = 0x3811,
|
||||
WMDM_FORMATCODE_IMAGE_RESERVED_LAST = 0x3FFF,
|
||||
WMDM_FORMATCODE_UNDEFINEDFIRMWARE = 0xB802,
|
||||
WMDM_FORMATCODE_WINDOWSIMAGEFORMAT = 0xB881,
|
||||
WMDM_FORMATCODE_UNDEFINEDAUDIO = 0xB900,
|
||||
WMDM_FORMATCODE_WMA = 0xB901,
|
||||
WMDM_FORMATCODE_OGG = 0xB902,
|
||||
WMDM_FORMATCODE_AAC = 0xB903,
|
||||
WMDM_FORMATCODE_AUDIBLE = 0xB904,
|
||||
WMDM_FORMATCODE_FLAC = 0xB906,
|
||||
WMDM_FORMATCODE_UNDEFINEDVIDEO = 0xB980,
|
||||
WMDM_FORMATCODE_WMV = 0xB981,
|
||||
WMDM_FORMATCODE_MP4 = 0xB982,
|
||||
WMDM_FORMATCODE_MP2 = 0xB983,
|
||||
WMDM_FORMATCODE_UNDEFINEDCOLLECTION = 0xBA00,
|
||||
WMDM_FORMATCODE_ABSTRACTMULTIMEDIAALBUM = 0xBA01,
|
||||
WMDM_FORMATCODE_ABSTRACTIMAGEALBUM = 0xBA02,
|
||||
WMDM_FORMATCODE_ABSTRACTAUDIOALBUM = 0xBA03,
|
||||
WMDM_FORMATCODE_ABSTRACTVIDEOALBUM = 0xBA04,
|
||||
WMDM_FORMATCODE_ABSTRACTAUDIOVIDEOPLAYLIST = 0xBA05,
|
||||
WMDM_FORMATCODE_ABSTRACTCONTACTGROUP = 0xBA06,
|
||||
WMDM_FORMATCODE_ABSTRACTMESSAGEFOLDER = 0xBA07,
|
||||
WMDM_FORMATCODE_ABSTRACTCHAPTEREDPRODUCTION = 0xBA08,
|
||||
WMDM_FORMATCODE_WPLPLAYLIST = 0xBA10,
|
||||
WMDM_FORMATCODE_M3UPLAYLIST = 0xBA11,
|
||||
WMDM_FORMATCODE_MPLPLAYLIST = 0xBA12,
|
||||
WMDM_FORMATCODE_ASXPLAYLIST = 0xBA13,
|
||||
WMDM_FORMATCODE_PLSPLAYLIST = 0xBA14,
|
||||
WMDM_FORMATCODE_UNDEFINEDDOCUMENT = 0xBA80,
|
||||
WMDM_FORMATCODE_ABSTRACTDOCUMENT = 0xBA81,
|
||||
WMDM_FORMATCODE_XMLDOCUMENT = 0xBA82,
|
||||
WMDM_FORMATCODE_MICROSOFTWORDDOCUMENT= 0xBA83,
|
||||
WMDM_FORMATCODE_MHTCOMPILEDHTMLDOCUMENT = 0xBA84,
|
||||
WMDM_FORMATCODE_MICROSOFTEXCELSPREADSHEET = 0xBA85,
|
||||
WMDM_FORMATCODE_MICROSOFTPOWERPOINTDOCUMENT = 0xBA86,
|
||||
WMDM_FORMATCODE_UNDEFINEDMESSAGE = 0xBB00,
|
||||
WMDM_FORMATCODE_ABSTRACTMESSAGE = 0xBB01,
|
||||
WMDM_FORMATCODE_UNDEFINEDCONTACT = 0xBB80,
|
||||
WMDM_FORMATCODE_ABSTRACTCONTACT = 0xBB81,
|
||||
WMDM_FORMATCODE_VCARD2 = 0xBB82,
|
||||
WMDM_FORMATCODE_VCARD3 = 0xBB83,
|
||||
WMDM_FORMATCODE_UNDEFINEDCALENDARITEM = 0xBE00,
|
||||
WMDM_FORMATCODE_ABSTRACTCALENDARITEM = 0xBE01,
|
||||
WMDM_FORMATCODE_VCALENDAR1 = 0xBE02,
|
||||
WMDM_FORMATCODE_VCALENDAR2 = 0xBE03,
|
||||
WMDM_FORMATCODE_UNDEFINEDWINDOWSEXECUTABLE = 0xBE80,
|
||||
WMDM_FORMATCODE_MEDIA_CAST = 0xBE81,
|
||||
WMDM_FORMATCODE_SECTION = 0xBE82
|
||||
} WMDM_FORMATCODE;
|
||||
|
||||
|
||||
|
||||
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());
|
||||
|
||||
// Find the device
|
||||
boost::scoped_array<wchar_t> name(new wchar_t[canonical_name.length() + 1]);
|
||||
canonical_name.toWCharArray(name.get());
|
||||
name[canonical_name.length()] = '\0';
|
||||
|
||||
IWMDMDevice* device = NULL;
|
||||
if (thread->manager()->GetDeviceFromCanonicalName(name.get(), &device)) {
|
||||
qWarning() << "Error in GetDeviceFromCanonicalName for" << canonical_name;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the list of storages from the device
|
||||
IWMDMEnumStorage* storage_it = NULL;
|
||||
device->EnumStorage(&storage_it);
|
||||
|
||||
ULONG storage_fetched = 0;
|
||||
IWMDMStorage* storage = NULL;
|
||||
while (storage_it->Next(1, &storage, &storage_fetched) == S_OK) {
|
||||
RecursiveExploreStorage(storage);
|
||||
storage->Release();
|
||||
}
|
||||
|
||||
storage_it->Release();
|
||||
device->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) {
|
||||
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);
|
||||
|
||||
if (attributes & WMDM_FILE_ATTR_FILE) {
|
||||
LoadFile(child);
|
||||
} else if (attributes & WMDM_FILE_ATTR_FOLDER) {
|
||||
RecursiveExploreStorage(child);
|
||||
}
|
||||
|
||||
child->Release();
|
||||
}
|
||||
child_it->Release();
|
||||
}
|
||||
|
||||
void WmdmLoader::LoadFile(IWMDMStorage* file) {
|
||||
// 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();
|
||||
|
||||
// Store the metadata in here
|
||||
Song song;
|
||||
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 = ReadValue(type, value, length);
|
||||
|
||||
// Store it in the song if it's something we recognise
|
||||
if (wcscmp(name, g_wszWMDMTitle) == 0)
|
||||
song.set_title(item_value.toString());
|
||||
else if (wcscmp(name, g_wszWMDMAuthor) == 0)
|
||||
song.set_artist(item_value.toString());
|
||||
else if (wcscmp(name, g_wszWMDMDescription) == 0)
|
||||
song.set_comment(item_value.toString());
|
||||
else if (wcscmp(name, g_wszWMDMAlbumTitle) == 0)
|
||||
song.set_album(item_value.toString());
|
||||
else if (wcscmp(name, g_wszWMDMTrack) == 0)
|
||||
song.set_track(item_value.toInt());
|
||||
else if (wcscmp(name, g_wszWMDMGenre) == 0)
|
||||
song.set_genre(item_value.toString());
|
||||
else if (wcscmp(name, g_wszWMDMYear) == 0)
|
||||
song.set_year(item_value.toInt());
|
||||
else if (wcscmp(name, g_wszWMDMComposer) == 0)
|
||||
song.set_composer(item_value.toString());
|
||||
else if (wcscmp(name, g_wszWMDMBitrate) == 0)
|
||||
song.set_bitrate(item_value.toInt());
|
||||
else if (wcscmp(name, g_wszWMDMFileName) == 0)
|
||||
song.set_filename(item_value.toString());
|
||||
else if (wcscmp(name, g_wszWMDMDuration) == 0)
|
||||
song.set_length(item_value.toULongLong() / 10000000ll);
|
||||
else if (wcscmp(name, L"WMDM/FileSize") == 0)
|
||||
song.set_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);
|
||||
}
|
||||
|
||||
metadata->Release();
|
||||
|
||||
// Decide if this is music or not
|
||||
if (count == 0 || non_consumable)
|
||||
return;
|
||||
|
||||
switch (format) {
|
||||
case WMDM_FORMATCODE_AIFF:
|
||||
song.set_filetype(Song::Type_Aiff);
|
||||
break;
|
||||
|
||||
case WMDM_FORMATCODE_WAVE:
|
||||
song.set_filetype(Song::Type_Wav);
|
||||
break;
|
||||
|
||||
case WMDM_FORMATCODE_MP2:
|
||||
case WMDM_FORMATCODE_MP3:
|
||||
case WMDM_FORMATCODE_MPEG:
|
||||
song.set_filetype(Song::Type_Mpeg);
|
||||
break;
|
||||
|
||||
case WMDM_FORMATCODE_WMA:
|
||||
case WMDM_FORMATCODE_ASF:
|
||||
song.set_filetype(Song::Type_Asf);
|
||||
break;
|
||||
|
||||
case WMDM_FORMATCODE_OGG:
|
||||
song.set_filetype(Song::Type_OggVorbis);
|
||||
break;
|
||||
|
||||
case WMDM_FORMATCODE_AAC:
|
||||
case WMDM_FORMATCODE_MP4:
|
||||
song.set_filetype(Song::Type_Mp4);
|
||||
break;
|
||||
|
||||
case WMDM_FORMATCODE_FLAC:
|
||||
song.set_filetype(Song::Type_Flac);
|
||||
break;
|
||||
|
||||
case WMDM_FORMATCODE_AUDIBLE:
|
||||
case WMDM_FORMATCODE_UNDEFINEDAUDIO:
|
||||
song.set_filetype(Song::Type_Unknown);
|
||||
break;
|
||||
|
||||
default:
|
||||
return; // It's not music
|
||||
}
|
||||
|
||||
song.set_directory_id(1);
|
||||
song.set_valid(true);
|
||||
song.set_mtime(0);
|
||||
song.set_ctime(0);
|
||||
songs_ << song;
|
||||
}
|
||||
|
||||
QVariant WmdmLoader::ReadValue(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));
|
||||
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();
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/* 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/>.
|
||||
*/
|
||||
|
||||
#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);
|
||||
void LoadFile(IWMDMStorage* file);
|
||||
|
||||
static QVariant ReadValue(int type, uchar* data, uint length);
|
||||
|
||||
private:
|
||||
boost::shared_ptr<ConnectedDevice> device_;
|
||||
QThread* original_thread_;
|
||||
|
||||
TaskManager* task_manager_;
|
||||
LibraryBackend* backend_;
|
||||
|
||||
SongList songs_;
|
||||
};
|
||||
|
||||
#endif // WMDMLOADER_H
|
|
@ -0,0 +1,70 @@
|
|||
/* 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 "wmdmthread.h"
|
||||
|
||||
#include <mswmdm.h>
|
||||
|
||||
#include <QtDebug>
|
||||
|
||||
BYTE abPVK[] = {0x00};
|
||||
BYTE abCert[] = {0x00};
|
||||
|
||||
|
||||
WmdmThread::WmdmThread()
|
||||
: device_manager_(NULL)
|
||||
{
|
||||
// Initialise COM
|
||||
CoInitialize(0);
|
||||
|
||||
// Authenticate with WMDM
|
||||
IComponentAuthenticate* auth;
|
||||
if (CoCreateInstance(CLSID_MediaDevMgr, NULL, CLSCTX_ALL,
|
||||
IID_IComponentAuthenticate, (void**) &auth)) {
|
||||
qWarning() << "Error creating the IComponentAuthenticate interface";
|
||||
return;
|
||||
}
|
||||
|
||||
sac_ = CSecureChannelClient_New();
|
||||
if (CSecureChannelClient_SetCertificate(
|
||||
sac_, SAC_CERT_V1, abCert, sizeof(abCert), abPVK, sizeof(abPVK))) {
|
||||
qWarning() << "Error setting SAC certificate";
|
||||
return;
|
||||
}
|
||||
|
||||
CSecureChannelClient_SetInterface(sac_, auth);
|
||||
if (CSecureChannelClient_Authenticate(sac_, SAC_PROTOCOL_V1)) {
|
||||
qWarning() << "Error authenticating with SAC";
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the device manager
|
||||
if (auth->QueryInterface(IID_IWMDeviceManager2, (void**)&device_manager_)) {
|
||||
qWarning() << "Error creating WMDM device manager";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
WmdmThread::~WmdmThread() {
|
||||
// Release the device manager
|
||||
device_manager_->Release();
|
||||
|
||||
// SAC
|
||||
CSecureChannelClient_Free(sac_);
|
||||
|
||||
// Uninitialise COM
|
||||
CoUninitialize();
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef WMDMTHREAD_H
|
||||
#define WMDMTHREAD_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#include <sac_shim.h>
|
||||
|
||||
struct IWMDeviceManager2;
|
||||
|
||||
class WmdmThread {
|
||||
public:
|
||||
WmdmThread();
|
||||
~WmdmThread();
|
||||
|
||||
IWMDeviceManager2* manager() const { return device_manager_; }
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(WmdmThread);
|
||||
|
||||
IWMDeviceManager2* device_manager_;
|
||||
SacHandle sac_;
|
||||
};
|
||||
|
||||
#endif // WMDMTHREAD_H
|
|
@ -967,6 +967,9 @@ msgstr ""
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -967,6 +967,9 @@ msgstr ""
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -988,6 +988,9 @@ msgstr "Carregant la radio de Last.fm"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr "Carregant la base de dades de l'iPod"
|
||||
|
||||
|
|
|
@ -971,6 +971,9 @@ msgstr "Načítám rádio Last.fm"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -972,6 +972,9 @@ msgstr "Indlæser Last.fm-radio"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -987,6 +987,9 @@ msgstr "Last.fm Radio wird geladen"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr "iPod-Datenbank wird geladen"
|
||||
|
||||
|
|
|
@ -987,6 +987,9 @@ msgstr "Φόρτωμα Last.fm"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr "Φόρτωση της βάσης δεδομένων iPod"
|
||||
|
||||
|
|
|
@ -971,6 +971,9 @@ msgstr "Loading Last.fm radio"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -969,6 +969,9 @@ msgstr "Loading Last.fm radio"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -990,6 +990,9 @@ msgstr "Cargando radio de Last.fm"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr "Cargando base de datos del iPod"
|
||||
|
||||
|
|
|
@ -968,6 +968,9 @@ msgstr ""
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -981,6 +981,9 @@ msgstr "Chargement de la radio Last.fm"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -969,6 +969,9 @@ msgstr "Carregando a rádio da Last.fm"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -982,6 +982,9 @@ msgstr "Last.fm rádió betöltése"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr "iPod adatbázis betöltése"
|
||||
|
||||
|
|
|
@ -994,6 +994,9 @@ msgstr "Caricamento radio Last.fm"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr "Caricamento database dell'iPod"
|
||||
|
||||
|
|
|
@ -969,6 +969,9 @@ msgstr ""
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -967,6 +967,9 @@ msgstr ""
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -970,6 +970,9 @@ msgstr "Laster inn Last.fm radio"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -984,6 +984,9 @@ msgstr "Last.fm radio laden"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr "iPod database laden"
|
||||
|
||||
|
|
|
@ -967,6 +967,9 @@ msgstr "Cargament de la ràdio Last.fm"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -976,6 +976,9 @@ msgstr "Ładowanie radia Last.fm"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr "Wczytywanie bazy danych iPod-a"
|
||||
|
||||
|
|
|
@ -982,6 +982,9 @@ msgstr "Carregando a rádio Last.fm"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr "Carregando base de dados iPod"
|
||||
|
||||
|
|
|
@ -977,6 +977,9 @@ msgstr "Carregando rádio Last.fm"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -968,6 +968,9 @@ msgstr "Se încarcă radio Last.fm"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -979,6 +979,9 @@ msgstr "Загрузка радио Last.fm"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr "Загружается база данных iPod"
|
||||
|
||||
|
|
|
@ -981,6 +981,9 @@ msgstr "Načítava sa Last.fm rádio"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr "Načítava sa iPod databáza"
|
||||
|
||||
|
|
|
@ -981,6 +981,9 @@ msgstr "Nalaganje Last.fm radia"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr "Nalagam iPod podatkovno bazo"
|
||||
|
||||
|
|
|
@ -972,6 +972,9 @@ msgstr "Учитавам ЛастФМ радио"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr "Учитавам Ајподову базу података"
|
||||
|
||||
|
|
|
@ -976,6 +976,9 @@ msgstr "Laddar Last.fm radio"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr "Läser in iPod-databas"
|
||||
|
||||
|
|
|
@ -971,6 +971,9 @@ msgstr "Last.fm radyosu yükleniyor"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr "iPod veritabanı yükleniyor"
|
||||
|
||||
|
|
|
@ -957,6 +957,9 @@ msgstr ""
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -980,6 +980,9 @@ msgstr "Завантаження радіо Last.fm"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr "Завантаження бази даних iPod"
|
||||
|
||||
|
|
|
@ -967,6 +967,9 @@ msgstr ""
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -972,6 +972,9 @@ msgstr "載入 Last.fm電台"
|
|||
msgid "Loading MTP device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading Windows Media device"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading iPod database"
|
||||
msgstr ""
|
||||
|
||||
|
|
Loading…
Reference in New Issue