version: '3'
services:
musicn:
image: ghcr.io/wy580477/musicn-container:latest #官方镜像
image: ghcr-pull.ygxz.in/wy580477/musicn-container:latest #国内加速镜像
container_name: musicn
restart: always
ports:
- "7478:7478" # 网页操作入口
volumes:
- "./data:/data"
entrypoint: ["/sbin/tini", "--", "msc", "-q"] # -q参数启动网页服务
安装完后 打开界面 :7478
然后浏览器安装 油猴脚本
// ==UserScript==
// @name 播放列表控制脚本(带排序功能和自动播放)
// @namespace http://tampermonkey.net/
// @version 1.3
// @description 使用静态数据创建右侧播放列表,支持多种排序,自动播放下一首
// @author You
// @match http://192.168.0.108:7478/
// @grant none
// ==/UserScript==
(function() {
'use strict';
// 配置
const INPUT_SELECTOR = '.ant-input';
const BUTTON_SELECTOR = '.ant-btn.ant-btn-primary.ant-input-search-button';
const LIST_ITEM_SELECTOR = '.ant-list-items li:first-child';
// 静态数据(一行一个)
const STATIC_DATA = `Wild World - Yusuf / Cat Stevens
在冬天和奶奶一起晒太阳 - 赵照
Wonderwall - Oasis
Never Say Never - The Fray`;
// 全局变量
let currentPlaylist = [];
let originalPlaylist = [];
let currentPlayingIndex = -1; // 当前播放的索引
let isAutoPlayNextEnabled = true; // 是否启用自动播放下一首
let progressObserver = null; // 进度观察器
// 创建悬浮面板
function createFloatingPanel() {
const panel = document.createElement('div');
panel.id = 'custom-playlist-panel';
panel.style.cssText = `
position: fixed;
top: 50%;
right: 20px;
transform: translateY(-50%);
width: 320px;
height: 520px;
background: rgba(255, 255, 255, 0.95);
border: 1px solid #d9d9d9;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
z-index: 9999;
overflow: hidden;
display: flex;
flex-direction: column;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
`;
// 标题栏
const header = document.createElement('div');
header.style.cssText = `
padding: 12px 16px;
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
color: white;
font-weight: bold;
font-size: 14px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #40a9ff;
`;
header.innerHTML = `
<span>播放列表 (${currentPlaylist.length})</span>
<div style="display: flex; gap: 8px;">
<button id="order-sort-btn" style="
background: #13c2c2;
border: none;
color: white;
padding: 4px 10px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
transition: all 0.3s;
display: flex;
align-items: center;
gap: 4px;
" title="顺序排序">
<svg viewBox="0 0 1024 1024" width="12" height="12" fill="currentColor">
<path d="M904 160H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8zM904 784H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8zM904 472H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8z"></path>
</svg>
顺序
</button>
<button id="alphabet-sort-btn" style="
background: #fa8c16;
border: none;
color: white;
padding: 4px 10px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
transition: all 0.3s;
display: flex;
align-items: center;
gap: 4px;
" title="首字母排序">
<svg viewBox="0 0 1024 1024" width="12" height="12" fill="currentColor">
<path d="M574 665.4c-9.8 12.6-28.8 12.6-38.6 0L346.2 437.8c-12.5-16.1-1.4-38.8 19.3-38.8h228.9c20.7 0 31.8 22.7 19.3 38.8l-39.7 51.1zM304.2 300h415.6c14.9 0 27-12.1 27-27s-12.1-27-27-27H304.2c-14.9 0-27 12.1-27 27s12.1 27 27 27zM304.2 724h415.6c14.9 0 27-12.1 27-27s-12.1-27-27-27H304.2c-14.9 0-27 12.1-27 27s12.1 27 27 27z"></path>
</svg>
字母
</button>
<button id="random-sort-btn" style="
background: #722ed1;
border: none;
color: white;
padding: 4px 10px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
transition: all 0.3s;
display: flex;
align-items: center;
gap: 4px;
" title="随机排序">
<svg viewBox="0 0 1024 1024" width="12" height="12" fill="currentColor">
<path d="M433.1 657.7c-11.8-5.5-25.8-1.6-31.3 10.2-5.5 11.8-1.6 25.8 10.2 31.3 56.2 26.3 121.4 18.5 169.8-19.4C631 664.1 651 631.7 651 591c0-48.1-22.1-91.4-56.8-120.1-57.5-47.5-139.6-44.8-193.3 6.7-5.3 5.1-13.6 5.1-18.9 0l-39.6-38.4c-5.1-5-5.2-13.3-0.2-18.4 75.3-77.8 195.7-82.4 276.9-10.8C729 398.9 751 453.5 751 512c0 71.8-31.1 136.6-80.6 181.3-69.3 62.1-163.4 78.4-248 47.4zM512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64z m0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path>
</svg>
随机
</button>
<button id="original-sort-btn" style="
background: #52c41a;
border: none;
color: white;
padding: 4px 10px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
transition: all 0.3s;
display: flex;
align-items: center;
gap: 4px;
" title="原始顺序">
<svg viewBox="0 0 1024 1024" width="12" height="12" fill="currentColor">
<path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474c-6.1-7.7-15.3-12.2-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1 0.4-12.8-6.3-12.8z"></path>
</svg>
原始
</button>
</div>
`;
// 控制栏
const controlContainer = document.createElement('div');
controlContainer.style.cssText = `
padding: 8px 16px;
background: #fafafa;
border-bottom: 1px solid #f0f0f0;
display: flex;
align-items: center;
justify-content: space-between;
`;
// 自动播放开关
const autoPlayControl = document.createElement('div');
autoPlayControl.style.cssText = `
display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: #666;
`;
const autoPlaySwitch = document.createElement('div');
autoPlaySwitch.id = 'auto-play-switch';
autoPlaySwitch.style.cssText = `
width: 36px;
height: 18px;
background: ${isAutoPlayNextEnabled ? '#1890ff' : '#d9d9d9'};
border-radius: 9px;
position: relative;
cursor: pointer;
transition: all 0.3s;
`;
const autoPlaySwitchDot = document.createElement('div');
autoPlaySwitchDot.style.cssText = `
width: 14px;
height: 14px;
background: white;
border-radius: 50%;
position: absolute;
top: 2px;
left: ${isAutoPlayNextEnabled ? '20px' : '2px'};
transition: all 0.3s;
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
`;
autoPlaySwitch.appendChild(autoPlaySwitchDot);
autoPlaySwitch.addEventListener('click', function() {
isAutoPlayNextEnabled = !isAutoPlayNextEnabled;
autoPlaySwitch.style.background = isAutoPlayNextEnabled ? '#1890ff' : '#d9d9d9';
autoPlaySwitchDot.style.left = isAutoPlayNextEnabled ? '20px' : '2px';
if (isAutoPlayNextEnabled) {
startProgressObserver();
showNotification('已开启自动播放下一首', 'success');
} else {
stopProgressObserver();
showNotification('已关闭自动播放下一首', 'warning');
}
});
autoPlayControl.innerHTML = `
<span>自动下一首</span>
`;
autoPlayControl.appendChild(autoPlaySwitch);
// 当前播放显示
const nowPlaying = document.createElement('div');
nowPlaying.id = 'now-playing-display';
nowPlaying.style.cssText = `
font-size: 12px;
color: #1890ff;
font-weight: 500;
padding: 2px 8px;
background: rgba(24, 144, 255, 0.1);
border-radius: 4px;
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
`;
nowPlaying.textContent = '未播放';
controlContainer.appendChild(autoPlayControl);
controlContainer.appendChild(nowPlaying);
// 搜索框区域
const searchContainer = document.createElement('div');
searchContainer.style.cssText = `
padding: 12px 16px;
background: #fafafa;
border-bottom: 1px solid #f0f0f0;
`;
const searchInput = document.createElement('input');
searchInput.type = 'text';
searchInput.id = 'playlist-search';
searchInput.placeholder = '搜索歌曲...';
searchInput.style.cssText = `
width: 100%;
padding: 8px 12px;
border: 1px solid #d9d9d9;
border-radius: 4px;
font-size: 13px;
outline: none;
transition: all 0.3s;
`;
searchInput.addEventListener('input', function() {
filterPlaylist(this.value);
});
searchContainer.appendChild(searchInput);
// 内容区域
const content = document.createElement('div');
content.id = 'playlist-content';
content.style.cssText = `
flex: 1;
overflow-y: auto;
padding: 0;
`;
panel.appendChild(header);
panel.appendChild(controlContainer);
panel.appendChild(searchContainer);
panel.appendChild(content);
document.body.appendChild(panel);
// 添加按钮事件
document.getElementById('random-sort-btn').addEventListener('click', randomSortPlaylist);
document.getElementById('order-sort-btn').addEventListener('click', orderSortPlaylist);
document.getElementById('alphabet-sort-btn').addEventListener('click', alphabetSortPlaylist);
document.getElementById('original-sort-btn').addEventListener('click', originalSortPlaylist);
// 添加按钮悬停效果
const randomBtn = document.getElementById('random-sort-btn');
const orderBtn = document.getElementById('order-sort-btn');
const alphabetBtn = document.getElementById('alphabet-sort-btn');
const originalBtn = document.getElementById('original-sort-btn');
[randomBtn, orderBtn, alphabetBtn, originalBtn].forEach(btn => {
btn.addEventListener('mouseenter', () => {
btn.style.transform = 'scale(1.05)';
btn.style.boxShadow = '0 2px 8px rgba(0, 0, 0, 0.1)';
});
btn.addEventListener('mouseleave', () => {
btn.style.transform = 'scale(1)';
btn.style.boxShadow = 'none';
});
});
// 添加拖拽功能
makeDraggable(panel, header);
}
// 使面板可拖拽
function makeDraggable(element, handle) {
let isDragging = false;
let offsetX, offsetY;
handle.style.cursor = 'move';
handle.addEventListener('mousedown', startDrag);
document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', stopDrag);
function startDrag(e) {
isDragging = true;
const rect = element.getBoundingClientRect();
offsetX = e.clientX - rect.left;
offsetY = e.clientY - rect.top;
element.style.transition = 'none';
}
function drag(e) {
if (!isDragging) return;
e.preventDefault();
element.style.left = (e.clientX - offsetX) + 'px';
element.style.top = (e.clientY - offsetY) + 'px';
element.style.right = 'auto';
element.style.transform = 'none';
}
function stopDrag() {
isDragging = false;
}
}
// 初始化数据
function initData() {
const lines = STATIC_DATA.split('\n').filter(line => line.trim() !== '');
originalPlaylist = [...lines];
currentPlaylist = [...lines];
updateHeaderCount();
}
// 更新标题栏计数
function updateHeaderCount() {
const header = document.querySelector('#custom-playlist-panel > div:first-child span');
if (header) {
const searchInput = document.getElementById('playlist-search');
const searchValue = searchInput ? searchInput.value : '';
const count = searchValue ? currentPlaylist.filter(item =>
item.toLowerCase().includes(searchValue.toLowerCase())
).length : currentPlaylist.length;
header.textContent = `播放列表 (${count})`;
}
}
// 更新当前播放显示
function updateNowPlayingDisplay() {
const nowPlayingElement = document.getElementById('now-playing-display');
if (nowPlayingElement) {
if (currentPlayingIndex >= 0 && currentPlayingIndex < currentPlaylist.length) {
nowPlayingElement.textContent = `${currentPlayingIndex + 1}. ${currentPlaylist[currentPlayingIndex]}`;
nowPlayingElement.style.color = '#1890ff';
} else {
nowPlayingElement.textContent = '未播放';
nowPlayingElement.style.color = '#999';
}
}
}
// 显示播放列表
function displayPlaylist() {
const content = document.getElementById('playlist-content');
const searchInput = document.getElementById('playlist-search');
const searchValue = searchInput ? searchInput.value : '';
// 过滤数据
let displayData = currentPlaylist;
if (searchValue) {
displayData = currentPlaylist.filter(item =>
item.toLowerCase().includes(searchValue.toLowerCase())
);
}
content.innerHTML = '';
if (displayData.length === 0) {
content.innerHTML = `
<div style="
padding: 40px 20px;
text-align: center;
color: #999;
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
">
<svg viewBox="0 0 1024 1024" width="48" height="48" fill="#d9d9d9">
<path d="M885.6 230.2L779.1 123.8c-12.2-12.2-32-12.2-44.2 0L234.7 623.9c-12.2 12.2-12.2 32 0 44.2l106.5 106.5c12.2 12.2 32 12.2 44.2 0l500.2-500.2c12.2-12.2 12.2-32 0-44.2z m-178 0L402.1 542.1l-88.5-88.5L619 141.7l88.6 88.5zM153.1 854.9l271.9-271.9 88.5 88.5-271.9 271.9-88.5-88.5z"></path>
</svg>
<div>${searchValue ? '未找到相关歌曲' : '没有数据'}</div>
</div>
`;
updateHeaderCount();
return;
}
displayData.forEach((line, index) => {
// 在原始播放列表中查找索引
const originalIndex = currentPlaylist.indexOf(line);
const isCurrentPlaying = originalIndex === currentPlayingIndex;
const item = document.createElement('div');
item.className = 'playlist-item';
item.dataset.index = originalIndex;
item.style.cssText = `
padding: 12px 16px;
margin: 0;
background: ${isCurrentPlaying ? '#f0f9ff' : '#fff'};
border-left: 3px solid ${isCurrentPlaying ? '#1890ff' : 'transparent'};
border-bottom: 1px solid #f0f0f0;
cursor: pointer;
transition: all 0.3s;
font-size: 13px;
line-height: 1.4;
word-break: break-word;
display: flex;
align-items: center;
gap: 12px;
position: relative;
`;
if (isCurrentPlaying) {
item.style.boxShadow = 'inset 0 0 0 1px rgba(24, 144, 255, 0.1)';
}
// 序号样式
const numberStyle = `
display: inline-flex;
align-items: center;
justify-content: center;
width: 50px;
height: 24px;
background: ${isCurrentPlaying ? '#1890ff' : '#f0f0f0'};
color: ${isCurrentPlaying ? 'white' : '#666'};
border-radius: 4px;
font-size: 12px;
font-weight: 500;
flex-shrink: 0;
transition: all 0.3s;
`;
// 当前播放标记
const playingIndicator = isCurrentPlaying ? `
<div style="
position: absolute;
left: 4px;
top: 50%;
transform: translateY(-50%);
width: 6px;
height: 6px;
background: #1890ff;
border-radius: 50%;
animation: pulse 1.5s infinite;
"></div>
` : '';
item.innerHTML = `
${playingIndicator}
<span style="${numberStyle}">${originalIndex + 1}</span>
<span style="flex: 1; ${isCurrentPlaying ? 'font-weight: 500; color: #1890ff;' : ''}">${line}</span>
<svg viewBox="0 0 1024 1024" width="14" height="14" fill="${isCurrentPlaying ? '#1890ff' : '#bfbfbf'}" style="flex-shrink: 0; opacity: ${isCurrentPlaying ? '1' : '0'}; transition: opacity 0.3s;">
<path d="M765.7 486.8L314.9 134.7c-5.3-4.1-12.9-0.4-12.9 6.3v73.9c0 4.9 2.3 9.6 6.1 12.6l360 281.1-360 281.1c-3.9 3-6.1 7.7-6.1 12.6V883c0 6.7 7.7 10.4 12.9 6.3l450.8-352.1c16.4-12.8 16.4-37.6 0-50.4z"></path>
</svg>
`;
// 添加点击事件
item.addEventListener('click', () => handleItemClick(line, originalIndex));
// 添加鼠标悬停效果
item.addEventListener('mouseenter', () => {
if (!isCurrentPlaying) {
item.style.background = '#f6ffed';
item.querySelector('svg').style.opacity = '1';
}
});
item.addEventListener('mouseleave', () => {
if (!isCurrentPlaying) {
item.style.background = '#fff';
item.querySelector('svg').style.opacity = '0';
}
});
content.appendChild(item);
});
updateHeaderCount();
updateNowPlayingDisplay();
}
// 随机排序播放列表
function randomSortPlaylist() {
// Fisher-Yates 洗牌算法
const shuffled = [...currentPlaylist];
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
}
currentPlaylist = shuffled;
currentPlayingIndex = -1; // 重置当前播放索引
displayPlaylist();
showNotification('已随机排序播放列表', 'success');
}
// 顺序排序播放列表(按当前顺序)
function orderSortPlaylist() {
// 如果当前播放列表已经是原始顺序,则无需操作
if (JSON.stringify(currentPlaylist) === JSON.stringify([...originalPlaylist].sort())) {
return;
}
currentPlaylist = [...currentPlaylist].sort();
currentPlayingIndex = -1;
displayPlaylist();
showNotification('已按字母顺序排序(通用)', 'success');
}
// 首字母排序播放列表
function alphabetSortPlaylist() {
currentPlaylist = [...currentPlaylist].sort((a, b) => {
// 提取歌曲名(去除空格和特殊字符)
const getFirstChar = (str) => {
// 移除歌手信息,只保留歌曲名
const songName = str.split('-')[0].trim();
// 获取第一个字符
const firstChar = songName.charAt(0);
// 判断是否为中文
if (/[\u4e00-\u9fa5]/.test(firstChar)) {
return firstChar;
}
// 判断是否为字母
if (/[a-zA-Z]/.test(firstChar)) {
return firstChar.toUpperCase();
}
// 其他字符
return firstChar;
};
const charA = getFirstChar(a);
const charB = getFirstChar(b);
// 中文比较
if (/[\u4e00-\u9fa5]/.test(charA) && /[\u4e00-\u9fa5]/.test(charB)) {
return charA.localeCompare(charB, 'zh-CN');
}
// 字母比较
if (/[a-zA-Z]/.test(charA) && /[a-zA-Z]/.test(charB)) {
return charA.localeCompare(charB);
}
// 混合比较:字母优先于中文
if (/[a-zA-Z]/.test(charA) && /[\u4e00-\u9fa5]/.test(charB)) {
return -1;
}
if (/[\u4e00-\u9fa5]/.test(charA) && /[a-zA-Z]/.test(charB)) {
return 1;
}
return a.localeCompare(b);
});
currentPlayingIndex = -1;
displayPlaylist();
showNotification('已按首字母排序', 'success');
}
// 原始顺序排序播放列表
function originalSortPlaylist() {
currentPlaylist = [...originalPlaylist];
currentPlayingIndex = -1;
displayPlaylist();
showNotification('已恢复原始顺序', 'success');
}
// 过滤播放列表
function filterPlaylist(searchValue) {
displayPlaylist();
}
// 处理项目点击
async function handleItemClick(line, index) {
try {
// 更新当前播放索引
currentPlayingIndex = index;
displayPlaylist();
// 1. 查找输入框并赋值
const input = findIn**lement();
if (input) {
input.value = line;
// 触发输入事件,确保相关**被触发
input.dispatchEvent(new Event('input', { bubbles: true }));
input.dispatchEvent(new Event('change', { bubbles: true }));
}
// 2. 查找并点击搜索按钮
const button = findSearchButton();
if (button) {
button.click();
// 3. 等待列表出现并点击第一项
await waitForListAndClickFirst();
// 4. 开始监听播放进度(如果启用自动播放)
if (isAutoPlayNextEnabled) {
setTimeout(startProgressObserver, 1000); // 等待1秒后开始监听
}
} else {
console.warn('未找到搜索按钮');
showNotification('未找到搜索按钮', 'warning');
}
} catch (error) {
console.error('操作失败:', error);
showNotification('操作失败: ' + error.message, 'error');
}
}
// 开始监听播放进度
function startProgressObserver() {
stopProgressObserver(); // 先停止之前的监听
if (!isAutoPlayNextEnabled) return;
progressObserver = setInterval(() => {
const progressBar = document.querySelector('.aplayer-played');
if (progressBar) {
const style = progressBar.getAttribute('style') || '';
const widthMatch = style.match(/width:\s*([\d.]+)%/);
if (widthMatch) {
const width = parseFloat(widthMatch[1]);
// 当进度达到90%时开始每毫秒检测
if (width >= 90 && width < 99) {
// 切换到更频繁的检测
stopProgressObserver();
progressObserver = setInterval(() => {
const progressBar = document.querySelector('.aplayer-played');
if (progressBar) {
const style = progressBar.getAttribute('style') || '';
const widthMatch = style.match(/width:\s*([\d.]+)%/);
if (widthMatch) {
const currentWidth = parseFloat(widthMatch[1]);
// 当进度达到99%~100%时播放下一首
if (currentWidth >= 99 && currentWidth <= 100) {
playNextSong();
stopProgressObserver();
setTimeout(startProgressObserver, 1000); // 重新开始监听
}
}
}
}, 100); // 每100毫秒检测一次
}
}
}
}, 500); // 每500毫秒检测一次
}
// 停止监听播放进度
function stopProgressObserver() {
if (progressObserver) {
clearInterval(progressObserver);
progressObserver = null;
}
}
// 播放下一首歌曲
function playNextSong() {
if (currentPlayingIndex >= 0 && currentPlaylist.length > 0) {
let nextIndex = (currentPlayingIndex + 1) % currentPlaylist.length;
const nextSong = currentPlaylist[nextIndex];
showNotification(`正在播放下一首: ${nextSong}`, 'info');
// 模拟点击下一首
setTimeout(() => {
handleItemClick(nextSong, nextIndex);
}, 300);
}
}
// 查找输入框元素
function findIn**lement() {
// 尝试多种选择器
const selectors = [
INPUT_SELECTOR,
'input.ant-input',
'input[class*="ant-input"]',
'textarea.ant-input',
'textarea[class*="ant-input"]'
];
for (const selector of selectors) {
const element = document.querySelector(selector);
if (element) return element;
}
// 如果没找到,查找所有可能的输入框
const allInputs = document.querySelectorAll('input, textarea');
for (const input of allInputs) {
if (input.className && input.className.includes('ant-input')) {
return input;
}
}
console.warn('未找到输入框元素');
showNotification('未找到输入框', 'warning');
return null;
}
// 查找搜索按钮
function findSearchButton() {
// 尝试多种选择器
const selectors = [
BUTTON_SELECTOR,
'button.ant-btn-primary',
'button[class*="ant-input-search-button"]',
'button:has(.anticon-search)',
'.ant-input-search .ant-btn'
];
for (const selector of selectors) {
const element = document.querySelector(selector);
if (element) return element;
}
// 如果没找到,查找所有可能的按钮
const allButtons = document.querySelectorAll('button');
for (const button of allButtons) {
const classes = button.className || '';
if (classes.includes('ant-btn') && classes.includes('ant-btn-primary')) {
return button;
}
}
console.warn('未找到搜索按钮');
showNotification('未找到搜索按钮', 'warning');
return null;
}
// 等待列表出现并点击第一项
function waitForListAndClickFirst() {
return new Promise((resolve, reject) => {
let attempts = 0;
const maxAttempts = 50; // 最多尝试10秒(200ms * 50)
const checkInterval = setInterval(() => {
attempts++;
// 尝试多种选择器
const selectors = [
LIST_ITEM_SELECTOR,
'.ant-list-items li',
'.ant-list li:first-child',
'[class*="list"] li:first-child'
];
let firstItem = null;
for (const selector of selectors) {
const item = document.querySelector(selector);
if (item) {
firstItem = item;
break;
}
}
if (firstItem) {
clearInterval(checkInterval);
firstItem.click();
showNotification('操作完成', 'success');
resolve();
} else if (attempts >= maxAttempts) {
clearInterval(checkInterval);
console.warn('未找到列表项');
showNotification('未找到列表项,但输入和搜索已完成', 'warning');
resolve();
}
}, 200); // 每200ms检查一次
});
}
// 显示通知
function showNotification(message, type = 'info') {
const notification = document.createElement('div');
const colors = {
success: '#52c41a',
error: '#ff4d4f',
warning: '#faad14',
info: '#1890ff'
};
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
padding: 12px 20px;
background: white;
border: 1px solid ${colors[type]};
border-left: 4px solid ${colors[type]};
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
z-index: 10000;
animation: slideIn 0.3s ease;
font-size: 14px;
color: #333;
max-width: 300px;
`;
notification.innerHTML = `
<div style="display: flex; align-items: center;">
<span style="
width: 8px;
height: 8px;
border-radius: 50%;
background: ${colors[type]};
margin-right: 10px;
"></span>
${message}
</div>
`;
document.body.appendChild(notification);
// 3秒后自动移除
setTimeout(() => {
notification.style.animation = 'slideOut 0.3s ease';
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 300);
}, 3000);
}
// 添加CSS样式
function addStyles() {
const style = document.createElement('style');
style.textContent = `
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideOut {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(100%);
opacity: 0;
}
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(24, 144, 255, 0.7);
}
70% {
box-shadow: 0 0 0 4px rgba(24, 144, 255, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(24, 144, 255, 0);
}
}
.playlist-item:hover {
transform: translateX(-2px);
}
#custom-playlist-panel::-webkit-scrollbar {
width: 6px;
}
#custom-playlist-panel::-webkit-scrollbar-track {
background: #f1f1f1;
}
#custom-playlist-panel::-webkit-scrollbar-thumb {
background: #b8b8b8;
border-radius: 3px;
}
#custom-playlist-panel::-webkit-scrollbar-thumb:hover {
background: #999;
}
#playlist-search:focus {
border-color: #1890ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
#auto-play-switch:hover {
opacity: 0.8;
}
`;
document.head.appendChild(style);
}
// 初始化
function init() {
// 添加样式
addStyles();
// 初始化数据
initData();
// 创建悬浮面板
createFloatingPanel();
// 显示播放列表
displayPlaylist();
console.log('播放列表脚本已加载,带排序功能和自动播放');
}
// 页面加载完成后初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
然后替换中间的歌曲信息部分
音乐迁移-GoMusic 这个网址去获取自己的歌单信息 小心个单中有 ` 符号需要去除