Greasy Fork

Greasy Fork is available in English.

NGA 图片浏览器

爬取当前页面图片,以图片浏览器形式展示,提供批量下载功能

当前为 2021-03-11 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         NGA 图片浏览器
// @namespace    http://greasyfork.icu/zh-CN/users/164691-shy07
// @version      0.02
// @description  爬取当前页面图片,以图片浏览器形式展示,提供批量下载功能
// @author       Shy07
// @match        *://nga.178.com/*
// @match        *://bbs.ngacn.cc/*
// @match        *://bbs.nga.cn/*
// @match        *://ngabbs.com/*
// @grant        GM.xmlHttpRequest
// @grant        GM_xmlHttpRequest
// jshint esversion:6
// ==/UserScript==

((n, self) => {
  'use strict'

  if (n === undefined) return

  const targetNode = document.querySelector('body')
  const config = {
    childList: true
  }
  let imageSources = []
  let currentImage = 0
  const callerId = '_shy07_gallery_caller'
  const containerClass = '_shy07_gallery_container'
  const imgClass = '_shy07_gallery_img'
  const galleryContainerStyle = `
    display: block;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    position: fixed;
    background: rgba(0, 0, 0, 0.9);
`
  const galleryImgStyle = `
    display: block;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    background-repeat: no-repeat;
    background-size: contain;
    background-position: center;
  `
  const arrowStyle = `
    display: block;
    position: fixed;
    top: 0;
    line-height: 100vh;
    color: #fff;
    font-size: 5rem;
    text-decoration-line: none;
  `
  const leftArrowStyle = `
    left: 0;
    padding-left: 1rem;
  `
  const rightArrowStyle = `
    right: 0;
    padding-right: 1rem;
  `
  const closeBtnStyle = `
    display: text-block;
    position: fixed;
    top: 0;
    right: 0;
    padding: 1rem 2rem;
    color: #fff;
    text-decoration-line: none;
  `
  const topLeftMenuStyle = `
    position: fixed;
    top: 0;
    left: 0;
    padding: 1rem 2rem;
  `
  const topLeftMenuItemStyle = `
    display: text-block;
    margin-right: 1rem;
    color: #fff;
    text-decoration-line: none;
  `

  const showCollapseContent = container => {
    const elements = container.querySelectorAll('.collapse_btn')
    if (elements && elements.length > 0) {
      elements.forEach(ele => {
        const btn = ele.querySelector('button')
        btn && btn.click()
      })
    }
  }

  const setImageSrc = src => {
    const img = document.querySelector('.' + imgClass)
    if (img) img.style.backgroundImage = `url(${src})`
  }
  const prevImage = () => {
    currentImage = currentImage === 0 ? imageSources.length - 1 : currentImage - 1
    setImageSrc(imageSources[currentImage])
  }
  const nextImage = () => {
    currentImage = currentImage === imageSources.length - 1 ? 0 : currentImage + 1
    setImageSrc(imageSources[currentImage])
  }
  const handleKeydown = ev => {
    const code = ev.keyCode
    if (code === 37) {
      prevImage()
    } else if (code === 39) {
      nextImage()
    }
  }

  const ajaxDownload = (url, callback, args, tryTimes) => {
    tryTimes = tryTimes || 0
    const GM_download = GM.xmlHttpRequest || GM_xmlHttpRequest
    const clearUrl = url.replace(/[&\?]?download_timestamp=\d+/, '')
    const retryUrl = clearUrl + (clearUrl.indexOf('?') === -1 ? '?' : '&') + 'download_timestamp=' + new Date().getTime()
    GM_download({
      method: 'GET',
      responseType: 'blob',
      url: url,
      onreadystatechange: (responseDetails) => {
        if (responseDetails.readyState === 4) {
          if (responseDetails.status === 200 || responseDetails.status === 304 || responseDetails.status === 0) {
            const blob = responseDetails.response, size = blob && blob.size
            if (size && (size / 1024 >= 5)) {
              callback(blob, args)
            } else if (tryTimes++ == 3) {
              callback(blob, args)
            } else {
              ajaxDownload(retryUrl, callback, args, tryTimes)
            }
          } else {
            if (tryTimes++ == 3) {
              callback(null, args)
            } else {
              ajaxDownload(retryUrl, callback, args, tryTimes)
            }
          }
        }
      },
      onerror: (responseDetails) => {
        if (tryTimes++ == 3) {
          callback(null, args)
        } else {
          ajaxDownload(retryUrl, callback, args, tryTimes)
        }
        console.log(responseDetails.status)
      }
    })
  }

  const fileNameFromHeader = (disposition, url) => {
    if (disposition && /filename=.*/ig.test(disposition)) {
      const result = disposition.match(/filename=.*/ig)
      return decodeURI(result[0].split("=")[1])
    }
    return url.substring(url.lastIndexOf('/') + 1)
  }

  const downloadBlobFile = (content, fileName) => {
      if ('msSaveOrOpenBlob' in navigator) {
        navigator.msSaveOrOpenBlob(content, fileName)
      } else {
        const aLink = document.createElement('a')
        aLink.download = fileName
        aLink.style = 'display:none;'
        const blob = new Blob([content])
        aLink.href = window.URL.createObjectURL(blob)
        document.body.appendChild(aLink)
        if (document.all) {
          aLink.click() //IE
        } else {
          const evt = document.createEvent('MouseEvents')
          evt.initEvent('click', true, true)
          aLink.dispatchEvent(evt) // 其它浏览器
        }
        window.URL.revokeObjectURL(aLink.href)
        document.body.removeChild(aLink)
    }
  }

  const downloadUrlFile = (url, fileName) => {
    const aLink = document.createElement('a')
    if (fileName) {
      aLink.download = fileName
    } else {
      aLink.download = url.substring(url.lastIndexOf('/') + 1)
    }
    aLink.target = '_blank'
    aLink.style = 'display:none;'
    aLink.href = url
    document.body.appendChild(aLink)
    if (document.all) {
      aLink.click() //IE
    } else {
      const evt = document.createEvent('MouseEvents')
      evt.initEvent('click', true, true)
      aLink.dispatchEvent(evt) // 其它浏览器
    }
    document.body.removeChild(aLink)
  }

  const downloadImage = () => {
    const url = imageSources[currentImage]
    const tmp = url.split('/')
    const filename = tmp[tmp.length - 1]
    ajaxDownload(url, downloadBlobFile, filename)
  }

  const downloadAllImage = (blob = null, { list, filename } = {}) => {
    if (blob && filename) downloadBlobFile(blob, filename)
    const [first, ...newList] = list || imageSources
    const tmp = first.split('/')
    const f = tmp[tmp.length - 1]
    ajaxDownload(first, downloadAllImage, { list: newList, filename: f })
  }

  const openInNewTab = () => {
    window.open(imageSources[currentImage], '_blank')
  }

  const fixedMMC = value => {
    const ele = document.querySelector('#mmc')
    if (ele) ele.style.position =  value ? 'fixed' : 'inherit'
  }

  const createGalleryImage = () => {
    currentImage = 0
    const img = document.createElement('div')
    img.className = imgClass
    img.style = galleryImgStyle
    img.style.backgroundImage = `url(${imageSources[0]})`
    return img
  }
  const createLeftArrow = () => {
    const ele = document.createElement('a')
    ele.style = arrowStyle + leftArrowStyle
    ele.innerHTML = '<'
    ele.href = 'javascript:void(0)'
    ele.addEventListener('click', prevImage)
    return ele
  }
  const createRightArrow = () => {
    const ele = document.createElement('a')
    ele.style = arrowStyle + rightArrowStyle
    ele.innerHTML = '>'
    ele.href = 'javascript:void(0)'
    ele.addEventListener('click', nextImage)
    return ele
  }
  const createCloseBtn = () => {
    const ele = document.createElement('a')
    ele.style = closeBtnStyle
    ele.innerHTML = '关闭'
    ele.href = 'javascript:void(0)'
    ele.addEventListener('click', hideGallery)
    return ele
  }
  const createTopLeftMenu = () => {
    const ele = document.createElement('div')
    ele.style = topLeftMenuStyle
    const downloadBtn = document.createElement('a')
    downloadBtn.style= topLeftMenuItemStyle
    downloadBtn.innerHTML = '下载'
    downloadBtn.href = 'javascript:void(0)'
    downloadBtn.addEventListener('click', downloadImage)
    const downloadAllBtn = document.createElement('a')
    downloadAllBtn.style= topLeftMenuItemStyle
    downloadAllBtn.innerHTML = '全部下载'
    downloadAllBtn.href = 'javascript:void(0)'
    downloadAllBtn.addEventListener('click', downloadAllImage)
    const newTabBtn = document.createElement('a')
    newTabBtn.style= topLeftMenuItemStyle
    newTabBtn.innerHTML = '新页面打开'
    newTabBtn.href = 'javascript:void(0)'
    newTabBtn.addEventListener('click', openInNewTab)
    ele.appendChild(downloadBtn)
    ele.appendChild(downloadAllBtn)
    ele.appendChild(newTabBtn)
    return ele
  }

  const showGallery = () => {
    if (!imageSources.length) {
      window.alert('这层楼没有图片  ̄□ ̄||')
      return
    }
    document.addEventListener('keydown', handleKeydown)
    const galleryMask = document.querySelector('.' + containerClass)
    // fixedMMC(true)
    if (galleryMask) {
      currentImage = 0
      setImageSrc(imageSources[currentImage])
      galleryMask.style = galleryContainerStyle
    } else {
      const ele = document.createElement('div')
      ele.className = containerClass
      ele.style = galleryContainerStyle
      const img = createGalleryImage()
      const leftArrow = createLeftArrow()
      const rightArrow = createRightArrow()
      const closeBtn = createCloseBtn()
      const topLeftMenu = createTopLeftMenu()
      ele.appendChild(img)
      ele.appendChild(leftArrow)
      ele.appendChild(rightArrow)
      ele.appendChild(closeBtn)
      ele.appendChild(topLeftMenu)
      document.body.appendChild(ele)
    }
  }
  const hideGallery = () => {
    document.removeEventListener('keydown', handleKeydown)
    // fixedMMC(false)
    const galleryMask = document.querySelector('.' + containerClass)
    if (galleryMask) {
      galleryMask.style.display = 'none'
    }
  }

  const collectImages = container => {
    showCollapseContent(container)
    imageSources = []
    const imgs = container.querySelectorAll('img')
    const temp = []
    imgs.forEach(img => {
      const src = img.src
      const lazySrc = img.dataset ? img.dataset.srclazy : ''
      if (lazySrc) {
        imageSources.push(lazySrc)
        return
      }
      if (src.includes('/attachments/')) {
        const arr = img.src.replace(/https:/g, 'http:').split('http:')
        const src = arr.filter(s => !!s)
        imageSources.push(src[0])
      }
    })
  }

  const callerButton = container => {
    const a = document.createElement('a')
    const handleClick = () => {
      collectImages(container.parentElement)
      showGallery()
    }
    a.addEventListener('click', handleClick)
    a.id = callerId + container.id
    a.className = 'small_colored_text_btn block_txt_c0 stxt'
    a.href = 'javascript:void(0)'
    a.title = '图片浏览'
    a.innerHTML = `
      <span>&nbsp;
        <span style="font-family: comm_glyphs; -webkit-font-smoothing: antialiased; line-height: 1em;">
          图集
        </span>&nbsp;
      </span>
    `
    return a
  }

  const createCallerButton = container => {
    const checkExist = document.querySelector('#' + callerId + container.id)
    if (!checkExist) {
      container.appendChild(callerButton(container))
    }
  }

  const createCallerButtons = () => {
    const elements = document.querySelectorAll('.postInfo')
    if (elements && elements.length > 0) {
      elements.forEach(createCallerButton)
    }
  }

  const observer = new MutationObserver((mutationsList, observer) => {
    createCallerButtons()
  })
  observer.observe(targetNode, config)
  console.log('hello')

})(commonui, __CURRENT_UID)