<?php
// poster-proxy.php — HuggingFace Inference API poster generation
// Uses FLUX.1-schnell (Black Forest Labs) — dramatically better
// quality than SDXL, faster generation, free tier compatible.
//
// GET YOUR FREE API KEY:
// 1. Go to https://huggingface.co/settings/tokens
// 2. Create a token with "Read" or "Make calls to Inference API" permission
// 3. Paste it below
 
$HF_TOKEN = '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';  // <-- YOUR KEY
 
// ============================================================
// MODEL SELECTION — uncomment ONE line:
// ============================================================
 
// FLUX.1-schnell: Best balance of quality + speed (~5-10s, free tier)
$MODEL = 'black-forest-labs/FLUX.1-schnell';
 
// FLUX.1-dev: Highest quality, slower (~15-30s, free tier)
// $MODEL = 'black-forest-labs/FLUX.1-dev';
 
// Stable Diffusion 3.5 Large: Very good, medium speed
// $MODEL = 'stabilityai/stable-diffusion-3.5-large';
 
// Original SDXL (your old model — kept for fallback reference)
// $MODEL = 'stabilityai/stable-diffusion-xl-base-1.0';
 
// ============================================================
 
$API_URL = 'https://router.huggingface.co/hf-inference/models/' . $MODEL;
 
// Get prompt from query string
$prompt = isset($_GET['prompt']) ? $_GET['prompt'] : 'cinematic movie poster dramatic lighting';
 
// Optional: allow model override via query param (for testing)
if (isset($_GET['model'])) {
    $allowedModels = [
        
             'midj' => 'Kvikontent/midjourney-v7',
             'sd35'         => 'stabilityai/stable-diffusion-3.5-large',
        'sdxl'         => 'stabilityai/stable-diffusion-xl-base-1.0',
        
                'flux-krea'     => 'black-forest-labs/FLUX.1-Krea-dev',
  'flux-schnell' => 'black-forest-labs/FLUX.1-schnell',
        'flux-dev'     => 'black-forest-labs/FLUX.1-dev',
     
        
    ];
    $requestedModel = $_GET['model'];
    if (isset($allowedModels[$requestedModel])) {
        $MODEL = $allowedModels[$requestedModel];
        $API_URL = 'https://router.huggingface.co/hf-inference/models/' . $MODEL;
    }
}
 
// Check token
if ($HF_TOKEN === 'YOUR_HUGGINGFACE_TOKEN_HERE' || empty($HF_TOKEN)) {
    http_response_code(500);
    header('Content-Type: application/json');
    echo json_encode(['error' => 'HuggingFace token not configured.']);
    exit;
}
 
// Build payload — FLUX models support additional parameters
$payloadData = [
    'inputs' => $prompt
];
 
// FLUX models benefit from these parameters (optional, safe to include)
if (strpos($MODEL, 'FLUX') !== false) {
    $payloadData['parameters'] = [
        'num_inference_steps' => 4,    // schnell is optimized for 1-4 steps
        'width'  => 512,               // poster aspect ratio
        'height' => 768,
        'guidance_scale' => 0          // schnell doesn't use guidance
    ];
 
    // FLUX.1-dev uses more steps and guidance
    if (strpos($MODEL, 'dev') !== false) {
        $payloadData['parameters']['num_inference_steps'] = 20;
        $payloadData['parameters']['guidance_scale'] = 3.5;
    }
} elseif (strpos($MODEL, 'stable-diffusion-3') !== false) {
    // SD 3.5 parameters
    $payloadData['parameters'] = [
        'num_inference_steps' => 28,
        'width'  => 512,
        'height' => 768,
        'guidance_scale' => 7.0
    ];
} else {
    // SDXL fallback parameters
    $payloadData['parameters'] = [
        'num_inference_steps' => 30,
        'width'  => 512,
        'height' => 768,
        'guidance_scale' => 7.5
    ];
}
 
$payload = json_encode($payloadData);
 
// ---- SIMPLE FILE CACHE (optional, saves API calls) ----
$CACHE_DIR = __DIR__ . '/poster-cache';
$CACHE_TTL = 604800; // 1 week in seconds
 
$cacheKey = md5($MODEL . '|' . $prompt);
$cachePath = $CACHE_DIR . '/' . $cacheKey . '.jpg';
 
// Serve from cache if available and fresh
if (is_file($cachePath) && (time() - filemtime($cachePath)) < $CACHE_TTL) {
    header('Content-Type: image/jpeg');
    header('Cache-Control: public, max-age=604800');
    header('X-Poster-Cache: HIT');
    readfile($cachePath);
    exit;
}
 
// ---- API CALL ----
 
if (!function_exists('curl_init')) {
    // Fallback: file_get_contents
    $opts = [
        'http' => [
            'method'  => 'POST',
            'header'  => "Authorization: Bearer $HF_TOKEN\r\n" .
                         "Content-Type: application/json\r\n" .
                         "Accept: image/png\r\n",
            'content' => $payload,
            'timeout' => 180
        ]
    ];
    $context = stream_context_create($opts);
    $response = @file_get_contents($API_URL, false, $context);
 
    if ($response === false) {
        http_response_code(502);
        header('Content-Type: application/json');
        echo json_encode(['error' => 'file_get_contents failed.']);
        exit;
    }
 
    // Cache the result
    if (!is_dir($CACHE_DIR)) @mkdir($CACHE_DIR, 0755, true);
    if (is_dir($CACHE_DIR) && strlen($response) > 1000) {
        @file_put_contents($cachePath, $response);
    }
 
    header('Content-Type: image/jpeg');
    header('Cache-Control: public, max-age=604800');
    header('X-Poster-Cache: MISS');
    echo $response;
    exit;
}
 
// Primary: cURL
$ch = curl_init($API_URL);
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => $payload,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT        => 180,      // longer timeout for dev model
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $HF_TOKEN,
        'Content-Type: application/json',
        'Accept: image/png'
    ]
]);
 
$response    = curl_exec($ch);
$httpCode    = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
$curlError   = curl_error($ch);
curl_close($ch);
 
// cURL error
if ($curlError) {
    http_response_code(502);
    header('Content-Type: application/json');
    echo json_encode(['error' => 'cURL error: ' . $curlError]);
    exit;
}
 
// Model loading (503)
if ($httpCode === 503) {
    header('Content-Type: application/json');
    http_response_code(503);
    echo json_encode([
        'error' => 'Model is loading, retry in 20-30 seconds',
        'model' => $MODEL,
        'retry' => true
    ]);
    exit;
}
 
// Rate limit (429)
if ($httpCode === 429) {
    header('Content-Type: application/json');
    http_response_code(429);
    echo json_encode([
        'error' => 'Rate limited — too many requests. Wait a moment.',
        'model' => $MODEL,
        'retry' => true
    ]);
    exit;
}
 
// Other errors
if ($httpCode !== 200) {
    http_response_code($httpCode);
    header('Content-Type: application/json');
    echo json_encode([
        'error'     => 'HuggingFace API error',
        'http_code' => $httpCode,
        'model'     => $MODEL,
        'body'      => substr($response, 0, 500)
    ]);
    exit;
}
 
// Check if response is an image
if (strpos($contentType, 'image') !== false ||
    (strlen($response) > 1000 && substr($response, 0, 4) !== '{"er')) {
 
    // Cache the result
    if (!is_dir($CACHE_DIR)) @mkdir($CACHE_DIR, 0755, true);
    if (is_dir($CACHE_DIR) && strlen($response) > 1000) {
        @file_put_contents($cachePath, $response);
    }
 
    header('Content-Type: image/jpeg');
    header('Cache-Control: public, max-age=604800');
    header('X-Poster-Cache: MISS');
    header('X-Poster-Model: ' . $MODEL);
    echo $response;
    exit;
}
 
// Unexpected response
http_response_code(500);
header('Content-Type: application/json');
echo json_encode([
    'error' => 'Unexpected response from model',
    'model' => $MODEL,
    'body'  => substr($response, 0, 500)
]);
exit;
 