when shutdown is finished the script exits and then goes back to the command window instead of closing the window, I don't want the cmd window running still after shutdown is called.

import os
import sys
import json
import copy
import logging
import torch
import yt_dlp
import asyncio
import uvicorn

from fastapi import FastAPI, HTTPException, Request, UploadFile, File, Form
from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
from typing import Optional

import spaces
from audio_separator.separator import Separator
from assets.i18n.i18n import I18nAuto
from assets.presence.discord_presence import RPCManager, track_presence

# Setup logging
logging.basicConfig(level=logging.INFO)

# Constants and paths
now_dir = os.getcwd()
sys.path.append(now_dir)

models_dir = "./models"
out_dir = "./outputs"
temp_dir = "./temp"
os.makedirs(out_dir, exist_ok=True)
os.makedirs(temp_dir, exist_ok=True)

extensions = (".wav", ".flac", ".mp3", ".ogg", ".opus", ".m4a", ".aiff", ".ac3")

config_file = os.path.join(now_dir, "assets", "config.json")
default_settings_file = os.path.join(now_dir, "assets", "default_settings.json")
custom_settings_file = os.path.join(now_dir, "assets", "custom_settings.json")

device = "cuda" if torch.cuda.is_available() else "cpu"
use_autocast = device == "cuda"

# VR ARCH model (Server A)
VRARCH_MODEL = "UVR-DeEcho-DeReverb.pth"
VRARCH_MODEL_PATH = os.path.join(models_dir, VRARCH_MODEL)
if not os.path.exists(VRARCH_MODEL_PATH):
    raise FileNotFoundError(f"Model '{VRARCH_MODEL}' not found in '{models_dir}'")

# MDX23C models list (from Server B)
mdx23c_models = [
    'MDX23C_D1581.ckpt',
    'MDX23C-8KFFT-InstVoc_HQ.ckpt',
    'MDX23C-8KFFT-InstVoc_HQ_2.ckpt',
    'MDX23C-De-Reverb-aufr33-jarredou.ckpt',
    'MDX23C-DrumSep-aufr33-jarredou.ckpt'
]

# Internationalization instance
i18n = I18nAuto()

# --- Shutdown auth token, configure this to a secure value ---
SHUTDOWN_AUTH_TOKEN = "your-secret-token"  # Replace accordingly

# Global reference to uvicorn.Server for shutdown
server: uvicorn.Server = None


def load_settings_from_file(filepath):
    try:
        with open(filepath, 'r', encoding='utf-8') as f:
            return json.load(f)
    except Exception:
        return None


def read_main_config():
    try:
        with open(config_file, "r", encoding="utf8") as f:
            return json.load(f)
    except Exception:
        return {}


def get_initial_settings():
    main_config = read_main_config()
    load_custom = main_config.get('load_custom_settings', False)

    settings_to_load = {}
    default_settings = load_settings_from_file(default_settings_file)

    if load_custom:
        custom_settings = load_settings_from_file(custom_settings_file)
        if custom_settings:
            settings_to_load = copy.deepcopy(default_settings) or {}
            for section, params in custom_settings.items():
                if section in settings_to_load:
                    for key, value in params.items():
                        settings_to_load[section][key] = value
                else:
                    settings_to_load[section] = params
        else:
            settings_to_load = default_settings or {}
    else:
        settings_to_load = default_settings or {}

    return settings_to_load


initial_settings = get_initial_settings()

# FastAPI app
app = FastAPI(title="Combined UVR VR ARCH and MDX23C Separation Server")

# Mount static files under /outputs for client downloads (Server B)
app.mount("/outputs", StaticFiles(directory=out_dir), name="outputs")

# --------- Server A part (VR ARCH) ---------


class SeparationRequest(BaseModel):
    audio_path: str  # local file path to input audio
    output_format: str = "wav"
    window_size: int = 512
    aggression: int = 5
    tta: bool = True
    post_process: bool = False
    post_process_threshold: float = 0.2
    high_end_process: bool = False
    batch_size: int = 1
    normalization_threshold: float = 0.9
    amplification_threshold: float = 0.7
    single_stem: Optional[str] = ""  # empty or stem name


@app.post("/separate")
def separate_audio(req: SeparationRequest):
    audio_path = req.audio_path

    if not os.path.isfile(audio_path):
        raise HTTPException(status_code=400, detail=f"Audio file '{audio_path}' does not exist")

    os.makedirs(out_dir, exist_ok=True)

    try:
        logging.info("Initializing Separator for VR ARCH model...")
        separator = Separator(
            log_level=logging.WARNING,
            model_file_dir=models_dir,
            output_dir=out_dir,
            output_format=req.output_format,
            use_autocast=use_autocast,
            normalization_threshold=req.normalization_threshold,
            amplification_threshold=req.amplification_threshold,
            output_single_stem=req.single_stem,
            vr_params={
                "batch_size": req.batch_size,
                "window_size": req.window_size,
                "aggression": req.aggression,
                "enable_tta": req.tta,
                "enable_post_process": req.post_process,
                "post_process_threshold": req.post_process_threshold,
                "high_end_process": req.high_end_process,
            }
        )

        logging.info(f"Loading model {VRARCH_MODEL}...")
        separator.load_model(model_filename=VRARCH_MODEL)

        logging.info(f"Starting separation on audio: {audio_path} ...")
        separation = separator.separate(audio_path)

        stems_paths = [os.path.abspath(os.path.join(out_dir, fname)) for fname in separation]

        if req.single_stem and req.single_stem.strip():
            logging.info(f"Returning single stem at: {stems_paths[0]}")
            return {"stem1": stems_paths[0], "stem2": None}
        else:
            stem1 = stems_paths[0] if len(stems_paths) > 0 else None
            stem2 = stems_paths[1] if len(stems_paths) > 1 else None
            logging.info(f"Returning stems at: {stem1}, {stem2}")
            return {"stem1": stem1, "stem2": stem2}

    except Exception as e:
        logging.error(f"Error during VR ARCH separation: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"Separation failed: {e}")


@app.get("/")
def root():
    return {"message": "Combined UVR VR ARCH and MDX23C Separation Server - Use POST /separate or /mdx23c/separate to process audio files."}

# --------- Server B part (MDX23C) ---------


@track_presence("Performing MDXC Separation")
@spaces.GPU(duration=60)
def mdxc_separator(audio_path, model, out_format, segment_size, override_seg_size, overlap, batch_size, norm_thresh, amp_thresh, single_stem):
    model_path = os.path.join(models_dir, model)
    if not os.path.exists(model_path):
        raise FileNotFoundError(f"Model '{model}' not found in '{models_dir}'")

    separator = Separator(
        log_level=logging.WARNING,
        model_file_dir=models_dir,
        output_dir=out_dir,
        output_format=out_format,
        use_autocast=use_autocast,
        normalization_threshold=norm_thresh,
        amplification_threshold=amp_thresh,
        output_single_stem=single_stem,
        mdxc_params={
            "segment_size": segment_size,
            "override_model_segment_size": override_seg_size,
            "batch_size": batch_size,
            "overlap": overlap,
        }
    )

    separator.load_model(model_filename=model)
    separation = separator.separate(audio_path)
    stems = [os.path.join(out_dir, file_name) for file_name in separation]
    if single_stem.strip():
        return stems[0], None
    return stems[0], stems[1]


def download_audio(url, output_dir="ytdl"):
    os.makedirs(output_dir, exist_ok=True)
    ydl_opts = {
        'format': 'bestaudio/best',
        'postprocessors': [{
            'key': 'FFmpegExtractAudio',
            'preferredcodec': 'wav',
            'preferredquality': '32',
        }],
        'outtmpl': os.path.join(output_dir, '%(title)s.%(ext)s'),
        'postprocessor_args': [
            '-acodec', 'pcm_f32le'
        ],
        'quiet': True,
        'no_warnings': True,
    }
    try:
        with yt_dlp.YoutubeDL(ydl_opts) as ydl:
            info = ydl.extract_info(url, download=False)
            video_title = info['title']
            ydl.download([url])
            file_path = os.path.join(output_dir, f"{video_title}.wav")
            if os.path.exists(file_path):
                return os.path.abspath(file_path)
            else:
                raise Exception("Audio conversion failed or output file missing.")
    except Exception as e:
        raise Exception(f"Error extracting audio with yt-dlp: {str(e)}")


@app.post("/mdx23c/separate")
async def separate_mdx23c(
    request: Request,
    audio: UploadFile = File(...),
    model: str = Form(...),
    output_format: str = Form(...),
    segment_size: int = Form(...),
    override_segment_size: bool = Form(...),
    overlap: int = Form(...),
    batch_size: int = Form(...),
    normalization_threshold: float = Form(...),
    amplification_threshold: float = Form(...),
    single_stem: str = Form("")
):
    input_path = os.path.join(temp_dir, audio.filename)
    try:
        with open(input_path, "wb") as f:
            f.write(await audio.read())

        stem1_path, stem2_path = mdxc_separator(
            audio_path=input_path,
            model=model,
            out_format=output_format,
            segment_size=segment_size,
            override_seg_size=override_segment_size,
            overlap=overlap,
            batch_size=batch_size,
            norm_thresh=normalization_threshold,
            amp_thresh=amplification_threshold,
            single_stem=single_stem,
        )

        base_url = str(request.base_url).rstrip("/")
        response = {"stem1": f"{base_url}/outputs/{os.path.basename(stem1_path)}"}
        if stem2_path:
            response["stem2"] = f"{base_url}/outputs/{os.path.basename(stem2_path)}"

        return JSONResponse(content=response)

    except Exception as e:
        logging.error(f"Error during MDX23C separation: {e}", exc_info=True)
        return JSONResponse(content={"error": str(e)}, status_code=500)
    finally:
        try:
            if os.path.exists(input_path):
                os.remove(input_path)
        except Exception:
            pass


@app.get("/mdx23c/models")
def list_models():
    return {"models": mdx23c_models}


@app.get("/mdx23c/download_audio")
def download_audio_endpoint(url: str):
    try:
        audio_path = download_audio(url)
        return {"audio_path": audio_path}
    except Exception as e:
        return JSONResponse(content={"error": str(e)}, status_code=500)


@app.post("/shutdown")
async def shutdown(request: Request):
    global server
    token = request.headers.get("X-Auth-Token")
    if token != SHUTDOWN_AUTH_TOKEN:
        raise HTTPException(status_code=401, detail="Unauthorized")

    if server is None:
        raise HTTPException(status_code=503, detail="Server instance not found")

    loop = asyncio.get_event_loop()

    def stop_server():
        logging.info("Shutdown initiated via /shutdown endpoint.")
        server.should_exit = True

    loop.call_soon_threadsafe(stop_server)

    await asyncio.sleep(0.1)  # allow shutdown process to start

    return {"status": "shutting down"}


@app.on_event("shutdown")
def on_shutdown():
    logging.info("Server shutdown event triggered.")


def main():
    global server

    # Only assign server in the actual reloader worker process to avoid 503s
    if os.environ.get("RUN_MAIN") == "true" or os.environ.get("RUN_MAIN") is None:
        port = 8123  # default port
        if "--port" in sys.argv:
            try:
                idx = sys.argv.index("--port")
                if idx + 1 < len(sys.argv):
                    port = int(sys.argv[idx + 1])
            except Exception:
                logging.warning("Invalid port argument - using default 8123")

        config = uvicorn.Config(app, host="0.0.0.0", port=port, log_level="info")
        server = uvicorn.Server(config)
        logging.info(f"Starting server on port {port}")
        server.run()
    else:
        logging.info("Reload parent process detected - server instance not started here.")


if __name__ == "__main__":
    main()

return script fixed in full tysm