Update index.html
Browse files- index.html +120 -7
index.html
CHANGED
|
@@ -284,13 +284,13 @@
|
|
| 284 |
<div class="settings-group"><label>سید (Seed) تصادفی=-1:</label><input type="number" id="seed_input" value="-1"></div>
|
| 285 |
|
| 286 |
<div class="settings-group"><label>مقیاس هدایت (CFG):</label><input type="number" id="cfg_input" value="7.0" step="0.5"></div>
|
| 287 |
-
<!-- اضافه شدن انتخاب زمان در کنار CFG -->
|
| 288 |
<div class="settings-group"><label>مدت زمان آهنگ:</label><select id="duration_select"><option value="unknown" selected>خودکار (استاندارد)</option><option value="short">کوتاه (حدود ۱ دقیقه)</option><option value="medium">متوسط (حدود ۲ دقیقه)</option><option value="long">طولانی (حدود ۳ دقیقه)</option></select></div>
|
| 289 |
</div>
|
| 290 |
|
| 291 |
<div class="settings-group" style="border-top: 1px solid #e2e8f0; padding-top: 15px; margin-bottom: 10px;">
|
| 292 |
<label>آپلود آهنگ نمونه (Audio Conditioning):</label>
|
| 293 |
<input type="file" id="audio_reference" accept="audio/*" style="padding: 10px; width: 100%; background: #fff;">
|
|
|
|
| 294 |
</div>
|
| 295 |
|
| 296 |
<div class="checkbox-wrapper">
|
|
@@ -602,6 +602,105 @@
|
|
| 602 |
|
| 603 |
initDB();
|
| 604 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 605 |
// --- تابع آپلود فایل صوتی به Gradio ---
|
| 606 |
async function uploadAudioFile(file) {
|
| 607 |
const formData = new FormData();
|
|
@@ -639,12 +738,28 @@
|
|
| 639 |
loader.style.display = 'block';
|
| 640 |
|
| 641 |
try {
|
| 642 |
-
// 1. آپلود فایل نمونه
|
| 643 |
const audioInput = document.getElementById('audio_reference');
|
| 644 |
let uploadedAudioObj = null;
|
|
|
|
| 645 |
if (audioInput.files.length > 0) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 646 |
loaderText.innerText = "در حال آپلود فایل نمونه...";
|
| 647 |
-
uploadedAudioObj = await uploadAudioFile(
|
| 648 |
}
|
| 649 |
|
| 650 |
// 2. درخواست متن و پرامپت
|
|
@@ -670,7 +785,6 @@
|
|
| 670 |
historySection.style.display = 'block';
|
| 671 |
processBtn.disabled = false;
|
| 672 |
|
| 673 |
-
// نمایش مودال ارتقا با متن مناسب
|
| 674 |
upgradeModalTitle.innerText = "محدودیت ساخت روزانه";
|
| 675 |
upgradeModalText.innerText = "شما به سقف ۵ آهنگ رایگان در روز رسیدهاید. برای ساخت آهنگهای بیشتر و نامحدود، لطفا حساب خود را ارتقا دهید.";
|
| 676 |
upgradeModal.classList.add('open');
|
|
@@ -685,12 +799,12 @@
|
|
| 685 |
|
| 686 |
const finalLyrics = isInstrumental ? "" : lyrics;
|
| 687 |
|
| 688 |
-
// ایندکس 3: مدت زمان آهنگ
|
| 689 |
const payload = [
|
| 690 |
getVal('model_select'),
|
| 691 |
"custom",
|
| 692 |
uploadedAudioObj,
|
| 693 |
-
getVal('duration_select'),
|
| 694 |
musicPrompt,
|
| 695 |
finalLyrics,
|
| 696 |
0, "", "", "unknown",
|
|
@@ -736,7 +850,6 @@
|
|
| 736 |
hasResult = true;
|
| 737 |
|
| 738 |
if (processedUrls.size === 1) {
|
| 739 |
-
// تنظیم دکمه دانلود اصلی
|
| 740 |
mainDownloadBtn.style.display = 'inline-flex';
|
| 741 |
mainDownloadBtn.onclick = (e) => handleSecureDownload(fullUrl, e);
|
| 742 |
|
|
|
|
| 284 |
<div class="settings-group"><label>سید (Seed) تصادفی=-1:</label><input type="number" id="seed_input" value="-1"></div>
|
| 285 |
|
| 286 |
<div class="settings-group"><label>مقیاس هدایت (CFG):</label><input type="number" id="cfg_input" value="7.0" step="0.5"></div>
|
|
|
|
| 287 |
<div class="settings-group"><label>مدت زمان آهنگ:</label><select id="duration_select"><option value="unknown" selected>خودکار (استاندارد)</option><option value="short">کوتاه (حدود ۱ دقیقه)</option><option value="medium">متوسط (حدود ۲ دقیقه)</option><option value="long">طولانی (حدود ۳ دقیقه)</option></select></div>
|
| 288 |
</div>
|
| 289 |
|
| 290 |
<div class="settings-group" style="border-top: 1px solid #e2e8f0; padding-top: 15px; margin-bottom: 10px;">
|
| 291 |
<label>آپلود آهنگ نمونه (Audio Conditioning):</label>
|
| 292 |
<input type="file" id="audio_reference" accept="audio/*" style="padding: 10px; width: 100%; background: #fff;">
|
| 293 |
+
<div style="font-size: 0.8rem; color: #666; margin-top: 5px;">(تمامی فرمتهای صوتی پشتیبانی میشوند، خودکار به WAV تبدیل میشود)</div>
|
| 294 |
</div>
|
| 295 |
|
| 296 |
<div class="checkbox-wrapper">
|
|
|
|
| 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;
|
| 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 = function(e) {
|
| 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){
|
| 695 |
+
console.error("Audio decode failed", e);
|
| 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();
|
|
|
|
| 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 |
+
try {
|
| 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. درخواست متن و پرامپت
|
|
|
|
| 785 |
historySection.style.display = 'block';
|
| 786 |
processBtn.disabled = false;
|
| 787 |
|
|
|
|
| 788 |
upgradeModalTitle.innerText = "محدودیت ساخت روزانه";
|
| 789 |
upgradeModalText.innerText = "شما به سقف ۵ آهنگ رایگان در روز رسیدهاید. برای ساخت آهنگهای بیشتر و نامحدود، لطفا حساب خود را ارتقا دهید.";
|
| 790 |
upgradeModal.classList.add('open');
|
|
|
|
| 799 |
|
| 800 |
const finalLyrics = isInstrumental ? "" : lyrics;
|
| 801 |
|
| 802 |
+
// ایندکس 3: مدت زمان آهنگ
|
| 803 |
const payload = [
|
| 804 |
getVal('model_select'),
|
| 805 |
"custom",
|
| 806 |
uploadedAudioObj,
|
| 807 |
+
getVal('duration_select'),
|
| 808 |
musicPrompt,
|
| 809 |
finalLyrics,
|
| 810 |
0, "", "", "unknown",
|
|
|
|
| 850 |
hasResult = true;
|
| 851 |
|
| 852 |
if (processedUrls.size === 1) {
|
|
|
|
| 853 |
mainDownloadBtn.style.display = 'inline-flex';
|
| 854 |
mainDownloadBtn.onclick = (e) => handleSecureDownload(fullUrl, e);
|
| 855 |
|