2018-02-27 18:06:05 +01:00
|
|
|
/*
|
|
|
|
* Strawberry Music Player
|
|
|
|
* This file was part of Clementine.
|
|
|
|
* Copyright 2010, David Sansome <me@davidsansome.com>
|
2021-03-20 21:14:47 +01:00
|
|
|
* Copyright 2019-2021, Jonas Kvinge <jonas@jkvinge.net>
|
2018-02-27 18:06:05 +01:00
|
|
|
*
|
|
|
|
* Strawberry 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.
|
|
|
|
*
|
|
|
|
* Strawberry 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 Strawberry. If not, see <http://www.gnu.org/licenses/>.
|
2018-08-09 18:39:44 +02:00
|
|
|
*
|
2018-02-27 18:06:05 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
2021-06-21 19:52:37 +02:00
|
|
|
#include <algorithm>
|
2024-04-23 17:15:42 +02:00
|
|
|
#include <utility>
|
2021-06-21 19:52:37 +02:00
|
|
|
|
2018-05-01 00:41:33 +02:00
|
|
|
#include <QObject>
|
|
|
|
#include <QtConcurrentMap>
|
|
|
|
#include <QFuture>
|
|
|
|
#include <QFutureWatcher>
|
|
|
|
#include <QString>
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2023-07-21 05:55:24 +02:00
|
|
|
#include "core/shared_ptr.h"
|
2023-04-21 20:20:53 +02:00
|
|
|
#include "core/networkaccessmanager.h"
|
2022-12-28 03:12:00 +01:00
|
|
|
#include "utilities/timeconstants.h"
|
2021-04-25 21:16:44 +02:00
|
|
|
#include "engine/chromaprinter.h"
|
2018-02-27 18:06:05 +01:00
|
|
|
#include "acoustidclient.h"
|
|
|
|
#include "musicbrainzclient.h"
|
2018-05-01 00:41:33 +02:00
|
|
|
#include "tagfetcher.h"
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2023-07-21 05:55:24 +02:00
|
|
|
TagFetcher::TagFetcher(SharedPtr<NetworkAccessManager> network, QObject *parent)
|
2018-02-27 18:06:05 +01:00
|
|
|
: QObject(parent),
|
|
|
|
fingerprint_watcher_(nullptr),
|
2023-04-21 20:20:53 +02:00
|
|
|
acoustid_client_(new AcoustidClient(network, this)),
|
|
|
|
musicbrainz_client_(new MusicBrainzClient(network, this)) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
QObject::connect(acoustid_client_, &AcoustidClient::Finished, this, &TagFetcher::PuidsFound);
|
|
|
|
QObject::connect(musicbrainz_client_, &MusicBrainzClient::Finished, this, &TagFetcher::TagsFetched);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
QString TagFetcher::GetFingerprint(const Song &song) {
|
|
|
|
return Chromaprinter(song.url().toLocalFile()).CreateFingerprint();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TagFetcher::StartFetch(const SongList &songs) {
|
|
|
|
|
|
|
|
Cancel();
|
|
|
|
|
|
|
|
songs_ = songs;
|
|
|
|
|
2021-06-08 20:58:46 +02:00
|
|
|
bool have_fingerprints = true;
|
2021-07-11 09:49:38 +02:00
|
|
|
if (std::any_of(songs.begin(), songs.end(), [](const Song &song) { return song.fingerprint().isEmpty(); })) {
|
2021-06-21 19:52:37 +02:00
|
|
|
have_fingerprints = false;
|
2021-06-08 20:58:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (have_fingerprints) {
|
2021-08-23 21:21:08 +02:00
|
|
|
for (int i = 0; i < songs_.count(); ++i) {
|
2021-06-08 20:58:46 +02:00
|
|
|
const Song &song = songs_[i];
|
|
|
|
emit Progress(song, tr("Identifying song"));
|
|
|
|
acoustid_client_->Start(i, song.fingerprint(), static_cast<int>(song.length_nanosec() / kNsecPerMsec));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
QFuture<QString> future = QtConcurrent::mapped(songs_, GetFingerprint);
|
|
|
|
fingerprint_watcher_ = new QFutureWatcher<QString>(this);
|
|
|
|
QObject::connect(fingerprint_watcher_, &QFutureWatcher<QString>::resultReadyAt, this, &TagFetcher::FingerprintFound);
|
2021-06-16 00:30:21 +02:00
|
|
|
fingerprint_watcher_->setFuture(future);
|
2024-04-23 17:15:42 +02:00
|
|
|
for (const Song &song : std::as_const(songs_)) {
|
2021-06-08 20:58:46 +02:00
|
|
|
emit Progress(song, tr("Fingerprinting song"));
|
|
|
|
}
|
2018-02-27 18:06:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void TagFetcher::Cancel() {
|
|
|
|
|
|
|
|
if (fingerprint_watcher_) {
|
|
|
|
fingerprint_watcher_->cancel();
|
|
|
|
|
2021-01-26 16:48:04 +01:00
|
|
|
fingerprint_watcher_->deleteLater();
|
2018-02-27 18:06:05 +01:00
|
|
|
fingerprint_watcher_ = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
acoustid_client_->CancelAll();
|
|
|
|
musicbrainz_client_->CancelAll();
|
|
|
|
songs_.clear();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-06-29 19:57:20 +02:00
|
|
|
void TagFetcher::FingerprintFound(const int index) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
2021-06-08 20:58:46 +02:00
|
|
|
QFutureWatcher<QString> *watcher = reinterpret_cast<QFutureWatcher<QString>*>(sender());
|
2021-01-26 16:48:04 +01:00
|
|
|
if (!watcher || index >= songs_.count()) return;
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
const QString fingerprint = watcher->resultAt(index);
|
|
|
|
const Song &song = songs_[index];
|
|
|
|
|
|
|
|
if (fingerprint.isEmpty()) {
|
|
|
|
emit ResultAvailable(song, SongList());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
emit Progress(song, tr("Identifying song"));
|
2021-03-21 18:53:02 +01:00
|
|
|
acoustid_client_->Start(index, fingerprint, static_cast<int>(song.length_nanosec() / kNsecPerMsec));
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-06-29 19:57:20 +02:00
|
|
|
void TagFetcher::PuidsFound(const int index, const QStringList &puid_list, const QString &error) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (index >= songs_.count()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Song &song = songs_[index];
|
|
|
|
|
|
|
|
if (puid_list.isEmpty()) {
|
2019-06-29 19:57:20 +02:00
|
|
|
emit ResultAvailable(song, SongList(), error);
|
2018-02-27 18:06:05 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
emit Progress(song, tr("Downloading metadata"));
|
|
|
|
musicbrainz_client_->Start(index, puid_list);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-06-29 19:57:20 +02:00
|
|
|
void TagFetcher::TagsFetched(const int index, const MusicBrainzClient::ResultList &results, const QString &error) {
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
if (index >= songs_.count()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const Song &original_song = songs_[index];
|
|
|
|
SongList songs_guessed;
|
2021-06-20 19:04:08 +02:00
|
|
|
songs_guessed.reserve(results.count());
|
2018-02-27 18:06:05 +01:00
|
|
|
for (const MusicBrainzClient::Result &result : results) {
|
|
|
|
Song song;
|
|
|
|
song.Init(result.title_, result.artist_, result.album_, result.duration_msec_ * kNsecPerMsec);
|
|
|
|
song.set_track(result.track_);
|
|
|
|
song.set_year(result.year_);
|
|
|
|
songs_guessed << song;
|
|
|
|
}
|
|
|
|
|
2019-06-29 19:57:20 +02:00
|
|
|
emit ResultAvailable(original_song, songs_guessed, error);
|
2018-02-27 18:06:05 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|