Some updates
This commit is contained in:
parent
571eaa0fa5
commit
d3ee202313
@ -1 +1 @@
|
||||
Subproject commit 65760b9e3cd5647091c7a38a0427c23bffaa815e
|
||||
Subproject commit 9a26231c1f6c44f799419f87a3cac37a55425bdb
|
@ -397,8 +397,8 @@ bool ConnectedClient::handle_text_command(
|
||||
}
|
||||
}
|
||||
|
||||
string data = "\"" + song->url + "\" added by " + invoker;
|
||||
if(song->id == current_song)
|
||||
string data = "\"" + song->original_url + "\" added by " + invoker;
|
||||
if(song->song_id == current_song)
|
||||
data = "[color=orange]" + data + "[/color]";
|
||||
send_message(bot, " - " + data);
|
||||
}
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include <misc/strobf.h>
|
||||
#include <misc/scope_guard.h>
|
||||
#include <bbcode/bbcodes.h>
|
||||
#include <src/music/MusicPlaylist.h>
|
||||
|
||||
namespace fs = std::experimental::filesystem;
|
||||
using namespace std::chrono;
|
||||
@ -818,15 +819,33 @@ command_result ConnectedClient::handleCommandPlaylistSongList(ts::Command &cmd)
|
||||
Command notify(this->notify_response_command("notifyplaylistsonglist"));
|
||||
notify["playlist_id"] = playlist->playlist_id();
|
||||
|
||||
auto extract_metadata = cmd.hasParm("extract-metadata");
|
||||
|
||||
size_t index = 0;
|
||||
for(const auto& song : songs) {
|
||||
notify[index]["song_id"] = song->id;
|
||||
notify[index]["song_id"] = song->song_id;
|
||||
notify[index]["song_invoker"] = song->invoker;
|
||||
notify[index]["song_previous_song_id"] = song->previous_song_id;
|
||||
notify[index]["song_url"] = song->url;
|
||||
notify[index]["song_url"] = song->original_url;
|
||||
notify[index]["song_url_loader"] = song->url_loader;
|
||||
notify[index]["song_loaded"] = song->loaded;
|
||||
notify[index]["song_metadata"] = song->metadata;
|
||||
notify[index]["song_loaded"] = song->metadata.is_loaded();
|
||||
notify[index]["song_metadata"] = song->metadata.json_string;
|
||||
|
||||
if(extract_metadata) {
|
||||
auto metadata = song->metadata.loaded_data;
|
||||
if(extract_metadata && song->metadata.is_loaded() && metadata) {
|
||||
notify[index]["song_metadata_title"] = metadata->title;
|
||||
notify[index]["song_metadata_description"] = metadata->description;
|
||||
notify[index]["song_metadata_url"] = metadata->url;
|
||||
notify[index]["song_metadata_length"] = metadata->length.count();
|
||||
if(auto thumbnail = static_pointer_cast<::music::ThumbnailUrl>(metadata->thumbnail); thumbnail && thumbnail->type() == ::music::THUMBNAIL_URL) {
|
||||
notify[index]["song_metadata_thumbnail_url"] = thumbnail->url();
|
||||
} else {
|
||||
notify[index]["song_metadata_thumbnail_url"] = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
this->sendCommand(notify);
|
||||
@ -862,18 +881,30 @@ command_result ConnectedClient::handleCommandPlaylistSongAdd(ts::Command &cmd) {
|
||||
if(auto perr = playlist->client_has_permissions(this->ref(), permission::i_playlist_song_needed_add_power, permission::i_playlist_song_add_power); perr)
|
||||
return command_result{perr};
|
||||
|
||||
if(!cmd[0].has("invoker"))
|
||||
cmd["invoker"] = "";
|
||||
if(cmd[0].has("invoker"))
|
||||
cmd["type"] = "";
|
||||
else if(!cmd[0].has("type"))
|
||||
cmd["type"] = "";
|
||||
|
||||
if(!cmd[0].has("previous")) {
|
||||
auto songs = playlist->list_songs();
|
||||
if(songs.empty())
|
||||
cmd["previous"] = "0";
|
||||
else
|
||||
cmd["previous"] = songs.back()->id;
|
||||
cmd["previous"] = songs.back()->song_id;
|
||||
}
|
||||
|
||||
auto& type = cmd[0]["type"];
|
||||
std::string loader_string{""};
|
||||
if((type.castable<int>() && type.as<int>() == 0) || type.as<string>() == "yt") {
|
||||
loader_string = "YouTube";
|
||||
} else if((type.castable<int>() && type.as<int>() == 1) || type.as<string>() == "ffmpeg") {
|
||||
loader_string = "FFMpeg";
|
||||
} else if((type.castable<int>() && type.as<int>() == 2) || type.as<string>() == "channel") {
|
||||
loader_string = "ChannelProvider";
|
||||
}
|
||||
|
||||
auto song = playlist->add_song(_this.lock(), cmd["url"], cmd["invoker"], cmd["previous"]);
|
||||
auto song = playlist->add_song(_this.lock(), cmd["url"], loader_string, cmd["previous"]);
|
||||
if(!song) return command_result{error::vs_critical};
|
||||
|
||||
return command_result{error::ok};
|
||||
@ -927,6 +958,7 @@ command_result ConnectedClient::handleCommandPlaylistSongRemove(ts::Command &cmd
|
||||
|
||||
/****** legacy ******/
|
||||
command_result ConnectedClient::handleCommandMusicBotQueueList(Command& cmd) {
|
||||
#if false
|
||||
CMD_REQ_SERVER;
|
||||
CMD_RESET_IDLE;
|
||||
CMD_CHK_AND_INC_FLOOD_POINTS(25);
|
||||
@ -947,7 +979,7 @@ command_result ConnectedClient::handleCommandMusicBotQueueList(Command& cmd) {
|
||||
auto songs = playlist->list_songs();
|
||||
int begin_index{0};
|
||||
for(auto it = songs.begin(); it != songs.end(); it++)
|
||||
if((*it)->id == playlist->currently_playing())
|
||||
if((*it)->song_id == playlist->currently_playing())
|
||||
break;
|
||||
else
|
||||
begin_index--;
|
||||
@ -957,12 +989,12 @@ command_result ConnectedClient::handleCommandMusicBotQueueList(Command& cmd) {
|
||||
notify = Command(this->notify_response_command("notifymusicqueueentry"));
|
||||
|
||||
auto song = *it;
|
||||
notify[command_index]["song_id"] = song->id;
|
||||
notify[command_index]["song_url"] = song->url;
|
||||
notify[command_index]["song_id"] = song->song_id;
|
||||
notify[command_index]["song_url"] = song->original_url;
|
||||
notify[command_index]["song_invoker"] = song->invoker;
|
||||
notify[command_index]["song_loaded"] = song->loaded;
|
||||
notify[command_index]["song_loaded"] = song->metadata.is_loaded();
|
||||
|
||||
auto entry = song->load_future ? song->load_future->getValue({}) : nullptr;
|
||||
auto entry = song->load_future && song->load_future->succeeded() ? song->load_future->getValue({}) : nullptr;
|
||||
if(entry) {
|
||||
notify[command_index]["song_loaded"] = true;
|
||||
if(entry->type != ::music::TYPE_STREAM && entry->type != ::music::TYPE_VIDEO)
|
||||
@ -999,8 +1031,9 @@ command_result ConnectedClient::handleCommandMusicBotQueueList(Command& cmd) {
|
||||
notify["bot_id"] = bot->getClientDatabaseId();
|
||||
this->sendCommand(notify);
|
||||
}
|
||||
|
||||
return command_result{error::ok};
|
||||
#endif
|
||||
return command_result{error::not_implemented}; //FIXME
|
||||
}
|
||||
|
||||
command_result ConnectedClient::handleCommandMusicBotQueueAdd(Command& cmd) {
|
||||
|
@ -1,9 +1,7 @@
|
||||
#include <dlfcn.h>
|
||||
#include "ChannelProvider.h"
|
||||
#include "../../MusicClient.h"
|
||||
#include "../../../../InstanceHandler.h"
|
||||
#include "../../../../server/file/FileServer.h"
|
||||
#include "../../../../channel/ServerChannel.h"
|
||||
#include "../../../../../../music/providers/ffmpeg/FFMpegProvider.h"
|
||||
|
||||
|
||||
@ -16,10 +14,10 @@ using namespace ts::server;
|
||||
|
||||
struct AudioFileInfo {
|
||||
std::string absolute_path;
|
||||
std::string title;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
extern ts::server::InstanceHandler* serverInstance;
|
||||
threads::Future<std::shared_ptr<::music::MusicPlayer>> ChannelProvider::createPlayer(const std::string &url, void*, void* ptr_server) {
|
||||
auto server = ((VirtualServer*) ptr_server)->ref();
|
||||
threads::Future<std::shared_ptr<::music::MusicPlayer>> future;
|
||||
@ -70,8 +68,8 @@ threads::Future<std::shared_ptr<::music::MusicPlayer>> ChannelProvider::createPl
|
||||
|
||||
auto info = make_shared<AudioFileInfo>();
|
||||
info->absolute_path = file->path + "/" + file->name;
|
||||
info->description = file->name;
|
||||
|
||||
info->title = file->name; /* fallback */
|
||||
info->description = "File from channel " + channel->name() + " at " + path; /* fallback */
|
||||
auto ffmpeg = ::music::manager::resolveProvider("FFMpeg", "");
|
||||
if(!ffmpeg) {
|
||||
future.executionFailed("missing ffmpeg");
|
||||
@ -84,16 +82,20 @@ threads::Future<std::shared_ptr<::music::MusicPlayer>> ChannelProvider::createPl
|
||||
future.executionFailed("failed to allocate memory");
|
||||
return;
|
||||
}
|
||||
memset(ffmpeg_data, 0, sizeof(::music::FFMpegData::FileReplay));
|
||||
|
||||
ffmpeg_data->version = ::music::FFMpegData::CURRENT_VERSION;
|
||||
ffmpeg_data->version = 2;
|
||||
ffmpeg_data->type = ::music::FFMpegData::REPLAY_FILE;
|
||||
ffmpeg_data->_free = ::free;
|
||||
|
||||
ffmpeg_data->file_path = (char*) malloc(info->absolute_path.length() + 1);
|
||||
ffmpeg_data->file_path[info->absolute_path.length()] = '\0';
|
||||
ffmpeg_data->file_title = (char*) malloc(info->title.length() + 1);
|
||||
ffmpeg_data->file_title[info->title.length()] = '\0';
|
||||
ffmpeg_data->file_description = (char*) malloc(info->description.length() + 1);
|
||||
ffmpeg_data->file_description[info->description.length()] = '\0';
|
||||
|
||||
memcpy(ffmpeg_data->file_title, info->title.data(), info->title.length());
|
||||
memcpy(ffmpeg_data->file_path, info->absolute_path.data(), info->absolute_path.length());
|
||||
memcpy(ffmpeg_data->file_description, info->description.data(), info->description.length());
|
||||
|
||||
@ -101,9 +103,10 @@ threads::Future<std::shared_ptr<::music::MusicPlayer>> ChannelProvider::createPl
|
||||
p.wait();
|
||||
if(p.failed())
|
||||
future.executionFailed(p.errorMegssage());
|
||||
else
|
||||
else {
|
||||
future.executionSucceed(*p.get());
|
||||
}
|
||||
}
|
||||
return;
|
||||
} catch(std::exception& ex) {
|
||||
future.executionFailed("failed to process data");
|
||||
@ -135,38 +138,17 @@ vector<string> ChannelProvider::availableProtocols() {
|
||||
}
|
||||
|
||||
std::shared_ptr<::music::manager::PlayerProvider> ChannelProvider::create_provider() {
|
||||
/*
|
||||
std::shared_ptr<yt::YTProviderConfig> config = make_shared<yt::YTProviderConfig>();
|
||||
|
||||
{
|
||||
auto config_path = fs::u8path("providers/config_channel.ini");
|
||||
music::log::log(music::log::debug, "[YT-DL] Using config file located at " + config_path.string());
|
||||
if(fs::exists(config_path)) {
|
||||
INIReader ini_reader(config_path.string());
|
||||
|
||||
if(ini_reader.ParseError()) {
|
||||
music::log::log(music::log::err, "[YT-DL] Could not parse config! Using default values");
|
||||
} else {
|
||||
config->youtubedl_command = ini_reader.Get("general", "youtubedl_command", config->youtubedl_command);
|
||||
config->commands.version = ini_reader.Get("commands", "version", config->commands.version);
|
||||
config->commands.query_video = ini_reader.Get("commands", "query_video", config->commands.query_video);
|
||||
music::log::log(music::log::info, "[YT-DL] Config successfully loaded");
|
||||
}
|
||||
} else {
|
||||
music::log::log(music::log::debug, "[YT-DL] Missing configuration file. Using default values");
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
return std::shared_ptr<ChannelProvider>(new ChannelProvider(), [](ChannelProvider* provider){
|
||||
if(!provider) return;
|
||||
delete provider;
|
||||
});
|
||||
}
|
||||
|
||||
#if 0
|
||||
threads::Future<shared_ptr<UrlInfo>> ChannelProvider::query_info(const std::string &string, void *pVoid, void *pVoid1) {
|
||||
auto info = make_shared<UrlSongInfo>();
|
||||
|
||||
|
||||
info->type = UrlType::TYPE_VIDEO;
|
||||
info->url = string;
|
||||
info->title = "";
|
||||
@ -177,3 +159,111 @@ threads::Future<shared_ptr<UrlInfo>> ChannelProvider::query_info(const std::stri
|
||||
result.executionSucceed(info);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
threads::Future<shared_ptr<UrlInfo>> ChannelProvider::query_info(const std::string &url, void*, void* ptr_server) {
|
||||
auto server = ((VirtualServer*) ptr_server)->ref();
|
||||
threads::Future<shared_ptr<UrlInfo>> future;
|
||||
|
||||
if(server) {
|
||||
std::thread([future, server, url, ptr_server]{
|
||||
auto f_server = serverInstance->getFileServer();
|
||||
if(!f_server) {
|
||||
future.executionFailed("instance missing file server");
|
||||
return;
|
||||
}
|
||||
|
||||
Command command("");
|
||||
try {
|
||||
command = Command::parse(pipes::buffer_view{url.data(), url.length()}, false);
|
||||
} catch(std::exception& ex) {
|
||||
future.executionFailed("failed to parse data");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auto channel_id = command["cid"].as<ChannelId>();
|
||||
auto name = command["name"].string();
|
||||
auto path = command["path"].string();
|
||||
|
||||
auto channel = server->getChannelTree()->findChannel(channel_id);
|
||||
if(!channel) {
|
||||
future.executionFailed("could not find target channel");
|
||||
return;
|
||||
}
|
||||
|
||||
auto directory = f_server->resolveDirectory(server, channel, path);
|
||||
if(!directory) {
|
||||
future.executionFailed("invalid file path");
|
||||
return;
|
||||
}
|
||||
|
||||
auto file = f_server->findFile(name, directory);
|
||||
if(!file) {
|
||||
future.executionFailed("could not find file");
|
||||
return;
|
||||
}
|
||||
|
||||
if(file->type != file::FileType::FILE) {
|
||||
future.executionFailed("file isnt a file");
|
||||
return;
|
||||
}
|
||||
|
||||
auto info = make_shared<AudioFileInfo>();
|
||||
info->absolute_path = file->path + "/" + file->name;
|
||||
info->title = file->name; /* fallback */
|
||||
info->description = "File from channel " + channel->name() + " at " + file->path; /* fallback */
|
||||
|
||||
auto ffmpeg = ::music::manager::resolveProvider("FFMpeg", "");
|
||||
if(!ffmpeg) {
|
||||
future.executionFailed("missing ffmpeg");
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto ffmpeg_data = (::music::FFMpegData::FileReplay*) malloc(sizeof(::music::FFMpegData::FileReplay));
|
||||
if(!ffmpeg_data) {
|
||||
future.executionFailed("failed to allocate memory");
|
||||
return;
|
||||
}
|
||||
memset(ffmpeg_data, 0, sizeof(::music::FFMpegData::FileReplay));
|
||||
|
||||
ffmpeg_data->version = 2;
|
||||
ffmpeg_data->type = ::music::FFMpegData::REPLAY_FILE;
|
||||
ffmpeg_data->_free = ::free;
|
||||
|
||||
ffmpeg_data->file_path = (char*) malloc(info->absolute_path.length() + 1);
|
||||
ffmpeg_data->file_path[info->absolute_path.length()] = '\0';
|
||||
ffmpeg_data->file_title = (char*) malloc(info->title.length() + 1);
|
||||
ffmpeg_data->file_title[info->title.length()] = '\0';
|
||||
ffmpeg_data->file_description = (char*) malloc(info->description.length() + 1);
|
||||
ffmpeg_data->file_description[info->description.length()] = '\0';
|
||||
|
||||
memcpy(ffmpeg_data->file_title, info->title.data(), info->title.length());
|
||||
memcpy(ffmpeg_data->file_path, info->absolute_path.data(), info->absolute_path.length());
|
||||
memcpy(ffmpeg_data->file_description, info->description.data(), info->description.length());
|
||||
|
||||
auto p = ffmpeg->query_info("", ffmpeg_data, ptr_server);
|
||||
if(!p.wait(std::chrono::system_clock::now() + std::chrono::seconds{30}))
|
||||
future.executionFailed("ffmpeg load timeout");
|
||||
else if(p.failed())
|
||||
future.executionFailed(p.errorMegssage());
|
||||
else {
|
||||
auto result = *p.get();
|
||||
result->url = url;
|
||||
future.executionSucceed(result);
|
||||
}
|
||||
}
|
||||
return;
|
||||
} catch(std::exception& ex) {
|
||||
future.executionFailed("failed to process data");
|
||||
return;
|
||||
}
|
||||
}).detach();
|
||||
} else {
|
||||
future.executionFailed("invalid bot");
|
||||
}
|
||||
|
||||
|
||||
return future;
|
||||
}
|
@ -11,7 +11,7 @@
|
||||
#include "../../../cmake-build-debug-wsl/license/LicenseRequest.pb.h"
|
||||
#include "src/InstanceHandler.h"
|
||||
|
||||
//#define DO_LOCAL_REQUEST
|
||||
#define DO_LOCAL_REQUEST
|
||||
using namespace ts::server::license;
|
||||
|
||||
LicenseService::LicenseService() {
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include "src/VirtualServer.h"
|
||||
#include "src/client/ConnectedClient.h"
|
||||
#include <log/LogUtils.h>
|
||||
|
||||
#include <utility>
|
||||
#include "teaspeak/MusicPlayer.h"
|
||||
|
||||
using namespace ts;
|
||||
@ -11,8 +13,8 @@ using namespace ts::music;
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
|
||||
Playlist::Playlist(const std::shared_ptr<ts::music::MusicBotManager> &manager, const std::shared_ptr<ts::Properties> &properties, std::shared_ptr<permission::v2::PermissionManager> permissions) :
|
||||
PlaylistPermissions{std::move(permissions)}, _properties(properties), manager(manager) { }
|
||||
Playlist::Playlist(const std::shared_ptr<ts::music::MusicBotManager> &manager, std::shared_ptr<ts::Properties> properties, std::shared_ptr<permission::v2::PermissionManager> permissions) :
|
||||
PlaylistPermissions{std::move(permissions)}, _properties{std::move(properties)}, manager{manager} { }
|
||||
|
||||
Playlist::~Playlist() {
|
||||
this->destroy_tree();
|
||||
@ -59,7 +61,7 @@ std::shared_ptr<PlaylistEntry> Playlist::playlist_find(const std::unique_lock<st
|
||||
while(current) {
|
||||
assert(current->entry);
|
||||
|
||||
if(current->entry->id == id)
|
||||
if(current->entry->song_id == id)
|
||||
return current;
|
||||
|
||||
current = current->next_song;
|
||||
@ -145,19 +147,19 @@ bool Playlist::sql_add(const std::shared_ptr<ts::music::PlaylistEntryInfo> &entr
|
||||
auto sql_handle = this->get_sql();
|
||||
if(!sql_handle) return false;
|
||||
|
||||
entry->id = ++current_id;
|
||||
entry->song_id = ++current_id;
|
||||
|
||||
//`serverId` INT NOT NULL, `playlist_id` INT, `song_id` INT, `order_id` INT, `invoker_dbid` INT, `url` TEXT
|
||||
auto sql_result = sql::command(sql_handle, "INSERT INTO `playlist_songs` (`serverId`, `playlist_id`, `song_id`, `order_id`, `invoker_dbid`, `url`, `url_loader`, `loaded`, `metadata`) VALUES (:server_id, :playlist_id, :song_id, :order_id, :invoker_dbid, :url, :url_loader, :loaded, :metadata)",
|
||||
variable{":server_id", this->get_server_id()},
|
||||
variable{":playlist_id", this->playlist_id()},
|
||||
variable{":song_id", entry->id},
|
||||
variable{":song_id", entry->song_id},
|
||||
variable{":order_id", entry->previous_song_id},
|
||||
variable{":invoker_dbid", entry->invoker},
|
||||
variable{":url", entry->url},
|
||||
variable{":url", entry->original_url},
|
||||
variable{":url_loader", entry->url_loader},
|
||||
variable{":loaded", entry->loaded},
|
||||
variable{":metadata", entry->metadata}
|
||||
variable{":loaded", entry->metadata.load_state == LoadState::REQUIRES_PARSE || entry->metadata.load_state == LoadState::LOADED},
|
||||
variable{":metadata", entry->metadata.json_string}
|
||||
).execute();
|
||||
|
||||
LOG_SQL_CMD(sql_result);
|
||||
@ -171,13 +173,13 @@ bool Playlist::sql_apply_changes(const std::shared_ptr<ts::music::PlaylistEntryI
|
||||
sql::command(sql_handle, "UPDATE `playlist_songs` SET `order_id` = :order_id, `invoker_dbid` = :invoker_dbid, `url` = :url , `url_loader` = :url_loader, `loaded` = :loaded, `metadata` = :metadata WHERE `serverId` = :server_id AND `playlist_id` = :playlist_id AND `song_id` = :song_id",
|
||||
variable{":server_id", this->get_server_id()},
|
||||
variable{":playlist_id", this->playlist_id()},
|
||||
variable{":song_id", entry->id},
|
||||
variable{":song_id", entry->song_id},
|
||||
variable{":order_id", entry->previous_song_id},
|
||||
variable{":invoker_dbid", entry->invoker},
|
||||
variable{":url", entry->url},
|
||||
variable{":url", entry->original_url},
|
||||
variable{":url_loader", entry->url_loader},
|
||||
variable{":loaded", entry->loaded},
|
||||
variable{":metadata", entry->metadata}
|
||||
variable{":loaded", entry->metadata.load_state == LoadState::REQUIRES_PARSE || entry->metadata.load_state == LoadState::LOADED},
|
||||
variable{":metadata", entry->metadata.json_string}
|
||||
).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed future"});
|
||||
|
||||
return true;
|
||||
@ -211,7 +213,7 @@ bool Playlist::sql_remove(const std::shared_ptr<ts::music::PlaylistEntryInfo> &e
|
||||
sql::command(sql_handle, "DELETE FROM `playlist_songs` WHERE `serverId` = :server_id AND `playlist_id` = :playlist_id AND `song_id` = :song_id",
|
||||
variable{":server_id", this->get_server_id()},
|
||||
variable{":playlist_id", this->playlist_id()},
|
||||
variable{":song_id", entry->id}
|
||||
variable{":song_id", entry->song_id}
|
||||
).executeLater().waitAndGetLater(LOG_SQL_CMD, {-1, "failed future"});
|
||||
|
||||
return true;
|
||||
@ -231,34 +233,34 @@ std::deque<std::shared_ptr<PlaylistEntryInfo>> Playlist::load_entries() {
|
||||
for(int index = 0; index < length; index++) {
|
||||
try {
|
||||
if(columns[index] == "song_id")
|
||||
entry->id = (SongId) stoll(values[index]);
|
||||
entry->song_id = (SongId) stoll(values[index]);
|
||||
else if(columns[index] == "order_id")
|
||||
entry->previous_song_id = (SongId) stoll(values[index]);
|
||||
else if(columns[index] == "invoker_dbid")
|
||||
entry->invoker = (ClientDbId) stoll(values[index]);
|
||||
else if(columns[index] == "url")
|
||||
entry->url = values[index];
|
||||
entry->original_url = values[index];
|
||||
else if(columns[index] == "url_loader")
|
||||
entry->url_loader = values[index];
|
||||
else if(columns[index] == "loaded")
|
||||
entry->loaded = values[index].length() > 0 && stoll(values[index]) == 1;
|
||||
entry->metadata.load_state = (values[index].length() > 0 && stoll(values[index]) == 1) ? LoadState::REQUIRES_PARSE : LoadState::REQUIRES_QUERY;
|
||||
else if(columns[index] == "metadata")
|
||||
entry->metadata = values[index];
|
||||
entry->metadata.json_string = values[index];
|
||||
} catch(const std::exception& ex) {
|
||||
logError(this->get_server_id(), "[PlayList] Failed to parse song entry property in playlist {}. Key: {}, Value: {}, Error: {}", this->playlist_id(), columns[index], values[index], ex.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(entry->id > this->current_id)
|
||||
this->current_id = entry->id;
|
||||
if(entry->song_id > this->current_id)
|
||||
this->current_id = entry->song_id;
|
||||
result.push_back(move(entry));
|
||||
});
|
||||
LOG_SQL_CMD(sql_result);
|
||||
|
||||
map<PlaylistId, size_t> count;
|
||||
for(const auto& entry : result)
|
||||
++count[entry->id];
|
||||
++count[entry->song_id];
|
||||
|
||||
for(const auto& entry : count) {
|
||||
if(entry.second <= 1) continue;
|
||||
@ -276,7 +278,7 @@ std::deque<std::shared_ptr<PlaylistEntryInfo>> Playlist::load_entries() {
|
||||
|
||||
std::deque<std::shared_ptr<PlaylistEntryInfo>> _result;
|
||||
for(const auto& entry : result)
|
||||
if(count[entry->id] <= 1) {
|
||||
if(count[entry->song_id] <= 1) {
|
||||
this->enqueue_load(entry);
|
||||
_result.push_back(entry);
|
||||
}
|
||||
@ -294,14 +296,14 @@ bool Playlist::build_tree(deque<shared_ptr<PlaylistEntryInfo>> entries) {
|
||||
unique_lock list_lock(this->playlist_lock);
|
||||
auto find_entry = [&](SongId id) -> shared_ptr<PlaylistEntryInfo> {
|
||||
for(const auto& entry : entries)
|
||||
if(entry->id == id)
|
||||
if(entry->song_id == id)
|
||||
return entry;
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
deque<shared_ptr<linked::entry>> l_entries;
|
||||
for(const auto& entry : entries)
|
||||
l_entries.push_back(move(linked::create_entry(0, entry->id, entry->previous_song_id)));
|
||||
l_entries.push_back(move(linked::create_entry(0, entry->song_id, entry->previous_song_id)));
|
||||
|
||||
deque<string> errors;
|
||||
auto head = linked::build_chain(l_entries, errors);
|
||||
@ -372,7 +374,7 @@ std::shared_ptr<PlaylistEntryInfo> Playlist::find_song(ts::SongId id) {
|
||||
unique_lock list_lock(this->playlist_lock);
|
||||
auto head = this->playlist_head;
|
||||
while(head) {
|
||||
if(head->entry->id == id)
|
||||
if(head->entry->song_id == id)
|
||||
return head->entry;
|
||||
|
||||
head = head->next_song;
|
||||
@ -391,7 +393,7 @@ std::shared_ptr<PlaylistEntryInfo> Playlist::add_song(ClientDbId client, const s
|
||||
|
||||
entry->previous_song_id = order;
|
||||
entry->invoker = client;
|
||||
entry->url = url;
|
||||
entry->original_url = url;
|
||||
entry->url_loader = url_loader;
|
||||
|
||||
if(!this->sql_add(entry)) return nullptr;
|
||||
@ -402,15 +404,15 @@ std::shared_ptr<PlaylistEntryInfo> Playlist::add_song(ClientDbId client, const s
|
||||
unique_lock list_lock(this->playlist_lock);
|
||||
if(order == 0) {
|
||||
auto end = playlist_end(list_lock);
|
||||
entry->previous_song_id = end ? end->entry->id : 0;
|
||||
entry->previous_song_id = end ? end->entry->song_id : 0;
|
||||
list_entry->modified = true;
|
||||
}
|
||||
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) {
|
||||
if(order_entry ? order_entry->entry->song_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;
|
||||
entry->previous_song_id = list_entry->previous_song ? list_entry->previous_song->entry->song_id : 0;
|
||||
list_lock.unlock();
|
||||
|
||||
this->sql_apply_changes(entry);
|
||||
@ -455,15 +457,15 @@ bool Playlist::reorder_song(ts::SongId song_id, ts::SongId order_id) {
|
||||
|
||||
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 */
|
||||
std::lock_guard song_lock_lock{entry->metadata.load_lock};
|
||||
if(!entry->metadata.requires_load())
|
||||
return;
|
||||
|
||||
entry->load_start = system_clock::now();
|
||||
entry->load_future = make_unique<PlaylistEntryInfo::load_future_t>();
|
||||
weak_ptr weak_self = this->ref<Playlist>();
|
||||
weak_ptr weak_entry = entry;
|
||||
|
||||
logTrace(this->get_server_id(), "[PlayList] Enqueueing song {} for loading", entry->id);
|
||||
entry->metadata.load_begin = system_clock::now();
|
||||
std::weak_ptr weak_self = this->ref<Playlist>();
|
||||
std::weak_ptr weak_entry{entry};
|
||||
|
||||
logTrace(this->get_server_id(), "[PlayList] Enqueueing song {} for loading", entry->song_id);
|
||||
MusicBotManager::load_music.execute([weak_self, weak_entry] {
|
||||
shared_ptr<Playlist> self = weak_self.lock();
|
||||
auto entry = weak_entry.lock();
|
||||
@ -474,7 +476,6 @@ void Playlist::enqueue_load(const std::shared_ptr<ts::music::PlaylistEntryInfo>
|
||||
}
|
||||
|
||||
#define FORCE_LOAD \
|
||||
entry->loaded = false; \
|
||||
goto load_entry
|
||||
|
||||
/*
|
||||
@ -491,28 +492,41 @@ goto load_entry
|
||||
*/
|
||||
|
||||
using UrlType = ::music::UrlType;
|
||||
using UrlSongInfo = ::music::UrlSongInfo;
|
||||
using UrlPlaylistInfo = ::music::UrlPlaylistInfo;
|
||||
using UrlSongInfo = ::music::UrlSongInfo;
|
||||
|
||||
#define load_finished(state, error, result) \
|
||||
do { \
|
||||
{ \
|
||||
std::lock_guard slock{entry->metadata.load_lock}; \
|
||||
entry->metadata.load_state = state; \
|
||||
entry->metadata.loaded_data = result; \
|
||||
entry->metadata.load_error = error; \
|
||||
entry->metadata.loaded_cv.notify_all(); \
|
||||
} \
|
||||
this->notify_song_loaded(entry);\
|
||||
} while(0)
|
||||
|
||||
void Playlist::execute_async_load(const std::shared_ptr<ts::music::PlaylistEntryInfo> &entry) {
|
||||
if(entry->loaded) { /* we need to parse the metadata */
|
||||
//TODO: Song might get removed while being loaded. Need better handling specially with the notified etc.
|
||||
if(entry->metadata.load_state == LoadState::REQUIRES_PARSE) { /* we need to parse the metadata */
|
||||
Json::Value data;
|
||||
Json::CharReaderBuilder rbuilder;
|
||||
std::string errs;
|
||||
|
||||
istringstream jsonStream(entry->metadata);
|
||||
istringstream jsonStream(entry->metadata.json_string);
|
||||
bool parsingSuccessful = Json::parseFromStream(rbuilder, jsonStream, &data, &errs);
|
||||
if (!parsingSuccessful) {
|
||||
logError(this->get_server_id(), "[PlayList] Failed to parse loaded metadata for song {}. Metadata data!", entry->id);
|
||||
logError(this->get_server_id(), "[PlayList] Failed to parse loaded metadata for song {}. Metadata data!", entry->song_id);
|
||||
FORCE_LOAD;
|
||||
}
|
||||
|
||||
if(!data["type"].isUInt()) {
|
||||
logError(this->get_server_id(), "[PlayList] Failed to parse loaded metadata for song {}. Metadata has an invalid key 'type'. Query data again!", entry->id);
|
||||
logError(this->get_server_id(), "[PlayList] Failed to parse loaded metadata for song {}. Metadata has an invalid key 'type'. Query data again!", entry->song_id);
|
||||
FORCE_LOAD;
|
||||
}
|
||||
if(!data["url"].isString()) {
|
||||
logError(this->get_server_id(), "[PlayList] Failed to parse loaded metadata for song {}. Metadata has an invalid key 'url'. Query data again!", entry->id);
|
||||
logError(this->get_server_id(), "[PlayList] Failed to parse loaded metadata for song {}. Metadata has an invalid key 'url'. Query data again!", entry->song_id);
|
||||
FORCE_LOAD;
|
||||
}
|
||||
|
||||
@ -531,25 +545,25 @@ void Playlist::execute_async_load(const std::shared_ptr<ts::music::PlaylistEntry
|
||||
info->type = type;
|
||||
info->description = data["description"].asString();
|
||||
|
||||
entry->load_future->executionSucceed(info);
|
||||
load_finished(LoadState::LOADED, "", info);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
load_entry:
|
||||
if(!entry->loaded) {
|
||||
auto provider = ::music::manager::resolveProvider(entry->url_loader, entry->url);
|
||||
auto provider = ::music::manager::resolveProvider(entry->url_loader, entry->original_url);
|
||||
if(!provider) {
|
||||
entry->load_future->executionFailed("failed to load info provider");
|
||||
this->notify_song_loaded(entry);
|
||||
load_finished(LoadState::LOAD_ERROR, "failed to load info provider", nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
auto info_future = provider->query_info(entry->url, nullptr, &*this->get_server());
|
||||
auto info_future = provider->query_info(entry->original_url, nullptr, &*this->get_server());
|
||||
info_future.waitAndGet(system_clock::now() + seconds(30)); /* TODO load timeout configurable? */
|
||||
|
||||
if(info_future.succeeded() && info_future.get()) {
|
||||
auto result = *info_future.get();
|
||||
|
||||
/* create the json string */
|
||||
{
|
||||
Json::Value root;
|
||||
root["type"] = result->type;
|
||||
@ -569,18 +583,16 @@ void Playlist::execute_async_load(const std::shared_ptr<ts::music::PlaylistEntry
|
||||
root["metadata"][meta.first] = meta.second;
|
||||
}
|
||||
|
||||
entry->metadata = root.toStyledString();
|
||||
|
||||
Json::StreamWriterBuilder builder;
|
||||
builder["indentation"] = ""; // If you want whitespace-less output
|
||||
entry->metadata = Json::writeString(builder, root);
|
||||
entry->metadata.json_string = Json::writeString(builder, root);
|
||||
}
|
||||
|
||||
if(result->type == UrlType::TYPE_PLAYLIST) {
|
||||
auto playlist = static_pointer_cast<UrlPlaylistInfo>(result);
|
||||
debugMessage(this->get_server_id(), "[PlayList] Song {} shows up as a playlist. Inserting entries {}", entry->id, playlist->entries.size());
|
||||
debugMessage(this->get_server_id(), "[PlayList] Song {} shows up as a playlist. Inserting entries {}", entry->song_id, playlist->entries.size());
|
||||
|
||||
SongId previous_id = entry->id;
|
||||
SongId previous_id = entry->song_id;
|
||||
size_t current_songs = this->list_songs().size();
|
||||
auto max_songs = this->max_songs();
|
||||
|
||||
@ -592,24 +604,18 @@ void Playlist::execute_async_load(const std::shared_ptr<ts::music::PlaylistEntry
|
||||
auto queued_element = this->add_song(entry->invoker, element->url, "", previous_id);
|
||||
if(!queued_element) continue; //TODO log insert fail?
|
||||
|
||||
previous_id = queued_element->id;
|
||||
previous_id = queued_element->song_id;
|
||||
}
|
||||
|
||||
this->delete_song(entry->id); /* delete playlist song entry because it has been resolved compleatly */
|
||||
}
|
||||
entry->load_future->executionSucceed(result);
|
||||
entry->loaded = true;
|
||||
|
||||
/* 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);
|
||||
this->delete_song(entry->song_id); /* delete playlist song entry because it has been resolved completely */
|
||||
return;
|
||||
}
|
||||
|
||||
load_finished(LoadState::LOADED, "", static_pointer_cast<UrlSongInfo>(result));
|
||||
this->sql_apply_changes(entry);
|
||||
} else {
|
||||
load_finished(LoadState::LOAD_ERROR, info_future.failed() ? "failed to query info: " + info_future.errorMegssage() : "info load timeout", nullptr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -634,15 +640,16 @@ inline bool write_song(const std::shared_ptr<PlaylistEntryInfo> &entry, command_
|
||||
return true;
|
||||
}
|
||||
|
||||
builder.put_unchecked(index, "song_id", entry->id);
|
||||
builder.put_unchecked(index, "song_id", entry->song_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", entry->original_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);
|
||||
|
||||
std::lock_guard llock{entry->metadata.load_lock};
|
||||
builder.put_unchecked(index, "song_loaded", entry->metadata.is_loaded());
|
||||
builder.put_unchecked(index, "song_metadata", entry->metadata.json_string);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -672,7 +679,7 @@ bool Playlist::notify_song_remove(const std::shared_ptr<PlaylistEntryInfo> &entr
|
||||
command_builder result{"notifyplaylistsongremove"};
|
||||
|
||||
result.put_unchecked(0, "playlist_id", this->playlist_id());
|
||||
result.put_unchecked(0, "song_id", entry->id);
|
||||
result.put_unchecked(0, "song_id", entry->song_id);
|
||||
|
||||
this->broadcast_notify(result);
|
||||
return true;
|
||||
@ -682,7 +689,7 @@ bool Playlist::notify_song_reorder(const std::shared_ptr<PlaylistEntryInfo> &ent
|
||||
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_id", entry->song_id);
|
||||
result.put_unchecked(0, "song_previous_song_id", entry->previous_song_id);
|
||||
|
||||
this->broadcast_notify(result);
|
||||
@ -693,14 +700,16 @@ bool Playlist::notify_song_loaded(const std::shared_ptr<PlaylistEntryInfo> &entr
|
||||
command_builder result{"notifyplaylistsongloaded"};
|
||||
|
||||
result.put_unchecked(0, "playlist_id", this->playlist_id());
|
||||
result.put_unchecked(0, "song_id", entry->id);
|
||||
result.put_unchecked(0, "song_id", entry->song_id);
|
||||
|
||||
if(entry->load_future && entry->load_future->failed()) {
|
||||
|
||||
std::lock_guard llock{entry->metadata.load_lock};
|
||||
if(entry->metadata.has_failed_to_load()) {
|
||||
result.put_unchecked(0, "success", "0");
|
||||
result.put_unchecked(0, "load_error_msg", entry->load_future->errorMegssage());
|
||||
result.put_unchecked(0, "load_error_msg", entry->metadata.load_error);
|
||||
} else {
|
||||
result.put_unchecked(0, "success", "1");
|
||||
result.put_unchecked(0, "song_metadata", entry->metadata);
|
||||
result.put_unchecked(0, "song_metadata", entry->metadata.json_string);
|
||||
}
|
||||
|
||||
this->broadcast_notify(result);
|
||||
|
@ -21,24 +21,41 @@ namespace ts {
|
||||
namespace music {
|
||||
class Playlist;
|
||||
|
||||
enum struct LoadState {
|
||||
REQUIRES_QUERY, /* requires general song query */
|
||||
REQUIRES_PARSE, /* requires a parse of the given json string */
|
||||
|
||||
LOADED, /* metadata has been loaded */
|
||||
LOAD_ERROR
|
||||
};
|
||||
|
||||
struct PlaylistEntryInfo {
|
||||
typedef threads::Future<std::shared_ptr<::music::UrlInfo>> load_future_t;
|
||||
SongId previous_song_id = 0;
|
||||
/* static part available all the time */
|
||||
SongId previous_song_id{0};
|
||||
SongId song_id{0};
|
||||
|
||||
ClientDbId invoker = 0;
|
||||
SongId id = 0;
|
||||
|
||||
std::string url;
|
||||
std::string url_loader;
|
||||
std::string metadata; /* only present when loaded */
|
||||
bool loaded = false;
|
||||
|
||||
std::chrono::system_clock::time_point load_start;
|
||||
std::unique_ptr<load_future_t> load_future;
|
||||
ClientDbId invoker{0};
|
||||
std::string original_url{};
|
||||
std::string url_loader{};
|
||||
|
||||
/* dynamic part only available after the song has been loaded successfully */
|
||||
struct {
|
||||
bool successfully_loaded;
|
||||
} data;
|
||||
std::mutex load_lock{};
|
||||
LoadState load_state{false};
|
||||
|
||||
std::string json_string{};
|
||||
std::condition_variable loaded_cv{};
|
||||
|
||||
std::shared_ptr<::music::UrlSongInfo> loaded_data{nullptr}; /* only set when successfully loaded */
|
||||
std::string load_error{};
|
||||
|
||||
std::chrono::system_clock::time_point load_begin{};
|
||||
|
||||
[[nodiscard]] inline bool requires_load() const { return this->load_state == LoadState::REQUIRES_PARSE ||this->load_state == LoadState::REQUIRES_QUERY; }
|
||||
[[nodiscard]] inline bool is_loading() const { return (this->load_begin.time_since_epoch().count() > 0) && (this->load_state == LoadState::REQUIRES_PARSE ||this->load_state == LoadState::REQUIRES_QUERY); }
|
||||
[[nodiscard]] inline bool is_loaded() const { return this->load_state == LoadState::LOADED; }
|
||||
[[nodiscard]] inline bool has_failed_to_load() const { return this->load_state == LoadState::LOAD_ERROR; }
|
||||
} metadata;
|
||||
};
|
||||
|
||||
class PlaylistEntry {
|
||||
@ -57,7 +74,7 @@ namespace ts {
|
||||
assert(this->entry);
|
||||
|
||||
this->previous_song = song;
|
||||
this->entry->previous_song_id = song ? song->entry->id : 0;
|
||||
this->entry->previous_song_id = song ? song->entry->song_id : 0;
|
||||
this->modified = true;
|
||||
}
|
||||
};
|
||||
@ -73,7 +90,7 @@ namespace ts {
|
||||
};
|
||||
};
|
||||
|
||||
Playlist(const std::shared_ptr<MusicBotManager>& /* manager */, const std::shared_ptr<Properties>& /* properties */, std::shared_ptr<permission::v2::PermissionManager> /* permissions */);
|
||||
Playlist(const std::shared_ptr<MusicBotManager>& /* manager */, std::shared_ptr<Properties> /* properties */, std::shared_ptr<permission::v2::PermissionManager> /* permissions */);
|
||||
virtual ~Playlist();
|
||||
|
||||
virtual void load_songs();
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "src/VirtualServer.h"
|
||||
#include "src/client/music/MusicClient.h"
|
||||
#include "src/client/ConnectedClient.h"
|
||||
#include "MusicPlaylist.h"
|
||||
#include <log/LogUtils.h>
|
||||
|
||||
using namespace ts;
|
||||
@ -39,7 +40,7 @@ void PlayablePlaylist::previous() {
|
||||
|
||||
auto entry = this->playlist_previous_entry();
|
||||
if(entry) this->enqueue_load(entry);
|
||||
this->properties()[property::PLAYLIST_CURRENT_SONG_ID] = entry ? entry->id : 0;
|
||||
this->properties()[property::PLAYLIST_CURRENT_SONG_ID] = entry ? entry->song_id : 0;
|
||||
this->current_loading_entry = entry;
|
||||
}
|
||||
|
||||
@ -48,7 +49,7 @@ void PlayablePlaylist::next() {
|
||||
|
||||
auto entry = this->playlist_next_entry();
|
||||
if(entry) this->enqueue_load(entry);
|
||||
this->properties()[property::PLAYLIST_CURRENT_SONG_ID] = entry ? entry->id : 0;
|
||||
this->properties()[property::PLAYLIST_CURRENT_SONG_ID] = entry ? entry->song_id : 0;
|
||||
this->current_loading_entry = entry;
|
||||
}
|
||||
|
||||
@ -58,20 +59,19 @@ std::shared_ptr<ts::music::PlayableSong> PlayablePlaylist::current_song(bool& aw
|
||||
unique_lock lock(this->currently_playing_lock);
|
||||
auto entry = this->current_loading_entry.lock();
|
||||
|
||||
auto id = entry ? entry->id : 0;
|
||||
auto id = entry ? entry->song_id : 0;
|
||||
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) return nullptr; /* skip this entry */
|
||||
if(!entry || entry->metadata.requires_load()) return nullptr;
|
||||
|
||||
if(entry->load_future->state() == threads::FutureState::WORKING) {
|
||||
if(entry->load_start + seconds(30) < system_clock::now()) {
|
||||
this->delete_song(entry->id); /* delete song because we cant load it */
|
||||
if(entry->metadata.is_loading()) {
|
||||
if(entry->metadata.load_begin + seconds(30) < system_clock::now()) {
|
||||
this->delete_song(entry->song_id); /* delete song because we cant load it */
|
||||
|
||||
return PlayableSong::create({
|
||||
entry->id,
|
||||
entry->url,
|
||||
entry->song_id,
|
||||
entry->original_url,
|
||||
entry->invoker
|
||||
}, MusicClient::failedLoader("Song failed to load"));
|
||||
}
|
||||
@ -80,28 +80,29 @@ std::shared_ptr<ts::music::PlayableSong> PlayablePlaylist::current_song(bool& aw
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if(!entry->load_future->succeeded() || !entry->load_future->getValue(nullptr)) {
|
||||
this->delete_song(entry->id); /* acquired playlist */
|
||||
if(!entry->metadata.is_loaded()) {
|
||||
assert(entry->metadata.has_failed_to_load());
|
||||
this->delete_song(entry->song_id); /* acquired playlist */
|
||||
|
||||
/* return promise */
|
||||
return PlayableSong::create({
|
||||
entry->id,
|
||||
entry->url,
|
||||
entry->song_id,
|
||||
entry->original_url,
|
||||
entry->invoker
|
||||
}, MusicClient::failedLoader(entry->load_future->errorMegssage()));
|
||||
}, MusicClient::failedLoader(entry->metadata.load_error));
|
||||
}
|
||||
|
||||
auto result = entry->load_future->getValue(nullptr);
|
||||
auto result = entry->metadata.loaded_data;
|
||||
assert(result); /* previous tested */
|
||||
|
||||
if(result->type != ::music::UrlType::TYPE_VIDEO && result->type != ::music::UrlType::TYPE_STREAM) {
|
||||
/* we cant replay that kind of entry. Skipping it */
|
||||
debugMessage(this->get_server_id(), "[PlayList] Tried to replay an invalid entry type (SongID: {}, Type: {})! Skipping song", entry->id, result->type);
|
||||
debugMessage(this->get_server_id(), "[PlayList] Tried to replay an invalid entry type (SongID: {}, Type: {})! Skipping song", entry->song_id, result->type);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return PlayableSong::create({
|
||||
entry->id,
|
||||
entry->song_id,
|
||||
result->url,
|
||||
entry->invoker
|
||||
}, MusicClient::providerLoader(entry->url_loader));
|
||||
@ -154,7 +155,7 @@ std::shared_ptr<PlaylistEntryInfo> PlayablePlaylist::playlist_next_entry() {
|
||||
}
|
||||
playlist_lock.unlock();
|
||||
if(current_song && this->properties()[property::PLAYLIST_FLAG_DELETE_PLAYED].as<bool>()) {
|
||||
this->delete_song(current_song->entry->id);
|
||||
this->delete_song(current_song->entry->song_id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -197,7 +198,7 @@ bool PlayablePlaylist::set_current_song(SongId song_id) {
|
||||
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;
|
||||
auto id = current_song ? current_song->entry->song_id : 0;
|
||||
if(this->properties()[property::PLAYLIST_CURRENT_SONG_ID].as<SongId>() != id)
|
||||
this->properties()[property::PLAYLIST_CURRENT_SONG_ID] = id;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user