Nyaa.si - Load More Thumbnail

Load image from cover/screenshot links.

As of 2024-04-05. See the latest version.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Nyaa.si - Load More Thumbnail
// @name:zh-CN   Nyaa.si - 自动加载更多预览图
// @namespace    none
// @description  Load image from cover/screenshot links.
// @description:zh-CN  从封面/截图链接加载图片并显示。
// @icon         https://www.google.com/s2/favicons?sz=64&domain=sukebei.nyaa.si
// @version      0.9.2
// @license      MIT
// @author       York Wang
// @match        https://sukebei.nyaa.si/*
// @match        https://pics.dmm.co.jp/*
// @match        https://img169.com/*
// @match        https://xpic.org/*
// @match        https://imgrock.pw/*
// @match        https://picrok.com/*
// @match        https://picbaron.com/*
// @match        https://imgbaron.com/*
// @match        https://kvador.com/*
// @match        https://kropic.com/*
// @match        https://imgsto.com/*
// @match        https://imgsen.com/*
// @match        https://imgstar.eu/*
// @match        https://picdollar.com/*
// @match        https://pics4you.net/*
// @match        https://silverpic.com/*
// @match        https://fotokiz.com/*
// @match        https://premalo.com/*
// @match        https://piczhq.com/*
// @match        https://trypicz.com/*
// @match        http://imglord.com/*
// @match        https://croea.com/*
// @match        https://imgtaxi.com/*
// @match        https://imgadult.com/*
// @match        https://imgdrive.net/*
// @match        https://xxxwebdlxxx.org/*
// @match        https://xxxwebdlxxx.top/*
// @match        https://uvonahaze.xyz/*
// @match        https://trans.firm.in/*
// @match        https://imgdawgknuttz.com/*
// @match        https://imagetwist.netlify.app/*
// @match        https://imagetwist.com/*
// @match        https://imagexport.com/*
// @match        https://imagenimage.com/*
// @match        https://hentai4free.net/*
// @match        https://pixhost.to/*
// @match        https://imgair.net/*
// @match        http://imgair.net/*
// @match        http://imgfrost.net/*
// @match        http://imgblaze.net/*
// @match        https://pig69.com/*
// @match        https://ai18.pics/*
// @match        https://porn4f.com/*
// @match        https://javball.com/*
// @match        https://ovabee.com/*
// @match        https://idol69.net/*
// @match        https://cnpics.org/*
// @match        https://cnxx.me/*
// @match        https://cosplay18.pics/*
// @run-at       document-end
// @grant        unsafeWindow
// @grant        GM_xmlhttpRequest
// ==/UserScript==

(function() {
    'use strict';

    function Handler(pattern, process, processNyaa) {
        this.pattern = pattern
        this.process = process
        this.processNyaa = processNyaa
    }
    Handler.prototype.canHandle = function(url) {
        return this.pattern.test(url)
    }
    Handler.prototype.handle = function(url) {
        this.process(href => {
            document.location.href = href
            unsafeWindow.top.postMessage({"LMT": href, "LMT_SRC": url}, '*')
        })
    }
    Handler.prototype.handleNyaa = function(url) {
        if(this.processNyaa) {
            this.processNyaa(url, href => {
                unsafeWindow.top.postMessage({"LMT": href, "LMT_SRC": url}, '*')
            })
        } else {
            unsafeWindow.LMT_Frame.src = url
        }
    }
    const handlers = []
    const addHandler = (pattern, process, processNyaa) => handlers.push(new Handler(pattern, process, processNyaa))

    addHandler(/^https?:\/\/pics\.dmm\.co\.jp(\/[\w]+)+\.jpg.*/, callback => {
        callback(document.location.href.match(/^https?:\/\/pics\.dmm\.co\.jp(\/[\w]+)+\.jpg/)[0])
    })
    addHandler(/^https?:\/\/img169\.com\/images\/\d{4}\/\d{2}\/\d{2}\/\w+\.jpg/, callback => {
        return false
    }, (url, callback) => {
        callback(url)
    })
    addHandler(/^https?:\/\/xpic\.org(\/\w+)+/, callback => {
        unsafeWindow.wuLu && unsafeWindow.wuLu()
        const img = document.querySelector('img.attachment-original.size-original')
        if(img) {
            callback(img.src)
        }
    }, (url, callback) => {
        GM_xmlhttpRequest({
            method: 'GET',
            url: url,
            onload: res => {
                const src = res.responseText.match(/src="(.*)" class="attachment-original size-original"/)
                if(src.length > 1) callback(src[1])
            }
        })
    })
    addHandler(/^https?:\/\/(imgrock\.pw)(\/[\w\-]+)+(\.[\w\-]+)+/, callback => {
        // pause on CAPTCHA
        const iframe = document.querySelector('iframe')
        if(iframe && iframe.src.indexOf('captcha') > -1) return

        const img = document.querySelector('.picview')
        if(img) {
            callback(img.src)
        } else {
            const btns = document.querySelectorAll('input[name=fnext]')
            for(let i=0;i<btns.length;i++) {if(!btns[i].style.display) btns[i].click()}
            const forms = document.querySelectorAll('form')
            for(let i=0;i<forms.length;i++) {if(forms[i].hito) {forms[i].submit()}}
        }
    })
    addHandler(/^https?:\/\/(picrok\.com)(\/[\w\-]+)+\.php/, callback => {
        // pause on CAPTCHA
        const iframe = document.querySelector('iframe')
        if(iframe && iframe.src.indexOf('captcha') > -1) return

        const img = document.querySelector('.picview')
        if(img) {
            callback(img.src)
        } else {
            unsafeWindow.setTimeout(() => {
              const forms = document.querySelectorAll('form')
              const btns = document.querySelectorAll('form>button')
              // for(let i=0;i<btns.length;i++) {if(btns[i].style.display) forms[i-1].submit()}
            }, 5000)
        }
    })
    addHandler(/^https?:\/\/(picbaron\.com|imgbaron\.com|kvador\.com|kropic\.com|imgsto\.com|imgsen\.com|imgstar\.eu|picdollar\.com|pics4you\.net|silverpic\.com|fotokiz\.com|premalo\.com|piczhq\.com|trypicz\.com|imglord\.com)(\/.+)+(\.[\w\-]+)+/, callback => {
        const img = document.querySelector('.pic')
        if(img) {
            callback(img.src)
        } else {
            const form = document.querySelector('form')
            form && form.submit()
        }
    })
    addHandler(/^https?:\/\/(croea\.com)(\/\w+)+/, callback => {
        const img = document.querySelector('.pic')
        if(img) {
            callback(img.src)
        } else {
            const form = document.querySelector('form')
            form && form.submit()
        }
    }, (url, callback) => {
        GM_xmlhttpRequest({
            method: 'GET',
            url: url,
            onload: res => {
                const src = res.responseText.match(/src="(.*)" class="pic img img-responsive"/)
                if(src.length > 1) {
                    GM_xmlhttpRequest({
                        method: 'GET',
                        responseType: "blob",
                        url: src[1],
                        onload: res => {
                            const reader = new FileReader()
                            reader.onload = () => {
                                callback(reader.result)
                            }
                            reader.readAsDataURL(res.response)
                        }
                    })
                }
            }
        })
    })
    addHandler(/^https?:\/\/(imgtaxi\.com|imgadult\.com|imgdrive\.net)(\/\w+)+/, callback => {
        unsafeWindow.ctipops = []
        unsafeWindow.adbctipops = []
        const img = document.querySelector('img.centred') || document.querySelector('img.centred_resized')
        if(img) {
            callback(img.src)
        } else {
            unsafeWindow.setTimeout(() => {
                const btn = document.querySelector('.overlay_ad_link')
              if(btn) {
                btn.focus()
                btn.click()
              }
            }, 1000)
        }
    }, (url, callback) => {
        GM_xmlhttpRequest({
            method: 'GET',
            url: url,
            onload: res => {
                const src = res.responseText.match(/og:image:secure_url" content="(.*)"/)
                if(src.length > 1) callback(src[1].replace('small','big'))
            }
        })
    })
    addHandler(/^https?:\/\/(xxxwebdlxxx\.org|xxxwebdlxxx\.top)(\/\w+)+/, callback => {
        unsafeWindow.ctipops = []
        unsafeWindow.adbctipops = []
        const img = document.querySelector('img.centred') || document.querySelector('img.centred_resized')
        if(img) {
            callback(img.src)
        } else {
            unsafeWindow.setTimeout(() => {
                const btn = document.querySelector('.overlay_ad_link')
              if(btn) {
                btn.focus()
                btn.click()
              }
            }, 1000)
        }
    })
    addHandler(/^https?:\/\/(uvonahaze\.xyz|trans\.firm\.in||imgdawgknuttz\.com)(\/\w+)+/, callback => {
        const img = document.querySelector('img.centred') || document.querySelector('img.centred_resized')
        if(img) {
            callback(img.src)
        } else {
            const btn = document.querySelector('input[name=imgContinue]')
            btn && btn.click()
        }
    })
    addHandler(/^https?:\/\/(imagetwist\.netlify\.app)(\/\w+)+/, callback => {
        if(unsafeWindow.targetURL) document.location.href = unsafeWindow.targetURL
        const btn = document.querySelector('a.btn')
        btn && btn.click()
    })
    addHandler(/^https?:\/\/(imagetwist\.com|imagexport\.com|imagenimage\.com)(\/\w+)+/, callback => {
        const img = document.querySelector('.img-responsive')
        if(img) {
            callback(img.src)
        }
    })
    addHandler(/^https?:\/\/hentai4free\.net(\/\w+)+/, callback => {
        unsafeWindow.wuLu && unsafeWindow.wuLu()
        const img = document.querySelector('#image-viewer-container>img')
        if(img) {
            callback(img.src)
        }
    })
    addHandler(/^https?:\/\/pixhost\.to(\/\w+)+/, callback => {
        const img = document.querySelector('img.image-img')
        if(img) {
            callback(img.src)
        } else {
            const btn = document.querySelector('a.continue')
            btn && btn.click()
        }
    })
    addHandler(/^https?:\/\/(imgair\.net|imgfrost\.net|imgblaze\.net)(\/\w+)+/, callback => {
        unsafeWindow.wuLu && unsafeWindow.wuLu()
        const img = document.querySelector('#newImgE')
        if(img) {
            callback(img.src)
        }
    }, (url, callback) => {
        GM_xmlhttpRequest({
            method: 'GET',
            url: url.replace(/^https?:\/\/(imgfrost\.net|imgblaze\.net)/, 'https://imgair.net'),
            onload: res => {
                const txt = res.responseText
                let title = txt.match(/<title>(.*)<\/title>/)
                if(!title) return false
                title = title[1].trim()
                const badChar = /[\[\(\$\^\.\]\*\\\?\+\{\}\\|\)]/gi
                const pattern = new RegExp("(http.*" + title.replace(badChar, (c) => `\\${c}`) + ")")
                const src = txt.match(pattern)
                if(src.length > 0) callback(src[0])
            }
        })
    })
    addHandler(/^https?:\/\/(pig69\.com|ai18\.pics|porn4f\.com|javball\.com|ovabee\.com|idol69\.net|cnpics\.org|cnxx\.me|cosplay18\.pics)(\/\w+)+/, callback => {
        const img = document.querySelector('#fileOriginalModal img')
        if(img) {
            callback(img.src)
        } else {
            const btn = document.querySelector('a.continue')
            btn && btn.click()
        }
    }, (url, callback) => {
        GM_xmlhttpRequest({
            method: 'GET',
            url: url,
            onload: res => {
                const src = res.responseText.match(/"https?:\/\/((pig69\.com|ai18\.pics|porn4f\.com|javball\.com|ovabee\.com|idol69\.net|cnpics\.org|cnxx\.me|cosplay18\.pics)\/upload\/Application\/storage\/app\/public\/uploads\/users\/.*)"/)
                if(src.length > 1) callback("http://"+src[1])
            }
        })
    })

    const href = document.location.href
    if(/^https?:\/\/(sukebei\.nyaa\.si).+/g.test(href)) {

        let LMT_Wrap, LMT_Frame, LMT_Loading, LMT_panel
        const panelWidth = 480
        const panelHeight = 480
        function createWrap(parent) {
            parent.parentNode.insertAdjacentHTML('afterend', '<div class="panel panel-default"><div class="panel-body" id="LMT_Wrap"></div></div>')
            LMT_Wrap = document.querySelector('#LMT_Wrap')

            LMT_Loading = document.createElement('div')
            LMT_Loading.textContent = 'Loading Images...'
            LMT_Wrap.appendChild(LMT_Loading)
        }
        function createPanel() {
            LMT_panel = document.createElement('div')
            LMT_panel.style.position = 'fixed'
            LMT_panel.style.top = '-1000px'
            LMT_panel.style.left = '-1000px'
            LMT_panel.style.backgroundColor = '#f5f5f5'
            LMT_panel.style.backgroundSize = 'contain'
            LMT_panel.style.backgroundRepeat = 'no-repeat'
            LMT_panel.style.backgroundPosition = 'center'
            LMT_panel.style.border = '1px solid #ddd'
            LMT_panel.style.borderRadius = '6px'
            LMT_panel.style.boxShadow = '0 1px 1px rgba(0,0,0,.05)'
            LMT_panel.style.width = `${panelWidth}px`
            LMT_panel.style.height = `${panelHeight}px`
            document.body.appendChild(LMT_panel)

            LMT_Frame = document.createElement('iframe')
            LMT_Frame.id = 'LMT_Frame'
            LMT_Frame.sandbox = 'allow-forms allow-scripts allow-same-origin'
            LMT_Frame.style.display = 'none'
            document.body.appendChild(LMT_Frame)
        }

        const imgList = []

        function process() {
            if(imgList.length) {
                let url = imgList.shift()
                url.handler.handleNyaa(url.href)
            } else {
                if(LMT_Wrap) {
                    LMT_Loading.innerText = ''
                    document.body.removeChild(LMT_Frame)
                } else {
                    LMT_Frame.src = 'about:blank'
                }
            }
        }

        unsafeWindow.addEventListener('message', function (e) {
            if(e.data.LMT) {
                if(LMT_Wrap) {
                    LMT_Frame.src = ''
                    const img = document.createElement('img')
                    img.src = e.data.LMT
                    img.style['max-width'] = '100%'
                    LMT_Wrap.appendChild(img)
                }
                if(e.data.LMT_SRC) {
                    const url_src = e.data.LMT_SRC
                    const a = document.querySelector(`a[data-lmt-src="${url_src}"]`)
                    if(!a.dataset.lmt) {
                        a.dataset.lmt = e.data.LMT
                        const span = document.createElement("span");
                        span.innerHTML='✅'
                        span.dataset.lmt = e.data.LMT
                        a.before(span)
                    }
                }
                process()
            }
        })

        let windowWidth = unsafeWindow.innerWidth
        let windowHeight = unsafeWindow.innerHeight
        unsafeWindow.addEventListener('resize', function (e) {
            windowWidth = unsafeWindow.innerWidth
            windowHeight = unsafeWindow.innerHeight
        })
        unsafeWindow.addEventListener('mouseover', function (e) {
            const a = e.target
            if(a.dataset.lmt) {
                LMT_panel.style.backgroundImage = `url('${a.dataset.lmt}')`
                if(e.clientX + panelWidth < windowWidth) {
                    LMT_panel.style.left = e.clientX+'px'
                } else {
                    LMT_panel.style.left = e.clientX-panelWidth+'px'
                }
                if(e.clientY + panelHeight < windowHeight) {
                    LMT_panel.style.top = e.clientY+'px'
                } else {
                    LMT_panel.style.top = e.clientY-panelHeight+'px'
                }
            }
        })
        unsafeWindow.addEventListener('mouseout', function (e) {
            const a = e.target
            if(a.dataset.lmt) {
                LMT_panel.style.top = '-1000px'
                LMT_panel.style.left = '-1000px'
            }
        })

        if(/^https?:\/\/(sukebei\.nyaa\.si\/view\/).+/g.test(href)) {
            // Detail Page
            const desc = document.querySelector('#torrent-description')
            const links = desc.querySelectorAll('a')
            if(!desc || !links) return

            for(let i = 0; i < links.length; i++) {
                if(!links[i].href) continue
                links[i].dataset.lmtSrc = links[i].href
                handlers.forEach(h=>{h.canHandle(links[i].href) && imgList.push({href:links[i].href,handler:h})})
            }

            if(!LMT_Frame) {
                createWrap(desc)
            }
            createPanel()
            process()
        } else {
            // List Page
            unsafeWindow.addEventListener('mouseover', async (e) => {
                const a = e.target
                if(!a.dataset.lmtSrc && a.href) {
                    const detail = await getDetail(a.href)
                    let desc = detail.responseText.match(/id="torrent-description">(.*?)<\/div>/)
                    if(!desc) return false
                    desc = desc[1].replaceAll('&#10;', '\n')
                    const matchs = desc.match(/(https?:\/\/[^\s\)]+)/g)
                    let flag = false
                    for (let i in matchs) {
                        const href = matchs[i]
                        for(let j in handlers) {
                            const h = handlers[j]
                            if(href.indexOf('nyaa') < 0 && h.canHandle(href)) {
                                a.dataset.lmtSrc = href
                                imgList.push({href:href,handler:h})
                                flag = true
                                break
                            }
                        }
                        if(flag) {
                            process()
                            break
                        }
                    }
                    if(!flag) {
                        a.dataset.lmtSrc = '#'
                    }
                }
            })
            createPanel()
            const getDetail = url => {
                return new Promise((resolve, reject) => {
                    GM_xmlhttpRequest({
                        method: 'GET',
                        url: url,
                        onload: res => { resolve(res) },
                        onError: err => { reject(err) }
                    })
                })
            }
        }
    } else {
        // Image Host Websites
        handlers.forEach(h=>{h.canHandle(href) && h.handle(document.location.href)})
    }
})();