Update index.html
Browse files- index.html +59 -57
index.html
CHANGED
|
@@ -102,7 +102,8 @@
|
|
| 102 |
textarea { min-height: 120px; resize: vertical; }
|
| 103 |
textarea:focus, input:focus, select:focus { border-color: var(--accent-primary); background: #fff; }
|
| 104 |
|
| 105 |
-
|
|
|
|
| 106 |
|
| 107 |
/* دکمه اصلی */
|
| 108 |
@keyframes move-gradient {
|
|
@@ -167,43 +168,43 @@
|
|
| 167 |
.download-btn-style.locked { background-color: #f1f5f9; color: #94a3b8; }
|
| 168 |
.download-btn-style.locked:hover { background-color: #e2e8f0; }
|
| 169 |
|
| 170 |
-
.audio-item { margin-bottom: 10px; }
|
| 171 |
audio { width: 100%; height: 50px; border-radius: 25px; margin-top: 5px; }
|
| 172 |
|
| 173 |
-
/*
|
| 174 |
.lyrics-container {
|
| 175 |
-
background: var(--input-bg);
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
-
|
|
|
|
|
|
|
| 182 |
}
|
| 183 |
|
|
|
|
| 184 |
.lyric-line {
|
| 185 |
-
margin:
|
|
|
|
| 186 |
font-size: 1rem;
|
| 187 |
-
color: #
|
| 188 |
-
transition: all 0.3s
|
| 189 |
-
line-height: 1.8;
|
| 190 |
-
opacity: 0.5;
|
| 191 |
-
transform: scale(0.95);
|
| 192 |
cursor: default;
|
|
|
|
|
|
|
| 193 |
}
|
| 194 |
|
| 195 |
-
/* استایل خط فعال
|
| 196 |
.lyric-line.active {
|
| 197 |
-
color: var(--accent-primary);
|
| 198 |
-
font-weight: 800;
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
text-shadow: 0 5px 15px rgba(74, 108, 250, 0.2);
|
| 203 |
}
|
| 204 |
|
| 205 |
-
.lyrics-tag { color: var(--accent-secondary); font-size: 0.8em; margin-top: 15px; display: block; font-weight: 700; }
|
| 206 |
-
|
| 207 |
/* لودر */
|
| 208 |
#loader { display: none; text-align: center; padding: 20px; }
|
| 209 |
.wave-bars { display: flex; justify-content: center; gap: 4px; height: 30px; align-items: flex-end; }
|
|
@@ -608,7 +609,6 @@
|
|
| 608 |
mainDownloadBtn.style.display = 'inline-flex';
|
| 609 |
mainDownloadBtn.onclick = (e) => handleSecureDownload(audioURL, e);
|
| 610 |
|
| 611 |
-
// اگر دیتای LRC ذخیره شده باشد از آن استفاده میکنیم، وگرنه از متن ساده
|
| 612 |
setupPlayerWithLyrics(audioURL, item.lrcData || null, item.lyrics);
|
| 613 |
|
| 614 |
finalResult.style.display = 'block';
|
|
@@ -658,7 +658,6 @@
|
|
| 658 |
resolve(msg.output);
|
| 659 |
} else if (msg.msg === 'close') {
|
| 660 |
eventSource.close();
|
| 661 |
-
// گاهی اوقات close میاد ولی دیتا در process_completed بوده
|
| 662 |
}
|
| 663 |
};
|
| 664 |
|
|
@@ -743,17 +742,19 @@
|
|
| 743 |
|
| 744 |
if(!audioFileObj) throw new Error("Audio generation failed");
|
| 745 |
|
| 746 |
-
// 4. دریافت LRC (سینک کردن) - فانکشن
|
|
|
|
| 747 |
let lrcData = null;
|
| 748 |
if (!isInstrumental) {
|
| 749 |
loaderText.innerText = "در حال هماهنگسازی متن و آهنگ...";
|
| 750 |
try {
|
| 751 |
-
|
| 752 |
-
// ورودی احتمالا فایل صوتی است
|
| 753 |
-
const lrcOutput = await runGradioFunction(85, [audioFileObj]);
|
| 754 |
-
// خروجی فانکشن 85 معمولا یک لیست است که عنصر اولش متن LRC است
|
| 755 |
if (lrcOutput && lrcOutput.data && lrcOutput.data[0]) {
|
| 756 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 757 |
}
|
| 758 |
} catch (e) {
|
| 759 |
console.warn("LRC generation failed, using plain lyrics", e);
|
|
@@ -810,29 +811,32 @@
|
|
| 810 |
window.scrollTo({ top: 0, behavior: 'smooth' });
|
| 811 |
}
|
| 812 |
|
| 813 |
-
// --- پارسر
|
| 814 |
-
function
|
| 815 |
const lines = [];
|
| 816 |
-
// فرمت
|
| 817 |
-
const regex = /
|
| 818 |
|
| 819 |
-
const
|
| 820 |
-
for(let
|
| 821 |
-
const match =
|
| 822 |
if(match) {
|
| 823 |
-
const
|
| 824 |
-
const
|
| 825 |
-
const
|
| 826 |
-
|
| 827 |
-
|
| 828 |
-
if(text) {
|
| 829 |
-
lines.push({ time, text });
|
| 830 |
}
|
| 831 |
}
|
| 832 |
}
|
| 833 |
return lines;
|
| 834 |
}
|
| 835 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 836 |
function setupPlayerWithLyrics(audioSrc, lrcData, rawLyrics) {
|
| 837 |
playerWrapper.innerHTML = '';
|
| 838 |
finalLyricsBox.innerHTML = '';
|
|
@@ -843,9 +847,9 @@
|
|
| 843 |
audio.src = audioSrc;
|
| 844 |
playerWrapper.appendChild(audio);
|
| 845 |
|
| 846 |
-
// اگر دیتای
|
| 847 |
-
if (lrcData && typeof lrcData === 'string' && lrcData.includes('
|
| 848 |
-
const parsedLyrics =
|
| 849 |
|
| 850 |
parsedLyrics.forEach((line, index) => {
|
| 851 |
const p = document.createElement('p');
|
|
@@ -859,12 +863,11 @@
|
|
| 859 |
const currentTime = audio.currentTime;
|
| 860 |
let activeIndex = -1;
|
| 861 |
|
| 862 |
-
// پیدا کردن خطی که
|
| 863 |
for (let i = 0; i < parsedLyrics.length; i++) {
|
| 864 |
-
if (currentTime >= parsedLyrics[i].
|
| 865 |
activeIndex = i;
|
| 866 |
-
|
| 867 |
-
break;
|
| 868 |
}
|
| 869 |
}
|
| 870 |
|
|
@@ -882,16 +885,15 @@
|
|
| 882 |
} else {
|
| 883 |
// حالت متن ساده (بدون سینک)
|
| 884 |
if (rawLyrics) {
|
| 885 |
-
// نمایش با استایل خط به خط اما بدون اکتیو شدن
|
| 886 |
const lines = rawLyrics.split('\n').filter(l => l.trim() !== '');
|
| 887 |
lines.forEach(line => {
|
| 888 |
const p = document.createElement('p');
|
| 889 |
-
p.className = 'lyric-line';
|
| 890 |
p.innerText = line;
|
| 891 |
finalLyricsBox.appendChild(p);
|
| 892 |
});
|
| 893 |
} else {
|
| 894 |
-
finalLyricsBox.innerHTML = '<span style="color:#
|
| 895 |
}
|
| 896 |
}
|
| 897 |
}
|
|
|
|
| 102 |
textarea { min-height: 120px; resize: vertical; }
|
| 103 |
textarea:focus, input:focus, select:focus { border-color: var(--accent-primary); background: #fff; }
|
| 104 |
|
| 105 |
+
/* استایل اختصاصی فایل آپلود */
|
| 106 |
+
input[type="file"] { padding: 10px; font-size: 0.9rem; background: #fff; }
|
| 107 |
|
| 108 |
/* دکمه اصلی */
|
| 109 |
@keyframes move-gradient {
|
|
|
|
| 168 |
.download-btn-style.locked { background-color: #f1f5f9; color: #94a3b8; }
|
| 169 |
.download-btn-style.locked:hover { background-color: #e2e8f0; }
|
| 170 |
|
| 171 |
+
.audio-item { margin-bottom: 10px; width: 100%; }
|
| 172 |
audio { width: 100%; height: 50px; border-radius: 25px; margin-top: 5px; }
|
| 173 |
|
| 174 |
+
/* استایل باکس لیریک (برگشت به حالت اول) */
|
| 175 |
.lyrics-container {
|
| 176 |
+
background: var(--input-bg);
|
| 177 |
+
border-radius: 16px;
|
| 178 |
+
padding: 20px;
|
| 179 |
+
max-height: 350px;
|
| 180 |
+
overflow-y: auto;
|
| 181 |
+
border: 1px solid var(--input-border);
|
| 182 |
+
margin-top: 15px;
|
| 183 |
+
text-align: center;
|
| 184 |
+
scroll-behavior: smooth; /* اسکرول نرم */
|
| 185 |
}
|
| 186 |
|
| 187 |
+
/* استایل خطوط متن */
|
| 188 |
.lyric-line {
|
| 189 |
+
margin: 0;
|
| 190 |
+
padding: 8px 0;
|
| 191 |
font-size: 1rem;
|
| 192 |
+
color: #4a5568; /* رنگ خاکستری تیره اصلی */
|
| 193 |
+
transition: all 0.3s ease;
|
|
|
|
|
|
|
|
|
|
| 194 |
cursor: default;
|
| 195 |
+
line-height: 1.8;
|
| 196 |
+
font-weight: 400;
|
| 197 |
}
|
| 198 |
|
| 199 |
+
/* استایل خط فعال */
|
| 200 |
.lyric-line.active {
|
| 201 |
+
color: var(--accent-primary); /* آبی اصلی */
|
| 202 |
+
font-weight: 800; /* بولد */
|
| 203 |
+
transform: scale(1.05); /* کمی بزرگتر */
|
| 204 |
+
background: rgba(74, 108, 250, 0.05); /* پس زمینه خیلی محو */
|
| 205 |
+
border-radius: 8px;
|
|
|
|
| 206 |
}
|
| 207 |
|
|
|
|
|
|
|
| 208 |
/* لودر */
|
| 209 |
#loader { display: none; text-align: center; padding: 20px; }
|
| 210 |
.wave-bars { display: flex; justify-content: center; gap: 4px; height: 30px; align-items: flex-end; }
|
|
|
|
| 609 |
mainDownloadBtn.style.display = 'inline-flex';
|
| 610 |
mainDownloadBtn.onclick = (e) => handleSecureDownload(audioURL, e);
|
| 611 |
|
|
|
|
| 612 |
setupPlayerWithLyrics(audioURL, item.lrcData || null, item.lyrics);
|
| 613 |
|
| 614 |
finalResult.style.display = 'block';
|
|
|
|
| 658 |
resolve(msg.output);
|
| 659 |
} else if (msg.msg === 'close') {
|
| 660 |
eventSource.close();
|
|
|
|
| 661 |
}
|
| 662 |
};
|
| 663 |
|
|
|
|
| 742 |
|
| 743 |
if(!audioFileObj) throw new Error("Audio generation failed");
|
| 744 |
|
| 745 |
+
// 4. دریافت LRC (سینک کردن) - فانکشن 84 طبق لاگ شما
|
| 746 |
+
// لاگ شما نشان داد فانکشن 84 فایل VTT تولید میکند
|
| 747 |
let lrcData = null;
|
| 748 |
if (!isInstrumental) {
|
| 749 |
loaderText.innerText = "در حال هماهنگسازی متن و آهنگ...";
|
| 750 |
try {
|
| 751 |
+
const lrcOutput = await runGradioFunction(84, [audioFileObj]);
|
|
|
|
|
|
|
|
|
|
| 752 |
if (lrcOutput && lrcOutput.data && lrcOutput.data[0]) {
|
| 753 |
+
// دریافت محتوای فایل VTT
|
| 754 |
+
const vttFile = lrcOutput.data[0];
|
| 755 |
+
const vttUrl = vttFile.url ? vttFile.url : `${ACE_SPACE_URL}gradio_api/file=${vttFile.path}`;
|
| 756 |
+
const resp = await fetch(vttUrl);
|
| 757 |
+
lrcData = await resp.text();
|
| 758 |
}
|
| 759 |
} catch (e) {
|
| 760 |
console.warn("LRC generation failed, using plain lyrics", e);
|
|
|
|
| 811 |
window.scrollTo({ top: 0, behavior: 'smooth' });
|
| 812 |
}
|
| 813 |
|
| 814 |
+
// --- پارسر VTT ---
|
| 815 |
+
function parseVTT(vttText) {
|
| 816 |
const lines = [];
|
| 817 |
+
// Regex برای فرمت VTT: 00:00:16.320 --> 00:00:20.640
|
| 818 |
+
const regex = /(\d{2}:\d{2}:\d{2}\.\d{3})\s-->\s(\d{2}:\d{2}:\d{2}\.\d{3})\n(.*)/;
|
| 819 |
|
| 820 |
+
const blocks = vttText.split('\n\n');
|
| 821 |
+
for(let block of blocks) {
|
| 822 |
+
const match = block.match(regex);
|
| 823 |
if(match) {
|
| 824 |
+
const startTime = timeToSeconds(match[1]);
|
| 825 |
+
const endTime = timeToSeconds(match[2]);
|
| 826 |
+
const text = match[3].trim();
|
| 827 |
+
if(text && text !== '[Intro]' && text !== '[Chorus]' && text !== '[Verse]') {
|
| 828 |
+
lines.push({ startTime, endTime, text });
|
|
|
|
|
|
|
| 829 |
}
|
| 830 |
}
|
| 831 |
}
|
| 832 |
return lines;
|
| 833 |
}
|
| 834 |
|
| 835 |
+
function timeToSeconds(timeStr) {
|
| 836 |
+
const [h, m, s] = timeStr.split(':');
|
| 837 |
+
return parseFloat(h) * 3600 + parseFloat(m) * 60 + parseFloat(s);
|
| 838 |
+
}
|
| 839 |
+
|
| 840 |
function setupPlayerWithLyrics(audioSrc, lrcData, rawLyrics) {
|
| 841 |
playerWrapper.innerHTML = '';
|
| 842 |
finalLyricsBox.innerHTML = '';
|
|
|
|
| 847 |
audio.src = audioSrc;
|
| 848 |
playerWrapper.appendChild(audio);
|
| 849 |
|
| 850 |
+
// اگر دیتای VTT موجود باشد (سینک شده)
|
| 851 |
+
if (lrcData && typeof lrcData === 'string' && lrcData.includes('WEBVTT')) {
|
| 852 |
+
const parsedLyrics = parseVTT(lrcData);
|
| 853 |
|
| 854 |
parsedLyrics.forEach((line, index) => {
|
| 855 |
const p = document.createElement('p');
|
|
|
|
| 863 |
const currentTime = audio.currentTime;
|
| 864 |
let activeIndex = -1;
|
| 865 |
|
| 866 |
+
// پیدا کردن خطی که در بازه زمانی فعلی است
|
| 867 |
for (let i = 0; i < parsedLyrics.length; i++) {
|
| 868 |
+
if (currentTime >= parsedLyrics[i].startTime && currentTime < parsedLyrics[i].endTime) {
|
| 869 |
activeIndex = i;
|
| 870 |
+
break;
|
|
|
|
| 871 |
}
|
| 872 |
}
|
| 873 |
|
|
|
|
| 885 |
} else {
|
| 886 |
// حالت متن ساده (بدون سینک)
|
| 887 |
if (rawLyrics) {
|
|
|
|
| 888 |
const lines = rawLyrics.split('\n').filter(l => l.trim() !== '');
|
| 889 |
lines.forEach(line => {
|
| 890 |
const p = document.createElement('p');
|
| 891 |
+
p.className = 'lyric-line';
|
| 892 |
p.innerText = line;
|
| 893 |
finalLyricsBox.appendChild(p);
|
| 894 |
});
|
| 895 |
} else {
|
| 896 |
+
finalLyricsBox.innerHTML = '<span style="color:#a0aec0; display:block; padding-top:100px;">(متن آهنگی موجود نیست یا آهنگ بیکلام است)</span>';
|
| 897 |
}
|
| 898 |
}
|
| 899 |
}
|