From a94698b8e0d5b753f8057bca920a43f876d09ce0 Mon Sep 17 00:00:00 2001 From: zaqxs123456 <18200842@life.hkbu.edu.hk> Date: Tue, 22 Oct 2024 18:20:03 +0800 Subject: [PATCH] should be done --- .gitignore | 3 + 2024-10-21T17:19:57,186481017+08:00.png | Bin 17809 -> 0 bytes admin.py | 107 ++++++++++++++++ app.py | 138 +++++++++++++++++---- cache/101260155_p0.png | Bin 319789 -> 0 bytes cache/1b3aa1b096a23fec1def6fc275b1f4b4.png | Bin 22791 -> 0 bytes cache/97144634_p0.png | Bin 691578 -> 61763 bytes cache/nextsec.png | Bin 344613 -> 33706 bytes cache/5月壁紙.png | Bin 533003 -> 45996 bytes images/101260155_p0.png | Bin 13192387 -> 0 bytes requirements.txt | 2 - static/gallery_styles.css | 104 +++++++--------- static/login_styles.css | 2 + static/styles.css | 1 + templates/gallery.html | 135 +++++++++++++++++++- templates/login.html | 1 + 16 files changed, 405 insertions(+), 88 deletions(-) delete mode 100644 2024-10-21T17:19:57,186481017+08:00.png create mode 100644 admin.py delete mode 100644 cache/101260155_p0.png delete mode 100644 cache/1b3aa1b096a23fec1def6fc275b1f4b4.png delete mode 100644 images/101260155_p0.png delete mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore index 5d381cc..f48547f 100644 --- a/.gitignore +++ b/.gitignore @@ -160,3 +160,6 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ +db/ +images/ +cache/ diff --git a/2024-10-21T17:19:57,186481017+08:00.png b/2024-10-21T17:19:57,186481017+08:00.png deleted file mode 100644 index 225dd2707542e438baa6313de0a2338d77463d28..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17809 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
Z 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 Q1OR!Wrmb4}@Y(DC20kIdp8x;= diff --git a/admin.py b/admin.py new file mode 100644 index 0000000..dc16924 --- /dev/null +++ b/admin.py @@ -0,0 +1,107 @@ +import argparse +import json +import os +import secrets +from werkzeug.security import generate_password_hash +from duckduckgo_search import DDGS +import requests +import shutil + +def set_login_info(username, password): + salt = secrets.token_hex(16) + password_hash = generate_password_hash(password + salt) + key = secrets.token_hex(32) + + login_info = { + "login_info": { + "username": username, + "password_hash": password_hash, + "salt": salt + }, + "key": key + } + + os.makedirs('db', exist_ok=True) + with open('db/keys.json', 'w') as f: + json.dump(login_info, f, indent=4) + +def get_next_batch_number(): + existing_files = os.listdir('images') + batch_numbers = [int(f.split('_')[0]) for f in existing_files if '_' in f] + return max(batch_numbers, default=0) + 1 + +def download_images(query, total_images=100, batch_size=10): + os.makedirs('images', exist_ok=True) + ddgs = DDGS() + results = ddgs.images(query, max_results=total_images) + batch_number = get_next_batch_number() + downloaded_images = 0 + valid_extensions = {'jpg', 'jpeg', 'png', 'gif'} + + for i, result in enumerate(results): + image_url = result['image'] + try: + response = requests.get(image_url) + if response.status_code == 200: + ext = image_url.split('.')[-1].lower() + if ext not in valid_extensions: + ext = 'jpg' + filename = f'{batch_number:04d}_{(downloaded_images % batch_size) + 1:05d}.{ext}' + with open(f'images/{filename}', 'wb') as f: + f.write(response.content) + downloaded_images += 1 + else: + print(f"Failed to download image from {image_url}. Status code: {response.status_code}") + except requests.RequestException as e: + print(f"An error occurred while downloading image from {image_url}: {e}") + except Exception as e: + print(f"An unexpected error occurred: {e}") + + if downloaded_images > 0 and downloaded_images % batch_size == 0: + batch_number += 1 + + print(f"Downloaded {downloaded_images} images for query '{query}'.") + +def clear_cache(): + cache_folder = 'cache' + if os.path.exists(cache_folder): + shutil.rmtree(cache_folder) + print(f"Cache directory '{cache_folder}' removed successfully.") + else: + print(f"Cache directory '{cache_folder}' does not exist.") + +def main(): + parser = argparse.ArgumentParser( + description='Set username and password for the application and optionally download images using DuckDuckGo.', + formatter_class=argparse.ArgumentDefaultsHelpFormatter + ) + + # Add an argument to enter user setting mode + parser.add_argument('--user-setting', action='store_true', help='Enter user setting mode to set username and password.') + + # Create a userset argument group + userset_group = parser.add_argument_group('userset', 'Set username and password') + userset_group.add_argument('-u', '--username', type=str, help='The username to set.') + userset_group.add_argument('-p', '--password', type=str, help='The password to set.') + + parser.add_argument('-q', '--query', type=str, help='The search query to download images for testing.') + parser.add_argument('-t', '--total_images', type=int, default=10, help='The total number of images to download.') + parser.add_argument('-b', '--batch_size', type=int, default=5, help='The number of images to download in each batch.') + parser.add_argument('-c', '--clear-cache', action='store_true', help='Remove the cache directory.') + + args = parser.parse_args() + + if args.user_setting: + if not args.username or not args.password: + parser.error('--user-setting requires --username and --password.') + set_login_info(args.username, args.password) + print(f"Username and password set successfully. Key generated and saved in db/keys.json.") + elif args.clear_cache: + clear_cache() + elif args.query: + download_images(args.query, args.total_images, args.batch_size) + else: + parser.print_help() + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/app.py b/app.py index cd663d0..abfc228 100644 --- a/app.py +++ b/app.py @@ -1,31 +1,66 @@ -from flask import Flask, render_template, request, redirect, url_for, send_from_directory, session -from flask_socketio import SocketIO, emit +from flask import Flask, render_template, request, redirect, url_for, send_from_directory, session, jsonify +from flask_socketio import SocketIO, emit, disconnect import os -from werkzeug.security import generate_password_hash, check_password_hash -from PIL import Image +import json +from werkzeug.security import check_password_hash +from PIL import Image, UnidentifiedImageError from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler app = Flask(__name__) -app.config['SECRET_KEY'] = 'your_secret_key' # Replace with a strong secret key + +# Load configuration from keys.json +with open('db/keys.json', 'r') as f: + config = json.load(f) + +app.config['SECRET_KEY'] = config['key'] socketio = SocketIO(app) app.config['UPLOAD_FOLDER'] = 'images' # Folder to store images app.config['CACHE_FOLDER'] = 'cache' # Folder to store cached resized images app.config['ALLOWED_EXTENSIONS'] = {'png', 'jpg', 'jpeg', 'gif'} -# Fixed username and password -USERNAME = 'user' -PASSWORD = generate_password_hash('password') # Hashed password +USERNAME = config['login_info']['username'] +PASSWORD_HASH = config['login_info']['password_hash'] +SALT = config['login_info']['salt'] def allowed_file(filename): return '.' in filename and \ filename.rsplit('.', 1)[1].lower() in app.config['ALLOWED_EXTENSIONS'] +def is_image_valid(image_path): + try: + with Image.open(image_path) as img: + img.verify() # Verify that it is, in fact, an image + return True + except Exception: + return False + def resize_image(image_path, cache_path, size=(800, 450)): with Image.open(image_path) as img: img.thumbnail(size) - img.save(cache_path) + if img.mode in ("RGBA", "P"): # Convert to RGB if necessary + img = img.convert("RGB") + img.save(cache_path, format="JPEG") + +def create_cache(): + images = os.listdir(app.config['UPLOAD_FOLDER']) + cached_images = [] + + if not os.path.exists(app.config['CACHE_FOLDER']): + os.makedirs(app.config['CACHE_FOLDER']) + + for image in images: + image_path = os.path.join(app.config['UPLOAD_FOLDER'], image) + cache_path = os.path.join(app.config['CACHE_FOLDER'], image) + + if not os.path.exists(cache_path) and is_image_valid(image_path): + resize_image(image_path, cache_path) + + if is_image_valid(image_path): + cached_images.append(image) + + return cached_images @app.route('/') def index(): @@ -36,7 +71,7 @@ def login(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] - if username == USERNAME and check_password_hash(PASSWORD, password): + if username == USERNAME and check_password_hash(PASSWORD_HASH, password + SALT): session['logged_in'] = True return redirect(url_for('gallery')) else: @@ -54,43 +89,92 @@ def gallery(): if not session.get('logged_in'): return redirect(url_for('login')) - images = os.listdir(app.config['UPLOAD_FOLDER']) - cached_images = [] + cached_images = create_cache() + batches = [] + current_batch = [] + current_batch_number = None + unstructured_images = [] - if not os.path.exists(app.config['CACHE_FOLDER']): - os.makedirs(app.config['CACHE_FOLDER']) + for image in cached_images: + parts = image.split('_') + if len(parts) == 2 and parts[1].split('.')[0].isdigit(): + batch_number = int(parts[0]) + if current_batch_number is None: + current_batch_number = batch_number + if batch_number != current_batch_number: + batches.append(current_batch) + current_batch = [] + current_batch_number = batch_number + current_batch.append(image) + else: + unstructured_images.append(image) - for image in images: - image_path = os.path.join(app.config['UPLOAD_FOLDER'], image) - cache_path = os.path.join(app.config['CACHE_FOLDER'], image) + if current_batch: + batches.append(current_batch) + if unstructured_images: + batches.append(unstructured_images) - if not os.path.exists(cache_path): - resize_image(image_path, cache_path) - - cached_images.append(image) - - return render_template('gallery.html', images=cached_images) + return render_template('gallery.html', batches=batches) @app.route('/images/') def uploaded_file(filename): - return send_from_directory(app.config['CACHE_FOLDER'], filename) + if not session.get('logged_in'): + return redirect(url_for('login')) + + image_path = os.path.join(app.config['UPLOAD_FOLDER'], filename) + if is_image_valid(image_path): + return send_from_directory(app.config['UPLOAD_FOLDER'], filename) + else: + return "Invalid image", 404 + +@app.route('/cached/') +def cached_file(filename): + if not session.get('logged_in'): + return redirect(url_for('login')) + + image_path = os.path.join(app.config['CACHE_FOLDER'], filename) + if is_image_valid(image_path): + return send_from_directory(app.config['CACHE_FOLDER'], filename) + else: + return "Invalid image", 404 + +@app.route('/api/images') +def api_images(): + if not session.get('logged_in'): + return jsonify([]) + + images = sorted( + os.listdir(app.config['UPLOAD_FOLDER']), + key=lambda x: os.path.getctime(os.path.join(app.config['UPLOAD_FOLDER'], x)), + reverse=True + ) + valid_images = [img for img in images if is_image_valid(os.path.join(app.config['UPLOAD_FOLDER'], img))] + return jsonify(valid_images) def emit_gallery_update(): socketio.emit('update_gallery') class Watcher(FileSystemEventHandler): def on_modified(self, event): - if not event.is_directory: + if not event.is_directory and event.src_path.startswith(app.config['UPLOAD_FOLDER']): + create_cache() emit_gallery_update() def on_created(self, event): - if not event.is_directory: + if not event.is_directory and event.src_path.startswith(app.config['UPLOAD_FOLDER']): + create_cache() emit_gallery_update() def on_deleted(self, event): - if not event.is_directory: + if not event.is_directory and event.src_path.startswith(app.config['UPLOAD_FOLDER']): + create_cache() emit_gallery_update() +@socketio.on('connect') +def handle_connect(): + if not session.get('logged_in'): + disconnect() + if __name__ == '__main__': observer = Observer() observer.schedule(Watcher(), path=app.config['UPLOAD_FOLDER'], recursive=False) diff --git a/cache/101260155_p0.png b/cache/101260155_p0.png deleted file mode 100644 index 85fae6929af3e2069a867578658e5faa00561517..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 319789 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�#Hc}n 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