Merge branch 'master' of https://code.google.com/r/asfa194-clementineremote into remote
This commit is contained in:
commit
b7b7e97455
|
@ -423,6 +423,7 @@ add_subdirectory(tools/ultimate_lyrics_parser)
|
||||||
add_subdirectory(ext/libclementine-common)
|
add_subdirectory(ext/libclementine-common)
|
||||||
add_subdirectory(ext/libclementine-tagreader)
|
add_subdirectory(ext/libclementine-tagreader)
|
||||||
add_subdirectory(ext/clementine-tagreader)
|
add_subdirectory(ext/clementine-tagreader)
|
||||||
|
add_subdirectory(ext/libclementine-remote)
|
||||||
|
|
||||||
option(WITH_DEBIAN OFF)
|
option(WITH_DEBIAN OFF)
|
||||||
if(WITH_DEBIAN)
|
if(WITH_DEBIAN)
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
include_directories(${PROTOBUF_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
set(MESSAGES
|
||||||
|
remotecontrolmessages.proto
|
||||||
|
)
|
||||||
|
|
||||||
|
protobuf_generate_cpp(PROTO_SOURCES PROTO_HEADERS ${MESSAGES})
|
||||||
|
|
||||||
|
add_library(libclementine-remote STATIC
|
||||||
|
${PROTO_SOURCES}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(libclementine-remote
|
||||||
|
libclementine-common
|
||||||
|
)
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
package pb.remote;
|
||||||
|
|
||||||
|
enum MsgType {
|
||||||
|
// Messages generally send from client to server
|
||||||
|
CONNECT = 0;
|
||||||
|
DISCONNECT = 1;
|
||||||
|
REQUEST_PLAYLISTS = 2;
|
||||||
|
REQUEST_PLAYLIST_SONGS = 3;
|
||||||
|
CHANGE_SONG = 4;
|
||||||
|
SET_VOLUME = 5;
|
||||||
|
|
||||||
|
// Messages send by both
|
||||||
|
PLAY = 20;
|
||||||
|
PLAYPAUSE = 21;
|
||||||
|
PAUSE = 22;
|
||||||
|
STOP = 23;
|
||||||
|
NEXT = 24;
|
||||||
|
PREV = 25;
|
||||||
|
|
||||||
|
// Messages send from server to client
|
||||||
|
INFOS = 40;
|
||||||
|
CURRENT_METAINFOS = 41;
|
||||||
|
PLAYLISTS = 42;
|
||||||
|
PLAYLIST_SONGS = 43;
|
||||||
|
KEEP_ALIVE = 44;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EngineState {
|
||||||
|
Empty = 0;
|
||||||
|
Idle = 1;
|
||||||
|
Playing = 2;
|
||||||
|
Paused = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Message {
|
||||||
|
required MsgType msgType = 1;
|
||||||
|
|
||||||
|
optional EngineState state = 2;
|
||||||
|
optional ClementineInfos infos = 3;
|
||||||
|
optional SongMetadata currentSong = 4;
|
||||||
|
optional int32 volume = 5;
|
||||||
|
repeated Playlist playlists = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ClementineInfos {
|
||||||
|
optional string version = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SongMetadata {
|
||||||
|
optional int32 id = 1;
|
||||||
|
optional int32 index = 2;
|
||||||
|
optional string title = 3;
|
||||||
|
optional string album = 4;
|
||||||
|
optional string artist = 5;
|
||||||
|
optional string albumartist = 6;
|
||||||
|
optional int32 track = 7;
|
||||||
|
optional int32 disc = 8;
|
||||||
|
optional string pretty_year = 9;
|
||||||
|
optional string genre = 10;
|
||||||
|
optional int32 playcount = 11;
|
||||||
|
optional string pretty_length = 12;
|
||||||
|
optional string art = 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Playlist {
|
||||||
|
optional int32 id = 1;
|
||||||
|
optional string name = 2;
|
||||||
|
optional int32 item_count = 3;
|
||||||
|
optional bool active = 4;
|
||||||
|
|
||||||
|
repeated SongMetadata songs = 10;
|
||||||
|
}
|
||||||
|
|
|
@ -62,6 +62,8 @@ endif(HAVE_BREAKPAD)
|
||||||
include_directories(${CMAKE_SOURCE_DIR}/ext/libclementine-common)
|
include_directories(${CMAKE_SOURCE_DIR}/ext/libclementine-common)
|
||||||
include_directories(${CMAKE_SOURCE_DIR}/ext/libclementine-tagreader)
|
include_directories(${CMAKE_SOURCE_DIR}/ext/libclementine-tagreader)
|
||||||
include_directories(${CMAKE_BINARY_DIR}/ext/libclementine-tagreader)
|
include_directories(${CMAKE_BINARY_DIR}/ext/libclementine-tagreader)
|
||||||
|
include_directories(${CMAKE_SOURCE_DIR}/ext/libclementine-remote)
|
||||||
|
include_directories(${CMAKE_BINARY_DIR}/ext/libclementine-remote)
|
||||||
|
|
||||||
cmake_policy(SET CMP0011 NEW)
|
cmake_policy(SET CMP0011 NEW)
|
||||||
include(../cmake/ParseArguments.cmake)
|
include(../cmake/ParseArguments.cmake)
|
||||||
|
@ -212,6 +214,11 @@ set(SOURCES
|
||||||
musicbrainz/musicbrainzclient.cpp
|
musicbrainz/musicbrainzclient.cpp
|
||||||
musicbrainz/tagfetcher.cpp
|
musicbrainz/tagfetcher.cpp
|
||||||
|
|
||||||
|
networkremote/networkremotehelper.cpp
|
||||||
|
networkremote/networkremote.cpp
|
||||||
|
networkremote/incomingdataparser.cpp
|
||||||
|
networkremote/outgoingdatacreator.cpp
|
||||||
|
|
||||||
playlist/dynamicplaylistcontrols.cpp
|
playlist/dynamicplaylistcontrols.cpp
|
||||||
playlist/playlist.cpp
|
playlist/playlist.cpp
|
||||||
playlist/playlistbackend.cpp
|
playlist/playlistbackend.cpp
|
||||||
|
@ -323,6 +330,7 @@ set(SOURCES
|
||||||
ui/iconloader.cpp
|
ui/iconloader.cpp
|
||||||
ui/mainwindow.cpp
|
ui/mainwindow.cpp
|
||||||
ui/networkproxysettingspage.cpp
|
ui/networkproxysettingspage.cpp
|
||||||
|
ui/networkremotesettingspage.cpp
|
||||||
ui/notificationssettingspage.cpp
|
ui/notificationssettingspage.cpp
|
||||||
ui/organisedialog.cpp
|
ui/organisedialog.cpp
|
||||||
ui/organiseerrordialog.cpp
|
ui/organiseerrordialog.cpp
|
||||||
|
@ -484,6 +492,11 @@ set(HEADERS
|
||||||
musicbrainz/musicbrainzclient.h
|
musicbrainz/musicbrainzclient.h
|
||||||
musicbrainz/tagfetcher.h
|
musicbrainz/tagfetcher.h
|
||||||
|
|
||||||
|
networkremote/networkremotehelper.h
|
||||||
|
networkremote/networkremote.h
|
||||||
|
networkremote/incomingdataparser.h
|
||||||
|
networkremote/outgoingdatacreator.h
|
||||||
|
|
||||||
playlist/dynamicplaylistcontrols.h
|
playlist/dynamicplaylistcontrols.h
|
||||||
playlist/playlist.h
|
playlist/playlist.h
|
||||||
playlist/playlistbackend.h
|
playlist/playlistbackend.h
|
||||||
|
@ -580,6 +593,7 @@ set(HEADERS
|
||||||
ui/globalshortcutssettingspage.h
|
ui/globalshortcutssettingspage.h
|
||||||
ui/mainwindow.h
|
ui/mainwindow.h
|
||||||
ui/networkproxysettingspage.h
|
ui/networkproxysettingspage.h
|
||||||
|
ui/networkremotesettingspage.h
|
||||||
ui/notificationssettingspage.h
|
ui/notificationssettingspage.h
|
||||||
ui/organisedialog.h
|
ui/organisedialog.h
|
||||||
ui/organiseerrordialog.h
|
ui/organiseerrordialog.h
|
||||||
|
@ -697,6 +711,7 @@ set(UI
|
||||||
ui/globalshortcutssettingspage.ui
|
ui/globalshortcutssettingspage.ui
|
||||||
ui/mainwindow.ui
|
ui/mainwindow.ui
|
||||||
ui/networkproxysettingspage.ui
|
ui/networkproxysettingspage.ui
|
||||||
|
ui/networkremotesettingspage.ui
|
||||||
ui/notificationssettingspage.ui
|
ui/notificationssettingspage.ui
|
||||||
ui/organisedialog.ui
|
ui/organisedialog.ui
|
||||||
ui/organiseerrordialog.ui
|
ui/organiseerrordialog.ui
|
||||||
|
@ -1125,6 +1140,7 @@ add_dependencies(clementine_lib pot)
|
||||||
target_link_libraries(clementine_lib
|
target_link_libraries(clementine_lib
|
||||||
libclementine-common
|
libclementine-common
|
||||||
libclementine-tagreader
|
libclementine-tagreader
|
||||||
|
libclementine-remote
|
||||||
${SHA2_LIBRARIES}
|
${SHA2_LIBRARIES}
|
||||||
${TAGLIB_LIBRARIES}
|
${TAGLIB_LIBRARIES}
|
||||||
${MYGPOQT_LIBRARIES}
|
${MYGPOQT_LIBRARIES}
|
||||||
|
|
|
@ -30,6 +30,8 @@
|
||||||
#include "globalsearch/globalsearch.h"
|
#include "globalsearch/globalsearch.h"
|
||||||
#include "library/library.h"
|
#include "library/library.h"
|
||||||
#include "library/librarybackend.h"
|
#include "library/librarybackend.h"
|
||||||
|
#include "networkremote/networkremote.h"
|
||||||
|
#include "networkremote/networkremotehelper.h"
|
||||||
#include "playlist/playlistbackend.h"
|
#include "playlist/playlistbackend.h"
|
||||||
#include "playlist/playlistmanager.h"
|
#include "playlist/playlistmanager.h"
|
||||||
#include "podcasts/gpoddersync.h"
|
#include "podcasts/gpoddersync.h"
|
||||||
|
@ -63,7 +65,9 @@ Application::Application(QObject* parent)
|
||||||
podcast_downloader_(NULL),
|
podcast_downloader_(NULL),
|
||||||
gpodder_sync_(NULL),
|
gpodder_sync_(NULL),
|
||||||
moodbar_loader_(NULL),
|
moodbar_loader_(NULL),
|
||||||
moodbar_controller_(NULL)
|
moodbar_controller_(NULL),
|
||||||
|
network_remote_(NULL),
|
||||||
|
network_remote_helper_(NULL)
|
||||||
{
|
{
|
||||||
tag_reader_client_ = new TagReaderClient(this);
|
tag_reader_client_ = new TagReaderClient(this);
|
||||||
MoveToNewThread(tag_reader_client_);
|
MoveToNewThread(tag_reader_client_);
|
||||||
|
@ -100,6 +104,13 @@ Application::Application(QObject* parent)
|
||||||
moodbar_controller_ = new MoodbarController(this, this);
|
moodbar_controller_ = new MoodbarController(this, this);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Network Remote
|
||||||
|
network_remote_ = new NetworkRemote(this);
|
||||||
|
MoveToNewThread(network_remote_);
|
||||||
|
|
||||||
|
network_remote_helper_ = new NetworkRemoteHelper(this);
|
||||||
|
network_remote_helper_->StartServer();
|
||||||
|
|
||||||
library_->Init();
|
library_->Init();
|
||||||
|
|
||||||
DoInAMinuteOrSo(database_, SLOT(DoBackup()));
|
DoInAMinuteOrSo(database_, SLOT(DoBackup()));
|
||||||
|
|
|
@ -36,6 +36,8 @@ class LibraryBackend;
|
||||||
class LibraryModel;
|
class LibraryModel;
|
||||||
class MoodbarController;
|
class MoodbarController;
|
||||||
class MoodbarLoader;
|
class MoodbarLoader;
|
||||||
|
class NetworkRemote;
|
||||||
|
class NetworkRemoteHelper;
|
||||||
class Player;
|
class Player;
|
||||||
class PlaylistBackend;
|
class PlaylistBackend;
|
||||||
class PodcastDownloader;
|
class PodcastDownloader;
|
||||||
|
@ -73,6 +75,8 @@ public:
|
||||||
GPodderSync* gpodder_sync() const { return gpodder_sync_; }
|
GPodderSync* gpodder_sync() const { return gpodder_sync_; }
|
||||||
MoodbarLoader* moodbar_loader() const { return moodbar_loader_; }
|
MoodbarLoader* moodbar_loader() const { return moodbar_loader_; }
|
||||||
MoodbarController* moodbar_controller() const { return moodbar_controller_; }
|
MoodbarController* moodbar_controller() const { return moodbar_controller_; }
|
||||||
|
NetworkRemote* network_remote() const { return network_remote_; }
|
||||||
|
NetworkRemoteHelper* network_remote_helper() const { return network_remote_helper_; }
|
||||||
|
|
||||||
LibraryBackend* library_backend() const;
|
LibraryBackend* library_backend() const;
|
||||||
LibraryModel* library_model() const;
|
LibraryModel* library_model() const;
|
||||||
|
@ -111,6 +115,8 @@ private:
|
||||||
GPodderSync* gpodder_sync_;
|
GPodderSync* gpodder_sync_;
|
||||||
MoodbarLoader* moodbar_loader_;
|
MoodbarLoader* moodbar_loader_;
|
||||||
MoodbarController* moodbar_controller_;
|
MoodbarController* moodbar_controller_;
|
||||||
|
NetworkRemote* network_remote_;
|
||||||
|
NetworkRemoteHelper* network_remote_helper_;
|
||||||
|
|
||||||
QList<QObject*> objects_in_threads_;
|
QList<QObject*> objects_in_threads_;
|
||||||
QList<QThread*> threads_;
|
QList<QThread*> threads_;
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2012, Andreas Muttscheller <asfa194@gmail.com>
|
||||||
|
|
||||||
|
Clementine is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Clementine is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "incomingdataparser.h"
|
||||||
|
#include "core/logging.h"
|
||||||
|
#include "engines/enginebase.h"
|
||||||
|
#include "playlist/playlistmanager.h"
|
||||||
|
|
||||||
|
IncomingDataParser::IncomingDataParser(Application* app)
|
||||||
|
:app_(app)
|
||||||
|
{
|
||||||
|
// Connect all the signals
|
||||||
|
// due the player is in a different thread, we cannot access these functions directly
|
||||||
|
connect(this, SIGNAL(Play()),
|
||||||
|
app_->player(), SLOT(Play()));
|
||||||
|
connect(this, SIGNAL(PlayPause()),
|
||||||
|
app_->player(), SLOT(PlayPause()));
|
||||||
|
connect(this, SIGNAL(Pause()),
|
||||||
|
app_->player(), SLOT(Pause()));
|
||||||
|
connect(this, SIGNAL(Stop()),
|
||||||
|
app_->player(), SLOT(Stop()));
|
||||||
|
connect(this, SIGNAL(Next()),
|
||||||
|
app_->player(), SLOT(Next()));
|
||||||
|
connect(this, SIGNAL(Previous()),
|
||||||
|
app_->player(), SLOT(Previous()));
|
||||||
|
connect(this, SIGNAL(SetVolume(int)),
|
||||||
|
app_->player(), SLOT(SetVolume(int)));
|
||||||
|
connect(this, SIGNAL(PlayAt(int,Engine::TrackChangeFlags,bool)),
|
||||||
|
app_->player(), SLOT(PlayAt(int,Engine::TrackChangeFlags,bool)));
|
||||||
|
connect(this, SIGNAL(SetActivePlaylist(int)),
|
||||||
|
app_->playlist_manager(), SLOT(SetActivePlaylist(int)));
|
||||||
|
}
|
||||||
|
|
||||||
|
IncomingDataParser::~IncomingDataParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IncomingDataParser::close_connection() {
|
||||||
|
return close_connection_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IncomingDataParser::Parse(const QByteArray& b64_data) {
|
||||||
|
close_connection_ = false;
|
||||||
|
QByteArray pb_data = QByteArray::fromBase64(b64_data);
|
||||||
|
|
||||||
|
// Parse the incoming data
|
||||||
|
pb::remote::Message msg;
|
||||||
|
if (!msg.ParseFromArray(pb_data.constData(), pb_data.size())) {
|
||||||
|
qLog(Info) << "Couldn't parse data";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now check what's to do
|
||||||
|
switch (msg.msgtype()) {
|
||||||
|
case pb::remote::CONNECT: emit SendClementineInfos();
|
||||||
|
emit SendFirstData();
|
||||||
|
break;
|
||||||
|
case pb::remote::DISCONNECT: close_connection_ = true;
|
||||||
|
break;
|
||||||
|
case pb::remote::REQUEST_PLAYLISTS: emit SendAllPlaylists();
|
||||||
|
break;
|
||||||
|
case pb::remote::REQUEST_PLAYLIST_SONGS: GetPlaylistSongs(&msg);
|
||||||
|
break;
|
||||||
|
case pb::remote::SET_VOLUME: emit SetVolume(msg.volume());
|
||||||
|
break;
|
||||||
|
case pb::remote::PLAY: emit Play();
|
||||||
|
break;
|
||||||
|
case pb::remote::PLAYPAUSE: emit PlayPause();
|
||||||
|
break;
|
||||||
|
case pb::remote::PAUSE: emit Pause();
|
||||||
|
break;
|
||||||
|
case pb::remote::STOP: emit Stop();
|
||||||
|
break;
|
||||||
|
case pb::remote::NEXT: emit Next();
|
||||||
|
break;
|
||||||
|
case pb::remote::PREV: emit Previous();
|
||||||
|
break;
|
||||||
|
case pb::remote::CHANGE_SONG: ChangeSong(&msg);
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IncomingDataParser::GetPlaylistSongs(pb::remote::Message* msg) {
|
||||||
|
// Check if we got a playlist
|
||||||
|
if (msg->playlists_size() == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the first entry and send the songs
|
||||||
|
pb::remote::Playlist playlist = msg->playlists(0);
|
||||||
|
emit SendPlaylistSongs(playlist.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
void IncomingDataParser::ChangeSong(pb::remote::Message* msg) {
|
||||||
|
// Check if we got a song
|
||||||
|
if (msg->playlists_size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the first entry and check if there is a song
|
||||||
|
pb::remote::Playlist playlist = msg->playlists(0);
|
||||||
|
if (playlist.songs_size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pb::remote::SongMetadata song = playlist.songs(0);
|
||||||
|
|
||||||
|
// Check if we need to change the playlist
|
||||||
|
if (playlist.id() != app_->playlist_manager()->active_id()) {
|
||||||
|
emit SetActivePlaylist(playlist.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play the selected song
|
||||||
|
emit PlayAt(song.index(), Engine::Manual, false);
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
#ifndef INCOMINGDATAPARSER_H
|
||||||
|
#define INCOMINGDATAPARSER_H
|
||||||
|
|
||||||
|
#include "core/player.h"
|
||||||
|
#include "core/application.h"
|
||||||
|
#include "remotecontrolmessages.pb.h"
|
||||||
|
|
||||||
|
class IncomingDataParser : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
IncomingDataParser(Application* app);
|
||||||
|
~IncomingDataParser();
|
||||||
|
|
||||||
|
void Parse(const QByteArray& pb_data);
|
||||||
|
bool close_connection();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void SendClementineInfos();
|
||||||
|
void SendFirstData();
|
||||||
|
void SendAllPlaylists();
|
||||||
|
void SendPlaylistSongs(int id);
|
||||||
|
|
||||||
|
void Play();
|
||||||
|
void PlayPause();
|
||||||
|
void Pause();
|
||||||
|
void Stop();
|
||||||
|
void Next();
|
||||||
|
void Previous();
|
||||||
|
void SetVolume(int volume);
|
||||||
|
void PlayAt(int i, Engine::TrackChangeFlags change, bool reshuffle);
|
||||||
|
void SetActivePlaylist(int id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Application* app_;
|
||||||
|
bool close_connection_;
|
||||||
|
|
||||||
|
void GetPlaylistSongs(pb::remote::Message* msg);
|
||||||
|
void ChangeSong(pb::remote::Message* msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // INCOMINGDATAPARSER_H
|
|
@ -0,0 +1,144 @@
|
||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2012, Andreas Muttscheller <asfa194@gmail.com>
|
||||||
|
|
||||||
|
Clementine is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Clementine is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "core/logging.h"
|
||||||
|
#include "covers/currentartloader.h"
|
||||||
|
#include "playlist/playlistmanager.h"
|
||||||
|
|
||||||
|
#include "networkremote.h"
|
||||||
|
|
||||||
|
#include <QDataStream>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
const char* NetworkRemote::kSettingsGroup = "NetworkRemote";
|
||||||
|
const int NetworkRemote::kDefaultServerPort = 5500;
|
||||||
|
|
||||||
|
NetworkRemote::NetworkRemote(Application* app)
|
||||||
|
: app_(app)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
NetworkRemote::~NetworkRemote() {
|
||||||
|
server_->close();
|
||||||
|
delete incoming_data_parser_;
|
||||||
|
delete outgoing_data_creator_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkRemote::ReadSettings() {
|
||||||
|
QSettings s;
|
||||||
|
|
||||||
|
s.beginGroup(NetworkRemote::kSettingsGroup);
|
||||||
|
use_remote_ = s.value("use_remote").toBool();
|
||||||
|
port_ = s.value("port").toInt();
|
||||||
|
if (port_ == 0) {
|
||||||
|
port_ = kDefaultServerPort;
|
||||||
|
}
|
||||||
|
s.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkRemote::SetupServer() {
|
||||||
|
server_ = new QTcpServer();
|
||||||
|
incoming_data_parser_ = new IncomingDataParser(app_);
|
||||||
|
outgoing_data_creator_ = new OutgoingDataCreator(app_);
|
||||||
|
|
||||||
|
connect(app_->current_art_loader(),
|
||||||
|
SIGNAL(ArtLoaded(const Song&, const QString&, const QImage&)),
|
||||||
|
outgoing_data_creator_,
|
||||||
|
SLOT(CurrentSongChanged(const Song&, const QString&, const QImage&)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkRemote::StartServer() {
|
||||||
|
if (!app_) {
|
||||||
|
qLog(Error) << "Start Server called without having an application!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Check if user desires to start a network remote server
|
||||||
|
ReadSettings();
|
||||||
|
if (!use_remote_) {
|
||||||
|
qLog(Info) << "Network Remote deactivated";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qLog(Info) << "Starting network remote";
|
||||||
|
|
||||||
|
clients_ = NULL;
|
||||||
|
|
||||||
|
connect(server_, SIGNAL(newConnection()), this, SLOT(AcceptConnection()));
|
||||||
|
|
||||||
|
server_->listen(QHostAddress::Any, port_);
|
||||||
|
|
||||||
|
qLog(Info) << "Listening on port " << port_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkRemote::StopServer() {
|
||||||
|
if (server_->isListening()) {
|
||||||
|
server_->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkRemote::ReloadSettings() {
|
||||||
|
StopServer();
|
||||||
|
StartServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkRemote::AcceptConnection() {
|
||||||
|
if (!clients_) {
|
||||||
|
// Create a new QList with clients
|
||||||
|
clients_ = new QList<QTcpSocket*>();
|
||||||
|
outgoing_data_creator_->SetClients(clients_);
|
||||||
|
|
||||||
|
// Setting up the signals, but only once
|
||||||
|
connect(incoming_data_parser_, SIGNAL(SendClementineInfos()),
|
||||||
|
outgoing_data_creator_, SLOT(SendClementineInfos()));
|
||||||
|
connect(incoming_data_parser_, SIGNAL(SendFirstData()),
|
||||||
|
outgoing_data_creator_, SLOT(SendFirstData()));
|
||||||
|
connect(incoming_data_parser_, SIGNAL(SendAllPlaylists()),
|
||||||
|
outgoing_data_creator_, SLOT(SendAllPlaylists()));
|
||||||
|
connect(incoming_data_parser_, SIGNAL(SendPlaylistSongs(int)),
|
||||||
|
outgoing_data_creator_, SLOT(SendPlaylistSongs(int)));
|
||||||
|
|
||||||
|
connect(app_->playlist_manager(), SIGNAL(ActiveChanged(Playlist*)),
|
||||||
|
outgoing_data_creator_, SLOT(ActiveChanged(Playlist*)));
|
||||||
|
connect(app_->playlist_manager(), SIGNAL(PlaylistChanged(Playlist*)),
|
||||||
|
outgoing_data_creator_, SLOT(PlaylistChanged(Playlist*)));
|
||||||
|
|
||||||
|
connect(app_->player(), SIGNAL(VolumeChanged(int)), outgoing_data_creator_,
|
||||||
|
SLOT(VolumeChanged(int)));
|
||||||
|
connect(app_->player()->engine(), SIGNAL(StateChanged(Engine::State)),
|
||||||
|
outgoing_data_creator_, SLOT(StateChanged(Engine::State)));
|
||||||
|
}
|
||||||
|
QTcpSocket* client = server_->nextPendingConnection();
|
||||||
|
|
||||||
|
clients_->push_back(client);
|
||||||
|
|
||||||
|
// Connect to the slot IncomingData when receiving data
|
||||||
|
connect(client, SIGNAL(readyRead()), this, SLOT(IncomingData()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkRemote::IncomingData() {
|
||||||
|
QTcpSocket* client = static_cast<QTcpSocket*>(QObject::sender());
|
||||||
|
|
||||||
|
// Now read all the data from the socket
|
||||||
|
QByteArray data;
|
||||||
|
data = client->readAll();
|
||||||
|
incoming_data_parser_->Parse(data);
|
||||||
|
|
||||||
|
if (incoming_data_parser_->close_connection()) {
|
||||||
|
client->close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
#ifndef NETWORKREMOTE_H
|
||||||
|
#define NETWORKREMOTE_H
|
||||||
|
|
||||||
|
#include <QtNetwork>
|
||||||
|
#include <QTcpServer>
|
||||||
|
#include <QTcpSocket>
|
||||||
|
|
||||||
|
#include "core/player.h"
|
||||||
|
#include "core/application.h"
|
||||||
|
#include "incomingdataparser.h"
|
||||||
|
#include "outgoingdatacreator.h"
|
||||||
|
|
||||||
|
class NetworkRemote : public QThread {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
static const char* kSettingsGroup;
|
||||||
|
static const int kDefaultServerPort;
|
||||||
|
|
||||||
|
NetworkRemote(Application* app);
|
||||||
|
~NetworkRemote();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void SetupServer();
|
||||||
|
void StartServer();
|
||||||
|
void ReloadSettings();
|
||||||
|
void AcceptConnection();
|
||||||
|
void IncomingData();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTcpServer* server_;
|
||||||
|
QList<QTcpSocket*>* clients_;
|
||||||
|
IncomingDataParser* incoming_data_parser_;
|
||||||
|
OutgoingDataCreator* outgoing_data_creator_;
|
||||||
|
int port_;
|
||||||
|
bool use_remote_;
|
||||||
|
Application* app_;
|
||||||
|
|
||||||
|
void StopServer();
|
||||||
|
void ReadSettings();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NETWORKREMOTE_H
|
|
@ -0,0 +1,58 @@
|
||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2012, Andreas Muttscheller <asfa194@gmail.com>
|
||||||
|
|
||||||
|
Clementine is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Clementine is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "core/logging.h"
|
||||||
|
|
||||||
|
#include "networkremote.h"
|
||||||
|
#include "networkremotehelper.h"
|
||||||
|
|
||||||
|
NetworkRemoteHelper* NetworkRemoteHelper::sInstance = NULL;
|
||||||
|
|
||||||
|
NetworkRemoteHelper::NetworkRemoteHelper(Application* app)
|
||||||
|
: app_(app)
|
||||||
|
{
|
||||||
|
app_ = app;
|
||||||
|
connect(this, SIGNAL(ReloadSettingsSig()),
|
||||||
|
app_->network_remote(), SLOT(ReloadSettings()));
|
||||||
|
connect(this, SIGNAL(StartServerSig()),
|
||||||
|
app_->network_remote(), SLOT(StartServer()));
|
||||||
|
connect(this, SIGNAL(SetupServerSig()),
|
||||||
|
app_->network_remote(), SLOT(SetupServer()));
|
||||||
|
|
||||||
|
sInstance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkRemoteHelper::~NetworkRemoteHelper() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkRemoteHelper::StartServer() {
|
||||||
|
emit SetupServerSig();
|
||||||
|
emit StartServerSig();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkRemoteHelper::ReloadSettings() {
|
||||||
|
emit ReloadSettingsSig();
|
||||||
|
}
|
||||||
|
|
||||||
|
// For using in Settingsdialog, we haven't the appication there
|
||||||
|
NetworkRemoteHelper* NetworkRemoteHelper::Instance() {
|
||||||
|
if (!sInstance) {
|
||||||
|
// normally he shouldn't go here. Only for safety
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return sInstance;
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef NETWORKREMOTEHELPER_H
|
||||||
|
#define NETWORKREMOTEHELPER_H
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
#include "networkremote.h"
|
||||||
|
|
||||||
|
class NetworkRemoteHelper : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
static NetworkRemoteHelper* Instance();
|
||||||
|
|
||||||
|
NetworkRemoteHelper(Application* app);
|
||||||
|
~NetworkRemoteHelper();
|
||||||
|
|
||||||
|
void StartServer();
|
||||||
|
void ReloadSettings();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void SetupServerSig();
|
||||||
|
void StartServerSig();
|
||||||
|
void ReloadSettingsSig();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static NetworkRemoteHelper* sInstance;
|
||||||
|
Application* app_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NETWORKREMOTEHELPER_H
|
|
@ -0,0 +1,268 @@
|
||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2012, Andreas Muttscheller <asfa194@gmail.com>
|
||||||
|
|
||||||
|
Clementine is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Clementine is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "outgoingdatacreator.h"
|
||||||
|
#include "core/logging.h"
|
||||||
|
|
||||||
|
OutgoingDataCreator::OutgoingDataCreator(Application* app)
|
||||||
|
: app_(app),
|
||||||
|
clients_(NULL)
|
||||||
|
{
|
||||||
|
// Create Keep Alive Timer
|
||||||
|
keep_alive_timer_ = new QTimer(this);
|
||||||
|
connect(keep_alive_timer_, SIGNAL(timeout()), this, SLOT(SendKeepAlive()));
|
||||||
|
keep_alive_timeout_ = 10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
OutgoingDataCreator::~OutgoingDataCreator() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutgoingDataCreator::SetClients(QList<QTcpSocket*>* clients) {
|
||||||
|
clients_ = clients;
|
||||||
|
// After we got some clients, start the keep alive timer
|
||||||
|
// Default: every 10 seconds
|
||||||
|
keep_alive_timer_->start(keep_alive_timeout_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutgoingDataCreator::SendDataToClients(pb::remote::Message* msg) {
|
||||||
|
// Check if we have clients to send data to
|
||||||
|
if (!clients_ || clients_->size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTcpSocket* sock;
|
||||||
|
foreach(sock, *clients_) {
|
||||||
|
// Check if the client is still active
|
||||||
|
if (sock->state() == QTcpSocket::ConnectedState) {
|
||||||
|
std::string data = msg->SerializeAsString();
|
||||||
|
QByteArray b64_data = QByteArray::fromRawData(data.data(), data.length());
|
||||||
|
sock->write(b64_data.toBase64());
|
||||||
|
sock->write("\n");
|
||||||
|
sock->flush();
|
||||||
|
} else {
|
||||||
|
clients_->removeAt(clients_->indexOf(sock));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutgoingDataCreator::SendClementineInfos() {
|
||||||
|
// Create the general message and set the message type
|
||||||
|
pb::remote::Message msg;
|
||||||
|
msg.set_msgtype(pb::remote::INFOS);
|
||||||
|
|
||||||
|
// Now add the message specific data
|
||||||
|
SetEngineState(&msg);
|
||||||
|
|
||||||
|
QString version = QString("%1 %2").arg(QCoreApplication::applicationName(),
|
||||||
|
QCoreApplication::applicationVersion());
|
||||||
|
pb::remote::ClementineInfos *infos = msg.mutable_infos();
|
||||||
|
infos->set_version(version.toAscii());
|
||||||
|
|
||||||
|
SendDataToClients(&msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutgoingDataCreator::SetEngineState(pb::remote::Message *msg) {
|
||||||
|
switch(app_->player()->GetState()) {
|
||||||
|
case Engine::Idle: msg->set_state(pb::remote::Idle);
|
||||||
|
break;
|
||||||
|
case Engine::Empty: msg->set_state(pb::remote::Empty);
|
||||||
|
break;
|
||||||
|
case Engine::Playing: msg->set_state(pb::remote::Playing);
|
||||||
|
break;
|
||||||
|
case Engine::Paused: msg->set_state(pb::remote::Paused);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutgoingDataCreator::SendAllPlaylists() {
|
||||||
|
// Get all Playlists
|
||||||
|
QList<Playlist*> playlists = app_->playlist_manager()->GetAllPlaylists();
|
||||||
|
QListIterator<Playlist*> i(playlists);
|
||||||
|
int active_playlist = app_->playlist_manager()->active_id();
|
||||||
|
|
||||||
|
// Create message
|
||||||
|
pb::remote::Message msg;
|
||||||
|
msg.set_msgtype(pb::remote::PLAYLISTS);
|
||||||
|
|
||||||
|
while(i.hasNext()) {
|
||||||
|
// Get the next Playlist
|
||||||
|
Playlist* p = i.next();
|
||||||
|
QString playlist_name = app_->playlist_manager()->GetPlaylistName(p->id());
|
||||||
|
|
||||||
|
// Create a new playlist
|
||||||
|
pb::remote::Playlist* playlist = msg.add_playlists();
|
||||||
|
playlist->set_name(playlist_name.toStdString());
|
||||||
|
playlist->set_id(p->id());
|
||||||
|
playlist->set_item_count(p->GetAllSongs().size());
|
||||||
|
playlist->set_active((p->id() == active_playlist));
|
||||||
|
}
|
||||||
|
|
||||||
|
SendDataToClients(&msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutgoingDataCreator::ActiveChanged(Playlist *) {
|
||||||
|
// When a playlist was changed, send the new list
|
||||||
|
SendAllPlaylists();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutgoingDataCreator::SendFirstData() {
|
||||||
|
// First Send the current song
|
||||||
|
PlaylistItemPtr item = app_->player()->GetCurrentItem();
|
||||||
|
if (!item) {
|
||||||
|
qLog(Info) << "No current item found!";
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentSongChanged(current_song_, current_uri_, current_image_);
|
||||||
|
|
||||||
|
// then the current volume
|
||||||
|
VolumeChanged(app_->player()->GetVolume());
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutgoingDataCreator::CurrentSongChanged(const Song& song, const QString& uri, const QImage& img) {
|
||||||
|
current_song_ = song;
|
||||||
|
current_uri_ = uri;
|
||||||
|
current_image_ = img;
|
||||||
|
|
||||||
|
if (clients_) {
|
||||||
|
// Create the message
|
||||||
|
pb::remote::Message msg;
|
||||||
|
msg.set_msgtype(pb::remote::CURRENT_METAINFOS);
|
||||||
|
|
||||||
|
// If there is no song, create an empty node, otherwise fill it with data
|
||||||
|
int i = app_->playlist_manager()->active()->current_row();
|
||||||
|
CreateSong(msg.mutable_currentsong(), ¤t_song_, &uri, i);
|
||||||
|
|
||||||
|
SendDataToClients(&msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutgoingDataCreator::CreateSong(pb::remote::SongMetadata* song_metadata,
|
||||||
|
Song* song, const QString* artUri, int index) {
|
||||||
|
if (song->is_valid()) {
|
||||||
|
song_metadata->set_id(song->id());
|
||||||
|
song_metadata->set_index(index);
|
||||||
|
song_metadata->set_title( DataCommaSizeFromQString(song->PrettyTitle()));
|
||||||
|
song_metadata->set_artist(DataCommaSizeFromQString(song->artist()));
|
||||||
|
song_metadata->set_album( DataCommaSizeFromQString(song->album()));
|
||||||
|
song_metadata->set_albumartist(DataCommaSizeFromQString(song->albumartist()));
|
||||||
|
song_metadata->set_pretty_length(DataCommaSizeFromQString(song->PrettyLength()));
|
||||||
|
song_metadata->set_genre(DataCommaSizeFromQString(song->genre()));
|
||||||
|
song_metadata->set_pretty_year(DataCommaSizeFromQString(song->PrettyYear()));
|
||||||
|
song_metadata->set_track(song->track());
|
||||||
|
song_metadata->set_disc(song->disc());
|
||||||
|
song_metadata->set_playcount(song->playcount());
|
||||||
|
|
||||||
|
// Append coverart
|
||||||
|
if (!artUri->isEmpty()) {
|
||||||
|
QImage orig(QUrl(*artUri).toLocalFile());
|
||||||
|
QImage small;
|
||||||
|
// Check if we resize the image
|
||||||
|
if (orig.width() > 1000) {
|
||||||
|
small = orig.scaled(1000, 1000, Qt::KeepAspectRatio);
|
||||||
|
} else {
|
||||||
|
small = orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the image in a buffer and compress it
|
||||||
|
QByteArray data;
|
||||||
|
QBuffer buf(&data);
|
||||||
|
buf.open(QIODevice::WriteOnly);
|
||||||
|
small.save(&buf, "JPG");
|
||||||
|
|
||||||
|
// Append the Data in the protocol buffer
|
||||||
|
song_metadata->set_art(data.toBase64());
|
||||||
|
|
||||||
|
buf.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void OutgoingDataCreator::VolumeChanged(int volume) {
|
||||||
|
// Create the message
|
||||||
|
pb::remote::Message msg;
|
||||||
|
msg.set_msgtype(pb::remote::SET_VOLUME);
|
||||||
|
msg.set_volume(volume);
|
||||||
|
SendDataToClients(&msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutgoingDataCreator::SendPlaylistSongs(int id) {
|
||||||
|
// Get the PlaylistQByteArray(data.data(), data.size()
|
||||||
|
Playlist* playlist = app_->playlist_manager()->playlist(id);
|
||||||
|
if(!playlist) {
|
||||||
|
qLog(Info) << "Could not find playlist with id = " << id;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SongList song_list = playlist->GetAllSongs();
|
||||||
|
QListIterator<Song> i(song_list);
|
||||||
|
|
||||||
|
// Create the message and the playlist
|
||||||
|
pb::remote::Message msg;
|
||||||
|
msg.set_msgtype(pb::remote::PLAYLIST_SONGS);
|
||||||
|
// Create a new playlist
|
||||||
|
pb::remote::Playlist* pb_playlist = msg.add_playlists();
|
||||||
|
pb_playlist->set_id(id);
|
||||||
|
pb_playlist->set_item_count(playlist->GetAllSongs().size());
|
||||||
|
|
||||||
|
// Send all songs
|
||||||
|
int index = 0;
|
||||||
|
while(i.hasNext()) {
|
||||||
|
Song song = i.next();
|
||||||
|
QString art = song.art_automatic();
|
||||||
|
pb::remote::SongMetadata* pb_song = pb_playlist->add_songs();
|
||||||
|
CreateSong(pb_song, &song, &art, index);
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
SendDataToClients(&msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutgoingDataCreator::PlaylistChanged(Playlist* playlist) {
|
||||||
|
// If a playlist changed, then send the new songs to the client
|
||||||
|
SendPlaylistSongs(playlist->id());
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutgoingDataCreator::StateChanged(Engine::State state) {
|
||||||
|
// Send state only if it changed
|
||||||
|
// When selecting next song, StateChanged is emitted, but we already know
|
||||||
|
// that we are playing
|
||||||
|
if (state == last_state_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
last_state_ = state;
|
||||||
|
|
||||||
|
pb::remote::Message msg;
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case Engine::Playing: msg.set_msgtype(pb::remote::PLAY);
|
||||||
|
break;
|
||||||
|
case Engine::Paused: msg.set_msgtype(pb::remote::PAUSE);
|
||||||
|
break;
|
||||||
|
case Engine::Empty: msg.set_msgtype(pb::remote::STOP); // Empty is called when player stopped
|
||||||
|
break;
|
||||||
|
default: msg.set_msgtype(pb::remote::STOP);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
SendDataToClients(&msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutgoingDataCreator::SendKeepAlive() {
|
||||||
|
pb::remote::Message msg;
|
||||||
|
msg.set_msgtype(pb::remote::KEEP_ALIVE);
|
||||||
|
SendDataToClients(&msg);
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
#ifndef OUTGOINGDATACREATOR_H
|
||||||
|
#define OUTGOINGDATACREATOR_H
|
||||||
|
|
||||||
|
#include <QTcpSocket>
|
||||||
|
#include <QImage>
|
||||||
|
#include <QList>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "core/player.h"
|
||||||
|
#include "core/application.h"
|
||||||
|
#include "engines/enginebase.h"
|
||||||
|
#include "engines/engine_fwd.h"
|
||||||
|
#include "playlist/playlist.h"
|
||||||
|
#include "playlist/playlistmanager.h"
|
||||||
|
#include "remotecontrolmessages.pb.h"
|
||||||
|
|
||||||
|
class OutgoingDataCreator : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
OutgoingDataCreator(Application* app);
|
||||||
|
~OutgoingDataCreator();
|
||||||
|
|
||||||
|
void SetClients(QList<QTcpSocket*>* clients);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void SendClementineInfos();
|
||||||
|
void SendAllPlaylists();
|
||||||
|
void SendFirstData();
|
||||||
|
void SendPlaylistSongs(int id);
|
||||||
|
void PlaylistChanged(Playlist*);
|
||||||
|
void VolumeChanged(int volume);
|
||||||
|
void ActiveChanged(Playlist*);
|
||||||
|
void CurrentSongChanged(const Song& song, const QString& uri, const QImage& img);
|
||||||
|
void StateChanged(Engine::State);
|
||||||
|
void SendKeepAlive();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Application* app_;
|
||||||
|
QList<QTcpSocket*>* clients_;
|
||||||
|
Song current_song_;
|
||||||
|
QString current_uri_;
|
||||||
|
QImage current_image_;
|
||||||
|
Engine::State last_state_;
|
||||||
|
QTimer* keep_alive_timer_;
|
||||||
|
int keep_alive_timeout_;
|
||||||
|
|
||||||
|
void SendDataToClients(pb::remote::Message* msg);
|
||||||
|
void SetEngineState(pb::remote::Message* msg);
|
||||||
|
void CreateSong(pb::remote::SongMetadata* song_metadata, Song* song, const QString* art_uri, int index);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // OUTGOINGDATACREATOR_H
|
|
@ -0,0 +1,70 @@
|
||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
|
||||||
|
Clementine is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Clementine is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "iconloader.h"
|
||||||
|
#include "networkremotesettingspage.h"
|
||||||
|
#include "ui_networkremotesettingspage.h"
|
||||||
|
#include "networkremote/networkremote.h"
|
||||||
|
#include "networkremote/networkremotehelper.h"
|
||||||
|
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
|
||||||
|
NetworkRemoteSettingsPage::NetworkRemoteSettingsPage(SettingsDialog* dialog)
|
||||||
|
: SettingsPage(dialog),
|
||||||
|
ui_(new Ui_NetworkRemoteSettingsPage)
|
||||||
|
{
|
||||||
|
ui_->setupUi(this);
|
||||||
|
setWindowIcon(IconLoader::Load("ipodtouchicon"));
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkRemoteSettingsPage::~NetworkRemoteSettingsPage() {
|
||||||
|
delete ui_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkRemoteSettingsPage::Load() {
|
||||||
|
QSettings s;
|
||||||
|
int port;
|
||||||
|
|
||||||
|
s.beginGroup(NetworkRemote::kSettingsGroup);
|
||||||
|
|
||||||
|
port = s.value("port").toInt();
|
||||||
|
if (port == 0) {
|
||||||
|
ui_->remote_port->setValue(NetworkRemote::kDefaultServerPort);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ui_->remote_port->setValue(s.value("port").toInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
ui_->use_remote->setChecked(s.value("use_remote").toBool());
|
||||||
|
|
||||||
|
s.endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkRemoteSettingsPage::Save() {
|
||||||
|
QSettings s;
|
||||||
|
|
||||||
|
s.beginGroup(NetworkRemote::kSettingsGroup);
|
||||||
|
s.setValue("port", ui_->remote_port->value());
|
||||||
|
s.setValue("use_remote", ui_->use_remote->isChecked());
|
||||||
|
|
||||||
|
s.endGroup();
|
||||||
|
|
||||||
|
if (NetworkRemoteHelper::Instance()) {
|
||||||
|
NetworkRemoteHelper::Instance()->ReloadSettings();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
/* This file is part of Clementine.
|
||||||
|
Copyright 2010, David Sansome <me@davidsansome.com>
|
||||||
|
|
||||||
|
Clementine is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Clementine is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with Clementine. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETWORKREMOTESETTINGSPAGE_H
|
||||||
|
#define NETWORKREMOTESETTINGSPAGE_H
|
||||||
|
|
||||||
|
#include "settingspage.h"
|
||||||
|
|
||||||
|
class Ui_NetworkRemoteSettingsPage;
|
||||||
|
|
||||||
|
class NetworkRemoteSettingsPage : public SettingsPage {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
NetworkRemoteSettingsPage(SettingsDialog* dialog);
|
||||||
|
~NetworkRemoteSettingsPage();
|
||||||
|
|
||||||
|
void Load();
|
||||||
|
void Save();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui_NetworkRemoteSettingsPage* ui_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // NETWORKREMOTESETTINGSPAGE_H
|
|
@ -0,0 +1,103 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>NetworkRemoteSettingsPage</class>
|
||||||
|
<widget class="QWidget" name="NetworkRemoteSettingsPage">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Network Remote</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="use_remote">
|
||||||
|
<property name="text">
|
||||||
|
<string>Use Remotecontrol</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="use_remote_container" native="true">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>24</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_15">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>171</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="layoutDirection">
|
||||||
|
<enum>Qt::LeftToRight</enum>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Port</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="remote_port">
|
||||||
|
<property name="maximum">
|
||||||
|
<number>65535</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>8080</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_5">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>36</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>use_remote</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>use_remote_container</receiver>
|
||||||
|
<slot>setEnabled(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>199</x>
|
||||||
|
<y>19</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>199</x>
|
||||||
|
<y>60</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
|
@ -23,6 +23,7 @@
|
||||||
#include "iconloader.h"
|
#include "iconloader.h"
|
||||||
#include "playbacksettingspage.h"
|
#include "playbacksettingspage.h"
|
||||||
#include "networkproxysettingspage.h"
|
#include "networkproxysettingspage.h"
|
||||||
|
#include "networkremotesettingspage.h"
|
||||||
#include "notificationssettingspage.h"
|
#include "notificationssettingspage.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "settingsdialog.h"
|
#include "settingsdialog.h"
|
||||||
|
@ -130,6 +131,7 @@ SettingsDialog::SettingsDialog(Application* app, BackgroundStreams* streams, QWi
|
||||||
AddPage(Page_Library, new LibrarySettingsPage(this), general);
|
AddPage(Page_Library, new LibrarySettingsPage(this), general);
|
||||||
AddPage(Page_Proxy, new NetworkProxySettingsPage(this), general);
|
AddPage(Page_Proxy, new NetworkProxySettingsPage(this), general);
|
||||||
AddPage(Page_Transcoding, new TranscoderSettingsPage(this), general);
|
AddPage(Page_Transcoding, new TranscoderSettingsPage(this), general);
|
||||||
|
AddPage(Page_NetworkRemote, new NetworkRemoteSettingsPage(this), general);
|
||||||
|
|
||||||
#ifdef HAVE_WIIMOTEDEV
|
#ifdef HAVE_WIIMOTEDEV
|
||||||
AddPage(Page_Wiimotedev, new WiimoteSettingsPage(this), general);
|
AddPage(Page_Wiimotedev, new WiimoteSettingsPage(this), general);
|
||||||
|
|
|
@ -63,6 +63,7 @@ public:
|
||||||
Page_GlobalShortcuts,
|
Page_GlobalShortcuts,
|
||||||
Page_GlobalSearch,
|
Page_GlobalSearch,
|
||||||
Page_Appearance,
|
Page_Appearance,
|
||||||
|
Page_NetworkRemote,
|
||||||
Page_Notifications,
|
Page_Notifications,
|
||||||
Page_Library,
|
Page_Library,
|
||||||
Page_Lastfm,
|
Page_Lastfm,
|
||||||
|
|
Loading…
Reference in New Issue