收起左侧

Musicn音乐下载工具部署

15
回复
5458
查看
[ 复制链接 ]

3

主题

11

回帖

0

牛值

江湖小虾

社区共建团荣誉勋章

2024-12-20 10:43:44 显示全部楼层 阅读模式

Musicn音乐下载工具部署

一、Musicn简介

Musicn是一个支持咪咕、酷狗和**云音乐的命令行工具,可用于播放和下载普通MP3格式的音乐。容器支持amd64和arm64架构。

注意事项:

  • 仅支持普通MP3格式,不支持无损格式。
  • 部分会员专属歌曲下载暂不支持。
  • 仅供个人学习研究,严禁用于商业用途。

二、环境准备

2.1 本地环境

  • 操作系统: fnos最新版

2.2 检查Docker服务

确保Docker服务正常运行:

systemctl status docker

2.3 检查Docker版本

docker -v

2.4 检查Docker Compose版本

docker compose version

三、部署Musicn

3.1 下载Musicn镜像

拉取Musicn镜像:

docker pull ghcr.io/wy580477/musicn-container:latest

3.2 创建部署目录

mkdir -p /data/mus-dw && cd /data/mus-dw

3.3 使用Docker Compose部署

创建 docker-compose.yaml文件:

version: '3.4'

services:
  musicn:
    image: ghcr.io/wy580477/musicn-container:latest
    container_name: musicn
    restart: always
    entrypoint: ["/sbin/tini", "--", "msc", "-q"]
    ports:
      - "7478:7478"
    volumes:
      - ./data:/data

3.4 启动Musicn容器

docker compose up -d

3.5 检查容器状态

docker compose ps

3.6 查看容器日志

docker compose logs

四、访问Musicn服务

4.1 访问Musicn主页

访问地址:http://<服务器IP>:7478,例如:http://192.168.3.88:7478

4.2 下载音乐文件

在Musicn主页选择音源(如酷狗音乐),搜索歌曲名并点击下载。

五、总结

通过Docker部署Musicn音乐下载工具,简化了环境配置和依赖管理。Musicn支持多个音乐平台的MP3格式播放和下载,适合个人学习和研究使用。

收藏
送赞 2
分享

8

主题

138

回帖

60

牛值

fnOS系统内测组

社区上线纪念勋章社区共建团荣誉勋章

2024-12-23 09:26:15 显示全部楼层
学习了,感谢分享

4

主题

6

回帖

0

牛值

江湖小虾

2024-12-25 10:49:06 显示全部楼层
访问地址是运行日志里的访问链接吗,为什么打不开
内网ip加映射的端口  详情 回复
2024-12-26 09:45

3

主题

11

回帖

0

牛值

江湖小虾

社区共建团荣誉勋章

2024-12-26 09:45:53 楼主 显示全部楼层
内网ip加映射的端口

0

主题

3

回帖

0

牛值

江湖小虾

2025-1-10 13:19:09 显示全部楼层
访问本机7478,浏览器一直转,没内容
加载半天返回个结果,{{ item.name }}  详情 回复
2025-1-10 13:21

0

主题

3

回帖

0

牛值

江湖小虾

2025-1-10 13:21:42 显示全部楼层
加载半天返回个结果,{{ item.name }}
+1解决了吗  详情 回复
2025-2-10 14:00

4

主题

6

回帖

0

牛值

江湖小虾

2025-2-10 14:00:31 显示全部楼层
+1解决了吗

4

主题

6

回帖

0

牛值

江湖小虾

2025-2-10 14:08:54 显示全部楼层
打开显示{{ item.name }}的,找到问题了,开了广告屏蔽的工具,关了屏蔽的工具就好使了

0

主题

4

回帖

0

牛值

江湖小虾

2025-2-25 21:11:25 显示全部楼层
第三部就开始失败。放弃
搞定了。希望步骤在详细些就好了  详情 回复
2025-2-25 21:26

0

主题

4

回帖

0

牛值

江湖小虾

2025-2-25 21:26:30 显示全部楼层
搞定了。希望步骤在详细些就好了

0

主题

3

回帖

0

牛值

江湖小虾

2025-3-3 12:21:30 显示全部楼层
所以说那个无损的音乐是不是下载不了?

0

主题

15

回帖

0

牛值

江湖小虾

2025-12-6 17:21:22 显示全部楼层

docker pull ghcr.io/wy580477/musicn-container:latest

卡在这个步骤了。

0

主题

4

回帖

0

牛值

江湖小虾

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 这个网址去获取自己的歌单信息 小心个单中有 ` 符号需要去除

镜像里面删除国外的就行,只能用一个  详情 回复
昨天 00:27

0

主题

4

回帖

0

牛值

江湖小虾

image.png

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
用**的音源  详情 回复
昨天 00:33

0

主题

4

回帖

0

牛值

江湖小虾

半夜三更 发表于 2025-12-20 00:26
version: '3'
services:
  musicn:

镜像里面删除国外的就行,只能用一个

0

主题

4

回帖

0

牛值

江湖小虾

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则