Hamed744 commited on
Commit
9194ea0
·
verified ·
1 Parent(s): 5ee67e2

Upload wordpress_export (9).html

Browse files
Files changed (1) hide show
  1. lib/wordpress_export (9).html +481 -0
lib/wordpress_export (9).html ADDED
@@ -0,0 +1,481 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="fa" dir="rtl">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>تولید صدای هوشمند با هوش مصنوعی | AI Sada</title>
7
+ <meta name="description" content="با AI Sada، متن فارسی خود را به صدایی طبیعی تبدیل کنید.">
8
+ <!-- Font Awesome -->
9
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
10
+ <!-- Turnstile -->
11
+ <script src="https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit" async defer></script>
12
+
13
+ <style>
14
+ @import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;600;700;800;900&display=swap');
15
+
16
+ :root {
17
+ --app-font: 'Vazirmatn', sans-serif; --app-bg: #F8F9FC; --panel-bg: #FFFFFF;
18
+ --panel-border: #EAEFF7; --input-bg: #F6F8FB; --input-border: #E1E7EF;
19
+ --text-primary: #1A202C; --text-secondary: #626F86; --text-tertiary: #8A94A6;
20
+ --accent-primary: #4A6CFA; --accent-primary-hover: #3553D6;
21
+ --accent-secondary: #0FD4A8; --accent-secondary-hover: #0DA986;
22
+ --shadow-lg: 0 10px 15px -3px rgba(26, 32, 44, 0.06);
23
+ --radius-card: 24px; --radius-btn: 14px; --radius-input: 12px;
24
+ --transition-smooth: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
25
+ --danger: #E53E3E;
26
+ }
27
+
28
+ body { font-family: var(--app-font); direction: rtl; background-color: var(--app-bg); color: var(--text-primary); margin: 0; padding: 0; }
29
+ .page-wrapper { max-width: 820px; width: 92%; margin: 0 auto; padding: 2rem 0; }
30
+ .app-header { text-align: center; margin-bottom: 2rem; }
31
+ .app-header h1 { font-size: 2.2em; font-weight: 900; background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-bottom: 0.5rem; }
32
+
33
+ .main-content { background: var(--panel-bg); padding: 2rem; border-radius: var(--radius-card); box-shadow: var(--shadow-lg); border: 1px solid var(--panel-border); }
34
+ .form-group { margin-bottom: 1.5rem; }
35
+ label { display: block; font-weight: 700; margin-bottom: 0.5rem; }
36
+ textarea, input[type="text"], input[type="email"] { width: 100%; padding: 1rem; border-radius: var(--radius-input); border: 1px solid var(--input-border); background: var(--input-bg); font-family: inherit; font-size: 1rem; box-sizing: border-box; }
37
+
38
+ .generate-btn { width: 100%; padding: 1rem; background: linear-gradient(90deg, var(--accent-secondary), var(--accent-primary)); color: white; border: none; border-radius: var(--radius-btn); font-weight: 800; font-size: 1.1em; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 10px; transition: var(--transition-smooth); }
39
+ .generate-btn:disabled { opacity: 0.6; cursor: not-allowed; }
40
+ .spinner { 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: none; }
41
+ @keyframes spin { to { transform: rotate(360deg); } }
42
+
43
+ .output-section { margin-top: 2rem; background: var(--input-bg); padding: 1.5rem; border-radius: var(--radius-card); border: 2px dashed var(--input-border); text-align: center; min-height: 100px; display: flex; flex-direction: column; justify-content: center; }
44
+ .output-section.has-content { border-style: solid; background: #fff; border-color: var(--panel-border); }
45
+
46
+ .request-card { background: #fff; border: 1px solid var(--panel-border); padding: 1rem; border-radius: 16px; margin-bottom: 1rem; display: flex; flex-direction: column; gap: 10px; box-shadow: 0 2px 4px rgba(0,0,0,0.02); }
47
+ .card-header { display: flex; justify-content: space-between; align-items: center; }
48
+ .project-name { font-weight: 700; font-size: 1rem; }
49
+ .project-status { font-size: 0.8rem; padding: 2px 8px; border-radius: 4px; background: #eee; }
50
+ .status-processing { background: #EBF8FF; color: #2B6CB0; }
51
+ .status-completed { background: #F0FFF4; color: #2F855A; }
52
+ .status-failed { background: #FFF5F5; color: #C53030; }
53
+
54
+ .progress-bar { width: 100%; height: 6px; background: #eee; border-radius: 3px; overflow: hidden; margin-top: 5px; }
55
+ .progress-fill { height: 100%; background: var(--accent-primary); width: 0%; transition: width 0.3s; }
56
+
57
+ /* Upload Area */
58
+ .upload-area { border: 2px dashed var(--input-border); padding: 2rem; border-radius: var(--radius-input); cursor: pointer; background: var(--input-bg); transition: 0.3s; }
59
+ .upload-area:hover { border-color: var(--accent-primary); background: #fff; }
60
+ #file-preview { display: none; align-items: center; justify-content: space-between; background: #e0e7ff; padding: 10px; border-radius: 10px; margin-top: 10px; color: #3730a3; font-weight: 600; }
61
+
62
+ .modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.5); backdrop-filter: blur(5px); display: none; align-items: center; justify-content: center; z-index: 999; }
63
+ .modal-dialog { background: #fff; padding: 2rem; border-radius: 20px; width: 90%; max-width: 400px; position: relative; }
64
+ .close-modal-btn { position: absolute; top: 1rem; left: 1rem; background: none; border: none; font-size: 1.5rem; cursor: pointer; }
65
+
66
+ /* Audio Player Simple */
67
+ .simple-player { width: 100%; margin-top: 10px; }
68
+ .download-btn { display: block; margin-top: 5px; text-align: center; background: #f0fdf4; color: #166534; padding: 8px; border-radius: 8px; text-decoration: none; font-weight: 600; font-size: 0.9rem; }
69
+
70
+ #cf-container-clone, #cf-container-login { margin: 1rem 0; display: flex; justify-content: center; }
71
+
72
+ /* Hide Standard View by default for this snippet focus */
73
+ #standard-view { display: none; }
74
+ #voice-clone-view { display: block; }
75
+
76
+ .nav-tabs { display: flex; justify-content: center; gap: 10px; margin-bottom: 1.5rem; }
77
+ .nav-btn { padding: 8px 16px; border: 1px solid var(--panel-border); background: #fff; border-radius: 20px; cursor: pointer; }
78
+ .nav-btn.active { background: var(--accent-primary); color: white; border-color: var(--accent-primary); }
79
+
80
+ #user-status-container { display: none; justify-content: center; gap: 10px; margin-bottom: 1rem; font-size: 0.9rem; font-weight: 600; }
81
+ </style>
82
+ </head>
83
+ <body>
84
+
85
+ <div class="page-wrapper">
86
+ <div class="app-container">
87
+
88
+ <header class="app-header">
89
+ <h1>هوش مصنوعی آلفا</h1>
90
+ <div id="user-status-container">
91
+ <span id="user-email-display"></span>
92
+ <button id="logout-btn" style="background:none;border:none;color:red;cursor:pointer;">خروج</button>
93
+ </div>
94
+ <button id="login-check-btn" class="generate-btn" style="width: auto; margin: 0 auto; padding: 0.5rem 1.5rem;">ورود / ثبت نام</button>
95
+ </header>
96
+
97
+ <div class="nav-tabs">
98
+ <button class="nav-btn" onclick="switchTab('standard')">متن به صدا</button>
99
+ <button class="nav-btn active" onclick="switchTab('clone')">شبیه‌سازی صدا</button>
100
+ </div>
101
+
102
+ <!-- STANDARD VIEW (Placeholder) -->
103
+ <div id="standard-view" style="text-align:center; padding: 2rem;">
104
+ <p>برای بخش متن به صدا، به کد اصلی مراجعه کنید. تمرکز این فایل روی رفع مشکل شبیه‌سازی است.</p>
105
+ </div>
106
+
107
+ <!-- VOICE CLONE VIEW -->
108
+ <div id="voice-clone-view">
109
+ <main class="main-content">
110
+ <form id="voice-clone-form" onsubmit="return false;">
111
+ <div class="form-group">
112
+ <label>📝 متن اصلی</label>
113
+ <textarea id="text-input-clone" placeholder="متنی که می‌خواهید با صدای خودتان خوانده شود..."></textarea>
114
+ </div>
115
+
116
+ <div class="form-group">
117
+ <label>🎤 صدای شما (مرجع)</label>
118
+ <label class="upload-area" id="upload-area">
119
+ <div>📂</div>
120
+ <p>فایل صوتی خود را اینجا بکشید یا کلیک کنید (۳ تا ۱۰ ثانیه، فرمت WAV/MP3)</p>
121
+ <input type="file" id="user-voice-input" accept="audio/*" style="display: none;">
122
+ </label>
123
+ <div id="file-preview">
124
+ <span id="file-name"></span>
125
+ <button type="button" id="remove-file-btn" style="background:none;border:none;cursor:pointer;">✕</button>
126
+ </div>
127
+ </div>
128
+
129
+ <div id="cf-container-clone"></div>
130
+
131
+ <button type="submit" id="generate-btn-clone" class="generate-btn">
132
+ <span class="btn-text">شروع پردازش</span>
133
+ <div class="spinner"></div>
134
+ </button>
135
+ </form>
136
+
137
+ <div style="margin-top: 2rem;">
138
+ <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:1rem;">
139
+ <h3>تاریخچه درخواست‌ها</h3>
140
+ <button id="clear-history" style="background:none;border:none;color:gray;cursor:pointer;">پاکسازی</button>
141
+ </div>
142
+ <div id="history-list"></div>
143
+ </div>
144
+ </main>
145
+ </div>
146
+
147
+ </div>
148
+ </div>
149
+
150
+ <!-- Login Modal -->
151
+ <div id="email-modal" class="modal-overlay">
152
+ <div class="modal-dialog">
153
+ <button class="close-modal-btn" onclick="document.getElementById('email-modal').classList.remove('visible')">×</button>
154
+ <h2>ورود به حساب</h2>
155
+ <form id="email-form">
156
+ <input type="email" id="login-email-input" placeholder="ایمیل خود را وارد کنید" required style="margin-bottom:1rem;">
157
+ <div id="cf-container-login"></div>
158
+ <button type="submit" class="generate-btn">ارسال کد</button>
159
+ </form>
160
+ <form id="code-form" style="display:none; margin-top:1rem;">
161
+ <input type="text" id="code-input" placeholder="کد تایید" required style="margin-bottom:1rem;">
162
+ <button type="submit" class="generate-btn">تایید</button>
163
+ </form>
164
+ </div>
165
+ </div>
166
+
167
+ <script>
168
+ // --- Config ---
169
+ const PROXY_URL = '/tts/proxy.php'; // Ensure this matches your PHP filename/path
170
+ let widgetIdClone, widgetIdLogin;
171
+ let currentUser = { email: localStorage.getItem('userEmail'), status: 'unknown' };
172
+
173
+ // --- UI Helpers ---
174
+ function switchTab(tab) {
175
+ document.getElementById('standard-view').style.display = tab === 'standard' ? 'block' : 'none';
176
+ document.getElementById('voice-clone-view').style.display = tab === 'clone' ? 'block' : 'none';
177
+ document.querySelectorAll('.nav-btn').forEach(b => b.classList.remove('active'));
178
+ event.target.classList.add('active');
179
+ }
180
+
181
+ function updateAuthUI() {
182
+ if(currentUser.email) {
183
+ document.getElementById('user-email-display').textContent = currentUser.email;
184
+ document.getElementById('user-status-container').style.display = 'flex';
185
+ document.getElementById('login-check-btn').style.display = 'none';
186
+ } else {
187
+ document.getElementById('user-status-container').style.display = 'none';
188
+ document.getElementById('login-check-btn').style.display = 'block';
189
+ }
190
+ }
191
+ updateAuthUI();
192
+
193
+ // --- History Logic ---
194
+ function getJobs() { return JSON.parse(localStorage.getItem('aisada_jobs_v2') || '[]'); }
195
+ function saveJob(job) {
196
+ let jobs = getJobs();
197
+ const existingIndex = jobs.findIndex(j => j.id === job.id);
198
+ if(existingIndex > -1) jobs[existingIndex] = job;
199
+ else jobs.unshift(job);
200
+ localStorage.setItem('aisada_jobs_v2', JSON.stringify(jobs));
201
+ renderHistory();
202
+ }
203
+
204
+ function renderHistory() {
205
+ const list = document.getElementById('history-list');
206
+ list.innerHTML = '';
207
+ const jobs = getJobs();
208
+
209
+ jobs.forEach(job => {
210
+ let statusHtml = '';
211
+ let contentHtml = '';
212
+
213
+ if(job.status === 'completed') {
214
+ statusHtml = '<span class="project-status status-completed">تکمیل شد</span>';
215
+ const dlUrl = `${PROXY_URL}?endpoint=download-clone&filename=${job.filename}`;
216
+ contentHtml = `
217
+ <audio controls src="${dlUrl}" class="simple-player"></audio>
218
+ <a href="${dlUrl}" class="download-btn">دانلود فایل نهایی</a>
219
+ `;
220
+ } else if(job.status === 'failed') {
221
+ statusHtml = '<span class="project-status status-failed">خطا</span>';
222
+ contentHtml = `<p style="color:red;font-size:0.9rem;">خطا: ${job.error || 'ناشناخته'}</p>`;
223
+ } else {
224
+ statusHtml = '<span class="project-status status-processing">در حال پردازش</span>';
225
+ contentHtml = `
226
+ <div class="progress-bar"><div class="progress-fill" style="width:${job.progress || 10}%"></div></div>
227
+ <p style="font-size:0.8rem;color:gray;margin-top:5px;">${job.step_desc || 'در حال انجام کار...'}</p>
228
+ `;
229
+ }
230
+
231
+ const div = document.createElement('div');
232
+ div.className = 'request-card';
233
+ div.innerHTML = `
234
+ <div class="card-header">
235
+ <span class="project-name">${job.text_preview}</span>
236
+ ${statusHtml}
237
+ </div>
238
+ ${contentHtml}
239
+ `;
240
+ list.appendChild(div);
241
+ });
242
+ }
243
+
244
+ // --- File Handling ---
245
+ const fileInput = document.getElementById('user-voice-input');
246
+ const uploadArea = document.getElementById('upload-area');
247
+ document.getElementById('remove-file-btn').addEventListener('click', () => {
248
+ fileInput.value = '';
249
+ document.getElementById('file-preview').style.display = 'none';
250
+ uploadArea.style.display = 'block';
251
+ });
252
+ fileInput.addEventListener('change', () => {
253
+ if(fileInput.files[0]) {
254
+ document.getElementById('file-name').textContent = fileInput.files[0].name;
255
+ uploadArea.style.display = 'none';
256
+ document.getElementById('file-preview').style.display = 'flex';
257
+ }
258
+ });
259
+ uploadArea.addEventListener('click', () => fileInput.click());
260
+
261
+ // --- The Core Cloning Logic (Orchestrator) ---
262
+ async function runCloneProcess(text, file, turnstileToken) {
263
+ const jobId = 'job_' + Date.now();
264
+ const newJob = {
265
+ id: jobId,
266
+ text_preview: text.substring(0, 20) + '...',
267
+ status: 'processing',
268
+ progress: 5,
269
+ step_desc: 'تولید صدای پایه (TTS)...'
270
+ };
271
+ saveJob(newJob);
272
+
273
+ try {
274
+ // Step 1: Generate TTS
275
+ const ttsParams = {
276
+ text: text,
277
+ speaker: 'Charon', // Base speaker for raw audio
278
+ temperature: 0.1,
279
+ email: currentUser.email,
280
+ turnstile_token: turnstileToken,
281
+ fingerprint: 'browser_fp'
282
+ };
283
+
284
+ const ttsInitRes = await fetch(`${PROXY_URL}?endpoint=generate`, {
285
+ method: 'POST',
286
+ headers: {'Content-Type': 'application/json'},
287
+ body: JSON.stringify(ttsParams)
288
+ });
289
+
290
+ if(!ttsInitRes.ok) throw new Error('خطا در شروع تولید صدا');
291
+ const ttsInitData = await ttsInitRes.json();
292
+ const ttsJobId = ttsInitData.job_id;
293
+
294
+ // Step 2: Poll TTS
295
+ let ttsAudioUrl = null;
296
+ for(let i=0; i<30; i++) { // Max 90 seconds wait
297
+ newJob.progress = 10 + (i*2); // Fake progress up to 70%
298
+ newJob.step_desc = 'در حال ساخت صدای پایه...';
299
+ saveJob(newJob);
300
+
301
+ const pollRes = await fetch(`${PROXY_URL}?endpoint=check-tts-status`, {
302
+ method: 'POST',
303
+ headers: {'Content-Type': 'application/json'},
304
+ body: JSON.stringify({job_id: ttsJobId})
305
+ });
306
+ const pollData = await pollRes.json();
307
+
308
+ if(pollData.status === 'completed' && pollData.proxy_url) {
309
+ // For the proxy to download it, we might need the full URL or handle properly
310
+ // The React app uses: `${TTS_BASE_URL}${statusData.proxy_url}`
311
+ // Here we assume the proxy can handle the download if we fetch it client side
312
+ // But to upload via PHP, we need the Blob here.
313
+
314
+ // Fetch the actual audio blob from the TTS server (via proxy if needed, or direct if CORS allowed)
315
+ // The 'proxy_url' is usually relative e.g. /download/file.wav.
316
+ // We need to fetch it. Since user's PHP is simple, let's try fetching via the new 'download-clone' endpoint with url param if CORS fails
317
+
318
+ // Construct absolute URL for TTS
319
+ const ttsBase = 'https://ezmary-padgenpro2.hf.space';
320
+ ttsAudioUrl = ttsBase + pollData.proxy_url;
321
+ break;
322
+ }
323
+ if(pollData.status === 'failed') throw new Error('تولید صدای پایه ناموفق بود.');
324
+ await new Promise(r => setTimeout(r, 3000));
325
+ }
326
+
327
+ if(!ttsAudioUrl) throw new Error('تایم‌اوت در تولید صدا.');
328
+
329
+ // Step 3: Fetch TTS Blob
330
+ newJob.step_desc = 'دانلود صدای پایه...';
331
+ saveJob(newJob);
332
+ const ttsBlob = await fetch(ttsAudioUrl).then(r => r.blob());
333
+
334
+ // Step 4: Upload to VC (Source = TTS Blob, Ref = User File)
335
+ newJob.step_desc = 'ارسال به موتور شبیه‌سازی...';
336
+ newJob.progress = 75;
337
+ saveJob(newJob);
338
+
339
+ const formData = new FormData();
340
+ formData.append('email', currentUser.email);
341
+ formData.append('source_audio', ttsBlob, 'source.wav');
342
+ formData.append('ref_audio', file, 'ref.wav');
343
+
344
+ const vcUploadRes = await fetch(`${PROXY_URL}?endpoint=vc-upload`, {
345
+ method: 'POST',
346
+ body: formData
347
+ });
348
+ if(!vcUploadRes.ok) throw new Error('خطا در ارسال به سرور کلون');
349
+ const vcInitData = await vcUploadRes.json();
350
+ const vcJobId = vcInitData.job_id;
351
+
352
+ // Step 5: Poll VC
353
+ for(let i=0; i<30; i++) {
354
+ newJob.progress = 80 + i;
355
+ newJob.step_desc = 'نهایی‌سازی شبیه‌سازی...';
356
+ saveJob(newJob);
357
+
358
+ const vcPollRes = await fetch(`${PROXY_URL}?endpoint=vc-status`, {
359
+ method: 'POST',
360
+ headers: {'Content-Type': 'application/json'},
361
+ body: JSON.stringify({
362
+ job_id: vcJobId,
363
+ total_chunks: vcInitData.total_chunks || 1,
364
+ chunks: vcInitData.chunks || []
365
+ })
366
+ });
367
+ const vcData = await vcPollRes.json();
368
+
369
+ if(vcData.status === 'completed') {
370
+ newJob.status = 'completed';
371
+ newJob.progress = 100;
372
+ newJob.filename = vcData.filename;
373
+ saveJob(newJob);
374
+ return;
375
+ }
376
+ if(vcData.status === 'failed') throw new Error('خطا در موتور شبیه‌سازی');
377
+
378
+ await new Promise(r => setTimeout(r, 3000));
379
+ }
380
+ throw new Error('تایم‌اوت در شبیه‌سازی نهایی.');
381
+
382
+ } catch(e) {
383
+ newJob.status = 'failed';
384
+ newJob.error = e.message;
385
+ saveJob(newJob);
386
+ }
387
+ }
388
+
389
+ // --- Form Handlers ---
390
+ document.getElementById('voice-clone-form').addEventListener('submit', async () => {
391
+ if(!currentUser.email) {
392
+ document.getElementById('email-modal').classList.add('visible');
393
+ return;
394
+ }
395
+
396
+ const text = document.getElementById('text-input-clone').value;
397
+ const file = document.getElementById('user-voice-input').files[0];
398
+
399
+ if(!text.trim() || !file) {
400
+ alert('لطفا متن و فایل صوتی را وارد کنید.');
401
+ return;
402
+ }
403
+
404
+ const token = turnstile.getResponse(widgetIdClone);
405
+ if(!token) { alert('کپچا را حل کنید'); return; }
406
+
407
+ // Start Process
408
+ const btn = document.getElementById('generate-btn-clone');
409
+ btn.disabled = true;
410
+ btn.querySelector('.btn-text').textContent = 'درخواست ارسال شد';
411
+
412
+ // Run Async
413
+ runCloneProcess(text, file, token).then(() => {
414
+ btn.disabled = false;
415
+ btn.querySelector('.btn-text').textContent = 'شروع پردازش';
416
+ turnstile.reset(widgetIdClone);
417
+ });
418
+ });
419
+
420
+ document.getElementById('clear-history').addEventListener('click', () => {
421
+ localStorage.removeItem('aisada_jobs_v2');
422
+ renderHistory();
423
+ });
424
+
425
+ // --- Init ---
426
+ renderHistory();
427
+ setTimeout(() => {
428
+ if(window.turnstile) {
429
+ widgetIdClone = turnstile.render('#cf-container-clone', { sitekey: '0x4AAAAAACJYw8vz3QHa-WFi' });
430
+ widgetIdLogin = turnstile.render('#cf-container-login', { sitekey: '0x4AAAAAACJYw8vz3QHa-WFi' });
431
+ }
432
+ }, 1000);
433
+
434
+ // Login Handlers
435
+ document.getElementById('login-check-btn').addEventListener('click', () => document.getElementById('email-modal').classList.add('visible'));
436
+ document.getElementById('logout-btn').addEventListener('click', () => {
437
+ localStorage.removeItem('userEmail');
438
+ location.reload();
439
+ });
440
+
441
+ // Auth Forms (Simplified for snippet)
442
+ document.getElementById('email-form').addEventListener('submit', async (e) => {
443
+ e.preventDefault();
444
+ const email = document.getElementById('login-email-input').value;
445
+ const token = turnstile.getResponse(widgetIdLogin);
446
+ if(!token) return alert('کپچا؟');
447
+
448
+ // Simulate Send Code
449
+ await fetch('/tts/send_code.php', {
450
+ method: 'POST',
451
+ body: JSON.stringify({email, turnstile_token: token})
452
+ });
453
+ document.getElementById('email-form').style.display='none';
454
+ document.getElementById('code-form').style.display='block';
455
+ });
456
+
457
+ document.getElementById('code-form').addEventListener('submit', async (e) => {
458
+ e.preventDefault();
459
+ const email = document.getElementById('login-email-input').value;
460
+ const code = document.getElementById('code-input').value;
461
+
462
+ const res = await fetch('/tts/verify_code.php', {
463
+ method: 'POST',
464
+ body: JSON.stringify({email, code})
465
+ });
466
+ const d = await res.json();
467
+ if(d.status === 'success') {
468
+ localStorage.setItem('userEmail', email);
469
+ currentUser.email = email;
470
+ currentUser.status = d.status_type || 'free';
471
+ updateAuthUI();
472
+ document.getElementById('email-modal').classList.remove('visible');
473
+ } else {
474
+ alert('کد اشتباه است');
475
+ }
476
+ });
477
+
478
+ </script>
479
+
480
+ </body>
481
+ </html>