@@ -170,35 +170,44 @@ def calculate_subnet(base_subnet: str, base_netmask: int, offset: int) -> str:
170170
171171
172172def assign_app_ports (
173- app_name : str , app : dict [str , Any ], config : dict [str , Any ]
173+ app_name : str ,
174+ app : dict [str , Any ],
175+ config : dict [str , Any ],
176+ app_index : int = 0 ,
177+ instance_number : int = 0 ,
174178) -> list [int ]:
175179 """
176- Assign available ports for an app based on its configuration.
180+ Assign available ports for an app based on its configuration using consistent offset logic.
181+
182+ This function uses a unified formula for both main and multiproxy instances:
183+ port = DEFAULT_PORT_BASE + (app_index * PORT_OFFSET_PER_APP) + (instance_number * PORT_OFFSET_PER_INSTANCE) + port_within_app_offset
177184
178185 Args:
179186 app_name (str): Name of the app
180187 app (dict[str, Any]): App configuration containing compose_config
181188 config (dict[str, Any]): User configuration for the app
189+ app_index (int, optional): Zero-based index of the app in the apps list. Defaults to 0.
190+ instance_number (int, optional): Instance number (0 for main, 1+ for multiproxy). Defaults to 0.
182191
183192 Returns:
184193 list[int]: List of assigned available ports
185194 """
186195 port_count = len (app ["compose_config" ]["ports" ])
187196 assigned_ports = []
188- default_ports = [DEFAULT_PORT_BASE + j for j in range (port_count )]
197+
198+ # Calculate the base port for this specific app and instance using the unified formula
199+ base_port_for_app_instance = (
200+ DEFAULT_PORT_BASE
201+ + (app_index * PORT_OFFSET_PER_APP )
202+ + (instance_number * PORT_OFFSET_PER_INSTANCE )
203+ )
189204
190205 for i in range (port_count ):
191- starting_port = config .get ("ports" , default_ports )
192- # Determine the base port for this index
193- if isinstance (starting_port , list ):
194- port_base = (
195- starting_port [i ] if i < len (starting_port ) else DEFAULT_PORT_BASE + i
196- )
197- else :
198- port_base = DEFAULT_PORT_BASE + i
206+ # For apps with multiple ports, add a small offset for each additional port
207+ port_candidate = base_port_for_app_instance + i
199208
200- # Find next available port and assign it
201- available_port = find_next_available_port (port_base )
209+ # Find next available port starting from the calculated candidate
210+ available_port = find_next_available_port (port_candidate )
202211 assigned_ports .append (available_port )
203212
204213 # Log the port assignment
@@ -208,7 +217,9 @@ def assign_app_ports(
208217 and i < len (app ["compose_config" ]["ports" ])
209218 else f"port_{ i + 1 } "
210219 )
211- logging .info (f"Port { port_placeholder } for { app_name } set to: { available_port } " )
220+ logging .info (
221+ f"Port { port_placeholder } for { app_name } (app_index={ app_index } , instance={ instance_number } ) set to: { available_port } "
222+ )
212223
213224 return assigned_ports
214225
@@ -420,15 +431,25 @@ def collect_user_info(user_config: dict[str, Any], m4b_config: dict[str, Any]) -
420431 logging .info (f"Device name set to: { device_name } " )
421432
422433
423- def _configure_apps (user_config : dict [str , Any ], apps : dict , m4b_config : dict ):
434+ def _configure_apps (
435+ user_config : dict [str , Any ],
436+ apps : dict ,
437+ m4b_config : dict ,
438+ app_index_offset : int = 0 ,
439+ ):
424440 """
425441 Configure apps by collecting user inputs.
426442
427443 Args:
428444 user_config (dict): The user configuration dictionary.
429445 apps (dict): The app configuration dictionary.
430446 m4b_config (dict): The m4b configuration dictionary.
447+ app_index_offset (int): Starting port_app_index for this category.
448+ Defaults to 0. Only counts apps WITH ports.
431449 """
450+ # Track port_app_index separately - only incremented for apps WITH ports
451+ port_app_index = app_index_offset
452+
432453 for app in apps :
433454 app_name = app ["name" ].lower ()
434455 config = user_config ["apps" ].get (app_name , {})
@@ -463,12 +484,16 @@ def _configure_apps(user_config: dict[str, Any], apps: dict, m4b_config: dict):
463484 else :
464485 logging .error (f"Flag { flag_name } not recognized" )
465486
466- # Port configuration for apps with defined ports (should have a 'ports' key in the compose_config and a <app_name>_ports key in the user_config )
487+ # Port configuration with unified offset logic (instance 0 = main )
467488 if "ports" in app ["compose_config" ]:
468- assigned_ports = assign_app_ports (app_name , app , config )
489+ assigned_ports = assign_app_ports (
490+ app_name , app , config , app_index = port_app_index , instance_number = 0
491+ )
469492 # Always store as list for consistency
470493 config ["ports" ] = assigned_ports
471494 logging .info (f"Ports for { app_name } set to: { config ['ports' ]} " )
495+ # Only increment port_app_index for apps that actually have ports
496+ port_app_index += 1
472497
473498 user_config ["apps" ][app_name ] = config
474499
@@ -484,7 +509,7 @@ def configure_apps(
484509 app_config (dict): The app configuration dictionary.
485510 m4b_config (dict): The m4b configuration dictionary.
486511 """
487- _configure_apps (user_config , app_config ["apps" ], m4b_config )
512+ _configure_apps (user_config , app_config ["apps" ], m4b_config , app_index_offset = 0 )
488513
489514
490515def configure_extra_apps (
@@ -498,7 +523,14 @@ def configure_extra_apps(
498523 app_config (dict): The app configuration dictionary.
499524 m4b_config (dict): The m4b configuration dictionary.
500525 """
501- _configure_apps (user_config , app_config ["extra-apps" ], m4b_config )
526+ # Extra apps port_app_index starts AFTER regular apps WITH ports
527+ # Count only apps that have ports in compose_config
528+ port_consuming_apps = sum (
529+ 1 for app in app_config .get ("apps" , []) if "ports" in app .get ("compose_config" , {})
530+ )
531+ _configure_apps (
532+ user_config , app_config ["extra-apps" ], m4b_config , app_index_offset = port_consuming_apps
533+ )
502534
503535
504536# Supported services and their URL patterns
@@ -701,8 +733,9 @@ def setup_multiproxy_instances(
701733
702734 instance_m4b_config ["network" ]["subnet" ] = new_subnet
703735
704- # Update all enabled apps with unique ports to avoid conflicts
705- app_index = 0
736+ # Update all enabled apps with unique ports using the unified assignment logic
737+ # Track port_app_index separately - only incremented for apps WITH ports
738+ port_app_index = 0
706739 for app_category in ["apps" , "extra-apps" ]:
707740 for app_details in instance_app_config .get (app_category , []):
708741 app_name = app_details ["name" ].lower ()
@@ -713,7 +746,8 @@ def setup_multiproxy_instances(
713746 app_config_entry and app_config_entry .get ("enabled" , False )
714747 ):
715748 logging .info (
716- f"Processing port assignments for { app_name } in instance { instance_project_name } "
749+ f"Processing port assignments for { app_name } "
750+ f"in instance { instance_project_name } "
717751 )
718752
719753 # If app isn't in user_config yet, initialize it
@@ -722,42 +756,25 @@ def setup_multiproxy_instances(
722756 app_config_entry = instance_user_config ["apps" ][app_name ]
723757
724758 # Check if this app has ports defined in compose_config
725- has_ports = False
726759 if (
727760 "compose_config" in app_details
728761 and "ports" in app_details ["compose_config" ]
729762 ):
730- has_ports = True
731-
732- # Or check if it already has ports in user_config
733- if "ports" in app_config_entry :
734- has_ports = True
735-
736- # If app uses ports, ensure they're unique for this instance
737- if has_ports :
738- # Get base port from user config or default
739- base_port = app_config_entry .get (
740- "ports" ,
741- [DEFAULT_PORT_BASE + app_index * PORT_OFFSET_PER_APP ],
763+ # Use unified port assignment logic (instance number = i+1)
764+ assigned_ports = assign_app_ports (
765+ app_name ,
766+ app_details ,
767+ app_config_entry ,
768+ app_index = port_app_index ,
769+ instance_number = i + 1 ,
742770 )
743- # Ensure we have a list (handles legacy configs with int)
744- if not isinstance (base_port , list ):
745- base_port = [base_port ]
746-
747- # Update all ports with unique values for this instance
748- app_config_entry ["ports" ] = [
749- find_next_available_port (
750- port + (i + 1 ) * PORT_OFFSET_PER_INSTANCE
751- )
752- for port in base_port
753- ]
771+ app_config_entry ["ports" ] = assigned_ports
754772 logging .info (
755- f"Updated ports for { app_name } in instance "
756- f"{ instance_project_name } to { app_config_entry [ 'ports' ] } "
773+ f"Assigned ports for { app_name } in instance "
774+ f"{ instance_project_name } : { assigned_ports } "
757775 )
758-
759- # Increment app index for each enabled app processed
760- app_index += 1
776+ # Only increment port_app_index for apps that have ports
777+ port_app_index += 1
761778
762779 # Properly disable dashboard for multiproxy instances to avoid port conflicts
763780 if "m4b_dashboard" in instance_user_config :
0 commit comments