let currentCate = "ing"; let nextPage = 2; let totalPage = 1; let isLoading = false; const spinner = document.getElementById("spinner"); const listContainer = document.getElementById("screen_movie_list"); const categoryButtons = document.querySelectorAll("#anime_category button"); const categoryConfig = { ing: { endpoint: "anime_list", makeData: (page) => ({ page: String(page), type: "ing" }), title: "애니", }, movie: { endpoint: "screen_movie_list", makeData: (page) => ({ page: String(page) }), title: "극장판", }, complete: { endpoint: "complete_anilist", makeData: (page) => ({ page: String(page) }), title: "성인", }, top_view: { endpoint: "anime_list", makeData: (page) => ({ page: String(page), type: "top_view" }), title: "최신", }, }; function setLoading(flag) { isLoading = flag; spinner.style.display = flag ? "block" : "none"; } function setActiveCategory(cate) { categoryButtons.forEach((button) => { button.classList.toggle("active", button.id === cate); }); } function escapeHtml(value) { return String(value ?? "") .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function getImageUrl(imageLink) { const image = String(imageLink || "").trim(); return image === "" ? placeholder_image : image; } function renderEmpty(message) { listContainer.innerHTML = `
${escapeHtml(message)}
`; } function openPlayerPage(url) { if (!url) { $.notify("재생 URL을 찾지 못했습니다.", { type: "warning", }); return; } window.open(url, "_blank"); } async function playLatest(code, title) { try { const response = await fetch(`/${package_name}/ajax/play_latest`, { method: "POST", cache: "no-cache", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams({ code: String(code || ""), title: String(title || ""), }), }); const ret = await response.json(); if (ret.ret !== "success" || ret.data == null) { $.notify(`${escapeHtml(ret.log || "재생 정보를 가져오지 못했습니다.")}`, { type: "warning", }); return; } openPlayerPage(ret.data.play_url); } catch (error) { console.error(error); $.notify("재생 정보를 가져오지 못했습니다.", { type: "warning", }); } } function renderItems(ret, append = false) { const items = Array.isArray(ret.episode) ? ret.episode : []; const page = Number(ret.page || 1); const title = categoryConfig[currentCate]?.title || "목록"; if (items.length === 0 && append === false) { renderEmpty(`${title} 목록이 없습니다.`); return; } let html = ""; if (append === false) { html += `
`; html += ``; html += `
`; html += `
`; } for (const item of items) { const code = escapeHtml(item.code); const titleText = escapeHtml(item.title); const chapter = escapeHtml(item.chapter || ""); const imageUrl = escapeHtml(getImageUrl(item.image_link)); html += `
`; html += `
`; html += `${titleText}`; if (chapter !== "") { html += `${chapter}`; } html += `
`; html += `
${titleText}
`; html += `분석`; html += ``; html += `
`; html += `
`; html += `
`; } if (append === false) { html += `
`; listContainer.innerHTML = html; } else { const wrapper = document.createElement("div"); wrapper.innerHTML = html; const target = document.getElementById("inner_screen_movie"); while (wrapper.firstChild) { target.appendChild(wrapper.firstChild); } } if (page >= totalPage) { nextPage = totalPage + 1; } else { nextPage = page + 1; } } async function fetchCategory(cate, page = 1, append = false) { const config = categoryConfig[cate]; if (config === undefined || isLoading) { return; } currentCate = cate; setActiveCategory(cate); setLoading(true); try { const response = await fetch(`/${package_name}/ajax/${config.endpoint}`, { method: "POST", cache: "no-cache", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams(config.makeData(page)), }); const ret = await response.json(); if (ret.ret !== "success") { renderEmpty(ret.log || "목록을 불러오지 못했습니다."); return; } totalPage = Number(ret.total_page || 1); renderItems(ret, append); } catch (error) { console.error(error); renderEmpty("목록을 불러오지 못했습니다."); } finally { setLoading(false); } } async function runSearch() { const input = document.getElementById("input_search"); const query = input.value.trim(); if (query === "" || isLoading) { return; } setLoading(true); try { const response = await fetch(`/${package_name}/ajax/search`, { method: "POST", cache: "no-cache", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: new URLSearchParams({ query }), }); const ret = await response.json(); if (ret.ret !== "success") { renderEmpty(ret.log || "검색 결과를 불러오지 못했습니다."); return; } currentCate = "search"; setActiveCategory(""); totalPage = 1; nextPage = 2; const result = { page: 1, total_page: 1, episode: Array.isArray(ret.episode) ? ret.episode : [], }; renderItems(result, false); if (result.episode.length === 0) { renderEmpty(`"${query}" 검색 결과가 없습니다.`); } } catch (error) { console.error(error); renderEmpty("검색 결과를 불러오지 못했습니다."); } finally { setLoading(false); } } function loadNextPage() { if (isLoading) { return; } if (currentCate === "search" || currentCate === "top_view") { return; } if (nextPage > totalPage) { return; } fetchCategory(currentCate, nextPage, true); } function onCategoryClick(event) { const cate = event.target.id; if (categoryConfig[cate] === undefined) { return; } nextPage = 2; totalPage = 1; fetchCategory(cate, 1, false); } function onScroll() { const threshold = 50; const { scrollTop, scrollHeight, clientHeight } = document.documentElement; if (clientHeight + scrollTop + threshold >= scrollHeight) { loadNextPage(); } } function debounce(func, delay) { let timeoutId = null; return (...args) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => func(...args), delay); }; } document.getElementById("anime_category").addEventListener("click", onCategoryClick); document.getElementById("btn_search").addEventListener("click", runSearch); document.getElementById("input_search").addEventListener("keydown", (event) => { if (event.key === "Enter") { event.preventDefault(); runSearch(); } }); document.addEventListener("scroll", debounce(onScroll, 250)); document.body.addEventListener("click", (event) => { const button = event.target.closest(".play-latest-btn"); if (!button) { return; } event.preventDefault(); playLatest(button.dataset.code, button.dataset.title); }); document.addEventListener("DOMContentLoaded", () => { fetchCategory("ing", 1, false); });