Add source

This commit is contained in:
2026-07-20 18:37:42 -05:00
parent b65800fbe8
commit 74ba9e94fb
8 changed files with 352 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
.DS_Store
bin
obj
publish
npm/node_modules
npm/package-lock.json
npm/dist
thumbs.db
.env

29
cross.json Normal file
View File

@@ -0,0 +1,29 @@
{
"$schema": "https:\/\/crosslang.tesseslanguage.com\/\/schema\/cross-json-schema.json",
"info": {
"short_name": "changeme",
"short_name_pretty": "Change Me",
"type": "webapp"
},
"name": "tytd-submission-box",
"prebuild": [
{
"commands": [
[
"npm",
"install"
],
[
"npm",
"run",
"publish"
]
],
"res": [
"npm\/dist"
],
"workdir": "npm"
}
],
"version": "1.0.0.0-prod"
}

11
npm/package.json Normal file
View File

@@ -0,0 +1,11 @@
{
"dependencies": {
"beercss": "^4.0.21"
},
"devDependencies": {
"esbuild": "^0.28.0"
},
"scripts": {
"publish": "esbuild --minify --sourcemap --define:'process.env.NODE_ENV=\"production\"' --bundle --outdir=dist web.mjs --loader:.svg=file --loader:.woff2=file"
}
}

78
npm/web.css Normal file
View File

@@ -0,0 +1,78 @@
:root,
body.light {
--primary:#855400;
--on-primary:#ffffff;
--primary-container:#ffddb7;
--on-primary-container:#2a1700;
--secondary:#705b41;
--on-secondary:#ffffff;
--secondary-container:#fcdebc;
--on-secondary-container:#281805;
--tertiary:#53643e;
--on-tertiary:#ffffff;
--tertiary-container:#d6e9b9;
--on-tertiary-container:#121f03;
--error:#ba1a1a;
--on-error:#ffffff;
--error-container:#ffdad6;
--on-error-container:#410002;
--background:#fffbff;
--on-background:#1f1b16;
--surface:#fff8f4;
--on-surface:#1f1b16;
--surface-variant:#f0e0d0;
--on-surface-variant:#504539;
--outline:#827568;
--outline-variant:#d4c4b5;
--shadow:#000000;
--scrim:#000000;
--inverse-surface:#352f2a;
--inverse-on-surface:#f9efe7;
--inverse-primary:#ffb95c;
--surface-dim:#e2d8d1;
--surface-bright:#fff8f4;
--surface-container-lowest:#ffffff;
--surface-container-low:#fcf2ea;
--surface-container:#f6ece4;
--surface-container-high:#f0e6de;
--surface-container-highest:#ebe1d9;
}
body.dark {
--primary:#ffb95c;
--on-primary:#462a00;
--primary-container:#653e00;
--on-primary-container:#ffddb7;
--secondary:#dfc2a2;
--on-secondary:#3f2d17;
--secondary-container:#57432b;
--on-secondary-container:#fcdebc;
--tertiary:#bacd9f;
--on-tertiary:#263514;
--tertiary-container:#3c4c28;
--on-tertiary-container:#d6e9b9;
--error:#ffb4ab;
--on-error:#690005;
--error-container:#93000a;
--on-error-container:#ffb4ab;
--background:#1f1b16;
--on-background:#ebe1d9;
--surface:#17130e;
--on-surface:#ebe1d9;
--surface-variant:#504539;
--on-surface-variant:#d4c4b5;
--outline:#9c8e80;
--outline-variant:#504539;
--shadow:#000000;
--scrim:#000000;
--inverse-surface:#ebe1d9;
--inverse-on-surface:#352f2a;
--inverse-primary:#855400;
--surface-dim:#17130e;
--surface-bright:#3e3833;
--surface-container-lowest:#110e09;
--surface-container-low:#1f1b16;
--surface-container:#231f1a;
--surface-container-high:#2e2924;
--surface-container-highest:#39342f;
}

2
npm/web.mjs Normal file
View File

@@ -0,0 +1,2 @@
import 'beercss'
import './web.css'

17
res/ratelimit.html Normal file
View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/dist/web.css">
<title>tytd-submission-box - {title}</title>
<script src="/dist/web.js" defer></script>
</head>
<body>
<main class="responsive">
<h1>You are rate limited</h1>
<p>Please wait 10 or so seconds</p>
<a href="/">Home</a>
</main>
</body>
</html>

138
src/index.tcross Normal file
View File

@@ -0,0 +1,138 @@
func template(title,body)
{
<return>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/dist/web.css">
<title>TYTD Submission Box - {title}</title>
<script src="/dist/web.js" defer></script>
</head>
<body>
<main class="responsive">
<raw(body)>
</main>
</body>
</html>
</return>
}
func TYTD.GetVideoId(v)
{
func IsValidId(_v)
{
if(_v.Count != 11) return false;
each(var item : _v)
{
if(!(item.IsLetter() || item.IsDigit() || item == '-' || item == '_')) return false;
}
return true;
}
if(TypeOf(v) != "String") return null;
if(IsValidId(v)) return v;
each(var __re : ["youtube\\..+?/watch.*?v=(.*?)(?:&|/|$)","youtu\\.be/(.*?)(?:\\?|&|/|$)","youtube\\..+?/embed/(.*?)(?:\\?|&|/|$)","youtube\\..+?/shorts/(.*?)(?:\\?|&|/|$)","youtube\\..+?/live/(.*?)(?:\\?|&|/|$)"])
{
var __r = new Regex(__re);
var __s = __r.Search(v);
if(__s.Count == 2)
{
__r=__s[1].Text;
if(IsValidId(__r))
{
return __r;
}
}
}
return null;
}
func Index(ctx)
{
ctx.WithDebug(true);
if(ctx.Method == "POST")
{
const v = ctx.QueryParams.TryGetFirst("v");
if(!TypeIsString(v))
{
ctx.WithMimeType("text/html").SendText(
template(
"v is not defined",
<null>
<h1>Error</h1>
The post request did not contain the {"\"v\""} parameter
<a href="/">Go Home</a>
</null>
)
);
return true;
}
const url = TYTD.GetVideoId(v);
if(!TypeIsString(url))
{
ctx.WithMimeType("text/html").SendText(
template(
"Not a valid url",
<null>
<h1>Error</h1>
The url must be a valid YouTube video
<a href="/">Go Home</a>
</null>
)
);
return true;
}
if(needsToRateLimit())
{
ctx.SendRedirect("/ratelimit",303);
return true;
}
try {
const tytdReq = Net.Http.MakeRequest(
$"{tytd_url}.TrimEnd('/')/api/v1/add",
{
Method = "POST",
RequestHeaders = [
{Key = "Authorization",Value=$"Bearer {tytd_apikey}"}
],
Body = new JsonHttpRequestBody(
[
{
url = url,
res = "LowVideo"
}
]
)
}
);
tytdReq.ReadToString();
}catch(ex) {}
ctx.SendRedirect("/",303);
return true;
}
ctx.WithMimeType("text/html").SendText(
template(
"Add a video",
<form method="POST" action="/">
<article class="medium middle-align center-align">
<div>
<i class="extra">download</i>
<h5>Add a video to my YouTube Downloader</h5>
<div class="space"></div>
<nav class="no-space">
<div class="max field border left-round">
<input name="v">
</div>
<button class="large right-round">Add</button>
</nav>
</div>
</article>
</form>
)
);
return true;
}

68
src/program.tcross Normal file
View File

@@ -0,0 +1,68 @@
class MyWebApp {
private fileServer;
private mountable;
private pages;
public MyWebApp()
{
this.fileServer = new FileServer(embeddir("/"), true, false);
this.mountable = new MountableServer((ctx)=>{
const page = this.pages.[ctx.Path];
if(TypeIsDefined(page)) return page(ctx);
return false;
});
this.mountable.Mount("/dist/",this.fileServer);
this.pages = {
.["/"] = Index,
.["/ratelimit"] = RateLimit
};
}
public Handle(ctx)
{
return this.mountable.Handle(ctx);
}
public Close()
{
this.mountable = null;
}
}
const tytd_url = Env["TYTD_URL"];
const tytd_apikey = Env["TYTD_APIKEY"];
func WebAppMain(args)
{
if(!TypeIsString(tytd_url) || !TypeIsString(tytd_apikey))
{
Console.WriteLine("Define the environment variables TYTD_URL and TYTD_APIKEY");
return 1;
}
return new MyWebApp();
}
func RateLimit(ctx)
{
ctx.WithMimeType("text/html").SendStream(embedstrm("ratelimit.html"));
return true;
}
const mtx = new Mutex();
var lastRequest = 0;
func needsToRateLimit()
{
const time = DateTime.Now;
mtx.Lock();
const lastTime = lastRequest;
mtx.Unlock();
if(lastTime + 10 > time)
return true;
mtx.Lock();
lastRequest = time;
mtx.Unlock();
return false;
}