- // ==UserScript==
- // @name Pixiv Plus
- // @name:zh-CN Pixiv 增强
- // @name:zh-TW Pixiv 增強
- // @namespace https://github.com/Ahaochan/Tampermonkey
- // @version 0.7.0
- // @icon http://www.pixiv.net/favicon.ico
- // @description Focus on immersive experience,
- // @description:zh-CN 专注沉浸式体验,
- // @description:zh-TW 專注沉浸式體驗,
- // @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://gf.qytechs.cn/scripts/2963-gif-js/code/gifjs.js?version=8596
- // @require https://gf.qytechs.cn/scripts/375359-gm4-polyfill-1-0-1/code/gm4-polyfill-101.js?version=652238
- // @run-at document-end
- // @noframes
- // ==/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);
-
- // 判断是否登录(不可用)
- 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'];
-
- 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 = $(`
- <select id="select-ahao-favorites">
- <option value=""></option>
- <option value="20000users入り">20000users入り</option>
- <option value="10000users入り">10000users入り</option>
- <option value="5000users入り" > 5000users入り</option>
- <option value="1000users入り" > 1000users入り</option>
- <option value="500users入り" > 500users入り</option>
- <option value="300users入り" > 300users入り</option>
- <option value="100users入り" > 100users入り</option>
- <option value="50users入り" > 50users入り</option>
- </select>`);
-
- // 1. 初始化通用页面UI
- (function () {
- let enable = (isArtworkPage() || isMemberPage() || isSearchPage());
- if (enable) {
- return;
- }
- console.log("初始化通用页面 按收藏数搜索");
- let icon = $('._discovery-icon').attr('src');
- let $menu = $(`
- <div class="menu-group">
- <a class="menu-item js-click-trackable-later">
- <img class="_howto-icon" src="${icon}">
- <span class="label">${label}:</span>
- <!--select-->
- </a>
- </div>`);
- $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;
- }
- });
- })();
-
- // 3. 如果已经有搜索字符串, 就在改变选项时直接搜索
- $('body').on('change', '#select-ahao-favorites', function () {
- if (!!$('input[name="word"]').val()) {
- $('form[action="/search.php"]').submit();
- }
- });
-
- // 4. 在提交搜索前处理搜索关键字
- $('form[action="/search.php"]').submit(function () {
- let $text = $(this).find('input[name="word"]');
- let $favorites = $('#select-ahao-favorites');
- // 2.4.1. 去除旧的搜索选项
- $text.val((index, val) => val.replace(/\d*users入り/g, ''));
- // 2.4.2. 去除多余空格
- $text.val((index, val) => val.replace(/\s\s+/g, ' '));
- // 2.4.3. 添加新的搜索选项
- $text.val((index, val) => `${val} ${$favorites.val()}`);
- });
- })();
-
- // 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 = $(`<form class="ui-search" style="position: static;width: 100px;">
- <div class="container" style="width:80%;">
- <input class="ahao-input" placeholder="${options.placeholder}" style="width:80%;"/>
- </div>
- <input type="submit" class="submit sprites-search-old" value="">
- </form>`);
- let $div = $('<div class="ahao-search"></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 = $('<div></div>').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(`<p style="display: inline">${options.text}</p>`);
- $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(`<span class="ahao-img-size" style="position: ${position}; right: 0; top: 28px;
- color: #ffffff; font-size: x-large; font-weight: bold; -webkit-text-stroke: 1.0px #000000;"></span>`);
- }
- // 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 结构都为 <a href=""><img/></a>
- let $link = $target.find('img[srcset]');
- $link.each(function () {
- let $this = $(this);
- let href = $this.parent('a').attr('href');
- if(!!href) {
- replaceImg($this, 'src', href);
- replaceImg($this, 'srcset', href);
- addImgSize({$img: $this}); // 显示图片大小
- }
- });
-
- // 2. 移除马赛克遮罩, https://www.pixiv.net/member_illust.php?mode=medium&illust_id=50358638
- // $('.e2p8rxc2').hide(); // 懒得适配了, 自行去个人资料设置 https://www.pixiv.net/setting_user.php
- }
- },
- option: {attributes: true, childList: true, subtree: true, attributeFilter: ['src', 'srcset', 'href']}
- });
- // 下载动图帧zip, gif图
- observerFactory(function (mutations, observer) {
- for (let i = 0, len = mutations.length; i < len; i++) {
- let mutation = mutations[i], $target = $(mutation.target);
-
- // 1. 单图、多图、gif图三种模式
- let $shareBtn = $target.find(selectorShareBtn), $canvas = $target.find('canvas');
- // 2. 显示图片大小
- addImgSize({$img: $canvas})
- if (!isGifMode() || mutation.type !== 'childList' ||
- $shareBtn.length <= 0 ||
- $target.find('#ahao-download-zip').length > 0) {
- continue
- }
- console.log('下载gif图');
-
-
-
- // 3. 初始化 下载按钮
- let $zipBtn = initDownloadBtn({
- $shareButtonContainer: $shareBtn,
- id: 'ahao-download-zip',
- text: 'zip',
- });
- let $gifBtn = initDownloadBtn({
- $shareButtonContainer: $shareBtn,
- id: 'ahao-download-gif',
- text: 'gif',
- clickFun: function () {
- // 从 pixiv 官方 api 获取 gif 的数据
- $.ajax({
- url: `/ajax/illust/${illust().illustId}/ugoira_meta`, dataType: 'json',
- success: response => {
- // 1. 初始化 gif 下载按钮 点击事件
- // GIF_worker_URL 来自 https://gf.qytechs.cn/scripts/2963-gif-js/code/gifjs.js?version=8596
- let gifUrl, gifFrames = [],
- gifFactory = new GIF({workers: 1, quality: 10, workerScript: GIF_worker_URL});
-
- for (let frameIdx = 0, frames = response.body.frames, framesLen = frames.length; frameIdx < framesLen; frameIdx++) {
- let frame = frames[i],
- url = illust().urls.original.replace('ugoira0.', `ugoira${frameIdx}.`);
- GM.xmlHttpRequest({
- method: 'GET', url: url,
- headers: {referer: 'https://www.pixiv.net/'},
- overrideMimeType: 'text/plain; charset=x-user-defined',
- onload: function (xhr) {
- // 2. 转为blob类型
- let r = xhr.responseText, data = new Uint8Array(r.length), i = 0;
- while (i < r.length) {
- data[i] = r.charCodeAt(i);
- i++;
- }
- let suffix = url.split('.').splice(-1);
- let blob = new Blob([data], {type: mimeType(suffix)});
-
- // 3. 压入gifFrames数组中, 手动同步sync
- let img = document.createElement('img');
- img.src = URL.createObjectURL(blob);
- img.width = illust().width;
- img.height = illust().height;
- img.onload = function () {
- gifFrames[frameIdx] = {frame: img, option: {delay: frame.delay}};
- if (Object.keys(gifFrames).length >= framesLen) {
- $.each(gifFrames, (i, f) => gifFactory.addFrame(f.frame, f.option));
- gifFactory.render();
- }
- };
- }
- });
- }
- gifFactory.on('progress', function (pct) {
- $gifBtn.find('p').text(`gif ${parseInt(pct * 100)}%`);
- });
- gifFactory.on('finished', function (blob) {
- gifUrl = URL.createObjectURL(blob);
- GM.getValue(GMkeys.downloadName, `{pid}`).then(name => {
- let $a = $(`<a href="${gifUrl}" download="${getDownloadName(name)}"></a>`);
- $gifBtn.find('button').wrap($a);
- });
-
- });
- $gifBtn.find('button').off('click').on('click', () => {
- if (!gifUrl) {
- alert('Gif未加载完毕, 请稍等片刻!');
- return;
- }
- // Adblock 禁止直接打开 blob url, https://github.com/jnordberg/gif.js/issues/71#issuecomment-367260284
- // window.open(gifUrl);
- });
- }
- });
- }
- });
-
- // 4. 控制是否预下载, 避免多个页面导致爆内存, 直接下载 zip
- $.ajax({
- url: `/ajax/illust/${illust().illustId}/ugoira_meta`, dataType: 'json',
- success: response => {
- GM.getValue(GMkeys.downloadName, `{pid}`).then(name => {
- let $a = $(`<a href="${response.body.originalSrc}" download="${getDownloadName(name)}"></a>`);
- $zipBtn.find('button').wrap($a);
- });
- }
- });
- GM.getValue(GMkeys.switchImgPreload, true).then(open => { if(open) { $gifBtn.find('button').click(); } });
-
- // 5. 取消监听
- GM.getValue(GMkeys.MO, true).then(function (v) { if(!v) observer.disconnect(); });
- }
- });
- // 下载多图zip
- observerFactory(function (mutations, observer) {
- for (let i = 0, len = mutations.length; i < len; i++) {
- let mutation = mutations[i], $target = $(mutation.target);
-
- // 1. 单图、多图、gif图三种模式
- let $shareBtn = $target.find(selectorShareBtn);
- if (!isMoreMode() || mutation.type !== 'childList' || !$shareBtn.length || !!$target.find('#ahao-download-zip').length) {
- continue
- }
- console.log('下载多图');
-
- // 3. 初始化 图片数量, 图片url
- let zip = new JSZip();
- let downloaded = 0; // 下载完成数量
- let num = illust().pageCount; // 下载目标数量
- let url = illust().urls.original;
- let imgUrls = Array(parseInt(num)).fill()
- .map((value, index) => url.replace(/_p\d\./, `_p${index}.`));
-
- // 4. 初始化 下载按钮, 复制分享按钮并旋转180度
- let $zipBtn = initDownloadBtn({
- $shareButtonContainer: $shareBtn,
- id: 'ahao-download-zip',
- text: `${i18n('download')}`,
- clickFun: function () {
- // 3.1. 下载图片, https://wiki.greasespot.net/GM.xmlHttpRequest
- if($(this).attr('start') !== 'true') {
- $(this).attr('start', true);
- $.each(imgUrls, function (index, url) {
- GM.xmlHttpRequest({
- method: 'GET', url: url,
- headers: {referer: 'https://www.pixiv.net/'},
- overrideMimeType: 'text/plain; charset=x-user-defined',
- onload: function (xhr) {
- // 4.1. 转为blob类型
- let r = xhr.responseText, data = new Uint8Array(r.length), i = 0;
- while (i < r.length) {
- data[i] = r.charCodeAt(i);
- i++;
- }
- let suffix = url.split('.').splice(-1);
- let blob = new Blob([data], {type: mimeType(suffix)});
-
- // 4.2. 压缩图片
- GM.getValue(GMkeys.downloadName, `{pid}`).then(name => {
- zip.file(`${getDownloadName(name)}_${index}.${suffix}`, blob, {binary: true});
- });
-
-
- // 4.3. 手动sync, 避免下载不完全的情况
- downloaded++;
- $zipBtn.find('p').html(`${i18n('download')}${downloaded}/${num}`);
- }
- });
- });
- return;
- }
-
- // 3.2. 手动sync, 避免下载不完全
- if (downloaded < num) {
- alert(i18n('download_wait'));
- return;
- }
- // 3.3. 使用jszip.js和FileSaver.js压缩并下载图片
- GM.getValue(GMkeys.downloadName, `{pid}`).then(name => {
- zip.generateAsync({type: 'blob', base64: true})
- .then(content => saveAs(content, getDownloadName(name)));
- });
- }
- });
-
- // 4. 控制是否预下载, 避免多个页面导致爆内存
- GM.getValue(GMkeys.switchImgPreload, true).then(open => { if(open) { $zipBtn.find('button').click(); } });
-
- // 5. 取消监听
- GM.getValue(GMkeys.MO, true).then(function (v) { if(!v) observer.disconnect(); });
- }
- });
- })();
-
- // 5. 在画师页面和作品页面显示画师id、画师背景图, 用户头像允许右键保存
- observerFactory(function (mutations, observer) {
- if (!isMemberIndexPage()) {
- return;
- }
- for (let i = 0, len = mutations.length; i < len; i++) {
- let mutation = mutations[i];
- // 1. 判断是否改变节点, 或者是否有[section]节点
- let $target = $(mutation.target), externalLinksContainer = '_2AOtfl9'; // 多个反混淆externalLinksContainer
- let $row = $(`ul.${externalLinksContainer}`).parent();
- if (mutation.type !== 'childList' || $row.length <= 0 || $('body').find('#uid').length > 0) {
- continue;
- }
- // 1. 添加新的一行的div
- let $ahaoRow = $row.clone(), $ul = $ahaoRow.find('ul');
- $ul.empty();
- $row.before($ahaoRow);
-
- // 2. 显示画师id, 点击自动复制到剪贴板
- let $uid = $(`<li id="uid"><div style="font-size: 20px;font-weight: 700;color: #333;margin-right: 8px;line-height: 1">UID:${uid}</div></li>`)
- .on('click', function () {
- let $this = $(this);
- $this.html(`<span>UID${i18n('copy_to_clipboard')}</span>`);
- GM.setClipboard(uid);
- setTimeout(function () {
- $this.html(`<span>UID${uid}</span>`);
- }, 2000);
- });
- $ul.append($uid);
-
- // 3. 显示画师背景图
- let background = preloadData.user[uid].background;
- let url = (background && background.url) || '';
- let $bgli = $('<li><div style="font-size: 20px;font-weight: 700;color: #333;margin-right: 8px;line-height: 1"></div></li>'),
- $bg = $bgli.find('div');
- if (!!url && url !== 'none') {
- $bg.append(`<img src="${url}" width="30px"><a target="_blank" href="${url}">${i18n('background')}</a>`);
- } else {
- $bg.append(`<span>${i18n('background_not_found')}</span>`);
- }
- $ul.append($bgli);
-
- // 4. 取消监听
- GM.getValue(GMkeys.MO, true).then(function (v) { if(!v) observer.disconnect(); });
- }
- }); // 画师页面UI
- observerFactory(function (mutations, observer) {
- if (!isArtworkPage()) {
- return;
- }
-
- for (let i = 0, len = mutations.length; i < len; i++) {
- let mutation = mutations[i];
- // 1. 判断是否改变节点, 或者是否有[section]节点
- let $aside = $(mutation.target).parent().find('main').next('aside');
- if (mutation.type !== 'childList' || $aside.length <= 0) {
- continue;
- }
-
- let $row = $aside.find('section:first').find('h2');
- if ($row.length <= 0 || $aside.find('#ahao-background').length > 0) {
- continue;
- }
-
- // 2. 显示画师背景图
- let background = preloadData.user[uid].background;
- let url = (background && background.url) || '';
- let $bgDiv = $row.clone().attr('id', 'ahao-background');
- $bgDiv.children('a').remove();
- $bgDiv.children('div').children('div').remove();
- $bgDiv.prepend(`<img src="${url}" width="10%"/>`);
- $bgDiv.find('div a').attr('href', !!url ? url : 'javascript:void(0)').attr('target', '_blank')
- .text(!!url ? i18n('background') : i18n('background_not_found'));
- $row.after($bgDiv);
-
- // 3. 显示画师id, 点击自动复制到剪贴板
- let $uid = $row.clone();
- $uid.children('a').remove();
- $uid.children('div').children('div').remove();
- $uid.find('a').attr('href', 'javascript:void(0)').attr('id', 'ahao-uid').text('UID: ' + uid);
- $uid.on('click', function () {
- let $this = $(this);
- $this.find('a').text('UID' + i18n('copy_to_clipboard'));
- GM.setClipboard(uid);
- setTimeout(function () {
- $this.find('a').text('UID: ' + uid);
- }, 2000);
- });
- $bgDiv.after($uid);
-
- // 4. 取消监听
- GM.getValue(GMkeys.MO, true).then(function (v) { if(!v) observer.disconnect(); });
- }
- }); // 作品页面UI
- // 解除 用户头像 的background 限制, 方便保存用户头像
- 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. 将作者头像由 background 转为 <img>
- let $target = $(mutation.target);
- $target.find('div[role="img"]').each(function () {
- let $this = $(this);
- let tagName = $this.prop('tagName');
-
- let imgUrl = $this.getBackgroundUrl();
- if (!imgUrl) {
- return;
- }
-
- let $userImg = $('<img class="ahao-user-img" src=""/>').attr('src', imgUrl);
- $userImg.css('width', $this.css('width'))
- .css('height', $this.css('height'));
-
- // if(tagName.toLowerCase() === 'a') {
- // $this.html($userImg);
- // $this.css('background-image', '');
- // return;
- // }
-
- if (tagName.toLowerCase() === 'div') {
- $userImg.attr('class', $this.attr('class'));
- $userImg.html($this.html());
- $this.replaceWith(() => $userImg);
- return;
- }
- });
-
- // 3. 将评论头像由 background 转为 <img>
- $target.find('a[data-user_id][data-src]').each(function () {
- let $this = $(this), $div = $this.find('div'),
- $img = $('<img/>');
- $img.attr('src', $this.attr('data-src'));
- if (!!$div.length) {
- $img.attr('class', $div.attr('class'))
- .css('width', $div.css('width'))
- .css('height', $div.css('height'));
- $this.html($img);
- }
- });
- }
- });
-
- // 6. 自动加载评论
- GM.getValue(GMkeys.switchComment, true).then(open => {
- if(!open || !isArtworkPage()){
- return;
- }
- let moreCommentSelector = '._1Hom0qN';
- let moreReplaySelector = '._28zR1MQ';
- 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. 模拟点击加载按钮
- let $moreCommentBtn = $(mutation.target).find(moreCommentSelector);
- $moreCommentBtn.click();
-
- let $moreReplayBtn = $(mutation.target).find(moreReplaySelector);
- $moreReplayBtn.click();
- }
- });
- });
-
- // 7. 对主页动态中的图片标记作品类型
- (function () {
- if (!isMemberDynamicPage()) {
- return;
- }
-
- let illustTitleSelector = '.stacc_ref_illust_title';
- observerFactory(function (mutations, observer) {
- for (let i = 0, len = mutations.length; i < len; i++) {
- let mutation = mutations[i];
- // 1. 判断是否改变节点
- let $title = $(mutation.target).find(illustTitleSelector);
- if (mutation.type !== 'childList' || !$title.length) {
- continue;
- }
-
- $title.each(function () {
- let $a = $(this).find('a');
- // 1. 已经添加过标记的就不再添加
- if (!!$a.attr('ahao-illust-id')) {
- return;
- }
- // 2. 获取pid, 设置标记避免二次生成
- let illustId = new URL(location.origin + '/' + $a.attr('href')).searchParams.get('illust_id');
- $a.attr('ahao-illust-id', illustId);
- // 3. 调用官方api, 判断作品类型
- $.ajax({
- url: '/ajax/illust/' + illustId, dataType: 'json',
- success: response => {
- let illustType = parseInt(response.body.illustType);
- let isMultiPic = parseInt(response.body.pageCount) > 1;
- switch (illustType) {
- case 0:
- case 1:
- $a.after('<p>' + (isMultiPic ? i18n('illust_type_multiple') : i18n('illust_type_single')) + '</p>');
- break;
- case 2:
- $a.after('<p>' + i18n('illust_type_gif') + '</p>');
- 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 = $(`<span class="sc-LzLvL">${i18n('sort_by_popularity')}</span>`);
- $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 = $(`<table style="width: 700px;">
- <tbody>
- <tr><th width="185">Pixiv增强配置</th><td width="500">
- <label><input type="checkbox" name="${GMkeys.MO}">兼容PJAX(推荐)</label><br/>
- <label><input type="checkbox" name="${GMkeys.switchComment}">自动加载评论</label><br/>
- <label><input type="checkbox" name="${GMkeys.switchImgSize}">显示图片尺寸大小</label><br/>
- <label><input type="checkbox" name="${GMkeys.switchImgPreload}">预下载Gif、Zip(耗流量)</label><br/>
-
- <label>下载文件名: <input type="text" name="${GMkeys.downloadName}" placeholder="{pid}-{uid}-{pname}-{uname}"></label>
- <a>保存</a>
- <a onclick="alert('{pid}是作品id--------{uid}是画师id\\n{pname}是作品名--------{uname}是画师名\\n注意, 多图情况下, 会自动填充index索引编号\\n目前只支持GIF和多图的重命名');">说明</a>
- </td></tr>
- </tbody>
- </table>`);
- $('.settingContent table:first').after($table);
-
- $table.find('input[type="checkbox"]').each(function () {
- let $checkbox = $(this), name = $checkbox.attr('name');
- GM.getValue(name, true).then(function (value) { $checkbox.prop('checked', value); });
- $checkbox.on('change', function () {
- let checked = $checkbox.prop('checked');
- $checkbox.prop(checked, checked);
- GM.setValue(name, checked);
- });
- });
- $table.find('input[type="text"]').each(function () {
- let $input = $(this), name = $input.attr('name');
- GM.getValue(name).then(function (value) { $input.val(value); });
- $input.on('change', () => {
- GM.setValue(name, $input.val());
- });
- });
- })();
-
- //TODO 增强新页面fanbox https://www.pixiv.net/fanbox/creator/22926661?utm_campaign=www_profile&utm_medium=site_flow&utm_source=pixiv
- //TODO 日语化
- //TODO 搜索框ui混乱 https://www.pixiv.net/member_illust.php?mode=medium&illust_id=899657
- });