花瓣 - 添加下载按钮

给花瓣的图加上“下载”按钮,方便下载

目前为 2023-08-22 提交的版本。查看 最新版本

// ==UserScript==
// @name         花瓣 - 添加下载按钮
// @namespace    http://tampermonkey.net/
// @version      0.5.1
// @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, // 是否重命名
            show_source_img: false, // 是否显示大图
            show_img_title: 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 style="height:1px; width:100%; background: #cdd3ce47; margin: 7px;"></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_source_img" ${setting.show_source_img?'checked':''}>
          </div>
          <div style="font-size: 0.8em; color: #7a7a7a; margin-left: 20px;">
              <div>刷新页面后生效。</div>
          </div>
          <div style="height:1px; width:100%; background: #cdd3ce47; margin: 7px;"></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_img_title" ${setting.show_img_title?'checked':''}>
          </div>
          <div style="font-size: 0.8em; color: #7a7a7a; margin-left: 20px;">
              <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)
        })

        // 显示大图
        document.querySelector('#neo_huaban_menu .content .show_source_img').addEventListener('change', function(e){
            if(e.target.checked){
                e.target.removeAttribute('checked')
                setting.show_source_img = true
            }else{
                e.target.setAttribute('checked', true)
                setting.show_source_img = false
            }
            GM_setValue('setting', setting)
        })

        // 显示图片标题
        document.querySelector('#neo_huaban_menu .content .show_img_title').addEventListener('change', function(e){
            if(e.target.checked){
                e.target.removeAttribute('checked')
                setting.show_img_title = true
            }else{
                e.target.setAttribute('checked', true)
                setting.show_img_title = 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
            // 图片dom
            var imgNode = tempList[tempList.length - 1]
            // 与图片父级a标签同级,并处于上方的元素
            var lookNode = tempList[tempList.length - 2]

            lookNode.setAttribute('hidden',true)
            lookNode.className = ""
            lookNode.style.cssText="position: absolute;bottom: 8px; right: 8px; display: flex; flex-direction: row;align-items: center;"
            // 添加鼠标悬停时的样式
            lookNode.parentNode.addEventListener('mouseover', function() {
                lookNode.removeAttribute('hidden')
            });

            // 移除鼠标悬停时的样式
            lookNode.parentNode.addEventListener('mouseout', function() {
                lookNode.setAttribute('hidden',true)
            });
            if (lookNode.querySelectorAll('.neo_add').length === 0) {

                var btnContainer = document.createElement('div')
                btnContainer.style = "display:flex;"

                if(setting.show_source_img){
                    // 添加打开大图按钮
                    var sourceBtn = document.createElement('div')
                    sourceBtn.className = 'neo_add_source'
                    sourceBtn.innerText = '大图'
                    sourceBtn.addEventListener('click', () => {
                        window.open(imgInfo.src.replace('_fw240webp',''))
                    })

                    sourceBtn.style.cssText = btnStyleText + 'margin-left:3px;'
                    btnContainer.appendChild(sourceBtn)
                }
                // 添加下载图片按钮
                var downloadBtn = document.createElement('div')
                downloadBtn.className = 'neo_add'
                downloadBtn.innerText = '下载'
                downloadBtn.addEventListener('click', () => {
                    downloadImage(imgInfo)
                })

                downloadBtn.style.cssText = btnStyleText + 'margin-left:3px;'
                btnContainer.appendChild(downloadBtn)
                lookNode.insertBefore(btnContainer,null)
                // 添加图片标题
                if(setting.show_img_title){
                    var domTitle = document.createElement('div')
                    domTitle.innerText = imgInfo.title
                    domTitle.title = imgInfo.title
                    domTitle.style.cssText = "padding-left:5px;font-size: 0.5em;text-overflow: ellipsis;white-space: nowrap;overflow: hidden; color: rgba(30,32,35,.65);height:3em;"
                    dom.parentNode.parentNode.parentNode.appendChild(domTitle)
                }
            }
        });
    }
    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;
    }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址