diff --git a/official-templates/better-ai-launcher/app/app.py b/official-templates/better-ai-launcher/app/app.py index 82488d1..8114c79 100644 --- a/official-templates/better-ai-launcher/app/app.py +++ b/official-templates/better-ai-launcher/app/app.py @@ -121,25 +121,22 @@ def index(): 'status': status, 'installed': dirs_ok, 'install_status': install_status, - 'is_bcomfy': app_name == 'bcomfy' # Add this line + 'is_bcomfy': app_name == 'bcomfy' } + filebrowser_status = get_filebrowser_status() return render_template('index.html', - apps=app_configs, - app_status=app_status, - pod_id=RUNPOD_POD_ID, - RUNPOD_PUBLIC_IP=os.environ.get('RUNPOD_PUBLIC_IP'), - RUNPOD_TCP_PORT_22=os.environ.get('RUNPOD_TCP_PORT_22'), - - # lutzapps - CHANGE #2 - allow localhost Url for unsecure "http" and "ws" WebSockets protocol, - # according to LOCAL_DEBUG ENV var (used 3x in "index.html" changes) - enable_unsecure_localhost=os.environ.get('LOCAL_DEBUG'), - - settings=settings, - current_auth_method=current_auth_method, - ssh_password=ssh_password, - ssh_password_status=ssh_password_status, - filebrowser_status=filebrowser_status) + apps=app_configs, + app_status=app_status, + pod_id=RUNPOD_POD_ID, + RUNPOD_PUBLIC_IP=os.environ.get('RUNPOD_PUBLIC_IP'), + RUNPOD_TCP_PORT_22=os.environ.get('RUNPOD_TCP_PORT_22'), + enable_unsecure_localhost=os.environ.get('LOCAL_DEBUG'), + settings=settings, + current_auth_method=current_auth_method, + ssh_password=ssh_password, + ssh_password_status=ssh_password_status, + filebrowser_status=filebrowser_status) @app.route('/start/') def start_app(app_name): @@ -520,35 +517,52 @@ def get_model_types_route(): @app.route('/download_model', methods=['POST']) def download_model_route(): - url = request.json.get('url') - model_name = request.json.get('model_name') - model_type = request.json.get('model_type') - civitai_token = request.json.get('civitai_token') or load_civitai_token() - hf_token = request.json.get('hf_token') or load_huggingface_token() # lutzapps - added HF_TOKEN ENV var support - version_id = request.json.get('version_id') - file_index = request.json.get('file_index') - - is_civitai, _, _, _ = check_civitai_url(url) - is_huggingface, _, _, _, _ = check_huggingface_url(url) # TODO: double call - - if not (is_civitai or is_huggingface): - return jsonify({'status': 'error', 'message': 'Unsupported URL. Please use Civitai or Hugging Face URLs.'}), 400 - - if is_civitai and not civitai_token: - return jsonify({'status': 'error', 'message': 'Civitai token is required for downloading from Civitai.'}), 400 - try: - success, message = download_model(url, model_name, model_type, civitai_token, hf_token, version_id, file_index) - if success: - if isinstance(message, dict) and 'choice_required' in message: - return jsonify({'status': 'choice_required', 'data': message['choice_required']}) - return jsonify({'status': 'success', 'message': message}) - else: - return jsonify({'status': 'error', 'message': message}), 400 + data = request.json + url = data.get('url') + model_name = data.get('model_name') + model_type = data.get('model_type') + civitai_token = data.get('civitai_token') + hf_token = data.get('hf_token') + version_id = data.get('version_id') + file_index = data.get('file_index') + + # If no token provided in request, try to read from file + if not civitai_token: + try: + if os.path.exists('/workspace/.civitai_token'): + with open('/workspace/.civitai_token', 'r') as f: + token_data = json.load(f) + civitai_token = token_data.get('token') + except Exception as e: + app.logger.error(f"Error reading token file: {str(e)}") + + is_civitai, _, _, _ = check_civitai_url(url) + is_huggingface, _, _, _, _ = check_huggingface_url(url) + + if not (is_civitai or is_huggingface): + return jsonify({'status': 'error', 'message': 'Unsupported URL. Please use Civitai or Hugging Face URLs.'}), 400 + + if is_civitai and not civitai_token: + return jsonify({'status': 'error', 'message': 'Civitai token is required for downloading from Civitai.'}), 400 + + try: + success, message = download_model(url, model_name, model_type, civitai_token, hf_token, version_id, file_index) + if success: + if isinstance(message, dict) and 'choice_required' in message: + return jsonify({'status': 'choice_required', 'data': message['choice_required']}) + return jsonify({'status': 'success', 'message': message}) + else: + return jsonify({'status': 'error', 'message': message}), 400 + except Exception as e: + error_message = f"Model download error: {str(e)}\n{traceback.format_exc()}" + app.logger.error(error_message) + return jsonify({'status': 'error', 'message': error_message}), 500 + except Exception as e: - error_message = f"Model download error: {str(e)}\n{traceback.format_exc()}" + error_message = f"Error processing request: {str(e)}\n{traceback.format_exc()}" app.logger.error(error_message) - return jsonify({'status': 'error', 'message': error_message}), 500 + return jsonify({'status': 'error', 'message': error_message}), 400 @app.route('/get_model_folders') def get_model_folders(): diff --git a/official-templates/better-ai-launcher/app/templates/index.html b/official-templates/better-ai-launcher/app/templates/index.html index 167d9eb..b984366 100644 --- a/official-templates/better-ai-launcher/app/templates/index.html +++ b/official-templates/better-ai-launcher/app/templates/index.html @@ -45,15 +45,15 @@ font-size: 1.4em; } .button-group { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 10px; + display: flex; + flex-direction: row; + gap: 8px; margin-bottom: 10px; } button { border: none; color: white; - padding: 12px 15px; + padding: 8px 15px; text-align: center; text-decoration: none; display: inline-flex; @@ -63,51 +63,36 @@ cursor: pointer; border-radius: 5px; transition: background-color 0.3s, transform 0.1s; - width: 100%; - } - button:hover:not(:disabled) { - transform: translateY(-2px); } button:disabled { - background-color: #cccccc; + opacity: 0.6; cursor: not-allowed; } button i { - margin-right: 5px; - } - .start-button { - background-color: #4CAF50; - } - .start-button:hover:not(:disabled) { - background-color: #45a049; - } - .stop-button { - background-color: #f44336; - } - .stop-button:hover:not(:disabled) { - background-color: #da190b; - } - .log-button { - background-color: #008CBA; - } - .log-button:hover:not(:disabled) { - background-color: #007aa3; - } - .open-button { - background-color: #FF9800; - } - .open-button:hover:not(:disabled) { - background-color: #e68a00; + margin-right: 8px; } + .start-button { background-color: #4CAF50; } + .stop-button { background-color: #f44336; } + .log-button { background-color: #008CBA; } + .open-button { background-color: #FF9800; } + .force-kill-button { background-color: #d9534f; } + .install-button { + width: 100%; + margin-top: 10px; background-color: #9C27B0; - grid-column: span 2; } .install-button:hover { background-color: #7B1FA2; } - .force-kill-button { - background-color: #d9534f; + .log-button:hover:not(:disabled) { + background-color: #007aa3; + } + .open-button:hover:not(:disabled) { + background-color: #e68a00; + } + .install-button:hover { + background-color: #7B1FA2; } .force-kill-button:hover:not(:disabled) { background-color: #c9302c; @@ -456,7 +441,8 @@ } .fix-custom-nodes-button { - background-color: #9C27B0; + width: 100%; /* Keep fix custom nodes button full width */ + margin-top: 10px; } .fix-custom-nodes-button:hover { @@ -1061,13 +1047,6 @@ flex-direction: column; } - #logs-container { - flex: 1; - display: flex; - flex-direction: column; - position: relative; - } - #logs { flex: 1; @@ -1963,10 +1942,8 @@ overflow-y: auto; } - .model-downloader-section, - .right-column { - height: auto; - max-height: calc(50vh - 100px); + .model-folders-container { + max-height: 600px; } } @@ -2336,6 +2313,63 @@ width: 100%; box-sizing: border-box; } + + /* Example URLs styling */ + .example-urls { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 15px; + margin-top: 20px; + padding: 15px; + background-color: #333; + border-radius: 10px; + } + + .example-url { + background-color: #2a2a2a; + padding: 15px; + border-radius: 8px; + transition: transform 0.2s, box-shadow 0.2s; + } + + .example-url:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + } + + .example-label { + display: block; + font-weight: bold; + color: #4CAF50; + margin-bottom: 8px; + font-size: 0.9em; + text-transform: uppercase; + } + + .example-link { + display: block; + color: #fff; + text-decoration: none; + word-break: break-all; + font-family: monospace; + font-size: 0.9em; + padding: 8px; + background-color: #1a1a1a; + border-radius: 4px; + border: 1px solid #444; + } + + .example-link:hover { + background-color: #2c2c2c; + border-color: #4CAF50; + } + + /* Responsive adjustments */ + @media (max-width: 1200px) { + .example-urls { + grid-template-columns: 1fr; + } + } @@ -2369,62 +2403,34 @@ {% for app_key, app_info in apps.items() %}

{{ app_info.name }}

- {% if app_status[app_key]['dirs_ok'] %} -
- - - - - - {% if app_status[app_key]['dirs_ok'] and app_status[app_key]['is_bcomfy'] %} - - {% endif %} -
- - {{ app_status[app_key]['status'] | capitalize }} - - {% else %} -

{{ app_status[app_key]['message'] }}

- {% if not app_status[app_key]['installed'] %} -
- -
-
-
Download Progress:
-
-
0%
-
-
-
-
Unpack Progress:
-
-
0%
-
-
-
- - -
-
-
-
-
- {% endif %} +
+ + + + + +
+ {% if app_key == 'bcomfy' and app_status[app_key]['installed'] %} + {% endif %} + {% if not app_status[app_key]['installed'] %} + + {% endif %} +
Status: {{ app_status[app_key]['status'] }}
{% endfor %} @@ -2537,27 +2543,27 @@
- Stable Diffusion: + Stable Diffusion Model:
- LoRA: + LoRA Model:
- VAE: + VAE Model:
- Upscaler: + Upscaler Model:
- Flux Dev: + Flux Dev Model:
- Flux Schnell: + Flux Schnell Model:
@@ -2589,568 +2595,646 @@
+ - socket.onmessage = function(event) { - try { - const data = JSON.parse(event.data); - console.log(`Data received from server:`, data); - - if (data.type === 'heartbeat') { - // Reset heartbeat timeout on receiving heartbeat response - resetHeartbeatTimeout(); - } else if (data.type === 'model_download_progress') { - updateModelDownloadProgress(data.data); - } else if (data.type === 'status_update') { - updateAppStatus(data.data); + +