Compare commits

..

1 Commits

Author SHA1 Message Date
zaqxs123456 1c11c3dcd8 dump fix, css fix, auto load, del cache. 2024-10-26 02:52:11 +08:00
34 changed files with 164 additions and 153 deletions

145
app.py
View File

@ -6,7 +6,6 @@ from werkzeug.security import check_password_hash
from PIL import Image, UnidentifiedImageError from PIL import Image, UnidentifiedImageError
from watchdog.observers import Observer from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler from watchdog.events import FileSystemEventHandler
from os.path import realpath, join
app = Flask(__name__) app = Flask(__name__)
@ -17,9 +16,10 @@ with open('db/keys.json', 'r') as f:
app.config['SECRET_KEY'] = config['key'] app.config['SECRET_KEY'] = config['key']
socketio = SocketIO(app) socketio = SocketIO(app)
app.config['UPLOAD_FOLDER'] = '/home/kenny/workspace/Image_Gen_Server/output/expo_postprocessed' # Folder to store images app.config['UPLOAD_FOLDER'] = 'images' # Folder to store images
app.config['DUMP_FOLDER'] = '/home/kenny/workspace/ComfyUI-halloween/output' # Folder to store dump images app.config['DUMP_FOLDER'] = 'dump_images'
app.config['CACHE_FOLDER'] = 'cache' # Folder to store cached resized images app.config['CACHE_FOLDER'] = 'cache' # Folder to store cached resized images
app.config['DUMP_CACHE_FOLDER'] = 'dump_cache'
app.config['ALLOWED_EXTENSIONS'] = {'png', 'jpg', 'jpeg', 'gif'} app.config['ALLOWED_EXTENSIONS'] = {'png', 'jpg', 'jpeg', 'gif'}
USERNAME = config['login_info']['username'] USERNAME = config['login_info']['username']
@ -45,24 +45,78 @@ def resize_image(image_path, cache_path, size=(800, 450)):
img = img.convert("RGB") img = img.convert("RGB")
img.save(cache_path, format="JPEG") img.save(cache_path, format="JPEG")
def delete_non_existing_cache():
cache_folder = app.config['CACHE_FOLDER']
upload_folder = app.config['UPLOAD_FOLDER']
dump_cache_folder = app.config['DUMP_CACHE_FOLDER']
dump_folder = app.config['DUMP_FOLDER']
images = os.listdir(cache_folder)
for image in images:
image_path_upload = os.path.join(upload_folder, image)
cache_path = os.path.join(cache_folder, image)
if not os.path.exists(image_path_upload):
if os.path.exists(cache_path):
os.remove(cache_path)
dump_images = os.listdir(dump_cache_folder)
for image in dump_images:
image_path_dump = os.path.join(dump_folder, image)
dump_cache_path = os.path.join(dump_cache_folder, image)
if not os.path.exists(image_path_dump):
if os.path.exists(dump_cache_path):
os.remove(dump_cache_path)
def create_cache(): def create_cache():
images = os.listdir(app.config['UPLOAD_FOLDER'])
cached_images = [] cached_images = []
if not os.path.exists(app.config['CACHE_FOLDER']): if not os.path.exists(app.config['CACHE_FOLDER']):
os.makedirs(app.config['CACHE_FOLDER']) os.makedirs(app.config['CACHE_FOLDER'])
for image in os.listdir(app.config['UPLOAD_FOLDER']): caches_path = set([os.path.join(app.config['CACHE_FOLDER'], image) for image in os.listdir(app.config['CACHE_FOLDER'])])
image_path = os.path.join(app.config['UPLOAD_FOLDER'], image) images_path = set([os.path.join(app.config['UPLOAD_FOLDER'], image) for image in images])
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)
# get the images that are not cached
uncached_images = images_path - caches_path
for image_path in uncached_images:
if is_image_valid(image_path): if is_image_valid(image_path):
cached_images.append(image) cache_path = os.path.join(app.config['CACHE_FOLDER'], os.path.basename(image_path))
resize_image(image_path, cache_path)
cached_images.append(os.path.basename(image_path))
return cached_images return cached_images
def create_dump_cache():
images = os.listdir(app.config['DUMP_FOLDER'])
cached_images = []
if not os.path.exists(app.config['DUMP_CACHE_FOLDER']):
os.makedirs(app.config['DUMP_CACHE_FOLDER'])
caches_path = set([os.path.join(app.config['DUMP_CACHE_FOLDER'], image) for image in os.listdir(app.config['DUMP_CACHE_FOLDER'])])
images_path = set([os.path.join(app.config['DUMP_FOLDER'], image) for image in images])
# get the images that are not cached
uncached_images = images_path - caches_path
for image_path in uncached_images:
if is_image_valid(image_path):
cache_path = os.path.join(app.config['DUMP_CACHE_FOLDER'], os.path.basename(image_path))
resize_image(image_path, cache_path)
cached_images.append(os.path.basename(image_path))
return cached_images
def update_cache():
create_cache()
delete_non_existing_cache()
def update_dump_cache():
create_dump_cache()
delete_non_existing_cache()
@app.route('/') @app.route('/')
def index(): def index():
return redirect(url_for('login')) return redirect(url_for('login'))
@ -91,6 +145,7 @@ def gallery():
return redirect(url_for('login')) return redirect(url_for('login'))
cached_images = create_cache() cached_images = create_cache()
cached_images.sort()
batches = [] batches = []
current_batch = [] current_batch = []
current_batch_number = None current_batch_number = None
@ -112,6 +167,9 @@ def gallery():
if current_batch: if current_batch:
batches.append(current_batch) batches.append(current_batch)
batches = batches[::-1]
if unstructured_images: if unstructured_images:
batches.append(unstructured_images) batches.append(unstructured_images)
@ -128,6 +186,17 @@ def uploaded_file(filename):
else: else:
return "Invalid image", 404 return "Invalid image", 404
@app.route('/dump_images/<filename>')
def uploaded_dump_file(filename):
if not session.get('logged_in'):
return redirect(url_for('login'))
image_path = os.path.join(app.config['DUMP_FOLDER'], filename)
if is_image_valid(image_path):
return send_from_directory(app.config['DUMP_FOLDER'], filename)
else:
return "Invalid image", 404
@app.route('/cached/<filename>') @app.route('/cached/<filename>')
def cached_file(filename): def cached_file(filename):
if not session.get('logged_in'): if not session.get('logged_in'):
@ -139,14 +208,14 @@ def cached_file(filename):
else: else:
return "Invalid image", 404 return "Invalid image", 404
@app.route('/dump/<filename>') @app.route('/dump_cached/<filename>')
def dump_file(filename): def dump_cached_file(filename):
if not session.get('logged_in'): if not session.get('logged_in'):
return redirect(url_for('login')) return redirect(url_for('login'))
image_path = os.path.join(app.config['DUMP_FOLDER'], filename) image_path = os.path.join(app.config['DUMP_CACHE_FOLDER'], filename)
if is_image_valid(image_path): if is_image_valid(image_path):
return send_from_directory(app.config['DUMP_FOLDER'], filename) return send_from_directory(app.config['DUMP_CACHE_FOLDER'], filename)
else: else:
return "Invalid image", 404 return "Invalid image", 404
@ -155,11 +224,8 @@ def api_images():
if not session.get('logged_in'): if not session.get('logged_in'):
return jsonify([]) return jsonify([])
images = sorted( images = sorted(os.listdir(app.config['UPLOAD_FOLDER']), reverse=True)
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))] valid_images = [img for img in images if is_image_valid(os.path.join(app.config['UPLOAD_FOLDER'], img))]
return jsonify(valid_images) return jsonify(valid_images)
@ -168,44 +234,51 @@ def dump():
if not session.get('logged_in'): if not session.get('logged_in'):
return redirect(url_for('login')) return redirect(url_for('login'))
#cached_images = create_cache() cached_images = create_dump_cache()
dump_folder = app.config['DUMP_FOLDER'] # Folder to store dump images cached_images.sort(reverse=True)
images = sorted(
os.listdir(dump_folder), valid_images = [img for img in cached_images if is_image_valid(os.path.join(app.config['DUMP_FOLDER'], img))]
key=lambda x: os.path.getctime(os.path.join(dump_folder, x)),
reverse=True
)
valid_images = [img for img in images if is_image_valid(os.path.join(dump_folder, img))]
print(valid_images)
return render_template('dump.html', images=valid_images) return render_template('dump.html', images=valid_images)
def emit_gallery_update(): def emit_gallery_update():
socketio.emit('update_gallery') socketio.emit('update_gallery')
class Watcher(FileSystemEventHandler): def emit_dump_update():
def on_modified(self, event): socketio.emit('update_dump')
if not event.is_directory and event.src_path.startswith(app.config['UPLOAD_FOLDER']):
create_cache()
emit_gallery_update()
class Watcher(FileSystemEventHandler):
def on_created(self, event): def on_created(self, event):
if not event.is_directory and event.src_path.startswith(app.config['UPLOAD_FOLDER']): if not event.is_directory and event.src_path.startswith(app.config['UPLOAD_FOLDER']):
create_cache() print(f"File created: {event.src_path}")
update_cache()
emit_gallery_update() emit_gallery_update()
if not event.is_directory and event.src_path.startswith(app.config['DUMP_FOLDER']):
print(f"Dump file created: {event.src_path}")
update_dump_cache()
emit_dump_update()
def on_deleted(self, event): def on_deleted(self, event):
if not event.is_directory and event.src_path.startswith(app.config['UPLOAD_FOLDER']): if not event.is_directory and event.src_path.startswith(app.config['UPLOAD_FOLDER']):
create_cache() print(f"File deleted: {event.src_path}")
update_cache()
emit_gallery_update() emit_gallery_update()
if not event.is_directory and event.src_path.startswith(app.config['DUMP_FOLDER']):
print(f"Dump file deleted: {event.src_path}")
update_dump_cache()
emit_dump_update()
@socketio.on('connect') @socketio.on('connect')
def handle_connect(): def handle_connect():
if not session.get('logged_in'): if not session.get('logged_in'):
disconnect() disconnect()
if __name__ == '__main__': if __name__ == '__main__':
event_handler = Watcher()
observer = Observer() observer = Observer()
observer.schedule(Watcher(), path=app.config['UPLOAD_FOLDER'], recursive=False) observer.schedule(event_handler, path=app.config['UPLOAD_FOLDER'], recursive=False)
observer.schedule(event_handler, path=app.config['DUMP_FOLDER'], recursive=False)
observer.start() observer.start()
try: try:
context = ('cert.pem', 'key.pem') # Replace with your self-signed certificate and key files context = ('cert.pem', 'key.pem') # Replace with your self-signed certificate and key files

BIN
cache/97144634_p0.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

BIN
cache/nextsec.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

BIN
cache/unnamed.jpg vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

BIN
cache/5月壁紙.png vendored

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 322 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 MiB

4
requirements.txt Normal file
View File

@ -0,0 +1,4 @@
Flask
requests
Pillow
duckduckgo_search

View File

@ -16,6 +16,7 @@ form h1 {
} }
form { form {
background-color: #fff; background-color: #fff;
margin: 20px;
padding: 30px; padding: 30px;
border-radius: 10px; border-radius: 10px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);

View File

@ -1,9 +1,24 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pumpkin Gallery🎃</title> <title>Pumpkin Gallery🎃</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles.css') }}"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles.css') }}">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='gallery_styles.css') }}"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='gallery_styles.css') }}">
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.min.js"></script>
<style>
.gallery-container {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 10px;
}
.gallery-container img {
width: 100%;
height: auto;
display: block;
}
</style>
</head> </head>
<body> <body>
<div class="button-container"> <div class="button-container">
@ -11,20 +26,39 @@
<a href="{{ url_for('dump') }}" class="button">🎃🎃🎃</a> <a href="{{ url_for('dump') }}" class="button">🎃🎃🎃</a>
</div> </div>
<h1>Pumpkin Gallery🎃</h1> <h1>Pumpkin Gallery🎃</h1>
<div class="gallery-container" style="flex-direction: row; flex-wrap: wrap;"> <div class="gallery-container">
{% for image in images %} {% for image in images %}
<a href="{{ url_for('dump_file', filename=image) }}" data-original="{{ url_for('dump_file', filename=image) }}" target="_blank" style="flex: 1 0 25%;"> <a href="#" data-original="{{ url_for('uploaded_dump_file', filename=image) }}" target="_blank">
<img class="responsive-img loaded" src="{{ url_for('dump_file', filename=image) }}" alt="{{ image }}" style="max-width:33vw;"> <img class="responsive-img" src="{{ url_for('dump_cached_file', filename=image) }}" alt="{{ image }}">
</a> </a>
{% endfor %} {% endfor %}
</div> </div>
<script> <script>
const images = document.querySelectorAll('.gallery-container a'); var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);
images.forEach(image => {
image.addEventListener('click', (event) => { socket.on('update_dump', function() {
location.reload();
});
document.addEventListener("DOMContentLoaded", function() {
var images = document.querySelectorAll('.responsive-img');
images.forEach(function(img) {
img.addEventListener('load', function() {
img.classList.add('loaded');
});
if (img.complete) {
img.classList.add('loaded');
}
});
var links = document.querySelectorAll('.gallery-container a');
links.forEach(function(link) {
link.addEventListener('click', function(event) {
event.preventDefault(); event.preventDefault();
const original = image.getAttribute('data-original'); var originalUrl = link.getAttribute('data-original');
window.open(original, '_blank'); window.open(originalUrl, '_blank');
});
}); });
}); });
</script> </script>

View File

@ -1,6 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fencing Gallery🤺</title> <title>Fencing Gallery🤺</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles.css') }}"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles.css') }}">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='gallery_styles.css') }}"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='gallery_styles.css') }}">
@ -35,111 +36,8 @@
<script> <script>
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port); var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);
function parseImageName(image) {
var parts = image.split('_');
if (parts.length === 2) {
var batchNumber = parseInt(parts[0], 10);
var index = parseInt(parts[1].split('.')[0], 10);
return { batchNumber: batchNumber, index: index };
}
return { batchNumber: 0, index: 0 };
}
function removeNonExistentImages(existingImages, images) {
var galleryContainer = document.getElementById('gallery-container');
existingImages.forEach(function(image) {
if (!images.includes(image)) {
var imgElement = galleryContainer.querySelector(`img[alt="${image}"]`);
if (imgElement) {
imgElement.parentElement.classList.add('removed');
setTimeout(() => {
imgElement.parentElement.remove();
}, 500); // Match the duration of the fadeOut animation
}
}
});
}
function addNewImages(existingImages, images) {
var galleryContainer = document.getElementById('gallery-container');
var currentBatchContainer = null;
var currentBatchNumber = null;
images.forEach(function(image) {
if (!existingImages.includes(image)) {
var parsedImage = parseImageName(image);
var batchNumber = parsedImage.batchNumber;
var index = parsedImage.index;
if (currentBatchNumber !== batchNumber) {
currentBatchNumber = batchNumber;
currentBatchContainer = document.querySelector(`.batch-${batchNumber}`);
if (!currentBatchContainer) {
currentBatchContainer = document.createElement('div');
currentBatchContainer.className = `batch-container batch-${batchNumber}`;
galleryContainer.appendChild(currentBatchContainer);
if (galleryContainer.children.length > 1) {
var divider = document.createElement('hr');
galleryContainer.insertBefore(divider, currentBatchContainer);
}
}
}
var anchor = document.createElement('a');
anchor.href = '#';
anchor.setAttribute('data-original', `/images/${image}`);
anchor.target = '_blank';
anchor.classList.add('added');
var img = document.createElement('img');
img.className = `responsive-img index-${index}`;
img.alt = image;
img.src = `/cached/${image}`;
anchor.appendChild(img);
// Insert the image in the correct location based on the index
var inserted = false;
var children = currentBatchContainer.children;
for (var i = 0; i < children.length; i++) {
var childIndex = parseInt(children[i].querySelector('img').className.split('index-')[1], 10);
if (index < childIndex) {
currentBatchContainer.insertBefore(anchor, children[i]);
inserted = true;
break;
}
}
if (!inserted) {
currentBatchContainer.appendChild(anchor);
}
img.addEventListener('load', function() {
img.classList.add('loaded');
});
if (img.complete) {
img.classList.add('loaded');
}
anchor.addEventListener('click', function(event) {
event.preventDefault();
var originalUrl = anchor.getAttribute('data-original');
window.open(originalUrl, '_blank');
});
}
});
}
socket.on('update_gallery', function() { socket.on('update_gallery', function() {
fetch('/api/images') location.reload();
.then(response => response.json())
.then(images => {
var galleryContainer = document.getElementById('gallery-container');
var existingImages = Array.from(galleryContainer.getElementsByTagName('img')).map(img => img.alt);
removeNonExistentImages(existingImages, images);
addNewImages(existingImages, images);
});
}); });
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function() {

View File

@ -1,6 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login</title> <title>Login</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles.css') }}"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles.css') }}">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='login_styles.css') }}"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='login_styles.css') }}">