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>
|