refactor: Remove Bunny Stream completely from UI and unify Teacher Studio to streamlined Cloudflare R2 & FFmpeg HLS uploader

This commit is contained in:
Hamza-Ayed
2026-08-28 02:40:17 +03:00
parent 7ddaff6b14
commit 0ce5616e22
+29 -156
View File
@@ -446,24 +446,24 @@ class TeacherPortal
</div>
</div>
<!-- TAB 2: Zero-Touch Autonomous Video & AI Studio -->
<!-- TAB 2: Zero-Touch Autonomous Video & Cloudflare R2 Studio -->
<div id="tab_courses_content" class="studio-card" style="display: none;">
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 20px; flex-wrap: wrap; gap: 12px;">
<div>
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px;">
<span style="font-size: 11px; font-weight: 800; background: rgba(56,189,248,0.12); color: var(--accent-cyan); padding: 3px 10px; border-radius: 6px;">⚡ Cloudflare R2 + HLS Adaptive</span>
<span style="font-size: 11px; font-weight: 800; background: rgba(0,245,212,0.12); color: #00F5D4; padding: 3px 10px; border-radius: 6px;">🧠 Gemini Socratic Engine</span>
</div>
<h3 style="font-size: 18px; font-weight: 900;">استوديو رفع الحصص والتحليل الجنائي للذكاء الاصطناعي 🎬</h3>
<p style="font-size: 12px; color: var(--text-muted); margin-top: 4px;">ارفع الفيديو واترك الباقي لمحرك صَقِل: تقطيع HLS، مطابقة المنهاج، وتوليد الأسئلة السقراطية آلياً.</p>
</div>
<div style="display: flex; gap: 10px;">
<button type="button" onclick="switchVideoUploadMode('api')" id="btn_mode_api" class="btn-primary" style="width: auto; padding: 8px 18px; font-size: 12px; background: linear-gradient(135deg, #0284C7, #0369A1);">1. الرفع المباشر عبر السيرفر ⚡</button>
<button type="button" onclick="switchVideoUploadMode('bunny')" id="btn_mode_bunny" class="btn-primary" style="width: auto; padding: 8px 18px; font-size: 12px; background: rgba(255,255,255,0.06); border: 1px solid var(--border);">2. ربط Bunny Stream CDN 🐰</button>
<p style="font-size: 12px; color: var(--text-muted); margin-top: 4px;">ارفع الفيديو بضغطة زر واحدة: يتولى السيرفر التقطيع إلى بث HLS، والتخزين على Cloudflare R2، وتوليد الفحص السقراطي آلياً.</p>
</div>
</div>
<!-- UPLOADER BOX 1: Direct API Upload -->
<div id="uploader_box_api" style="background: rgba(11, 19, 43, 0.85); border: 1px solid rgba(56, 189, 248, 0.3); border-radius: 18px; padding: 24px; margin-bottom: 24px; box-shadow: 0 10px 30px rgba(0,0,0,0.4);">
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px;">
<h4 style="font-size: 15px; font-weight: 800; color: var(--accent-cyan);">⚡ رفع مباشر ومعالجة ذكية فورية (HLS + Gemini Socratic Engine)</h4>
<span style="font-size: 11px; background: rgba(56,189,248,0.1); color: var(--accent-cyan); padding: 3px 10px; border-radius: 6px; font-weight: 700;">معالجة آلية 100% 🧠</span>
<!-- UNIFIED UPLOADER BOX -->
<div style="background: rgba(11, 19, 43, 0.85); border: 1px solid rgba(56, 189, 248, 0.3); border-radius: 18px; padding: 26px; margin-bottom: 24px; box-shadow: 0 10px 30px rgba(0,0,0,0.4);">
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; flex-wrap: wrap; gap: 8px;">
<h4 style="font-size: 15px; font-weight: 800; color: var(--accent-cyan);">⚡ رفع وحفظ الحصة ومعالجة البث الذكي</h4>
<span style="font-size: 11px; background: rgba(56,189,248,0.15); color: var(--accent-cyan); padding: 4px 12px; border-radius: 980px; font-weight: 700;">معالجة تلقائية بالكامل 100% 🧠</span>
</div>
<form id="form_direct_upload" onsubmit="handleDirectVideoUpload(event)">
@@ -486,10 +486,10 @@ class TeacherPortal
<div class="form-group" style="margin-top: 12px;">
<label class="form-label">ملف الفيديو (MP4, WebM, MOV - حتى 2GB)</label>
<div style="border: 2px dashed rgba(56, 189, 248, 0.4); border-radius: 14px; padding: 28px; text-align: center; background: rgba(0,0,0,0.2); cursor: pointer;" onclick="document.getElementById('direct_video_file').click()">
<div style="font-size: 32px; margin-bottom: 8px;">🎬</div>
<div style="border: 2px dashed rgba(56, 189, 248, 0.4); border-radius: 14px; padding: 32px 20px; text-align: center; background: rgba(0,0,0,0.25); cursor: pointer; transition: all 0.2s ease;" onclick="document.getElementById('direct_video_file').click()" onmouseover="this.style.borderColor='var(--accent-cyan)'" onmouseout="this.style.borderColor='rgba(56, 189, 248, 0.4)'">
<div style="font-size: 36px; margin-bottom: 8px;">🎬</div>
<div style="font-size: 14px; font-weight: 700; color: #FFFFFF;" id="file_selected_label">اضغط لاختيار ملف الفيديو أو اسحبه وأفلته هنا</div>
<div style="font-size: 11.5px; color: var(--text-muted); margin-top: 6px;">يتولى السيرفر التقطيع إلى HLS والتحليل الجنائي وتوليد نقاط الفحص السقراطي فوراً في الخلفية</div>
<div style="font-size: 11.5px; color: var(--text-muted); margin-top: 6px;">يتولى السيرفر التقطيع إلى HLS، المزامنة مع Cloudflare R2، وتوليد نقاط الفحص السقراطي فوراً في الخلفية</div>
<input type="file" id="direct_video_file" accept="video/mp4,video/webm,video/quicktime,video/x-matroska" style="display: none;" onchange="onVideoFileSelected(this)">
</div>
</div>
@@ -505,52 +505,17 @@ class TeacherPortal
</div>
</div>
<button type="submit" id="btn_submit_direct_upload" class="btn-primary" style="margin-top: 18px; width: auto; padding: 12px 36px; font-size: 13.5px; font-weight: 800;">رفع وحفظ الحصة والتحليل الذكي الآن 🚀</button>
<button type="submit" id="btn_submit_direct_upload" class="btn-primary" style="margin-top: 18px; width: auto; padding: 12px 40px; font-size: 14px; font-weight: 800;">رفع وبث الحصة وتوليد الفحص السقراطي آلياً 🚀</button>
</form>
</div>
<!-- UPLOADER BOX 2: Bunny Stream CDN -->
<div id="uploader_box_bunny" style="display: none; background: rgba(11, 19, 43, 0.85); border: 1px solid rgba(245, 158, 11, 0.3); border-radius: 18px; padding: 24px; margin-bottom: 24px; box-shadow: 0 10px 30px rgba(0,0,0,0.4);">
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px;">
<h4 style="font-size: 15px; font-weight: 800; color: var(--accent-gold);">🐰 الربط مع شبكة Bunny.net Stream (Cloud CDN & Token DRM)</h4>
<span style="font-size: 11px; background: rgba(245,158,11,0.1); color: var(--accent-gold); padding: 3px 10px; border-radius: 6px; font-weight: 700;">HLS + SHA256 DRM</span>
</div>
<form id="form_bunny_link" onsubmit="handleBunnyVideoLink(event)">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 16px;">
<div class="form-group">
<label class="form-label">الدورة التدريبية</label>
<select id="bunny_course_select" class="input-text" required>
<option value="1">الرياضيات العلمي — توجيهي 2008 (المستوى الثالث)</option>
</select>
</div>
<div class="form-group">
<label class="form-label">عنوان الحصة / الدرس</label>
<input type="text" id="bunny_lesson_title" placeholder="مثال: الدرس 2 — مشتقة ضرب اقترانين" class="input-text" required>
</div>
<div class="form-group">
<label class="form-label">معرف فيديو Bunny Stream (Video GUID)</label>
<div style="display: flex; gap: 8px;">
<input type="text" id="bunny_video_id_input" placeholder="e.g. 78a9c12b-34ef-..." class="input-text" style="font-family: monospace;" required>
<button type="button" onclick="handleBunnyCreateVideo()" class="btn-primary" style="width: auto; padding: 0 14px; font-size: 11px; background: #D97706; white-space: nowrap;">+ توليد GUID</button>
</div>
</div>
<div class="form-group">
<label class="form-label">مدة الفيديو المقدرة (بالثواني)</label>
<input type="number" id="bunny_duration_input" value="1800" class="input-text">
</div>
</div>
<button type="submit" id="btn_submit_bunny_link" class="btn-primary" style="margin-top: 18px; width: auto; padding: 12px 36px; font-size: 13.5px; font-weight: 800; background: linear-gradient(135deg, #F59E0B, #D97706);">حفظ وربط درس Bunny Stream ✓</button>
</form>
</div>
<!-- AUTONOMOUS AI BANNER -->
<!-- AUTONOMOUS AI & R2 PIPELINE BANNER -->
<div style="background: rgba(0, 245, 212, 0.05); border: 1px solid rgba(0, 245, 212, 0.2); border-radius: 18px; padding: 20px; display: flex; align-items: center; gap: 16px;">
<div style="font-size: 32px;">🧠</div>
<div>
<h4 style="font-size: 14px; font-weight: 800; color: var(--accent-cyan); margin-bottom: 2px;">المعالجة السقراطية الذكية تعمل تلقائياً 100%</h4>
<h4 style="font-size: 14px; font-weight: 800; color: var(--accent-cyan); margin-bottom: 2px;">المعالجة السقراطية والتخزين السحابي يعملان تلقائياً 100%</h4>
<p style="font-size: 12px; color: var(--text-muted); line-height: 1.6; margin: 0;">
لا داعي لأي جهد يدوي من المعلم. فور رفعك للملف، يطابق النظام نصوص المنهاج الوزاري، ويولد الفهرس الزمني للأفكار ونقاط الفحص السقراطي اللحظي، وتظهر فوراً للطالب في المشغل التفاعلي.
لا يوجد أي جهد إضافي على المعلم. فور انتهاء الرفع، يُجزأ الفيديو إلى مقاطع HLS مشفرة، وتُحفظ على شبكة Cloudflare R2 فائقة السرعة، ويولد الذكاء الاصطناعي الفهرس الزمني وأسئلة التثبيت الصدمي لتظهر فوراً للطالب.
</p>
</div>
</div>
@@ -1248,29 +1213,6 @@ class TeacherPortal
// ==========================================
// Video Storage & Bunny Stream Studio Handlers
// ==========================================
function switchVideoUploadMode(mode) {
const apiBox = document.getElementById('uploader_box_api');
const bunnyBox = document.getElementById('uploader_box_bunny');
const btnApi = document.getElementById('btn_mode_api');
const btnBunny = document.getElementById('btn_mode_bunny');
if (mode === 'api') {
apiBox.style.display = 'block';
bunnyBox.style.display = 'none';
btnApi.style.background = 'linear-gradient(135deg, #0284C7, #0369A1)';
btnApi.style.border = 'none';
btnBunny.style.background = 'rgba(255,255,255,0.06)';
btnBunny.style.border = '1px solid var(--border)';
} else {
apiBox.style.display = 'none';
bunnyBox.style.display = 'block';
btnBunny.style.background = 'linear-gradient(135deg, #F59E0B, #D97706)';
btnBunny.style.border = 'none';
btnApi.style.background = 'rgba(255,255,255,0.06)';
btnApi.style.border = '1px solid var(--border)';
}
}
function onVideoFileSelected(input) {
if (input.files && input.files[0]) {
const f = input.files[0];
@@ -1312,7 +1254,7 @@ class TeacherPortal
progressContainer.style.display = 'block';
submitBtn.disabled = true;
submitBtn.textContent = 'جارٍ رفع الفيديو... ⏳';
submitBtn.textContent = 'جارٍ رفع الفيديو والمعالجة الذكية... ⏳';
const xhr = new XMLHttpRequest();
xhr.open('POST', '/api/teacher/videos/upload-direct', true);
@@ -1331,12 +1273,12 @@ class TeacherPortal
xhr.onload = function() {
submitBtn.disabled = false;
submitBtn.textContent = 'بدء رفع وحفظ الفيديو الآن 🚀';
submitBtn.textContent = 'رفع وبث الحصة وتوليد الفحص السقراطي آلياً 🚀';
try {
const data = JSON.parse(xhr.responseText);
if (xhr.status >= 200 && xhr.status < 300 && data.status === 'success') {
showLuxuryToast('تم حفظ الفيديو بنجاح 🎬', data.message || 'تم إنشاء الدرس وحفظ الفيديو');
alert('✅ ' + data.message + '\nرابط البث المباشر: ' + data.data.stream_url);
showLuxuryToast('تم حفظ ومعالجة الفيديو بنجاح 🎬', data.message || 'تم إنشاء الدرس وبث HLS');
alert('✅ ' + data.message + '\n\nتم تقطيع الفيديو لـ HLS وحفظه وتوليد نقاط الفحص السقراطي آلياً!');
document.getElementById('form_direct_upload').reset();
document.getElementById('file_selected_label').textContent = 'اضغط لاختيار ملف الفيديو أو اسحبه وأفلته هنا';
progressContainer.style.display = 'none';
@@ -1351,77 +1293,13 @@ class TeacherPortal
xhr.onerror = function() {
submitBtn.disabled = false;
submitBtn.textContent = 'بدء رفع وحفظ الفيديو الآن 🚀';
submitBtn.textContent = 'رفع وبث الحصة وتوليد الفحص السقراطي آلياً 🚀';
alert('❌ حدث خطأ في الاتصال أثناء رفع الفيديو');
};
xhr.send(formData);
}
async function handleBunnyCreateVideo() {
const token = getAuthToken();
const title = document.getElementById('bunny_lesson_title').value.trim() || 'درس جديد في الرياضيات';
const courseId = document.getElementById('bunny_course_select').value;
try {
const res = await fetch('/api/teacher/videos/bunny-create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
},
body: JSON.stringify({ title: title, course_id: courseId })
});
const data = await res.json();
if (res.ok && data.status === 'success') {
document.getElementById('bunny_video_id_input').value = data.data.video_id;
showLuxuryToast('تم توليد المعرف في Bunny Stream 🐰', `GUID: ${data.data.video_id}`);
} else {
alert('خطأ: ' + (data.message || 'فشل الاتصال بـ Bunny Stream'));
}
} catch (err) {
console.error(err);
alert('خطأ في الاتصال بالخادم');
}
}
async function handleBunnyVideoLink(e) {
e.preventDefault();
const token = getAuthToken();
const courseId = document.getElementById('bunny_course_select').value;
const title = document.getElementById('bunny_lesson_title').value.trim();
const bunnyVideoId = document.getElementById('bunny_video_id_input').value.trim();
const duration = document.getElementById('bunny_duration_input').value;
try {
const res = await fetch('/api/teacher/videos/bunny-link', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
},
body: JSON.stringify({
course_id: courseId,
title: title,
bunny_video_id: bunnyVideoId,
duration_seconds: duration
})
});
const data = await res.json();
if (res.ok && data.status === 'success') {
showLuxuryToast('تم ربط الدرس بنجاح 🐰', data.message);
alert('✅ تم ربط الحصة بـ Bunny Stream وحمايتها بروابط التشفير الرقمي DRM!');
document.getElementById('form_bunny_link').reset();
loadTeacherCourses();
} else {
alert('خطأ: ' + (data.message || 'فشل حفظ الدرس'));
}
} catch (err) {
console.error(err);
alert('خطأ في الاتصال بالخادم');
}
}
async function loadTeacherCourses() {
const token = getAuthToken();
if (!token) return;
@@ -1431,17 +1309,12 @@ class TeacherPortal
});
const data = await res.json();
if (res.ok && data.data && data.data.length > 0) {
const selects = [
document.getElementById('direct_upload_course_select'),
document.getElementById('bunny_course_select')
];
selects.forEach(sel => {
if (sel) {
sel.innerHTML = data.data.map(c => `
<option value="${c.id}">${c.title} (${c.semester || 'فصل أول'})</option>
`).join('');
}
});
const sel = document.getElementById('direct_upload_course_select');
if (sel) {
sel.innerHTML = data.data.map(c => `
<option value="${c.id}">${c.title} (${c.semester || 'فصل أول'})</option>
`).join('');
}
}
} catch (e) {
console.error('Load courses note:', e);