// ==UserScript==
// @name 阿里云盘助手
// @namespace http://tampermonkey.net/
// @version 1.0.1
// @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 += ' '
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()
})
}
// 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 = '
'
html += '
共加载了 ' + fileList.length + '个文件
'
html += '
'
fileList.forEach(function(item, index) {
html += '
' + (++index) + ':' + item.name + '
'
if (item.type === 'file') {
html += '
' + item.download_url + '
'
} else if (item.type === 'folder') {
html += '
请进入文件夹下载
'
}
})
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) {}
}
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 = ''
if (element.length) {
element.append(elementhtml)
} else {
jq(document.body).append('
')
}
var hide = hideShow
setTimeout(function() {
hide()
}, timeout || 3000)
}
let showError = function(msg, timeout) {
let jq = getJquery()
hideShow()
var element = jq('.aDrive div')
var elementhtml = '
'
if (element.length) {
element.append(elementhtml)
} else {
jq(document.body).append('
')
}
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('你好,罗根')
})()