Greasy Fork is available in English.

PotPlayer云盘-专供版

此脚本为《PotPlayer播放云盘视频》姊妹篇,需配合MediaPlayParse - PanVideo.as脚本使用。在potplayer中选择画质、字幕,迅雷云盘增加原画,阿里云盘增加时长。

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name         PotPlayer云盘-专供版
// @namespace    https://github.com/Bleu404/PotplayerPanVideoSV
// @version      1.1.0
// @description  此脚本为《PotPlayer播放云盘视频》姊妹篇,需配合MediaPlayParse - PanVideo.as脚本使用。在potplayer中选择画质、字幕,迅雷云盘增加原画,阿里云盘增加时长。
// @author       bleu
// @compatible   edge Tampermonkey
// @compatible   chrome Tampermonkey
// @compatible   firefox Tampermonkey
// @license      MIT
// @match        https://pan.xunlei.com/*
// @match        https://www.aliyundrive.com/*
// @icon         https://fastly.jsdelivr.net/gh/Bleu404/PRPO@latest/png/ppvsv.png
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_xmlhttpRequest
// @grant        GM_registerMenuCommand
// @grant        unsafeWindow
// @connect      *
// @connect      xunlei.com
// @connect      aliyundrive.com
// @connect      jianguoyun.com
// @connect      teracloud.jp
// @require      https://fastly.jsdelivr.net/npm/sweetalert2@11.1.0/dist/sweetalert2.all.min.js
// @require      https://fastly.jsdelivr.net/npm/bleutools@1.0.2/bleutools.min.js
// ==/UserScript==

(function () {
    'use strict';
    const ORGXHRSRH = XMLHttpRequest.prototype.setRequestHeader;
    let bleuc,contextMenu, itemsInfo, arryIndex, Option, observer,cloud;
    const flieTypeStr = ".wmv,.rmvb,.avi,.mp4,.mkv,.flv,.swf.mpeg4,.mpeg2,.3gp,.mpga,.qt,.rm,.wmz,.wmd,.wvx,.wmx,.wm,.mpg,.mpeg,mov,.asf,.m4v,";
    const tools = {
        getCloudName() {
            switch (document.domain) {
                case 'xunlei.com':
                    cloud = xunlei;
                    break;
                case 'pan.xunlei.com':
                    cloud = xunlei;
                    break;
                case 'www.aliyundrive.com':
                    cloud = aliyun;
                    this.hookXHRHeader();
                    break;
            }
        },
        checkFileType(name) {
            let type = name.toLowerCase().substring(name.lastIndexOf('.')) || "bleu"
            return flieTypeStr.indexOf(`${type},`) >= 0 ? true : false
        },
        async putFileInWebdav(name, info) {
            let header = {
                "authorization": `Basic ${btoa(`${bleuc.cun}:${bleuc.cpw}`)}`
            }
            let url = `https://${bleuc.cip}/PanPlaylist`;
            let method = bleuc.cip.indexOf('teracloud')>0?'GET':'PROPFIND';
            await bleu.XHR(method, url, undefined, header, undefined).then( () => {}
            ,async()=>{await bleu.XHR('MKCOL', url, undefined, header, undefined)})
            url = `https://${bleuc.cip}/PanPlaylist/${name}`;
            await bleu.XHR('PUT',url , info, header, 'xml').then(() => {
                bleu.swalInfo(`✅${name}`, 3000, 'center');
            }, () => bleu.swalInfo(`❌${name}`, 3000, 'center'))
        },
        checkConfig() {
            bleuc = JSON.parse(GM_getValue('bleuc') || null) || {cip: '',cun: '',cpw: ''};
            if (!(bleuc.cip != '' && bleuc.cun != '' && bleuc.cpw != '')) {
                bleu.swalInfo(`❗请先设置WEBDAV`, '', 'center')
                return false
            }
            return true
        },
        saveConfig() {
            let temp = document.querySelector('#cip').value.trim()
            temp = temp.charAt(temp.length - 1) === '/' ? temp.substring(0, temp.length - 1) : temp
            temp = temp.indexOf('https://') < 0 ? temp : temp.replace('https://', '')
            GM_setValue("bleuc", JSON.stringify({
                'cip': temp,
                'cun': document.querySelector('#cun').value.trim(),
                'cpw': document.querySelector('#cpw').value.trim(),
            }));
        },
        configHtml() {
            bleuc = JSON.parse(GM_getValue('bleuc') || null) || {cip: '',cun: '',cpw: ''};
            let html = `
                <div class="bleuc_config_item"><p>
                <div><label>主机:</label><input type="text" class="bleuc_inp" id="cip" value="${bleuc.cip}"/></div>
                <div><label>用户:</label><input type="text" class="bleuc_inp" id="cun" value="${bleuc.cun}"/></div>
                <div><label>密码:</label><input type="text" class="bleuc_inp" id="cpw" value="${bleuc.cpw}"/></div></p></div>
                `;
            return html;
        },
        hookXHRHeader() {
            XMLHttpRequest.prototype.setRequestHeader = function(header, value) {
                if(header == "x-signature"){
                    aliyun._signature=value;
                }
                return ORGXHRSRH.apply(this, arguments);
            }
        },
        cssStyle: `
            .bleuc_config_item{border-radius: 10px;font-size: 20px;margin: 12px 50px;color: #fff;background: linear-gradient(45deg,#12c2e9, #c471ed, #f64f59);box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2);}
            .bleuc_config_item label{font-size: 15px}
            .bleuc_config_item input.bleuc_inp{margin: 0px 10px;font-size: 15px;background: linear-gradient(45deg,#12c2e9, #c471ed, #f64f59);border-style:none;color:black;width:200px}
            .bleuc_config_item p{text-align: left;margin: 0px 20px;}`,

    }
    const xunlei = {
        addTag() {
            if (contextMenu.innerText.match(/PotPlayer/)) return
            let ul = document.createElement('ul');
            ul.innerHTML = `<a id="bleuReSave" class="pan-dropdown-menu-item">PotPlayer打开</a>`;
            contextMenu.firstChild.prepend(ul.firstChild);
            main.addClickEvent();
        },
        getselectFilesInfo() {
            let temp = document.querySelectorAll('li.SourceListItem__item--XxpOC.SourceListItem__active--4U0f4');
            temp.forEach((item) => {
                this._pushItem(item.__vue__.info);
            })
        },
        async updateFile(item) {
            Option["list"].push({
                "title": item.name,
                "url": `https://api-pan.xunlei.com/drive/v1/files/${item.id}`
            });
        },
        async openNextDir(item) {
            let url = `https://api-pan.xunlei.com/drive/v1/files?limit=100&parent_id=${item.id}&filters={"phase":{"eq":"PHASE_TYPE_COMPLETE"},"trashed":{"eq":false}}&with_audit=true`;
            await bleu.XHR('GET', url, undefined, Option.header).then((res) => {
                arryIndex++;
                res.files.forEach((item) => {
                    xunlei._pushItem(item);
                })
            },()=>{
                bleu.swalInfo("❗进出目录之后重新转存", '', 'center');
                throw "PPVSV:迅雷-超时,重新转存";
            })
        },
        findContext(node) {
            if (node.className === 'pan-content') {
                node = node.querySelector('div.pan-dropdown-menu.context-menu');
                if (!node) return;
                contextMenu = node;
                xunlei.addTag();
            }
        },
        closeMenu() {},
        _pushItem(temp) {
            if (!itemsInfo[arryIndex]) itemsInfo[arryIndex] = [];
            if (temp.kind === 'drive#file' && !tools.checkFileType(temp.name)) return
            let itemInfo = {
                'id': temp.id,
                'isdir': temp.kind === 'drive#file' ? false : true,
                'name': temp.name,
            };
            itemsInfo[arryIndex].push(itemInfo);
        },
        getHeaderInfo() {
            Option.header = {};
            Option.header.withCredentials = false;
            Option.header['content-type'] = 'application/json';
            for (let key in localStorage) {
                let temp = localStorage.getItem(key)
                if (key.indexOf('credentials') === 0) {
                    Option.header["Authorization"] = JSON.parse(temp).token_type + ' ' + JSON.parse(temp).access_token;
                    Option.header["token_type"] = JSON.parse(temp).token_type;
                    Option.header["access_token"] = JSON.parse(temp).access_token;
                    Option.header["refresh_token"] = JSON.parse(temp).refresh_token;
                    Option["clientid"] = key.substring(key.indexOf('_') + 1);
                }
                if (key.indexOf('captcha') === 0)
                    Option.header['x-captcha-token'] = JSON.parse(temp).token
                if (key === 'deviceid')
                    Option.header['x-device-id'] = temp.substring(temp.indexOf('.') + 1, 32 + temp.indexOf('.') + 1)
            }
        },
        async finallyFunc() {
            Option.header["clientid"] = Option["clientid"];
            await tools.putFileInWebdav('panvideo.txt', JSON.stringify(Option));
            unsafeWindow.location.href = `potplayer://panvideo##xunlei##https://${bleuc.cip}/PanPlaylist/panvideo.txt##${bleuc.cun}##${bleuc.cpw}`;
        }
    }
    const aliyun = {
        addTag() {
            if (contextMenu.innerText.match(/PotPlayer|新建/)) return
            let ul = document.createElement('ul');
            ul.innerHTML = `<li id="bleuReSave" class="ant-dropdown-menu-item ant-dropdown-menu-item-only-child" role="menuitem"><div class="outer-menu--ihDUR"><div data-confirm="false" class="menu-wrapper--1ZYh_" data-spm-anchor-id="aliyundrive.drive.0.i11.40516c75ahPUGN"><div class="menu-name--1F5vk" data-spm-anchor-id="aliyundrive.drive.0.i12.40516c75ahPUGN">PotPlayer打开</div></div></div></li>`;
            contextMenu.prepend(ul.firstChild);
            main.addClickEvent();
        },
        getselectFilesInfo() {
            let temp = document.querySelectorAll('div[data-index]')
            let attrName;
            for(let attr in temp[0]){
                if(attr.indexOf('__reactFiber')==0){
                    attrName = attr;
                    break;
                }
            }
            temp.forEach((item)=>{
                if(item.querySelector('input')&&item.querySelector('input').checked){
                    let value = item[attrName].return.pendingProps;
                    aliyun._pushItem(value.data[value.index]||value.data[value.rowIndex][value.columnIndex]);
                }
            })
            
        },
        async updateFile(item) {
            Option["list"].push({
                "title": item.name,
                "url": "https://"+item.id
            });
        },
        async openNextDir(item) {
            let url = `https://api.aliyundrive.com/adrive/v3/file/list?jsonmask=next_marker%2Citems(name%2Cfile_id%2Cdrive_id%2Ctype%2Csize%2Ccreated_at%2Cupdated_at%2Ccategory%2Cfile_extension%2Cparent_file_id%2Cmime_type%2Cstarred%2Cthumbnail%2Curl%2Cstreams_info%2Ccontent_hash%2Cuser_tags%2Ctrashed%2Cvideo_media_metadata%2Cvideo_preview_metadata)`,
                token = JSON.parse(localStorage.getItem('token')),
                data = {
                    'drive_id': token.default_drive_id,
                    'parent_file_id': item.id,
                    'limit': 100,
                },
                header = {
                    'x-canary': 'client=web,app=adrive,version=v2.4.0',
                    'x-device-id': document.cookie.match(/cna=([^;]*)/)[1],
                    authorization: `${token.token_type} ${token.access_token}`,
                    'x-signature':this._signature
                };
            await bleu.XHR('POST', url, JSON.stringify(data),header).then((res) => {
                arryIndex++;
                res.items.forEach((item)=>{
                    aliyun._pushItem(item);
                },()=>{
                    bleu.swalInfo("🔴💬刷新页面,重新获取", '', 'center');
                    throw "PPVSV:阿里-超时,重新转存";
                })
            })
        },
        _signature:'',
        findContext(node) {
            node = document.querySelector('ul.ant-dropdown-menu');
            if (!node) return;
            //observer.disconnect();
            contextMenu = node;
            aliyun.addTag();
        },
        closeMenu(){
            contextMenu.parentNode.className='ant-dropdown dropdown-menu--1KRbu ant-dropdown-placement-bottomLeft  ant-dropdown-hidden';
            contextMenu.parentNode.style.left='-578px'; 
            contextMenu.parentNode.style.top='-646px';
        },
        _pushItem(temp) {
            if(!itemsInfo[arryIndex]) itemsInfo[arryIndex]= [];
            if (temp.type==='file'&&temp.category!="video") return
            let itemInfo = {
                'id': temp.fileId||temp.file_id,
                'isdir': temp.type === 'file' ? false : true,
                'name': temp.name,
            };
            itemsInfo[arryIndex].push(itemInfo);
        },
        getHeaderInfo() {
            Option.header ={};
            let token = JSON.parse(localStorage.getItem('token'));
            Option.header["authorization"] =`${token.token_type} ${token.access_token}`;
            Option.header["drive_id"] =token.default_drive_id;
            Option.header["x-signature"]=this._signature;
            Option.header["x-device-id"]=document.cookie.match(/cna=([^;]*)/)[1];//decodeURIComponent(localStorage.getItem('APLUS_CNA').substring(9))
        },
        async finallyFunc(){
            await tools.putFileInWebdav('panvideo.txt', JSON.stringify(Option));
            unsafeWindow.location.href = `potplayer://panvideo##aliyun##https://${bleuc.cip}/PanPlaylist/panvideo.txt##${bleuc.cun}##${bleuc.cpw}`;
        }
    }
    const main = {
        init() {
            observer = new MutationObserver(function (mutations) {
                for (let mutation of mutations) {
                    if (mutation.type === 'childList') {
                        cloud.findContext(mutation.target);
                    }
                }
            });
            observer.observe(document, {
                'childList': true,
                'subtree': true
            });
        },
        addClickEvent() {
            let bleuButton = document.getElementById('bleuReSave');
            bleuButton.addEventListener('click', async function () {
                itemsInfo = [];
                arryIndex = 0;
                Option = {}, Option["list"] = [];
                if(!tools.checkConfig())return;
                cloud.closeMenu();
                cloud.getselectFilesInfo();
                cloud.getHeaderInfo();
                if (!itemsInfo[arryIndex]||itemsInfo[arryIndex].length === 0) {
                    bleu.swalInfo(`❌未选择文件转存!`, 3000, 'center')
                    return;
                }
                try {
                    await main.updateAllFiles(itemsInfo[arryIndex]);
                } catch (e) {
                    console.log(e);
                    return;
                }
                Option["list"].length!=0&&cloud.finallyFunc();
            })
        },
        async updateAllFiles(loopArry) {
            for (let index = 0; index < loopArry.length; index++) {
                if (!loopArry[index].isdir) {
                    await cloud.updateFile(loopArry[index]);
                } else {
                    await cloud.openNextDir(loopArry[index]);
                    await main.updateAllFiles(itemsInfo[arryIndex]);
                }
                bleu.sleep(800);
            }
        },
    };
    tools.getCloudName();
    //tools.checkConfig();
    bleu.addCssStyle(tools.cssStyle);
    GM_registerMenuCommand('配置WEBDAV', () => {
        bleu.swalUI('WEBDAV', tools.configHtml(), '400px').then(tools.saveConfig)
    }, 'w')
    main.init();
})();