Vue+Electron從零開始打造一個本地播放器

 是新朋友嗎?記得先點web前端學習圈關注我哦~

爲何要作?

女友工做是音頻後期,日常會收集一些音頻音樂,須要看音頻的頻譜波形,每次用au這種大型軟件播放音樂看波形,很不方便,看到她這麼辛苦,身爲程序猿的我痛心疾首,因而,就有了這麼一個小軟件,軟件涉及到的技術主要爲electron,vue,node,波形的展現主要經過wavesurfer生成。前端

從零開始-搭建項目

項目經過vue腳手架搭建的,因此須要安裝cli工具,若是已經裝了,能夠跳過這一步.vue

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

裝好後,經過腳手架搭建項目node

vue create music

vue須要與electron集成,這裏社區已經有比較成熟的vue插件了,Vue CLI Plugin Electron Builder。git

vue add electron-builder

懶人能夠直接去clone個人搭建好得架子直接開發, 戳這裏 :https://github.com/Kerinlin/simple-electron-vue-template 。github

從零開始-項目開發

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

  • 不添加文件目錄,加載任意的本地文件系統內的音頻文件,直接調用播放器播放
  • 前一首後一首功能
  • 聲音音量控制
  • 自定義軟件窗口

如何關聯播放

如何實現關聯播放?由於對electron不是很熟,查了好久 electron 的資料,終於找到了配置項,須要配置 fileAssociationsshell

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

配置好後,經過 electron 的open-file事件,獲取打開的音頻文件的本地路徑。對於 windows , 須要經過 process.argv ,來獲取文件路徑。npm

const filePath = process.argv[1];

如何加載本地音頻文件

上一步經過配置拿到文件的本地路徑後,下一步就是經過路徑讀取音頻文件的信息。因爲音頻的插件沒法解析絕對路徑,因此須要經過node的文件系統,經過fs.readFileSync讀取到文件的buffer信息。小程序

let buffer = fs.readFileSync(diskPath); //讀取文件,並將緩存區進行轉換

讀取後須要將buffer轉換成node可讀流segmentfault

const stream = this.bufferToStream(buffer);//將buffer數據轉換成node 可讀流

轉換方法 bufferToStream

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

轉換成流後須要將音頻流轉換成blob對象來加載,實現方法

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)
  })
}

轉blob

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

      //將blob對象轉成blob連接
      let filePath = window.URL.createObjectURL(fileUrl);
      // console.log(filePath);
      this.wavesurfer.load(filePath);

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

這樣就實現了加載本地文件播放了

上一首下一首功能

這裏的上一首下一首的功能是基於上面獲取到的文件的絕對路徑,經過node的path模塊,path.dirname獲取到文件的父級目錄。

const dirPath = path.dirname(diskPath);

而後經過fs.readdir讀取目錄下全部文件,會返回一個文件名數組,找到該目錄下正在播放的文件的下標,經過數組下標判斷前一首和後一首歌曲的名稱,而後再組裝成絕對路徑,讀取資源播放

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

聲音音量控制

音量控制須要經過監聽input的鍵入事件,獲取到range的值,而後經過設置樣式background-image,動態計算百分比,而後調用wavesurfer的setVolume方法調節音量

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

自定義標題欄

我的以爲系統自帶的菜單欄太醜了,就給設置了無邊框再本身加上最小化,關閉的功能。最小化,關閉是經過ipc通訊,渲染進程監聽到有點擊操做後,通知主進程進行相應的操做。

渲染進程

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

主進程

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

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

打開多個實例的問題

在實際測試的過程當中發現會出現,打開一首新的音樂播放,就會出現從新開一個實例的現象,不能實現覆蓋播放,後面查閱資料發現electron有一個second-instance事件,能夠監聽是否打開了第二個實例。當第二個實例被執行而且調用 app.requestSingleInstanceLock()") 時,這個事件將在應用程序的首個實例中觸發,而且會返回第二個實例的相關信息,而後經過主進程通知渲染進程,告知渲染進程第二個實例的本地絕對路徑,渲染進程接收到信息後,立馬加載第二個實例的資源。app.requestSingleInstanceLock(),表示應用程序實例是否成功取得了鎖。若是它取得鎖失敗,能夠假設另外一個應用實例已經取得了鎖而且仍舊在運行,因此能夠直接關閉掉,這樣就避免了打開多個實例的問題

主進程

const gotTheLock = app.requestSingleInstanceLock();
if (gotTheLock) {
  app.on("second-instance", (event, commandLine, workingDirectory) => {
    // 監聽是否有第二個實例,向渲染進程發送第二個實例的本地路徑
    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();
}

渲染進程

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

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

自動更新

需求的原由是,在很興奮的給女友成品的時候,尷尬的被女友試出不少bug(捂臉ing),而後頻繁的修改打包,而後經過私發傳給她。特別麻煩,因此這個需求很急迫。最後查了資料,經過electron-updater實現了這個需求.

安裝electron-updater

yarn add electron-updater

發佈設置

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

主進程監聽

autoUpdater.on("checking-for-update", () => {});
autoUpdater.on("update-available", info => {
  dialog.showMessageBox({
    title"新版本發佈",
    message"有新內容更新,稍後將從新爲您安裝",
    buttons: ["肯定"],
    type"info",
    noLinktrue
  });
});

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

生成Github Access Token 由於是用github做爲更新站,因此本地須要相應的操做權限,去這裏生成token,戳這,生成後,在powershell中設置

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

打包上傳Github

yarn electron:build -p always

完成上面步驟後軟件會自動上傳打包後的文件到release,而後編輯下release就能夠直接發佈了,軟件是基於版本號更新的,因此記得必定要改版本號

從零開始-結束

做爲程序猿最開心的事莫過於獲得女友的誇獎,雖然這是一個小程序,實現難度也不高,可是最後作出最小可用的版本呈如今女友面前的時候,看到女盆友感動的眼神,我想,這應該是我做爲程序猿惟一感到欣慰的時候。軟件還有不少能夠改進的地方,源碼在此,戳這裏Github :https://github.com/Kerinlin/localMusicPlayer

源自:https://segmentfault.com/a/1190000037613988

聲明:文章著做權歸做者全部,若有侵權,請聯繫小編刪除。

感謝 · 轉發歡迎你們留言

本文分享自微信公衆號 - web前端學習圈(web-xxq)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索