AI
This commit is contained in:
1 parent
e61c34bc0d
commit
1911d55d5e
13 files changed
+539
-10
No files matched your search
@@ -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
|
||||||
@@ -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>
|
||||||
@@ -10,5 +10,6 @@ dependencies = [
|
|||||||
"pydantic",
|
"pydantic",
|
||||||
"python-multipart",
|
"python-multipart",
|
||||||
"qrcode",
|
"qrcode",
|
||||||
|
"urlextract>=1.9.0",
|
||||||
"uvicorn",
|
"uvicorn",
|
||||||
]
|
]
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -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
@@ -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">
|
||||||
|
|||||||
@@ -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 %}
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
Reference in new issue
Block a user