Added file serving for downloaded files

This commit is contained in:
Leon Etienne (ubuntu wsl) 2020-09-27 18:51:53 +02:00
parent 7487dd517e
commit e470676e40
8 changed files with 129 additions and 151 deletions

21
Tubio/$$DL_PROG_BUF_FILE Normal file
View File

@ -0,0 +1,21 @@
[youtube] eJsTaFRsa_k: Downloading webpage
[youtube] Downloading just video eJsTaFRsa_k because of --no-playlist
[youtube] eJsTaFRsa_k: Downloading js player 12237e3d
[youtube] eJsTaFRsa_k: Downloading js player 12237e3d
[download] Destination: dlcache\download\1KmyoJ.webm
[download] 0.0% of 6.60MiB at 249.94KiB/s ETA 00:27
[download] 0.0% of 6.60MiB at 749.83KiB/s ETA 00:09
[download] 0.1% of 6.60MiB at 1.71MiB/s ETA 00:03
[download] 0.2% of 6.60MiB at 3.66MiB/s ETA 00:01
[download] 0.5% of 6.60MiB at 1.59MiB/s ETA 00:04
[download] 0.9% of 6.60MiB at 1.71MiB/s ETA 00:03
[download] 1.9% of 6.60MiB at 2.48MiB/s ETA 00:02
[download] 3.8% of 6.60MiB at 3.61MiB/s ETA 00:01
[download] 7.6% of 6.60MiB at 5.73MiB/s ETA 00:01
[download] 15.1% of 6.60MiB at 8.32MiB/s ETA 00:00
[download] 30.3% of 6.60MiB at 14.17MiB/s ETA 00:00
[download] 60.6% of 6.60MiB at 17.77MiB/s ETA 00:00
[download] 100.0% of 6.60MiB at 28.45MiB/s ETA 00:00
[download] 100% of 6.60MiB in 00:00
[ffmpeg] Destination: dlcache\download\1KmyoJ.mp3
Deleting original file dlcache\download\1KmyoJ.webm (pass -k to keep)

View File

@ -28,6 +28,7 @@ std::string DownloadManager::QueueDownload(std::string url, DOWNLOAD_MODE mode)
newDownload.tubio_id = tubioId; newDownload.tubio_id = tubioId;
newDownload.mode = mode; newDownload.mode = mode;
newDownload.download_progress = 0; newDownload.download_progress = 0;
newDownload.download_url = "/download/" + newDownload.tubio_id;
if (!IsJsonValid(jsString)) if (!IsJsonValid(jsString))
{ {
@ -145,35 +146,36 @@ void DownloadManager::DownloadNext()
std::stringstream ss; std::stringstream ss;
if (entry->mode == DOWNLOAD_MODE::VIDEO) if (entry->mode == DOWNLOAD_MODE::VIDEO)
{ {
std::string ytdl_call_video = std::string ytdl_call_video_base =
"youtube-dl --newline --no-call-home --no-playlist --limit-rate $$DL_RATE" "youtube-dl --newline --no-call-home --no-playlist --no-part --no-warnings --limit-rate $$DL_RATE"
" --no-mtime --no-cache-dir --format \"bestvideo[ext=mp4]+bestaudio/best[ext=mp4]/best\"" " --no-mtime --no-cache-dir --recode-video mp4 --format \"bestvideo[ext=mp4]+bestaudio/best[ext=mp4]/best\""
" --merge-output-format mp4 -o \"$$DL_FILE\" $$DL_URL > \"$$DL_PROG_BUF_FILE\""; " --merge-output-format mp4 -o \"$$DL_FILE\" $$DL_URL > \"$$DL_PROG_BUF_FILE\"";
ytdl_call_video = Internal::StringHelpers::Replace(ytdl_call_video, "$$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 = Internal::StringHelpers::Replace(ytdl_call_video, "$$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 = Internal::StringHelpers::Replace(ytdl_call_video, "$$DL_URL", entry->webpage_url); ytdl_call_video_base = Internal::StringHelpers::Replace(ytdl_call_video_base, "$$DL_URL", entry->webpage_url);
ytdl_call_video = Internal::StringHelpers::Replace(ytdl_call_video, "$$DL_PROG_BUF_FILE", XGConfig::downloader.cachedir + "/dlprogbuf/" + entry->tubio_id + ".buf"); ytdl_call_video_base = Internal::StringHelpers::Replace(ytdl_call_video_base, "$$DL_PROG_BUF_FILE", XGConfig::downloader.cachedir + "/dlprogbuf/" + entry->tubio_id + ".buf");
ss << ytdl_call_video; entry->downloaded_filename = XGConfig::downloader.cachedir + "/download/" + entry->tubio_id + ".mp4";
ss << ytdl_call_video_base;
} }
else // DOWNLOAD_MODE::AUDIO else // DOWNLOAD_MODE::AUDIO
{ {
std::string ytdl_call_audio = std::string ytdl_call_audio_base =
"youtube-dl --newline --no-call-home --no-playlist --limit-rate $$DL_RATE" "youtube-dl --newline --no-call-home --no-playlist --no-part --no-warnings --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 --audio-format mp3 --audio-quality 0 --extract-audio -o \"$$DL_FILE\""
" $$DL_URL > \"$$DL_PROG_BUF_FILE\""; " $$DL_URL > \"$$DL_PROG_BUF_FILE\"";
ytdl_call_audio = Internal::StringHelpers::Replace(ytdl_call_audio, "$$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 = Internal::StringHelpers::Replace(ytdl_call_audio, "$$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 = Internal::StringHelpers::Replace(ytdl_call_audio, "$$DL_URL", entry->webpage_url); ytdl_call_audio_base = Internal::StringHelpers::Replace(ytdl_call_audio_base, "$$DL_URL", entry->webpage_url);
ytdl_call_audio = Internal::StringHelpers::Replace(ytdl_call_audio, "$$DL_PROG_BUF_FILE", XGConfig::downloader.cachedir + "/dlprogbuf/" + entry->tubio_id + ".buf"); ytdl_call_audio_base = Internal::StringHelpers::Replace(ytdl_call_audio_base, "$$DL_PROG_BUF_FILE", XGConfig::downloader.cachedir + "/dlprogbuf/" + entry->tubio_id + ".buf");
ss << ytdl_call_audio; entry->downloaded_filename = XGConfig::downloader.cachedir + "/download/" + entry->tubio_id + ".mp3";
ss << ytdl_call_audio_base;
} }
int returnCode = system(ss.str().c_str()); int returnCode = system(ss.str().c_str());
std::cout << returnCode << std::endl;
if (returnCode == 0) if (returnCode == 0)
{ {
@ -263,6 +265,26 @@ JsonArray DownloadManager::GetQueueAsJson()
return arr; return arr;
} }
bool DownloadManager::DoesTubioIDExist(std::string tubioId)
{
for (std::size_t i = 0; i < queue.size(); i++)
{
if (queue[i].tubio_id == tubioId) return true;
}
return false;
}
DownloadEntry& DownloadManager::GetDownloadEntryByTubioID(std::string tubioId)
{
for (std::size_t i = 0; i < queue.size(); i++)
{
if (queue[i].tubio_id == tubioId) return queue[i];
}
throw std::exception("TubioID not found!");
std::terminate();
}
bool DownloadManager::ClearDownloadCache() bool DownloadManager::ClearDownloadCache()
{ {
if (GetNumActiveDownloads() == 0) if (GetNumActiveDownloads() == 0)
@ -397,6 +419,16 @@ void DownloadManager::Load()
newEntry.thumbnail_url = iter["thumbnail_url"]; newEntry.thumbnail_url = iter["thumbnail_url"];
} }
if ((iter.DoesExist("download_url")) && (iter["download_url"].GetDataType() == JDType::STRING))
{
newEntry.download_url = iter["download_url"];
}
if ((iter.DoesExist("downloaded_filename")) && (iter["downloaded_filename"].GetDataType() == JDType::STRING))
{
newEntry.downloaded_filename = iter["downloaded_filename"];
}
if ((iter.DoesExist("mode")) && (iter["mode"].GetDataType() == JDType::STRING)) if ((iter.DoesExist("mode")) && (iter["mode"].GetDataType() == JDType::STRING))
{ {
std::string cachedStrMode = iter["mode"]; std::string cachedStrMode = iter["mode"];
@ -507,6 +539,8 @@ JsonBlock DownloadEntry::GetAsJson()
jb.Set(Ele("webpage_url", webpage_url)); jb.Set(Ele("webpage_url", webpage_url));
jb.Set(Ele("thumbnail_url", thumbnail_url)); jb.Set(Ele("thumbnail_url", thumbnail_url));
jb.Set(Ele("download_progress", download_progress)); jb.Set(Ele("download_progress", download_progress));
jb.Set(Ele("downloaded_filename", downloaded_filename));
jb.Set(Ele("download_url", download_url));
switch (mode) switch (mode)
{ {

View File

@ -34,6 +34,8 @@ namespace Downloader
std::string tubio_id; std::string tubio_id;
std::string webpage_url; std::string webpage_url;
std::string thumbnail_url; std::string thumbnail_url;
std::string downloaded_filename;
std::string download_url;
DOWNLOAD_STATUS status; DOWNLOAD_STATUS status;
DOWNLOAD_MODE mode; DOWNLOAD_MODE mode;
int download_progress; int download_progress;
@ -64,8 +66,24 @@ namespace Downloader
/// <returns></returns> /// <returns></returns>
static std::size_t GetQueueLength(); static std::size_t GetQueueLength();
/// <summary>
/// Will return the whole queue in json format
/// </summary>
/// <returns></returns>
static JasonPP::JsonArray GetQueueAsJson(); static JasonPP::JsonArray GetQueueAsJson();
/// <summary>
/// Returns whether or not a tubio id exists
/// </summary>
/// <param name="tubioId">The id to check</param>
static bool DoesTubioIDExist(std::string tubioId);
/// <summary>
/// Returns a reference to a DownloadEntry by its tubio id
/// </summary>
/// <param name="tubioId">The corresponding tubio id</param>
static DownloadEntry& GetDownloadEntryByTubioID(std::string tubioId);
/// <summary> /// <summary>
/// Will delete all cached downloads! /// Will delete all cached downloads!
/// If downloads are currently active, tubio will wait for them to finish and return false! /// If downloads are currently active, tubio will wait for them to finish and return false!

View File

@ -88,6 +88,10 @@ void HttpServer::EventHandler(mg_connection* pNc, int ev, void* p)
{ {
ProcessAPIRequest(pNc, ev, p); ProcessAPIRequest(pNc, ev, p);
} }
else if (requestedUri.substr(0, 9) == "/download")
{
ServeDownloadedResource(pNc, ev, p, requestedUri);
}
else else
{ {
// Just serve the files requested // Just serve the files requested
@ -103,7 +107,7 @@ void HttpServer::EventHandler(mg_connection* pNc, int ev, void* p)
catch (...) catch (...)
{ {
Json j; Json j;
j.CloneFrom(RestResponseTemplates::GetByCode(INTERNAL_SERVER_ERROR, "Das not gud")); j.CloneFrom(RestResponseTemplates::GetByCode(INTERNAL_SERVER_ERROR, "Das not good"));
ServeStringToConnection(pNc, j.Render(), INTERNAL_SERVER_ERROR); ServeStringToConnection(pNc, j.Render(), INTERNAL_SERVER_ERROR);
} }
@ -147,6 +151,40 @@ void HttpServer::ProcessAPIRequest(mg_connection* pNc, int ev, void* p)
return; return;
} }
void HttpServer::ServeDownloadedResource(mg_connection* pNc, int ev, void* p, std::string uri)
{
std::string fileId = uri.substr(10, uri.length() - 10);
if (Downloader::DownloadManager::DoesTubioIDExist(fileId))
{
Downloader::DownloadEntry& entry = Downloader::DownloadManager::GetDownloadEntryByTubioID(fileId);
if (entry.status == Downloader::DOWNLOAD_STATUS::FINISHED)
{
std::stringstream ss;
std::string downloadedFilename = entry.title + (entry.mode == Downloader::DOWNLOAD_MODE::AUDIO ? ".mp3" : ".mp4");
ss << "Access-Control-Allow-Origin: *\nContent-Disposition: attachment; filename=\"" << downloadedFilename << "\"\nPragma: public\nCache-Control: must-revalidate, post-check=0, pre-check=0";
mg_http_serve_file(pNc, (http_message*)p, entry.downloaded_filename.c_str(), mg_mk_str("application/octet-stream"), mg_mk_str(ss.str().c_str()));
}
else
{
Json j;
j.CloneFrom(RestResponseTemplates::GetByCode(BAD_REQUEST, "File download not ready!"));
ServeStringToConnection(pNc, j.Render(), BAD_REQUEST);
}
}
else
{
Json j;
j.CloneFrom(RestResponseTemplates::GetByCode(BAD_REQUEST, "Invalid file id!"));
ServeStringToConnection(pNc, j.Render(), BAD_REQUEST);
}
return;
}
void HttpServer::OnExit() void HttpServer::OnExit()
{ {
log->cout << "Shutting down http-server..."; log->cout << "Shutting down http-server...";

View File

@ -23,6 +23,7 @@ namespace Rest
private: private:
bool InitWebServer(); bool InitWebServer();
static void ProcessAPIRequest(struct mg_connection* pNc, int ev, void* p); static void ProcessAPIRequest(struct mg_connection* pNc, int ev, void* p);
static void ServeDownloadedResource(struct mg_connection* pNc, int ev, void* p, std::string uri);
static void EventHandler(struct mg_connection* pNc, int ev, void* p); static void EventHandler(struct mg_connection* pNc, int ev, void* p);
static void ServeStringToConnection(struct mg_connection* c, std::string str, int httpStatusCode = 200); static void ServeStringToConnection(struct mg_connection* c, std::string str, int httpStatusCode = 200);

View File

@ -1,32 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello, World!</title>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<h1 id="headline">Hello, World!</h1>
<div class="button-wrapper">
<div class="button" id="btn_foo">
<p>
Foo!
</p>
</div>
<div class="button" id="btn_killserver">
<p>
Kill server
</p>
</div>
</div>
<script src="/script.js"></script>
</body>
</html>

View File

@ -1,27 +0,0 @@
$(".button").mousedown(function(e){
// Animations
e.target.classList.add("clicked");
setTimeout(function(){
e.target.classList.remove("clicked");
}, 100);
});
$("#btn_foo").click(function(e){
// Functionality
axios.post("/api", {
request: "foo"
}).then(function(response){
// Do something with the servers response
});
});
$("#btn_killserver").click(function(e){
// Functionality
axios.post("/api", {
request: "kill_yourself"
}).then(function(response){
if (response.data.status == "OK") {
$("#headline").html("Sever's dead");
}
});
});

View File

@ -1,75 +0,0 @@
body {
background-color: #00aa77;
}
h1,
h2,
h3,
h4,
p {
font-family: sans-serif;
color: #fff;
}
h1 {
font-size: 38pt;
text-align: center;
}
.button-wrapper {
display: flex;
width: 100%;
height: 100%;
align-items: center;
flex-direction: column;
}
.button {
background-color: #ee8800;
padding: 0.5em 8em;
width: 200px;
border-radius: 1em;
cursor: pointer;
text-align: center;
user-select: none;
transition:
background-color 0.2s,
transform 0.2s;
}
@media (max-width: 600px) {
.button {
width: 80%;
padding: 0.15em 3em;
}
}
.button:first-child
{
margin-top: 5em;
}
.button:not(:first-child)
{
margin-top: 2em;
}
@media (min-width: 600px) {
.button:hover {
background-color: #ffaa00;
transform: scale(1.05);
}
}
.button.clicked {
background-color: #cc6600;
transform: scale(0.95);
}
.button p{
font-size: 24pt;
}
.button * {
pointer-events: none;
}