Very basic support for reading iPods with libgpod. Uses a hardcoded ~/.gvfs path for now.

This commit is contained in:
David Sansome 2010-07-04 20:52:45 +00:00
parent 48a2e3dc33
commit e9525e8ec3
43 changed files with 410 additions and 23 deletions

View File

@ -53,6 +53,7 @@ else(WIN32)
pkg_check_modules(GLIB glib-2.0)
pkg_check_modules(LIBXML libxml-2.0)
pkg_check_modules(GOBJECT gobject-2.0)
pkg_check_modules(LIBGPOD libgpod-1.0)
endif(WIN32)
find_library(LASTFM_LIBRARIES lastfm)

View File

@ -453,6 +453,15 @@ if(NOT APPLE AND NOT WIN32)
list(APPEND HEADERS devices/devicekitlister.h)
endif(NOT APPLE AND NOT WIN32)
# Libgpod device backend
if(LIBGPOD_FOUND)
set(HAVE_LIBGPOD ON)
include_directories(${LIBGPOD_INCLUDE_DIRS})
list(APPEND SOURCES devices/gpoddevice.cpp devices/gpodloader.cpp)
list(APPEND HEADERS devices/gpoddevice.h devices/gpodloader.h)
endif(LIBGPOD_FOUND)
# Mac specific startup stuff
if(APPLE)
list(APPEND SOURCES core/mac_startup.mm)
@ -465,6 +474,10 @@ list(APPEND OTHER_SOURCES
core/macglobalshortcutbackend.mm
devices/devicekitlister.h
devices/devicekitlister.cpp
devices/gpoddevice.cpp
devices/gpoddevice.h
devices/gpodloader.cpp
devices/gpodloader.h
ui/macsystemtrayicon.h
ui/macsystemtrayicon.mm
widgets/osd_mac.mm
@ -516,6 +529,10 @@ if(ENABLE_VISUALISATIONS)
target_link_libraries(clementine_lib projectM)
endif(ENABLE_VISUALISATIONS)
if(HAVE_LIBGPOD)
target_link_libraries(clementine_lib ${LIBGPOD_LIBRARIES})
endif(HAVE_LIBGPOD)
if (APPLE)
target_link_libraries(clementine_lib
${GROWL}

View File

@ -38,4 +38,6 @@
#cmakedefine HAVE_STATIC_SQLITE
#cmakedefine HAVE_LIBGPOD
#endif // CONFIG_H_IN

View File

@ -529,6 +529,37 @@ void Song::InitFromLastFM(const lastfm::Track& track) {
d->length_ = track.duration();
}
#ifdef HAVE_LIBGPOD
void Song::InitFromItdb(Itdb_Track* track) {
d->valid_ = true;
d->title_ = QString::fromUtf8(track->title);
d->album_ = QString::fromUtf8(track->album);
d->artist_ = QString::fromUtf8(track->artist);
d->albumartist_ = QString::fromUtf8(track->albumartist);
d->composer_ = QString::fromUtf8(track->composer);
d->track_ = track->track_nr;
d->disc_ = track->cd_nr;
d->bpm_ = track->BPM;
d->year_ = track->year;
d->genre_ = QString::fromUtf8(track->genre);
d->comment_ = QString::fromUtf8(track->comment);
d->compilation_ = track->compilation;
d->length_ = track->tracklen / 1000;
d->bitrate_ = track->bitrate;
d->samplerate_ = track->samplerate;
d->mtime_ = track->time_modified;
d->ctime_ = track->time_added;
d->filesize_ = track->size;
d->filetype_ = track->type2 ? Type_Mpeg : Type_Mp4;
itdb_filename_ipod2fs(track->ipod_path);
d->filename_ = QString::fromLocal8Bit(track->ipod_path);
d->basefilename_ = QFileInfo(d->filename_).fileName();
}
#endif
void Song::MergeFromSimpleMetaBundle(const Engine::SimpleMetaBundle &bundle) {
d->valid_ = true;

View File

@ -26,11 +26,16 @@
#include <QString>
#include <QMetaType>
#include "config.h"
#include "engines/engine_fwd.h"
#include <taglib/id3v1tag.h>
#include "nsUniversalDetector.h"
#ifdef HAVE_LIBGPOD
# include <gpod/itdb.h>
#endif
namespace lastfm {
class Track;
}
@ -118,8 +123,13 @@ class Song {
void InitFromFile(const QString& filename, int directory_id);
void InitFromQuery(const QSqlQuery& query, int col = 0);
void InitFromLastFM(const lastfm::Track& track);
void MergeFromSimpleMetaBundle(const Engine::SimpleMetaBundle& bundle);
#ifdef HAVE_LIBGPOD
void InitFromItdb(Itdb_Track* track);
#endif
static QString Decode(const TagLib::String& tag, const QTextCodec* codec);
// Save

View File

@ -51,3 +51,26 @@ ConnectedDevice::ConnectedDevice(DeviceLister* lister, const QString& unique_id,
ConnectedDevice::~ConnectedDevice() {
backend_->deleteLater();
}
void ConnectedDevice::InitBackendDirectory(const QString& mount_point, bool first_time) {
if (first_time)
backend_->AddDirectory(mount_point);
else {
// This is a bit of a hack. The device might not be mounted at the same
// path each time, so if it's different we have to munge all the paths in
// the database to fix it. This can be done entirely in sqlite so it's
// relatively fast...
// Get the directory it was mounted at last time. Devices only have one
// directory (the root).
Directory dir = backend_->GetAllDirectories()[0];
if (dir.path != mount_point) {
// The directory is different, commence the munging.
qDebug() << "Changing path from" << dir.path << "to" << mount_point;
backend_->ChangeDirPath(dir.id, mount_point);
}
// Load the directory properly now
backend_->LoadDirectoriesAsync();
}
}

View File

@ -39,6 +39,10 @@ public:
signals:
void TaskStarted(int id);
void Error(const QString& message);
protected:
void InitBackendDirectory(const QString& mount_point, bool first_time);
protected:
DeviceLister* lister_;

View File

@ -14,11 +14,16 @@
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "devicekitlister.h"
#include "filesystemdevice.h"
#include "dbus/udisks.h"
#include "dbus/udisksdevice.h"
#ifdef HAVE_LIBGPOD
# include "gpoddevice.h"
#endif
#include <QtDebug>
DeviceKitLister::DeviceKitLister()
@ -66,6 +71,13 @@ void DeviceKitLister::Init() {
device_data[data.unique_id()] = data;
}
DeviceData ipod;
ipod.device_mount_paths << QDir::homePath() + "/.gvfs/iPod touch";
ipod.device_presentation_name = "iPod Touch";
ipod.suitable = true;
ipod.drive_serial = "ipod";
device_data[ipod.unique_id()] = ipod;
// Update the internal cache
{
QMutexLocker l(&mutex_);
@ -227,7 +239,20 @@ QString DeviceKitLister::FindUniqueIdByPath(const QDBusObjectPath &path) const {
boost::shared_ptr<ConnectedDevice> DeviceKitLister::Connect(
const QString &unique_id, DeviceManager* manager, int database_id,
bool first_time) {
return boost::shared_ptr<ConnectedDevice>(new FilesystemDevice(
LockAndGetDeviceInfo(unique_id, &DeviceData::device_mount_paths)[0],
this, unique_id, manager, database_id, first_time));
QString mount_point = LockAndGetDeviceInfo(
unique_id, &DeviceData::device_mount_paths)[0];
boost::shared_ptr<ConnectedDevice> ret;
#ifdef HAVE_LIBGPOD
if (QFile::exists(mount_point + "/iTunes_Control")) {
ret.reset(new GPodDevice(
mount_point, this, unique_id, manager, database_id, first_time));
return ret;
}
#endif
ret.reset(new FilesystemDevice(
mount_point, this, unique_id, manager, database_id, first_time));
return ret;
}

View File

@ -14,6 +14,7 @@
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "connecteddevice.h"
#include "devicedatabasebackend.h"
#include "devicemanager.h"
@ -273,6 +274,7 @@ boost::shared_ptr<ConnectedDevice> DeviceManager::Connect(int row) {
info.device_ = info.lister_->Connect(
info.unique_id_, this, info.database_id_, first_time);
connect(info.device_.get(), SIGNAL(TaskStarted(int)), SLOT(DeviceTaskStarted(int)));
connect(info.device_.get(), SIGNAL(Error(QString)), SIGNAL(Error(QString)));
return info.device_;
}

View File

@ -81,6 +81,7 @@ public:
signals:
void DeviceDisconnected(int row);
void Error(const QString& message);
private slots:
void PhysicalDeviceAdded(const QString& id);

View File

@ -57,26 +57,7 @@ FilesystemDevice::FilesystemDevice(
backend_, SLOT(UpdateCompilations()));
connect(watcher, SIGNAL(ScanStarted(int)), SIGNAL(TaskStarted(int)));
if (first_time)
backend_->AddDirectory(mount_point);
else {
// This is a bit of a hack. The device might not be mounted at the same
// path each time, so if it's different we have to munge all the paths in
// the database to fix it. This can be done entirely in sqlite so it's
// relatively fast...
// Get the directory it was mounted at last time. Devices only have one
// directory (the root).
Directory dir = backend_->GetAllDirectories()[0];
if (dir.path != mount_point) {
// The directory is different, commence the munging.
qDebug() << "Changing path from" << dir.path << "to" << mount_point;
backend_->ChangeDirPath(dir.id, mount_point);
}
// Load the directory properly now, this signals the watcher as well.
backend_->LoadDirectoriesAsync();
}
InitBackendDirectory(mount_point, first_time);
model_->Init();
}

View File

@ -0,0 +1,48 @@
/* 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 "gpoddevice.h"
#include "gpodloader.h"
#include "library/librarymodel.h"
#include <QtDebug>
#include <gpod/itdb.h>
GPodDevice::GPodDevice(
const QString& mount_point, DeviceLister* lister, const QString& unique_id,
DeviceManager* manager, int database_id, bool first_time)
: ConnectedDevice(lister, unique_id, manager, database_id),
loader_thread_(new QThread(this)),
loader_(new GPodLoader(mount_point, manager->task_manager(), backend_))
{
InitBackendDirectory(mount_point, first_time);
model_->Init();
loader_->moveToThread(loader_thread_);
connect(loader_, SIGNAL(Error(QString)), SIGNAL(Error(QString)));
connect(loader_, SIGNAL(TaskStarted(int)), SIGNAL(TaskStarted(int)));
connect(loader_thread_, SIGNAL(started()), loader_, SLOT(LoadDatabase()));
loader_thread_->start();
// TODO: loader cleanup
}
GPodDevice::~GPodDevice() {
}

38
src/devices/gpoddevice.h Normal file
View File

@ -0,0 +1,38 @@
/* 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 GPODDEVICE_H
#define GPODDEVICE_H
#include "connecteddevice.h"
class GPodLoader;
class GPodDevice : public ConnectedDevice {
Q_OBJECT
public:
GPodDevice(const QString& mount_point, DeviceLister* lister,
const QString& unique_id, DeviceManager* manager,
int database_id, bool first_time);
~GPodDevice();
private:
QThread* loader_thread_;
GPodLoader* loader_;
};
#endif // GPODDEVICE_H

View File

@ -0,0 +1,73 @@
/* 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 "gpodloader.h"
#include "core/song.h"
#include "core/taskmanager.h"
#include "library/librarybackend.h"
#include <gpod/itdb.h>
#include <QtDebug>
GPodLoader::GPodLoader(const QString& mount_point, TaskManager* task_manager,
LibraryBackend* backend, QObject *parent)
: QObject(parent),
mount_point_(mount_point),
task_manager_(task_manager),
backend_(backend)
{
}
void GPodLoader::LoadDatabase() {
int task_id = task_manager_->StartTask(tr("Loading iPod database"));
emit TaskStarted(task_id);
// Load the iTunes database
GError* error = NULL;
Itdb_iTunesDB* db = itdb_parse(mount_point_.toLocal8Bit(), &error);
// Check for errors
if (!db) {
qDebug() << "GPodLoader error:" << error->message;
emit Error(QString::fromUtf8(error->message));
g_error_free(error);
task_manager_->SetTaskFinished(task_id);
return;
}
// Convert all the tracks from libgpod structs into Song classes
SongList songs;
for (GList* tracks = db->tracks ; tracks != NULL ; tracks = tracks->next) {
Itdb_Track* track = static_cast<Itdb_Track*>(tracks->data);
Song song;
song.InitFromItdb(track);
song.set_directory_id(1);
song.set_filename(mount_point_ + song.filename());
songs << song;
}
// 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);
// Cleanup
itdb_free(db);
task_manager_->SetTaskFinished(task_id);
}

47
src/devices/gpodloader.h Normal file
View File

@ -0,0 +1,47 @@
/* 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 GPODLOADER_H
#define GPODLOADER_H
#include <QObject>
#include <boost/shared_ptr.hpp>
class LibraryBackend;
class TaskManager;
class GPodLoader : public QObject {
Q_OBJECT
public:
GPodLoader(const QString& mount_point, TaskManager* task_manager,
LibraryBackend* backend, QObject* parent = 0);
public slots:
void LoadDatabase();
signals:
void Error(const QString& message);
void TaskStarted(int task_id);
private:
QString mount_point_;
TaskManager* task_manager_;
LibraryBackend* backend_;
};
#endif // GPODLOADER_H

View File

@ -886,6 +886,9 @@ msgstr ""
msgid "Loading Last.fm radio"
msgstr ""
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr ""

View File

@ -890,6 +890,9 @@ msgstr ""
msgid "Loading Last.fm radio"
msgstr "Načítám rádio Last.fm"
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr "Načítám kanál"

View File

@ -891,6 +891,9 @@ msgstr ""
msgid "Loading Last.fm radio"
msgstr "Indlæser Last.fm-radio"
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr "Indlæser stream"

View File

@ -893,6 +893,9 @@ msgstr "Wiedergabeliste laden..."
msgid "Loading Last.fm radio"
msgstr "Last.fm Radio wird geladen"
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr "Lade Stream"

View File

@ -899,6 +899,9 @@ msgstr "Φόρτωση λίστας αναπαραγωγής..."
msgid "Loading Last.fm radio"
msgstr "Φόρτωμα Last.fm"
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr "Φόρτωμα ροής (stream)"

View File

@ -890,6 +890,9 @@ msgstr "Load playlist..."
msgid "Loading Last.fm radio"
msgstr "Loading Last.fm radio"
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr "Loading stream"

View File

@ -888,6 +888,9 @@ msgstr ""
msgid "Loading Last.fm radio"
msgstr "Loading Last.fm radio"
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr "Loading stream"

View File

@ -897,6 +897,9 @@ msgstr "Cargar lista de reproducción"
msgid "Loading Last.fm radio"
msgstr "Cargando radio de Last.fm"
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr "Cargando flujo"

View File

@ -887,6 +887,9 @@ msgstr "Lataa soittolista..."
msgid "Loading Last.fm radio"
msgstr ""
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr ""

View File

@ -894,6 +894,9 @@ msgstr ""
msgid "Loading Last.fm radio"
msgstr "Chargement de la radio Last.fm"
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr "Chargement du flux"

View File

@ -888,6 +888,9 @@ msgstr ""
msgid "Loading Last.fm radio"
msgstr "Carregando a rádio da Last.fm"
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr "A carregar a stream"

View File

@ -895,6 +895,9 @@ msgstr "Carica la scaletta..."
msgid "Loading Last.fm radio"
msgstr "Caricamento radio Last.fm"
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr "Caricamento flusso"

View File

@ -888,6 +888,9 @@ msgstr ""
msgid "Loading Last.fm radio"
msgstr ""
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr ""

View File

@ -889,6 +889,9 @@ msgstr ""
msgid "Loading Last.fm radio"
msgstr "Laster inn Last.fm radio"
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr "Lader lydstrøm"

View File

@ -886,6 +886,9 @@ msgstr ""
msgid "Loading Last.fm radio"
msgstr "Cargament de la ràdio Last.fm"
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr "Cargament del flux"

View File

@ -888,6 +888,9 @@ msgstr ""
msgid "Loading Last.fm radio"
msgstr "Ładowanie radia Last.fm"
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr "Ładowanie strumienia"

View File

@ -896,6 +896,9 @@ msgstr "Carregar lista..."
msgid "Loading Last.fm radio"
msgstr "Carregando a rádio Last.fm"
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr "A carregar emissão"

View File

@ -896,6 +896,9 @@ msgstr "Carregar lista de reprodução..."
msgid "Loading Last.fm radio"
msgstr "Carregando rádio Last.fm"
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr "Carregando transmissão"

View File

@ -887,6 +887,9 @@ msgstr ""
msgid "Loading Last.fm radio"
msgstr "Se încarcă radio Last.fm"
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr "Se încarcă fluxul"

View File

@ -890,6 +890,9 @@ msgstr "Загрузить список воспроизведения..."
msgid "Loading Last.fm radio"
msgstr "Загрузка радио Last.fm"
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr "Загрузка потока"

View File

@ -896,6 +896,9 @@ msgstr "Načítať playlist..."
msgid "Loading Last.fm radio"
msgstr "Načítava sa Last.fm rádio"
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr "Načítava sa stream"

View File

@ -891,6 +891,9 @@ msgstr "Läs in spellista..."
msgid "Loading Last.fm radio"
msgstr "Laddar Last.fm radio"
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr "Laddar ström"

View File

@ -886,6 +886,9 @@ msgstr ""
msgid "Loading Last.fm radio"
msgstr ""
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr ""

View File

@ -877,6 +877,9 @@ msgstr ""
msgid "Loading Last.fm radio"
msgstr ""
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr ""

View File

@ -895,6 +895,9 @@ msgstr "Завантажити список відтворення..."
msgid "Loading Last.fm radio"
msgstr "Завантаження радіо Last.fm"
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr "Завнтаження потоку"

View File

@ -886,6 +886,9 @@ msgstr ""
msgid "Loading Last.fm radio"
msgstr ""
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr ""

View File

@ -886,6 +886,9 @@ msgstr "載入播放清單..."
msgid "Loading Last.fm radio"
msgstr ""
msgid "Loading iPod database"
msgstr ""
msgid "Loading stream"
msgstr ""

View File

@ -356,6 +356,9 @@ MainWindow::MainWindow(NetworkAccessManager* network, Engine::Type engine, QWidg
connect(task_manager_, SIGNAL(PauseLibraryWatchers()), library_, SLOT(PauseWatcher()));
connect(task_manager_, SIGNAL(ResumeLibraryWatchers()), library_, SLOT(ResumeWatcher()));
// Devices connections
connect(devices_, SIGNAL(Error(QString)), error_dialog_.get(), SLOT(ShowMessage(QString)));
// Library filter widget
QAction* library_config_action = new QAction(
IconLoader::Load("configure"), tr("Configure library..."), this);