mirror of
https://github.com/kodxana/madiator-docker-runpod.git
synced 2024-11-22 10:50:12 +01:00
ui changes and bugfixes
This commit is contained in:
parent
0124ebe43b
commit
ae6b5014ff
3 changed files with 1309 additions and 1134 deletions
|
@ -121,8 +121,9 @@ def index():
|
||||||
'status': status,
|
'status': status,
|
||||||
'installed': dirs_ok,
|
'installed': dirs_ok,
|
||||||
'install_status': install_status,
|
'install_status': install_status,
|
||||||
'is_bcomfy': app_name == 'bcomfy' # Add this line
|
'is_bcomfy': app_name == 'bcomfy'
|
||||||
}
|
}
|
||||||
|
|
||||||
filebrowser_status = get_filebrowser_status()
|
filebrowser_status = get_filebrowser_status()
|
||||||
return render_template('index.html',
|
return render_template('index.html',
|
||||||
apps=app_configs,
|
apps=app_configs,
|
||||||
|
@ -130,11 +131,7 @@ def index():
|
||||||
pod_id=RUNPOD_POD_ID,
|
pod_id=RUNPOD_POD_ID,
|
||||||
RUNPOD_PUBLIC_IP=os.environ.get('RUNPOD_PUBLIC_IP'),
|
RUNPOD_PUBLIC_IP=os.environ.get('RUNPOD_PUBLIC_IP'),
|
||||||
RUNPOD_TCP_PORT_22=os.environ.get('RUNPOD_TCP_PORT_22'),
|
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'),
|
enable_unsecure_localhost=os.environ.get('LOCAL_DEBUG'),
|
||||||
|
|
||||||
settings=settings,
|
settings=settings,
|
||||||
current_auth_method=current_auth_method,
|
current_auth_method=current_auth_method,
|
||||||
ssh_password=ssh_password,
|
ssh_password=ssh_password,
|
||||||
|
@ -520,16 +517,28 @@ def get_model_types_route():
|
||||||
|
|
||||||
@app.route('/download_model', methods=['POST'])
|
@app.route('/download_model', methods=['POST'])
|
||||||
def download_model_route():
|
def download_model_route():
|
||||||
url = request.json.get('url')
|
try:
|
||||||
model_name = request.json.get('model_name')
|
data = request.json
|
||||||
model_type = request.json.get('model_type')
|
url = data.get('url')
|
||||||
civitai_token = request.json.get('civitai_token') or load_civitai_token()
|
model_name = data.get('model_name')
|
||||||
hf_token = request.json.get('hf_token') or load_huggingface_token() # lutzapps - added HF_TOKEN ENV var support
|
model_type = data.get('model_type')
|
||||||
version_id = request.json.get('version_id')
|
civitai_token = data.get('civitai_token')
|
||||||
file_index = request.json.get('file_index')
|
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_civitai, _, _, _ = check_civitai_url(url)
|
||||||
is_huggingface, _, _, _, _ = check_huggingface_url(url) # TODO: double call
|
is_huggingface, _, _, _, _ = check_huggingface_url(url)
|
||||||
|
|
||||||
if not (is_civitai or is_huggingface):
|
if not (is_civitai or is_huggingface):
|
||||||
return jsonify({'status': 'error', 'message': 'Unsupported URL. Please use Civitai or Hugging Face URLs.'}), 400
|
return jsonify({'status': 'error', 'message': 'Unsupported URL. Please use Civitai or Hugging Face URLs.'}), 400
|
||||||
|
@ -550,6 +559,11 @@ def download_model_route():
|
||||||
app.logger.error(error_message)
|
app.logger.error(error_message)
|
||||||
return jsonify({'status': 'error', 'message': error_message}), 500
|
return jsonify({'status': 'error', 'message': error_message}), 500
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
error_message = f"Error processing request: {str(e)}\n{traceback.format_exc()}"
|
||||||
|
app.logger.error(error_message)
|
||||||
|
return jsonify({'status': 'error', 'message': error_message}), 400
|
||||||
|
|
||||||
@app.route('/get_model_folders')
|
@app.route('/get_model_folders')
|
||||||
def get_model_folders():
|
def get_model_folders():
|
||||||
folders = {}
|
folders = {}
|
||||||
|
|
|
@ -45,15 +45,15 @@
|
||||||
font-size: 1.4em;
|
font-size: 1.4em;
|
||||||
}
|
}
|
||||||
.button-group {
|
.button-group {
|
||||||
display: grid;
|
display: flex;
|
||||||
grid-template-columns: repeat(2, 1fr);
|
flex-direction: row;
|
||||||
gap: 10px;
|
gap: 8px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
button {
|
button {
|
||||||
border: none;
|
border: none;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 12px 15px;
|
padding: 8px 15px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
@ -63,51 +63,36 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
transition: background-color 0.3s, transform 0.1s;
|
transition: background-color 0.3s, transform 0.1s;
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
button:hover:not(:disabled) {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
}
|
||||||
button:disabled {
|
button:disabled {
|
||||||
background-color: #cccccc;
|
opacity: 0.6;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
button i {
|
button i {
|
||||||
margin-right: 5px;
|
margin-right: 8px;
|
||||||
}
|
|
||||||
.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;
|
|
||||||
}
|
}
|
||||||
|
.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 {
|
.install-button {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 10px;
|
||||||
background-color: #9C27B0;
|
background-color: #9C27B0;
|
||||||
grid-column: span 2;
|
|
||||||
}
|
}
|
||||||
.install-button:hover {
|
.install-button:hover {
|
||||||
background-color: #7B1FA2;
|
background-color: #7B1FA2;
|
||||||
}
|
}
|
||||||
.force-kill-button {
|
.log-button:hover:not(:disabled) {
|
||||||
background-color: #d9534f;
|
background-color: #007aa3;
|
||||||
|
}
|
||||||
|
.open-button:hover:not(:disabled) {
|
||||||
|
background-color: #e68a00;
|
||||||
|
}
|
||||||
|
.install-button:hover {
|
||||||
|
background-color: #7B1FA2;
|
||||||
}
|
}
|
||||||
.force-kill-button:hover:not(:disabled) {
|
.force-kill-button:hover:not(:disabled) {
|
||||||
background-color: #c9302c;
|
background-color: #c9302c;
|
||||||
|
@ -456,7 +441,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.fix-custom-nodes-button {
|
.fix-custom-nodes-button {
|
||||||
background-color: #9C27B0;
|
width: 100%; /* Keep fix custom nodes button full width */
|
||||||
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fix-custom-nodes-button:hover {
|
.fix-custom-nodes-button:hover {
|
||||||
|
@ -1061,13 +1047,6 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
#logs-container {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#logs {
|
#logs {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
@ -1963,10 +1942,8 @@
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.model-downloader-section,
|
.model-folders-container {
|
||||||
.right-column {
|
max-height: 600px;
|
||||||
height: auto;
|
|
||||||
max-height: calc(50vh - 100px);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2336,6 +2313,63 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -2369,7 +2403,6 @@
|
||||||
{% for app_key, app_info in apps.items() %}
|
{% for app_key, app_info in apps.items() %}
|
||||||
<div class="app">
|
<div class="app">
|
||||||
<h2>{{ app_info.name }}</h2>
|
<h2>{{ app_info.name }}</h2>
|
||||||
{% if app_status[app_key]['dirs_ok'] %}
|
|
||||||
<div class="button-group">
|
<div class="button-group">
|
||||||
<button onclick="startApp('{{ app_key }}')" id="start-{{ app_key }}" class="start-button" {% if app_status[app_key]['status'] == 'running' %}disabled{% endif %}>
|
<button onclick="startApp('{{ app_key }}')" id="start-{{ app_key }}" class="start-button" {% if app_status[app_key]['status'] == 'running' %}disabled{% endif %}>
|
||||||
<i class="fas fa-play"></i> Start
|
<i class="fas fa-play"></i> Start
|
||||||
|
@ -2384,47 +2417,20 @@
|
||||||
<i class="fas fa-external-link-alt"></i> Open App
|
<i class="fas fa-external-link-alt"></i> Open App
|
||||||
</button>
|
</button>
|
||||||
<button onclick="forceKillApp('{{ app_key }}')" id="force-kill-{{ app_key }}" class="force-kill-button">
|
<button onclick="forceKillApp('{{ app_key }}')" id="force-kill-{{ app_key }}" class="force-kill-button">
|
||||||
<i class="fas fa-skull-crossbones"></i> Force Kill
|
<i class="fas fa-times-circle"></i> Force Kill
|
||||||
</button>
|
</button>
|
||||||
{% if app_status[app_key]['dirs_ok'] and app_status[app_key]['is_bcomfy'] %}
|
</div>
|
||||||
|
{% if app_key == 'bcomfy' and app_status[app_key]['installed'] %}
|
||||||
<button onclick="fixCustomNodes('{{ app_key }}')" id="fix-custom-nodes-{{ app_key }}" class="fix-custom-nodes-button">
|
<button onclick="fixCustomNodes('{{ app_key }}')" id="fix-custom-nodes-{{ app_key }}" class="fix-custom-nodes-button">
|
||||||
<i class="fas fa-wrench"></i> Fix Custom Nodes
|
<i class="fas fa-wrench"></i> Fix Custom Nodes
|
||||||
</button>
|
</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
|
||||||
<span id="status-{{ app_key }}" class="status status-{{ app_status[app_key]['status'] }}">
|
|
||||||
{{ app_status[app_key]['status'] | capitalize }}
|
|
||||||
</span>
|
|
||||||
{% else %}
|
|
||||||
<p class="error-message">{{ app_status[app_key]['message'] }}</p>
|
|
||||||
{% if not app_status[app_key]['installed'] %}
|
{% if not app_status[app_key]['installed'] %}
|
||||||
<div class="install-container">
|
<button onclick="installApp('{{ app_key }}')" id="install-{{ app_key }}" class="install-button">
|
||||||
<button onclick="installApp('{{ app_key }}')" id="install-{{ app_key }}" class="install-button" {% if app_status[app_key]['install_status']['status'] == 'in_progress' %}disabled{% endif %}>
|
<i class="fas fa-download"></i> Install
|
||||||
<i class="fas fa-download"></i> {% if app_status[app_key]['install_status']['status'] == 'in_progress' %}Installing...{% else %}Install {{ app_info.name }}{% endif %}
|
|
||||||
</button>
|
</button>
|
||||||
<div id="install-progress-{{ app_key }}" class="install-progress" {% if app_status[app_key]['install_status']['status'] == 'in_progress' %}style="display: block;"{% endif %}>
|
|
||||||
<div class="progress-container">
|
|
||||||
<div class="progress-label">Download Progress:</div>
|
|
||||||
<div class="progress-bar">
|
|
||||||
<div class="progress-bar-fill download-progress" style="width: 0%">0%</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="progress-container">
|
|
||||||
<div class="progress-label">Unpack Progress:</div>
|
|
||||||
<div class="progress-bar">
|
|
||||||
<div class="progress-bar-fill unpack-progress" style="width: 0%">0%</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="download-info">
|
|
||||||
<span class="download-speed"></span>
|
|
||||||
<span class="download-eta"></span>
|
|
||||||
</div><!-- 4 lutzapps - remove a typo "4" -->
|
|
||||||
<div class="install-stage"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="install-logs-{{ app_key }}" class="install-logs"></div>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<div class="status">Status: {{ app_status[app_key]['status'] }}</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -2537,27 +2543,27 @@
|
||||||
<div id="example-urls" class="inner-tab-content active">
|
<div id="example-urls" class="inner-tab-content active">
|
||||||
<div class="example-urls">
|
<div class="example-urls">
|
||||||
<div class="example-url">
|
<div class="example-url">
|
||||||
<span class="example-label">Stable Diffusion:</span>
|
<span class="example-label">Stable Diffusion Model:</span>
|
||||||
<a href="#" class="example-link" id="example-sd" onclick="useInModelDownloader('ckpt', this.textContent); return false;"></a>
|
<a href="#" class="example-link" id="example-sd" onclick="useInModelDownloader('ckpt', this.textContent); return false;"></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="example-url">
|
<div class="example-url">
|
||||||
<span class="example-label">LoRA:</span>
|
<span class="example-label">LoRA Model:</span>
|
||||||
<a href="#" class="example-link" id="example-lora" onclick="useInModelDownloader('loras', this.textContent); return false;"></a>
|
<a href="#" class="example-link" id="example-lora" onclick="useInModelDownloader('loras', this.textContent); return false;"></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="example-url">
|
<div class="example-url">
|
||||||
<span class="example-label">VAE:</span>
|
<span class="example-label">VAE Model:</span>
|
||||||
<a href="#" class="example-link" id="example-vae" onclick="useInModelDownloader('vae', this.textContent); return false;"></a>
|
<a href="#" class="example-link" id="example-vae" onclick="useInModelDownloader('vae', this.textContent); return false;"></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="example-url">
|
<div class="example-url">
|
||||||
<span class="example-label">Upscaler:</span>
|
<span class="example-label">Upscaler Model:</span>
|
||||||
<a href="#" class="example-link" id="example-upscaler" onclick="useInModelDownloader('upscale_models', this.textContent); return false;"></a>
|
<a href="#" class="example-link" id="example-upscaler" onclick="useInModelDownloader('upscale_models', this.textContent); return false;"></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="example-url">
|
<div class="example-url">
|
||||||
<span class="example-label">Flux Dev:</span>
|
<span class="example-label">Flux Dev Model:</span>
|
||||||
<a href="#" class="example-link" id="example-flux-dev" onclick="useInModelDownloader('unet', this.textContent); return false;"></a>
|
<a href="#" class="example-link" id="example-flux-dev" onclick="useInModelDownloader('unet', this.textContent); return false;"></a>
|
||||||
</div>
|
</div>
|
||||||
<div class="example-url">
|
<div class="example-url">
|
||||||
<span class="example-label">Flux Schnell:</span>
|
<span class="example-label">Flux Schnell Model:</span>
|
||||||
<a href="#" class="example-link" id="example-flux-schnell" onclick="useInModelDownloader('unet', this.textContent); return false;"></a>
|
<a href="#" class="example-link" id="example-flux-schnell" onclick="useInModelDownloader('unet', this.textContent); return false;"></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2589,6 +2595,20 @@
|
||||||
</audio>
|
</audio>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Add this near the top of the body, before other scripts -->
|
||||||
|
<script>
|
||||||
|
// Make app_configs available to JavaScript
|
||||||
|
const app_configs = {
|
||||||
|
{% for app_key, app_info in apps.items() %}
|
||||||
|
"{{ app_key }}": {
|
||||||
|
"name": "{{ app_info.name }}",
|
||||||
|
"port": {{ app_info.port }}
|
||||||
|
},
|
||||||
|
{% endfor %}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Rest of your JavaScript code -->
|
||||||
<script>
|
<script>
|
||||||
const appStatuses = {};
|
const appStatuses = {};
|
||||||
let currentLogApp = null;
|
let currentLogApp = null;
|
||||||
|
@ -2806,25 +2826,96 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
async function startApp(appKey) {
|
async function startApp(appKey) {
|
||||||
|
try {
|
||||||
const response = await fetch(`/start/${appKey}`);
|
const response = await fetch(`/start/${appKey}`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (data.status === 'started') {
|
if (data.status === 'started' || data.status === 'already_running') {
|
||||||
updateAppStatus(appKey, 'running');
|
updateAppStatus({ [appKey]: 'running' });
|
||||||
viewLogs(appKey, appConfigs[appKey].name);
|
// Get the app name from app_configs and show logs
|
||||||
} else if (data.status === 'error') {
|
const appName = app_configs[appKey].name;
|
||||||
alert(data.message);
|
// Switch to logs view
|
||||||
|
currentLogApp = appKey;
|
||||||
|
currentLogAppName = appName;
|
||||||
|
document.getElementById('currentAppName').textContent = `Logs - ${appName}`;
|
||||||
|
document.getElementById('downloadLogsBtn').style.display = 'flex';
|
||||||
|
document.getElementById('logs').textContent = ''; // Clear existing logs
|
||||||
|
updateLogs(); // Start updating logs
|
||||||
|
|
||||||
|
// Make sure logs section is visible
|
||||||
|
const logsSection = document.querySelector('.logs-section');
|
||||||
|
if (logsSection) {
|
||||||
|
logsSection.style.display = 'flex';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAppStatus(data) {
|
||||||
|
for (const [appKey, status] of Object.entries(data)) {
|
||||||
|
// Update status text
|
||||||
|
const statusDiv = document.querySelector(`#apps .app:has(#start-${appKey}) .status`);
|
||||||
|
if (statusDiv) {
|
||||||
|
statusDiv.textContent = `Status: ${status}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update button states
|
||||||
|
const startButton = document.getElementById(`start-${appKey}`);
|
||||||
|
const stopButton = document.getElementById(`stop-${appKey}`);
|
||||||
|
const openButton = document.getElementById(`open-${appKey}`);
|
||||||
|
|
||||||
|
if (startButton) {
|
||||||
|
startButton.disabled = (status === 'running');
|
||||||
|
}
|
||||||
|
if (stopButton) {
|
||||||
|
stopButton.disabled = (status === 'stopped');
|
||||||
|
}
|
||||||
|
if (openButton) {
|
||||||
|
openButton.disabled = (status === 'stopped');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function viewLogs(appKey, appName) {
|
||||||
|
currentLogApp = appKey;
|
||||||
|
currentLogAppName = appName;
|
||||||
|
document.getElementById('currentAppName').textContent = `Logs - ${appName}`;
|
||||||
|
document.getElementById('downloadLogsBtn').style.display = 'flex';
|
||||||
|
updateLogs();
|
||||||
|
|
||||||
|
// Make sure logs section is visible
|
||||||
|
const logsSection = document.querySelector('.logs-section');
|
||||||
|
if (logsSection) {
|
||||||
|
logsSection.style.display = 'flex';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function stopApp(appKey) {
|
async function stopApp(appKey) {
|
||||||
|
try {
|
||||||
const response = await fetch(`/stop/${appKey}`);
|
const response = await fetch(`/stop/${appKey}`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (data.status === 'stopped') {
|
if (data.status === 'stopped' || data.status === 'already_stopped') {
|
||||||
updateAppStatus(appKey, 'stopped');
|
// Update button states
|
||||||
|
const startButton = document.getElementById(`start-${appKey}`);
|
||||||
|
const stopButton = document.getElementById(`stop-${appKey}`);
|
||||||
|
const openButton = document.getElementById(`open-${appKey}`);
|
||||||
|
|
||||||
|
if (startButton) startButton.disabled = false; // Enable start button
|
||||||
|
if (stopButton) stopButton.disabled = true; // Disable stop button
|
||||||
|
if (openButton) openButton.disabled = true; // Disable open button
|
||||||
|
|
||||||
|
// Update status text
|
||||||
|
const statusDiv = document.querySelector(`#apps .app:has(#start-${appKey}) .status`);
|
||||||
|
if (statusDiv) {
|
||||||
|
statusDiv.textContent = 'Status: stopped';
|
||||||
|
}
|
||||||
} else if (data.status === 'error') {
|
} else if (data.status === 'error') {
|
||||||
alert(data.message);
|
alert(data.message);
|
||||||
}
|
}
|
||||||
await updateStatus();
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openApp(appKey, port) {
|
async function openApp(appKey, port) {
|
||||||
|
@ -2878,14 +2969,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function viewLogs(appKey, appName) {
|
|
||||||
currentLogApp = appKey;
|
|
||||||
currentLogAppName = appName;
|
|
||||||
document.getElementById('currentAppName').textContent = `Logs: ${appName}`;
|
|
||||||
updateLogs();
|
|
||||||
document.getElementById('downloadLogsBtn').style.display = 'flex';
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateLogs() {
|
function updateLogs() {
|
||||||
if (currentLogApp) {
|
if (currentLogApp) {
|
||||||
fetch(`/logs/${currentLogApp}`)
|
fetch(`/logs/${currentLogApp}`)
|
||||||
|
@ -3150,6 +3233,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function showVersionChoiceDialog(data, url, modelName, modelType, civitaiToken, hfToken) {
|
function showVersionChoiceDialog(data, url, modelName, modelType, civitaiToken, hfToken) {
|
||||||
const dialogContent = `
|
const dialogContent = `
|
||||||
<h3>Multiple versions available. Please choose one:</h3>
|
<h3>Multiple versions available. Please choose one:</h3>
|
||||||
|
@ -3236,7 +3320,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveCivitaiToken() {
|
async function saveCivitaiToken() {
|
||||||
const token = document.getElementById('civitaiTokenSave').value;
|
// Get token from the main Civitai token input field
|
||||||
|
const token = document.getElementById('civitaiToken').value;
|
||||||
|
if (!token) {
|
||||||
|
alert('Please enter a Civitai API token');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/save_civitai_token', {
|
const response = await fetch('/save_civitai_token', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
@ -3247,15 +3337,16 @@
|
||||||
});
|
});
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
if (result.status === 'success') {
|
if (result.status === 'success') {
|
||||||
document.getElementById('civitaiTokenStatus').textContent = 'Token saved successfully.';
|
// Update both token fields to show it's saved
|
||||||
document.getElementById('civitaiToken').value = '********';
|
document.getElementById('civitaiToken').value = '********';
|
||||||
document.getElementById('civitaiTokenSave').value = '********';
|
document.getElementById('civitaiTokenSave').value = '********';
|
||||||
|
alert('Civitai token saved successfully.');
|
||||||
} else {
|
} else {
|
||||||
document.getElementById('civitaiTokenStatus').textContent = `Error: ${result.message}`;
|
alert(`Error: ${result.message}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
document.getElementById('civitaiTokenStatus').textContent = `Error: ${error.message}`;
|
alert(`Error: ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3285,15 +3376,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatSize(sizeInBytes) {
|
function formatSize(bytes) {
|
||||||
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
if (!bytes || isNaN(bytes)) return '0 B';
|
||||||
let size = sizeInBytes;
|
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||||
let unitIndex = 0;
|
const i = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), sizes.length - 1);
|
||||||
while (size >= 1024 && unitIndex < units.length - 1) {
|
const size = bytes / Math.pow(1024, i);
|
||||||
size /= 1024;
|
return `${size.toFixed(2)} ${sizes[i]}`;
|
||||||
unitIndex++;
|
|
||||||
}
|
|
||||||
return `${size.toFixed(2)} ${units[unitIndex]}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatTime(seconds) {
|
function formatTime(seconds) {
|
||||||
|
|
|
@ -14,7 +14,7 @@ import time
|
||||||
import datetime
|
import datetime
|
||||||
import shutil
|
import shutil
|
||||||
from utils.app_configs import (app_configs, DEBUG_SETTINGS, pretty_dict, init_app_configs, init_debug_settings, write_debug_setting, ensure_kohya_local_venv_is_symlinked)
|
from utils.app_configs import (app_configs, DEBUG_SETTINGS, pretty_dict, init_app_configs, init_debug_settings, write_debug_setting, ensure_kohya_local_venv_is_symlinked)
|
||||||
from utils.model_utils import (get_sha256_hash_from_file)
|
from utils.model_utils import (get_sha256_hash_from_file, download_civitai_model)
|
||||||
|
|
||||||
INSTALL_STATUS_FILE = '/tmp/install_status.json'
|
INSTALL_STATUS_FILE = '/tmp/install_status.json'
|
||||||
|
|
||||||
|
@ -701,6 +701,11 @@ def download_and_unpack_venv_v2(app_name:str, app_configs:dict, send_websocket_m
|
||||||
send_websocket_message('install_progress', {'app_name': app_name, 'percentage': 100, 'stage': 'Unpacking Complete'})
|
send_websocket_message('install_progress', {'app_name': app_name, 'percentage': 100, 'stage': 'Unpacking Complete'})
|
||||||
send_websocket_message('install_log', {'app_name': app_name, 'log': 'Unpacking complete. Proceeding to clone repository...'})
|
send_websocket_message('install_log', {'app_name': app_name, 'log': 'Unpacking complete. Proceeding to clone repository...'})
|
||||||
|
|
||||||
|
# Get the app config from app_configs
|
||||||
|
app_config = app_configs.get(app_name)
|
||||||
|
if not app_config:
|
||||||
|
return False, f"App '{app_name}' not found in configurations."
|
||||||
|
|
||||||
# Clone the repository
|
# Clone the repository
|
||||||
success, message = clone_application(app_config, send_websocket_message)
|
success, message = clone_application(app_config, send_websocket_message)
|
||||||
if not success:
|
if not success:
|
||||||
|
@ -1074,3 +1079,71 @@ def install_app(app_name:str, app_configs:dict, send_websocket_message) -> tuple
|
||||||
return success, message
|
return success, message
|
||||||
else:
|
else:
|
||||||
return False, f"Unknown app: {app_name}"
|
return False, f"Unknown app: {app_name}"
|
||||||
|
|
||||||
|
def download_civitai_model(url, model_name, model_type, civitai_token=None, version_id=None, file_index=None):
|
||||||
|
try:
|
||||||
|
model_id = extract_model_id(url)
|
||||||
|
if not model_id:
|
||||||
|
raise ValueError("Could not extract model ID from URL")
|
||||||
|
|
||||||
|
# Use provided token or 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:
|
||||||
|
print(f"Error reading token file: {str(e)}")
|
||||||
|
|
||||||
|
headers = {"Authorization": f"Bearer {civitai_token}"} if civitai_token else {}
|
||||||
|
|
||||||
|
# Get model info
|
||||||
|
model_info_url = f"https://civitai.com/api/v1/models/{model_id}"
|
||||||
|
response = requests.get(model_info_url, headers=headers)
|
||||||
|
if response.status_code != 200:
|
||||||
|
raise Exception(f"Failed to get model info: {response.text}")
|
||||||
|
|
||||||
|
model_info = response.json()
|
||||||
|
versions = model_info.get('modelVersions', [])
|
||||||
|
|
||||||
|
if not versions:
|
||||||
|
raise Exception("No versions found for this model")
|
||||||
|
|
||||||
|
# Handle version selection - return early if selection needed
|
||||||
|
if version_id is None and len(versions) > 1:
|
||||||
|
return {
|
||||||
|
'type': 'version',
|
||||||
|
'versions': versions,
|
||||||
|
'civitai_token': civitai_token
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get the selected version
|
||||||
|
selected_version = versions[0] if version_id is None else next(
|
||||||
|
(v for v in versions if str(v['id']) == str(version_id)), None)
|
||||||
|
|
||||||
|
if not selected_version:
|
||||||
|
raise Exception("Specified version not found")
|
||||||
|
|
||||||
|
# Handle file selection - return early if selection needed
|
||||||
|
files = selected_version.get('files', [])
|
||||||
|
if not files:
|
||||||
|
raise Exception("No files found for this version")
|
||||||
|
|
||||||
|
if file_index is None and len(files) > 1:
|
||||||
|
return {
|
||||||
|
'type': 'file',
|
||||||
|
'files': files,
|
||||||
|
'version_id': selected_version['id'],
|
||||||
|
'civitai_token': civitai_token
|
||||||
|
}
|
||||||
|
|
||||||
|
# Select the file
|
||||||
|
selected_file = files[file_index if file_index is not None else 0]
|
||||||
|
if not selected_file:
|
||||||
|
raise Exception("No file found")
|
||||||
|
|
||||||
|
return selected_file
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception(f"Exception downloading from CivitAI: {str(e)}")
|
||||||
|
|
Loading…
Reference in a new issue