Change Spotify to use the new MessageHandler, remove SpotifyMessageHandler

This commit is contained in:
David Sansome 2012-01-07 23:30:33 +00:00
parent 67c6dead5a
commit bbf99fdd0b
13 changed files with 70 additions and 235 deletions

View File

@ -23,7 +23,6 @@
#include "mediapipeline.h"
#include "spotifyclient.h"
#include "spotifykey.h"
#include "spotifymessagehandler.h"
#include "spotifymessages.pb.h"
#include "spotify_utilities.h"
#include "core/logging.h"
@ -39,12 +38,13 @@ const int SpotifyClient::kWaveHeaderSize = 44;
SpotifyClient::SpotifyClient(QObject* parent)
: QObject(parent),
: AbstractMessageHandler<pb::spotify::Message>(NULL, parent),
api_key_(QByteArray::fromBase64(kSpotifyApiKey)),
protocol_socket_(new QTcpSocket(this)),
handler_(new SpotifyMessageHandler(protocol_socket_, this)),
session_(NULL),
events_timer_(new QTimer(this)) {
SetDevice(protocol_socket_);
memset(&spotify_callbacks_, 0, sizeof(spotify_callbacks_));
memset(&spotify_config_, 0, sizeof(spotify_config_));
memset(&playlistcontainer_callbacks_, 0, sizeof(playlistcontainer_callbacks_));
@ -91,8 +91,6 @@ SpotifyClient::SpotifyClient(QObject* parent)
events_timer_->setSingleShot(true);
connect(events_timer_, SIGNAL(timeout()), SLOT(ProcessEvents()));
connect(handler_, SIGNAL(MessageArrived(pb::spotify::Message)),
SLOT(HandleMessage(pb::spotify::Message)));
connect(protocol_socket_, SIGNAL(disconnected()),
QCoreApplication::instance(), SLOT(quit()));
}
@ -227,7 +225,7 @@ void SpotifyClient::SendSearchResponse(sp_search* result) {
if (error != SP_ERROR_OK) {
response->set_error(sp_error_message(error));
handler_->SendMessage(message);
SendMessage(message);
sp_search_release(result);
return;
}
@ -261,11 +259,11 @@ void SpotifyClient::SendSearchResponse(sp_search* result) {
response->set_total_tracks(sp_search_total_tracks(result));
response->set_did_you_mean(sp_search_did_you_mean(result));
handler_->SendMessage(message);
SendMessage(message);
sp_search_release(result);
}
void SpotifyClient::HandleMessage(const pb::spotify::Message& message) {
void SpotifyClient::MessageArrived(const pb::spotify::Message& message) {
if (message.has_login_request()) {
Login(message.login_request());
} else if (message.has_load_playlist_request()) {
@ -340,7 +338,7 @@ void SpotifyClient::SendLoginCompleted(bool success, const QString& error,
response->set_error_code(error_code);
}
handler_->SendMessage(message);
SendMessage(message);
}
void SpotifyClient::PlaylistContainerLoadedCallback(sp_playlistcontainer* pc, void* userdata) {
@ -424,7 +422,7 @@ void SpotifyClient::SendPlaylistList() {
}
}
handler_->SendMessage(message);
SendMessage(message);
}
sp_playlist* SpotifyClient::GetPlaylist(pb::spotify::PlaylistType type, int user_index) {
@ -467,7 +465,7 @@ void SpotifyClient::LoadPlaylist(const pb::spotify::LoadPlaylistRequest& req) {
pb::spotify::Message message;
pb::spotify::LoadPlaylistResponse* response = message.mutable_load_playlist_response();
*response->mutable_request() = req;
handler_->SendMessage(message);
SendMessage(message);
return;
}
@ -543,7 +541,7 @@ void SpotifyClient::PlaylistStateChangedForLoadPlaylist(sp_playlist* pl, void* u
me->ConvertTrack(track, response->add_track());
sp_track_release(track);
}
me->handler_->SendMessage(message);
me->SendMessage(message);
// Unref the playlist and remove our callbacks
sp_playlist_remove_callbacks(pl, &me->load_playlist_callbacks_, me);
@ -752,7 +750,7 @@ void SpotifyClient::SendDownloadProgress(
progress->mutable_request()->set_user_playlist_index(index);
}
progress->set_sync_progress(download_progress);
handler_->SendMessage(message);
SendMessage(message);
}
int SpotifyClient::GetDownloadProgress(sp_playlist* playlist) {
@ -839,7 +837,7 @@ void SpotifyClient::SendPlaybackError(const QString& error) {
pb::spotify::PlaybackError* msg = message.mutable_playback_error();
msg->set_error(DataCommaSizeFromQString(error));
handler_->SendMessage(message);
SendMessage(message);
}
void SpotifyClient::LoadImage(const QString& id_b64) {
@ -852,7 +850,7 @@ void SpotifyClient::LoadImage(const QString& id_b64) {
pb::spotify::Message message;
pb::spotify::ImageResponse* msg = message.mutable_image_response();
msg->set_id(DataCommaSizeFromQString(id_b64));
handler_->SendMessage(message);
SendMessage(message);
return;
}
@ -904,7 +902,7 @@ void SpotifyClient::TryImageAgain(sp_image* image) {
if (data && size) {
msg->set_data(data, size);
}
handler_->SendMessage(message);
SendMessage(message);
// Free stuff
image_callbacks_registered_[image] --;
@ -961,6 +959,6 @@ void SpotifyClient::AlbumBrowseComplete(sp_albumbrowse* result, void* userdata)
me->ConvertTrack(sp_albumbrowse_track(result, i), msg->add_track());
}
me->handler_->SendMessage(message);
me->SendMessage(message);
sp_albumbrowse_release(result);
}

View File

@ -23,6 +23,7 @@
#define SPOTIFYCLIENT_H
#include "spotifymessages.pb.h"
#include "core/messagehandler.h"
#include <QMap>
#include <QObject>
@ -34,9 +35,8 @@ class QTimer;
class MediaPipeline;
class ResponseMessage;
class SpotifyMessageHandler;
class SpotifyClient : public QObject {
class SpotifyClient : public AbstractMessageHandler<pb::spotify::Message> {
Q_OBJECT
public:
@ -48,8 +48,10 @@ public:
void Init(quint16 port);
protected:
void MessageArrived(const pb::spotify::Message& message);
private slots:
void HandleMessage(const pb::spotify::Message& message);
void ProcessEvents();
private:
@ -155,7 +157,6 @@ private:
QByteArray api_key_;
QTcpSocket* protocol_socket_;
SpotifyMessageHandler* handler_;
sp_session_config spotify_config_;
sp_session_callbacks spotify_callbacks_;

View File

@ -27,11 +27,19 @@
_MessageHandlerBase::_MessageHandlerBase(QIODevice* device, QObject* parent)
: QObject(parent),
device_(device),
device_(NULL),
flush_abstract_socket_(NULL),
flush_local_socket_(NULL),
reading_protobuf_(false),
expected_length_(0) {
if (device) {
SetDevice(device);
}
}
void _MessageHandlerBase::SetDevice(QIODevice* device) {
device_ = device;
buffer_.open(QIODevice::ReadWrite);
connect(device, SIGNAL(readyRead()), SLOT(DeviceReadyRead()));
@ -64,7 +72,7 @@ void _MessageHandlerBase::DeviceReadyRead() {
// Did we get everything?
if (buffer_.size() == expected_length_) {
// Parse the message
if (!MessageArrived(buffer_.data())) {
if (!RawMessageArrived(buffer_.data())) {
qLog(Error) << "Malformed protobuf message";
device_->close();
return;

View File

@ -95,15 +95,19 @@ class _MessageHandlerBase : public QObject {
Q_OBJECT
public:
// device can be NULL, in which case you must call SetDevice before writing
// any messages.
_MessageHandlerBase(QIODevice* device, QObject* parent);
void SetDevice(QIODevice* device);
protected slots:
void WriteMessage(const QByteArray& data);
void DeviceReadyRead();
virtual void SocketClosed() {}
protected:
virtual bool MessageArrived(const QByteArray& data) = 0;
virtual bool RawMessageArrived(const QByteArray& data) = 0;
protected:
typedef bool (QAbstractSocket::*FlushAbstractSocket)();
@ -156,7 +160,7 @@ protected:
virtual void MessageArrived(const MessageType& message) {}
// _MessageHandlerBase
bool MessageArrived(const QByteArray& data);
bool RawMessageArrived(const QByteArray& data);
void SocketClosed();
private:
@ -197,7 +201,7 @@ void AbstractMessageHandler<MessageType>::SendReply(const MessageType& request,
}
template<typename MessageType>
bool AbstractMessageHandler<MessageType>::MessageArrived(const QByteArray& data) {
bool AbstractMessageHandler<MessageType>::RawMessageArrived(const QByteArray& data) {
MessageType message;
if (!message.ParseFromArray(data.constData(), data.size())) {
return false;

View File

@ -1,32 +1,18 @@
include_directories(${PROTOBUF_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories(${CMAKE_SOURCE_DIR}/ext/libclementine-common)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/blobversion.h.in
${CMAKE_CURRENT_BINARY_DIR}/blobversion.h)
set(COMMON_SOURCES
spotifymessagehandler.cpp
)
set(COMMON_HEADERS
spotifymessagehandler.h
)
set(COMMON_MESSAGES
set(MESSAGES
spotifymessages.proto
)
qt4_wrap_cpp(COMMON_MOC ${COMMON_HEADERS})
protobuf_generate_cpp(PROTO_SOURCES PROTO_HEADERS ${COMMON_MESSAGES})
protobuf_generate_cpp(PROTO_SOURCES PROTO_HEADERS ${MESSAGES})
add_library(clementine-spotifyblob-messages STATIC
${COMMON_SOURCES}
${COMMON_MOC}
${PROTO_SOURCES}
)
target_link_libraries(clementine-spotifyblob-messages
libclementine-common
)

View File

@ -1,90 +0,0 @@
/* This file is part of Clementine.
Copyright 2011, David Sansome <me@davidsansome.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Note: this file is licensed under the Apache License instead of GPL because
// it is used by the Spotify blob which links against libspotify and is not GPL
// compatible.
#include "spotifymessages.pb.h"
#include "spotifymessagehandler.h"
#include "core/logging.h"
#include <QAbstractSocket>
#include <QBuffer>
SpotifyMessageHandler::SpotifyMessageHandler(QAbstractSocket* device, QObject* parent)
: QObject(parent),
device_(device),
reading_protobuf_(false),
expected_length_(0) {
buffer_.open(QIODevice::ReadWrite);
connect(device, SIGNAL(readyRead()), SLOT(DeviceReadyRead()));
}
void SpotifyMessageHandler::DeviceReadyRead() {
while (device_->bytesAvailable()) {
if (!reading_protobuf_) {
// Read the length of the next message
QDataStream s(device_);
s >> expected_length_;
reading_protobuf_ = true;
}
// Read some of the message
buffer_.write(device_->read(expected_length_ - buffer_.size()));
// Did we get everything?
if (buffer_.size() == expected_length_) {
// Parse the message
pb::spotify::SpotifyMessage message;
if (!message.ParseFromArray(buffer_.data().constData(), buffer_.size())) {
qLog(Error) << "Malformed protobuf message";
device_->close();
return;
}
emit MessageArrived(message);
// Clear the buffer
buffer_.close();
buffer_.setData(QByteArray());
buffer_.open(QIODevice::ReadWrite);
reading_protobuf_ = false;
}
}
}
void SpotifyMessageHandler::SendMessage(const pb::spotify::SpotifyMessage& message) {
std::string data = message.SerializeAsString();
WriteMessage(QByteArray(data.data(), data.size()));
}
void SpotifyMessageHandler::SendMessageAsync(const pb::spotify::SpotifyMessage& message) {
std::string data = message.SerializeAsString();
metaObject()->invokeMethod(this, "WriteMessage", Qt::QueuedConnection,
Q_ARG(QByteArray, QByteArray(data.data(), data.size())));
}
void SpotifyMessageHandler::WriteMessage(const QByteArray& data) {
QDataStream s(device_);
s << quint32(data.length());
s.writeRawData(data.data(), data.length());
device_->flush();
}

View File

@ -1,67 +0,0 @@
/* This file is part of Clementine.
Copyright 2011, David Sansome <me@davidsansome.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Note: this file is licensed under the Apache License instead of GPL because
// it is used by the Spotify blob which links against libspotify and is not GPL
// compatible.
#ifndef SPOTIFYMESSAGEHANDLER_H
#define SPOTIFYMESSAGEHANDLER_H
#include <QBuffer>
#include <QObject>
class QAbstractSocket;
namespace pb {
namespace spotify {
class SpotifyMessage;
}
}
#define QStringFromStdString(x) \
QString::fromUtf8(x.data(), x.size())
#define DataCommaSizeFromQString(x) \
x.toUtf8().constData(), x.toUtf8().length()
class SpotifyMessageHandler : public QObject {
Q_OBJECT
public:
SpotifyMessageHandler(QAbstractSocket* device, QObject* parent);
void SendMessage(const pb::spotify::SpotifyMessage& message);
void SendMessageAsync(const pb::spotify::SpotifyMessage& message);
signals:
void MessageArrived(const pb::spotify::SpotifyMessage& message);
private slots:
void WriteMessage(const QByteArray& data);
void DeviceReadyRead();
private:
QAbstractSocket* device_;
bool reading_protobuf_;
quint32 expected_length_;
QBuffer buffer_;
};
#endif // SPOTIFYMESSAGEHANDLER_H

View File

@ -163,6 +163,9 @@ message PlaybackSettings {
}
message Message {
// Not currently used
optional int32 id = 18;
optional LoginRequest login_request = 1;
optional LoginResponse login_response = 2;
optional Playlists playlists_updated = 3;

View File

@ -1,5 +1,4 @@
include_directories(${PROTOBUF_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(MESSAGES
tagreadermessages.proto

View File

@ -15,7 +15,6 @@
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#include "spotifymessagehandler.h"
#include "spotifysearchprovider.h"
#include "core/logging.h"
#include "internet/internetmodel.h"

View File

@ -20,17 +20,14 @@
#include "core/logging.h"
#include "spotifymessages.pb.h"
#include "spotifymessagehandler.h"
#include <QTcpServer>
#include <QTcpSocket>
#include <QTimer>
SpotifyServer::SpotifyServer(QObject* parent)
: QObject(parent),
: AbstractMessageHandler<pb::spotify::Message>(NULL, parent),
server_(new QTcpServer(this)),
protocol_socket_(NULL),
handler_(NULL),
logged_in_(false)
{
connect(server_, SIGNAL(newConnection()), SLOT(NewConnection()));
@ -47,33 +44,31 @@ int SpotifyServer::server_port() const {
}
void SpotifyServer::NewConnection() {
delete protocol_socket_;
delete handler_;
QTcpSocket* socket = server_->nextPendingConnection();
SetDevice(socket);
protocol_socket_ = server_->nextPendingConnection();
handler_ = new SpotifyMessageHandler(protocol_socket_, this);
connect(handler_, SIGNAL(MessageArrived(pb::spotify::Message)),
SLOT(HandleMessage(pb::spotify::Message)));
qLog(Info) << "Connection from port" << protocol_socket_->peerPort();
qLog(Info) << "Connection from port" << socket->peerPort();
// Send any login messages that were queued before the client connected
foreach (const pb::spotify::Message& message, queued_login_messages_) {
SendMessage(message);
SendOrQueueMessage(message);
}
queued_login_messages_.clear();
// Don't take any more connections from clients
disconnect(server_, SIGNAL(newConnection()), this, 0);
}
void SpotifyServer::SendMessage(const pb::spotify::Message& message) {
void SpotifyServer::SendOrQueueMessage(const pb::spotify::Message& message) {
const bool is_login_message = message.has_login_request();
QList<pb::spotify::Message>* queue =
is_login_message ? &queued_login_messages_ : &queued_messages_;
if (!protocol_socket_ || (!is_login_message && !logged_in_)) {
if (!device_ || (!is_login_message && !logged_in_)) {
queue->append(message);
} else {
handler_->SendMessage(message);
SendMessage(message);
}
}
@ -89,7 +84,7 @@ void SpotifyServer::Login(const QString& username, const QString& password,
request->mutable_playback_settings()->set_bitrate(bitrate);
request->mutable_playback_settings()->set_volume_normalisation(volume_normalisation);
SendMessage(message);
SendOrQueueMessage(message);
}
void SpotifyServer::SetPlaybackSettings(pb::spotify::Bitrate bitrate, bool volume_normalisation) {
@ -99,10 +94,10 @@ void SpotifyServer::SetPlaybackSettings(pb::spotify::Bitrate bitrate, bool volum
request->set_bitrate(bitrate);
request->set_volume_normalisation(volume_normalisation);
SendMessage(message);
SendOrQueueMessage(message);
}
void SpotifyServer::HandleMessage(const pb::spotify::Message& message) {
void SpotifyServer::MessageArrived(const pb::spotify::Message& message) {
if (message.has_login_response()) {
const pb::spotify::LoginResponse& response = message.login_response();
logged_in_ = response.success();
@ -110,7 +105,7 @@ void SpotifyServer::HandleMessage(const pb::spotify::Message& message) {
if (response.success()) {
// Send any messages that were queued before the client logged in
foreach (const pb::spotify::Message& message, queued_messages_) {
SendMessage(message);
SendOrQueueMessage(message);
}
queued_messages_.clear();
}
@ -165,7 +160,7 @@ void SpotifyServer::LoadPlaylist(pb::spotify::PlaylistType type, int index) {
req->set_user_playlist_index(index);
}
SendMessage(message);
SendOrQueueMessage(message);
}
void SpotifyServer::SyncPlaylist(
@ -178,7 +173,7 @@ void SpotifyServer::SyncPlaylist(
}
req->set_offline_sync(offline);
SendMessage(message);
SendOrQueueMessage(message);
}
void SpotifyServer::SyncInbox() {
@ -223,7 +218,7 @@ void SpotifyServer::StartPlayback(const QString& uri, quint16 port) {
req->set_track_uri(DataCommaSizeFromQString(uri));
req->set_media_port(port);
SendMessage(message);
SendOrQueueMessage(message);
}
void SpotifyServer::Seek(qint64 offset_bytes) {
@ -231,7 +226,7 @@ void SpotifyServer::Seek(qint64 offset_bytes) {
pb::spotify::SeekRequest* req = message.mutable_seek_request();
req->set_offset_bytes(offset_bytes);
SendMessage(message);
SendOrQueueMessage(message);
}
void SpotifyServer::Search(const QString& text, int limit, int limit_album) {
@ -241,7 +236,7 @@ void SpotifyServer::Search(const QString& text, int limit, int limit_album) {
req->set_query(DataCommaSizeFromQString(text));
req->set_limit(limit);
req->set_limit_album(limit_album);
SendMessage(message);
SendOrQueueMessage(message);
}
void SpotifyServer::LoadImage(const QString& id) {
@ -249,7 +244,7 @@ void SpotifyServer::LoadImage(const QString& id) {
pb::spotify::ImageRequest* req = message.mutable_image_request();
req->set_id(DataCommaSizeFromQString(id));
SendMessage(message);
SendOrQueueMessage(message);
}
void SpotifyServer::AlbumBrowse(const QString& uri) {
@ -257,5 +252,5 @@ void SpotifyServer::AlbumBrowse(const QString& uri) {
pb::spotify::BrowseAlbumRequest* req = message.mutable_browse_album_request();
req->set_uri(DataCommaSizeFromQString(uri));
SendMessage(message);
SendOrQueueMessage(message);
}

View File

@ -19,16 +19,16 @@
#define SPOTIFYSERVER_H
#include "spotifymessages.pb.h"
#include "core/messagehandler.h"
#include <QImage>
#include <QObject>
class SpotifyMessageHandler;
class QTcpServer;
class QTcpSocket;
class SpotifyServer : public QObject {
class SpotifyServer : public AbstractMessageHandler<pb::spotify::Message> {
Q_OBJECT
public:
@ -70,18 +70,18 @@ signals:
void SyncPlaylistProgress(const pb::spotify::SyncPlaylistProgress& progress);
void AlbumBrowseResults(const pb::spotify::BrowseAlbumResponse& response);
protected:
void MessageArrived(const pb::spotify::Message& message);
private slots:
void NewConnection();
void HandleMessage(const pb::spotify::Message& message);
private:
void LoadPlaylist(pb::spotify::PlaylistType type, int index = -1);
void SyncPlaylist(pb::spotify::PlaylistType type, int index, bool offline);
void SendMessage(const pb::spotify::Message& message);
void SendOrQueueMessage(const pb::spotify::Message& message);
QTcpServer* server_;
QTcpSocket* protocol_socket_;
SpotifyMessageHandler* handler_;
bool logged_in_;
QList<pb::spotify::Message> queued_login_messages_;

View File

@ -2,7 +2,6 @@
#include "config.h"
#include "internetmodel.h"
#include "spotifyblobdownloader.h"
#include "spotifymessagehandler.h"
#include "spotifyserver.h"
#include "spotifyservice.h"
#include "spotifysearchplaylisttype.h"