Add source
This commit is contained in:
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal 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
29
cross.json
Normal 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
11
npm/package.json
Normal 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
78
npm/web.css
Normal 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
2
npm/web.mjs
Normal file
@@ -0,0 +1,2 @@
|
||||
import 'beercss'
|
||||
import './web.css'
|
||||
17
res/ratelimit.html
Normal file
17
res/ratelimit.html
Normal 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
138
src/index.tcross
Normal 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
68
src/program.tcross
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user