Greasy Fork is available in English.
先通过alist将阿里云盘资源秒传到115网盘,然后再通过cd2播放,alist转存有缓存时间,cd2是实时,所以用它来播放,需要alist添加阿里云盘和115的存储,挂载路径为阿里云盘和115且需要设置缓存时间设置为1分钟,不然转存后可能要等很久才能够秒传,cd2需添加115网盘,名称为115,阿里云盘需设置默认转存目录进入目标目录后按ALT+S保存,设置的临时目录需和alist挂载设备一致,不可一个在资源库,一个在备份盘
// ==UserScript== // @name 阿里云盘一键秒传到115云盘播放 // @namespace http://tampermonkey.net/ // @version 1.0.2 // @description 先通过alist将阿里云盘资源秒传到115网盘,然后再通过cd2播放,alist转存有缓存时间,cd2是实时,所以用它来播放,需要alist添加阿里云盘和115的存储,挂载路径为阿里云盘和115且需要设置缓存时间设置为1分钟,不然转存后可能要等很久才能够秒传,cd2需添加115网盘,名称为115,阿里云盘需设置默认转存目录进入目标目录后按ALT+S保存,设置的临时目录需和alist挂载设备一致,不可一个在资源库,一个在备份盘 // @author bygavin // @match https://www.aliyundrive.com/drive* // @match https://www.alipan.com/drive* // @match https://www.aliyundrive.com/s/* // @match https://www.alipan.com/s/* // @icon https://img.alicdn.com/imgextra/i1/O1CN01JDQCi21Dc8EfbRwvF_!!6000000000236-73-tps-64-64.ico // @license MIT // @grant GM_setValue // @grant GM_getValue // @grant unsafeWindow // @grant GM_xmlhttpRequest // ==/UserScript== //#region 变量定义 var savedevice_id, alistaliyun, getundone, getalist, oneclicksave, startbatch, morethen5G, videonamelist var obj = {}, pathinfo = {} const alist115yun = "/115/阿里云转存" var cd2url = GM_getValue("cd2url") || '' var alisturl = GM_getValue("alisturl") || '' var alisttoken = GM_getValue("alisttoken") || '' var oldxhr = unsafeWindow.XMLHttpRequest var savemode = GM_getValue("savemode") || 'save115' const saveinfo = GM_getValue('saveinfo') || {} const default_drive_id = JSON.parse(localStorage.getItem('token'))?.default_drive_id function newobj() { } //#endregion //#region 劫持send (function (send) { unsafeWindow.XMLHttpRequest.prototype.send = function (sendParams) { const sendurl = new URL(this.__recordInfo__.url).pathname if (sendurl.endsWith("/file/list") || sendurl.endsWith("/file/list_by_share")) {//修改获取列表数量为200 const oldargument = JSON.parse(sendParams) oldargument.limit && (oldargument.limit = 200) arguments[0] = JSON.stringify(oldargument); } else if (sendurl.endsWith('/batch')) {//强制修改转存路径 const oldargument = JSON.parse(sendParams) oldargument.requests.map(item => { if (item.body.to_parent_file_id) { item.body.to_parent_file_id = saveinfo.savefile_id item.body.to_drive_id = saveinfo.savedevice_id } }) arguments[0] = JSON.stringify(oldargument); } send.apply(this, arguments); }; })(unsafeWindow.XMLHttpRequest.prototype.send); //#endregion //#region 劫持xhr unsafeWindow.XMLHttpRequest = function () { let tagetobk = new newobj(); tagetobk.oldxhr = new oldxhr(); let handle = { get: function (target, prop) { 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 = new URL(target.oldxhr.responseURL).pathname if (responseURL.endsWith('/batch') && prop.indexOf('response') !== -1 && saveinfo.savedevice_id) { const res = JSON.parse(target.oldxhr?.response || target.oldxhr?.responseText); const resstatus = res.responses.pop()?.status if ((resstatus === 200 || resstatus === 201)) { alito115play(saveinfo.alistaliyun, true) } } else if (responseURL.endsWith('file/get_path') && prop.indexOf('response') !== -1) { const res = JSON.parse(target.oldxhr?.response || target.oldxhr?.responseText); savedevice_id = res.items[0].drive_id let dirlist = res.items.map(item => item.name).reverse() if (savedevice_id === default_drive_id) dirlist = ["备份文件", ...dirlist] alistaliyun = ["阿里云盘", ...dirlist].join('/') pathinfo.alistaliyun = alistaliyun pathinfo.pathname = location.pathname } else if ((responseURL.endsWith("file/list") || responseURL.endsWith("file/list_by_share")) && prop.indexOf('response') !== -1) { const res = JSON.parse(target.oldxhr?.response || target.oldxhr?.responseText); if (res.items?.length) { morethen5G = res.items.filter((item) => { return item.size > 5368709120 }).map(item => item.name).join(',') videonamelist = res.items.map(item => item.name) } } 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); } } return new Proxy(tagetobk, handle); } //#region 功能主体 function alito115play(videopath, isshare) { startbatch && clearTimeout(startbatch) startbatch = setTimeout(() => { if (videopath) obj.showNotify("开始转存:" + videopath); else { obj.showNotify("无效的资源路径", "fail"); return } const resfun = function (response) { if (response.status === 200) { const alistlistinfo = JSON.parse(response.responseText).data.content if (!alistlistinfo) { getalist && clearTimeout(getalist) getalist = setTimeout(() => { obj.showNotify("缓存未刷新,请等待"); alistfun("api/fs/list", JSON.stringify({ "path": videopath, "password": "", "page": 1, "per_page": 0, "refresh": false }), resfun) }, 1000) } else { const savedir = alistlistinfo.length == 1 && alistlistinfo[0].is_dir ? ('/' + alistlist[0]) : '' const pandir = isshare ? '' : ('/' + videopath.split('/').pop()) const alistlist = alistlistinfo.map(item => item.name); const potplayerrun = function () { const cd2 = new URL(cd2url) const potplayerurl = `potplayer://${cd2url}static/${cd2.protocol.slice(0, -1) + '/' + cd2.host}/True/${encodeURIComponent(alist115yun + savedir + pandir)}.clfsplaylist.m3u` const dl = document.createElement('a'); dl.href = potplayerurl dl.click(); } if (!isshare) { if (new Set([...videonamelist, ...alistlist]).size === alistlist.length) { obj.showNotify("资源已存在115网盘中,无需转存,启动potplayer播放115网盘资源"); potplayerrun() return } } const copyjson = { "src_dir": videopath, "dst_dir": alist115yun + pandir, "names": alistlist } const removejson = { "dir": videopath, "names": alistlist } obj.showNotify("开始秒传到115"); alistfun("api/fs/copy", JSON.stringify(copyjson), function (res1) { if (res1.status === 200) { const removealiyun = function () { alistfun("api/admin/task/copy/undone", null, function (res2) { if (res1.status === 200) { const undonelist = JSON.parse(res2.responseText).data.length if (undonelist) { obj.showNotify("秒传进行中,请等待"); getundone && clearTimeout(getundone) getundone = setTimeout(removealiyun, 1000) } else { const play115 = function () { obj.showNotify("启动potplayer播放115网盘资源"); potplayerrun() } if (isshare) { obj.showNotify("开始删除阿里云转存"); alistfun("api/fs/remove", JSON.stringify(removejson), play115) } else { setTimeout(play115, 0) } } } }, 'GET') } getundone && clearTimeout(getundone) getundone = setTimeout(removealiyun, 1000) } }) } } } if (savemode === 'save115') alistfun("api/fs/list", JSON.stringify({ "path": videopath, "password": "", "page": 1, "per_page": 0, "refresh": false }), resfun) else { const alist115yuns = alist115yun.split('/') const dirname = alist115yuns.pop() const removejson = { "dir": alist115yuns.join('/'), "names": [dirname] } alistfun("api/fs/remove", JSON.stringify(removejson), function () { alistfun("api/fs/list", JSON.stringify({ "path": videopath, "password": "", "page": 1, "per_page": 0, "refresh": false }), resfun) }) } }, 333) } //#endregion //#region alist请求 function alistfun(url, data, fun, method) { GM_xmlhttpRequest({ method: (method || 'POST'), url: alisturl + url, data: data, headers: { "authorization": alisttoken, "content-type": "application/json;charset=UTF-8", }, onload: fun }); } //#endregion //#endregion //#region 键盘快捷键 document.addEventListener('keydown', function (e) { if (e.altKey && e.code == 'KeyS') { if (savedevice_id && alistaliyun) { saveinfo.savedevice_id = savedevice_id saveinfo.savefile_id = (this.location.pathname.split('/')[4] || 'root') saveinfo.alistaliyun = alistaliyun obj.confirm('确定设置此目录为临时转存目录?', () => GM_setValue("saveinfo", saveinfo)) } } }); //#endregion //#region svg图标 const aliyunto115 = '<svg class="aliyunto115" fill="#637dff" width="3.7em" height="3.7em"><use xlink:href="#PDSsetting"></use></svg>' //#endregion //#region 自定义样式 function setStyle() { document.querySelector('#newsetstyle1')?.remove(); var style = document.createElement('style'); style.id = 'newsetstyle1' style.innerHTML = ` #setting-panel .breadcrumb-item-link--9zcQY,#confirm-panel .breadcrumb-item-link--9zcQY{color:var(--theme_hover)} .history-item{display:flex;margin-bottom:10px}.history-item .breadcrumb-item--j8J5H{max-width:50%}.history-item .breadcrumb-item-link--9zcQY{overflow: hidden;text-overflow: ellipsis;}.history-item input::placeholder{color: #3b8f4c;} .right-bottom-container--6JaaW{right:-47px !important;transition:right 0.3s;}.right-bottom-container--6JaaW:hover{right:0px !important;} .aliyunto115{position:relative;right:-47px;transition:right 0.3s}.aliyunto115:hover{fill:var(--theme_hover);right:0px;} #setting-panel,#confirm-panel{position:fixed;top:0px;left:0px;width:100%;height:100%;background-color:rgba(0,0,0,0.5);z-index:99} #setting-panel>div,#confirm-panel>div{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:#fff;padding:20px;border-radius:10px;box-shadow:0 2px 10px rgba(0,0,0,0.2);width:800px} #confirm-panel .ant-input{box-shadow:0 0 0 1px var(--theme_primary)} .btnsetting{position:absolute;right:10px} .btnsetting:hover>.mymemu:not(:empty){display:flex} .mymemu{position:relative;display:none;top:-20px} .mymemu button{padding:5px 10px;border:none;color:#fff;border-radius:4px;cursor:pointer;margin-top:15px;margin-right:10px} .breadcrumb--gnRPG.play-button:hover{color:rebeccapurple} .oneclicksave{margin-left:16px;background-color: #00c270 !important;} `; const Notifycss = [ ".notify{display:none;position:absolute;top:0;left:25%;width:50%;text-align:center;overflow:hidden;z-index:1010}", ".alert-success,.alert-loading{background:#36be63 !important;}", ".alert-fail{background:#ff794a !important;}", ".alert.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}", ".alert.fade.in{opacity:1}" ] style.innerHTML += Notifycss.join(' ') document.head.appendChild(style); } setStyle(); //#endregion //#region 显示管理面板 function showManagementPanel() { document.querySelector('#confirm-panel,#setting-panel')?.remove() var panelsetting = document.createElement('div'); panelsetting.id = 'setting-panel'; panelsetting.style = `z-index: 100;` panelsetting.innerHTML = `<div><div style="display: flex; "><h2 style="margin-top: 0; margin-bottom: 10px; font-size: 24px;">设置</h2><h2 style="margin:0 auto 10px auto; font-size: 24px;">${!saveinfo.alistaliyun ? '请到目标文件夹按Alt+S设置转存目录' : ('当前转存目录:' + saveinfo.alistaliyun.replace('阿里云盘/', ''))}</h2><div style="display: flex; justify-content: flex-end;top:30px" class="btnsetting"><div class="mymemu" style="right:10px;display:block"><button class="save-button" playinfo-index="-1" style="background-color: #00c270;">保存</button></div></div></div><hr style="margin-bottom: 20px;"><div class="history-item"><div data-more="false" data-hide="false" class="breadcrumb-item--j8J5H" style="flex: 1;"><div class="breadcrumb-item-link--9zcQY">115盘存储模式</div></div><ul class="tabs--dur-d tabs--SWY-k" style="flex: 4;margin-left: 10px;"><li class="save115 tab--j-QyM active--SEscZ"><span class="title--la5nd" title="不会进行清理,如115盘容量不够时会失败">保留</span></li><li class="clear115 tab--j-QyM"><span class="title--la5nd" title="转存到115盘之前会删除该文件夹再重新创建">清空</span></li></ul></div><div class="history-item"><div data-more="false" data-hide="false" class="breadcrumb-item--j8J5H" style="flex: 1;"><div class="breadcrumb-item-link--9zcQY">CD2网址</div></div><input placeholder="例如:http://192.168.99.211:19798/ 最后需要有/" value='${cd2url}' style="margin-left: 13px;height: 100%;flex: 4;" class="ant-input ant-input-borderless input--TWZaN" type="text"></div><div class="history-item"><div data-more="false" data-hide="false" class="breadcrumb-item--j8J5H" style="flex: 1;"><div class="breadcrumb-item-link--9zcQY">Alist网址</div></div><input placeholder="例如:http://192.168.99.211:5244/ 最后需要有/" value='${alisturl}' style="margin-left: 13px;height: 100%;flex: 4;" class="ant-input ant-input-borderless input--TWZaN" type="text"></div><div class="history-item"><div data-more="false" data-hide="false" class="breadcrumb-item--j8J5H" style="flex: 1;"><div class="breadcrumb-item-link--9zcQY">Alist Token</div></div><input placeholder="登录alist后,按F12,获取 应用程序>>本地存储>>token 的值" value='${alisttoken}' style="margin-left: 13px;height: 100%;flex: 4;" class="ant-input ant-input-borderless input--TWZaN" type="text"></div> </div>` document.body.appendChild(panelsetting); panelsetting.querySelectorAll('.tab--j-QyM').forEach(item => item.classList.toggle('active--SEscZ', false)) panelsetting.querySelector('.tab--j-QyM.' + savemode).classList.toggle('active--SEscZ', true) panelsetting.addEventListener('click', function (event) { let el = event.target; if (el == panelsetting) panelsetting.parentNode.removeChild(panelsetting); else if (el.classList.contains('save-button')) { const inputs = panelsetting.querySelectorAll('input') GM_setValue('cd2url', inputs[0].value) GM_setValue('alisturl', inputs[1].value) GM_setValue('alisttoken', inputs[2].value) location.reload() } else if (el.classList.contains('title--la5nd') || el.classList.contains('tab--j-QyM')) { el = el.classList.contains('tab--j-QyM') ? el : el.parentElement if (!el.classList.contains('active--SEscZ')) { panelsetting.querySelectorAll('.tab--j-QyM').forEach(item => item.classList.toggle('active--SEscZ')) savemode = el.classList.contains('save115') ? 'save115' : 'clear115' GM_setValue('savemode', savemode) } } }) } //#endregion //#region 显示提示信息 obj.showNotify = function (message, ...args) { if (unsafeWindow.application) { unsafeWindow.application.showNotify(message, ...args); } else { document.body.insertAdjacentHTML('beforeend', '<div id="J_Notify" class="notify" style="margin: 10px auto; display: none;"></div>') unsafeWindow.application = { notifySets: { type_class_obj: { success: "alert-success", fail: "alert-fail", loading: "alert-loading" }, count: 0, delay: 3e3 }, showNotify: function (message, ...args) { const opts = { message: message } args.forEach(arg => { if (typeof arg === 'number') opts.time = arg else if (typeof arg === 'string') opts.type = ["success", "fail", "loading"].includes(arg) ? arg : "success" }); var that = this, class_obj = that.notifySets.type_class_obj, count = that.notifySets.count; opts.type == "loading" && (delay *= 5); var JNotify = document.getElementById('J_Notify'); if (!document.querySelector(".alert")) { if (JNotify) { JNotify.innerHTML = '<div class="alert in fade button--WC7or primary--NVxfK medium--Pt0UL"></div>'; JNotify.style.display = 'block'; } } else { Object.keys(class_obj).forEach(function (key) { JNotify.classList.toggle(class_obj[key], false); }); } var alert = document.querySelector('.alert'); alert.textContent = opts.message; alert.classList.add(class_obj[opts.type]); that.notifySets.count += 1; var delay = opts.time || that.notifySets.delay; setTimeout(function () { if (++count == that.notifySets.count) { that.hideNotify(); } }, delay); }, hideNotify: function () { document.getElementById('J_Notify').innerHTML = ''; } }; obj.showNotify(message, ...args); } }; obj.hideNotify = function () { if (unsafeWindow.application) { unsafeWindow.application.hideNotify(); } }; //#endregion //#region 页面完全加载完成执行 new MutationObserver(function () { const savebtn = document.querySelector('.right-wrapper--cxNFP,.header--wVY7B') if (savebtn && alisturl && alisttoken && !savebtn.querySelector('.oneclicksave')) { savebtn.insertAdjacentHTML('beforeend', '<button class="oneclicksave button--WC7or primary--NVxfK medium--Pt0UL btn-save--SqM8z">一键转存播放</button>') savebtn.querySelector('.oneclicksave').addEventListener('click', () => { const thisrun = function () { if (savebtn.classList[0] === 'header--wVY7B') { alistaliyun = pathinfo?.alistaliyun == alistaliyun && pathinfo?.pathname == location.pathname ? alistaliyun : '' alito115play(alistaliyun, false) } else { oneclicksave = true document.querySelector('.button--WC7or.primary--NVxfK.medium--Pt0UL.btn-save--SqM8z').click() } } if (morethen5G) { obj.confirm(morethen5G + "\n超过了5G不支持秒传,是否继续?", () => thisrun()) } else { thisrun() } }) } const savenow = document.querySelector('.button--WC7or.primary--NVxfK.small--e7LRt.button--a4hgk') if (savenow && oneclicksave) { oneclicksave = false savenow.click() } const setbutton = document.getElementById('setting-button') if (!setbutton) { var settingsButton = document.createElement('div'); settingsButton.innerHTML = `<div id="setting-button" style="cursor:pointer;position: fixed; right: 0px; bottom:92px; z-index:112;border:none;width:auto;">${aliyunto115}</div>`; document.body.appendChild(settingsButton); settingsButton.addEventListener('click', showManagementPanel); } }).observe(document.body, { childList: true, subtree: true }); //#endregion //#region 确认框 obj.confirm = function (message, fun) { document.querySelector('#confirm-panel,#setting-panel')?.remove() var panelconfirm = document.createElement('div'); panelconfirm.id = 'confirm-panel'; panelconfirm.style = `z-index: 100;` panelconfirm.innerHTML = `<div style="background-color: rgb(225, 230, 215);"><div style="display: flex; "><h2 style="margin:0 auto 10px auto; font-size: 24px;">${message}</h2></div><div class="history-item"><div class="mymemu" style="display:block;margin: auto;top: 0px;"><button class="cancel-button" style="background-color: #E6A23C;">取消</button><button class="save-button" style="background-color: #1681FF;">确定</button></div></div> </div>` document.body.appendChild(panelconfirm); panelconfirm.addEventListener('click', function (event) { event.target.classList.contains('save-button') && fun && fun(); (event.target == panelconfirm || event.target.classList.contains('save-button') || event.target.classList.contains('cancel-button')) && panelconfirm.parentNode.removeChild(panelconfirm); }) } //#endregion