// ==UserScript==
// @name 阿里云盘标清替换成最高画质
// @namespace http://tampermonkey.net/
// @version 1.1.6
// @description 通过阿里云盘开放平台将原标清替换成最高画质(支持分享链接),打开视频自动播放,视频前进后退5秒,更多倍速播放以及记忆倍速,视频下载,自动跳过片头片尾,播放器工具栏置底并设置透明度,播放历史记录,字幕颜色设置,上传本地字幕(vtt,srt,ass),WebDav同步配置信息,键盘快捷键: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
// @grant GM_xmlhttpRequest
// ==/UserScript==
var openapiclient_id = GM_getValue('openapiclient_id') || '55091393987b4cc090b090ee17e85e0a'
var wdusername = GM_getValue('webdav_user')
var wdpassword = GM_getValue('webdav_pwd')
var wdurl = GM_getValue('webdav_url')
var authHeader = 'Basic ' + btoa(wdusername + ':' + wdpassword);
var newurl, sdlurl, isfullscreen, sinfo, openapicode_verifier, observer, storedBlob, savedevice_id, playinfo
var vtthearder = 'WEBVTT\n0:00:00.000 --> 0:00:01.500\n<b>脚本制作人:bygavin(星峰)</b>\n'
var oldxhr = unsafeWindow.XMLHttpRequest
var oldfetch = unsafeWindow.fetch
function newobj() { }
if (openapiclient_id) {
(function (send) {
unsafeWindow.XMLHttpRequest.prototype.send = function (sendParams) {
const sendurl = this.__recordInfo__.url
if (sendurl.indexOf("/file/list") > 0 || sendurl.indexOf("/file/search") > 0 || sendurl.indexOf("/file/list_by_share") > 0) {
const oldargument = JSON.parse(sendParams)
oldargument.limit = 200
if (sendurl.indexOf("/file/list_by_share") <= 0)
savedevice_id = oldargument?.drive_id
arguments[0] = JSON.stringify(oldargument);
}
send.apply(this, arguments);
};
})(unsafeWindow.XMLHttpRequest.prototype.send);
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 = target.oldxhr.responseURL
const lpfn = localStorage.getItem('last_play_file_name')?.split('›').pop()
if (responseURL.indexOf("/file/get_video_preview_play_info") > 0 && prop.indexOf('response') !== -1) {
const isshare = responseURL.indexOf("/file/get_video_preview_play_info_by_share") > 0
const response = target.oldxhr?.response || target.oldxhr?.responseText
var res = JSON.parse(response);
const tasklist = res?.video_preview_play_info?.live_transcoding_task_list
if (!lpfn) {
const share_token = target.oldxhr.__reqCtx__.headers["x-share-token"]
sinfo = { share_token: share_token, file_id: res.file_id }
sdlurl = ''
newurl = getsetnewurl(res.file_id)
if (newurl) {
newurl.length > 1 && (sdlurl = newurl[1])
newurl = newurl[0]
}
else {
if (isshare) {
const saveinfo = savefile(res.file_id, share_token).responses[0].body
newurl = getVideoPreviewPlayInfo(saveinfo.drive_id, saveinfo.file_id)
deletefile(saveinfo.file_id)
}
else
newurl = getVideoPreviewPlayInfo(res.drive_id, res.file_id)
newurl && getsetnewurl(res.file_id, [newurl])
}
newurl && (tasklist[isshare ? 1 : 0].url = newurl)
createVttBlob(res)
res = JSON.stringify(res);
if (newurl)
return res;
}
else {
sdlurl = ''
newurl = tasklist[isshare ? 1 : 0].url
return response;
}
}
else if (responseURL.indexOf("/file/get_video_preview_play_info") > 0 && prop === "statusText")
morerate()
else if ((responseURL.indexOf("/file/list") > 0 || responseURL.indexOf("/file/list_by_share") > 0) && prop === "statusText" && lpfn && observer === undefined) {
observer = new MutationObserver(function () {
const target = document.querySelector('.text-primary--JzAb9')
if (target) {
observer.disconnect();
const lastvideo = document.querySelector('.text-primary--JzAb9[title="' + lpfn + '"]');
if (lastvideo) {
lastvideo.click()
localStorage.removeItem('last_play_file_name')
}
else {
const anyvideo = document.querySelector('img[alt="video"]')
if (anyvideo)
anyvideo.click()
else
localStorage.removeItem('last_play_file_name')
}
}
});
observer.observe(document, { childList: true, subtree: true });
}
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);
}
unsafeWindow.fetch = function (...bianliang) {
return new Promise(function (resolve) {
oldfetch(...bianliang).then(function (response) {
let handler = {
get: function (target, prop) {
if (typeof Reflect.get(target, prop) === 'function') {
if (Reflect.get(target, prop + 'proxy') === undefined) {
target[prop + 'proxy'] = (...funcargs) => {
let result = target[prop].call(target, ...funcargs)
if (bianliang.length > 0 && prop === 'blob' && bianliang[0].startsWith('blob')) {
return new Promise(function (resolve) {
result.then(
function (data) {
if (bianliang[0].endsWith('#bygavin') && storedBlob) {
const blob = new Blob([storedBlob], { type: 'application/octet-stream;charset=utf-8;' });
resolve(blob);
}
else
resolve(data)
}
)
});
}
return result
}
}
return Reflect.get(target, prop + 'proxy')
}
return Reflect.get(target, prop);
},
set(target, prop, value) {
return Reflect.set(target, prop, value);
},
};
resolve(new Proxy(response, handler))
})
});
}
}
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 }));
return xhr.status === 200 ? JSON.parse(xhr.responseText).video_preview_play_info?.live_transcoding_task_list.pop().url : 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 }));
return xhr.status === 200 ? JSON.parse(xhr.responseText).url : xhr.statusText;
}
function savefile(sharefileid, share_token) {
const token = JSON.parse(localStorage.getItem('token'))
const getsaveinfo = GM_getValue('saveinfo');
let saveinfo = getsaveinfo || {}
if (getsaveinfo && typeof getsaveinfo === "string") {
saveinfo = JSON.parse(getsaveinfo)
NewGM_setValue('saveinfo', 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));
return xhr.status === 200 ? JSON.parse(xhr.responseText) : xhr.statusText;
}
function deletefile(file_id) {
const token = JSON.parse(localStorage.getItem('token'))
const saveinfo = 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));
return xhr.status === 200 ? JSON.parse(xhr.responseText) : xhr.statusText;
}
function getopenapicode() {
openapicode_verifier = new Date().getTime();
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));
return xhr.status === 200 ? JSON.parse(xhr.responseText).redirectUri.split('=')[1] : xhr.statusText;
}
function getopenapitoken() {
const nowtime = new Date().getTime();
const openapitoken = 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
NewGM_setValue('openapitoken', tokejson)
return tokejson.access_token;
} else
return (xhr.statusText);
}
function createVttBlob(json) {
const blob = new Blob([vtthearder], { type: 'application/octet-stream;charset=utf-8;' });
const url = URL.createObjectURL(blob);
let sublist = json.video_preview_play_info?.live_transcoding_subtitle_task_list || []
if (sublist.length > 0) {
const newsublist = []
var xhr = new oldxhr();
sublist.forEach(function (item) {
xhr.open('GET', item.url, false);
xhr.send();
if (xhr.status === 200) {
const subvalue = xhr.responseText
if (subvalue.length > 1000) {
const newblob = new Blob([subvalue.replace('WEBVTT', vtthearder)], { type: 'application/octet-stream;charset=utf-8;' });
item.url = URL.createObjectURL(newblob) + "#byweb";
newsublist.push(item)
}
}
})
sublist = newsublist
}
sublist.push({ "status": "finished", url: url + "#bygavin" })
json.video_preview_play_info.live_transcoding_subtitle_task_list = sublist
}
function NewGM_setValue(key, value) {
GM_setValue('timestamp', new Date().getTime())
GM_setValue(key, value)
}
unsafeWindow.addEventListener('beforeunload', updateConfigIfNecessary)
function addkeyboardevent(e) {
const videoElement = document.querySelector('video')
var isvideo = true
let volume = parseInt(videoElement.volume * 100)
const volumeflag = volume < 6 ? 1 : volume < 33 ? 5 : 10
if (e && e.key === 'ArrowLeft')
videoElement.currentTime -= 5;
else if (e && e.key === 'ArrowRight')
videoElement.currentTime += 5;
else if (e && e.key === 'ArrowUp')
volume += volumeflag;
else if (e && e.key === 'ArrowDown')
volume -= volumeflag;
else
isvideo = false
videoElement.volume = (volume > 100 ? 100 : volume < 0 ? 0 : volume) / 100
return isvideo
}
document.addEventListener('keydown', function (e) {
const videoElement = document.querySelector('video')
if (videoElement) {
if (e.key === 'Home' || e.key === 'End')
changevideo(e.key === 'Home' ? -1 : 1)
else if (e.key === 'Enter')
document.querySelector('.action--HeWYA:not([data-active])').click()
addkeyboardevent(e)
}
if (e.altKey && e.code == 'KeyS') {
if (savedevice_id) {
const savefile_id = this.location.pathname.split('/')[4] || 'root'
if (confirm("确定设置此目录为临时转存目录"))
NewGM_setValue("saveinfo", { savedevice_id: savedevice_id, savefile_id: savefile_id })
}
}
else if (e.altKey && e.code == 'KeyD') {
var userInput = prompt("输入阿里云盘开放平台的client_id");
if (userInput !== null) {
NewGM_setValue('openapiclient_id', userInput)
openapiclient_id = userInput;
NewGM_setValue('openapitoken', null)
}
}
});
document.addEventListener('wheel', function (event) {
const videoElement = document.querySelector('video')
if (event.target === videoElement) {
let volume = parseInt(videoElement.volume * 100)
const volumeflag = volume < 6 ? 1 : volume < 33 ? 5 : 10
volume += volumeflag * (event.deltaY > 0 ? -1 : 1);
videoElement.volume = (volume > 100 ? 100 : volume < 0 ? 0 : volume) / 100
}
else if (event.target.tagName === "INPUT" && event.target.type === "time") {
const times = event.target.value.split(':')
const maxtimes = event.target.max.split(':')
const changeindex = event.offsetX > 30 ? 2 : 1
let newvalue = parseInt(times[changeindex]) + (event.deltaY > 0 ? -1 : 1)
let maxvalue = parseInt(maxtimes[changeindex])
newvalue > maxvalue && (newvalue = 0)
newvalue < 0 && (newvalue = maxvalue)
times[changeindex] = ("0" + (newvalue)).slice(-2);
event.target.value = times.join(':')
}
})
function morerate() {
storedBlob = null
let playbackRate = GM_getValue("playbackRate") || 1
let video = document.querySelector("video");
if (video) {
video.onplay = function () {
video.playbackRate = playbackRate
video.volume = GM_getValue('volume') || 1
if (video.volume < 1) {
video.volume += .01
video.volume -= .01
}
setTimeout(() => {
const allsubel = document.querySelectorAll('.meta--iPZhB')
const subInput = allsubel[allsubel.length - 1]
subInput.querySelector('.text--G8ymN').textContent = '本地外挂字幕'
subInput.querySelector('.text--G8ymN').title = '本地外挂字幕'
}, 0)
playing();
};
}
const ul = document.querySelector('div[class^="drawer-list-grid"]')
if (!ul || !video)
return
else if (document.querySelector('.btndownload'))
return
addsubcolor();
adddownloadbut();
setfullscreen();
video.addEventListener('dblclick', function () {
document.querySelector('.video-player--k1J-M .btn--UrTVT').click()
});
video.addEventListener('mousedown', function (event) {
event.button === 1 && document.querySelector('.action--HeWYA:not([data-active])').click()
});
video.addEventListener('volumechange', function () {
NewGM_setValue('volume', video.volume)
const volume = video.volume * 100
let volumelevel = parseInt((volume - 2) / 33) + 1
volumelevel = volume == 0 ? 0 : volumelevel
Array.from(document.querySelectorAll('.current--Dbz2w.current--0tS5B')).pop().style.width = volume + '%'
Array.from(document.querySelectorAll('.indicator--oPSic.indicator--qlLq-')).pop().style = `left: ${volume}%; transform: translate(-${volume}%, -50%);`
Array.from(document.querySelectorAll('.icon--EkKaB.icon--D3kMk use')).pop().setAttribute('xlink:href', '#PDSvolume' + volumelevel)
})
video.parentElement.parentElement.removeEventListener('keydown', null);
video.parentElement.parentElement.addEventListener('keydown', function (e) {
if (document.querySelector('.drawers--t3zFN').contains(e.target))
e.stopPropagation()
else
addkeyboardevent(e) && e.stopPropagation()
})
document.querySelector('.header-left--Kobd9').removeEventListener('click', updateConfigIfNecessary)
document.querySelector('.header-left--Kobd9').addEventListener('click', updateConfigIfNecessary)
const selector = ul.parentElement
ul.remove()
selector.innerHTML += `<ul class="drawer-list--qzUDz"><li class="drawer-item--22XTO playbackRate" data-is-current="true"><div class="text--dp1BR" style="margin:auto">${playbackRate} 倍速</div><span data-role="icon" style="position: absolute;right: 10px;" data-render-as="svg" data-icon-type="PDSCheckmark"class="icon--TBY0u icon--D3kMk "><svg viewBox="0 0 1024 1024"><use xlink:href="#PDSCheckmark"></use></svg></span></li></ul>`
const ratelist = [0.1, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.5, 3, 4, 5, 6, 8, 10, 12, 14, 16]
let moreratehtml = `<ul class="drawer-list--qzUDz ratemenulist" style="position:absolute;width:100%;top:15px;overflow:hidden;height:111px;padding-top:0px;padding-bottom:0px;display:none"><div style="width:91%;position:absolute;">${[ratelist[ratelist.length - 1], ...ratelist, ratelist[0]].map(ratenub => { return `<li class="drawer-item--22XTO" data-is-current="false"><div class="text--dp1BR" style="margin:auto">${ratenub} 倍速</div></li>` }).join('')}</div></ul><div class="drawer-label--FWKBs" ><span>跳过片头时长</span><div class="media-label-action--PwF31" style="margin:auto;" settype="default" >设置默认</div><span>跳过片尾时长</span><div></div></div><div class="drawer-label--FWKBs setautojump"><div class="media-label-action--PwF31" settype="start" >设置</div><input class="ant-input ant-input-borderless input--TWZaN" type="time" value="${playinfo?.startTime || "00:00:00"}" min="00:00:00" max="00:09:59" step="1" ></input><div class="media-label-action--PwF31" style="margin:auto;" settype="now" >设置播放进度</div><input class="ant-input ant-input-borderless input--TWZaN" type="time" value="${playinfo?.endTime || "00:00:00"}" min="00:00:00" max="00:09:59" step="1"></input><div class="media-label-action--PwF31" settype="end" >设置</div></div>`
selector.innerHTML += moreratehtml
const playbr = document.querySelector('.playbackRate')
const ratemenulist = document.querySelector('.ratemenulist')
const nowratediv = playbr.querySelector('div')
playbr.addEventListener('click', (e) => {
let nowrate = parseFloat(nowratediv.textContent.replace(' 倍速', ''))
nowratediv.textContent = ' '
ratemenulist.style.display = 'block'
const nowrateindex = ratelist.indexOf(nowrate)
ratemenulist.firstChild.style.top = (nowrateindex * -37) + 'px';
e.stopPropagation();
})
ratemenulist.addEventListener('click', (event) => {
let newSpeedStr = ''
if (event.target.tagName === 'LI')
newSpeedStr = event.target.firstChild.textContent
else if (event.target.tagName === 'DIV')
newSpeedStr = event.target.textContent
playbackRate = parseFloat(newSpeedStr.replace(' 倍速', ''));
video.playbackRate = playbackRate
NewGM_setValue("playbackRate", playbackRate)
nowratediv.textContent = newSpeedStr;
ratemenulist.style.display = 'none';
})
ratemenulist.addEventListener('wheel', function (event) {
event.preventDefault();
const flag = (event.deltaY > 0 ? -1 : 1);
let newtop = parseInt(ratemenulist.firstChild.style.top.replace('px', '')) + flag * 37
let mintop = -37 * (ratelist.length - 1)
if (newtop > 0)
newtop = mintop
if (newtop < mintop)
newtop = 0
ratemenulist.firstChild.style.top = newtop + 'px';
});
selector.addEventListener("click", (event) => {
const target = event.target
const classList = target.classList
if (!target.classList.contains('playbackRate')) {
ratemenulist.style.display = 'none';
nowratediv.textContent = playbackRate + ' 倍速';
}
if (classList.contains('media-label-action--PwF31')) {
let buttype = target.getAttribute('settype')
const timetxts = document.querySelectorAll('input[type="time"]')
let t = ''
const playhistory = GM_getValue('playhistory') || []
const file_path = location.pathname
const index = playhistory.findIndex(item => item.file_path === file_path)
playinfo = index === -1 ? { file_path: file_path } : playhistory[index]
if (buttype === 'default') {
const defaulttime = '00:02:00'
playinfo.startTime = defaulttime
playinfo.endTime = defaulttime
timetxts[0].value = defaulttime
timetxts[1].value = defaulttime
}
else {
if (buttype === 'now') {
t = parseInt(video.currentTime);
buttype = t < video.duration / 2 ? 'start' : 'end';
t = buttype === 'start' ? t : parseInt(video.duration - t)
t = Math.min(t, 599)
let date = new Date(t * 1000);
t = ("0" + (date.getHours() - 8)).slice(-2) + ':' + ("0" + date.getMinutes()).slice(-2) + ':' + ("0" + date.getSeconds()).slice(-2)
if (buttype === 'start')
timetxts[0].value = t
else if (buttype === 'end')
timetxts[1].value = t
}
else {
if (buttype === 'start')
t = timetxts[0].value
else if (buttype === 'end')
t = timetxts[1].value
}
if (buttype === 'start')
playinfo.startTime = t
else
playinfo.endTime = t
}
if (index === -1)
playhistory.unshift(playinfo)
NewGM_setValue('playhistory', playhistory)
}
})
}
function playing() {
const lpfn = localStorage.getItem('last_play_file_name')?.split('›').pop()
if (lpfn) {
document.querySelector('.title--RYadk[title="' + lpfn + '"]')?.click()
localStorage.removeItem('last_play_file_name')
}
else {
const video = document.querySelector('video')
let file_id = new URLSearchParams(newurl).get('f')
const playhistory = GM_getValue('playhistory') || []
let file_path = location.pathname
const index = playhistory.findIndex(item => item.file_path === file_path)
playinfo = index === -1 ? { file_path: file_path } : playhistory[index]
const nameel = document.querySelectorAll('.breadcrumb--gnRPG[data-calc="true"] .breadcrumb-item--j8J5H')
let file_name = ''
for (let i = 1; i < nameel.length; i++)
file_name += nameel[i].textContent
var listItems = Array.from(document.querySelectorAll('.list--5o17x li'));
if (listItems.length) {
var currentIndex = listItems.findIndex(li => li.getAttribute('data-is-current') === 'true');
file_name += listItems[currentIndex].querySelector('.title--RYadk').textContent
const currentTime = JSON.parse(localStorage.getItem('currentTime') || '{}')
if (currentTime?.currentTime) {
const currentTimeindex = playhistory.findIndex(item => item.file_path === currentTime.file_path)
if (currentTimeindex !== -1) {
playhistory[currentTimeindex].currentTime = currentTime.currentTime
if (index === currentTimeindex)
playinfo.currentTime = currentTime.currentTime
}
}
if (playinfo.currentTime && playinfo.file_id === file_id) {
video.currentTime = playinfo.currentTime
}
else if (playinfo.startTime) {
video.currentTime = playinfo.startTime.split(':').reduce((acc, v) => 60 * acc + +v);
}
playinfo.file_id = file_id
playinfo.file_name = file_name
index !== -1 && playhistory.splice(index, 1)
playhistory.unshift(playinfo)
}
NewGM_setValue('playhistory', playhistory.slice(0, 10))
video.removeEventListener('timeupdate', null)
video.addEventListener('timeupdate', function () {
video.currentTime && localStorage.setItem('currentTime', JSON.stringify({ file_path: file_path, currentTime: video.currentTime }))
if (playinfo.endTime)
video.currentTime > video.duration - playinfo.endTime.split(':').reduce((acc, v) => 60 * acc + +v) && changevideo(1)
})
}
}
const colorsgv = '#637dff'
const settingsvg = '<svg fill="' + colorsgv + '" style="margin-right: 15px; stroke-width="0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" class="hope-icon hope-c-XNyZK hope-c-PJLV hope-c-PJLV-ifkxHPo-css" tips="local_settings" height="1.5em" width="2em" style="overflow: visible;"><path d="M924.8 625.7l-65.5-56c3.1-19 4.7-38.4 4.7-57.8s-1.6-38.8-4.7-57.8l65.5-56a32.03 32.03 0 009.3-35.2l-.9-2.6a443.74 443.74 0 00-79.7-137.9l-1.8-2.1a32.12 32.12 0 00-35.1-9.5l-81.3 28.9c-30-24.6-63.5-44-99.7-57.6l-15.7-85a32.05 32.05 0 00-25.8-25.7l-2.7-.5c-52.1-9.4-106.9-9.4-159 0l-2.7.5a32.05 32.05 0 00-25.8 25.7l-15.8 85.4a351.86 351.86 0 00-99 57.4l-81.9-29.1a32 32 0 00-35.1 9.5l-1.8 2.1a446.02 446.02 0 00-79.7 137.9l-.9 2.6c-4.5 12.5-.8 26.5 9.3 35.2l66.3 56.6c-3.1 18.8-4.6 38-4.6 57.1 0 19.2 1.5 38.4 4.6 57.1L99 625.5a32.03 32.03 0 00-9.3 35.2l.9 2.6c18.1 50.4 44.9 96.9 79.7 137.9l1.8 2.1a32.12 32.12 0 0035.1 9.5l81.9-29.1c29.8 24.5 63.1 43.9 99 57.4l15.8 85.4a32.05 32.05 0 0025.8 25.7l2.7.5a449.4 449.4 0 00159 0l2.7-.5a32.05 32.05 0 0025.8-25.7l15.7-85a350 350 0 0099.7-57.6l81.3 28.9a32 32 0 0035.1-9.5l1.8-2.1c34.8-41.1 61.6-87.5 79.7-137.9l.9-2.6c4.5-12.3.8-26.3-9.3-35zM788.3 465.9c2.5 15.1 3.8 30.6 3.8 46.1s-1.3 31-3.8 46.1l-6.6 40.1 74.7 63.9a370.03 370.03 0 01-42.6 73.6L721 702.8l-31.4 25.8c-23.9 19.6-50.5 35-79.3 45.8l-38.1 14.3-17.9 97a377.5 377.5 0 01-85 0l-17.9-97.2-37.8-14.5c-28.5-10.8-55-26.2-78.7-45.7l-31.4-25.9-93.4 33.2c-17-22.9-31.2-47.6-42.6-73.6l75.5-64.5-6.5-40c-2.4-14.9-3.7-30.3-3.7-45.5 0-15.3 1.2-30.6 3.7-45.5l6.5-40-75.5-64.5c11.3-26.1 25.6-50.7 42.6-73.6l93.4 33.2 31.4-25.9c23.7-19.5 50.2-34.9 78.7-45.7l37.9-14.3 17.9-97.2c28.1-3.2 56.8-3.2 85 0l17.9 97 38.1 14.3c28.7 10.8 55.4 26.2 79.3 45.8l31.4 25.8 92.8-32.9c17 22.9 31.2 47.6 42.6 73.6L781.8 426l6.5 39.9zM512 326c-97.2 0-176 78.8-176 176s78.8 176 176 176 176-78.8 176-176-78.8-176-176-176zm79.2 255.2A111.6 111.6 0 01512 614c-29.9 0-58-11.7-79.2-32.8A111.6 111.6 0 01400 502c0-29.9 11.7-58 32.8-79.2C454 401.6 482.1 390 512 390c29.9 0 58 11.6 79.2 32.8A111.6 111.6 0 01624 502c0 29.9-11.7 58-32.8 79.2z"></path></svg>'
const potplayerdiv = '<svg style="margin-left: 10px;margin-right: 10px;cursor: pointer;" fill="' + colorsgv + '" stroke-width="0" class="hope-icon hope-c-XNyZK hope-c-PJLV hope-c-PJLV-ifkxHPo-css history" tips="history" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2770" width="2.5em" height="2.5em"><path d="M981.184 160.096c-143.616-20.64-302.336-32.096-469.184-32.096s-325.568 11.456-469.184 32.096c-27.52 107.712-42.816 226.752-42.816 351.904s15.264 244.16 42.816 351.904c143.648 20.64 302.336 32.096 469.184 32.096s325.568-11.456 469.184-32.096c27.52-107.712 42.816-226.752 42.816-351.904s-15.264-244.16-42.816-351.904zM384 704l0-384 320 192-320 192z" p-id="2771"></path></svg>'
function setStyle() {
const fontcss = GM_getValue('fontColor') || ''
const newsetstyle = document.querySelector('#newsetstyle')
newsetstyle && newsetstyle.remove();
var style = document.createElement('style');
const butnum = 3
const rightpx = 41 + butnum * 62
style.id = 'newsetstyle'
style.innerHTML = `
#webdav-panel .ant-input{box-shadow: 0 0 0 1px var(--theme_primary);}
.setautojump .media-label-action--PwF31{margin-left:2px;margin-right:2px;}
input[type="time"]{color:white !important;text-indent:-11px;width:61px;height:27px;top:-3px;}
input[type="time"]::-webkit-inner-spin-button,input[type="time"]::-webkit-calendar-picker-indicator {display: none;-webkit-appearance: none;}
.action--H2mi0:first-of-type,.drawers--t3zFN .drawer-container--tMDPx:first-of-type .title--6eEg9{text-indent:-9999px;display:flex;}
.action--H2mi0:first-of-type::after,.drawers--t3zFN .drawer-container--tMDPx:first-of-type .title--6eEg9::after {content: "设置";text-indent:0px;}
.breadcrumb--gnRPG.play-button:hover{color: rebeccapurple;}
.breadcrumb--gnRPG.play-button:hover svg{display:block}
.breadcrumb--gnRPG.play-button svg{height:20px;width:20px;position:absolute;top:-16px;display:none;}
.btnsetting{position: absolute;right: 10px;}
.btnsetting:hover>.mymemu:not(:empty) {display: flex;}
.mymemu {position: relative;display: none;right: -${rightpx}px;top: -20px;background-color: white;border-radius:10px}
.mymemu button{ padding: 5px 10px; border: none; color: #fff; border-radius: 4px; cursor: pointer;margin-top: 15px;margin-bottom: 15px;margin-right: 10px;}
.list--5o17x,.scroll-container--MsQem{scrollbar-width: none; -ms-overflow-style: none; }
#playhistory-panel .breadcrumb-item-link--9zcQY,#webdav-panel .breadcrumb-item-link--9zcQY{color: var(--theme_hover);}
: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,.ant-tooltip-inner,.loading--zyXaT{display:none}
.ended-container--Tz5lR,.content-wrapper--A93tB,.feature-blocker--vh7jp,.sign-bar--1XrSl,.ai-summary-btn--fQnJ{display:none!important}
`;
fontcss && (style.innerHTML += '.cue--rlq6T.cue-medium--be9UK,#colorPreview{color:' + fontcss + ';cursor: pointer;}')
document.head.appendChild(style);
}
setStyle();
function getsetnewurl(file_id, setnewurl) {
const today = new Date().toLocaleDateString()
const newurls = GM_getValue('newurls') || {}
if (!newurls.date)
newurls.date = today
if (setnewurl) {
newurls[file_id] = setnewurl;
NewGM_setValue('newurls', newurls)
}
else {
if (today === newurls.date) {
let geturl = newurls[file_id]
if (geturl) {
if (typeof geturl === "string")
geturl = [geturl]
const newsearch = new URLSearchParams(geturl[0])
if (parseInt(newsearch.get('x-oss-expires') + '000') < new Date().getTime())
geturl = undefined
}
return geturl
}
else {
NewGM_setValue('newurls', null)
return undefined;
}
}
}
function setfullscreen() {
function videoEndHandler() {
changevideo(1)
}
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
});
});
const video = document.querySelector("video");
video.removeEventListener('ended', videoEndHandler, false);
video.addEventListener('ended', videoEndHandler, false);
}
function addsubcolor() {
const fontcss = GM_getValue('fontColor') || ''
document.querySelectorAll('.scroll-wrapper--aByOe .drawer-label--FWKBs')[1].insertAdjacentHTML('beforeend', '<input ' + (fontcss ? '' : 'value="' + fontcss + '"') + ' style="margin-right: 130px;top:-50px;position:relative;" type="color" id="colorInput"><span id="colorPreview">字幕颜色</span>')
var colorInput = document.querySelector('#colorInput');
var colorPreview = document.querySelector('#colorPreview');
colorPreview.addEventListener('click', () => { colorInput.click(); });
colorInput.addEventListener('change', function () {
colorPreview.style.color = colorInput.value;
NewGM_setValue('fontColor', colorInput.value)
setStyle();
});
const loadsubbtn = document.querySelector('.meta--iPZhB').parentElement.parentElement
loadsubbtn.insertAdjacentHTML('beforeend', loadsubbtn.innerHTML.replace('手动添加外挂字幕', '添加本地外挂字幕').replace('data-is-current', 'style="margin-left: auto;" data-is-current'))
loadsubbtn.style.display = 'flex'
loadsubbtn.querySelectorAll('li .meta--iPZhB')[1].parentElement.addEventListener('click', function () {
var input = document.createElement('input');
input.type = 'file';
input.style.display = 'none';
document.body.appendChild(input);
input.addEventListener('change', function (e) {
var file = e.target.files[0];
var reader = new FileReader();
reader.onload = function (event) {
storedBlob = event.target.result;
if (file.name.toLowerCase().endsWith('.ass'))
storedBlob = convertAssToVtt(storedBlob)
else if (file.name.toLowerCase().endsWith('.srt'))
storedBlob = convertSrtToVtt(storedBlob)
document.body.removeChild(input);
document.querySelector('.action--H2mi0[data-active="true"]')?.click()
const subInput = Array.from(document.querySelectorAll('.meta--iPZhB')).pop()
subInput.querySelector('.text--G8ymN').textContent = file.name
subInput.querySelector('.text--G8ymN').title = file.name
if (subInput.parentElement.getAttribute('data-is-current') === 'true')
subInput.click()
subInput.click()
};
reader.readAsText(file);
});
input.click()
})
}
function convertAssToVtt(assSubtitles) {
const assLines = assSubtitles.split('\n');
let vttOutput = vtthearder;
assLines.forEach((line) => {
if (!line.trim() || line.trim().startsWith(';') || !line.trim().startsWith('Dialogue:')) return;
const lines = line.split(',');
const startTime = lines[1];
const endTime = lines[2];
const text = lines.slice(9).join(',');
const startTimeVtt = convertAssTimeToVttTime(startTime);
const endTimeVtt = convertAssTimeToVttTime(endTime);
vttOutput += `\n${startTimeVtt} --> ${endTimeVtt}\n${text}\n`
});
return vttOutput;
}
function convertAssTimeToVttTime(assTime) {
const parts = assTime.split('.').map(part => part.padStart(2, '0'));
const hours = parts[0].split(':')[0];
const minutes = parts[0].split(':')[1];
const seconds = parts[0].split(':')[2];
const milliseconds = '000' + parts[1];
return `${hours}:${minutes}:${seconds}.${milliseconds.substring(milliseconds.length - 3)}`;
}
function convertSrtToVtt(srtSubtitles) {
const assLines = srtSubtitles.split('\n');
let vttOutput = vtthearder;
assLines.forEach((line) => {
const regex = /^\d+$/;
if (regex.test(line.trim())) return;
if (line.indexOf('-->') !== -1)
line = line.replaceAll(',', '.')
vttOutput += line + '\n';
});
return vttOutput;
}
function adddownloadbut() {
const selffile = location.pathname.startsWith('/drive/file')
const div = document.querySelector('.actions--YfXrK')
div.insertAdjacentHTML('beforeend', '<div class="action--H2mi0 btndownload" data-active="false" data-disabled="false">下载</div>');
document.querySelector('.btndownload').addEventListener("click", () => {
const dl = document.createElement('a');
if (!sdlurl) {
const sss = new URLSearchParams(newurl)
const sfile_id = sss.get('f')
const newurls = GM_getValue('newurls') || {}
if (selffile) {
sdlurl = getDownloadUrl(sss.get('dr'), sfile_id)
newurls[sfile_id] = [newurl, sdlurl]
}
else {
const saveinfo = savefile(sinfo.file_id, sinfo.share_token).responses[0].body
sdlurl = getDownloadUrl(saveinfo.drive_id, saveinfo.file_id)
deletefile(saveinfo.file_id)
newurls[sinfo.file_id] = [newurl, sdlurl]
}
NewGM_setValue('newurls', newurls)
}
dl.href = sdlurl
dl.download = ''
dl.click();
})
setTimeout(() => {
const allmenubtn = document.querySelectorAll('.action--H2mi0:not(.btndownload)')
let showtime = []
allmenubtn.forEach(function (element, index) {
showtime.push(false)
const allshowdiv = document.querySelectorAll('.drawer-container--tMDPx')
const menudiv = allshowdiv[index + allshowdiv.length - allmenubtn.length];
[element, menudiv].forEach((el) => {
el.addEventListener('mouseenter', function () {
if (!document.querySelector('.action--H2mi0:not(.btndownload)[data-active="true"]')) {
showtime[index] && clearTimeout(showtime[index])
showtime[index] = setTimeout(() => {
menudiv.setAttribute('data-open', 'true')
menudiv.style.height = '384px'
}, 111);
}
});
el.addEventListener('mouseleave', function () {
if (!document.querySelector('.action--H2mi0:not(.btndownload)[data-active="true"]')) {
showtime[index] && clearTimeout(showtime[index])
showtime[index] = setTimeout(() => {
menudiv.setAttribute('data-open', 'false')
menudiv.style.height = '68px'
}, 111);
}
});
})
});
}, 0)
}
function changevideo(direction) {
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();
}
}
function abnormalplay() {
new MutationObserver(function () {
if (document.querySelector('.error--3ZTlN'))
document.querySelector('.video-player--k1J-M .btn--UrTVT').click()
}).observe(document.body, { childList: true, subtree: true });
}
unsafeWindow.onload = function () {
updateConfigIfNecessary();
abnormalplay();
addplayhistory();
};
function addplayhistory() {
var settingsButton = document.createElement('div');
settingsButton.innerHTML = `<div style="position: fixed; right: 40px; bottom:115px; z-index:112;border:none;width:auto;" class='membership-wrapper--6egJF'>${potplayerdiv}</div>`;
document.body.appendChild(settingsButton);
document.querySelector('.history').addEventListener('click', showManagementPanel);
}
function showManagementPanel() {
var previousPanel = document.getElementById('playhistory-panel');
previousPanel && previousPanel.parentNode.removeChild(previousPanel);
var playlist = GM_getValue('playhistory') || [];
var panel = document.createElement('div');
panel.id = 'playhistory-panel';
panel.style = `position: fixed; top: 0px; left: 0px; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); z-index: 99;`
panel.innerHTML =
`<div style="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;"><div style="display: flex; "><h2 style="margin-top: 0; margin-bottom: 10px; font-size: 24px;">历史播放记录</h2><div style="display: flex; justify-content: flex-end;top:30px" class="btnsetting"><div class="mymemu" style="right:0px"><button class="reset-button" playinfo-index="-1" style="background-color: #ffa31a;">重置所有设置</button><button class="clear-button" playinfo-index="-1" style="background-color: #ff4d4f;">清空播放记录</button><button class="webdav-button" playinfo-index="-1" style="background-color: #00c270;margin-right: 0px;">WebDav设置</button></div>${settingsvg}</div></div><hr style="margin-bottom: 20px;"><div style="overflow-y: auto; max-height: 600px;">${playlist.map(function (playinfo, index) {
let filenamelist = playinfo.file_name.split('›').filter(item => { return item !== "" }); const filename = filenamelist.pop(); let anotherNamehtml = ''; playinfo.anotherName && (filenamelist = [playinfo.anotherName]); filenamelist.map((fn) => { anotherNamehtml += `<div class="breadcrumb-item--j8J5H" data-hide="false" data-more="false"><div class="breadcrumb-item-link--9zcQY">${fn}</div><div class="breadcrumb-item-separator--MnbFV">›</div></div>` }); return `<div class="history-item" style="display: flex; margin-bottom: 10px;"><div playinfo-index="${index}" style="display: ruby;margin-top: auto;margin-bottom: auto;flex: 1;padding-right: 36px;cursor: pointer;" class="breadcrumb--gnRPG play-button" data-calc="true">${anotherNamehtml + filename}<div style="position: relative;">${potplayerdiv}</div></div><div style="display: flex; justify-content: flex-end;" class="btnsetting"><div class="mymemu" style="right:0px"><button class="rename-button" playinfo-index="${index}" style="background-color: #ffa31a;">别名</button><button class="delete-button" playinfo-index="${index}" style="background-color: #ff4d4f;margin-right: 0px;">删除</button></div>${settingsvg}</div></div>`;
}).join('')}</div></div>`;
document.body.appendChild(panel);
panel.addEventListener('click', function (e) {
var playhistory = GM_getValue('playhistory') || [];
let target = e.target
if (target == panel)
panel.parentNode.removeChild(panel);
else if (target.classList.contains('webdav-button')) {
panel.parentNode.removeChild(panel);
var panelwebdav = document.getElementById('webdav-panel');
panelwebdav && panelwebdav.parentNode.removeChild(panelwebdav);
panelwebdav = document.createElement('div');
panelwebdav.id = 'webdav-panel';
panelwebdav.style = `position: fixed; top: 0px; left: 0px; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); z-index: 99;`
panelwebdav.innerHTML =
`<div style="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;"><div style="display: flex; "><h2 style="margin-top: 0; margin-bottom: 10px; font-size: 24px;">WebDav设置</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;margin-right: 0px;">保存</button></div></div></div><hr style="margin-bottom: 20px;"><div class="history-item" style="display: flex; margin-bottom: 10px;"><div data-more="false" data-hide="false" class="breadcrumb-item--j8J5H" style="flex: 1;"><div class="breadcrumb-item-link--9zcQY">配置文件完整地址</div></div><input value=${wdurl} style="margin-left: 13px;height: 100%;flex: 4;" class="ant-input ant-input-borderless input--TWZaN" type="text"></div><div class="history-item" style="display: flex; margin-bottom: 10px;"><div data-more="false" data-hide="false" class="breadcrumb-item--j8J5H" style="flex: 1;"><div class="breadcrumb-item-link--9zcQY">账号</div></div><input value=${wdusername} style="margin-left: 13px;height: 100%;flex: 4;" class="ant-input ant-input-borderless input--TWZaN" type="text"></div><div class="history-item" style="display: flex; margin-bottom: 10px;"><div data-more="false" data-hide="false" class="breadcrumb-item--j8J5H" style="flex: 1;"><div class="breadcrumb-item-link--9zcQY">密码</div></div><input value=${wdpassword} style="margin-left: 13px;height: 100%;flex: 4;" class="ant-input ant-input-borderless input--TWZaN" type="text"></div>
</div>`
document.body.appendChild(panelwebdav);
panelwebdav.addEventListener('click', function (event) {
if (event.target == panelwebdav)
panelwebdav.parentNode.removeChild(panelwebdav);
else if (event.target.tagName === "BUTTON") {
const inputs = panelwebdav.querySelectorAll('input')
GM_setValue('webdav_url', inputs[0].value)
GM_setValue('webdav_user', inputs[1].value)
GM_setValue('webdav_pwd', inputs[2].value)
location.reload()
}
})
}
else {
if (target.parentElement.getAttribute('playinfo-index'))
target = target.parentElement
if (target.parentElement.parentElement.getAttribute('playinfo-index'))
target = target.parentElement.parentElement
let index = target.getAttribute('playinfo-index')
if (target.classList.contains('play-button')) {
localStorage.setItem('last_play_file_name', playhistory[index].file_name)
location.href = playhistory[index].file_path
}
else if (target.classList.contains('delete-button')) {
playhistory.splice(index, 1)
NewGM_setValue('playhistory', playhistory)
}
else if (target.classList.contains('rename-button')) {
var userInput = prompt("设置别名为:");
if (userInput) {
playhistory[index].anotherName = userInput
NewGM_setValue('playhistory', playhistory)
}
}
else if (target.classList.contains('reset-button'))
confirm("确定要重置所有设置吗?") && clearsetValue(['openapiclient_id', 'openapitoken', 'saveinfo', 'playbackRate', 'volume', 'fontColor', 'newurls', 'playhistory'])
else if (target.classList.contains('clear-button'))
confirm("确定要清空播放记录吗?") && clearsetValue(['newurls', 'playhistory'])
showManagementPanel()
}
function clearsetValue(keys) {
keys.map(key => NewGM_setValue(key, null))
}
});
}
function getAllGMdata() {
const GMkey = ['openapiclient_id', 'saveinfo', 'playbackRate', 'volume', 'fontColor', 'playhistory', 'timestamp']
const allGMdata = {}
GMkey.map(key => {
const GMvalue = GM_getValue(key)
GMvalue && (allGMdata[key] = GMvalue)
})
return allGMdata
}
function updateGMdata(jsobject) {
Object.keys(jsobject).map((key) => {
GM_setValue(key, jsobject[key])
})
}
function updateConfigIfNecessary() {
const currentTime = JSON.parse(localStorage.getItem('currentTime') || '{}')
if (currentTime?.currentTime) {
const playhistory = GM_getValue('playhistory') || []
const index = playhistory.findIndex(item => item.file_path === currentTime.file_path)
if (index !== -1) {
localStorage.removeItem('currentTime')
playhistory[index].currentTime = currentTime.currentTime
NewGM_setValue('playhistory', playhistory)
}
}
if (wdpassword !== '' && wdurl !== '' && wdusername !== '') {
const configPath = wdurl + '#' + new Date().getTime()
GM_xmlhttpRequest({
method: 'GET',
url: configPath,
headers: {
'Authorization': authHeader
},
onload: function (response) {
const localConfigData = getAllGMdata()
if (response.status === 200) {
const serverConfigObject = JSON.parse(response.responseText);
if ((localConfigData?.timestamp || 0) > serverConfigObject.timestamp)
updateWebDAVConfig(localConfigData);
else {
updateGMdata(serverConfigObject)
}
}
else {
updateWebDAVConfig(localConfigData);
}
}
});
}
}
function updateWebDAVConfig(newConfigData) {
const configPath = wdurl + '#' + new Date().getTime()
newConfigData.timestamp = new Date().getTime()
GM_xmlhttpRequest({
method: 'PUT',
url: configPath,
data: JSON.stringify(newConfigData),
headers: {
'Authorization': authHeader,
'Content-Type': 'application/json;charset=UTF-8'
}
});
}
//#endregion