All changes for ClemRemote v1.0 (in one go)
This commit is contained in:
parent
d7966c8285
commit
9714b0632d
|
@ -15,7 +15,7 @@
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Note: this file is licensed under the Apache License instead of GPL, so
|
// Note: this file is licensed under the Apache License instead of GPL, so
|
||||||
// 3rd party applications or libraries can use another license besides GPL.
|
// 3rd party applications or libraries can use another license besides GPL.
|
||||||
|
|
||||||
syntax = "proto2";
|
syntax = "proto2";
|
||||||
|
@ -36,6 +36,7 @@ enum MsgType {
|
||||||
REMOVE_SONGS = 9;
|
REMOVE_SONGS = 9;
|
||||||
OPEN_PLAYLIST = 10;
|
OPEN_PLAYLIST = 10;
|
||||||
CLOSE_PLAYLIST = 11;
|
CLOSE_PLAYLIST = 11;
|
||||||
|
UPDATE_PLAYLIST = 60;
|
||||||
GET_LYRICS = 14;
|
GET_LYRICS = 14;
|
||||||
DOWNLOAD_SONGS = 15;
|
DOWNLOAD_SONGS = 15;
|
||||||
SONG_OFFER_RESPONSE = 16;
|
SONG_OFFER_RESPONSE = 16;
|
||||||
|
@ -46,6 +47,10 @@ enum MsgType {
|
||||||
GET_LIBRARY = 18;
|
GET_LIBRARY = 18;
|
||||||
RATE_SONG = 19;
|
RATE_SONG = 19;
|
||||||
GLOBAL_SEARCH = 100;
|
GLOBAL_SEARCH = 100;
|
||||||
|
REQUEST_SAVED_RADIOS = 110;
|
||||||
|
// access Files from remote control
|
||||||
|
REQUEST_FILES = 200;
|
||||||
|
APPEND_FILES = 201;
|
||||||
|
|
||||||
// Messages send by both
|
// Messages send by both
|
||||||
DISCONNECT = 2;
|
DISCONNECT = 2;
|
||||||
|
@ -79,6 +84,8 @@ enum MsgType {
|
||||||
GLOBAL_SEARCH_RESULT = 54;
|
GLOBAL_SEARCH_RESULT = 54;
|
||||||
TRANSCODING_FILES = 55;
|
TRANSCODING_FILES = 55;
|
||||||
GLOBAL_SEARCH_STATUS = 56;
|
GLOBAL_SEARCH_STATUS = 56;
|
||||||
|
// access Files from remote control
|
||||||
|
LIST_FILES = 202;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid Engine states
|
// Valid Engine states
|
||||||
|
@ -113,8 +120,8 @@ message SongMetadata {
|
||||||
STREAM = 99;
|
STREAM = 99;
|
||||||
}
|
}
|
||||||
|
|
||||||
optional int32 id = 1; // unique id of the song
|
optional int32 id = 1; // unique id of the song
|
||||||
optional int32 index = 2; // Index of the current row of the active playlist
|
optional int32 index = 2; // Index of the current row of the active playlist
|
||||||
optional string title = 3;
|
optional string title = 3;
|
||||||
optional string album = 4;
|
optional string album = 4;
|
||||||
optional string artist = 5;
|
optional string artist = 5;
|
||||||
|
@ -130,7 +137,7 @@ message SongMetadata {
|
||||||
optional bool is_local = 15;
|
optional bool is_local = 15;
|
||||||
optional string filename = 16;
|
optional string filename = 16;
|
||||||
optional int32 file_size = 17;
|
optional int32 file_size = 17;
|
||||||
optional float rating = 18; // 0 (0 stars) to 1 (5 stars)
|
optional float rating = 18; // 0 (0 stars) to 1 (5 stars)
|
||||||
optional string url = 19;
|
optional string url = 19;
|
||||||
optional string art_automatic = 20;
|
optional string art_automatic = 20;
|
||||||
optional string art_manual = 21;
|
optional string art_manual = 21;
|
||||||
|
@ -144,6 +151,7 @@ message Playlist {
|
||||||
optional int32 item_count = 3;
|
optional int32 item_count = 3;
|
||||||
optional bool active = 4;
|
optional bool active = 4;
|
||||||
optional bool closed = 5;
|
optional bool closed = 5;
|
||||||
|
optional bool favorite = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid Repeatmodes
|
// Valid Repeatmodes
|
||||||
|
@ -200,6 +208,11 @@ message Shuffle {
|
||||||
message ResponseClementineInfo {
|
message ResponseClementineInfo {
|
||||||
optional string version = 1;
|
optional string version = 1;
|
||||||
optional EngineState state = 2;
|
optional EngineState state = 2;
|
||||||
|
|
||||||
|
optional bool allow_downloads = 3;
|
||||||
|
|
||||||
|
// allowed extensions for REQUEST_FILES and LIST_FILES
|
||||||
|
repeated string files_music_extensions = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The current song played
|
// The current song played
|
||||||
|
@ -210,12 +223,13 @@ message ResponseCurrentMetadata {
|
||||||
// The playlists in clementine
|
// The playlists in clementine
|
||||||
message ResponsePlaylists {
|
message ResponsePlaylists {
|
||||||
repeated Playlist playlist = 1;
|
repeated Playlist playlist = 1;
|
||||||
|
optional bool include_closed = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A list of songs in a playlist
|
// A list of songs in a playlist
|
||||||
message ResponsePlaylistSongs {
|
message ResponsePlaylistSongs {
|
||||||
optional Playlist requested_playlist = 1;
|
optional Playlist requested_playlist = 1;
|
||||||
|
|
||||||
// The songs that are in the playlist
|
// The songs that are in the playlist
|
||||||
repeated SongMetadata songs = 2;
|
repeated SongMetadata songs = 2;
|
||||||
}
|
}
|
||||||
|
@ -226,7 +240,7 @@ message ResponseEngineStateChanged {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sends the current position of the track
|
// Sends the current position of the track
|
||||||
message ResponseUpdateTrackPosition {
|
message ResponseUpdateTrackPosition {
|
||||||
optional int32 position = 1;
|
optional int32 position = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,10 +276,12 @@ message RequestInsertUrls {
|
||||||
// In which playlist should the urls be inserted?
|
// In which playlist should the urls be inserted?
|
||||||
optional int32 playlist_id = 1;
|
optional int32 playlist_id = 1;
|
||||||
repeated string urls = 2;
|
repeated string urls = 2;
|
||||||
optional int32 position = 3 [default=-1];
|
optional int32 position = 3 [default = -1];
|
||||||
optional bool play_now = 4 [default=false];
|
optional bool play_now = 4 [default = false];
|
||||||
optional bool enqueue = 5 [default=false];
|
optional bool enqueue = 5 [default = false];
|
||||||
repeated SongMetadata songs = 6;
|
repeated SongMetadata songs = 6;
|
||||||
|
// if we wish to create a new playlist
|
||||||
|
optional string new_playlist_name = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client want to change track
|
// Client want to change track
|
||||||
|
@ -283,6 +299,13 @@ message RequestOpenPlaylist {
|
||||||
message RequestClosePlaylist {
|
message RequestClosePlaylist {
|
||||||
optional int32 playlist_id = 1;
|
optional int32 playlist_id = 1;
|
||||||
}
|
}
|
||||||
|
message RequestUpdatePlaylist {
|
||||||
|
optional int32 playlist_id = 1;
|
||||||
|
optional string new_playlist_name = 2;
|
||||||
|
optional bool favorite = 3;
|
||||||
|
optional bool create_new_playlist = 4;
|
||||||
|
optional bool clear_playlist = 5;
|
||||||
|
}
|
||||||
|
|
||||||
// Message containing lyrics
|
// Message containing lyrics
|
||||||
message ResponseLyrics {
|
message ResponseLyrics {
|
||||||
|
@ -305,6 +328,13 @@ message RequestDownloadSongs {
|
||||||
optional DownloadItem download_item = 1;
|
optional DownloadItem download_item = 1;
|
||||||
optional int32 playlist_id = 2;
|
optional int32 playlist_id = 2;
|
||||||
repeated string urls = 3;
|
repeated string urls = 3;
|
||||||
|
|
||||||
|
// within a Playlist, download only requested songs
|
||||||
|
repeated int32 songs_ids = 4;
|
||||||
|
|
||||||
|
// download from the FileSystem remotely
|
||||||
|
// using the defined root directory and the urls (filenames)
|
||||||
|
optional string relative_path = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ResponseSongFileChunk {
|
message ResponseSongFileChunk {
|
||||||
|
@ -312,7 +342,7 @@ message ResponseSongFileChunk {
|
||||||
optional int32 chunk_count = 2;
|
optional int32 chunk_count = 2;
|
||||||
optional int32 file_number = 3;
|
optional int32 file_number = 3;
|
||||||
optional int32 file_count = 4;
|
optional int32 file_count = 4;
|
||||||
optional SongMetadata song_metadata = 6; // only sent with first chunk!
|
optional SongMetadata song_metadata = 6; // only sent with first chunk!
|
||||||
optional bytes data = 7;
|
optional bytes data = 7;
|
||||||
optional int32 size = 8;
|
optional int32 size = 8;
|
||||||
optional bytes file_hash = 9;
|
optional bytes file_hash = 9;
|
||||||
|
@ -327,11 +357,11 @@ message ResponseLibraryChunk {
|
||||||
}
|
}
|
||||||
|
|
||||||
message ResponseSongOffer {
|
message ResponseSongOffer {
|
||||||
optional bool accepted = 1; // true = client wants to download item
|
optional bool accepted = 1; // true = client wants to download item
|
||||||
}
|
}
|
||||||
|
|
||||||
message RequestRateSong {
|
message RequestRateSong {
|
||||||
optional float rating = 1; // 0 to 1
|
optional float rating = 1; // 0 to 1
|
||||||
}
|
}
|
||||||
|
|
||||||
message ResponseDownloadTotalSize {
|
message ResponseDownloadTotalSize {
|
||||||
|
@ -367,10 +397,56 @@ message ResponseGlobalSearchStatus {
|
||||||
optional GlobalSearchStatus status = 3;
|
optional GlobalSearchStatus status = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// access the FileSystem remotely from a defined root directory
|
||||||
|
message RequestListFiles {
|
||||||
|
optional string relative_path = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message FileMetadata {
|
||||||
|
optional string filename = 1;
|
||||||
|
optional bool is_dir = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ResponseListFiles {
|
||||||
|
enum Error {
|
||||||
|
NONE = 0;
|
||||||
|
ROOT_DIR_NOT_SET = 1;
|
||||||
|
DIR_NOT_ACCESSIBLE = 2;
|
||||||
|
DIR_NOT_EXIST = 3;
|
||||||
|
UNKNOWN = 4;
|
||||||
|
}
|
||||||
|
optional string relative_path = 1;
|
||||||
|
repeated FileMetadata files = 2;
|
||||||
|
optional Error error = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RequestAppendFiles {
|
||||||
|
// where to append the files
|
||||||
|
optional int32 playlist_id = 1;
|
||||||
|
// or we create a new playlist
|
||||||
|
optional string new_playlist_name = 2;
|
||||||
|
|
||||||
|
optional string relative_path = 3;
|
||||||
|
repeated string files = 4;
|
||||||
|
|
||||||
|
optional bool play_now = 5;
|
||||||
|
optional bool clear_first = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Stream {
|
||||||
|
optional string name = 1;
|
||||||
|
optional string url = 2;
|
||||||
|
optional string url_logo = 3;
|
||||||
|
}
|
||||||
|
message ResponseSavedRadios {
|
||||||
|
repeated Stream streams = 1;
|
||||||
|
}
|
||||||
|
|
||||||
// The message itself
|
// The message itself
|
||||||
message Message {
|
message Message {
|
||||||
optional int32 version = 1 [default=21];
|
optional int32 version = 1 [default = 21];
|
||||||
optional MsgType type = 2 [default=UNKNOWN]; // What data is in the message?
|
optional MsgType type = 2
|
||||||
|
[default = UNKNOWN]; // What data is in the message?
|
||||||
|
|
||||||
optional RequestConnect request_connect = 21;
|
optional RequestConnect request_connect = 21;
|
||||||
optional RequestPlaylists request_playlists = 27;
|
optional RequestPlaylists request_playlists = 27;
|
||||||
|
@ -382,13 +458,16 @@ message Message {
|
||||||
optional RequestRemoveSongs request_remove_songs = 26;
|
optional RequestRemoveSongs request_remove_songs = 26;
|
||||||
optional RequestOpenPlaylist request_open_playlist = 28;
|
optional RequestOpenPlaylist request_open_playlist = 28;
|
||||||
optional RequestClosePlaylist request_close_playlist = 29;
|
optional RequestClosePlaylist request_close_playlist = 29;
|
||||||
|
optional RequestUpdatePlaylist request_update_playlist = 53;
|
||||||
optional RequestDownloadSongs request_download_songs = 31;
|
optional RequestDownloadSongs request_download_songs = 31;
|
||||||
optional RequestRateSong request_rate_song = 35;
|
optional RequestRateSong request_rate_song = 35;
|
||||||
optional RequestGlobalSearch request_global_search = 37;
|
optional RequestGlobalSearch request_global_search = 37;
|
||||||
|
optional RequestListFiles request_list_files = 50;
|
||||||
|
optional RequestAppendFiles request_append_files = 51;
|
||||||
|
|
||||||
optional Repeat repeat = 13;
|
optional Repeat repeat = 13;
|
||||||
optional Shuffle shuffle = 14;
|
optional Shuffle shuffle = 14;
|
||||||
|
|
||||||
optional ResponseClementineInfo response_clementine_info = 15;
|
optional ResponseClementineInfo response_clementine_info = 15;
|
||||||
optional ResponseCurrentMetadata response_current_metadata = 16;
|
optional ResponseCurrentMetadata response_current_metadata = 16;
|
||||||
optional ResponsePlaylists response_playlists = 17;
|
optional ResponsePlaylists response_playlists = 17;
|
||||||
|
@ -405,4 +484,6 @@ message Message {
|
||||||
optional ResponseGlobalSearch response_global_search = 38;
|
optional ResponseGlobalSearch response_global_search = 38;
|
||||||
optional ResponseTranscoderStatus response_transcoder_status = 39;
|
optional ResponseTranscoderStatus response_transcoder_status = 39;
|
||||||
optional ResponseGlobalSearchStatus response_global_search_status = 40;
|
optional ResponseGlobalSearchStatus response_global_search_status = 40;
|
||||||
|
optional ResponseListFiles response_list_files = 52;
|
||||||
|
optional ResponseSavedRadios response_saved_radios = 54;
|
||||||
}
|
}
|
||||||
|
|
|
@ -339,6 +339,7 @@ set(SOURCES
|
||||||
ui/edittagdialog.cpp
|
ui/edittagdialog.cpp
|
||||||
ui/lovedialog.cpp
|
ui/lovedialog.cpp
|
||||||
ui/equalizer.cpp
|
ui/equalizer.cpp
|
||||||
|
ui/filechooserwidget.cpp
|
||||||
ui/flowlayout.cpp
|
ui/flowlayout.cpp
|
||||||
ui/globalshortcutgrabber.cpp
|
ui/globalshortcutgrabber.cpp
|
||||||
ui/globalshortcutssettingspage.cpp
|
ui/globalshortcutssettingspage.cpp
|
||||||
|
@ -625,6 +626,7 @@ set(HEADERS
|
||||||
ui/console.h
|
ui/console.h
|
||||||
ui/coverfromurldialog.h
|
ui/coverfromurldialog.h
|
||||||
ui/edittagdialog.h
|
ui/edittagdialog.h
|
||||||
|
ui/filechooserwidget.h
|
||||||
ui/lovedialog.h
|
ui/lovedialog.h
|
||||||
ui/equalizer.h
|
ui/equalizer.h
|
||||||
ui/globalshortcutgrabber.h
|
ui/globalshortcutgrabber.h
|
||||||
|
|
|
@ -69,6 +69,8 @@ bool Application::kIsPortable = false;
|
||||||
const char* Application::kLegacyPortableDataDir = "data";
|
const char* Application::kLegacyPortableDataDir = "data";
|
||||||
const char* Application::kDefaultPortableDataDir = "clementine-data";
|
const char* Application::kDefaultPortableDataDir = "clementine-data";
|
||||||
const char* Application::kPortableDataDir = nullptr;
|
const char* Application::kPortableDataDir = nullptr;
|
||||||
|
const QStringList Application::kDefaultMusicExtensionsAllowedRemotely = {
|
||||||
|
"aac", "alac", "flac", "m3u", "m4a", "mp3", "ogg", "wav", "wmv"};
|
||||||
|
|
||||||
class ApplicationImpl {
|
class ApplicationImpl {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -65,6 +65,7 @@ class Application : public QObject {
|
||||||
static const char* kPortableDataDir;
|
static const char* kPortableDataDir;
|
||||||
static const char* kLegacyPortableDataDir;
|
static const char* kLegacyPortableDataDir;
|
||||||
static const char* kDefaultPortableDataDir;
|
static const char* kDefaultPortableDataDir;
|
||||||
|
static const QStringList kDefaultMusicExtensionsAllowedRemotely;
|
||||||
static bool IsPortable() { return kIsPortable; }
|
static bool IsPortable() { return kIsPortable; }
|
||||||
|
|
||||||
explicit Application(QObject* parent = nullptr);
|
explicit Application(QObject* parent = nullptr);
|
||||||
|
|
|
@ -36,7 +36,8 @@ class MimeData : public QMimeData {
|
||||||
enqueue_next_now_(enqueue_next_now),
|
enqueue_next_now_(enqueue_next_now),
|
||||||
open_in_new_playlist_(open_in_new_playlist),
|
open_in_new_playlist_(open_in_new_playlist),
|
||||||
name_for_new_playlist_(QString()),
|
name_for_new_playlist_(QString()),
|
||||||
from_doubleclick_(false) {}
|
from_doubleclick_(false),
|
||||||
|
playlist_id(-1) {}
|
||||||
|
|
||||||
// If this is set then MainWindow will not touch any of the other flags.
|
// If this is set then MainWindow will not touch any of the other flags.
|
||||||
bool override_user_settings_;
|
bool override_user_settings_;
|
||||||
|
@ -69,6 +70,10 @@ class MimeData : public QMimeData {
|
||||||
// the defaults set by the user.
|
// the defaults set by the user.
|
||||||
bool from_doubleclick_;
|
bool from_doubleclick_;
|
||||||
|
|
||||||
|
// The Network Remote can use this MimeData to drop songs on another
|
||||||
|
// playlist than the one currently opened on the server
|
||||||
|
int playlist_id;
|
||||||
|
|
||||||
// Returns a pretty name for a playlist containing songs described by this
|
// Returns a pretty name for a playlist containing songs described by this
|
||||||
// MimeData
|
// MimeData
|
||||||
// object. By pretty name we mean the value of 'name_for_new_playlist_' or
|
// object. By pretty name we mean the value of 'name_for_new_playlist_' or
|
||||||
|
|
|
@ -79,7 +79,8 @@ void SavedRadio::LoadStreams() {
|
||||||
for (int i = 0; i < count; ++i) {
|
for (int i = 0; i < count; ++i) {
|
||||||
s.setArrayIndex(i);
|
s.setArrayIndex(i);
|
||||||
streams_ << Stream(QUrl(s.value("url").toString()),
|
streams_ << Stream(QUrl(s.value("url").toString()),
|
||||||
s.value("name").toString());
|
s.value("name").toString(),
|
||||||
|
QUrl(s.value("url_logo").toString()));
|
||||||
}
|
}
|
||||||
s.endArray();
|
s.endArray();
|
||||||
}
|
}
|
||||||
|
@ -94,6 +95,7 @@ void SavedRadio::SaveStreams() {
|
||||||
s.setArrayIndex(i);
|
s.setArrayIndex(i);
|
||||||
s.setValue("url", streams_[i].url_);
|
s.setValue("url", streams_[i].url_);
|
||||||
s.setValue("name", streams_[i].name_);
|
s.setValue("name", streams_[i].name_);
|
||||||
|
s.setValue("url_logo", streams_[i].url_logo_);
|
||||||
}
|
}
|
||||||
s.endArray();
|
s.endArray();
|
||||||
|
|
||||||
|
@ -148,15 +150,18 @@ void SavedRadio::Edit() {
|
||||||
edit_dialog_->set_save_visible(false);
|
edit_dialog_->set_save_visible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
edit_dialog_->set_name(context_item->text());
|
|
||||||
edit_dialog_->set_url(context_item->data(InternetModel::Role_Url).toUrl());
|
|
||||||
if (edit_dialog_->exec() == QDialog::Rejected) return;
|
|
||||||
|
|
||||||
int i = streams_.indexOf(
|
int i = streams_.indexOf(
|
||||||
Stream(QUrl(context_item->data(InternetModel::Role_Url).toUrl())));
|
Stream(QUrl(context_item->data(InternetModel::Role_Url).toUrl())));
|
||||||
Stream* stream = &streams_[i];
|
Stream* stream = &streams_[i];
|
||||||
|
|
||||||
|
edit_dialog_->set_name(context_item->text());
|
||||||
|
edit_dialog_->set_url(context_item->data(InternetModel::Role_Url).toUrl());
|
||||||
|
edit_dialog_->set_url_logo(stream->url_logo_);
|
||||||
|
if (edit_dialog_->exec() == QDialog::Rejected) return;
|
||||||
|
|
||||||
stream->name_ = edit_dialog_->name();
|
stream->name_ = edit_dialog_->name();
|
||||||
stream->url_ = edit_dialog_->url();
|
stream->url_ = edit_dialog_->url();
|
||||||
|
stream->url_logo_ = edit_dialog_->url_logo();
|
||||||
|
|
||||||
context_item->setText(stream->name_);
|
context_item->setText(stream->name_);
|
||||||
context_item->setData(stream->url_, InternetModel::Role_Url);
|
context_item->setData(stream->url_, InternetModel::Role_Url);
|
||||||
|
@ -173,10 +178,11 @@ void SavedRadio::AddStreamToList(const Stream& stream, QStandardItem* parent) {
|
||||||
parent->appendRow(s);
|
parent->appendRow(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SavedRadio::Add(const QUrl& url, const QString& name) {
|
void SavedRadio::Add(const QUrl& url, const QString& name,
|
||||||
|
const QUrl& url_logo) {
|
||||||
if (streams_.contains(Stream(url))) return;
|
if (streams_.contains(Stream(url))) return;
|
||||||
|
|
||||||
Stream stream(url, name);
|
Stream stream(url, name, url_logo);
|
||||||
streams_ << stream;
|
streams_ << stream;
|
||||||
|
|
||||||
if (!root_->data(InternetModel::Role_CanLazyLoad).toBool()) {
|
if (!root_->data(InternetModel::Role_CanLazyLoad).toBool()) {
|
||||||
|
|
|
@ -41,14 +41,16 @@ class SavedRadio : public InternetService {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Stream {
|
struct Stream {
|
||||||
explicit Stream(const QUrl& url, const QString& name = QString())
|
explicit Stream(const QUrl& url, const QString& name = QString(),
|
||||||
: url_(url), name_(name) {}
|
const QUrl& url_logo = QUrl())
|
||||||
|
: url_(url), name_(name), url_logo_(url_logo) {}
|
||||||
|
|
||||||
// For QList::contains
|
// For QList::contains
|
||||||
bool operator==(const Stream& other) const { return url_ == other.url_; }
|
bool operator==(const Stream& other) const { return url_ == other.url_; }
|
||||||
|
|
||||||
QUrl url_;
|
QUrl url_;
|
||||||
QString name_;
|
QString name_;
|
||||||
|
QUrl url_logo_;
|
||||||
};
|
};
|
||||||
typedef QList<Stream> StreamList;
|
typedef QList<Stream> StreamList;
|
||||||
|
|
||||||
|
@ -60,9 +62,10 @@ class SavedRadio : public InternetService {
|
||||||
|
|
||||||
void ShowContextMenu(const QPoint& global_pos);
|
void ShowContextMenu(const QPoint& global_pos);
|
||||||
|
|
||||||
void Add(const QUrl& url, const QString& name = QString());
|
void Add(const QUrl& url, const QString& name = QString(),
|
||||||
|
const QUrl& url_logo = QUrl());
|
||||||
|
|
||||||
StreamList Streams() const { return streams_; }
|
const StreamList& Streams() const { return streams_; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void ShowAddStreamDialog();
|
void ShowAddStreamDialog();
|
||||||
|
|
|
@ -17,9 +17,11 @@
|
||||||
|
|
||||||
#include "incomingdataparser.h"
|
#include "incomingdataparser.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "core/logging.h"
|
#include "core/logging.h"
|
||||||
|
#include "core/mimedata.h"
|
||||||
#include "core/timeconstants.h"
|
#include "core/timeconstants.h"
|
||||||
#include "engines/enginebase.h"
|
#include "engines/enginebase.h"
|
||||||
#include "internet/core/internetmodel.h"
|
#include "internet/core/internetmodel.h"
|
||||||
|
@ -36,47 +38,55 @@ IncomingDataParser::IncomingDataParser(Application* app) : app_(app) {
|
||||||
ReloadSettings();
|
ReloadSettings();
|
||||||
connect(app_, SIGNAL(SettingsChanged()), SLOT(ReloadSettings()));
|
connect(app_, SIGNAL(SettingsChanged()), SLOT(ReloadSettings()));
|
||||||
|
|
||||||
|
Player* player = app_->player();
|
||||||
|
PlaylistManager* playlist_manager = app_->playlist_manager();
|
||||||
|
|
||||||
// Connect all the signals
|
// Connect all the signals
|
||||||
// due the player is in a different thread, we cannot access these functions
|
// due the player is in a different thread, we cannot access these functions
|
||||||
// directly
|
// directly
|
||||||
connect(this, SIGNAL(Play()), app_->player(), SLOT(Play()));
|
connect(this, SIGNAL(Play()), player, SLOT(Play()));
|
||||||
connect(this, SIGNAL(PlayPause()), app_->player(), SLOT(PlayPause()));
|
connect(this, SIGNAL(PlayPause()), player, SLOT(PlayPause()));
|
||||||
connect(this, SIGNAL(Pause()), app_->player(), SLOT(Pause()));
|
connect(this, SIGNAL(Pause()), player, SLOT(Pause()));
|
||||||
connect(this, SIGNAL(Stop()), app_->player(), SLOT(Stop()));
|
connect(this, SIGNAL(Stop()), player, SLOT(Stop()));
|
||||||
connect(this, SIGNAL(StopAfterCurrent()), app_->player(),
|
connect(this, SIGNAL(StopAfterCurrent()), player, SLOT(StopAfterCurrent()));
|
||||||
SLOT(StopAfterCurrent()));
|
connect(this, SIGNAL(Next()), player, SLOT(Next()));
|
||||||
connect(this, SIGNAL(Next()), app_->player(), SLOT(Next()));
|
connect(this, SIGNAL(Previous()), player, SLOT(Previous()));
|
||||||
connect(this, SIGNAL(Previous()), app_->player(), SLOT(Previous()));
|
connect(this, SIGNAL(SetVolume(int)), player, SLOT(SetVolume(int)));
|
||||||
connect(this, SIGNAL(SetVolume(int)), app_->player(), SLOT(SetVolume(int)));
|
connect(this, SIGNAL(PlayAt(int, Engine::TrackChangeFlags, bool)), player,
|
||||||
connect(this, SIGNAL(PlayAt(int, Engine::TrackChangeFlags, bool)),
|
SLOT(PlayAt(int, Engine::TrackChangeFlags, bool)));
|
||||||
app_->player(), SLOT(PlayAt(int, Engine::TrackChangeFlags, bool)));
|
connect(this, SIGNAL(SeekTo(int)), player, SLOT(SeekTo(int)));
|
||||||
connect(this, SIGNAL(SeekTo(int)), app_->player(), SLOT(SeekTo(int)));
|
connect(this, SIGNAL(Enque(int, int)), playlist_manager,
|
||||||
connect(this, SIGNAL(Enque(int, int)), app_->playlist_manager(),
|
|
||||||
SLOT(Enque(int, int)));
|
SLOT(Enque(int, int)));
|
||||||
|
|
||||||
connect(this, SIGNAL(SetActivePlaylist(int)), app_->playlist_manager(),
|
connect(this, SIGNAL(SetActivePlaylist(int)), playlist_manager,
|
||||||
SLOT(SetActivePlaylist(int)));
|
SLOT(SetActivePlaylist(int)));
|
||||||
connect(this, SIGNAL(ShuffleCurrent()), app_->playlist_manager(),
|
connect(this, SIGNAL(ShuffleCurrent()), playlist_manager,
|
||||||
SLOT(ShuffleCurrent()));
|
SLOT(ShuffleCurrent()));
|
||||||
connect(this, SIGNAL(SetRepeatMode(PlaylistSequence::RepeatMode)),
|
connect(this, SIGNAL(SetRepeatMode(PlaylistSequence::RepeatMode)),
|
||||||
app_->playlist_manager()->sequence(),
|
playlist_manager->sequence(),
|
||||||
SLOT(SetRepeatMode(PlaylistSequence::RepeatMode)));
|
SLOT(SetRepeatMode(PlaylistSequence::RepeatMode)));
|
||||||
connect(this, SIGNAL(SetShuffleMode(PlaylistSequence::ShuffleMode)),
|
connect(this, SIGNAL(SetShuffleMode(PlaylistSequence::ShuffleMode)),
|
||||||
app_->playlist_manager()->sequence(),
|
playlist_manager->sequence(),
|
||||||
SLOT(SetShuffleMode(PlaylistSequence::ShuffleMode)));
|
SLOT(SetShuffleMode(PlaylistSequence::ShuffleMode)));
|
||||||
connect(this, SIGNAL(InsertUrls(int, const QList<QUrl>&, int, bool, bool)),
|
connect(this, SIGNAL(InsertUrls(int, const QList<QUrl>&, int, bool, bool)),
|
||||||
app_->playlist_manager(),
|
playlist_manager,
|
||||||
SLOT(InsertUrls(int, const QList<QUrl>&, int, bool, bool)));
|
SLOT(InsertUrls(int, const QList<QUrl>&, int, bool, bool)));
|
||||||
connect(this, SIGNAL(InsertSongs(int, const SongList&, int, bool, bool)),
|
connect(this, SIGNAL(InsertSongs(int, const SongList&, int, bool, bool)),
|
||||||
app_->playlist_manager(),
|
playlist_manager,
|
||||||
SLOT(InsertSongs(int, const SongList&, int, bool, bool)));
|
SLOT(InsertSongs(int, const SongList&, int, bool, bool)));
|
||||||
connect(this, SIGNAL(RemoveSongs(int, const QList<int>&)),
|
connect(this, SIGNAL(RemoveSongs(int, const QList<int>&)), playlist_manager,
|
||||||
app_->playlist_manager(),
|
|
||||||
SLOT(RemoveItemsWithoutUndo(int, const QList<int>&)));
|
SLOT(RemoveItemsWithoutUndo(int, const QList<int>&)));
|
||||||
connect(this, SIGNAL(Open(int)), app_->playlist_manager(), SLOT(Open(int)));
|
connect(this, SIGNAL(New(const QString&)), playlist_manager,
|
||||||
connect(this, SIGNAL(Close(int)), app_->playlist_manager(), SLOT(Close(int)));
|
SLOT(New(const QString&)));
|
||||||
|
connect(this, SIGNAL(Open(int)), playlist_manager, SLOT(Open(int)));
|
||||||
|
connect(this, SIGNAL(Close(int)), playlist_manager, SLOT(Close(int)));
|
||||||
|
connect(this, SIGNAL(Clear(int)), playlist_manager, SLOT(Clear(int)));
|
||||||
|
connect(this, SIGNAL(Rename(int, const QString&)), playlist_manager,
|
||||||
|
SLOT(Rename(int, const QString&)));
|
||||||
|
connect(this, SIGNAL(Favorite(int, bool)), playlist_manager,
|
||||||
|
SLOT(Favorite(int, bool)));
|
||||||
|
|
||||||
connect(this, SIGNAL(RateCurrentSong(double)), app_->playlist_manager(),
|
connect(this, SIGNAL(RateCurrentSong(double)), playlist_manager,
|
||||||
SLOT(RateCurrentSong(double)));
|
SLOT(RateCurrentSong(double)));
|
||||||
|
|
||||||
#ifdef HAVE_LIBLASTFM
|
#ifdef HAVE_LIBLASTFM
|
||||||
|
@ -99,7 +109,6 @@ bool IncomingDataParser::close_connection() { return close_connection_; }
|
||||||
|
|
||||||
void IncomingDataParser::Parse(const pb::remote::Message& msg) {
|
void IncomingDataParser::Parse(const pb::remote::Message& msg) {
|
||||||
close_connection_ = false;
|
close_connection_ = false;
|
||||||
|
|
||||||
RemoteClient* client = qobject_cast<RemoteClient*>(sender());
|
RemoteClient* client = qobject_cast<RemoteClient*>(sender());
|
||||||
|
|
||||||
// Now check what's to do
|
// Now check what's to do
|
||||||
|
@ -167,6 +176,9 @@ void IncomingDataParser::Parse(const pb::remote::Message& msg) {
|
||||||
case pb::remote::CLOSE_PLAYLIST:
|
case pb::remote::CLOSE_PLAYLIST:
|
||||||
ClosePlaylist(msg);
|
ClosePlaylist(msg);
|
||||||
break;
|
break;
|
||||||
|
case pb::remote::UPDATE_PLAYLIST:
|
||||||
|
UpdatePlaylist(msg);
|
||||||
|
break;
|
||||||
case pb::remote::LOVE:
|
case pb::remote::LOVE:
|
||||||
emit Love();
|
emit Love();
|
||||||
break;
|
break;
|
||||||
|
@ -192,6 +204,18 @@ void IncomingDataParser::Parse(const pb::remote::Message& msg) {
|
||||||
case pb::remote::GLOBAL_SEARCH:
|
case pb::remote::GLOBAL_SEARCH:
|
||||||
GlobalSearch(client, msg);
|
GlobalSearch(client, msg);
|
||||||
break;
|
break;
|
||||||
|
case pb::remote::REQUEST_FILES:
|
||||||
|
emit SendListFiles(
|
||||||
|
QString::fromStdString(msg.request_list_files().relative_path()),
|
||||||
|
client);
|
||||||
|
break;
|
||||||
|
case pb::remote::APPEND_FILES:
|
||||||
|
AppendFilesToPlaylist(msg);
|
||||||
|
break;
|
||||||
|
case pb::remote::REQUEST_SAVED_RADIOS:
|
||||||
|
emit SendSavedRadios(client);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -267,6 +291,7 @@ void IncomingDataParser::SetShuffleMode(const pb::remote::Shuffle& shuffle) {
|
||||||
|
|
||||||
void IncomingDataParser::InsertUrls(const pb::remote::Message& msg) {
|
void IncomingDataParser::InsertUrls(const pb::remote::Message& msg) {
|
||||||
const pb::remote::RequestInsertUrls& request = msg.request_insert_urls();
|
const pb::remote::RequestInsertUrls& request = msg.request_insert_urls();
|
||||||
|
int playlist_id = request.playlist_id();
|
||||||
|
|
||||||
// Insert plain urls without metadata
|
// Insert plain urls without metadata
|
||||||
if (!request.urls().empty()) {
|
if (!request.urls().empty()) {
|
||||||
|
@ -276,9 +301,13 @@ void IncomingDataParser::InsertUrls(const pb::remote::Message& msg) {
|
||||||
urls << QUrl(QStringFromStdString(s));
|
urls << QUrl(QStringFromStdString(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (request.has_new_playlist_name())
|
||||||
|
playlist_id =
|
||||||
|
app_->playlist_manager()->New(request.new_playlist_name().c_str());
|
||||||
|
|
||||||
// Insert the urls
|
// Insert the urls
|
||||||
emit InsertUrls(request.playlist_id(), urls, request.position(),
|
emit InsertUrls(playlist_id, urls, request.position(), request.play_now(),
|
||||||
request.play_now(), request.enqueue());
|
request.enqueue());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add songs with metadata if present
|
// Add songs with metadata if present
|
||||||
|
@ -287,6 +316,13 @@ void IncomingDataParser::InsertUrls(const pb::remote::Message& msg) {
|
||||||
for (int i = 0; i < request.songs().size(); i++) {
|
for (int i = 0; i < request.songs().size(); i++) {
|
||||||
songs << CreateSongFromProtobuf(request.songs(i));
|
songs << CreateSongFromProtobuf(request.songs(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create a new playlist if required and not already done above by
|
||||||
|
// InsertUrls
|
||||||
|
if (request.has_new_playlist_name() && playlist_id == request.playlist_id())
|
||||||
|
playlist_id =
|
||||||
|
app_->playlist_manager()->New(request.new_playlist_name().c_str());
|
||||||
|
|
||||||
emit InsertSongs(request.playlist_id(), songs, request.position(),
|
emit InsertSongs(request.playlist_id(), songs, request.position(),
|
||||||
request.play_now(), request.enqueue());
|
request.play_now(), request.enqueue());
|
||||||
}
|
}
|
||||||
|
@ -338,6 +374,28 @@ void IncomingDataParser::ClosePlaylist(const pb::remote::Message& msg) {
|
||||||
emit Close(msg.request_close_playlist().playlist_id());
|
emit Close(msg.request_close_playlist().playlist_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IncomingDataParser::UpdatePlaylist(const pb::remote::Message& msg) {
|
||||||
|
const pb::remote::RequestUpdatePlaylist& req_update =
|
||||||
|
msg.request_update_playlist();
|
||||||
|
if (req_update.has_create_new_playlist() &&
|
||||||
|
req_update.create_new_playlist()) {
|
||||||
|
emit New(req_update.has_new_playlist_name()
|
||||||
|
? req_update.new_playlist_name().c_str()
|
||||||
|
: "New Playlist");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (req_update.has_clear_playlist() && req_update.clear_playlist()) {
|
||||||
|
emit Clear(req_update.playlist_id());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (req_update.has_new_playlist_name() &&
|
||||||
|
req_update.new_playlist_name().size())
|
||||||
|
emit Rename(req_update.playlist_id(),
|
||||||
|
req_update.new_playlist_name().c_str());
|
||||||
|
if (req_update.has_favorite())
|
||||||
|
emit Favorite(req_update.playlist_id(), req_update.favorite());
|
||||||
|
}
|
||||||
|
|
||||||
void IncomingDataParser::RateSong(const pb::remote::Message& msg) {
|
void IncomingDataParser::RateSong(const pb::remote::Message& msg) {
|
||||||
double rating = (double)msg.request_rate_song().rating();
|
double rating = (double)msg.request_rate_song().rating();
|
||||||
emit RateCurrentSong(rating);
|
emit RateCurrentSong(rating);
|
||||||
|
@ -370,3 +428,63 @@ Song IncomingDataParser::CreateSongFromProtobuf(
|
||||||
|
|
||||||
return song;
|
return song;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IncomingDataParser::AppendFilesToPlaylist(const pb::remote::Message& msg) {
|
||||||
|
if (files_root_folder_.isEmpty()) { // should never happen...
|
||||||
|
qLog(Warning) << "Remote root dir is not set although receiving "
|
||||||
|
"APPEND_FILES request...";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QDir root_dir(files_root_folder_);
|
||||||
|
if (!root_dir.exists()) {
|
||||||
|
qLog(Warning) << "Remote root dir doesn't exist...";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pb::remote::RequestAppendFiles& req_append = msg.request_append_files();
|
||||||
|
QString relative_path = QString::fromStdString(req_append.relative_path());
|
||||||
|
if (relative_path.startsWith("/")) relative_path.remove(0, 1);
|
||||||
|
|
||||||
|
QFileInfo fi_folder(root_dir, relative_path);
|
||||||
|
if (!fi_folder.exists())
|
||||||
|
qLog(Warning) << "Remote relative path " << relative_path
|
||||||
|
<< " doesn't exist...";
|
||||||
|
else if (!fi_folder.isDir())
|
||||||
|
qLog(Warning) << "Remote relative path " << relative_path
|
||||||
|
<< " is not a directory...";
|
||||||
|
else if (root_dir.relativeFilePath(fi_folder.absoluteFilePath())
|
||||||
|
.startsWith("../"))
|
||||||
|
qLog(Warning) << "Remote relative path " << relative_path
|
||||||
|
<< " should not be accessed...";
|
||||||
|
else {
|
||||||
|
QList<QUrl> urls;
|
||||||
|
QDir dir(fi_folder.absoluteFilePath());
|
||||||
|
for (const auto& file : req_append.files()) {
|
||||||
|
QFileInfo fi(dir, file.c_str());
|
||||||
|
if (fi.exists()) urls << QUrl::fromLocalFile(fi.canonicalFilePath());
|
||||||
|
}
|
||||||
|
if (urls.size()) {
|
||||||
|
MimeData* data = new MimeData;
|
||||||
|
data->setUrls(urls);
|
||||||
|
if (req_append.has_play_now()) data->play_now_ = req_append.play_now();
|
||||||
|
if (req_append.has_clear_first())
|
||||||
|
data->clear_first_ = req_append.clear_first();
|
||||||
|
if (req_append.has_new_playlist_name()) {
|
||||||
|
QString playlist_name =
|
||||||
|
QString::fromStdString(req_append.new_playlist_name());
|
||||||
|
if (!playlist_name.isEmpty()) {
|
||||||
|
data->open_in_new_playlist_ = true;
|
||||||
|
data->name_for_new_playlist_ = playlist_name;
|
||||||
|
}
|
||||||
|
} else if (req_append.has_playlist_id()) {
|
||||||
|
// if playing we will drop the files in another playlist
|
||||||
|
if (app_->player()->GetState() == Engine::Playing)
|
||||||
|
data->playlist_id = req_append.playlist_id();
|
||||||
|
else
|
||||||
|
// as me may play the song, we change the current playlist
|
||||||
|
emit SetCurrentPlaylist(req_append.playlist_id());
|
||||||
|
}
|
||||||
|
emit AddToPlaylistSignal(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,10 @@ class IncomingDataParser : public QObject {
|
||||||
|
|
||||||
bool close_connection();
|
bool close_connection();
|
||||||
|
|
||||||
|
void SetRemoteRootFiles(const QString& files_root_folder) {
|
||||||
|
files_root_folder_ = files_root_folder;
|
||||||
|
}
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void Parse(const pb::remote::Message& msg);
|
void Parse(const pb::remote::Message& msg);
|
||||||
void ReloadSettings();
|
void ReloadSettings();
|
||||||
|
@ -25,8 +29,12 @@ class IncomingDataParser : public QObject {
|
||||||
void SendAllPlaylists();
|
void SendAllPlaylists();
|
||||||
void SendAllActivePlaylists();
|
void SendAllActivePlaylists();
|
||||||
void SendPlaylistSongs(int id);
|
void SendPlaylistSongs(int id);
|
||||||
|
void New(const QString& new_playlist_name);
|
||||||
void Open(int id);
|
void Open(int id);
|
||||||
|
void Clear(int id);
|
||||||
void Close(int id);
|
void Close(int id);
|
||||||
|
void Rename(int id, const QString& new_playlist_name);
|
||||||
|
void Favorite(int id, bool favorite);
|
||||||
void GetLyrics();
|
void GetLyrics();
|
||||||
void Love();
|
void Love();
|
||||||
void Ban();
|
void Ban();
|
||||||
|
@ -56,10 +64,16 @@ class IncomingDataParser : public QObject {
|
||||||
|
|
||||||
void DoGlobalSearch(QString, RemoteClient*);
|
void DoGlobalSearch(QString, RemoteClient*);
|
||||||
|
|
||||||
|
void SendSavedRadios(RemoteClient* client);
|
||||||
|
void SendListFiles(QString, RemoteClient*);
|
||||||
|
void AddToPlaylistSignal(QMimeData* data);
|
||||||
|
void SetCurrentPlaylist(int id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Application* app_;
|
Application* app_;
|
||||||
bool close_connection_;
|
bool close_connection_;
|
||||||
MainWindow::PlaylistAddBehaviour doubleclick_playlist_addmode_;
|
MainWindow::PlaylistAddBehaviour doubleclick_playlist_addmode_;
|
||||||
|
QString files_root_folder_;
|
||||||
|
|
||||||
void GetPlaylistSongs(const pb::remote::Message& msg);
|
void GetPlaylistSongs(const pb::remote::Message& msg);
|
||||||
void ChangeSong(const pb::remote::Message& msg);
|
void ChangeSong(const pb::remote::Message& msg);
|
||||||
|
@ -71,8 +85,10 @@ class IncomingDataParser : public QObject {
|
||||||
void SendPlaylists(const pb::remote::Message& msg);
|
void SendPlaylists(const pb::remote::Message& msg);
|
||||||
void OpenPlaylist(const pb::remote::Message& msg);
|
void OpenPlaylist(const pb::remote::Message& msg);
|
||||||
void ClosePlaylist(const pb::remote::Message& msg);
|
void ClosePlaylist(const pb::remote::Message& msg);
|
||||||
|
void UpdatePlaylist(const pb::remote::Message& msg);
|
||||||
void RateSong(const pb::remote::Message& msg);
|
void RateSong(const pb::remote::Message& msg);
|
||||||
void GlobalSearch(RemoteClient* client, const pb::remote::Message& msg);
|
void GlobalSearch(RemoteClient* client, const pb::remote::Message& msg);
|
||||||
|
void AppendFilesToPlaylist(const pb::remote::Message& msg);
|
||||||
|
|
||||||
Song CreateSongFromProtobuf(const pb::remote::SongMetadata& pb);
|
Song CreateSongFromProtobuf(const pb::remote::SongMetadata& pb);
|
||||||
};
|
};
|
||||||
|
|
|
@ -72,6 +72,11 @@ void NetworkRemote::SetupServer() {
|
||||||
SLOT(AcceptConnection()));
|
SLOT(AcceptConnection()));
|
||||||
connect(server_ipv6_.get(), SIGNAL(newConnection()), this,
|
connect(server_ipv6_.get(), SIGNAL(newConnection()), this,
|
||||||
SLOT(AcceptConnection()));
|
SLOT(AcceptConnection()));
|
||||||
|
|
||||||
|
connect(incoming_data_parser_.get(), SIGNAL(AddToPlaylistSignal(QMimeData*)),
|
||||||
|
SIGNAL(AddToPlaylistSignal(QMimeData*)));
|
||||||
|
connect(incoming_data_parser_.get(), SIGNAL(SetCurrentPlaylist(int)),
|
||||||
|
SIGNAL(SetCurrentPlaylist(int)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkRemote::StartServer() {
|
void NetworkRemote::StartServer() {
|
||||||
|
@ -171,6 +176,13 @@ void NetworkRemote::AcceptConnection() {
|
||||||
SIGNAL(DoGlobalSearch(QString, RemoteClient*)),
|
SIGNAL(DoGlobalSearch(QString, RemoteClient*)),
|
||||||
outgoing_data_creator_.get(),
|
outgoing_data_creator_.get(),
|
||||||
SLOT(DoGlobalSearch(QString, RemoteClient*)));
|
SLOT(DoGlobalSearch(QString, RemoteClient*)));
|
||||||
|
|
||||||
|
connect(incoming_data_parser_.get(),
|
||||||
|
SIGNAL(SendListFiles(QString, RemoteClient*)),
|
||||||
|
outgoing_data_creator_.get(),
|
||||||
|
SLOT(SendListFiles(QString, RemoteClient*)));
|
||||||
|
connect(incoming_data_parser_.get(), SIGNAL(SendSavedRadios(RemoteClient*)),
|
||||||
|
outgoing_data_creator_.get(), SLOT(SendSavedRadios(RemoteClient*)));
|
||||||
}
|
}
|
||||||
|
|
||||||
QTcpServer* server = qobject_cast<QTcpServer*>(sender());
|
QTcpServer* server = qobject_cast<QTcpServer*>(sender());
|
||||||
|
@ -213,6 +225,14 @@ void NetworkRemote::CreateRemoteClient(QTcpSocket* client_socket) {
|
||||||
RemoteClient* client = new RemoteClient(app_, client_socket);
|
RemoteClient* client = new RemoteClient(app_, client_socket);
|
||||||
clients_.push_back(client);
|
clients_.push_back(client);
|
||||||
|
|
||||||
|
// Update the Remote Root Files for the latest Client
|
||||||
|
outgoing_data_creator_->SetMusicExtensions(
|
||||||
|
client->files_music_extensions());
|
||||||
|
outgoing_data_creator_->SetRemoteRootFiles(client->files_root_folder());
|
||||||
|
incoming_data_parser_->SetRemoteRootFiles(client->files_root_folder());
|
||||||
|
// update OutgoingDataCreator with latest allow_downloads setting
|
||||||
|
outgoing_data_creator_->SetAllowDownloads(client->allow_downloads());
|
||||||
|
|
||||||
// Connect the signal to parse data
|
// Connect the signal to parse data
|
||||||
connect(client, SIGNAL(Parse(pb::remote::Message)),
|
connect(client, SIGNAL(Parse(pb::remote::Message)),
|
||||||
incoming_data_parser_.get(), SLOT(Parse(pb::remote::Message)));
|
incoming_data_parser_.get(), SLOT(Parse(pb::remote::Message)));
|
||||||
|
|
|
@ -13,6 +13,7 @@ class QImage;
|
||||||
class QTcpServer;
|
class QTcpServer;
|
||||||
class QTcpSocket;
|
class QTcpSocket;
|
||||||
class RemoteClient;
|
class RemoteClient;
|
||||||
|
class QMimeData;
|
||||||
|
|
||||||
class NetworkRemote : public QObject {
|
class NetworkRemote : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -24,6 +25,10 @@ class NetworkRemote : public QObject {
|
||||||
explicit NetworkRemote(Application* app, QObject* parent = nullptr);
|
explicit NetworkRemote(Application* app, QObject* parent = nullptr);
|
||||||
~NetworkRemote();
|
~NetworkRemote();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void AddToPlaylistSignal(QMimeData* data);
|
||||||
|
void SetCurrentPlaylist(int id);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void SetupServer();
|
void SetupServer();
|
||||||
void StartServer();
|
void StartServer();
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include "outgoingdatacreator.h"
|
#include "outgoingdatacreator.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
#include <QSqlDatabase>
|
#include <QSqlDatabase>
|
||||||
#include <QSqlQuery>
|
#include <QSqlQuery>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
@ -26,6 +27,8 @@
|
||||||
#include "core/timeconstants.h"
|
#include "core/timeconstants.h"
|
||||||
#include "core/utilities.h"
|
#include "core/utilities.h"
|
||||||
#include "globalsearch/librarysearchprovider.h"
|
#include "globalsearch/librarysearchprovider.h"
|
||||||
|
#include "internet/core/internetmodel.h"
|
||||||
|
#include "internet/internetradio/savedradio.h"
|
||||||
#include "library/librarybackend.h"
|
#include "library/librarybackend.h"
|
||||||
#include "networkremote.h"
|
#include "networkremote.h"
|
||||||
#include "ui/iconloader.h"
|
#include "ui/iconloader.h"
|
||||||
|
@ -180,10 +183,15 @@ void OutgoingDataCreator::SendClementineInfo() {
|
||||||
msg.mutable_response_clementine_info();
|
msg.mutable_response_clementine_info();
|
||||||
SetEngineState(info);
|
SetEngineState(info);
|
||||||
|
|
||||||
|
// allowed extensions for REQUEST_FILES and LIST_FILES
|
||||||
|
for (const QString& ext : files_music_extensions_)
|
||||||
|
*info->add_files_music_extensions() = ext.toStdString();
|
||||||
|
|
||||||
QString version =
|
QString version =
|
||||||
QString("%1 %2").arg(QCoreApplication::applicationName(),
|
QString("%1 %2").arg(QCoreApplication::applicationName(),
|
||||||
QCoreApplication::applicationVersion());
|
QCoreApplication::applicationVersion());
|
||||||
info->set_version(version.toLatin1());
|
info->set_version(version.toLatin1());
|
||||||
|
info->set_allow_downloads(allow_downloads_);
|
||||||
SendDataToClients(&msg);
|
SendDataToClients(&msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,20 +216,22 @@ void OutgoingDataCreator::SetEngineState(
|
||||||
|
|
||||||
void OutgoingDataCreator::SendAllPlaylists() {
|
void OutgoingDataCreator::SendAllPlaylists() {
|
||||||
// Get all Playlists
|
// Get all Playlists
|
||||||
QList<Playlist*> app_playlists = app_->playlist_manager()->GetAllPlaylists();
|
PlaylistManager* playlist_manager = app_->playlist_manager();
|
||||||
int active_playlist = app_->playlist_manager()->active_id();
|
int active_playlist = playlist_manager->active_id();
|
||||||
|
|
||||||
// Create message
|
// Create message
|
||||||
pb::remote::Message msg;
|
pb::remote::Message msg;
|
||||||
msg.set_type(pb::remote::PLAYLISTS);
|
msg.set_type(pb::remote::PLAYLISTS);
|
||||||
|
|
||||||
pb::remote::ResponsePlaylists* playlists = msg.mutable_response_playlists();
|
pb::remote::ResponsePlaylists* playlists = msg.mutable_response_playlists();
|
||||||
|
playlists->set_include_closed(true);
|
||||||
|
|
||||||
// Get all playlists, even ones that are hidden in the UI.
|
// Get all playlists, even ones that are hidden in the UI.
|
||||||
for (const PlaylistBackend::Playlist& p :
|
for (const PlaylistBackend::Playlist& p :
|
||||||
app_->playlist_backend()->GetAllPlaylists()) {
|
app_->playlist_backend()->GetAllPlaylists()) {
|
||||||
bool playlist_open = app_->playlist_manager()->IsPlaylistOpen(p.id);
|
bool playlist_open = playlist_manager->IsPlaylistOpen(p.id);
|
||||||
int item_count = playlist_open ? app_playlists.at(p.id)->rowCount() : 0;
|
int item_count =
|
||||||
|
playlist_open ? playlist_manager->playlist(p.id)->rowCount() : 0;
|
||||||
|
|
||||||
// Create a new playlist
|
// Create a new playlist
|
||||||
pb::remote::Playlist* playlist = playlists->add_playlist();
|
pb::remote::Playlist* playlist = playlists->add_playlist();
|
||||||
|
@ -230,6 +240,7 @@ void OutgoingDataCreator::SendAllPlaylists() {
|
||||||
playlist->set_active((p.id == active_playlist));
|
playlist->set_active((p.id == active_playlist));
|
||||||
playlist->set_item_count(item_count);
|
playlist->set_item_count(item_count);
|
||||||
playlist->set_closed(!playlist_open);
|
playlist->set_closed(!playlist_open);
|
||||||
|
playlist->set_favorite(p.favorite);
|
||||||
}
|
}
|
||||||
|
|
||||||
SendDataToClients(&msg);
|
SendDataToClients(&msg);
|
||||||
|
@ -259,6 +270,7 @@ void OutgoingDataCreator::SendAllActivePlaylists() {
|
||||||
playlist->set_active((p->id() == active_playlist));
|
playlist->set_active((p->id() == active_playlist));
|
||||||
playlist->set_item_count(p->rowCount());
|
playlist->set_item_count(p->rowCount());
|
||||||
playlist->set_closed(false);
|
playlist->set_closed(false);
|
||||||
|
playlist->set_favorite(p->is_favorite());
|
||||||
}
|
}
|
||||||
|
|
||||||
SendDataToClients(&msg);
|
SendDataToClients(&msg);
|
||||||
|
@ -289,8 +301,9 @@ void OutgoingDataCreator::PlaylistRenamed(int id, const QString& new_name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutgoingDataCreator::SendFirstData(bool send_playlist_songs) {
|
void OutgoingDataCreator::SendFirstData(bool send_playlist_songs) {
|
||||||
|
Player* player = app_->player();
|
||||||
// First Send the current song
|
// First Send the current song
|
||||||
PlaylistItemPtr item = app_->player()->GetCurrentItem();
|
PlaylistItemPtr item = player->GetCurrentItem();
|
||||||
if (!item) {
|
if (!item) {
|
||||||
qLog(Info) << "No current item found!";
|
qLog(Info) << "No current item found!";
|
||||||
}
|
}
|
||||||
|
@ -298,11 +311,11 @@ void OutgoingDataCreator::SendFirstData(bool send_playlist_songs) {
|
||||||
CurrentSongChanged(current_song_, current_uri_, current_image_);
|
CurrentSongChanged(current_song_, current_uri_, current_image_);
|
||||||
|
|
||||||
// then the current volume
|
// then the current volume
|
||||||
VolumeChanged(app_->player()->GetVolume());
|
VolumeChanged(player->GetVolume());
|
||||||
|
|
||||||
// Check if we need to start the track position timer
|
// Check if we need to start the track position timer
|
||||||
if (!track_position_timer_->isActive() &&
|
if (!track_position_timer_->isActive() &&
|
||||||
app_->player()->engine()->state() == Engine::Playing) {
|
player->engine()->state() == Engine::Playing) {
|
||||||
track_position_timer_->start(1000);
|
track_position_timer_->start(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,11 +559,11 @@ void OutgoingDataCreator::UpdateTrackPosition() {
|
||||||
pb::remote::Message msg;
|
pb::remote::Message msg;
|
||||||
msg.set_type(pb::remote::UPDATE_TRACK_POSITION);
|
msg.set_type(pb::remote::UPDATE_TRACK_POSITION);
|
||||||
|
|
||||||
int position = std::floor(
|
qint64 position_nanosec = app_->player()->engine()->position_nanosec();
|
||||||
float(app_->player()->engine()->position_nanosec()) / kNsecPerSec + 0.5);
|
int position = static_cast<int>(
|
||||||
|
std::floor(static_cast<double>(position_nanosec) / kNsecPerSec + 0.5));
|
||||||
|
|
||||||
if (app_->player()->engine()->position_nanosec() >
|
if (position_nanosec > current_song_.length_nanosec())
|
||||||
current_song_.length_nanosec())
|
|
||||||
position = last_track_position_;
|
position = last_track_position_;
|
||||||
|
|
||||||
msg.mutable_response_update_track_position()->set_position(position);
|
msg.mutable_response_update_track_position()->set_position(position);
|
||||||
|
@ -744,3 +757,71 @@ void OutgoingDataCreator::SearchFinished(int id) {
|
||||||
|
|
||||||
qLog(Debug) << "SearchFinished" << req.id_ << req.query_;
|
qLog(Debug) << "SearchFinished" << req.id_ << req.query_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OutgoingDataCreator::SendListFiles(QString relative_path,
|
||||||
|
RemoteClient* client) {
|
||||||
|
pb::remote::Message msg;
|
||||||
|
msg.set_type(pb::remote::LIST_FILES);
|
||||||
|
pb::remote::ResponseListFiles* files = msg.mutable_response_list_files();
|
||||||
|
// Security checks
|
||||||
|
if (files_root_folder_.isEmpty()) {
|
||||||
|
files->set_error(pb::remote::ResponseListFiles::ROOT_DIR_NOT_SET);
|
||||||
|
SendDataToClients(&msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir root_dir(files_root_folder_);
|
||||||
|
if (!root_dir.exists())
|
||||||
|
files->set_error(pb::remote::ResponseListFiles::ROOT_DIR_NOT_SET);
|
||||||
|
else if (relative_path.startsWith("..") || relative_path.startsWith("./.."))
|
||||||
|
files->set_error(pb::remote::ResponseListFiles::DIR_NOT_ACCESSIBLE);
|
||||||
|
else {
|
||||||
|
if (relative_path.startsWith("/")) relative_path.remove(0, 1);
|
||||||
|
|
||||||
|
QFileInfo fi_folder(root_dir, relative_path);
|
||||||
|
if (!fi_folder.exists())
|
||||||
|
files->set_error(pb::remote::ResponseListFiles::DIR_NOT_EXIST);
|
||||||
|
else if (!fi_folder.isDir())
|
||||||
|
files->set_error(pb::remote::ResponseListFiles::DIR_NOT_EXIST);
|
||||||
|
else if (root_dir.relativeFilePath(fi_folder.absoluteFilePath())
|
||||||
|
.startsWith("../"))
|
||||||
|
files->set_error(pb::remote::ResponseListFiles::DIR_NOT_ACCESSIBLE);
|
||||||
|
else {
|
||||||
|
files->set_relative_path(
|
||||||
|
root_dir.relativeFilePath(fi_folder.absoluteFilePath())
|
||||||
|
.toStdString());
|
||||||
|
QDir dir(fi_folder.absoluteFilePath());
|
||||||
|
dir.setFilter(QDir::NoDotAndDotDot | QDir::AllEntries);
|
||||||
|
dir.setSorting(QDir::Name | QDir::DirsFirst);
|
||||||
|
|
||||||
|
for (const QFileInfo& fi : dir.entryInfoList()) {
|
||||||
|
if (fi.isDir() || files_music_extensions_.contains(fi.suffix())) {
|
||||||
|
pb::remote::FileMetadata* pb_file = files->add_files();
|
||||||
|
pb_file->set_is_dir(fi.isDir());
|
||||||
|
pb_file->set_filename(fi.fileName().toStdString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client->SendData(&msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OutgoingDataCreator::SendSavedRadios(RemoteClient* client) {
|
||||||
|
pb::remote::Message msg;
|
||||||
|
msg.set_type(pb::remote::REQUEST_SAVED_RADIOS);
|
||||||
|
|
||||||
|
SavedRadio* radio_service = static_cast<SavedRadio*>(
|
||||||
|
InternetModel::ServiceByName(SavedRadio::kServiceName));
|
||||||
|
if (radio_service) {
|
||||||
|
pb::remote::ResponseSavedRadios* radios =
|
||||||
|
msg.mutable_response_saved_radios();
|
||||||
|
for (const auto& stream : radio_service->Streams()) {
|
||||||
|
pb::remote::Stream* pb_stream = radios->add_streams();
|
||||||
|
pb_stream->set_name(stream.name_.toStdString());
|
||||||
|
pb_stream->set_url(stream.url_.toString().toStdString());
|
||||||
|
if (!stream.url_logo_.isEmpty())
|
||||||
|
pb_stream->set_url_logo(stream.url_logo_.toString().toStdString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client->SendData(&msg);
|
||||||
|
}
|
||||||
|
|
|
@ -47,7 +47,15 @@ class OutgoingDataCreator : public QObject {
|
||||||
static const quint32 kFileChunkSize;
|
static const quint32 kFileChunkSize;
|
||||||
|
|
||||||
void SetClients(QList<RemoteClient*>* clients);
|
void SetClients(QList<RemoteClient*>* clients);
|
||||||
|
void SetRemoteRootFiles(const QString& files_root_folder) {
|
||||||
|
files_root_folder_ = files_root_folder;
|
||||||
|
}
|
||||||
|
void SetMusicExtensions(const QStringList& files_music_extensions) {
|
||||||
|
files_music_extensions_ = files_music_extensions;
|
||||||
|
}
|
||||||
|
void SetAllowDownloads(bool allow_downloads) {
|
||||||
|
allow_downloads_ = allow_downloads;
|
||||||
|
}
|
||||||
static void CreateSong(const Song& song, const QImage& art, const int index,
|
static void CreateSong(const Song& song, const QImage& art, const int index,
|
||||||
pb::remote::SongMetadata* song_metadata);
|
pb::remote::SongMetadata* song_metadata);
|
||||||
|
|
||||||
|
@ -83,6 +91,9 @@ class OutgoingDataCreator : public QObject {
|
||||||
void ResultsAvailable(int id, const SearchProvider::ResultList& results);
|
void ResultsAvailable(int id, const SearchProvider::ResultList& results);
|
||||||
void SearchFinished(int id);
|
void SearchFinished(int id);
|
||||||
|
|
||||||
|
void SendListFiles(QString relative_path, RemoteClient* client);
|
||||||
|
void SendSavedRadios(RemoteClient* client);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Application* app_;
|
Application* app_;
|
||||||
QList<RemoteClient*>* clients_;
|
QList<RemoteClient*>* clients_;
|
||||||
|
@ -95,6 +106,9 @@ class OutgoingDataCreator : public QObject {
|
||||||
int keep_alive_timeout_;
|
int keep_alive_timeout_;
|
||||||
int last_track_position_;
|
int last_track_position_;
|
||||||
bool aww_;
|
bool aww_;
|
||||||
|
QString files_root_folder_;
|
||||||
|
QStringList files_music_extensions_;
|
||||||
|
bool allow_downloads_;
|
||||||
|
|
||||||
std::unique_ptr<UltimateLyricsReader> ultimate_reader_;
|
std::unique_ptr<UltimateLyricsReader> ultimate_reader_;
|
||||||
QMap<int, SongInfoFetcher::Result> results_;
|
QMap<int, SongInfoFetcher::Result> results_;
|
||||||
|
|
|
@ -28,9 +28,6 @@ RemoteClient::RemoteClient(Application* app, QTcpSocket* client)
|
||||||
downloader_(false),
|
downloader_(false),
|
||||||
client_(client),
|
client_(client),
|
||||||
song_sender_(new SongSender(app, this)) {
|
song_sender_(new SongSender(app, this)) {
|
||||||
// Open the buffer
|
|
||||||
buffer_.setData(QByteArray());
|
|
||||||
buffer_.open(QIODevice::ReadWrite);
|
|
||||||
reading_protobuf_ = false;
|
reading_protobuf_ = false;
|
||||||
|
|
||||||
// Connect to the slot IncomingData when receiving data
|
// Connect to the slot IncomingData when receiving data
|
||||||
|
@ -43,7 +40,11 @@ RemoteClient::RemoteClient(Application* app, QTcpSocket* client)
|
||||||
use_auth_code_ = s.value("use_auth_code", false).toBool();
|
use_auth_code_ = s.value("use_auth_code", false).toBool();
|
||||||
auth_code_ = s.value("auth_code", 0).toInt();
|
auth_code_ = s.value("auth_code", 0).toInt();
|
||||||
allow_downloads_ = s.value("allow_downloads", false).toBool();
|
allow_downloads_ = s.value("allow_downloads", false).toBool();
|
||||||
|
files_root_folder_ = s.value("files_root_folder", "").toString();
|
||||||
|
files_music_extensions_ =
|
||||||
|
s.value("files_music_extensions",
|
||||||
|
Application::kDefaultMusicExtensionsAllowedRemotely)
|
||||||
|
.toStringList();
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
|
||||||
// If we don't use an auth code, we don't need to authenticate the client.
|
// If we don't use an auth code, we don't need to authenticate the client.
|
||||||
|
@ -86,17 +87,16 @@ void RemoteClient::IncomingData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read some of the message
|
// Read some of the message
|
||||||
buffer_.write(client_->read(expected_length_ - buffer_.size()));
|
buffer_.append(
|
||||||
|
client_->read(static_cast<qint32>(expected_length_) - buffer_.size()));
|
||||||
|
|
||||||
// Did we get everything?
|
// Did we get everything?
|
||||||
if (buffer_.size() == expected_length_) {
|
if (buffer_.size() == static_cast<qint32>(expected_length_)) {
|
||||||
// Parse the message
|
// Parse the message
|
||||||
ParseMessage(buffer_.data());
|
ParseMessage(buffer_);
|
||||||
|
|
||||||
// Clear the buffer
|
// Clear the buffer
|
||||||
buffer_.close();
|
buffer_.clear();
|
||||||
buffer_.setData(QByteArray());
|
|
||||||
buffer_.open(QIODevice::ReadWrite);
|
|
||||||
reading_protobuf_ = false;
|
reading_protobuf_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
#ifndef REMOTECLIENT_H
|
#ifndef REMOTECLIENT_H
|
||||||
#define REMOTECLIENT_H
|
#define REMOTECLIENT_H
|
||||||
|
|
||||||
#include <QAbstractSocket>
|
|
||||||
#include <QBuffer>
|
|
||||||
#include <QTcpSocket>
|
#include <QTcpSocket>
|
||||||
|
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
|
@ -23,6 +21,11 @@ class RemoteClient : public QObject {
|
||||||
void DisconnectClient(pb::remote::ReasonDisconnect reason);
|
void DisconnectClient(pb::remote::ReasonDisconnect reason);
|
||||||
|
|
||||||
SongSender* song_sender() { return song_sender_; }
|
SongSender* song_sender() { return song_sender_; }
|
||||||
|
const QString& files_root_folder() const { return files_root_folder_; }
|
||||||
|
const QStringList& files_music_extensions() const {
|
||||||
|
return files_music_extensions_;
|
||||||
|
}
|
||||||
|
bool allow_downloads() const { return allow_downloads_; }
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void IncomingData();
|
void IncomingData();
|
||||||
|
@ -47,8 +50,11 @@ class RemoteClient : public QObject {
|
||||||
QTcpSocket* client_;
|
QTcpSocket* client_;
|
||||||
bool reading_protobuf_;
|
bool reading_protobuf_;
|
||||||
quint32 expected_length_;
|
quint32 expected_length_;
|
||||||
QBuffer buffer_;
|
QByteArray buffer_;
|
||||||
SongSender* song_sender_;
|
SongSender* song_sender_;
|
||||||
|
|
||||||
|
QString files_root_folder_;
|
||||||
|
QStringList files_music_extensions_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // REMOTECLIENT_H
|
#endif // REMOTECLIENT_H
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include "songsender.h"
|
#include "songsender.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
|
@ -87,7 +88,7 @@ void SongSender::SendSongs(const pb::remote::RequestDownloadSongs& request) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case pb::remote::APlaylist:
|
case pb::remote::APlaylist:
|
||||||
SendPlaylist(request.playlist_id());
|
SendPlaylist(request);
|
||||||
break;
|
break;
|
||||||
case pb::remote::Urls:
|
case pb::remote::Urls:
|
||||||
SendUrls(request);
|
SendUrls(request);
|
||||||
|
@ -321,7 +322,8 @@ void SongSender::SendAlbum(const Song& song) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SongSender::SendPlaylist(int playlist_id) {
|
void SongSender::SendPlaylist(const pb::remote::RequestDownloadSongs& request) {
|
||||||
|
int playlist_id = request.playlist_id();
|
||||||
Playlist* playlist = app_->playlist_manager()->playlist(playlist_id);
|
Playlist* playlist = app_->playlist_manager()->playlist(playlist_id);
|
||||||
if (!playlist) {
|
if (!playlist) {
|
||||||
qLog(Info) << "Could not find playlist with id = " << playlist_id;
|
qLog(Info) << "Could not find playlist with id = " << playlist_id;
|
||||||
|
@ -329,17 +331,22 @@ void SongSender::SendPlaylist(int playlist_id) {
|
||||||
}
|
}
|
||||||
SongList song_list = playlist->GetAllSongs();
|
SongList song_list = playlist->GetAllSongs();
|
||||||
|
|
||||||
|
QList<int> requested_ids;
|
||||||
|
for (auto song_id : request.songs_ids()) requested_ids << song_id;
|
||||||
|
|
||||||
// Count the local songs
|
// Count the local songs
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (Song s : song_list) {
|
for (Song s : song_list) {
|
||||||
if (s.url().scheme() == "file") {
|
if (s.url().scheme() == "file" &&
|
||||||
|
(requested_ids.isEmpty() || requested_ids.contains(s.id()))) {
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Song s : song_list) {
|
for (Song s : song_list) {
|
||||||
// Only local files!
|
// Only local files!
|
||||||
if (s.url().scheme() == "file") {
|
if (s.url().scheme() == "file" &&
|
||||||
|
(requested_ids.isEmpty() || requested_ids.contains(s.id()))) {
|
||||||
DownloadItem item(s, song_list.indexOf(s) + 1, count);
|
DownloadItem item(s, song_list.indexOf(s) + 1, count);
|
||||||
download_queue_.append(item);
|
download_queue_.append(item);
|
||||||
}
|
}
|
||||||
|
@ -350,14 +357,45 @@ void SongSender::SendUrls(const pb::remote::RequestDownloadSongs& request) {
|
||||||
SongList song_list;
|
SongList song_list;
|
||||||
|
|
||||||
// First gather all valid songs
|
// First gather all valid songs
|
||||||
for (auto it = request.urls().begin(); it != request.urls().end(); ++it) {
|
if (request.has_relative_path()) {
|
||||||
std::string s = *it;
|
// Security checks, cf OutgoingDataCreator::SendListFiles
|
||||||
QUrl url = QUrl(QStringFromStdString(s));
|
const QString& files_root_folder = client_->files_root_folder();
|
||||||
|
if (files_root_folder.isEmpty()) return;
|
||||||
|
QDir root_dir(files_root_folder);
|
||||||
|
QString relative_path(request.relative_path().c_str());
|
||||||
|
if (!root_dir.exists() || relative_path.startsWith("..") ||
|
||||||
|
relative_path.startsWith("./.."))
|
||||||
|
return;
|
||||||
|
if (relative_path.startsWith("/")) relative_path.remove(0, 1);
|
||||||
|
|
||||||
Song song = app_->library_backend()->GetSongByUrl(url);
|
QFileInfo fi_folder(root_dir, relative_path);
|
||||||
|
if (!fi_folder.exists() || !fi_folder.isDir() ||
|
||||||
|
root_dir.relativeFilePath(fi_folder.absoluteFilePath())
|
||||||
|
.startsWith("../"))
|
||||||
|
return;
|
||||||
|
|
||||||
if (song.is_valid() && song.url().scheme() == "file") {
|
QDir dir(fi_folder.absoluteFilePath());
|
||||||
song_list.append(song);
|
const QStringList& files_music_extensions =
|
||||||
|
client_->files_music_extensions();
|
||||||
|
for (const std::string& s : request.urls()) {
|
||||||
|
QFileInfo fi(dir, s.c_str());
|
||||||
|
if (fi.exists() && fi.isFile() &&
|
||||||
|
files_music_extensions.contains(fi.suffix())) {
|
||||||
|
Song song;
|
||||||
|
song.set_basefilename(fi.fileName());
|
||||||
|
song.set_filesize(fi.size());
|
||||||
|
song.set_url(QUrl::fromLocalFile(fi.absoluteFilePath()));
|
||||||
|
song.set_valid(true);
|
||||||
|
song_list.append(song);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const std::string& s : request.urls()) {
|
||||||
|
QUrl url = QUrl(QStringFromStdString(s));
|
||||||
|
Song song = app_->library_backend()->GetSongByUrl(url);
|
||||||
|
if (song.is_valid() && song.url().scheme() == "file") {
|
||||||
|
song_list.append(song);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ class SongSender : public QObject {
|
||||||
|
|
||||||
void SendSingleSong(DownloadItem download_item);
|
void SendSingleSong(DownloadItem download_item);
|
||||||
void SendAlbum(const Song& song);
|
void SendAlbum(const Song& song);
|
||||||
void SendPlaylist(int playlist_id);
|
void SendPlaylist(const pb::remote::RequestDownloadSongs& request);
|
||||||
void SendUrls(const pb::remote::RequestDownloadSongs& request);
|
void SendUrls(const pb::remote::RequestDownloadSongs& request);
|
||||||
void OfferNextSong();
|
void OfferNextSong();
|
||||||
void SendTotalFileSize();
|
void SendTotalFileSize();
|
||||||
|
|
|
@ -140,9 +140,9 @@ Playlist* PlaylistManager::AddPlaylist(int id, const QString& name,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlaylistManager::New(const QString& name, const SongList& songs,
|
int PlaylistManager::New(const QString& name, const SongList& songs,
|
||||||
const QString& special_type) {
|
const QString& special_type) {
|
||||||
if (name.isNull()) return;
|
if (name.isNull()) return -1;
|
||||||
|
|
||||||
int id = playlist_backend_->CreatePlaylist(name, special_type);
|
int id = playlist_backend_->CreatePlaylist(name, special_type);
|
||||||
|
|
||||||
|
@ -157,6 +157,8 @@ void PlaylistManager::New(const QString& name, const SongList& songs,
|
||||||
if (name == tr("Playlist")) {
|
if (name == tr("Playlist")) {
|
||||||
Rename(id, QString("%1 %2").arg(name).arg(id));
|
Rename(id, QString("%1 %2").arg(name).arg(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlaylistManager::Load(const QString& filename) {
|
void PlaylistManager::Load(const QString& filename) {
|
||||||
|
@ -295,6 +297,11 @@ void PlaylistManager::Favorite(int id, bool favorite) {
|
||||||
emit PlaylistFavorited(id, favorite);
|
emit PlaylistFavorited(id, favorite);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PlaylistManager::Clear(int id) {
|
||||||
|
if (playlists_.count() <= 1 || !playlists_.contains(id)) return;
|
||||||
|
playlists_[id].p->Clear();
|
||||||
|
}
|
||||||
|
|
||||||
bool PlaylistManager::Close(int id) {
|
bool PlaylistManager::Close(int id) {
|
||||||
// Won't allow removing the last playlist
|
// Won't allow removing the last playlist
|
||||||
if (playlists_.count() <= 1 || !playlists_.contains(id)) return false;
|
if (playlists_.count() <= 1 || !playlists_.contains(id)) return false;
|
||||||
|
@ -490,6 +497,7 @@ void PlaylistManager::InsertUrls(int id, const QList<QUrl>& urls, int pos,
|
||||||
bool play_now, bool enqueue) {
|
bool play_now, bool enqueue) {
|
||||||
Q_ASSERT(playlists_.contains(id));
|
Q_ASSERT(playlists_.contains(id));
|
||||||
|
|
||||||
|
if (play_now && active_ != id) SetActivePlaylist(id);
|
||||||
playlists_[id].p->InsertUrls(urls, pos, play_now, enqueue);
|
playlists_[id].p->InsertUrls(urls, pos, play_now, enqueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -497,6 +505,7 @@ void PlaylistManager::InsertSongs(int id, const SongList& songs, int pos,
|
||||||
bool play_now, bool enqueue) {
|
bool play_now, bool enqueue) {
|
||||||
Q_ASSERT(playlists_.contains(id));
|
Q_ASSERT(playlists_.contains(id));
|
||||||
|
|
||||||
|
if (play_now && active_ != id) SetActivePlaylist(id);
|
||||||
playlists_[id].p->InsertSongs(songs, pos, play_now, enqueue);
|
playlists_[id].p->InsertSongs(songs, pos, play_now, enqueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,8 +73,8 @@ class PlaylistManagerInterface : public QObject {
|
||||||
virtual PlaylistContainer* playlist_container() const = 0;
|
virtual PlaylistContainer* playlist_container() const = 0;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void New(const QString& name, const SongList& songs = SongList(),
|
virtual int New(const QString& name, const SongList& songs = SongList(),
|
||||||
const QString& special_type = QString()) = 0;
|
const QString& special_type = QString()) = 0;
|
||||||
virtual void Load(const QString& filename) = 0;
|
virtual void Load(const QString& filename) = 0;
|
||||||
virtual void Save(int id, const QString& filename,
|
virtual void Save(int id, const QString& filename,
|
||||||
Playlist::Path path_type) = 0;
|
Playlist::Path path_type) = 0;
|
||||||
|
@ -181,8 +181,8 @@ class PlaylistManager : public PlaylistManagerInterface {
|
||||||
PlaylistContainer* playlist_container() const { return playlist_container_; }
|
PlaylistContainer* playlist_container() const { return playlist_container_; }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void New(const QString& name, const SongList& songs = SongList(),
|
int New(const QString& name, const SongList& songs = SongList(),
|
||||||
const QString& special_type = QString());
|
const QString& special_type = QString());
|
||||||
void Load(const QString& filename);
|
void Load(const QString& filename);
|
||||||
void Save(int id, const QString& filename, Playlist::Path path_type);
|
void Save(int id, const QString& filename, Playlist::Path path_type);
|
||||||
// Display a file dialog to let user choose a file before saving the file
|
// Display a file dialog to let user choose a file before saving the file
|
||||||
|
@ -190,6 +190,7 @@ class PlaylistManager : public PlaylistManagerInterface {
|
||||||
void Rename(int id, const QString& new_name);
|
void Rename(int id, const QString& new_name);
|
||||||
void Favorite(int id, bool favorite);
|
void Favorite(int id, bool favorite);
|
||||||
void Delete(int id);
|
void Delete(int id);
|
||||||
|
void Clear(int id);
|
||||||
bool Close(int id);
|
bool Close(int id);
|
||||||
void Open(int id);
|
void Open(int id);
|
||||||
void ChangePlaylistOrder(const QList<int>& ids);
|
void ChangePlaylistOrder(const QList<int>& ids);
|
||||||
|
|
|
@ -41,30 +41,33 @@ AddStreamDialog::AddStreamDialog(QWidget* parent)
|
||||||
ui_->save->setChecked(s.value("save", true).toBool());
|
ui_->save->setChecked(s.value("save", true).toBool());
|
||||||
ui_->url->setText(s.value("url").toString());
|
ui_->url->setText(s.value("url").toString());
|
||||||
ui_->name->setText(s.value("name").toString());
|
ui_->name->setText(s.value("name").toString());
|
||||||
|
ui_->url_logo->setText(s.value("url_logo").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
AddStreamDialog::~AddStreamDialog() { delete ui_; }
|
AddStreamDialog::~AddStreamDialog() { delete ui_; }
|
||||||
|
|
||||||
QUrl AddStreamDialog::url() const { return QUrl(ui_->url->text()); }
|
QUrl AddStreamDialog::url() const { return QUrl(ui_->url->text()); }
|
||||||
|
|
||||||
QString AddStreamDialog::name() const { return ui_->name->text(); }
|
QString AddStreamDialog::name() const { return ui_->name->text(); }
|
||||||
|
QUrl AddStreamDialog::url_logo() const { return QUrl(ui_->url_logo->text()); }
|
||||||
|
|
||||||
void AddStreamDialog::set_name(const QString& name) {
|
void AddStreamDialog::set_name(const QString& name) {
|
||||||
ui_->name->setText(name);
|
ui_->name->setText(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddStreamDialog::set_url(const QUrl& url) {
|
void AddStreamDialog::set_url(const QUrl& url) {
|
||||||
ui_->url->setText(url.toString());
|
ui_->url->setText(url.toString());
|
||||||
}
|
}
|
||||||
|
void AddStreamDialog::set_url_logo(const QUrl& url) {
|
||||||
|
ui_->url_logo->setText(url.toString());
|
||||||
|
}
|
||||||
|
|
||||||
void AddStreamDialog::set_save_visible(bool visible) {
|
void AddStreamDialog::set_save_visible(bool visible) {
|
||||||
ui_->save->setVisible(visible);
|
ui_->save->setVisible(visible);
|
||||||
if (!visible) ui_->name_container->setEnabled(true);
|
if (!visible) ui_->details_container->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddStreamDialog::accept() {
|
void AddStreamDialog::accept() {
|
||||||
if (ui_->save->isChecked() && saved_radio_) {
|
if (ui_->save->isChecked() && saved_radio_) {
|
||||||
saved_radio_->Add(url(), name());
|
saved_radio_->Add(url(), name(), url_logo());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save settings
|
// Save settings
|
||||||
|
|
|
@ -33,6 +33,7 @@ class AddStreamDialog : public QDialog {
|
||||||
|
|
||||||
void set_url(const QUrl& url);
|
void set_url(const QUrl& url);
|
||||||
void set_name(const QString& name);
|
void set_name(const QString& name);
|
||||||
|
void set_url_logo(const QUrl& url);
|
||||||
void set_save_visible(bool visible);
|
void set_save_visible(bool visible);
|
||||||
void set_add_on_accept(SavedRadio* saved_radio) {
|
void set_add_on_accept(SavedRadio* saved_radio) {
|
||||||
saved_radio_ = saved_radio;
|
saved_radio_ = saved_radio;
|
||||||
|
@ -40,6 +41,7 @@ class AddStreamDialog : public QDialog {
|
||||||
|
|
||||||
QUrl url() const;
|
QUrl url() const;
|
||||||
QString name() const;
|
QString name() const;
|
||||||
|
QUrl url_logo() const;
|
||||||
|
|
||||||
void accept();
|
void accept();
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>400</width>
|
<width>400</width>
|
||||||
<height>168</height>
|
<height>220</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -39,21 +39,31 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QWidget" name="name_container" native="true">
|
<widget class="QWidget" name="details_container" native="true">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QGridLayout" name="gridLayout">
|
||||||
<property name="margin">
|
<property name="margin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="label_2">
|
<widget class="QLabel" name="label_2">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Give it a name:</string>
|
<string>Give it a name:</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item row="0" column="1">
|
||||||
<widget class="QLineEdit" name="name"/>
|
<widget class="QLineEdit" name="name"/>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>URL of its Logo:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLineEdit" name="url_logo"/>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -127,7 +137,7 @@
|
||||||
<connection>
|
<connection>
|
||||||
<sender>save</sender>
|
<sender>save</sender>
|
||||||
<signal>toggled(bool)</signal>
|
<signal>toggled(bool)</signal>
|
||||||
<receiver>name_container</receiver>
|
<receiver>details_container</receiver>
|
||||||
<slot>setEnabled(bool)</slot>
|
<slot>setEnabled(bool)</slot>
|
||||||
<hints>
|
<hints>
|
||||||
<hint type="sourcelabel">
|
<hint type="sourcelabel">
|
||||||
|
|
|
@ -796,6 +796,12 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
|
||||||
connect(InternetModel::Service<SavedRadio>(), SIGNAL(ShowAddStreamDialog()),
|
connect(InternetModel::Service<SavedRadio>(), SIGNAL(ShowAddStreamDialog()),
|
||||||
SLOT(AddStream()));
|
SLOT(AddStream()));
|
||||||
|
|
||||||
|
// Network Remote
|
||||||
|
connect(app->network_remote(), SIGNAL(AddToPlaylistSignal(QMimeData*)),
|
||||||
|
SLOT(AddToPlaylist(QMimeData*)));
|
||||||
|
connect(app->network_remote(), SIGNAL(SetCurrentPlaylist(int)),
|
||||||
|
app_->playlist_manager(), SLOT(SetCurrentPlaylist(int)));
|
||||||
|
|
||||||
#ifdef Q_OS_DARWIN
|
#ifdef Q_OS_DARWIN
|
||||||
mac::SetApplicationHandler(this);
|
mac::SetApplicationHandler(this);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1669,6 +1675,7 @@ void MainWindow::ApplyPlayBehaviour(MainWindow::PlayBehaviour b,
|
||||||
void MainWindow::AddToPlaylist(QMimeData* data) {
|
void MainWindow::AddToPlaylist(QMimeData* data) {
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
|
Playlist* playlist = nullptr;
|
||||||
if (MimeData* mime_data = qobject_cast<MimeData*>(data)) {
|
if (MimeData* mime_data = qobject_cast<MimeData*>(data)) {
|
||||||
// Should we replace the flags with the user's preference?
|
// Should we replace the flags with the user's preference?
|
||||||
if (mime_data->override_user_settings_) {
|
if (mime_data->override_user_settings_) {
|
||||||
|
@ -1684,10 +1691,14 @@ void MainWindow::AddToPlaylist(QMimeData* data) {
|
||||||
if (mime_data->open_in_new_playlist_) {
|
if (mime_data->open_in_new_playlist_) {
|
||||||
app_->playlist_manager()->New(mime_data->get_name_for_new_playlist());
|
app_->playlist_manager()->New(mime_data->get_name_for_new_playlist());
|
||||||
}
|
}
|
||||||
|
// or shall we drop the songs in another playlist?
|
||||||
|
else if (mime_data->playlist_id != -1)
|
||||||
|
playlist = app_->playlist_manager()->playlist(mime_data->playlist_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
app_->playlist_manager()->current()->dropMimeData(data, Qt::CopyAction, -1, 0,
|
if (!playlist) playlist = app_->playlist_manager()->current();
|
||||||
QModelIndex());
|
|
||||||
|
playlist->dropMimeData(data, Qt::CopyAction, -1, 0, QModelIndex());
|
||||||
delete data;
|
delete data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,13 @@ void NetworkRemoteSettingsPage::Load() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui_->files_root_folder->SetPath(s.value("files_root_folder", "").toString());
|
||||||
|
ui_->files_music_extensions->setText(
|
||||||
|
s.value("files_music_extensions",
|
||||||
|
Application::kDefaultMusicExtensionsAllowedRemotely)
|
||||||
|
.toStringList()
|
||||||
|
.join(","));
|
||||||
|
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
|
||||||
// Get local ip addresses
|
// Get local ip addresses
|
||||||
|
@ -147,6 +154,17 @@ void NetworkRemoteSettingsPage::Save() {
|
||||||
.value<TranscoderPreset>();
|
.value<TranscoderPreset>();
|
||||||
s.setValue("last_output_format", preset.codec_mimetype_);
|
s.setValue("last_output_format", preset.codec_mimetype_);
|
||||||
|
|
||||||
|
s.setValue("files_root_folder", ui_->files_root_folder->Path());
|
||||||
|
|
||||||
|
QStringList files_music_extensions;
|
||||||
|
for (const QString& extension :
|
||||||
|
ui_->files_music_extensions->text().split(",")) {
|
||||||
|
QString ext = extension.trimmed();
|
||||||
|
if (ext.size() > 0 && ext.size() < 8) // no empty string, less than 8 char
|
||||||
|
files_music_extensions << ext;
|
||||||
|
}
|
||||||
|
s.setValue("files_music_extensions", files_music_extensions);
|
||||||
|
|
||||||
s.endGroup();
|
s.endGroup();
|
||||||
|
|
||||||
if (NetworkRemoteHelper::Instance()) {
|
if (NetworkRemoteHelper::Instance()) {
|
||||||
|
|
|
@ -101,7 +101,7 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="0">
|
<item row="9" column="0">
|
||||||
<widget class="QLabel" name="label_2">
|
<widget class="QLabel" name="label_2">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Enter this IP in the App to connect to Clementine.</string>
|
<string>Enter this IP in the App to connect to Clementine.</string>
|
||||||
|
@ -111,7 +111,7 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="1">
|
<item row="9" column="1">
|
||||||
<widget class="QLabel" name="ip_address">
|
<widget class="QLabel" name="ip_address">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string notr="true">127.0.0.1</string>
|
<string notr="true">127.0.0.1</string>
|
||||||
|
@ -128,7 +128,7 @@
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="0" colspan="2">
|
<item row="7" column="0" colspan="2">
|
||||||
<widget class="QGroupBox" name="download_settings_container">
|
<widget class="QGroupBox" name="download_settings_container">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
|
@ -172,6 +172,39 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="5" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Root folder that will be browsable from the network remote</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Files root folder</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="5" column="1">
|
||||||
|
<widget class="FileChooserWidget" name="files_root_folder" native="true">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>100</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="1">
|
||||||
|
<widget class="QLineEdit" name="files_music_extensions"/>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>coma separated list of the allowed extensions that will be visible from the network remote (ex: m3u,mp3,flac,ogg,wav)</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Music extensions remotely visible</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -259,6 +292,14 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>FileChooserWidget</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header location="global">ui/filechooserwidget.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="../../data/data.qrc"/>
|
<include location="../../data/data.qrc"/>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
Loading…
Reference in New Issue