欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Electron+vue從零開始打造一個(gè)本地播放器的方法示例

 更新時(shí)間:2020年10月27日 09:26:24   作者:kerin  
這篇文章主要介紹了Electron+vue從零開始打造一個(gè)本地播放器的方法示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

為什么要做?

女朋友工作是音頻后期,平常會(huì)收集一些音頻音樂,需要看音頻的頻譜波形,每次用au這種大型軟件播放音樂看波形,很不方便,看到她這么辛苦,身為程序猿的我痛心疾首,于是,就有了這么一個(gè)小軟件,軟件涉及到的技術(shù)主要為electron,vue,node,波形的展示主要通過wavesurfer生成。

從零開始-搭建項(xiàng)目

項(xiàng)目通過vue腳手架搭建的,所以需要安裝cli工具,如果已經(jīng)裝了,可以跳過這一步.

npm install -g @vue/cli
# OR
yarn global add @vue/cli

裝好后,通過腳手架搭建項(xiàng)目

vue create music

vue需要與electron集成,這里社區(qū)已經(jīng)有比較成熟的vue插件了,Vue CLI Plugin Electron Builder。

vue add electron-builder

懶人可以直接去clone我的搭建好得架子直接開發(fā), 戳這里。

從零開始-項(xiàng)目開發(fā)

首先先明確下這個(gè)播放器的功能需求,主要有這幾個(gè)

  • 不添加文件目錄,加載任意的本地文件系統(tǒng)內(nèi)的音頻文件,直接調(diào)用播放器播放
  • 前一首后一首功能
  • 聲音音量控制
  • 自定義軟件窗口

如何關(guān)聯(lián)播放

如何實(shí)現(xiàn)關(guān)聯(lián)播放?因?yàn)閷?duì)electron不是很熟,查了很久electron的資料,終于找到了配置項(xiàng),需要配置fileAssociations

    fileAssociations: [
     {
      ext: ["mp3", "wav", "flac", "ogg", "m4a"],
      name: "music",
      role: "Editor"
     }
    ],

配置好后,通過electron的open-file事件,獲取打開的音頻文件的本地路徑。對(duì)于windows,需要通過process.argv,來獲取文件路徑。

const filePath = process.argv[1];

如何加載本地音頻文件

上一步通過配置拿到文件的本地路徑后,下一步就是通過路徑讀取音頻文件的信息。由于音頻的插件無法解析絕對(duì)路徑,所以需要通過node的文件系統(tǒng),通過fs.readFileSync讀取到文件的buffer信息。

let buffer = fs.readFileSync(diskPath); //讀取文件,并將緩存區(qū)進(jìn)行轉(zhuǎn)換

讀取后需要將buffer轉(zhuǎn)換成node可讀流

const stream = this.bufferToStream(buffer);//將buffer數(shù)據(jù)轉(zhuǎn)換成node 可讀流

轉(zhuǎn)換方法 bufferToStream

  bufferToStream(binary) {
   const readableInstanceStream = new Readable({
    read() {
     this.push(binary);
     this.push(null);
    }
   });
   return readableInstanceStream;
  }

轉(zhuǎn)換成流后需要將音頻流轉(zhuǎn)換成blob對(duì)象來加載,實(shí)現(xiàn)方法

module.exports = streamToBlob

function streamToBlob (stream, mimeType) {
 if (mimeType != null && typeof mimeType !== 'string') {
  throw new Error('Invalid mimetype, expected string.')
 }
 return new Promise((resolve, reject) => {
  const chunks = []
  stream
   .on('data', chunk => chunks.push(chunk))
   .once('end', () => {
    const blob = mimeType != null
     ? new Blob(chunks, { type: mimeType })
     : new Blob(chunks)
    resolve(blob)
   })
   .once('error', reject)
 })
}

轉(zhuǎn)blob

 let fileUrl; // blob對(duì)象
 streamToBlob(stream)
  .then(res => {
   fileUrl = res;
   // console.log(fileUrl);

   //將blob對(duì)象轉(zhuǎn)成blob鏈接
   let filePath = window.URL.createObjectURL(fileUrl);
   // console.log(filePath);
   this.wavesurfer.load(filePath);

   // 自動(dòng)播放
   this.wavesurfer.play();
   this.playing = true;
  })
  .catch(err => {
   console.log(err);
  });

這樣就實(shí)現(xiàn)了加載本地文件播放了

上一首下一首功能

這里的上一首下一首的功能是基于上面獲取到的文件的絕對(duì)路徑,通過node的path模塊,path.dirname獲取到文件的父級(jí)目錄。

const dirPath = path.dirname(diskPath);

然后通過fs.readdir讀取目錄下所有文件,會(huì)返回一個(gè)文件名數(shù)組,找到該目錄下正在播放的文件的下標(biāo),通過數(shù)組下標(biāo)判斷前一首和后一首歌曲的名稱,然后再組裝成絕對(duì)路徑,讀取資源播放

  playFileList(diskPath, pos) {
   let isInFiles;
   let fileIndex;
   let preIndex;
   let nextIndex;
   let fullPath;
   let dirPath = path.dirname(diskPath);
   let basename = path.basename(diskPath);
   fs.readdir(dirPath, (err, files) => {
    isInFiles = files.includes(basename);

    if (isInFiles && pos === "pre") {
     fileIndex = files.indexOf(basename);
     preIndex = fileIndex - 1;
     fullPath = path.resolve(dirPath, files[preIndex]);

     this.loadMusic(fullPath);
    }
    if (isInFiles && pos === "next") {
     fileIndex = files.indexOf(basename);
     nextIndex = fileIndex + 1;
     fullPath = path.resolve(dirPath, files[nextIndex]);
     this.loadMusic(fullPath);
    }
   });
  },

聲音音量控制

音量控制需要通過監(jiān)聽input的鍵入事件,獲取到range的值,然后通過設(shè)置樣式background-image,動(dòng)態(tài)計(jì)算百分比,然后調(diào)用wavesurfer的setVolume方法調(diào)節(jié)音量

:style="`background-image:linear-gradient( to right, ${fillColor}, ${fillColor} ${percent}, ${emptyColor} ${percent})`"

改變音量changeVol事件

  changeVol(e) {
   let val = e.target.value;
   let min = e.target.min;
   let max = e.target.max;
   let rate = (val - min) / (max - min);
   this.percent = 100 * rate + "%";
   console.log(this.percent, rate);
   this.wavesurfer.setVolume(Number(rate));
  },

自定義標(biāo)題欄

個(gè)人覺得系統(tǒng)自帶的菜單欄太丑了,就給設(shè)置了無邊框再自己加上最小化,關(guān)閉的功能。最小化,關(guān)閉是通過ipc通信,渲染進(jìn)程監(jiān)聽到有點(diǎn)擊操作后,通知主進(jìn)程進(jìn)行相應(yīng)的操作。

渲染進(jìn)程

  close() {
   ipcRenderer.send("close");
  },
  minimize() {
   ipcRenderer.send("minimize");
  }

主進(jìn)程

ipcMain.on("close", () => {
 win.close();
 app.quit();
});

ipcMain.on("minimize", () => {
 win.minimize();
});

打開多個(gè)實(shí)例的問題

在實(shí)際測試的過程中發(fā)現(xiàn)會(huì)出現(xiàn),打開一首新的音樂播放,就會(huì)出現(xiàn)重新開一個(gè)實(shí)例的現(xiàn)象,不能實(shí)現(xiàn)覆蓋播放,后面查閱資料發(fā)現(xiàn)electron有一個(gè)second-instance事件,可以監(jiān)聽是否打開了第二個(gè)實(shí)例。當(dāng)?shù)诙€(gè)實(shí)例被執(zhí)行并且調(diào)用 時(shí),這個(gè)事件將在應(yīng)用程序的首個(gè)實(shí)例中觸發(fā),并且會(huì)返回第二個(gè)實(shí)例的相關(guān)信息,然后通過主進(jìn)程通知渲染進(jìn)程,告知渲染進(jìn)程第二個(gè)實(shí)例的本地絕對(duì)路徑,渲染進(jìn)程接收到信息后,立馬加載第二個(gè)實(shí)例的資源。app.requestSingleInstanceLock(),表示應(yīng)用程序?qū)嵗欠癯晒θ〉昧随i。 如果它取得鎖失敗,可以假設(shè)另一個(gè)應(yīng)用實(shí)例已經(jīng)取得了鎖并且仍舊在運(yùn)行,所以可以直接關(guān)閉掉,這樣就避免了打開多個(gè)實(shí)例的問題

主進(jìn)程

const gotTheLock = app.requestSingleInstanceLock();
if (gotTheLock) {
 app.on("second-instance", (event, commandLine, workingDirectory) => {
  // 監(jiān)聽是否有第二個(gè)實(shí)例,向渲染進(jìn)程發(fā)送第二個(gè)實(shí)例的本地路徑
  win.webContents.send("path", `${commandLine[commandLine.length - 1]}`);
  if (win) {
   if (win.isMinimized()) win.restore();
   win.focus();
  }
 });

 app.on("ready", async () => {
  createWindow();
 });
} else {
 app.quit();
}

渲染進(jìn)程

 ipcRenderer.on("path", (event, arg) => {
  const newOriginPath = arg;

  // console.log(newOriginPath);
  this.loadMusic(newOriginPath);
 });

自動(dòng)更新

需求的起因是,在很興奮的給女朋友成品的時(shí)候,尷尬的被女朋友試出很多bug(捂臉ing),然后頻繁的修改打包,然后通過私發(fā)傳給她。特別麻煩,所以這個(gè)需求很急迫。最后查了資料,通過electron-updater實(shí)現(xiàn)了這個(gè)需求.

安裝electron-updater

yarn add electron-updater

發(fā)布設(shè)置

  electronBuilder: {
   builderOptions: {
    publish: ['github']
   }
  }

主進(jìn)程監(jiān)聽

autoUpdater.on("checking-for-update", () => {});
autoUpdater.on("update-available", info => {
 dialog.showMessageBox({
  title: "新版本發(fā)布",
  message: "有新內(nèi)容更新,稍后將重新為您安裝",
  buttons: ["確定"],
  type: "info",
  noLink: true
 });
});

autoUpdater.on("update-downloaded", info => {
 autoUpdater.quitAndInstall();
});

生成Github Access Token

因?yàn)槭怯胓ithub作為更新站,所以本地需要相應(yīng)的操作權(quán)限,去這里生成token,戳這,生成后,在powershell中設(shè)置

[Environment]::SetEnvironmentVariable("GH_TOKEN","<YOUR_TOKEN_HERE>","User")
# 例如 [Environment]::SetEnvironmentVariable("GH_TOKEN","sdfdsfgsdg14463232","User")

打包上傳Github

yarn electron:build -p always

完成上面步驟后軟件會(huì)自動(dòng)上傳打包后的文件到release,然后編輯下release就可以直接發(fā)布了,軟件是基于版本號(hào)更新的,所以記得一定要改版本號(hào)

從零開始-結(jié)束

作為程序猿最開心的事莫過于得到女朋友的夸獎(jiǎng),雖然這是一個(gè)小程序,實(shí)現(xiàn)難度也不高,但是最后做出最小可用的版本呈現(xiàn)在女朋友面前的時(shí)候,看到女盆友感動(dòng)的眼神,我想,這應(yīng)該是我作為程序猿唯一感到欣慰的時(shí)候。軟件還有很多可以改進(jìn)的地方,源碼在此,戳這里Github

到此這篇關(guān)于Electron+vue從零開始打造一個(gè)本地播放器的方法示例的文章就介紹到這了,更多相關(guān)Electron+vue本地播放器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論