Update index.html
Browse files- index.html +192 -86
index.html
CHANGED
|
@@ -15,8 +15,8 @@
|
|
| 15 |
--input-border: #E1E7EF;
|
| 16 |
--text-primary: #1A202C;
|
| 17 |
--text-secondary: #626F86;
|
| 18 |
-
--accent-primary: #
|
| 19 |
-
--accent-glow: rgba(
|
| 20 |
--success-color: #38A169;
|
| 21 |
--radius-card: 20px;
|
| 22 |
--radius-btn: 14px;
|
|
@@ -57,7 +57,7 @@
|
|
| 57 |
|
| 58 |
h1 {
|
| 59 |
font-size: 1.8rem; font-weight: 800; margin: 0;
|
| 60 |
-
background: linear-gradient(90deg, #2d3748,
|
| 61 |
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
| 62 |
}
|
| 63 |
.subtitle { font-size: 0.9rem; color: var(--text-secondary); margin-top: 5px; }
|
|
@@ -89,10 +89,10 @@
|
|
| 89 |
textarea { min-height: 120px; resize: vertical; }
|
| 90 |
textarea:focus, input:focus, select:focus { border-color: var(--accent-primary); background: #fff; }
|
| 91 |
|
| 92 |
-
/*
|
| 93 |
.btn-main {
|
| 94 |
width: 100%; padding: 16px;
|
| 95 |
-
background: linear-gradient(135deg, var(--accent-primary), #
|
| 96 |
color: #fff; border: none; border-radius: var(--radius-btn);
|
| 97 |
font-size: 1.1rem; font-weight: 700; cursor: pointer;
|
| 98 |
display: flex; justify-content: center; align-items: center; gap: 10px;
|
|
@@ -108,7 +108,7 @@
|
|
| 108 |
}
|
| 109 |
.btn-outline:hover { border-color: var(--accent-primary); color: var(--accent-primary); }
|
| 110 |
|
| 111 |
-
/*
|
| 112 |
.accordion {
|
| 113 |
background-color: var(--input-bg);
|
| 114 |
color: var(--text-primary);
|
|
@@ -152,18 +152,14 @@
|
|
| 152 |
.checkbox-wrapper { display: flex; align-items: center; gap: 10px; margin-top: 5px; padding-bottom: 15px; border-top: 1px solid #e2e8f0; padding-top: 15px; }
|
| 153 |
.checkbox-wrapper input { width: 20px; height: 20px; cursor: pointer; }
|
| 154 |
|
| 155 |
-
/*
|
| 156 |
-
#finalResult { display: none; animation: slideUp 0.6s cubic-bezier(0.16, 1, 0.3, 1); }
|
| 157 |
-
|
| 158 |
.player-header {
|
| 159 |
display: flex; align-items: center; justify-content: space-between;
|
| 160 |
margin-bottom: 15px; padding-bottom: 15px;
|
| 161 |
border-bottom: 1px solid var(--panel-border);
|
| 162 |
}
|
| 163 |
-
|
| 164 |
-
/* دکمه دانلود */
|
| 165 |
#mainDownloadLink {
|
| 166 |
-
background-color: rgba(
|
| 167 |
color: var(--accent-primary);
|
| 168 |
text-decoration: none;
|
| 169 |
font-size: 0.9rem;
|
|
@@ -173,11 +169,9 @@
|
|
| 173 |
transition: 0.2s;
|
| 174 |
display: none;
|
| 175 |
}
|
| 176 |
-
#mainDownloadLink:hover { background-color: rgba(
|
| 177 |
-
|
| 178 |
.audio-item { margin-bottom: 10px; }
|
| 179 |
audio { width: 100%; height: 45px; border-radius: 20px; margin-top: 5px; }
|
| 180 |
-
|
| 181 |
.lyrics-container {
|
| 182 |
background: var(--input-bg);
|
| 183 |
border-radius: 16px;
|
|
@@ -192,17 +186,7 @@
|
|
| 192 |
border: 1px solid var(--input-border);
|
| 193 |
margin-top: 15px;
|
| 194 |
}
|
| 195 |
-
|
| 196 |
-
.lyrics-tag {
|
| 197 |
-
color: var(--accent-primary);
|
| 198 |
-
font-weight: 800;
|
| 199 |
-
display: block;
|
| 200 |
-
margin-top: 20px;
|
| 201 |
-
margin-bottom: 5px;
|
| 202 |
-
font-size: 0.9em;
|
| 203 |
-
letter-spacing: 1px;
|
| 204 |
-
text-transform: uppercase;
|
| 205 |
-
}
|
| 206 |
|
| 207 |
/* لودر */
|
| 208 |
#loader { display: none; text-align: center; padding: 20px; }
|
|
@@ -213,6 +197,54 @@
|
|
| 213 |
.bar:nth-child(4) { animation-delay: 0.3s; height: 50%; }
|
| 214 |
@keyframes jump { 0%, 100% { height: 20%; } 50% { height: 100%; } }
|
| 215 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 216 |
.hidden { display: none; }
|
| 217 |
@keyframes slideUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
|
| 218 |
</style>
|
|
@@ -223,14 +255,13 @@
|
|
| 223 |
<div class="container">
|
| 224 |
<header>
|
| 225 |
<div class="logo-box">
|
| 226 |
-
<!-- آیکون جدید موسیقی -->
|
| 227 |
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18V5l12-2v13"></path><circle cx="6" cy="18" r="3"></circle><circle cx="18" cy="16" r="3"></circle></svg>
|
| 228 |
</div>
|
| 229 |
<h1>استودیو موزیک آلفا</h1>
|
| 230 |
<p class="subtitle">ساخت آهنگ حرفهای با هوش مصنوعی</p>
|
| 231 |
</header>
|
| 232 |
|
| 233 |
-
<!-- مرحله اصلی
|
| 234 |
<div id="step1" class="card">
|
| 235 |
<div class="form-label">
|
| 236 |
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"></path><path d="M19 10v2a7 7 0 0 1-14 0v-2"></path></svg>
|
|
@@ -238,14 +269,13 @@
|
|
| 238 |
</div>
|
| 239 |
<textarea id="ideaInput" placeholder="مثال: آهنگ تولد برای سمیرا، سبک پاپ شاد..."></textarea>
|
| 240 |
|
| 241 |
-
<
|
| 242 |
-
<button class="accordion">تنظیمات پیشرفته (مدل، تعداد، کیفیت)</button>
|
| 243 |
<div class="panel">
|
| 244 |
<div class="settings-grid">
|
| 245 |
<div class="settings-group">
|
| 246 |
<label>مدل هوش مصنوعی:</label>
|
| 247 |
<select id="model_select">
|
| 248 |
-
<option value="acestep-v15-turbo-shift3" selected>Turbo-Shift3 (دقیقترین
|
| 249 |
<option value="acestep-v15-turbo">Turbo (سریعترین)</option>
|
| 250 |
</select>
|
| 251 |
</div>
|
|
@@ -288,36 +318,55 @@
|
|
| 288 |
<p style="color: #718096; font-size: 0.9rem; margin-top: 10px;" id="loaderText">در حال پردازش...</p>
|
| 289 |
</div>
|
| 290 |
|
| 291 |
-
<!--
|
| 292 |
-
<div id="finalResult" class="card">
|
| 293 |
<div class="player-header">
|
| 294 |
<div style="font-weight: 700; color: var(--success-color); display: flex; align-items: center; gap: 5px;">
|
| 295 |
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>
|
| 296 |
-
آهنگ آماده شد
|
| 297 |
</div>
|
| 298 |
-
<a id="mainDownloadLink" href="#" target="_blank" download>
|
| 299 |
-
دانلود آهنگ 📥
|
| 300 |
-
</a>
|
| 301 |
</div>
|
| 302 |
-
|
| 303 |
<div id="playerWrapper"></div>
|
| 304 |
-
|
| 305 |
<div class="form-label" style="margin-top: 20px; justify-content: center; color: #718096;">متن آهنگ</div>
|
| 306 |
<div class="lyrics-container" id="finalLyricsBox"></div>
|
| 307 |
-
|
| 308 |
<button onclick="location.reload()" class="btn-main btn-outline">ساخت آهنگ جدید</button>
|
| 309 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 310 |
</div>
|
| 311 |
|
| 312 |
<script>
|
| 313 |
const ACE_SPACE_URL = "https://ace-step-ace-step-v1-5.hf.space/";
|
| 314 |
|
| 315 |
-
//
|
| 316 |
const getVal = (id) => document.getElementById(id).value;
|
| 317 |
const getNum = (id) => Number(document.getElementById(id).value);
|
| 318 |
const getChk = (id) => document.getElementById(id).checked;
|
| 319 |
|
| 320 |
-
// المانها
|
| 321 |
const ideaInput = document.getElementById('ideaInput');
|
| 322 |
const processBtn = document.getElementById('processBtn');
|
| 323 |
const step1 = document.getElementById('step1');
|
|
@@ -327,32 +376,101 @@
|
|
| 327 |
const playerWrapper = document.getElementById('playerWrapper');
|
| 328 |
const finalLyricsBox = document.getElementById('finalLyricsBox');
|
| 329 |
const mainDownloadLink = document.getElementById('mainDownloadLink');
|
|
|
|
| 330 |
|
| 331 |
-
//
|
| 332 |
const acc = document.getElementsByClassName("accordion");
|
| 333 |
for (let i = 0; i < acc.length; i++) {
|
| 334 |
acc[i].addEventListener("click", function() {
|
| 335 |
this.classList.toggle("active");
|
| 336 |
const panel = this.nextElementSibling;
|
| 337 |
-
|
| 338 |
-
panel.style.maxHeight = null;
|
| 339 |
-
} else {
|
| 340 |
-
panel.style.maxHeight = panel.scrollHeight + "px";
|
| 341 |
-
}
|
| 342 |
});
|
| 343 |
}
|
| 344 |
|
| 345 |
-
// ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 346 |
processBtn.addEventListener('click', async () => {
|
| 347 |
if (!ideaInput.value.trim()) return alert("لطفا موضوع آهنگ را بنویسید");
|
| 348 |
|
| 349 |
-
// مخفی کردن فرم و نمایش لودر
|
| 350 |
processBtn.disabled = true;
|
| 351 |
step1.style.display = 'none';
|
| 352 |
loader.style.display = 'block';
|
| 353 |
|
| 354 |
try {
|
| 355 |
-
// 1. دریافت متن و پرامپت از Gemini
|
| 356 |
loaderText.innerText = "آلفا در حال نوشتن شعر و ملودی...";
|
| 357 |
|
| 358 |
const response = await fetch('/api/refine', {
|
|
@@ -367,34 +485,25 @@
|
|
| 367 |
const lyrics = data.lyrics;
|
| 368 |
const musicPrompt = data.music_prompt;
|
| 369 |
|
| 370 |
-
|
| 371 |
-
loaderText.innerText = "در حال ضبط آهنگ در استودیو آلفا (لطفاً صبر کنید)...";
|
| 372 |
|
| 373 |
-
// آمادهسازی م
|
| 374 |
formatLyricsForDisplay(lyrics);
|
| 375 |
playerWrapper.innerHTML = '';
|
| 376 |
|
| 377 |
const payload = [
|
| 378 |
-
getVal('model_select'),
|
| 379 |
-
|
| 380 |
-
musicPrompt,
|
| 381 |
-
lyrics,
|
| 382 |
0, "", "", "unknown",
|
| 383 |
-
getNum('steps_input'),
|
| 384 |
-
getNum('
|
| 385 |
-
|
| 386 |
-
getNum('seed_input'),
|
| 387 |
-
null, -1,
|
| 388 |
-
getNum('batch_size'),
|
| 389 |
-
null, null, 0, -1,
|
| 390 |
"Fill the audio semantic mask based on the given conditions:",
|
| 391 |
1, "text2music", false, 0, 1, 3, "ode", "", "mp3", 0.85,
|
| 392 |
-
getChk('think_checkbox'),
|
| 393 |
-
2, 0, 0.9, "NO USER INPUT", true, true, true, null, false, true, false, false, 0.5, 8, null, [], false, null, null, null, null
|
| 394 |
];
|
| 395 |
|
| 396 |
const session_hash = Math.random().toString(36).substring(2);
|
| 397 |
-
|
| 398 |
const joinResp = await fetch(`${ACE_SPACE_URL}gradio_api/queue/join`, {
|
| 399 |
method: 'POST',
|
| 400 |
headers: { 'Content-Type': 'application/json' },
|
|
@@ -412,37 +521,35 @@
|
|
| 412 |
} else if (msg.msg === 'process_completed') {
|
| 413 |
eventSource.close();
|
| 414 |
loader.style.display = 'none';
|
| 415 |
-
handleAudioOutput(msg.output);
|
| 416 |
}
|
| 417 |
};
|
| 418 |
-
|
| 419 |
-
eventSource.onerror = () => {
|
| 420 |
-
throw new Error('ارتباط با سرور قطع شد.');
|
| 421 |
-
};
|
| 422 |
|
| 423 |
} catch (e) {
|
| 424 |
alert("خطا: " + e.message);
|
| 425 |
loader.style.display = 'none';
|
| 426 |
-
step1.style.display = 'block';
|
| 427 |
processBtn.disabled = false;
|
| 428 |
}
|
| 429 |
});
|
| 430 |
|
| 431 |
-
function handleAudioOutput(data) {
|
| 432 |
const processedUrls = new Set();
|
| 433 |
let hasResult = false;
|
| 434 |
|
| 435 |
function addAudio(url) {
|
| 436 |
const fullUrl = url.startsWith('http') ? url : ACE_SPACE_URL.replace(/\/$/, '') + url;
|
| 437 |
-
|
| 438 |
if (processedUrls.has(fullUrl)) return;
|
| 439 |
processedUrls.add(fullUrl);
|
| 440 |
-
|
| 441 |
hasResult = true;
|
| 442 |
|
|
|
|
| 443 |
if (processedUrls.size === 1) {
|
| 444 |
mainDownloadLink.href = fullUrl;
|
| 445 |
mainDownloadLink.style.display = 'inline-block';
|
|
|
|
|
|
|
| 446 |
}
|
| 447 |
|
| 448 |
const div = document.createElement('div');
|
|
@@ -470,26 +577,25 @@
|
|
| 470 |
}
|
| 471 |
}
|
| 472 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 473 |
function formatLyricsForDisplay(text) {
|
| 474 |
-
|
| 475 |
-
finalLyricsBox.innerHTML = formatted;
|
| 476 |
}
|
| 477 |
|
|
|
|
| 478 |
const canvas = document.getElementById('music-canvas');
|
| 479 |
const ctx = canvas.getContext('2d');
|
| 480 |
let t = 0;
|
| 481 |
-
|
| 482 |
-
function resize() {
|
| 483 |
-
canvas.width = window.innerWidth;
|
| 484 |
-
canvas.height = 400;
|
| 485 |
-
}
|
| 486 |
window.addEventListener('resize', resize);
|
| 487 |
resize();
|
| 488 |
-
|
| 489 |
function anim() {
|
| 490 |
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
| 491 |
ctx.beginPath();
|
| 492 |
-
ctx.strokeStyle = "rgba(
|
| 493 |
ctx.lineWidth = 2;
|
| 494 |
for(let i=0; i<canvas.width; i+=20) {
|
| 495 |
ctx.moveTo(i, 0);
|
|
|
|
| 15 |
--input-border: #E1E7EF;
|
| 16 |
--text-primary: #1A202C;
|
| 17 |
--text-secondary: #626F86;
|
| 18 |
+
--accent-primary: #4A6CFA;
|
| 19 |
+
--accent-glow: rgba(74, 108, 250, 0.2);
|
| 20 |
--success-color: #38A169;
|
| 21 |
--radius-card: 20px;
|
| 22 |
--radius-btn: 14px;
|
|
|
|
| 57 |
|
| 58 |
h1 {
|
| 59 |
font-size: 1.8rem; font-weight: 800; margin: 0;
|
| 60 |
+
background: linear-gradient(90deg, #2d3748, #4A6CFA);
|
| 61 |
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
| 62 |
}
|
| 63 |
.subtitle { font-size: 0.9rem; color: var(--text-secondary); margin-top: 5px; }
|
|
|
|
| 89 |
textarea { min-height: 120px; resize: vertical; }
|
| 90 |
textarea:focus, input:focus, select:focus { border-color: var(--accent-primary); background: #fff; }
|
| 91 |
|
| 92 |
+
/* دکمهها */
|
| 93 |
.btn-main {
|
| 94 |
width: 100%; padding: 16px;
|
| 95 |
+
background: linear-gradient(135deg, var(--accent-primary), #3b5bdb);
|
| 96 |
color: #fff; border: none; border-radius: var(--radius-btn);
|
| 97 |
font-size: 1.1rem; font-weight: 700; cursor: pointer;
|
| 98 |
display: flex; justify-content: center; align-items: center; gap: 10px;
|
|
|
|
| 108 |
}
|
| 109 |
.btn-outline:hover { border-color: var(--accent-primary); color: var(--accent-primary); }
|
| 110 |
|
| 111 |
+
/* تنظیمات پیشرفته */
|
| 112 |
.accordion {
|
| 113 |
background-color: var(--input-bg);
|
| 114 |
color: var(--text-primary);
|
|
|
|
| 152 |
.checkbox-wrapper { display: flex; align-items: center; gap: 10px; margin-top: 5px; padding-bottom: 15px; border-top: 1px solid #e2e8f0; padding-top: 15px; }
|
| 153 |
.checkbox-wrapper input { width: 20px; height: 20px; cursor: pointer; }
|
| 154 |
|
| 155 |
+
/* پلیر و متن */
|
|
|
|
|
|
|
| 156 |
.player-header {
|
| 157 |
display: flex; align-items: center; justify-content: space-between;
|
| 158 |
margin-bottom: 15px; padding-bottom: 15px;
|
| 159 |
border-bottom: 1px solid var(--panel-border);
|
| 160 |
}
|
|
|
|
|
|
|
| 161 |
#mainDownloadLink {
|
| 162 |
+
background-color: rgba(74, 108, 250, 0.1);
|
| 163 |
color: var(--accent-primary);
|
| 164 |
text-decoration: none;
|
| 165 |
font-size: 0.9rem;
|
|
|
|
| 169 |
transition: 0.2s;
|
| 170 |
display: none;
|
| 171 |
}
|
| 172 |
+
#mainDownloadLink:hover { background-color: rgba(74, 108, 250, 0.2); }
|
|
|
|
| 173 |
.audio-item { margin-bottom: 10px; }
|
| 174 |
audio { width: 100%; height: 45px; border-radius: 20px; margin-top: 5px; }
|
|
|
|
| 175 |
.lyrics-container {
|
| 176 |
background: var(--input-bg);
|
| 177 |
border-radius: 16px;
|
|
|
|
| 186 |
border: 1px solid var(--input-border);
|
| 187 |
margin-top: 15px;
|
| 188 |
}
|
| 189 |
+
.lyrics-tag { color: var(--accent-primary); font-weight: 800; display: block; margin-top: 20px; margin-bottom: 5px; font-size: 0.9em; letter-spacing: 1px; text-transform: uppercase; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
|
| 191 |
/* لودر */
|
| 192 |
#loader { display: none; text-align: center; padding: 20px; }
|
|
|
|
| 197 |
.bar:nth-child(4) { animation-delay: 0.3s; height: 50%; }
|
| 198 |
@keyframes jump { 0%, 100% { height: 20%; } 50% { height: 100%; } }
|
| 199 |
|
| 200 |
+
/* استایل بخش تاریخچه (History) */
|
| 201 |
+
.history-section {
|
| 202 |
+
margin-top: 30px;
|
| 203 |
+
width: 100%;
|
| 204 |
+
}
|
| 205 |
+
.history-title {
|
| 206 |
+
font-size: 1.2rem; font-weight: 800; color: var(--text-primary); margin-bottom: 15px; display: flex; align-items: center; gap: 10px;
|
| 207 |
+
}
|
| 208 |
+
.history-list {
|
| 209 |
+
display: grid; grid-template-columns: repeat(auto-fill, minmax(100%, 1fr)); gap: 12px;
|
| 210 |
+
}
|
| 211 |
+
.history-card {
|
| 212 |
+
background: white; border-radius: 16px; padding: 15px;
|
| 213 |
+
display: flex; align-items: center; justify-content: space-between;
|
| 214 |
+
border: 1px solid var(--panel-border);
|
| 215 |
+
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
| 216 |
+
cursor: pointer;
|
| 217 |
+
position: relative; overflow: hidden;
|
| 218 |
+
}
|
| 219 |
+
.history-card:hover { transform: translateY(-3px); box-shadow: 0 8px 20px rgba(74, 108, 250, 0.15); border-color: var(--accent-primary); }
|
| 220 |
+
.h-info { display: flex; align-items: center; gap: 15px; }
|
| 221 |
+
.h-icon { width: 45px; height: 45px; background: var(--input-bg); border-radius: 12px; display: flex; align-items: center; justify-content: center; color: var(--accent-primary); font-size: 1.2rem; }
|
| 222 |
+
.h-details h4 { margin: 0; font-size: 1rem; color: var(--text-primary); font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 200px; }
|
| 223 |
+
.h-details span { font-size: 0.8rem; color: var(--text-secondary); }
|
| 224 |
+
.h-play { color: var(--accent-primary); opacity: 0; transition: 0.3s; transform: scale(0.8); }
|
| 225 |
+
.history-card:hover .h-play { opacity: 1; transform: scale(1); }
|
| 226 |
+
|
| 227 |
+
/* مودال (Modal) */
|
| 228 |
+
.modal-overlay {
|
| 229 |
+
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
| 230 |
+
background: rgba(0,0,0,0.6); backdrop-filter: blur(5px);
|
| 231 |
+
z-index: 1000; display: none; align-items: center; justify-content: center;
|
| 232 |
+
opacity: 0; transition: opacity 0.3s;
|
| 233 |
+
}
|
| 234 |
+
.modal-content {
|
| 235 |
+
width: 90%; max-width: 600px; max-height: 90vh; overflow-y: auto;
|
| 236 |
+
background: white; border-radius: 20px; padding: 25px;
|
| 237 |
+
transform: scale(0.9); transition: transform 0.3s;
|
| 238 |
+
position: relative;
|
| 239 |
+
}
|
| 240 |
+
.modal-close {
|
| 241 |
+
position: absolute; top: 15px; left: 15px; background: #eee; border: none;
|
| 242 |
+
width: 30px; height: 30px; border-radius: 50%; cursor: pointer; font-size: 1.2rem;
|
| 243 |
+
display: flex; align-items: center; justify-content: center; color: #555;
|
| 244 |
+
}
|
| 245 |
+
.modal-overlay.open { display: flex; opacity: 1; }
|
| 246 |
+
.modal-overlay.open .modal-content { transform: scale(1); }
|
| 247 |
+
|
| 248 |
.hidden { display: none; }
|
| 249 |
@keyframes slideUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
|
| 250 |
</style>
|
|
|
|
| 255 |
<div class="container">
|
| 256 |
<header>
|
| 257 |
<div class="logo-box">
|
|
|
|
| 258 |
<svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18V5l12-2v13"></path><circle cx="6" cy="18" r="3"></circle><circle cx="18" cy="16" r="3"></circle></svg>
|
| 259 |
</div>
|
| 260 |
<h1>استودیو موزیک آلفا</h1>
|
| 261 |
<p class="subtitle">ساخت آهنگ حرفهای با هوش مصنوعی</p>
|
| 262 |
</header>
|
| 263 |
|
| 264 |
+
<!-- مرحله اصلی -->
|
| 265 |
<div id="step1" class="card">
|
| 266 |
<div class="form-label">
|
| 267 |
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"></path><path d="M19 10v2a7 7 0 0 1-14 0v-2"></path></svg>
|
|
|
|
| 269 |
</div>
|
| 270 |
<textarea id="ideaInput" placeholder="مثال: آهنگ تولد برای سمیرا، سبک پاپ شاد..."></textarea>
|
| 271 |
|
| 272 |
+
<button class="accordion">تنظیمات پیشرفته</button>
|
|
|
|
| 273 |
<div class="panel">
|
| 274 |
<div class="settings-grid">
|
| 275 |
<div class="settings-group">
|
| 276 |
<label>مدل هوش مصنوعی:</label>
|
| 277 |
<select id="model_select">
|
| 278 |
+
<option value="acestep-v15-turbo-shift3" selected>Turbo-Shift3 (دقیقترین)</option>
|
| 279 |
<option value="acestep-v15-turbo">Turbo (سریعترین)</option>
|
| 280 |
</select>
|
| 281 |
</div>
|
|
|
|
| 318 |
<p style="color: #718096; font-size: 0.9rem; margin-top: 10px;" id="loaderText">در حال پردازش...</p>
|
| 319 |
</div>
|
| 320 |
|
| 321 |
+
<!-- نتیجه نهایی (همچنین برای مودال استفاده میشود) -->
|
| 322 |
+
<div id="finalResult" class="card" style="display:none;">
|
| 323 |
<div class="player-header">
|
| 324 |
<div style="font-weight: 700; color: var(--success-color); display: flex; align-items: center; gap: 5px;">
|
| 325 |
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>
|
| 326 |
+
آهنگ جدید آماده شد
|
| 327 |
</div>
|
| 328 |
+
<a id="mainDownloadLink" href="#" target="_blank" download>دانلود آهنگ 📥</a>
|
|
|
|
|
|
|
| 329 |
</div>
|
|
|
|
| 330 |
<div id="playerWrapper"></div>
|
|
|
|
| 331 |
<div class="form-label" style="margin-top: 20px; justify-content: center; color: #718096;">متن آهنگ</div>
|
| 332 |
<div class="lyrics-container" id="finalLyricsBox"></div>
|
|
|
|
| 333 |
<button onclick="location.reload()" class="btn-main btn-outline">ساخت آهنگ جدید</button>
|
| 334 |
</div>
|
| 335 |
+
|
| 336 |
+
<!-- بخش سوابق (History) -->
|
| 337 |
+
<div class="history-section">
|
| 338 |
+
<div class="history-title">
|
| 339 |
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>
|
| 340 |
+
آخرین آهنگهای ساخته شده
|
| 341 |
+
</div>
|
| 342 |
+
<div class="history-list" id="historyList">
|
| 343 |
+
<!-- آیتمها اینجا اضافه میشوند -->
|
| 344 |
+
</div>
|
| 345 |
+
</div>
|
| 346 |
+
</div>
|
| 347 |
+
|
| 348 |
+
<!-- مودال نمایش سابقه -->
|
| 349 |
+
<div class="modal-overlay" id="historyModal">
|
| 350 |
+
<div class="modal-content">
|
| 351 |
+
<button class="modal-close" onclick="closeModal()">×</button>
|
| 352 |
+
<div class="player-header">
|
| 353 |
+
<div style="font-weight: 700; color: var(--accent-primary);">پخش آهنگ آرشیو شده</div>
|
| 354 |
+
<a id="modalDownloadLink" href="#" target="_blank" style="color:var(--accent-primary); font-weight:700; text-decoration:none;">دانلود 📥</a>
|
| 355 |
+
</div>
|
| 356 |
+
<div id="modalAudioWrapper" style="margin-bottom:15px;"></div>
|
| 357 |
+
<div class="form-label">متن آهنگ</div>
|
| 358 |
+
<div class="lyrics-container" id="modalLyricsBox"></div>
|
| 359 |
+
</div>
|
| 360 |
</div>
|
| 361 |
|
| 362 |
<script>
|
| 363 |
const ACE_SPACE_URL = "https://ace-step-ace-step-v1-5.hf.space/";
|
| 364 |
|
| 365 |
+
// المانها
|
| 366 |
const getVal = (id) => document.getElementById(id).value;
|
| 367 |
const getNum = (id) => Number(document.getElementById(id).value);
|
| 368 |
const getChk = (id) => document.getElementById(id).checked;
|
| 369 |
|
|
|
|
| 370 |
const ideaInput = document.getElementById('ideaInput');
|
| 371 |
const processBtn = document.getElementById('processBtn');
|
| 372 |
const step1 = document.getElementById('step1');
|
|
|
|
| 376 |
const playerWrapper = document.getElementById('playerWrapper');
|
| 377 |
const finalLyricsBox = document.getElementById('finalLyricsBox');
|
| 378 |
const mainDownloadLink = document.getElementById('mainDownloadLink');
|
| 379 |
+
const historyList = document.getElementById('historyList');
|
| 380 |
|
| 381 |
+
// آکاردئون
|
| 382 |
const acc = document.getElementsByClassName("accordion");
|
| 383 |
for (let i = 0; i < acc.length; i++) {
|
| 384 |
acc[i].addEventListener("click", function() {
|
| 385 |
this.classList.toggle("active");
|
| 386 |
const panel = this.nextElementSibling;
|
| 387 |
+
panel.style.maxHeight = panel.style.maxHeight ? null : panel.scrollHeight + "px";
|
|
|
|
|
|
|
|
|
|
|
|
|
| 388 |
});
|
| 389 |
}
|
| 390 |
|
| 391 |
+
// --- مدیریت سوابق (History Manager) ---
|
| 392 |
+
function loadHistory() {
|
| 393 |
+
const history = JSON.parse(localStorage.getItem('alphaHistory')) || [];
|
| 394 |
+
historyList.innerHTML = '';
|
| 395 |
+
|
| 396 |
+
if (history.length === 0) {
|
| 397 |
+
historyList.innerHTML = '<div style="text-align:center; color:#999; padding:20px;">هنوز آهنگی نساختهاید</div>';
|
| 398 |
+
return;
|
| 399 |
+
}
|
| 400 |
+
|
| 401 |
+
history.forEach((item, index) => {
|
| 402 |
+
const div = document.createElement('div');
|
| 403 |
+
div.className = 'history-card';
|
| 404 |
+
div.innerHTML = `
|
| 405 |
+
<div class="h-info">
|
| 406 |
+
<div class="h-icon">🎵</div>
|
| 407 |
+
<div class="h-details">
|
| 408 |
+
<h4>${item.idea.substring(0, 30)}${item.idea.length > 30 ? '...' : ''}</h4>
|
| 409 |
+
<span>${item.date}</span>
|
| 410 |
+
</div>
|
| 411 |
+
</div>
|
| 412 |
+
<div class="h-play">▶ نمایش</div>
|
| 413 |
+
`;
|
| 414 |
+
div.onclick = () => openHistoryItem(item);
|
| 415 |
+
historyList.appendChild(div);
|
| 416 |
+
});
|
| 417 |
+
}
|
| 418 |
+
|
| 419 |
+
function saveToHistory(idea, lyrics, audioUrl) {
|
| 420 |
+
let history = JSON.parse(localStorage.getItem('alphaHistory')) || [];
|
| 421 |
+
|
| 422 |
+
const newItem = {
|
| 423 |
+
id: Date.now(),
|
| 424 |
+
idea: idea,
|
| 425 |
+
lyrics: lyrics,
|
| 426 |
+
audioUrl: audioUrl,
|
| 427 |
+
date: new Date().toLocaleDateString('fa-IR', { hour: '2-digit', minute: '2-digit' })
|
| 428 |
+
};
|
| 429 |
+
|
| 430 |
+
// اضافه کردن به اول لیست
|
| 431 |
+
history.unshift(newItem);
|
| 432 |
+
|
| 433 |
+
// اگر بیشتر از 10 تا شد، آخری را حذف کن
|
| 434 |
+
if (history.length > 10) {
|
| 435 |
+
history.pop();
|
| 436 |
+
}
|
| 437 |
+
|
| 438 |
+
localStorage.setItem('alphaHistory', JSON.stringify(history));
|
| 439 |
+
loadHistory();
|
| 440 |
+
}
|
| 441 |
+
|
| 442 |
+
function openHistoryItem(item) {
|
| 443 |
+
const modal = document.getElementById('historyModal');
|
| 444 |
+
document.getElementById('modalLyricsBox').innerHTML = formatLyrics(item.lyrics);
|
| 445 |
+
document.getElementById('modalAudioWrapper').innerHTML = `<audio controls autoplay src="${item.audioUrl}"></audio>`;
|
| 446 |
+
document.getElementById('modalDownloadLink').href = item.audioUrl;
|
| 447 |
+
|
| 448 |
+
modal.classList.add('open');
|
| 449 |
+
}
|
| 450 |
+
|
| 451 |
+
function closeModal() {
|
| 452 |
+
const modal = document.getElementById('historyModal');
|
| 453 |
+
modal.classList.remove('open');
|
| 454 |
+
document.getElementById('modalAudioWrapper').innerHTML = ''; // توقف موزیک
|
| 455 |
+
}
|
| 456 |
+
|
| 457 |
+
// بستن مودال با کلیک بیرون
|
| 458 |
+
document.getElementById('historyModal').addEventListener('click', (e) => {
|
| 459 |
+
if (e.target === document.getElementById('historyModal')) closeModal();
|
| 460 |
+
});
|
| 461 |
+
|
| 462 |
+
// لود کردن سوابق هنگام شروع
|
| 463 |
+
loadHistory();
|
| 464 |
+
|
| 465 |
+
// --- فرآیند اصلی ---
|
| 466 |
processBtn.addEventListener('click', async () => {
|
| 467 |
if (!ideaInput.value.trim()) return alert("لطفا موضوع آهنگ را بنویسید");
|
| 468 |
|
|
|
|
| 469 |
processBtn.disabled = true;
|
| 470 |
step1.style.display = 'none';
|
| 471 |
loader.style.display = 'block';
|
| 472 |
|
| 473 |
try {
|
|
|
|
| 474 |
loaderText.innerText = "آلفا در حال نوشتن شعر و ملودی...";
|
| 475 |
|
| 476 |
const response = await fetch('/api/refine', {
|
|
|
|
| 485 |
const lyrics = data.lyrics;
|
| 486 |
const musicPrompt = data.music_prompt;
|
| 487 |
|
| 488 |
+
loaderText.innerText = "در حال ضبط آهنگ در استودیو آلفا...";
|
|
|
|
| 489 |
|
| 490 |
+
// آمادهسازی نمایش نتیجه جاری
|
| 491 |
formatLyricsForDisplay(lyrics);
|
| 492 |
playerWrapper.innerHTML = '';
|
| 493 |
|
| 494 |
const payload = [
|
| 495 |
+
getVal('model_select'), "custom", null, "unknown",
|
| 496 |
+
musicPrompt, lyrics,
|
|
|
|
|
|
|
| 497 |
0, "", "", "unknown",
|
| 498 |
+
getNum('steps_input'), getNum('cfg_input'),
|
| 499 |
+
true, getNum('seed_input'), null, -1,
|
| 500 |
+
getNum('batch_size'), null, null, 0, -1,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 501 |
"Fill the audio semantic mask based on the given conditions:",
|
| 502 |
1, "text2music", false, 0, 1, 3, "ode", "", "mp3", 0.85,
|
| 503 |
+
getChk('think_checkbox'), 2, 0, 0.9, "NO USER INPUT", true, true, true, null, false, true, false, false, 0.5, 8, null, [], false, null, null, null, null
|
|
|
|
| 504 |
];
|
| 505 |
|
| 506 |
const session_hash = Math.random().toString(36).substring(2);
|
|
|
|
| 507 |
const joinResp = await fetch(`${ACE_SPACE_URL}gradio_api/queue/join`, {
|
| 508 |
method: 'POST',
|
| 509 |
headers: { 'Content-Type': 'application/json' },
|
|
|
|
| 521 |
} else if (msg.msg === 'process_completed') {
|
| 522 |
eventSource.close();
|
| 523 |
loader.style.display = 'none';
|
| 524 |
+
handleAudioOutput(msg.output, lyrics, ideaInput.value);
|
| 525 |
}
|
| 526 |
};
|
| 527 |
+
eventSource.onerror = () => { throw new Error('ارتباط با سرور قطع شد.'); };
|
|
|
|
|
|
|
|
|
|
| 528 |
|
| 529 |
} catch (e) {
|
| 530 |
alert("خطا: " + e.message);
|
| 531 |
loader.style.display = 'none';
|
| 532 |
+
step1.style.display = 'block';
|
| 533 |
processBtn.disabled = false;
|
| 534 |
}
|
| 535 |
});
|
| 536 |
|
| 537 |
+
function handleAudioOutput(data, lyrics, idea) {
|
| 538 |
const processedUrls = new Set();
|
| 539 |
let hasResult = false;
|
| 540 |
|
| 541 |
function addAudio(url) {
|
| 542 |
const fullUrl = url.startsWith('http') ? url : ACE_SPACE_URL.replace(/\/$/, '') + url;
|
|
|
|
| 543 |
if (processedUrls.has(fullUrl)) return;
|
| 544 |
processedUrls.add(fullUrl);
|
|
|
|
| 545 |
hasResult = true;
|
| 546 |
|
| 547 |
+
// نمایش در نتیجه اصلی
|
| 548 |
if (processedUrls.size === 1) {
|
| 549 |
mainDownloadLink.href = fullUrl;
|
| 550 |
mainDownloadLink.style.display = 'inline-block';
|
| 551 |
+
// ذخیره در سوابق
|
| 552 |
+
saveToHistory(idea, lyrics, fullUrl);
|
| 553 |
}
|
| 554 |
|
| 555 |
const div = document.createElement('div');
|
|
|
|
| 577 |
}
|
| 578 |
}
|
| 579 |
|
| 580 |
+
function formatLyrics(text) {
|
| 581 |
+
return text.replace(/\[(.*?)\]/g, '<span class="lyrics-tag">[$1]</span>');
|
| 582 |
+
}
|
| 583 |
+
|
| 584 |
function formatLyricsForDisplay(text) {
|
| 585 |
+
finalLyricsBox.innerHTML = formatLyrics(text);
|
|
|
|
| 586 |
}
|
| 587 |
|
| 588 |
+
// انیمیشن پسزمینه
|
| 589 |
const canvas = document.getElementById('music-canvas');
|
| 590 |
const ctx = canvas.getContext('2d');
|
| 591 |
let t = 0;
|
| 592 |
+
function resize() { canvas.width = window.innerWidth; canvas.height = 400; }
|
|
|
|
|
|
|
|
|
|
|
|
|
| 593 |
window.addEventListener('resize', resize);
|
| 594 |
resize();
|
|
|
|
| 595 |
function anim() {
|
| 596 |
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
| 597 |
ctx.beginPath();
|
| 598 |
+
ctx.strokeStyle = "rgba(74, 108, 250, 0.1)";
|
| 599 |
ctx.lineWidth = 2;
|
| 600 |
for(let i=0; i<canvas.width; i+=20) {
|
| 601 |
ctx.moveTo(i, 0);
|