Implemented barebone rest-queries for youtube downloading. Still needs a lot of polishing!

This commit is contained in:
Leon Etienne (ubuntu wsl) 2020-09-25 02:04:30 +02:00
parent 17d7e743df
commit 96d11c80f3
19 changed files with 922 additions and 161 deletions

6
.gitignore vendored
View File

@ -14,6 +14,12 @@
config.json config.json
log.txt log.txt
log.json log.json
dlcache/
# Rediculously large dependencies
ffmpeg.exe
ffplay.exe
ffprobe.exe
# User-specific files (MonoDevelop/Xamarin Studio) # User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs *.userprefs

View File

@ -1,26 +1,10 @@
Please read all the LICENSE-files! # Setup:
## youtube-dl.exe
This project is a semi-automatic youtube to mp3 converter. Download youtube-dl and Microsoft Visual C++ 2010 Redist x86 from here:
It does only work, if you have the youtube-dl.exe and ffmpeg.exe, ffplay.exe and ffprobe.exe binaries in the root directory of the main executeable. [http://ytdl-org.github.io/youtube-dl/download.html](http://ytdl-org.github.io/youtube-dl/download.html)
These are not mine, but they are licensed in a way that i can include them in the project, so no source code for them. and put youtube-dl.exe into your build directory. It has to be in Tubio's working directory!!!
Youtube-dl should be already there, but it is always good to update it.
You can leave this application running in the background. As soon as you copy any youtube link, it will automatically download it to mp3 format.
Mp3 files will be saved in a "Downloads" folder in the root directory of the main executeable.
If you want to download an entire playlist at once, you just have to type PLAYLIST somewhere into the url of a playlist.
(This way you can quickly download single videos out of a playlist by just copying the url)
This project is NOT entirely mine! My project is based on these four binaries that are NOT mine:
youtube-dl.exe binary is from https://rg3.github.io/youtube-dl/ and is released under public domain.
ffmpeg.exe, ffplay.exe and ffprobe.exe binaries are copyrighted by Fabrice Bellard and released under the gnu - general public license
The C++ project (without the before mentioned four binaries) are licensed under gnu - general public license.
You may redistribute it (under the same license),
you may display it,
you may copy it,
you may modify it,
as long as you do not sub-license it or say it is your own.
## ffmpeg
Download the windows builds from ffmpeg from [https://ffmpeg.org/download.html#build-windows](https://ffmpeg.org/download.html#build-windows)
and put the executeables (ffmpeg.exe, ffplay.exe and ffprobe.exe) into your build directory. They have to be in Tubio's working directory!!!

476
Tubio/DownloadManager.cpp Normal file
View File

@ -0,0 +1,476 @@
#include "DownloadManager.h"
using namespace Downloader;
using namespace JasonPP;
void DownloadManager::PreInit()
{
log = new Logging::Logger("DownloadManager");
FileSystem::CreateDirectoryIfNotExists(XGConfig::downloader.cachedir);
FileSystem::CreateDirectoryIfNotExists(XGConfig::downloader.cachedir + "/metadata");
FileSystem::CreateDirectoryIfNotExists(XGConfig::downloader.cachedir + "/download");
FileSystem::CreateDirectoryIfNotExists(XGConfig::downloader.cachedir + "/dlprogbuf");
Load();
return;
}
std::string DownloadManager::QueueDownload(std::string url, DOWNLOAD_MODE mode)
{
std::string tubioId = CreateNewTubioID();
FetchInformation(url, tubioId);
std::string jsString = FileSystem::ReadFile(XGConfig::downloader.cachedir + "/metadata/" + tubioId + ".json");
DownloadEntry newDownload;
newDownload.tubio_id = tubioId;
newDownload.status = DOWNLOAD_STATUS::QUEUED;
newDownload.mode = mode;
newDownload.download_progress = 0;
Json j;
j.Parse(jsString);
if (j.GetDataType() != JDType::JSON)
{
newDownload.status = DOWNLOAD_STATUS::FAILED;
}
else
{
if ((j.AsJson.DoesExist("title")) && (j.AsJson["title"].GetDataType() == JDType::STRING))
{
newDownload.title = j["title"];
}
if ((j.AsJson.DoesExist("description")) && (j.AsJson["description"].GetDataType() == JDType::STRING))
{
newDownload.description = j["description"];
}
if ((j.AsJson.DoesExist("uploader")) && (j.AsJson["uploader"].GetDataType() == JDType::STRING))
{
newDownload.uploader = j["uploader"];
}
if ((j.AsJson.DoesExist("duration")) && (j.AsJson["duration"].GetDataType() == JDType::INT))
{
newDownload.duration = j["duration"];
}
if ((j.AsJson.DoesExist("webpage_url")) && (j.AsJson["webpage_url"].GetDataType() == JDType::STRING))
{
newDownload.webpage_url = j["webpage_url"];
}
if ((j.AsJson.DoesExist("thumbnails")) && (j.AsJson["thumbnails"].GetDataType() == JDType::ARRAY))
{
JsonArray& thumbnails = j.AsJson["thumbnails"].AsArray;
if (thumbnails.Size() > 0)
{
if (thumbnails.Size() > 1)
{
// If we have more than one thumbnail to choose from, choose the second-highes quality.
newDownload.thumbnail_url = thumbnails[thumbnails.Size() - 2]["url"];
}
else
{
newDownload.thumbnail_url = thumbnails[thumbnails.Size() - 1]["url"];
}
}
}
}
queue.push_back(newDownload);
Save();
return tubioId;
}
void DownloadManager::Update()
{
//if (shouldSave) Save();
std::size_t numActiveDownloads = GetNumActiveDownloads();
if (numActiveDownloads < XGConfig::downloader.num_threads)
{
DownloadNext();
}
// Check every second, non-blocking
if ((numActiveDownloads > 0) && (time(0) - lastProgressCheck > 2))
{
UpdateDownloadProgressPercentages();
}
return;
}
void DownloadManager::DownloadNext()
{
if (GetQueueLength() == 0) return;
DownloadEntry* next = nullptr;
for (std::size_t i = 0; i < queue.size(); i++)
{
if (queue[i].status == DOWNLOAD_STATUS::QUEUED)
{
next = &queue[i];
break;
}
}
next->status = DOWNLOAD_STATUS::DOWNLOADING;
std::thread* downloadThread = new std::thread([=]() {
DownloadEntry* entry = next;
std::stringstream ss;
if (entry->mode == DOWNLOAD_MODE::VIDEO)
{
ss << "youtube-dl --newline --no-call-home --no-playlist --limit-rate " << XGConfig::downloader.max_dlrate_per_thread
<< " --no-mtime --no-cache-dir --format \"bestvideo[ext=mp4]+bestaudio\" --merge-output-format mp4"
<< " -o \"" << XGConfig::downloader.cachedir << "/download/" << entry->tubio_id
<< ".mp4\" " << entry->webpage_url << " > \"" << XGConfig::downloader.cachedir
<< "/dlprogbuf/" << entry->tubio_id << ".buf" << "\"";
}
else // DOWNLOAD_MODE::AUDIO
{
ss << "youtube-dl --newline --no-call-home --no-playlist --limit-rate " << XGConfig::downloader.max_dlrate_per_thread
<< " --no-mtime --no-cache-dir --audio-format mp3 --audio-quality 0 --extract-audio -o \""
<< XGConfig::downloader.cachedir << "/download/" << entry->tubio_id << ".%(ext)s\" "
<< entry->webpage_url << " > \"" << XGConfig::downloader.cachedir
<< "/dlprogbuf/" << entry->tubio_id << ".buf" << "\"";
}
system(ss.str().c_str());
entry->status = DOWNLOAD_STATUS::FINISHED;
entry->download_progress = 100;
shouldSave = true;
return;
});
downloadThreads.push_back(downloadThread);
return;
}
void DownloadManager::UpdateDownloadProgressPercentages()
{
for (std::size_t i = 0; i < queue.size(); i++)
{
if (queue[i].status == DOWNLOAD_STATUS::DOWNLOADING)
{
std::string filePath = XGConfig::downloader.cachedir + "/dlprogbuf/" + queue[i].tubio_id + ".buf";
if (FileSystem::Exists(filePath))
{
std::ifstream ifs;
ifs.open(filePath, std::ios::in);
if (ifs.good())
{
std::string lbuf;
while (std::getline(ifs, lbuf))
{
if (lbuf.length() > 14)
{
if (lbuf.substr(0, 10) == "[download]")
{
std::string dirtyDigits = lbuf.substr(11, 3);
std::stringstream ss;
for (std::size_t j = 0; j < dirtyDigits.length(); j++)
{
if ((dirtyDigits[j] >= '0') && (dirtyDigits[j] <= '9')) ss << dirtyDigits[j];
}
if (ss.str().length() > 0)
{
int newPercentage = std::stoi(ss.str());
queue[i].download_progress = newPercentage;
}
}
}
}
}
ifs.close();
}
}
}
lastProgressCheck = time(0);
return;
}
std::size_t DownloadManager::GetQueueLength()
{
std::size_t counter = 0;
for (std::size_t i = 0; i < queue.size(); i++)
{
if (queue[i].status == DOWNLOAD_STATUS::QUEUED) counter++;
}
return counter;
}
JsonArray DownloadManager::GetQueueAsJson()
{
JsonArray arr;
for (std::size_t i = 0; i < queue.size(); i++)
{
arr += queue[i].GetAsJson();
}
return arr;
}
void Downloader::DownloadManager::ClearDownloadCache()
{
if (FileSystem::ExistsDirectory(XGConfig::downloader.cachedir))
{
FileSystem::DeleteDirectory(XGConfig::downloader.cachedir);
FileSystem::CreateDirectoryIfNotExists(XGConfig::downloader.cachedir + "/metadata");
FileSystem::CreateDirectoryIfNotExists(XGConfig::downloader.cachedir + "/download");
FileSystem::CreateDirectoryIfNotExists(XGConfig::downloader.cachedir + "/dlprogbuf");
queue.clear();
}
return;
}
void DownloadManager::Save()
{
JsonArray arr;
for (std::size_t i = 0; i < queue.size(); i++)
{
if (queue[i].status == DOWNLOAD_STATUS::FINISHED)
{
arr += queue[i].GetAsJson();
}
}
Json j(arr);
if (!FileSystem::WriteFile(XGConfig::downloader.cachedir + "/index.json", j.Render()))
{
log->cout << log->Err() << "Unable to save download cache index file!";
log->Flush();
}
shouldSave = false;
return;
}
void DownloadManager::Load()
{
// No file = nothing to load
if (!FileSystem::Exists(XGConfig::downloader.cachedir + "/index.json"))
{
log->cout << "Did not load download cache, because \"" << XGConfig::downloader.cachedir << "/index.json"
<< "\" was not found.";
log->Flush();
return;
}
std::string fileContent = FileSystem::ReadFile(XGConfig::downloader.cachedir + "/index.json");
if (IsJsonValid(fileContent))
{
Json j;
j.Parse(fileContent);
if (j.GetDataType() == JDType::ARRAY)
{
const JsonArray& cachedArr = j.AsArray;
for (std::size_t i = 0; i < cachedArr.Size(); i++)
{
JsonBlock iter = cachedArr[i].AsJson;
DownloadEntry newEntry;
newEntry.download_progress = -1;
newEntry.status = DOWNLOAD_STATUS::FINISHED; // All saved entries are finished...
if ((iter.DoesExist("title")) && (iter["title"].GetDataType() == JDType::STRING))
{
newEntry.title = iter["title"];
}
if ((iter.DoesExist("description")) && (iter["description"].GetDataType() == JDType::STRING))
{
newEntry.description = iter["description"];
}
if ((iter.DoesExist("uploader")) && (iter["uploader"].GetDataType() == JDType::STRING))
{
newEntry.uploader = iter["uploader"];
}
if ((iter.DoesExist("duration")) && (iter["duration"].GetDataType() == JDType::INT))
{
newEntry.duration = iter["duration"];
}
if ((iter.DoesExist("tubio_id")) && (iter["tubio_id"].GetDataType() == JDType::STRING))
{
newEntry.tubio_id = iter["tubio_id"];
}
if ((iter.DoesExist("webpage_url")) && (iter["webpage_url"].GetDataType() == JDType::STRING))
{
newEntry.webpage_url = iter["webpage_url"];
}
if ((iter.DoesExist("thumbnail_url")) && (iter["thumbnail_url"].GetDataType() == JDType::STRING))
{
newEntry.thumbnail_url = iter["thumbnail_url"];
}
if ((iter.DoesExist("mode")) && (iter["mode"].GetDataType() == JDType::STRING))
{
std::string cachedStrMode = iter["mode"];
if (cachedStrMode == "video") newEntry.mode = DOWNLOAD_MODE::VIDEO;
else if (cachedStrMode == "audio") newEntry.mode = DOWNLOAD_MODE::AUDIO;
else newEntry.mode = DOWNLOAD_MODE::VIDEO;
}
queue.push_back(newEntry);
}
}
else
{
log->cout << log->Err() << "Unable to parse download cache index file! Not json-type array!";
log->Flush();
}
}
else
{
log->cout << log->Err() << "Unable to parse download cache index file! Invalid json!";
log->Flush();
}
return;
}
void DownloadManager::FetchInformation(std::string url, std::string tubId)
{
std::stringstream ss;
ss << "youtube-dl.exe --skip-download --dump-json " << url << " > \"" << XGConfig::downloader.cachedir << "/metadata/" << tubId << ".json" << "\"" << std::endl;
system(ss.str().c_str());
return;
}
std::string DownloadManager::CreateNewTubioID()
{
bool isIdUnique = false;
std::size_t counter = 0;
std::string newId;
while (!isIdUnique)
{
if (counter > 100000000) throw std::exception("Tubio download id generator timeout");
newId = Internal::Helpers::Base10_2_X(time(0), "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
isIdUnique = true;
for (std::size_t i = 0; i < queue.size(); i++)
{
if (queue[i].tubio_id == newId) isIdUnique = false;
}
counter++;
}
return newId;
}
std::size_t Downloader::DownloadManager::GetNumActiveDownloads()
{
std::size_t counter = 0;
for (std::size_t i = 0; i < queue.size(); i++)
{
if (queue[i].status == DOWNLOAD_STATUS::DOWNLOADING) counter++;
}
return counter;
}
void DownloadManager::OnExit()
{
if (downloadThreads.size() > 0)
{
log->cout << "Waiting for active download threads to finish...";
log->Flush();
for (std::size_t i = 0; i < downloadThreads.size(); i++)
{
downloadThreads[i]->join();
delete downloadThreads[i];
downloadThreads[i] = nullptr;
}
}
// Clear dlprogbuf directory.
if (FileSystem::ExistsDirectory(XGConfig::downloader.cachedir + "/dlprogbuf"))
{
FileSystem::DeleteDirectory(XGConfig::downloader.cachedir + "/dlprogbuf");
}
Save();
return;
}
void DownloadManager::PostExit()
{
delete log;
log = nullptr;
return;
}
std::vector<DownloadEntry> DownloadManager::queue;
std::vector<std::thread*> DownloadManager::downloadThreads;
::Logging::Logger* DownloadManager::log;
bool DownloadManager::shouldSave = false;
time_t DownloadManager::lastProgressCheck = 0;
JsonBlock DownloadEntry::GetAsJson()
{
JsonBlock jb;
jb.Set(Ele("title",title));
jb.Set(Ele("description", description));
jb.Set(Ele("uploader", uploader));
jb.Set(Ele("duration", duration));
jb.Set(Ele("tubio_id", tubio_id));
jb.Set(Ele("webpage_url", webpage_url));
jb.Set(Ele("thumbnail_url", thumbnail_url));
jb.Set(Ele("download_progress", download_progress));
switch (mode)
{
case DOWNLOAD_MODE::VIDEO:
jb.Set(Ele("mode", "video"));
break;
case DOWNLOAD_MODE::AUDIO:
jb.Set(Ele("mode", "audio"));
break;
}
switch (status)
{
case DOWNLOAD_STATUS::QUEUED:
jb.Set(Ele("status", "queued"));
break;
case DOWNLOAD_STATUS::DOWNLOADING:
jb.Set(Ele("status", "downloading"));
break;
case DOWNLOAD_STATUS::FINISHED:
jb.Set(Ele("status", "finished"));
break;
case DOWNLOAD_STATUS::FAILED:
jb.Set(Ele("status", "failed"));
break;
}
return jb;
}

95
Tubio/DownloadManager.h Normal file
View File

@ -0,0 +1,95 @@
#pragma once
#include <vector>
#include <string>
#include <iostream>
#include <ctime>
#include <thread>
#include "FileSystem.h"
#include "XGConfig.h"
#include "Logger.h"
namespace Downloader
{
enum class DOWNLOAD_MODE
{
VIDEO,
AUDIO
};
enum class DOWNLOAD_STATUS
{
QUEUED,
DOWNLOADING,
FINISHED,
FAILED
};
class DownloadEntry
{
public:
std::string title;
std::string description;
std::string uploader;
int duration;
std::string tubio_id;
std::string webpage_url;
std::string thumbnail_url;
DOWNLOAD_STATUS status;
DOWNLOAD_MODE mode;
int download_progress;
JasonPP::JsonBlock GetAsJson();
};
class DownloadManager
{
public:
static void PreInit();
static void Update();
static void OnExit();
static void PostExit();
/// <summary>
/// Queues a video for download. Returns its tubio download id
/// </summary>
/// <param name="url"></param>
/// <param name="mode">If video or audio</param>
/// <returns>Tubio download id</returns>
static std::string QueueDownload(std::string url, DOWNLOAD_MODE mode);
/// <summary>
/// Returns the number of videos queued
/// </summary>
/// <returns></returns>
static std::size_t GetQueueLength();
static JasonPP::JsonArray GetQueueAsJson();
/// <summary>
/// Will delete all cached downloads!
/// </summary>
static void ClearDownloadCache();
private:
static void Save();
static void Load();
static void FetchInformation(std::string url, std::string tubId);
static std::string CreateNewTubioID();
static std::size_t GetNumActiveDownloads();
/// <summary>
/// Will start a download-thread for the next queue-entry with status "queued"
/// </summary>
static void DownloadNext();
static void UpdateDownloadProgressPercentages();
static std::vector<DownloadEntry> queue;
static std::vector<std::thread*> downloadThreads;
static Logging::Logger* log;
// This gets set by other threads
static bool shouldSave;
static time_t lastProgressCheck;
};
}

View File

@ -37,6 +37,27 @@ bool FileSystem::Exists(std::string filename)
return true; return true;
} }
bool FileSystem::ExistsDirectory(std::string name)
{
return (!Exists(name) && (std::filesystem::exists(name)));
}
bool FileSystem::CreateDirectory(std::string name)
{
return std::filesystem::create_directories(name);
}
bool FileSystem::CreateDirectoryIfNotExists(std::string name)
{
if (!ExistsDirectory(name)) return CreateDirectory(name);
return false;
}
bool FileSystem::DeleteDirectory(std::string name)
{
return std::filesystem::remove_all(name);
}
bool FileSystem::Copy(std::string from, std::string to) bool FileSystem::Copy(std::string from, std::string to)
{ {
std::ifstream ifs; std::ifstream ifs;

View File

@ -3,6 +3,7 @@
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#include <stdio.h> #include <stdio.h>
#include <filesystem>
class FileSystem class FileSystem
@ -13,7 +14,10 @@ public:
static bool Exists(std::string filename); static bool Exists(std::string filename);
static bool Copy(std::string from, std::string to); static bool Copy(std::string from, std::string to);
static bool Delete(std::string filename); static bool Delete(std::string filename);
static bool ExistsDirectory(std::string name);
static bool CreateDirectory(std::string name);
static bool CreateDirectoryIfNotExists(std::string name);
static bool DeleteDirectory(std::string name);
private: private:
}; };

View File

@ -2,6 +2,7 @@
using namespace Logging; using namespace Logging;
using namespace Rest; using namespace Rest;
using namespace Downloader;
Framework::Framework() Framework::Framework()
{ {
@ -16,7 +17,6 @@ Framework::Framework()
PostInit(); PostInit();
XGControl::keepServerRunning = true; XGControl::keepServerRunning = true;
Run();
return; return;
} }
@ -37,6 +37,7 @@ void Framework::Run()
while (XGControl::keepServerRunning) while (XGControl::keepServerRunning)
{ {
httpServer->Update(); httpServer->Update();
DownloadManager::Update();
} }
OnExit(); OnExit();
@ -52,6 +53,7 @@ void Framework::PreInit()
LogHistory::PreInit(); LogHistory::PreInit();
XGConfig::PreInit(); XGConfig::PreInit();
RestQueryHandler::PreInit(); RestQueryHandler::PreInit();
DownloadManager::PreInit();
return; return;
} }
@ -67,6 +69,7 @@ void Framework::PostInit()
void Framework::OnExit() void Framework::OnExit()
{ {
httpServer->OnExit(); httpServer->OnExit();
DownloadManager::OnExit();
return; return;
} }
@ -76,6 +79,7 @@ void Framework::PostExit()
XGConfig::PostExit(); XGConfig::PostExit();
RestQueryHandler::PostExit(); RestQueryHandler::PostExit();
LogHistory::PostExit(); LogHistory::PostExit();
DownloadManager::PostExit();
return; return;
} }

View File

@ -2,6 +2,7 @@
#include "Logger.h" #include "Logger.h"
#include "LogHistory.h" #include "LogHistory.h"
#include "HttpServer.h" #include "HttpServer.h"
#include "DownloadManager.h"
#include "XGControl.h" #include "XGControl.h"
#include "XGConfig.h" #include "XGConfig.h"
@ -10,10 +11,9 @@ class Framework
public: public:
Framework(); Framework();
~Framework(); ~Framework();
private:
void Run(); void Run();
private:
void PostInit(); void PostInit();
void OnExit(); void OnExit();
void PreInit(); void PreInit();

View File

@ -82,15 +82,30 @@ void HttpServer::EventHandler(mg_connection* pNc, int ev, void* p)
http_message* hpm = (http_message*)p; http_message* hpm = (http_message*)p;
std::string requestedUri = FixUnterminatedString(hpm->uri.p, hpm->uri.len); std::string requestedUri = FixUnterminatedString(hpm->uri.p, hpm->uri.len);
if ((requestedUri == "/api")) try
{ {
ProcessAPIRequest(pNc, ev, p); if (requestedUri == "/api")
{
ProcessAPIRequest(pNc, ev, p);
}
else
{
// Just serve the files requested
mg_serve_http(pNc, (struct http_message*)p, frontend_serve_opts);
}
} }
else catch (std::exception& e)
{ {
mg_serve_http(pNc, (struct http_message*)p, frontend_serve_opts); Json j;
j.CloneFrom(RestResponseTemplates::GetByCode(INTERNAL_SERVER_ERROR, e.what()));
ServeStringToConnection(pNc, j.Render(), INTERNAL_SERVER_ERROR);
}
catch (...)
{
Json j;
j.CloneFrom(RestResponseTemplates::GetByCode(INTERNAL_SERVER_ERROR, "Das not gud"));
ServeStringToConnection(pNc, j.Render(), INTERNAL_SERVER_ERROR);
} }
break; break;
} }
@ -112,19 +127,20 @@ void HttpServer::ProcessAPIRequest(mg_connection* pNc, int ev, void* p)
Json requestBody; Json requestBody;
requestBody.Parse(requestBodyRaw); requestBody.Parse(requestBodyRaw);
char addr[32]; char peer_addr[32];
mg_sock_addr_to_str(&pNc->sa, addr, sizeof(addr), MG_SOCK_STRINGIFY_IP); mg_sock_addr_to_str(&pNc->sa, peer_addr, sizeof(peer_addr), MG_SOCK_STRINGIFY_IP);
JsonBlock responseBody; JsonBlock responseBody;
HTTP_STATUS_CODE returnCode; HTTP_STATUS_CODE returnCode;
RestQueryHandler::ProcessQuery(std::string(addr), requestBody, responseBody, returnCode); RestQueryHandler::ProcessQuery(std::string(peer_addr), requestBody, responseBody, returnCode);
Json response(responseBody); Json response(responseBody);
ServeStringToConnection(pNc, response.Render(), returnCode); ServeStringToConnection(pNc, response.Render(), returnCode);
} }
else // return error message for invalid json else // return error message for invalid json
{ {
Json errorJson = RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "Received json is fucked up"); Json errorJson;
errorJson.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "Received json is fucked"));
ServeStringToConnection(pNc, errorJson.Render(), HTTP_STATUS_CODE::BAD_REQUEST); ServeStringToConnection(pNc, errorJson.Render(), HTTP_STATUS_CODE::BAD_REQUEST);
} }

View File

@ -262,7 +262,7 @@ std::size_t JsonArray::RemoveSimilar(const JsonData reference)
return counter; return counter;
} }
std::size_t JsonArray::RemoveAllOfType(const JDType type) std::size_t JsonArray::RemoveAllOfType(const JSON_DATA_TYPE type)
{ {
std::size_t counter = 0; std::size_t counter = 0;
for (long long int i = content->size() - 1; i >= 0; i--) for (long long int i = content->size() - 1; i >= 0; i--)
@ -277,7 +277,7 @@ std::size_t JsonArray::RemoveAllOfType(const JDType type)
return counter; return counter;
} }
std::size_t JsonArray::RemoveAllExceptType(const JDType type) std::size_t JsonArray::RemoveAllExceptType(const JSON_DATA_TYPE type)
{ {
std::size_t counter = 0; std::size_t counter = 0;
for (long long int i = content->size() - 1; i >= 0; i--) for (long long int i = content->size() - 1; i >= 0; i--)
@ -524,7 +524,7 @@ void JsonArray::Sort(const std::string shorthandKey, const JSON_ARRAY_SORT_MODE
const JsonData* b = &At(j + 1); const JsonData* b = &At(j + 1);
// Check if they are of type json (this is the json sorter) (deep sort) // Check if they are of type json (this is the json sorter) (deep sort)
if ((a->GetDataType() == JDType::JSON) && (b->GetDataType() == JDType::JSON)) if ((a->GetDataType() == JSON_DATA_TYPE::JSON) && (b->GetDataType() == JSON_DATA_TYPE::JSON))
{ {
// Check if the requested key even exists // Check if the requested key even exists
if ((a->GetJsonData().DoesShorthandExist(shorthandKey, shorthandDelimiter)) && if ((a->GetJsonData().DoesShorthandExist(shorthandKey, shorthandDelimiter)) &&
@ -560,8 +560,8 @@ void JsonArray::Sort(const JSON_ARRAY_SORT_MODE mode)
// Only if neither a or b's are neither of type json or array // Only if neither a or b's are neither of type json or array
// This is the "shallow"-sort // This is the "shallow"-sort
if (!(((a.GetDataType() == JDType::JSON) || (a.GetDataType() == JDType::ARRAY)) && if (!(((a.GetDataType() == JSON_DATA_TYPE::JSON) || (a.GetDataType() == JSON_DATA_TYPE::ARRAY)) &&
((b.GetDataType() == JDType::JSON) || (b.GetDataType() == JDType::ARRAY)))) ((b.GetDataType() == JSON_DATA_TYPE::JSON) || (b.GetDataType() == JSON_DATA_TYPE::ARRAY))))
{ {
if (Sort__Compare(a, b, mode)) if (Sort__Compare(a, b, mode))
{ {
@ -590,11 +590,11 @@ bool JsonArray::Sort__Compare(const JsonData& a, const JsonData& b, const JSON_A
// If it's BOOL, INT or FLOAT, just get it's value as string. Eg "53", "53.2" or "false" // If it's BOOL, INT or FLOAT, just get it's value as string. Eg "53", "53.2" or "false"
// This way numerics can still be sorted alphabetically // This way numerics can still be sorted alphabetically
// Also allows for sorting after bools // Also allows for sorting after bools
if (a.GetDataType() == JDType::STRING) aStr = a.GetStringData(); if (a.GetDataType() == JSON_DATA_TYPE::STRING) aStr = a.GetStringData();
else if ((a.GetDataType() != JDType::JSON) && (a.GetDataType() != JDType::ARRAY)) aStr = a.Render(); else if ((a.GetDataType() != JSON_DATA_TYPE::JSON) && (a.GetDataType() != JSON_DATA_TYPE::ARRAY)) aStr = a.Render();
else return true; // Datatype invalid. Swap, to keep the others in order. else return true; // Datatype invalid. Swap, to keep the others in order.
if (b.GetDataType() == JDType::STRING) bStr = b.GetStringData(); if (b.GetDataType() == JSON_DATA_TYPE::STRING) bStr = b.GetStringData();
else if ((b.GetDataType() != JDType::JSON) && (b.GetDataType() != JDType::ARRAY)) bStr = b.Render(); else if ((b.GetDataType() != JSON_DATA_TYPE::JSON) && (b.GetDataType() != JSON_DATA_TYPE::ARRAY)) bStr = b.Render();
else return true; // Datatype invalid. Swap, to keep the others in order. else return true; // Datatype invalid. Swap, to keep the others in order.
return StringHelpers::SortDescriminator_Alphabetically(aStr, bStr); return StringHelpers::SortDescriminator_Alphabetically(aStr, bStr);
@ -612,11 +612,11 @@ bool JsonArray::Sort__Compare(const JsonData& a, const JsonData& b, const JSON_A
// If it's BOOL, INT or FLOAT, just get it's value as string. Eg "53", "53.2" or "false" // If it's BOOL, INT or FLOAT, just get it's value as string. Eg "53", "53.2" or "false"
// This way numerics can still be sorted alphabetically // This way numerics can still be sorted alphabetically
// Also allows for sorting after bools // Also allows for sorting after bools
if (a.GetDataType() == JDType::STRING) aStr = a.GetStringData(); if (a.GetDataType() == JSON_DATA_TYPE::STRING) aStr = a.GetStringData();
else if ((a.GetDataType() != JDType::JSON) && (a.GetDataType() != JDType::ARRAY)) aStr = a.Render(); else if ((a.GetDataType() != JSON_DATA_TYPE::JSON) && (a.GetDataType() != JSON_DATA_TYPE::ARRAY)) aStr = a.Render();
else return true; // Datatype invalid. Swap, to keep the others in order. else return true; // Datatype invalid. Swap, to keep the others in order.
if (b.GetDataType() == JDType::STRING) bStr = b.GetStringData(); if (b.GetDataType() == JSON_DATA_TYPE::STRING) bStr = b.GetStringData();
else if ((b.GetDataType() != JDType::JSON) && (b.GetDataType() != JDType::ARRAY)) bStr = b.Render(); else if ((b.GetDataType() != JSON_DATA_TYPE::JSON) && (b.GetDataType() != JSON_DATA_TYPE::ARRAY)) bStr = b.Render();
else return true; // Datatype invalid. Swap, to keep the others in order. else return true; // Datatype invalid. Swap, to keep the others in order.
return StringHelpers::SortDescriminator_Alphabetically(bStr, aStr); return StringHelpers::SortDescriminator_Alphabetically(bStr, aStr);
@ -631,13 +631,13 @@ bool JsonArray::Sort__Compare(const JsonData& a, const JsonData& b, const JSON_A
switch (a.GetDataType()) switch (a.GetDataType())
{ {
case JDType::INT: case JSON_DATA_TYPE::INT:
dataA = (long double)a.GetIntData(); dataA = (long double)a.GetIntData();
break; break;
case JDType::FLOAT: case JSON_DATA_TYPE::FLOAT:
dataA = a.GetFloatData(); dataA = a.GetFloatData();
break; break;
case JDType::BOOL: case JSON_DATA_TYPE::BOOL:
dataA = a.GetBoolData() ? 1.0 : 0.0; dataA = a.GetBoolData() ? 1.0 : 0.0;
break; break;
default: default:
@ -645,13 +645,13 @@ bool JsonArray::Sort__Compare(const JsonData& a, const JsonData& b, const JSON_A
} }
switch (b.GetDataType()) switch (b.GetDataType())
{ {
case JDType::INT: case JSON_DATA_TYPE::INT:
dataB = (long double)b.GetIntData(); dataB = (long double)b.GetIntData();
break; break;
case JDType::FLOAT: case JSON_DATA_TYPE::FLOAT:
dataB = b.GetFloatData(); dataB = b.GetFloatData();
break; break;
case JDType::BOOL: case JSON_DATA_TYPE::BOOL:
dataB = b.GetBoolData() ? 1.0 : 0.0; dataB = b.GetBoolData() ? 1.0 : 0.0;
break; break;
default: default:
@ -669,13 +669,13 @@ bool JsonArray::Sort__Compare(const JsonData& a, const JsonData& b, const JSON_A
switch (a.GetDataType()) switch (a.GetDataType())
{ {
case JDType::INT: case JSON_DATA_TYPE::INT:
dataA = (long double)a.GetIntData(); dataA = (long double)a.GetIntData();
break; break;
case JDType::FLOAT: case JSON_DATA_TYPE::FLOAT:
dataA = a.GetFloatData(); dataA = a.GetFloatData();
break; break;
case JDType::BOOL: case JSON_DATA_TYPE::BOOL:
dataA = a.GetBoolData() ? 1.0 : 0.0; dataA = a.GetBoolData() ? 1.0 : 0.0;
break; break;
default: default:
@ -683,13 +683,13 @@ bool JsonArray::Sort__Compare(const JsonData& a, const JsonData& b, const JSON_A
} }
switch (b.GetDataType()) switch (b.GetDataType())
{ {
case JDType::INT: case JSON_DATA_TYPE::INT:
dataB = (long double)b.GetIntData(); dataB = (long double)b.GetIntData();
break; break;
case JDType::FLOAT: case JSON_DATA_TYPE::FLOAT:
dataB = b.GetFloatData(); dataB = b.GetFloatData();
break; break;
case JDType::BOOL: case JSON_DATA_TYPE::BOOL:
dataB = b.GetBoolData() ? 1.0 : 0.0; dataB = b.GetBoolData() ? 1.0 : 0.0;
break; break;
default: default:
@ -753,7 +753,7 @@ JsonArray& JsonArray::operator-=(const JsonData& data)
return *this; return *this;
} }
JsonArray& JsonArray::operator-=(const JDType type) JsonArray& JsonArray::operator-=(const JSON_DATA_TYPE type)
{ {
RemoveAllOfType(type); RemoveAllOfType(type);
return *this; return *this;
@ -954,7 +954,8 @@ void JsonArray::Parse(const std::string jsonCode)
{ {
const char c = minifiedCode[i]; const char c = minifiedCode[i];
if ((!areWeInString) && (!areWeInCode) && (arrayBracketLevel == 1) && (curlyBracketLevel == 0)) if ((!areWeInString) && (!areWeInCode) && (arrayBracketLevel == 1) &&
(curlyBracketLevel == 0) && (i < minifiedCode.length() - 1))
{ {
start = i; start = i;
areWeInCode = true; areWeInCode = true;
@ -1031,29 +1032,29 @@ bool JasonPP::IsJsonValid(const std::string code)
return true; return true;
} }
std::string JasonPP::JsonDataType2String(const JDType type) std::string JasonPP::JsonDataType2String(const JSON_DATA_TYPE type)
{ {
switch (type) switch (type)
{ {
case JDType::__NULL__: case JSON_DATA_TYPE::__NULL__:
return std::string("NULL"); return std::string("NULL");
case JDType::BOOL: case JSON_DATA_TYPE::BOOL:
return std::string("BOOL"); return std::string("BOOL");
case JDType::INT: case JSON_DATA_TYPE::INT:
return std::string("INT"); return std::string("INT");
case JDType::FLOAT: case JSON_DATA_TYPE::FLOAT:
return std::string("FLOAT"); return std::string("FLOAT");
case JDType::STRING: case JSON_DATA_TYPE::STRING:
return std::string("STRING"); return std::string("STRING");
case JDType::JSON: case JSON_DATA_TYPE::JSON:
return std::string("JSON"); return std::string("JSON");
case JDType::ARRAY: case JSON_DATA_TYPE::ARRAY:
return std::string("ARRAY"); return std::string("ARRAY");
} }
@ -1457,37 +1458,37 @@ JsonData::JsonData()
} }
// Set default data per type // Set default data per type
JsonData::JsonData(const JDType type) JsonData::JsonData(const JSON_DATA_TYPE type)
{ {
Init(); Init();
switch (type) switch (type)
{ {
case JDType::__NULL__: case JSON_DATA_TYPE::__NULL__:
// Default value is already NULL // Default value is already NULL
break; break;
case JDType::BOOL: case JSON_DATA_TYPE::BOOL:
SetBoolData(false); SetBoolData(false);
break; break;
case JDType::INT: case JSON_DATA_TYPE::INT:
SetIntData(0); SetIntData(0);
break; break;
case JDType::FLOAT: case JSON_DATA_TYPE::FLOAT:
SetFloatData(0); SetFloatData(0);
break; break;
case JDType::STRING: case JSON_DATA_TYPE::STRING:
SetStringData(""); SetStringData("");
break; break;
case JDType::JSON: case JSON_DATA_TYPE::JSON:
SetJsonDataAsPointer(new JsonBlock()); SetJsonDataAsPointer(new JsonBlock());
break; break;
case JDType::ARRAY: case JSON_DATA_TYPE::ARRAY:
SetArrayDataAsPointer(new JsonArray()); SetArrayDataAsPointer(new JsonArray());
break; break;
} }
@ -1602,7 +1603,7 @@ void JsonData::SetFloatPrecision(const double precision)
JsonArray& JsonData::SetArrayDataAsPointer(JsonArray* p) JsonArray& JsonData::SetArrayDataAsPointer(JsonArray* p)
{ {
dataType = JDType::ARRAY; dataType = JSON_DATA_TYPE::ARRAY;
if (arrayData != nullptr) if (arrayData != nullptr)
{ {
@ -1617,7 +1618,7 @@ JsonArray& JsonData::SetArrayDataAsPointer(JsonArray* p)
JsonBlock& JsonData::SetJsonDataAsPointer(JsonBlock* p) JsonBlock& JsonData::SetJsonDataAsPointer(JsonBlock* p)
{ {
dataType = JDType::JSON; dataType = JSON_DATA_TYPE::JSON;
if (jsonData != nullptr) if (jsonData != nullptr)
{ {
@ -1631,7 +1632,7 @@ JsonBlock& JsonData::SetJsonDataAsPointer(JsonBlock* p)
void JsonData::Init() void JsonData::Init()
{ {
dataType = JDType::__NULL__; dataType = JSON_DATA_TYPE::__NULL__;
intData = 0l; intData = 0l;
floatData = 0.0f; floatData = 0.0f;
stringData = std::string(); stringData = std::string();
@ -1686,12 +1687,12 @@ double JsonData::GetFloatPrecision() const
bool JsonData::IsOfNumericType() const bool JsonData::IsOfNumericType() const
{ {
return (dataType == JDType::INT) || (dataType == JDType::FLOAT); return (dataType == JSON_DATA_TYPE::INT) || (dataType == JSON_DATA_TYPE::FLOAT);
} }
bool JsonData::GetBoolData() const bool JsonData::GetBoolData() const
{ {
JDType typeToGet = JDType::BOOL; JSON_DATA_TYPE typeToGet = JSON_DATA_TYPE::BOOL;
if (dataType != typeToGet) ThrowDataTypeException(typeToGet); if (dataType != typeToGet) ThrowDataTypeException(typeToGet);
@ -1700,11 +1701,11 @@ bool JsonData::GetBoolData() const
long long int JsonData::GetIntData() const long long int JsonData::GetIntData() const
{ {
JDType typeToGet = JDType::INT; JSON_DATA_TYPE typeToGet = JSON_DATA_TYPE::INT;
if (dataType != typeToGet) if (dataType != typeToGet)
{ {
if (dataType == JDType::FLOAT) if (dataType == JSON_DATA_TYPE::FLOAT)
{ {
return (long long int)floatData; return (long long int)floatData;
} }
@ -1719,11 +1720,11 @@ long long int JsonData::GetIntData() const
long double JsonData::GetFloatData() const long double JsonData::GetFloatData() const
{ {
JDType typeToGet = JDType::FLOAT; JSON_DATA_TYPE typeToGet = JSON_DATA_TYPE::FLOAT;
if (dataType != typeToGet) if (dataType != typeToGet)
{ {
if (dataType == JDType::INT) if (dataType == JSON_DATA_TYPE::INT)
{ {
return (float)intData; return (float)intData;
} }
@ -1738,7 +1739,7 @@ long double JsonData::GetFloatData() const
std::string JsonData::GetStringData() const std::string JsonData::GetStringData() const
{ {
JDType typeToGet = JDType::STRING; JSON_DATA_TYPE typeToGet = JSON_DATA_TYPE::STRING;
if (dataType != typeToGet) ThrowDataTypeException(typeToGet); if (dataType != typeToGet) ThrowDataTypeException(typeToGet);
@ -1747,35 +1748,35 @@ std::string JsonData::GetStringData() const
JsonBlock& JsonData::GetJsonData() JsonBlock& JsonData::GetJsonData()
{ {
JDType typeToGet = JDType::JSON; JSON_DATA_TYPE typeToGet = JSON_DATA_TYPE::JSON;
if (dataType != typeToGet) ThrowDataTypeException(typeToGet); if (dataType != typeToGet) ThrowDataTypeException(typeToGet);
return *jsonData; return *jsonData;
} }
const JsonBlock& JsonData::GetJsonData() const const JsonBlock& JsonData::GetJsonData() const
{ {
JDType typeToGet = JDType::JSON; JSON_DATA_TYPE typeToGet = JSON_DATA_TYPE::JSON;
if (dataType != typeToGet) ThrowDataTypeException(typeToGet); if (dataType != typeToGet) ThrowDataTypeException(typeToGet);
return *jsonData; return *jsonData;
} }
JsonArray& JsonData::GetArrayData() JsonArray& JsonData::GetArrayData()
{ {
JDType typeToGet = JDType::ARRAY; JSON_DATA_TYPE typeToGet = JSON_DATA_TYPE::ARRAY;
if (dataType != typeToGet) ThrowDataTypeException(typeToGet); if (dataType != typeToGet) ThrowDataTypeException(typeToGet);
return *arrayData; return *arrayData;
} }
const JsonArray& JsonData::GetArrayData() const const JsonArray& JsonData::GetArrayData() const
{ {
JDType typeToGet = JDType::ARRAY; JSON_DATA_TYPE typeToGet = JSON_DATA_TYPE::ARRAY;
if (dataType != typeToGet) ThrowDataTypeException(typeToGet); if (dataType != typeToGet) ThrowDataTypeException(typeToGet);
return *arrayData; return *arrayData;
} }
short JsonData::GetNullData() short JsonData::GetNullData()
{ {
JDType typeToGet = JDType::__NULL__; JSON_DATA_TYPE typeToGet = JSON_DATA_TYPE::__NULL__;
if (dataType != typeToGet) ThrowDataTypeException(typeToGet); if (dataType != typeToGet) ThrowDataTypeException(typeToGet);
@ -1803,7 +1804,7 @@ bool JsonData::HasParent() const
void JsonData::SetBoolData(const bool data) void JsonData::SetBoolData(const bool data)
{ {
dataType = JDType::BOOL; dataType = JSON_DATA_TYPE::BOOL;
intData = (int)data; intData = (int)data;
return; return;
@ -1811,7 +1812,7 @@ void JsonData::SetBoolData(const bool data)
void JsonData::SetIntData(const long long int data) void JsonData::SetIntData(const long long int data)
{ {
dataType = JDType::INT; dataType = JSON_DATA_TYPE::INT;
intData = data; intData = data;
return; return;
@ -1819,7 +1820,7 @@ void JsonData::SetIntData(const long long int data)
void JsonData::SetIntData(const int data) void JsonData::SetIntData(const int data)
{ {
dataType = JDType::INT; dataType = JSON_DATA_TYPE::INT;
intData = data; intData = data;
return; return;
@ -1827,7 +1828,7 @@ void JsonData::SetIntData(const int data)
void JsonData::SetFloatData(const long double data) void JsonData::SetFloatData(const long double data)
{ {
dataType = JDType::FLOAT; dataType = JSON_DATA_TYPE::FLOAT;
floatData = data; floatData = data;
return; return;
@ -1835,7 +1836,7 @@ void JsonData::SetFloatData(const long double data)
void JsonData::SetStringData(const std::string data) void JsonData::SetStringData(const std::string data)
{ {
dataType = JDType::STRING; dataType = JSON_DATA_TYPE::STRING;
stringData = data; stringData = data;
return; return;
@ -1848,7 +1849,7 @@ JsonBlock& JsonData::SetJsonData(const JsonBlock data)
JsonArray& JsonData::SetArrayData(const std::vector<JsonData> data) JsonArray& JsonData::SetArrayData(const std::vector<JsonData> data)
{ {
dataType = JDType::ARRAY; dataType = JSON_DATA_TYPE::ARRAY;
JsonArray* newArr = new JsonArray; JsonArray* newArr = new JsonArray;
newArr->CopyJsonDataFromVector_Pointer(&data); // Slightly more performant than constructor newArr->CopyJsonDataFromVector_Pointer(&data); // Slightly more performant than constructor
return SetArrayDataAsPointer(newArr); return SetArrayDataAsPointer(newArr);
@ -1861,14 +1862,14 @@ JsonArray& JsonData::SetArrayData(const JsonArray data)
void JsonData::SetNull() void JsonData::SetNull()
{ {
dataType = JDType::__NULL__; dataType = JSON_DATA_TYPE::__NULL__;
return; return;
} }
/* MISC */ /* MISC */
void JsonData::ThrowDataTypeException(const JDType toFetch) const void JsonData::ThrowDataTypeException(const JSON_DATA_TYPE toFetch) const
{ {
throw JsonWrongDataTypeException( throw JsonWrongDataTypeException(
JsonDataType2String(toFetch), JsonDataType2String(toFetch),
@ -1890,32 +1891,32 @@ std::string JsonData::Render(unsigned int num_tabs, const bool minify) const
switch (dataType) switch (dataType)
{ {
case JDType::__NULL__: case JSON_DATA_TYPE::__NULL__:
ss << "null"; ss << "null";
break; break;
case JDType::BOOL: case JSON_DATA_TYPE::BOOL:
ss << ((intData != 0) ? "true" : "false"); ss << ((intData != 0) ? "true" : "false");
break; break;
case JDType::INT: case JSON_DATA_TYPE::INT:
ss << intData; ss << intData;
break; break;
case JDType::FLOAT: case JSON_DATA_TYPE::FLOAT:
ss.precision((std::streamsize)((-log10(GetFloatPrecision())) + 1)); ss.precision((std::streamsize)((-log10(GetFloatPrecision())) + 1));
ss << floatData; ss << floatData;
break; break;
case JDType::STRING: case JSON_DATA_TYPE::STRING:
ss << "\"" << StringHelpers::Escape(stringData) << "\""; ss << "\"" << StringHelpers::Escape(stringData) << "\"";
break; break;
case JDType::JSON: case JSON_DATA_TYPE::JSON:
ss << jsonData->Render(num_tabs, minify); ss << jsonData->Render(num_tabs, minify);
break; break;
case JDType::ARRAY: case JSON_DATA_TYPE::ARRAY:
ss << arrayData->Render(num_tabs, minify); ss << arrayData->Render(num_tabs, minify);
break; break;
} }
@ -2007,31 +2008,31 @@ void JsonData::CloneFrom(const JsonData& other)
switch (other.dataType) switch (other.dataType)
{ {
case JDType::__NULL__: case JSON_DATA_TYPE::__NULL__:
// Default value is already NULL // Default value is already NULL
break; break;
case JDType::BOOL: case JSON_DATA_TYPE::BOOL:
SetBoolData(other.intData != 0); SetBoolData(other.intData != 0);
break; break;
case JDType::INT: case JSON_DATA_TYPE::INT:
SetIntData(other.intData); SetIntData(other.intData);
break; break;
case JDType::FLOAT: case JSON_DATA_TYPE::FLOAT:
SetFloatData(other.floatData); SetFloatData(other.floatData);
break; break;
case JDType::STRING: case JSON_DATA_TYPE::STRING:
SetStringData(other.stringData); SetStringData(other.stringData);
break; break;
case JDType::ARRAY: case JSON_DATA_TYPE::ARRAY:
SetArrayData(*other.arrayData); SetArrayData(*other.arrayData);
break; break;
case JDType::JSON: case JSON_DATA_TYPE::JSON:
SetJsonData(*other.jsonData); SetJsonData(*other.jsonData);
break; break;
} }
@ -2042,8 +2043,8 @@ void JsonData::CloneFrom(const JsonData& other)
bool JsonData::IsIdentical(const JsonData& other) const bool JsonData::IsIdentical(const JsonData& other) const
{ {
// Special case for int/float implicit conversion // Special case for int/float implicit conversion
if (((dataType == JDType::INT) && (other.dataType == JDType::FLOAT)) || if (((dataType == JSON_DATA_TYPE::INT) && (other.dataType == JSON_DATA_TYPE::FLOAT)) ||
((other.dataType == JDType::INT) && (dataType == JDType::FLOAT))) ((other.dataType == JSON_DATA_TYPE::INT) && (dataType == JSON_DATA_TYPE::FLOAT)))
{ {
// Here we have to get the float value via the getter because of implicit conversion // Here we have to get the float value via the getter because of implicit conversion
// Use the more precise precision of the two... // Use the more precise precision of the two...
@ -2055,32 +2056,32 @@ bool JsonData::IsIdentical(const JsonData& other) const
switch (dataType) switch (dataType)
{ {
case JDType::__NULL__: case JSON_DATA_TYPE::__NULL__:
return true; return true;
case JDType::BOOL: case JSON_DATA_TYPE::BOOL:
// Values can't be of different type because of the check at the beginning of the function // Values can't be of different type because of the check at the beginning of the function
return intData == other.intData; return intData == other.intData;
break; break;
case JDType::INT: case JSON_DATA_TYPE::INT:
return intData == other.intData; return intData == other.intData;
break; break;
case JDType::FLOAT: case JSON_DATA_TYPE::FLOAT:
// Use the more precise precision of the two... // Use the more precise precision of the two...
return Helpers::AreSame(floatData, other.floatData, Helpers::Min<double>(GetFloatPrecision(), other.GetFloatPrecision())); return Helpers::AreSame(floatData, other.floatData, Helpers::Min<double>(GetFloatPrecision(), other.GetFloatPrecision()));
break; break;
case JDType::STRING: case JSON_DATA_TYPE::STRING:
return stringData == other.stringData; return stringData == other.stringData;
break; break;
case JDType::ARRAY: case JSON_DATA_TYPE::ARRAY:
return arrayData->IsIdentical(*other.arrayData); return arrayData->IsIdentical(*other.arrayData);
break; break;
case JDType::JSON: case JSON_DATA_TYPE::JSON:
return jsonData->IsIdentical(*other.jsonData); return jsonData->IsIdentical(*other.jsonData);
break; break;
} }
@ -2140,11 +2141,11 @@ bool JsonData::operator!=(const JsonData& other) const
JsonData& JsonData::operator+=(const JsonElement ele) JsonData& JsonData::operator+=(const JsonElement ele)
{ {
if (dataType == JDType::JSON) if (dataType == JSON_DATA_TYPE::JSON)
{ {
return jsonData->Add(ele); return jsonData->Add(ele);
} }
ThrowDataTypeException(JDType::JSON); ThrowDataTypeException(JSON_DATA_TYPE::JSON);
std::terminate(); std::terminate();
} }
@ -2257,7 +2258,7 @@ JsonData::operator long double() const
JsonData::operator std::string() const JsonData::operator std::string() const
{ {
if (dataType == JDType::STRING) return GetStringData(); if (dataType == JSON_DATA_TYPE::STRING) return GetStringData();
else return Render(JASONPP_STRINGCONV_MINIFY); else return Render(JASONPP_STRINGCONV_MINIFY);
} }
@ -2266,7 +2267,7 @@ namespace JasonPP
{ {
std::ostream& operator<<(std::ostream& os, const JsonData& jd) std::ostream& operator<<(std::ostream& os, const JsonData& jd)
{ {
if (jd.dataType == JDType::STRING) return os << jd.GetStringData(); if (jd.dataType == JSON_DATA_TYPE::STRING) return os << jd.GetStringData();
else return os << jd.Render(JASONPP_STRINGCONV_MINIFY); else return os << jd.Render(JASONPP_STRINGCONV_MINIFY);
} }
} }
@ -2690,10 +2691,10 @@ bool JsonBlock::DoesShorthandExist(const std::string shorthand, const std::strin
const JsonBlock* jb = &const_cast<const JsonBlock&>(*this); const JsonBlock* jb = &const_cast<const JsonBlock&>(*this);
for (std::size_t i = 0; i < segments.size(); i++) for (std::size_t i = 0; i < segments.size(); i++)
{ {
if ((jb->DoesExist(segments[i])) && ((jb->Get(segments[i]).GetDataType() == JDType::JSON) || (i == segments.size() - 1))) if ((jb->DoesExist(segments[i])) && ((jb->Get(segments[i]).GetDataType() == JSON_DATA_TYPE::JSON) || (i == segments.size() - 1)))
{ {
if (i == segments.size() - 1) return true; // We are at the end. Let's just return it if (i == segments.size() - 1) return true; // We are at the end. Let's just return it
if (jb->Get(segments[i]).GetDataType() == JDType::JSON) jb = &jb->Get(segments[i]).GetJsonData(); if (jb->Get(segments[i]).GetDataType() == JSON_DATA_TYPE::JSON) jb = &jb->Get(segments[i]).GetJsonData();
else return false; else return false;
} }
else else
@ -2718,10 +2719,10 @@ const JsonData& JsonBlock::ShorthandGet(const std::string shorthand, const std::
const JsonBlock* jb = &const_cast<const JsonBlock&>(*this); const JsonBlock* jb = &const_cast<const JsonBlock&>(*this);
for (std::size_t i = 0; i < segments.size(); i++) for (std::size_t i = 0; i < segments.size(); i++)
{ {
if ((jb->DoesExist(segments[i])) && ((jb->Get(segments[i]).GetDataType() == JDType::JSON) || (i == segments.size() - 1))) if ((jb->DoesExist(segments[i])) && ((jb->Get(segments[i]).GetDataType() == JSON_DATA_TYPE::JSON) || (i == segments.size() - 1)))
{ {
if (i == segments.size() - 1) return jb->Get(segments[i]); // We are at the end. Let's just return it if (i == segments.size() - 1) return jb->Get(segments[i]); // We are at the end. Let's just return it
if (jb->Get(segments[i]).GetDataType() == JDType::JSON) jb = &jb->Get(segments[i]).GetJsonData(); if (jb->Get(segments[i]).GetDataType() == JSON_DATA_TYPE::JSON) jb = &jb->Get(segments[i]).GetJsonData();
else throw JsonShorthandInvalidException(shorthand); else throw JsonShorthandInvalidException(shorthand);
} }
else else
@ -2757,7 +2758,7 @@ JsonData& JsonBlock::ShorthandAdd(const std::string shorthand, const std::string
} }
else else
{ {
if (jb->Get(segments[i]).GetDataType() != JDType::JSON) throw JsonShorthandInvalidException(shorthand, "A path segment already exists and is not of type json!"); if (jb->Get(segments[i]).GetDataType() != JSON_DATA_TYPE::JSON) throw JsonShorthandInvalidException(shorthand, "A path segment already exists and is not of type json!");
jb = &jb->Get(segments[i]).GetJsonData(); jb = &jb->Get(segments[i]).GetJsonData();
} }
} }
@ -2804,7 +2805,7 @@ void JsonBlock::ShorthandRemove(const std::string shorthand, const std::string d
JsonData* dt = &ShorthandGet(shorthandParent, delimiter); JsonData* dt = &ShorthandGet(shorthandParent, delimiter);
// Is the parent object of the object to be deleted even of type json? // Is the parent object of the object to be deleted even of type json?
if (dt->GetDataType() != JDType::JSON) throw JsonShorthandInvalidException(shorthand, "The parent of the object to be deleted is not of type json!"); if (dt->GetDataType() != JSON_DATA_TYPE::JSON) throw JsonShorthandInvalidException(shorthand, "The parent of the object to be deleted is not of type json!");
parentJson = &dt->GetJsonData(); parentJson = &dt->GetJsonData();
} }

View File

@ -2142,7 +2142,7 @@ namespace JasonPP
}; };
} }
#define JASONPP_VERSION (1.021) #define JASONPP_VERSION (1.0215)
namespace JasonPP namespace JasonPP
{ {

View File

@ -2,6 +2,7 @@
using namespace Rest; using namespace Rest;
using namespace Logging; using namespace Logging;
using namespace Downloader;
using namespace JasonPP; using namespace JasonPP;
void RestQueryHandler::PreInit() void RestQueryHandler::PreInit()
@ -26,6 +27,9 @@ bool RestQueryHandler::ProcessQuery(const std::string clientAdress, const Json&
if (requestName == "kill_yourself") return KillYourself(requestBody, responseBody, responseCode); if (requestName == "kill_yourself") return KillYourself(requestBody, responseBody, responseCode);
else if (requestName == "queue_download") return QueueDownload(requestBody, responseBody, responseCode);
else if (requestName == "fetch_queue") return FetchQueue(requestBody, responseBody, responseCode);
else if (requestName == "clear_download_cache") return ClearDownloadCache(requestBody, responseBody, responseCode);
else if (requestName == "foo") return Example_Foo(requestBody, responseBody, responseCode); else if (requestName == "foo") return Example_Foo(requestBody, responseBody, responseCode);
@ -52,6 +56,57 @@ bool RestQueryHandler::Example_Foo(const JsonBlock& request, JsonBlock& response
return true; return true;
} }
bool RestQueryHandler::QueueDownload(const JsonBlock& request, JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode)
{
if ((!ValidateField("video_url", JDType::STRING, request, responseBody)) ||
(!ValidateField("mode", JDType::STRING, request, responseBody)))
{
responseCode = BAD_REQUEST;
return false;
}
std::string modeParam = request.Get("mode").AsString;
std::string videoUrl = request.Get("video_url").AsString;
DOWNLOAD_MODE mode;
if (modeParam == "video") mode = DOWNLOAD_MODE::VIDEO;
else if (modeParam == "audio") mode = DOWNLOAD_MODE::AUDIO;
else
{
responseCode = BAD_REQUEST;
responseBody.CloneFrom(RestResponseTemplates::GetByCode(BAD_REQUEST, "Parameter 'mode' is of wrong value. Should be either 'video' or 'audio'."));
return false;
}
log->cout << "Queued video \"" << videoUrl << "\"...";
log->Flush();
std::string tubId = DownloadManager::QueueDownload(videoUrl, mode);
responseCode = OK;
responseBody.CloneFrom(RestResponseTemplates::GetByCode(OK));
responseBody.Set("message") = "Download queued!";
responseBody.Set("queue_position") = (long long int)DownloadManager::GetQueueLength();
responseBody.Set("tubio_id") = tubId;
responseBody.Set("queue") = DownloadManager::GetQueueAsJson();
return true;
}
bool RestQueryHandler::FetchQueue(const JsonBlock& request, JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode)
{
responseCode = OK;
responseBody.CloneFrom(RestResponseTemplates::GetByCode(OK));
responseBody.Set("queue") = DownloadManager::GetQueueAsJson();
return true;
}
bool RestQueryHandler::ClearDownloadCache(const JsonBlock& request, JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode)
{
responseCode = OK;
responseBody.CloneFrom(RestResponseTemplates::GetByCode(OK));
DownloadManager::ClearDownloadCache();
return true;
}
bool RestQueryHandler::KillYourself(const JsonBlock& request, JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode) bool RestQueryHandler::KillYourself(const JsonBlock& request, JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode)
{ {
XGControl::keepServerRunning = false; XGControl::keepServerRunning = false;
@ -65,6 +120,23 @@ bool RestQueryHandler::KillYourself(const JsonBlock& request, JsonBlock& respons
return true; return true;
} }
bool RestQueryHandler::ValidateField(const std::string name, const JasonPP::JDType type, const JasonPP::Json& checkThat, JasonPP::JsonBlock& putErrorResponseHere) bool RestQueryHandler::ValidateField(const std::string name, const JasonPP::JDType type, const JasonPP::Json& checkThat, JasonPP::JsonBlock& putErrorResponseHere)
{ {
if (checkThat.GetDataType() != JDType::JSON) if (checkThat.GetDataType() != JDType::JSON)

View File

@ -3,6 +3,7 @@
#include "RestResponseTemplates.h" #include "RestResponseTemplates.h"
#include "XGControl.h" #include "XGControl.h"
#include "Logger.h" #include "Logger.h"
#include "DownloadManager.h"
namespace Rest namespace Rest
{ {
@ -17,6 +18,9 @@ namespace Rest
private: private:
static bool Example_Foo(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode); static bool Example_Foo(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
static bool FetchQueue(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
static bool QueueDownload(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
static bool ClearDownloadCache(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
static bool KillYourself(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode); static bool KillYourself(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
static bool ValidateField(const std::string name, const JasonPP::JDType type, const JasonPP::Json& checkThat, JasonPP::JsonBlock& putErrorResponseHere); static bool ValidateField(const std::string name, const JasonPP::JDType type, const JasonPP::Json& checkThat, JasonPP::JsonBlock& putErrorResponseHere);

View File

@ -116,6 +116,7 @@
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
@ -139,6 +140,7 @@
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="DownloadManager.cpp" />
<ClCompile Include="FileSystem.cpp" /> <ClCompile Include="FileSystem.cpp" />
<ClCompile Include="Framework.cpp" /> <ClCompile Include="Framework.cpp" />
<ClCompile Include="JasonPP.cpp" /> <ClCompile Include="JasonPP.cpp" />
@ -153,6 +155,7 @@
<ClCompile Include="XGControl.cpp" /> <ClCompile Include="XGControl.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="DownloadManager.h" />
<ClInclude Include="FileSystem.h" /> <ClInclude Include="FileSystem.h" />
<ClInclude Include="Framework.h" /> <ClInclude Include="Framework.h" />
<ClInclude Include="JasonPP.hpp" /> <ClInclude Include="JasonPP.hpp" />

View File

@ -54,6 +54,9 @@
<ClCompile Include="HttpServer.cpp"> <ClCompile Include="HttpServer.cpp">
<Filter>Quelldateien</Filter> <Filter>Quelldateien</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="DownloadManager.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="JasonPP.hpp"> <ClInclude Include="JasonPP.hpp">
@ -92,5 +95,8 @@
<ClInclude Include="HttpServer.h"> <ClInclude Include="HttpServer.h">
<Filter>Headerdateien</Filter> <Filter>Headerdateien</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="DownloadManager.h">
<Filter>Headerdateien</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -39,6 +39,15 @@ void XGConfig::InitializeDefaultValues()
logging.logfile_text = "log.txt"; logging.logfile_text = "log.txt";
logging.logfile_json = "log.json"; logging.logfile_json = "log.json";
downloader.cachedir = "dlcache";
downloader.max_dlrate_per_thread = "100M";
downloader.num_threads = 1;
downloader.loginCredentials.use_account = false;
downloader.loginCredentials.username = "";
downloader.loginCredentials.password = "";
downloader.loginCredentials.twofactor = "";
return; return;
} }
@ -72,6 +81,37 @@ void XGConfig::LoadFromJson(const JasonPP::JsonBlock& json)
} }
if (IsJsonFieldValid(json, "downloader.cachedir", JDType::STRING))
{
downloader.cachedir = json.ShorthandGet("downloader.cachedir").AsString;
}
if (IsJsonFieldValid(json, "downloader.num_threads", JDType::INT))
{
downloader.num_threads = json.ShorthandGet("downloader.num_threads").AsInt;
}
if (IsJsonFieldValid(json, "downloader.max_dlrate_per_thread", JDType::STRING))
{
downloader.max_dlrate_per_thread = json.ShorthandGet("downloader.max_dlrate_per_thread").AsString;
}
if (IsJsonFieldValid(json, "downloader.loginCredentials.use_account", JDType::BOOL))
{
downloader.loginCredentials.use_account = json.ShorthandGet("downloader.loginCredentials.use_account").AsBool;
}
if (IsJsonFieldValid(json, "downloader.loginCredentials.username", JDType::STRING))
{
downloader.loginCredentials.username = json.ShorthandGet("downloader.loginCredentials.username").AsString;
}
if (IsJsonFieldValid(json, "downloader.loginCredentials.password", JDType::STRING))
{
downloader.loginCredentials.password = json.ShorthandGet("downloader.loginCredentials.password").AsString;
}
if (IsJsonFieldValid(json, "downloader.loginCredentials.twofactor", JDType::STRING))
{
downloader.loginCredentials.twofactor = json.ShorthandGet("downloader.loginCredentials.twofactor").AsString;
}
return; return;
} }
@ -86,6 +126,17 @@ JsonBlock XGConfig::CreateJson()
Ele("logging", JsonBlock({ Ele("logging", JsonBlock({
Ele("logfile_text", logging.logfile_text), Ele("logfile_text", logging.logfile_text),
Ele("logfile_json", logging.logfile_json) Ele("logfile_json", logging.logfile_json)
})),
Ele("downloader", JsonBlock({
Ele("cachedir", downloader.cachedir),
Ele("max_dlrate_per_thread", downloader.max_dlrate_per_thread),
Ele("num_threads", downloader.num_threads),
Ele("login_credentials", JsonBlock({
Ele("use_account", downloader.loginCredentials.use_account),
Ele("username", downloader.loginCredentials.username),
Ele("password", downloader.loginCredentials.password),
Ele("twofactor", downloader.loginCredentials.twofactor)
}))
})) }))
}); });
} }
@ -197,4 +248,5 @@ void XGConfig::Load()
XGConfig::HttpServer XGConfig::httpServer; XGConfig::HttpServer XGConfig::httpServer;
XGConfig::Logging XGConfig::logging; XGConfig::Logging XGConfig::logging;
XGConfig::Downloader XGConfig::downloader;
::Logging::Logger* XGConfig::log; ::Logging::Logger* XGConfig::log;

View File

@ -20,7 +20,22 @@ public:
std::string logfile_text; std::string logfile_text;
std::string logfile_json; std::string logfile_json;
}; };
struct Downloader
{
struct LoginCredentials
{
bool use_account;
std::string username;
std::string password;
std::string twofactor;
};
std::string cachedir;
std::string max_dlrate_per_thread;
LoginCredentials loginCredentials;
int num_threads;
};
static void PreInit(); static void PreInit();
static void Save(); static void Save();
@ -28,6 +43,7 @@ public:
static HttpServer httpServer; static HttpServer httpServer;
static XGConfig::Logging logging; static XGConfig::Logging logging;
static XGConfig::Downloader downloader;
static ::Logging::Logger* log; static ::Logging::Logger* log;

View File

@ -3,6 +3,7 @@
int main() int main()
{ {
Framework framework; Framework framework;
framework.Run();
return 0; return 0;
} }

BIN
Tubio/youtube-dl.exe Normal file

Binary file not shown.