media no longer copied twice

This commit is contained in:
jetstream0
2023-11-06 05:25:47 +00:00
parent 59d2ffe544
commit ae0166b238
10 changed files with 122 additions and 43 deletions

2
.gitignore vendored
View File

@@ -1,6 +1,6 @@
.env .env
node_modules node_modules
static static_assets
host_info.json host_info.json
*.js *.js
package-lock.json package-lock.json

View File

@@ -1,8 +1,8 @@
The most legalest private file (specifically for manga, anime, music) hosting ever, on TOR. Includes a manga viewer. Nothing fancy. Uses HTTP Basic Authentication with daily rotating password derived from a master password. The most legalest private file (specifically for manga, anime, music) hosting ever, on TOR. Includes a manga viewer. Nothing fancy. Uses HTTP Basic Authentication with daily rotating password derived from a master password.
Most of the code is a modification of [hedgeblog](https://github.com/jetstream0/hedgeblog), albeit with some pretty significant modifications in places (templates, host.ts, build.ts, saki.ts). Most of the code is a modification of [hedgeblog](https://github.com/jetstream0/hedgeblog), albeit with some pretty significant modifications in places (templates, host.ts, build.ts).
Since TOR is pretty slow to download large files (eg, videos), the videos are streamed so load time isn't as bad as one might expect (HTTP protocol is amazing, browsers are amazing!). Since TOR is pretty slow to download large files (eg, videos), the videos are streamed so load time isn't as bad as one might expect (HTTP protocol is amazing, browsers are amazing).
# Running # Running
After first cloning or downloading the repository: After first cloning or downloading the repository:
@@ -25,4 +25,23 @@ Then to actually run:
bash tor_prebuilt.sh bash tor_prebuilt.sh
``` ```
# Tips
## Adding media
Add it to the relevant static directory (`/static_assets/anime_assets`, `/static_assets/manga_assets`, or `/static_assets/music_assets`), and create an entry for it in `host_info.json`.
## Hosting Multiple TOR Hidden Services
If you are running multiple TOR hidden services, you will need to modify the [.torrc file](https://stackoverflow.com/questions/14321214/how-to-run-multiple-tor-processes-at-once-with-different-exit-ips#18895491). If you are running multiple TOR hidden services, you will need to modify the [.torrc file](https://stackoverflow.com/questions/14321214/how-to-run-multiple-tor-processes-at-once-with-different-exit-ips#18895491).
## Master Password
To set the master password, create a `.env` file like so:
```
master_password=example_password_do_not_actually_use_this_as_your_password_obviously
```
## Username
The username doesn't matter. Only the password is important.
# Disclaimer
blah blah blah meant for private use, I would never dream of breaking copyright law blah blah blah not the intended purpose.

View File

@@ -36,17 +36,10 @@ interface MangaVars {
const listings: Listing[] = _host_info.listings.filter((listing: any): listing is Listing => typeof listing.name === "string" && (listing?.type === "anime" || listing?.type === "manga" || listing?.type === "music")); const listings: Listing[] = _host_info.listings.filter((listing: any): listing is Listing => typeof listing.name === "string" && (listing?.type === "anime" || listing?.type === "manga" || listing?.type === "music"));
let renderer: Renderer = new Renderer("templates", "components"); let renderer: Renderer = new Renderer("templates", "components");
let builder: Builder; let builder: Builder = new Builder("/build");;
if (process.argv[2] === "--quick") {
builder = new Builder("/build", ["anime_assets", "manga_assets", "music_assets", "password"]); //password is included since it is static
} else {
builder = new Builder();
}
//static page //static page
if (process.argv[2] !== "--quick") { builder.serve_static_folder("static");
builder.serve_static_folder("static");
}
//main page //main page
builder.serve_template(renderer, "/", "index", { builder.serve_template(renderer, "/", "index", {
@@ -71,7 +64,7 @@ let songs: string[] = [];
for (let i = 0; i < listings.length; i++) { for (let i = 0; i < listings.length; i++) {
const listing: Listing = listings[i]; const listing: Listing = listings[i];
directory_serve_paths.push(`/${listing.type}/${listing.name}`); directory_serve_paths.push(`/${listing.type}/${listing.name}`);
const chapters: string[] = readdirSync(path.join(__dirname, `/static/${listing.type}_assets/${listing.name}`), { withFileTypes: true }).map((d) => d.name.replace(".mp4", "").replace(".mp3", "")); const chapters: string[] = readdirSync(path.join(__dirname, `/static_assets/${listing.type}_assets/${listing.name}`), { withFileTypes: true }).map((d) => d.name.replace(".mp4", "").replace(".mp3", ""));
directory_vars.push({ directory_vars.push({
listing, listing,
chapters, chapters,
@@ -91,7 +84,7 @@ for (let i = 0; i < listings.length; i++) {
for (let j = 0; j < chapters.length; j++) { for (let j = 0; j < chapters.length; j++) {
const chapter: string = chapters[j]; const chapter: string = chapters[j];
manga_serve_paths.push(`/${listing.type}/${listing.name}/${chapter}`); manga_serve_paths.push(`/${listing.type}/${listing.name}/${chapter}`);
const images: string[] = readdirSync(path.join(__dirname, `/static/${listing.type}_assets/${listing.name}/${chapter}`), { withFileTypes: true }).map((d) => d.name); const images: string[] = readdirSync(path.join(__dirname, `/static_assets/${listing.type}_assets/${listing.name}/${chapter}`), { withFileTypes: true }).map((d) => d.name);
manga_vars.push({ manga_vars.push({
listing, listing,
chapter, chapter,

10
host.ts
View File

@@ -28,12 +28,17 @@ createServer((req, res) => {
//end response //end response
return res.end(); return res.end();
} }
const url_obj = new URL(req.url, `http://${req.headers.host}`);
if (!req.url.includes(".")) { if (!req.url.includes(".")) {
req_path = path.join(__dirname, "build", decodeURI(req.url), "index.html"); req_path = path.join(__dirname, "build", decodeURI(req.url), "index.html");
} else {
//is file
if (url_obj.pathname.startsWith("/anime_assets") || url_obj.pathname.startsWith("/manga_assets") || url_obj.pathname.startsWith("/music_assets")) {
req_path = path.join(__dirname, "static_assets", decodeURI(req.url));
} else { } else {
req_path = path.join(__dirname, "build", decodeURI(req.url)); req_path = path.join(__dirname, "build", decodeURI(req.url));
} }
const url_obj = new URL(req.url, `http://${req.headers.host}`); }
//check for auth //check for auth
//hopefully no security vulnerabilities. please look away //hopefully no security vulnerabilities. please look away
if (url_obj.pathname !== "/password") { if (url_obj.pathname !== "/password") {
@@ -64,10 +69,11 @@ createServer((req, res) => {
res.write("404"); res.write("404");
return res.end(); return res.end();
} }
const file_ext: string = req_path.split(".")[1];
//set content type //set content type
let non_utf8_content_types: string[] = ["image/png", "image/gif", "image/jpeg", "audio/mpeg", "video/mp4"]; let non_utf8_content_types: string[] = ["image/png", "image/gif", "image/jpeg", "audio/mpeg", "video/mp4"];
let content_type: string; let content_type: string;
switch (req_path.split(".")[1]) { switch (file_ext) {
case "html": case "html":
content_type = "text/html; charset=utf-8"; content_type = "text/html; charset=utf-8";
break; break;

View File

@@ -6,7 +6,6 @@
"scripts": { "scripts": {
"compile": "tsc -p .", "compile": "tsc -p .",
"build": "node build.js", "build": "node build.js",
"build-quick": "node build.js --quick",
"host": "node host.js" "host": "node host.js"
}, },
"repository": { "repository": {

20
saki.ts
View File

@@ -4,33 +4,19 @@ import type { Renderer } from './ryuji.js';
export class Builder { export class Builder {
build_dir: string; build_dir: string;
ignore?: string[]; //top level directories/files to not wipe
constructor(build_dir: string="/build", ignore?: string[]) { constructor(build_dir: string="/build") {
this.build_dir = path.join(__dirname, build_dir); this.build_dir = path.join(__dirname, build_dir);
this.ignore = ignore;
if (existsSync(this.build_dir)) { if (existsSync(this.build_dir)) {
//wipe the build directory //wipe the build directory
if (!this.ignore || this.ignore?.length === 0) {
rmSync(this.build_dir, { rmSync(this.build_dir, {
recursive: true, recursive: true,
}); });
} else {
//do not wipe certain directories/files
readdirSync(this.build_dir).forEach((member: string) => {
if (!this.ignore.includes(member)) {
rmSync(path.join(this.build_dir, member), {
recursive: true,
});
} }
});
}
}
if (!this.ignore || this.ignore?.length === 0) {
mkdirSync(this.build_dir); mkdirSync(this.build_dir);
} }
}
//todo: option where if path already exists, let it be (much faster build times)
static copy_folder(folder_path: string, dest_path: string) { static copy_folder(folder_path: string, dest_path: string) {
let children: string[] = readdirSync(folder_path); let children: string[] = readdirSync(folder_path);
for (let i=0; i < children.length; i++) { for (let i=0; i < children.length; i++) {
@@ -42,7 +28,7 @@ export class Builder {
copyFileSync(child_path, copy_path); copyFileSync(child_path, copy_path);
} else { } else {
//directory, make directory and recursively copy //directory, make directory and recursively copy
mkdirSync(copy_path); if (!existsSync(copy_path)) mkdirSync(copy_path);
Builder.copy_folder(child_path, path.join(dest_path, child)); Builder.copy_folder(child_path, path.join(dest_path, child));
} }
} }

View File

@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>holaa</title>
</head>
<body>
<div id="main">
<input id="master-password" type="password">
<button onclick="get_passwords()">get</button>
<br>
<span id="computed"></span>
<br>
<span id="computed2"></span>
</div>
<script>
function get_passwords() {
get_password(new Date(), "computed");
get_password(new Date((new Date()).getTime() + 24 * 60 * 60 * 1000), "computed2");
}
async function get_password(date, el) {
//password changes every day
const master_bytes = new TextEncoder().encode(`${document.getElementById("master-password").value}${date.getUTCFullYear()}-${date.getUTCMonth()}-${date.getUTCDate()}`);
const hash_bytes = new Uint8Array(await crypto.subtle.digest("SHA-256", master_bytes));
//uint8array into hex
let hex_chars = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];
let hash_hex = "";
for (let i = 0; i < hash_bytes.length; i++) {
hash_hex += hex_chars[Math.floor(hash_bytes[i] / 16)];
hash_hex += hex_chars[hash_bytes[i] % 16];
}
document.getElementById(el).innerText = hash_hex.toLowerCase();
}
</script>
</body>
</html>

View File

@@ -1 +1 @@
<li><a href="/[[ listing.type ]]/[[ listing.name ]]">[[ listing.name ]] ([[ listing.type ]])</a></li> <li class="listing [[ listing.type ]]-listing"><a href="/[[ listing.type ]]/[[ listing.name ]]">[[ listing.name ]] ([[ listing.type ]])</a></li>

View File

@@ -10,6 +10,13 @@
</head> </head>
<body> <body>
<div id="main"> <div id="main">
<label for="show">Show:</label>
<select id="show" onchange="show_change()">
<option value="all" selected>All</option>
<option value="anime">Anime</option>
<option value="manga">Manga</option>
<option value="music">Music</option>
</select>
<div> <div>
<ul> <ul>
[[ for:listings:listing ]] [[ for:listings:listing ]]
@@ -18,5 +25,40 @@
</ul> </ul>
</div> </div>
</div> </div>
<script>
function show_change() {
const show_value = document.getElementById("show").value;
if (show_value === "all") {
document.querySelectorAll(".listing").forEach((l) => {
l.style.display = "list-item";
});
} else if (show_value === "anime") {
document.querySelectorAll(".listing").forEach((l) => {
if (l.classList.contains("anime-listing")) {
l.style.display = "list-item";
} else {
l.style.display = "none";
}
});
} else if (show_value === "manga") {
document.querySelectorAll(".listing").forEach((l) => {
if (l.classList.contains("manga-listing")) {
l.style.display = "list-item";
} else {
l.style.display = "none";
}
});
} else if (show_value === "music") {
document.querySelectorAll(".listing").forEach((l) => {
if (l.classList.contains("music-listing")) {
l.style.display = "list-item";
} else {
l.style.display = "none";
}
});
}
}
show_change();
</script>
</body> </body>
</html> </html>

View File

@@ -1,3 +0,0 @@
npm run compile
npm run build-quick
tor -f .torrc & npm run host