Add persistent default temp directory setting with web UI
- Add settings.json persistence (load/save) in backend - Add GET/POST /api/settings endpoints for default_temp_dir - Add Default Temp Directory config UI in Temporary Storage section - Auto-fallback: jobs use saved default if no per-job temp_dir set - Per-job Advanced Options placeholder syncs with saved default - Add settings.json to .gitignore for per-machine config
This commit is contained in:
@@ -12,6 +12,7 @@ temp
|
|||||||
server.log
|
server.log
|
||||||
.uvicorn.pid
|
.uvicorn.pid
|
||||||
jobs.json
|
jobs.json
|
||||||
|
settings.json
|
||||||
|
|
||||||
# OS
|
# OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
+51
-2
@@ -86,6 +86,29 @@ main_loop = None
|
|||||||
|
|
||||||
JOBS_FILE = os.path.join(upscaler.BASE_DIR, "jobs.json")
|
JOBS_FILE = os.path.join(upscaler.BASE_DIR, "jobs.json")
|
||||||
|
|
||||||
|
SETTINGS_FILE = os.path.join(upscaler.BASE_DIR, "settings.json")
|
||||||
|
|
||||||
|
def load_settings() -> dict:
|
||||||
|
"""Load persistent app settings from settings.json"""
|
||||||
|
defaults = {"default_temp_dir": ""}
|
||||||
|
if os.path.exists(SETTINGS_FILE):
|
||||||
|
try:
|
||||||
|
with open(SETTINGS_FILE, "r") as f:
|
||||||
|
saved = json.load(f)
|
||||||
|
defaults.update(saved)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error loading settings: {e}")
|
||||||
|
return defaults
|
||||||
|
|
||||||
|
def save_settings(settings: dict):
|
||||||
|
"""Save persistent app settings to settings.json"""
|
||||||
|
try:
|
||||||
|
with open(SETTINGS_FILE, "w") as f:
|
||||||
|
json.dump(settings, f, indent=4)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error saving settings: {e}")
|
||||||
|
|
||||||
|
|
||||||
def load_jobs_db():
|
def load_jobs_db():
|
||||||
global jobs_db
|
global jobs_db
|
||||||
if os.path.exists(JOBS_FILE):
|
if os.path.exists(JOBS_FILE):
|
||||||
@@ -452,7 +475,7 @@ def start_upscale(req: StartUpscaleRequest):
|
|||||||
ai_face_restoration=req.ai_face_restoration,
|
ai_face_restoration=req.ai_face_restoration,
|
||||||
ai_rife_interpolation=req.ai_rife_interpolation,
|
ai_rife_interpolation=req.ai_rife_interpolation,
|
||||||
ai_audio_denoise=req.ai_audio_denoise,
|
ai_audio_denoise=req.ai_audio_denoise,
|
||||||
temp_dir=req.temp_dir
|
temp_dir=req.temp_dir or load_settings().get("default_temp_dir") or None
|
||||||
)
|
)
|
||||||
|
|
||||||
jobs_db[job_id] = job
|
jobs_db[job_id] = job
|
||||||
@@ -527,7 +550,7 @@ def start_upscale_batch(req: StartBatchUpscaleRequest):
|
|||||||
ai_face_restoration=req.ai_face_restoration,
|
ai_face_restoration=req.ai_face_restoration,
|
||||||
ai_rife_interpolation=req.ai_rife_interpolation,
|
ai_rife_interpolation=req.ai_rife_interpolation,
|
||||||
ai_audio_denoise=req.ai_audio_denoise,
|
ai_audio_denoise=req.ai_audio_denoise,
|
||||||
temp_dir=req.temp_dir
|
temp_dir=req.temp_dir or load_settings().get("default_temp_dir") or None
|
||||||
)
|
)
|
||||||
jobs_db[job_id] = job
|
jobs_db[job_id] = job
|
||||||
job_queue.put(job_id)
|
job_queue.put(job_id)
|
||||||
@@ -1064,6 +1087,32 @@ def setup_webhook(req: WebhookSetupRequest):
|
|||||||
global_webhook_url = req.url
|
global_webhook_url = req.url
|
||||||
return {"status": "success", "webhook_url": global_webhook_url}
|
return {"status": "success", "webhook_url": global_webhook_url}
|
||||||
|
|
||||||
|
# ─── Settings Endpoints ─────────────────────────────────────────────
|
||||||
|
class UpdateSettingsRequest(BaseModel):
|
||||||
|
default_temp_dir: str = ""
|
||||||
|
|
||||||
|
@app.get("/api/settings")
|
||||||
|
def get_settings():
|
||||||
|
"""Get persistent app settings"""
|
||||||
|
settings = load_settings()
|
||||||
|
return settings
|
||||||
|
|
||||||
|
@app.post("/api/settings")
|
||||||
|
def update_settings(req: UpdateSettingsRequest):
|
||||||
|
"""Update persistent app settings"""
|
||||||
|
settings = load_settings()
|
||||||
|
# Validate the directory path if provided
|
||||||
|
new_dir = req.default_temp_dir.strip()
|
||||||
|
if new_dir:
|
||||||
|
# Ensure the parent directory exists or can be created
|
||||||
|
try:
|
||||||
|
os.makedirs(new_dir, exist_ok=True)
|
||||||
|
except OSError as e:
|
||||||
|
raise HTTPException(status_code=400, detail=f"Invalid directory path: {e}")
|
||||||
|
settings["default_temp_dir"] = new_dir
|
||||||
|
save_settings(settings)
|
||||||
|
return {"status": "saved", "settings": settings}
|
||||||
|
|
||||||
@app.get("/api/uploads/{filename}")
|
@app.get("/api/uploads/{filename}")
|
||||||
def get_upload_file(filename: str):
|
def get_upload_file(filename: str):
|
||||||
file_path = os.path.join(upscaler.UPLOAD_DIR, filename)
|
file_path = os.path.join(upscaler.UPLOAD_DIR, filename)
|
||||||
|
|||||||
@@ -2122,6 +2122,55 @@ async function initializeApp() {
|
|||||||
|
|
||||||
// Load initial temp info
|
// Load initial temp info
|
||||||
loadTempInfo();
|
loadTempInfo();
|
||||||
|
|
||||||
|
// Default temp directory setting
|
||||||
|
const saveTempDirBtn = document.getElementById("save-temp-dir-btn");
|
||||||
|
const defaultTempDirInput = document.getElementById("default-temp-dir-input");
|
||||||
|
const tempDirStatus = document.getElementById("temp-dir-status");
|
||||||
|
|
||||||
|
// Load saved default temp dir on init
|
||||||
|
loadDefaultTempDir();
|
||||||
|
|
||||||
|
if (saveTempDirBtn) {
|
||||||
|
saveTempDirBtn.addEventListener("click", async () => {
|
||||||
|
const newDir = defaultTempDirInput ? defaultTempDirInput.value.trim() : "";
|
||||||
|
tempDirStatus.textContent = "";
|
||||||
|
tempDirStatus.style.color = "";
|
||||||
|
saveTempDirBtn.disabled = true;
|
||||||
|
saveTempDirBtn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i> Saving...';
|
||||||
|
try {
|
||||||
|
const res = await fetch("/api/settings", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ default_temp_dir: newDir })
|
||||||
|
});
|
||||||
|
if (res.ok) {
|
||||||
|
tempDirStatus.style.color = "var(--success, #4caf50)";
|
||||||
|
tempDirStatus.textContent = newDir
|
||||||
|
? `\u2713 Default temp directory saved: ${newDir}`
|
||||||
|
: "\u2713 Reset to project default temp/ directory.";
|
||||||
|
// Also update the per-job input placeholder/value
|
||||||
|
const perJobInput = document.getElementById("temp-dir-input");
|
||||||
|
if (perJobInput && !perJobInput.value.trim()) {
|
||||||
|
perJobInput.placeholder = newDir
|
||||||
|
? `Default: ${newDir} (override here)`
|
||||||
|
: "e.g. /mnt/fast_ssd/temp (Leave empty for default temp/)";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const errData = await res.json().catch(() => ({}));
|
||||||
|
tempDirStatus.style.color = "var(--danger, #f44336)";
|
||||||
|
tempDirStatus.textContent = `\u2717 ${errData.detail || "Failed to save setting."}`;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
tempDirStatus.style.color = "var(--danger, #f44336)";
|
||||||
|
tempDirStatus.textContent = "\u2717 Network error saving setting.";
|
||||||
|
console.error(err);
|
||||||
|
} finally {
|
||||||
|
saveTempDirBtn.disabled = false;
|
||||||
|
saveTempDirBtn.innerHTML = '<i class="fa-solid fa-floppy-disk"></i> Save';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadTempInfo() {
|
async function loadTempInfo() {
|
||||||
@@ -2220,4 +2269,22 @@ async function loadTempInfo() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadDefaultTempDir() {
|
||||||
|
try {
|
||||||
|
const res = await fetch("/api/settings");
|
||||||
|
if (!res.ok) return;
|
||||||
|
const settings = await res.json();
|
||||||
|
const savedDir = settings.default_temp_dir || "";
|
||||||
|
const defaultInput = document.getElementById("default-temp-dir-input");
|
||||||
|
if (defaultInput) defaultInput.value = savedDir;
|
||||||
|
// Update per-job input placeholder
|
||||||
|
const perJobInput = document.getElementById("temp-dir-input");
|
||||||
|
if (perJobInput && savedDir && !perJobInput.value.trim()) {
|
||||||
|
perJobInput.placeholder = `Default: ${savedDir} (override here)`;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Failed to load settings:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
initializeApp();
|
initializeApp();
|
||||||
|
|||||||
@@ -581,6 +581,26 @@
|
|||||||
Monitor temporary image frames created during upscaling. Running jobs cannot be cleared.
|
Monitor temporary image frames created during upscaling. Running jobs cannot be cleared.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<!-- Default Temp Directory Setting -->
|
||||||
|
<div class="temp-dir-setting" style="margin-bottom: 1.5rem; padding: 1.25rem; border-radius: 12px; background: rgba(255,255,255,0.02); border: 1px solid var(--border-color);">
|
||||||
|
<div style="display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.75rem;">
|
||||||
|
<i class="fa-solid fa-folder-gear" style="color: var(--primary); font-size: 1.1rem;"></i>
|
||||||
|
<span style="font-weight: 600; font-size: 0.95rem;">Default Temp Directory</span>
|
||||||
|
</div>
|
||||||
|
<p style="font-size: 0.82rem; color: var(--text-muted); margin-bottom: 0.75rem;">
|
||||||
|
Set a persistent default directory for all upscale job temp files. This is applied automatically unless overridden per-job in Advanced Options. Use a fast SSD or a drive with plenty of space.
|
||||||
|
</p>
|
||||||
|
<div style="display: flex; gap: 0.5rem; align-items: stretch;">
|
||||||
|
<input type="text" id="default-temp-dir-input"
|
||||||
|
placeholder="e.g. /mnt/fast_ssd/upscaler_temp (Leave empty for project default temp/)"
|
||||||
|
style="flex: 1; padding: 0.6rem 0.85rem; border-radius: 8px; background: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-primary); font-size: 0.88rem; font-family: 'JetBrains Mono', monospace;">
|
||||||
|
<button type="button" id="save-temp-dir-btn" class="btn btn-primary btn-small" style="white-space: nowrap; padding: 0.6rem 1.2rem;">
|
||||||
|
<i class="fa-solid fa-floppy-disk"></i> Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div id="temp-dir-status" style="font-size: 0.78rem; margin-top: 0.5rem; min-height: 1.2em;"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="temp-stats-summary" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 1.5rem;">
|
<div class="temp-stats-summary" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 1.5rem;">
|
||||||
<div class="glass-card" style="padding: 1rem; background: rgba(255,255,255,0.01); text-align: center;">
|
<div class="glass-card" style="padding: 1rem; background: rgba(255,255,255,0.01); text-align: center;">
|
||||||
<span style="font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; display: block;">Total Temp Size</span>
|
<span style="font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; display: block;">Total Temp Size</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user