aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Tuomi <jan@jantuomi.fi>2026-07-12 20:50:56 +0300
committerJan Tuomi <jan@jantuomi.fi>2026-07-12 20:50:56 +0300
commitd080e8a1e2241540f1dae78806b687413783e61d (patch)
treefcd8e3fb6b99931d6be4255b0ad8f621b844669e
parentb0204b30ea5bf54010d351f314ca123514021930 (diff)
Improve env vars, reformatHEADmain
-rw-r--r--app.py101
1 files changed, 72 insertions, 29 deletions
diff --git a/app.py b/app.py
index 1f51107..a0a016e 100644
--- a/app.py
+++ b/app.py
@@ -1,34 +1,39 @@
import os
-import threading
-import time
-from flask.helpers import url_for
-import requests
import re
import sqlite3
+import threading
+import time
+import requests
from dotenv import load_dotenv
+from flask.helpers import url_for
+
load_dotenv()
-from flask import Flask, render_template, make_response, send_file
-from flask_compress import Compress
from collections import defaultdict
-from PIL import Image
from io import BytesIO
+from flask import Flask, make_response, render_template, send_file
+from flask_compress import Compress
+from PIL import Image
+
# Environment config
GDRIVE_FOLDER = os.environ["GDRIVE_FOLDER"]
GAPI_KEY = os.environ["GAPI_KEY"]
-PORT = int(os.environ.get("PORT", 5000))
-THUMBNAIL_DIR = "thumbnails"
-DB_FILE = "data/sqlite.db"
-ISO_DATE_REGEX = re.compile(r"^\d{4}-\d{2}-\d{2}")
+DATA_PREFIX = os.environ.get("DATA_PREFIX", ".")
AUTHOR = os.environ["AUTHOR"]
IG_LINK = os.environ.get("IG_LINK", None)
BASE_URL = os.environ["BASE_URL"]
+THUMBNAIL_DIR = os.path.join(DATA_PREFIX, "thumbnails")
+DB_FILE = os.path.join(DATA_PREFIX, "sqlite.db")
+
+ISO_DATE_REGEX = re.compile(r"^\d{4}-\d{2}-\d{2}")
+
# SQLite connection configuration per kerkour.com/sqlite-for-servers
_write_lock = threading.Lock()
+
def _configure_connection(conn):
conn.execute("PRAGMA journal_mode = WAL")
conn.execute("PRAGMA busy_timeout = 5000")
@@ -37,16 +42,19 @@ def _configure_connection(conn):
conn.execute("PRAGMA foreign_keys = true")
conn.execute("PRAGMA temp_store = memory")
+
def get_read_db():
conn = sqlite3.connect(DB_FILE)
_configure_connection(conn)
return conn
+
def get_write_db():
conn = sqlite3.connect(DB_FILE, isolation_level=None)
_configure_connection(conn)
return conn
+
# Initialize DB
def init_db():
os.makedirs(os.path.dirname(DB_FILE), exist_ok=True)
@@ -65,12 +73,18 @@ def init_db():
finally:
conn.close()
+
# Get image files from the given Google Drive folder (non-recursive)
def list_drive_files(folder_id):
url = "https://www.googleapis.com/drive/v3/files"
image_mime_types = {
- "image/jpeg", "image/png", "image/webp", "image/gif",
- "image/bmp", "image/tiff", "image/jpg"
+ "image/jpeg",
+ "image/png",
+ "image/webp",
+ "image/gif",
+ "image/bmp",
+ "image/tiff",
+ "image/jpg",
}
params = {
"q": f"'{folder_id}' in parents and trashed = false",
@@ -82,6 +96,7 @@ def list_drive_files(folder_id):
files = resp.json().get("files", [])
return [f for f in files if f.get("mimeType") in image_mime_types]
+
# Download image and generate thumbnail
def download_and_create_thumbnail(file_id):
url = f"https://drive.google.com/uc?id={file_id}"
@@ -98,6 +113,7 @@ def download_and_create_thumbnail(file_id):
img.save(path, format="JPEG")
return path
+
# Background thread to sync Drive files
def save_files_periodically():
while True:
@@ -114,26 +130,42 @@ def save_files_periodically():
for f in drive_files:
current_ids.add(f["id"])
date_match = ISO_DATE_REGEX.match(f["name"])
- date = date_match.group(0) if date_match else f["createdTime"][:10]
+ date = (
+ date_match.group(0) if date_match else f["createdTime"][:10]
+ )
- cur.execute("SELECT description FROM files WHERE id = ?", (f["id"],))
+ cur.execute(
+ "SELECT description FROM files WHERE id = ?", (f["id"],)
+ )
row = cur.fetchone()
if row is None:
thumb_path = download_and_create_thumbnail(f["id"])
- cur.execute("""
+ cur.execute(
+ """
INSERT OR IGNORE INTO files (id, name, description, date, thumbnail)
VALUES (?, ?, ?, ?, ?)
- """, (f["id"], f["name"], f.get("description", ""), date, thumb_path))
+ """,
+ (
+ f["id"],
+ f["name"],
+ f.get("description", ""),
+ date,
+ thumb_path,
+ ),
+ )
else:
existing_description = row[0] or ""
new_description = f.get("description", "")
if existing_description != new_description:
- cur.execute("""
+ cur.execute(
+ """
UPDATE files
SET name = ?, description = ?, date = ?
WHERE id = ?
- """, (f["name"], new_description, date, f["id"]))
+ """,
+ (f["name"], new_description, date, f["id"]),
+ )
# Delete removed files
cur.execute("SELECT id, thumbnail FROM files")
@@ -155,6 +187,7 @@ def save_files_periodically():
print("Error updating file list:", e)
time.sleep(60)
+
def create_app() -> Flask:
init_db()
@@ -166,38 +199,45 @@ def create_app() -> Flask:
Compress(app)
return app
+
# Setup
app = create_app()
+
# Route: Gallery
@app.route("/")
def gallery():
conn = get_read_db()
try:
cur = conn.cursor()
- cur.execute("SELECT id, name, description, date, thumbnail FROM files ORDER BY date DESC")
+ cur.execute(
+ "SELECT id, name, description, date, thumbnail FROM files ORDER BY date DESC"
+ )
files = cur.fetchall()
finally:
conn.close()
grouped = defaultdict(list)
for file in files:
- grouped[file[3]].append({
- "id": file[0],
- "name": file[1],
- "description": file[2],
- "thumbnail": file[4],
- })
+ grouped[file[3]].append(
+ {
+ "id": file[0],
+ "name": file[1],
+ "description": file[2],
+ "thumbnail": file[4],
+ }
+ )
sorted_dates = sorted(grouped.keys(), reverse=True)
if len(files) > 0:
- cover_img_path = url_for("serve_thumbnail", filename=files[0][4].split('/')[-1])
+ cover_img_path = url_for("serve_thumbnail", filename=files[0][4].split("/")[-1])
cover_img_url = BASE_URL + cover_img_path
else:
cover_img_url = None
- return render_template("gallery.html",
+ return render_template(
+ "gallery.html",
grouped=grouped,
dates=sorted_dates,
author=AUTHOR,
@@ -206,6 +246,7 @@ def gallery():
base_url=BASE_URL,
)
+
@app.route("/thumbnails/<filename>")
def serve_thumbnail(filename):
path = os.path.join(THUMBNAIL_DIR, filename)
@@ -213,5 +254,7 @@ def serve_thumbnail(filename):
return "Not Found", 404
response = make_response(send_file(path))
- response.headers["Cache-Control"] = "public, max-age=86400, stale-while-revalidate=604800"
+ response.headers["Cache-Control"] = (
+ "public, max-age=86400, stale-while-revalidate=604800"
+ )
return response