澪的开发经验分享 - admin
type: channel
link: https://chorblack.top/index.php/author/1/
description: admin
items: Typecho插件Pio版本更新
items: 在Alpine Linux中使用交叉编译
items: luckfox使用记录
items: 个人网易云网页端部署记录
items: ATRI使用方法
items: 词典使用方法
items: watch gt 加载ATRI文本的方法
items: vivo手表表盘制作教程

Typecho插件Pio版本更新
type: item
date: 2026-03-28T12:06:00+00:00
description: 前因逛模之屋的时候发现，里面居然有live2d模型的分享，遂下载下来想放到网站上体验，但是发现模型版本是Live2D Cubism 4，而原始插件Pio只支持到Live2D Cubism 2，于是便想将Live2D SDK更新一下，支持最新的模型。实施方案修改模型发现逻辑，同时识别 model.json（Cubism 2）和 *.model3.json（Cubism 3/4），并将画布从 canvas改为 div容器用 pixi-live2d-display 替换 loadlive2d()修改模型发现逻辑 foreach($load as $key => $value){ $single = substr($value, 26); // 优先检测 Cubism 3/4 的 model3.json $model3_files = glob($value . "/*.model3.json"); if(!empty($model3_files)){ $models[$single] = ucfirst($single) . " (Cubism 3/4)"; } // 再检测 Cubism 2 的 model.json else if(file_exists($value . "/model.json")){ $models[$single] = ucfirst($single); } };下载SDKcdn感觉不如本地稳定文件用途pixi.min.jsPixiJS 渲染引擎live2d.min.jsCubism 2 运行库（pio/kurumi 模型需要）live2dcubismcore.min.jsCubism 4 运行库（Nahida 模型需要）pixi-live2d-display.min.js统一加载器，同时支持 Cubism 2/3/4下载链接替换旧api重写 pio.js，用 pixi-live2d-display 的 PIXI.live2d.Live2DModel.from() 替换旧的 loadlive2d()，同时保留所有原有交互功能。主要修改部分：// 加载模型（支持 Cubism 2 model.json 和 Cubism 3/4 *.model3.json） const loadModel = async (modelUrl) => { if (!window.PIXI || !PIXI.live2d) { console.error("pixi-live2d-display 未加载"); return; } // 首次加载时创建 PIXI.Application if (!current.app) { const width = current.container.clientWidth || 280; const height = current.container.clientHeight || 250; current.app = new PIXI.Application({ width: width, height: height, backgroundAlpha: 0, view: current.container.tagName === "CANVAS" ? current.container : undefined }); // 如果容器不是 canvas，将 PIXI 创建的 canvas 插入容器 if (current.container.tagName !== "CANVAS") { current.container.innerHTML = ""; current.container.appendChild(current.app.view); current.app.view.style.width = width + "px"; current.app.view.style.height = height + "px"; } } // 移除旧模型 if (current.model) { current.app.stage.removeChild(current.model); current.model.destroy(); current.model = null; } try { const model = await PIXI.live2d.Live2DModel.from(modelUrl, { autoInteract: false }); // 自适应缩放 const scaleX = current.app.view.width / model.width; const scaleY = current.app.view.height / model.heigh…

在Alpine Linux中使用交叉编译
type: item
date: 2026-03-17T11:27:00+00:00
description: 前言在B站看到一个项目仅用几十KB就构建了一个Minecraft服务端，运行更是只需要少得可怜的资源（esp32-c3都能运行），于是记录一下在Alpine Linux上的移植过程。开始因为使用的是 Alpine Linux，它底层使用 musl 而不是通常的 glibc (Ubuntu) 或 uClibc (Buildroot)。这需要一个专门针对 arm-linux-musleabihf 的交叉编译器，否则编译出的程序在板子上运行时会报错（例如 File not found 或段错误）。因此我们需要获取 musl 交叉编译工具链，不过我建议在宿主机手动下载然后传到wsl，不然这速度太慢了。 # 下载工具链 wget https://musl.cc/arm-linux-musleabihf-cross.tgz # 解压 tar -xf arm-linux-musleabihf-cross.tgz echo 'export PATH=$PATH:/home/luckfox/Desktop/arm-linux-musleabihf-cross/bin' >> ~/.bashrc source ~/.bashrc arm-linux-musleabihf-gcc --version搭建编译环境，运行setup_env.sh#!/bin/bash # Setup script for BareIron development environment on WSL/Ubuntu # Installs Java 21 and Node.js set -e echo "[1/3] Updating package lists..." sudo apt update echo "[2/3] Installing dependencies (curl, wget, tar, build-essential)..." sudo apt install -y curl wget tar sudo build-essential echo "[3/3] Installing Java 21..." # Check if OpenJDK 21 is available in default repos if apt-cache search openjdk-21-jdk | grep -q openjdk-21-jdk; then sudo apt install -y openjdk-21-jdk else echo "OpenJDK 21 not found in default repositories. Adding PPA.." sudo add-apt-repository -y ppa:openjdk-r/ppa sudo apt update sudo apt install -y openjdk-21-jdk fi # Verify Java installation java -version echo "Installing Node.js (Latest LTS)..." # Using NodeSource for up-to-date Node.js if ! command -v node &> /dev/null; then curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - sudo apt install -y nodejs else echo "Node.js is already installed." fi # Verify Node installation node -v echo "--------------------------------------------------------" echo "Setup Complete!" echo "--------------------------------------------------------" echo "1. Java 21 and Node.js have been installed." echo "" echo "Note: You need to manually install or configure your cross-compilation toolchain." echo "For Luckfox Pico (Alpine), ensure 'arm-linux-musleabihf-gcc' is in your PATH." echo "" echo "Then you can proceed with:" echo " ./…

luckfox使用记录
type: item
date: 2026-03-17T09:56:00+00:00
description: 前言之前买了一个luckfox pico plus，有一说一，当时不知道为什么脑子抽了买个这玩意，60多💴，拿来不知道有啥用，就跟着教程随便摸索了一下，记录一下摸索过程。开始摸索先看了几遍幸狐科技给的wiki文档，有一说一，还挺通俗易懂，luckfox pico plus使用RV1103处理器，基于单核 ARM Cortex-A7 32 位架构，计算性能可达 0.5 TOPS，板载内存64MB DDR2与板载存储128MB spi nand flash，跟着教程也装了ubuntu（卡的要死）、Buildroot，不过用起来感觉不怎么样，看见教程有Alpine Linux 移植，就跟着进行了移植（幸狐这wiki搬运教程也太不上心了，我看到好多错的地方）。移植开始docker运行Alpine Linux容器我用的windows桌面端docker，所以运行前执行docker run --rm --privileged multiarch/qemu-user-static --reset --persistent yes在wsl的ubuntu22.04换行中，执行以下代码，将临时文件挂载到系统，用来存储新的rootfsdd if=/dev/zero of=rootfs.ext4 bs=1M count=100 mkfs.ext4 rootfs.ext4 mkdir -p /home/luckfox/my-rootfs sudo mount rootfs.ext4 /home/luckfox/my-rootfs然后执行启动armv7alpine容器docker run -it --name armv7alpine --net=host -v /tmp/my-rootfs:/my-rootfs arm32v7/alpine容器内操作在容器里，将Alpine Linux的官方源替换成阿里源sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories apk update安装openrc用来管理进程apk add openrc rc-update add devfs boot rc-update add procfs boot rc-update add sysfs boot添加安全终端 ：允许 root 用户从 ttyFIQ0 登录。删除tty1到tty6开始的行，这一步一定要做，否则串口启动会卡死在找ttyX上。执行完最好vi /etc/inittab看看ttyFIQ0::respawn:/sbin/agetty --autologin root ttyFIQ0 vt100前面有没有#echo ttyFIQ0 > /etc/securetty # 删除 tty1-tty6 的配置 sed -i '/tty[1-6]/d' /etc/inittab # 添加 ttyFIQ0 自动登录配置 sed -i '/# Put a agetty on the serial port/a ttyFIQ0::respawn:/sbin/agetty --autologin root ttyFIQ0 vt100' /etc/inittab配置静态ipcat <<EOF > /etc/network/interfaces auto lo iface lo inet loopback auto eth0 iface eth0 inet static address 192.168.1.100 netmask 255.255.255.0 gateway 192.168.1.1 EOF添加开机启动rc-update add networking default修改dnscat <<EOF > /etc/resolv.conf nameserver 114.114.114.114 nameserver 8.8.8.8 EOF设置板子名字，方便ssh连接echo luckfox > /etc/hostname安装配置ssh，设置登录密码apk add openssh vi /etc/ssh/sshd_config sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config rc-update add sshd default passwd创建本地启动服务脚本cat <<EOF > /etc/local.d/crond.start #!/bin/bash # 网络校时并启动 crond 定时任务 ntpd -d -q && crond # 关闭板载 LED 灯 (…

个人网易云网页端部署记录
type: item
date: 2026-03-13T14:49:00+00:00
description: 说明为什么我要部署这个项目？答案很简单：听歌不想花钱。在没有部署之前，我是用的酷我提供的API。【感谢酷我】但是近半年来，酷我的API越来越不稳定了。所以，在此基础上，我使用了GD Studio's Online Music Platform API，本文也是根据此api获取的音乐播放链接。开始之前你需要准备：一个能访问github的电脑。安装了node.js 24版本或以上配置好了npm镜像以及下载了pnpm模块后端的部署首先，后端部署比较简单只需要访问将项目源码下载到你的电脑，然后按照教程，在项目目录里执行pnpm i然后等待模块儿全部安装完毕当你看到如上图一样的就表示你安装成功了，接下来就可以执行# 默认端口 3000 node app.js执行后就会显示访问后端，你就会看到到此，后端部署完毕【当然，到这里还是使用的网易云音乐的api，无法获取付费音乐，如何修改我们放在第三步来说明】前端的部署前端我试了很多的网易云项目，如AlgerMusicPlayer、SPlayer、YesPlayMusic等。YesPlayMusic界面比较素（太素了，啥也没有），所以没有使用；AlgerMusicPlayer没有手机端适配，没有使用；最终选择了SPlayer作为前端。同样的，将项目下载下来，在项目目录执行pnpm install安装完依赖后，复制 .env.example 为 .env 并按需修改，如：将 .env 文件中的 VITE_API_URL 改为第一步得到的 API 地址VITE_API_URL = http://localhost:3000;【最后不能/】执行编译打包之前，需要再添加一句SKIP_NATIVE_BUILD=true所以最后你的.env文件样式可能是# WEB 端口 VITE_WEB_PORT=14558 # API 端口 VITE_SERVER_PORT=25884 # API 地址 - 结尾不要加 / VITE_API_URL=http://localhost:3000 SKIP_NATIVE_BUILD=true 执行编译打包pnpm build文件输出在out/renderer 目录，本地测试可以按照下面的方法测试cd out/renderer npx http-server -p 4000访问http://127.0.0.1:4000/，就可以发现已经部署成功了！修改后端Api因为版权限制，无法听vip音乐，除非你账号本来就有vip【不过既然你有vip了还部署这个干嘛】目前网站默认关闭试听，需要在设置里打开而且试听会有提醒，需要去掉。我们需要做的有这几点：默认打开播放试听去除试听弹窗修改音乐播放链接使用的api实施计划默认打开播放试听在SPlayer里，src/stores/setting.ts中的第552行playSongDemo: false, -> playSongDemo: true,这样就实现了默认播放试听【如果你不打开，它不让你播放】去除试听弹窗在SPlayer里，src/core/player/SongManager.ts中的第552行【？行数还一样了】if (isTrial) window.$message.warning("当前歌曲仅可试听"); -> // if (isTrial) window.$message.warning("当前歌曲仅可试听");注释掉就行了。修改完不要忘记再次执行pnpm build npx http-server -p 4000修改音乐播放链接使用的api回到api-enhanced，首先安装axios【跨域请求第三方api】pnpm add axios修改文件module/song_url_v1.js【这次改的有点多】//module/song_url_v1.js // 歌曲链接 - v1 // 此版本不再采用 br 作为音质区分的标准 // 而是采用 standard, exhigh, lossless, hires, jyeffect(高清环绕声), sky(沉浸环绕声), jymaster(超清母带) 进行音质判断 // 当unblock为true时, 会尝试使用unblockmusic-utils进行解锁, 同时音质设置不会生效, 但仍然为必须传入参数 const logger = require('../util/logger.js') const createOption = require('../util/option.js') const axios = require('axios') module.exports = async (query, request) => { const { matchID, } = requi…

ATRI使用方法
type: item
date: 2025-04-21T14:02:00+00:00
description: 首先进SYSETM依次点击后等待下载完成点击去目录后显示文件总数123，即为下载完全下载完成后点击START进入游戏游戏界面左滑即可呼出菜单可执行的操作：保存：保存当前进度加载：读取存储的进度，与LOAD功能相同跳过场景：跳过当前场景跳过章节：跳过本章chapter

词典使用方法
type: item
date: 2025-04-21T13:59:00+00:00
description: 首先点击去下载然后点击下载数据，等数据下载完成下载完毕后如果显示37即为下载完成全部数据退出应用后重新进入即可使用点击错题本即可查看不会的单词点击单词即可显示出单词意思

watch gt 加载ATRI文本的方法
type: item
date: 2025-04-16T11:59:00+00:00
description: 起因原ATRI文本文件大小为1.6MB，而watch gt这破表加载300KB的文本文件时，不管是import的形式，还是storage的形式，亦或是file.read的形式，都会卡到重启。于是，在此背景下研究了一个分块加载文本的代码格式举个例子我有如下一个json的文本列表，我就会在达到某一个条件时加载其中符合条件的那个。PS：之前试过使用require分块加载，但是还是会报错，不知道是系统原因还是啥原因，人家小米全import加载都不会报错(jsonDataList: [ 'b999.json', 'b101.json', 'b102.json', 'b103.json', 'b111.json', 'b112.json', 'b113.json', 'b114.json', 'b121.json', 'b122.json', 'b123.json', 'b124.json', 'b200.json', 'b201.json', 'b202.json', 'b203.json', 'b204.json', 'b205.json', 'b206.json', 'b207.json', 'b301.json', 'b302.json', 'b303.json', 'b304.json', 'b401.json', 'b402.json', 'b403.json', 'b404.json', 'b405.json', 'b406.json', 'b407.json', 'b501.json', 'b601.json', 'b701.json' ],如下代码为ATRI加载文本文件使用的方法PS:原计划是将文本放在本地的，但是我不会读取读取本地文件，开发文档也没讲，寄！async loadData(chapter) { const num = this.jsonDataList[chapter]; // 将回调转换为Promise return new Promise((resolve, reject) => { file.readText({ uri: `internal://files/${num}`, success: (data) => { try { const parsedData = JSON.parse(data.text); resolve(parsedData); // 正确传递解析后的数据 } catch (e) { reject("JSON解析失败"); } }, fail: (_, code) => { reject(`文件读取失败，错误码：${code}`); } }); }); },最近在做一个可查找的词典，但是由于词库过大，加载200KB的索引会卡死，于是将索引分为26份，按照首字母（全部转化为小写）进行查找，不必将所有索引全部加载，实现按需查找的功能，并且也不会爆内存，完美！下面给出我写的下载模块，因为watch gt不支持数据块传输，所以只能使用全部下载字节流的方式了：import fetch from '@blueos.network.fetch' import file from '@blueos.storage.file' import prompt from '@blueos.window.prompt' export default { data: { indexData: null, jsonDataList: ['output_part_1.json'], }, async fetchPromise(options) { return new Promise((resolve, reject) => { fetch.fetch({ ...options, success: resolve, fail: reject, }); }); }, async writeTextPromise(options) { return new Promise((resolve, reject) => { file.writeText({ ...options, success: resolve, fail: (data, code) => reject({ data, code }), }); }); }, async writeArrayBufferPromise(options) { return new Promise((resolve, reject) => { file.writeArrayBuffer({ ...options, success: resolve, fail: (data, code) => reject({ data, code }), }); }); }, async…

vivo手表表盘制作教程
type: item
date: 2025-04-14T13:38:00+00:00
description: 准备工具：视频文件Python一个电脑（可以联网）预备工具下载步骤：抽取视频帧图片找到你想要开始的视频时间，如图1中所示为1:01，即61秒；找到你想要结束的视频时间，如图2中所示为1:03，即63秒；图1图2打开python代码：提取保存png.py图3将开始时间和结束时间填入所示的框内，如图3所示，运行代码，在同目录下会出现一个文件夹：output，如图4所示图4运行代码后在output中生成了一系列图片，如图5所示图5找到图片帧截取位置准备一个图片截取工具，如画图、ps等，如图6、图7所示，推荐ps，本文将使用ps进行演示；图6图7首先查看图片的分辨率大小，如图8所示，本文使用的视频分辨率为2560×1080图8由于要截取的图片我需要保持宽度不变，那么由计算得知：$$\frac{390}{450} = \frac{x}{1080}$$得出x约等于936从ps边界线处拉出两条参考线，如图9所示，先确认第一条线的位置，再在第一条线的坐标上加上刚才的x值，本文为936，即为第二条线的坐标值；如果你的图像中心对称，你也可以通过计算得出第一条线的横坐标a：$$a = \frac{2560}{2} - \frac{936}{2} = 812$$图9计算完成后，点击第一条线，右击，选择"编辑所选参考线"，将得到的a填入，再将a加上半个x，得到第二条线的坐标值，填入看看效果，如图10所示；图10确认完成后，将python代码：切割图片png.py复制到output文件夹内，打开代码，在所选框中填入如下四个数值：以左上角为坐标原点，右方向为x正方向，下方向为y正方向，切割图片起始横坐标a、切割图片起始纵坐标（本文为0）、裁剪宽度x、裁剪长度（本文为1080），运行代码，在output下生成了一个output文件夹，如图11、图12所示：图11图12图片分辨率、大小修改选取图片帧范围，本文选取了frame_001852.png\~frame_001890.png，然后打开网址：resize-images进行图片分辨率修改，如图14所示：Why：由于性能因素以及rpk包限制，建议将图片分辨率设置为与表盘分辨率一致；图14将所选图片拖入后，点击右侧"添加尺寸"，按照图15所示，设置格式为png、宽度为390、高度为450、图片布局选择自适应，确认选项后点击开始；图15为保证图片命名具有规律性，点击重命名，自定义命名、数字、补零等，如图16所示，确认后点击下载zip将图片包下载下来并解压：图16解压之后，点击compress-images进行图片压缩，由此来减少rpk包的大小，如图17所示（我要是能装上原版大小图片的包我还会压缩？）图17同样的，将上次重置分辨率的图片全部拖进去，选择无损，点击开始，如果你压缩后包小于2MB，恭喜你，不需要进有损压缩了，如果压缩后包大小仍然大于2MB，点击返回，选择自定义，数值越大图片越糊，选择合适的大小，将图片包控制在2MB左右，如图18所示：小技巧：如果你某些部分不需要太高分辨率，可以进行分块压缩，需要好的显示的可以使用无损压缩，对于如特效等不需要仔细显示的可以使用数值大一点的自定义压缩。图18这样，我们所需要的图像帧资源就得到了编辑表盘代码将图像资源放入对应的表盘文件夹中，在data中找到frames项，填写图片个数与图片路径，本文使用的是52张图片，路径规则如图19所示：Ps：duration项控制着图片帧的播放速率，本文使用的是50ms，即20帧；图19到此，你就可以成功将表盘运行起来了，当然你仍需要进行一些配置来保证安装，更改manifest.json的数据：更改package，即包名，命名格式可以是域名反转或中间；更改屏幕尺寸，watch gt为390，watch 3为466；更换id和name，id是表盘唯一标识，id相同则无法安装，因此有一个独特的id能保证你安装成功，name则为表盘显得的名称，图21中的name也需更改；previewImage为预览图，显示在表盘切换界面；图20图21到此为止，表盘制作成功，其他内容请查看蓝河开发文档：vivo蓝河应用开发感谢观看，如果觉得有用的话，请投喂我哦~编写者：澪编写时间：2025.4.12QQ：1562818788博客主页