Yande.re 手动加近期热门图

点击按钮加载指定时间范围内按分数排序的热门图,支持分级和翻页加载,性能更优,结果更准。

// ==UserScript==
// @name         Yande.re 手动加近期热门图
// @namespace    http://tampermonkey.net/
// @version      2.0
// @description  点击按钮加载指定时间范围内按分数排序的热门图,支持分级和翻页加载,性能更优,结果更准。
// @author       银蓝色 & Gemini
// @match        https://yande.re/post*
// @grant        GM_xmlhttpRequest
// @connect      yande.re
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // ==== 配置 ====
    const DAYS_BACK = 500;     // 搜索范围:最近 N 天
    const DEFAULT_TAGS = "";   // 可附加固定标签,如 genshin_impact。多个标签用空格隔开。
    const POSTS_PER_PAGE = 50; // 每次加载的图片数量(Yande.re API 上限为 100)
    // ==============

    let selectedRating = "safe"; // 默认分级
    let currentPage = 1;         // 当前加载的页码
    let isLoading = false;       // 请求状态锁,防止重复点击

    // --- 1. 创建初始操作界面 ---
    const uiBox = document.createElement("div");
    uiBox.style = "position:fixed; top:20px; right:20px; z-index:9999; background:#fff; padding:10px; border:1px solid #ccc; box-shadow: 0 2px 5px rgba(0,0,0,0.2);";

    const ratingSelect = document.createElement("select");
    ratingSelect.style = "padding: 5px; border: 1px solid #ccc;";
    ratingSelect.innerHTML = `
        <option value="safe">🟢 Safe</option>
        <option value="questionable">🟡 Questionable</option>
        <option value="explicit">🔴 Explicit</option>
        <option value="all">⚪️ All</option>
    `;
    ratingSelect.addEventListener("change", () => selectedRating = ratingSelect.value);

    const loadBtn = document.createElement("button");
    loadBtn.textContent = "📥 加载热门图";
    loadBtn.style = "margin-left:10px; padding: 5px 10px; cursor: pointer;";
    loadBtn.addEventListener("click", startLoading);

    uiBox.appendChild(ratingSelect);
    uiBox.appendChild(loadBtn);
    document.body.appendChild(uiBox);

    // --- 2. 初始化加载流程 ---
    let container, resultsContainer, loadMoreBtn;

    function startLoading() {
        // 移除初始按钮,创建结果容器
        uiBox.remove();
        initUI();
        // 首次加载第一页数据
        fetchAndRenderPage();
    }

    // --- 3. 创建结果显示区域 ---
    function initUI() {
        container = document.createElement('div');
        container.style = "margin:20px; padding:10px; background:#f5f5f5; border:1px solid #ddd;";
        const ratingText = selectedRating === 'all' ? 'ALL' : selectedRating.toUpperCase();
        container.innerHTML = `<h2>🔥 最近 ${DAYS_BACK} 天热门图片 (${ratingText})</h2>`;

        resultsContainer = document.createElement('div');
        resultsContainer.style = "display: flex; flex-wrap: wrap; justify-content: center;";
        container.appendChild(resultsContainer);

        loadMoreBtn = document.createElement('button');
        loadMoreBtn.textContent = '⏬ 加载更多...';
        loadMoreBtn.style = "display:block; width:80%; max-width:400px; margin: 20px auto; padding:12px 20px; font-size:16px; cursor: pointer; border: 1px solid #ccc;";
        loadMoreBtn.onclick = fetchAndRenderPage; // 点击加载下一页

        container.appendChild(loadMoreBtn);

        // 将结果容器插入到页面合适位置
        const insertTarget = document.querySelector("#post-list-posts") || document.body;
        insertTarget.prepend(container);
    }

    // --- 4. 核心:获取并渲染单页数据 ---
    function fetchAndRenderPage() {
        if (isLoading) return;
        isLoading = true;
        loadMoreBtn.textContent = '正在加载中...';
        loadMoreBtn.disabled = true;

        const sinceDate = new Date(new Date().getTime() - DAYS_BACK * 86400000);
        const sinceStr = sinceDate.toISOString().split("T")[0];

        // 构造查询标签
        let tags = `date:>${sinceStr} order:score`; // [修正] 核心改动:在查询中加入 order:score
        if (selectedRating !== "all") {
            tags += ` rating:${selectedRating}`;
        }
        if (DEFAULT_TAGS) {
            tags += ` ${DEFAULT_TAGS}`;
        }

        GM_xmlhttpRequest({
            method: "GET",
            url: `https://yande.re/post.json?tags=${encodeURIComponent(tags)}&page=${currentPage}&limit=${POSTS_PER_PAGE}`,
            onload: function (response) {
                const posts = JSON.parse(response.responseText);

                if (posts.length > 0) {
                    renderPosts(posts);
                    currentPage++; // 准备加载下一页
                    loadMoreBtn.textContent = '⏬ 加载更多...';
                    loadMoreBtn.disabled = false;
                } else {
                    loadMoreBtn.textContent = '✅ 已加载全部图片';
                    loadMoreBtn.disabled = true;
                }
                isLoading = false;
            },
            onerror: function(error) {
                console.error("Yande.re Script Error:", error);
                loadMoreBtn.textContent = '❌ 加载失败,请检查控制台';
                loadMoreBtn.disabled = false; // 允许重试
                isLoading = false;
            }
        });
    }

    // --- 5. 渲染图片到页面 ---
    function renderPosts(posts) {
        const fragment = document.createDocumentFragment();
        posts.forEach(post => {
            const div = document.createElement('div');
            div.style = "display:inline-block; margin:8px; text-align:center; width:180px; vertical-align: top; background: #fff; padding: 5px; border: 1px solid #ddd;";
            div.innerHTML = `
                <a href="/post/show/${post.id}" target="_blank" title="Tags: ${post.tags}">
                    <img src="${post.preview_url}" style="max-width:170px; height: 170px; object-fit: cover; border:1px solid #ccc;">
                </a>
                <div style="font-size:12px; margin-top:5px;">⭐ ${post.score}</div>
            `;
            fragment.appendChild(div);
        });
        resultsContainer.appendChild(fragment);
    }

})();