Initial commit

This commit is contained in:
Aherobo Ovie Victor
2025-10-27 18:43:42 +01:00
commit e559238be5
46 changed files with 3813 additions and 0 deletions
+295
View File
@@ -0,0 +1,295 @@
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
import uvicorn
import os
import logging
import json
import base64
import io
from viral_velocity_scorer import ViralVelocityScorer
from image_enhancer import ImageEnhancer
from typing import Optional
from pydantic import BaseModel
# Set up logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('api.log'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
# Request model
class UserPreferences(BaseModel):
aesthetic: str = ""
niche: str = ""
target_audience: str = ""
content_type: str = ""
brand_voice: str = ""
class ScoreImageRequest(BaseModel):
image: str # base64 encoded image
user_preferences: Optional[UserPreferences] = None
app = FastAPI(title="Viral Velocity API", description="AI-powered social media image scoring with personalization")
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # In production, specify actual origins
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Mount static files (frontend)
if os.path.exists("frontend"):
app.mount("/static", StaticFiles(directory="frontend"), name="static")
# Initialize the scorer and enhancer
try:
logger.info("Initializing ViralVelocityScorer for API...")
scorer = ViralVelocityScorer()
logger.info("ViralVelocityScorer initialized successfully")
logger.info("Initializing ImageEnhancer for API...")
enhancer = ImageEnhancer()
logger.info("ImageEnhancer initialized successfully")
except Exception as e:
logger.error(f"Failed to initialize services: {e}")
print(f"Failed to initialize services: {e}")
scorer = None
enhancer = None
@app.get("/")
def read_root():
"""Serve the main frontend page"""
logger.info("Root endpoint accessed")
if os.path.exists("frontend/index.html"):
return FileResponse("frontend/index.html")
else:
return {"message": "Viral Velocity API - Social Media Image Scorer with Personalization"}
@app.post("/score-image")
async def score_image(request: ScoreImageRequest):
"""
Score an image using the 4-pillar Viral Velocity scoring system with personalization
Request body:
{
"image": "base64 string",
"user_preferences": {
"aesthetic": "",
"niche": "",
"target_audience": "",
"content_type": "",
"brand_voice": ""
}
}
"""
logger.info("Received image scoring request")
if not scorer:
logger.error("Scorer not initialized, returning 500 error")
raise HTTPException(status_code=500, detail="Scorer not initialized")
try:
# Decode base64 image
logger.info("Decoding base64 image...")
image_data = base64.b64decode(request.image)
image = io.BytesIO(image_data)
# Save temporarily for processing
with open("temp_image.jpg", "wb") as f:
f.write(image_data)
logger.info("Image decoded and saved temporarily")
# Analyze the image
logger.info("Starting efficient image analysis...")
user_prefs_dict = request.user_preferences.dict() if request.user_preferences else None
result = scorer.analyze_image_efficient("temp_image.jpg", user_prefs_dict)
# Clean up temporary file
os.remove("temp_image.jpg")
# Handle content moderation rejections specifically
if result.get('status') == 'rejected':
logger.warning(f"Content rejected: {result['rejection_reason']}")
return result
# Handle other errors
if 'error' in result:
logger.error(f"Analysis returned error: {result['error']}")
raise HTTPException(status_code=500, detail=result['error'])
logger.info(f"Analysis completed successfully. Final score: {result.get('final_score', 'N/A')}")
return result
except Exception as e:
logger.error(f"Error processing image: {str(e)}", exc_info=True)
raise HTTPException(status_code=500, detail=f"Analysis failed: {str(e)}")
@app.get("/health")
def health_check():
"""Health check endpoint"""
logger.info("Health check endpoint accessed")
return {
"status": "healthy",
"scorer_initialized": scorer is not None
}
@app.get("/scoring-weights")
def get_scoring_weights():
"""Get the current scoring weights"""
logger.info("Scoring weights endpoint accessed")
if not scorer:
logger.error("Scorer not initialized, returning 500 error")
raise HTTPException(status_code=500, detail="Scorer not initialized")
return {
"weights": scorer.weights,
"description": "4-pillar scoring system weights"
}
@app.get("/available-preferences")
def get_available_preferences():
"""Get available preference options for users"""
logger.info("Available preferences endpoint accessed")
return {
"aesthetics": [
"Y2K", "Maximalist", "Minimalist", "Ethereal Grunge",
"Cottagecore", "Dark Academia", "Cyberpunk", "Vintage",
"Modern", "Boho", "Streetwear", "High Fashion",
"Luxury", "Casual", "Professional", "Artistic"
],
"niches": [
"Fashion Influencer", "Food Blogger", "Travel Photographer",
"Fitness Influencer", "Tech Professional", "Artist",
"Business Professional", "Lifestyle Blogger", "Beauty Influencer",
"Parenting Blogger", "Pet Influencer", "Gaming Content Creator"
],
"target_audiences": [
"Gen Z", "Millennials", "Gen X", "Boomers",
"Teenagers", "Young Adults", "Professionals", "Parents",
"Students", "Entrepreneurs", "Creative Professionals"
],
"content_types": [
"Instagram Post", "Instagram Story", "TikTok Video",
"YouTube Thumbnail", "LinkedIn Post", "Twitter Post",
"Facebook Post", "Pinterest Pin", "Blog Post"
],
"brand_voices": [
"Playful and Trendy", "Professional and Trustworthy",
"Casual and Relatable", "Luxury and Sophisticated",
"Fun and Energetic", "Calm and Minimalist",
"Bold and Confident", "Warm and Friendly"
]
}
@app.get("/moderation-status")
def get_moderation_status():
"""Get content moderation system status"""
logger.info("Moderation status endpoint accessed")
if not scorer:
logger.error("Scorer not initialized, returning 500 error")
raise HTTPException(status_code=500, detail="Scorer not initialized")
moderation_status = scorer.content_moderator.get_moderation_status()
return {
"moderation_system": "Google Cloud Vision SafeSearch",
"status": moderation_status,
"description": "Content safety and moderation system for detecting inappropriate content"
}
@app.post("/enhance-image")
async def enhance_image(request: ScoreImageRequest):
"""
Generate 5 enhanced versions of an image using AI
Request body:
{
"image": "base64 string",
"user_preferences": {
"aesthetic": "",
"niche": "",
"target_audience": "",
"content_type": "",
"brand_voice": ""
}
}
"""
logger.info("Received image enhancement request")
if not enhancer:
logger.error("Enhancer not initialized, returning 500 error")
raise HTTPException(status_code=500, detail="Enhancer not initialized")
try:
# Decode base64 image
logger.info("Decoding base64 image for enhancement...")
image_data = base64.b64decode(request.image)
image = io.BytesIO(image_data)
# Save temporarily for processing
with open("temp_enhance_image.jpg", "wb") as f:
f.write(image_data)
logger.info("Image decoded and saved temporarily for enhancement")
# Prepare user preferences
user_prefs_dict = request.user_preferences.dict() if request.user_preferences else None
# Generate enhanced images
logger.info("Starting AI image enhancement...")
result = enhancer.enhance_image("temp_enhance_image.jpg", user_prefs_dict)
# Clean up temporary file
os.remove("temp_enhance_image.jpg")
if result['status'] == 'error':
logger.error(f"Enhancement failed: {result['error']}")
raise HTTPException(status_code=500, detail=result['error'])
logger.info(f"Enhancement completed successfully. Generated {result['total_generated']} images")
# Convert enhanced images to base64 for frontend
enhanced_images_data = []
for enhanced_img in result['enhanced_images']:
try:
with open(enhanced_img['image_path'], 'rb') as f:
img_data = f.read()
img_base64 = base64.b64encode(img_data).decode()
enhanced_images_data.append({
'version': enhanced_img['version'],
'image': img_base64,
'prompt': enhanced_img['prompt'],
'image_path': enhanced_img['image_path']
})
except Exception as e:
logger.error(f"Failed to convert enhanced image {enhanced_img['version']} to base64: {e}")
return {
'status': 'success',
'message': f'Successfully generated {len(enhanced_images_data)} enhanced images',
'enhanced_images': enhanced_images_data,
'total_generated': len(enhanced_images_data),
'original_analysis': result['original_image']['analysis']
}
except Exception as e:
logger.error(f"Error processing enhancement request: {str(e)}", exc_info=True)
raise HTTPException(status_code=500, detail=f"Enhancement failed: {str(e)}")
if __name__ == "__main__":
logger.info("Starting Viral Velocity API server...")
uvicorn.run(app, host="0.0.0.0", port=5300)