2019-07-17 19:37:18 +02:00
|
|
|
#include "MusicClient.h"
|
|
|
|
#include "src/client/voice/VoiceClient.h"
|
|
|
|
#include <misc/endianness.h>
|
|
|
|
#include <log/LogUtils.h>
|
|
|
|
#include <misc/memtracker.h>
|
|
|
|
#include <ThreadPool/Timer.h>
|
|
|
|
#include "Song.h"
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace std::chrono;
|
|
|
|
using namespace ts;
|
|
|
|
using namespace ts::server;
|
|
|
|
|
|
|
|
MusicClient::loader_t MusicClient::ytLoader() {
|
2020-01-24 02:57:58 +01:00
|
|
|
return providerLoader("YouTube");
|
2019-07-17 19:37:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
MusicClient::loader_t MusicClient::ffmpegLoader() {
|
2020-01-24 02:57:58 +01:00
|
|
|
return providerLoader("FFMpeg");
|
2019-07-17 19:37:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
MusicClient::loader_t MusicClient::channelLoader() {
|
2020-01-24 02:57:58 +01:00
|
|
|
return providerLoader("ChannelProvider");
|
2019-07-17 19:37:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
MusicClient::loader_t MusicClient::failedLoader(const std::string &message) {
|
2020-01-26 18:04:38 +01:00
|
|
|
return make_shared<music::PlayableSong::song_loader_t>([message] (const shared_ptr<VirtualServer>, const shared_ptr<music::PlayableSong>&, std::string& error) mutable -> std::shared_ptr<music::PlayableSong::LoadedData> {
|
2020-01-24 02:57:58 +01:00
|
|
|
error = message;
|
|
|
|
return nullptr;
|
|
|
|
});
|
2019-07-17 19:37:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
MusicClient::loader_t MusicClient::providerLoader(const std::string &name, shared_ptr<::music::manager::PlayerProvider> provider) {
|
2020-01-26 18:04:38 +01:00
|
|
|
return make_shared<music::PlayableSong::song_loader_t>([name, provider, used_provider = std::move(provider)] (const shared_ptr<VirtualServer> server, const shared_ptr<music::PlayableSong>& entry, std::string& error) mutable -> std::shared_ptr<music::PlayableSong::LoadedData> {
|
2020-01-24 02:57:58 +01:00
|
|
|
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<music::PlayableSong::LoadedData>();
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
});
|
2019-07-17 19:37:18 +02:00
|
|
|
}
|
|
|
|
|
2020-01-26 18:04:38 +01:00
|
|
|
MusicClient::MusicClient(const std::shared_ptr<VirtualServer>& handle, const std::string& uniqueId) : ConnectedClient(handle->getSql(), handle) {
|
2020-01-24 02:57:58 +01:00
|
|
|
memtrack::allocated<MusicClient>(this);
|
2019-07-17 19:37:18 +02:00
|
|
|
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;
|
|
|
|
|
2020-01-24 02:57:58 +01:00
|
|
|
{
|
|
|
|
//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);
|
|
|
|
}
|
2019-07-17 19:37:18 +02:00
|
|
|
|
2020-01-24 02:57:58 +01:00
|
|
|
this->_player_state = SLEEPING;
|
2019-07-17 19:37:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
MusicClient::~MusicClient() {
|
|
|
|
if(this->voice.encoder) {
|
|
|
|
opus_encoder_destroy(this->voice.encoder);
|
2020-01-24 02:57:58 +01:00
|
|
|
this->voice.encoder = nullptr;
|
2019-07-17 19:37:18 +02:00
|
|
|
}
|
2020-01-24 02:57:58 +01:00
|
|
|
memtrack::freed<MusicClient>(this);
|
2019-07-17 19:37:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void MusicClient::sendCommand(const ts::Command &command, bool low) { }
|
2020-01-26 14:21:34 +01:00
|
|
|
void MusicClient::sendCommand(const ts::command_builder &command, bool low) { }
|
2019-07-17 19:37:18 +02:00
|
|
|
|
2020-02-01 14:32:16 +01:00
|
|
|
bool MusicClient::close_connection(const std::chrono::system_clock::time_point&) {
|
2019-11-23 21:16:55 +01:00
|
|
|
logError(this->getServerId(), "Music manager is forced to disconnect!");
|
2019-07-17 19:37:18 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
if(this->server)
|
|
|
|
this->server->unregisterInternalClient(static_pointer_cast<InternalClient>(_this.lock()));
|
|
|
|
*/
|
|
|
|
//TODO!
|
|
|
|
this->properties()[property::CLIENT_ID] = 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-10-20 12:09:28 +02:00
|
|
|
bool MusicClient::disconnect(const std::string &reason) {
|
2020-01-24 02:57:58 +01:00
|
|
|
this->player_reset(false);
|
|
|
|
if(this->currentChannel)
|
|
|
|
this->properties()[property::CLIENT_LAST_CHANNEL] = this->currentChannel->channelId();
|
|
|
|
return true;
|
2019-10-20 12:09:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool server::MusicClient::notifyClientMoved(
|
2020-01-24 02:57:58 +01:00
|
|
|
const std::shared_ptr<ConnectedClient> &client,
|
|
|
|
const std::shared_ptr<BasicChannel> &target_channel,
|
|
|
|
ViewReasonId reason,
|
|
|
|
std::string msg,
|
|
|
|
std::shared_ptr<ConnectedClient> invoker,
|
2019-10-20 12:09:28 +02:00
|
|
|
bool lock_channel_tree) {
|
|
|
|
|
2020-01-24 02:57:58 +01:00
|
|
|
if(&*client == this) {
|
|
|
|
this->properties()[property::CLIENT_LAST_CHANNEL] = target_channel ? target_channel->channelId() : 0;
|
|
|
|
}
|
|
|
|
return true;
|
2019-07-17 19:37:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void MusicClient::initialize_bot() {
|
2020-01-24 02:57:58 +01:00
|
|
|
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::value>() == MusicClient::UptimeMode::TIME_SINCE_SERVER_START) {
|
|
|
|
this->properties()[property::CLIENT_LASTCONNECTED] = duration_cast<seconds>(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();
|
|
|
|
|
2020-02-01 14:32:16 +01:00
|
|
|
if(this->getClientId() == 0 || this->server->find_client_by_id(this->getClientId()) != _this.lock()) {
|
2020-01-24 02:57:58 +01:00
|
|
|
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];
|
2019-07-17 19:37:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void MusicClient::broadcast_text_message(const std::string &message) {
|
2020-02-02 14:58:46 +01:00
|
|
|
this->server->send_text_message(this->currentChannel, this->ref(), message);
|
2019-07-17 19:37:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
//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<ts::server::ConnectedClient> &client) {
|
2020-01-24 02:57:58 +01:00
|
|
|
lock_guard lock(this->subscriber_lock);
|
|
|
|
this->subscribers.erase(remove_if(this->subscribers.begin(), this->subscribers.end(), [&](const std::tuple<std::weak_ptr<ConnectedClient>, system_clock::time_point>& entry) {
|
|
|
|
auto e = get<0>(entry).lock();
|
|
|
|
return !e || e == client;
|
|
|
|
}), this->subscribers.end());
|
2019-07-17 19:37:18 +02:00
|
|
|
|
2020-01-24 02:57:58 +01:00
|
|
|
this->subscribers.push_back({client, system_clock::time_point{}});
|
2019-07-17 19:37:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool MusicClient::is_subscriber(const std::shared_ptr<ts::server::ConnectedClient> &client) {
|
2020-01-24 02:57:58 +01:00
|
|
|
lock_guard lock(this->subscriber_lock);
|
|
|
|
for(const auto& entry : this->subscribers)
|
|
|
|
if(std::get<0>(entry).lock() == client)
|
|
|
|
return true;
|
|
|
|
return false;
|
2019-07-17 19:37:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void MusicClient::remove_subscriber(const std::shared_ptr<ts::server::ConnectedClient> &client) {
|
2020-01-24 02:57:58 +01:00
|
|
|
lock_guard lock(this->subscriber_lock);
|
|
|
|
this->subscribers.erase(remove_if(this->subscribers.begin(), this->subscribers.end(), [&](const std::tuple<std::weak_ptr<ConnectedClient>, system_clock::time_point>& entry) {
|
|
|
|
auto e = get<0>(entry).lock();
|
|
|
|
return !e || e == client;
|
|
|
|
}), this->subscribers.end());
|
2019-07-17 19:37:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void MusicClient::changePlayerState(ReplayState state) {
|
2020-01-24 02:57:58 +01:00
|
|
|
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::ClientProperties>{property::CLIENT_PLAYER_STATE});
|
2019-07-17 19:37:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void MusicClient::notifySongChange(const std::shared_ptr<music::SongInfo>& song) {
|
2020-01-24 02:57:58 +01:00
|
|
|
this->server->forEachClient([&](shared_ptr<ConnectedClient> client) {
|
|
|
|
client->notifyMusicPlayerSongChange(dynamic_pointer_cast<MusicClient>(_this.lock()), song);
|
|
|
|
});
|
2019-07-17 19:37:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void MusicClient::handle_event_song_ended() {
|
2020-01-24 02:57:58 +01:00
|
|
|
this->replay_next_song();
|
2019-07-17 19:37:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void MusicClient::handle_event_song_dry() {
|
2020-01-24 02:57:58 +01:00
|
|
|
this->replay_next_song();
|
2019-07-17 19:37:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void MusicClient::handle_event_song_replay_failed() {
|
2020-01-24 02:57:58 +01:00
|
|
|
this->replay_next_song();
|
2019-07-17 19:37:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void MusicClient::replay_next_song() {
|
2020-01-24 02:57:58 +01:00
|
|
|
auto playlist = this->playlist();
|
|
|
|
if(playlist) {
|
2020-02-02 14:58:46 +01:00
|
|
|
bool await_load;
|
|
|
|
if(auto song = playlist->current_song(await_load); song)
|
2020-01-24 02:57:58 +01:00
|
|
|
this->replay_song(song);
|
2020-02-02 14:58:46 +01:00
|
|
|
else if(!await_load)
|
|
|
|
playlist->next();
|
2020-01-24 02:57:58 +01:00
|
|
|
}
|
2019-07-17 19:37:18 +02:00
|
|
|
}
|