From c9d5b8100b65a64d46fc1c5dc46ca88379b9ea6c Mon Sep 17 00:00:00 2001 From: John Maguire Date: Thu, 6 Jan 2011 15:09:09 +0000 Subject: [PATCH] More remote work. Clementine now requires QJson. --- CMakeLists.txt | 2 + src/CMakeLists.txt | 1 + src/main.cpp | 7 ++-- src/remote/httpconnection.cpp | 78 +++++++++++++++++++++++++++++++++-- src/remote/httpconnection.h | 17 +++++++- src/remote/httpserver.cpp | 7 ++-- src/remote/httpserver.h | 6 ++- 7 files changed, 106 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 54370a42e..7d6c39c20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,7 @@ pkg_check_modules(PLIST libplist) pkg_check_modules(USBMUXD libusbmuxd) pkg_check_modules(LIBMTP libmtp>=1.0) pkg_check_modules(INDICATEQT indicate-qt) +pkg_check_modules(QJSON QJson) if (WIN32) find_package(ZLIB REQUIRED) @@ -111,6 +112,7 @@ include_directories(${GLIB_INCLUDE_DIRS}) include_directories(${GLIBCONFIG_INCLUDE_DIRS}) include_directories(${LIBXML_INCLUDE_DIRS}) include_directories("3rdparty/universalchardet") +include_directories(${QJSON_INCLUDE_DIRS}) # Remove GLU and GL from the link line - they're not really required # and don't exist on my mingw toolchain diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1a55dc0b3..d7a2869a5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -814,6 +814,7 @@ target_link_libraries(clementine_lib ${QTSINGLECOREAPPLICATION_LIBRARIES} ${QTIOCOMPRESSOR_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} + ${QJSON_LIBRARIES} dl z ) diff --git a/src/main.cpp b/src/main.cpp index 2849a4d50..9f80c694f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -319,10 +319,11 @@ int main(int argc, char *argv[]) { Zeroconf* zeroconf = Zeroconf::GetZeroconf(); if (zeroconf) { - HttpServer* server = new HttpServer; - server->Listen(QHostAddress::Any, 12345); + HttpServer* server = new HttpServer(&player); + int port = 12345; + while (!server->Listen(QHostAddress::Any, port)) { ++port; } - zeroconf->Publish("local", "_clementine._tcp", "Clementine", 12345); + zeroconf->Publish("local", "_clementine._tcp", "Clementine", port); } // Window diff --git a/src/remote/httpconnection.cpp b/src/remote/httpconnection.cpp index 4c8569f6b..5ef61131f 100644 --- a/src/remote/httpconnection.cpp +++ b/src/remote/httpconnection.cpp @@ -1,13 +1,27 @@ #include "httpconnection.h" +#include #include #include -HttpConnection::HttpConnection(QTcpSocket* socket) +#include + +#include "core/player.h" +#include "playlist/playlistitem.h" + +HttpConnection::HttpConnection(Player* player, QTcpSocket* socket) : QObject(socket), socket_(socket), - state_(WaitingForRequest) { + state_(WaitingForRequest), + player_(player), + cover_loader_(new AlbumCoverLoader(this)) { connect(socket_, SIGNAL(readyRead()), SLOT(ReadyRead())); + connect(cover_loader_, SIGNAL(ImageLoaded(quint64, const QImage&)), + SLOT(ImageReady(quint64, const QImage&))); + connect(socket_, SIGNAL(disconnected()), socket_, SLOT(deleteLater())); + + cover_loader_->SetDesiredHeight(400); + cover_loader_->SetScaleOutputImage(true); } void HttpConnection::ReadyRead() { @@ -40,6 +54,7 @@ void HttpConnection::ReadyRead() { if (state_ == Error) { socket_->close(); + socket_->deleteLater(); return; } @@ -64,9 +79,64 @@ bool HttpConnection::ParseRequest(const QString& line) { } void HttpConnection::DoRequest() { + PlaylistItemPtr current_item = player_->GetCurrentItem(); + if (current_item) { + quint64 id = cover_loader_->LoadImageAsync(current_item->Metadata()); + pending_tasks_[id] = current_item->Metadata(); + return; + } + + SendHeaders(); + + QVariantMap state = GetState(); + + QJson::Serializer serializer; + QString output = serializer.serialize(state); + socket_->write(output.toUtf8()); + + socket_->disconnectFromHost(); +} + +void HttpConnection::ImageReady(quint64 id, const QImage& image) { + Song song = pending_tasks_.take(id); + SendSongInfo(song, image); +} + +void HttpConnection::SendHeaders() { socket_->write("HTTP/1.0 200 OK\r\n"); socket_->write("Content-type: application/json\r\n"); socket_->write("\r\n"); - socket_->write("{content:'Hello World!'}"); - socket_->close(); +} + +QVariantMap HttpConnection::GetState() { + QVariantMap json; + json.insert("state", player_->GetState()); + json.insert("volume", player_->GetVolume()); + return json; +} + +void HttpConnection::SendSongInfo(Song song, const QImage& cover) { + SendHeaders(); + + QVariantMap metadata; + metadata.insert("title", song.title()); + metadata.insert("album", song.album()); + metadata.insert("artist", song.artist()); + if (!cover.isNull()) { + QByteArray cover_data; + { + QBuffer cover_buffer(&cover_data); + cover_buffer.open(QIODevice::WriteOnly); + cover.save(&cover_buffer, "PNG"); + } + metadata.insert("cover", cover_data.toBase64()); + } + + QVariantMap state = GetState(); + state.insert("song", metadata); + + QJson::Serializer serializer; + QString output = serializer.serialize(state); + socket_->write(output.toUtf8()); + socket_->disconnectFromHost(); } diff --git a/src/remote/httpconnection.h b/src/remote/httpconnection.h index e82981071..44dd91c2f 100644 --- a/src/remote/httpconnection.h +++ b/src/remote/httpconnection.h @@ -3,20 +3,30 @@ #include #include +#include + +#include "core/song.h" class QTcpSocket; +class AlbumCoverLoader; +class Player; + class HttpConnection : public QObject { Q_OBJECT public: - HttpConnection(QTcpSocket* sock); + HttpConnection(Player* player, QTcpSocket* sock); private slots: void ReadyRead(); + void ImageReady(quint64 id, const QImage& image); private: void DoRequest(); bool ParseRequest(const QString& line); + void SendHeaders(); + QVariantMap GetState(); + void SendSongInfo(Song song, const QImage& cover); QTcpSocket* socket_; @@ -30,6 +40,11 @@ class HttpConnection : public QObject { QNetworkAccessManager::Operation method_; QString path_; + + Player* player_; + AlbumCoverLoader* cover_loader_; + + QMap pending_tasks_; }; #endif diff --git a/src/remote/httpserver.cpp b/src/remote/httpserver.cpp index 4ea0970cc..15fa8a2f1 100644 --- a/src/remote/httpserver.cpp +++ b/src/remote/httpserver.cpp @@ -5,8 +5,9 @@ #include "httpconnection.h" -HttpServer::HttpServer(QObject* parent) - : QObject(parent) { +HttpServer::HttpServer(Player* player, QObject* parent) + : QObject(parent), + player_(player) { connect(&server_, SIGNAL(newConnection()), SLOT(NewConnection())); } @@ -17,5 +18,5 @@ bool HttpServer::Listen(const QHostAddress& addr, quint16 port) { void HttpServer::NewConnection() { qDebug() << Q_FUNC_INFO; QTcpSocket* socket = server_.nextPendingConnection(); - new HttpConnection(socket); + new HttpConnection(player_, socket); } diff --git a/src/remote/httpserver.h b/src/remote/httpserver.h index b2f26b389..4d53a84b9 100644 --- a/src/remote/httpserver.h +++ b/src/remote/httpserver.h @@ -3,10 +3,12 @@ #include +class Player; + class HttpServer : public QObject { Q_OBJECT public: - HttpServer(QObject* parent = 0); + HttpServer(Player* player, QObject* parent = 0); bool Listen(const QHostAddress& addr, quint16 port); private slots: @@ -14,6 +16,8 @@ class HttpServer : public QObject { private: QTcpServer server_; + + Player* player_; }; #endif