Compare commits
79 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
7ef2432796 | ||
|
2055cf574c | ||
|
772c9d607c | ||
|
0e520fe937 | ||
|
bded0b69b2 | ||
|
a181ad89d0 | ||
|
b0f4250c60 | ||
|
a88d8b0c55 | ||
|
927f5489c6 | ||
|
e482c3706c | ||
|
b6863ac2e7 | ||
|
97289fec3f | ||
|
af1a093fe4 | ||
|
be277cd1e1 | ||
|
94cc5f40d8 | ||
|
b66026c7e0 | ||
|
e603b7319b | ||
|
cba08636c6 | ||
|
46f32badc6 | ||
|
fc0ae6fb40 | ||
|
b0d1f1f176 | ||
|
b2a673f887 | ||
|
d9d4a3e093 | ||
|
0cd35b4e9a | ||
|
572b7c41cd | ||
|
2fb67b39c6 | ||
|
4ff86707f2 | ||
|
296e911e54 | ||
|
6b9324347f | ||
|
8a449f5f14 | ||
|
bb93ac9bbb | ||
|
07c9a45395 | ||
|
cb48994d71 | ||
|
ff95fa8871 | ||
|
c75ca8ee6f | ||
|
37389ad72d | ||
|
ecf3ce99a7 | ||
|
d3aa2042ee | ||
|
7b2c8576f1 | ||
|
94215bc84d | ||
|
2f7f1d742c | ||
|
895a830122 | ||
|
e5286c0571 | ||
|
bd62d41317 | ||
|
2b5839ca92 | ||
|
0451e5c163 | ||
|
23ba5f175b | ||
|
ff0538c368 | ||
|
14795eaeaa | ||
|
c06f521476 | ||
|
fad583b2a4 | ||
|
db2714770d | ||
|
8b31627450 | ||
|
7eeac4ac91 | ||
|
e5d5a60512 | ||
|
d1578ac94b | ||
|
683ed00a53 | ||
|
5f3012948c | ||
|
c10a3f40f2 | ||
|
402ff761d4 | ||
|
43a779f6ec | ||
|
2914c3762b | ||
|
9a02b27273 | ||
|
661e260022 | ||
|
f8c585a0dd | ||
|
2515001b63 | ||
|
c66883efd6 | ||
|
818213da50 | ||
|
7dbc94cff0 | ||
|
8c2b620da5 | ||
|
7e156c2e0b | ||
|
f4a67c4671 | ||
|
82edabae45 | ||
|
3556d6eb5c | ||
|
9aed5d6d77 | ||
|
d741eed045 | ||
|
c087beb844 | ||
|
44a5893038 | ||
|
80a0f4b2c4 |
5
.gitignore
vendored
@ -11,6 +11,7 @@
|
|||||||
*.sln.docstates
|
*.sln.docstates
|
||||||
|
|
||||||
# Tubio files
|
# Tubio files
|
||||||
|
/Tubio/Tubio
|
||||||
config.json
|
config.json
|
||||||
log.txt
|
log.txt
|
||||||
log.json
|
log.json
|
||||||
@ -28,6 +29,7 @@ ffprobe.exe
|
|||||||
mono_crash.*
|
mono_crash.*
|
||||||
|
|
||||||
# Build results
|
# Build results
|
||||||
|
build/
|
||||||
[Dd]ebug/
|
[Dd]ebug/
|
||||||
[Dd]ebugPublic/
|
[Dd]ebugPublic/
|
||||||
[Rr]elease/
|
[Rr]elease/
|
||||||
@ -42,6 +44,7 @@ bld/
|
|||||||
[Oo]bj/
|
[Oo]bj/
|
||||||
[Ll]og/
|
[Ll]og/
|
||||||
[Ll]ogs/
|
[Ll]ogs/
|
||||||
|
Release.zip
|
||||||
|
|
||||||
# Visual Studio 2015/2017 cache/options directory
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
.vs/
|
.vs/
|
||||||
@ -370,4 +373,4 @@ MigrationBackup/
|
|||||||
.ionide/
|
.ionide/
|
||||||
|
|
||||||
# Fody - auto-generated XML schema
|
# Fody - auto-generated XML schema
|
||||||
FodyWeavers.xsd
|
FodyWeavers.xsd
|
||||||
|
270
README.md
@ -1,131 +1,139 @@
|
|||||||
# Tubio
|

|
||||||
The free, open source video downloader!
|
|
||||||
|
# Tubio
|
||||||
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.
|
The free, open source video downloader!
|
||||||
|
|
||||||
## But what is Tubio?
|
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.
|
||||||
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 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>
|
||||||
## But, how does it work?
|
The goal of Tubio is to make this awesome software more accessible. Not everyone knows how to use the command line!
|
||||||
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!
|
|
||||||
|
## But, how does it 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)
|
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!
|
||||||
|
|
||||||
When your download finished, just click "download" and have fun!
|
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)
|
||||||
|
|
||||||
## No, i mean on a more technical level
|
When your download finished, just click "download" and have fun!
|
||||||
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.
|
## 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 frontend is a nuxt.js web application.
|
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.
|
||||||
|
|
||||||
## But how does it look?
|
The frontend is a nuxt.js web application.
|
||||||
Have a sneak peak!
|
|
||||||
|
## But how does it look?
|
||||||

|
Have a sneak peak!
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 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.
|
## There's also a Chrome Extension
|
||||||
```json
|

|
||||||
{
|
|
||||||
"access": {
|

|
||||||
"enable_whitelist": true,
|
|
||||||
"only_allow_localhost": false,
|
|
||||||
"whitelist": [
|
## How can i configure it?
|
||||||
"127.0.0.1",
|
See this json file (config.json).
|
||||||
"192.168.1.12",
|
Most of these values are configurable in the web-interface, but not all, such as the port.
|
||||||
"192.168.1.14",
|
```json
|
||||||
"192.168.1.10"
|
{
|
||||||
]
|
"access": {
|
||||||
},
|
"enable_whitelist": true,
|
||||||
"downloader": {
|
"only_allow_localhost": false,
|
||||||
"cachedir": "dlcache",
|
"whitelist": [
|
||||||
"max_dlrate_per_thread": "100M",
|
"127.0.0.1",
|
||||||
"num_threads": 10
|
"192.168.1.12",
|
||||||
},
|
"192.168.1.14",
|
||||||
"general": {
|
"192.168.1.10"
|
||||||
"show_console": true
|
]
|
||||||
},
|
},
|
||||||
"httpServer": {
|
"downloader": {
|
||||||
"polling_rate": 100,
|
"cachedir": "dlcache",
|
||||||
"port": "80",
|
"max_dlrate_per_thread": "100M",
|
||||||
"rootdir": "./frontend"
|
"num_threads": 10
|
||||||
},
|
},
|
||||||
"logging": {
|
"general": {
|
||||||
"autosave_interval": 20,
|
"show_console": true
|
||||||
"logfile_json": "log.json",
|
},
|
||||||
"logfile_text": "log.txt"
|
"httpServer": {
|
||||||
}
|
"polling_rate": 100,
|
||||||
}
|
"port": "80",
|
||||||
```
|
"rootdir": "./frontend"
|
||||||
|
},
|
||||||
## Can i use Tubio on multiple devices?
|
"logging": {
|
||||||
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.
|
"autosave_interval": 20,
|
||||||
|
"logfile_json": "log.json",
|
||||||
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.
|
"logfile_text": "log.txt"
|
||||||
|
}
|
||||||
!!! 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.
|
## 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.
|
||||||
## Setup (Windows)
|
|
||||||
#### Install youtube-dl.exe:
|
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.
|
||||||
3) Go here: [click](http://ytdl-org.github.io/youtube-dl/download.html).
|
|
||||||
2) From there, download Microsoft Visual C++ 2010 Redistributable Package (x86) and install it.
|
!!! IMPORTANT
|
||||||
4) Go there: [click](https://www.gyan.dev/ffmpeg/builds/).
|
Tubio does NOT manage sessions or accounts! Everyone using your Tubio instance will see **all your downloads** and vica versa.
|
||||||
5) Download the ffmpeg-release-full.7z. At the time of writing this, the direct link is [this](https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-full.7z). Save that for later.
|
|
||||||
6) Download the latest Tubio build from [here](https://github.com/Leonetienne/Tubio/releases).
|
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.
|
||||||
7) Create some folder on your pc. This will be the installation folder.
|
|
||||||
8) Dump in the contents of the Tubio build you just downloaded.
|
## Setup (Linux)
|
||||||
9) Dump in the contents of `ffmpeg-<some-numbers>/bin/` of ffmpeg-release-full.7z into that same installation directory. It should be three .exe files. They HAVE to be in the same directory as `Tubio.exe`!
|
1) Clone this repository and build it with cmake
|
||||||
10) Launch Tubio.exe
|
2) Create some folder on your system. This will be the installation folder.
|
||||||
11) Navigate to `http://localhost/settings` and click "Update ytdl", as the version Tubio shipped with might be out of date.
|
3) In this folder, dump the contents of /Tubio/.
|
||||||
12) <Optional>: Set up `Tubio.exe` to launch with windows to have it\`s service always at hand.
|
4) Install python3 and then yt-dlp via `pip install yt-dlp`.
|
||||||
13) Enjoy <3
|
5) Launch the tubio executable
|
||||||
|
6) Enjoy <3
|
||||||
## Frequently Asked Questions
|
|
||||||
#### My downloads fail!
|
## Frequently Asked Questions
|
||||||
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.
|
### 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
|
||||||
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).
|
a video this may take a while, as it may be converting webm to mp4.
|
||||||
|
|
||||||
If it\`s still not working, you\`re out of luck. :(
|
### 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,
|
||||||
#### I locked myself out by enabling localhost only on another device!
|
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.
|
||||||
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.
|
|
||||||
|
If it\`s still not working, you\`re out of luck. :(
|
||||||
#### Does it work on linux?
|
You may want to check youtube-dl compatible sites.
|
||||||
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.
|
|
||||||
|
### I locked myself out by enabling localhost only on another device!
|
||||||
#### Can i use it on my phone?
|
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.
|
||||||
Sure. Read [this](/#user-content-can-i-use-tubio-on-multiple-devices?).
|
|
||||||
|
### Does it work on Windows?
|
||||||
#### Can i use this to host my own downloader website?
|
Sure it does. You just have to compile it yourself using cmake, and put yt-dlp.exe, ffmpeg.exe, ffplay.exe and ffprobe.exe
|
||||||
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!
|
in Tubios working directory (installation directory). You could then just launch it at startup.
|
||||||
|
|
||||||
#### XY is not working, you have to fix it now!
|
### Can I use it on my phone?
|
||||||
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. :)
|
You bet! That's what I'm mainly using it for :) Read [this](#user-content-can-i-use-tubio-on-multiple-devices).
|
||||||
|
|
||||||
#### XY is not polished enough!
|
### Can I use this to host my own downloader website?
|
||||||
This is an alpha-version. What did you expect? :D
|
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!
|
||||||
|
|
||||||
#### Can you please add support for website XY?
|
### XY is not working, you have to fix it now!
|
||||||
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)
|
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. :)
|
||||||
|
|
||||||
## Important notice!
|
### XY is not polished enough!
|
||||||
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!
|
This is an alpha-version. What did you expect? :D
|
||||||
|
|
||||||
## License
|
### Can you please add support for website XY?
|
||||||
Tubio is distributed under the GNU General Public License v3.0.
|
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).
|
||||||
Please read the [license file](https://github.com/Leonetienne/Tubio/blob/master/license.txt).
|
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).
|
||||||
|
|
||||||
|
31
Tubio.sln
@ -1,31 +0,0 @@
|
|||||||
|
|
||||||
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
|
|
53
Tubio/CMakeLists.txt
Executable file
@ -0,0 +1,53 @@
|
|||||||
|
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)
|
||||||
|
|
@ -17,22 +17,28 @@ void DownloadManager::PreInit()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DownloadManager::QueueDownload(std::string url, DOWNLOAD_MODE mode)
|
std::string DownloadManager::QueueDownload(std::string url, DOWNLOAD_MODE mode, DOWNLOAD_QUALITY quality)
|
||||||
{
|
{
|
||||||
|
// Create uniquie tubio id
|
||||||
std::string tubioId = CreateNewTubioID();
|
std::string tubioId = CreateNewTubioID();
|
||||||
FetchInformation(url, tubioId);
|
|
||||||
|
|
||||||
|
// Fetch metadata
|
||||||
|
FetchInformation(url, tubioId);
|
||||||
std::string jsString = FileSystem::ReadFile(XGConfig::downloader.cachedir + "/metadata/" + tubioId + ".json");
|
std::string jsString = FileSystem::ReadFile(XGConfig::downloader.cachedir + "/metadata/" + tubioId + ".json");
|
||||||
|
|
||||||
|
// Create download entry structure
|
||||||
DownloadEntry newDownload;
|
DownloadEntry newDownload;
|
||||||
newDownload.tubio_id = tubioId;
|
newDownload.tubio_id = tubioId;
|
||||||
newDownload.mode = mode;
|
newDownload.mode = mode;
|
||||||
|
newDownload.quality = quality;
|
||||||
newDownload.download_progress = 0;
|
newDownload.download_progress = 0;
|
||||||
newDownload.queued_timestamp = time(0);
|
newDownload.queued_timestamp = time(0);
|
||||||
newDownload.download_url = "/download/" + newDownload.tubio_id;
|
newDownload.download_url = "download/" + newDownload.tubio_id;
|
||||||
|
|
||||||
|
// Check for missing dependencies
|
||||||
WarnIfMissingDependenciesWIN();
|
WarnIfMissingDependenciesWIN();
|
||||||
|
|
||||||
|
// Interpret metadata
|
||||||
if (!IsJsonValid(jsString))
|
if (!IsJsonValid(jsString))
|
||||||
{
|
{
|
||||||
newDownload.status = DOWNLOAD_STATUS::FAILED;
|
newDownload.status = DOWNLOAD_STATUS::FAILED;
|
||||||
@ -92,6 +98,7 @@ std::string DownloadManager::QueueDownload(std::string url, DOWNLOAD_MODE mode)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add to list of unfinished downloads
|
||||||
unfinishedCache.push_back(newDownload);
|
unfinishedCache.push_back(newDownload);
|
||||||
|
|
||||||
return tubioId;
|
return tubioId;
|
||||||
@ -146,32 +153,37 @@ void DownloadManager::DownloadNext()
|
|||||||
|
|
||||||
std::thread* downloadThread = new std::thread([=]() {
|
std::thread* downloadThread = new std::thread([=]() {
|
||||||
DownloadEntry* entry = next;
|
DownloadEntry* entry = next;
|
||||||
|
std::string tubioId = entry->tubio_id;
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
if (entry->mode == DOWNLOAD_MODE::VIDEO)
|
if (entry->mode == DOWNLOAD_MODE::VIDEO)
|
||||||
{
|
{
|
||||||
std::string ytdl_call_video_base =
|
// Call template
|
||||||
"youtube-dl --newline --no-call-home --no-playlist --no-part --no-warnings --socket-timeout 5 --limit-rate $$DL_RATE"
|
std::string ytdl_call_video_base =
|
||||||
" --no-mtime --no-cache-dir --recode-video mp4 --prefer-ffmpeg"
|
"yt-dlp --newline --no-call-home --no-playlist --no-part --no-warnings --socket-timeout 5 --limit-rate $$DL_RATE"
|
||||||
" -o \"$$DL_FILE\" \"$$DL_URL\" > \"$$DL_PROG_BUF_FILE\"";
|
" --no-mtime --no-cache-dir -f \"$$QUALITY\" --recode-video mp4 --prefer-ffmpeg"
|
||||||
|
" -o \"$$DL_FILE\" \"$$DL_URL\" > \"$$DL_PROG_BUF_FILE\" 2>&1";
|
||||||
|
|
||||||
|
// Fill template
|
||||||
|
ytdl_call_video_base = Internal::StringHelpers::Replace(ytdl_call_video_base, "$$QUALITY", DownloadQualityToStringParams(entry->quality));
|
||||||
ytdl_call_video_base = Internal::StringHelpers::Replace(ytdl_call_video_base, "$$DL_RATE", XGConfig::downloader.max_dlrate_per_thread);
|
ytdl_call_video_base = Internal::StringHelpers::Replace(ytdl_call_video_base, "$$DL_RATE", XGConfig::downloader.max_dlrate_per_thread);
|
||||||
ytdl_call_video_base = Internal::StringHelpers::Replace(ytdl_call_video_base, "$$DL_FILE", XGConfig::downloader.cachedir + "/download/" + entry->tubio_id + ".%(ext)s");
|
ytdl_call_video_base = Internal::StringHelpers::Replace(ytdl_call_video_base, "$$DL_FILE", XGConfig::downloader.cachedir + "/download/" + entry->tubio_id + ".%(ext)s");
|
||||||
ytdl_call_video_base = Internal::StringHelpers::Replace(ytdl_call_video_base, "$$DL_URL", entry->webpage_url);
|
ytdl_call_video_base = Internal::StringHelpers::Replace(ytdl_call_video_base, "$$DL_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");
|
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";
|
entry->downloaded_filename = XGConfig::downloader.cachedir + "/download/" + entry->tubio_id + ".mp4";
|
||||||
ss << ytdl_call_video_base;
|
ss << ytdl_call_video_base;
|
||||||
}
|
}
|
||||||
else // DOWNLOAD_MODE::AUDIO
|
else // DOWNLOAD_MODE::AUDIO
|
||||||
{
|
{
|
||||||
|
// Call template
|
||||||
std::string ytdl_call_audio_base =
|
std::string ytdl_call_audio_base =
|
||||||
"youtube-dl --newline --no-call-home --no-playlist --no-part --no-warnings --socket-timeout 5 --limit-rate $$DL_RATE"
|
"yt-dlp --newline --no-call-home --no-playlist --no-part --no-warnings --socket-timeout 5 --limit-rate $$DL_RATE"
|
||||||
" --no-mtime --no-cache-dir --audio-format mp3 --audio-quality 0 --extract-audio -o \"$$DL_FILE\""
|
" --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\"";
|
" \"$$DL_URL\" > \"$$DL_PROG_BUF_FILE\" 2>&1";
|
||||||
|
|
||||||
|
|
||||||
|
// Fill template
|
||||||
ytdl_call_audio_base = Internal::StringHelpers::Replace(ytdl_call_audio_base, "$$DL_RATE", XGConfig::downloader.max_dlrate_per_thread);
|
ytdl_call_audio_base = Internal::StringHelpers::Replace(ytdl_call_audio_base, "$$DL_RATE", XGConfig::downloader.max_dlrate_per_thread);
|
||||||
ytdl_call_audio_base = Internal::StringHelpers::Replace(ytdl_call_audio_base, "$$DL_FILE", XGConfig::downloader.cachedir + "/download/" + entry->tubio_id + ".%(ext)s");
|
ytdl_call_audio_base = Internal::StringHelpers::Replace(ytdl_call_audio_base, "$$DL_FILE", XGConfig::downloader.cachedir + "/download/" + entry->tubio_id + ".%(ext)s");
|
||||||
ytdl_call_audio_base = Internal::StringHelpers::Replace(ytdl_call_audio_base, "$$DL_URL", entry->webpage_url);
|
ytdl_call_audio_base = Internal::StringHelpers::Replace(ytdl_call_audio_base, "$$DL_URL", entry->webpage_url);
|
||||||
@ -182,13 +194,21 @@ void DownloadManager::DownloadNext()
|
|||||||
ss << ytdl_call_audio_base;
|
ss << ytdl_call_audio_base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This call takes a good while and is run in a seperate thread (look a few lines above. The lambda function)
|
||||||
int returnCode = system(ss.str().c_str());
|
int returnCode = system(ss.str().c_str());
|
||||||
|
|
||||||
|
// Fetch new instance
|
||||||
|
// Don't ask me why the old one isn't valid anymore -.-
|
||||||
|
for (std::size_t i = 0; i < unfinishedCache.size(); i++)
|
||||||
|
if (unfinishedCache[i].tubio_id == tubioId)
|
||||||
|
entry = &unfinishedCache[i];
|
||||||
|
|
||||||
if (returnCode == 0)
|
if (returnCode == 0)
|
||||||
{
|
{
|
||||||
// Download succeeded
|
// Download succeeded
|
||||||
entry->status = DOWNLOAD_STATUS::FINISHED;
|
entry->status = DOWNLOAD_STATUS::FINISHED;
|
||||||
entry->download_progress = 100;
|
entry->download_progress = 100;
|
||||||
|
|
||||||
shouldSave = true;
|
shouldSave = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -400,6 +420,7 @@ bool DownloadManager::RemoveFromCacheByID(std::string id)
|
|||||||
if ((wasFinished) && (FileSystem::Exists(filePath)))
|
if ((wasFinished) && (FileSystem::Exists(filePath)))
|
||||||
{
|
{
|
||||||
FileSystem::Delete(filePath);
|
FileSystem::Delete(filePath);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only save, if we can save immediately
|
// Only save, if we can save immediately
|
||||||
@ -554,6 +575,15 @@ std::vector<DownloadEntry> DownloadManager::ParseJsonArrayToEntries(const JasonP
|
|||||||
newEntry.queued_timestamp = iter["queued_timestamp"].AsInt;
|
newEntry.queued_timestamp = iter["queued_timestamp"].AsInt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((iter.DoesExist("quality")) && (iter["quality"].GetDataType() == JDType::STRING))
|
||||||
|
{
|
||||||
|
newEntry.quality = GetDownloadQualityByName(iter["quality"].AsString);
|
||||||
|
if (newEntry.quality == DOWNLOAD_QUALITY::INVALID)
|
||||||
|
newEntry.quality = DOWNLOAD_QUALITY::_BEST;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
newEntry.quality = DOWNLOAD_QUALITY::_BEST;
|
||||||
|
|
||||||
if ((iter.DoesExist("mode")) && (iter["mode"].GetDataType() == JDType::STRING))
|
if ((iter.DoesExist("mode")) && (iter["mode"].GetDataType() == JDType::STRING))
|
||||||
{
|
{
|
||||||
std::string cachedStrMode = iter["mode"];
|
std::string cachedStrMode = iter["mode"];
|
||||||
@ -568,10 +598,84 @@ std::vector<DownloadEntry> DownloadManager::ParseJsonArrayToEntries(const JasonP
|
|||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string DownloadManager::DownloadQualityToStringParams(DOWNLOAD_QUALITY quality)
|
||||||
|
{
|
||||||
|
switch (quality)
|
||||||
|
{
|
||||||
|
case DOWNLOAD_QUALITY::_BEST:
|
||||||
|
return "best";
|
||||||
|
case DOWNLOAD_QUALITY::_1440p:
|
||||||
|
return "bestvideo[height<=1440]+bestaudio";
|
||||||
|
case DOWNLOAD_QUALITY::_1080p:
|
||||||
|
return "bestvideo[height<=1080]+bestaudio";
|
||||||
|
case DOWNLOAD_QUALITY::_720p:
|
||||||
|
return "bestvideo[height<=720]+bestaudio";
|
||||||
|
case DOWNLOAD_QUALITY::_480p:
|
||||||
|
return "bestvideo[height<=480]+bestaudio";
|
||||||
|
case DOWNLOAD_QUALITY::_360p:
|
||||||
|
return "bestvideo[height<=360]+bestaudio";
|
||||||
|
case DOWNLOAD_QUALITY::_240p:
|
||||||
|
return "bestvideo[height<=240]+bestaudio";
|
||||||
|
case DOWNLOAD_QUALITY::_144p:
|
||||||
|
return "bestvideo[height<=144]+bestaudio";
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DownloadManager::DownloadQualityToName(DOWNLOAD_QUALITY quality)
|
||||||
|
{
|
||||||
|
switch (quality)
|
||||||
|
{
|
||||||
|
case DOWNLOAD_QUALITY::_BEST:
|
||||||
|
return "best";
|
||||||
|
case DOWNLOAD_QUALITY::_1440p:
|
||||||
|
return "1440p";
|
||||||
|
case DOWNLOAD_QUALITY::_1080p:
|
||||||
|
return "1080p";
|
||||||
|
case DOWNLOAD_QUALITY::_720p:
|
||||||
|
return "720p";
|
||||||
|
case DOWNLOAD_QUALITY::_480p:
|
||||||
|
return "480p";
|
||||||
|
case DOWNLOAD_QUALITY::_360p:
|
||||||
|
return "360p";
|
||||||
|
case DOWNLOAD_QUALITY::_240p:
|
||||||
|
return "240p";
|
||||||
|
case DOWNLOAD_QUALITY::_144p:
|
||||||
|
return "144p";
|
||||||
|
case DOWNLOAD_QUALITY::INVALID:
|
||||||
|
return "INVALID";
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
DOWNLOAD_QUALITY DownloadManager::GetDownloadQualityByName(const std::string& qualityName)
|
||||||
|
{
|
||||||
|
if (qualityName == "best")
|
||||||
|
return DOWNLOAD_QUALITY::_BEST;
|
||||||
|
else if (qualityName == "1440p")
|
||||||
|
return DOWNLOAD_QUALITY::_1440p;
|
||||||
|
else if (qualityName == "1080p")
|
||||||
|
return DOWNLOAD_QUALITY::_1080p;
|
||||||
|
else if (qualityName == "720p")
|
||||||
|
return DOWNLOAD_QUALITY::_720p;
|
||||||
|
else if (qualityName == "480p")
|
||||||
|
return DOWNLOAD_QUALITY::_480p;
|
||||||
|
else if (qualityName == "360p")
|
||||||
|
return DOWNLOAD_QUALITY::_360p;
|
||||||
|
else if (qualityName == "240p")
|
||||||
|
return DOWNLOAD_QUALITY::_240p;
|
||||||
|
else if (qualityName == "144p")
|
||||||
|
return DOWNLOAD_QUALITY::_144p;
|
||||||
|
|
||||||
|
return DOWNLOAD_QUALITY::INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
void DownloadManager::FetchInformation(std::string url, std::string tubId)
|
void DownloadManager::FetchInformation(std::string url, std::string tubId)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "youtube-dl --skip-download --no-warnings --no-call-home --no-playlist --socket-timeout 5 --dump-json \"" << url << "\" > \"" << XGConfig::downloader.cachedir << "/metadata/" << tubId << ".json" << "\"";
|
ss << "yt-dlp --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());
|
system(ss.str().c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -603,9 +707,9 @@ std::string DownloadManager::CreateNewTubioID()
|
|||||||
void DownloadManager::WarnIfMissingDependenciesWIN()
|
void DownloadManager::WarnIfMissingDependenciesWIN()
|
||||||
{
|
{
|
||||||
#ifdef _WIN
|
#ifdef _WIN
|
||||||
if (!FileSystem::Exists("youtube-dl.exe"))
|
if (!FileSystem::Exists("yt-dlp.exe"))
|
||||||
{
|
{
|
||||||
log->cout << log->Warn() << "Dependency youtube-dl.exe missing! Try updating it! (\"request\": \"update_dep_youtubedl\"). "
|
log->cout << log->Warn() << "Dependency yt-dlp.exe missing! Try updating it! (\"request\": \"update_dep_YtDlp\"). "
|
||||||
<< "Dependencies HAVE to lie in Tubios working directory! (where the config.json is)";
|
<< "Dependencies HAVE to lie in Tubios working directory! (where the config.json is)";
|
||||||
log->Flush();
|
log->Flush();
|
||||||
}
|
}
|
||||||
@ -689,6 +793,7 @@ Downloader::DownloadEntry::DownloadEntry()
|
|||||||
download_url = "";
|
download_url = "";
|
||||||
status = DOWNLOAD_STATUS::QUEUED;
|
status = DOWNLOAD_STATUS::QUEUED;
|
||||||
mode = DOWNLOAD_MODE::AUDIO;
|
mode = DOWNLOAD_MODE::AUDIO;
|
||||||
|
quality = DOWNLOAD_QUALITY::INVALID;
|
||||||
download_progress = 0;
|
download_progress = 0;
|
||||||
queued_timestamp = 0;
|
queued_timestamp = 0;
|
||||||
|
|
||||||
@ -709,6 +814,7 @@ JsonBlock DownloadEntry::GetAsJson()
|
|||||||
jb.Set(Ele("downloaded_filename", downloaded_filename));
|
jb.Set(Ele("downloaded_filename", downloaded_filename));
|
||||||
jb.Set(Ele("download_url", download_url));
|
jb.Set(Ele("download_url", download_url));
|
||||||
jb.Set(Ele("queued_timestamp", (long long int)queued_timestamp));
|
jb.Set(Ele("queued_timestamp", (long long int)queued_timestamp));
|
||||||
|
jb.Set(Ele("quality", DownloadManager::DownloadQualityToName(quality)));
|
||||||
|
|
||||||
switch (mode)
|
switch (mode)
|
||||||
{
|
{
|
||||||
|
@ -23,6 +23,18 @@ namespace Downloader
|
|||||||
FINISHED,
|
FINISHED,
|
||||||
FAILED
|
FAILED
|
||||||
};
|
};
|
||||||
|
enum class DOWNLOAD_QUALITY
|
||||||
|
{
|
||||||
|
_BEST, // best quality
|
||||||
|
_1440p, // 1440p
|
||||||
|
_1080p, // 1080p
|
||||||
|
_720p, // 720p
|
||||||
|
_480p, // 480p
|
||||||
|
_360p, // 360p
|
||||||
|
_240p, // 240p
|
||||||
|
_144p, // 144p
|
||||||
|
INVALID
|
||||||
|
};
|
||||||
|
|
||||||
class DownloadEntry
|
class DownloadEntry
|
||||||
{
|
{
|
||||||
@ -41,6 +53,7 @@ namespace Downloader
|
|||||||
std::string download_url;
|
std::string download_url;
|
||||||
DOWNLOAD_STATUS status;
|
DOWNLOAD_STATUS status;
|
||||||
DOWNLOAD_MODE mode;
|
DOWNLOAD_MODE mode;
|
||||||
|
DOWNLOAD_QUALITY quality;
|
||||||
int download_progress;
|
int download_progress;
|
||||||
time_t queued_timestamp;
|
time_t queued_timestamp;
|
||||||
|
|
||||||
@ -61,7 +74,7 @@ namespace Downloader
|
|||||||
/// <param name="url"></param>
|
/// <param name="url"></param>
|
||||||
/// <param name="mode">If video or audio</param>
|
/// <param name="mode">If video or audio</param>
|
||||||
/// <returns>Tubio download id</returns>
|
/// <returns>Tubio download id</returns>
|
||||||
static std::string QueueDownload(std::string url, DOWNLOAD_MODE mode);
|
static std::string QueueDownload(std::string url, DOWNLOAD_MODE mode, DOWNLOAD_QUALITY quality = DOWNLOAD_QUALITY::_BEST);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the number of videos queued
|
/// Returns the number of videos queued
|
||||||
@ -102,12 +115,43 @@ namespace Downloader
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
static bool RemoveFromCacheByID(std::string id);
|
static bool RemoveFromCacheByID(std::string id);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Will return a name of a download quality. Like, '1080p' or 'best' for example
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="quality">The quality to get the name from</param>
|
||||||
|
/// <returns>The name of the download quality</returns>
|
||||||
|
static std::string DownloadQualityToName(DOWNLOAD_QUALITY quality);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Will return a download quality object based on a name, like '1080p' or 'best' for example
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="qualityName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
static DOWNLOAD_QUALITY GetDownloadQualityByName(const std::string& qualityName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void Save();
|
static void Save();
|
||||||
static void Load();
|
static void Load();
|
||||||
static std::vector<DownloadEntry> ParseJsonArrayToEntries(const JasonPP::JsonArray& arr);
|
static std::vector<DownloadEntry> ParseJsonArrayToEntries(const JasonPP::JsonArray& arr);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Will return a yt-dlp 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>
|
||||||
|
static std::string DownloadQualityToStringParams(DOWNLOAD_QUALITY quality);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Will fetch metadata of an url
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="url">Url to fetch from</param>
|
||||||
|
/// <param name="tubId">Tubio id to save data to</param>
|
||||||
static void FetchInformation(std::string url, std::string tubId);
|
static void FetchInformation(std::string url, std::string tubId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Will create an unique tubio id (based on time())
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Unique tubio id</returns>
|
||||||
static std::string CreateNewTubioID();
|
static std::string CreateNewTubioID();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "Filesystem.h"
|
#include "FileSystem.h"
|
||||||
#ifdef _WIN
|
#ifdef _WIN
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,264 +1,307 @@
|
|||||||
#include "HttpServer.h"
|
#include "HttpServer.h"
|
||||||
|
#include "external_dependencies/leonetienne/stringtools/StringTools.h"
|
||||||
using namespace Logging;
|
|
||||||
using namespace Rest;
|
using namespace Logging;
|
||||||
using namespace JasonPP;
|
using namespace Rest;
|
||||||
|
using namespace JasonPP;
|
||||||
HttpServer::HttpServer()
|
|
||||||
{
|
HttpServer::HttpServer()
|
||||||
pMgr = new mg_mgr();
|
{
|
||||||
pNc = nullptr;
|
pMgr = new mg_mgr();
|
||||||
log = new Logger("HttpServer");
|
pNc = nullptr;
|
||||||
|
log = new Logger("HttpServer");
|
||||||
return;
|
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
HttpServer::~HttpServer()
|
|
||||||
{
|
HttpServer::~HttpServer()
|
||||||
delete pMgr;
|
{
|
||||||
delete log;
|
delete pMgr;
|
||||||
|
delete log;
|
||||||
log = nullptr;
|
|
||||||
pMgr = nullptr;
|
log = nullptr;
|
||||||
|
pMgr = nullptr;
|
||||||
return;
|
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
void HttpServer::PostInit()
|
|
||||||
{
|
void HttpServer::PostInit()
|
||||||
isBootedSuccessfully = InitWebServer();
|
{
|
||||||
|
isBootedSuccessfully = InitWebServer();
|
||||||
return;
|
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
bool HttpServer::InitWebServer()
|
|
||||||
{
|
bool HttpServer::InitWebServer()
|
||||||
mg_mgr_init(pMgr, NULL);
|
{
|
||||||
|
mg_mgr_init(pMgr, NULL);
|
||||||
log->cout << "Starting http-server on port " << XGConfig::httpServer.port << "...";
|
|
||||||
log->Flush();
|
log->cout << "Starting http-server on port " << XGConfig::httpServer.port << "...";
|
||||||
|
log->Flush();
|
||||||
pNc = mg_bind(pMgr, XGConfig::httpServer.port.c_str(), this->EventHandler);
|
|
||||||
|
pNc = mg_bind(pMgr, XGConfig::httpServer.port.c_str(), this->EventHandler);
|
||||||
if (pNc == NULL)
|
|
||||||
{
|
if (pNc == NULL)
|
||||||
log->cout << log->Err() << "Failed to boot the http-server! - Unable to bind listener! (port: " << XGConfig::httpServer.port << ")";
|
{
|
||||||
log->Flush();
|
log->cout << log->Err() << "Failed to boot the http-server! - Unable to bind listener! (port: " << XGConfig::httpServer.port << ")";
|
||||||
return false;
|
log->Flush();
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
mg_set_protocol_http_websocket(pNc);
|
|
||||||
frontend_serve_opts.document_root = XGConfig::httpServer.rootdir.c_str();
|
mg_set_protocol_http_websocket(pNc);
|
||||||
frontend_serve_opts.enable_directory_listing = "no";
|
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();
|
log->cout << "Started http-server successfully!";
|
||||||
isBootedSuccessfully = true;
|
log->Flush();
|
||||||
|
isBootedSuccessfully = true;
|
||||||
return true;
|
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
void HttpServer::Update()
|
|
||||||
{
|
void HttpServer::Update()
|
||||||
mg_mgr_poll(pMgr, XGConfig::httpServer.polling_rate);
|
{
|
||||||
|
mg_mgr_poll(pMgr, XGConfig::httpServer.polling_rate);
|
||||||
return;
|
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
void HttpServer::ServeStringToConnection(struct mg_connection* c, std::string str, int httpStatusCode)
|
|
||||||
{
|
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());
|
mg_send_head(c, httpStatusCode, str.length(), "content-type: application/json\nAccess-Control-Allow-Origin: *");
|
||||||
|
mg_printf(c, "%s", str.c_str());
|
||||||
return;
|
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
void HttpServer::EventHandler(mg_connection* pNc, int ev, void* p)
|
|
||||||
{
|
void HttpServer::EventHandler(mg_connection* pNc, int ev, void* p)
|
||||||
switch (ev)
|
{
|
||||||
{
|
switch (ev)
|
||||||
case MG_EV_HTTP_REQUEST:
|
{
|
||||||
// Reset standby timer
|
case MG_EV_HTTP_REQUEST:
|
||||||
XGControl::last_query_time = time(0);
|
// 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);
|
http_message* hpm = (http_message*)p;
|
||||||
|
std::string requestedUri = FixUnterminatedString(hpm->uri.p, hpm->uri.len);
|
||||||
// Get clients ip address
|
|
||||||
std::string peer_addr;
|
// 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);
|
char buf[32];
|
||||||
peer_addr = buf;
|
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))
|
std::string denialReason;
|
||||||
{
|
if (IsConnectionAllowed(peer_addr, denialReason))
|
||||||
try
|
{
|
||||||
{
|
try
|
||||||
if (requestedUri == "/api")
|
{
|
||||||
{
|
if (requestedUri == "/api")
|
||||||
ProcessAPIRequest(pNc, ev, p, peer_addr);
|
{
|
||||||
}
|
ProcessAPIRequest(pNc, ev, p, peer_addr);
|
||||||
else if (requestedUri.substr(0, 9) == "/download")
|
}
|
||||||
{
|
else if (requestedUri.substr(0, 12) == "/downloadlog")
|
||||||
ServeDownloadeableResource(pNc, ev, p, requestedUri);
|
{
|
||||||
}
|
ServeDownloadLog(pNc, ev, p, requestedUri);
|
||||||
else
|
}
|
||||||
{
|
else if (requestedUri.substr(0, 9) == "/download")
|
||||||
// Just serve the files requested
|
{
|
||||||
mg_serve_http(pNc, (struct http_message*)p, frontend_serve_opts);
|
ServeDownloadeableResource(pNc, ev, p, requestedUri);
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
catch (std::exception& e)
|
{
|
||||||
{
|
// Just serve the files requested
|
||||||
Json j;
|
mg_serve_http(pNc, (struct http_message*)p, frontend_serve_opts);
|
||||||
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR, e.what()));
|
}
|
||||||
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR);
|
}
|
||||||
}
|
catch (std::exception& e)
|
||||||
catch (...)
|
{
|
||||||
{
|
Json j;
|
||||||
Json j;
|
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR, e.what()));
|
||||||
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR, "Das not good"));
|
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR);
|
||||||
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR);
|
}
|
||||||
}
|
catch (...)
|
||||||
|
{
|
||||||
break;
|
Json j;
|
||||||
}
|
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR, "Das not good"));
|
||||||
else // Client is not allowed, serve error json
|
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR);
|
||||||
{
|
}
|
||||||
Json j;
|
|
||||||
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::UNAUTHORIZED, denialReason));
|
break;
|
||||||
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::UNAUTHORIZED);
|
}
|
||||||
}
|
else // Client is not allowed, serve error json
|
||||||
}
|
{
|
||||||
|
Json j;
|
||||||
return;
|
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::UNAUTHORIZED, denialReason));
|
||||||
}
|
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::UNAUTHORIZED);
|
||||||
|
}
|
||||||
void HttpServer::ProcessAPIRequest(mg_connection* pNc, int ev, void* p, std::string peerAddress)
|
}
|
||||||
{
|
|
||||||
// Get struct with http message informations
|
return;
|
||||||
http_message* hpm = (http_message*)p;
|
}
|
||||||
|
|
||||||
// Get the transmitted message body
|
std::string HttpServer::SanitizeString(std::string in) {
|
||||||
std::string requestBodyRaw = FixUnterminatedString(hpm->body.p, hpm->body.len);
|
in = StringTools::Replace(in, '`', "\\\\`");
|
||||||
|
in = StringTools::Replace(in, '|', "\\\\|");
|
||||||
// Check for the body being valid json
|
in = StringTools::Replace(in, '$', "\\\\$");
|
||||||
if (IsJsonValid(requestBodyRaw))
|
in = StringTools::Replace(in, "&&", "\\\\&\\\\&");
|
||||||
{
|
in = StringTools::Replace(in, ";", "\\\\;");
|
||||||
Json requestBody;
|
|
||||||
requestBody.Parse(requestBodyRaw);
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpServer::ProcessAPIRequest(mg_connection* pNc, int ev, void* p, std::string peerAddress)
|
||||||
JsonBlock responseBody;
|
{
|
||||||
HTTP_STATUS_CODE returnCode;
|
// Get struct with http message informations
|
||||||
RestQueryHandler::ProcessQuery(peerAddress, requestBody, responseBody, returnCode);
|
http_message* hpm = (http_message*)p;
|
||||||
|
|
||||||
Json response(responseBody);
|
// Get the transmitted message body
|
||||||
ServeStringToConnection(pNc, response.Render(), (int)returnCode);
|
std::string requestBodyRaw = FixUnterminatedString(hpm->body.p, hpm->body.len);
|
||||||
}
|
|
||||||
else // return error message for invalid json
|
// Sanitize it
|
||||||
{
|
requestBodyRaw = SanitizeString(requestBodyRaw);
|
||||||
Json errorJson;
|
|
||||||
errorJson.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "Received json is fucked"));
|
// Check for the body being valid json
|
||||||
ServeStringToConnection(pNc, errorJson.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST);
|
if (IsJsonValid(requestBodyRaw))
|
||||||
}
|
{
|
||||||
|
Json requestBody;
|
||||||
return;
|
requestBody.Parse(requestBodyRaw);
|
||||||
}
|
|
||||||
|
|
||||||
void HttpServer::ServeDownloadeableResource(mg_connection* pNc, int ev, void* p, std::string uri)
|
|
||||||
{
|
JsonBlock responseBody;
|
||||||
std::string fileId = uri.substr(10, uri.length() - 10);
|
HTTP_STATUS_CODE returnCode;
|
||||||
|
RestQueryHandler::ProcessQuery(peerAddress, requestBody, responseBody, returnCode);
|
||||||
if (Downloader::DownloadManager::DoesTubioIDExist(fileId))
|
|
||||||
{
|
Json response(responseBody);
|
||||||
Downloader::DownloadEntry& entry = Downloader::DownloadManager::GetDownloadEntryByTubioID(fileId);
|
ServeStringToConnection(pNc, response.Render(), (int)returnCode);
|
||||||
|
}
|
||||||
if (entry.status == Downloader::DOWNLOAD_STATUS::FINISHED)
|
else // return error message for invalid json
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
Json errorJson;
|
||||||
std::string downloadedFilename = entry.title + (entry.mode == Downloader::DOWNLOAD_MODE::AUDIO ? ".mp3" : ".mp4");
|
errorJson.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "Received json is fucked"));
|
||||||
|
ServeStringToConnection(pNc, errorJson.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST);
|
||||||
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()));
|
|
||||||
}
|
return;
|
||||||
else
|
}
|
||||||
{
|
|
||||||
Json j;
|
void HttpServer::ServeDownloadeableResource(mg_connection* pNc, int ev, void* p, std::string uri)
|
||||||
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "File download not ready!"));
|
{
|
||||||
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST);
|
std::string fileId = uri.substr(10, uri.length() - 10);
|
||||||
}
|
|
||||||
}
|
if (Downloader::DownloadManager::DoesTubioIDExist(fileId))
|
||||||
else
|
{
|
||||||
{
|
Downloader::DownloadEntry& entry = Downloader::DownloadManager::GetDownloadEntryByTubioID(fileId);
|
||||||
Json j;
|
|
||||||
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "Invalid tubio id!"));
|
if (entry.status == Downloader::DOWNLOAD_STATUS::FINISHED)
|
||||||
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST);
|
{
|
||||||
}
|
std::stringstream ss;
|
||||||
|
std::string downloadedFilename = entry.title + (entry.mode == Downloader::DOWNLOAD_MODE::AUDIO ? ".mp3" : ".mp4");
|
||||||
return;
|
|
||||||
}
|
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()));
|
||||||
bool HttpServer::IsConnectionAllowed(std::string peer_address, std::string& denialReason)
|
}
|
||||||
{
|
else
|
||||||
// Localhost is always allowed!
|
{
|
||||||
if (peer_address == "127.0.0.1") return true;
|
Json j;
|
||||||
|
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "File download not ready!"));
|
||||||
// Peer is not localhost, but only localhost is allowed!
|
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST);
|
||||||
else if (XGConfig::access.only_allow_localhost)
|
}
|
||||||
{
|
}
|
||||||
denialReason = "Only localhost allowed!";
|
else
|
||||||
return false;
|
{
|
||||||
}
|
Json j;
|
||||||
|
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "Invalid tubio id!"));
|
||||||
// Let's check if the whitelist is active
|
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST);
|
||||||
else if (XGConfig::access.enable_whitelist)
|
}
|
||||||
{
|
|
||||||
// It is. Let's check if our peer is whitelisted
|
return;
|
||||||
for (std::size_t i = 0; i < XGConfig::access.whitelist.size(); i++)
|
}
|
||||||
{
|
|
||||||
// Whitelist is enabled, and peer is whitelisted
|
void HttpServer::ServeDownloadLog(mg_connection* pNc, int ev, void* p, std::string uri)
|
||||||
if (XGConfig::access.whitelist[i] == peer_address) return true;
|
{
|
||||||
}
|
std::string fileId = uri.substr(13, uri.length() - 13);
|
||||||
|
|
||||||
// Whitelist is enabled, but peer is NOT whitelisted
|
if (Downloader::DownloadManager::DoesTubioIDExist(fileId))
|
||||||
denialReason = std::string("Not whitelisted! Ask your tubio administrator to whitelist '") + peer_address + "' in order to gain access.";
|
{
|
||||||
return false;
|
Downloader::DownloadEntry& entry = Downloader::DownloadManager::GetDownloadEntryByTubioID(fileId);
|
||||||
}
|
|
||||||
else // Whitelist is NOT enabled and only_allow_localhost is FALSE!
|
std::stringstream ss;
|
||||||
{
|
std::string logFilename = std::string("./dlcache/dlprogbuf/") + fileId + ".buf";
|
||||||
return true;
|
|
||||||
}
|
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()));
|
||||||
// Should never come to this point
|
}
|
||||||
denialReason = "Access denied";
|
else
|
||||||
return false;
|
{
|
||||||
}
|
Json j;
|
||||||
|
std::stringstream ss;
|
||||||
void HttpServer::OnExit()
|
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "Invalid tubio id!"));
|
||||||
{
|
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST);
|
||||||
log->cout << "Shutting down http-server...";
|
}
|
||||||
log->Flush();
|
|
||||||
|
return;
|
||||||
mg_mgr_free(pMgr);
|
}
|
||||||
|
|
||||||
return;
|
bool HttpServer::IsConnectionAllowed(std::string peer_address, std::string& denialReason)
|
||||||
}
|
{
|
||||||
|
// Localhost is always allowed!
|
||||||
|
if (peer_address == "127.0.0.1") return true;
|
||||||
std::string HttpServer::FixUnterminatedString(const char* cstr, const std::size_t len)
|
|
||||||
{
|
// Peer is not localhost, but only localhost is allowed!
|
||||||
std::stringstream ss;
|
else if (XGConfig::access.only_allow_localhost)
|
||||||
for (std::size_t i = 0; i < len; i++)
|
{
|
||||||
{
|
denialReason = "Only localhost allowed!";
|
||||||
ss << *(cstr + i);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ss.str();
|
// Let's check if the whitelist is active
|
||||||
}
|
else if (XGConfig::access.enable_whitelist)
|
||||||
|
{
|
||||||
mg_serve_http_opts HttpServer::frontend_serve_opts;
|
// 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;
|
||||||
|
@ -24,6 +24,7 @@ namespace Rest
|
|||||||
bool InitWebServer();
|
bool InitWebServer();
|
||||||
static void ProcessAPIRequest(struct mg_connection* pNc, int ev, void* p, std::string peerAddress);
|
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 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 EventHandler(struct mg_connection* pNc, int ev, void* p);
|
||||||
static void ServeStringToConnection(struct mg_connection* c, std::string str, int httpStatusCode = 200);
|
static void ServeStringToConnection(struct mg_connection* c, std::string str, int httpStatusCode = 200);
|
||||||
@ -31,6 +32,8 @@ namespace Rest
|
|||||||
|
|
||||||
static bool IsConnectionAllowed(std::string peer_address, std::string& denialReason);
|
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_mgr* pMgr;
|
||||||
struct mg_connection* pNc;
|
struct mg_connection* pNc;
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
#include "Idler.h"
|
#include "Idler.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#define TIME_TO_FALL_ASLEEP MINUTES(5)
|
#define TIME_TO_FALL_ASLEEP MINUTES(3)
|
||||||
#define SLEEP_TIME SECONDS(3)
|
#define SLEEP_TIME SECONDS(3)
|
||||||
|
|
||||||
void Idler::Update()
|
void Idler::Update()
|
||||||
{
|
{
|
||||||
// Has no request been made within 3 minutes?
|
// Has no request been made within x minutes?
|
||||||
if (time(0) - XGControl::last_query_time > 10)
|
if (time(0) - XGControl::last_query_time > TIME_TO_FALL_ASLEEP)
|
||||||
{
|
{
|
||||||
// Let the processor chill for a second.
|
// Let the processor chill for a second.
|
||||||
// This should reduce the idling-cpu load to near 0%
|
// This should reduce the idling-cpu load to near 0%
|
||||||
|
@ -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 == "get_os_name") return GetOSName(requestBody, responseBody, responseCode);
|
||||||
else if (requestName == "fetch_session_logs") return FetchSessionLogs(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 == "fetch_alltime_logs") return FetchAlltimeLogs(requestBody, responseBody, responseCode);
|
||||||
else if (requestName == "update_dep_youtubedl") return UpdateYoutubeDL(requestBody, responseBody, responseCode);
|
else if (requestName == "update_dep_yt-dlp") return UpdateYtDlp(requestBody, responseBody, responseCode);
|
||||||
else if (requestName == "remove_download_entry") return RemoveDownloadEntry(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 == "update_config") return UpdateConfig(requestBody, responseBody, responseCode);
|
||||||
else if (requestName == "reset_config_to_default_values") return ResetConfigDefaults(requestBody, responseBody, responseCode);
|
else if (requestName == "reset_config_to_default_values") return ResetConfigDefaults(requestBody, responseBody, responseCode);
|
||||||
@ -73,6 +73,7 @@ bool RestQueryHandler::Example_Foo(const JsonBlock& request, JsonBlock& response
|
|||||||
|
|
||||||
bool RestQueryHandler::QueueDownload(const JsonBlock& request, JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode)
|
bool RestQueryHandler::QueueDownload(const JsonBlock& request, JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode)
|
||||||
{
|
{
|
||||||
|
// Fetch parameters
|
||||||
if ((!ValidateField("video_url", JDType::STRING, request, responseBody)) ||
|
if ((!ValidateField("video_url", JDType::STRING, request, responseBody)) ||
|
||||||
(!ValidateField("mode", JDType::STRING, request, responseBody)))
|
(!ValidateField("mode", JDType::STRING, request, responseBody)))
|
||||||
{
|
{
|
||||||
@ -80,8 +81,22 @@ bool RestQueryHandler::QueueDownload(const JsonBlock& request, JsonBlock& respon
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string modeParam = request.Get("mode").AsString;
|
|
||||||
std::string videoUrl = request.Get("video_url").AsString;
|
std::string videoUrl = request.Get("video_url").AsString;
|
||||||
|
std::string modeParam = request.Get("mode").AsString;
|
||||||
|
std::string qualityParam;
|
||||||
|
|
||||||
|
// 'quality' is an optional parameter. Default value is 'best'
|
||||||
|
if ((ValidateField("quality", JDType::STRING, request, responseBody)))
|
||||||
|
{
|
||||||
|
qualityParam = request.Get("quality").AsString;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qualityParam = "best";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process parameters
|
||||||
|
// Process download mode
|
||||||
DOWNLOAD_MODE mode;
|
DOWNLOAD_MODE mode;
|
||||||
if (modeParam == "video") mode = DOWNLOAD_MODE::VIDEO;
|
if (modeParam == "video") mode = DOWNLOAD_MODE::VIDEO;
|
||||||
else if (modeParam == "audio") mode = DOWNLOAD_MODE::AUDIO;
|
else if (modeParam == "audio") mode = DOWNLOAD_MODE::AUDIO;
|
||||||
@ -92,10 +107,18 @@ bool RestQueryHandler::QueueDownload(const JsonBlock& request, JsonBlock& respon
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
log->cout << "Queued video \"" << videoUrl << "\"...";
|
// Process download quality
|
||||||
log->Flush();
|
DOWNLOAD_QUALITY quality = DownloadManager::GetDownloadQualityByName(qualityParam);
|
||||||
|
if (quality == DOWNLOAD_QUALITY::INVALID) {
|
||||||
|
responseCode = HTTP_STATUS_CODE::BAD_REQUEST;
|
||||||
|
responseBody.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "Parameter 'quality' is of wrong value. Choose either 'best', '1440p', '1080p', '720p', '480p', '360p', '240p', or '144p'."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::string tubId = DownloadManager::QueueDownload(videoUrl, mode);
|
std::string tubId = DownloadManager::QueueDownload(videoUrl, mode, quality);
|
||||||
|
|
||||||
|
log->cout << "Queued video \"" << videoUrl << "\" with id \"" << tubId << "\"...";
|
||||||
|
log->Flush();
|
||||||
|
|
||||||
responseCode = HTTP_STATUS_CODE::OK;
|
responseCode = HTTP_STATUS_CODE::OK;
|
||||||
responseBody.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::OK));
|
responseBody.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::OK));
|
||||||
@ -347,9 +370,9 @@ bool RestQueryHandler::GetDiskUsage(const JsonBlock& request, JsonBlock& respons
|
|||||||
{
|
{
|
||||||
dependencies += FileSystem::CalculateSize("ffplay.exe");
|
dependencies += FileSystem::CalculateSize("ffplay.exe");
|
||||||
}
|
}
|
||||||
if (FileSystem::Exists("youtube-dl.exe"))
|
if (FileSystem::Exists("yt-dlp.exe"))
|
||||||
{
|
{
|
||||||
dependencies += FileSystem::CalculateSize("youtube-dl.exe");
|
dependencies += FileSystem::CalculateSize("yt-dlp.exe");
|
||||||
}
|
}
|
||||||
diskUsages.Set("dependencies") = dependencies;
|
diskUsages.Set("dependencies") = dependencies;
|
||||||
|
|
||||||
@ -374,12 +397,12 @@ bool RestQueryHandler::ClearLogs(const JsonBlock& request, JsonBlock& responseBo
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RestQueryHandler::UpdateYoutubeDL(const JsonBlock& request, JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode)
|
bool RestQueryHandler::UpdateYtDlp(const JsonBlock& request, JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode)
|
||||||
{
|
{
|
||||||
log->cout << "Updating youtube-dl...";
|
log->cout << "Updating yt-dlp...";
|
||||||
log->Flush();
|
log->Flush();
|
||||||
|
|
||||||
std::string result = Updater::UpdateYoutubeDL();
|
std::string result = Updater::UpdateYtDlp();
|
||||||
if (result == "OK")
|
if (result == "OK")
|
||||||
{
|
{
|
||||||
log->cout << " => OK!";
|
log->cout << " => OK!";
|
||||||
@ -387,7 +410,7 @@ bool RestQueryHandler::UpdateYoutubeDL(const JsonBlock& request, JsonBlock& resp
|
|||||||
|
|
||||||
responseCode = HTTP_STATUS_CODE::OK;
|
responseCode = HTTP_STATUS_CODE::OK;
|
||||||
responseBody.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::OK));
|
responseBody.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::OK));
|
||||||
responseBody.Set("message") = "Updated youtube-dl.exe successfully!";
|
responseBody.Set("message") = "Updated yt-dlp.exe successfully!";
|
||||||
}
|
}
|
||||||
else if (result == "not implemented")
|
else if (result == "not implemented")
|
||||||
{
|
{
|
||||||
@ -397,7 +420,7 @@ bool RestQueryHandler::UpdateYoutubeDL(const JsonBlock& request, JsonBlock& resp
|
|||||||
log->Flush();
|
log->Flush();
|
||||||
responseCode = HTTP_STATUS_CODE::NOT_IMPLEMENTED;
|
responseCode = HTTP_STATUS_CODE::NOT_IMPLEMENTED;
|
||||||
responseBody.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::NOT_IMPLEMENTED));
|
responseBody.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::NOT_IMPLEMENTED));
|
||||||
responseBody.Set("message") = "On linux you have to update youtube-dl yourself since it is a system-wide package handled by various package managers!";
|
responseBody.Set("message") = "On linux you have to update yt-dlp yourself since it is a system-wide package handled by various package managers!";
|
||||||
}
|
}
|
||||||
else // Some other error
|
else // Some other error
|
||||||
{
|
{
|
||||||
@ -406,7 +429,7 @@ bool RestQueryHandler::UpdateYoutubeDL(const JsonBlock& request, JsonBlock& resp
|
|||||||
|
|
||||||
responseCode = HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR;
|
responseCode = HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR;
|
||||||
responseBody.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR));
|
responseBody.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR));
|
||||||
responseBody.Set("message") = "Unable do update youtube-dl.exe! See urlmon " + result;
|
responseBody.Set("message") = "Unable do update yt-dlp.exe! See urlmon " + result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -32,7 +32,7 @@ namespace Rest
|
|||||||
static bool FetchAlltimeLogs(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
|
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 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 GetDiskUsage(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 UpdateYtDlp(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 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 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);
|
static bool ResetConfigDefaults(const JasonPP::JsonBlock& request, JasonPP::JsonBlock& responseBody, HTTP_STATUS_CODE& responseCode);
|
||||||
|
@ -1,195 +0,0 @@
|
|||||||
<?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>
|
|
@ -1,126 +0,0 @@
|
|||||||
<?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>
|
|
@ -1,9 +1,71 @@
|
|||||||
#include "Updater.h"
|
#include "Updater.h"
|
||||||
|
|
||||||
std::string Updater::UpdateYoutubeDL()
|
using namespace JasonPP;
|
||||||
|
|
||||||
|
std::string Updater::UpdateYtDlp()
|
||||||
{
|
{
|
||||||
#ifdef _WIN
|
#ifdef _WIN
|
||||||
HRESULT res = URLDownloadToFileA(NULL, "https://yt-dl.org/downloads/latest/youtube-dl.exe", "youtube-dl.exe", 0, NULL);
|
// 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);
|
||||||
|
|
||||||
if (SUCCEEDED(res))
|
if (SUCCEEDED(res))
|
||||||
{
|
{
|
||||||
|
@ -5,14 +5,16 @@
|
|||||||
#ifdef _WIN
|
#ifdef _WIN
|
||||||
#include <urlmon.h>
|
#include <urlmon.h>
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
#include <atlbase.h>
|
||||||
|
#include "external_dependencies/leonetienne/JasonPP/JasonPP.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class Updater
|
class Updater
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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!
|
/// 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!
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
static std::string UpdateYoutubeDL();
|
static std::string UpdateYtDlp();
|
||||||
};
|
};
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#define TUBIO_SERVER_VERSION (0.535)
|
#define TUBIO_SERVER_VERSION (0.66)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "Filesystem.h"
|
#include "FileSystem.h"
|
||||||
#include "external_dependencies/leonetienne/JasonPP/JasonPP.hpp"
|
#include "external_dependencies/leonetienne/JasonPP/JasonPP.hpp"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
|
|
||||||
|
@ -0,0 +1,155 @@
|
|||||||
|
#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();
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
#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
|
@ -1,9 +1,9 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<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/f55729f.js" as="script"><link rel="preload" href="/_nuxt/bc6e142.js" as="script"><link rel="preload" href="/_nuxt/eab22ae.js" as="script"><link rel="preload" href="/_nuxt/0fa9552.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/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">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<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:{},staticAssetsBase:"/_nuxt/static/1611507770"}</script>
|
<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/f55729f.js"></script><script src="/_nuxt/bc6e142.js"></script><script src="/_nuxt/eab22ae.js"></script><script src="/_nuxt/0fa9552.js"></script></body>
|
<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>
|
||||||
</html>
|
</html>
|
||||||
|
1
Tubio/frontend/_nuxt/07c557f.js
Normal file
@ -0,0 +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}}]);
|
1
Tubio/frontend/_nuxt/10b0b66.js
Normal file
@ -0,0 +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()}([]);
|
1
Tubio/frontend/_nuxt/1259aff.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
(window.webpackJsonp=window.webpackJsonp||[]).push([[15],{253:function(t,e,h){var content=h(267);content.__esModule&&(content=content.default),"string"==typeof content&&(content=[[t.i,content,""]]),content.locals&&(t.exports=content.locals);(0,h(15).default)("ac8f1938",content,!0,{sourceMap:!1})},256:function(t,e,h){"use strict";h.r(e);var r={props:{height:{type:String,default:"0"},m_height:{type:String,default:"0"}},computed:{mobile_height:function(){return"0"===this.m_height?this.height:this.m_height}}},n=(h(266),h(9)),component=Object(n.a)(r,(function(){var t=this,e=t.$createElement;return(t._self._c||e)("div",{staticClass:"spacer",style:"--height: "+t.height+"; --m_height: "+t.mobile_height+";"})}),[],!1,null,"70a5daf0",null);e.default=component.exports},266:function(t,e,h){"use strict";h(253)},267:function(t,e,h){var r=h(14)(!1);r.push([t.i,".spacer[data-v-70a5daf0]{width:1px;height:var(--height)}@media(max-width:660px){.spacer[data-v-70a5daf0]{height:var(--m_height)}}",""]),t.exports=r}}]);
|
1
Tubio/frontend/_nuxt/282c5a4.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
(window.webpackJsonp=window.webpackJsonp||[]).push([[8],{298:function(t,l,e){"use strict";e.r(l);var n=e(9),component=Object(n.a)({},(function(){var t=this,l=t.$createElement,e=t._self._c||l;return e("svg",{staticClass:"bi bi-terminal",attrs:{viewBox:"0 0 16 16",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg"}},[e("path",{attrs:{"fill-rule":"evenodd",d:"M14 2H2a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1zM2 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H2z"}}),t._v(" "),e("path",{attrs:{"fill-rule":"evenodd",d:"M6 9a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3A.5.5 0 0 1 6 9zM3.146 4.146a.5.5 0 0 1 .708 0l2 2a.5.5 0 0 1 0 .708l-2 2a.5.5 0 1 1-.708-.708L4.793 6.5 3.146 4.854a.5.5 0 0 1 0-.708z"}})])}),[],!1,null,null,null);l.default=component.exports}}]);
|
1
Tubio/frontend/_nuxt/33f8e12.js
Normal file
@ -0,0 +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}}]);
|
1
Tubio/frontend/_nuxt/3c81a41.js
Normal file
@ -0,0 +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}}]);
|
1
Tubio/frontend/_nuxt/4abfebf.js
Normal file
1
Tubio/frontend/_nuxt/4ddf16a.js
Normal file
1
Tubio/frontend/_nuxt/762bd10.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
(window.webpackJsonp=window.webpackJsonp||[]).push([[4],{291:function(t,l,e){"use strict";e.r(l);var r=e(9),component=Object(r.a)({},(function(){var t=this,l=t.$createElement,e=t._self._c||l;return e("svg",{staticClass:"bi bi-arrow-right-square",attrs:{viewBox:"0 0 16 16",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg"}},[e("path",{attrs:{"fill-rule":"evenodd",d:"M14 1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"}}),t._v(" "),e("path",{attrs:{"fill-rule":"evenodd",d:"M4 8a.5.5 0 0 0 .5.5h5.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3a.5.5 0 0 0 0-.708l-3-3a.5.5 0 1 0-.708.708L10.293 7.5H4.5A.5.5 0 0 0 4 8z"}})])}),[],!1,null,null,null);l.default=component.exports}}]);
|
2
Tubio/frontend/_nuxt/8a34dcc.js
Normal file
1
Tubio/frontend/_nuxt/91d721a.js
Normal file
1
Tubio/frontend/_nuxt/959803f.js
Normal file
@ -0,0 +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}}]);
|
1
Tubio/frontend/_nuxt/98ddc40.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
(window.webpackJsonp=window.webpackJsonp||[]).push([[10],{275:function(t,e,l){"use strict";l.r(e);var n=l(9),component=Object(n.a)({},(function(){var t=this.$createElement,e=this._self._c||t;return e("svg",{staticClass:"bi bi-toggle-on",attrs:{viewBox:"-4 -4 24 24",fill:"#8a54a2",xmlns:"http://www.w3.org/2000/svg"}},[e("path",{attrs:{"fill-rule":"evenodd",d:"M5 3a5 5 0 0 0 0 10h6a5 5 0 0 0 0-10H5zm6 9a4 4 0 1 0 0-8 4 4 0 0 0 0 8z"}})])}),[],!1,null,null,null);e.default=component.exports}}]);
|
@ -1,6 +1,6 @@
|
|||||||
/*!
|
/*!
|
||||||
* vue-router v3.4.5
|
* vue-router v3.5.1
|
||||||
* (c) 2020 Evan You
|
* (c) 2021 Evan You
|
||||||
* @license MIT
|
* @license MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -10,6 +10,13 @@
|
|||||||
* Released under the MIT License.
|
* Released under the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* vuex v3.6.2
|
||||||
|
* (c) 2021 Evan You
|
||||||
|
* @license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* vue-client-only v2.0.0
|
* vue-client-only v2.0.0
|
||||||
* (c) 2019-present egoist <0x142857@gmail.com>
|
* (c) 2019-present egoist <0x142857@gmail.com>
|
||||||
@ -21,9 +28,3 @@
|
|||||||
* (c) 2018-present egoist <0x142857@gmail.com>
|
* (c) 2018-present egoist <0x142857@gmail.com>
|
||||||
* Released under the MIT License.
|
* Released under the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*!
|
|
||||||
* vuex v3.5.1
|
|
||||||
* (c) 2020 Evan You
|
|
||||||
* @license MIT
|
|
||||||
*/
|
|
||||||
|
1
Tubio/frontend/_nuxt/a1bf271.js
Normal file
1
Tubio/frontend/_nuxt/b925fa9.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
(window.webpackJsonp=window.webpackJsonp||[]).push([[9],{276:function(t,e,l){"use strict";l.r(e);var n=l(9),component=Object(n.a)({},(function(){var t=this.$createElement,e=this._self._c||t;return e("svg",{staticClass:"bi bi-toggle-off",attrs:{viewBox:"-4 -4 24 24",xmlns:"http://www.w3.org/2000/svg"}},[e("path",{attrs:{"fill-rule":"evenodd",d:"M11 4a4 4 0 0 1 0 8H8a4.992 4.992 0 0 0 2-4 4.992 4.992 0 0 0-2-4h3zm-6 8a4 4 0 1 1 0-8 4 4 0 0 1 0 8zM0 8a5 5 0 0 0 5 5h6a5 5 0 0 0 0-10H5a5 5 0 0 0-5 5z"}})])}),[],!1,null,null,null);e.default=component.exports}}]);
|
1
Tubio/frontend/_nuxt/b995640.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
(window.webpackJsonp=window.webpackJsonp||[]).push([[16,9,10],{262:function(t,e,l){var content=l(278);content.__esModule&&(content=content.default),"string"==typeof content&&(content=[[t.i,content,""]]),content.locals&&(t.exports=content.locals);(0,l(15).default)("1d10f8d7",content,!0,{sourceMap:!1})},275:function(t,e,l){"use strict";l.r(e);var o=l(9),component=Object(o.a)({},(function(){var t=this.$createElement,e=this._self._c||t;return e("svg",{staticClass:"bi bi-toggle-on",attrs:{viewBox:"-4 -4 24 24",fill:"#8a54a2",xmlns:"http://www.w3.org/2000/svg"}},[e("path",{attrs:{"fill-rule":"evenodd",d:"M5 3a5 5 0 0 0 0 10h6a5 5 0 0 0 0-10H5zm6 9a4 4 0 1 0 0-8 4 4 0 0 0 0 8z"}})])}),[],!1,null,null,null);e.default=component.exports},276:function(t,e,l){"use strict";l.r(e);var o=l(9),component=Object(o.a)({},(function(){var t=this.$createElement,e=this._self._c||t;return e("svg",{staticClass:"bi bi-toggle-off",attrs:{viewBox:"-4 -4 24 24",xmlns:"http://www.w3.org/2000/svg"}},[e("path",{attrs:{"fill-rule":"evenodd",d:"M11 4a4 4 0 0 1 0 8H8a4.992 4.992 0 0 0 2-4 4.992 4.992 0 0 0-2-4h3zm-6 8a4 4 0 1 1 0-8 4 4 0 0 1 0 8zM0 8a5 5 0 0 0 5 5h6a5 5 0 0 0 0-10H5a5 5 0 0 0-5 5z"}})])}),[],!1,null,null,null);e.default=component.exports},277:function(t,e,l){"use strict";l(262)},278:function(t,e,l){var o=l(14)(!1);o.push([t.i,".toggle-button[data-v-7e3e3a36]{width:50px;overflow:visible;stroke-width:1px;fill:#bbb;stroke:#bbb;cursor:pointer;transition:fill .1s,stroke .1s}.toggle-button[data-v-7e3e3a36]:hover{fill:#fff;stroke:#fff}",""]),t.exports=o},285:function(t,e,l){"use strict";l.r(e);var o=l(275),n=l(276),r={props:{isOn:{type:Boolean,default:!1}},components:{IconToggleOn:o.default,IconToggleOff:n.default}},f=(l(277),l(9)),component=Object(f.a)(r,(function(){var t=this,e=t.$createElement,l=t._self._c||e;return l("div",[l("div",{staticClass:"toggle-button"},[t.isOn?l("IconToggleOn"):l("IconToggleOff")],1)])}),[],!1,null,"7e3e3a36",null);e.default=component.exports}}]);
|
1
Tubio/frontend/_nuxt/be47b68.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
(window.webpackJsonp=window.webpackJsonp||[]).push([[12,13],{252:function(t,o,e){var content=e(264);content.__esModule&&(content=content.default),"string"==typeof content&&(content=[[t.i,content,""]]),content.locals&&(t.exports=content.locals);(0,e(15).default)("3edd6b06",content,!0,{sourceMap:!1})},255:function(t,o,e){"use strict";e.r(o);var r={props:{mode:{type:String,default:"normal"},message:{type:String}}},n=(e(263),e(9)),component=Object(n.a)(r,(function(){var t=this,o=t.$createElement,e=t._self._c||o;return e("div",[e("p",{class:t.mode},[t._v("\n "+t._s(t.message)+"\n ")])])}),[],!1,null,"6828301b",null);o.default=component.exports},263:function(t,o,e){"use strict";e(252)},264:function(t,o,e){var r=e(14)(!1);r.push([t.i,".warn[data-v-6828301b]{background-color:rgba(255,204,0,.26667)}.error[data-v-6828301b],.warn[data-v-6828301b]{color:#bbb;font-size:14pt}.error[data-v-6828301b]{background-color:rgba(221,68,0,.26667)}.normal[data-v-6828301b]{color:#bbb;font-size:14pt}",""]),t.exports=r},265:function(t,o,e){var content=e(280);content.__esModule&&(content=content.default),"string"==typeof content&&(content=[[t.i,content,""]]),content.locals&&(t.exports=content.locals);(0,e(15).default)("4667df8d",content,!0,{sourceMap:!1})},279:function(t,o,e){"use strict";e(265)},280:function(t,o,e){var r=e(14)(!1);r.push([t.i,".log-box[data-v-fc279734]{width:100%;height:600px;border-radius:5px;padding:20px;background-color:hsla(0,0%,100%,.33333);overflow-y:scroll}",""]),t.exports=r},286:function(t,o,e){"use strict";e.r(o);e(39);var r={components:{LogEntry:e(255).default},computed:{logs:function(){return this.$store.state.logs.logs}},mounted:function(){var t=this;this.$store.dispatch("logs/update"),setInterval((function(){t.$store.dispatch("logs/update")}),1e3)}},n=(e(279),e(9)),component=Object(n.a)(r,(function(){var t=this,o=t.$createElement,e=t._self._c||o;return e("div",{staticClass:"log-box"},t._l(t.logs,(function(t,o){return e("LogEntry",{key:o,attrs:{mode:0===t.type?"normal":1===t.type?"warn":"error",message:t.compiledMessage}})})),1)}),[],!1,null,"fc279734",null);o.default=component.exports;installComponents(component,{LogEntry:e(255).default})}}]);
|
1
Tubio/frontend/_nuxt/c0a3d47.js
Normal file
1
Tubio/frontend/_nuxt/e42c667.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
(window.webpackJsonp=window.webpackJsonp||[]).push([[13],{252:function(o,r,t){var content=t(264);content.__esModule&&(content=content.default),"string"==typeof content&&(content=[[o.i,content,""]]),content.locals&&(o.exports=content.locals);(0,t(15).default)("3edd6b06",content,!0,{sourceMap:!1})},255:function(o,r,t){"use strict";t.r(r);var e={props:{mode:{type:String,default:"normal"},message:{type:String}}},n=(t(263),t(9)),component=Object(n.a)(e,(function(){var o=this,r=o.$createElement,t=o._self._c||r;return t("div",[t("p",{class:o.mode},[o._v("\n "+o._s(o.message)+"\n ")])])}),[],!1,null,"6828301b",null);r.default=component.exports},263:function(o,r,t){"use strict";t(252)},264:function(o,r,t){var e=t(14)(!1);e.push([o.i,".warn[data-v-6828301b]{background-color:rgba(255,204,0,.26667)}.error[data-v-6828301b],.warn[data-v-6828301b]{color:#bbb;font-size:14pt}.error[data-v-6828301b]{background-color:rgba(221,68,0,.26667)}.normal[data-v-6828301b]{color:#bbb;font-size:14pt}",""]),o.exports=e}}]);
|
2
Tubio/frontend/_nuxt/eb2cb16.js
Normal file
1
Tubio/frontend/_nuxt/f319ef6.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
(window.webpackJsonp=window.webpackJsonp||[]).push([[5],{258:function(t,l,e){"use strict";e.r(l);var n=e(9),component=Object(n.a)({},(function(){var t=this,l=t.$createElement,e=t._self._c||l;return e("svg",{staticClass:"bi bi-download",attrs:{viewBox:"0 0 16 16",fill:"currentColor",xmlns:"http://www.w3.org/2000/svg"}},[e("path",{attrs:{"fill-rule":"evenodd",d:"M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"}}),t._v(" "),e("path",{attrs:{"fill-rule":"evenodd",d:"M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"}})])}),[],!1,null,null,null);l.default=component.exports}}]);
|
@ -1 +0,0 @@
|
|||||||
!function(e){function r(data){for(var r,n,l=data[0],f=data[1],d=data[2],i=0,h=[];i<l.length;i++)n=l[i],Object.prototype.hasOwnProperty.call(o,n)&&o[n]&&h.push(o[n][0]),o[n]=0;for(r in f)Object.prototype.hasOwnProperty.call(f,r)&&(e[r]=f[r]);for(v&&v(data);h.length;)h.shift()();return c.push.apply(c,d||[]),t()}function t(){for(var e,i=0;i<c.length;i++){for(var r=c[i],t=!0,n=1;n<r.length;n++){var f=r[n];0!==o[f]&&(t=!1)}t&&(c.splice(i--,1),e=l(l.s=r[0]))}return e}var n={},o={4:0},c=[];function l(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,l),t.l=!0,t.exports}l.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 c,script=document.createElement("script");script.charset="utf-8",script.timeout=120,l.nc&&script.setAttribute("nonce",l.nc),script.src=function(e){return l.p+""+{2:"a9a0d78",3:"21ee9b0"}[e]+".js"}(e);var f=new Error;c=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),c=r&&r.target&&r.target.src;f.message="Loading chunk "+e+" failed.\n("+n+": "+c+")",f.name="ChunkLoadError",f.type=n,f.request=c,t[1](f)}o[e]=void 0}};var d=setTimeout((function(){c({type:"timeout",target:script})}),12e4);script.onerror=script.onload=c,document.head.appendChild(script)}return Promise.all(r)},l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(object,e){return Object.prototype.hasOwnProperty.call(object,e)},l.p="/_nuxt/",l.oe=function(e){throw console.error(e),e};var f=window.webpackJsonp=window.webpackJsonp||[],d=f.push.bind(f);f.push=r,f=f.slice();for(var i=0;i<f.length;i++)r(f[i]);var v=d;t()}([]);
|
|
@ -1 +0,0 @@
|
|||||||
__NUXT_JSONP__("/", {data:[{}],fetch:[],mutations:[]});
|
|
@ -1 +0,0 @@
|
|||||||
__NUXT_JSONP__("/settings", {data:[{}],fetch:[],mutations:[]});
|
|
@ -1 +0,0 @@
|
|||||||
window.__NUXT__={staticAssetsBase:"\u002F_nuxt\u002Fstatic\u002F1611507770",layout:"default",error:null,state:{diskUsage:{usage:{}},dlcache:{cache:[]},logs:{logs:[]},serverOs:{os_name:""},serverVersion:{serverVersion:-1},settings:{config:{}}},serverRendered:true,routePath:"\u002Fsettings",config:{}};
|
|
@ -1 +0,0 @@
|
|||||||
window.__NUXT__={staticAssetsBase:"\u002F_nuxt\u002Fstatic\u002F1611507770",layout:"default",error:null,state:{diskUsage:{usage:{}},dlcache:{cache:[]},logs:{logs:[]},serverOs:{os_name:""},serverVersion:{serverVersion:-1},settings:{config:{}}},serverRendered:true,routePath:"\u002F",config:{}};
|
|
1
Tubio/frontend/_nuxt/static/1644155680/manifest.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
__NUXT_JSONP__("manifest.js", {routes:["\u002F","\u002Fsettings"]})
|
1
Tubio/frontend/_nuxt/static/1644155680/payload.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
__NUXT_JSONP__("/", {data:[{}],fetch:{},mutations:[]});
|
@ -0,0 +1 @@
|
|||||||
|
__NUXT_JSONP__("/settings", {data:[{}],fetch:{},mutations:[]});
|
1
Tubio/frontend/_nuxt/static/1644155680/settings/state.js
Normal file
@ -0,0 +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));
|
1
Tubio/frontend/_nuxt/static/1644155680/state.js
Normal file
@ -0,0 +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"));
|
@ -1,5 +1,21 @@
|
|||||||
{
|
{
|
||||||
"cache": [
|
"cache": [
|
||||||
|
{
|
||||||
|
"description": "Stream or buy the track here: https://umg.lnk.to/Knebel\n\n\"F & M\", the new Lindemann album \u2013 out now: https://umg.lnk.to/FundM\n\nVideo Director: Zoran Bihac\nVideo Producer: Nafta Films & Zoki.tv\n\nhttps://www.instagram.com/lindemannofficial/\nhttps://www.facebook.com/Lindemann\nhttps://www.lindemann.band\n\n#LINDEMANN #Knebel",
|
||||||
|
"download_progress": 100,
|
||||||
|
"download_url": "/download/1Ll3WR",
|
||||||
|
"downloaded_filename": "dlcache/download/1Ll3WR.mp4",
|
||||||
|
"duration": 232,
|
||||||
|
"mode": "video",
|
||||||
|
"queued_timestamp": 1615640695,
|
||||||
|
"status": "finished",
|
||||||
|
"thumbnail_url": "https://i.ytimg.com/vi/p64X_5GX0J8/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAmnCY71V_pUKqziS-9ceQrjuLszg",
|
||||||
|
"title": "LINDEMANN - Knebel (Official Video)",
|
||||||
|
"tubio_id": "1Ll3WR",
|
||||||
|
"uploader": "Lindemann Official",
|
||||||
|
"webpage_url": "https://www.youtube.com/watch?v=p64X_5GX0J8",
|
||||||
|
"quality": "best"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Download Eminem's 'MMLP2' Album on iTunes now:http://smarturl.it/MMLP2\n\nCredits below\nVideo Director: Rich Lee\nVideo Producer: Justin Diener\nVideo Producer: Kathy Angstadt\n\nPlaylist Best of Eminem: https://goo.gl/AquNpo\nSubscribe for more: https://goo.gl/DxCrDV\n\n#Eminem #RapGod #Vevo",
|
"description": "Download Eminem's 'MMLP2' Album on iTunes now:http://smarturl.it/MMLP2\n\nCredits below\nVideo Director: Rich Lee\nVideo Producer: Justin Diener\nVideo Producer: Kathy Angstadt\n\nPlaylist Best of Eminem: https://goo.gl/AquNpo\nSubscribe for more: https://goo.gl/DxCrDV\n\n#Eminem #RapGod #Vevo",
|
||||||
"download_progress": 0,
|
"download_progress": 0,
|
||||||
@ -13,22 +29,24 @@
|
|||||||
"title": "Eminem - Rap God (Explicit) [Official Video]",
|
"title": "Eminem - Rap God (Explicit) [Official Video]",
|
||||||
"tubio_id": "1KnEwh",
|
"tubio_id": "1KnEwh",
|
||||||
"uploader": "EminemVEVO",
|
"uploader": "EminemVEVO",
|
||||||
"webpage_url": "https://www.youtube.com/watch?v=XbGs_qK2PQA"
|
"webpage_url": "https://www.youtube.com/watch?v=XbGs_qK2PQA",
|
||||||
|
"quality": "1080p"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Check out my SoundCloud Channel for more music: https://soundcloud.com/user-411907790\n\u266c \u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u266c \nA big thank you to everyone who voluntarily financially supports my work and thus makes it possible:\n\u26abPaypal: https://www.paypal.me/KarlSternau\n\u26abPatreon: https://www.patreon.com/karlsternau\n\u266c \u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012\u2012 \u266c",
|
"description": "Rick Astley's official music video for \u201cNever Gonna Give You Up\u201d \nListen to Rick Astley: https://RickAstley.lnk.to/_listenYD\n\nSubscribe to the official Rick Astley YouTube channel: https://RickAstley.lnk.to/subscribeYD\n\nFollow Rick Astley:\nFacebook: https://RickAstley.lnk.to/followFI\nTwitter: https://RickAstley.lnk.to/followTI\nInstagram: https://RickAstley.lnk.to/followII\nWebsite: https://RickAstley.lnk.to/followWI\nSpotify: https://RickAstley.lnk.to/followSI\n\nLyrics:\nNever gonna give you up\nNever gonna let you down\nNever gonna run around and desert you\nNever gonna make you cry\nNever gonna say goodbye\nNever gonna tell a lie and hurt you\n\n#RickAstley #NeverGonnaGiveYouUp #DancePop",
|
||||||
"download_progress": 98,
|
"download_progress": 100,
|
||||||
"download_url": "/download/1KnEwb",
|
"download_url": "/download/1Lij6Z",
|
||||||
"downloaded_filename": "dlcache/download/1KnEwb.mp4",
|
"downloaded_filename": "dlcache/download/1Lij6Z.mp4",
|
||||||
"duration": 413,
|
"duration": 212,
|
||||||
"mode": "video",
|
"mode": "video",
|
||||||
"queued_timestamp": 1601481591,
|
"queued_timestamp": 1614984000,
|
||||||
"status": "downloading",
|
"status": "finished",
|
||||||
"thumbnail_url": "https://i.ytimg.com/vi/c3wRzxiQ8Zk/hqdefault.jpg?sqp=-oaymwEZCNACELwBSFXyq4qpAwsIARUAAIhCGAFwAQ==&rs=AOn4CLC4MWTAc7sY5uF6hFzNk2D0tawTSA",
|
"thumbnail_url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg?sqp=-oaymwEcCNACELwBSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLB_p0PncTtkrhaNDZtntrE3gKkoYw",
|
||||||
"title": "Sing with Karl - Des Morgens Um Halb Viere [Old soldier and student way][All Stanzas]",
|
"title": "Rick Astley - Never Gonna Give You Up (Video)",
|
||||||
"tubio_id": "1KnEwb",
|
"tubio_id": "1Lij6Z",
|
||||||
"uploader": "Karl Sternau",
|
"uploader": "RickAstleyVEVO",
|
||||||
"webpage_url": "https://www.youtube.com/watch?v=c3wRzxiQ8Zk"
|
"webpage_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||||
|
"quality": "720p"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Latest video off of Lil Dicky's debut album \"Professional Rapper.\" Download the album here: http://smarturl.it/LilDickyiTunes \n\nWatch Lil Dicky create the most epic rap video, all while he $aves Dat Money! Click here for the documentary: https://www.youtube.com/watch?v=zkXpb20b-NQ\n\nDirector\nTony Yacenda\nwww.TonyYacenda.com\n\nProducer\nJim Cummings\n\nCinematographer\nAlan Gwizdowski\n\nMusic Video Editor\nBrian Vannucci\n\nDocumentary Editor\nBrad Allen Wilde\n\nPost Supervisor\nRyan Ross\n\nColorist\nSean Wells\n\nSound Mixers\nDi Le\nDarrell Tung\n\nSecond Unit DPs\nAdam Lee\nJeff Kulig\nLuc Delamare\n\nBoat Girls\nMelissa Soria\nSuzanne Quast\nJulia Misaki\n\nSpecial Thanks\n\"The Study\u201d Hollywood\nAvilaVIP\nMrs. \u201cK\u201d\nThe Noho Diner\nFrosty Treats/Amp Entertainment\n\nWeedmaps\nMaster and Dynamic\nRGF Productions\nJash\nMeUndies (www.meundies.com)\n\nand\n\nSarah Silverman\nKevin Durant\nDillon Francis\nHannibal Buress\nAbbi Jacobson\nIlana Glazer\nMark Cuban\nTom Petty\n\nSong produced by Money Alwayz\nSong Mixed by Rob Kinelski at The Fortress of Amplitude\nAssistant Engineer: David Baker",
|
"description": "Latest video off of Lil Dicky's debut album \"Professional Rapper.\" Download the album here: http://smarturl.it/LilDickyiTunes \n\nWatch Lil Dicky create the most epic rap video, all while he $aves Dat Money! Click here for the documentary: https://www.youtube.com/watch?v=zkXpb20b-NQ\n\nDirector\nTony Yacenda\nwww.TonyYacenda.com\n\nProducer\nJim Cummings\n\nCinematographer\nAlan Gwizdowski\n\nMusic Video Editor\nBrian Vannucci\n\nDocumentary Editor\nBrad Allen Wilde\n\nPost Supervisor\nRyan Ross\n\nColorist\nSean Wells\n\nSound Mixers\nDi Le\nDarrell Tung\n\nSecond Unit DPs\nAdam Lee\nJeff Kulig\nLuc Delamare\n\nBoat Girls\nMelissa Soria\nSuzanne Quast\nJulia Misaki\n\nSpecial Thanks\n\"The Study\u201d Hollywood\nAvilaVIP\nMrs. \u201cK\u201d\nThe Noho Diner\nFrosty Treats/Amp Entertainment\n\nWeedmaps\nMaster and Dynamic\nRGF Productions\nJash\nMeUndies (www.meundies.com)\n\nand\n\nSarah Silverman\nKevin Durant\nDillon Francis\nHannibal Buress\nAbbi Jacobson\nIlana Glazer\nMark Cuban\nTom Petty\n\nSong produced by Money Alwayz\nSong Mixed by Rob Kinelski at The Fortress of Amplitude\nAssistant Engineer: David Baker",
|
||||||
@ -43,7 +61,8 @@
|
|||||||
"title": "Lil Dicky - $ave Dat Money feat. Fetty Wap and Rich Homie Quan (Official Music Video)",
|
"title": "Lil Dicky - $ave Dat Money feat. Fetty Wap and Rich Homie Quan (Official Music Video)",
|
||||||
"tubio_id": "1KnEw2",
|
"tubio_id": "1KnEw2",
|
||||||
"uploader": "Lil Dicky",
|
"uploader": "Lil Dicky",
|
||||||
"webpage_url": "https://www.youtube.com/watch?v=yvHYWD29ZNY"
|
"webpage_url": "https://www.youtube.com/watch?v=yvHYWD29ZNY",
|
||||||
|
"quality": "360p"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Get the new album \"Wintersaga\" here: https://smarturl.it/Wintersaga-NPR\n\nWind Rose states:\n\u201cMining is one of the most important activities for a Dwarf, naturally Wind Rose needed a theme song for this great honor of collecting these jewels from the soil, so sing with us with pride!!\u201c \n\n\"Diggy Diggy Hole\" originally written by Yogscast\n\nMixed and Mastered by Lasse Lammert",
|
"description": "Get the new album \"Wintersaga\" here: https://smarturl.it/Wintersaga-NPR\n\nWind Rose states:\n\u201cMining is one of the most important activities for a Dwarf, naturally Wind Rose needed a theme song for this great honor of collecting these jewels from the soil, so sing with us with pride!!\u201c \n\n\"Diggy Diggy Hole\" originally written by Yogscast\n\nMixed and Mastered by Lasse Lammert",
|
||||||
@ -58,7 +77,8 @@
|
|||||||
"title": "WIND ROSE - Diggy Diggy Hole (Official Video) | Napalm Records",
|
"title": "WIND ROSE - Diggy Diggy Hole (Official Video) | Napalm Records",
|
||||||
"tubio_id": "1KnEvV",
|
"tubio_id": "1KnEvV",
|
||||||
"uploader": "Napalm Records",
|
"uploader": "Napalm Records",
|
||||||
"webpage_url": "https://www.youtube.com/watch?v=34CZjsEI1yU"
|
"webpage_url": "https://www.youtube.com/watch?v=34CZjsEI1yU",
|
||||||
|
"quality": "144p"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"cache_size": 4,
|
"cache_size": 4,
|
||||||
|
@ -1,7 +1,45 @@
|
|||||||
#include "Framework.h"
|
#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()
|
int main()
|
||||||
{
|
{
|
||||||
|
#ifndef _WIN
|
||||||
|
// Deamonize();
|
||||||
|
#endif
|
||||||
|
|
||||||
Framework framework;
|
Framework framework;
|
||||||
framework.Run();
|
framework.Run();
|
||||||
|
|
||||||
|
@ -32,6 +32,33 @@
|
|||||||
},
|
},
|
||||||
"response": []
|
"response": []
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "GetServerVersion",
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"header": [],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\r\n \"request\": \"get_server_version\"\r\n}",
|
||||||
|
"options": {
|
||||||
|
"raw": {
|
||||||
|
"language": "json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "localhost:6969/api",
|
||||||
|
"host": [
|
||||||
|
"localhost"
|
||||||
|
],
|
||||||
|
"port": "6969",
|
||||||
|
"path": [
|
||||||
|
"api"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "DownloadVideo",
|
"name": "DownloadVideo",
|
||||||
"request": {
|
"request": {
|
||||||
@ -182,12 +209,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"url": {
|
"url": {
|
||||||
"raw": "192.168.1.12:6969/api",
|
"raw": "localhost:6969/api",
|
||||||
"host": [
|
"host": [
|
||||||
"192",
|
"localhost"
|
||||||
"168",
|
|
||||||
"1",
|
|
||||||
"12"
|
|
||||||
],
|
],
|
||||||
"port": "6969",
|
"port": "6969",
|
||||||
"path": [
|
"path": [
|
||||||
@ -212,12 +236,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"url": {
|
"url": {
|
||||||
"raw": "192.168.1.12:6969/api",
|
"raw": "localhost:6969/api",
|
||||||
"host": [
|
"host": [
|
||||||
"192",
|
"localhost"
|
||||||
"168",
|
|
||||||
"1",
|
|
||||||
"12"
|
|
||||||
],
|
],
|
||||||
"port": "6969",
|
"port": "6969",
|
||||||
"path": [
|
"path": [
|
||||||
@ -323,12 +344,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"url": {
|
"url": {
|
||||||
"raw": "192.168.1.12:6969/api",
|
"raw": "localhost:6969/api",
|
||||||
"host": [
|
"host": [
|
||||||
"192",
|
"localhost"
|
||||||
"168",
|
|
||||||
"1",
|
|
||||||
"12"
|
|
||||||
],
|
],
|
||||||
"port": "6969",
|
"port": "6969",
|
||||||
"path": [
|
"path": [
|
||||||
@ -499,7 +517,33 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"response": []
|
"response": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ResetConfig to default",
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"header": [],
|
||||||
|
"body": {
|
||||||
|
"mode": "raw",
|
||||||
|
"raw": "{\r\n \"request\": \"reset_config_to_default_values\"\r\n}",
|
||||||
|
"options": {
|
||||||
|
"raw": {
|
||||||
|
"language": "json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"raw": "localhost:6969/api",
|
||||||
|
"host": [
|
||||||
|
"localhost"
|
||||||
|
],
|
||||||
|
"port": "6969",
|
||||||
|
"path": [
|
||||||
|
"api"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"response": []
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"protocolProfileBehavior": {}
|
|
||||||
}
|
}
|
BIN
github-media/chromeext-context.png
Normal file
After Width: | Height: | Size: 327 KiB |
BIN
github-media/chromeext-popup.png
Normal file
After Width: | Height: | Size: 122 KiB |
BIN
github-media/teaser.png
Normal file
After Width: | Height: | Size: 108 KiB |
3
goodies/chrome-extension/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
*.zip
|
||||||
|
*.rar
|
||||||
|
*.6zip
|
58
goodies/chrome-extension/background.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
function QueueDownloadByInfo(info, mode, quality) {
|
||||||
|
|
||||||
|
// Select either the current page location, or the target of a href (if the user clicked on one)
|
||||||
|
QueueDownload(
|
||||||
|
urlToDownload = typeof(info.linkUrl) != 'undefined' ? info.linkUrl : info.pageUrl,
|
||||||
|
mode,
|
||||||
|
quality
|
||||||
|
); // <- Defined in queueDwonload.js
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create main context menu entry
|
||||||
|
chrome.contextMenus.create({
|
||||||
|
title: "Tubio",
|
||||||
|
contexts:["all"],
|
||||||
|
id: "tubio-parent-contextmenu-entry",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Automate creating a lot of entries
|
||||||
|
function CreateContextMenuOption(optionName, callback) {
|
||||||
|
chrome.contextMenus.create({
|
||||||
|
title: optionName,
|
||||||
|
contexts: ["all"],
|
||||||
|
type: "normal",
|
||||||
|
parentId: "tubio-parent-contextmenu-entry",
|
||||||
|
onclick: callback
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create all download methods
|
||||||
|
CreateContextMenuOption("Download Video - Best", function(info) {
|
||||||
|
QueueDownloadByInfo(info, "video", "best");
|
||||||
|
});
|
||||||
|
CreateContextMenuOption("Download Video - 1440p", function(info) {
|
||||||
|
QueueDownloadByInfo(info, "video", "1440p");
|
||||||
|
});
|
||||||
|
CreateContextMenuOption("Download Video - 1080p", function(info) {
|
||||||
|
QueueDownloadByInfo(info, "video", "1080p");
|
||||||
|
});
|
||||||
|
CreateContextMenuOption("Download Video - 720p", function(info) {
|
||||||
|
QueueDownloadByInfo(info, "video", "720p");
|
||||||
|
});
|
||||||
|
CreateContextMenuOption("Download Video - 480p", function(info) {
|
||||||
|
QueueDownloadByInfo(info, "video", "480p");
|
||||||
|
});
|
||||||
|
CreateContextMenuOption("Download Video - 360p", function(info) {
|
||||||
|
QueueDownloadByInfo(info, "video", "360p");
|
||||||
|
});
|
||||||
|
CreateContextMenuOption("Download Video - 240p", function(info) {
|
||||||
|
QueueDownloadByInfo(info, "video", "240p");
|
||||||
|
});
|
||||||
|
CreateContextMenuOption("Download Video - 144p", function(info) {
|
||||||
|
QueueDownloadByInfo(info, "video", "144p");
|
||||||
|
});
|
||||||
|
CreateContextMenuOption("Download Audio", function(info) {
|
||||||
|
QueueDownloadByInfo(info, "audio", "best"); // <- Quality is ignored when downloading audio only
|
||||||
|
});
|
BIN
goodies/chrome-extension/icon/128.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
goodies/chrome-extension/icon/16.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
goodies/chrome-extension/icon/48.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
3
goodies/chrome-extension/lib/axios.js
Normal file
2
goodies/chrome-extension/lib/jquery-3.5.1.min.js
vendored
Normal file
33
goodies/chrome-extension/manifest.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"manifest_version": 2,
|
||||||
|
"name": "Tubio - Companion Extension",
|
||||||
|
"version": "1.0",
|
||||||
|
|
||||||
|
"icons": {
|
||||||
|
"16": "icon/16.png",
|
||||||
|
"48": "icon/48.png",
|
||||||
|
"128": "icon/128.png"
|
||||||
|
},
|
||||||
|
|
||||||
|
"browser_action": {
|
||||||
|
"default_popup": "popup/index.html"
|
||||||
|
},
|
||||||
|
|
||||||
|
"permissions": [
|
||||||
|
"background",
|
||||||
|
"contextMenus",
|
||||||
|
"webRequest",
|
||||||
|
"windows",
|
||||||
|
"tabs",
|
||||||
|
"storage",
|
||||||
|
"*://*/*"
|
||||||
|
],
|
||||||
|
|
||||||
|
"background": {
|
||||||
|
"scripts": [
|
||||||
|
"lib/axios.js",
|
||||||
|
"queueDownload.js",
|
||||||
|
"background.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
37
goodies/chrome-extension/popup/css/background.css
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
body {
|
||||||
|
width: 300px;
|
||||||
|
height: 340px;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
body .bgmask {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
position: fixed;
|
||||||
|
z-index: -8;
|
||||||
|
background-image: linear-gradient(#0000 50%, #04254e);
|
||||||
|
}
|
||||||
|
|
||||||
|
body .bggatter {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
z-index: -9;
|
||||||
|
-webkit-mask-image: url("../image/grid.png");
|
||||||
|
background-color: #5954a4;
|
||||||
|
}
|
||||||
|
|
||||||
|
body .bggatter__wrapper {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
position: fixed;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: -9;
|
||||||
|
}
|
||||||
|
|
||||||
|
body .bgdrop {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
position: fixed;
|
||||||
|
z-index: -10;
|
||||||
|
background-color: #04254e;
|
||||||
|
}
|
42
goodies/chrome-extension/popup/css/button.css
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
button {
|
||||||
|
padding: 0.5em 2em;
|
||||||
|
color: #04254e;
|
||||||
|
font-size: 14pt;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition:
|
||||||
|
color 0.15s ease,
|
||||||
|
transform 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.small {
|
||||||
|
padding: 0.15em 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
transform: scale(1.035);
|
||||||
|
color: #fff;
|
||||||
|
background-color: #5954a4;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.special:hover {
|
||||||
|
/*background-color: #5954a4;*/
|
||||||
|
color: #fff;
|
||||||
|
animation: color-cycle;
|
||||||
|
animation-duration: 1s;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.lock {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes color-cycle {
|
||||||
|
0% {background-color: #ff6666; transform: scale(1.075);}
|
||||||
|
16.67% {background-color: #c966ff;}
|
||||||
|
33.33% {background-color: #6678ff;}
|
||||||
|
50% {background-color: #66edff; transform: scale(1.15);}
|
||||||
|
66.66% {background-color: #66ff70;}
|
||||||
|
83.33% {background-color: #fcff66;}
|
||||||
|
100% {background-color: #ff6666; transform: scale(1.075);}
|
||||||
|
}
|
1
goodies/chrome-extension/popup/css/font/include.css
Normal file
@ -0,0 +1 @@
|
|||||||
|
@import "zilla-slab.css"
|
49
goodies/chrome-extension/popup/css/font/zilla-slab.css
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: "ZillaSlab";
|
||||||
|
src: url("../../font/zilla-slab/ZillaSlab-Regular.ttf") format('truetype');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "ZillaSlab";
|
||||||
|
src: url("../../font/zilla-slab/ZillaSlab-Italic.ttf") format('truetype');
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "ZillaSlab";
|
||||||
|
src: url("../../font/zilla-slab/ZillaSlab-Bold.ttf") format('truetype');
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "ZillaSlab";
|
||||||
|
src: url("../../font/zilla-slab/ZillaSlab-BoldItalic.ttf") format('truetype');
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "ZillaSlab";
|
||||||
|
src: url("../../font/zilla-slab/ZillaSlab-Light.ttf") format('truetype');
|
||||||
|
font-weight: lighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "ZillaSlab";
|
||||||
|
src: url("../../font/zilla-slab/ZillaSlab-Light.ttf") format('truetype');
|
||||||
|
font-weight: lighter;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "ZillaSlab";
|
||||||
|
src: url("../../font/zilla-slab/ZillaSlab-Medium.ttf") format('truetype');
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "ZillaSlab";
|
||||||
|
src: url("../../font/zilla-slab/ZillaSlab-Medium.ttf") format('truetype');
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
14
goodies/chrome-extension/popup/css/form.css
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
label {
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
border-radius: 5px;
|
||||||
|
height: 25pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
textarea {
|
||||||
|
color: #04254e;
|
||||||
|
font-size: 14pt;
|
||||||
|
}
|
23
goodies/chrome-extension/popup/css/modular.css
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
.text-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pt-1 {
|
||||||
|
padding-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-col {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.justify-content-center {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-items-center {
|
||||||
|
align-items: center;
|
||||||
|
}
|
15
goodies/chrome-extension/popup/css/typography.css
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
* {
|
||||||
|
font-family: 'ZillaSlab' !important;
|
||||||
|
color: #dadada;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
a,
|
||||||
|
label {
|
||||||
|
font-size: 14pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2 {
|
||||||
|
line-height: 1em;
|
||||||
|
}
|
BIN
goodies/chrome-extension/popup/image/grid.png
Normal file
After Width: | Height: | Size: 527 B |
46
goodies/chrome-extension/popup/index.html
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="css/background.css">
|
||||||
|
<link rel="stylesheet" href="css/typography.css">
|
||||||
|
<link rel="stylesheet" href="css/modular.css">
|
||||||
|
<link rel="stylesheet" href="css/form.css">
|
||||||
|
<link rel="stylesheet" href="css/button.css">
|
||||||
|
<link rel="stylesheet" href="css/font/include.css">
|
||||||
|
<title>Tubio - Companion Extension</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="bgdrop"></div>
|
||||||
|
<div class="bggatter__wrapper">
|
||||||
|
<div class="bggatter"></div>
|
||||||
|
</div>
|
||||||
|
<div class="bgmask"></div>
|
||||||
|
|
||||||
|
|
||||||
|
<h1 class="text-center pt-1" style="margin-bottom: 0;">Tubio</h1>
|
||||||
|
<h2 class="text-center" style="margin-top: 0; padding-top: 0;">Companion Extension</h2>
|
||||||
|
|
||||||
|
<div class="flex justify-content-center">
|
||||||
|
<div class="flex flex-col align-items-center">
|
||||||
|
|
||||||
|
<!-- Download this - button -->
|
||||||
|
<button class="special" id="button--download" title="Will download this pages URL as a video of highest quality.">
|
||||||
|
Download!
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button class="small" id="button--take-me-home" style="margin-top: 0.5em;">Take me to my downloads</button>
|
||||||
|
|
||||||
|
<!-- Tubio config form -->
|
||||||
|
<label for="tubio-address" style="margin-top: 3em;">Where can i reach tubio?</label>
|
||||||
|
<input type="text" id="tubio-address" class="text-center" />
|
||||||
|
<button class="small" id="button--save-settings" style="margin-top: 0.5em;">Save settings</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="../lib/jquery-3.5.1.min.js"></script>
|
||||||
|
<script src="../lib/axios.js"></script>
|
||||||
|
<script src="../queueDownload.js"></script>
|
||||||
|
<script src="js/script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
38
goodies/chrome-extension/popup/js/script.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
$("#button--download").click(function() {
|
||||||
|
|
||||||
|
$("#button--download").addClass("lock");
|
||||||
|
|
||||||
|
// Query active tab in active window...
|
||||||
|
chrome.windows.getCurrent(function(w) {
|
||||||
|
chrome.tabs.query({
|
||||||
|
active: true,
|
||||||
|
windowId: w.id
|
||||||
|
}, function(foundTabs) {
|
||||||
|
if (foundTabs.length > 0) {
|
||||||
|
QueueDownload(foundTabs[0].url, "video", "best", function(){
|
||||||
|
window.close();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log("Wtf, this should not happen. You don't have ANY open tab?...");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
chrome.storage.local.get(['tubio_address'], function(result) {
|
||||||
|
$("#tubio-address").val(result.tubio_address);
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#button--save-settings").click(function() {
|
||||||
|
chrome.storage.local.set({
|
||||||
|
tubio_address: $("#tubio-address").val()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#button--take-me-home").click(function() {
|
||||||
|
chrome.storage.local.get(['tubio_address'], function(result) {
|
||||||
|
chrome.tabs.create({url: result.tubio_address});
|
||||||
|
});
|
||||||
|
});
|
38
goodies/chrome-extension/queueDownload.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
function QueueDownload(url, mode, quality, callback) {
|
||||||
|
|
||||||
|
console.log("Queueing '" + url + "'...");
|
||||||
|
|
||||||
|
// Fetch tubio address
|
||||||
|
chrome.storage.local.get(['tubio_address'], function(result) {
|
||||||
|
|
||||||
|
if ((typeof(result.tubio_address) == 'undefined') ||
|
||||||
|
(result.tubio_address.length == 0)) {
|
||||||
|
alert("Please first set an address to reach Tubio at in the extensions settings! (Click on the extensions icon in the toolbar).");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
tubioUrl = result.tubio_address;
|
||||||
|
if (tubioUrl[tubioUrl.length-1] != '/') { // Has the user not appended a /?
|
||||||
|
tubioUrl += '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
axios.post(tubioUrl + "api", {
|
||||||
|
request: "queue_download",
|
||||||
|
video_url: url,
|
||||||
|
mode: mode,
|
||||||
|
quality: quality
|
||||||
|
}).then(function(response){
|
||||||
|
console.log("Queued successfully...");
|
||||||
|
if (typeof(callback) != 'undefined') callback(true, response);
|
||||||
|
|
||||||
|
}).catch(function(response){
|
||||||
|
console.log("Something went wrong...");
|
||||||
|
console.log(response);
|
||||||
|
if (typeof(callback) != 'undefined') callback(false, response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
9
goodies/chrome-extension/readme.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# 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.).
|
||||||
|
|
23
goodies/dockerfiles/Dockerfile
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
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"]
|
||||||
|
|
4
goodies/dockerfiles/README.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# 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.
|
||||||
|
|
18
goodies/dockerfiles/docker-compose.yml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
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
|
||||||
|
|