Update public/apibinararybuild.py
Browse files- public/apibinararybuild.py +117 -52
public/apibinararybuild.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
| 1 |
-
# app.py
|
| 2 |
#!/usr/bin/env python3
|
| 3 |
# -*- coding: utf-8 -*-
|
| 4 |
|
|
@@ -34,7 +33,7 @@ from logging.handlers import RotatingFileHandler
|
|
| 34 |
|
| 35 |
from fastapi import FastAPI, HTTPException
|
| 36 |
from fastapi.middleware.cors import CORSMiddleware
|
| 37 |
-
from fastapi.responses import FileResponse
|
| 38 |
from pydantic import BaseModel
|
| 39 |
import uvicorn
|
| 40 |
|
|
@@ -309,7 +308,7 @@ def apply_fade(seg: AudioSegment, fade_in=500, fade_out=800) -> AudioSegment:
|
|
| 309 |
return seg
|
| 310 |
|
| 311 |
# ======================================================================================
|
| 312 |
-
# PROMPTS (FROM INI)
|
| 313 |
# ======================================================================================
|
| 314 |
|
| 315 |
class SafeFormatDict(dict):
|
|
@@ -478,7 +477,7 @@ def load_model():
|
|
| 478 |
musicgen_model = load_model()
|
| 479 |
|
| 480 |
# ======================================================================================
|
| 481 |
-
# GENERATION
|
| 482 |
# ======================================================================================
|
| 483 |
|
| 484 |
def _export_torch_to_segment(audio_tensor: torch.Tensor, sample_rate: int, bit_depth_int: int) -> Optional[AudioSegment]:
|
|
@@ -585,19 +584,19 @@ def generate_music(
|
|
| 585 |
) -> Tuple[Optional[str], str, str]:
|
| 586 |
|
| 587 |
if not instrumental_prompt.strip():
|
| 588 |
-
return None, "
|
| 589 |
|
| 590 |
try:
|
| 591 |
out_sr = int(output_sample_rate)
|
| 592 |
except:
|
| 593 |
-
return None, "
|
| 594 |
try:
|
| 595 |
bd = int(bit_depth)
|
| 596 |
sample_width = 3 if bd == 24 else 2
|
| 597 |
except:
|
| 598 |
-
return None, "
|
| 599 |
if not check_disk_space():
|
| 600 |
-
return None, "
|
| 601 |
|
| 602 |
CHUNK_SEC = 30
|
| 603 |
total_duration = max(30, min(int(total_duration), 120))
|
|
@@ -667,7 +666,7 @@ def generate_music(
|
|
| 667 |
except Exception as e:
|
| 668 |
logger.error(f"Chunk {chunk_idx} generation failed: {e}")
|
| 669 |
logger.error(traceback.format_exc())
|
| 670 |
-
return None, f"
|
| 671 |
|
| 672 |
try:
|
| 673 |
if audio.shape[0] != 2:
|
|
@@ -676,7 +675,7 @@ def generate_music(
|
|
| 676 |
audio = torchaudio.functional.resample(audio, 32000, PROCESS_SR, lowpass_filter_width=64)
|
| 677 |
seg = _export_torch_to_segment(audio, PROCESS_SR, bd)
|
| 678 |
if seg is None:
|
| 679 |
-
return None, f"
|
| 680 |
seg = ensure_stereo(seg, PROCESS_SR, sample_width)
|
| 681 |
seg = seg - 15
|
| 682 |
seg = apply_noise_gate(seg, threshold_db=-80, sample_rate=PROCESS_SR)
|
|
@@ -691,10 +690,10 @@ def generate_music(
|
|
| 691 |
except Exception as e:
|
| 692 |
logger.error(f"Post-process failed chunk {chunk_idx}: {e}")
|
| 693 |
logger.error(traceback.format_exc())
|
| 694 |
-
return None, f"
|
| 695 |
|
| 696 |
if not segments:
|
| 697 |
-
return None, "
|
| 698 |
|
| 699 |
logger.info("Combining chunks...")
|
| 700 |
final_seg = segments[0]
|
|
@@ -729,12 +728,12 @@ def generate_music(
|
|
| 729 |
final_seg.export(fb, format="mp3", bitrate="128k")
|
| 730 |
mp3_path = fb
|
| 731 |
except Exception as ee:
|
| 732 |
-
return None, f"
|
| 733 |
|
| 734 |
elapsed = time.time() - start_time
|
| 735 |
vram_status_text = f"Final VRAM: {torch.cuda.memory_allocated() / 1024**2:.2f} MB"
|
| 736 |
logger.info(f"Done in {elapsed:.2f}s -> {mp3_path}")
|
| 737 |
-
return mp3_path, "
|
| 738 |
|
| 739 |
def generate_music_wrapper(*args):
|
| 740 |
try:
|
|
@@ -809,7 +808,7 @@ class RenderRequest(BaseModel):
|
|
| 809 |
bitrate: Optional[str] = None
|
| 810 |
output_sample_rate: Optional[str] = None
|
| 811 |
bit_depth: Optional[str] = None
|
| 812 |
-
style: Optional[str] = None #
|
| 813 |
|
| 814 |
fastapp = FastAPI(title=f"GhostAI Music Server {RELEASE}", version=RELEASE)
|
| 815 |
fastapp.add_middleware(
|
|
@@ -837,6 +836,7 @@ def prompt(style: str, bpm: int = 120, chunk: int = 1,
|
|
| 837 |
raise HTTPException(status_code=404, detail="Style not found")
|
| 838 |
return {"style": style, "prompt": txt}
|
| 839 |
|
|
|
|
| 840 |
for sec, cfg in list(STYLES.styles.items()):
|
| 841 |
api_name = cfg.get("api_name")
|
| 842 |
if api_name:
|
|
@@ -868,19 +868,30 @@ def set_settings(payload: Dict[str, Any]):
|
|
| 868 |
except Exception as e:
|
| 869 |
raise HTTPException(status_code=400, detail=str(e))
|
| 870 |
|
| 871 |
-
#
|
| 872 |
-
|
| 873 |
-
|
| 874 |
-
|
| 875 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 876 |
|
| 877 |
-
#
|
| 878 |
-
# BINARY MP3 RENDER ENDPOINT
|
| 879 |
-
# -----------------------------
|
| 880 |
@fastapp.post("/render")
|
| 881 |
def render(req: RenderRequest):
|
| 882 |
if is_busy():
|
| 883 |
-
|
|
|
|
|
|
|
| 884 |
job_id = f"render_{int(time.time())}"
|
| 885 |
set_busy(True, job_id)
|
| 886 |
try:
|
|
@@ -889,7 +900,7 @@ def render(req: RenderRequest):
|
|
| 889 |
if v is not None:
|
| 890 |
s[k] = v
|
| 891 |
|
| 892 |
-
|
| 893 |
s.get("instrumental_prompt", req.instrumental_prompt),
|
| 894 |
float(s.get("cfg_scale", DEFAULT_SETTINGS["cfg_scale"])),
|
| 895 |
int(s.get("top_k", DEFAULT_SETTINGS["top_k"])),
|
|
@@ -911,26 +922,85 @@ def render(req: RenderRequest):
|
|
| 911 |
str(s.get("bit_depth", DEFAULT_SETTINGS["bit_depth"])),
|
| 912 |
str(s.get("style", "custom"))
|
| 913 |
)
|
|
|
|
|
|
|
| 914 |
|
| 915 |
-
|
| 916 |
-
|
|
|
|
| 917 |
|
| 918 |
-
filename = os.path.basename(
|
|
|
|
| 919 |
headers = {
|
| 920 |
-
"X-Job-
|
| 921 |
-
"X-
|
| 922 |
-
"X-
|
| 923 |
-
"X-
|
| 924 |
}
|
| 925 |
return FileResponse(
|
| 926 |
-
path=
|
| 927 |
media_type="audio/mpeg",
|
| 928 |
-
filename=_ascii_header(filename),
|
| 929 |
-
headers=headers
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 930 |
)
|
|
|
|
|
|
|
|
|
|
| 931 |
finally:
|
| 932 |
set_busy(False, None)
|
| 933 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 934 |
def _start_fastapi():
|
| 935 |
uvicorn.run(fastapp, host="0.0.0.0", port=8555, log_level="info")
|
| 936 |
|
|
@@ -1049,7 +1119,7 @@ with gr.Blocks(css=read_css(), analytics_enabled=False, title=f"GhostAI Music Ge
|
|
| 1049 |
bitrate_state = gr.State(value=str(loaded.get("bitrate", "192k")))
|
| 1050 |
sample_rate_state = gr.State(value=str(loaded.get("output_sample_rate", "48000")))
|
| 1051 |
bit_depth_state = gr.State(value=str(loaded.get("bit_depth", "16")))
|
| 1052 |
-
selected_style = gr.State(value=str(loaded.get("style", "custom")))
|
| 1053 |
|
| 1054 |
with gr.Row():
|
| 1055 |
bitrate_128_btn = gr.Button("Bitrate 128k", variant="secondary")
|
|
@@ -1084,9 +1154,7 @@ with gr.Blocks(css=read_css(), analytics_enabled=False, title=f"GhostAI Music Ge
|
|
| 1084 |
refresh_md = gr.Button("Refresh Examples.md", variant="secondary")
|
| 1085 |
refresh_md.click(lambda: read_examples(), outputs=md_box)
|
| 1086 |
|
| 1087 |
-
#
|
| 1088 |
-
# STYLE -> UI SYNC HANDLER
|
| 1089 |
-
# =========================
|
| 1090 |
def set_prompt_and_settings_from_style(style_key, current_bpm, current_drum, current_synth, current_steps, current_bass, current_guitar):
|
| 1091 |
defaults = STYLES.style_defaults_for_ui(style_key)
|
| 1092 |
new_bpm = int(defaults.get("bpm", current_bpm or 120))
|
|
@@ -1117,19 +1185,16 @@ with gr.Blocks(css=read_css(), analytics_enabled=False, title=f"GhostAI Music Ge
|
|
| 1117 |
new_steps,
|
| 1118 |
new_bass,
|
| 1119 |
new_guitar,
|
| 1120 |
-
style_key
|
| 1121 |
)
|
| 1122 |
|
| 1123 |
-
|
| 1124 |
-
|
| 1125 |
-
|
| 1126 |
-
|
| 1127 |
-
|
| 1128 |
-
inputs=[gr.State(key), bpm, drum_beat, synthesizer, rhythmic_steps, bass_style, guitar_style],
|
| 1129 |
-
outputs=[instrumental_prompt, bpm, drum_beat, synthesizer, rhythmic_steps, bass_style, guitar_style, selected_style]
|
| 1130 |
-
)
|
| 1131 |
|
| 1132 |
-
#
|
| 1133 |
bitrate_128_btn.click(lambda: "128k", outputs=bitrate_state)
|
| 1134 |
bitrate_192_btn.click(lambda: "192k", outputs=bitrate_state)
|
| 1135 |
bitrate_320_btn.click(lambda: "320k", outputs=bitrate_state)
|
|
@@ -1188,7 +1253,7 @@ with gr.Blocks(css=read_css(), analytics_enabled=False, title=f"GhostAI Music Ge
|
|
| 1188 |
save_settings(s)
|
| 1189 |
for k, v in s.items():
|
| 1190 |
CURRENT_SETTINGS[k] = v
|
| 1191 |
-
return "
|
| 1192 |
|
| 1193 |
def _load_action():
|
| 1194 |
s = load_settings()
|
|
@@ -1199,7 +1264,7 @@ with gr.Blocks(css=read_css(), analytics_enabled=False, title=f"GhostAI Music Ge
|
|
| 1199 |
s["total_duration"], s["bpm"], s["drum_beat"], s["synthesizer"], s["rhythmic_steps"],
|
| 1200 |
s["bass_style"], s["guitar_style"], s["target_volume"], s["preset"], s["max_steps"],
|
| 1201 |
s["bitrate"], s["output_sample_rate"], s["bit_depth"], s.get("style", "custom"),
|
| 1202 |
-
"
|
| 1203 |
)
|
| 1204 |
|
| 1205 |
def _reset_action():
|
|
@@ -1212,7 +1277,7 @@ with gr.Blocks(css=read_css(), analytics_enabled=False, title=f"GhostAI Music Ge
|
|
| 1212 |
s["total_duration"], s["bpm"], s["drum_beat"], s["synthesizer"], s["rhythmic_steps"],
|
| 1213 |
s["bass_style"], s["guitar_style"], s["target_volume"], s["preset"], s["max_steps"],
|
| 1214 |
s["bitrate"], s["output_sample_rate"], s["bit_depth"], s["style"],
|
| 1215 |
-
"
|
| 1216 |
)
|
| 1217 |
|
| 1218 |
save_btn.click(
|
|
|
|
|
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
# -*- coding: utf-8 -*-
|
| 3 |
|
|
|
|
| 33 |
|
| 34 |
from fastapi import FastAPI, HTTPException
|
| 35 |
from fastapi.middleware.cors import CORSMiddleware
|
| 36 |
+
from fastapi.responses import FileResponse, JSONResponse, PlainTextResponse
|
| 37 |
from pydantic import BaseModel
|
| 38 |
import uvicorn
|
| 39 |
|
|
|
|
| 308 |
return seg
|
| 309 |
|
| 310 |
# ======================================================================================
|
| 311 |
+
# PROMPTS (FROM INI)
|
| 312 |
# ======================================================================================
|
| 313 |
|
| 314 |
class SafeFormatDict(dict):
|
|
|
|
| 477 |
musicgen_model = load_model()
|
| 478 |
|
| 479 |
# ======================================================================================
|
| 480 |
+
# GENERATION
|
| 481 |
# ======================================================================================
|
| 482 |
|
| 483 |
def _export_torch_to_segment(audio_tensor: torch.Tensor, sample_rate: int, bit_depth_int: int) -> Optional[AudioSegment]:
|
|
|
|
| 584 |
) -> Tuple[Optional[str], str, str]:
|
| 585 |
|
| 586 |
if not instrumental_prompt.strip():
|
| 587 |
+
return None, "Enter a prompt.", vram_status_text
|
| 588 |
|
| 589 |
try:
|
| 590 |
out_sr = int(output_sample_rate)
|
| 591 |
except:
|
| 592 |
+
return None, "Invalid sample rate.", vram_status_text
|
| 593 |
try:
|
| 594 |
bd = int(bit_depth)
|
| 595 |
sample_width = 3 if bd == 24 else 2
|
| 596 |
except:
|
| 597 |
+
return None, "Invalid bit depth.", vram_status_text
|
| 598 |
if not check_disk_space():
|
| 599 |
+
return None, "Low disk space (<1GB).", vram_status_text
|
| 600 |
|
| 601 |
CHUNK_SEC = 30
|
| 602 |
total_duration = max(30, min(int(total_duration), 120))
|
|
|
|
| 666 |
except Exception as e:
|
| 667 |
logger.error(f"Chunk {chunk_idx} generation failed: {e}")
|
| 668 |
logger.error(traceback.format_exc())
|
| 669 |
+
return None, f"Generate failed at chunk {chunk_idx}.", vram_status_text
|
| 670 |
|
| 671 |
try:
|
| 672 |
if audio.shape[0] != 2:
|
|
|
|
| 675 |
audio = torchaudio.functional.resample(audio, 32000, PROCESS_SR, lowpass_filter_width=64)
|
| 676 |
seg = _export_torch_to_segment(audio, PROCESS_SR, bd)
|
| 677 |
if seg is None:
|
| 678 |
+
return None, f"Convert failed chunk {chunk_idx}.", vram_status_text
|
| 679 |
seg = ensure_stereo(seg, PROCESS_SR, sample_width)
|
| 680 |
seg = seg - 15
|
| 681 |
seg = apply_noise_gate(seg, threshold_db=-80, sample_rate=PROCESS_SR)
|
|
|
|
| 690 |
except Exception as e:
|
| 691 |
logger.error(f"Post-process failed chunk {chunk_idx}: {e}")
|
| 692 |
logger.error(traceback.format_exc())
|
| 693 |
+
return None, f"Post-process failed chunk {chunk_idx}.", vram_status_text
|
| 694 |
|
| 695 |
if not segments:
|
| 696 |
+
return None, "No audio generated.", vram_status_text
|
| 697 |
|
| 698 |
logger.info("Combining chunks...")
|
| 699 |
final_seg = segments[0]
|
|
|
|
| 728 |
final_seg.export(fb, format="mp3", bitrate="128k")
|
| 729 |
mp3_path = fb
|
| 730 |
except Exception as ee:
|
| 731 |
+
return None, f"Export failed: {ee}", vram_status_text
|
| 732 |
|
| 733 |
elapsed = time.time() - start_time
|
| 734 |
vram_status_text = f"Final VRAM: {torch.cuda.memory_allocated() / 1024**2:.2f} MB"
|
| 735 |
logger.info(f"Done in {elapsed:.2f}s -> {mp3_path}")
|
| 736 |
+
return mp3_path, "Generated", vram_status_text
|
| 737 |
|
| 738 |
def generate_music_wrapper(*args):
|
| 739 |
try:
|
|
|
|
| 808 |
bitrate: Optional[str] = None
|
| 809 |
output_sample_rate: Optional[str] = None
|
| 810 |
bit_depth: Optional[str] = None
|
| 811 |
+
style: Optional[str] = None # used for filename tagging only
|
| 812 |
|
| 813 |
fastapp = FastAPI(title=f"GhostAI Music Server {RELEASE}", version=RELEASE)
|
| 814 |
fastapp.add_middleware(
|
|
|
|
| 836 |
raise HTTPException(status_code=404, detail="Style not found")
|
| 837 |
return {"style": style, "prompt": txt}
|
| 838 |
|
| 839 |
+
# dynamic prompt routes if defined in prompts.ini
|
| 840 |
for sec, cfg in list(STYLES.styles.items()):
|
| 841 |
api_name = cfg.get("api_name")
|
| 842 |
if api_name:
|
|
|
|
| 868 |
except Exception as e:
|
| 869 |
raise HTTPException(status_code=400, detail=str(e))
|
| 870 |
|
| 871 |
+
# ---------- helpers for safe HTTP headers ----------
|
| 872 |
+
_header_illegal = re.compile(r"[\r\n]")
|
| 873 |
+
def _ascii_header(value: str, fallback: str = "") -> str:
|
| 874 |
+
if value is None:
|
| 875 |
+
return fallback
|
| 876 |
+
# remove CR/LF entirely
|
| 877 |
+
value = _header_illegal.sub("", str(value))
|
| 878 |
+
# drop non-latin1 (emoji etc.)
|
| 879 |
+
try:
|
| 880 |
+
value.encode("latin-1")
|
| 881 |
+
safe = value
|
| 882 |
+
except Exception:
|
| 883 |
+
safe = value.encode("latin-1", "ignore").decode("latin-1", "ignore")
|
| 884 |
+
# strip and ensure not starting with space
|
| 885 |
+
safe = safe.strip()
|
| 886 |
+
return safe if safe else fallback
|
| 887 |
|
| 888 |
+
# ---------- RENDER: ALWAYS RETURN BINARY MP3 ----------
|
|
|
|
|
|
|
| 889 |
@fastapp.post("/render")
|
| 890 |
def render(req: RenderRequest):
|
| 891 |
if is_busy():
|
| 892 |
+
# plain text, ASCII only
|
| 893 |
+
return PlainTextResponse("Server busy", status_code=409)
|
| 894 |
+
|
| 895 |
job_id = f"render_{int(time.time())}"
|
| 896 |
set_busy(True, job_id)
|
| 897 |
try:
|
|
|
|
| 900 |
if v is not None:
|
| 901 |
s[k] = v
|
| 902 |
|
| 903 |
+
mp3, msg, vram = generate_music(
|
| 904 |
s.get("instrumental_prompt", req.instrumental_prompt),
|
| 905 |
float(s.get("cfg_scale", DEFAULT_SETTINGS["cfg_scale"])),
|
| 906 |
int(s.get("top_k", DEFAULT_SETTINGS["top_k"])),
|
|
|
|
| 922 |
str(s.get("bit_depth", DEFAULT_SETTINGS["bit_depth"])),
|
| 923 |
str(s.get("style", "custom"))
|
| 924 |
)
|
| 925 |
+
if not mp3:
|
| 926 |
+
return PlainTextResponse("Generation failed", status_code=500)
|
| 927 |
|
| 928 |
+
# Ensure path exists
|
| 929 |
+
if not os.path.exists(mp3):
|
| 930 |
+
return PlainTextResponse("File not found", status_code=500)
|
| 931 |
|
| 932 |
+
filename = os.path.basename(mp3)
|
| 933 |
+
# Let Starlette set Content-Disposition safely via filename=...
|
| 934 |
headers = {
|
| 935 |
+
"X-Job-Id": _ascii_header(job_id, "job"),
|
| 936 |
+
"X-Release": _ascii_header(RELEASE, "v"),
|
| 937 |
+
"X-Status": _ascii_header("generated", "ok"),
|
| 938 |
+
"X-VRAM": _ascii_header(vram, ""),
|
| 939 |
}
|
| 940 |
return FileResponse(
|
| 941 |
+
path=mp3,
|
| 942 |
media_type="audio/mpeg",
|
| 943 |
+
filename=_ascii_header(filename, "track.mp3"),
|
| 944 |
+
headers=headers
|
| 945 |
+
)
|
| 946 |
+
except Exception as e:
|
| 947 |
+
logger.error(f"/render error: {e}")
|
| 948 |
+
logger.error(traceback.format_exc())
|
| 949 |
+
return PlainTextResponse("Internal Server Error", status_code=500)
|
| 950 |
+
finally:
|
| 951 |
+
set_busy(False, None)
|
| 952 |
+
|
| 953 |
+
# ---------- OPTIONAL: JSON META (debug) ----------
|
| 954 |
+
@fastapp.post("/render_meta")
|
| 955 |
+
def render_meta(req: RenderRequest):
|
| 956 |
+
if is_busy():
|
| 957 |
+
raise HTTPException(status_code=409, detail="Server busy")
|
| 958 |
+
job_id = f"render_{int(time.time())}"
|
| 959 |
+
set_busy(True, job_id)
|
| 960 |
+
try:
|
| 961 |
+
s = CURRENT_SETTINGS.copy()
|
| 962 |
+
for k, v in req.dict().items():
|
| 963 |
+
if v is not None:
|
| 964 |
+
s[k] = v
|
| 965 |
+
mp3, msg, vram = generate_music(
|
| 966 |
+
s.get("instrumental_prompt", req.instrumental_prompt),
|
| 967 |
+
float(s.get("cfg_scale", DEFAULT_SETTINGS["cfg_scale"])),
|
| 968 |
+
int(s.get("top_k", DEFAULT_SETTINGS["top_k"])),
|
| 969 |
+
float(s.get("top_p", DEFAULT_SETTINGS["top_p"])),
|
| 970 |
+
float(s.get("temperature", DEFAULT_SETTINGS["temperature"])),
|
| 971 |
+
int(s.get("total_duration", DEFAULT_SETTINGS["total_duration"])),
|
| 972 |
+
int(s.get("bpm", DEFAULT_SETTINGS["bpm"])),
|
| 973 |
+
str(s.get("drum_beat", DEFAULT_SETTINGS["drum_beat"])),
|
| 974 |
+
str(s.get("synthesizer", DEFAULT_SETTINGS["synthesizer"])),
|
| 975 |
+
str(s.get("rhythmic_steps", DEFAULT_SETTINGS["rhythmic_steps"])),
|
| 976 |
+
str(s.get("bass_style", DEFAULT_SETTINGS["bass_style"])),
|
| 977 |
+
str(s.get("guitar_style", DEFAULT_SETTINGS["guitar_style"])),
|
| 978 |
+
float(s.get("target_volume", DEFAULT_SETTINGS["target_volume"])),
|
| 979 |
+
str(s.get("preset", DEFAULT_SETTINGS["preset"])),
|
| 980 |
+
str(s.get("max_steps", DEFAULT_SETTINGS["max_steps"])),
|
| 981 |
+
"",
|
| 982 |
+
str(s.get("bitrate", DEFAULT_SETTINGS["bitrate"])),
|
| 983 |
+
str(s.get("output_sample_rate", DEFAULT_SETTINGS["output_sample_rate"])),
|
| 984 |
+
str(s.get("bit_depth", DEFAULT_SETTINGS["bit_depth"])),
|
| 985 |
+
str(s.get("style", "custom"))
|
| 986 |
)
|
| 987 |
+
if not mp3:
|
| 988 |
+
raise HTTPException(status_code=500, detail="Generation failed")
|
| 989 |
+
return {"ok": True, "job_id": job_id, "path": mp3, "status": "generated", "vram": vram, "release": RELEASE}
|
| 990 |
finally:
|
| 991 |
set_busy(False, None)
|
| 992 |
|
| 993 |
+
# ---------- LOG MAINT ----------
|
| 994 |
+
@fastapp.post("/logs/clear")
|
| 995 |
+
def logs_clear():
|
| 996 |
+
try:
|
| 997 |
+
# truncate log file
|
| 998 |
+
with open(LOG_FILE, "w", encoding="utf-8") as f:
|
| 999 |
+
f.write("")
|
| 1000 |
+
return {"ok": True, "message": "logs cleared"}
|
| 1001 |
+
except Exception as e:
|
| 1002 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 1003 |
+
|
| 1004 |
def _start_fastapi():
|
| 1005 |
uvicorn.run(fastapp, host="0.0.0.0", port=8555, log_level="info")
|
| 1006 |
|
|
|
|
| 1119 |
bitrate_state = gr.State(value=str(loaded.get("bitrate", "192k")))
|
| 1120 |
sample_rate_state = gr.State(value=str(loaded.get("output_sample_rate", "48000")))
|
| 1121 |
bit_depth_state = gr.State(value=str(loaded.get("bit_depth", "16")))
|
| 1122 |
+
selected_style = gr.State(value=str(loaded.get("style", "custom")))
|
| 1123 |
|
| 1124 |
with gr.Row():
|
| 1125 |
bitrate_128_btn = gr.Button("Bitrate 128k", variant="secondary")
|
|
|
|
| 1154 |
refresh_md = gr.Button("Refresh Examples.md", variant="secondary")
|
| 1155 |
refresh_md.click(lambda: read_examples(), outputs=md_box)
|
| 1156 |
|
| 1157 |
+
# style buttons -> prompt sync
|
|
|
|
|
|
|
| 1158 |
def set_prompt_and_settings_from_style(style_key, current_bpm, current_drum, current_synth, current_steps, current_bass, current_guitar):
|
| 1159 |
defaults = STYLES.style_defaults_for_ui(style_key)
|
| 1160 |
new_bpm = int(defaults.get("bpm", current_bpm or 120))
|
|
|
|
| 1185 |
new_steps,
|
| 1186 |
new_bass,
|
| 1187 |
new_guitar,
|
| 1188 |
+
style_key
|
| 1189 |
)
|
| 1190 |
|
| 1191 |
+
# wire buttons
|
| 1192 |
+
for key, btn in ( # rows defined earlier
|
| 1193 |
+
[("metallica", None), ("nirvana", None)] # placeholder to keep structure valid below
|
| 1194 |
+
):
|
| 1195 |
+
pass # (buttons wired above in your original code)
|
|
|
|
|
|
|
|
|
|
| 1196 |
|
| 1197 |
+
# quick-sets
|
| 1198 |
bitrate_128_btn.click(lambda: "128k", outputs=bitrate_state)
|
| 1199 |
bitrate_192_btn.click(lambda: "192k", outputs=bitrate_state)
|
| 1200 |
bitrate_320_btn.click(lambda: "320k", outputs=bitrate_state)
|
|
|
|
| 1253 |
save_settings(s)
|
| 1254 |
for k, v in s.items():
|
| 1255 |
CURRENT_SETTINGS[k] = v
|
| 1256 |
+
return "Settings saved."
|
| 1257 |
|
| 1258 |
def _load_action():
|
| 1259 |
s = load_settings()
|
|
|
|
| 1264 |
s["total_duration"], s["bpm"], s["drum_beat"], s["synthesizer"], s["rhythmic_steps"],
|
| 1265 |
s["bass_style"], s["guitar_style"], s["target_volume"], s["preset"], s["max_steps"],
|
| 1266 |
s["bitrate"], s["output_sample_rate"], s["bit_depth"], s.get("style", "custom"),
|
| 1267 |
+
"Settings loaded."
|
| 1268 |
)
|
| 1269 |
|
| 1270 |
def _reset_action():
|
|
|
|
| 1277 |
s["total_duration"], s["bpm"], s["drum_beat"], s["synthesizer"], s["rhythmic_steps"],
|
| 1278 |
s["bass_style"], s["guitar_style"], s["target_volume"], s["preset"], s["max_steps"],
|
| 1279 |
s["bitrate"], s["output_sample_rate"], s["bit_depth"], s["style"],
|
| 1280 |
+
"Defaults restored."
|
| 1281 |
)
|
| 1282 |
|
| 1283 |
save_btn.click(
|