init
|
@ -0,0 +1,7 @@
|
||||||
|
[Dolphin]
|
||||||
|
Timestamp=2024,10,21,18,10,45.565
|
||||||
|
Version=4
|
||||||
|
ViewMode=1
|
||||||
|
|
||||||
|
[Settings]
|
||||||
|
HiddenFilesShown=true
|
After Width: | Height: | Size: 17 KiB |
|
@ -0,0 +1,103 @@
|
||||||
|
from flask import Flask, render_template, request, redirect, url_for, send_from_directory, session
|
||||||
|
from flask_socketio import SocketIO, emit
|
||||||
|
import os
|
||||||
|
from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
|
from PIL import Image
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
def allowed_file(filename):
|
||||||
|
return '.' in filename and \
|
||||||
|
filename.rsplit('.', 1)[1].lower() in app.config['ALLOWED_EXTENSIONS']
|
||||||
|
|
||||||
|
def resize_image(image_path, cache_path, size=(800, 450)):
|
||||||
|
with Image.open(image_path) as img:
|
||||||
|
img.thumbnail(size)
|
||||||
|
img.save(cache_path)
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
@app.route('/login', methods=['GET', 'POST'])
|
||||||
|
def login():
|
||||||
|
if request.method == 'POST':
|
||||||
|
username = request.form['username']
|
||||||
|
password = request.form['password']
|
||||||
|
if username == USERNAME and check_password_hash(PASSWORD, password):
|
||||||
|
session['logged_in'] = True
|
||||||
|
return redirect(url_for('gallery'))
|
||||||
|
else:
|
||||||
|
error = 'Invalid credentials'
|
||||||
|
return render_template('login.html', error=error)
|
||||||
|
return render_template('login.html')
|
||||||
|
|
||||||
|
@app.route('/logout')
|
||||||
|
def logout():
|
||||||
|
session.pop('logged_in', None)
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
@app.route('/gallery')
|
||||||
|
def gallery():
|
||||||
|
if not session.get('logged_in'):
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
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):
|
||||||
|
resize_image(image_path, cache_path)
|
||||||
|
|
||||||
|
cached_images.append(image)
|
||||||
|
|
||||||
|
return render_template('gallery.html', images=cached_images)
|
||||||
|
|
||||||
|
@app.route('/images/<filename>')
|
||||||
|
def uploaded_file(filename):
|
||||||
|
return send_from_directory(app.config['CACHE_FOLDER'], filename)
|
||||||
|
|
||||||
|
def emit_gallery_update():
|
||||||
|
socketio.emit('update_gallery')
|
||||||
|
|
||||||
|
class Watcher(FileSystemEventHandler):
|
||||||
|
def on_modified(self, event):
|
||||||
|
if not event.is_directory:
|
||||||
|
emit_gallery_update()
|
||||||
|
|
||||||
|
def on_created(self, event):
|
||||||
|
if not event.is_directory:
|
||||||
|
emit_gallery_update()
|
||||||
|
|
||||||
|
def on_deleted(self, event):
|
||||||
|
if not event.is_directory:
|
||||||
|
emit_gallery_update()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
observer = Observer()
|
||||||
|
observer.schedule(Watcher(), path=app.config['UPLOAD_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()
|
After Width: | Height: | Size: 312 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 675 KiB |
After Width: | Height: | Size: 336 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 520 KiB |
|
@ -0,0 +1,31 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
-----END CERTIFICATE-----
|
After Width: | Height: | Size: 13 MiB |
After Width: | Height: | Size: 15 MiB |
After Width: | Height: | Size: 2.8 MiB |
After Width: | Height: | Size: 322 KiB |
After Width: | Height: | Size: 12 MiB |
|
@ -0,0 +1,52 @@
|
||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
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
|
||||||
|
XSganpYkLqJCS1lgaReUZcgjcBvBkco=
|
||||||
|
-----END PRIVATE KEY-----
|
|
@ -0,0 +1,2 @@
|
||||||
|
Flask
|
||||||
|
Werkzeug
|
|
@ -0,0 +1,85 @@
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #ffeeee 25%, #dce2ff 75%);
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: #333;
|
||||||
|
margin: 20px 0;
|
||||||
|
font-size: 24px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.gallery-container {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1200px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Media queries for responsive padding */
|
||||||
|
@media (max-width: 1199px) {
|
||||||
|
.gallery-container {
|
||||||
|
padding-left: 40px;
|
||||||
|
padding-right: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: 991px) {
|
||||||
|
.gallery-container {
|
||||||
|
padding-left: 30px;
|
||||||
|
padding-right: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.gallery-container {
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.responsive-img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
border: 2px solid #ddd;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
transition: transform 0.2s;
|
||||||
|
aspect-ratio: 16 / 9;
|
||||||
|
object-fit: cover;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.5s ease-in-out, transform 0.2s;
|
||||||
|
}
|
||||||
|
.responsive-img.loaded {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
.responsive-img:hover {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Media queries for responsive columns */
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
.gallery-container {
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (min-width: 992px) and (max-width: 1199px) {
|
||||||
|
.gallery-container {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (min-width: 768px) and (max-width: 991px) {
|
||||||
|
.gallery-container {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.gallery-container {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
form h1 {
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 24px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 30px;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
width: 350px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
form div {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
color: #555;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
input[type="text"], input[type="password"] {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
padding: 12px;
|
||||||
|
background-color: #007BFF;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s, transform 0.2s;
|
||||||
|
font-size: 16px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: red;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
/* Common styles */
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
background: linear-gradient(135deg, #ffeeee 25%, #dce2ff 75%);
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: #333;
|
||||||
|
margin: 20px 0;
|
||||||
|
font-size: 24px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 30px;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
width: 350px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
form div {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
color: #555;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
input[type="text"], input[type="password"] {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
padding: 12px;
|
||||||
|
background-color: #007BFF;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s, transform 0.2s;
|
||||||
|
font-size: 16px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
background-color: #0056b3;
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: red;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-size: 14px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Image Gallery</title>
|
||||||
|
<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>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Image Gallery</h1>
|
||||||
|
<div class="gallery-container" id="gallery-container">
|
||||||
|
{% for image in images %}
|
||||||
|
<img class="responsive-img" src="{{ url_for('uploaded_file', filename=image) }}" alt="{{ image }}">
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);
|
||||||
|
socket.on('update_gallery', 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');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,24 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Login</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='login_styles.css') }}">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form method="POST">
|
||||||
|
<h1>Login</h1>
|
||||||
|
{% if error %}
|
||||||
|
<p class="error"><strong>Error:</strong> {{ error }}</p>
|
||||||
|
{% endif %}
|
||||||
|
<div>
|
||||||
|
<label for="username">Username:</label>
|
||||||
|
<input type="text" name="username" id="username" required>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="password">Password:</label>
|
||||||
|
<input type="password" name="password" id="password" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit">Login</button>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|