Bring TYTD Back
This commit is contained in:
commit
3d1a64770a
|
@ -0,0 +1,3 @@
|
|||
bin/
|
||||
obj/
|
||||
Creds.priv.cs
|
|
@ -0,0 +1,17 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SSH.NET" Version="2024.1.0" />
|
||||
<PackageReference Include="Tesses.WebServer" Version="1.0.4.3" />
|
||||
<PackageReference Include="Tesses.WebServer.EasyServer" Version="1.0.1" />
|
||||
<PackageReference Include="YouTubeExplode" Version="6.4.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,9 @@
|
|||
public static partial class Creds
|
||||
{
|
||||
public static string IP => _ip;
|
||||
public static string Username => _username;
|
||||
|
||||
public static string Password => _password;
|
||||
|
||||
public static string DirectoryOnServer => _dir;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||
ADD . /src
|
||||
WORKDIR /src
|
||||
RUN dotnet publish -c Release -o /out
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/runtime:8.0
|
||||
EXPOSE 3252
|
||||
COPY --from=build /out /out
|
||||
COPY --from=build /src/wwwroot /out/wwwroot
|
||||
|
||||
WORKDIR /out
|
||||
CMD ["/out/BringTYTDBack"]
|
||||
RUN chmod +x /out/BringTYTDBack
|
|
@ -0,0 +1,166 @@
|
|||
using System.Collections.Concurrent;
|
||||
using Renci.SshNet;
|
||||
using YoutubeExplode;
|
||||
using YoutubeExplode.Videos;
|
||||
using Tesses.WebServer;
|
||||
using static Creds;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using YoutubeExplode.Playlists;
|
||||
using YoutubeExplode.Channels;
|
||||
|
||||
YoutubeClient ytc=new YoutubeClient();
|
||||
ConcurrentStack<VideoId> videoIds=new ConcurrentStack<VideoId>();
|
||||
bool isRunning=true;
|
||||
|
||||
async Task AddVideo(VideoId id)
|
||||
{
|
||||
SshClient client = new SshClient(IP,Username,Password);
|
||||
await client.ConnectAsync(default);
|
||||
using(var res=client.CreateCommand($"echo {DirectoryOnServer}/*-{id.Value}.mp4"))
|
||||
{
|
||||
await res.ExecuteAsync();
|
||||
|
||||
if(res.Result == $"{DirectoryOnServer}/*-{id.Value}.mp4\n")
|
||||
{
|
||||
using(var res2=client.CreateCommand($"cd {DirectoryOnServer} && tytd {id.Value}"))
|
||||
{
|
||||
await res2.ExecuteAsync();
|
||||
Console.WriteLine($"Downloaded: https://www.youtube.com/watch?v={id.Value}");
|
||||
_=res2.Result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Video already exists: https://www.youtube.com/watch?v={id.Value}");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async Task AddItem(string url)
|
||||
{
|
||||
VideoId? vId = VideoId.TryParse(url);
|
||||
PlaylistId? pId = PlaylistId.TryParse(url);
|
||||
ChannelId? cId = ChannelId.TryParse(url);
|
||||
UserName? username = UserName.TryParse(url);
|
||||
ChannelSlug? slug = ChannelSlug.TryParse(url);
|
||||
ChannelHandle? handle = ChannelHandle.TryParse(url);
|
||||
|
||||
if(url.Length == 11 && vId.HasValue)
|
||||
{
|
||||
videoIds.Push(vId.Value);
|
||||
}
|
||||
else if(pId.HasValue)
|
||||
{
|
||||
await foreach(var v in ytc.Playlists.GetVideosAsync(pId.Value))
|
||||
{
|
||||
videoIds.Push(v.Id);
|
||||
}
|
||||
}
|
||||
else if(vId.HasValue)
|
||||
{
|
||||
videoIds.Push(vId.Value);
|
||||
}
|
||||
else if(cId.HasValue)
|
||||
{
|
||||
await foreach(var c in ytc.Channels.GetUploadsAsync(cId.Value))
|
||||
{
|
||||
videoIds.Push(c.Id);
|
||||
}
|
||||
}
|
||||
else if(username.HasValue)
|
||||
{
|
||||
cId = (await ytc.Channels.GetByUserAsync(username.Value)).Id;
|
||||
await foreach(var c in ytc.Channels.GetUploadsAsync(cId.Value))
|
||||
{
|
||||
videoIds.Push(c.Id);
|
||||
}
|
||||
}
|
||||
else if(slug.HasValue)
|
||||
{
|
||||
cId = (await ytc.Channels.GetBySlugAsync(slug.Value)).Id;
|
||||
await foreach(var c in ytc.Channels.GetUploadsAsync(cId.Value))
|
||||
{
|
||||
videoIds.Push(c.Id);
|
||||
}
|
||||
}
|
||||
else if(handle.HasValue)
|
||||
{
|
||||
cId = (await ytc.Channels.GetByHandleAsync(handle.Value)).Id;
|
||||
await foreach(var c in ytc.Channels.GetUploadsAsync(cId.Value))
|
||||
{
|
||||
videoIds.Push(c.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Task.Factory.StartNew(async()=>{
|
||||
while(isRunning)
|
||||
{
|
||||
if(videoIds.TryPop(out var res))
|
||||
{
|
||||
await AddVideo(res);
|
||||
}
|
||||
}
|
||||
},TaskCreationOptions.LongRunning).Wait(0);
|
||||
|
||||
MountableServer msvr=new MountableServer(new StaticServer("wwwroot"));
|
||||
|
||||
msvr.Mount("/api/AddItemRes/1/",new TYTDServer(AddItem));
|
||||
msvr.Mount("/api/AddItem/",new TYTDServer(AddItem));
|
||||
msvr.Mount("/api/AddVideoRes/1/",new TYTDServer(videoIds));
|
||||
msvr.Mount("/api/AddVideo/",new TYTDServer(videoIds));
|
||||
RouteServer routeServer=new RouteServer(msvr);
|
||||
routeServer.Add("/itemsInQueue",async(ctx)=>{
|
||||
await ctx.SendJsonAsync(videoIds.Count);
|
||||
});
|
||||
routeServer.Add("/add",async(ctx)=>{
|
||||
if(ctx.QueryParams.TryGetFirst("v",out var v))
|
||||
{
|
||||
await AddItem(v);
|
||||
}
|
||||
await ctx.SendRedirectAsync("/");
|
||||
});
|
||||
routeServer.StartServer(3252);
|
||||
|
||||
public class TYTDServer : Server
|
||||
{
|
||||
bool onlyVideos;
|
||||
ConcurrentStack<VideoId>? stk;
|
||||
|
||||
Func<string,Task>? cb;
|
||||
public TYTDServer(ConcurrentStack<VideoId> stk)
|
||||
{
|
||||
onlyVideos=true;
|
||||
this.stk = stk;
|
||||
}
|
||||
|
||||
public TYTDServer(Func<string,Task> cb)
|
||||
{
|
||||
onlyVideos=false;
|
||||
this.cb = cb;
|
||||
}
|
||||
public override async Task GetAsync(ServerContext ctx)
|
||||
{
|
||||
if(onlyVideos)
|
||||
{
|
||||
string url=ctx.UrlPath.Substring(1);
|
||||
stk?.Push(url);
|
||||
await ctx.SendTextAsync("<script>window.history.back();</script>");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(cb == null) {
|
||||
await ctx.SendTextAsync("<script>window.history.back();</script>");
|
||||
|
||||
return;
|
||||
}
|
||||
string url=ctx.UrlPath.Substring(1);
|
||||
await cb(url);
|
||||
await ctx.SendTextAsync("<script>window.history.back();</script>");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
Bring TYTD Back
|
||||
===============
|
||||
|
||||
I wanted to bring [TYTD](https://gitea.site.tesses.net/tesses50/tytd) back so I did somewhat
|
||||
|
||||
This project requires [tytd-cli](https://gitea.site.tesses.net/tesses50/tytd-cli-cpp) to be installed on a ssh (password protected server, could use private key if you modify Program.cs async Task AddVideo), the tytd-cli must be installed on PATH
|
||||
|
||||
To Configure you must add a file called Creds.priv.cs with the following content
|
||||
|
||||
```cs
|
||||
public static partial class Creds
|
||||
{
|
||||
static string _ip = "YOUR_SSH_SERVER";
|
||||
static string _username = "YOUR_SSH_USERNAME";
|
||||
static string _password = "YOUR_SSH_PASSWORD";
|
||||
static string _dir = "YOUR_SSH_PATH";
|
||||
}
|
||||
```
|
||||
|
||||
# WITHOUT DOCKER
|
||||
then compile the project using
|
||||
```bash
|
||||
dotnet publish -c Release -o out --self-contained -r linux-x64 -p:PublishSingleFile=true -p:PublishReadyToRun=true
|
||||
```
|
||||
then copy wwwroot to out
|
||||
|
||||
# WITH DOCKER
|
||||
```bash
|
||||
docker build -t bring-tytd-back:latest .
|
||||
docker run -p 3252:3252 -d bring-tytd-back:latest
|
||||
```
|
Binary file not shown.
After Width: | Height: | Size: 5.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
After Width: | Height: | Size: 7.2 KiB |
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>TYTD</title>
|
||||
<link rel="stylesheet" href="./beer.min.css">
|
||||
<link rel="stylesheet" href="./theme.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<article class="medium middle-align center-align">
|
||||
<div>
|
||||
<i class="extra">download</i>
|
||||
<h5>Add a video, playlist or channel to my TYTD</h5>
|
||||
|
||||
<div class="space"></div>
|
||||
<form action="./add">
|
||||
|
||||
<nav class="no-space">
|
||||
|
||||
<div class="max field border left-round">
|
||||
<input type="url" name="v">
|
||||
</div>
|
||||
<button type="submit" class="large right-round">Add</button>
|
||||
|
||||
</nav>
|
||||
</form>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article>
|
||||
There are <span id="noItems">0</span> item(s) in the queue
|
||||
</article>
|
||||
|
||||
<script src="./beer.min.js"></script>
|
||||
|
||||
<script defer>
|
||||
const d = document.getElementById('noItems');
|
||||
setInterval(()=>{
|
||||
fetch('./itemsInQueue').then(e=>e.text()).then(e=>{
|
||||
d.innerText = e;
|
||||
});
|
||||
},3000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "Tesses YouTube Downloader",
|
||||
"short_name": "Tesses YouTube Downloader",
|
||||
"start_url": "./",
|
||||
"scope": ".",
|
||||
"icons": [
|
||||
{
|
||||
"src": "./android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "./android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ff0000",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
:root,html,
|
||||
body {
|
||||
--primary:#ffb4a8;
|
||||
--on-primary:#690000;
|
||||
--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;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root,html,
|
||||
body {
|
||||
--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;
|
||||
}
|
||||
}
|
||||
|
||||
.horizonal
|
||||
{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.vertical
|
||||
{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.filler
|
||||
{
|
||||
flex-grow: 1;
|
||||
}
|
Loading…
Reference in New Issue