diff --git a/.gitignore b/.gitignore index 0add645..e5eccb0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ Temp/ config.json Music *.info.json -yt-dlp \ No newline at end of file +yt-dlp +ffmpeg \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index a94a6f3..1558cba 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -77,6 +77,26 @@ "typeinfo": "cpp", "valarray": "cpp", "variant": "cpp", - "*.ipp": "cpp" + "*.ipp": "cpp", + "format": "cpp", + "span": "cpp", + "__bit_reference": "cpp", + "__bits": "cpp", + "__config": "cpp", + "__debug": "cpp", + "__errc": "cpp", + "__hash_table": "cpp", + "__locale": "cpp", + "__mutex_base": "cpp", + "__node_handle": "cpp", + "__nullptr": "cpp", + "__split_buffer": "cpp", + "__string": "cpp", + "__threading_support": "cpp", + "__tree": "cpp", + "__tuple": "cpp", + "ios": "cpp", + "locale": "cpp", + "queue": "cpp" } } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 7965a91..6fa2f31 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,8 @@ set(BOT_NAME "BumbleCee") project(${BOT_NAME}) aux_source_directory("src" coresrc) aux_source_directory("src/Commands" commands) -add_executable(${BOT_NAME} ${coresrc} ${commands}) +aux_source_directory("src/Settings" settings) +add_executable(${BOT_NAME} ${coresrc} ${commands} ${settings}) string(ASCII 27 Esc) diff --git a/include/AsyncDownloadManager.hpp b/include/AsyncDownloadManager.hpp index 543902f..43d567c 100644 --- a/include/AsyncDownloadManager.hpp +++ b/include/AsyncDownloadManager.hpp @@ -5,30 +5,28 @@ #include #include #include -#include #include #include #define WORKER_COUNT 5 namespace bumbleBee { +/// @brief 싱글톤 멀티스레딩 다운로드 매니저 class AsyncDownloadManager { public: - static AsyncDownloadManager& getInstance(int worker_count, std::weak_ptr bot, std::shared_ptr musicQueue) { + static AsyncDownloadManager& getInstance(int worker_count, std::weak_ptr weak_cluster, std::shared_ptr musicQueue) { static AsyncDownloadManager dl(worker_count); - dl.bot = bot; + dl.weak_cluster = weak_cluster; dl.musicQueue = musicQueue; return dl; } - void enqueue(std::string query) { + void enqueue(std::pair query) { std::thread th(&bumbleBee::AsyncDownloadManager::enqueueAsyncDL, this, query); th.detach(); } - - ~AsyncDownloadManager(){ - auto cluster = bot.lock(); - assert(cluster); - cluster->log(dpp::ll_info, "AsyncDownloadManager Destructor called."); + void stop() { + auto cluster = weak_cluster.lock(); + cluster->log(dpp::ll_info, "AsyncDownloadManager stop/destructor called."); terminate = true; dlQueueCondition.notify_all(); @@ -37,6 +35,10 @@ public: } } + ~AsyncDownloadManager(){ + stop(); + } + private: AsyncDownloadManager(int worker_count){ worker_thread.reserve(worker_count); @@ -46,13 +48,13 @@ private: } } - void enqueueAsyncDL(std::string query); + void enqueueAsyncDL(std::pair query); void downloadWorker(); - std::queue downloadQueue; + std::queue> downloadQueue; std::condition_variable dlQueueCondition; std::mutex dlQueueMutex; - std::weak_ptr bot; + std::weak_ptr weak_cluster; std::shared_ptr musicQueue; std::vector worker_thread; bool terminate; diff --git a/include/BumbleBee.hpp b/include/BumbleBee.hpp index 4dd8859..3a93623 100644 --- a/include/BumbleBee.hpp +++ b/include/BumbleBee.hpp @@ -18,9 +18,8 @@ public: /** * @fn BumbleBee(nlohmann::json settings) * @brief 생성자 - * @param settings 설정 json **/ - BumbleBee(nlohmann::json settings); + BumbleBee(); /** * @fn ~BumbleBee() * @brief 파괴자 diff --git a/include/BumbleBeeCommand.hpp b/include/BumbleBeeCommand.hpp index db6da76..d470ae1 100644 --- a/include/BumbleBeeCommand.hpp +++ b/include/BumbleBeeCommand.hpp @@ -7,15 +7,19 @@ namespace bumbleBee::commands { class ICommand { public: - /// @brief 기본 생성자 - /// @param botID 명령어 등록을 위한 봇 아이디 - /// @param queue 음악이 저장되어 있는 큐 + /** + * @brief 기본 생성자 + * @param botID 명령어 등록을 위한 봇 아이디 + * @param queue 음악이 저장되어 있는 큐 + **/ ICommand(dpp::snowflake botID, std::weak_ptr queue) { this->botID = botID; this->queue = queue; } - /// @brief 명령어 호출 시에 콜백되는 함수 - /// @param event + /** + * @brief 명령어 호출 시에 콜백될 메소드 + * @param event dpp::slashcommand_t + **/ virtual void operator()(const dpp::slashcommand_t &event){}; /// @brief 봇 명령어들의 이름과 별명들을 저장하는 벡터 @@ -26,11 +30,19 @@ private: dpp::snowflake botID; /// @brief 음악 큐에 대한 약한 포인터 std::weak_ptr queue; + + /** + * @brief 명령어 별명 추가 + * @param name 명령어 이름 + * @param description 명령어 설명 + **/ + void addCommandAliase(std::string name, std::string description) { + nameAndAliases.push_back(dpp::slashcommand(name, description, botID)); + } }; } /** - * @fn _DECLARE_BUMBLEBEE_COMMAND_one_PARAM_one_ALIAS(name, alias, description, option_name, option_desc) * @brief 명령어 인자가 없는 명령어의 boilerplate 대체 매크로 * @param name 명령어 이름 및 클래스명 * @param alias 명령어 별명 @@ -52,7 +64,6 @@ public: \ } /** - * @fn _DECLARE_BUMBLEBEE_COMMAND_one_PARAM_one_ALIAS(name, alias, description, option_name, option_desc) * @brief 명령어 인자를 하나 갖는 명령어의 boilerplate 대체 매크로 * @param name 명령어 이름 및 클래스명 * @param alias 명령어 별명 diff --git a/include/MusicPlayManager.hpp b/include/MusicPlayManager.hpp new file mode 100644 index 0000000..978bb5f --- /dev/null +++ b/include/MusicPlayManager.hpp @@ -0,0 +1,16 @@ +#pragma once +#ifndef _MUSICPLAYMANAGER_HPP_ +#define _MUSICPLAYMANAGER_HPP_ +#include + +namespace BumbleBee { +class MusicPlayManager { +public: + MusicPlayManager(std::shared_ptr cluster) { + this->cluster = cluster; + } +private: + std::shared_ptr cluster; +}; +} +#endif \ No newline at end of file diff --git a/include/MusicQueue.hpp b/include/MusicQueue.hpp index fb03468..dc08f4f 100644 --- a/include/MusicQueue.hpp +++ b/include/MusicQueue.hpp @@ -5,14 +5,23 @@ #include #include #include +#include namespace bumbleBee { class MusicQueueElement { public: - MusicQueueElement(std::string id, std::string url) : id(id), url(url) {} + MusicQueueElement( + dpp::message originalResponse, + std::string id, + std::string url, + FILE* stream) : + + originalResponse(originalResponse), id(id), url(url), stream(stream) {} + dpp::message originalResponse; std::string id; std::string url; + FILE* stream; }; class MusicQueue { @@ -23,6 +32,7 @@ public: } void enqueue(std::shared_ptr Element); std::shared_ptr dequeue(); + std::shared_ptr findById(std::string id); std::weak_ptr nowplaying(); std::weak_ptr next_music(); std::weak_ptr jump_to_index(int idx); diff --git a/include/Settings/SettingsManager.hpp b/include/Settings/SettingsManager.hpp new file mode 100644 index 0000000..7728c9d --- /dev/null +++ b/include/Settings/SettingsManager.hpp @@ -0,0 +1,30 @@ +#pragma once +#ifndef _SETTINGSMANAGER_HPP_ +#define _SETTINGSMANAGER_HPP_ +#include +#include +#include + +namespace bumbleBee { +class settingsManager { +public: + /// @brief 봇 토큰 + static std::string TOKEN; + /// @brief yt-dlp 실행 명령어 + static std::string YTDLP_CMD; + /// @brief ffmpeg 실행 명령어 + static std::string FFMPEG_CMD; + /// @brief db접속 url + static std::string DBURL; + /// @brief db접속 유저 + static std::string DBUSER; + /// @brief db접속 비밀번호 + static std::string DBPASSWORD; + + + static void load(); + + static void saveToFile(); +}; +} +#endif \ No newline at end of file diff --git a/include/Utils/ConsoleUtils.hpp b/include/Utils/ConsoleUtils.hpp new file mode 100644 index 0000000..0f5140a --- /dev/null +++ b/include/Utils/ConsoleUtils.hpp @@ -0,0 +1,40 @@ +#pragma once +#ifndef _CONSOLEUTILS_HPP_ +#define _CONSOLEUTILS_HPP_ +#include +#include +#include + +namespace bumbleBee { +class ConsoleUtils { +public: + /** + * @brief 명령어를 쉘에서 실행하고 결과를 EOF 전까지 읽어 \n을 구분자로 토큰화하여 반환합니다 + * @param cmd 실행할 명령 + * @return std::queue tokens + */ + static std::queue getResultFromCommand(std::string cmd) { + std::string result, token; + std::queue tokens; + FILE* stream; + const int maxBuffer = 12; // 적당한 크기 + char buffer[maxBuffer]; + cmd.append(" 2>&1"); // 표준에러를 표준출력으로 redirect + + stream = popen(cmd.c_str(), "r"); // 주어진 command를 shell로 실행하고 파이프 연결 (fd 반환) + if (stream) { + while (fgets(buffer, maxBuffer, stream) != NULL) result.append(buffer); // fgets: fd (stream)를 길이 (maxBuffer)만큼 읽어 버퍼 (buffer)에 저장 + pclose(stream); + } + + std::stringstream ss(result); + while (std::getline(ss, token, '\n')) { + tokens.push(token); + } + + return tokens; + } +}; +} + +#endif \ No newline at end of file diff --git a/include/Utils/VersionsCheckUtils.hpp b/include/Utils/VersionsCheckUtils.hpp new file mode 100644 index 0000000..1cb6081 --- /dev/null +++ b/include/Utils/VersionsCheckUtils.hpp @@ -0,0 +1,75 @@ +#pragma once +#ifndef _VERSIONCHECKUTILS_HPP_ +#define _VERSIONCHECKUTILS_HPP_ +#include +#include "ConsoleUtils.hpp" +#include "../Settings/SettingsManager.hpp" + +namespace bumbleBee { +class VersionsCheckUtils { +public: + static bool isThereCMD(std::shared_ptr cluster, std::string cmd) { + if (ConsoleUtils::getResultFromCommand("which " + cmd).size() == 0) { + cluster->log(dpp::ll_error, cmd + " is unavaliable. unresolable error please install " + cmd); + return false; + } + else + return true; + } + + static void validateYTDLPFFMPEGBinary(std::shared_ptr cluster) { + cluster->log(dpp::ll_info, "Checking if yt-dlp and ffmpeg is available..."); + std::queue result = ConsoleUtils::getResultFromCommand(settingsManager::FFMPEG_CMD + " -version"); + std::string front = result.front(); + if (front[0] != 'f' || + front[1] != 'f' || + front[2] != 'm' || + front[3] != 'p' || + front[4] != 'e' || + front[5] != 'g') { + cluster->log(dpp::ll_warning, "ffmpeg is unavailable. downloading ffmpeg..."); + + if (!isThereCMD(cluster, "curl")) { + exit(1); + } + if (!isThereCMD(cluster, "tar")) { + exit(1); + } + + system("curl -LO https://github.com/BtbN/FFmpeg-Builds/releases/latest/download/ffmpeg-master-latest-linux64-gpl.tar.xz"); + system("tar -xf ffmpeg-master-latest-linux64-gpl.tar.xz"); + system("rm ffmpeg-master-latest-linux64-gpl.tar.xz"); + system("mv ffmpeg-master-latest-linux64-gpl ffmpeg"); + } + result = ConsoleUtils::getResultFromCommand(settingsManager::YTDLP_CMD + " --version"); + front = result.front(); + if ((front[0]-'0' < 0 || front[0]-'0' > 9) || + (front[1]-'0' < 0 || front[1]-'0' > 9) || + (front[2]-'0' < 0 || front[2]-'0' > 9) || + (front[3]-'0' < 0 || front[3]-'0' > 9)) { + cluster->log(dpp::ll_warning, "ytdlp is unavailable. downloading ytdlp..."); + + if (!isThereCMD(cluster, "curl")) { + exit(1); + } + if (!isThereCMD(cluster, "tar")) { + exit(1); + } + + system("curl -LO https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp"); + system("chmod +x ./yt-dlp"); + } + } + + static void updateytdlp(std::shared_ptr cluster) { + cluster->log(dpp::ll_info, "Checking if yt-dlp update is available..."); + std::queue result = ConsoleUtils::getResultFromCommand("./yt-dlp -U"); + while(!result.empty()) { + std::string front = result.front(); + result.pop(); + cluster->log(dpp::ll_info, front); + } + } +}; +} +#endif \ No newline at end of file diff --git a/src/AsyncDownloadManager.cpp b/src/AsyncDownloadManager.cpp index 4af229b..a9bd593 100644 --- a/src/AsyncDownloadManager.cpp +++ b/src/AsyncDownloadManager.cpp @@ -1,36 +1,20 @@ #include #include +#include "Utils/ConsoleUtils.hpp" +#include "Settings/SettingsManager.hpp" +#include +#include +#include +#include namespace bumbleBee { -void AsyncDownloadManager::enqueueAsyncDL(std::string query) { +void AsyncDownloadManager::enqueueAsyncDL(std::pair query) { std::lock_guard lock(dlQueueMutex); downloadQueue.push(query); dlQueueCondition.notify_one(); } -std::queue getResultFromCommand(std::string cmd) { - std::string result, token; - std::queue tokens; - FILE* stream; - const int maxBuffer = 12; // 버퍼의 크기는 적당하게 - char buffer[maxBuffer]; - cmd.append(" 2>&1"); // 표준에러를 표준출력으로 redirect - - stream = popen(cmd.c_str(), "r"); // 주어진 command를 shell로 실행하고 파이프 연결 (fd 반환) - if (stream) { - while (fgets(buffer, maxBuffer, stream) != NULL) result.append(buffer); // fgets: fd (stream)를 길이 (maxBuffer)만큼 읽어 버퍼 (buffer)에 저장 - pclose(stream); - } - - std::stringstream ss(result); - while (std::getline(ss, token, '\n')) { - tokens.push(token); - } - - return tokens; -} - void AsyncDownloadManager::downloadWorker() { std::ostringstream tid; tid << std::this_thread::get_id(); @@ -38,38 +22,49 @@ void AsyncDownloadManager::downloadWorker() { //mutex lock std::unique_lock dlQueueLock(dlQueueMutex); dlQueueCondition.wait(dlQueueLock, [&]{ return !downloadQueue.empty() || terminate; }); - auto cluster = bot.lock(); - assert(cluster); - if (terminate) { - cluster->log(dpp::ll_info, "Terminating Thread" + tid.str()); + auto cluster = weak_cluster.lock(); + if (weak_cluster.expired()) { + cluster->log(dpp::ll_error, "Missing cluster, terminating thread " + tid.str()); break; } - std::string query = downloadQueue.front(); + if (terminate) { + cluster->log(dpp::ll_info, "Terminating thread " + tid.str()); + break; + } + std::string query = downloadQueue.front().first; + dpp::message oRes = downloadQueue.front().second; downloadQueue.pop(); dlQueueLock.unlock(); - cluster->log(dpp::ll_info, "Enqueuing " + query + " accepted."); + cluster->log(dpp::ll_info, "AsyncDownloadManager: " + query + " accepted."); - std::queue ids = getResultFromCommand("./yt-dlp --default-search ytsearch --flat-playlist --skip-download --quiet --ignore-errors --print id " + query); + std::queue ids = + ConsoleUtils::getResultFromCommand(settingsManager::YTDLP_CMD + + " --default-search ytsearch --flat-playlist --skip-download --quiet --ignore-errors --print id " + query); if (ids.size() >= 2) { + cluster->log(dpp::ll_info, query + " is playlist"); while (!ids.empty()) { if (ids.front() == "") { ids.pop(); continue; } - cluster->log(dpp::ll_info, "Enqueuing " + ids.front()); - enqueue("https://youtu.be/" + ids.front()); + cluster->log(dpp::ll_info, "Enqueuing playlist element " + ids.front()); + enqueue(std::make_pair("https://youtu.be/" + ids.front(), oRes)); ids.pop(); } break; } - std::queue urls = getResultFromCommand("./yt-dlp -f ba* --print urls https://youtu.be/" + ids.front()); + std::queue urls = + ConsoleUtils::getResultFromCommand(settingsManager::YTDLP_CMD + + " -f ba* --print urls https://youtu.be/" + ids.front()); - cluster->log(dpp::ll_info, "url: " + urls.front()); + cluster->log(dpp::ll_debug, "url: " + urls.front()); - musicQueue->enqueue(std::make_shared(ids.front(), urls.front())); + FILE* stream; + + musicQueue->enqueue(std::make_shared(oRes, ids.front(), urls.front(), stream)); std::string downloadID = ids.front(); @@ -81,13 +76,12 @@ void AsyncDownloadManager::downloadWorker() { cluster->log(dpp::ll_info, "Thread id: " + tid.str() + ": " + downloadID + " accepted."); - system(("./yt-dlp -o \"Temp/%(id)s\" --quiet --ignore-errors -f ba* https://youtu.be/" + downloadID).c_str()); + std::string command = std::string("./streamAndDownload.sh " + settingsManager::YTDLP_CMD + " " + downloadID + " " + settingsManager::FFMPEG_CMD); + stream = popen(command.c_str(), "r"); + pclose(stream); + stream = NULL; - system((std::string() + "yes n 2>/dev/null | ffmpeg -hide_banner -loglevel error -i \"" - + "Temp/" + downloadID + "\" -acodec libopus -vn Music/" + downloadID + ".ogg").c_str()); - system((std::string() + "rm -f Temp/" + downloadID).c_str()); - - cluster->log(dpp::ll_info, "Thread id: " + tid.str() + ": " + downloadID + " downloaded."); + cluster->log(dpp::ll_info, "Thread id: " + tid.str() + ": " + downloadID + " download complete."); }); th.detach(); } diff --git a/src/BumbleBee.cpp b/src/BumbleBee.cpp index ff5d06b..ea1eacd 100644 --- a/src/BumbleBee.cpp +++ b/src/BumbleBee.cpp @@ -1,13 +1,17 @@ #include +#include +#include +#include namespace bumbleBee{ -BumbleBee::BumbleBee(nlohmann::json settings) { - this->cluster = std::make_shared(settings["token"]); +BumbleBee::BumbleBee() { + settingsManager::load(); + this->cluster = std::make_shared(settingsManager::TOKEN); dbDriver = sql::mariadb::get_driver_instance(); - this->dbURL = std::make_shared(settings["dbURL"]); + this->dbURL = std::make_shared(settingsManager::DBURL); sql::Properties pro({ - {"user", std::string(settings["user"])}, - {"password", std::string(settings["password"])} + {"user", std::string(settingsManager::DBUSER)}, + {"password", std::string(settingsManager::DBPASSWORD)} }); this->dbProperties = std::make_shared(pro); @@ -16,6 +20,9 @@ BumbleBee::BumbleBee(nlohmann::json settings) { cluster->on_ready([this](const dpp::ready_t &event){on_ready(event);}); queue = std::make_shared(); + + VersionsCheckUtils::validateYTDLPFFMPEGBinary(cluster); + VersionsCheckUtils::updateytdlp(cluster); } void BumbleBee::start() { this->cluster->start(dpp::st_wait); } diff --git a/src/MusicQueue.cpp b/src/MusicQueue.cpp index 6e29d33..b288cdd 100644 --- a/src/MusicQueue.cpp +++ b/src/MusicQueue.cpp @@ -14,7 +14,17 @@ std::shared_ptr MusicQueue::dequeue() { queue.pop_front(); return value; } -std::weak_ptr MusicQueue::nowplaying() { +std::shared_ptr MusicQueue::findById(std::string id) { + std::lock_guard lock(queueMutex); + int index = 0; + for (auto iter = queue.begin(); iter != queue.end(); iter++) { + if ((*iter).get()->id == id) + return *iter; + } + return std::shared_ptr(); +} +std::weak_ptr MusicQueue::nowplaying() +{ return *currentPlayingPosition; } std::weak_ptr MusicQueue::next_music() { @@ -38,7 +48,6 @@ std::weak_ptr MusicQueue::jump_to_index(int idx) { return *iter; } } - std::shared_ptr empty; - return empty; + return std::shared_ptr(); } } \ No newline at end of file diff --git a/src/Settings/SettingsManager.cpp b/src/Settings/SettingsManager.cpp new file mode 100644 index 0000000..fa6b4e0 --- /dev/null +++ b/src/Settings/SettingsManager.cpp @@ -0,0 +1,67 @@ +#include + +namespace bumbleBee { + +std::string settingsManager::TOKEN = ""; +std::string settingsManager::YTDLP_CMD = "./yt-dlp"; +std::string settingsManager::FFMPEG_CMD = "./ffmpeg/bin/ffmpeg"; +std::string settingsManager::DBURL = ""; +std::string settingsManager::DBUSER = ""; +std::string settingsManager::DBPASSWORD = ""; + +void settingsManager::load() { + nlohmann::json configdocument; + try { + std::ifstream configfile("config.json"); + configfile >> configdocument; + + TOKEN = configdocument["TOKEN"]; + YTDLP_CMD = configdocument["YTDLP_CMD"]; + FFMPEG_CMD = configdocument["FFMPEG_CMD"]; + DBURL = configdocument["DBURL"]; + DBUSER = configdocument["DBUSER"]; + DBPASSWORD = configdocument["DBPASSWORD"]; + } + catch (const nlohmann::json::type_error& e) { + saveToFile(); + std::ifstream configfile("config.json"); + configfile >> configdocument; + + TOKEN = configdocument["TOKEN"]; + YTDLP_CMD = configdocument["YTDLP_CMD"]; + FFMPEG_CMD = configdocument["FFMPEG_CMD"]; + DBURL = configdocument["DBURL"]; + DBUSER = configdocument["DBUSER"]; + DBPASSWORD = configdocument["DBPASSWORD"]; + } + catch (const nlohmann::json::parse_error& e) { + saveToFile(); + std::ifstream configfile("config.json"); + configfile >> configdocument; + + TOKEN = configdocument["TOKEN"]; + YTDLP_CMD = configdocument["YTDLP_CMD"]; + FFMPEG_CMD = configdocument["FFMPEG_CMD"]; + DBURL = configdocument["DBURL"]; + DBUSER = configdocument["DBUSER"]; + DBPASSWORD = configdocument["DBPASSWORD"]; + } +} + +void settingsManager::saveToFile() { + nlohmann::json configdocument; + + configdocument["TOKEN"] = TOKEN; + configdocument["YTDLP_CMD"] = YTDLP_CMD; + configdocument["FFMPEG_CMD"] = FFMPEG_CMD; + configdocument["DBURL"] = DBURL; + configdocument["DBUSER"] = DBUSER; + configdocument["DBPASSWORD"] = DBPASSWORD; + + std::ofstream configFile("config.json"); + if (configFile.is_open()) { + configFile << configdocument.dump(4); + configFile.close(); + } +} +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 735ce8d..629bff2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,17 +4,14 @@ #include int main() { - nlohmann::json configdocument; - std::ifstream configfile("config.json"); - configfile >> configdocument; - - bumbleBee::BumbleBee bot(configdocument); + bumbleBee::BumbleBee bot; bumbleBee::AsyncDownloadManager& manager = bumbleBee::AsyncDownloadManager::getInstance(5, bot.cluster, bot.queue); - manager.enqueue("https://music.youtube.com/playlist?list=PL5NSTAfQ-wQBqZYMTqxADemyUW8mxJq2h&si=vFV4jlm70kxGfKNa"); + manager.enqueue(std::make_pair("https://music.youtube.com/watch?v=4NnqIu_v1QA&si=buZP2UwzQtJLENmb", nullptr)); - std::thread th([](){sleep(11);}); + std::thread th([](){sleep(60);}); th.join(); + manager.stop(); std::cout << "\n\n\n\n\nend\n\n\n\n\n\n\n"; //bot.start(); diff --git a/streamAndDownload.sh b/streamAndDownload.sh new file mode 100755 index 0000000..ae5cef7 --- /dev/null +++ b/streamAndDownload.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +YTDLP_CMD=$1 +DOWNLOAD_ID=$2 +FFMPEG_CMD=$3 + +$YTDLP_CMD -o - --quiet --ignore-errors -f bestaudio https://youtu.be/$DOWNLOAD_ID | \ +$FFMPEG_CMD -i - -hide_banner -ar 48000 -ac 2 -c:a libopus -f ogg - \ No newline at end of file diff --git a/test.ogg b/test.ogg new file mode 100644 index 0000000..e69de29 diff --git a/yt-dlp b/yt-dlp index 9c57ab4..fde5494 100755 Binary files a/yt-dlp and b/yt-dlp differ