YouTube Player APIで簡単なWEBアプリケーション「トリプルループ」を作ってみた

今回はiframe 組み込みの YouTube Player API リファレンスを参考にして簡単なWEBアプリケーションを作ってみました。

Youtube動画の開始位置(秒)を指定すると指定されたシーンを連続再生するだけのアプリです。

1回の秒数指定で3回連続再生する様に作成したのでアプリ名は「トリプルループ」というなんのヒネリもない、そのままの名前にとりあえずしました。

以下がサイトです。
トリプルループ-Youtube動画連続再生

このWEBアプリを使って以下の様に連続再生してみました。


登美丘高校ダンス部のバブリーダンスでしもしも、ころがしてる、おったまげ〜の連続再生

 

 


WBC 決勝 韓国戦 「決勝タイムリー」

 

 


木村拓哉ちょ待てよ

 

 


事件は会議室で起きてるんじゃない 現場で起きてるんだ。。青嶋確保だー

 

 


半沢直樹やられたらやり返す

 

 


にゃんぱすー

 

 


恋ダンスオープニング

 

 

トリプルループアプリのソースコード

今回作成したアプリのソースコードです。デザインはBootstrap、 YoutubeのAPIはJavascript、ボタンなどのイベントの呼び出しはJquery、サーバーサイドはPHPで実装しています。

<?php
    //HTML エンティティに変換
    function h($value){
        return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
    }
?>
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="description" content="Youtube動画のお気に入りの名台詞や大好きな歌詞、振り付けなどシーンをピンポイントで連続再生するアプリです">
    <!-- 表示領域を端末や ブラウザに合わせ、初期倍率を1に設定 -->
    <meta name="viewport" content="width=device-width,initial-scale=1.0">


    <!-- flatly bootstrap css  -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootswatch/3.3.7/flatly/bootstrap.min.css">
    <title>トリプルループ - Youtube動画連続再生</title>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-53156513-28"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'UA-53156513-28');
</script>
</head>
<body>
<!-- ソーシャルボタン用 facebook sdk -->
<div id="fb-root"></div>
<script>(function(d, s, id) {
  var js, fjs = d.getElementsByTagName(s)[0];
  if (d.getElementById(id)) return;
  js = d.createElement(s); js.id = id;
  js.src = 'https://connect.facebook.net/ja_JP/sdk.js#xfbml=1&version=v2.12&appId=910672038986001&autoLogAppEvents=1';
  fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>

<div class="navbar navbar-default navbar-fixed-top">
    <div class="container">
        <a href="/youtube.php" class="navbar-brand">トリプルループ</a>
    </div>
</div>
<div class="container" style="margin-top:100px;">
    <div class="row">
        <div class="col-lg-12">

          <div class="panel panel-default">
            <div class="panel-heading text-center">
                <h4>お気に入りの名台詞や大好きな歌詞、振り付けなど特定のシーンを3回連続再生します</h4>
                <?php
                    // スマホ判定用にユーザエージェントを取得
                    $ua=$_SERVER['HTTP_USER_AGENT'];
                ?>
                <?php if((strpos($ua, 'iPhone') !== false) || (strpos($ua, 'iPod') !== false) ||(strpos($ua, 'Android') !== false)): ?>
                    (スマホではうまく再生されない場合があります)
                <?php endif; ?>
            </div>
            <div class="panel-body text-center">
            <!-- YoutubeのIFrameを以下(idがplayer)の要素に設定する -->
                <div id="player" class="hide"></div>
            </div>
            <form method="get">
                <!-- クエリパラメータにshareが設定されている場合、動画設定項目を非表示 -->
                <div class="condition" style="<?php echo empty($_GET['share']) && empty($_GET['share-btn'])?'':'display:none;' ?>">
                    <div class="panel-body">
                        <h4>
                            <i class="glyphicon glyphicon-flag"></i>
                             1.Youtube動画のURLを指定(コピペ)してください
                        </h4>
                        <div class="input-group">
                            <span class="input-group-btn">
                            <!-- youtubeサイトのリンク先を指定 -->
                                <a href="https://www.youtube.com/?hl=ja&gl=JP" class="btn btn-info" target="_blank">Yotube動画 <span class="glyphicon glyphicon-new-window"></span></a>
                            </span>
                            <input name="url" class="form-control" id="youtube-url" value="<?php echo empty($_GET['url'])?'':h($_GET['url']) ?>" placeholder="Youtube動画のURL">
                        </div>
                    </div>
                    <div class="panel-body">
                        <h4>
                            <i class="glyphicon glyphicon-flag"></i>
                             2.ループ再生を行う秒(開始位置)を指定してください。
                            <span class="glyphicon glyphicon-question-sign" data-toggle="tooltip" data-html="true"  title="<p>・秒は複数指定が可能です。<br>複数指定する場合は<br>「,(カンマ)」区切りで<br>指定してください<br>例)12,18,30</p><p>・小数点以下一桁まで指定可能<br>例)18.5</p><p>・動画を再生後「秒取得」<br>をクリックすると閲覧中動画の経過時間(秒)を取得し追加します。</p>"></span>
                        </h4>
                        <div class="input-group">
                            <span class="input-group-btn">
                                <button class="btn btn-info start-set" <?= empty($_GET['set'])?'disabled':'' ?> type="button" id="start-set">秒取得 <span class="glyphicon glyphicon-plus-sign"></span></button>
                            </span>
                            <input name="start" class="form-control" id="start" placeholder="開始位置。カンマ区切りで複数指定可能。 例)12,19,30" value="<?php echo empty($_GET['start'])?'':h($_GET['start']) ?>">
                            <span class="input-group-btn">
                                <button class="btn btn-default" type="button" id="start-clear" title="秒をクリア"><span class="glyphicon glyphicon-remove-sign"></span></button>
                            </span>
                        </div>
                    </div>
                    <div class="panel-body text-center">
                        <button class="btn btn-success btn-sm" id="confirm" type="button"><span class="glyphicon glyphicon-play"></span> 確認再生</button>
                        <!-- submitタイプを指定 -->
                        <button class="btn btn-success btn-sm" name="share-btn" value="1" type="submit"><span class="glyphicon glyphicon-share-alt"></span> シェアする</button>
                    </div>
                </div>
                <!-- クエリパラメータにshare-btnが設定されている場合、動画設定項目を非表示 -->
                <div class="panel-body footer-btn" style="<?php echo empty($_GET['share']) && empty($_GET['share-btn'])?'display:none;':'' ?>">
                    <div class="form-group text-center">
                        <button class="btn btn-large btn-info" id="play" type="button"><span class="glyphicon glyphicon-play"></span> 再生する</button>
                    </div>
                    <div class="form-group pull-right">
                        <a href="/youtube.php" class="btn btn-xs btn-success"><span class="glyphicon glyphicon-plus"></span> 作成する</a>
                        <button class="btn btn-xs btn-default" id="customize" type="button"><span class="glyphicon glyphicon glyphicon-cog"></span> 調整</button>
                        <button class="btn btn-xs btn-default" id="stop" type="button"><span class="glyphicon glyphicon glyphicon glyphicon-stop"></span> 停止</button>
                        <button class="btn btn-xs btn-default" id="share" type="button"><span class="glyphicon glyphicon-share-alt"></span> 共有</button>
                    </div>
                </div>
            </form>
          </div>


          <div class="panel panel-default">
            <div class="panel-heading text-center">
                作成したトリプルループ
            </div>
            <div class="panel-body">
                <div class="row">
                    <div class="panel-body col-lg-4">
                        <a href="http://app.it-ti.work/youtube.php?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DB5rC0Bl7TX0&start=352.5%2C354%2C354%2C354%2C354%2C354%2C355&share=1" rel="noopener"><img src="http://i.ytimg.com/vi/B5rC0Bl7TX0/mqdefault.jpg" /><br />WBC 決勝 韓国戦 「決勝タイムリー」</a>
                    </div>
                    <div class="panel-body col-lg-4">
                        <a href="http://app.it-ti.work/youtube.php?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DLxr9tvYUHcg&start=96%2C97%2C98.2%2C98.2%2C98.2&share=1" rel="noopener"><img src="http://i.ytimg.com/vi/Lxr9tvYUHcg/mqdefault.jpg" /><br />登美丘高校ダンス部のバブリーダンスでしもしも、ころがしてる、おったまげ〜の連続再生</a>
                    </div>
                    <div class="panel-body col-lg-4">
                        <a href="http://app.it-ti.work/youtube.php?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DqNWqQ3eVyns&start=35%2C38%2C47.5%2C48.2%2C48.2&share=1" rel="noopener"><img src="http://i.ytimg.com/vi/qNWqQ3eVyns/mqdefault.jpg" /><br />事件は会議室で起きてるんじゃない 現場で起きてるんだ。。青嶋確保だー</a>
                    </div>
                </div>

                <div class="row">
                    <div class="panel-body col-lg-4">
                        <a href="http://app.it-ti.work/youtube.php?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DLIeSpreR854&start=3.8%2C3.9%2C4%2C4.1%2C4.2&share=1" rel="noopener"><img src="http://i.ytimg.com/vi/LIeSpreR854/mqdefault.jpg" /><br />木村拓哉ちょ待てよ</a>
                    </div>
                    <div class="panel-body col-lg-4">
                        <a href="http://app.it-ti.work/youtube.php?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3Dv9m73XFT8nc&start=6%2C9%2C9%2C9%2C9%2C9%2C9&share=1" rel="noopener"><img src="http://i.ytimg.com/vi/v9m73XFT8nc/mqdefault.jpg" /><br />半沢直樹やられたらやり返す</a>
                    </div>
                    <div class="panel-body col-lg-4">
                        <a href="http://app.it-ti.work/youtube.php?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3Dou-e_YFSgmE&start=0.3%2C0.3%2C0.3%2C1%2C1%2C1%2C1&share=1" rel="noopener"><img src="http://i.ytimg.com/vi/ou-e_YFSgmE/mqdefault.jpg" /><br />にゃんぱすー</a>
                    </div>
                </div>

                <div class="row">
                    <div class="panel-body col-lg-3">
                        <a href="http://app.it-ti.work/youtube.php?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DjhOVibLEDhA&start=1%2C3%2C5%2C7%2C9&share=1" rel="noopener"><img src="http://i.ytimg.com/vi/jhOVibLEDhA/mqdefault.jpg" /><br />恋ダンスオープニング</a>
                    </div>
                </div>
            </div>
          </div>

        </div>
    </div>
</div>



<?php
    //各ソーシャルボタンのタイトル、URLの設定
    $title='トリプルループ';
    $url=$_SERVER["HTTP_HOST"].$_SERVER["REQUEST_URI"].'&share=1';

    //以下のクエリパラメータ削除
    $url=str_replace('&play=1','',$url);
    $url=str_replace('&share-btn=1','',$url);

    $full_url=(empty($_SERVER["HTTPS"]) ? "http://" : "https://").$url
?>


<!-- 共有ボタンクリック時に表示されるダイアログ -->
<div class="modal fade" id="share_box">
  <div class="modal-dialog">
      <div class="modal-content">


      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
        <h4 class="modal-title">リンクを共有</h4>
      </div>
      <div class="modal-body">
        <div class="form-group">
            <input class="form-control" id="link-text" value="<?php echo h($full_url); ?>">
        </div>
        <div class="form-group text-center">
            <ul class="nav nav-pills">
                <li class="list-group-item">
                    <a href="http://b.hatena.ne.jp/entry/<?php $url ?>" class="hatena-bookmark-button" data-hatena-bookmark-layout="vertical-normal" data-hatena-bookmark-lang="ja" title="このエントリーをはてなブックマークに追加"><img src="https://b.st-hatena.com/images/entry-button/button-only@2x.png" alt="このエントリーをはてなブックマークに追加" width="20" height="20" style="border: none;" /></a><script type="text/javascript" src="https://b.st-hatena.com/js/bookmark_button.js" charset="utf-8" async="async"></script>
                </li>
                <li class="list-group-item">
                    <div class="fb-like" data-href="<?php echo $full_url ?>" data-layout="box_count" data-action="like" data-size="small" data-show-faces="true" data-share="true"></div>
                </li>
                <li class="list-group-item">
                    <a href="https://twitter.com/share" class="twitter-share-button" data-url="<?php echo $url ?>" data-text="<?php echo $title.' '.$full_url; ?>">Tweet</a>
                    <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
                </li>
                <li class="list-group-item">
                    <div class="line-it-button" data-lang="ja" data-type="like" data-url="<?php echo $full_url ?>" data-share="true" style="display: none;"></div>
                     <script src="https://d.line-scdn.net/r/web/social-plugin/js/thirdparty/loader.min.js" async="async" defer="defer"></script>
                </li>
            </ul>
        </div>
      </div>


    </div>
  </div>
</div>



<!-- jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

<!-- bootstrap JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>



<script>


    //--- 非同期でIFrame Player Apiコードを読み込み ---
    //scriptタグを生成
    var tag = document.createElement('script');

    //生成したscriptのソースを設定
    tag.src = "https://www.youtube.com/iframe_api";

    //先頭のscriptタグを取得
    var firstScriptTag = document.getElementsByTagName('script')[0];

    //先頭のscriptタグの前に生成したscriptタグを挿入
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);


    //IFrameを作成します。上記APIコードの読み込みが完了後、呼び出される
    var player;
    function onYouTubeIframeAPIReady() {
        player = new YT.Player('player', {
            width:'100%',
            events: {
              'onReady': onPlayerReady,
              'onStateChange': onPlayerStateChange
            }
        });
    }

    //上記で設定したプレーヤーの準備ができたときに実行される
    function onPlayerReady(event) {
        <?php if(isset($_GET['url'])): ?>
            var v=getQueries('v',$('#youtube-url').val());
            //「play」クエリパラメータが設定されているか判定
            <?php if(empty($_GET['play'])): ?>
                //動画を読み込み再生させない
                playVideo(v,false);
            <?php else: ?>
                //動画を読み込み再生させる
                playVideo(v,true);
            <?php endif; ?>
        <?php endif; ?>
    }


    //各変数初期化
    var loop = 0;
    var checkID=0;
    var stopID=0;
    var LOOP_CNT=3;

    //ユーザーエージェントを取得しブラウザがsafariの場合は間隔を調整
    //(safariだけタイマーの間隔を開けないと上手く再生出来ないので)
    var userAgent = window.navigator.userAgent.toLowerCase();
    if(userAgent.indexOf('safari') != -1) {
        var delay=800;
    }else{
        var delay=500;
    }


    //プレーヤーの状態が変更される度に発生
    function onPlayerStateChange(event) {

        if (loop){
            loop--;
            return;
        }
        if (event.data == YT.PlayerState.PLAYING) {
            if (!$('#start').val()) return;


            //画面上で設定された秒数を配列に格納
            var start=$('#start').val();
            var secArr=start.split(',');
            var targetArr=[];

            ////画面上で設定された秒数を昇順にする
            secArr.sort(function(a,b){return a - b;});

            //画面上で設定された秒数のうち、現在の再生時間より大きい値を配列に格納
            targetArr = secArr.filter( function( start ) {return Math.floor(start) >= Math.floor(player.getCurrentTime());});

            //タイマー処理により再生中の動画を監視
            clearTimeout(stopID);
            clearInterval(checkID);
            checkID=setInterval(function(){

                if(player.getPlayerState()==YT.PlayerState.PAUSED || player.getPlayerState()==YT.PlayerState.ENDED){
                    //動画が一時停止、または終了した場合、タイマー処理をクリア
                    clearInterval(checkID);
                }

                if (targetArr.length>0){
                    //ループ再生する秒数を格納した配列が存在する場合
                    var current=Math.floor(player.getCurrentTime()*10); //現在の再生秒の小数点以下1桁まで取得
                    var repSec=targetArr[0];

                    if (Math.floor(repSec*10)==current){
                        //再生秒と設定秒が一致した場合、ループ再生させる
                        loopVideo(repSec);

                        //ループ再生した秒の配列を削除
                        targetArr.shift();
                    }
                }else{
                    //ループ再生する秒数を格納した配列が存在しない場合、数秒後に動画を停止する
                    loop=0;
                    clearInterval(checkID);
                    stopID=setTimeout(function(){player.stopVideo();}, delay*5);
                }



            }, 100);

        }
    }

    //500ミリ秒毎にLOOP_CNT分ループ再生
    function loopVideo(repSec) {
        var loopID=0;
        var cnt=1;

        loopID=setInterval(function(){
            if (cnt>=LOOP_CNT){
                clearInterval(loopID);
                return;
            }

            if (player.getPlayerState()==YT.PlayerState.PLAYING){
                loop+=2;
                player.seekTo(repSec); //指定時間に再生位置を移動
            }

            cnt++;
        }, delay);


    }

    //動画の読み込み、再生
    function playVideo(videoID,play){

        $('#player').removeClass('hide');
        $('#start-set').prop('disabled',false);


        var start = $('#start').val();
        var secArr=start.split(',');
        secArr.sort(function(a,b){return a - b;});
        var start=secArr[0]-0.3; //設定された再生位置より0.3秒前に設定

        if (play){
            player.loadVideoById(videoID, start , "large");
        }else{
            player.cueVideoById(videoID, start, "large");
        }

    }


    //keyで指定したクエリパラメータ取得
    function getQueries(key,url){
        var query = {},
            queries = url.slice(url.indexOf('?') + 1).split('&'), //クエリパラメーターを&で区切り配列に格納
            i = 0;

        //パラメータ名を配列の添え字にし、各パラメータ値を配列に格納
        for(i; i < queries.length; i ++) {
            var t = queries[i].split("=");
            query[t[0]] = t[1];
        }

        //keyで指定されたクエリパラメータを返す
        return (query === null ? null : query[key] ? query[key] : null);

    }

    //入力チェック
    function doValidation(confirm){
        var v=getQueries('v',$('#youtube-url').val());
        if(!$('#youtube-url').val() || !v){
            alert('YoutubeのURLを正しく指定してください');
            return false;
        }

        if((!$('#start').val() && !confirm && $('.condition').is(':visible'))){
            alert('ループする秒(開始位置)を正しく指定してください');
            return false;
        }

        return true;
    }


    $(function () {
        //bootstrapのツールチップ。「?」マーククリック用
        $('[data-toggle="tooltip"]').tooltip({
            container:'body'
        });

        //共有ボタンクリック用の処理。クエリパラメータにshare-btnが存在する場合、共有ボタンのダイアログを表示
        <?php if(isset($_GET['share-btn'])): ?>
            $('#share_box').modal('show');
        <?php endif; ?>

        //「秒取得」クリック時に現在の再生経過時間を設定する
        $('#start-set').on('click',function(e) {
            var current=player.getCurrentTime();
            if($('#start').val()){
                $('#start').val($('#start').val()+','+current.toFixed(1));
            }else{
                $('#start').val(current.toFixed(1));
            }

        });

        //「×」クリック時に設定時間をクリアする
        $('#start-clear').on('click',function(e) {
            if(window.confirm('秒をクリアします。よろしいですか?')){
                $('#start').val('');
            }
        });

        //「トリプルループ再生」クリック時に入力チェックを行い、動画を再生する
        $('#confirm').on('click',function(e) {

            if (doValidation(1)){
                playVideo(getQueries('v',$('#youtube-url').val()),true);
            }

        });

        //「トリプルループ再生」クリック時に入力チェックを行い、動画を再生する
        $('#play').on('click',function(e) {

            if (doValidation(0)){
                playVideo(getQueries('v',$('#youtube-url').val()),true);
            }

        });


        //「submit時(シェアする)」クリック時に入力チェックを行う
        $('form').on('submit',function(){

            return doValidation();
        })

        //リンク用テキストボックスクリック時にURLを選択状態にする
        $("#link-text").click(function(){
            $(this).select();
        });

        //「停止」クリック時に動画を停止する
        $("#stop").click(function(){
            loop=0;
            player.stopVideo();
            clearInterval(checkID);
        });

        //「カスタマイズ」クリック時に設定項目を開閉する
        $('#customize').on('click',function(e) {
            if ($('.condition').is(':visible')){
                $('.condition').val()
                $('.condition').slideUp(); //下から上にスライドして閉じる
                $('.footer-btn').show();
            }else{
                $('.condition').slideDown();  //上から下にスライドして開く
                $('.footer-btn').hide();
            }
        });

        //「共有」クリック時に動画を停止する
        $("#share").click(function(){
            $('#share_box').modal('show');
        });


    })


</script>


</body>
</html>

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です