D&E-JP Image Download

Download the original images from the fan club photo page. Notice: Membership required

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.

(У мене вже є менеджер скриптів, дайте мені встановити його!)

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              D&E-JP Image Download
// @name:zh-CN        D&E 日咖图片下载
// @namespace         http://liuzesen.com/
// @match             https://superjunior-dne.jp/group/*
// @version           1.1
// @description       Download the original images from the fan club photo page. Notice: Membership required
// @description:zh-CN 下载日咖页面上的原图。注:部分页面需要会员身份
// @icon              https://superjunior-dne.jp/assets/superjuniordne/fabicon-df43bc3beaeb85e3425f9d200bfb1577.jpg
// @homepage          https://github.com/hnliuzesen/DnE-JP-IMG-DL
// @supportURL        https://github.com/hnliuzesen/DnE-JP-IMG-DL/issues
// @license           MIT
// @grant             GM_xmlhttpRequest
// @grant             GM_download
// @grant             GM_registerMenuCommand
// @grant             GM_showFolderPicker
// ==/UserScript==

(function () {
  'use strict'

  // 用于存储用户选择的路径
  let selectedDirectoryHandle = null

  // 添加菜单按钮
  GM_registerMenuCommand('下载所有图片', downloadAllImages)

  async function downloadAllImages () {
    // 创建进度显示窗
    const progressArea = document.createElement('div')
    progressArea.style.position = 'fixed'
    progressArea.style.top = '50%'
    progressArea.style.left = '50%'
    progressArea.style.transform = 'translate(-50%, -50%)'
    progressArea.style.padding = '10px'
    progressArea.style.backgroundColor = 'rgb(255, 255, 255)'
    progressArea.style.color = 'rgb(27, 70, 185)'
    progressArea.style.borderRadius = '5px'
    progressArea.style.zIndex = '10000'
    progressArea.style.fontSize = '14px'
    document.body.appendChild(progressArea)

    // 查找带有 data-original 属性的 img 标签
    const images = document.querySelectorAll('img[data-original]')
    if (images.length === 0) {
      document.body.removeChild(progressArea)
      alert('没有找到可以下载的图片')
      return
    }

    if (!selectedDirectoryHandle) {
      try {
        selectedDirectoryHandle = await window.showDirectoryPicker()
      } catch (error) {
        console.error('路径选择失败', error)
        document.body.removeChild(progressArea)
        alert('未选择路径,下载已取消。')
        return
      }
    }

    // 获取文件夹名称
    const folderName = getFolderName()
    if (!folderName) {
      document.body.removeChild(progressArea)
      alert('未找到合适的文件夹名称,下载已取消。')
      return
    }

    // 移除文件夹名称中的非法字符
    const sanitizedFolderName = folderName.replace(/[\\/:*?"<>|]/g, '')

    // 创建子目录
    const subDirHandle = await selectedDirectoryHandle.getDirectoryHandle(sanitizedFolderName, { create: true })

    let downloadCount = 0
    let completedDownloads = 0
    progressArea.textContent = `下载进度: ${downloadCount} / ${images.length}`

    images.forEach(img => {
      const imageUrl = img.getAttribute('data-original')
      if (!imageUrl) return

      const fileName = getFileName(imageUrl)

      GM_xmlhttpRequest({
        method: 'GET',
        url: imageUrl,
        responseType: 'blob',
        onload: async (response) => {
          if (response.status === 200) {
            const blob = response.response
            const fileHandle = await subDirHandle.getFileHandle(fileName, { create: true })
            let writable
            try {
              writable = await fileHandle.createWritable()
              await writable.write(blob)
              downloadCount++
              progressArea.textContent = `下载进度: ${downloadCount} / ${images.length}`
            } catch (error) {
              console.error(`保存文件时出错: ${fileName}`, error)
            } finally {
              if (writable) await writable.close()
            }
            console.log(`图片下载成功: ${fileName}`)
          } else {
            console.error(`下载图片失败: ${imageUrl}, 状态码: ${response.status}`)
          }
          completedDownloads++
          if (completedDownloads === images.length) {
            alert('下载完成!')
            document.body.removeChild(progressArea)
          }
        },
        onerror: (error) => {
          console.error(`请求图片失败: ${imageUrl}`, error)
          completedDownloads++
          if (completedDownloads === images.length) {
            alert('下载完成!')
            document.body.removeChild(progressArea)
          }
        }
      })
    })
  }

  function getFolderName () {
    try {
      const titleContainer = document.querySelector('.comp-gallery-show01-title')
      if (!titleContainer) return null

      const firstChildOfTitle = titleContainer.firstElementChild
      let result = firstChildOfTitle ? firstChildOfTitle.textContent.trim() : null

      const dateContainer = document.querySelector('.comp-gallery-show01-meta')
      if (!dateContainer) return result

      const firstChildOfDate = dateContainer.firstElementChild
      // 将 firstChildOfDate 的 datetime 属性拼接在 result 前面
      result = firstChildOfDate ? `${firstChildOfDate.getAttribute('datetime')} ${result}` : result

      return result
    } catch (error) {
      console.error('获取文件夹名称时出错:', error)
      return null
    }
  }

  function getFileName (url) {
    try {
      const parsedUrl = new URL(url)
      const pathname = parsedUrl.pathname
      const fileNameMatch = pathname.match(/([^\/]+\.[a-zA-Z]{3,4})(?:[?#]|$)/)
      if (!fileNameMatch) {
        console.warn(`无法解析文件名: ${url}`)
        return `image_${Date.now()}.jpg`
      }
      return fileNameMatch[1]
    } catch (e) {
      console.error('解析URL时出错:', e)
      return `image_${Date.now()}.jpg`
    }
  }
})()