diff --git a/button_script.user.js b/button_script.user.js index 90f5880..15e18cb 100644 --- a/button_script.user.js +++ b/button_script.user.js @@ -6,7 +6,7 @@ // @grant GM_registerMenuCommand // @grant GM_getValue // @grant GM_setValue -// @version 1.0r +// @version 1.1r // @author https://t.me/Nyako_TW // @downloadURL https://git.nekono.su/Nyako/Local-Booru/raw/branch/main/button_script.user.js // @updateURL https://git.nekono.su/Nyako/Local-Booru/raw/branch/main/button_script.user.js @@ -107,7 +107,7 @@ if (originalImageLinkElement) { headers: { "Content-Type": "application/json" }, - data: JSON.stringify({ id: imageID }), + data: JSON.stringify({ id: imageID , type: "download"}), onload: function(response) { console.log('Ответ от API:', response.responseText); newDownloadButton.style.outline = "2px solid green"; // Успех diff --git a/run.py b/run.py index 3daa8c0..0681224 100644 --- a/run.py +++ b/run.py @@ -8,7 +8,7 @@ import requests from flask import (Flask, jsonify, render_template, render_template_string, request, send_from_directory, make_response) -from system_module import db, gelbooru, image_processor, logger +from system_module import db, gelbooru, image_processor, logger, tasks if Path("config_gb.json").exists(): CONFIG_FILE = "config_gb.json" @@ -53,6 +53,10 @@ def index(): def analytics(): return render_template("analytics.html") +@app.route("/task") +def task(): + return render_template("task.html") + @app.route("/post/") def post(post_id): return render_template("post.html") @@ -69,42 +73,6 @@ def serve_image(filename): def serve_thumb(filename): return send_from_directory(THUMB_FOLDER, filename) -@app.route("/dw_api", methods=['POST']) -def dw_api(): - data = request.get_json() - if not data or 'id' not in data: - return jsonify({'status': 'error', 'message': 'URL not get'}), 400 - image_id = data["id"] - try: - data_get, url_file, file_name = gb.get_from_id(image_id) - if database.check_uni(data_get["md5"]): - message = f"File '{file_name}' already exists. Skipping." - log.send("warning", "all", message) - return jsonify({'status': 'skipped', 'message': message}) - with requests.get(url_file, stream=True, headers=GB_HEADERS, timeout=30) as r: - r.raise_for_status() - content_type = r.headers.get('Content-Type', '') - if 'text/html' in content_type: - log.send("error", "all", "The server returned HTML instead of an image. Bot protection.") - return False - with open(FILE_DW_FOLDERS+"/"+file_name, 'wb') as f: - for chunk in r.iter_content(chunk_size=8192): - f.write(chunk) - database.add_raw(data_get) - res_data = image_processor.generate_thumb(FILE_DW_FOLDERS+"/"+file_name, THUMB_FOLDER, TARGET_MIN_SIZE) - if not res_data["status"]: - print(res_data["message"]) - return jsonify({'status': 'success'}) - except requests.exceptions.RequestException as e: - error_message = f"Network error {image_id}: {e}" - log.send("error", "all", error_message) - return jsonify({'status': 'error', 'message': error_message}), 500 - except Exception as e: - traceback.print_exc() - error_message = f"Unknown error: {e}" - log.send("error", "all", error_message) - return jsonify({'status': 'error', 'message': error_message}), 500 - @app.route("/api", methods=["GET", "POST"]) def api(): data = request.get_json() @@ -132,6 +100,45 @@ def api(): post_id = data["post_id"] result_post = database.get_from_id(post_id) return jsonify(result_post) + elif data["type"] == "task": + if data["task"] == "analytic": + if tasks.calculate_analytic(): + return "", 200 + else: + return "", 500 + elif data["type"] == "download": + if not data or 'id' not in data: + return jsonify({'status': 'error', 'message': 'URL not get'}), 400 + image_id = data["id"] + try: + data_get, url_file, file_name = gb.get_from_id(image_id) + if database.check_uni(data_get["md5"]): + message = f"File '{file_name}' already exists. Skipping." + log.send("warning", "all", message) + return jsonify({'status': 'skipped', 'message': message}) + with requests.get(url_file, stream=True, headers=GB_HEADERS, timeout=30) as r: + r.raise_for_status() + content_type = r.headers.get('Content-Type', '') + if 'text/html' in content_type: + log.send("error", "all", "The server returned HTML instead of an image. Bot protection.") + return False + with open(FILE_DW_FOLDERS+"/"+file_name, 'wb') as f: + for chunk in r.iter_content(chunk_size=8192): + f.write(chunk) + database.add_raw(data_get) + res_data = image_processor.generate_thumb(FILE_DW_FOLDERS+"/"+file_name, THUMB_FOLDER, TARGET_MIN_SIZE) + if not res_data["status"]: + print(res_data["message"]) + return jsonify({'status': 'success'}) + except requests.exceptions.RequestException as e: + error_message = f"Network error {image_id}: {e}" + log.send("error", "all", error_message) + return jsonify({'status': 'error', 'message': error_message}), 500 + except Exception as e: + traceback.print_exc() + error_message = f"Unknown error: {e}" + log.send("error", "all", error_message) + return jsonify({'status': 'error', 'message': error_message}), 500 else: pass return "", 204 diff --git a/system_module/tasks.py b/system_module/tasks.py new file mode 100644 index 0000000..5a520ce --- /dev/null +++ b/system_module/tasks.py @@ -0,0 +1,2 @@ +def calculate_analytic(): + pass \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index f912482..1d6df03 100644 --- a/templates/index.html +++ b/templates/index.html @@ -18,6 +18,7 @@ header { display: flex; align-items: center; background-color: var(--bg-header); padding: 10px 20px; border-bottom: 1px solid var(--border-color); gap: 15px; flex-wrap: wrap; } .logo { font-weight: bold; font-size: 18px; color: white; text-decoration: none; } .analytics { color: #88bece; text-decoration: none; font-size: 12px; } + .tasks { color: #88bece; text-decoration: none; font-size: 12px; } .search-wrapper { flex-grow: 1; max-width: 400px; position: relative; } .search-wrapper input { width: 100%; background-color: var(--bg-input); border: 1px solid var(--border-color); color: white; padding: 6px 10px; border-radius: 3px; outline: none; } @@ -66,6 +67,7 @@ + ⚙️ Tasks
diff --git a/templates/task.html b/templates/task.html new file mode 100644 index 0000000..c58608b --- /dev/null +++ b/templates/task.html @@ -0,0 +1,182 @@ + + + + + + Tasks - LocalBooru + + + +
+ +
+ +
    +
    +
    + +
    +

    System Tasks

    + +
    + + +
    +
    +

    Пересчёт аналитики

    +

    Обновляет статистику (количество файлов, занимаемое место). Рекомендуется запускать после массового добавления или удаления постов.

    +
    +
    + + +
    +
    + + + + +
    +
    + + + + \ No newline at end of file diff --git a/updater/config.json b/updater/config.json new file mode 100644 index 0000000..65ae636 --- /dev/null +++ b/updater/config.json @@ -0,0 +1,5 @@ +{ + "host":"https://git.nekono.su", + "user":"Nyako", + "repo":"Local-Booru" +} \ No newline at end of file diff --git a/updater/run.py b/updater/run.py new file mode 100644 index 0000000..9a66c5a --- /dev/null +++ b/updater/run.py @@ -0,0 +1,59 @@ +import os +import json +import shutil +import tempfile +import zipfile +import urllib.request + +CONFIG_PATH = os.path.join(os.path.dirname(__file__), "config.json") + +EXCLUDE_PATHS = { + "data", + "config.json", + "logs.log", + "updater" +} + +def load_config(): + with open(CONFIG_PATH, "r", encoding="utf-8") as f: + return json.load(f) + +def get_latest_release(cfg): + url = f"{cfg['host']}/api/v1/repos/{cfg['user']}/{cfg['repo']}/releases/latest" + with urllib.request.urlopen(url) as r: + return json.loads(r.read().decode("utf-8")) + +def download(url, path): + urllib.request.urlretrieve(url, path) + +def should_exclude(relative_path: str) -> bool: + parts = set(relative_path.split(os.sep)) + return any(ex in parts for ex in EXCLUDE_PATHS) + +def apply_update(src_root, target_root): + for root, dirs, files in os.walk(src_root): + rel_dir = os.path.relpath(root, src_root) + if rel_dir == ".": + rel_dir = "" + for file in files: + rel_path = os.path.normpath(os.path.join(rel_dir, file)) + if should_exclude(rel_path): + continue + src_file = os.path.join(root, file) + dst_file = os.path.join(target_root, rel_path) + os.makedirs(os.path.dirname(dst_file), exist_ok=True) + shutil.copy2(src_file, dst_file) + +cfg = load_config() +release = get_latest_release(cfg) +zip_url = release["zipball_url"] +tmp = tempfile.mkdtemp() +zip_path = os.path.join(tmp, "release.zip") +print("Downloading:", release["tag_name"]) +download(zip_url, zip_path) +with zipfile.ZipFile(zip_path, "r") as z: + z.extractall(tmp) +extracted_root = os.path.join(tmp, os.listdir(tmp)[0]) +project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +apply_update(extracted_root, project_root) +print("Update success!")