adbird(広告鳥) 備忘録

VivaldiブラウザのWebパネルに簡易メディアプレイヤーを追加

概要

VivaldiブラウザのWebパネルにYouTubeやストリーミング系の音楽サイトを表示して、音楽を再生するってのは見かけるけど、ローカル(自分のPC内)にある音楽データを再生してくれる音楽プレーヤーをパネルに表示できないだろうか?(Operaブラウザにかつてあった「音楽パネル」が念頭にあったw)といろいろ調べたところ、以下の方法でできた。

追記:

これを作った後に、 Winamp2-js の存在を知りました。機能的にはたぶん、そちらのほうが幸せになりますw。

ただし、Winamp2-js だと、動画の音声は再生できるけど、映像は表示できないっぽいです。

ここで紹介する方法では、もちろん、映像も含めて、動画の再生が可能です!
それぐらいしか強みはないです…。
あとは表示がシンプルで見やすいというところぐらいしか…w。

Winamp2-js についての個人的感想としては、Winampは昔、お世話になってたけどやはりデザイン的に古いなぁと思ったのと(スキンが代えられるのは知っているけど)、操作ボタン・フォント等が小さすぎ、そして、「(パネルじゃなくて)Winampで再生してる」って感じがして、ちょっと違うかな…ってなった(^_^;)。

更新

  • Windowsの方への注意事項(html保存の際に UTF-8 に)追加。(2018/4/8)
  • 「開く/追加」ボタンを追加、その他CSS修正。(2018/3/27)
    • @z7wacg2u さん、本当にありがとうございました!
  • htmlソースを一部修正 (2018/3/25)
    • プレイリストの曲名に番号が付くようにした。
    • 背景色・フォント色を変更。
    • tableタグを削除して、パネルの幅に合わせてプレイヤー・動画が拡大・縮小されるようになった。
  • Webパネルにアイコンが表示されるように修正。(2018/3/22)
    • ※表示されたり、されなかったりと動作が不安定です…。
  • パネルを狭めると「次へ」が隠れるので、再生曲名の下に「次へ」ボタンが来るように修正。

Webパネル用のアイコンを保存

(※追記:原因は不明ですが、アイコンが表示されたりされなかったりします…。不安定なため、このプロセスは飛ばしてもらって構いません。)

なくてもいいのだけど、Webパネルにアイコンが表示されないのが寂しかったので、アイコンが表示されるようにする。

【フリーアイコン】 再生 あたりから、適当に好きなアイコンをもらってきて、保存。

ここでは「再生アイコン.png」として保存。
32px(ピクセル)× 32px の大きさにするのがいいっぽい。

後述のhtmlソースの、以下の部分で指定すれば、アイコンが表示される(表示されなかったら、いったんWebパネルから削除して、もう一度Webパネルに追加すれば表示されるはず)。

  <!-- パネルのアイコン画像 -->
  <link rel="shortcut icon" type="image/png" href="再生アイコン.png">

htmlファイル を保存

下記リンク先にアップしたhtmlをローカル(自分のPC)に保存するか、

下記のソースを「mediaplayer.html」として保存。

Windowsの方は保存の際に、文字コードUTF-8 にして保存しないと、「開く/追加」「次へ」ボタンが文字化けするかもしれません。

いまさらHTML5 (簡易プレイヤー編)」のhtmlソースがほぼそのままのベースとなっています。また、@z7wacg2u さんに「開く/追加」ボタンのJavaScriptの編集をしていただきました。

適宜、前述のアイコン画像ファイルのURLや、デフォルトの音量部分を編集すること。

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <!-- パネルのアイコン画像 -->
  <link rel="shortcut icon" type="image/png" href="再生アイコン.png">

  <title>メディアプレイヤー</title>
  <style>
    /* 背景色 フォント色 */
    body{background-color:#E1DDD8;
      color:#4F4F4F;}
    .nowplaying{margin-top:5px;
      margin-left:5px;
      margin-bottom:5px;}

        label {color: #E5E5E5;  
            background-color: #7D7D7D;
            margin:0 0 0 5px;
            padding: 4px 6px;
            border-radius: 5px;
            font-size:9pt;}
    .openbutton{display:none;}
    .nextbutton{float:right;
                display:block;
                border-style: none;
                background-color: #7D7D7D;
                color: #E5E5E5;
                margin:0 5px 3px 0;
                padding:3px 15px;
                border-radius: 5px;
                font-size:9pt
}

  </style>
  <script type="text/javascript">
    // VIDEOタグ
    var video = null;
    // AUDIOタグ
    var audio = null;
    // ファイル一覧
    var list = [];
    // 初期処理
    function init() {
      // ファイルドロップイベント設定
      document.getElementById("drop").addEventListener("dragover", eventStop, false);
      document.getElementById("drop").addEventListener("drop", filedrop, false);
      // VIDEOタグ取得
      video = document.getElementById("video");
      // 終了イベント取得設定
      video.addEventListener("ended", playerEvent, false);
      // VIDEO非表示
      video.style.display = "none";
      // AUDIOタグ取得
      audio = document.getElementById("audio");
      // 終了イベント取得設定
      audio.addEventListener("ended", playerEvent, false);
      // AUDIO非表示
      audio.style.display = "none";
      // 次へボタン押下イベント設定
      document.getElementById("next").addEventListener("click", next, false);
      // 次へボタン非表示
      document.getElementById("next").style.display = "none";
      // ファイル選択ボタンのイベント
      document.getElementById("openfile").addEventListener("change", open, false);
      // キーイベント
      document.onkeydown = key_event;
    }
    // ショートカットキー
    function key_event() {
      var code = event.keyCode;
      switch(code) {
        // Space
        case 32:
          // audioの再生と一時停止Toggle
          if (audio.paused) audio.play(); else audio.pause();
          // videoの再生と一時停止Toggle
          if (video.paused) video.play(); else video.pause();
          break;
        // N
        case 78:
          next();
          break;
      }
    }
    // ファイルがドラッグされた場合
    function eventStop(event) {
      // イベントキャンセル
      event.stopPropagation();
      event.preventDefault();
      // 操作をリンクに変更
      event.dataTransfer.dropEffect = "link";
    }
    // ファイルがドロップされた場合
    function filedrop(event) {
      try {
        // イベントキャンセル
        event.stopPropagation();
        event.preventDefault();
        // ファイル存在チェック
        if (event.dataTransfer.files) {
          var old = list.length;
          // ファイル一覧取得
          var files = event.dataTransfer.files;
          // ファイル数分ループ
          for (var i = 0; i < files.length; i++) {
            // ファイル取得
            var file = files[i];
            // 再生可能ファイルであるか判定
            if (video.canPlayType(file.type) || audio.canPlayType(file.type)) {
              // ファイル情報生成
              var item = {
                name: file.name,
                type: file.type,
                url: URL.createObjectURL(file)
              };
              // ファイル追加
              list.push(item);
            }
          }
          // 表示
          view();
          // 最初であるか判定
          if (old == 0 && list.length > 0) {
            // 次へボタン表示
            document.getElementById("next").style.display = "block";
            // 次を再生(最初)
            next();
          }
        }
      } catch (e) {
        // エラーの場合
        alert(e.message);
      }
    }
    // ファイルを開く
    function open() {
      var fileRef = document.getElementById("openfile");
      if (fileRef.files) {
        var old = list.length;
        // ファイル一覧
        var files = fileRef.files;
        for (var i = 0; i < files.length; i++) {
          // ファイル取得
          var file = files[i];
          // 再生可能ファイルであるか判定
          if (video.canPlayType(file.type) || audio.canPlayType(file.type)) {
            // ファイル情報生成
            var item = {
                name: file.name,
                type: file.type,
                url: URL.createObjectURL(file)
                };
            // ファイル追加
           list.push(item);
          }
        }
        // 表示
        view();
        // 最初であるか判定
        if (old == 0 && list.length > 0) {
          // 次へボタン表示
          document.getElementById("next").style.display = "block";
          // 次を再生(最初)
          next();
        }
      }
    }
    // 次を再生
    function next() {
        // ファイル数チェック
        if (list.length > 0) {
            // 最初のファイルを設定
            var item = list[0];
            // ファイル数分ループ
            for (var i = 0; i < list.length; i++) {
                // VIDEOタグかAUDIOタグと一致するか判定
                if (video.src === list[i].url || audio.src === list[i].url) {
                    // 最後のファイルか判定
                    if (i != list.length - 1) {
                        // 次のファイルを設定
                        item = list[i + 1];
                        break;
                    }
                }
            }
            // AUDIO停止
            audio.pause();
            // VIDEO停止
            video.pause();
            // タイプ判断
            if (item.type.indexOf("audio/") == 0) {
                // AUDIOなので、VIDEO初期化
                video.src = "";
                video.style.display = "none";
                // AUDIO表示
                audio.style.display = "block";
                // 前回とURLが異なるか判定
                if (audio.src != item.url) {
                    // URL更新
                    audio.src = item.url;
                }
                // AUDIO再生
                audio.play();
                // デフォルトの音量調整 0〜1.0
                audio.volume = 0.5;
            } else if (item.type.indexOf("video/") == 0) {
                // VIDEOなので、AUDIO初期化
                audio.src = "";
                audio.style.display = "none";
                // VIDEO表示
                video.style.display = "block";
                // 前回とURLが異なるか判定
                if (video.src != item.url) {
                    // URL更新
                    video.src = item.url;
                }
                // VIDEO再生
                video.play();
                // デフォルトの音量調整 0〜1.0
                video.volume = 0.5;
            }
            // ファイル名表示
            document.getElementById("playfilename").textContent = item.name;
        }
    }
    // イベント取得
    function playerEvent(event) {
        // イベントタイプ判定
        if (event.type == "ended") {
            // 終了なので、次へ
            next();
        }
    }
    // 表示
    function view() {
        // 表示されているリスト取得
        var nodes = document.getElementById("playlist").childNodes;
        // 全部削除
        while (nodes.length > 0) {
            document.getElementById("playlist").removeChild(nodes[0]);
        }
        // リスト数分追加 番号なし表示
        /*
        for (var i = 0; i < list.length; i++) {
          var div = document.createElement("div");
          div.textContent = list[i].name;
          document.getElementById("playlist").appendChild(div);
        }
        */
        // リスト数分追加 番号あり表示
        for (var i = 0; i < list.length; i++) {
            var li = document.createElement("li");
            li.textContent = list[i].name;
            document.getElementById("playlist").appendChild(li);
        }
        // 表示幅調整
        document.getElementById("playlist").style.width = "100%";
    }
    // ロードイベント登録
    window.addEventListener("load", init, false);
  </script>
</head>
<body>
  <div id="drop" style="position: fixed; top: 0px; left: 0px; width: 100%; height: 100%; overflow: auto;">

    <!-- ファイル名 -->
    <div id="playfilename" class="nowplaying"></div>

    <!-- ファイルを開く -->
    <label for="openfile">
    開く/追加
    <input id="openfile" type="file"  class="openbutton" accept="audio/*,video/*" multiple />
    </label>

    <!-- 次へ部分 -->
    <button id="next" class="nextbutton">次へ</button>
    <!-- プレイヤー部分 -->
    <div style="resize: both; overflow: auto; width: 100%;">
        <video id="video" controls style="width: 99%; height: 99%;"></video>
        <audio id="audio" controls style="width: 99%; height: 40px"></audio>
    </div>

    <!-- ドロップ&一覧部分 番号なしで表示したい場合はこちら。
    <div id="playlist" style="width: 100px; height:; overflow: auto;"></div>
    -->
    <!-- ドロップ&一覧部分 番号つきで表示-->
    <ol id="playlist" style="width:; height:; overflow: auto;">
    </ol>
  </div>
</body>
</html>

デフォルトの音量調整

デフォルトの音量を調整したい場合、上記htmlソースのこの部分を書き換えてください。

 // デフォルトの音量調整 0〜1.0
 audio.volume = 0.5;

(中略)

// デフォルトの音量調整 0〜1.0
video.volume = 0.5;

Webパネルに追加

Vivaldiブラウザで「mediaplayer.html」を開いて、ウェブパネルに追加。

音楽データや動画データをドラッグ・アンド・ドロップ
または
「開く/追加」ボタンをクリックして、追加

すれば再生される。


追記:

Vivaldiのピクチャ―・イン・ピクチャ―の機能が邪魔して、初回の「開く/追加」のボタンがうまく作動してくれません(「開く/追加」ボタンの右下ギリギリをクリックすれば開きます)。

初回のみ「開く/追加」の右下ギリギリをクリックするか、音楽データや動画データをドラッグ・アンド・ドロップしてください。

またはVivaldiの設定からピクチャ―・イン・ピクチャ―の機能そのものを無効化してください。


プレイリストをクリアしたい場合は、Webパネルのツールバー(※1)の「再読み込み」か「ホームページへ移動」のボタンを押す。

※1:
表示されていなかったら、ウェブパネルのアイコン上で
 右クリック > ツールバー > 表示
 右クリック > ツールバー > 全てのコントロールを表示
で表示する

こんな感じ

こんな感じにブラウザで音楽再生が可能に。僕はウェブパネルを右側に表示している。

f:id:adbird:20180327171320p:plain

他力本願な願い… 願いがかなった!

僕は htmlとcssは少しは分かるけど、JavaScript はさっぱりなので(音量調整のやつはネットで調べて偶々うまくいっただけ)、どなたか、ボタンを押せば、フォルダダイアログが開いて、プレイリストにファイルを追加する機能の「開く/追加」ボタンを作ってくれないだろうか…。
ドラッグアンドドロップのためにわざわざファイルマネージャを開きたくない。

@z7wacg2u さんからJavaScriptソースの提供をいただき、「開く/追加」ボタンが付きました!

これでフォルダダイアログが開いて、ファイルが追加できるようになりました!
もちろん、これまでどおり、ドラッグアンドドロップでの追加でもできます。

@z7wacg2u さん、本当にありがとうございました!