Added file serving for downloaded files
This commit is contained in:
parent
7487dd517e
commit
e470676e40
21
Tubio/$$DL_PROG_BUF_FILE
Normal file
21
Tubio/$$DL_PROG_BUF_FILE
Normal 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)
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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!
|
||||||
|
@ -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...";
|
||||||
|
@ -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);
|
||||||
|
@ -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>
|
|
@ -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");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
@ -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;
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user