#include "MusicClient.h" #include "src/client/voice/VoiceClient.h" #include #include #include #include #include "Song.h" using namespace std; using namespace std::chrono; using namespace ts; using namespace ts::server; MusicClient::loader_t MusicClient::ytLoader() { return providerLoader("YouTube"); } MusicClient::loader_t MusicClient::ffmpegLoader() { return providerLoader("FFMpeg"); } MusicClient::loader_t MusicClient::channelLoader() { return providerLoader("ChannelProvider"); } MusicClient::loader_t MusicClient::failedLoader(const std::string &message) { return make_shared([message] (const shared_ptr, const shared_ptr&, std::string& error) mutable -> std::shared_ptr { error = message; return nullptr; }); } MusicClient::loader_t MusicClient::providerLoader(const std::string &name, shared_ptr<::music::manager::PlayerProvider> provider) { return make_shared([name, provider, used_provider = std::move(provider)] (const shared_ptr server, const shared_ptr& entry, std::string& error) mutable -> std::shared_ptr { if(!used_provider) used_provider = ::music::manager::resolveProvider(name, entry->getUrl()); if(!used_provider){ error = "Could not resolve '" + name + "' provider!"; return nullptr; } auto promise = used_provider->createPlayer(entry->getUrl(), nullptr, &*server); auto player = promise.waitAndGet(nullptr, system_clock::now() + seconds(60)); if(!player) { if(promise.state() == threads::FutureState::WORKING) error = "load timeout"; else error = promise.errorMegssage(); return nullptr; } { auto data = make_shared(); data->player = player; data->title = player->songTitle(); data->description = player->songDescription(); data->length = player->length(); for(const auto& thumbnail : player->thumbnails()) { if(thumbnail->type() == ::music::THUMBNAIL_URL) { data->thumbnail = static_pointer_cast<::music::ThumbnailUrl>(thumbnail)->url(); break; } } return data; } }); } MusicClient::MusicClient(const std::shared_ptr& handle, const std::string& uniqueId) : ConnectedClient(handle->getSql(), handle) { memtrack::allocated(this); this->state = CONNECTED; this->properties()[property::CLIENT_TYPE] = ClientType::CLIENT_TEAMSPEAK; this->properties()[property::CLIENT_TYPE_EXACT] = ClientType::CLIENT_MUSIC; this->properties()[property::CLIENT_UNIQUE_IDENTIFIER] = uniqueId; { //FIXME handle encoder create error, or assume that this never happens int error = 0; this->voice.encoder = opus_encoder_create(48000, 2, OPUS_APPLICATION_AUDIO, &error); } this->_player_state = SLEEPING; } MusicClient::~MusicClient() { if(this->voice.encoder) { opus_encoder_destroy(this->voice.encoder); this->voice.encoder = nullptr; } memtrack::freed(this); } void MusicClient::sendCommand(const ts::Command &command, bool low) { } void MusicClient::sendCommand(const ts::command_builder &command, bool low) { } bool MusicClient::close_connection(const std::chrono::system_clock::time_point&) { logError(this->getServerId(), "Music manager is forced to disconnect!"); /* if(this->server) this->server->unregisterInternalClient(static_pointer_cast(_this.lock())); */ //TODO! this->properties()[property::CLIENT_ID] = 0; return true; } bool MusicClient::disconnect(const std::string &reason) { this->player_reset(false); if(this->currentChannel) this->properties()[property::CLIENT_LAST_CHANNEL] = this->currentChannel->channelId(); return true; } bool server::MusicClient::notifyClientMoved( const std::shared_ptr &client, const std::shared_ptr &target_channel, ViewReasonId reason, std::string msg, std::shared_ptr invoker, bool lock_channel_tree) { if(&*client == this) { this->properties()[property::CLIENT_LAST_CHANNEL] = target_channel ? target_channel->channelId() : 0; } return true; } void MusicClient::initialize_bot() { this->_player_state = this->properties()[property::CLIENT_PLAYER_STATE]; if(this->_player_state == ReplayState::LOADING) this->_player_state = ReplayState::PLAYING; if(!this->properties()->has(property::CLIENT_COUNTRY) || this->properties()[property::CLIENT_COUNTRY].value().empty()) this->properties()[property::CLIENT_COUNTRY] = config::geo::countryFlag; if(this->properties()[property::CLIENT_UPTIME_MODE].as() == MusicClient::UptimeMode::TIME_SINCE_SERVER_START) { this->properties()[property::CLIENT_LASTCONNECTED] = duration_cast(this->server->start_timestamp().time_since_epoch()).count(); } else { this->properties()[property::CLIENT_LASTCONNECTED] = this->properties()[property::CLIENT_CREATED].value(); } this->properties()[property::CLIENT_LASTCONNECTED] = this->properties()[property::CLIENT_CREATED].value(); ChannelId last = this->properties()[property::CLIENT_LAST_CHANNEL]; auto channel = this->server->getChannelTree()->findChannel(last); if(!channel) channel = this->server->getChannelTree()->getDefaultChannel(); if(this->getClientId() == 0 || this->server->find_client_by_id(this->getClientId()) != _this.lock()) { this->server->registerClient(_this.lock()); } { unique_lock server_channel_lock(this->server->channel_tree_lock); this->server->client_move(this->ref(), channel, nullptr, "", ViewReasonId::VREASON_USER_ACTION, true, server_channel_lock); } this->player.volume_modifier = this->properties()[property::CLIENT_PLAYER_VOLUME]; } void MusicClient::broadcast_text_message(const std::string &message) { this->server->send_text_message(this->currentChannel, this->ref(), message); } //https://en.wikipedia.org/wiki/Decibel void MusicClient::apply_volume(const std::shared_ptr<::music::SampleSegment> &seg) { for(int sample = 0; sample < seg->segmentLength; sample++){ for(int channel = 0; channel < seg->channels; channel++){ int16_t lane = seg->segments[sample * seg->channels + channel]; //lane *= pow(10.f, this->_volumeModifier * .05f); lane *= this->player.volume_modifier; seg->segments[sample * seg->channels + channel] = (int16_t) lane; } } } void MusicClient::add_subscriber(const std::shared_ptr &client) { lock_guard lock(this->subscriber_lock); this->subscribers.erase(remove_if(this->subscribers.begin(), this->subscribers.end(), [&](const std::tuple, system_clock::time_point>& entry) { auto e = get<0>(entry).lock(); return !e || e == client; }), this->subscribers.end()); this->subscribers.push_back({client, system_clock::time_point{}}); } bool MusicClient::is_subscriber(const std::shared_ptr &client) { lock_guard lock(this->subscriber_lock); for(const auto& entry : this->subscribers) if(std::get<0>(entry).lock() == client) return true; return false; } void MusicClient::remove_subscriber(const std::shared_ptr &client) { lock_guard lock(this->subscriber_lock); this->subscribers.erase(remove_if(this->subscribers.begin(), this->subscribers.end(), [&](const std::tuple, system_clock::time_point>& entry) { auto e = get<0>(entry).lock(); return !e || e == client; }), this->subscribers.end()); } void MusicClient::changePlayerState(ReplayState state) { if(this->_player_state == state) return; this->_player_state = state; this->properties()[property::CLIENT_PLAYER_STATE] = state; this->properties()[property::CLIENT_FLAG_TALKING] = state == ReplayState::PLAYING; this->server->notifyClientPropertyUpdates(_this.lock(), deque{property::CLIENT_PLAYER_STATE}); } void MusicClient::notifySongChange(const std::shared_ptr& song) { this->server->forEachClient([&](shared_ptr client) { client->notifyMusicPlayerSongChange(dynamic_pointer_cast(_this.lock()), song); }); } void MusicClient::handle_event_song_ended() { auto playlist = this->playlist(); if(playlist) playlist->next(); this->replay_next_song(); } void MusicClient::handle_event_song_dry() { this->replay_next_song(); } void MusicClient::handle_event_song_replay_failed() { auto playlist = this->playlist(); if(playlist) playlist->next(); this->replay_next_song(); } void MusicClient::replay_next_song() { auto playlist = this->playlist(); if(playlist) { bool await_load; if(auto song = playlist->current_song(await_load); song) this->replay_song(song); else if(!await_load) playlist->next(); } else this->replay_song(nullptr); }