Skip to content

Commit 9de93c6

Browse files
committed
Refactor EncodeForgeCore for lazy initialization of Whisper manager and handler setup
- Removed early import of WhisperManager and its initialization in EncodeForgeCore. - Implemented lazy loading of Whisper manager with a property method. - Added reset method for Whisper manager availability check. - Introduced lazy initialization for handlers to improve startup performance. - Updated logging to reflect changes in Whisper model availability and initialization. Enhance RenamingHandler with parallel processing for metadata lookups - Integrated ThreadPoolExecutor for concurrent metadata retrieval. - Improved performance for renaming operations by processing files in parallel. Update SubtitleHandler to handle Whisper model fallbacks and improved logging - Added fallback logic for Whisper models if the requested model is not installed. - Enhanced logging for model usage and detection. - Updated subtitle application methods to support different modes (external, embed, burn-in). - Improved error handling and messaging for subtitle generation and application. Implement ResourceManager for intelligent system resource detection and worker allocation - Created ResourceManager class to manage CPU and RAM detection. - Added methods to calculate optimal worker counts for various tasks. - Integrated resource management into Whisper and encoding processes for better performance.
1 parent ed3800e commit 9de93c6

13 files changed

Lines changed: 1578 additions & 307 deletions

File tree

EncodeForge/src/main/java/com/encodeforge/controller/MainController.java

Lines changed: 367 additions & 58 deletions
Large diffs are not rendered by default.

EncodeForge/src/main/java/com/encodeforge/controller/SettingsController.java

Lines changed: 179 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public class SettingsController {
3434
private Stage dialogStage;
3535
private ConversionSettings settings;
3636
private PythonBridge pythonBridge;
37+
private com.encodeforge.service.PythonProcessPool processPool;
3738
private DependencyManager dependencyManager;
3839
private StatusManager statusManager;
3940
private boolean applied = false;
@@ -296,6 +297,12 @@ public void setPythonBridge(PythonBridge pythonBridge) {
296297
checkFFmpegStatus();
297298

298299
// Encoder detection is now handled instantly by Java HardwareDetector
300+
}
301+
302+
public void setProcessPool(com.encodeforge.service.PythonProcessPool processPool) {
303+
this.processPool = processPool;
304+
// Update Whisper status using process pool
305+
updateWhisperStatus();
299306
// No need for delayed backend checks
300307
logger.info("Settings dialog initialized with hardware-detected encoders");
301308
}
@@ -1066,6 +1073,7 @@ private void handleSetupWhisper() {
10661073

10671074
try {
10681075
WhisperSetupDialog setupDialog = new WhisperSetupDialog(dependencyManager);
1076+
setupDialog.setProcessPool(processPool);
10691077
setupDialog.showAndWait();
10701078

10711079
// Refresh status after installation
@@ -1184,69 +1192,68 @@ private void handleDownloadWhisperModel() {
11841192

11851193
// Update UI to show progress
11861194
if (whisperStatusLabel != null) {
1187-
whisperStatusLabel.setText("⏳ Downloading " + selectedModel + " model...");
1195+
whisperStatusLabel.setText("⏳ Preparing to download " + selectedModel + " model...");
11881196
}
11891197

1190-
// Run download in background
1191-
new Thread(() -> {
1192-
try {
1193-
if (pythonBridge == null) {
1194-
throw new RuntimeException("PythonBridge not initialized");
1195-
}
1196-
1197-
// Call Python backend to download model
1198-
JsonObject command = new JsonObject();
1199-
command.addProperty("action", "download_whisper_model");
1200-
command.addProperty("model", selectedModel);
1201-
1202-
JsonObject response = pythonBridge.sendCommand(command);
1203-
1198+
// Use processPool with streaming if available
1199+
if (processPool != null) {
1200+
JsonObject params = new JsonObject();
1201+
params.addProperty("model", selectedModel);
1202+
1203+
// Submit streaming task with progress updates
1204+
processPool.submitStreamingTask("download_whisper_model", params, progressResponse -> {
12041205
Platform.runLater(() -> {
1205-
if (response == null) {
1206-
Alert alert = new Alert(Alert.AlertType.ERROR);
1207-
alert.setTitle("Download Failed");
1208-
alert.setHeaderText("No response from Python backend");
1209-
alert.setContentText("The Python backend did not respond. Check logs for more details.");
1210-
alert.showAndWait();
1211-
updateWhisperStatus();
1206+
// Null check
1207+
if (progressResponse == null) {
1208+
logger.warn("Received null progress response during model download");
12121209
return;
12131210
}
12141211

1215-
String status = response.has("status") ? response.get("status").getAsString() : "error";
1216-
String message = response.has("message") ? response.get("message").getAsString() : "Unknown error";
1217-
1218-
if ("success".equals(status)) {
1219-
Alert info = new Alert(Alert.AlertType.INFORMATION);
1220-
info.setTitle("Model Downloaded");
1221-
info.setHeaderText(selectedModel + " model downloaded successfully");
1222-
info.setContentText(message);
1223-
info.showAndWait();
1212+
// Check response type
1213+
if (progressResponse.has("type") && "progress".equals(progressResponse.get("type").getAsString())) {
1214+
// This is a progress update
1215+
int progress = progressResponse.has("progress") ? progressResponse.get("progress").getAsInt() : 0;
1216+
String message = progressResponse.has("message") ? progressResponse.get("message").getAsString() : "Downloading...";
12241217

1225-
updateWhisperStatus();
1218+
if (whisperStatusLabel != null) {
1219+
whisperStatusLabel.setText(String.format("⏳ %s (%d%%)", message, progress));
1220+
}
1221+
logger.debug("Download progress: {}% - {}", progress, message);
12261222
} else {
1227-
Alert alert = new Alert(Alert.AlertType.ERROR);
1228-
alert.setTitle("Download Failed");
1229-
alert.setHeaderText("Failed to download " + selectedModel + " model");
1230-
alert.setContentText(message);
1231-
alert.showAndWait();
1232-
1233-
updateWhisperStatus();
1223+
// This is the final response
1224+
handleDownloadResponse(selectedModel, progressResponse);
12341225
}
12351226
});
1236-
} catch (Exception e) {
1237-
logger.error("Failed to download Whisper model", e);
1238-
Platform.runLater(() -> {
1239-
Alert alert = new Alert(Alert.AlertType.ERROR);
1240-
alert.setTitle("Error");
1241-
alert.setHeaderText("Failed to download model");
1242-
alert.setContentText(e.getMessage());
1243-
alert.showAndWait();
1227+
});
1228+
} else if (pythonBridge != null) {
1229+
// Fallback to legacy pythonBridge (blocking)
1230+
new Thread(() -> {
1231+
try {
1232+
JsonObject command = new JsonObject();
1233+
command.addProperty("action", "download_whisper_model");
1234+
command.addProperty("model", selectedModel);
12441235

1245-
updateWhisperStatus();
1246-
});
1247-
}
1248-
}).start();
1249-
1236+
JsonObject response = pythonBridge.sendCommand(command);
1237+
handleDownloadResponse(selectedModel, response);
1238+
} catch (Exception e) {
1239+
logger.error("Failed to download Whisper model via pythonBridge", e);
1240+
Platform.runLater(() -> {
1241+
Alert alert = new Alert(Alert.AlertType.ERROR);
1242+
alert.setTitle("Error");
1243+
alert.setHeaderText("Failed to download model");
1244+
alert.setContentText(e.getMessage());
1245+
alert.showAndWait();
1246+
updateWhisperStatus();
1247+
});
1248+
}
1249+
}).start();
1250+
} else {
1251+
Alert alert = new Alert(Alert.AlertType.ERROR);
1252+
alert.setTitle("Error");
1253+
alert.setHeaderText("Python backend not available");
1254+
alert.setContentText("Neither ProcessPool nor PythonBridge is available");
1255+
alert.showAndWait();
1256+
}
12501257
} catch (Exception e) {
12511258
logger.error("Failed to start Whisper model download", e);
12521259
Alert alert = new Alert(Alert.AlertType.ERROR);
@@ -1258,10 +1265,75 @@ private void handleDownloadWhisperModel() {
12581265
}
12591266
}
12601267

1268+
/**
1269+
* Handle download response (helper method for both processPool and pythonBridge paths)
1270+
*/
1271+
private void handleDownloadResponse(String selectedModel, JsonObject response) {
1272+
Platform.runLater(() -> {
1273+
if (response == null) {
1274+
Alert alert = new Alert(Alert.AlertType.ERROR);
1275+
alert.setTitle("Download Failed");
1276+
alert.setHeaderText("No response from Python backend");
1277+
alert.setContentText("The Python backend did not respond. Check logs for more details.");
1278+
alert.showAndWait();
1279+
updateWhisperStatus();
1280+
return;
1281+
}
1282+
1283+
String status = response.has("status") ? response.get("status").getAsString() : "error";
1284+
String message = response.has("message") ? response.get("message").getAsString() : "Unknown error";
1285+
1286+
if ("success".equals(status)) {
1287+
Alert info = new Alert(Alert.AlertType.INFORMATION);
1288+
info.setTitle("Model Downloaded");
1289+
info.setHeaderText(selectedModel + " model downloaded successfully");
1290+
info.setContentText(message);
1291+
info.showAndWait();
1292+
1293+
updateWhisperStatus();
1294+
} else {
1295+
Alert alert = new Alert(Alert.AlertType.ERROR);
1296+
alert.setTitle("Download Failed");
1297+
alert.setHeaderText("Failed to download " + selectedModel + " model");
1298+
alert.setContentText(message);
1299+
alert.showAndWait();
1300+
1301+
updateWhisperStatus();
1302+
}
1303+
});
1304+
}
1305+
12611306
/**
12621307
* Update Whisper status display
12631308
*/
12641309
private void updateWhisperStatus() {
1310+
// Use processPool if available, fallback to statusManager
1311+
if (processPool != null) {
1312+
// Use process pool for non-blocking status check
1313+
try {
1314+
JsonObject command = new JsonObject();
1315+
command.addProperty("action", "check_whisper");
1316+
1317+
processPool.submitQuickTask("check_whisper", command)
1318+
.thenAccept(response -> {
1319+
Platform.runLater(() -> handleWhisperStatusResponse(response));
1320+
})
1321+
.exceptionally(e -> {
1322+
logger.warn("Could not check Whisper status via processPool", e);
1323+
Platform.runLater(() -> {
1324+
whisperStatusLabel.setText("⚠️ Whisper status check failed");
1325+
whisperInstalledModelsLabel.setText("Unable to check status");
1326+
});
1327+
return null;
1328+
});
1329+
return;
1330+
} catch (Exception e) {
1331+
logger.warn("Error submitting Whisper status check", e);
1332+
// Fall through to statusManager fallback
1333+
}
1334+
}
1335+
1336+
// Fallback to statusManager (legacy)
12651337
if (statusManager == null) {
12661338
whisperStatusLabel.setText("Whisper status unavailable");
12671339
return;
@@ -1326,6 +1398,61 @@ private void updateWhisperStatus() {
13261398
}
13271399
}
13281400

1401+
/**
1402+
* Handle Whisper status response from process pool
1403+
*/
1404+
private void handleWhisperStatusResponse(JsonObject response) {
1405+
if (response == null) {
1406+
whisperStatusLabel.setText("⚠️ Status check failed");
1407+
whisperInstalledModelsLabel.setText("No response from Python");
1408+
return;
1409+
}
1410+
1411+
// Check whisper_available field (the actual response format from check_whisper)
1412+
boolean available = response.has("whisper_available") && response.get("whisper_available").getAsBoolean();
1413+
1414+
if (available) {
1415+
// Parse installed models first to check if any exist
1416+
List<String> modelList = new ArrayList<>();
1417+
if (response.has("installed_models")) {
1418+
JsonArray models = response.getAsJsonArray("installed_models");
1419+
for (int i = 0; i < models.size(); i++) {
1420+
modelList.add(models.get(i).getAsString());
1421+
}
1422+
}
1423+
1424+
// Update status label based on whether models are installed
1425+
if (modelList.isEmpty()) {
1426+
whisperStatusLabel.setText("⚠️ Whisper installed but no models available");
1427+
whisperStatusLabel.setStyle("-fx-text-fill: #dcdcaa;"); // Yellow/warning color
1428+
whisperInstalledModelsLabel.setText("No models installed. Click 'Download Model' to install one.");
1429+
} else {
1430+
whisperStatusLabel.setText("✅ Whisper AI is installed and ready");
1431+
whisperStatusLabel.setStyle("-fx-text-fill: #4ec9b0;");
1432+
whisperInstalledModelsLabel.setText("Installed models: " + String.join(", ", modelList));
1433+
}
1434+
1435+
// Enable buttons since Whisper itself is installed
1436+
if (uninstallWhisperButton != null) {
1437+
uninstallWhisperButton.setDisable(false);
1438+
}
1439+
if (downloadModelButton != null) {
1440+
downloadModelButton.setDisable(false);
1441+
}
1442+
} else {
1443+
whisperStatusLabel.setText("❌ Whisper AI is not installed");
1444+
whisperStatusLabel.setStyle("-fx-text-fill: #d13438;");
1445+
whisperInstalledModelsLabel.setText("Click 'Setup Whisper AI' to install");
1446+
1447+
if (uninstallWhisperButton != null) {
1448+
uninstallWhisperButton.setDisable(true);
1449+
}
1450+
if (downloadModelButton != null) {
1451+
downloadModelButton.setDisable(true);
1452+
}
1453+
}
1454+
}
1455+
13291456
/**
13301457
* Handle Download FFmpeg button - uses DependencyManager
13311458
*/

0 commit comments

Comments
 (0)