fudium

Tampermonkey/Greasemonkey script hack for Medium articles – zaps paywalls overlays nags so you can read without the noise. Not affiliated with Medium. Use at your own risk.

当前为 2025-08-25 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         fudium
// @namespace    https://github.com/ThoriqFathurrozi/
// @version      1.0
// @description  Tampermonkey/Greasemonkey script hack for Medium articles – zaps paywalls overlays nags so you can read without the noise. Not affiliated with Medium. Use at your own risk.
// @author       frrzyriq
// @match        https://medium.com
// @match        https://*.medium.com/*
// @icon         https://miro.medium.com/v2/5d8de952517e8160e40ef9841c781cdc14a5db313057fa3c3de41c6f5b494b19
// @grant        none
// @run-at       document-end
// @homepageURL  https://github.com/ThoriqFathurrozi/fudium
// @license      MIT; https://raw.githubusercontent.com/ThoriqFathurrozi/fudium/refs/heads/main/LICENSE
// ==/UserScript==

await(async function () {
    'use strict';

    const freedium = 'https://freedium.cfd/'

    function iconUnlock() {
        return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640" width="16" fill="white"><!--!Font Awesome Free v7.0.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M320 96c-35.3 0-64 28.7-64 64v64h192c35.3 0 64 28.7 64 64v224c0 35.3-28.7 64-64 64H192c-35.3 0-64-28.7-64-64V288c0-35.3 28.7-64 64-64v-64c0-70.7 57.3-128 128-128 63.5 0 116.1 46.1 126.2 106.7 2.9 17.4-8.8 33.9-26.3 36.9s-33.9-8.8-36.9-26.3c-5-30.2-31.3-53.3-63-53.3m40 328c13.3 0 24-10.7 24-24s-10.7-24-24-24h-80c-13.3 0-24 10.7-24 24s10.7 24 24 24z"/></svg>`;
    }

    function bannerTagArticle(link) {
        const banner = document.createElement("a");
        banner.href = freedium + link
        banner.id = "fudium-article-banner";
        banner.style.position = "absolute";
        banner.style.padding = "10px";
        banner.style.borderRadius = "5px";
        banner.style.color = "white";
        banner.style.zIndex = "498";
        banner.style.top = "0";
        banner.style.right = "0";
        banner.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
        banner.innerHTML = `${iconUnlock()} Open Free`;
        return banner;
    }

    function bannerPageArticle(link) {
        const banner = document.createElement("a");
        banner.href = freedium + link
        banner.id = "fudium-page-banner";
        banner.style.position = "fixed";
        banner.style.padding = "10px";
        banner.style.borderRadius = "5px";
        banner.style.color = "white";
        banner.style.zIndex = "498";
        banner.style.bottom = "50vh";
        banner.style.right = "20px";
        banner.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
        banner.innerHTML = `${iconUnlock()} Open Free`;
        return banner;
    }

    function waitForElementWithMutationObserver(selector, timeout) {
        return new Promise((resolve, reject) => {
            const observer = new MutationObserver((mutationsList, observer) => {
                const element = document.querySelectorAll(selector);
                if (element.length > 0) {
                    observer.disconnect();
                    resolve(element);
                }
            });

            observer.observe(document.body, { childList: true, subtree: true });

            // Set a timeout to prevent infinite waiting
            setTimeout(() => {
                observer.disconnect();
                reject(new Error(`Element with selector "${selector}" not found within timeout.`));
            }, timeout);
        });
    }

    function initElement() {
        waitForElementWithMutationObserver('article', 10000)
            .then(element => {
                element.forEach((e) => {
                    const linkElement = e.querySelector('div[role="link"]');
                    if (linkElement && !linkElement.querySelector('#fudium-article-banner') && checkMemberArticle(linkElement)) {
                        const rawLink = linkElement.dataset.href
                        linkElement.append(bannerTagArticle(rawLink));
                    }
                });
            })
            .catch(error => {
                console.error(error.message);
            });
    }

    function checkHeight() {
        return (document.height !== undefined) ? document.height : document.body.offsetHeight;
    }

    async function appendElement() {
        try {
            const articles = await waitForElementWithMutationObserver('article', 4000);
            if (articles.length === 0) {
                console.log("No articles found");
                return [];
            }
            return articles;
        } catch (error) {
            console.log(error)
            return [];
        }
    }


    function checkMemberArticle(element) {
        const isMember = element.querySelector('button[aria-label="Member-only story"]');
        return isMember !== null;

    }

    function isHash(str) {
        // Common hash lengths in hex
        const hashLengths = [12, 32, 40, 64, 128];
        // Check only hex characters and length
        return /^[a-f0-9]+$/i.test(str) && hashLengths.includes(str.length);
    }

    function checkIsPageArticle() {
        const url = document.URL.split('/');
        if (url.length >= 2 && isHash(url[url.length - 1].split('-').reverse()[0])) {
            return true;
        }
        return false;
    }

    function appendElementInPageArticle() {
        const isMember = document.querySelector('#paywallButton-programming');
        if (isMember) {
            document.querySelector("h1").after(bannerPageArticle(document.URL));
        }
    }

    async function init() {
        let screen = checkHeight();
        if (!checkIsPageArticle()) {
            initElement();
        } else {
            appendElementInPageArticle();
        }
        let ticking = false;

        window.addEventListener("scroll", () => {
            if (checkIsPageArticle()) {
                ticking = true;
                if (!document.querySelector('#fudium-page-banner')) {
                    appendElementInPageArticle();
                }
            } else {
                if (document.querySelector('#fudium-page-banner')) {
                    document.querySelector('#fudium-page-banner').remove();
                }
                ticking = false;
            }

            if (!ticking) {
                window.requestAnimationFrame(() => {
                    if (screen !== checkHeight()) {
                        appendElement().then(async (element) => {
                            const arrElement = [...element];
                            if (arrElement.length > 0) {
                                arrElement.forEach((e) => {
                                    const linkElement = e.querySelector('div[role="link"]');
                                    if (linkElement && !linkElement.querySelector('#fudium-article-banner') && checkMemberArticle(linkElement)) {
                                        const rawLink = linkElement.dataset.href

                                        linkElement.append(bannerTagArticle(rawLink));
                                    }
                                });
                            }
                            screen = checkHeight();

                        });
                    }
                    ticking = false;
                });
                ticking = true;
            }
        });
    }

    init();

})();