|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>AI Adult Content Creator | OnlyFans Toolkit</title> |
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
|
|
<script src="https://cdn.jsdelivr.net/npm/face-api.js"></script> |
|
|
<style> |
|
|
.editor-container { |
|
|
background: linear-gradient(135deg, #2b2d42 0%, #1a1a2e 100%); |
|
|
} |
|
|
.tool-btn { |
|
|
transition: all 0.3s ease; |
|
|
} |
|
|
.tool-btn:hover { |
|
|
transform: translateY(-3px); |
|
|
box-shadow: 0 10px 20px rgba(255, 20, 147, 0.3); |
|
|
} |
|
|
.preview-box { |
|
|
box-shadow: 0 20px 25px -5px rgba(0,0,0,0.3), 0 10px 10px -5px rgba(0,0,0,0.1); |
|
|
} |
|
|
.slider-thumb::-webkit-slider-thumb { |
|
|
-webkit-appearance: none; |
|
|
appearance: none; |
|
|
width: 20px; |
|
|
height: 20px; |
|
|
border-radius: 50%; |
|
|
background: #ff1493; |
|
|
cursor: pointer; |
|
|
} |
|
|
.tab-active { |
|
|
border-bottom: 3px solid #ff1493; |
|
|
} |
|
|
.image-thumbnail { |
|
|
transition: all 0.2s ease; |
|
|
} |
|
|
.image-thumbnail:hover { |
|
|
transform: scale(1.05); |
|
|
box-shadow: 0 4px 6px rgba(255, 20, 147, 0.3); |
|
|
} |
|
|
.image-thumbnail.active { |
|
|
border: 2px solid #ff1493; |
|
|
} |
|
|
.prompt-box { |
|
|
min-height: 100px; |
|
|
resize: vertical; |
|
|
} |
|
|
.pose-preview { |
|
|
background-size: contain; |
|
|
background-repeat: no-repeat; |
|
|
background-position: center; |
|
|
} |
|
|
.processing-overlay { |
|
|
position: absolute; |
|
|
top: 0; |
|
|
left: 0; |
|
|
right: 0; |
|
|
bottom: 0; |
|
|
background: rgba(0,0,0,0.8); |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
color: white; |
|
|
z-index: 10; |
|
|
} |
|
|
.face-landmarks { |
|
|
position: absolute; |
|
|
width: 20px; |
|
|
height: 20px; |
|
|
background: rgba(255, 20, 147, 0.7); |
|
|
border-radius: 50%; |
|
|
transform: translate(-50%, -50%); |
|
|
} |
|
|
.selection-box { |
|
|
position: absolute; |
|
|
border: 2px dashed #ff1493; |
|
|
background: rgba(255, 20, 147, 0.2); |
|
|
z-index: 5; |
|
|
} |
|
|
.predictive-options { |
|
|
position: absolute; |
|
|
background: #2b2d42; |
|
|
border-radius: 4px; |
|
|
box-shadow: 0 2px 10px rgba(0,0,0,0.3); |
|
|
z-index: 20; |
|
|
padding: 5px; |
|
|
border: 1px solid #ff1493; |
|
|
} |
|
|
.predictive-option { |
|
|
padding: 8px 12px; |
|
|
cursor: pointer; |
|
|
border-radius: 3px; |
|
|
color: white; |
|
|
} |
|
|
.predictive-option:hover { |
|
|
background: #ff1493; |
|
|
} |
|
|
.effect-thumb { |
|
|
background-size: cover; |
|
|
background-position: center; |
|
|
} |
|
|
.nsfw-badge { |
|
|
position: absolute; |
|
|
top: 10px; |
|
|
right: 10px; |
|
|
background: #ff1493; |
|
|
color: white; |
|
|
padding: 2px 8px; |
|
|
border-radius: 12px; |
|
|
font-size: 10px; |
|
|
font-weight: bold; |
|
|
} |
|
|
.category-tag { |
|
|
display: inline-block; |
|
|
background: rgba(255, 20, 147, 0.2); |
|
|
color: #ff1493; |
|
|
padding: 2px 8px; |
|
|
border-radius: 12px; |
|
|
font-size: 12px; |
|
|
margin-right: 5px; |
|
|
margin-bottom: 5px; |
|
|
} |
|
|
.face-detection-box { |
|
|
position: absolute; |
|
|
border: 2px solid #00ff00; |
|
|
background: rgba(0, 255, 0, 0.1); |
|
|
z-index: 5; |
|
|
} |
|
|
.face-swap-preview { |
|
|
position: relative; |
|
|
overflow: hidden; |
|
|
} |
|
|
.face-swap-canvas { |
|
|
position: absolute; |
|
|
top: 0; |
|
|
left: 0; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body class="bg-gray-900 text-gray-100"> |
|
|
|
|
|
<header class="bg-gray-800 shadow-sm"> |
|
|
<div class="container mx-auto px-4 py-4 flex justify-between items-center"> |
|
|
<div class="flex items-center space-x-2"> |
|
|
<i class="fas fa-venus-mars text-pink-600 text-2xl"></i> |
|
|
<h1 class="text-xl font-bold">AI Adult Content Creator</h1> |
|
|
</div> |
|
|
<nav class="hidden md:flex space-x-8"> |
|
|
<a href="#" class="text-gray-400 hover:text-pink-500 font-medium">Dashboard</a> |
|
|
<a href="#" class="text-gray-400 hover:text-pink-500 font-medium">Templates</a> |
|
|
<a href="#" class="text-gray-400 hover:text-pink-500 font-medium">Analytics</a> |
|
|
<a href="#" class="text-gray-400 hover:text-pink-500 font-medium">Monetization</a> |
|
|
</nav> |
|
|
<div class="flex items-center space-x-4"> |
|
|
<button class="px-4 py-2 rounded-md text-gray-400 hover:bg-gray-700"> |
|
|
<i class="fas fa-bell text-xl"></i> |
|
|
</button> |
|
|
<button class="px-4 py-2 bg-pink-600 text-white rounded-md hover:bg-pink-700"> |
|
|
<i class="fas fa-rocket mr-2"></i> Premium |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</header> |
|
|
|
|
|
|
|
|
<main class="container mx-auto px-4 py-8"> |
|
|
<div class="flex flex-col lg:flex-row gap-8"> |
|
|
|
|
|
<div class="w-full lg:w-1/4 bg-gray-800 rounded-xl shadow-md p-6 h-fit"> |
|
|
<div class="flex border-b border-gray-700 mb-6"> |
|
|
<button id="image-tab" class="tab-active px-4 py-2 font-medium text-pink-500">Photo</button> |
|
|
<button id="video-tab" class="px-4 py-2 font-medium text-gray-400 hover:text-pink-500">Video</button> |
|
|
</div> |
|
|
|
|
|
<div id="image-tools"> |
|
|
|
|
|
<div class="mb-6"> |
|
|
<h3 class="font-medium text-gray-300 mb-3">Upload Content</h3> |
|
|
<div class="border-2 border-dashed border-gray-700 rounded-lg p-4 text-center cursor-pointer hover:bg-gray-700 mb-3" id="upload-area"> |
|
|
<i class="fas fa-cloud-upload-alt text-3xl text-gray-500 mb-2"></i> |
|
|
<p class="text-gray-400">Drag & drop NSFW content</p> |
|
|
<p class="text-sm text-gray-500 mt-1">or click to browse</p> |
|
|
<input type="file" id="file-upload" class="hidden" accept="image/*,video/*" multiple> |
|
|
</div> |
|
|
<div id="thumbnail-container" class="grid grid-cols-3 gap-2 mt-2 max-h-40 overflow-y-auto"></div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="mb-6"> |
|
|
<h3 class="font-medium text-gray-300 mb-3">AI Enhancement Prompts</h3> |
|
|
<div class="relative"> |
|
|
<textarea id="ai-prompt" class="w-full border border-gray-700 bg-gray-700 rounded-md p-2 text-sm prompt-box" placeholder="Describe your desired enhancements (e.g. 'make ass bigger', 'enhance curves', 'add toy')"></textarea> |
|
|
<div class="absolute right-2 bottom-2 text-gray-400 text-xs"> |
|
|
<span id="char-count">0</span>/200 |
|
|
</div> |
|
|
</div> |
|
|
<div class="mt-2 grid grid-cols-2 gap-2"> |
|
|
<button onclick="applyPrompt('Enhance curves, make ass bigger, smooth skin, perfect lighting')" class="px-2 py-1 bg-gray-700 text-xs rounded hover:bg-pink-600 hover:text-white">Body Enhance</button> |
|
|
<button onclick="applyPrompt('Add realistic dildo, perfect lighting, professional look')" class="px-2 py-1 bg-gray-700 text-xs rounded hover:bg-pink-600 hover:text-white">Add Toy</button> |
|
|
<button onclick="showFaceSwapModal()" class="px-2 py-1 bg-gray-700 text-xs rounded hover:bg-pink-600 hover:text-white">Face Swap</button> |
|
|
<button onclick="applyPrompt('Smooth skin, remove blemishes, perfect lighting, professional retouch')" class="px-2 py-1 bg-gray-700 text-xs rounded hover:bg-pink-600 hover:text-white">Skin Perfect</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="mb-6"> |
|
|
<h3 class="font-medium text-gray-300 mb-3">Content Categories</h3> |
|
|
<div class="flex flex-wrap"> |
|
|
<span class="category-tag cursor-pointer" onclick="applyPrompt('Solo female, teasing, implied nudity, perfect lighting')">Solo Tease</span> |
|
|
<span class="category-tag cursor-pointer" onclick="applyPrompt('Explicit solo, legs spread, close-up, wet look')">Explicit Solo</span> |
|
|
<span class="category-tag cursor-pointer" onclick="applyPrompt('Butt plug, anal play, bent over, high resolution')">Anal Play</span> |
|
|
<span class="category-tag cursor-pointer" onclick="applyPrompt('Dildo play, penetration, wet look, professional lighting')">Toy Play</span> |
|
|
<span class="category-tag cursor-pointer" onclick="applyPrompt('Lingerie, stockings, high heels, seductive pose')">Lingerie</span> |
|
|
<span class="category-tag cursor-pointer" onclick="applyPrompt('Outdoor, risky, public flash, natural lighting')">Outdoor</span> |
|
|
<span class="category-tag cursor-pointer" onclick="applyPrompt('BDSM, restraints, domination, professional setup')">BDSM</span> |
|
|
<span class="category-tag cursor-pointer" onclick="applyPrompt('Cosplay, fantasy, roleplay, perfect costume')">Cosplay</span> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="mb-6"> |
|
|
<h3 class="font-medium text-gray-300 mb-3">Body Enhancements</h3> |
|
|
<div class="space-y-4"> |
|
|
<div> |
|
|
<label class="block text-sm text-gray-400 mb-1">Breast Size <span class="text-pink-500" id="breast-value">0%</span></label> |
|
|
<input type="range" class="w-full slider-thumb" id="breast-slider" min="-30" max="50" value="0" oninput="updateBodyParam('breast', this.value)"> |
|
|
<div class="flex justify-between text-xs text-gray-500"> |
|
|
<span>Smaller</span> |
|
|
<span>Larger</span> |
|
|
</div> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-sm text-gray-400 mb-1">Ass Size <span class="text-pink-500" id="ass-value">0%</span></label> |
|
|
<input type="range" class="w-full slider-thumb" id="ass-slider" min="-20" max="60" value="0" oninput="updateBodyParam('ass', this.value)"> |
|
|
<div class="flex justify-between text-xs text-gray-500"> |
|
|
<span>Smaller</span> |
|
|
<span>Bubble</span> |
|
|
</div> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-sm text-gray-400 mb-1">Waist Slimness <span class="text-pink-500" id="waist-value">0%</span></label> |
|
|
<input type="range" class="w-full slider-thumb" id="waist-slider" min="-30" max="40" value="0" oninput="updateBodyParam('waist', this.value)"> |
|
|
<div class="flex justify-between text-xs text-gray-500"> |
|
|
<span>Thicker</span> |
|
|
<span>Slimmer</span> |
|
|
</div> |
|
|
</div> |
|
|
<div> |
|
|
<label class="block text-sm text-gray-400 mb-1">Pussy Plumpness <span class="text-pink-500" id="pussy-value">0%</span></label> |
|
|
<input type="range" class="w-full slider-thumb" id="pussy-slider" min="-20" max="40" value="0" oninput="updateBodyParam('pussy', this.value)"> |
|
|
<div class="flex justify-between text-xs text-gray-500"> |
|
|
<span>Less</span> |
|
|
<span>More</span> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="mb-6"> |
|
|
<h3 class="font-medium text-gray-300 mb-3">Toys & Props</h3> |
|
|
<div class="grid grid-cols-3 gap-3"> |
|
|
<button class="tool-btn flex flex-col items-center p-3 bg-gray-700 rounded-lg hover:bg-pink-600 hover:text-white" onclick="applyPrompt('Add realistic pink dildo to scene, perfect lighting, professional look')"> |
|
|
<i class="fas fa-dildo text-pink-500 text-xl mb-1"></i> |
|
|
<span class="text-xs">Dildo</span> |
|
|
</button> |
|
|
<button class="tool-btn flex flex-col items-center p-3 bg-gray-700 rounded-lg hover:bg-pink-600 hover:text-white" onclick="applyPrompt('Add jeweled butt plug, make it look realistic, perfect lighting')"> |
|
|
<i class="fas fa-gem text-pink-500 text-xl mb-1"></i> |
|
|
<span class="text-xs">Butt Plug</span> |
|
|
</button> |
|
|
<button class="tool-btn flex flex-col items-center p-3 bg-gray-700 rounded-lg hover:bg-pink-600 hover:text-white" onclick="applyPrompt('Add vibrator to hand, make it look realistic, perfect lighting')"> |
|
|
<i class="fas fa-vibrate text-pink-500 text-xl mb-1"></i> |
|
|
<span class="text-xs">Vibrator</span> |
|
|
</button> |
|
|
<button class="tool-btn flex flex-col items-center p-3 bg-gray-700 rounded-lg hover:bg-pink-600 hover:text-white" onclick="applyPrompt('Add realistic cum effect on body, perfect lighting, professional look')"> |
|
|
<i class="fas fa-tint text-pink-500 text-xl mb-1"></i> |
|
|
<span class="text-xs">Cum Effect</span> |
|
|
</button> |
|
|
<button class="tool-btn flex flex-col items-center p-3 bg-gray-700 rounded-lg hover:bg-pink-600 hover:text-white" onclick="applyPrompt('Add rope bondage to wrists and ankles, realistic texture, perfect lighting')"> |
|
|
<i class="fas fa-hands-bound text-pink-500 text-xl mb-1"></i> |
|
|
<span class="text-xs">Bondage</span> |
|
|
</button> |
|
|
<button class="tool-btn flex flex-col items-center p-3 bg-gray-700 rounded-lg hover:bg-pink-600 hover:text-white" onclick="applyPrompt('Add lace lingerie set, make it look realistic, perfect lighting')"> |
|
|
<i class="fas fa-tshirt text-pink-500 text-xl mb-1"></i> |
|
|
<span class="text-xs">Lingerie</span> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="mb-6"> |
|
|
<h3 class="font-medium text-gray-300 mb-3">Pose Generator</h3> |
|
|
<div class="grid grid-cols-3 gap-2 mb-3"> |
|
|
<div class="pose-preview h-16 bg-gray-700 rounded cursor-pointer hover:ring-2 hover:ring-pink-500" onclick="applyPose('bent-over')" style="background-image: url('https://i.imgur.com/JQlE0gP.png')"></div> |
|
|
<div class="pose-preview h-16 bg-gray-700 rounded cursor-pointer hover:ring-2 hover:ring-pink-500" onclick="applyPose('legs-spread')" style="background-image: url('https://i.imgur.com/5XkJQqG.png')"></div> |
|
|
<div class="pose-preview h-16 bg-gray-700 rounded cursor-pointer hover:ring-2 hover:ring-pink-500" onclick="applyPose('doggy')" style="background-image: url('https://i.imgur.com/8zJqWQk.png')"></div> |
|
|
<div class="pose-preview h-16 bg-gray-700 rounded cursor-pointer hover:ring-2 hover:ring-pink-500" onclick="applyPose('missionary')" style="background-image: url('https://i.imgur.com/3mJQkqG.png')"></div> |
|
|
<div class="pose-preview h-16 bg-gray-700 rounded cursor-pointer hover:ring-2 hover:ring-pink-500" onclick="applyPose('squat')" style="background-image: url('https://i.imgur.com/7XkJQqG.png')"></div> |
|
|
<div class="pose-preview h-16 bg-gray-700 rounded cursor-pointer hover:ring-2 hover:ring-pink-500" onclick="applyPose('standing-tease')" style="background-image: url('https://i.imgur.com/9zJqWQk.png')"></div> |
|
|
</div> |
|
|
<button class="w-full py-2 bg-pink-900 text-pink-300 rounded-md text-sm hover:bg-pink-700 hover:text-white" onclick="showCustomPoseModal()"> |
|
|
<i class="fas fa-plus mr-1"></i> Custom Pose |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="mb-6"> |
|
|
<h3 class="font-medium text-gray-300 mb-3">Background</h3> |
|
|
<div class="grid grid-cols-3 gap-3"> |
|
|
<button class="tool-btn flex flex-col items-center p-3 bg-gray-700 rounded-lg hover:bg-pink-600 hover:text-white" onclick="applyPrompt('Change background to luxury bedroom, perfect lighting, professional setup')"> |
|
|
<i class="fas fa-bed text-pink-500 text-xl mb-1"></i> |
|
|
<span class="text-xs">Bedroom</span> |
|
|
</button> |
|
|
<button class="tool-btn flex flex-col items-center p-3 bg-gray-700 rounded-lg hover:bg-pink-600 hover:text-white" onclick="applyPrompt('Change background to hotel room, perfect lighting, professional setup')"> |
|
|
<i class="fas fa-hotel text-pink-500 text-xl mb-1"></i> |
|
|
<span class="text-xs">Hotel</span> |
|
|
</button> |
|
|
<button class="tool-btn flex flex-col items-center p-3 bg-gray-700 rounded-lg hover:bg-pink-600 hover:text-white" onclick="applyPrompt('Change background to outdoor pool, perfect lighting, professional setup')"> |
|
|
<i class="fas fa-umbrella-beach text-pink-500 text-xl mb-1"></i> |
|
|
<span class="text-xs">Outdoor</span> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div id="video-tools" class="hidden"> |
|
|
|
|
|
<div class="text-center py-8 text-gray-500"> |
|
|
<i class="fas fa-video text-3xl mb-2"></i> |
|
|
<p>Video editing coming soon!</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="w-full lg:w-2/4 editor-container rounded-xl p-6"> |
|
|
<div class="preview-box bg-gray-800 rounded-lg overflow-hidden relative" style="height: 600px;"> |
|
|
<div class="absolute inset-0 flex items-center justify-center" id="placeholder"> |
|
|
<div class="text-center"> |
|
|
<i class="fas fa-image text-gray-600 text-5xl mb-3"></i> |
|
|
<p class="text-gray-500">Upload NSFW content to start editing</p> |
|
|
</div> |
|
|
</div> |
|
|
<div id="preview-container" class="relative w-full h-full hidden"> |
|
|
<img id="preview-image" src="" alt="" class="w-full h-full object-contain"> |
|
|
<video id="preview-video" controls class="hidden w-full h-full object-contain"></video> |
|
|
<canvas id="face-swap-canvas" class="face-swap-canvas"></canvas> |
|
|
</div> |
|
|
<div id="processing-overlay" class="processing-overlay hidden"> |
|
|
<i class="fas fa-spinner fa-spin text-4xl mb-4 text-pink-500"></i> |
|
|
<p id="processing-text" class="text-xl">Processing your content...</p> |
|
|
<div class="w-full bg-gray-700 rounded-full h-2.5 mt-4 max-w-md"> |
|
|
<div id="progress-bar" class="bg-pink-500 h-2.5 rounded-full" style="width: 0%"></div> |
|
|
</div> |
|
|
<div id="ai-suggestions" class="mt-6 text-center max-w-md hidden"> |
|
|
<h4 class="font-medium mb-2">AI Suggestions</h4> |
|
|
<div class="grid grid-cols-2 gap-2"> |
|
|
<button class="px-3 py-1 bg-pink-700 text-white rounded text-sm hover:bg-pink-600" onclick="applySuggestion('enhance-curves')">Enhance Curves</button> |
|
|
<button class="px-3 py-1 bg-pink-700 text-white rounded text-sm hover:bg-pink-600" onclick="applySuggestion('add-toy')">Add Toy</button> |
|
|
<button class="px-3 py-1 bg-pink-700 text-white rounded text-sm hover:bg-pink-600" onclick="applySuggestion('smooth-skin')">Smooth Skin</button> |
|
|
<button class="px-3 py-1 bg-pink-700 text-white rounded text-sm hover:bg-pink-600" onclick="applySuggestion('sexier-pose')">Sexier Pose</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="nsfw-badge hidden">NSFW</div> |
|
|
</div> |
|
|
|
|
|
<div class="mt-6 flex flex-wrap justify-center gap-3"> |
|
|
<button id="undo-btn" class="px-4 py-2 bg-gray-700 text-gray-300 rounded-md hover:bg-gray-600 disabled:opacity-50" disabled> |
|
|
<i class="fas fa-undo mr-2"></i> Undo |
|
|
</button> |
|
|
<button id="redo-btn" class="px-4 py-2 bg-gray-700 text-gray-300 rounded-md hover:bg-gray-600 disabled:opacity-50" disabled> |
|
|
<i class="fas fa-redo mr-2"></i> Redo |
|
|
</button> |
|
|
<button id="reset-btn" class="px-4 py-2 bg-gray-700 text-gray-300 rounded-md hover:bg-gray-600" onclick="resetEditor()"> |
|
|
<i class="fas fa-trash-alt mr-2"></i> Reset |
|
|
</button> |
|
|
<button id="auto-enhance-btn" class="px-6 py-2 bg-pink-800 text-pink-200 rounded-md hover:bg-pink-700 hover:text-white" onclick="autoEnhance()"> |
|
|
<i class="fas fa-magic mr-2"></i> Auto-Enhance |
|
|
</button> |
|
|
<button id="process-btn" class="px-6 py-2 bg-pink-600 text-white rounded-md hover:bg-pink-700" onclick="processImage()"> |
|
|
<i class="fas fa-cogs mr-2"></i> Process |
|
|
</button> |
|
|
<button id="save-btn" class="px-6 py-2 bg-purple-600 text-white rounded-md hover:bg-purple-700" onclick="saveImage()"> |
|
|
<i class="fas fa-download mr-2"></i> Save |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="predictive-suggestions" class="mt-6 bg-gray-800 rounded-lg p-4 hidden"> |
|
|
<h3 class="font-medium text-gray-300 mb-3 flex justify-between items-center"> |
|
|
<span>AI Suggestions</span> |
|
|
<button onclick="hidePredictiveSuggestions()" class="text-gray-400 hover:text-white"> |
|
|
<i class="fas fa-times"></i> |
|
|
</button> |
|
|
</h3> |
|
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-3"> |
|
|
<button class="px-3 py-2 bg-gray-700 rounded text-sm hover:bg-pink-600 hover:text-white flex items-center" onclick="applySuggestion('enhance-curves')"> |
|
|
<i class="fas fa-venus mr-2"></i> Enhance Curves |
|
|
</button> |
|
|
<button class="px-3 py-2 bg-gray-700 rounded text-sm hover:bg-pink-600 hover:text-white flex items-center" onclick="applySuggestion('bigger-ass')"> |
|
|
<i class="fas fa-pepper-hot mr-2"></i> Bigger Ass |
|
|
</button> |
|
|
<button class="px-3 py-2 bg-gray-700 rounded text-sm hover:bg-pink-600 hover:text-white flex items-center" onclick="applySuggestion('add-toy')"> |
|
|
<i class="fas fa-dildo mr-2"></i> Add Toy |
|
|
</button> |
|
|
<button class="px-3 py-2 bg-gray-700 rounded text-sm hover:bg-pink-600 hover:text-white flex items-center" onclick="applySuggestion('sexier-pose')"> |
|
|
<i class="fas fa-fire mr-2"></i> Sexier Pose |
|
|
</button> |
|
|
<button class="px-3 py-2 bg-gray-700 rounded text-sm hover:bg-pink-600 hover:text-white flex items-center" onclick="applySuggestion('smooth-skin')"> |
|
|
<i class="fas fa-spa mr-2"></i> Smooth Skin |
|
|
</button> |
|
|
<button class="px-3 py-2 bg-gray-700 rounded text-sm hover:bg-pink-600 hover:text-white flex items-center" onclick="applySuggestion('better-lighting')"> |
|
|
<i class="fas fa-lightbulb mr-2"></i> Better Lighting |
|
|
</button> |
|
|
<button class="px-3 py-2 bg-gray-700 rounded text-sm hover:bg-pink-600 hover:text-white flex items-center" onclick="applySuggestion('add-lingerie')"> |
|
|
<i class="fas fa-tshirt mr-2"></i> Add Lingerie |
|
|
</button> |
|
|
<button class="px-3 py-2 bg-gray-700 rounded text-sm hover:bg-pink-600 hover:text-white flex items-center" onclick="applySuggestion('add-plug')"> |
|
|
<i class="fas fa-gem mr-2"></i> Add Plug |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="w-full lg:w-1/4 bg-gray-800 rounded-xl shadow-md p-6 h-fit"> |
|
|
<div class="flex justify-between items-center mb-4"> |
|
|
<h3 class="font-medium text-gray-300">Content Library</h3> |
|
|
<button class="text-pink-500 hover:text-pink-300" onclick="document.getElementById('file-upload').click()"> |
|
|
<i class="fas fa-plus"></i> |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
<div id="reference-container" class="space-y-3 max-h-64 overflow-y-auto"> |
|
|
|
|
|
</div> |
|
|
|
|
|
<div class="mt-6"> |
|
|
<h3 class="font-medium text-gray-300 mb-3">Quick Edits</h3> |
|
|
<div class="grid grid-cols-2 gap-3"> |
|
|
<button class="px-3 py-2 bg-gray-700 rounded text-sm hover:bg-pink-600 hover:text-white" onclick="quickEdit('blur-background')"> |
|
|
<i class="fas fa-eye-slash mr-1"></i> Blur BG |
|
|
</button> |
|
|
<button class="px-3 py-2 bg-gray-700 rounded text-sm hover:bg-pink-600 hover:text-white" onclick="quickEdit('enhance-nipples')"> |
|
|
<i class="fas fa-dot-circle mr-1"></i> Enhance Nipples |
|
|
</button> |
|
|
<button class="px-3 py-2 bg-gray-700 rounded text-sm hover:bg-pink-600 hover:text-white" onclick="quickEdit('plump-lips')"> |
|
|
<i class="fas fa-lips mr-1"></i> Plump Lips |
|
|
</button> |
|
|
<button class="px-3 py-2 bg-gray-700 rounded text-sm hover:bg-pink-600 hover:text-white" onclick="quickEdit('smooth-skin')"> |
|
|
<i class="fas fa-spa mr-1"></i> Smooth Skin |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mt-6"> |
|
|
<h3 class="font-medium text-gray-300 mb-3">OnlyFans Tools</h3> |
|
|
<div class="space-y-3"> |
|
|
<button class="w-full py-2 bg-pink-700 text-white rounded-md text-sm hover:bg-pink-600 flex items-center justify-center" onclick="addWatermark()"> |
|
|
<i class="fas fa-copyright mr-2"></i> Add Watermark |
|
|
</button> |
|
|
<button class="w-full py-2 bg-purple-700 text-white rounded-md text-sm hover:bg-purple-600 flex items-center justify-center" onclick="generateTeaser()"> |
|
|
<i class="fas fa-lock-open mr-2"></i> Create Teaser |
|
|
</button> |
|
|
<button class="w-full py-2 bg-gray-700 text-white rounded-md text-sm hover:bg-gray-600 flex items-center justify-center" onclick="showSocialModal()"> |
|
|
<i class="fas fa-share-alt mr-2"></i> Social Preview |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mt-6"> |
|
|
<h3 class="font-medium text-gray-300 mb-3">Export Options</h3> |
|
|
<div class="space-y-3"> |
|
|
<div class="flex items-center"> |
|
|
<input type="radio" id="format-jpg" name="export-format" class="mr-2" checked> |
|
|
<label for="format-jpg" class="text-sm">JPG (Recommended)</label> |
|
|
</div> |
|
|
<div class="flex items-center"> |
|
|
<input type="radio" id="format-png" name="export-format" class="mr-2"> |
|
|
<label for="format-png" class="text-sm">PNG (Lossless)</label> |
|
|
</div> |
|
|
<div class="flex items-center"> |
|
|
<input type="radio" id="format-webp" name="export-format" class="mr-2"> |
|
|
<label for="format-webp" class="text-sm">WebP (Smaller)</label> |
|
|
</div> |
|
|
</div> |
|
|
<div class="mt-3"> |
|
|
<label class="block text-sm text-gray-400 mb-1">Quality</label> |
|
|
<input type="range" id="export-quality" class="w-full slider-thumb" min="50" max="100" value="90"> |
|
|
<div class="flex justify-between text-xs text-gray-500"> |
|
|
<span>Low</span> |
|
|
<span>High</span> |
|
|
</div> |
|
|
</div> |
|
|
<div class="mt-3"> |
|
|
<label class="block text-sm text-gray-400 mb-1">Resolution</label> |
|
|
<select id="export-resolution" class="w-full p-2 border border-gray-700 bg-gray-700 rounded-md text-sm"> |
|
|
<option value="original">Original</option> |
|
|
<option value="1080">1080p (HD)</option> |
|
|
<option value="720">720p</option> |
|
|
<option value="4k">4K (UHD)</option> |
|
|
</select> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mt-6"> |
|
|
<h3 class="font-medium text-gray-300 mb-3">Content Stats</h3> |
|
|
<div class="bg-gray-700 rounded-md p-3 text-sm"> |
|
|
<div class="flex justify-between mb-1"> |
|
|
<span class="text-gray-400">Engagement Score:</span> |
|
|
<span class="text-pink-400" id="engagement-score">--</span> |
|
|
</div> |
|
|
<div class="flex justify-between mb-1"> |
|
|
<span class="text-gray-400">Spicy Level:</span> |
|
|
<span class="text-pink-400" id="spicy-level">--</span> |
|
|
</div> |
|
|
<div class="flex justify-between mb-1"> |
|
|
<span class="text-gray-400">Market Value:</span> |
|
|
<span class="text-pink-400" id="market-value">--</span> |
|
|
</div> |
|
|
<div class="flex justify-between"> |
|
|
<span class="text-gray-400">AI Rating:</span> |
|
|
<span class="text-pink-400" id="ai-rating">--</span> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</main> |
|
|
|
|
|
|
|
|
<div id="face-swap-modal" class="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center hidden z-50"> |
|
|
<div class="bg-gray-800 rounded-lg p-6 w-full max-w-2xl border border-pink-500"> |
|
|
<div class="flex justify-between items-center mb-4"> |
|
|
<h3 class="text-lg font-medium text-white">Face Swap</h3> |
|
|
<button onclick="hideFaceSwapModal()" class="text-gray-400 hover:text-white"> |
|
|
<i class="fas fa-times"></i> |
|
|
</button> |
|
|
</div> |
|
|
<div class="grid grid-cols-2 gap-4 mb-4"> |
|
|
<div> |
|
|
<h4 class="text-sm font-medium mb-2 text-gray-300">Source Face</h4> |
|
|
<select id="modal-source-face" class="w-full p-2 border border-gray-700 bg-gray-700 rounded-md text-sm mb-2 text-white" onchange="updateFacePreview('source')"> |
|
|
<option value="" class="text-gray-400">Select reference image</option> |
|
|
</select> |
|
|
<div class="border border-gray-700 rounded-md h-40 bg-gray-700 flex items-center justify-center relative face-swap-preview" id="source-face-preview"> |
|
|
<span class="text-gray-500">No image selected</span> |
|
|
</div> |
|
|
</div> |
|
|
<div> |
|
|
<h4 class="text-sm font-medium mb-2 text-gray-300">Target Face</h4> |
|
|
<select id="modal-target-face" class="w-full p-2 border border-gray-700 bg-gray-700 rounded-md text-sm mb-2 text-white" onchange="updateFacePreview('target')"> |
|
|
<option value="" class="text-gray-400">Select reference image</option> |
|
|
</select> |
|
|
<div class="border border-gray-700 rounded-md h-40 bg-gray-700 flex items-center justify-center relative face-swap-preview" id="target-face-preview"> |
|
|
<span class="text-gray-500">No image selected</span> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="mb-4"> |
|
|
<label class="block text-sm text-gray-400 mb-1">Face Alignment Strength</label> |
|
|
<input type="range" id="face-align-strength" class="w-full slider-thumb" min="0" max="100" value="75"> |
|
|
</div> |
|
|
<div class="mb-4"> |
|
|
<label class="block text-sm text-gray-400 mb-1">Skin Tone Matching</label> |
|
|
<input type="range" id="skin-tone-match" class="w-full slider-thumb" min="0" max="100" value="50"> |
|
|
</div> |
|
|
<div class="mb-4"> |
|
|
<label class="block text-sm text-gray-400 mb-1">Blending Intensity</label> |
|
|
<input type="range" id="blend-intensity" class="w-full slider-thumb" min="0" max="100" value="80"> |
|
|
</div> |
|
|
<div class="flex justify-end space-x-3"> |
|
|
<button onclick="hideFaceSwapModal()" class="px-4 py-2 bg-gray-700 text-gray-300 rounded-md hover:bg-gray-600"> |
|
|
Cancel |
|
|
</button> |
|
|
<button onclick="applyModalFaceSwap()" class="px-4 py-2 bg-pink-600 text-white rounded-md hover:bg-pink-700"> |
|
|
Apply Face Swap |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="custom-pose-modal" class="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center hidden z-50"> |
|
|
<div class="bg-gray-800 rounded-lg p-6 w-full max-w-2xl border border-pink-500"> |
|
|
<div class="flex justify-between items-center mb-4"> |
|
|
<h3 class="text-lg font-medium text-white">Create Custom Pose</h3> |
|
|
<button onclick="hideCustomPoseModal()" class="text-gray-400 hover:text-white"> |
|
|
<i class="fas fa-times"></i> |
|
|
</button> |
|
|
</div> |
|
|
<div class="mb-4"> |
|
|
<label class="block text-sm text-gray-400 mb-1">Pose Description</label> |
|
|
<textarea id="pose-description" class="w-full border border-gray-700 bg-gray-700 rounded-md p-2 text-sm text-white" placeholder="Describe the pose you want (e.g. 'bent over, ass up, looking back')"></textarea> |
|
|
</div> |
|
|
<div class="mb-4"> |
|
|
<label class="block text-sm text-gray-400 mb-1">Pose Strength</label> |
|
|
<input type="range" id="pose-strength" class="w-full slider-thumb" min="0" max="100" value="50"> |
|
|
</div> |
|
|
<div class="mb-4"> |
|
|
<label class="block text-sm text-gray-400 mb-1">Suggestions</label> |
|
|
<div class="grid grid-cols-2 gap-2"> |
|
|
<button class="px-2 py-1 bg-gray-700 text-xs rounded hover:bg-pink-600 hover:text-white" onclick="document.getElementById('pose-description').value = 'bent over, ass up, looking back'">Bent Over</button> |
|
|
<button class="px-2 py-1 bg-gray-700 text-xs rounded hover:bg-pink-600 hover:text-white" onclick="document.getElementById('pose-description').value = 'legs spread, touching self'">Legs Spread</button> |
|
|
<button class="px-2 py-1 bg-gray-700 text-xs rounded hover:bg-pink-600 hover:text-white" onclick="document.getElementById('pose-description').value = 'on knees, chest up, mouth open'">Kneeling</button> |
|
|
<button class="px-2 py-1 bg-gray-700 text-xs rounded hover:bg-pink-600 hover:text-white" onclick="document.getElementById('pose-description').value = 'squatting, showing everything'">Squatting</button> |
|
|
</div> |
|
|
</div> |
|
|
<div class="flex justify-end space-x-3"> |
|
|
<button onclick="hideCustomPoseModal()" class="px-4 py-2 bg-gray-700 text-gray-300 rounded-md hover:bg-gray-600"> |
|
|
Cancel |
|
|
</button> |
|
|
<button onclick="applyCustomPose()" class="px-4 py-2 bg-pink-600 text-white rounded-md hover:bg-pink-700"> |
|
|
Create Pose |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="social-modal" class="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center hidden z-50"> |
|
|
<div class="bg-gray-800 rounded-lg p-6 w-full max-w-2xl border border-pink-500"> |
|
|
<div class="flex justify-between items-center mb-4"> |
|
|
<h3 class="text-lg font-medium text-white">Social Media Preview</h3> |
|
|
<button onclick="hideSocialModal()" class="text-gray-400 hover:text-white"> |
|
|
<i class="fas fa-times"></i> |
|
|
</button> |
|
|
</div> |
|
|
<div class="grid grid-cols-3 gap-4 mb-4"> |
|
|
<div class="bg-gray-900 p-2 rounded text-center"> |
|
|
<div class="h-32 bg-gray-700 rounded mb-2 flex items-center justify-center"> |
|
|
<i class="fas fa-instagram text-2xl text-pink-500"></i> |
|
|
</div> |
|
|
<p class="text-xs text-gray-400">Instagram</p> |
|
|
</div> |
|
|
<div class="bg-gray-900 p-2 rounded text-center"> |
|
|
<div class="h-32 bg-gray-700 rounded mb-2 flex items-center justify-center"> |
|
|
<i class="fas fa-twitter text-2xl text-blue-400"></i> |
|
|
</div> |
|
|
<p class="text-xs text-gray-400">Twitter</p> |
|
|
</div> |
|
|
<div class="bg-gray-900 p-2 rounded text-center"> |
|
|
<div class="h-32 bg-gray-700 rounded mb-2 flex items-center justify-center"> |
|
|
<i class="fas fa-tiktok text-2xl text-white"></i> |
|
|
</div> |
|
|
<p class="text-xs text-gray-400">TikTok</p> |
|
|
</div> |
|
|
</div> |
|
|
<div class="mb-4"> |
|
|
<label class="block text-sm text-gray-400 mb-1">Preview Text</label> |
|
|
<input type="text" class="w-full p-2 border border-gray-700 bg-gray-700 rounded-md text-sm text-white" value="New content coming soon! 🔥"> |
|
|
</div> |
|
|
<div class="flex justify-end space-x-3"> |
|
|
<button onclick="hideSocialModal()" class="px-4 py-2 bg-gray-700 text-gray-300 rounded-md hover:bg-gray-600"> |
|
|
Close |
|
|
</button> |
|
|
<button onclick="copySocialPreview()" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700"> |
|
|
Copy Preview |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
|
|
|
let uploadedImages = []; |
|
|
let currentImageIndex = 0; |
|
|
let editHistory = []; |
|
|
let currentHistoryIndex = -1; |
|
|
let bodyParams = { |
|
|
breast: 0, |
|
|
ass: 0, |
|
|
waist: 0, |
|
|
pussy: 0 |
|
|
}; |
|
|
let faceDetectionModelsLoaded = false; |
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', async function() { |
|
|
|
|
|
const promptBox = document.getElementById('ai-prompt'); |
|
|
const charCount = document.getElementById('char-count'); |
|
|
|
|
|
promptBox.addEventListener('input', function() { |
|
|
charCount.textContent = this.value.length; |
|
|
|
|
|
|
|
|
if(this.value.length > 10 && !this.value.includes('ass') && !this.value.includes('toy') && !this.value.includes('plug')) { |
|
|
showPredictiveSuggestions(); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
const sliders = ['breast', 'ass', 'waist', 'pussy']; |
|
|
sliders.forEach(slider => { |
|
|
const sliderElement = document.getElementById(`${slider}-slider`); |
|
|
const valueElement = document.getElementById(`${slider}-value`); |
|
|
|
|
|
sliderElement.addEventListener('input', function() { |
|
|
valueElement.textContent = `${this.value}%`; |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('undo-btn').addEventListener('click', undo); |
|
|
document.getElementById('redo-btn').addEventListener('click', redo); |
|
|
|
|
|
|
|
|
await loadFaceDetectionModels(); |
|
|
|
|
|
|
|
|
let firstUpload = true; |
|
|
}); |
|
|
|
|
|
|
|
|
async function loadFaceDetectionModels() { |
|
|
try { |
|
|
await faceapi.nets.tinyFaceDetector.loadFromUri('/models'); |
|
|
await faceapi.nets.faceLandmark68Net.loadFromUri('/models'); |
|
|
await faceapi.nets.faceRecognitionNet.loadFromUri('/models'); |
|
|
faceDetectionModelsLoaded = true; |
|
|
console.log('Face detection models loaded successfully'); |
|
|
} catch (error) { |
|
|
console.error('Error loading face detection models:', error); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const fileUpload = document.getElementById('file-upload'); |
|
|
const uploadArea = document.getElementById('upload-area'); |
|
|
const thumbnailContainer = document.getElementById('thumbnail-container'); |
|
|
const referenceContainer = document.getElementById('reference-container'); |
|
|
const previewImage = document.getElementById('preview-image'); |
|
|
const previewVideo = document.getElementById('preview-video'); |
|
|
const placeholder = document.getElementById('placeholder'); |
|
|
const previewContainer = document.getElementById('preview-container'); |
|
|
const nsfwBadge = document.querySelector('.nsfw-badge'); |
|
|
|
|
|
|
|
|
uploadArea.addEventListener('dragover', (e) => { |
|
|
e.preventDefault(); |
|
|
uploadArea.classList.add('bg-pink-900', 'border-pink-500'); |
|
|
}); |
|
|
|
|
|
uploadArea.addEventListener('dragleave', () => { |
|
|
uploadArea.classList.remove('bg-pink-900', 'border-pink-500'); |
|
|
}); |
|
|
|
|
|
uploadArea.addEventListener('drop', (e) => { |
|
|
e.preventDefault(); |
|
|
uploadArea.classList.remove('bg-pink-900', 'border-pink-500'); |
|
|
if (e.dataTransfer.files.length > 0) { |
|
|
fileUpload.files = e.dataTransfer.files; |
|
|
handleFileUpload(); |
|
|
} |
|
|
}); |
|
|
|
|
|
uploadArea.addEventListener('click', () => fileUpload.click()); |
|
|
|
|
|
fileUpload.addEventListener('change', handleFileUpload); |
|
|
|
|
|
function handleFileUpload() { |
|
|
const files = fileUpload.files; |
|
|
if (!files || files.length === 0) return; |
|
|
|
|
|
for (let i = 0; i < files.length; i++) { |
|
|
const file = files[i]; |
|
|
if (!file.type.startsWith('image/') && !file.type.startsWith('video/')) continue; |
|
|
|
|
|
const reader = new FileReader(); |
|
|
reader.onload = function(event) { |
|
|
const mediaData = { |
|
|
id: Date.now() + i, |
|
|
src: event.target.result, |
|
|
name: file.name, |
|
|
originalSrc: event.target.result, |
|
|
type: file.type.startsWith('image/') ? 'image' : 'video' |
|
|
}; |
|
|
uploadedImages.push(mediaData); |
|
|
|
|
|
|
|
|
const thumbnail = document.createElement('div'); |
|
|
thumbnail.className = `image-thumbnail relative ${uploadedImages.length === 1 ? 'active' : ''}`; |
|
|
thumbnail.innerHTML = ` |
|
|
${mediaData.type === 'image' ? |
|
|
`<img src="${mediaData.src}" class="w-full h-full object-cover rounded">` : |
|
|
`<video src="${mediaData.src}" class="w-full h-full object-cover rounded"></video>`} |
|
|
<div class="absolute inset-0 flex items-center justify-center bg-black bg-opacity-0 hover:bg-opacity-30 transition-all duration-200"> |
|
|
<button onclick="setActiveImage(${mediaData.id})" class="text-white opacity-0 hover:opacity-100"> |
|
|
<i class="fas fa-check-circle text-xl"></i> |
|
|
</button> |
|
|
</div> |
|
|
`; |
|
|
thumbnailContainer.appendChild(thumbnail); |
|
|
|
|
|
|
|
|
const referenceItem = document.createElement('div'); |
|
|
referenceItem.className = 'flex items-center p-2 bg-gray-700 rounded-md hover:bg-gray-600'; |
|
|
referenceItem.innerHTML = ` |
|
|
<div class="w-10 h-10 bg-gray-600 rounded-md mr-3 overflow-hidden"> |
|
|
${mediaData.type === 'image' ? |
|
|
`<img src="${mediaData.src}" class="w-full h-full object-cover">` : |
|
|
`<video src="${mediaData.src}" class="w-full h-full object-cover"></video>`} |
|
|
</div> |
|
|
<div class="flex-1 truncate"> |
|
|
<p class="text-sm font-medium truncate">${file.name}</p> |
|
|
<p class="text-xs text-gray-400">${(file.size / 1024).toFixed(1)} KB</p> |
|
|
</div> |
|
|
<button onclick="removeImage(${mediaData.id})" class="text-gray-400 hover:text-pink-500 ml-2"> |
|
|
<i class="fas fa-times"></i> |
|
|
</button> |
|
|
`; |
|
|
referenceContainer.appendChild(referenceItem); |
|
|
|
|
|
|
|
|
updateSelectOptions(); |
|
|
|
|
|
|
|
|
if (uploadedImages.length === 1) { |
|
|
setActiveImage(mediaData.id); |
|
|
nsfwBadge.classList.remove('hidden'); |
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
autoEnhance(); |
|
|
}, 500); |
|
|
} |
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
analyzeContent(); |
|
|
}, 1000); |
|
|
}; |
|
|
reader.readAsDataURL(file); |
|
|
} |
|
|
} |
|
|
|
|
|
function setActiveImage(id) { |
|
|
const imageData = uploadedImages.find(img => img.id === id); |
|
|
if (!imageData) return; |
|
|
|
|
|
currentImageIndex = uploadedImages.findIndex(img => img.id === id); |
|
|
|
|
|
if (imageData.type === 'image') { |
|
|
previewImage.src = imageData.src; |
|
|
previewImage.classList.remove('hidden'); |
|
|
previewVideo.classList.add('hidden'); |
|
|
} else { |
|
|
previewVideo.src = imageData.src; |
|
|
previewVideo.classList.remove('hidden'); |
|
|
previewImage.classList.add('hidden'); |
|
|
} |
|
|
|
|
|
previewContainer.classList.remove('hidden'); |
|
|
placeholder.classList.add('hidden'); |
|
|
|
|
|
|
|
|
document.querySelectorAll('.image-thumbnail').forEach((thumb, index) => { |
|
|
if (index === currentImageIndex) { |
|
|
thumb.classList.add('active'); |
|
|
} else { |
|
|
thumb.classList.remove('active'); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
saveToHistory(); |
|
|
|
|
|
|
|
|
analyzeContent(); |
|
|
} |
|
|
|
|
|
function removeImage(id) { |
|
|
const index = uploadedImages.findIndex(img => img.id === id); |
|
|
if (index === -1) return; |
|
|
|
|
|
uploadedImages.splice(index, 1); |
|
|
|
|
|
|
|
|
thumbnailContainer.children[index].remove(); |
|
|
|
|
|
|
|
|
referenceContainer.children[index].remove(); |
|
|
|
|
|
|
|
|
updateSelectOptions(); |
|
|
|
|
|
|
|
|
if (currentImageIndex === index) { |
|
|
if (uploadedImages.length > 0) { |
|
|
|
|
|
const newIndex = Math.min(index, uploadedImages.length - 1); |
|
|
setActiveImage(uploadedImages[newIndex].id); |
|
|
} else { |
|
|
|
|
|
previewImage.src = ''; |
|
|
previewVideo.src = ''; |
|
|
previewContainer.classList.add('hidden'); |
|
|
placeholder.classList.remove('hidden'); |
|
|
nsfwBadge.classList.add('hidden'); |
|
|
} |
|
|
} else if (currentImageIndex > index) { |
|
|
currentImageIndex--; |
|
|
} |
|
|
|
|
|
saveToHistory(); |
|
|
} |
|
|
|
|
|
function updateSelectOptions() { |
|
|
const modalSourceFace = document.getElementById('modal-source-face'); |
|
|
const modalTargetFace = document.getElementById('modal-target-face'); |
|
|
|
|
|
|
|
|
[modalSourceFace, modalTargetFace].forEach(select => { |
|
|
while (select.options.length > 1) { |
|
|
select.remove(1); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
uploadedImages.forEach((img, index) => { |
|
|
const option = document.createElement('option'); |
|
|
option.value = img.id; |
|
|
option.textContent = `Image ${index + 1}`; |
|
|
option.className = 'text-white'; |
|
|
|
|
|
const option2 = option.cloneNode(true); |
|
|
|
|
|
modalSourceFace.appendChild(option); |
|
|
modalTargetFace.appendChild(option2); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function saveToHistory() { |
|
|
if (uploadedImages.length === 0) return; |
|
|
|
|
|
|
|
|
if (currentHistoryIndex < editHistory.length - 1) { |
|
|
editHistory = editHistory.slice(0, currentHistoryIndex + 1); |
|
|
} |
|
|
|
|
|
|
|
|
const historyItem = { |
|
|
imageSrc: uploadedImages[currentImageIndex].src, |
|
|
bodyParams: {...bodyParams} |
|
|
}; |
|
|
|
|
|
editHistory.push(historyItem); |
|
|
currentHistoryIndex = editHistory.length - 1; |
|
|
|
|
|
|
|
|
document.getElementById('undo-btn').disabled = currentHistoryIndex <= 0; |
|
|
document.getElementById('redo-btn').disabled = currentHistoryIndex >= editHistory.length - 1; |
|
|
} |
|
|
|
|
|
function undo() { |
|
|
if (currentHistoryIndex <= 0) return; |
|
|
|
|
|
currentHistoryIndex--; |
|
|
applyHistoryState(); |
|
|
} |
|
|
|
|
|
function redo() { |
|
|
if (currentHistoryIndex >= editHistory.length - 1) return; |
|
|
|
|
|
currentHistoryIndex++; |
|
|
applyHistoryState(); |
|
|
} |
|
|
|
|
|
function applyHistoryState() { |
|
|
const historyItem = editHistory[currentHistoryIndex]; |
|
|
|
|
|
|
|
|
if (uploadedImages[currentImageIndex].type === 'image') { |
|
|
previewImage.src = historyItem.imageSrc; |
|
|
} else { |
|
|
previewVideo.src = historyItem.imageSrc; |
|
|
} |
|
|
uploadedImages[currentImageIndex].src = historyItem.imageSrc; |
|
|
|
|
|
|
|
|
bodyParams = {...historyItem.bodyParams}; |
|
|
document.getElementById('breast-slider').value = bodyParams.breast; |
|
|
document.getElementById('ass-slider').value = bodyParams.ass; |
|
|
document.getElementById('waist-slider').value = bodyParams.waist; |
|
|
document.getElementById('pussy-slider').value = bodyParams.pussy; |
|
|
|
|
|
|
|
|
document.getElementById('breast-value').textContent = `${bodyParams.breast}%`; |
|
|
document.getElementById('ass-value').textContent = `${bodyParams.ass}%`; |
|
|
document.getElementById('waist-value').textContent = `${bodyParams.waist}%`; |
|
|
document.getElementById('pussy-value').textContent = `${bodyParams.pussy}%`; |
|
|
|
|
|
|
|
|
document.getElementById('undo-btn').disabled = currentHistoryIndex <= 0; |
|
|
document.getElementById('redo-btn').disabled = currentHistoryIndex >= editHistory.length - 1; |
|
|
} |
|
|
|
|
|
|
|
|
function resetEditor() { |
|
|
if (uploadedImages.length === 0) return; |
|
|
|
|
|
if (uploadedImages[currentImageIndex].type === 'image') { |
|
|
previewImage.src = uploadedImages[currentImageIndex].originalSrc; |
|
|
} else { |
|
|
previewVideo.src = uploadedImages[currentImageIndex].originalSrc; |
|
|
} |
|
|
uploadedImages[currentImageIndex].src = uploadedImages[currentImageIndex].originalSrc; |
|
|
|
|
|
|
|
|
document.getElementById('breast-slider').value = 0; |
|
|
document.getElementById('ass-slider').value = 0; |
|
|
document.getElementById('waist-slider').value = 0; |
|
|
document.getElementById('pussy-slider').value = 0; |
|
|
bodyParams = { breast: 0, ass: 0, waist: 0, pussy: 0 }; |
|
|
|
|
|
|
|
|
document.getElementById('breast-value').textContent = "0%"; |
|
|
document.getElementById('ass-value').textContent = "0%"; |
|
|
document.getElementById('waist-value').textContent = "0%"; |
|
|
document.getElementById('pussy-value').textContent = "0%"; |
|
|
|
|
|
|
|
|
editHistory = []; |
|
|
currentHistoryIndex = -1; |
|
|
document.getElementById('undo-btn').disabled = true; |
|
|
document.getElementById('redo-btn').disabled = true; |
|
|
|
|
|
saveToHistory(); |
|
|
} |
|
|
|
|
|
|
|
|
async function processImage() { |
|
|
if (!previewImage.src && !previewVideo.src) { |
|
|
alert('Please upload NSFW content first'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const promptBox = document.getElementById('ai-prompt'); |
|
|
const prompt = promptBox.value; |
|
|
|
|
|
if (!prompt && Object.values(bodyParams).every(val => val === 0)) { |
|
|
alert('Please enter a prompt or adjust body parameters'); |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
const overlay = document.getElementById('processing-overlay'); |
|
|
const progressBar = document.getElementById('progress-bar'); |
|
|
const processingText = document.getElementById('processing-text'); |
|
|
const aiSuggestions = document.getElementById('ai-suggestions'); |
|
|
|
|
|
overlay.classList.remove('hidden'); |
|
|
processingText.textContent = 'Enhancing your content...'; |
|
|
aiSuggestions.classList.add('hidden'); |
|
|
|
|
|
|
|
|
let progress = 0; |
|
|
const interval = setInterval(() => { |
|
|
progress += Math.random() * 10; |
|
|
if (progress > 100) progress = 100; |
|
|
progressBar.style.width = `${progress}%`; |
|
|
|
|
|
if (progress === 100) { |
|
|
clearInterval(interval); |
|
|
processingText.textContent = 'Finalizing enhancements...'; |
|
|
|
|
|
setTimeout(() => { |
|
|
overlay.classList.add('hidden'); |
|
|
progressBar.style.width = '0%'; |
|
|
|
|
|
|
|
|
|
|
|
if (uploadedImages[currentImageIndex].type === 'image') { |
|
|
const canvas = document.createElement('canvas'); |
|
|
const ctx = canvas.getContext('2d'); |
|
|
const img = new Image(); |
|
|
img.onload = function() { |
|
|
canvas.width = img.width; |
|
|
canvas.height = img.height; |
|
|
ctx.drawImage(img, 0, 0); |
|
|
|
|
|
|
|
|
if (bodyParams.breast !== 0) { |
|
|
|
|
|
ctx.fillStyle = 'rgba(255, 200, 200, 0.1)'; |
|
|
ctx.beginPath(); |
|
|
ctx.ellipse(canvas.width * 0.4, canvas.height * 0.4, |
|
|
canvas.width * 0.1 * (1 + bodyParams.breast/100), |
|
|
canvas.height * 0.15 * (1 + bodyParams.breast/100), |
|
|
0, 0, Math.PI * 2); |
|
|
ctx.fill(); |
|
|
|
|
|
ctx.beginPath(); |
|
|
ctx.ellipse(canvas.width * 0.6, canvas.height * 0.4, |
|
|
canvas.width * 0.1 * (1 + bodyParams.breast/100), |
|
|
canvas.height * 0.15 * (1 + bodyParams.breast/100), |
|
|
0, 0, Math.PI * 2); |
|
|
ctx.fill(); |
|
|
} |
|
|
|
|
|
if (bodyParams.ass !== 0) { |
|
|
|
|
|
ctx.fillStyle = 'rgba(200, 200, 255, 0.1)'; |
|
|
ctx.beginPath(); |
|
|
ctx.ellipse(canvas.width * 0.5, canvas.height * 0.6, |
|
|
canvas.width * 0.15 * (1 + bodyParams.ass/100), |
|
|
canvas.height * 0.1 * (1 + bodyParams.ass/100), |
|
|
0, 0, Math.PI * 2); |
|
|
ctx.fill(); |
|
|
} |
|
|
|
|
|
if (bodyParams.waist !== 0) { |
|
|
|
|
|
ctx.fillStyle = 'rgba(255, 255, 200, 0.1)'; |
|
|
ctx.beginPath(); |
|
|
ctx.ellipse(canvas.width * 0.5, canvas.height * 0.5, |
|
|
canvas.width * 0.1 * (1 - bodyParams.waist/200), |
|
|
canvas.height * 0.15 * (1 - bodyParams.waist/200), |
|
|
0, 0, Math.PI * 2); |
|
|
ctx.fill(); |
|
|
} |
|
|
|
|
|
if (bodyParams.pussy !== 0) { |
|
|
|
|
|
ctx.fillStyle = 'rgba(255, 150, 200, 0.1)'; |
|
|
ctx.beginPath(); |
|
|
ctx.ellipse(canvas.width * 0.5, canvas.height * 0.7, |
|
|
canvas.width * 0.08 * (1 + bodyParams.pussy/100), |
|
|
canvas.height * 0.05 * (1 + bodyParams.pussy/100), |
|
|
0, 0, Math.PI * 2); |
|
|
ctx.fill(); |
|
|
} |
|
|
|
|
|
|
|
|
if (prompt.includes('smooth') || prompt.includes('skin')) { |
|
|
|
|
|
ctx.filter = 'blur(1px)'; |
|
|
ctx.drawImage(canvas, 0, 0); |
|
|
ctx.filter = 'none'; |
|
|
} |
|
|
|
|
|
if (prompt.includes('toy') || prompt.includes('dildo') || prompt.includes('vibrator')) { |
|
|
|
|
|
ctx.fillStyle = '#ff69b4'; |
|
|
ctx.beginPath(); |
|
|
ctx.ellipse(canvas.width * 0.5, canvas.height * 0.7, |
|
|
canvas.width * 0.05, canvas.height * 0.15, |
|
|
Math.PI/4, 0, Math.PI * 2); |
|
|
ctx.fill(); |
|
|
} |
|
|
|
|
|
if (prompt.includes('plug') || prompt.includes('anal')) { |
|
|
|
|
|
ctx.fillStyle = '#9370db'; |
|
|
ctx.beginPath(); |
|
|
ctx.ellipse(canvas.width * 0.5, canvas.height * 0.3, |
|
|
canvas.width * 0.03, canvas.height * 0.03, |
|
|
0, 0, Math.PI * 2); |
|
|
ctx.fill(); |
|
|
|
|
|
|
|
|
ctx.fillStyle = '#ffd700'; |
|
|
ctx.beginPath(); |
|
|
ctx.ellipse(canvas.width * 0.5, canvas.height * 0.28, |
|
|
canvas.width * 0.01, canvas.height * 0.01, |
|
|
0, 0, Math.PI * 2); |
|
|
ctx.fill(); |
|
|
} |
|
|
|
|
|
|
|
|
const newSrc = canvas.toDataURL('image/jpeg'); |
|
|
previewImage.src = newSrc; |
|
|
uploadedImages[currentImageIndex].src = newSrc; |
|
|
|
|
|
saveToHistory(); |
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
overlay.classList.remove('hidden'); |
|
|
processingText.textContent = 'Enhancement complete!'; |
|
|
aiSuggestions.classList.remove('hidden'); |
|
|
progressBar.style.width = '0%'; |
|
|
|
|
|
|
|
|
setTimeout(() => { |
|
|
overlay.classList.add('hidden'); |
|
|
}, 5000); |
|
|
}, 500); |
|
|
}; |
|
|
img.src = previewImage.src; |
|
|
} |
|
|
}, 500); |
|
|
} |
|
|
}, 100); |
|
|
} |
|
|
|
|
|
|
|
|
function autoEnhance() { |
|
|
if (!previewImage.src && !previewVideo.src) { |
|
|
alert('Please upload NSFW content first'); |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
document.getElementById('breast-slider').value = 15; |
|
|
document.getElementById('ass-slider').value = 25; |
|
|
document.getElementById('waist-slider').value = 20; |
|
|
document.getElementById('pussy-slider').value = 10; |
|
|
|
|
|
|
|
|
document.getElementById('breast-value').textContent = "15%"; |
|
|
document.getElementById('ass-value').textContent = "25%"; |
|
|
document.getElementById('waist-value').textContent = "20%"; |
|
|
document.getElementById('pussy-value').textContent = "10%"; |
|
|
|
|
|
|
|
|
bodyParams.breast = 15; |
|
|
bodyParams.ass = 25; |
|
|
bodyParams.waist = 20; |
|
|
bodyParams.pussy = 10; |
|
|
|
|
|
|
|
|
document.getElementById('ai-prompt').value = "Enhance curves, smooth skin, perfect lighting, professional look"; |
|
|
|
|
|
|
|
|
processImage(); |
|
|
} |
|
|
|
|
|
|
|
|
function saveImage() { |
|
|
if (!previewImage.src && !previewVideo.src) { |
|
|
alert('Please upload NSFW content first'); |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
let format = 'jpg'; |
|
|
if (document.getElementById('format-png').checked) format = 'png'; |
|
|
if (document.getElementById('format-webp').checked) format = 'webp'; |
|
|
|
|
|
|
|
|
const quality = document.getElementById('export-quality').value; |
|
|
|
|
|
|
|
|
const link = document.createElement('a'); |
|
|
if (previewImage.src) { |
|
|
link.href = previewImage.src; |
|
|
link.download = `onlyfans-content.${format}`; |
|
|
} else { |
|
|
link.href = previewVideo.src; |
|
|
link.download = 'onlyfans-video.mp4'; |
|
|
} |
|
|
document.body.appendChild(link); |
|
|
link.click(); |
|
|
document.body.removeChild(link); |
|
|
|
|
|
|
|
|
alert(`Content saved as ${format.toUpperCase()} at ${quality}% quality!`); |
|
|
} |
|
|
|
|
|
|
|
|
function updateBodyParam(param, value) { |
|
|
bodyParams[param] = parseInt(value); |
|
|
document.getElementById(`${param}-value`).textContent = `${value}%`; |
|
|
saveToHistory(); |
|
|
} |
|
|
|
|
|
|
|
|
function applyPrompt(prompt) { |
|
|
document.getElementById('ai-prompt').value = prompt; |
|
|
document.getElementById('char-count').textContent = prompt.length; |
|
|
processImage(); |
|
|
} |
|
|
|
|
|
|
|
|
async function showFaceSwapModal() { |
|
|
if (uploadedImages.length < 2) { |
|
|
alert('You need at least 2 images to perform face swap'); |
|
|
return; |
|
|
} |
|
|
|
|
|
document.getElementById('face-swap-modal').classList.remove('hidden'); |
|
|
|
|
|
|
|
|
if (!faceDetectionModelsLoaded) { |
|
|
await loadFaceDetectionModels(); |
|
|
} |
|
|
} |
|
|
|
|
|
function hideFaceSwapModal() { |
|
|
document.getElementById('face-swap-modal').classList.add('hidden'); |
|
|
} |
|
|
|
|
|
async function updateFacePreview(type) { |
|
|
const select = type === 'source' ? |
|
|
document.getElementById('modal-source-face') : |
|
|
document.getElementById('modal-target-face'); |
|
|
const preview = document.getElementById(`${type}-face-preview`); |
|
|
|
|
|
const selectedId = select.value; |
|
|
if (!selectedId) { |
|
|
preview.innerHTML = '<span class="text-gray-500">No image selected</span>'; |
|
|
return; |
|
|
} |
|
|
|
|
|
const imageData = uploadedImages.find(img => img.id == selectedId); |
|
|
if (!imageData) return; |
|
|
|
|
|
|
|
|
preview.innerHTML = ''; |
|
|
|
|
|
|
|
|
const img = document.createElement('img'); |
|
|
img.src = imageData.src; |
|
|
img.className = 'w-full h-full object-contain'; |
|
|
preview.appendChild(img); |
|
|
|
|
|
|
|
|
if (faceDetectionModelsLoaded) { |
|
|
const detections = await faceapi.detectAllFaces(img, new faceapi.TinyFaceDetectorOptions()) |
|
|
.withFaceLandmarks() |
|
|
.withFaceDescriptors(); |
|
|
|
|
|
if (detections.length > 0) { |
|
|
|
|
|
detections.forEach(detection => { |
|
|
const box = detection.detection.box; |
|
|
const faceBox = document.createElement('div'); |
|
|
faceBox.className = 'face-detection-box'; |
|
|
faceBox.style.width = `${box.width}px`; |
|
|
faceBox.style.height = `${box.height}px`; |
|
|
faceBox.style.left = `${box.x}px`; |
|
|
faceBox.style.top = `${box.y}px`; |
|
|
preview.appendChild(faceBox); |
|
|
}); |
|
|
|
|
|
|
|
|
detections.forEach(detection => { |
|
|
const landmarks = detection.landmarks; |
|
|
landmarks.positions.forEach(point => { |
|
|
const dot = document.createElement('div'); |
|
|
dot.className = 'face-landmarks'; |
|
|
dot.style.left = `${point.x}px`; |
|
|
dot.style.top = `${point.y}px`; |
|
|
preview.appendChild(dot); |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
async function applyModalFaceSwap() { |
|
|
const sourceId = document.getElementById('modal-source-face').value; |
|
|
const targetId = document.getElementById('modal-target-face').value; |
|
|
|
|
|
if (!sourceId || !targetId) { |
|
|
alert('Please select both source and target faces'); |
|
|
return; |
|
|
} |
|
|
|
|
|
if (sourceId === targetId) { |
|
|
alert('Source and target faces must be different'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const strength = document.getElementById('face-align-strength').value; |
|
|
const skinTone = document.getElementById('skin-tone-match').value; |
|
|
const blendIntensity = document.getElementById('blend-intensity').value; |
|
|
|
|
|
|
|
|
const overlay = document.getElementById('processing-overlay'); |
|
|
const progressBar = document.getElementById('progress-bar'); |
|
|
const processingText = document.getElementById('processing-text'); |
|
|
|
|
|
overlay.classList.remove('hidden'); |
|
|
processingText.textContent = 'Swapping faces...'; |
|
|
|
|
|
|
|
|
const sourceImg = uploadedImages.find(img => img.id == sourceId); |
|
|
const targetImg = uploadedImages.find(img => img.id == targetId); |
|
|
|
|
|
|
|
|
const sourceCanvas = document.createElement('canvas'); |
|
|
const sourceCtx = sourceCanvas.getContext('2d'); |
|
|
const targetCanvas = document.createElement('canvas'); |
|
|
const targetCtx = targetCanvas.getContext('2d'); |
|
|
|
|
|
|
|
|
const sourceImage = new Image(); |
|
|
sourceImage.onload = async function() { |
|
|
sourceCanvas.width = sourceImage.width; |
|
|
sourceCanvas.height = sourceImage.height; |
|
|
sourceCtx.drawImage(sourceImage, 0, 0); |
|
|
|
|
|
|
|
|
const targetImage = new Image(); |
|
|
targetImage.onload = async function() { |
|
|
targetCanvas.width = targetImage.width; |
|
|
targetCanvas.height = targetImage.height; |
|
|
targetCtx.drawImage(targetImage, 0, 0); |
|
|
|
|
|
|
|
|
|
|
|
</html> |