diff --git a/.vscode/settings.json b/.vscode/settings.json index 9ae7d58..a94a6f3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -76,6 +76,7 @@ "typeindex": "cpp", "typeinfo": "cpp", "valarray": "cpp", - "variant": "cpp" + "variant": "cpp", + "*.ipp": "cpp" } } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 1667003..7965a91 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,6 @@ set_target_properties(${BOT_NAME} PROPERTIES set(THREADS_PREFER_PTHREAD_FLAG TRUE) find_package(Threads REQUIRED) find_package(DPP) -find_package(VLC) if(APPLE) if(CMAKE_APPLE_SILICON_PROCESSOR) set(OPENSSL_ROOT_DIR "/opt/homebrew/opt/openssl") diff --git a/include/AsyncDownloadManager.hpp b/include/AsyncDownloadManager.hpp index 094031d..543902f 100644 --- a/include/AsyncDownloadManager.hpp +++ b/include/AsyncDownloadManager.hpp @@ -14,7 +14,7 @@ namespace bumbleBee { class AsyncDownloadManager { public: - static AsyncDownloadManager& getInstance(int worker_count, std::weak_ptr bot, std::weak_ptr musicQueue) { + static AsyncDownloadManager& getInstance(int worker_count, std::weak_ptr bot, std::shared_ptr musicQueue) { static AsyncDownloadManager dl(worker_count); dl.bot = bot; dl.musicQueue = musicQueue; @@ -24,15 +24,11 @@ public: std::thread th(&bumbleBee::AsyncDownloadManager::enqueueAsyncDL, this, query); th.detach(); } -private: - AsyncDownloadManager(int worker_count){ - worker_thread.reserve(worker_count); - terminate = false; - for (int i=0; idownloadWorker();}); - } - } + ~AsyncDownloadManager(){ + auto cluster = bot.lock(); + assert(cluster); + cluster->log(dpp::ll_info, "AsyncDownloadManager Destructor called."); terminate = true; dlQueueCondition.notify_all(); @@ -41,6 +37,15 @@ private: } } +private: + AsyncDownloadManager(int worker_count){ + worker_thread.reserve(worker_count); + terminate = false; + for (int i=0; idownloadWorker();}); + } + } + void enqueueAsyncDL(std::string query); void downloadWorker(); @@ -48,7 +53,7 @@ private: std::condition_variable dlQueueCondition; std::mutex dlQueueMutex; std::weak_ptr bot; - std::weak_ptr musicQueue; + std::shared_ptr musicQueue; std::vector worker_thread; bool terminate; }; diff --git a/include/BumbleBee.hpp b/include/BumbleBee.hpp index a587390..4dd8859 100644 --- a/include/BumbleBee.hpp +++ b/include/BumbleBee.hpp @@ -3,7 +3,6 @@ #define _BUMBLEBEE_HPP_ #include #include -#include #include #include #include diff --git a/include/MusicQueue.hpp b/include/MusicQueue.hpp index c35e151..fb03468 100644 --- a/include/MusicQueue.hpp +++ b/include/MusicQueue.hpp @@ -1,10 +1,39 @@ #pragma once #ifndef _MUSICQUEUE_HPP_ #define _MUSICQUEUE_HPP_ +#include +#include +#include +#include namespace bumbleBee { + +class MusicQueueElement { +public: + MusicQueueElement(std::string id, std::string url) : id(id), url(url) {} + std::string id; + std::string url; +}; + class MusicQueue { public: + MusicQueue() { + currentPlayingPosition = queue.begin(); + repeat = false; + } + void enqueue(std::shared_ptr Element); + std::shared_ptr dequeue(); + std::weak_ptr nowplaying(); + std::weak_ptr next_music(); + std::weak_ptr jump_to_index(int idx); + + bool repeat; + std::function on_queue_added; +private: + std::condition_variable queueItemAdded; + std::mutex queueMutex; + std::list> queue; + std::list>::iterator currentPlayingPosition; }; } diff --git a/src/AsyncDownloadManager.cpp b/src/AsyncDownloadManager.cpp index d79e597..4af229b 100644 --- a/src/AsyncDownloadManager.cpp +++ b/src/AsyncDownloadManager.cpp @@ -9,8 +9,9 @@ void AsyncDownloadManager::enqueueAsyncDL(std::string query) { dlQueueCondition.notify_one(); } -std::string getResultFromCommand(std::string cmd) { - std::string result; +std::queue getResultFromCommand(std::string cmd) { + std::string result, token; + std::queue tokens; FILE* stream; const int maxBuffer = 12; // 버퍼의 크기는 적당하게 char buffer[maxBuffer]; @@ -19,9 +20,15 @@ std::string getResultFromCommand(std::string cmd) { 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); // 파이프 닫는 것 잊지 마시고요! + pclose(stream); } - return result; + + std::stringstream ss(result); + while (std::getline(ss, token, '\n')) { + tokens.push(token); + } + + return tokens; } void AsyncDownloadManager::downloadWorker() { @@ -31,26 +38,26 @@ 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()); + break; + } std::string query = downloadQueue.front(); downloadQueue.pop(); dlQueueLock.unlock(); - auto cluster = bot.lock(); - assert(cluster); cluster->log(dpp::ll_info, "Enqueuing " + query + " accepted."); - std::string idstring = getResultFromCommand("./yt-dlp --default-search ytsearch --flat-playlist --skip-download --quiet --ignore-errors --print id " + query); - std::queue ids; - std::stringstream ss(idstring); - - std::string _id; - while (std::getline(ss, _id, '\n')) { - ids.push(_id); - } + std::queue ids = getResultFromCommand("./yt-dlp --default-search ytsearch --flat-playlist --skip-download --quiet --ignore-errors --print id " + query); if (ids.size() >= 2) { - cluster->log(dpp::ll_info, "Playlist detected."); while (!ids.empty()) { + if (ids.front() == "") { + ids.pop(); + continue; + } cluster->log(dpp::ll_info, "Enqueuing " + ids.front()); enqueue("https://youtu.be/" + ids.front()); ids.pop(); @@ -58,12 +65,31 @@ void AsyncDownloadManager::downloadWorker() { break; } - cluster->log(dpp::ll_info, "Thread id: " + tid.str() + ": " + ids.front() + " accepted."); + std::queue urls = getResultFromCommand("./yt-dlp -f ba* --print urls https://youtu.be/" + ids.front()); - system(("./yt-dlp -o \"Temp/%(id)s\" --no-clean-info-json --write-info-json --default-search ytsearch \ - --flat-playlist --skip-download --quiet --ignore-errors -f ba* https://youtu.be/" + ids.front()).c_str()); + cluster->log(dpp::ll_info, "url: " + urls.front()); - cluster->log(dpp::ll_info, "Thread id: " + tid.str() + ": " + ids.front() + " downloaded."); + musicQueue->enqueue(std::make_shared(ids.front(), urls.front())); + + std::string downloadID = ids.front(); + + std::thread th([&, downloadID](){ + if (terminate) + return; + std::ostringstream tid; + tid << std::this_thread::get_id(); + + 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()); + + 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."); + }); + th.detach(); } } } \ No newline at end of file diff --git a/src/BumbleBee.cpp b/src/BumbleBee.cpp index 33237a3..ff5d06b 100644 --- a/src/BumbleBee.cpp +++ b/src/BumbleBee.cpp @@ -14,6 +14,8 @@ BumbleBee::BumbleBee(nlohmann::json settings) { cluster->on_log(dpp::utility::cout_logger()); cluster->on_slashcommand([this](const dpp::slashcommand_t& event){on_slashcommand(event);}); cluster->on_ready([this](const dpp::ready_t &event){on_ready(event);}); + + queue = std::make_shared(); } void BumbleBee::start() { this->cluster->start(dpp::st_wait); } diff --git a/src/MusicQueue.cpp b/src/MusicQueue.cpp new file mode 100644 index 0000000..6e29d33 --- /dev/null +++ b/src/MusicQueue.cpp @@ -0,0 +1,44 @@ +#include +#include + +namespace bumbleBee { + +void MusicQueue::enqueue(std::shared_ptr Element) { + std::lock_guard lock(queueMutex); + queue.push_back(Element); +} + +std::shared_ptr MusicQueue::dequeue() { + std::lock_guard lock(queueMutex); + auto value = std::move(queue.front()); + queue.pop_front(); + return value; +} +std::weak_ptr MusicQueue::nowplaying() { + return *currentPlayingPosition; +} +std::weak_ptr MusicQueue::next_music() { + std::lock_guard lock(queueMutex); + if (currentPlayingPosition == queue.end()) + return *currentPlayingPosition; + + ++currentPlayingPosition; + + if (repeat && currentPlayingPosition == queue.end()) { + currentPlayingPosition = queue.begin(); + } + return *currentPlayingPosition; +} +std::weak_ptr MusicQueue::jump_to_index(int idx) { + std::lock_guard lock(queueMutex); + int index = 0; + for (auto iter = queue.begin(); iter != queue.end(); iter++) { + if (idx == index++) { + currentPlayingPosition = iter; + return *iter; + } + } + std::shared_ptr empty; + return empty; +} +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 2262a67..735ce8d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,10 +11,11 @@ int main() { bumbleBee::BumbleBee bot(configdocument); bumbleBee::AsyncDownloadManager& manager = bumbleBee::AsyncDownloadManager::getInstance(5, bot.cluster, bot.queue); - manager.enqueue("https://music.youtube.com/playlist?list=PL5NSTAfQ-wQBqZYMTqxADemyUW8mxJq2h&si=S1OwPaaif_litCqN"); + manager.enqueue("https://music.youtube.com/playlist?list=PL5NSTAfQ-wQBqZYMTqxADemyUW8mxJq2h&si=vFV4jlm70kxGfKNa"); - std::thread th([](){sleep(100);}); + std::thread th([](){sleep(11);}); th.join(); + std::cout << "\n\n\n\n\nend\n\n\n\n\n\n\n"; //bot.start(); } \ No newline at end of file