@@ -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