Spotify: add the ability to add/remove tracks to/from Starred playlist

This commit is contained in:
Arnaud Bienner 2015-01-14 22:49:36 +01:00
parent 53279ebc66
commit 7039c2a2bf
6 changed files with 122 additions and 34 deletions

View File

@ -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);
@ -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;
}
@ -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;
}
@ -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!";

View File

@ -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

View File

@ -210,11 +210,22 @@ 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()));
@ -222,12 +233,25 @@ void SpotifyServer::AddSongsToPlaylist(int playlist_index,
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);
}

View File

@ -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);
@ -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_;

View File

@ -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) {
@ -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);
@ -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.
@ -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);
@ -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);
@ -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) {
@ -885,10 +907,16 @@ 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() !=
@ -896,21 +924,33 @@ void SpotifyService::RemoveCurrentFromPlaylist() {
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() {

View File

@ -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
@ -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);