all repos — artbound-python @ bb331c5170916a5958ae4d3054d7bec96df5d84b

A client-server reimplementation of the administration panel for ArtBound.

polish, add fanart rotation
BiRabittoh birabittoh@tilde.team
Sat, 12 Aug 2023 15:41:35 +0200
commit

bb331c5170916a5958ae4d3054d7bec96df5d84b

parent

59880ee3e9ba90fe6f77fc0463a617aec3f8907d

M artbound_python/cache.pyartbound_python/cache.py

@@ -8,9 +8,6 @@

CACHE_DIRECTORY = "cache" CACHE_PATH = os.path.join("artbound_python", "static", "res", CACHE_DIRECTORY) -db = [] -last_updated = "" - with suppress(FileExistsError): os.makedirs(CACHE_PATH)

@@ -35,13 +32,10 @@ return fanart

def handle_row(row): fanart_date = datetime.strptime(row[0], "%d/%m/%Y %H.%M.%S") - fanart_id = row[3][33:] return { - 'id': fanart_id, + 'id': row[3][33:], 'date': fanart_date.strftime("%Y-%m"), 'name': row[1], - 'enabled': 1, - 'watermark': { 'invert': '' } } class DB():
M artbound_python/static/script.jsartbound_python/static/script.js

@@ -23,7 +23,8 @@ opacity_range = document.getElementById("opacity_range"),

content_div = document.getElementById("content"), canvas_link = document.getElementById("canvas-download"), canvas_ig = document.getElementById("instagram-canvas"), - fanart_template = document.getElementById("fanart-template").innerHTML; + fanart_template = document.getElementById("fanart-template").innerHTML, + radians_factor = 90 * Math.PI / 180; let new_entries = 0;

@@ -45,7 +46,7 @@ }

element.canvas.addEventListener('mousemove', abc); element.canvas.addEventListener('mouseleave', () => { if (!element.clicked && element.enabled) - setBaseImage(element.image, element.canvas, ctx); + setBaseImage(element); }); element.canvas.addEventListener('mousedown', (e) => { element.clicked = true;

@@ -74,7 +75,7 @@ const ctx = element.canvas.getContext("2d");

element.image = new Image(); element.image.addEventListener("load", () => { const wm_info = element.watermark; - setBaseImage(element.image, element.canvas, ctx); + setBaseImage(element); if (element.clicked) drawWatermark(wm_info, ctx); addCanvasEvents(element, ctx);

@@ -113,6 +114,7 @@ entry = getFanart(id);

if (!entry) return; entry.enabled = !entry.enabled; + entry.clicked = false; updateFanartList(); }

@@ -150,10 +152,10 @@ ctx.drawImage(WATERMARK, wm_info.x - (WATERMARK_WIDTH / 2), wm_info.y - (WATERMARK_HEIGHT / 2), WATERMARK_WIDTH, WATERMARK_HEIGHT);

} function addWatermark(event, element, ctx) { - setBaseImage(element.image, element.canvas, ctx); - const [x, y] = clickCoordsToCanvas(event.clientX, event.clientY, element.canvas); + setBaseImage(element); + let [x, y] = clickCoordsToCanvas(event.clientX, event.clientY, element.canvas); element.watermark.x = x; - element.watermark.y = y + element.watermark.y = y; drawWatermark(element.watermark, ctx); }

@@ -165,12 +167,37 @@ function getFactor(img_width, img_height, max_width, max_height) {

return Math.min(max_width / img_width, max_height / img_height); } -function setBaseImage(img, c, ctx) { +function setBaseImage(element) { + const img = element.image; + const c = element.canvas; + const ctx = c.getContext("2d"); const f = getFactor(img.width, img.height, MAX_WIDTH, MAX_HEIGHT); - const new_width = c.width = Math.ceil(img.width * f); - const new_height = c.height = Math.ceil(img.height * f) ctx.imageSmoothingEnabled = (f < 1); - ctx.drawImage(img, 0, 0, new_width, new_height); + + const pre_width = Math.ceil(img.width * f); + const pre_height = Math.ceil(img.height * f); + + if (element.rotated % 2) [c.width, c.height] = [pre_height, pre_width]; + else [c.width, c.height] = [pre_width, pre_height]; + + const rotation_radians = radians_factor * element.rotated; + ctx.rotate(rotation_radians); + + offset_x = (element.rotated == 2 || element.rotated == 3) ? -pre_width : 0; + offset_y = (element.rotated == 1 || element.rotated == 2) ? -pre_height : 0; + ctx.drawImage(img, offset_x, offset_y, pre_width, pre_height); + ctx.rotate(-rotation_radians); +} + +function rotateEntry(id) { + const entry = fanarts.find(element => element.id == id); + if (!entry) return; + if (!entry.enabled) return; + + entry.rotated++; + if (entry.rotated >= 4 || entry.rotated < 0) entry.rotated = 0; + entry.clicked = false; + updateFanartList(); } function moveUpDown(id, amount) {

@@ -192,7 +219,7 @@ if (!entry.enabled) return;

entry.watermark.invert = entry.watermark.invert == '' ? 'invert(1)' : ''; button.innerText = entry.watermark.invert ? emoji_color_black : emoji_color; const ctx = entry.canvas.getContext('2d'); - setBaseImage(entry.image, entry.canvas, ctx); + setBaseImage(entry); drawWatermark(entry.watermark, ctx); }

@@ -233,7 +260,16 @@ if (months.has(month_value)) return;

get_button.disabled = true; get_button.innerText = "🔄" postData("/", { month: month_value }).then((data) => { - fanarts.push(...data); + + const new_data = data.map((element) => { + return { + ...element, + "enabled": 1, + "rotated": 0, + "watermark": { invert: "" }, + }; + }); + fanarts.push(...new_data); controls_div.hidden = false; updateOpacity(); updateFanartList();
A artbound_python/static/simple.html

@@ -0,0 +1,65 @@

+<!DOCTYPE html> + +<head> + <title>ArtBound Panel 2.0</title> + <style> + .container { + width: 100%; + display: grid; + grid-template-columns: repeat(4, 1fr); + grid-template-rows: repeat(auto-fill, 120px); + grid-row-gap: .5em; + grid-column-gap: 1em; + } + </style> +</head> + +<body> + <div id="app" class="container"> + </div> + + <button onclick="addTemplate()">Add element</button> + + <template id="item_template"> + <div class="item" style="background: {{color}}"> + <p>{{ num }}</p> + <p> + <button class="del_el" onClick="deleteElement(this)">Delete</button> + </p> + </div> + </template> + <script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.3.0/mustache.min.js"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script> + <script> + const COLORS = [ + '#FE9', + '#9AF', + '#F9A', + "#AFA", + "#FA7" + ]; + + function addItem(container, template) { + let color = COLORS[_.random(COLORS.length - 1)]; + let num = _.random(10000); + + node = Mustache.render(template, { color, num }); + container.insertAdjacentHTML('beforeend', node); + } + + function deleteElement(element) { + element.parentNode.parentNode.remove(); + } + + const tmpl = document.getElementById('item_template').innerHTML; + const container = document.getElementById('app'); + + function addTemplate() { + addItem(container, tmpl); + } + + for (let i = 0; i < 5; i++) { + addTemplate() + } + </script> +</body>
M artbound_python/templates/help.htmlartbound_python/templates/help.html

@@ -69,6 +69,7 @@ <ul>

<li>{{ emoji["prev"] }}: porta indietro la fanart;</li> <li>{{ emoji["toggle"] }}: abilita o disabilita la fanart;</li> <li>{{ emoji["color"] }}/{{ emoji["color_black"] }}: alterna tra watermark bianco e nero;</li> + <li>{{ emoji["rotate"] }}: ruota la fanart in senso orario;</li> <li>{{ emoji["save"] }}: salva la fanart nel formato base, per Facebook, Mastodon, Twitter e Google Forms;</li> <li>{{ emoji["save_ig"] }}: salva la fanart nel formato storia, per Instagram.</li> <li>{{ emoji["next"] }}: porta avanti la fanart.</li>
M artbound_python/templates/index.htmlartbound_python/templates/index.html

@@ -91,13 +91,13 @@ <div class="card mb-4 box-shadow my-card">

<canvas class="card-img-top entry-img" id="{| id |}" data-name="{| name |}" data-content="{| content |}" data-filename="{| filename |}"></canvas> <div class="card-body"> - <a class="card-text" title="Clicca per copiare." - onclick="navigator.clipboard.writeText(this.innerText);">{| filename |}</a> + <a class="card-text" title="Clicca per copiare." onclick="navigator.clipboard.writeText(this.innerText);">{| filename |}</a> <div class="d-flex justify-content-between align-items-center card-controls"> <div class="btn-group"> <button class="btn btn-sm btn-outline-secondary" onclick="moveUpDown('{| id |}', -1);">{{ emoji["prev"] }}</button> <button class="btn btn-sm btn-outline-secondary" onclick="toggleEntry('{| id |}');">{{ emoji["toggle"] }}</button> <button class="btn btn-sm btn-outline-secondary" onclick="toggleInvert('{| id |}', this);">{{ emoji["color"] }}</button> + <button class="btn btn-sm btn-outline-secondary" onclick="rotateEntry('{| id |}');">{{ emoji["rotate"] }}</button> <button class="btn btn-sm btn-outline-secondary" onclick="saveEntry('{| id |}');">{{ emoji["save"] }}</button> <button class="btn btn-sm btn-outline-secondary" onclick="saveEntryIG('{| id |}');">{{ emoji["save_ig"] }}</button> <button class="btn btn-sm btn-outline-secondary" onclick="moveUpDown('{| id |}', 1);">{{ emoji["next"] }}</button>
M artbound_python/views.pyartbound_python/views.py

@@ -21,6 +21,7 @@ "color": "⚪",

"color_black": "⚫", "help": "❔", "home": "🏠", + "rotate": "🔁", } @app.route('/', methods=['GET', 'POST'])