diff --git a/.DS_Store b/.DS_Store index fe0b8b9..c143d70 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/README.md b/README.md index 7cb01e0..b0db7aa 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,10 @@ ## Tiếng Việt ### Giới thiệu -aPix Image Workspace là một ứng dụng Flask đơn giản giúp bạn tạo hình ảnh dựa trên prompt, cho phép upload tài liệu tham khảo và chọn tỷ lệ khung hình, độ phân giải. +aPix Image Workspace là một giao diện Flask nhẹ giúp bạn tạo hình ảnh bằng API Model Gemini Image 3 Pro (Nano Banana Pro). Bạn có thể gửi prompt, upload tài liệu tham khảo và điều chỉnh tỷ lệ khung hình/độ phân giải. + +![Preview](./preview.jpeg) +Ảnh preview được lưu ngay tại `preview.jpeg` nếu bạn cần xem offline hoặc nhúng lại trong tài liệu khác. ### Cài đặt 1. Tạo môi trường ảo (ví dụ `python -m venv .venv`) và kích hoạt nó. @@ -11,6 +14,12 @@ aPix Image Workspace là một ứng dụng Flask đơn giản giúp bạn tạo pip install -r requirements.txt ``` +### Khởi chạy nhanh bằng `run_app` +1. Đặt `GOOGLE_API_KEY` qua biến môi trường hoặc giao diện. +2. Dùng `./run_app.sh` (macOS/Linux) hoặc `run_app.bat`/`run_app.command` (Windows) để tự động tìm Python, tạo `.venv`, cài `requirements.txt` và khởi động `app.py`. +3. Mở `http://127.0.0.1:8888`, nhập prompt/tùy chọn rồi nhấn Generate. +4. Hình ảnh mới nằm trong `static/generated/`; `/gallery` thể hiện lịch sử. + ### Sử dụng 1. Đặt biến môi trường `GOOGLE_API_KEY` với API key của Google GenAI hoặc nhập trực tiếp trong giao diện. 2. Chạy Flask: @@ -22,7 +31,10 @@ aPix Image Workspace là một ứng dụng Flask đơn giản giúp bạn tạo ## English ### Overview -aPix Image Workspace is a lightweight Flask frontend to Google GenAI image generation. You can submit a prompt, optional reference files, and pick aspect ratio/resolution. +aPix Image Workspace is a lightweight Flask frontend for Google GenAI image generation powered by the Gemini Image 3 Pro (Nano Banana Pro) model. Submit a prompt, include reference files if needed, and choose aspect ratio/resolution. + +![Preview](./preview.jpeg) +The preview screenshot is also stored as `preview.jpeg` in the repo for offline use or documentation embeds. ### Setup 1. Create & activate a virtual environment (e.g., `python -m venv .venv`). @@ -30,12 +42,11 @@ aPix Image Workspace is a lightweight Flask frontend to Google GenAI image gener ```bash pip install -r requirements.txt ``` +3. macOS/Linux users can run `./run_app.sh`; Windows users can use `run_app.bat` (or `run_app.command`) to auto-detect Python, create the `.venv`, install deps, and launch the Flask server on port 8888. -### Basic usage -1. Supply a `GOOGLE_API_KEY` via environment variable or the UI form. -2. Launch the app: - ```bash - python app.py - ``` -3. Visit `http://127.0.0.1:8888`, fill in the prompt and optional settings, then click Generate. -4. Generated assets appear in `static/generated/`, and `/gallery` exposes the list of saved images. +### Quick start via `run_app` +1. Export `GOOGLE_API_KEY` via your environment or the UI form. +2. On macOS/Linux run `./run_app.sh` (or `run_app.command`); on Windows use `run_app.bat` or `run_app.command`. + - The script detects `python3`/`python`, creates `.venv`, installs `requirements.txt`, activates the virtualenv, and then launches `app.py`. +3. Open `http://127.0.0.1:8888`, submit prompts/options, and click Generate. +4. New images appear under `static/generated/`, and `/gallery` exposes the history. diff --git a/app.py b/app.py index 0d5f964..928cf50 100644 --- a/app.py +++ b/app.py @@ -3,7 +3,9 @@ import base64 import uuid import glob import json +from datetime import datetime from io import BytesIO +from send2trash import send2trash from flask import Flask, render_template, request, jsonify, url_for from google import genai from google.genai import types @@ -145,8 +147,9 @@ def generate_image(): print(f"Error processing uploaded file: {e}") continue + model_name = "gemini-3-pro-image-preview" response = client.models.generate_content( - model="gemini-3-pro-image-preview", + model=model_name, contents=contents, config=types.GenerateContentConfig( response_modalities=['IMAGE'], @@ -161,7 +164,25 @@ def generate_image(): image = Image.open(BytesIO(image_bytes)) png_info = PngImagePlugin.PngInfo() - filename = f"{uuid.uuid4()}.png" + date_str = datetime.now().strftime("%Y%m%d") + + # Find existing files to determine next ID + search_pattern = os.path.join(GENERATED_DIR, f"{model_name}_{date_str}_*.png") + existing_files = glob.glob(search_pattern) + max_id = 0 + for f in existing_files: + try: + basename = os.path.basename(f) + name_without_ext = os.path.splitext(basename)[0] + id_part = name_without_ext.split('_')[-1] + id_num = int(id_part) + if id_num > max_id: + max_id = id_num + except ValueError: + continue + + next_id = max_id + 1 + filename = f"{model_name}_{date_str}_{next_id}.png" filepath = os.path.join(GENERATED_DIR, filename) rel_path = os.path.join('generated', filename) image_url = url_for('static', filename=rel_path) @@ -209,7 +230,7 @@ def delete_image(): if os.path.exists(filepath): try: - os.remove(filepath) + send2trash(filepath) return jsonify({'success': True}) except Exception as e: return jsonify({'error': str(e)}), 500 diff --git a/preview.jpeg b/preview.jpeg new file mode 100644 index 0000000..e969f6d Binary files /dev/null and b/preview.jpeg differ diff --git a/requirements.txt b/requirements.txt index 5e9e21c..5513fc5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ flask google-genai pillow +Send2Trash diff --git a/run_app.bat b/run_app.bat new file mode 100644 index 0000000..3125701 --- /dev/null +++ b/run_app.bat @@ -0,0 +1,34 @@ +@echo off +setlocal + +cd /d "%~dp0" + +if defined PYTHON_BIN goto :found_python + +for /f "delims=" %%P in ('where python3 2^>nul') do ( + set "PYTHON_BIN=%%~P" + goto :found_python +) +for /f "delims=" %%P in ('where python 2^>nul') do ( + set "PYTHON_BIN=%%~P" + goto :found_python +) +for /f "delims=" %%P in ('py -3 -c "import sys; print(sys.executable)" 2^>nul') do ( + set "PYTHON_BIN=%%~P" + goto :found_python +) + +echo No python interpreter found in PATH. +exit /b 1 + +:found_python +if not exist ".venv" ( + "%PYTHON_BIN%" -m venv .venv +) + +call .venv\Scripts\activate.bat + +pip install -r requirements.txt + +call .venv\Scripts\python.exe app.py +endlocal diff --git a/run_app.command b/run_app.command index 942f0f1..f9776bb 100755 --- a/run_app.command +++ b/run_app.command @@ -1,7 +1,11 @@ -# /opt/miniconda3/bin/python is required by the user #!/bin/zsh cd "$(dirname "$0")" -PYTHON_BIN="/opt/miniconda3/bin/python" +# Prefer python3 but fall back to python; allow overriding via env +PYTHON_BIN="${PYTHON_BIN:-$(command -v python3 || command -v python)}" +if [[ -z "$PYTHON_BIN" ]]; then + echo "No python interpreter found in PATH." + exit 1 +fi # Create a virtual environment if missing, then activate it if [[ ! -d ".venv" ]]; then diff --git a/run_app.sh b/run_app.sh new file mode 100755 index 0000000..3894f6c --- /dev/null +++ b/run_app.sh @@ -0,0 +1,24 @@ +#!/bin/bash +set -euo pipefail + +cd "$(dirname "$0")" + +# Prefer python3 but fall back to python; allow override via environment +PYTHON_BIN="${PYTHON_BIN:-$(command -v python3 || command -v python)}" +if [[ -z "$PYTHON_BIN" ]]; then + echo "No python interpreter found in PATH." + exit 1 +fi + +# Create a virtual environment if missing, then activate it +if [[ ! -d ".venv" ]]; then + "$PYTHON_BIN" -m venv .venv +fi + +source .venv/bin/activate + +# Ensure dependencies are available +pip install -r requirements.txt + +# Start the Flask app on port 8888 +exec .venv/bin/python app.py diff --git a/static/modules/gallery.js b/static/modules/gallery.js index 6cf33b4..35ff8fd 100644 --- a/static/modules/gallery.js +++ b/static/modules/gallery.js @@ -61,7 +61,7 @@ export function createGallery({ galleryGrid, onSelect }) { deleteBtn.title = 'Delete image'; deleteBtn.addEventListener('click', async (e) => { e.stopPropagation(); - if (!confirm('Are you sure you want to delete this image?')) return; + const filename = imageUrl.split('/').pop().split('?')[0]; try { diff --git a/templates/index.html b/templates/index.html index 5dba4f5..b7a15ce 100644 --- a/templates/index.html +++ b/templates/index.html @@ -9,7 +9,9 @@ - + @@ -59,8 +61,8 @@
@@ -101,11 +103,18 @@ @@ -132,4 +141,4 @@ - + \ No newline at end of file