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 += ``;
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);
});