<?php
/**
 * FLV → MP3 / MP4 converter — same visual theme as amor-daneshjouyi sites.
 *
 * The entire conversion happens inside the browser via ffmpeg.wasm; no file
 * is ever uploaded to the server. This PHP file is a simple static shell
 * so the page can be served with `php -S` for local testing.
 */
?><!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>تبدیل FLV به MP3/MP4 — امور دانشجویی</title>
<meta name="description" content="تبدیل فایل‌های FLV کلاس ضبط‌شده ویانا به MP3 و MP4 کاملاً سمت کاربر — امور دانشجویی @omor_shahed">
<link rel="stylesheet" href="assets/css/style.css?v=15">
<link rel="icon" type="image/svg+xml" href="assets/img/logo.svg">
<script>
  (function () {
    try {
      var saved = localStorage.getItem('theme');
      if (saved === 'light' || saved === 'dark') {
        document.documentElement.setAttribute('data-theme', saved);
      }
    } catch (e) { /* localStorage unavailable; ignore. */ }
  })();
</script>
</head>
<body>
<div class="container">

  <header class="site-header">
    <div class="brand">
      <div class="brand-logo" aria-label="لوگو امور دانشجویی" title="لوگوی امور دانشجویی">
        <img src="assets/img/logo.svg" alt="لوگو امور دانشجویی">
      </div>
      <div class="brand-text">
        <div class="title">تبدیل و پخش کلای های ضبط شده</div>
        <div class="subtitle">پخش کلاس ساده و بی دردسر — امور دانشجویی</div>
      </div>
    </div>
    <div class="header-actions">
      <button type="button" id="themeToggle" class="theme-toggle" aria-label="تغییر تم" title="تغییر تم">
        <svg class="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
          <circle cx="12" cy="12" r="4"/>
          <path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/>
        </svg>
        <svg class="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
          <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
        </svg>
      </button>
    </div>
  </header>

  <div id="engineBanner" class="engine-banner" aria-live="polite">
    <span class="engine-label">در حال آماده‌سازی موتور تبدیل…</span>
    <div class="engine-progress-wrap"><div id="engineProgress" class="engine-progress"></div></div>
    <span id="engineProgressText" class="engine-progress-text">۰٪</span>
  </div>

  <section class="converter" aria-label="تبدیل FLV">
    <h2>📤 انتخاب فایل‌های FLV کلاس</h2>
    <p class="lead">
      فولدری که فایل زیپ کلاس را در آن extract کرده‌اید یا تک‌تک فایل‌های <code>.flv</code>
      را انتخاب کنید. سایت به‌صورت خودکار فایل‌های صوتی (<code>cameraVoip_*.flv</code>) و
      ویدئویی اسکرین‌شیر (<code>screenshare_*.flv</code>) را شناسایی، به‌ترتیب مرتب و به
      یک <strong>MP3</strong> و یک <strong>MP4</strong> تبدیل می‌کند. هیچ فایلی به سرور ارسال نمی‌شود.
    </p>

    <div id="dropZone" class="drop-zone" tabindex="0" role="button" aria-label="انتخاب یا کشاندن فایل‌ها">
      <div class="dz-title">📁 فولدر یا فایل‌های <code>.flv</code> را اینجا بکشید</div>
      <div class="dz-hint">
        یا روی همین کادر کلیک کنید تا فایل‌ها را دستی انتخاب کنید
        <br>
        <span class="dz-or">انتخاب فولدر</span>
        <span class="dz-or">یا</span>
        <span class="dz-or">انتخاب چند فایل</span>
      </div>
    </div>

    <!-- Hidden pickers MUST live outside the drop-zone. If they are inside,
         a programmatic click on one bubbles up to the drop-zone's click
         handler and accidentally opens the other picker too. display:none
         still allows .click() to open the native dialog. -->
    <input type="file" id="filePicker" multiple accept=".flv,video/x-flv" style="display:none">
    <input type="file" id="folderPicker" webkitdirectory style="display:none">

    <div class="btn-row">
      <button type="button" id="btnPickFolder" class="btn">📂 انتخاب فولدر</button>
      <button type="button" id="btnPickFiles" class="btn secondary">📄 انتخاب چند فایل</button>
      <button type="button" id="btnClear" class="btn secondary">🗑️ پاک کردن لیست</button>
      <button type="button" id="btnConvert" class="btn" disabled>▶️ شروع تبدیل</button>
      <button type="button" id="btnCancel" class="btn danger" style="display:none">🛑 توقف عملیات</button>
    </div>

    <div id="summary" class="files-summary" style="display:none"></div>
    <div id="groups" class="groups"></div>

    <div id="statusBanner" class="status-banner" style="display:none"></div>
    <div class="progress-row">
      <div class="progress-wrap" aria-hidden="true"><div id="progressBar" class="progress-bar"></div></div>
      <div id="progressText" class="progress-text">0%</div>
    </div>
    <div id="log" class="log" aria-live="polite"></div>

    <div id="outputs" class="outputs"></div>
  </section>

  <!-- ======================================================== -->
  <!-- Chat extraction from ftchat*.xml. Hidden until xml files  -->
  <!-- are detected; otherwise sits below the converter section. -->
  <!-- ======================================================== -->
  <section class="converter chat-section" aria-label="چت کلاس">
    <h2>💬 چت‌های کلاس</h2>
    <p class="lead">
      وقتی فولدر کامل کلاس را در بالا انتخاب کنید (شامل فایل‌های <code>ftchat*.xml</code>)،
      پیام‌های چت کلاس به‌ترتیب زمان رخدادشان همین‌جا نمایش داده می‌شوند.
      پیام‌های بدون متن (مثل اعلام در حال تایپ) در صورت وجود با رنگ کم‌رنگ نشان داده می‌شوند.
    </p>
    <div id="chatStats" class="chat-stats"></div>
    <div id="chatEmpty" class="chat-empty">برای دیدن چت‌ها، فولدر کلاس را در کادر بالا انتخاب کنید.</div>
    <div id="chatList" class="chat-list" aria-live="polite"></div>
    <div class="btn-row">
      <button type="button" id="btnExportChat" class="btn" disabled>⬇️ دانلود چت (TXT)</button>
    </div>
  </section>

  <!-- ======================================================== -->
  <!-- Full-class video build. Reads indexstream.xml for total   -->
  <!-- duration and stream timing, encodes each cameraVoip and   -->
  <!-- screenshare segment, fills gaps with silence/logo, then   -->
  <!-- muxes a synced MP4. All client-side via ffmpeg.wasm.      -->
  <!-- ======================================================== -->
  <section class="converter full-build" aria-label="ساخت ویدیوی کامل کلاس">
    <h2>🎬 ساخت ویدیوی کامل کلاس (سینک شده)</h2>
    <p class="lead">
      با تحلیل فایل ها، تایم‌لاین کلاس بازسازی می‌شود.
      هر بخش <strong>اسکرین‌شیر</strong> و هر بخش <strong>صدای کلاس</strong> سر جای واقعی خود قرار می‌گیرد؛
      بازه‌های بدون اسکرین‌شیر با لوگوی امور دانشجویی پر می‌شود و بازه‌های بدون صدا سکوت می‌گیرند.
      خروجی یک فایل MP4 سینک‌شده است. پردازش کاملاً سمت مرورگر کاربر انجام می‌شود.
    </p>
    <div id="timelineSummary" class="timeline-summary"></div>
    <div class="btn-row">
      <button type="button" id="btnFullBuild" class="btn" disabled>🎬 شروع ساخت ویدیوی کامل</button>
      <button type="button" id="btnFullCancel" class="btn danger" style="display:none">🛑 توقف</button>
    </div>
    <div id="fullStatus" class="status-banner" style="display:none"></div>
    <div class="progress-row">
      <div class="progress-wrap" aria-hidden="true"><div id="fullProgressBar" class="progress-bar"></div></div>
      <div id="fullProgressText" class="progress-text">0%</div>
    </div>
    <div id="fullLog" class="log" aria-live="polite"></div>
    <div id="fullOutputs" class="outputs"></div>
  </section>

  <!-- ======================================================== -->
  <!-- PDF + ftcontent XML → synced video. For classes where the -->
  <!-- instructor uploaded a PDF into Adobe Connect's Share pod  -->
  <!-- and presented on it (no screenshare). We replay every     -->
  <!-- page-turn / scroll / pointer event from ftcontent*.xml    -->
  <!-- against the user-supplied PDF and render an MP4.          -->
  <!-- ======================================================== -->
  <section class="converter pdfvideo-section" aria-label="ساخت ویدیو از PDF + رویدادها">
    <h2>🖼️ ساخت ویدیو از PDF + رویدادهای ضبط شده</h2>
    <p class="lead">
      اگر استاد به‌جای اشتراک صفحه، یک فایل <strong>PDF</strong> را داخل ادوبی کانکت بارگذاری کرده و
      روی آن ارائه داده باشد، این بخش با خواندن رویدادهای ضبط‌شده (<code>ftcontent*.xml</code>)
      اسکرول‌ها، تغییر صفحه‌ها و حرکت اشاره‌گر استاد را روی همان PDF بازسازی می‌کند و یک
      ویدیوی MP4 سینک‌شده می‌سازد. فقط فایل PDF را انتخاب کنید — XML رویدادها و فایل‌های
      صدا/اسکرین‌شیر به‌صورت خودکار از فولدر کلاسی که در بخش بالا انتخاب کرده‌اید گرفته می‌شوند.
      هیچ چیز به سرور ارسال نمی‌شود.
    </p>

    <div class="pv-grid pv-grid-single">
      <div class="pv-pickbox" id="pvPdfBox">
        <div class="pv-pickbox-title">📄 فایل‌های PDF ارائه استاد</div>
        <div class="pv-pickbox-help">اگر در طول کلاس چند PDF نمایش داده شده، همه را با هم انتخاب کنید.
          ترتیب نمایش پس از انتخاب در کادر زیر قابل تنظیم است.</div>
        <button type="button" id="btnPvPickPdf" class="btn">انتخاب PDF (یک یا چند فایل)</button>
        <div id="pvPdfInfo" class="pv-fileinfo">هنوز فایلی انتخاب نشده است.</div>
        <input type="file" id="pvPdfPicker" accept="application/pdf,.pdf" multiple style="display:none">
        <ul id="pvPdfList" class="pv-pdf-list" aria-label="ترتیب فایل‌های PDF"></ul>
        <div id="pvPdfMapHint" class="pv-fileinfo pv-pdf-map-hint" style="display:none"></div>
      </div>
    </div>

    <div id="pvFolderStatus" class="pv-folder-status">
      <strong>فولدر کلاس:</strong> هنوز انتخاب نشده است. از بخش بالای صفحه «📂 انتخاب فولدر» را بزنید
      تا فایل‌های <code>ftcontent*.xml</code>، <code>cameraVoip_*.flv</code> و
      <code>screenshare_*.flv</code> به‌صورت خودکار برای این بخش هم در دسترس باشند.
    </div>

    <div id="pvSummary" class="pv-summary" style="display:none"></div>

    <div class="btn-row">
      <button type="button" id="btnPvBuild" class="btn" disabled>🎬 ساخت ویدیو</button>
      <button type="button" id="btnPvCancel" class="btn danger" style="display:none">🛑 توقف</button>
    </div>

    <div id="pvStatus" class="status-banner" style="display:none"></div>
    <div class="progress-row">
      <div class="progress-wrap" aria-hidden="true"><div id="pvProgressBar" class="progress-bar"></div></div>
      <div id="pvProgressText" class="progress-text">0%</div>
    </div>
    <div id="pvLog" class="log" aria-live="polite"></div>

    <div id="pvOutputs" class="outputs"></div>
  </section>

  <section class="info-grid" aria-label="راهنما">
    <div class="info-card">
      <h3>🧩 منطق گروه‌بندی فایل‌ها</h3>
      <ul>
        <li>فایل‌های <code>cameraVoip_N_M.flv</code> → صوت کلاس → ادغام و تبدیل به <strong>MP3</strong>.</li>
        <li>فایل‌های <code>screenshare_N_M.flv</code> → ویدئو کلاس → ادغام و تبدیل به <strong>MP4</strong>.</li>
        <li>
          ترتیب اجرا بر اساس دو عدد انتهایی اسم فایل است: ابتدا مقدار <code>N</code> و در برابری
          بر اساس <code>M</code>.
        </li>
        <li>فایل‌های خراب یا نامعتبر نادیده گرفته می‌شوند و در لاگ گزارش می‌شوند.</li>
      </ul>
    </div>

    <div class="info-card">
      <h3>🎬 پخش داخل مرورگر</h3>
      <p>
        پس از پایان تبدیل، پلیر MP3 و MP4 در پایین همین صفحه ظاهر می‌شود. برای ذخیره
        فایل خروجی روی دکمه «دانلود» کلیک کنید. اگر فایل ویدیویی در مرورگر پخش نشد،
        بعد از دانلود می‌توانید با <a href="https://soft98.ir/multi-media/video-player/2438-potplayer.html" target="_blank" rel="noopener">PotPlayer</a>
        در ویندوز یا <a href="https://dl2.soft98.ir/mobile/MX.Player.1.93.4_arm64_Soft98.iR.apk" target="_blank" rel="noopener">MX Player</a>
        در اندروید پخش کنید.
      </p>
    </div>

    <div class="info-card">
      <h3>⚠️ نکات مهم</h3>
      <ul>
        <li>اولین اجرا حدود ۳۰ مگابایت فایل WASM لود می‌شود (فقط یک‌بار).</li>
        <li>پردازش ویدیو ممکن است چند دقیقه طول بکشد؛ مرورگر را نبندید.</li>
        <li>اگر مرورگر کرش کرد، فایل‌های ورودی را گروه‌گروه کوچک‌تر امتحان کنید.</li>
        <li>این ابزار روی مرورگرهای مدرن (Chrome, Edge, Firefox نسخه‌های جدید) کار می‌کند.</li>
      </ul>
    </div>
  </section>

  <footer class="footer">
    © <?= date('Y') ?> — تمامی حقوق برای <strong>امور دانشجویی</strong> محفوظ است. |
    <a href="https://rubika.ir/omor_shahed" target="_blank" rel="noopener">@omor_shahed</a>
  </footer>

</div>

<!-- FFmpeg.wasm UMD loader (single-threaded; no COOP/COEP required) -->
<script src="assets/vendor/ffmpeg/ffmpeg.js?v=1"></script>
<script src="assets/vendor/pdfjs/pdf.min.js?v=1"></script>
<script src="assets/js/app.js?v=14" defer></script>
<script src="assets/js/extras.js?v=5" defer></script>
<script src="assets/js/pdfvideo.js?v=3" defer></script>
</body>
</html>
