231 lines
9.2 KiB
PHP
231 lines
9.2 KiB
PHP
<?php
|
|
$viewerUrl = $viewDatas['entity']->getViewer() ?? '';
|
|
$viewerUrl = trim((string) $viewerUrl);
|
|
|
|
// 개발 중 디버그 출력(원하면 false로)
|
|
$debug = true;
|
|
|
|
$isHls = $viewerUrl && preg_match('#\.m3u8(\?|$)#i', $viewerUrl);
|
|
|
|
// 정적 JS 경로 (네 프로젝트에 맞게 수정)
|
|
$ovenJs = '/js/ovenplayer.js';
|
|
$hlsJs = '/js/hls.min.js';
|
|
?>
|
|
|
|
<?php if ($debug): ?>
|
|
<div style="font-size:12px;color:#666;margin:6px 0;">
|
|
viewerUrl:
|
|
<?= esc($viewerUrl) ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<style>
|
|
/* ====== 공통: 뷰어 영역이 항상 넓게 ====== */
|
|
.console-stage {
|
|
width: 100%;
|
|
height: 70vh;
|
|
/* 기본: 화면 높이의 70% */
|
|
min-height: 560px;
|
|
/* 최소 높이 */
|
|
background: #000;
|
|
border-radius: 6px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* 모달 안에서 더 크게 쓰고 싶으면 높이를 좀 더 키움 */
|
|
.modal.show .console-stage {
|
|
height: 78vh;
|
|
min-height: 600px;
|
|
}
|
|
|
|
/* iframe은 stage를 꽉 채우게 */
|
|
.console-frame {
|
|
width: 100%;
|
|
height: 100%;
|
|
border: 0;
|
|
background: #000;
|
|
}
|
|
|
|
/* ====== 핵심: 1280x800 원본 비율 유지하며 크게 (레터박스 허용) ======
|
|
- contain: 비율 유지 + 남는 공간 검정 여백
|
|
- cover: 꽉 채우기(일부 잘림)
|
|
*/
|
|
#ovenplayer-container video {
|
|
width: 100% !important;
|
|
height: 100% !important;
|
|
object-fit: contain;
|
|
/* 여기만 cover로 바꾸면 꽉 채움 */
|
|
background: #000;
|
|
}
|
|
|
|
/* 모달 내부 padding 때문에 작아지는 것 방지(가능하면 0) */
|
|
.modal-body {
|
|
padding: 0.75rem;
|
|
}
|
|
</style>
|
|
|
|
<div id="container" class="content">
|
|
<div class="mb-3">
|
|
<div class="d-flex align-items-center gap-2 flex-wrap">
|
|
<?php if ($viewerUrl): ?>
|
|
<a class="btn btn-sm btn-primary" href="<?= esc($viewerUrl) ?>" target="_blank" rel="noopener">
|
|
새 창으로 열기
|
|
</a>
|
|
<small class="text-muted">
|
|
(iframe이 차단되는 장비/콘솔은 새 창으로 열어주세요)
|
|
</small>
|
|
<?php else: ?>
|
|
<div class="alert alert-warning mb-0">
|
|
viewerUrl이 비어있습니다. (ServerEntity->viewer 확인)
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if ($viewerUrl): ?>
|
|
|
|
<?php if ($isHls): ?>
|
|
<!-- HLS/LL-HLS: OvenPlayer -->
|
|
<div class="console-stage">
|
|
<div id="ovenplayer-container" style="width:100%;height:100%;"></div>
|
|
<div id="player-error" style="display:none;color:#fff;padding:12px;"></div>
|
|
</div>
|
|
|
|
<script>
|
|
(function () {
|
|
const url = <?= json_encode($viewerUrl, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?>;
|
|
const OVEN_JS = <?= json_encode($ovenJs) ?>;
|
|
const HLS_JS = <?= json_encode($hlsJs) ?>;
|
|
|
|
let playerInstance = null;
|
|
let started = false;
|
|
|
|
function showError(msg) {
|
|
const err = document.getElementById('player-error');
|
|
const box = document.getElementById('ovenplayer-container');
|
|
if (box) box.style.display = 'none';
|
|
if (err) {
|
|
err.style.display = 'block';
|
|
err.textContent = msg;
|
|
}
|
|
console.error(msg);
|
|
}
|
|
|
|
function loadScriptOnce(src, key) {
|
|
return new Promise((resolve, reject) => {
|
|
const existing = document.querySelector(`script[data-${key}="1"]`);
|
|
if (existing) return resolve();
|
|
|
|
const s = document.createElement('script');
|
|
s.src = src;
|
|
s.async = true;
|
|
s.dataset[key] = "1";
|
|
s.onload = () => resolve();
|
|
s.onerror = () => reject(new Error(`Failed to load ${src}`));
|
|
document.head.appendChild(s);
|
|
});
|
|
}
|
|
|
|
async function ensureDeps() {
|
|
// hls.js 먼저 (Chrome/Edge는 필수)
|
|
if (typeof window.Hls === 'undefined') {
|
|
await loadScriptOnce(HLS_JS, 'hlsjs');
|
|
}
|
|
// OvenPlayer
|
|
if (typeof window.OvenPlayer === 'undefined') {
|
|
await loadScriptOnce(OVEN_JS, 'ovenplayer');
|
|
}
|
|
}
|
|
|
|
function destroyPlayerIfAny() {
|
|
try {
|
|
if (playerInstance && typeof playerInstance.remove === 'function') {
|
|
playerInstance.remove();
|
|
}
|
|
} catch (e) { }
|
|
playerInstance = null;
|
|
|
|
// 컨테이너 초기화
|
|
const box = document.getElementById('ovenplayer-container');
|
|
if (box) box.innerHTML = '';
|
|
}
|
|
|
|
function startPlayer() {
|
|
if (started) return;
|
|
started = true;
|
|
|
|
if (typeof window.OvenPlayer === 'undefined') {
|
|
showError('OvenPlayer 로드 실패(ovenplayer.js 경로 확인)');
|
|
return;
|
|
}
|
|
if (typeof window.Hls === 'undefined') {
|
|
showError('hls.js 로드 실패(hls.min.js 경로 확인)');
|
|
return;
|
|
}
|
|
|
|
destroyPlayerIfAny();
|
|
|
|
// LL-HLS(fMP4) 안정 옵션 + credentials 강제 off
|
|
playerInstance = window.OvenPlayer.create('ovenplayer-container', {
|
|
autoStart: true,
|
|
autoFallback: true,
|
|
mute: false,
|
|
sources: [{ type: 'hls', file: url }],
|
|
hlsConfig: {
|
|
lowLatencyMode: true,
|
|
backBufferLength: 0,
|
|
liveSyncDurationCount: 1,
|
|
liveMaxLatencyDurationCount: 3,
|
|
xhrSetup: function (xhr) { xhr.withCredentials = false; }
|
|
}
|
|
});
|
|
}
|
|
|
|
async function boot() {
|
|
try {
|
|
await ensureDeps();
|
|
|
|
// ===== Bootstrap modal 안에서 “작게” 뜨는 문제 해결 =====
|
|
// 모달이 완전히 열린(shown) 다음에 플레이어 생성
|
|
const container = document.getElementById('ovenplayer-container');
|
|
const modal = container ? container.closest('.modal') : null;
|
|
|
|
if (modal) {
|
|
// 이미 열린 모달이라면 약간 딜레이 후 시작
|
|
if (modal.classList.contains('show')) {
|
|
setTimeout(startPlayer, 80);
|
|
} else {
|
|
modal.addEventListener('shown.bs.modal', function () {
|
|
started = false; // 다시 열릴 때 재생성 가능하게
|
|
setTimeout(startPlayer, 80);
|
|
}, { once: true });
|
|
}
|
|
} else {
|
|
// 모달이 아니면 바로 시작
|
|
setTimeout(startPlayer, 30);
|
|
}
|
|
} catch (e) {
|
|
showError(e.message || String(e));
|
|
}
|
|
}
|
|
|
|
boot();
|
|
})();
|
|
</script>
|
|
|
|
<?php else: ?>
|
|
<!-- iLO/noVNC/Proxmox 콘솔/텍스트 콘솔: iframe embed -->
|
|
<div class="console-stage">
|
|
<iframe class="console-frame" src="<?= esc($viewerUrl) ?>" allow="clipboard-read; clipboard-write; fullscreen"
|
|
referrerpolicy="no-referrer"
|
|
sandbox="allow-same-origin allow-scripts allow-forms allow-popups allow-pointer-lock allow-top-navigation-by-user-activation">
|
|
</iframe>
|
|
</div>
|
|
|
|
<div class="mt-2 text-muted" style="font-size:12px;">
|
|
※ iLO/일부 콘솔은 보안 헤더로 iframe 표시가 막힐 수 있습니다. 그 경우 위의 “새 창으로 열기”를 사용하세요.
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php endif; ?>
|
|
</div>
|