1 /* This file is part of Clementine.
2 Copyright 2012, David Sansome <me@davidsansome.com>
3 Copyright 2012, 2014, John Maguire <john.maguire@gmail.com>
4 Copyright 2013, Arnaud Bienner <arnaud.bienner@gmail.com>
5 Copyright 2014, Krzysztof Sobiecki <sobkas@gmail.com>
6
7 Clementine is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 Clementine is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Clementine. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "player.h"
22 #include "tagreaderclient.h"
23
24 #include <QCoreApplication>
25 #include <QFile>
26 #include <QProcess>
27 #include <QTcpServer>
28 #include <QThread>
29 #include <QUrl>
30
31 const char* TagReaderClient::kWorkerExecutableName = "clementine-tagreader";
32 TagReaderClient* TagReaderClient::sInstance = nullptr;
33
TagReaderClient(QObject * parent)34 TagReaderClient::TagReaderClient(QObject* parent)
35 : QObject(parent), worker_pool_(new WorkerPool<HandlerType>(this)) {
36 sInstance = this;
37
38 QSettings s;
39 s.beginGroup(Player::kSettingsGroup);
40
41 int max_workers = QThread::idealThreadCount();
42 int num_workers = s.value("max_numprocs_tagclients", max_workers).toInt();
43
44 worker_pool_->SetExecutableName(kWorkerExecutableName);
45 worker_pool_->SetWorkerCount(num_workers);
46 connect(worker_pool_, SIGNAL(WorkerFailedToStart()),
47 SLOT(WorkerFailedToStart()));
48 }
49
Start()50 void TagReaderClient::Start() { worker_pool_->Start(); }
51
WorkerFailedToStart()52 void TagReaderClient::WorkerFailedToStart() {
53 qLog(Error) << "The" << kWorkerExecutableName << "executable was not found"
54 << "in the current directory or on the PATH. Clementine will"
55 << "not be able to read music file tags without it.";
56 }
57
ReadFile(const QString & filename)58 TagReaderReply* TagReaderClient::ReadFile(const QString& filename) {
59 pb::tagreader::Message message;
60 pb::tagreader::ReadFileRequest* req = message.mutable_read_file_request();
61
62 req->set_filename(DataCommaSizeFromQString(filename));
63
64 return worker_pool_->SendMessageWithReply(&message);
65 }
66
SaveFile(const QString & filename,const Song & metadata)67 TagReaderReply* TagReaderClient::SaveFile(const QString& filename,
68 const Song& metadata) {
69 pb::tagreader::Message message;
70 pb::tagreader::SaveFileRequest* req = message.mutable_save_file_request();
71
72 req->set_filename(DataCommaSizeFromQString(filename));
73 metadata.ToProtobuf(req->mutable_metadata());
74
75 return worker_pool_->SendMessageWithReply(&message);
76 }
77
UpdateSongStatistics(const Song & metadata)78 TagReaderReply* TagReaderClient::UpdateSongStatistics(const Song& metadata) {
79 pb::tagreader::Message message;
80 pb::tagreader::SaveSongStatisticsToFileRequest* req =
81 message.mutable_save_song_statistics_to_file_request();
82
83 req->set_filename(DataCommaSizeFromQString(metadata.url().toLocalFile()));
84 metadata.ToProtobuf(req->mutable_metadata());
85
86 return worker_pool_->SendMessageWithReply(&message);
87 }
88
UpdateSongsStatistics(const SongList & songs)89 void TagReaderClient::UpdateSongsStatistics(const SongList& songs) {
90 for (const Song& song : songs) {
91 TagReaderReply* reply = UpdateSongStatistics(song);
92 connect(reply, SIGNAL(Finished(bool)), reply, SLOT(deleteLater()));
93 }
94 }
95
UpdateSongRating(const Song & metadata)96 TagReaderReply* TagReaderClient::UpdateSongRating(const Song& metadata) {
97 pb::tagreader::Message message;
98 pb::tagreader::SaveSongRatingToFileRequest* req =
99 message.mutable_save_song_rating_to_file_request();
100
101 req->set_filename(DataCommaSizeFromQString(metadata.url().toLocalFile()));
102 metadata.ToProtobuf(req->mutable_metadata());
103
104 return worker_pool_->SendMessageWithReply(&message);
105 }
106
UpdateSongsRating(const SongList & songs)107 void TagReaderClient::UpdateSongsRating(const SongList& songs) {
108 for (const Song& song : songs) {
109 TagReaderReply* reply = UpdateSongRating(song);
110 connect(reply, SIGNAL(Finished(bool)), reply, SLOT(deleteLater()));
111 }
112 }
113
IsMediaFile(const QString & filename)114 TagReaderReply* TagReaderClient::IsMediaFile(const QString& filename) {
115 pb::tagreader::Message message;
116 pb::tagreader::IsMediaFileRequest* req =
117 message.mutable_is_media_file_request();
118
119 req->set_filename(DataCommaSizeFromQString(filename));
120
121 return worker_pool_->SendMessageWithReply(&message);
122 }
123
LoadEmbeddedArt(const QString & filename)124 TagReaderReply* TagReaderClient::LoadEmbeddedArt(const QString& filename) {
125 pb::tagreader::Message message;
126 pb::tagreader::LoadEmbeddedArtRequest* req =
127 message.mutable_load_embedded_art_request();
128
129 req->set_filename(DataCommaSizeFromQString(filename));
130
131 return worker_pool_->SendMessageWithReply(&message);
132 }
133
ReadCloudFile(const QUrl & download_url,const QString & title,int size,const QString & mime_type,const QString & authorisation_header)134 TagReaderReply* TagReaderClient::ReadCloudFile(
135 const QUrl& download_url, const QString& title, int size,
136 const QString& mime_type, const QString& authorisation_header) {
137 pb::tagreader::Message message;
138 pb::tagreader::ReadCloudFileRequest* req =
139 message.mutable_read_cloud_file_request();
140
141 const QString url_string = download_url.toEncoded();
142 req->set_download_url(DataCommaSizeFromQString(url_string));
143 req->set_title(DataCommaSizeFromQString(title));
144 req->set_size(size);
145 req->set_mime_type(DataCommaSizeFromQString(mime_type));
146 req->set_authorisation_header(DataCommaSizeFromQString(authorisation_header));
147
148 return worker_pool_->SendMessageWithReply(&message);
149 }
150
ReadFileBlocking(const QString & filename,Song * song)151 void TagReaderClient::ReadFileBlocking(const QString& filename, Song* song) {
152 Q_ASSERT(QThread::currentThread() != thread());
153
154 TagReaderReply* reply = ReadFile(filename);
155 if (reply->WaitForFinished()) {
156 song->InitFromProtobuf(reply->message().read_file_response().metadata());
157 }
158 reply->deleteLater();
159 }
160
SaveFileBlocking(const QString & filename,const Song & metadata)161 bool TagReaderClient::SaveFileBlocking(const QString& filename,
162 const Song& metadata) {
163 Q_ASSERT(QThread::currentThread() != thread());
164
165 bool ret = false;
166
167 TagReaderReply* reply = SaveFile(filename, metadata);
168 if (reply->WaitForFinished()) {
169 ret = reply->message().save_file_response().success();
170 }
171 reply->deleteLater();
172
173 return ret;
174 }
175
UpdateSongStatisticsBlocking(const Song & metadata)176 bool TagReaderClient::UpdateSongStatisticsBlocking(const Song& metadata) {
177 Q_ASSERT(QThread::currentThread() != thread());
178
179 bool ret = false;
180
181 TagReaderReply* reply = UpdateSongStatistics(metadata);
182 if (reply->WaitForFinished()) {
183 ret = reply->message().save_song_statistics_to_file_response().success();
184 }
185 reply->deleteLater();
186
187 return ret;
188 }
189
UpdateSongRatingBlocking(const Song & metadata)190 bool TagReaderClient::UpdateSongRatingBlocking(const Song& metadata) {
191 Q_ASSERT(QThread::currentThread() != thread());
192
193 bool ret = false;
194
195 TagReaderReply* reply = UpdateSongRating(metadata);
196 if (reply->WaitForFinished()) {
197 ret = reply->message().save_song_rating_to_file_response().success();
198 }
199 reply->deleteLater();
200
201 return ret;
202 }
203
IsMediaFileBlocking(const QString & filename)204 bool TagReaderClient::IsMediaFileBlocking(const QString& filename) {
205 Q_ASSERT(QThread::currentThread() != thread());
206
207 bool ret = false;
208
209 TagReaderReply* reply = IsMediaFile(filename);
210 if (reply->WaitForFinished()) {
211 ret = reply->message().is_media_file_response().success();
212 }
213 reply->deleteLater();
214
215 return ret;
216 }
217
LoadEmbeddedArtBlocking(const QString & filename)218 QImage TagReaderClient::LoadEmbeddedArtBlocking(const QString& filename) {
219 Q_ASSERT(QThread::currentThread() != thread());
220
221 QImage ret;
222
223 TagReaderReply* reply = LoadEmbeddedArt(filename);
224 if (reply->WaitForFinished()) {
225 const std::string& data_str =
226 reply->message().load_embedded_art_response().data();
227 ret.loadFromData(QByteArray(data_str.data(), data_str.size()));
228 }
229 reply->deleteLater();
230
231 return ret;
232 }
233