Update index.html
Browse files- index.html +10 -78
index.html
CHANGED
|
@@ -66,7 +66,6 @@
|
|
| 66 |
}
|
| 67 |
.subtitle { font-size: 0.9rem; color: var(--text-secondary); margin-top: 5px; margin-bottom: 10px; }
|
| 68 |
|
| 69 |
-
/* استایل بج وضعیت اشتراک */
|
| 70 |
.status-badge {
|
| 71 |
display: inline-block; padding: 6px 16px; border-radius: 20px;
|
| 72 |
font-size: 0.85rem; font-weight: 700; margin-top: 5px;
|
|
@@ -102,7 +101,6 @@
|
|
| 102 |
textarea { min-height: 120px; resize: vertical; }
|
| 103 |
textarea:focus, input:focus, select:focus { border-color: var(--accent-primary); background: #fff; }
|
| 104 |
|
| 105 |
-
/* دکمه اصلی */
|
| 106 |
@keyframes move-gradient {
|
| 107 |
0% { background-position: 0% 50%; }
|
| 108 |
50% { background-position: 100% 50%; }
|
|
@@ -134,7 +132,6 @@
|
|
| 134 |
}
|
| 135 |
.btn-outline:hover { border-color: var(--accent-primary); color: var(--accent-primary); }
|
| 136 |
|
| 137 |
-
/* تنظیمات پیشرفته */
|
| 138 |
.accordion {
|
| 139 |
background-color: var(--input-bg); color: var(--text-primary); cursor: pointer; padding: 15px; width: 100%; border: none;
|
| 140 |
text-align: right; outline: none; font-size: 0.95rem; font-weight: 600; border-radius: 12px;
|
|
@@ -153,10 +150,8 @@
|
|
| 153 |
.checkbox-wrapper { display: flex; align-items: center; gap: 10px; margin-top: 5px; padding-bottom: 15px; border-top: 1px solid #e2e8f0; padding-top: 15px; }
|
| 154 |
.settings-group label { font-size: 0.8rem; color: var(--text-secondary); display: block; margin-bottom: 6px; font-weight: 500; }
|
| 155 |
|
| 156 |
-
/* پلیر و متن */
|
| 157 |
.player-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px solid var(--panel-border); }
|
| 158 |
|
| 159 |
-
/* استایل دکمه دانلود */
|
| 160 |
.download-btn-style {
|
| 161 |
background-color: rgba(74, 108, 250, 0.1); color: var(--accent-primary); text-decoration: none; font-size: 0.9rem;
|
| 162 |
font-weight: 700; padding: 8px 16px; border-radius: 10px; transition: 0.2s; display: inline-flex; align-items: center; gap: 5px; cursor: pointer; border: none;
|
|
@@ -173,7 +168,6 @@
|
|
| 173 |
}
|
| 174 |
.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; }
|
| 175 |
|
| 176 |
-
/* لودر */
|
| 177 |
#loader { display: none; text-align: center; padding: 20px; }
|
| 178 |
.wave-bars { display: flex; justify-content: center; gap: 4px; height: 30px; align-items: flex-end; }
|
| 179 |
.bar { width: 5px; background: var(--accent-primary); animation: jump 1s infinite; border-radius: 2px; }
|
|
@@ -182,7 +176,6 @@
|
|
| 182 |
.bar:nth-child(4) { animation-delay: 0.3s; height: 50%; }
|
| 183 |
@keyframes jump { 0%, 100% { height: 20%; } 50% { height: 100%; } }
|
| 184 |
|
| 185 |
-
/* --- استایلهای تاریخچه و حذف --- */
|
| 186 |
#historySection { margin-top: 30px; width: 100%; }
|
| 187 |
.history-title { font-size: 1.2rem; font-weight: 800; color: var(--text-primary); margin-bottom: 15px; display: flex; align-items: center; gap: 10px; }
|
| 188 |
.history-list { display: grid; gap: 12px; }
|
|
@@ -207,7 +200,6 @@
|
|
| 207 |
.history-card:hover .h-delete { opacity: 1; transform: scale(1); }
|
| 208 |
.h-delete:hover { background: var(--danger-color); color: white; transform: scale(1.1); }
|
| 209 |
|
| 210 |
-
/* مودالها */
|
| 211 |
.modal-overlay {
|
| 212 |
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
| 213 |
background: rgba(0, 0, 0, 0.6); backdrop-filter: blur(8px);
|
|
@@ -233,7 +225,6 @@
|
|
| 233 |
.btn-confirm { background: var(--danger-color); color: white; box-shadow: 0 5px 15px rgba(229, 62, 62, 0.3); }
|
| 234 |
.btn-confirm:hover { transform: translateY(-2px); box-shadow: 0 8px 20px rgba(229, 62, 62, 0.4); }
|
| 235 |
|
| 236 |
-
/* مودال ارتقا */
|
| 237 |
@keyframes pulse-gold { 0% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.7); } 70% { box-shadow: 0 0 0 15px rgba(255, 193, 7, 0); } 100% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0); } }
|
| 238 |
.upgrade-icon {
|
| 239 |
width: 70px; height: 70px;
|
|
@@ -358,15 +349,13 @@
|
|
| 358 |
|
| 359 |
<script>
|
| 360 |
const ACE_SPACE_URL = "https://ace-step-ace-step-v1-5.hf.space/";
|
| 361 |
-
// مشخصات نسخه پولی
|
| 362 |
const PREMIUM_PAGE_ID = '1149636';
|
| 363 |
const PREMIUM_URL = '#/nav/online/news/getSingle/1149636/eyJpdiI6InZSVUdlLzBlR0FzOHZJdXFZeWhER0E9PSIsInZhbHVlIjoiWFhqRXBLc29vSFpHdk9nYmRjZGVuWHRHRHVSZHRlTG1BUENLaE5mNXBNVVRGWFg3ZWN0djJ5K1dIY1RqTHJGaCIsIm1hYyI6IjIzYzFlZTMwYmVmMTdkYjQ0YTQ4YWMxNmFhN2RmNWQ2OTU0MjI0ZWVlZGI3ZjJjMjhkNmQxNjM4MDFlZTIxNmUiLCJ0YWciOiIifQ==/20934991';
|
| 364 |
|
| 365 |
let db;
|
| 366 |
let songToDeleteId = null;
|
| 367 |
-
let userSubscriptionStatus = 'free';
|
| 368 |
|
| 369 |
-
// المانها
|
| 370 |
const ideaInput = document.getElementById('ideaInput');
|
| 371 |
const processBtn = document.getElementById('processBtn');
|
| 372 |
const step1 = document.getElementById('step1');
|
|
@@ -397,18 +386,15 @@
|
|
| 397 |
});
|
| 398 |
}
|
| 399 |
|
| 400 |
-
// --- مدیریت اثر انگشت (Fingerprint) ---
|
| 401 |
function getFingerprint() {
|
| 402 |
let fp = localStorage.getItem('user_fingerprint');
|
| 403 |
if (!fp) {
|
| 404 |
-
// تولید یک شناسه تصادفی و ذخیره آن
|
| 405 |
fp = 'user_' + Math.random().toString(36).substr(2, 9) + Date.now().toString(36);
|
| 406 |
localStorage.setItem('user_fingerprint', fp);
|
| 407 |
}
|
| 408 |
return fp;
|
| 409 |
}
|
| 410 |
|
| 411 |
-
// --- مدیریت اشتراک و ارتباط با آیفریم مادر ---
|
| 412 |
function isUserPaid(userObject) {
|
| 413 |
return userObject && userObject.isLogin && userObject.accessible_pages &&
|
| 414 |
(userObject.accessible_pages.includes(PREMIUM_PAGE_ID) ||
|
|
@@ -426,7 +412,6 @@
|
|
| 426 |
}
|
| 427 |
}
|
| 428 |
|
| 429 |
-
// شنونده پیام از آیفریم مادر
|
| 430 |
window.addEventListener('message', (event) => {
|
| 431 |
if (event.data && event.data.type === 'USER_STATUS_RESPONSE') {
|
| 432 |
try {
|
|
@@ -439,26 +424,21 @@
|
|
| 439 |
}
|
| 440 |
});
|
| 441 |
|
| 442 |
-
// درخواست وضعیت کاربر در شروع برنامه
|
| 443 |
parent.postMessage({ type: 'REQUEST_USER_STATUS' }, '*');
|
| 444 |
|
| 445 |
-
// مدیریت کلیک دکمه دانلود (تابع امنیتی)
|
| 446 |
function handleSecureDownload(url, e) {
|
| 447 |
if (e) e.preventDefault();
|
| 448 |
|
| 449 |
if (userSubscriptionStatus === 'free') {
|
| 450 |
-
// تنظیم متن مودال برای دانلود
|
| 451 |
upgradeModalTitle.innerText = "دانلود مخصوص اعضای ویژه";
|
| 452 |
upgradeModalText.innerText = "برای دانلود آهنگهای ساخته شده با کیفیت اصلی و بدون محدودیت، لطفا حساب کاربری خود را به نسخه نامحدود ارتقا دهید.";
|
| 453 |
upgradeModal.classList.add('open');
|
| 454 |
} else {
|
| 455 |
-
// ارسال درخواست دانلود به آیفریم مادر
|
| 456 |
parent.postMessage({
|
| 457 |
type: 'INITIATE_DOWNLOAD_FROM_URL',
|
| 458 |
payload: { audioUrl: url }
|
| 459 |
}, '*');
|
| 460 |
|
| 461 |
-
// تغییر موقت متن دکمه برای فیدبک
|
| 462 |
const btn = e ? e.target.closest('button') : null;
|
| 463 |
if(btn) {
|
| 464 |
const originalText = btn.innerHTML;
|
|
@@ -468,7 +448,6 @@
|
|
| 468 |
}
|
| 469 |
}
|
| 470 |
|
| 471 |
-
// دکمه ارتقا در مودال
|
| 472 |
document.getElementById('btnGoPremium').addEventListener('click', () => {
|
| 473 |
parent.postMessage({
|
| 474 |
type: 'NAVIGATE_TO_PREMIUM',
|
|
@@ -480,7 +459,6 @@
|
|
| 480 |
upgradeModal.classList.remove('open');
|
| 481 |
}
|
| 482 |
|
| 483 |
-
// --- مدیریت دیتابیس ---
|
| 484 |
function initDB() {
|
| 485 |
const request = indexedDB.open("alphaMusicDB", 1);
|
| 486 |
request.onerror = (event) => console.error("IndexedDB error:", event);
|
|
@@ -490,7 +468,6 @@
|
|
| 490 |
|
| 491 |
async function saveToHistory(idea, lyrics, audioUrl) {
|
| 492 |
try {
|
| 493 |
-
// برای ذخیره در DB باید فایل را فچ کنیم
|
| 494 |
const response = await fetch(audioUrl);
|
| 495 |
const audioBlob = await response.blob();
|
| 496 |
const newItem = {
|
|
@@ -501,7 +478,6 @@
|
|
| 501 |
const store = transaction.objectStore("songs");
|
| 502 |
store.add(newItem);
|
| 503 |
|
| 504 |
-
// شرط نگهداری فقط ۱۰ آیتم آخر
|
| 505 |
const countReq = store.count();
|
| 506 |
countReq.onsuccess = () => {
|
| 507 |
if (countReq.result > 10) store.openCursor().onsuccess = (e) => { if(e.target.result) e.target.result.delete(); };
|
|
@@ -536,9 +512,7 @@
|
|
| 536 |
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18"></path><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path></svg>
|
| 537 |
</div>
|
| 538 |
`;
|
| 539 |
-
// کلیک روی خود کارت برای پخش
|
| 540 |
div.onclick = (e) => {
|
| 541 |
-
// اگر روی دکمه حذف کلیک نشده باشد
|
| 542 |
if (!e.target.closest('.h-delete')) {
|
| 543 |
openHistoryItem(item);
|
| 544 |
}
|
|
@@ -548,9 +522,8 @@
|
|
| 548 |
};
|
| 549 |
}
|
| 550 |
|
| 551 |
-
// --- توابع حذف ---
|
| 552 |
function askToDelete(event, id) {
|
| 553 |
-
event.stopPropagation();
|
| 554 |
songToDeleteId = id;
|
| 555 |
deleteModal.classList.add('open');
|
| 556 |
}
|
|
@@ -572,7 +545,6 @@
|
|
| 572 |
}
|
| 573 |
}
|
| 574 |
|
| 575 |
-
// بستن مودال با کلیک بیرون
|
| 576 |
deleteModal.addEventListener('click', (e) => {
|
| 577 |
if (e.target === deleteModal) closeDeleteModal();
|
| 578 |
});
|
|
@@ -589,7 +561,6 @@
|
|
| 589 |
headerText.innerHTML = `<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> آهنگ آرشیو شده`;
|
| 590 |
headerText.style.color = 'var(--accent-primary)';
|
| 591 |
|
| 592 |
-
// تنظیم دکمه دانلود
|
| 593 |
mainDownloadBtn.style.display = 'inline-flex';
|
| 594 |
mainDownloadBtn.onclick = (e) => handleSecureDownload(audioURL, e);
|
| 595 |
|
|
@@ -602,33 +573,12 @@
|
|
| 602 |
|
| 603 |
initDB();
|
| 604 |
|
| 605 |
-
// --- توابع تبدیل فرمت صوتی (Client-Side WAV Converter) ---
|
| 606 |
function writeString(view, offset, string) {
|
| 607 |
for (let i = 0; i < string.length; i++) {
|
| 608 |
view.setUint8(offset + i, string.charCodeAt(i));
|
| 609 |
}
|
| 610 |
}
|
| 611 |
|
| 612 |
-
function floatTo16BitPCM(output, offset, input) {
|
| 613 |
-
for (let i = 0; i < input.length; i++, offset += 2) {
|
| 614 |
-
let s = Math.max(-1, Math.min(1, input[i]));
|
| 615 |
-
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
|
| 616 |
-
}
|
| 617 |
-
}
|
| 618 |
-
|
| 619 |
-
function interleave(inputL, inputR) {
|
| 620 |
-
const length = inputL.length + inputR.length;
|
| 621 |
-
const result = new Float32Array(length);
|
| 622 |
-
let index = 0;
|
| 623 |
-
let inputIndex = 0;
|
| 624 |
-
while (index < length) {
|
| 625 |
-
result[index++] = inputL[inputIndex];
|
| 626 |
-
result[index++] = inputR[inputIndex];
|
| 627 |
-
inputIndex++;
|
| 628 |
-
}
|
| 629 |
-
return result;
|
| 630 |
-
}
|
| 631 |
-
|
| 632 |
function bufferToWave(abuffer, len) {
|
| 633 |
let numOfChan = abuffer.numberOfChannels;
|
| 634 |
let length = len * numOfChan * 2 + 44;
|
|
@@ -638,7 +588,6 @@
|
|
| 638 |
let offset = 0;
|
| 639 |
let pos = 0;
|
| 640 |
|
| 641 |
-
// write WAVE header
|
| 642 |
writeString(view, 0, 'RIFF');
|
| 643 |
view.setUint32(4, 36 + len * numOfChan * 2, true);
|
| 644 |
writeString(view, 8, 'WAVE');
|
|
@@ -653,12 +602,10 @@
|
|
| 653 |
writeString(view, 36, 'data');
|
| 654 |
view.setUint32(40, len * numOfChan * 2, true);
|
| 655 |
|
| 656 |
-
// write interleaved data
|
| 657 |
for(i = 0; i < abuffer.numberOfChannels; i++)
|
| 658 |
channels.push(abuffer.getChannelData(i));
|
| 659 |
|
| 660 |
offset = 44;
|
| 661 |
-
// Interleave logic handled simply here for 1 or 2 channels
|
| 662 |
if (numOfChan === 2) {
|
| 663 |
while(pos < len) {
|
| 664 |
for(i = 0; i < numOfChan; i++) {
|
|
@@ -688,7 +635,6 @@
|
|
| 688 |
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
| 689 |
audioContext.decodeAudioData(e.target.result, function(buffer) {
|
| 690 |
const wavBlob = bufferToWave(buffer, buffer.length);
|
| 691 |
-
// ساخت فایل جدید با فرمت wav
|
| 692 |
const wavFile = new File([wavBlob], file.name.replace(/\.[^/.]+$/, "") + ".wav", { type: "audio/wav" });
|
| 693 |
resolve(wavFile);
|
| 694 |
}, function(e){
|
|
@@ -701,7 +647,6 @@
|
|
| 701 |
});
|
| 702 |
}
|
| 703 |
|
| 704 |
-
// --- تابع آپلود فایل صوتی به Gradio ---
|
| 705 |
async function uploadAudioFile(file) {
|
| 706 |
const formData = new FormData();
|
| 707 |
formData.append("files", file);
|
|
@@ -728,7 +673,6 @@
|
|
| 728 |
}
|
| 729 |
}
|
| 730 |
|
| 731 |
-
// --- فرآیند اصلی ---
|
| 732 |
processBtn.addEventListener('click', async () => {
|
| 733 |
if (!ideaInput.value.trim()) return alert("لطفا موضوع آهنگ را بنویسید");
|
| 734 |
|
|
@@ -738,35 +682,22 @@
|
|
| 738 |
loader.style.display = 'block';
|
| 739 |
|
| 740 |
try {
|
| 741 |
-
// 1. آپلود و تبدیل فایل نمونه
|
| 742 |
const audioInput = document.getElementById('audio_reference');
|
| 743 |
let uploadedAudioObj = null;
|
| 744 |
|
| 745 |
if (audioInput.files.length > 0) {
|
| 746 |
let fileToUpload = audioInput.files[0];
|
| 747 |
-
|
| 748 |
-
// بررسی و تبدیل به WAV
|
| 749 |
if (fileToUpload.type !== 'audio/wav' && !fileToUpload.name.toLowerCase().endsWith('.wav')) {
|
| 750 |
loaderText.innerText = "در حال تبدیل فرمت فایل صوتی به WAV...";
|
| 751 |
-
|
| 752 |
-
fileToUpload = await convertAudioToWav(fileToUpload);
|
| 753 |
-
console.log("Converted to WAV:", fileToUpload.name, fileToUpload.size);
|
| 754 |
-
} catch (err) {
|
| 755 |
-
console.error(err);
|
| 756 |
-
alert("خطا در تبدیل فایل صوتی. لطفاً فایل WAV معتبر آپلود کنید.");
|
| 757 |
-
throw new Error("Audio conversion failed");
|
| 758 |
-
}
|
| 759 |
}
|
| 760 |
-
|
| 761 |
loaderText.innerText = "در حال آپلود فایل نمونه...";
|
| 762 |
uploadedAudioObj = await uploadAudioFile(fileToUpload);
|
| 763 |
}
|
| 764 |
|
| 765 |
-
// 2. درخواست متن و پرامپت
|
| 766 |
loaderText.innerText = "آلفا در حال نوشتن شعر و ملودی...";
|
| 767 |
const isInstrumental = getChk('instrumental_chk');
|
| 768 |
|
| 769 |
-
// ارسال درخواست به سرور با فینگرپرینت و وضعیت اشتراک
|
| 770 |
const response = await fetch('/api/refine', {
|
| 771 |
method: 'POST',
|
| 772 |
headers: {'Content-Type': 'application/json'},
|
|
@@ -778,7 +709,6 @@
|
|
| 778 |
})
|
| 779 |
});
|
| 780 |
|
| 781 |
-
// بررسی خطای محدودیت روزانه (429)
|
| 782 |
if (response.status === 429) {
|
| 783 |
loader.style.display = 'none';
|
| 784 |
step1.style.display = 'block';
|
|
@@ -786,7 +716,7 @@
|
|
| 786 |
processBtn.disabled = false;
|
| 787 |
|
| 788 |
upgradeModalTitle.innerText = "محدودیت ساخت روزانه";
|
| 789 |
-
upgradeModalText.innerText = "شما به سقف ۵ آهنگ رایگان در روز رسیدهاید.
|
| 790 |
upgradeModal.classList.add('open');
|
| 791 |
return;
|
| 792 |
}
|
|
@@ -799,7 +729,10 @@
|
|
| 799 |
|
| 800 |
const finalLyrics = isInstrumental ? "" : lyrics;
|
| 801 |
|
| 802 |
-
//
|
|
|
|
|
|
|
|
|
|
| 803 |
const payload = [
|
| 804 |
getVal('model_select'),
|
| 805 |
"custom",
|
|
@@ -810,7 +743,7 @@
|
|
| 810 |
0, "", "", "unknown",
|
| 811 |
getNum('steps_input'), getNum('cfg_input'), true, getNum('seed_input'), null, -1,
|
| 812 |
getNum('batch_size'), null, null, 0, -1, "Fill the audio semantic mask based on the given conditions:",
|
| 813 |
-
1,
|
| 814 |
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
|
| 815 |
];
|
| 816 |
|
|
@@ -852,7 +785,6 @@
|
|
| 852 |
if (processedUrls.size === 1) {
|
| 853 |
mainDownloadBtn.style.display = 'inline-flex';
|
| 854 |
mainDownloadBtn.onclick = (e) => handleSecureDownload(fullUrl, e);
|
| 855 |
-
|
| 856 |
saveToHistory(idea, lyrics, fullUrl);
|
| 857 |
}
|
| 858 |
playerWrapper.innerHTML += `<div class="audio-item"><audio controls autoplay src="${fullUrl}"></audio></div>`;
|
|
@@ -881,7 +813,7 @@
|
|
| 881 |
}
|
| 882 |
}
|
| 883 |
|
| 884 |
-
function formatLyrics(text) { return text.replace(/\[(.*?)\]/g, '<span class="lyrics-tag">[$1]</span>'); }
|
| 885 |
|
| 886 |
const canvas = document.getElementById('music-canvas');
|
| 887 |
const ctx = canvas.getContext('2d');
|
|
|
|
| 66 |
}
|
| 67 |
.subtitle { font-size: 0.9rem; color: var(--text-secondary); margin-top: 5px; margin-bottom: 10px; }
|
| 68 |
|
|
|
|
| 69 |
.status-badge {
|
| 70 |
display: inline-block; padding: 6px 16px; border-radius: 20px;
|
| 71 |
font-size: 0.85rem; font-weight: 700; margin-top: 5px;
|
|
|
|
| 101 |
textarea { min-height: 120px; resize: vertical; }
|
| 102 |
textarea:focus, input:focus, select:focus { border-color: var(--accent-primary); background: #fff; }
|
| 103 |
|
|
|
|
| 104 |
@keyframes move-gradient {
|
| 105 |
0% { background-position: 0% 50%; }
|
| 106 |
50% { background-position: 100% 50%; }
|
|
|
|
| 132 |
}
|
| 133 |
.btn-outline:hover { border-color: var(--accent-primary); color: var(--accent-primary); }
|
| 134 |
|
|
|
|
| 135 |
.accordion {
|
| 136 |
background-color: var(--input-bg); color: var(--text-primary); cursor: pointer; padding: 15px; width: 100%; border: none;
|
| 137 |
text-align: right; outline: none; font-size: 0.95rem; font-weight: 600; border-radius: 12px;
|
|
|
|
| 150 |
.checkbox-wrapper { display: flex; align-items: center; gap: 10px; margin-top: 5px; padding-bottom: 15px; border-top: 1px solid #e2e8f0; padding-top: 15px; }
|
| 151 |
.settings-group label { font-size: 0.8rem; color: var(--text-secondary); display: block; margin-bottom: 6px; font-weight: 500; }
|
| 152 |
|
|
|
|
| 153 |
.player-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px solid var(--panel-border); }
|
| 154 |
|
|
|
|
| 155 |
.download-btn-style {
|
| 156 |
background-color: rgba(74, 108, 250, 0.1); color: var(--accent-primary); text-decoration: none; font-size: 0.9rem;
|
| 157 |
font-weight: 700; padding: 8px 16px; border-radius: 10px; transition: 0.2s; display: inline-flex; align-items: center; gap: 5px; cursor: pointer; border: none;
|
|
|
|
| 168 |
}
|
| 169 |
.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; }
|
| 170 |
|
|
|
|
| 171 |
#loader { display: none; text-align: center; padding: 20px; }
|
| 172 |
.wave-bars { display: flex; justify-content: center; gap: 4px; height: 30px; align-items: flex-end; }
|
| 173 |
.bar { width: 5px; background: var(--accent-primary); animation: jump 1s infinite; border-radius: 2px; }
|
|
|
|
| 176 |
.bar:nth-child(4) { animation-delay: 0.3s; height: 50%; }
|
| 177 |
@keyframes jump { 0%, 100% { height: 20%; } 50% { height: 100%; } }
|
| 178 |
|
|
|
|
| 179 |
#historySection { margin-top: 30px; width: 100%; }
|
| 180 |
.history-title { font-size: 1.2rem; font-weight: 800; color: var(--text-primary); margin-bottom: 15px; display: flex; align-items: center; gap: 10px; }
|
| 181 |
.history-list { display: grid; gap: 12px; }
|
|
|
|
| 200 |
.history-card:hover .h-delete { opacity: 1; transform: scale(1); }
|
| 201 |
.h-delete:hover { background: var(--danger-color); color: white; transform: scale(1.1); }
|
| 202 |
|
|
|
|
| 203 |
.modal-overlay {
|
| 204 |
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
| 205 |
background: rgba(0, 0, 0, 0.6); backdrop-filter: blur(8px);
|
|
|
|
| 225 |
.btn-confirm { background: var(--danger-color); color: white; box-shadow: 0 5px 15px rgba(229, 62, 62, 0.3); }
|
| 226 |
.btn-confirm:hover { transform: translateY(-2px); box-shadow: 0 8px 20px rgba(229, 62, 62, 0.4); }
|
| 227 |
|
|
|
|
| 228 |
@keyframes pulse-gold { 0% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.7); } 70% { box-shadow: 0 0 0 15px rgba(255, 193, 7, 0); } 100% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0); } }
|
| 229 |
.upgrade-icon {
|
| 230 |
width: 70px; height: 70px;
|
|
|
|
| 349 |
|
| 350 |
<script>
|
| 351 |
const ACE_SPACE_URL = "https://ace-step-ace-step-v1-5.hf.space/";
|
|
|
|
| 352 |
const PREMIUM_PAGE_ID = '1149636';
|
| 353 |
const PREMIUM_URL = '#/nav/online/news/getSingle/1149636/eyJpdiI6InZSVUdlLzBlR0FzOHZJdXFZeWhER0E9PSIsInZhbHVlIjoiWFhqRXBLc29vSFpHdk9nYmRjZGVuWHRHRHVSZHRlTG1BUENLaE5mNXBNVVRGWFg3ZWN0djJ5K1dIY1RqTHJGaCIsIm1hYyI6IjIzYzFlZTMwYmVmMTdkYjQ0YTQ4YWMxNmFhN2RmNWQ2OTU0MjI0ZWVlZGI3ZjJjMjhkNmQxNjM4MDFlZTIxNmUiLCJ0YWciOiIifQ==/20934991';
|
| 354 |
|
| 355 |
let db;
|
| 356 |
let songToDeleteId = null;
|
| 357 |
+
let userSubscriptionStatus = 'free';
|
| 358 |
|
|
|
|
| 359 |
const ideaInput = document.getElementById('ideaInput');
|
| 360 |
const processBtn = document.getElementById('processBtn');
|
| 361 |
const step1 = document.getElementById('step1');
|
|
|
|
| 386 |
});
|
| 387 |
}
|
| 388 |
|
|
|
|
| 389 |
function getFingerprint() {
|
| 390 |
let fp = localStorage.getItem('user_fingerprint');
|
| 391 |
if (!fp) {
|
|
|
|
| 392 |
fp = 'user_' + Math.random().toString(36).substr(2, 9) + Date.now().toString(36);
|
| 393 |
localStorage.setItem('user_fingerprint', fp);
|
| 394 |
}
|
| 395 |
return fp;
|
| 396 |
}
|
| 397 |
|
|
|
|
| 398 |
function isUserPaid(userObject) {
|
| 399 |
return userObject && userObject.isLogin && userObject.accessible_pages &&
|
| 400 |
(userObject.accessible_pages.includes(PREMIUM_PAGE_ID) ||
|
|
|
|
| 412 |
}
|
| 413 |
}
|
| 414 |
|
|
|
|
| 415 |
window.addEventListener('message', (event) => {
|
| 416 |
if (event.data && event.data.type === 'USER_STATUS_RESPONSE') {
|
| 417 |
try {
|
|
|
|
| 424 |
}
|
| 425 |
});
|
| 426 |
|
|
|
|
| 427 |
parent.postMessage({ type: 'REQUEST_USER_STATUS' }, '*');
|
| 428 |
|
|
|
|
| 429 |
function handleSecureDownload(url, e) {
|
| 430 |
if (e) e.preventDefault();
|
| 431 |
|
| 432 |
if (userSubscriptionStatus === 'free') {
|
|
|
|
| 433 |
upgradeModalTitle.innerText = "دانلود مخصوص اعضای ویژه";
|
| 434 |
upgradeModalText.innerText = "برای دانلود آهنگهای ساخته شده با کیفیت اصلی و بدون محدودیت، لطفا حساب کاربری خود را به نسخه نامحدود ارتقا دهید.";
|
| 435 |
upgradeModal.classList.add('open');
|
| 436 |
} else {
|
|
|
|
| 437 |
parent.postMessage({
|
| 438 |
type: 'INITIATE_DOWNLOAD_FROM_URL',
|
| 439 |
payload: { audioUrl: url }
|
| 440 |
}, '*');
|
| 441 |
|
|
|
|
| 442 |
const btn = e ? e.target.closest('button') : null;
|
| 443 |
if(btn) {
|
| 444 |
const originalText = btn.innerHTML;
|
|
|
|
| 448 |
}
|
| 449 |
}
|
| 450 |
|
|
|
|
| 451 |
document.getElementById('btnGoPremium').addEventListener('click', () => {
|
| 452 |
parent.postMessage({
|
| 453 |
type: 'NAVIGATE_TO_PREMIUM',
|
|
|
|
| 459 |
upgradeModal.classList.remove('open');
|
| 460 |
}
|
| 461 |
|
|
|
|
| 462 |
function initDB() {
|
| 463 |
const request = indexedDB.open("alphaMusicDB", 1);
|
| 464 |
request.onerror = (event) => console.error("IndexedDB error:", event);
|
|
|
|
| 468 |
|
| 469 |
async function saveToHistory(idea, lyrics, audioUrl) {
|
| 470 |
try {
|
|
|
|
| 471 |
const response = await fetch(audioUrl);
|
| 472 |
const audioBlob = await response.blob();
|
| 473 |
const newItem = {
|
|
|
|
| 478 |
const store = transaction.objectStore("songs");
|
| 479 |
store.add(newItem);
|
| 480 |
|
|
|
|
| 481 |
const countReq = store.count();
|
| 482 |
countReq.onsuccess = () => {
|
| 483 |
if (countReq.result > 10) store.openCursor().onsuccess = (e) => { if(e.target.result) e.target.result.delete(); };
|
|
|
|
| 512 |
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18"></path><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path></svg>
|
| 513 |
</div>
|
| 514 |
`;
|
|
|
|
| 515 |
div.onclick = (e) => {
|
|
|
|
| 516 |
if (!e.target.closest('.h-delete')) {
|
| 517 |
openHistoryItem(item);
|
| 518 |
}
|
|
|
|
| 522 |
};
|
| 523 |
}
|
| 524 |
|
|
|
|
| 525 |
function askToDelete(event, id) {
|
| 526 |
+
event.stopPropagation();
|
| 527 |
songToDeleteId = id;
|
| 528 |
deleteModal.classList.add('open');
|
| 529 |
}
|
|
|
|
| 545 |
}
|
| 546 |
}
|
| 547 |
|
|
|
|
| 548 |
deleteModal.addEventListener('click', (e) => {
|
| 549 |
if (e.target === deleteModal) closeDeleteModal();
|
| 550 |
});
|
|
|
|
| 561 |
headerText.innerHTML = `<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> آهنگ آرشیو شده`;
|
| 562 |
headerText.style.color = 'var(--accent-primary)';
|
| 563 |
|
|
|
|
| 564 |
mainDownloadBtn.style.display = 'inline-flex';
|
| 565 |
mainDownloadBtn.onclick = (e) => handleSecureDownload(audioURL, e);
|
| 566 |
|
|
|
|
| 573 |
|
| 574 |
initDB();
|
| 575 |
|
|
|
|
| 576 |
function writeString(view, offset, string) {
|
| 577 |
for (let i = 0; i < string.length; i++) {
|
| 578 |
view.setUint8(offset + i, string.charCodeAt(i));
|
| 579 |
}
|
| 580 |
}
|
| 581 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 582 |
function bufferToWave(abuffer, len) {
|
| 583 |
let numOfChan = abuffer.numberOfChannels;
|
| 584 |
let length = len * numOfChan * 2 + 44;
|
|
|
|
| 588 |
let offset = 0;
|
| 589 |
let pos = 0;
|
| 590 |
|
|
|
|
| 591 |
writeString(view, 0, 'RIFF');
|
| 592 |
view.setUint32(4, 36 + len * numOfChan * 2, true);
|
| 593 |
writeString(view, 8, 'WAVE');
|
|
|
|
| 602 |
writeString(view, 36, 'data');
|
| 603 |
view.setUint32(40, len * numOfChan * 2, true);
|
| 604 |
|
|
|
|
| 605 |
for(i = 0; i < abuffer.numberOfChannels; i++)
|
| 606 |
channels.push(abuffer.getChannelData(i));
|
| 607 |
|
| 608 |
offset = 44;
|
|
|
|
| 609 |
if (numOfChan === 2) {
|
| 610 |
while(pos < len) {
|
| 611 |
for(i = 0; i < numOfChan; i++) {
|
|
|
|
| 635 |
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
| 636 |
audioContext.decodeAudioData(e.target.result, function(buffer) {
|
| 637 |
const wavBlob = bufferToWave(buffer, buffer.length);
|
|
|
|
| 638 |
const wavFile = new File([wavBlob], file.name.replace(/\.[^/.]+$/, "") + ".wav", { type: "audio/wav" });
|
| 639 |
resolve(wavFile);
|
| 640 |
}, function(e){
|
|
|
|
| 647 |
});
|
| 648 |
}
|
| 649 |
|
|
|
|
| 650 |
async function uploadAudioFile(file) {
|
| 651 |
const formData = new FormData();
|
| 652 |
formData.append("files", file);
|
|
|
|
| 673 |
}
|
| 674 |
}
|
| 675 |
|
|
|
|
| 676 |
processBtn.addEventListener('click', async () => {
|
| 677 |
if (!ideaInput.value.trim()) return alert("لطفا موضوع آهنگ را بنویسید");
|
| 678 |
|
|
|
|
| 682 |
loader.style.display = 'block';
|
| 683 |
|
| 684 |
try {
|
|
|
|
| 685 |
const audioInput = document.getElementById('audio_reference');
|
| 686 |
let uploadedAudioObj = null;
|
| 687 |
|
| 688 |
if (audioInput.files.length > 0) {
|
| 689 |
let fileToUpload = audioInput.files[0];
|
|
|
|
|
|
|
| 690 |
if (fileToUpload.type !== 'audio/wav' && !fileToUpload.name.toLowerCase().endsWith('.wav')) {
|
| 691 |
loaderText.innerText = "در حال تبدیل فرمت فایل صوتی به WAV...";
|
| 692 |
+
fileToUpload = await convertAudioToWav(fileToUpload);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 693 |
}
|
|
|
|
| 694 |
loaderText.innerText = "در حال آپلود فایل نمونه...";
|
| 695 |
uploadedAudioObj = await uploadAudioFile(fileToUpload);
|
| 696 |
}
|
| 697 |
|
|
|
|
| 698 |
loaderText.innerText = "آلفا در حال نوشتن شعر و ملودی...";
|
| 699 |
const isInstrumental = getChk('instrumental_chk');
|
| 700 |
|
|
|
|
| 701 |
const response = await fetch('/api/refine', {
|
| 702 |
method: 'POST',
|
| 703 |
headers: {'Content-Type': 'application/json'},
|
|
|
|
| 709 |
})
|
| 710 |
});
|
| 711 |
|
|
|
|
| 712 |
if (response.status === 429) {
|
| 713 |
loader.style.display = 'none';
|
| 714 |
step1.style.display = 'block';
|
|
|
|
| 716 |
processBtn.disabled = false;
|
| 717 |
|
| 718 |
upgradeModalTitle.innerText = "محدودیت ساخت روزانه";
|
| 719 |
+
upgradeModalText.innerText = "شما به سقف ۵ آهنگ رایگان در روز رسیدهاید.";
|
| 720 |
upgradeModal.classList.add('open');
|
| 721 |
return;
|
| 722 |
}
|
|
|
|
| 729 |
|
| 730 |
const finalLyrics = isInstrumental ? "" : lyrics;
|
| 731 |
|
| 732 |
+
// ** LOGIC CORRECTION: Determine Generation Mode **
|
| 733 |
+
// If audio is uploaded, we must switch the task from "text2music" to "prompt" (or equivalent for conditioning)
|
| 734 |
+
const taskType = uploadedAudioObj ? "prompt" : "text2music";
|
| 735 |
+
|
| 736 |
const payload = [
|
| 737 |
getVal('model_select'),
|
| 738 |
"custom",
|
|
|
|
| 743 |
0, "", "", "unknown",
|
| 744 |
getNum('steps_input'), getNum('cfg_input'), true, getNum('seed_input'), null, -1,
|
| 745 |
getNum('batch_size'), null, null, 0, -1, "Fill the audio semantic mask based on the given conditions:",
|
| 746 |
+
1, taskType, false, 0, 1, 3, "ode", "", "mp3", 0.85,
|
| 747 |
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
|
| 748 |
];
|
| 749 |
|
|
|
|
| 785 |
if (processedUrls.size === 1) {
|
| 786 |
mainDownloadBtn.style.display = 'inline-flex';
|
| 787 |
mainDownloadBtn.onclick = (e) => handleSecureDownload(fullUrl, e);
|
|
|
|
| 788 |
saveToHistory(idea, lyrics, fullUrl);
|
| 789 |
}
|
| 790 |
playerWrapper.innerHTML += `<div class="audio-item"><audio controls autoplay src="${fullUrl}"></audio></div>`;
|
|
|
|
| 813 |
}
|
| 814 |
}
|
| 815 |
|
| 816 |
+
function formatLyrics(text) { return text ? text.replace(/\[(.*?)\]/g, '<span class="lyrics-tag">[$1]</span>') : "متن آهنگ: (بیکلام)"; }
|
| 817 |
|
| 818 |
const canvas = document.getElementById('music-canvas');
|
| 819 |
const ctx = canvas.getContext('2d');
|