Compare commits
6
Commits
bc1fc4ea61
...
d5f578cfd0
No files matched your search
@@ -1,6 +1,4 @@
|
|||||||
def main():
|
import uvicorn
|
||||||
print("Hello from qrcode-from-onetab!")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
uvicorn.run("uhhhhup:app", host="0.0.0.0", port=2222, reload=True)
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
:root {
|
||||||
|
--bg-color: #121212;
|
||||||
|
--text-color: #e0e0e0;
|
||||||
|
--primary-color: #bb86fc;
|
||||||
|
--secondary-color: #03dac6;
|
||||||
|
--error-color: #cf6679;
|
||||||
|
--surface-color: #1e1e1e;
|
||||||
|
--border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--bg-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100dvh;
|
||||||
|
box-sizing: border-box; /* Ensure padding is included in width/height */
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input[type="submit"],
|
||||||
|
.btn {
|
||||||
|
background-color: var(--surface-color);
|
||||||
|
color: var(--primary-color);
|
||||||
|
border: 1px solid var(--primary-color);
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: background-color 0.2s, color 0.2s;
|
||||||
|
margin: 5px;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover,
|
||||||
|
input[type="submit"]:hover,
|
||||||
|
.btn:hover {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
color: var(--bg-color);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
background-color: var(--surface-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
border: 1px solid #333;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
padding: 10px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 800px;
|
||||||
|
font-family: monospace;
|
||||||
|
min-width: 20%;
|
||||||
|
min-height: 20dvh;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 800px;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 800px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
background-color: var(--surface-color);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
word-break: break-all;
|
||||||
|
width: min-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
li a {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qrcode {
|
||||||
|
margin-top: 10px;
|
||||||
|
background-color: white;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Specifics for reader */
|
||||||
|
#b {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
padding: 20px 40px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Specifics for index */
|
||||||
|
.hero {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 20px;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1000px;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background-color: var(--surface-color);
|
||||||
|
padding: 30px;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
flex: 1;
|
||||||
|
min-width: 300px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
border: 1px solid #333;
|
||||||
|
/* max-height: 90dvh; Prevent card from exceeding viewport height */
|
||||||
|
overflow-y: auto; /* Scroll if content is too tall */
|
||||||
|
}
|
||||||
|
|
||||||
|
.card h2 {
|
||||||
|
color: var(--secondary-color);
|
||||||
|
margin-top: 0;
|
||||||
|
/* margin-bottom: 10px; Reduced margin */
|
||||||
|
/* font-size: 1.2rem; Slightly smaller title */
|
||||||
|
}
|
||||||
|
|
||||||
|
.card p {
|
||||||
|
margin-bottom: 15px; /* Reduced margin */
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card .actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
details {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
|
margin-top: 20px;
|
||||||
|
background-color: var(--surface-color);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
border: 1px solid #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary {
|
||||||
|
padding: 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--secondary-color);
|
||||||
|
list-style: none; /* Hide default triangle */
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary::after {
|
||||||
|
content: '+';
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
details[open] summary::after {
|
||||||
|
content: '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
details[open] summary {
|
||||||
|
border-bottom: 1px solid #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.route-list {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 15px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.route-item {
|
||||||
|
background-color: var(--surface-color);
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Queue Grid */
|
||||||
|
.queue-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 20px;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.queue-grid li {
|
||||||
|
flex: 1 1 300px; /* Grow, shrink, basis */
|
||||||
|
max-width: 400px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.queue-grid li a {
|
||||||
|
align-items: center;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Trail Page */
|
||||||
|
.trail-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 80dvh;
|
||||||
|
width: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trail-count {
|
||||||
|
font-size: 5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--secondary-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.trail-hint {
|
||||||
|
color: #666;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
+42
-16
@@ -3,23 +3,49 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Queue Reader</title>
|
<title>Document</title>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="{{ url_for('static', path='style.css') }}"
|
||||||
|
/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{% from 'macros.html' import magic_link %} {% if current_link %}
|
<h1>Queue Manager</h1>
|
||||||
<p>{{ magic_link(current_link, current_link, "a") }}</p>
|
|
||||||
<button id="b">skip/fw</button>
|
<div class="hero">
|
||||||
<script>
|
<div class="card">
|
||||||
document.getElementById("b").addEventListener("click", async () => {
|
<h2>Input (PC)</h2>
|
||||||
await fetch("/r", { method: "GET", redirect: "manual" });
|
<p>
|
||||||
setTimeout(() => {
|
Paste your OneTab export or list of links here to add them
|
||||||
window.location.reload();
|
to the queue.
|
||||||
});
|
</p>
|
||||||
});
|
<div class="actions">
|
||||||
</script>
|
<a href="/queuein" class="btn">Add Links</a>
|
||||||
{% else %}
|
</div>
|
||||||
<p>no</p>
|
</div>
|
||||||
<button onclick="location.reload();">reload</button>
|
|
||||||
{% endif %}
|
<div class="card">
|
||||||
|
<h2>Output (Mobile)</h2>
|
||||||
|
<p>Open these on your phone to consume the queue.</p>
|
||||||
|
<div class="actions">
|
||||||
|
<a href="/q" class="btn">Reader (QR)</a>
|
||||||
|
<a href="/t" class="btn">Trail (Focus)</a>
|
||||||
|
<a href="/r" class="btn">Next Link</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<details >
|
||||||
|
<summary>System Routes</summary>
|
||||||
|
<div class="route-list">
|
||||||
|
{% macro this(route) %}
|
||||||
|
<div class="route-item">
|
||||||
|
<a href="{{ url_for(route.name) }}">{{ route.name }}</a>
|
||||||
|
<span>{{ route.path }}</span>
|
||||||
|
</div>
|
||||||
|
{% endmacro %} {% for route in routes if "GET" in route.methods %}
|
||||||
|
{{ this(route) }} {% endfor %}
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
{% endmacro %} {% macro qrcode_queue(queue_items) %} {% if queue_items %}
|
{% endmacro %} {% macro qrcode_queue(queue_items) %} {% if queue_items %}
|
||||||
<ol>
|
<ol class="queue-grid">
|
||||||
{% for item in queue_items %}
|
{% for item in queue_items %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{{ item }}" target="_blank" rel="noreferrer noopener">
|
<a href="{{ item }}" target="_blank" rel="noreferrer noopener">
|
||||||
@@ -33,14 +33,6 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<style>
|
|
||||||
/* it works ig */
|
|
||||||
& {
|
|
||||||
li:has(.qrcode) {
|
|
||||||
width: min-content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</ol>
|
</ol>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/[email protected]/qrcode.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/[email protected]/qrcode.js"></script>
|
||||||
<script>
|
<script>
|
||||||
@@ -50,6 +42,8 @@
|
|||||||
new QRCode(e, {
|
new QRCode(e, {
|
||||||
text: a,
|
text: a,
|
||||||
correctLevel: QRCode.CorrectLevel.L,
|
correctLevel: QRCode.CorrectLevel.L,
|
||||||
|
width: 256,
|
||||||
|
height: 256,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+13
-14
@@ -4,11 +4,6 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Queue dump type shit</title>
|
<title>Queue dump type shit</title>
|
||||||
<style>
|
|
||||||
li {
|
|
||||||
width: min-content;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@@ -34,9 +29,9 @@
|
|||||||
<script>
|
<script>
|
||||||
/** @type {HTMLDivElement} */
|
/** @type {HTMLDivElement} */
|
||||||
const bij = document.getElementById("gonk");
|
const bij = document.getElementById("gonk");
|
||||||
let arrlen = 0;
|
|
||||||
/** @type {HTMLInputElement} */
|
/** @type {HTMLInputElement} */
|
||||||
const n = document.getElementById("n");
|
const n = document.getElementById("n");
|
||||||
|
|
||||||
const changeHandler = async () => {
|
const changeHandler = async () => {
|
||||||
const number = parseInt(n.value);
|
const number = parseInt(n.value);
|
||||||
if (isNaN(number)) {
|
if (isNaN(number)) {
|
||||||
@@ -46,12 +41,11 @@
|
|||||||
const f = new URL("/api/queue", window.location.href);
|
const f = new URL("/api/queue", window.location.href);
|
||||||
f.searchParams.append("n", number);
|
f.searchParams.append("n", number);
|
||||||
console.log("fetching", f);
|
console.log("fetching", f);
|
||||||
const c = await fetch(f)
|
return await fetch(f)
|
||||||
.then((r) => r.json())
|
.then((r) => r.json())
|
||||||
.then((n) => {
|
.then((n) => {
|
||||||
console.log(n);
|
console.log(n);
|
||||||
arrlen = n.length;
|
if (n.length > 0) {
|
||||||
if (n) {
|
|
||||||
const ol = document.createElement("ol");
|
const ol = document.createElement("ol");
|
||||||
for (const i of n) {
|
for (const i of n) {
|
||||||
const li = document.createElement("li");
|
const li = document.createElement("li");
|
||||||
@@ -64,7 +58,7 @@
|
|||||||
ol.appendChild(li);
|
ol.appendChild(li);
|
||||||
}
|
}
|
||||||
bij.textContent = `next ${
|
bij.textContent = `next ${
|
||||||
n.length != 1
|
n.length > 1
|
||||||
? `${n.length} entries are`
|
? `${n.length} entries are`
|
||||||
: "entry is"
|
: "entry is"
|
||||||
} SO gone`;
|
} SO gone`;
|
||||||
@@ -72,26 +66,31 @@
|
|||||||
} else {
|
} else {
|
||||||
bij.textContent = "wym empty";
|
bij.textContent = "wym empty";
|
||||||
}
|
}
|
||||||
|
return n.length;
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
arrlen = 0;
|
|
||||||
bij.textContent = `shit. cant fetch`;
|
bij.textContent = `shit. cant fetch`;
|
||||||
|
return 0;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
n.addEventListener("change", changeHandler);
|
n.addEventListener("change", changeHandler);
|
||||||
|
|
||||||
/** @type {HTMLFormElement} */
|
/** @type {HTMLFormElement} */
|
||||||
const c = document.getElementById("c");
|
const c = document.getElementById("c");
|
||||||
c.addEventListener("submit", async (e) => {
|
c.addEventListener("submit", async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
await changeHandler();
|
await changeHandler();
|
||||||
});
|
});
|
||||||
|
|
||||||
changeHandler();
|
changeHandler();
|
||||||
|
|
||||||
const s = document.getElementById("s");
|
const s = document.getElementById("s");
|
||||||
s.addEventListener("click", (e) => {
|
s.addEventListener("click", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
console.log("ts runs");
|
console.log("ts runs");
|
||||||
changeHandler();
|
const arrlen = changeHandler();
|
||||||
for (const _ of Array(arrlen)) {
|
for (const _ of Array(arrlen)) {
|
||||||
const w = window.open(
|
const w = window.open(
|
||||||
"/r",
|
"/r",
|
||||||
@@ -103,11 +102,12 @@
|
|||||||
arrlen = 0;
|
arrlen = 0;
|
||||||
bij.textContent = "something idk";
|
bij.textContent = "something idk";
|
||||||
});
|
});
|
||||||
|
|
||||||
/** @type {HTMLButtonElement} */
|
/** @type {HTMLButtonElement} */
|
||||||
const all = document.getElementById("all");
|
const all = document.getElementById("all");
|
||||||
all.addEventListener(
|
all.addEventListener(
|
||||||
"click",
|
"click",
|
||||||
async () =>
|
async (e) =>
|
||||||
await fetch("/api/queue/len")
|
await fetch("/api/queue/len")
|
||||||
.then((d) => d.json())
|
.then((d) => d.json())
|
||||||
.then((len) => {
|
.then((len) => {
|
||||||
@@ -120,7 +120,6 @@
|
|||||||
);
|
);
|
||||||
console.log(w);
|
console.log(w);
|
||||||
}
|
}
|
||||||
arrlen = 0;
|
|
||||||
bij.textContent = "everything idk";
|
bij.textContent = "everything idk";
|
||||||
})
|
})
|
||||||
.catch((e) => console.error(e))
|
.catch((e) => console.error(e))
|
||||||
|
|||||||
+15
-13
@@ -4,32 +4,34 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Queue Input</title>
|
<title>Queue Input</title>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="{{ url_for('static', path='style.css') }}"
|
||||||
|
/>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div style="display: flex; align-items: center; justify-content: space-between; width: 100%; max-width: 800px; margin-bottom: 20px;">
|
||||||
|
<h1 style="margin: 0;">Add Links</h1>
|
||||||
|
<a href="/" class="btn" style="margin: 0;">← Home</a>
|
||||||
|
</div>
|
||||||
<form method="POST" action="/queuein">
|
<form method="POST" action="/queuein">
|
||||||
<label for="lines">in:</label>
|
<textarea id="lines" rows="20" cols="100" name="lines"></textarea>
|
||||||
<textarea
|
|
||||||
id="l"
|
|
||||||
rows="20"
|
|
||||||
cols="100"
|
|
||||||
style="min-width: 20%; min-height: 20dvh"
|
|
||||||
name="lines"
|
|
||||||
></textarea>
|
|
||||||
<input type="submit" value="ahh" />
|
<input type="submit" value="ahh" />
|
||||||
</form>
|
</form>
|
||||||
<script>
|
<script>
|
||||||
/**@type {HTMLTextAreaElement} */
|
|
||||||
(
|
|
||||||
// lifted from google search ai
|
// lifted from google search ai
|
||||||
document.getElementById("l")
|
/**@type {HTMLTextAreaElement} */
|
||||||
).addEventListener("keydown", function (e) {
|
(document.getElementById("lines")).addEventListener(
|
||||||
|
"keydown",
|
||||||
|
function (e) {
|
||||||
// Check for Ctrl+Enter (Windows/Linux) or Command+Enter (Mac)
|
// Check for Ctrl+Enter (Windows/Linux) or Command+Enter (Mac)
|
||||||
if ((e.ctrlKey || e.metaKey) && e.key === "Enter") {
|
if ((e.ctrlKey || e.metaKey) && e.key === "Enter") {
|
||||||
e.preventDefault(); // Prevent the default newline character
|
e.preventDefault(); // Prevent the default newline character
|
||||||
// Submit the form associated with this textarea
|
// Submit the form associated with this textarea
|
||||||
this.form.submit();
|
this.form.submit();
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
{% from "macros.html" import qrcode_queue %} {% if queue_items %}
|
{% from "macros.html" import qrcode_queue %} {% if queue_items %}
|
||||||
<p>total: {{ queue_items|length }}</p>
|
<p>total: {{ queue_items|length }}</p>
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Queue Reader</title>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="{{ url_for('static', path='style.css') }}"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{% from 'macros.html' import magic_link %} {% if current_link %}
|
||||||
|
<div class="card">
|
||||||
|
<h2>Current Link</h2>
|
||||||
|
<p>{{ magic_link(current_link, current_link, "a") }}</p>
|
||||||
|
|
||||||
|
<div class="qrcode" id="current-qr"></div>
|
||||||
|
|
||||||
|
<div class="actions" style="margin-top: 20px">
|
||||||
|
<button id="b" class="btn">Skip / Next</button>
|
||||||
|
</div>
|
||||||
|
<p style="margin-top: 10px; font-size: 0.8rem; color: #666">
|
||||||
|
{{ queue_length }} items remaining
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/[email protected]/qrcode.js"></script>
|
||||||
|
<script>
|
||||||
|
// QR Code generation
|
||||||
|
new QRCode(document.getElementById("current-qr"), {
|
||||||
|
text: "{{ current_link }}",
|
||||||
|
correctLevel: QRCode.CorrectLevel.L,
|
||||||
|
width: 256,
|
||||||
|
height: 256,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Skip button logic
|
||||||
|
document.getElementById("b").addEventListener("click", async () => {
|
||||||
|
await fetch("/r", { redirect: "manual" });
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% else %}
|
||||||
|
<div class="card">
|
||||||
|
<h2>Queue Empty</h2>
|
||||||
|
<p>No links in the queue. Go add some!</p>
|
||||||
|
<div class="actions">
|
||||||
|
<a href="/" class="btn">Home</a>
|
||||||
|
<a href="/queuein" class="btn">Add Links</a>
|
||||||
|
<a href="javascript:location.reload()" class="btn">Reload</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Trail</title>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="{{ url_for('static', path='style.css') }}"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{% if queue_length > 0 %}
|
||||||
|
<div class="trail-container">
|
||||||
|
<div class="trail-count">{{ queue_length }}</div>
|
||||||
|
<p>Items Left</p>
|
||||||
|
|
||||||
|
<p
|
||||||
|
style="
|
||||||
|
max-width: 80%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
Next:
|
||||||
|
<a
|
||||||
|
href="{{ current_link }}"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer noopener"
|
||||||
|
>{{ current_link }}</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="trail-hint">Tap anywhere to open & next</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
if (window.opener) {
|
||||||
|
// new tab = window open no "noopener". this probs does exist
|
||||||
|
console.log(/** @type {Window} */ (window.opener)); // hi hello hi
|
||||||
|
}
|
||||||
|
window.addEventListener("load", () => {
|
||||||
|
document.body.addEventListener("click", async (e) => {
|
||||||
|
if (e.target.tagName === "A") return;
|
||||||
|
|
||||||
|
// do trix
|
||||||
|
await fetch("/r", { redirect: "manual" });
|
||||||
|
|
||||||
|
// now this is the new updated
|
||||||
|
const newTab = window.open(window.location.href, "_blank");
|
||||||
|
|
||||||
|
// yo this is good no security error
|
||||||
|
window.location.replace("{{ current_link }}");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% else %}
|
||||||
|
<div class="card">
|
||||||
|
<h2>All Done!</h2>
|
||||||
|
<p>The queue is empty.</p>
|
||||||
|
<div class="actions">
|
||||||
|
<a href="/" class="btn">Home</a>
|
||||||
|
<a href="/queuein" class="btn">Add More</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
"why outdated"
|
||||||
|
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
|
||||||
import flask
|
import flask
|
||||||
@@ -13,11 +15,11 @@ a = flask.Flask(__name__)
|
|||||||
queue: deque[str] = deque()
|
queue: deque[str] = deque()
|
||||||
|
|
||||||
|
|
||||||
@a.route("/")
|
@a.route("/q")
|
||||||
def no():
|
def no():
|
||||||
"if i click the link then it proceed. if i reload it doesnt."
|
"if i click the link then it proceed. if i reload it doesnt."
|
||||||
current_link = queue[0] if queue else None
|
current_link = queue[0] if queue else None
|
||||||
return flask.render_template("index.html", current_link=current_link)
|
return flask.render_template("reader.html", current_link=current_link)
|
||||||
|
|
||||||
|
|
||||||
@a.route("/r")
|
@a.route("/r")
|
||||||
@@ -26,7 +28,7 @@ def redir():
|
|||||||
if queue:
|
if queue:
|
||||||
q = queue.popleft()
|
q = queue.popleft()
|
||||||
return flask.redirect(q)
|
return flask.redirect(q)
|
||||||
return flask.render_template("index.html", current_link=None)
|
return flask.render_template("reader.html", current_link=None)
|
||||||
|
|
||||||
|
|
||||||
@a.route("/queuein", methods=["GET", "POST"])
|
@a.route("/queuein", methods=["GET", "POST"])
|
||||||
|
|||||||
+32
-8
@@ -6,11 +6,13 @@ from itertools import islice
|
|||||||
import uvicorn
|
import uvicorn
|
||||||
from fastapi import FastAPI, Form, Request
|
from fastapi import FastAPI, Form, Request
|
||||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||||
|
from fastapi.staticfiles import StaticFiles
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||||
templates = Jinja2Templates(directory="templates")
|
templates = Jinja2Templates(directory="templates")
|
||||||
queue: deque[str] = deque() # lifo ahh
|
queue: deque[str] = deque() # fifo ahh
|
||||||
|
|
||||||
|
|
||||||
def link_extract_from_onetab(line):
|
def link_extract_from_onetab(line):
|
||||||
@@ -20,10 +22,28 @@ def link_extract_from_onetab(line):
|
|||||||
|
|
||||||
|
|
||||||
@app.get("/", response_class=HTMLResponse)
|
@app.get("/", response_class=HTMLResponse)
|
||||||
|
async def index(request: Request):
|
||||||
|
"idgaf"
|
||||||
|
return templates.TemplateResponse(request, "index.html", {"routes": app.routes})
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/q", response_class=HTMLResponse)
|
||||||
async def no(request: Request):
|
async def no(request: Request):
|
||||||
current_link = queue[0] if queue else None
|
current_link = queue[0] if queue else None
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request, "index.html", {"current_link": current_link}
|
request,
|
||||||
|
"reader.html",
|
||||||
|
{"current_link": current_link, "queue_length": len(queue)},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/t", response_class=HTMLResponse)
|
||||||
|
async def trail(request: Request):
|
||||||
|
current_link = queue[0] if queue else None
|
||||||
|
return templates.TemplateResponse(
|
||||||
|
request,
|
||||||
|
"trail.html",
|
||||||
|
{"current_link": current_link, "queue_length": len(queue)},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -32,22 +52,26 @@ async def redir(request: Request):
|
|||||||
if queue:
|
if queue:
|
||||||
q = queue.popleft()
|
q = queue.popleft()
|
||||||
return RedirectResponse(url=q)
|
return RedirectResponse(url=q)
|
||||||
return templates.TemplateResponse(request, "index.html", {"current_link": None})
|
return templates.TemplateResponse(
|
||||||
|
request, "reader.html", {"current_link": None, "queue_length": len(queue)}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/m", response_class=HTMLResponse)
|
@app.get("/m", response_class=HTMLResponse)
|
||||||
async def mlti(request: Request):
|
async def mlti(request: Request):
|
||||||
"request first new style"
|
"""request first new style.
|
||||||
|
js still breaking (on mobile. they dont like mass popups) im so sad
|
||||||
|
"""
|
||||||
return templates.TemplateResponse(request, "multi.html")
|
return templates.TemplateResponse(request, "multi.html")
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api/queue")
|
@app.get("/api/queue")
|
||||||
async def queueview(n: int | None = None, s: int | None = None):
|
async def queue_view(n: int | None = None, s: int | None = None):
|
||||||
return list(islice(queue, s, n))
|
return list(islice(queue, s, n))
|
||||||
|
|
||||||
|
|
||||||
@app.get("/api/queue/len")
|
@app.get("/api/queue/len")
|
||||||
async def queuelen():
|
async def queue_len():
|
||||||
return len(queue)
|
return len(queue)
|
||||||
|
|
||||||
|
|
||||||
@@ -57,14 +81,14 @@ async def qrcodequeue(request: Request):
|
|||||||
|
|
||||||
|
|
||||||
@app.get("/queuein", response_class=HTMLResponse)
|
@app.get("/queuein", response_class=HTMLResponse)
|
||||||
async def cuh_get(request: Request):
|
async def queue_in(request: Request):
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request, "queue.html", {"queue_items": list(queue)}
|
request, "queue.html", {"queue_items": list(queue)}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.post("/queuein")
|
@app.post("/queuein")
|
||||||
async def cuh_post(lines: str = Form("")):
|
async def send_queue(lines: str = Form("")):
|
||||||
"""var name == form ele name (name="lines")"""
|
"""var name == form ele name (name="lines")"""
|
||||||
queue.extend(
|
queue.extend(
|
||||||
link_extract_from_onetab(line) for line in lines.splitlines() if line.strip()
|
link_extract_from_onetab(line) for line in lines.splitlines() if line.strip()
|
||||||
|
|||||||
Reference in new issue
Block a user