Files
qrcode-from-onetab/templates/extractor.html
T
2025-12-25 23:25:12 +07:00

222 lines
6.9 KiB
HTML

{% 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 %}