Update index.html
Browse files- index.html +70 -143
index.html
CHANGED
|
@@ -401,7 +401,6 @@
|
|
| 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 |
}
|
|
@@ -426,7 +425,6 @@
|
|
| 426 |
}
|
| 427 |
}
|
| 428 |
|
| 429 |
-
// شنونده پیام از آیفریم مادر
|
| 430 |
window.addEventListener('message', (event) => {
|
| 431 |
if (event.data && event.data.type === 'USER_STATUS_RESPONSE') {
|
| 432 |
try {
|
|
@@ -439,26 +437,16 @@
|
|
| 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,12 +456,8 @@
|
|
| 468 |
}
|
| 469 |
}
|
| 470 |
|
| 471 |
-
// دکمه ارتقا در مودال
|
| 472 |
document.getElementById('btnGoPremium').addEventListener('click', () => {
|
| 473 |
-
parent.postMessage({
|
| 474 |
-
type: 'NAVIGATE_TO_PREMIUM',
|
| 475 |
-
payload: { url: PREMIUM_URL }
|
| 476 |
-
}, '*');
|
| 477 |
});
|
| 478 |
|
| 479 |
function closeUpgradeModal() {
|
|
@@ -490,7 +474,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 +484,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 +518,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 |
}
|
|
@@ -550,7 +530,7 @@
|
|
| 550 |
|
| 551 |
// --- توابع حذف ---
|
| 552 |
function askToDelete(event, id) {
|
| 553 |
-
event.stopPropagation();
|
| 554 |
songToDeleteId = id;
|
| 555 |
deleteModal.classList.add('open');
|
| 556 |
}
|
|
@@ -572,7 +552,6 @@
|
|
| 572 |
}
|
| 573 |
}
|
| 574 |
|
| 575 |
-
// بستن مودال با کلیک بیرون
|
| 576 |
deleteModal.addEventListener('click', (e) => {
|
| 577 |
if (e.target === deleteModal) closeDeleteModal();
|
| 578 |
});
|
|
@@ -589,7 +568,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,122 +580,36 @@
|
|
| 602 |
|
| 603 |
initDB();
|
| 604 |
|
| 605 |
-
// --- توابع تبدیل
|
| 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;
|
| 635 |
-
let buffer = new ArrayBuffer(length);
|
| 636 |
-
let view = new DataView(buffer);
|
| 637 |
-
let channels = [], i, sample;
|
| 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');
|
| 645 |
-
writeString(view, 12, 'fmt ');
|
| 646 |
-
view.setUint32(16, 16, true);
|
| 647 |
-
view.setUint16(20, 1, true);
|
| 648 |
-
view.setUint16(22, numOfChan, true);
|
| 649 |
-
view.setUint32(24, abuffer.sampleRate, true);
|
| 650 |
-
view.setUint32(28, abuffer.sampleRate * 2 * numOfChan, true);
|
| 651 |
-
view.setUint16(32, numOfChan * 2, true);
|
| 652 |
-
view.setUint16(34, 16, true);
|
| 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++) {
|
| 665 |
-
sample = Math.max(-1, Math.min(1, channels[i][pos]));
|
| 666 |
-
sample = (sample < 0 ? sample * 0x8000 : sample * 0x7FFF) | 0;
|
| 667 |
-
view.setInt16(offset, sample, true);
|
| 668 |
-
offset += 2;
|
| 669 |
-
}
|
| 670 |
-
pos++;
|
| 671 |
-
}
|
| 672 |
-
} else {
|
| 673 |
-
while(pos < len) {
|
| 674 |
-
sample = Math.max(-1, Math.min(1, channels[0][pos]));
|
| 675 |
-
sample = (sample < 0 ? sample * 0x8000 : sample * 0x7FFF) | 0;
|
| 676 |
-
view.setInt16(offset, sample, true);
|
| 677 |
-
offset += 2;
|
| 678 |
-
pos++;
|
| 679 |
-
}
|
| 680 |
-
}
|
| 681 |
-
return new Blob([buffer], { type: "audio/wav" });
|
| 682 |
-
}
|
| 683 |
-
|
| 684 |
async function convertAudioToWav(file) {
|
| 685 |
return new Promise((resolve, reject) => {
|
| 686 |
const reader = new FileReader();
|
| 687 |
-
reader.onload =
|
| 688 |
-
const
|
| 689 |
-
|
| 690 |
-
|
| 691 |
-
|
| 692 |
-
|
| 693 |
-
|
| 694 |
-
|
| 695 |
-
|
| 696 |
-
reject("فرمت فایل صوتی پشتیبانی نمیشود.");
|
| 697 |
-
});
|
| 698 |
};
|
| 699 |
-
reader.onerror = reject;
|
| 700 |
reader.readAsArrayBuffer(file);
|
| 701 |
});
|
| 702 |
}
|
| 703 |
|
| 704 |
-
// --- تابع آپلود فایل صوتی به Gradio ---
|
| 705 |
async function uploadAudioFile(file) {
|
| 706 |
const formData = new FormData();
|
| 707 |
formData.append("files", file);
|
| 708 |
try {
|
| 709 |
-
const response = await fetch(`${ACE_SPACE_URL}gradio_api/upload`, {
|
| 710 |
-
method: "POST",
|
| 711 |
-
body: formData
|
| 712 |
-
});
|
| 713 |
const data = await response.json();
|
| 714 |
if (data && data.length > 0) {
|
| 715 |
return {
|
| 716 |
"path": data[0],
|
| 717 |
"url": `${ACE_SPACE_URL}gradio_api/file=${data[0]}`,
|
| 718 |
-
"orig_name": file.name,
|
| 719 |
-
"size": file.size,
|
| 720 |
-
"mime_type": file.type,
|
| 721 |
"meta": {"_type": "gradio.FileData"}
|
| 722 |
};
|
| 723 |
}
|
|
@@ -738,53 +630,44 @@
|
|
| 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'},
|
| 773 |
body: JSON.stringify({
|
| 774 |
-
idea: ideaInput.value,
|
| 775 |
-
fingerprint: getFingerprint(),
|
| 776 |
is_premium: userSubscriptionStatus === 'paid',
|
| 777 |
is_instrumental: isInstrumental
|
| 778 |
})
|
| 779 |
});
|
| 780 |
|
| 781 |
-
// بررسی خطای محدودیت روزانه (429)
|
| 782 |
if (response.status === 429) {
|
| 783 |
loader.style.display = 'none';
|
| 784 |
step1.style.display = 'block';
|
| 785 |
historySection.style.display = 'block';
|
| 786 |
processBtn.disabled = false;
|
| 787 |
-
|
| 788 |
upgradeModalTitle.innerText = "محدودیت ساخت روزانه";
|
| 789 |
upgradeModalText.innerText = "شما به سقف ۵ آهنگ رایگان در روز رسیدهاید. برای ساخت آهنگهای بیشتر و نامحدود، لطفا حساب خود را ارتقا دهید.";
|
| 790 |
upgradeModal.classList.add('open');
|
|
@@ -799,10 +682,9 @@
|
|
| 799 |
|
| 800 |
const finalLyrics = isInstrumental ? "" : lyrics;
|
| 801 |
|
| 802 |
-
// ایندکس 3: مدت زمان آهنگ
|
| 803 |
const payload = [
|
| 804 |
getVal('model_select'),
|
| 805 |
-
|
| 806 |
uploadedAudioObj,
|
| 807 |
getVal('duration_select'),
|
| 808 |
musicPrompt,
|
|
@@ -903,6 +785,51 @@
|
|
| 903 |
requestAnimationFrame(anim);
|
| 904 |
}
|
| 905 |
anim();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 906 |
</script>
|
| 907 |
</body>
|
| 908 |
</html>
|
|
|
|
| 401 |
function getFingerprint() {
|
| 402 |
let fp = localStorage.getItem('user_fingerprint');
|
| 403 |
if (!fp) {
|
|
|
|
| 404 |
fp = 'user_' + Math.random().toString(36).substr(2, 9) + Date.now().toString(36);
|
| 405 |
localStorage.setItem('user_fingerprint', fp);
|
| 406 |
}
|
|
|
|
| 425 |
}
|
| 426 |
}
|
| 427 |
|
|
|
|
| 428 |
window.addEventListener('message', (event) => {
|
| 429 |
if (event.data && event.data.type === 'USER_STATUS_RESPONSE') {
|
| 430 |
try {
|
|
|
|
| 437 |
}
|
| 438 |
});
|
| 439 |
|
|
|
|
| 440 |
parent.postMessage({ type: 'REQUEST_USER_STATUS' }, '*');
|
| 441 |
|
|
|
|
| 442 |
function handleSecureDownload(url, e) {
|
| 443 |
if (e) e.preventDefault();
|
|
|
|
| 444 |
if (userSubscriptionStatus === 'free') {
|
|
|
|
| 445 |
upgradeModalTitle.innerText = "دانلود مخصوص اعضای ویژه";
|
| 446 |
upgradeModalText.innerText = "برای دانلود آهنگهای ساخته شده با کیفیت اصلی و بدون محدودیت، لطفا حساب کاربری خود را به نسخه نامحدود ارتقا دهید.";
|
| 447 |
upgradeModal.classList.add('open');
|
| 448 |
} else {
|
| 449 |
+
parent.postMessage({ type: 'INITIATE_DOWNLOAD_FROM_URL', payload: { audioUrl: url } }, '*');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 450 |
const btn = e ? e.target.closest('button') : null;
|
| 451 |
if(btn) {
|
| 452 |
const originalText = btn.innerHTML;
|
|
|
|
| 456 |
}
|
| 457 |
}
|
| 458 |
|
|
|
|
| 459 |
document.getElementById('btnGoPremium').addEventListener('click', () => {
|
| 460 |
+
parent.postMessage({ type: 'NAVIGATE_TO_PREMIUM', payload: { url: PREMIUM_URL } }, '*');
|
|
|
|
|
|
|
|
|
|
| 461 |
});
|
| 462 |
|
| 463 |
function closeUpgradeModal() {
|
|
|
|
| 474 |
|
| 475 |
async function saveToHistory(idea, lyrics, audioUrl) {
|
| 476 |
try {
|
|
|
|
| 477 |
const response = await fetch(audioUrl);
|
| 478 |
const audioBlob = await response.blob();
|
| 479 |
const newItem = {
|
|
|
|
| 484 |
const store = transaction.objectStore("songs");
|
| 485 |
store.add(newItem);
|
| 486 |
|
|
|
|
| 487 |
const countReq = store.count();
|
| 488 |
countReq.onsuccess = () => {
|
| 489 |
if (countReq.result > 10) store.openCursor().onsuccess = (e) => { if(e.target.result) e.target.result.delete(); };
|
|
|
|
| 518 |
<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>
|
| 519 |
</div>
|
| 520 |
`;
|
|
|
|
| 521 |
div.onclick = (e) => {
|
|
|
|
| 522 |
if (!e.target.closest('.h-delete')) {
|
| 523 |
openHistoryItem(item);
|
| 524 |
}
|
|
|
|
| 530 |
|
| 531 |
// --- توابع حذف ---
|
| 532 |
function askToDelete(event, id) {
|
| 533 |
+
event.stopPropagation();
|
| 534 |
songToDeleteId = id;
|
| 535 |
deleteModal.classList.add('open');
|
| 536 |
}
|
|
|
|
| 552 |
}
|
| 553 |
}
|
| 554 |
|
|
|
|
| 555 |
deleteModal.addEventListener('click', (e) => {
|
| 556 |
if (e.target === deleteModal) closeDeleteModal();
|
| 557 |
});
|
|
|
|
| 568 |
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> آهنگ آرشیو شده`;
|
| 569 |
headerText.style.color = 'var(--accent-primary)';
|
| 570 |
|
|
|
|
| 571 |
mainDownloadBtn.style.display = 'inline-flex';
|
| 572 |
mainDownloadBtn.onclick = (e) => handleSecureDownload(audioURL, e);
|
| 573 |
|
|
|
|
| 580 |
|
| 581 |
initDB();
|
| 582 |
|
| 583 |
+
// --- توابع تبدیل و آپلود ---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 584 |
async function convertAudioToWav(file) {
|
| 585 |
return new Promise((resolve, reject) => {
|
| 586 |
const reader = new FileReader();
|
| 587 |
+
reader.onload = (e) => {
|
| 588 |
+
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
| 589 |
+
audioCtx.decodeAudioData(e.target.result)
|
| 590 |
+
.then(buffer => {
|
| 591 |
+
const wavBlob = bufferToWave(buffer, buffer.length);
|
| 592 |
+
const wavFile = new File([wavBlob], file.name.replace(/\.[^/.]+$/, "") + ".wav", { type: "audio/wav" });
|
| 593 |
+
resolve(wavFile);
|
| 594 |
+
})
|
| 595 |
+
.catch(err => reject(`خطا در دیکد کردن فایل: ${err.message}`));
|
|
|
|
|
|
|
| 596 |
};
|
| 597 |
+
reader.onerror = (e) => reject(`خطا در خواندن فایل: ${e.message}`);
|
| 598 |
reader.readAsArrayBuffer(file);
|
| 599 |
});
|
| 600 |
}
|
| 601 |
|
|
|
|
| 602 |
async function uploadAudioFile(file) {
|
| 603 |
const formData = new FormData();
|
| 604 |
formData.append("files", file);
|
| 605 |
try {
|
| 606 |
+
const response = await fetch(`${ACE_SPACE_URL}gradio_api/upload`, { method: "POST", body: formData });
|
|
|
|
|
|
|
|
|
|
| 607 |
const data = await response.json();
|
| 608 |
if (data && data.length > 0) {
|
| 609 |
return {
|
| 610 |
"path": data[0],
|
| 611 |
"url": `${ACE_SPACE_URL}gradio_api/file=${data[0]}`,
|
| 612 |
+
"orig_name": file.name, "size": file.size, "mime_type": file.type,
|
|
|
|
|
|
|
| 613 |
"meta": {"_type": "gradio.FileData"}
|
| 614 |
};
|
| 615 |
}
|
|
|
|
| 630 |
loader.style.display = 'block';
|
| 631 |
|
| 632 |
try {
|
|
|
|
| 633 |
const audioInput = document.getElementById('audio_reference');
|
| 634 |
let uploadedAudioObj = null;
|
| 635 |
+
let mode = "custom"; // حالت پیشفرض
|
| 636 |
|
| 637 |
if (audioInput.files.length > 0) {
|
| 638 |
let fileToUpload = audioInput.files[0];
|
|
|
|
|
|
|
| 639 |
if (fileToUpload.type !== 'audio/wav' && !fileToUpload.name.toLowerCase().endsWith('.wav')) {
|
| 640 |
loaderText.innerText = "در حال تبدیل فرمت فایل صوتی به WAV...";
|
| 641 |
+
fileToUpload = await convertAudioToWav(fileToUpload);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 642 |
}
|
| 643 |
|
| 644 |
loaderText.innerText = "در حال آپلود فایل نمونه...";
|
| 645 |
uploadedAudioObj = await uploadAudioFile(fileToUpload);
|
| 646 |
+
|
| 647 |
+
// --- **نکته کلیدی: تغییر حالت** ---
|
| 648 |
+
if (uploadedAudioObj) {
|
| 649 |
+
mode = "Continuation";
|
| 650 |
+
}
|
| 651 |
}
|
| 652 |
|
|
|
|
| 653 |
loaderText.innerText = "آلفا در حال نوشتن شعر و ملودی...";
|
| 654 |
const isInstrumental = getChk('instrumental_chk');
|
| 655 |
|
|
|
|
| 656 |
const response = await fetch('/api/refine', {
|
| 657 |
method: 'POST',
|
| 658 |
headers: {'Content-Type': 'application/json'},
|
| 659 |
body: JSON.stringify({
|
| 660 |
+
idea: ideaInput.value, fingerprint: getFingerprint(),
|
|
|
|
| 661 |
is_premium: userSubscriptionStatus === 'paid',
|
| 662 |
is_instrumental: isInstrumental
|
| 663 |
})
|
| 664 |
});
|
| 665 |
|
|
|
|
| 666 |
if (response.status === 429) {
|
| 667 |
loader.style.display = 'none';
|
| 668 |
step1.style.display = 'block';
|
| 669 |
historySection.style.display = 'block';
|
| 670 |
processBtn.disabled = false;
|
|
|
|
| 671 |
upgradeModalTitle.innerText = "محدودیت ساخت روزانه";
|
| 672 |
upgradeModalText.innerText = "شما به سقف ۵ آهنگ رایگان در روز رسیدهاید. برای ساخت آهنگهای بیشتر و نامحدود، لطفا حساب خود را ارتقا دهید.";
|
| 673 |
upgradeModal.classList.add('open');
|
|
|
|
| 682 |
|
| 683 |
const finalLyrics = isInstrumental ? "" : lyrics;
|
| 684 |
|
|
|
|
| 685 |
const payload = [
|
| 686 |
getVal('model_select'),
|
| 687 |
+
mode, // **اصلاح کلیدی:** استفاده از حالت داینامیک
|
| 688 |
uploadedAudioObj,
|
| 689 |
getVal('duration_select'),
|
| 690 |
musicPrompt,
|
|
|
|
| 785 |
requestAnimationFrame(anim);
|
| 786 |
}
|
| 787 |
anim();
|
| 788 |
+
|
| 789 |
+
// --- توابع کمکی برای تبدیل به WAV ---
|
| 790 |
+
function bufferToWave(aBuffer, len) {
|
| 791 |
+
let numOfChan = aBuffer.numberOfChannels,
|
| 792 |
+
length = len * numOfChan * 2 + 44,
|
| 793 |
+
buffer = new ArrayBuffer(length),
|
| 794 |
+
view = new DataView(buffer),
|
| 795 |
+
channels = [], i, sample,
|
| 796 |
+
offset = 0,
|
| 797 |
+
pos = 0;
|
| 798 |
+
|
| 799 |
+
writeString(view, 0, 'RIFF');
|
| 800 |
+
view.setUint32(4, 36 + len * numOfChan * 2, true);
|
| 801 |
+
writeString(view, 8, 'WAVE');
|
| 802 |
+
writeString(view, 12, 'fmt ');
|
| 803 |
+
view.setUint32(16, 16, true);
|
| 804 |
+
view.setUint16(20, 1, true);
|
| 805 |
+
view.setUint16(22, numOfChan, true);
|
| 806 |
+
view.setUint32(24, aBuffer.sampleRate, true);
|
| 807 |
+
view.setUint32(28, aBuffer.sampleRate * 4, true);
|
| 808 |
+
view.setUint16(32, numOfChan * 2, true);
|
| 809 |
+
view.setUint16(34, 16, true);
|
| 810 |
+
writeString(view, 36, 'data');
|
| 811 |
+
view.setUint32(40, len * numOfChan * 2, true);
|
| 812 |
+
|
| 813 |
+
for (i = 0; i < aBuffer.numberOfChannels; i++)
|
| 814 |
+
channels.push(aBuffer.getChannelData(i));
|
| 815 |
+
|
| 816 |
+
while (pos < len) {
|
| 817 |
+
for (i = 0; i < numOfChan; i++) {
|
| 818 |
+
sample = Math.max(-1, Math.min(1, channels[i][pos]));
|
| 819 |
+
sample = (0.5 + sample < 0 ? sample * 32768 : sample * 32767) | 0;
|
| 820 |
+
view.setInt16(offset, sample, true);
|
| 821 |
+
offset += 2;
|
| 822 |
+
}
|
| 823 |
+
pos++;
|
| 824 |
+
}
|
| 825 |
+
return new Blob([view], { type: 'audio/wav' });
|
| 826 |
+
}
|
| 827 |
+
|
| 828 |
+
function writeString(view, offset, string) {
|
| 829 |
+
for (var i = 0; i < string.length; i++) {
|
| 830 |
+
view.setUint8(offset + i, string.charCodeAt(i));
|
| 831 |
+
}
|
| 832 |
+
}
|
| 833 |
</script>
|
| 834 |
</body>
|
| 835 |
</html>
|