diff --git a/Tubio/$$DL_PROG_BUF_FILE b/Tubio/$$DL_PROG_BUF_FILE new file mode 100644 index 0000000..6221ed3 --- /dev/null +++ b/Tubio/$$DL_PROG_BUF_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) diff --git a/Tubio/DownloadManager.cpp b/Tubio/DownloadManager.cpp index 92cc255..48705b7 100644 --- a/Tubio/DownloadManager.cpp +++ b/Tubio/DownloadManager.cpp @@ -28,6 +28,7 @@ std::string DownloadManager::QueueDownload(std::string url, DOWNLOAD_MODE mode) newDownload.tubio_id = tubioId; newDownload.mode = mode; newDownload.download_progress = 0; + newDownload.download_url = "/download/" + newDownload.tubio_id; if (!IsJsonValid(jsString)) { @@ -145,35 +146,36 @@ void DownloadManager::DownloadNext() std::stringstream ss; if (entry->mode == DOWNLOAD_MODE::VIDEO) { - std::string ytdl_call_video = - "youtube-dl --newline --no-call-home --no-playlist --limit-rate $$DL_RATE" - " --no-mtime --no-cache-dir --format \"bestvideo[ext=mp4]+bestaudio/best[ext=mp4]/best\"" + std::string ytdl_call_video_base = + "youtube-dl --newline --no-call-home --no-playlist --no-part --no-warnings --limit-rate $$DL_RATE" + " --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\""; - ytdl_call_video = Internal::StringHelpers::Replace(ytdl_call_video, "$$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 = Internal::StringHelpers::Replace(ytdl_call_video, "$$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_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); + 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 { - std::string ytdl_call_audio = - "youtube-dl --newline --no-call-home --no-playlist --limit-rate $$DL_RATE" + std::string ytdl_call_audio_base = + "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\"" " $$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 = Internal::StringHelpers::Replace(ytdl_call_audio, "$$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 = 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_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); + 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()); - std::cout << returnCode << std::endl; if (returnCode == 0) { @@ -263,6 +265,26 @@ JsonArray DownloadManager::GetQueueAsJson() 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() { if (GetNumActiveDownloads() == 0) @@ -397,6 +419,16 @@ void DownloadManager::Load() 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)) { std::string cachedStrMode = iter["mode"]; @@ -507,6 +539,8 @@ JsonBlock DownloadEntry::GetAsJson() jb.Set(Ele("webpage_url", webpage_url)); jb.Set(Ele("thumbnail_url", thumbnail_url)); jb.Set(Ele("download_progress", download_progress)); + jb.Set(Ele("downloaded_filename", downloaded_filename)); + jb.Set(Ele("download_url", download_url)); switch (mode) { diff --git a/Tubio/DownloadManager.h b/Tubio/DownloadManager.h index 7f0ce05..326caa6 100644 --- a/Tubio/DownloadManager.h +++ b/Tubio/DownloadManager.h @@ -34,6 +34,8 @@ namespace Downloader std::string tubio_id; std::string webpage_url; std::string thumbnail_url; + std::string downloaded_filename; + std::string download_url; DOWNLOAD_STATUS status; DOWNLOAD_MODE mode; int download_progress; @@ -64,8 +66,24 @@ namespace Downloader /// static std::size_t GetQueueLength(); + /// + /// Will return the whole queue in json format + /// + /// static JasonPP::JsonArray GetQueueAsJson(); + /// + /// Returns whether or not a tubio id exists + /// + /// The id to check + static bool DoesTubioIDExist(std::string tubioId); + + /// + /// Returns a reference to a DownloadEntry by its tubio id + /// + /// The corresponding tubio id + static DownloadEntry& GetDownloadEntryByTubioID(std::string tubioId); + /// /// Will delete all cached downloads! /// If downloads are currently active, tubio will wait for them to finish and return false! diff --git a/Tubio/HttpServer.cpp b/Tubio/HttpServer.cpp index bacc563..35d56a6 100644 --- a/Tubio/HttpServer.cpp +++ b/Tubio/HttpServer.cpp @@ -88,6 +88,10 @@ void HttpServer::EventHandler(mg_connection* pNc, int ev, void* p) { ProcessAPIRequest(pNc, ev, p); } + else if (requestedUri.substr(0, 9) == "/download") + { + ServeDownloadedResource(pNc, ev, p, requestedUri); + } else { // Just serve the files requested @@ -103,7 +107,7 @@ void HttpServer::EventHandler(mg_connection* pNc, int ev, void* p) catch (...) { 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); } @@ -147,6 +151,40 @@ void HttpServer::ProcessAPIRequest(mg_connection* pNc, int ev, void* p) 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() { log->cout << "Shutting down http-server..."; diff --git a/Tubio/HttpServer.h b/Tubio/HttpServer.h index 1456478..13d727f 100644 --- a/Tubio/HttpServer.h +++ b/Tubio/HttpServer.h @@ -23,6 +23,7 @@ namespace Rest private: bool InitWebServer(); 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 ServeStringToConnection(struct mg_connection* c, std::string str, int httpStatusCode = 200); diff --git a/Tubio/frontend/index.html b/Tubio/frontend/index.html deleted file mode 100644 index 6e63e9e..0000000 --- a/Tubio/frontend/index.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - Hello, World! - - - - - - - -

Hello, World!

- -
-
-

- Foo! -

-
-
-

- Kill server -

-
-
- - - - - diff --git a/Tubio/frontend/script.js b/Tubio/frontend/script.js deleted file mode 100644 index a016338..0000000 --- a/Tubio/frontend/script.js +++ /dev/null @@ -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"); - } - }); -}); diff --git a/Tubio/frontend/style.css b/Tubio/frontend/style.css deleted file mode 100644 index 06668af..0000000 --- a/Tubio/frontend/style.css +++ /dev/null @@ -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; -}