lc-youtube-player
Version:
Web Component reproductor de YouTube personalizado.
1 lines • 39.1 kB
JavaScript
window.__ytApiReadyPromise||(window.__ytApiReadyPromise=new Promise(t=>{let e=!1,i=null;const s=()=>{e||(e=!0,i&&clearInterval(i),t())};if(window.YT&&window.YT.Player)return void s();const r=window.onYouTubeIframeAPIReady;window.onYouTubeIframeAPIReady=function(){if("function"==typeof r)try{r()}catch(t){}s()},i=setInterval(()=>{window.YT&&window.YT.Player&&s()},50);if(!Array.from(document.getElementsByTagName("script")).some(t=>t.src&&-1!==t.src.indexOf("youtube.com/iframe_api"))){const t=document.createElement("script");t.src="https://www.youtube.com/iframe_api",document.head.appendChild(t)}}));class LCYouTube extends HTMLElement{static get observedAttributes(){return["video","playlist","index","autoplay","dvr-window","color-surface","color-text","color-accent","color-subtle","color-overlay"]}constructor(){super(),this._video="",this._playlist="",this._index=0,this._autoplay=!1,this._userInteracted=!1,this._player=null,this._duration=0,this._timer=null,this._lastTap=0,this._lastDuration=0,this._dvrWindow=14400,this._liveStable=!1,this._liveTicks=0,this._dvrStable=!1,this._dvrTicks=0,this._userMuted=!1,this._pendingPlayIntent=null,this._pendingForceUnmute=!1,this._isSeeking=!1,this._seekPointerId=null,this._seekPreview=null,this._awaitingLiveEdge=!1,this._ignoreSeekClick=!1,this._handleKeydown=null,this._defaultColors={surface:"#000",text:"#fff",accent:"#427ddbff",subtle:"rgba(255,255,255,.12)",overlay:"rgba(0,0,0,.1)"},this._colors={...this._defaultColors},this._colorAttrMap={"color-surface":"surface","color-text":"text","color-accent":"accent","color-subtle":"subtle","color-overlay":"overlay"},this._isConnected=!1,this.attachShadow({mode:"open"}),this.shadowRoot.innerHTML='\n\t <style>\n\t .vol-toggle{display:inline-flex}\n\t :host{display:block}\n\t .yt-wrap{position:relative;width:var(--lc-width,100%);max-width:var(--lc-max-width,100%);margin:var(--lc-margin,0 auto);background:var(--lc-bg,#000);aspect-ratio:16/9;border-radius:5px}\n\t .overlay,.controls,#player,.live-badge{border-radius:inherit}\n\t iframe{border-radius:inherit}\n\t iframe{position:absolute;inset:0;width:100%;height:100%;border:0}\n\t .overlay{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:var(--lc-overlay-base,rgba(0,0,0,.1));cursor:pointer;z-index:3;background-size:cover;background-position:center;background-repeat:no-repeat}\n\t .overlay-content{display:flex;flex-direction:column;align-items:center;gap:18px;text-align:center}\n\t .overlay.playing{ background: transparent; }\n\t .overlay.playing .play{ display: none; }\n\t .overlay .play{width:84px;height:84px;border-radius:50%;background:var(--lc-accent);display:grid;place-items:center;box-shadow:0 8px 30px rgba(0,0,0,.4)}\n\t .overlay .play:after{content:"";display:block;width:0;height:0;border-left:28px solid #ffffffff;border-top:18px solid transparent;border-bottom:18px solid transparent;margin-left:6px}\n\t .sound-hint{position:static;background:var(--lc-hint-bg,rgba(0,0,0,.65));color:var(--lc-text,#fff);padding:6px 12px;border-radius:8px;font:500 13px/1 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;cursor:pointer;z-index:6;user-select:none}\n\t .sound-hint.hide{display:none}\n\t .controls{position:absolute;left:0;right:0;bottom:0;padding:10px;display:flex;gap:10px;align-items:center;background:linear-gradient(to top, rgba(0,0,0,0.5), rgba(0,0,0,0));z-index:4;user-select:none;opacity:0;pointer-events:none;transition: opacity .2s ease}\n\t .yt-wrap:hover .controls,.yt-wrap.show-controls .controls{opacity:1;pointer-events:auto}\n\t .btn,.time,.vol,.fs{color:var(--lc-text,#fff);font:500 14px/1 system-ui, -apple-system, Segoe UI, Roboto, sans-serif}\n\t .btn{display:inline-flex;align-items:center;gap:6px;padding:6px 10px;border-radius:10px;background:var(--lc-btn-bg,rgba(255,255,255,.12));cursor:pointer}\n\t .btn:hover{background:var(--lc-btn-bg-hover,rgba(255,255,255,.2))}\n\t .btn-live{background:var(--lc-live-bg,rgba(255,0,0,.85)); font-weight:600}\n\t .btn-live:hover{background:var(--lc-live-bg-hover,rgba(255,0,0,.95))}\n\t .btn-live .dot{width:8px;height:8px;border-radius:50%;background:#fff;box-shadow:0 0 0 0 rgba(255,255,255,0.9);animation: pulse 1.2s ease-out infinite}\n\t #goLive{display:none}\n\t .progress{position:relative;flex:1;height:6px;background:var(--lc-progress-bg,rgba(255,255,255,.25));border-radius:999px;cursor:pointer}\n\t .progress.disabled{opacity:.5; cursor:not-allowed}\n\t .progress.disabled .seek{display:none}\n\t .progress .bar{position:absolute;left:0;top:0;height:100%;width:0;background:var(--lc-progress-fill,#fff);border-radius:999px}\n\t .progress .seek{position:absolute;left:0;top:50%;transform:translate(-50%,-50%);width:14px;height:14px;border-radius:50%;background:var(--lc-progress-fill,#fff)}\n\t .time{min-width:110px;text-align:center;opacity:.9}\n\t .vol{display:flex;align-items:center;gap:6px;background:var(--lc-btn-bg,rgba(255,255,255,.12));padding:6px 10px;border-radius:10px}\n .vol input{accent-color:var(--lc-accent,#fff)}\n\t .fs{cursor:pointer;background:var(--lc-btn-bg,rgba(255,255,255,.12));padding:6px 10px;border-radius:10px}\n\t .vol-toggle{position:relative;align-items:center;justify-content:center;color:var(--lc-text,#fff)}\n\t .vol-toggle svg{width:20px;height:20px;display:block}\n\t .vol-toggle svg path{fill:currentColor !important} /* fuerza a usar currentColor */\n\t .vol-toggle.muted{color:var(--lc-muted-color,#ff3b30)}\n\t .live-badge{position:absolute;top:10px;left:10px;display:none;align-items:center;gap:8px;padding:6px 10px;border-radius:999px;background:var(--lc-live-bg,rgba(255,0,0,.85));color:var(--lc-text,#fff);font:600 12px/1 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;letter-spacing:.3px;z-index:5;pointer-events:none}\n\t .live-badge .dot{width:8px;height:8px;border-radius:50%;background:#fff;box-shadow:0 0 0 0 rgba(255,255,255,0.9);animation: pulse 1.2s ease-out infinite}\n .error-mask{position:absolute;inset:0;display:none;align-items:center;justify-content:center;background:#000;color:#fff;font:600 16px/1.4 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;z-index:7;text-align:center;padding:20px;border-radius:inherit}\n .error-mask.show{display:flex}\n @keyframes pulse{0%{box-shadow:0 0 0 0 rgba(255,255,255,0.9)}70%{box-shadow:0 0 0 10px rgba(255,255,255,0)}100%{box-shadow:0 0 0 0 rgba(255,255,255,0)}}\n .root{ -webkit-touch-callout:none; -webkit-user-select:none; -moz-user-select:none; user-select:none }\n .yt-wrap.live .controls{opacity:1;pointer-events:auto}\n /* === Accesibilidad móvil / touch === */\n /* Respeta la zona segura de iOS (home bar) y hace la barra más grande para dedo */\n :root { --lc-safe: env(safe-area-inset-bottom, 0px); }\n /* En dispositivos táctiles, no ocultar controles por hover */\n @media (hover: none) {\n .yt-wrap .controls{opacity:1;pointer-events:auto}\n }\n /* Aumenta el padding inferior considerando la zona segura */\n .controls{padding-bottom: calc(10px + var(--lc-safe));}\n /* Ajustes específicos para pantallas pequeñas */\n @media (max-width: 600px){\n .controls{gap:8px;padding:12px 10px calc(12px + var(--lc-safe));}\n .btn,.time,.vol,.fs{font:500 15px/1 system-ui, -apple-system, Segoe UI, Roboto, sans-serif}\n .progress{height:12px}\n .progress .seek{width:18px;height:18px}\n .time{min-width:90px}\n }\n /* Controles de volumen y GoLive compactos en móvil */\n @media (max-width:600px){\n .vol{display:none !important;} /* Oculta slider */\n #goLive{display:none !important;} /* Oculta botón para liberar espacio */\n .vol-toggle{display:inline-flex;} /* Muestra botón toggle */\n .fs{display:none !important;} /* Oculta fullscreen en móviles */\n }\n @media (min-width:601px){\n .vol-toggle{display:inline-flex;} /* En desktop también mostramos el botón simple */\n }\n </style>\n\t <div class="root">\n\t <div class="yt-wrap" id="wrap">\n\t <div class="overlay" id="overlay" aria-label="Reproducir/Pausar" role="button" tabindex="0">\n\t <div class="overlay-content">\n\t <div class="play" title="Reproducir"></div>\n\t <div class="sound-hint hide" id="soundHint">🔊 Toca para activar sonido</div>\n\t </div>\n\t </div>\n\t <div id="player"></div>\n\t <div class="error-mask" id="errorMask">Video no disponible</div>\n\t <div class="live-badge" id="live"><span class="dot"></span> EN VIVO</div>\n\t <div class="controls" id="controls">\n <div class="btn" id="prev" title="Anterior">⏮</div>\n <div class="btn" id="playPause" title="Reproducir/Pausar">▶︎</div>\n <div class="btn" id="next" title="Siguiente">⏭</div>\n <div class="progress" id="progress" title="Buscar">\n <div class="bar" id="bar"></div>\n <div class="seek" id="seek"></div>\n </div>\n <div class="time" id="time">00:00 / 00:00</div>\n <div class="btn btn-live" id="goLive" title="Ir al punto en vivo"><span class="dot"></span> IR AL VIVO</div>\n <div class="btn vol-toggle" id="volToggle" title="Silenciar/Activar volumen">\n <svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" aria-hidden="true" focusable="false">\n <g><g>\n <path d="M59.998,28.001h-7.999c-2.211,0-4,1.789-4,4s1.789,4,4,4h7.999c2.211,0,4-1.789,4-4 S62.209,28.001,59.998,28.001z"></path>\n <path d="M49.71,19.466l6.929-4c1.914-1.105,2.57-3.551,1.461-5.465c-1.102-1.914-3.547-2.57-5.46-1.465l-6.93,4 c-1.914,1.105-2.57,3.551-1.461,5.464C45.351,19.915,47.796,20.571,49.71,19.466z"></path>\n <path d="M56.639,48.535l-6.929-3.999c-1.914-1.105-4.355-0.449-5.461,1.464c-1.105,1.914-0.453,4.359,1.461,5.465 l6.93,4c1.913,1.105,4.358,0.449,5.464-1.465S58.553,49.641,56.639,48.535z"></path>\n <path d="M37.53,0.307c-1.492-0.625-3.211-0.277-4.359,0.867L18.343,16.001H4c-2.211,0-4,1.789-4,4v24 C0,46.211,1.789,48,4,48h14.343l14.828,14.828C33.937,63.594,34.96,64,35.999,64c0.516,0,1.035-0.098,1.531-0.305 c1.496-0.617,2.469-2.078,2.469-3.695V4.001C39.999,2.384,39.026,0.924,37.53,0.307z"></path>\n </g></g>\n </svg>\n </div>\n <div class="vol"><input type="range" id="volume" min="0" max="100" value="100" /></div>\n <div class="fs btn" id="fs" title="Pantalla completa">⛶</div>\n </div>\n </div>\n </div>\n '}get video(){return this._video}set video(t){if(null==t)return void this.removeAttribute("video");const e=String(t);this.getAttribute("video")!==e&&this.setAttribute("video",e)}get playlist(){return this._playlist}set playlist(t){if(null==t||""===t)return void this.removeAttribute("playlist");const e=String(t);this.getAttribute("playlist")!==e&&this.setAttribute("playlist",e)}get index(){const t=this.getAttribute("index");if(null==t)return null;const e=parseInt(t,10);return Number.isNaN(e)?null:e}set index(t){if(null==t||""===t)return void this.removeAttribute("index");const e=Number(t);if(Number.isNaN(e))return;const i=String(e);this.getAttribute("index")!==i&&this.setAttribute("index",i)}get autoplay(){return this._autoplay}set autoplay(t){this._parseBool(t)?this.hasAttribute("autoplay")||this.setAttribute("autoplay",""):this.hasAttribute("autoplay")&&this.removeAttribute("autoplay")}get dvrWindow(){return this._dvrWindow}set dvrWindow(t){const e=Number(t);if(Number.isNaN(e)||e<=0)return void this.removeAttribute("dvr-window");const i=String(e);this.getAttribute("dvr-window")!==i&&this.setAttribute("dvr-window",i)}_parseBool(t){if(null==t)return!1;const e=String(t).toLowerCase().trim();return"1"===e||"true"===e||"yes"===e||""===e}_parseListId(t){if(!t)return"";try{if(/^https?:\/\//i.test(t)){const e=new URL(t,window.location.href).searchParams.get("list");if(e)return e}}catch(t){}return/^(PL|UU|OL|RD)[A-Za-z0-9_-]+$/.test(t)?t:""}_parseVideoId(t){if(!t)return"";try{if(/^https?:\/\//i.test(t)){const e=new URL(t,window.location.href),i=e.searchParams.get("v");if(i)return i;const s=(e.hostname||"").replace(/^www\./,""),r=(e.pathname||"").split("/").filter(Boolean);if("youtu.be"===s&&r[0])return r[0];const n=r.indexOf("embed");if(-1!==n&&r[n+1])return r[n+1];const a=r.indexOf("shorts");if(-1!==a&&r[a+1])return r[a+1];const l=r.indexOf("live");if(-1!==l&&r[l+1])return r[l+1]}}catch(t){}return/^[A-Za-z0-9_-]{11}$/.test(t)?t:""}connectedCallback(){this._video=this._parseVideoId(this.getAttribute("video")||""),this._playlist=this._parseListId(this.getAttribute("playlist")||"");let t=parseInt(this.getAttribute("index")||"0",10);-1===t?(this._startAtLast=!0,this._index=0):(this._startAtLast=!1,this._index=t>0?t-1:0),this._autoplay=this._parseBool(this.getAttribute("autoplay"))||this.hasAttribute("autoplay");const e=parseInt(this.getAttribute("dvr-window")||"",10);!Number.isNaN(e)&&e>60&&(this._dvrWindow=e),this._cacheEls(),this._syncColorAttributes(),this._isConnected=!0,this._applyColorVars(),this._bindUI(),this._mountPlayer()}disconnectedCallback(){this._isConnected=!1,this._teardown()}attributeChangedCallback(t,e,i){if(this._colorAttrMap&&t in this._colorAttrMap){if(e===i)return;this._updateColorAttribute(t,i)}else{if("video"===t&&e!==i){if(this._video=this._parseVideoId(i||""),this._isLive=!1,!this._player)return void((this._video||this._playlist)&&this._mountPlayer());if(this._video)try{this._player.loadVideoById(this._video)}catch(t){}}if("playlist"===t&&e!==i){if(this._playlist=this._parseListId(i||""),!this._player)return(this._playlist||this._video)&&this._mountPlayer(),void this._updatePlaylistNav();if(this._player&&this._playlist){const t={list:this._playlist,listType:"playlist",index:this._index};try{this._autoplay?this._player.loadPlaylist(t):this._player.cuePlaylist(t)}catch(t){}}this._updatePlaylistNav()}if("index"===t&&e!==i){let t=parseInt(i||"0",10);if(-1===t?(this._startAtLast=!0,this._index=0):(this._startAtLast=!1,this._index=t>0?t-1:0),this._player&&this._playlist)if(this._startAtLast&&"function"==typeof this._player.getPlaylist){const t=this._player.getPlaylist(),e=Array.isArray(t)?t.length-1:0;try{this._player.playVideoAt(e)}catch(t){}}else try{this._player.playVideoAt(this._index)}catch(t){}this._updatePlaylistNav()}if("autoplay"===t&&e!==i){if(this._autoplay=this._parseBool(i)||this.hasAttribute("autoplay"),this._player&&this._autoplay)try{this._player.mute(),this._player.playVideo()}catch(t){}this._updatePlaylistNav()}if("dvr-window"===t&&e!==i){const t=parseInt(i||"",10);!Number.isNaN(t)&&t>60&&(this._dvrWindow=t)}}}_cacheEls(){const t=this.shadowRoot;this.$wrap=t.getElementById("wrap"),this.$overlay=t.getElementById("overlay"),this.$player=t.getElementById("player"),this.$live=t.getElementById("live"),this.$soundHint=t.getElementById("soundHint"),this.$progress=t.getElementById("progress"),this.$bar=t.getElementById("bar"),this.$seek=t.getElementById("seek"),this.$time=t.getElementById("time"),this.$goLive=t.getElementById("goLive"),this.$vol=t.getElementById("volume"),this.$volToggle=t.getElementById("volToggle"),this.$fs=t.getElementById("fs"),this.$playBtn=t.getElementById("playPause"),this.$error=t.getElementById("errorMask"),this.$controls=t.getElementById("controls"),this.$prev=t.getElementById("prev"),this.$next=t.getElementById("next")}_showError(){try{this._stopTimer()}catch(t){}this.$overlay&&(this.$overlay.style.display="none"),this.$controls&&(this.$controls.style.display="none"),this.$error&&this.$error.classList.add("show")}_emit(t,e={}){try{this.dispatchEvent(new CustomEvent(t,{detail:e||{},bubbles:!0,composed:!0}))}catch(t){}}_detectLive(){try{if(this._playlist)return!1;const t=this._player?.getVideoData?.()||{};if("boolean"==typeof t.isLiveNow)return t.isLiveNow;if("boolean"==typeof t.isLive)return t.isLive;if("boolean"==typeof t.isLiveContent)return t.isLiveContent;const e=this._player?.getPlayerState?.(),i=e===YT.PlayerState.PLAYING||e===YT.PlayerState.BUFFERING||e===YT.PlayerState.PAUSED,s=Number(this._player?.getDuration?.()||0);if(i&&0===s)return!0;if(i&&s>0){const t=Number(this._player?.getCurrentTime?.()||0);if(t>=0&&t<=s+1)return!0}return!1}catch(t){return!1}}_hasLiveDVR(){try{if(!this._isLive)return!1;const t=Number(this._player?.getDuration?.()||0)>0;return t!==this._dvrStable?(this._dvrTicks++,this._dvrTicks>=2&&(this._dvrStable=t,this._dvrTicks=0)):this._dvrTicks=0,this._dvrStable}catch(t){return this._dvrStable||!1}}_getDvrRange(){const t=Math.max(0,this._player?.getDuration?.()||this._duration||0),e=Math.max(60,Math.min(this._dvrWindow,t));return{start:Math.max(0,t-e),end:t}}_setLiveUI(t){const e=!!this._isLive,i=!!t;if(this._isLive=i,i&&!e?this._awaitingLiveEdge=!0:i||(this._awaitingLiveEdge=!1),this.$wrap&&(i?this.$wrap.classList.add("live"):this.$wrap.classList.remove("live")),this.$live&&e!==i&&(this.$live.style.display=i?"inline-flex":"none"),this.$progress){this.$progress.style.display="";this._isLive&&!this._hasLiveDVR()?this.$progress.classList.add("disabled"):this.$progress.classList.remove("disabled")}e!==i&&this._emit("lc-live-change",{live:i})}_ensureAudioOnUserPlay(t=!1,e={}){try{if(!this._player)return void(t&&(this._pendingForceUnmute=!0));const i={source:e.source||"auto"};if(t){let t=this.$vol?parseInt(this.$vol.value,10):100;return(!Number.isFinite(t)||t<=0)&&(t=100,this.$vol&&(this.$vol.value=String(t))),this._player?.unMute?.(),this._player?.setVolume?.(t),this._userMuted=!1,this._pendingForceUnmute=!1,this.$volToggle&&this.$volToggle.classList.remove("muted"),void this._emit("lc-unmute",{...i,volume:t})}if(this._player?.isMuted&&this._player.isMuted()&&!this._userMuted){this._player?.unMute?.();const t=this.$vol&&parseInt(this.$vol.value,10)||100;this._player?.setVolume?.(t),this.$volToggle&&this.$volToggle.classList.remove("muted"),this._emit("lc-unmute",{...i,volume:t})}}catch(t){}}_handleUserPlayToggle(){this._userInteracted=!0;const t=this._player?.getPlayerState?.(),e="undefined"!=typeof YT&&YT.PlayerState||{},i=![e.PLAYING,e.BUFFERING].filter(t=>"number"==typeof t).includes(t),s=this._player&&"function"==typeof this._player.playVideo&&"function"==typeof this._player.pauseVideo,r=i?"play":"pause";if(this._emit("lc-playback-request",{action:r}),"play"===r?this._emit("lc-play-request",{}):this._emit("lc-pause-request",{}),this._ensureAudioOnUserPlay(i,{source:"toggle"}),s)try{i?this._player.playVideo():this._player.pauseVideo()}catch(t){}else this._pendingPlayIntent=i?"play":"pause"}_flushPendingPlayback(){if(this._player){if(this._pendingForceUnmute&&(this._ensureAudioOnUserPlay(!0,{source:"pending-playback"}),this._pendingForceUnmute=!1),"play"===this._pendingPlayIntent)try{this._player.playVideo()}catch(t){}else if("pause"===this._pendingPlayIntent)try{this._player.pauseVideo()}catch(t){}this._pendingPlayIntent=null}}_eventToProgressFraction(t){if(!this.$progress||"number"!=typeof t)return null;const e=this.$progress.getBoundingClientRect();if(!e||e.width<=0)return null;const i=(t-e.left)/e.width;return Number.isFinite(i)?Math.min(1,Math.max(0,i)):null}_computeSeekTargetFromFraction(t){if(!Number.isFinite(t))return null;const e=Math.min(1,Math.max(0,t));if(this._isLive&&this._hasLiveDVR()){const{start:t,end:i}=this._getDvrRange(),s=Math.max(0,i-t);return{target:s>0?t+e*s:i,start:t,end:i,span:s,live:!0}}let i=Number(this._player?.getDuration?.()||0);if((!i||i<=0)&&(i=Number(this._duration||0)),(!i||i<=0)&&(i=Number(this._player?.getCurrentTime?.()||0)),!i||i<=0)return null;const s=Math.max(i,0);return{target:e*s,start:0,end:s,span:s,live:!1}}_previewSeekFraction(t){const e=this._computeSeekTargetFromFraction(t);return e?(this._seekPreview=e.target,this._immediateUI(e.target),e):null}_commitSeekFraction(t,{autoPlay:e=!0}={}){const i=this._computeSeekTargetFromFraction(t);if(i&&this._player){this._seekPreview=null;try{this._player.seekTo(i.target,!0)}catch(t){}if(this._userInteracted=!0,e&&this._ensureAudioOnUserPlay(!1,{source:"seek"}),e)try{this._player.playVideo()}catch(t){}this._immediateUI(i.target),this._emit("lc-seek",{position:i.target,live:i.live,autoPlay:e})}}_seekToLiveEdge(t=!1,{autoPlay:e=!0}={}){if(!this._player||!this._isLive)return!1;const i=t||this._pendingForceUnmute;if(!this._hasLiveDVR()){if(i&&e?this._ensureAudioOnUserPlay(!0,{source:"live-edge"}):e&&this._ensureAudioOnUserPlay(!1,{source:"live-edge"}),e)try{this._player.playVideo()}catch(t){}return this._awaitingLiveEdge=!1,i&&(this._pendingForceUnmute=!1),this._emit("lc-live-edge",{target:null,dvr:!1,autoPlay:e,forced:!!t}),!0}const{start:s,end:r}=this._getDvrRange();if(!(r>0&&r>=s))return this._awaitingLiveEdge=!0,i&&e&&(this._pendingForceUnmute=!0),!1;const n=Math.max(0,r-s),a=Math.max(.5,Math.min(2,.01*n||.75)),l=Math.max(s,r-a);try{this._player.seekTo(l,!0)}catch(t){}if(i&&e?this._ensureAudioOnUserPlay(!0,{source:"live-edge"}):e&&this._ensureAudioOnUserPlay(!1,{source:"live-edge"}),e)try{this._player.playVideo()}catch(t){}return this._immediateUI(l),this._awaitingLiveEdge=!1,i&&(this._pendingForceUnmute=!1),this._emit("lc-live-edge",{target:l,dvr:!0,autoPlay:e,forced:!!t}),!0}_maybeAutoLiveEdge(){if(!this._awaitingLiveEdge)return;if(!this._isLive)return void(this._awaitingLiveEdge=!1);if(!this._hasLiveDVR())return void(this._awaitingLiveEdge=!1);const{end:t,start:e}=this._getDvrRange();if(t>0&&t>=e){let t=!!this._autoplay;try{const e=this._player?.getPlayerState?.(),i="undefined"!=typeof YT&&YT.PlayerState||{};e!==i.PLAYING&&e!==i.BUFFERING||(t=!0)}catch(t){}this._seekToLiveEdge(!1,{autoPlay:t})}}_getCurrentVideoId(){try{const t=this._player?.getVideoData?this._player.getVideoData():null;if(t&&t.video_id)return t.video_id}catch(t){}return this._video}_buildThumbnailUrl(t){return t?`https://i.ytimg.com/vi/${t}/hqdefault.jpg`:""}_setOverlayThumbnail(t){if(!this.$overlay)return;const e=this._colors?.overlay||this._defaultColors.overlay;if(!t)return this.$overlay.style.backgroundImage="",void(this.$overlay.style.backgroundColor="");const i=this._getCurrentVideoId();if(!i)return this.$overlay.style.backgroundImage="",void(this.$overlay.style.backgroundColor=e);const s=this._buildThumbnailUrl(i);this.$overlay.style.backgroundImage=`linear-gradient(${e}, ${e}), url('${s}')`,this.$overlay.style.backgroundColor=""}_applyColorVars(){if(!this.$wrap)return;const t=this._colors.surface||this._defaultColors.surface,e=this._colors.text||this._defaultColors.text,i=this._colors.accent||this._defaultColors.accent,s=this._colors.subtle||this._defaultColors.subtle,r=this._colors.overlay||this._defaultColors.overlay,n={"--lc-bg":t,"--lc-overlay-base":r,"--lc-controls-bg":/gradient/i.test(s)?s:`linear-gradient(to top, ${s}, transparent)`,"--lc-btn-bg":s,"--lc-btn-bg-hover":s,"--lc-text":e,"--lc-progress-bg":s,"--lc-progress-fill":i,"--lc-live-bg":i,"--lc-live-bg-hover":i,"--lc-muted-color":i,"--lc-hint-bg":r,"--lc-accent":i};try{if(Object.entries(n).forEach(([t,e])=>{null!=e&&""!==e?this.$wrap.style.setProperty(t,e):this.$wrap.style.removeProperty(t)}),this.$overlay){const t=!this.$overlay.classList.contains("playing");this._setOverlayThumbnail(t)}}catch(t){}}_syncColorAttributes(){this._colorAttrMap&&Object.entries(this._colorAttrMap).forEach(([t,e])=>{const i=this.getAttribute(t);null!=i&&""!==String(i).trim()?this._colors[e]=String(i).trim():this._colors[e]=this._defaultColors[e]})}_updateColorAttribute(t,e){if(!this._colorAttrMap)return;const i=this._colorAttrMap[t];if(!i)return;const s=null==e?"":String(e).trim();this._colors[i]=s||this._defaultColors[i],this._isConnected&&this._applyColorVars()}_getFullscreenElement(){return"undefined"==typeof document?null:document.fullscreenElement||document.webkitFullscreenElement||document.mozFullScreenElement||document.msFullscreenElement||null}_requestFullscreen(t){if(!t)return;const e=t.requestFullscreen||t.webkitRequestFullscreen||t.msRequestFullscreen||t.mozRequestFullScreen;if(e)try{e.call(t)}catch(t){}}_exitFullscreen(){if("undefined"==typeof document)return;const t=document.exitFullscreen||document.webkitExitFullscreen||document.msExitFullscreen||document.mozCancelFullScreen;if(t)try{t.call(document)}catch(t){}}_toggleFullscreen(t){const e=!!this._getFullscreenElement();if(!0===t){e||this._requestFullscreen(this.$wrap);const t=!!this._getFullscreenElement();return void(t!==e&&this._emit("lc-fullscreen-change",{fullscreen:t}))}if(!1===t){e&&this._exitFullscreen();const t=!!this._getFullscreenElement();return void(t!==e&&this._emit("lc-fullscreen-change",{fullscreen:t}))}e?this._exitFullscreen():this._requestFullscreen(this.$wrap);const i=!!this._getFullscreenElement();i!==e&&this._emit("lc-fullscreen-change",{fullscreen:i})}_bindUI(){this._controlsTimer=null;const t=()=>{this.$wrap.classList.add("show-controls"),clearTimeout(this._controlsTimer),this._controlsTimer=setTimeout(()=>this.$wrap.classList.remove("show-controls"),2e3)};this._blinkControls=t,["mousemove","touchstart"].forEach(e=>this.$wrap.addEventListener(e,t)),this.$soundHint&&(this._autoplay?this.$soundHint.classList.remove("hide"):this.$soundHint.classList.add("hide"));const e=()=>{let t=this.$soundHint&&!this.$soundHint.classList.contains("hide");if(!t&&this._autoplay)try{t=!!(this._player?.isMuted&&this._player.isMuted())||this._userMuted}catch(t){}if(!t)return!1;this._ensureAudioOnUserPlay(!0,{source:"hint"}),this.$soundHint&&this.$soundHint.classList.add("hide"),this._userMuted=!1,this._userInteracted=!0;const e=this.$vol?parseInt(this.$vol.value,10)||0:"function"==typeof this._player?.getVolume&&this._player.getVolume()||0;return this._emit("lc-volume-change",{volume:e,muted:!1,source:"hint"}),!0};this._autoplay&&this.$soundHint&&this.$soundHint.addEventListener("click",t=>{t.stopPropagation();if(e()){const t="undefined"!=typeof YT&&YT.PlayerState||{},e=this._player?.getPlayerState?.();e!==t.PLAYING&&this._handleUserPlayToggle()}});const i=()=>{this._handleUserPlayToggle(),this.$soundHint&&this.$soundHint.classList.add("hide")};this.$overlay.addEventListener("click",()=>{if(e())try{const t="undefined"!=typeof YT&&YT.PlayerState||{},e=this._player?.getPlayerState?.();if(e===t.PLAYING||e===t.BUFFERING)return}catch(t){}i()}),this.$overlay.addEventListener("keydown",t=>{"Space"!==t.code&&" "!==t.key||(t.preventDefault(),this.$overlay.click())}),this.$playBtn.addEventListener("click",()=>{i()}),this.$goLive&&this.$goLive.addEventListener("click",t=>{t.stopPropagation(),this._userInteracted=!0;const e=this._seekToLiveEdge(!0,{autoPlay:!0});e||(this._awaitingLiveEdge=!0),this._emit("lc-go-live",{success:e})});const s=()=>this._isLive&&!this._hasLiveDVR()||this.$progress.classList.contains("disabled"),r=t=>{if(s())return;const e=this._eventToProgressFraction(t);null!=e&&this._commitSeekFraction(e)},n=t=>{if(s())return;const e=this._eventToProgressFraction(t);null!=e&&this._previewSeekFraction(e)};this.$progress.addEventListener("click",t=>{this._ignoreSeekClick?this._ignoreSeekClick=!1:r(t.clientX)});if("undefined"!=typeof window&&window.PointerEvent){this.$progress.addEventListener("pointerdown",t=>{if(("mouse"!==t.pointerType||0===t.button)&&!s()){this._ignoreSeekClick=!0,this._isSeeking=!0,this._seekPointerId=t.pointerId;try{this.$progress.setPointerCapture?.(t.pointerId)}catch(t){}n(t.clientX),t.preventDefault()}}),this.$progress.addEventListener("pointermove",t=>{!this._isSeeking||null!==this._seekPointerId&&t.pointerId!==this._seekPointerId||(n(t.clientX),t.preventDefault())});const t=t=>{if(this._isSeeking&&(null===this._seekPointerId||t.pointerId===this._seekPointerId)){r(t.clientX),this._isSeeking=!1,this._seekPointerId=null;try{this.$progress.releasePointerCapture?.(t.pointerId)}catch(t){}setTimeout(()=>{this._ignoreSeekClick=!1},0),t.preventDefault()}};this.$progress.addEventListener("pointerup",t),this.$progress.addEventListener("pointercancel",t=>{if(this._isSeeking&&(null===this._seekPointerId||t.pointerId===this._seekPointerId)){this._isSeeking=!1,this._seekPointerId=null,this._seekPreview=null;try{this.$progress.releasePointerCapture?.(t.pointerId)}catch(t){}this._updateUI(),this._ignoreSeekClick=!1}})}else{const t=(t,e=!1)=>{s()||(e?r(t):n(t))};this.$progress.addEventListener("touchstart",e=>{e.changedTouches&&0!==e.changedTouches.length&&(this._isSeeking=!0,this._ignoreSeekClick=!0,t(e.changedTouches[0].clientX,!1))},{passive:!1}),this.$progress.addEventListener("touchmove",e=>{this._isSeeking&&e.changedTouches&&0!==e.changedTouches.length&&(t(e.changedTouches[0].clientX,!1),e.preventDefault())},{passive:!1});const e=e=>{this._isSeeking&&e.changedTouches&&0!==e.changedTouches.length&&(t(e.changedTouches[0].clientX,!0),this._isSeeking=!1,this._seekPreview=null,setTimeout(()=>{this._ignoreSeekClick=!1},0))};this.$progress.addEventListener("touchend",e),this.$progress.addEventListener("touchcancel",()=>{this._isSeeking=!1,this._seekPreview=null,this._updateUI(),this._ignoreSeekClick=!1})}if(this.$overlay.addEventListener("dblclick",e=>{e.preventDefault(),this._toggleFullscreen(),t()}),this.$overlay.addEventListener("touchend",e=>{const s=Date.now(),r=s-this._lastTap,n=r>0&&r<300;if(this._lastTap=s,n){if(this._isLive&&!this._hasLiveDVR())return void e.preventDefault();const i=e.changedTouches[0],s=this.$overlay.getBoundingClientRect(),r=i.clientX-s.left,n=s.width/2;let a=this._player?.getCurrentTime?.()||0;if(this._isLive&&this._hasLiveDVR()){const{start:t,end:e}=this._getDvrRange();a=Math.min(Math.max(a,t),e)}const l=r<n?-10:10;let o=Math.max(0,a+l);if(this._isLive&&this._hasLiveDVR()){const{start:t,end:e}=this._getDvrRange();o=Math.min(Math.max(o,t),e);try{this._userInteracted=!0}catch(t){}}try{this._player?.seekTo?.(o,!0)}catch(t){}this._ensureAudioOnUserPlay(!1,{source:"double-tap"});try{this._player?.playVideo?.()}catch(t){}return this._immediateUI(o),this._emit("lc-skip",{delta:l,target:o}),t(),void e.preventDefault()}if(this._isLive&&!this._hasLiveDVR())return e.preventDefault(),void i();e.preventDefault(),i(),t()}),this.$vol.addEventListener("input",()=>{const t=parseInt(this.$vol.value,10);if(this._player.setVolume(t),this._userMuted=0===t,this.$volToggle&&this.$volToggle.classList.toggle("muted",this._userMuted),0===t)try{this._player.mute&&this._player.mute()}catch(t){}else try{this._player.unMute&&this._player.unMute()}catch(t){}const e={volume:t,muted:this._userMuted,source:"slider"};this._emit("lc-volume-change",e),this._userMuted?this._emit("lc-mute",e):this._emit("lc-unmute",e)}),this.$volToggle&&this.$volToggle.addEventListener("click",()=>{try{const t="function"==typeof this._player?.getVolume?this._player.getVolume():100;this._player?.isMuted&&this._player.isMuted()||0===t||this.$vol&&0===parseInt(this.$vol.value,10)?(this._player?.unMute?.(),this._player?.setVolume?.(100),this.$vol&&(this.$vol.value=100),this._userMuted=!1):(this._player?.setVolume?.(0),this._player?.mute?.(),this.$vol&&(this.$vol.value=0),this._userMuted=!0),this.$volToggle&&this.$volToggle.classList.toggle("muted",this._userMuted),this._userInteracted=!0;const e={volume:this.$vol?parseInt(this.$vol.value,10):this._userMuted?0:100,muted:this._userMuted,source:"toggle"};this._emit("lc-volume-change",e),this._userMuted?this._emit("lc-mute",e):this._emit("lc-unmute",e)}catch(t){}}),this.$fs.addEventListener("click",()=>{this._toggleFullscreen()}),"undefined"!=typeof document&&(this._handleKeydown=t=>{"Escape"!==t.key&&"Escape"!==t.code||this._getFullscreenElement()&&this._exitFullscreen()},document.addEventListener("keydown",this._handleKeydown)),this.$wrap.addEventListener("contextmenu",t=>t.preventDefault()),this.$prev&&this.$next){const t=this._playlist?"inline-flex":"none";this.$prev.style.display=t,this.$next.style.display=t}this.$prev&&this.$prev.addEventListener("click",()=>{if(!this.$prev.disabled)try{this._player.previousVideo()}catch(t){}}),this.$next&&this.$next.addEventListener("click",()=>{if(!this.$next.disabled)try{this._player.nextVideo()}catch(t){}})}async _mountPlayer(){if(!this._video&&!this._playlist)return;await window.__ytApiReadyPromise;const t={playerVars:{controls:0,modestbranding:1,rel:0,disablekb:1,fs:0,playsinline:1,iv_load_policy:3,autoplay:this._autoplay?1:0}};this._playlist||(t.videoId=this._video),this._player=new YT.Player(this.$player,{...t,events:{onReady:t=>this._onReady(t),onStateChange:t=>this._onStateChange(t),onError:t=>this._onError(t)}})}_onReady(t){if(t&&t.target&&(this._player=t.target),this._player&&"function"==typeof this._player.setVolume&&this._player.setVolume(parseInt(this.$vol.value,10)),this._userMuted=this._autoplay||0===parseInt(this.$vol.value,10),this.$volToggle&&this.$volToggle.classList.toggle("muted",this._userMuted),this._autoplay)try{this._player&&"function"==typeof this._player.mute&&this._player.mute()}catch(t){}if(this._duration=this._player&&"function"==typeof this._player.getDuration&&this._player.getDuration()||0,this._playlist)try{const t={listType:"playlist",list:this._playlist,index:this._index};this._autoplay?this._player.loadPlaylist(t):this._player.cuePlaylist(t)}catch(t){}else if(this._autoplay)try{this._player&&"function"==typeof this._player.playVideo&&this._player.playVideo()}catch(t){}if(this._flushPendingPlayback(),this._updateUI(),this._setLiveUI(this._detectLive()),this._setOverlayThumbnail(!0),this._isLive){const t=!!this._autoplay;this._seekToLiveEdge(!1,{autoPlay:t})}if(this._startAtLast&&this._playlist&&"function"==typeof this._player.getPlaylist){const t=()=>{const e=this._player.getPlaylist();if(Array.isArray(e)&&e.length>0){const t=e.length-1;try{this._player.playVideoAt(t)}catch(t){}this._startAtLast=!1}else setTimeout(t,100)};t()}this._updatePlaylistNav(),this._emit("lc-ready",{videoId:this._getCurrentVideoId(),playlist:this._playlist||null})}_onStateChange(t){const e=t?.data;if(this._setLiveUI(this._detectLive()),this._updateUI(),e===YT.PlayerState.CUED&&this._autoplay)try{this._player&&"function"==typeof this._player.playVideo&&this._player.playVideo()}catch(t){}e===YT.PlayerState.PLAYING?(this.$overlay.classList.add("playing"),this._setOverlayThumbnail(!1),this._startTimer(),this._blinkControls(),this.$playBtn.textContent="❚❚",this._emit("lc-play",{state:e,currentTime:this._player?.getCurrentTime?.()||0})):e===YT.PlayerState.PAUSED?(this.$overlay.classList.remove("playing"),this._setOverlayThumbnail(!0),this._stopTimer(),this._blinkControls(),this.$playBtn.textContent="▶︎",this._emit("lc-pause",{state:e,currentTime:this._player?.getCurrentTime?.()||0})):e===YT.PlayerState.ENDED?(this.$overlay.classList.remove("playing"),this._setOverlayThumbnail(!0),this._stopTimer(),this._blinkControls(),this.$playBtn.textContent="▶︎",this._emit("lc-ended",{state:e,currentTime:this._player?.getCurrentTime?.()||0})):e===YT.PlayerState.BUFFERING?this._emit("lc-buffering",{state:e}):e===YT.PlayerState.CUED&&this._emit("lc-cued",{state:e}),this._emit("lc-state-change",{state:e}),this._updatePlaylistNav()}_onError(t){this._showError();const e=t?.data;this._emit("lc-error",{code:e})}_updateUI(){if(!this._player||"function"!=typeof this._player.getCurrentTime)return;if(this._maybeAutoLiveEdge(),this._isSeeking&&null!==this._seekPreview)return;const t=this._detectLive(),e=!("undefined"==typeof window||!window.matchMedia)&&(window.matchMedia("(max-width: 600px)").matches||window.matchMedia("(hover: none)").matches);this._setLiveUI(t);const i=this._player.getCurrentTime()||0;if(this._lastDuration=this._duration,this._duration=this._player.getDuration()||this._duration||0,t&&this._duration<i&&(this._duration=i),t&&!this._hasLiveDVR()&&(this.$bar&&(this.$bar.style.width="100%"),this.$seek&&(this.$seek.style.left="100%"),this.$time&&(this.$time.textContent=e?this._fmt(i):"EN VIVO"),this.$goLive&&(this.$goLive.style.display="none"),!e))return;let s=0;if(t)if(this._hasLiveDVR()){const{start:t,end:e}=this._getDvrRange(),r=Math.max(1,e-t);s=(Math.min(Math.max(i,t),e)-t)/r}else s=1;else this._duration&&0!==this._duration||(this._duration=Math.max(this._duration,i)),s=this._duration?i/this._duration:0;if(this.$goLive)if(t&&this._hasLiveDVR()){const{end:t}=this._getDvrRange(),e=Math.max(0,t-i);e>1?this.$goLive.style.display="inline-flex":e<=.5&&(this.$goLive.style.display="none")}else this.$goLive.style.display="none";this.$bar.style.width=100*s+"%",this.$seek.style.left=100*s+"%",this.$time.textContent=e?this._fmt(i):t?`${this._fmt(i)} / EN VIVO`:`${this._fmt(i)} / ${this._fmt(this._duration)}`}_immediateUI(t){if(this._isLive&&!this._hasLiveDVR())return;const e=!("undefined"==typeof window||!window.matchMedia)&&(window.matchMedia("(max-width: 600px)").matches||window.matchMedia("(hover: none)").matches);let i=0;if(this._isLive&&this._hasLiveDVR()){const{start:e,end:s}=this._getDvrRange(),r=Math.max(1,s-e);i=(Math.min(Math.max(t,e),s)-e)/r}else!this._isLive||this._duration&&0!==this._duration||(this._duration=Math.max(this._duration,t||0)),i=this._duration?t/this._duration:0;this.$bar.style.width=100*i+"%",this.$seek.style.left=100*i+"%",e?this.$time.textContent=this._fmt(this._player?.getCurrentTime?.()||t||0):this._isLive?this.$time.textContent=`${this._fmt(this._player?.getCurrentTime?.()||t)} / EN VIVO`:this.$time.textContent=`${this._fmt(t)} / ${this._fmt(this._duration)}`,setTimeout(()=>this._updateUI(),120)}_startTimer(){this._timer||(this._timer=setInterval(()=>this._updateUI(),250))}_stopTimer(){clearInterval(this._timer),this._timer=null}_fmt(t){const e=(t=Math.max(0,Math.floor(t||0)))%60,i=Math.floor(t/60),s=i%60,r=Math.floor(i/60);if(0===r)return`${String(i).padStart(2,"0")}:${String(e).padStart(2,"0")}`;const n=Math.floor(r/24),a=r%24;return 0===n?`${r}:${String(s).padStart(2,"0")}:${String(e).padStart(2,"0")}`:`${n}d ${String(a).padStart(2,"0")}:${String(s).padStart(2,"0")}:${String(e).padStart(2,"0")}`}_teardown(){try{"undefined"!=typeof document&&this._handleKeydown&&(document.removeEventListener("keydown",this._handleKeydown),this._handleKeydown=null),this._stopTimer(),this._player&&this._player.destroy&&this._player.destroy()}catch(t){}}_updatePlaylistNav(){if(!this._player||!this._playlist)return this.$prev&&(this.$prev.disabled=!0),void(this.$next&&(this.$next.disabled=!0));let t=0,e=0;try{if("function"==typeof this._player.getPlaylistIndex&&(t=this._player.getPlaylistIndex()),"function"==typeof this._player.getPlaylist){const t=this._player.getPlaylist();Array.isArray(t)&&(e=t.length)}}catch(t){}this.$prev&&(this.$prev.disabled=t<=0),this.$next&&(this.$next.disabled=0===e||t>=e-1)}}customElements.define("lc-youtube",LCYouTube);