Typecho博客的折腾记录——为Jasmine添加Meting播放器

技术性踩雷  ·  2026-05-26

meting.png

前言

Jasmine主题简约而又强大,配合Meting Aplayer 可以得到相当不错的体验,但是仅限于文章页面,我希望能在保留两者的强大功能的情况下,将播放器深度集成进网站,在查找源码询问ai以后一番折腾便有了如下教程,感谢ai

正文:在 Jasmine 主题首页集成 Meting APlayer 音乐播放器

前置说明

在实际部署中,三个项目的目录结构如下(并不在同一层级):

Typecho 根目录/
├── usr/
│   ├── plugins/
│   │   └── Meting/                ← Meting Aplayer 插件(重命名后放在这里)
│   │       ├── Plugin.php
│   │       ├── Action.php
│   │       ├── assets/
│   │       ├── driver/
│   │       └── include/
│   └── themes/
│       └── Jasmine/               ← Jasmine 主题
│           ├── index.php
│           ├── template-parts/
│           │   ├── header.php
│   │   ├── footer.php
│           │   └── ...
│           └── assets/
└── index.php

Meting-master/ 是 Meting-api 的 Node.js 源码仓库,仅用于参考或二次开发,不参与部署。实际运行时使用的是插件内 include/Meting.php(PHP 版本)。


第一步:安装 Meting Aplayer 插件

  1. Meting Aplayer/ 文件夹上传至 Typecho 的 usr/plugins/ 目录
  2. 将文件夹重命名为 Meting(这是必须的,插件内部通过 pluginUrl.'/Meting/assets' 引用资源)
  3. 进入 Typecho 后台 → 控制台 → 插件,启用 "APlayer-Typecho | Meting"
  4. 确认服务器已安装 PHP 的 curlopenssl 扩展

插件关键配置项(后台可调)

配置项 默认值 说明
theme #ad7a86 播放器主题色
height 340px 播放列表最大高度
autoplay false 是否自动播放
order list 播放顺序(list / random)
preload auto 音频预加载策略
bitrate 192 音质(128 / 192 / 320)
api 自动生成 云解析地址,一般无需修改
salt 自动生成 API 接口保护盐值,一般无需修改

第二步:理解播放器的工作机制

插件激活后会自动注入以下前端资源到所有页面:

  • header 钩子:注入 APlayer.min.cssAPlayer.min.js,以及全局变量 meting_api
  • footer 钩子:注入 Meting.min.js(负责发现 DOM 中的 .aplayer 元素并初始化播放器)
  • content 钩子:解析文章/页面内容中的 短代码,将其渲染为 <div class="aplayer" ...> DOM 元素

因此,只要页面中存在带有正确 data-* 属性的 .aplayer 元素,Meting.min.js 就会自动将其初始化为播放器。


第三步:在 Jasmine 首页添加播放器

Jasmine 主题的首页模板是 index.php,它按顺序加载 header.phpleft.phpnavbar.php、文章列表、right.phpfooter.php

有两种方式将播放器嵌入首页:

方式 A:直接修改 index.php 模板(推荐)

编辑 Jasmine/index.php,在文章列表区域的上方或下方插入一个 .aplayer 容器。
注意:插入位置取决于那个区域是主题的哪个组件在负责,例如右边就查找包含right字段的文件,根据名称可定位具体位置,在对应的位置插入这一行即可

插入位置示例(在文章列表上方,导航栏下方):

<?php $this->need('template-parts/navbar.php'); ?>

<!-- ↓ 在这里插入播放器 ↓ -->
<div id="home-player"></div>
<!-- ↑ 插入结束 ↑ -->

<div class="container-fluid p-4 d-flex flex-column row-gap-4" id="index-content">
    <?php if ($this->have()): ?>

然后在 Jasmine/template-parts/footer.php 中($this->footer() 之前),添加初始化脚本:

<script>
document.addEventListener('DOMContentLoaded', function() {
    var el = document.getElementById('home-player');
    if (el && typeof APlayer !== 'undefined' && typeof meting_api !== 'undefined') {
        var api = meting_api
            .replace(':server', 'netease')
            .replace(':type', 'playlist')
            .replace(':id', '你的歌单ID')
            .replace(':auth', '')
            .replace(':r', Math.random());

        var xhr = new XMLHttpRequest();
        xhr.open('GET', api, true);
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4 && xhr.status === 200) {
                var data = JSON.parse(xhr.responseText);
                new APlayer({
                    container: el,
                    mini: false,
                    autoplay: false,
                    lrcType: 3,
                    mutex: true,
                    preload: 'auto',
                    theme: '#ad7a86',
                    listMaxHeight: '340px',
                    order: 'list',
                    audio: data
                });
            }
        };
        xhr.send();
    }
});
</script>

你的歌单ID 替换为实际的歌单 ID(如网易云歌单 436843836)。平台和类型也可改为 tencentkugou 等。

不显示播放列表:如需保留播放器面板但隐藏列表,可使用 CSS:在 Typecho 后台 → 外观 → 设置 → 自定义头部 HTML 中添加 <style>.aplayer .aplayer-list{display:none!important}</style>

方式 B:通过主题 customFooter 配置注入(无需改模板文件)

Jasmine 主题在后台提供了 customFooter 配置项,内容会被注入到 footer.php 第 12 行 $this->options->customFooter() 处。

在 Typecho 后台 → 外观 → 设置 → 自定义底部 HTML 中填入:

<div id="home-player"></div>
<script>
document.addEventListener('DOMContentLoaded', function() {
    var el = document.getElementById('home-player');
    if (el && typeof APlayer !== 'undefined' && typeof meting_api !== 'undefined') {
        var api = meting_api
            .replace(':server', 'netease')
            .replace(':type', 'playlist')
            .replace(':id', '你的歌单ID')
            .replace(':auth', '')
            .replace(':r', Math.random());

        var xhr = new XMLHttpRequest();
        xhr.open('GET', api, true);
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4 && xhr.status === 200) {
                var data = JSON.parse(xhr.responseText);
                new APlayer({
                    container: el,
                    mini: false,
                    autoplay: false,
                    lrcType: 3,
                    mutex: true,
                    preload: 'auto',
                    theme: '#ad7a86',
                    listMaxHeight: '340px',
                    order: 'list',
                    audio: data
                });
            }
        };
        xhr.send();
    }
});
</script>

注意:这种方式下播放器会出现在页面 footer 区域,而非首页文章列表上方。如果只希望在首页显示,需要在脚本开头加判断:

if (window.location.pathname !== '/' && window.location.pathname !== '/index.php') return;

方式 C:仅在首页文章内容中使用短代码(最简单但需创建特殊文章)

如果你有一个专门的"音乐"页面,可以在文章中直接写:

但这种方式不能直接用于首页文章列表区域,因为 index.php 渲染的是文章卡片摘要,不会展开文章正文中的短代码。此方式仅适用于单篇文章/页面的正文。


第四步:主题调整(可选)

图标大小调整(可选)。直接编辑 Jasmine/assets/main/main.css,修改以下已有规则的数值:

/* 第 60-65 行,左侧栏容器尺寸 */
#left .nav-link {
    display: block;
    width: 56px;       /* 原 50px */
    height: 56px;      /* 原 50px */
    color: rgba(var(--link-color-rgba));
}

/* 第 67-75 行,左侧栏图标大小 */
#left .nav-link i {
    font-size: 28px;   /* 原 24px */
    padding-top: .75rem !important;
    padding-bottom: .75rem !important;
    padding-right: 1rem !important;
    padding-left: 1rem !important;
    border-radius: var(--bs-border-radius) !important;
    transition: all .15s ease-in-out;
}

手机端顶部导航栏和抽屉菜单的图标在 navbar.php 中通过 Bootstrap 的 fs-5 类控制大小,直接在 navbar.php 中将 fs-5 改为 fs-4 即可,或在 main.css 末尾追加:

/* 手机端导航图标(覆盖顶部栏、抽屉菜单、底部快捷入口) */
.navbar .nav-link i,
.offcanvas a i { font-size: 24px; }

注意:抽屉菜单底部的页面图标(首页、归档、友链等)是裸 <a> 标签,不带 .nav-link 类,所以用 .offcanvas a i 统一覆盖。


第五步:音乐控制按钮侧边显示与移动端适配

Jasmine 主题的左侧栏底部(template-parts/left.php)有"切换模式"和"返回顶部"两个按钮。在它们上方添加一个音乐播放/暂停按钮,使用 Tabler Icons 的 ti-music(播放中)和 ti-music-off(已暂停)。

5.1 修改 template-parts/left.php

在底部 <ul> 中,"切换模式"按钮之前插入以下 <li>

<ul class="nav flex-column nav-pills gap-0 row-gap-3 sticky-bottom bottom-0">
    <!-- ↓ 新增音乐控制按钮 ↓ -->
    <li class="nav-item d-flex justify-content-center position-relative" id="music-toggle-wrap" style="display:none!important;">
        <a class="nav-link p-0 d-flex align-items-center justify-content-center" id="music-toggle"
           href="javascript:void(0)" title="播放音乐">
            <i class="ti ti-music px-3 py-1 rounded" id="music-toggle-icon"></i>
        </a>
        <span class="position-absolute nav-item-title text-nowrap" id="music-toggle-title">播放音乐</span>
    </li>
    <!-- ↑ 新增结束 ↑ -->
    <li class="nav-item d-flex justify-content-center position-relative">
        <a class="nav-link p-0 d-flex align-items-center justify-content-center" id="bd-theme"

按钮默认隐藏(style="display:none!important"),等播放器初始化成功后再显示。

5.2 修改播放器初始化脚本

将第三步中的初始化脚本替换为以下版本(同时负责初始化播放器和绑定按钮事件):

<script>
document.addEventListener('DOMContentLoaded', function() {
    var el = document.getElementById('home-player');
    if (!el || typeof APlayer === 'undefined' || typeof meting_api === 'undefined') return;

    var api = meting_api
        .replace(':server', 'netease')
        .replace(':type', 'playlist')
        .replace(':id', '你的歌单ID')
        .replace(':auth', '')
        .replace(':r', Math.random());

    var xhr = new XMLHttpRequest();
    xhr.open('GET', api, true);
    xhr.onreadystatechange = function() {
        if (xhr.readyState !== 4 || xhr.status !== 200) return;
        var data = JSON.parse(xhr.responseText);
        var ap = new APlayer({
            container: el,
            mini: false,
            autoplay: false,
            lrcType: 3,
            mutex: true,
            preload: 'auto',
            theme: '#ad7a86',
            listMaxHeight: '340px',
            order: 'list',
            audio: data
        });

        // 暴露到全局,供按钮和 PJAX 回调使用
        window.homeAPlayer = ap;

        // 显示控制按钮
        var wrap = document.getElementById('music-toggle-wrap');
        if (wrap) wrap.style.display = '';

        // 绑定按钮点击事件
        var btn = document.getElementById('music-toggle');
        var icon = document.getElementById('music-toggle-icon');
        var title = document.getElementById('music-toggle-title');
        if (btn) {
            btn.addEventListener('click', function() {
                ap.toggle();
            });
        }

        // 监听播放/暂停事件,切换图标
        ap.on('play', function() {
            if (icon) {
                icon.classList.remove('ti-music-off');
                icon.classList.add('ti-music');
            }
            if (title) title.textContent = '暂停音乐';
        });
        ap.on('pause', function() {
            if (icon) {
                icon.classList.remove('ti-music');
                icon.classList.add('ti-music-off');
            }
            if (title) title.textContent = '播放音乐';
        });
    };
    xhr.send();
});
</script>

5.3 手机端适配

左侧栏在手机端被 d-none d-lg-block 完全隐藏,因此需要在 navbar.php 的手机端按钮区域中也添加一个音乐控制按钮。

编辑 Jasmine/template-parts/navbar.php,找到第 44 行的 <div class="d-flex d-block d-lg-none">,在主题切换按钮和汉堡菜单按钮之间插入:

<div class="d-flex d-block d-lg-none">
    <a class="nav-link p-0 d-flex align-items-center justify-content-center" id="bd-theme"
       href="javascript:changeBsTheme()">
        <i class="ti ti-sun-moon px-3 py-1 rounded fs-5"></i>
    </a>
    <!-- ↓ 新增手机端音乐控制按钮 ↓ -->
    <a class="nav-link p-0 d-flex align-items-center justify-content-center d-lg-none" id="music-toggle-mobile"
       href="javascript:void(0)" title="播放音乐" style="display:none;">
        <i class="ti ti-music px-3 py-1 rounded fs-5" id="music-toggle-icon-mobile"></i>
    </a>
    <!-- ↑ 新增结束 ↑ -->
    <button class="navbar-toggler border-0 pe-0" type="button" ...>

然后在播放器初始化脚本中,增加对手机端按钮的绑定。在 ap.on('pause', ...) 回调之后添加:

// 手机端按钮绑定
var btnMobile = document.getElementById('music-toggle-mobile');
var iconMobile = document.getElementById('music-toggle-icon-mobile');
if (btnMobile) {
    btnMobile.style.display = '';
    btnMobile.addEventListener('click', function() { ap.toggle(); });
}
ap.on('play', function() {
    if (iconMobile) {
        iconMobile.classList.remove('ti-music-off');
        iconMobile.classList.add('ti-music');
    }
});
ap.on('pause', function() {
    if (iconMobile) {
        iconMobile.classList.remove('ti-music');
        iconMobile.classList.add('ti-music-off');
    }
});

5.4 工作原理

  1. 播放器初始化成功后,脚本将 APlayer 实例挂载到 window.homeAPlayer
  2. 同时将 #music-toggle-wrapdisplay 设为空,使按钮可见
  3. 点击按钮调用 ap.toggle() 切换播放/暂停
  4. 通过 APlayer 的 playpause 事件同步切换图标:播放中显示 ti-music,暂停时显示 ti-music-off
  5. 鼠标悬停在按钮上时,左侧弹出的提示文字也会动态更新("播放音乐" / "暂停音乐")

第六步:PJAX 兼容(如适用)

如果 Jasmine 主题启用了 PJAX(页面无刷新切换),播放器在页面切换后不会自动销毁和重建。需要在主题的 PJAX 回调中添加:

// 页面切换前销毁播放器
if (typeof aplayers !== 'undefined') {
    for (var i = 0; i < aplayers.length; i++) {
        try { aplayers[i].destroy(); } catch(e) {}
    }
}

// 页面加载后重新初始化
if (typeof loadMeting === 'function') {
    loadMeting();
}

快速验证清单

  • Meting 插件文件夹已命名为 Meting 并放在 usr/plugins/
  • 插件已在 Typecho 后台启用
  • 服务器有 curlopenssl PHP 扩展
  • 首页源码中能看到 <div class="aplayer" ...> 元素
  • 浏览器控制台无 JS 报错
  • 播放器能正常加载歌曲列表并播放

支持的音乐平台与资源类型

平台 code 单曲 专辑 歌单 歌手
网易云音乐 netease song album playlist artist
QQ 音乐 tencent song album playlist artist
酷狗音乐 kugou song album playlist artist

示例:播放网易云歌单

示例:播放自定义音频

评论
森罗幻想. All Rights Reserved. Theme Jasmine_Plus by 罗伊