New db layout
This commit is contained in:
@@ -9,8 +9,10 @@
|
||||
#include <functional>
|
||||
|
||||
#include <ctre.hpp>
|
||||
#include <fmt/chrono.h>
|
||||
#include <fmt/ostream.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <openssl/md5.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
using json = nlohmann::json;
|
||||
@@ -28,49 +30,82 @@ void Api::RegisterServerHandles(httplib::Server& server)
|
||||
|
||||
server.Get("/api/files", std::bind_front(&Api::files, this));
|
||||
|
||||
server.Get("/api/file/(\\d+)", std::bind_front(&Api::file, this));
|
||||
server.Get("/api/file/([A-E0-9]+)", std::bind_front(&Api::file, this));
|
||||
|
||||
server.Options("/(.*)", [this](httplib::Request const& rq, httplib::Response& rp) {});
|
||||
|
||||
server.set_pre_routing_handler([this](httplib::Request const& rq, httplib::Response& rp) {
|
||||
this->set_cross_headers(rp);
|
||||
spdlog::info("Request {}", rq.method);
|
||||
spdlog::info("Request {} {}", rq.method, rq.path);
|
||||
return httplib::Server::HandlerResponse::Unhandled;
|
||||
});
|
||||
}
|
||||
|
||||
/* /api/add
|
||||
*
|
||||
* body: json
|
||||
* - url
|
||||
* - audio_only - default false
|
||||
* - format - optional default source
|
||||
*
|
||||
* return: json
|
||||
* - status: bool
|
||||
* - file_id: hash
|
||||
* - error: string
|
||||
*
|
||||
*/
|
||||
|
||||
void Api::add(const httplib::Request& rq, httplib::Response& rs)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto data = json::parse(rq.body);
|
||||
|
||||
File f;
|
||||
// Parse Request
|
||||
Task task;
|
||||
|
||||
task.url = data["url"];
|
||||
task.audio_only = data.contains("audio_only") && data["audio_only"].get<bool>();
|
||||
|
||||
f.url = data["url"];
|
||||
f.status = FileStatus::PENDING;
|
||||
if (data.contains("format"))
|
||||
task.format = data["format"].get<std::string>();
|
||||
|
||||
sql << "INSERT INTO Files (url, status) values(:url, :status)", soci::use(f.url, "url"),
|
||||
soci::use(f.status, "status");
|
||||
task.timestamp = std::time(nullptr);
|
||||
|
||||
long long int tmp;
|
||||
task.hash = fmt::format(
|
||||
"{:X}",
|
||||
std::hash<std::string>()(
|
||||
task.url + fmt::format("{:%Y-%m-%d %H:%M:%S}", *std::localtime(&task.timestamp))));
|
||||
task.status = FileStatus::PENDING;
|
||||
|
||||
// Insert Sql
|
||||
|
||||
sql << "INSERT INTO Tasks (hash, timestamp, url, audio_only, status, format) "
|
||||
"values(:hash, :timestamp, :url, :audio_only, :status, :format) ",
|
||||
soci::use(task);
|
||||
|
||||
/*long long int tmp;
|
||||
sql.get_last_insert_id("Files", tmp);
|
||||
f.id = tmp;
|
||||
f.id = tmp;*/
|
||||
|
||||
json jr;
|
||||
jr["status"] = "Ok";
|
||||
jr["id"] = f.id;
|
||||
jr["status"] = "Ok";
|
||||
jr["file_id"] = task.hash;
|
||||
|
||||
spdlog::info("New Task: {}", f);
|
||||
spdlog::info("New Task: {}", task);
|
||||
|
||||
rs.set_content(jr.dump(), "application/json");
|
||||
}
|
||||
catch (json::exception const& e)
|
||||
{
|
||||
spdlog::error("Api Error: {}", e.what());
|
||||
|
||||
json jr;
|
||||
jr["status"] = "Err";
|
||||
jr["error"] = e.what();
|
||||
|
||||
rs.status = 400;
|
||||
rs.set_content(std::format("Json Error: {}", e.what()), "text/plain");
|
||||
rs.set_content(jr.dump(), "application/json");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,37 +115,52 @@ void Api::files(httplib::Request const& rq, httplib::Response& rs)
|
||||
{
|
||||
json res;
|
||||
|
||||
soci::rowset<File> data = (sql.prepare << "SELECT * FROM Files ORDER BY id DESC");
|
||||
// Query incomplete Tasks
|
||||
|
||||
soci::rowset<Task> data = (sql.prepare << "SELECT * "
|
||||
"FROM Tasks "
|
||||
"WHERE status!=2 "
|
||||
"ORDER BY id DESC ");
|
||||
|
||||
for (auto const& el : data)
|
||||
{
|
||||
json current;
|
||||
current["id"] = el.id;
|
||||
current["status"] = magic_enum::enum_name(el.status);
|
||||
current["status_id"] = static_cast<int>(el.status);
|
||||
current["source"] = el.url;
|
||||
current["name"] = fmt::format("File {}", el.id);
|
||||
|
||||
current["file_name"] = "Unknown";
|
||||
|
||||
if (el.status == FileStatus::COMPLETE && false)
|
||||
{
|
||||
std::string filename;
|
||||
soci::rowset<std::string> ret =
|
||||
(sql.prepare << "SELECT path From Downloads WHERE id=:id",
|
||||
soci::use(el.id, "id"));
|
||||
|
||||
if (ret.begin() != ret.end())
|
||||
{
|
||||
std::filesystem::path p(*ret.begin());
|
||||
|
||||
current["file_name"] = p.filename();
|
||||
}
|
||||
}
|
||||
current["hash"] = el.hash;
|
||||
current["status"] = magic_enum::enum_name(el.status);
|
||||
current["status_id"] = static_cast<int>(el.status);
|
||||
current["source"] = el.url;
|
||||
current["name"] = fmt::format("File {}", el.id);
|
||||
current["audio_only"] = el.audio_only;
|
||||
current["format"] = el.format.value_or("Source");
|
||||
|
||||
res["queue"].push_back(current);
|
||||
}
|
||||
|
||||
// Downloaded
|
||||
|
||||
soci::rowset<soci::row> files =
|
||||
(sql.prepare
|
||||
<< R"(SELECT Tasks.id, Files.timestamp, Files.hash, audio_only, filename, Files.format
|
||||
FROM Files,
|
||||
Tasks
|
||||
WHERE Tasks.hash == Files.hash
|
||||
ORDER BY Files.timestamp DESC )");
|
||||
|
||||
for (auto const& el : files)
|
||||
{
|
||||
json current;
|
||||
|
||||
std::time_t time = el.get<int>("timestamp");
|
||||
|
||||
current["timestamp"] = fmt::format("{:%Y-%m-%d %H:%M:%S}", *std::localtime(&time));
|
||||
current["hash"] = el.get<std::string>("hash");
|
||||
current["audio_only"] = el.get<int>("audio_only");
|
||||
current["filename"] = el.get<std::string>("filename");
|
||||
current["format"] = el.get<std::string>("format");
|
||||
|
||||
res["files"].push_back(current);
|
||||
}
|
||||
|
||||
rs.set_content(res.dump(), "application/json");
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
@@ -130,28 +180,23 @@ void Api::file(const httplib::Request& rq, httplib::Response& rs)
|
||||
}
|
||||
|
||||
auto file = rq.matches[1];
|
||||
|
||||
spdlog::info("File Request: {}", file);
|
||||
|
||||
try
|
||||
{
|
||||
Download dw;
|
||||
sql << fmt::format("SELECT * FROM Downloads WHERE id={};", file), soci::into(dw);
|
||||
File dw;
|
||||
sql << fmt::format("SELECT * FROM Files WHERE hash='{}';", file), soci::into(dw);
|
||||
|
||||
|
||||
std::ifstream fs(dw.path, std::ios_base::binary);
|
||||
std::ifstream fs(dw.local_path, std::ios_base::binary);
|
||||
fs.seekg(0, std::ios_base::end);
|
||||
auto size = fs.tellg();
|
||||
fs.seekg(0);
|
||||
rs.body.resize(static_cast<size_t>(size));
|
||||
fs.read(&rs.body[0], static_cast<std::streamsize>(size));
|
||||
|
||||
rs.set_header("Content-Disposition", fmt::format("attachment; filename={};", dw.path));
|
||||
if (auto [match, type] = ctre::match<R"(.*\.(.*))">(dw.path); match)
|
||||
{
|
||||
spdlog::debug(type);
|
||||
rs.set_header("content-type", fmt::format("application/{}", type.to_view()));
|
||||
}
|
||||
rs.set_header("Content-Disposition", fmt::format("attachment; filename={};", dw.filename));
|
||||
rs.set_header("content-type", fmt::format("application/{}", dw.format));
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user