Amazon Elastic TranscoderのAPIをaws-cliで叩く

# 概要
ElasticTranscoderの利用について調べると、Amazon Lambdaを使う方法や
マネジメントコンソールから手動でエンコードする方法は見つかるが、
aws-cli経由でAPIを叩く方法が出てこないような気がしたので、
以前軽く触った内容をメモがてら書いておく。

ちなみにLambdaでS3のアップロードイベントをトリガーにエンコードした方が楽そうだが、
やんごとなき事情にてaws-cliからAPI叩く方向で。

今回はデータ暗号化も利用するため、大まかに以下のような流れになる。

1. 入力用と出力用のS3バケットを作成する
2. IAMで暗号化キーを作成する
3. pipelineを作成する
4. Jobの設定内容をJSONで作成
5. 設定JSONをinputにしてaws-cliのコマンドを叩く

なお、今回はmp4をHLS形式にエンコードする。

# 入力用と出力用のS3バケットを作成する
適当に作る。今回は例ということで以下。

| S3バケット名 | 用途 |
|:------------|:-----|
|input        |入力  |
|output       |出力  |

# IAMで暗号化キーを作成する
IAMの左部のメニューから適当に暗号化キーを作成。
arn:aws:kms:ap-northeast-1:XXXXXXXXXXXX:key/XXXXXXX-XXXXXXXXX-XXXX-XXXX-XXXXXXX
こんな感じのができるのでメモっておく。

# pipelineを作成する
触ったアカウントにIAM周りの権限がなく、管理者に依頼して作ってもらったためイマイチ不明。
pipeline作成手順については他の記事を参照する方が画像付で説明されてたりするので吉。

適当に名前と入出力バケットは設定して、Thumbnails用バケットはoutputの方に設定しておく。
後はEncryptionはCustomを選択して、作成してメモっておいた暗号化キーを貼り付ける。
arn:aws:kms:ap-northeast-1:XXXXXXXXXXXX:key/XXXXXXX-XXXXXXXXX-XXXX-XXXX-XXXXXXX

# Jobの設定内容をinputにしてaws-cliのコマンドを叩く
下記のようなjsonのテンプレートを利用する。\_\_HOGE\_\_のようになってる部分はスクリプトで置換する場所。
大まかにはファイル名と再生時間、エンコード用のプリセット指定と、HLS周りの指定だけしている。
HlsContentProtectionで使うKey/KeyMD5/InitializationVectorについては1回手動でJobを作り、
そのJobで生成されたものをコピーして使う。PresetIdはデフォルトで用意されていたHLS用のもの。

```template.json
{
    "PipelineId": "__PIPELINE_ID__",
    "Input": {
        "Key": "sp/__FILE_AND_EXT__",
        "FrameRate": "auto",
        "Resolution": "auto",
        "AspectRatio": "auto",
        "Interlaced": "auto",
        "Container": "auto"
    },
    "Output": {
        "Key": "__FILE__",
        "Rotate": "auto",
        "PresetId": "1351620000001-200010",
        "SegmentDuration": "15.0"
    },
    "OutputKeyPrefix": "sp/",
    "Playlists": [
        {
            "Name": "__FILE___master",
            "Format": "HLSv3",
            "OutputKeys": [
                "__FILE__"
            ],
            "HlsContentProtection": {
                "Method": "aes-128",
                "Key": "__KEY__",
                "KeyMd5": "__KEY_MD5__",
                "InitializationVector": "__INIT_VEC__",
                "KeyStoragePolicy":"WithVariantPlaylists"
            }
        }
    ]
}
```

もっと細かい設定をするなら以下でテンプレートJSONを取得して、必要な項目だけ残せばOK。

```
aws elastictranscoder create-job --generate-cli-skeleton
```

設定用JSONファイルができたら以下のコマンドを叩けばOK。

```
aws elastictranscoder create-job --cli-input-json file://path/to/input.json
```
※file://がないと何故かエラーになるので注意。ハマった。

# 番外編：シェルスクリプトでまとめてエンコードする
入力バケットのファイルをまとめてエンコードして、完了確認までするスクリプトを作った。
力押しの適当な作りになっているのは見逃してほしい。
一応2重に叩かれないようにlockファイル作ったりもしてるが、途中でこけると手動でlockファイル消さないと動かなかったりする。

流れはこんな感じ
1. s3 lsで入力用バケットに入ってるファイルリストを取得する
2. 1で取得したリストの各ファイルについて1個ずつJobを作成する(この時Jobのリストを保存しておく)
3. 2で保存しておいたJobリストが全件Error/Completeになるまでループでsleep入れながらチェック
4. 3でCompleteになっていたものだけ入力用バケットから削除する

awscliをインストール済みで、aws configureも設定済みという想定。

```
#!/bin/bash
 
# configs
script_dir=$(cd $(dirname $0);pwd)
work_dir=${script_dir}/work_tmp/encoding_movie
input_file_list=${work_dir}/input_file.list
 
# elastic transcoder config
pipeline_id=""
encryption_key=""
key_md5=""
init_vec=""
 
# s3 config
input_bucket_name=input
input_file_path=sp
 
# check existence of work dir
if [ ! -e ${work_dir} ]; then
  mkdir ${work_dir}
fi
 
# check lock file
if [ -e ${work_dir}/.lock ]; then
  echo "${work_dir}/.lock is exists"
  exit
else
  # lock execution
  touch ${work_dir}/.lock
fi
  
# init job list
if [ -e ${work_dir}/job.list ]; then
  rm ${work_dir}/job.list
fi
 
# init complete list
if [ -e ${work_dir}/complete.list ]; then
  rm ${work_dir}/complete.list
fi
 
# get filename-hashed list
echo "get input file list"
aws s3 ls s3://${input_bucket_name}/${input_file_path}/ | awk '{print $4}' > ${input_file_list}
 
# Create encode job
for file_name_ext in `cat ${input_file_list}`;
do
  # Extract extension
  file_name="${file_name_ext%.*}"
 
  # Create input json file from template
  echo "create input json for ${file_name}"
  cat ${script_dir}/template/job_template.json \
  | sed -e "s/__PIPELINE_ID__/${pipeline_id}/g" \
        -e "s/__FILE_AND_EXT__/${file_name_ext}/g" \
        -e "s/__FILE__/${file_name}/g" \
        -e "s/__KEY_MD5__/${key_md5}/g" \
        -e "s/__KEY__/${encryption_key}/g" \
        -e "s/__INIT_VEC__/${init_vec}/g" > ${work_dir}/job.json
  
  # Create job and record job and filename list
  echo "create job for ${file_name}"
  aws elastictranscoder create-job \
      --cli-input-json file://${work_dir}/job.json | jq '.Job | [.Id , .Input["Key"] ]| @csv' | sed -e 's/[\"\\]//g' >> ${work_dir}/job.list
done
 
echo "creating job was ended"
 
# wait for completion of encoding movie
for line in `cat ${work_dir}/job.list`;
do
 
  job=`echo ${line} | cut -d ',' -f 1`
  fname=`echo ${line} | cut -d ',' -f 2`
 
  # check job status until all job status are Complete or Error.
  echo "check job status until that status is Complete or Error"
  while true;
  do
    status=`aws elastictranscoder read-job --id ${job} | jq '.Job | .Status' | sed -e "s/\"//g"`
    if [ $status = "Error" ]; then
      echo "Error in Job Id: ${job}"
      break
    elif [ $status = "Complete" ]; then
      echo "Completed"
      echo $fname >> ${work_dir}/complete.list
      break
    fi
    sleep 5
  done
done
  
# delete input_files
echo "delete input_files"
for fname in `cat ${work_dir}/complete.list`;
do
  aws s3 rm s3://${input_bucket_name}/${fname}
done
 
# unlock execution
rm ${work_dir}/.lock
```