Greasy Fork

来自缓存

Greasy Fork is available in English.

阿里云盘-电视剧批量重命名工具-剧集刮削

用于阿里云盘批量修改文件名,主要为 Filmly 适配阿里云盘上的不规则电视剧命名刮削准备,代码抄袭自 wdwy 发布的另外一个脚本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         阿里云盘-电视剧批量重命名工具-剧集刮削
// @namespace    http://tampermonkey.net/
// @version      2.05
// @description  用于阿里云盘批量修改文件名,主要为 Filmly 适配阿里云盘上的不规则电视剧命名刮削准备,代码抄袭自 wdwy 发布的另外一个脚本
// @author       wdwy
// @match        https://www.aliyundrive.com/drive/folder/*
// @match        https://www.aliyundrive.com/drive/*
// @match        https://www.aliyundrive.com/drive/legacy/folder/*
// @match        https://www.aliyundrive.com/drive/legacy/
// @match        https://www.aliyundrive.com/drive/file/backup/*
// @match        https://www.aliyundrive.com/drive/file/backup/
// @match        https://www.aliyundrive.com/drive/file/*
// @match        https://www.alipan.com/drive/folder/*
// @match        https://www.alipan.com/drive/*
// @match        https://www.alipan.com/drive/legacy/folder/*
// @match        https://www.alipan.com/drive/legacy/
// @match        https://www.alipan.com/drive/file/backup/*
// @match        https://www.alipan.com/drive/file/backup/
// @match        https://www.alipan.com/drive/file/*
// @icon         https://gw.alicdn.com/imgextra/i3/O1CN01aj9rdD1GS0E8io11t_!!6000000000620-73-tps-16-16.ico
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// @run-at       document-body
// @license      GPLv3
// ==/UserScript==

(function() {
    'use strict';

    var $ = $ || window.$;

    var obj = {
        files: [],
        url: location.href,
    };

    var op1 = `
        <div class="wrap--MfXl2" >
            <div class="container--30UDJ" style="line-height: 40px;">
                <div class="content--nfnm6" style="padding: 32px;">
                    <div style="display:flex; width:100%; text-align:center; padding:10px 5px;">
                        <text style="width:20%;">文件夹:</text>
                        <text class="batch-path"/>
                    </div>

                    <table style="width:100%;border:1px solid black;">
                      <tr style="border:1px solid black;">
                        <th style="border:1px solid black;">原文件</th>
                        <th style="border:1px solid black;">42.mp4</th>
                        <th style="border:1px solid black;">回家的诱惑-第60集-720P(avc1F).mp4</th>
                      </tr>
                      <tr style="border:1px solid black;">
                        <td style="border:1px solid black;">模板</td>
                        <td style="border:1px solid black;"><text style="color:yellow;background-color:gray">$$.mp4</text></td>
                        <td style="border:1px solid black;">
                        <text>回家的诱惑-第</text>
                        <text style="color:yellow;background-color:gray">$$</text>
                        <text>集-720P(avc1F).mp4</text>
                        </td>
                      </tr>
                      <tr style="border:1px solid black;">
                        <td style="border:1px solid black;">结果</td>
                        <td style="border:1px solid black;">回家的诱惑.S01E01.mp4</td>
                        <td style="border:1px solid black;">回家的诱惑.S01E01.mp4</td>
                      </tr>
                    </table>


                    <div style="display:flex; width:100%; text-align:center; padding:10px 5px;">
                        <text style="width:20%; ">模板:</text>
                        <input style="width:400px;" class="batch-before" type="text" value="$$" />
                    </div>

                    <div style="display:flex; width:100%; text-align:center; padding:10px 5px;">
                        <text style="width:20%;">年份:</text>
                        <input style="width:400px;" class="input-years" type="text" value="" placeholder="选填" />
                    </div>

                    <div style="display:flex; width:100%; text-align:center; padding:10px 5px;">
                        <text style="width:20%;">剧名:</text>
                        <input style="width:400px;" class="batch-after" type="text" value="" >
                    </div>
                    <div style="display:flex; width:100%; text-align:center; padding:10px 5px;">
                        <text style="width:20%; ">季数:</text>
                        <select id="select-season" style="width: 120px;text-align: center; " name="choice">
                           <option value="1" selected>1</option>
                           <option value="2">2</option>
                           <option value="3">3</option>
                           <option value="4">4</option>
                           <option value="5">5</option>
                           <option value="6">6</option>
                           <option value="7">7</option>
                           <option value="8">8</option>
                           <option value="9">9</option>
                        </select>
                    </div>

                    <div class="group--20rOb" style="display:flex; width:100%; text-align:center; padding:10px 5px;">
                         <text style="width:20%;">预览:</text>
                         <text id="preview-text"></text>
                    </div>

                </div>
                <div style="display:flex; justify-content:center">
                    <button class="button--WC7or primary--NVxfK small--e7LRt batch-modify" style="height: 48px;min-width: 96px;">修改</button>
                </div>
            </div> <!-- container -->
        </div> <!-- wrap -->
    `;


    obj.reset = function () {
        obj.files = [];
        obj.url = location.href;
    };

    obj.initBatchButton = function () {
        if ($(".button--batch").length) {
            return;
        }
        if ($("#root header").length) {
            var html = '';
            html += '<div style="margin:0px 8px;"></div><button class="button--WC7or primary--NVxfK small--e7LRt button--batch">批量修改</button>';
            $("#root header:eq(0)").append(html);
            $(".button--batch").on("click", obj.showModifyPage);
        }else {
            setTimeout(obj.initBatchButton, 1000);
        }
    };

    obj.init = function () {
        var send = XMLHttpRequest.prototype.send;
        XMLHttpRequest.prototype.send = function(data) {
            this.addEventListener("load", function(event) {
                if(this.readyState == 4 && this.status == 200) {
                    let { response, responseURL } = this;
                    try { response = JSON.parse(response) } catch (error) { };
                    if (responseURL.endsWith("/file/get_path")) {
                        obj.initBatchButton();

                        // 设置路径名
                        if (response?.items) {
                            obj.path = response.items.reverse().reduce((path, item) => path + item.name + "/", "/");
                        }

                        if(response?.items){
                            obj.TVname = response.items[response.items.length-1].name
                        }
                    }else if (responseURL.indexOf("/file/list") > 0) {
                        // 将 video 添加到 obj.files
                        if (response && response.items) {
                            if(obj.url && obj.url == location.href) {
                                obj.files = obj.files.concat(response.items.filter(item=> item.category ==='video' ));
                            }else {
                                obj.reset();
                                obj.files = response.items.filter(item=> item.category ==='video')
                            }
                        }
                        // 确定 file_extension
                        obj.fileExtensions = [...new Set(obj.files.filter(item=> item.category ==='video').map(file => file.file_extension))];
                    }
                }
            }, false);
            send.apply(this, arguments);
        };
    };

    obj.showModifyPage = function() {
        //滚动到底,自动获取所有文件
        const draggableElement = document.querySelector('.scroller---esn7');
        draggableElement.scrollTop = draggableElement.scrollHeight;

        var html = `
        <div class="ant-modal-root ant-modal-batch-modify">
            <div class="ant-modal-mask" style="z-index: 999999;"></div>
            <div tabindex="-1" class="ant-modal-wrap" role="dialog" style="z-index: 999999;">
                <div role="document" class="ant-modal modal-wrapper--5SA7y" style="width: 600px;  ">
                    <div tabindex="0" aria-hidden="true" style="width: 0px; height: 0px; overflow: hidden; outline: none;"></div>
                    <div class="ant-modal-content" style="height: auto;">
                        <div class="ant-modal-body" style="padding: 0px;">
                            <div class="icon-wrapper--TbIdu">
                                <span data-role="icon" data-render-as="svg" data-icon-type="PDSClose" class="close-icon--KF5OX icon--D3kMk ">
                                    <svg viewBox="0 0 1024 1024">
                                        <use xlink:href="#PDSClose"></use>
                                    </svg>
                                </span>
                            </div>

                            <div  style="width:600px; height: auto;">
                                <div class="content--mUZVS batch-content-op" style="color:rgba(var(--context), 0.9); font-weight:bolder">
                                `+op1+`
                                </div> <!-- content -->
                            </div> <!-- container -->
                        </div>
                        <br/>
                    </div>
                    <div tabindex="0" aria-hidden="true" style="width: 0px; height: 0px; overflow: hidden; outline: none;"></div>
                </div>
            </div>
        </div>
            `;

        $("body").append(html);
        $(".ant-modal-batch-modify .icon-wrapper--TbIdu").on("click", function () {
            $(".ant-modal-batch-modify").remove();
        });
        $(".ant-modal-batch-modify .ant-modal-wrap").on("click", function (event) {
            if ($(event.target).closest(".ant-modal-content").length == 0) {
                $(".ant-modal-batch-modify").remove();
            }
        });


        $(".input-years").on("input",function(){
            obj.updatePreview()
        })

        $("#select-season").on("change",function(){
            obj.updatePreview()
        })
        $(".batch-after").on("input",function(){
            obj.updatePreview()
        })
        $(".batch-before").on("input",function(){
            const bef = $(this).val()

            const hasDoubleDoller = bef.indexOf("$$") === -1
            const modifyButton = $(".batch-modify")
            if(hasDoubleDoller){
                modifyButton.attr('disabled',true)
                modifyButton.css('background-color', 'gray');
                modifyButton.text("模板错误(需要包含 $$ 字符)");
            }else{
                modifyButton.attr('disabled',false)
                modifyButton.css('background-color', 'darkorange');
                modifyButton.text("修改");
            }
        })
        $(".batch-path").text(obj.path);
        $(".batch-modify").on("click", obj.batchModify);
        $(".batch-after").val(obj.TVname)

        obj.updateTemplate()
        obj.updatePreview()
    };

    obj.updateTemplate = function(){
        if(obj.files && obj.files.length > 0){
            const tvName = obj.files[0].name
            if(!$.isNumeric(tvName)){ // 不是简单的数字例如 40.mp4
                $(".batch-before").val(tvName)
                $(".batch-modify").attr('disabled',true);
                $(".batch-modify").css('background-color', 'gray');
                $(".batch-modify").text("模板错误(需要包含 $$ 字符)");
            }
        }else{
            console.log('empty file')
        }
    }

    obj.updatePreview = function() {
        const name = $(".batch-after").val()
        const years = $(".input-years").val()
        const season = $("#select-season").val()
        let text = ''
        for (const extension of obj.fileExtensions) {
            text += `${name}.S0${season}E01`;
            if(years){
                text += `.${years}`;
            }
            text += `.${extension}
            `
        }
        $("#preview-text").text(text);
    }


    obj.loading = function() {
        var html = `
            <div class="ant-modal-root ant-modal-loading">
                <div class="ant-modal-mask"></div>
            <div tabindex="-1" class="ant-modal-wrap" role="dialog">
                <div role="document" class="ant-modal modal-wrapper--5SA7y" style="width: 666px;">
                    <div class="ant-modal-content">
                        <div class="ant-modal-header">
                            <div class="ant-modal-title" id="rcDialogTitle1"></div>
            </div>
            <div class="ant-modal-body">
                <div class="content--nfnm6" style="color:rgba(var(--context), 0.9); font-weight:bolder">
                    批量处理中。。。<br/>
                    如果文件过多,请耐心等待弹框提示处理结果!
            <!-- 已完成处理&nbsp&nbsp&nbsp<div class="batch-modify-loading-num">0</div>&nbsp&nbsp&nbsp个文件 -->
            </div>
            </div>
            <br/>
                    <div class="ant-modal-footer">
                        <div class="footer--1GqVx" style="display:flex; justify-content:center;">
                            </div>
            </div>
            </div>
            </div>
            </div>
            </div>
            `;
        $("body").append(html);
    }

    obj.refreshLoading = function(num) {
        var loading = $(".batch-modify-loading-num");
        if(loading) loading.html(num);
        loading.get(0).innerHTML = loading.get(0).innerHTML;
    }


    obj.enableButton = function() {
        $(".batch-modify").attr('disabled',false);
    };

    obj.batchModify = function() {
        $(".batch-modify").attr('disabled',true);

        const years = $(".input-years").val()
        const season = $("#select-season").val()


        var nameBefore = $(".batch-before").val().split("$$");
        var nameAfter = $(".batch-after").val();
        var suff = $(".batch-after-suff").val();

        if(!obj.files) {
            alert("未从阿里云盘 /file/list 接口获取到 files !");
            obj.enableButton();
            return;
        }

        if(nameBefore.length !== 2){
            alert("$$ 模版有误 ");
            obj.enableButton();
            return;
        }

        const left = nameBefore[0]
        const right = nameBefore[1]


        //解析token
        const token = JSON.parse(getToken());
        if(!token) {
            alert("请先登录!");
            obj.enableButton();
            return;
        }
        var tokenStr = token.token_type + " " + token.access_token;

        $(".ant-modal-batch-modify").remove();

        obj.loading();
        setTimeout(function() {
            let count = 0;
            for(let file of obj.files) {
                if(file.name.length < left.length + right.length ) continue;
                if(count > 200) break;

                const startIndex = file.name.indexOf(left) + left.length;
                const endIndex = file.name.lastIndexOf(right);


                // 提取出集数
                let episode = file.name.substring(startIndex, endIndex);
                if(!$.isNumeric(episode)) {
                    console.log('error: 集数不是 number , [',episode, ']   name:', file.name)
                    continue
                };

                if(episode.length < 2){
                    episode =`0${episode}`
                }

                let newName = `${nameAfter}.S0${season}E${episode}`;
                if(years){
                    newName += `.${years}`;
                }
                newName += `.${file.file_extension}`

                if(newName === file.name){
                    console.log('相同的文件名, 无需修改, 跳过')
                    continue
                }



                obj.ajaxModify(file, newName, tokenStr);
                count++;
            }

            //$(".ant-modal-batch-modify").remove();
            $(".ant-modal-loading").remove();
            alert("完成修改【"+count+"】个文件");

            setTimeout(function() {window.location.reload()}, 100);
        }, 0);
    }

    function getToken() {
        let token = localStorage.getItem("token");
        if(isBlank(token)) {
            token = sessionStorage.getItem("token");
            if(isBlank(token)) {
                token = getCookie("token");
            }
        }
        return token;
    }

    function getCookie(cname) {
        var name = cname + "=";
        var ca = document.cookie.split(';');
        for(var i=0; i<ca.length; i++) {
            var c = ca[i].trim();
            if (c.indexOf(name)==0) return c.substring(name.length,c.length);
        }
        return "";
    }

    obj.ajaxModify = function(file, newName, token) {
        $.ajax({
            type: "post",
            url: "https://api.aliyundrive.com/v3/file/update",
            data: JSON.stringify({
                "drive_id": file.drive_id,
                "file_id": file.file_id,
                "name": newName,
                "check_name_mode": "refuse"
            }),
            headers: {
                "authority": "api.aliyundrive.com",
                "authorization": token,
                "content-type": "application/json;charset=UTF-8"
            },
            async: false, //批量修改文件多时,无法预估请求时间,关闭异步保证所有文件修改成功
            success: function (response) {
                file.name = newName;
            },
            error: function (error) {
                console.error("modify error", error);
            }
        });
    }

    function isBlank(str) {
        if(str == null || str === '') return true;
        else if(str.trim() === '') return true;
        else return false;
    }


    obj.init();
    setTimeout(obj.initBatchButton, 1000);

})();