htsign's blog

ノンジャンルで書くつもりだけど技術系が多いんじゃないかと思います

MIMEタイプが audio/xxx か video/xxx なファイルをで直接参照していた場合にやを直後に追加するスクリプト

あっちの界隈でよくあるADV*1なんかだと、公式サイトのキャラ紹介にmp3なファイルが直接置かれている場合が少なくありません。
特にIEさんやFxさんはこの手のものに出会うと、デフォルトでは一旦DLしてローカルの別のアプリケーションで再生する、という挙動になっています。

例えばゴミプラグインの代表格であるQuickTime辺りをインストールしておけばブラウザ上で再生させることも可能でしょう。
でもそれも外部のアプリケーションに頼っていることに変わりありません。

そこでHTML5で新しく定義された<audio><video>を使ってみましょう。
これらはブラウザ上にメディアファイルを埋め込むことができるものです。
従来のHTMLで言えば embed, object, applet 辺りの仲間でしょうか。

これらを埋め込むことでブラウザ上でも快適に音楽や動画を鑑賞することができます。

それでは

以下コード

(function(){
  var checked = [];        // 同じURLに複数回リクエストを送るのを防ぐためのリストを格納
  var timeout = 30 * 1000; // タイムアウトになるまでの時間(単位:ms)

  if (!HTMLAnchorElement.prototype.addPopup) { // addPopupメソッドをA要素に追加

    Object.defineProperty(HTMLAnchorElement.prototype, "addPopup", {
      value: function(url){
        var self = this;

        var xhr = new XMLHttpRequest(); // HEADリクエストを送る
        xhr.open("HEAD", url, true);
        xhr.onreadystatechange = function(){
          if (xhr.readyState === 4) {
            console.log(xhr.status + " : " + url);

            if (xhr.status !== 200) return;

            var header = xhr.getResponseHeader("Content-Type");
            var type   = header.split("/")[0];
            var allowList = ["audio", "video"];

            if (allowList.some(function(v){ return v === type })) { // いずれかにヒットすれば true
              console.log(header + " - OK");
              addPopupItem(self, type);
            }
          }
        };
        xhr.send(null);

        window.setTimeout(function(){ // 一定時間でタイムアウト
          xhr.abort();
        }, timeout);
      }
    });
  }
  else {
    return false;
  }

  // 他のスタイルから影響を受けないよう Date.now() を追加
  var thisClass = "media-popup-" + Date.now();

  var elems = document.getElementsByTagName("a");
  elems = Array.prototype.slice.call(elems); // A要素を全て抽出

  var popupItem = (function(){ // ポップアップ用の殻を作成
    var div = document.createElement("div");
    div.className = thisClass;
    return div;
  })();

  var popupStyle = function(){ // スタイル定義
    var style = document.createElement("style");
    document.getElementsByTagName("head")[0].appendChild(style);
    var s = style.sheet;

    // カーソルを置いたらポップアップするように
    s.insertRule("." + thisClass + "{"
    + "display: none !important;"
    + "text-indent: 0 !important;"
    + "z-index: 100 !important;"
    + "}", 0);
    s.insertRule("a:hover + ." + thisClass + ", ." + thisClass + ":hover {"
    + "display: block !important;"
    + "}", s.cssRules.length);

    s.insertRule("." + thisClass + "{"
    + "position: absolute !important;"
    + "}", s.cssRules.length);
  };
  popupStyle();

  for (var i in elems) {
    var url = elems[i].href;
    
    if (url.indexOf("http") === 0) {
      
      url = url.split("#")[0]; // URLに # を含む場合は除去
      
      if (checked.every(function(li){ return url !== li })) { // 全てとアンマッチなら true
        checked.push(url);
        elems[i].addPopup(url);
      }
    }
  }

  function addPopupItem(target, mediaType) { // ポップアップを生成
    var popup = popupItem.cloneNode(), s = popup.style;
    s.left = target.offsetLeft + "px";
    s.top  = target.offsetTop + target.offsetHeight + "px";

    var element; // 殻の中身を定義

    element = document.createElement(mediaType);
    element.setAttribute("src", target.href);
    element.setAttribute("preload", "metadata");
    element.setAttribute("controls", "controls");

    popup.appendChild(element);
    target.parentElement.insertBefore(popup, target.nextSibling); // 目標の直後に配置

    // 某ADVの公式サイトでなぜかレイアウトが崩れるので対症療法的な何か
    var objRect = popup.getBoundingClientRect();
    if (objRect.left === 0 && objRect.top === 0) {
      s.left = s.top = "auto";
    }
  }

})();
ブックマークレット


原理的には非常に簡単なもので、

  1. ドキュメント上からA要素をすべて抜き出し、
  2. それぞれの参照先に一気にHEADリクエストを送り、
  3. MIMEタイプを取得、
  4. それが audio/mpeg や video/mp4 等の場合にポップアップを作成する。

というもの。


うまくいくとこのように

マウスカーソルを重ねるだけで

再生窓が表示されます。(画像はIE10のものです。再生窓の形状はブラウザにより異なります。)

あとはマウスカーソルを再生ボタンに持って行ってクリックするだけです。
ね、簡単でしょう?


スクリプトの性質上、ブラウザやその設定によっては相手方のサーバーに負荷をかける恐れがあります。ご利用は計画的に。
MIMEタイプとブラウザの組み合わせによっては再生することができません。例えばFirefoxは audio/mpeg を再生出来ません。
※IE8以下では余裕で動きませんが、そもそも<audio>とか<video>に対応していないのでご安心ください。

更新履歴

  • 2013/03/03 05:36
    • URLに「#」が含まれていた場合のログ表示が#付きURLのままだったのを修正しました。
    • 一度リクエストを送った箇所には再度送らないようにしました。
      • 代わりに同じURLの<audio>,<video>が複数回出ても2つ目以降にはポップアップが付かないようになりました。
        • これについては後々いい感じの解決法を考えて修正予定。
  • 2013/02/27 23:42
    • URLに「#」が含まれると必ず404を返す事象に対処しました。
    • CSSプロパティに対して!importantキーワードを付与するようにしました。
  • 2013/02/26 21:45
    • 少しだけリファクタリングしました。
    • 一部サイトでなぜか彼方に飛んでいったので強引に修正しました。
  • 2013/02/26 01:44
    • 一部のサイトにおいて、 text-indent でかっ飛ばされていたので対抗しました。
    • ログを出力するようにしました。
    • 半角スペースをいちいち%20に置き換えていたのをやめました。

*1:いわゆるエロゲ