B站广告替换为Pixiv推荐图片

移除B站首页推荐中的所有推广视频广告,包括小火箭🚀,漫画,纪录片等,以及正统广告。使用Pixiv推荐图片替换广告内容。需要提前登陆过pixiv账号,不需要Cookies或者账号token。

当前为 2025-03-01 提交的版本,查看 最新版本

// ==UserScript==
// @name           B站广告替换为Pixiv推荐图片
// @name:en        Bilibili Ad Replacement with Pixiv Recommended Images
// @namespace      http://tampermonkey.net/
// @version        1.4.1
// @description    移除B站首页推荐中的所有推广视频广告,包括小火箭🚀,漫画,纪录片等,以及正统广告。使用Pixiv推荐图片替换广告内容。需要提前登陆过pixiv账号,不需要Cookies或者账号token。
// @description:en Remove promotional video ads from Bilibili's homepage recommendations, including small rocket 🚀 ads and regular ads. Use Pixiv recommended images to replace the ads. A Pixiv account must be logged in beforehand, but no cookies or account tokens are required.
// @author         RecycleBee
// @match          *://www.bilibili.com/*
// @match          *://www.pixiv.net/en/*
// @grant          GM_openInTab
// @grant          GM_addValueChangeListener
// @grant          GM_setValue
// @grant          GM_getValue
// @grant          GM_xmlhttpRequest
// @license MIT

// ==/UserScript==

(function() {
    'use strict';

    const currentUrl = window.location.href;


    // === 处理 Pixiv 页面 ===
    if (currentUrl.includes("pixiv.net/en/")) {
        console.log("少女祈祷中...");

        function waitForElements(selector, callback, timeout = 10000) {
            const startTime = Date.now();
            const checkInterval = 500;

            function check() {
                let elements = document.querySelectorAll(selector);
                if (elements.length > 0) {
                    callback(elements);
                } else if (Date.now() - startTime < timeout) {
                    setTimeout(check, checkInterval);
                } else {
                    console.warn("超时,未找到目标元素");
                }
            }

            check();
        }

        function closePage() {
            window.close();
        }

        async function fetchIllustDetails(pixivID) {
            const apiUrl = `https://www.pixiv.net/ajax/illust/${pixivID}`;

            try {
                const response = await fetch(apiUrl);
                const data = await response.json();

                if (data.body && data.body.urls) {
                    let imgUrl = data.body.urls.original;
                    imgUrl = imgUrl.replace(/\\/g, "");
                    const dateMatch = imgUrl.match(/\/(\d{4})\/(\d{2})\/(\d{2})\/(\d{2})\/(\d{2})\/(\d{2})\/(\d+)/);
                    if (dateMatch) {
                        const [_, year, month, day, hour, minute, second, id] = dateMatch;

                        const newImgUrl = `https://i.pixiv.cat/c/360x360_70/img-master/img/${year}/${month}/${day}/${hour}/${minute}/${second}/${id}_p0_square1200.jpg`;
                        const formattedDate = `${year}-${month}-${day}`;

                        const illustTitle = data.body.title;
                        const authorId = data.body.tags.authorId;
                        const userUrl = `https://www.pixiv.net/en/users/${authorId}`;
                        const artworkUrl = `https://www.pixiv.net/en/artworks/${pixivID}`;
                        const username = data.body.userName;
                        console.log(`作品ID ${pixivID} 缩略URL:${newImgUrl}`);
                        return {
                            imgUrl: newImgUrl,
                            illustTitle,
                            artworkUrl,
                            userUrl,
                            date: formattedDate,
                            username,
                        };

                    } else {
                        console.warn(`无法提取${pixivID}`);
                        return null;
                    }
                }
            } catch (error) {
                console.error("获取信息时报错:", error);
                return null;
            }
        }

        async function fetchAllPixivUrls(uniquePixivIDs) {
            let imgUrls = [];
            let additionalData = [];
            for (const pixivID of uniquePixivIDs) {
                const result = await fetchIllustDetails(pixivID);
                if (result) {
                    imgUrls.push(result.imgUrl); // 添加图片URL
                    additionalData.push({
                        title: result.illustTitle,
                        artworkUrl: result.artworkUrl,
                        userUrl: result.userUrl,
                        date: result.date,
                        username: result.username,
                    });
                }
            }
            GM_setValue("pixivImgUrls", imgUrls);
            GM_setValue("pixivAdditionalData", additionalData);
            return { imgUrls, additionalData };
        }


        if (GM_getValue("isFetchingPixiv", false)) {
            console.log("少女不用祈祷...");
            return;
        }

        waitForElements("div.gtm-toppage-thumbnail-illustration-recommend-works-zone", async function(divs) {
            let pixivIDs = new Set(); // 去重
            divs.forEach(div => {
                div.querySelectorAll('a[href^="/en/artworks/"]').forEach(anchor => {
                    let href = anchor.getAttribute("href");
                    let match = href.match(/^\/en\/artworks\/(\d+)/);
                    if (match) {
                        pixivIDs.add(match[1]);
                    }
                });
            });

            let uniquePixivIDs = Array.from(pixivIDs); // 转为数组
            await fetchAllPixivUrls(uniquePixivIDs);
            GM_setValue("pixivFetched", true);
            closePage();
        });
        return;

    }

    // === 处理 Bilibili 页面 ===

    // 预加载图片
    function preloadImages(imageUrls) {
        imageUrls.forEach(url => {
            if (!preloadedImages.some(img => img.src === url)) {
                let img = new Image();
                img.src = url;
                preloadedImages.push(img);
            }
        });
    }



    function removeAds() {
        // 1.处理未替换的广告
        document.querySelectorAll('.bili-video-card.is-rcmd').forEach(card => {
            if (!card.classList.contains('enable-no-interest')) {
                let imageLink = card.querySelector('.bili-video-card__image--link');

                if (imageLink) {
                    // 获取父元素的宽度
                    let parentWidth = card.offsetWidth;
                    let parentHeight = parentWidth * (9 / 16);

                    let placeholder = document.createElement("div");
                    placeholder.style.cssText = `
                    position: relative;
                    width: 100%;
                    height: 0;
                    padding-top: 56.25%;
                    background: #f4f4f4;
                    border-radius: 8px;
                    border: 1px dashed #ccc;
                    margin: auto;
                `;

                    let textContainer = document.createElement("div");
                    textContainer.style.cssText = `
                    position: absolute;
                    top: 50%;
                    left: 50%;
                    transform: translate(-50%, -50%);
                    color: #888;
                    font-size: 14px;
                    font-weight: bold;
                    text-align: center;
                    width: 100%;
                `;
                    textContainer.innerText = "🚫 广告已屏蔽";
                    placeholder.appendChild(textContainer);

                    imageLink.replaceWith(placeholder);

                    // 清空文字内容,但保留元素结构,不知道为什么一定得要有字符在innerText,空格还不行。
                    let placeholderText = "\u200B";
                    let titleElement = card.querySelector('.bili-video-card__info--tit');
                    if (titleElement) {
                        let link = titleElement.querySelector('a');
                        if (link) {
                            link.innerText = placeholderText;
                        }
                    }

                    let authorElement = card.querySelector('.bili-video-card__info--author');
                    if (authorElement) authorElement.innerText = placeholderText;

                    let dateElement = card.querySelector('.bili-video-card__info--date');
                    if (dateElement) dateElement.innerText = placeholderText;

                    let creativeAd = card.querySelector('.vui_icon.bili-video-card__info--creative-ad');
                    if (creativeAd) creativeAd.remove();

                    let adInfo = card.querySelector('.bili-video-card__info--ad');
                    if (adInfo) adInfo.remove();


                    isPixivImageLoaded = false;
                    processAdsOrPlaceholders(placeholder);
                }
            }
        });

        // 处理已经替换成占位符的广告封面
        document.querySelectorAll('div').forEach(placeholder => {
            if (placeholder.innerText === "🚫 广告已屏蔽") {
                processAdsOrPlaceholders(placeholder);
            }
        });
    }


    function processAdsOrPlaceholders(element) {
        let pixivImgUrls = GM_getValue("pixivImgUrls", []);
        let additionalData = GM_getValue("pixivAdditionalData", []);
        if (pixivImgUrls.length>0) {
            let imgUrl = pixivImgUrls.shift();
            let { artworkUrl, title, date, username, userUrl} = additionalData.shift();

            if (imgUrl) {
                preloadImages(pixivImgUrls); // 预加载剩余图片

                let img = document.createElement("img");
                img.src = imgUrl;
                img.alt = "Pixiv 图片";
                img.style.cssText = `
                position: absolute;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                object-fit: cover;
                border-radius: 8px;
            `;

                //包裹图片
                let link = document.createElement("a");
                link.href = artworkUrl;
                link.target = "_blank";
                link.style.display = "block";
                link.appendChild(img);

                //图片容器
                let imgContainer = document.createElement("div");
                imgContainer.style.cssText = `
                position: relative;
                width: 100%;
                height: 0;
                padding-top: 56.25%;
                background: #f4f4f4;
                border-radius: 8px;
                overflow: hidden;
            `;

                imgContainer.appendChild(link);
                element.removeAttribute('style');
                element.innerHTML = ""; // 清空原内容
                element.appendChild(imgContainer);

                // 更新其他信息
                let titleContainer = element.closest('.bili-video-card').querySelector('.bili-video-card__info--tit');
                if (titleContainer) {
                    titleContainer.title = title;
                    let titleElement = titleContainer.querySelector('a');
                    if (titleElement) {
                        titleElement.innerText = title;
                        titleElement.href = artworkUrl;
                        titleElement.title = title;
                    }
                }

                let ownerElement = element.closest('.bili-video-card').querySelector('.bili-video-card__info--owner');
                if (ownerElement) ownerElement.href = userUrl;

                let authorElement = element.closest('.bili-video-card').querySelector('.bili-video-card__info--author');
                if (authorElement) {
                    authorElement.innerText = username;
                    authorElement.title = username;
                }

                let dateElement = element.closest('.bili-video-card').querySelector('.bili-video-card__info--date');
                if (dateElement) dateElement.innerText = "· " + date;

                // 删除广告标识
                element.closest('.bili-video-card').querySelectorAll('.vui_icon.bili-video-card__info--creative-ad, .bili-video-card__info--ad')
                    .forEach(el => el.remove());

                // 标记 Pixiv 图片已加载
                isPixivImageLoaded = true;

                // 更新存储
                GM_setValue("pixivImgUrls", pixivImgUrls);
                console.log("ID剩余:"+pixivImgUrls.length);
                GM_setValue("pixivAdditionalData", additionalData);
            }
            if (pixivImgUrls.length <= minThreshold && !isFetchingPixiv) {
                console.log(`图片少于 ${minThreshold} 张(当前 ${pixivImgUrls.length} 张),重新抓取...`);
                isFetchingPixiv = true;
                GM_setValue("pixivFetched", false);
                GM_openInTab("https://www.pixiv.net/en/", { active: false, insert: true, setParent: true });

            }
        }
    }



    function removeSpecificElements() {
        // 1.删除所有纪录片、漫画等
        document.querySelectorAll('div[data-v-128321c6]').forEach(element => {
            element.remove();
        });

        // 2.删除所有瀑布流的广告
        document.querySelectorAll('div[data-v-3581b8d4]').forEach(element => {
            if (!element.closest('.feed-card') && element.classList.contains('bili-video-card') && element.classList.contains('is-rcmd') && element.classList.length === 2) {
                element.remove();
            }
        });
    }

    console.log("Bilibili 运行,检测是否需要抓取 Pixiv 图片...");
    let isFetchingPixiv = false;
    let pixivImgUrls = GM_getValue("pixivImgUrls", []); // 检查是否有 URLs
    let minThreshold = 3; // 设定最少剩余图片数,低于这个值就触发抓取

    if (pixivImgUrls.length <= minThreshold && !isFetchingPixiv) {
        console.log(`图片少于 ${minThreshold} 张,重新抓取...`);
        let isFetchingPixiv = true;
        let tab = GM_openInTab("https://www.pixiv.net/en/", { active: false, insert: true, setParent: true });
    }


    // 让火焰净化一切!!!

    let preloadedImages = [];
    removeSpecificElements();
    GM_addValueChangeListener("pixivFetched", (name, oldValue, newValue, remote) => {
        if (newValue === true) {
            console.log("Pixiv 图片已抓取,等待 DOM 加载后更新广告...");
            isPixivImageLoaded = false;
            isFetchingPixiv = false;

            let observer = new MutationObserver(() => {
                if (document.querySelector('.bili-video-card')) {
                    removeAds();
                    observer.disconnect(); // 只执行一次
                }
            });
            observer.observe(document.body, { childList: true, subtree: true });

            setTimeout(() => {
                if (document.body) {
                    removeAds();
                } else {
                    console.warn("DOM未加载");
                    setTimeout(removeAds, 500);
                }
            }, 500);
        }
    });

    let isPixivImageLoaded = false;
    removeAds();

    // 监听 DOM
    let observer = new MutationObserver(() => {
        removeSpecificElements();
        removeAds();
    });
    observer.observe(document.body, { childList: true, subtree: true });

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址