A lot of music updates

This commit is contained in:
WolverinDEV 2020-02-02 14:58:46 +01:00
parent d28b0ba316
commit 57693f25c9
15 changed files with 392 additions and 99 deletions

View File

@ -532,7 +532,7 @@ void DatabaseHelper::savePlaylistPermissions(const std::shared_ptr<VirtualServer
sql::command(this->sql, query,
variable{":serverId", server ? server->getServerId() : 0},
variable{":id", pid},
variable{":chId", 0},
variable{":chId", update.channel_id},
variable{":type", permission::SQL_PERM_PLAYLIST},
variable{":permId", permission_data->name},

View File

@ -1175,3 +1175,44 @@ void VirtualServer::ensureValidDefaultGroups() {
this->properties()[property::VIRTUALSERVER_DEFAULT_CHANNEL_ADMIN_GROUP] = admin_channel_group->groupId();
}
}
void VirtualServer::send_text_message(const std::shared_ptr<BasicChannel> &channel, const std::shared_ptr<ConnectedClient> &client, const std::string &message) {
assert(channel);
assert(client);
auto client_id = client->getClientId();
auto channel_id = channel->channelId();
auto now = chrono::system_clock::now();
bool conversation_private = channel->properties()[property::CHANNEL_FLAG_CONVERSATION_PRIVATE].as<bool>();
auto flag_password = channel->properties()[property::CHANNEL_FLAG_PASSWORD].as<bool>();
for(const auto& client : this->getClients()) {
if(client->connectionState() != ConnectionState::CONNECTED)
continue;
auto type = client->getType();
if(type == ClientType::CLIENT_INTERNAL || type == ClientType::CLIENT_MUSIC)
continue;
auto own_channel = client->currentChannel == channel;
if(conversation_private && !own_channel)
continue;
if(type != ClientType::CLIENT_TEAMSPEAK || own_channel) {
if(!own_channel && &*client != client) {
if(flag_password)
continue; /* TODO: Send notification about new message. The client then could request messages via message history */
if(!client->calculate_and_get_join_state(channel))
continue;
}
client->notifyTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, client, client_id, channel_id, now, message);
}
}
if(!conversation_private) {
auto conversations = this->conversation_manager();
auto conversation = conversations->get_or_create(channel->channelId());
conversation->register_message(client->getClientDatabaseId(), client->getUid(), this->getDisplayName(), now, message);
}
}

View File

@ -270,6 +270,8 @@ namespace ts {
std::unique_lock<std::shared_mutex>& /* tree lock */
);
void send_text_message(const std::shared_ptr<BasicChannel>& /* channel */, const std::shared_ptr<ConnectedClient>& /* sender */, const std::string& /* message */);
inline int voice_encryption_mode() { return this->_voice_encryption_mode; }
inline std::shared_ptr<conversation::ConversationManager> conversation_manager() { return this->_conversation_manager; }
protected:

View File

@ -3,6 +3,7 @@
#include <MusicPlayer.h>
#include <misc/net.h>
#include <cstdint>
#include <src/music/PlayablePlaylist.h>
#include "music/Song.h"
#include "../channel/ClientChannelView.h"
#include "DataClient.h"
@ -294,6 +295,10 @@ namespace ts {
}
return disconnect_lock;
}
inline bool playlist_subscribed(const std::shared_ptr<ts::music::Playlist>& playlist) const {
return this->_subscribed_playlist.lock() == playlist;
}
protected:
std::weak_ptr<ConnectedClient> _this;
sockaddr_storage remote_address;
@ -362,6 +367,7 @@ namespace ts {
std::weak_ptr<MusicClient> selectedBot;
std::weak_ptr<MusicClient> subscribed_bot;
std::weak_ptr<ts::music::Playlist> _subscribed_playlist{};
virtual void tick(const std::chrono::system_clock::time_point &time);
//Locked by everything who has something todo with command handling
@ -532,6 +538,7 @@ namespace ts {
command_result handleCommandPlaylistList(Command&);
command_result handleCommandPlaylistCreate(Command&);
command_result handleCommandPlaylistDelete(Command&);
command_result handleCommandPlaylistSetSubscription(Command&);
command_result handleCommandPlaylistPermList(Command&);
command_result handleCommandPlaylistAddPerm(Command&);
@ -546,6 +553,7 @@ namespace ts {
command_result handleCommandPlaylistEdit(Command&);
command_result handleCommandPlaylistSongList(Command&);
command_result handleCommandPlaylistSongSetCurrent(Command&);
command_result handleCommandPlaylistSongAdd(Command&);
command_result handleCommandPlaylistSongReorder(Command&);
command_result handleCommandPlaylistSongRemove(Command&);

View File

@ -225,6 +225,8 @@ command_result ConnectedClient::handleCommand(Command &cmd) {
else if (command == "playlistlist") return this->handleCommandPlaylistList(cmd);
else if (command == "playlistcreate") return this->handleCommandPlaylistCreate(cmd);
else if (command == "playlistdelete") return this->handleCommandPlaylistDelete(cmd);
else if (command == "playlistsetsubscription") return this->handleCommandPlaylistSetSubscription(cmd);
else if (command == "playlistpermlist") return this->handleCommandPlaylistPermList(cmd);
else if (command == "playlistaddperm") return this->handleCommandPlaylistAddPerm(cmd);
else if (command == "playlistdelperm") return this->handleCommandPlaylistDelPerm(cmd);
@ -235,6 +237,7 @@ command_result ConnectedClient::handleCommand(Command &cmd) {
else if (command == "playlistedit") return this->handleCommandPlaylistEdit(cmd);
else if (command == "playlistsonglist") return this->handleCommandPlaylistSongList(cmd);
else if (command == "playlistsongsetcurrent") return this->handleCommandPlaylistSongSetCurrent(cmd);
else if (command == "playlistsongadd") return this->handleCommandPlaylistSongAdd(cmd);
else if (command == "playlistsongreorder" || command == "playlistsongmove") return this->handleCommandPlaylistSongReorder(cmd);
else if (command == "playlistsongremove") return this->handleCommandPlaylistSongRemove(cmd);
@ -557,40 +560,7 @@ command_result ConnectedClient::handleCommandSendTextMessage(Command &cmd) {
return command_result{permission::b_client_channel_textmessage_send}; /* You're not allowed to send messages :) */
}
auto message = cmd["msg"].string();
auto _this = this->_this.lock();
auto client_id = this->getClientId();
auto flag_password = channel->properties()[property::CHANNEL_FLAG_PASSWORD].as<bool>();
for(const auto& client : this->server->getClients()) {
if(client->connectionState() != ConnectionState::CONNECTED)
continue;
auto type = client->getType();
if(type == ClientType::CLIENT_INTERNAL || type == ClientType::CLIENT_MUSIC)
continue;
auto own_channel = client->currentChannel == this->currentChannel;
if(conversation_private && !own_channel)
continue;
if(type != ClientType::CLIENT_TEAMSPEAK || own_channel) {
if(!own_channel && &*client != this) {
if(flag_password)
continue; /* TODO: Send notification about new message. The client then could request messages via message history */
if(!client->calculate_and_get_join_state(channel))
continue;
}
client->notifyTextMessage(ChatMessageMode::TEXTMODE_CHANNEL, _this, client_id, channel_id, timestamp, message);
}
}
if(!conversation_private) {
auto conversations = this->server->conversation_manager();
auto conversation = conversations->get_or_create(channel->channelId());
conversation->register_message(this->getClientDatabaseId(), this->getUid(), this->getDisplayName(), timestamp, cmd["msg"].string());
}
this->server->send_text_message(channel, this->ref(), cmd["msg"].string());
} else if (cmd["targetmode"] == ChatMessageMode::TEXTMODE_SERVER) {
ACTION_REQUIRES_GLOBAL_PERMISSION(permission::b_client_server_textmessage_send, 1);

View File

@ -798,6 +798,23 @@ command_result ConnectedClient::handleCommandPlaylistSongList(ts::Command &cmd)
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandPlaylistSongSetCurrent(ts::Command &cmd) {
CMD_REF_SERVER(ref_server);
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(25);
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
if(!playlist) return command_result{error::playlist_invalid_id};
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_song_needed_move_power, permission::i_playlist_song_move_power); perr)
return command_result{perr};
if(!playlist->set_current_song(cmd["song_id"]))
return command_result{error::playlist_invalid_song_id};
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandPlaylistSongAdd(ts::Command &cmd) {
CMD_REF_SERVER(ref_server);
CMD_RESET_IDLE;
@ -823,10 +840,6 @@ command_result ConnectedClient::handleCommandPlaylistSongAdd(ts::Command &cmd) {
auto song = playlist->add_song(_this.lock(), cmd["url"], cmd["invoker"], cmd["previous"]);
if(!song) return command_result{error::vs_critical};
Command notify(this->notify_response_command("notifyplaylistsongadd"));
notify["song_id"] = song->id;
this->sendCommand(notify);
return command_result{error::ok};
}
@ -1051,6 +1064,33 @@ command_result ConnectedClient::handleCommandMusicBotPlaylistAssign(ts::Command
return command_result{error::ok};
}
command_result ConnectedClient::handleCommandPlaylistSetSubscription(ts::Command &cmd) {
CMD_REF_SERVER(ref_server);
CMD_RESET_IDLE;
CMD_CHK_AND_INC_FLOOD_POINTS(5);
if(!config::music::enabled) return command_result{error::music_disabled};
auto playlist = ref_server->musicManager->find_playlist(cmd["playlist_id"]);
if(!playlist && cmd["playlist_id"] != 0) return command_result{error::playlist_invalid_id};
{
auto old_playlist = this->_subscribed_playlist.lock();
if(old_playlist)
old_playlist->remove_subscriber(_this.lock());
}
if(playlist) {
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_needed_view_power, permission::i_playlist_view_power); perr)
return command_result{perr};
playlist->add_subscriber(_this.lock());
this->_subscribed_playlist = playlist;
}
return command_result{error::ok};
}

View File

@ -159,8 +159,7 @@ void MusicClient::initialize_bot() {
}
void MusicClient::broadcast_text_message(const std::string &message) {
for(const auto& cl : this->server->getClientsByChannel<VoiceClient>(this->currentChannel))
cl->sendChannelMessage(_this.lock(), message);
this->server->send_text_message(this->currentChannel, this->ref(), message);
}
//https://en.wikipedia.org/wiki/Decibel
@ -230,8 +229,10 @@ void MusicClient::handle_event_song_replay_failed() {
void MusicClient::replay_next_song() {
auto playlist = this->playlist();
if(playlist) {
auto song = playlist->pop_next();
if(song)
bool await_load;
if(auto song = playlist->current_song(await_load); song)
this->replay_song(song);
else if(!await_load)
playlist->next();
}
}

View File

@ -75,6 +75,7 @@ namespace ts {
void player_pause();
void forwardSong();
void resetSong();
void rewindSong();
std::shared_ptr<music::MusicPlayer> current_player();

View File

@ -150,8 +150,8 @@ void MusicClient::tick(const std::chrono::system_clock::time_point &time) {
else if(!this->current_song()) {
auto playlist = this->playlist();
if(playlist) { /* may bot just got initialized */
auto song = playlist->pop_next();
if(song) {
bool await_load;
if(auto song = playlist->current_song(await_load); song) {
auto player_state = this->_player_state;
this->replay_song(song, [player_state](const shared_ptr<::music::MusicPlayer>& player){
if(player_state == ReplayState::STOPPED)
@ -161,6 +161,8 @@ void MusicClient::tick(const std::chrono::system_clock::time_point &time) {
else
player->play();
});
} else if(!await_load) {
this->replay_next_song();
}
}
if(!playlist || !this->current_song()) {
@ -224,14 +226,46 @@ void MusicClient::player_pause() {
void MusicClient::forwardSong() {
auto song = this->current_song();
auto playlist = this->playlist();
this->handle_event_song_ended();
/* explicitly wanted a "next" song so start over again */
if(playlist->properties()[property::PLAYLIST_FLAG_FINISHED].as<bool>()) {
playlist->properties()[property::PLAYLIST_FLAG_FINISHED] = false;
this->handle_event_song_ended();
}
if(song == this->current_song())
this->player_reset(true);
}
void MusicClient::rewindSong() {
void MusicClient::resetSong() {
auto song = this->current_song();
auto playlist = this->playlist();
if(playlist) {
bool await_load;
if(auto song = playlist->current_song(await_load); song) {
this->replay_song(song);
return;
}
}
this->player_reset(true);
}
void MusicClient::rewindSong() {
auto song = this->current_song();
auto playlist = this->playlist();
if(playlist) {
playlist->previous();
bool await_load;
if(auto song = playlist->current_song(await_load); song) {
this->replay_song(song);
return;
}
}
this->player_reset(true);
logError(this->getServerId(), "MusicClient::rewindSong hasn't been implemented yet!");
}
void MusicClient::musicEventHandler(const std::weak_ptr<ts::music::PlayableSong>& weak_player, ::music::MusicEvent event) {

View File

@ -4,7 +4,6 @@
#include "src/VirtualServer.h"
#include "src/client/ConnectedClient.h"
#include <log/LogUtils.h>
#include "src/client/music/internal_provider/channel_replay/ChannelProvider.h"
#include "MusicPlayer.h"
using namespace ts;
@ -351,9 +350,15 @@ void Playlist::destroy_tree() {
}
std::deque<std::shared_ptr<PlaylistEntryInfo> > Playlist::list_songs() {
unique_lock list_lock(this->playlist_lock);
return this->_list_songs(list_lock);
}
std::deque<std::shared_ptr<PlaylistEntryInfo>> Playlist::_list_songs(const std::unique_lock<std::shared_mutex>& lock) {
assert(lock.owns_lock());
deque<shared_ptr<PlaylistEntryInfo>> result;
unique_lock list_lock(this->playlist_lock);
auto head = this->playlist_head;
while(head) {
result.push_back(head->entry);
@ -402,13 +407,19 @@ std::shared_ptr<PlaylistEntryInfo> Playlist::add_song(ClientDbId client, const s
}
auto order_entry = this->playlist_find(list_lock, entry->previous_song_id);
this->playlist_insert(list_lock, list_entry, order_entry);
if(order_entry ? order_entry->entry->id : 0 != order || list_entry->modified) {
list_entry->modified = false;
entry->previous_song_id = list_entry->previous_song ? list_entry->previous_song->entry->id : 0;
list_lock.unlock();
this->sql_apply_changes(entry);
} else {
list_lock.unlock();
}
this->enqueue_load(entry);
this->notify_song_add(entry);
this->properties()[property::PLAYLIST_FLAG_FINISHED] = false;
return entry;
}
@ -422,6 +433,7 @@ bool Playlist::delete_song(ts::SongId id) {
list_lock.unlock();
this->sql_remove(song->entry);
this->notify_song_remove(song->entry);
return true;
}
@ -431,15 +443,18 @@ bool Playlist::reorder_song(ts::SongId song_id, ts::SongId order_id) {
auto order = this->playlist_find(list_lock, order_id);
if(!song) return false;
if(!order && order_id != 0) return false;
if(song->previous_song == order) return true;
if(!this->playlist_reorder(list_lock, song, order)) return false;
list_lock.unlock();
this->sql_flush_all_changes();
this->notify_song_reorder(song->entry);
return true;
}
void Playlist::enqueue_load(const std::shared_ptr<ts::music::PlaylistEntryInfo> &entry) {
assert(entry);
if(entry->load_future) return; /* song has been already loaded or parsed */
entry->load_start = system_clock::now();
@ -525,6 +540,7 @@ void Playlist::execute_async_load(const std::shared_ptr<ts::music::PlaylistEntry
auto provider = ::music::manager::resolveProvider(entry->url_loader, entry->url);
if(!provider) {
entry->load_future->executionFailed("failed to load info provider");
this->notify_song_loaded(entry);
return;
}
@ -542,11 +558,15 @@ void Playlist::execute_async_load(const std::shared_ptr<ts::music::PlaylistEntry
auto casted = static_pointer_cast<UrlPlaylistInfo>(result);
} else if(result->type == UrlType::TYPE_STREAM || result->type == UrlType::TYPE_VIDEO) {
auto casted = static_pointer_cast<UrlSongInfo>(result);
root["length"] = chrono::ceil<chrono::milliseconds>(casted->length).count();
root["title"] = casted->title;
root["description"] = casted->description;
for(const auto& meta : casted->metadata) {
root["metadata"][meta.first] = meta.second;
if(casted->thumbnail) {
if(auto thump = dynamic_pointer_cast<::music::ThumbnailUrl>(casted->thumbnail); thump)
root["thumbnail"] = thump->url();
}
for(const auto& meta : casted->metadata)
root["metadata"][meta.first] = meta.second;
}
entry->metadata = root.toStyledString();
@ -580,11 +600,109 @@ void Playlist::execute_async_load(const std::shared_ptr<ts::music::PlaylistEntry
entry->load_future->executionSucceed(result);
entry->loaded = true;
if(result->type != UrlType::TYPE_PLAYLIST) /* we've already deleted this entry if this is a playlist entry*/
/* we've already deleted this entry if this is a playlist entry */
if(result->type != UrlType::TYPE_PLAYLIST) {
this->sql_apply_changes(entry);
this->notify_song_loaded(entry);
}
} else {
entry->load_future->executionFailed("failed to load info: " + info_future.errorMegssage());
this->notify_song_loaded(entry);
return;
}
}
}
void Playlist::add_subscriber(const std::shared_ptr<server::ConnectedClient> &client) {
std::lock_guard slock{this->subscriber_lock};
this->subscribers.push_back(client);
}
void Playlist::remove_subscriber(const std::shared_ptr<server::ConnectedClient> &tclient) {
std::lock_guard slock{this->subscriber_lock};
this->subscribers.erase(std::remove_if(this->subscribers.begin(), this->subscribers.end(), [&](const std::weak_ptr<server::ConnectedClient>& wclient) {
server::ConnectedLockedClient client{wclient.lock()};
if(!client || client == tclient) return true;
return false;
}), this->subscribers.end());
}
inline bool write_song(const std::shared_ptr<PlaylistEntryInfo> &entry, command_builder& builder, size_t index) {
if(!entry) {
builder.put_unchecked(index, "song_id", "0");
return true;
}
builder.put_unchecked(index, "song_id", entry->id);
builder.put_unchecked(index, "song_previous_song_id", entry->previous_song_id);
builder.put_unchecked(index, "song_url", entry->url);
builder.put_unchecked(index, "song_url_loader", entry->url_loader);
builder.put_unchecked(index, "song_loaded", entry->loaded);
builder.put_unchecked(index, "song_invoker", entry->invoker);
builder.put_unchecked(index, "song_metadata", entry->metadata);
return true;
}
void Playlist::broadcast_notify(const ts::command_builder &command) {
std::lock_guard slock{this->subscriber_lock};
this->subscribers.erase(std::remove_if(this->subscribers.begin(), this->subscribers.end(), [&](const std::weak_ptr<server::ConnectedClient>& wclient) {
server::ConnectedLockedClient client{wclient.lock()};
if(!client) return true;
client->sendCommand(command);
return false;
}), this->subscribers.end());
}
bool Playlist::notify_song_add(const std::shared_ptr<PlaylistEntryInfo> &entry) {
command_builder result{"notifyplaylistsongadd"};
result.put_unchecked(0, "playlist_id", this->playlist_id());
write_song(entry, result, 0);
this->broadcast_notify(result);
return true;
}
bool Playlist::notify_song_remove(const std::shared_ptr<PlaylistEntryInfo> &entry) {
command_builder result{"notifyplaylistsongremove"};
result.put_unchecked(0, "playlist_id", this->playlist_id());
result.put_unchecked(0, "song_id", entry->id);
this->broadcast_notify(result);
return true;
}
bool Playlist::notify_song_reorder(const std::shared_ptr<PlaylistEntryInfo> &entry) {
command_builder result{"notifyplaylistsongreorder"};
result.put_unchecked(0, "playlist_id", this->playlist_id());
result.put_unchecked(0, "song_id", entry->id);
result.put_unchecked(0, "song_previous_song_id", entry->previous_song_id);
this->broadcast_notify(result);
return true;
}
bool Playlist::notify_song_loaded(const std::shared_ptr<PlaylistEntryInfo> &entry) {
command_builder result{"notifyplaylistsongloaded"};
result.put_unchecked(0, "playlist_id", this->playlist_id());
result.put_unchecked(0, "song_id", entry->id);
if(entry->load_future && entry->load_future->failed()) {
result.put_unchecked(0, "success", "0");
result.put_unchecked(0, "load_error_msg", entry->load_future->errorMegssage());
} else {
result.put_unchecked(0, "success", "1");
result.put_unchecked(0, "song_metadata", entry->metadata);
}
this->broadcast_notify(result);
return true;
}

View File

@ -5,6 +5,7 @@
#include <shared_mutex>
#include <atomic>
#include <MusicPlayer.h>
#include <query/command3.h>
#include "MusicBotManager.h"
#include "PlaylistPermissions.h"
@ -56,7 +57,7 @@ namespace ts {
assert(this->entry);
this->previous_song = song;
this->entry->previous_song_id = 0;
this->entry->previous_song_id = song ? song->entry->id : 0;
this->modified = true;
}
};
@ -75,7 +76,7 @@ namespace ts {
Playlist(const std::shared_ptr<MusicBotManager>& /* manager */, const std::shared_ptr<Properties>& /* properties */, std::shared_ptr<permission::v2::PermissionManager> /* permissions */);
virtual ~Playlist();
void load_songs();
virtual void load_songs();
inline bool loaded() { return this->_songs_loaded; };
virtual std::deque<std::shared_ptr<PlaylistEntryInfo>> list_songs();
@ -103,6 +104,11 @@ namespace ts {
template <typename T = Playlist, typename std::enable_if<std::is_base_of<Playlist, T>::value, int>::type = 0>
inline std::shared_ptr<Playlist> ref() { return std::dynamic_pointer_cast<T>(this->_self.lock()); }
void add_subscriber(const std::shared_ptr<server::ConnectedClient>&);
void remove_subscriber(const std::shared_ptr<server::ConnectedClient>&);
bool is_subscriber(const std::shared_ptr<server::ConnectedClient>&);
protected:
virtual void set_self_ref(const std::shared_ptr<Playlist>& /* playlist */);
bool is_playlist_owner(ClientDbId database_id) const override { return this->properties()[property::PLAYLIST_OWNER_DBID].as_save<ClientDbId>() == database_id; }
@ -117,8 +123,13 @@ namespace ts {
std::shared_ptr<server::VirtualServer> get_server();
ServerId get_server_id();
std::shared_mutex playlist_lock;
std::shared_ptr<PlaylistEntry> playlist_head;
std::shared_mutex playlist_lock{};
std::shared_ptr<PlaylistEntry> playlist_head{};
std::mutex subscriber_lock{};
std::deque<std::weak_ptr<server::ConnectedClient>> subscribers{};
virtual std::deque<std::shared_ptr<PlaylistEntryInfo>> _list_songs(const std::unique_lock<std::shared_mutex>& /* playlist lock */);
/* playlist functions are threadsave */
std::shared_ptr<PlaylistEntry> playlist_find(const std::unique_lock<std::shared_mutex>& /* playlist lock */, SongId /* song */);
@ -141,6 +152,12 @@ namespace ts {
bool sql_apply_changes(const std::shared_ptr<PlaylistEntryInfo>& /* entry */);
bool sql_flush_all_changes();
void broadcast_notify(const command_builder& /* command */);
bool notify_song_add(const std::shared_ptr<PlaylistEntryInfo>& /* entry */);
bool notify_song_remove(const std::shared_ptr<PlaylistEntryInfo>& /* entry */);
bool notify_song_reorder(const std::shared_ptr<PlaylistEntryInfo>& /* entry */);
bool notify_song_loaded(const std::shared_ptr<PlaylistEntryInfo>& /* entry */);
void enqueue_load(const std::shared_ptr<PlaylistEntryInfo>& /* entry */);
void execute_async_load(const std::shared_ptr<ts::music::PlaylistEntryInfo> &/* entry */);
};

View File

@ -14,51 +14,73 @@ PlayablePlaylist::PlayablePlaylist(const std::shared_ptr<MusicBotManager> &handl
PlayablePlaylist::~PlayablePlaylist() {}
void PlayablePlaylist::load_songs() {
Playlist::load_songs();
unique_lock playlist_lock(this->playlist_lock);
auto song_id = this->properties()[property::PLAYLIST_CURRENT_SONG_ID].as_save<SongId>();
auto current_song = this->playlist_find(playlist_lock, song_id);
if(!current_song && song_id != 0) {
logWarning(this->get_server_id(), "[Playlist] Failed to reinitialize current song index for playlist {}. Song {} is missing.", this->playlist_id(), song_id);
this->properties()[property::PLAYLIST_CURRENT_SONG_ID] = 0;
}
if(current_song) {
this->current_loading_entry = current_song->entry;
this->enqueue_load(current_song->entry);
}
}
void PlayablePlaylist::set_self_ref(const std::shared_ptr<Playlist> &ptr) {
Playlist::set_self_ref(ptr);
}
std::shared_ptr<ts::music::PlayableSong> PlayablePlaylist::pop_next() {
void PlayablePlaylist::previous() {
unique_lock lock(this->currently_playing_lock);
auto entry = this->loading_entry.lock();
if(!entry) {
lock.unlock();
entry = this->pop_next_entry();
lock.lock();
this->loading_entry = entry;
auto entry = this->playlist_previous_entry();
if(entry) this->enqueue_load(entry);
this->properties()[property::PLAYLIST_CURRENT_SONG_ID] = entry ? entry->id : 0;
this->current_loading_entry = entry;
}
void PlayablePlaylist::next() {
unique_lock lock(this->currently_playing_lock);
auto entry = this->playlist_next_entry();
if(entry) this->enqueue_load(entry);
this->properties()[property::PLAYLIST_CURRENT_SONG_ID] = entry ? entry->id : 0;
this->current_loading_entry = entry;
}
std::shared_ptr<ts::music::PlayableSong> PlayablePlaylist::current_song(bool& await_load) {
await_load = false;
unique_lock lock(this->currently_playing_lock);
auto entry = this->current_loading_entry.lock();
auto id = entry ? entry->id : 0;
if(this->properties()[property::PLAYLIST_CURRENT_SONG_ID].as<SongId>() != id)
if(this->properties()[property::PLAYLIST_CURRENT_SONG_ID].as<SongId>() != id) /* should not happen */
this->properties()[property::PLAYLIST_CURRENT_SONG_ID] = id;
}
if(!entry) return nullptr;
if(!entry->load_future) {
this->loading_entry.reset();
return nullptr; /* skip this entry */
}
if(!entry) return nullptr;
if(!entry->load_future) return nullptr; /* skip this entry */
if(entry->load_future->state() == threads::FutureState::WORKING) {
if(entry->load_start + seconds(30) < system_clock::now()) {
this->loading_entry.reset();
logError(this->get_server_id(), "[PlayList] Failed to load song info. Error: {}. Timeout. Removing song", entry->load_future->errorMegssage());
this->delete_song(entry->id); /* acquired playlist */
this->delete_song(entry->id); /* delete song because we cant load it */
return PlayableSong::create({
entry->id,
entry->url,
entry->invoker
}, MusicClient::failedLoader("Failed to parse song info. Info loader timeouted!"));
}, MusicClient::failedLoader("Song failed to load"));
}
await_load = true;
return nullptr;
}
this->loading_entry.reset(); /* reset the loading entry because we're returning it when succeeded, else we skip*/
if(!entry->load_future->succeeded() || !entry->load_future->getValue(nullptr)) {
//TODO log error within channel?
logError(this->get_server_id(), "[PlayList] Failed to load song info. Error: {}. Removing song", entry->load_future->errorMegssage());
this->delete_song(entry->id); /* acquired playlist */
/* return promise */
@ -66,7 +88,7 @@ std::shared_ptr<ts::music::PlayableSong> PlayablePlaylist::pop_next() {
entry->id,
entry->url,
entry->invoker
}, MusicClient::failedLoader("Failed to parse song info: " + entry->load_future->errorMegssage()));
}, MusicClient::failedLoader(entry->load_future->errorMegssage()));
}
auto result = entry->load_future->getValue(nullptr);
@ -85,19 +107,19 @@ std::shared_ptr<ts::music::PlayableSong> PlayablePlaylist::pop_next() {
}, MusicClient::providerLoader(entry->url_loader));
}
std::shared_ptr<PlaylistEntryInfo> PlayablePlaylist::pop_next_entry() {
if(!this->playlist_head) return nullptr; /* fuzzy check if we're not maybe empty */
std::shared_ptr<PlaylistEntryInfo> PlayablePlaylist::playlist_next_entry() {
if(!this->playlist_head) return nullptr; /* fuzzy check if we're not empty */
auto replay_mode = this->properties()[property::PLAYLIST_REPLAY_MODE].as<ReplayMode::value>();
auto songs = this->list_songs();
if(songs.empty()) return nullptr; /* we've nothing to play */
unique_lock playlist_lock(this->playlist_lock);
auto current_song = this->playlist_find(playlist_lock, this->currently_playing());
if(replay_mode == ReplayMode::SINGLE_LOOPED) {
if(current_song) return current_song->entry;
return songs.front();
if(this->playlist_head) return this->playlist_head->entry;
this->properties()[property::PLAYLIST_FLAG_FINISHED] = true;
return nullptr;
}
std::shared_ptr<PlaylistEntryInfo> result;
@ -112,13 +134,17 @@ std::shared_ptr<PlaylistEntryInfo> PlayablePlaylist::pop_next_entry() {
result = nullptr;
}
} else if(replay_mode == ReplayMode::LINEAR_LOOPED || !this->properties()[property::PLAYLIST_FLAG_FINISHED]) {
result = songs.front();
result = this->playlist_head ? this->playlist_head->entry : nullptr;
}
} else if(replay_mode == ReplayMode::SHUFFLE) {
auto songs = this->_list_songs(playlist_lock);
//TODO may add a already played list?
if(songs.size() == 1) {
if(songs.size() == 0) {
this->properties()[property::PLAYLIST_FLAG_FINISHED] = true;
result = nullptr;
} else if(songs.size() == 1) {
result = songs.front();
} else {
size_t index;
while(songs[index = (rand() % songs.size())] == (!current_song ? nullptr : current_song->entry)) { }
@ -127,18 +153,48 @@ std::shared_ptr<PlaylistEntryInfo> PlayablePlaylist::pop_next_entry() {
}
}
playlist_lock.unlock();
if(current_song && this->properties()[property::PLAYLIST_FLAG_DELETE_PLAYED].as<bool>())
if(current_song && this->properties()[property::PLAYLIST_FLAG_DELETE_PLAYED].as<bool>()) {
this->delete_song(current_song->entry->id);
}
return result;
}
std::shared_ptr<PlaylistEntryInfo> PlayablePlaylist::playlist_previous_entry() {
if(!this->playlist_head) return nullptr; /* fuzzy check if we're not empty */
auto replay_mode = this->properties()[property::PLAYLIST_REPLAY_MODE].as<ReplayMode::value>();
unique_lock playlist_lock(this->playlist_lock);
this->properties()[property::PLAYLIST_FLAG_FINISHED] = false;
auto current_song = this->playlist_find(playlist_lock, this->currently_playing());
if(current_song) {
if(replay_mode == ReplayMode::LINEAR || replay_mode == ReplayMode::LINEAR_LOOPED) {
if(current_song->previous_song) return current_song->previous_song->entry;
if(!this->playlist_head) return nullptr;
if(replay_mode == ReplayMode::LINEAR)
return this->playlist_head->entry;
auto tail = this->playlist_head;
while(tail->next_song) tail = tail->next_song;
return tail->entry;
} else if(replay_mode == ReplayMode::SINGLE_LOOPED) {
return current_song ? current_song->entry : this->playlist_head ? this->playlist_head->entry : nullptr;
} else {
return current_song ? current_song->entry : nullptr; /* not possible to rewind shuffle mode */
}
} else {
return this->playlist_head ? this->playlist_head->entry : nullptr;
}
}
bool PlayablePlaylist::set_current_song(SongId song_id) {
{
unique_lock playlist_lock(this->playlist_lock);
auto current_song = this->playlist_find(playlist_lock, song_id);
this->loading_entry = current_song->entry;
if(!current_song && song_id != 0) return false;
this->current_loading_entry = current_song->entry;
if(current_song) {
this->enqueue_load(current_song->entry);
auto id = current_song ? current_song->entry->id : 0;
@ -148,7 +204,7 @@ bool PlayablePlaylist::set_current_song(SongId song_id) {
}
{
auto bot = this->current_bot();
bot->forwardSong(); /* skip to the song */
bot->resetSong(); /* reset the song and use the song which is supposed to be the "current" */
}
return true;

View File

@ -27,22 +27,27 @@ namespace ts {
PlayablePlaylist(const std::shared_ptr<MusicBotManager>& /* manager */, const std::shared_ptr<Properties>& /* properties */, const std::shared_ptr<permission::v2::PermissionManager>& /* permissions */);
virtual ~PlayablePlaylist();
void load_songs() override;
inline ReplayMode::value replay_mode() { return this->properties()[property::PLAYLIST_REPLAY_MODE].as<ReplayMode::value>(); }
inline void set_replay_mode(ReplayMode::value mode) { this->properties()[property::PLAYLIST_REPLAY_MODE] = mode; }
inline SongId currently_playing() { return this->properties()[property::PLAYLIST_CURRENT_SONG_ID]; }
bool set_current_song(SongId /* song */);
std::shared_ptr<music::PlayableSong> pop_next();
void previous();
void next();
std::shared_ptr<music::PlayableSong> current_song(bool& await_load);
inline std::shared_ptr<server::MusicClient> current_bot() { return this->_current_bot.lock(); }
protected:
std::mutex currently_playing_lock;
std::weak_ptr<PlaylistEntryInfo> loading_entry;
std::weak_ptr<PlaylistEntryInfo> current_loading_entry;
std::weak_ptr<server::MusicClient> _current_bot;
void set_self_ref(const std::shared_ptr<Playlist> &ptr) override;
std::shared_ptr<PlaylistEntryInfo> pop_next_entry();
std::shared_ptr<PlaylistEntryInfo> playlist_previous_entry();
std::shared_ptr<PlaylistEntryInfo> playlist_next_entry();
};
}
}

View File

@ -25,5 +25,5 @@ permission::PermissionType PlaylistPermissions::client_has_permissions(
return permission::v2::permission_granted(
this->permission_manager()->permission_value_flagged(needed_permission),
this->calculate_client_specific_permissions(granted_permission, client),
flags & do_no_require_granted) ? permission::ok : needed_permission;
(flags & do_no_require_granted) == 0) ? permission::ok : granted_permission;
}

2
shared

@ -1 +1 @@
Subproject commit 04666982d937a1f14fe7fff22aa735afe7563525
Subproject commit 0f920b58bccc90e825dc57ebba133e6834639cf5