// ==UserScript== // @name Pixiv Plus // @name:zh-CN Pixiv 增强 // @name:zh-TW Pixiv 增強 // @namespace https://github.com/Ahaochan/Tampermonkey // @version 0.7.2 // @icon http://www.pixiv.net/favicon.ico // @description Focus on immersive experience, // @description:ja 没入型 // @description:zh-tw hhhh // @description:zh-CN 专注沉 // @author Ahaochan // @include http*://www.pixiv.net* // @match http://www.pixiv.net/ // @connect i.pximg.net // @license GPL-3.0 // @supportURL https://github.com/Ahaochan/Tampermonkey // @grant unsafeWindow // @grant GM.xmlHttpRequest // @grant GM.setClipboard // @grant GM.setValue // @grant GM.getValue // @grant GM_addStyle // @grant GM_xmlhttpRequest // @grant GM_setClipboard // @grant GM_setValue // @grant GM_getValue // @require https://code.jquery.com/jquery-2.2.4.min.js // @require https://cdn.bootcss.com/jszip/3.1.4/jszip.min.js // @require https://cdn.bootcss.com/FileSaver.js/1.3.2/FileSaver.min.js // @require https://greasyfork.org/scripts/2963-gif-js/code/gifjs.js?version=8596 // @require https://greasyfork.org/scripts/375359-gm4-polyfill-1-0-1/code/gm4-polyfill-101.js?version=652238 // @run-at document-end // @noframes // @downloadURL none // ==/UserScript== jQuery(function ($) { 'use strict'; // 加载依赖 // ============================ jQuery插件 ==================================== $.fn.extend({ fitWindow: function () { this.css('width', 'auto').css('height', 'auto') .css('max-width', '').css('max-height', $(window).height()); }, replaceTagName: function (replaceWith) { var tags = [], i = this.length; while (i--) { var newElement = document.createElement(replaceWith), thisi = this[i], thisia = thisi.attributes; for (var a = thisia.length - 1; a >= 0; a--) { var attrib = thisia[a]; newElement.setAttribute(attrib.name, attrib.value); } newElement.innerHTML = thisi.innerHTML; $(thisi).after(newElement).remove(); tags[i] = newElement; } return $(tags); }, getBackgroundUrl: function () { let imgUrls = []; this.each(function (index, ele) { let bgUrl = $(this).css('background-image') || ele.style.backgroundImage || 'url("")'; let matchArr = bgUrl.match(/url\((['"])(.*?)\1\)/); bgUrl = matchArr && matchArr.length >= 2 ? matchArr[2] : ''; imgUrls.push(bgUrl); }); return imgUrls.length === 1 ? imgUrls[0] : imgUrls; } }); // ============================ 全局参数 ==================================== let globalData, preloadData; $.ajax({ url: location.href, async: false, success: response => { let html = document.createElement( 'html' ); html.innerHTML = response; globalData = JSON.parse($(html).find('meta[name="global-data"]').attr('content') || '{}'); preloadData = JSON.parse($(html).find('meta[name="preload-data"]').attr('content') || '{}'); } }); let lang = (document.documentElement.getAttribute('lang') || 'en').toLowerCase(), illustJson = {}; console.log(globalData); console.log(preloadData); let illust = function () { // 1. 判断是否已有作品id(兼容按左右方向键翻页的情况) let preIllustId = $('body').attr('ahao_illust_id'); let paramRegex = location.href.match(/artworks\/(\d*)$/); let urlIllustId = !!paramRegex && paramRegex.length > 0 ? paramRegex[1] : ''; // 2. 如果illust_id没变, 则不更新json if (parseInt(preIllustId) === parseInt(urlIllustId)) { return illustJson; } // 3. 如果illust_id变化, 则持久化illust_id, 且同步更新json if (!!urlIllustId) { $('body').attr('ahao_illust_id', urlIllustId); $.ajax({ url: '/ajax/illust/' + urlIllustId, dataType: 'json', async: false, success: response => illustJson = response.body }); } return illustJson; }; let uid = preloadData && preloadData.user && Object.keys(preloadData.user)[0]; let observerFactory = function (option) { let options; if (typeof option === 'function') { options = { callback: option, node: document.getElementsByTagName('body')[0], option: {childList: true, subtree: true} }; } else { options = $.extend({ callback: () => { }, node: document.getElementsByTagName('body')[0], option: {childList: true, subtree: true} }, option); } let MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver, observer = new MutationObserver((mutations, observer) => { options.callback.call(this, mutations, observer); // GM.getValue('MO', true).then(function (v) { if(!v) observer.disconnect(); }); }); observer.observe(options.node, options.option); return observer; }; let isLogin = function () { let status = 0; $.ajax({url: 'https://www.pixiv.net/setting_user.php', async: false}) .done((data, statusText, xhr) => status = xhr.status); return status === 200; }; // ============================ 配置信息 ==================================== let GMkeys = { MO: 'MO', // MutationObserver 的开关 selectorShareBtn: 'selectorShareBtn', // 下载按钮的selector selectorRightColumn: 'selectorRightColumn', // 作品页面的作者信息selector switchImgSize: 'switch-img-size', // 是否显示图片大小的开关 switchImgPreload: 'switch-img-preload', // 是否预下载的开关 switchComment: 'switch-comment', // 是否自动加载评论的开关 switchOrderByPopular: 'switch-order-by-popular',// 是否按收藏数排序的开关(单页排序) downloadName: 'download-name', // 下载名pattern }; // ============================ i18n 国际化 =============================== let i18nLib = { ja: { favorites: 'users入り', }, en: { favorites: 'favorites', illegal: 'illegal', download: 'download', download_wait: 'please wait download completed', copy_to_clipboard: 'copy to Clipboard', background: 'background', background_not_found: 'no-background', loginWarning: 'Pixiv Plus Script Warning! Please login to Pixiv for a better experience! Failure to login may result in unpredictable bugs!', illust_type_single: '[single pic]', illust_type_multiple: '[multiple pic]', illust_type_gif: '[gif pic]', sort_by_popularity: 'Sort_by_popularity(single page)' }, ko: {}, zh: { favorites: '收藏人数', illegal: '不合法', download: '下载', download_wait: '请等待下载完成', copy_to_clipboard: '已复制到剪贴板', background: '背景图', background_not_found: '无背景图', loginWarning: 'Pixiv增强 脚本警告! 请登录Pixiv获得更好的体验! 未登录可能产生不可预料的bug!', illust_type_single: '[单图]', illust_type_multiple: '[多图]', illust_type_gif: '[gif图]', sort_by_popularity: '按收藏数搜索(单页)' }, 'zh-cn': {}, 'zh-tw': { favorites: '收藏人數', illegal: '不合法', download: '下載', download_wait: '請等待下載完成', copy_to_clipboard: '已復製到剪貼板', background: '背景圖', background_not_found: '無背景圖', loginWarning: 'Pixiv增強 腳本警告! 請登錄Pixiv獲得更好的體驗! 未登錄可能產生不可預料的bug!', illust_type_single: '[單圖]', illust_type_multiple: '[多圖]', illust_type_gif: '[gif圖]', sort_by_popularity: '按收藏數搜索(單頁)' } }; i18nLib['zh-cn'] = $.extend({}, i18nLib.zh); // TODO 待翻译 i18nLib.ja = $.extend({}, i18nLib.en, i18nLib.ja); i18nLib.ko = $.extend({}, i18nLib.en, i18nLib.ko); let i18n = key => i18nLib[lang][key] || `i18n[${lang}][${key}] not found`; // ============================ url 页面判断 ============================== let isArtworkPage = () => /.+artworks\/\d+.*/.test(location.href); let isMemberIndexPage = () => /.+member.php.*id=\d+.*/.test(location.href); let isMemberIllustPage = () => /.+\/member_illust\.php\?id=\d+/.test(location.href); let isMemberBookmarkPage = () => /.+\/bookmark\.php\?id=\d+/.test(location.href); let isMemberFriendPage = () => /.+\/mypixiv_all\.php\?id=\d+/.test(location.href); let isMemberDynamicPage = () => /.+\/stacc.+/.test(location.href); let isMemberPage = () => isMemberIndexPage() || isMemberIllustPage() || isMemberBookmarkPage() || isMemberFriendPage(), isSearchPage = () => /.+\/search\.php.*/.test(location.href) || /.+\/tags\/.*\/artworks.*/.test(location.href); // 判断是否登录 if (!isLogin()) { alert(i18n('loginWarning')); } // 1. 屏蔽广告, 全局进行css处理 (function () { // 1. 删除静态添加的广告 $('.ad').remove(); $('._premium-lead-tag-search-bar').hide(); $('.popular-introduction-overlay').hide();// 移除热门图片遮罩层 $('.ad-footer').remove();//移除页脚广告 // 2. 删除动态添加的广告 let adSelectors = ['iframe', '._premium-lead-promotion-banner', '.jkOmhW' // https://www.pixiv.net/tags/%E6%9D%B1%E6%96%B9/artworks?s_mode=s_tag 热门作品 ]; observerFactory(function (mutations, observer) { mutations.forEach(function (mutation) { if (mutation.type !== 'childList') { return; } let $parent = $(mutation.target).parent(); // 2.1. 隐藏广告 let $ad = $parent.find(adSelectors.join(',')); $ad.hide(); }); }); })(); // 2. 使用users入り的方式进行搜索, 优先显示高质量作品 (function () { let label = i18n('favorites'); // users入り let $select = $(` `); // 1. 初始化通用页面UI (function () { let enable = (isArtworkPage() || isMemberPage() || isSearchPage()); if (enable) { return; } console.log("初始化通用页面 按收藏数搜索"); let icon = $('._discovery-icon').attr('src'); let $menu = $(`
`); $menu.find('span.label').after($select); $('.navigation-menu-right').append($menu); })(); // 2. 初始化作品页面和画师页面UI (function () { let enable = !(isArtworkPage() || isMemberPage() || isSearchPage()); if (enable) { return; } console.log("初始化作品页面 按收藏数搜索"); let discoverySelector = 'a[href="/discovery"]'; observerFactory(function (mutations, observer) { for (let i = 0, len = mutations.length; i < len; i++) { let mutation = mutations[i]; // 1. 判断是否改变节点, 或者是否有[发现]节点 // let $discovery = $(mutation.target).find(discoverySelector); let $discovery = $(discoverySelector); if (mutation.type !== 'childList' || $discovery.length <= 0) { continue; } // 2. clone [发现]节点, 移除href属性, 避免死循环 let $tabGroup = $discovery.closest('div'); let $tab = $discovery.closest('ul').clone(); $tab.find(discoverySelector).attr('href', 'javascript:void(0)'); // 3. 加入dom中 $tabGroup.prepend($tab); $tab.find('a').contents().last()[0].textContent = label; $tab.find('a').after($select); observer.disconnect(); break; } }); })(); setTimeout(() => { // 3. 如果已经有搜索字符串, 就在改变选项时直接搜索 let $form = $('input[name="word"]').parent(); $('body').on('change', '#select-ahao-favorites', function () { if (!!$('input[name="word"]').val()) { $form.submit(); } }); // 4. 在提交搜索前处理搜索关键字 $form.submit(function (e) { e.preventDefault(); let $text = $(this).find('input[name="word"]'); let $favorites = $('#select-ahao-favorites'); if(!!$favorites.val()) { // 2.4.1. 去除旧的搜索选项 $text.val((index, val) => val.replace(/\d*users入り/g, '')); $text.val((index, val) => val.replace(/\d*$/g, '')); // 2.4.2. 去除多余空格 $text.val((index, val) => val.replace(/\s\s*/g, '')); $text.val((index, val) => val + ' '); // 2.4.3. 添加新的搜索选项 $text.val((index, val) => `${val}${$favorites.val()}`); } let value = $text.val(); if(!!value) { location.href = `https://www.pixiv.net/tags/${value}/artworks?s_mode=s_tag`; } }); }, 3000); })(); // 3. 追加搜索pid和uid功能 (function () { let enable = (isArtworkPage() || isMemberPage() || isSearchPage()); if (enable) { return; } console.log("初始化通用页面 搜索UID和PID"); let initSearch = function (option) { let options = $.extend({right: '0px', placeholder: '', url: ''}, option); // 1. 初始化表单UI let $form = $(``); let $div = $('').css('position', 'absolute') .css('bottom', '44px').css('height', '30px').css('right', options.right); $div.append($form); $('#suggest-container').before($div); // 2. 绑定submit事件 $form.submit(function (e) { e.preventDefault(); let $input = $(this).find('.ahao-input'); let id = $input.val(); // 2.1. ID 必须为纯数字 if (!/^[0-9]+$/.test(id)) { let label = options.placeholder + i18n('illegal'); alert(label); return; } // 2.2. 新窗口打开url let url = option.url + id; window.open(url); // 2.3. 清空input等待下次输入 $input.val(''); }); }; // 1. UID搜索 initSearch({right: '235px', placeholder: 'UID', url: 'https://www.pixiv.net/member.php?id='}); // 2. PID搜索 initSearch({ right: '345px', placeholder: 'PID', url: 'https://www.pixiv.net/member_illust.php?mode=medium&illust_id=' }); })(); // 初始化通用页面UI (function () { let enable = !(isArtworkPage() || isMemberPage() || isSearchPage()); if (enable) { return; } console.log("初始化作品页面 搜索UID和PID"); observerFactory(function (mutations, observer) { for (let i = 0, len = mutations.length; i < len; i++) { let mutation = mutations[i]; // 1. 判断是否改变节点, 或者是否有[form]节点 // let $form = $(mutation.target).find(formSelector); let $form = $('input[name="word"]').parent('form'); if (mutation.type !== 'childList' || !$form.length) { continue; } // 2. 使用flex布局包裹 let $flexBox = $('').css('display', 'flex'); $form.wrap($flexBox); $flexBox = $form.closest('div'); let initSearch = function (option) { let options = $.extend({placeholder: '', url: ''}, option); // 1. clone form表单 let $cloneForm = $form.clone(); $cloneForm.attr('action', '').addClass('ahao-search').css('margin-right', '15px').css('width', '126px'); $cloneForm.find('input[name="s_mode"]').remove(); // 只保留一个input $cloneForm.find('input:first').attr('placeholder', options.placeholder).attr('name', options.placeholder).css('width', '64px').val(''); $flexBox.prepend($cloneForm); // 2. 绑定submit事件 $cloneForm.submit(function (e) { e.preventDefault(); let $input = $(this).find(`input[name="${options.placeholder}"]`); let id = $input.val(); // ID 必须为纯数字 if (!/^[0-9]+$/.test(id)) { var label = options.placeholder + i18n('illegal'); alert(label); return; } // 新窗口打开url let url = option.url + id; window.open(url); // 清空input等待下次输入 $input.val(''); }); }; // 3. UID搜索 initSearch({placeholder: 'UID', url: 'https://www.pixiv.net/member.php?id='}); // 4. PID搜索 initSearch({placeholder: 'PID', url: 'https://www.pixiv.net/member_illust.php?mode=medium&illust_id='}); observer.disconnect(); break; } }); })(); // 初始化作品页面和画师页面UI // 4. 单张图片替换为原图格式. 追加下载按钮, 下载gif图、gif的帧压缩包、多图 (async function () { if (!isArtworkPage()) { return; } // 1. 初始化方法 let initDownloadBtn = function (option) { // 下载按钮, 复制分享按钮并旋转180度 let options = $.extend({ $shareButtonContainer: undefined, id: '', text: '', clickFun: () => {} }, option); let $downloadButtonContainer = options.$shareButtonContainer.clone(); $downloadButtonContainer.addClass('ahao-download-btn') .attr('id', options.id) .removeClass(options.$shareButtonContainer.attr('class')) .css('margin-right', '10px') .css('position', 'relative') .css('border', '1px solid') .css('padding', '1px 10px') .append(`${options.text}
`); $downloadButtonContainer.find('button').css('transform', 'rotate(180deg)') .on('click', options.clickFun); options.$shareButtonContainer.after($downloadButtonContainer); return $downloadButtonContainer; }; let addImgSize = async function (option) { // 从 $img 获取图片大小, after 到 $img let options = $.extend({ $img: undefined, position: 'absolute', }, option); let $img = options.$img, position = options.position; if ($img.length !== 1) { return; } GM.getValue(GMkeys.switchImgSize, true).then(open => { if (!!open) { // 1. 找到 显示图片大小 的 span, 没有则添加 let $span = $img.next('span'); if ($span.length <= 0) { // 添加前 去除失去依赖的 span $('body').find('.ahao-img-size').each(function () { let $this = $(this), $prev = $this.prev('canvas, img'); if ($prev.length <= 0) { $this.remove(); } }); $img.after(``); } // 2. 根据标签获取图片大小, 目前只有 canvas 和 img 两种 if ($img.prop('tagName') === 'IMG') { let img = new Image(); img.src = $img.attr('src'); img.onload = function () { $span.text(`${this.width}x${this.height}`); }; } else { let width = $img.attr('width') || $img.css('width').replace('px', '') || $img.css('max-width').replace('px', '') || 0; let height = $img.attr('height') || $img.css('height').replace('px', '') || $img.css('max-height').replace('px', '') || 0; $span.text(`${width}x${height}`); } } }); }; let mimeType = suffix => { let lib = {png: "image/png", jpg: "image/jpeg", gif: "image/gif"}; return lib[suffix] || `mimeType[${suffix}] not found`; }; let getDownloadName = (name) => { name = name.replace('{pid}', illust().illustId); name = name.replace('{uid}', illust().userId); name = name.replace('{pname}', illust().illustTitle); name = name.replace('{uname}', illust().userName); return name; }; let isMoreMode = () => illust().pageCount > 1, isGifMode = () => illust().illustType === 2, isSingleMode = () => (illust().illustType === 0 || illust().illustType === 1) && illust().pageCount === 1; let selectorShareBtn = await GM.getValue(GMkeys.selectorShareBtn, '.UXmvz'); // section 下的 div // 热修复下载按钮的className observerFactory(function (mutations, observer) { for (let i = 0, len = mutations.length; i < len; i++) { let mutation = mutations[i], $target = $(mutation.target); if($target.prop('tagName').toLowerCase() !== 'section') continue; let $section = $target.find('section'); if($section.length <= 0) continue; let className = $section.eq(0).children('div').eq(1).attr('class').split(' ')[1]; GM.setValue(GMkeys.selectorShareBtn, `.${className}`); observer.disconnect(); return; } }); // 显示单图、多图原图 observerFactory({ callback: function (mutations, observer) { for (let i = 0, len = mutations.length; i < len; i++) { let mutation = mutations[i], $target = $(mutation.target); let replaceImg = function ($target, attr, value) { let oldValue = $target.attr(attr); if (new RegExp(`.*i\.pximg\.net.*\/${illust().id}_.*`).test(oldValue) && !/.+original.+/.test(oldValue)) { $target.attr(attr, value).css('filter', 'none'); $target.fitWindow(); } }; // 1. 单图、多图 DOM 结构都为' + (isMultiPic ? i18n('illust_type_multiple') : i18n('illust_type_single')) + '
'); break; case 2: $a.after('' + i18n('illust_type_gif') + '
'); break; } } }); }) } }); })(); // 8. 对jump.php取消重定向 (function () { let jumpSelector = 'a[href*="jump.php"]'; observerFactory(function (mutations, observer) { for (let i = 0, len = mutations.length; i < len; i++) { let mutation = mutations[i]; // 1. 判断是否改变节点 if (mutation.type !== 'childList') { continue; } // 2. 修改href let $jump = $(mutation.target).find(jumpSelector); $jump.each(function () { let $this = $(this), url = $this.attr('href').match(/jump\.php\?(url=)?(.*)$/)[2]; $this.attr('href', decodeURIComponent(url)); }); } }); })(); // 9. 单页排序 (function () { if (!isSearchPage() || true) { return; } // 9.1. 生成按收藏数排序的按钮 observerFactory(function (mutations, observer) { for (let i = 0, len = mutations.length; i < len; i++) { let mutation = mutations[i]; // 1. 判断是否改变节点 let $section = $('section'); if (mutation.type !== 'childList' || $section.length <= 0) { continue; } let $div = $section.prev().find('div').eq(0); // 2. 添加按收藏数排序的按钮 let $sort = $(`${i18n('sort_by_popularity')}`); $sort.on('click', function () { var value = !$(this).hasClass('bNPzQX'); console.log(value); GM.setValue(GMkeys.switchOrderByPopular, value); if (value) { $sort.attr('class', 'sc-LzLvL bNPzQX'); } else { $sort.attr('class', 'sc-LzLvL lfAMBc'); } }); $div.prepend($sort); GM.getValue(GMkeys.switchOrderByPopular, true).then(value => { if (value) { $sort.attr('class', 'sc-LzLvL bNPzQX'); } else { $sort.attr('class', 'sc-LzLvL lfAMBc'); } }); observer.disconnect(); break; } }); // 9.2. 按收藏数排序 // TODO 页面没有展示收藏数, 关闭单页排序 observerFactory(function (mutations, observer) { for (let i = 0, len = mutations.length; i < len; i++) { let mutation = mutations[i]; // 1. 判断是否改变节点 let $div = $(mutation.target); if (mutation.type !== 'childList' || $div.find('.count-list').length > 0) { continue; } // 2. 获取所有的item, 排序并填充 GM.getValue(GMkeys.switchOrderByPopular, true).then(value => { if(!value) { return; } let $container = $('section#js-react-search-mid').find('div:first'); let $list = $container.children(); let getCount = $ => parseInt($.find('ul.count-list a').text()) || 0; $list.sort((a, b) => getCount($(b)) - getCount($(a))); $container.html($list); }); return; // 本次变更只排序一次 } }); })(); // 10. 兼容模式检测是否PJAX并刷新页面, https://stackoverflow.com/a/4585031/6335926 (function(history){ let pushState = history.pushState; history.pushState = function(state) { if (typeof history.onpushstate == "function") { history.onpushstate({state: state}); } GM.getValue(GMkeys.MO, true).then(function (enableMO) { if(enableMO) { return; } location.reload(); }); return pushState.apply(history, arguments); }; })(window.history); // 11. 控制面板 (function () { if(!/.+setting_user\.php.*/.test(location.href)) { return; } let $table = $(`Pixiv增强配置 |
保存 说明 |
---|