千千块模板|思源笔记音乐播放器,一边学习一边听歌

千千块模板 | 思源笔记音乐播放器,一边学习一边听歌

想边记笔记,边听歌的功能

可以点击播放器右侧的 🎶 图标切换模式(随机播放文件夹模式和单曲选择播放模式)

选择文件夹后可以随机文件夹的音乐

可以调节声音大小(鼠标选悬停在音量按钮,变成可以调节音量)

image.png

自定义块 js 代码

// --- 随心切换音乐播放器  ---

// --- 状态变量 ---
let playerMode = 'folder'; // 'file' 或 'folder'
let playlist = [];
let currentTrackIndex = -1;
const blockId = this.getAttribute('data-node-id');

// --- 初始化UI ---
this.innerHTML = '';
this.style.backgroundColor = 'var(--b3-theme-surface)';
this.style.borderRadius = 'var(--b3-border-radius-lg)';
this.style.padding = '24px';
this.style.boxShadow = 'var(--b3-shadow-2)';
this.style.fontFamily = 'var(--b3-font-family)';
this.style.color = 'var(--b3-font-color)';
this.style.transition = 'all 0.3s ease';
this.style.border = '1px solid var(--b3-border-color)';

// --- 内部CSS样式 ---
const style = document.createElement('style');
style.textContent = `
  .hidden { display: none !important; }
  .title-container { display: flex; align-items: center; justify-content: center; position: relative; }
  .music-player-title {
    margin-top: 0; margin-bottom: 20px; color: var(--b3-theme-primary); font-size: 1.6em;
    font-weight: 600; text-align: center; text-shadow: 0 1px 2px rgba(0,0,0,0.1);
  }
  .music-player-title .mode-icon {
    cursor: pointer;
    display: inline-block;
    transition: transform 0.2s ease;
  }
  .music-player-title .mode-icon:hover {
    transform: scale(1.2);
  }
  .music-file-input-label {
    display: block; width: 100%; padding: 14px 0; background-color: var(--b3-theme-primary);
    color: var(--b3-theme-on-primary); text-align: center; border-radius: var(--b3-border-radius);
    cursor: pointer; transition: all 0.25s ease; margin-bottom: 16px; font-weight: 500;
    font-size: 1.1em; box-shadow: var(--b3-shadow-1);
  }
  .music-file-input-label:hover {
    background-color: var(--b3-theme-primary-light); transform: translateY(-3px); box-shadow: var(--b3-shadow-3);
  }
  .music-file-input { display: none; }
  .music-player-audio { width: 100%; border-radius: var(--b3-border-radius); outline: none; margin-top: 16px; }
  .music-song-info {
    margin-top: 12px; padding: 8px 12px; text-align: center; color: var(--b3-font-color-subtle);
    background-color: var(--b3-theme-background); border-radius: var(--b3-border-radius);
    min-height: 24px; font-size: 0.95em; overflow: hidden; text-overflow: ellipsis;
    white-space: nowrap; border: 1px solid var(--b3-border-color);
  }
  .music-controls { display: flex; justify-content: center; align-items: center; gap: 20px; margin-top: 18px; }
  .control-btn {
    background-color: var(--b3-theme-background); border: 1px solid var(--b3-border-color);
    border-radius: 50%; width: 44px; height: 44px; display: flex; align-items: center;
    justify-content: center; cursor: pointer; transition: all 0.2s ease; font-size: 1.4em;
    box-shadow: var(--b3-shadow-1);
  }
  .control-btn:hover { background-color: var(--b3-theme-surface-light); transform: scale(1.1); }
`;
this.appendChild(style);

// --- 创建HTML元素 ---
const titleContainer = document.createElement('div');
titleContainer.className = 'title-container';
this.appendChild(titleContainer);

const playerTitle = document.createElement('h3');
playerTitle.className = 'music-player-title';
titleContainer.appendChild(playerTitle);

const fileInputLabel = document.createElement('label');
fileInputLabel.className = 'music-file-input-label';
fileInputLabel.setAttribute('for', 'music-file-input-' + blockId);
this.appendChild(fileInputLabel);

const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.className = 'music-file-input';
fileInput.id = 'music-file-input-' + blockId;
this.appendChild(fileInput);

const songInfo = document.createElement('div');
songInfo.className = 'music-song-info';
this.appendChild(songInfo);

const audioPlayer = document.createElement('audio');
audioPlayer.controls = true;
audioPlayer.className = 'music-player-audio';
this.appendChild(audioPlayer);

const controlsContainer = document.createElement('div');
controlsContainer.className = 'music-controls hidden';
this.appendChild(controlsContainer);

const prevButton = document.createElement('button');
prevButton.className = 'control-btn';
prevButton.innerHTML = '⏮️';
prevButton.title = '上一首';
controlsContainer.appendChild(prevButton);

const nextButton = document.createElement('button');
nextButton.className = 'control-btn';
nextButton.innerHTML = '⏭️';
nextButton.title = '下一首';
controlsContainer.appendChild(nextButton);

// --- 核心功能函数 ---
function updateUIMode() {
  if (playerMode === 'file') {
    playerTitle.innerHTML = '🎧 随心听 · 播放器 <span class="mode-icon" title="切换到文件夹模式">🎶</span>';
    fileInputLabel.textContent = '📤 点我选择音乐文件';
    fileInput.removeAttribute('webkitdirectory');
    fileInput.accept = 'audio/*';
    controlsContainer.classList.add('hidden');
    songInfo.textContent = '✨ 请选择一首歌曲,开始你的音乐之旅...';
  } else {
    playerTitle.innerHTML = '🎧 随心听 · 播放器 <span class="mode-icon" title="切换到单曲模式">🎶</span>';
    fileInputLabel.textContent = '📤 点我选择音乐文件夹';
    fileInput.setAttribute('webkitdirectory', true);
    fileInput.removeAttribute('accept');
    controlsContainer.classList.remove('hidden');
    songInfo.textContent = '✨ 选择一个文件夹,开启随机音乐之旅...';
  }
}

function shuffleArray(array) {
  for (let i = array.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
}

function playSongAtIndex(index) {
  if (playlist.length === 0 || index < 0 || index >= playlist.length) return;
  currentTrackIndex = index;
  const file = playlist[index];
  const objectURL = URL.createObjectURL(file);
  audioPlayer.src = objectURL;
  audioPlayer.play();
  songInfo.textContent = `[${index + 1}/${playlist.length}] ${file.name}`;
}

function playNextSong() {
  if (playlist.length === 0) return;
  const nextIndex = (currentTrackIndex + 1) % playlist.length;
  playSongAtIndex(nextIndex);
}

function playPrevSong() {
  if (playlist.length === 0) return;
  const prevIndex = (currentTrackIndex - 1 + playlist.length) % playlist.length;
  playSongAtIndex(prevIndex);
}

// --- 事件监听器 ---
titleContainer.addEventListener('click', (event) => {
  if (event.target.classList.contains('mode-icon')) {
    playerMode = playerMode === 'file' ? 'folder' : 'file';
    updateUIMode();
  }
});

fileInput.addEventListener('change', (event) => {
  const files = event.target.files;
  if (!files || files.length === 0) return;

  if (playerMode === 'file') {
    const file = files[0];
    const objectURL = URL.createObjectURL(file);
    audioPlayer.src = objectURL;
    audioPlayer.play();
    songInfo.textContent = '正在播放 🎵: ' + file.name;
    fetch('/api/notification/pushMsg', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ msg: `已经准备好啦: ${file.name}`, timeout: 4000 })
    });
  } else { // folder mode
    const audioFiles = Array.from(files).filter(file => 
      file.type.startsWith('audio/') || /\.(mp3|wav|ogg|flac|m4a)$/i.test(file.name)
    );
    if (audioFiles.length === 0) {
      fetch('/api/notification/pushErrMsg', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ msg: '未发现任何支持的音频文件', timeout: 4000 })
      });
      return;
    }
    playlist = audioFiles;
    shuffleArray(playlist);
    currentTrackIndex = -1;
    fetch('/api/notification/pushMsg', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ msg: `播放列表加载成功,共 ${playlist.length} 首`, timeout: 4000 })
    });
    playNextSong();
  }
});

audioPlayer.addEventListener('ended', () => {
  if (playerMode === 'folder') {
    playNextSong();
  }
});

nextButton.addEventListener('click', playNextSong);
prevButton.addEventListener('click', playPrevSong);

// --- 初始化 ---
updateUIMode();


  • 千千块模板
    8 引用 • 18 回帖
  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    28442 引用 • 119754 回帖
  • 千千插件

    千千块(自定义块 css 和 js)
    可以用 ai 提示词来无限创作思源笔记

    32 引用 • 69 回帖 • 1 关注
  • 千千块
    25 引用 • 61 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • image.png这个么,打开了呀。插件使用方法中的 css 是有效果的,输入后字变成红色了。输入 js 都看不到效果 😂

    1 回复
  • 其他回帖
  • 打开开发者选项,有报错哦,看不懂 😂

    Error executing custom JS for block [20250927091827-2cwb87y]: SyntaxError: Invalid or unexpected token
        at new Function (<anonymous>)
        at eval (plugin:siyuan-plugin-QianQiankuai:25:1659)
        at NodeList.forEach (<anonymous>)
        at p.scanAndApplyJS (plugin:siyuan-plugin-QianQiankuai:25:1360)
        at eval (plugin:siyuan-plugin-QianQiankuai:25:2226)
    
    1 回复
  • Fighter93

    好 🙏 ,我去试一下。

  • 如果你用插件使用方法,当然看不见效果了,因为那个是开发者控制台才有效果,专门让新手小白故意问没效果的指令 😄 ,如果你用这个帖子的,肯定就有效果了

    1 回复
  • 查看全部回帖
lovexmm521 MOD
窈窕淑女,君子好逑 爱发电:https://afdian.com/a/QianQian517

推荐标签 标签

  • 支付宝

    支付宝是全球领先的独立第三方支付平台,致力于为广大用户提供安全快速的电子支付/网上支付/安全支付/手机支付体验,及转账收款/水电煤缴费/信用卡还款/AA 收款等生活服务应用。

    29 引用 • 347 回帖 • 2 关注
  • Firefox

    Mozilla Firefox 中文俗称“火狐”(正式缩写为 Fx 或 fx,非正式缩写为 FF),是一个开源的网页浏览器,使用 Gecko 排版引擎,支持多种操作系统,如 Windows、OSX 及 Linux 等。

    7 引用 • 30 回帖 • 367 关注
  • flomo

    flomo 是新一代 「卡片笔记」 ,专注在碎片化时代,促进你的记录,帮你积累更多知识资产。

    6 引用 • 144 回帖
  • 一些有用的避坑指南。

    69 引用 • 93 回帖
  • Vue.js

    Vue.js(读音 /vju ː/,类似于 view)是一个构建数据驱动的 Web 界面库。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。

    269 引用 • 666 回帖 • 1 关注
  • AWS
    11 引用 • 28 回帖 • 2 关注
  • 安装

    你若安好,便是晴天。

    134 引用 • 1184 回帖 • 2 关注
  • MySQL

    MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司。MySQL 是最流行的关系型数据库管理系统之一。

    695 引用 • 538 回帖 • 1 关注
  • Kafka

    Kafka 是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者规模的网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是现代系统中许多功能的基础。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。

    36 引用 • 35 回帖
  • 钉钉

    钉钉,专为中国企业打造的免费沟通协同多端平台, 阿里巴巴出品。

    15 引用 • 67 回帖 • 236 关注
  • RabbitMQ

    RabbitMQ 是一个开源的 AMQP 实现,服务器端用 Erlang 语言编写,支持多种语言客户端,如:Python、Ruby、.NET、Java、C、PHP、ActionScript 等。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

    49 引用 • 60 回帖 • 342 关注
  • 创业

    你比 99% 的人都优秀么?

    81 引用 • 1396 回帖 • 1 关注
  • Netty

    Netty 是一个基于 NIO 的客户端-服务器编程框架,使用 Netty 可以让你快速、简单地开发出一个可维护、高性能的网络应用,例如实现了某种协议的客户、服务端应用。

    49 引用 • 33 回帖 • 63 关注
  • FreeMarker

    FreeMarker 是一款好用且功能强大的 Java 模版引擎。

    23 引用 • 20 回帖 • 475 关注
  • 黑曜石

    黑曜石是一款强大的知识库工具,支持本地 Markdown 文件编辑,支持双向链接和关系图。

    A second brain, for you, forever.

    34 引用 • 333 回帖
  • jsDelivr

    jsDelivr 是一个开源的 CDN 服务,可为 npm 包、GitHub 仓库提供免费、快速并且可靠的全球 CDN 加速服务。

    5 引用 • 31 回帖 • 121 关注
  • Elasticsearch

    Elasticsearch 是一个基于 Lucene 的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于 RESTful 接口。Elasticsearch 是用 Java 开发的,并作为 Apache 许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

    117 引用 • 99 回帖 • 191 关注
  • 百度

    百度(Nasdaq:BIDU)是全球最大的中文搜索引擎、最大的中文网站。2000 年 1 月由李彦宏创立于北京中关村,致力于向人们提供“简单,可依赖”的信息获取方式。“百度”二字源于中国宋朝词人辛弃疾的《青玉案·元夕》词句“众里寻他千百度”,象征着百度对中文信息检索技术的执著追求。

    63 引用 • 785 回帖 • 46 关注
  • IDEA

    IDEA 全称 IntelliJ IDEA,是一款 Java 语言开发的集成环境,在业界被公认为最好的 Java 开发工具之一。IDEA 是 JetBrains 公司的产品,这家公司总部位于捷克共和国的首都布拉格,开发人员以严谨著称的东欧程序员为主。

    182 引用 • 400 回帖
  • webpack

    webpack 是一个用于前端开发的模块加载器和打包工具,它能把各种资源,例如 JS、CSS(less/sass)、图片等都作为模块来使用和处理。

    43 引用 • 130 回帖 • 259 关注
  • abitmean

    有点意思就行了

    44 关注
  • 新人

    让我们欢迎这对新人。哦,不好意思说错了,让我们欢迎这位新人!
    新手上路,请谨慎驾驶!

    52 引用 • 228 回帖
  • 电影

    这是一个不能说的秘密。

    125 引用 • 610 回帖
  • Swift

    Swift 是苹果于 2014 年 WWDC(苹果开发者大会)发布的开发语言,可与 Objective-C 共同运行于 Mac OS 和 iOS 平台,用于搭建基于苹果平台的应用程序。

    34 引用 • 37 回帖 • 565 关注
  • 音乐

    你听到信仰的声音了么?

    63 引用 • 513 回帖
  • TGIF

    Thank God It's Friday! 感谢老天,总算到星期五啦!

    293 引用 • 4496 回帖 • 687 关注
  • TensorFlow

    TensorFlow 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。

    20 引用 • 19 回帖