您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Yande.re浏览增强:悬停大图预览,显示艺术家名称,点击跳转主页,右键名称标记艺术家,高亮显示不同颜色,支持双击/键盘翻页,自动显示隐藏图片,直接打开原图
// ==UserScript== // @name Yande.re 大图预览 显示艺术家 打开原图 // @version 1.2 // @description Yande.re浏览增强:悬停大图预览,显示艺术家名称,点击跳转主页,右键名称标记艺术家,高亮显示不同颜色,支持双击/键盘翻页,自动显示隐藏图片,直接打开原图 // @description 原脚本已数年未更新,在原作者Joker(Streams)的基础上修改; konachan有限支持(需自行添加@match),部分功能可能无法使用 // @author Cylirix // @author Joker // @author Streams // @match https://yande.re/* // @icon https://yande.re/favicon.ico // @grant GM_getValue // @grant GM_setValue // @namespace https://greasyfork.org/users/467741 // ==/UserScript== jQuery.noConflict(); jQuery(function ($) { // 全局唯一标识系统 var currentUUID = null; var requestController = null; var hoverTimer = null; var currentHoverItem = null; // 初始化艺术家状态(使用Tampermonkey存储系统) var artistStates = {}; // 从Tampermonkey存储加载数据 try { const savedStates = GM_getValue("artistStates", "{}"); artistStates = JSON.parse(savedStates); } catch (e) { console.error("Error loading artist states:", e); } // 右键菜单容器 const $contextMenu = $('<div id="artist-context-menu">').css({ position: 'fixed', display: 'none', background: 'rgba(0,0,0,0.7)', borderRadius: '6px', zIndex: 10000, padding: '5px 5px' }).appendTo('body'); // 状态配置 - 使用字母a、b、c作为ID const stateConfig = [ {id: 'a', text: '已收藏', color: '#ffff00'}, {id: 'b', text: '已下载', color: '#00ff11'}, {id: 'c', text: 'ignore', color: '#999'}, {id: 'clear', text: '清除', color: '#fff'} ]; // 创建菜单项 stateConfig.forEach(option => { $('<div class="menu-item">') .text(option.text) .css({ padding: '5px 15px', cursor: 'pointer', color: option.color, fontSize: '12px' }) .data('action', option.id) .hover( function() { $(this).css('background', '#444'); }, function() { $(this).css('background', 'transparent'); } ) .appendTo($contextMenu); }); // 预览框容器 var $zoombox = $('<div id="zoombox">').css({ position: 'fixed', top: 0, left: 0, 'z-index': 9999, 'pointer-events': 'none', display: 'none', 'overflow': 'hidden', 'transition': 'width 0.3s, height 0.3s' }).appendTo('body'); // 添加全局样式 function addGlobalStyle(css) { $('<style></style>').html(css).appendTo('head'); } // 动态生成状态样式 let stateStyles = ''; stateConfig.forEach(state => { if (state.id !== 'clear') { stateStyles += ` .artist-label.status-${state.id} { color: ${state.color} !important; } `; // 特殊状态样式 if (state.id === 'c') { stateStyles += ` .artist-label.status-c { text-decoration: line-through; } `; } } }); addGlobalStyle(` /* 主预览框样式 */ #zoombox { max-width: 100%; max-height: 100%; border-radius: 4px; } /* 预览图内部样式 */ #zoombox img { display: block; pointer-events: none; object-fit: contain; transition: opacity 0.3s; border-radius: 4px; } /* 图片容器 */ .inner { position: relative; } /* 控制容器 - 右上角 */ .control-box { position: absolute; top: 0px; right: 0px; z-index: 1000; pointer-events: none; text-align: right; max-width: 80%; /* 限制最大宽度 */ font-size: 11px; /* 统一字体大小 */ line-height: 1.2; display: flex; /* 使用flex布局 */ flex-direction: column; /* 垂直排列元素 */ align-items: flex-end; /* 内容右对齐 */ } /* 艺术家标签 - 多行显示 */ .artist-label { background: rgba(0,0,0,0.3); color: white; /* 默认文字颜色 */ padding: 2px 5px; border-radius: 3px; font-weight: bold; white-space: normal; /* 允许多行显示 */ word-break: break-word; /* 长单词换行 */ pointer-events: auto; margin-bottom: 2px; /* 与下方按钮间距 */ max-width: 100%; /* 宽度限制 */ text-align: left; /* 内部文本左对齐 */ cursor: context-menu; /* 显示右键菜单光标 */ } /* unknown状态特殊样式 */ .artist-label.unknown { color: #aaa !important; /* 更灰暗的颜色 */ font-style: italic; /* 添加斜体效果 */ } /* 艺术家状态样式 */ ${stateStyles} /* 艺术家链接样式 */ .artist-label a { color: inherit; /* 继承父元素颜色 */ text-decoration: none; } .artist-label a:hover { text-decoration: underline; } /* 原图按钮 - 背景大小优化 */ .original-btn { background: rgba(0,0,0,0.35); color: white; padding: 2px 5px; border-radius: 3px; text-decoration: none; font-weight: bold; pointer-events: auto; white-space: nowrap; /* 禁止文字换行 */ display: none; /* 默认隐藏 */ } /* 悬停时显示原图按钮 */ .inner:hover .original-btn { display: block; /* 显示为块级元素,独占一行 */ } /* 原图按钮悬停效果 */ .original-btn:hover { background: rgba(200, 80, 80, 0.8); } /* 右键菜单样式 */ #artist-context-menu { font-family: Arial, sans-serif; } .menu-item { transition: background 0.5s; } /* === 默认显示分辨率 === */ #post-list-posts li a.directlink span.directlink-info { display: none !important; } #post-list-posts li a.directlink span.directlink-res { display: inline !important; } `); // 翻页功能 function addKey() { $(document).dblclick(function (e) { var w = document.documentElement.offsetWidth || document.body.offsetWidth; if (e.clientX > w / 2) nextPage(); else previousPage(); }); $(document).keydown(function (e) { if (e.keyCode == 37) previousPage(); else if (e.keyCode == 39) nextPage(); }); function nextPage() { var $nextBtn = $('a.next_page'); if ($nextBtn.length > 0) $nextBtn[0].click(); } function previousPage() { var $preBtn = $('a.previous_page'); if ($preBtn.length > 0) $preBtn[0].click(); } } // 生成唯一标识符 function generateUUID() { return Date.now().toString(36) + Math.random().toString(36).substr(2); } function setupContextMenu() { // 右键点击事件 $(document).on('contextmenu', '.artist-label', function(e) { e.preventDefault(); const artistName = $(this).text().trim(); if (!artistName || artistName === 'unknown') return; // === 直接使用视口坐标 === const x = e.clientX; const y = e.clientY; // 显示菜单 $contextMenu .data('artist', artistName) .css({ display: 'block', left: x + 'px', top: y + 'px' }); // === 添加边界检查 === setTimeout(() => { const menuRect = $contextMenu[0].getBoundingClientRect(); const windowWidth = window.innerWidth; const windowHeight = window.innerHeight; let adjustedX = x; let adjustedY = y; // 检查右边界 if (menuRect.right > windowWidth) { adjustedX = windowWidth - menuRect.width - 10; } // 检查下边界 if (menuRect.bottom > windowHeight) { adjustedY = windowHeight - menuRect.height - 10; } // 如果需要调整位置 if (adjustedX !== x || adjustedY !== y) { $contextMenu.css({ left: adjustedX + 'px', top: adjustedY + 'px' }); } }, 0); }); // 菜单项点击事件 $contextMenu.on('click', '.menu-item', function() { const action = $(this).data('action'); const artistName = $contextMenu.data('artist'); if (action === 'clear') { // 清除标记 delete artistStates[artistName]; } else { // 设置状态 artistStates[artistName] = action; } // 保存到Tampermonkey存储 GM_setValue("artistStates", JSON.stringify(artistStates)); // 更新所有该艺术家的标签 $(`.artist-label:contains('${artistName}')`).each(function() { const $label = $(this); // 清除所有状态类 stateConfig.forEach(state => { if (state.id !== 'clear') { $label.removeClass(`status-${state.id}`); } }); // 添加新状态类 if (action !== 'clear' && artistStates[artistName]) { $label.addClass(`status-${artistStates[artistName]}`); } }); // 隐藏菜单 $contextMenu.hide(); }); // 点击其他地方关闭菜单 $(document).on('click', function(e) { if (!$(e.target).closest('#artist-context-menu').length) { $contextMenu.hide(); } }); } // 优化的预览逻辑 function addMouseZoomPreview() { $("#post-list-posts > li, .pool-show .inner").each(function() { const $item = $(this); const $inner = $item.find('.inner'); if ($inner.length === 0) return; let postId = $item.data('id'); if (!postId) { // If not found, try to extract from thumb link const $thumbLink = $inner.find('a.thumb'); if ($thumbLink.length) { const href = $thumbLink.attr('href'); const postIdMatch = href && href.match(/\/post\/show\/(\d+)/); if (postIdMatch && postIdMatch[1]) { postId = postIdMatch[1]; } } } // 创建控制容器(艺术家标签和原图按钮的父元素) const $controlBox = $('<div class="control-box"></div>'); $inner.append($controlBox); // 创建原图按钮 let originalUrl = null; let sampleUrl = null; const $thumbLink = $inner.find('a.thumb'); // === 获取当前域名 === const currentDomain = window.location.hostname; const isKonachan = currentDomain.includes('konachan'); const isYandere = currentDomain.includes('yande.re'); // 方法1:从pool页面结构获取URL if ($thumbLink.length) { const href = $thumbLink.attr('href'); const postIdMatch = href && href.match(/\/post\/show\/(\d+)/); if (postIdMatch && postIdMatch[1]) { const postId = postIdMatch[1]; const thumbSrc = $thumbLink.find('img').attr('src'); if (thumbSrc) { const md5 = thumbSrc.split('/').pop().split('.')[0]; if (md5) { // === 根据域名动态生成URL === if (isYandere) { originalUrl = `https://files.yande.re/image/${md5}/yande.re%20${postId}.jpg`; sampleUrl = `https://files.yande.re/sample/${md5}/yande.re%20${postId}%20sample.jpg`; } else if (isKonachan) { originalUrl = `https://${currentDomain}/image/${md5}.jpg`; sampleUrl = `https://${currentDomain}/sample/${md5}.jpg`; } } } } } // 方法2:从普通页面结构获取URL(仅当方法1失败时尝试) if (!originalUrl || !sampleUrl) { const $directLink = $item.find('a[class*="directlink"][href]'); if ($directLink.length) { const url = $directLink.attr('href'); if (url) { originalUrl = url; // === 根据域名动态生成sample URL === if (isYandere) { if (url.includes('/jpeg/')) { originalUrl = url.replace('/jpeg/', '/image/'); } if (url.includes('/image/')) { sampleUrl = url.replace('/image/', '/sample/'); } else if (url.includes('/jpeg/')) { sampleUrl = url.replace('/jpeg/', '/sample/'); } else { sampleUrl = url; } } else if (isKonachan) { // Konachan的URL格式不同 if (url.includes('/image/')) { sampleUrl = url.replace('/image/', '/sample/'); } else { // 尝试从URL中提取md5 const md5Match = url.match(/\/([a-f0-9]{32})\.jpg$/); if (md5Match && md5Match[1]) { sampleUrl = `https://${currentDomain}/sample/${md5Match[1]}.jpg`; } } } } } } // 如果无法获取URL则跳过 if (!originalUrl || !sampleUrl) return; // 创建并添加原图按钮 - 默认隐藏 const $originalBtn = $(`<a class="original-btn" href="${originalUrl}" target="_blank">打开原图</a>`); $controlBox.append($originalBtn); // 获取艺术家信息 if (postId) { $.get(`/post.json?api_version=2&include_tags=1&tags=id:${postId}`, function(data) { if (data.posts && data.posts.length > 0) { const post = data.posts[0]; const tagTypes = data.tags || {}; const tags = post.tags.split(' '); const artists = tags.filter(tag => tagTypes[tag] === 'artist'); let artistText = artists.length > 0 ? artists.join(', ') : 'unknown'; // 创建艺术家标签 const $artistLabel = $('<div class="artist-label"></div>'); // 检查是否为unknown状态 const isUnknown = artistText === 'unknown'; if (isUnknown) { $artistLabel.addClass('unknown'); } // 处理艺术家标签(单艺术家可点击,多艺术家只显示) if (artists.length === 1) { const artistName = artists[0]; // 创建艺术家链接 $artistLabel.append( $(`<a href="/post?tags=${encodeURIComponent(artistName)}" target="_blank"></a>`) .text(artistName) ); // === 使用TM存储的状态 === if (artistStates[artistName]) { $artistLabel.addClass(`status-${artistStates[artistName]}`); } } else { $artistLabel.text(artistText); } // 添加到控制容器顶部 $controlBox.prepend($artistLabel); } }); } // 获取缩略图元素 const $thumbImg = $item.find('img.preview'); if ($thumbImg.length === 0) return; // 获取预设的图片宽高比 const thumbWidth = $thumbImg.width(); const thumbHeight = $thumbImg.height(); const aspectRatio = thumbWidth / thumbHeight; // 悬停事件 $item.hover( // 鼠标进入 function (e) { // 保存当前悬停的元素 currentHoverItem = this; // 清除之前的延时器 if (hoverTimer) { clearTimeout(hoverTimer); hoverTimer = null; } // 设置延时器(300毫秒) hoverTimer = setTimeout(() => { // 检查是否仍在同一个元素上 if (currentHoverItem !== this) return; // 终止可能存在的旧请求 if (requestController) { requestController.abort(); } // 生成新UUID作为当前悬停标识 const thisUUID = generateUUID(); currentUUID = thisUUID; requestController = new AbortController(); // 获取屏幕尺寸 const screenWidth = $(window).width(); const screenHeight = $(window).height(); // 判断横竖图 const isVertical = aspectRatio <= 1; // 根据宽高比设置初始尺寸 let initWidth, initHeight; if (isVertical) { // 竖图:高度占满屏幕高度(无上下边距) initHeight = screenHeight; initWidth = initHeight * aspectRatio; } else { // 横图:宽度占屏幕宽度50% initWidth = Math.min(screenWidth * 0.5, screenHeight * aspectRatio); initHeight = initWidth / aspectRatio; } // 避开鼠标位置的逻辑 const mouseX = e.clientX; let positionLeft = 'auto'; let positionRight = '10px'; if (mouseX < screenWidth / 2) { // 鼠标在左侧,显示在右侧 positionRight = '10px'; positionLeft = 'auto'; } else { // 鼠标在右侧,显示在左侧 positionLeft = '10px'; positionRight = 'auto'; } // 应用位置 $zoombox.css({ top: '5px', left: positionLeft, right: positionRight, width: initWidth + 'px', height: initHeight + 'px' }); // 创建加载容器 const $imgContainer = $('<div>').css({ position: 'relative', overflow: 'hidden', width: '100%', height: '100%' }); const $img = $('<img>').css({ opacity: 1, width: '100%', height: '100%' }); $imgContainer.append($img); $zoombox.empty().append($imgContainer).show(); // 绑定加载信号 requestController.signal.addEventListener('abort', () => { if (currentUUID !== thisUUID) return; $zoombox.hide().empty(); }); // 加载完成的回调 $img.on('load', function() { if (currentUUID !== thisUUID) return; // 获取实际图片信息 const naturalWidth = this.naturalWidth; const naturalHeight = this.naturalHeight; const actualAspectRatio = naturalWidth / naturalHeight; // 判断实际图片横竖图 const isActualVertical = actualAspectRatio <= 1; // 最终尺寸调整 if (isActualVertical) { // 竖图:高度占满屏幕高度(无上下边距) const finalHeight = screenHeight; const finalWidth = finalHeight * actualAspectRatio; $zoombox.css({ width: finalWidth + 'px', height: finalHeight + 'px' }); } else { // 横图:宽度占屏幕宽度50% const finalWidth = screenWidth * 0.5; const finalHeight = finalWidth / actualAspectRatio; $zoombox.css({ width: finalWidth + 'px', height: finalHeight + 'px' }); } }); $img.on('error', function() { if (currentUUID !== thisUUID) return; $img.attr('src', originalUrl); }); // 开始加载图片 $img.attr('src', sampleUrl); }, 300); // 300毫秒延时 }, // 鼠标移出 function () { // 清除延时器 if (hoverTimer) { clearTimeout(hoverTimer); hoverTimer = null; } // 重置当前悬停元素 currentHoverItem = null; // 终止加载请求 if (requestController) { requestController.abort(); requestController = null; } $zoombox.hide().empty(); } ); }); } // 显示隐藏图片 function showHiddenImage() { $("#post-list-posts > li.javascript-hide").removeClass("javascript-hide"); } // 独立函数:禁止图片气泡消息(移除title属性) function disableImageTooltips() { // 针对列表页的图片缩略图移除title属性 $("#post-list-posts img.preview[title]").removeAttr("title"); // 如果有其他可能的图片元素,可以在这里扩展选择器 } // 初始化操作 $(document).ready(function() { setTimeout(function() { showHiddenImage(); addKey(); addMouseZoomPreview(); setupContextMenu(); // 初始化右键菜单功能 disableImageTooltips(); // 禁止气泡消息 }, 500); }); });