JS脚本:原琴·五线谱版 (#370)

* update repo.json

* JS脚本:原琴·五线谱版

功能及其强大的原琴脚本-适配五线谱翻谱
核心功能------------------------------>
1.轻松实现根据五线谱翻版琴谱,支持单音、和弦
2.曲谱支持录入BPM、拍号
3.特殊音符支持休止符、浮点音符、(三/六)连音、(三/六)连音标记线、装饰音·倚音
注意事项------------------------------>
1.音域只有3个八度,受原琴音域限制,本脚本的上限取决于翻谱的大佬(卑微
2.实际上装饰音·倚音的时长视为基础时值单位(比如拍号2/4的基础时值单位就是4分音符)的1/16
3.制铺说明:曲谱JSON文件的notes必须保证为一行且不能包括空白符(空格和换行符等);小节之间用|隔开,|不是必要的,作用是方便曲谱维护

* update repo.json

* Delete repo/js/README.md

* Delete repo/js/main.js

* Delete repo/js/manifest.json

* Delete repo/js/settings.json

* update repo.json

* JS脚本:原琴·五线谱版

* update repo.json
This commit is contained in:
提瓦特钓鱼玳师
2025-02-26 22:39:30 +08:00
committed by GitHub
parent 28b2c2a058
commit f704ba61fd
10 changed files with 707 additions and 1 deletions

132
repo/js/AutoLyre/README.md Normal file
View File

@@ -0,0 +1,132 @@
# 曲谱 JSON 文件说明
此文档供曲谱制作人阅读,本文档详细说明了如何书写一个标准格式的曲谱.json文件包括各个字段的解释以及曲谱内容的格式要求。
重要即使制作了曲谱的JSON文件放到了正确的路径下在调度器的JS脚本配置里也不会出现你制作的曲谱上传方法如下
## 上传方法
1.上传到BetterGI脚本仓库的repo\js\AutoLyre\assets路径下根据已存在的曲谱在你的文件名前添加序号(例如 10.曲名.json)完成后请联系BetterGI v7群主更新JS脚本
2.联系BetterGI v71029539994群主帮你更新到仓库
3.发送邮件到hijiwos@hotmail.com并说明你的谱子将会在一段时间内更新到仓库
## 曲谱制作问题
\assets\五线谱注解.png包含了五线谱高音区和低音区对应的4组键盘键位相邻的红蓝大写字母为一组每组音域为三个八度
有不懂的地方请在\assets\example.json内找这个谱子内包含了该脚本的五线谱相关的所有功能
## 曲谱文件位置
所有的曲谱文件应放置于 AutoLyre\assets 目录下,并在文件名前添加正确的序号
## 文件结构
一个标准的曲谱.json文件的基本结构如下
{
"name": "",
"author": "",
"description": "",
"bpm": "",
"time_signature": "",
"composer": "",
"arranger": "",
"notes": ""
}
注意:仅: 右侧的双引号内的部分可以更改具体的曲谱格式请参考assets文件夹下的example.json
## 字段说明
name: 曲谱名,必填。
author: 录谱人,制作这个.json曲谱的作者名。
description: 描述,可以随意填写关于该曲谱的附加信息
bpm: 曲谱的BPMBeats Per Minute必填。
time_signature: 拍号,必填,例如 3/4 代表 以四分音符为一拍每小节三拍被设为一拍的音符仅支持2的幂
composer: 曲师,选填。
arranger: 谱师,选填。
notes: 曲谱内容,必填,具体格式请参考以下解析规则。
## Notes 解析规则(重要)
notes 字段中包含的是乐谱内容。音符-必须使用-大写字母,乐谱内容使用字符串表示,小节之间用 | 隔开。单个小节的解析规则如下:
A[4]: 表示按下A键A键视作四分音符。
(ASD)[4-#]: 表示装饰音·倚音
同时按下ASD键这个和弦视作四分音符的装饰音该装饰音的时值固定为拍号中的标准时值(3/4的标准时值为四分音符的时值)的1/16
A[4-8.3](AS)[4-8.3](ASD)[4-8.&]: 表示一个三连音六连音用法与此相似仅需将3改成6
A[4-8.3]: 4表示该三连音的总时值相当于四分音符8表示当前音符在乐谱上显示的时值相当于八分音符的时值3表示这是一个三连音的音符
(AS)[4-8.3]: 同上,只不过这里举的例子是和弦
(ASD)[4-8.&]: $表示这是当前连音的最后一个音符
H[4-4.3]G[4-8.&]: 表示一个三连音连音线(与三连音用法相同,区别于三连音,三连音连音线允许连线内出现不同类型的音符)
H[4-4.3]: 第一个4表示整个三连音的总时值为一个四分音符第二个4表示当前音符在乐谱上显示的时值相当于四分音符的时值3表示这是一个三连音的音符
G[4-8.&]: 4表示整个三连音的总时值为一个四分音符8表示这是一个八分音符$表示这是当前连音的最后一个音符
@[2-8.6](AF)[2-16.6]N[2-16.6](AF)[2-16.6]N[2-16.6](AF)[2-16.6]N[2-16.6](AF)[2-16.6]N[2-16.6](AF)[2-16.6]N[2-16.$]: 表示一个六连音连音线乐谱上表示为一个六连音连音线内包含1个八分休止符和10个十六分音符区别于三连音六连音的.后面的数字是6
@[2-8.6]: 2表示该六连音的总时值相当于一个二分音符8表示当前音符在乐谱上显示的时值相当于八分音符的时值6表示这是一个六连音
N[2-16.$]: 16表示当前音符在乐谱上显示的时值相当于十六分音符的时值$表示这是当前连音的最后一个音符
@[4]: 表示一个休止符
中括号内标明这是几分休止符,例如这里表示四分休止符。
A[4-*]: 表示一个附点四分音符
表示按下A键A键视作附点四分音符。
## 代码美化
曲谱JSON文件的"notes"的值视作一个字符串在这个字符串内可以使用空格和换行符美化代码notes内的空格和换行符不会被读取执行
例如:
{
"name": "示例曲谱",
"author": "录谱人A",
"bpm": "120",
"description": 预计时长: xxxxx, 五线谱网址: xxx.xxxx.xxx,
"time_signature": "4/4",
"composer": "曲师B",
"arranger": "谱师C",
"notes": "A[4](ASD)[8]Y[8-#]F[8-#](DFG)[8]R[4-*]T[8]|\n@[4](DFG)[8](CVB)[8]D[4]A[4]|\nA[4](ASD)[8]Y[8-#]F[8-#](DFG)[8]R[4]T[4]|\n@[4](DFG)[8](CVB)[8]D[4]A[4]"
}
## 附:
中括号 []- 前表示音符类型,- 后用于区分特殊音符)
[4] 表示四分音符。
[16] 表示十六分音符。
[-#] 表示装饰音。
[-3] 表示三连音(使用时必须保证是三个连续的三连音)。
例如:[16-#] 表示十六分音符的装饰音A[4-3]S[4-3]D[4-3] 表示一个时值为4分音符的三连音。
## 示例
一个完整的曲谱.json文件示例如下仅包含两个小节
文件名: 示例曲谱.json
{
"name": "示例曲谱",
"author": "录谱人A",
"bpm": "120",
"description": "曲谱信息",
"time_signature": "4/4",
"composer": "曲师B",
"arranger": "谱师C",
"notes": "A[4](ASD)[8]Y[8-#]F[8-#](DFG)[8]R[4-*]T[8]|\n@[4](DFG)[8](CVB)[8]D[4]A[4]|\nA[4](ASD)[8]Y[8-#]F[8-#](DFG)[8]R[4]T[4]|\n@[4](DFG)[8](CVB)[8]D[4]A[4]"
}

View File

@@ -0,0 +1,9 @@
{
"name": "小星星",
"author": "提瓦特钓鱼玳师",
"bpm": "80",
"time_signature": "4/4",
"composer": "none",
"arranger": "none",
"notes": "Z[4]Z[4]B[4]B[4]|N[4]N[4]B[2]|V[4]V[4]C[4]C[4]|X[4]X[4]Z[2]|B[4]B[4]V[4]V[4]|C[4]C[4]X[2]|B[4]B[4]V[4]V[4]|C[4]C[4]X[2]|Z[4]Z[4]B[4]B[4]|N[4]N[4]B[2]|V[4]V[4]C[4]C[4]|X[4]X[4]Z[2]|"
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

470
repo/js/AutoLyre/main.js Normal file
View File

@@ -0,0 +1,470 @@
(async function () { // 更改了连音
// 乐曲名(带序号)
const music_list = ["1.小星星", "2.小星星变奏曲", "3.Unknown Mother Goose [アンノウン・マザーグース]"]
const base_path = "assets/"
/**
*
* 执行单音
*
* @param key {string}
*
*/
async function play_note(key) {
keyDown(key);
keyUp(key);
}
/**
*
* 执行和弦
*
* @param keys {Array.string}
*
*/
async function play_chord(keys) {
for (const key of keys) {
play_note(key);
}
}
/**
*
* 根据乐曲文件名生成乐曲文件路径
*
* @param music_name 乐曲文件名
* @returns {string} 乐曲文件路径
*/
function path_join(music_name) {
return base_path + music_name + ".json";
}
/**
*
* 计算当前音符的时长(检测音符后是否有装饰音)
*
* @param sheet_list {Object[][]} 解析后的乐谱
* @param symbol_time 每一拍的时间
* @param symbol 以几分音符为一拍
* @param note_type 音符类型
* @param count 当前音符下标
* @param note_time 当前音符的时长默认为undefined不为空时symbol note_type count实效
* @returns {number}
*/
function cal_time_ornament(sheet_list, symbol_time, symbol, note_type, count, note_time=undefined) {
try {
if (note_time === undefined) {
// 该音符的正常时长
note_time = Math.round(symbol_time * (symbol / note_type));
}
// 装饰音时长
let ornament_time = Math.round(symbol_time / 16)
let check_count = count + 1;
let ornament_count = 0; // 装饰音计数
while (check_count < sheet_list.length) { // 装饰音不可能在曲谱末尾else会在匹配不到装饰音的循环触发
if (sheet_list[check_count]["spl"] === "#") {
ornament_count += 1;
} else {
if (ornament_count === 0) {
return note_time;
} else {
// 装饰音占用的时间过长就不预留时间
if (ornament_time * ornament_count < note_time) {
return note_time - ornament_time * ornament_count;
} else {
return note_time;
}
}
}
check_count += 1;
}
} catch (error) {
log.error(`出错(cal_time_ornament): ${error}`);
}
}
/**
* 获取JS脚本配置
*
* @returns {Object} 包含解析后JS脚本配置的对象具有以下属性
* - type {string} 执行类型:单曲和队列
* - repeat {integer} 单曲重复次数
* - interval {integer} 队列间隔时间(type为"single"时无此属性)
* - music {string}|{Array.string} 乐曲名type为"single"时为 {string}, type为"queue"时为 {Array.<string>}
*
*/
function get_settings() {
try{
// 读取选择的单曲
let music_single = typeof(settings.music_selector) === 'undefined' ? 0 : settings.music_selector;
// 读取循环次数
let music_repeat = typeof(settings.music_repeat) === 'undefined' ? 1 : parseInt(settings.music_repeat, 10);
// 读取循环间隔时间
let repeat_interval = typeof(settings.repeat_interval) === 'undefined' ? 0 : parseInt(settings.repeat_interval, 10);
// 读取循环模式
let repeat_mode = typeof(settings.repeat_mode) === 'undefined' ? "单曲循环" : settings.repeat_mode;
// 读取乐曲队列
let music_queue = typeof(settings.music_queue) === 'undefined' ? 0 : settings.music_queue;
// 读取队列间隔时间
let music_interval = typeof(settings.music_interval) === 'undefined' ? 0 : parseInt(settings.music_interval, 10);
if (music_queue === 0) { // 单曲执行
if (music_single !== 0) {
return {
"type": "single",
"repeat": music_repeat,
"repeat_interval": repeat_interval,
"music": music_single,
};
} else {
log.error(`错误JS脚本配置有误单曲未选择`);
}
} else { // 队列执行
let local_music_dic = {}; // 存储本地乐曲对照字典
let temp_music_list = []; // 存储乐曲名
// 写入本地乐曲对照字典
for (const each of music_list) {
if (each !== "example") {
// 从文件名提取编号
let temp_num = each.split(".")[0];
local_music_dic[temp_num] = each;
}
}
// 读取乐曲队列配置
for (const num of music_queue.split(" ")) {
if (Object.keys(local_music_dic).includes(num)) {
temp_music_list.push(local_music_dic[num]);
log.info(`乐曲: ${local_music_dic[num]} 已加入队列`)
} else {
log.info(`编号不存在,已跳过(编号:${num})`)
}
}
return {
"type": "queue",
"repeat": music_repeat,
"repeat_interval": repeat_interval,
"repeat_mode": repeat_mode,
"interval": music_interval,
"music": temp_music_list
};
}
} catch (error) {
log.error(`读取JS脚本配置时出错${error}`);
}
}
/**
*
* 读取并解析一个乐谱文件
*
* @param music_name {string} 乐曲文件名
* @returns {Promise<{}|null>}
*/
async function get_music_msg(music_name) {
let music_path = path_join(music_name);
let file_text = ""; // 存储乐曲文件内容
try{
file_text = file.readTextSync(music_path);
} catch (error) {
log.error(`文件无法读取:${music_path}\nerror:${error}`);
}
if(file_text == null){ // 检测文件是否读取
log.error(`读取文件 ${music_path} 错误,文件为空`);
return null ;
} else {
log.info(`文件读取成功: ${music_path}`);
}
let music_msg_dic = {};
// 正则表达式,用于匹配如下内容
let regex_name = /(?<="name": ")[\s\S]*?(?=")/
let regex_author = /(?<="author": ")[\s\S]*?(?=")/
let regex_bpm = /(?<="bpm": ")[\s\S]*?(?=")/
let regex_time_signature = /(?<="time_signature": ")[\s\S]*?(?=")/
let regex_composer = /(?<="composer": ")[\s\S]*?(?=")/
let regex_arranger = /(?<="arranger": ")[\s\S]*?(?=")/
let regex_notes = /(?<="notes": ")[\s\S]*?(?=")/
let regex_blank = /[\\n]/g
try {
// 歌曲名
music_msg_dic["name"] = file_text.match(regex_name)[0];
// 录谱人
music_msg_dic["author"] = file_text.match(regex_author)[0];
// 歌曲BPM
music_msg_dic["bpm"] = file_text.match(regex_bpm)[0];
// 拍号
music_msg_dic["time_signature"] = file_text.match(regex_time_signature)[0];
// 曲师
music_msg_dic["composer"] = file_text.match(regex_composer)[0];
// 谱师
music_msg_dic["arranger"] = file_text.match(regex_arranger)[0];
// 曲谱内容(删除换行符)
music_msg_dic["notes"] = file_text.match(regex_notes)[0].replace(regex_blank, '');
} catch(error) {
log.info(`曲谱解析错误:${error}\n请检查曲谱文件格式是否正确`);
return null;
}
return music_msg_dic;
}
/**
*
* 解析乐谱字符串乐谱JSON文件中的notes
*
* 小节之间用|隔开且乐谱中不能有空格,单个小节的解析规则如下:
* A[4] 表示按下A键A键视作四分音符
* (ASD)[4-#] 表示同时按下ASD键这个和弦视作四分音符的装饰音
* A[4-3](AS)[4-3](ASD)[4-3] 表示等分四分音符的三连音(-后填3必须要连着写三个这样的音符按顺序按下A、AS、ASD键
* @[4] 表示休止符中括号内标明这是几分休止符例如这里表示4分休止符
* 附:
* 中括号(-前表示音符类型-后用于区分特殊音符):[填4表示4分音符填16表示16分音符...-填#表示装饰音填3表示三连音] 例:[16-#]
*
* @param sheet {string} 乐谱
* @returns {Object[][]}
*/
function parseMusicSheet(sheet) {
// 将输入字符串按照小节分割
let bars = sheet.split('|');
let result = [];
// 遍历每个小节
bars.forEach(bar => {
let i = 0;
// 逐个字符解析小节中的音符及其属性
while (i < bar.length) {
let note = ''; // 存储音符
let type = ''; // 存储音符类型
let chord = false; // 判断是否为和弦
let spl = 'none'; // 存储特殊音符属性,默认值为 "none"
// 检查是否为和弦(和弦用圆括号包裹)
if (bar[i] === '(') {
chord = true;
i++;
while (bar[i] !== ')') {
note += bar[i];
i++;
}
i++; // 跳过闭合圆括号
} else if (bar[i] === '@') {
// 处理休止符
note = '@';
i++;
} else {
note = bar[i];
i++;
}
// 解析音符类型(用方括号包裹)
if (bar[i] === '[') {
i++;
while (bar[i] !== ']') {
type += bar[i];
i++;
}
i++; // 跳过闭合方括号
}
// 解析特殊音符属性如果type中包含'-'
if (type.includes('-')) {
let splIndex = type.indexOf('-');
spl = type.slice(splIndex + 1);
type = parseInt(type.slice(0, splIndex), 10);
}
// 将解析结果添加到parsedNotes数组中
result.push({
"note": note,
"type": type,
"chord": chord,
"spl": spl
});
}
});
return result;
}
/**
*
* 根据解析后的乐谱进行演奏
*
* @param sheet_list {Object[][]} 解析后的乐谱
* @param bpm BPM (240)
* @param ts 拍号 (3/4)
* @returns {Promise<void>}
*/
async function play_sheet(sheet_list, bpm, ts) {
// 确定是以几分音符为一拍
let symbol = parseInt(ts.split("/")[1], 10);
// 每拍所需的时间
let symbol_time = Math.round(60000 / bpm);
// 装饰音时长
let ornament_time = Math.round(symbol_time / 16)
// 存储连音
let temp_legato = [];
// test 需要额外计算装饰音时值的影响
for (let i = 0; i < sheet_list.length; i++) {
// 显示正在演奏的音符
log.info(`${sheet_list[i]["note"]}[${sheet_list[i]["type"]}-${sheet_list[i]["spl"]}]`);
if (sheet_list[i]["spl"] === 'none') { // 单音、休止符或和弦
if (sheet_list[i]["chord"]) {
await play_chord(sheet_list[i]["note"]); // 和弦
} else {
if (sheet_list[i]["note"] === '@') { // 休止符
// pass
} else {
await play_note(sheet_list[i]["note"]); // 单音
}
}
if (i !== sheet_list.length - 1) {
await sleep(cal_time_ornament(sheet_list, symbol_time, symbol, sheet_list[i]["type"], i));
}
} else if (sheet_list[i]["spl"] === '#') { // 装饰音不会包含休止符时值为symbol的时值的1/16
if (sheet_list[i]["chord"]) {
await play_chord(sheet_list[i]["note"]); // 和弦
} else {
await play_note(sheet_list[i]["note"]); // 单音
}
await sleep(ornament_time);
} else if (/\.3|\.6|\.\$/.test(sheet_list[i]["spl"])) { // 三连音/六连音(可能包含休止符)
temp_legato.push({
"note": sheet_list[i]["note"],
"chord": sheet_list[i]["chord"],
"type": sheet_list[i]["type"]
});
// 演奏连音
if ("$".includes(sheet_list[i]["spl"])) {
// 连音的总时长
let time_legato = Math.round(symbol_time * (symbol / sheet_list[i]["type"]));
// 当前音符类型
let current_type = parseInt(sheet_list[i]["spl"].split(".")[0])
// 连音的音符数值总和(用于计算当前音符时长)
let time_all = temp_legato.reduce((sum, each) => sum + 1 / parseInt(each["spl"].split(".")[0]), 0);
// 当前音符时长
let time_current = Math.round(time_legato * (1 / current_type) / time_all);
// 计数
let count = undefined;
for (const note_legato of temp_legato) {
if (sheet_list[i]["chord"]) {
await play_chord(sheet_list[i]["note"]); // 和弦
} else {
if (sheet_list[i]["note"] === '@') { // 休止符
await sleep(time_current);
} else {
await play_note(sheet_list[i]["note"]); // 单音
}
}
if (count === temp_legato.length - 1 && i !== sheet_list.length - 1) {
// 计算连音的最后一个音的时值(计算装饰音)
await sleep(cal_time_ornament(sheet_list, symbol_time, symbol, sheet_list[i]["type"], i, time_current));
// 重置连音缓存区
temp_legato = [];
} else {
await sleep(time_current);
}
count += 1;
}
}
} else if (sheet_list[i]["spl"] === '*') { // 附点音符
if (sheet_list[i]["chord"]) {
await play_chord(sheet_list[i]["note"]); // 和弦
} else {
if (sheet_list[i]["note"] === '@') { // 休止符
// pass
} else {
await play_note(sheet_list[i]["note"]); // 单音
}
}
if (i !== sheet_list.length - 1) {
await sleep(cal_time_ornament(sheet_list, symbol_time * 1.5, symbol, sheet_list[i]["type"], i));
}
} else {
log.info(`错误: ${sheet_list[i]["spl"]}`);
return null;
}
}
}
async function main() {
const settings_dic = get_settings();
if (settings_dic["type"] === "single") { // 单曲
if (settings_dic["repeat"] === 1) {
log.info(`1`);
let music_msg = await get_music_msg(settings_dic["music"]);
const music_sheet = parseMusicSheet(music_msg["notes"]);
await play_sheet(music_sheet, music_msg["bpm"], music_msg["time_signature"]);
} else {
for (let i = 0; i < settings_dic["repeat"]; i++) {
log.info(`2`);
await sleep(settings_dic["repeat_interval"] * 1000); // 循环间隔
const music_msg = await get_music_msg(settings_dic["music"]);
const music_sheet = parseMusicSheet(music_msg["notes"]);
await play_sheet(music_sheet, music_msg["bpm"], music_msg["time_signature"]);
}
}
} else { // 队列
let repeat_queue = 1;
if (settings_dic["repeat_mode"] === "队列循环") { // 队列循环
log.info(`3`);
repeat_queue = settings_dic["repeat"];
}
for (let r = 0; r < repeat_queue; r++) {
for (const music_name of settings_dic["music"]) {
// 读取乐谱
const music_msg = await get_music_msg(music_name);
// 解析乐谱
const music_sheet = parseMusicSheet(music_msg["notes"]);
if (settings_dic["repeat"] === 1) {
await play_sheet(music_sheet, music_msg["bpm"], music_msg["time_signature"]);
} else {
let repeat_single = 1;
if (settings_dic["repeat_mode"] !== "队列循环") { // 单曲循环
log.info(`4`);
repeat_single = settings_dic["repeat"];
}
for (let i = 0; i < repeat_single; i++) {
await play_sheet(music_sheet, music_msg["bpm"], music_msg["time_signature"]);
log.info(`曲目: ${music_name} 演奏完成`);
if (repeat_single !== 1) {
await sleep(settings_dic["repeat_interval"] * 1000); // 单曲循环间隔
}
}
}
// 队列内间隔
await sleep(settings_dic["interval"] * 1000);
}
if (repeat_queue !== 1) {
await sleep(settings_dic["repeat_interval"] * 1000); // 队列循环间隔
}
}
}
}
await main();
})();

View File

@@ -0,0 +1,15 @@
{
"manifest_version": 1,
"name": "原琴·五线谱版",
"version": "1.0",
"bgi_version": "0.42.0",
"description": "功能描述:功能及其强大的原琴脚本\n核心功能------------------------------>\n1.轻松实现根据五线谱翻版琴谱,支持单音、和弦\n2.曲谱支持录入BPM、拍号\n3.特殊音符支持休止符、浮点音符、(三/六)连音、(三/六)连音标记线、装饰音·倚音\n注意事项------------------------------>\n1.音域只有3个八度受原琴音域限制本脚本的上限取决于翻谱的大佬卑微\n2.实际上装饰音·倚音的时长视为基础时值单位(比如拍号2/4的基础时值单位就是4分音符)的1/16\n3.制铺说明曲谱JSON文件的notes必须保证为一行且不能包括空白符空格和换行符等小节之间用|隔开,|不是必要的,作用是方便曲谱维护\n---------------------------------------->\n版本ver1.0\n作者提瓦特钓鱼玳师\n脚本反馈邮箱hijiwos@hotmail.com",
"authors": [
{
"name": "提瓦特钓鱼玳师",
"url": "https://github.com/Hijiwos"
}
],
"settings_ui": "settings.json",
"main": "main.js"
}

View File

@@ -0,0 +1,41 @@
[
{
"name": "music_selector",
"type": "select",
"label": "选择乐曲(队列执行启用后该选项实效)",
"options": [
"1.小星星",
"2.小星星变奏曲",
"3.Unknown Mother Goose [アンノウン・マザーグース]"
]
},
{
"name": "music_repeat",
"type": "input-text",
"label": "循环执行次数(不填默认不循环)"
},
{
"name": "repeat_interval",
"type": "input-text",
"label": "循环间隔时间单位s, 循环执行启用时生效不填默认0s"
},
{
"name": "repeat_mode",
"type": "select",
"label": "循环模式(不填默认为单曲循环)",
"options": [
"单曲循环",
"队列循环"
]
},
{
"name": "music_queue",
"type": "input-text",
"label": "队列执行(填写数字[乐曲前的序号],空格隔开,不填默认不启用)"
},
{
"name": "music_interval",
"type": "input-text",
"label": "队列内间隔时间单位s, 队列执行启用时生效)"
}
]