diff --git a/data/data.qrc b/data/data.qrc index 11f36f9c9..06c998d76 100644 --- a/data/data.qrc +++ b/data/data.qrc @@ -157,6 +157,7 @@ icons/48x48/help-hint.png icons/48x48/list-add.png icons/48x48/list-remove.png + icons/48x48/media-optical.png icons/48x48/media-playback-pause.png icons/48x48/media-playback-start.png icons/48x48/media-playback-stop.png diff --git a/data/icons/48x48/media-optical.png b/data/icons/48x48/media-optical.png new file mode 100644 index 000000000..e4cb7aab9 Binary files /dev/null and b/data/icons/48x48/media-optical.png differ diff --git a/dist/macdeploy.py b/dist/macdeploy.py index a90e9ae6b..73ac40c0d 100755 --- a/dist/macdeploy.py +++ b/dist/macdeploy.py @@ -77,6 +77,9 @@ GSTREAMER_PLUGINS=[ # Fingerprinting support 'libgstofa.so', + + # CD support + 'libgstcdio.so', ] GSTREAMER_SEARCH_PATH=[ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d8e11d7f6..8b1e920ca 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -193,6 +193,7 @@ set(SOURCES playlistparsers/xspfparser.cpp resolvers/libraryresolver.cpp + resolvers/songresolver.cpp smartplaylists/generator.cpp smartplaylists/generatorinserter.cpp @@ -406,6 +407,7 @@ set(HEADERS resolvers/libraryresolver.h resolvers/resolver.h + resolvers/songresolver.h smartplaylists/generator.h smartplaylists/generatorinserter.h diff --git a/src/devices/cddadevice.cpp b/src/devices/cddadevice.cpp index 736647494..cddfdec26 100644 --- a/src/devices/cddadevice.cpp +++ b/src/devices/cddadevice.cpp @@ -121,7 +121,7 @@ void CddaDevice::Init() { musicbrainz_client->StartDiscIdRequest(musicbrainz_discid); g_free(string_mb); } - + // Clean all the Gstreamer objects we have used: we don't need them anymore gst_element_set_state (pipe, GST_STATE_NULL); gst_object_unref(GST_OBJECT(pipe)); @@ -156,8 +156,8 @@ void CddaDevice::AudioCDTagsLoaded(const QString& artist, const QString& album, } void CddaDevice::Refresh() { - if ((cdio_ && cdda_) && /* already init... */ - !cdio_get_media_changed(cdio_) /* ...and hasn't change since last time */) { + if ((cdio_ && cdda_) && /* already init... */ + cdio_get_media_changed(cdio_) != 1 /* ...and hasn't change since last time */) { return; } // Check if mutex is already token (i.e. init is already taking place) diff --git a/src/devices/cddalister.cpp b/src/devices/cddalister.cpp index 53e485cd1..179dcc303 100644 --- a/src/devices/cddalister.cpp +++ b/src/devices/cddalister.cpp @@ -92,14 +92,29 @@ void CddaLister::UpdateDeviceFreeSpace(const QString&) { } void CddaLister::Init() { + cdio_init(); +#ifdef Q_OS_DARWIN + if (!cdio_have_driver(DRIVER_OSX)) { + qLog(Error) << "libcdio was compiled without support for OS X!"; + } +#endif char **devices = cdio_get_devices(DRIVER_DEVICE); if (!devices) { + qLog(Debug) << "No CD devices found"; return; } for (; *devices != NULL; ++devices) { if (strcmp("/dev/cdrom", *devices) == 0) continue; + QString device(*devices); +#ifdef Q_OS_DARWIN + // Every track is detected as a separate device on Darwin. The raw disk looks + // like /dev/rdisk1 + if (!device.contains(QRegExp("^/dev/rdisk[0-9]$"))) { + continue; + } +#endif devices_list_ << device; emit DeviceAdded(device); } diff --git a/src/musicbrainz/musicbrainzclient.cpp b/src/musicbrainz/musicbrainzclient.cpp index 2d1401d2e..d8a847b67 100644 --- a/src/musicbrainz/musicbrainzclient.cpp +++ b/src/musicbrainz/musicbrainzclient.cpp @@ -91,7 +91,7 @@ void MusicBrainzClient::DiscIdRequestFinished() { ResultList ret; QString artist; QString album; - + if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() != 200) { emit Finished(artist, album, ret); return; @@ -117,11 +117,14 @@ void MusicBrainzClient::DiscIdRequestFinished() { } while (!reader.atEnd()) { - if (reader.readNext() == QXmlStreamReader::StartElement && reader.name() == "track") { + QXmlStreamReader::TokenType token = reader.readNext(); + if (token == QXmlStreamReader::StartElement && reader.name() == "track") { Result track = ParseTrack(&reader); if (!track.title_.isEmpty()) { ret << track; } + } else if (token == QXmlStreamReader::EndElement && reader.name() == "track-list") { + break; } } diff --git a/src/playlistparsers/parserbase.cpp b/src/playlistparsers/parserbase.cpp index 613e6be57..49c9b20e5 100644 --- a/src/playlistparsers/parserbase.cpp +++ b/src/playlistparsers/parserbase.cpp @@ -19,6 +19,7 @@ #include "library/librarybackend.h" #include "library/libraryquery.h" #include "library/sqlrow.h" +#include "resolvers/songresolver.h" #include @@ -31,6 +32,9 @@ ParserBase::ParserBase(LibraryBackendInterface* library, QObject *parent) void ParserBase::LoadSong(const QString& filename_or_url, qint64 beginning, const QDir& dir, Song* song) const { if (filename_or_url.isEmpty()) { + // Try and resolve from various sources. + SongResolver resolver(library_); + resolver.ResolveSong(song); return; } diff --git a/src/playlistparsers/xspfparser.cpp b/src/playlistparsers/xspfparser.cpp index 468fff829..660be3e3e 100644 --- a/src/playlistparsers/xspfparser.cpp +++ b/src/playlistparsers/xspfparser.cpp @@ -90,13 +90,13 @@ Song XSPFParser::ParseTrack(QXmlStreamReader* reader, const QDir& dir) const { } return_song: - Song song = LoadSong(location, 0, dir); - - // Override metadata with what was in the playlist + Song song; song.set_title(title); song.set_artist(artist); song.set_album(album); song.set_length_nanosec(nanosec); + LoadSong(location, 0, dir, &song); + return song; } diff --git a/src/resolvers/libraryresolver.cpp b/src/resolvers/libraryresolver.cpp index fa8d2bedb..6269fb590 100644 --- a/src/resolvers/libraryresolver.cpp +++ b/src/resolvers/libraryresolver.cpp @@ -22,6 +22,7 @@ int LibraryResolver::ResolveSong(const Song& song) { LibraryQuery* query = new LibraryQuery; query->AddWhere("artist", song.artist()); query->AddWhere("title", song.title()); + query->SetColumnSpec("%songs_table.ROWID, " + Song::kColumnSpec); QFuture future = QtConcurrent::run( backend_, &LibraryBackendInterface::ExecQuery, query); diff --git a/src/resolvers/songresolver.cpp b/src/resolvers/songresolver.cpp new file mode 100644 index 000000000..2b68bd3a1 --- /dev/null +++ b/src/resolvers/songresolver.cpp @@ -0,0 +1,51 @@ +#include "songresolver.h" + +#include "core/logging.h" +#include "core/song.h" +#include "internet/internetmodel.h" +#include "internet/spotifyservice.h" +#include "libraryresolver.h" +#include "spotifyresolver.h" + +SongResolver::SongResolver(LibraryBackendInterface* library, QObject* parent) + : QObject(parent), + song_(NULL), + resolvers_finished_(0), + resolved_(false) { + // Register in the order they should be checked. + RegisterResolver(new LibraryResolver(library)); + RegisterResolver(new SpotifyResolver(InternetModel::Service()->server())); +} + +SongResolver::~SongResolver() { + qDeleteAll(resolvers_); + resolvers_.clear(); +} + +void SongResolver::RegisterResolver(Resolver* resolver) { + resolvers_ << resolver; + connect(resolver, SIGNAL(ResolveFinished(int, SongList)), SLOT(ResolveFinished(int, SongList))); +} + +bool SongResolver::ResolveSong(Song* song) { + song_ = song; + foreach (Resolver* resolver, resolvers_) { + resolver->ResolveSong(*song); + } + loop_.exec(); + return resolved_; +} + +void SongResolver::ResolveFinished(int, SongList resolved_songs) { + ++resolvers_finished_; + if (resolvers_finished_ == resolvers_.size()) { + loop_.quit(); + } + + if (!resolved_songs.isEmpty()) { + *song_ = resolved_songs.first(); + qLog(Debug) << "Resolved song:" << song_->title() << "from:" << sender()->metaObject()->className(); + resolved_ = true; + loop_.quit(); + } +} diff --git a/src/resolvers/songresolver.h b/src/resolvers/songresolver.h new file mode 100644 index 000000000..dbecf4185 --- /dev/null +++ b/src/resolvers/songresolver.h @@ -0,0 +1,37 @@ +#ifndef SONGRESOLVER_H +#define SONGRESOLVER_H + +#include +#include +#include + +#include "core/song.h" + +class LibraryBackendInterface; +class Resolver; + +class SongResolver : public QObject { + Q_OBJECT + public: + SongResolver(LibraryBackendInterface* library, QObject* parent = 0); + virtual ~SongResolver(); + + // Blocking + bool ResolveSong(Song* song); + + private slots: + void ResolveFinished(int, SongList resolved_songs); + + private: + void RegisterResolver(Resolver* resolver); + + QList resolvers_; + Song* song_; + + QEventLoop loop_; + + int resolvers_finished_; + bool resolved_; +}; + +#endif