Implemented barebone rest-queries for youtube downloading. Still needs a lot of polishing!
This commit is contained in:
parent
17d7e743df
commit
96d11c80f3
6
.gitignore
vendored
6
.gitignore
vendored
@ -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
|
||||||
|
34
README.md
34
README.md
@ -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
476
Tubio/DownloadManager.cpp
Normal 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
95
Tubio/DownloadManager.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
@ -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;
|
||||||
|
@ -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:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2142,7 +2142,7 @@ namespace JasonPP
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#define JASONPP_VERSION (1.021)
|
#define JASONPP_VERSION (1.0215)
|
||||||
|
|
||||||
namespace JasonPP
|
namespace JasonPP
|
||||||
{
|
{
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
@ -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" />
|
||||||
|
@ -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>
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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
BIN
Tubio/youtube-dl.exe
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user