// ==UserScript==
// @name 花瓣 - 添加下载按钮
// @namespace http://tampermonkey.net/
// @version 0.5
// @description 给花瓣的图加上“下载”按钮,方便下载
// @author 潘志城_Neo
// @match *://huaban.com/*
// @match *://hbimg.huabanimg.com/*
// @grant GM_download
// @grant GM_notification
// @grant GM_registerMenuCommand
// @grant GM_setValue
// @grant GM_getValue
// ==/UserScript==
(function () {
'use strict';
// 所有图片
var allImages = []
// 按钮样式
var btnStyleText = 'border:0; color:#ffffff ;background-color: rgb(26 179 125 / 75%);border-radius:8px;padding:3px 12px;cursor:pointer;pointer-events:all;'
var interval = null
// 配置信息
var setting = GM_getValue('setting')
if(!setting){
setting = {
prefix: "HB", // 前缀
show_notification: true, // 是否显示通知消息
rename: false // 是否重命名
}
GM_setValue('setting', setting)
}
// 主函数
function main() {
document.body.addEventListener("click", function(e){
// 点击img标签的时候才尝试添加下载按钮
if(e,e.target.tagName === 'IMG'){
addDonwloadBtnToPreivew()
}
})
// 网页滚动的时候,检测图片是否有添加下载按钮,没有就添加
document.addEventListener('scroll', throttle(addDownloadBtn, 300))
// 添加设置选项
setSettingMenu()
addDownloadBtn()
interval = setInterval(() => {
if (allImages.length === 0) {
addDownloadBtn()
} else {
clearInterval(interval)
}
}, 1500);
}
main()
/**
* 添加设置选项
*/
function setSettingMenu(){
var menuCommandSetting = GM_registerMenuCommand("设置", function(e) {
addMenu()
}, "S");
}
// 插入菜单到页面
function addMenu(){
var domMenu = document.getElementById('neo_huaban_menu')
if(domMenu !== null){
return
}
domMenu = document.createElement('div')
domMenu.id = "neo_huaban_menu"
domMenu.style = "z-index:2333; width:252px; min-height:120px; display:flex; flex-direction: column; position:fixed; top:50%; left: 50%; transform:translate(-50%,-50%); border-radius:8px; overflow:hidden; background:white;box-shadow: 2px 2px 6px 1px #5668577a;"
var domHtml =`
<div class="title" style="padding:7px; text-align:center; background-color:#1AB37D;color:white;cursor:default;">
设置
</div>
<div class="content" style="display: flex; flex-direction: column; padding:15px; flex:1;">
<div>
<div style="display:flex; align-items:center;">
<div style="display:flex; align-items:center;">
<span style="display: inline-block;">重命名</span>
<input type="checkbox" style="margin-left:5px;cursor:pointer;" class="rename" ${setting.rename?'checked':''}>
</div>
<div style="display:flex; align-items:center;">
<span style="margin-left: 13px; outline: none;" >前缀</span>
<input style="margin-left:5px; outline:none; width:100px; height: 50%; border-radius: 4px; border: 1px solid #adadadd1;" class="prefix" value="${setting.prefix}">
</div>
</div>
<div style="font-size: 0.8em; color: #7a7a7a; margin-left: 20px;">
<div >格式:前缀-年月日-pid</div>
<div>示例:HB-20230216-<a title="https://huaban.com/pins/5073719443=>最后那串数字就是pid" href="https://huaban.com/pins/5073719443" target="_blank">5073719443</a></div>
</div>
</div>
<div style="height:1px; width:100%; background: #cdd3ce47; margin: 7px;"></div>
<div>
<div style="display:flex; align-items:center;">
<span style="display: inline-block;">显示提示信息</span>
<input type="checkbox" style="margin-left:5px;cursor:pointer;" class="show_notification" ${setting.show_notification?'checked':''}>
</div>
<div style="font-size: 0.8em; color: #7a7a7a; margin-left: 20px;">
<div>只在360极速浏览器有用</div>
</div>
</div>
</div>
<div class="close" style="padding:4px; text-align:center; background-color:#ebebeb;color:#333;cursor:pointer;">
关闭
</div>
`
domMenu.innerHTML= domHtml
document.body.appendChild(domMenu)
// 添加事件
document.querySelector('#neo_huaban_menu .content .rename').addEventListener('change', function(e){
if(e.target.checked){
e.target.removeAttribute('checked')
setting.rename = true
}else{
e.target.setAttribute('checked', true)
setting.rename = false
}
GM_setValue('setting',setting)
})
// 关闭按钮
document.querySelector('#neo_huaban_menu .close').addEventListener('click', function(e){
removeMenu()
})
// 修改前缀
var domPrefix = document.querySelector('#neo_huaban_menu .prefix')
domPrefix.addEventListener('change', function(e){
setting.prefix = domPrefix.value
GM_setValue("setting", setting)
})
// 显示通知消息
document.querySelector('#neo_huaban_menu .content .show_notification').addEventListener('change', function(e){
if(e.target.checked){
e.target.removeAttribute('checked')
setting.show_notification = true
}else{
e.target.setAttribute('checked', true)
setting.show_notification = false
}
GM_setValue('setting', setting)
})
}
// 从页面中移除菜单
function removeMenu(){
var domMenu = document.getElementById('neo_huaban_menu')
if(domMenu){
domMenu.remove()
}
}
/**
* 添加下载按钮(如果有按钮,就不添加)
*/
function addDownloadBtn() {
// if(document.URL.includes('discovery') || document.URL.includes('domains') || document.URL.includes('boards') || document.URL.includes('follow') || document.URL.includes('search')){
// addDownloadBtnToDiscovery()
// }
if(document.URL.includes('pins')){
addDonwloadBtnToPreivew()
}else{
if(!document.URL.includes('user')){
addDownloadBtnToDiscovery()
}
}
}
function addDownloadBtnToDiscovery() {
allImages = document.querySelectorAll('.main .infinite-scroll-component .hb-image')
allImages.forEach(dom => {
var pinInfo = dom.parentNode.href.split('/')
// 图片标题和样式
var imgInfo = {
title: dom.getAttribute('alt'),
src: dom.getAttribute('src'),
pin: pinInfo[pinInfo.length-1]
}
// 和包含图片的a标签同级的节点
var tempList = dom.parentNode.parentNode.childNodes
var imgNode = tempList[tempList.length - 1]
var lookNode = tempList[tempList.length - 2]
if (lookNode.querySelectorAll('.neo_add').length === 0) {
var downloadBtn = document.createElement('div')
downloadBtn.className = 'neo_add'
downloadBtn.innerText = '下载'
downloadBtn.addEventListener('click', () => {
downloadImage(imgInfo)
})
downloadBtn.style.cssText = btnStyleText
lookNode.insertBefore(downloadBtn, null)
}
});
}
function addDonwloadBtnToPreivew(){
var newBtn = document.createElement("button")
newBtn.innerText = "下载"
newBtn.style.cssText = btnStyleText+ "border-radius:12px;padding:9px 12px;"
newBtn.className = "neo_add_btn"
newBtn.addEventListener("click",function(){
download()
})
function download(){
var imgDom = document.querySelector('#pin_detail div img')
var pinInfo = document.URL.split('/')
var imgInfo = {}
imgInfo.title = imgDom.alt
imgInfo.src = imgDom.src
imgInfo.pin = pinInfo[pinInfo.length-1]
downloadImage(imgInfo)
}
var count = 0 // 尝试添加下载按钮的次数
var maxCount = 8 // 最大尝试次数
var interval = setInterval(function(){
var btnDom = document.querySelector('#pin_detail div button')
if(btnDom){
clearInterval(interval)
var neoAddDom = document.querySelector('#pin_detail div button.neo_add_btn')
// 如果存在就不继续添加了
if(neoAddDom){
return
}
btnDom.parentNode.appendChild(newBtn)
}
if(count >= maxCount){
clearInterval(interval)
}else{
count++
}
},1000)
}
/**
* 下载图片
* @param {Object} imgInfo src:图片链接; title:图片标题
*/
function downloadImage(imgInfo) {
//替换文件名中不能有的字符
var sign_list = ["\\*", "\\'", '\\"', "<", ">", "\\?", "\\.", "\\|", "\\/"]
for (var i = 0; i < sign_list.length; i++) {
var reg = "/" + sign_list[i] + "/g";
var title = imgInfo.title
if (title) {
imgInfo.title = imgInfo.title.replace(eval(reg), "_");
} else {
imgInfo.title = '无标题'
}
}
imgInfo.src = imgInfo.src.replace(/_fw240.*/, '')
imgInfo.src = imgInfo.src.replace(/_fw658.*/, '')
var imgTitle = imgInfo.title
if(setting.rename){
imgTitle = (setting.prefix?setting.prefix+'-':'') + formatDate(new Date()) +'-'+imgInfo.pin
}
show_notification({ text: imgTitle, title: "图片已添加下载", timeout: 2000 })
//启用油猴的增强下载函数,可跨域
GM_download({
url: imgInfo.src,
name: imgTitle,
onprogress:function(){
if(setting.show_notification){
var isNotice = false
return function(){
if(!isNotice){
show_notification({ text: imgTitle, title: "图片已添加下载", timeout: 2000 })
isNotice=true
}
}
}
},
onload: function () {
//下载完成之后,右下角弹窗通知。
show_notification({ text: imgTitle, title: "图片已完成下载", timeout: 5000 })
},
onerror :function(){
//下载出错,右下角弹窗通知。
show_notification({ text: imgTitle + '\n' + imgInfo.src, title: "下载出错", timeout: 5000 })
}
});
}
function show_notification(item){
if(setting.show_notification){
GM_notification(item)
}
}
function throttle(cb, wait = 300) {
var last = 0;
return function () {
var now = new Date().getTime();;
if (now - last > wait) {
cb.call(this);
last = new Date().getTime();;
}
}
}
//格式化时间
function formatDate(dat){
//获取年月日,时间
var year = dat.getFullYear();
var mon = (dat.getMonth()+1) < 10 ? "0"+(dat.getMonth()+1) : dat.getMonth()+1;
var data = dat.getDate() < 10 ? "0"+(dat.getDate()) : dat.getDate();
var newDate = year + mon + data
return newDate;
}
})();