您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
下载原始图片
当前为
// ==UserScript== // @name MyFigureCollection: 图片下载器 // @name:en MyFigureCollection: Image Downloader // @name:zh-CN MyFigureCollection: 图片下载器 // @name:zh-TW MyFigureCollection: 图片下载器 // @description 下载原始图片 // @description:en The original fullsize images downloader // @description:zh-CN 下载原始图片 // @description:zh-TW 下载原始图片 // @namespace Violentmonkey Scripts // @match https://myfigurecollection.net/picture/* // @match https://myfigurecollection.net/pictures.php // @grant GM_download // @grant GM_xmlhttpRequest // @grant GM_getValue // @grant GM_setValue // @require https://greasyfork.org/scripts/444466-mini-mvvm/code/mini%20mvvm.js?version=1047057 // @license GPL-3.0 // @compatible Chrome // @version 1.4.8 // @author ayan0312 // ==/UserScript== const TIME_OUT = 30 * 1000 const FILTER_URLS = [ 'https://static.myfigurecollection.net/ressources/nsfw.png', 'https://static.myfigurecollection.net/ressources/spoiler.png' ] const globalState =observe({ group:GM_getValue('groupCount',1), count:GM_getValue('picCount',1), componentDownloadStatus:{}, downloadStates:{ total:0, normal:0, loading:0, error:0, timeout:0, downloaded:0, } }) new Watcher(null,()=>{ return globalState.count },(newVal)=>{ GM_setValue('picCount',newVal) }) new Watcher(null,()=>{ return globalState.group },(newVal)=>{ GM_setValue('groupCount',newVal) globalState.count = 0 }) new Watcher(null,()=>{ return globalState.componentDownloadStatus },(newVal)=>{ Object.assign(globalState.downloadStates,{ total:0, normal:0, loading:0, error:0, timeout:0, downloaded:0, }) const states = globalState.downloadStates Object.keys(newVal).forEach(key=>{ const status = newVal[key] if(states[status] != null){ states[status]+=1 states.total+=1 } }) },true) function beforeDownload(){ if(GM_getValue('groupCount',1) != globalState.group){ const curCount = GM_getValue('picCount',1) + 1 globalState.group = GM_getValue('groupCount',1) globalState.count = curCount }else{ globalState.group = GM_getValue('groupCount',1) globalState.count = GM_getValue('picCount',1) + 1 } return {group:globalState.group,count:globalState.count} } const DownloadSequence = { template: ` <span>Group: </span> <button v-on:click="decreaseGroup">-</button> <span style="margin:0 10px">{{global.group}}</span> <button v-on:click="increaseGroup">+</button> <span style="margin-left:10px">Item: </span> <button v-on:click="decreaseCount">-</button> <span style="margin:0 10px">{{global.count}}</span> <button v-on:click="increaseCount">+</button> `, data(){ return { global:globalState, } }, methods:{ increaseCount(){ this.global.count+=1 }, decreaseCount(){ this.global.count-=1 }, increaseGroup(){ this.global.group+=1 }, decreaseGroup(){ this.global.group-=1 } } } const REQEUST_BUTTON_STYLES = { normal:{}, loading: { background: 'white',color:'black',cursor:'wait'}, error:{background:'red',color:'white'}, timeout:{background:'yellow',color:'black'}, downloaded: {background:'green',color:'white'} } const DownloadButton = { template: ` <button v-on:click="download" v-style="downloadBtnStyle"> {{downloadedMsg}} </button> `, data(){ return { oldStatus:'normal', downloadStatus:'normal' // 'normal' 'loading' 'error' 'timeout' 'downloaded' } }, computed:{ downloadBtnStyle(){ return REQEUST_BUTTON_STYLES[this.downloadStatus] }, downloadedMsg(){ const messages = { normal:'Download' , loading: 'Downloading...', error:'Failed', timeout:'Timeout', downloaded: 'Redownload' } return messages[this.downloadStatus] } }, watch:{ downloadStatus(newStatus,oldStatus){ this.oldStatus = oldStatus globalState.componentDownloadStatus[this.cid] = newStatus } }, created(){ globalState.componentDownloadStatus[this.cid] = this.downloadStatus }, destoryed(){ delete globalState.componentDownloadStatus[this.cid] }, methods:{ download(){ if(this.downloadStatus === 'loading') return refreshGroup() this.downloadStatus = 'loading' if(this.oldStatus !== 'error' && this.oldStatus !== 'timeout') this.value = beforeDownload() this.$emit('download',this.value) }, } } const DownloadState = { template:` <div style="display:flex;flex-direction:row;padding:5px;flex-wrap:wrap"> <div style="margin-right:15px;color:black"> <span style="">Total:</span> <span>{{states.total}}</span> </div> <div style="margin-right:15px;color:green"> <span style="">Downloaded:</span> <span>{{states.downloaded}}</span> </div> <div style="margin-right:15px;color:grey"> <span style="">Downloading:</span> <span>{{states.loading}}</span> </div> <div style="margin-right:15px;color:brown"> <span style="">Timeout:</span> <span>{{states.timeout}}</span> </div> <div style="color:red"> <span>Failed:</span> <span>{{states.error}}</span> </div> </div> `, data(){ return { states:globalState.downloadStates } }, methods:{ download(value){ this.$emit('download',value) } } } const PictureDownload = { components:{ 'download-button':DownloadButton, 'download-sequence':DownloadSequence, }, template:` <div> <download-sequence></download-sequence> <span v-show:downloaded>{{msg}}</span> <download-button v-ref="downloadButton" v-on:download="download"></download-button> </div> `, data(){ return { group:0, count:0, downloaded:false } }, computed:{ msg(){ return `Group: ${this.group} Item: ${this.count}` } }, mounted(){ const value = GM_getValue(window.location.href.split('&')[0]) if(!value) return this.$refs.downloadButton.downloadStatus = value.downloadStatus this.$refs.downloadButton.value = { group: value.group, count: value.count } this.downloaded = true this.group = value.group this.count = value.count }, methods:{ download(value){ this.downloaded = true this.group = value.group this.count = value.count this.$emit('download',value) } } } function insertAfter(targetNode, afterNode){ const parentNode = afterNode.parentNode const beforeNode = afterNode.nextElementSibling if(beforeNode == null) parentNode.appendChild(targetNode) else parentNode.insertBefore(targetNode, beforeNode) } function mountVM(node,component){ const vm = new MVVMComponent(component) vm.$mount(node) return vm } function download({vm,picture,group,count,origin}){ const values = origin.split('/') const fileType = values[values.length - 1].split('.')[1] const name = `${group}.${count}.${fileType}` const end = (status)=>{ return ()=>{ vm.downloadStatus = status GM_setValue(picture.split('&')[0],{origin,group,count,downloadStatus:status}) } } GM_download({ url:origin, name, timeout:TIME_OUT, onload:end('downloaded'), onerror:end('error'), ontimeout:end('timeout'), }) } function renderPictureExtension(objectMeta,thePicture){ const origin = thePicture.href const div = document.createElement('div') objectMeta.appendChild(div) const vm = mountVM(div,PictureDownload) vm.$on('download',(value)=>{ download({ ...value, origin, picture:window.location.href, vm:vm.$refs.downloadButton, }) }) } const createPictruePreview = ({thumb,origin,picture}) => { const commonStyle={ 'margin':'10px 10px', 'border':'1px solid #000', 'padding':'10px', 'border-radius':'5px', 'background':'#fff', 'transition':'all 0.5s', 'box-sizing': 'border-box' } return { components:{ 'download-button':DownloadButton }, template: ` <div v-style="containerStyle"> <div style="margin-bottom:10px;display:flex;flex-direction:row;justify-content:center;align-items:center;width:100%;"> <div style="margin:0 10px;flex-shrink:0"><img style="cursor:pointer" v-on:click="openPicturePage" [src]="thumb" /></div> <div style="flex-shrink:1" v-show="originalImage"><img style="width:100%" [src]="src" /></div> </div> <div style="display:flex;justify-content:center;align-items:center;flex-direction:column"> <download-button v-ref="downloadButton" v-on:download="download"></download-button> <br v-show:downloaded /> <div v-show:downloaded> <span>Group:</span> <span >{{group}}</span> <span>Item:</span> <span >{{count}}</span> </div> <br /> <button v-on:click="toggle">{{msg}}</button> <br /> <button v-show:refresh v-on:click="refreshOrigin" v-style="refreshBtnStyle"> {{refreshMsg}} </button> </div> </div> `, data(){ return { thumb, origin, picture, refresh:FILTER_URLS.includes(origin), originalImage:false, group:0, count:0, downloaded:false, refreshStatus:'normal' // 'normal' 'loading' 'error' 'timeout' 'downloaded' } }, computed:{ src(){ return this.originalImage ? this.origin : this.thumb }, msg(){ return this.originalImage ? 'Close Preview' : 'Preview' }, containerStyle(){ return Object.assign({},commonStyle,this.originalImage ? { width:'100%' }:{} ) }, refreshBtnStyle(){ return REQEUST_BUTTON_STYLES[this.refreshStatus] }, refreshMsg(){ const messages = { normal:'Show Spoiler/NSFW' , loading: 'Showing...', error:'Failed', timeout:'Timeout', downloaded: 'Reshow' } return messages[this.refreshStatus] } }, mounted(){ const value = GM_getValue(picture.split('&')[0]) if(!value) return this.$refs.downloadButton.downloadStatus = value.downloadStatus this.$refs.downloadButton.value = { group: value.group, count: value.count } this.downloaded = true this.group = value.group this.count = value.count }, methods:{ download(value){ this.downloaded = true this.group = value.group this.count = value.count if(this.refresh && this.refreshStatus !== 'downloaded') this.refreshOrigin() .then(()=>{ this.$emit('download',{...value,origin:this.origin}) }) else this.$emit('download',{...value,origin:this.origin}) }, toggle(){ if(this.refresh && !this.originalImage && this.refreshStatus !== 'downloaded') this.refreshOrigin() this.originalImage = !this.originalImage }, refreshOrigin(){ if(this.refreshStatus === 'loading') return this.refreshStatus = 'loading' return new Promise((resolve,reject)=>{ GM_xmlhttpRequest({ url:this.picture, responseType:'document', timeout:TIME_OUT, onload:(data)=>{ const doc = data.response const a = doc.querySelector('.the-picture>a') if(a){ this.origin = a.href const thumb = a.href.split('/') thumb.splice(thumb.length - 1,0,'thumbnails') this.thumb = thumb.join('/') this.refreshStatus = 'downloaded' resolve() return } this.refreshStatus = 'error' reject() }, onerror:()=>{ this.refreshStatus = 'error' reject() }, ontimeout:()=>{ this.refreshStatus = 'timeout' reject() } }) }) }, openPicturePage(){ window.open(picture) } } } } function parseOriginalImageURL(thumb_url){ if(thumb_url.indexOf('thumbnails/') > -1){ url = thumb_url.split('thumbnails/').join('') } else { const paths = thumb_url.split('pictures/')[1].split('/') if(paths.length > 2){ paths.splice(3,1) url = [thumb_url.split('pictures/')[0],paths.join('/')].join('pictures/') } } return url } function getImageURLs(node){ const picture = node.querySelector('a').href const viewport = node.querySelector('.viewport') const thumb = viewport.style.background.split('"')[1] let origin = thumb if(FILTER_URLS.includes(origin)) origin = thumb else origin = parseOriginalImageURL(origin) return {thumb,origin,picture} } function refreshGroup(){ const itemId = (new URL(location.href)).searchParams.get('itemId') if(itemId && !GM_getValue(itemId)){ GM_setValue(itemId,true) if(globalState.count > 0) globalState.group+=1 } } function renderPicturesExtension(thumbs){ function _initParentNode(parentNode){ parentNode.innerHTML = '' parentNode.style.setProperty('display','flex') parentNode.style.setProperty('flex-direction','row') parentNode.style.setProperty('flex-wrap','wrap') parentNode.style.setProperty('justify-content','center') parentNode.style.setProperty('align-items','center') const div1 = document.createElement('div') parentNode.parentNode.insertBefore(div1,parentNode) mountVM(div1,DownloadSequence) const div2 = document.createElement('div') const pageCount = document.querySelector('.listing-count-pages') || parentNode pageCount.parentNode.insertBefore(div2,pageCount) mountVM(div2,DownloadState) } const parentNode = thumbs[0].parentNode _initParentNode(parentNode) thumbs.forEach(thumb_node=>{ const imageURLs = getImageURLs(thumb_node) const preview = createPictruePreview(imageURLs) const div3 = document.createElement('div') parentNode.appendChild(div3) const previewVM = mountVM(div3,preview) previewVM.$on('download',(value)=>{ download({ ...value, picture:imageURLs.picture, vm:previewVM.$refs.downloadButton, }) }) }) } function render(){ const objectMeta = document.querySelector('.object-meta') const thePicture = document.querySelector('.the-picture>a') if(objectMeta && thePicture) renderPictureExtension(objectMeta,thePicture) const thumbs = [] document.querySelectorAll('.picture-icon.tbx-tooltip').forEach(thumb=>{ thumbs.push(thumb) }) if(thumbs.length > 0) renderPicturesExtension(thumbs.reverse()) } render()