diff --git a/static/app.js b/static/app.js new file mode 100644 index 0000000..251cdf0 --- /dev/null +++ b/static/app.js @@ -0,0 +1,121 @@ +/** + * AniWorld Downloader Web Interface + * Main JavaScript file + */ + +// Format timestamps +function formatDateTime(dateString) { + const date = new Date(dateString); + return date.toLocaleString('de-DE', { + day: '2-digit', + month: '2-digit', + year: 'numeric', + hour: '2-digit', + minute: '2-digit' + }); +} + +// Helper to capitalize first letter +function capitalizeFirst(string) { + return string.charAt(0).toUpperCase() + string.slice(1); +} + +// Helper to truncate text with ellipsis +function truncateText(text, maxLength) { + return text.length > maxLength ? text.substring(0, maxLength) + '...' : text; +} + +// Parse URL parameters +function getUrlParams() { + const params = {}; + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + + for (const [key, value] of urlParams) { + params[key] = value; + } + + return params; +} + +// Show notification +function showNotification(message, type = 'info') { + // Check if notification container exists + let container = document.getElementById('notification-container'); + + if (!container) { + container = document.createElement('div'); + container.id = 'notification-container'; + container.style.position = 'fixed'; + container.style.top = '20px'; + container.style.right = '20px'; + container.style.zIndex = '9999'; + document.body.appendChild(container); + } + + // Create notification + const notification = document.createElement('div'); + notification.className = `alert alert-${type} alert-dismissible fade show`; + notification.innerHTML = ` + ${message} + + `; + + container.appendChild(notification); + + // Auto-close after 5 seconds + setTimeout(() => { + notification.classList.remove('show'); + setTimeout(() => { + notification.remove(); + }, 150); + }, 5000); +} + +// Extract title from URL +function extractTitleFromUrl(url) { + try { + if (url.includes('bs.to')) { + const match = url.match(/\/serie\/([^\/]+)/); + return match ? match[1].replace(/-/g, ' ') : 'Unknown Title'; + } else { + const match = url.match(/\/stream\/([^\/]+)/); + return match ? match[1].replace(/-/g, ' ') : 'Unknown Title'; + } + } catch (e) { + return 'Unknown Title'; + } +} + +// Check if URL is valid +function isValidUrl(url) { + const pattern = /^(https?:\/\/)?(www\.)?(aniworld\.to|s\.to|bs\.to)\/.*$/; + return pattern.test(url); +} + +// Initialize page-specific scripts when DOM is loaded +document.addEventListener('DOMContentLoaded', function() { + // Check for link parameter and populate the input + const urlParams = getUrlParams(); + if (urlParams.link && document.getElementById('animeLink')) { + document.getElementById('animeLink').value = urlParams.link; + + // Auto-analyze if requested + if (urlParams.analyze === 'true') { + document.getElementById('analyzeButton').click(); + } + } + + // Apply theme preference + const prefersDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches; + if (prefersDarkMode) { + document.body.classList.add('dark-mode'); + } +}); + +// Handle AJAX errors globally +window.addEventListener('error', function(e) { + if (e.message.includes('NetworkError') || e.message.includes('Failed to fetch')) { + showNotification('Netzwerkfehler. Bitte überprüfe deine Verbindung.', 'danger'); + } +}); \ No newline at end of file diff --git a/static/logs-bootstrap.css b/static/logs-bootstrap.css new file mode 100644 index 0000000..6b5a1d1 --- /dev/null +++ b/static/logs-bootstrap.css @@ -0,0 +1,139 @@ +/* Bootstrap-kompatible Log Styles */ +.logs-container { + height: 600px; + background: #f8f9fa; + border: 1px solid #dee2e6; + border-radius: 0.375rem; + overflow: hidden; +} + +.logs-content { + height: 100%; + overflow-y: auto; + padding: 1rem; + font-family: 'Courier New', monospace; + font-size: 0.875rem; + line-height: 1.4; + background: #212529; + color: #fff; +} + +.log-entry { + margin-bottom: 0.5rem; + padding: 0.5rem 0.75rem; + border-radius: 0.375rem; + border-left: 4px solid transparent; + transition: all 0.2s ease; + word-wrap: break-word; +} + +.log-entry:hover { + background: rgba(255, 255, 255, 0.1); +} + +.log-entry.INFO { + border-left-color: #198754; + background: rgba(25, 135, 84, 0.1); +} + +.log-entry.WARNING { + border-left-color: #fd7e14; + background: rgba(253, 126, 20, 0.1); +} + +.log-entry.ERROR { + border-left-color: #dc3545; + background: rgba(220, 53, 69, 0.1); +} + +.log-timestamp { + color: #6c757d; + font-size: 0.75rem; + margin-right: 0.75rem; +} + +.log-level { + font-weight: bold; + margin-right: 0.75rem; + min-width: 60px; + display: inline-block; + font-size: 0.75rem; +} + +.log-level.INFO { + color: #198754; +} + +.log-level.WARNING { + color: #fd7e14; +} + +.log-level.ERROR { + color: #dc3545; +} + +.log-message { + color: #fff; +} + +/* Button States */ +.btn.active { + background-color: var(--bs-primary); + border-color: var(--bs-primary); + color: white; +} + +/* Status Indicator */ +#log-status-indicator { + transition: all 0.3s ease; +} + +#log-status-indicator:hover { + transform: scale(1.05); +} + +.badge.bg-success { + animation: pulse 2s infinite; +} + +@keyframes pulse { + 0% { opacity: 1; } + 50% { opacity: 0.7; } + 100% { opacity: 1; } +} + +/* Dark theme for logs */ +@media (prefers-color-scheme: dark) { + .logs-container { + background: #343a40; + border-color: #495057; + } + + .card { + background: #343a40; + border-color: #495057; + } + + .card-header { + background: #495057; + border-color: #6c757d; + color: #fff; + } +} + +/* Responsive */ +@media (max-width: 768px) { + .logs-container { + height: 400px; + } + + .logs-content { + font-size: 0.75rem; + padding: 0.5rem; + } + + .log-entry { + padding: 0.25rem 0.5rem; + margin-bottom: 0.25rem; + } +} \ No newline at end of file diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..d65c34e --- /dev/null +++ b/static/style.css @@ -0,0 +1,249 @@ +:root { + --bg: #121C22; + --bg-2nd: #637CF9; + --bg-card: #1e2832; + --fg: #FFFFFF; + --fg-muted: #a0aec0; + --border-color: #2d3748; + --hover-bg: #1a242f; +} + +body { + background-color: var(--bg); + color: var(--fg); + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; +} + +/* Navbar styling */ +.navbar-dark { + background-color: var(--bg-2nd) !important; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2); +} + +.navbar-brand { + font-weight: 600; +} + +.nav-link { + margin: 0 5px; +} + +/* Card styling */ +.card { + background-color: var(--bg-card); + border-color: var(--border-color); + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + margin-bottom: 20px; +} + +.card-header { + background-color: var(--bg-2nd); + border-bottom-color: var(--bg-2nd); + color: var(--fg); + font-weight: 600; +} + +/* Form controls */ +.form-control, +.form-select { + background-color: #2d3748; + border-color: var(--border-color); + color: var(--fg); +} + +.form-control:focus, +.form-select:focus { + background-color: #2d3748; + border-color: var(--bg-2nd); + color: var(--fg); + box-shadow: 0 0 0 0.2rem rgba(99, 124, 249, 0.25); +} + +.form-control::placeholder { + color: var(--fg-muted); +} + +.form-check-input { + background-color: #2d3748; + border-color: var(--bg-2nd); +} + +.form-check-input:checked { + background-color: var(--bg-2nd); + border-color: var(--bg-2nd); +} + +/* Buttons */ +.btn-primary { + background-color: var(--bg-2nd); + border-color: var(--bg-2nd); +} + +.btn-primary:hover, +.btn-primary:focus { + background-color: #5a6fd8; + border-color: #5a6fd8; +} + +.btn-outline-secondary { + color: var(--fg); + border-color: var(--fg-muted); +} + +.btn-outline-secondary:hover { + background-color: #2d3748; + color: var(--fg); +} + +/* Tables */ +.table { + color: var(--fg); +} + +.table thead th { + border-bottom-color: var(--border-color); + color: var(--fg-muted); +} + +.table tbody tr:hover { + background-color: var(--hover-bg); +} + +.table td, .table th { + border-color: var(--border-color); +} + +/* Alerts */ +.alert-warning, +.alert-info, +.alert-danger, +.alert-success { + background-color: #2d3748; + border-color: var(--bg-2nd); + color: var(--fg); +} + +.alert-info { + border-left: 4px solid var(--bg-2nd); +} + +.alert-danger { + border-left: 4px solid #e53e3e; +} + +.alert-success { + border-left: 4px solid #38a169; +} + +.alert-warning { + border-left: 4px solid #d69e2e; +} + +/* Text colors */ +.text-muted { + color: var(--fg-muted) !important; +} + +/* Progress bars */ +.progress { + background-color: #2d3748; +} + +.progress-bar { + background-color: var(--bg-2nd); +} + +/* List groups */ +.list-group-item { + background-color: var(--bg-card); + border-color: var(--border-color); + color: var(--fg); +} + +.list-group-item:hover { + background-color: var(--hover-bg); +} + +.list-group-item-action:focus, +.list-group-item-action:hover { + background-color: var(--hover-bg); + color: var(--fg); +} + +/* Badges */ +.badge { + font-weight: 500; +} + +/* Download items */ +.download-item { + background-color: var(--hover-bg); + border-radius: 4px; +} + +.download-item:hover { + background-color: #243447; +} + +/* Provider list */ +.provider-list { + max-height: 300px; + overflow-y: auto; +} + +.provider-item { + cursor: move; + background-color: var(--hover-bg); +} + +.provider-item:hover { + background-color: #243447; +} + +/* Modals */ +.modal-content { + background-color: var(--bg-card); + color: var(--fg); +} + +.modal-header, +.modal-footer { + border-color: var(--border-color); +} + +/* Spinner */ +.spinner-border { + color: var(--bg-2nd); +} + +/* Footer */ +footer { + color: var(--fg-muted); +} + +/* Media queries */ +@media (max-width: 768px) { + .card-body { + padding: 1rem; + } + + .container { + padding-left: 12px; + padding-right: 12px; + } +} + +/* Animations */ +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +.fade-in { + animation: fadeIn 0.3s ease-in; +} + +/* Dark mode toggle */ +.dark-mode-toggle { + cursor: pointer; +} \ No newline at end of file