動画のスライドを抽出する

動画の全スライドを抽出して PDF 化する

スライドの抽出処理

ffmpeg で画像切り出しをした所で下記の記事を発見した。

qiita.com

ありがたや〜早速動かしてみる。

必要なパッケージのインストール

brew install ffmpeg imagemagick
touch movie2slide.sh

movie2slide.sh

#!/bin/bash

for command in ffmpeg convert diff
do
    which command$
    if [ $? != 0 ]; then
        echo "$command not found."
        exit 1
    fi
done

PSNR_threadshold=30
SLIDE_PREFIX=slide
RATE=1
isRATEdecimal=$( echo "$RATE > 1" | bc )
PHOTOFORMAT=jpg

moviefile=$1
pathname_tmp=${moviefile// /_}
pathname=${pathname_tmp%.*}
workdirname="${pathname}.$( date +"slide_%Y%m%d_%H%M%S" )"
mkdir $workdirname

ffmpeg -i "$1" -map 0:1 -vn -ac 2 -acodec pcm_s16le  -f wav ${workdirname}/audio.wav
ffmpeg -i "$1" -map 0:1 -vn -ac 2 -acodec libmp3lame -f wav ${workdirname}/audio.mp3

ffmpeg -i "$1" -r ${RATE} ${workdirname}/${SLIDE_PREFIX}.%d.png

cd ${workdirname}
finalslide_number=$( ls | wc -l )

merge_and_delete_duplicated_photos() {
    local same_picture_begin=$1
    local same_picture_end=$2
    local j

    if [ "${same_picture_begin}" != "${same_picture_end}" ];then
        if [ "${SEQUENCE}" != "" ]; then
            convert -evaluate-sequence ${SEQUENCE} $(
                for j in $( seq ${same_picture_begin} ${same_picture_end} ) ; do
                echo ${SLIDE_PREFIX}.$j.png
                done
            ) zmedian.${PHOTOFORMAT}
        fi

        for j in $( seq $((same_picture_begin+1)) ${same_picture_end} ) ; do
            rm ${SLIDE_PREFIX}.$j.png
        done

        if [ -e zmedian.${PHOTOFORMAT} ]; then
            mv zmedian.${PHOTOFORMAT} ${SLIDE_PREFIX}.${same_picture_begin}.${PHOTOFORMAT}
        fi
    fi

    if [ "${PHOTOFORMAT}" != "png" ]; then
        convert ${SLIDE_PREFIX}.${same_picture_begin}.png ${SLIDE_PREFIX}.${same_picture_begin}.${PHOTOFORMAT}
        rm ${SLIDE_PREFIX}.${same_picture_begin}.png
    else
        pngquant ${SLIDE_PREFIX}.${same_picture_begin}.png
        mv ${SLIDE_PREFIX}.${same_picture_begin}-fs8.png ${SLIDE_PREFIX}.${same_picture_begin}.png
    fi
}

detect_duplicated_photos() {

    local same_picture_begin=1
    local same_picture_end=1
    local i

    for i in $( seq 1 ${finalslide_number} ); do

        filename_current=${SLIDE_PREFIX}.$i.png
        filename_next=${SLIDE_PREFIX}.$((i+1)).png

        filename_start=${SLIDE_PREFIX}.${same_picture_begin}.png

        if [ -e $filename_current ] && [ -e $filename_next ]; then


            diff -b $filename_start $filename_next > /dev/null

            if [ $? -eq 0 ]; then

                PSNR=0
            else

                PSNR=$( compare -metric PSNR $filename_start $filename_next zdiff.png 2>&1 )
                rm zdiff.png


                PSNR=${PSNR%.*}
            fi

            echo -en "$filename_start -> $filename_next : PSNR = " $PSNR


            if [ $PSNR -eq 0 ] || [ $PSNR -ge $PSNR_threadshold ]; then
                echo -n " : delete"
                same_picture_end=$((i+1))
            else
                echo -n " : convert to ${PHOTOFORMAT}"
                merge_and_delete_duplicated_photos $same_picture_begin $same_picture_end

                same_picture_begin=$((i+1))
                same_picture_end=$((i+1))
            fi
            echo ""
        fi
    done

    merge_and_delete_duplicated_photos $same_picture_begin $same_picture_end
}

rename_filename_with_timestamped() {

    for file in $( ls ${SLIDE_PREFIX}.*.${PHOTOFORMAT} )
    do
        filename_tmp=${file%%.${PHOTOFORMAT}}
        number=${filename_tmp##${SLIDE_PREFIX}.}
        number=$(( number + 0 ))

        raw_seconds=$( echo "scale=2; $number/$RATE" | bc )
          hour=$( echo "scale=0; $raw_seconds/3600"            | bc )
        minute=$( echo "scale=0;($raw_seconds -$hour*3600)/60" | bc )
        second=$( echo "scale=0;($raw_seconds -$hour*3600 -$minute*60)*2/2" | bc )

        if [ $isRATEdecimal == 1 ]; then
            millisecond=$( echo "scale=0; ($raw_seconds -$hour*3600 -$minute*60 -$second)*100" | bc )
            timestamp=$(printf "%02d.%02d.%02d.%02d" ${hour%.*} ${minute%.*} ${second%.*} ${millisecond%.*} )
        else
            timestamp=$(printf "%02d.%02d.%02d" $hour $minute $second )
        fi
        mv $file ${SLIDE_PREFIX}.$timestamp.${PHOTOFORMAT}
    done

    for file in $( ls ${SLIDE_PREFIX}.*.${PHOTOFORMAT} ) ; do touch $file ; done
}

detect_duplicated_photos
rename_filename_with_timestamped

実行

zsh ./movie2slide.sh {xxxxx}.mp4

僕の環境で 1 時間の動画を変換するのに 20 分ほどかかった。 また、途中ディレクトリの容量を観察してみると 10GB を超えていたので容量には注意。 最初に出来上がった PNG ファイル 4200 枚が 重複分を削除し、614 枚まで減った。 最終的に 43.8MB に落ち着いた。

PDF 化処理

こちらの記事に大枠はありました。

qiita.com

ありがたや〜。

必要なパッケージのインストール

pip3 install img2pdf
pip3 install natsort
touch convert2pdf.py

img2pdf が brew パッケージにあれば 1 つのシェルスクリプトファイルだけで完結するのだが、仕方なし。 記事では出力された画像がソートされていないのでパッケージを追加して PDF を作成。

convert2pdf.py

import os
import img2pdf
from PIL import Image

if __name__ == '__main__':
    pdf_FileName = "./output.pdf" # Export PDF filename
    png_Folder = "./jpg/" # Input Images Directory
    extension  = ".jpg" # convert only jpg files

    with open(pdf_FileName,"wb") as f:
        f.write(img2pdf.convert([Image.open(jpg_Folder+j).filename for j in natsorted(os.listdir(jpg_Folder))if j.endswith(extension)]))

よく使うが覚えていないコマンド群

なぜ書くか?

誰しも何度もググる事はある。むしろググればわかる、という安心感は依存さえ覚える。

ただ、本当に何回も同じ検索をするのは非効率なので(覚えろという話はさておき)、

よく使うが覚えきれていないコマンドを列挙する。

もし閲覧された方が面白いと思われるものがあれば嬉しい。

コマンド一覧(逐次追加)

連番ファイルの作成

touch hoge-{1..30}.md

ファイルサイズ順に降順にソート

ls -lS

特定の文字列の検索

find . -type f -print | xargs grep 'hoge'

一つ上の階層に移動

..

いつもcd ..としがちなので。

npm run devで開くlocalhostのポート番号を変更

ポート番号の変更

Reactではデフォルトのポート番号として3000番が割り当てられている。

ただこれだと、複数のローカルサーバを立ち上げるときにポート番号がバッティングしてしまう。

それを避けるためにpackage.jsonにおいて下記の変更を行なう。

(Nextjsを使っているがreact-scriptsでも同様)

  "scripts": {
    "dev": "PORT=3001 next dev",
    "build": "next build",
    "start": "PORT=3001 next start"
  },

[GitHub]Deprecation Notice

Github から届いたメール

Hi @hoge,

You recently used a password to access the repository at hoge/fuga with git using git/2.30.1 (Apple Git-130).

Basic authentication using a password to Git is deprecated and will soon no longer work. Visit https://github.blog/2020-12-15-token-authentication-requirements-for-git-operations/ for more information around suggested workarounds and removal dates.

Thanks, The GitHub Team

ふむふむ、2021 年 8 月 13 日以降はパスワードを用いたアクセスができなくなるらしい。 そのため、今のうちに 2 段階認証やトークンを用いたアクセスに切り替えればよいとのこと。

qiita.com

上記記事はためになったものの、情報が不足していたので実際に僕が行なった設定を書いておく。

設定手順

2 段階認証を有効化する

下記の Github のセキュリティページより有効化。 簡単なので説明は不要でしょう。MFA か SMS 認証かを選べます。

github.com

Personal access tokens を発行する

下記の Github のアクセストークンページより新規トークンを作成。

repo から gist までを全てチェックの上 Generate token。

表示されるトークンは一度きりなので必ずどこかに保存。

github.com

接続確認

Terminal から下記コマンドを実行し、クレデンシャル情報をリセットする。

docs.github.com

git credential-osxkeychain erase
host=github.com
protocol=https

次に先ほど保存したトークンを用いて適当な自身のプライベートリポジトリをクローン。

git clone https://github.com/hoge/fuga.git
Cloning into 'fuga'...
Username for 'https://github.com': your-username
Password for 'https://hoge@github.com': your-token

MacでSafariとChromeの間でタブをショートカットでスイッチする方法

スイッチするメリット

今開いているタブを Safari で開きたいのにな。

また逆も然りで、Chrome で開きたいのにな。

そう思う瞬間が誰にでも一度はあるはずです(ほぼない)

そう、誰が求めているかわからないニッチな話です。

僕自身はこんな使い方をしています。

  1. 動画を見るとき:Safari は倍速時の音質が悪い

  2. リーダーモードで読みたい時:chrome://flags から追加できるが使いにくい

という事で基本的に記事を読む時は Safari、動画を見る時は Chrome と使い分けています。

で、早速方法を調べてみてもなかなかヒットしない。

Safari から Chrome を開く方法はかろうじてヒットするものの、

逆は英語で検索してもなかなか出てこない。

1 時間程かけてなんとか見つかったので覚書。

結論

Safari から Chrome の開き方

  1. Safari の Develop メニューを有効化

  2. 利用中の Chrome のバージョンをコピー

  3. System Preferences > Keyboard > App Shortcuts より Safari のショートカットを追加

上記の三本立て。

まず Safari を開いて Cmd+,もしくは Menu Bar > Safari > Preferences。

一番右にある Advanced から Show Develop menu in menu bar をチェック。

そうすると Menu Bar に Develop が表示されます。

念の為確認しましょう。

Develop > Open Page With > Google Chrome.app ("Google Chrome Version")

このメニューにショートカットを割り当てます。

次に現在の Chrome のバージョンを確認します。

Chrome を開いて Cmd+,もしくは Menu Bar > Safari > Preferences。

サイドバーから About Chrome を開くとバージョンが確認できるのでこれをコピー。

f:id:hashlog:20210801142504p:plain
Google Chrome Version

私の場合 92.0.4515.107 でした。

最後にショートカットを設定します。

System Preferences を開いて、Keyboard の一番下にある App Shortcuts を開いてください。

+ボタンを押して Applications を Safari、Menu Title を Google Chrome.app ("Google Chrome Version")とします。

app と(の間に半角スペース、また()は半角である事にご注意ください。

"<Google Chrome Version"を先ほどコピーしてもらったバージョンに差し替えてくださいね。

お好きなショートカットを設定して Add。(僕は Cmd+Shift+O にしました)

完了したら是非ショートカットから実行してみてください。

Chrome から Safari の開き方

  1. AutomatorApple Script を書いた Action を作成、保存

  2. System Preferences > Keyboard > App Shortcuts より Safari のショートカットを追加

上記の二本立て。

先ほどは標準のメニューにショートカットを追加しただけでしたが、

こちらは用意されていないため自分で作らねばなりません。

そこで Automator.app を利用します。

New Document > Quick Actions > Utilities > Run Apple Script と進みますと

コードの編集画面が表示されます。

有志の方が処理を Gist にあげてくださっていますので、ありがたく使わせていただきましょう。

gist.github.com

こちらのコードを貼り付けてお好きな名前で保存。

ちなみに、デフォルトで./Library/Services にファイルは保存されます。

System Preferences を開いて、Keyboard の真ん中ほどにある Services を開いてください。

一番下に先ほど保存した名前の項目が増えていますので、Add Shortcut をクリック。

お好きなショートカットを設定して Add。(僕はこちらも Cmd+Shift+O にしました)

完了したら是非ショートカットから実行してみてください。

iOS端末の標準アプリ削除・非表示方法

結論

  1. 「×」で簡単に消せるもの

アプリを長押しで表示される「×」を押す

これで、Podcast、Maps、iTunes Store、メールなど多くの標準アプリが消せる

  1. Screen Timeで機能をなくせるもの①

「Screen Time」→「Contents & Privacy Restrictions」→「Allowed Apps」

これで、Safari、Camera、Wallet、Healthが消せる

  1. Screen Timeで機能をなくせるもの②

「Screen Time」→「Contents & Privacy Restrictions」→「iTunes & App Store Purchases」→「Installing Apps」→「Don't Allow」

これで、App Storeが消せる

  1. どうにも消せないもの

下記の標準アプリを消す方法は現状見つかっていない

Clock、Settings、Photos、Phone、Message、Find My

Vimeoの動画を拡張機能等使わず直接ダウンロードする方法

拡張機能がはらむセキュリティ問題

Google Chrome を含む昨今のほとんどのブラウザには便利な拡張機能が用意されている。 動画の再生速度を変更したり、煩わしい広告をブロックしたり、Amazon の価格推移を確認したり。 その便利さには抗えず、それがどんなコードで書かれているか気にせず拡張機能をインストールしてしまう。 幸いにも悪意のない拡張機能がほとんどではあるが、悪意のあるコードが仕組まれている事が実際にある。

例えば、2020 年 12 月にアンチウイルスソフトウェアの会社として長い歴史を持つ Avast が動画ダウンロードに関連する拡張機能の利用に注意を喚起している。

press.avast.com

Google Chrome拡張機能は基本的に JavaScript をベースとして作成されるが、したがって機能以外のコードを拡張機能のプログラム内に埋め込むことで開発者が好き勝手できるという訳だ。それにほとんどのブラウザの利用者はエンジニアではなく、もし仮にエンジニアだとしても中身のコードまで確認するような人は皆無である。よって、利用者がかなり増えた段階で調査が行われ、明るみに出たという話である。

どんなコードかもし気になる方がいらっしゃれば、発見の先駆けになったであろうチェコ語のブログ記事のリンクを記載するので、確認されたい。数多くのコードの中のよく解読しないと何をしているのかわからないたった 14 行のコードが埋め込まれていたようである。

blog.nic.cz

いずれにせよ、拡張機能を利用する際は自己責任のもと可能な限り信頼のおけるものを選んでいただきたい。

Vimeo 動画のダウンロード方法

さて、早速本題にいこう。 なぜ前置きを長々とお伝えしたかというと、不必要なリスクは負わない方が良いためである。 少なくとも Vimeo の場合は公開者の設定次第ではあるが、全くセキュリティに問題のないダウンロード方法が存在する。

※ダウンロードした著作権を所有していない動画を再アップロードするなど意図的・意図的でない著作権違反にご注意を。

まずはダウンロードしたい動画のページを開く。 この時点で URL は下記の 2 種類のいずれかである。

  1. https://vimeo.com/{hogefuga}
  2. https://player.vimeo.com/video/{hogefuga}

二つ目の動画 URL であればそのまま、一つ目の動画 URL であれば{hogefuga}部分をコピーして二つ目の動画 URL 形式で開き直す。 その URL に「/config」を付け足す。つまり

https://player.vimeo.com/video/{hogefuga}/config

とし、ページを開くと白地に黒文字のみのシンプルなページが開く。 これはいわゆる JSON 形式のもので JavaScript で取り扱いのしやすいデータ形式である。 このページ内で「.mp4"」と検索すると解像度ごと(周辺に 720p、360p などが記載)に動画の URL が記載されている。 例えば、下記のような URL が見受けられる。

https://vod-progressive.akamaized.net/exp={hoge}.mp4~hmac={fuga}/vimeo-prod-skyfire-std-us/{piyo}.mp4

上記の URL を再び開くとシンプルな動画の再生画面が表示される。この画面において右下の「︙」をクリックし、「Download」をクリックすることで動画をダウンロードする事ができる。