ghassan 66fd78c10f Add multi-language audio tracks and self-hosted flag-icons
Introduce per-video language support and multiple audio tracks
(VideoAudioTrack model + migrations for language, description, title),
a reusable language-select component, and a track-editor form. Bundle
the self-hosted flag-icons v7.2.3 library and a NAS auto-sync command.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 21:32:52 +03:00

90 lines
4.6 KiB
PHP

{{--
Tabbed description + insights box.
Expects: $video (Video model)
Optionally accepts: $descriptionSlot raw HTML to show in the About tab body
--}}
@php
$isVideoOwner = Auth::check() && Auth::id() === $video->user_id;
$hasDesc = !empty($video->description) || isset($descriptionSlot);
$showBox = $hasDesc || $isVideoOwner;
$fullDescription = $video->description ?? '';
$shortDescription = Str::limit($fullDescription, 200);
$needsExpand = strlen($fullDescription) > 200;
@endphp
@if ($showBox)
<style>
/* ── Description box ─────────────────────────────────── */
.vdb-wrap { background:var(--bg-secondary); border-radius:12px; margin-top:12px; overflow:hidden; border:1px solid var(--border-color); }
.vdb-tabs { display:flex; border-bottom:1px solid var(--border-color); padding:0 4px; }
.vdb-tab { background:none; border:none; color:var(--text-secondary); font-size:13px; font-weight:600; padding:0 16px; height:44px; cursor:pointer; position:relative; transition:color .15s; white-space:nowrap; display:flex; align-items:center; gap:6px; }
.vdb-tab:hover { color:var(--text-primary); }
.vdb-tab.active { color:var(--text-primary); }
.vdb-tab.active::after { content:''; position:absolute; bottom:0; left:0; right:0; height:2px; background:#ef4444; border-radius:2px 2px 0 0; }
.vdb-panel { display:none; padding:14px 16px 16px; }
.vdb-panel.active { display:block; }
.vdb-meta { font-size:13px; font-weight:600; color:var(--text-secondary); margin-bottom:10px; display:flex; gap:10px; flex-wrap:wrap; }
.vdb-desc-text { font-size:14px; line-height:1.6; color:var(--text-primary); white-space:pre-wrap; }
.vdb-desc-text p { margin-bottom:8px; }
.vdb-desc-text a { color:#3ea6ff; }
.vdb-show-more { background:none; border:none; color:var(--text-primary); font-weight:700; font-size:13px; cursor:pointer; padding:6px 0 0; }
</style>
<div class="vdb-wrap" id="vdbWrap">
<div class="vdb-tabs">
<button class="vdb-tab active" data-panel="vdb-about" onclick="switchVdbTab('vdb-about',this)">
<i class="bi bi-card-text"></i> About
</button>
@if($isVideoOwner)
<button class="vdb-tab" data-panel="vdb-insights" onclick="switchVdbTab('vdb-insights',this)">
<i class="bi bi-bar-chart-line-fill"></i> Insights
</button>
@endif
</div>
<div class="vdb-panel active" id="vdb-about">
<div class="vdb-meta">
<span><i class="bi bi-eye" style="margin-right:4px;"></i>{{ number_format($video->view_count) }} views</span>
<span></span>
<span>{{ $video->created_at->format('M d, Y') }}</span>
@if($video->duration)<span></span><span><i class="bi bi-clock" style="margin-right:4px;"></i>{{ $video->formatted_duration }}</span>@endif
</div>
@if(isset($descriptionSlot))
{!! $descriptionSlot !!}
@elseif($hasDesc)
<div id="vdbDescShort" class="vdb-desc-text">{{ $needsExpand ? $shortDescription : $fullDescription }}</div>
@if($needsExpand)
<div id="vdbDescFull" class="vdb-desc-text" style="display:none;">{{ $fullDescription }}</div>
<button class="vdb-show-more" onclick="toggleVdbDesc(this)">Show more</button>
@endif
@else
<p style="font-size:13px;color:var(--text-secondary);margin:0;">No description added.</p>
@endif
</div>
@if($isVideoOwner)
<x-video-insights :video="$video" />
@endif
</div>
<script>
// ── Tab switching ──────────────────────────────────────
function switchVdbTab(panelId, btn) {
document.querySelectorAll('.vdb-tab').forEach(b => b.classList.remove('active'));
document.querySelectorAll('.vdb-panel').forEach(p => p.classList.remove('active'));
btn.classList.add('active');
document.getElementById(panelId).classList.add('active');
if (panelId === 'vdb-insights') {
const panel = document.getElementById('vdb-insights');
const currentUrl = panel && panel.dataset.insightsBase;
if (currentUrl && currentUrl !== window._insLoadedUrl) loadInsights();
}
}
function toggleVdbDesc(btn) {
const s = document.getElementById('vdbDescShort'), f = document.getElementById('vdbDescFull');
if (!f) return;
if (f.style.display === 'none') { s.style.display='none'; f.style.display='block'; btn.textContent='Show less'; }
else { s.style.display='block'; f.style.display='none'; btn.textContent='Show more'; }
}
</script>
@endif