// ==UserScript== // @name Medium傻瓜式一键解锁(可配置多源)bypass Medium // @namespace https://www.deviantart.com/yuumei // @version 1.3 // @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 // @downloadURL https://update.greasyfork.icu/scripts/519017/Medium%E5%82%BB%E7%93%9C%E5%BC%8F%E4%B8%80%E9%94%AE%E8%A7%A3%E9%94%81%28%E5%8F%AF%E9%85%8D%E7%BD%AE%E5%A4%9A%E6%BA%90%29bypass%20Medium.user.js // @updateURL https://update.greasyfork.icu/scripts/519017/Medium%E5%82%BB%E7%93%9C%E5%BC%8F%E4%B8%80%E9%94%AE%E8%A7%A3%E9%94%81%28%E5%8F%AF%E9%85%8D%E7%BD%AE%E5%A4%9A%E6%BA%90%29bypass%20Medium.meta.js // ==/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 = `

解锁源设置

`; 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) => `
${source.name} ${source.url}
`).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 = `
⚙️
${sources.map(source => `
${source.name}
`).join('')}
`; 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 }); } } // 检查是否为Medium文章页面 function isMediumArticle() { // 检查多个 Medium 特征 const mediumFeatures = [ // 检查是否存在 article 元素 () => document.querySelector('article') !== null, // 检查页面 meta 信息 () => { const generator = document.querySelector('meta[name="generator"]'); return generator && generator.content.toLowerCase().includes('medium'); }, // 检查特定的 Medium CSS 类名 () => { return document.querySelector('.progressiveMedia, .graf--title, .section-content') !== null; }, // 检查 Medium 的特征性 script () => { const scripts = Array.from(document.getElementsByTagName('script')); return scripts.some(script => script.src && ( script.src.includes('medium.com') || script.src.includes('cdn-client.medium.com') ) ); }, // 检查 Medium 的 API 端点 () => { const links = Array.from(document.getElementsByTagName('link')); return links.some(link => link.href && ( link.href.includes('medium.com') || link.href.includes('cdn-static-1.medium.com') ) ); } ]; // 如果满足任意两个特征,就认为是 Medium 文章 return mediumFeatures.filter(check => check()).length >= 2; } // 更新解锁按钮 function updateUnlockButton() { const oldButton = document.getElementById('unlock-button'); if(oldButton) oldButton.remove(); createUnlockButton(); } // 初始化函数 function init() { // 延迟检查,确保页面完全加载 setTimeout(() => { if(isMediumArticle()) { if(!document.getElementById('medium-unlock-settings')) { createSettingsPanel(); } if(!document.getElementById('unlock-button')) { createUnlockButton(); } } }, 1500); // 增加延迟时间以确保页面元素加载完成 } // 页面加载和动态导航处理 if(document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } // 使用 MutationObserver 监听页面变化 let lastUrl = location.href; const observer = new MutationObserver((mutations) => { const url = location.href; if (url !== lastUrl) { lastUrl = url; init(); } // 检查DOM变化是否添加了新的Medium特征 if(mutations.some(mutation => mutation.addedNodes.length > 0)) { if(!document.getElementById('unlock-button') && isMediumArticle()) { init(); } } }); observer.observe(document, { subtree: true, childList: true }); })();