From 2e680f2bd8f3b84f8aadc011d301f5ff54b44b35 Mon Sep 17 00:00:00 2001
From: Nico Athanassiadis <nico@dsv.su.se>
Date: Tue, 14 Jan 2025 12:46:08 +0100
Subject: [PATCH] Added explicit whisper properties, so the application is
 easier to configure. Changed the upload and output root so when developing
 you can more easily find the files. Model folder is also changed to be
 consistent with other directory changes. Users are no longer required to go
 from a login page and click on the application name to get to oauth login.
 Transcribed files in the files management view are now sorted the same way as
 the uploaded file status.

---
 .gitignore                                    |  6 ++++
 .../java/se/su/dsv/seshat/Application.java    |  5 ++++
 .../seshat/controllers/FileController.java    | 16 +++++++++-
 .../seshat/controllers/LandingController.java |  7 ++---
 .../su/dsv/seshat/services/Transcriber.java   | 30 +++++++++++++------
 .../seshat/services/WhisperProperties.java    | 11 +++++++
 src/main/resources/application.properties     | 11 +++++--
 .../resources/templates/file-management.html  |  2 +-
 8 files changed, 70 insertions(+), 18 deletions(-)
 create mode 100644 src/main/java/se/su/dsv/seshat/services/WhisperProperties.java

diff --git a/.gitignore b/.gitignore
index 549e00a..f7dd3c1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,3 +31,9 @@ build/
 
 ### VS Code ###
 .vscode/
+
+### Whisper Models ###
+whisper-models/
+
+### Seshat stuff ###
+files/
\ No newline at end of file
diff --git a/src/main/java/se/su/dsv/seshat/Application.java b/src/main/java/se/su/dsv/seshat/Application.java
index b217ee6..4ededf8 100644
--- a/src/main/java/se/su/dsv/seshat/Application.java
+++ b/src/main/java/se/su/dsv/seshat/Application.java
@@ -4,9 +4,14 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.CommandLineRunner;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import se.su.dsv.seshat.services.JobProcessorService;
+import se.su.dsv.seshat.services.WhisperProperties;
 
 @SpringBootApplication
+@EnableConfigurationProperties
+@ConfigurationPropertiesScan
 public class Application implements CommandLineRunner {
 
     @Autowired
diff --git a/src/main/java/se/su/dsv/seshat/controllers/FileController.java b/src/main/java/se/su/dsv/seshat/controllers/FileController.java
index edee48e..bd61b10 100644
--- a/src/main/java/se/su/dsv/seshat/controllers/FileController.java
+++ b/src/main/java/se/su/dsv/seshat/controllers/FileController.java
@@ -23,6 +23,8 @@ import se.su.dsv.seshat.services.UserService;
 
 import java.io.File;
 import java.time.LocalDate;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
@@ -64,7 +66,19 @@ public class FileController {
 
                 }));
 
-        model.addAttribute("filesByDirectory", filesByDirectory);
+        Map<FileMetadata, List<FileMetadata>> sortedFilesByDirectory = filesByDirectory.entrySet().stream()
+                .sorted(Map.Entry.comparingByKey(Comparator.comparing(FileMetadata::getUploadedAt).reversed()))
+                .collect(Collectors.toMap(
+                        Map.Entry::getKey,
+                        Map.Entry::getValue,
+                        (e1, e2) -> {
+                            e1.addAll(e2);
+                            return e1;
+                        },
+                        LinkedHashMap::new
+                ));
+
+        model.addAttribute("filesByDirectory", sortedFilesByDirectory);
         model.addAttribute("statuses", statuses);
 
         return "file-management";
diff --git a/src/main/java/se/su/dsv/seshat/controllers/LandingController.java b/src/main/java/se/su/dsv/seshat/controllers/LandingController.java
index f9c712b..e07debc 100644
--- a/src/main/java/se/su/dsv/seshat/controllers/LandingController.java
+++ b/src/main/java/se/su/dsv/seshat/controllers/LandingController.java
@@ -8,11 +8,8 @@ import org.springframework.web.bind.annotation.GetMapping;
 public class LandingController {
 
     @GetMapping("/")
-    public String showHomePage(Authentication authentication) {
-        if (authentication != null) {
-            return "redirect:/files/manage";
-        }
-        return "redirect:/login";
+    public String showHomePage() {
+        return "redirect:/files/manage";
     }
 
 }
diff --git a/src/main/java/se/su/dsv/seshat/services/Transcriber.java b/src/main/java/se/su/dsv/seshat/services/Transcriber.java
index f7fe21e..76176fe 100644
--- a/src/main/java/se/su/dsv/seshat/services/Transcriber.java
+++ b/src/main/java/se/su/dsv/seshat/services/Transcriber.java
@@ -11,14 +11,22 @@ import java.io.IOException;
 public class Transcriber {
 
     private static final Logger logger = LoggerFactory.getLogger(Transcriber.class);
-    private static final String MODEL_PATH = System.getProperty("user.home") + "/playground/whisper.cpp/models";
-    private static final String WHISPER_WORKING_DIRECTORY = System.getProperty("user.home") + "/git/python-whisper/";
+    private String modelPath;
+    private String selectedModel;
+    private String workingDirectory;
+    ProcessBuilder startVirtualEnv;
+    private String selectedDevice;
 
-    String[] activateVirtualEnv = {"/bin/bash", "-c", "source activate"};
-    ProcessBuilder startVirtualEnv = new ProcessBuilder(activateVirtualEnv);
+    public Transcriber(WhisperProperties whisperProperties) {
+        modelPath = whisperProperties.modelPath();
+        selectedModel = whisperProperties.selectedModel();
+        startVirtualEnv = new ProcessBuilder(whisperProperties.activateVirtualEnvCommand());
+        workingDirectory = whisperProperties.workingDirectory();
+        selectedDevice = whisperProperties.selectedDevice();
+    }
 
     private void setStartVirtualEnv() {
-        startVirtualEnv.directory(new File(WHISPER_WORKING_DIRECTORY + "env/bin/"));
+        startVirtualEnv.directory(new File(workingDirectory + "/env/bin/"));
         startVirtualEnv.inheritIO();
         try {
             Process p = startVirtualEnv.start();
@@ -33,9 +41,13 @@ public class Transcriber {
         StringBuilder whisperCommandOptions = new StringBuilder("env/bin/whisper ")
                 .append(inputFilePath)
                 .append(" --model_dir ")
-                .append(MODEL_PATH)
-                .append(" --model medium --output_dir ")
-                .append(outputDirectory);
+                .append(modelPath)
+                .append(" --model ")
+                .append(selectedModel)
+                .append(" --output_dir ")
+                .append(outputDirectory)
+                .append(" --device ")
+                .append(selectedDevice);
         if(language != null && !language.equalsIgnoreCase("auto")) {
             whisperCommandOptions.append(" --language ")
                     .append(language);
@@ -44,7 +56,7 @@ public class Transcriber {
         String[] whisperCommand = {"/bin/bash", "-c", whisperCommandOptions.toString()};
         ProcessBuilder transcribeProcess = new ProcessBuilder(whisperCommand);
 
-        transcribeProcess.directory(new File(WHISPER_WORKING_DIRECTORY));
+        transcribeProcess.directory(new File(workingDirectory));
         transcribeProcess.inheritIO();
         try {
             Process p = transcribeProcess.start();
diff --git a/src/main/java/se/su/dsv/seshat/services/WhisperProperties.java b/src/main/java/se/su/dsv/seshat/services/WhisperProperties.java
new file mode 100644
index 0000000..a959d53
--- /dev/null
+++ b/src/main/java/se/su/dsv/seshat/services/WhisperProperties.java
@@ -0,0 +1,11 @@
+package se.su.dsv.seshat.services;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties(prefix = "whisper")
+public record WhisperProperties(String modelPath,
+                                String selectedModel,
+                                String workingDirectory,
+                                String selectedDevice,
+                                String[] activateVirtualEnvCommand) {
+}
\ No newline at end of file
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 2710d11..6f256bb 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -4,10 +4,17 @@ server.port=8181
 spring.servlet.multipart.max-file-size=5GB
 spring.servlet.multipart.max-request-size=5GB
 
-app.upload-root=/seshat/uploads
-app.output-root=/seshat/outputs
+app.upload-root=@project.basedir@/files/uploads
+app.output-root=@project.basedir@/files/outputs
 app.onSuccess-homepage=/files/manage
 
+# Whisper properties
+whisper.activate-virtual-env-command=bash, -c, source activate
+whisper.model-path=@project.basedir@/whisper-models
+whisper.selected-model=medium
+whisper.working-directory=${user.home}/git/python-whisper
+whisper.selected-device=cpu
+
 # Database properties (local development)
 spring.datasource.url=jdbc:mariadb://localhost:3306/seshat
 spring.datasource.username=myuser
diff --git a/src/main/resources/templates/file-management.html b/src/main/resources/templates/file-management.html
index ff54067..1abf486 100644
--- a/src/main/resources/templates/file-management.html
+++ b/src/main/resources/templates/file-management.html
@@ -3,7 +3,7 @@
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>File Management - Seshat App</title>
+    <title>Seshat Auido Transcriber</title>
     <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css">
     <link rel="stylesheet"
           href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.9.1/font/bootstrap-icons.min.css">