Spaces:
Sleeping
Sleeping
TheM1N9 commited on
Commit ·
951cbfb
1
Parent(s): 02b7ad4
cookies removed
Browse files- app.py +22 -11
- templates/index.html +64 -62
app.py
CHANGED
|
@@ -1,8 +1,18 @@
|
|
| 1 |
import uvicorn
|
| 2 |
-
from fastapi import
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
from fastapi.responses import FileResponse, HTMLResponse, RedirectResponse
|
| 4 |
from fastapi.staticfiles import StaticFiles
|
| 5 |
from fastapi.templating import Jinja2Templates
|
|
|
|
| 6 |
import shutil
|
| 7 |
import os
|
| 8 |
from pathlib import Path
|
|
@@ -18,6 +28,7 @@ logger = logging.getLogger(__name__)
|
|
| 18 |
load_dotenv()
|
| 19 |
|
| 20 |
app = FastAPI(docs_url=None, redoc_url=None) # Disable Swagger UI # Disable ReDoc
|
|
|
|
| 21 |
|
| 22 |
# Mount static directory - make sure this comes before other routes
|
| 23 |
app.mount("/static", StaticFiles(directory="static"), name="static")
|
|
@@ -57,28 +68,24 @@ ADMIN_PASSWORD = os.getenv("ADMIN_PASSWORD", "password123")
|
|
| 57 |
|
| 58 |
|
| 59 |
@app.post("/login")
|
| 60 |
-
async def login(
|
| 61 |
-
username: str = Form(...), password: str = Form(...), response: Response = None
|
| 62 |
-
):
|
| 63 |
if username == ADMIN_USERNAME and password == ADMIN_PASSWORD:
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
return response
|
| 67 |
raise HTTPException(status_code=401, detail="Invalid credentials")
|
| 68 |
|
| 69 |
|
| 70 |
@app.get("/", response_class=HTMLResponse)
|
| 71 |
-
async def read_root(request: Request):
|
| 72 |
"""Serve the index page"""
|
| 73 |
-
# Check if user is authenticated
|
| 74 |
-
is_authenticated = request.cookies.get("authenticated") == "true"
|
| 75 |
return templates.TemplateResponse(
|
| 76 |
-
"index.html", {"request": request, "is_authenticated":
|
| 77 |
)
|
| 78 |
|
| 79 |
|
| 80 |
@app.post("/upload-pdf/")
|
| 81 |
async def upload_pdf(
|
|
|
|
| 82 |
file: UploadFile = File(...),
|
| 83 |
tharun_voice_id: str = Form(...),
|
| 84 |
akshara_voice_id: str = Form(...),
|
|
@@ -215,11 +222,15 @@ async def get_status():
|
|
| 215 |
@app.post("/regenerate-segment/{index}")
|
| 216 |
async def regenerate_segment(
|
| 217 |
index: int,
|
|
|
|
| 218 |
speaker: str = Form(...),
|
| 219 |
text: str = Form(...),
|
| 220 |
tharun_voice_id: str = Form(...),
|
| 221 |
akshara_voice_id: str = Form(...),
|
| 222 |
):
|
|
|
|
|
|
|
|
|
|
| 223 |
try:
|
| 224 |
# Select the appropriate voice ID based on speaker
|
| 225 |
voice_id = akshara_voice_id if speaker == "Akshara" else tharun_voice_id
|
|
|
|
| 1 |
import uvicorn
|
| 2 |
+
from fastapi import (
|
| 3 |
+
FastAPI,
|
| 4 |
+
UploadFile,
|
| 5 |
+
File,
|
| 6 |
+
HTTPException,
|
| 7 |
+
Form,
|
| 8 |
+
Request,
|
| 9 |
+
Response,
|
| 10 |
+
Depends,
|
| 11 |
+
)
|
| 12 |
from fastapi.responses import FileResponse, HTMLResponse, RedirectResponse
|
| 13 |
from fastapi.staticfiles import StaticFiles
|
| 14 |
from fastapi.templating import Jinja2Templates
|
| 15 |
+
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
| 16 |
import shutil
|
| 17 |
import os
|
| 18 |
from pathlib import Path
|
|
|
|
| 28 |
load_dotenv()
|
| 29 |
|
| 30 |
app = FastAPI(docs_url=None, redoc_url=None) # Disable Swagger UI # Disable ReDoc
|
| 31 |
+
security = HTTPBasic()
|
| 32 |
|
| 33 |
# Mount static directory - make sure this comes before other routes
|
| 34 |
app.mount("/static", StaticFiles(directory="static"), name="static")
|
|
|
|
| 68 |
|
| 69 |
|
| 70 |
@app.post("/login")
|
| 71 |
+
async def login(username: str = Form(...), password: str = Form(...)):
|
|
|
|
|
|
|
| 72 |
if username == ADMIN_USERNAME and password == ADMIN_PASSWORD:
|
| 73 |
+
# Instead of setting a cookie, redirect with query parameter
|
| 74 |
+
return RedirectResponse(url="/?authenticated=true", status_code=303)
|
|
|
|
| 75 |
raise HTTPException(status_code=401, detail="Invalid credentials")
|
| 76 |
|
| 77 |
|
| 78 |
@app.get("/", response_class=HTMLResponse)
|
| 79 |
+
async def read_root(request: Request, authenticated: bool = False):
|
| 80 |
"""Serve the index page"""
|
|
|
|
|
|
|
| 81 |
return templates.TemplateResponse(
|
| 82 |
+
"index.html", {"request": request, "is_authenticated": authenticated}
|
| 83 |
)
|
| 84 |
|
| 85 |
|
| 86 |
@app.post("/upload-pdf/")
|
| 87 |
async def upload_pdf(
|
| 88 |
+
authenticated: bool = False,
|
| 89 |
file: UploadFile = File(...),
|
| 90 |
tharun_voice_id: str = Form(...),
|
| 91 |
akshara_voice_id: str = Form(...),
|
|
|
|
| 222 |
@app.post("/regenerate-segment/{index}")
|
| 223 |
async def regenerate_segment(
|
| 224 |
index: int,
|
| 225 |
+
authenticated: bool = False,
|
| 226 |
speaker: str = Form(...),
|
| 227 |
text: str = Form(...),
|
| 228 |
tharun_voice_id: str = Form(...),
|
| 229 |
akshara_voice_id: str = Form(...),
|
| 230 |
):
|
| 231 |
+
if not authenticated:
|
| 232 |
+
raise HTTPException(status_code=401, detail="Authentication required")
|
| 233 |
+
|
| 234 |
try:
|
| 235 |
# Select the appropriate voice ID based on speaker
|
| 236 |
voice_id = akshara_voice_id if speaker == "Akshara" else tharun_voice_id
|
templates/index.html
CHANGED
|
@@ -134,6 +134,28 @@
|
|
| 134 |
document.getElementById("uploadForm").onsubmit = async (e) => {
|
| 135 |
e.preventDefault();
|
| 136 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
console.log("Form submission started");
|
| 138 |
|
| 139 |
const submitButton = e.target.querySelector("button[type='submit']");
|
|
@@ -175,54 +197,42 @@
|
|
| 175 |
if (input) input.disabled = true;
|
| 176 |
});
|
| 177 |
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
formData
|
| 182 |
-
|
| 183 |
-
document.getElementById("tharunVoiceId")?.value || ""
|
| 184 |
-
);
|
| 185 |
-
formData.append(
|
| 186 |
-
"akshara_voice_id",
|
| 187 |
-
document.getElementById("aksharaVoiceId")?.value || ""
|
| 188 |
-
);
|
| 189 |
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
|
| 196 |
-
|
|
|
|
| 197 |
|
| 198 |
-
|
| 199 |
-
|
|
|
|
|
|
|
|
|
|
| 200 |
}
|
| 201 |
|
| 202 |
-
const
|
| 203 |
-
|
|
|
|
| 204 |
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
audioPlayer.src = `/download/${data.podcast_file}`;
|
| 209 |
-
audioResult.style.display = "block";
|
| 210 |
}
|
| 211 |
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
if (segmentCount) {
|
| 218 |
-
segmentCount.textContent = `(${data.segments.length} segments)`;
|
| 219 |
-
}
|
| 220 |
-
|
| 221 |
-
data.segments.forEach((segment, index) => {
|
| 222 |
-
const segmentDiv = document.createElement("div");
|
| 223 |
-
segmentDiv.className = "segment";
|
| 224 |
-
segmentDiv.id = `segment-${index}`;
|
| 225 |
-
segmentDiv.innerHTML = `
|
| 226 |
<div class="segment-info">
|
| 227 |
<div class="segment-header">
|
| 228 |
<div class="segment-speaker">${segment.speaker}</div>
|
|
@@ -241,35 +251,26 @@
|
|
| 241 |
</button>
|
| 242 |
</div>
|
| 243 |
`;
|
| 244 |
-
|
| 245 |
-
|
| 246 |
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
}
|
| 250 |
}
|
| 251 |
}
|
| 252 |
-
} catch (error) {
|
| 253 |
-
console.error("Error:", error);
|
| 254 |
-
if (error) {
|
| 255 |
-
error.textContent =
|
| 256 |
-
error.message || "An error occurred during upload";
|
| 257 |
-
error.style.display = "block";
|
| 258 |
-
}
|
| 259 |
-
} finally {
|
| 260 |
-
// Reset button state
|
| 261 |
-
if (submitButton) {
|
| 262 |
-
submitButton.disabled = false;
|
| 263 |
-
submitButton.innerHTML = "Generate Podcast";
|
| 264 |
-
}
|
| 265 |
-
// Re-enable inputs
|
| 266 |
-
inputs.forEach((input) => {
|
| 267 |
-
if (input) input.disabled = false;
|
| 268 |
-
});
|
| 269 |
}
|
| 270 |
};
|
| 271 |
|
| 272 |
async function regenerateSegment(index, newText = null) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 273 |
const segment = document.querySelector(`#segment-${index}`);
|
| 274 |
const speaker = segment.querySelector(".segment-speaker").textContent;
|
| 275 |
const text =
|
|
@@ -288,6 +289,7 @@
|
|
| 288 |
|
| 289 |
try {
|
| 290 |
const formData = new FormData();
|
|
|
|
| 291 |
formData.append("speaker", speaker);
|
| 292 |
formData.append("text", text);
|
| 293 |
formData.append(
|
|
|
|
| 134 |
document.getElementById("uploadForm").onsubmit = async (e) => {
|
| 135 |
e.preventDefault();
|
| 136 |
|
| 137 |
+
// Get authentication status from URL
|
| 138 |
+
const urlParams = new URLSearchParams(window.location.search);
|
| 139 |
+
const isAuthenticated = urlParams.get("authenticated") === "true";
|
| 140 |
+
|
| 141 |
+
if (!isAuthenticated) {
|
| 142 |
+
alert("Please log in first");
|
| 143 |
+
return;
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
// Add authentication to FormData
|
| 147 |
+
const formData = new FormData();
|
| 148 |
+
formData.append("authenticated", "true");
|
| 149 |
+
formData.append("file", document.getElementById("pdfFile").files[0]);
|
| 150 |
+
formData.append(
|
| 151 |
+
"tharun_voice_id",
|
| 152 |
+
document.getElementById("tharunVoiceId").value
|
| 153 |
+
);
|
| 154 |
+
formData.append(
|
| 155 |
+
"akshara_voice_id",
|
| 156 |
+
document.getElementById("aksharaVoiceId").value
|
| 157 |
+
);
|
| 158 |
+
|
| 159 |
console.log("Form submission started");
|
| 160 |
|
| 161 |
const submitButton = e.target.querySelector("button[type='submit']");
|
|
|
|
| 197 |
if (input) input.disabled = true;
|
| 198 |
});
|
| 199 |
|
| 200 |
+
console.log("Sending request to server...");
|
| 201 |
+
const response = await fetch("/upload-pdf/", {
|
| 202 |
+
method: "POST",
|
| 203 |
+
body: formData,
|
| 204 |
+
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
|
| 206 |
+
console.log("Server response received:", response.status);
|
| 207 |
+
|
| 208 |
+
if (!response.ok) {
|
| 209 |
+
throw new Error(`HTTP error! status: ${response.status}`);
|
| 210 |
+
}
|
| 211 |
|
| 212 |
+
const data = await response.json();
|
| 213 |
+
console.log("Response data:", data);
|
| 214 |
|
| 215 |
+
if (audioResult && data.podcast_file) {
|
| 216 |
+
const audioPlayer = document.getElementById("podcast-player");
|
| 217 |
+
if (audioPlayer) {
|
| 218 |
+
audioPlayer.src = `/download/${data.podcast_file}`;
|
| 219 |
+
audioResult.style.display = "block";
|
| 220 |
}
|
| 221 |
|
| 222 |
+
const segmentsList = document.getElementById("segments-list");
|
| 223 |
+
if (segmentsList && data.segments) {
|
| 224 |
+
segmentsList.innerHTML = "";
|
| 225 |
|
| 226 |
+
const segmentCount = document.querySelector(".segment-count");
|
| 227 |
+
if (segmentCount) {
|
| 228 |
+
segmentCount.textContent = `(${data.segments.length} segments)`;
|
|
|
|
|
|
|
| 229 |
}
|
| 230 |
|
| 231 |
+
data.segments.forEach((segment, index) => {
|
| 232 |
+
const segmentDiv = document.createElement("div");
|
| 233 |
+
segmentDiv.className = "segment";
|
| 234 |
+
segmentDiv.id = `segment-${index}`;
|
| 235 |
+
segmentDiv.innerHTML = `
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 236 |
<div class="segment-info">
|
| 237 |
<div class="segment-header">
|
| 238 |
<div class="segment-speaker">${segment.speaker}</div>
|
|
|
|
| 251 |
</button>
|
| 252 |
</div>
|
| 253 |
`;
|
| 254 |
+
segmentsList.appendChild(segmentDiv);
|
| 255 |
+
});
|
| 256 |
|
| 257 |
+
if (segmentsContainer) {
|
| 258 |
+
segmentsContainer.style.display = "block";
|
|
|
|
| 259 |
}
|
| 260 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 261 |
}
|
| 262 |
};
|
| 263 |
|
| 264 |
async function regenerateSegment(index, newText = null) {
|
| 265 |
+
// Get authentication status from URL
|
| 266 |
+
const urlParams = new URLSearchParams(window.location.search);
|
| 267 |
+
const isAuthenticated = urlParams.get("authenticated") === "true";
|
| 268 |
+
|
| 269 |
+
if (!isAuthenticated) {
|
| 270 |
+
alert("Please log in first");
|
| 271 |
+
return;
|
| 272 |
+
}
|
| 273 |
+
|
| 274 |
const segment = document.querySelector(`#segment-${index}`);
|
| 275 |
const speaker = segment.querySelector(".segment-speaker").textContent;
|
| 276 |
const text =
|
|
|
|
| 289 |
|
| 290 |
try {
|
| 291 |
const formData = new FormData();
|
| 292 |
+
formData.append("authenticated", "true");
|
| 293 |
formData.append("speaker", speaker);
|
| 294 |
formData.append("text", text);
|
| 295 |
formData.append(
|