import { BasePlugin } from '../core/base-plugin.js'; import { __publicField } from '../core/constants.js'; import { CDN_SOURCES, STORE_NAME, dbHelper } from './task.js'; class NewVideoPlugin extends BasePlugin { constructor() { super(...arguments); __publicField(this, "currentPage", 1); __publicField(this, "pageSize", 30); } getName() { return "NewVideoPlugin"; } async initCss() { return "\n \n "; } async showNewVideoCount() { const totalNewVideoCount = (await storageManager.getFavoriteActressList()).reduce(((accumulator, actress) => { var _a2; return accumulator + ((null == (_a2 = actress.newVideoList) ? void 0 : _a2.length) ?? 0); }), 0); $("#newVideoCount").text(`${totalNewVideoCount}`); } async resetBtnTip() { const taskPlugin = this.getBean("TaskPlugin"), settingObj = await storageManager.getSetting(), lastCheckTimeStr = localStorage.getItem(taskPlugin.lastCheckFavoriteActressTimeKey) || "无", checkFavoriteActress_IntervalTime = settingObj.checkFavoriteActress_IntervalTime, lastCheckNewVideoTimeStr = localStorage.getItem(taskPlugin.lastCheckNewVideoTimeKey) || "无", checkNewVideo_intervalTime = settingObj.checkNewVideo_intervalTime; $("#checkFavoriteActress").attr("data-tip", `上次自动同步时间: ${lastCheckTimeStr}; 检测间隔时间: ${checkFavoriteActress_IntervalTime}小时`); $("#checkNewVideo").attr("data-tip", `上次检测时间: ${lastCheckNewVideoTimeStr}; 检测间隔时间: ${checkNewVideo_intervalTime}小时`); } async openDialog() { const taskPlugin = this.getBean("TaskPlugin"), settingObj = await storageManager.getSetting(), lastCheckTimeStr = localStorage.getItem(taskPlugin.lastCheckFavoriteActressTimeKey) || "无", checkFavoriteActress_IntervalTime = settingObj.checkFavoriteActress_IntervalTime, lastCheckNewVideoTimeStr = localStorage.getItem(taskPlugin.lastCheckNewVideoTimeKey) || "无", checkNewVideo_intervalTime = settingObj.checkNewVideo_intervalTime; let html = `\n
\n
\n
\n ${this.actressSvg}    手动同步演员\n ${this.newSvg}    手动检测最新作品\n ${this.settingSvg}    配置\n \n
\n
\n \n \n ${this.refreshSvg}    刷新\n
\n\n
\n
\n
\n
\n `; layer.open({ type: 1, title: '新作品检测 ❓', content: html, scrollbar: !1, area: utils.getResponsiveArea([ "80%", "90%" ]), anim: -1, success: async (layero, index) => { this.loadData(); this.bindClick(); utils.setupEscClose(index); } }); } bindClick() { const taskPlugin = this.getBean("TaskPlugin"); $("#reLoad").on("click", (event => { this.loadData(); $("#checkNewVideoMsg").text(""); })); $("#toSetting").on("click", (event => { this.getBean("SettingPlugin").openSettingDialog("task-panel", (() => { $("#setting-checkFavoriteActress").css({ border: "1px solid #f40" }); $("#setting-checkNewVideo").css({ border: "1px solid #f40" }); })); })); $("#checkFavoriteActress").on("click", (event => { utils.q({ clientX: event.clientX, clientY: event.clientY + 20 }, "是否手动同步演员?", (() => { navigator.locks.request(taskPlugin.singleTaskKey, { ifAvailable: !0 }, (async lock => { if (!lock) { show.error("当前有定时任务在后台执行中, 无法发起手动任务"); return; } if ($('a[href*="/users/profile"]').length > 0) { await taskPlugin.checkFavoriteActress(); this.loadData(); } else show.error("未登录JavDb, 同步失败"); })).catch((error => { console.error("锁任务出现错误:", error); clog.error("锁任务出现错误:", error); })); })); })); $("#checkNewVideo").on("click", (event => { utils.q({ clientX: event.clientX, clientY: event.clientY + 20 }, "是否手动检测最新作品?", (() => { navigator.locks.request(taskPlugin.singleTaskKey, { ifAvailable: !0 }, (async lock => { lock ? await taskPlugin.checkNewVideo(!0) : show.error("当前有定时任务在后台执行中, 无法发起手动任务"); })).catch((error => { console.error("锁任务出现错误:", error); clog.error("锁任务出现错误:", error); })); })); })); $("#paramActressType").on("change", (event => { this.loadData(); })); } loadData() { this.currentPage = 1; this.renderActressCards().then(); } async renderActressCards() { const $actressCardContainer = $("#actress-card-container"); if (!$actressCardContainer.length) return; let favoriteActressesList = await storageManager.getFavoriteActressList(); const paramActressType = $("#paramActressType").val(); "all" !== paramActressType && (favoriteActressesList = favoriteActressesList.filter((actress => actress.actressType === paramActressType))); const fullActressList = utils.genericSort(favoriteActressesList, [ { key: item => { var _a2; return (null == (_a2 = item.newVideoList) ? void 0 : _a2.length) ?? 0; }, order: "desc" }, { key: "lastPublishTime", order: "desc" } ]), totalCount = fullActressList.length, totalPages = Math.ceil(totalCount / this.pageSize), startIndex = (this.currentPage - 1) * this.pageSize, endIndex = startIndex + this.pageSize, pagedData = fullActressList.slice(startIndex, endIndex), javDbUrl = await this.getBean("OtherSitePlugin").getJavDbUrl(), taskPlugin = this.getBean("TaskPlugin"), checkNewVideo_ruleTime = await storageManager.getSetting("checkNewVideo_ruleTime") || 8760, cardsHtml = pagedData.map((data => { var _a2, _b, _c; const allNamesStr = Array.isArray(data.allName) ? data.allName.join(",") : ""; Array.isArray(data.newVideoList) && data.newVideoList.join(","); const detailLink = `${javDbUrl}/actors/${data.starId}?t=d`; let isUnCheck = !1; data.lastPublishTime && (isUnCheck = !taskPlugin.isUnnecessaryCheck(data.lastPublishTime, checkNewVideo_ruleTime)); let actressTypeText = "未知", actressTypeBgc = "#9E9E9E"; if ("uncensored" === data.actressType) { actressTypeText = "无码"; actressTypeBgc = "#4CAF50"; } else if ("censored" === data.actressType) { actressTypeText = "有码"; actressTypeBgc = "#FF9800"; } let cardUnnecessaryBtnCss = ""; isUnCheck && (cardUnnecessaryBtnCss = "background: linear-gradient(145deg, #e0e0e0 0%, #cabdbd 100%);box-shadow: none"); return `\n
\n \n ${allNamesStr}\n \n\n
\n \n
${data.name}
\n
\n
${allNamesStr}
\n
\n \n
\n 上次检测: ${data.lastCheckTime || ""}\n
\n
\n 最后发行作品: ${data.lastPublishTime || ""}\n
\n\n
\n ${isUnCheck ? "停更" + checkNewVideo_ruleTime / 24 / 365 + "年以上, 下轮任务不再进行检测" : ""}\n
\n \n
\n ${data.remark || ""}\n
\n \n
\n ${this.editSvg}\n ${this.deleteSvg}\n ${this.checkSvg}\n
\n \n
${actressTypeText}
\n
\n 🔔 ${(null == (_c = data.newVideoList) ? void 0 : _c.length) || 0} \n
\n
\n `; })).join(""); $actressCardContainer.html(cardsHtml); $(".btn-delete-actress").off("click").on("click", (e => { e.preventDefault(); const starId = $(e.currentTarget).attr("data-starId"), actress = fullActressList.find((item => item.starId === starId)); utils.q(e, `是否取消收藏 ${actress.name}?`, (async () => { let deleteActressUrl = `${await this.getBean("OtherSitePlugin").getJavDbUrl()}/actors/${starId}/uncollect`; const csrfToken = document.querySelector("meta[name=csrf-token]").content, res = await gmHttp.post(deleteActressUrl, null, { "x-csrf-token": csrfToken }); if (res.includes("removeClass")) { await storageManager.removeFavoriteActress(starId); this.loadData(); } else { show.error("移除失败"); clog.error("移除失败,返回值:", res); } })); })); $(".btn-edit-actress").off("click").on("click", (e => { e.preventDefault(); const starId = $(e.currentTarget).attr("data-starId"), actress = fullActressList.find((item => item.starId === starId)); actress ? this.editActress(actress) : show.error(`未找到 starId 为 ${starId} 的女优记录。`); })); $(".btn-check-actress").off("click").on("click", (e => { e.preventDefault(); navigator.locks.request(taskPlugin.singleTaskKey, { ifAvailable: !0 }, (async lock => { if (!lock) { show.error("当前有定时任务在后台执行中, 无法发起手动任务"); return; } const starId = $(e.currentTarget).attr("data-starId"), actress = fullActressList.find((item => item.starId === starId)); await taskPlugin.checkOneNewVideo(actress); })).catch((error => { console.error("锁任务出现错误:", error); clog.error("锁任务出现错误:", error); })); })); this.renderPagination(totalCount, totalPages); show.ok("加载完成"); } async editActress(actress) { const initialName = actress.name, initialAvatar = actress.avatar, initialRemark = actress.remark || "", initialAllName = Array.isArray(actress.allName) ? actress.allName.join(",") : "", initialNewVideoList = Array.isArray(actress.newVideoList) ? actress.newVideoList.join(",") : "", starId = actress.starId, textareaStyle = "width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; min-height: 60px; overflow-y: hidden;", initActressType = actress.actressType || "", editFormHtml = `\n
\n
\n Avatar Preview\n
\n \n \n
\n \n \n
\n
\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n `; layer.open({ type: 1, title: `编辑女优: ${initialName} (${starId})`, area: [ "500px", "750px" ], content: editFormHtml, btn: [ "保存", "取消" ], success: (layero, index) => { const autoResizeTextarea = $textarea => { $textarea.css("height", "auto"); $textarea.css("height", $textarea[0].scrollHeight + 15 + "px"); }; $("#edit-actress-avatar").on("input", (function() { const newUrl = $(this).val(); $("#edit-avatar-preview").attr("src", newUrl); })); const $allNameTextarea = $("#edit-actress-allname"); $allNameTextarea.on("input", (function() { autoResizeTextarea($(this)); })); autoResizeTextarea($allNameTextarea); const $videoListTextarea = $("#edit-actress-newvideolist"); $videoListTextarea.on("input", (function() { autoResizeTextarea($(this)); })); autoResizeTextarea($videoListTextarea); $("#search-avatar-btn").on("click", (async () => { await this.searchAvatar(); })); $("#select-cdn-btn").on("click", (async () => { await async function() { const initialIndex = currentCdnIndex, radioOptions = CDN_SOURCES.map(((source, index) => `\n
\n \n \n
\n `)).join(""), cdnSelectHtml = `\n
\n

请选择头像数据源 (当前: ${CDN_SOURCES[initialIndex].name}):

\n ${radioOptions}\n

切换源会清除本地缓存的数据,并在下次搜索时重新加载。

\n
\n `; layer.open({ type: 1, title: "选择 CDN 源", area: [ "400px", "auto" ], content: cdnSelectHtml, btn: [ "确定", "取消" ], success: (layero, index) => { utils.setupEscClose(index); }, yes: async index => { const newIndexStr = $('input[name="cdn-source"]:checked').val(), newIndex = parseInt(newIndexStr, 10); if (newIndex !== currentCdnIndex) { currentCdnIndex = newIndex; localStorage.setItem("jhs_img_cdn_index", newIndex.toString()); window.G_FRIENDS_JSON_URL = CDN_SOURCES[newIndex].json; window.CDN_BASE_URL = CDN_SOURCES[newIndex].base; window.G_FRIENDS_DATA_CACHE = null; window.G_FRIENDS_AVATAR_MAP = null; try { await dbHelper.set("filetree_data", null); } catch (e) { clog.error("清除 IndexedDB 缓存失败:", e); } show.ok(`CDN 源已切换为: ${CDN_SOURCES[newIndex].name}`); layer.close(index); } else layer.close(index); } }); }(); })); utils.setupEscClose(index); }, yes: async index => { const newAvatar = $("#edit-actress-avatar").val().trim(), newName = $("#edit-actress-name").val().trim(), newAllNameStr = $("#edit-actress-allname").val().trim(), newVideoListStr = $("#edit-actress-newvideolist").val().trim(), newRemark = $("#edit-remark").val().trim(), newActressType = $("#actressType").val(); if (!newName) { show.error("主名称不能为空"); return !1; } const newAllName = newAllNameStr.split(/[\uff0c,]/).map((n => n.trim())).filter((n => n.length > 0)), newVideoList = newVideoListStr.split(/[\uff0c,]/).map((n => n.trim())).filter((n => n.length > 0)); actress.avatar = newAvatar; actress.name = newName; actress.allName = newAllName; actress.newVideoList = newVideoList; actress.actressType = newActressType; actress.remark = newRemark; if (await storageManager.updateFavoriteActress(actress)) show.error("修改失败"); else { show.ok(`女优 ${newName} 信息已更新`); await this.renderActressCards(); layer.close(index); } } }); } renderPagination(totalCount, totalPages) { const currentPage = this.currentPage; let paginationHtml = ""; const $actressPagination = $("#actress-pagination"); if (0 === totalPages) { paginationHtml = '共 0 条记录'; $actressPagination.html(paginationHtml); return; } currentPage > 1 && totalPages > 5 && (paginationHtml += ''); currentPage > 1 && (paginationHtml += ``); let startPage = Math.max(1, currentPage - Math.floor(2.5)), endPage = Math.min(totalPages, startPage + 5 - 1); endPage - startPage < 4 && (startPage = Math.max(1, endPage - 5 + 1)); for (let i = startPage; i <= endPage; i++) { paginationHtml += ``; } currentPage < totalPages && (paginationHtml += ``); currentPage < totalPages && totalPages > 5 && (paginationHtml += ``); paginationHtml += `共 ${totalCount} 条记录 (第 ${currentPage}/${totalPages} 页)`; $actressPagination.html(paginationHtml); $(".pagination-btn").off("click").on("click", (e => { if ($(e.currentTarget).is("[disabled]")) return; const newPage = parseInt($(e.currentTarget).data("page")); if (newPage >= 1 && newPage <= totalPages && newPage !== this.currentPage) { this.currentPage = newPage; this.renderActressCards(); } })); } async searchAvatar() { const $mainNameInput = $("#edit-actress-name"), $aliasInput = $("#edit-actress-allname"), currentName = $mainNameInput.val().trim(), searchNames = $aliasInput.val().trim().split(/[\uff0c,]/).map((n => n.trim())).filter((n => n.length > 0)); currentName && searchNames.unshift(currentName); if (0 === searchNames.length) { show.error("请先填写女优主名称或别名进行搜索。"); return; } const loadObj = loading("正在搜索头像..."); let imageLinks = []; try { imageLinks = await searchActorAvatars(searchNames); } catch (e) { show.error(`头像数据加载或搜索失败: ${e.message || e}`); return; } finally { loadObj.close(); } if (0 === imageLinks.length) { show.error(`未找到与 '${searchNames.join("、")}' 相关的头像。请检查名称。`); return; } const imageItems = imageLinks.map(((url, index) => `\n
\n \n
...
\n
\n `)).join(""), imageListHtml = `\n \n \n
\n

\n 点击图片即可选择(初始共 ${imageLinks.length} 张)\n

\n
\n
\n ${imageItems}\n
\n
\n
\n `; let errorCount = 0; layer.open({ type: 1, title: `选择女优头像 (${imageLinks.length} 张)`, area: utils.getResponsiveArea([ "900px", "85%" ]), content: imageListHtml, btn: [ "关闭" ], success: (selectionLayero, selectionIndex) => { const $container = $(selectionLayero), $images = $container.find(".gfriends-selectable-img"), $prompt = $container.find("#gfriends-prompt"); $images.each((function() { const $img = $(this), wrapperId = $img.data("wrapper-id"), $wrapper = $container.find(`#${wrapperId}`), $sizeTag = $container.find(`.gfriends-size-tag[data-size-for="${wrapperId}"]`); $img.on("load", (function() { const width = this.naturalWidth, height = this.naturalHeight; $sizeTag.text(`${width} x ${height}`); })); $img.on("error", (function() { $wrapper.remove(); errorCount++; const validCount = imageLinks.length - errorCount; $prompt.text(`点击图片即可选择(已移除 ${errorCount} 张错误图片,剩余 ${validCount} 张)`); if (0 === validCount) { show.error("所有搜索到的头像链接均已失效,无法选择。"); layer.close(selectionIndex); } })); this.complete && (this.naturalWidth > 0 ? $img.trigger("load") : $img.trigger("error")); })); $images.on("click", (function() { const $clickedImg = $(this), selectedUrl = $clickedImg.data("url"); $("#edit-actress-avatar").val(selectedUrl); $("#edit-avatar-preview").attr("src", selectedUrl); $images.removeClass("is-selected"); $clickedImg.addClass("is-selected"); setTimeout((() => { layer.close(selectionIndex); }), 150); })); utils.setupEscClose(selectionIndex); } }); } } export { NewVideoPlugin };