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

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

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

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

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

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

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 回帖
  • 思源笔记

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

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

    28446 引用 • 119783 回帖
  • 千千插件

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

    32 引用 • 69 回帖
  • 千千块
    25 引用 • 61 回帖

相关帖子

欢迎来到这里!

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

注册 关于
请输入回帖内容 ...
  • Fighter93

    看了你之前发的帖子,最近体验了两天,相比于在 HTML 中写代码,你这种方式优点是方便,不需要多创建一个 HTML 块,缺点是执行速度较慢。内容块渲染好了,0-2 秒(可以被明显感知到)后,它的自定义样式和脚本才开始生效或执行。

    1 回复
  • 建议你用 千千块 插件 是瞬间渲染

    2 回复
  • Fighter93

    好 🙏 ,我去试一下。

  • 怎么使用啊大佬,单击块标 → 插件 → 自定义块 js→ 输入 js 代码?

    试了好多个 js 都没什么反应的么

    小白,求教

    1 回复
  • 按思源笔记右上角的插件,下面有个千千块,按定住,然后千千块在顶部图标显示,打开开关。。。。。。

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

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

    1 回复
  • 帖子的 JS 复制到自定义快 JS 的输入框里也没效果呀 😂

    1 回复
  • 你再创建个工作空间,新的工作空间什么也没有,再试试千千块,如果可以的话,那就是插件冲突,如果不可以的话,那就真不知道怎么回事了。。。。。。 然后?左上角开发者选项,按控制台,如果 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 回复
  • 说明在某个代码块中执行的 JS 脚本存在语法错误,那么开始你复制的问题,你会不会连代码框的 css 那个也复制到了,你先将代码复制到思源笔记上,看看是否复制了 css"这种代码框

请输入回帖内容 ...
lovexmm521 MOD
窈窕淑女,君子好逑 爱发电:https://afdian.com/a/QianQian517

推荐标签 标签

  • Ubuntu

    Ubuntu(友帮拓、优般图、乌班图)是一个以桌面应用为主的 Linux 操作系统,其名称来自非洲南部祖鲁语或豪萨语的“ubuntu”一词,意思是“人性”、“我的存在是因为大家的存在”,是非洲传统的一种价值观,类似华人社会的“仁爱”思想。Ubuntu 的目标在于为一般用户提供一个最新的、同时又相当稳定的主要由自由软件构建而成的操作系统。

    127 引用 • 169 回帖
  • Git

    Git 是 Linux Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。

    215 引用 • 358 回帖
  • Sym

    Sym 是一款用 Java 实现的现代化社区(论坛/BBS/社交网络/博客)系统平台。

    下一代的社区系统,为未来而构建

    524 引用 • 4602 回帖 • 731 关注
  • 电影

    这是一个不能说的秘密。

    125 引用 • 610 回帖
  • MySQL

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

    695 引用 • 538 回帖 • 1 关注
  • 微软

    微软是一家美国跨国科技公司,也是世界 PC 软件开发的先导,由比尔·盖茨与保罗·艾伦创办于 1975 年,公司总部设立在华盛顿州的雷德蒙德(Redmond,邻近西雅图)。以研发、制造、授权和提供广泛的电脑软件服务业务为主。

    8 引用 • 44 回帖 • 2 关注
  • JRebel

    JRebel 是一款 Java 虚拟机插件,它使得 Java 程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。

    26 引用 • 78 回帖 • 693 关注
  • WiFiDog

    WiFiDog 是一套开源的无线热点认证管理工具,主要功能包括:位置相关的内容递送;用户认证和授权;集中式网络监控。

    1 引用 • 7 回帖 • 633 关注
  • Sillot

    Insights(注意当前设置 master 为默认分支)

    汐洛彖夲肜矩阵(Sillot T☳Converbenk Matrix),致力于服务智慧新彖乄,具有彖乄驱动、极致优雅、开发者友好的特点。其中汐洛绞架(Sillot-Gibbet)基于自思源笔记(siyuan-note),前身是思源笔记汐洛版(更早是思源笔记汐洛分支),是智慧新录乄终端(多端融合,移动端优先)。

    主仓库地址:Hi-Windom/Sillot

    文档地址:sillot.db.sc.cn

    注意事项:

    1. ⚠️ 汐洛仍在早期开发阶段,尚不稳定
    2. ⚠️ 汐洛并非面向普通用户设计,使用前请了解风险
    3. ⚠️ 汐洛绞架基于思源笔记,开发者尽最大努力与思源笔记保持兼容,但无法实现 100% 兼容
    29 引用 • 25 回帖 • 152 关注
  • Facebook

    Facebook 是一个联系朋友的社交工具。大家可以通过它和朋友、同事、同学以及周围的人保持互动交流,分享无限上传的图片,发布链接和视频,更可以增进对朋友的了解。

    4 引用 • 15 回帖 • 443 关注
  • 开源中国

    开源中国是目前中国最大的开源技术社区。传播开源的理念,推广开源项目,为 IT 开发者提供了一个发现、使用、并交流开源技术的平台。目前开源中国社区已收录超过两万款开源软件。

    7 引用 • 86 回帖
  • BAE

    百度应用引擎(Baidu App Engine)提供了 PHP、Java、Python 的执行环境,以及云存储、消息服务、云数据库等全面的云服务。它可以让开发者实现自动地部署和管理应用,并且提供动态扩容和负载均衡的运行环境,让开发者不用考虑高成本的运维工作,只需专注于业务逻辑,大大降低了开发者学习和迁移的成本。

    19 引用 • 75 回帖 • 702 关注
  • 千千插件

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

    32 引用 • 69 回帖
  • Bootstrap

    Bootstrap 是 Twitter 推出的一个用于前端开发的开源工具包。它由 Twitter 的设计师 Mark Otto 和 Jacob Thornton 合作开发,是一个 CSS / HTML 框架。

    18 引用 • 33 回帖 • 646 关注
  • 博客

    记录并分享人生的经历。

    274 引用 • 2393 回帖 • 1 关注
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    167 引用 • 408 回帖 • 494 关注
  • Typecho

    Typecho 是一款博客程序,它在 GPLv2 许可证下发行,基于 PHP 构建,可以运行在各种平台上,支持多种数据库(MySQL、PostgreSQL、SQLite)。

    12 引用 • 67 回帖 • 436 关注
  • 互联网

    互联网(Internet),又称网际网络,或音译因特网、英特网。互联网始于 1969 年美国的阿帕网,是网络与网络之间所串连成的庞大网络,这些网络以一组通用的协议相连,形成逻辑上的单一巨大国际网络。

    99 引用 • 367 回帖 • 1 关注
  • 又拍云

    又拍云是国内领先的 CDN 服务提供商,国家工信部认证通过的“可信云”,乌云众测平台认证的“安全云”,为移动时代的创业者提供新一代的 CDN 加速服务。

    20 引用 • 37 回帖 • 577 关注
  • 百度

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

    63 引用 • 785 回帖 • 46 关注
  • 尊园地产

    昆明尊园房地产经纪有限公司,即:Kunming Zunyuan Property Agency Company Limited(简称“尊园地产”)于 2007 年 6 月开始筹备,2007 年 8 月 18 日正式成立,注册资本 200 万元,公司性质为股份经纪有限公司,主营业务为:代租、代售、代办产权过户、办理银行按揭、担保、抵押、评估等。

    1 引用 • 22 回帖 • 838 关注
  • Tomcat

    Tomcat 最早是由 Sun Microsystems 开发的一个 Servlet 容器,在 1999 年被捐献给 ASF(Apache Software Foundation),隶属于 Jakarta 项目,现在已经独立为一个顶级项目。Tomcat 主要实现了 JavaEE 中的 Servlet、JSP 规范,同时也提供 HTTP 服务,是市场上非常流行的 Java Web 容器。

    162 引用 • 529 回帖 • 3 关注
  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    950 引用 • 1460 回帖 • 2 关注
  • SQLite

    SQLite 是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite 是全世界使用最为广泛的数据库引擎。

    4 引用 • 7 回帖
  • 小薇

    小薇是一个用 Java 写的 QQ 聊天机器人 Web 服务,可以用于社群互动。

    由于 Smart QQ 从 2019 年 1 月 1 日起停止服务,所以该项目也已经停止维护了!

    35 引用 • 468 回帖 • 768 关注
  • 脑图

    脑图又叫思维导图,是表达发散性思维的有效图形思维工具 ,它简单却又很有效,是一种实用性的思维工具。

    40 引用 • 157 回帖
  • Gitea

    Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。

    5 引用 • 16 回帖 • 3 关注