<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="/feeds/atom-style.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://artist.cc.cd/</id>
    <title>清静世界</title>
    <updated>2026-10-22T12:28:22.979Z</updated>
    <generator>Astro-Theme-Retypeset with Feed for Node.js</generator>
    <author>
        <name>Liviruy</name>
        <uri>https://artist.cc.cd/</uri>
    </author>
    <link rel="alternate" href="https://artist.cc.cd/"/>
    <link rel="self" href="https://artist.cc.cd/atom.xml"/>
    <subtitle>觅一处安静角落</subtitle>
    <rights>Copyright © 2026 Liviruy</rights>
    <entry>
        <title type="html"><![CDATA[康佳 LED55P7 实战：ADB 去开机广告、清理桌面广告，并用 TVBox 打造简洁电视]]></title>
        <id>https://artist.cc.cd/posts/konka-led55p7/</id>
        <link href="https://artist.cc.cd/posts/konka-led55p7/"/>
        <updated>2026-09-24T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[在 KONKA LED55P7 实机上用 ADB 把开机商业广告换成原厂开机片、清掉桌面/悬浮/屏保广告，并记录它的三层自愈机制与最终直接用 TVBox 观看的完整过程。]]></summary>
        <content type="html"><![CDATA[<blockquote>
<p>本文基于 <strong>KONKA LED55P7 实机操作</strong>整理，文中的系统版本、硬件参数、广告组件、文件路径和命令结果均以本机实际测试为依据。</p>
<p><strong>本文最终目标不是把康佳系统全部删除，而是：</strong></p>
<p><strong>开机跳过商业广告 → 进入桌面不再被广告打扰 → 直接打开 TVBox → 搜索资源并观看。</strong></p>
</blockquote>
<hr />
<h2>一、先看最终效果</h2>
<p>这次最终采用的方案其实比最开始设想的简单得多：</p>
<pre><code>电视开机
   ↓
原来的商业开机广告被替换
   ↓
进入康佳桌面
   ↓
桌面广告/悬浮广告/屏保广告被处理
   ↓
直接打开 TVBox
   ↓
搜索资源
   ↓
播放观看
</code></pre>
<p>本次最终实际达到的核心效果：</p>
<ul>
<li>
<p>开机商业广告被替换为原厂开机片</p>
</li>
<li>
<p>桌面广告、悬浮/角标、屏保/待机广告得到处理</p>
</li>
<li>
<p>TVBox 可以正常安装和运行</p>
</li>
<li>
<p>实际使用 TVBox 搜索和观看视频时没有出现额外广告干扰</p>
</li>
<li>
<p>没有刷机</p>
</li>
<li>
<p>没有删除系统 APK</p>
</li>
<li>
<p>没有更换原厂 Launcher</p>
</li>
<li>
<p>投屏相关组件没有被关闭</p>
</li>
<li>
<p>最终没有为了“彻底清空广告链”继续修改更多系统组件</p>
</li>
</ul>
<p><strong>做到这里，就停止了。</strong></p>
<p>这也是本文最终采用的思路：</p>
<blockquote>
<p><strong>不要为了“看起来更干净”而继续修改已经不影响实际使用的系统部分。</strong></p>
</blockquote>
<hr />
<h2>二、为什么不把康佳所有广告 APK 全部删掉？</h2>
<p>最开始可能会很容易产生一种想法：</p>
<pre><code>发现一个广告 APK
↓
删掉

再发现一个
↓
再删掉

直到全部删光
</code></pre>
<p>实际操作后发现，这种做法并不适合这台电视。</p>
<p>因为康佳的广告并不是一个 APK 独立完成的，而是一整条链路：</p>
<pre><code>广告服务器
    ↓
广告素材下载
    ↓
广告程序
    ↓
桌面/屏保/待机/悬浮窗口
</code></pre>
<p>而且系统本身还有自动更新、自启动和 Provider 依赖。</p>
<p>所以：</p>
<blockquote>
<p><strong>广告已经不影响使用以后，就没有必要继续为了“少几个 APK”去冒险。</strong></p>
</blockquote>
<p>本次实机最终选择的是：</p>
<pre><code>解决真正影响观看的问题
        ↓
安装自己需要的播放器
        ↓
绕开康佳自己的内容入口
        ↓
达到目标后停止修改
</code></pre>
<p>而不是追求：</p>
<pre><code>康佳系统一个广告程序都不剩
</code></pre>
<hr />
<h2>三、当贝市场：可选，不是必装</h2>
<p>这一点需要特别说明。</p>
<h3>当贝市场是什么？</h3>
<p>简单来说：</p>
<blockquote>
<p><strong>当贝市场就是电视应用商店。</strong></p>
</blockquote>
<p>它适合：</p>
<pre><code>想经常安装各种电视 APK
        ↓
希望直接在电视上搜索应用
        ↓
不想每次都通过电脑 ADB 安装
</code></pre>
<p>如果你的需求只是：</p>
<pre><code>搜索资源
↓
看视频
</code></pre>
<p>那么当贝市场<strong>完全不是必需品</strong>。</p>
<p>本次实际使用中，我最终<strong>没有安装当贝市场</strong>。</p>
<p>所以本文把它定义为：</p>
<pre><code>当贝市场：可选
</code></pre>
<p>以后需要安装其他电视应用的时候，再通过 ADB 装上也可以。</p>
<hr />
<h2>四、我的电视到底是什么配置？</h2>
<p>虽然机器外壳写的是：</p>
<pre><code>KONKA LED55P7
</code></pre>
<p>但 Android 系统实际报告的是：</p>
<pre><code>Konka Android TV 9632
KKMT9632
</code></pre>
<p>实机检测结果：</p>
<pre><code>型号：KONKA LED55P7
平台：MediaTek MT9632
ro.hardware：m7332
CPU：4 核 ARMv7
Android：9 / API 28
RAM：894,288 kB，约 872 MiB
eMMC：8 GB
ABI：armeabi-v7a, armeabi
ABI64：为空
</code></pre>
<p>也就是说：</p>
<blockquote>
<p><strong>这是一台纯 32 位 ARM Android TV。</strong></p>
</blockquote>
<p>这一点对安装 TVBox 非常重要。</p>
<hr />
<h2>五、为什么网上康佳教程不能直接照抄？</h2>
<p>因为不同康佳电视：</p>
<pre><code>不同主板
不同 SoC
不同 Android 版本
不同固件
不同权限
不同广告组件
</code></pre>
<p>可能完全不同。</p>
<p>例如这台 LED55P7 实测存在：</p>
<pre><code>su
SELinux Permissive
没有 dm-verity
userdebug 固件
</code></pre>
<p>因此它能够做的一些事情，普通零售康佳电视未必能做。</p>
<p>但这里一定要记住：</p>
<blockquote>
<p><strong>权限高 ≠ 什么都应该删。</strong></p>
</blockquote>
<p>本文最后没有采用刷机、删 <code>/system</code>、改系统分区等方式，就是因为“能做到”和“值得做到”是两回事。</p>
<hr />
<h2>六、第一步：开启 ADB</h2>
<p>这台康佳电视可以通过工程菜单开启 ADB。</p>
<p>实际操作：</p>
<pre><code>遥控器
↓
依次输入 2008
↓
进入工程菜单
↓
开启 ADB 调试
↓
查看电视 IP
</code></pre>
<p>例如（示例地址，请替换为电视实际 IP）：</p>
<pre><code>192.168.1.100
</code></pre>
<hr />
<h2>七、电脑端准备 ADB</h2>
<p>Windows 电脑准备：</p>
<pre><code>adb.exe
AdbWinApi.dll
</code></pre>
<p>本次实机使用：</p>
<pre><code>ADB 1.0.41
</code></pre>
<p>如果 ADB 没加入系统 PATH，可以直接进入 <code>adb.exe</code> 所在目录。</p>
<p>例如 PowerShell：</p>
<pre><code>cd "你的 adb.exe 所在目录"
</code></pre>
<p>然后：</p>
<pre><code>.\adb.exe devices
</code></pre>
<hr />
<h2>八、连接电视</h2>
<p>示例使用的是：</p>
<pre><code>192.168.1.100:5555
</code></pre>
<p>连接：</p>
<pre><code>.\adb.exe connect 192.168.1.100:5555
</code></pre>
<p>再：</p>
<pre><code>.\adb.exe devices
</code></pre>
<p>正常应该看到：</p>
<pre><code>192.168.1.100:5555    device
</code></pre>
<p>注意：</p>
<blockquote>
<p><strong>5555 是这台电视实际使用的端口，不代表所有 Android TV 都固定使用 5555。</strong></p>
</blockquote>
<p>如果自己的电视连接不上，不要直接假设是 ADB 坏了，需要先确认电视实际开放的 ADB 端口。</p>
<hr />
<h2>九、正式修改之前：先做只读体检</h2>
<p>这一部分非常重要。</p>
<p>不要一上来执行：</p>
<pre><code>pm disable-user
</code></pre>
<p>更不要：</p>
<pre><code>pm uninstall
</code></pre>
<p>先查看：</p>
<h3>Android 版本</h3>
<pre><code>adb shell getprop ro.build.version.release
</code></pre>
<h3>固件</h3>
<pre><code>adb shell getprop ro.build.display.id
</code></pre>
<h3>主板</h3>
<pre><code>adb shell getprop ro.product.board
</code></pre>
<h3>硬件</h3>
<pre><code>adb shell getprop ro.hardware
</code></pre>
<h3>CPU ABI</h3>
<pre><code>adb shell getprop ro.product.cpu.abilist
adb shell getprop ro.product.cpu.abilist64
</code></pre>
<h3>Root</h3>
<p>这台电视的 <code>su</code> 不是 Magisk 常见语法，而是：</p>
<pre><code>adb shell "su 0 id"
</code></pre>
<p>正常：</p>
<pre><code>uid=0(root)
</code></pre>
<p>不要写：</p>
<pre><code>adb shell "su -c id"
</code></pre>
<p>本机实测这种写法会报：</p>
<pre><code>su: invalid uid/gid '-c'
</code></pre>
<p>正确语法是：</p>
<pre><code>su [UID] [命令]
</code></pre>
<p>例如：</p>
<pre><code>adb shell "su 0 id"
adb shell "su 0 sh -c '命令'"
</code></pre>
<hr />
<h2>十、检查 SELinux 和 dm-verity</h2>
<p>SELinux：</p>
<pre><code>adb shell getenforce
</code></pre>
<p>本机：</p>
<pre><code>Permissive
</code></pre>
<p>检查 dm-verity：</p>
<pre><code>adb shell "ls /dev/dm-*"
</code></pre>
<p>以及：</p>
<pre><code>adb shell "cat /proc/cmdline" | grep -o "androidboot.verifiedbootstate=[a-z]*"
</code></pre>
<p>本机没有发现 dm-verity 正常工作的迹象，bootloader 状态为：</p>
<pre><code>orange
</code></pre>
<p>但是这里不要误解：</p>
<blockquote>
<p><strong>没有发现 dm-verity，并不代表修改系统分区就是安全的。</strong></p>
</blockquote>
<p>它只是说明少了一层完整性校验机制。</p>
<hr />
<h2>十一、先找出康佳广告链路</h2>
<p>执行：</p>
<pre><code>adb shell pm list packages | grep konka
</code></pre>
<p>Windows PowerShell 也可以：</p>
<pre><code>adb shell pm list packages | findstr /i "konka"
</code></pre>
<p>实际梳理后，主要的广告相关组件包括：</p>
<pre><code>com.konka.systemadvert
com.konka.adverttool
com.konka.sticker
com.konka.downloadcenter
com.konka.vadr
com.konka.message
com.konka.appupgrade
</code></pre>
<p>大致可以理解成：</p>
<pre><code>systemadvert
↓
负责广告显示

downloadcenter
↓
下载广告素材

vadr
↓
设备/数据上报

message
↓
推送消息

appupgrade
↓
后台更新
</code></pre>
<hr />
<h2>十二、最关键的发现：开机广告不是普通 APK</h2>
<p>这是整个实验里最值得记录的一点。</p>
<p>很多人会以为：</p>
<pre><code>com.konka.systemadvert
</code></pre>
<p>就是全部广告。</p>
<p>但实际上这台电视的开机广告是由：</p>
<pre><code>/system/bin/bootvideo
</code></pre>
<p>这个原生服务播放的。</p>
<p>它读取：</p>
<pre><code>/data/misc/konka/AdBoot/AdBootMedia/bootvideo.mp4
</code></pre>
<p>因此：</p>
<blockquote>
<p><strong>禁用 <code>com.konka.systemadvert</code>，并不能直接让开机 MP4 消失。</strong></p>
</blockquote>
<p>它解决的是：</p>
<pre><code>屏保
悬浮窗
待机广告
待机唤醒广告
角标
</code></pre>
<p>而开机广告视频本身需要单独处理。</p>
<p>这也是很多网上“禁用某个康佳广告 APK 就完事”的教程，在这台电视上并不成立的原因。</p>
<hr />
<h2>十三、第一批广告组件：优先使用 disable</h2>
<p>本次第一批处理：</p>
<pre><code>adb shell pm disable-user --user 0 com.konka.adverttool
adb shell pm disable-user --user 0 com.konka.sticker
adb shell pm disable-user --user 0 com.konka.fourkshow
adb shell pm disable-user --user 0 fusion.android.tv.demo
</code></pre>
<p>这里故意使用：</p>
<pre><code>disable-user
</code></pre>
<p>而不是：</p>
<pre><code>uninstall
</code></pre>
<p>原因很简单：</p>
<pre><code>disable
↓
关闭
↓
以后可以 enable 恢复
</code></pre>
<p>而：</p>
<pre><code>uninstall
↓
改变系统状态
↓
恢复更加麻烦
</code></pre>
<p>对于老电视这种实验性改造：</p>
<blockquote>
<p><strong>可逆性比“删得干净”重要。</strong></p>
</blockquote>
<hr />
<h2>十四、广告总闸：systemadvert</h2>
<p>然后处理真正的核心广告程序：</p>
<pre><code>adb shell pm disable-user --user 0 com.konka.systemadvert
</code></pre>
<p>检查：</p>
<pre><code>adb shell pm list packages -d
</code></pre>
<p>以及：</p>
<pre><code>adb shell dumpsys package com.konka.systemadvert | grep enabled
</code></pre>
<p>Windows：</p>
<pre><code>adb shell dumpsys package com.konka.systemadvert | findstr /i "enabled"
</code></pre>
<p>本机实测：</p>
<pre><code>enabled=3
</code></pre>
<p>也就是：</p>
<pre><code>COMPONENT_ENABLED_STATE_DISABLED_USER
</code></pre>
<p>注意：</p>
<pre><code>enabled=3
</code></pre>
<p>只能说明用户级禁用状态，不能单独作为“整个服务彻底没有运行”的证明。</p>
<p>最终需要结合：</p>
<pre><code>实际进程
实际广播
实际功能
重新启动后的状态
</code></pre>
<p>一起判断。</p>
<hr />
<h2>十五、开机广告第一次尝试：chmod，失败</h2>
<p>最初的想法很简单：</p>
<pre><code>让广告程序不能写广告 MP4
</code></pre>
<p>于是尝试：</p>
<pre><code>chmod 0555 /data/misc/konka/AdBoot/AdBootMedia
</code></pre>
<p>但是重启后：</p>
<pre><code>0555
↓
恢复成 0777
</code></pre>
<p>广告 MP4 又被系统换成了新的广告素材。</p>
<p>后来定位到了具体原因：</p>
<pre><code>/vendor/etc/init/hw/init.m7332.rc
</code></pre>
<p>里面有：</p>
<pre><code>mkdir /data/misc/konka                          0777 system system
mkdir /data/misc/konka/AdBoot                   0777 system system
mkdir /data/misc/konka/AdBoot/AdBootAnimation   0777 system system
mkdir /data/misc/konka/AdBoot/KonkaAdBootAnimation 0777 system system
mkdir /data/misc/konka/AdBoot/AdBootMedia       0777 system system
</code></pre>
<p>Android <code>init</code> 每次开机都会执行这些初始化操作。</p>
<p>因此：</p>
<pre><code>chmod 0555
↓
重启
↓
init
↓
恢复 0777
↓
广告程序重新写文件
</code></pre>
<p>得到的结论是：</p>
<blockquote>
<p><strong>单纯靠目录 chmod 防止广告回写，这台电视上是经不起一次重启的。</strong></p>
</blockquote>
<hr />
<h2>十六、最终有效方案：锁住广告文件本身</h2>
<p>既然目录权限会被重新设置，那么就不锁目录，直接锁文件。</p>
<p>本机发现原厂无广告开机视频位于：</p>
<pre><code>/kkpanel/link/bootmedia/bootvideo.mp4
</code></pre>
<p>本机文件大小：</p>
<pre><code>3,475,603 B
</code></pre>
<p>将它复制到广告 MP4 的位置：</p>
<pre><code>adb shell "su 0 sh -c 'cp -f /kkpanel/link/bootmedia/bootvideo.mp4 /data/misc/konka/AdBoot/AdBootMedia/bootvideo.mp4
chown system:system /data/misc/konka/AdBoot/AdBootMedia/bootvideo.mp4
chmod 0444 /data/misc/konka/AdBoot/AdBootMedia/bootvideo.mp4'"
</code></pre>
<p>然后：</p>
<pre><code>adb shell "su 0 toybox chattr +i /data/misc/konka/AdBoot/AdBootMedia/bootvideo.mp4"
</code></pre>
<p>检查：</p>
<pre><code>adb shell "su 0 toybox lsattr /data/misc/konka/AdBoot/AdBootMedia/bootvideo.mp4"
</code></pre>
<p>正常：</p>
<pre><code>----i--------
</code></pre>
<p>这里的：</p>
<pre><code>i
</code></pre>
<p>就是 immutable。</p>
<p>意思可以简单理解为：</p>
<blockquote>
<p><strong>即使程序重新拿到写权限，也不能直接修改这个文件。</strong></p>
</blockquote>
<hr />
<h2>十七、为什么 <code>chattr +i</code> 必须这样验证？</h2>
<p>如果用：</p>
<pre><code>su 0
</code></pre>
<p>测试文件写保护，没有意义。</p>
<p>因为：</p>
<pre><code>root
</code></pre>
<p>本来就拥有最高权限。</p>
<p>所以真正有意义的测试是模拟广告程序身份，例如：</p>
<pre><code>adb shell "su 1000 sh -c 'echo x &gt; /data/misc/konka/AdBoot/AdBootMedia/bootvideo.mp4'"
</code></pre>
<p>本机测试结果为：</p>
<pre><code>Operation not permitted
</code></pre>
<p>这才说明普通系统身份确实无法改。</p>
<p>而且文件本身使用 immutable 后：</p>
<pre><code>root
↓
也不能直接 rm
</code></pre>
<p>必须先：</p>
<pre><code>chattr -i
</code></pre>
<p>才能恢复修改。</p>
<hr />
<h2>十八、千万不要给 AdBootAnimation 目录乱放占位文件</h2>
<p>本机：</p>
<pre><code>AdBootAnimation/
</code></pre>
<p>目前是空的。</p>
<p>不要为了“保险”随便塞一个假的：</p>
<pre><code>bootanimation.zip
</code></pre>
<p>因为：</p>
<blockquote>
<p>如果系统以后真的切换到这个路径播放，而里面是损坏的占位文件，反而可能把开机动画搞坏。</p>
</blockquote>
<p>所以：</p>
<blockquote>
<p><strong>空目录就保持空目录。</strong></p>
</blockquote>
<hr />
<h2>十九、必须做真实冷启动验证</h2>
<p>这是整个方法里很重要的一步。</p>
<p>不能只看：</p>
<pre><code>lsattr
</code></pre>
<p>也不能只看：</p>
<pre><code>enabled=3
</code></pre>
<p>真正验证：</p>
<pre><code>冷启动
↓
Android 完整启动
↓
重新读取 bootvideo
↓
看 immutable 是否还存在
↓
看 MD5 是否改变
</code></pre>
<p>本机真实冷启动后：</p>
<pre><code>lsattr
----i--------
</code></pre>
<p>并且：</p>
<pre><code>MD5
</code></pre>
<p>仍然保持原厂片的 MD5：</p>
<pre><code>c27ed929a2f9ca8d0053f73cb69d34d7
</code></pre>
<p>说明：</p>
<blockquote>
<p><strong>本机当前固件下，这套开机广告替换 + immutable 防回写方案已经通过真实冷启动验证。</strong></p>
</blockquote>
<p>注意这里的表述是：</p>
<blockquote>
<p><strong>本机实测有效。</strong></p>
</blockquote>
<p>不要扩展成：</p>
<blockquote>
<p>所有康佳电视都适用。</p>
</blockquote>
<p>不同型号必须重新检测。</p>
<hr />
<h2>二十、一个非常容易踩的坑：遥控器“关机”不一定是真关机</h2>
<p>这台电视存在：</p>
<pre><code>com.konka.quickstandby
</code></pre>
<p>它使用 STR 快速待机机制。</p>
<p>也就是说：</p>
<pre><code>按遥控器关机
↓
不一定真正关闭 Android
↓
可能进入假待机
</code></pre>
<p>这种情况下：</p>
<pre><code>uptime
</code></pre>
<p>可能不会归零。</p>
<p>因此测试开机广告时，不要把普通待机唤醒当成真正冷启动。</p>
<p>可以通过：</p>
<pre><code>adb shell getprop ro.boot.bootreason
</code></pre>
<p>以及：</p>
<pre><code>adb shell cat /proc/uptime
</code></pre>
<p>辅助判断。</p>
<p>本机真正的冷启动测试，必须确保 Android 实际重新启动。</p>
<hr />
<h2>二十一、最重要的三层“自愈机制”</h2>
<p>这次实验最大的价值之一，就是发现康佳电视并不是“你禁掉了它，它就永远没了”。</p>
<h3>第一层：应用自动更新</h3>
<p><code>com.konka.appupgrade</code> 会进行后台应用更新。</p>
<p>如果某个系统 APK 被重新安装：</p>
<pre><code>原来的 disable 状态
↓
重新安装
↓
恢复默认启用
</code></pre>
<p>因此：</p>
<blockquote>
<p><strong>先断更新链，再禁广告包</strong>，是继续做深度精简时更合理的顺序。</p>
</blockquote>
<p>但本文最终没有继续做这一步，因为到 TVBox 正常使用以后，实际目标已经达成。</p>
<hr />
<h3>第二层：init.rc 重置目录权限</h3>
<p>这就是之前：</p>
<pre><code>chmod 0555
</code></pre>
<p>重启失效的原因。</p>
<p>所以：</p>
<blockquote>
<p><strong>依赖目录权限的广告防回写方案，不适合这台电视。</strong></p>
</blockquote>
<p>最终采用的是锁文件本身。</p>
<hr />
<h3>第三层：PERSISTENT 进程</h3>
<p>有一些系统应用带：</p>
<pre><code>PERSISTENT
</code></pre>
<p>即使禁用，进程也可能仍存在。</p>
<p>因此：</p>
<pre><code>enabled=3
</code></pre>
<p>不等于：</p>
<pre><code>进程一定不存在
</code></pre>
<p>本机 <code>fourkshow</code> 就曾经出现过这种情况。</p>
<p>所以对于这种程序：</p>
<blockquote>
<p>最终应该看实际进程、PSS、广播和功能，而不是只看一个状态值。</p>
</blockquote>
<hr />
<h2>二十二、为什么不要随便禁 appupgrade？</h2>
<p>这一点对于以后继续折腾电视的人尤其重要。</p>
<p>本机审计发现：</p>
<pre><code>com.konka.appupgrade
</code></pre>
<p>暴露了 Provider。</p>
<p>而：</p>
<pre><code>com.konka.livelauncher
com.konka.tvsettings
com.konka.market.main
</code></pre>
<p>等组件会访问这些 Provider。</p>
<p>因此：</p>
<pre><code>直接整包禁用 appupgrade
</code></pre>
<p>有可能产生：</p>
<pre><code>桌面查询异常
设置查询异常
应用商店异常
</code></pre>
<p>更安全的办法，是未来真的要处理它时，先进行 Provider 依赖审计，再考虑只关闭：</p>
<pre><code>BootCompleteReceiver
AlarmReceiver
AppUpgradeService
</code></pre>
<p>也就是说：</p>
<blockquote>
<p><strong>系统 APK 在禁用之前，一定先查 Provider 和组件依赖。</strong></p>
</blockquote>
<p>不要看到名字像“更新”，就直接整包砍掉。</p>
<hr />
<h2>二十三、为什么没有把系统 APK 全部删除？</h2>
<p>因为：</p>
<pre><code>能删
≠
值得删
</code></pre>
<p>例如：</p>
<pre><code>com.konka.tvsettings
</code></pre>
<p>负责系统设置。</p>
<pre><code>com.android.packageinstaller
</code></pre>
<p>负责安装 APK。</p>
<pre><code>com.konka.mirror
</code></pre>
<p>负责手机镜像。</p>
<pre><code>com.konka.kkmultiscreen
</code></pre>
<p>负责多屏互动。</p>
<pre><code>com.konka.livelauncher
</code></pre>
<p>是当前系统桌面。</p>
<p>这些东西虽然不一定与你“看视频”直接相关，但删除以后可能造成：</p>
<pre><code>设置进不去
投屏失效
APK 装不上
没有桌面
电视功能异常
</code></pre>
<p>所以最终采取：</p>
<blockquote>
<p><strong>只处理明确对应广告、并且经过验证可以处理的组件。</strong></p>
</blockquote>
<hr />
<h2>二十四、为什么最终直接使用 TVBox？</h2>
<p>这才是本次改造真正的“终点”。</p>
<p>我的实际需求并不是：</p>
<pre><code>安装很多电视应用
</code></pre>
<p>而是：</p>
<pre><code>搜索资源
↓
看视频
</code></pre>
<p>所以只需要：</p>
<pre><code>TVBox
</code></pre>
<p>作为主要入口。</p>
<p>整个使用方式变成：</p>
<pre><code>康佳原厂系统
       ↓
负责底层电视功能
       ↓
TVBox
       ↓
搜索资源
       ↓
观看视频
</code></pre>
<p>而不是：</p>
<pre><code>康佳首页
↓
康佳推荐
↓
康佳内容平台
↓
广告
↓
广告
↓
广告
</code></pre>
<p>最终实际使用 TVBox 时没有出现额外广告干扰，因此后续广告链没有再继续处理。</p>
<hr />
<h2>二十五、TVBox 安装前一定检查 ABI</h2>
<p>这台电视是：</p>
<pre><code>armeabi-v7a
armeabi
</code></pre>
<p>同时：</p>
<pre><code>ro.product.cpu.abilist64
</code></pre>
<p>为空。</p>
<p>所以这是：</p>
<blockquote>
<p><strong>纯 32 位 Android TV。</strong></p>
</blockquote>
<p>TVBoxOSC 一个 Release 可能同时提供：</p>
<pre><code>java
java32
java64
python
python32
python64
</code></pre>
<p>对于这台机器：</p>
<pre><code>java32
</code></pre>
<p>最合适。</p>
<p>不要随便安装：</p>
<pre><code>arm64-v8a
</code></pre>
<p>版本。</p>
<hr />
<h2>二十六、为什么 APK 不能“下载下来能打开就算合法”？</h2>
<p>这是另一个实际踩过的坑。</p>
<p>某个 APK 即使：</p>
<pre><code>文件名是 .apk
file 命令说 Android package
</code></pre>
<p>也不能证明：</p>
<pre><code>文件完整
</code></pre>
<p>下载过程中如果文件被截断：</p>
<pre><code>file
</code></pre>
<p>仍然可能把它识别成 APK。</p>
<p>更可靠的方法是：</p>
<h3>1. 获取官方 Release 的 SHA-256</h3>
<p>例如从 GitHub Release API 获取：</p>
<pre><code>digest
</code></pre>
<h3>2. 本地计算</h3>
<pre><code>sha256sum TVBox.apk
</code></pre>
<h3>3. 对比</h3>
<p>必须：</p>
<pre><code>完全一致
</code></pre>
<p>再安装。</p>
<hr />
<h2>二十七、TVBox 安装流程</h2>
<p>先确认 APK：</p>
<pre><code>TVBox_..._java32.apk
</code></pre>
<p>再：</p>
<pre><code>adb install -r TVBox.apk
</code></pre>
<p>成功：</p>
<pre><code>Success
</code></pre>
<p>查真实包名：</p>
<pre><code>adb shell pm list packages | grep -i tvbox
</code></pre>
<p>不要自己猜包名。</p>
<p>例如本机实际包名可能是：</p>
<pre><code>com.github.tvbox.osc.jun
</code></pre>
<p>然后：</p>
<pre><code>adb shell monkey -p com.github.tvbox.osc.jun 1
</code></pre>
<p>实际使用时必须以你自己电视查询出来的真实包名为准。</p>
<hr />
<h2>二十八、为什么不在教程里提供影视接口？</h2>
<p>TVBox 本身只是：</p>
<pre><code>播放器 / 聚合框架
</code></pre>
<p>真正决定里面显示什么内容的是：</p>
<pre><code>配置地址 / 内容源
</code></pre>
<p>所以本文不提供来历不明的影视接口，也不推荐从陌生网站下载配置。</p>
<p>使用时：</p>
<pre><code>TVBox
↓
设置
↓
配置地址
↓
填写你自己选择并确认可以使用的内容源
</code></pre>
<p>这样可以把：</p>
<pre><code>软件本身
</code></pre>
<p>和：</p>
<pre><code>内容来源
</code></pre>
<p>分开。</p>
<p>另外：</p>
<blockquote>
<p><strong>“TVBox 没有广告”不等于“所有内容源的视频流永远没有广告”。</strong></p>
</blockquote>
<p>本次实机使用的配置在实际观看过程中没有出现额外广告，因此本文将其作为<strong>本机实测结果</strong>记录，而不是对所有内容源作绝对保证。</p>
<hr />
<h2>二十九、内存只有约 872 MiB，这是为什么要尽量简单？</h2>
<p>这台电视实际总内存：</p>
<pre><code>894,288 kB
</code></pre>
<p>约：</p>
<pre><code>872 MiB
</code></pre>
<p>Android 开机后本身就已经占用了相当一部分。</p>
<p>而且系统判断：</p>
<pre><code>status critical
</code></pre>
<p>因此：</p>
<blockquote>
<p><strong>这台电视并不适合安装一大堆常驻后台的电视 App。</strong></p>
</blockquote>
<p>这也是为什么这次方案选择：</p>
<pre><code>原厂底层
+
一个主要播放器
</code></pre>
<p>而不是：</p>
<pre><code>十几个电视应用同时安装
</code></pre>
<hr />
<h2>三十、不要直接把 RSS 相加算内存</h2>
<p>这是另一个很容易误判的问题。</p>
<p>例如：</p>
<pre><code>ps -A -o RSS,NAME
</code></pre>
<p>看到很多进程以后，直接全部相加，会得到一个非常夸张的数字。</p>
<p>原因是共享内存会被多个进程重复计算。</p>
<p>本机同一时间：</p>
<pre><code>RSS 合计约 1,647 MB
</code></pre>
<p>但电视总 RAM 只有：</p>
<pre><code>约 872 MB
</code></pre>
<p>显然不可能真的用了 1.6GB 独占内存。</p>
<p>所以真正分析内存，建议看：</p>
<pre><code>adb shell dumpsys meminfo
</code></pre>
<p>重点关注：</p>
<pre><code>PSS
</code></pre>
<p>而不是把所有 RSS 简单相加。</p>
<hr />
<h2>三十一、一个重要经验：一次只改一个变量</h2>
<p>本次最初的第一轮实验其实同时做了：</p>
<pre><code>替换开机视频
+
chmod
+
禁用多个广告包
</code></pre>
<p>结果出现问题以后，需要重新回头定位：</p>
<pre><code>到底是哪一步失效
</code></pre>
<p>这很浪费时间。</p>
<p>因此以后再改这种老设备：</p>
<pre><code>只改一项
↓
验证
↓
需要重启时人工重启
↓
验证
↓
再改下一项
</code></pre>
<p>会可靠得多。</p>
<hr />
<h2>三十二、以后不要让 Agent 自动重启电视</h2>
<p>这个必须单独写进教程。</p>
<p>不要在 Agent 指令里允许：</p>
<pre><code>adb reboot
</code></pre>
<p>或者：</p>
<pre><code>adb shell reboot
</code></pre>
<p>或者：</p>
<pre><code>adb shell su 0 reboot
</code></pre>
<p>原因很简单：</p>
<p>这台电视真正冷启动之后：</p>
<pre><code>Wi-Fi
↓
ADB
↓
5555
</code></pre>
<p>可能需要接近：</p>
<pre><code>110 秒
</code></pre>
<p>才完全恢复。</p>
<p>中途可能出现：</p>
<pre><code>offline
ping 不通
5555 关闭
</code></pre>
<p>这并不一定代表电视坏了。</p>
<p>而且：</p>
<blockquote>
<p><strong>开机广告到底有没有被解决，只有人在电视屏幕前才能真正确认。</strong></p>
</blockquote>
<p>因此最合理的模式是：</p>
<pre><code>Agent
↓
执行修改
↓
发现需要重启
↓
告诉你
↓
停止
↓
你自己决定是否重启
</code></pre>
<hr />
<h2>三十三、什么时候才需要继续深度清理？</h2>
<p>只有当你发现：</p>
<pre><code>还有康佳广告
</code></pre>
<p>或者：</p>
<pre><code>后台一直自动安装东西
</code></pre>
<p>或者：</p>
<pre><code>内存已经影响 TVBox 使用
</code></pre>
<p>才有必要继续分析：</p>
<pre><code>downloadcenter
vadr
message
appupgrade
market
PERSISTENT 服务
</code></pre>
<p>而不是单纯为了“系统看起来更干净”继续删除。</p>
<p>本次最终没有继续处理这些后续链路，就是因为：</p>
<blockquote>
<p><strong>TVBox 已经绕过了康佳的主要内容入口，而且实际观看已经达到了预期效果。</strong></p>
</blockquote>
<hr />
<h2>三十四、完整回退思路</h2>
<p>如果以后想恢复广告相关组件，可以：</p>
<pre><code>adb shell pm enable com.konka.systemadvert
adb shell pm enable com.konka.adverttool
adb shell pm enable com.konka.sticker
adb shell pm enable com.konka.fourkshow
adb shell pm enable fusion.android.tv.demo
</code></pre>
<p>如果要恢复开机广告文件：</p>
<p>首先解除 immutable：</p>
<pre><code>adb shell "su 0 toybox chattr -i /data/misc/konka/AdBoot/AdBootMedia/bootvideo.mp4"
</code></pre>
<p>然后恢复备份：</p>
<pre><code>adb shell "su 0 sh -c 'cp -f /data/local/konka_ad_backup/ad_current.mp4 /data/misc/konka/AdBoot/AdBootMedia/bootvideo.mp4'"
</code></pre>
<p>注意：</p>
<blockquote>
<p><strong>必须先 <code>chattr -i</code>，再执行 <code>cp</code> 或 <code>rm</code>。</strong></p>
</blockquote>
<p>否则 immutable 文件连 root 都无法正常覆盖。</p>
<hr />
<h2>三十五、本文最终采用的实际方案</h2>
<p>最终没有走：</p>
<pre><code>刷第三方 ROM
↓
替换 Launcher
↓
删除系统 APK
↓
修改 /system
↓
修改 /vendor
</code></pre>
<p>而是：</p>
<pre><code>① 开启 ADB
      ↓
② 只读检测电视
      ↓
③ 找出康佳广告链
      ↓
④ 禁用低风险广告组件
      ↓
⑤ 禁用 systemadvert
      ↓
⑥ 找到真实开机广告 MP4
      ↓
⑦ 使用原厂开机片覆盖
      ↓
⑧ chattr +i 锁住文件
      ↓
⑨ 做真实冷启动验证
      ↓
⑩ 安装 TVBox
      ↓
⑪ 实际使用 TVBox 搜索/播放
      ↓
⑫ 达到目标
      ↓
⑬ 停止继续修改
</code></pre>
<hr />
<h2>三十六、最终状态</h2>
<p>以本次实际操作结束时的状态为准：</p>
<pre><code>✅ 开机商业广告
   → 已替换为原厂开机片
   → 使用 chattr +i 锁定
   → 已通过真实冷启动验证

✅ 桌面广告
   → 核心广告组件已处理
   → 桌面悬浮/角标/屏保/待机广告得到处理

✅ TVBox
   → 已安装
   → 可以正常启动
   → 可以搜索资源并观看
   → 本次实际使用没有出现额外广告

✅ 原厂桌面
   → 保留
   → 没有为了换桌面而冒额外风险

✅ 手机投屏
   → 相关 `mirror / kkmultiscreen` 组件没有关闭

✅ 系统底层
   → 没有刷机
   → 没有删除系统 APK
   → 没有修改 `/system`
   → 没有修改 `/vendor`
   → 没有修改 init.rc
   → 没有修改 build.prop

⭕ 当贝市场
   → 可选，不是必装
   → 本次实际没有安装

⏸ 后续广告链
   → 没有继续处理
   → 因为 TVBox 已经绕开康佳内容入口
   → 实际观看目标已经达到
</code></pre>
<hr />
<h2>三十七、最终复盘：这次真正值得记录的是什么？</h2>
<p>这次改造最值得记录的，不是某一条 ADB 命令，而是几个实际踩出来的结论。</p>
<h3>1. 康佳广告不是一个 APK</h3>
<p>它是一条链：</p>
<pre><code>下载
↓
渲染
↓
推送
↓
更新
</code></pre>
<p>所以必须先查清楚谁负责什么。</p>
<h3>2. 开机广告和桌面广告是两件事</h3>
<p>开机广告实际上是：</p>
<pre><code>bootvideo
+
bootvideo.mp4
</code></pre>
<p>而不是单纯的：</p>
<pre><code>systemadvert
</code></pre>
<h3>3. chmod 目录不可靠</h3>
<p>因为：</p>
<pre><code>init.rc
</code></pre>
<p>会在开机重新建立目录并恢复权限。</p>
<h3>4. immutable 文件锁是本机实测有效的办法</h3>
<p>不是“网上说有效”。</p>
<p>而是：</p>
<pre><code>替换原厂片
↓
chattr +i
↓
真实冷启动
↓
MD5 不变
↓
实测通过
</code></pre>
<p>这才算真正验证。</p>
<h3>5. enabled=3 不代表一切都结束</h3>
<p>还要看：</p>
<pre><code>进程
广播
PSS
功能
重启后的状态
</code></pre>
<h3>6. 不要为了彻底而彻底</h3>
<p>这次最大的经验其实是：</p>
<blockquote>
<p><strong>当电视已经没有明显广告，并且 TVBox 已经满足观看需求时，就没有必要再继续修改更多系统组件。</strong></p>
</blockquote>
<hr />
<h2>三十八、最终结果，用一句话概括</h2>
<p>这次不是把康佳电视“魔改成另一个系统”，而是：</p>
<blockquote>
<p><strong>保留原厂系统负责电视本身的功能，关闭真正影响体验的广告，把观看视频的入口交给 TVBox。</strong></p>
</blockquote>
<p>最终变成：</p>
<pre><code>开机
 ↓
没有商业开机广告
 ↓
进入桌面
 ↓
没有明显广告干扰
 ↓
打开 TVBox
 ↓
搜索资源
 ↓
直接观看
</code></pre>
<p>这就是这台 <strong>KONKA LED55P7</strong> 本次实机改造最终采用的方案。</p>
]]></content>
        <author>
            <name>Liviruy</name>
            <uri>https://artist.cc.cd/</uri>
        </author>
        <published>2026-09-24T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[博客音乐播放器：按需加载、歌词滚动与封面自动匹配]]></title>
        <id>https://artist.cc.cd/posts/music-player-lyrics/</id>
        <link href="https://artist.cc.cd/posts/music-player-lyrics/"/>
        <updated>2026-09-21T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[给静态博客加一个会滚歌词的音乐播放器：首页常驻代码 0.28KB，播放器只在第一次点击时才进浏览器；歌词用对象存储上的 LRC，专辑封面由构建期脚本从公开 API 匹配并缓存。附六个真实 bug 的根因与修法。]]></summary>
        <content type="html"><![CDATA[<p>大多数博客播放器的做法是把播放器常驻在页面里：即使没人点，首屏也要背上几十 KB 的 JS、CSS 和一轮音频元数据请求。这篇记录的是另一条路线——<strong>首页只留一个按钮，其余全部延后</strong>，以及在这之上做的歌词滚动和专辑封面自动匹配。</p>
<p>所有数字都是构建产物和浏览器里实测的，不是估算。文末附六个踩到的真 bug，其中两个是"自测全绿、真机全废"那一类。</p>
<h2>成果概览</h2>
<table>
<thead>
<tr>
<th>指标</th>
<th>实测值</th>
</tr>
</thead>
<tbody>
<tr>
<td>首页常驻音乐代码</td>
<td>0.28 KB gzip（原始 0.4 KB），不含任何 DOM</td>
</tr>
<tr>
<td>首屏音乐相关请求</td>
<td><strong>0</strong></td>
</tr>
<tr>
<td>第一次点击 ♪ 新增</td>
<td>3 个：播放器 JS 5.0 KB、CSS 2.5 KB、<code>songs.json</code> 0.7 KB（压缩后传输体积，合计约 8.1 KB）</td>
</tr>
<tr>
<td>封面 / 歌词</td>
<td>面板打开后各自按需再拉，切歌才拉下一首的</td>
</tr>
<tr>
<td>音频</td>
<td><code>&lt;audio preload="none"&gt;</code>，不点播放不下载一个字节</td>
</tr>
<tr>
<td>运行时第三方 API</td>
<td><strong>0</strong>（封面与元数据在构建期就落进 JSON）</td>
</tr>
<tr>
<td>依赖</td>
<td>无。原生 <code>&lt;audio&gt;</code> + <code>&lt;dialog&gt;</code> + <code>fetch</code>，图标是内联的 Phosphor 官方 SVG path</td>
</tr>
</tbody>
</table>
<h2>一、架构：首页只留一个按钮</h2>
<p>入口组件不渲染任何标记，只挂一段脚本：</p>
<pre><code>// MusicLauncher.astro —— 构建后 0.28KB gzip
if (!window.__musicLauncherBound) {
  window.__musicLauncherBound = true
  let busy = false
  document.addEventListener('click', async (event) =&gt; {
    if (!(event.target instanceof Element) || !event.target.closest('#music-toggle')) {
      return
    }
    if (busy) return
    busy = true
    try {
      const { toggleMusicPanel } = await import('./music-player')
      await toggleMusicPanel()
    }
    catch { /* 拉取失败时按钮保持可用，下次点击重试 */ }
    finally { busy = false }
  })
}
</code></pre>
<p><code>window.__musicLauncherBound</code> 那道闸门不是洁癖：Astro 在 ClientRouter 换页后会重跑普通 <code>&lt;script&gt;</code>，不加守卫每次导航就会多叠一层委托监听。</p>
<p>播放器本体是一个懒加载模块，内部只有一个 <code>new Audio()</code> 和一个 <code>&lt;dialog&gt;</code>，DOM 在首次点击时才建。三个坑值得单独记：</p>
<p><strong>CSS 会被提走。</strong> Astro 会把模块里 <code>import './x.css'</code> 的样式提升到每页的 <code>&lt;link rel="stylesheet"&gt;</code>——那就白懒加载了。改成 <code>import cssUrl from './music-player.css?url'</code>，挂载时用 JS 注入 <code>&lt;link id="music-player-css"&gt;</code>，并在 <code>astro:page-load</code> 里幂等重注入（ClientRouter 换 <code>&lt;head&gt;</code> 会把它带走）。</p>
<p><strong>dialog 要跨换页存活。</strong> <code>&lt;dialog&gt;</code> 挂在 <code>body</code> 上，SPA 换页把 body 子节点清空后，节点本身还活着，<code>page-load</code> 里重新 <code>append</code> 回去即可——<strong>正在播的歌不会被打断</strong>，也不需要把播放状态提升到全局。</p>
<p><strong>进度刷新别用 setInterval。</strong> 播放中用 <code>requestAnimationFrame</code> 驱动进度条，暂停即自然停；面板关闭时短路掉刷新逻辑。</p>
<h2>二、控件做减法</h2>
<p>播放行最后只剩三个键：上一首 / 播放 / 下一首。随机播放和"顺序 ↔ 单曲循环"两态按钮都被砍掉了——歌单只有几首的时候它们没有意义。</p>
<p>音量换成手机信号格那样的 5 根阶梯柱：</p>
<pre><code>const VOLUME_STEPS = [20, 40, 60, 80, 100]   // 柱高 6/10/14/18/22px，底部对齐
</code></pre>
<p>整组是一个 pointer 表面（柱子本身 <code>pointer-events: none</code>）：<strong>点击</strong>吸附到最近一档，<strong>拖动</strong>连续取值，<strong>滑过左缘</strong>即静音。因为静音由滑动承担了，原来那枚喇叭图标也删了。</p>
<blockquote>
<p>这里有个教训：音量柱是底部对齐的阶梯，它的<strong>视觉重心</strong>比外接框中心低约 4px。按 DOM 几何把喇叭图标和柱子"对齐"了三次，肉眼看还是歪的。最后靠删图标解决——观感问题要用眼睛验收，不能只看 <code>getBoundingClientRect()</code>。</p>
</blockquote>
<h2>三、歌词滚动</h2>
<p>歌词就是一个标准 <code>.lrc</code>，和 mp3 一起放在对象存储里：</p>
<pre><code>[ti:戒不掉（原声版）]
[ar:欧阳耀莹]
[00:14.58]今天开始新出发吗
[00:18.88]留过了疤
</code></pre>
<p>解析用一个正则，<code>[ti:]</code> <code>[ar:]</code> <code>[offset:]</code> 这类元数据因为冒号前不是数字而天然不匹配，剥掉标签后为空的行再丢一次，所以不会渲染出空行。一行多个时间戳也会全部收下。</p>
<p><strong>滚动不用 <code>scrollTop</code>，用 <code>transform</code>。</strong> 当前行居中靠给整列加一个位移：</p>
<pre><code>const y = boxHeight / 2 - (activeLine.offsetTop + activeLine.offsetHeight / 2)
list.style.transform = `translateY(${y}px)`
</code></pre>
<p>好处是位置完全可控、能加 450ms 缓动、上下留白由遮罩决定，也不会和浏览器原生滚动行为打架。上下淡出用一条渐变遮罩，非当前行降透明度并加极轻的模糊：</p>
<pre><code>.mp-lyrics {
  overflow: clip;
  mask-image: linear-gradient(to bottom, transparent, #000 32%, #000 68%, transparent);
}
.mp-lyric-line { opacity: 0.32; filter: blur(0.4px); }
.mp-lyric-line.is-active { opacity: 1; font-weight: 600; filter: none; }
</code></pre>
<p>手动浏览也加在了这套位移模型上：拖拽和滚轮改的是"在自动居中之上再叠一个偏移量"，松手 2.5 秒后把偏移量归零、滑回当前行。拖动过程中歌曲继续往前唱，高亮照常换行，但会反向补偿偏移量让画面纹丝不动，否则你正在看的那句会被突然推走。</p>
<p>点击任意一句 → 跳到该句时间并起播。<strong>每一行是真正的 <code>&lt;button&gt;</code></strong>，<code>aria-label</code> 带上时间（<code>1:12 喜欢这触摸俘虏我</code>），键盘 Tab + Enter 也能跳。</p>
<p>歌词只在面板打开、且这首歌配了 <code>.lrc</code> 时才 <code>fetch</code> 一次，结果按曲目缓存在内存里，重开面板零请求（实测 <code>extraFetches: 0</code>）。没有歌词的曲目整块区域 <code>display: none</code>，布局回到从前。</p>
<h2>四、专辑封面自动匹配（构建期）</h2>
<p>只给歌名和歌手，脚本自动去找专辑封面。数据源优先级：<strong>iTunes Search → MusicBrainz + Cover Art Archive → 返回 null</strong>。</p>
<p>iTunes 这一步的实测结果直接决定了实现：</p>
<table>
<thead>
<tr>
<th>请求</th>
<th>结果</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>country=CN</code></td>
<td><strong>0 条</strong>（国区搜索接口不给结果）</td>
</tr>
<tr>
<td>默认 US</td>
<td>返回 <code>Sunny Day / Cool Music / Piano Instrumental</code> 一类同名钢琴翻弹——典型错配</td>
</tr>
<tr>
<td><code>country=TW</code> / <code>HK</code></td>
<td>✅ <code>晴天 / 周杰倫 / 葉惠美</code>，Live 版排在第 2 位</td>
</tr>
</tbody>
</table>
<p>所以查询要打 TW + HK 两个店，并且<strong>不能取第一条了事</strong>，要评分：</p>
<ul>
<li>归一化（NFKC、小写、剥标点空白）后歌名全等 → 高分</li>
<li>歌手是区分同名翻唱的唯一屏障，判得更严：等长且共享过半字符才算同一名字的异体写法</li>
<li>原版查询撞上 <code>Live / Remix / 伴奏 / 翻唱</code> 等版本标记 → 直接丢弃，不进候选</li>
<li>专辑名含 <code>精选 / Best / OST</code> → 降级</li>
<li>两个店同时命中同一 <code>trackId</code> → 互相印证加分</li>
</ul>
<p>封面尺寸按 Apple 的 URL 规则替换即可任意放大：<code>/100x100bb.jpg</code> → <code>/300x300bb.jpg</code>。实测同一张图 <code>100x100bb</code> 3.0 KB、<code>200x200bb</code> 7.1 KB、<code>300x300bb</code> 14.7 KB、<code>600x600bb</code> 82 KB。<strong>取 300</strong>：封面在面板里最大只画到 74px（歌单行 46px），3 倍屏也只要 222px，用 600 等于让首次点开面板多背 65 KB 却看不出差别。</p>
<p><strong>三态输出，宁缺勿滥</strong>：</p>
<table>
<thead>
<tr>
<th>状态</th>
<th>含义</th>
<th>是否写封面</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>matched</code></td>
<td>标题异体或全等 + 有封面 + 是首位结果 + 无并列候选 + 歌手全等或两店互证</td>
<td>✅</td>
</tr>
<tr>
<td><code>review</code></td>
<td>差一点（例如结果标题多了 <code>(原聲版)</code> 后缀）</td>
<td>❌，但把前 3 个候选连封面一起存下来给人看</td>
</tr>
<tr>
<td><code>not_found</code></td>
<td>没有可靠结果</td>
<td>❌</td>
</tr>
</tbody>
</table>
<p><code>review</code> 用 <code>--accept &lt;id&gt;</code> 一键提升为 <code>matched</code>。写盘前还会对封面 URL 发一次 HEAD，不可达就降级——往 <code>songs.json</code> 里写一个坏地址，等于亲手给播放器做破图。</p>
<p>首跑三首歌的真实结果：</p>
<pre><code>[1/3] 戒不掉 - 欧阳耀莹              ! review（候选 8 分：戒不掉 (原聲版) / 歐陽耀瑩）
[2/3] 春娇与志明 - 街道办GDC/欧阳耀莹  ✓ Apple Music  专辑：春嬌與志明 - Single
[3/3] 我们好像在哪见过 - 杨宗纬/叶蓓    ✗ Apple Music  ✗ MusicBrainz  ! 未找到封面
</code></pre>
<p>第 3 首是<strong>正确</strong>的失败：Apple 的繁中/港曲库里只有同名翻唱，MusicBrainz 里最接近的是薛之谦的《我好像在哪见过你》——不是同一首歌。找不到就留空，播放器会回落到"歌名首字"的字母砖。</p>
<blockquote>
<p>版权：第三方封面图片的版权/使用许可需要根据来源和具体用途确认，不要默认认为 API 返回的图片可以任意再分发。这条提示也写在了脚本头部注释里。</p>
</blockquote>
<h2>五、踩到的六个坑</h2>
<p><strong>1. 重开面板后歌词偏 459.6px。</strong> <code>display: none</code> 的元素 <code>getBoundingClientRect()</code> 全是 0。居中量的是窗口高度，而渲染发生在 <code>showModal()</code> 之前，于是按 0 高度算出了位移。修法：先 <code>showModal()</code> 再渲染。</p>
<p><strong>2. 真机上点歌词完全不跳。</strong> <code>setPointerCapture</code> 会把后续的 <code>pointerup</code> 和它派生的 <code>click</code> <strong>一起改派到捕获元素</strong>，于是 <code>event.target.closest('.mp-lyric-line')</code> 取不到那一句。更阴的是：自测时用的是合成 <code>PointerEvent</code>，<code>pointerId</code> 不存在，<code>setPointerCapture</code> 直接抛错、根本没形成捕获，于是测试一路绿灯。修法：跳转改在 <code>pointerup</code> 里做，用的是 <code>pointerdown</code> 当时记下的行（<code>pointerdown</code> 早于同一个 handler 里设置捕获，target 可信）；<code>click</code> 监听只服务键盘。</p>
<blockquote>
<p>顺带一个测试陷阱：合成 <code>MouseEvent('click')</code> 不传 <code>detail</code> 时默认是 <code>0</code>，会被判成键盘激活。测指针点击必须显式 <code>detail: 1</code>。</p>
</blockquote>
<p><strong>3. 聚焦把歌词容器滚走了。</strong> 歌词行改成可聚焦的 <code>&lt;button&gt;</code> 之后，浏览器在聚焦时会滚动 <code>overflow: hidden</code> 的容器（它仍是可编程滚动的），实测内部 <code>scrollTop</code> 被推了 628px，居中全废。修法：<code>overflow: clip</code>——它不是滚动容器。</p>
<p><strong>4. 切歌从上一首的时间点继续播。</strong> <code>audio.src = ...; audio.load()</code> 之后 <code>currentTime</code> 并不保证立刻归零，实测新曲从 0:07 开始，歌词也跟着先跳再弹回。修法：显式 <code>audio.currentTime = 0</code>，并且在新媒体元数据就绪前不采信 <code>currentTime</code>。</p>
<p><strong>5. 短视口下播放键被裁出卡片。</strong> 歌词窗口占了固定高度后，卡片内容 366px &gt; 可用 246px，播放键整个滚不到。修法：删掉"窄屏换行"分支，改成始终三列 + <code>height: clamp(4.6rem, 22vh, 8.5rem)</code>——矮屏退回封面自身高度，控件永不被裁。</p>
<p><strong>6. 拖拽边界和自动居中打架。</strong> 边界最初写成"内容不能露出空白"（<code>clamp(box - content, 0)</code>），但自动居中时首行上方本来就留了半窗空白，两套规则互相否定：往下拖 60px 被错误夹到 0，而首行的居中位其实是 +24.5。修法：以首行、末行各自的居中位作为上、下限，手动与自动落在同一条连续刻度上。</p>
<h2>六、搬到你自己的项目</h2>
<p>涉及的文件：</p>
<pre><code>src/components/music/MusicLauncher.astro   常驻入口，无 DOM
src/components/music/music-player.ts       懒加载播放器（JS/CSS/数据/音频全在这里延后）
src/components/music/music-player.css      面板样式（以 ?url 方式引入）
src/pages/music/songs.json.ts              构建期产出 dist/music/songs.json
src/config/music.ts                        手写曲表（id/title/artist/url/lyrics）
src/data/music.ts                          合并自动元数据，产出播放器用的字段
src/data/music-metadata.json               匹配结果 + 缓存（脚本写，前端读）
scripts/music-meta.ts                      封面匹配脚本
</code></pre>
<p>三步接入：</p>
<ol>
<li>把音频和 <code>.lrc</code> 传到对象存储，在 <code>src/config/music.ts</code> 的 <code>tracks</code> 里各写一条</li>
<li><code>pnpm music-meta</code> 匹配封面（之后 <code>pnpm build</code>，元数据在构建期就固化了）</li>
<li>在导航里放一个 <code>id="music-toggle"</code> 的按钮，在 Layout 末尾挂 <code>&lt;MusicLauncher /&gt;</code></li>
</ol>
<p>日常命令：</p>
<pre><code>pnpm music-meta                    # 只处理未匹配成功的，已 matched 跳过
pnpm music-meta --force            # 全部重查
pnpm music-meta --song 晴天 周杰伦  # 单曲试查，只打印不写盘
pnpm music-meta --list-review      # 看哪些在等人工确认
pnpm music-meta --accept &lt;id&gt;      # 把 review 的首选提升为 matched
</code></pre>
<p>限速是硬要求：iTunes 每请求间隔 500ms，MusicBrainz 1200ms（它要求 ≤1 req/s 且必须带可识别的 <code>User-Agent</code>），超时 15 秒，5xx/429 退避重试两次。批量绝不并发。</p>
<p>最后一点心得：这套东西真正花时间的地方不在功能，而在**"看起来对了"和"在真机上对了"之间的缝隙**——<code>display: none</code> 下量不到尺寸、捕获改派了 click、聚焦悄悄滚了容器，这三条自测全都没抓到。所以凡是涉及视觉与手势的改动，最后一定要用真实指针走一遍。</p>
]]></content>
        <author>
            <name>Liviruy</name>
            <uri>https://artist.cc.cd/</uri>
        </author>
        <published>2026-09-21T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[用 EdgeOne 部署 Twikoo 评论系统]]></title>
        <id>https://artist.cc.cd/posts/edgeone-twikoo/</id>
        <link href="https://artist.cc.cd/posts/edgeone-twikoo/"/>
        <updated>2026-09-19T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[本主题内置 Twikoo 评论支持。本文记录如何用腾讯云 EdgeOne Makers 免费部署 Twikoo 后端（twikoo-eo...]]></summary>
        <content type="html"><![CDATA[<p>本主题内置 <a href="https://twikoo.js.org/">Twikoo</a> 评论支持。本文记录如何用腾讯云 EdgeOne Makers 免费部署 Twikoo 后端（<a href="https://github.com/Mintimate/twikoo-eo">twikoo-eo</a>），并将评论系统接入博客。相比 Vercel + MongoDB 的方案，这个方式<strong>不需要数据库、免费额度充足、自带全球 CDN</strong>，管理也更简单。</p>
<p>文中示例域名 <code>comments.example.com</code>、<code>your-site.com</code> 请替换为你自己的域名。</p>
<h2>部署 Twikoo 后端</h2>
<ol>
<li>注册腾讯云账号并开通 EdgeOne 服务</li>
<li>前往 GitHub fork <a href="https://github.com/Mintimate/twikoo-eo">Mintimate/twikoo-eo</a> 到你的账号下</li>
<li>EdgeOne 控制台 → <strong>服务总览 → Makers → 创建项目 → 导入 Git 仓库</strong>，选择你的 fork，等待自动构建部署</li>
<li>部署完成后，项目会分配一个默认访问域名（形如 <code>xxx.edgeone.app</code>）。浏览器访问它，看到如下 JSON 即为部署成功：</li>
</ol>
<pre><code>{"code":0,"message":"Twikoo 云函数运行正常...","version":"1.7.19"}
</code></pre>
<p>存储使用 Makers 自带的 Blob，首次请求自动初始化，<strong>无需任何数据库配置</strong>。到这一步，其实已经可以直接把默认域名填入 <code>envId</code> 使用。</p>
<h2>绑定自定义域名（可选）</h2>
<p>默认分配域名可用但不好记。若想绑定自己的子域名（如 <code>comments.example.com</code>），按以下步骤：</p>
<h3>1. 添加域名</h3>
<p>在 Makers 项目的「域名」中添加自定义域名。<strong>加速区域选择注意</strong>：</p>
<ul>
<li>域名已 ICP 备案 → 可选「全球（含中国内地）」，大陆节点速度最佳</li>
<li>未备案域名 → 只能选「不包含中国内地」，大陆访客仍可正常访问（走海外节点），只是延迟为海外级</li>
</ul>
<h3>2. 验证域名所有权</h3>
<p>EdgeOne 会要求添加一条 <strong>TXT 记录</strong>（形如 <code>edgeonereclaim.comments</code>，值以 <code>reclaim-</code> 开头）。到你的 DNS 托管商（如 Cloudflare）添加：</p>
<table>
<thead>
<tr>
<th>字段</th>
<th>填法</th>
</tr>
</thead>
<tbody>
<tr>
<td>类型</td>
<td>TXT</td>
</tr>
<tr>
<td>名称</td>
<td>EdgeOne 给出的主机记录（只填子域前缀部分）</td>
</tr>
<tr>
<td>内容</td>
<td>那串 token，原样粘贴</td>
</tr>
</tbody>
</table>
<blockquote>
<p>若提示该子域曾被其他账号认领（<code>reclaim</code> 验证不通过），换一个子域名前缀重新添加即可。</p>
</blockquote>
<h3>3. 添加 CNAME 记录</h3>
<p>TXT 验证通过后，EdgeOne 会给出真正的 <strong>CNAME 记录值</strong>（形如 <code>xxx.edgeone.traffic</code>）。回到 DNS 托管商添加 CNAME：</p>
<ul>
<li>名称：<code>comments</code>（前缀，托管商会自动补全）</li>
<li>目标：EdgeOne 给的记录值</li>
<li><strong>代理状态必须选「仅 DNS」（灰云）</strong>，否则流量会被双重代理，验证不通过</li>
</ul>
<h3>4. 申请 HTTPS 证书</h3>
<p>在域名的「HTTPS / SSL 证书」设置中开启免费证书（自动验证签发，等待几分钟）。<strong>这一步是必须的</strong>：博客本身是 HTTPS 站点，浏览器会拦截对 HTTP 接口的请求（混合内容），<code>envId</code> 只能填 <code>https://</code> 地址。</p>
<h2>主题配置</h2>
<p>修改 <a href="https://github.com/radishzzz/astro-theme-retypeset/blob/master/src/config.ts">src/config.ts</a> 的 <code>comment</code> 部分，共两处：</p>
<pre><code>comment: {
  // 开启评论系统
  enabled: true
  // twikoo 评论系统
  twikoo: {
    envId: 'https://comments.example.com' // 不带末尾斜杠
  }
  // waline 评论系统
  waline: {
    // 服务器地址
    serverURL: '' // ⚠️ 必须清空！
  }
}
</code></pre>
<blockquote>
<p>⚠️ 主题的渲染逻辑是"配置了谁就渲染谁，可叠加"。如果从 Waline 切换过来，<strong>必须清空 <code>waline.serverURL</code></strong>，否则页面会出现两个评论区。</p>
</blockquote>
<p>重新构建并部署博客即可。主题对 Twikoo 的支持开箱即用：滚动到评论区才懒加载、中英文文章自动使用各自语言、<code>comment.css</code> 中已适配主题字体与深色模式。</p>
<h2>管理评论</h2>
<p>这个部署方式<strong>没有独立的 <code>/admin</code> 管理页面</strong>，管理入口集成在评论区：</p>
<ol>
<li>打开任意有评论的文章，滚动到底部评论区</li>
<li>点击评论区右下角的<strong>齿轮图标</strong></li>
<li>首次进入要求<strong>设置管理员密码</strong>——这就是唯一的管理凭证</li>
</ol>
<p>之后删除评论、标记垃圾、导出导入数据、配置邮件通知（SMTP）都在这个面板中完成。注意两点：</p>
<ul>
<li>管理员是"密码制"而非账号制：知道密码的人都是管理员，请保管好密码，可随时在面板设置中更换</li>
<li>普通访客的身份（accessToken）只能管理自己的评论，无法进入后台</li>
</ul>
<h2>常见问题</h2>
<p><strong>评论提交报错 CORS 相关？</strong>
Makers 项目环境变量 <code>CORS_ALLOW_ORIGIN</code> 控制允许的来源域名（逗号分隔，如 <code>your-site.com,www.your-site.com</code>）。<strong>不设置 = 允许所有来源</strong>，个人博客保持默认即可；后续收紧时修改后需重新部署生效。</p>
<p><strong>表情栏偶尔加载不出来？</strong>
OωO 表情资源默认从 Twikoo 作者站点拉取，如遇不稳定可参考 <a href="https://twikoo.js.org/frontend.html">Twikoo 表情配置</a> 自托管。</p>
<p><strong>改完配置评论没变化？</strong>
确认 <code>src/config.ts</code> 修改后重新执行了构建部署；Makers 侧改环境变量则需在「部署」页点「重新部署」（或在关联仓库 push 一次提交自动触发）。</p>
]]></content>
        <author>
            <name>Liviruy</name>
            <uri>https://artist.cc.cd/</uri>
        </author>
        <published>2026-09-19T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[《儿子，别回来》]]></title>
        <id>https://artist.cc.cd/posts/son-dont-come-back/</id>
        <link href="https://artist.cc.cd/posts/son-dont-come-back/"/>
        <updated>2026-09-06T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[一位父亲写给儿子的一封信]]></summary>
        <content type="html"><![CDATA[<h1>儿子，别回来</h1>
<p>&lt;img title="" src="https://music.artist.cc.cd/images/76.webp" alt="远行" width="613"&gt;</p>
<p>儿子，你去年去德国的时候，我没送你。你今年回德国，我也不送你。去年我给你写信，写的是：<strong>飞得再远，你的根还连着我们。</strong> 但根这个东西，不是我们替你长好的，根是你自己往下扎的。你在马格德堡那间没有空调的寝室里，一个人过了整个冬天。没有你妈喊你吃饭，没有小区门口那棵樟树，你照样活过来了。在波兰过春节，在意大利看庞贝古城，在巴黎看埃菲尔铁塔，那就是你的根，再往土里钻。我不该把你当成一棵必须长在院子里的树苗。所以今年我想说：  <strong>别回来了。</strong> 这几个字，我写下来的时候手抖了一下。你知道，一个当父亲的要把这几个字说出口不容易。不是不爱，是爱到了深处，才敢这么说。真正的爱有时候不是挽留，是推你出去，再关上门，然后自己站在门后，听你的脚步声越来越远。</p>
<p>&lt;br&gt;&lt;br&gt;</p>
<p>世界正在急剧变化，高墙不仅是在地缘的边界，更应在我们每个人的心里。我们站在历史的十字路口，看着远方的硝烟和近处的围墙。旧的叙事正在崩塌，新的神话尚未诞生。我们唯一能捂住的，或许就是那份源自内心的清醒。那就是不被宏大的口号所裹挟，警惕所有以保护为名的剥夺。而你正处在世界的褶皱里，能听到旧结构嘎吱作响的声音。那是历史在换轨。你这一代人的命运，注定是在两条或者多条轨道之间自己铺路。</p>
<p>&lt;br&gt;&lt;br&gt;</p>
<p>你说还是国内方便、舒服。但是现代化不单单是硬件，你要看到每年1000多万年轻人从大学里出来，像潮水一样涌向有限的城市、有限的岗位、有限的机会。不是他们不够好，是这片土地还不知道如何装下这么多飞鸟。你回头看看你那些留在国内的同学，有几个真正地在做自己喜欢的事？有几个不是在卷，在熬，在等？你不一样，你有一张可以飞出去的票。如果你把这张票糟蹋了，回头硬挤进一个不适合你的罐头里，那才叫对不起你自己，也对不起家人。</p>
<p>&lt;br&gt;&lt;br&gt;</p>
<p>你在德国，先要把自己变成一个在世界任何地方都能站住脚的人。一个真正的现代人，有中国人的筋骨，有不同肤色的人能和你共事。有东北人的倔，你懂得在规则里迂回。有上海人的务实，但不被精致的利己主义捆住手脚。你从我们这里拿到的不是一份需要偿还的债，而是一份需要你用在自己身上的翅膀。</p>
<p><strong>翅膀硬了，才属于你。</strong></p>
<p>你不用，就是一堆羽毛，风一吹就散了。</p>
<p>&lt;br&gt;&lt;br&gt;</p>
<p>我不跟你说“落叶归根”了。这四个字，是中国农业时代留给我们的惯性。你这一代人早就不是土壤里长出来的树，而是被风带走的种子。 <strong>落地生根比落叶归根重要得多。</strong> 哪里的土养人，就在哪里长成树。你要做的是世界公民，不是哪条村的游子。</p>
<p>&lt;br&gt;&lt;br&gt;</p>
<p>你外婆问你明年回不回来，你说看情况。这个回答有分寸，有决断，有年轻人该有的清醒。我希望你明年不回来，后年也不回来。你能请我们过去，让我们看看你的新天地，那当然好。如果我们去不了，你也不用歉疚。你能在视频里让我看看你的书架，看看你窗外的那条路，看看你走路带风的样子，就足够了。</p>
<p>&lt;br&gt;&lt;br&gt;</p>
<p>你爸这一辈子，十岁从怀德走到长春，四十五岁从长春走到上海。我是在中国的地图上用脚底板丈量自己。你今天用的是世界地图。马格德堡只是第一站。柏林的图书馆在等你，慕尼黑的实验室在等你，苏黎世的银行在等你，旧金山的车库在等你。不是说你一定要去，而是说当这些门还开着的时候，要赶快一点。以后门会一扇扇关上。你正站在门还留着一道缝的时刻，趁现在侧身进去。进去之后，就别老回头张望门口有没有人送你。</p>
<p>&lt;br&gt;&lt;br&gt;</p>
<p>儿子，你飞的是地球，将来你的孩子也许能到月球或者火星上去。一代人有一代人的远方。你不能因为眷恋家里的沙发，就把自己绑在客厅里。我也不会因为你不在病床前，就觉得你不孝。你只要在外面活得好，活得正，活得有劲儿，你就是最好的儿子。</p>
<p>&lt;br&gt;&lt;br&gt;</p>
<p>你走吧，把根扎在遥远的地方去。哪一天你在外面撑起了自己的天，那一天就是对我们最好的告慰。我们不会站在路口等你，但你心里知道，家永远为你在这个世界上留着一盏灯。只是那盏灯不是用来照你回来的路，而是用来告诉你：</p>
<p><strong>你从哪里来，你永远有来处。</strong></p>
<h1>飞吧，儿子</h1>
<p><strong>把世界踩在脚下。</strong></p>
]]></content>
        <author>
            <name>Liviruy</name>
            <uri>https://artist.cc.cd/</uri>
        </author>
        <published>2026-09-06T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[DIY电脑配置]]></title>
        <id>https://artist.cc.cd/posts/computer-configuration/</id>
        <link href="https://artist.cc.cd/posts/computer-configuration/"/>
        <updated>2026-08-23T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[分享一套 DIY 电脑配置，以及选择硬件时的预算和搭配思路。]]></summary>
        <content type="html"><![CDATA[<p>今天来讲一下我的DIY电脑配置，买的时候行情还行，还没大涨价，自己动手DIY电脑其实不难，这给我省了不少钱，不过就是需要点时间去了解一下，先讲个电脑DIY大致的流程吧。</p>
<h5>最大预算</h5>
<p>这个没什么好说的，取决自己有多少钱能买得上哪个层级的电脑，先看口袋有多少钱，再去看对应价位的产品，建议少搜那些高端的看，因为看得太好的话，容易对下层价位产品产生看不上的想法，一对比后虚荣心就会作祟了，就得不断的提前消费买买买了，当然是有钱的话另当别论了，直接全部最顶级就是了。</p>
<h5>自身需求</h5>
<p>配电脑当然第一步是清楚自己用电脑来是干什么，比如，打游戏、办公。这两个是前者囊括了后者，因为能打游戏的电脑对于办公这种场景来说是小意思，所以想什么都玩一玩的，还是尽量往高一点的配置来靠，但是这又得花上不少钱，对于预算卡得很紧的就没有办法了，只能在预算中选最优解，所以是以上面第一步为基准的。</p>
<h5>产品对比</h5>
<p>考虑好前两步后，就是要自己去了解电脑硬件产品，然后多方面进行对比了，组成电脑主机所需要的硬件有以下八样， <code>CPU</code> ，<code>主板</code>， <code>内存</code> ，<code>显卡</code>，<code>硬盘</code>，<code>散热器</code>，<code>电源</code>，<code>机箱</code>。最后一个机箱随便自己喜欢，不要也不会影响性能，反正就一个壳子。显卡最重要，负责画面输出，所以要占预算的大头，再就是CPU，到内存，再到硬盘，电源，散热器。可以直接在网上搜，对应的预算的电脑大概的配置，再根据自己的需要或喜好，哪一部分应该减点预算加到另一个上面，使得性能最大化。</p>
<p>&lt;style type="text/css"&gt;&lt;/style&gt;</p>
<table>
<thead>
<tr>
<th>配置</th>
<th>推荐品牌（一线 + 二三线）</th>
<th>简要说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>CPU（处理器）</strong></td>
<td>AMD、Intel</td>
<td>AMD游戏性价比高；Intel多核与兼容性好。</td>
</tr>
<tr>
<td><strong>主板</strong></td>
<td>华硕（ASUS）、微星（MSI）、技嘉（GIGABYTE）、华擎（ASRock） <strong>二三线</strong>：七彩虹（Colorful）、铭瑄（MAXSUN）、映泰（Biostar）</td>
<td>一线BIOS完善、稳定性强。七彩虹、铭瑄性价比高、扩展够用，适合中低端；映泰做工不错，价格友好。</td>
</tr>
<tr>
<td><strong>内存（RAM）</strong></td>
<td>金士顿（Kingston）、海盗船（Corsair）、芝奇（G.Skill）、三星（Samsung） <strong>二三线</strong>：十铨（TeamGroup）、威刚（ADATA/XPG）、英睿达（Crucial）</td>
<td>一线超频与稳定性顶尖。十铨、威刚颗粒好、性价比突出；英睿达原厂颗粒可靠，适合追求稳定不追求极致超频。</td>
</tr>
<tr>
<td><strong>固态硬盘（SSD）</strong></td>
<td>三星（Samsung）、西部数据/闪迪（WD/SanDisk）、金士顿（Kingston）、致态（ZhiTai） <strong>二三线</strong>：雷克沙（Lexar）、铠侠（Kioxia）、七彩虹、英睿达（Crucial）</td>
<td>三星/致态耐用性与速度优秀。雷克沙、铠侠原厂盘性价比高；七彩虹、英睿达日常使用稳定可靠。优先PCIe 4.0/5.0 NVMe。</td>
</tr>
<tr>
<td><strong>显卡（GPU）</strong></td>
<td>华硕（ASUS）、微星（MSI）、技嘉（GIGABYTE）、七彩虹（Colorful）、影驰（Galaxy） <strong>二三线</strong>：索泰（Zotac）、铭瑄（MAXSUN）、瀚铠（AMD卡）</td>
<td>一线散热与做工优秀。七彩虹/影驰国内渠道好、售后方便；索泰、铭瑄价格更友好，性能释放正常。芯片以NVIDIA RTX 50系列为主，AMD可选瀚铠等。</td>
</tr>
<tr>
<td><strong>电源（PSU）</strong></td>
<td>海韵（Seasonic）、海盗船（Corsair）、全汉（FSP）、安钛克（Antec）、振华（Super Flower） <strong>二三线</strong>：航嘉（Huntkey）、鑫谷（Segotep）、酷冷至尊（Cooler Master）、长城</td>
<td>海韵/振华品质顶尖。航嘉、鑫谷性价比高、渠道广；酷冷至尊中端稳定。建议80 Plus金牌及以上，功率留足余量。</td>
</tr>
<tr>
<td><strong>机箱</strong></td>
<td>联力（Lian Li）、恩杰（NZXT）、追风者（Phanteks）、酷冷至尊（Cooler Master）、乔思伯（Jonsbo） <strong>二三线</strong>：先马（Sama）、鑫谷、航嘉</td>
<td>一线风道与颜值优秀。先马、鑫谷、航嘉价格实惠、空间够用，适合普通装机。关注显卡长度与散热支持。</td>
</tr>
<tr>
<td><strong>散热器（CPU冷却）</strong></td>
<td>猫头鹰（Noctua）、利民（Thermalright）、九州风神（Deepcool）、酷冷至尊（Cooler Master） <strong>二三线</strong>：ID-Cooling、超频三、乔思伯</td>
<td>猫头鹰静音顶级；利民性价比之王。ID-Cooling、超频三散热表现好、价格低；适合中低端风冷或入门水冷。</td>
</tr>
</tbody>
</table>
<p>可以根据上面表格的品牌进行选择购买，一线的品控会比二三线的要好些，出问题的情况会比较少，二三线就是性价比了，可以通过合理的搭配用最少的钱达到最大的性能，这要看个人的选择了。</p>
<h4>下面是我DIY满意的配置：</h4>
<h5>CPU+主板：Intel i7 14650HX 天邦Modt</h5>
<p><img src="https://music.artist.cc.cd/images/68.webp" alt="" /></p>
<h5>内存：玖合3200Mhz  8G*2  C16</h5>
<p><img src="https://music.artist.cc.cd/images/69.webp" alt="" /></p>
<h5>硬盘：雷克沙ARES 790战神 1TB</h5>
<p><img src="https://music.artist.cc.cd/images/70.webp" alt="" /></p>
<h5>显卡：耕升3080 10G  LHR</h5>
<p><img src="https://music.artist.cc.cd/images/73.webp" alt="" /></p>
<h5>电源：研风TX850w</h5>
<p><img src="https://music.artist.cc.cd/images/71.webp" alt="" /></p>
<h5>散热器：酷里奥B60T V2 双塔散热</h5>
<p><img src="https://music.artist.cc.cd/images/72.webp" alt="" /></p>
<p>CPU+主板：Intel i7 14650HX 天邦Modt</p>
<p>内存：玖合3200Mhz 8G*2 C16</p>
<p>硬盘：雷克沙ARES 790战神 1TB</p>
<p>显卡：耕升3080 10G LHR</p>
<p>电源：研风TX850w</p>
<p>散热器：酷里奥B60T V2 双塔散热</p>
<p>以上就是目前在使用的主机了，总花费：1510+193+333+1655+281+120=4092元。显卡买的时候pdd返了95，所以是1655，买硬件的时候多刷优惠卷更便宜，我只有显卡买了二手的，如果不在乎新旧，去咸鱼淘还能更便宜，买二手最好是带质保的，因为说不定用着什么时候就挂了。我对这套配置很满意，基本什么游戏都能畅玩，使用一年左右了，没有出现什么大问题，此外还入手了一个4k显示器，虽然便宜但观感也不错。</p>
<p><img src="https://music.artist.cc.cd/images/74.webp" alt="" /></p>
<p>显示器返了28块实付685元，面板用的还是LG的，外壳质感不怎么样，显示还是很OK的。此外比较的满意的外设还有键盘，用的套件，自己买了轴体和键帽组装起来的，乐趣十足。</p>
<p><img src="https://music.artist.cc.cd/images/75.webp" alt="电脑主机配置" /></p>
<p>对于自己DIY出来的电脑不仅是一种乐趣成就，又像是自己亲自赋予了它一种特殊的情感</p>
]]></content>
        <author>
            <name>Liviruy</name>
            <uri>https://artist.cc.cd/</uri>
        </author>
        <published>2026-08-23T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Markdown 扩展功能]]></title>
        <id>https://artist.cc.cd/posts/markdown-extended-features/</id>
        <link href="https://artist.cc.cd/posts/markdown-extended-features/"/>
        <updated>2025-04-25T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[本文介绍 Retypeset 主题支持的 Markdown 扩展功能，包括语法示例与效果展示。使用标准的 Markdown 图像语法 ![a...]]></summary>
        <content type="html"><![CDATA[<p>本文介绍 Retypeset 主题支持的 Markdown 扩展功能，包括语法示例与效果展示。</p>
<h2>图注</h2>
<p>使用标准的 Markdown 图像语法 <code>![alt](src)</code>，即可自动生成图注。在 <code>alt</code> 前添加下划线 <code>_</code> 或留空 <code>alt</code>，即可隐藏图注。</p>
<h3>语法</h3>
<pre><code>![图片描述](https://music.artist.cc.cd/images/13.webp)

![_图片描述](https://music.artist.cc.cd/images/13.webp)
</code></pre>
<h3>效果</h3>
<p><img src="https://music.artist.cc.cd/images/13.webp" alt="图片描述" /></p>
<p><img src="https://music.artist.cc.cd/images/13.webp" alt="_图片描述" /></p>
<h2>提示块</h2>
<p>使用 GitHub 语法 <code>&gt; [!TYPE]</code> 或三冒号语法 <code>:::type</code>，即可创建提示块。支持 <code>note</code>、<code>tip</code>、<code>important</code>、<code>warning</code>、<code>caution</code> 五种类型。</p>
<h3>语法</h3>
<pre><code>&gt; [!NOTE]
&gt; 即使快速浏览，也值得用户留意的信息。

&gt; [!TIP]
&gt; 可选信息，可帮助用户更轻松地完成操作。

&gt; [!IMPORTANT]
&gt; 用户成功所需的关键信息。

:::warning
由于存在潜在风险，需要用户立即关注的关键内容。
:::

:::caution
某些操作可能带来的负面后果。
:::

:::note[自定义标题]
这是一个自定义标题的提示块。
:::
</code></pre>
<h3>效果</h3>
<blockquote>
<p>[!NOTE]
即使快速浏览，也值得用户留意的信息。</p>
</blockquote>
<blockquote>
<p>[!TIP]
可选信息，可帮助用户更轻松地完成操作。</p>
</blockquote>
<blockquote>
<p>[!IMPORTANT]
用户成功所需的关键信息。</p>
</blockquote>
<p>:::warning
由于存在潜在风险，需要用户立即关注的关键内容。
:::</p>
<p>:::caution
某些操作可能带来的负面后果。
:::</p>
<p>:::note[自定义标题]
这是一个自定义标题的提示块。
:::</p>
<h2>折叠块</h2>
<p>使用三冒号语法 <code>:::fold[title]</code>，即可创建折叠块。点击标题可以展开或收起。</p>
<h3>语法</h3>
<pre><code>:::fold[使用提示]
如果需要添加并非所有读者都会感兴趣的内容，可以将其放在折叠块中。
:::
</code></pre>
<h3>效果</h3>
<p>:::fold[使用提示]
如果需要添加并非所有读者都会感兴趣的内容，可以将其放在折叠块中。
:::</p>
<h2>Mermaid 图表</h2>
<p>使用代码块包裹 Mermaid 语法，并标注语言类型 <code>mermaid</code>，即可创建 Mermaid 图表。</p>
<h3>语法</h3>
<pre><code>```mermaid
graph TD;
    A--&gt;B;
    A--&gt;C;
    B--&gt;D;
    C--&gt;D;
```
</code></pre>
<h3>效果</h3>
<pre><code>graph TD;
    A--&gt;B;
    A--&gt;C;
    B--&gt;D;
    C--&gt;D;
</code></pre>
<h2>画廊</h2>
<p>使用三冒号语法 <code>:::gallery</code>，即可创建图片画廊。水平滚动以查看更多图片。</p>
<h3>语法</h3>
<pre><code>:::gallery
![春日植物园 1](https://music.artist.cc.cd/images/14.webp)
![春日植物园 2](https://music.artist.cc.cd/images/15.webp)
![海边日落 1](https://music.artist.cc.cd/images/16.webp)
![海边日落 2](https://music.artist.cc.cd/images/17.webp)
![海边日落 3](https://music.artist.cc.cd/images/18.webp)
:::
</code></pre>
<h3>效果</h3>
<p>:::gallery
<img src="https://music.artist.cc.cd/images/14.webp" alt="春日植物园 1" />
<img src="https://music.artist.cc.cd/images/15.webp" alt="春日植物园 2" />
<img src="https://music.artist.cc.cd/images/16.webp" alt="海边日落 1" />
<img src="https://music.artist.cc.cd/images/17.webp" alt="海边日落 2" />
<img src="https://music.artist.cc.cd/images/18.webp" alt="海边日落 3" />
:::</p>
<h2>GitHub 仓库</h2>
<p>使用双冒号语法 <code>::github{repo="owner/repo"}</code>，即可嵌入 GitHub 仓库。</p>
<h3>语法</h3>
<pre><code>::github{repo="radishzzz/astro-theme-retypeset"}
</code></pre>
<h3>效果</h3>
<p>::github{repo="radishzzz/astro-theme-retypeset"}</p>
<h2>视频</h2>
<p>使用双冒号语法 <code>::youtube{id="video-id"}</code>，即可嵌入视频。</p>
<h3>语法</h3>
<pre><code>::youtube{id="9pP0pIgP2kE"}

::bilibili{id="BV1sK4y1Z7KG"}
</code></pre>
<h3>效果</h3>
<p>::youtube{id="9pP0pIgP2kE"}</p>
<p>::bilibili{id="BV1sK4y1Z7KG"}</p>
<h2>Spotify</h2>
<p>使用双冒号语法 <code>::spotify{url="spotify-url"}</code>，即可嵌入 Spotify 内容。</p>
<h3>语法</h3>
<pre><code>::spotify{url="https://open.spotify.com/track/0HYAsQwJIO6FLqpyTeD3l6"}

::spotify{url="https://open.spotify.com/album/03QiFOKDh6xMiSTkOnsmMG"}
</code></pre>
<h3>效果</h3>
<p>::spotify{url="https://open.spotify.com/track/0HYAsQwJIO6FLqpyTeD3l6"}</p>
<p>::spotify{url="https://open.spotify.com/album/03QiFOKDh6xMiSTkOnsmMG"}</p>
<h2>X 推文</h2>
<p>使用双冒号语法 <code>::tweet{url="tweet-url"}</code>，即可嵌入 X 推文。</p>
<h3>语法</h3>
<pre><code>::tweet{url="https://x.com/hachi_08/status/1906456524337123549"}
</code></pre>
<h3>效果</h3>
<p>::tweet{url="https://x.com/hachi_08/status/1906456524337123549"}</p>
<h2>CodePen</h2>
<p>使用双冒号语法 <code>::codepen{url="codepen-url"}</code>，即可嵌入 CodePen 演示。</p>
<h3>语法</h3>
<pre><code>::codepen{url="https://codepen.io/jh3y/pen/NWdNMBJ"}
</code></pre>
<h3>效果</h3>
<p>::codepen{url="https://codepen.io/jh3y/pen/NWdNMBJ"}</p>
]]></content>
        <author>
            <name>Liviruy</name>
            <uri>https://artist.cc.cd/</uri>
        </author>
        <published>2025-04-25T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[主题配色指南]]></title>
        <id>https://artist.cc.cd/posts/theme-color-schemes/</id>
        <link href="https://artist.cc.cd/posts/theme-color-schemes/"/>
        <updated>2025-04-11T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Retypeset 基于 OKLCH 颜色空间来定义主题配色，默认为模拟印刷风格的黑白灰配色。为满足个性化需求，我为主题制作了一些配色方案...]]></summary>
        <content type="html"><![CDATA[<p>Retypeset 基于 <a href="https://oklch.com/">OKLCH</a> 颜色空间来定义主题配色，默认为模拟印刷风格的黑白灰配色。</p>
<p>为满足个性化需求，我为主题制作了一些配色方案。你可以在 <a href="https://github.com/radishzzz/astro-theme-retypeset/blob/master/src/config.ts">src/config.ts</a> 中替换默认配色，并重启开发服务器以预览新配色。</p>
<h2>葱白</h2>
<p><img src="https://artist.cc.cd/_astro/1-light.ClVuZxd5_21HiG1.webp" alt="Light mode" />
<img src="https://artist.cc.cd/_astro/1-dark.BivCfSmu_Z1FcXLT.webp" alt="Dark mode" /></p>
<pre><code>light: {
  primary: 'oklch(0.25 0.03 211.86)',
  secondary: 'oklch(0.40 0.03 211.86)',
  background: 'oklch(0.99 0.0039 106.47)',
  highlight: 'oklch(0.93 0.195089 103.2532 / 0.5)',
},
dark: {
  primary: 'oklch(0.92 0.0015 106.47)',
  secondary: 'oklch(0.79 0.0015 106.47)',
  background: 'oklch(0.24 0.0039 106.47)',
  highlight: 'oklch(0.93 0.195089 103.2532 / 0.2)',
},
</code></pre>
<h2>鸦青</h2>
<p><img src="https://artist.cc.cd/_astro/2-light.DLXS38ks_2j3zhb.webp" alt="Light mode" />
<img src="https://artist.cc.cd/_astro/2-dark.prgZMrfY_Z1i4yAx.webp" alt="Dark mode" /></p>
<pre><code>light: {
  primary: 'oklch(0.24 0.0172 280.05)',
  secondary: 'oklch(0.40 0.0172 280.05)',
  background: 'oklch(0.98 0.0172 280.05)',
  highlight: 'oklch(0.93 0.195089 103.2532 / 0.5)',
},
dark: {
  primary: 'oklch(0.92 0.0172 280.05)',
  secondary: 'oklch(0.79 0.0172 280.05)',
  background: 'oklch(0.24 0.0172 280.05)',
  highlight: 'oklch(0.93 0.195089 103.2532 / 0.2)',
},
</code></pre>
<h2>墨蓝</h2>
<p><img src="https://artist.cc.cd/_astro/4-light.DIUeDw9p_Z2fbxhe.webp" alt="Light mode" />
<img src="https://artist.cc.cd/_astro/4-dark.CKqTknpt_2n6Qrb.webp" alt="Dark mode" /></p>
<pre><code>light: {
  primary: 'oklch(0.24 0.053 261.24)',
  secondary: 'oklch(0.39 0.053 261.24)',
  background: 'oklch(1 0 0)',
  highlight: 'oklch(0.93 0.195089 103.2532 / 0.5)',
},
dark: {
  primary: 'oklch(0.92 0 0)',
  secondary: 'oklch(0.79 0 0)',
  background: 'oklch(0.24 0.016 265.21)',
  highlight: 'oklch(0.93 0.195089 103.2532 / 0.2)',
},
</code></pre>
<h2>米黄</h2>
<p><img src="https://artist.cc.cd/_astro/3-light.8aAAK8Ad_BewEA.webp" alt="Light mode" />
<img src="https://artist.cc.cd/_astro/3-dark.CGQcMuXC_Z2c4VKc.webp" alt="Dark mode" /></p>
<pre><code>light: {
  primary: 'oklch(0.25 0 0)',
  secondary: 'oklch(0.41 0 0)',
  background: 'oklch(0.95 0.0237 59.39)',
  highlight: 'oklch(0.93 0.195089 103.2532 / 0.5)',
},
dark: {
  primary: 'oklch(0.93 0.019 59.39)',
  secondary: 'oklch(0.80 0.017 59.39)',
  background: 'oklch(0.23 0 0)',
  highlight: 'oklch(0.93 0.195089 103.2532 / 0.2)',
},
</code></pre>
]]></content>
        <author>
            <name>Liviruy</name>
            <uri>https://artist.cc.cd/</uri>
        </author>
        <published>2025-04-11T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[Markdown 样式指南]]></title>
        <id>https://artist.cc.cd/posts/markdown-style-guide/</id>
        <link href="https://artist.cc.cd/posts/markdown-style-guide/"/>
        <updated>2025-03-23T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[以下是一些基本的 Markdown 语法示例，及其在 Retypeset 主题中的样式效果。在文本前添加井号 # 与空格，即可创建标题。井号...]]></summary>
        <content type="html"><![CDATA[<p>以下是一些基本的 Markdown 语法示例，及其在 Retypeset 主题中的样式效果。</p>
<h2>标题</h2>
<p>在文本前添加井号 <code>#</code> 与空格，即可创建标题。井号数量对应标题等级。</p>
<h3>语法</h3>
<pre><code># 一级标题
## 二级标题
### 三级标题
#### 四级标题
##### 五级标题
###### 六级标题
</code></pre>
<h3>效果</h3>
<h1>一级标题</h1>
<h2>二级标题</h2>
<h3>三级标题</h3>
<h4>四级标题</h4>
<h5>五级标题</h5>
<h6>六级标题</h6>
<h2>段落</h2>
<p>使用空行分隔文本，即可创建段落。</p>
<h3>语法</h3>
<pre><code>孔乙己一到店，所有喝酒的人便都看着他笑，有的叫道：“孔乙己，你脸上又添上新伤疤了！”他不回答，对柜里说：“温两碗酒，要一碟茴香豆。”便排出九文大钱。他们又故意的高声嚷道：“你一定又偷了人家的东西了！”孔乙己睁大眼睛说：“你怎么这样凭空污人清白……”“什么清白？我前天亲眼见你偷了何家的书，吊着打。”孔乙己便涨红了脸，额上的青筋条条绽出，争辩道：“窃书不能算偷……窃书！……读书人的事，能算偷么？”接连便是难懂的话，什么“君子固穷”，什么“者乎”之类，引得众人都哄笑起来：店内外充满了快活的空气。

听人家背地里谈论，孔乙己原来也读过书，但终于没有进学，又不会营生；于是愈过愈穷，弄到将要讨饭了。
</code></pre>
<h3>效果</h3>
<p>孔乙己一到店，所有喝酒的人便都看着他笑，有的叫道：“孔乙己，你脸上又添上新伤疤了！”他不回答，对柜里说：“温两碗酒，要一碟茴香豆。”便排出九文大钱。他们又故意的高声嚷道：“你一定又偷了人家的东西了！”孔乙己睁大眼睛说：“你怎么这样凭空污人清白……”“什么清白？我前天亲眼见你偷了何家的书，吊着打。”孔乙己便涨红了脸，额上的青筋条条绽出，争辩道：“窃书不能算偷……窃书！……读书人的事，能算偷么？”接连便是难懂的话，什么“君子固穷”，什么“者乎”之类，引得众人都哄笑起来：店内外充满了快活的空气。</p>
<p>听人家背地里谈论，孔乙己原来也读过书，但终于没有进学，又不会营生；于是愈过愈穷，弄到将要讨饭了。</p>
<h2>图片</h2>
<p>使用感叹号 <code>!</code> 方括号 <code>[]</code> 与圆括号 <code>()</code>，即可添加图片。这些都是半角符号，而非全角符号。</p>
<h3>语法</h3>
<pre><code>![图片描述](../_images/image-01.jpeg)

![图片描述](https://music.artist.cc.cd/images/20.webp)
</code></pre>
<h3>效果</h3>
<p><img src="https://music.artist.cc.cd/images/19.webp" alt="图片示例" /></p>
<h2>块引用</h2>
<p>使用 <code>&gt;</code> 符号和空格，即可创建块引用，其中可包含多个段落。使用 <code>&lt;cite&gt;</code> 或 <code>&lt;footer&gt;</code> 标签，即可标注引用来源，同时可通过 <code>[^1]</code> 或 <code>[^note]</code> 格式插入脚注。</p>
<h3>多个段落</h3>
<h4>语法</h4>
<pre><code>&gt; 天地不仁，以万物为刍狗。
&gt;
&gt; **提示**：引用块内可使用 _Markdown 语法_。
</code></pre>
<h4>效果</h4>
<blockquote>
<p>天地不仁，以万物为刍狗。</p>
<p><strong>提示</strong>：引用块内可使用 <em>Markdown 语法</em>。</p>
</blockquote>
<h3>标注引用来源</h3>
<h4>语法</h4>
<pre><code>&gt; 在我的后园，可以看见墙外有两株树，一株是枣树，还有一株也是枣树。
&gt;
&gt; —— &lt;cite&gt;《秋夜》[^1]&lt;/cite&gt;

[^1]: 《[秋夜](https://zh.wikisource.org/wiki/%E7%A7%8B%E5%A4%9C_(%E9%AD%AF%E8%BF%85))》是鲁迅散文诗集《野草》中的第一首散文诗，创作于 1924 年。
</code></pre>
<h4>效果</h4>
<blockquote>
<p>在我的后园，可以看见墙外有两株树，一株是枣树，还有一株也是枣树。</p>
<p>—— &lt;cite&gt;《秋夜》[^1]&lt;/cite&gt;</p>
</blockquote>
<p>[^1]: 《<a href="https://zh.wikisource.org/wiki/%E7%A7%8B%E5%A4%9C_(%E9%AD%AF%E8%BF%85)">秋夜</a>》是鲁迅散文诗集《野草》中的第一首散文诗，创作于 1924 年。</p>
<h2>表格</h2>
<p>使用三个或多个连字符 <code>---</code> 分隔标题，并使用管道符 <code>|</code> 分隔每列，即可创建表格。</p>
<h3>语法</h3>
<pre><code>| 斜体   | 粗体     | 代码   |
| ----- | ------- | ------ |
| _斜体_ | **粗体** | `代码` |
| _斜体_ | **粗体** | `代码` |
</code></pre>
<h3>效果</h3>
<table>
<thead>
<tr>
<th>斜体</th>
<th>粗体</th>
<th>代码</th>
</tr>
</thead>
<tbody>
<tr>
<td><em>斜体</em></td>
<td><strong>粗体</strong></td>
<td><code>代码</code></td>
</tr>
<tr>
<td><em>斜体</em></td>
<td><strong>粗体</strong></td>
<td><code>代码</code></td>
</tr>
</tbody>
</table>
<h2>代码块</h2>
<p>使用三个反引号 <code>```</code> 包裹代码，即可创建代码块。在顶部的反引号后标注语言类型，例如 html、javascript、css、markdown 等，即可实现语法高亮。</p>
<h3>语法</h3>
<pre><code>```html
&lt;!doctype html&gt;
&lt;html lang="zh-CN"&gt;
  &lt;head&gt;
    &lt;meta charset="utf-8" /&gt;
    &lt;title&gt;HTML5 示例文档&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;p&gt;测试&lt;/p&gt;
  &lt;/body&gt;
&lt;/html&gt;
```
</code></pre>
<h3>效果</h3>
<pre><code>&lt;!doctype html&gt;
&lt;html lang="zh-CN"&gt;
  &lt;head&gt;
    &lt;meta charset="utf-8" /&gt;
    &lt;title&gt;HTML5 示例文档&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;p&gt;测试&lt;/p&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<h2>列表</h2>
<h3>有序列表</h3>
<h4>语法</h4>
<pre><code>1. 第一项
2. 第二项
3. 第三项
</code></pre>
<h4>效果</h4>
<ol>
<li>第一项</li>
<li>第二项</li>
<li>第三项</li>
</ol>
<h3>无序列表</h3>
<h4>语法</h4>
<pre><code>- 列表项
- 图表项
- 更多项
</code></pre>
<h4>效果</h4>
<ul>
<li>列表项</li>
<li>图表项</li>
<li>更多项</li>
</ul>
<h3>嵌套列表</h3>
<h4>语法</h4>
<pre><code>- 水果
  - 苹果
  - 橙子
  - 香蕉
- 蔬菜
  - 青菜
  - 萝卜
</code></pre>
<h4>效果</h4>
<ul>
<li>水果
<ul>
<li>苹果</li>
<li>橙子</li>
<li>香蕉</li>
</ul>
</li>
<li>蔬菜
<ul>
<li>青菜</li>
<li>萝卜</li>
</ul>
</li>
</ul>
<h2>其他元素</h2>
<p>包括 <code>&lt;sup&gt;</code> 上标，<code>&lt;sub&gt;</code> 下标，<code>&lt;abbr&gt;</code> 缩写，<code>&lt;del&gt;</code> 删除线，<code>&lt;u&gt;</code> 波浪线，<code>&lt;kbd&gt;</code> 键盘输入，<code>&lt;mark&gt;</code> 高亮，<code>&lt;hr&gt;</code> 分隔线。</p>
<h3>语法</h3>
<pre><code>H&lt;sub&gt;2&lt;/sub&gt;O

X&lt;sup&gt;n&lt;/sup&gt; + Y&lt;sup&gt;n&lt;/sup&gt; = Z&lt;sup&gt;n&lt;/sup&gt;

&lt;abbr title="Graphics Interchange Format"&gt;GIF&lt;/abbr&gt; 是一种位图图像格式。

书籍是人类进步的&lt;del&gt;楼梯&lt;/del&gt;阶梯。

优秀的作家总是会仔细检查&lt;u title="拼写"&gt;拚写&lt;/u&gt;问题。

按下 &lt;kbd&gt;Ctrl&lt;/kbd&gt; + &lt;kbd&gt;Alt&lt;/kbd&gt; + &lt;kbd&gt;Delete&lt;/kbd&gt; 以结束会话。

大多数&lt;mark&gt;蝾螈&lt;/mark&gt;昼伏夜出，以昆虫、蠕虫等小生物为食。

使用三个连字符 `---` 或 `&lt;hr&gt;` 标签，即可创建如下分隔线。

---
</code></pre>
<h3>效果</h3>
<p>H&lt;sub&gt;2&lt;/sub&gt;O</p>
<p>X&lt;sup&gt;n&lt;/sup&gt; + Y&lt;sup&gt;n&lt;/sup&gt; = Z&lt;sup&gt;n&lt;/sup&gt;</p>
<p>&lt;abbr title="Graphics Interchange Format"&gt;GIF&lt;/abbr&gt; 是一种位图图像格式。</p>
<p>书籍是人类进步的&lt;del&gt;楼梯&lt;/del&gt;阶梯。</p>
<p>优秀的作家总是会仔细检查&lt;u title="拼写"&gt;拚写&lt;/u&gt;问题。</p>
<p>按下 &lt;kbd&gt;Ctrl&lt;/kbd&gt; + &lt;kbd&gt;Alt&lt;/kbd&gt; + &lt;kbd&gt;Delete&lt;/kbd&gt; 以结束会话。</p>
<p>大多数&lt;mark&gt;蝾螈&lt;/mark&gt;昼伏夜出，以昆虫、蠕虫等小生物为食。</p>
<p>使用三个连字符 <code>---</code> 或 <code>&lt;hr&gt;</code> 标签，即可创建如下分隔线。</p>
<hr />
]]></content>
        <author>
            <name>Liviruy</name>
            <uri>https://artist.cc.cd/</uri>
        </author>
        <published>2025-03-08T00:00:00.000Z</published>
    </entry>
    <entry>
        <title type="html"><![CDATA[主题上手指南]]></title>
        <id>https://artist.cc.cd/posts/theme-guide/</id>
        <link href="https://artist.cc.cd/posts/theme-guide/"/>
        <updated>2025-04-13T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Retypeset 是一款基于 Astro 框架的静态博客主题，中文名为重新编排。本文为 Retypeset 主题上手指南，主要介绍如何修改...]]></summary>
        <content type="html"><![CDATA[<p>Retypeset 是一款基于 <a href="https://astro.build/">Astro</a> 框架的静态博客主题，中文名为重新编排。本文为 Retypeset 主题上手指南，主要介绍如何修改主题配置与创建新文章，来帮助你快速搭建个人博客。</p>
<h2>主题配置</h2>
<p>通过修改配置文件 <a href="https://github.com/radishzzz/astro-theme-retypeset/blob/master/src/config.ts">src/config.ts</a> 来自定义你的博客。</p>
<h3>站点信息</h3>
<pre><code>site: {
  // 站点标题
  title: 'Retypeset'
  // 站点副标题
  subtitle: 'Revive the beauty of typography'
  // 站点描述
  description: 'Retypeset is a static blog theme...'
  // 使用 src/i18n/ui.ts 中的多语言标题/副标题/站点描述，代替上方静态配置
  i18nTitle: true // true | false
  // 作者名称
  author: 'radishzz'
  // 站点地址
  url: 'https://lartist.top'
  // 基础路径
  // 所有页面和资产的根目录
  base: '/' // 例如 '/blog', '/docs'
  // 站点图标
  // 推荐格式：svg, png, ico
  favicon: '/icons/favicon.svg' // 或 https://example.com/favicon.svg
}
</code></pre>
<h3>主题配色</h3>
<pre><code>color: {
  // 默认主题
  mode: 'light' // light | dark | auto
  // 亮色模式
  light: {
    // 主要颜色
    // 用于站点标题、鼠标悬停效果等
    primary: 'oklch(25% 0.005 298)'
    // 次要颜色
    // 用于普通文本
    secondary: 'oklch(40% 0.005 298)'
    // 背景色
    background: 'oklch(96% 0.005 298)'
    // 高亮颜色
    // 用于导航栏、选中文本等
    highlight: 'oklch(0.93 0.195089 103.2532 / 0.5)'
  }
  // 暗色模式
  dark: {
    // 主要颜色
    primary: 'oklch(92% 0.005 298)'
    // 次要颜色
    secondary: 'oklch(77% 0.005 298)'
    // 背景色
    background: 'oklch(22% 0.005 298)'
    // 高亮颜色
    highlight: 'oklch(0.93 0.195089 103.2532 / 0.5)'
  }
}
</code></pre>
<h3>全局设置</h3>
<pre><code>global: {
  // 默认语言
  // 站点根路径 '/' 的语言
  locale: 'zh' // zh | en
  // 更多语言
  // 生成 '/en/' 等多语言路径
  // 不要重复填写默认语言，可以为空 []
  moreLocales: ['en']
  // 字体样式
  fontStyle: 'sans' // sans | serif
  // 文章日期格式
  // YYYY-MM-DD | MM-DD-YYYY | DD-MM-YYYY | MMM D YYYY | D MMM YYYY
  // 2025-04-13, 04-13-2025, 13-04-2025, Apr 13 2025，13 Apr 2025
  dateFormat: 'YYYY-MM-DD'
  // 开启文章目录
  toc: true // true | false
  // 开启 KaTeX 数学渲染
  katex: true // true | false
  // 减少动画效果
  reduceMotion: false // true | false
}
</code></pre>
<h3>评论系统</h3>
<pre><code>comment: {
  // 开启评论系统
  enabled: true // true | false
  // twikoo 评论系统
  twikoo: {
    envId: ''
    // version: 前端版本可在 package.json 中修改
  }
}
</code></pre>
<h3>搜索引擎优化</h3>
<pre><code>seo: {
  // @twitter ID
  twitterID: '@radishzz_'
  // 站点验证
  verification: {
    // google 搜索控制台
    google: 'AUCrz5F1e5qbnmKKDXl2Sf8u6y0kOpEO1wLs6HMMmlM'
    // bing 网站管理员工具
    bing: '64708CD514011A7965C84DDE1D169F87'
    // yandex 网站管理员
    yandex: ''
    // baidu 站长工具
    baidu: ''
  }
  // google 网站分析
  googleAnalyticsID: ''
  // umami 网站分析
  umamiAnalyticsID: 'dab0e4b9-9cbf-43c3-af60-b09d3b545c38'
  // folo 验证
  folo: {
    // 订阅 ID
    feedID: ''
    // 用户 ID
    userID: ''
  }
  // apiflash 访问密钥
  // 生成网站截图用于 open graph
  // 获取访问密钥：https://apiflash.com/
  apiflashKey: ''
}
</code></pre>
<h3>自定义页脚</h3>
<pre><code>footer: {
  // 社交链接
  links: [
    {
      name: 'RSS',
      url: '/atom.xml', // 或 /rss.xml
    },
    {
      name: 'GitHub',
      url: 'https://github.com/radishzzz/astro-theme-retypeset',
    },
    {
      name: 'Email',
      url: 'email@example.com',
    }
    // {
    //   name: 'X',
    //   url: 'https://x.com/radishzz_',
    // },
  ]
  // 建站年份
  startYear: 2025
}
</code></pre>
<h3>资源预加载</h3>
<pre><code>preload: {
  // 图床地址
  // 优化远程图片并生成低质量占位符
  imageHostURL: 'music.artist.cc.cd'
  // 定制 google analytics 跟踪脚本
  // 适用于代理跟踪脚本到自定义域名的用户
  customGoogleAnalyticsJS: ''
  // 定制 umami 跟踪脚本
  // 适用于自部署 umami 或代理跟踪脚本到自定义域名的用户
  customUmamiAnalyticsJS: 'https://views.radishzz.cc/script.js'
}
</code></pre>
<h2>更多配置</h2>
<p>除了配置文件 <code>src/config.ts</code> 外，还有部分配置项位于其它文件中。</p>
<h3>语法高亮</h3>
<p>代码块的语法高亮主题。</p>
<pre><code>// astro.config.ts

shikiConfig: {
  // 可选主题：https://shiki.style/themes
  // 背景色固定跟随博客主题，而非语法高亮主题
  themes: {
    light: 'github-light' // 亮色主题
    dark: 'github-dark' // 暗色主题
  }
}
</code></pre>
<h3>文章摘要</h3>
<p>文章自动摘要的字符数量。</p>
<pre><code>// src/utils/description.ts

const excerptLengths: Record&lt;ExcerptScene, {
  cjk: number // 中文、日文、韩文
  other: number // 其他语言
}&gt; = {
  list: { // 首页文章列表
    cjk: 120, // 自动摘要前 120 字
    other: 240, // 自动摘要前 240 字
  },
}
</code></pre>
<h3>Open Graph</h3>
<p><a href="https://orcascan.com/tools/open-graph-validator?url=https%3A%2F%2Flartist.top%2Fposts%2Ftheme-guide%2F">Open Graph 社交卡片</a> 样式。</p>
<pre><code>// src/pages/og/[...image].ts

getImageOptions: (_path, page) =&gt; ({
  logo: {
    path: './public/icons/og-logo.png', // 本地路径的 PNG 图片
    size: [250], // logo 宽度
  },
  font: {
    title: { // 标题
      families: ['Noto Sans SC'], // 字体
      weight: 'Bold', // 字重
      color: [34, 33, 36], // 颜色
      lineHeight: 1.5, // 行高
    },
  },
  fonts: [ // 字体路径（本地或远程）
    'https://cdn.jsdelivr.net/gh/notofonts/noto-cjk@main/Sans/SubsetOTF/SC/NotoSansSC-Bold.otf',
    'https://cdn.jsdelivr.net/gh/notofonts/noto-cjk@main/Sans/SubsetOTF/SC/NotoSansSC-Regular.otf',
  ],
  bgGradient: [[242, 241, 245]], // 背景色
  // 更多配置：https://github.com/delucis/astro-og-canvas/tree/latest/packages/astro-og-canvas
})
</code></pre>
<h3>RSS 订阅</h3>
<p><a href="https://lartist.top/atom.xml">RSS 订阅页</a> 配色。</p>
<pre><code>

&lt;style type="text/css"&gt;
body{color:oklch(25% 0.005 298)} /* 字体颜色 */
.bg-white{background-color:oklch(0.96 0.005 298)!important} /* 背景颜色 */
.text-gray{color:oklch(0.25 0.005 298 / 75%)!important} /* 次要字体颜色 */
&lt;/style&gt;
</code></pre>
<h2>创建新文章</h2>
<p>执行 <code>pnpm new-post &lt;filename&gt;</code> 创建新文章，可在 <code>src/content/posts/</code> 目录中编辑。</p>
<pre><code>pnpm new-post                      -&gt;  src/content/posts/new-post.md
pnpm new-post first-post           -&gt;  src/content/posts/first-post.md
pnpm new-post 2025/03/first-post   -&gt;  src/content/posts/2025/03/first-post.md
pnpm new-post first-post.mdx       -&gt;  src/content/posts/first-post.mdx
</code></pre>
<h3>Front Matter</h3>
<p><code>title</code> 和 <code>published</code> 为必填项，其余配置均可删除。</p>
<pre><code>---
# 必填
title: 主题上手指南
published: 2025-01-26

# 可选
description: 自动选取文章前 120 字作为摘要。
updated: 2025-03-26
tags:
  - 博客主题
  - 指南

# 进阶，可选
draft: true/false
pin: 0-99
toc: true/false
lang: zh/en
abbrlink: theme-guide
---
</code></pre>
<h3>进阶配置</h3>
<h4>draft</h4>
<p>是否标记文章为草稿。设为 true 时无法发布文章，仅供本地开发预览。默认为 false。</p>
<h4>pin</h4>
<p>是否置顶文章。数字越大，文章的置顶优先级越高。默认为 0，即不置顶。</p>
<h4>toc</h4>
<p>是否生成目录。显示 h2 至 h4 标题。默认由全局配置项 <code>global.toc</code> 决定，可在文章中单独设置以覆盖全局配置。</p>
<h4>lang</h4>
<p>指定文章语言。只能指定一种语言，不指定则默认显示在所有语言路径下。</p>
<pre><code># src/config.ts
# locale: 'zh'
# moreLocales: ['en']

# lang: ''
src/content/posts/apple.md   -&gt;  example.com/posts/apple/
                             -&gt;  example.com/en/posts/apple/
# lang: en
src/content/posts/apple.md   -&gt;  example.com/posts/apple/
# lang: zh
src/content/posts/apple.md   -&gt;  example.com/posts/apple/
</code></pre>
<h4>abbrlink</h4>
<p>自定义文章 URL。只能包含小写字母、数字和连字符 <code>-</code>。</p>
<pre><code># src/config.ts
# locale: 'zh'
# moreLocales: ['en']
# lang: 'en'

# abbrlink: ''
src/content/posts/apple.md           -&gt;  example.com/en/posts/apple/
src/content/posts/guide/apple.md     -&gt;  example.com/en/posts/guide/apple/
src/content/posts/2025/03/apple.md   -&gt;  example.com/en/posts/2025/03/apple/

# abbrlink: 'banana'
src/content/posts/apple.md           -&gt;  example.com/en/posts/banana/
src/content/posts/guide/apple.md     -&gt;  example.com/en/posts/banana/
src/content/posts/2025/03/apple.md   -&gt;  example.com/en/posts/banana/
</code></pre>
<h3>混排优化</h3>
<p>执行 <code>pnpm format-posts</code>，可优化 <code>src/content/</code> 目录中 Markdown 文件的排版格式。在 CJK（中文、日文、韩文）与英文混写的场景下，补充正确的空格，纠正标点符号等。</p>
<pre><code>pnpm format-posts
🔍 Scanning Markdown files...
📦 Found 8 Markdown files
✅ src/content/posts/guides/Theme Guide-zh.md
✨ Formatted 1 file successfully
</code></pre>
]]></content>
        <author>
            <name>Liviruy</name>
            <uri>https://artist.cc.cd/</uri>
        </author>
        <published>2025-01-26T00:00:00.000Z</published>
    </entry>
</feed>