This commit is contained in:
lda committed 2025-12-25 23:25:12 +07:00
1 parent e61c34bc0d
commit 1911d55d5e
13 files changed
+539 -10

No files matched your search

+36 -1
View File
@@ -1,11 +1,13 @@
import base64
import socket import socket
import string import string
from collections import deque from collections import deque
from itertools import islice from itertools import islice
from random import choice from random import choice
from typing import Optional from typing import Iterator, Optional, cast
from urllib.parse import urlsplit, urlunsplit from urllib.parse import urlsplit, urlunsplit
import urlextract
from fastapi import FastAPI, Form, HTTPException, Request from fastapi import FastAPI, Form, HTTPException, Request
from fastapi.responses import HTMLResponse, PlainTextResponse, RedirectResponse from fastapi.responses import HTMLResponse, PlainTextResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
@@ -192,3 +194,36 @@ async def api_paste_delete(name: str):
raise HTTPException(status_code=404, detail="Paste not found") raise HTTPException(status_code=404, detail="Paste not found")
content = texts.pop(name) content = texts.pop(name)
return {"name": name, "content": content} return {"name": name, "content": content}
# @app.get("/nsfw-shield", response_class=HTMLResponse)
# async def this(request: Request):
# return templates.TemplateResponse(request, "nsfw_shield.html")
# @app.get("/nsfw-shield/{link:path}")
# async def that(link: str):
# b64link = encode_safe(link)
# return RedirectResponse(f"/nsfw-shield?redirect={b64link}") # if i convert this thing to
@app.get("/extractor", response_class=HTMLResponse)
async def extractor_ui(request: Request):
"""Link Extractor UI."""
return templates.TemplateResponse(request, "extractor.html")
def encode_safe(link: str) -> str:
return base64.urlsafe_b64encode(link.encode()).rstrip(b"=").decode()
def decode_safe(should_be_b64: str) -> str:
supposedlength = (len(should_be_b64) + 3) // 4 * 4
b64 = should_be_b64.ljust(supposedlength, "=")
return base64.urlsafe_b64decode(b64).decode()
def extract_urls_from_jumblang(txt: str) -> Iterator[str]:
return cast(
Iterator[str], urlextract.URLExtract().gen_urls(txt)
) # i read the code. PLEASE have overloads mr coders
+30
View File
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
console.log(window.location.search);
try {
console.log(
Array.from(new URLSearchParams(location.search).entries())
); // [["https://ldlda.com", ""]]
} catch (e) {
console.error(e);
}
const cuhwuhfduhs = window.location.search.replace(/^\?/g, "");
if (cuhwuhfduhs) {
const bbugsbg = new URL(cuhwuhfduhs);
const hweriwehr = new URL(location);
hweriwehr.search = "";
if (bbugsbg != hweriwehr) {
// window.location.href = cuhwuhfduhs;
alert(`redir to ${bbugsbg}`);
}
}
</script>
</body>
</html>
+1
View File
@@ -10,5 +10,6 @@ dependencies = [
"pydantic", "pydantic",
"python-multipart", "python-multipart",
"qrcode", "qrcode",
"urlextract>=1.9.0",
"uvicorn", "uvicorn",
] ]
+1
View File
@@ -22,6 +22,7 @@ a:hover {
.container { .container {
width: 100%; width: 100%;
max-width: 800px; /* Default width */
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
+1
View File
@@ -123,6 +123,7 @@ textarea.form-control {
/* Navbar */ /* Navbar */
.navbar { .navbar {
width: 100%; width: 100%;
max-width: 800px; /* Default width */
background-color: var(--surface-color); background-color: var(--surface-color);
padding: 1rem 2rem; padding: 1rem 2rem;
display: flex; display: flex;
+186
View File
@@ -0,0 +1,186 @@
:root {
--bg-color: #121212;
--surface-color: #1e1e1e;
--primary-color: #bb86fc;
--text-color: #e0e0e0;
--error-color: #cf6679;
--success-color: #03dac6;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
font-family: system-ui, -apple-system, sans-serif;
display: flex;
justify-content: center;
align-items: center;
margin: 0;
padding: 20px;
box-sizing: border-box;
min-height: 100lvh;
}
.card {
background-color: var(--surface-color);
padding: 2rem;
border-radius: 12px;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.4);
max-width: 400px;
width: 100%;
text-align: center;
border: 1px solid #333;
}
h1 {
margin-top: 0;
color: var(--error-color);
font-size: 1.8rem;
}
p {
line-height: 1.5;
margin-bottom: 1.5rem;
}
input {
width: 100%;
padding: 12px;
margin: 10px 0;
background: #2d2d2d;
border: 1px solid #444;
color: white;
border-radius: 6px;
box-sizing: border-box;
font-size: 1rem;
}
input:focus {
outline: 2px solid var(--primary-color);
border-color: transparent;
}
.buttons {
display: flex;
gap: 10px;
justify-content: center;
margin-top: 20px;
}
button {
padding: 12px 24px;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: bold;
font-size: 1rem;
transition: transform 0.1s, opacity 0.2s;
}
button:active {
transform: scale(0.98);
}
button.primary {
background-color: var(--error-color);
color: #000;
}
button.create-btn {
background-color: var(--primary-color);
color: #000;
}
button.secondary {
background-color: #333;
color: #fff;
border: 1px solid #444;
}
button:hover {
opacity: 0.9;
}
.hidden {
display: none;
}
#result-area {
margin-top: 20px;
padding: 15px;
background: #252525;
border-radius: 6px;
border: 1px solid #333;
text-align: left;
}
.link-display {
word-break: break-all;
color: var(--primary-color);
margin-bottom: 10px;
display: block;
font-family: monospace;
}
.copy-feedback {
display: none;
color: var(--success-color);
font-size: 0.8rem;
margin-left: 10px;
opacity: 0;
transition: opacity 0.3s;
}
.copy-feedback.show {
opacity: 1;
display: unset;
}
footer {
margin-top: 30px;
font-size: 0.8rem;
color: #666;
}
footer a {
color: #888;
text-decoration: none;
}
footer a:hover {
text-decoration: underline;
}
/* Utility Classes */
.text-primary {
color: var(--primary-color);
}
.text-muted {
color: #888;
font-size: 0.9em;
}
.mb-small {
margin: 0 0 5px 0;
}
.btn-full {
width: 100%;
padding: 8px;
}
.warning-box {
background: #252525;
padding: 10px;
border-radius: 4px;
margin-bottom: 20px;
text-align: left;
}
.destination-url {
color: white;
display: block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
+4 -2
View File
@@ -1,6 +1,6 @@
.pastepad-container { .pastepad-container {
width: 100%; width: 100%;
max-width: 1000px; /* Increased from 800px */ /* max-width handled by .layout-wide .container */
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 20px; gap: 20px;
@@ -51,6 +51,8 @@
box-sizing: border-box; box-sizing: border-box;
} }
.navbar:has( ~ .container .pastepad-container) { /* Wide Layout Override */
.layout-wide .navbar,
.layout-wide .container {
max-width: 1000px; max-width: 1000px;
} }
-4
View File
@@ -70,7 +70,3 @@ details:has(.route-list) {
.queue-form textarea { .queue-form textarea {
resize: vertical; resize: vertical;
} }
.navbar:has(~ .container .queue-title) {
max-width: 800px;
}
+1 -1
View File
@@ -10,7 +10,7 @@
/> />
{% block head %}{% endblock %} {% block head %}{% endblock %}
</head> </head>
<body> <body class="{% block body_class %}{% endblock %}">
<nav class="navbar"> <nav class="navbar">
<a href="/" class="nav-brand">QM</a> <a href="/" class="nav-brand">QM</a>
<div class="nav-links"> <div class="nav-links">
+221
View File
@@ -0,0 +1,221 @@
{% extends "base.html" %} {% block title %}Link Extractor{% endblock %}{% block
body_class %}layout-wide{% endblock %}{% block content %}
<div class="container">
<h1>Link Extractor</h1>
<p>Paste HTML or Markdown below to extract all links.</p>
<div class="card" style="width: 100%; max-width: 800px">
<div style="margin-bottom: 10px; text-align: left">
<label for="file-upload" class="btn btn-sm" style="cursor: pointer"
>📂 Upload File</label
>
<input
type="file"
id="file-upload"
style="display: none"
accept=".html,.htm,.md,.txt,.markdown"
/>
<span
id="file-name"
style="margin-left: 10px; color: #aaa; font-size: 0.9em"
></span>
</div>
<textarea
id="input-content"
class="form-control"
placeholder="Paste content here..."
style="min-height: 200px; font-family: monospace"
></textarea>
<div class="actions" style="margin-top: 15px">
<button onclick="extractLinks('html')" class="btn">
Extract from HTML
</button>
<button onclick="extractLinks('markdown')" class="btn">
Extract from Markdown
</button>
<button onclick="extractLinks('auto')" class="btn btn-primary">
Auto Detect & Extract
</button>
</div>
</div>
<div
id="results-container"
class="card hidden"
style="
width: 100%;
max-width: 800px;
margin-top: 20px;
text-align: left;
"
>
<div
style="
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
"
>
<h2 style="margin: 0">
Found <span id="link-count">0</span> Links
</h2>
<div>
<button onclick="copyAll()" class="btn-sm">Copy All</button>
<button onclick="sendToQueue()" class="btn-sm btn-primary">
Send to Queue
</button>
</div>
</div>
<textarea
id="output-links"
class="form-control"
readonly
style="min-height: 150px; white-space: pre"
></textarea>
</div>
</div>
<script>
// File Upload Handler
/** @type {HTMLInputElement} */
const fileInput = document.getElementById("file-upload");
fileInput.addEventListener("change", function (e) {
/** @type {File} */
const file = e.target.files[0];
if (!file) return;
document.getElementById("file-name").textContent = file.name;
const reader = new FileReader();
reader.onload = function (e) {
/** @type {HTMLTextAreaElement} */
const textarea = document.getElementById("input-content");
textarea.value = e.target.result;
// Optional: Auto-detect mode based on extension
if (
file.name.endsWith(".md") ||
file.name.endsWith(".markdown")
) {
extractLinks("markdown");
} else if (
file.name.endsWith(".html") ||
file.name.endsWith(".htm")
) {
extractLinks("html");
}
};
reader.readAsText(file);
});
/**
* Extracts links from the input content based on the specified mode.
* @param {'html' | 'markdown' | 'auto'} mode
*/
function extractLinks(mode) {
/** @type {string} */
const content = document.getElementById("input-content").value;
if (!content.trim()) return;
/** @type {string[]} */
let links = [];
if (mode === "auto") {
// Simple heuristic: if it has <a href, it's likely HTML.
mode =
content.includes("<a href") ||
content.includes("<!DOCTYPE html>")
? "html"
: "markdown";
}
if (mode === "html") {
const parser = new DOMParser();
const doc = parser.parseFromString(content, "text/html");
const anchors = doc.querySelectorAll("a[href]");
links = Array.from(anchors)
.map((a) => a.href)
.filter((h) => h.startsWith("http"));
} else {
// Markdown regex for [text](url) and <url>
const mdLinkRegex = /\[.*?\]\((https?:\/\/[^\s)]+)\)/g;
const rawLinkRegex = /<(https?:\/\/[^>]+)>/g;
let match;
while ((match = mdLinkRegex.exec(content)) !== null) {
links.push(match[1]);
}
while ((match = rawLinkRegex.exec(content)) !== null) {
links.push(match[1]);
}
// Also catch plain URLs if they are on their own line or space-separated
// This is a bit aggressive, so maybe optional? For now let's stick to explicit MD syntax
// or use a library if we want full robustness.
}
// Deduplicate
links = [...new Set(links)];
displayResults(links);
}
/**
* Displays the extracted links in the results container.
* @param {string[]} links
*/
function displayResults(links) {
const container = document.getElementById("results-container");
/** @type {HTMLTextAreaElement} */
const output = document.getElementById("output-links");
const count = document.getElementById("link-count");
output.value = links.join("\n");
count.textContent = links.length.toString();
container.classList.remove("hidden");
container.scrollIntoView({ behavior: "smooth" });
}
/**
* Copies all extracted links to the clipboard.
*/
function copyAll() {
/** @type {HTMLTextAreaElement} */
const output = document.getElementById("output-links");
output.select();
navigator.clipboard.writeText(output.value);
}
/**
* Sends the extracted links to the queue.
*/
async function sendToQueue() {
/** @type {HTMLTextAreaElement} */
const output = document.getElementById("output-links");
const links = output.value;
if (!links) return;
const formData = new FormData();
formData.append("lines", links);
try {
await fetch("/queuein", {
method: "POST",
body: formData,
});
alert("Links sent to queue!");
} catch (e) {
alert("Failed to send to queue");
}
}
</script>
<style>
.hidden {
display: none;
}
</style>
{% endblock %}
+8
View File
@@ -62,6 +62,14 @@
<a href="/pastepad" class="btn">Visit</a> <a href="/pastepad" class="btn">Visit</a>
</div> </div>
</div> </div>
<div class="card">
<h2>Extractor</h2>
<p>Extract links from HTML or Markdown.</p>
<div class="actions">
<a href="/extractor" class="btn">Visit</a>
</div>
</div>
</div> </div>
<details> <details>
<summary>System Routes</summary> <summary>System Routes</summary>
+6 -2
View File
@@ -1,5 +1,9 @@
{% extends "base.html" %} {% block title %}PastePad - Queue Manager{% endblock {% extends "base.html" %}
%} {% block content %}
{% block title %}PastePad - Queue Manager{% endblock %}
{% block body_class %}layout-wide{% endblock %}
{% block content %}
<div class="pastepad-container"> <div class="pastepad-container">
<h1>PastePad</h1> <h1>PastePad</h1>
Generated
+44
View File
@@ -77,6 +77,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/3e/57/aa70121b5008f44031be645a61a7c4abc24e0e888ad3fc8fda916f4d188e/fastapi-0.124.4-py3-none-any.whl", hash = "sha256:6d1e703698443ccb89e50abe4893f3c84d9d6689c0cf1ca4fad6d3c15cf69f15", size = 113281, upload-time = "2025-12-12T15:00:42.44Z" }, { url = "https://files.pythonhosted.org/packages/3e/57/aa70121b5008f44031be645a61a7c4abc24e0e888ad3fc8fda916f4d188e/fastapi-0.124.4-py3-none-any.whl", hash = "sha256:6d1e703698443ccb89e50abe4893f3c84d9d6689c0cf1ca4fad6d3c15cf69f15", size = 113281, upload-time = "2025-12-12T15:00:42.44Z" },
] ]
[[package]]
name = "filelock"
version = "3.20.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/a7/23/ce7a1126827cedeb958fc043d61745754464eb56c5937c35bbf2b8e26f34/filelock-3.20.1.tar.gz", hash = "sha256:b8360948b351b80f420878d8516519a2204b07aefcdcfd24912a5d33127f188c", size = 19476, upload-time = "2025-12-15T23:54:28.027Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e3/7f/a1a97644e39e7316d850784c642093c99df1290a460df4ede27659056834/filelock-3.20.1-py3-none-any.whl", hash = "sha256:15d9e9a67306188a44baa72f569d2bfd803076269365fdea0934385da4dc361a", size = 16666, upload-time = "2025-12-15T23:54:26.874Z" },
]
[[package]] [[package]]
name = "flask" name = "flask"
version = "3.1.2" version = "3.1.2"
@@ -185,6 +194,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" },
] ]
[[package]]
name = "platformdirs"
version = "4.5.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" },
]
[[package]] [[package]]
name = "pydantic" name = "pydantic"
version = "2.12.5" version = "2.12.5"
@@ -284,6 +302,7 @@ dependencies = [
{ name = "pydantic" }, { name = "pydantic" },
{ name = "python-multipart" }, { name = "python-multipart" },
{ name = "qrcode" }, { name = "qrcode" },
{ name = "urlextract" },
{ name = "uvicorn" }, { name = "uvicorn" },
] ]
@@ -294,6 +313,7 @@ requires-dist = [
{ name = "pydantic" }, { name = "pydantic" },
{ name = "python-multipart" }, { name = "python-multipart" },
{ name = "qrcode" }, { name = "qrcode" },
{ name = "urlextract", specifier = ">=1.9.0" },
{ name = "uvicorn" }, { name = "uvicorn" },
] ]
@@ -330,6 +350,30 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
] ]
[[package]]
name = "uritools"
version = "6.0.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/dd/f7/6651d145bedd535a5bdd6dad108329ec1fec89d38ec611f8d98834eb5378/uritools-6.0.1.tar.gz", hash = "sha256:2f9e9cb954e7877232b2c863f724a44a06eb98d9c7ebdd69914876e9487b94f8", size = 22857, upload-time = "2025-12-21T18:58:54.446Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/d7/e1542857c3f7615a1a9afa6b602b87cb5a33885db41c686aa7bf5092d4f0/uritools-6.0.1-py3-none-any.whl", hash = "sha256:d9507b82206c857d2f93d8fcc84f3b05ae4174096761102be690aa76a360cc1b", size = 10466, upload-time = "2025-12-21T18:58:52.903Z" },
]
[[package]]
name = "urlextract"
version = "1.9.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "filelock" },
{ name = "idna" },
{ name = "platformdirs" },
{ name = "uritools" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e1/1d/57a552551f851c6db5e5d66f505548d47dd3f734a8e648f9d4f05e4e5ac9/urlextract-1.9.0.tar.gz", hash = "sha256:70508e02ba9df372e25cf0642db367cece273e8712cd0ce78178fc5dd7ea00db", size = 35369, upload-time = "2024-02-29T09:14:24.221Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/cb/cb/82ef2994dec66351aaffa45061f30051b6b557bf592952f14657f38b6e43/urlextract-1.9.0-py3-none-any.whl", hash = "sha256:f88963532488b1c7c405e21bd162ae97871754ea04b60e18d33ee075b19b82fd", size = 21460, upload-time = "2024-02-29T09:14:22.297Z" },
]
[[package]] [[package]]
name = "uvicorn" name = "uvicorn"
version = "0.38.0" version = "0.38.0"