title: Amalfa Ad Creative Studio
emoji: ✨
colorFrom: pink
colorTo: yellow
sdk: docker
app_port: 7860
Amalfa Ad Creative Studio
Full-stack app: paste an Amalfa product page URL → scrape product data → run a deep marketing analysis (OpenAI) → generate 20 ad creative packages (product-focused + no-product) for Meta (Facebook/Instagram).
What it does
- Scrape – Fetches the product page and extracts name, description, price, images, brand, category (JSON-LD, meta tags, fallbacks).
- Analyze – GPT-based analysis: positioning, taglines, ideal customer, emotional triggers, price framing, ad angles, USPs, copy direction.
- Creatives – Generates 20 ad concepts (17 product-focused, 3 no-product) with headlines, body copy, image prompts, and layout notes.
- Generate ad images – Select any of the 50 creatives, pick a Replicate image model, and generate actual ad images from the prompts (via Replicate).
Setup
Backend (Python)
cd backend
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
Create a .env file (copy from .env.example):
cp .env.example .env
Set your API keys in .env:
OPENAI_API_KEY=sk-your-key-here
REPLICATE_API_TOKEN=r8_your-replicate-token # optional; for generating ad images
KIE_API_KEY=your-kie-key # optional; Kie.ai for nano-banana-pro (kie.ai/nano-banana-pro)
Get a Replicate token at https://replicate.com/account/api-tokens (needed only for the “Generate ads” feature).
Optional – save generated creatives to Cloudflare R2:
Set R2_ENDPOINT, R2_BUCKET_NAME, R2_ACCESS_KEY, and R2_SECRET_KEY in .env. When set, each generated ad image is uploaded to R2 and the API returns the R2 presigned URL (7-day expiry) instead of the temporary Replicate URL.
Run the API:
uvicorn app.main:app --reload --host 0.0.0.0 --port 8002
API docs: http://127.0.0.1:8002/docs
Frontend (React + Vite)
cd frontend
npm install
npm run dev
The frontend is configured to proxy /api to http://127.0.0.1:8002, so run the backend first.
Creating a user (script only)
The app requires login. Create users with the script (no signup UI). Use the backend venv so dependencies are available:
# From repo root (use backend venv)
backend/.venv/bin/python backend/scripts/create_user.py --username admin --password yourpassword
Or from the backend directory (after activating the venv):
cd backend
source .venv/bin/activate # Windows: .venv\Scripts\activate
python scripts/create_user.py -u admin -p yourpassword
Users are stored in backend/data/users.json. For production, set JWT_SECRET in .env to a long random string.
Usage
- Open http://localhost:5174 and sign in with a user created via the script above.
- Paste an Amalfa product URL (e.g.
https://amalfa.in/products/amor-infinity-heart-ring-for-women) - Click Generate
- Wait for scrape → analysis → creatives (about 1–2 minutes)
- Browse Product card, Marketing Analysis, and Ad Creatives (click a creative to expand copy + image prompt).
- Generate ad images: tick the checkboxes for the creatives you want, choose an image model (e.g. nano-banana, flux-2-max), click Generate ads. Generated images appear in a grid below (Replicate URLs may expire after a while).
API
- POST /api/run – Body:
{ "url": "https://amalfa.in/products/..." }
Returns:{ product_data, analysis, creatives } - POST /api/scrape – Body:
{ "url": "..." }
Returns: product data only - GET /api/image-models – Returns list of Replicate image models for ad generation
- POST /api/generate-ads – Body:
{ "creatives": [ full creative JSON per item (id, concept_name, scene_prompt, ad_copy, etc.) ], "model_key": "nano-banana" }. The whole creative object is sent to the image model as the prompt.
Returns:{ "results": [{ "creative_id", "image_url", "error" }] }
Project layout
amalfa fr/
├── backend/
│ ├── app/
│ │ ├── main.py # FastAPI app, /api/run, /api/scrape
│ │ ├── scraper.py # Amalfa product page scraper
│ │ ├── llm.py # OpenAI client, call_llm, extract_json
│ │ ├── analysis.py # Deep product analysis prompt
│ │ ├── creatives.py # Ad creative generation prompt
│ │ └── replicate_image.py # Replicate image generation for selected creatives
│ ├── requirements.txt
│ └── .env.example
├── frontend/
│ ├── src/
│ │ ├── App.jsx # URL input, run pipeline, Product / Analysis / Creatives UI
│ │ ├── main.jsx
│ │ └── index.css
│ ├── index.html
│ ├── package.json
│ └── vite.config.js # proxy /api → backend
├── my_repo.md # Original backend script (reference)
└── README.md
Deploy on Hugging Face
- Create a new Space at huggingface.co/new-space. Choose Docker as the SDK.
- Push this repo (or copy
Dockerfile,README.md, and thebackend/andfrontend/folders) into the Space. - Secrets (required for full features): In the Space → Settings → Repository secrets, add:
OPENAI_API_KEY– required for analysis and creativesREPLICATE_API_TOKEN– required for “Generate ads” (Replicate image generation)JWT_SECRET– optional; set a long random string for production- R2 (optional):
R2_ACCESS_KEY,R2_SECRET_KEY,R2_BUCKET_NAME,R2_ENDPOINT– to store generated images in Cloudflare R2; also used to persist the gallery index so your gallery survives Space rebuilds/redeploys
- Reference images: Uploaded reference image URLs must be reachable by Replicate. Use the app URL (e.g.
https://your-username-amalfa-creative-studio.hf.space), not the hub page (https://huggingface.co/spaces/...). You can set secretBASE_URLto that URL, or leave it unset — the app will use the request host when you open the Space via its.hf.spacelink. Reference files are stored underDATA_DIR(e.g./tmpon Spaces), so they are lost if the Space restarts; upload again after a restart if needed.
The Docker image builds the React frontend and serves it from FastAPI. Default user is created from backend/data/users.json (e.g. admin – set its password via the create-user script before pushing, or change it in the repo).
Notes
- OPENAI_API_KEY is required for analysis and creatives; scraping works without it.
- REPLICATE_API_TOKEN is required only for “Generate ads” (image generation from selected creatives).
- The backend uses
gpt-4o-miniby default (set inllm.py). You can change the model there. - For production, set CORS
allow_originsinbackend/app/main.pyto your frontend origin.