// ==UserScript== // @name 阿里云盘标清替换成最高画质 // @namespace http://tampermonkey.net/ // @version 1.0.4 // @description 通过阿里云盘开放平台将原标清替换成最高画质(支持分享链接),打开视频自动播放,视频前进后退5秒,更多倍速播放以及记忆倍速,播放器工具栏置底并设置透明度,快捷键:home上一集,end下一集,enter全屏 // @author bygavin // @match https://www.aliyundrive.com/* // @match https://www.alipan.com/* // @icon https://img.alicdn.com/imgextra/i1/O1CN01JDQCi21Dc8EfbRwvF_!!6000000000236-73-tps-64-64.ico // @license MIT // @grant unsafeWindow // @grant GM_setValue // @grant GM_getValue // @downloadURL none // ==/UserScript== // 将localStorage内容转移到油猴脚本,以便两个域名共用,下个版本移除该段代码 ['openapitoken', 'playbackRate', 'newurls', 'saveinfo', 'openapiclient_id'].forEach((el) => { const key = el.toString() const elvalue = localStorage.getItem(key) localStorage.removeItem(key) if (elvalue) { GM_setValue(key, elvalue) } }) let openapiclient_id = GM_getValue('openapiclient_id') || '55091393987b4cc090b090ee17e85e0a'//该client_id是官方文档出现的,测试可用,也可自行申请替换 const openapicode_verifier = generateUUID(12, '-') var newurl var isfullscreen = false; var device_list = {}; var time var oldxhr = unsafeWindow.XMLHttpRequest function newobj() { } if (openapiclient_id) { //劫持xhr unsafeWindow.XMLHttpRequest = function () { let tagetobk = new newobj(); tagetobk.oldxhr = new oldxhr(); let handle = { get: function (target, prop, receiver) { if (prop === 'oldxhr') { return Reflect.get(target, prop); } if (typeof Reflect.get(target.oldxhr, prop) === 'function') { if (Reflect.get(target.oldxhr, prop + 'proxy') === undefined) { target.oldxhr[prop + 'proxy'] = new Proxy(Reflect.get(target.oldxhr, prop), { apply: function (target, thisArg, argumentsList) { return Reflect.apply(target, thisArg.oldxhr, argumentsList); } }); } return Reflect.get(target.oldxhr, prop + 'proxy') } const responseURL = target.oldxhr.responseURL if (responseURL.indexOf("/file/get_video_preview_play_info_by_share") > 0 && prop.indexOf('response') !== -1) {//播放分享链接 const response = target.oldxhr?.response || target.oldxhr?.responseText var shareinfo = JSON.parse(response); newurl = getsetnewurl(shareinfo.file_id) if (!newurl) { const share_token = target.oldxhr.__reqCtx__.headers["x-share-token"] const saveinfo = savefile(shareinfo.file_id, share_token).responses[0].body newurl = getVideoPreviewPlayInfo(saveinfo.drive_id, saveinfo.file_id) deletefile(saveinfo.file_id) newurl && getsetnewurl(shareinfo.file_id, newurl) } newurl && (shareinfo.video_preview_play_info.live_transcoding_task_list[1].url = newurl) shareinfo = JSON.stringify(shareinfo); if (newurl) return shareinfo; } else if (responseURL.indexOf("/file/get_video_preview_play_info") > 0 && prop.indexOf('response') !== -1) {//播放自己云盘内容 const response = target.oldxhr?.response || target.oldxhr?.responseText var res = JSON.parse(response); newurl = getsetnewurl(res.file_id) if (!newurl) { newurl = getVideoPreviewPlayInfo(res.drive_id, res.file_id) newurl && getsetnewurl(res.file_id, newurl) } newurl && (res.video_preview_play_info.live_transcoding_task_list[0].url = newurl) res = JSON.stringify(res); if (newurl) return res; } else if (responseURL.indexOf("get_video_preview_play_info") > 0 && prop === "statusText") {//预览链接加载完成 morerate(); } else if (responseURL.endsWith("/user/get") && prop.indexOf('response') !== -1) {//获取自己云盘的设备id const response = target.oldxhr?.response || target.oldxhr?.responseText const res = JSON.parse(response) device_list.resource_drive_id = res.resource_drive_id device_list.backup_drive_id = res.backup_drive_id } return Reflect.get(target.oldxhr, prop); }, set(target, prop, value) { return Reflect.set(target.oldxhr, prop, value); }, has(target, key) { return Reflect.has(target.oldxhr, key); } } let ret = new Proxy(tagetobk, handle); return ret; } } //通过阿里云盘开放平台获取视频信息 function getVideoPreviewPlayInfo(drive_id, file_id) { const access_token = getopenapitoken(); var xhr = new oldxhr(); xhr.open("POST", "https://openapi.aliyundrive.com/adrive/v1.0/openFile/getVideoPreviewPlayInfo", false); xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); xhr.setRequestHeader("Authorization", access_token); xhr.send(JSON.stringify({ "drive_id": drive_id, "file_id": file_id, "category": "live_transcoding", "url_expire_sec": 14400 })); if (xhr.status === 200) { return (JSON.parse(xhr.responseText)).video_preview_play_info?.live_transcoding_task_list.pop().url; } else { return (xhr.statusText); } } //通过阿里云盘开放平台获取视频下载链接 function getDownloadUrl(drive_id, file_id) { const access_token = getopenapitoken(); var xhr = new oldxhr(); xhr.open("POST", "https://openapi.aliyundrive.com/adrive/v1.0/openFile/getDownloadUrl", false); xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); xhr.setRequestHeader("Authorization", access_token); xhr.send(JSON.stringify({ "drive_id": drive_id, "file_id": file_id, "expire_sec": 14400 })); if (xhr.status === 200) { return (JSON.parse(xhr.responseText)).url; } else { return (xhr.statusText); } } //转存文件到备份盘的根目录 function savefile(sharefileid, share_token) { const token = JSON.parse(localStorage.getItem('token')) const saveinfo = JSON.parse(GM_getValue('saveinfo') || "{}") const shareid = location.pathname.split('/')[2] const savedata = { "requests": [ { "body": { "file_id": sharefileid, "share_id": shareid, "auto_rename": true, "to_parent_file_id": saveinfo?.savefile_id || 'root', "to_drive_id": saveinfo?.savedevice_id || token.default_drive_id }, "headers": { "Content-Type": "application/json" }, "id": "0", "method": "POST", "url": "/file/copy" } ], "resource": "file" } var xhr = new oldxhr(); xhr.open("POST", "https://api.aliyundrive.com/adrive/v3/batch", false); xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); xhr.setRequestHeader("Authorization", token.access_token); xhr.setRequestHeader("X-Share-Token", share_token); xhr.send(JSON.stringify(savedata)); if (xhr.status === 200) { return (JSON.parse(xhr.responseText)); } else { return (xhr.statusText); } } //删除刚刚转存的文件 function deletefile(file_id) { const token = JSON.parse(localStorage.getItem('token')) const saveinfo = JSON.parse(GM_getValue('saveinfo') || "{}") const deleteinfo = { "requests": [ { "body": { "drive_id": saveinfo?.savedevice_id || token.default_drive_id, "file_id": file_id }, "headers": { "Content-Type": "application/json" }, "id": file_id, "method": "POST", "url": "/file/delete" } ], "resource": "file" } var xhr = new oldxhr(); xhr.open("POST", "https://api.aliyundrive.com/adrive/v3/batch", false); xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); xhr.setRequestHeader("Authorization", token.access_token); xhr.send(JSON.stringify(deleteinfo)); if (xhr.status === 200) { return (JSON.parse(xhr.responseText)); } else { return (xhr.statusText); } } //获取阿里云盘开放平台code function getopenapicode() { const accesstk = JSON.parse(localStorage.getItem('token')).access_token var xhr = new oldxhr(); var url = 'https://open.aliyundrive.com/oauth/users/authorize'; var data = { scope: 'user:base,file:all:read,file:all:write', authorize: 1, drives: ['backup', 'resource'] }; var params = '?client_id=' + openapiclient_id + '&redirect_uri=oob&scope=user:base,file:all:read,file:all:write&code_challenge=' + openapicode_verifier + '&code_challenge_method=plain'; xhr.open('POST', url + params, false); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.setRequestHeader('authorization', 'Bearer ' + accesstk); xhr.send(JSON.stringify(data)); if (xhr.status === 200) { const codeurl = JSON.parse(xhr.responseText).redirectUri return codeurl.split('=')[1] } else { return (xhr.statusText); } } //获取阿里云盘开放平台token function getopenapitoken() { const nowtime = new Date().getTime(); const openapitoken = JSON.parse(GM_getValue('openapitoken') || '{}') if (openapitoken.access_token) { if (openapitoken.createdate + openapitoken.expires_in > nowtime) { return openapitoken.access_token } } const openapicode = getopenapicode() var xhr = new oldxhr(); var url = 'https://openapi.aliyundrive.com/oauth/access_token'; var data = { client_id: openapiclient_id, grant_type: 'authorization_code', code: openapicode, code_verifier: openapicode_verifier }; xhr.open('POST', url, false); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.send(JSON.stringify(data)); if (xhr.status === 200) { const tokejson = JSON.parse(xhr.responseText) tokejson.createdate = nowtime GM_setValue('openapitoken', JSON.stringify(tokejson)) return tokejson.access_token; } else { return (xhr.statusText); } } //单双击功能 function clickanddbclick() { const video = document.querySelector("video"); let timer = 0; let delay = 200; let prevent = false; video.addEventListener('click', function (event) { timer = setTimeout(function () { if (!prevent) { document.querySelector('.video-player--k1J-M .btn--UrTVT').click() } prevent = false; }, delay); }); video.addEventListener('dblclick', function (event) { clearTimeout(timer); prevent = true; document.querySelector('.action--HeWYA:not([data-active])').click() }); } //全屏以及自动播放 function setfullscreen() { if (isfullscreen && document.fullscreenElement === null) { document.querySelector('.action--HeWYA:not([data-active])').click() } document.querySelector('.video-player--k1J-M .btn--UrTVT').click() var elements = document.querySelectorAll('.list--5o17x li, .next--k9RTS'); elements.forEach(function (element) { element.addEventListener('click', function () { isfullscreen = document.fullscreenElement !== null }); }); function videoEndHandler() { changevideo(1) } const video = document.querySelector("video"); video.removeEventListener('ended', videoEndHandler, false); video.addEventListener('ended', videoEndHandler, false); } //更多播放倍率 function morerate() { let playbackRate = GM_getValue("playbackRate") || 1; let video = document.querySelector("video"); if (video) { video.onplay = function () { video.playbackRate = playbackRate; }; } const ul = document.querySelector('div[class^="drawer-list-grid"]'); if (!ul || !video) { return; } else if (ul.childNodes.length > 7) {//确保只执行一次 return } setfullscreen(); clickanddbclick(); const close = ul.parentElement.parentElement.previousElementSibling.children.item(1); let firstChild = [...ul.children].find( (el) => el.firstChild.textContent === "1.5 倍" ); let secendChild = [...ul.children].find( (el) => el.firstChild.textContent === "0.75 倍" ); const originChild = firstChild; const rates = ["12", "10", "8", "6", "5", "4", "3", "2.5", "2", "0.5", "0.25", "0.1"]; rates.forEach((rate) => { if (parseInt(rate) < 2) { const cloneNode = secendChild.cloneNode(true); cloneNode.firstChild.innerHTML = `${rate} 倍`; ul.insertBefore(cloneNode, secendChild); secendChild = cloneNode; } else { const cloneNode = firstChild.cloneNode(true); cloneNode.firstChild.innerHTML = `${rate} 倍`; ul.insertBefore(cloneNode, firstChild); firstChild = cloneNode; } }); ul.insertBefore(originChild, firstChild); const backRateNodes = [...ul.children]; const changeSelectColor = (select) => { setTimeout(() => { backRateNodes.forEach((item) => { item.dataset.isCurrent = "false"; }); if (!select.dataset.isCurrent) { select.parentElement.dataset.isCurrent = "true"; } else { select.dataset.isCurrent = "true"; } }); }; const currentTarget = [...ul.children].find( (el) => el.firstChild.textContent === `${playbackRate} 倍` ); changeSelectColor(currentTarget); const tagNodes = [...ul.children]; for (let i = 0; i < 3; i++) { const node = tagNodes[tagNodes.length - 1 - i]; if (node && node.parentNode) { node.parentNode.removeChild(node); } } ul.addEventListener("click", (e) => { const target = e.target; playbackRate = target.textContent.replace(" 倍", ""); video.playbackRate = playbackRate; GM_setValue("playbackRate", playbackRate); changeSelectColor(target); close.click(); }); isChanged = true; } //获取或设置最高画质m3u8链接 function getsetnewurl(file_id, setnewurl) { const today = new Date().toLocaleDateString() const newurls = JSON.parse(GM_getValue('newurls') || '{}') if (!newurls.date) newurls.date = today if (setnewurl) { newurls[file_id] = setnewurl; GM_setValue('newurls', JSON.stringify(newurls)) } else { if (today === newurls.date) { let geturl = newurls[file_id] const newsearch = new URLSearchParams(geturl) if (parseInt(newsearch.get('x-oss-expires') + '000') < new Date().getTime()) { geturl = undefined } return geturl } else {//隔天清空缓存 GM_setValue('newurls', '{}') return undefined; } } } //生成guid function generateUUID(length, radix) { var d = new Date().getTime(); var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { var r = (d + Math.random() * 16) % 16 | 0; d = Math.floor(d / 16); return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16); }); length && (uuid = uuid.substring(0, length)); radix && (uuid = uuid.replaceAll(radix, '')); return uuid } //切换上下集体 function changevideo(direction) { time && clearTimeout(time) time = setTimeout(() => { var listItems = Array.from(document.querySelectorAll('.list--5o17x li')); var currentIndex = listItems.findIndex(li => li.getAttribute('data-is-current') === 'true'); if ((direction === -1 && currentIndex > 0) || (direction === 1 && currentIndex < listItems.length - 1)) { isfullscreen = document.fullscreenElement !== null listItems[currentIndex + direction]?.click(); } }, 111) } //处理播放异常 function abnormalplay() { // 创建一个观察器实例并传入回调函数 const observer = new MutationObserver(function (mutationsList, observer) { for (let mutation of mutationsList) { if (mutation.type === 'childList') { const isPlayError = containsErrorMessage(mutation.target, '播放异常,请稍后再试'); if (isPlayError) { document.querySelector('.video-player--k1J-M .btn--UrTVT')?.click() } } } }); // 选择需要观察变动的节点 const targetNode = document.body; // 配置观察选项 const config = { childList: true, subtree: true }; // 传入目标节点和观察选项 observer.observe(targetNode, config); function containsErrorMessage(node, text) { if (node.nodeType === Node.TEXT_NODE) { // 如果是文本节点,检查文本内容是否包含指定文本 if (node.textContent.includes(text)) { return true; } } else if (node.childNodes && node.childNodes.length > 0) { // 如果是元素节点,并且有子节点,递归遍历子节点 for (let i = 0; i < node.childNodes.length; i++) { if (containsErrorMessage(node.childNodes[i], text)) { return true; } } } return false; } } //自定义样式 (function () { var style = document.createElement('style'); style.innerHTML = ` :fullscreen .video-player--k1J-M {bottom: 0px;opacity:0!important;} .video-player--k1J-M {bottom: -80px;opacity:0.8!important;} .text-primary--3DHOJ{overflow:visible;font-weight:bold} .loader--3P7-4,.loader--zXBWG{opacity:0.8!important} .outer-wrapper--3ViSy{opacity:0!important} .outer-wrapper--3ViSy:hover,.video-player--k1J-M:hover{opacity:0.8!important} .button--1pH7M,.container--CIvrv{display:none} .ended-container--Tz5lR,.content-wrapper--A93tB,.feature-blocker--vh7jp,.sign-bar--1XrSl,.ai-summary-btn--fQnJ{display:none!important} `; document.head.appendChild(style); })() //键盘快捷键 document.addEventListener('keydown', function (e) { const videoElement = document.querySelector('video') if (videoElement) { if (e.key === 'Home' || e.key === 'End') { var direction = event.key === 'Home' ? -1 : 1; changevideo(direction) } else if (e.key === 'Enter') { document.querySelector('.action--HeWYA:not([data-active])').click() } else if (e && e.keyCode === 37) { videoElement.currentTime -= 5; } else if (e && e.keyCode === 39) { videoElement.currentTime += 5; } return false } if (e.altKey && e.code == 'KeyS') { let savedevice_id = '' if (location.pathname.startsWith('/drive/file/resource')) { savedevice_id = device_list.resource_drive_id } else if (this.location.pathname.startsWith('/drive/file/backup')) { savedevice_id = device_list.backup_drive_id } if (savedevice_id) { const savefile_id = this.location.pathname.split('/')[4] || 'root' if (confirm("确定设置此目录为临时转存目录")) GM_setValue("saveinfo", JSON.stringify({ savedevice_id: savedevice_id, savefile_id: savefile_id })) } } else if (e.altKey && e.code == 'KeyD') { var userInput = prompt("输入阿里云盘开放平台的client_id"); if (userInput !== null) { GM_setValue('openapiclient_id', userInput) openapiclient_id = userInput; GM_setValue('openapitoken', '{}') } } });