mirror of
https://onedev.site.tesses.net/tytd2025
synced 2026-06-01 18:05:32 +00:00
First commit
This commit is contained in:
18
Tesses.YouTubeDownloader.Server/cross.json
Normal file
18
Tesses.YouTubeDownloader.Server/cross.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"icon": "icon.png",
|
||||
"info": {
|
||||
"description": "Download YouTube Videos (using CrossLang, via web interface, great for homelabs)",
|
||||
"maintainer": "Mike Nolan",
|
||||
"repo":"https://onedev.site.tesses.net/tytd2025",
|
||||
"homepage": "https://tesses.net/apps/tytd/2025/",
|
||||
"license": "GPLv3",
|
||||
"short_name": "tytd2025",
|
||||
"short_name_pretty": "Tesses YouTube Downloader 2025",
|
||||
"type": "webapp"
|
||||
},
|
||||
"name": "Tesses.YouTubeDownloader.Server",
|
||||
"project_dependencies": [
|
||||
"..\/Tesses.YouTubeDownloader"
|
||||
],
|
||||
"version": "1.0.0.0-prod"
|
||||
}
|
||||
1
Tesses.YouTubeDownloader.Server/res/beer.min.css
vendored
Normal file
1
Tesses.YouTubeDownloader.Server/res/beer.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
Tesses.YouTubeDownloader.Server/res/beer.min.js
vendored
Normal file
1
Tesses.YouTubeDownloader.Server/res/beer.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
Tesses.YouTubeDownloader.Server/res/favicon.ico
Normal file
BIN
Tesses.YouTubeDownloader.Server/res/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 264 KiB |
1
Tesses.YouTubeDownloader.Server/res/htmx.min.js
vendored
Normal file
1
Tesses.YouTubeDownloader.Server/res/htmx.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
Tesses.YouTubeDownloader.Server/res/icon.png
Normal file
BIN
Tesses.YouTubeDownloader.Server/res/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
19
Tesses.YouTubeDownloader.Server/res/loading-indicator.svg
Normal file
19
Tesses.YouTubeDownloader.Server/res/loading-indicator.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<svg width="100%" height="100%" viewBox="4 4 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#D0BCFF">
|
||||
<animate attributeName="d" dur="5s" repeatCount="indefinite" calcMode="spline" keySplines="0.5 0.2 0 0.8; 0.5 0.2 0 0.8; 0.5 0.2 0 0.8; 0.5 0.2 0 0.8; 0.5 0.2 0 0.8; 0.5 0.2 0 0.8; 0.5 0.2 0 0.8; 0.5 0.2 0 0.8; 0.5 0.2 0 0.8; 0.5 0.2 0 0.8; 0.5 0.2 0 0.8; 0.5 0.2 0 0.8; 0.5 0.2 0 0.8" keyTimes="0; 0.14; 0.14; 0.29; 0.29; 0.43; 0.43; 0.57; 0.57; 0.71; 0.71; 0.86; 0.86; 1" values="M20.9 10.4 21.4 9.5 21.9 8.7 22.5 7.8 23.2 7.2 24.2 7 25.1 7.4 25.7 8.1 26.2 9 26.8 9.8 27.3 10.6 28.1 11.2 29 11.3 30 11 30.9 10.6 31.8 10.3 32.8 9.9 33.7 10 34.5 10.6 34.9 11.5 34.8 12.5 34.8 13.5 34.7 14.5 34.7 15.5 35.2 16.3 36 16.8 37 17.1 37.9 17.3 38.9 17.5 39.8 17.9 40.4 18.7 40.5 19.7 40 20.5 39.4 21.3 38.7 22 38.1 22.8 37.6 23.7 37.7 24.6 38.3 25.5 38.9 26.2 39.6 27 40.2 27.7 40.5 28.7 40.3 29.6 39.5 30.3 38.6 30.6 37.6 30.8 36.7 31 35.7 31.3 35 31.9 34.6 32.8 34.7 33.8 34.8 34.8 34.9 35.8 34.8 36.8 34.3 37.6 33.4 38.1 32.4 38 31.5 37.6 30.6 37.2 29.7 36.9 28.7 36.6 27.8 36.9 27.1 37.6 26.6 38.5 26.1 39.3 25.5 40.2 24.8 40.8 23.8 41 22.9 40.6 22.3 39.9 21.8 39 21.2 38.2 20.7 37.4 19.9 36.8 19 36.7 18 37 17.1 37.4 16.2 37.7 15.2 38.1 14.3 38 13.5 37.4 13.1 36.5 13.2 35.5 13.2 34.5 13.3 33.5 13.3 32.5 12.8 31.7 12 31.2 11 31 10.1 30.7 9.1 30.5 8.2 30.1 7.6 29.3 7.5 28.3 8 27.5 8.7 26.7 9.3 26 9.9 25.2 10.4 24.3 10.3 23.4 9.7 22.5 9.1 21.8 8.4 21 7.8 20.3 7.5 19.3 7.7 18.4 8.5 17.7 9.4 17.4 10.4 17.2 11.3 17 12.3 16.7 13 16.1 13.4 15.2 13.3 14.2 13.2 13.2 13.1 12.2 13.2 11.2 13.7 10.4 14.6 9.9 15.6 10 16.5 10.4 17.4 10.8 18.3 11.1 19.3 11.4 20.2 11.1Z;
|
||||
M20.3 8.6 21.1 8 22 7.6 23 7.3 23 7.3 24 7.2 25 7.3 25.9 7.5 26.8 8 27.6 8.6 28.4 9.1 28.4 9.1 29.3 9.6 30.3 9.8 31.3 9.9 32.3 10 33.3 10.2 34.2 10.6 34.2 10.6 35 11.2 35.7 11.9 36.3 12.7 36.7 13.6 36.9 14.6 37.2 15.5 37.2 15.5 37.6 16.5 38.2 17.3 38.9 18 39.6 18.7 40.2 19.5 40.6 20.4 40.6 20.4 40.9 21.3 41 22.3 40.9 23.3 40.6 24.3 40.2 25.2 39.8 26.1 39.8 26.1 39.5 27 39.4 28 39.5 29 39.6 30 39.5 31 39.3 32 39.3 32 38.9 32.9 38.3 33.7 37.6 34.4 36.8 35 35.9 35.4 35 35.8 35 35.8 34.1 36.3 33.4 37 32.9 37.9 32.3 38.7 31.6 39.4 30.8 40 30.8 40 29.9 40.4 28.9 40.7 27.9 40.8 27 40.7 26 40.4 25 40.1 25 40.1 24 40 23 40.1 22.1 40.4 21.1 40.7 20.1 40.8 19.1 40.7 19.1 40.7 18.2 40.5 17.3 40 16.4 39.5 15.7 38.8 15.2 37.9 14.6 37.1 14.6 37.1 13.9 36.4 13.1 35.8 12.2 35.4 11.3 35 10.5 34.4 9.7 33.8 9.7 33.8 9.1 32.9 8.7 32 8.5 31.1 8.4 30.1 8.5 29.1 8.6 28.1 8.6 28.1 8.5 27.1 8.3 26.1 7.8 25.2 7.4 24.3 7.1 23.4 7 22.4 7 22.4 7.1 21.4 7.3 20.4 7.8 19.5 8.3 18.7 9.1 18 9.8 17.3 9.8 17.3 10.4 16.5 10.8 15.6 11 14.6 11.3 13.7 11.7 12.8 12.2 11.9 12.2 11.9 12.9 11.2 13.8 10.7 14.7 10.2 15.6 10 16.6 9.9 17.6 9.8 17.6 9.8 18.6 9.6 19.5 9.2Z;
|
||||
M18.6 9.6 19.5 9.2 20.3 8.6 21.1 8 22 7.6 23 7.3 24 7.2 25 7.3 25.9 7.5 26.8 8 27.6 8.6 28.4 9.1 29.3 9.6 30.3 9.8 31.3 9.9 32.3 10 33.3 10.2 34.2 10.6 35 11.2 35.7 11.9 36.3 12.7 36.7 13.6 36.9 14.6 37.2 15.5 37.6 16.4 38.2 17.3 38.9 18 39.6 18.7 40.2 19.5 40.6 20.4 40.9 21.3 41 22.3 40.9 23.3 40.6 24.3 40.2 25.2 39.8 26.1 39.5 27 39.4 28 39.5 29 39.6 30 39.5 31 39.3 32 38.9 32.9 38.3 33.7 37.6 34.4 36.8 35 35.9 35.4 35 35.8 34.1 36.3 33.4 37 32.9 37.9 32.3 38.7 31.6 39.4 30.8 40 29.9 40.4 28.9 40.7 27.9 40.8 27 40.7 26 40.4 25 40.1 24 40 23 40.1 22.1 40.4 21.1 40.7 20.1 40.8 19.1 40.7 18.2 40.5 17.3 40 16.4 39.5 15.7 38.8 15.2 37.9 14.6 37.1 13.9 36.4 13.1 35.8 12.2 35.4 11.3 35 10.5 34.4 9.7 33.8 9.1 32.9 8.7 32 8.5 31.1 8.4 30.1 8.5 29.1 8.6 28.1 8.5 27.1 8.3 26.1 7.8 25.2 7.4 24.3 7.1 23.4 7 22.4 7.1 21.4 7.3 20.4 7.8 19.5 8.3 18.7 9.1 18 9.8 17.3 10.4 16.5 10.8 15.6 11 14.6 11.3 13.7 11.7 12.8 12.2 11.9 12.9 11.2 13.8 10.7 14.7 10.2 15.6 10 16.6 9.9 17.6 9.8Z;
|
||||
M18.6 9.9 19.5 9.4 20.3 8.8 21.1 8.2 22 7.8 23 7.6 23.9 7.5 24.9 7.6 25.9 7.8 26.8 8.2 27.6 8.7 28.5 9.3 29.3 9.9 30.1 10.5 30.9 11 31.7 11.6 32.5 12.2 33.3 12.8 33.7 13.1 34.1 13.3 34.9 13.9 35.7 14.5 36.6 15 37.4 15.6 38.2 16.2 39 16.8 39.7 17.5 40.2 18.3 40.7 19.2 40.9 20.1 41 21.1 40.9 22.1 40.7 23.1 40.3 24 40 24.9 39.7 25.9 39.4 26.8 39 27.8 38.7 28.7 38.4 29.6 38.1 30.6 37.8 31.5 37.5 32.5 37.2 33.4 36.9 34.4 36.6 35.3 36.2 36.2 35.7 37.1 35 37.8 34.3 38.4 33.4 38.9 32.5 39.3 31.5 39.5 30.5 39.5 30 39.5 29.5 39.5 28.5 39.5 27.5 39.5 26.5 39.5 25.5 39.4 24.5 39.4 23.6 39.4 22.6 39.4 21.6 39.5 20.6 39.5 19.6 39.5 18.6 39.5 17.6 39.5 16.6 39.5 15.6 39.3 14.7 39 13.8 38.5 13.1 37.9 12.4 37.2 11.9 36.3 11.5 35.4 11.2 34.5 10.9 33.5 10.6 32.6 10.3 31.6 10 30.7 9.7 29.7 9.3 28.8 9 27.9 8.7 26.9 8.4 26 8 25 7.7 24.1 7.4 23.2 7.1 22.2 7.1 21.7 7 21.2 7.1 20.2 7.3 19.3 7.7 18.4 8.3 17.5 8.9 16.8 9.7 16.2 10.5 15.6 11.4 15.1 12.2 14.5 13 14 13.8 13.4 14.6 12.8 15.4 12.3 16.2 11.7 17 11.1 17.8 10.5Z;
|
||||
M15.4 12.3 16.2 11.7 17 11.1 17.8 10.5 18.6 9.9 19.5 9.4 20.3 8.8 21.1 8.3 22 7.8 23 7.6 23.9 7.5 24.9 7.6 25.9 7.8 26.8 8.2 27.6 8.7 28.5 9.3 29.3 9.9 30.1 10.5 30.9 11 31.7 11.6 32.5 12.2 33.3 12.8 34.1 13.3 34.9 13.9 35.7 14.5 36.6 15 37.4 15.6 38.2 16.2 39 16.8 39.7 17.5 40.2 18.3 40.7 19.2 40.9 20.1 41 21.1 40.9 22.1 40.7 23.1 40.3 24 40 24.9 39.7 25.9 39.4 26.8 39 27.8 38.7 28.7 38.4 29.6 38.1 30.6 37.8 31.5 37.5 32.5 37.2 33.4 36.9 34.4 36.6 35.3 36.2 36.2 35.7 37.1 35 37.8 34.3 38.4 33.4 38.9 32.5 39.3 31.5 39.4 30.5 39.5 29.5 39.5 28.5 39.5 27.5 39.5 26.5 39.5 25.5 39.4 24.5 39.4 23.6 39.4 22.6 39.4 21.6 39.5 20.6 39.5 19.6 39.5 18.6 39.5 17.6 39.5 16.6 39.5 15.6 39.3 14.7 39 13.8 38.5 13.1 37.9 12.4 37.2 11.9 36.3 11.5 35.4 11.2 34.5 10.9 33.5 10.6 32.6 10.3 31.6 10 30.7 9.7 29.7 9.3 28.8 9 27.9 8.7 26.9 8.4 26 8 25 7.7 24.1 7.4 23.2 7.1 22.2 7 21.2 7.1 20.2 7.3 19.3 7.7 18.4 8.3 17.5 8.9 16.8 9.7 16.2 10.5 15.6 11.4 15.1 12.2 14.5 13 14 13.8 13.4 14.6 12.8Z;
|
||||
M17 12.8 17.7 12.1 18.5 11.5 19.3 10.9 20.1 10.5 20.2 10.4 21.1 10 22 9.7 23 9.4 24 9.2 25 9 26 9 27 9 27.6 9.1 28 9.1 28.9 9.3 29.9 9.6 30.9 9.9 31.8 10.3 32.6 10.8 33.5 11.3 34.3 11.9 34.6 12.2 35 12.6 35.7 13.3 36.4 14.1 36.9 14.9 37.4 15.8 37.9 16.6 38.3 17.6 38.6 18.5 38.6 18.7 38.8 19.5 38.9 20.5 39 21.5 39 22.5 38.9 23.5 38.7 24.5 38.5 25.4 38.2 26.3 38.2 26.4 37.8 27.3 37.4 28.2 36.8 29.1 36.2 29.9 35.6 30.6 34.9 31.3 34.2 32 33.8 32.5 33.5 32.8 32.8 33.5 32.1 34.2 31.4 34.9 30.7 35.6 29.9 36.2 29.1 36.8 28.2 37.3 27.9 37.5 27.4 37.8 26.4 38.2 25.5 38.5 24.5 38.7 23.5 38.9 22.5 39 21.5 39 20.5 38.9 20.4 38.9 19.5 38.8 18.6 38.6 17.6 38.3 16.7 37.9 15.8 37.5 14.9 37 14.1 36.4 13.4 35.8 13.3 35.8 12.6 35.1 12 34.3 11.3 33.5 10.8 32.7 10.3 31.8 9.9 30.9 9.6 30 9.4 29.3 9.3 29 9.1 28 9 27 9 26 9 25 9.2 24 9.4 23 9.6 22.1 9.8 21.7 10 21.1 10.4 20.2 10.9 19.4 11.4 18.5 12.1 17.8 12.7 17 13.4 16.3 14.2 15.6 14.2 15.5 14.9 14.9 15.6 14.2 16.3 13.5Z;
|
||||
M33.5 11.3 34.3 11.9 35 12.6 35.3 12.8 35.7 13.3 36.4 14.1 36.9 14.9 37.4 15.8 37.9 16.6 38.3 17.6 38.3 17.7 38.6 18.5 38.8 19.5 38.9 20.5 39 21.5 39 22.5 38.9 23.5 38.9 23.5 38.7 24.5 38.5 25.4 38.2 26.4 37.8 27.3 37.4 28.2 36.9 28.9 36.8 29.1 36.2 29.9 35.6 30.6 34.9 31.3 34.2 32 33.5 32.8 33.1 33.2 32.8 33.5 32.1 34.2 31.4 34.9 30.7 35.6 29.9 36.2 29.1 36.8 28.7 37 28.2 37.3 27.4 37.8 26.4 38.2 25.5 38.5 24.5 38.7 23.5 38.9 23.3 38.9 22.5 39 21.5 39 20.5 38.9 19.5 38.8 18.6 38.6 17.6 38.3 17.6 38.3 16.7 37.9 15.8 37.5 14.9 37 14.1 36.4 13.3 35.8 12.7 35.2 12.6 35.1 12 34.3 11.3 33.5 10.8 32.7 10.3 31.8 9.9 30.9 9.7 30.3 9.6 29.9 9.3 29 9.1 28 9 27 9 26 9 25 9.1 24.5 9.2 24 9.4 23 9.6 22.1 10 21.1 10.4 20.2 10.9 19.4 11.1 19.1 11.5 18.5 12.1 17.7 12.7 17 13.5 16.3 14.2 15.6 14.9 14.9 14.9 14.8 15.6 14.2 16.3 13.5 17 12.8 17.7 12.1 18.5 11.5 19.3 11 19.3 10.9 20.2 10.4 21.1 10 22 9.7 23 9.4 24 9.2 24.7 9.1 25 9 26 9 27 9 28 9.1 28.9 9.3 29.9 9.6 30.4 9.7 30.9 9.9 31.8 10.3 32.6 10.8Z;
|
||||
M33.2 11.1 34.2 11.2 35.1 11.4 36 11.9 36.6 12.7 36.8 13.7 36.9 14.7 36.9 15.7 37 16.7 37.1 17.7 37.3 18.6 37.9 19.4 38.5 20.2 39.2 20.9 39.8 21.7 40.5 22.4 40.9 23.3 41 24.3 40.7 25.2 40.1 26 39.4 26.8 38.8 27.5 38.1 28.3 37.5 29.1 37.1 30 37 31 37 31.9 36.9 32.9 36.8 33.9 36.7 34.9 36.2 35.8 35.5 36.4 34.5 36.8 33.6 36.8 32.6 36.9 31.6 37 30.6 37.1 29.6 37.2 28.8 37.7 28 38.4 27.3 39 26.5 39.7 25.8 40.3 24.9 40.8 23.9 41 23 40.8 22.1 40.2 21.4 39.6 20.6 38.9 19.9 38.3 19.1 37.6 18.3 37.2 17.3 37 16.3 37 15.3 36.9 14.3 36.8 13.3 36.7 12.4 36.4 11.7 35.7 11.3 34.8 11.2 33.8 11.1 32.8 11 31.8 11 30.8 10.9 29.9 10.4 29 9.8 28.2 9.1 27.5 8.5 26.7 7.8 26 7.3 25.1 7 24.2 7.1 23.2 7.6 22.3 8.2 21.6 8.9 20.8 9.5 20.1 10.2 19.3 10.7 18.5 10.9 17.5 11 16.6 11.1 15.6 11.1 14.6 11.2 13.6 11.5 12.6 12.1 11.9 13 11.4 13.9 11.2 14.9 11.1 15.9 11 16.9 11 17.9 10.9 18.8 10.6 19.6 10 20.4 9.3 21.1 8.6 21.9 8 22.6 7.4 23.6 7 24.6 7.1 25.5 7.5 26.2 8.1 27 8.7 27.7 9.4 28.5 10 29.3 10.6 30.2 10.9 31.2 11 32.2 11.1Z;
|
||||
M27.7 9.4 28.5 10 29.3 10.6 30.2 10.9 31.2 11 32.2 11.1 33.2 11.1 34.2 11.2 35.1 11.4 36 11.9 36.6 12.7 36.8 13.7 36.9 14.7 36.9 15.7 37 16.7 37.1 17.7 37.3 18.6 37.9 19.4 38.5 20.2 39.2 20.9 39.8 21.7 40.5 22.4 40.9 23.3 41 24.3 40.7 25.2 40.1 26 39.4 26.8 38.8 27.5 38.1 28.3 37.5 29.1 37.1 30 37 31 37 31.9 36.9 32.9 36.8 33.9 36.7 34.9 36.2 35.8 35.5 36.4 34.5 36.8 33.6 36.9 32.6 36.9 31.6 37 30.6 37.1 29.6 37.2 28.8 37.7 28 38.4 27.3 39 26.5 39.7 25.8 40.3 24.9 40.8 23.9 41 23 40.8 22.1 40.2 21.4 39.6 20.6 38.9 19.9 38.3 19.1 37.6 18.3 37.2 17.3 37 16.3 37 15.3 36.9 14.3 36.8 13.3 36.7 12.4 36.4 11.7 35.7 11.3 34.8 11.2 33.8 11.1 32.8 11 31.8 11 30.8 10.9 29.9 10.4 29 9.8 28.2 9.1 27.5 8.5 26.7 7.8 26 7.3 25.1 7 24.2 7.1 23.2 7.6 22.3 8.2 21.6 8.9 20.8 9.5 20.1 10.2 19.3 10.7 18.5 10.9 17.5 11 16.5 11.1 15.6 11.1 14.6 11.2 13.6 11.5 12.6 12.1 11.9 13 11.4 13.9 11.2 14.9 11.1 15.9 11 16.9 11 17.9 10.9 18.8 10.6 19.6 10 20.4 9.3 21.1 8.6 21.9 8 22.6 7.4 23.6 7 24.6 7.1 25.5 7.5 26.2 8.1 27 8.7Z;
|
||||
M27.9 10.6 28.8 10.3 29.8 10.1 30.8 10 31.8 10.1 32.7 10.3 33.7 10.6 34.6 11.1 34.8 11.3 35.4 11.7 36.1 12.4 36.7 13.1 37.2 14 37.6 14.9 37.9 15.9 38 16.9 38 17.9 37.8 18.8 37.5 19.8 37.1 20.7 36.8 21.6 36.5 22.6 36.4 23.6 36.4 24.4 36.4 24.6 36.5 25.5 36.8 26.5 37.2 27.4 37.6 28.3 37.8 29.3 38 30.3 38 31.3 37.8 32.3 37.6 33.2 37.2 34.1 36.6 35 36 35.7 35.3 36.4 34.4 37 34.1 37.2 33.6 37.4 32.6 37.8 31.6 37.9 30.6 38 29.7 37.9 28.7 37.7 27.8 37.3 26.8 36.9 25.9 36.6 24.9 36.4 23.9 36.4 22.9 36.4 22 36.6 21 37 20.1 37.4 20.1 37.4 19.2 37.7 18.2 37.9 17.2 38 16.2 37.9 15.3 37.7 14.3 37.4 13.4 36.9 12.6 36.3 11.9 35.6 11.3 34.9 10.8 34 10.4 33.1 10.1 32.1 10 31.1 10 30.6 10 30.1 10.2 29.2 10.5 28.2 10.9 27.3 11.2 26.4 11.5 25.4 11.6 24.4 11.6 23.4 11.5 22.5 11.2 21.5 10.8 20.6 10.4 19.7 10.2 18.7 10 17.7 10 16.7 10 16.6 10.2 15.7 10.4 14.8 10.8 13.9 11.4 13 12 12.3 12.7 11.6 13.6 11 14.4 10.6 15.4 10.2 16.4 10.1 17.4 10 18.3 10.1 19.3 10.3 20.2 10.7 20.9 11 21.2 11.1 22.1 11.4 23.1 11.6 24.1 11.6 25.1 11.6 26 11.4 27 11Z;
|
||||
M36 35.7 35.3 36.4 34.4 37 33.6 37.4 32.6 37.8 31.6 37.9 30.6 38 29.7 37.9 28.7 37.7 27.8 37.3 26.8 36.9 25.9 36.6 24.9 36.4 23.9 36.4 22.9 36.4 22 36.6 21 37 20.1 37.4 19.2 37.7 18.2 37.9 17.2 38 16.2 37.9 15.3 37.7 14.3 37.4 13.4 36.9 12.6 36.3 11.9 35.6 11.3 34.9 10.8 34 10.4 33.1 10.1 32.1 10 31.1 10 30.2 10.2 29.2 10.5 28.2 10.9 27.3 11.2 26.4 11.5 25.4 11.6 24.4 11.6 23.4 11.5 22.5 11.2 21.5 10.8 20.6 10.4 19.7 10.2 18.7 10 17.7 10 16.7 10.2 15.7 10.4 14.8 10.8 13.9 11.4 13 12 12.3 12.7 11.6 13.6 11 14.4 10.6 15.4 10.2 16.4 10.1 17.4 10 18.3 10.1 19.3 10.3 20.2 10.7 21.2 11.1 22.1 11.4 23.1 11.6 24.1 11.6 25.1 11.6 26 11.4 27 11 27.9 10.6 28.8 10.3 29.8 10.1 30.8 10 31.8 10.1 32.7 10.3 33.7 10.6 34.6 11.1 35.4 11.7 36.1 12.4 36.7 13.1 37.2 14 37.6 14.9 37.9 15.9 38 16.9 38 17.8 37.8 18.8 37.5 19.8 37.1 20.7 36.8 21.6 36.5 22.6 36.4 23.6 36.4 24.6 36.5 25.5 36.8 26.5 37.2 27.4 37.6 28.3 37.8 29.3 38 30.3 38 31.3 37.8 32.3 37.6 33.2 37.2 34.1 36.6 35Z;
|
||||
M32.1 32.1 31.4 32.8 30.7 33.5 29.9 34.1 29.1 34.7 28.3 35.3 27.6 35.8 27.5 35.8 26.6 36.4 25.8 36.8 24.9 37.3 24 37.7 23.1 38 22.1 38.3 21.2 38.6 20.2 38.8 19.2 38.9 18.2 39 17.2 39 16.6 38.9 16.3 38.9 15.3 38.7 14.3 38.4 13.4 38 12.5 37.5 11.7 36.9 11.1 36.3 10.5 35.5 10 34.6 9.6 33.7 9.3 32.7 9.1 31.7 9.1 31.4 9 30.8 9 29.8 9.1 28.8 9.2 27.8 9.4 26.8 9.7 25.9 10 24.9 10.3 24 10.7 23.1 11.2 22.2 11.6 21.4 12.2 20.5 12.2 20.4 12.7 19.7 13.3 18.9 13.9 18.1 14.5 17.3 15.2 16.6 15.9 15.9 16.6 15.2 17.3 14.5 18.1 13.9 18.9 13.3 19.7 12.7 20.4 12.2 20.5 12.2 21.4 11.6 22.2 11.2 23.1 10.7 24 10.3 24.9 10 25.9 9.7 26.8 9.4 27.8 9.2 28.8 9.1 29.8 9 30.8 9 31.4 9.1 31.7 9.1 32.7 9.3 33.7 9.6 34.6 10 35.5 10.5 36.3 11.1 36.9 11.7 37.5 12.5 38 13.4 38.4 14.3 38.7 15.3 38.9 16.3 38.9 16.6 39 17.2 39 18.2 38.9 19.2 38.8 20.2 38.6 21.2 38.3 22.1 38 23.1 37.7 24 37.3 24.9 36.8 25.8 36.4 26.6 35.8 27.5 35.8 27.6 35.3 28.3 34.7 29.1 34.1 29.9 33.5 30.7 32.8 31.4Z;
|
||||
M24.3 10.2 24.9 10 25.9 9.7 26.8 9.4 27.1 9.4 27.8 9.2 28.8 9.1 29.8 9 29.9 9 30.8 9 31.7 9.1 32.7 9.3 32.8 9.3 33.7 9.6 34.6 10 35.5 10.5 35.5 10.5 36.3 11.1 36.9 11.7 37.5 12.5 37.5 12.5 38 13.4 38.4 14.3 38.7 15.2 38.7 15.3 38.9 16.3 39 17.2 39 18.1 39 18.2 38.9 19.2 38.8 20.2 38.6 20.9 38.6 21.2 38.3 22.1 38 23.1 37.8 23.7 37.7 24 37.3 24.9 36.8 25.8 36.5 26.4 36.4 26.6 35.8 27.5 35.3 28.3 35 28.8 34.7 29.1 34.1 29.9 33.5 30.7 33.1 31.1 32.8 31.4 32.1 32.1 31.4 32.8 31.1 33.1 30.7 33.5 29.9 34.1 29.1 34.7 28.8 35 28.3 35.3 27.5 35.8 26.6 36.4 26.4 36.5 25.8 36.8 24.9 37.3 24 37.7 23.7 37.8 23.1 38 22.1 38.3 21.2 38.6 20.9 38.6 20.2 38.8 19.2 38.9 18.2 39 18.1 39 17.2 39 16.3 38.9 15.3 38.7 15.2 38.7 14.3 38.4 13.4 38 12.5 37.5 12.5 37.5 11.7 36.9 11.1 36.3 10.5 35.5 10.5 35.5 10 34.6 9.6 33.7 9.3 32.8 9.3 32.7 9.1 31.7 9 30.8 9 29.9 9 29.8 9.1 28.8 9.2 27.8 9.4 27.1 9.4 26.8 9.7 25.9 10 24.9 10.2 24.3 10.3 24 10.7 23.1 11.2 22.2 11.5 21.6 11.6 21.4 12.2 20.5 12.7 19.7 13 19.2 13.3 18.9 13.9 18.1 14.5 17.3 14.9 16.9 15.2 16.6 15.9 15.9 16.6 15.2 16.9 14.9 17.3 14.5 18.1 13.9 18.9 13.3 19.2 13 19.7 12.7 20.5 12.2 21.4 11.6 21.6 11.5 22.2 11.2 23.1 10.7 24 10.3Z;
|
||||
M22.5 7.8 23.2 7.2 24.2 7 25.1 7.4 25.7 8.1 26.2 9 26.8 9.8 27.3 10.6 28.1 11.2 29 11.3 30 11 30.9 10.6 31.8 10.3 32.8 9.9 33.7 10 34.5 10.6 34.9 11.5 34.8 12.5 34.8 13.5 34.7 14.5 34.7 15.5 35.2 16.3 36 16.8 37 17 37.9 17.3 38.9 17.5 39.8 17.9 40.4 18.7 40.5 19.7 40 20.5 39.3 21.3 38.7 22 38.1 22.8 37.6 23.7 37.7 24.6 38.3 25.4 38.9 26.2 39.6 27 40.2 27.7 40.5 28.6 40.3 29.6 39.5 30.3 38.6 30.6 37.6 30.8 36.7 31 35.7 31.3 35 31.9 34.6 32.8 34.7 33.8 34.8 34.8 34.8 35.8 34.8 36.8 34.3 37.6 33.4 38.1 32.4 38 31.5 37.6 30.6 37.2 29.7 36.9 28.7 36.6 27.8 36.9 27.1 37.6 26.6 38.5 26.1 39.3 25.5 40.2 24.8 40.8 23.8 41 22.9 40.6 22.3 39.9 21.8 39 21.2 38.2 20.7 37.4 19.9 36.8 19 36.7 18 37 17.1 37.4 16.2 37.7 15.2 38.1 14.3 38 13.5 37.4 13.1 36.5 13.2 35.5 13.2 34.5 13.3 33.5 13.3 32.5 12.8 31.7 12 31.2 11 31 10.1 30.7 9.1 30.5 8.2 30.1 7.6 29.3 7.5 28.3 8 27.5 8.7 26.7 9.3 26 9.9 25.2 10.4 24.3 10.3 23.4 9.7 22.6 9.1 21.8 8.4 21 7.8 20.3 7.5 19.4 7.7 18.4 8.5 17.7 9.4 17.4 10.4 17.2 11.3 17 12.3 16.7 13 16.1 13.4 15.2 13.3 14.2 13.2 13.2 13.2 12.2 13.2 11.2 13.7 10.4 14.6 9.9 15.6 10 16.5 10.4 17.4 10.8 18.3 11.1 19.3 11.4 20.2 11.1 20.9 10.4 21.4 9.5 21.9 8.7Z"></animate>
|
||||
<animateTransform attributeName="transform" attributeType="XML" type="rotate" dur="5s" repeatCount="indefinite" calcMode="spline" keySplines="0.5 0.2 0 0.8; 0.5 0.2 0 0.8; 0.5 0.2 0 0.8; 0.5 0.2 0 0.8; 0.5 0.2 0 0.8; 0.5 0.2 0 0.8; 0.5 0.2 0 0.8" keyTimes="0; 0.14; 0.29; 0.43; 0.57; 0.71; 0.86; 1" values="0 24 24; 154 24 24; 309 24 24; 463 24 24; 617 24 24; 771 24 24; 926 24 24; 1080 24 24"></animateTransform>
|
||||
</path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
Binary file not shown.
BIN
Tesses.YouTubeDownloader.Server/res/material-symbols-sharp.woff2
Normal file
BIN
Tesses.YouTubeDownloader.Server/res/material-symbols-sharp.woff2
Normal file
Binary file not shown.
Binary file not shown.
78
Tesses.YouTubeDownloader.Server/res/theme.css
Normal file
78
Tesses.YouTubeDownloader.Server/res/theme.css
Normal file
@@ -0,0 +1,78 @@
|
||||
:root,
|
||||
body.light {
|
||||
--primary:#c00100;
|
||||
--on-primary:#ffffff;
|
||||
--primary-container:#ffdad4;
|
||||
--on-primary-container:#410000;
|
||||
--secondary:#775651;
|
||||
--on-secondary:#ffffff;
|
||||
--secondary-container:#ffdad4;
|
||||
--on-secondary-container:#2c1512;
|
||||
--tertiary:#705c2e;
|
||||
--on-tertiary:#ffffff;
|
||||
--tertiary-container:#fbdfa6;
|
||||
--on-tertiary-container:#251a00;
|
||||
--error:#ba1a1a;
|
||||
--on-error:#ffffff;
|
||||
--error-container:#ffdad6;
|
||||
--on-error-container:#410002;
|
||||
--background:#fffbff;
|
||||
--on-background:#201a19;
|
||||
--surface:#fff8f6;
|
||||
--on-surface:#201a19;
|
||||
--surface-variant:#f5ddda;
|
||||
--on-surface-variant:#534341;
|
||||
--outline:#857370;
|
||||
--outline-variant:#d8c2be;
|
||||
--shadow:#000000;
|
||||
--scrim:#000000;
|
||||
--inverse-surface:#362f2e;
|
||||
--inverse-on-surface:#fbeeec;
|
||||
--inverse-primary:#ffb4a8;
|
||||
--surface-dim:#e4d7d5;
|
||||
--surface-bright:#fff8f6;
|
||||
--surface-container-lowest:#ffffff;
|
||||
--surface-container-low:#fef1ee;
|
||||
--surface-container:#f8ebe9;
|
||||
--surface-container-high:#f3e5e3;
|
||||
--surface-container-highest:#ede0dd;
|
||||
}
|
||||
|
||||
body.dark {
|
||||
--primary:#ffb4a8;
|
||||
--on-primary:#690100;
|
||||
--primary-container:#930100;
|
||||
--on-primary-container:#ffdad4;
|
||||
--secondary:#e7bdb6;
|
||||
--on-secondary:#442925;
|
||||
--secondary-container:#5d3f3b;
|
||||
--on-secondary-container:#ffdad4;
|
||||
--tertiary:#dec48c;
|
||||
--on-tertiary:#3e2e04;
|
||||
--tertiary-container:#564419;
|
||||
--on-tertiary-container:#fbdfa6;
|
||||
--error:#ffb4ab;
|
||||
--on-error:#690005;
|
||||
--error-container:#93000a;
|
||||
--on-error-container:#ffb4ab;
|
||||
--background:#201a19;
|
||||
--on-background:#ede0dd;
|
||||
--surface:#181211;
|
||||
--on-surface:#ede0dd;
|
||||
--surface-variant:#534341;
|
||||
--on-surface-variant:#d8c2be;
|
||||
--outline:#a08c89;
|
||||
--outline-variant:#534341;
|
||||
--shadow:#000000;
|
||||
--scrim:#000000;
|
||||
--inverse-surface:#ede0dd;
|
||||
--inverse-on-surface:#362f2e;
|
||||
--inverse-primary:#c00100;
|
||||
--surface-dim:#181211;
|
||||
--surface-bright:#3f3736;
|
||||
--surface-container-lowest:#120d0c;
|
||||
--surface-container-low:#201a19;
|
||||
--surface-container:#251e1d;
|
||||
--surface-container-high:#2f2827;
|
||||
--surface-container-highest:#3b3332;
|
||||
}
|
||||
66
Tesses.YouTubeDownloader.Server/res/tytd.svg
Normal file
66
Tesses.YouTubeDownloader.Server/res/tytd.svg
Normal file
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="200mm"
|
||||
height="200mm"
|
||||
viewBox="0 0 200 200"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
sodipodi:docname="tytd.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.1438552"
|
||||
inkscape:cx="396.90338"
|
||||
inkscape:cy="561.25985"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1528"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:#ff0000;stroke-width:0.224175"
|
||||
id="rect113"
|
||||
width="120"
|
||||
height="84"
|
||||
x="40"
|
||||
y="58"
|
||||
ry="18.792341" />
|
||||
<path
|
||||
style="display:inline;fill:#00AA00;fill-rule:evenodd;stroke:#000000;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M
|
||||
100 140
|
||||
130 106
|
||||
105 106
|
||||
105 60
|
||||
95 60
|
||||
95 106
|
||||
70 106
|
||||
100 140
|
||||
"
|
||||
id="path510"
|
||||
inkscape:connector-type="polyline"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1,38 @@
|
||||
func Components.AddToPersonalList(tytd,id)
|
||||
{
|
||||
|
||||
return <form hx-post="./add-to-list" hx-swap="outerHTML">
|
||||
<input type="hidden" name="id" value={id}>
|
||||
<div class="row">
|
||||
<div class="max">
|
||||
<div class="field label suffix border fill">
|
||||
<select name="name">
|
||||
<option value="" selected>Create personal list</option>
|
||||
<each(var item : tytd.GetPersonalLists())>
|
||||
<option value={item.name}>Add to {item.name}</option>
|
||||
</each>
|
||||
</select>
|
||||
<label>Personal List</label>
|
||||
<i>arrow_drop_down</i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button name="action" value="add"><i>add</i></button>
|
||||
</div>
|
||||
</form>;
|
||||
}
|
||||
|
||||
func Components.CreateNewList(tytd,id)
|
||||
{
|
||||
return <form hx-post="./add-to-list" hx-swap="outerHTML">
|
||||
<input type="hidden" name="id" value={id}>
|
||||
<div class="row">
|
||||
<div class="field label border max">
|
||||
<input type="text" name="name">
|
||||
<label>List Name</label>
|
||||
</div>
|
||||
|
||||
<button name="action" value="add"><i>add</i></button>
|
||||
</div>
|
||||
</form>;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
func Components.AddVideo(id)
|
||||
{
|
||||
return <form hx-post="./add-video" hx-swap="outerHTML">
|
||||
<input type="hidden" name="url" value={id}>
|
||||
<div class="row">
|
||||
<div class="max">
|
||||
<div class="field label suffix border fill">
|
||||
<select name="res">
|
||||
<each(var item : Resolution.Resolutions)>
|
||||
<if(item.default)>
|
||||
<true>
|
||||
<option value={item.value} selected>{item.name}</option>
|
||||
</true>
|
||||
<false>
|
||||
<option value={item.value}>{item.name}</option>
|
||||
</false>
|
||||
</if>
|
||||
</each>
|
||||
</select>
|
||||
<label>Resolution</label>
|
||||
<i>arrow_drop_down</i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button name="action" value="add"><i>add</i></button>
|
||||
<button name="action" value="download"><i>download</i></button>
|
||||
<button name="action" value="play"><i>play_arrow</i></button>
|
||||
</div>
|
||||
</form>;
|
||||
}
|
||||
33
Tesses.YouTubeDownloader.Server/src/components/add.tcross
Normal file
33
Tesses.YouTubeDownloader.Server/src/components/add.tcross
Normal file
@@ -0,0 +1,33 @@
|
||||
func Components.Add()
|
||||
{
|
||||
return <form hx-post="./add" hx-swap="outerHTML">
|
||||
<div class="field border fill">
|
||||
<input type="text" name="url">
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="max">
|
||||
<div class="field label suffix border fill">
|
||||
<select name="res">
|
||||
<each(var item : Resolution.Resolutions)>
|
||||
<if(item.default)>
|
||||
<true>
|
||||
<option value={item.value} selected>{item.name}</option>
|
||||
</true>
|
||||
<false>
|
||||
<option value={item.value}>{item.name}</option>
|
||||
</false>
|
||||
</if>
|
||||
</each>
|
||||
</select>
|
||||
<label>Resolution</label>
|
||||
<i>arrow_drop_down</i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button name="action" value="add"><i>add</i></button>
|
||||
<button name="action" value="info"><i>info</i></button>
|
||||
<button name="action" value="download"><i>download</i></button>
|
||||
<button name="action" value="play"><i>play_arrow</i></button>
|
||||
</div>
|
||||
</form>;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
func Components.DiscoverEntry(item)
|
||||
{
|
||||
switch(item.type)
|
||||
{
|
||||
case "video":
|
||||
return
|
||||
<div class="row">
|
||||
<div class="min">
|
||||
<img src={$"./video-thumbnail?v={Net.Http.UrlEncode(item.id)}"} width="144" width="120">
|
||||
</div>
|
||||
<div class="max">
|
||||
<div class="col">
|
||||
<div class="min">
|
||||
<a target="_blank" href={$"./video?v={Net.Http.UrlEncode(item.id)}"}>{item.title}</a>
|
||||
</div>
|
||||
<div class="min">
|
||||
<span>{item.views} • {item.uploaded}</span>
|
||||
</div>
|
||||
<div class="min">
|
||||
<each(var part : item.author)>
|
||||
<if(TypeOf(part.navigationEndpoint) == "Dictionary")>
|
||||
<true>
|
||||
<a target="_blank" href={$"./channel?id={Net.Http.UrlEncode(part.navigationEndpoint.browseEndpoint.browseId)}"}>{part.text}</a>
|
||||
|
||||
</true>
|
||||
<false>
|
||||
<span>{part.text}</span>
|
||||
</false>
|
||||
</if>
|
||||
</each>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
break;
|
||||
case "channel":
|
||||
return
|
||||
<div class="row">
|
||||
<div class="min">
|
||||
<img src={$"./channel-thumbnail?id={Net.Http.UrlEncode(item.id)}"} width="144" width="144">
|
||||
</div>
|
||||
<div class="max">
|
||||
<div class="col">
|
||||
<div class="min">
|
||||
<a target="_blank" href={$"./channel?id={Net.Http.UrlEncode(item.id)}"}>{item.title}</a>
|
||||
</div>
|
||||
<div class="min">
|
||||
<span>{item.subs}</span>
|
||||
</div>
|
||||
<div class ="min">
|
||||
<p class="min">{item.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>;
|
||||
break;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
func Components.DownloadedVideo(item)
|
||||
{
|
||||
return
|
||||
<div class="row">
|
||||
<div class="min">
|
||||
<img src={$"./api/v1/video-thumbnail?v={Net.Http.UrlEncode(item.videoId)}"} width="144" width="120">
|
||||
</div>
|
||||
<div class="max">
|
||||
<div class="col">
|
||||
<div class="min">
|
||||
<a hx-get={$"./video?v={Net.Http.UrlEncode(item.videoId)}"} hx-target="body" hx-push-url="true" href={$"./video?v={Net.Http.UrlEncode(item.videoId)}"}>{item.title}</a>
|
||||
</div>
|
||||
<div class="min">
|
||||
<span>{item.viewCountStr} (when downloaded)</span>
|
||||
</div>
|
||||
<div class="min">
|
||||
<a hx-get={$"./channel?id={Net.Http.UrlEncode(item.channelId)}"} href={$"./channel?id={Net.Http.UrlEncode(item.channelId)}"} hx-target="body" hx-push-url="true">{item.author}</a>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
|
||||
|
||||
}
|
||||
func Components.DownloadedPlaylist(item)
|
||||
{
|
||||
var res = <div class="row">
|
||||
<div class="min">
|
||||
<img src={$"./api/v1/playlist-thumbnail?id={Net.Http.UrlEncode(item.playlistId)}"} width="144" width="120">
|
||||
</div>
|
||||
<div class="max">
|
||||
<div class="col">
|
||||
<div class="min">
|
||||
<a hx-get={$"./playlist?id={Net.Http.UrlEncode(item.playlistId)}"} href={$"./playlist?id={Net.Http.UrlEncode(item.playlistId)}"} hx-target="body" hx-push-url="true">{item.title}</a>
|
||||
</div>
|
||||
<div class="min">
|
||||
<a hx-get={$"./channel?id={Net.Http.UrlEncode(item.channelId)}"} href={$"./channel?id={Net.Http.UrlEncode(item.channelId)}"} hx-target="body" hx-push-url="true">{item.channelTitle}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
return res;
|
||||
|
||||
}
|
||||
func Components.DownloadedChannel(item)
|
||||
{
|
||||
var res = <div class="row">
|
||||
<div class="min">
|
||||
<img src={$"./api/v1/channel-thumbnail?id={Net.Http.UrlEncode(item.channelId)}"} width="144" width="120">
|
||||
</div>
|
||||
<div class="max">
|
||||
<a hx-get={$"./channel?id={Net.Http.UrlEncode(item.channelId)}"} href={$"./channel?id={Net.Http.UrlEncode(item.channelId)}"} hx-target="body" hx-push-url="true">{item.title}</a>
|
||||
</div>
|
||||
</div>;
|
||||
return res;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
func Components.MusicAlbum(item)
|
||||
{
|
||||
return <div class="row">
|
||||
<div class="min">
|
||||
<img src={$"./album-art?id={Net.Http.UrlEncode(item.id)}"} width="200" height="200">
|
||||
</div>
|
||||
<div class="min">
|
||||
<a href={$"./music-album?id={Net.Http.UrlEncode(item.id)}"}>{item.title}</a>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
func Components.MusicArtist(item)
|
||||
{
|
||||
return <a href={$"./music-artist?id={Net.Http.UrlEncode(item.id)}"}>{item.name}</a>;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
func Components.PackageItem(tytd,item)
|
||||
{
|
||||
|
||||
var html = <div class="row">
|
||||
<div class="min">
|
||||
<img width="64" height="64" src={item.thumb} alt="Package thumbnail">
|
||||
</div>
|
||||
<div class="max">
|
||||
<div class="col">
|
||||
<div class="min">
|
||||
<a target="_blank" href={item.url}>{item.name}</a>
|
||||
</div>
|
||||
<div class="min">
|
||||
<span>{item.version}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="min">
|
||||
<raw(Components.InstallButton(tytd,item.name,item.version))>
|
||||
</div>
|
||||
</div>;
|
||||
|
||||
return html;
|
||||
}
|
||||
func Components.InstallButton(tytd, name, version, $confirm)
|
||||
{
|
||||
var id = Crypto.Base64Encode(new ByteArray($"{name}-{version}")).Replace("=","");
|
||||
|
||||
var version = Version.Parse(version);
|
||||
|
||||
if(version != null)
|
||||
{
|
||||
|
||||
var state = tytd.PackageState(name,version);
|
||||
return <div id={id}>
|
||||
<if(state == 0)>
|
||||
<true>
|
||||
<button hx-post="./package-manage" hx-vals={Json.Encode({action="install",name=name,version=version.ToString()})} hx-target={$"#{id}"} hx-swap="outerHTML">Install</button>
|
||||
</true>
|
||||
<false>
|
||||
<if(confirm)>
|
||||
<true>
|
||||
<div class="row">
|
||||
<div class="min">
|
||||
<button hx-post="./package-manage" hx-vals={Json.Encode({action="confirm",name=name,version=version.ToString()})} hx-target={$"#{id}"} hx-swap="outerHTML">Yes Uninstall</button>
|
||||
</div>
|
||||
<div class="min">
|
||||
<button hx-post="./package-manage" hx-vals={Json.Encode({action="cancel",name=name,version=version.ToString()})} hx-target={$"#{id}"} hx-swap="outerHTML">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</true>
|
||||
<false>
|
||||
<if(state == 1)>
|
||||
<true>
|
||||
<button hx-post="./package-manage" hx-vals={Json.Encode({action="uninstall",name=name,version=version.ToString()})} hx-target={$"#{id}"} hx-swap="outerHTML">Uninstall</button>
|
||||
</true>
|
||||
<false>
|
||||
<nav class="group split">
|
||||
<button hx-post="./package-manage" hx-vals={Json.Encode({action="install",name=name,version=version.ToString()})} hx-target={$"#{id}"} hx-swap="outerHTML" class="left-round">
|
||||
<i>update</i>
|
||||
<span>Update</span>
|
||||
</button>
|
||||
<button hx-post="./package-manage" hx-vals={Json.Encode({action="uninstall",name=name,version=version.ToString()})} hx-target={$"#{id}"} hx-swap="outerHTML" class="right-round square">
|
||||
<i>delete</i>
|
||||
</button>
|
||||
</nav>
|
||||
</false>
|
||||
</if>
|
||||
</false>
|
||||
</if>
|
||||
|
||||
</false>
|
||||
</if>
|
||||
</div>;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
30
Tesses.YouTubeDownloader.Server/src/components/plugin.tcross
Normal file
30
Tesses.YouTubeDownloader.Server/src/components/plugin.tcross
Normal file
@@ -0,0 +1,30 @@
|
||||
func Components.InstalledPlugin(item)
|
||||
{
|
||||
|
||||
|
||||
var html = <div class="row">
|
||||
<div class="min">
|
||||
<img width="64" height="64" src={$"./api/v1/plugin-thumbnail.png?name={Net.Http.UrlEncode(item.pluginName)}"} alt="Package thumbnail">
|
||||
</div>
|
||||
<div class="max">
|
||||
<div class="col">
|
||||
<div class="min">
|
||||
<if(item.pluginObject.Server != undefined && item.pluginObject.Server != null)>
|
||||
<true>
|
||||
<a href={$"./plugin/{Net.Http.UrlPathEncode(item.pluginName)}/"}>{TypeOf(item.info.short_name_pretty) == "String" ? item.info.short_name_pretty : (TypeOf(item.info.short_name) == "String" ? item.info.short_name : item.name)}</a>
|
||||
</true>
|
||||
<false>
|
||||
<span>{TypeOf(item.info.short_name_pretty) == "String" ? item.info.short_name_pretty : (TypeOf(item.info.short_name) == "String" ? item.info.short_name : item.name)}</span>
|
||||
</false>
|
||||
</if>
|
||||
</div>
|
||||
<div class="min">
|
||||
<span>{item.version}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>;
|
||||
|
||||
return html;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
var progress=0;
|
||||
func Components.Progress(tytd)
|
||||
{
|
||||
var vid = tytd.CurrentVideo;
|
||||
|
||||
var html = <div hx-trigger="every 1500ms" hx-indicator="none" hx-get="./progress" hx-swap="outerHTML">
|
||||
<h2>{vid.Title}</h2>
|
||||
<h4>{vid.Channel}</h4>
|
||||
<progress value={tytd.CurrentVideoProgress * 100.0} max="100" min="0"></progress>
|
||||
</div>;
|
||||
progress++;
|
||||
return html;
|
||||
}
|
||||
68
Tesses.YouTubeDownloader.Server/src/components/shell.tcross
Normal file
68
Tesses.YouTubeDownloader.Server/src/components/shell.tcross
Normal file
@@ -0,0 +1,68 @@
|
||||
func Components.Shell(title, html, page, $mypage)
|
||||
{
|
||||
if(TypeOf(mypage) != "Path")
|
||||
mypage = /;
|
||||
|
||||
var index = (/).MakeRelative(mypage).ToString();
|
||||
if(index == "") index = "./";
|
||||
|
||||
var pages = [
|
||||
{
|
||||
text="Home",
|
||||
icon="home",
|
||||
href=index,
|
||||
classStr=""
|
||||
},
|
||||
{
|
||||
text = "Downloads",
|
||||
icon = "download",
|
||||
href=(/"downloads").MakeRelative(mypage).ToString(),
|
||||
classStr=""
|
||||
},
|
||||
{
|
||||
text = "Plugins",
|
||||
icon = "extension",
|
||||
href= (/"plugins").MakeRelative(mypage).ToString(),
|
||||
classStr=""
|
||||
},
|
||||
{
|
||||
text = "Settings",
|
||||
icon = "settings",
|
||||
href= (/"settings").MakeRelative(mypage).ToString(),
|
||||
classStr=""
|
||||
}
|
||||
];
|
||||
pages[page].classStr = "active";
|
||||
return <!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>TYTD - {title}</title>
|
||||
<link rel="stylesheet" href={(/"beer.min.css").MakeRelative(mypage).ToString()}>
|
||||
<link rel="stylesheet" href={(/"theme.css").MakeRelative(mypage).ToString()}>
|
||||
<script src={(/"htmx.min.js").MakeRelative(mypage).ToString()} defer></script>
|
||||
<script type="module" src={(/"beer.min.js").MakeRelative(mypage).ToString()} defer></script>
|
||||
</head>
|
||||
<body hx-indicator="#loading-indicator">
|
||||
|
||||
<nav class="bottom">
|
||||
<each(var page : pages)>
|
||||
<a hx-get={page.href} hx-target="body" hx-push-url="true" class={page.classStr}>
|
||||
<i>{page.icon}</i>
|
||||
<div>{page.text}</div>
|
||||
</a>
|
||||
</each>
|
||||
</nav>
|
||||
|
||||
<main class="responsive">
|
||||
<div class="htmx-indicator shape loading-indicator extra" id="loading-indicator">
|
||||
<img class="responsive" src="./tytd.svg">
|
||||
</div>
|
||||
<raw(html)>
|
||||
</main>
|
||||
|
||||
|
||||
</body>
|
||||
</html>;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
func Components.Subscribe(tytd,id)
|
||||
{
|
||||
var bell = tytd.GetSubscriptionBell(id);
|
||||
var subscribed= bell != null;
|
||||
|
||||
var bellLogo = "notifications_off";
|
||||
switch(bell)
|
||||
{
|
||||
case SubscriptionBell.DownloadLow:
|
||||
case SubscriptionBell.DownloadHigh:
|
||||
bellLogo = "download";
|
||||
break;
|
||||
case SubscriptionBell.Bell:
|
||||
case SubscriptionBell.BellLow:
|
||||
case SubscriptionBell.BellHigh:
|
||||
bellLogo="notifications_active";
|
||||
break;
|
||||
}
|
||||
|
||||
<return>
|
||||
<div id="subscribe-btn">
|
||||
<if(subscribed)>
|
||||
<true>
|
||||
<div class="row">
|
||||
<div class="min">
|
||||
<button hx-post="./subscribe" hx-swap="outerHTML" hx-target="#subscribe-btn" hx-vals={Json.Encode({id,type="unsubscribe"})}>Unsubscribe</button>
|
||||
</div>
|
||||
<div class="min">
|
||||
<button class="circle">
|
||||
<i>{bellLogo}</i>
|
||||
<menu class="left no-wrap">
|
||||
<each(var b : SubscriptionBell.Bells)>
|
||||
<li hx-post="./subscribe" hx-swap="outerHTML" hx-target="#subscribe-btn" hx-vals={Json.Encode({id,type="bell",bell=b.value})}>{b.name}</li>
|
||||
</each>
|
||||
</menu>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</true>
|
||||
<false>
|
||||
<button hx-post="./subscribe" hx-swap="outerHTML" hx-target="#subscribe-btn" hx-vals={Json.Encode({id,type="subscribe"})}>Subscribe</button>
|
||||
</false>
|
||||
</if>
|
||||
</div>
|
||||
</return>
|
||||
}
|
||||
374
Tesses.YouTubeDownloader.Server/src/main.tcross
Normal file
374
Tesses.YouTubeDownloader.Server/src/main.tcross
Normal file
@@ -0,0 +1,374 @@
|
||||
var TYTDResources = [
|
||||
{path="/beer.min.css",value=embed("beer.min.css")},
|
||||
{path="/beer.min.js",value=embed("beer.min.js")},
|
||||
{path="/material-symbols-outlined.woff2",value=embed("material-symbols-outlined.woff2")},
|
||||
{path="/material-symbols-rounded.woff2",value=embed("material-symbols-rounded.woff2")},
|
||||
{path="/material-symbols-sharp.woff2",value=embed("material-symbols-sharp.woff2")},
|
||||
{path="/material-symbols-subset.woff2",value=embed("material-symbols-subset.woff2")},
|
||||
{path="/htmx.min.js",value=embed("htmx.min.js")},
|
||||
{path="/favicon.ico",value=embed("favicon.ico")},
|
||||
{path="/tytd.svg",value=embed("tytd.svg")},
|
||||
{path="/loading-indicator.svg",value=embed("loading-indicator.svg")},
|
||||
{path="/theme.css",value=embed("theme.css")}
|
||||
];
|
||||
var times=1;
|
||||
|
||||
class TYTDApp {
|
||||
private TYTD;
|
||||
|
||||
public TYTDApp()
|
||||
{
|
||||
|
||||
var tytdfs = new SubdirFilesystem(FS.Local, "TYTD");
|
||||
this.TYTD = new TYTD.Downloader(tytdfs,FS.MakeFull("TYTD"));
|
||||
this.TYTD.Start();
|
||||
|
||||
}
|
||||
public Handle(ctx)
|
||||
{
|
||||
if(ctx.Path == "/")
|
||||
{
|
||||
ctx.WithMimeType("text/html").SendText(Pages.Index(this.TYTD));
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path == "/api/v1/download")
|
||||
{
|
||||
var v = ctx.QueryParams.TryGetFirst("v");
|
||||
var inline = ctx.QueryParams.GetFirstBoolean("inline");
|
||||
var res = ctx.QueryParams.TryGetFirst("res");
|
||||
|
||||
if(TypeOf(res) != "String") res = Resolution.LowVideo;
|
||||
|
||||
var path = this.TYTD.GetVideoPath(v,res);
|
||||
|
||||
if(path != null)
|
||||
{
|
||||
var info = this.TYTD.GetVideo(v);
|
||||
var filename = $"{info.title}-{info.videoId}.{path.GetExtension()}";
|
||||
var strm = this.TYTD.Storage.OpenFile(path,"rb");
|
||||
ctx.WithMimeType(Net.Http.MimeType(path.ToString())).WithContentDisposition(filename,inline).SendStream(strm);
|
||||
strm.Close();
|
||||
}
|
||||
}
|
||||
else if(ctx.Path == "/api/v1/playlist.json")
|
||||
{
|
||||
var id = ctx.QueryParams.TryGetFirst("id");
|
||||
var page = ctx.QueryParams.TryGetFirstInt("page");
|
||||
var count = ctx.QueryParams.TryGetFirstInt("count");
|
||||
if(TypeOf(page)!="Long") page = 1;
|
||||
if(TypeOf(count)!="Long") count = 20;
|
||||
if(TypeOf(id)!="String") id = "";
|
||||
ctx.WithMimeType("application/json").SendJson(this.TYTD.GetPlaylistContents(id,page-1,count));
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path == "/api/v1/channel.json")
|
||||
{
|
||||
var id = ctx.QueryParams.TryGetFirst("id");
|
||||
var page = ctx.QueryParams.TryGetFirstInt("page");
|
||||
var count = ctx.QueryParams.TryGetFirstInt("count");
|
||||
if(TypeOf(page)!="Long") page = 1;
|
||||
if(TypeOf(count)!="Long") count = 20;
|
||||
if(TypeOf(id)!="String") id = "";
|
||||
ctx.WithMimeType("application/json").SendJson(this.TYTD.GetChannelContents(id,page-1,count));
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path == "/api/v1/downloads.json")
|
||||
{
|
||||
var q = ctx.QueryParams.TryGetFirst("q");
|
||||
var type = ctx.QueryParams.TryGetFirst("type");
|
||||
var page = ctx.QueryParams.TryGetFirstInt("page");
|
||||
var count = ctx.QueryParams.TryGetFirstInt("count");
|
||||
if(TypeOf(page)!="Long") page = 1;
|
||||
if(TypeOf(count)!="Long") count = 20;
|
||||
if(TypeOf(type)!="String") type = "videos";
|
||||
if(TypeOf(q)!="String") q = "";
|
||||
|
||||
var result = { entries = [] };
|
||||
switch(type)
|
||||
{
|
||||
case "videos":
|
||||
|
||||
result.entries = this.TYTD.GetVideos(q,page-1,count);
|
||||
|
||||
break;
|
||||
case "playlists":
|
||||
result.entries = this.TYTD.GetPlaylists(q,page-1,count);
|
||||
break;
|
||||
case "channels":
|
||||
result.entries = this.TYTD.GetChannels(q,page-1,count);
|
||||
break;
|
||||
|
||||
}
|
||||
ctx.WithMimeType("application/json").SendJson(result);
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path == "/subscribe")
|
||||
{
|
||||
var id = ctx.QueryParams.TryGetFirst("id");
|
||||
var bell = ctx.QueryParams.TryGetFirst("bell");
|
||||
var type = ctx.QueryParams.TryGetFirst("type");
|
||||
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case "subscribe":
|
||||
this.TYTD.SetSubscriptionBell(id, SubscriptionBell.BellLow);
|
||||
|
||||
break;
|
||||
case "unsubscribe":
|
||||
this.TYTD.SetSubscriptionBell(id, null);
|
||||
break;
|
||||
case "bell":
|
||||
this.TYTD.SetSubscriptionBell(id, bell);
|
||||
break;
|
||||
}
|
||||
ctx.WithMimeType("text/html").SendText(Components.Subscribe(this.TYTD,id));
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path == "/downloads")
|
||||
{
|
||||
ctx.WithMimeType("text/html").SendText(Pages.Downloads(this.TYTD,ctx));
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path == "/list")
|
||||
{
|
||||
ctx.WithMimeType("text/html").SendText(Pages.List(this.TYTD,ctx));
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path == "/settings")
|
||||
{
|
||||
if(ctx.Method == "POST")
|
||||
{
|
||||
/*
|
||||
hours
|
||||
minutes
|
||||
seconds
|
||||
tag
|
||||
enablePlugins
|
||||
*/
|
||||
var enablePlugins = ctx.QueryParams.GetFirstBoolean("enablePlugins");
|
||||
var tag = ctx.QueryParams.TryGetFirst("tag");
|
||||
var hours = ctx.QueryParams.TryGetFirstInt("hours");
|
||||
var minutes = ctx.QueryParams.TryGetFirstInt("minutes");
|
||||
var seconds = ctx.QueryParams.TryGetFirstInt("seconds");
|
||||
|
||||
if(TypeOf(tag) == "String" && TypeOf(hours) == "Long" && TypeOf(minutes) == "Long" && TypeOf(seconds) == "Long")
|
||||
{
|
||||
seconds += minutes * 60;
|
||||
seconds += hours * 3600;
|
||||
this.TYTD.Config.TYTDTag = tag;
|
||||
this.TYTD.Config.BellTimer = seconds;
|
||||
this.TYTD.Config.EnablePlugins = enablePlugins;
|
||||
this.TYTD.SaveConfig();
|
||||
}
|
||||
}
|
||||
ctx.WithMimeType("text/html").SendText(Pages.Settings(this.TYTD,ctx));
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path == "/video")
|
||||
{
|
||||
ctx.WithMimeType("text/html").SendText(Pages.VideoInfo(this.TYTD,ctx));
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path == "/playlist")
|
||||
{
|
||||
ctx.WithMimeType("text/html").SendText(Pages.PlaylistInfo(this.TYTD,ctx));
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path == "/channel")
|
||||
{
|
||||
ctx.WithMimeType("text/html").SendText(Pages.ChannelInfo(this.TYTD,ctx));
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path == "/add")
|
||||
{
|
||||
var url = ctx.QueryParams.TryGetFirst("url");
|
||||
var action =ctx.QueryParams.TryGetFirst("action");
|
||||
var res = ctx.QueryParams.TryGetFirst("res");
|
||||
if(action == "add")
|
||||
{
|
||||
this.TYTD.DownloadItem(url,res);
|
||||
}
|
||||
else if(action == "info")
|
||||
{
|
||||
|
||||
ctx.ResponseHeaders.SetValue("HX-Redirect", TYTD.PageRedirect(url));
|
||||
|
||||
|
||||
|
||||
}
|
||||
else if(action == "download")
|
||||
{
|
||||
ctx.ResponseHeaders.SetValue("HX-Redirect", $"./api/v1/download?v={Net.Http.UrlEncode(url)}&res={Net.Http.UrlEncode(res)}");
|
||||
|
||||
}
|
||||
else if(action == "play")
|
||||
{
|
||||
ctx.ResponseHeaders.SetValue("HX-Redirect", $"./api/v1/download?v={Net.Http.UrlEncode(url)}&res={Net.Http.UrlEncode(res)}&inline=true");
|
||||
|
||||
}
|
||||
ctx.WithMimeType("text/html").SendText(Components.Add());
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path == "/add-video")
|
||||
{
|
||||
var url = ctx.QueryParams.TryGetFirst("url");
|
||||
var action =ctx.QueryParams.TryGetFirst("action");
|
||||
var res = ctx.QueryParams.TryGetFirst("res");
|
||||
if(action == "add")
|
||||
{
|
||||
this.TYTD.DownloadItem(url,res);
|
||||
}
|
||||
|
||||
else if(action == "download")
|
||||
{
|
||||
ctx.ResponseHeaders.SetValue("HX-Redirect", $"./api/v1/download?v={Net.Http.UrlEncode(url)}&res={Net.Http.UrlEncode(res)}");
|
||||
|
||||
}
|
||||
else if(action == "play")
|
||||
{
|
||||
ctx.ResponseHeaders.SetValue("HX-Redirect", $"./api/v1/download?v={Net.Http.UrlEncode(url)}&res={Net.Http.UrlEncode(res)}&inline=true");
|
||||
|
||||
}
|
||||
ctx.WithMimeType("text/html").SendText(Components.AddVideo(url));
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path == "/add-to-list")
|
||||
{
|
||||
var id = ctx.QueryParams.TryGetFirst("id");
|
||||
if(TypeOf(id) != "String") id = "";
|
||||
|
||||
var name = ctx.QueryParams.TryGetFirst("name");
|
||||
if(TypeOf(name) != "String") name = "";
|
||||
|
||||
if(name == "")
|
||||
{
|
||||
ctx.WithMimeType("text/html").SendText(Components.CreateNewList(this.TYTD,id));
|
||||
}
|
||||
else {
|
||||
this.TYTD.AddToPersonalList(name,id);
|
||||
ctx.WithMimeType("text/html").SendText(Components.AddToPersonalList(this.TYTD,id));
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path == "/progress")
|
||||
{
|
||||
ctx.WithMimeType("text/html").SendText(Components.Progress(this.TYTD));
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path == "/plugins")
|
||||
{
|
||||
ctx.WithMimeType("text/html").SendText(Pages.Plugins(this.TYTD,ctx));
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path == "/plugins-download")
|
||||
{
|
||||
ctx.WithMimeType("text/html").SendText(Pages.DownloadPlugins(this.TYTD,ctx));
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path == "/package-manage" && ctx.Method == "POST")
|
||||
{
|
||||
var data = {
|
||||
name = ctx.QueryParams.TryGetFirst("name"),
|
||||
version = ctx.QueryParams.TryGetFirst("version"),
|
||||
action = ctx.QueryParams.TryGetFirst("action")
|
||||
};
|
||||
|
||||
var mustConfirm = false;
|
||||
|
||||
|
||||
switch(data.action)
|
||||
{
|
||||
case "install":
|
||||
{
|
||||
var v = Version.Parse(data.version);
|
||||
if(v != null)
|
||||
this.TYTD.PackageInstall(data.name,v);
|
||||
}
|
||||
break;
|
||||
case "uninstall":
|
||||
{
|
||||
mustConfirm = true;
|
||||
}
|
||||
break;
|
||||
case "confirm":
|
||||
{
|
||||
this.TYTD.PackageUninstall(data.name);
|
||||
}
|
||||
break;
|
||||
}
|
||||
ctx.WithMimeType("text/html").SendText(Components.InstallButton(this.TYTD, data.name, data.version, mustConfirm));
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path == "/api/v1/video-thumbnail")
|
||||
{
|
||||
var v = ctx.QueryParams.TryGetFirst("v");
|
||||
var res = ctx.QueryParams.TryGetFirst("res");
|
||||
if(TypeOf(res) != "String") res = "0";
|
||||
var thu = this.TYTD.GetVideoThumbnail(v,res);
|
||||
if(thu == null) return false;
|
||||
ctx.WithMimeType("image/jpg").SendBytes(thu);
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path == "/api/v1/playlist-thumbnail")
|
||||
{
|
||||
var id = ctx.QueryParams.TryGetFirst("id");
|
||||
var res = ctx.QueryParams.TryGetFirst("res");
|
||||
if(TypeOf(res) != "String") res = "0";
|
||||
var thu = this.TYTD.GetPlaylistThumbnail(id,res);
|
||||
if(thu == null) return false;
|
||||
ctx.WithMimeType("image/jpg").SendBytes(thu);
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path == "/api/v1/channel-thumbnail")
|
||||
{
|
||||
var id = ctx.QueryParams.TryGetFirst("id");
|
||||
var res = ctx.QueryParams.TryGetFirst("res");
|
||||
if(TypeOf(res) != "String") res = "0";
|
||||
var thu = this.TYTD.GetChannelThumbnail(id,res);
|
||||
if(thu == null) return false;
|
||||
ctx.WithMimeType("image/jpg").SendBytes(thu);
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path == "/api/v1/plugin-thumbnail.png")
|
||||
{
|
||||
var name = ctx.QueryParams.TryGetFirst("name");
|
||||
ctx.WithMimeType().SendBytes(this.TYTD.GetPluginThumbnail(name));
|
||||
return true;
|
||||
}
|
||||
else if(ctx.Path.StartsWith("/plugin/"))
|
||||
{
|
||||
return this.TYTD.Servers.Handle(ctx);
|
||||
}
|
||||
else {
|
||||
each(var file : TYTDResources)
|
||||
{
|
||||
if(ctx.Path == file.path)
|
||||
{
|
||||
ctx.WithMimeType(Net.Http.MimeType(file.path)).SendBytes(file.value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Close()
|
||||
{
|
||||
this.TYTD.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
func WebAppMain(args)
|
||||
{
|
||||
return new TYTDApp();
|
||||
}
|
||||
func main(args)
|
||||
{
|
||||
var res = WebAppMain(args);
|
||||
Net.Http.ListenSimpleWithLoop(res,3255);
|
||||
res.Close();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
func Pages.ChannelInfo(tytd,ctx)
|
||||
{
|
||||
var id = ctx.QueryParams.TryGetFirst("id");
|
||||
var page = ctx.QueryParams.TryGetFirstInt("page");
|
||||
if(TypeOf(page) != "Long") page = 1;
|
||||
page--;
|
||||
|
||||
var html = <h1>Could not find channel</h1>;
|
||||
var title = "N/A";
|
||||
|
||||
if(TypeOf(id) == "String")
|
||||
{
|
||||
id = TYTD.GetChannelId(id);
|
||||
var res = tytd.GetChannelContents(id,page,20);
|
||||
|
||||
html = <null>
|
||||
<div class="row">
|
||||
<div class="max">
|
||||
<h1>{res.authorName}</h1>
|
||||
</div>
|
||||
<div class="min">
|
||||
<raw(Components.Subscribe(tytd,id))>
|
||||
</div>
|
||||
</div>
|
||||
<each(var item : res.items)>
|
||||
<raw(Components.DownloadedVideo(item))>
|
||||
</each>
|
||||
<footer class="row center-align">
|
||||
|
||||
<button hx-get={$"./channel?id={Net.Http.UrlEncode(id)}&page={page}"} hx-target="body" hx-push-url="true" >Prev</button>{page+1}<button hx-get={$"./channel?id={Net.Http.UrlEncode(id)}&type=videos&page={page+2}"} hx-target="body" hx-push-url="true" >Next</button>
|
||||
|
||||
</footer>
|
||||
</null>;
|
||||
|
||||
title = res.authorName;
|
||||
}
|
||||
|
||||
return Components.Shell($"Channel {title}",html,1);
|
||||
}
|
||||
34
Tesses.YouTubeDownloader.Server/src/pages/discover.tcross
Normal file
34
Tesses.YouTubeDownloader.Server/src/pages/discover.tcross
Normal file
@@ -0,0 +1,34 @@
|
||||
func Pages.Discover(tytd,ctx)
|
||||
{
|
||||
|
||||
var q = ctx.QueryParams.TryGetFirst("q");
|
||||
var res = null;
|
||||
var q2 = "";
|
||||
if(TypeOf(q) == "String")
|
||||
{
|
||||
q2 = q;
|
||||
res = tytd.Discover(q);
|
||||
|
||||
}
|
||||
|
||||
var html = <null>
|
||||
<form hx-get="./discover" hx-target="body" hx-push-url="true">
|
||||
<div class="field large prefix round fill active">
|
||||
<i class="front">search</i>
|
||||
<input name="q" value={q2}>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
<if(res != null)>
|
||||
<true>
|
||||
<ul>
|
||||
<each(var item : res.items)>
|
||||
<li><raw(Components.DiscoverEntry(item))></li>
|
||||
</each>
|
||||
</ul>
|
||||
<button class="responsive">More</button>
|
||||
</true></null>;
|
||||
|
||||
|
||||
return Components.Shell($"Discover {q2}",html,1);
|
||||
}
|
||||
293
Tesses.YouTubeDownloader.Server/src/pages/downloads.tcross
Normal file
293
Tesses.YouTubeDownloader.Server/src/pages/downloads.tcross
Normal file
@@ -0,0 +1,293 @@
|
||||
func Pages.Downloads(tytd,ctx)
|
||||
{
|
||||
var type = ctx.QueryParams.TryGetFirst("type");
|
||||
var q = ctx.QueryParams.TryGetFirst("q");
|
||||
var page = ctx.QueryParams.TryGetFirstInt("page");
|
||||
if(TypeOf(page) != "Long") page = 1;
|
||||
page--;
|
||||
|
||||
if(TypeOf(q) != "String")
|
||||
{
|
||||
q = "";
|
||||
}
|
||||
if(TypeOf(type) != "String")
|
||||
{
|
||||
type = "videos";
|
||||
}
|
||||
var html ="";
|
||||
|
||||
switch(type) {
|
||||
case "videos":
|
||||
{
|
||||
|
||||
var res = tytd.GetVideos(q,page,20);
|
||||
html = <null>
|
||||
<form hx-get="./downloads" hx-target="body" hx-push-url="true" class="s m">
|
||||
<div class="row">
|
||||
<div class="max">
|
||||
<div class="field label suffix border round fill">
|
||||
<select name="type">
|
||||
<option value="videos" selected>Videos</option>
|
||||
<option value="playlists">Playlists</option>
|
||||
<option value="channels">Channels</option>
|
||||
<option value="personal">Personal List</option>
|
||||
</select>
|
||||
<label>Type</label>
|
||||
<i>arrow_drop_down</i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="min">
|
||||
<button>Change</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<nav class="tabbed l">
|
||||
<a class="active" hx-get="./downloads?type=videos" hx-target="body" hx-push-url="true">
|
||||
<i>movie</i>
|
||||
<span>Videos</span>
|
||||
</a>
|
||||
<a hx-get="./downloads?type=playlists" hx-target="body" hx-push-url="true">
|
||||
<i>list</i>
|
||||
<span>Playlists</span>
|
||||
</a>
|
||||
<a hx-get="./downloads?type=channels" hx-target="body" hx-push-url="true">
|
||||
<i>person</i>
|
||||
<span>Channels</span>
|
||||
</a>
|
||||
<a hx-get="./downloads?type=personal" hx-target="body" hx-push-url="true">
|
||||
<i>edit_note</i>
|
||||
<span>Personal Lists</span>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
</nav>
|
||||
<form hx-get="./downloads" hx-target="body" hx-push-url="true">
|
||||
<div class="field large prefix round fill active">
|
||||
<i class="front">search</i>
|
||||
<input name="q" value={q}>
|
||||
<input name="type" type="hidden" value="videos">
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<each(var item : res)>
|
||||
<raw(Components.DownloadedVideo(item))>
|
||||
</each>
|
||||
<footer class="row center-align">
|
||||
|
||||
<button hx-get={$"./downloads?q={Net.Http.UrlEncode(q)}&type=videos&page={page}"} hx-target="body" hx-push-url="true" >Prev</button>{page+1}<button hx-get={$"./downloads?q={Net.Http.UrlEncode(q)}&type=videos&page={page+2}"} hx-target="body" hx-push-url="true" >Next</button>
|
||||
|
||||
</footer>
|
||||
|
||||
</null>;
|
||||
}
|
||||
break;
|
||||
case "playlists":
|
||||
{
|
||||
|
||||
var res = tytd.GetPlaylists(q,page,20);
|
||||
html = <null>
|
||||
<form hx-get="./downloads" hx-target="body" hx-push-url="true" class="s m">
|
||||
<div class="row">
|
||||
<div class="max">
|
||||
<div class="field label suffix border round fill">
|
||||
<select name="type">
|
||||
<option value="videos">Videos</option>
|
||||
<option value="playlists" selected>Playlists</option>
|
||||
<option value="channels">Channels</option>
|
||||
<option value="personal">Personal List</option>
|
||||
|
||||
</select>
|
||||
<label>Type</label>
|
||||
<i>arrow_drop_down</i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="min">
|
||||
<button>Change</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<nav class="tabbed l">
|
||||
<a hx-get="./downloads?type=videos" hx-target="body" hx-push-url="true">
|
||||
<i>movie</i>
|
||||
<span>Videos</span>
|
||||
</a>
|
||||
<a class="active" hx-get="./downloads?type=playlists" hx-target="body" hx-push-url="true">
|
||||
<i>list</i>
|
||||
<span>Playlists</span>
|
||||
</a>
|
||||
<a hx-get="./downloads?type=channels" hx-target="body" hx-push-url="true">
|
||||
<i>person</i>
|
||||
<span>Channels</span>
|
||||
</a>
|
||||
<a hx-get="./downloads?type=personal" hx-target="body" hx-push-url="true">
|
||||
<i>edit_note</i>
|
||||
<span>Personal Lists</span>
|
||||
</a>
|
||||
|
||||
|
||||
</nav>
|
||||
<form hx-get="./downloads" hx-target="body" hx-push-url="true">
|
||||
<div class="field large prefix round fill active">
|
||||
<i class="front">search</i>
|
||||
<input name="q" value={q}>
|
||||
<input name="type" type="hidden" value="playlists">
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<each(var item : res)>
|
||||
<raw(Components.DownloadedPlaylist(item))>
|
||||
</each>
|
||||
<footer class="row center-align">
|
||||
|
||||
<button hx-get={$"./downloads?q={Net.Http.UrlEncode(q)}&type=playlists&page={page}"} hx-target="body" hx-push-url="true" >Prev</button>{page+1}<button hx-get={$"./downloads?q={Net.Http.UrlEncode(q)}&type=playlists&page={page+2}"} hx-target="body" hx-push-url="true" >Next</button>
|
||||
|
||||
</footer>
|
||||
|
||||
</null>;
|
||||
}
|
||||
break;
|
||||
case "channels":
|
||||
{
|
||||
|
||||
var res = tytd.GetChannels(q,page,20);
|
||||
html = <null>
|
||||
<form hx-get="./downloads" hx-target="body" hx-push-url="true" class="s m">
|
||||
<div class="row">
|
||||
<div class="max">
|
||||
<div class="field label suffix border round fill">
|
||||
<select name="type">
|
||||
<option value="videos">Videos</option>
|
||||
<option value="playlists">Playlists</option>
|
||||
<option value="channels" selected>Channels</option>
|
||||
<option value="personal">Personal List</option>
|
||||
</select>
|
||||
<label>Type</label>
|
||||
<i>arrow_drop_down</i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="min">
|
||||
<button>Change</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<nav class="tabbed l">
|
||||
<a hx-get="./downloads?type=videos" hx-target="body" hx-push-url="true">
|
||||
<i>movie</i>
|
||||
<span>Videos</span>
|
||||
</a>
|
||||
<a hx-get="./downloads?type=playlists" hx-target="body" hx-push-url="true">
|
||||
<i>list</i>
|
||||
<span>Playlists</span>
|
||||
</a>
|
||||
<a class="active" hx-get="./downloads?type=channels" hx-target="body" hx-push-url="true">
|
||||
<i>person</i>
|
||||
<span>Channels</span>
|
||||
</a>
|
||||
<a hx-get="./downloads?type=personal" hx-target="body" hx-push-url="true">
|
||||
<i>edit_note</i>
|
||||
<span>Personal Lists</span>
|
||||
</a>
|
||||
|
||||
|
||||
</nav>
|
||||
<form hx-get="./downloads" hx-target="body" hx-push-url="true">
|
||||
<div class="field large prefix round fill active">
|
||||
<i class="front">search</i>
|
||||
<input name="q" value={q}>
|
||||
<input name="type" type="hidden" value="channels">
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<each(var item : res)>
|
||||
<raw(Components.DownloadedChannel(item))>
|
||||
</each>
|
||||
<footer class="row center-align">
|
||||
|
||||
<button hx-get={$"./downloads?q={Net.Http.UrlEncode(q)}&type=channels&page={page}"} hx-target="body" hx-push-url="true" >Prev</button>{page+1}<button hx-get={$"./downloads?q={Net.Http.UrlEncode(q)}&type=channels&page={page+2}"} hx-target="body" hx-push-url="true" >Next</button>
|
||||
|
||||
</footer>
|
||||
|
||||
</null>;
|
||||
}
|
||||
break;
|
||||
case "personal":
|
||||
{
|
||||
var res = tytd.GetPersonalLists();
|
||||
html = <null>
|
||||
<form hx-get="./downloads" hx-target="body" hx-push-url="true" class="s m">
|
||||
<div class="row">
|
||||
<div class="max">
|
||||
<div class="field label suffix border round fill">
|
||||
<select name="type">
|
||||
<option value="videos">Videos</option>
|
||||
<option value="playlists">Playlists</option>
|
||||
<option value="channels">Channels</option>
|
||||
<option value="personal" selected>Personal List</option>
|
||||
</select>
|
||||
<label>Type</label>
|
||||
<i>arrow_drop_down</i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="min">
|
||||
<button>Change</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<nav class="tabbed l">
|
||||
<a hx-get="./downloads?type=videos" hx-target="body" hx-push-url="true">
|
||||
<i>movie</i>
|
||||
<span>Videos</span>
|
||||
</a>
|
||||
<a hx-get="./downloads?type=playlists" hx-target="body" hx-push-url="true">
|
||||
<i>list</i>
|
||||
<span>Playlists</span>
|
||||
</a>
|
||||
<a hx-get="./downloads?type=channels" hx-target="body" hx-push-url="true">
|
||||
<i>person</i>
|
||||
<span>Channels</span>
|
||||
</a>
|
||||
<a class="active" hx-get="./downloads?type=personal" hx-target="body" hx-push-url="true">
|
||||
<i>edit_note</i>
|
||||
<span>Personal Lists</span>
|
||||
</a>
|
||||
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
<each(var item : res)>
|
||||
<if(TypeOf(item.firstVideo) == "String")>
|
||||
<true>
|
||||
<div class="row">
|
||||
<div class="min">
|
||||
<img src={$"./api/v1/video-thumbnail?v={Net.Http.UrlEncode(item.firstVideo)}"} width="144" width="120">
|
||||
</div>
|
||||
<div class="max">
|
||||
<a href={$"./list?name={Net.Http.UrlEncode(item.name)}"}>{item.name}</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</true>
|
||||
<false>
|
||||
<div><a href={$"./list?name={Net.Http.UrlEncode(item.name)}"}>{item.name}</a></div>
|
||||
</false>
|
||||
</if>
|
||||
</each>
|
||||
|
||||
|
||||
</null>;
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return Components.Shell($"Downloads {q}",html,1);
|
||||
}
|
||||
11
Tesses.YouTubeDownloader.Server/src/pages/index.tcross
Normal file
11
Tesses.YouTubeDownloader.Server/src/pages/index.tcross
Normal file
@@ -0,0 +1,11 @@
|
||||
func Pages.Index(tytd)
|
||||
{
|
||||
|
||||
var html = <null>
|
||||
<raw(Components.Add())>
|
||||
<raw(Components.Progress(tytd))>
|
||||
</null>;
|
||||
|
||||
|
||||
return Components.Shell("Home",html,0);
|
||||
}
|
||||
36
Tesses.YouTubeDownloader.Server/src/pages/list.tcross
Normal file
36
Tesses.YouTubeDownloader.Server/src/pages/list.tcross
Normal file
@@ -0,0 +1,36 @@
|
||||
func Pages.List(tytd,ctx)
|
||||
{
|
||||
var name = ctx.QueryParams.TryGetFirst("name");
|
||||
var page = ctx.QueryParams.TryGetFirstInt("page");
|
||||
if(TypeOf(page) != "Long") page = 1;
|
||||
page--;
|
||||
if(TypeOf(name) != "String")
|
||||
{
|
||||
name = "";
|
||||
}
|
||||
|
||||
var res = tytd.GetPersonalListContents(name, page, 20);
|
||||
|
||||
var html = <null>
|
||||
<div class="row">
|
||||
<div class="min">
|
||||
<button hx-get="./downloads?type=personal" hx-target="body" hx-push-url="true"><i>arrow_back</i></button>
|
||||
</div>
|
||||
<div class="max">
|
||||
<h1>{name}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<each(var item : res)>
|
||||
<raw(Components.DownloadedVideo(item))>
|
||||
</each>
|
||||
<footer class="row center-align">
|
||||
|
||||
<button hx-get={$"./downloads?name={Net.Http.UrlEncode(name)}&page={page}"} hx-target="body" hx-push-url="true" >Prev</button>{page+1}<button hx-get={$"./downloads?name={Net.Http.UrlEncode(name)}&page={page+2}"} hx-target="body" hx-push-url="true" >Next</button>
|
||||
|
||||
</footer>
|
||||
|
||||
</null>;
|
||||
|
||||
|
||||
return Components.Shell($"Personal List {name}",html,1);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
func Pages.Artist(music,ctx)
|
||||
{
|
||||
|
||||
var id = ctx.QueryParams.TryGetFirst("id");
|
||||
var res = music.GetArtistsAlbums(id);
|
||||
|
||||
if(TypeOf(id) != "String")
|
||||
{
|
||||
return Components.Shell("Error",<h1>Artist id not specified</h1>,pages);
|
||||
}
|
||||
|
||||
|
||||
var html = <null>
|
||||
<h1>{res.name}</h1>
|
||||
<ul>
|
||||
<each(var item : res.items)>
|
||||
<li><raw(Components.MusicAlbum(item))></li>
|
||||
</each>
|
||||
</ul>
|
||||
</null>;
|
||||
|
||||
|
||||
return Components.Shell($"Artist {res.name}",html,2);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
func Pages.Artists(music,ctx)
|
||||
{
|
||||
|
||||
var q = ctx.QueryParams.TryGetFirst("q");
|
||||
var res = null;
|
||||
var q2 = "";
|
||||
if(TypeOf(q) == "String")
|
||||
{
|
||||
q2 = q;
|
||||
res = music.GetArtists(q);
|
||||
|
||||
}
|
||||
|
||||
var html = <null>
|
||||
<form hx-get="./music-artists" hx-target="body" hx-push-url="true">
|
||||
<div class="field large prefix round fill active">
|
||||
<i class="front">search</i>
|
||||
<input name="q" value={q2}>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
<if(res != null)>
|
||||
<true>
|
||||
<ul>
|
||||
<each(var item : res)>
|
||||
<li><raw(Components.MusicArtist(item))></li>
|
||||
</each>
|
||||
</ul>
|
||||
</true></null>;
|
||||
|
||||
|
||||
return Components.Shell($"Search Artists {q2}",html,2);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
func Pages.Music(ctx)
|
||||
{
|
||||
var html = <ul>
|
||||
<div><a href="./music-artists">Artists</a></div>
|
||||
<div><a href="./music-downloaded">My Music</a></div>
|
||||
</ul>;
|
||||
return Components.Shell("Music",html,2);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
func Pages.PlaylistInfo(tytd,ctx)
|
||||
{
|
||||
var id = ctx.QueryParams.TryGetFirst("id");
|
||||
var page = ctx.QueryParams.TryGetFirstInt("page");
|
||||
if(TypeOf(page) != "Long") page = 1;
|
||||
page--;
|
||||
|
||||
var html = <h1>Could not find playlist</h1>;
|
||||
var title = "N/A";
|
||||
|
||||
if(TypeOf(id) == "String")
|
||||
{
|
||||
id = TYTD.GetPlaylistId(id);
|
||||
var res = tytd.GetPlaylistContents(id,page,20);
|
||||
|
||||
html = <null>
|
||||
<div class="col">
|
||||
<div class="max">
|
||||
<h1>{res.title}</h1>
|
||||
</div>
|
||||
<div class="max">
|
||||
<a href={$"./channel?id={Net.Http.UrlEncode(res.channelId)}"}>{res.channelTitle}</a>
|
||||
</div>
|
||||
</div>
|
||||
<each(var item : res.items)>
|
||||
<raw(Components.DownloadedVideo(item))>
|
||||
</each>
|
||||
<footer class="row center-align">
|
||||
|
||||
<button hx-get={$"./playlist?id={Net.Http.UrlEncode(id)}&page={page}"} hx-target="body" hx-push-url="true" >Prev</button>{page+1}<button hx-get={$"./playlist?id={Net.Http.UrlEncode(id)}&type=videos&page={page+2}"} hx-target="body" hx-push-url="true" >Next</button>
|
||||
|
||||
</footer>
|
||||
</null>;
|
||||
|
||||
title = res.authorName;
|
||||
}
|
||||
|
||||
return Components.Shell($"Playlist {title}",html,1);
|
||||
}
|
||||
79
Tesses.YouTubeDownloader.Server/src/pages/plugins-dl.tcross
Normal file
79
Tesses.YouTubeDownloader.Server/src/pages/plugins-dl.tcross
Normal file
@@ -0,0 +1,79 @@
|
||||
func Pages.DownloadPlugins(tytd,ctx)
|
||||
{
|
||||
var q = ctx.QueryParams.TryGetFirst("q");
|
||||
if(TypeOf(q) != "String")
|
||||
{
|
||||
q = "";
|
||||
}
|
||||
var server = ctx.QueryParams.TryGetFirst("server");
|
||||
if(TypeOf(server) != "String") server = "https://cpkg.tesseslanguage.com/";
|
||||
var items=[];
|
||||
var items2 = [];
|
||||
|
||||
each(var item : tytd.PackageManager.Search(q,{server,type="lib",pluginHost="tytd2025"}))
|
||||
{
|
||||
items2.Add({
|
||||
name = item.packageName,
|
||||
version = item.version,
|
||||
url = $"{server}/package?name={Net.Http.UrlEncode(item.packageName)}",
|
||||
thumb = $"{server}/api/v1/package_icon.png?name={Net.Http.UrlEncode(item.packageName)}&version={Net.Http.UrlEncode(item.version)}"
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
each(var item : tytd.PackageManager.GetPackageServers())
|
||||
{
|
||||
items.Add({
|
||||
active = items.Count == 0,
|
||||
url = item
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
var html= <null><nav class="tabbed">
|
||||
<a hx-get="./plugins" hx-target="body" hx-push-url="true">
|
||||
<i>download_done</i>
|
||||
<span>Installed</span>
|
||||
</a>
|
||||
<a hx-get="./plugins-download" hx-target="body" class="active" hx-push-url="true">
|
||||
<i>download</i>
|
||||
<span>Download</span>
|
||||
</a>
|
||||
</nav>
|
||||
<form hx-get="./plugins-download" hx-target="body" hx-push-url="true">
|
||||
|
||||
<div class="field suffix border round">
|
||||
<select name="server">
|
||||
<each(var item : items)>
|
||||
<if(item.active)>
|
||||
<true>
|
||||
<option value={item.url} selected>{item.url}</option>
|
||||
</true>
|
||||
<false>
|
||||
<option value={item.url}>{item.url}</option>
|
||||
</false>
|
||||
</if>
|
||||
</each>
|
||||
</select>
|
||||
<i>arrow_drop_down</i>
|
||||
</div>
|
||||
<div class="row no-space">
|
||||
<div class="field border left-round max">
|
||||
<input type="text" name="q" value={q}>
|
||||
</div>
|
||||
<button type="submit" class="large right-round min">Search</button>
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
<each(var item : items2)>
|
||||
<raw(Components.PackageItem(tytd,item))>
|
||||
|
||||
</each>
|
||||
|
||||
</null>;
|
||||
|
||||
return Components.Shell("Download plugins",html ,2);
|
||||
}
|
||||
21
Tesses.YouTubeDownloader.Server/src/pages/plugins.tcross
Normal file
21
Tesses.YouTubeDownloader.Server/src/pages/plugins.tcross
Normal file
@@ -0,0 +1,21 @@
|
||||
func Pages.Plugins(tytd,ctx)
|
||||
{
|
||||
var html= <null><nav class="tabbed">
|
||||
<a class="active" hx-get="./plugins" hx-target="body" hx-push-url="true">
|
||||
<i>download_done</i>
|
||||
<span>Installed</span>
|
||||
</a>
|
||||
<a hx-get="./plugins-download" hx-target="body" hx-push-url="true">
|
||||
<i>download</i>
|
||||
<span>Download</span>
|
||||
</a>
|
||||
</nav>
|
||||
|
||||
<each(var item : tytd.Plugins)>
|
||||
<raw(Components.InstalledPlugin(item))>
|
||||
|
||||
</each>
|
||||
</null>;
|
||||
|
||||
return Components.Shell("Installed plugins",html ,2);
|
||||
}
|
||||
66
Tesses.YouTubeDownloader.Server/src/pages/settings.tcross
Normal file
66
Tesses.YouTubeDownloader.Server/src/pages/settings.tcross
Normal file
@@ -0,0 +1,66 @@
|
||||
func Pages.Settings(tytd,ctx)
|
||||
{
|
||||
var totalSecs = tytd.Config.BellTimer ?? 10800;
|
||||
var enablePlugins = tytd.Config.EnablePlugins ?? true;
|
||||
var hours = totalSecs / 3600;
|
||||
totalSecs -= hours * 3600;
|
||||
var minutes = totalSecs / 60;
|
||||
var seconds = totalSecs % 60;
|
||||
|
||||
|
||||
var html = <form hx-post="./settings" hx-target="body" >
|
||||
<div class="field middle-align">
|
||||
<nav>
|
||||
<div class="max">
|
||||
<h6>Enable plugins</h6>
|
||||
<div>Plugins allow you to extend tytd, but they have full access</div>
|
||||
</div>
|
||||
<label class="switch">
|
||||
<if(enablePlugins)>
|
||||
<true>
|
||||
<input name="enablePlugins" type="checkbox" checked>
|
||||
</true>
|
||||
<false>
|
||||
<input name="enablePlugins" type="checkbox">
|
||||
</false>
|
||||
</if>
|
||||
<span></span>
|
||||
</label>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="field label border">
|
||||
<input type="text" name="tag" value={tytd.TYTDTag}>
|
||||
<label>TYTD Tag</label>
|
||||
</div>
|
||||
|
||||
<fieldset>
|
||||
<legend>Subscriber Poll Rate</legend>
|
||||
<div class="row">
|
||||
<div class="max">
|
||||
<div class="field label border">
|
||||
<input type="number" name="hours" value={hours}>
|
||||
<label>Hours</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="max">
|
||||
<div class="field label border">
|
||||
<input type="number" name="minutes" value={minutes}>
|
||||
<label>Minutes</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="max">
|
||||
<div class="field label border">
|
||||
<input type="number" name="seconds" value={seconds}>
|
||||
<label>Seconds</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<footer>
|
||||
<button>Save</button>
|
||||
</footer>
|
||||
</form>;
|
||||
|
||||
return Components.Shell("Settings",html ,3);
|
||||
}
|
||||
30
Tesses.YouTubeDownloader.Server/src/pages/video/info.tcross
Normal file
30
Tesses.YouTubeDownloader.Server/src/pages/video/info.tcross
Normal file
@@ -0,0 +1,30 @@
|
||||
func Pages.VideoInfo(tytd,ctx)
|
||||
{
|
||||
var vid = ctx.QueryParams.TryGetFirst("v");
|
||||
tytd.PutVideoInfoIfNotExists(vid);
|
||||
var vi = tytd.GetVideo(vid);
|
||||
var html = <h1>Could not find video</h1>;
|
||||
|
||||
if(vi != null)
|
||||
{
|
||||
html = <div class="col">
|
||||
<div class="min">
|
||||
<img src={$"./api/v1/video-thumbnail?v={Net.Http.UrlEncode(vi.videoId)}"}>
|
||||
</div>
|
||||
<div class="min">
|
||||
<raw(Components.AddVideo($"https://www.youtube.com/watch?v={vi.videoId}"))>
|
||||
</div>
|
||||
<div class="min">
|
||||
<raw(Components.AddToPersonalList(tytd,vi.videoId))>
|
||||
</div>
|
||||
<div class="min">
|
||||
<h4>{vi.title}</h4>
|
||||
<a href={$"./channel?id={Net.Http.UrlEncode(vi.channelId)}"}>{vi.author}</a>
|
||||
<p>{vi.shortDescription}</p>
|
||||
</div>
|
||||
|
||||
</div>;
|
||||
}
|
||||
|
||||
return Components.Shell(vi != null ? vi.title : "Could not find video", html, 1);
|
||||
}
|
||||
Reference in New Issue
Block a user