// ==UserScript==
// @name Kemono 下载工具
// @namespace http://tampermonkey.net/
// @version 1.1
// @description 仅支持Files内图片下载
// @author ljw2487
// @match *://*.kemono.su/*
// @match *://*.kemono.party/*
// @match *://kemono.su/*
// @match *://kemono.party/*
// @license GNU General Public License v3.0 or later
// @grant GM_addStyle
// @grant GM_addElement
// @grant GM_xmlhttpRequest
// @grant GM_download
// ==/UserScript==
GM_addStyle(".ad-container {display: none;}");
GM_addStyle(".root--ujvuu {display: none;}");
// 下载文件命名规则:
// 包含:作者 | 主标题 | 副标题(来源) | 页码 | 共多少页 (副标题一般是(Pixiv Fanbox) (Patreon))
// 标题示例:Yelan 1 Part 3 (Loop) (Patreon) 其中(Patreon)为副标题
// 1 -> 页码.格式 示例:1.jpg
// 2 -> 页码_共多少页.格式 示例:1_20.jpg
// 3 -> 主标题 页码_共多少页.格式 示例:Yelan 1 Part 3 (Loop) 1_20.jpg
// 4 -> 主标题 副标题(来源) 页码_共多少页.格式 示例:Yelan 1 Part 3 (Loop) (Patreon) 1_20.jpg
// 5 -> [作者] 主标题 副标题(来源) [页码_共多少页].格式 示例:[Abcde] Yelan 1 Part 3 (Loop) (Patreon) [1_20].jpg
const nameRule = 5
function script() {
const host = location.host
const path = location.pathname
const regular = {
"/": homePage, // 首页
"/artists": artistsPage, // 画师列表
"/(\\w+)/user/(\\d+)": userPage, // 画师详情
"/(\\w+)/user/(\\d+)/post/(\\d+)": postPage, // 作品页
};
Object.keys(regular).some((pattern) => {
if (new RegExp(`^${pattern}$`).test(path)) {
regular[pattern]();
return true;
}
})
}
script()
function homePage () {
// console.log('homepage');
}
function artistsPage () {
// console.log('artistsPage');
}
function userPage () {
// console.log('userPage');
}
function postPage () {
GM_addStyle(".download-button {color: white; font-size: 16px; background-color: #3e3e44; padding: 3px 15px; border-radius: 5px; appearance: none; border: none; outline: none; transition: color 0.2s ease-in-out;}");
GM_addStyle(".download-button:hover {color: #f8b696; transition: color 0.2s ease-in-out;}");
GM_addStyle(".modify-h2 {display:flex; align-items: center; justify-content: space-between;}");
// GET ARTIST {String} "name"
let artist = ""
artist = document.getElementsByClassName('post__user-name')[0].innerText
// GET TITLES {Array} [mainTitle, subTitle]
let titles = []
let titleNode = document.getElementsByClassName('post__title')[0]
titleNode = titleNode.getElementsByTagName('span')
Array.from(titleNode).forEach(item => { titles.push(item.innerText) })
// GET FILE URL LIST {Array} [Url, Url, ...]
let fileList = []
let filesNode = document.getElementsByClassName('post__thumbnail')
Array.from(filesNode).forEach(file => {
let href = file.querySelector('a').getAttribute('href')
fileList.push(href)
})
// Modify-DOM
let h2Node = document.getElementsByTagName("h2")
Array.from(h2Node).forEach(element => {
// Files
if(element.innerHTML == "Files") {
let fileTitle = createElement("h2", "Files")
let fileBtn = createElement("button", "下载全部", {
id: "fileButton",
class: "download-button",
type: "button"
})
element.innerHTML = ""
element.classList.add("modify-h2")
element.appendChild(fileTitle)
element.appendChild(fileBtn)
fileBtn.addEventListener("click", function() {
fileBtn.disabled = true
fileBtn.innerHTML = "下载中"
downloadingFiles(fileBtn, artist, titles, fileList)
})
}
})
}
function createElement(tag, text, attributes = undefined) {
let element = document.createElement(tag);
element.innerHTML = text
if (attributes) {
for (let k in attributes) {
element.setAttribute(k, attributes[k])
}
}
return element
}
function createFileName(artist, titles, index, pages) {
// console.log(artist, titles, index, pages);
if (nameRule === 1) {
return `${index}` // 1.jpg
} else if (nameRule === 2) {
return `${index}_${pages}` // 1_20.jpg
} else if (nameRule === 3) {
return `${titles[0]} ${index}_${pages}` // title 1_20.jpg
} else if (nameRule === 4) {
return `${titles[0]} ${titles[1]} ${index}_${pages}` // title (fanbox) 1_20.jpg
} else if (nameRule === 5) {
return `[${artist}] ${titles[0]} ${titles[1]} [${index}_${pages}]` // [artist] title (fanbox) [1_20].jpg
}
}
function downloadingFiles(btn, artist, titles, fileList) {
let totalQuest = fileList.length, success = 0, faild = 0
fileList.forEach((url, index) => {
let fileName = createFileName(artist, titles, index + 1, fileList.length)
GM_download({
url: url,
name: fileName,
onload: function() {
console.log(`下载 ${index + 1}/${fileList.length} 完成`)
totalQuest -= 1
success += 1
downloadingFilesFinished(btn, totalQuest, fileList.length, success, faild)
},
onerror: function(err) {
console.error(`下载 ${index + 1}/${fileList.length} 失败 准备二次尝试`, err)
GM_download({
url: url,
name: fileName,
onload: function() {
console.log(`下载 ${index + 1}/${fileList.length} 完成 (二次尝试)`)
totalQuest -= 1
success += 1
downloadingFilesFinished(btn, totalQuest, fileList.length, success, faild)
},
onerror: function(err) {
console.log(`下载 ${index + 1}/${fileList.length} 失败 (二次尝试) 请尝试手动下载`, err, {
name: fileName,
url: url
})
totalQuest -= 1
faild += 1
downloadingFilesFinished(btn, totalQuest, fileList.length, success, faild)
}
})
}
})
})
}
function downloadingFilesFinished(btn, totalQuest, total, success, faild) {
if (totalQuest === 0) {
btn.disabled = false
btn.innerHTML = "已下载"
alert(`下载完成, 共${total}张, 成功${success}张, 失败${faild}张`)
}
}