MediaGoblinでホームビデオを編集・共有する
原文は Michael Lynch により に公開されました。 このブログを購読する
目的
このチュートリアルでは、デジタル化したビデオキャプチャを小さなクリップに編集し、パスワードで保護された自前のMediaGoblinサーバーで公開する方法を紹介します。無料のHeroku dynoを使用するため、このプライベートなメディアサーバーを運用する継続的なコストはGoogle Cloud Storageのストレージ料金のみで、1GBあたり2.3セントです。
私はこのワークフローを使って家族のホームビデオを編集・共有しており、コストは月額わずか0.77ドルでした。詳しい経緯については、ブログ記事「My Eight-Year Quest to Digitize 45 Videotapes」をご覧ください。このワークフローは、切り出して共有したい多数のサブクリップを含むあらゆる種類の動画ファイルに利用できます。
前提条件
- Python 3
- virtualenv
- Git
- Docker
- ffmpeg
- 動画プレーヤー
- 理想的にはAdobe Premiere ElementsやOpenShotのような編集タイムラインを備えたもの。
- 無料のHerokuアカウント
- さらにHeroku CLI
- Google Cloud Platformアカウント(請求が有効なもの)
デモ動画
例として、このチュートリアル全体で使用する動画は、1950年代のテキサスのある家族のパブリックドメインのホームビデオです。
このチュートリアルで編集・共有する素材として使用するパブリックドメインの動画(この動画には音声が含まれていないことに注意してください)
この動画をダウンロードするには、以下のコマンドを実行してください。
VIDEO_URL="https://github.com/mtlynch/free-usage-videos/blob/master/texas-farm-family-1952/TexasFar1952.mp4?raw=true"RAW_VIDEOS_DIR="${HOME}/videos-raw"RAW_VIDEO="${RAW_VIDEOS_DIR}/TexasFar1952.mp4"mkdir -p "$RAW_VIDEOS_DIR"wget "$VIDEO_URL" -O "$RAW_VIDEO"アノテーションの付与
動画ファイルは一連のフレームで構成されています。動画ファイル内のクリップの開始位置と終了位置を特定するには、再生位置に対応するフレーム番号を識別する方法が必要です。そのために、ffmpegを使って元の動画にフレーム番号のアノテーションを付けた作業用コピーを作成できます。
ANNOTATED_VIDEO=TexasFar1952-annotated.mp4ffmpeg \ -i "$RAW_VIDEO" \ -vf "drawtext=fontfile=Arial.ttf: text='%{frame_num}': start_number=1: x=(w-tw)/2: y=h-(2*lh): fontcolor=black: fontsize=20: box=1: boxcolor=white: boxborderw=5" \ -c:a copy \ "$ANNOTATED_VIDEO"これにより、次のような動画のコピーが作成されます。
カタログ作成
次に、テープに収録された映像をカタログ化します。動画を確認しながら、個々のシーンや登場人物、そこで何が起きているかを特定していきます。
お好みの動画プレーヤーを開いてください。私はAdobe Premiere Elementsが最適だと思いますが、無料のOpenShotエディタを試すこともできます。技術的にはどの動画プレーヤーでも動作しますが、フレームごとのコマ送りやズーム可能なタイムラインに対応したものを選ぶと時間を節約できます。
次に、スプレッドシートを作成します。私のスプレッドシートから始めることもできます。各フィールドについて以下で説明します。

私のスプレッドシートと同じ形式で、すべてのメタデータをスプレッドシートに記録してください。
tape_id: 動画ファイルのファイル名(拡張子なし)です。tape_shortname: この素材ファイルに由来するファイルを識別するための短縮名です。tape_idと同じでもかまいません。tape_friendly_name: MediaGoblinがこのクリップがどのテープに由来するかを表示する際に使用するフィールドです。テープのファイル名よりもわかりやすい名前がある場合は、ここに入力してください。scene_start_frame: クリップが始まる最初のフレームの番号です。- 例として挙げたテキサス農場の映像では、最初の数フレームは空白なので、最初のクリップはフレーム28から始まります。
title: このシーンのタイトルです。出力されるファイル名やMediaGoblinのサムネイルで使用されます。render_scenesに特定の区間を無視させたい場合は、タイトルをjunkにしてください。
description: (任意)シーンで何が起きているかの説明です。MediaGoblinの動画視聴画面でクリップの下に表示されます。- hashtags:
description列の後に、ハッシュマークで始まるタグ列を作成できます。ハッシュタグ列にy(または何らかの値)が入っているクリップには、そのタグがMediaGoblinで追加されます。- 例:私のスプレッドシートの9行目では、
#adamと#archieの列にyが入っています。publish_to_mediagoblinがクリップをMediaGoblinに追加する際に、adamとarchieのタグがクリップに追加されます。 - これらの列の目的は、動画内でタグ付けしたい家族やペットがいる場合に、そのハッシュタグの列を追加し、登場するクリップごとにその列に
yを入力できるようにすることです。
- 例:私のスプレッドシートの9行目では、
date: クリップが録画された日付です。- 次のいずれかの形式で記述できます。
YYYY-MM-DDYYYY-MMYYYY
- 次のいずれかの形式で記述できます。
other_tags: クリップに付けたい追加のタグがあれば、カンマ区切りで記述してください。
render_scenes(後述)を実行すると、次の命名規則で動画が短いクリップに分割されます。
[tape_shortname] - [clip index] - [title].mp4
例:
Texas Family - 1952 - 05 - Archie in the Corn Fields.mp4
シーン情報のYAMLファイルを作成する
クリップのカタログ化ができたら、私のスクリプトを使ってCSVをYAMLファイルに変換できます。このYAMLファイルは、他のスクリプトが動画を分割してMediaGoblinにインポートする方法を指示します。
git clone https://github.com/mtlynch/process-home-videos.gitcd process-home-videosmkdir -p ./venvvirtualenv --python python3 ./venv. venv/bin/activatepip install --requirement requirements.txt次に、設定ファイルを作成する必要があります。process-home-videos内のconfig.example.yamlをconfig.yamlにコピーし、ファイル内の指示に従ってクリップ用に編集してください。
cp config.example.yaml config.yaml次に、クリップのメタデータであるCSVファイルとYAMLファイルのパスを作成します。
MEDIAGOBLIN_METADATA="${HOME}/mediagoblin-meta"mkdir -p "$MEDIAGOBLIN_METADATA"# Path to the CSV you created.SCENES_CSV="${MEDIAGOBLIN_METADATA}/scenes.csv"# Path to output file to create.SCENES_YAML="${MEDIAGOBLIN_METADATA}/scenes.yaml"私のスプレッドシートの例に沿って進めている場合は、CSVとしてダウンロードできます。
CSV_URL="https://docs.google.com/spreadsheets/d/1kuamVFEYBrOI097IWBQ8sB0q37ZRACYe2o389Ag92zI/export?format=csv&id=1kuamVFEYBrOI097IWBQ8sB0q37ZRACYe2o389Ag92zI&gid=401061703"wget "$CSV_URL" -O "$SCENES_CSV"次のスクリプトを実行して、CSVをYAMLファイルに変換してください。
app/csv_to_yaml.py \ --config config.yaml \ "$SCENES_CSV" \ > "$SCENES_YAML"これにより、次のようなYAMLファイルが作成されます。
$ head -n 19 "$SCENES_YAML"- description: 'Abigail and Adam stand outside with Abigail primping Adam''s coat. Adam is 12. Recorded between June 1, 1952 and June 30, 1952. Came from tape "Texas Farm Family - 1952," scene #01.' duration_frames: 457 raw_source_filename: TexasFar1952.mp4 rendered_filename: Texas Family - 1952 - 01 - Abigail Primping Adam.mp4 tags: - abigail - adam - best of timecode_start: '0:00:00.934267' title: Abigail Primping Adamクリップの切り出し
すべてのメタデータの準備ができたので、大きな動画ファイルを一連の小さなクリップに切り出す段階です。
# Specify the directory where the script should write out the processed clips.PROCESSED_CLIPS_DIR="${HOME}/videos-processed"mkdir -p "$RAW_VIDEOS_DIR"app/render_scenes.py \ --metadata "$SCENES_YAML" \ --raw_videos_dir "$RAW_VIDEOS_DIR" \ --output_clips_dir "$PROCESSED_CLIPS_DIR"スクリプトが完了すると、次のようなフォルダができあがります。
$ ls "$PROCESSED_CLIPS_DIR"'Texas Family - 1952 - 01 - Abigail Primping Adam.mp4''Texas Family - 1952 - 02 - Abigail Standing on the Stairs.mp4''Texas Family - 1952 - 03 - Adam Plays with Cars on the Sidewalk.mp4''Texas Family - 1952 - 04 - Trudy Working at the Loom.mp4''Texas Family - 1952 - 05 - Archie in the Corn Fields.mp4''Texas Family - 1952 - 06 - Tending to the Cows, Bessie Nursing.mp4''Texas Family - 1952 - 07 - Giving Pigs Belly-Rubs.mp4''Texas Family - 1952 - 08 - Checking on the Chickens and Hens.mp4'MediaGoblin用のファイルを作成する
クリップの処理が終われば、MediaGoblinにファイルをアップロードする準備が整いました。まず、ローカルマシン上のMediaGoblinインスタンスに動画とメタデータをインポートします。これにより、MediaGoblinがファイルを内部データベースとファイル構造に変換し、クラウドでMediaGoblinインスタンスを実行する際に再利用できるようになります。
まず、MediaGoblinの設定ファイルであるmediagoblin.iniに環境情報を入力します。
MG_という接頭辞はホストマシン、MGC_はDockerコンテナ内のパスを指すという命名規則に従っています。MG_CONFIG=$(mktemp)wget https://mtlynch.io/digitizing-home-videos-walkthrough/mediagoblin.ini \ -O "$MG_CONFIG"# Path to MediaGoblin's home directory within the container.MGC_HOME="/var/lib/mediagoblin"MGC_DB_PATH="${MGC_HOME}/mediagoblin.db"# Update the relevant lines in the config file.sed \ --in-place \ "s@.*sql_engine = .*@sql_engine = sqlite:///${MGC_DB_PATH}@" \ "$MG_CONFIG"次に、コンテナを起動します。
# This is a path on the host machine that will receive the files the MediaGoblin# container generates.MG_SERVING_DIR=$(mktemp --directory)# These are paths within the MediaGoblin container.MGC_INPUT_VOLUME="/opt/input-videos"MGC_APP_DIR="/srv/mediagoblin.example.org/mediagoblin"# Name for MediaGoblin Docker container.MGC_NAME=mediaglindocker run \ --tty \ --detach \ --publish 6543:6543 \ --volume "${PROCESSED_CLIPS_DIR}:${MGC_INPUT_VOLUME}" \ --volume "${MG_CONFIG}:${MGC_APP_DIR}/mediagoblin_host.ini" \ --volume "${MG_SERVING_DIR}:${MGC_HOME}" \ --name "$MGC_NAME" \ mtlynch/mediagoblin上記のコマンドは、ホストマシン上のいくつかのパスをMediaGoblinコンテナにマウントし、ホストとMediaGoblin間でファイルを共有できるようにします。
PROCESSED_CLIPS_DIRにより、MediaGoblinは上記で作成したクリップを読み取ることができます。MG_CONFIGはMediaGoblinが使用する設定ファイルを指定します。MG_SERVING_DIRは、MediaGoblinがローカルファイルシステム上に配置した動画ファイルやサムネイルファイルを受け取ります。
コンテナを実行すると、MediaGoblinの起動に数秒かかります。次のコマンドで進行状況を確認できます。
docker logs "$MGC_NAME" -fサーバーの準備ができると、次のような行が表示されます。
Starting server in PID 26.Serving on http://0.0.0.0:6543http://localhost:6543にアクセスして、MediaGoblinサーバーが起動していることを確認できます。
admin、パスワードadminでログインできますが、ログインは必須ではありません。次のスクリプトを実行して、クリップとメタデータをMediaGoblinにインポートしてください。
MEDIAGOBLIN_PUBLISH_HISTORY=$(mktemp)app/publish_to_mediagoblin.py \ "$MGC_INPUT_VOLUME" \ --metadata "$SCENES_YAML" \ --publish_history "$MEDIAGOBLIN_PUBLISH_HISTORY" \ --container_name "$MGC_NAME"スクリプトが完了すると、http://localhost:6543のMediaGoblinのウェブインターフェースにすべてのクリップが表示されるはずです。

MediaGoblinのウェブインターフェースに、8つのクリップすべてのサムネイルが表示されるはずです。
動画をクリックしても、MediaGoblinが現代のストリーミング最適化された動画形式には古すぎるウェブプレーヤーを使用しているため、通常の動画プレーヤーでは視聴できません。「Original file」リンクをクリックすれば視聴できます。

ローカルのMediaGoblinインスタンスにおける単一動画の表示
心配はいりません。次のステップで修正するので、すべての動画を埋め込みプレーヤーで通常通り視聴できるようになります。
ローカルコンテナでの作業はこれで完了なので、削除します。
docker rm --force "$MGC_NAME"Dockerが共有フォルダの所有権を取得したので、取り戻します。
sudo chown "${USER}:${USER}" "$MG_SERVING_DIR" --recursiveそのディレクトリを確認すると、次のような内容が表示されるはずです。
find "$MG_SERVING_DIR" -type f/tmp/tmp.bq7GAbNNNW/media/public/media_entries/8/Texas_Family_-_1952_-_08_-_Checking_on_the_Chickens_and_Hens.mp4/tmp/tmp.bq7GAbNNNW/media/public/media_entries/8/Texas_Family_-_1952_-_08_-_Checking_on_the_Chickens_and_Hens.thumbnail.jpg/tmp/tmp.bq7GAbNNNW/media/public/media_entries/5/Texas_Family_-_1952_-_05_-_Archie_in_the_Corn_Fields.mp4/tmp/tmp.bq7GAbNNNW/media/public/media_entries/5/Texas_Family_-_1952_-_05_-_Archie_in_the_Corn_Fields.thumbnail.jpg/tmp/tmp.bq7GAbNNNW/media/public/media_entries/1/Texas_Family_-_1952_-_01_-_Abigail_Primping_Adam.thumbnail.jpg/tmp/tmp.bq7GAbNNNW/media/public/media_entries/1/Texas_Family_-_1952_-_01_-_Abigail_Primping_Adam.mp4/tmp/tmp.bq7GAbNNNW/media/public/media_entries/4/Texas_Family_-_1952_-_04_-_Trudy_Working_at_the_Loom.thumbnail.jpg/tmp/tmp.bq7GAbNNNW/media/public/media_entries/4/Texas_Family_-_1952_-_04_-_Trudy_Working_at_the_Loom.mp4/tmp/tmp.bq7GAbNNNW/media/public/media_entries/7/Texas_Family_-_1952_-_07_-_Giving_Pigs_Belly-Rubs.mp4/tmp/tmp.bq7GAbNNNW/media/public/media_entries/7/Texas_Family_-_1952_-_07_-_Giving_Pigs_Belly-Rubs.thumbnail.jpg/tmp/tmp.bq7GAbNNNW/media/public/media_entries/6/Texas_Family_-_1952_-_06_-_Tending_to_the_Cows_Bessie_Nursing.thumbnail.jpg/tmp/tmp.bq7GAbNNNW/media/public/media_entries/6/Texas_Family_-_1952_-_06_-_Tending_to_the_Cows_Bessie_Nursing.mp4/tmp/tmp.bq7GAbNNNW/media/public/media_entries/3/Texas_Family_-_1952_-_03_-_Adam_Plays_with_Cars_on_the_Sidewalk.thumbnail.jpg/tmp/tmp.bq7GAbNNNW/media/public/media_entries/3/Texas_Family_-_1952_-_03_-_Adam_Plays_with_Cars_on_the_Sidewalk.mp4/tmp/tmp.bq7GAbNNNW/media/public/media_entries/2/Texas_Family_-_1952_-_02_-_Abigail_Standing_on_the_Stairs.mp4/tmp/tmp.bq7GAbNNNW/media/public/media_entries/2/Texas_Family_-_1952_-_02_-_Abigail_Standing_on_the_Stairs.thumbnail.jpg/tmp/tmp.bq7GAbNNNW/mediagoblin.db/tmp/tmp.bq7GAbNNNW/.cache/gstreamer-1.0/registry.x86_64.binGoogle Cloud Storageの設定
前ステップの目的は、MediaGoblinにすべてのクリップのデータベース、ファイル構成、動画サムネイルを生成させることでした。次はMediaGoblinをクラウドサーバーにデプロイして、ローカルマシンだけでなくどこからでもアクセスできるようにします。
新しいGoogle Cloud Platformプロジェクトを作成する(任意)
MediaGoblin用のファイルを置くためにクリーンなGoogle Cloud Platformプロジェクトを用意したい場合は、以下のコマンドで作成できます。既存のプロジェクトを使用したい場合は、GCP_PROJECT_IDを使用したいプロジェクトIDに設定し、このセクションをスキップしてください。
GCP_PROJECT_NAME="mediagoblin"RANDOM_SUFFIX=$(head /dev/urandom | tr -dc 'a-z0-9' | head -c 16 ; echo '')GCP_PROJECT_ID="${GCP_PROJECT_NAME}-${RANDOM_SUFFIX}"gcloud projects create \ "$GCP_PROJECT_ID" \ --name "$GCP_PROJECT_NAME" \ --set-as-default新しいプロジェクトを請求先アカウントにリンクする必要があります。請求先アカウントの一覧を表示するには、次を実行してください。
gcloud beta billing accounts list次に、BILLING_ACCOUNTをこのストレージバケットの請求に使用したいアカウントに置き換えてください。
BILLING_ACCOUNT="0X0X0X-0X0X0X-0X0X0X" # Replace with account from the listgcloud beta billing projects link \ "$GCP_PROJECT_ID" \ --billing-account "$BILLING_ACCOUNT"ストレージバケットを作成する
次に、ファイル用のGoogle Cloud Storageバケットを作成します。私はすべてのオブジェクトを公開するバケットを作成しましたが、ランダムなバケット名を推測することの困難さに依存しています。
RANDOM_SUFFIX=$(head /dev/urandom | tr -dc 'a-z0-9' | head -c 24 ; echo '')BUCKET_NAME="mediagoblin-${RANDOM_SUFFIX}"# All the videos need to be public for users to access them directly from the# browser, but we're adding enough entropy to the bucket name to make it# infeasible for unauthorized users to access the files by guessing or# enumerating URLs.UNIFORM_ACCESS="on"# Bucket storage properties. You can adjust these depending on your preferences.STORAGE_CLASS="Standard"BUCKET_LOCATION="US-EAST1"# Create the GCS bucket.gsutil mb \ -p "$GCP_PROJECT_ID" \ -c "$STORAGE_CLASS" \ -l "$BUCKET_LOCATION" \ -b "$UNIFORM_ACCESS" \ "gs://${BUCKET_NAME}/"# Set uniform access policy for the bucket.# We use legacyObjectReader because it grants read access to individual files but# prevents clients from exploring the bucket's contents.gsutil iam ch allUsers:roles/storage.legacyObjectReader "gs://${BUCKET_NAME}"ファイルをGoogle Cloud Storageにコピーする
MediaGoblinコンテナがMG_SERVING_DIRディレクトリに生成したファイル(こちら)を覚えていますか?それらを先ほど作成したGoogle Cloud Storageバケットに移動します。
gsutil -m cp -r "${MG_SERVING_DIR}/media/public/*" "gs://${BUCKET_NAME}/"RANDOM_FOLDER=$(head /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c 16 ; echo '')gsutil cp "${MG_SERVING_DIR}/mediagoblin.db" "gs://${BUCKET_NAME}/${RANDOM_FOLDER}/"# Save the public URL of the mediagoblin.db file.MEDIAGOBLIN_DB_URL="https://storage.googleapis.com/${BUCKET_NAME}/${RANDOM_FOLDER}/mediagoblin.db"mediagoblin.dbの保存場所にさらにランダム性を加えているのは、そうしないとバケット名を知っている誰もがデータベースファイルを発見できてしまうためです。このようにエントロピーを追加することで、個々の動画を共有する際にコレクション全体を公開することなく共有できるようになります。Herokuへのデプロイ
メディアファイルがGoogle Cloud Storage上のインターネットからアクセス可能な場所に置かれたので、あとはMediaGoblinサーバーをデプロイするだけです。HerokuはDockerコンテナに対して寛大な無料枠を提供しており、セットアップも簡単なので利用しています。
このマシンでまだHeroku CLIの認証を行っていない場合は、Herokuの認証情報でログインしてください。
heroku login --interactive次に、Herokuのコンテナレジストリに認証し、新しいHerokuアプリを作成します。デフォルトでは、作成されるアプリは無料のHeroku dynoになります。
heroku container:loginRANDOM_SUFFIX=$(head /dev/urandom | tr -dc 'a-z0-9' | head -c 10 ; echo '')HEROKU_APP_NAME="mediagoblin-${RANDOM_SUFFIX}"heroku apps:create "$HEROKU_APP_NAME"最後のステップでは、認証設定とタイトルでDockerビルドをカスタマイズし、Herokuにプッシュして公開します。
pushd $(mktemp -d)REPO="https://github.com/mtlynch/mediagoblin-docker.git"# This is a special branch I created that adds authentication and adds a few# other features.TARGET_BRANCH="mtlynch-custom"git clone --single-branch --branch "$TARGET_BRANCH" "$REPO" .HTTP_AUTH_USER="mediagoblin" # change this to a username you chooseHTTP_AUTH_PASS="goblinmedia" # change this to a password you choose# Title you want for your MediaGoblin server.HTML_TITLE="My Demo MediaGoblin Server"heroku container:push web \ --app "$HEROKU_APP_NAME" \ --arg "HTTP_AUTH_USER=${HTTP_AUTH_USER},HTTP_AUTH_PASS=${HTTP_AUTH_PASS},GCS_BUCKET=${BUCKET_NAME},MEDIAGOBLIN_DB_URL=${MEDIAGOBLIN_DB_URL},HTML_TITLE=${HTML_TITLE}"heroku container:release --app "$HEROKU_APP_NAME" web && \ printf "Your app is live at https://${HEROKU_APP_NAME}.herokuapp.com/\n"すべてがうまくいけば、新しいMediaGoblinインスタンスのURLが次のように表示されます。
Your app is live at https://mediagoblin-v5lmqis51k.herokuapp.com/おまけ:個々の動画を共有する
この手順を家族の動画の保存に使っている場合、家族にはすべての動画を閲覧・アクセスできるようにしたい一方で、コレクション全体へのアクセスを与えることなく個々の動画を他の人と共有したい場合もあるでしょう。
そのようにしたい場合は、MediaGoblinインターフェースの「Original file」リンクをクリックしてください。
これによりゲストはその個別のファイルにアクセスできるようになりますが、Google Cloud Storageバケットの権限を設定した方法により、ライブラリを探索して他の動画にアクセスすることはできません。
「original file」リンクは、次のようなURLになります。
https://storage.googleapis.com/mediagoblin-39dpduhfz1wstbprmyk5ak29/media_entries/4/Texas_Family_-_1952_-_04_-_Trudy_Working_at_the_Loom.mp4
ご覧のとおり、上記のURLがあればその単一の動画を視聴できますが、バケットを探索して他の動画を見つけることはできません。
ソースコード
| リポジトリ | 説明 |
|---|---|
| MediaGoblin | MediaGoblinコアリポジトリとCircleCI設定のミラーです。 ブランチmtlynch-customには、私のインスタンス用のカスタム修正が含まれています(古い動画プレーヤーを置き換え、不要なUIの一部を削除しています)。 |
| mediagoblin-docker | MediaGoblin用のDockerイメージをビルドします。 ブランチmtlynch-customは、Google Cloud Storageバケットへの紐付けやHTTP Basic認証の追加を含む私のカスタマイズでビルドされます。 |
| process-home-videos | 生の動画ファイルをクリップに分割し、それらのクリップをMediaGoblinに公開するためのPythonスクリプトです。 |
記事をランダムに読む


コメント
ログインしてコメントする