working
BiRabittoh birabittoh@tilde.team
Fri, 04 Aug 2023 19:04:50 +0200
6 files changed,
64 insertions(+),
13 deletions(-)
M
README.md
→
README.md
@@ -1,3 +1,12 @@
# artbound-python -A client-server reimplementation of the administration panel for ArtBound.+A client-server reimplementation of the administration panel for ArtBound. + +## Instructions +1. Copy `.env.example` into `.env` and fill it out; +2. Generate a `credentials.json` with Drive and Sheets APIs and the following redirect URL: `http://localhost:1111`; +3. Install dependencies: `poetry install`; + +## Usage +* Debug: `poetry run flask --app artbound_python run --port 1111 --debug`. +* Production: `poetry run waitress-serve --host 0.0.0.0 --port 1111 artbound_python:app`
M
artbound_python/static/script.js
→
artbound_python/static/script.js
@@ -15,7 +15,8 @@ togglecolor_button = document.getElementById("togglecolor_button"),
controls_div = document.getElementById("controls"), opacity_range = document.getElementById("opacity_range"), main_container_div = document.getElementById("main_container"), - content_div = document.getElementById("content"); + content_div = document.getElementById("content"), + canvas_link = document.getElementById("canvas-download"); let fanarts = new Array(), watermark_invert = '';@@ -46,7 +47,9 @@ div4 = document.createElement("div"),
button1 = document.createElement("button"), button2 = document.createElement("button"), button3 = document.createElement("button"), - button4 = document.createElement("button"); + button4 = document.createElement("button"), + button5 = document.createElement("button"), + filename = `${('0' + element.index).slice(-2)} - ${element.name}.png`; element.div.className = `col-md-${BS_COL_WIDTH} entry${element.enabled == 0 ? " entry-disabled" : ""}`; element.div.id = `div-${element.id}`;@@ -56,28 +59,32 @@ element.canvas.className = "card-img-top entry-img";
element.canvas.id = element.id; element.canvas.setAttribute("data-name", element.name); element.canvas.setAttribute("data-content", element.content); + element.canvas.setAttribute("data-filename", filename) div2.className = "card-body"; a.className = "card-text"; - a.innerText = `${('0' + element.index).slice(-2)} - ${element.name}.png`; + a.innerText = filename; a.title = "Clicca per copiare." a.addEventListener("click", function() { navigator.clipboard.writeText(a.innerText); }, false) div3.className = "d-flex justify-content-between align-items-center card-controls"; div4.className = "btn-group"; - button1.className = button2.className = button3.className = button4.className = "btn btn-sm btn-outline-secondary"; + button1.className = button2.className = button3.className = button4.className = button5.className = "btn btn-sm btn-outline-secondary"; button1.innerText = "⬅️"; button2.innerText = "*️⃣"; button3.innerText = "🔄"; - button4.innerText = "➡️"; + button5.innerText = "➡️"; + button4.innerText = "💾"; button1.addEventListener("click", function() { moveUpDown(element.id, -1); }, false); button2.addEventListener("click", function() { toggleEntry(element.id); }, false); button3.addEventListener("click", function() { reloadEntry(element.id); }, false); - button4.addEventListener("click", function() { moveUpDown(element.id, 1); }, false); + button4.addEventListener("click", function() { saveCanvas(element.id); }, false); + button5.addEventListener("click", function() { moveUpDown(element.id, 1); }, false); div4.appendChild(button1); div4.appendChild(button2); div4.appendChild(button3); div4.appendChild(button4); + div4.appendChild(button5); div3.appendChild(div4); div2.appendChild(a); div2.appendChild(div3);@@ -101,7 +108,7 @@ }
async function updateFanartList() { main_container_div.hidden = false; - //content_div.innerHTML = "" + content_div.innerHTML = "" let i = 0; for (fanart of fanarts) {@@ -130,6 +137,16 @@ if (!entry) return;
entry.enabled = !entry.enabled; updateFanartList() +} + +function saveCanvas(id) { + entry = getFanart(id); + if (!entry) return; + + const image = entry.canvas.toDataURL("image/png").replace("image/png", "image/octet-stream"); + canvas_link.href = image; + canvas_link.setAttribute("download", entry.canvas.getAttribute("data-filename")); + canvas_link.click() } function reloadEntry(id){@@ -214,9 +231,11 @@
function getArtworks() { postData("/", { month: month_input.value }, "application/json").then((data) => { console.log(data); - fanarts = data; + fanarts = fanarts.concat(data); controls_div.hidden = false; updateOpacity(); updateFanartList(); }); } + +
M
artbound_python/templates/index.html
→
artbound_python/templates/index.html
@@ -14,6 +14,7 @@ <link href="/static/style.css" rel="stylesheet">
</head> <body> + <a id="canvas-download" hidden></a> <main role="main"> <section class="jumbotron text-center"> <div class="container">@@ -24,7 +25,7 @@ class="feather feather-activity">
<polygon points="14 2 18 6 7 17 3 17 3 13 14 2"></polygon> <line x1="3" y1="22" x2="21" y2="22"></line> </svg> - ArtBound Panel 2.0 + ArtBound Panel 1.5 </h1> <a href="/update" class="lead">{{ last_updated }}</a> <div id="month_div">
M
artbound_python/views.py
→
artbound_python/views.py
@@ -1,7 +1,7 @@
import json from flask import request, redirect, render_template from artbound_python import app -from artbound_python.cache import last_updated, DB +from artbound_python.cache import last_updated, DB, clear_cache database = DB()@@ -20,4 +20,10 @@
@app.route('/update') def route_update(): database.update_database() - return redirect("/")+ return redirect("/") + +@app.route('/clear') +def route_clear(): + clear_cache() + return redirect("/") +
M
poetry.lock
→
poetry.lock
@@ -575,6 +575,21 @@ secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] +name = "waitress" +version = "2.1.2" +description = "Waitress WSGI server" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "waitress-2.1.2-py3-none-any.whl", hash = "sha256:7500c9625927c8ec60f54377d590f67b30c8e70ef4b8894214ac6e4cad233d2a"}, + {file = "waitress-2.1.2.tar.gz", hash = "sha256:780a4082c5fbc0fde6a2fcfe5e26e6efc1e8f425730863c04085769781f51eba"}, +] + +[package.extras] +docs = ["Sphinx (>=1.8.1)", "docutils", "pylons-sphinx-themes (>=1.0.9)"] +testing = ["coverage (>=5.0)", "pytest", "pytest-cover"] + +[[package]] name = "werkzeug" version = "2.3.6" description = "The comprehensive WSGI web application library."@@ -594,4 +609,4 @@
[metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "cb1107f6d454cb00e9b6555731384542b5857a6b3c8ff8d9b9981eee533b5ac2" +content-hash = "a525faa662df61925931db4f337e435cab2d4875dcc954107776f1798c382831"
M
pyproject.toml
→
pyproject.toml
@@ -14,6 +14,7 @@ google-auth-httplib2 = "^0.1.0"
google-auth-oauthlib = "^1.0.0" requests = "^2.31.0" python-dotenv = "^1.0.0" +waitress = "^2.1.2" [build-system]