MAJOR FEATURES ADDED: ====================== 🤖 ML Analysis System: - Comprehensive scene image analysis with per-scene predictions - Enhanced database schema with scene_ml_analysis table - Advanced detection for clothing colors, body types, age categories, positions, settings - Support for multiple prediction types (clothing, body, sexual acts, etc.) - Confidence scoring and ML source tracking 🧠 Enhanced Search Capabilities: - Natural language parser for complex queries (e.g., "Teenage Riley Reid creampie older man pink thong black heels red couch") - Category-based search with confidence-weighted results - ML-enhanced tag matching with automatic fallback to traditional search - Support for "Money Shot: Creampie" vs "Cum in Open Mouth" detection 🗄️ Advanced Database Schema: - Male detection: circumcised field (0/1) - Pubic hair types: natural, shaved, trimmed, landing strip, bushy, hairy - Scene ML analysis table for storing per-scene predictions - Comprehensive seed tags for all detection categories 🏗️ Dual Scraper Architecture: - Flexible import service supporting both TPDB and Adult Empire scrapers - Bulk scraper implementation for Adult Empire using multiple search strategies - Progress tracking with Server-Sent Events (SSE) for real-time updates - Graceful fallback from Adult Empire to TPDB when needed 📝 Enhanced Import System: - Individual bulk imports (performers, studios, scenes, movies) - Combined "import all" operation - Real-time progress tracking with job management - Error handling and retry mechanisms - Support for multiple import sources and strategies 🔧 Technical Improvements: - Modular component architecture for maintainability - Enhanced error handling and logging - Performance-optimized database queries with proper indexing - Configurable import limits and rate limiting - Comprehensive testing framework This commit establishes Goondex as a comprehensive adult content discovery platform with ML-powered analysis and advanced search capabilities, ready for integration with computer vision models for automated tagging and scene analysis.
271 lines
9.4 KiB
HTML
271 lines
9.4 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Logo Animation Test</title>
|
|
<style>
|
|
body { background: #1a1a1a; color: white; padding: 2rem; font-family: Arial, sans-serif; }
|
|
.logo { margin: 2rem 0; width: 180px; height: 110px; }
|
|
.logo svg { width: 100%; height: 100%; display: block; }
|
|
.goondex-logo-animated {
|
|
animation: logoBounce 1.5s ease-in-out infinite;
|
|
}
|
|
.goondex-logo-animated .nipple-left,
|
|
.goondex-logo-animated .nipple-right {
|
|
animation: nippleBounce 1.5s ease-in-out infinite;
|
|
}
|
|
.goondex-logo-animated .nipple-right {
|
|
animation-delay: 0.1s;
|
|
}
|
|
|
|
@keyframes logoBounce {
|
|
0% { transform: translateY(0) scaleY(1); }
|
|
15% { transform: translateY(-12px) scaleY(1.02); }
|
|
30% { transform: translateY(0) scaleY(0.85); }
|
|
40% { transform: translateY(3px) scaleY(1.05); }
|
|
100% { transform: translateY(0) scaleY(1); }
|
|
}
|
|
|
|
@keyframes nippleBounce {
|
|
0%, 100% { transform: translateY(0); }
|
|
15% { transform: translateY(-10px); }
|
|
30% { transform: translateY(0); }
|
|
40% { transform: translateY(2px); }
|
|
100% { transform: translateY(0); }
|
|
}
|
|
|
|
button { background: #ff5fa2; color: white; border: none; padding: 0.5rem 1rem; border-radius: 4px; margin-right: 1rem; cursor: pointer; }
|
|
.global-loader {
|
|
position: fixed;
|
|
inset: 0;
|
|
background: rgba(0, 0, 0, 0.55);
|
|
backdrop-filter: blur(2px);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 2000;
|
|
}
|
|
.global-loader .loader-content {
|
|
background: #2a2a2a;
|
|
padding: 1.5rem 2rem;
|
|
border-radius: 12px;
|
|
border: 1px solid #444;
|
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.35);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
align-items: center;
|
|
color: white;
|
|
min-width: 280px;
|
|
justify-content: center;
|
|
}
|
|
.global-loader .logo svg {
|
|
width: 90px;
|
|
height: 55px;
|
|
filter: drop-shadow(0 2px 8px rgba(255, 95, 162, 0.3));
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>Goondex Logo Animation Test</h1>
|
|
|
|
<div style="margin: 2rem 0;">
|
|
<h2>Static Logo:</h2>
|
|
<div id="static-logo" class="logo">
|
|
<img src="http://localhost:8788/static/img/logo/GOONDEX_Titty.svg" alt="Goondex" width="180" height="110">
|
|
</div>
|
|
</div>
|
|
|
|
<div style="margin: 2rem 0;">
|
|
<h2>Animated Logo:</h2>
|
|
<div id="animated-logo" class="logo">
|
|
<img src="http://localhost:8788/static/img/logo/GOONDEX_Titty.svg" alt="Goondex" width="180" height="110">
|
|
</div>
|
|
</div>
|
|
|
|
<div style="margin: 2rem 0;">
|
|
<button onclick="startAnimation()">Start Animation</button>
|
|
<button onclick="stopAnimation()">Stop Animation</button>
|
|
</div>
|
|
|
|
<div style="margin: 2rem 0;">
|
|
<button onclick="testLoader()">Test Loader (3 seconds)</button>
|
|
</div>
|
|
|
|
<div id="global-loader" class="global-loader" style="display:none;">
|
|
<div class="loader-content">
|
|
<div id="loader-logo" class="logo">
|
|
<img src="http://localhost:8788/static/img/logo/GOONDEX_Titty.svg" alt="Goondex" width="90" height="55">
|
|
</div>
|
|
<div>Working...</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
class LogoAnimator {
|
|
constructor() {
|
|
this.isAnimating = false;
|
|
this.logoElement = null;
|
|
}
|
|
|
|
init(svgElement) {
|
|
this.logoElement = svgElement;
|
|
this.identifyNipples();
|
|
}
|
|
if (!this.logoElement) return;
|
|
|
|
const paths = this.logoElement.querySelectorAll('path');
|
|
let nippleIndex = 0;
|
|
|
|
paths.forEach((path) => {
|
|
const d = path.getAttribute('d');
|
|
if (d && d.includes('1463.5643,67.636337')) {
|
|
path.classList.add('nipple-left');
|
|
nippleIndex++;
|
|
} else if (d && d.includes('70.4489,0') && nippleIndex === 1) {
|
|
path.classList.add('nipple-right');
|
|
nippleIndex++;
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
startBounce() {
|
|
if (!this.logoElement || this.isAnimating) return;
|
|
|
|
this.logoElement.classList.add('goondex-logo-animated');
|
|
this.isAnimating = true;
|
|
}
|
|
|
|
stopBounce() {
|
|
if (!this.logoElement) return;
|
|
|
|
this.logoElement.classList.remove('goondex-logo-animated');
|
|
this.isAnimating = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
identifyParts() {
|
|
if (!this.logoElement) return;
|
|
const nipples = [];
|
|
const breasts = [];
|
|
|
|
const breastCandidates = [
|
|
this.logoElement.querySelector('#breast-left'),
|
|
this.logoElement.querySelector('#breast-right')
|
|
].filter(Boolean);
|
|
const nippleCandidates = [
|
|
this.logoElement.querySelector('#nipple-left'),
|
|
this.logoElement.querySelector('#nipple-right')
|
|
].filter(Boolean);
|
|
|
|
breasts.push(...breastCandidates);
|
|
nipples.push(...nippleCandidates);
|
|
|
|
if (nipples.length < 2) {
|
|
const circ = Array.from(this.logoElement.querySelectorAll('circle, ellipse'));
|
|
while (nipples.length < 2 && circ.length) nipples.push(circ.shift());
|
|
}
|
|
if (breasts.length < 2) {
|
|
const shapes = Array.from(this.logoElement.querySelectorAll('path, polygon, rect'));
|
|
while (breasts.length < 2 && shapes.length) breasts.push(shapes.shift());
|
|
}
|
|
if (breasts.length === 0) breasts.push(this.logoElement);
|
|
if (breasts.length === 1) breasts.push(this.logoElement);
|
|
|
|
if (breasts[0]) breasts[0].classList.add('breast-left');
|
|
if (breasts[1]) breasts[1].classList.add('breast-right');
|
|
|
|
if (nipples.length === 0) nipples.push(breasts[0], breasts[1]);
|
|
nipples.slice(0, 2).forEach((el, idx) => el && el.classList.add(idx === 0 ? 'nipple-left' : 'nipple-right'));
|
|
}
|
|
|
|
startBounce() {
|
|
if (!this.logoElement || this.isAnimating) return;
|
|
this.logoElement.classList.add('goondex-logo-animated');
|
|
this.isAnimating = true;
|
|
}
|
|
|
|
stopBounce() {
|
|
if (!this.logoElement) return;
|
|
this.logoElement.classList.remove('goondex-logo-animated');
|
|
this.isAnimating = false;
|
|
}
|
|
}
|
|
|
|
async function loadSVG(urls, targetId) {
|
|
const target = document.getElementById(targetId);
|
|
if (!target) return null;
|
|
for (const url of urls) {
|
|
try {
|
|
const res = await fetch(url);
|
|
if (!res.ok) throw new Error('fetch failed');
|
|
const svgText = await res.text();
|
|
target.innerHTML = svgText;
|
|
const svg = target.querySelector('svg');
|
|
return svg;
|
|
} catch (e) {
|
|
continue;
|
|
}
|
|
}
|
|
// Fallback to img if all fetches fail (no animation possible)
|
|
target.innerHTML = `<img src=\"${urls[0]}\" alt=\"Goondex Logo\" width=\"100%\" height=\"100%\">`;
|
|
return null;
|
|
}
|
|
|
|
const logoURLs = [
|
|
"/static/img/logo/GOONDEX_Titty.svg",
|
|
"static/img/logo/GOONDEX_Titty.svg",
|
|
"./static/img/logo/GOONDEX_Titty.svg"
|
|
|
|
|
|
let animator = null;
|
|
let loaderAnimator = null;
|
|
|
|
function initLogos() {
|
|
const animatedLogo = document.querySelector('#animated-logo img');
|
|
const loaderLogo = document.querySelector('#loader-logo img');
|
|
|
|
if (animatedLogo) {
|
|
animator = new LogoAnimator();
|
|
animator.init(animatedLogo);
|
|
console.log('Animator initialized');
|
|
}
|
|
|
|
if (loaderLogo) {
|
|
loaderAnimator = new LogoAnimator();
|
|
loaderAnimator.init(loaderLogo);
|
|
console.log('Loader animator initialized');
|
|
}
|
|
}
|
|
|
|
function startAnimation() {
|
|
if (animator) animator.startBounce();
|
|
}
|
|
|
|
function stopAnimation() {
|
|
if (animator) animator.stopBounce();
|
|
}
|
|
|
|
function testLoader() {
|
|
const loader = document.getElementById('global-loader');
|
|
loader.style.display = 'flex';
|
|
|
|
if (loaderAnimator) {
|
|
loaderAnimator.startBounce();
|
|
}
|
|
|
|
setTimeout(() => {
|
|
loader.style.display = 'none';
|
|
if (loaderAnimator) {
|
|
loaderAnimator.stopBounce();
|
|
}
|
|
}, 3000);
|
|
}
|
|
|
|
initLogos();
|
|
</script>
|
|
</body>
|
|
</html>
|