dsa / index.html
tgatech's picture
undefined - Initial Deployment
36a4764 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flask Video İşleme API</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&family=Rajdhani:wght@300;500;700&display=swap');
:root {
--space-purple: #5d3fd3;
--deep-space: #1a103d;
--neon-purple: #9c27b0;
--cosmic-pink: #e91e63;
--starry-night: #0f0524;
}
body {
font-family: 'Rajdhani', sans-serif;
background: linear-gradient(135deg, var(--deep-space), var(--starry-night));
color: white;
min-height: 100vh;
background-image:
radial-gradient(circle at 10% 20%, rgba(156, 39, 176, 0.15) 0%, transparent 20%),
radial-gradient(circle at 90% 30%, rgba(93, 63, 211, 0.15) 0%, transparent 25%),
radial-gradient(circle at 50% 80%, rgba(233, 30, 99, 0.15) 0%, transparent 30%);
}
h1, h2, h3 {
font-family: 'Orbitron', sans-serif;
text-transform: uppercase;
}
.container {
max-width: 1200px;
}
.section {
background: rgba(26, 16, 61, 0.7);
backdrop-filter: blur(10px);
border: 1px solid rgba(156, 39, 176, 0.3);
border-radius: 16px;
box-shadow: 0 0 20px rgba(156, 39, 176, 0.3);
transition: all 0.3s ease;
}
.section:hover {
box-shadow: 0 0 30px rgba(156, 39, 176, 0.5);
transform: translateY(-2px);
}
input, button, select, textarea {
background: rgba(26, 16, 61, 0.5);
border: 1px solid var(--space-purple);
color: white;
transition: all 0.3s ease;
}
input:focus, button:focus, select:focus, textarea:focus {
outline: none;
border-color: var(--neon-purple);
box-shadow: 0 0 10px rgba(156, 39, 176, 0.5);
}
button {
background: linear-gradient(45deg, var(--space-purple), var(--neon-purple));
text-transform: uppercase;
letter-spacing: 1px;
font-weight: bold;
}
button:hover {
background: linear-gradient(45deg, var(--neon-purple), var(--cosmic-pink));
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(156, 39, 176, 0.4);
}
.status-processing {
color: #ff9800;
}
.status-completed {
color: #4caf50;
}
.status-failed {
color: #f44336;
}
.status-queued {
color: #2196f3;
}
.download-link {
color: var(--neon-purple);
text-decoration: none;
font-weight: bold;
}
.download-link:hover {
color: var(--cosmic-pink);
text-decoration: underline;
}
table {
border-collapse: separate;
border-spacing: 0;
width: 100%;
}
th {
background: rgba(93, 63, 211, 0.2);
position: sticky;
top: 0;
backdrop-filter: blur(5px);
}
td, th {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid rgba(156, 39, 176, 0.2);
}
tr:hover {
background: rgba(93, 63, 211, 0.1);
}
.glow-text {
text-shadow: 0 0 10px rgba(156, 39, 176, 0.7);
}
.file-input-wrapper {
position: relative;
overflow: hidden;
display: inline-block;
width: 100%;
}
.file-input-wrapper input[type=file] {
position: absolute;
left: 0;
top: 0;
opacity: 0;
width: 100%;
height: 100%;
cursor: pointer;
}
.file-input-label {
display: flex;
align-items: center;
justify-content: center;
padding: 12px;
border: 2px dashed var(--space-purple);
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
}
.file-input-label:hover {
border-color: var(--neon-purple);
background: rgba(93, 63, 211, 0.1);
}
.file-input-label i {
margin-right: 10px;
color: var(--neon-purple);
}
.pulse {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(156, 39, 176, 0.4);
}
70% {
box-shadow: 0 0 0 10px rgba(156, 39, 176, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(156, 39, 176, 0);
}
}
.cosmic-divider {
height: 2px;
background: linear-gradient(90deg, transparent, var(--space-purple), var(--neon-purple), transparent);
margin: 20px 0;
border: none;
}
@media (max-width: 768px) {
.section {
padding: 15px;
}
table {
display: block;
overflow-x: auto;
}
}
</style>
</head>
<body class="p-4 md:p-8">
<div class="container mx-auto">
<div class="text-center mb-10">
<h1 class="text-4xl md:text-5xl font-bold mb-4 glow-text text-purple-400">
<i class="fas fa-rocket mr-3"></i>Flask Video İşleme API
</h1>
<p class="text-lg text-purple-200">Uzay temalı video işleme platformu</p>
</div>
<!-- Video Yükle ve İşle Section -->
<div class="section p-6 mb-8">
<h2 class="text-2xl font-bold mb-6 text-purple-300 flex items-center">
<i class="fas fa-upload mr-3"></i>Video Yükle ve İşle
</h2>
<form id="uploadForm" enctype="multipart/form-data" class="space-y-6">
<div class="file-input-wrapper">
<label for="videoFile" class="file-input-label">
<i class="fas fa-video text-xl"></i>
<span id="fileName">Video Dosyası Seçin</span>
</label>
<input type="file" id="videoFile" name="video" accept="video/*" required>
</div>
<button type="submit" class="w-full py-3 px-6 rounded-lg font-bold pulse">
<i class="fas fa-cogs mr-2"></i>Yükle ve İşle
</button>
</form>
<div id="uploadStatus" class="mt-4 p-3 rounded-lg"></div>
</div>
<hr class="cosmic-divider my-10">
<!-- İşlem Durumu Sorgula Section -->
<div class="section p-6 mb-8">
<h2 class="text-2xl font-bold mb-6 text-purple-300 flex items-center">
<i class="fas fa-search mr-3"></i>İşlem Durumu Sorgula
</h2>
<div class="flex flex-col md:flex-row gap-4">
<div class="flex-grow relative">
<i class="fas fa-id-card absolute left-3 top-3 text-purple-400"></i>
<input type="text" id="taskIdInput" placeholder="İşlem ID'sini buraya yapıştır"
class="w-full pl-10 pr-4 py-2 rounded-lg">
</div>
<button onclick="checkStatus()" class="md:w-auto py-2 px-6 rounded-lg font-bold">
<i class="fas fa-sync-alt mr-2"></i>Durumu Sorgula
</button>
</div>
<div id="taskStatus" class="mt-4 p-4 rounded-lg bg-gray-800 bg-opacity-50"></div>
</div>
<hr class="cosmic-divider my-10">
<!-- Tüm İşlem Durumları Section -->
<div class="section p-6">
<h2 class="text-2xl font-bold mb-6 text-purple-300 flex items-center">
<i class="fas fa-list-alt mr-3"></i>Tüm İşlem Durumları <span class="text-sm ml-2 text-purple-200">(Otomatik Yenilenir)</span>
</h2>
<div class="overflow-x-auto rounded-lg">
<table id="allStatusesTable" class="w-full">
<thead>
<tr class="text-purple-300">
<th class="py-3"><i class="fas fa-fingerprint mr-2"></i>ID</th>
<th class="py-3"><i class="fas fa-file-video mr-2"></i>Orijinal Dosya</th>
<th class="py-3"><i class="fas fa-clock mr-2"></i>Yükleme Zamanı</th>
<th class="py-3"><i class="fas fa-info-circle mr-2"></i>Durum</th>
<th class="py-3"><i class="fas fa-stopwatch mr-2"></i>İşlem Süresi (s)</th>
<th class="py-3"><i class="fas fa-exclamation-triangle mr-2"></i>Hata</th>
<th class="py-3"><i class="fas fa-download mr-2"></i>İndir</th>
</tr>
</thead>
<tbody>
<tr><td colspan="7" class="text-center py-4 text-red-400"><i class="fas fa-exclamation-circle mr-2"></i>Tüm işlem durumları yüklenemedi. Sunucu hatası olabilir.</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<script>
const uploadForm = document.getElementById('uploadForm');
const videoFile = document.getElementById('videoFile');
const uploadStatusDiv = document.getElementById('uploadStatus');
const taskIdInput = document.getElementById('taskIdInput');
const taskStatusDiv = document.getElementById('taskStatus');
const allStatusesTableBody = document.querySelector('#allStatusesTable tbody');
const fileNameSpan = document.getElementById('fileName');
// Update filename display when file is selected
videoFile.addEventListener('change', function() {
if (this.files.length) {
fileNameSpan.textContent = this.files[0].name;
} else {
fileNameSpan.textContent = 'Video Dosyası Seçin';
}
});
// Video Yükleme Formu
uploadForm.addEventListener('submit', async (e) => {
e.preventDefault();
if (!videoFile.files.length) {
uploadStatusDiv.innerHTML = '<i class="fas fa-exclamation-circle mr-2"></i>Lütfen bir video dosyası seçin.';
uploadStatusDiv.className = 'mt-4 p-3 rounded-lg bg-red-900 bg-opacity-30 text-red-300';
return;
}
const formData = new FormData();
formData.append('video', videoFile.files[0]);
uploadStatusDiv.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Video yükleniyor ve işleniyor... Lütfen bekleyin.';
uploadStatusDiv.className = 'mt-4 p-3 rounded-lg bg-blue-900 bg-opacity-30 text-blue-300';
try {
const response = await fetch('/upload-and-process', {
method: 'POST',
body: formData
});
const result = await response.json();
if (response.ok) {
uploadStatusDiv.innerHTML = `<i class="fas fa-check-circle mr-2"></i>Başarılı! İşlem ID: <strong>${result.task_id}</strong>. Durum: <span class="status-${result.status}">${result.status}</span>`;
uploadStatusDiv.className = 'mt-4 p-3 rounded-lg bg-green-900 bg-opacity-30 text-green-300';
taskIdInput.value = result.task_id; // Otomatik olarak ID'yi doldur
fetchAllStatuses(); // Tabloyu güncelle
} else {
uploadStatusDiv.innerHTML = `<i class="fas fa-times-circle mr-2"></i>Hata: ${result.error || 'Bilinmeyen hata'}`;
uploadStatusDiv.className = 'mt-4 p-3 rounded-lg bg-red-900 bg-opacity-30 text-red-300';
}
} catch (error) {
uploadStatusDiv.innerHTML = `<i class="fas fa-times-circle mr-2"></i>Ağ hatası: ${error.message}`;
uploadStatusDiv.className = 'mt-4 p-3 rounded-lg bg-red-900 bg-opacity-30 text-red-300';
console.error('Upload Error:', error);
}
});
// Tekil İşlem Durumu Sorgulama
async function checkStatus() {
const taskId = taskIdInput.value.trim();
if (!taskId) {
taskStatusDiv.innerHTML = '<i class="fas fa-exclamation-circle mr-2"></i>Lütfen bir İşlem ID girin.';
taskStatusDiv.className = 'mt-4 p-4 rounded-lg bg-red-900 bg-opacity-30 text-red-300';
return;
}
taskStatusDiv.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>Durum sorgulanıyor...';
taskStatusDiv.className = 'mt-4 p-4 rounded-lg bg-blue-900 bg-opacity-30 text-blue-300';
try {
const response = await fetch(`/status/${taskId}`);
const status = await response.json();
if (response.ok) {
taskStatusDiv.innerHTML = `
<div class="space-y-2">
<div><i class="fas fa-info-circle mr-2 text-purple-400"></i><strong>Durum:</strong> <span class="status-${status.status}">${status.status}</span></div>
<div><i class="fas fa-file-video mr-2 text-purple-400"></i><strong>Orijinal Dosya:</strong> ${status.original_filename}</div>
<div><i class="fas fa-calendar-alt mr-2 text-purple-400"></i><strong>Yükleme Zamanı:</strong> ${status.uploaded_at_readable || 'N/A'}</div>
${status.start_time_readable ? `<div><i class="fas fa-play mr-2 text-purple-400"></i><strong>Başlama Zamanı:</strong> ${status.start_time_readable}</div>` : ''}
${status.end_time_readable ? `<div><i class="fas fa-stop mr-2 text-purple-400"></i><strong>Bitiş Zamanı:</strong> ${status.end_time_readable}</div>` : ''}
${status.duration_seconds !== null ? `<div><i class="fas fa-hourglass-half mr-2 text-purple-400"></i><strong>İşlem Süresi:</strong> ${status.duration_seconds} saniye</div>` : ''}
${status.error ? `<div><i class="fas fa-exclamation-triangle mr-2 text-red-400"></i><strong>Hata:</strong> <span class="text-red-300">${status.error}</span></div>` : ''}
${status.status === 'completed' ? `<div class="mt-3"><a href="/download/${status.output_filename}" target="_blank" class="download-link py-2 px-4 rounded-lg inline-block"><i class="fas fa-download mr-2"></i>İndir (${status.output_filename})</a></div>` : ''}
</div>
`;
taskStatusDiv.className = 'mt-4 p-4 rounded-lg bg-gray-800 bg-opacity-50';
} else {
taskStatusDiv.innerHTML = `<i class="fas fa-times-circle mr-2"></i>Hata: ${status.error || 'İşlem ID bulunamadı.'}`;
taskStatusDiv.className = 'mt-4 p-4 rounded-lg bg-red-900 bg-opacity-30 text-red-300';
}
} catch (error) {
taskStatusDiv.innerHTML = `<i class="fas fa-times-circle mr-2"></i>Ağ hatası: ${error.message}`;
taskStatusDiv.className = 'mt-4 p-4 rounded-lg bg-red-900 bg-opacity-30 text-red-300';
console.error('Status Check Error:', error);
}
}
// Tüm İşlem Durumlarını Çek ve Tabloyu Doldur
async function fetchAllStatuses() {
try {
const response = await fetch('/all-statuses');
const statuses = await response.json();
allStatusesTableBody.innerHTML = ''; // Tabloyu temizle
if (statuses.length === 0) {
const row = allStatusesTableBody.insertRow();
const cell = row.insertCell(0);
cell.colSpan = 7;
cell.className = 'text-center py-4 text-purple-300';
cell.innerHTML = '<i class="fas fa-info-circle mr-2"></i>Henüz işlenmiş video yok.';
return;
}
statuses.forEach(status => {
const row = allStatusesTableBody.insertRow();
row.className = 'hover:bg-purple-900 hover:bg-opacity-10';
// ID
const idCell = row.insertCell(0);
idCell.className = 'font-mono text-purple-300';
idCell.textContent = status.id.substring(0, 8) + '...';
// Original Filename
row.insertCell(1).textContent = status.original_filename;
// Upload Time
row.insertCell(2).textContent = status.uploaded_at_readable || 'N/A';
// Status
const statusCell = row.insertCell(3);
statusCell.textContent = status.status;
statusCell.className = `status-${status.status} font-bold`;
// Duration
const durationCell = row.insertCell(4);
durationCell.className = 'text-center';
durationCell.textContent = status.duration_seconds !== null ? status.duration_seconds : '-';
// Error
const errorCell = row.insertCell(5);
errorCell.textContent = status.error || '-';
if (status.error) {
errorCell.className = 'text-red-400';
}
// Download
const downloadCell = row.insertCell(6);
downloadCell.className = 'text-center';
if (status.status === 'completed' && status.output_filename) {
const downloadLink = document.createElement('a');
downloadLink.href = `/download/${status.output_filename}`;
downloadLink.className = 'download-link px-3 py-1 rounded-lg inline-block';
downloadLink.innerHTML = '<i class="fas fa-download mr-1"></i>İndir';
downloadLink.target = '_blank';
downloadCell.appendChild(downloadLink);
} else {
downloadCell.textContent = '-';
}
});
} catch (error) {
console.error('Tüm durumları getirirken hata oluştu:', error);
const row = allStatusesTableBody.insertRow();
const cell = row.insertCell(0);
cell.colSpan = 7;
cell.className = 'text-center py-4 text-red-400';
cell.innerHTML = '<i class="fas fa-exclamation-circle mr-2"></i>Tüm işlem durumları yüklenemedi. Sunucu hatası olabilir.';
}
}
// Sayfa yüklendiğinde ve belirli aralıklarla durumları güncelle
document.addEventListener('DOMContentLoaded', () => {
fetchAllStatuses();
setInterval(fetchAllStatuses, 5000); // Her 5 saniyede bir yenile
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=tgatech/dsa" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>