summernoteで動画ファイルをアップロードする

#はじめに
summernoteの画像オプションにはファイルをアップロードして挿入する方法とURLから挿入する方法の2種類がありますが、動画についてはURLから挿入する方法しかありません。

ファイルをアップロードするプラグインはいくつかありますが、ファイルをアップロードしてエディタ内に動画を埋め込みたいという要望にうまくマッチするものがありません。
summernoteのコアファイルは弄りたくないので、画像オプションを参考にしつつ既存の動画オプションを拡張する形で動画ファイルのアップロードができるプラグインを作る事にしました。

ここではプラグイン作成手順の簡単な説明と、プラグインの利用方法について備忘録として残したいと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186115/9d3dffbd-8f90-a914-64e9-d8d3b81eb001.png)
なお、作成したプラグインのソースはGitHubに上げています。
[summernote-video-upload](https://github.com/fakefurcoronet/summernote-video-upload)
#プラグイン作成
プラグインの作成は基本的に[公式サイト](https://summernote.org/plugins)の通り進めればよいのですが、コールバック関数の設定については記載がありません。
アイコンのWEBフォントと同じくプラグインオプションとして設定することができます。

```javascript
$.extend($.summernote.options, {
  videoUpload: {
    icon: '<i class="note-icon-video"></i>'
  },
  callbacks: {
    onVideoLinkInsert: null,
    onVideoUpload: null,
    onVideoUploadError: null
  }
});
```
##動画オプションメソッド
コアファイルから移植する動画オプションのメソッドは下記の4種類です。
show、showVideoDialog、createVideoNode、readFileAsDataURL

特にcreateVideoNodeメソッドでは動画のURLを解析して、YoutubeであればYoutube用のプレイヤーを、ViemoであればViemo用のプレイヤーを生成しています。
埋め込まれるプレイヤーの縦横サイズを変えたい場合にはこのメソッドを変更します。

```javascript
$video = $('<iframe>').attr('frameborder', 0)
  .attr('src', '//www.youtube.com/embed/' + youtubeId + (start > 0 ? '?start=' + start : ''))
  .attr('width', '640')
  .attr('height', '360');
```
##画像オプションのメソッド
動画ファイルをアップロードするため、下記の4種類の画像オプションのメソッドを流用します。
insertImage、createImage、insertImagesAsDataURL、insertImagesOrCallback
それぞれ下記のように名称を変更しつつ、コールバック関数を利用するために必要なメソッドを追加しました。
insertVideo、createVideo、insertVideosAsDataURL、insertVideosOrCallback、insertVideoLinkOrCallback

まずshowメソッド内でファイルのURLが入力されたのかファイルがアップロードされたのかを判定し、
URLであればinsertVideoLinkOrCallbackメソッド、アップロードならinsertVideosOrCallbackメソッドを呼び出します。
insertVideoLinkOrCallbackメソッドとinsertVideosOrCallbackメソッドではその名の通りコールバックが指定されているかを判定します。

その後insertVideosAsDataURLメソッド、insertVideoメソッド、createVideoメソッドと続いて、各メソッド内でバリデーションチェックなど行いつつcreateVideoNodeメソッドで動画プレイヤーの生成をするのですが、
画像オプションを流用しているため、コールバック関数を利用しないとBase64エンコードされた動画が埋め込まれる事となります。

数MB程度であればいいですが、数100MBの動画をBase64エンコードして埋め込むのは怖いですね。
#コールバック関数の利用
summernoteの呼び出し時に下記のように記述してコールバック関数を指定します。

```javascript
$(function() {
  $('.summernote').summernote({
    lang: "ja-JP",
    toolbar: [
      ['insert', ['videoUpload']]
    ],
    callbacks: {
      onVideoUpload: function(files) {
        sendVideoFile(files[0], $(this));
      }
    }
  });
});
```
下記のようにして利用できます。
サーバー側で処理した後、動画ファイルのURLをinsertVideoLinkOrCallbackメソッドへ返してやる事で、エディター内にプレイヤーを埋め込むことができます。

```javascript
function sendVideoFile(file, $editor) {
  $.ajax({
      type: 'POST',
      url: 'xxxxx',
      data: {
        fileSize: file.size,
        fileName: file.name,
        fileType: file.type
      },
      beforeSend() {
        $editor.summernote('videoUpload.showVideoUploadMessage', '動画をアップロードしています。');
      }
    })
    .then(function(data) {
      $editor.summernote('videoUpload.insertVideoLinkOrCallback', data.videoUri);
    })
    .catch((...args) => {
      console.log('error');
    })
    .then(function() {
      console.log('finish');
    });
}
```
#おわりに
実はこのプラグイン、エラー処理については微妙なところがあります。

summernoteのデフォルトでは画像オプションも動画オプションも、エラーが発生した場合はモーダルウィンドウを閉じて処理が終了するだけで、特にメッセージが表示されたりなどはしません。
動画ファイルは重くなりがちなのでエラーが発生した場合には何らかのメッセージを表示してやりたいところですが…モーダルウィンドウを開いたまま処理を止めてしまうと、再度ファイルをアップロードしてもうまく動いてくれず、デフォルトのままモーダルウィンドウを閉じています。

その辺りは改善の余地がありますね。