104 lines
25 KiB
JavaScript
104 lines
25 KiB
JavaScript
import{a as b,s as w,i as C,K as $,h as f,c as P,d as T}from"./keyboard-nav-D6R5vIR2.js";const l={video:null,currentEpisode:1,currentServer:0,recommendations:[],isLoading:!0};window.state=l;let t={};function E(){var e,i;t={videoPlayer:document.getElementById("videoPlayer"),videoPlayerContainer:document.getElementById("videoPlayerContainer"),playerLoading:document.getElementById("playerLoading"),closePlayer:document.getElementById("closePlayer"),heroBg:document.getElementById("heroBg"),movieTitle:document.getElementById("movieTitleDesktop"),movieMatch:document.getElementById("movieMatchDesktop"),movieYear:document.getElementById("movieYearDesktop"),movieRating:document.getElementById("movieRatingDesktop"),movieQuality:document.getElementById("movieQualityDesktop"),movieDescription:document.getElementById("movieDescriptionDesktop"),movieTags:document.getElementById("movieTags"),movieTitleMobile:document.getElementById("movieTitleMobile"),movieMatchMobile:document.getElementById("movieMatchMobile"),movieYearMobile:document.getElementById("movieYearMobile"),movieRatingMobile:document.getElementById("movieRatingMobile"),movieDuration:document.getElementById("movieDurationDesktop"),movieDurationMobile:document.getElementById("movieDurationMobile"),movieQualityMobile:document.getElementById("movieQualityMobile"),movieDescriptionMobile:document.getElementById("movieDescriptionMobile"),playBtn:document.getElementById("playBtnDesktop"),addListBtn:document.getElementById("addListBtnDesktop"),addListIcon:(e=document.getElementById("addListBtnDesktop"))==null?void 0:e.querySelector(".material-symbols-outlined"),addListText:(i=document.getElementById("addListBtnDesktop"))==null?void 0:i.querySelector("span:last-child"),playBtnMobile:document.getElementById("playBtnMobile"),addListBtnMobile:document.getElementById("addListBtnMobile"),shareBtnMobile:document.getElementById("shareBtnMobile"),mobilePlayBtn:document.getElementById("mobilePlayBtn"),watchHeader:document.getElementById("watchHeader"),tabNav:document.getElementById("tabNav"),watchBackBtn:document.getElementById("watchBackBtn"),episodesPanel:document.getElementById("episodesPanel"),trailersPanel:document.getElementById("trailersPanel"),detailsPanel:document.getElementById("detailsPanel"),seasonSelect:document.getElementById("seasonSelect"),seasonSelectContainer:document.getElementById("seasonSelectContainer"),episodeCount:document.getElementById("episodeCount"),episodesGrid:document.getElementById("episodesGrid"),episodesLoading:document.getElementById("episodesLoading"),castCarousel:document.getElementById("castCarousel"),recommendationsContainer:document.getElementById("recommendationsContainer"),detailsList:document.getElementById("detailsList"),searchModal:document.getElementById("searchModal"),searchBtn:document.getElementById("searchBtn"),searchInput:document.getElementById("searchInput"),closeSearch:document.getElementById("closeSearch")}}async function L(){E(),new $().init();const i=new URLSearchParams(window.location.search),o=i.get("id"),n=i.get("slug"),a=parseInt(i.get("ep"))||1;if(l.currentEpisode=a,!o&&!n){k("No video specified");return}E(),_(),await S(o,n),await F()}function _(){window.addEventListener("scroll",()=>{t.watchHeader&&(window.scrollY>50?t.watchHeader.style.backgroundColor="rgba(20,20,20,0.95)":t.watchHeader.style.backgroundColor="transparent")}),t.watchBackBtn&&t.watchBackBtn.addEventListener("click",o=>{var a;o.preventDefault(),t.videoPlayerContainer&&(t.videoPlayerContainer.style.display!=="none"||!t.videoPlayerContainer.classList.contains("hidden"))?(f(),(a=window.history.state)!=null&&a.playerOpen?window.history.back():v()):document.referrer&&document.referrer.includes(window.location.host)?(f(),window.history.back()):window.location.href="/index.html"});const e=document.getElementById("playerBackButton");if(e&&e.addEventListener("click",()=>{var o;f(),(o=window.history.state)!=null&&o.playerOpen?window.history.back():v()}),window.addEventListener("popstate",o=>{var s;const n=t.videoPlayerContainer||document.getElementById("videoPlayerContainer");n&&!n.classList.contains("hidden")&&!((s=o.state)!=null&&s.playerOpen)&&v(!1)}),document.addEventListener("keydown",o=>{o.key==="Escape"&&(t.videoPlayerContainer&&!t.videoPlayerContainer.classList.contains("hidden")&&v(),t.searchModal&&!t.searchModal.classList.contains("hidden")&&t.searchModal.classList.add("hidden"))}),[t.playBtn,t.playBtnMobile,t.mobilePlayBtn].forEach(o=>{o&&o.addEventListener("click",()=>{o&&P(),t.videoPlayerContainer&&(t.videoPlayerContainer.classList.remove("hidden"),t.videoPlayerContainer.style.display="block"),t.videoPlayer&&(t.videoPlayer.style.display="block"),I()})}),t.closePlayer&&t.closePlayer.addEventListener("click",()=>{var o;(o=window.history.state)!=null&&o.playerOpen?window.history.back():v()}),t.searchBtn&&t.searchBtn.addEventListener("click",()=>{t.searchModal&&(t.searchModal.classList.remove("hidden"),setTimeout(()=>{var o;return(o=t.searchInput)==null?void 0:o.focus()},100))}),t.closeSearch&&t.closeSearch.addEventListener("click",()=>{t.searchModal&&t.searchModal.classList.add("hidden")}),[t.addListBtn,t.addListBtnMobile].forEach(o=>{o&&o.addEventListener("click",()=>{var a;if(!l.video)return;const n=(a=window.historyService)==null?void 0:a.toggleFavorite(l.video);x(n),f(),n?w("Added to My List","success"):w("Removed from My List","info")})}),t.shareBtnMobile&&t.shareBtnMobile.addEventListener("click",()=>{var o;navigator.share?(f(),navigator.share({title:((o=l.video)==null?void 0:o.title)||"StreamFlix",url:window.location.href})):(f(),navigator.clipboard.writeText(window.location.href),w("Link copied to clipboard","success"))}),t.tabNav){const o=t.tabNav.querySelectorAll(".tab-btn"),n={episodes:t.episodesPanel,details:t.detailsPanel};o.forEach(a=>{a.addEventListener("click",()=>{f();const s=a.dataset.tab;o.forEach(d=>{d.classList.remove("text-white","font-bold","border-b-4","border-primary"),d.classList.add("text-gray-400","font-medium")}),a.classList.remove("text-gray-400","font-medium"),a.classList.add("text-white","font-bold","border-b-4","border-primary"),Object.entries(n).forEach(([d,c])=>{c&&(d===s?c.classList.remove("hidden"):c.classList.add("hidden"))})})})}document.querySelectorAll("#mobileBottomNav .nav-item").forEach(o=>{o.addEventListener("click",n=>{n.preventDefault();const a=o.dataset.view;a&&(f(),window.location.href=`/index.html?view=${a}`)})})}function v(e=!0){var a;const i=t.videoPlayerContainer||document.getElementById("videoPlayerContainer"),o=t.videoPlayer||document.getElementById("videoPlayer"),n=t.playerLoading||document.getElementById("playerLoading");i&&(i.classList.add("hidden"),i.style.display="none"),T(),o&&(o.innerHTML="",o.style.display="none"),n&&(n.style.display="none"),e&&((a=window.history.state)!=null&&a.playerOpen)}function x(e){const i=e?"check":"add",o=e?"In List":"My List";if(t.addListBtn){const n=t.addListBtn.querySelector(".material-symbols-outlined"),a=t.addListBtn.querySelector("span:last-child");n&&(n.textContent=i),a&&(a.textContent=o),e?t.addListBtn.classList.add("bg-white/20"):t.addListBtn.classList.remove("bg-white/20")}if(t.addListBtnMobile){const n=t.addListBtnMobile.querySelector(".material-symbols-outlined"),a=t.addListBtnMobile.querySelector("span:last-child");n&&(n.textContent=i),a&&(a.textContent=o),e?(t.addListBtnMobile.classList.add("bg-white/10"),t.addListBtnMobile.classList.remove("bg-[#2b2b2b]")):(t.addListBtnMobile.classList.remove("bg-white/10"),t.addListBtnMobile.classList.add("bg-[#2b2b2b]"))}}async function S(e,i){var o,n,a,s,d,c;try{l.isLoading=!0;let m=null;const u=i||e;if(u)try{const p=await b.getRophimMovie(u);if(p){const r=p.movie||p,y=p.episodes||[];m={id:r.slug||u,slug:r.slug||u,title:r.name||r.title||u,original_title:r.origin_name||r.original_title||"",description:r.content||r.description||"",thumbnail:r.poster_url||r.thumb_url||r.thumbnail||"",year:r.year,rating:((o=r.tmdb)==null?void 0:o.vote_average)||r.rating||"N/A",quality:r.quality||"HD",duration:r.time||r.duration||"",genres:Array.isArray(r.category)?r.category.map(g=>g.name||g):Array.isArray(r.genres)?r.genres:typeof r.genre=="string"?r.genre.split(",").map(g=>g.trim()):[],country:((a=(n=r.country)==null?void 0:n[0])==null?void 0:a.name)||r.country||"",country:((d=(s=r.country)==null?void 0:s[0])==null?void 0:d.name)||r.country||"",cast:r.actor||r.cast||[],director:((c=r.director)==null?void 0:c[0])||r.director||"",source_url:`https://phimmoichill.network/phim/${u}`,episodes:D(y)}}}catch(p){console.warn("API fetch failed:",p)}if(!m)throw new Error("Video data not found");l.video=m,window.historyService&&window.historyService.addToHistory(m,{episode:l.currentEpisode}),H(m),window.historyService&&x(window.historyService.isFavorite(m.slug))}catch(m){console.error("Failed to load video:",m),k("Failed to load video data")}finally{l.isLoading=!1}}function D(e){if(!e||!Array.isArray(e)||e.length===0)return[];const i=e[0];return((i==null?void 0:i.server_data)||[]).map((n,a)=>({number:a+1,name:n.name||`Episode ${a+1}`,title:n.filename||`Episode ${a+1}`,slug:n.slug||"",link_embed:n.link_embed||"",link_m3u8:n.link_m3u8||""}))}function H(e){if(t.heroBg){const i=e.backdrop||e.poster_url||e.thumb_url||e.thumbnail||"";i&&(t.heroBg.style.backgroundImage=`url('${i}')`)}if(t.movieTitle&&(t.movieTitle.textContent=e.title),t.movieYear&&(t.movieYear.textContent=e.year||""),t.movieDuration)if(e.runtime_minutes){const i=Math.floor(e.runtime_minutes/60),o=e.runtime_minutes%60;t.movieDuration.textContent=i>0?`${i}h ${o}m`:`${o}m`}else e.duration&&(t.movieDuration.textContent=e.duration);if(t.movieQuality&&(t.movieQuality.textContent=e.quality||"HD"),t.movieRating){const i=e.rating||e.tmdb_rating;i&&i!=="N/A"?t.movieRating.textContent=typeof i=="number"?`${i.toFixed(1)} ★`:i:t.movieRating.textContent="TV-MA"}if(t.movieMatch){const i=Math.floor(85+Math.random()*14);t.movieMatch.textContent=`${i}% Match`}if(t.movieDescription){const i=e.tmdb_description||e.description||"No description available.";t.movieDescription.innerHTML=i,t.movieDescriptionMobile&&(t.movieDescriptionMobile.innerHTML=i)}if(t.movieTitleMobile&&(t.movieTitleMobile.textContent=e.title),t.movieYearMobile&&(t.movieYearMobile.textContent=e.year||""),t.movieRatingMobile){const i=e.rating||e.tmdb_rating;t.movieRatingMobile.textContent=i&&i!=="N/A"?typeof i=="number"?i.toFixed(1):i:"TV-MA"}if(t.movieDurationMobile&&(t.movieDurationMobile.textContent=t.movieDuration?t.movieDuration.textContent:e.duration||""),t.movieQualityMobile&&(t.movieQualityMobile.textContent=e.quality||"HD"),t.movieMatchMobile&&t.movieMatch&&(t.movieMatchMobile.textContent=t.movieMatch.textContent),t.movieTags){const i=e.genres||[],o=e.director,n=e.country;let a="";i.length>0&&(a+=`<div><span class="text-white/50">Genres:</span> <span class="text-white">${i.join(", ")}</span></div>`),o&&o!=="Unknown"&&(a+=`<div><span class="text-white/50">Director:</span> <span class="text-white">${o}</span></div>`),n&&n!=="Unknown"&&(a+=`<div><span class="text-white/50">Country:</span> <span class="text-white">${n}</span></div>`),t.movieTags.innerHTML=a}document.title=`${e.title} - StreamFlix`,window.historyService&&e.slug&&x(window.historyService.isFavorite(e.slug)),M(e),e.tmdb_cast&&e.tmdb_cast.length>0?B(e.tmdb_cast,!0):e.cast&&e.cast.length>0&&B(e.cast,!1),A(e)}function M(e){if(!t.episodesPanel)return;let i=[];if(Array.isArray(e.episodes)&&e.episodes.length>0&&(e.episodes[0].server_data?i=e.episodes[0].server_data:i=e.episodes),i.length<=1){t.seasonSelectContainer&&(t.seasonSelectContainer.style.display="none"),t.episodesLoading&&(t.episodesLoading.style.display="none"),t.episodesGrid&&(t.episodesGrid.innerHTML=`
|
|
<div class="flex items-center gap-4 p-4 bg-surface-dark rounded-lg border border-white/5">
|
|
<span class="material-symbols-outlined text-3xl text-primary">play_circle</span>
|
|
<div>
|
|
<p class="text-white font-medium">Full Movie</p>
|
|
<p class="text-gray-400 text-sm">Click Play to watch</p>
|
|
</div>
|
|
</div>
|
|
`);return}if(t.episodeCount&&(t.episodeCount.textContent=`${i.length} Episodes`),t.episodesLoading&&(t.episodesLoading.style.display="none"),t.episodesGrid){const n=i.length,a=n<=15,s=d=>{if(t.episodesGrid.innerHTML=i.slice(0,d).map((c,m)=>{const u=m+1,p=u===l.currentEpisode,r=c.name||`Episode ${u}`,y=c.title||c.filename||"";return`
|
|
<div class="flex items-start gap-4 p-4 rounded-lg cursor-pointer transition-colors hover:bg-surface-dark border border-transparent hover:border-white/10 ${p?"bg-surface-dark border-primary/40":""}" onclick="window.selectEpisode(${u})">
|
|
<div class="flex-none text-2xl font-bold text-gray-500 w-8 text-center">${u}</div>
|
|
<div class="flex-1">
|
|
<h4 class="text-white font-medium ${p?"text-primary":""}">${r}</h4>
|
|
${y?`<p class="text-gray-400 text-sm mt-1 line-clamp-2">${y}</p>`:""}
|
|
</div>
|
|
${p?'<span class="material-symbols-outlined text-primary">play_circle</span>':""}
|
|
</div>
|
|
`}).join(""),d<n){const c=document.createElement("button");c.className="w-full py-4 text-gray-400 hover:text-white font-medium flex items-center justify-center gap-2 border-t border-white/5 mt-2 transition-colors",c.innerHTML=`
|
|
<span>See more episodes (${n-d} remaining)</span>
|
|
<span class="material-symbols-outlined">expand_more</span>
|
|
`,c.onclick=()=>s(n),t.episodesGrid.appendChild(c)}};s(a?n:10)}}function A(e){if(!t.detailsList)return;const i=[];e.original_title&&i.push({label:"Original Title",value:e.original_title}),e.director&&e.director!=="Unknown"&&i.push({label:"Director",value:e.director}),e.country&&e.country!=="Unknown"&&i.push({label:"Country",value:e.country}),e.year&&i.push({label:"Release Year",value:e.year}),e.quality&&i.push({label:"Quality",value:e.quality}),e.duration&&i.push({label:"Duration",value:e.duration}),e.genres&&e.genres.length>0&&i.push({label:"Genres",value:e.genres.join(", ")}),t.detailsList.innerHTML="",i.forEach(o=>{const n=document.createElement("div");n.className="flex gap-4";const a=document.createElement("span");a.className="text-white/50 min-w-[100px] font-medium",a.textContent=`${o.label}:`;const s=document.createElement("span");s.className="text-white font-medium",s.textContent=o.value,n.appendChild(a),n.appendChild(s),t.detailsList.appendChild(n)})}window.selectEpisode=e=>{l.currentEpisode=e;const i=new URL(window.location);i.searchParams.set("ep",e),window.history.replaceState({},"",i),M(l.video),I(),window.scrollTo({top:0,behavior:"smooth"})};async function I(){if(l.video){t.playerLoading&&(t.playerLoading.style.display="flex");try{let e=null,i=l.video.thumbnail,o=[];Array.isArray(l.video.episodes)&&l.video.episodes.length>0&&(l.video.episodes[0].server_data?o=l.video.episodes[0].server_data:o=l.video.episodes);const n=o[l.currentEpisode-1];if(window.historyService&&window.historyService.addToHistory(l.video,{episode:l.currentEpisode,timestamp:Date.now()}),n&&(n.link_m3u8?e=n.link_m3u8:n.link_embed&&(e=n.link_embed)),!e&&l.video.slug)try{const a=await b.getRophimStream(l.video.slug,l.currentEpisode);a!=null&&a.stream_url&&(e=a.stream_url)}catch(a){console.warn("Stream API fallback also failed",a)}if(t.playerLoading&&(t.playerLoading.style.display="none"),e){j(e,i,l.video.title);const a=o.length>1?`Episode ${l.currentEpisode} `:"Movie";w(`Playing ${a} `,"success")}else{const a=l.currentEpisode===1?"full":l.currentEpisode,s=`https://phimmoichill.network/xem-phim/${l.video.slug}/tap-${a}-sv-0`;N(s)}}catch(e){console.error(e),q(e.message)}}}function N(e){t.videoPlayer.innerHTML=`
|
|
<div style="display:flex;height:100%;align-items:center;justify-content:center;flex-direction:column;gap:20px;padding:40px;text-align:center;">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="64" height="64" style="color:#ff4d4d; opacity: 0.8;">
|
|
<circle cx="12" cy="12" r="10"/>
|
|
<line x1="15" y1="9" x2="9" y2="15"/>
|
|
<line x1="9" y1="9" x2="15" y2="15"/>
|
|
</svg>
|
|
<h3 style="color:#fff;margin:0;font-size:1.5rem;">It cannot load</h3>
|
|
<p style="color:#aaa;margin:0;max-width:400px;opacity:0.7;">This stream is currently unavailable. Please try again later or choose another source.</p>
|
|
</div>
|
|
`}function j(e,i,o){var a;e.includes("embed")||!e.match(/\.(mp4|m3u8)$/i)?t.videoPlayer.innerHTML=`
|
|
<iframe src="${e}" allowfullscreen allow="autoplay; encrypted-media"></iframe>
|
|
`:(R(e,i,o),(a=window.history.state)!=null&&a.playerOpen||window.history.pushState({playerOpen:!0},"",window.location.href))}function R(e,i,o){const n=C(t.videoPlayer,{url:e,poster:i,title:o+` - Ep ${l.currentEpisode}`,autoplay:!0});if(n&&window.historyService){n.on("video:timeupdate",()=>{const d=n.currentTime,c=n.duration;d>0&&c>0&&Math.floor(d)%5===0&&window.historyService.addToHistory(l.video,{currentTime:d,duration:c,percentage:d/c*100,episode:l.currentEpisode})});const s=window.historyService.getHistory().find(d=>d.slug===l.video.slug);s&&s.progress&&s.progress.episode===l.currentEpisode&&s.progress.currentTime>0&&s.progress.percentage<95&&n.once("video:canplay",()=>{n.currentTime=s.progress.currentTime})}}function q(e){t.videoPlayer.innerHTML=`
|
|
<div class="video-theater__loading">
|
|
<p>Error loading video: ${e}</p>
|
|
<button class="action-btn action-btn--primary" onclick="location.reload()">Retry</button>
|
|
</div>
|
|
`}function B(e,i=!1){if(!t.castCarousel)return;const o=e.slice(0,10);i?t.castCarousel.innerHTML=o.map(n=>{const a=n.profile_photo&&!n.profile_photo.includes("ui-avatars.com"),s=n.profile_photo||"",d=`/?search=${encodeURIComponent(n.name)}`,c=n.name.split(" ").map(m=>m[0]).join("").toUpperCase().slice(0,2);return`
|
|
<a href="${d}" class="flex-none w-28 group text-center snap-start">
|
|
<div class="size-20 mx-auto rounded-full overflow-hidden bg-surface-dark border-2 border-transparent group-hover:border-primary transition-all">
|
|
${a?`<img src="${s}" alt="${n.name}" class="w-full h-full object-cover" loading="lazy">`:`<div class="w-full h-full flex items-center justify-center text-2xl font-bold text-gray-400">${c}</div>`}
|
|
</div>
|
|
<p class="text-white text-sm font-medium mt-2 truncate group-hover:text-primary transition-colors">${n.name}</p>
|
|
<p class="text-gray-400 text-xs truncate">${n.character||"Actor"}</p>
|
|
</a>
|
|
`}).join(""):t.castCarousel.innerHTML=o.map(n=>{const a=`/?search=${encodeURIComponent(n)}`,s=n.split(" ").map(d=>d[0]).join("").toUpperCase().slice(0,2);return`
|
|
<a href="${a}" class="flex-none w-28 group text-center snap-start">
|
|
<div class="size-20 mx-auto rounded-full overflow-hidden bg-surface-dark border-2 border-transparent group-hover:border-primary transition-all flex items-center justify-center">
|
|
<span class="text-2xl font-bold text-gray-400">${s}</span>
|
|
</div>
|
|
<p class="text-white text-sm font-medium mt-2 truncate group-hover:text-primary transition-colors">${n}</p>
|
|
<p class="text-gray-400 text-xs truncate">Actor</p>
|
|
</a>
|
|
`}).join("")}async function F(){const e=t.recommendationsContainer;if(e)try{e.innerHTML='<div class="flex justify-center py-12"><div class="loading-spinner"></div></div>';const i=l.video;if(!i)return;const o=i.slug,n=new Set([o]),a=i.category?Object.values(i.category):i.genres||[],s=i.country?Object.values(i.country):i.countries||[],d=i.year,c=[];if(a.length>0){let r="";typeof a[0]=="object"&&a[0].slug?r=a[0].slug:typeof a[0]=="string"&&(r=a[0].toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"").replace(/đ/g,"d").replace(/\s+/g,"-")),r&&c.push(b.getRophimCatalog({page:1,limit:24,category:`the-loai/${r}`}).then(y=>({title:"More Like This",movies:y.movies||[]})).catch(()=>null))}if(s.length>0){let r="";typeof s[0]=="object"&&s[0].slug?r=s[0].slug:typeof s[0]=="string"&&(r=s[0].toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"").replace(/đ/g,"d").replace(/\s+/g,"-")),r&&c.push(b.getRophimCatalog({page:1,limit:24,category:`quoc-gia/${r}`}).then(y=>({title:`Movies from ${s[0].name||s[0]}`,movies:y.movies||[]})).catch(()=>null))}d&&c.push(b.getRophimCatalog({page:1,limit:24,category:`nam-phat-hanh/${d}`}).then(r=>({title:`Released in ${d}`,movies:r.movies||[]})).catch(()=>null));const m=await Promise.all(c);e.innerHTML="";const u=new Set;let p=!1;m.forEach(r=>{if(!r||!r.movies||r.movies.length===0||r.title&&u.has(r.title))return;r.title&&u.add(r.title);const y=r.movies.filter(h=>!n.has(h.slug));if(y.forEach(h=>n.add(h.slug)),y.length===0)return;p=!0;const g=`
|
|
<div class="space-y-4">
|
|
${r.title?`<h4 class="text-md font-bold text-gray-300 pl-4 md:pl-0 border-l-2 border-primary md:border-none md:pl-0 leading-none h-5 flex items-center md:block">${r.title}</h4>`:""}
|
|
<div class="grid grid-cols-4 md:grid-cols-5 lg:grid-cols-6 xl:grid-cols-8 gap-3">
|
|
${y.map(h=>O(h)).join("")}
|
|
</div>
|
|
</div>
|
|
`;e.insertAdjacentHTML("beforeend",g)}),p||(e.innerHTML='<p class="text-gray-400 text-center py-8">No specific recommendations found.</p>')}catch(i){console.error("Failed to load recommendations:",i),e.innerHTML='<p class="text-gray-400 text-center py-8">Failed to load recommendations</p>'}}function O(e){const i=e.poster_url||e.thumbnail||e.thumb_url||"",o=e.name||e.title||"Untitled",n=e.year||"",a=e.quality||"HD",s=e.matchScore||Math.floor(Math.random()*15+85),d=e.tmdb_rating||0,c=Math.round(d*10);return`
|
|
<div class="flex-none w-full cursor-pointer group relative transition-all duration-300 hover:z-30 hover:scale-105" onclick="window.location.href='/watch.html?id=${e.slug||e.id||""}'">
|
|
<div class="relative aspect-[2/3] rounded-md overflow-hidden bg-surface-dark shadow-sm group-hover:shadow-lg ring-0 group-hover:ring-2 group-hover:ring-white/20">
|
|
<div class="absolute inset-0 bg-cover bg-center transition-transform duration-500 group-hover:scale-110" style="background-image: url('${i}');"></div>
|
|
|
|
<!-- Gradient Overlay -->
|
|
<div class="absolute inset-0 bg-gradient-to-t from-black/80 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
|
|
|
|
<!-- Badges -->
|
|
<div class="absolute top-2 left-2 flex flex-col gap-1 z-20">
|
|
${n==new Date().getFullYear()?'<span class="bg-primary text-white text-[8px] font-bold px-1.5 py-0.5 rounded shadow">NEW</span>':""}
|
|
<span class="bg-black/60 backdrop-blur-md text-white text-[8px] font-bold px-1.5 py-0.5 rounded border border-white/10 uppercase">${a.replace("FHD","HD")}</span>
|
|
</div>
|
|
|
|
<!-- Content Overlay -->
|
|
<div class="absolute inset-0 z-20 opacity-0 group-hover:opacity-100 transition-opacity duration-300 flex flex-col justify-end p-2 pointer-events-none">
|
|
<div class="flex items-center gap-1 mb-1 pointer-events-auto">
|
|
<button class="bg-white text-black size-6 rounded-full flex items-center justify-center hover:scale-110 transition-transform">
|
|
<span class="material-symbols-outlined text-[16px]">play_arrow</span>
|
|
</button>
|
|
<button class="bg-black/60 border border-gray-400 text-white size-6 rounded-full flex items-center justify-center hover:bg-zinc-700 transition-transform">
|
|
<span class="material-symbols-outlined text-[14px]">add</span>
|
|
</button>
|
|
</div>
|
|
|
|
<h3 class="text-xs font-bold text-white line-clamp-1 mb-0.5 drop-shadow-md">${o}</h3>
|
|
<div class="flex items-center gap-1.5 text-[9px] text-gray-300 font-medium">
|
|
<span class="text-[#46d369] font-bold">${s}% Match</span>
|
|
<div class="flex items-center gap-1">
|
|
<span class="bg-[#FA320A] text-white px-1 rounded flex items-center gap-0.5 h-3">
|
|
<span class="material-symbols-outlined text-[8px]">local_pizza</span> ${c}%
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`}function k(e){document.body.innerHTML=`
|
|
<div class="min-h-screen flex flex-col items-center justify-center text-white gap-6 p-4">
|
|
<span class="material-symbols-outlined text-6xl text-primary">error</span>
|
|
<h1 class="text-2xl font-bold">${e}</h1>
|
|
<a href="/" class="bg-primary text-white px-6 py-2 rounded font-medium hover:bg-primary/90 transition-colors">Go Home</a>
|
|
</div>
|
|
`}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",L):L();
|