remove gitkeep from uploads; try to fix issues
This commit is contained in:
@@ -6,6 +6,7 @@ h11==0.14.0
|
||||
httptools==0.6.4
|
||||
idna==3.10
|
||||
Jinja2==3.1.6
|
||||
loguru==0.7.3
|
||||
MarkupSafe==3.0.2
|
||||
pillow==11.1.0
|
||||
pydantic==2.10.6
|
||||
@@ -22,3 +23,4 @@ uvloop==0.21.0
|
||||
watchfiles==1.0.4
|
||||
websockets==15.0.1
|
||||
wheel==0.45.1
|
||||
yt-dlp==2025.2.19
|
||||
|
||||
11
src/main.py
11
src/main.py
@@ -1,5 +1,7 @@
|
||||
import asyncio
|
||||
import shutil
|
||||
import json
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from fastapi import FastAPI, UploadFile, File, HTTPException, Request, WebSocket, WebSocketDisconnect
|
||||
from fastapi.responses import HTMLResponse
|
||||
@@ -7,9 +9,16 @@ from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
from config import UPLOAD_DIR, ALLOWED_EXTENSIONS, STATIC_DIR, TEMPLATES_DIR
|
||||
from worker import audio_processor, FileStatus
|
||||
|
||||
|
||||
app = FastAPI(title="Transcriptor")
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
audio_processor.start() # Startup code
|
||||
yield
|
||||
audio_processor.stop() # Shutdown code
|
||||
|
||||
app = FastAPI(title="Transcriptor", lifespan=lifespan)
|
||||
|
||||
active_connections = []
|
||||
app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
|
||||
|
||||
@@ -82,15 +82,6 @@
|
||||
color: #777;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
#refreshButton {
|
||||
background-color: #5bc0de;
|
||||
margin-left: 10px;
|
||||
padding: 5px 10px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
#refreshButton:hover {
|
||||
background-color: #46b8da;
|
||||
}
|
||||
.connection-status {
|
||||
font-size: 0.8em;
|
||||
font-weight: normal;
|
||||
@@ -115,7 +106,7 @@
|
||||
<body>
|
||||
<h1>Audio File Upload</h1>
|
||||
<p>Upload your MP3 or WAV audio files</p>
|
||||
|
||||
|
||||
<div class="upload-form">
|
||||
<form id="uploadForm">
|
||||
<div class="form-group">
|
||||
@@ -125,12 +116,12 @@
|
||||
<button type="submit">Upload</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="result" style="display: none;"></div>
|
||||
|
||||
|
||||
<!-- File List Dashboard -->
|
||||
<div class="file-dashboard">
|
||||
<h2>Uploaded Files <span id="connectionStatus" class="connection-status">Connecting...</span></h2>
|
||||
<h2>Uploaded Files <span id="connectionStatus" class="connection-status connecting">Connecting...</span></h2>
|
||||
<div id="fileList" class="file-list">
|
||||
<p>Loading files...</p>
|
||||
</div>
|
||||
@@ -140,43 +131,43 @@
|
||||
// File upload handling
|
||||
document.getElementById('uploadForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
|
||||
const fileInput = document.getElementById('audioFile');
|
||||
const file = fileInput.files[0];
|
||||
|
||||
|
||||
if (!file) {
|
||||
showResult('Please select a file.', false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Check file extension
|
||||
const fileName = file.name;
|
||||
const fileExt = fileName.split('.').pop().toLowerCase();
|
||||
|
||||
|
||||
if (!['mp3', 'wav'].includes(fileExt)) {
|
||||
showResult('Only MP3 and WAV files are allowed.', false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Create form data
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
|
||||
try {
|
||||
const response = await fetch('/upload', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
|
||||
if (response.ok) {
|
||||
if (result.status === "duplicate") {
|
||||
showResult(`File "${result.filename}" already exists in uploads directory!`, false);
|
||||
} else {
|
||||
showResult(`File "${result.filename}" uploaded successfully!`, true);
|
||||
fileInput.value = ''; // Clear the input
|
||||
|
||||
|
||||
// No need to manually refresh - WebSocket will handle it
|
||||
}
|
||||
} else {
|
||||
@@ -186,35 +177,35 @@
|
||||
showResult(`Upload failed: ${error.message}`, false);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function showResult(message, isSuccess) {
|
||||
const resultDiv = document.getElementById('result');
|
||||
resultDiv.textContent = message;
|
||||
resultDiv.className = isSuccess ? 'success' : 'error';
|
||||
resultDiv.style.display = 'block';
|
||||
|
||||
|
||||
// Hide the message after 5 seconds
|
||||
setTimeout(() => {
|
||||
resultDiv.style.display = 'none';
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
|
||||
// File list handling
|
||||
let socket = null;
|
||||
|
||||
|
||||
function connectWebSocket() {
|
||||
// Close any existing connection
|
||||
if (socket) {
|
||||
socket.close();
|
||||
}
|
||||
|
||||
|
||||
// Create WebSocket connection
|
||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsUrl = `${protocol}//${window.location.host}/ws`;
|
||||
socket = new WebSocket(wsUrl);
|
||||
|
||||
|
||||
const statusEl = document.getElementById('connectionStatus');
|
||||
|
||||
|
||||
// Connection opened
|
||||
socket.addEventListener('open', (event) => {
|
||||
statusEl.textContent = 'Connected';
|
||||
@@ -222,7 +213,7 @@
|
||||
// Request initial file list
|
||||
socket.send('getFiles');
|
||||
});
|
||||
|
||||
|
||||
// Listen for messages
|
||||
socket.addEventListener('message', (event) => {
|
||||
try {
|
||||
@@ -232,7 +223,7 @@
|
||||
console.error('Error parsing WebSocket message:', error);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Connection closed
|
||||
socket.addEventListener('close', (event) => {
|
||||
statusEl.textContent = 'Disconnected - Reconnecting...';
|
||||
@@ -240,7 +231,7 @@
|
||||
// Try to reconnect after a delay
|
||||
setTimeout(connectWebSocket, 3000);
|
||||
});
|
||||
|
||||
|
||||
// Connection error
|
||||
socket.addEventListener('error', (event) => {
|
||||
console.error('WebSocket error:', event);
|
||||
@@ -248,15 +239,15 @@
|
||||
statusEl.className = 'connection-status disconnected';
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function updateFileList(files) {
|
||||
const fileListElement = document.getElementById('fileList');
|
||||
|
||||
|
||||
if (!files || files.length === 0) {
|
||||
fileListElement.innerHTML = '<p>No files uploaded yet.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let html = '';
|
||||
files.forEach(file => {
|
||||
// Format the file size
|
||||
@@ -266,20 +257,20 @@
|
||||
const fileSizeMB = (fileSizeKB / 1024).toFixed(1);
|
||||
fileSizeStr = fileSizeMB + ' MB';
|
||||
}
|
||||
|
||||
|
||||
// Format the date
|
||||
const date = new Date(file.created * 1000);
|
||||
const dateStr = date.toLocaleString();
|
||||
|
||||
|
||||
html += `<div class="file-item">
|
||||
<div class="file-name">${file.name}</div>
|
||||
<div class="file-meta">Size: ${fileSizeStr} | Uploaded: ${dateStr}</div>
|
||||
</div>`;
|
||||
});
|
||||
|
||||
|
||||
fileListElement.innerHTML = html;
|
||||
}
|
||||
|
||||
|
||||
// Initialize WebSocket connection when page loads
|
||||
document.addEventListener('DOMContentLoaded', connectWebSocket);
|
||||
</script>
|
||||
|
||||
85
src/worker.py
Normal file
85
src/worker.py
Normal file
@@ -0,0 +1,85 @@
|
||||
# src/worker.py
|
||||
import asyncio
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Set, Optional
|
||||
from enum import Enum
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from config import UPLOAD_DIR
|
||||
|
||||
|
||||
class FileStatus(Enum):
|
||||
PENDING = "pending"
|
||||
PROCESSING = "processing"
|
||||
COMPLETED = "completed"
|
||||
FAILED = "failed"
|
||||
|
||||
|
||||
class AudioProcessor:
|
||||
def __init__(self):
|
||||
self.file_status: Dict[str, FileStatus] = {}
|
||||
self.is_running = False
|
||||
self._task: Optional[asyncio.Task] = None
|
||||
|
||||
def start(self):
|
||||
"""Start the background worker"""
|
||||
if not self._task or self._task.done():
|
||||
self.is_running = True
|
||||
self._task = asyncio.create_task(self._monitor_files())
|
||||
|
||||
def stop(self):
|
||||
"""Stop the background worker"""
|
||||
self.is_running = False
|
||||
if self._task and not self._task.done():
|
||||
self._task.cancel()
|
||||
|
||||
async def _monitor_files(self):
|
||||
"""Monitor the uploads folder for new files"""
|
||||
while self.is_running:
|
||||
# Check for new files
|
||||
for file_path in UPLOAD_DIR.iterdir():
|
||||
if file_path.is_file() and file_path.suffix.lower() in ['.mp3', '.wav']:
|
||||
filename = file_path.name
|
||||
if filename not in self.file_status:
|
||||
# New file found, mark as pending
|
||||
self.file_status[filename] = FileStatus.PENDING
|
||||
|
||||
# Sleep before next check
|
||||
await asyncio.sleep(2)
|
||||
|
||||
async def process_file(self, filename: str) -> bool:
|
||||
"""Process a specific audio file"""
|
||||
file_path = UPLOAD_DIR / filename
|
||||
|
||||
# Check if file exists and is valid
|
||||
if not file_path.exists():
|
||||
return False
|
||||
|
||||
# Update status
|
||||
self.file_status[filename] = FileStatus.PROCESSING
|
||||
|
||||
try:
|
||||
# TODO: Implement actual processing logic here
|
||||
await asyncio.sleep(3) # For now, just simulate processing with a delay
|
||||
|
||||
# Mark as completed
|
||||
self.file_status[filename] = FileStatus.COMPLETED
|
||||
return True
|
||||
except Exception as e:
|
||||
self.file_status[filename] = FileStatus.FAILED
|
||||
logger.error(f"Error processing {filename}: {e}")
|
||||
return False
|
||||
|
||||
def get_status(self, filename: str) -> FileStatus:
|
||||
"""Get the status of a file"""
|
||||
return self.file_status.get(filename, FileStatus.PENDING)
|
||||
|
||||
def get_all_statuses(self) -> Dict[str, str]:
|
||||
"""Get status of all files"""
|
||||
return {name: status.value for name, status in self.file_status.items()}
|
||||
|
||||
|
||||
# Create a global instance of the processor
|
||||
audio_processor = AudioProcessor()
|
||||
Reference in New Issue
Block a user