44import com .encodeforge .model .ConversionSettings ;
55import com .encodeforge .service .DependencyManager ;
66import com .encodeforge .service .PythonBridge ;
7+ import com .encodeforge .service .PythonProcessPool ;
78import com .encodeforge .util .HardwareDetector ;
89import com .encodeforge .util .PathManager ;
910import com .encodeforge .util .StatusManager ;
4950import java .util .Set ;
5051import java .util .concurrent .CompletableFuture ;
5152import java .util .concurrent .TimeoutException ;
53+ import java .util .concurrent .TimeUnit ;
5254
5355/**
5456 * Main window controller for the modernized UI
5557 */
5658public class MainController {
5759 private static final Logger logger = LoggerFactory .getLogger (MainController .class );
5860
59- private final PythonBridge pythonBridge ;
61+ private final PythonBridge pythonBridge ; // Legacy mode
62+ private final PythonProcessPool processPool ; // Multi-worker mode
6063 private final DependencyManager dependencyManager ;
6164 private final StatusManager statusManager ;
6265 // Separate queues for different states
@@ -299,8 +302,37 @@ private enum ResizeDirection {
299302 @ FXML private Tab fileInfoTab ;
300303 @ FXML private Tab logsTab ;
301304
305+ /**
306+ * Constructor for legacy single-worker mode
307+ */
302308 public MainController (PythonBridge pythonBridge ) {
303309 this .pythonBridge = pythonBridge ;
310+ this .processPool = null ; // Legacy mode
311+
312+ // Get dependency manager from MainApp (will be passed in future refactor)
313+ // For now, create a new one
314+ try {
315+ this .dependencyManager = new DependencyManager ();
316+ } catch (IOException e ) {
317+ logger .error ("Failed to initialize DependencyManager" , e );
318+ throw new RuntimeException ("Could not initialize dependency manager" , e );
319+ }
320+
321+ this .statusManager = StatusManager .getInstance ();
322+
323+ // Load settings from disk
324+ this .settings = ConversionSettings .load ();
325+ logger .info ("Settings loaded from: {}" , ConversionSettings .getSettingsFilePath ());
326+
327+ logger .info ("MainController initialized with PythonBridge (legacy mode)" );
328+ }
329+
330+ /**
331+ * Constructor for new multi-worker mode
332+ */
333+ public MainController (PythonProcessPool processPool ) {
334+ this .pythonBridge = null ; // Multi-worker mode
335+ this .processPool = processPool ;
304336
305337 // Get dependency manager from MainApp (will be passed in future refactor)
306338 // For now, create a new one
@@ -317,7 +349,7 @@ public MainController(PythonBridge pythonBridge) {
317349 this .settings = ConversionSettings .load ();
318350 logger .info ("Settings loaded from: {}" , ConversionSettings .getSettingsFilePath ());
319351
320- // FFmpeg is now managed by DependencyManager, no need for embedded setup here
352+ logger . info ( "MainController initialized with PythonProcessPool (multi-worker mode)" );
321353 }
322354
323355 @ FXML
@@ -2532,8 +2564,137 @@ private void resetProcessingState() {
25322564 * FFmpeg detection now uses Java-side DependencyManager instead of Python.
25332565 */
25342566 private void updateAllStatus () {
2567+ // Use parallel execution if process pool is available
2568+ if (processPool != null ) {
2569+ updateAllStatusParallel ();
2570+ } else {
2571+ updateAllStatusSequential ();
2572+ }
2573+ }
2574+
2575+ /**
2576+ * Parallel version using PythonProcessPool for fast concurrent checks
2577+ */
2578+ private void updateAllStatusParallel () {
2579+ try {
2580+ logger .info ("Starting parallel status check (multi-process mode)" );
2581+ long startTime = System .currentTimeMillis ();
2582+
2583+ // Sync settings with Python backend (quick operation)
2584+ try {
2585+ JsonObject settingsParams = new JsonObject ();
2586+ settingsParams .add ("settings" , settings .toJson ());
2587+ processPool .submitQuickTask ("update_settings" , settingsParams ).get (2 , TimeUnit .SECONDS );
2588+ logger .info ("Settings synchronized with Python backend" );
2589+ } catch (Exception e ) {
2590+ logger .error ("Failed to sync settings with Python backend" , e );
2591+ }
2592+
2593+ // Check FFmpeg using Java DependencyManager (parallel with Python checks)
2594+ CompletableFuture <Boolean > ffmpegCheckFuture = CompletableFuture .supplyAsync (() -> {
2595+ try {
2596+ boolean available = dependencyManager .checkFFmpeg ().get ();
2597+ logger .info ("FFmpeg check completed: {}" , available );
2598+ return available ;
2599+ } catch (Exception e ) {
2600+ logger .error ("Error checking FFmpeg via DependencyManager" , e );
2601+ return false ;
2602+ }
2603+ });
2604+
2605+ // Parallel Python backend checks
2606+ CompletableFuture <JsonObject > getAllStatusFuture = CompletableFuture .supplyAsync (() -> {
2607+ try {
2608+ JsonObject result = processPool .submitQuickTask ("get_all_status" ).get (5 , TimeUnit .SECONDS );
2609+ logger .info ("get_all_status completed" );
2610+ return result ;
2611+ } catch (Exception e ) {
2612+ logger .error ("Error getting all status" , e );
2613+ JsonObject error = new JsonObject ();
2614+ error .addProperty ("status" , "error" );
2615+ error .addProperty ("message" , e .getMessage ());
2616+ return error ;
2617+ }
2618+ });
2619+
2620+ // Wait for all checks to complete
2621+ CompletableFuture <Void > allChecks = CompletableFuture .allOf (
2622+ ffmpegCheckFuture ,
2623+ getAllStatusFuture
2624+ );
2625+
2626+ allChecks .get (10 , TimeUnit .SECONDS ); // Wait for all checks
2627+
2628+ // Get results
2629+ boolean ffmpegAvailable = ffmpegCheckFuture .get ();
2630+ String ffmpegVersion = "Not Found" ;
2631+
2632+ if (ffmpegAvailable ) {
2633+ java .nio .file .Path ffmpegPath = dependencyManager .getInstalledFFmpegPath ();
2634+ if (ffmpegPath != null ) {
2635+ ffmpegVersion = "Found" ;
2636+ logger .info ("FFmpeg detected via DependencyManager at: {}" , ffmpegPath );
2637+ } else {
2638+ ffmpegVersion = "System PATH" ;
2639+ logger .info ("FFmpeg detected in system PATH" );
2640+ }
2641+ }
2642+
2643+ JsonObject response = getAllStatusFuture .get ();
2644+
2645+ if (response .has ("status" ) && !response .get ("status" ).isJsonNull ()
2646+ && response .get ("status" ).getAsString ().equals ("error" )) {
2647+ String errorMsg = response .has ("message" ) && !response .get ("message" ).isJsonNull ()
2648+ ? response .get ("message" ).getAsString ()
2649+ : "Unknown error" ;
2650+ logger .error ("Error getting Python status: " + errorMsg );
2651+ }
2652+
2653+ // Update FFmpeg info in response (override Python's check with Java's)
2654+ if (response .has ("ffmpeg" )) {
2655+ JsonObject ffmpegInfo = response .getAsJsonObject ("ffmpeg" );
2656+ ffmpegInfo .addProperty ("available" , ffmpegAvailable );
2657+ if (ffmpegAvailable && ffmpegInfo .has ("version" )) {
2658+ ffmpegVersion = ffmpegInfo .get ("version" ).getAsString ();
2659+ }
2660+ }
2661+
2662+ // Update StatusManager with the consolidated response
2663+ com .encodeforge .util .StatusManager .getInstance ().updateFromResponse (response );
2664+
2665+ // Update UI on JavaFX thread
2666+ final boolean finalFfmpegAvailable = ffmpegAvailable ;
2667+ final String finalFfmpegVersion = ffmpegVersion ;
2668+
2669+ Platform .runLater (() -> {
2670+ updateFFmpegStatus (finalFfmpegAvailable , finalFfmpegVersion );
2671+ updateUIFromStatus ();
2672+ });
2673+
2674+ long elapsedMs = System .currentTimeMillis () - startTime ;
2675+ logger .info ("Parallel status check completed in {}ms" , elapsedMs );
2676+
2677+ } catch (TimeoutException e ) {
2678+ logger .error ("Status check timed out" , e );
2679+ Platform .runLater (() -> {
2680+ log ("ERROR: Status check timed out - " + e .getMessage ());
2681+ updateFFmpegStatus (false , "Timeout" );
2682+ });
2683+ } catch (Exception e ) {
2684+ logger .error ("Unexpected error in parallel status check" , e );
2685+ Platform .runLater (() -> {
2686+ log ("ERROR: " + e .getMessage ());
2687+ updateFFmpegStatus (false , "Error" );
2688+ });
2689+ }
2690+ }
2691+
2692+ /**
2693+ * Sequential version using legacy PythonBridge (backward compatibility)
2694+ */
2695+ private void updateAllStatusSequential () {
25352696 try {
2536- logger .info ("Starting consolidated status check" );
2697+ logger .info ("Starting consolidated status check (legacy mode) " );
25372698
25382699 // First, sync settings with Python backend
25392700 try {
@@ -6394,7 +6555,13 @@ private void checkForOngoingConversions() {
63946555 try {
63956556 logger .info ("Checking for ongoing conversions from previous session..." );
63966557
6397- JsonObject response = pythonBridge .checkOngoingConversion ();
6558+ // Use appropriate bridge based on mode
6559+ JsonObject response ;
6560+ if (processPool != null ) {
6561+ response = processPool .submitQuickTask ("check_ongoing_conversion" ).get (3 , TimeUnit .SECONDS );
6562+ } else {
6563+ response = pythonBridge .checkOngoingConversion ();
6564+ }
63986565
63996566 if (response == null || !response .has ("status" )) {
64006567 logger .warn ("Invalid response from check_ongoing_conversion" );
0 commit comments