// ==UserScript== // @name 阿里云盘助手 // @namespace http://tampermonkey.net/ // @version 1.0.2 // @description 支持生成文件下载链接,支持第三方播放器DPlayer(突破视频2分钟限制,选集,上下集) // @author 罗根大人 // @match https://www.aliyundrive.com/* // @connect * // @icon https://img.alicdn.com/imgextra/i1/O1CN01JDQCi21Dc8EfbRwvF_!!6000000000236-73-tps-64-64.ico // @require https://cdn.bootcdn.net/ajax/libs/localforage/1.10.0/localforage.min.js // @require https://cdn.bootcdn.net/ajax/libs/jquery/3.6.3/jquery.min.js // @require https://cdn.bootcdn.net/ajax/libs/dplayer/1.27.0/DPlayer.min.js // @require https://cdn.bootcdn.net/ajax/libs/hls.js/1.3.0/hls.min.js // @run-at document-body // @grant unsafeWindow // @grant GM_xmlhttpRequest // @license MIT // @downloadURL none // ==/UserScript== (function() { 'use strict' let dp = { dPlayerNode: null, player: null, // 当前页信息 page: { id: null, order: null, order_by: null, item: [] }, videoPage: { id: '', video: {}, player: null } } let transcoding = { UHD: '4K 超清', QHD: '2K 超清', FHD: '1080 全高清', HD: '720 高清', SD: '540 标清', LD: '360 流畅' } dp.createDPlayer = function() { let jquery = getJquery() let videoNode = jquery('video') if (videoNode.length <= 0) { return false } dp.dPlayerNode = jquery('
')[0] videoNode[0].parentNode.parentNode.replaceWith(dp.dPlayerNode) let video = dp.videoPage.video //视频信息 let play_info = video.video_preview_play_info //清晰度 let task_list = play_info.live_transcoding_task_list dp.videoPage.id = video.file_id let quality = [], defaultQuality task_list.forEach(function(item, index) { let name = transcoding[item.template_id] defaultQuality = index quality.push({ name: name, url: item.url || item.preview_url, type: 'hls' }) }) let options = { container: dp.dPlayerNode, video: { quality: quality, defaultQuality: defaultQuality, customType: { hls: function(video, player) { const hls = new Hls() hls.loadSource(video.src) hls.attachMedia(video) } } }, autoplay: true, screenshot: true, hotkey: false, airplay: true, volume: 1.0, contextmenu: [] } try { let player = dp.videoPage.player = new DPlayer(options) dp.selectEpisode() // player.options.hotkey || aliyunObj.dPlayerHotkey(); // aliyunObj.dPlayerEvents(player); // aliyunObj.dPlayerInitAspectRatio(); // aliyunObj.autoSkipPlayNext(); } catch (error) { console.error('播放器创建失败', error) } // let player = dp.player = new DPlayer(options); return true } dp.playerBindEvents = function() { dp.playerReady(dp.videoPage.player, function() { let player = dp.videoPage.player const { options: { contextmenu } } = player aliyunObj.memoryPlay() aliyunObj.dPlayerAspectRatio() aliyunObj.playSetting() aliyunObj.selectEpisode() aliyunObj.addCueVideoSubtitle(function(textTracks) { if (textTracks) { aliyunObj.selectSubtitles(textTracks) aliyunObj.dPlayerSubtitleStyle() } }) player.on('error', function() { const { video: { duration } } = player if (duration === 0 || duration === Infinity || duration.toString() === 'NaN') { alert('获取视频时长失败') } }) player.on('quality_end', function() { localStorage.setItem('dplayer-quality', player.quality.name) aliyunObj.addCueVideoSubtitle() }) player.speed(localStorage.getItem('dplayer-speed') || 1) player.on('ratechange', function() { player.notice('播放速度:' + player.video.playbackRate) localStorage.getItem('dplayer-speed') == player.video.playbackRate || localStorage.setItem('dplayer-speed', player.video.playbackRate) }) localStorage.getItem('dplayer-isfullscreen') == 'true' && player.fullScreen.request('browser') player.on('fullscreen', function() { localStorage.setItem('dplayer-isfullscreen', true) try { screen.orientation.lock('landscape') } catch (error) { } }) player.on('fullscreen_cancel', function() { localStorage.removeItem('dplayer-isfullscreen') try { screen.orientation.unlock() } catch (error) { } }) }) } dp.selectEpisode = function() { let jq = getJquery() if (jq('.dplayer-icons-right #btn-select-episode').length) return if (document.querySelectorAll('.dplayer-menu-item').length < 4) return let fileList = dp.page.item , videoList = fileList.filter(function(item, index) { return item.category === 'video' }) , fileIndex = videoList.findIndex(function(item, index) { return item.file_id === dp.videoPage.id }) if (!(fileIndex > -1 && videoList.length > 1)) return var elevideo = '' videoList.forEach(function(item, index) { if (fileIndex === index) { elevideo += '
' + item.name + '
' } else { elevideo += '
' + item.name + '
' } }) var svg = '' var html = '' html += '
' + elevideo + '
' html += '' jq('.dplayer-icons-right').prepend(html) jq('#btn-select-episode').on('click', function() { var eleEpisode = jq('.playlist-content') if (eleEpisode.css('transform').match(/\d+/) > 0) { eleEpisode.css('transform', 'scale(0)') } else { eleEpisode.css('transform', 'scale(1)') jq('.dplayer-mask').addClass('dplayer-mask-show') var singleheight = jq('.dplayer-icons-right .video-item')[0].offsetHeight var totalheight = jq('.dplayer-icons-right .playlist-content').height() jq('.dplayer-icons-right .playlist-content').scrollTop((fileIndex + 1) * singleheight - totalheight / 2) } }) jq('.dplayer-mask').on('click', function() { var eleEpisode = jq('.playlist-content') if (eleEpisode.css('transform').match(/\d+/) > 0) { eleEpisode.css('transform', 'scale(0)') jq(this).removeClass('dplayer-mask-show') } }) jq('.playlist-content .video-item').on('click', function() { var $this = jq(this) if ($this.hasClass('active')) return jq('.dplayer-mask').removeClass('dplayer-mask-show') var oldele = jq('.video-item.active') oldele.removeClass('active') oldele.css({ 'background-color': '', 'color': '#fff' }) $this.addClass('active') $this.css({ 'background-color': 'rgba(0,0,0,.3)', 'color': '#0df' }) let file = videoList[$this.index()] dp.playByFile(file) }) jq('.prev-icon').on('click', function() { var file = videoList[fileIndex - 1] file ? dp.playByFile(file) : showError('没有上一集了') }) jq('.next-icon').on('click', function() { var file = videoList[fileIndex + 1] file ? dp.playByFile(file) : showError('没有下一集了') }) } dp.playByFile = function(file) { let player = dp.videoPage.player try { player.pause() document.removeEventListener('click', player.docClickFun, true) player.container.removeEventListener('click', player.containerClickFun, true) player.fullScreen && player.fullScreen.destroy && player.fullScreen.destroy() player.hotkey && player.hotkey.destroy && player.hotkey.destroy() player.contextmenu && player.contextmenu.destroy && player.contextmenu.destroy() player.controller && player.controller.destroy && player.controller.destroy() player.timer && player.timer.destroy && player.timer.destroy() } catch (error) { } dp.videoPage.id = file.file_id dp.getVideoPreviewPlayInfo(function(response) { getJquery()('.header-file-name--CN_fq, .text--2KGvI').text(file.name) }) } dp.getVideoPreviewPlayInfo = function(callback) { refresh_token(function(result) { if (result) { if (location.href.indexOf('aliyundrive.com/drive') > 0) { videoPreviewPlayInfo(callback) } else { share_token(function(result) { if (result) { shareLinkVideoPlayInfo(callback) } else { callback && callback('') } }) } } else { callback && callback('') } }) } dp.playerReady = function(callback) { let player = dp.videoPage.player if (player.video.duration > 0 || player.video.readyState > 2) { player.isReady = true callback && callback() } else if (player.isReady) { callback && callback() } else { player.video.ondurationchange = function() { player.video.ondurationchange = null player.isReady = true callback && callback() } } } // 播放器 dp.playInfo = function(res) { let list = res.response.video_preview_play_info.live_transcoding_task_list dp.videoPage.id = res.response.file_id if (list[0].hasOwnProperty("preview_url")) { dp.getVideoPreviewPlayInfo(function(response) { if (response===''){ showError("播放信息获取失败,请刷新重试", 10000); return; } dp.videoPage.video = response; initPre() }) }else { dp.videoPage.video = res.response; initPre() } // dp.videoPage.video // initPre() // console.log(res) } dp.fileGet = function(response) { console.log(response) } //当文件较多时,需要合并 dp.fileList = function(res) { let data = res.data let response = res.response // 数据量过大,进行数组合并 if (dp.page.id === data.parent_file_id && dp.page.order === data.order_direction && dp.page.order_by === data.order_by) { dp.page.item = dp.page.item.concat(response.items) } else { dp.page.id = data.parent_file_id dp.page.order = data.order_direction dp.page.order_by = data.order_by dp.page.item = response.items } showSuccess(`已加载${dp.page.item.length}个文件`) if (dp.isHome()) { dp.initMenuButton() } else { dp.initShareButton() } } dp.getPlayNode = function(e) { let fileList = dp.page.item , parent_file_id = fileList[0].parent_file_id , videoMemory = getItem('video_memory') if (videoMemory && videoMemory[parent_file_id]) { return videoMemory[parent_file_id][e] } return '' } dp.isHome = function() { return location.href.indexOf('com/drive') > 0 } dp.isExpires = function(item) { let time = Date.parse(item.expire_time) - Date.now() return time > 0 && time < 1000 * Number(item.expires_in) } dp.setExpires = function(item, time) { item.expire_time = new Date(Date.now() + time).toISOString() item.expires_in = void 0 === time ? 600 : time return item } dp.showBox = function(fileList) { if (!fileList) { fileList = dp.selectedFileList() if (fileList.length === 0) { return } } let rowStyle = 'margin:10px 0px; overflow:hidden; white-space:nowrap; text-overflow:ellipsis;' let html = '' getJquery()('body').append(html) getJquery()('.ant-modal-Link .icon-wrapper--3dbbo').one('click', function() { getJquery()('.ant-modal-Link').remove() }) getJquery()('.ant-modal-Link .ant-modal-wrap').on('click', function(event) { if (getJquery()(event.target).closest('.ant-modal-content').length === 0) { getJquery()('.ant-modal-Link').remove() } }) getJquery()('.ant-modal-Link .appreciation').on('click', function() { window.open('https://greasyfork.org/zh-CN/scripts/458626', '_blank') }) } dp.showDownloadHomePage = function() { let max = dp.page.item.length dp.page.item.forEach(function(item, index) { if (item.download_url && !dp.isExpires(item)) { item.download_url = '' } if (item.download_url || item.type === 'folder') { max-- if (max === 0) { dp.showBox() } } else { getDownloadUrl(item.file_id, item.drive_id, function(download_url) { max-- if (download_url) { dp.setExpires(item, 1600) item.download_url = download_url } if (max === 0) { dp.showBox() } }) } }) } dp.initMenuButton = function() { let jq = getJquery() if (jq('.button-download-aliyun').length !== 0) { return } if (jq('#root header').length !== 0) { var html = '' html += '
' jq('#root header:eq(0)').append(html) jq('.button-download-aliyun').on('click', dp.showDownloadHomePage) } else { setTimeout(dp.initMenuButton, 1000) } } dp.selectedFileList = function() { let jq = getJquery() let selectedFileList = [], fileList = dp.page.item if (fileList.length === 0) { console.error('获取文件列表失败') return [] } let node = '' if (jq('.tbody--3Y4Fn .tr--5N-1q.tr--3Ypim').length) { node = jq('.tbody--3Y4Fn .tr--5N-1q.tr--3Ypim') } else if (jq('.outer-wrapper--25yYA').length) { node = jq('.outer-wrapper--25yYA') } node.each(function(index) { var $this = jq(this) if ($this.attr('data-is-selected') === 'true') { let data_index = $this.closest('[data-index]').attr('data-index') data_index && selectedFileList.push(fileList[data_index]) } }) return selectedFileList.length ? selectedFileList : fileList } // 分享按钮显示 dp.initShareButton = function() { let jq = getJquery() if (jq('.button-download-aliyun').length !== 0) { return } if (jq('#root [class^=banner] [class^=right]').length !== 0) { var html = '' html += '
' jq('#root [class^=banner] [class^=right]').prepend(html) jq('.button-download-aliyun').on('click', dp.showDownloadSharePage) } else { setTimeout(dp.initShareButton, 500) } } dp.showDownloadSharePage = function() { if (document.querySelector('[class^=login]')) { document.querySelector('[class^=login]').click() return } let fileList = dp.selectedFileList() if (fileList.length === 0) { return } dp.getShareLinkDownloadUrlAll(fileList, function(fileList) { dp.showBox(fileList) }) } dp.shareLinkDownloadUrl = function(file_id, share_id, callback) { var token = getToken() getJquery().ajax({ type: 'post', url: 'https://api.aliyundrive.com/v2/file/get_share_link_download_url', data: JSON.stringify({ //expire_sec: 600, file_id: file_id, share_id: share_id }), headers: { 'authorization': ''.concat(token.token_type || '', ' ').concat(token.access_token || ''), 'content-type': 'application/json;charset=utf-8', 'x-share-token': getItem('shareToken').share_token }, async: true, success: function(response) { if (response.download_url) { callback && callback(response.download_url) } else { callback && callback('') } }, error: function(error) { let errorCode = error.responseJSON ? error.responseJSON.code : '' if ('InvalidParameterNotMatch.ShareId' === errorCode) { showError('错误:参数不匹配,请刷新', 10000) } callback && callback('') } }) } dp.getShareLinkDownloadUrlAll = function(fileList, callback) { let max = fileList.length fileList.forEach(function(item, index) { if (item.download_url && !dp.isExpires(item)) { item.download_url = '' } if (item.download_url || item.type === 'folder') { max-- if (max === 0) { callback && callback(fileList) } } else { dp.getShareLinkDownloadUrl(item.file_id, item.share_id, function(download_url) { if (download_url) { dp.setExpires(item, 1600) item.download_url = download_url } max-- if (max === 0) { callback && callback(fileList) } }) } }) } dp.getShareLinkDownloadUrl = function(file_id, share_id, callback) { refresh_token(function(result) { if (result) { share_token(function(result) { if (result) { dp.shareLinkDownloadUrl(file_id, share_id, callback) } else { callback && callback('') } }) } else { callback && callback('') } }) } class XMLHttp { request = function(param) { } response = function(param) { } } let http = new XMLHttp() http.response = function(res) { let config = res.config if (config.status != 200) { return } if (getJquery()('.ant-modal-mask').length > 0) { return } let response = res.response, url = config.url try { config.data = JSON.parse(config.data) } catch (error) { config.data = {} } response = { response: response, data: config.data } if (url.endsWith('/file/get')) { dp.fileGet(response) } else if (url.indexOf('/file/list') > 0 || url.indexOf('/file/search') > 0) { dp.fileList(response) } else if (url.indexOf('/file/get_video_preview_play_info') > 0) { dp.playInfo(response) } } // 初始化 拦截XMLHttpRequest function initXMLHttpRequest() { let open = XMLHttpRequest.prototype.open XMLHttpRequest.prototype.open = function(...args) { let send = this.send let _this = this let post_data = [] this.send = function(...data) { post_data = data return send.apply(_this, data) } // 请求前拦截 http.request(args) this.addEventListener('readystatechange', function() { if (this.readyState === 4) { let config = { url: args[1], status: this.status, method: args[0], data: post_data } // 请求后拦截 let res = this.response if (typeof res == 'string') { try { res = JSON.parse(this.response) } catch (e) { console.log("解析出问题了, " ,e) } } http.response({ config, response: res }) } }, false) return open.apply(this, args) } } let getDownloadUrl = function(file_id, drive_id, callback) { let token = getToken() var that = getDownloadUrl getJquery().ajax({ type: 'post', url: 'https://api.aliyundrive.com/v2/file/get_download_url', data: JSON.stringify({ expire_sec: 14400, drive_id: drive_id, file_id: file_id }), headers: { 'authorization': ''.concat(token.token_type || '', ' ').concat(token.access_token || ''), 'content-type': 'application/json;charset=utf-8' }, async: true, success: function(response) { if (response.url) { callback && callback(response.url) } else { console.error('获取下载链接失败', response) callback && callback('') } }, error: function(error) { let errorCode = error.responseJSON ? error.responseJSON.code : '' if (errorCode === 'TooManyRequests') { setTimeout(function() { that(file_id, drive_id, callback) }, 500) } else { console.error('获取下载链接 错误', error) callback && callback('') } } }) } let getHomeDownloadUrl = function(fileId, driveId, callback) { let token = getToken() if (token == null) { refresh_token(function(res) { getHomeDownloadUrl() }) } else { getDownloadUrl(fileId, driveId, callback) } } let getToken = function() { let token = getLocalforage().getItem(`token`) if (token != null) { token = JSON.parse(token) if (dp.isExpires(token)) { return token } return null } return token } let getItem = function(n) { let item = getLocalforage().getItem(n) if (!item) { return null } try { return JSON.parse(item) } catch (e) { return n } } let videoPreviewPlayInfo = function(callback) { let token = getToken() || {}, file_id = dp.videoPage.id getJquery().ajax({ type: 'post', url: 'https://api.aliyundrive.com/v2/file/get_video_preview_play_info', data: JSON.stringify({ category: 'live_transcoding', drive_id: token.default_drive_id, file_id: file_id, template_id: '', get_subtitle_info: !0 }), headers: { 'authorization': ''.concat(token.token_type || '', ' ').concat(token.access_token || ''), 'content-type': 'application/json;charset=UTF-8' }, async: true, success: function(response) { callback && callback(response) }, error: function(error) { callback && callback('') } }) } let shareLinkVideoPlayInfo = function(callback) { var token = getToken() || {}, share_id = shareId(), file_id = dp.videoPage.id getJquery().ajax({ type: 'post', url: 'https://api.aliyundrive.com/v2/file/get_share_link_video_preview_play_info', data: JSON.stringify({ category: 'live_transcoding', file_id: file_id, get_preview_url: true, share_id: share_id, template_id: '', get_subtitle_info: !0 }), headers: { 'authorization': ''.concat(token.token_type || '', ' ').concat(token.access_token || ''), 'content-type': 'application/json;charset=UTF-8', 'x-share-token': getItem('shareToken').share_token }, async: true, success: function(response) { callback && callback(response) }, error: function(error) { callback && callback('') } }) } let shareId = function() { var url = location.href var match = url.match(/aliyundrive\.com\/s\/([a-zA-Z\d]+)/) return match ? match[1] : null } let share_token = function(callback) { var shareToken = getItem('shareToken') var share = shareId if (!shareToken) { showError('请登陆后刷新此页面重试!', 1e4) return callback && callback('') } if (dp.isExpires(shareToken)) { return callback && callback(shareToken) } getJquery().ajax({ type: 'post', url: 'https://api.aliyundrive.com/v2/share_link/get_share_token', data: JSON.stringify({ share_id: share(), share_pwd: '' }), headers: { 'Content-type': 'application/json;charset=utf-8' }, success: function(response) { if (response.share_token) { localStorage.setItem('shareToken', response instanceof Object ? JSON.stringify(response) : response) callback && callback(response) } else { callback && callback('') } }, error: function(error) { if (error.responseJSON.code === 'InvalidResource.SharePwd') { showError('请刷新并重新填写提取码', 1e4) } callback && callback('') } }) } let hideShow = function() { let t = getJquery()('.aDrive-notice') t.length && 'function' == typeof t.remove ? t.remove() : 'function' == typeof t.removeNode && t.removeNode(true) } let showSuccess = function(msg, timeout) { let jq = getJquery() hideShow() let element = jq('.aDrive div') let elementhtml = '
' + msg + '
' if (element.length) { element.append(elementhtml) } else { jq(document.body).append('
' + elementhtml + '
') } var hide = hideShow setTimeout(function() { hide() }, timeout || 3000) } let showError = function(msg, timeout) { let jq = getJquery() hideShow() var element = jq('.aDrive div') var elementhtml = '
' + msg + '
' if (element.length) { element.append(elementhtml) } else { jq(document.body).append('
' + elementhtml + '
') } var hide = hideShow setTimeout(function() { hide() }, timeout || 3000) } let refresh_token = function(callback) { let token = getToken() if (token != null) { return callback && callback(token) } if (!(token && token.refresh_token)) { return callback && callback('') } getJquery().ajax({ type: 'post', url: 'https://api.aliyundrive.com/token/refresh', data: JSON.stringify({ refresh_token: token.refresh_token }), headers: { 'Content-type': 'application/json;charset=utf-8' }, success: function(response) { if (response instanceof Object && response.access_token) { delete response.user_data localStorage.setItem('token', response instanceof Object ? JSON.stringify(response) : response) callback && callback(response) } else { callback && callback('') } }, error: function() { callback && callback('') } }) } let getJquery = function() { return $ } let getLocalforage = function() { return localStorage } let initPre = function() { if (!dp.createDPlayer()) { setTimeout(initPre, 500) } } initXMLHttpRequest() console.log('你好,罗根') })()