Audio Player - Node.js

ディレクトリ構成

  • example
    • audio
      • sample
        • sample1.wav
        • sample2.wav
    • css
      • style.css
    • node_modules
    • app.js
    • index.html
    • package-lock.json
    • package.json
    • preload.js
    • renderer.js

ファイル内容

app.js

const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('node:path');
const fs = require('fs');

const createWindow = () => {
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, '/preload.js')
    }
  })

  win.loadFile('index.html');
}

app.whenReady().then(() => {
  createWindow()

  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) {
      createWindow()
    }
  })
})

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

ipcMain.handle('get-dir', (event, dirPath) => {
  const dir = fs.readdirSync(dirPath, { withFileTypes: true });
  return dir;
})

ipcMain.handle('get-file', (event, arr) => {
  const file = fs.readdirSync(arr[0] + arr[1]);
  return file;
})

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'"/>
    <script src="./script.js"></script>
    <link rel="stylesheet" href="./css/style.css">
    <title>Hello Electron</title>
  </head>
  <body>
    <h1>Audio Player</h1>
    <p><button type="button" id="start">start</button></p>
    <p><button type="button" id="stop">stop</button></p>
    <p><button type="button" id="cont">continue</button></p>
    <p><input type="range" name="volume" id="volume" min="0.0" max="1.0" value="0.5" step=".1"><span id="volume-view"></span></p>
    <p><button type="button" id="reset">reset</button></p>
    <div id="dir">
      <ul id="dir-target">

      </ul>
    </div>
    <div id="file">
      <ul id="file-target">
        
      </ul>
    </div>
  </body>
</html>

style.css

li {
  list-style: none;
}

preload.js

const { contextBridge, ipcRenderer } =  require('electron');

function checkCheckBox(filesName){
  let arr = [];
  for(let i = 0; i < filesName.length; i++){
    if(filesName[i].checked) {
      arr.push(filesName[i].value);
    }
  };
  return arr;
}

function checkedRadio(){
  const dirName = document.getElementsByName('dir');
  const len = dirName.length;
  let checkValue = '';

  for (let i = 0; i < len; i++){
    if (dirName.item(i).checked){
      checkValue = dirName.item(i).value;
    }
  }
  return checkValue;
}

function resetCheckBox(filesName){
  for(let i of filesName){
    i.checked = false;
  }
}

contextBridge.exposeInMainWorld('myApi', {
  setup: async ()=>{
    const file = document.getElementById('file');
    const dirPath = "./audio/";
    const start = document.getElementById('start');
    const stop = document.getElementById('stop');
    const cont = document.getElementById('cont');
    const volume_view = document.getElementById('volume-view'); 
    const volumeBar = document.getElementById('volume')
    const filesName = document.getElementsByName('file');
    const reset = document.getElementById('reset');
    const fileTarget = document.getElementById('file-target');
    const allDirents = await ipcRenderer.invoke('get-dir', dirPath);
    
    const audio = new Audio();
    audio.volume = volumeBar.value;
    volume_view.innerHTML = volumeBar.value * 10;

    for(let i of allDirents){
      document.getElementById('dir-target').innerHTML += `<li><label for="${i["name"]}"><input type="radio" name="dir" id="${i["name"]}" value="${i["name"]}">${i["name"]}</label></li>`;
    }
  
    const dirAll = document.querySelectorAll("input[name='dir']");
  
    for(let element of dirAll) {
      element.addEventListener('change', async function(){
        if( this.checked ) {
          fileTarget.innerHTML = "";
          const files = await ipcRenderer.invoke('get-file', [dirPath, this.value]);
          for(let i of files){
            fileTarget.innerHTML += `<li><label for="${i}"><input type="checkbox" name="file" id="${i}" value="${i}">${i}</label></li>`;
          }
        }
      });
    }
  
    start.addEventListener('click', function(){
      let arr = checkCheckBox(filesName);
      let checkValue = checkedRadio();
  
      (async function(){
        for(let i of arr){
          await new Promise((resolve) => {
            audio.src = `${dirPath}${checkValue}/${i}`;
            audio.play();
            audio.addEventListener('ended',() => {
              resolve();
            });
          });
        }
      })();
    });
  
    volumeBar.addEventListener('input', (e) => {
      audio.volume = e.target.value;
      volume_view.innerHTML = audio.volume * 10;
    });
  
    stop.addEventListener('click', function(){
      if(!audio.paused) {
        audio.pause();
      }
    });
  
    cont.addEventListener('click', function(){
      audio.play();
    });
  
    reset.addEventListener('click', function(){
      audio.pause();
      audio.src = "";
      resetCheckBox(filesName);
    });
   
  },
})

renderer.js

window.onload = function(){
  window.myApi.setup();
}