mirror of
https://github.com/HappyTanuki/BumbleCee.git
synced 2025-10-26 01:45:15 +00:00
일단은 임시저장, 스트리밍의 실마리를 잡았다
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -3,4 +3,5 @@ Temp/
|
|||||||
config.json
|
config.json
|
||||||
Music
|
Music
|
||||||
*.info.json
|
*.info.json
|
||||||
yt-dlp
|
yt-dlp
|
||||||
|
ffmpeg
|
||||||
22
.vscode/settings.json
vendored
22
.vscode/settings.json
vendored
@@ -77,6 +77,26 @@
|
|||||||
"typeinfo": "cpp",
|
"typeinfo": "cpp",
|
||||||
"valarray": "cpp",
|
"valarray": "cpp",
|
||||||
"variant": "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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,8 @@ set(BOT_NAME "BumbleCee")
|
|||||||
project(${BOT_NAME})
|
project(${BOT_NAME})
|
||||||
aux_source_directory("src" coresrc)
|
aux_source_directory("src" coresrc)
|
||||||
aux_source_directory("src/Commands" commands)
|
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)
|
string(ASCII 27 Esc)
|
||||||
|
|
||||||
|
|||||||
@@ -5,30 +5,28 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <curl/curl.h>
|
|
||||||
#include <dpp/dpp.h>
|
#include <dpp/dpp.h>
|
||||||
#include <MusicQueue.hpp>
|
#include <MusicQueue.hpp>
|
||||||
|
|
||||||
#define WORKER_COUNT 5
|
#define WORKER_COUNT 5
|
||||||
|
|
||||||
namespace bumbleBee {
|
namespace bumbleBee {
|
||||||
|
/// @brief 싱글톤 멀티스레딩 다운로드 매니저
|
||||||
class AsyncDownloadManager {
|
class AsyncDownloadManager {
|
||||||
public:
|
public:
|
||||||
static AsyncDownloadManager& getInstance(int worker_count, std::weak_ptr<dpp::cluster> bot, std::shared_ptr<bumbleBee::MusicQueue> musicQueue) {
|
static AsyncDownloadManager& getInstance(int worker_count, std::weak_ptr<dpp::cluster> weak_cluster, std::shared_ptr<bumbleBee::MusicQueue> musicQueue) {
|
||||||
static AsyncDownloadManager dl(worker_count);
|
static AsyncDownloadManager dl(worker_count);
|
||||||
dl.bot = bot;
|
dl.weak_cluster = weak_cluster;
|
||||||
dl.musicQueue = musicQueue;
|
dl.musicQueue = musicQueue;
|
||||||
return dl;
|
return dl;
|
||||||
}
|
}
|
||||||
void enqueue(std::string query) {
|
void enqueue(std::pair<std::string, dpp::message> query) {
|
||||||
std::thread th(&bumbleBee::AsyncDownloadManager::enqueueAsyncDL, this, query);
|
std::thread th(&bumbleBee::AsyncDownloadManager::enqueueAsyncDL, this, query);
|
||||||
th.detach();
|
th.detach();
|
||||||
}
|
}
|
||||||
|
void stop() {
|
||||||
~AsyncDownloadManager(){
|
auto cluster = weak_cluster.lock();
|
||||||
auto cluster = bot.lock();
|
cluster->log(dpp::ll_info, "AsyncDownloadManager stop/destructor called.");
|
||||||
assert(cluster);
|
|
||||||
cluster->log(dpp::ll_info, "AsyncDownloadManager Destructor called.");
|
|
||||||
terminate = true;
|
terminate = true;
|
||||||
dlQueueCondition.notify_all();
|
dlQueueCondition.notify_all();
|
||||||
|
|
||||||
@@ -37,6 +35,10 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~AsyncDownloadManager(){
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AsyncDownloadManager(int worker_count){
|
AsyncDownloadManager(int worker_count){
|
||||||
worker_thread.reserve(worker_count);
|
worker_thread.reserve(worker_count);
|
||||||
@@ -46,13 +48,13 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void enqueueAsyncDL(std::string query);
|
void enqueueAsyncDL(std::pair<std::string, dpp::message> query);
|
||||||
void downloadWorker();
|
void downloadWorker();
|
||||||
|
|
||||||
std::queue<std::string> downloadQueue;
|
std::queue<std::pair<std::string, dpp::message>> downloadQueue;
|
||||||
std::condition_variable dlQueueCondition;
|
std::condition_variable dlQueueCondition;
|
||||||
std::mutex dlQueueMutex;
|
std::mutex dlQueueMutex;
|
||||||
std::weak_ptr<dpp::cluster> bot;
|
std::weak_ptr<dpp::cluster> weak_cluster;
|
||||||
std::shared_ptr<bumbleBee::MusicQueue> musicQueue;
|
std::shared_ptr<bumbleBee::MusicQueue> musicQueue;
|
||||||
std::vector<std::thread> worker_thread;
|
std::vector<std::thread> worker_thread;
|
||||||
bool terminate;
|
bool terminate;
|
||||||
|
|||||||
@@ -18,9 +18,8 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @fn BumbleBee(nlohmann::json settings)
|
* @fn BumbleBee(nlohmann::json settings)
|
||||||
* @brief 생성자
|
* @brief 생성자
|
||||||
* @param settings 설정 json
|
|
||||||
**/
|
**/
|
||||||
BumbleBee(nlohmann::json settings);
|
BumbleBee();
|
||||||
/**
|
/**
|
||||||
* @fn ~BumbleBee()
|
* @fn ~BumbleBee()
|
||||||
* @brief 파괴자
|
* @brief 파괴자
|
||||||
|
|||||||
@@ -7,15 +7,19 @@
|
|||||||
namespace bumbleBee::commands {
|
namespace bumbleBee::commands {
|
||||||
class ICommand {
|
class ICommand {
|
||||||
public:
|
public:
|
||||||
/// @brief 기본 생성자
|
/**
|
||||||
/// @param botID 명령어 등록을 위한 봇 아이디
|
* @brief 기본 생성자
|
||||||
/// @param queue 음악이 저장되어 있는 큐
|
* @param botID 명령어 등록을 위한 봇 아이디
|
||||||
|
* @param queue 음악이 저장되어 있는 큐
|
||||||
|
**/
|
||||||
ICommand(dpp::snowflake botID, std::weak_ptr<MusicQueue> queue) {
|
ICommand(dpp::snowflake botID, std::weak_ptr<MusicQueue> queue) {
|
||||||
this->botID = botID;
|
this->botID = botID;
|
||||||
this->queue = queue;
|
this->queue = queue;
|
||||||
}
|
}
|
||||||
/// @brief 명령어 호출 시에 콜백되는 함수
|
/**
|
||||||
/// @param event
|
* @brief 명령어 호출 시에 콜백될 메소드
|
||||||
|
* @param event dpp::slashcommand_t
|
||||||
|
**/
|
||||||
virtual void operator()(const dpp::slashcommand_t &event){};
|
virtual void operator()(const dpp::slashcommand_t &event){};
|
||||||
|
|
||||||
/// @brief 봇 명령어들의 이름과 별명들을 저장하는 벡터
|
/// @brief 봇 명령어들의 이름과 별명들을 저장하는 벡터
|
||||||
@@ -26,11 +30,19 @@ private:
|
|||||||
dpp::snowflake botID;
|
dpp::snowflake botID;
|
||||||
/// @brief 음악 큐에 대한 약한 포인터
|
/// @brief 음악 큐에 대한 약한 포인터
|
||||||
std::weak_ptr<MusicQueue> queue;
|
std::weak_ptr<MusicQueue> 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 대체 매크로
|
* @brief 명령어 인자가 없는 명령어의 boilerplate 대체 매크로
|
||||||
* @param name 명령어 이름 및 클래스명
|
* @param name 명령어 이름 및 클래스명
|
||||||
* @param alias 명령어 별명
|
* @param alias 명령어 별명
|
||||||
@@ -52,7 +64,6 @@ public: \
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @fn _DECLARE_BUMBLEBEE_COMMAND_one_PARAM_one_ALIAS(name, alias, description, option_name, option_desc)
|
|
||||||
* @brief 명령어 인자를 하나 갖는 명령어의 boilerplate 대체 매크로
|
* @brief 명령어 인자를 하나 갖는 명령어의 boilerplate 대체 매크로
|
||||||
* @param name 명령어 이름 및 클래스명
|
* @param name 명령어 이름 및 클래스명
|
||||||
* @param alias 명령어 별명
|
* @param alias 명령어 별명
|
||||||
|
|||||||
16
include/MusicPlayManager.hpp
Normal file
16
include/MusicPlayManager.hpp
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
#ifndef _MUSICPLAYMANAGER_HPP_
|
||||||
|
#define _MUSICPLAYMANAGER_HPP_
|
||||||
|
#include <dpp/dpp.h>
|
||||||
|
|
||||||
|
namespace BumbleBee {
|
||||||
|
class MusicPlayManager {
|
||||||
|
public:
|
||||||
|
MusicPlayManager(std::shared_ptr<dpp::cluster> cluster) {
|
||||||
|
this->cluster = cluster;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::shared_ptr<dpp::cluster> cluster;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -5,14 +5,23 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <dpp/dpp.h>
|
||||||
|
|
||||||
namespace bumbleBee {
|
namespace bumbleBee {
|
||||||
|
|
||||||
class MusicQueueElement {
|
class MusicQueueElement {
|
||||||
public:
|
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 id;
|
||||||
std::string url;
|
std::string url;
|
||||||
|
FILE* stream;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MusicQueue {
|
class MusicQueue {
|
||||||
@@ -23,6 +32,7 @@ public:
|
|||||||
}
|
}
|
||||||
void enqueue(std::shared_ptr<MusicQueueElement> Element);
|
void enqueue(std::shared_ptr<MusicQueueElement> Element);
|
||||||
std::shared_ptr<MusicQueueElement> dequeue();
|
std::shared_ptr<MusicQueueElement> dequeue();
|
||||||
|
std::shared_ptr<MusicQueueElement> findById(std::string id);
|
||||||
std::weak_ptr<MusicQueueElement> nowplaying();
|
std::weak_ptr<MusicQueueElement> nowplaying();
|
||||||
std::weak_ptr<MusicQueueElement> next_music();
|
std::weak_ptr<MusicQueueElement> next_music();
|
||||||
std::weak_ptr<MusicQueueElement> jump_to_index(int idx);
|
std::weak_ptr<MusicQueueElement> jump_to_index(int idx);
|
||||||
|
|||||||
30
include/Settings/SettingsManager.hpp
Normal file
30
include/Settings/SettingsManager.hpp
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
#ifndef _SETTINGSMANAGER_HPP_
|
||||||
|
#define _SETTINGSMANAGER_HPP_
|
||||||
|
#include <string>
|
||||||
|
#include <dpp/nlohmann/json.hpp>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
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
|
||||||
40
include/Utils/ConsoleUtils.hpp
Normal file
40
include/Utils/ConsoleUtils.hpp
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
#ifndef _CONSOLEUTILS_HPP_
|
||||||
|
#define _CONSOLEUTILS_HPP_
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
namespace bumbleBee {
|
||||||
|
class ConsoleUtils {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief 명령어를 쉘에서 실행하고 결과를 EOF 전까지 읽어 \n을 구분자로 토큰화하여 반환합니다
|
||||||
|
* @param cmd 실행할 명령
|
||||||
|
* @return std::queue<std::string> tokens
|
||||||
|
*/
|
||||||
|
static std::queue<std::string> getResultFromCommand(std::string cmd) {
|
||||||
|
std::string result, token;
|
||||||
|
std::queue<std::string> 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
|
||||||
75
include/Utils/VersionsCheckUtils.hpp
Normal file
75
include/Utils/VersionsCheckUtils.hpp
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
#pragma once
|
||||||
|
#ifndef _VERSIONCHECKUTILS_HPP_
|
||||||
|
#define _VERSIONCHECKUTILS_HPP_
|
||||||
|
#include <dpp/dpp.h>
|
||||||
|
#include "ConsoleUtils.hpp"
|
||||||
|
#include "../Settings/SettingsManager.hpp"
|
||||||
|
|
||||||
|
namespace bumbleBee {
|
||||||
|
class VersionsCheckUtils {
|
||||||
|
public:
|
||||||
|
static bool isThereCMD(std::shared_ptr<dpp::cluster> 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<dpp::cluster> cluster) {
|
||||||
|
cluster->log(dpp::ll_info, "Checking if yt-dlp and ffmpeg is available...");
|
||||||
|
std::queue<std::string> 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<dpp::cluster> cluster) {
|
||||||
|
cluster->log(dpp::ll_info, "Checking if yt-dlp update is available...");
|
||||||
|
std::queue<std::string> result = ConsoleUtils::getResultFromCommand("./yt-dlp -U");
|
||||||
|
while(!result.empty()) {
|
||||||
|
std::string front = result.front();
|
||||||
|
result.pop();
|
||||||
|
cluster->log(dpp::ll_info, front);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -1,36 +1,20 @@
|
|||||||
#include <AsyncDownloadManager.hpp>
|
#include <AsyncDownloadManager.hpp>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include "Utils/ConsoleUtils.hpp"
|
||||||
|
#include "Settings/SettingsManager.hpp"
|
||||||
|
#include <ogg/ogg.h>
|
||||||
|
#include <oggz/oggz.h>
|
||||||
|
#include <opus/opusfile.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace bumbleBee {
|
namespace bumbleBee {
|
||||||
|
|
||||||
void AsyncDownloadManager::enqueueAsyncDL(std::string query) {
|
void AsyncDownloadManager::enqueueAsyncDL(std::pair<std::string, dpp::message> query) {
|
||||||
std::lock_guard<std::mutex> lock(dlQueueMutex);
|
std::lock_guard<std::mutex> lock(dlQueueMutex);
|
||||||
downloadQueue.push(query);
|
downloadQueue.push(query);
|
||||||
dlQueueCondition.notify_one();
|
dlQueueCondition.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::queue<std::string> getResultFromCommand(std::string cmd) {
|
|
||||||
std::string result, token;
|
|
||||||
std::queue<std::string> 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() {
|
void AsyncDownloadManager::downloadWorker() {
|
||||||
std::ostringstream tid;
|
std::ostringstream tid;
|
||||||
tid << std::this_thread::get_id();
|
tid << std::this_thread::get_id();
|
||||||
@@ -38,38 +22,49 @@ void AsyncDownloadManager::downloadWorker() {
|
|||||||
//mutex lock
|
//mutex lock
|
||||||
std::unique_lock<std::mutex> dlQueueLock(dlQueueMutex);
|
std::unique_lock<std::mutex> dlQueueLock(dlQueueMutex);
|
||||||
dlQueueCondition.wait(dlQueueLock, [&]{ return !downloadQueue.empty() || terminate; });
|
dlQueueCondition.wait(dlQueueLock, [&]{ return !downloadQueue.empty() || terminate; });
|
||||||
auto cluster = bot.lock();
|
auto cluster = weak_cluster.lock();
|
||||||
assert(cluster);
|
if (weak_cluster.expired()) {
|
||||||
if (terminate) {
|
cluster->log(dpp::ll_error, "Missing cluster, terminating thread " + tid.str());
|
||||||
cluster->log(dpp::ll_info, "Terminating Thread" + tid.str());
|
|
||||||
break;
|
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();
|
downloadQueue.pop();
|
||||||
dlQueueLock.unlock();
|
dlQueueLock.unlock();
|
||||||
|
|
||||||
cluster->log(dpp::ll_info, "Enqueuing " + query + " accepted.");
|
cluster->log(dpp::ll_info, "AsyncDownloadManager: " + query + " accepted.");
|
||||||
|
|
||||||
std::queue<std::string> ids = getResultFromCommand("./yt-dlp --default-search ytsearch --flat-playlist --skip-download --quiet --ignore-errors --print id " + query);
|
std::queue<std::string> ids =
|
||||||
|
ConsoleUtils::getResultFromCommand(settingsManager::YTDLP_CMD +
|
||||||
|
" --default-search ytsearch --flat-playlist --skip-download --quiet --ignore-errors --print id " + query);
|
||||||
|
|
||||||
if (ids.size() >= 2) {
|
if (ids.size() >= 2) {
|
||||||
|
cluster->log(dpp::ll_info, query + " is playlist");
|
||||||
while (!ids.empty()) {
|
while (!ids.empty()) {
|
||||||
if (ids.front() == "") {
|
if (ids.front() == "") {
|
||||||
ids.pop();
|
ids.pop();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
cluster->log(dpp::ll_info, "Enqueuing " + ids.front());
|
cluster->log(dpp::ll_info, "Enqueuing playlist element " + ids.front());
|
||||||
enqueue("https://youtu.be/" + ids.front());
|
enqueue(std::make_pair("https://youtu.be/" + ids.front(), oRes));
|
||||||
ids.pop();
|
ids.pop();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::queue<std::string> urls = getResultFromCommand("./yt-dlp -f ba* --print urls https://youtu.be/" + ids.front());
|
std::queue<std::string> 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<MusicQueueElement>(ids.front(), urls.front()));
|
FILE* stream;
|
||||||
|
|
||||||
|
musicQueue->enqueue(std::make_shared<MusicQueueElement>(oRes, ids.front(), urls.front(), stream));
|
||||||
|
|
||||||
std::string downloadID = ids.front();
|
std::string downloadID = ids.front();
|
||||||
|
|
||||||
@@ -81,13 +76,12 @@ void AsyncDownloadManager::downloadWorker() {
|
|||||||
|
|
||||||
cluster->log(dpp::ll_info, "Thread id: " + tid.str() + ": " + downloadID + " accepted.");
|
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 \""
|
cluster->log(dpp::ll_info, "Thread id: " + tid.str() + ": " + downloadID + " download complete.");
|
||||||
+ "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();
|
th.detach();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,17 @@
|
|||||||
#include <BumbleBee.hpp>
|
#include <BumbleBee.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <Settings/SettingsManager.hpp>
|
||||||
|
#include <Utils/VersionsCheckUtils.hpp>
|
||||||
|
|
||||||
namespace bumbleBee{
|
namespace bumbleBee{
|
||||||
BumbleBee::BumbleBee(nlohmann::json settings) {
|
BumbleBee::BumbleBee() {
|
||||||
this->cluster = std::make_shared<dpp::cluster>(settings["token"]);
|
settingsManager::load();
|
||||||
|
this->cluster = std::make_shared<dpp::cluster>(settingsManager::TOKEN);
|
||||||
dbDriver = sql::mariadb::get_driver_instance();
|
dbDriver = sql::mariadb::get_driver_instance();
|
||||||
this->dbURL = std::make_shared<sql::SQLString>(settings["dbURL"]);
|
this->dbURL = std::make_shared<sql::SQLString>(settingsManager::DBURL);
|
||||||
sql::Properties pro({
|
sql::Properties pro({
|
||||||
{"user", std::string(settings["user"])},
|
{"user", std::string(settingsManager::DBUSER)},
|
||||||
{"password", std::string(settings["password"])}
|
{"password", std::string(settingsManager::DBPASSWORD)}
|
||||||
});
|
});
|
||||||
this->dbProperties = std::make_shared<sql::Properties>(pro);
|
this->dbProperties = std::make_shared<sql::Properties>(pro);
|
||||||
|
|
||||||
@@ -16,6 +20,9 @@ BumbleBee::BumbleBee(nlohmann::json settings) {
|
|||||||
cluster->on_ready([this](const dpp::ready_t &event){on_ready(event);});
|
cluster->on_ready([this](const dpp::ready_t &event){on_ready(event);});
|
||||||
|
|
||||||
queue = std::make_shared<MusicQueue>();
|
queue = std::make_shared<MusicQueue>();
|
||||||
|
|
||||||
|
VersionsCheckUtils::validateYTDLPFFMPEGBinary(cluster);
|
||||||
|
VersionsCheckUtils::updateytdlp(cluster);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BumbleBee::start() { this->cluster->start(dpp::st_wait); }
|
void BumbleBee::start() { this->cluster->start(dpp::st_wait); }
|
||||||
|
|||||||
@@ -14,7 +14,17 @@ std::shared_ptr<MusicQueueElement> MusicQueue::dequeue() {
|
|||||||
queue.pop_front();
|
queue.pop_front();
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
std::weak_ptr<MusicQueueElement> MusicQueue::nowplaying() {
|
std::shared_ptr<MusicQueueElement> MusicQueue::findById(std::string id) {
|
||||||
|
std::lock_guard<std::mutex> 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<MusicQueueElement>();
|
||||||
|
}
|
||||||
|
std::weak_ptr<MusicQueueElement> MusicQueue::nowplaying()
|
||||||
|
{
|
||||||
return *currentPlayingPosition;
|
return *currentPlayingPosition;
|
||||||
}
|
}
|
||||||
std::weak_ptr<MusicQueueElement> MusicQueue::next_music() {
|
std::weak_ptr<MusicQueueElement> MusicQueue::next_music() {
|
||||||
@@ -38,7 +48,6 @@ std::weak_ptr<MusicQueueElement> MusicQueue::jump_to_index(int idx) {
|
|||||||
return *iter;
|
return *iter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::shared_ptr<MusicQueueElement> empty;
|
return std::shared_ptr<MusicQueueElement>();
|
||||||
return empty;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
67
src/Settings/SettingsManager.cpp
Normal file
67
src/Settings/SettingsManager.cpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#include <Settings/SettingsManager.hpp>
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/main.cpp
11
src/main.cpp
@@ -4,17 +4,14 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
nlohmann::json configdocument;
|
bumbleBee::BumbleBee bot;
|
||||||
std::ifstream configfile("config.json");
|
|
||||||
configfile >> configdocument;
|
|
||||||
|
|
||||||
bumbleBee::BumbleBee bot(configdocument);
|
|
||||||
|
|
||||||
bumbleBee::AsyncDownloadManager& manager = bumbleBee::AsyncDownloadManager::getInstance(5, bot.cluster, bot.queue);
|
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();
|
th.join();
|
||||||
|
manager.stop();
|
||||||
|
|
||||||
std::cout << "\n\n\n\n\nend\n\n\n\n\n\n\n";
|
std::cout << "\n\n\n\n\nend\n\n\n\n\n\n\n";
|
||||||
//bot.start();
|
//bot.start();
|
||||||
|
|||||||
8
streamAndDownload.sh
Executable file
8
streamAndDownload.sh
Executable file
@@ -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 -
|
||||||
Reference in New Issue
Block a user