Massively hacky basic integration to tomahawk.

This commit is contained in:
John Maguire 2011-03-08 21:16:09 +00:00
parent 7ca69e009e
commit 48b7dc3501
5 changed files with 331 additions and 6 deletions

View File

@ -24,22 +24,29 @@
#include <QSocketNotifier>
#include <QTimer>
#include <QtDebug>
#include <QVariant>
#include <gloox/capabilities.h>
#include <gloox/client.h>
#include <gloox/connectionlistener.h>
#include <gloox/connectiontcpclient.h>
#include <gloox/disco.h>
#include <gloox/discohandler.h>
#include <gloox/loghandler.h>
#include <gloox/message.h>
#include <gloox/messagehandler.h>
#include <gloox/rosterlistener.h>
#include <gloox/rostermanager.h>
#include <qjson/parser.h>
namespace xrme {
struct Connection::Private : public gloox::ConnectionListener,
public gloox::LogHandler,
public gloox::RosterListener,
public gloox::DiscoHandler {
public gloox::DiscoHandler,
public gloox::MessageHandler {
Private(Connection* parent)
: parent_(parent),
server_(kDefaultServer),
@ -126,10 +133,13 @@ struct Connection::Private : public gloox::ConnectionListener,
void handleDiscoInfo(const gloox::JID&, const gloox::Disco::Info&, int);
void handleDiscoItems(const gloox::JID&, const gloox::Disco::Items&, int);
void handleDiscoError(const gloox::JID&, const gloox::Error*, int);
// gloox::MessageHandler
void handleMessage(const gloox::Message& message, gloox::MessageSession* session);
};
const char* Connection::Private::kDefaultServer = "talk.google.com";
const char* Connection::Private::kDefaultJIDResource = "xrmeagent";
const char* Connection::Private::kDefaultJIDResource = "tomahawkxrmeagent";
const char* Connection::Private::kDefaultJIDHost = "gmail.com";
@ -245,15 +255,13 @@ bool Connection::Connect() {
d->client_->rosterManager()->registerRosterListener(d.data());
d->client_->logInstance().registerLogHandler(
gloox::LogLevelDebug, gloox::LogAreaAll, d.data());
d->client_->registerMessageHandler(d.data());
// Setup disco
d->client_->disco()->setIdentity("client", "bot");
d->client_->disco()->setVersion(d->agent_name_.toUtf8().constData(), std::string());
d->client_->disco()->addFeature(kXmlnsXrme);
// Tomahawk support
d->client_->disco()->addFeature("tomahawk:player");
d->media_player_extension_ = new MediaPlayerExtension;
d->remote_control_extension_ = new RemoteControlExtension;
d->client_->registerStanzaExtension(d->media_player_extension_);
@ -265,7 +273,13 @@ bool Connection::Connect() {
}
// Set presence
d->client_->setPresence(gloox::Presence::Available, -128);
d->client_->setPresence(gloox::Presence::Available, 1);
// Tomahawk support
d->client_->disco()->addFeature("tomahawk:player");
gloox::Capabilities* caps = new gloox::Capabilities;
caps->setNode("http://tomahawk-player.org/");
d->client_->presence().addExtension(caps);
d->client_->setSASLMechanisms(gloox::SaslMechGoogleToken);
@ -529,4 +543,19 @@ void Connection::Private::handleDiscoError(
}
}
void Connection::Private::handleMessage(const gloox::Message& message, gloox::MessageSession* session) {
qDebug() << Q_FUNC_INFO << message.tag()->xml().c_str();
const std::string body = message.body();
qDebug() << body.c_str();
QJson::Parser parser;
bool ok = false;
QVariant result = parser.parse(QByteArray::fromRawData(body.data(), body.size()), &ok);
if (ok) {
qDebug() << result;
emit parent_->TomahawkSIPReceived(result);
}
}
} // namespace xrme

View File

@ -137,6 +137,8 @@ signals:
void PeerFound(const xrme::Connection::Peer& peer);
void PeerRemoved(const xrme::Connection::Peer& peer);
void TomahawkSIPReceived(const QVariant& json);
private slots:
void SocketReadyReceive();
void CleanupClient();

View File

@ -941,6 +941,7 @@ if(HAVE_REMOTE)
target_link_libraries(clementine_lib ${GLOOX_LIBRARIES})
target_link_libraries(clementine_lib xrme)
target_link_libraries(clementine_lib portfwd)
target_link_libraries(clementine_lib qjson)
link_directories(${GLOOX_LIBRARY_DIRS})
endif(HAVE_REMOTE)

View File

@ -22,9 +22,14 @@
#include "playlist/playlist.h"
#include "playlist/playlistmanager.h"
#include <QDataStream>
#include <QHostAddress>
#include <QSettings>
#include <QTimer>
#include <qjson/parser.h>
#include <qjson/serializer.h>
Remote::Remote(Player* player, QObject* parent)
: QObject(parent),
player_(player),
@ -44,6 +49,10 @@ Remote::Remote(Player* player, QObject* parent)
connect(player_, SIGNAL(Seeked(qlonglong)), SLOT(SetStateChanged()));
connect(player_->playlists(), SIGNAL(CurrentSongChanged(Song)), SLOT(SetStateChanged()));
connect(connection_,
SIGNAL(TomahawkSIPReceived(const QVariant&)),
SLOT(TomahawkSIPReceived(const QVariant&)));
ReloadSettings();
}
@ -163,3 +172,266 @@ void Remote::ArtLoaded(const Song&, const QString&, const QImage& image) {
last_image_ = image;
AlbumArtChanged();
}
void Remote::TomahawkSIPReceived(const QVariant& json) {
QVariantMap message = json.toMap();
const QString& ip = message["ip"].toString();
const QString& key = message["key"].toString();
const quint16 port = message["port"].toUInt();
const QString& unique_name = message["uniqname"].toString();
const bool visible = message["visible"].toBool();
QTcpSocket* socket = new QTcpSocket(this);
socket->connectToHost(ip, port);
connect(socket, SIGNAL(connected()), SLOT(TomahawkConnected()));
connect(socket, SIGNAL(disconnected()), SLOT(TomahawkDisconnected()));
connect(socket, SIGNAL(readyRead()), SLOT(TomahawkReadyRead()));
connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
SLOT(TomahawkError(QAbstractSocket::SocketError)));
qDebug() << Q_FUNC_INFO << "connecting to" << ip << port;
TomahawkConnection* connection = new TomahawkConnection;
connection->key = key;
connection->unique_name = unique_name;
connection->visible = visible;
connection->num_bytes = -1;
tomahawk_connections_[socket] = connection;
}
void Remote::TomahawkConnected() {
qDebug() << Q_FUNC_INFO;
QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());
Q_ASSERT(socket);
TomahawkConnection* connection = tomahawk_connections_[socket];
QVariantMap map;
map["conntype"] = "accept-offer";
map["key"] = connection->key;
map["port"] = 12345;
map["nodeid"] = "foobar";
map["controlid"] = "foobarbaz";
QJson::Serializer serialiser;
const QByteArray& serialised = serialiser.serialize(map);
quint32 length = serialised.length();
// Send offer acceptance.
{
QDataStream stream(socket);
stream << (quint32)length;
stream << (quint8)(128 | 2);
stream.writeRawData(serialised.constData(), serialised.length());
qDebug() << "now:" << socket->bytesToWrite();
}
{
QDataStream stream(socket);
stream << (quint32)2;
stream << (quint8)(128 | 1);
stream.writeRawData("ok", 2);
}
// Send ping.
{
QDataStream stream(socket);
stream << (quint32)0;
stream << (quint8)(32);
}
/*
{
QVariantMap json;
json["method"] = "dbsync-offer";
json["key"] = "abcdefg";
const QByteArray& request = serialiser.serialize(json);
QDataStream stream(socket);
stream << (quint32)request.length();
stream << (quint8)2; // JSON
stream.writeRawData(request.constData(), request.length());
}
*/
}
void Remote::TomahawkReadyRead() {
QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());
Q_ASSERT(socket);
TomahawkConnection* connection = tomahawk_connections_[socket];
if (connection->num_bytes == -1) {
// Expecting header.
if (socket->bytesAvailable() >= 5) {
QDataStream stream(socket);
quint32 size;
stream >> size;
quint8 flags;
stream >> flags;
connection->num_bytes = size > 0 ? size : -1;
connection->flags = flags;
if (flags & 32) {
qDebug() << "PING!";
}
if (connection->num_bytes == -1) {
return;
}
} else {
return;
}
}
if (socket->bytesAvailable() < connection->num_bytes) {
return;
}
QByteArray data = socket->readAll();
connection->num_bytes = -1;
qDebug() << Q_FUNC_INFO;
qDebug() << data;
qDebug() << Q_FUNC_INFO << "done";
if (connection->flags & 2) {
QJson::Parser parser;
bool ok = false;
QVariant json = parser.parse(data, &ok);
if (ok) {
QVariantMap map = json.toMap();
QString method = map["method"].toString();
if (method == "dbsync-offer") {
qDebug() << "DBSYNC!";
TomahawkConnection* db_connection = new TomahawkConnection;
db_connection->key = map["key"].toString();
db_connection->num_bytes = -1;
QTcpSocket* sync_socket = new QTcpSocket(this);
sync_socket->connectToHost(socket->peerAddress(), socket->peerPort());
connect(sync_socket, SIGNAL(connected()), SLOT(TomahawkDBConnected()));
connect(sync_socket, SIGNAL(disconnected()), SLOT(TomahawkDBDisconnected()));
connect(sync_socket, SIGNAL(readyRead()), SLOT(TomahawkDBReadyRead()));
tomahawk_connections_[sync_socket] = db_connection;
}
}
}
}
void Remote::TomahawkDBConnected() {
qDebug() << Q_FUNC_INFO;
qDebug() << Q_FUNC_INFO;
QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());
Q_ASSERT(socket);
TomahawkConnection* connection = tomahawk_connections_[socket];
{
QVariantMap request;
request["method"] = "accept-offer";
request["key"] = connection->key;
QJson::Serializer serialiser;
const QByteArray& json = serialiser.serialize(request);
QDataStream stream(socket);
stream << (quint32)json.length();
stream << (quint8)(2);
stream.writeRawData(json.constData(), json.length());
}
{
QDataStream stream(socket);
stream << (quint32)2;
stream << (quint8)(128 | 1); // SETUP | RAW
stream.writeRawData("ok", 2);
}
{
QVariantMap request;
request["command"] = "addfiles";
QVariantMap file;
file["url"] = "http://data.clementine-player.org/rainymood";
file["mtime"] = 123456;
file["size"] = 1e7;
file["hash"] = "abcdefg";
file["mimetype"] = "audio/mpeg";
file["duration"] = 1000;
file["bitrate"] = 128;
file["artist"] = "foo";
file["album"] = "bar";
file["track"] = "Rain!";
file["albumpos"] = 1;
file["year"] = 2011;
QVariantList files;
files << file;
request["files"] = files;
QJson::Serializer serialiser;
const QByteArray& json = serialiser.serialize(request);
qDebug() << json;
QDataStream stream(socket);
stream << (quint32)json.length();
stream << (quint8)(16 | 2); // DBOP | JSON
stream.writeRawData(json.constData(), json.length());
}
}
void Remote::TomahawkDBReadyRead() {
qDebug() << Q_FUNC_INFO;
QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender());
Q_ASSERT(socket);
TomahawkConnection* connection = tomahawk_connections_[socket];
if (connection->num_bytes == -1) {
// Expecting header.
if (socket->bytesAvailable() >= 5) {
QDataStream stream(socket);
quint32 size;
stream >> size;
quint8 flags;
stream >> flags;
connection->num_bytes = size > 0 ? size : -1;
connection->flags = flags;
if (flags & 32) {
qDebug() << "PING!";
}
if (connection->num_bytes == -1) {
return;
}
} else {
return;
}
}
if (socket->bytesAvailable() < connection->num_bytes) {
return;
}
QByteArray data = socket->readAll();
connection->num_bytes = -1;
qDebug() << Q_FUNC_INFO;
qDebug() << data;
qDebug() << Q_FUNC_INFO << "done";
if (connection->flags & 2) {
QJson::Parser parser;
bool ok = false;
QVariant json = parser.parse(data, &ok);
if (ok) {
QVariantMap map = json.toMap();
qDebug() << map;
}
}
}
void Remote::TomahawkDBDisconnected() {
qDebug() << Q_FUNC_INFO;
}
void Remote::TomahawkDisconnected() {
qDebug() << Q_FUNC_INFO;
}
void Remote::TomahawkError(QAbstractSocket::SocketError error) {
qDebug() << Q_FUNC_INFO << error;
}

View File

@ -21,6 +21,7 @@
#include "core/song.h"
#include <QObject>
#include <QTcpSocket>
#include <xrme/connection.h>
#include <xrme/mediaplayerinterface.h>
@ -59,6 +60,16 @@ private slots:
void SetStateChanged();
void TomahawkSIPReceived(const QVariant& json);
void TomahawkConnected();
void TomahawkDisconnected();
void TomahawkReadyRead();
void TomahawkError(QAbstractSocket::SocketError);
void TomahawkDBConnected();
void TomahawkDBDisconnected();
void TomahawkDBReadyRead();
private:
bool is_configured() const;
@ -66,6 +77,16 @@ private:
Player* player_;
xrme::Connection* connection_;
struct TomahawkConnection {
QString key;
QString unique_name;
bool visible;
QTcpSocket* socket;
int num_bytes;
quint8 flags;
};
QMap<QTcpSocket*, TomahawkConnection*> tomahawk_connections_;
QImage last_image_;
int retry_count_;
};