下载日咖页面上的原图。注:部分页面需要会员身份
// ==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`
}
}
})()