input/qobuz: implement InputPlugin::scan_tags()
[mpd-mirror.git] / src / input / plugins / QobuzInputPlugin.cxx
blobe5c4f6d64c64fde18dcbe81db3f21f33f433308a
1 /*
2 * Copyright 2003-2018 The Music Player Daemon Project
3 * http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "config.h"
21 #include "QobuzInputPlugin.hxx"
22 #include "QobuzClient.hxx"
23 #include "QobuzTrackRequest.hxx"
24 #include "QobuzTagScanner.hxx"
25 #include "CurlInputPlugin.hxx"
26 #include "PluginUnavailable.hxx"
27 #include "input/ProxyInputStream.hxx"
28 #include "input/FailingInputStream.hxx"
29 #include "input/InputPlugin.hxx"
30 #include "config/Block.hxx"
31 #include "thread/Mutex.hxx"
32 #include "util/StringCompare.hxx"
34 #include <stdexcept>
35 #include <memory>
37 #include <time.h>
39 static QobuzClient *qobuz_client;
41 class QobuzInputStream final
42 : public ProxyInputStream, QobuzSessionHandler, QobuzTrackHandler {
44 const std::string track_id;
46 std::unique_ptr<QobuzTrackRequest> track_request;
48 std::exception_ptr error;
50 public:
51 QobuzInputStream(const char *_uri, const char *_track_id,
52 Mutex &_mutex, Cond &_cond) noexcept
53 :ProxyInputStream(_uri, _mutex, _cond),
54 track_id(_track_id)
56 qobuz_client->AddLoginHandler(*this);
59 ~QobuzInputStream() {
60 qobuz_client->RemoveLoginHandler(*this);
63 /* virtual methods from InputStream */
65 void Check() override {
66 if (error)
67 std::rethrow_exception(error);
70 private:
71 void Failed(std::exception_ptr e) {
72 SetInput(std::make_unique<FailingInputStream>(GetURI(), e,
73 mutex, cond));
76 /* virtual methods from QobuzSessionHandler */
77 void OnQobuzSession() noexcept override;
79 /* virtual methods from QobuzTrackHandler */
80 void OnQobuzTrackSuccess(std::string url) noexcept override;
81 void OnQobuzTrackError(std::exception_ptr error) noexcept override;
84 void
85 QobuzInputStream::OnQobuzSession() noexcept
87 const std::lock_guard<Mutex> protect(mutex);
89 try {
90 const auto session = qobuz_client->GetSession();
92 QobuzTrackHandler &handler = *this;
93 track_request = std::make_unique<QobuzTrackRequest>(*qobuz_client,
94 session,
95 track_id.c_str(),
96 handler);
97 track_request->Start();
98 } catch (...) {
99 Failed(std::current_exception());
103 void
104 QobuzInputStream::OnQobuzTrackSuccess(std::string url) noexcept
106 const std::lock_guard<Mutex> protect(mutex);
107 track_request.reset();
109 try {
110 SetInput(OpenCurlInputStream(url.c_str(), {},
111 mutex, cond));
112 } catch (...) {
113 Failed(std::current_exception());
117 void
118 QobuzInputStream::OnQobuzTrackError(std::exception_ptr e) noexcept
120 const std::lock_guard<Mutex> protect(mutex);
121 track_request.reset();
123 Failed(e);
126 static void
127 InitQobuzInput(EventLoop &event_loop, const ConfigBlock &block)
129 const char *base_url = block.GetBlockValue("base_url",
130 "http://www.qobuz.com/api.json/0.2/");
132 const char *app_id = block.GetBlockValue("app_id");
133 if (app_id == nullptr)
134 throw PluginUnavailable("No Qobuz app_id configured");
136 const char *app_secret = block.GetBlockValue("app_secret");
137 if (app_secret == nullptr)
138 throw PluginUnavailable("No Qobuz app_secret configured");
140 const char *device_manufacturer_id = block.GetBlockValue("device_manufacturer_id",
141 "df691fdc-fa36-11e7-9718-635337d7df8f");
143 const char *username = block.GetBlockValue("username");
144 const char *email = block.GetBlockValue("email");
145 if (username == nullptr && email == nullptr)
146 throw PluginUnavailable("No Qobuz username configured");
148 const char *password = block.GetBlockValue("password");
149 if (password == nullptr)
150 throw PluginUnavailable("No Qobuz password configured");
152 const char *format_id = block.GetBlockValue("format_id", "5");
154 qobuz_client = new QobuzClient(event_loop, base_url,
155 app_id, app_secret,
156 device_manufacturer_id,
157 username, email, password,
158 format_id);
161 static void
162 FinishQobuzInput()
164 delete qobuz_client;
167 gcc_pure
168 static const char *
169 ExtractQobuzTrackId(const char *uri)
171 // TODO: what's the standard "qobuz://" URI syntax?
172 const char *track_id = StringAfterPrefix(uri, "qobuz://track/");
173 if (track_id == nullptr)
174 return nullptr;
176 if (*track_id == 0)
177 return nullptr;
179 return track_id;
182 static InputStreamPtr
183 OpenQobuzInput(const char *uri, Mutex &mutex, Cond &cond)
185 assert(qobuz_client != nullptr);
187 const char *track_id = ExtractQobuzTrackId(uri);
188 if (track_id == nullptr)
189 return nullptr;
191 // TODO: validate track_id
193 return std::make_unique<QobuzInputStream>(uri, track_id, mutex, cond);
196 static std::unique_ptr<RemoteTagScanner>
197 ScanQobuzTags(const char *uri, RemoteTagHandler &handler)
199 assert(qobuz_client != nullptr);
201 const char *track_id = ExtractQobuzTrackId(uri);
202 if (track_id == nullptr)
203 return nullptr;
205 return std::make_unique<QobuzTagScanner>(*qobuz_client, track_id,
206 handler);
209 const InputPlugin qobuz_input_plugin = {
210 "qobuz",
211 InitQobuzInput,
212 FinishQobuzInput,
213 OpenQobuzInput,
214 ScanQobuzTags,