Opera8 commited on
Commit
32502ca
·
verified ·
1 Parent(s): b9a787f

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +324 -268
index.html CHANGED
@@ -1,293 +1,349 @@
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>دستیار ساخت آهنگ هوشمند</title>
7
- <link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;700&display=swap" rel="stylesheet">
8
- <style>
9
- :root {
10
- --bg-color: #0f172a;
11
- --card-bg: #1e293b;
12
- --primary: #6366f1;
13
- --primary-hover: #4f46e5;
14
- --text-main: #f8fafc;
15
- --text-muted: #94a3b8;
16
- --border: #334155;
17
- }
18
-
19
- body {
20
- font-family: 'Vazirmatn', sans-serif;
21
- background-color: var(--bg-color);
22
- color: var(--text-main);
23
- margin: 0;
24
- padding: 20px;
25
- display: flex;
26
- justify-content: center;
27
- min-height: 100vh;
28
- }
29
-
30
- .container {
31
- width: 100%;
32
- max-width: 900px;
33
- background: var(--card-bg);
34
- border-radius: 16px;
35
- padding: 30px;
36
- box-shadow: 0 10px 30px rgba(0,0,0,0.3);
37
- border: 1px solid var(--border);
38
- }
39
-
40
- h1 {
41
- text-align: center;
42
- color: var(--primary);
43
- margin-bottom: 10px;
44
- }
45
-
46
- p.subtitle {
47
- text-align: center;
48
- color: var(--text-muted);
49
- margin-bottom: 30px;
50
- }
51
-
52
- .input-group {
53
- margin-bottom: 25px;
54
- }
55
-
56
- label {
57
- display: block;
58
- margin-bottom: 8px;
59
- font-weight: bold;
60
- color: var(--text-main);
61
- }
62
-
63
- textarea {
64
- width: 100%;
65
- padding: 15px;
66
- border-radius: 12px;
67
- border: 2px solid var(--border);
68
- background: #0f172a;
69
- color: white;
70
- font-family: 'Vazirmatn', sans-serif;
71
- font-size: 16px;
72
- resize: vertical;
73
- min-height: 100px;
74
- box-sizing: border-box;
75
- transition: border-color 0.3s;
76
- }
77
-
78
- textarea:focus {
79
- outline: none;
80
- border-color: var(--primary);
81
- }
82
-
83
- button#generateBtn {
84
- width: 100%;
85
- padding: 15px;
86
- background: var(--primary);
87
- color: white;
88
- border: none;
89
- border-radius: 12px;
90
- font-size: 18px;
91
- font-weight: bold;
92
- cursor: pointer;
93
- transition: background 0.3s, transform 0.1s;
94
- display: flex;
95
- align-items: center;
96
- justify-content: center;
97
- gap: 10px;
98
- }
99
-
100
- button#generateBtn:hover {
101
- background: var(--primary-hover);
102
- }
103
-
104
- button#generateBtn:active {
105
- transform: scale(0.98);
106
- }
107
-
108
- button:disabled {
109
- background: var(--border);
110
- cursor: not-allowed;
111
- }
112
-
113
- .results-area {
114
- display: grid;
115
- grid-template-columns: 1fr 1fr;
116
- gap: 20px;
117
- margin-top: 30px;
118
- opacity: 0;
119
- transform: translateY(20px);
120
- transition: all 0.5s ease;
121
- }
122
-
123
- .results-area.visible {
124
- opacity: 1;
125
- transform: translateY(0);
126
- }
127
-
128
- .result-box {
129
- background: #0f172a;
130
- border: 1px solid var(--border);
131
- border-radius: 12px;
132
- padding: 15px;
133
- position: relative;
134
- }
135
-
136
- .result-header {
137
- display: flex;
138
- justify-content: space-between;
139
- align-items: center;
140
- margin-bottom: 10px;
141
- border-bottom: 1px solid var(--border);
142
- padding-bottom: 10px;
143
- }
144
-
145
- .result-title {
146
- font-weight: bold;
147
- color: var(--primary);
148
- }
149
 
150
- .copy-btn {
151
- background: transparent;
152
- border: 1px solid var(--border);
153
- color: var(--text-muted);
154
- padding: 5px 10px;
155
- border-radius: 6px;
156
- cursor: pointer;
157
- font-size: 12px;
158
- transition: all 0.2s;
159
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
- .copy-btn:hover {
162
- background: var(--border);
163
- color: white;
164
- }
 
 
165
 
166
- .output-text {
167
- white-space: pre-wrap;
168
- line-height: 1.8;
169
- font-size: 14px;
170
- color: #e2e8f0;
171
- max-height: 300px;
172
- overflow-y: auto;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  }
174
 
175
- /* لودر */
176
- .loader {
177
- border: 4px solid rgba(255, 255, 255, 0.1);
178
- border-left-color: white;
179
- border-radius: 50%;
180
- width: 24px;
181
- height: 24px;
182
- animation: spin 1s linear infinite;
183
- display: none;
 
 
 
 
 
 
184
  }
185
 
186
- @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
187
-
188
- @media (max-width: 768px) {
189
- .results-area {
190
- grid-template-columns: 1fr;
 
 
 
191
  }
 
192
  }
193
- </style>
194
- </head>
195
- <body>
196
-
197
- <div class="container">
198
- <h1>🎵 استودیو هوشمند ترانه و آهنگ</h1>
199
- <p class="subtitle">ایده خود را بنویسید تا متن شعر (با اعراب دقیق) و پرامپت موسیقی را دریافت کنید.</p>
200
 
201
- <div class="input-group">
202
- <label for="userInput">توضیحات آهنگ شما:</label>
203
- <textarea id="userInput" placeholder="مثال: یک آهنگ سنتی غمگین در مورد پاییز و باران با صدای زن..."></textarea>
204
- </div>
205
-
206
- <button id="generateBtn" onclick="generateContent()">
207
- <span class="btn-text">ساخت ترانه و پرامپت</span>
208
- <div class="loader"></div>
209
- </button>
210
-
211
- <div class="results-area" id="resultsArea">
212
- <!-- باکس متن شعر -->
213
- <div class="result-box">
214
- <div class="result-header">
215
- <span class="result-title">📜 متن شعر (با اعراب)</span>
216
- <button class="copy-btn" onclick="copyToClipboard('lyricsOutput')">کپی</button>
217
- </div>
218
- <div class="output-text" id="lyricsOutput"></div>
219
- </div>
220
-
221
- <!-- باکس پرامپت موزیک -->
222
- <div class="result-box">
223
- <div class="result-header">
224
- <span class="result-title">🎹 پرامپت موسیقی (انگلیسی)</span>
225
- <button class="copy-btn" onclick="copyToClipboard('promptOutput')">کپی</button>
226
- </div>
227
- <div class="output-text" id="promptOutput" style="direction: ltr; text-align: left;"></div>
228
- </div>
229
- </div>
230
- </div>
231
-
232
- <script>
233
- async function generateContent() {
234
- const input = document.getElementById('userInput').value;
235
- const btn = document.getElementById('generateBtn');
236
- const loader = document.querySelector('.loader');
237
- const btnText = document.querySelector('.btn-text');
238
- const resultsArea = document.getElementById('resultsArea');
239
-
240
- if (!input.trim()) {
241
- alert("لطفا توضیحات آهنگ را وارد کنید.");
242
  return;
243
  }
244
 
245
- // UI Loading State
246
- btn.disabled = true;
247
- loader.style.display = 'block';
248
- btnText.style.display = 'none';
249
- resultsArea.classList.remove('visible');
 
250
 
251
  try {
252
- const response = await fetch('/api/generate', {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  method: 'POST',
254
- headers: {
255
- 'Content-Type': 'application/json'
256
- },
257
- body: JSON.stringify({ description: input })
258
  });
259
 
260
- if (!response.ok) {
261
- throw new Error('Network response was not ok');
262
- }
263
-
264
- const data = await response.json();
265
 
266
- // Display Results
267
- document.getElementById('lyricsOutput').innerText = data.lyrics;
268
- document.getElementById('promptOutput').innerText = data.music_prompt;
269
 
270
- resultsArea.classList.add('visible');
271
-
272
- } catch (error) {
273
- console.error('Error:', error);
274
- alert("خطایی رخ داد. لطفا کلید API را چک کنید یا دوباره تلاش کنید.");
275
- } finally {
276
- // Reset UI
277
- btn.disabled = false;
 
 
 
 
 
 
 
 
 
 
278
  loader.style.display = 'none';
279
- btnText.style.display = 'block';
 
280
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
  }
282
-
283
- function copyToClipboard(elementId) {
284
- const text = document.getElementById(elementId).innerText;
285
- navigator.clipboard.writeText(text).then(() => {
286
- alert("کپی شد!");
287
- }).catch(err => {
288
- console.error('خطا در کپی:', err);
289
- });
290
- }
291
  </script>
292
- </body>
293
  </html>
 
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>استودیو هوشمند ACE-Step</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@400;500;600;700;800&display=swap" rel="stylesheet">
8
+ <style>
9
+ :root {
10
+ --app-font: 'Vazirmatn', sans-serif;
11
+ --app-bg: #F8F9FC;
12
+ --panel-bg: #FFFFFF;
13
+ --panel-border: #EAEFF7;
14
+ --input-bg: #F6F8FB;
15
+ --input-border: #E1E7EF;
16
+ --text-primary: #1A202C;
17
+ --text-secondary: #626F86;
18
+ --text-tertiary: #8A94A6;
19
+ --accent-primary: #4A6CFA;
20
+ --accent-secondary: #0FD4A8;
21
+ --success-color: #38A169;
22
+ --danger-color: #e53e3e;
23
+ --radius-card: 24px;
24
+ --radius-btn: 14px;
25
+ --radius-input: 12px;
26
+ --transition-smooth: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
27
+ --accent-primary-glow: rgba(74, 108, 250, 0.25);
28
+ }
29
+
30
+ @keyframes fadeIn { from { opacity: 0; transform: translateY(15px); } to { opacity: 1; transform: translateY(0); } }
31
+ @keyframes pulse-loader { 0% { box-shadow: 0 0 40px rgba(56, 189, 248, 0.3); } 50% { box-shadow: 0 0 60px rgba(56, 189, 248, 0.7); } 100% { box-shadow: 0 0 40px rgba(56, 189, 248, 0.3); } }
32
+
33
+ body { font-family: var(--app-font); background-color: var(--app-bg); color: var(--text-primary); margin: 0; padding: 2.5rem 1rem; display: flex; justify-content: center; align-items: flex-start; min-height: 100vh; }
34
+ .container { max-width: 820px; width: 100%; }
35
+
36
+ header { position: relative; text-align: center; margin-bottom: 2.5rem; padding: 2rem 0; animation: fadeIn 0.8s 0.1s ease-out backwards; overflow: hidden; }
37
+ #music-canvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; opacity: 0.6; }
38
+ .header-content { position: relative; z-index: 2; }
39
+
40
+ .ai-synapse-container { width: 100px; height: 100px; margin: 0 auto 1.5rem; position: relative; display: flex; align-items: center; justify-content: center; background: radial-gradient(circle, rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0)); border-radius: 50%; box-shadow: 0 0 30px var(--accent-primary-glow); }
41
+ .music-icon { width: 60px; height: 60px; color: var(--accent-primary); filter: drop-shadow(0 0 5px var(--accent-primary)); }
42
+
43
+ h1 { font-size: 2.8rem; font-weight: 800; margin: 0; background: linear-gradient(90deg, var(--accent-primary), var(--accent-secondary)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; letter-spacing: -1px; }
44
+ .subtitle { font-size: 1.1rem; color: var(--text-secondary); margin-top: 0.5rem; }
45
+
46
+ main { padding: 3rem; background-color: var(--panel-bg); border-radius: var(--radius-card); box-shadow: 0 20px 25px -5px rgba(26, 32, 44, 0.07); border: 1px solid var(--panel-border); animation: fadeIn 0.8s 0.3s ease-out backwards; }
47
+
48
+ .form-group { margin-bottom: 2.5rem; }
49
+ .form-label { display: flex; align-items: center; gap: 0.75rem; font-weight: 700; color: var(--text-primary); font-size: 1.2em; margin-bottom: 1.2rem; }
50
+ .form-label svg { width: 24px; height: 24px; color: var(--accent-primary); }
51
+
52
+ .text-input-container { background-color: var(--input-bg); border: 2px solid var(--input-border); border-radius: var(--radius-input); padding: 1rem; transition: var(--transition-smooth); }
53
+ .text-input-container:focus-within { border-color: var(--accent-primary); background-color: #fff; box-shadow: 0 0 15px var(--accent-primary-glow); }
54
+
55
+ textarea, input, select { width: 100%; border: none; background: transparent; font-family: var(--app-font); font-size: 1rem; color: var(--text-primary); outline: none; resize: vertical; }
56
+ textarea { min-height: 100px; line-height: 1.6; }
57
+
58
+ .btn-magic { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border: none; padding: 12px 24px; border-radius: var(--radius-btn); cursor: pointer; font-family: var(--app-font); font-weight: 600; display: flex; align-items: center; gap: 8px; width: 100%; justify-content: center; margin-top: 10px; transition: transform 0.2s; }
59
+ .btn-magic:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(118, 75, 162, 0.3); }
60
+ .btn-magic:disabled { opacity: 0.7; cursor: wait; }
61
+
62
+ #generateButton { display: flex; align-items: center; justify-content: center; gap: 0.75rem; width: 100%; padding: 1.1rem; font-size: 1.2rem; font-weight: 700; background: linear-gradient(95deg, var(--accent-secondary) 0%, var(--accent-primary) 100%); color: #fff; border: none; border-radius: var(--radius-btn); cursor: pointer; transition: all 0.3s ease; box-shadow: 0 6px 12px -3px var(--accent-primary-glow); margin-top: 2.5rem; }
63
+ #generateButton:hover:not(:disabled) { transform: translateY(-5px) scale(1.02); }
64
+ #generateButton:disabled { background: var(--text-tertiary); cursor: not-allowed; }
65
+
66
+ /* Result Area */
67
+ #result-container { margin-top: 2rem; display: none; flex-direction: column; align-items: center; }
68
+ #result-container.active { display: flex; animation: fadeIn 0.5s; }
69
+
70
+ .status-message { width: 100%; padding: 0.8rem 1rem; margin-bottom: 0.5rem; border-radius: var(--radius-input); font-size: 0.95rem; background: var(--input-bg); border: 1px solid var(--input-border); color: var(--text-secondary); text-align: center; }
71
+
72
+ /* Loader */
73
+ #aiLoader { width: 100%; display: none; justify-content: center; margin-bottom: 2rem; }
74
+ .generator-container { position: relative; width: 100%; max-width: 400px; height: 120px; border: 2px solid #38bdf8; border-radius: 20px; overflow: hidden; box-shadow: 0 0 40px rgba(56, 189, 248, 0.3); animation: pulse-loader 2s infinite ease-in-out; background-color: #161b22; color: #f0f6fc; display: flex; align-items: center; justify-content: center; }
75
+ .waveform-visualizer { display: flex; align-items: center; gap: 4px; height: 60px; }
76
+ .bar { width: 6px; background: var(--accent-secondary); border-radius: 3px; animation: waveform-bounce 1s infinite ease-in-out; }
77
+ .bar:nth-child(odd) { background: var(--accent-primary); animation-duration: 0.8s; }
78
+ .bar:nth-child(2) { animation-delay: 0.1s; } .bar:nth-child(3) { animation-delay: 0.2s; } .bar:nth-child(4) { animation-delay: 0.3s; }
79
+ @keyframes waveform-bounce { 0%, 100% { height: 10px; } 50% { height: 50px; } }
80
+
81
+ .audio-card { width: 100%; background: #fff; border: 1px solid var(--input-border); border-radius: var(--radius-input); padding: 1.5rem; box-shadow: 0 4px 6px rgba(0,0,0,0.05); margin-top: 1rem; border-right: 5px solid var(--success-color); }
82
+ audio { width: 100%; margin-top: 10px; border-radius: 30px; }
83
+
84
+ /* Grid for settings */
85
+ .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
86
+ details { background: var(--input-bg); border-radius: var(--radius-input); padding: 0.5rem; margin-bottom: 2rem; }
87
+ summary { cursor: pointer; padding: 0.5rem; font-weight: 600; color: var(--text-secondary); }
88
+ </style>
89
+ </head>
90
+ <body>
91
+ <div class="container">
92
+ <header>
93
+ <canvas id="music-canvas"></canvas>
94
+ <div class="header-content">
95
+ <div class="ai-synapse-container">
96
+ <svg class="music-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
97
+ <path d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"/>
98
+ </svg>
99
+ </div>
100
+ <h1>استودیو هوشمند ACE-Step</h1>
101
+ <p class="subtitle">تبدیل ایده به موسیقی با هوش مصنوعی جمینای</p>
102
+ </div>
103
+ </header>
104
+ <main>
105
+ <!-- Step 1: Idea Input -->
106
+ <div class="form-group">
107
+ <div class="form-label">
108
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
109
+ ۱. ایده اولیه آهنگ
110
+ </div>
111
+ <div class="text-input-container">
112
+ <textarea id="ideaInput" placeholder="مثال: یک آهنگ شاد تولد برای پسرم آرش به سبک پاپ ایرانی..."></textarea>
113
+ </div>
114
+ <button id="refineBtn" class="btn-magic">
115
+ <svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.384-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z" /></svg>
116
+ پردازش متن توسط هوش مصنوعی
117
+ </button>
118
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
 
120
+ <!-- Step 2: Generated Prompt & Lyrics -->
121
+ <div class="form-group">
122
+ <div class="form-label">
123
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 19V6l12-3v13M9 19c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zm12-3c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zM9 10l12-3" /></svg>
124
+ ۲. پرامپت و شعر (تولید شده)
125
+ </div>
126
+ <div class="grid-2">
127
+ <div class="text-input-container">
128
+ <label style="font-size:0.8rem; color:var(--text-tertiary);">توصیف انگلیسی آهنگ</label>
129
+ <textarea id="captions" rows="4"></textarea>
130
+ </div>
131
+ <div class="text-input-container">
132
+ <label style="font-size:0.8rem; color:var(--text-tertiary);">متن شعر (با اعراب)</label>
133
+ <textarea id="lyrics" rows="4" dir="rtl"></textarea>
134
+ </div>
135
+ </div>
136
+ </div>
137
+
138
+ <!-- Hidden Configs -->
139
+ <details>
140
+ <summary>تنظیمات پیشرفته (مدل و زمان)</summary>
141
+ <div class="grid-2" style="margin-top:10px;">
142
+ <select id="config_path" class="text-input-container">
143
+ <option value="acestep-v15-turbo" selected>Turbo Model</option>
144
+ <option value="acestep-v15-turbo-shift3">Shift3 Model</option>
145
+ </select>
146
+ <input type="number" id="audio_duration" value="30" class="text-input-container" placeholder="زمان (ثانیه)">
147
+ <input type="number" id="inference_steps" value="8" class="text-input-container" placeholder="Steps">
148
+ <input type="text" id="seed" value="-1" class="text-input-container" placeholder="Seed">
149
+ </div>
150
+ </details>
151
+
152
+ <!-- Step 3: Generate Audio -->
153
+ <button id="generateButton">
154
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>
155
+ <span>ساخت نهایی آهنگ</span>
156
+ </button>
157
+
158
+ <div id="result-container">
159
+ <div id="statusMessages"></div>
160
+ <div id="aiLoader">
161
+ <div class="generator-container">
162
+ <div class="waveform-visualizer">
163
+ <div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div><div class="bar"></div>
164
+ </div>
165
+ <div style="margin-right:15px; font-weight:bold;">در حال آهنگسازی...</div>
166
+ </div>
167
+ </div>
168
+ <div id="outputArea" style="width: 100%;"></div>
169
+ </div>
170
+ </main>
171
+ </div>
172
+
173
+ <script>
174
+ (function() {
175
+ // --- 1. Logic for Gemini (Refine Text) ---
176
+ const refineBtn = document.getElementById('refineBtn');
177
+ const ideaInput = document.getElementById('ideaInput');
178
+ const captionsBox = document.getElementById('captions');
179
+ const lyricsBox = document.getElementById('lyrics');
180
+
181
+ refineBtn.addEventListener('click', async () => {
182
+ const idea = ideaInput.value;
183
+ if(!idea) { alert('لطفا ایده خود را بنویسید'); return; }
184
+
185
+ refineBtn.disabled = true;
186
+ refineBtn.innerHTML = 'در حال تفکر...';
187
+
188
+ try {
189
+ const res = await fetch('/api/refine', {
190
+ method: 'POST',
191
+ headers: {'Content-Type': 'application/json'},
192
+ body: JSON.stringify({ idea: idea })
193
+ });
194
+ const data = await res.json();
195
+
196
+ if(data.error) throw new Error(data.error);
197
 
198
+ captionsBox.value = data.music_prompt;
199
+ lyricsBox.value = data.lyrics;
200
+
201
+ // Animation effect
202
+ captionsBox.style.backgroundColor = '#e6fffa';
203
+ setTimeout(() => captionsBox.style.backgroundColor = 'transparent', 1000);
204
 
205
+ } catch(e) {
206
+ alert('خطا در ارتباط با هوش مصنوعی: ' + e.message);
207
+ } finally {
208
+ refineBtn.disabled = false;
209
+ refineBtn.innerHTML = `
210
+ <svg width="20" height="20" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19.428 15.428a2 2 0 00-1.022-.547l-2.384-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z" /></svg>
211
+ پردازش متن توسط هوش مصنوعی`;
212
+ }
213
+ });
214
+
215
+ // --- 2. Logic for Music Generation (ACE-Step) ---
216
+ const SPACE_URL = "https://ace-step-ace-step-v1-5.hf.space/";
217
+ const GENERATE_FN_INDEX = 77;
218
+ let processedUrls = new Set();
219
+
220
+ const generateBtn = document.getElementById('generateButton');
221
+ const statusDiv = document.getElementById('statusMessages');
222
+ const loader = document.getElementById('aiLoader');
223
+ const outputArea = document.getElementById('outputArea');
224
+ const resultContainer = document.getElementById('result-container');
225
+
226
+ const val = (id) => document.getElementById(id).value;
227
+ const num = (id) => Number(document.getElementById(id).value);
228
+
229
+ function setStatus(msg) {
230
+ resultContainer.classList.add('active');
231
+ statusDiv.innerHTML = `<div class="status-message">${msg}</div>`;
232
  }
233
 
234
+ function addAudio(url) {
235
+ if (processedUrls.has(url)) return;
236
+ processedUrls.add(url);
237
+ const fullUrl = url.startsWith('http') ? url : SPACE_URL.replace(/\/$/, '') + url;
238
+
239
+ const card = document.createElement('div');
240
+ card.className = 'audio-card';
241
+ card.innerHTML = `
242
+ <div style="display:flex; justify-content:space-between; align-items:center;">
243
+ <strong>🎵 آهنگ نهایی شما</strong>
244
+ <a href="${fullUrl}" target="_blank" style="color:#4A6CFA;text-decoration:none;">دانلود</a>
245
+ </div>
246
+ <audio controls src="${fullUrl}"></audio>
247
+ `;
248
+ outputArea.prepend(card);
249
  }
250
 
251
+ function scanForAudios(data) {
252
+ function traverse(obj) {
253
+ if (typeof obj === 'string' && obj.includes('/file=') && obj.endsWith('.mp3')) {
254
+ addAudio(obj);
255
+ } else if (obj && typeof obj === 'object') {
256
+ if (obj.url && obj.url.endsWith('.mp3')) addAudio(obj.url);
257
+ Object.values(obj).forEach(traverse);
258
+ }
259
  }
260
+ traverse(data);
261
  }
 
 
 
 
 
 
 
262
 
263
+ generateBtn.addEventListener('click', async () => {
264
+ if(!val('captions') || !val('lyrics')) {
265
+ alert('لطفا ابتدا متن و پرامپت را بسازید یا وارد کنید.');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
  return;
267
  }
268
 
269
+ generateBtn.disabled = true;
270
+ generateBtn.innerHTML = 'در حال ارسال به سرور...';
271
+ loader.style.display = 'flex';
272
+ statusDiv.innerHTML = '';
273
+ outputArea.innerHTML = '';
274
+ processedUrls.clear();
275
 
276
  try {
277
+ // Payload structure for ACE-Step
278
+ const payload = [
279
+ val('config_path'), "custom", null, "unknown",
280
+ val('captions'), val('lyrics'),
281
+ 0, "", "", "unknown", num('inference_steps'), 7, true,
282
+ val('seed'), null, -1, 1, null, null, 0, -1,
283
+ "Fill the audio semantic mask based on the given conditions:",
284
+ 1, "text2music", false, 0, 1, 3, "ode", "", "mp3", 0.85,
285
+ true, 2, 0, 0.9, "NO USER INPUT", true, true, true, null, false, true, false, false, 0.5, 8, null, [], false, null, null, null, null
286
+ ];
287
+
288
+ const session_hash = Math.random().toString(36).substring(2);
289
+ setStatus('در حال اتصال به صف پردازش...');
290
+
291
+ const joinResp = await fetch(`${SPACE_URL}gradio_api/queue/join`, {
292
  method: 'POST',
293
+ headers: { 'Content-Type': 'application/json' },
294
+ body: JSON.stringify({ data: payload, fn_index: GENERATE_FN_INDEX, session_hash })
 
 
295
  });
296
 
297
+ if (!joinResp.ok) throw new Error('خطا در اتصال به صف');
 
 
 
 
298
 
299
+ const eventSource = new EventSource(`${SPACE_URL}gradio_api/queue/data?session_hash=${session_hash}`);
 
 
300
 
301
+ eventSource.onmessage = (event) => {
302
+ const msg = JSON.parse(event.data);
303
+ if (msg.msg === 'estimation') setStatus(`در صف انتظار: نفر ${msg.rank + 1}`);
304
+ else if (msg.msg === 'process_starts') setStatus('موتور هوش مصنوعی شروع به کار کرد...');
305
+ else if (msg.msg === 'process_generating' || msg.msg === 'process_completed') {
306
+ scanForAudios(msg.output);
307
+ if (msg.msg === 'process_completed') {
308
+ eventSource.close();
309
+ loader.style.display = 'none';
310
+ generateBtn.disabled = false;
311
+ generateBtn.innerHTML = 'ساخت مجدد';
312
+ setStatus('پایان پردازش.');
313
+ }
314
+ }
315
+ };
316
+
317
+ } catch (e) {
318
+ setStatus('خطا: ' + e.message);
319
  loader.style.display = 'none';
320
+ generateBtn.disabled = false;
321
+ generateBtn.innerHTML = 'تلاش مجدد';
322
  }
323
+ });
324
+
325
+ // Canvas Animation
326
+ const canvas = document.getElementById('music-canvas');
327
+ if(canvas) {
328
+ const ctx = canvas.getContext('2d');
329
+ let time = 0;
330
+ function resize() { canvas.width = canvas.parentElement.clientWidth; canvas.height = canvas.parentElement.clientHeight; }
331
+ window.addEventListener('resize', resize); resize();
332
+ function animate() {
333
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
334
+ ctx.beginPath();
335
+ for (let x = 0; x < canvas.width; x++) {
336
+ const y = canvas.height / 2 + Math.sin(x * 0.01 + time) * 30;
337
+ ctx.lineTo(x, y);
338
+ }
339
+ ctx.strokeStyle = 'rgba(74, 108, 250, 0.2)';
340
+ ctx.stroke();
341
+ time += 0.05;
342
+ requestAnimationFrame(animate);
343
+ }
344
+ animate();
345
  }
346
+ })();
 
 
 
 
 
 
 
 
347
  </script>
348
+ </body>
349
  </html>