您需要先安装一个扩展,例如 篡改猴、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://gf.qytechs.cn/scripts/444466-mini-mvvm/code/mini%20mvvm.js?version=1047057 // @license GPL-3.0 // @compatible Chrome // @version 1.4.9 // @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) { let url = 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()
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址