diff --git a/dist/jhs-enhance.user.js b/dist/jhs-enhance.user.js
index dad5e4f..310f55b 100644
--- a/dist/jhs-enhance.user.js
+++ b/dist/jhs-enhance.user.js
@@ -631,6 +631,198 @@
background: rgba(59, 130, 246, 0.9);
box-shadow: 0 4px 16px rgba(59, 130, 246, 0.4);
}
+
+ .magnet-4k-highlight {
+ background-color: #fff5f5 !important;
+ }
+ .magnet-4k-highlight .name {
+ color: #f40 !important;
+ }
+
+ .msf-overlay {
+ position: fixed;
+ inset: 0;
+ background: rgba(0, 0, 0, 0.3);
+ z-index: 999997;
+ }
+ .msf-panel {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background: #fff;
+ border-radius: 10px;
+ box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2);
+ z-index: 999998;
+ width: 380px;
+ display: flex;
+ flex-direction: column;
+ }
+ .msf-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 14px 18px;
+ border-bottom: 1px solid #e5e7eb;
+ background: #f9fafb;
+ border-radius: 10px 10px 0 0;
+ }
+ .msf-title {
+ font-size: 15px;
+ font-weight: 600;
+ color: #1e293b;
+ }
+ .msf-close {
+ font-size: 22px;
+ color: #9ca3af;
+ cursor: pointer;
+ padding: 0 6px;
+ line-height: 1;
+ transition: color 0.2s;
+ }
+ .msf-close:hover {
+ color: #ef4444;
+ }
+ .msf-body {
+ padding: 18px;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ }
+ .msf-section {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ }
+ .msf-section-title {
+ font-size: 13px;
+ font-weight: 600;
+ color: #6b7280;
+ }
+ .msf-sort-list {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ }
+ .msf-sort-item {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 10px 14px;
+ background: #f8fafc;
+ border: 1px solid #e2e8f0;
+ border-radius: 6px;
+ cursor: default;
+ transition: all 0.2s;
+ user-select: none;
+ }
+ .msf-sort-item:hover {
+ border-color: #cbd5e1;
+ background: #f1f5f9;
+ }
+ .msf-sort-item.msf-dragging {
+ opacity: 0.5;
+ border-color: #3b82f6;
+ background: #eff6ff;
+ }
+ .msf-sort-item.msf-drag-over {
+ border-color: #3b82f6;
+ background: #eff6ff;
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
+ }
+ .msf-drag-handle {
+ color: #cbd5e1;
+ font-size: 16px;
+ cursor: grab;
+ line-height: 1;
+ letter-spacing: -2px;
+ transition: color 0.2s;
+ }
+ .msf-drag-handle:active {
+ cursor: grabbing;
+ }
+ .msf-sort-item:hover .msf-drag-handle {
+ color: #94a3b8;
+ }
+ .msf-sort-label {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 14px;
+ color: #374151;
+ cursor: pointer;
+ flex: 1;
+ }
+ .msf-sort-checkbox {
+ margin: 0;
+ cursor: pointer;
+ width: 16px;
+ height: 16px;
+ }
+ .msf-sort-hint {
+ font-size: 12px;
+ color: #9ca3af;
+ margin-left: 4px;
+ }
+ .msf-filter-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ }
+ .msf-filter-label {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ padding: 8px 14px;
+ background: #f8fafc;
+ border: 1px solid #e2e8f0;
+ border-radius: 6px;
+ font-size: 14px;
+ color: #374151;
+ cursor: pointer;
+ transition: all 0.2s;
+ user-select: none;
+ }
+ .msf-filter-label:hover {
+ border-color: #cbd5e1;
+ background: #f1f5f9;
+ }
+ .msf-filter-checkbox {
+ margin: 0;
+ cursor: pointer;
+ width: 16px;
+ height: 16px;
+ }
+ .msf-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+ padding: 14px 18px;
+ border-top: 1px solid #e5e7eb;
+ }
+ .msf-btn {
+ padding: 8px 22px;
+ border: none;
+ border-radius: 6px;
+ font-size: 14px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s;
+ }
+ .msf-btn-cancel {
+ background: #f3f4f6;
+ color: #374151;
+ }
+ .msf-btn-cancel:hover {
+ background: #e5e7eb;
+ }
+ .msf-btn-confirm {
+ background: #3b82f6;
+ color: #fff;
+ }
+ .msf-btn-confirm:hover {
+ background: #2563eb;
+ }
`;
const subtitleModalCss = `
@@ -8075,26 +8267,112 @@ ${value}\r
getName() {
return "HighlightMagnetPlugin";
}
- doFilterMagnet() {
- this.handleDb();
- this.handleBus();
+
+ parseDateFromAttr(dateStr) {
+ if (!dateStr || dateStr.length !== 8) {
+ const d2 = new Date(dateStr);
+ return isNaN(d2.getTime()) ? 0 : d2.getTime();
+ }
+ const y = parseInt(dateStr.substring(0, 4), 10);
+ const m = parseInt(dateStr.substring(4, 6), 10) - 1;
+ const d = parseInt(dateStr.substring(6, 8), 10);
+ const date = new Date(y, m, d);
+ return isNaN(date.getTime()) ? 0 : date.getTime();
}
- handleDb() {
+
+ getRowInfo(row) {
+ const $row = $(row);
+ const nameText = $row.find(".name").text().toLowerCase();
+ const tags = [];
+ $row.find(".tags .tag").each((_, tag) => tags.push($(tag).text().toLowerCase()));
+ const sizeMB = parseInt($row.attr("data-size"), 10) || 0;
+ const files = parseInt($row.attr("data-files"), 10) || 0;
+ const dateStr = $row.attr("data-date") || "";
+ return {
+ row,
+ $row,
+ nameText,
+ date: this.parseDateFromAttr(dateStr),
+ dateStr,
+ size: sizeMB * 1024 * 1024,
+ files,
+ tags,
+ is4K: nameText.includes("4k") || nameText.includes("[4k]") || tags.includes("4k"),
+ isHD: nameText.includes("高清") || tags.includes("高清"),
+ isSubtitle: nameText.includes("字幕") || nameText.includes("-c") || nameText.includes(".chs") || nameText.includes(".cht") || nameText.includes(".chi") || tags.includes("字幕"),
+ isUncensored: nameText.includes("无码") || nameText.includes("無碼") || nameText.includes("-u") || nameText.includes("-uc") || tags.includes("无码") || tags.includes("無碼")
+ };
+ }
+
+ async applySortAndFilter(settings) {
if (!isJavDb$1) return;
- let magnetNameList = $("#magnets-content .name");
- if (0 === magnetNameList.length) return;
- const HIGH_QUALITY_KEYWORDS = ["4k", "-c", "-u", "-uc"];
- let hasHighQuality = false;
- magnetNameList.each(((_, el) => {
- const $el = $(el), text = $el.text().toLowerCase(), isHighQuality = HIGH_QUALITY_KEYWORDS.some(((keyword) => text.includes(keyword)));
- $el.parent().parent().parent().addClass("magnet-row");
- text.includes("4k") && $el.css("color", "#f40");
- if (isHighQuality) {
- hasHighQuality = true;
- $el.parent().parent().parent().addClass("high-quality");
- }
- }));
- hasHighQuality ? $("#magnets-content .magnet-row").not(".high-quality").hide() : $("#enable-magnets-filter").addClass("do-hide");
+ const $container = $("#magnets-content");
+ const $rows = $container.find(".item[data-rank]");
+ if (0 === $rows.length) return;
+ const rowInfos = $rows.toArray().map((row) => this.getRowInfo(row));
+ const {filterHD, filter4K, filterSubtitle, filterUncensored, sortOrder} = settings;
+ const anyFilterEnabled = filterHD || filter4K || filterSubtitle || filterUncensored;
+ let visibleInfos = rowInfos;
+ if (anyFilterEnabled) {
+ visibleInfos = rowInfos.filter((info) => {
+ if (filterHD && info.isHD) return true;
+ if (filter4K && info.is4K) return true;
+ if (filterSubtitle && info.isSubtitle) return true;
+ if (filterUncensored && info.isUncensored) return true;
+ return false;
+ });
+ }
+ if (sortOrder && sortOrder.length > 0) {
+ visibleInfos.sort((a, b) => {
+ for (const key of sortOrder) {
+ let cmp = 0;
+ switch (key) {
+ case "date":
+ cmp = b.date - a.date;
+ break;
+ case "size":
+ cmp = b.size - a.size;
+ break;
+ case "files":
+ cmp = a.files - b.files;
+ break;
+ }
+ if (0 !== cmp) return cmp;
+ }
+ return 0;
+ });
+ }
+ const container = document.getElementById("magnets-content");
+ if (!container) return;
+ const hiddenInfos = rowInfos.filter((info) => !visibleInfos.includes(info));
+ const reviewsContainer = document.getElementById("reviewsContainer");
+ const insertRef = reviewsContainer && reviewsContainer.parentNode === container ? reviewsContainer.previousElementSibling || reviewsContainer : null;
+ const fragment = document.createDocumentFragment();
+ visibleInfos.forEach((info) => fragment.appendChild(info.row));
+ hiddenInfos.forEach((info) => fragment.appendChild(info.row));
+ if (insertRef) {
+ container.insertBefore(fragment, insertRef);
+ } else {
+ container.appendChild(fragment);
+ }
+ visibleInfos.forEach((info) => {
+ if (info.is4K) {
+ info.$row.addClass("magnet-4k-highlight");
+ }
+ });
+ hiddenInfos.forEach((info) => $(info.row).hide());
+ }
+
+ showAll() {
+ if (isJavDb$1) {
+ $("#magnets-content .item[data-rank]").toArray().forEach(((el) => {
+ $(el).show();
+ $(el).removeClass("magnet-4k-highlight");
+ }));
+ }
+ if (isJavBus$1) {
+ $("#magnet-table tr").toArray().forEach(((el) => $(el).show()));
+ }
}
handleBus() {
isJavBus$1 && window.isDetailPage && utils.loopDetector((() => $("#magnet-table td a").length > 0), (() => {
@@ -8114,12 +8392,6 @@ ${value}\r
})) : $("#enable-magnets-filter").addClass("do-hide");
}));
}
- showAll() {
- if (isJavDb$1) {
- $("#magnets-content .item").toArray().forEach(((el) => $(el).show()));
- }
- isJavBus$1 && $("#magnet-table tr").toArray().forEach(((el) => $(el).show()));
- }
}
class FoldCategoryPlugin extends BasePlugin {
getName() {
@@ -12896,7 +13168,7 @@ ${err.stack}` : "");
}
async createMenuBtn() {
const pageInfo = this.getPageInfo(), carNum2 = pageInfo.carNum,
- buttonsHtml = '\n
\n
\n \n \n \n \n
\n \n
\n \n \n \n \n
\n
\n ';
+ buttonsHtml = '\n \n
\n \n \n \n \n
\n \n
\n \n \n \n \n
\n
\n ';
isJavDb$1 && $(".tabs").after(buttonsHtml);
isJavBus$1 && $("#mag-submit-show").before(buttonsHtml);
$("#favoriteBtn").on("click", (() => this.favoriteOne()));
@@ -12916,19 +13188,38 @@ ${err.stack}` : "");
}
});
}));
- const highlightMagnetPlugin = this.getBean("HighlightMagnetPlugin"), enableMagnetsFilter = await storageManager.getSetting("enableMagnetsFilter", YES);
- $("#magnets-span").text(enableMagnetsFilter === YES ? "关闭磁力过滤" : "开启磁力过滤");
- enableMagnetsFilter === YES && highlightMagnetPlugin.doFilterMagnet();
+ const highlightMagnetPlugin = this.getBean("HighlightMagnetPlugin"),
+ enableMagnetsFilter = await storageManager.getSetting("enableMagnetsFilter", NO);
+ if (enableMagnetsFilter === YES) {
+ const savedSettings = await storageManager.getSetting("magnetSortAndFilter", "{}");
+ let settings;
+ try {
+ settings = JSON.parse(savedSettings);
+ } catch (e) {
+ settings = {};
+ }
+ const sortEnabled = settings.sortEnabled || {};
+ const fullSortOrder = settings.sortOrder && settings.sortOrder.length === 3 ? settings.sortOrder : ["date", "size", "files"];
+ const effectiveSortOrder = fullSortOrder.filter((key) => sortEnabled[key]);
+ highlightMagnetPlugin.applySortAndFilter({
+ filterHD: settings.filterHD || false,
+ filter4K: settings.filter4K || false,
+ filterSubtitle: settings.filterSubtitle || false,
+ filterUncensored: settings.filterUncensored || false,
+ sortOrder: effectiveSortOrder
+ });
+ $("#magnets-span").text("取消磁力过滤");
+ } else {
+ $("#magnets-span").text("排序与过滤");
+ }
$("#enable-magnets-filter").on("click", ((event) => {
- let $span = $("#magnets-span");
- if ("关闭磁力过滤" === $span.text()) {
+ const $span = $("#magnets-span");
+ if ("取消磁力过滤" === $span.text()) {
highlightMagnetPlugin.showAll();
- $span.text("开启磁力过滤");
+ $span.text("排序与过滤");
storageManager.saveSettingItem("enableMagnetsFilter", NO);
} else {
- highlightMagnetPlugin.doFilterMagnet();
- $span.text("关闭磁力过滤");
- storageManager.saveSettingItem("enableMagnetsFilter", YES);
+ this.openMagnetSortFilterPanel(highlightMagnetPlugin);
}
}));
$("#subtitleSearchBtn").on("click", (() => {
@@ -12937,6 +13228,186 @@ ${err.stack}` : "");
window.JavPackSubtitle.preload115Matches(carNum2);
this.showStatus(carNum2).then();
}
+
+ async openMagnetSortFilterPanel(highlightMagnetPlugin) {
+ const existingPanel = document.getElementById("magnet-sort-filter-panel");
+ if (existingPanel) {
+ existingPanel.remove();
+ return;
+ }
+ const savedSettings = await storageManager.getSetting("magnetSortAndFilter", "{}");
+ let settings;
+ try {
+ settings = JSON.parse(savedSettings);
+ } catch (e) {
+ settings = {};
+ }
+ const sortEnabled = settings.sortEnabled || {date: false, size: false, files: false};
+ const sortOrder = settings.sortOrder && settings.sortOrder.length === 3 ? settings.sortOrder : ["date", "size", "files"];
+ const filterHD = settings.filterHD || false;
+ const filter4K = settings.filter4K || false;
+ const filterSubtitle = settings.filterSubtitle || false;
+ const filterUncensored = settings.filterUncensored || false;
+ const SORT_LABELS = {date: "发布日期", size: "文件大小", files: "文件数量"};
+ const SORT_HINTS = {date: "从新到旧", size: "从大到小", files: "从少到多"};
+ const FILTER_ITEMS = [
+ {key: "HD", label: "高清", checked: filterHD},
+ {key: "4K", label: "4K", checked: filter4K},
+ {key: "Subtitle", label: "字幕", checked: filterSubtitle},
+ {key: "Uncensored", label: "无码", checked: filterUncensored}
+ ];
+ const currentSortOrder = [...sortOrder];
+ const currentSortEnabled = {...sortEnabled};
+ const currentFilters = {
+ HD: filterHD,
+ "4K": filter4K,
+ Subtitle: filterSubtitle,
+ Uncensored: filterUncensored
+ };
+ const buildSortItemsHTML = () => {
+ return currentSortOrder.map((key) => {
+ const checked = currentSortEnabled[key] ? "checked" : "";
+ return `
+ ⋮⋮
+
+
`;
+ }).join("");
+ };
+ const buildFilterItemsHTML = () => {
+ return FILTER_ITEMS.map((item) => {
+ const checked = currentFilters[item.key] ? "checked" : "";
+ return ``;
+ }).join("");
+ };
+ const panelHTML = `
+
+
+
+
+
+
排序优先级(拖动调整顺序,勾选启用)
+
+ ${buildSortItemsHTML()}
+
+
+
+
过滤条件(可多选,并集关系)
+
+ ${buildFilterItemsHTML()}
+
+
+
+
+
+ `;
+ const wrapper = document.createElement("div");
+ wrapper.innerHTML = panelHTML;
+ document.body.appendChild(wrapper);
+ const panel = document.getElementById("magnet-sort-filter-panel");
+ const overlay = document.getElementById("msf-overlay");
+ const closePanel = () => {
+ panel.remove();
+ overlay.remove();
+ };
+ overlay.addEventListener("click", closePanel);
+ document.getElementById("msf-close-btn").addEventListener("click", closePanel);
+ document.getElementById("msf-cancel-btn").addEventListener("click", closePanel);
+ const rebuildSortList = () => {
+ const sortList = document.getElementById("msf-sort-list");
+ sortList.innerHTML = buildSortItemsHTML();
+ bindSortEvents();
+ };
+ const bindSortEvents = () => {
+ const sortItems = document.querySelectorAll("#msf-sort-list .msf-sort-item");
+ sortItems.forEach((item) => {
+ item.addEventListener("dragstart", (e) => {
+ e.dataTransfer.effectAllowed = "move";
+ e.dataTransfer.setData("text/plain", item.dataset.sortKey);
+ item.classList.add("msf-dragging");
+ });
+ item.addEventListener("dragend", () => {
+ item.classList.remove("msf-dragging");
+ document.querySelectorAll("#msf-sort-list .msf-sort-item").forEach((el) => el.classList.remove("msf-drag-over"));
+ });
+ item.addEventListener("dragover", (e) => {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = "move";
+ item.classList.add("msf-drag-over");
+ });
+ item.addEventListener("dragleave", () => {
+ item.classList.remove("msf-drag-over");
+ });
+ item.addEventListener("drop", (e) => {
+ e.preventDefault();
+ item.classList.remove("msf-drag-over");
+ const fromKey = e.dataTransfer.getData("text/plain");
+ const toKey = item.dataset.sortKey;
+ if (fromKey === toKey) return;
+ const fromIdx = currentSortOrder.indexOf(fromKey);
+ const toIdx = currentSortOrder.indexOf(toKey);
+ currentSortOrder.splice(fromIdx, 1);
+ currentSortOrder.splice(toIdx, 0, fromKey);
+ rebuildSortList();
+ });
+ const checkbox = item.querySelector(".msf-sort-checkbox");
+ checkbox.addEventListener("change", () => {
+ currentSortEnabled[item.dataset.sortKey] = checkbox.checked;
+ });
+ });
+ };
+ bindSortEvents();
+ document.querySelectorAll(".msf-filter-checkbox").forEach((cb) => {
+ cb.addEventListener("change", () => {
+ currentFilters[cb.dataset.filterKey] = cb.checked;
+ });
+ });
+ document.getElementById("msf-confirm-btn").addEventListener("click", async () => {
+ try {
+ const enabledSortOrder = currentSortOrder.filter((key) => currentSortEnabled[key]);
+ const newSettings = {
+ sortOrder: currentSortOrder,
+ sortEnabled: currentSortEnabled,
+ filterHD: currentFilters.HD,
+ filter4K: currentFilters["4K"],
+ filterSubtitle: currentFilters.Subtitle,
+ filterUncensored: currentFilters.Uncensored
+ };
+ await storageManager.saveSettingItem("magnetSortAndFilter", JSON.stringify(newSettings));
+ const hasAnyFilter = currentFilters.HD || currentFilters["4K"] || currentFilters.Subtitle || currentFilters.Uncensored;
+ const hasAnySort = enabledSortOrder.length > 0;
+ if (hasAnyFilter || hasAnySort) {
+ await highlightMagnetPlugin.applySortAndFilter({
+ filterHD: currentFilters.HD,
+ filter4K: currentFilters["4K"],
+ filterSubtitle: currentFilters.Subtitle,
+ filterUncensored: currentFilters.Uncensored,
+ sortOrder: enabledSortOrder
+ });
+ $("#magnets-span").text("取消磁力过滤");
+ await storageManager.saveSettingItem("enableMagnetsFilter", YES);
+ } else {
+ highlightMagnetPlugin.showAll();
+ $("#magnets-span").text("排序与过滤");
+ await storageManager.saveSettingItem("enableMagnetsFilter", NO);
+ }
+ } catch (e) {
+ console.error("[磁力排序] 应用失败:", e);
+ }
+ closePanel();
+ });
+ }
async showStatus(carNum2) {
const $filterBtn = $("#filterBtn span"), $favoriteBtn = $("#favoriteBtn span"), $hasDownBtn = $("#hasDownBtn span"), $hasWatchBtn = $("#hasWatchBtn span"), hotKeyDisplay = (hotKey) => hotKey ? `(${hotKey})` : "";
$filterBtn.text(`🚫 屏蔽 ${hotKeyDisplay(this.filterHotKey)}`);
diff --git a/src/core/constants.js b/src/core/constants.js
index 1b1d27b..6fe8ace 100644
--- a/src/core/constants.js
+++ b/src/core/constants.js
@@ -569,6 +569,198 @@ const mainCss = `
background: rgba(59, 130, 246, 0.9);
box-shadow: 0 4px 16px rgba(59, 130, 246, 0.4);
}
+
+ .magnet-4k-highlight {
+ background-color: #fff5f5 !important;
+ }
+ .magnet-4k-highlight .name {
+ color: #f40 !important;
+ }
+
+ .msf-overlay {
+ position: fixed;
+ inset: 0;
+ background: rgba(0, 0, 0, 0.3);
+ z-index: 999997;
+ }
+ .msf-panel {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ background: #fff;
+ border-radius: 10px;
+ box-shadow: 0 8px 30px rgba(0, 0, 0, 0.2);
+ z-index: 999998;
+ width: 380px;
+ display: flex;
+ flex-direction: column;
+ }
+ .msf-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 14px 18px;
+ border-bottom: 1px solid #e5e7eb;
+ background: #f9fafb;
+ border-radius: 10px 10px 0 0;
+ }
+ .msf-title {
+ font-size: 15px;
+ font-weight: 600;
+ color: #1e293b;
+ }
+ .msf-close {
+ font-size: 22px;
+ color: #9ca3af;
+ cursor: pointer;
+ padding: 0 6px;
+ line-height: 1;
+ transition: color 0.2s;
+ }
+ .msf-close:hover {
+ color: #ef4444;
+ }
+ .msf-body {
+ padding: 18px;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ }
+ .msf-section {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ }
+ .msf-section-title {
+ font-size: 13px;
+ font-weight: 600;
+ color: #6b7280;
+ }
+ .msf-sort-list {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ }
+ .msf-sort-item {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 10px 14px;
+ background: #f8fafc;
+ border: 1px solid #e2e8f0;
+ border-radius: 6px;
+ cursor: default;
+ transition: all 0.2s;
+ user-select: none;
+ }
+ .msf-sort-item:hover {
+ border-color: #cbd5e1;
+ background: #f1f5f9;
+ }
+ .msf-sort-item.msf-dragging {
+ opacity: 0.5;
+ border-color: #3b82f6;
+ background: #eff6ff;
+ }
+ .msf-sort-item.msf-drag-over {
+ border-color: #3b82f6;
+ background: #eff6ff;
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.2);
+ }
+ .msf-drag-handle {
+ color: #cbd5e1;
+ font-size: 16px;
+ cursor: grab;
+ line-height: 1;
+ letter-spacing: -2px;
+ transition: color 0.2s;
+ }
+ .msf-drag-handle:active {
+ cursor: grabbing;
+ }
+ .msf-sort-item:hover .msf-drag-handle {
+ color: #94a3b8;
+ }
+ .msf-sort-label {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 14px;
+ color: #374151;
+ cursor: pointer;
+ flex: 1;
+ }
+ .msf-sort-checkbox {
+ margin: 0;
+ cursor: pointer;
+ width: 16px;
+ height: 16px;
+ }
+ .msf-sort-hint {
+ font-size: 12px;
+ color: #9ca3af;
+ margin-left: 4px;
+ }
+ .msf-filter-list {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 10px;
+ }
+ .msf-filter-label {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ padding: 8px 14px;
+ background: #f8fafc;
+ border: 1px solid #e2e8f0;
+ border-radius: 6px;
+ font-size: 14px;
+ color: #374151;
+ cursor: pointer;
+ transition: all 0.2s;
+ user-select: none;
+ }
+ .msf-filter-label:hover {
+ border-color: #cbd5e1;
+ background: #f1f5f9;
+ }
+ .msf-filter-checkbox {
+ margin: 0;
+ cursor: pointer;
+ width: 16px;
+ height: 16px;
+ }
+ .msf-footer {
+ display: flex;
+ justify-content: flex-end;
+ gap: 10px;
+ padding: 14px 18px;
+ border-top: 1px solid #e5e7eb;
+ }
+ .msf-btn {
+ padding: 8px 22px;
+ border: none;
+ border-radius: 6px;
+ font-size: 14px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s;
+ }
+ .msf-btn-cancel {
+ background: #f3f4f6;
+ color: #374151;
+ }
+ .msf-btn-cancel:hover {
+ background: #e5e7eb;
+ }
+ .msf-btn-confirm {
+ background: #3b82f6;
+ color: #fff;
+ }
+ .msf-btn-confirm:hover {
+ background: #2563eb;
+ }
`;
diff --git a/src/plugins/detail-page-button.js b/src/plugins/detail-page-button.js
index a0c1b56..c79b492 100644
--- a/src/plugins/detail-page-button.js
+++ b/src/plugins/detail-page-button.js
@@ -23,7 +23,7 @@ class DetailPageButtonPlugin extends BasePlugin {
}
async createMenuBtn() {
const pageInfo = this.getPageInfo(), carNum2 = pageInfo.carNum,
- buttonsHtml = '\n \n
\n \n \n \n \n
\n \n
\n \n \n \n \n
\n
\n ';
+ buttonsHtml = '\n \n
\n \n \n \n \n
\n \n
\n \n \n \n \n
\n
\n ';
isJavDb && $(".tabs").after(buttonsHtml);
isJavBus && $("#mag-submit-show").before(buttonsHtml);
$("#favoriteBtn").on("click", (() => this.favoriteOne()));
@@ -43,19 +43,38 @@ class DetailPageButtonPlugin extends BasePlugin {
}
});
}));
- const highlightMagnetPlugin = this.getBean("HighlightMagnetPlugin"), enableMagnetsFilter = await storageManager.getSetting("enableMagnetsFilter", YES);
- $("#magnets-span").text(enableMagnetsFilter === YES ? "关闭磁力过滤" : "开启磁力过滤");
- enableMagnetsFilter === YES && highlightMagnetPlugin.doFilterMagnet();
+ const highlightMagnetPlugin = this.getBean("HighlightMagnetPlugin"),
+ enableMagnetsFilter = await storageManager.getSetting("enableMagnetsFilter", NO);
+ if (enableMagnetsFilter === YES) {
+ const savedSettings = await storageManager.getSetting("magnetSortAndFilter", "{}");
+ let settings;
+ try {
+ settings = JSON.parse(savedSettings);
+ } catch (e) {
+ settings = {};
+ }
+ const sortEnabled = settings.sortEnabled || {};
+ const fullSortOrder = (settings.sortOrder && settings.sortOrder.length === 3) ? settings.sortOrder : ["date", "size", "files"];
+ const effectiveSortOrder = fullSortOrder.filter(key => sortEnabled[key]);
+ highlightMagnetPlugin.applySortAndFilter({
+ filterHD: settings.filterHD || !1,
+ filter4K: settings.filter4K || !1,
+ filterSubtitle: settings.filterSubtitle || !1,
+ filterUncensored: settings.filterUncensored || !1,
+ sortOrder: effectiveSortOrder,
+ });
+ $("#magnets-span").text("取消磁力过滤");
+ } else {
+ $("#magnets-span").text("排序与过滤");
+ }
$("#enable-magnets-filter").on("click", (event => {
- let $span = $("#magnets-span");
- if ("关闭磁力过滤" === $span.text()) {
+ const $span = $("#magnets-span");
+ if ("取消磁力过滤" === $span.text()) {
highlightMagnetPlugin.showAll();
- $span.text("开启磁力过滤");
+ $span.text("排序与过滤");
storageManager.saveSettingItem("enableMagnetsFilter", NO);
} else {
- highlightMagnetPlugin.doFilterMagnet();
- $span.text("关闭磁力过滤");
- storageManager.saveSettingItem("enableMagnetsFilter", YES);
+ this.openMagnetSortFilterPanel(highlightMagnetPlugin);
}
}));
$("#subtitleSearchBtn").on("click", (() => {
@@ -66,6 +85,186 @@ class DetailPageButtonPlugin extends BasePlugin {
// $("#xunLeiSubtitleBtn").on("click", (() => this.searchXunLeiSubtitle(carNum2)));
this.showStatus(carNum2).then();
}
+
+ async openMagnetSortFilterPanel(highlightMagnetPlugin) {
+ const existingPanel = document.getElementById("magnet-sort-filter-panel");
+ if (existingPanel) {
+ existingPanel.remove();
+ return;
+ }
+ const savedSettings = await storageManager.getSetting("magnetSortAndFilter", "{}");
+ let settings;
+ try {
+ settings = JSON.parse(savedSettings);
+ } catch (e) {
+ settings = {};
+ }
+ const sortEnabled = settings.sortEnabled || {date: !1, size: !1, files: !1};
+ const sortOrder = (settings.sortOrder && settings.sortOrder.length === 3) ? settings.sortOrder : ["date", "size", "files"];
+ const filterHD = settings.filterHD || !1;
+ const filter4K = settings.filter4K || !1;
+ const filterSubtitle = settings.filterSubtitle || !1;
+ const filterUncensored = settings.filterUncensored || !1;
+ const SORT_LABELS = {date: "发布日期", size: "文件大小", files: "文件数量"};
+ const SORT_HINTS = {date: "从新到旧", size: "从大到小", files: "从少到多"};
+ const FILTER_ITEMS = [
+ {key: "HD", label: "高清", checked: filterHD},
+ {key: "4K", label: "4K", checked: filter4K},
+ {key: "Subtitle", label: "字幕", checked: filterSubtitle},
+ {key: "Uncensored", label: "无码", checked: filterUncensored},
+ ];
+ const currentSortOrder = [...sortOrder];
+ const currentSortEnabled = {...sortEnabled};
+ const currentFilters = {
+ HD: filterHD,
+ "4K": filter4K,
+ Subtitle: filterSubtitle,
+ Uncensored: filterUncensored,
+ };
+ const buildSortItemsHTML = () => {
+ return currentSortOrder.map(key => {
+ const checked = currentSortEnabled[key] ? "checked" : "";
+ return `
+ ⋮⋮
+
+
`;
+ }).join("");
+ };
+ const buildFilterItemsHTML = () => {
+ return FILTER_ITEMS.map(item => {
+ const checked = currentFilters[item.key] ? "checked" : "";
+ return ``;
+ }).join("");
+ };
+ const panelHTML = `
+
+
+
+
+
+
排序优先级(拖动调整顺序,勾选启用)
+
+ ${buildSortItemsHTML()}
+
+
+
+
过滤条件(可多选,并集关系)
+
+ ${buildFilterItemsHTML()}
+
+
+
+
+
+ `;
+ const wrapper = document.createElement("div");
+ wrapper.innerHTML = panelHTML;
+ document.body.appendChild(wrapper);
+ const panel = document.getElementById("magnet-sort-filter-panel");
+ const overlay = document.getElementById("msf-overlay");
+ const closePanel = () => {
+ panel.remove();
+ overlay.remove();
+ };
+ overlay.addEventListener("click", closePanel);
+ document.getElementById("msf-close-btn").addEventListener("click", closePanel);
+ document.getElementById("msf-cancel-btn").addEventListener("click", closePanel);
+ const rebuildSortList = () => {
+ const sortList = document.getElementById("msf-sort-list");
+ sortList.innerHTML = buildSortItemsHTML();
+ bindSortEvents();
+ };
+ const bindSortEvents = () => {
+ const sortItems = document.querySelectorAll("#msf-sort-list .msf-sort-item");
+ sortItems.forEach(item => {
+ item.addEventListener("dragstart", (e) => {
+ e.dataTransfer.effectAllowed = "move";
+ e.dataTransfer.setData("text/plain", item.dataset.sortKey);
+ item.classList.add("msf-dragging");
+ });
+ item.addEventListener("dragend", () => {
+ item.classList.remove("msf-dragging");
+ document.querySelectorAll("#msf-sort-list .msf-sort-item").forEach(el => el.classList.remove("msf-drag-over"));
+ });
+ item.addEventListener("dragover", (e) => {
+ e.preventDefault();
+ e.dataTransfer.dropEffect = "move";
+ item.classList.add("msf-drag-over");
+ });
+ item.addEventListener("dragleave", () => {
+ item.classList.remove("msf-drag-over");
+ });
+ item.addEventListener("drop", (e) => {
+ e.preventDefault();
+ item.classList.remove("msf-drag-over");
+ const fromKey = e.dataTransfer.getData("text/plain");
+ const toKey = item.dataset.sortKey;
+ if (fromKey === toKey) return;
+ const fromIdx = currentSortOrder.indexOf(fromKey);
+ const toIdx = currentSortOrder.indexOf(toKey);
+ currentSortOrder.splice(fromIdx, 1);
+ currentSortOrder.splice(toIdx, 0, fromKey);
+ rebuildSortList();
+ });
+ const checkbox = item.querySelector(".msf-sort-checkbox");
+ checkbox.addEventListener("change", () => {
+ currentSortEnabled[item.dataset.sortKey] = checkbox.checked;
+ });
+ });
+ };
+ bindSortEvents();
+ document.querySelectorAll(".msf-filter-checkbox").forEach(cb => {
+ cb.addEventListener("change", () => {
+ currentFilters[cb.dataset.filterKey] = cb.checked;
+ });
+ });
+ document.getElementById("msf-confirm-btn").addEventListener("click", async () => {
+ try {
+ const enabledSortOrder = currentSortOrder.filter(key => currentSortEnabled[key]);
+ const newSettings = {
+ sortOrder: currentSortOrder,
+ sortEnabled: currentSortEnabled,
+ filterHD: currentFilters.HD,
+ filter4K: currentFilters["4K"],
+ filterSubtitle: currentFilters.Subtitle,
+ filterUncensored: currentFilters.Uncensored,
+ };
+ await storageManager.saveSettingItem("magnetSortAndFilter", JSON.stringify(newSettings));
+ const hasAnyFilter = currentFilters.HD || currentFilters["4K"] || currentFilters.Subtitle || currentFilters.Uncensored;
+ const hasAnySort = enabledSortOrder.length > 0;
+ if (hasAnyFilter || hasAnySort) {
+ await highlightMagnetPlugin.applySortAndFilter({
+ filterHD: currentFilters.HD,
+ filter4K: currentFilters["4K"],
+ filterSubtitle: currentFilters.Subtitle,
+ filterUncensored: currentFilters.Uncensored,
+ sortOrder: enabledSortOrder,
+ });
+ $("#magnets-span").text("取消磁力过滤");
+ await storageManager.saveSettingItem("enableMagnetsFilter", YES);
+ } else {
+ highlightMagnetPlugin.showAll();
+ $("#magnets-span").text("排序与过滤");
+ await storageManager.saveSettingItem("enableMagnetsFilter", NO);
+ }
+ } catch (e) {
+ console.error("[磁力排序] 应用失败:", e);
+ }
+ closePanel();
+ });
+ }
async showStatus(carNum2) {
const $filterBtn = $("#filterBtn span"), $favoriteBtn = $("#favoriteBtn span"), $hasDownBtn = $("#hasDownBtn span"), $hasWatchBtn = $("#hasWatchBtn span"), hotKeyDisplay = hotKey => hotKey ? `(${hotKey})` : "";
$filterBtn.text(`🚫 屏蔽 ${hotKeyDisplay(this.filterHotKey)}`);
diff --git a/src/plugins/highlight-magnet.js b/src/plugins/highlight-magnet.js
index a532ef6..1290bc9 100644
--- a/src/plugins/highlight-magnet.js
+++ b/src/plugins/highlight-magnet.js
@@ -5,26 +5,114 @@ class HighlightMagnetPlugin extends BasePlugin {
getName() {
return "HighlightMagnetPlugin";
}
- doFilterMagnet() {
- this.handleDb();
- this.handleBus();
+
+ parseDateFromAttr(dateStr) {
+ if (!dateStr || dateStr.length !== 8) {
+ const d = new Date(dateStr);
+ return isNaN(d.getTime()) ? 0 : d.getTime();
+ }
+ const y = parseInt(dateStr.substring(0, 4), 10);
+ const m = parseInt(dateStr.substring(4, 6), 10) - 1;
+ const d = parseInt(dateStr.substring(6, 8), 10);
+ const date = new Date(y, m, d);
+ return isNaN(date.getTime()) ? 0 : date.getTime();
}
- handleDb() {
+
+ getRowInfo(row) {
+ const $row = $(row);
+ const nameText = $row.find('.name').text().toLowerCase();
+ const tags = [];
+ $row.find('.tags .tag').each((_, tag) => tags.push($(tag).text().toLowerCase()));
+ const sizeMB = parseInt($row.attr('data-size'), 10) || 0;
+ const files = parseInt($row.attr('data-files'), 10) || 0;
+ const dateStr = $row.attr('data-date') || '';
+ return {
+ row: row,
+ $row: $row,
+ nameText: nameText,
+ date: this.parseDateFromAttr(dateStr),
+ dateStr: dateStr,
+ size: sizeMB * 1024 * 1024,
+ files: files,
+ tags: tags,
+ is4K: nameText.includes('4k') || nameText.includes('[4k]') || tags.includes('4k'),
+ isHD: nameText.includes('高清') || tags.includes('高清'),
+ isSubtitle: nameText.includes('字幕') || nameText.includes('-c') || nameText.includes('.chs') || nameText.includes('.cht') || nameText.includes('.chi') || tags.includes('字幕'),
+ isUncensored: nameText.includes('无码') || nameText.includes('無碼') || nameText.includes('-u') || nameText.includes('-uc') || tags.includes('无码') || tags.includes('無碼'),
+ };
+ }
+
+ async applySortAndFilter(settings) {
if (!isJavDb) return;
- let magnetNameList = $("#magnets-content .name");
- if (0 === magnetNameList.length) return;
- const HIGH_QUALITY_KEYWORDS = [ "4k", "-c", "-u", "-uc" ];
- let hasHighQuality = !1;
- magnetNameList.each(((_, el) => {
- const $el = $(el), text = $el.text().toLowerCase(), isHighQuality = HIGH_QUALITY_KEYWORDS.some((keyword => text.includes(keyword)));
- $el.parent().parent().parent().addClass("magnet-row");
- text.includes("4k") && $el.css("color", "#f40");
- if (isHighQuality) {
- hasHighQuality = !0;
- $el.parent().parent().parent().addClass("high-quality");
+ const $container = $("#magnets-content");
+ const $rows = $container.find(".item[data-rank]");
+ if (0 === $rows.length) return;
+ const rowInfos = $rows.toArray().map(row => this.getRowInfo(row));
+ const {filterHD, filter4K, filterSubtitle, filterUncensored, sortOrder} = settings;
+ const anyFilterEnabled = filterHD || filter4K || filterSubtitle || filterUncensored;
+ let visibleInfos = rowInfos;
+ if (anyFilterEnabled) {
+ visibleInfos = rowInfos.filter(info => {
+ if (filterHD && info.isHD) return !0;
+ if (filter4K && info.is4K) return !0;
+ if (filterSubtitle && info.isSubtitle) return !0;
+ if (filterUncensored && info.isUncensored) return !0;
+ return !1;
+ });
+ }
+ if (sortOrder && sortOrder.length > 0) {
+ visibleInfos.sort((a, b) => {
+ for (const key of sortOrder) {
+ let cmp = 0;
+ switch (key) {
+ case 'date':
+ cmp = b.date - a.date;
+ break;
+ case 'size':
+ cmp = b.size - a.size;
+ break;
+ case 'files':
+ cmp = a.files - b.files;
+ break;
+ }
+ if (0 !== cmp) return cmp;
+ }
+ return 0;
+ });
+ }
+ const container = document.getElementById("magnets-content");
+ if (!container) return;
+ const hiddenInfos = rowInfos.filter(info => !visibleInfos.includes(info));
+ const reviewsContainer = document.getElementById("reviewsContainer");
+ const insertRef = reviewsContainer && reviewsContainer.parentNode === container
+ ? (reviewsContainer.previousElementSibling || reviewsContainer)
+ : null;
+ const fragment = document.createDocumentFragment();
+ visibleInfos.forEach(info => fragment.appendChild(info.row));
+ hiddenInfos.forEach(info => fragment.appendChild(info.row));
+ if (insertRef) {
+ container.insertBefore(fragment, insertRef);
+ } else {
+ container.appendChild(fragment);
+ }
+ visibleInfos.forEach(info => {
+ if (info.is4K) {
+ info.$row.addClass('magnet-4k-highlight');
}
- }));
- hasHighQuality ? $("#magnets-content .magnet-row").not(".high-quality").hide() : $("#enable-magnets-filter").addClass("do-hide");
+ });
+ hiddenInfos.forEach(info => $(info.row).hide());
+ }
+
+ showAll() {
+ if (isJavDb) {
+ $("#magnets-content .item[data-rank]").toArray().forEach((el => {
+ $(el).show();
+ $(el).removeClass('magnet-4k-highlight');
+ }));
+ }
+ if (isJavBus) {
+ $("#magnet-table tr").toArray().forEach((el => $(el).show()));
+ }
}
handleBus() {
isJavBus && window.isDetailPage && utils.loopDetector((() => $("#magnet-table td a").length > 0), (() => {
@@ -44,12 +132,6 @@ class HighlightMagnetPlugin extends BasePlugin {
})) : $("#enable-magnets-filter").addClass("do-hide");
}));
}
- showAll() {
- if (isJavDb) {
- $("#magnets-content .item").toArray().forEach((el => $(el).show()));
- }
- isJavBus && $("#magnet-table tr").toArray().forEach((el => $(el).show()));
- }
}