Opera8 commited on
Commit
4ca9510
·
verified ·
1 Parent(s): 89d021e

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +10 -78
index.html CHANGED
@@ -66,7 +66,6 @@
66
  }
67
  .subtitle { font-size: 0.9rem; color: var(--text-secondary); margin-top: 5px; margin-bottom: 10px; }
68
 
69
- /* استایل بج وضعیت اشتراک */
70
  .status-badge {
71
  display: inline-block; padding: 6px 16px; border-radius: 20px;
72
  font-size: 0.85rem; font-weight: 700; margin-top: 5px;
@@ -102,7 +101,6 @@
102
  textarea { min-height: 120px; resize: vertical; }
103
  textarea:focus, input:focus, select:focus { border-color: var(--accent-primary); background: #fff; }
104
 
105
- /* دکمه اصلی */
106
  @keyframes move-gradient {
107
  0% { background-position: 0% 50%; }
108
  50% { background-position: 100% 50%; }
@@ -134,7 +132,6 @@
134
  }
135
  .btn-outline:hover { border-color: var(--accent-primary); color: var(--accent-primary); }
136
 
137
- /* تنظیمات پیشرفته */
138
  .accordion {
139
  background-color: var(--input-bg); color: var(--text-primary); cursor: pointer; padding: 15px; width: 100%; border: none;
140
  text-align: right; outline: none; font-size: 0.95rem; font-weight: 600; border-radius: 12px;
@@ -153,10 +150,8 @@
153
  .checkbox-wrapper { display: flex; align-items: center; gap: 10px; margin-top: 5px; padding-bottom: 15px; border-top: 1px solid #e2e8f0; padding-top: 15px; }
154
  .settings-group label { font-size: 0.8rem; color: var(--text-secondary); display: block; margin-bottom: 6px; font-weight: 500; }
155
 
156
- /* پلیر و متن */
157
  .player-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px solid var(--panel-border); }
158
 
159
- /* استایل دکمه دانلود */
160
  .download-btn-style {
161
  background-color: rgba(74, 108, 250, 0.1); color: var(--accent-primary); text-decoration: none; font-size: 0.9rem;
162
  font-weight: 700; padding: 8px 16px; border-radius: 10px; transition: 0.2s; display: inline-flex; align-items: center; gap: 5px; cursor: pointer; border: none;
@@ -173,7 +168,6 @@
173
  }
174
  .lyrics-tag { color: var(--accent-primary); font-weight: 800; display: block; margin-top: 20px; margin-bottom: 5px; font-size: 0.9em; letter-spacing: 1px; text-transform: uppercase; }
175
 
176
- /* لودر */
177
  #loader { display: none; text-align: center; padding: 20px; }
178
  .wave-bars { display: flex; justify-content: center; gap: 4px; height: 30px; align-items: flex-end; }
179
  .bar { width: 5px; background: var(--accent-primary); animation: jump 1s infinite; border-radius: 2px; }
@@ -182,7 +176,6 @@
182
  .bar:nth-child(4) { animation-delay: 0.3s; height: 50%; }
183
  @keyframes jump { 0%, 100% { height: 20%; } 50% { height: 100%; } }
184
 
185
- /* --- استایل‌های تاریخچه و حذف --- */
186
  #historySection { margin-top: 30px; width: 100%; }
187
  .history-title { font-size: 1.2rem; font-weight: 800; color: var(--text-primary); margin-bottom: 15px; display: flex; align-items: center; gap: 10px; }
188
  .history-list { display: grid; gap: 12px; }
@@ -207,7 +200,6 @@
207
  .history-card:hover .h-delete { opacity: 1; transform: scale(1); }
208
  .h-delete:hover { background: var(--danger-color); color: white; transform: scale(1.1); }
209
 
210
- /* مودال‌ها */
211
  .modal-overlay {
212
  position: fixed; top: 0; left: 0; width: 100%; height: 100%;
213
  background: rgba(0, 0, 0, 0.6); backdrop-filter: blur(8px);
@@ -233,7 +225,6 @@
233
  .btn-confirm { background: var(--danger-color); color: white; box-shadow: 0 5px 15px rgba(229, 62, 62, 0.3); }
234
  .btn-confirm:hover { transform: translateY(-2px); box-shadow: 0 8px 20px rgba(229, 62, 62, 0.4); }
235
 
236
- /* مودال ارتقا */
237
  @keyframes pulse-gold { 0% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.7); } 70% { box-shadow: 0 0 0 15px rgba(255, 193, 7, 0); } 100% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0); } }
238
  .upgrade-icon {
239
  width: 70px; height: 70px;
@@ -358,15 +349,13 @@
358
 
359
  <script>
360
  const ACE_SPACE_URL = "https://ace-step-ace-step-v1-5.hf.space/";
361
- // مشخصات نسخه پولی
362
  const PREMIUM_PAGE_ID = '1149636';
363
  const PREMIUM_URL = '#/nav/online/news/getSingle/1149636/eyJpdiI6InZSVUdlLzBlR0FzOHZJdXFZeWhER0E9PSIsInZhbHVlIjoiWFhqRXBLc29vSFpHdk9nYmRjZGVuWHRHRHVSZHRlTG1BUENLaE5mNXBNVVRGWFg3ZWN0djJ5K1dIY1RqTHJGaCIsIm1hYyI6IjIzYzFlZTMwYmVmMTdkYjQ0YTQ4YWMxNmFhN2RmNWQ2OTU0MjI0ZWVlZGI3ZjJjMjhkNmQxNjM4MDFlZTIxNmUiLCJ0YWciOiIifQ==/20934991';
364
 
365
  let db;
366
  let songToDeleteId = null;
367
- let userSubscriptionStatus = 'free'; // پیش‌فرض رایگان
368
 
369
- // المان‌ها
370
  const ideaInput = document.getElementById('ideaInput');
371
  const processBtn = document.getElementById('processBtn');
372
  const step1 = document.getElementById('step1');
@@ -397,18 +386,15 @@
397
  });
398
  }
399
 
400
- // --- مدیریت اثر انگشت (Fingerprint) ---
401
  function getFingerprint() {
402
  let fp = localStorage.getItem('user_fingerprint');
403
  if (!fp) {
404
- // تولید یک شناسه تصادفی و ذخیره آن
405
  fp = 'user_' + Math.random().toString(36).substr(2, 9) + Date.now().toString(36);
406
  localStorage.setItem('user_fingerprint', fp);
407
  }
408
  return fp;
409
  }
410
 
411
- // --- مدیریت اشتراک و ارتباط با آیفریم مادر ---
412
  function isUserPaid(userObject) {
413
  return userObject && userObject.isLogin && userObject.accessible_pages &&
414
  (userObject.accessible_pages.includes(PREMIUM_PAGE_ID) ||
@@ -426,7 +412,6 @@
426
  }
427
  }
428
 
429
- // شنونده پیام از آیفریم مادر
430
  window.addEventListener('message', (event) => {
431
  if (event.data && event.data.type === 'USER_STATUS_RESPONSE') {
432
  try {
@@ -439,26 +424,21 @@
439
  }
440
  });
441
 
442
- // درخواست وضعیت کاربر در شروع برنامه
443
  parent.postMessage({ type: 'REQUEST_USER_STATUS' }, '*');
444
 
445
- // مدیریت کلیک دکمه دانلود (تابع امنیتی)
446
  function handleSecureDownload(url, e) {
447
  if (e) e.preventDefault();
448
 
449
  if (userSubscriptionStatus === 'free') {
450
- // تنظیم متن مودال برای دانلود
451
  upgradeModalTitle.innerText = "دانلود مخصوص اعضای ویژه";
452
  upgradeModalText.innerText = "برای دانلود آهنگ‌های ساخته شده با کیفیت اصلی و بدون محدودیت، لطفا حساب کاربری خود را به نسخه نامحدود ارتقا دهید.";
453
  upgradeModal.classList.add('open');
454
  } else {
455
- // ارسال درخواست دانلود به آیفریم مادر
456
  parent.postMessage({
457
  type: 'INITIATE_DOWNLOAD_FROM_URL',
458
  payload: { audioUrl: url }
459
  }, '*');
460
 
461
- // تغییر موقت متن دکمه برای فیدبک
462
  const btn = e ? e.target.closest('button') : null;
463
  if(btn) {
464
  const originalText = btn.innerHTML;
@@ -468,7 +448,6 @@
468
  }
469
  }
470
 
471
- // دکمه ارتقا در مودال
472
  document.getElementById('btnGoPremium').addEventListener('click', () => {
473
  parent.postMessage({
474
  type: 'NAVIGATE_TO_PREMIUM',
@@ -480,7 +459,6 @@
480
  upgradeModal.classList.remove('open');
481
  }
482
 
483
- // --- مدیریت دیتابیس ---
484
  function initDB() {
485
  const request = indexedDB.open("alphaMusicDB", 1);
486
  request.onerror = (event) => console.error("IndexedDB error:", event);
@@ -490,7 +468,6 @@
490
 
491
  async function saveToHistory(idea, lyrics, audioUrl) {
492
  try {
493
- // برای ذخیره در DB باید فایل را فچ کنیم
494
  const response = await fetch(audioUrl);
495
  const audioBlob = await response.blob();
496
  const newItem = {
@@ -501,7 +478,6 @@
501
  const store = transaction.objectStore("songs");
502
  store.add(newItem);
503
 
504
- // شرط نگهداری فقط ۱۰ آیتم آخر
505
  const countReq = store.count();
506
  countReq.onsuccess = () => {
507
  if (countReq.result > 10) store.openCursor().onsuccess = (e) => { if(e.target.result) e.target.result.delete(); };
@@ -536,9 +512,7 @@
536
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18"></path><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path></svg>
537
  </div>
538
  `;
539
- // کلیک روی خود کارت برای پخش
540
  div.onclick = (e) => {
541
- // اگر روی دکمه حذف کلیک نشده باشد
542
  if (!e.target.closest('.h-delete')) {
543
  openHistoryItem(item);
544
  }
@@ -548,9 +522,8 @@
548
  };
549
  }
550
 
551
- // --- توابع حذف ---
552
  function askToDelete(event, id) {
553
- event.stopPropagation(); // جلوگیری از باز شدن پلیر
554
  songToDeleteId = id;
555
  deleteModal.classList.add('open');
556
  }
@@ -572,7 +545,6 @@
572
  }
573
  }
574
 
575
- // بستن مودال با کلیک بیرون
576
  deleteModal.addEventListener('click', (e) => {
577
  if (e.target === deleteModal) closeDeleteModal();
578
  });
@@ -589,7 +561,6 @@
589
  headerText.innerHTML = `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg> آهنگ آرشیو شده`;
590
  headerText.style.color = 'var(--accent-primary)';
591
 
592
- // تنظیم دکمه دانلود
593
  mainDownloadBtn.style.display = 'inline-flex';
594
  mainDownloadBtn.onclick = (e) => handleSecureDownload(audioURL, e);
595
 
@@ -602,33 +573,12 @@
602
 
603
  initDB();
604
 
605
- // --- توابع تبدیل فرمت صوتی (Client-Side WAV Converter) ---
606
  function writeString(view, offset, string) {
607
  for (let i = 0; i < string.length; i++) {
608
  view.setUint8(offset + i, string.charCodeAt(i));
609
  }
610
  }
611
 
612
- function floatTo16BitPCM(output, offset, input) {
613
- for (let i = 0; i < input.length; i++, offset += 2) {
614
- let s = Math.max(-1, Math.min(1, input[i]));
615
- output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
616
- }
617
- }
618
-
619
- function interleave(inputL, inputR) {
620
- const length = inputL.length + inputR.length;
621
- const result = new Float32Array(length);
622
- let index = 0;
623
- let inputIndex = 0;
624
- while (index < length) {
625
- result[index++] = inputL[inputIndex];
626
- result[index++] = inputR[inputIndex];
627
- inputIndex++;
628
- }
629
- return result;
630
- }
631
-
632
  function bufferToWave(abuffer, len) {
633
  let numOfChan = abuffer.numberOfChannels;
634
  let length = len * numOfChan * 2 + 44;
@@ -638,7 +588,6 @@
638
  let offset = 0;
639
  let pos = 0;
640
 
641
- // write WAVE header
642
  writeString(view, 0, 'RIFF');
643
  view.setUint32(4, 36 + len * numOfChan * 2, true);
644
  writeString(view, 8, 'WAVE');
@@ -653,12 +602,10 @@
653
  writeString(view, 36, 'data');
654
  view.setUint32(40, len * numOfChan * 2, true);
655
 
656
- // write interleaved data
657
  for(i = 0; i < abuffer.numberOfChannels; i++)
658
  channels.push(abuffer.getChannelData(i));
659
 
660
  offset = 44;
661
- // Interleave logic handled simply here for 1 or 2 channels
662
  if (numOfChan === 2) {
663
  while(pos < len) {
664
  for(i = 0; i < numOfChan; i++) {
@@ -688,7 +635,6 @@
688
  const audioContext = new (window.AudioContext || window.webkitAudioContext)();
689
  audioContext.decodeAudioData(e.target.result, function(buffer) {
690
  const wavBlob = bufferToWave(buffer, buffer.length);
691
- // ساخت فایل جدید با فرمت wav
692
  const wavFile = new File([wavBlob], file.name.replace(/\.[^/.]+$/, "") + ".wav", { type: "audio/wav" });
693
  resolve(wavFile);
694
  }, function(e){
@@ -701,7 +647,6 @@
701
  });
702
  }
703
 
704
- // --- تابع آپلود فایل صوتی به Gradio ---
705
  async function uploadAudioFile(file) {
706
  const formData = new FormData();
707
  formData.append("files", file);
@@ -728,7 +673,6 @@
728
  }
729
  }
730
 
731
- // --- فرآیند اصلی ---
732
  processBtn.addEventListener('click', async () => {
733
  if (!ideaInput.value.trim()) return alert("لطفا موضوع آهنگ را بنویسید");
734
 
@@ -738,35 +682,22 @@
738
  loader.style.display = 'block';
739
 
740
  try {
741
- // 1. آپلود و تبدیل فایل نمونه
742
  const audioInput = document.getElementById('audio_reference');
743
  let uploadedAudioObj = null;
744
 
745
  if (audioInput.files.length > 0) {
746
  let fileToUpload = audioInput.files[0];
747
-
748
- // بررسی و تبدیل به WAV
749
  if (fileToUpload.type !== 'audio/wav' && !fileToUpload.name.toLowerCase().endsWith('.wav')) {
750
  loaderText.innerText = "در حال تبدیل فرمت فایل صوتی به WAV...";
751
- try {
752
- fileToUpload = await convertAudioToWav(fileToUpload);
753
- console.log("Converted to WAV:", fileToUpload.name, fileToUpload.size);
754
- } catch (err) {
755
- console.error(err);
756
- alert("خطا در تبدیل فایل صوتی. لطفاً فایل WAV معتبر آپلود کنید.");
757
- throw new Error("Audio conversion failed");
758
- }
759
  }
760
-
761
  loaderText.innerText = "در حال آپلود فایل نمونه...";
762
  uploadedAudioObj = await uploadAudioFile(fileToUpload);
763
  }
764
 
765
- // 2. درخواست متن و پرامپت
766
  loaderText.innerText = "آلفا در حال نوشتن شعر و ملودی...";
767
  const isInstrumental = getChk('instrumental_chk');
768
 
769
- // ارسال درخواست به سرور با فینگرپرینت و وضعیت اشتراک
770
  const response = await fetch('/api/refine', {
771
  method: 'POST',
772
  headers: {'Content-Type': 'application/json'},
@@ -778,7 +709,6 @@
778
  })
779
  });
780
 
781
- // بررسی خطای محدودیت روزانه (429)
782
  if (response.status === 429) {
783
  loader.style.display = 'none';
784
  step1.style.display = 'block';
@@ -786,7 +716,7 @@
786
  processBtn.disabled = false;
787
 
788
  upgradeModalTitle.innerText = "محدودیت ساخت روزانه";
789
- upgradeModalText.innerText = "شما به سقف ۵ آهنگ رایگان در روز رسیده‌اید. برای ساخت آهنگ‌های بیشتر و نامحدود، لطفا حساب خود را ارتقا دهید.";
790
  upgradeModal.classList.add('open');
791
  return;
792
  }
@@ -799,7 +729,10 @@
799
 
800
  const finalLyrics = isInstrumental ? "" : lyrics;
801
 
802
- // ایندکس 3: مدت زمان آهنگ
 
 
 
803
  const payload = [
804
  getVal('model_select'),
805
  "custom",
@@ -810,7 +743,7 @@
810
  0, "", "", "unknown",
811
  getNum('steps_input'), getNum('cfg_input'), true, getNum('seed_input'), null, -1,
812
  getNum('batch_size'), null, null, 0, -1, "Fill the audio semantic mask based on the given conditions:",
813
- 1, "text2music", false, 0, 1, 3, "ode", "", "mp3", 0.85,
814
  getChk('think_checkbox'), 2, 0, 0.9, "NO USER INPUT", true, true, true, null, false, true, false, false, 0.5, 8, null, [], false, null, null, null, null
815
  ];
816
 
@@ -852,7 +785,6 @@
852
  if (processedUrls.size === 1) {
853
  mainDownloadBtn.style.display = 'inline-flex';
854
  mainDownloadBtn.onclick = (e) => handleSecureDownload(fullUrl, e);
855
-
856
  saveToHistory(idea, lyrics, fullUrl);
857
  }
858
  playerWrapper.innerHTML += `<div class="audio-item"><audio controls autoplay src="${fullUrl}"></audio></div>`;
@@ -881,7 +813,7 @@
881
  }
882
  }
883
 
884
- function formatLyrics(text) { return text.replace(/\[(.*?)\]/g, '<span class="lyrics-tag">[$1]</span>'); }
885
 
886
  const canvas = document.getElementById('music-canvas');
887
  const ctx = canvas.getContext('2d');
 
66
  }
67
  .subtitle { font-size: 0.9rem; color: var(--text-secondary); margin-top: 5px; margin-bottom: 10px; }
68
 
 
69
  .status-badge {
70
  display: inline-block; padding: 6px 16px; border-radius: 20px;
71
  font-size: 0.85rem; font-weight: 700; margin-top: 5px;
 
101
  textarea { min-height: 120px; resize: vertical; }
102
  textarea:focus, input:focus, select:focus { border-color: var(--accent-primary); background: #fff; }
103
 
 
104
  @keyframes move-gradient {
105
  0% { background-position: 0% 50%; }
106
  50% { background-position: 100% 50%; }
 
132
  }
133
  .btn-outline:hover { border-color: var(--accent-primary); color: var(--accent-primary); }
134
 
 
135
  .accordion {
136
  background-color: var(--input-bg); color: var(--text-primary); cursor: pointer; padding: 15px; width: 100%; border: none;
137
  text-align: right; outline: none; font-size: 0.95rem; font-weight: 600; border-radius: 12px;
 
150
  .checkbox-wrapper { display: flex; align-items: center; gap: 10px; margin-top: 5px; padding-bottom: 15px; border-top: 1px solid #e2e8f0; padding-top: 15px; }
151
  .settings-group label { font-size: 0.8rem; color: var(--text-secondary); display: block; margin-bottom: 6px; font-weight: 500; }
152
 
 
153
  .player-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 15px; padding-bottom: 15px; border-bottom: 1px solid var(--panel-border); }
154
 
 
155
  .download-btn-style {
156
  background-color: rgba(74, 108, 250, 0.1); color: var(--accent-primary); text-decoration: none; font-size: 0.9rem;
157
  font-weight: 700; padding: 8px 16px; border-radius: 10px; transition: 0.2s; display: inline-flex; align-items: center; gap: 5px; cursor: pointer; border: none;
 
168
  }
169
  .lyrics-tag { color: var(--accent-primary); font-weight: 800; display: block; margin-top: 20px; margin-bottom: 5px; font-size: 0.9em; letter-spacing: 1px; text-transform: uppercase; }
170
 
 
171
  #loader { display: none; text-align: center; padding: 20px; }
172
  .wave-bars { display: flex; justify-content: center; gap: 4px; height: 30px; align-items: flex-end; }
173
  .bar { width: 5px; background: var(--accent-primary); animation: jump 1s infinite; border-radius: 2px; }
 
176
  .bar:nth-child(4) { animation-delay: 0.3s; height: 50%; }
177
  @keyframes jump { 0%, 100% { height: 20%; } 50% { height: 100%; } }
178
 
 
179
  #historySection { margin-top: 30px; width: 100%; }
180
  .history-title { font-size: 1.2rem; font-weight: 800; color: var(--text-primary); margin-bottom: 15px; display: flex; align-items: center; gap: 10px; }
181
  .history-list { display: grid; gap: 12px; }
 
200
  .history-card:hover .h-delete { opacity: 1; transform: scale(1); }
201
  .h-delete:hover { background: var(--danger-color); color: white; transform: scale(1.1); }
202
 
 
203
  .modal-overlay {
204
  position: fixed; top: 0; left: 0; width: 100%; height: 100%;
205
  background: rgba(0, 0, 0, 0.6); backdrop-filter: blur(8px);
 
225
  .btn-confirm { background: var(--danger-color); color: white; box-shadow: 0 5px 15px rgba(229, 62, 62, 0.3); }
226
  .btn-confirm:hover { transform: translateY(-2px); box-shadow: 0 8px 20px rgba(229, 62, 62, 0.4); }
227
 
 
228
  @keyframes pulse-gold { 0% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.7); } 70% { box-shadow: 0 0 0 15px rgba(255, 193, 7, 0); } 100% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0); } }
229
  .upgrade-icon {
230
  width: 70px; height: 70px;
 
349
 
350
  <script>
351
  const ACE_SPACE_URL = "https://ace-step-ace-step-v1-5.hf.space/";
 
352
  const PREMIUM_PAGE_ID = '1149636';
353
  const PREMIUM_URL = '#/nav/online/news/getSingle/1149636/eyJpdiI6InZSVUdlLzBlR0FzOHZJdXFZeWhER0E9PSIsInZhbHVlIjoiWFhqRXBLc29vSFpHdk9nYmRjZGVuWHRHRHVSZHRlTG1BUENLaE5mNXBNVVRGWFg3ZWN0djJ5K1dIY1RqTHJGaCIsIm1hYyI6IjIzYzFlZTMwYmVmMTdkYjQ0YTQ4YWMxNmFhN2RmNWQ2OTU0MjI0ZWVlZGI3ZjJjMjhkNmQxNjM4MDFlZTIxNmUiLCJ0YWciOiIifQ==/20934991';
354
 
355
  let db;
356
  let songToDeleteId = null;
357
+ let userSubscriptionStatus = 'free';
358
 
 
359
  const ideaInput = document.getElementById('ideaInput');
360
  const processBtn = document.getElementById('processBtn');
361
  const step1 = document.getElementById('step1');
 
386
  });
387
  }
388
 
 
389
  function getFingerprint() {
390
  let fp = localStorage.getItem('user_fingerprint');
391
  if (!fp) {
 
392
  fp = 'user_' + Math.random().toString(36).substr(2, 9) + Date.now().toString(36);
393
  localStorage.setItem('user_fingerprint', fp);
394
  }
395
  return fp;
396
  }
397
 
 
398
  function isUserPaid(userObject) {
399
  return userObject && userObject.isLogin && userObject.accessible_pages &&
400
  (userObject.accessible_pages.includes(PREMIUM_PAGE_ID) ||
 
412
  }
413
  }
414
 
 
415
  window.addEventListener('message', (event) => {
416
  if (event.data && event.data.type === 'USER_STATUS_RESPONSE') {
417
  try {
 
424
  }
425
  });
426
 
 
427
  parent.postMessage({ type: 'REQUEST_USER_STATUS' }, '*');
428
 
 
429
  function handleSecureDownload(url, e) {
430
  if (e) e.preventDefault();
431
 
432
  if (userSubscriptionStatus === 'free') {
 
433
  upgradeModalTitle.innerText = "دانلود مخصوص اعضای ویژه";
434
  upgradeModalText.innerText = "برای دانلود آهنگ‌های ساخته شده با کیفیت اصلی و بدون محدودیت، لطفا حساب کاربری خود را به نسخه نامحدود ارتقا دهید.";
435
  upgradeModal.classList.add('open');
436
  } else {
 
437
  parent.postMessage({
438
  type: 'INITIATE_DOWNLOAD_FROM_URL',
439
  payload: { audioUrl: url }
440
  }, '*');
441
 
 
442
  const btn = e ? e.target.closest('button') : null;
443
  if(btn) {
444
  const originalText = btn.innerHTML;
 
448
  }
449
  }
450
 
 
451
  document.getElementById('btnGoPremium').addEventListener('click', () => {
452
  parent.postMessage({
453
  type: 'NAVIGATE_TO_PREMIUM',
 
459
  upgradeModal.classList.remove('open');
460
  }
461
 
 
462
  function initDB() {
463
  const request = indexedDB.open("alphaMusicDB", 1);
464
  request.onerror = (event) => console.error("IndexedDB error:", event);
 
468
 
469
  async function saveToHistory(idea, lyrics, audioUrl) {
470
  try {
 
471
  const response = await fetch(audioUrl);
472
  const audioBlob = await response.blob();
473
  const newItem = {
 
478
  const store = transaction.objectStore("songs");
479
  store.add(newItem);
480
 
 
481
  const countReq = store.count();
482
  countReq.onsuccess = () => {
483
  if (countReq.result > 10) store.openCursor().onsuccess = (e) => { if(e.target.result) e.target.result.delete(); };
 
512
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18"></path><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path></svg>
513
  </div>
514
  `;
 
515
  div.onclick = (e) => {
 
516
  if (!e.target.closest('.h-delete')) {
517
  openHistoryItem(item);
518
  }
 
522
  };
523
  }
524
 
 
525
  function askToDelete(event, id) {
526
+ event.stopPropagation();
527
  songToDeleteId = id;
528
  deleteModal.classList.add('open');
529
  }
 
545
  }
546
  }
547
 
 
548
  deleteModal.addEventListener('click', (e) => {
549
  if (e.target === deleteModal) closeDeleteModal();
550
  });
 
561
  headerText.innerHTML = `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg> آهنگ آرشیو شده`;
562
  headerText.style.color = 'var(--accent-primary)';
563
 
 
564
  mainDownloadBtn.style.display = 'inline-flex';
565
  mainDownloadBtn.onclick = (e) => handleSecureDownload(audioURL, e);
566
 
 
573
 
574
  initDB();
575
 
 
576
  function writeString(view, offset, string) {
577
  for (let i = 0; i < string.length; i++) {
578
  view.setUint8(offset + i, string.charCodeAt(i));
579
  }
580
  }
581
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
582
  function bufferToWave(abuffer, len) {
583
  let numOfChan = abuffer.numberOfChannels;
584
  let length = len * numOfChan * 2 + 44;
 
588
  let offset = 0;
589
  let pos = 0;
590
 
 
591
  writeString(view, 0, 'RIFF');
592
  view.setUint32(4, 36 + len * numOfChan * 2, true);
593
  writeString(view, 8, 'WAVE');
 
602
  writeString(view, 36, 'data');
603
  view.setUint32(40, len * numOfChan * 2, true);
604
 
 
605
  for(i = 0; i < abuffer.numberOfChannels; i++)
606
  channels.push(abuffer.getChannelData(i));
607
 
608
  offset = 44;
 
609
  if (numOfChan === 2) {
610
  while(pos < len) {
611
  for(i = 0; i < numOfChan; i++) {
 
635
  const audioContext = new (window.AudioContext || window.webkitAudioContext)();
636
  audioContext.decodeAudioData(e.target.result, function(buffer) {
637
  const wavBlob = bufferToWave(buffer, buffer.length);
 
638
  const wavFile = new File([wavBlob], file.name.replace(/\.[^/.]+$/, "") + ".wav", { type: "audio/wav" });
639
  resolve(wavFile);
640
  }, function(e){
 
647
  });
648
  }
649
 
 
650
  async function uploadAudioFile(file) {
651
  const formData = new FormData();
652
  formData.append("files", file);
 
673
  }
674
  }
675
 
 
676
  processBtn.addEventListener('click', async () => {
677
  if (!ideaInput.value.trim()) return alert("لطفا موضوع آهنگ را بنویسید");
678
 
 
682
  loader.style.display = 'block';
683
 
684
  try {
 
685
  const audioInput = document.getElementById('audio_reference');
686
  let uploadedAudioObj = null;
687
 
688
  if (audioInput.files.length > 0) {
689
  let fileToUpload = audioInput.files[0];
 
 
690
  if (fileToUpload.type !== 'audio/wav' && !fileToUpload.name.toLowerCase().endsWith('.wav')) {
691
  loaderText.innerText = "در حال تبدیل فرمت فایل صوتی به WAV...";
692
+ fileToUpload = await convertAudioToWav(fileToUpload);
 
 
 
 
 
 
 
693
  }
 
694
  loaderText.innerText = "در حال آپلود فایل نمونه...";
695
  uploadedAudioObj = await uploadAudioFile(fileToUpload);
696
  }
697
 
 
698
  loaderText.innerText = "آلفا در حال نوشتن شعر و ملودی...";
699
  const isInstrumental = getChk('instrumental_chk');
700
 
 
701
  const response = await fetch('/api/refine', {
702
  method: 'POST',
703
  headers: {'Content-Type': 'application/json'},
 
709
  })
710
  });
711
 
 
712
  if (response.status === 429) {
713
  loader.style.display = 'none';
714
  step1.style.display = 'block';
 
716
  processBtn.disabled = false;
717
 
718
  upgradeModalTitle.innerText = "محدودیت ساخت روزانه";
719
+ upgradeModalText.innerText = "شما به سقف ۵ آهنگ رایگان در روز رسیده‌اید.";
720
  upgradeModal.classList.add('open');
721
  return;
722
  }
 
729
 
730
  const finalLyrics = isInstrumental ? "" : lyrics;
731
 
732
+ // ** LOGIC CORRECTION: Determine Generation Mode **
733
+ // If audio is uploaded, we must switch the task from "text2music" to "prompt" (or equivalent for conditioning)
734
+ const taskType = uploadedAudioObj ? "prompt" : "text2music";
735
+
736
  const payload = [
737
  getVal('model_select'),
738
  "custom",
 
743
  0, "", "", "unknown",
744
  getNum('steps_input'), getNum('cfg_input'), true, getNum('seed_input'), null, -1,
745
  getNum('batch_size'), null, null, 0, -1, "Fill the audio semantic mask based on the given conditions:",
746
+ 1, taskType, false, 0, 1, 3, "ode", "", "mp3", 0.85,
747
  getChk('think_checkbox'), 2, 0, 0.9, "NO USER INPUT", true, true, true, null, false, true, false, false, 0.5, 8, null, [], false, null, null, null, null
748
  ];
749
 
 
785
  if (processedUrls.size === 1) {
786
  mainDownloadBtn.style.display = 'inline-flex';
787
  mainDownloadBtn.onclick = (e) => handleSecureDownload(fullUrl, e);
 
788
  saveToHistory(idea, lyrics, fullUrl);
789
  }
790
  playerWrapper.innerHTML += `<div class="audio-item"><audio controls autoplay src="${fullUrl}"></audio></div>`;
 
813
  }
814
  }
815
 
816
+ function formatLyrics(text) { return text ? text.replace(/\[(.*?)\]/g, '<span class="lyrics-tag">[$1]</span>') : "متن آهنگ: (بی‌کلام)"; }
817
 
818
  const canvas = document.getElementById('music-canvas');
819
  const ctx = canvas.getContext('2d');