Files
mobile-wallpaper-service/templates/index.html
2026-04-19 15:39:23 +02:00

223 lines
8.9 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mobile Wallpaper Processor</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
</head>
<body>
<div class="container">
<header>
<h1>📱 Mobile Wallpaper Processor</h1>
<p class="subtitle">Optimiere Bilder für 1440×3120 Displays (Pixel 6 Pro, Nothing Phone 3)</p>
</header>
<div class="upload-zone" id="dropZone">
<input type="file" id="fileInput" accept=".jpg,.jpeg,.png,.gif,.bmp,.webp,.tiff" hidden>
<div class="upload-content">
<svg class="upload-icon" viewbox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="17 8 12 3 7 8"></polyline>
<line x1="12" y1="3" x2="12" y2="15"></line>
</svg>
<p class="upload-text">Bild hier ziehen oder klicken zum Auswählen</p>
<p class="upload-hint">JPG, PNG, WEBP, GIF | max. 50MB</p>
</div>
</div>
<div class="options">
<label class="checkbox-label">
<input type="checkbox" id="removeText" checked>
<span class="checkmark"></span>
<span class="option-text">
<strong>Text erkennen &amp; entfernen</strong>
<small>Verwendet AI-Inpainting um Text im Bild zu entfernen</small>
</span>
</label>
</div>
<div id="preview" class="preview hidden">
<h3>Vorschau</h3>
<div class="preview-container">
<div class="preview-item">
<img id="originalPreview" alt="Original">
<span class="preview-label">Original</span>
</div>
<div class="preview-item">
<div class="phone-frame">
<div class="phone-screen">
<img id="croppedPreview" alt="Zugeschnitten">
</div>
<div class="phone-notch"></div>
</div>
<span class="preview-label">1440×3120 Crop</span>
</div>
</div>
</div>
<div id="progress" class="progress hidden">
<div class="progress-bar">
<div class="progress-fill"></div>
</div>
<p class="progress-text">Verarbeite Bild...</p>
<div class="processing-steps">
<span id="step1" class="step">Text erkennen</span>
<span id="step2" class="step">Text entfernen</span>
<span id="step3" class="step">Smart Crop</span>
<span id="step4" class="step">Optimieren</span>
</div>
</div>
<div id="result" class="result hidden">
<div class="result-phone">
<img id="resultImage" alt="Ergebnis">
</div>
<div class="result-actions">
<a id="downloadBtn" class="btn btn-primary" href="#">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="7 10 12 15 17 10"></polyline>
<line x1="12" y1="15" x2="12" y2="3"></line>
</svg>
Wallpaper herunterladen (1440×3120)
</a>
<button class="btn btn-secondary" onclick="resetApp()">Neues Bild</button>
</div>
<div class="result-info">
<p>Das Bild wurde auf <strong>1440×3120 Pixel</strong> optimiert.</p>
<p>Format: <strong>9.7:19.5 Hochkant</strong></p>
</div>
</div>
<div id="error" class="error hidden"></div>
<footer>
<p>Automatische Optimierung: Smart Crop, AI Text-Entfernung, Kontrast &amp; Schärfe</p>
</footer>
</div>
<script>
const dropZone = document.getElementById('dropZone');
const fileInput = document.getElementById('fileInput');
const originalPreview = document.getElementById('originalPreview');
const croppedPreview = document.getElementById('croppedPreview');
const preview = document.getElementById('preview');
const progress = document.getElementById('progress');
const result = document.getElementById('result');
const resultImage = document.getElementById('resultImage');
const downloadBtn = document.getElementById('downloadBtn');
const error = document.getElementById('error');
// Event Listeners
dropZone.addEventListener('click', () => fileInput.click());
dropZone.addEventListener('dragover', (e) => { e.preventDefault(); dropZone.classList.add('dragover'); });
dropZone.addEventListener('dragleave', () => dropZone.classList.remove('dragover'));
dropZone.addEventListener('drop', handleDrop);
fileInput.addEventListener('change', handleFileSelect);
function handleDrop(e) {
e.preventDefault();
dropZone.classList.remove('dragover');
const files = e.dataTransfer.files;
if (files.length) processFile(files[0]);
}
function handleFileSelect(e) {
const files = e.target.files;
if (files.length) processFile(files[0]);
}
function processFile(file) {
// Validierung
const validTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/bmp', 'image/tiff'];
if (!validTypes.includes(file.type)) {
showError('Bitte ein gültiges Bildformat hochladen (JPG, PNG, WEBP, GIF)');
return;
}
if (file.size > 50 * 1024 * 1024) {
showError('Datei zu groß (max. 50MB)');
return;
}
// Vorschau anzeigen
const reader = new FileReader();
reader.onload = (e) => {
originalPreview.src = e.target.result;
croppedPreview.src = e.target.result;
preview.classList.remove('hidden');
};
reader.readAsDataURL(file);
// Upload
uploadFile(file);
}
function uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
formData.append('remove_text', document.getElementById('removeText').checked);
// UI Update
dropZone.style.display = 'none';
preview.classList.add('hidden');
progress.classList.remove('hidden');
result.classList.add('hidden');
error.classList.add('hidden');
// Progress Animation
const steps = ['step1', 'step2', 'step3', 'step4'];
let currentStep = 0;
const stepInterval = setInterval(() => {
if (currentStep < steps.length) {
document.getElementById(steps[currentStep]).classList.add('active');
currentStep++;
}
}, 800);
fetch('/upload', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
clearInterval(stepInterval);
progress.classList.add('hidden');
if (data.success) {
resultImage.src = data.preview_url;
downloadBtn.href = data.download_url;
result.classList.remove('hidden');
} else {
showError(data.error || 'Unbekannter Fehler');
}
})
.catch(err => {
clearInterval(stepInterval);
progress.classList.add('hidden');
showError('Netzwerkfehler: ' + err.message);
});
}
function showError(msg) {
error.textContent = msg;
error.classList.remove('hidden');
dropZone.style.display = 'block';
}
function resetApp() {
dropZone.style.display = 'block';
preview.classList.add('hidden');
progress.classList.add('hidden');
result.classList.add('hidden');
error.classList.add('hidden');
fileInput.value = '';
// Reset steps
document.querySelectorAll('.step').forEach(s => s.classList.remove('active', 'done'));
}
</script>
</body>
</html>