diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 25a27e301..f7faf2511 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1034,10 +1034,12 @@ optional_source(HAVE_AUDIOCD SOURCES devices/cddadevice.cpp devices/cddalister.cpp + devices/cddasongloader.cpp ui/ripcd.cpp HEADERS devices/cddadevice.h devices/cddalister.h + devices/cddasongloader.h ui/ripcd.h UI ui/ripcd.ui diff --git a/src/core/songloader.cpp b/src/core/songloader.cpp index 040b51eed..440944f9a 100644 --- a/src/core/songloader.cpp +++ b/src/core/songloader.cpp @@ -37,6 +37,7 @@ #include "core/song.h" #include "core/tagreaderclient.h" #include "core/timeconstants.h" +#include "devices/cddasongloader.h" #include "internet/fixlastfm.h" #include "internet/internetmodel.h" #include "library/librarybackend.h" @@ -136,113 +137,32 @@ SongLoader::Result SongLoader::LoadLocalPartial(const QString& filename) { } SongLoader::Result SongLoader::LoadAudioCD() { -//#ifdef HAVE_AUDIOCD -// // Create gstreamer cdda element -// GstElement* cdda = gst_element_make_from_uri( -// GST_URI_SRC, "cdda://", nullptr, nullptr); -// if (cdda == nullptr) { -// qLog(Error) << "Error while creating CDDA GstElement"; -// return Error; -// } -// -// // Change the element's state to ready and paused, to be able to query it -// if (gst_element_set_state(cdda, GST_STATE_READY) == -// GST_STATE_CHANGE_FAILURE || -// gst_element_set_state(cdda, GST_STATE_PAUSED) == -// GST_STATE_CHANGE_FAILURE) { -// qLog(Error) << "Error while changing CDDA GstElement's state"; -// gst_element_set_state(cdda, GST_STATE_NULL); -// gst_object_unref(GST_OBJECT(cdda)); -// return Error; -// } -// -// // Get number of tracks -// GstFormat fmt = gst_format_get_by_nick("track"); -// gint64 num_tracks = 0; -// if (!gst_element_query_duration(cdda, fmt, &num_tracks)) { -// qLog(Error) << "Error while querying cdda GstElement"; -// gst_object_unref(GST_OBJECT(cdda)); -// return Error; -// } -// -// for (int track_number = 1; track_number <= num_tracks; track_number++) { -// // Init song -// Song song; -// guint64 duration = 0; -// // quint64 == ulonglong and guint64 == ulong, therefore we must cast -// if (gst_tag_list_get_uint64( -// GST_CDDA_BASE_SRC(cdda)->tracks[track_number - 1].tags, -// GST_TAG_DURATION, &duration)) { -// song.set_length_nanosec((quint64)duration); -// } -// song.set_valid(true); -// song.set_filetype(Song::Type_Cdda); -// song.set_url(QUrl(QString("cdda://%1").arg(track_number))); -// song.set_title(QString("Track %1").arg(track_number)); -// song.set_track(track_number); -// songs_ << song; -// } -// -// // Generate MusicBrainz DiscId -// gst_tag_register_musicbrainz_tags(); -// GstElement* pipe = gst_pipeline_new("pipeline"); -// gst_bin_add(GST_BIN(pipe), cdda); -// gst_element_set_state(pipe, GST_STATE_READY); -// gst_element_set_state(pipe, GST_STATE_PAUSED); -// GstMessage* msg = gst_bus_timed_pop_filtered( -// GST_ELEMENT_BUS(pipe), GST_CLOCK_TIME_NONE, GST_MESSAGE_TAG); -// GstTagList* tags = nullptr; -// gst_message_parse_tag(msg, &tags); -// char* string_mb = nullptr; -// if (gst_tag_list_get_string(tags, GST_TAG_CDDA_MUSICBRAINZ_DISCID, -// &string_mb)) { -// QString musicbrainz_discid(string_mb); -// qLog(Info) << "MusicBrainz discid: " << musicbrainz_discid; -// -// MusicBrainzClient* musicbrainz_client = new MusicBrainzClient(this); -// connect(musicbrainz_client, SIGNAL(Finished(const QString&, const QString&, -// MusicBrainzClient::ResultList)), -// SLOT(AudioCDTagsLoaded(const QString&, const QString&, -// MusicBrainzClient::ResultList))); -// musicbrainz_client->StartDiscIdRequest(musicbrainz_discid); -// g_free(string_mb); -// } -// -// // Clean all the Gstreamer objects we have used: we don't need them anymore -// gst_object_unref(GST_OBJECT(cdda)); -// gst_element_set_state(pipe, GST_STATE_NULL); -// gst_object_unref(GST_OBJECT(pipe)); -// gst_object_unref(GST_OBJECT(msg)); -// gst_object_unref(GST_OBJECT(tags)); -// -// return Success; -//#else // HAVE_AUDIOCD +#ifdef HAVE_AUDIOCD + CddaSongLoader* cdda_song_loader = new CddaSongLoader; + connect(cdda_song_loader, SIGNAL(SongsDurationLoaded(SongList)), + this, SLOT(AudioCDTracksLoadedSlot(SongList))); + connect(cdda_song_loader, SIGNAL(SongsMetadataLoaded(SongList)), + this, SLOT(AudioCDTracksTagsLoaded(SongList))); + cdda_song_loader->LoadSongs(); + return Success; +#else // HAVE_AUDIOCD return Error; -//#endif +#endif } -void SongLoader::AudioCDTagsLoaded( - const QString& artist, const QString& album, - const MusicBrainzClient::ResultList& results) { - // Remove previously added songs metadata, because there are not needed - // and that we are going to fill it with new (more complete) ones - songs_.clear(); - int track_number = 1; - for (const MusicBrainzClient::Result& ret : results) { - Song song; - song.set_artist(artist); - song.set_album(album); - song.set_title(ret.title_); - song.set_length_nanosec(ret.duration_msec_ * kNsecPerMsec); - song.set_track(track_number); - song.set_year(ret.year_); - // We need to set url: that's how playlist will find the correct item to - // update - song.set_url(QUrl(QString("cdda://%1").arg(track_number++))); - songs_ << song; - } +#ifdef HAVE_AUDIOCD +void SongLoader::AudioCDTracksLoadedSlot(const SongList& songs) { + songs_ = songs; + emit AudioCDTracksLoaded(); +} + +void SongLoader::AudioCDTracksTagsLoaded(const SongList& songs) { + CddaSongLoader* cdda_song_loader = qobject_cast(sender()); + cdda_song_loader->deleteLater(); + songs_ = songs; emit LoadAudioCDFinished(true); } +#endif // HAVE_AUDIOCD SongLoader::Result SongLoader::LoadLocal(const QString& filename) { qLog(Debug) << "Loading local file" << filename; diff --git a/src/core/songloader.h b/src/core/songloader.h index 24889e12f..5a8ab95fa 100644 --- a/src/core/songloader.h +++ b/src/core/songloader.h @@ -37,6 +37,7 @@ class ParserBase; class Player; class PlaylistParser; class PodcastParser; +class CddaSongLoader; class SongLoader : public QObject { Q_OBJECT @@ -73,14 +74,15 @@ class SongLoader : public QObject { Result LoadAudioCD(); signals: + void AudioCDTracksLoaded(); void LoadAudioCDFinished(bool success); void LoadRemoteFinished(); private slots: void Timeout(); void StopTypefind(); - void AudioCDTagsLoaded(const QString& artist, const QString& album, - const MusicBrainzClient::ResultList& results); + void AudioCDTracksLoadedSlot(const SongList& songs); + void AudioCDTracksTagsLoaded(const SongList& songs); private: enum State { WaitingForType, WaitingForMagic, WaitingForData, Finished, }; diff --git a/src/devices/cddadevice.cpp b/src/devices/cddadevice.cpp index 011c78599..91bd77a5d 100644 --- a/src/devices/cddadevice.cpp +++ b/src/devices/cddadevice.cpp @@ -16,12 +16,7 @@ */ #include -#include -#include -#include -#include "core/logging.h" -#include "core/timeconstants.h" #include "library/librarybackend.h" #include "library/librarymodel.h" @@ -32,200 +27,33 @@ CddaDevice::CddaDevice(const QUrl& url, DeviceLister* lister, Application* app, int database_id, bool first_time) : ConnectedDevice(url, lister, unique_id, manager, app, database_id, first_time), - cdda_(nullptr), - cdio_(nullptr) {} - -CddaDevice::~CddaDevice() { - if (cdio_) cdio_destroy(cdio_); + cdda_song_loader_(url) { + connect(&cdda_song_loader_, SIGNAL(SongsLoaded(SongList)), + this, SLOT(SongsLoaded(SongList))); + connect(&cdda_song_loader_, SIGNAL(SongsDurationLoaded(SongList)), + this, SLOT(SongsLoaded(SongList))); + connect(&cdda_song_loader_, SIGNAL(SongsMetadataLoaded(SongList)), + this, SLOT(SongsLoaded(SongList))); + connect(this, SIGNAL(SongsDiscovered(SongList)), + model_, SLOT(SongsDiscovered(SongList))); } +CddaDevice::~CddaDevice() {} + void CddaDevice::Init() { - QMutexLocker locker(&mutex_init_); song_count_ = 0; // Reset song count, in case it was already set - cdio_ = cdio_open(url_.path().toLocal8Bit().constData(), DRIVER_DEVICE); - if (cdio_ == nullptr) { - return; - } - // Create gstreamer cdda element - GError* error = nullptr; - cdda_ = gst_element_make_from_uri(GST_URI_SRC, "cdda://", nullptr, &error); - if (error) { - qLog(Error) << error->code << error->message; - } - if (cdda_ == nullptr) { - model_->Reset(); - return; - } - - g_object_set(cdda_, "device", g_strdup(url_.path().toLocal8Bit().constData()), - nullptr); - if (g_object_class_find_property (G_OBJECT_GET_CLASS (cdda_), "paranoia-mode")) { - g_object_set (cdda_, "paranoia-mode", 0, NULL); - } - - // Change the element's state to ready and paused, to be able to query it - if (gst_element_set_state(cdda_, GST_STATE_READY) == - GST_STATE_CHANGE_FAILURE || - gst_element_set_state(cdda_, GST_STATE_PAUSED) == - GST_STATE_CHANGE_FAILURE) { - model_->Reset(); - gst_element_set_state(cdda_, GST_STATE_NULL); - gst_object_unref(GST_OBJECT(cdda_)); - return; - } - - // Get number of tracks - GstFormat fmt = gst_format_get_by_nick("track"); - GstFormat out_fmt = fmt; - gint64 num_tracks = 0; - if (!gst_element_query_duration(cdda_, out_fmt, &num_tracks) || - out_fmt != fmt) { - qLog(Error) << "Error while querying cdda GstElement"; - model_->Reset(); - gst_object_unref(GST_OBJECT(cdda_)); - return; - } - - SongList songs; - for (int track_number = 1; track_number <= num_tracks; track_number++) { - // Init song - Song song; - song.set_id(track_number); - song.set_valid(true); - song.set_filetype(Song::Type_Cdda); - song.set_url( - QUrl(QString("cdda://%1/%2").arg(url_.path()).arg(track_number))); - song.set_title(QString("Track %1").arg(track_number)); - song.set_track(track_number); - songs << song; - } - song_count_ = num_tracks; - connect(this, SIGNAL(SongsDiscovered(const SongList&)), model_, - SLOT(SongsDiscovered(const SongList&))); - // Reset the model before emitting the SongsDiscovered signal. This - // ensures that the model is updated properly even when a disc that - // doesn't exist in MusicBrainz is inserted. - model_->Reset(); - emit SongsDiscovered(songs); - - - gst_tag_register_musicbrainz_tags(); - - GstElement* pipeline = gst_pipeline_new("pipeline"); - GstElement* sink = gst_element_factory_make ("fakesink", NULL); - gst_bin_add_many (GST_BIN (pipeline), cdda_, sink, NULL); - gst_element_link (cdda_, sink); - gst_element_set_state(pipeline, GST_STATE_READY); - gst_element_set_state(pipeline, GST_STATE_PAUSED); - - // Get TOC and TAG messages - GstMessage* msg = nullptr; - GstMessage* msg_toc = nullptr; - GstMessage* msg_tag = nullptr; - while ((msg = gst_bus_timed_pop_filtered(GST_ELEMENT_BUS(pipeline), - GST_SECOND, (GstMessageType)(GST_MESSAGE_TOC | GST_MESSAGE_TAG)))) { - if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_TOC) { - if (msg_toc) gst_message_unref(msg_toc); // Shouldn't happen, but just in case - msg_toc = msg; - } else if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_TAG) { - if (msg_tag) gst_message_unref(msg_tag); - msg_tag = msg; - } - } - - // Handle TOC message: get tracks duration - if (msg_toc) { - GstToc* toc; - gst_message_parse_toc (msg_toc, &toc, nullptr); - if (toc) { - GList* entries = gst_toc_get_entries(toc); - if (entries && songs.size() <= g_list_length (entries)) { - int i = 0; - for (GList* node = entries; node != nullptr; node = node->next) { - GstTocEntry *entry = static_cast(node->data); - quint64 duration = 0; - gint64 start, stop; - if (gst_toc_entry_get_start_stop_times (entry, &start, &stop)) - duration = stop - start; - songs[i++].set_length_nanosec(duration); - } - } - } - gst_message_unref(msg_toc); - } - // Update songs with duration - model_->Reset(); - emit SongsDiscovered(songs); - song_count_ = songs.size(); - - // Handle TAG message: generate MusicBrainz DiscId - if (msg_tag) { - GstTagList* tags = nullptr; - gst_message_parse_tag(msg_tag, &tags); - char* string_mb = nullptr; - if (gst_tag_list_get_string(tags, GST_TAG_CDDA_MUSICBRAINZ_DISCID, - &string_mb)) { - QString musicbrainz_discid(string_mb); - qLog(Info) << "MusicBrainz discid: " << musicbrainz_discid; - - MusicBrainzClient* musicbrainz_client = new MusicBrainzClient(this); - connect(musicbrainz_client, SIGNAL(Finished(const QString&, const QString&, - MusicBrainzClient::ResultList)), - SLOT(AudioCDTagsLoaded(const QString&, const QString&, - MusicBrainzClient::ResultList))); - musicbrainz_client->StartDiscIdRequest(musicbrainz_discid); - g_free(string_mb); - gst_message_unref(msg_tag); - gst_tag_list_free(tags); - } - } - - gst_element_set_state(pipeline, GST_STATE_NULL); - // This will also cause cdda_ to be unref'd. - gst_object_unref(pipeline); -} - -void CddaDevice::AudioCDTagsLoaded( - const QString& artist, const QString& album, - const MusicBrainzClient::ResultList& results) { - MusicBrainzClient* musicbrainz_client = - qobject_cast(sender()); - musicbrainz_client->deleteLater(); - SongList songs; - int track_number = 1; - if (results.size() == 0) return; - model_->Reset(); - for (const MusicBrainzClient::Result& ret : results) { - Song song; - song.set_artist(artist); - song.set_album(album); - song.set_title(ret.title_); - song.set_length_nanosec(ret.duration_msec_ * kNsecPerMsec); - song.set_track(track_number); - song.set_year(ret.year_); - song.set_id(track_number); - // We need to set url: that's how playlist will find the correct item to - // update - song.set_url( - QUrl(QString("cdda://%1/%2").arg(unique_id()).arg(track_number++))); - songs << song; - } - connect(this, SIGNAL(SongsDiscovered(const SongList&)), model_, - SLOT(SongsDiscovered(const SongList&))); - emit SongsDiscovered(songs); - song_count_ = songs.size(); + cdda_song_loader_.LoadSongs(); } void CddaDevice::Refresh() { - if ((cdio_ && cdda_) && /* already init... */ - cdio_get_media_changed(cdio_) != - 1 /* ...and hasn't change since last time */) { + if (!cdda_song_loader_.HasChanged()) { return; } - // Check if mutex is already token (i.e. init is already taking place) - if (!mutex_init_.tryLock()) { - return; - } - mutex_init_.unlock(); Init(); } + +void CddaDevice::SongsLoaded(const SongList& songs) { + model_->Reset(); + emit SongsDiscovered(songs); + song_count_ = songs.size(); +} diff --git a/src/devices/cddadevice.h b/src/devices/cddadevice.h index d89221f07..60b0e1a20 100644 --- a/src/devices/cddadevice.h +++ b/src/devices/cddadevice.h @@ -24,6 +24,7 @@ #include #include +#include "cddasongloader.h" #include "connecteddevice.h" #include "core/song.h" #include "musicbrainz/musicbrainzclient.h" @@ -48,13 +49,10 @@ signals: void SongsDiscovered(const SongList& songs); private slots: - void AudioCDTagsLoaded(const QString& artist, const QString& album, - const MusicBrainzClient::ResultList& results); + void SongsLoaded(const SongList& songs); private: - GstElement* cdda_; - CdIo_t* cdio_; - QMutex mutex_init_; + CddaSongLoader cdda_song_loader_; }; #endif diff --git a/src/devices/cddasongloader.cpp b/src/devices/cddasongloader.cpp new file mode 100644 index 000000000..f10bd902e --- /dev/null +++ b/src/devices/cddasongloader.cpp @@ -0,0 +1,217 @@ +/* This file is part of Clementine. + Copyright 2014, David Sansome + + 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 . +*/ + +#include +#include +//#include + +#include + +#include "core/logging.h" +#include "core/timeconstants.h" + +#include "cddasongloader.h" + +CddaSongLoader::CddaSongLoader(const QUrl& url, QObject* parent) + : QObject(parent), + url_(url), + cdda_(nullptr), + cdio_(nullptr) {} + +CddaSongLoader::~CddaSongLoader() { + if (cdio_) cdio_destroy(cdio_); +} + +QUrl CddaSongLoader::GetUrlFromTrack(int track_number) const { + if (url_.isEmpty()) { + return QUrl(QString("cdda://%1").arg(track_number)); + } else { + return QUrl(QString("cdda://%1/%2").arg(url_.path()).arg(track_number)); + } +} + +void CddaSongLoader::LoadSongs() { + QMutexLocker locker(&mutex_load_); + cdio_ = cdio_open(url_.path().toLocal8Bit().constData(), DRIVER_DEVICE); + if (cdio_ == nullptr) { + return; + } + // Create gstreamer cdda element + GError* error = nullptr; + cdda_ = gst_element_make_from_uri(GST_URI_SRC, "cdda://", nullptr, &error); + if (error) { + qLog(Error) << error->code << error->message; + } + if (cdda_ == nullptr) { + return; + } + + if (!url_.isEmpty()) { + g_object_set(cdda_, "device", g_strdup(url_.path().toLocal8Bit().constData()), + nullptr); + } + if (g_object_class_find_property (G_OBJECT_GET_CLASS (cdda_), "paranoia-mode")) { + g_object_set (cdda_, "paranoia-mode", 0, NULL); + } + + // Change the element's state to ready and paused, to be able to query it + if (gst_element_set_state(cdda_, GST_STATE_READY) == + GST_STATE_CHANGE_FAILURE || + gst_element_set_state(cdda_, GST_STATE_PAUSED) == + GST_STATE_CHANGE_FAILURE) { + gst_element_set_state(cdda_, GST_STATE_NULL); + gst_object_unref(GST_OBJECT(cdda_)); + return; + } + + // Get number of tracks + GstFormat fmt = gst_format_get_by_nick("track"); + GstFormat out_fmt = fmt; + gint64 num_tracks = 0; + if (!gst_element_query_duration(cdda_, out_fmt, &num_tracks) || + out_fmt != fmt) { + qLog(Error) << "Error while querying cdda GstElement"; + gst_object_unref(GST_OBJECT(cdda_)); + return; + } + + SongList songs; + for (int track_number = 1; track_number <= num_tracks; track_number++) { + // Init song + Song song; + song.set_id(track_number); + song.set_valid(true); + song.set_filetype(Song::Type_Cdda); + song.set_url( + GetUrlFromTrack(track_number)); + song.set_title(QString("Track %1").arg(track_number)); + song.set_track(track_number); + songs << song; + } + emit SongsLoaded(songs); + + + gst_tag_register_musicbrainz_tags(); + + GstElement* pipeline = gst_pipeline_new("pipeline"); + GstElement* sink = gst_element_factory_make ("fakesink", NULL); + gst_bin_add_many (GST_BIN (pipeline), cdda_, sink, NULL); + gst_element_link (cdda_, sink); + gst_element_set_state(pipeline, GST_STATE_READY); + gst_element_set_state(pipeline, GST_STATE_PAUSED); + + // Get TOC and TAG messages + GstMessage* msg = nullptr; + GstMessage* msg_toc = nullptr; + GstMessage* msg_tag = nullptr; + while ((msg = gst_bus_timed_pop_filtered(GST_ELEMENT_BUS(pipeline), + GST_SECOND, (GstMessageType)(GST_MESSAGE_TOC | GST_MESSAGE_TAG)))) { + if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_TOC) { + if (msg_toc) gst_message_unref(msg_toc); // Shouldn't happen, but just in case + msg_toc = msg; + } else if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_TAG) { + if (msg_tag) gst_message_unref(msg_tag); + msg_tag = msg; + } + } + + // Handle TOC message: get tracks duration + if (msg_toc) { + GstToc* toc; + gst_message_parse_toc (msg_toc, &toc, nullptr); + if (toc) { + GList* entries = gst_toc_get_entries(toc); + if (entries && songs.size() <= g_list_length (entries)) { + int i = 0; + for (GList* node = entries; node != nullptr; node = node->next) { + GstTocEntry *entry = static_cast(node->data); + quint64 duration = 0; + gint64 start, stop; + if (gst_toc_entry_get_start_stop_times (entry, &start, &stop)) + duration = stop - start; + songs[i++].set_length_nanosec(duration); + } + } + } + gst_message_unref(msg_toc); + } + emit SongsDurationLoaded(songs); + + // Handle TAG message: generate MusicBrainz DiscId + if (msg_tag) { + GstTagList* tags = nullptr; + gst_message_parse_tag(msg_tag, &tags); + char* string_mb = nullptr; + if (gst_tag_list_get_string(tags, GST_TAG_CDDA_MUSICBRAINZ_DISCID, + &string_mb)) { + QString musicbrainz_discid(string_mb); + qLog(Info) << "MusicBrainz discid: " << musicbrainz_discid; + + MusicBrainzClient* musicbrainz_client = new MusicBrainzClient; + connect(musicbrainz_client, SIGNAL(Finished(const QString&, const QString&, + MusicBrainzClient::ResultList)), + SLOT(AudioCDTagsLoaded(const QString&, const QString&, + MusicBrainzClient::ResultList))); + musicbrainz_client->StartDiscIdRequest(musicbrainz_discid); + g_free(string_mb); + gst_message_unref(msg_tag); + gst_tag_list_free(tags); + } + } + + gst_element_set_state(pipeline, GST_STATE_NULL); + // This will also cause cdda_ to be unref'd. + gst_object_unref(pipeline); +} + +void CddaSongLoader::AudioCDTagsLoaded( + const QString& artist, const QString& album, + const MusicBrainzClient::ResultList& results) { + MusicBrainzClient* musicbrainz_client = + qobject_cast(sender()); + musicbrainz_client->deleteLater(); + SongList songs; + int track_number = 1; + if (results.size() == 0) return; + for (const MusicBrainzClient::Result& ret : results) { + Song song; + song.set_artist(artist); + song.set_album(album); + song.set_title(ret.title_); + song.set_length_nanosec(ret.duration_msec_ * kNsecPerMsec); + song.set_track(track_number); + song.set_year(ret.year_); + song.set_id(track_number); + // We need to set url: that's how playlist will find the correct item to + // update + song.set_url(GetUrlFromTrack(track_number++)); + songs << song; + } + emit SongsMetadataLoaded(songs); +} + +bool CddaSongLoader::HasChanged() { + if ((cdio_ && cdda_) && cdio_get_media_changed(cdio_) != 1) { + return false; + } + // Check if mutex is already token (i.e. init is already taking place) + if (!mutex_load_.tryLock()) { + return false; + } + mutex_load_.unlock(); + return true; +} diff --git a/src/devices/cddasongloader.h b/src/devices/cddasongloader.h new file mode 100644 index 000000000..7cb8b10ca --- /dev/null +++ b/src/devices/cddasongloader.h @@ -0,0 +1,69 @@ +/* This file is part of Clementine. + Copyright 2014, David Sansome + + 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 . +*/ + +#ifndef CDDASONGLOADER_H +#define CDDASONGLOADER_H + +#include +#include +#include + +// These must come after Qt includes (issue 3247) +#include +#include + +#include "core/song.h" +#include "musicbrainz/musicbrainzclient.h" + +// This class provides a (hopefully) nice, high level interface to get CD +// information and load tracks +class CddaSongLoader : public QObject { + Q_OBJECT + + public: + CddaSongLoader( + // Url of the CD device. Will use the default device if empty + const QUrl& url = QUrl(), + QObject* parent = nullptr); + ~CddaSongLoader(); + + // Load songs. + // Signals declared below will be emitted anytime new information will be available. + void LoadSongs(); + bool HasChanged(); + + signals: + void SongsLoaded(const SongList& songs); + void SongsDurationLoaded(const SongList& songs); + void SongsMetadataLoaded(const SongList& songs); + + private slots: + void AudioCDTagsLoaded(const QString& artist, const QString& album, + const MusicBrainzClient::ResultList& results); + + private: + QUrl GetUrlFromTrack(int track_number) const; + + QUrl url_; + GstElement* cdda_; + CdIo_t* cdio_; + QMutex mutex_load_; +}; + + + +#endif // CDDASONGLOADER_H diff --git a/src/playlist/playlist.cpp b/src/playlist/playlist.cpp index 34f879197..b2e48b294 100644 --- a/src/playlist/playlist.cpp +++ b/src/playlist/playlist.cpp @@ -1151,7 +1151,9 @@ void Playlist::UpdateItems(const SongList& songs) { if (item->Metadata().url() == song.url() && (item->Metadata().filetype() == Song::Type_Unknown || // Stream may change and may need to be updated too - item->Metadata().filetype() == Song::Type_Stream)) { + item->Metadata().filetype() == Song::Type_Stream || + // And CD tracks as well (tags are loaded in a second step) + item->Metadata().filetype() == Song::Type_Cdda)) { PlaylistItemPtr new_item; if (song.id() == -1) { new_item = PlaylistItemPtr(new SongPlaylistItem(song)); diff --git a/src/playlist/songloaderinserter.cpp b/src/playlist/songloaderinserter.cpp index b11114d4e..f90932ba0 100644 --- a/src/playlist/songloaderinserter.cpp +++ b/src/playlist/songloaderinserter.cpp @@ -85,20 +85,25 @@ void SongLoaderInserter::LoadAudioCD(Playlist* destination, int row, enqueue_ = enqueue; SongLoader* loader = new SongLoader(library_, player_, this); + NewClosure(loader, SIGNAL(AudioCDTracksLoaded()), + this, SLOT(AudioCDTracksLoaded(SongLoader*)), loader); connect(loader, SIGNAL(LoadAudioCDFinished(bool)), SLOT(AudioCDTagsLoaded(bool))); qLog(Info) << "Loading audio CD..."; SongLoader::Result ret = loader->LoadAudioCD(); if (ret == SongLoader::Error) { emit Error(tr("Error while loading audio CD")); delete loader; - } else { - songs_ = loader->songs(); - InsertSongs(); } + // Songs will be loaded later: see AudioCDTracksLoaded and AudioCDTagsLoaded slots } void SongLoaderInserter::DestinationDestroyed() { destination_ = nullptr; } +void SongLoaderInserter::AudioCDTracksLoaded(SongLoader* loader) { + songs_ = loader->songs(); + InsertSongs(); +} + void SongLoaderInserter::AudioCDTagsLoaded(bool success) { SongLoader* loader = qobject_cast(sender()); if (!loader || !destination_) return; diff --git a/src/playlist/songloaderinserter.h b/src/playlist/songloaderinserter.h index 8ed6001ab..14b3783a6 100644 --- a/src/playlist/songloaderinserter.h +++ b/src/playlist/songloaderinserter.h @@ -50,6 +50,7 @@ signals: private slots: void DestinationDestroyed(); + void AudioCDTracksLoaded(SongLoader* loader); void AudioCDTagsLoaded(bool success); void InsertSongs();