ブラウザで動く音楽プレーヤー 【JavaScript & Python】

Python

JavaScript と Python を使った、ブラウザで動く音楽プレーヤーについてプログラムをまとめておきます。スタンドアロンのパソコンで動作します。

以下の環境で動作確認をしています。
環境: Windows パソコン、Microsoft Edge ブラウザ、Python 3.x (+ Anaconda)インストール済み

背景 ~ 最小限のステップで音楽が聴けるようにしたい!

日頃、音楽をよく聴きます。作業時の BGM としても、いつも活用しています。

過去、mp3 などで気に入った曲があると、ローカルのパソコンにフォルダを作って保存してきています。また、mp4 などの動画でも気に入った曲があるとフォルダを作って保存しています。
このような状態でしばらくすると、気に入った音楽や動画を入れたフォルダがパソコン内のあちこちに増えていくことになります。

すると、ふと気づくと、音楽を聴くたび、どこに入れたかパソコン内のフォルダを探し、聴きたい曲をダブルクリックして再生するようになっています。
また、Windows の自動更新で、知らない間にアプリが更新されてしまうこともよくあります。外観や操作性が突然変わり、連続再生ができなくなったりします。動画や音声の拡張子ごとに起動するアプリも違っていたりします。
音楽を聴いているとき、連続再生ができないと、1曲終わるごとにつぎの音楽ファイルをダブルクリックする必要が生じていました。

ところで、音楽や動画を普通に再生する程度であれば、ブラウザで十分です。
ブラウザは、常時、起動していることが多いので、もしブラウザのお気に入りから音楽が再生できると、上記の煩雑さが軽減・解消します。ユーザーインターフェースも、自分でカスタマイズができれば、とても便利です。

ということで、ブラウザから音楽が再生できるローカル Web アプリについてプログラムをまとめ、公開しておくことにします。

プログラム作成の考え方としては、まず、音楽や動画を入れたフォルダのパスを Python のスクリプトに記載しておくようにします。Python のスクリプトを実行すると、設定されている複数の各フォルダを参照し、再生リストを生成するようにします。再生リストは JavaScript の js ファイルとします。
そして、Web アプリ化した HTML ファイルをダブルクリックして開きます。この際、生成した再生リストを読み込むようにします。
ブラウザ上の Web アプリでは、各音楽に対応したボタンをクリックすると、音楽が再生できるようにします。音楽を再生すると、そのまま連続再生ができるようにします。

このようにすると、音楽を聴くとき、ほぼ最小限の手間・ステップ数で、聴きたい曲をブラウザから再生できることになります。広告が出ることもありません。
従来は、聴きたい曲ごとにエクスプローラを起動してフォルダを探し、各曲をダブルクリックして聴いていました。Web アプリ化しただけでも、日々の手間やストレスを格段に減らせることになります。
スクリプトはテキストエディタで修正できますので、フォルダ構成や曲順、機能追加などのカスタマイズも自由自在になります。

なお、手元の音声・動画ファイルで試したところ、mp3、wav、mp4、mov ファイルについて、Windows のブラウザで音声の再生ができることを確認しています。

設定手順

① 以下を参考に、パソコン内に音楽再生のためのフォルダ(audio_web_app1)を作成します。
例: c:\user\audio_web_app1\
② ①のフォルダの中に、さらに音楽ファイルを入れるためのフォルダ(例:audio1、audio2)を作成します。
例:
c:\user\audio_web_app1\audio1\
c:\user\audio_web_app1\audio2\
※ フォルダは、任意の場所に複数、設定できますが、最初は上記の事例でフォルダを作り、動作確認をしてみてください。
③ ①のフォルダの中に、”playlist1.py” という名前でテキストファイルを作成し、下記のサンプルコード(Python のスクリプト)を貼りつけて保存します。
④ ①のフォルダの中に、”audio_web_app1.html” という名前でテキストファイルを作成し、下記のサンプルコード(HTML/JavaScript のスクリプト)を貼りつけて保存します。

使い方

⑤ ②のフォルダに、音楽や動画のファイルを入れます。
※ 最初は、上で作った audio1、audio2 の両方に音声ファイルを入れてみて、うまく動くことを確認してください。うまく動いたら、フォルダの参照先、フォルダの数を変えるなど、アレンジしてみてください。
⑥ Windows パソコンのコマンドプロンプト(または、Anaconda Prompt)を起動し、③の Python のスクリプトを実行します。
例: python c:\user\audio_web_app1\playlist1.py
→ ①のフォルダ内に playlist1.js が生成されたら、再生リストの生成まで成功です。

⑦ ④の audio_web_app1.html をダブルクリックして、ブラウザを起動します。
⑧ ブラウザの画面上で、[001]、[002] 等となっているボタンをクリックしてみてください。
→ 音声が再生できたら、成功です。

※ [play] ボタンをクリックすると、設定されているファイルを再生します。
※ [stop] ボタンをクリックすると、再生を停止します。
※ [prev] ボタンで1曲前に移動します。[next] ボタンで、つぎの曲に移動します。
※ [continuous play] のところのチェックを入れておくと、曲のリストの順番に連続再生します。

※ うまく動かないときは、⑥で生成した再生リスト(playlist1.js)をテキストエディタで開いてみて、②の各フォルダ内のファイル(ファイルのパス)が書き出されているか、確認してみてください。
※ また、下記のサンプルスクリプトのデフォルトでは、Python のスクリプト、HTMLファイル、および、Python で自動生成される js ファイルは、同一のフォルダ内にあることが必要です。フォルダの位置関係を変えた場合は、スクリプト等を修正する必要があります。
※ もし、Python の実行環境がない場合は、playlist1.js を自作する必要があります。
この場合は、playlist1.js という名前でテキストファイルを作成し、たとえば、以下の4行を書き込んで保存してみてください。
arr1 = [
“file:///C:/user/audio_web_app1/audio1/file1.mp3”,
“file:///C:/user/audio_web_app1/audio1/file2.mp3”
];
上で記載しているフォルダ “audio1” 内に、暫定で file1.mp3、file2.mp3 という名前で 任意の mp3 ファイルのコピーを入れてください。
この再生リスト playlist1.js を HTML ファイル audio_web_app1.html と同一フォルダに入れ、音声ファイルも入れた状態で HTML ファイルをダブルクリックし、ブラウザで開いてみてください。

うまく動いたら

※ うまく動いたら、よく聴く曲を追加するなど、カスタマイズをしてみてください。また、お使いのパソコン内で音楽ファイルが入っているフォルダを設定するなど、参照先についてもカスタマイズしてみてください。
※ フォルダや音声ファイルを追加・変更したときは、Python のスクリプトを設定・修正し、再度、⑥を実行してください。
※ Python のスクリプトで、path2 = [ …, … ] としてあるところで、参照するフォルダを指定しています。
サンプルスクリプトでは、2つのフォルダを探しにいくようにしていますが、同様に、3つめのフォルダ、4つめのフォルダ、…、を追加して指定することで、各フォルダ内のファイルを参照し、再生リスト(playlist1.js)を生成します。
path2 で設定しているフォルダの順を入れ替えることで、リストの上位に並ぶフォルダを設定できます。
スクリプトを修正することで、すべての曲でソートをする、お気に入りの曲を優先して再生するなど、カスタマイズが可能です。
※ よく聴く曲はだいたい決まっていると思います。
すると、⑥で、1度、Python のスクリプトを実行して再生リストを作成しておけば、以後、ブラウザさえ起動すれば、任意の音楽をほぼ最小限のステップで聴くことができるようになります。ネット上の一般の Web アプリと同等の操作性で、音楽の再生が可能になります。ローカルアプリやエクスプローラを操作していた煩雑さを低減できると思います。
※ 音声データが複数のフォルダにまたがっていても/動画や音声データなどファイルフォーマットが異なっていても、一覧表示/連続再生できます。
たとえば、フォルダが10個以上あったり、フォルダのある階層関係がバラバラだと、フォルダを探し回る手間が増えていきますが、Python のスクリプトで指定しておけば、一覧表示が可能となります。最小限のステップ数で一覧からの一発再生が可能になります。
※ 今回、再生リスト playlist1.js は Python で生成することとし、HTML ファイルから読み込むようにしました。
前述のとおり、再生リスト playlist1.js をテキストで開くと、配列 arr1 を定義しています。この配列の中に②のフォルダにあるファイルのフルパスをすべて書き出すようにしています。
HTML 側で表示される曲順は、再生リスト playlist1.js に記載したままで表示しています。
したがって、たとえば、playlist1.js を手動で編集して、HTML ファイルを再表示すると、Web アプリ上の曲順などを任意に変更できます。好きなようにアレンジできることになります。

スクリプトの説明

Python のスクリプト playlist1.py について

・ スクリプトの冒頭では、テキストファイルの読み書きなどのための関数を定義しています。
・ def get_list1( … )、def get_list0( … ) で、ユーザーが指定したフォルダについて、内部のファイル名を取得しています。
フォルダが複数あっても読み込めるようにするため、get_list1() を定義しています。get_list0() は、1つのフォルダ内のファイルを取得する関数です。
もし、mp3 のみ取得したい、mp4 のみ取得したい、という場合は、”*.*” となっているところを、”*.mp3″、”*.mp4″ などに修正してください。
・ def generate_js1( … )、def generate_js0( … ) で、音声ファイルのリストから、JavaScript で読み込むためのスクリプトファイルを生成しています。
具体的には、JavaScript の <script> タグ内で、全音楽ファイルのフルパスが入った配列 arr1 = [ … ] を定義したいので、Python 側からこの配列の文字列を生成します。
・ path1 = … とした行で、Python のスクリプトが入ったフォルダのパスを取得しています。このフォルダ内に再生リスト playlist1.js を出力するためです。
・ path2 = [ …, … ] とした行で、読み込みにいくフォルダのパスを設定しています。
たとえば、フォルダを追加する場合は、記載例にしたがって、path2 = [ …, …, … ] という形になるようにするなど、カスタマイズをしてみてください。
・ a1 = … 、str1 = … としている行で、音声ファイルの全リストを取得し、JavaScript で読み込める記載を作っています。
・ 最後に write1( … ) とした行で、playlist1.js を書き出しています。

もしも、ローカルネットワーク内で自由にサーバーなどが使える環境があれば、このスクリプトをサーバー側に移植してしまって、サーバー上の音声ファイルを再生するようにするなど、アレンジも可能です。

音楽プレーヤーのサンプルコード audio_web_app1.html について

・ ファイル全体は、HTML のフォーマットに従って記載しています。
<script src = “playlist1.js” > としたところで、Python で生成した再生リスト playlist1.js を読み込んでいます。以後、このファイルで定義した配列 arr1 = [ … ] から音楽データのパスを取得して、再生できるようにしています。
・ function init_arr1() としたところで、音声ファイルのリストが入った配列 arr1 から、新たな配列 arr2 を生成します。arr1 には、ファイルのフルパスの羅列が入っているだけであり、表示上、ファイル操作上、やや不便ですので、各音声ファイルのパスと、ファイル名に分割して、arr2 を作成しています。この関数 init_arr1() は、HTML を読み込んだ直後の初期化時に実行します。
・ function timer1() としたところで、一定時間ごとに音楽再生などのステータスを監視し、各処理を実行しています。setTimeout() を実行することで、2秒ごとに timer1() を繰り返し実行しています。
たとえば、初期起動時では、ステータス status1 は -1 となっていますが、ユーザーが [start] ボタンをクリックするなど、再生を実行すると、start1 = 1 等となって、arr2 で読み込んだファイルを再生します。
・ function prev1()、next1()、play1()、stop1() は、ブラウザ画面上の各ボタン [prev]、[next]、[play]、[stop] をクリックしたときに実行される処理です。
クリックすると、ステータス status1 の値を変更し、timer1() で設定されている各処理を実行することで、前の曲の設定、つぎの曲の設定、再生、停止の動きをさせています。
・ function generate_table1() は、HTML を読み込んだ最初の段階で実行し、ファイル名を入れた表を生成するための関数です。
表の中にボタンを入れ込んであり、各ボタンをクリックすることで、何行目の曲を再生するのか、値を設定するようにしています。

まとめ

ブラウザで動く音楽プレーヤーについて、Python と JavaScript のプログラムをまとめました。

実質、3回程度のクリックで、よく聴く曲/気に入った曲を再生できるようになりました。
私の場合、上記の HTML をブラウザのお気に入りに登録し、200曲以上の音楽を設定していつも聴いています。(広告が出ることもありません!)

他にも、ABリピート再生が可能な音楽プレーヤーなど、Python のスクリプトなどをまとめ、公開しています。もし関心があるようでしたら、以下の関連リンクなども参照してみてください。

関連リンク
・ 指定範囲を繰り返し再生する音楽プレーヤー【Python】
・ 動画ファイルから音声データを一括で引き抜く 【Python & ffmpeg】
・ 音声データの音量レベルを一括で変換する
・ ドレミファソラシドを鳴らすサンプル【Python】
・ キーボードピアノ 【本格49鍵!】
・ Python で Just the Two of Us 進行 【コード進行】
・ フォルダ内のすべての音声・動画ファイルを再生する 【Raspberry Pi】

サンプルコード

再生リスト生成のための Python スクリプト playlist1.py

#!/usr/bin/python3
# coding: utf-8
import os 
import glob as gb1 

def read1( file1 ): 
    with open( file1, 'r', encoding='utf-8' ) as f1: 
        str1 = f1.read()
    return str1 

def write1( file1, str1 ): 
    with open( file1, 'w', encoding='utf-8' ) as f1: 
        f1.write( str1 ) 
    return 0 

def get_list1( path1 ): 
    a1 = [] 
    for i1 in range( len( path1 ) ): 
        a1 = a1 + get_list0( path1[i1] ) 
    return a1 

def get_list0( path1 ): 
    pattern1 = path1 + "/*.*" 
    files1 = gb1.glob( pattern1 ) 
    a1 = [] 
    for file1 in files1: 
        str1 = "file:///" + file1.replace( "\\", "/" ) 
        a1.append( str1 ) 
    return a1 

def generate_js1( a1 ): 
    str1 = generate_js0( a1 ) 
    str1 = "let arr1 = [\n" + str1 + "\n];" 
    return str1 

def generate_js0( a1 ): 
    if len( a1 ) > 0: 
        str1 = '"' + a1[0] + '"' 
        for i1 in range( len(a1)-1 ): 
            str1 = str1 + ',\n' + '"' + a1[i1+1] + '"' 
    else: 
        str1 = "" 
    return str1 

path1 = os.path.dirname(__file__) 
path2 = [ r"C:\user\audio_web_app1\audio1", r"C:\user\audio_web_app1\audio2" ] 

a1 = get_list1( path2 ) 
str1 = generate_js1( a1 ) 

file1 = path1 + "/playlist1.js" 
write1( file1, str1 ) 

音楽プレーヤー audio_web_app1.html

※ この HTML を動かすためには、このファイルと同一のフォルダ内に再生リストとなる playlist1.js を入れておく必要があります。手動で playlist1.js を作る場合は、上記の「使い方」の末尾を参照してください。

<html lang="ja" > 
<head >

<meta charset="utf-8"> 
<meta http-equiv="content-type" content="text/html; "> 

<meta name="viewport" content="width=device-width,initial-scale=1.0"> 
<title>audio_web_app v0.1</title> 
<script src="playlist1.js"></script>
<script> 

let status1 = -1; 
let n1 = 1; 
let arr2 = []; 

window.onload = function() { 
  init1(); 
}; 

function init1() { 
  status1 = -1; 
  n1 = 1; 
  init_arr1(); 
  generate_table1(); 
  set_src1(); 
  timer1(); 
} 

function init_arr1() { 
  arr2 = []; 
  for (let i1 = 0; i1 < arr1.length; i1++ ) { 
    let arr3 = arr1[i1].split( "/" ); 
    let str1 = arr3[arr3.length-1]; 
    arr3.length = arr3.length-1; 
    let str2 = arr3.join( "/" ); 
    arr2.push( [str2, str1] ); 
  } 
} 

function timer1 () { 
  if ( status1 < 1 ) { 
  } else if ( status1 == 1 ) { 
    set_src0(); 
    status1 = 2; 
  } else if ( status1 == 2 ) { 
    status1 = 3; 
    audio1.play(); 
  } else if ( status1 == 3 ) { 
    if ( isNaN( audio1.duration ) ) { 
      if ( checkbox1.checked ) { 
        next1(); 
        status1 = 2; 
      } else { 
        status1 = -1; 
      } 
    } else if ( audio1.currentTime >= audio1.duration ) { 
      const checkbox1 = document.getElementById( "checkbox1" ); 
      if ( checkbox1.checked ) { 
        next1(); 
        status1 = 2; 
      } else { 
        status1 = -1; 
      } 
    } 
  } 
  show_status1(); 
  setTimeout( timer1, 2000 ); 
} 

function set_src0() { 
  audio1.src = document.getElementById( "text2" ).value + "/" + document.getElementById( "text3" ).value; 
} 

function set_src1() { 
  document.getElementById( "text1" ).value = ("000" + n1).slice(-3); 
  document.getElementById( "text2" ).value = arr2[n1-1][0]; 
  document.getElementById( "text3" ).value = arr2[n1-1][1]; 
  set_src0(); 
} 

function prev1() { 
  let n2 = document.getElementById( "text1" ).value; 
  n2 = parseInt( n2 ) - 1; 
  if ( n2 < 1 ) { 
    n2 = arr1.length; 
  } 
  n1 = n2; 
  set_src1(); 
  if ( status1 == 3 ) { 
    status1 = -1; 
    play1(); 
  } 
} 

function next1() { 
  let n2 = document.getElementById( "text1" ).value; 
  n2 = parseInt( n2 ) + 1; 
  if ( n2 > arr1.length ) {
    n2 = 1; 
  } 
  n1 = n2; 
  set_src1(); 
  if ( status1 == 3 ) { 
    status1 = -1; 
    play1(); 
  } 
} 

function play1( event1 ) { 
  if ( status1 == 3 ) { 
    status1 = -1; 
  } else { 
    status1 = 1; 
  } 
} 

function stop1() { 
  set_src1(); 
  status1 = -1; 
} 

function show_status1() { 
  document.getElementById( "text4" ).value = ("000" + parseInt( audio1.currentTime )).slice(-3) + "/" + ("000" + parseInt( audio1.duration )).slice(-3) + " " +  status1; 
} 

function click_table1( n0 ) { 
  n1 = n0; 
  document.getElementById( "text1" ).value = ("000" + n1).slice(-3); 
  document.getElementById( "text2" ).value = arr2[n1-1][0]; 
  document.getElementById( "text3" ).value = arr2[n1-1][1]; 
  status1 = 1; 
} 

function generate_table1() { 
  let tb1 = document.getElementById( 'table1' ); 
  let tr1 = document.createElement( 'tr' ); 
  let td1 = document.createElement( 'td' ); 
  tb1.setAttribute( 'style', 'font-size:14' ); 
  td1.setAttribute( 'style', 'width:60px; text-align:center;' ); 
  td1.textContent = "No."; 
  tr1.appendChild( td1 ); 
  td1 = document.createElement( 'td' ); 
  td1.textContent = ""; 
  tr1.appendChild( td1 ); 
  tb1.appendChild( tr1 ); 
  for ( let i1 = 0; i1 < arr1.length; i1++ ) { 
    tr1 = document.createElement( 'tr' ); 
    td1 = document.createElement( 'td' ); 
    td1.setAttribute( 'style', 'width:60px; text-align:center;' ); 
    let button1 = document.createElement( 'button' ); 
    button1.setAttribute( 'onclick', 'click_table1('+ (i1+1) + ')'); 
    button1.innerHTML = ("000" + (i1+1)).slice(-3); 
    td1.appendChild( button1 ); 
    tr1.appendChild( td1 ); 
    td1 = document.createElement( 'td' ); 
    td1.textContent = arr2[i1][1]; 
    tr1.appendChild( td1 ); 
    tb1.appendChild( tr1 ); 
  } 
} 
</script> 
</head> 

<body style="margin:0; padding:0;"> 
<div style="position:sticky; top:0px; left:0px; background-color:#dddddd; " > 
  <div style="text-align:center;"> 
    <label for="text3" style="color:#dddddd;"><br><input type="text" id="text3" name="text3" style="width:97%; height:42; font-size:17;" value="" >:</label><br> 
  </div> 
  <div style="width:100%; display:flex; flex-direction:row; flex:1; line-height:180%; "> 
    <div style="width:400px; " > 
      <div style="text-align:center; line-height:250%;"> 
        <button onclick="prev1()" style="width:70px; height:25px; ">prev</button> 
        <button onclick="play1()" style="width:70px; height:25px; ">play</button> 
        <button onclick="next1()" style="width:70px; height:25px; ">next</button> 
        <button onclick="stop1()" style="width:70px; height:25px; ">stop</button><br> 
      </div> 
      <div style="text-align:center; "> 
        <audio controls id="audio1"><source src="" type="audio/mp3" /></audio>
      </div> 
    </div> 
    <div> 
      <input type="text" id="text1" size="1" value="1"><label for="text1"> : No. </label><br> 
      <input type="text" id="text2" size="40" value="" ><label for="text2"> : folder </label><br> 
      <input type="text" id="text4" size="40" value="" ><label for="text4"> : status </label><br> 
      <input type="checkbox" id="checkbox1" name="checkbox1" checked><label for="checkbox1"> continuous play</label><br> 
    </div> 
  </div>
</div>

<table id="table1"></table> 

</body> 
</html> 

タイトルとURLをコピーしました