跳到主要内容

插件的方法

插件实例中的方法用来定义插件的一些功能函数,如请求数据等,所有方法都可以省略,省略则意味着插件未实现对应的功能。

插件功能缺失时,反映在UI上可能表现为:

  1. 功能无法使用(如点击歌曲无法播放);
  2. 不展示该插件(如搜索结果中不展示某插件的搜索结果等)。
方法名入参返回值备注
search搜索关键词、页码、媒体、歌单类型见下根据关键词搜索媒体资源
getMediaSourceMediaBase 类型音乐、音质见下(根据音质)获取音源
getMusicInfoMediaBase 类型音乐见下获取音乐详情
getLyricMediaBase 类型音乐见下获取歌词
getAlbumInfoAlbumItem 类型专辑、页码见下获取完整的专辑信息(包括音乐列表)
getMusicSheetInfoMusicSheetItem 类型歌单、页码见下获取完整的歌单信息(包括音乐列表)
getArtistWorksArtistItem 类型作者、页码、媒体类型见下获取某个作者的音乐/专辑
importMusicItem包含目标链接的字符串MusicItem 类型音乐导入单曲
importMusicSheet包含目标链接的字符串MusicItem 类型数组导入歌单
getTopListsMusicSheetGroupItem 类型歌单集合获取排行榜歌单列表
getTopListDetailMusicSheetItem 类型歌单带音乐列表的 MusicSheetItem 类型专辑获取排行榜中歌单的详细音乐列表
getRecommendSheetTags见下热门歌单的tag集合
getRecommendSheetsByTagtag,页码见下某个tag下的所有歌单

搜索(search)

搜索方法,可省略。当用户在app内点击搜索、下拉刷新、触达搜索底部时调用。
当插件中没有此函数时,搜索结果的面板中就不会出现这个插件。

方法签名

    type SupportMediaType = 'music' | 'album' | 'artist' | 'sheet';

type SupportMediaItem = {
music: MusicItem;
album: AlbumItem;
artist: ArtistItem;
sheet: MusicSheetItem;
};

interface ISearchResult<T extends SupportMediaType> {
isEnd?: boolean;
data: SupportMediaItem[T][];
}

type search = <T extends SupportMediaType>(
query: string,
page: number,
type: T,
) => Promise<ISearchResult<T>>

参数和返回值

方法的入参依次为:

  • query:字符串类型,为用户本次搜索的关键词;
  • page:数字类型,为用户本次搜索的页码;页码数从1开始;
  • type,取值为music、album、artist或者sheet,即音乐、专辑、作者、歌单。

方法的返回值为形如:

    {
isEnd: true, // 是否到达页尾
data: [] // 媒体对象列表
}

的对象。

  • isEnd为布尔类型,可省略。true表示当前关键词的搜索已结束(到达页尾,无更多数据);为false表示还有下一页。如果省略,app内会按true进行处理。
  • data为媒体类型数组的搜索结果。当入参的type为music时,此字段应当为音乐类型的数组;当入参的type为music时,此字段应当为专辑类型的数组;当入参的type为artist时,此字段应当为作者类型的数组;

注意:你可以在搜索结果中扩展任何可被序列化的字段;这些字段在下文的获取音源方法中等也会被传入,因此最好保持你的插件基于“基本媒体类型”有固定字段的扩展,防止字段不兼容的情况发生。

举例

你可以把所有的逻辑都写在模块导出中:

module.exports = {
async search(query, page, type){
// 搜索音乐
if(type === 'music') {
return {
isEnd: true,
data: []
};
}
// 搜索专辑
if (type === 'album') {
return {
isEnd: true,
data: []
};
}
// 其他情况(搜索作者)
return {
isEnd: true,
data: []
}
}
}

如果逻辑太多,维护成本太高的话,也可以拆到不同的函数中:

const axios = require('axios');

// 搜索歌曲
async function searchMusic(query, page) {
// 网络请求
const result = (await axios.get("http://example.upup.fun", {
params: {
query: query,
page: page,
pageSize: 20,
otherParams: "something"
}
})).data;

// 可以执行一些过滤逻辑,并把接口返回的结果映射为MusicItem类型的对象
return {
isEnd: true, // 分页已结束, 根据实际情况设置即可
data: result.map(item => ({
id: item.id,
title: item.title || '没有名字的音乐',
// ...
}))
};
}

// 搜索专辑
async function searchAlbum(query, page) {

// 类似上述逻辑,只不过data内返回的是AlbumItem类型的对象
return {
isEnd: true, // 分页已结束, 根据实际情况设置即可
data: [
{
id: 'test001',
title: 'xxx',
// ...
}
];
};
}

// 模块导出
module.exports = {
async search(query, page, type){
// 搜索音乐
if(type === 'music') {
return searchMusic(query, page);
}
// 搜索专辑
if (type === 'album') {
return searchAlbum(query, page);
}
// 其他情况(搜索作者)
return {
isEnd: true,
data: []
}
}
}

播放/下载相关

以下三个方法为播放/下载相关的方法。整体的过程示意图如下:

获取音源(getMediaSource)

获取音乐的真实URL,可省略。

当用户在app内点击播放、下载时调用。当插件中没有此函数时,会自动取入参音乐类型中的url字段(如果有)作为真实音源。 第二个参数为音质,可以根据此参数返回不同音质的音源,也可以忽略此参数。

APP内调用时,如果当前音质的音乐不存在,则会自动再次调用该函数,传入一个更高(或者更低)的音质。直至找到一个有效音质;或者播放/下载失败,跳过。

方法签名

    interface IMediaSourceResult {
/** 请求URL所需要的headers */
headers?: Record<string, string>;
/** 请求URL所需要的user-agent */
userAgent?: string;
/** 音源 */
url: string;
}

type getMediaSource = (musicItem: MusicItem, quality?: 'low' | 'standard' | 'high' | 'super') => Promise<IMediaSourceResult | null>

参数和返回值

方法的入参为音乐类型,即搜索得到的音乐类型,可能包含一些你额外扩展的字段。
返回值为IMediaSourceResult或null。

举例


module.exports = {
async getMediaSource(musicItem) {
return {
url: "https://example.upup.fun/example.mp3"
}
}
}

获取音乐详情(getMusicInfo)

获取音乐详情的方法,可省略。 当用户在app内点击播放时调用。

播放时会先执行getMediaSource获取音源,接下来执行getMusicInfo获取完整的音乐信息。比如某些搜索结果中不包含专辑封面的歌曲,就可以在这里补充完整。

方法签名

    type getMusicInfo = (musicBase: MediaBase) => Promise<Partial<MusicItem> | null>;

参数和返回值

方法的入参为: MediaBase 类型音乐

方法的返回值为该音乐的部分或者完整属性(即MusicItem类型的部分字段,其中platform字段和$字段会自动忽略):

举例

module.exports = {
// 其他属性或方法...
async getMusicInfo(musicItem){
return {
artwork: "https://example.upup.fun/coverimage.png"
}
}
}

获取歌词(getLyric)

获取歌词,可省略。 当用户在app内点击切换到歌词页时调用。app会优先使用返回值的rawLrc字段。

方法签名

    interface ILyricSource {
lrc?: string; // 歌词文件的链接
rawLrc?: string; // 文本格式的歌词
}

type getLyric = (musicItem: IMusic.MusicItemBase) => Promise<ILyricSource | null>;

参数和返回值

方法的入参为: MediaBase 类型音乐

方法的返回值为ILyricSource

举例


module.exports = {
async getLyric(musicItem){
return {
lrc: "http://xxx.lrc", // 链接
rawLrc: "[00:00.00]xxxx" // 文本格式的歌词
}
}
}

获取专辑详情(getAlbumInfo)

获取专辑详情,可省略。 当用户进入到专辑详情页时调用,用来补全albumItem缺失的属性,以及获取专辑下的音乐列表。

方法签名

    interface IGetAlbumInfoResult = {
isEnd?: boolean;
musicList: MusicItem[],
albumItem?: Partial<AlbumItem>
}

type getAlbumInfo = (albumItem: AlbumItem, page: number) => Promise<IGetAlbumInfoResult>;

参数和返回值

方法的入参为:AlbumItem 类型专辑以及当前页码(从1开始)
返回值包括三个字段:

  1. isEnd: 专辑内歌曲是否已经到达页尾;默认是true
  2. musicList: 专辑内的歌曲列表
  3. albumItem: 专辑的其他信息(仅在page<=1时返回即可)

举例


module.exports = {
async getAlbumInfo(albumItem, page) {
return {
isEnd: true,
musicList: [],
}
}
}

获取歌单详情(getMusicSheetInfo)

获取专辑详情,可省略。 当用户进入到歌单详情页时调用,用来补全sheetItem缺失的属性,以及获取歌单下的音乐列表。

方法签名

    interface IGetSheetInfoResult = {
isEnd?: boolean;
musicList: MusicItem[],
sheetItem?: Partial<MusicSheetItem>
}

type getMusicSheetInfo = (sheetItem: MusicSheetItem, page: number) => Promise<IGetSheetInfoResult>;

参数和返回值

方法的入参为:MusicSheetItem 类型歌单 以及当前页码(从1开始)
返回值包括三个字段:

  1. isEnd: 专辑内歌曲是否已经到达页尾;默认是true
  2. musicList: 专辑内的歌曲列表
  3. sheetItem: 专辑的其他信息(仅在page<=1时返回即可)

举例


module.exports = {
async getMusicSheetInfo(albumItem, page) {
return {
isEnd: true,
musicList: [],
}
}
}

获取作者作品(getArtistWorks)

获取作者作品,可省略。
当用户进入作者详情页时调用,用来获得作者的歌曲/专辑信息。

方法签名

    type ArtistMediaType = 'music' | 'album';

type getArtistWorks = <T extends ArtistMediaType>(
artistItem: ArtistItem,
page: number,
type: T,
) => Promise<ISearchResult<T>>;

参数和返回值

方法的入参依次为:

  • artistItem:ArtistItem 类型作者
  • page:数字类型,为用户本次搜索的页码;页码数从1开始;
  • type,字符串类型,取值为music、album,即音乐、专辑。

方法的返回同搜索的返回值,只不过没有“ArtistItem数组”。

举例


module.exports = {
async getArtistWorks(artistItem, page, type){
// 获取作者的音乐
if(type === 'music') {
return {
isEnd: true,
data: []
}
}
// 获取作者的专辑
if (type === 'album') {
return {
isEnd: false,
data: []
}
}
}
}

导入单曲(importMusicItem)

导入单曲,可省略。省略时插件页不会看到“导入单曲”按钮。
当用户进入侧边栏插件页,点击“导入单曲”后会进行导入。

方法签名

    type importMusicItem = (urlLike: string) => Promise<MusicItem>

参数和返回值

方法的入参为用户输入的字符串,你可以通过在插件中匹配特定的信息执行后续操作。

方法的返回值是一个音乐类型的对象。

举例

const axios = require('axios');

module.exports = {
async importMusicItem(urlLike){
const id = urlLike.match(/http:\/\/www\.foo\.bar\/([0-9]+)/)[1];
const musicItem = (await axios.get('http://bar.foo', {
params: {id}
})).data;
return musicItem;
}
}

导入歌单(importMusicSheet)

导入歌单,可省略。省略时插件页不会看到“导入歌单”按钮。

当用户进入侧边栏插件页,点击“导入歌单”后会进行导入。

方法签名

    type importMusicSheet = (urlLike: string) => Promise<MusicItem[]>

参数和返回值

方法的入参为一个包含url的字符串。

方法的返回值是音乐类型的数组。

举例

const axios = require('axios');

module.exports = {
async importMusicSheet(urlLike){
const id = urlLike.match(/http:\/\/www\.foo\.bar\/([0-9]+)/)[1];
const musicItems = (await axios.get('http://bar.foo/getlist', {
params: {id}
})).data;
return musicItems;
}
}


以下两个方法为榜单用到的方法。

获取榜单列表(getTopLists)

获取榜单列表,可省略。

当插件中没有此函数时,首页-榜单面板中不会出现该插件。

方法签名

    type getTopLists = () => Promise<MusicSheetGroupItem[]>

参数和返回值

无入参。

方法的返回值是一个榜单列表对象。如果榜单是静态的(参数不会动态变化),可以直接硬编码在插件中。

举例


module.exports = {
async getTopLists(){
return [{
title: "榜单类别1",
data: [{
id: "新歌榜的ID",
description: "新歌榜的描述",
coverImg: "新歌榜的封面",
title: "新歌榜",
}]
}]
}
}

获取榜单详情(getTopListDetail)

获取榜单详情(榜单中的音乐),可省略。

方法签名

    type WithMusicList<T> = T & {
musicList?: MusicItem[];
};

type getTopListDetail = (topListItem: MusicSheetItem) => Promise<WithMusicList<MusicSheetItem>>

参数和返回值

方法的入参是getTopLists返回的榜单条目。

方法的返回值是一个带有音乐列表的榜单信息。

举例


module.exports = {
async getTopListDetail(topListItem){
return {
id: "新歌榜的ID",
description: "新歌榜的描述",
coverImg: "新歌榜的封面",
title: "新歌榜",
musicList: []
};
}
}

获取热门歌单tag(getRecommendSheetTags)

获取热门歌单的tag分类,可省略

方法签名


interface IGetRecommendSheetTagsResult {
// 固定的tag
pinned?: MusicSheetItem[];
data?: MusicSheetGroupItem[];
}

type getRecommendSheetTags = () => Promise<IGetRecommendSheetTagsResult>;

参数和返回值

方法无入参。

方法的返回值是一系列标签列表,其中pinned表示固定在顶部的标签;data表示标签列表面板中的标签。

举例


module.exports = {
async getRecommendSheetTags(){
return {
pinned: [{
id: "1",
title: "纯音乐"
}],
data: [{
title: "年代",
data: [{
id: "101",
title: "80后"
}, {
id: "102",
title: "90后"
}]
}]
};
}
}

获取某个tag下的所有歌单(getRecommendSheetsByTag)

获取tag下的所有歌单列表,可省略。

方法签名

    type getRecommendSheetsByTag = (
tag: ICommon.IUnique,
page?: number,
) => Promise<{
isEnd: boolean,
data: Array<MusicSheetItem>
}>;

参数和返回值

方法的入参是getRecommendSheetTags返回的标签,类似:

{
id: '101',
title: '80后'
}

方法的返回值是当前tag下的所有音乐歌单。

需要注意,app会有一个默认tag,此时传入的id为空字符串(后续可能修改逻辑)。

举例


module.exports = {
async getRecommendSheetsByTag(tagItem){
return {
isEnd: false,
data: [{
title: "歌单1",
id: "xxxx",
artwork: "xxx",
playCount:122220
}]
};
}
}