(function(){ 'use strict'; const CFG={title:'Episodes Grid',styleId:'jf-imdb-episodes-grid-style-v8',root:'[data-jf-ieg-root="1"]',watchDogMs:800,maxWaitMs:12000,readyAnchorWaitMs:2200,reapplyDelayMs:250,ttl:86400000}; const INV_KEY='jf-imdb-episodes-grid-inverted-v1'; const HOVER_STYLE_ID='jf-hover-tooltip-style'; const HOVER_TOOLTIP_ID='jf-hover-tooltip'; let scheduled=null,burst=[],runSeq=0,lastItemId=''; let hoverTimer=null,hoverCard=null,hoverX=0,hoverY=0; const sleep=ms=>new Promise(r=>setTimeout(r,ms)); const q=(s,r=document)=>r.querySelector(s); const qa=(s,r=document)=>Array.from(r.querySelectorAll(s)); const cacheGet=k=>{try{const o=JSON.parse(sessionStorage.getItem(k)||'null');if(!o||Date.now()>o.e)return null;return o.v;}catch{return null;}}; const cacheSet=(k,v,ttl=CFG.ttl)=>{try{sessionStorage.setItem(k,JSON.stringify({v,e:Date.now()+ttl}));}catch{}}; const toRating=v=>{const n=typeof v==='string'?parseFloat(v):Number(v);return Number.isFinite(n)&&n>0&&n<=10?n:null;}; const getInv=()=>{try{return localStorage.getItem(INV_KEY)==='true';}catch{return false;}}; const setInv=v=>{try{localStorage.setItem(INV_KEY,v?'true':'false');}catch{}}; const scheduleRun=d=>{if(scheduled)clearTimeout(scheduled);scheduled=setTimeout(()=>{scheduled=null;run();},typeof d==='number'?d:0);}; const scheduleBurst=arr=>{burst.forEach(clearTimeout);burst=[];(arr||[0]).forEach(d=>burst.push(setTimeout(run,d||0)));}; const token=()=>{try{const o=JSON.parse(localStorage.getItem('jellyfin_credentials')||'null');const ss=o&&o.Servers||[];for(const s of ss)if(s&&s.AccessToken)return s.AccessToken;}catch{}return null;}; const api=async path=>{const t=token();if(!t)throw new Error('no token');const r=await fetch(location.origin+path,{headers:{'X-Emby-Token':t}});if(!r.ok)throw new Error('HTTP '+r.status);return r.json();}; const itemIdFromUrl=()=>{const u=new URL(location.href);return u.searchParams.get('id')||(((u.hash||'').match(/[?&]id=([^&]+)/)||[])[1]?decodeURIComponent(((u.hash||'').match(/[?&]id=([^&]+)/)||[])[1]):null);}; const serverIdFromUrl=()=>{const u=new URL(location.href);return u.searchParams.get('serverId')||(((u.hash||'').match(/[?&]serverId=([^&]+)/)||[])[1]?decodeURIComponent(((u.hash||'').match(/[?&]serverId=([^&]+)/)||[])[1]):'');}; const detailsHash=(id,sid)=>'/details?id='+encodeURIComponent(id)+'&serverId='+encodeURIComponent(sid); const webRoot=()=>{const m=String(location.pathname||'').match(/^(.*\/web\/)(?:index\.html)?$/i);return m?m[1]:'/web/';}; const detailsUrl=(id,sid)=>location.origin+webRoot()+'#'+detailsHash(id,sid); const visible=el=>{if(!el||!el.isConnected)return false;const cs=getComputedStyle(el),r=el.getBoundingClientRect();return cs.display!=='none'&&cs.visibility!=='hidden'&&cs.opacity!=='0'&&r.width>2&&r.height>2;}; const best=els=>{let b=null,a=0;for(const el of els){if(!visible(el))continue;const r=el.getBoundingClientRect(),x=r.width*r.height;if(x>a){a=x;b=el;}}return b||els[els.length-1]||null;}; const isDetails=()=>{const h=String(location.hash||'');return h.includes('/details')&&(h.includes('id=')||new URL(location.href).searchParams.get('id'));}; async function fetchItem(id){const k='ieg_item_'+id,c=cacheGet(k);if(c)return c;const v=await api('/Items/'+encodeURIComponent(id));cacheSet(k,v);return v;} async function fetchJf(seriesId){ const k='ieg_jf_'+seriesId,c=cacheGet(k);if(c)return c; const [epsRes,ssRes]=await Promise.all([ api('/Shows/'+encodeURIComponent(seriesId)+'/Episodes?Fields=CommunityRating,IndexNumberEnd,ParentIndexNumber,IndexNumber,Name,PremiereDate&EnableImages=false&EnableUserData=false&Limit=20000').catch(()=>({Items:[]})), api('/Shows/'+encodeURIComponent(seriesId)+'/Seasons?Fields=IndexNumber&EnableImages=false&EnableUserData=false').catch(()=>({Items:[]})) ]); const seasonsByNum={},seasonIds={}; for(const s of (ssRes.Items||[])){const n=Number(s&&s.IndexNumber);if(Number.isFinite(n)&&n>0)seasonIds[n]=s.Id||'';} for(const ep of (epsRes.Items||[])){ const sn=Number(ep&&ep.ParentIndexNumber),en=Number(ep&&ep.IndexNumber); if(!Number.isFinite(sn)||!Number.isFinite(en)||sn<1||en<1)continue; const end=typeof ep.IndexNumberEnd==='number'&&ep.IndexNumberEnd>=en?ep.IndexNumberEnd:en; if(!seasonsByNum[sn])seasonsByNum[sn]=[]; seasonsByNum[sn].push({ep:en,epEnd:end,jfId:ep.Id||'',name:ep.Name||'',airDate:ep.PremiereDate||'',rating:toRating(ep&&ep.CommunityRating)}); } const val={seasonsByNum,seasonIds};cacheSet(k,val);return val; } function buildData(jf){ const nums=[0,...Object.keys(jf.seasonsByNum||{}).map(Number)],max=Math.max(...nums),out=[]; for(let s=1;s<=max;s++){ const byEp={},jfList=(jf.seasonsByNum[s]||[]); for(const j of jfList){ const start=Number(j.ep),end=Number(j.epEnd)>=start?Number(j.epEnd):start; for(let n=start;n<=end;n++){ if(!byEp[n])byEp[n]={ep:n,exists:true,rating:null,jfId:'',name:'',airDate:'',combined:end>start}; const x=byEp[n]; if(x.rating==null&&j.rating!=null)x.rating=j.rating; if(!x.jfId&&j.jfId)x.jfId=j.jfId; if(!x.name&&j.name)x.name=j.name; if(!x.airDate&&j.airDate)x.airDate=j.airDate; if(end>start)x.combined=true; } } const episodes=Object.values(byEp).sort((a,b)=>a.ep-b.ep); if(episodes.length)out.push({num:s,seasonJfId:jf.seasonIds[s]||'',episodes}); } return out; } function ratingStyle(r){ const v=toRating(r);if(v==null)return ''; let h=0,s=78,l=16; if(v>=9.5){h=92;s=100;l=51.5;} else if(v>=9.0){h=120;s=100;l=45;} else if(v>=8.0){h=84;s=98;l=30.6;} else if(v>=7.0){h=32;s=88;l=20;} else if(v>=6.0){h=18;s=90;l=19;} else if(v>=5.0){h=10;s=84;l=17;} else if(v>=4.0){h=6;s=82;l=15;} else if(v>=3.0){h=3;s=80;l=14;} else if(v>=2.0){h=1;s=78;l=13;} else{h=0;s=76;l=12;} const frac=v%1,lb=frac*(v>=7?7:3.8),light=Math.min(l+lb,v>=7?72:26),a=v>=7?(.82+frac*.12):(.92+frac*.04); let glow='inset 0 1px 0 rgba(255,255,255,.06)'; if(v>=9.8)glow='0 0 8px rgba(255,240,28,.24),0 0 16px rgba(255,240,28,.12),inset 0 1px 0 rgba(255,255,255,.12)'; else if(v>=9.7)glow='0 0 8px rgba(146,255,74,.28),0 0 16px rgba(146,255,74,.13),inset 0 1px 0 rgba(255,255,255,.11)'; else if(v>=9.6)glow='0 0 6px rgba(86,255,72,.22),0 0 12px rgba(86,255,72,.10),inset 0 1px 0 rgba(255,255,255,.10)'; else if(v>=9.0)glow='0 0 5px hsla('+h+','+s+'%,'+(light+8)+'%,.28),inset 0 1px 0 rgba(255,255,255,.08)'; return 'background:hsla('+h+','+s+'%,'+light+'%,'+a+');border-color:hsla('+h+','+s+'%,'+Math.min(light+(v>=7?18:10),v>=7?85:34)+'%,.30);color:#fff;text-shadow:0 1px 2px rgba(0,0,0,.70),0 0 1px rgba(0,0,0,.55);box-shadow:'+glow+';'; } function injectStyle(){ if(document.getElementById(CFG.styleId))return; const s=document.createElement('style');s.id=CFG.styleId;s.textContent=` ${CFG.root}{--c1:2.72rem;--c2:3.05rem;--rh:2.36rem;--axis:rgba(34,34,40,.84);--axis2:rgba(48,48,56,.90);margin:.85em 0 1.1em;position:relative;z-index:3;clear:both;width:100%;max-width:calc(100% - 3.15rem)} .jf-ieg-box{border-radius:12px;overflow:hidden;background:rgba(18,18,18,.26);border:1px solid rgba(255,255,255,.08)} .jf-ieg-toggle{width:100%;display:flex;align-items:center;gap:.42rem;border:0;margin:0;padding:.72rem .95rem;cursor:pointer;color:inherit;background:rgba(255,255,255,.03);text-align:left;font:inherit;outline:none !important;box-shadow:none !important;-webkit-tap-highlight-color:transparent}.jf-ieg-toggle:hover{background:rgba(255,255,255,.05)}.jf-ieg-toggle-label{font-size:1.05rem;font-weight:700;line-height:1.2}.jf-ieg-toggle-icon{transition:transform .16s ease;opacity:.92;flex:0 0 auto}.jf-ieg-toggle[aria-expanded="true"] .jf-ieg-toggle-icon{transform:rotate(180deg)} .jf-ieg-panel{border-top:1px solid rgba(255,255,255,.08);background:rgba(0,0,0,.09);overflow:hidden}.jf-ieg-panel[hidden]{display:none !important}.jf-ieg-body{padding:.72rem .82rem .82rem;background:rgba(0,0,0,.06);overflow:hidden}.jf-ieg-status{font-size:.92rem;opacity:.9;padding:.15rem .05rem}.jf-ieg-link{color:inherit !important;text-decoration:none !important;font-weight:700} .jf-ieg-scroll{overflow-x:auto;overflow-y:hidden;padding-bottom:.08rem;padding-right:.55rem;max-width:100%} .jf-ieg-grid{display:grid;column-gap:.26rem;row-gap:.26rem;align-items:stretch;min-width:max-content;grid-auto-rows:var(--rh)} .jf-ieg-cell{height:var(--rh);min-height:var(--rh);max-height:var(--rh);display:flex;align-items:center;justify-content:center;text-align:center;border-radius:8px;box-sizing:border-box;padding:.18rem .24rem;line-height:1;border:1px solid rgba(255,255,255,.08);background:rgba(255,255,255,.03);font-size:.86rem;position:relative;flex:0 0 auto} .jf-ieg-season,.jf-ieg-rating,.jf-ieg-empty,.jf-ieg-ghost{width:var(--c1);min-width:var(--c1);max-width:var(--c1)}.jf-ieg-episode{width:var(--c2);min-width:var(--c2);max-width:var(--c2)}.jf-ieg-episode-compact,.jf-ieg-corner-compact{width:var(--c1);min-width:var(--c1);max-width:var(--c1)}.jf-ieg-corner-wide{width:var(--c2);min-width:var(--c2);max-width:var(--c2)} .jf-ieg-corner{position:sticky;left:0;z-index:6;background:var(--axis) !important;border-color:rgba(255,255,255,.14) !important;box-shadow:inset 0 1px 0 rgba(255,255,255,.05)} .jf-ieg-corner-btn{cursor:pointer;outline:none !important;-webkit-tap-highlight-color:transparent;color:inherit}.jf-ieg-corner-btn:hover{background:var(--axis2) !important;border-color:rgba(255,255,255,.24) !important}.jf-ieg-corner-icon{width:1rem;height:1rem;display:block;opacity:.92;transition:transform .16s ease,opacity .16s ease;pointer-events:none;color:inherit} .jf-ieg-season,.jf-ieg-episode{font-weight:700;background:var(--axis) !important;font-size:.89rem;color:inherit !important;text-decoration:none !important;outline:none !important;box-shadow:inset 0 1px 0 rgba(255,255,255,.05) !important;border-color:rgba(255,255,255,.14) !important} .jf-ieg-season:hover,.jf-ieg-episode:hover{background:var(--axis2) !important;border-color:rgba(255,255,255,.24) !important} .jf-ieg-sticky-left{position:sticky;left:0;z-index:5;background:var(--axis) !important;border-color:rgba(255,255,255,.14) !important;box-shadow:inset 0 1px 0 rgba(255,255,255,.05) !important} .jf-ieg-axis-match{background:var(--axis2) !important;border-color:rgba(255,255,255,.30) !important;box-shadow:0 0 0 1px rgba(255,255,255,.04),inset 0 1px 0 rgba(255,255,255,.05) !important} .jf-ieg-rating{cursor:pointer;font-family:inherit !important;font-weight:700 !important;font-size:1.28rem;letter-spacing:-.01em;font-variant-numeric:tabular-nums;transition:filter .16s ease,border-color .16s ease,box-shadow .16s ease;text-decoration:none !important;outline:none !important;color:#fff !important;z-index:1}.jf-ieg-rating:hover{filter:brightness(1.36) saturate(1.42) contrast(1.12);border-color:rgba(255,255,255,.22)} .jf-ieg-rating-96{filter:brightness(1.26) saturate(1.34) contrast(1.10);text-shadow:0 1px 0 rgba(0,0,0,.88),0 0 1px rgba(0,0,0,.58),0 0 5px rgba(86,255,72,.26),0 0 10px rgba(86,255,72,.11) !important} .jf-ieg-rating-97{filter:brightness(1.32) saturate(1.40) contrast(1.12);text-shadow:0 1px 0 rgba(0,0,0,.90),0 0 1px rgba(0,0,0,.60),0 0 6px rgba(146,255,74,.32),0 0 12px rgba(146,255,74,.15) !important} .jf-ieg-rating-98plus{filter:brightness(1.36) saturate(1.42) contrast(1.12);text-shadow:0 1px 0 rgba(0,0,0,.90),0 0 1px rgba(0,0,0,.60),0 0 6px rgba(255,240,28,.34),0 0 12px rgba(255,240,28,.18) !important} .jf-ieg-rating-96:hover{filter:brightness(1.34) saturate(1.40) contrast(1.12) !important;box-shadow:0 0 8px rgba(86,255,72,.30),0 0 16px rgba(86,255,72,.15),0 0 22px rgba(86,255,72,.08),inset 0 0 0 1px rgba(255,255,255,.05),inset 0 1px 0 rgba(255,255,255,.04) !important} .jf-ieg-rating-97:hover{filter:brightness(1.40) saturate(1.46) contrast(1.13) !important;box-shadow:0 0 10px rgba(146,255,74,.36),0 0 19px rgba(146,255,74,.20),0 0 26px rgba(146,255,74,.11),inset 0 0 0 1px rgba(255,255,255,.06),inset 0 1px 0 rgba(255,255,255,.05) !important} .jf-ieg-rating-98plus:hover{filter:brightness(1.40) saturate(1.46) contrast(1.13) !important;box-shadow:0 0 10px rgba(255,240,28,.38),0 0 19px rgba(255,240,28,.22),0 0 28px rgba(255,240,28,.12),inset 0 0 0 1px rgba(255,255,255,.06),inset 0 1px 0 rgba(255,255,255,.05) !important} .jf-ieg-empty{cursor:pointer;font-weight:800;font-size:1.02rem;color:rgba(255,255,255,.76) !important;text-decoration:none !important;outline:none !important;background:rgba(112,112,124,.18) !important;border-color:rgba(220,220,230,.12) !important;box-shadow:inset 0 1px 0 rgba(255,255,255,.04) !important}.jf-ieg-empty:hover{filter:brightness(1.10);border-color:rgba(255,255,255,.18) !important} .jf-ieg-ghost{opacity:0;pointer-events:none;background:transparent !important;border-color:transparent !important;box-shadow:none !important} @media (max-width:900px){${CFG.root}{max-width:calc(100% - .4rem)}.jf-ieg-body{padding:.72rem .42rem .82rem .42rem !important}.jf-ieg-scroll{padding-right:.12rem !important}} `;document.head.appendChild(s); } function ensureHoverStyle(){ if(document.getElementById(HOVER_STYLE_ID))return; const s=document.createElement('style');s.id=HOVER_STYLE_ID;s.textContent='#'+HOVER_TOOLTIP_ID+'{position:fixed;z-index:99999;background:rgba(15,15,15,.95);color:#fff;padding:16px;border-radius:10px;width:320px;box-shadow:0 12px 40px rgba(0,0,0,.8);border:1px solid rgba(255,255,255,.15);backdrop-filter:blur(12px);pointer-events:none;opacity:0;transition:opacity .2s ease-in-out;display:none;font-family:sans-serif}#'+HOVER_TOOLTIP_ID+'.visible{opacity:1;display:block}.jf-tooltip-title{font-size:1.15em;font-weight:800;margin:0 0 6px;color:#fff;line-height:1.2}.jf-tooltip-meta{font-size:.8em;color:#10b981;margin-bottom:10px;font-weight:700;text-transform:uppercase;letter-spacing:.5px}.jf-tooltip-overview{font-size:.85em;line-height:1.5;color:#d1d5db;display:-webkit-box;-webkit-line-clamp:6;-webkit-box-orient:vertical;overflow:hidden}';document.head.appendChild(s); } function getTooltip(){let t=document.getElementById(HOVER_TOOLTIP_ID);if(!t){t=document.createElement('div');t.id=HOVER_TOOLTIP_ID;document.body.appendChild(t);}return t;} function hideTooltip(){clearTimeout(hoverTimer);hoverTimer=null;hoverCard=null;const t=document.getElementById(HOVER_TOOLTIP_ID);if(t)t.classList.remove('visible');} function positionTooltip(t){let x=hoverX+15,y=hoverY+15;if(x+340>window.innerWidth)x=hoverX-335;if(y+200>window.innerHeight)y=hoverY-180;t.style.left=x+'px';t.style.top=y+'px';} async function fetchHoverItem(id){if(typeof ApiClient==='undefined'||!id)return null;const uid=ApiClient.getCurrentUserId&&ApiClient.getCurrentUserId();if(!uid)return null;try{return await ApiClient.getJSON(ApiClient.getUrl('Users/'+uid+'/Items/'+id));}catch{return null;}} function showTooltip(item){ const t=getTooltip(); const meta=[item.ProductionYear||'',item.CommunityRating?('⭐ '+item.CommunityRating.toFixed(1)):'',item.Genres&&item.Genres.slice?item.Genres.slice(0,3).join(','):''].filter(Boolean).join('|'); t.innerHTML='