// ==UserScript==
// @name 阿里云盘标清替换成最高画质
// @namespace http://tampermonkey.net/
// @version 1.0.6
// @description 通过阿里云盘开放平台将原标清替换成最高画质(支持分享链接),打开视频自动播放,视频前进后退5秒,更多倍速播放以及记忆倍速,视频下载,播放器工具栏置底并设置透明度,播放历史记录,快捷键:home上一集,end下一集,enter全屏
// @author bygavin
// @match https://www.aliyundrive.com/*
// @match https://www.alipan.com/*
// @icon https://img.alicdn.com/imgextra/i1/O1CN01JDQCi21Dc8EfbRwvF_!!6000000000236-73-tps-64-64.ico
// @license MIT
// @grant unsafeWindow
// @grant GM_setValue
// @grant GM_getValue
// ==/UserScript==
//#region 变量声明
var openapiclient_id = GM_getValue('openapiclient_id') || '55091393987b4cc090b090ee17e85e0a'//该client_id是官方文档出现的,测试可用,也可自行申请替换
var newurl, sdlurl, isfullscreen, time, sinfo, openapicode_verifier, observer
var device_list = {};
var oldxhr = unsafeWindow.XMLHttpRequest
function newobj() { }
//#endregion
//#region 主入口
if (openapiclient_id) {
//劫持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 = target.oldxhr.responseURL
if (responseURL.indexOf("/file/get_video_preview_play_info_by_share") > 0 && prop.indexOf('response') !== -1) {//播放分享链接
const response = target.oldxhr?.response || target.oldxhr?.responseText
const share_token = target.oldxhr.__reqCtx__.headers["x-share-token"]
var shareinfo = JSON.parse(response);
sinfo = { share_token: share_token, file_id: shareinfo.file_id }
sdlurl = ''
newurl = getsetnewurl(shareinfo.file_id)
if (newurl) {
if (newurl.length > 1) {
sdlurl = newurl[1]
}
newurl = newurl[0]
}
else {
const saveinfo = savefile(shareinfo.file_id, share_token).responses[0].body
newurl = getVideoPreviewPlayInfo(saveinfo.drive_id, saveinfo.file_id)
deletefile(saveinfo.file_id)
newurl && getsetnewurl(shareinfo.file_id, [newurl])
}
newurl && (shareinfo.video_preview_play_info.live_transcoding_task_list[1].url = newurl)
shareinfo = JSON.stringify(shareinfo);
if (newurl)
return shareinfo;
}
else if (responseURL.indexOf("/file/get_video_preview_play_info") > 0 && prop.indexOf('response') !== -1) {//播放自己云盘内容
const response = target.oldxhr?.response || target.oldxhr?.responseText
var res = JSON.parse(response);
sdlurl = ''
newurl = getsetnewurl(res.file_id)
if (newurl) {
if (newurl.length > 1) {
sdlurl = newurl[1]
}
newurl = newurl[0]
}
else {
newurl = getVideoPreviewPlayInfo(res.drive_id, res.file_id)
newurl && getsetnewurl(res.file_id, [newurl])
}
newurl && (res.video_preview_play_info.live_transcoding_task_list[0].url = newurl)
res = JSON.stringify(res);
if (newurl)
return res;
}
else if (responseURL.indexOf("get_video_preview_play_info") > 0 && prop === "statusText") {//预览链接加载完成
morerate();
}
else if (responseURL.endsWith("/user/get") && prop.indexOf('response') !== -1) {//获取自己云盘的设备id
const response = target.oldxhr?.response || target.oldxhr?.responseText
const res = JSON.parse(response)
device_list.resource_drive_id = res.resource_drive_id
device_list.backup_drive_id = res.backup_drive_id
}
else if ((responseURL.indexOf("/file/list") > 0 || responseURL.indexOf("/file/list_by_share") > 0) && prop === "statusText" && localStorage.getItem('last_play_file_name') && observer === undefined) {
let selector = '.text-primary--JzAb9';
observer = new MutationObserver(function () {
const target = document.querySelector(selector)
if (target) {
observer.disconnect();
const anyvideo = document.querySelector('img[alt="video"]')
if (anyvideo) {
anyvideo.click()
}
}
});
let config = { childList: true, subtree: true };
observer.observe(document, config);
// 等待元素.text-primary--JzAb9加载完成,出现然后点击
// document.querySelector('.text-primary--JzAb9').click()
// document.querySelector('img[alt="video"]').click()
}
return Reflect.get(target.oldxhr, prop);
},
set(target, prop, value) {
return Reflect.set(target.oldxhr, prop, value);
},
has(target, key) {
return Reflect.has(target.oldxhr, key);
}
}
let ret = new Proxy(tagetobk, handle);
return ret;
}
}
//#endregion
//#region 网络请求
//通过阿里云盘开放平台获取视频信息
function getVideoPreviewPlayInfo(drive_id, file_id) {
const access_token = getopenapitoken();
var xhr = new oldxhr();
xhr.open("POST", "https://openapi.aliyundrive.com/adrive/v1.0/openFile/getVideoPreviewPlayInfo", false);
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.setRequestHeader("Authorization", access_token);
xhr.send(JSON.stringify({ "drive_id": drive_id, "file_id": file_id, "category": "live_transcoding", "url_expire_sec": 14400 }));
if (xhr.status === 200) {
return (JSON.parse(xhr.responseText)).video_preview_play_info?.live_transcoding_task_list.pop().url;
} else {
return (xhr.statusText);
}
}
//通过阿里云盘开放平台获取视频下载链接
function getDownloadUrl(drive_id, file_id) {
const access_token = getopenapitoken();
var xhr = new oldxhr();
xhr.open("POST", "https://openapi.aliyundrive.com/adrive/v1.0/openFile/getDownloadUrl", false);
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.setRequestHeader("Authorization", access_token);
xhr.send(JSON.stringify({ "drive_id": drive_id, "file_id": file_id, "expire_sec": 14400 }));
if (xhr.status === 200) {
return (JSON.parse(xhr.responseText)).url;
} else {
return (xhr.statusText);
}
}
//转存文件
function savefile(sharefileid, share_token) {
const token = JSON.parse(localStorage.getItem('token'))
const getsaveinfo = GM_getValue('saveinfo');
let saveinfo = getsaveinfo || {}
if (getsaveinfo && typeof getsaveinfo === "string") {
saveinfo = JSON.parse(getsaveinfo)
GM_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));
if (xhr.status === 200) {
return (JSON.parse(xhr.responseText));
} else {
return (xhr.statusText);
}
}
//删除刚刚转存的文件
function deletefile(file_id) {
const token = JSON.parse(localStorage.getItem('token'))
const saveinfo = GM_getValue('saveinfo') || {}
const deleteinfo = {
"requests": [
{
"body": {
"drive_id": saveinfo?.savedevice_id || token.default_drive_id,
"file_id": file_id
},
"headers": {
"Content-Type": "application/json"
},
"id": file_id,
"method": "POST",
"url": "/file/delete"
}
],
"resource": "file"
}
var xhr = new oldxhr();
xhr.open("POST", "https://api.aliyundrive.com/adrive/v3/batch", false);
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.setRequestHeader("Authorization", token.access_token);
xhr.send(JSON.stringify(deleteinfo));
if (xhr.status === 200) {
return (JSON.parse(xhr.responseText));
} else {
return (xhr.statusText);
}
}
//获取阿里云盘开放平台code
function getopenapicode() {
openapicode_verifier = generateUUID(12, '-')
const accesstk = JSON.parse(localStorage.getItem('token')).access_token
var xhr = new oldxhr();
var url = 'https://open.aliyundrive.com/oauth/users/authorize';
var data = {
scope: 'user:base,file:all:read,file:all:write',
authorize: 1,
drives: ['backup', 'resource']
};
var params = '?client_id=' + openapiclient_id + '&redirect_uri=oob&scope=user:base,file:all:read,file:all:write&code_challenge=' + openapicode_verifier + '&code_challenge_method=plain';
xhr.open('POST', url + params, false);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('authorization', 'Bearer ' + accesstk);
xhr.send(JSON.stringify(data));
if (xhr.status === 200) {
const codeurl = JSON.parse(xhr.responseText).redirectUri
return codeurl.split('=')[1]
} else {
return (xhr.statusText);
}
}
//获取阿里云盘开放平台token
function getopenapitoken() {
const nowtime = new Date().getTime();
const openapitoken = GM_getValue('openapitoken') || {}
if (openapitoken?.access_token) {
if (openapitoken.createdate + openapitoken.expires_in > nowtime) {
return openapitoken.access_token
}
}
const openapicode = getopenapicode()
var xhr = new oldxhr();
var url = 'https://openapi.aliyundrive.com/oauth/access_token';
var data = {
client_id: openapiclient_id,
grant_type: 'authorization_code',
code: openapicode,
code_verifier: openapicode_verifier
};
xhr.open('POST', url, false);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(data));
if (xhr.status === 200) {
const tokejson = JSON.parse(xhr.responseText)
tokejson.createdate = nowtime
GM_setValue('openapitoken', tokejson)
return tokejson.access_token;
} else {
return (xhr.statusText);
}
}
//#endregion
//#region 功能函数
//键盘快捷键
document.addEventListener('keydown', function (e) {
const videoElement = document.querySelector('video')
if (videoElement) {
if (e.key === 'Home' || e.key === 'End') {
var direction = event.key === 'Home' ? -1 : 1;
changevideo(direction)
}
else if (e.key === 'Enter') {
document.querySelector('.action--HeWYA:not([data-active])').click()
}
else if (e && e.keyCode === 37) {
videoElement.currentTime -= 5;
}
else if (e && e.keyCode === 39) {
videoElement.currentTime += 5;
}
return false
}
if (e.altKey && e.code == 'KeyS') {
let savedevice_id = ''
if (location.pathname.startsWith('/drive/file/resource')) {
savedevice_id = device_list.resource_drive_id
}
else if (this.location.pathname.startsWith('/drive/file/backup')) {
savedevice_id = device_list.backup_drive_id
}
if (savedevice_id) {
const savefile_id = this.location.pathname.split('/')[4] || 'root'
if (confirm("确定设置此目录为临时转存目录"))
GM_setValue("saveinfo", { savedevice_id: savedevice_id, savefile_id: savefile_id })
}
}
else if (e.altKey && e.code == 'KeyD') {
var userInput = prompt("输入阿里云盘开放平台的client_id");
if (userInput !== null) {
GM_setValue('openapiclient_id', userInput)
openapiclient_id = userInput;
GM_setValue('openapitoken', {})
}
}
});
//自定义样式
(function () {
var style = document.createElement('style');
style.innerHTML = `
:fullscreen .video-player--k1J-M {bottom: 0px;opacity:0!important;}
.video-player--k1J-M {bottom: -80px;opacity:0.8!important;}
.text-primary--3DHOJ{overflow:visible;font-weight:bold}
.loader--3P7-4,.loader--zXBWG{opacity:0.8!important}
.outer-wrapper--3ViSy{opacity:0!important}
.outer-wrapper--3ViSy:hover,.video-player--k1J-M:hover{opacity:0.8!important}
.button--1pH7M,.container--CIvrv{display:none}
.ended-container--Tz5lR,.content-wrapper--A93tB,.feature-blocker--vh7jp,.sign-bar--1XrSl,.ai-summary-btn--fQnJ{display:none!important}
`;
document.head.appendChild(style);
})()
//获取或设置最高画质m3u8链接
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;
GM_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 {//隔天清空缓存
GM_setValue('newurls', {})
return undefined;
}
}
}
//单双击功能
function clickanddbclick() {
const video = document.querySelector("video");
let timer = 0;
let delay = 200;
video.addEventListener('click', function () {
timer && clearTimeout(timer);
timer = setTimeout(function () {
document.querySelector('.video-player--k1J-M .btn--UrTVT').click()
}, delay);
});
video.addEventListener('dblclick', function () {
timer && clearTimeout(timer);
document.querySelector('.action--HeWYA:not([data-active])').click()
});
}
//全屏以及自动播放
function setfullscreen() {
if (isfullscreen && document.fullscreenElement === null) {
document.querySelector('.action--HeWYA:not([data-active])').click()
}
document.querySelector('.video-player--k1J-M .btn--UrTVT').click()
var elements = document.querySelectorAll('.list--5o17x li, .next--k9RTS');
elements.forEach(function (element) {
element.addEventListener('click', function () {
isfullscreen = document.fullscreenElement !== null
});
});
function videoEndHandler() {
changevideo(1)
}
const video = document.querySelector("video");
video.removeEventListener('ended', videoEndHandler, false);
video.addEventListener('ended', videoEndHandler, false);
}
//更多播放倍率
function morerate() {
let playbackRate = GM_getValue("playbackRate") || 1;
let video = document.querySelector("video");
if (video) {
video.onplay = function () {
video.playbackRate = playbackRate;
addhistory();
};
}
const ul = document.querySelector('div[class^="drawer-list-grid"]');
if (!ul || !video) {
return;
}
else if (ul.childNodes.length > 7) {//确保只执行一次
return
}
adddownloadbut();
setfullscreen();
clickanddbclick();
const close = ul.parentElement.parentElement.previousElementSibling.children.item(1);
let firstChild = [...ul.children].find(
(el) => el.firstChild.textContent === "1.5 倍"
);
let secendChild = [...ul.children].find(
(el) => el.firstChild.textContent === "0.75 倍"
);
const originChild = firstChild;
const rates = ["12", "10", "8", "6", "5", "4", "3", "2.5", "2", "0.5", "0.25", "0.1"];
rates.forEach((rate) => {
if (parseInt(rate) < 2) {
const cloneNode = secendChild.cloneNode(true);
cloneNode.firstChild.innerHTML = `${rate} 倍`;
ul.insertBefore(cloneNode, secendChild);
secendChild = cloneNode;
}
else {
const cloneNode = firstChild.cloneNode(true);
cloneNode.firstChild.innerHTML = `${rate} 倍`;
ul.insertBefore(cloneNode, firstChild);
firstChild = cloneNode;
}
});
ul.insertBefore(originChild, firstChild);
const backRateNodes = [...ul.children];
const changeSelectColor = (select) => {
setTimeout(() => {
backRateNodes.forEach((item) => {
item.dataset.isCurrent = "false";
});
if (!select.dataset.isCurrent) {
select.parentElement.dataset.isCurrent = "true";
} else {
select.dataset.isCurrent = "true";
}
});
};
const currentTarget = [...ul.children].find(
(el) => el.firstChild.textContent === `${playbackRate} 倍`
);
changeSelectColor(currentTarget);
const tagNodes = [...ul.children];
for (let i = 0; i < 3; i++) {
const node = tagNodes[tagNodes.length - 1 - i];
if (node && node.parentNode) {
node.parentNode.removeChild(node);
}
}
ul.addEventListener("click", (e) => {
const target = e.target;
playbackRate = target.textContent.replace(" 倍", "");
video.playbackRate = playbackRate;
GM_setValue("playbackRate", playbackRate);
changeSelectColor(target);
close.click();
});
}
//添加下载按钮
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]
}
GM_setValue('newurls', newurls)
}
dl.href = sdlurl
dl.download = ''
dl.click();
})
}
//生成guid
function generateUUID(length, radix) {
var d = new Date().getTime();
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
});
length && (uuid = uuid.substring(0, length));
radix && (uuid = uuid.replaceAll(radix, ''));
return uuid
}
//切换上下集体
function changevideo(direction) {
time && clearTimeout(time)
time = setTimeout(() => {
var listItems = Array.from(document.querySelectorAll('.list--5o17x li'));
var currentIndex = listItems.findIndex(li => li.getAttribute('data-is-current') === 'true');
if ((direction === -1 && currentIndex > 0) || (direction === 1 && currentIndex < listItems.length - 1)) {
isfullscreen = document.fullscreenElement !== null
listItems[currentIndex + direction]?.click();
}
}, 111)
}
//添加更新播放记录或切换选集
function addhistory() {
if (localStorage.getItem('last_play_file_name')) {
document.querySelector('.title--RYadk[title="' + localStorage.getItem('last_play_file_name').split('>').pop() + '"]').click()
localStorage.removeItem('last_play_file_name')
}
else {
const sss = new URLSearchParams(newurl)
const file_id = sss.get('f')
const playhistory = GM_getValue('playhistory') || []
const file_path = location.pathname
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].outerHTML
}
var listItems = Array.from(document.querySelectorAll('.list--5o17x li'));
var currentIndex = listItems.findIndex(li => li.getAttribute('data-is-current') === 'true');
file_name += listItems[currentIndex].querySelector('.title--RYadk').textContent
const playinfo = { file_id: file_id, file_name: file_name, file_path: file_path }
const index = playhistory.findIndex(item => item.file_path === file_path)
if (index === -1) {
playhistory.unshift(playinfo)
} else {
playhistory[index] = playinfo
}
GM_setValue('playhistory', playhistory.slice(0, 10))
}
}
// 页面完全加载完成执行
unsafeWindow.onload = addplayhistory
//添加显示播放记录的悬浮div
function addplayhistory() {
const colorsgv = '#637dff'
let 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>'
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('multiple-accounts-panel');
if (previousPanel) {
previousPanel.parentNode.removeChild(previousPanel);
}
var playlist = GM_getValue('playhistory') || [];
// 创建弹出元素
var panel = document.createElement('div');
panel.id = 'multiple-accounts-panel';
panel.style.position = 'fixed';
panel.style.top = '0';
panel.style.left = '0';
panel.style.width = '100%';
panel.style.height = '100%';
panel.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
panel.style.zIndex = '9999';
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;">
<h3 style="margin-top: 0; margin-bottom: 20px; font-size: 24px;">历史播放记录</h3>
<div style="display: flex; justify-content: space-between; margin-bottom: 10px; font-size: 18px;">
<div style="flex: 1; font-weight: bold;">名称</div>
<div style="flex: 1; display: flex; justify-content: flex-end; font-weight: bold;margin-right: 38px;">操作</div>
</div>
<div style="overflow-y: auto; max-height: 600px;">
${playlist.map(function (playinfo, index) {
return `<div style="display: flex; justify-content: space-between; margin-bottom: 10px;">
<div style="flex: 5;display: ruby;" class="breadcrumb--gnRPG" data-calc="true">${playinfo.file_name}</div>
<div style="flex: 1; display: flex; justify-content: flex-end;">
<button class="play-button" playinfo-index="${index}" style="margin-right: 10px; padding: 5px 10px; border: none; background-color: #007fff; color: #fff; border-radius: 4px; cursor: pointer;">播放</button>
<button class="delete-button" playinfo-index="${index}" style="padding: 5px 10px; border: none; background-color: #ff4d4f; color: #fff; border-radius: 4px; cursor: pointer;">删除</button>
</div>
</div>
`;
}).join('')}
</div>
</div>
`;
document.body.appendChild(panel);
// 绑定事件
panel.addEventListener('click', function (e) {
var playhistory = GM_getValue('playhistory') || [];
const index = e.target.getAttribute('playinfo-index')
if (e.target.classList.contains('play-button')) {
localStorage.setItem('last_play_file_name', playhistory[index].file_name)
location.href = playhistory[index].file_path
}
else if (e.target.classList.contains('delete-button')) {
playhistory.splice(index, 1)
GM_setValue('playhistory', playhistory)
showManagementPanel()
}
else if (e.target == panel) {
panel.parentNode.removeChild(panel);
}
});
}
//#endregion