Skip to content

Commit

Permalink
Spotify: add the ability to add/remove tracks to/from Starred playlist
Browse files Browse the repository at this point in the history
  • Loading branch information
ArnaudBienner committed Jan 14, 2015
1 parent 53279eb commit 7039c2a
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 34 deletions.
23 changes: 16 additions & 7 deletions ext/clementine-spotifyblob/spotifyclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,7 @@ void SpotifyClient::PlaylistContainerLoadedCallback(sp_playlistcontainer* pc,
SpotifyClient* me = reinterpret_cast<SpotifyClient*>(userdata);

// Install callbacks on all the playlists
sp_playlist_add_callbacks(sp_session_starred_create(me->session_), &me->get_playlists_callbacks_, me);
const int count = sp_playlistcontainer_num_playlists(pc);
for (int i = 0; i < count; ++i) {
sp_playlist* playlist = sp_playlistcontainer_playlist(pc, i);
Expand Down Expand Up @@ -611,11 +612,10 @@ void SpotifyClient::PlaylistStateChangedForGetPlaylists(sp_playlist* pl,
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);
sp_playlist* playlist = GetPlaylist(req.playlist_type(), req.playlist_index());
if (!playlist) {
qLog(Error) << "Playlist " << playlist_index << "not found";
qLog(Error) << "Playlist " << req.playlist_type() << "," <<
req.playlist_index() << "not found";
return;
}

Expand Down Expand Up @@ -650,11 +650,11 @@ void SpotifyClient::AddTracksToPlaylist(
void SpotifyClient::RemoveTracksFromPlaylist(
const pb::spotify::RemoveTracksFromPlaylistRequest& req) {
// Get the playlist we want to update
int playlist_index = req.playlist_index();
sp_playlist* playlist =
GetPlaylist(pb::spotify::UserPlaylist, playlist_index);
GetPlaylist(req.playlist_type(), req.playlist_index());
if (!playlist) {
qLog(Error) << "Playlist " << playlist_index << "not found";
qLog(Error) << "Playlist " << req.playlist_type() << "," <<
req.playlist_index() << "not found";
return;
}

Expand All @@ -664,6 +664,15 @@ void SpotifyClient::RemoveTracksFromPlaylist(
tracks_indices_array[i] = req.track_index(i);
}

// WTF: sp_playlist_remove_tracks indexes start from the end for starred
// playlist, not from the beginning like other playlists: reverse them
if (req.playlist_type() == pb::spotify::Starred) {
int num_tracks = sp_playlist_num_tracks(playlist);
for (int i = 0; i < req.track_index_size(); i++) {
tracks_indices_array[i] = num_tracks - tracks_indices_array[i] - 1;
}
}

if (sp_playlist_remove_tracks(playlist, tracks_indices_array.get(),
req.track_index_size()) != SP_ERROR_OK) {
qLog(Error) << "Error when removing tracks!";
Expand Down
10 changes: 6 additions & 4 deletions ext/libclementine-spotifyblob/spotifymessages.proto
Original file line number Diff line number Diff line change
Expand Up @@ -193,13 +193,15 @@ message PauseRequest {
}

message AddTracksToPlaylistRequest {
required int64 playlist_index = 1;
repeated string track_uri = 2;
required PlaylistType playlist_type = 1;
optional int64 playlist_index = 2; // Used if playlist_index == UserPlaylist
repeated string track_uri = 3;
}

message RemoveTracksFromPlaylistRequest {
required int64 playlist_index = 1;
repeated int64 track_index = 2;
required PlaylistType playlist_type = 1;
optional int64 playlist_index = 2; // Used if playlist_index == UserPlaylist
repeated int64 track_index = 3;
}

// NEXT_ID: 25
Expand Down
32 changes: 28 additions & 4 deletions src/internet/spotify/spotifyserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,24 +210,48 @@ void SpotifyServer::LoadUserPlaylist(int index) {
LoadPlaylist(pb::spotify::UserPlaylist, index);
}

void SpotifyServer::AddSongsToPlaylist(int playlist_index,
const QList<QUrl>& songs_urls) {
void SpotifyServer::AddSongsToStarred(const QList<QUrl>& songs_urls) {
AddSongsToPlaylist(pb::spotify::Starred, songs_urls);
}

void SpotifyServer::AddSongsToUserPlaylist(int playlist_index,
const QList<QUrl>& songs_urls) {
AddSongsToPlaylist(pb::spotify::UserPlaylist, songs_urls, playlist_index);
}

void SpotifyServer::AddSongsToPlaylist(const pb::spotify::PlaylistType playlist_type,
const QList<QUrl>& songs_urls,
int playlist_index) {
pb::spotify::Message message;
pb::spotify::AddTracksToPlaylistRequest* req =
message.mutable_add_tracks_to_playlist();
req->set_playlist_type(playlist_type);
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::RemoveSongsFromStarred(const QList<int>& songs_indices_to_remove) {
RemoveSongsFromPlaylist(pb::spotify::Starred, songs_indices_to_remove);
}

void SpotifyServer::RemoveSongsFromUserPlaylist(int playlist_index,
const QList<int>& songs_indices_to_remove) {
RemoveSongsFromPlaylist(pb::spotify::UserPlaylist, songs_indices_to_remove, playlist_index);
}

void SpotifyServer::RemoveSongsFromPlaylist(
int playlist_index, const QList<int>& songs_indices_to_remove) {
const pb::spotify::PlaylistType playlist_type,
const QList<int>& songs_indices_to_remove, int playlist_index) {
pb::spotify::Message message;
pb::spotify::RemoveTracksFromPlaylistRequest* req =
message.mutable_remove_tracks_from_playlist();
req->set_playlist_index(playlist_index);
req->set_playlist_type(playlist_type);
if (playlist_type == pb::spotify::UserPlaylist) {
req->set_playlist_index(playlist_index);
}
for (int song_index : songs_indices_to_remove) {
req->add_track_index(song_index);
}
Expand Down
16 changes: 13 additions & 3 deletions src/internet/spotify/spotifyserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,11 @@ 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 RemoveSongsFromPlaylist(int playlist_index,
const QList<int>& songs_indices_to_remove);
void AddSongsToStarred(const QList<QUrl>& songs_urls);
void AddSongsToUserPlaylist(int playlist_index, const QList<QUrl>& songs_urls);
void RemoveSongsFromUserPlaylist(int playlist_index,
const QList<int>& songs_indices_to_remove);
void RemoveSongsFromStarred(const QList<int>& songs_indices_to_remove);
void StartPlaybackLater(const QString& uri, quint16 port);
void Search(const QString& text, int limit, int limit_album = 0);
void LoadImage(const QString& id);
Expand Down Expand Up @@ -89,6 +91,14 @@ class SpotifyServer : public AbstractMessageHandler<pb::spotify::Message> {
private:
void LoadPlaylist(pb::spotify::PlaylistType type, int index = -1);
void SyncPlaylist(pb::spotify::PlaylistType type, int index, bool offline);
void AddSongsToPlaylist(const pb::spotify::PlaylistType playlist_type,
const QList<QUrl>& songs_urls,
// Used iff type is user_playlist
int playlist_index = -1);
void RemoveSongsFromPlaylist(const pb::spotify::PlaylistType playlist_type,
const QList<int>& songs_indices_to_remove,
// Used iff type is user_playlist
int playlist_index = -1);
void SendOrQueueMessage(const pb::spotify::Message& message);

QTcpServer* server_;
Expand Down
66 changes: 53 additions & 13 deletions src/internet/spotify/spotifyservice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,15 +374,24 @@ void SpotifyService::InstallBlob() {

void SpotifyService::BlobDownloadFinished() { EnsureServerCreated(); }

void SpotifyService::AddCurrentSongToPlaylist(QAction* action) {
void SpotifyService::AddCurrentSongToUserPlaylist(QAction* action) {
int playlist_index = action->data().toInt();
AddSongsToPlaylist(playlist_index, QList<QUrl>() << current_song_url_);
AddSongsToUserPlaylist(playlist_index, QList<QUrl>() << current_song_url_);
}

void SpotifyService::AddSongsToPlaylist(int playlist_index,
void SpotifyService::AddSongsToUserPlaylist(int playlist_index,
const QList<QUrl>& songs_urls) {
EnsureServerCreated();
server_->AddSongsToPlaylist(playlist_index, songs_urls);
server_->AddSongsToUserPlaylist(playlist_index, songs_urls);
}

void SpotifyService::AddCurrentSongToStarredPlaylist() {
AddSongsToStarred(QList<QUrl>() << current_song_url_);
}

void SpotifyService::AddSongsToStarred(const QList<QUrl>& songs_urls) {
EnsureMenuCreated();
server_->AddSongsToStarred(songs_urls);
}

void SpotifyService::PlaylistsUpdated(const pb::spotify::Playlists& response) {
Expand All @@ -407,6 +416,7 @@ void SpotifyService::PlaylistsUpdated(const pb::spotify::Playlists& response) {
starred_->setData(true, InternetModel::Role_CanLazyLoad);
starred_->setData(InternetModel::PlayBehaviour_MultipleItems,
InternetModel::Role_PlayBehaviour);
starred_->setData(true, InternetModel::Role_CanBeModified);

inbox_ = new QStandardItem(IconLoader::Load("mail-message"), tr("Inbox"));
inbox_->setData(Type_InboxPlaylist, InternetModel::Role_Type);
Expand All @@ -424,6 +434,12 @@ void SpotifyService::PlaylistsUpdated(const pb::spotify::Playlists& response) {
root_->appendRow(toplist_);
root_->appendRow(starred_);
root_->appendRow(inbox_);
} else {
// Always reset starred playlist
// TODO: might be improved by including starred playlist in the response,
// and reloading it only when needed, like other playlists.
starred_->removeRows(0, starred_->rowCount());
LazyPopulate(starred_);
}

// Don't do anything if the playlists haven't changed since last time.
Expand Down Expand Up @@ -589,6 +605,12 @@ QList<QAction*> SpotifyService::playlistitem_actions(const Song& song) {
delete action;
}

QAction* add_to_starred = new QAction(QIcon(":/star-on.png"),
tr("Add to Spotify starred"), this);
connect(add_to_starred, SIGNAL(triggered()),
SLOT(AddCurrentSongToStarredPlaylist()));
playlistitem_actions_.append(add_to_starred);

// 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);
Expand All @@ -602,7 +624,7 @@ QList<QAction*> SpotifyService::playlistitem_actions(const Song& song) {
playlists_menu->addAction(add_to_playlist);
}
connect(playlists_menu, SIGNAL(triggered(QAction*)),
SLOT(AddCurrentSongToPlaylist(QAction*)));
SLOT(AddCurrentSongToUserPlaylist(QAction*)));
add_to_playlists->setMenu(playlists_menu);
playlistitem_actions_.append(add_to_playlists);

Expand Down Expand Up @@ -823,7 +845,7 @@ void SpotifyService::DropMimeData(const QMimeData* data,
}
if (!q_playlist_index.isValid()) return;

AddSongsToPlaylist(q_playlist_index.toInt(), data->urls());
AddSongsToUserPlaylist(q_playlist_index.toInt(), data->urls());
}

void SpotifyService::LoadImage(const QString& id) {
Expand Down Expand Up @@ -885,32 +907,50 @@ void SpotifyService::ShowConfig() {
void SpotifyService::RemoveCurrentFromPlaylist() {
const QModelIndexList& indexes(model()->selected_indexes());
QMap<int, QList<int>> playlists_songs_indices;
QList<int> starred_songs_indices;

for (const QModelIndex& index : indexes) {
if (index.parent().data(InternetModel::Role_Type).toInt() !=
bool is_starred = false;
if (index.parent().data(InternetModel::Role_Type).toInt() ==
Type_StarredPlaylist) {
is_starred = true;
} else if (index.parent().data(InternetModel::Role_Type).toInt() !=
InternetModel::Type_UserPlaylist) {
continue;
continue;
}

if (index.data(InternetModel::Role_Type).toInt() !=
InternetModel::Type_Track) {
continue;
}

int playlist_index = index.parent().data(Role_UserPlaylistIndex).toInt();
int song_index = index.row();
playlists_songs_indices[playlist_index] << song_index;
if (is_starred) {
starred_songs_indices << song_index;
} else {
int playlist_index = index.parent().data(Role_UserPlaylistIndex).toInt();
playlists_songs_indices[playlist_index] << song_index;
}
}

for (QMap<int, QList<int>>::const_iterator it =
playlists_songs_indices.constBegin();
it != playlists_songs_indices.constEnd(); ++it) {
RemoveSongsFromPlaylist(it.key(), it.value());
RemoveSongsFromUserPlaylist(it.key(), it.value());
}
if (!starred_songs_indices.isEmpty()) {
RemoveSongsFromStarred(starred_songs_indices);
}
}

void SpotifyService::RemoveSongsFromPlaylist(
void SpotifyService::RemoveSongsFromUserPlaylist(
int playlist_index, const QList<int>& songs_indices_to_remove) {
server_->RemoveSongsFromPlaylist(playlist_index, songs_indices_to_remove);
server_->RemoveSongsFromUserPlaylist(playlist_index, songs_indices_to_remove);
}

void SpotifyService::RemoveSongsFromStarred(
const QList<int>& songs_indices_to_remove) {
server_->RemoveSongsFromStarred(songs_indices_to_remove);
}

void SpotifyService::Logout() {
Expand Down
9 changes: 6 additions & 3 deletions src/internet/spotify/spotifyservice.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ class SpotifyService : public InternetService {
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 AddSongsToUserPlaylist(int playlist_index, const QList<QUrl>& songs_urls);
void AddSongsToStarred(const QList<QUrl>& songs_urls);
void EnsureMenuCreated();
// Create a new "show config" action. The caller is responsible for deleting
// the pointer (or adding it to menu or anything else that will take ownership
Expand All @@ -129,9 +130,11 @@ class SpotifyService : public InternetService {
void BlobProcessError(QProcess::ProcessError error);
void LoginCompleted(bool success, const QString& error,
pb::spotify::LoginResponse_Error error_code);
void AddCurrentSongToPlaylist(QAction* action);
void RemoveSongsFromPlaylist(int playlist_index,
void AddCurrentSongToUserPlaylist(QAction* action);
void AddCurrentSongToStarredPlaylist();
void RemoveSongsFromUserPlaylist(int playlist_index,
const QList<int>& songs_indices_to_remove);
void RemoveSongsFromStarred(const QList<int>& songs_indices_to_remove);
void PlaylistsUpdated(const pb::spotify::Playlists& response);
void InboxLoaded(const pb::spotify::LoadPlaylistResponse& response);
void StarredLoaded(const pb::spotify::LoadPlaylistResponse& response);
Expand Down

0 comments on commit 7039c2a

Please sign in to comment.