豆瓣图书多平台资源搜索

豆瓣图书页显示多平台搜索按钮 + 微信读书推荐值 + Goodreads评分

// ==UserScript==
// @name           豆瓣图书多平台资源搜索
// @description    豆瓣图书页显示多平台搜索按钮 + 微信读书推荐值 + Goodreads评分
// @author         bai
// @version        1.9
// @icon           https://book.douban.com/favicon.ico
// @grant          GM_addStyle
// @grant          GM_xmlhttpRequest
// @require        https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js
// @include        https://book.douban.com/subject/*
// @run-at         document-end
// @license        Apache-2.0
// @namespace      douban_book_multi_search
// ==/UserScript==

$(document).ready(function () {
    // 注入样式,新增 Goodreads 评分相关样式
    GM_addStyle(`
        #multi_book_search { margin: 10px 0; padding: 12px; background: #f0f7f7; border-radius: 6px; }
        .search_platforms { display: grid; grid-template-columns: 1fr; gap: 8px; }
        .search_platform { padding: 8px; border-radius: 4px; background: white; display: flex; align-items: center; gap: 8px; }
        .platform_header { font-weight: bold; min-width: 80px; }
        .search_btn { padding: 3px 8px; border: 1px solid #ccc; background: #f0f7f7; color: #333; border-radius: 3px; text-decoration: none; font-size: 12px; }
        .search_btn:hover { background: #f2f2f2; color: #000; }
        .weread_rating, .goodreads_rating { font-size: 12px; color: #333; background: #f8f8f8; padding: 2px 6px; border-radius: 4px; }
        .weread_loading, .goodreads_loading { color: #666; font-size: 12px; }
        .weread_error, .goodreads_error { color: #e53935; font-size: 12px; }
        .debug_hint { font-size: 11px; color: #999; margin-top: 5px; }
    `);

    // 提取豆瓣图书信息
    function getDoubanBookInfo() {
        const title = $('#wrapper > h1 > span').first().text().replace(/[:\(].*$/, '').trim();
        const author = $('#info span.pl:contains("作者")').next().text().replace(/\s+/g, ' ').trim() || '';
        console.log("[多平台搜索] 提取到图书信息:", { title, author }); // 调试日志
        return { title, author };
    }

    const bookInfo = getDoubanBookInfo();
    if (!bookInfo.title) {
        console.log("[多平台搜索] 未提取到图书标题,终止执行");
        return;
    }

    // 获取微信读书推荐值(增强版,带详细日志)
    function getWeReadRating(title, author) {
        const encodedTitle = encodeURIComponent(title);
        const url = `https://weread.qq.com/web/search/books?keyword=${encodedTitle}`;
        console.log("[微信读书] 开始请求推荐值,URL:", url);

        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: url,
                onload: function (response) {
                    console.log("[微信读书] 请求成功,状态码:", response.status);

                    // 状态码不是200,直接失败
                    if (response.status!== 200) {
                        reject(new Error(`请求失败,状态码:${response.status}`));
                        return;
                    }

                    try {
                        const tempDiv = document.createElement('div');
                        tempDiv.innerHTML = response.responseText;

                        // 方案1:按类名查找(微信读书可能用的类名)
                        let rating = null;
                        const possibleSelectors = [
                            // 常见的推荐值元素选择器(多写几个备用)
                            '.wr_bookList_item_reading_percent',
                            '.rating-percent',
                            '.book-rating .percent',
                            '.reading-percent'
                        ];

                        // 逐个尝试选择器
                        for (const selector of possibleSelectors) {
                            const elem = tempDiv.querySelector(selector);
                            if (elem) {
                                rating = elem.textContent.trim();
                                console.log(`[微信读书] 用选择器 ${selector} 找到推荐值:`, rating);
                                break;
                            }
                        }

                        // 方案2:按文本内容查找(如果类名变了,但文本包含“推荐值”)
                        if (!rating) {
                            const allTextNodes = tempDiv.querySelectorAll('*');
                            for (const node of allTextNodes) {
                                const text = node.textContent || '';
                                if (text.includes('推荐值') && text.includes('%')) {
                                    // 提取类似“推荐值 93.1%”中的百分比
                                    const match = text.match(/推荐值\s*([\d.]+%)/);
                                    if (match && match[1]) {
                                        rating = match[1];
                                        console.log("[微信读书] 按文本匹配找到推荐值:", rating);
                                        break;
                                    }
                                }
                            }
                        }

                        // 方案3:如果有作者信息,尝试匹配更精准的结果
                        if (!rating && author) {
                            console.log("[微信读书] 尝试结合作者信息匹配...");
                            const bookItems = tempDiv.querySelectorAll('.wr_bookList_item, .book-item');
                            for (const item of bookItems) {
                                const authorText = item.textContent || '';
                                if (authorText.includes(author.replace(/\s+/g, ''))) {
                                    // 找到包含作者名的条目,再从中找推荐值
                                    const percentElem = item.querySelector(possibleSelectors.join(', '));
                                    if (percentElem) {
                                        rating = percentElem.textContent.trim();
                                        console.log("[微信读书] 结合作者匹配找到推荐值:", rating);
                                        break;
                                    }
                                }
                            }
                        }

                        if (rating) {
                            resolve(rating);
                        } else {
                            console.log("[微信读书] 所有方案都未找到推荐值,HTML片段:", response.responseText.substring(0, 500)); // 打印前500字符方便调试
                            resolve(null);
                        }
                    } catch (e) {
                        console.error("[微信读书] 解析HTML出错:", e);
                        reject(e);
                    }
                },
                onerror: function (error) {
                    console.error("[微信读书] 请求出错:", error);
                    reject(error);
                },
                onabort: function () {
                    reject(new Error("请求被中止"));
                },
                timeout: 15000 // 延长超时时间到15秒
            });
        });
    }

    // 获取 Goodreads 评分
    function getGoodreadsRating(title, author) {
        const encodedTitle = encodeURIComponent(title);
        const url = `https://www.goodreads.com/search?q=${encodedTitle}&search_type=books`;
        console.log("[Goodreads] 开始请求评分,URL:", url);

        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: url,
                onload: function (response) {
                    console.log("[Goodreads] 请求成功,状态码:", response.status);

                    if (response.status!== 200) {
                        reject(new Error(`请求失败,状态码:${response.status}`));
                        return;
                    }

                    try {
                        const tempDiv = document.createElement('div');
                        tempDiv.innerHTML = response.responseText;

                        // 定位第一本图书的评分元素,根据页面结构调整选择器
                        const firstBookRatingElem = tempDiv.querySelector('tr[itemtype="http://schema.org/Book"] .minirating');
                        if (firstBookRatingElem) {
                            const ratingText = firstBookRatingElem.textContent.trim();
                            // 提取评分数值,如从 "4.08 avg rating — 466,511 ratings" 中提取 4.08
                            const ratingMatch = ratingText.match(/(\d+\.\d+)\s+avg rating/);
                            if (ratingMatch && ratingMatch[1]) {
                                const rating = ratingMatch[1];
                                console.log("[Goodreads] 找到评分:", rating);
                                resolve(rating);
                            } else {
                                console.log("[Goodreads] 未匹配到有效评分格式,原始文本:", ratingText);
                                resolve(null);
                            }
                        } else {
                            console.log("[Goodreads] 未找到第一本图书的评分元素");
                            resolve(null);
                        }
                    } catch (e) {
                        console.error("[Goodreads] 解析HTML出错:", e);
                        reject(e);
                    }
                },
                onerror: function (error) {
                    console.error("[Goodreads] 请求出错:", error);
                    reject(error);
                },
                onabort: function () {
                    reject(new Error("请求被中止"));
                },
                timeout: 15000
            });
        });
    }

    // 配置多平台搜索
    const searchPlatforms = [
        {
            name: "微信读书",
            icon: "📖",
            searchUrl: `https://weread.qq.com/web/search/books?keyword=${encodeURIComponent(bookInfo.title)}`,
            hasRating: true
        },
        {
            name: "Goodreads",
            icon: "🔖",
            searchUrl: `https://www.goodreads.com/search?q=${encodeURIComponent(bookInfo.title)}&search_type=books`,
            hasRating: true
        },
        // 其他平台...
        { name: "Z站", icon: "📚", searchUrl: `https://z-library.ec/s/${encodeURIComponent(bookInfo.title)}?` },
        { name: "安娜读书", icon: "📚︎", searchUrl: `https://annas-archive.org/search?q=${encodeURIComponent(bookInfo.title)}` },
        { name: "SaltyLeo书架", icon: "🎒", searchUrl: `https://tstrs.me/search?search_type=default&q=${encodeURIComponent(bookInfo.title)}` },
        { name: "喜马拉雅", icon: "🎧", searchUrl: `https://www.ximalaya.com/so/${encodeURIComponent([bookInfo.title, bookInfo.author].filter(Boolean).join('+'))}` },
    ];

    // 生成搜索模块,区分不同平台的加载占位
    function createSearchModule() {
        let moduleHtml = `
            <div id="multi_book_search">
                <h3>多平台资源搜索</h3>
                <div class="search_platforms">
        `;

        searchPlatforms.forEach((platform, index) => {
            let ratingPlaceholder = '';
            if (platform.name === "微信读书") {
                ratingPlaceholder = '<span class="weread_loading">获取推荐值中...</span>';
            } else if (platform.name === "Goodreads") {
                ratingPlaceholder = '<span class="goodreads_loading">获取评分中...</span>';
            }

            moduleHtml += `
                <div class="search_platform" id="platform_${index}">
                    <div class="platform_header">${platform.icon} ${platform.name}</div>
                    <a href="${platform.searchUrl}" target="_blank" class="search_btn">直达搜索</a>
                    ${ratingPlaceholder}
                </div>
            `;
        });

        moduleHtml += `
                </div>
            </div>
        `;
        return moduleHtml;
    }

    // 插入到页面
    $("#content div.aside").prepend(createSearchModule());

    // 获取并显示微信读书推荐值
    const weReadIndex = searchPlatforms.findIndex(p => p.name === "微信读书");
    if (weReadIndex!== -1) {
        getWeReadRating(bookInfo.title, bookInfo.author)
           .then(rating => {
                const $platform = $(`#platform_${weReadIndex}`);
                if (rating) {
                    $platform.find('.weread_loading').replaceWith(`
                        <span class="weread_rating">推荐值 ${rating}</span>
                    `);
                } else {
                    $platform.find('.weread_loading').replaceWith(`
                        <span class="weread_error">未找到推荐值</span>
                    `);
                }
            })
           .catch(err => {
                console.error("[微信读书] 推荐值获取失败:", err);
                $(`#platform_${weReadIndex} .weread_loading`).replaceWith(`
                    <span class="weread_error">获取失败(${err.message})</span>
                `);
            });
    }

    // 获取并显示 Goodreads 评分
    const goodreadsIndex = searchPlatforms.findIndex(p => p.name === "Goodreads");
    if (goodreadsIndex!== -1) {
        getGoodreadsRating(bookInfo.title, bookInfo.author)
           .then(rating => {
                const $platform = $(`#platform_${goodreadsIndex}`);
                if (rating) {
                    $platform.find('.goodreads_loading').replaceWith(`
                        <span class="goodreads_rating">评分 ${rating}</span>
                    `);
                } else {
                    $platform.find('.goodreads_loading').replaceWith(`
                        <span class="goodreads_error">未找到评分</span>
                    `);
                }
            })
           .catch(err => {
                console.error("[Goodreads] 评分获取失败:", err);
                $(`#platform_${goodreadsIndex} .goodreads_loading`).replaceWith(`
                    <span class="goodreads_error">获取失败(${err.message})</span>
                `);
            });
    }
});