Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot]
f899af480a
Bump node-fetch from 2.6.1 to 2.6.7 in /tubio-frontend-nuxt-app
Bumps [node-fetch](https://github.com/node-fetch/node-fetch) from 2.6.1 to 2.6.7.
- [Release notes](https://github.com/node-fetch/node-fetch/releases)
- [Commits](https://github.com/node-fetch/node-fetch/compare/v2.6.1...v2.6.7)

---
updated-dependencies:
- dependency-name: node-fetch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-02-06 13:43:40 +00:00
50 changed files with 886 additions and 937 deletions

4
.gitignore vendored
View File

@ -11,7 +11,6 @@
*.sln.docstates
# Tubio files
/Tubio/Tubio
config.json
log.txt
log.json
@ -29,7 +28,6 @@ ffprobe.exe
mono_crash.*
# Build results
build/
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
@ -373,4 +371,4 @@ MigrationBackup/
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
FodyWeavers.xsd

278
README.md
View File

@ -1,139 +1,139 @@
![Tubio](https://gitea.leonetienne.de/leonetienne/Tubio/raw/branch/master/github-media/teaser.png)
# Tubio
The free, open source video downloader!
No longer do you have to rely on shady websites, shoving tons of ads in your face to fulfil your downloady needs. No longer will you be held back by artificially crippled download speeds, login-/paywalls or even watermarks.
## But what is Tubio?
Tubio in of itself is not a downloader, but a GUI for the widely known, open-source, public-domain cli [yt-dlp](https://github.com/yt-dlp/yt-dlp/). <sup>Thanks for this awesome tool! You guys are heroes! (Obviously, same goes for youtube-dl, which yt-dlp is based on)</sup>
The goal of Tubio is to make this awesome software more accessible. Not everyone knows how to use the command line!
## But, how does it work?
It\`s quite easy! Make sure the Tubio service is running in the background, navigate to http://localhost, paste in your video/music url and chances are that it will work!
This is because the set of supported websites is **extremely** large! See here: [supportedsites.md](https://github.com/blackjack4494/yt-dlc/blob/master/docs/supportedsites.md)
When your download finished, just click "download" and have fun!
## No, i mean on a more technical level
The backend is a C++ webserver, powered by [casenta/mongoose](https://github.com/cesanta/mongoose).<sup>Thanks, you guys are awesome!</sup>
The connection is **not** encrypted, but that\`s okay because it is intended for localhost only. Mongoose does support ssl, so you can always add it, if you fancy it.
The frontend is a nuxt.js web application.
## But how does it look?
Have a sneak peak!
![soundcloud](https://gitea.leonetienne.de/leonetienne/Tubio/raw/branch/master/github-media/soundcloud.png)
![reddit](https://gitea.leonetienne.de/leonetienne/Tubio/raw/branch/master/github-media/reddit.png)
![youtube](https://gitea.leonetienne.de/leonetienne/Tubio/raw/branch/master/github-media/youtube.png)
![settings](https://gitea.leonetienne.de/leonetienne/Tubio/raw/branch/master/github-media/settings.png)
![mobile](https://gitea.leonetienne.de/leonetienne/Tubio/raw/branch/master/github-media/mobile.png)
## There's also a Chrome Extension
![extension-popup](https://gitea.leonetienne.de/leonetienne/Tubio/raw/branch/master/github-media/chromeext-popup.png)
![extension-contextmenu](https://gitea.leonetienne.de/leonetienne/Tubio/raw/branch/master/github-media/chromeext-context.png)
## How can i configure it?
See this json file (config.json).
Most of these values are configurable in the web-interface, but not all, such as the port.
```json
{
"access": {
"enable_whitelist": true,
"only_allow_localhost": false,
"whitelist": [
"127.0.0.1",
"192.168.1.12",
"192.168.1.14",
"192.168.1.10"
]
},
"downloader": {
"cachedir": "dlcache",
"max_dlrate_per_thread": "100M",
"num_threads": 10
},
"general": {
"show_console": true
},
"httpServer": {
"polling_rate": 100,
"port": "80",
"rootdir": "./frontend"
},
"logging": {
"autosave_interval": 20,
"logfile_json": "log.json",
"logfile_text": "log.txt"
}
}
```
## Can i use Tubio on multiple devices?
Tubio hosts a webserver, after all. It is intended to be used for localhost only (since no encryption), but no one is preventing you from unticking `localhost only` under `/settings`. Then you could connect to it via your local IPv4 address (such as `192.168.1.12`) or even over the global WAN! However, regarding WAN, i would **strongly** advise against such a careless setup.
It wouldn't be that complicated to enable TLS. You'd have to install and link `libcryptopp`, obtain a certificate + key, and pass them to the mongoose webserver. But that would break that whole *"compiles without any dependencies thing"*. See [the mongoose docs TLS page](https://mongoose.ws/tutorials/tls/) for instructions. If you implement this cleanly, like with a special make target, a merge request would be greatly appreciated.
!!! IMPORTANT
Tubio does NOT manage sessions or accounts! Everyone using your Tubio instance will see **all your downloads** and vica versa.
If you opt for unleashing Tubio on your LAN, i would **strongly** recommend enabling the whitelist! You can do this either in the `config.json` or in /settings. Either way, it is a json-array of strings which represent IPv4 addresses.
## Setup (Linux)
1) Clone this repository and build it with cmake
2) Create some folder on your system. This will be the installation folder.
3) In this folder, dump the contents of /Tubio/.
4) Install python3 and then yt-dlp via `pip install yt-dlp`.
5) Launch the tubio executable
6) Enjoy &lt;3
## Frequently Asked Questions
### My downloads hang at 99% or 100%
This happens as there almost always is some post-processing to do after downloading. Like, audio-extracting and/or video recoding. If you're downloading
a video this may take a while, as it may be converting webm to mp4.
### My downloads fail!
First thing to do: Verify that are using the latest version of yt-dlp. The team behind its upstream, youtube-dl, are quite fast adjusting the downloading backend to changes in popular platforms, such as youtube. If your downloads still fail,
verify that yt-dlp is in fact capable of downloading the url you supplied by executing `yt-dlp $url`, replacing `$url` with your url. If this works, you can extract the tubio id of the url you just queued in `/settings`, and then open `<your-tubio-host>/downloadlog/<tubio-id>` to see the output stdout and stderr of the yt-dlp call. Then work from the error messages you see in there.
If it\`s still not working, you\`re out of luck. :(
You may want to check youtube-dl compatible sites.
### I locked myself out by enabling localhost only on another device!
This can only be undone from localhost. Open Tubio via, and this is important, either `localhost` or `127.0.0.1` and untick it again. If you can only ssh into the host, you can edit the `config.json` itself and restart Tubio.
### Does it work on Windows?
Sure it does. You just have to compile it yourself using cmake, and put yt-dlp.exe, ffmpeg.exe, ffplay.exe and ffprobe.exe
in Tubios working directory (installation directory). You could then just launch it at startup.
### Can I use it on my phone?
You bet! That's what I'm mainly using it for :) Read [this](#user-content-can-i-use-tubio-on-multiple-devices).
### Can I use this to host my own downloader website?
On your own risk! Tubio is NOT designed for this! Also do note that tubio does NOT manage sessions or accounts! Everyone accessing this instance can see everyones downloads and access the admin panel! Tubio is really designed for one user!
### XY is not working, you have to fix it now!
I do not. Tubio is a tool I originally made for myself only. I just thought it\`s nice and worth sharing. I will address issues when I have time. Feel free to submit issues and I will have a look when I get to it. :)
### XY is not polished enough!
This is an alpha-version. What did you expect? :D
### Can you please add support for website XY?
Please address the awesome team at youtube-dl, as they make the downloading-backend: [github.com/ytdl-org/youtube-dl/issues](https://github.com/ytdl-org/youtube-dl/issues).
The downloader uses ([yt-dlp](https://github.com/yt-dlp/yt-dlp/)), which is based on youtube-dl.
## Important notice!
I do NOT endorse illegal downloads in any way, shape, or form. Tubio is a tool to download media from legal sources! Use Tubio at your own discretion! Neither do i provide ANY warranty in ANY way, shape, or form!
## License
Tubio is distributed under the GNU General Public License v3.0.
Please read the [license file](https://gitea.leonetienne.de/leonetienne/Tubio/src/branch/master/LICENSE).
![Tubio](https://raw.githubusercontent.com/Leonetienne/Tubio/master/github-media/teaser.png)
# Tubio
The free, open source video downloader!
No longer do you have to rely on shady websites, shoving tons of ads in your face to fulfil your downloady needs. No longer will you be held back by artificially crippled download speeds, login-/paywalls or even watermarks.
## But what is Tubio?
Tubio in of itself is not a downloader, but a GUI for the widely known, open-source, public-domain cli [youtube-dl](http://youtube-dl.org/). <sup>Thanks for this awesome tool! You guys are heroes!</sup>
The goal of Tubio is to make this awesome software more accessible. Not everyone knows how to use the command line!
## But, how does it work?
It\`s quite easy! Make sure the Tubio service is running in the background, navigate to http://localhost, paste in your video/music url and chances are that it will work!
This is because the set of supported websites is **extremely** large! See here: [supportedsites.md](https://github.com/blackjack4494/yt-dlc/blob/master/docs/supportedsites.md)
When your download finished, just click "download" and have fun!
## No, i mean on a more technical level
The backend is a C++ webserver, powered by [casenta/mongoose](https://github.com/cesanta/mongoose).<sup>Thanks, you guys are awesome!</sup>
The connection is **not** encrypted, but that\`s okay because it is intended for localhost only. Mongoose does support ssl, so you can always add it, if you fancy it.
The frontend is a nuxt.js web application.
## But how does it look?
Have a sneak peak!
![soundcloud](https://raw.githubusercontent.com/Leonetienne/Tubio/master/github-media/soundcloud.png)
![reddit](https://raw.githubusercontent.com/Leonetienne/Tubio/master/github-media/reddit.png)
![youtube](https://raw.githubusercontent.com/Leonetienne/Tubio/master/github-media/youtube.png)
![settings](https://raw.githubusercontent.com/Leonetienne/Tubio/master/github-media/settings.png)
![mobile](https://raw.githubusercontent.com/Leonetienne/Tubio/master/github-media/mobile.png)
## NEW! Get the Chrome Companion Extension!
![extension-popup](https://raw.githubusercontent.com/Leonetienne/Tubio/master/github-media/chromeext-popup.png)
![extension-contextmenu](https://raw.githubusercontent.com/Leonetienne/Tubio/master/github-media/chromeext-context.png)
## How can i configure it?
See this json file (config.json).
Most of these values are configurable in the web-interface, but not all, such as the port.
```json
{
"access": {
"enable_whitelist": true,
"only_allow_localhost": false,
"whitelist": [
"127.0.0.1",
"192.168.1.12",
"192.168.1.14",
"192.168.1.10"
]
},
"downloader": {
"cachedir": "dlcache",
"max_dlrate_per_thread": "100M",
"num_threads": 10
},
"general": {
"show_console": true
},
"httpServer": {
"polling_rate": 100,
"port": "80",
"rootdir": "./frontend"
},
"logging": {
"autosave_interval": 20,
"logfile_json": "log.json",
"logfile_text": "log.txt"
}
}
```
## Can i use Tubio on multiple devices?
Tubio is a webserver, after all. It is intended to be used for localhost only (since no encryption), but no one is preventing you from unticking `localhost only` under /settings.
Then you could connect to it via your local IPv4 address (such as `192.168.1.12`) or even over the global WAN! However, regarding WAN, i would **strongly** advise against such a careless setup.
!!! IMPORTANT
Tubio does NOT manage sessions or accounts! Everyone using your Tubio instance will see **all your downloads** and vica versa.
If you opt for unleashing Tubio on your LAN, i would **strongly** recommend enabling the whitelist! You can do this either in the `config.json` or in /settings. Either way, it is a json-array of strings which represent IPv4 addresses.
## Setup (Windows)
#### Install youtube-dl.exe:
1) Download the latest Tubio build from [here](https://github.com/Leonetienne/Tubio/releases).
2) Create some folder on your pc. This will be the installation folder.
3) Dump in the contents of the Tubio build you just downloaded.
4) Launch vcredist_x86.exe and install it. This will install Microsoft Visual C++ 2010 Redistributable Package (x86) which is needed for dependencies.
5) Launch VC_redist.x64.exe and install it. This will install Microsoft Visual C++ 2015/17/19 Redistributable Package (x64) which is needed to run Tubio.exe
6) Launch Tubio.exe
7) Navigate to `http://localhost/settings` and click "Update ytdl", as the version Tubio shipped with might be out of date.
8) &lt;Optional&gt;: Set up `Tubio.exe` to launch with windows to have it\`s service always at hand.
9) Enjoy &lt;3
## Frequently Asked Questions
### My downloads hang at 99% or 100%
This happens as there often is some post-processing to do after downloading.
### My downloads fail!
First thing to do: Navigate to /settings and click "Update ytdl". This will update your local instance of [youtube-dl](http://youtube-dl.org/). Check the logs to see if it worked. If not, restart Tubio and try again.
If it\`s still not working, you are most likely trying to download a video from a playlist with a weird url. Try a different one (The one from the share button, the one from right-clicking the video or the one from the url-bar in your browser).
If it\`s still not working, you\`re out of luck. :(
### I locked myself out by enabling localhost only on another device!
This can only be undone from the host. Open Tubio via, and this is important, either `localhost` or `127.0.0.1` and untick it again. If you can only ssh into the host, you can edit the `config.json` itself and restart Tubio.
### Does it work on linux?
Well, technically, yes. You would have to install youtube-dl and the ffmpeg suite yourself, and compile Tubio yourself though. I have not tried it yet, but it should work.
### Can i use it on my phone?
Sure. Read [this](#user-content-can-i-use-tubio-on-multiple-devices).
### Can i use this to host my own downloader website?
On your own risk! Tubio is NOT designed for this! Also do note that tubio does NOT manage sessions or accounts! Everyone accessing this instance can see everyones downloads and access the admin panel! Tubio is really designed for one user!
### XY is not working, you have to fix it now!
I do not. Tubio is a tool i originally made for myself only. I just thought it\`s nice and worth sharing. I will address issues when i have time. Feel free to submit issues and i will have a look when i get to it. :)
### XY is not polished enough!
This is an alpha-version. What did you expect? :D
### Can you please add support for website XY?
Please address the awesome team at youtube-dl, as they made the downloading-end: [github.com/ytdl-org/youtube-dl/issues](https://github.com/ytdl-org/youtube-dl/issues)
## Important notice!
I do NOT endorse illegal downloads in any way, shape, or form. Tubio is a tool to download media from legal sources! Use Tubio at your own discretion! Neither do i provide ANY warranty in ANY way, shape, or form!
## License
Tubio is distributed under the GNU General Public License v3.0.
Please read the [license file](https://github.com/Leonetienne/Tubio/blob/master/license.txt).

31
Tubio.sln Normal file
View File

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30413.136
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Tubio", "Tubio\Tubio.vcxproj", "{8AE799C5-CB17-4714-A362-D8B9817A723E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8AE799C5-CB17-4714-A362-D8B9817A723E}.Debug|x64.ActiveCfg = Debug|x64
{8AE799C5-CB17-4714-A362-D8B9817A723E}.Debug|x64.Build.0 = Debug|x64
{8AE799C5-CB17-4714-A362-D8B9817A723E}.Debug|x86.ActiveCfg = Debug|Win32
{8AE799C5-CB17-4714-A362-D8B9817A723E}.Debug|x86.Build.0 = Debug|Win32
{8AE799C5-CB17-4714-A362-D8B9817A723E}.Release|x64.ActiveCfg = Release|x64
{8AE799C5-CB17-4714-A362-D8B9817A723E}.Release|x64.Build.0 = Release|x64
{8AE799C5-CB17-4714-A362-D8B9817A723E}.Release|x86.ActiveCfg = Release|Win32
{8AE799C5-CB17-4714-A362-D8B9817A723E}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {11C6C438-21D5-40CC-9CDC-FBA3AE6634F3}
EndGlobalSection
EndGlobal

View File

@ -1,53 +0,0 @@
cmake_minimum_required(VERSION 3.16)
project(Tubio)
# Set C++ standard
set(CMAKE_CXX_STANDARD 17)
# Add external-directories dir to include dir list
include_directories(./external_dependencies/)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
# Add absolutely kawaii sources to Tubio <3
add_executable(Tubio
main.cpp
ConsoleManager.cpp
ConsoleManager.h
DownloadManager.cpp
DownloadManager.h
FileSystem.cpp
FileSystem.h
Framework.cpp
Framework.h
HttpServer.cpp
HttpServer.h
Idler.cpp
Idler.h
LogHistory.cpp
LogHistory.h
LogTypes.h
Logger.cpp
Logger.h
RestQueryHandler.cpp
RestQueryHandler.h
RestResponseTemplates.cpp
RestResponseTemplates.h
TimeUnits.h
Updater.cpp
Updater.h
Version.h
XGConfig.cpp
XGConfig.h
XGControl.cpp
XGControl.h
external_dependencies/casenta/mongoose/mongoose.c
external_dependencies/casenta/mongoose/mongoose.h
external_dependencies/leonetienne/JasonPP/JasonPP.cpp
external_dependencies/leonetienne/JasonPP/JasonPP.hpp
external_dependencies/leonetienne/stringtools/StringTools.cpp
external_dependencies/leonetienne/stringtools/StringTools.h
)
target_link_libraries(Tubio pthread)

View File

@ -160,9 +160,9 @@ void DownloadManager::DownloadNext()
{
// Call template
std::string ytdl_call_video_base =
"yt-dlp --newline --no-call-home --no-playlist --no-part --no-warnings --socket-timeout 5 --limit-rate $$DL_RATE"
"youtube-dl --newline --no-call-home --no-playlist --no-part --no-warnings --socket-timeout 5 --limit-rate $$DL_RATE"
" --no-mtime --no-cache-dir -f \"$$QUALITY\" --recode-video mp4 --prefer-ffmpeg"
" -o \"$$DL_FILE\" \"$$DL_URL\" > \"$$DL_PROG_BUF_FILE\" 2>&1";
" -o \"$$DL_FILE\" \"$$DL_URL\" > \"$$DL_PROG_BUF_FILE\"";
// Fill template
ytdl_call_video_base = Internal::StringHelpers::Replace(ytdl_call_video_base, "$$QUALITY", DownloadQualityToStringParams(entry->quality));
@ -171,6 +171,7 @@ void DownloadManager::DownloadNext()
ytdl_call_video_base = Internal::StringHelpers::Replace(ytdl_call_video_base, "$$DL_URL", entry->webpage_url);
ytdl_call_video_base = Internal::StringHelpers::Replace(ytdl_call_video_base, "$$DL_PROG_BUF_FILE", XGConfig::downloader.cachedir + "/dlprogbuf/" + entry->tubio_id + ".buf");
entry->downloaded_filename = XGConfig::downloader.cachedir + "/download/" + entry->tubio_id + ".mp4";
ss << ytdl_call_video_base;
}
@ -178,9 +179,9 @@ void DownloadManager::DownloadNext()
{
// Call template
std::string ytdl_call_audio_base =
"yt-dlp --newline --no-call-home --no-playlist --no-part --no-warnings --socket-timeout 5 --limit-rate $$DL_RATE"
"youtube-dl --newline --no-call-home --no-playlist --no-part --no-warnings --socket-timeout 5 --limit-rate $$DL_RATE"
" --no-mtime --no-cache-dir -f worstvideo+bestaudio --audio-format mp3 --audio-quality 0 --extract-audio -o \"$$DL_FILE\""
" \"$$DL_URL\" > \"$$DL_PROG_BUF_FILE\" 2>&1";
" \"$$DL_URL\" > \"$$DL_PROG_BUF_FILE\"";
// Fill template
@ -675,7 +676,7 @@ DOWNLOAD_QUALITY DownloadManager::GetDownloadQualityByName(const std::string& qu
void DownloadManager::FetchInformation(std::string url, std::string tubId)
{
std::stringstream ss;
ss << "yt-dlp --skip-download --no-warnings --no-call-home --no-playlist --socket-timeout 5 --dump-json \"" << url << "\" > \"" << XGConfig::downloader.cachedir << "/metadata/" << tubId << ".json" << "\"";
ss << "youtube-dl --skip-download --no-warnings --no-call-home --no-playlist --socket-timeout 5 --dump-json \"" << url << "\" > \"" << XGConfig::downloader.cachedir << "/metadata/" << tubId << ".json" << "\"";
system(ss.str().c_str());
return;
}
@ -707,9 +708,9 @@ std::string DownloadManager::CreateNewTubioID()
void DownloadManager::WarnIfMissingDependenciesWIN()
{
#ifdef _WIN
if (!FileSystem::Exists("yt-dlp.exe"))
if (!FileSystem::Exists("youtube-dl.exe"))
{
log->cout << log->Warn() << "Dependency yt-dlp.exe missing! Try updating it! (\"request\": \"update_dep_YtDlp\"). "
log->cout << log->Warn() << "Dependency youtube-dl.exe missing! Try updating it! (\"request\": \"update_dep_youtubedl\"). "
<< "Dependencies HAVE to lie in Tubios working directory! (where the config.json is)";
log->Flush();
}

View File

@ -135,10 +135,10 @@ namespace Downloader
static std::vector<DownloadEntry> ParseJsonArrayToEntries(const JasonPP::JsonArray& arr);
/// <summary>
/// Will return a yt-dlp quality string based on 'quality'
/// Will return a youtube-dl quality string based on 'quality'
/// </summary>
/// <param name="quality">The download quality to get the parameter from</param>
/// <returns>The yt-dlp quality parameter</returns>
/// <returns>The youtube-dl quality parameter</returns>
static std::string DownloadQualityToStringParams(DOWNLOAD_QUALITY quality);
/// <summary>

View File

@ -1,4 +1,4 @@
#include "FileSystem.h"
#include "Filesystem.h"
#ifdef _WIN
#include <Windows.h>
#endif

View File

@ -1,307 +1,264 @@
#include "HttpServer.h"
#include "external_dependencies/leonetienne/stringtools/StringTools.h"
using namespace Logging;
using namespace Rest;
using namespace JasonPP;
HttpServer::HttpServer()
{
pMgr = new mg_mgr();
pNc = nullptr;
log = new Logger("HttpServer");
return;
}
HttpServer::~HttpServer()
{
delete pMgr;
delete log;
log = nullptr;
pMgr = nullptr;
return;
}
void HttpServer::PostInit()
{
isBootedSuccessfully = InitWebServer();
return;
}
bool HttpServer::InitWebServer()
{
mg_mgr_init(pMgr, NULL);
log->cout << "Starting http-server on port " << XGConfig::httpServer.port << "...";
log->Flush();
pNc = mg_bind(pMgr, XGConfig::httpServer.port.c_str(), this->EventHandler);
if (pNc == NULL)
{
log->cout << log->Err() << "Failed to boot the http-server! - Unable to bind listener! (port: " << XGConfig::httpServer.port << ")";
log->Flush();
return false;
}
mg_set_protocol_http_websocket(pNc);
frontend_serve_opts.document_root = XGConfig::httpServer.rootdir.c_str();
frontend_serve_opts.enable_directory_listing = "no";
log->cout << "Started http-server successfully!";
log->Flush();
isBootedSuccessfully = true;
return true;
}
void HttpServer::Update()
{
mg_mgr_poll(pMgr, XGConfig::httpServer.polling_rate);
return;
}
void HttpServer::ServeStringToConnection(struct mg_connection* c, std::string str, int httpStatusCode)
{
mg_send_head(c, httpStatusCode, str.length(), "content-type: application/json\nAccess-Control-Allow-Origin: *");
mg_printf(c, "%s", str.c_str());
return;
}
void HttpServer::EventHandler(mg_connection* pNc, int ev, void* p)
{
switch (ev)
{
case MG_EV_HTTP_REQUEST:
// Reset standby timer
XGControl::last_query_time = time(0);
http_message* hpm = (http_message*)p;
std::string requestedUri = FixUnterminatedString(hpm->uri.p, hpm->uri.len);
// Get clients ip address
std::string peer_addr;
{
char buf[32];
mg_sock_addr_to_str(&pNc->sa, buf, sizeof(buf), MG_SOCK_STRINGIFY_IP);
peer_addr = buf;
}
std::string denialReason;
if (IsConnectionAllowed(peer_addr, denialReason))
{
try
{
if (requestedUri == "/api")
{
ProcessAPIRequest(pNc, ev, p, peer_addr);
}
else if (requestedUri.substr(0, 12) == "/downloadlog")
{
ServeDownloadLog(pNc, ev, p, requestedUri);
}
else if (requestedUri.substr(0, 9) == "/download")
{
ServeDownloadeableResource(pNc, ev, p, requestedUri);
}
else
{
// Just serve the files requested
mg_serve_http(pNc, (struct http_message*)p, frontend_serve_opts);
}
}
catch (std::exception& e)
{
Json j;
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR, e.what()));
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR);
}
catch (...)
{
Json j;
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR, "Das not good"));
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR);
}
break;
}
else // Client is not allowed, serve error json
{
Json j;
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::UNAUTHORIZED, denialReason));
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::UNAUTHORIZED);
}
}
return;
}
std::string HttpServer::SanitizeString(std::string in) {
in = StringTools::Replace(in, '`', "\\\\`");
in = StringTools::Replace(in, '|', "\\\\|");
in = StringTools::Replace(in, '$', "\\\\$");
in = StringTools::Replace(in, "&&", "\\\\&\\\\&");
in = StringTools::Replace(in, ";", "\\\\;");
return in;
}
void HttpServer::ProcessAPIRequest(mg_connection* pNc, int ev, void* p, std::string peerAddress)
{
// Get struct with http message informations
http_message* hpm = (http_message*)p;
// Get the transmitted message body
std::string requestBodyRaw = FixUnterminatedString(hpm->body.p, hpm->body.len);
// Sanitize it
requestBodyRaw = SanitizeString(requestBodyRaw);
// Check for the body being valid json
if (IsJsonValid(requestBodyRaw))
{
Json requestBody;
requestBody.Parse(requestBodyRaw);
JsonBlock responseBody;
HTTP_STATUS_CODE returnCode;
RestQueryHandler::ProcessQuery(peerAddress, requestBody, responseBody, returnCode);
Json response(responseBody);
ServeStringToConnection(pNc, response.Render(), (int)returnCode);
}
else // return error message for invalid json
{
Json errorJson;
errorJson.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "Received json is fucked"));
ServeStringToConnection(pNc, errorJson.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST);
}
return;
}
void HttpServer::ServeDownloadeableResource(mg_connection* pNc, int ev, void* p, std::string uri)
{
std::string fileId = uri.substr(10, uri.length() - 10);
if (Downloader::DownloadManager::DoesTubioIDExist(fileId))
{
Downloader::DownloadEntry& entry = Downloader::DownloadManager::GetDownloadEntryByTubioID(fileId);
if (entry.status == Downloader::DOWNLOAD_STATUS::FINISHED)
{
std::stringstream ss;
std::string downloadedFilename = entry.title + (entry.mode == Downloader::DOWNLOAD_MODE::AUDIO ? ".mp3" : ".mp4");
ss << "Access-Control-Allow-Origin: *\nContent-Disposition: attachment; filename=\"" << downloadedFilename << "\"\nPragma: public\nCache-Control: must-revalidate, post-check=0, pre-check=0";
mg_http_serve_file(pNc, (http_message*)p, entry.downloaded_filename.c_str(), mg_mk_str("application/octet-stream"), mg_mk_str(ss.str().c_str()));
}
else
{
Json j;
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "File download not ready!"));
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST);
}
}
else
{
Json j;
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "Invalid tubio id!"));
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST);
}
return;
}
void HttpServer::ServeDownloadLog(mg_connection* pNc, int ev, void* p, std::string uri)
{
std::string fileId = uri.substr(13, uri.length() - 13);
if (Downloader::DownloadManager::DoesTubioIDExist(fileId))
{
Downloader::DownloadEntry& entry = Downloader::DownloadManager::GetDownloadEntryByTubioID(fileId);
std::stringstream ss;
std::string logFilename = std::string("./dlcache/dlprogbuf/") + fileId + ".buf";
ss << "Access-Control-Allow-Origin: *\nContent-Type: text/plain; Cache-Control: must-revalidate, post-check=0, pre-check=0";
mg_http_serve_file(pNc, (http_message*)p, logFilename.c_str(), mg_mk_str("text/plain"), mg_mk_str(ss.str().c_str()));
}
else
{
Json j;
std::stringstream ss;
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "Invalid tubio id!"));
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST);
}
return;
}
bool HttpServer::IsConnectionAllowed(std::string peer_address, std::string& denialReason)
{
// Localhost is always allowed!
if (peer_address == "127.0.0.1") return true;
// Peer is not localhost, but only localhost is allowed!
else if (XGConfig::access.only_allow_localhost)
{
denialReason = "Only localhost allowed!";
return false;
}
// Let's check if the whitelist is active
else if (XGConfig::access.enable_whitelist)
{
// It is. Let's check if our peer is whitelisted
for (std::size_t i = 0; i < XGConfig::access.whitelist.size(); i++)
{
// Whitelist is enabled, and peer is whitelisted
if (XGConfig::access.whitelist[i] == peer_address) return true;
}
// Whitelist is enabled, but peer is NOT whitelisted
denialReason = std::string("Not whitelisted! Ask your tubio administrator to whitelist '") + peer_address + "' in order to gain access.";
return false;
}
else // Whitelist is NOT enabled and only_allow_localhost is FALSE!
{
return true;
}
// Should never come to this point
denialReason = "Access denied";
return false;
}
void HttpServer::OnExit()
{
log->cout << "Shutting down http-server...";
log->Flush();
mg_mgr_free(pMgr);
return;
}
std::string HttpServer::FixUnterminatedString(const char* cstr, const std::size_t len)
{
std::stringstream ss;
for (std::size_t i = 0; i < len; i++)
{
ss << *(cstr + i);
}
return ss.str();
}
mg_serve_http_opts HttpServer::frontend_serve_opts;
#include "HttpServer.h"
using namespace Logging;
using namespace Rest;
using namespace JasonPP;
HttpServer::HttpServer()
{
pMgr = new mg_mgr();
pNc = nullptr;
log = new Logger("HttpServer");
return;
}
HttpServer::~HttpServer()
{
delete pMgr;
delete log;
log = nullptr;
pMgr = nullptr;
return;
}
void HttpServer::PostInit()
{
isBootedSuccessfully = InitWebServer();
return;
}
bool HttpServer::InitWebServer()
{
mg_mgr_init(pMgr, NULL);
log->cout << "Starting http-server on port " << XGConfig::httpServer.port << "...";
log->Flush();
pNc = mg_bind(pMgr, XGConfig::httpServer.port.c_str(), this->EventHandler);
if (pNc == NULL)
{
log->cout << log->Err() << "Failed to boot the http-server! - Unable to bind listener! (port: " << XGConfig::httpServer.port << ")";
log->Flush();
return false;
}
mg_set_protocol_http_websocket(pNc);
frontend_serve_opts.document_root = XGConfig::httpServer.rootdir.c_str();
frontend_serve_opts.enable_directory_listing = "no";
log->cout << "Started http-server successfully!";
log->Flush();
isBootedSuccessfully = true;
return true;
}
void HttpServer::Update()
{
mg_mgr_poll(pMgr, XGConfig::httpServer.polling_rate);
return;
}
void HttpServer::ServeStringToConnection(struct mg_connection* c, std::string str, int httpStatusCode)
{
mg_send_head(c, httpStatusCode, str.length(), "content-type: application/json\nAccess-Control-Allow-Origin: *");
mg_printf(c, "%s", str.c_str());
return;
}
void HttpServer::EventHandler(mg_connection* pNc, int ev, void* p)
{
switch (ev)
{
case MG_EV_HTTP_REQUEST:
// Reset standby timer
XGControl::last_query_time = time(0);
http_message* hpm = (http_message*)p;
std::string requestedUri = FixUnterminatedString(hpm->uri.p, hpm->uri.len);
// Get clients ip address
std::string peer_addr;
{
char buf[32];
mg_sock_addr_to_str(&pNc->sa, buf, sizeof(buf), MG_SOCK_STRINGIFY_IP);
peer_addr = buf;
}
std::string denialReason;
if (IsConnectionAllowed(peer_addr, denialReason))
{
try
{
if (requestedUri == "/api")
{
ProcessAPIRequest(pNc, ev, p, peer_addr);
}
else if (requestedUri.substr(0, 9) == "/download")
{
ServeDownloadeableResource(pNc, ev, p, requestedUri);
}
else
{
// Just serve the files requested
mg_serve_http(pNc, (struct http_message*)p, frontend_serve_opts);
}
}
catch (std::exception& e)
{
Json j;
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR, e.what()));
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR);
}
catch (...)
{
Json j;
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR, "Das not good"));
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR);
}
break;
}
else // Client is not allowed, serve error json
{
Json j;
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::UNAUTHORIZED, denialReason));
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::UNAUTHORIZED);
}
}
return;
}
void HttpServer::ProcessAPIRequest(mg_connection* pNc, int ev, void* p, std::string peerAddress)
{
// Get struct with http message informations
http_message* hpm = (http_message*)p;
// Get the transmitted message body
std::string requestBodyRaw = FixUnterminatedString(hpm->body.p, hpm->body.len);
// Check for the body being valid json
if (IsJsonValid(requestBodyRaw))
{
Json requestBody;
requestBody.Parse(requestBodyRaw);
JsonBlock responseBody;
HTTP_STATUS_CODE returnCode;
RestQueryHandler::ProcessQuery(peerAddress, requestBody, responseBody, returnCode);
Json response(responseBody);
ServeStringToConnection(pNc, response.Render(), (int)returnCode);
}
else // return error message for invalid json
{
Json errorJson;
errorJson.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "Received json is fucked"));
ServeStringToConnection(pNc, errorJson.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST);
}
return;
}
void HttpServer::ServeDownloadeableResource(mg_connection* pNc, int ev, void* p, std::string uri)
{
std::string fileId = uri.substr(10, uri.length() - 10);
if (Downloader::DownloadManager::DoesTubioIDExist(fileId))
{
Downloader::DownloadEntry& entry = Downloader::DownloadManager::GetDownloadEntryByTubioID(fileId);
if (entry.status == Downloader::DOWNLOAD_STATUS::FINISHED)
{
std::stringstream ss;
std::string downloadedFilename = entry.title + (entry.mode == Downloader::DOWNLOAD_MODE::AUDIO ? ".mp3" : ".mp4");
ss << "Access-Control-Allow-Origin: *\nContent-Disposition: attachment; filename=\"" << downloadedFilename << "\"\nPragma: public\nCache-Control: must-revalidate, post-check=0, pre-check=0";
mg_http_serve_file(pNc, (http_message*)p, entry.downloaded_filename.c_str(), mg_mk_str("application/octet-stream"), mg_mk_str(ss.str().c_str()));
}
else
{
Json j;
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "File download not ready!"));
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST);
}
}
else
{
Json j;
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "Invalid tubio id!"));
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST);
}
return;
}
bool HttpServer::IsConnectionAllowed(std::string peer_address, std::string& denialReason)
{
// Localhost is always allowed!
if (peer_address == "127.0.0.1") return true;
// Peer is not localhost, but only localhost is allowed!
else if (XGConfig::access.only_allow_localhost)
{
denialReason = "Only localhost allowed!";
return false;
}
// Let's check if the whitelist is active
else if (XGConfig::access.enable_whitelist)
{
// It is. Let's check if our peer is whitelisted
for (std::size_t i = 0; i < XGConfig::access.whitelist.size(); i++)
{
// Whitelist is enabled, and peer is whitelisted
if (XGConfig::access.whitelist[i] == peer_address) return true;
}
// Whitelist is enabled, but peer is NOT whitelisted
denialReason = std::string("Not whitelisted! Ask your tubio administrator to whitelist '") + peer_address + "' in order to gain access.";
return false;
}
else // Whitelist is NOT enabled and only_allow_localhost is FALSE!
{
return true;
}
// Should never come to this point
denialReason = "Access denied";
return false;
}
void HttpServer::OnExit()
{
log->cout << "Shutting down http-server...";
log->Flush();
mg_mgr_free(pMgr);
return;
}
std::string HttpServer::FixUnterminatedString(const char* cstr, const std::size_t len)
{
std::stringstream ss;
for (std::size_t i = 0; i < len; i++)
{
ss << *(cstr + i);
}
return ss.str();
}
mg_serve_http_opts HttpServer::frontend_serve_opts;

View File

@ -24,7 +24,6 @@ namespace Rest
bool InitWebServer();
static void ProcessAPIRequest(struct mg_connection* pNc, int ev, void* p, std::string peerAddress);
static void ServeDownloadeableResource(struct mg_connection* pNc, int ev, void* p, std::string uri);
static void ServeDownloadLog(struct mg_connection* pNc, int ev, void* p, std::string uri);
static void EventHandler(struct mg_connection* pNc, int ev, void* p);
static void ServeStringToConnection(struct mg_connection* c, std::string str, int httpStatusCode = 200);
@ -32,8 +31,6 @@ namespace Rest
static bool IsConnectionAllowed(std::string peer_address, std::string& denialReason);
//! Will remove all `, | and && from a string to prevent remote code execution
static std::string SanitizeString(std::string in);
struct mg_mgr* pMgr;
struct mg_connection* pNc;

View File

@ -41,7 +41,7 @@ bool RestQueryHandler::ProcessQuery(const std::string clientAdress, const Json&
else if (requestName == "get_os_name") return GetOSName(requestBody, responseBody, responseCode);
else if (requestName == "fetch_session_logs") return FetchSessionLogs(requestBody, responseBody, responseCode);
else if (requestName == "fetch_alltime_logs") return FetchAlltimeLogs(requestBody, responseBody, responseCode);
else if (requestName == "update_dep_yt-dlp") return UpdateYtDlp(requestBody, responseBody, responseCode);
else if (requestName == "update_dep_youtubedl") return UpdateYoutubeDL(requestBody, responseBody, responseCode);
else if (requestName == "remove_download_entry") return RemoveDownloadEntry(requestBody, responseBody, responseCode);
else if (requestName == "update_config") return UpdateConfig(requestBody, responseBody, responseCode);
else if (requestName == "reset_config_to_default_values") return ResetConfigDefaults(requestBody, responseBody, responseCode);
@ -115,11 +115,11 @@ bool RestQueryHandler::QueueDownload(const JsonBlock& request, JsonBlock& respon
return false;
}
std::string tubId = DownloadManager::QueueDownload(videoUrl, mode, quality);
log->cout << "Queued video \"" << videoUrl << "\" with id \"" << tubId << "\"...";
log->cout << "Queued video \"" << videoUrl << "\"...";
log->Flush();
std::string tubId = DownloadManager::QueueDownload(videoUrl, mode, quality);
responseCode = HTTP_STATUS_CODE::OK;
responseBody.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::OK));
responseBody.Set("message") = "Download queued!";
@ -370,9 +370,9 @@ bool RestQueryHandler::GetDiskUsage(const JsonBlock& request, JsonBlock& respons
{
dependencies += FileSystem::CalculateSize("ffplay.exe");
}
if (FileSystem::Exists("yt-dlp.exe"))
if (FileSystem::Exists("youtube-dl.exe"))
{
dependencies += FileSystem::CalculateSize("yt-dlp.exe");
dependencies += FileSystem::CalculateSize("youtube-dl.exe");
}
diskUsages.Set("dependencies") = dependencies;
@ -397,12 +397,12 @@ bool RestQueryHandler::ClearLogs(const JsonBlock& request, JsonBlock& responseBo
return true;
}
bool RestQueryHandler::UpdateYtDlp(const JsonBlock& request, JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode)
bool RestQueryHandler::UpdateYoutubeDL(const JsonBlock& request, JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode)
{
log->cout << "Updating yt-dlp...";
log->cout << "Updating youtube-dl...";
log->Flush();
std::string result = Updater::UpdateYtDlp();
std::string result = Updater::UpdateYoutubeDL();
if (result == "OK")
{
log->cout << " => OK!";
@ -410,7 +410,7 @@ bool RestQueryHandler::UpdateYtDlp(const JsonBlock& request, JsonBlock& response
responseCode = HTTP_STATUS_CODE::OK;
responseBody.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::OK));
responseBody.Set("message") = "Updated yt-dlp.exe successfully!";
responseBody.Set("message") = "Updated youtube-dl.exe successfully!";
}
else if (result == "not implemented")
{
@ -420,7 +420,7 @@ bool RestQueryHandler::UpdateYtDlp(const JsonBlock& request, JsonBlock& response
log->Flush();
responseCode = HTTP_STATUS_CODE::NOT_IMPLEMENTED;
responseBody.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::NOT_IMPLEMENTED));
responseBody.Set("message") = "On linux you have to update yt-dlp yourself since it is a system-wide package handled by various package managers!";
responseBody.Set("message") = "On linux you have to update youtube-dl yourself since it is a system-wide package handled by various package managers!";
}
else // Some other error
{
@ -429,7 +429,7 @@ bool RestQueryHandler::UpdateYtDlp(const JsonBlock& request, JsonBlock& response
responseCode = HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR;
responseBody.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR));
responseBody.Set("message") = "Unable do update yt-dlp.exe! See urlmon " + result;
responseBody.Set("message") = "Unable do update youtube-dl.exe! See urlmon " + result;
}
return true;

View File

@ -32,7 +32,7 @@ namespace Rest
static bool FetchAlltimeLogs(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
static bool FetchSessionLogs(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
static bool GetDiskUsage(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
static bool UpdateYtDlp(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
static bool UpdateYoutubeDL(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
static bool RemoveDownloadEntry(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
static bool UpdateConfig(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
static bool ResetConfigDefaults(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);

195
Tubio/Tubio.vcxproj Normal file
View File

@ -0,0 +1,195 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{8ae799c5-cb17-4714-a362-d8b9817a723e}</ProjectGuid>
<RootNamespace>Tubio</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_WIN;JASONPP_RENDER_SORTED;_WIN32;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>urlmon.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_WIN;JASONPP_RENDER_SORTED;_WIN32;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>urlmon.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_WIN;JASONPP_RENDER_SORTED;_WIN64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>urlmon.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_WIN;JASONPP_RENDER_SORTED;_WIN64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>urlmon.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="ConsoleManager.cpp" />
<ClCompile Include="DownloadManager.cpp" />
<ClCompile Include="external_dependencies\casenta\mongoose\mongoose.c">
<WarningLevel Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">TurnOffAllWarnings</WarningLevel>
<WarningLevel Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">TurnOffAllWarnings</WarningLevel>
<WarningLevel Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">TurnOffAllWarnings</WarningLevel>
<WarningLevel Condition="'$(Configuration)|$(Platform)'=='Release|x64'">TurnOffAllWarnings</WarningLevel>
</ClCompile>
<ClCompile Include="external_dependencies\leonetienne\JasonPP\JasonPP.cpp" />
<ClCompile Include="FileSystem.cpp" />
<ClCompile Include="Framework.cpp" />
<ClCompile Include="Idler.cpp" />
<ClCompile Include="Logger.cpp" />
<ClCompile Include="LogHistory.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="RestQueryHandler.cpp" />
<ClCompile Include="RestResponseTemplates.cpp" />
<ClCompile Include="HttpServer.cpp" />
<ClCompile Include="Updater.cpp" />
<ClCompile Include="XGConfig.cpp" />
<ClCompile Include="XGControl.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="ConsoleManager.h" />
<ClInclude Include="DownloadManager.h" />
<ClInclude Include="external_dependencies\casenta\mongoose\mongoose.h" />
<ClInclude Include="external_dependencies\leonetienne\JasonPP\JasonPP.hpp" />
<ClInclude Include="FileSystem.h" />
<ClInclude Include="Framework.h" />
<ClInclude Include="Idler.h" />
<ClInclude Include="Logger.h" />
<ClInclude Include="LogHistory.h" />
<ClInclude Include="LogTypes.h" />
<ClInclude Include="RestQueryHandler.h" />
<ClInclude Include="RestResponseTemplates.h" />
<ClInclude Include="HttpServer.h" />
<ClInclude Include="TimeUnits.h" />
<ClInclude Include="Updater.h" />
<ClInclude Include="Version.h" />
<ClInclude Include="XGConfig.h" />
<ClInclude Include="XGControl.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

126
Tubio/Tubio.vcxproj.filters Normal file
View File

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Quelldateien">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Headerdateien">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Ressourcendateien">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="_external_dependencies">
<UniqueIdentifier>{714ac1f3-3586-412c-9b83-bf00f08d58ab}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="Logger.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="LogHistory.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="Framework.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="RestResponseTemplates.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="RestQueryHandler.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="XGControl.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="XGConfig.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="FileSystem.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="HttpServer.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="DownloadManager.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="ConsoleManager.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="external_dependencies\casenta\mongoose\mongoose.c">
<Filter>_external_dependencies</Filter>
</ClCompile>
<ClCompile Include="external_dependencies\leonetienne\JasonPP\JasonPP.cpp">
<Filter>_external_dependencies</Filter>
</ClCompile>
<ClCompile Include="Updater.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
<ClCompile Include="Idler.cpp">
<Filter>Quelldateien</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Logger.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="LogHistory.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="LogTypes.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="Framework.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="RestResponseTemplates.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="RestQueryHandler.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="XGControl.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="XGConfig.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="FileSystem.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="HttpServer.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="DownloadManager.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="ConsoleManager.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="external_dependencies\casenta\mongoose\mongoose.h">
<Filter>_external_dependencies</Filter>
</ClInclude>
<ClInclude Include="external_dependencies\leonetienne\JasonPP\JasonPP.hpp">
<Filter>_external_dependencies</Filter>
</ClInclude>
<ClInclude Include="Updater.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="Idler.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="TimeUnits.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="Version.h">
<Filter>Headerdateien</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -1,71 +1,9 @@
#include "Updater.h"
using namespace JasonPP;
std::string Updater::UpdateYtDlp()
std::string Updater::UpdateYoutubeDL()
{
#ifdef _WIN
// Fetch rest respone for latest yt-dlp release
CComPtr<IStream> dlStream;
HRESULT res = URLOpenBlockingStreamA(
nullptr,
"https://api.github.com/repos/yt-dlp/yt-dlp/releases/latest",
&dlStream,
0,
nullptr
);
if (FAILED(res))
{
return "Error fetching latest yt-dlp release identifier. Error code: 0x" + (JasonPP::Internal::Helpers::Base10_2_X(res, "0123456789abcdef"));
}
char buffer[4096];
std::stringstream restContent;
do
{
DWORD bytesRead = 0;
res = dlStream->Read(buffer, sizeof(buffer), &bytesRead);
if (bytesRead)
restContent.write(buffer, bytesRead);
} while ((SUCCEEDED(res)) && (res != S_FALSE));
// Parse response
Json json;
try
{
json.Parse(restContent.str());
}
catch (JsonException& e)
{
return "Error parsing the json githubs rest api returned, whilst trying to update yt-dlp.";
}
// Look for the asset in the release that's named "yt-dlp.exe"
std::string downloadUrlLatestExe = "";
try
{
const JsonArray assetsArr = json.AsJson["assets"].AsArray;
for (std::size_t i = 0; i < assetsArr.Size(); i++)
{
if (assetsArr[i].AsJson["name"] == "yt-dlp.exe")
downloadUrlLatestExe = assetsArr[i].AsJson["browser_download_url"];
}
}
catch (JsonException& e)
{
return "Error whilst trying to access the json key assets[n][\"name\"/\"browser_download_url\"] whilst trying to update yt-dlp.";
}
if (downloadUrlLatestExe == "")
{
return "Error: No suitable asset found in latest release. Looking for name \"yt-dlp.exe\".";
}
// Download the latest yt-dlp.exe
res = URLDownloadToFileA(NULL, downloadUrlLatestExe.c_str(), "yt-dlp.exe", 0, NULL);
HRESULT res = URLDownloadToFileA(NULL, "https://yt-dl.org/downloads/latest/youtube-dl.exe", "youtube-dl.exe", 0, NULL);
if (SUCCEEDED(res))
{

View File

@ -5,16 +5,14 @@
#ifdef _WIN
#include <urlmon.h>
#include <Windows.h>
#include <atlbase.h>
#include "external_dependencies/leonetienne/JasonPP/JasonPP.hpp"
#endif
class Updater
{
public:
/// <summary>
/// Will update yt-dlp.exe on windows only!! Returns error message. On linux, you have to update it yourself, since it is a package of its own!
/// Will update youtube-dl.exe on windows only!! Returns error message. On linux, you have to update it yourself, since it is a package of its own!
/// </summary>
/// <returns></returns>
static std::string UpdateYtDlp();
static std::string UpdateYoutubeDL();
};

View File

@ -1,2 +1,2 @@
#pragma once
#define TUBIO_SERVER_VERSION (0.66)
#define TUBIO_SERVER_VERSION (0.5396)

View File

@ -1,6 +1,6 @@
#pragma once
#include <vector>
#include "FileSystem.h"
#include "Filesystem.h"
#include "external_dependencies/leonetienne/JasonPP/JasonPP.hpp"
#include "Logger.h"

View File

@ -1,155 +0,0 @@
#include "StringTools.h"
#include <sstream>
std::string StringTools::Replace(const std::string& str, const char find, const std::string& subst) {
std::stringstream ss;
for (std::size_t i = 0; i < str.length(); i++)
{
if (str[i] != find)
ss << str[i];
else
ss << subst;
}
return ss.str();
}
std::string StringTools::Replace(const std::string& str, const std::string& find, const std::string& subst) {
if (find.length() == 0)
return str;
std::stringstream ss;
std::size_t posFound = 0;
std::size_t lastFound = 0;
while (posFound != std::string::npos)
{
lastFound = posFound;
posFound = str.find(find, posFound);
if (posFound != std::string::npos)
{
ss << str.substr(lastFound, posFound - lastFound) << subst;
posFound += find.length();
}
else
{
ss << str.substr(lastFound, (str.length()) - lastFound);
}
}
return ss.str();
}
std::string StringTools::Replace(const std::string& str, const char find, const char subst) {
std::stringstream ss;
ss << subst;
return Replace(str, find, ss.str());
}
std::string StringTools::Replace(const std::string& str, const std::string& find, const char subst) {
std::stringstream ss;
ss << subst;
return Replace(str, find, ss.str());
}
std::string StringTools::Lower(const std::string& str) {
std::stringstream ss;
for (std::size_t i = 0; i < str.size(); i++)
{
const char c = str[i];
// Quick-accept: regular letters
if ((c >= 'A') && (c <= 'Z'))
ss << (char)(c | 32);
// Else: keep the character as is
else ss << c;
}
return ss.str();
}
std::string StringTools::Upper(const std::string& str) {
std::stringstream ss;
for (std::size_t i = 0; i < str.size(); i++)
{
const char c = str[i];
// Quick-accept: regular letters
if ((c >= 'a') && (c <= 'z'))
ss << (char)(c & ~32);
// Else: keep the character as is
else ss << c;
}
return ss.str();
}
std::vector<std::string> StringTools::Split(const std::string& str, const std::string& seperator) {
std::vector<std::string> toRet;
// Quick-accept: str length is 0
if (str.length() == 0)
toRet.push_back("");
// Quick-accept: seperator length is 0
else if (seperator.length() == 0) {
for (const char c : str)
toRet.push_back(std::string(&c, (&c) + 1));
}
else {
std::size_t idx = 0;
while (idx != std::string::npos) {
std::size_t lastIdx = idx;
idx = str.find(seperator, idx);
// Grab our substring until the next finding of sep
if (idx != std::string::npos) {
toRet.push_back(str.substr(
lastIdx,
idx - lastIdx
));
idx += seperator.length();
}
// No more seperator found. Grab the rest until the end of the string
else {
toRet.push_back(str.substr(
lastIdx
));
}
}
}
return toRet;
}
std::string StringTools::PadLeft(const std::string& str, const char pad, const std::size_t len) {
std::stringstream ss;
for (std::size_t i = str.length(); i < len; i++)
ss << pad;
ss << str;
return ss.str();
}
std::string StringTools::PadRight(const std::string& str, const char pad, const std::size_t len) {
std::stringstream ss;
ss << str;
for (std::size_t i = str.length(); i < len; i++)
ss << pad;
return ss.str();
}

View File

@ -1,43 +0,0 @@
#ifndef STRINGTOOLS_STRINGTOOLS_H
#define STRINGTOOLS_STRINGTOOLS_H
#include <string>
#include <vector>
/* Handy utensils to manipulate strings */
class StringTools
{
public:
//! Will replace every occurence of `find` in `str` by `subst`.
static std::string Replace(const std::string& str, const char find, const std::string& subst);
//! Will replace every occurence of `find` in `str` by `subst`.
static std::string Replace(const std::string& str, const std::string& find, const std::string& subst);
//! Will replace every occurence of `find` in `str` by `subst`.
static std::string Replace(const std::string& str, const char find, const char subst);
//! Will replace every occurence of `find` in `str` by `subst`.
static std::string Replace(const std::string& str, const std::string& find, const char subst);
//! Will make a string all-lowercase.
static std::string Lower(const std::string& str);
//! Will make a string all-uppercase.
static std::string Upper(const std::string& str);
//! Will split a string by a string seperator
static std::vector<std::string> Split(const std::string& str, const std::string& seperator);
//! Will pad a string to the left to length l
static std::string PadLeft(const std::string& str, const char pad, const std::size_t len);
//! Will pad a string to the right to length l
static std::string PadRight(const std::string& str, const char pad, const std::size_t len);
private:
// No instanciation! >:(
StringTools();
};
#endif //STRINGTOOLS_STRINGTOOLS_H

View File

@ -1,9 +1,9 @@
<!doctype html>
<html>
<head>
<title>Tubio - Video downloader</title><meta data-n-head="1" charset="utf-8"><meta data-n-head="1" name="viewport" content="width=device-width,initial-scale=1"><meta data-n-head="1" data-hid="description" name="description" content=""><meta data-n-head="1" name="msapplication-TileColor" content="#031934"><meta data-n-head="1" name="theme-color" content="#031934"><link data-n-head="1" rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><link data-n-head="1" rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"><link data-n-head="1" rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link data-n-head="1" rel="manifest" href="/site.webmanifest"><link data-n-head="1" rel="mask-icon" href="/safari-pinned-tab.svg" color="#031934"><link rel="preload" href="/_nuxt/10b0b66.js" as="script"><link rel="preload" href="/_nuxt/8a34dcc.js" as="script"><link rel="preload" href="/_nuxt/eb2cb16.js" as="script"><link rel="preload" href="/_nuxt/4ddf16a.js" as="script">
<title>Tubio - Video downloader</title><meta data-n-head="1" charset="utf-8"><meta data-n-head="1" name="viewport" content="width=device-width,initial-scale=1"><meta data-n-head="1" data-hid="description" name="description" content=""><meta data-n-head="1" name="msapplication-TileColor" content="#031934"><meta data-n-head="1" name="theme-color" content="#031934"><link data-n-head="1" rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><link data-n-head="1" rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"><link data-n-head="1" rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link data-n-head="1" rel="manifest" href="/site.webmanifest"><link data-n-head="1" rel="mask-icon" href="/safari-pinned-tab.svg" color="#031934"><link rel="preload" href="/_nuxt/319ed71.js" as="script"><link rel="preload" href="/_nuxt/8a34dcc.js" as="script"><link rel="preload" href="/_nuxt/d640171.js" as="script"><link rel="preload" href="/_nuxt/b2e29d0.js" as="script">
</head>
<body>
<div id="__nuxt"><style>#nuxt-loading{background:#fff;visibility:hidden;opacity:0;position:absolute;left:0;right:0;top:0;bottom:0;display:flex;justify-content:center;align-items:center;flex-direction:column;animation:nuxtLoadingIn 10s ease;-webkit-animation:nuxtLoadingIn 10s ease;animation-fill-mode:forwards;overflow:hidden}@keyframes nuxtLoadingIn{0%{visibility:hidden;opacity:0}20%{visibility:visible;opacity:0}100%{visibility:visible;opacity:1}}@-webkit-keyframes nuxtLoadingIn{0%{visibility:hidden;opacity:0}20%{visibility:visible;opacity:0}100%{visibility:visible;opacity:1}}#nuxt-loading>div,#nuxt-loading>div:after{border-radius:50%;width:5rem;height:5rem}#nuxt-loading>div{font-size:10px;position:relative;text-indent:-9999em;border:.5rem solid #f5f5f5;border-left:.5rem solid #000;-webkit-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0);-webkit-animation:nuxtLoading 1.1s infinite linear;animation:nuxtLoading 1.1s infinite linear}#nuxt-loading.error>div{border-left:.5rem solid #ff4500;animation-duration:5s}@-webkit-keyframes nuxtLoading{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes nuxtLoading{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}</style><script>window.addEventListener("error",function(){var e=document.getElementById("nuxt-loading");e&&(e.className+=" error")})</script><div id="nuxt-loading" aria-live="polite" role="status"><div>Loading...</div></div></div><script>window.__NUXT__={config:{app:{basePath:"/",assetsPath:"/_nuxt/",cdnURL:null}},staticAssetsBase:"/_nuxt/static/1644155680"}</script>
<script src="/_nuxt/10b0b66.js"></script><script src="/_nuxt/8a34dcc.js"></script><script src="/_nuxt/eb2cb16.js"></script><script src="/_nuxt/4ddf16a.js"></script></body>
<div id="__nuxt"><style>#nuxt-loading{background:#fff;visibility:hidden;opacity:0;position:absolute;left:0;right:0;top:0;bottom:0;display:flex;justify-content:center;align-items:center;flex-direction:column;animation:nuxtLoadingIn 10s ease;-webkit-animation:nuxtLoadingIn 10s ease;animation-fill-mode:forwards;overflow:hidden}@keyframes nuxtLoadingIn{0%{visibility:hidden;opacity:0}20%{visibility:visible;opacity:0}100%{visibility:visible;opacity:1}}@-webkit-keyframes nuxtLoadingIn{0%{visibility:hidden;opacity:0}20%{visibility:visible;opacity:0}100%{visibility:visible;opacity:1}}#nuxt-loading>div,#nuxt-loading>div:after{border-radius:50%;width:5rem;height:5rem}#nuxt-loading>div{font-size:10px;position:relative;text-indent:-9999em;border:.5rem solid #f5f5f5;border-left:.5rem solid #000;-webkit-transform:translateZ(0);-ms-transform:translateZ(0);transform:translateZ(0);-webkit-animation:nuxtLoading 1.1s infinite linear;animation:nuxtLoading 1.1s infinite linear}#nuxt-loading.error>div{border-left:.5rem solid #ff4500;animation-duration:5s}@-webkit-keyframes nuxtLoading{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes nuxtLoading{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}</style><script>window.addEventListener("error",function(){var e=document.getElementById("nuxt-loading");e&&(e.className+=" error")})</script><div id="nuxt-loading" aria-live="polite" role="status"><div>Loading...</div></div></div><script>window.__NUXT__={config:{app:{basePath:"/",assetsPath:"/_nuxt/",cdnURL:null}},staticAssetsBase:"/_nuxt/static/1615643415"}</script>
<script src="/_nuxt/319ed71.js"></script><script src="/_nuxt/8a34dcc.js"></script><script src="/_nuxt/d640171.js"></script><script src="/_nuxt/b2e29d0.js"></script></body>
</html>

View File

@ -1 +1 @@
!function(e){function r(data){for(var r,n,c=data[0],l=data[1],d=data[2],i=0,h=[];i<c.length;i++)n=c[i],Object.prototype.hasOwnProperty.call(o,n)&&o[n]&&h.push(o[n][0]),o[n]=0;for(r in l)Object.prototype.hasOwnProperty.call(l,r)&&(e[r]=l[r]);for(v&&v(data);h.length;)h.shift()();return f.push.apply(f,d||[]),t()}function t(){for(var e,i=0;i<f.length;i++){for(var r=f[i],t=!0,n=1;n<r.length;n++){var l=r[n];0!==o[l]&&(t=!1)}t&&(f.splice(i--,1),e=c(c.s=r[0]))}return e}var n={},o={19:0},f=[];function c(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,c),t.l=!0,t.exports}c.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var f,script=document.createElement("script");script.charset="utf-8",script.timeout=120,c.nc&&script.setAttribute("nonce",c.nc),script.src=function(e){return c.p+""+{2:"c0a3d47",3:"a1bf271",4:"762bd10",5:"f319ef6",6:"07c557f",7:"3c81a41",8:"282c5a4",9:"b925fa9",10:"98ddc40",11:"959803f",12:"be47b68",13:"e42c667",14:"33f8e12",15:"1259aff",16:"b995640",17:"4abfebf",18:"91d721a"}[e]+".js"}(e);var l=new Error;f=function(r){script.onerror=script.onload=null,clearTimeout(d);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),f=r&&r.target&&r.target.src;l.message="Loading chunk "+e+" failed.\n("+n+": "+f+")",l.name="ChunkLoadError",l.type=n,l.request=f,t[1](l)}o[e]=void 0}};var d=setTimeout((function(){f({type:"timeout",target:script})}),12e4);script.onerror=script.onload=f,document.head.appendChild(script)}return Promise.all(r)},c.m=e,c.c=n,c.d=function(e,r,t){c.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},c.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},c.t=function(e,r){if(1&r&&(e=c(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(c.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)c.d(t,n,function(r){return e[r]}.bind(null,n));return t},c.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return c.d(r,"a",r),r},c.o=function(object,e){return Object.prototype.hasOwnProperty.call(object,e)},c.p="/_nuxt/",c.oe=function(e){throw console.error(e),e};var l=window.webpackJsonp=window.webpackJsonp||[],d=l.push.bind(l);l.push=r,l=l.slice();for(var i=0;i<l.length;i++)r(l[i]);var v=d;t()}([]);
!function(e){function r(data){for(var r,n,c=data[0],l=data[1],d=data[2],i=0,h=[];i<c.length;i++)n=c[i],Object.prototype.hasOwnProperty.call(o,n)&&o[n]&&h.push(o[n][0]),o[n]=0;for(r in l)Object.prototype.hasOwnProperty.call(l,r)&&(e[r]=l[r]);for(v&&v(data);h.length;)h.shift()();return f.push.apply(f,d||[]),t()}function t(){for(var e,i=0;i<f.length;i++){for(var r=f[i],t=!0,n=1;n<r.length;n++){var l=r[n];0!==o[l]&&(t=!1)}t&&(f.splice(i--,1),e=c(c.s=r[0]))}return e}var n={},o={19:0},f=[];function c(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,c),t.l=!0,t.exports}c.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var f,script=document.createElement("script");script.charset="utf-8",script.timeout=120,c.nc&&script.setAttribute("nonce",c.nc),script.src=function(e){return c.p+""+{2:"8a8f637",3:"8f953c4",4:"762bd10",5:"f319ef6",6:"99f9b46",7:"5ee7790",8:"282c5a4",9:"b925fa9",10:"98ddc40",11:"6a9af62",12:"be47b68",13:"e42c667",14:"f70744c",15:"1259aff",16:"b995640",17:"5f3baa0",18:"8debf85"}[e]+".js"}(e);var l=new Error;f=function(r){script.onerror=script.onload=null,clearTimeout(d);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),f=r&&r.target&&r.target.src;l.message="Loading chunk "+e+" failed.\n("+n+": "+f+")",l.name="ChunkLoadError",l.type=n,l.request=f,t[1](l)}o[e]=void 0}};var d=setTimeout((function(){f({type:"timeout",target:script})}),12e4);script.onerror=script.onload=f,document.head.appendChild(script)}return Promise.all(r)},c.m=e,c.c=n,c.d=function(e,r,t){c.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},c.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},c.t=function(e,r){if(1&r&&(e=c(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(c.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)c.d(t,n,function(r){return e[r]}.bind(null,n));return t},c.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return c.d(r,"a",r),r},c.o=function(object,e){return Object.prototype.hasOwnProperty.call(object,e)},c.p="/_nuxt/",c.oe=function(e){throw console.error(e),e};var l=window.webpackJsonp=window.webpackJsonp||[],d=l.push.bind(l);l.push=r,l=l.slice();for(var i=0;i<l.length;i++)r(l[i]);var v=d;t()}([]);

View File

@ -1 +1 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[7],{261:function(t,e,l){"use strict";l.r(e);var n=l(9),component=Object(n.a)({},(function(){var t=this,e=t.$createElement,l=t._self._c||e;return l("svg",{staticClass:"bi bi-music-note-beamed",attrs:{viewBox:"0 0 16 16",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg"}},[l("path",{attrs:{d:"M6 13c0 1.105-1.12 2-2.5 2S1 14.105 1 13c0-1.104 1.12-2 2.5-2s2.5.896 2.5 2zm9-2c0 1.105-1.12 2-2.5 2s-2.5-.895-2.5-2 1.12-2 2.5-2 2.5.895 2.5 2z"}}),t._v(" "),l("path",{attrs:{"fill-rule":"evenodd",d:"M14 11V2h1v9h-1zM6 3v10H5V3h1z"}}),t._v(" "),l("path",{attrs:{d:"M5 2.905a1 1 0 0 1 .9-.995l8-.8a1 1 0 0 1 1.1.995V3L5 4V2.905z"}})])}),[],!1,null,null,null);e.default=component.exports}}]);
(window.webpackJsonp=window.webpackJsonp||[]).push([[7],{260:function(t,e,l){"use strict";l.r(e);var n=l(9),component=Object(n.a)({},(function(){var t=this,e=t.$createElement,l=t._self._c||e;return l("svg",{staticClass:"bi bi-music-note-beamed",attrs:{viewBox:"0 0 16 16",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg"}},[l("path",{attrs:{d:"M6 13c0 1.105-1.12 2-2.5 2S1 14.105 1 13c0-1.104 1.12-2 2.5-2s2.5.896 2.5 2zm9-2c0 1.105-1.12 2-2.5 2s-2.5-.895-2.5-2 1.12-2 2.5-2 2.5.895 2.5 2z"}}),t._v(" "),l("path",{attrs:{"fill-rule":"evenodd",d:"M14 11V2h1v9h-1zM6 3v10H5V3h1z"}}),t._v(" "),l("path",{attrs:{d:"M5 2.905a1 1 0 0 1 .9-.995l8-.8a1 1 0 0 1 1.1.995V3L5 4V2.905z"}})])}),[],!1,null,null,null);e.default=component.exports}}]);

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[11],{259:function(t,l,e){"use strict";e.r(l);var n=e(9),component=Object(n.a)({},(function(){var t=this.$createElement,l=this._self._c||t;return l("svg",{staticClass:"bi bi-x",attrs:{viewBox:"0 0 16 16",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg"}},[l("path",{attrs:{"fill-rule":"evenodd",d:"M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"}})])}),[],!1,null,null,null);l.default=component.exports}}]);
(window.webpackJsonp=window.webpackJsonp||[]).push([[11],{261:function(t,l,e){"use strict";e.r(l);var n=e(9),component=Object(n.a)({},(function(){var t=this.$createElement,l=this._self._c||t;return l("svg",{staticClass:"bi bi-x",attrs:{viewBox:"0 0 16 16",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg"}},[l("path",{attrs:{"fill-rule":"evenodd",d:"M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"}})])}),[],!1,null,null,null);l.default=component.exports}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[6],{260:function(h,v,t){"use strict";t.r(v);var l=t(9),component=Object(l.a)({},(function(){var h=this.$createElement,v=this._self._c||h;return v("svg",{staticClass:"bi bi-film",attrs:{viewBox:"0 0 16 16",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg"}},[v("path",{attrs:{"fill-rule":"evenodd",d:"M0 1a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V1zm4 0h8v6H4V1zm8 8H4v6h8V9zM1 1h2v2H1V1zm2 3H1v2h2V4zM1 7h2v2H1V7zm2 3H1v2h2v-2zm-2 3h2v2H1v-2zM15 1h-2v2h2V1zm-2 3h2v2h-2V4zm2 3h-2v2h2V7zm-2 3h2v2h-2v-2zm2 3h-2v2h2v-2z"}})])}),[],!1,null,null,null);v.default=component.exports}}]);
(window.webpackJsonp=window.webpackJsonp||[]).push([[6],{259:function(h,v,t){"use strict";t.r(v);var l=t(9),component=Object(l.a)({},(function(){var h=this.$createElement,v=this._self._c||h;return v("svg",{staticClass:"bi bi-film",attrs:{viewBox:"0 0 16 16",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg"}},[v("path",{attrs:{"fill-rule":"evenodd",d:"M0 1a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V1zm4 0h8v6H4V1zm8 8H4v6h8V9zM1 1h2v2H1V1zm2 3H1v2h2V4zM1 7h2v2H1V7zm2 3H1v2h2v-2zm-2 3h2v2H1v-2zM15 1h-2v2h2V1zm-2 3h2v2h-2V4zm2 3h-2v2h2V7zm-2 3h2v2h-2v-2zm2 3h-2v2h2v-2z"}})])}),[],!1,null,null,null);v.default=component.exports}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
(window.webpackJsonp=window.webpackJsonp||[]).push([[14],{274:function(t,e,o){var content=o(284);content.__esModule&&(content=content.default),"string"==typeof content&&(content=[[t.i,content,""]]),content.locals&&(t.exports=content.locals);(0,o(15).default)("5d20b250",content,!0,{sourceMap:!1})},283:function(t,e,o){"use strict";o(274)},284:function(t,e,o){var n=o(14)(!1);n.push([t.i,".click-blocker[data-v-1fe60afe]{bottom:0}.box[data-v-1fe60afe],.click-blocker[data-v-1fe60afe]{position:absolute;left:0;right:0;top:0}.box[data-v-1fe60afe]{height:60%;overflow:hidden}@media(max-width:1280px){.box[data-v-1fe60afe]{position:absolute;top:100px;bottom:100px;left:100px;right:100px}}@media(max-width:768px){.box[data-v-1fe60afe]{position:absolute;top:0;bottom:0;left:0;right:0}}.box h2[data-v-1fe60afe]{text-align:center;font-size:56pt;color:#bbb}@media(max-width:768px){.box h2[data-v-1fe60afe]{margin-top:36px;max-height:unset}}@media(max-width:768px){.box .dots[data-v-1fe60afe]{margin-top:50px}}.box .dots .dot[data-v-1fe60afe]{width:50px;height:50px;background-color:#888;border-radius:50px;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}.box .dots .dot[data-v-1fe60afe]:first-child{-webkit-animation:dot-hover-data-v-1fe60afe 1.5s infinite;animation:dot-hover-data-v-1fe60afe 1.5s infinite;-webkit-animation-delay:0;animation-delay:0}.box .dots .dot[data-v-1fe60afe]:nth-child(2){-webkit-animation:dot-hover-data-v-1fe60afe 1.5s infinite;animation:dot-hover-data-v-1fe60afe 1.5s infinite;-webkit-animation-delay:.3s;animation-delay:.3s}.box .dots .dot[data-v-1fe60afe]:nth-child(3){-webkit-animation:dot-hover-data-v-1fe60afe 1.5s infinite;animation:dot-hover-data-v-1fe60afe 1.5s infinite;-webkit-animation-delay:.6s;animation-delay:.6s}@media(max-width:768px){.box .dots .dot[data-v-1fe60afe]{width:30px;height:30px}.box .dots .dot[data-v-1fe60afe]:not(:first-child){margin-left:15px}.box .dots .dot[data-v-1fe60afe]:not(:last-child){margin-right:15px}}@media(min-width:768px){.box .dots .dot[data-v-1fe60afe]{font-size:36pt}.box .dots .dot[data-v-1fe60afe]:not(:first-child){margin-left:25px}.box .dots .dot[data-v-1fe60afe]:not(:last-child){margin-right:25px}}.box .loading-text[data-v-1fe60afe]{font-size:36pt}@media(max-width:768px){.box .loading-text[data-v-1fe60afe]{font-size:24pt}}@-webkit-keyframes dot-hover-data-v-1fe60afe{0%{transform:translateY(0)}50%{transform:translateY(1em)}to{transform:translateY(0)}}@keyframes dot-hover-data-v-1fe60afe{0%{transform:translateY(0)}50%{transform:translateY(1em)}to{transform:translateY(0)}}",""]),t.exports=n},288:function(t,e,o){"use strict";o.r(e);var n={},d=(o(283),o(9)),component=Object(d.a)(n,(function(){var t=this,e=t.$createElement;t._self._c;return t._m(0)}),[function(){var t=this,e=t.$createElement,o=t._self._c||e;return o("div",{staticClass:"click-blocker flex justify-center md:items-center"},[o("div",{staticClass:"box"},[o("div",{staticClass:"w-full h-full flex flex-col justify-center items-center"},[o("h2",{staticClass:"loading-text"},[t._v("Just a second...")]),t._v(" "),o("div",{staticClass:"dots flex w-full justify-center items-center mt-24"},[o("div",{staticClass:"dot"}),t._v(" "),o("div",{staticClass:"dot"}),t._v(" "),o("div",{staticClass:"dot"})])])])])}],!1,null,"1fe60afe",null);e.default=component.exports}}]);
(window.webpackJsonp=window.webpackJsonp||[]).push([[14],{274:function(t,e,o){var content=o(284);content.__esModule&&(content=content.default),"string"==typeof content&&(content=[[t.i,content,""]]),content.locals&&(t.exports=content.locals);(0,o(15).default)("5d20b250",content,!0,{sourceMap:!1})},283:function(t,e,o){"use strict";o(274)},284:function(t,e,o){var n=o(14)(!1);n.push([t.i,".click-blocker[data-v-1fe60afe]{bottom:0}.box[data-v-1fe60afe],.click-blocker[data-v-1fe60afe]{position:absolute;left:0;right:0;top:0}.box[data-v-1fe60afe]{height:60%;overflow:hidden}@media(max-width:1280px){.box[data-v-1fe60afe]{position:absolute;top:100px;bottom:100px;left:100px;right:100px}}@media(max-width:768px){.box[data-v-1fe60afe]{position:absolute;top:0;bottom:0;left:0;right:0}}.box h2[data-v-1fe60afe]{text-align:center;font-size:56pt;color:#bbb}@media(max-width:768px){.box h2[data-v-1fe60afe]{margin-top:36px;max-height:unset}}@media(max-width:768px){.box .dots[data-v-1fe60afe]{margin-top:50px}}.box .dots .dot[data-v-1fe60afe]{width:50px;height:50px;background-color:#888;border-radius:50px;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}.box .dots .dot[data-v-1fe60afe]:first-child{-webkit-animation:dot-hover-data-v-1fe60afe 1.5s infinite;animation:dot-hover-data-v-1fe60afe 1.5s infinite;-webkit-animation-delay:0;animation-delay:0}.box .dots .dot[data-v-1fe60afe]:nth-child(2){-webkit-animation:dot-hover-data-v-1fe60afe 1.5s infinite;animation:dot-hover-data-v-1fe60afe 1.5s infinite;-webkit-animation-delay:.3s;animation-delay:.3s}.box .dots .dot[data-v-1fe60afe]:nth-child(3){-webkit-animation:dot-hover-data-v-1fe60afe 1.5s infinite;animation:dot-hover-data-v-1fe60afe 1.5s infinite;-webkit-animation-delay:.6s;animation-delay:.6s}@media(max-width:768px){.box .dots .dot[data-v-1fe60afe]{width:30px;height:30px}.box .dots .dot[data-v-1fe60afe]:not(:first-child){margin-left:15px}.box .dots .dot[data-v-1fe60afe]:not(:last-child){margin-right:15px}}@media(min-width:768px){.box .dots .dot[data-v-1fe60afe]{font-size:36pt}.box .dots .dot[data-v-1fe60afe]:not(:first-child){margin-left:25px}.box .dots .dot[data-v-1fe60afe]:not(:last-child){margin-right:25px}}.box .loading-text[data-v-1fe60afe]{font-size:36pt}@media(max-width:768px){.box .loading-text[data-v-1fe60afe]{font-size:24pt}}@-webkit-keyframes dot-hover-data-v-1fe60afe{0%{transform:translateY(0)}50%{transform:translateY(1em)}to{transform:translateY(0)}}@keyframes dot-hover-data-v-1fe60afe{0%{transform:translateY(0)}50%{transform:translateY(1em)}to{transform:translateY(0)}}",""]),t.exports=n},287:function(t,e,o){"use strict";o.r(e);var n={},d=(o(283),o(9)),component=Object(d.a)(n,(function(){var t=this,e=t.$createElement;t._self._c;return t._m(0)}),[function(){var t=this,e=t.$createElement,o=t._self._c||e;return o("div",{staticClass:"click-blocker flex justify-center md:items-center"},[o("div",{staticClass:"box"},[o("div",{staticClass:"w-full h-full flex flex-col justify-center items-center"},[o("h2",{staticClass:"loading-text"},[t._v("Just a second...")]),t._v(" "),o("div",{staticClass:"dots flex w-full justify-center items-center mt-24"},[o("div",{staticClass:"dot"}),t._v(" "),o("div",{staticClass:"dot"}),t._v(" "),o("div",{staticClass:"dot"})])])])])}],!1,null,"1fe60afe",null);e.default=component.exports}}]);

View File

@ -1 +1 @@
window.__NUXT__=(function(a){return {staticAssetsBase:"\u002F_nuxt\u002Fstatic\u002F1644155680",layout:"default",error:a,state:{diskUsage:{usage:{}},dlcache:{cache:[]},logs:{logs:[]},serverOs:{os_name:""},serverVersion:{serverVersion:-1},settings:{config:{}}},serverRendered:true,routePath:"\u002Fsettings",config:{app:{basePath:"\u002F",assetsPath:"\u002F_nuxt\u002F",cdnURL:a}}}}(null));
window.__NUXT__=(function(a){return {staticAssetsBase:"\u002F_nuxt\u002Fstatic\u002F1615643415",layout:"default",error:a,state:{diskUsage:{usage:{}},dlcache:{cache:[]},logs:{logs:[]},serverOs:{os_name:""},serverVersion:{serverVersion:-1},settings:{config:{}}},serverRendered:true,routePath:"\u002Fsettings",config:{app:{basePath:"\u002F",assetsPath:"\u002F_nuxt\u002F",cdnURL:a}}}}(null));

View File

@ -1 +1 @@
window.__NUXT__=(function(a,b){return {staticAssetsBase:"\u002F_nuxt\u002Fstatic\u002F1644155680",layout:"default",error:a,state:{diskUsage:{usage:{}},dlcache:{cache:[]},logs:{logs:[]},serverOs:{os_name:""},serverVersion:{serverVersion:-1},settings:{config:{}}},serverRendered:true,routePath:b,config:{app:{basePath:b,assetsPath:"\u002F_nuxt\u002F",cdnURL:a}}}}(null,"\u002F"));
window.__NUXT__=(function(a,b){return {staticAssetsBase:"\u002F_nuxt\u002Fstatic\u002F1615643415",layout:"default",error:a,state:{diskUsage:{usage:{}},dlcache:{cache:[]},logs:{logs:[]},serverOs:{os_name:""},serverVersion:{serverVersion:-1},settings:{config:{}}},serverRendered:true,routePath:b,config:{app:{basePath:b,assetsPath:"\u002F_nuxt\u002F",cdnURL:a}}}}(null,"\u002F"));

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,45 +1,7 @@
#include "Framework.h"
#ifndef _WIN
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
void Deamonize() {
// Fork me, kernel-san :o
pid_t fork_res = fork();
// Error handling...
if (fork_res < 0) {
std::cerr << "Aww shit! Deamonizing failed! Couldn't get forked..." << std::endl;
exit(-1);
}
// Close launcher process...
if (fork_res > 0) {
std::cout << "Successfully spawned tubio daemon... Exiting launcher gracefully..." << std::endl;
exit(0);
}
// And set the daemon process free
if (setsid() < 0) {
std::cerr << "Aww shit! Deamonizing failed! Couldn't create new session..." << std::endl;
exit(-1);
}
}
#endif
int main()
{
#ifndef _WIN
// Deamonize();
#endif
Framework framework;
framework.Run();

BIN
Tubio/youtube-dl.exe Normal file

Binary file not shown.

View File

@ -1,9 +1,5 @@
# Chrome Extension for Tubio
Authors note:
Since I am no longer using chromium, this is no longer maintained.
Adds a context-menu and a popup to download videos instantly.
It does not open Tubio for you. It just downloads the videos. But you can click a button in the extensions popup that takes you to Tubio.
For now this is not on the chrome store, but you can easily install unlisted extensions from source as shown [here](https://developer.chrome.com/docs/extensions/mv2/faq/#:~:text=Click%20the%20Chrome%20menu%20icon,a%20packaged%20extension%2C%20and%20more.).

View File

@ -1,23 +0,0 @@
FROM debian
MAINTAINER Leon Etienne
RUN apt-get update
RUN apt-get install -y \
python3 \
python3-pip
RUN pip install \
yt-dlp
RUN apt-get install -y \
ffmpeg
COPY ./entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
WORKDIR /app/
ENTRYPOINT ["/app/entrypoint.sh"]
CMD ["./Tubio"]

View File

@ -1,4 +0,0 @@
# Dockerfiles
Here are basic dockerfiles to get an image running.
They are expected (all three) to be lying within the same directory as the Tubio executable.

View File

@ -1,18 +0,0 @@
version: '3'
services:
main:
build: .
restart: always
container_name: tubio
volumes:
- .:/app
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- 80:80
environment:
- TZ=Europe/Berlin

View File

@ -1,5 +0,0 @@
#!/bin/bash
cd instance
exec $@

31
linux_build/build.sh Normal file
View File

@ -0,0 +1,31 @@
#!/bin/bash
g++ \
\
-std=c++17 \
\
-D __linux__ \
-D JASONPP_RENDER_SORTED \
\
../Tubio/main.cpp \
../Tubio/ConsoleManager.cpp \
../Tubio/DownloadManager.cpp \
../Tubio/FileSystem.cpp \
../Tubio/Framework.cpp \
../Tubio/HttpServer.cpp \
../Tubio/Logger.cpp \
../Tubio/LogHistory.cpp \
../Tubio/RestQueryHandler.cpp \
../Tubio/RestResponseTemplates.cpp \
../Tubio/XGConfig.cpp \
../Tubio/XGControl.cpp \
../Tubio/Updater.cpp \
../Tubio/Idler.cpp \
\
\
../Tubio/external_dependencies/casenta/mongoose/mongoose.c \
../Tubio/external_dependencies/leonetienne/JasonPP/JasonPP.cpp \
\
-lpthread \
\
-Wall \
-o ./tubio.out

BIN
linux_build/tubio.out Normal file

Binary file not shown.

View File

@ -165,7 +165,7 @@ export default {
data: function() {
return {
canUpdate: {type: Boolean, default: true},
version__webUI: 0.78
version__webUI: 0.762
};
},
@ -209,7 +209,7 @@ export default {
updateYtdl: function() {
const that = this;
axios.post("/api", {
request: "update_dep_yt-dlp",
request: "update_dep_youtubedl",
}).then(function(response){
if (response.data.status === "OK") {
that.$store.dispatch("logs/update", that);

View File

@ -2339,15 +2339,15 @@ browserify-zlib@^0.2.0:
pako "~1.0.5"
browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.3, browserslist@^4.6.4:
version "4.16.6"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2"
integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==
version "4.16.3"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717"
integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw==
dependencies:
caniuse-lite "^1.0.30001219"
colorette "^1.2.2"
electron-to-chromium "^1.3.723"
caniuse-lite "^1.0.30001181"
colorette "^1.2.1"
electron-to-chromium "^1.3.649"
escalade "^3.1.1"
node-releases "^1.1.71"
node-releases "^1.1.70"
buffer-equal-constant-time@1.0.1:
version "1.0.1"
@ -2580,10 +2580,10 @@ caniuse-api@^3.0.0:
lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001191, caniuse-lite@^1.0.30001219:
version "1.0.30001230"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001230.tgz#8135c57459854b2240b57a4a6786044bdc5a9f71"
integrity sha512-5yBd5nWCBS+jWKTcHOzXwo5xzcj4ePE/yjtkZyUV1BTUmrBaA9MRGC+e7mxnqXSA90CmCA8L3eKLaSUkt099IQ==
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001181, caniuse-lite@^1.0.30001191:
version "1.0.30001194"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001194.tgz#3d16ff3d734a5a7d9818402c28b1f636c5be5bed"
integrity sha512-iDUOH+oFeBYk5XawYsPtsx/8fFpndAPUQJC7gBTfxHM8xw5nOZv7ceAD4frS1MKCLUac7QL5wdAJiFQlDRjXlA==
caseless@~0.12.0:
version "0.12.0"
@ -2855,7 +2855,7 @@ color@^3.0.0, color@^3.1.2:
color-convert "^1.9.1"
color-string "^1.5.4"
colorette@^1.2.1, colorette@^1.2.2:
colorette@^1.2.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
@ -3695,10 +3695,10 @@ ee-first@1.1.1:
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
electron-to-chromium@^1.3.723:
version "1.3.739"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.739.tgz#f07756aa92cabd5a6eec6f491525a64fe62f98b9"
integrity sha512-+LPJVRsN7hGZ9EIUUiWCpO7l4E3qBYHNadazlucBfsXBbccDFNKUBAgzE68FnkWGJPwD/AfKhSzL+G+Iqb8A4A==
electron-to-chromium@^1.3.649:
version "1.3.679"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.679.tgz#69b45bbf1e0bc2320cb1f3e65b9283e86c4d5e2f"
integrity sha512-PNF7JSPAEa/aV2nQLvflRcnIMy31EOuCY87Jbdz0KsUf8O/eFNGpuwgQn2DmyJkKzfQb0zrieanRGWvf/4H+BA==
elliptic@^6.5.3:
version "6.5.4"
@ -6496,9 +6496,11 @@ node-emoji@^1.8.1:
lodash.toarray "^4.4.0"
node-fetch@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
dependencies:
whatwg-url "^5.0.0"
node-gyp@^3.8.0:
version "3.8.0"
@ -6559,10 +6561,10 @@ node-object-hash@^1.2.0:
resolved "https://registry.yarnpkg.com/node-object-hash/-/node-object-hash-1.4.2.tgz#385833d85b229902b75826224f6077be969a9e94"
integrity sha512-UdS4swXs85fCGWWf6t6DMGgpN/vnlKeSGEQ7hJcrs7PBFoxoKLmibc3QRb7fwiYsjdL7PX8iI/TMSlZ90dgHhQ==
node-releases@^1.1.71:
version "1.1.72"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe"
integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==
node-releases@^1.1.70:
version "1.1.71"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb"
integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg==
node-res@^5.0.1:
version "5.0.1"
@ -7111,9 +7113,9 @@ path-key@^3.0.0, path-key@^3.1.0:
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
path-parse@^1.0.6:
version "1.0.7"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
version "1.0.6"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
path-to-regexp@0.1.7:
version "0.1.7"
@ -9792,6 +9794,11 @@ tough-cookie@~2.5.0:
psl "^1.1.28"
punycode "^2.1.1"
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
trim-newlines@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
@ -10329,6 +10336,11 @@ watchpack@^1.7.4:
chokidar "^3.4.1"
watchpack-chokidar2 "^2.0.1"
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
webpack-bundle-analyzer@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.4.0.tgz#74013106e7e2b07cbd64f3a5ae847f7e814802c7"
@ -10422,6 +10434,14 @@ webpackbar@^4.0.0:
text-table "^0.2.0"
wrap-ansi "^6.0.0"
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0=
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
which-boxed-primitive@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"