YesPlayMusic插件开发入门:创建你的第一个扩展

YesPlayMusic插件开发入门:创建你的第一个扩展

【免费下载链接】YesPlayMusic qier222/YesPlayMusic: 是一个基于 Electron 的高质量音乐播放器,支持多种音乐格式和云音乐服务。该项目提供了一个简单易用的音乐播放器,可以方便地实现音乐播放和管理,同时支持多种音乐格式和云音乐服务。 【免费下载链接】YesPlayMusic 项目地址: https://gitcode.com/gh_mirrors/ye/YesPlayMusic

引言:扩展音乐体验的痛点与解决方案

你是否曾想为YesPlayMusic添加自定义歌词翻译、音乐可视化效果或与其他服务的集成功能?作为一款基于Electron的高质量音乐播放器,YesPlayMusic虽然提供了丰富的核心功能,但官方并未提供完整的插件系统。本文将带你从零开始构建一个插件系统,通过拦截API请求、扩展UI组件和利用Electron的进程间通信(IPC)机制,实现自定义功能的无缝集成。读完本文后,你将掌握:

  • 插件系统的核心架构设计
  • API请求拦截与数据修改技术
  • Vue组件扩展与状态管理
  • Electron主进程与渲染进程通信
  • 插件打包与分发流程

插件系统设计:从架构到实现

系统架构概览

YesPlayMusic采用Electron的多进程架构,包含主进程(Main Process)和渲染进程(Renderer Process)。我们的插件系统将通过以下方式与现有架构融合:

mermaid

核心模块设计

  1. 插件管理器:负责插件的加载、注册和生命周期管理
  2. API拦截器:基于ncmModDef.js的API定义,拦截并修改请求/响应
  3. 组件扩展系统:允许插件注册新的Vue组件或修改现有组件
  4. 状态管理集成:通过Vuex存储插件状态,实现跨组件数据共享

准备工作:开发环境搭建

环境要求

  • Node.js 14+
  • npm/yarn
  • Git
  • 代码编辑器(推荐VS Code)

项目初始化

# 克隆仓库
git clone https://gitcode.com/gh_mirrors/ye/YesPlayMusic
cd YesPlayMusic

# 安装依赖
yarn install

# 启动开发服务器
yarn serve

插件目录结构

在项目根目录创建插件开发脚手架:

mkdir -p plugins/example-plugin
cd plugins/example-plugin
touch plugin.json index.js styles.css
mkdir components

实现插件系统核心

1. 扩展API拦截机制

YesPlayMusic的ncmModDef.js定义了所有网易云音乐API的路由映射。我们可以通过修改此文件,添加插件钩子:

// src/ncmModDef.js (修改部分)
module.exports = [
  {
    identifier: 'song_detail',
    route: '/song/detail',
    module: require('NeteaseCloudMusicApi/module/song_detail'),
    // 添加插件钩子
    hooks: {
      beforeRequest: (params) => params,
      afterResponse: (response) => response
    }
  },
  // 其他API定义...
];

创建API拦截管理器:

// src/api/pluginInterceptor.js
class ApiInterceptor {
  constructor() {
    this.hooks = new Map();
  }

  registerHook(apiIdentifier, hookType, callback) {
    if (!this.hooks.has(apiIdentifier)) {
      this.hooks.set(apiIdentifier, { beforeRequest: [], afterResponse: [] });
    }
    this.hooks.get(apiIdentifier)[hookType].push(callback);
  }

  async executeBeforeHooks(apiIdentifier, params) {
    const hooks = this.hooks.get(apiIdentifier)?.beforeRequest || [];
    let modifiedParams = { ...params };
    for (const hook of hooks) {
      modifiedParams = await hook(modifiedParams) || modifiedParams;
    }
    return modifiedParams;
  }

  async executeAfterHooks(apiIdentifier, response) {
    const hooks = this.hooks.get(apiIdentifier)?.afterResponse || [];
    let modifiedResponse = { ...response };
    for (const hook of hooks) {
      modifiedResponse = await hook(modifiedResponse) || modifiedResponse;
    }
    return modifiedResponse;
  }
}

export default new ApiInterceptor();

2. 开发插件管理器

// src/plugins/PluginManager.js
const fs = require('fs');
const path = require('path');
const { ipcMain } = require('electron');

class PluginManager {
  constructor() {
    this.plugins = [];
    this.pluginDir = path.join(__dirname, '../../plugins');
    this.apiInterceptor = require('../api/pluginInterceptor').default;
  }

  loadPlugins() {
    // 读取插件目录
    const pluginDirs = fs.readdirSync(this.pluginDir, { withFileTypes: true })
      .filter(dirent => dirent.isDirectory())
      .map(dirent => dirent.name);

    for (const dir of pluginDirs) {
      this.loadPlugin(dir);
    }
  }

  loadPlugin(pluginId) {
    const pluginPath = path.join(this.pluginDir, pluginId);
    const manifestPath = path.join(pluginPath, 'plugin.json');
    
    if (!fs.existsSync(manifestPath)) return;

    const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
    const plugin = {
      id: pluginId,
      ...manifest,
      path: pluginPath
    };

    // 加载插件主文件
    if (fs.existsSync(path.join(pluginPath, 'index.js'))) {
      const pluginModule = require(path.join(pluginPath, 'index.js'));
      pluginModule.init(this, plugin);
    }

    this.plugins.push(plugin);
    console.log(`Loaded plugin: ${plugin.name}`);
  }

  registerApiHook(apiIdentifier, hookType, callback) {
    this.apiInterceptor.registerHook(apiIdentifier, hookType, callback);
  }
}

module.exports = new PluginManager();

3. 修改主进程集成插件系统

// src/electron/main.js (添加部分)
const pluginManager = require('../plugins/PluginManager');

// 在应用就绪后加载插件
app.whenReady().then(() => {
  // 其他初始化代码...
  
  // 加载插件
  pluginManager.loadPlugins();
  
  // 创建窗口...
});

创建你的第一个插件:歌词翻译

插件清单文件

// plugins/example-plugin/plugin.json
{
  "name": "Lyric Translator",
  "version": "1.0.0",
  "description": "自动翻译歌词为指定语言",
  "author": "Your Name",
  "main": "index.js",
  "styles": "styles.css",
  "dependencies": {
    "baidu-translate-api": "^2.4.4"
  }
}

实现歌词翻译功能

// plugins/example-plugin/index.js
const translate = require('baidu-translate-api');

module.exports = {
  init(pluginManager, plugin) {
    // 注册API钩子,拦截歌词请求
    pluginManager.registerApiHook('lyric', 'afterResponse', async (response) => {
      if (!response.lrc || !response.lrc.lyric) return response;
      
      // 提取歌词文本
      const originalLyrics = response.lrc.lyric;
      
      // 翻译歌词(这里简化处理,实际应逐行翻译)
      const translated = await translate(originalLyrics, { to: 'en' });
      
      // 添加翻译后的歌词
      response.translatedLyric = translated.trans_result.map(item => 
        item.dst
      ).join('\n');
      
      return response;
    });
    
    // 注册UI组件
    pluginManager.registerComponent('LyricTranslator', () => 
      import('./components/LyricTranslator.vue')
    );
  }
};

创建Vue组件显示翻译歌词

<!-- plugins/example-plugin/components/LyricTranslator.vue -->
<template>
  <div class="translated-lyrics">
    <h3>Translated Lyrics</h3>
    <pre>{{ translatedLyric }}</pre>
  </div>
</template>

<script>
import { mapState } from 'vuex';

export default {
  name: 'LyricTranslator',
  computed: {
    ...mapState({
      currentLyric: state => state.player.currentLyric,
      translatedLyric: state => state.player.currentSong.translatedLyric || 'No translation available'
    })
  }
};
</script>

<style scoped>
.translated-lyrics {
  margin-top: 10px;
  padding: 10px;
  background: rgba(0, 0, 0, 0.5);
  border-radius: 4px;
}
</style>

扩展播放器界面

修改歌词组件以包含翻译内容:

<!-- src/components/Lyrics.vue (添加部分) -->
<template>
  <div class="lyrics-container">
    <!-- 原有歌词显示 -->
    <div class="original-lyrics">
      <!-- 原有代码 -->
    </div>
    
    <!-- 插件注入的翻译歌词组件 -->
    <component v-if="translatedLyric" is="LyricTranslator" />
  </div>
</template>

<script>
export default {
  computed: {
    ...mapState({
      // 原有代码...
      translatedLyric: state => state.player.currentSong.translatedLyric
    })
  }
};
</script>

插件打包与分发

创建打包脚本

# 创建打包脚本 plugins/package-plugin.sh
#!/bin/bash
PLUGIN_DIR=$1
cd $PLUGIN_DIR
npm install --production
zip -r ../$(basename $PLUGIN_DIR).ypm *

运行打包命令

chmod +x plugins/package-plugin.sh
./plugins/package-plugin.sh example-plugin

插件安装与管理

修改设置界面,添加插件管理面板,允许用户上传和管理.ypm格式的插件包。

高级主题:扩展主进程功能

利用Electron IPC实现全局快捷键

// 插件中注册全局快捷键
pluginManager.registerGlobalShortcut('translate-lyric', 'CommandOrControl+Shift+T', () => {
  // 通过IPC与渲染进程通信
  pluginManager.sendToRenderer('toggle-translation');
});

修改Vuex状态管理

// 在插件中访问和修改Vuex状态
pluginManager.registerStoreModule('lyricTranslator', {
  state: {
    targetLanguage: 'en'
  },
  mutations: {
    setTargetLanguage(state, lang) {
      state.targetLanguage = lang;
    }
  },
  actions: {
    updateLanguage({ commit }, lang) {
      commit('setTargetLanguage', lang);
    }
  }
});

总结与后续展望

通过本文的步骤,你已经成功构建了一个简单的插件系统,并开发了第一个歌词翻译插件。这个系统可以进一步扩展,添加插件依赖管理、沙箱安全机制和插件市场等功能。未来,你可以探索:

  • 开发音乐可视化插件
  • 实现与Last.fm等服务的集成
  • 创建自定义主题和皮肤
  • 添加音乐推荐算法

插件系统的可能性无穷无尽,希望本文能为你打开YesPlayMusic定制开发的大门。

附录:插件开发资源

核心文件参考

  1. ncmModDef.js - API路由定义
  2. src/api/request.js - HTTP请求封装
  3. src/store/index.js - Vuex状态管理
  4. src/electron/ipcMain.js - 主进程IPC处理

开发工具

【免费下载链接】YesPlayMusic qier222/YesPlayMusic: 是一个基于 Electron 的高质量音乐播放器,支持多种音乐格式和云音乐服务。该项目提供了一个简单易用的音乐播放器,可以方便地实现音乐播放和管理,同时支持多种音乐格式和云音乐服务。 【免费下载链接】YesPlayMusic 项目地址: https://gitcode.com/gh_mirrors/ye/YesPlayMusic

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值