// ==UserScript==
// @name b站播放列表
// @namespace http://tampermonkey.net/
// @version 1.0.0
// @description 将b站视频播放页面分集视频(目前只支持分P视频)添加到播放列表,实现列表顺序播放、随机播放、单集循环播放(需点击播放列表里面的视频进行播放才会生效),播放列表中支持移除视频,支持拖动排序,多选拖动排序,支持全选、反选,移除选中
// @author 繁星1678
// @match https://www.bilibili.com/video/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=bilibili.com
// @license MIT
// @grant none
// ==/UserScript==
(function() {
'use strict';
let isListVisible = false;
let listContainer;
let isDragging = false;
let offsetX, offsetY;
let videoList = [];//存储videoDict
let allVideoList = []; //存储videoDict的全部item
let videoDict = {}; // 存储视频id和视频元素(element)
let allVideoDict = {}; // 存储全部视频id和视频元素(element)
let playMode = 0; // 0:顺序播放 1:随机播放 2:单集循环
// let myList = []; // 存储用户添加的视频
let i =1 //点击图标添加的索引
let j =1//全部添加索引
let playId
let currentPlayVideoTitle
let playItemIndex = -1;
let previousPlayItemIndex = -1;
let isDarkMode = true;
let videoEl
// let myList
// let listItems
class vedioInfo {
constructor(index,title,videoElement) {
this.index = index;// 视频序号,在播放列表中的位置
this.title = title;//名称如:P4 你好吗
this.videoElement = videoElement;//视频播放元素,点击后可以播放
}
}
// let videoItem = document.querySelector('div.clickitem')||document.querySelector('divvideo-episode-card__info-title');
let targetNode = document.body; // 可以选择更具体的父节点
const observer = new MutationObserver((mutationsList, observer) => {
for (let mutation of mutationsList) {
if (mutation.type === 'childList') {
// 观察到视频条目插入+添加按钮
let videoItem = document.querySelector('div.clickitem')||document.querySelector('divvideo-episode-card__info-title');
// 找到视频页面头部就插入播放列表
let listHead = document.querySelector("#multi_page > div.head-con") ||
document.querySelector(".base-video-sections-v1 .video-sections-head") ||
document.querySelector(".action-list-container .action-list-header");
// 找到视频播放器就设置播放监听器
videoEl = document.querySelector("div.bpx-player-video-wrap > video");
if (videoEl&&listHead&&videoItem) {
// console.log(`找到 video 元素: ${videoEl}`);
// 添加各种事件监听器
videoEl.addEventListener('play', () => {
// console.log('视频开始播放');
});
videoEl.addEventListener('pause', () => {
// console.log('视频暂停,播放索引:'+playItemIndex);
});
videoEl.addEventListener('ended', (event) => {
// console.log('视频播放结束,播放索引:'+playItemIndex);
event.stopImmediatePropagation();
let videoListLength = videoList.length;
if(videoListLength===0){
return
}
// console.log('视频播放结束');
let randomIndex = Math.floor(Math.random() * videoListLength);
restorePreviousPlayItemBackgroundPlayingGifVisible()
switch (playMode) {
case 0: // 顺序播放
// console.log(`顺序播放`);
if(videoListLength===1){
allVideoList[playItemIndex+1].videoElement.click();
videoList[playItemIndex].videoElement.click();
}else{
// 更新播放索引
let NextPlayItemIndex = (playItemIndex + 1) % videoListLength;
if(videoList[NextPlayItemIndex].title===videoList[playItemIndex].title){
if (playItemIndex + 1 < allVideoList.length) {
allVideoList[playItemIndex + 1].videoElement.click();
playItemIndex = NextPlayItemIndex;
videoList[playItemIndex].videoElement.click();
} else if (playItemIndex - 1 >= 0) {
allVideoList[playItemIndex - 1].videoElement.click();
playItemIndex = NextPlayItemIndex;
videoList[playItemIndex].videoElement.click();
}
}else{
playItemIndex = NextPlayItemIndex;
videoList[playItemIndex].videoElement.click();
}
// 更新列表项的背景颜色
// listItems[playItemIndex].style.backgroundColor = 'skyblue';
videoList[playItemIndex].videoElement.click();
}
// setCurrentItemBackgroundAndPlayingGifVisible()
scrollPlayItemIntoView()
break;
case 1: // 随机播放
// console.log(`随机播放`);
// randomIndex = Math.floor(Math.random() * videoListLength);
// randomIndex与playItemIndex相等代表播放同一曲,若播放同一曲,播放完后再点击同一曲videoElement网页并不会重新播放,所以先点击其他videoElement的,再点击回来播放同一曲
if(randomIndex===playItemIndex){
// console.log('if(randomIndex===playItemIndex)随机播放,随机索引是:'+randomIndex);
// console.log('if(randomIndex===playItemIndex)随机播放,播放索引是:'+playItemIndex);
// playItemIndex = randomIndex;
if (allVideoList[0].videoElement != videoList[playItemIndex].videoElement) {
allVideoList[0].videoElement.click();
videoList[playItemIndex].videoElement.click();
} else{
allVideoList[1].videoElement.click();
videoList[playItemIndex].videoElement.click();
}
playItemIndex = randomIndex;
// videoList[playItemIndex].videoElement.click();
}else{
// console.log('else随机播放,随机索引是:'+randomIndex);
// console.log('else随机播放,播放索引是:'+playItemIndex);
// playItemIndex = randomIndex;
videoList[randomIndex].videoElement.click();
playItemIndex = randomIndex;
}
// setCurrentItemBackgroundAndPlayingGifVisible()
scrollPlayItemIntoView()
break;
case 2: // 单集循环
// console.log(`单集循环`);
// 若播放同一曲,播放完后再点击同一曲videoElement网页并不会重新播放,所以先点击其他videoElement的,再点击回来播放同一曲
if (playItemIndex + 1 < allVideoList.length) {
allVideoList[playItemIndex + 1].videoElement.click();
} else if (playItemIndex - 1 >= 0) {
allVideoList[playItemIndex - 1].videoElement.click();
}
videoList[playItemIndex].videoElement.click();
break;
}
setCurrentItemBackgroundAndPlayingGifVisible()
});
createVideoListButton()
createPLaylistPanel()
createAddBtnForVideoItem()
// 停止观察,因为我们已经找到了 video 元素
observer.disconnect();
break;
}
}
}
});
// 配置观察选项
let config = { childList: true, subtree: true };
// 开始观察
observer.observe(targetNode, config);
function getCrruentPlayVideoTitle(){
if(playItemIndex>-1){
currentPlayVideoTitle = videoList[playItemIndex].title;
if(currentPlayVideoTitle){
return currentPlayVideoTitle
}else{return null}
}else{
return null
}
}
// 创建播放列表按钮,点击弹出播放列表面板,再点击关闭播放列表面板
function createVideoListButton() {
// 创建播放列表按钮
const videoListButton = document.createElement('button');
videoListButton.textContent = '播放列表';
videoListButton.className = 'video-list-btn';
// 添加按钮样式
const style = document.createElement('style');
style.textContent = `
.video-list-btn {
background-color: #3e4149;
color: #defcf9;
border: none;
border-radius: 4px;
text-align: center;
font-size: 15px;
cursor: pointer;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.15);
transition: background-color 0.3s;
}
.video-list-btn:hover {
background-color: #521262;
}
.video-list-btn:active {
background-color: #6639a6;
}
.draggable {
cursor: move;
}
`;
document.head.appendChild(style);
getListHead()
// 添加按钮点击事件
videoListButton.addEventListener('click', () => {
isListVisible = !isListVisible;
listContainer.style.display = isListVisible ? 'block' : 'none';
videoListButton.textContent = isListVisible ? '关闭列表' : '播放列表';
});
function getListHead() {
let listHead = document.querySelector("#multi_page > div.head-con") ||
document.querySelector(".base-video-sections-v1 .video-sections-head") ||
document.querySelector(".action-list-container .action-list-header");
if (listHead) {
listHead.style.position = 'relative';
listHead.insertBefore(videoListButton, listHead.firstChild);
}
}
}
// 停止播放
function stopVideo(){
let videoEl = document.querySelector("div.bpx-player-video-wrap > video") // 获取视频元素
videoEl.pause();
videoEl.currentTime = 0;
}
// addItemToPLaylistPanel
// 如果列表项有选中状态的项就返回true,否则返回false
function hasCheckedCheckbox() {
const checkboxes = document.querySelectorAll('li.list-item input[type="checkbox"]');
for (let checkbox of checkboxes) {
if (checkbox.checked) {
return true;
}
}
return false;
}
// 将上一个播放项的背景色还原为透明浅蓝色,并将playing-gif隐藏
function restorePreviousPlayItemBackgroundPlayingGifVisible(){
let myList = document.getElementById('myList');
let listItems = myList.getElementsByTagName('li');
let previousPlayItem = listItems[playItemIndex]
if(isDarkMode){
previousPlayItem?previousPlayItem.style.color='#ba52ed':console.log("没有找到播放项");
}else{
previousPlayItem?previousPlayItem.style.color='#311d3f':console.log("没有找到播放项");
}
// previousPlayItem?previousPlayItem.style.color='#ba52ed':console.log("没有上一个播放项");
// previousPlayItem?previousPlayItem.style.backgroundColor = 'rgba(0, 0, 0, 0.8)':console.log("没有上一个播放项");
previousPlayItem?previousPlayItem.querySelector('.playing-gif').style.display = 'none':console.log("没有上一个播放项");
}
// 设置当前播放项动图playing-gif为显示状态 设置item背景色
function setCurrentItemBackgroundAndPlayingGifVisible() {
// let textNode = document.querySelector('.video-name')
let myList = document.getElementById('myList');
let listItems = myList.getElementsByTagName('li');
let CurrentPlayItem = listItems[playItemIndex]
// if(isDarkMode){
// CurrentPlayItem?CurrentPlayItem.style.color='#a3de83':console.log("没有找到播放项");
// }else{
// CurrentPlayItem?CurrentPlayItem.style.color='#071a52':console.log("没有找到播放项");
// }
CurrentPlayItem?CurrentPlayItem.style.color='#ff9a00':console.log("没有找到播放项");
// CurrentPlayItem?CurrentPlayItem.style.backgroundColor = 'rgba(0, 0, 0, 0.8)':console.log("没有找到播放项");
CurrentPlayItem?CurrentPlayItem.querySelector('.playing-gif').style.display = 'inline':console.log("没有找到播放项");
}
// 将播放项滚动到视图中
function scrollPlayItemIntoView() {
let myList = document.getElementById('myList');
let listItems = myList.getElementsByTagName('li');
let CurrentPlayItem = listItems[playItemIndex]
CurrentPlayItem.scrollIntoView({ behavior: 'smooth', block: 'center' });
}
// 创建播放列表,添加列表项,相关按钮,播放列表项点击事件,播放列表项拖拽排序事件
// 获取选中的列表项,将其文本组成一个数组,将其文本组成一个数组
function getCheckedItemsTextAndIndex() {
// 获取所有被勾选的 checkbox
const checkboxes = document.querySelectorAll('input[type="checkbox"]:checked');
// 获取所有 li 元素
const listItems = document.getElementById('myList').getElementsByTagName('li');
// 创建两个数组来存储文本和索引
let texts = [];
let indices = [];
// 遍历被勾选的 checkbox,获取对应的 li 元素的文本和索引
checkboxes.forEach(checkbox => {
const parentLi = checkbox.closest('li');
if (parentLi) {
const index = Array.from(listItems).indexOf(parentLi);
texts.push(parentLi.textContent.trim());
indices.push(index);
}
});
// 根据索引从小到大排序
indices.sort((a, b) => a - b);
texts.sort((a, b) => indices[texts.indexOf(a)] - indices[texts.indexOf(b)]);
// console.log('文本数组:', texts);
// console.log('索引数组:', indices);
return {texts, indices};
}
// 切换主题
function toggleTheme() {
const elements = document.querySelectorAll('.list-item');
// 遍历所有找到的元素
if (isDarkMode) {
// 替换类名 "dark-mode-list-group-item" 为 "list-group-item"
elements.forEach((element, index) => {
element.classList.replace('dark-mode-list-group-item', 'list-group-item');
// if (playItemIndex > -1 && playItemIndex === index) {
// element.style.color = '#a3de83';
// }
});
event.target.textContent = "透明";
} else {
// 替换类名 "list-group-item" 为 "dark-mode-list-group-item"
elements.forEach((element, index) => {
element.classList.replace('list-group-item', 'dark-mode-list-group-item');
// if (playItemIndex > -1 && playItemIndex === index) {
// element.style.color = '#005691';
// }
});
event.target.textContent = "暗黑";
}
}
// 点击+向播放列表面板添加列表项,同时提取所有li的视频名称 索引号 li元素存放在数组中
function addItemToPLaylistPanel(itemText) {
// <img src="//i0.hdslb.com/bfs/static/jinkela/video/asserts/playing.gif" style=""></img>
// 创建播放动图<img> 元素并设置其属性
const imgElement = document.createElement('img');
imgElement.src = "//i0.hdslb.com/bfs/static/jinkela/video/asserts/playing.gif";
imgElement.className = 'playing-gif';
imgElement.style.cssText = "color:#ff7c38;display: none; height: 17px; width: 17px; margin-right: 7px; vertical-align: middle;";
const newItem = document.createElement('li');
newItem.classList.add('list-item','dark-mode-list-group-item');
// newItem.className = 'dark-mode-list-group-item';
newItem.draggable = true;
// newItem.textContent = `${itemText}`;
// newItem.style.backgroundColor = 'rgba(0, 0, 0, 0.8)'
newItem.title = '点击播放,拖动排序,多选拖动排序';
// newItem.style.color = '#ba52ed'
// 创建一个文本节点
const textNode = document.createTextNode(itemText);
// textNode.className = 'video-name';
textNode.textContent = `${itemText}`
// 将 img 元素和文本节点添加到 newItem 中
newItem.appendChild(imgElement);
newItem.appendChild(textNode);
// 创建一个包含 SVG 图标和多选框的容器
const iconContainer = document.createElement('span');
iconContainer.style.float = 'right'; // 将容器浮动到右边
// iconContainer.style.display = 'inline-block';
iconContainer.style.alignItems = 'center'; // 垂直居中对齐
// 创建 SVG 图标,移除播放列表条目
const svgIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svgIcon.setAttribute('width', '24');
svgIcon.setAttribute('height', '24');
svgIcon.setAttribute('viewBox', '0 0 12 12');
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path.setAttribute('fill', '#ba52ed');
path.setAttribute('d', 'M5 3h2a1 1 0 0 0-2 0M4 3a2 2 0 1 1 4 0h2.5a.5.5 0 0 1 0 1h-.441l-.443 5.17A2 2 0 0 1 7.623 11H4.377a2 2 0 0 1-1.993-1.83L1.941 4H1.5a.5.5 0 0 1 0-1zm3.5 3a.5.5 0 0 0-1 0v2a.5.5 0 0 0 1 0zM5 5.5a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5M3.38 9.085a1 1 0 0 0 .997.915h3.246a1 1 0 0 0 .996-.915L9.055 4h-6.11z');
svgIcon.appendChild(path);
// 设置 SVG 图标的点击事件监听器
svgIcon.addEventListener('click', () => {
// 获取 SVG 图标的父元素(即包含它的 <li> 元素)
const parentLi = svgIcon.closest('li');
if (parentLi) {
// 获取被移除的 <li> 元素的索引
const listItems = document.getElementById('myList').getElementsByTagName('li');
const index = Array.from(listItems).indexOf(parentLi);
// console.log(`被移除的 <li> 元素的索引: ${index}`);
// 从 DOM 中移除 <li> 元素
parentLi.remove();
videoList.splice(index, 1);
// 当删除播放列表面板列表项时需要更新playItemIndex,才能正确播放对应列表项
if (index < playItemIndex) {
playItemIndex--;
} else if (index === playItemIndex) {
// 如果当前播放的列表项被删除,则停止播放
stopVideo();
}
}
let selectAllItemBtn = document.querySelector("#selectAllItem");
hasCheckedCheckbox()?selectAllItemBtn.textContent="反选":selectAllItemBtn.textContent="全选"
});
// 创建多选框
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.style.height = "20px";
checkbox.style.width = "18px";
checkbox.style.color = '#f5c7f7';
checkbox.style.marginLeft = '8px'; // 添加一些间距
// 将 SVG 图标和多选框添加到容器中
iconContainer.appendChild(svgIcon);
iconContainer.appendChild(checkbox);
// 添加 change 事件监听器
checkbox.addEventListener('change', (event) => {
let selectAllItemBtn = document.querySelector("#selectAllItem");
hasCheckedCheckbox()?selectAllItemBtn.textContent="反选":selectAllItemBtn.textContent="全选"
// console.log('复选框状态发生变化:', event.target.checked);
});
// 添加 click 事件监听器
checkbox.addEventListener('click', (event) => {
let selectAllItemBtn = document.querySelector("#selectAllItem");
hasCheckedCheckbox()?selectAllItemBtn.textContent="反选":selectAllItemBtn.textContent="全选"
// console.log('复选框被点击:', event.target.checked);
});
// 将容器添加到 <li> 元素中
newItem.appendChild(iconContainer);
document.getElementById('myList').appendChild(newItem);
}
// 创建播放列表面板
function createPLaylistPanel() {
// 创建一个带有样式的列表容器
listContainer = document.createElement('div');
// listContainer.style.width = '500px';
// listContainer.style.minWidth = '470px';
listContainer.style.width = 'fit-content';
listContainer.style.height = '600px';
listContainer.style.left = '700px';
listContainer.style.top = '200px';
listContainer.style.zIndex = '999999';
listContainer.style.overflowY = 'scroll';
// listContainer.style.overflowX = 'hidden';
listContainer.style.alignItems = 'center';
listContainer.style.backgroundColor = 'rgba(255, 255, 255, 0)';
listContainer.style.borderRadius = '6px';
listContainer.style.display = 'none'; // 初始隐藏
listContainer.style.position = 'fixed'; // 自适应固定在可见位置
// listContainer.style.flexDirection = 'column';
listContainer.style.boxShadow = '0 8px 5px rgba(0, 0, 0, 0.5)'; // 添加边框阴影
listContainer.className = 'container mt-5 draggable'; // 添加容器类和可拖动类
listContainer.innerHTML = `
<div style="position: sticky; top: 0; background-color: rgba(0, 0, 0, 0);margin-bottom: 10px;justify-content: space-between;align-items: center;">
<button type="button" class="btn btn-primary" id="closePlaylist">关闭列表</button>
<button type="button" class="btn btn-primary" id="playMode">顺序播放</button>
<button type="button" class="btn btn-primary" id="removeItem">移除选中</button>
<button type="button" class="btn btn-primary" id="selectAllItem">全选</button>
<button type="button" class="btn btn-primary" id="addAllItem">全部添加</button>
<button type="button" class="btn btn-primary" id="toggleStyleBtn">暗黑</button>
<span class="text-tip">点击视频左边+号添加</span>
</div>
<ul class="list-group myList" id="myList">
</ul>
`;
// 添加 CSS 样式
const style = document.createElement('style');
style.textContent = `
.list-group myList{
margin-top: 10px;
}
.text-tip {
margin-right: 8px;
font-size: 16px;
color: skyblue;
}
.btn.btn-primary{
cursor: pointer; /* 设置鼠标指针为小手 */
border-radius: 5px;
margin-right: 6px;
height: 30px;
text-align: center;
font-size: 14px;
color: #c5e3f6; /* 设置字体颜色 */
background-color: #48466d; /* 设置背景颜色 */
}
/* 基本样式 */
ul {
list-style-type: none;
padding: 0;
}
.list-group-item {
cursor: pointer; /* 设置鼠标指针为小手 */
margin-bottom: 6px; /* 设置li的行距 */
background-color: rgba(186, 82, 237, 0.26);
font-size: 16px; /* 设置字体大小 */
border-radius: 6px; /* 圆角边框 */
border: 1.4px solid #a82ffc;
color:#311d3f;
padding: 5px;
margin: 5px;
transition: box-shadow 0.3s ease;
}
.dark-mode-list-group-item {
background-color: rgba(0, 0, 0, 0.8);
cursor: pointer; /* 设置鼠标指针为小手 */
margin-bottom: 6px; /* 设置li的行距 */
font-size: 16px; /* 设置字体大小 */
border-radius: 6px; /* 圆角边框 */
border: 1.4px solid #a82ffc;
color: #ba52ed; /* 设置字体颜色 */
padding: 5px;
margin: 5px;
transition: box-shadow 0.3s ease;
}
/* 鼠标悬停时的阴影效果 */
.list-item:hover {
box-shadow: 5px 5px 10px rgba(255, 201, 60, 0.68);
}
`;
document.head.appendChild(style);
let currentSelectedItem = null; // 用于跟踪当前被选中的 <li> 元素
let isSelctedAll = false; // 用于跟踪是否全选
// 为item创建拖动排序
//////////////////////////////////
// 在 listContainer 上添加拖动事件监听器
let isDraggingListContainer = false;
let offsetX = 0;
let offsetY = 0;
// 在 listContainer 上添加拖动事件监听器
listContainer.addEventListener('dragstart', dragStart);
listContainer.addEventListener('dragover', dragOver);
listContainer.addEventListener('drop', drop);
listContainer.addEventListener('dragenter', dragEnter);
listContainer.addEventListener('dragleave', dragLeave);
listContainer.addEventListener('dragend', dragEnd);
let dragSrcEl;
let LiTextsList
let LiIndexList
let checkedItemNum
let dropIndex
let dropText
let ul
let moveItemList
let moveItem
function dragStart(e) {
if (e.target.tagName === 'LI') {
dragSrcEl = []; // 将 dragSrcEl 改为数组
LiTextsList = [];
LiIndexList = [];
checkedItemNum = 0;
const result = getCheckedItemsTextAndIndex();
LiTextsList = result.texts;
LiIndexList = result.indices;
checkedItemNum = LiTextsList.length;
// console.log('被拖动的项目数量' + checkedItemNum);
// console.log('被拖动的项目索引' + LiIndexList);
if (checkedItemNum) {
let combinedHtml = '';
for (let i = 0; i < checkedItemNum; i++) {
let videoListIndex = LiIndexList[i];
let selectedLi = document.querySelector(`#myList li:nth-child(${videoListIndex})`);
// selectedLi.style.backgroundColor = '#87ceeb'; // 修改背景色
dragSrcEl.push(selectedLi); // 将选中的 li 元素添加到 dragSrcEl 数组中
combinedHtml += selectedLi.outerHTML; // 将每个选中的 li 元素的 HTML 内容添加到 combinedHtml 中
}
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', combinedHtml); // 将合并后的 HTML 内容设置为数据传输对象的数据
dragSrcEl.forEach(element => {
element.style.opacity = '1';
});
}else{
dragSrcEl = e.target;
dragSrcEl.style.opacity = '1';
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', e.target.innerHTML);
}
}
}
function dragEnter(e) {
if (e.target.tagName === 'LI') {
e.target.classList.add('over');
}
}
function dragLeave(e) {
if (e.target.tagName === 'LI') {
e.target.classList.remove('over');
}
}
function dragOver(e) {
if (e.preventDefault) {
e.preventDefault();
}
e.dataTransfer.dropEffect = 'move';
return false;
}
function drop(e) {
if (e.stopPropagation) {
e.stopPropagation();
}
// if (dragSrcEl !== e.target && e.target.tagName === 'LI') {
// 获取 ul 元素
ul = e.target.parentNode;
// 获取所有的 li 元素
const listItems = ul.getElementsByTagName('li');
// 找到拖动元素和目标元素的索引
const dragIndex = Array.from(listItems).indexOf(dragSrcEl);
// console.log("拖动元素的索引: " + dragIndex);
// ul = e.target.parentNode;
// 获取所有的 li 元素
// const listItems = ul.getElementsByTagName('li');
dropIndex = Array.from(listItems).indexOf(e.target);
// console.log("目标元素的索引: " + dropIndex);
// console.log("目标元素的索引: " + dropIndex);
dropText = videoList[dropIndex].title;// 获取目标元素的文本
// console.log('目标元素文本: ' + dropText);
ul.innerHTML = '';
// // 交换 videoList 中的元素位置
moveItemList = []
let moveItem = null;
// // ///////////////////
if(checkedItemNum>0){
currentPlayVideoTitle = getCrruentPlayVideoTitle()
for(let i=0;i<checkedItemNum;i++){
let videoListIndex = LiIndexList[i]
// console.log('被拖动的项目索引'+videoListIndex)
// let moveItem = videoList.splice(videoListIndex,1)[0]
moveItem = videoList[videoListIndex];
// videoList.splice(videoListIndex, 1);
// console.log('被拖动的项目'+moveItem)
// console.log('被拖动的项目标题'+moveItem.title)
moveItemList.push(moveItem)
}
LiIndexList.sort((a, b) => b - a);//数组元素从大到小排序
// /*目标项目的位置在原来数组中的索引是dropIndex,判断被拖动的项目在原数组最小索引的元素是否在目标元素之前,
// 如果是,是从上往下拖,拖到目标项目之后,则拖动后的位置是新数组中目标元素索引+1处即insertIndex+1,
// 如果被拖动项目在原数组中的索引大于目标项目在原数组中的索引dropIndex若不等于0,即被拖动元素在目标元素之后,是从下往上拖,
// ,拖动目标项目之前,则拖动后的位置是目标元素在新数组中的索引-1,即insertIndex-1(dropIndex若不等于0,等于0 insertIndex为0)
// 原数组目标项目索引:dropIndex
// 原数组拖动项目最小索引:LiIndexList[LiIndexList.length-1]
// */
// // console.log('被拖动的项目索引列表中最大值: ' + LiIndexList[0])
// // console.log('被拖动的项目索引列表中最小值: ' + LiIndexList[LiIndexList.length-1])
LiIndexList.forEach((index) => {
videoList.splice(index, 1);
})
// // 根据目标元素文本求出其在被删减后的videoList中的索引,再根据索引进行插入
// // 从上往下拖,是插入到insertIndex+1位置,从下往上拖,是插入到insertIndex位置
setTimeout(() => {
let insertIndex = videoList.findIndex(video => video.title === dropText);
// console.log('新数组目标项目索引是:'+insertIndex)
if (LiIndexList[LiIndexList.length-1] < dropIndex) {
// 从上往下拖动,是插入到insertIndex+1位置
insertIndex=insertIndex+1
videoList.splice(insertIndex, 0, ...moveItemList);
}else if(LiIndexList[LiIndexList.length-1] > dropIndex){
// 从下往上拖动,是插入到insertIndex-1位置
videoList.splice(insertIndex, 0, ...moveItemList);
}
videoList.forEach((item) => {
addItemToPLaylistPanel(item.title);
})
playItemIndex = videoList.findIndex(item => item.title === currentPlayVideoTitle);
if(playItemIndex>-1){
setCurrentItemBackgroundAndPlayingGifVisible()
}else{console.log('没有播放视频')}
// playItemIndex?setCurrentItemBackgroundAndPlayingGifVisible():console.log('没有播放视频');
// setCurrentItemBackgroundAndPlayingGifVisible()
// console.log('拖动结束后的videoList.length: ', videoList.length);
},5)
}else{
currentPlayVideoTitle = getCrruentPlayVideoTitle()
let dragItem = videoList.splice(dragIndex, 1)[0];//将拖动项从列表中删除
if(dragIndex<dropIndex){
let insertIndex = videoList.findIndex(video => video.title === dropText)+1;
// let insertIndex = videoList.findIndex(video => video.title === dropText);
videoList.splice(insertIndex, 0, dragItem);//将拖动项添加到目标位置,对应的是播放面板上面拖到的位置
// console.log('从上往下拖动目标元素索引dropIndex是dragIndex:'+dropIndex);
// console.log('从上往下拖动目标元素索引dropIndex是:'+dropIndex);
}else if(dragIndex>dropIndex){
// let dragItem = videoList.splice(dragIndex, 1)[0];//将拖动项从列表中删除
console.log('从下往上拖动目标元素的索引dropIndex是:'+dropIndex);
let insertIndex = videoList.findIndex(video => video.title === dropText);
videoList.splice(insertIndex, 0, dragItem);//将拖动项添加到目标位置,对应的是播放面板上面拖到的位置
}
videoList.forEach((item) => {
addItemToPLaylistPanel(item.title);
})
if(currentPlayVideoTitle){
playItemIndex = videoList.findIndex(item => item.title === currentPlayVideoTitle);
// playItemIndex?setCurrentItemBackgroundAndPlayingGifVisible():console.log('没有播放视频');
if(playItemIndex>-1){
setCurrentItemBackgroundAndPlayingGifVisible()
}else{console.log('没有播放视频')}
}
}
return false;
}
function dragEnd(e) {
if (e.target.tagName === 'LI') {
e.target.classList.remove('over');
}
}
////////////////////////////////////////////
// 拖动 listContainer 的事件处理
listContainer.addEventListener('mousedown', (event) => {
if (!event.target.matches('li')) {
isDraggingListContainer = true;
offsetX = event.clientX - listContainer.offsetLeft;
offsetY = event.clientY - listContainer.offsetTop;
}
});
document.addEventListener('mousemove', (event) => {
if (isDraggingListContainer) {
listContainer.style.left = `${event.clientX - offsetX}px`;
listContainer.style.top = `${event.clientY - offsetY}px`;
}
});
document.addEventListener('mouseup', () => {
isDraggingListContainer = false;
});
//////////////////////////////////
// 为列表项添加点击事件监听器,点击歌曲条目播放
listContainer.addEventListener('click', (event) => {
// 如果点击播放列表项
if (event.target.matches('.list-item')) {
restorePreviousPlayItemBackgroundPlayingGifVisible()
currentSelectedItem = event.target; // 记录当前被选中的 <li> 元素
// currentSelectedItem.style.backgroundColor = 'skyblue'; // 设置被选中元素的背景颜色
// // 获取 ul 元素
let myList = document.getElementById('myList');
// // 获取所有的 li 元素
let listItems = myList.getElementsByTagName('li');
// 获取被点击的 li 元素的索引
let clickedIndex = Array.from(listItems).indexOf(event.target);
// console.log('点击的 li 元素的索引:', clickedIndex);
// 点击列表项时提取 PNumber
let textContent = currentSelectedItem.textContent.trim();
// 点击列表项时获取列表项的索引作为播放列表项索引
// playItemIndex = Number(textContent.split('.')[0]) - 1;
playItemIndex = clickedIndex;
// console.log('点击列表项playItemIndex:' + playItemIndex);
// let match = textContent.match(/P(\d+)/);
videoList[playItemIndex].videoElement.click();
setCurrentItemBackgroundAndPlayingGifVisible()
videoEl.play()
// 如果点击了关闭播放列表按钮.video-list-btn
} else if (event.target.matches('#closePlaylist')) {
let videoListButton = document.querySelector('.video-list-btn')
videoListButton.click()
// alert(`点击了: ${event.target.textContent}`);
// listContainer.style.display = 'none'; // 隐藏播放列表
// videoListButton.textContent = '播放列表';
// 如果点击了播放模式按钮
} else if (event.target.matches('#playMode')) {
playMode = (playMode + 1) % 3;
// console.log("播放模式按钮被点击,当前playMode是:"+playMode);
if (playMode === 0) {
event.target.textContent = "顺序播放";
// alert("已开启顺序播放,playMode: "+playMode);
} else if (playMode === 1) {
event.target.textContent = "随机播放";
// alert("已开启随机播放,playMode: "+playMode);
} else if (playMode === 2) {
event.target.textContent = "单集循环";
// alert("已开启单集循环,playMode: "+playMode);
}
// alert(`点击了: ${event.target.textContent}`);
// 如果点击了全选按钮
} else if (event.target.matches('#selectAllItem')) {
isSelctedAll = !isSelctedAll;
const checkboxes = listContainer.querySelectorAll('input[type="checkbox"]');
checkboxes.forEach(checkbox => {
checkbox.checked = !checkbox.checked; // 反转多选框的选中状态
});
let selectAllItemBtn = document.querySelector("#selectAllItem");
hasCheckedCheckbox()?selectAllItemBtn.textContent="反选":selectAllItemBtn.textContent="全选"
// 如果点击移除选中按钮
} else if (event.target.matches('#removeItem')) {
// selectAllItemBtn.textContent="全选"
// 移除所有选中状态的多选框对应的 <li> 元素
const checkboxes = listContainer.querySelectorAll('input[type="checkbox"]:checked');
const listItems = document.getElementById('myList').getElementsByTagName('li');
const indicesToRemove = [];
checkboxes.forEach(checkbox => {
const parentLi = checkbox.closest('li');
if (parentLi) {
// 获取被移除的 <li> 元素的索引
const index = Array.from(listItems).indexOf(parentLi);
indicesToRemove.push(index);
}
});
// 按索引从大到小排序,确保移除时索引不会变化
indicesToRemove.sort((a, b) => b - a);
for (let index of indicesToRemove) {
videoList.splice(index, 1);
// 当删除播放列表面板列表项时需要更新playItemIndex,才能正确播放对应列表项
if (index < playItemIndex) {
playItemIndex--;
} else if (index === playItemIndex) {
// 如果当前播放的列表项被删除,则停止播放
stopVideo();
}
}
indicesToRemove.forEach(index => {
// console.log(`被移除的 <li> 元素的索引: ${index}`);
listItems[index].remove();
});
let selectAllItemBtn = document.querySelector("#selectAllItem");
hasCheckedCheckbox()?selectAllItemBtn.textContent="反选":selectAllItemBtn.textContent="全选"
// 如果点击全部添加按钮
}else if(event.target.matches('#addAllItem')){
let selectAllItemBtn = document.querySelector("#selectAllItem");
selectAllItemBtn.textContent="全选"
let videoEl = document.querySelector("div.bpx-player-video-wrap > video")
videoEl.pause();
videoEl.currentTime = 0;
if(allVideoList.length>0){
// 获取ul元素
var ulElement = document.getElementById('myList');
// 移除ul中的所有li元素
while (ulElement.firstChild) {
ulElement.removeChild(ulElement.firstChild);
}
for(let i=0;i<allVideoList.length;i++){
addItemToPLaylistPanel(allVideoList[i].title)
}
// 清空数组
videoList.length = 0;
// 将allVideoList中的元素添加到videoList中
videoList.push(...allVideoList);
}
// 如果点击切换style按钮
}else if (event.target.matches('#toggleStyleBtn')) {
toggleTheme()
isDarkMode = !isDarkMode;
}
});
// 将列表容器插入到 document.body 中
document.body.appendChild(listContainer);
}
// 为视频前面添加+号,点击后可以添加到播放列表
function createAddBtnForVideoItem() {
const ul = document.querySelector('ul.list-box');
if (ul) {
// observer.disconnect();
// 定义SVG图标按钮
const svgIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" style="margin-right: 10px;" title="添加到播放列表"><path fill="#000000" d="M11 13H5v-2h6V5h2v6h6v2h-6v6h-2z"/></svg>`;
// ul.list-box > li > a > div.clickitem
// 遍历ul中的每个li元素
ul.querySelectorAll('li').forEach(li => {
// 获取li中的两个span元素
const spans = li.querySelectorAll('span');
const firstSpanText = spans[0].textContent?spans[0].textContent:'P';
const secondSpanText = spans[1].textContent?spans[1].textContent:'未匹配标题';
let vedioeElement = li.querySelector('a > div.clickitem');
let videoTitle = firstSpanText + ' ' + secondSpanText;
// let allPnumber = videoTitle.match(/P(\d+)/)[1];
// allVideoDict[allPnumber] = vedioeElement;
let allVideoInfo = new vedioInfo(j,videoTitle,vedioeElement);
allVideoList.push(allVideoInfo);
j++
// 获取li中的img元素
const img = li.querySelector('img');
if (img) {
// 在img前面插入SVG图标按钮
img.insertAdjacentHTML('beforebegin', svgIcon);
// 获取新插入的SVG元素
const newSvg = img.previousElementSibling;
// 点击+图标按钮时,添加到播放列表
newSvg.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
///////////
// 创建自定义提示框
const tooltip = document.createElement('div');
tooltip.textContent = `已添加${videoTitle}到播放列表`;
tooltip.style.position = 'fixed';
tooltip.style.top = '100px';
tooltip.style.left = '750px';
tooltip.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
tooltip.style.color = 'skyblue';
tooltip.style.padding = '10px';
tooltip.style.borderRadius = '5px';
tooltip.style.zIndex = '1000';
document.body.appendChild(tooltip);
// 设置定时器,3秒后自动消失
setTimeout(() => {
document.body.removeChild(tooltip);
}, 3000);
///////////
// console.log('点击的li元素:'+li);
// 提取点击项的PNumber
// let Pnumber = videoTitle.match(/P(\d+)/)[1];
// if (Pnumber){
// console.log(`PNumber: ${Pnumber}`);
// }else{
// alert(`78行无法提取PNumber: ${Pnumber}`);
// }
let mvedioInfo = new vedioInfo(i, videoTitle, vedioeElement);
// videoDict[Pnumber]=vedioeElement
videoList.push(mvedioInfo);
// console.log('videoList中元素个数:'+videoList.length);
// 输出字典中所有键值对
// let entries = Object.entries(videoDict);
// console.log(entries);
///////////////////
// alert('你点击了SVG图标按钮!视频标题为:' + videoTitle);
addItemToPLaylistPanel(videoTitle)
// toggleTheme()
// console.log(`当前歌曲列表中元素: ${videoList.join(',')}`);
// return videoTitle;
i++
});
}
});
}
}
/////////////////////////////////
// Your code here...
})();