Initial commit - Mobile Wallpaper Service
This commit is contained in:
222
templates/index.html
Normal file
222
templates/index.html
Normal file
@@ -0,0 +1,222 @@
|
||||
<!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 & 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 & 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>
|
||||
Reference in New Issue
Block a user