Update index.html
Browse files- index.html +79 -267
index.html
CHANGED
@@ -3,215 +3,47 @@
|
|
3 |
<head>
|
4 |
<meta charset="UTF-8">
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
-
<title
|
7 |
<link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@400;700&display=swap" rel="stylesheet">
|
8 |
<style>
|
9 |
-
/*
|
10 |
-
:root {
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
}
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
}
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
}
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
}
|
40 |
-
|
41 |
-
|
42 |
-
#delta-gate-container {
|
43 |
-
background: var(--glass-bg);
|
44 |
-
backdrop-filter: blur(15px);
|
45 |
-
-webkit-backdrop-filter: blur(15px);
|
46 |
-
border: 1px solid var(--glass-border);
|
47 |
-
padding: 40px;
|
48 |
-
border-radius: 20px;
|
49 |
-
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.3);
|
50 |
-
text-align: center;
|
51 |
-
max-width: 450px;
|
52 |
-
width: 90%;
|
53 |
-
transition: opacity 0.5s, transform 0.5s;
|
54 |
-
animation: fadeIn 0.8s ease-out;
|
55 |
-
}
|
56 |
-
|
57 |
-
#delta-gate-container.hidden {
|
58 |
-
opacity: 0;
|
59 |
-
transform: scale(0.9);
|
60 |
-
pointer-events: none;
|
61 |
-
}
|
62 |
-
|
63 |
-
.gate-icon {
|
64 |
-
width: 60px;
|
65 |
-
height: 60px;
|
66 |
-
margin-bottom: 20px;
|
67 |
-
opacity: 0.8;
|
68 |
-
}
|
69 |
-
|
70 |
-
#delta-gate-container h1 {
|
71 |
-
color: #fff;
|
72 |
-
margin-bottom: 15px;
|
73 |
-
font-size: 26px;
|
74 |
-
}
|
75 |
-
|
76 |
-
#delta-gate-container p {
|
77 |
-
color: var(--text-color);
|
78 |
-
margin-bottom: 30px;
|
79 |
-
font-size: 16px;
|
80 |
-
line-height: 1.6;
|
81 |
-
}
|
82 |
-
|
83 |
-
#delta-code-input {
|
84 |
-
width: 100%;
|
85 |
-
padding: 15px;
|
86 |
-
background: rgba(0, 0, 0, 0.2);
|
87 |
-
border: 1px solid var(--glass-border);
|
88 |
-
border-radius: 12px;
|
89 |
-
font-size: 16px;
|
90 |
-
text-align: center;
|
91 |
-
color: #fff;
|
92 |
-
box-sizing: border-box;
|
93 |
-
direction: ltr;
|
94 |
-
margin-bottom: 25px;
|
95 |
-
transition: all 0.3s ease;
|
96 |
-
}
|
97 |
-
|
98 |
-
#delta-code-input::placeholder {
|
99 |
-
color: rgba(255, 255, 255, 0.5);
|
100 |
-
}
|
101 |
-
|
102 |
-
#delta-code-input:focus {
|
103 |
-
outline: none;
|
104 |
-
border-color: var(--primary-color);
|
105 |
-
box-shadow: 0 0 0 4px rgba(61, 90, 254, 0.3);
|
106 |
-
}
|
107 |
-
|
108 |
-
#verify-button {
|
109 |
-
width: 100%;
|
110 |
-
padding: 15px;
|
111 |
-
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
112 |
-
color: white;
|
113 |
-
border: none;
|
114 |
-
border-radius: 12px;
|
115 |
-
cursor: pointer;
|
116 |
-
font-size: 18px;
|
117 |
-
font-weight: bold;
|
118 |
-
transition: all 0.3s ease;
|
119 |
-
position: relative;
|
120 |
-
overflow: hidden;
|
121 |
-
}
|
122 |
-
|
123 |
-
#verify-button:hover:not(:disabled) {
|
124 |
-
transform: translateY(-2px);
|
125 |
-
box-shadow: 0 6px 20px rgba(61, 90, 254, 0.4);
|
126 |
-
}
|
127 |
-
|
128 |
-
#verify-button:disabled {
|
129 |
-
background: #555;
|
130 |
-
cursor: not-allowed;
|
131 |
-
}
|
132 |
-
|
133 |
-
.loader {
|
134 |
-
width: 20px;
|
135 |
-
height: 20px;
|
136 |
-
border: 3px solid rgba(255, 255, 255, 0.3);
|
137 |
-
border-top-color: #fff;
|
138 |
-
border-radius: 50%;
|
139 |
-
animation: spin 1s linear infinite;
|
140 |
-
display: inline-block;
|
141 |
-
}
|
142 |
-
|
143 |
-
#error-message {
|
144 |
-
color: #ff8a80;
|
145 |
-
margin-top: 20px;
|
146 |
-
font-weight: bold;
|
147 |
-
min-height: 20px;
|
148 |
-
}
|
149 |
-
|
150 |
-
/* --- Main Content (Vevo API) Styles --- */
|
151 |
-
#main-content-container {
|
152 |
-
display: none;
|
153 |
-
width: 100%;
|
154 |
-
max-width: 650px;
|
155 |
-
}
|
156 |
-
|
157 |
-
#huggingface-vevo-api-container {
|
158 |
-
font-family: 'Vazirmatn', sans-serif;
|
159 |
-
margin: 20px auto;
|
160 |
-
background-color: #f7fafc;
|
161 |
-
color: #2d3748;
|
162 |
-
direction: rtl;
|
163 |
-
padding: 10px;
|
164 |
-
border-radius: 16px;
|
165 |
-
max-width: 600px;
|
166 |
-
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
|
167 |
-
animation: fadeIn 0.8s ease-out;
|
168 |
-
}
|
169 |
-
#huggingface-vevo-api-container .container {
|
170 |
-
padding: 20px;
|
171 |
-
background-color: white;
|
172 |
-
border-radius: 12px;
|
173 |
-
}
|
174 |
-
#huggingface-vevo-api-container h1 {
|
175 |
-
text-align: center; color: #2c5282; font-size: 24px;
|
176 |
-
}
|
177 |
-
#huggingface-vevo-api-container .file-input {
|
178 |
-
margin-bottom: 20px;
|
179 |
-
}
|
180 |
-
#huggingface-vevo-api-container label {
|
181 |
-
display: block; margin-bottom: 8px; font-weight: bold;
|
182 |
-
}
|
183 |
-
#huggingface-vevo-api-container .description {
|
184 |
-
font-size: 14px; color: #718096; margin-top: -5px; margin-bottom: 10px; font-weight: normal;
|
185 |
-
}
|
186 |
-
#huggingface-vevo-api-container input[type="file"] {
|
187 |
-
display: block; width: 100%; padding: 10px; border: 1px solid #cbd5e0; border-radius: 8px; box-sizing: border-box; background-color: #edf2f7; cursor: pointer;
|
188 |
-
}
|
189 |
-
#huggingface-vevo-api-container button {
|
190 |
-
display: block; width: 100%; padding: 14px; background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); color: white; border: none; border-radius: 8px; cursor: pointer; font-size: 16px; font-weight: bold; transition: all 0.3s;
|
191 |
-
}
|
192 |
-
#huggingface-vevo-api-container button:hover:not(:disabled) {
|
193 |
-
transform: translateY(-2px); box-shadow: 0 4px 15px rgba(61, 90, 254, 0.3);
|
194 |
-
}
|
195 |
-
#huggingface-vevo-api-container button:disabled {
|
196 |
-
background: #a0aec0; cursor: not-allowed;
|
197 |
-
}
|
198 |
-
#huggingface-vevo-api-container #status {
|
199 |
-
margin-top: 20px; padding: 12px; background-color: #e2e8f0; border-radius: 8px; text-align: center;
|
200 |
-
}
|
201 |
-
#huggingface-vevo-api-container #result {
|
202 |
-
margin-top: 20px; text-align: center;
|
203 |
-
}
|
204 |
-
#huggingface-vevo-api-container #result audio {
|
205 |
-
width: 100%; margin-top: 10px; border-radius: 8px;
|
206 |
-
}
|
207 |
-
#huggingface-vevo-api-container #result a {
|
208 |
-
color: var(--primary-color); text-decoration: none; display: inline-block; margin-top: 15px; font-weight: bold;
|
209 |
-
}
|
210 |
</style>
|
211 |
</head>
|
212 |
<body>
|
213 |
|
214 |
-
<!--
|
215 |
<div id="delta-gate-container">
|
216 |
<svg class="gate-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" color="#fff"><path d="M17 3.25H7C4.65279 3.25 2.75 5.15279 2.75 7.5V16.5C2.75 18.8472 4.65279 20.75 7 20.75H17C19.3472 20.75 21.25 18.8472 21.25 16.5V7.5C21.25 5.15279 19.3472 3.25 17 3.25ZM19.75 7.5V16.5C19.75 18.0376 18.5376 19.25 17 19.25H7C5.46243 19.25 4.25 18.0376 4.25 16.5V7.5C4.25 5.96243 5.46243 4.75 7 4.75H17C18.5376 4.75 19.75 5.96243 19.75 7.5ZM12 7.75C12.4142 7.75 12.75 8.08579 12.75 8.5V11H14.25C14.6642 11 15 11.3358 15 11.75V13.25C15 13.6642 14.6642 14 14.25 14H12.75V15.5C12.75 15.9142 12.4142 16.25 12 16.25C11.5858 16.25 11.25 15.9142 11.25 15.5V14H9.75C9.33579 14 9 13.6642 9 13.25V11.75C9 11.3358 9.33579 11 9.75 11H11.25V8.5C11.25 8.08579 11.5858 7.75 12 7.75Z"></path></svg>
|
217 |
<h1>ورود به پنل اختصاصی</h1>
|
@@ -220,20 +52,32 @@
|
|
220 |
<button id="verify-button">تأیید و ورود</button>
|
221 |
<div id="error-message"></div>
|
222 |
</div>
|
223 |
-
|
224 |
-
<!-- Main Content: Your Voice Conversion Tool (Initially Hidden) -->
|
225 |
<div id="main-content-container">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
226 |
<div id="huggingface-vevo-api-container">
|
227 |
<div class="container">
|
228 |
<h1>رابط کاربری API برای Vevo</h1>
|
229 |
<div class="file-input">
|
230 |
<label for="sourceAudio">فایل صوتی اصلی (Source Audio):</label>
|
231 |
-
<p class="description">این فایل صوتی حاوی کلامی است که میخواهید با صدای جدید خوانده
|
232 |
<input type="file" id="sourceAudio" accept="audio/*" />
|
233 |
</div>
|
234 |
<div class="file-input">
|
235 |
<label for="timbreRef">فایل صوتی مرجع (Timbre Reference):</label>
|
236 |
-
<p class="description">این فایل صوتی،
|
237 |
<input type="file" id="timbreRef" accept="audio/*" />
|
238 |
</div>
|
239 |
<button id="generateBtn">تبدیل صدا</button>
|
@@ -241,24 +85,16 @@
|
|
241 |
<div id="result"></div>
|
242 |
</div>
|
243 |
</div>
|
244 |
-
|
245 |
-
|
246 |
-
<script>
|
247 |
-
document.addEventListener('DOMContentLoaded', () => {
|
248 |
-
|
249 |
-
// --- Section 1: Gate Logic ---
|
250 |
-
const gateContainer = document.getElementById('delta-gate-container');
|
251 |
-
const mainContentContainer = document.getElementById('main-content-container');
|
252 |
-
const verifyButton = document.getElementById('verify-button');
|
253 |
-
const codeInput = document.getElementById('delta-code-input');
|
254 |
-
const errorMessage = document.getElementById('error-message');
|
255 |
-
const buttonOriginalText = verifyButton.innerHTML;
|
256 |
|
257 |
const showMainContent = () => {
|
258 |
gateContainer.classList.add('hidden');
|
259 |
setTimeout(() => {
|
260 |
gateContainer.style.display = 'none';
|
261 |
mainContentContainer.style.display = 'block';
|
|
|
|
|
|
|
262 |
}, 500);
|
263 |
};
|
264 |
|
@@ -279,33 +115,38 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
279 |
verifyButton.innerHTML = '<div class="loader"></div>';
|
280 |
|
281 |
try {
|
282 |
-
|
283 |
-
const response = await fetch('https://www.aisada.ir/app/gemin.php', {
|
284 |
method: 'POST',
|
285 |
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
286 |
body: `delta_code=${encodeURIComponent(code)}`
|
287 |
});
|
288 |
|
289 |
-
// The PHP script itself will return a response, but we only need to know if it was sent.
|
290 |
-
// A better implementation would check the JSON response, but this works for now.
|
291 |
if (!response.ok) {
|
292 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
293 |
}
|
294 |
-
|
295 |
-
// Assume success if the request was sent without network errors
|
296 |
-
localStorage.setItem('isDeltaCodeValidated', 'true');
|
297 |
-
showMainContent();
|
298 |
|
299 |
} catch (error) {
|
300 |
-
errorMessage.textContent = 'خطای شبکه. از اتصال
|
301 |
verifyButton.disabled = false;
|
302 |
verifyButton.innerHTML = buttonOriginalText;
|
303 |
}
|
304 |
});
|
305 |
|
306 |
-
// ---
|
307 |
-
|
308 |
-
|
|
|
|
|
309 |
const generateBtn = container.querySelector('#generateBtn');
|
310 |
const sourceAudioInput = container.querySelector('#sourceAudio');
|
311 |
const timbreRefInput = container.querySelector('#timbreRef');
|
@@ -314,8 +155,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
314 |
|
315 |
const API_URL = "https://amphion-vevo.hf.space/gradio_api/";
|
316 |
|
|
|
317 |
const generateSessionHash = () => Math.random().toString(36).substring(2, 15);
|
318 |
-
|
319 |
async function uploadFile(file) {
|
320 |
const formData = new FormData();
|
321 |
formData.append("files", file);
|
@@ -324,56 +165,30 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
324 |
if (result && result.length > 0) return result[0];
|
325 |
throw new Error("آپلود فایل با خطا مواجه شد.");
|
326 |
}
|
327 |
-
|
328 |
async function startGeneration() {
|
329 |
const sourceFile = sourceAudioInput.files[0];
|
330 |
const timbreFile = timbreRefInput.files[0];
|
331 |
-
|
332 |
-
if (!sourceFile || !timbreFile) {
|
333 |
-
alert("لطفاً هر دو فایل صوتی را انتخاب کنید.");
|
334 |
-
return;
|
335 |
-
}
|
336 |
-
|
337 |
generateBtn.disabled = true;
|
338 |
statusDiv.textContent = 'در حال آپلود فایل اصلی...';
|
339 |
resultDiv.innerHTML = '';
|
340 |
-
|
341 |
const sessionHash = generateSessionHash();
|
342 |
-
|
343 |
try {
|
344 |
const sourcePath = await uploadFile(sourceFile);
|
345 |
statusDiv.textContent = 'در حال آپلود فایل مرجع...';
|
346 |
const timbrePath = await uploadFile(timbreFile);
|
347 |
-
|
348 |
statusDiv.textContent = 'ارسال درخواست برای پردازش...';
|
349 |
-
|
350 |
-
|
351 |
-
"data": [
|
352 |
-
{ "path": sourcePath, "url": `${API_URL}file=${sourcePath}`, "orig_name": sourceFile.name, "meta": { "_type": "gradio.FileData" } },
|
353 |
-
{ "path": timbrePath, "url": `${API_URL}file=${timbrePath}`, "orig_name": timbreFile.name, "meta": { "_type": "gradio.FileData" } }
|
354 |
-
],
|
355 |
-
"fn_index": 0, "session_hash": sessionHash
|
356 |
-
};
|
357 |
-
|
358 |
-
await fetch(`${API_URL}queue/join?`, {
|
359 |
-
method: 'POST',
|
360 |
-
headers: { 'Content-Type': 'application/json' },
|
361 |
-
body: JSON.stringify(joinPayload)
|
362 |
-
});
|
363 |
-
|
364 |
statusDiv.textContent = 'در صف پردازش. منتظر نتیجه بمانید...';
|
365 |
const eventSource = new EventSource(`${API_URL}queue/data?session_hash=${sessionHash}`);
|
366 |
-
|
367 |
eventSource.onmessage = (event) => {
|
368 |
const data = JSON.parse(event.data);
|
369 |
-
|
370 |
-
if (data.msg === '
|
371 |
-
statusDiv.textContent = 'پردازش شروع شد...';
|
372 |
-
} else if (data.msg === 'process_completed') {
|
373 |
statusDiv.textContent = 'پردازش با موفقیت انجام شد!';
|
374 |
const resultPath = data.output.data[0].path;
|
375 |
const resultUrl = `${API_URL}file=${resultPath}`;
|
376 |
-
|
377 |
resultDiv.innerHTML = `<h3>نتیجه:</h3><audio controls src="${resultUrl}"></audio><br><a href="${resultUrl}" download="output_voice.wav">دانلود فایل</a>`;
|
378 |
eventSource.close();
|
379 |
generateBtn.disabled = false;
|
@@ -383,19 +198,16 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
383 |
generateBtn.disabled = false;
|
384 |
}
|
385 |
};
|
386 |
-
|
387 |
eventSource.onerror = () => {
|
388 |
statusDiv.textContent = 'خطا در ارتباط با سرور.';
|
389 |
eventSource.close();
|
390 |
generateBtn.disabled = false;
|
391 |
};
|
392 |
-
|
393 |
} catch (error) {
|
394 |
statusDiv.textContent = `خطا: ${error.message}`;
|
395 |
generateBtn.disabled = false;
|
396 |
}
|
397 |
}
|
398 |
-
|
399 |
generateBtn.addEventListener('click', startGeneration);
|
400 |
}
|
401 |
});
|
|
|
3 |
<head>
|
4 |
<meta charset="UTF-8">
|
5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<title>ورود به پنل اختصاصی</title>
|
7 |
<link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@400;700&display=swap" rel="stylesheet">
|
8 |
<style>
|
9 |
+
/* CSS styles from the previous response are here... (I've kept them exactly the same) */
|
10 |
+
:root { --primary-color: #3d5afe; --secondary-color: #00b0ff; --text-color: #e0e0e0; --bg-color-start: #1a2940; --bg-color-end: #2b3b58; --glass-bg: rgba(255, 255, 255, 0.05); --glass-border: rgba(255, 255, 255, 0.2); }
|
11 |
+
@keyframes fadeIn { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
|
12 |
+
@keyframes spin { to { transform: rotate(360deg); } }
|
13 |
+
body { font-family: 'Vazirmatn', sans-serif; background: linear-gradient(135deg, var(--bg-color-start), var(--bg-color-end)); color: var(--text-color); margin: 0; display: flex; justify-content: center; align-items: center; min-height: 100vh; overflow-x: hidden; }
|
14 |
+
#delta-gate-container { background: var(--glass-bg); backdrop-filter: blur(15px); -webkit-backdrop-filter: blur(15px); border: 1px solid var(--glass-border); padding: 40px; border-radius: 20px; box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.3); text-align: center; max-width: 450px; width: 90%; transition: opacity 0.5s, transform 0.5s; animation: fadeIn 0.8s ease-out; }
|
15 |
+
#delta-gate-container.hidden { opacity: 0; transform: scale(0.9); pointer-events: none; }
|
16 |
+
.gate-icon { width: 60px; height: 60px; margin-bottom: 20px; opacity: 0.8; }
|
17 |
+
#delta-gate-container h1 { color: #fff; margin-bottom: 15px; font-size: 26px; }
|
18 |
+
#delta-gate-container p { color: var(--text-color); margin-bottom: 30px; font-size: 16px; line-height: 1.6; }
|
19 |
+
#delta-code-input { width: 100%; padding: 15px; background: rgba(0, 0, 0, 0.2); border: 1px solid var(--glass-border); border-radius: 12px; font-size: 16px; text-align: center; color: #fff; box-sizing: border-box; direction: ltr; margin-bottom: 25px; transition: all 0.3s ease; }
|
20 |
+
#delta-code-input::placeholder { color: rgba(255, 255, 255, 0.5); }
|
21 |
+
#delta-code-input:focus { outline: none; border-color: var(--primary-color); box-shadow: 0 0 0 4px rgba(61, 90, 254, 0.3); }
|
22 |
+
#verify-button { width: 100%; padding: 15px; background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); color: white; border: none; border-radius: 12px; cursor: pointer; font-size: 18px; font-weight: bold; transition: all 0.3s ease; position: relative; overflow: hidden; }
|
23 |
+
#verify-button:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(61, 90, 254, 0.4); }
|
24 |
+
#verify-button:disabled { background: #555; cursor: not-allowed; }
|
25 |
+
.loader { width: 20px; height: 20px; border: 3px solid rgba(255, 255, 255, 0.3); border-top-color: #fff; border-radius: 50%; animation: spin 1s linear infinite; display: inline-block; }
|
26 |
+
#error-message { color: #ff8a80; margin-top: 20px; font-weight: bold; min-height: 20px; }
|
27 |
+
#main-content-container { display: none; width: 100%; max-width: 650px; }
|
28 |
+
#huggingface-vevo-api-container { font-family: 'Vazirmatn', sans-serif; margin: 20px auto; background-color: #f7fafc; color: #2d3748; direction: rtl; padding: 10px; border-radius: 16px; max-width: 600px; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1); animation: fadeIn 0.8s ease-out; }
|
29 |
+
#huggingface-vevo-api-container .container { padding: 20px; background-color: white; border-radius: 12px; }
|
30 |
+
#huggingface-vevo-api-container h1 { text-align: center; color: #2c5282; font-size: 24px; }
|
31 |
+
#huggingface-vevo-api-container .file-input { margin-bottom: 20px; }
|
32 |
+
#huggingface-vevo-api-container label { display: block; margin-bottom: 8px; font-weight: bold; }
|
33 |
+
#huggingface-vevo-api-container .description { font-size: 14px; color: #718096; margin-top: -5px; margin-bottom: 10px; font-weight: normal; }
|
34 |
+
#huggingface-vevo-api-container input[type="file"] { display: block; width: 100%; padding: 10px; border: 1px solid #cbd5e0; border-radius: 8px; box-sizing: border-box; background-color: #edf2f7; cursor: pointer; }
|
35 |
+
#huggingface-vevo-api-container button { display: block; width: 100%; padding: 14px; background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); color: white; border: none; border-radius: 8px; cursor: pointer; font-size: 16px; font-weight: bold; transition: all 0.3s; }
|
36 |
+
#huggingface-vevo-api-container button:hover:not(:disabled) { transform: translateY(-2px); box-shadow: 0 4px 15px rgba(61, 90, 254, 0.3); }
|
37 |
+
#huggingface-vevo-api-container button:disabled { background: #a0aec0; cursor: not-allowed; }
|
38 |
+
#huggingface-vevo-api-container #status { margin-top: 20px; padding: 12px; background-color: #e2e8f0; border-radius: 8px; text-align: center; }
|
39 |
+
#huggingface-vevo-api-container #result { margin-top: 20px; text-align: center; }
|
40 |
+
#huggingface-vevo-api-container #result audio { width: 100%; margin-top: 10px; border-radius: 8px; }
|
41 |
+
#huggingface-vevo-api-container #result a { color: var(--primary-color); text-decoration: none; display: inline-block; margin-top: 15px; font-weight: bold; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
</style>
|
43 |
</head>
|
44 |
<body>
|
45 |
|
46 |
+
<!-- HTML part remains the same -->
|
47 |
<div id="delta-gate-container">
|
48 |
<svg class="gate-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" color="#fff"><path d="M17 3.25H7C4.65279 3.25 2.75 5.15279 2.75 7.5V16.5C2.75 18.8472 4.65279 20.75 7 20.75H17C19.3472 20.75 21.25 18.8472 21.25 16.5V7.5C21.25 5.15279 19.3472 3.25 17 3.25ZM19.75 7.5V16.5C19.75 18.0376 18.5376 19.25 17 19.25H7C5.46243 19.25 4.25 18.0376 4.25 16.5V7.5C4.25 5.96243 5.46243 4.75 7 4.75H17C18.5376 4.75 19.75 5.96243 19.75 7.5ZM12 7.75C12.4142 7.75 12.75 8.08579 12.75 8.5V11H14.25C14.6642 11 15 11.3358 15 11.75V13.25C15 13.6642 14.6642 14 14.25 14H12.75V15.5C12.75 15.9142 12.4142 16.25 12 16.25C11.5858 16.25 11.25 15.9142 11.25 15.5V14H9.75C9.33579 14 9 13.6642 9 13.25V11.75C9 11.3358 9.33579 11 9.75 11H11.25V8.5C11.25 8.08579 11.5858 7.75 12 7.75Z"></path></svg>
|
49 |
<h1>ورود به پنل اختصاصی</h1>
|
|
|
52 |
<button id="verify-button">تأیید و ورود</button>
|
53 |
<div id="error-message"></div>
|
54 |
</div>
|
|
|
|
|
55 |
<div id="main-content-container">
|
56 |
+
<!-- Main content will be loaded here -->
|
57 |
+
</div>
|
58 |
+
|
59 |
+
<script>
|
60 |
+
document.addEventListener('DOMContentLoaded', () => {
|
61 |
+
const gateContainer = document.getElementById('delta-gate-container');
|
62 |
+
const mainContentContainer = document.getElementById('main-content-container');
|
63 |
+
const verifyButton = document.getElementById('verify-button');
|
64 |
+
const codeInput = document.getElementById('delta-code-input');
|
65 |
+
const errorMessage = document.getElementById('error-message');
|
66 |
+
const buttonOriginalText = verifyButton.innerHTML;
|
67 |
+
|
68 |
+
// The main app's HTML content
|
69 |
+
const mainAppHTML = `
|
70 |
<div id="huggingface-vevo-api-container">
|
71 |
<div class="container">
|
72 |
<h1>رابط کاربری API برای Vevo</h1>
|
73 |
<div class="file-input">
|
74 |
<label for="sourceAudio">فایل صوتی اصلی (Source Audio):</label>
|
75 |
+
<p class="description">این فایل صوتی حاوی کلامی است که میخواهید با صدای جدید خوانده شود.</p>
|
76 |
<input type="file" id="sourceAudio" accept="audio/*" />
|
77 |
</div>
|
78 |
<div class="file-input">
|
79 |
<label for="timbreRef">فایل صوتی مرجع (Timbre Reference):</label>
|
80 |
+
<p class="description">این فایل صوتی، صدای مدلی است که میخواهید به آن تبدیل شود.</p>
|
81 |
<input type="file" id="timbreRef" accept="audio/*" />
|
82 |
</div>
|
83 |
<button id="generateBtn">تبدیل صدا</button>
|
|
|
85 |
<div id="result"></div>
|
86 |
</div>
|
87 |
</div>
|
88 |
+
`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
|
90 |
const showMainContent = () => {
|
91 |
gateContainer.classList.add('hidden');
|
92 |
setTimeout(() => {
|
93 |
gateContainer.style.display = 'none';
|
94 |
mainContentContainer.style.display = 'block';
|
95 |
+
mainContentContainer.innerHTML = mainAppHTML;
|
96 |
+
// Now, initialize the Vevo API script AFTER the content is on the page
|
97 |
+
initializeVevoAPI();
|
98 |
}, 500);
|
99 |
};
|
100 |
|
|
|
115 |
verifyButton.innerHTML = '<div class="loader"></div>';
|
116 |
|
117 |
try {
|
118 |
+
const response = await fetch(window.location.href, { // POST to the same page
|
|
|
119 |
method: 'POST',
|
120 |
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
121 |
body: `delta_code=${encodeURIComponent(code)}`
|
122 |
});
|
123 |
|
|
|
|
|
124 |
if (!response.ok) {
|
125 |
+
throw new Error('خطای سرور');
|
126 |
+
}
|
127 |
+
|
128 |
+
const result = await response.json(); // Parse the JSON response from PHP
|
129 |
+
|
130 |
+
if (result.status === 'success') {
|
131 |
+
localStorage.setItem('isDeltaCodeValidated', 'true');
|
132 |
+
showMainContent();
|
133 |
+
} else {
|
134 |
+
// Show error message from server (e.g., "Failed to contact Telegram")
|
135 |
+
throw new Error(result.message || 'خطای ناشناخته از سمت سرور.');
|
136 |
}
|
|
|
|
|
|
|
|
|
137 |
|
138 |
} catch (error) {
|
139 |
+
errorMessage.textContent = error.message.includes('خطا') ? error.message : 'خطای شبکه. از اتصال خود مطمئن شوید.';
|
140 |
verifyButton.disabled = false;
|
141 |
verifyButton.innerHTML = buttonOriginalText;
|
142 |
}
|
143 |
});
|
144 |
|
145 |
+
// --- Vevo API Logic is now in its own function ---
|
146 |
+
function initializeVevoAPI() {
|
147 |
+
const container = document.getElementById('huggingface-vevo-api-container');
|
148 |
+
if (!container) return; // Exit if the container isn't ready
|
149 |
+
|
150 |
const generateBtn = container.querySelector('#generateBtn');
|
151 |
const sourceAudioInput = container.querySelector('#sourceAudio');
|
152 |
const timbreRefInput = container.querySelector('#timbreRef');
|
|
|
155 |
|
156 |
const API_URL = "https://amphion-vevo.hf.space/gradio_api/";
|
157 |
|
158 |
+
// ... (The rest of the Vevo API JavaScript is identical, no changes needed)
|
159 |
const generateSessionHash = () => Math.random().toString(36).substring(2, 15);
|
|
|
160 |
async function uploadFile(file) {
|
161 |
const formData = new FormData();
|
162 |
formData.append("files", file);
|
|
|
165 |
if (result && result.length > 0) return result[0];
|
166 |
throw new Error("آپلود فایل با خطا مواجه شد.");
|
167 |
}
|
|
|
168 |
async function startGeneration() {
|
169 |
const sourceFile = sourceAudioInput.files[0];
|
170 |
const timbreFile = timbreRefInput.files[0];
|
171 |
+
if (!sourceFile || !timbreFile) { alert("لطفاً هر دو فایل صوتی را انتخاب کنید."); return; }
|
|
|
|
|
|
|
|
|
|
|
172 |
generateBtn.disabled = true;
|
173 |
statusDiv.textContent = 'در حال آپلود فایل اصلی...';
|
174 |
resultDiv.innerHTML = '';
|
|
|
175 |
const sessionHash = generateSessionHash();
|
|
|
176 |
try {
|
177 |
const sourcePath = await uploadFile(sourceFile);
|
178 |
statusDiv.textContent = 'در حال آپلود فایل مرجع...';
|
179 |
const timbrePath = await uploadFile(timbreFile);
|
|
|
180 |
statusDiv.textContent = 'ارسال درخواست برای پردازش...';
|
181 |
+
const joinPayload = { "data": [ { "path": sourcePath, "url": `${API_URL}file=${sourcePath}`, "orig_name": sourceFile.name, "meta": { "_type": "gradio.FileData" } }, { "path": timbrePath, "url": `${API_URL}file=${timbrePath}`, "orig_name": timbreFile.name, "meta": { "_type": "gradio.FileData" } } ], "fn_index": 0, "session_hash": sessionHash };
|
182 |
+
await fetch(`${API_URL}queue/join?`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(joinPayload) });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
183 |
statusDiv.textContent = 'در صف پردازش. منتظر نتیجه بمانید...';
|
184 |
const eventSource = new EventSource(`${API_URL}queue/data?session_hash=${sessionHash}`);
|
|
|
185 |
eventSource.onmessage = (event) => {
|
186 |
const data = JSON.parse(event.data);
|
187 |
+
if (data.msg === 'process_starts') { statusDiv.textContent = 'پردازش شروع شد...'; }
|
188 |
+
else if (data.msg === 'process_completed') {
|
|
|
|
|
189 |
statusDiv.textContent = 'پردازش با موفقیت انجام شد!';
|
190 |
const resultPath = data.output.data[0].path;
|
191 |
const resultUrl = `${API_URL}file=${resultPath}`;
|
|
|
192 |
resultDiv.innerHTML = `<h3>نتیجه:</h3><audio controls src="${resultUrl}"></audio><br><a href="${resultUrl}" download="output_voice.wav">دانلود فایل</a>`;
|
193 |
eventSource.close();
|
194 |
generateBtn.disabled = false;
|
|
|
198 |
generateBtn.disabled = false;
|
199 |
}
|
200 |
};
|
|
|
201 |
eventSource.onerror = () => {
|
202 |
statusDiv.textContent = 'خطا در ارتباط با سرور.';
|
203 |
eventSource.close();
|
204 |
generateBtn.disabled = false;
|
205 |
};
|
|
|
206 |
} catch (error) {
|
207 |
statusDiv.textContent = `خطا: ${error.message}`;
|
208 |
generateBtn.disabled = false;
|
209 |
}
|
210 |
}
|
|
|
211 |
generateBtn.addEventListener('click', startGeneration);
|
212 |
}
|
213 |
});
|