Add the ability to add a Spotify track to a Spotify playlist through context menu.
TODO: drag and drop (i.e. implement DropMimeData), update special playlist (Favorites), remove from playlist.
This commit is contained in:
parent
60d82e212d
commit
39db4dbefe
@ -21,6 +21,7 @@
|
||||
#include "spotifyclient.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
@ -295,6 +296,8 @@ void SpotifyClient::MessageArrived(const pb::spotify::Message& message) {
|
||||
BrowseToplist(message.browse_toplist_request());
|
||||
} else if (message.has_pause_request()) {
|
||||
SetPaused(message.pause_request());
|
||||
} else if (message.has_add_tracks_to_playlist()) {
|
||||
AddTracksToPlaylist(message.add_tracks_to_playlist());
|
||||
}
|
||||
}
|
||||
|
||||
@ -438,6 +441,9 @@ void SpotifyClient::SendPlaylistList() {
|
||||
pb::spotify::Playlists::Playlist* msg = response->add_playlist();
|
||||
msg->set_index(i);
|
||||
msg->set_name(sp_playlist_name(playlist));
|
||||
sp_user* playlist_owner = sp_playlist_owner(playlist);
|
||||
msg->set_is_mine(sp_session_user(session_) == playlist_owner);
|
||||
msg->set_owner(sp_user_display_name(playlist_owner));
|
||||
|
||||
sp_playlist_offline_status offline_status =
|
||||
sp_playlist_get_offline_status(session_, playlist);
|
||||
@ -449,6 +455,7 @@ void SpotifyClient::SendPlaylistList() {
|
||||
} else if (offline_status == SP_PLAYLIST_OFFLINE_STATUS_WAITING) {
|
||||
msg->set_download_progress(0);
|
||||
}
|
||||
msg->set_nb_tracks(sp_playlist_num_tracks(playlist));
|
||||
}
|
||||
|
||||
SendMessage(message);
|
||||
@ -593,6 +600,45 @@ void SpotifyClient::PlaylistStateChangedForGetPlaylists(sp_playlist* pl,
|
||||
me->SendPlaylistList();
|
||||
}
|
||||
|
||||
void SpotifyClient::AddTracksToPlaylist(
|
||||
const pb::spotify::AddTracksToPlaylistRequest& req) {
|
||||
|
||||
// Get the playlist we want to update
|
||||
int playlist_index = req.playlist_index();
|
||||
sp_playlist* playlist =
|
||||
GetPlaylist(pb::spotify::UserPlaylist, playlist_index);
|
||||
if (!playlist) {
|
||||
qLog(Error) << "Playlist " << playlist_index << "not found";
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the tracks we want to add
|
||||
std::unique_ptr<sp_track*[]> tracks_array (new sp_track*[req.track_uri_size()]);
|
||||
for (int i = 0; i < req.track_uri_size(); ++i) {
|
||||
sp_link* track_link = sp_link_create_from_string(req.track_uri(i).c_str());
|
||||
sp_track* track = sp_link_as_track(track_link);
|
||||
sp_track_add_ref(track);
|
||||
sp_link_release(track_link);
|
||||
if (!track) {
|
||||
qLog(Error) << "Track" << QString::fromStdString(req.track_uri(i)) << "not found";
|
||||
}
|
||||
tracks_array[i] = track;
|
||||
}
|
||||
|
||||
// Actually add the tracks to the playlist
|
||||
if (sp_playlist_add_tracks(playlist, tracks_array.get(),
|
||||
req.track_uri_size(),
|
||||
0 /* TODO: don't insert at a hardcoded position */,
|
||||
session_) != SP_ERROR_OK) {
|
||||
qLog(Error) << "Error when adding tracks!";
|
||||
}
|
||||
|
||||
// Clean everything
|
||||
for (int i = 0; i < req.track_uri_size(); ++i) {
|
||||
sp_track_release(tracks_array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void SpotifyClient::ConvertTrack(sp_track* track, pb::spotify::Track* pb) {
|
||||
sp_album* album = sp_track_album(track);
|
||||
|
||||
|
@ -123,6 +123,7 @@ class SpotifyClient : public AbstractMessageHandler<pb::spotify::Message> {
|
||||
void Search(const pb::spotify::SearchRequest& req);
|
||||
void LoadPlaylist(const pb::spotify::LoadPlaylistRequest& req);
|
||||
void SyncPlaylist(const pb::spotify::SyncPlaylistRequest& req);
|
||||
void AddTracksToPlaylist(const pb::spotify::AddTracksToPlaylistRequest& req);
|
||||
void StartPlayback(const pb::spotify::PlaybackRequest& req);
|
||||
void Seek(qint64 offset_nsec);
|
||||
void LoadImage(const QString& id_b64);
|
||||
|
@ -46,9 +46,12 @@ message Playlists {
|
||||
message Playlist {
|
||||
required int32 index = 1;
|
||||
required string name = 2;
|
||||
required bool is_offline = 3;
|
||||
required int32 nb_tracks = 3;
|
||||
required bool is_mine = 4;
|
||||
required string owner= 5;
|
||||
required bool is_offline = 6;
|
||||
// Offline sync progress between 0-100.
|
||||
optional int32 download_progress = 4;
|
||||
optional int32 download_progress = 7;
|
||||
}
|
||||
|
||||
repeated Playlist playlist = 1;
|
||||
@ -191,7 +194,12 @@ message PauseRequest {
|
||||
optional bool paused = 1 [default = false];
|
||||
}
|
||||
|
||||
// NEXT_ID: 23
|
||||
message AddTracksToPlaylistRequest {
|
||||
required int64 playlist_index = 1;
|
||||
repeated string track_uri = 2;
|
||||
}
|
||||
|
||||
// NEXT_ID: 24
|
||||
message Message {
|
||||
// Not currently used
|
||||
optional int32 id = 18;
|
||||
@ -217,4 +225,5 @@ message Message {
|
||||
optional BrowseToplistResponse browse_toplist_response = 20;
|
||||
optional PauseRequest pause_request = 21;
|
||||
optional SeekCompleted seek_completed = 22;
|
||||
optional AddTracksToPlaylistRequest add_tracks_to_playlist = 23;
|
||||
}
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <QTcpServer>
|
||||
#include <QTcpSocket>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
|
||||
SpotifyServer::SpotifyServer(QObject* parent)
|
||||
: AbstractMessageHandler<pb::spotify::Message>(nullptr, parent),
|
||||
@ -206,6 +207,18 @@ void SpotifyServer::LoadUserPlaylist(int index) {
|
||||
LoadPlaylist(pb::spotify::UserPlaylist, index);
|
||||
}
|
||||
|
||||
void SpotifyServer::AddSongsToPlaylist(int playlist_index,
|
||||
const QList<QUrl>& songs_urls) {
|
||||
pb::spotify::Message message;
|
||||
pb::spotify::AddTracksToPlaylistRequest* req =
|
||||
message.mutable_add_tracks_to_playlist();
|
||||
req->set_playlist_index(playlist_index);
|
||||
for (const QUrl& song_url : songs_urls) {
|
||||
req->add_track_uri(DataCommaSizeFromQString(song_url.toString()));
|
||||
}
|
||||
SendOrQueueMessage(message);
|
||||
}
|
||||
|
||||
void SpotifyServer::StartPlaybackLater(const QString& uri, quint16 port) {
|
||||
QTimer* timer = new QTimer(this);
|
||||
connect(timer, SIGNAL(timeout()), timer, SLOT(deleteLater()));
|
||||
|
@ -43,6 +43,7 @@ class SpotifyServer : public AbstractMessageHandler<pb::spotify::Message> {
|
||||
void SyncInbox();
|
||||
void LoadUserPlaylist(int index);
|
||||
void SyncUserPlaylist(int index);
|
||||
void AddSongsToPlaylist(int playlist_index, const QList<QUrl>& songs_urls);
|
||||
void StartPlaybackLater(const QString& uri, quint16 port);
|
||||
void Search(const QString& text, int limit, int limit_album = 0);
|
||||
void LoadImage(const QString& id);
|
||||
|
@ -345,6 +345,17 @@ void SpotifyService::InstallBlob() {
|
||||
|
||||
void SpotifyService::BlobDownloadFinished() { EnsureServerCreated(); }
|
||||
|
||||
void SpotifyService::AddCurrentSongToPlaylist(QAction* action) {
|
||||
int playlist_index = action->data().toInt();
|
||||
AddSongsToPlaylist(playlist_index, QList<QUrl>() << current_song_url_);
|
||||
}
|
||||
|
||||
void SpotifyService::AddSongsToPlaylist(int playlist_index,
|
||||
const QList<QUrl>& songs_urls) {
|
||||
EnsureServerCreated();
|
||||
server_->AddSongsToPlaylist(playlist_index, songs_urls);
|
||||
}
|
||||
|
||||
void SpotifyService::PlaylistsUpdated(const pb::spotify::Playlists& response) {
|
||||
if (login_task_id_) {
|
||||
app_->task_manager()->SetTaskFinished(login_task_id_);
|
||||
@ -391,6 +402,7 @@ void SpotifyService::PlaylistsUpdated(const pb::spotify::Playlists& response) {
|
||||
qLog(Debug) << "Playlists haven't changed - not updating";
|
||||
return;
|
||||
}
|
||||
qLog(Debug) << "Playlist have changed: updating";
|
||||
|
||||
// Remove and recreate the other playlists
|
||||
for (QStandardItem* item : playlists_) {
|
||||
@ -401,10 +413,16 @@ void SpotifyService::PlaylistsUpdated(const pb::spotify::Playlists& response) {
|
||||
for (int i = 0; i < response.playlist_size(); ++i) {
|
||||
const pb::spotify::Playlists::Playlist& msg = response.playlist(i);
|
||||
|
||||
QStandardItem* item = new QStandardItem(QStringFromStdString(msg.name()));
|
||||
QString playlist_title = QStringFromStdString(msg.name());
|
||||
if (!msg.is_mine()) {
|
||||
const std::string& owner = msg.owner();
|
||||
playlist_title += tr(", by ") + QString::fromUtf8(owner.c_str(), owner.size());
|
||||
}
|
||||
QStandardItem* item = new QStandardItem(playlist_title);
|
||||
item->setData(InternetModel::Type_UserPlaylist, InternetModel::Role_Type);
|
||||
item->setData(true, InternetModel::Role_CanLazyLoad);
|
||||
item->setData(msg.index(), Role_UserPlaylistIndex);
|
||||
item->setData(msg.is_mine(), Role_UserPlaylistIsMine);
|
||||
item->setData(InternetModel::PlayBehaviour_MultipleItems,
|
||||
InternetModel::Role_PlayBehaviour);
|
||||
|
||||
@ -416,8 +434,8 @@ void SpotifyService::PlaylistsUpdated(const pb::spotify::Playlists& response) {
|
||||
}
|
||||
}
|
||||
|
||||
bool SpotifyService::DoPlaylistsDiffer(const pb::spotify::Playlists& response)
|
||||
const {
|
||||
bool SpotifyService::DoPlaylistsDiffer(
|
||||
const pb::spotify::Playlists& response) const {
|
||||
if (playlists_.count() != response.playlist_size()) {
|
||||
return true;
|
||||
}
|
||||
@ -433,6 +451,10 @@ bool SpotifyService::DoPlaylistsDiffer(const pb::spotify::Playlists& response)
|
||||
if (QStringFromStdString(msg.name()) != item->text()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (msg.nb_tracks() != item->rowCount()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -528,6 +550,37 @@ void SpotifyService::SongFromProtobuf(const pb::spotify::Track& track,
|
||||
song->set_filesize(0);
|
||||
}
|
||||
|
||||
QList<QAction*> SpotifyService::playlistitem_actions(const Song& song) {
|
||||
// Clear previous actions
|
||||
while (!playlistitem_actions_.isEmpty()) {
|
||||
QAction* action = playlistitem_actions_.takeFirst();
|
||||
delete action->menu();
|
||||
delete action;
|
||||
}
|
||||
|
||||
// Create a menu with 'add to playlist' actions for each Spotify playlist
|
||||
QAction* add_to_playlists = new QAction(IconLoader::Load("list-add"),
|
||||
tr("Add to Spotify playlists"), this);
|
||||
QMenu* playlists_menu = new QMenu();
|
||||
for (const QStandardItem* playlist_item : playlists_) {
|
||||
if (!playlist_item->data(Role_UserPlaylistIsMine).toBool()) {
|
||||
continue;
|
||||
}
|
||||
QAction* add_to_playlist = new QAction(playlist_item->text(), this);
|
||||
add_to_playlist->setData(playlist_item->data(Role_UserPlaylistIndex));
|
||||
playlists_menu->addAction(add_to_playlist);
|
||||
}
|
||||
connect(playlists_menu, SIGNAL(triggered(QAction*)),
|
||||
SLOT(AddCurrentSongToPlaylist(QAction*)));
|
||||
add_to_playlists->setMenu(playlists_menu);
|
||||
playlistitem_actions_.append(add_to_playlists);
|
||||
|
||||
// Keep in mind the current song URL
|
||||
current_song_url_ = song.url();
|
||||
|
||||
return playlistitem_actions_;
|
||||
}
|
||||
|
||||
QWidget* SpotifyService::HeaderWidget() const {
|
||||
if (IsLoggedIn()) return search_box_;
|
||||
return nullptr;
|
||||
|
@ -29,7 +29,10 @@ class SpotifyService : public InternetService {
|
||||
Type_Toplist,
|
||||
};
|
||||
|
||||
enum Role { Role_UserPlaylistIndex = InternetModel::RoleCount, };
|
||||
enum Role {
|
||||
Role_UserPlaylistIndex = InternetModel::RoleCount,
|
||||
Role_UserPlaylistIsMine, // Is this playlist owned by the user currently logged-in?
|
||||
};
|
||||
|
||||
// Values are persisted - don't change.
|
||||
enum LoginState {
|
||||
@ -53,6 +56,7 @@ class SpotifyService : public InternetService {
|
||||
void ShowContextMenu(const QPoint& global_pos);
|
||||
void ItemDoubleClicked(QStandardItem* item);
|
||||
void DropMimeData(const QMimeData* data, const QModelIndex& index);
|
||||
QList<QAction*> playlistitem_actions(const Song& song) override;
|
||||
QWidget* HeaderWidget() const;
|
||||
|
||||
void Logout();
|
||||
@ -88,6 +92,7 @@ signals:
|
||||
const google::protobuf::RepeatedPtrField<pb::spotify::Track>& tracks);
|
||||
void FillPlaylist(QStandardItem* item,
|
||||
const pb::spotify::LoadPlaylistResponse& response);
|
||||
void AddSongsToPlaylist(int playlist_index, const QList<QUrl>& songs_urls);
|
||||
void EnsureMenuCreated();
|
||||
void ClearSearchResults();
|
||||
|
||||
@ -100,6 +105,7 @@ signals:
|
||||
void BlobProcessError(QProcess::ProcessError error);
|
||||
void LoginCompleted(bool success, const QString& error,
|
||||
pb::spotify::LoginResponse_Error error_code);
|
||||
void AddCurrentSongToPlaylist(QAction* action);
|
||||
void PlaylistsUpdated(const pb::spotify::Playlists& response);
|
||||
void InboxLoaded(const pb::spotify::LoadPlaylistResponse& response);
|
||||
void StarredLoaded(const pb::spotify::LoadPlaylistResponse& response);
|
||||
@ -134,6 +140,8 @@ signals:
|
||||
QMenu* context_menu_;
|
||||
QMenu* playlist_context_menu_;
|
||||
QAction* playlist_sync_action_;
|
||||
QList<QAction*> playlistitem_actions_;
|
||||
QUrl current_song_url_;
|
||||
|
||||
SearchBoxWidget* search_box_;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user