Spaces:
Paused
Paused
Download views/embed_player.ejs from Streamixph05/pops: direct link, hf CLI and curl.
- Browser
- Download file 32.5 kB
-
https://huggingface.co/spaces/Streamixph05/pops/resolve/main/views/embed_player.ejs
- Command line
-
hf download hf://spaces/Streamixph05/pops/views/embed_player.ejs
-
curl -L -o embed_player.ejs https://huggingface.co/spaces/Streamixph05/pops/resolve/main/views/embed_player.ejs
32.5 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"> | |
| <title><%= typeof title !== 'undefined' ? title : 'Pinomax Player' %></title> | |
| <!-- HLS.js & Artplayer --> | |
| <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/artplayer/dist/artplayer.js"></script> | |
| <style> | |
| * { | |
| box-sizing: border-box; | |
| margin: 0; | |
| padding: 0; | |
| user-select: none; | |
| -webkit-tap-highlight-color: transparent; | |
| } | |
| /* 🟢 100% TRUE FULLSCREEN CONTAINER FIX FOR WEBVIEW & IFRAMES */ | |
| html, body { | |
| width: 100vw; | |
| height: 100vh; | |
| height: 100dvh; | |
| background: #000; | |
| overflow: hidden; | |
| position: fixed; | |
| inset: 0; | |
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; | |
| } | |
| #artplayer { | |
| width: 100% ; | |
| height: 100% ; | |
| position: absolute ; | |
| inset: 0 ; | |
| background: #000; | |
| } | |
| .artplayer video { | |
| object-fit: contain; | |
| width: 100% ; | |
| height: 100% ; | |
| } | |
| /* Itago ang default Artplayer UI elements */ | |
| .artplayer .art-bottom, | |
| .art-video-player .art-bottom, | |
| .art-controls, | |
| .art-state-lock, | |
| .art-layer-auto-playback { | |
| display: none ; | |
| } | |
| /* Subtitle Styling */ | |
| .art-video-player .art-subtitle { | |
| color: #ffffff ; | |
| font-size: 17px ; | |
| font-weight: 700 ; | |
| text-shadow: 0 2px 4px rgba(0,0,0,0.9), 0 -2px 4px rgba(0,0,0,0.9), 2px 0 4px rgba(0,0,0,0.9), -2px 0 4px rgba(0,0,0,0.9) ; | |
| bottom: 50px ; | |
| transition: all 0.2s ease; | |
| } | |
| /* 🟢 CUSTOM OVERLAY LAYER (Pinned sa Pinakailalim ng Device) */ | |
| .custom-ui-layer { | |
| position: absolute ; | |
| inset: 0 ; | |
| width: 100% ; | |
| height: 100% ; | |
| z-index: 50; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: space-between; | |
| padding: env(safe-area-inset-top, 10px) env(safe-area-inset-right, 14px) env(safe-area-inset-bottom, 8px) env(safe-area-inset-left, 14px); | |
| background: linear-gradient(180deg, rgba(0,0,0,0.8) 0%, rgba(0,0,0,0) 25%, rgba(0,0,0,0) 65%, rgba(0,0,0,0.9) 100%); | |
| opacity: 0; | |
| pointer-events: none; | |
| transition: opacity 0.22s cubic-bezier(0.4, 0, 0.2, 1); | |
| } | |
| .custom-ui-layer.show-ui { | |
| opacity: 1; | |
| pointer-events: auto; | |
| } | |
| /* 🟢 TOP LEFT TITLE BADGE */ | |
| .ui-top { | |
| display: flex; | |
| justify-content: flex-start; | |
| align-items: center; | |
| width: 100%; | |
| padding-top: 4px; | |
| } | |
| .top-badge-title { | |
| font-size: 10.5px; | |
| font-weight: 600; | |
| color: #f1f1f1; | |
| background: rgba(15, 17, 23, 0.8); | |
| backdrop-filter: blur(12px); | |
| -webkit-backdrop-filter: blur(12px); | |
| border: 1px solid rgba(255, 255, 255, 0.15); | |
| padding: 5px 12px; | |
| border-radius: 20px; | |
| letter-spacing: 0.3px; | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 6px; | |
| max-width: 85%; | |
| box-shadow: 0 4px 12px rgba(0,0,0,0.6); | |
| } | |
| .top-badge-title .dot { | |
| width: 6px; | |
| height: 6px; | |
| background: #e50914; | |
| border-radius: 50%; | |
| display: inline-block; | |
| box-shadow: 0 0 6px #e50914; | |
| flex-shrink: 0; | |
| } | |
| .top-badge-title .text-title { | |
| white-space: nowrap; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| } | |
| /* 🟢 SKIP INTRO BUTTON */ | |
| .btn-skip-intro { | |
| display: none; | |
| position: absolute; | |
| right: 18px; | |
| bottom: 55px; | |
| z-index: 70; | |
| background: rgba(18, 20, 29, 0.85); | |
| backdrop-filter: blur(10px); | |
| -webkit-backdrop-filter: blur(10px); | |
| border: 1px solid rgba(255, 255, 255, 0.25); | |
| color: #fff; | |
| font-size: 11.5px; | |
| font-weight: 700; | |
| padding: 6px 14px; | |
| border-radius: 20px; | |
| cursor: pointer; | |
| box-shadow: 0 4px 15px rgba(0,0,0,0.6); | |
| transition: transform 0.15s ease, background 0.15s ease; | |
| letter-spacing: 0.4px; | |
| } | |
| .btn-skip-intro:active { | |
| transform: scale(0.92); | |
| background: #e50914; | |
| } | |
| /* 🟢 LOCK BUTTON (Left Edge) */ | |
| .btn-lock-screen { | |
| position: absolute; | |
| left: 16px; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| width: 38px; | |
| height: 38px; | |
| border-radius: 50%; | |
| background: rgba(18, 20, 29, 0.75); | |
| backdrop-filter: blur(10px); | |
| -webkit-backdrop-filter: blur(10px); | |
| border: 1px solid rgba(255, 255, 255, 0.15); | |
| color: #fff; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| transition: transform 0.15s; | |
| z-index: 60; | |
| } | |
| .btn-lock-screen:active { transform: translateY(-50%) scale(0.9); } | |
| .btn-lock-screen svg { width: 17px; height: 17px; fill: #fff; } | |
| /* FLOATING UNLOCK BUTTON */ | |
| #floatingUnlockBtn { | |
| display: none; | |
| position: absolute; | |
| top: 16px; | |
| left: 16px; | |
| z-index: 100; | |
| width: 38px; | |
| height: 38px; | |
| border-radius: 50%; | |
| background: #e50914; | |
| border: 1.5px solid #fff; | |
| color: #fff; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| box-shadow: 0 0 15px rgba(229, 9, 20, 0.8); | |
| animation: popIn 0.2s ease; | |
| } | |
| #floatingUnlockBtn svg { width: 17px; height: 17px; fill: #fff; } | |
| /* 🟢 BOTTOM BAR & TIMELINE (Lapat sa pinakababa) */ | |
| .ui-bottom { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 3px; | |
| width: 100%; | |
| margin-bottom: 2px; | |
| } | |
| /* SCRUBBER PROGRESS BAR */ | |
| .progress-bar-container { | |
| width: 100%; | |
| height: 18px; | |
| display: flex; | |
| align-items: center; | |
| cursor: pointer; | |
| position: relative; | |
| } | |
| .progress-bg { | |
| width: 100%; | |
| height: 3.5px; | |
| background: rgba(255, 255, 255, 0.25); | |
| border-radius: 2px; | |
| position: relative; | |
| transition: height 0.15s ease; | |
| } | |
| .progress-bar-container:hover .progress-bg, | |
| .progress-bar-container:active .progress-bg { | |
| height: 5.5px; | |
| } | |
| .progress-buffered { | |
| height: 100%; | |
| background: rgba(255, 255, 255, 0.45); | |
| border-radius: 2px; | |
| position: absolute; | |
| left: 0; top: 0; width: 0%; | |
| } | |
| .progress-filled { | |
| height: 100%; | |
| background: #e50914; | |
| border-radius: 2px; | |
| position: absolute; | |
| left: 0; top: 0; width: 0%; | |
| box-shadow: 0 0 6px rgba(229, 9, 20, 0.8); | |
| } | |
| .progress-thumb { | |
| width: 12px; | |
| height: 12px; | |
| border-radius: 50%; | |
| background: #fff; | |
| position: absolute; | |
| top: 50%; | |
| transform: translate(-50%, -50%); | |
| left: 0%; | |
| box-shadow: 0 0 5px rgba(0,0,0,0.9); | |
| transition: transform 0.1s; | |
| } | |
| .progress-bar-container:hover .progress-thumb { transform: translate(-50%, -50%) scale(1.2); } | |
| /* BOTTOM CONTROLS ROW */ | |
| .bottom-row { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: 0 2px; | |
| height: 34px; | |
| } | |
| .bottom-left, .bottom-right { | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| .btn-icon-ctl { | |
| background: none; | |
| border: none; | |
| color: #fff; | |
| cursor: pointer; | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| width: 32px; | |
| height: 32px; | |
| border-radius: 50%; | |
| position: relative; | |
| transition: transform 0.12s, background 0.12s; | |
| } | |
| .btn-icon-ctl:active { transform: scale(0.85); background: rgba(255,255,255,0.15); } | |
| .btn-icon-ctl svg { width: 19px; height: 19px; fill: currentColor; } | |
| .time-label { | |
| font-size: 11.5px; | |
| font-weight: 600; | |
| color: #e0e0e0; | |
| letter-spacing: 0.4px; | |
| font-variant-numeric: tabular-nums; | |
| margin-left: 2px; | |
| } | |
| @keyframes popIn { | |
| from { transform: scale(0.8); opacity: 0; } | |
| to { transform: scale(1); opacity: 1; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="artplayer"></div> | |
| <input type="file" id="subFileInput" accept=".vtt,.srt" style="display:none;" /> | |
| <!-- UNLOCK BUTTON --> | |
| <div id="floatingUnlockBtn" title="Unlock Screen"> | |
| <svg viewBox="0 0 24 24"><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"/></svg> | |
| </div> | |
| <!-- SKIP INTRO BUTTON --> | |
| <div id="btnSkipIntro" class="btn-skip-intro">Skip Intro ➔</div> | |
| <!-- OVERLAY LAYER --> | |
| <div class="custom-ui-layer show-ui" id="customUiLayer"> | |
| <!-- 🟢 TOP LEFT TITLE BADGE --> | |
| <div class="ui-top"> | |
| <div class="top-badge-title"> | |
| <span class="dot"></span> | |
| <span class="text-title" id="playerTitleDisplay"><%= typeof title !== 'undefined' && title ? title : 'Pinomax Video' %></span> | |
| </div> | |
| </div> | |
| <!-- 🟢 LOCK SCREEN BUTTON --> | |
| <div class="btn-lock-screen" id="btnLockControls" title="Lock Screen"> | |
| <svg viewBox="0 0 24 24"><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6h1.9c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm0 12H6V10h12v10z"/></svg> | |
| </div> | |
| <!-- 🟢 BOTTOM CONTROLS & TIMELINE --> | |
| <div class="ui-bottom"> | |
| <div class="progress-bar-container" id="customProgressBar"> | |
| <div class="progress-bg"> | |
| <div class="progress-buffered" id="customProgressBuf"></div> | |
| <div class="progress-filled" id="customProgressFill"></div> | |
| <div class="progress-thumb" id="customProgressThumb"></div> | |
| </div> | |
| </div> | |
| <div class="bottom-row"> | |
| <!-- Kaliwa: Play, Volume, Oras --> | |
| <div class="bottom-left"> | |
| <button class="btn-icon-ctl" id="btnMiniPlay" title="Play/Pause"> | |
| <svg id="iconMiniPause" viewBox="0 0 24 24" style="display:none;"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/></svg> | |
| <svg id="iconMiniPlay" viewBox="0 0 24 24"><path d="M8 5v14l11-7z"/></svg> | |
| </button> | |
| <button class="btn-icon-ctl" id="btnCustomVolume" title="Mute/Unmute"> | |
| <svg id="iconVolHigh" viewBox="0 0 24 24"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"/></svg> | |
| <svg id="iconVolMute" viewBox="0 0 24 24" style="display:none;"><path d="M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z"/></svg> | |
| </button> | |
| <div class="time-label" id="customTimeDisplay">00:00 / 00:00</div> | |
| </div> | |
| <!-- Kanan: -10s, +10s, Settings, Fullscreen --> | |
| <div class="bottom-right"> | |
| <!-- 10s Rewind --> | |
| <button class="btn-icon-ctl" id="btnCustomRewind" title="Rewind 10s"> | |
| <svg viewBox="0 0 24 24"> | |
| <path d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8zm-1.1 11h-.85v-3.26l-1.01.32v-.59l1.8-.63h.06V16zm4.29-1.93c0 .32-.05.6-.16.84-.11.24-.26.43-.46.57-.2.14-.43.24-.7.29-.27.05-.56.08-.88.08s-.6-.03-.87-.08c-.27-.05-.5-.15-.69-.29-.2-.14-.35-.33-.46-.57-.11-.24-.17-.52-.17-.84v-1.28c0-.32.06-.6.17-.84.11-.24.26-.43.46-.57.2-.14.42-.24.69-.29.27-.05.56-.08.87-.08s.61.03.88.08c.27.05.5.15.7.29.2.14.35.33.46.57.11.24.16.52.16.84v1.28zm-.87-1.39c0-.2-.03-.37-.08-.5-.06-.14-.13-.24-.23-.31-.1-.07-.21-.1-.35-.1s-.26.04-.35.11c-.1.07-.17.17-.23.31-.05.14-.08.31-.08.52v1.51c0 .2.03.37.08.51.05.14.13.24.23.31.1.07.21.11.35.11s.25-.04.35-.11c.1-.07.18-.17.23-.31.05-.14.08-.31.08-.51v-1.52z"/> | |
| </svg> | |
| </button> | |
| <!-- 10s Forward --> | |
| <button class="btn-icon-ctl" id="btnCustomForward" title="Forward 10s"> | |
| <svg viewBox="0 0 24 24"> | |
| <path d="M12 5V1l5 5-5 5V7c-3.31 0-6 2.69-6 6s2.69 6 6 6 6-2.69 6-6h2c0 4.42-3.58 8-8 8s-8-3.58-8-8 3.58-8 8-8zm-1.1 11h-.85v-3.26l-1.01.32v-.59l1.8-.63h.06V16zm4.29-1.93c0 .32-.05.6-.16.84-.11.24-.26.43-.46.57-.2.14-.43.24-.7.29-.27.05-.56.08-.88.08s-.6-.03-.87-.08c-.27-.05-.5-.15-.69-.29-.2-.14-.35-.33-.46-.57-.11-.24-.17-.52-.17-.84v-1.28c0-.32.06-.6.17-.84.11-.24.26-.43.46-.57.2-.14.42-.24.69-.29.27-.05.56-.08.87-.08s.61.03.88.08c.27.05.5.15.7.29.2.14.35.33.46.57.11.24.16.52.16.84v1.28zm-.87-1.39c0-.2-.03-.37-.08-.5-.06-.14-.13-.24-.23-.31-.1-.07-.21-.1-.35-.1s-.26.04-.35.11c-.1.07-.17.17-.23.31-.05.14-.08.31-.08.52v1.51c0 .2.03.37.08.51.05.14.13.24.23.31.1.07.21.11.35.11s.25-.04.35-.11c.1-.07.18-.17.23-.31.05-.14.08-.31.08-.51v-1.52z"/> | |
| </svg> | |
| </button> | |
| <!-- Settings Gear --> | |
| <button class="btn-icon-ctl" id="btnCustomSettings" title="Settings"> | |
| <svg viewBox="0 0 24 24"><path d="M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/></svg> | |
| </button> | |
| <!-- Fullscreen Toggle --> | |
| <button class="btn-icon-ctl" id="btnCustomFullscreen" title="Fullscreen"> | |
| <svg id="iconEnterFs" viewBox="0 0 24 24"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></svg> | |
| <svg id="iconExitFs" viewBox="0 0 24 24" style="display:none;"><path d="M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z"/></svg> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // 🟢 DOM References | |
| const uiLayer = document.getElementById('customUiLayer'); | |
| const floatingUnlockBtn = document.getElementById('floatingUnlockBtn'); | |
| const btnSkipIntro = document.getElementById('btnSkipIntro'); | |
| const playerTitleDisplay = document.getElementById('playerTitleDisplay'); | |
| const btnLock = document.getElementById('btnLockControls'); | |
| const btnRewind = document.getElementById('btnCustomRewind'); | |
| const btnForward = document.getElementById('btnCustomForward'); | |
| const btnMiniPlay = document.getElementById('btnMiniPlay'); | |
| const iconMiniPlay = document.getElementById('iconMiniPlay'); | |
| const iconMiniPause = document.getElementById('iconMiniPause'); | |
| const timeDisplay = document.getElementById('customTimeDisplay'); | |
| const progressFill = document.getElementById('customProgressFill'); | |
| const progressThumb = document.getElementById('customProgressThumb'); | |
| const progressBuf = document.getElementById('customProgressBuf'); | |
| const progressBar = document.getElementById('customProgressBar'); | |
| const btnVol = document.getElementById('btnCustomVolume'); | |
| const iconVolHigh = document.getElementById('iconVolHigh'); | |
| const iconVolMute = document.getElementById('iconVolMute'); | |
| const btnSettings = document.getElementById('btnCustomSettings'); | |
| const btnFullscreen = document.getElementById('btnCustomFullscreen'); | |
| const iconEnterFs = document.getElementById('iconEnterFs'); | |
| const iconExitFs = document.getElementById('iconExitFs'); | |
| const subFileInput = document.getElementById('subFileInput'); | |
| let isScreenLocked = false; | |
| let hideTimeout = null; | |
| let currentSubtitleUrl = ''; | |
| const streamUrl = "<%= streamUrl %>"; | |
| const movieTitle = "<%= typeof title !== 'undefined' && title ? title : 'Pinomax Video' %>"; | |
| // 🟢 100% Naka-connect na agad sa public/intro.mp4 mo | |
| const introUrl = '/intro.mp4'; | |
| let isIntroPlaying = Boolean(introUrl); | |
| let baseDlUrl = "<%= typeof downloadUrl !== 'undefined' ? downloadUrl : streamUrl %>"; | |
| if (baseDlUrl.includes('/stream/')) { | |
| baseDlUrl = baseDlUrl.replace('/stream/', '/download/'); | |
| } | |
| const urlParams = new URLSearchParams(window.location.search); | |
| const isVipUser = urlParams.get('vip') === '1'; | |
| const searchTarget = urlParams.get('tmdb_id') || urlParams.get('tmdb') || movieTitle; | |
| function checkVipAccess(actionName) { | |
| if (!isVipUser) { | |
| if (confirm('👑 VIP FEATURE ONLY!\nAng ' + actionName + ' ay para lamang sa VIP Members.\nGusto mo bang mag-upgrade sa VIP ngayon?')) { | |
| if (window.top) window.top.location.href = 'vip.php'; | |
| else window.location.href = 'vip.php'; | |
| } | |
| return false; | |
| } | |
| return true; | |
| } | |
| function formatTime(seconds) { | |
| if (isNaN(seconds) || seconds < 0) return '00:00'; | |
| const s = Math.floor(seconds); | |
| const h = Math.floor(s / 3600); | |
| const m = Math.floor((s % 3600) / 60); | |
| const remS = s % 60; | |
| const mm = m < 10 ? '0' + m : m; | |
| const ss = remS < 10 ? '0' + remS : remS; | |
| if (h > 0) { | |
| const hh = h < 10 ? '0' + h : h; | |
| return hh + ':' + mm + ':' + ss; | |
| } | |
| return mm + ':' + ss; | |
| } | |
| function showControls() { | |
| if (isScreenLocked || !uiLayer) return; | |
| uiLayer.classList.add('show-ui'); | |
| clearTimeout(hideTimeout); | |
| if (art && art.playing) { | |
| hideTimeout = setTimeout(() => { | |
| uiLayer.classList.remove('show-ui'); | |
| }, 3500); | |
| } | |
| } | |
| function setPlayState(isPlaying) { | |
| if (isPlaying) { | |
| if (iconMiniPlay) iconMiniPlay.style.display = 'none'; | |
| if (iconMiniPause) iconMiniPause.style.display = 'block'; | |
| } else { | |
| if (iconMiniPlay) iconMiniPlay.style.display = 'block'; | |
| if (iconMiniPause) iconMiniPause.style.display = 'none'; | |
| } | |
| } | |
| function updateSubtitleStyle(prop, val) { | |
| const sub = document.querySelector('.art-video-player .art-subtitle'); | |
| if (sub) sub.style[prop] = val; | |
| } | |
| // 🟢 INITIALIZE ARTPLAYER | |
| const art = new Artplayer({ | |
| container: '#artplayer', | |
| url: isIntroPlaying ? introUrl : streamUrl, | |
| type: 'mp4', | |
| title: '', | |
| poster: '<%= typeof poster !== 'undefined' ? poster : "" %>', | |
| theme: '#E50914', | |
| volume: 0.8, | |
| isLive: false, | |
| muted: false, | |
| autoplay: true, | |
| pip: true, | |
| autoSize: true, | |
| autoMini: true, | |
| screenshot: true, | |
| setting: true, | |
| loop: false, | |
| playbackRate: true, | |
| aspectRatio: true, | |
| fullscreen: false, | |
| fullscreenWeb: false, | |
| miniProgressBar: false, | |
| lock: false, | |
| fastForward: true, | |
| autoPlayback: true, | |
| airplay: true, | |
| subtitle: { | |
| url: '', | |
| type: 'vtt', | |
| style: { | |
| color: '#ffffff', | |
| fontSize: '17px', | |
| fontWeight: 'bold', | |
| }, | |
| encoding: 'utf-8', | |
| bilingual: true, | |
| }, | |
| customType: { | |
| m3u8: function (video, url, artObj) { | |
| if (window.Hls && Hls.isSupported()) { | |
| if (artObj.hls) artObj.hls.destroy(); | |
| const hls = new Hls(); | |
| hls.loadSource(url); | |
| hls.attachMedia(video); | |
| artObj.hls = hls; | |
| artObj.on('destroy', () => hls.destroy()); | |
| } else if (video.canPlayType('application/vnd.apple.mpegurl')) { | |
| video.src = url; | |
| } | |
| } | |
| }, | |
| settings: [ | |
| { | |
| name: 'downloadMovie', | |
| html: '📥 Download Movie (VIP)', | |
| tooltip: isVipUser ? 'VIP Ready' : 'VIP Only', | |
| onClick: function () { | |
| if (!checkVipAccess('Download Movie')) return; | |
| window.open(baseDlUrl + (baseDlUrl.includes('?') ? '&' : '?') + 'vip=1', '_blank'); | |
| }, | |
| }, | |
| { | |
| name: 'downloadSub', | |
| html: '💬 Download Subtitle (VIP)', | |
| tooltip: isVipUser ? 'Download CC' : 'VIP Only', | |
| onClick: function () { | |
| if (!checkVipAccess('Download Subtitles')) return; | |
| if (!currentSubtitleUrl || currentSubtitleUrl === 'import') { | |
| alert('Walang aktibong subtitle na naka-load. Pumili muna sa listahan!'); | |
| return; | |
| } | |
| const a = document.createElement('a'); | |
| a.href = currentSubtitleUrl; | |
| a.download = (searchTarget || 'subtitle') + '.vtt'; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| }, | |
| }, | |
| { | |
| name: 'aspectRatio', | |
| html: '🔲 Video Fit / Aspect Ratio', | |
| width: 220, | |
| selector: [ | |
| { default: true, html: 'Default (Fit)', value: 'default' }, | |
| { html: 'Stretch / Zoom to Fill', value: 'cover' }, | |
| { html: '16:9 Widescreen', value: '16:9' }, | |
| { html: '4:3 Standard', value: '4:3' } | |
| ], | |
| onSelect: function (item) { | |
| if (item.value === 'cover') { | |
| art.video.style.objectFit = 'cover'; | |
| art.aspectRatio = 'default'; | |
| } else if (item.value === 'default') { | |
| art.video.style.objectFit = 'contain'; | |
| art.aspectRatio = 'default'; | |
| } else { | |
| art.video.style.objectFit = 'contain'; | |
| art.aspectRatio = item.value; | |
| } | |
| return item.html; | |
| } | |
| }, | |
| { | |
| name: 'subtitle', | |
| html: '💬 CC Subtitles', | |
| width: 260, | |
| selector: [ | |
| { default: true, html: '❌ Cc Off', url: '' }, | |
| { html: '📁 Import File (.srt / .vtt)', url: 'import' } | |
| ], | |
| onSelect: function (item) { | |
| currentSubtitleUrl = item.url; | |
| if (item.url === 'import') { | |
| subFileInput.click(); | |
| } else if (item.url) { | |
| art.subtitle.switch(item.url, { name: item.html, type: 'vtt' }); | |
| art.subtitle.show = true; | |
| art.notice.show = 'Subtitle: ' + item.html; | |
| } else { | |
| art.subtitle.show = false; | |
| art.notice.show = 'Subtitles Off'; | |
| } | |
| return item.html; | |
| }, | |
| }, | |
| { | |
| name: 'subSize', | |
| html: '🔤 Subtitle Size', | |
| width: 220, | |
| selector: [ | |
| { html: 'Maliit (15px)', value: '15px' }, | |
| { default: true, html: 'Normal (17px)', value: '17px' }, | |
| { html: 'Malaki (22px)', value: '22px' } | |
| ], | |
| onSelect: function (item) { | |
| updateSubtitleStyle('fontSize', item.value); | |
| return item.html; | |
| } | |
| } | |
| ], | |
| }); | |
| function startMainMovie() { | |
| if (!isIntroPlaying) return; | |
| isIntroPlaying = false; | |
| if (btnSkipIntro) btnSkipIntro.style.display = 'none'; | |
| if (playerTitleDisplay) playerTitleDisplay.innerText = movieTitle; | |
| art.switchUrl(streamUrl).then(() => { | |
| art.play(); | |
| art.notice.show = '🎬 Playing Movie...'; | |
| }).catch(() => { | |
| art.url = streamUrl; | |
| art.play(); | |
| }); | |
| } | |
| art.on('ready', () => { | |
| // 🟢 MOUNT DIRECTLY SA OUTERMOST CONTAINER PARA LAPAT SA ILALIM NG SCREEN | |
| const targetContainer = document.getElementById('artplayer'); | |
| if (targetContainer) { | |
| if (uiLayer) targetContainer.appendChild(uiLayer); | |
| if (btnSkipIntro) targetContainer.appendChild(btnSkipIntro); | |
| } | |
| if (isIntroPlaying) { | |
| if (btnSkipIntro) btnSkipIntro.style.display = 'block'; | |
| if (playerTitleDisplay) playerTitleDisplay.innerText = 'PINOMAX Intro...'; | |
| } | |
| art.on('video:ended', () => { | |
| if (isIntroPlaying) startMainMovie(); | |
| }); | |
| if (btnSkipIntro) { | |
| btnSkipIntro.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| startMainMovie(); | |
| }); | |
| } | |
| // Tap screen to toggle UI | |
| if (targetContainer) { | |
| targetContainer.addEventListener('click', (e) => { | |
| if (isScreenLocked) return; | |
| if (e.target.closest('button') || e.target.closest('.btn-skip-intro') || e.target.closest('.btn-lock-screen') || e.target.closest('.progress-bar-container') || e.target.closest('.art-setting-item')) return; | |
| if (uiLayer.classList.contains('show-ui')) { | |
| uiLayer.classList.remove('show-ui'); | |
| } else { | |
| showControls(); | |
| } | |
| }); | |
| } | |
| const onPlaying = () => { | |
| setPlayState(true); | |
| showControls(); | |
| }; | |
| const onPaused = () => { | |
| setPlayState(false); | |
| if (uiLayer && !isScreenLocked) uiLayer.classList.add('show-ui'); | |
| clearTimeout(hideTimeout); | |
| }; | |
| art.on('play', onPlaying); | |
| art.on('video:playing', onPlaying); | |
| art.on('pause', onPaused); | |
| art.on('video:pause', onPaused); | |
| // Lock Screen Toggle | |
| btnLock.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| isScreenLocked = true; | |
| uiLayer.classList.remove('show-ui'); | |
| if (btnSkipIntro) btnSkipIntro.style.display = 'none'; | |
| floatingUnlockBtn.style.display = 'flex'; | |
| art.notice.show = '🔒 Screen Locked'; | |
| }); | |
| floatingUnlockBtn.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| isScreenLocked = false; | |
| floatingUnlockBtn.style.display = 'none'; | |
| uiLayer.classList.add('show-ui'); | |
| if (isIntroPlaying && btnSkipIntro) btnSkipIntro.style.display = 'block'; | |
| art.notice.show = '🔓 Screen Unlocked'; | |
| showControls(); | |
| }); | |
| // 10s Rewind & Forward | |
| btnRewind.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| art.currentTime = Math.max(0, art.currentTime - 10); | |
| art.notice.show = '⏪ 10s'; | |
| showControls(); | |
| }); | |
| btnForward.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| art.currentTime = Math.min(art.duration || 0, art.currentTime + 10); | |
| art.notice.show = '⏩ 10s'; | |
| showControls(); | |
| }); | |
| btnMiniPlay.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| art.toggle(); | |
| }); | |
| // Timeline update | |
| art.on('video:timeupdate', () => { | |
| const cur = art.currentTime || 0; | |
| const dur = art.duration || 0; | |
| const pct = dur > 0 ? (cur / dur) * 100 : 0; | |
| if (progressFill) progressFill.style.width = pct + '%'; | |
| if (progressThumb) progressThumb.style.left = pct + '%'; | |
| if (timeDisplay) timeDisplay.innerText = formatTime(cur) + ' / ' + formatTime(dur); | |
| }); | |
| art.on('video:progress', () => { | |
| if (art.duration > 0 && art.video && art.video.buffered.length > 0) { | |
| const bufEnd = art.video.buffered.end(art.video.buffered.length - 1); | |
| if (progressBuf) progressBuf.style.width = (bufEnd / art.duration) * 100 + '%'; | |
| } | |
| }); | |
| progressBar.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| const rect = progressBar.getBoundingClientRect(); | |
| const clickPos = (e.clientX - rect.left) / rect.width; | |
| if (art.duration) { | |
| art.currentTime = Math.max(0, Math.min(art.duration, clickPos * art.duration)); | |
| } | |
| showControls(); | |
| }); | |
| btnVol.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| art.muted = !art.muted; | |
| if (iconVolHigh) iconVolHigh.style.display = art.muted ? 'none' : 'block'; | |
| if (iconVolMute) iconVolMute.style.display = art.muted ? 'block' : 'none'; | |
| showControls(); | |
| }); | |
| btnSettings.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| art.setting.show = true; | |
| }); | |
| // 🟢 100% RELIABLE FULLSCREEN WITH NATIVE AUTO-ROTATE LANDSCAPE | |
| async function toggleFullscreen() { | |
| const isFs = document.fullscreenElement || document.webkitFullscreenElement || art.fullscreen; | |
| if (isFs) { | |
| if (document.exitFullscreen) await document.exitFullscreen().catch(() => {}); | |
| else if (document.webkitExitFullscreen) document.webkitExitFullscreen(); | |
| if (screen.orientation && screen.orientation.unlock) { | |
| screen.orientation.unlock(); | |
| } | |
| art.fullscreen = false; | |
| iconEnterFs.style.display = 'block'; | |
| iconExitFs.style.display = 'none'; | |
| } else { | |
| try { | |
| const el = document.documentElement; | |
| if (el.requestFullscreen) { | |
| await el.requestFullscreen(); | |
| } else if (el.webkitRequestFullscreen) { | |
| el.webkitRequestFullscreen(); | |
| } else if (art.video && art.video.webkitEnterFullscreen) { | |
| art.video.webkitEnterFullscreen(); | |
| } | |
| // Awtomatikong i-rotate sa pahalang (Landscape) sa Android phone | |
| if (screen.orientation && screen.orientation.lock) { | |
| await screen.orientation.lock('landscape').catch(() => {}); | |
| } | |
| } catch (_) { | |
| art.fullscreenWeb = true; | |
| } | |
| iconEnterFs.style.display = 'none'; | |
| iconExitFs.style.display = 'block'; | |
| } | |
| } | |
| btnFullscreen.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| toggleFullscreen(); | |
| }); | |
| document.addEventListener('fullscreenchange', () => { | |
| const isFs = !!document.fullscreenElement; | |
| iconEnterFs.style.display = isFs ? 'none' : 'block'; | |
| iconExitFs.style.display = isFs ? 'block' : 'none'; | |
| }); | |
| }); | |
| // Subtitle Fetching API | |
| if (searchTarget && searchTarget !== 'Pinomax Video Player') { | |
| fetch('/api/subtitles/' + encodeURIComponent(searchTarget)) | |
| .then(res => res.json()) | |
| .then(data => { | |
| if (data.success && data.subtitles && data.subtitles.length > 0) { | |
| art.setting.update({ | |
| name: 'subtitle', | |
| width: 260, | |
| html: '💬 CC (' + data.subtitles.length + ')', | |
| selector: [ | |
| { default: true, html: '❌ Cc Off', url: '' }, | |
| ...data.subtitles.map(s => ({ | |
| html: s.name, | |
| url: s.url | |
| })), | |
| { html: '📁 Import File (.srt / .vtt)', url: 'import' } | |
| ], | |
| onSelect: function (item) { | |
| currentSubtitleUrl = item.url; | |
| if (item.url === 'import') { | |
| subFileInput.click(); | |
| } else if (item.url) { | |
| art.subtitle.switch(item.url, { name: item.html, type: 'vtt' }); | |
| art.subtitle.show = true; | |
| art.notice.show = 'Subtitle: ' + item.html; | |
| } else { | |
| art.subtitle.show = false; | |
| art.notice.show = 'Subtitles Off'; | |
| } | |
| return item.html; | |
| } | |
| }); | |
| } | |
| }).catch(err => console.warn('Sub fetch error:', err)); | |
| } | |
| subFileInput.addEventListener('change', function (e) { | |
| const file = e.target.files[0]; | |
| if (file) { | |
| const fileUrl = URL.createObjectURL(file); | |
| currentSubtitleUrl = fileUrl; | |
| const ext = file.name.split('.').pop().toLowerCase(); | |
| art.subtitle.switch(fileUrl, { | |
| name: file.name, | |
| type: ext === 'srt' ? 'srt' : 'vtt', | |
| }).then(() => { | |
| art.subtitle.show = true; | |
| art.notice.show = 'Subtitle Loaded: ' + file.name; | |
| }); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |