Goondex/test-logo-standalone.html
Stu Leak 3b8adad57d 🚀 Goondex v0.1.0-dev3 - Comprehensive ML-Powered Search & Import System
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.
2025-12-30 21:52:25 -05:00

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>