About page and progress bar

Added an about page that describes succinctly what the application is and what tech it is using.
Made it as a single page so that a link can be referenced in text.

Added a progressbar when uploading files.
This commit is contained in:
Nico Athanassiadis 2025-03-24 11:42:49 +01:00
parent 48845951cb
commit e39c5daaa5
6 changed files with 116 additions and 8 deletions
src/main
java/se/su/dsv/seshat
resources

@ -1,15 +1,18 @@
package se.su.dsv.seshat.controllers;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class LandingController {
public class RootController {
@GetMapping("/")
public String showHomePage() {
return "redirect:/files/manage";
}
@GetMapping("/about")
public String showAboutPage() {
return "about";
}
}

@ -99,7 +99,8 @@ public class StorageService {
String sanitizedFilename = sanitizeFilename(originalFilename);
// Users upload directory
Path userUploadDir = Paths.get(uploadRoot, user.getUsername(), UUID.randomUUID().toString());
String uuid = UUID.randomUUID().toString();
Path userUploadDir = Paths.get(uploadRoot, user.getUsername(), uuid);
try {
if(!Files.exists(userUploadDir)) {
@ -124,7 +125,7 @@ public class StorageService {
}
metadata.setUploadedAt(LocalDateTime.now());
String fileFolder = fileNameAndUploadedTime(metadata);
metadata.setOutputDirectory(outputRoot + File.separator + user.getUsername() + File.separator + fileFolder);
metadata.setOutputDirectory(outputRoot + File.separator + user.getUsername() + File.separator + uuid + File.separator + fileFolder);
metadata.setUser(user);
return fileMetadataRepository.save(metadata);
} catch (IOException e) {

@ -3,9 +3,16 @@
--bs-btn-bg: #002F5F;
}
.header {
padding: 6rem;
}
.header-link {
color: white;
text-decoration: none;
}
.header-powered-by {
font-size: 0.8rem;
}
@ -53,6 +60,10 @@
}
footer {
margin-top: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 6rem;
}
html, body {

@ -57,4 +57,44 @@ function validateFile() {
}
return true;
}
}
// Progress bar for file upload
document.getElementById('uploadForm').addEventListener('submit', function(event) {
event.preventDefault();
const fileInput = document.getElementById('file');
const file = fileInput.files[0];
const formData = new FormData();
formData.append('file', file);
formData.append('language', document.getElementById('language').value);
const xhr = new XMLHttpRequest();
xhr.open('POST', '/files/upload', true);
xhr.upload.addEventListener('progress', function(event) {
if (event.lengthComputable) {
const percentComplete = (event.loaded / event.total) * 100;
const progressBar = document.getElementById('progressBar');
progressBar.style.width = percentComplete + '%';
progressBar.setAttribute('aria-valuenow', percentComplete);
progressBar.textContent = Math.round(percentComplete) + '%';
}
});
xhr.addEventListener('load', function() {
if (xhr.status === 200) {
window.location.reload();
} else {
alert('File upload failed');
}
document.getElementById('progressContainer').style.display = 'none';
});
xhr.addEventListener('error', function() {
alert('File upload failed');
document.getElementById('progressContainer').style.display = 'none';
});
document.getElementById('progressContainer').style.display = 'block';
xhr.send(formData);
});

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Seshat Auido Transcriber</title>
<link th:rel="stylesheet" th:href="@{/3p/bootstrap-5.3.3-dist/css/bootstrap.min.css}" />
<link th:rel="stylesheet" th:href="@{/3p/bootstrap-icons-1.11.3/font/bootstrap-icons.min.css}" />
<link th:rel="stylesheet" th:href="@{/css/styles.css}" />
</head>
<body>
<header class="header bg-primary text-white py-3">
<div class="container d-flex justify-content-between align-items-center">
<img th:src="@{/images/SU_logotyp_Landscape_Invert.svg}" alt="Stockholm University Logotype" class="logo">
<h1 class="app-title mb-0"><a href="/files/manage" class="header-link">Seshat Audio Transcriber</a></h1>
<div th:if="${#authentication.name != 'anonymousUser'}" class="dropdown">
<a class="user-menu text-white text-decoration-none dropdown-toggle" href="#" id="userMenu" role="button"
data-bs-toggle="dropdown" aria-expanded="false">
<i class="bi bi-person-circle"></i>
<span th:text="${#authentication.getName()}">Username</span>
</a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userMenu">
<li><a class="dropdown-item" th:href="@{/logout}">Logout</a></li>
</ul>
</div>
</div>
<div class="container text-center">
<span class="span-small">
<a href="https://openai.com/index/whisper/" target="_blank" class="header-link header-powered-by">Powered with Whisper AI</a>
</span>
</div>
</header>
<main class="container mt-4">
<h2>About Seshat Audio Transcriber</h2>
<p>This tool allows you to upload audio files and transcribe them into text using whisperAI.</p>
<p>The application runs a local instance of <a href="https://github.com/openai/whisper" target="_blank">Whisper AI</a>, using the turbo model on one NVIDIA RTX A4000 graphics card.</p>
<p>All processing is done locally, and no data is sent to the cloud, ensuring your privacy and data security.</p>
</main>
<footer class="bg-primary text-white py-4">
<span>&copy; 2024 Seshat App</span>
<a href="/about" class="text-white text-decoration-none">About</a>
</footer>
<script type="text/javascript" th:src="@{/3p/bootstrap-5.3.3-dist/js/bootstrap.bundle.min.js}"></script>
<script type="text/javascript" th:src="@{/3p/htmx/2.0.4/dist/htmx.min.js}"></script>
<script type="text/javascript" th:src="@{/js/script.js}"></script>
</body>
</html>

@ -12,7 +12,7 @@
<header class="header bg-primary text-white py-3">
<div class="container d-flex justify-content-between align-items-center">
<img th:src="@{/images/SU_logotyp_Landscape_Invert.svg}" alt="Stockholm University Logotype" class="logo">
<h1 class="app-title mb-0">Seshat Audio Transcriber</h1>
<h1 class="app-title mb-0"><a href="/files/manage" class="header-link">Seshat Audio Transcriber</a></h1>
<div th:if="${#authentication.name != 'anonymousUser'}" class="dropdown">
<a class="user-menu text-white text-decoration-none dropdown-toggle" href="#" id="userMenu" role="button"
data-bs-toggle="dropdown" aria-expanded="false">
@ -26,7 +26,7 @@
</div>
<div class="container text-center">
<span class="span-small">
<a href="https://openai.com/index/whisper/" target="_blank" class="header-link">Powered with Whisper AI</a>
<a href="https://openai.com/index/whisper/" target="_blank" class="header-link header-powered-by">Powered with Whisper AI</a>
</span>
</div>
</header>
@ -36,7 +36,7 @@
<!-- File Upload Section -->
<section>
<h3>Upload File</h3>
<form th:action="@{/files/upload}" method="post" enctype="multipart/form-data">
<form id="uploadForm" th:action="@{/files/upload}" method="post" enctype="multipart/form-data">
<div class="mb-3">
<label for="file" class="form-label">Choose File</label>
<input type="file" id="file" name="file" class="form-control" required
@ -57,6 +57,11 @@
</div>
<button type="submit" class="btn btn-primary">Upload</button>
</form>
<div id="progressContainer" class="mt-3" style="display: none;">
<div class="progress">
<div id="progressBar" class="progress-bar" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
</div>
</div>
</section>
<!-- File Status Section -->
<section th:if="${fileUploadStatuses != null && !fileUploadStatuses.isEmpty()}" class="mt-5">
@ -160,6 +165,7 @@
</main>
<footer class="bg-primary text-white text-center py-3">
<p>&copy; 2024 Seshat App</p>
<a href="/about" class="text-white text-decoration-none">About</a>
</footer>
<script type="text/javascript" th:src="@{/3p/bootstrap-5.3.3-dist/js/bootstrap.bundle.min.js}"></script>
<script type="text/javascript" th:src="@{/3p/htmx/2.0.4/dist/htmx.min.js}"></script>