From c66883efd66cc984feece8f5a023aff9fbd51360 Mon Sep 17 00:00:00 2001 From: Leonetienne Date: Fri, 12 Mar 2021 22:55:47 +0100 Subject: [PATCH] Added backend capability to select specific download qualities --- Tubio/DownloadManager.cpp | 55 +++++++++++++++++++++++++++++++++----- Tubio/DownloadManager.h | 28 ++++++++++++++++++- Tubio/RestQueryHandler.cpp | 35 ++++++++++++++++++++++-- Tubio/Version.h | 2 +- 4 files changed, 109 insertions(+), 11 deletions(-) diff --git a/Tubio/DownloadManager.cpp b/Tubio/DownloadManager.cpp index 35f08ed..50c7dfe 100644 --- a/Tubio/DownloadManager.cpp +++ b/Tubio/DownloadManager.cpp @@ -17,22 +17,28 @@ void DownloadManager::PreInit() return; } -std::string DownloadManager::QueueDownload(std::string url, DOWNLOAD_MODE mode) +std::string DownloadManager::QueueDownload(std::string url, DOWNLOAD_MODE mode, DOWNLOAD_QUALITY quality) { + // Create uniquie tubio id std::string tubioId = CreateNewTubioID(); - FetchInformation(url, tubioId); + // Fetch metadata + FetchInformation(url, tubioId); std::string jsString = FileSystem::ReadFile(XGConfig::downloader.cachedir + "/metadata/" + tubioId + ".json"); + // Create download entry structure DownloadEntry newDownload; newDownload.tubio_id = tubioId; newDownload.mode = mode; + newDownload.quality = quality; newDownload.download_progress = 0; newDownload.queued_timestamp = time(0); - newDownload.download_url = "/download/" + newDownload.tubio_id; + newDownload.download_url = "download/" + newDownload.tubio_id; + // Check for missing dependencies WarnIfMissingDependenciesWIN(); + // Interpret metadata if (!IsJsonValid(jsString)) { newDownload.status = DOWNLOAD_STATUS::FAILED; @@ -92,6 +98,7 @@ std::string DownloadManager::QueueDownload(std::string url, DOWNLOAD_MODE mode) } } + // Add to list of unfinished downloads unfinishedCache.push_back(newDownload); return tubioId; @@ -146,16 +153,19 @@ void DownloadManager::DownloadNext() std::thread* downloadThread = new std::thread([=]() { DownloadEntry* entry = next; + std::string tubioId = entry->tubio_id; std::stringstream ss; if (entry->mode == DOWNLOAD_MODE::VIDEO) { - std::string ytdl_call_video_base = + // Call template + std::string ytdl_call_video_base = "youtube-dl --newline --no-call-home --no-playlist --no-part --no-warnings --socket-timeout 5 --limit-rate $$DL_RATE" - " --no-mtime --no-cache-dir --recode-video mp4 --prefer-ffmpeg" + " --no-mtime --no-cache-dir -f $$QUALITY --recode-video mp4 --prefer-ffmpeg" " -o \"$$DL_FILE\" \"$$DL_URL\" > \"$$DL_PROG_BUF_FILE\""; - + // Fill template + ytdl_call_video_base = Internal::StringHelpers::Replace(ytdl_call_video_base, "$$QUALITY", DownloadQualityToStringParams(entry->quality)); ytdl_call_video_base = Internal::StringHelpers::Replace(ytdl_call_video_base, "$$DL_RATE", XGConfig::downloader.max_dlrate_per_thread); ytdl_call_video_base = Internal::StringHelpers::Replace(ytdl_call_video_base, "$$DL_FILE", XGConfig::downloader.cachedir + "/download/" + entry->tubio_id + ".%(ext)s"); ytdl_call_video_base = Internal::StringHelpers::Replace(ytdl_call_video_base, "$$DL_URL", entry->webpage_url); @@ -167,11 +177,15 @@ void DownloadManager::DownloadNext() } else // DOWNLOAD_MODE::AUDIO { + // Call template std::string ytdl_call_audio_base = "youtube-dl --newline --no-call-home --no-playlist --no-part --no-warnings --socket-timeout 5 --limit-rate $$DL_RATE" - " --no-mtime --no-cache-dir --audio-format mp3 --audio-quality 0 --extract-audio -o \"$$DL_FILE\"" + " --no-mtime --no-cache-dir -f $$QUALITY --audio-format mp3 --audio-quality 0 --extract-audio -o \"$$DL_FILE\"" " \"$$DL_URL\" > \"$$DL_PROG_BUF_FILE\""; + + // Fill template + ytdl_call_audio_base = Internal::StringHelpers::Replace(ytdl_call_audio_base, "$$QUALITY", DownloadQualityToStringParams(entry->quality)); ytdl_call_audio_base = Internal::StringHelpers::Replace(ytdl_call_audio_base, "$$DL_RATE", XGConfig::downloader.max_dlrate_per_thread); ytdl_call_audio_base = Internal::StringHelpers::Replace(ytdl_call_audio_base, "$$DL_FILE", XGConfig::downloader.cachedir + "/download/" + entry->tubio_id + ".%(ext)s"); ytdl_call_audio_base = Internal::StringHelpers::Replace(ytdl_call_audio_base, "$$DL_URL", entry->webpage_url); @@ -182,13 +196,21 @@ void DownloadManager::DownloadNext() ss << ytdl_call_audio_base; } + // This call takes a good while and is run in a seperate thread (look a few lines above. The lambda function) int returnCode = system(ss.str().c_str()); + // Fetch new instance + // Don't ask me why the old one isn't valid anymore -.- + for (std::size_t i = 0; i < unfinishedCache.size(); i++) + if (unfinishedCache[i].tubio_id == tubioId) + entry = &unfinishedCache[i]; + if (returnCode == 0) { // Download succeeded entry->status = DOWNLOAD_STATUS::FINISHED; entry->download_progress = 100; + shouldSave = true; } else @@ -569,6 +591,25 @@ std::vector DownloadManager::ParseJsonArrayToEntries(const JasonP return entries; } +std::string DownloadManager::DownloadQualityToStringParams(DOWNLOAD_QUALITY quality) +{ + switch (quality) + { + case DOWNLOAD_QUALITY::_BEST: + return "bestvideo[ext=mp4]+bestaudio"; + case DOWNLOAD_QUALITY::_1080p: + return "bestvideo[ext=mp4][height^<=1080]+bestaudio"; + case DOWNLOAD_QUALITY::_720p: + return "bestvideo[ext=mp4][height^<=720]+bestaudio"; + case DOWNLOAD_QUALITY::_360p: + return "bestvideo[ext=mp4][height^<=360]+bestaudio"; + case DOWNLOAD_QUALITY::_144p: + return "bestvideo[ext=mp4][height^<=144]+bestaudio"; + } + + return std::string(); +} + void DownloadManager::FetchInformation(std::string url, std::string tubId) { std::stringstream ss; diff --git a/Tubio/DownloadManager.h b/Tubio/DownloadManager.h index 571e80b..bccbd09 100644 --- a/Tubio/DownloadManager.h +++ b/Tubio/DownloadManager.h @@ -23,6 +23,14 @@ namespace Downloader FINISHED, FAILED }; + enum class DOWNLOAD_QUALITY + { + _BEST, // best quality + _1080p, // 1080p + _720p, // 720p + _360p, // 360p + _144p // 144p + }; class DownloadEntry { @@ -41,6 +49,7 @@ namespace Downloader std::string download_url; DOWNLOAD_STATUS status; DOWNLOAD_MODE mode; + DOWNLOAD_QUALITY quality; int download_progress; time_t queued_timestamp; @@ -61,7 +70,7 @@ namespace Downloader /// /// If video or audio /// Tubio download id - static std::string QueueDownload(std::string url, DOWNLOAD_MODE mode); + static std::string QueueDownload(std::string url, DOWNLOAD_MODE mode, DOWNLOAD_QUALITY quality = DOWNLOAD_QUALITY::_BEST); /// /// Returns the number of videos queued @@ -107,7 +116,24 @@ namespace Downloader static void Load(); static std::vector ParseJsonArrayToEntries(const JasonPP::JsonArray& arr); + /// + /// Will return a youtube-dl quality string based on 'quality' + /// + /// + /// + static std::string DownloadQualityToStringParams(DOWNLOAD_QUALITY quality); + + /// + /// Will fetch metadata of an url + /// + /// Url to fetch from + /// Tubio id to save data to static void FetchInformation(std::string url, std::string tubId); + + /// + /// Will create an unique tubio id (based on time()) + /// + /// Unique tubio id static std::string CreateNewTubioID(); /// diff --git a/Tubio/RestQueryHandler.cpp b/Tubio/RestQueryHandler.cpp index 3f24b77..59b846a 100644 --- a/Tubio/RestQueryHandler.cpp +++ b/Tubio/RestQueryHandler.cpp @@ -73,6 +73,7 @@ bool RestQueryHandler::Example_Foo(const JsonBlock& request, JsonBlock& response bool RestQueryHandler::QueueDownload(const JsonBlock& request, JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode) { + // Fetch parameters if ((!ValidateField("video_url", JDType::STRING, request, responseBody)) || (!ValidateField("mode", JDType::STRING, request, responseBody))) { @@ -80,8 +81,21 @@ bool RestQueryHandler::QueueDownload(const JsonBlock& request, JsonBlock& respon return false; } - std::string modeParam = request.Get("mode").AsString; std::string videoUrl = request.Get("video_url").AsString; + std::string modeParam = request.Get("mode").AsString; + std::string qualityParam; + + // 'quality' is an optional parameter. Default value is 'best' + if ((ValidateField("quality", JDType::STRING, request, responseBody))) + { + qualityParam = request.Get("quality").AsString; + } + else + { + qualityParam = "best"; + } + + // Process parameters DOWNLOAD_MODE mode; if (modeParam == "video") mode = DOWNLOAD_MODE::VIDEO; else if (modeParam == "audio") mode = DOWNLOAD_MODE::AUDIO; @@ -92,10 +106,27 @@ bool RestQueryHandler::QueueDownload(const JsonBlock& request, JsonBlock& respon return false; } + DOWNLOAD_QUALITY quality; + if (qualityParam == "best") + quality = DOWNLOAD_QUALITY::_BEST; + else if (qualityParam == "1080p") + quality = DOWNLOAD_QUALITY::_1080p; + else if (qualityParam == "720p") + quality = DOWNLOAD_QUALITY::_720p; + else if (qualityParam == "360p") + quality = DOWNLOAD_QUALITY::_360p; + else if (qualityParam == "144p") + quality = DOWNLOAD_QUALITY::_144p; + else { + responseCode = HTTP_STATUS_CODE::BAD_REQUEST; + responseBody.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "Parameter 'quality' is of wrong value. Choose either 'best', '1080p', '720p', '360p', or '144p'.")); + return false; + } + log->cout << "Queued video \"" << videoUrl << "\"..."; log->Flush(); - std::string tubId = DownloadManager::QueueDownload(videoUrl, mode); + std::string tubId = DownloadManager::QueueDownload(videoUrl, mode, quality); responseCode = HTTP_STATUS_CODE::OK; responseBody.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::OK)); diff --git a/Tubio/Version.h b/Tubio/Version.h index e293521..2c28e98 100644 --- a/Tubio/Version.h +++ b/Tubio/Version.h @@ -1,2 +1,2 @@ #pragma once -#define TUBIO_SERVER_VERSION (0.537) +#define TUBIO_SERVER_VERSION (0.538)