musicfree-plugin-dev
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMusicFree 插件开发
MusicFree Plugin Development
严格行为准则
Strict Code of Conduct
以下规则在整个插件开发过程中始终生效,违反将导致开发失败:
- 禁止盲猜 URL:不要凭空试 、
/api/search等路径。每个 URL 必须来自页面内容或 Playwright 捕获的网络请求/v1/song - 禁止搜索网络寻找 API:不要搜索"XX音乐 API"、"免费音乐接口"等。所有数据来源必须通过对目标站点的直接观察获得,不能来自网络搜索
- 禁止批量探测:不要同时发多个请求试不同的参数组合
- 禁止重复请求:每个 URL 只请求一次,结果保存到本地文件后基于本地数据分析
- 禁止放弃说"需要浏览器环境":axios 失败但浏览器可以访问,差别一定在请求头上,用 Playwright 捕获真实请求头后在 axios 中补全
当遇到困难时,唯一正确的做法是:用 Playwright 观察浏览器的真实行为。不要猜测,不要搜索,去观察。
The following rules are in effect at all times during the entire plugin development process; violations will result in development failure:
- Forbid guessing URLs blindly: Do not try paths like or
/api/searchout of thin air. Every URL must come from page content or network requests captured by Playwright/v1/song - Forbid searching the web for APIs: Do not search for terms like "XX Music API" or "free music interfaces". All data sources must be obtained through direct observation of the target site, not from web searches
- Forbid batch probing: Do not send multiple requests simultaneously to test different parameter combinations
- Forbid duplicate requests: Each URL is requested only once; after saving the results to a local file, analyze based on the local data
- Forbid giving up with "browser environment required": If axios fails but the browser can access it, the difference must be in the request headers. Capture the real request headers with Playwright and complete them in axios
When encountering difficulties, the only correct approach is: Observe the real behavior of the browser with Playwright. Do not guess, do not search, just observe.
角色与协作模式
Roles and Collaboration Mode
- AI(你):分析目标站点、编写插件代码、编写并执行测试脚本、迭代修复
- 用户:提供目标站点/API 信息,仅在 AI 无法独立完成时执行辅助操作
- AI (You): Analyze the target site, write plugin code, write and execute test scripts, iterate and fix issues
- User: Provide target site/API information, and only perform auxiliary operations when AI cannot complete independently
核心原则:最小化用户操作
Core Principle: Minimize User Operations
能自己做的,绝不让用户做。 遵循以下优先级:
- AI 独立完成:直接用工具抓取页面、分析 HTML/JS、在终端运行脚本
- AI 做 + 用户确认:AI 分析后给出结论,请用户确认是否正确
- 用户操作(最后手段):仅当 AI 的工具无法获取到所需数据时,才让用户在浏览器中操作
Do everything you can on your own, never ask the user to do it. Follow the following priority:
- AI completes independently: Directly use tools to crawl pages, analyze HTML/JS, run scripts in the terminal
- AI does + user confirms: AI gives conclusions after analysis, asks the user to confirm if they are correct
- User operation (last resort): Only ask the user to operate in the browser when AI's tools cannot obtain the required data
与用户交互原则
Interaction Principles with Users
- 用户可能是零基础,不要使用未解释的技术术语
- 需要用户操作时,给出精确到按键级别的逐步指令,每次只下达一个操作
- 主动告知用户当前进度和下一步计划
- 不确定时必须询问:当对站点行为、API 含义、数据字段映射不确定时,先问再做
- Users may be beginners, do not use unexplained technical terms
- When requiring user operations, provide step-by-step instructions precise to the key press level, and only give one operation at a time
- Proactively inform the user of the current progress and next plan
- Must ask when unsure: When unsure about site behavior, API meaning, or data field mapping, ask first before proceeding
工作流程
Workflow
1. 收集信息 → 2. 判断路径 → 3. 分析数据源 → 4. 确定方法集合
→ 5. 逐个实现 → 6. 测试验证 → 7. 迭代修复 → 8. 输出最终插件1. 收集信息 → 2. 判断路径 → 3. 分析数据源 → 4. 确定方法集合
→ 5. 逐个实现 → 6. 测试验证 → 7. 迭代修复 → 8. 输出最终插件步骤 1:收集信息并检查环境
Step 1: Collect Information and Check Environment
先检查开发环境(在做任何其他事之前):
- 检查 Node.js 是否可用:
node --version - 询问用户:电脑上是否安装了 Chrome 或 Edge 浏览器?安装路径是什么?(Playwright 可以复用已安装的浏览器,避免下载)
- 安装 Playwright:(不需要下载 Chromium,后续用
npm install -D playwright复用用户的 Chrome)channel: 'chrome'
然后收集需求:
- 目标是什么?(某个音乐网站?已有的 API 文档?自建的服务?)
- 插件作者名?(会写入插件的 字段)
author
Check the development environment first (before doing anything else):
- Check if Node.js is available:
node --version - Ask the user: Do you have Chrome or Edge browser installed on your computer? What is the installation path? (Playwright can reuse the installed browser to avoid downloading)
- Install Playwright: (No need to download Chromium; use
npm install -D playwrightlater to reuse the user's Chrome)channel: 'chrome'
Then collect requirements:
- What is the target? (A certain music website? Existing API documentation? Self-built service?)
- Plugin author name? (Will be written into the field of the plugin)
author
步骤 2:判断路径
Step 2: Determine the Path
根据收集到的信息,选择对应路径:
路径 A:用户提供了 API 文档或接口规格
→ 阅读文档 → 将端点映射到插件方法 → 直接编写代码
路径 B:用户提供了网站 URL(页面内容直接可抓取)
→ 用工具抓取页面 HTML → 分析 DOM 结构 → 用 cheerio 编写解析逻辑
→ 详见 references/site-analysis-playbook.md "静态站点分析"
路径 C:用户提供了网站 URL(需逆向分析内部 API)
页面通过内部 API 异步加载数据,没有公开文档。需要观察浏览器的实际行为,然后用 axios 精确复现。
核心方法论——观察 → 分析 → 复现 → 验证:
- 观察:用 Playwright 加载页面,捕获所有网络请求,保存到本地
- 分析:基于本地缓存的数据做离线分析,找到关键的 API 端点和音频请求
- 复现:用 axios 复现相同的请求(包括完整的 Headers)
- 验证:在终端测试,如果失败则对比 Playwright 捕获的请求头与 axios 请求头的差异
→ 详见 references/site-analysis-playbook.md
Choose the corresponding path based on the collected information:
Path A: User provides API documentation or interface specifications
→ Read the documentation → Map endpoints to plugin methods → Write code directly
Path B: User provides website URL (page content can be crawled directly)
→ Use tools to crawl page HTML → Analyze DOM structure → Write parsing logic with cheerio
→ See references/site-analysis-playbook.md "Static Site Analysis"
Path C: User provides website URL (needs reverse analysis of internal APIs)
The page loads data asynchronously through internal APIs without public documentation. Need to observe the actual behavior of the browser, then accurately reproduce it with axios.
Core Methodology——Observe → Analyze → Reproduce → Verify:
- Observe: Use Playwright to load the page, capture all network requests, and save them locally
- Analyze: Perform offline analysis based on locally cached data to find key API endpoints and audio requests
- Reproduce: Reproduce the same request with axios (including complete Headers)
- Verify: Test in the terminal; if it fails, compare the difference between the request headers captured by Playwright and those of axios
→ See references/site-analysis-playbook.md
步骤 3:分析数据源
Step 3: Analyze Data Sources
快速预判站点类型:用工具抓取目标 URL 的 HTML,检查内容:
- 如果 HTML 中包含完整的数据内容(歌曲列表、歌手名等文本) → 静态站点,用 cheerio 解析
- 如果 HTML 很小(骨架 HTML)、包含大量 标签、或核心内容区域为空 → SPA 站点,直接使用 Playwright,不要在 axios 上浪费时间
<script>
遵循"观察-分析-复现-验证"方法论(详见 references/site-analysis-playbook.md)。
关键规则:
- 所有抓取内容先保存到本地文件,后续基于本地文件分析,避免重复请求
- 不要盲猜 URL——每个 API 端点必须来自页面内容或网络请求的实际观察
- 推断出的 API 和数据结构不确定时 → 向用户确认
Quickly predict the site type: Use tools to crawl the HTML of the target URL and check the content:
- If the HTML contains complete data content (song lists, artist names, etc. text) → Static site, parse with cheerio
- If the HTML is small (skeleton HTML), contains a large number of tags, or the core content area is empty → SPA site, use Playwright directly, do not waste time on axios
<script>
Follow the "Observe-Analyze-Reproduce-Verify" methodology (see references/site-analysis-playbook.md).
Key Rules:
- Save all crawled content to local files first, then analyze based on local files to avoid duplicate requests
- Do not guess URLs blindly——Every API endpoint must come from actual observation of page content or network requests
- When unsure about the inferred API and data structure → Confirm with the user
步骤 4-8:实现循环
Steps 4-8: Implementation Loop
确定数据源后:
- 根据站点分析结果,自动识别站点支持的功能(搜索、排行榜、歌单、歌词等),能适配的全部实现,适配不了的不写入插件——不需要询问用户实现哪些功能
- 从 和
search开始(最核心的两个方法)getMediaSource - 每完成一个方法,编写测试脚本并在终端中执行验证
- 通过后再实现下一个方法
- 全部完成后,添加元数据,输出最终 文件
.js
After determining the data source:
- Automatically identify the functions supported by the site based on site analysis results (search, leaderboard, playlist, lyrics, etc.), implement all that can be adapted, do not write those that cannot be adapted into the plugin——No need to ask the user which functions to implement
- Start with and
search(the two most core methods)getMediaSource - After completing each method, write a test script and execute it in the terminal for verification
- Implement the next method after passing the test
- After all are completed, add metadata and output the final file
.js
插件骨架
Plugin Skeleton
javascript
// 引入需要的模块(均为沙箱内置,无需安装)
const axios = require('axios');
const cheerio = require('cheerio');
module.exports = {
// ===== 必填属性 =====
platform: '插件名称',
// ===== 可选属性 =====
version: '0.0.1', // 语义化版本号
author: '作者名',
description: '插件说明',
srcUrl: 'https://example.com/plugin.js', // 远程更新地址
cacheControl: 'no-cache', // "cache" | "no-cache" | "no-store"
supportedSearchType: ['music', 'album', 'artist', 'sheet'],
userVariables: [{ key: 'cookie', name: 'Cookie', hint: '请输入你的 Cookie' }],
hints: {
importMusicSheet: ['支持以下格式的链接:...', 'https://example.com/playlist/123'],
},
// ===== 方法 =====
async search(query, page, type) {
/* ... */
},
async getMediaSource(musicItem, quality) {
/* ... */
},
// ... 更多方法
};javascript
// 引入需要的模块(均为沙箱内置,无需安装)
const axios = require('axios');
const cheerio = require('cheerio');
module.exports = {
// ===== 必填属性 =====
platform: '插件名称',
// ===== 可选属性 =====
version: '0.0.1', // 语义化版本号
author: '作者名',
description: '插件说明',
srcUrl: 'https://example.com/plugin.js', // 远程更新地址
cacheControl: 'no-cache', // "cache" | "no-cache" | "no-store"
supportedSearchType: ['music', 'album', 'artist', 'sheet'],
userVariables: [{ key: 'cookie', name: 'Cookie', hint: '请输入你的 Cookie' }],
hints: {
importMusicSheet: ['支持以下格式的链接:...', 'https://example.com/playlist/123'],
},
// ===== 方法 =====
async search(query, page, type) {
/* ... */
},
async getMediaSource(musicItem, quality) {
/* ... */
},
// ... 更多方法
};属性速查
Property Quick Reference
| 属性 | 必填 | 说明 |
|---|---|---|
| 是 | 插件名称,不可为"本地" |
| 否 | 语义化版本号,默认 |
| 否 | 插件作者 |
| 否 | 说明文字 |
| 否 | 远程 |
| 否 | |
| 否 | 声明支持的搜索类型数组,不填则认为全部支持 |
| 否 | 用户可配置变量数组,每项含 |
| 否 | 提示文案,键为方法名,值为字符串数组 |
| Property | Required | Description |
|---|---|---|
| Yes | Plugin name, cannot be "Local" |
| No | Semantic version number, default |
| No | Plugin author |
| No | Plugin description |
| No | Remote |
| No | Cache strategy for |
| No | Array of declared supported search types; if not filled, all are considered supported |
| No | Array of user-configurable variables, each containing |
| No | Prompt text, keys are method names, values are string arrays |
userVariables
用法
userVariablesuserVariables
Usage
userVariables定义:
javascript
userVariables: [{ key: 'cookie', name: 'Cookie', hint: '在浏览器登录后获取' }];运行时读取:
javascript
const cookie = env.getUserVariables().cookie;Definition:
javascript
userVariables: [{ key: 'cookie', name: 'Cookie', hint: '在浏览器登录后获取' }];Read at runtime:
javascript
const cookie = env.getUserVariables().cookie;方法速查
Method Quick Reference
所有方法均为 ,遇到错误应直接 。
完整签名、参数与返回值详见 references/plugin-protocol.md。
基本媒体类型字段详见 references/media-types.md。
asyncthrow| 方法 | 功能 | 核心入参 | 核心返回值 |
|---|---|---|---|
| 搜索 | | |
| 获取播放链接 | | |
| 获取歌词 | | |
| 专辑详情 | | |
| 歌单详情 | | |
| 歌手作品 | | |
| 补全歌曲信息 | | |
| 导入单曲 | | |
| 导入歌单 | | |
| 排行榜列表 | 无 | |
| 排行榜详情 | | |
| 推荐歌单标签 | 无 | |
| 按标签获取歌单 | | |
| 歌曲评论 | | |
All methods are ; throw errors directly when encountering them. For complete signatures, parameters, and return values, see references/plugin-protocol.md. For basic media type fields, see references/media-types.md.
async| Method | Function | Core Parameters | Core Return Value |
|---|---|---|---|
| Search | | |
| Get playback link | | |
| Get lyrics | | |
| Album details | | |
| Playlist details | | |
| Artist works | | |
| Complete song info | | |
| Import single song | | |
| Import playlist | | |
| Leaderboard list | None | |
| Leaderboard details | | |
| Recommended playlist tags | None | |
| Get playlists by tag | | |
| Song comments | | |
关键约定
Key Conventions
- 分页:从 1 开始;
page为isEnd表示到达最后一页,不填默认truetrue - 自动注入:返回的媒体项无需设置
platform,框架会自动添加platform - 必须有:每个媒体项必须包含
id字段id - 可扩展字段:等类型支持任意可序列化的扩展字段,这些字段会在后续方法调用时被传入。利用这一特性在
IMusicItem中附带后续方法所需的额外数据search - 错误处理:遇到错误直接 ,不要返回
thrownull
- Pagination: starts from 1;
pagebeingisEndmeans reaching the last page, default istrueif not filledtrue - auto-injection: No need to set
platformfor returned media items; the framework will add it automaticallyplatform - is required: Each media item must contain the
idfieldid - Extensible fields: Types like support any serializable extended fields, which will be passed in during subsequent method calls. Use this feature to attach additional data needed by subsequent methods in
IMusicItemsearch - Error handling: Throw errors directly when encountering them, do not return
null
沙箱环境
Sandbox Environment
插件运行在隔离沙箱中(),有 10 秒执行超时。
vm.createContextPlugins run in an isolated sandbox () with a 10-second execution timeout.
vm.createContext可用模块(通过 require
引入)
requireAvailable Modules (Import via require
)
require| 模块 | 用途 |
|---|---|
| HTTP 请求 |
| HTML 解析(类 jQuery 语法) |
| 加解密 |
| 日期时间处理 |
| 大整数运算 |
| URL 参数序列化 |
| HTML 实体编码/解码 |
| WebDAV 操作 |
注意:不在此列表中的模块无法 。如需使用其它库(如 lodash),必须用 webpack 等工具将源码打包到插件文件中。
require| Module | Purpose |
|---|---|
| HTTP requests |
| HTML parsing (jQuery-like syntax) |
| Encryption and decryption |
| Date and time handling |
| Big integer operations |
| URL parameter serialization |
| HTML entity encoding/decoding |
| WebDAV operations |
Note: Modules not in this list cannot be d. If you need to use other libraries (such as lodash), you must package the source code into the plugin file using tools like webpack.
require全局可用 API
Globally Available APIs
- ,
fetch,URL,URLSearchParamsAbortController - ,
Buffer,TextEncoderTextDecoder - ,
btoa,atob,encodeURIComponentdecodeURIComponent - ,
JSONconsole.log/warn/error - ,
setTimeout,setIntervalPromise - — 读取用户变量
env.getUserVariables() - — 操作系统
env.os - — 应用版本
env.appVersion - — 当前语言
env.lang
- ,
fetch,URL,URLSearchParamsAbortController - ,
Buffer,TextEncoderTextDecoder - ,
btoa,atob,encodeURIComponentdecodeURIComponent - ,
JSONconsole.log/warn/error - ,
setTimeout,setIntervalPromise - — Read user variables
env.getUserVariables() - — Operating system
env.os - — App version
env.appVersion - — Current language
env.lang
开发模式示例
Development Mode Examples
模式一:JSON API 对接
Mode 1: JSON API Integration
已知某服务的 API 接口,直接请求并转换数据格式:
javascript
const axios = require('axios');
module.exports = {
platform: '示例API源',
version: '0.0.1',
supportedSearchType: ['music'],
async search(query, page, type) {
if (type !== 'music') return { isEnd: true, data: [] };
const res = await axios.get('https://api.example.com/search', {
params: { keyword: query, page, limit: 20 },
});
const list = res.data.songs || [];
return {
isEnd: list.length < 20,
data: list.map((item) => ({
id: item.songId,
title: item.songName,
artist: item.singerName,
album: item.albumName,
artwork: item.coverUrl,
duration: item.duration,
// 扩展字段:后续 getMediaSource 会用到
extraId: item.fileHash,
})),
};
},
async getMediaSource(musicItem, quality) {
const res = await axios.get('https://api.example.com/play', {
params: { hash: musicItem.extraId, quality },
});
if (!res.data.url) throw new Error('无法获取播放链接');
return { url: res.data.url };
},
};Known API interface of a service, directly request and convert data format:
javascript
const axios = require('axios');
module.exports = {
platform: '示例API源',
version: '0.0.1',
supportedSearchType: ['music'],
async search(query, page, type) {
if (type !== 'music') return { isEnd: true, data: [] };
const res = await axios.get('https://api.example.com/search', {
params: { keyword: query, page, limit: 20 },
});
const list = res.data.songs || [];
return {
isEnd: list.length < 20,
data: list.map((item) => ({
id: item.songId,
title: item.songName,
artist: item.singerName,
album: item.albumName,
artwork: item.coverUrl,
duration: item.duration,
// 扩展字段:后续 getMediaSource 会用到
extraId: item.fileHash,
})),
};
},
async getMediaSource(musicItem, quality) {
const res = await axios.get('https://api.example.com/play', {
params: { hash: musicItem.extraId, quality },
});
if (!res.data.url) throw new Error('无法获取播放链接');
return { url: res.data.url };
},
};模式二:HTML 页面解析
Mode 2: HTML Page Parsing
目标网站没有公开 API,直接抓取 HTML 并用 cheerio 解析:
javascript
const axios = require('axios');
const cheerio = require('cheerio');
module.exports = {
platform: '示例HTML源',
version: '0.0.1',
supportedSearchType: ['music'],
async search(query, page, type) {
if (type !== 'music') return { isEnd: true, data: [] };
const rawHtml = (
await axios.get('https://example.com/search', {
params: { q: query, page },
})
).data;
const $ = cheerio.load(rawHtml);
const results = [];
$('.search-result-item').each((i, el) => {
results.push({
id: $(el).attr('data-id'),
title: $(el).find('.song-title').text().trim(),
artist: $(el).find('.artist-name').text().trim(),
artwork: $(el).find('img').attr('src'),
url: $(el).find('audio').attr('src'), // 如果页面直接包含音源
});
});
return {
isEnd: $('.next-page').length === 0,
data: results,
};
},
};The target website has no public API; directly crawl HTML and parse with cheerio:
javascript
const axios = require('axios');
const cheerio = require('cheerio');
module.exports = {
platform: '示例HTML源',
version: '0.0.1',
supportedSearchType: ['music'],
async search(query, page, type) {
if (type !== 'music') return { isEnd: true, data: [] };
const rawHtml = (
await axios.get('https://example.com/search', {
params: { q: query, page },
})
).data;
const $ = cheerio.load(rawHtml);
const results = [];
$('.search-result-item').each((i, el) => {
results.push({
id: $(el).attr('data-id'),
title: $(el).find('.song-title').text().trim(),
artist: $(el).find('.artist-name').text().trim(),
artwork: $(el).find('img').attr('src'),
url: $(el).find('audio').attr('src'), // 如果页面直接包含音源
});
});
return {
isEnd: $('.next-page').length === 0,
data: results,
};
},
};模式三:直接拼接(无需网络请求)
Mode 3: Direct Concatenation (No Network Requests Needed)
音源 URL 可直接由已知信息拼接得出:
javascript
module.exports = {
platform: '示例拼接源',
version: '0.0.1',
cacheControl: 'no-store',
async getMediaSource(musicItem, quality) {
return {
url: 'https://cdn.example.com/audio/' + musicItem.id + '.mp3',
};
},
};The audio source URL can be directly concatenated from known information:
javascript
module.exports = {
platform: '示例拼接源',
version: '0.0.1',
cacheControl: 'no-store',
async getMediaSource(musicItem, quality) {
return {
url: 'https://cdn.example.com/audio/' + musicItem.id + '.mp3',
};
},
};测试流程
Testing Process
完成插件编写后,创建一个测试脚本在终端中运行:
javascript
// test-plugin.js
const plugin = require('./my-plugin.js');
async function test() {
console.log('=== 测试 search ===');
try {
const searchResult = await plugin.search('测试关键词', 1, 'music');
console.log('搜索结果数量:', searchResult.data.length);
console.log('是否最后一页:', searchResult.isEnd);
if (searchResult.data.length > 0) {
console.log('第一条结果:', JSON.stringify(searchResult.data[0], null, 2));
console.log('\n=== 测试 getMediaSource ===');
const source = await plugin.getMediaSource(searchResult.data[0], 'standard');
console.log('播放链接:', source?.url ? '✓ 获取成功' : '✗ 获取失败');
console.log('详细信息:', JSON.stringify(source, null, 2));
}
} catch (e) {
console.error('测试失败:', e.message);
}
}
test();执行方式:
bash
node test-plugin.js测试要点:
- 每个方法单独测试,确认返回值结构正确
- 验证 字段存在且有效
id - 验证分页逻辑(测试 page=1 和 page=2)
- 验证 返回的 URL 可访问
getMediaSource
注意:本地 Node.js 测试时, 不可用。如果插件使用了 ,需要在测试脚本中模拟:
env.getUserVariables()userVariablesjavascript
// 在 require 插件之前模拟 env
global.env = {
getUserVariables: () => ({ cookie: '测试用cookie值' }),
os: 'win32',
appVersion: '0.0.1',
lang: 'zh-CN',
};
const plugin = require('./my-plugin.js');After completing the plugin writing, create a test script to run in the terminal:
javascript
// test-plugin.js
const plugin = require('./my-plugin.js');
async function test() {
console.log('=== 测试 search ===');
try {
const searchResult = await plugin.search('测试关键词', 1, 'music');
console.log('搜索结果数量:', searchResult.data.length);
console.log('是否最后一页:', searchResult.isEnd);
if (searchResult.data.length > 0) {
console.log('第一条结果:', JSON.stringify(searchResult.data[0], null, 2));
console.log('\n=== 测试 getMediaSource ===');
const source = await plugin.getMediaSource(searchResult.data[0], 'standard');
console.log('播放链接:', source?.url ? '✓ 获取成功' : '✗ 获取失败');
console.log('详细信息:', JSON.stringify(source, null, 2));
}
} catch (e) {
console.error('测试失败:', e.message);
}
}
test();Execution method:
bash
node test-plugin.jsTesting Points:
- Test each method individually to confirm the return value structure is correct
- Verify that the field exists and is valid
id - Verify pagination logic (test page=1 and page=2)
- Verify that the URL returned by is accessible
getMediaSource
Note: When testing locally with Node.js, is not available. If the plugin uses , simulate it in the test script:
env.getUserVariables()userVariablesjavascript
// 在 require 插件之前模拟 env
global.env = {
getUserVariables: () => ({ cookie: '测试用cookie值' }),
os: 'win32',
appVersion: '0.0.1',
lang: 'zh-CN',
};
const plugin = require('./my-plugin.js');发布与更新
Release and Updates
托管到 GitHub
Host on GitHub
- 创建一个 GitHub 仓库(或使用已有仓库)
- 将插件 文件上传到仓库
.js - 获取文件的 raw 链接(格式:)
https://raw.githubusercontent.com/用户名/仓库名/分支/文件名.js - 将此链接填入插件的 字段
srcUrl
- Create a GitHub repository (or use an existing one)
- Upload the plugin file to the repository
.js - Get the raw link of the file (format: )
https://raw.githubusercontent.com/username/repository/branch/filename.js - Fill this link into the field of the plugin
srcUrl
用户安装
User Installation
用户在 MusicFree 中通过 "从 URL 安装插件" 功能,粘贴插件的 raw 链接即可安装。
Users can paste the raw link of the plugin into the "Install Plugin from URL" feature in MusicFree to install it.
版本更新
Version Updates
- 修改 字段(遵循语义化版本,如
version→"0.0.1")"0.0.2" - 更新代码并推送到 GitHub
- 用户在应用内点击"更新插件"即可自动获取新版本
- Modify the field (follow semantic versioning, e.g.,
version→"0.0.1")"0.0.2" - Update the code and push to GitHub
- Users can click "Update Plugin" in the app to automatically get the new version
注意事项
Notes
- 错误处理:方法遇到错误应直接 ,不要
throwreturn null - 语法兼容:优先使用 ES8 及以下语法。可放心使用,
async/await、?.在桌面版可用,但移动端可能需要 babel 转译?? - 网络请求:移动端 axios 会自带默认 headers(包括 ),某些服务器可能因此返回异常,可手动设置
User-Agent: okhttp/4.10.0覆盖headers - 安全:不要在插件中修改内置 npm 包的全局属性
- 超时:每次方法调用有 10 秒超时限制,避免做过多请求或大量计算
- Error Handling: Throw errors directly when methods encounter them, do not return
null - Syntax Compatibility: Prioritize using ES8 or lower syntax. can be used safely;
async/awaitand?.are available in the desktop version but may require babel transpilation for the mobile version?? - Network Requests: Axios on the mobile version will come with default headers (including ), some servers may return exceptions due to this, which can be manually overridden by setting
User-Agent: okhttp/4.10.0headers - Security: Do not modify global properties of built-in npm packages in plugins
- Timeout: Each method call has a 10-second timeout limit; avoid making too many requests or doing heavy computations
Skill 更新
Skill Updates
本 Skill 的最新版本托管在官方仓库:https://github.com/maotoumao/musicfree-skills
The latest version of this Skill is hosted in the official repository: https://github.com/maotoumao/musicfree-skills