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