背景
最近正在做一个开源的项目--pear-rec,pear-rec 是一个跨平台的截图、录屏、录音、录像软件。截图功能上篇文章已经讲过了,如果没有看过的可以去看看这篇文章------------手把手教你,用electron实现截图软件,并且项目的框架搭建也可以参考上一篇文章,开发到现在我想要一个音乐(录音)预览、播放功能,说干就干,下面我介绍一下我的开发过程。
工具
- nodejs
- pnpm
- electron
- vite
- react
- antd
- aplayer
aplayer介绍
npmjs: www.npmjs.com/package/apl...
github: github.com/DIYgod/APla...
aplayer
是一款基于HTML5
的音乐播放器插件,它使用简单方便,并且功能强大。以下是aplayer
的一些特点和功能:
- 多格式支持:
aplayer
支持播放多种音频格式,包括MP3、WAV、FLAC等。 - 自适应布局:
aplayer
可以根据播放器容器的大小自动调整布局,适应不同的屏幕尺寸和设备类型。 - 音乐控制:
aplayer
提供了常见的音乐播放控制功能,包括播放/暂停、上一首/下一首、调节音量等。 - 播放列表:
aplayer
支持创建和管理播放列表,可以通过API动态添加或删除歌曲。 - 自定义样式:
aplayer
可以通过自定义CSS样式来美化播放器的外观,包括歌曲封面、进度条等。 - 丰富的事件和回调:
aplayer
提供了多个事件和回调函数,可以在播放器的不同状态下进行操作和处理。 - 响应式设计:
aplayer
支持响应式设计,可以在移动设备上提供更好的用户体验。 - 支持插件扩展:
aplayer
支持通过插件扩展功能,可以添加自定义的控制按钮、特效等。
总的来说,aplayer
是一个功能全面、易于使用且高度可定制的音乐播放器插件,适用于各种网页音乐播放的需求。它提供了丰富的API和事件,使开发者可以灵活地实现自己的音乐播放器界面和交互效果。
实现
其实实现音乐播放很简单,只需要两个页面就可以了,一个是主页面,一个是音乐播放页面。主页面主要功能是获取音乐位置或是要播放音频的,音乐播放页面就是展示音乐和播放音乐的页面。接下来,我们分两步实现音乐播放器的功能。
web 端实现
一、 路由配置
js
import Home from "@/pages/home";
import ViewAudio from "@/pages/viewAudio";
<Routes>
<Route path="/" element={<Home />}></Route>
<Route path="/viewAudio" element={<ViewAudio />}></Route>
</Routes>
二、 主页面
可以看到主页面有一个选择卡片,点击卡片,可以选择音乐的位置或要播放的音频。
js
import React, { useState, useImperativeHandle, forwardRef } from "react";
import { BsMusicNoteBeamed } from "react-icons/bs";
import { Button, Card, Upload } from "antd";
import { useNavigate } from "react-router-dom";
import type { UploadProps } from "antd/es/upload/interface";
const ViewAudioCard = forwardRef((props: any, ref: any) => {
const navigate = useNavigate();
useImperativeHandle(ref, () => ({
handleViewAudio,
}));
const uploadProps: UploadProps = {
accept: "audio/*",
name: "file",
multiple: false,
showUploadList: false,
customRequest: () => {},
beforeUpload: (file) => {
if (window.electronAPI) {
window.electronAPI.sendVaOpenWin({ url: file.path });
} else {
const url = window.URL.createObjectURL(file);
navigate(`/viewAudio?url=${encodeURIComponent(url)}`);
}
return false;
},
};
function handleViewAudio(e) {
window.electronAPI
? window.electronAPI.sendVaOpenWin()
: navigate("/viewAudio");
e.stopPropagation();
}
return (
<Upload {...uploadProps}>
<Card
title="查看音频"
hoverable
bordered={false}
extra={
<Button type="link" onClick={handleViewAudio}>
打开
</Button>
}
style={{ maxWidth: 300 }}
>
<div className="cardContent">
<BsMusicNoteBeamed rev={undefined} />
</div>
</Card>
</Upload>
);
});
export default ViewAudioCard;
三、 音乐页面
可以看到这是一个播放列表页面。要基于aplayer
制作一个页面播放器,你可以按照以下步骤进行:
- 安装
aplayer
css
npm install aplayer --save
- 引入
aplayer
模块
javascript
import 'APlayer/dist/APlayer.min.css';
import APlayer from 'APlayer';
- 创建一个容器标签,例如div,给它一个id,以便在js中使用。例如:
bash
<div id="player"></div>
- 在你的js文件中,实例化aplayer对象,并将其附加到容器上。例如:
css
var ap = new APlayer({
container: document.getElementById('player'),
autoplay: true,
audio: [{
name: 'Song Name',
artist: 'Artist Name',
url: 'http://www.example.com/song.mp3',
cover: 'http://www.example.com/cover.jpg'
}]
});
在这个例子中,我们使用了一个audio对象来定义一个歌曲,你可以根据自己的需求添加更多的歌曲。
- 通过aplayer的API可以控制播放器的行为,例如:
scss
ap.play(); // 播放音乐
ap.pause(); // 暂停音乐
ap.volume(0.5); // 设置音量
// 更多的控制选项请参考aplayer的文档。
这样,你就可以在页面上实现基于aplayer
的播放器了。你可以根据自己的需求,进行样式和交互的定制。
electron 端实现
现在我们已经在web端实现了音乐播放等功能了,接下来就是在electron 端实现了,实现逻辑一样,也是两个页面,一个主页面,一个播放页面。
- 创建一个播放页面
js
function createViewAudioWin(search?: any): BrowserWindow {
viewAudioWin = new BrowserWindow({
title: "pear-rec 视频预览",
icon: ICON,
autoHideMenuBar: true, // 自动隐藏菜单栏
webPreferences: {
preload,
},
});
if (url) {
// electron-vite-vue#298
viewAudioWin.loadURL(url + `#/viewAudio?url=${search?.url || ""}`);
// Open devTool if the app is not packaged
// viewAudioWin.webContents.openDevTools();
} else {
viewAudioWin.loadFile(indexHtml, {
hash: `/viewAudio?url=${search?.url || ""}`,
});
}
viewAudioWin.once("ready-to-show", async () => {
viewAudioWin?.show();
});
return viewAudioWin;
}
function openViewAudioWin(search?: any) {
if (!viewAudioWin || viewAudioWin?.isDestroyed()) {
viewAudioWin = createViewAudioWin(search);
}
viewAudioWin.show();
}
function closeViewAudioWin() {
viewAudioWin?.close();
viewAudioWin = null;
}
- 获取当前音频目录下的所有音频
js
// 获取所有音频
async function getAudios(audioUrl: any) {
let audios = await getAudiosByAudioUrl(audioUrl);
return audios;
}
js
// 遍历当前文件夹
function getAudiosByAudioUrl(audioUrl: string) {
const directoryPath = path.dirname(audioUrl);
const files = fs.readdirSync(directoryPath); // 读取目录内容
let audios: any[] = [];
let index = 0;
files.forEach(file => {
const filePath = path.join(directoryPath, file);
if (isAudioFile(filePath)) {
const fileName = path.basename(filePath);
if(filePath == audioUrl) {
audios.unshift({ url: `pearrec:///${filePath}`, name: fileName, cover: "./imgs/music.png" });
} else {
audios.push({ url: `pearrec:///${filePath}`, name: fileName, cover: "./imgs/music.png" });
}
index++;
}
});
return audios;
}
scss
// 判断是否是音频
function isAudioFile(filePath: string): boolean {
const ext = path.extname(filePath).toLowerCase();
return ['.mp3', '.wav', '.aac', '.ogg', '.flac', '.aiff', 'aif', '.m4a', '.alac', '.ac3'].includes(ext);
}
- 与web交互
ipcMain
文件
js
ipcMain.on("va:open-win", (e, search) => {
viewAudioWin.closeViewAudioWin();
viewAudioWin.openViewAudioWin(search);
});
ipcMain.handle("va:get-audios", async (e, audioUrl) => {
const audios = await viewAudioWin.getAudios(audioUrl);
return audios;
});
ipcMain.handle("va:set-historyAudio", async (e, audioUrl) => {
store.setHistoryAudio(audioUrl);
});
preload/electronAPI
文件
js
sendVaOpenWin: (search?: any) => ipcRenderer.send("va:open-win", search),
invokeVaGetAudios: (audioUrl: any) =>
ipcRenderer.invoke("va:get-audios", audioUrl),
sendVaSetHistoryAudio: (audioUrl: any) =>
ipcRenderer.send("va:set-historyAudio", audioUrl),
其实逻辑不复杂,大家可以自己动手做起来。
总结
文章写到这里基本结束了,简单回顾下文章的内容。
- Q: 有源码吗?
当然有,地址如下:github.com/027xiguapi/...,有兴趣的话可以大家一起探讨,同时也欢迎大家fork
和star