Added backend capability to select specific download qualities

This commit is contained in:
Leonetienne 2021-03-12 22:55:47 +01:00
parent 818213da50
commit c66883efd6
4 changed files with 109 additions and 11 deletions

View File

@ -17,22 +17,28 @@ void DownloadManager::PreInit()
return; 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(); std::string tubioId = CreateNewTubioID();
FetchInformation(url, tubioId);
// Fetch metadata
FetchInformation(url, tubioId);
std::string jsString = FileSystem::ReadFile(XGConfig::downloader.cachedir + "/metadata/" + tubioId + ".json"); std::string jsString = FileSystem::ReadFile(XGConfig::downloader.cachedir + "/metadata/" + tubioId + ".json");
// Create download entry structure
DownloadEntry newDownload; DownloadEntry newDownload;
newDownload.tubio_id = tubioId; newDownload.tubio_id = tubioId;
newDownload.mode = mode; newDownload.mode = mode;
newDownload.quality = quality;
newDownload.download_progress = 0; newDownload.download_progress = 0;
newDownload.queued_timestamp = time(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(); WarnIfMissingDependenciesWIN();
// Interpret metadata
if (!IsJsonValid(jsString)) if (!IsJsonValid(jsString))
{ {
newDownload.status = DOWNLOAD_STATUS::FAILED; 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); unfinishedCache.push_back(newDownload);
return tubioId; return tubioId;
@ -146,16 +153,19 @@ void DownloadManager::DownloadNext()
std::thread* downloadThread = new std::thread([=]() { std::thread* downloadThread = new std::thread([=]() {
DownloadEntry* entry = next; DownloadEntry* entry = next;
std::string tubioId = entry->tubio_id;
std::stringstream ss; std::stringstream ss;
if (entry->mode == DOWNLOAD_MODE::VIDEO) 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" "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\""; " -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_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_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); 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 else // DOWNLOAD_MODE::AUDIO
{ {
// Call template
std::string ytdl_call_audio_base = 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" "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\""; " \"$$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_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_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); 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; 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()); 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) if (returnCode == 0)
{ {
// Download succeeded // Download succeeded
entry->status = DOWNLOAD_STATUS::FINISHED; entry->status = DOWNLOAD_STATUS::FINISHED;
entry->download_progress = 100; entry->download_progress = 100;
shouldSave = true; shouldSave = true;
} }
else else
@ -569,6 +591,25 @@ std::vector<DownloadEntry> DownloadManager::ParseJsonArrayToEntries(const JasonP
return entries; 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) void DownloadManager::FetchInformation(std::string url, std::string tubId)
{ {
std::stringstream ss; std::stringstream ss;

View File

@ -23,6 +23,14 @@ namespace Downloader
FINISHED, FINISHED,
FAILED FAILED
}; };
enum class DOWNLOAD_QUALITY
{
_BEST, // best quality
_1080p, // 1080p
_720p, // 720p
_360p, // 360p
_144p // 144p
};
class DownloadEntry class DownloadEntry
{ {
@ -41,6 +49,7 @@ namespace Downloader
std::string download_url; std::string download_url;
DOWNLOAD_STATUS status; DOWNLOAD_STATUS status;
DOWNLOAD_MODE mode; DOWNLOAD_MODE mode;
DOWNLOAD_QUALITY quality;
int download_progress; int download_progress;
time_t queued_timestamp; time_t queued_timestamp;
@ -61,7 +70,7 @@ namespace Downloader
/// <param name="url"></param> /// <param name="url"></param>
/// <param name="mode">If video or audio</param> /// <param name="mode">If video or audio</param>
/// <returns>Tubio download id</returns> /// <returns>Tubio download id</returns>
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);
/// <summary> /// <summary>
/// Returns the number of videos queued /// Returns the number of videos queued
@ -107,7 +116,24 @@ namespace Downloader
static void Load(); static void Load();
static std::vector<DownloadEntry> ParseJsonArrayToEntries(const JasonPP::JsonArray& arr); static std::vector<DownloadEntry> ParseJsonArrayToEntries(const JasonPP::JsonArray& arr);
/// <summary>
/// Will return a youtube-dl quality string based on 'quality'
/// </summary>
/// <param name="quality"></param>
/// <returns></returns>
static std::string DownloadQualityToStringParams(DOWNLOAD_QUALITY quality);
/// <summary>
/// Will fetch metadata of an url
/// </summary>
/// <param name="url">Url to fetch from</param>
/// <param name="tubId">Tubio id to save data to</param>
static void FetchInformation(std::string url, std::string tubId); static void FetchInformation(std::string url, std::string tubId);
/// <summary>
/// Will create an unique tubio id (based on time())
/// </summary>
/// <returns>Unique tubio id</returns>
static std::string CreateNewTubioID(); static std::string CreateNewTubioID();
/// <summary> /// <summary>

View File

@ -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) bool RestQueryHandler::QueueDownload(const JsonBlock& request, JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode)
{ {
// Fetch parameters
if ((!ValidateField("video_url", JDType::STRING, request, responseBody)) || if ((!ValidateField("video_url", JDType::STRING, request, responseBody)) ||
(!ValidateField("mode", JDType::STRING, request, responseBody))) (!ValidateField("mode", JDType::STRING, request, responseBody)))
{ {
@ -80,8 +81,21 @@ bool RestQueryHandler::QueueDownload(const JsonBlock& request, JsonBlock& respon
return false; return false;
} }
std::string modeParam = request.Get("mode").AsString;
std::string videoUrl = request.Get("video_url").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; DOWNLOAD_MODE mode;
if (modeParam == "video") mode = DOWNLOAD_MODE::VIDEO; if (modeParam == "video") mode = DOWNLOAD_MODE::VIDEO;
else if (modeParam == "audio") mode = DOWNLOAD_MODE::AUDIO; else if (modeParam == "audio") mode = DOWNLOAD_MODE::AUDIO;
@ -92,10 +106,27 @@ bool RestQueryHandler::QueueDownload(const JsonBlock& request, JsonBlock& respon
return false; 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->cout << "Queued video \"" << videoUrl << "\"...";
log->Flush(); log->Flush();
std::string tubId = DownloadManager::QueueDownload(videoUrl, mode); std::string tubId = DownloadManager::QueueDownload(videoUrl, mode, quality);
responseCode = HTTP_STATUS_CODE::OK; responseCode = HTTP_STATUS_CODE::OK;
responseBody.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::OK)); responseBody.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::OK));

View File

@ -1,2 +1,2 @@
#pragma once #pragma once
#define TUBIO_SERVER_VERSION (0.537) #define TUBIO_SERVER_VERSION (0.538)