mirror of
				https://github.com/HappyTanuki/BumbleCee.git
				synced 2025-10-25 17:35:58 +00:00 
			
		
		
		
	완성(준)
This commit is contained in:
		
							
								
								
									
										1
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| FROM alpine | ||||
| @@ -8,13 +8,19 @@ | ||||
| namespace bumbleBee { | ||||
| class MusicPlayManager { | ||||
| public: | ||||
|     MusicPlayManager(std::shared_ptr<dpp::cluster> cluster) { | ||||
|         this->cluster = cluster; | ||||
|         this->queue = std::make_unique<MusicQueue>(); | ||||
|     MusicPlayManager(std::shared_ptr<dpp::cluster> cluster, std::vector<dpp::snowflake> GIDs) : | ||||
|         cluster(cluster), GIDs(GIDs) { | ||||
|         queueMap = std::unordered_map<dpp::snowflake, std::shared_ptr<MusicQueue>>(); | ||||
|         queueEmptyMutex = std::unordered_map<dpp::snowflake, std::shared_ptr<std::mutex>>(); | ||||
|  | ||||
|         cluster->on_voice_ready([this](const dpp::voice_ready_t &event){on_voice_ready(event);}); | ||||
|         cluster->on_voice_track_marker([this](const dpp::voice_track_marker_t &event){on_voice_track_marker(event);}); | ||||
|         cluster->on_voice_client_disconnect([this](const dpp::voice_client_disconnect_t& event){on_voice_client_disconnect(event);}); | ||||
|  | ||||
|         for (auto GID : GIDs) { | ||||
|             queueMap[GID] = std::make_shared<MusicQueue>(); | ||||
|             queueEmptyMutex[GID] = std::make_shared<std::mutex>(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -33,18 +39,31 @@ public: | ||||
|     **/ | ||||
|     void on_voice_client_disconnect(const dpp::voice_client_disconnect_t& event); | ||||
|  | ||||
|     void queue_music(const std::shared_ptr<MusicQueueElement> music); | ||||
|     void play(dpp::discord_voice_client* client); | ||||
|  | ||||
|     void queue_music(const dpp::snowflake guildId, const std::shared_ptr<MusicQueueElement> music); | ||||
|  | ||||
|     void clear(const dpp::snowflake guildId); | ||||
|     std::shared_ptr<MusicQueueElement> remove(const dpp::snowflake guildId, dpp::discord_voice_client* client, int index); | ||||
|     int size(const dpp::snowflake guildId); | ||||
|  | ||||
|     void setRepeat(const dpp::snowflake guildId, const bool value); | ||||
|     bool getRepeat(const dpp::snowflake guildId); | ||||
|  | ||||
|     std::list<MusicQueueElement> getQueue(const dpp::snowflake guildId); | ||||
|     MusicQueueElement getNowPlaying(const dpp::snowflake guildId); | ||||
|  | ||||
|     std::condition_variable queuedCondition; | ||||
| private: | ||||
|     std::shared_ptr<dpp::cluster> cluster; | ||||
|  | ||||
|     std::vector<dpp::snowflake> GIDs; | ||||
|     /// @brief 음악 큐 | ||||
|     std::unique_ptr<MusicQueue> queue; | ||||
|     std::unordered_map<dpp::snowflake, std::shared_ptr<MusicQueue>> queueMap; | ||||
|  | ||||
|     std::mutex queueEmptyMutex; | ||||
|     std::unordered_map<dpp::snowflake, std::shared_ptr<std::mutex>> queueEmptyMutex; | ||||
|  | ||||
|     void send_audio_to_voice(const MusicQueueElement& music, const dpp::voice_ready_t &event); | ||||
|     void send_audio_to_voice(const MusicQueueElement& music, const dpp::voice_track_marker_t& event); | ||||
|     void send_audio_to_voice(const MusicQueueElement& music, dpp::discord_voice_client* client); | ||||
| }; | ||||
| } | ||||
| #endif | ||||
| @@ -41,6 +41,8 @@ public: | ||||
|  | ||||
|     /// @brief DPP 기본 클러스터 객체 | ||||
|     std::shared_ptr<dpp::cluster> cluster; | ||||
|     /// @brief guild id 배열 | ||||
|     std::vector<dpp::snowflake> GIDs; | ||||
| private: | ||||
|     /// @brief Command 목록 | ||||
|     std::unordered_map<std::string, std::shared_ptr<commands::ICommand>> commands; | ||||
|   | ||||
| @@ -62,7 +62,7 @@ protected: \ | ||||
| _DECLARE_BUMBLEBEE_COMMAND(Delete,  d,          "큐의 해당하는 번호의 노래를 지웁니다") | ||||
| _DECLARE_BUMBLEBEE_COMMAND(Leave,   l,          "음성 채팅방을 떠납니다") | ||||
| _DECLARE_BUMBLEBEE_COMMAND(Play,    p,          "노래 재생") | ||||
| _DECLARE_BUMBLEBEE_COMMAND(Queue,   q,          "노래 예약 큐를 출력합니다") | ||||
| _DECLARE_BUMBLEBEE_COMMAND(Queue,   q,          "노래 예약 큐를 확인합니다") | ||||
| _DECLARE_BUMBLEBEE_COMMAND(Repeat,  r,          "반복을 켜거나 끕니다") | ||||
| _DECLARE_BUMBLEBEE_COMMAND(Skip,    s,          "현재 재생중인 곡을 스킵합니다") | ||||
| _DECLARE_BUMBLEBEE_COMMAND(Shuffle, shuffle,    "큐를 섞습니다") | ||||
|   | ||||
| @@ -15,20 +15,25 @@ public: | ||||
|     MusicQueue() { | ||||
|         queue = std::list<std::shared_ptr<MusicQueueElement>>(); | ||||
|         currentPlayingPosition = queue.begin(); | ||||
|         repeat = false; | ||||
|         repeat = true; | ||||
|     } | ||||
|     void                                                    enqueue(std::shared_ptr<MusicQueueElement> Element); | ||||
|     std::shared_ptr<MusicQueueElement>                      dequeue(); | ||||
|     std::list<std::shared_ptr<MusicQueueElement>>::iterator findById(std::string id); | ||||
|     std::list<std::shared_ptr<MusicQueueElement>>::iterator findByIndex(int index); | ||||
|     std::shared_ptr<MusicQueueElement>                      nowplaying(); | ||||
|     std::list<std::shared_ptr<MusicQueueElement>>::iterator next_music(); | ||||
|     std::shared_ptr<MusicQueueElement>                      jump_to_index(int idx); | ||||
|     void                                                    clear(); | ||||
|     std::shared_ptr<MusicQueueElement>                      erase(std::list<std::shared_ptr<MusicQueueElement>>::iterator it); | ||||
|     std::list<std::shared_ptr<MusicQueueElement>>           getQueueCopy(); | ||||
|     int                                                     size(); | ||||
|  | ||||
|     bool repeat; | ||||
|  | ||||
|     std::list<std::shared_ptr<MusicQueueElement>>::iterator currentPlayingPosition; | ||||
|     std::list<std::shared_ptr<MusicQueueElement>> queue; | ||||
| private: | ||||
|     std::list<std::shared_ptr<MusicQueueElement>> queue; | ||||
|     std::mutex queueMutex; | ||||
| }; | ||||
| } | ||||
|   | ||||
| @@ -10,12 +10,14 @@ public: | ||||
|     MusicQueueElement( | ||||
|         std::string id, | ||||
|         std::string query, | ||||
|         dpp::user issuingUser) : | ||||
|         id(id), query(query), issuingUser(issuingUser) {} | ||||
|         dpp::user issuingUser, | ||||
|         dpp::embed embed) : | ||||
|         id(id), query(query), issuingUser(issuingUser), embed(embed) {} | ||||
|  | ||||
|     const std::string id; | ||||
|     const std::string query; | ||||
|     const dpp::user issuingUser; | ||||
|     const dpp::embed embed; | ||||
| }; | ||||
| } | ||||
| #endif | ||||
| @@ -12,7 +12,7 @@ public:\ | ||||
|  | ||||
| namespace bumbleBee { | ||||
| /// @brief 모든 설정은 이 객체를 통해 스태틱하게 제공됨. | ||||
| class settingsManager { | ||||
| class SettingsManager { | ||||
|     /// @brief 봇 토큰 | ||||
|     _DECLARE_DEFAULT_ACCESSER_STATIC_VARIABLE(std::string, TOKEN, TOKEN) | ||||
|     /// @brief yt-dlp 실행 명령어 | ||||
| @@ -22,7 +22,7 @@ class settingsManager { | ||||
|     /// @brief 로그레벨 | ||||
|     _DECLARE_DEFAULT_ACCESSER_STATIC_VARIABLE(dpp::loglevel, LOGLEVEL, LOGLEVEL) | ||||
|     /// @brief 이전에 있던 명령을 지우고 등록할지 선택합니다. | ||||
|     _DECLARE_DEFAULT_ACCESSER_STATIC_VARIABLE(bool, CLCOMMAND, CLCOMMAND) | ||||
|     _DECLARE_DEFAULT_ACCESSER_STATIC_VARIABLE(bool, REGISTER_COMMAND, REGISTER_COMMAND) | ||||
| public: | ||||
|     /// @brief 설정 로드하기, 설정은 이 load()를 부르기 전까지는 적절하지 못한 값을 가지고 있음. | ||||
|     /// @return 로드 성공 시 true, 실패 시 false 반환. | ||||
|   | ||||
| @@ -19,7 +19,7 @@ public: | ||||
|  | ||||
|     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::getFFMPEG_CMD() + " -version"); | ||||
|         std::queue<std::string> result = ConsoleUtils::getResultFromCommand(SettingsManager::getFFMPEG_CMD() + " -version"); | ||||
|         std::string front = result.front(); | ||||
|         if (front[0] != 'f' || | ||||
|             front[1] != 'f' || | ||||
| @@ -40,9 +40,9 @@ public: | ||||
|             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"); | ||||
|             settingsManager::setFFMPEG_CMD("./ffmpeg/bin/ffmpeg"); | ||||
|             SettingsManager::setFFMPEG_CMD("./ffmpeg/bin/ffmpeg"); | ||||
|         } | ||||
|         result = ConsoleUtils::getResultFromCommand(settingsManager::getYTDLP_CMD() + " --version"); | ||||
|         result = ConsoleUtils::getResultFromCommand(SettingsManager::getYTDLP_CMD() + " --version"); | ||||
|         front = result.front(); | ||||
|         if ((front[0]-'0' < 0 || front[0]-'0' > 9) || | ||||
|             (front[1]-'0' < 0 || front[1]-'0' > 9) || | ||||
| @@ -59,7 +59,7 @@ public: | ||||
|  | ||||
|             system("curl -LO https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp"); | ||||
|             system("chmod +x ./yt-dlp"); | ||||
|             settingsManager::setYTDLP_CMD("./yt-dlp"); | ||||
|             SettingsManager::setYTDLP_CMD("./yt-dlp"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,23 +1,97 @@ | ||||
| #include <Audio/MusicPlayManager.hpp> | ||||
| #include <ogg/ogg.h> | ||||
| #include <oggz/oggz.h> | ||||
| #include <algorithm> | ||||
|  | ||||
| namespace bumbleBee { | ||||
|  | ||||
| void MusicPlayManager::on_voice_ready(const dpp::voice_ready_t& event) { | ||||
|     std::unique_lock<std::mutex> queueEmptyLock(queueEmptyMutex); | ||||
|     queuedCondition.wait(queueEmptyLock, [&]{ | ||||
|         return queue->queue.size() != 0; | ||||
|     }); | ||||
|     play(event.voice_client); | ||||
| } | ||||
|  | ||||
|     auto np = queue->nowplaying(); | ||||
|     auto music = *np; | ||||
|      | ||||
| void MusicPlayManager::on_voice_track_marker(const dpp::voice_track_marker_t& event) { | ||||
|     play(event.voice_client); | ||||
| } | ||||
|  | ||||
| void MusicPlayManager::on_voice_client_disconnect(const dpp::voice_client_disconnect_t& event) { // 안 불리는 듯? | ||||
|     dpp::snowflake gid = dpp::find_channel(event.voice_client->channel_id)->guild_id; | ||||
|     event.voice_client->stop_audio(); | ||||
|     queueMap[gid]->clear(); | ||||
| } | ||||
|  | ||||
| void MusicPlayManager::play(dpp::discord_voice_client* client) { | ||||
|     std::thread t([&](dpp::discord_voice_client* client){ | ||||
|         dpp::snowflake gid = dpp::find_channel(client->channel_id)->guild_id; | ||||
|  | ||||
|         std::unique_lock<std::mutex> queueEmptyLock(*queueEmptyMutex[gid]); | ||||
|         queuedCondition.wait(queueEmptyLock, [&]{ return queueMap[gid]->size() != 0; }); | ||||
|  | ||||
|         auto np = queueMap[gid]->next_music(); | ||||
|         auto music = **np; | ||||
|         send_audio_to_voice(music, client); | ||||
|     }, client); | ||||
|     t.detach(); | ||||
| } | ||||
|  | ||||
| void MusicPlayManager::queue_music(const dpp::snowflake guildId, const std::shared_ptr<MusicQueueElement> music) { | ||||
|     queueMap[guildId]->enqueue(music); | ||||
| } | ||||
|  | ||||
| void MusicPlayManager::clear(const dpp::snowflake guildId) { | ||||
|     queueMap[guildId]->clear(); | ||||
| } | ||||
| std::shared_ptr<MusicQueueElement> MusicPlayManager::remove(const dpp::snowflake guildId, dpp::discord_voice_client* client, int index) { | ||||
|     auto queue = queueMap[guildId]; | ||||
|     auto foundIterator = queue->findByIndex(index); | ||||
|  | ||||
|     if (queue->currentPlayingPosition == foundIterator) { | ||||
|         auto removed = queue->erase(queue->findByIndex(0)); | ||||
|         if (client == nullptr) | ||||
|             return removed; | ||||
|         client->pause_audio(true); | ||||
|         client->stop_audio(); | ||||
|         client->pause_audio(false); | ||||
|         client->insert_marker("end"); | ||||
|         return removed; | ||||
|     } | ||||
|  | ||||
|     return queue->erase(queue->findByIndex(index)); | ||||
| } | ||||
| int MusicPlayManager::size(const dpp::snowflake guildId) { | ||||
|     return queueMap[guildId]->size(); | ||||
| } | ||||
| void MusicPlayManager::setRepeat(const dpp::snowflake guildId, const bool value) { | ||||
|     queueMap[guildId]->repeat = value; | ||||
| } | ||||
|  | ||||
| bool MusicPlayManager::getRepeat(const dpp::snowflake guildId) { | ||||
|     return queueMap[guildId]->repeat; | ||||
| } | ||||
|  | ||||
| std::list<MusicQueueElement> MusicPlayManager::getQueue(const dpp::snowflake guildId){ | ||||
|     std::list<std::shared_ptr<MusicQueueElement>> queue = queueMap[guildId]->getQueueCopy(); | ||||
|     std::list<MusicQueueElement> returnValue; | ||||
|  | ||||
|     for (auto iter = queue.begin(); iter != queue.end(); iter++) | ||||
|         returnValue.push_back(**iter); | ||||
|  | ||||
|     return returnValue; | ||||
| } | ||||
|  | ||||
| MusicQueueElement MusicPlayManager::getNowPlaying(const dpp::snowflake guildId) { | ||||
|     std::shared_ptr<MusicQueueElement> nowplaying = queueMap[guildId]->nowplaying(); | ||||
|     MusicQueueElement returnValue(*nowplaying); | ||||
|     return returnValue; | ||||
| } | ||||
|  | ||||
| void MusicPlayManager::send_audio_to_voice(const MusicQueueElement& music, dpp::discord_voice_client* client) { | ||||
|     std::string command = "./streamOpus.sh ./yt-dlp ffmpeg https://youtu.be/"; | ||||
|     command += music.id; | ||||
|  | ||||
|     OGGZ* og = oggz_open_stdio(popen(command.c_str(), "r"), OGGZ_READ); | ||||
|  | ||||
|     client->stop_audio(); | ||||
|  | ||||
|     oggz_set_read_callback( | ||||
|         og, -1, | ||||
|         [](OGGZ *oggz, oggz_packet *packet, long serialno, void *user_data) { | ||||
| @@ -27,10 +101,10 @@ void MusicPlayManager::on_voice_ready(const dpp::voice_ready_t& event) { | ||||
|  | ||||
|             return 0; | ||||
|         }, | ||||
|         (void *)event.voice_client | ||||
|         (void *)client | ||||
|     ); | ||||
|  | ||||
|     while (event.voice_client && !event.voice_client->terminating) { | ||||
|     while (client && !client->terminating) { | ||||
|         static const constexpr long CHUNK_READ = BUFSIZ * 2; | ||||
|  | ||||
|         const long read_bytes = oggz_read(og, CHUNK_READ); | ||||
| @@ -41,28 +115,11 @@ void MusicPlayManager::on_voice_ready(const dpp::voice_ready_t& event) { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     //event.from->creator->log(dpp::ll_info, std::string("Sending ") + music.id + " complete!"); | ||||
|     client->creator->log(dpp::ll_info, "Sending " + music.id + " complete!"); | ||||
|  | ||||
|     oggz_close(og); | ||||
|  | ||||
|     event.voice_client->insert_marker("end"); | ||||
|  | ||||
|     queueEmptyLock.unlock(); | ||||
| } | ||||
|  | ||||
| void MusicPlayManager::on_voice_track_marker(const dpp::voice_track_marker_t& event) { | ||||
|     queue->next_music(); | ||||
| } | ||||
|  | ||||
| void MusicPlayManager::on_voice_client_disconnect(const dpp::voice_client_disconnect_t& event) { | ||||
|     event.voice_client->stop_audio(); | ||||
| } | ||||
|  | ||||
| void MusicPlayManager::queue_music(const std::shared_ptr<MusicQueueElement> music) { | ||||
|     queue->enqueue(music); | ||||
| } | ||||
|  | ||||
| void MusicPlayManager::send_audio_to_voice(const MusicQueueElement& music, const dpp::voice_ready_t &event) { | ||||
|      | ||||
|     client->insert_marker(); | ||||
| } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -7,15 +7,15 @@ | ||||
|  | ||||
| namespace bumbleBee{ | ||||
| BumbleBee::BumbleBee() { | ||||
|     if (!settingsManager::load()) { | ||||
|     if (!SettingsManager::load()) { | ||||
|         std::cout << "Please configure confing.json" << std::endl; | ||||
|         exit(1); | ||||
|     } | ||||
|  | ||||
|     cluster = std::make_shared<dpp::cluster>(settingsManager::getTOKEN()); | ||||
|     cluster = std::make_shared<dpp::cluster>(SettingsManager::getTOKEN()); | ||||
|  | ||||
|     cluster->on_log([](const dpp::log_t& event) { | ||||
| 		if (event.severity >= settingsManager::getLOGLEVEL()) { | ||||
| 		if (event.severity >= SettingsManager::getLOGLEVEL()) { | ||||
| 			std::cout << "[" << dpp::utility::current_date_time() << "] " << dpp::utility::loglevel(event.severity) << ": " << event.message << "\n"; | ||||
| 		} | ||||
| 	}); | ||||
| @@ -24,22 +24,6 @@ BumbleBee::BumbleBee() { | ||||
|  | ||||
|     VersionsCheckUtils::validateYTDLPFFMPEGBinary(cluster); | ||||
|     VersionsCheckUtils::updateytdlp(cluster); | ||||
|  | ||||
|     musicManager = std::make_shared<MusicPlayManager>(cluster); | ||||
|  | ||||
|     commands["d"] = std::make_shared<commands::Delete>  (cluster->cluster_id, musicManager); | ||||
|     commands["l"] = std::make_shared<commands::Leave>   (cluster->cluster_id, musicManager); | ||||
|     commands["p"] = std::make_shared<commands::Play>    (cluster->cluster_id, musicManager); | ||||
|     commands["q"] = std::make_shared<commands::Queue>   (cluster->cluster_id, musicManager); | ||||
|     commands["r"] = std::make_shared<commands::Repeat>  (cluster->cluster_id, musicManager); | ||||
|     commands["s"] = std::make_shared<commands::Skip>    (cluster->cluster_id, musicManager); | ||||
|  | ||||
|     for (auto command : commands) { | ||||
|         for (auto aliase : command.second->aliases) { | ||||
|             commands[aliase] = std::shared_ptr<commands::ICommand>(command.second); | ||||
|             commands[aliase]->set_name(aliase); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void BumbleBee::start() { this->cluster->start(dpp::st_wait); } | ||||
| @@ -56,7 +40,16 @@ void BumbleBee::on_slashcommand(const dpp::slashcommand_t &event) { | ||||
| } | ||||
|  | ||||
| void BumbleBee::on_ready(const dpp::ready_t &event) { | ||||
|     cluster->log(dpp::loglevel::ll_info, "Bot ready."); | ||||
|     GIDs = event.guilds; | ||||
|     musicManager = std::make_shared<MusicPlayManager>(cluster, GIDs); | ||||
|  | ||||
|     commands["d"]       = std::make_shared<commands::Delete>  (cluster->cluster_id, musicManager); | ||||
|     commands["l"]       = std::make_shared<commands::Leave>   (cluster->cluster_id, musicManager); | ||||
|     commands["p"]       = std::make_shared<commands::Play>    (cluster->cluster_id, musicManager); | ||||
|     commands["q"]       = std::make_shared<commands::Queue>   (cluster->cluster_id, musicManager); | ||||
|     commands["r"]       = std::make_shared<commands::Repeat>  (cluster->cluster_id, musicManager); | ||||
|     commands["s"]       = std::make_shared<commands::Skip>    (cluster->cluster_id, musicManager); | ||||
|     commands["shuffle"] = std::make_shared<commands::Shuffle> (cluster->cluster_id, musicManager); | ||||
|      | ||||
|     if (event.guilds.size() == 0) { | ||||
|         cluster->log(dpp::loglevel::ll_info, "Bot is not on any server! Please invite this bot to any server."); | ||||
| @@ -64,18 +57,24 @@ void BumbleBee::on_ready(const dpp::ready_t &event) { | ||||
|     } | ||||
|      | ||||
|     if (dpp::run_once<struct register_bot_commands>()) { | ||||
|         if (settingsManager::getCLCOMMAND()) { | ||||
|         if (SettingsManager::getREGISTER_COMMAND()) { | ||||
|             cluster->log(dpp::loglevel::ll_info, "Clear Pre-installed commands"); | ||||
|             cluster->global_bulk_command_delete([](const dpp::confirmation_callback_t &t){ | ||||
|                 std::cout << "cleared Pre-installed commands. Please restart Bot" << std::endl; | ||||
|                 settingsManager::setCLCOMMAND(false); | ||||
|                 exit(0); | ||||
|  | ||||
|             cluster->global_bulk_command_create({ | ||||
|                 *commands["d"], | ||||
|                 *commands["l"], | ||||
|                 *commands["p"], | ||||
|                 *commands["q"], | ||||
|                 *commands["r"], | ||||
|                 *commands["s"], | ||||
|                 *commands["shuffle"] | ||||
|             }, [&](const dpp::confirmation_callback_t &t){ | ||||
|                 cluster->log(dpp::loglevel::ll_info, "Command created."); | ||||
|                 SettingsManager::setREGISTER_COMMAND(false); | ||||
|             }); | ||||
|             return; | ||||
|         } | ||||
|         for (auto command : commands) | ||||
|             cluster->global_command_create(*command.second); | ||||
|     } | ||||
|     cluster->log(dpp::loglevel::ll_info, "Command created."); | ||||
|  | ||||
|     cluster->log(dpp::loglevel::ll_info, "Bot ready."); | ||||
| } | ||||
| } | ||||
| @@ -1,11 +1,39 @@ | ||||
| #include <Commands/BumbleBeeCommand.hpp> | ||||
| #include <format> | ||||
|  | ||||
| namespace bumbleBee::commands { | ||||
|     void Delete::execute(const dpp::slashcommand_t &event) { | ||||
|         event.edit_original_response(dpp::message("delete")); | ||||
|         if (std::holds_alternative<std::monostate>(event.get_parameter("pos"))) // 여기 들어올 일 있나? | ||||
|         { | ||||
|             event.edit_original_response(dpp::message("위치를 제공하여 주십시오")); | ||||
|             event.reply(""); | ||||
|             return; | ||||
|         } | ||||
|         int pos = std::get<std::int64_t>(event.get_parameter("pos")); | ||||
|  | ||||
|         if (pos < 0 || pos > musicManager->size(event.command.guild_id)) | ||||
|         { | ||||
|  | ||||
|             event.edit_original_response(dpp::message(std::string("이상한 인덱스 위치. Pos :") + std::to_string(pos))); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         dpp::voiceconn* v = event.from->get_voice(event.command.guild_id); | ||||
|  | ||||
|         std::shared_ptr<MusicQueueElement> removed; | ||||
|          | ||||
|         if (!v) // v-> 로 nullptr을 참조하면 안 되므로. | ||||
|             removed = musicManager->remove(event.command.guild_id, nullptr, pos); | ||||
|         else | ||||
|             removed = musicManager->remove(event.command.guild_id, v->voiceclient, pos); | ||||
|  | ||||
|         dpp::message msg("다음 항목을 큐에서 삭제했습니다!:"); | ||||
|         msg.add_embed(removed->embed); | ||||
|  | ||||
|         event.edit_original_response(msg); | ||||
|     } | ||||
|  | ||||
|     void Delete::init() { | ||||
|         aliases.push_back("delete"); | ||||
|         add_option(dpp::command_option(dpp::co_integer, "pos", "지울 위치(위치는 1부터 시작, 현재 재생곡은 0)", true)); | ||||
|     } | ||||
| } | ||||
| @@ -1,8 +1,17 @@ | ||||
| #include <Commands/BumbleBeeCommand.hpp> | ||||
|  | ||||
| namespace bumbleBee::commands { | ||||
|     void Leave::execute(const dpp::slashcommand_t &event) { | ||||
|         event.edit_original_response(dpp::message("leave")); | ||||
|     void Leave::execute(const dpp::slashcommand_t &event) { // 왜 read loop ended가 뜨는가... | ||||
|         dpp::voiceconn* v = event.from->get_voice(event.command.guild_id); | ||||
|  | ||||
|         if (!v || !v->voiceclient || !v->voiceclient->is_ready()) { | ||||
|             return; | ||||
|         } | ||||
|         musicManager->clear(event.command.guild_id); | ||||
|          | ||||
|         event.from->disconnect_voice(event.command.guild_id); | ||||
|  | ||||
|         event.edit_original_response(dpp::message("음성 채팅방을 떠납니다!")); | ||||
|     } | ||||
|  | ||||
|     void Leave::init() { | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| #include <Commands/BumbleBeeCommand.hpp> | ||||
| #include <Utils/ConsoleUtils.hpp> | ||||
| #include <Settings/SettingsManager.hpp> | ||||
| #include <dpp/nlohmann/json.hpp> | ||||
| #include <variant> | ||||
|  | ||||
| namespace bumbleBee::commands { | ||||
| @@ -10,7 +12,7 @@ namespace bumbleBee::commands { | ||||
|             event.edit_original_response(dpp::message("GUILD NOT FOUND!! WHAT IS THIS SORCERY??")); | ||||
|             return; | ||||
|         } | ||||
|         if (std::holds_alternative<std::monostate>(event.get_parameter("query"))) | ||||
|         if (std::holds_alternative<std::monostate>(event.get_parameter("query"))) // 이거 필요한가??? | ||||
|         { | ||||
|             event.reply("노래를 재생하려면 검색어 또는 링크를 입력해 주십시오."); | ||||
|             return; | ||||
| @@ -21,11 +23,18 @@ namespace bumbleBee::commands { | ||||
|         } | ||||
|         std::string query = std::get<std::string>(event.get_parameter("query")); | ||||
|  | ||||
|         std::queue<std::string> ids = ConsoleUtils::getResultFromCommand("./yt-dlp --default-search ytsearch --flat-playlist --skip-download --quiet --ignore-errors --print id " + query); | ||||
|         std::queue<std::string> ids = ConsoleUtils::getResultFromCommand(SettingsManager::getYTDLP_CMD() + " --default-search ytsearch --flat-playlist --skip-download --quiet --ignore-errors --print id " + query); | ||||
|  | ||||
|         std::queue<MusicQueueElement> musics; | ||||
|         std::queue<std::shared_ptr<MusicQueueElement>> musics; | ||||
|  | ||||
|         dpp::message origMsg; | ||||
|         int initialQueuedCount = musicManager->size(event.command.guild_id); | ||||
|  | ||||
|         dpp::message msg; | ||||
|  | ||||
|         if (musicManager->size(event.command.guild_id) == 0) | ||||
|             msg.content = "다음 곡을 재생합니다:"; | ||||
|         else | ||||
|             msg.content = "큐에 다음 곡을 추가했습니다:"; | ||||
|  | ||||
|         if (ids.size() >= 2) { | ||||
|             event.from->creator->log(dpp::ll_info, "Playlist detected."); | ||||
| @@ -34,34 +43,122 @@ namespace bumbleBee::commands { | ||||
|                     ids.pop(); | ||||
|                     continue; | ||||
|                 } | ||||
|                 event.from->creator->log(dpp::ll_info, "Enqueuing " + ids.front()); | ||||
|  | ||||
|                 MusicQueueElement music( | ||||
|                     ids.front(), | ||||
|                     query, | ||||
|                     event.command.usr | ||||
|                 ); | ||||
|                 FILE* file = popen((SettingsManager::getYTDLP_CMD() + " --default-search ytsearch --flat-playlist --skip-download --quiet --ignore-errors -J http://youtu.be/" + ids.front()).c_str(), "r"); | ||||
|              | ||||
|                 std::ostringstream oss; | ||||
|                 char buffer[1024]; | ||||
|                 size_t bytesRead; | ||||
|                  | ||||
|                 while ((bytesRead = fread(buffer, 1, sizeof(buffer), file)) > 0) { | ||||
|                     oss.write(buffer, bytesRead); | ||||
|                 } | ||||
|                 fclose(file); | ||||
|  | ||||
|                 musics.push(music); | ||||
|                 std::istringstream iss(oss.str()); | ||||
|                 nlohmann::json videoDataJson; | ||||
|                 iss >> videoDataJson; | ||||
|  | ||||
|                 // std::string dump = videoDataJson.dump(4); | ||||
|  | ||||
|                 time_t SongLength = int(videoDataJson["duration"]); | ||||
|                 char SongLengthStr[10]; | ||||
|                 tm t; | ||||
|                 t.tm_mday = SongLength / 86400; | ||||
|                 t.tm_hour = (SongLength % 86400)/3600; | ||||
|                 t.tm_min = (SongLength % 3600)/60; | ||||
|                 t.tm_sec = SongLength%60; | ||||
|                 strftime(SongLengthStr, sizeof(SongLengthStr), "%X", &t); | ||||
|  | ||||
|                 dpp::embed embed = dpp::embed() | ||||
|                     .set_color(dpp::colors::sti_blue) | ||||
|                     .set_title(std::string(videoDataJson["title"])) | ||||
|                     .set_description(std::string(videoDataJson["uploader"])) | ||||
|                     .set_url(std::string(videoDataJson["webpage_url"])) | ||||
|                     .set_image(std::string(videoDataJson["thumbnail"])) | ||||
|                     .add_field( | ||||
|                         "길이", | ||||
|                         SongLengthStr, | ||||
|                         true | ||||
|                     ); | ||||
|  | ||||
|                 musics.push(std::make_shared<MusicQueueElement>(ids.front(), query, event.command.usr, embed)); | ||||
|                 ids.pop(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         MusicQueueElement music( | ||||
|             ids.front(), | ||||
|             query, | ||||
|             event.command.usr | ||||
|         ); | ||||
|         musics.push(music); | ||||
|         if (!ids.empty()) { | ||||
|             FILE* file = popen((SettingsManager::getYTDLP_CMD() + " --default-search ytsearch --flat-playlist --skip-download --quiet --ignore-errors -J http://youtu.be/" + ids.front()).c_str(), "r"); | ||||
|              | ||||
|             std::ostringstream oss; | ||||
|             char buffer[1024]; | ||||
|             size_t bytesRead; | ||||
|              | ||||
|             while ((bytesRead = fread(buffer, 1, sizeof(buffer), file)) > 0) { | ||||
|                 oss.write(buffer, bytesRead); | ||||
|             } | ||||
|             fclose(file); | ||||
|  | ||||
|         while (!musics.empty()) { | ||||
|             auto element = musics.front(); | ||||
|             musicManager->queue_music(std::make_shared<MusicQueueElement>(element)); | ||||
|             musics.pop(); | ||||
|             std::istringstream iss(oss.str()); | ||||
|             nlohmann::json videoDataJson; | ||||
|             iss >> videoDataJson; | ||||
|  | ||||
|             time_t SongLength = int(videoDataJson["duration"]); | ||||
|             char SongLengthStr[10]; | ||||
|             tm t; | ||||
|             t.tm_mday = SongLength / 86400; | ||||
|             t.tm_hour = (SongLength % 86400)/3600; | ||||
|             t.tm_min = (SongLength % 3600)/60; | ||||
|             t.tm_sec = SongLength%60; | ||||
|             strftime(SongLengthStr, sizeof(SongLengthStr), "%X", &t); | ||||
|  | ||||
|             dpp::embed embed = dpp::embed() | ||||
|                 .set_color(dpp::colors::sti_blue) | ||||
|                 .set_title(std::string(videoDataJson["title"])) | ||||
|                 .set_description(std::string(videoDataJson["uploader"])) | ||||
|                 .set_url(std::string(videoDataJson["webpage_url"])) | ||||
|                 .set_image(std::string(videoDataJson["thumbnail"])) | ||||
|                 .add_field( | ||||
|                     "길이", | ||||
|                     SongLengthStr, | ||||
|                     true | ||||
|                 ); | ||||
|  | ||||
|             musics.push(std::make_shared<MusicQueueElement>(ids.front(), query, event.command.usr, embed)); | ||||
|         } | ||||
|  | ||||
|         event.edit_original_response(dpp::message("play")); | ||||
|         musicManager->queuedCondition.notify_one(); | ||||
|         if (musics.size() == 1) { | ||||
|             event.from->creator->log(dpp::ll_info, "Enqueuing " + musics.front()->id); | ||||
|             musicManager->queue_music(event.command.guild_id, musics.front()); | ||||
|             msg.add_embed(musics.front()->embed); | ||||
|             musics.pop(); | ||||
|              | ||||
|             event.edit_original_response(msg); | ||||
|             musicManager->queuedCondition.notify_all(); | ||||
|         } | ||||
|         else if (musics.size() > 1) { | ||||
|             event.from->creator->log(dpp::ll_info, "Enqueuing " + musics.front()->id); | ||||
|             musicManager->queue_music(event.command.guild_id, musics.front()); | ||||
|             msg.add_embed(musics.front()->embed); | ||||
|             musics.pop(); | ||||
|  | ||||
|             event.edit_original_response(msg); | ||||
|             musicManager->queuedCondition.notify_all(); | ||||
|  | ||||
|             while (!musics.empty()) { | ||||
|                 event.from->creator->log(dpp::ll_info, "Enqueuing " + musics.front()->id); | ||||
|                 dpp::message followMsg(event.command.channel_id, ""); | ||||
|  | ||||
|                 followMsg.add_embed(musics.front()->embed); | ||||
|                 event.from->creator->message_create(followMsg); // 어차피 원래 메시지를 지정해서 수정할 것이기 때문에 먼저 팔로잉 메시지를 작성해도 상관없음. | ||||
|  | ||||
|                 musicManager->queue_music(event.command.guild_id, musics.front()); | ||||
|                 musics.pop(); | ||||
|             } | ||||
|         } | ||||
|         else { // ?? | ||||
|             event.from->creator->log(dpp::ll_error, "??? not queueed any music"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void Play::init() { | ||||
|   | ||||
| @@ -2,7 +2,31 @@ | ||||
|  | ||||
| namespace bumbleBee::commands { | ||||
|     void Queue::execute(const dpp::slashcommand_t &event) { | ||||
|         event.edit_original_response(dpp::message("queue")); | ||||
|         auto queue = musicManager->getQueue(event.command.guild_id); | ||||
|         auto nowplaying = musicManager->getNowPlaying(event.command.guild_id); | ||||
|  | ||||
|         dpp::message msg; | ||||
|         dpp::embed embed; | ||||
|  | ||||
|         if (queue.size() == 0) { | ||||
|             embed | ||||
|                 .set_title("큐가 비었습니다!") | ||||
|                 .set_timestamp(time(0)); | ||||
|             msg.add_embed(embed); | ||||
|             event.edit_original_response(msg); | ||||
|             return; | ||||
|         } | ||||
|         msg.content = "지금 재생 중:"; | ||||
|         msg.add_embed(nowplaying.embed); | ||||
|  | ||||
|         for (auto iter = queue.begin(); iter != queue.end(); iter++) { | ||||
|             dpp::message followMsg(event.command.channel_id, ""); | ||||
|  | ||||
|             followMsg.add_embed(iter->embed); | ||||
|             event.from->creator->message_create(followMsg); // 어차피 원래 메시지를 지정해서 수정할 것이기 때문에 먼저 팔로잉 메시지를 작성해도 상관없음. | ||||
|         } | ||||
|          | ||||
|         event.edit_original_response(msg); | ||||
|     } | ||||
|  | ||||
|     void Queue::init() { | ||||
|   | ||||
| @@ -2,9 +2,15 @@ | ||||
|  | ||||
| namespace bumbleBee::commands { | ||||
|     void Repeat::execute(const dpp::slashcommand_t &event) { | ||||
|         event.edit_original_response(dpp::message("repeat")); | ||||
|         if (musicManager->getRepeat(event.command.guild_id)) { | ||||
|             event.edit_original_response(dpp::message("반복을 껐습니다.")); | ||||
|             musicManager->setRepeat(event.command.guild_id, false); | ||||
|         } | ||||
|         else { | ||||
|             event.edit_original_response(dpp::message("반복을 켰습니다.")); | ||||
|             musicManager->setRepeat(event.command.guild_id, true); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void Repeat::init() { | ||||
|     } | ||||
|     void Repeat::init() {} | ||||
| } | ||||
| @@ -2,7 +2,17 @@ | ||||
|  | ||||
| namespace bumbleBee::commands { | ||||
|     void Skip::execute(const dpp::slashcommand_t &event) { | ||||
|         event.edit_original_response(dpp::message("skip")); | ||||
|         dpp::voiceconn* v = event.from->get_voice(event.command.guild_id); | ||||
|  | ||||
|         if (!v || !v->voiceclient || !v->voiceclient->is_ready()) { | ||||
|             return; | ||||
|         } | ||||
|         v->voiceclient->pause_audio(true); | ||||
|         v->voiceclient->stop_audio(); | ||||
|         v->voiceclient->pause_audio(false); | ||||
|         v->voiceclient->insert_marker("end"); | ||||
|  | ||||
|         event.edit_original_response(dpp::message("스킵했습니다!")); | ||||
|     } | ||||
|  | ||||
|     void Skip::init() { | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| #include <Queue/MusicQueue.hpp> | ||||
| #include <iostream> | ||||
| #include <algorithm> | ||||
|  | ||||
| namespace bumbleBee { | ||||
|  | ||||
| @@ -25,14 +26,23 @@ std::list<std::shared_ptr<MusicQueueElement>>::iterator MusicQueue::findById(std | ||||
|     } | ||||
|     return queue.end(); | ||||
| } | ||||
| std::list<std::shared_ptr<MusicQueueElement>>::iterator MusicQueue::findByIndex(int index) { | ||||
|     std::lock_guard<std::mutex> lock(queueMutex); | ||||
|     if (index < 0) | ||||
|         return queue.end(); | ||||
|     if (index == 0) | ||||
|         return currentPlayingPosition; | ||||
|     return std::next(queue.begin(), index - 1); | ||||
| } | ||||
| std::shared_ptr<MusicQueueElement> MusicQueue::nowplaying() { | ||||
|     std::lock_guard<std::mutex> lock(queueMutex); | ||||
|     return *currentPlayingPosition; | ||||
| } | ||||
| std::list<std::shared_ptr<MusicQueueElement>>::iterator MusicQueue::next_music() { | ||||
|     std::lock_guard<std::mutex> lock(queueMutex); | ||||
|     if (currentPlayingPosition == std::prev(queue.end()) || !repeat) | ||||
|     if (currentPlayingPosition == --queue.end() && !repeat) | ||||
|         return queue.end(); | ||||
|     if (currentPlayingPosition == std::prev(queue.end()) || repeat) | ||||
|     if (currentPlayingPosition == --queue.end() && repeat) | ||||
|         currentPlayingPosition = queue.begin(); | ||||
|     else | ||||
|         ++currentPlayingPosition; | ||||
| @@ -49,4 +59,39 @@ std::shared_ptr<MusicQueueElement> MusicQueue::jump_to_index(int idx) { | ||||
|     } | ||||
|     return std::shared_ptr<MusicQueueElement>(); | ||||
| } | ||||
| void MusicQueue::clear() { | ||||
|     std::lock_guard<std::mutex> lock(queueMutex); | ||||
|     queue.clear(); | ||||
|     currentPlayingPosition = queue.begin(); | ||||
| } | ||||
| std::shared_ptr<MusicQueueElement> MusicQueue::erase(std::list<std::shared_ptr<MusicQueueElement>>::iterator it) { | ||||
|     std::lock_guard<std::mutex> lock(queueMutex); | ||||
|     if (it == queue.end()) | ||||
|         return nullptr; | ||||
|      | ||||
|     if (currentPlayingPosition == it) { | ||||
|         auto removedValue = *it; | ||||
|         auto after = queue.erase(currentPlayingPosition++); | ||||
|         if (after == queue.end()) | ||||
|             currentPlayingPosition = --queue.end(); | ||||
|         return removedValue; | ||||
|     } | ||||
|     else { | ||||
|         auto removedValue = *it; | ||||
|         queue.erase(it); | ||||
|         return removedValue; | ||||
|     } | ||||
| } | ||||
| std::list<std::shared_ptr<MusicQueueElement>> MusicQueue::getQueueCopy(){ | ||||
|     std::lock_guard<std::mutex> lock(queueMutex); | ||||
|     std::list<std::shared_ptr<MusicQueueElement>> returnValue; | ||||
|  | ||||
|     std::copy(queue.begin(), queue.end(), std::back_inserter(returnValue)); | ||||
|  | ||||
|     return returnValue;  | ||||
| } | ||||
| int MusicQueue::size() { | ||||
|     std::lock_guard<std::mutex> lock(queueMutex); | ||||
|     return queue.size(); | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -6,13 +6,13 @@ | ||||
|  | ||||
| namespace bumbleBee { | ||||
|  | ||||
| std::string settingsManager::TOKEN = ""; | ||||
| std::string settingsManager::YTDLP_CMD = "./yt-dlp"; | ||||
| std::string settingsManager::FFMPEG_CMD = "./ffmpeg/bin/ffmpeg"; | ||||
| dpp::loglevel settingsManager::LOGLEVEL = dpp::ll_debug; | ||||
| bool settingsManager::CLCOMMAND = false; | ||||
| std::string SettingsManager::TOKEN = ""; | ||||
| std::string SettingsManager::YTDLP_CMD = "./yt-dlp"; | ||||
| std::string SettingsManager::FFMPEG_CMD = "./ffmpeg/bin/ffmpeg"; | ||||
| dpp::loglevel SettingsManager::LOGLEVEL = dpp::ll_debug; | ||||
| bool SettingsManager::REGISTER_COMMAND = false; | ||||
|  | ||||
| bool settingsManager::validateToken() { | ||||
| bool SettingsManager::validateToken() { | ||||
|     nlohmann::json response; | ||||
|     if (ConsoleUtils::getResultFromCommand("which curl").size() == 0) { | ||||
|         std::cout << "curl is unavaliable. unresolable error please install curl." << std::endl; | ||||
| @@ -32,7 +32,7 @@ bool settingsManager::validateToken() { | ||||
|         return true; | ||||
| } | ||||
|  | ||||
| bool settingsManager::load() { | ||||
| bool SettingsManager::load() { | ||||
|     nlohmann::json configdocument; | ||||
|     try { | ||||
|         std::ifstream configfile("config.json"); | ||||
| @@ -66,7 +66,7 @@ bool settingsManager::load() { | ||||
|         else // 값이 병신같을때 기본값으로 ll_info 부여 | ||||
|             LOGLEVEL = dpp::ll_info; | ||||
|  | ||||
|         CLCOMMAND = configdocument["CLEAR_PREVIOUS_COMMAND"]; | ||||
|         REGISTER_COMMAND = configdocument["CLEAR_PREVIOUS_COMMAND"]; | ||||
|     } | ||||
|     catch (const nlohmann::json::type_error& e) { | ||||
|         saveToFile(); | ||||
| @@ -79,7 +79,7 @@ bool settingsManager::load() { | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| void settingsManager::saveToFile() { | ||||
| void SettingsManager::saveToFile() { | ||||
|     nlohmann::json configdocument; | ||||
|  | ||||
|     configdocument["TOKEN"] = TOKEN; | ||||
| @@ -107,7 +107,7 @@ void settingsManager::saveToFile() { | ||||
|         break; | ||||
|     } | ||||
|      | ||||
|     configdocument["CLEAR_PREVIOUS_COMMAND"] = CLCOMMAND; | ||||
|     configdocument["CLEAR_PREVIOUS_COMMAND"] = REGISTER_COMMAND; | ||||
|  | ||||
|     std::ofstream configFile("config.json"); | ||||
|     if (configFile.is_open()) { | ||||
|   | ||||
| @@ -38,7 +38,7 @@ void AsyncDownloadManager::downloadWorker() { | ||||
|         cluster->log(dpp::ll_info, "AsyncDownloadManager: " + query + " accepted."); | ||||
|  | ||||
|         std::queue<std::string> ids = | ||||
|             ConsoleUtils::getResultFromCommand(settingsManager::getYTDLP_CMD() + | ||||
|             ConsoleUtils::getResultFromCommand(SettingsManager::getYTDLP_CMD() + | ||||
|             " --default-search ytsearch --flat-playlist --skip-download --quiet --ignore-errors --print id " + query); | ||||
|  | ||||
|         if (ids.size() >= 2) { | ||||
| @@ -56,7 +56,7 @@ void AsyncDownloadManager::downloadWorker() { | ||||
|         } | ||||
|  | ||||
|         std::queue<std::string> urls = | ||||
|             ConsoleUtils::getResultFromCommand(settingsManager::getYTDLP_CMD() + | ||||
|             ConsoleUtils::getResultFromCommand(SettingsManager::getYTDLP_CMD() + | ||||
|             " -f ba* --print urls https://youtu.be/" + ids.front()); | ||||
|  | ||||
|         cluster->log(dpp::ll_debug, "url: " + urls.front()); | ||||
| @@ -75,7 +75,7 @@ void AsyncDownloadManager::downloadWorker() { | ||||
|  | ||||
|             cluster->log(dpp::ll_info, "Thread id: " + tid.str() + ": " + downloadID + " accepted."); | ||||
|  | ||||
|             std::string command = std::string("./streamOpus.sh " + settingsManager::getYTDLP_CMD() + " " + downloadID + " " + settingsManager::getFFMPEG_CMD()); | ||||
|             std::string command = std::string("./streamOpus.sh " + SettingsManager::getYTDLP_CMD() + " " + downloadID + " " + SettingsManager::getFFMPEG_CMD()); | ||||
|             stream = popen(command.c_str(), "r"); | ||||
|  | ||||
|             cluster->log(dpp::ll_info, "Thread id: " + tid.str() + " Opened stream: " + downloadID); | ||||
|   | ||||
| @@ -4,9 +4,9 @@ YTDLP_CMD=$1 | ||||
| FFMPEG_CMD=$2 | ||||
| URL=$3 | ||||
|  | ||||
| $YTDLP_CMD -o - --quiet --ignore-errors -f bestaudio $URL | \ | ||||
| $FFMPEG_CMD -hwaccel vaapi -i - -hide_banner -c:a copy -f opus - || \ | ||||
| $YTDLP_CMD -o - --quiet --ignore-errors -f bestaudio $URL | \ | ||||
| $FFMPEG_CMD -hwaccel vaapi -i - -hide_banner -f opus -c:a libopus -b:a 128k -ar 48000 -ac 2 - | ||||
| $YTDLP_CMD -o - --quiet --ignore-errors -f bestaudio $URL 2>/dev/null | \ | ||||
| $FFMPEG_CMD -hwaccel vaapi -i - -loglevel quiet -hide_banner -c:a copy -f opus - || \ | ||||
| $YTDLP_CMD -o - --quiet --ignore-errors -f bestaudio $URL 2>/dev/null | \ | ||||
| $FFMPEG_CMD -hwaccel vaapi -i - -loglevel quiet -hide_banner -f opus -c:a libopus -b:a 128k -ar 48000 -ac 2 - | ||||
|  | ||||
| #  -loglevel quiet | ||||
		Reference in New Issue
	
	Block a user