diff --git a/.vscode/settings.json b/.vscode/settings.json index e238adf..852b34b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -69,6 +69,10 @@ "iomanip": "cpp", "iosfwd": "cpp", "limits": "cpp", - "numbers": "cpp" + "numbers": "cpp", + "cinttypes": "cpp", + "bitset": "cpp", + "set": "cpp", + "regex": "cpp" } } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index cf49b53..2b19778 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,8 @@ set(BOT_NAME "BumbleCee") project(${BOT_NAME}) aux_source_directory("src" coresrc) -add_executable(${BOT_NAME} ${coresrc}) +aux_source_directory("src/Commands" commands) +add_executable(${BOT_NAME} ${coresrc} ${commands}) string(ASCII 27 Esc) @@ -74,3 +75,5 @@ else() target_link_libraries(${BOT_NAME} dpp) endif() + +#set(CMAKE_CXX_FLAGS "-g") \ No newline at end of file diff --git a/audioplayback.ogg b/audioplayback.ogg deleted file mode 100644 index 1abef2c..0000000 Binary files a/audioplayback.ogg and /dev/null differ diff --git a/include/Bot.hpp b/include/Bot.hpp index 7474fbf..71cee16 100644 --- a/include/Bot.hpp +++ b/include/Bot.hpp @@ -1,5 +1,6 @@ -#pragma once -#include "CommandType.hpp" +#ifndef _BOT_HPP_ +#define _BOT_HPP_ +#include #include #include @@ -10,10 +11,12 @@ public: void AddCommand(ICommand &Command); void Start(); - std::shared_ptr bot; + std::shared_ptr BotCluster; protected: virtual void OnReady(const dpp::ready_t& event); virtual void OnCommand(const dpp::slashcommand_t& event); std::vector CommandsArray; -}; \ No newline at end of file +}; + +#endif \ No newline at end of file diff --git a/include/BumbleCeepp.hpp b/include/BumbleCeepp.hpp index 96521f4..0311a5e 100644 --- a/include/BumbleCeepp.hpp +++ b/include/BumbleCeepp.hpp @@ -1,7 +1,10 @@ -#pragma once -#include "Bot.hpp" +#ifndef _BUMBLECEEPP_HPP_ +#define _BUMBLECEEPP_HPP_ #include #include +#include +#include +#include class BumbleCeepp : public IBot { public: @@ -9,12 +12,19 @@ public: static BumbleCeepp Instance(Token); return &Instance; } - void enqueue(std::string); - std::string dequeue(); + void enqueue(struct FQueueElement Element); + + void QueuePlay(); + + uint32_t VoiceJoinedShardId; protected: - std::list MusicQueue; private: BumbleCeepp(std::string Token); void OnCommand(const dpp::slashcommand_t& Event); -}; \ No newline at end of file + + std::list MusicQueue; + std::mutex QueueMutex; +}; + +#endif \ No newline at end of file diff --git a/include/CommandType.hpp b/include/CommandType.hpp index b75a5c6..7f886c8 100644 --- a/include/CommandType.hpp +++ b/include/CommandType.hpp @@ -1,12 +1,16 @@ -#pragma once +#ifndef _COMMANDTYPE_HPP_ +#define _COMMANDTYPE_HPP_ #include #include #include +#include class ICommand { public: virtual void operator() (const dpp::slashcommand_t& Event) = 0; - virtual void operator() (std::list& MusicQueue, const dpp::slashcommand_t& Event) = 0; + virtual void operator() (std::list& MusicQueue, const dpp::slashcommand_t& Event) = 0; std::vector CommandObjectVector; -}; \ No newline at end of file +}; + +#endif \ No newline at end of file diff --git a/include/Commands.hpp b/include/Commands.hpp index 519dd09..49e47f0 100644 --- a/include/Commands.hpp +++ b/include/Commands.hpp @@ -1,4 +1,5 @@ -#pragma once -#include "Commands/Play.hpp" -#include "Commands/Queue.hpp" -#include "Commands/Join.hpp" \ No newline at end of file +#ifndef _COMMANDS_HPP_ +#define _COMMANDS_HPP_ +#include + +#endif \ No newline at end of file diff --git a/include/Commands/Join.hpp b/include/Commands/Join.hpp deleted file mode 100644 index 2fbbf37..0000000 --- a/include/Commands/Join.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once -#include "../CommandType.hpp" - -class Join : public ICommand { -public: - Join(dpp::snowflake Id); - - void operator()(const dpp::slashcommand_t& Event) {} - void operator()(std::list& MusicQueue, const dpp::slashcommand_t& Event); -}; \ No newline at end of file diff --git a/include/Commands/Play.hpp b/include/Commands/Play.hpp index 83dc5a4..d326a93 100644 --- a/include/Commands/Play.hpp +++ b/include/Commands/Play.hpp @@ -1,10 +1,17 @@ -#pragma once -#include "../CommandType.hpp" +#ifndef _PLAY_HPP_ +#define _PLAY_HPP_ +#include +#include +#include class Play : public ICommand { public: - Play(dpp::snowflake Id); + Play(std::shared_ptr Bot); void operator()(const dpp::slashcommand_t& Event) {} - void operator()(std::list& MusicQueue, const dpp::slashcommand_t& Event); -}; \ No newline at end of file + void operator()(std::list& MusicQueue, const dpp::slashcommand_t& Event); +private: + std::shared_ptr Bot; +}; + +#endif \ No newline at end of file diff --git a/include/Commands/Queue.hpp b/include/Commands/Queue.hpp index f1725e1..5c4c0ac 100644 --- a/include/Commands/Queue.hpp +++ b/include/Commands/Queue.hpp @@ -1,10 +1,17 @@ -#pragma once -#include "../CommandType.hpp" +#ifndef _QUEUE_HPP_ +#define _QUEUE_HPP_ +#include +#include +#include class Queue : public ICommand { public: - Queue(dpp::snowflake Id); + Queue(std::shared_ptr Bot); void operator()(const dpp::slashcommand_t& Event) {} - void operator()(std::list& MusicQueue, const dpp::slashcommand_t& Event); -}; \ No newline at end of file + void operator()(std::list& MusicQueue, const dpp::slashcommand_t& Event); +private: + std::shared_ptr Bot; +}; + +#endif \ No newline at end of file diff --git a/include/FQueueElement.hpp b/include/FQueueElement.hpp new file mode 100644 index 0000000..4211c2f --- /dev/null +++ b/include/FQueueElement.hpp @@ -0,0 +1,15 @@ +#ifndef _FQUEUEELEMENT_HPP_ +#define _FQUEUEELEMENT_HPP_ +#include +#include + +struct FQueueElement { + std::string title; + std::string description; + std::string FileName; + std::string thumbnail; + std::string duration; + dpp::snowflake guild_id; +}; + +#endif \ No newline at end of file diff --git a/src/Bot.cpp b/src/Bot.cpp index cac3389..7aec4cb 100644 --- a/src/Bot.cpp +++ b/src/Bot.cpp @@ -1,11 +1,11 @@ -#include "Bot.hpp" +#include IBot::IBot(std::string Token) { - bot = std::make_shared(Token); - bot->on_log(dpp::utility::cout_logger()); + BotCluster = std::make_shared(Token); + BotCluster->on_log(dpp::utility::cout_logger()); - bot->on_slashcommand([this](const dpp::slashcommand_t& Event) {OnCommand(Event);}); - bot->on_ready([this](const dpp::ready_t& Event) {OnReady(Event);}); + BotCluster->on_slashcommand([this](const dpp::slashcommand_t& Event) {OnCommand(Event);}); + BotCluster->on_ready([this](const dpp::ready_t& Event) {OnReady(Event);}); } void IBot::AddCommand(ICommand &Command) { @@ -13,18 +13,18 @@ void IBot::AddCommand(ICommand &Command) { } void IBot::Start() { - bot->start(dpp::st_wait); + BotCluster->start(dpp::st_wait); } void IBot::OnReady(const dpp::ready_t& Event) { if (!dpp::run_once()) return; - bot->global_bulk_command_delete(); + //bot->global_bulk_command_delete(); for (auto command : CommandsArray) { for (auto Alias : command->CommandObjectVector) { - bot->global_command_create(Alias); + BotCluster->global_command_create(Alias); } } } diff --git a/src/BumbleCeepp.cpp b/src/BumbleCeepp.cpp index a70b201..dcd733c 100644 --- a/src/BumbleCeepp.cpp +++ b/src/BumbleCeepp.cpp @@ -1,13 +1,158 @@ #include "BumbleCeepp.hpp" #include +#include +#include +#include BumbleCeepp::BumbleCeepp(std::string Token) : IBot(Token) { + VoiceJoinedShardId = 0; +} + +void BumbleCeepp::enqueue(struct FQueueElement Element) { + QueueMutex.lock(); + MusicQueue.push_back(Element); + QueueMutex.unlock(); +} + +void BumbleCeepp::QueuePlay(){ + dpp::discord_client* JoinedShared = BotCluster->get_shard(VoiceJoinedShardId); + if (!JoinedShared) { + return; + } + + while (!MusicQueue.empty()) { + QueueMutex.lock(); + FQueueElement Music = MusicQueue.front(); + MusicQueue.pop_front(); + QueueMutex.unlock(); + + dpp::voiceconn* v = JoinedShared->get_voice(Music.guild_id); + if (!v || !v->voiceclient || !v->voiceclient->is_ready()) { + return; + } + + ogg_sync_state oy; + ogg_stream_state os; + ogg_page og; + ogg_packet op; + OpusHead header; + char *buffer; + + FILE *fd; + + fd = fopen((std::string(Music.FileName.c_str()) + ".ogg").c_str(), "rb"); + if (!fd) { + continue; + } + + fseek(fd, 0L, SEEK_END); + size_t sz = ftell(fd); + rewind(fd); + + ogg_sync_init(&oy); + + buffer = ogg_sync_buffer(&oy, sz); + fread(buffer, 1, sz, fd); + + ogg_sync_wrote(&oy, sz); + + if (ogg_sync_pageout(&oy, &og) != 1) { + fprintf(stderr,"Does not appear to be ogg stream.\n"); + exit(1); + } + + ogg_stream_init(&os, ogg_page_serialno(&og)); + + if (ogg_stream_pagein(&os,&og) < 0) { + fprintf(stderr,"Error reading initial page of ogg stream.\n"); + exit(1); + } + + if (ogg_stream_packetout(&os,&op) != 1) { + fprintf(stderr,"Error reading header packet of ogg stream.\n"); + exit(1); + } + + /* We must ensure that the ogg stream actually contains opus data */ + if (!(op.bytes > 8 && !memcmp("OpusHead", op.packet, 8))) { + fprintf(stderr,"Not an ogg opus stream.\n"); + exit(1); + } + + /* Parse the header to get stream info */ + int err = opus_head_parse(&header, op.packet, op.bytes); + if (err) { + fprintf(stderr,"Not a ogg opus stream\n"); + exit(1); + } + + /* Now we ensure the encoding is correct for Discord */ + if (header.channel_count != 2 && header.input_sample_rate != 48000) { + fprintf(stderr,"Wrong encoding for Discord, must be 48000Hz sample rate with 2 channels.\n"); + exit(1); + } + + std::list OpusList; + + /* Now loop though all the pages and send the packets to the vc */ + while (ogg_sync_pageout(&oy, &og) == 1) { + ogg_stream_init(&os, ogg_page_serialno(&og)); + + if(ogg_stream_pagein(&os,&og)<0) { + fprintf(stderr,"Error reading page of Ogg bitstream data.\n"); + exit(1); + } + + while (ogg_stream_packetout(&os,&op) != 0) { + + /* Read remaining headers */ + if (op.bytes > 8 && !memcmp("OpusHead", op.packet, 8)) { + int err = opus_head_parse(&header, op.packet, op.bytes); + if (err) { + fprintf(stderr,"Not a ogg opus stream\n"); + exit(1); + } + + if (header.channel_count != 2 && header.input_sample_rate != 48000) { + fprintf(stderr,"Wrong encoding for Discord, must be 48000Hz sample rate with 2 channels.\n"); + exit(1); + } + + continue; + } + + /* Skip the opus tags */ + if (op.bytes > 8 && !memcmp("OpusTags", op.packet, 8)) + continue; + + ogg_packet D; + + memcpy(&D, &op, sizeof(ogg_packet)); + + OpusList.push_back(D); + //v->voiceclient->send_audio_opus(op.packet, op.bytes, samples / 48); + } + } + + while (!OpusList.empty()) { + ogg_packet D = OpusList.front(); + OpusList.pop_front(); + + v->voiceclient->send_audio_opus(D.packet, D.bytes); + } + + /* Cleanup */ + ogg_stream_clear(&os); + ogg_sync_clear(&oy); + system(("rm -f " + std::string(Music.FileName.c_str()) + ".ogg").c_str()); + } } void BumbleCeepp::OnCommand(const dpp::slashcommand_t& Event) { for (auto Command : CommandsArray) { for (auto Alias : Command->CommandObjectVector) { if (Event.command.get_command_name() == Alias.name) { + (*Command)(Event); (*Command)(MusicQueue, Event); } } diff --git a/src/Commands/Play.cpp b/src/Commands/Play.cpp new file mode 100644 index 0000000..86fba3a --- /dev/null +++ b/src/Commands/Play.cpp @@ -0,0 +1,86 @@ +#include +#include +#include +#include + +using json = nlohmann::json; + +Play::Play(std::shared_ptr Bot) { + this->Bot = Bot; + + dpp::slashcommand Command = dpp::slashcommand("play", "노래 재생", Bot->BotCluster->me.id); + + Command.add_option( + dpp::command_option(dpp::co_string, "query", "링크 또는 검색어", Bot->BotCluster->me.id) + ); + + dpp::slashcommand Alias = dpp::slashcommand("p", "노래 재생", Bot->BotCluster->me.id); + + Alias.add_option( + dpp::command_option(dpp::co_string, "query", "링크 또는 검색어", Bot->BotCluster->me.id) + ); + + CommandObjectVector.push_back(Command); + CommandObjectVector.push_back(Alias); +} + +void Play::operator()(std::list& MusicQueue, const dpp::slashcommand_t& Event) { + if (std::holds_alternative(Event.get_parameter("query"))) { + Event.reply("노래를 재생하려면 좀 노래를 넣고 재생시켜라 게이야"); + return; + } + + /* Attempt to connect to a voice channel, returns false if we fail to connect. */ + if (!dpp::find_guild(Event.command.guild_id)->connect_member_voice(Event.command.get_issuing_user().id)) { + return Event.reply("우리 게이는 도대체 노래 들을 생각도 없으면서 왜 신청하는거노?"); + } + + std::string Query = std::get(Event.get_parameter("query")); + + system(("./yt-dlp -o temp --write-info-json -f 251 " + Query).c_str()); + + json document; + std::ifstream infofile("temp.info.json"); + infofile >> document; + system("rm -f temp.info.json"); + + system(("ffmpeg -i temp -c copy " + std::string(to_string(document["id"])) + ".ogg").c_str()); + system("rm -f temp"); + + FQueueElement Data = {std::string(document["title"]), + std::string(document["uploader"]), + std::string(document["id"]), + std::string(document["thumbnail"]), + to_string(document["duration"]), + Event.command.guild_id}; + + Bot->enqueue(Data); + + dpp::message msg(Event.command.channel_id, "큐에 다음 곡을 추가했습니다:"); + + msg.add_embed(dpp::embed() + .set_color(dpp::colors::sti_blue) + .set_title(document["title"]) + .set_description(document["uploader"]) + .set_url(Query) + .set_image(document["thumbnail"]) + .add_field( + "길이", + to_string(document["duration"]), + true + )); + + Event.reply(msg); + + dpp::voiceconn* v = Event.from->get_voice(Event.command.guild_id); + + Bot->VoiceJoinedShardId = Event.from->shard_id; + + /* If the voice channel was invalid, or there is an issue with it, then tell the user. */ + if (v && v->voiceclient && v->voiceclient->is_ready()) { + return Bot->QueuePlay(); + } + + Bot->BotCluster->on_voice_ready([this](const dpp::voice_ready_t& Voice){ Bot->QueuePlay(); }); + return; +} \ No newline at end of file diff --git a/src/Commands/Queue.cpp b/src/Commands/Queue.cpp new file mode 100644 index 0000000..3cb0da4 --- /dev/null +++ b/src/Commands/Queue.cpp @@ -0,0 +1,32 @@ +#include +#include + +Queue::Queue(std::shared_ptr Bot) { + this->Bot = Bot; + + dpp::slashcommand Command = dpp::slashcommand("queue", "노래 예약 큐 확인", Bot->BotCluster->me.id); + dpp::slashcommand Alias = dpp::slashcommand("q", "노래 예약 큐 확인", Bot->BotCluster->me.id); + + CommandObjectVector.push_back(Command); + CommandObjectVector.push_back(Alias); +} + +void Queue::operator()(std::list& MusicQueue, const dpp::slashcommand_t& Event) { + dpp::embed embed = dpp::embed() + .set_color(dpp::colors::sti_blue) + .set_title("영상 제목&링크") + .set_url("https://dpp.dev/") + .set_description("영상 설명란") + .set_image("https://dpp.dev/DPP-Logo.png") + .set_footer( + dpp::embed_footer() + .set_text("업로더 이름") + .set_icon("https://dpp.dev/DPP-Logo.png") + ) + .set_timestamp(time(0)); + + dpp::message msg(Event.command.channel_id, "some Text"); + msg.add_embed(embed); + + Event.reply(msg); +} \ No newline at end of file diff --git a/src/Join.cpp b/src/Join.cpp deleted file mode 100644 index 15669dd..0000000 --- a/src/Join.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include -#include -#include - -Join::Join(dpp::snowflake Id) { - dpp::slashcommand Command = dpp::slashcommand("join", "asdf", Id); - - dpp::slashcommand Alias = dpp::slashcommand("j", "asdf", Id); - - CommandObjectVector.push_back(Command); - CommandObjectVector.push_back(Alias); -} - -void Join::operator()(std::list& MusicQueue, const dpp::slashcommand_t& Event) { - dpp::guild* g = dpp::find_guild(Event.command.guild_id); - - /* Attempt to connect to a voice channel, returns false if we fail to connect. */ - if (!g->connect_member_voice(Event.command.get_issuing_user().id)) { - Event.reply("You don't seem to be in a voice channel!"); - return; - } - - /* Tell the user we joined their channel. */ - Event.reply("Joined your channel!"); -} \ No newline at end of file diff --git a/src/Play.cpp b/src/Play.cpp deleted file mode 100644 index 214eef3..0000000 --- a/src/Play.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#include -#include -#include -#include -#include -#include - -Play::Play(dpp::snowflake Id) { - dpp::slashcommand Command = dpp::slashcommand("play", "노래 재생", Id); - - Command.add_option( - dpp::command_option(dpp::co_string, "query", "링크 또는 검색어", Id) - ); - - dpp::slashcommand Alias = dpp::slashcommand("p", "노래 재생", Id); - - Alias.add_option( - dpp::command_option(dpp::co_string, "query", "링크 또는 검색어", Id) - ); - - CommandObjectVector.push_back(Command); - CommandObjectVector.push_back(Alias); -} - -void Play::operator()(std::list& MusicQueue, const dpp::slashcommand_t& Event) { - if (std::holds_alternative(Event.get_parameter("query"))) { - /* Get the voice channel the bot is in, in this current guild. */ - dpp::voiceconn* v = Event.from->get_voice(Event.command.guild_id); - - /* If the voice channel was invalid, or there is an issue with it, then tell the user. */ - if (!v || !v->voiceclient || !v->voiceclient->is_ready()) { - Event.reply("There was an issue with getting the voice channel. Make sure I'm in a voice channel!"); - return; - } - - ogg_sync_state oy; - ogg_stream_state os; - ogg_page og; - ogg_packet op; - OpusHead header; - char *buffer; - - FILE *fd; - - fd = fopen("../audioplayback.ogg", "rb"); - - fseek(fd, 0L, SEEK_END); - size_t sz = ftell(fd); - rewind(fd); - - ogg_sync_init(&oy); - - buffer = ogg_sync_buffer(&oy, sz); - fread(buffer, 1, sz, fd); - - ogg_sync_wrote(&oy, sz); - - if (ogg_sync_pageout(&oy, &og) != 1) { - fprintf(stderr,"Does not appear to be ogg stream.\n"); - exit(1); - } - - ogg_stream_init(&os, ogg_page_serialno(&og)); - - if (ogg_stream_pagein(&os,&og) < 0) { - fprintf(stderr,"Error reading initial page of ogg stream.\n"); - exit(1); - } - - if (ogg_stream_packetout(&os,&op) != 1) { - fprintf(stderr,"Error reading header packet of ogg stream.\n"); - exit(1); - } - - /* We must ensure that the ogg stream actually contains opus data */ - if (!(op.bytes > 8 && !memcmp("OpusHead", op.packet, 8))) { - fprintf(stderr,"Not an ogg opus stream.\n"); - exit(1); - } - - /* Parse the header to get stream info */ - int err = opus_head_parse(&header, op.packet, op.bytes); - if (err) { - fprintf(stderr,"Not a ogg opus stream\n"); - exit(1); - } - - /* Now we ensure the encoding is correct for Discord */ - if (header.channel_count != 2 && header.input_sample_rate != 48000) { - fprintf(stderr,"Wrong encoding for Discord, must be 48000Hz sample rate with 2 channels.\n"); - exit(1); - } - - /* Now loop though all the pages and send the packets to the vc */ - while (ogg_sync_pageout(&oy, &og) == 1) { - ogg_stream_init(&os, ogg_page_serialno(&og)); - - if(ogg_stream_pagein(&os,&og)<0) { - fprintf(stderr,"Error reading page of Ogg bitstream data.\n"); - exit(1); - } - - while (ogg_stream_packetout(&os,&op) != 0) { - - /* Read remaining headers */ - if (op.bytes > 8 && !memcmp("OpusHead", op.packet, 8)) { - int err = opus_head_parse(&header, op.packet, op.bytes); - if (err) { - fprintf(stderr,"Not a ogg opus stream\n"); - exit(1); - } - - if (header.channel_count != 2 && header.input_sample_rate != 48000) { - fprintf(stderr,"Wrong encoding for Discord, must be 48000Hz sample rate with 2 channels.\n"); - exit(1); - } - - continue; - } - - /* Skip the opus tags */ - if (op.bytes > 8 && !memcmp("OpusTags", op.packet, 8)) - continue; - - /* Send the audio */ - int samples = opus_packet_get_samples_per_frame(op.packet, 48000); - - v->voiceclient->send_audio_opus(op.packet, op.bytes, samples / 48); - } - } - - /* Cleanup */ - ogg_stream_clear(&os); - ogg_sync_clear(&oy); - - Event.reply("Finished playing the audio file!"); - } -} \ No newline at end of file diff --git a/src/Queue.cpp b/src/Queue.cpp deleted file mode 100644 index 87a9ae0..0000000 --- a/src/Queue.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include - -Queue::Queue(dpp::snowflake Id) { - dpp::slashcommand Command = dpp::slashcommand("queue", "노래 예약 큐 확인", Id); - dpp::slashcommand Alias = dpp::slashcommand("q", "노래 예약 큐 확인", Id); - - CommandObjectVector.push_back(Command); - CommandObjectVector.push_back(Alias); -} - -void Queue::operator()(std::list& MusicQueue, const dpp::slashcommand_t& Event) { - -} \ No newline at end of file diff --git a/src/i7p39xR0BHQ.ogg b/src/i7p39xR0BHQ.ogg new file mode 100644 index 0000000..5ae5412 Binary files /dev/null and b/src/i7p39xR0BHQ.ogg differ diff --git a/src/main.cpp b/src/main.cpp index 96828cf..1847ccf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,4 @@ #include -#include -#include #include #include @@ -11,15 +9,11 @@ int main() { std::ifstream configfile("../config.json"); configfile >> configdocument; - std::unique_ptr BumbleBee(BumbleCeepp::GetInstance(configdocument["token"])); + std::shared_ptr BumbleBee(BumbleCeepp::GetInstance(configdocument["token"])); - Play Command1(BumbleBee->bot->me.id); - Queue Command2(BumbleBee->bot->me.id); - Join Command3(BumbleBee->bot->me.id); + Play Command1(BumbleBee); BumbleBee->AddCommand(Command1); - BumbleBee->AddCommand(Command2); - BumbleBee->AddCommand(Command3); BumbleBee->Start(); diff --git a/src/yt-dlp b/src/yt-dlp new file mode 100755 index 0000000..04d25da Binary files /dev/null and b/src/yt-dlp differ