Editing and Sharing Home Videos with MediaGoblin

Michael Lynch

MediaGoblinでホームビデオを編集・共有する

目的

このチュートリアルでは、デジタル化したビデオを取り込んで短いクリップに編集し、パスワードで保護した自前の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アカウント
  • 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 editorも試せます。技術的にはどの動画プレーヤーでも構いませんが、フレームごとのコマ送りや拡大可能なタイムラインに対応したものを選ぶと時間を節約できます。

次に、スプレッドシートを作成します。私のものから始めることもできます。各フィールドについて以下で説明します。

私のGoogleスプレッドシートのスクリーンショット

私のものと同じ形式のスプレッドシートに、すべてのメタデータをまとめます。

  • 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に追加する際に、adamarchieのタグがクリップに付与されます。
    • これらの列の目的は、動画にタグ付けしたい家族やペットがいる場合に、そのハッシュタグの列を追加し、登場するクリップごとにyを入力できるようにすることです。
  • date: クリップが録画された日付です。
    • 次のいずれかの形式で記述できます。
      • YYYY-MM-DD
      • YYYY-MM
      • YYYY
  • 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.yamlconfig.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
これはちょっと間抜けな設計でした。他のスクリプトはCSVを直接読み込めばよかったのですが、こうしてしまいました。

クリップの分割

すべてのメタデータの準備ができたら、大きな動画ファイルを一連の短いクリップに分割します。

# 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に環境情報を入力します。

注意: ホストマシン上のパスとMediaGoblin Dockerコンテナ内のパスを区別しやすくするため、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:6543

http://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にすべてのクリップが表示された状態

MediaGoblinのウェブインターフェースに8つのクリップすべてのサムネイルが表示されるはずです。

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

1本の動画表示のスクリーンショット

ローカルのMediaGoblinインスタンスにおける1本の動画の表示

ご安心ください。次のステップで修正するので、埋め込みプレーヤーですべての動画を通常どおり視聴できるようになります。

ローカルコンテナでの作業はこれで完了ですので、削除します。

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.bin

Google 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バケットを作成します。私はすべてのオブジェクトを公開するバケットを作成しましたが、ランダムなバケット名を推測することの困難さに依拠しています。

24文字のランダムなサフィックスにより、3624 = 2.24 x 1037通りの可能性があります。言い換えれば、攻撃者があなたのバケットを見つけるには約1037個のバケット名を推測する必要があります。これは128ビットの秘密鍵を推測するのと同程度です。
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/
注意: Herokuの無料枠を使用しているため、ブラウザでリクエストした際にHerokuがオンデマンドでMediaGoblinインスタンスを起動します。そのため、初回のページ読み込みには30〜60秒かかり、HTTP 502 Gateway timeoutエラーが表示されることがありますが、これは正常です。アプリが起動した後は、数時間放置するまで高速かつレスポンシブに動作します。

おまけ:個別の動画を共有する

この方法で家族の動画を保存する場合、家族にはすべての動画を閲覧・アクセスできるようにしたい一方で、コレクション全体へのアクセス権を与えずに特定の動画だけを他の人と共有したい場合もあるでしょう。

その場合は、MediaGoblinインターフェースの「Original file」リンクをクリックします。

「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があればその1本の動画を視聴できますが、バケットを探索して他の動画を見つけることはできません。


ソースコード

リポジトリ説明
MediaGoblinMediaGoblinコアリポジトリのミラー+Circle CI設定。

ブランチmtlynch-customには、私のインスタンス用のカスタム修正が含まれています(古い動画プレーヤーを置き換え、不要なUIの一部を削除しています)。
mediagoblin-dockerMediaGoblin用のDockerイメージをビルドします。

ブランチmtlynch-customは、Google Cloud Storageバケットへの紐付けやHTTP Basic Authenticationの追加を含む私のカスタマイズでビルドされます。
process-home-videos素の動画ファイルをクリップに分割し、それらのクリップをMediaGoblinに公開するためのPythonスクリプトです。

原文は Michael Lynch により に公開されました。

この記事は「muse-spark-1.2-contributor」を使用して翻訳されました。