20. ブラウザで音楽プレーヤー(mplayer)

ブラウザを使ってコントロールする音楽プレーヤーを幾つか作ったが、音楽ファイルを指定して再生するものが無いので作った。
ネットにmplayerを利用しブラウザでコントロールするサンプルとなるmplayer-web-remoteと云うものが有ったので流用させて頂くことにした。

●必要なAPのインストール(ApacheとPHPは導入済み前提)


$ sudo apt-get install mplayer
・"mplayer"は、Radiko導入時にインストール済みだが念のため記載

$ sudo apt-get install flac
・flacファイルから埋め込みアルバムアートを取り出すのに付属ツール(metaflac)を使用する

●サンプルプログラムの環境依存部分を修正し動作確認


※メインメニューのindex.phpで下記部分を当方の音楽フォルダに変更

<!-- Put a list of your directories which contain content below -->
<a href="browse.php?b=/home/josh/tv">TV</a>
<a href="browse.php?b=/home/josh/movies">Movies</a>

問題無く動作したが、超シンプルに作成されているので味気ない。
そこで、改造して自分用に作り替えてみた。

●作成機能:

  1. ボタンをノッペリから立体感のあるものに変更
  2. 選曲は1曲だけでなく複数可能にする
  3. 再生中にアルバムアートを表示する
  4. 再生中音楽のTAG情報表示する
  5. 再生時間経過の表示 (プレーヤーから情報得られないので目安程度の精度)
  6. 複数曲選択時、再生中音楽を中断し次の曲を再生できるようにする

完成ブラウザ画面

①メインフォルダ一覧


②アーチスト別フォルダ一覧


③選択アーチストのアルバム一覧


④アルバム内選曲画面


⑤全選択ボタン押下


⑥再生中画面

●ファイル構成


index.php	:大元のフォルダ一覧(上図①)
browse.php	:選曲のためのフォルダやファイルの表示(上図②〜⑤)
controls.php:再生中音楽の情報表示、再生経過時間表示、音楽スキップ、再生中断(上図⑥)
play.php	:再生するファイル名をplay.shで実行させる
play.sh		:ファイルのTAG情報取得、アルバムアート画像取得、音楽再生

●ファイルのタグ情報
play.shで"ffprobe"コマンドを使い、ファイルのタグ情報(アーチスト、曲のタイトル、アルバム名、トラック番号、ジャンル)と曲の時間情報を取得し、controls.phpへデータ引継ぎのためのcsvファイルへ書き込む。


※タグ情報の取得(play.sh)

ffprobe -v error -i "$1" -show_entries format_tags=artist,title,album,track,genre -of default=noprint_wrappers=1:nokey=0 | sed 's/TAG://' | sed -z 's/\n/,/g' > ./tmp/test.csv

・オプションの"default=noprint_wrappers=1:nokey=0"は、指定情報だけキー付で出力させる。
・情報は1項目1行で出力され、行頭に"TAG:"が付くのでsedで削除して、改行を","に置き換える
  csv出力例:title=Calypso,artist=Suzanne Vega,album=Solitude Standing,track=07/11,genre=Rock

※曲の時間情報を取得
ffprobe -v error -i "$1" -show_entries format=duration -of default=noprint_wrappers=1:nokey=0 >> ./tmp/test.csv

●アルバムアートファイル
当初、play.shで再生曲の画像ファイル取得は"ffmpeg"コマンドを使用していたが、ファイル形式がflac場合、画像取得に時間が掛かる(2〜3分)。そこで、試しにflacコマンドを導入して、一緒に導入されるツール"metaflac"を使ってみたところ、一瞬で画像取り込みが出来たので、このコマンドを使う事にした。


※アルバムアートの取得(play.sh)

ORGIFS=$IFS		# 環境変数 IFS(Internal Filed Separator)の一時保存
IFS=,			# csvを配列変換するためにセパレータを","(カンマ)に指定
read -a item < ./tmp/tag-info.csv
for var in ${item[@]}		# 再生するアルバム名とアーチスト名
do
	if [ `echo ${var,,} | grep 'album='` ] ; then
		album=$var
	elif [ `echo ${var,,} | grep 'artist='` ] ; then
		artist=$var
	fi
done
if [ -e ./tmp/prev-tag-info.csv ] ; then
	read -a item < ./tmp/prev-tag-info.csv
	for var in ${item[@]}	# 直前に再生したアルバム名とアーチスト名
	do
		if [ `echo ${var,,} | grep 'album='` ] ; then
			prev_album=$var
		elif [ `echo ${var,,} | grep 'artist='` ] ; then
			prev_artist=$var
		fi
	done
else
	prev_album="temp"		# 直前の再生曲が無かったので仮の値
	prev_artist="temp"
fi

# 直前に再生した曲とアルバム名かアーチスト名が異なる場合はアルバムアートを取得する
if [ $album != $prev_album ] || [ $artist != $prev_artist ] ; then

	rm ./tmp/album.jpg
	rm ./tmp/album.png

	if [ "`echo $1 | grep '.flac'`" ]; then
		metaflac "$1" --export-picture-to=./tmp/album.jpg		# flac
	else
		ffmpeg  -i "$1" -an -scodec copy ./tmp/album.jpg		# flac以外
	fi

	# 組み込み画像が無かったら画像ファイルを探す
	if [ ! -e './tmp/album.jpg' ]; then
		IFS=$'\n'			# セパレータを改行に指定
		mypath=${1%/*}
		jpgs="${mypath}/*.jpg"
		if ls $jpgs > /dev/null 2>&1		# jpgファイルを探す
		then
			filearry=(`ls -1 $jpgs`)
			for img in ${filearray[@]}
			do
				case ${img,,} in
				"cover.jpg" | "folder.jpg" | "albumartsmall.jpg" | "front.jpg" ) cp $img ./tmp/album.jpg
																				break ;;
				"${album,,}.jpg" ) cp $img ./tmp/album.jpg
								break ;;
				esac
			done
		fi
		if [ ! -e './tmp/album.jpg' ]; then	# jpgのalbum artが見つからなかった
			pngs="${mypath}/*.png"			# pngファイル探す
			if ls $pngs > /dev/null 2>&1
			then
				filearry=(`ls -1 $pngs`)
				cp ${filearry[0]} ./tmp/album.png	# pngは最初のファイルをアルバム画像とみなす
			else
				cp ./tmp/no-album-art.png ./tmp/album.png
			fi
		fi
	fi
fi
IFS=$ORGIFS			# 環境変数を戻す

●PHP間のデータ継承
browse.phpで選択された曲をplay.phpへ渡すのに"session"を利用してみた。ネット情報を生噛りで使ったので不安だったが、どうにか動いている。


※browse.php
	if ( isset ($_POST['flist'])){				# 'play'ボタンが押された
		session_start();
		$flist = $_POST['flist'];
		$bpath = urldecode($_POST['b']);
		$ppath = urldecode($_POST['p']);
		$_SESSION['flist'] = $flist;			# 選択した曲ファイルのリスト
		$_SESSION['bpath'] = $bpath;			# baseパス(index.phpで選択したパス)
		$_SESSION['ppath'] = $ppath;			# 曲ファイルのパス
		session_write_close();
		header("Location: play.php");			# play.phpへ
		exit();
	}

※play.php
	session_start();
	$mpath = $_SESSION['bpath']. $_SESSION['ppath']. '/';
	$music_list = $_SESSION['flist'];			# 音楽リスト
	$music = array_splice($music_list, 0, 1);	# 最初の要素を抜き出す(配列要素が1つ減る)
	$_SESSION['flist'] = $music_list;			# 要素が1つ減った配列(リスト)を戻す
	session_write_close();

※controls.php
	session_start();
	$flist = $_SESSION['flist'];
	$bpath = $_SESSION['bpath'];
	$ppath = $_SESSION['ppath'];
	$mpath = $bpath. $ppath. '/';
	session_write_close();

●アルバム内の全曲選択ボタン
アルバム内の曲を1度に全曲選択するボタンをjavascriptを利用して作成した。


<?php
	echo "<input type='hidden' name='b' value='{$bb}'><br>";
	echo "<input type='hidden' name='p' value='{$pp}'><br>";
	if ( $available_file === true ) {
		echo "<p><input type='submit' value='▶ Play' style='width:400px;height:50px;'>";
		echo "<input type='reset' value='全選択解除' style='width:110px;height:50px;'>\n";
		echo "<input type='button' value='全選択' onclick='AllChecked();' class='mp' style='width:110px;height:50px;'></p>\n";
	}
?>

<script language="JavaScript" type="text/javascript">
<!--
  // 「全選択」ボタンで全てにチェック付ける
  function AllChecked(){
    var ElementsCount = document.music.elements.length;
    for (var i=0; i<ElementsCount; i++){
      document.music.elements[i].checked = true;
    }
  }
// -->
</script>

【参考】

  1. mplayer-web-remote
  2. シェルでmp3タグを読み取る方法
  3. sedで改行を置換・削除する
  4. PHPで連想配列のファイル入出力
  5. 【PHP入門】徹底解説!PHPでセッションを使う方法~基本から応用まで
  6. PHPで秒数の数値があり、それを時:分:秒みたいに変換する
  7. substr_replace — 文字列の一部を置換する
  8. strrpos — 文字列中に、ある部分文字列が最後に現れる場所を探す
  9. flac tools
  10. チェックボックス全部を一括ON/OFFする機能を作る方法
  11. Bash > ファイルやディレクトリの存在をチェックする方法

2019/07/02