Greasy Fork

Greasy Fork is available in English.

Medium傻瓜式一键解锁(可配置多源)bypass Medium

在Medium白嫖浏览付费文章,支持多个解锁源。Support for viewing paid articles for medium.com

当前为 2024-11-27 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Medium傻瓜式一键解锁(可配置多源)bypass Medium
// @namespace    https://www.deviantart.com/yuumei
// @version      1.2
// @description  在Medium白嫖浏览付费文章,支持多个解锁源。Support for viewing paid  articles for medium.com
// @author       mibboy
// @license      GPLv3
// @icon         https://i.imgur.com/Hs7AiY2.png
// @match        *://medium.com/*
// @match        *://*.medium.com/*
// @grant        GM_getValue
// @grant        GM_setValue
// ==/UserScript==

(function() {
    'use strict';

    // 默认解锁源
    const DEFAULT_SOURCES = [
        {name: 'Freedium', url: 'freedium.cfd', enabled: true},
        {name: 'ReadMedium', url: 'readmedium.com', enabled: false},
        {name: 'Scribe', url: 'scribe.rip', enabled: false}
    ];

    // 获取保存的按钮位置
    function getButtonPosition() {
        return GM_getValue('buttonPosition', {right: '20px', bottom: '20px'});
    }

    // 保存按钮位置
    function saveButtonPosition(position) {
        GM_setValue('buttonPosition', position);
    }

    // 获取保存的解锁源
    function getSources() {
        return GM_getValue('unlockerSources', DEFAULT_SOURCES);
    }

    // 保存解锁源
    function saveSources(sources) {
        GM_setValue('unlockerSources', sources);
    }

    // 创建设置面板
    function createSettingsPanel() {
        const panel = document.createElement('div');
        panel.id = 'medium-unlock-settings';
        panel.innerHTML = `
            <div id="settings-panel" style="
                display: none;
                position: fixed;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                background: white;
                padding: 20px;
                border-radius: 10px;
                box-shadow: 0 0 20px rgba(0,0,0,0.2);
                z-index: 10000;
                min-width: 300px;
                font-family: -apple-system,BlinkMacSystemFont,sans-serif;
            ">
                <h3 style="margin:0 0 15px 0;color:#333;">解锁源设置</h3>
                <div id="sources-list" style="margin-bottom:15px;max-height:300px;overflow-y:auto;"></div>
                <div style="margin-bottom:15px;">
                    <input type="text" id="new-source-name" placeholder="名称" style="margin-right:5px;padding:5px;">
                    <input type="text" id="new-source-url" placeholder="域名" style="margin-right:5px;padding:5px;">
                    <button id="add-source-btn" style="
                        background:#1a8917;
                        color:white;
                        border:none;
                        padding:5px 10px;
                        border-radius:5px;
                        cursor:pointer;
                    ">添加</button>
                </div>
                <div style="text-align:right;">
                    <button id="close-settings-btn" style="
                        background:#666;
                        color:white;
                        border:none;
                        padding:5px 15px;
                        border-radius:5px;
                        cursor:pointer;
                        margin-right:10px;
                    ">关闭</button>
                    <button id="save-settings-btn" style="
                        background:#1a8917;
                        color:white;
                        border:none;
                        padding:5px 15px;
                        border-radius:5px;
                        cursor:pointer;
                    ">保存</button>
                </div>
            </div>
        `;

        document.body.appendChild(panel);

        // 添加事件监听
        document.getElementById('add-source-btn').addEventListener('click', addNewSource);
        document.getElementById('close-settings-btn').addEventListener('click', closeSettings);
        document.getElementById('save-settings-btn').addEventListener('click', saveSettings);
    }

    // 添加新源
    function addNewSource() {
        const nameInput = document.getElementById('new-source-name');
        const urlInput = document.getElementById('new-source-url');
        
        if(nameInput.value && urlInput.value) {
            const sources = getSources();
            sources.push({
                name: nameInput.value,
                url: urlInput.value,
                enabled: true
            });
            updateSourcesList(sources);
            nameInput.value = '';
            urlInput.value = '';
        }
    }

    // 关闭设置
    function closeSettings() {
        const panel = document.getElementById('settings-panel');
        if(panel) panel.style.display = 'none';
    }

    // 保存设置
    function saveSettings() {
        const sources = [];
        document.querySelectorAll('.source-item').forEach(item => {
            sources.push({
                name: item.querySelector('.source-name').textContent,
                url: item.querySelector('.source-url').textContent,
                enabled: item.querySelector('.source-enabled').checked
            });
        });
        saveSources(sources);
        closeSettings();
        updateUnlockButton();
    }

    // 删除源
    function deleteSource(index) {
        const sources = getSources();
        sources.splice(index, 1);
        updateSourcesList(sources);
    }

    // 更新源列表显示
    function updateSourcesList(sources) {
        const list = document.getElementById('sources-list');
        list.innerHTML = sources.map((source, index) => `
            <div class="source-item" style="
                display:flex;
                align-items:center;
                margin-bottom:10px;
                padding:5px;
                border:1px solid #eee;
                border-radius:5px;
            ">
                <input type="checkbox" class="source-enabled" ${source.enabled ? 'checked' : ''} style="margin-right:10px;">
                <span class="source-name" style="margin-right:10px;min-width:80px;">${source.name}</span>
                <span class="source-url" style="margin-right:10px;color:#666;">${source.url}</span>
                <button onclick="(${deleteSource.toString()})(${index})" style="
                    margin-left:auto;
                    background:#ff4444;
                    color:white;
                    border:none;
                    padding:3px 8px;
                    border-radius:3px;
                    cursor:pointer;
                ">删除</button>
            </div>
        `).join('');
    }

    // 创建可拖动的解锁按钮
    function createUnlockButton() {
        const sources = getSources().filter(s => s.enabled);
        if(sources.length === 0) return;

        const position = getButtonPosition();
        const button = document.createElement('div');
        button.innerHTML = `
            <div id="unlock-button" style="
                position: fixed;
                bottom: ${position.bottom};
                right: ${position.right};
                z-index: 9999;
                display: flex;
                flex-direction: column;
                align-items: flex-end;
                gap: 10px;
                cursor: move;
            ">
                <div class="settings-trigger" style="
                    background: #666;
                    color: white;
                    padding: 8px;
                    border-radius: 50%;
                    cursor: pointer;
                    box-shadow: 0 2px 8px rgba(0,0,0,0.2);
                    transition: all 0.3s ease;
                ">
                    ⚙️
                </div>
                ${sources.map(source => `
                    <div class="unlock-option" style="
                        background: #1a8917;
                        color: white;
                        padding: 10px 15px;
                        border-radius: 20px;
                        cursor: pointer;
                        box-shadow: 0 2px 8px rgba(0,0,0,0.2);
                        transition: all 0.3s ease;
                        display: flex;
                        align-items: center;
                        font-family: -apple-system,BlinkMacSystemFont,sans-serif;
                    ">
                        <span>${source.name}</span>
                    </div>
                `).join('')}
            </div>
        `;

        document.body.appendChild(button);

        // 添加拖动功能
        const unlockButton = document.getElementById('unlock-button');
        makeDraggable(unlockButton);

        // 添加设置按钮事件
        unlockButton.querySelector('.settings-trigger').addEventListener('click', (e) => {
            e.stopPropagation(); // 防止触发拖动
            document.getElementById('settings-panel').style.display = 'block';
            updateSourcesList(getSources());
        });

        // 添加解锁按钮事件
        unlockButton.querySelectorAll('.unlock-option').forEach((option, index) => {
            option.addEventListener('click', (e) => {
                e.stopPropagation(); // 防止触发拖动
                const currentUrl = window.location.href;
                const unlockUrl = 'https://' + sources[index].url + '/' + currentUrl;
                window.open(unlockUrl, '_blank');
            });

            option.addEventListener('mouseover', function() {
                this.style.transform = 'scale(1.05)';
                this.style.background = '#147811';
            });

            option.addEventListener('mouseout', function() {
                this.style.transform = 'scale(1)';
                this.style.background = '#1a8917';
            });
        });
    }

    // 使元素可拖动
    function makeDraggable(element) {
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
        
        element.onmousedown = dragMouseDown;

        function dragMouseDown(e) {
            e = e || window.event;
            e.preventDefault();
            pos3 = e.clientX;
            pos4 = e.clientY;
            document.onmouseup = closeDragElement;
            document.onmousemove = elementDrag;
        }

        function elementDrag(e) {
            e = e || window.event;
            e.preventDefault();
            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;

            const newTop = element.offsetTop - pos2;
            const newLeft = element.offsetLeft - pos1;

            // 确保按钮不会超出屏幕
            if (newTop >= 0 && newTop <= window.innerHeight - element.offsetHeight) {
                element.style.top = newTop + "px";
            }
            if (newLeft >= 0 && newLeft <= window.innerWidth - element.offsetWidth) {
                element.style.left = newLeft + "px";
            }
        }

        function closeDragElement() {
            document.onmouseup = null;
            document.onmousemove = null;
            
            // 保存最终位置
            saveButtonPosition({
                right: element.style.right,
                bottom: element.style.bottom
            });
        }
    }

    // 更新解锁按钮
    function updateUnlockButton() {
        const oldButton = document.getElementById('unlock-button');
        if(oldButton) oldButton.remove();
        createUnlockButton();
    }

    // 检查是否为Medium文章页面
    function isMediumArticle() {
        return document.querySelector('article') !== null;
    }

    // 初始化
    function init() {
        if(isMediumArticle()) {
            if(!document.getElementById('medium-unlock-settings')) {
                createSettingsPanel();
            }
            if(!document.getElementById('unlock-button')) {
                createUnlockButton();
            }
        }
    }

    // 页面加载完成后执行
    if(document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

    // 处理动态加载的页面
    let lastUrl = location.href;
    new MutationObserver(() => {
        const url = location.href;
        if (url !== lastUrl) {
            lastUrl = url;
            setTimeout(init, 1000);
        }
    }).observe(document, {subtree: true, childList: true});

})();