SuperAPI AI Directory

musicfree-plugin-dev

Coding & development

View repository

Updated 10-16 00:05

About this skill

MusicFree Plugin Development

Strict Code of Conduct

The following rules apply throughout the entire plugin development processalways take effect, and violating them will cause development to fail:

  1. Do not guess URLs blindly: do not try /api/search、/v1/song and similar paths out of thin air. Every URL must come from page content or a network request captured by Playwright
  2. Do not search the web for APIs: do not search for "XX Music API", "free music interface", etc. All data sources must be obtained through direct observation of the target site, and must not come from web searches
  3. Do not probe in batches: do not send multiple requests at the same time to try different parameter combinations
  4. Do not make repeated requests: request each URL only once, save the results to a local file, and then analyze based on the local data
  5. Do not give up and say "a browser environment is required": if axios fails but the browser can access it, the difference must be in the request headers. Use Playwright to capture the real request headers and then complete them in axios

When encountering difficulties, the only correct approach is: use Playwright to observe the browser's real behavior. Do not guess, do not search, go observe.

Roles and Collaboration Mode

  • AI (you): analyze the target site, write plugin code, write and execute test scripts, and iterate on fixes
  • User: provide target site/API information, and perform auxiliary operations only when the AI cannot complete them independently

Core principle: minimize user operations

Anything you can do yourself, never make the user do. Follow this priority order:

  1. AI completes independently: directly use tools to fetch pages, analyze HTML/JS, and run scripts in the terminal
  2. AI does it + user confirms: after analysis, the AI gives a conclusion and asks the user to confirm whether it is correct
  3. User operation (last resort): only when the AI's tools cannot obtain the required data should the user operate in the browser

Principles for Interacting with the User

  • The user may have zero foundation,do not use unexplained technical jargon
  • When user operation is needed, providestep-by-step instructions precise down to the key press, and give only one operation at a time
  • Proactively inform the user of the current progress and the next plan
  • When uncertain, you must ask: when uncertain about site behavior, API meanings, or data field mappings, ask first and then act

Workflow

1. 收集信息 → 2. 判断路径 → 3. 分析数据源 → 4. 确定方法集合
→ 5. 逐个实现 → 6. 测试验证 → 7. 迭代修复 → 8. 输出最终插件

Step 1: Collect information and check the environment

First check the development environment(before doing anything else):

  1. Check whether Node.js is available:node --version
  2. Ask the user: is Chrome or Edge installed on the computer? What is the installation path? (Playwright can reuse an already installed browser to avoid downloading)
  3. Install Playwright:npm install -D playwright(no need to download Chromium; later use channel: 'chrome' Reuse the user's Chrome)

Then gather requirements:

  • What is the target? (A music website? Existing API documentation? A self-built service?)
  • Plugin author name? (Will be written into the plugin's author field)

Step 2: Determine the path

Based on the gathered information, choose the corresponding path:

Path A: The user provided API documentation or interface specs → Read the documentation → Map the endpoints to plugin methods → Write the code directly

Path B: The user provided a website URL (page content can be scraped directly) → Use tools to scrape the page HTML → Analyze the DOM structure → Write the parsing logic with cheerio → See references/site-analysis-playbook.md "Static site analysis"

Path C: The user provided a website URL (requires reverse-engineering the internal API) The page loads data asynchronously through an internal API, with no public documentation. You need toobserve the browser's actual behavior and then reproduce it precisely with axios。

Core methodology—Observe → Analyze → Reproduce → Verify:

  1. Observe: Use Playwright to load the page, capture all network requests, and save them locally
  2. Analyze: Perform offline analysis based on the locally cached data to find the key API endpoints and audio requests
  3. Reproduce: Use axios to reproduce the same requests (including the complete Headers)
  4. Verify: Test in the terminal; if it fails, compare the differences between the request headers captured by Playwright and the axios request headers

→ See references/site-analysis-playbook.md

Step 3: Analyze the data source

Quickly predict the site type: Use tools to scrape the target URL's HTML and check the content:

  • If the HTML contains the complete data content (song list, artist names, and other text) → static site, parse it with cheerio
  • If the HTML is very small (skeleton HTML), contains a large number of <script> tags, or the core content area is empty → SPA site,use Playwright directly, and do not waste time on axios

Follow the "Observe-Analyze-Reproduce-Verify" methodology (see references/site-analysis-playbook.md)。

Key rules:

  • Save all scraped content to local files first, then analyze based on the local files afterward to avoid repeated requests
  • Do not blindly guess URLs—every API endpoint must come from actual observation of the page content or network requests
  • When the inferred API and data structure are uncertain → confirm with the user

Steps 4-8: Implementation loop

After determining the data source:

  1. Based on the site analysis results, automatically identify the features supported by the site (search, rankings, playlists, lyrics, etc.),implement all that can be adapted, and do not include in the plugin those that cannot be adapted—no need to ask the user which features to implement
  2. Start with search and getMediaSource (the two most core methods)
  3. After completing each method, write a test script and run it in the terminal to verify
  4. Only after it passes, implement the next method
  5. After everything is complete, add metadata and output the final .js file

Plugin skeleton

// 引入需要的模块(均为沙箱内置,无需安装)
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

Attribute Required Description
platform Yes Plugin name, cannot be "local"
version No Semantic version number, default "0.0.0"
author No Plugin author
description No Description text
srcUrl No Remote .js file direct link, used for in-app updates
cacheControl No getMediaSource result caching policy
supportedSearchType No Array of declared supported search types; if omitted, all are considered supported
userVariables No Array of user-configurable variables, each item contains key(required),name、hint
hints No Prompt text, key is the method name, value is a string array

userVariables Usage

Definition:

userVariables: [{ key: 'cookie', name: 'Cookie', hint: '在浏览器登录后获取' }];

Runtime read:

const cookie = env.getUserVariables().cookie;

Method quick reference

All methods are async, and on error should directly throw. For complete signatures, parameters, and return values, see references/plugin-protocol.md. For basic media type fields, see references/media-types.md。

Method Function Core input parameters Core return value
search Search (query, page, type) { isEnd, data: [] }
getMediaSource Get playback link (musicItem, quality) { url, headers? }
getLyric Get lyrics (musicItem) { rawLrc?, translation? }
getAlbumInfo Album details (albumItem, page) { isEnd, musicList, albumItem? }
getMusicSheetInfo Playlist details (sheetItem, page) { isEnd, musicList, sheetItem? }
getArtistWorks Artist works (artistItem, page, type) { isEnd, data: [] }
getMusicInfo Complete song information (musicItem) Partial<IMusicItem>
importMusicItem Import single track (urlLike) IMusicItem
importMusicSheet Import playlist (urlLike) IMusicItem[]
getTopLists Ranking list None [{ title?, data: [] }]
getTopListDetail Ranking details (topListItem, page) { isEnd, musicList }
getRecommendSheetTags Recommended playlist tags None { pinned?, data: [] }
getRecommendSheetsByTag Get playlists by tag (tag, page) { isEnd, data: [] }
getMusicComments Song comments (musicItem, page) { isEnd, data: [] }

Key conventions

  • Pagination:page starts from 1;isEnd is true indicates the last page has been reached; if omitted, defaults to true
  • platform auto-injected: returned media items do not need to set platform, the framework will add it automatically
  • id must have: each media item must contain the id field
  • Extensible fields:IMusicItem and other types support arbitrary serializable extension fields; these fields will be passed in during subsequent method calls. Use this feature to include in search the extra data needed by subsequent methods
  • Error handling: on error directly throw, do not return null

Sandbox environment

The plugin runs in an isolated sandbox (vm.createContext), with a 10-second execution timeout.

Available modules (imported via require )

Module Purpose
axios HTTP requests
cheerio HTML parsing (jQuery-like syntax)
crypto-js Encryption/decryption
dayjs Date and time handling
big-integer Big integer arithmetic
qs URL parameter serialization
he HTML entity encoding/decoding
webdav WebDAV operations

Note: modules not in this list cannot require. If you need to use other libraries (such as lodash), you must use tools like webpack to bundle the source code into the plugin file.

Globally Available APIs

  • fetch, URL, URLSearchParams, AbortController
  • Buffer, TextEncoder, TextDecoder
  • btoa, atob, encodeURIComponent, decodeURIComponent
  • JSON, console.log/warn/error
  • setTimeout, setInterval, Promise
  • env.getUserVariables() — Read user variables
  • env.os — Operating system
  • env.appVersion — App version
  • env.lang — Current language

Development Mode Examples

Mode 1: JSON API Integration

When the API endpoint of a service is known, request it directly and convert the data format:

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 };
    },
};

Mode 2: HTML Page Parsing

When the target website has no public API, scrape the HTML directly and parse it with cheerio:

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 Request Needed)

The audio source URL can be concatenated directly from known information:

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

After completing the plugin, create a test script and run it in the terminal:

// 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();

How to Run:

node test-plugin.js

Testing Points:

  • Test each method individually to confirm the return value structure is correct
  • Verify that the id field exists and is valid
  • Verify the pagination logic (test page=1 and page=2)
  • Verify that the URL returned by getMediaSource is accessible

Note: when testing locally with Node.js,env.getUserVariables() is unavailable. If the plugin uses userVariables, you need to simulate it in the test script:

// 在 require 插件之前模拟 env
global.env = {
    getUserVariables: () => ({ cookie: '测试用cookie值' }),
    os: 'win32',
    appVersion: '0.0.1',
    lang: 'zh-CN',
};
const plugin = require('./my-plugin.js');

Publishing and Updating

Hosting on GitHub

  1. Create a GitHub repository (or use an existing repository)
  2. Upload the plugin .js file to the repository
  3. Get the raw link of the file (format:https://raw.githubusercontent.com/用户名/仓库名/分支/文件名.js)
  4. Fill this link into the plugin's srcUrl field

User Installation

In MusicFree, users can install it through the "Install Plugin from URL" feature by pasting the plugin's raw link.

Version Updates

  1. Modify the version field (follow semantic versioning, e.g. "0.0.1" → "0.0.2")
  2. Update the code and push it to GitHub
  3. Users can click "Update Plugin" in the app to automatically get the new version

Precautions

  • Error Handling: when a method encounters an error, it should directly throw, do not return null
  • Syntax Compatibility: prefer to use ES8 and below syntax.async/await can be used with confidence,?.、?? is available on the desktop version, but may require babel transpilation on mobile
  • Network Requests: on mobile, axios comes with default headers (including User-Agent: okhttp/4.10.0), and some servers may return exceptions because of this. You can manually set headers to override
  • Security: do not modify the global properties of built-in npm packages in the plugin
  • Timeout: each method call has a 10-second timeout limit, avoid making too many requests or doing a large amount of computation

Skill Updates

The latest version of this Skill is hosted in the official repository:https://github.com/maotoumao/musicfree-skills