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

147
app.py
View File

@ -6,7 +6,6 @@ from werkzeug.security import check_password_hash
from PIL import Image, UnidentifiedImageError
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from os.path import realpath, join
app = Flask(__name__)
@ -17,9 +16,10 @@ with open('db/keys.json', 'r') as f:
app.config['SECRET_KEY'] = config['key']
socketio = SocketIO(app)
app.config['UPLOAD_FOLDER'] = '/home/kenny/workspace/Image_Gen_Server/output/expo_postprocessed' # Folder to store images
app.config['DUMP_FOLDER'] = '/home/kenny/workspace/ComfyUI-halloween/output' # Folder to store dump images
app.config['UPLOAD_FOLDER'] = 'images' # Folder to store images
app.config['DUMP_FOLDER'] = 'dump_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'}
USERNAME = config['login_info']['username']
@ -45,24 +45,78 @@ def resize_image(image_path, cache_path, size=(800, 450)):
img = img.convert("RGB")
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():
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 os.listdir(app.config['UPLOAD_FOLDER']):
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)
caches_path = set([os.path.join(app.config['CACHE_FOLDER'], image) for image in os.listdir(app.config['CACHE_FOLDER'])])
images_path = set([os.path.join(app.config['UPLOAD_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):
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
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('/')
def index():
return redirect(url_for('login'))
@ -91,6 +145,7 @@ def gallery():
return redirect(url_for('login'))
cached_images = create_cache()
cached_images.sort()
batches = []
current_batch = []
current_batch_number = None
@ -112,6 +167,9 @@ def gallery():
if current_batch:
batches.append(current_batch)
batches = batches[::-1]
if unstructured_images:
batches.append(unstructured_images)
@ -127,6 +185,17 @@ def uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
else:
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>')
def cached_file(filename):
@ -139,14 +208,14 @@ def cached_file(filename):
else:
return "Invalid image", 404
@app.route('/dump/<filename>')
def dump_file(filename):
@app.route('/dump_cached/<filename>')
def dump_cached_file(filename):
if not session.get('logged_in'):
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):
return send_from_directory(app.config['DUMP_FOLDER'], filename)
return send_from_directory(app.config['DUMP_CACHE_FOLDER'], filename)
else:
return "Invalid image", 404
@ -155,11 +224,8 @@ 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
)
images = sorted(os.listdir(app.config['UPLOAD_FOLDER']), 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)
@ -168,48 +234,55 @@ def dump():
if not session.get('logged_in'):
return redirect(url_for('login'))
#cached_images = create_cache()
dump_folder = app.config['DUMP_FOLDER'] # Folder to store dump images
images = sorted(
os.listdir(dump_folder),
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)
cached_images = create_dump_cache()
cached_images.sort(reverse=True)
valid_images = [img for img in cached_images if is_image_valid(os.path.join(app.config['DUMP_FOLDER'], img))]
return render_template('dump.html', images=valid_images)
def emit_gallery_update():
socketio.emit('update_gallery')
class Watcher(FileSystemEventHandler):
def on_modified(self, event):
if not event.is_directory and event.src_path.startswith(app.config['UPLOAD_FOLDER']):
create_cache()
emit_gallery_update()
def emit_dump_update():
socketio.emit('update_dump')
class Watcher(FileSystemEventHandler):
def on_created(self, event):
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()
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):
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()
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')
def handle_connect():
if not session.get('logged_in'):
disconnect()
if __name__ == '__main__':
event_handler = Watcher()
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()
try:
context = ('cert.pem', 'key.pem') # Replace with your self-signed certificate and key files
socketio.run(app, debug=True, ssl_context=context)
except KeyboardInterrupt:
observer.stop()
observer.join()
observer.join()

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 {
background-color: #fff;
margin: 20px;
padding: 30px;
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);

View File

@ -1,9 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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='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>
<body>
<div class="button-container">
@ -11,22 +26,41 @@
<a href="{{ url_for('dump') }}" class="button">🎃🎃🎃</a>
</div>
<h1>Pumpkin Gallery🎃</h1>
<div class="gallery-container" style="flex-direction: row; flex-wrap: wrap;">
<div class="gallery-container">
{% 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%;">
<img class="responsive-img loaded" src="{{ url_for('dump_file', filename=image) }}" alt="{{ image }}" style="max-width:33vw;">
<a href="#" data-original="{{ url_for('uploaded_dump_file', filename=image) }}" target="_blank">
<img class="responsive-img" src="{{ url_for('dump_cached_file', filename=image) }}" alt="{{ image }}">
</a>
{% endfor %}
</div>
<script>
const images = document.querySelectorAll('.gallery-container a');
images.forEach(image => {
image.addEventListener('click', (event) => {
event.preventDefault();
const original = image.getAttribute('data-original');
window.open(original, '_blank');
});
});
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);
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();
var originalUrl = link.getAttribute('data-original');
window.open(originalUrl, '_blank');
});
});
});
</script>
</body>
</html>
</html>

View File

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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='gallery_styles.css') }}">
@ -35,111 +36,8 @@
<script>
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() {
fetch('/api/images')
.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);
});
location.reload();
});
document.addEventListener("DOMContentLoaded", function() {

View File

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<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='login_styles.css') }}">