From 2bda4966fce2411ca9bf3d80aef9d08a2e58b998 Mon Sep 17 00:00:00 2001 From: John Maguire Date: Thu, 28 Jul 2011 17:33:58 +0200 Subject: [PATCH 1/7] Basic working use of resolvers for XSPF playlists. --- src/CMakeLists.txt | 2 ++ src/playlistparsers/parserbase.cpp | 4 +++ src/playlistparsers/xspfparser.cpp | 6 ++-- src/resolvers/libraryresolver.cpp | 1 + src/resolvers/songresolver.cpp | 51 ++++++++++++++++++++++++++++++ src/resolvers/songresolver.h | 37 ++++++++++++++++++++++ 6 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 src/resolvers/songresolver.cpp create mode 100644 src/resolvers/songresolver.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d2431e875..dd5e4127a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -191,6 +191,7 @@ set(SOURCES playlistparsers/xspfparser.cpp resolvers/libraryresolver.cpp + resolvers/songresolver.cpp smartplaylists/generator.cpp smartplaylists/generatorinserter.cpp @@ -402,6 +403,7 @@ set(HEADERS resolvers/libraryresolver.h resolvers/resolver.h + resolvers/songresolver.h smartplaylists/generator.h smartplaylists/generatorinserter.h 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..eea1ca3c7 --- /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 other 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 From 9424037fe44b4a4f3320d4e2b48ecf7bc005594e Mon Sep 17 00:00:00 2001 From: John Maguire Date: Thu, 28 Jul 2011 18:01:32 +0200 Subject: [PATCH 2/7] Fix typo in comment. --- src/resolvers/songresolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resolvers/songresolver.cpp b/src/resolvers/songresolver.cpp index eea1ca3c7..2b68bd3a1 100644 --- a/src/resolvers/songresolver.cpp +++ b/src/resolvers/songresolver.cpp @@ -12,7 +12,7 @@ SongResolver::SongResolver(LibraryBackendInterface* library, QObject* parent) song_(NULL), resolvers_finished_(0), resolved_(false) { - // Register in the other they should be checked. + // Register in the order they should be checked. RegisterResolver(new LibraryResolver(library)); RegisterResolver(new SpotifyResolver(InternetModel::Service()->server())); } From c307d2e87ae744997dd7322503d3d99098196881 Mon Sep 17 00:00:00 2001 From: John Maguire Date: Tue, 9 Aug 2011 20:18:43 +0200 Subject: [PATCH 3/7] Add proper disc icon for non-Linux. --- data/data.qrc | 1 + data/icons/48x48/media-optical.png | Bin 0 -> 4863 bytes 2 files changed, 1 insertion(+) create mode 100644 data/icons/48x48/media-optical.png 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 0000000000000000000000000000000000000000..e4cb7aab94c31875d8969c04befde5729fc75b6a GIT binary patch literal 4863 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 literal 0 HcmV?d00001 From 176efeb4b06db3d4fe4fefab55f24ad9492f8cd6 Mon Sep 17 00:00:00 2001 From: John Maguire Date: Tue, 9 Aug 2011 20:20:45 +0200 Subject: [PATCH 4/7] Add debug logging to cdda lister. --- src/devices/cddalister.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/devices/cddalister.cpp b/src/devices/cddalister.cpp index 53e485cd1..25faa4b26 100644 --- a/src/devices/cddalister.cpp +++ b/src/devices/cddalister.cpp @@ -92,8 +92,15 @@ 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) { From 83a21f05765ccf1632bc36460cfaec04c76c34a0 Mon Sep 17 00:00:00 2001 From: John Maguire Date: Tue, 9 Aug 2011 21:14:40 +0200 Subject: [PATCH 5/7] Add gstreamer cdio plugin to mac bundle. --- dist/macdeploy.py | 3 +++ 1 file changed, 3 insertions(+) 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=[ From 6d9bc9d3f4fea90226d76864d64e848df340450c Mon Sep 17 00:00:00 2001 From: John Maguire Date: Tue, 9 Aug 2011 21:30:28 +0200 Subject: [PATCH 6/7] Don't constantly refresh the cd device if it doesn't support checking for media changes. Filter out irrelevant devices on OS X. --- src/devices/cddadevice.cpp | 6 +++--- src/devices/cddalister.cpp | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/devices/cddadevice.cpp b/src/devices/cddadevice.cpp index e60ef2e4e..e4d56f199 100644 --- a/src/devices/cddadevice.cpp +++ b/src/devices/cddadevice.cpp @@ -119,7 +119,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)); @@ -154,8 +154,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 25faa4b26..179dcc303 100644 --- a/src/devices/cddalister.cpp +++ b/src/devices/cddalister.cpp @@ -106,7 +106,15 @@ void CddaLister::Init() { 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); } From 6ddf9fa41b47cbe8c87a5ab7fd427858125321dc Mon Sep 17 00:00:00 2001 From: John Maguire Date: Tue, 9 Aug 2011 21:54:37 +0200 Subject: [PATCH 7/7] Only parse the tracks from the first release we find on musicbrainz for a CD. --- src/musicbrainz/musicbrainzclient.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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; } }