最近になって定期更新される YouTube チャンネルを観る習慣がついた。特定の発言がどの動画で出てきたかを探すために、キーワード検索を行うサービスを作成した。使用したコードは こちら、完成したものは こちら。
概要
- データの取得は YouTube API v3 を用いて行った。一つの動画について、データをまとめたファイルとサムネイル画像をローカルへ保存するようにした。
- サイト自体は Wordpress で立ち上げ、検索機能も Wordpress に標準搭載されているものを用いた。WP-CLI を使うことにより、上記データが自動的に記事として登録されるようにした。
- 以下、実装の手順を細かく見ていく。
データの取得
事前準備
- Google Cloud Platform 経由で API KEY を取得しておく。
- データを取得するため、チャンネルの動画をまとめたプレイリストの ID を把握する。チャンネルのトップページから「すべて再生」をクリックすると、チャンネルの動画が最新のものから順に並んだプレイリストにアクセスできるので、URL の
list?=
以下を控えておく。
データの保存
データ取得方法の概要を示す。詳細な仕様については こちらのスクリプト を参照。
- YouTube API v3 を叩くことによって、以下のようにプレイリスト内にある動画のメタデータを取得。さらに、それぞれの動画についてデータをまとめたファイルを
comments/[VideoID].md
へ保存する。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def process(self, next_page_token=None) -> None:
"""
Get videos from a playlist specified by `self.playlist_id`.
If next_page_token exist, call this function recursively
"""
sleep(1)
params: dict[str, str] = {
'playlistId': self.playlist_id,
'key': self.API_KEY,
'part': 'snippet',
'maxResults': 50,
}
if next_page_token is not None:
params['pageToken']: str = next_page_token
response: requests.Response = requests.get(self.endpoint + 'playlistItems', params=params)
resource: dict = response.json()
items: dict = resource['items']
for item in items:
self.idx += 1
# Update `self.video_info`
self.process_video_info(item)
- サムネイル画像を
thumbnails/[VideoID].jpg
へ保存する。
1
2
3
4
5
6
7
8
def download_thumbnail(self) -> None:
"""
Downlowd thumbnail of the video in the `self.video_info`
"""
response: requests.Response = requests.get(self.video_info.thumbnail_url)
image = response.content
with open(f'./thumbnails/{self.video_info.video_id}.jpg', "wb") as f:
f.write(image)
- 取得が完了した動画のメタデータについては log.tsv に保存しておく。
- 以下のコマンドを cron に登録し、上記 Python スクリプトを毎日実行するようにした。取得する動画は、公開から一週間経っており、かつ前回までの実行で取得していない (log.tsv に含まれていない) ものに限定する仕様とした。
1
/usr/local/bin/python /home/ternbusty/download_video_info.py
Wordpress への登録
上記 Python スクリプト内で、動画データを取得したついでに Wordpress へ登録するスクリプトも実行するようにする。
1
2
3
4
5
6
def register_to_wp(self) -> None:
"""
Register a comment file to wordpress
"""
dt_str = self.video_info.published_at.split('T')[0]
subprocess.run(['bash', 'register.sh', self.video_info.video_id, self.video_info.title, dt_str])
register.sh
の中身は以下の通り。
wp post create
を用いて Wordpress への記事登録を行う。後述する理由によりpost_excerpt
には動画の ID を設定した。wp media import
によってサムネイルをインポート & featured image への設定を行った。
1
2
3
4
5
6
7
8
9
ID=$1
POST_TITLE=$2
DT=$3
PATH="./comments/${ID}.md"
echo $PATH
POST_ID=`/usr/local/bin/wp post create ${PATH} --path="./www/" --post_title=${ID} --post_date=${DT} --post_status=publish --post_excerpt=${ID} --porcelain`
echo $POST_ID
/usr/local/bin/wp post update ${POST_ID} --path="./www/" --post_title="${POST_TITLE}"
/usr/local/bin/wp media import ./thumbnails/${ID}.jpg --path='./www/' --post_id=${POST_ID} --featured_image
Wordpress の設定
テーマには Cocoon を用いた。また、/wp-content/themes/cocoon-child-master/functions.php
に以下を追記した。
specific_url_redirect()
: 検索結果をクリックすると動画ではなくデータをまとめた記事自体にアクセスされてしまうので、リダイレクトを行う。ここで先ほど登録したexcerpt
を用いて動画 ID を取得し、URL を生成した (本当は記事の ID を用いたかったが、動画 ID のなかには記事の ID に用いることができない文字が含まれている場合があったため)。change_posts_per_page()
: Wordpress の検索機能では、例えば “あいう えお” などと全角スペースを用いて検索を行うと、”あいう” AND “えお” ではなく “あいう えお” という文字列自体が検索されてしまうらしい。そのため、検索クエリ内の全角スペースを半角に置換するようにした。custom_posts_search_orderby()
: 検索結果が古い順に表示されるようにした。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
add_action('get_header', 'specific_url_redirect');
function specific_url_redirect(){
$url = $_SERVER['REQUEST_URI'];
if (strstr($url, 'posts')){
$id = get_the_ID();
$excerpt = get_the_excerpt($id);
$new_url = 'https://www.youtube.com/watch?v='.$excerpt;
wp_redirect($new_url, 301);
exit;
}
}
add_action('pre_get_posts', 'change_posts_per_page');
function change_posts_per_page($query) {
if (is_admin() || !$query->is_main_query()){
return;
}
if ($query->is_search()) {
$query->set('post_type', 'post');
$s = $query->get('s');
$s = str_replace(' ', ' ', $s);
$query->set('s', $s);
}
}
add_filter('posts_search_orderby', custom_posts_search_orderby);
function custom_posts_search_orderby() {
return 'post_date asc';
}
サイトの見た目を整える
Wordpress のカスタマイズ機能を用いてサイトに こちらの CSS を設定した。何も分からんので !important
だらけでやばくなってしまったけど、まあええか……
感想
今まで web サイトの立ち上げには Jekyll しか使ったことがなく、Wordpress は初めてだったので何も分からんわという感じで悪戦苦闘していた。投稿をクリックで動画に飛ぶところ、リダイレクトではなく単にその投稿へのリンクを張ったほうがよい気がするのでそのうち functions.php
をごにょごにょして何とかしたい。