article_script2
メディアやコールアウトの挿入を楽にしよう!!
このブログで使用しているテーマでは、各種メディアとコールアウトをサポートしています。
- 画像
- Github Gist
- Youtube
- X Post(Tweet)
- Vimeo
- コールアウト
嬉しい機能ですが、マークダウンで記述する性質上、ショートコードを覚えている必要があり、めんどくさいのです。 そこで、またスクリプトを用意しました。
import argparse
import shutil
import re
from pathlib import Path
def main():
parser = argparse.ArgumentParser(
description="Insert media into article"
)
parser.add_argument("article_name", help="Target article name")
subparsers = parser.add_subparsers(
dest="mode",
required=True
)
# picture
pic = subparsers.add_parser("picture", help="Insert picture")
pic.add_argument("picture_name", help="Picture file name")
pic.add_argument("-l", "--line", type=int, required=True)
# callout
callout = subparsers.add_parser("callout", help="Insert callout")
callout.add_argument(
"callout_type",
choices=["alert", "warning", "tip"],
help="Callout type"
)
callout.add_argument("-l", "--line", type=int, required=True)
# youtube
yt = subparsers.add_parser("youtube", help="Insert YouTube embed")
yt.add_argument("url", help="YouTube URL")
yt.add_argument("-l", "--line", type=int, required=True)
# tweet
tw = subparsers.add_parser("tweet", help="Insert tweet embed")
tw.add_argument("url", help="Tweet URL")
tw.add_argument("-l", "--line", type=int, required=True)
args = parser.parse_args()
handle(args)
def handle(args):
article_path = resolve_article(args.article_name)
insert_text = build_insert_text(args)
new_content = insert_at_line(article_path, insert_text, args.line)
safe_write(article_path, new_content)
print(f"Inserted {args.mode} into {article_path} at line {args.line}")
# --------------- #
# Core functions #
# --------------- #
def resolve_article(article_name: str) -> Path:
candidates = [
Path(f"/yout/path/content/posts/{article_name}/index.md")
]
for p in candidates:
if p.exists():
return p
raise FileNotFoundError("Article not found")
def insert_at_line(path: Path, insert_text: str, line_no: int) -> str:
lines = path.read_text(encoding="utf-8").splitlines(keepends=True)
if line_no < 1 or line_no > len(lines) + 1:
raise ValueError("line number out of range")
lines.insert(line_no - 1, insert_text + "\n")
return "".join(lines)
def safe_write(path: Path, new_content: str):
backup = path.with_suffix(path.suffix + ".bak")
tmp = path.with_suffix(path.suffix + ".tmp")
shutil.copy2(path, backup)
tmp.write_text(new_content, encoding="utf-8")
tmp.replace(path)
def build_insert_text(args) -> str:
if args.mode == "picture":
return f'{{{{< figure src="{args.picture_name}" title="" >}}}}'
if args.mode == "callout":
return f'{{{{< callout type="{args.callout_type}" text="" >}}}}\n' # {{< callout type="alert" text="" >}}
if args.mode == "youtube":
video_id = extract_youtube_id(args.url)
return f'{{{{< youtube {video_id} >}}}}' # https://www.youtube.com/watch?v=w7Ft2ymGmfc → w7Ft2ymGmfc
if args.mode == "tweet":
tweet_id = extract_tweet_id(args.url)
return f'{{{{< tweet {tweet_id} >}}}}' # https://x.com/GoHugoIO/status/877500564405444608 → user="GoHugoIO" id="877500564405444608"
raise ValueError("Unknown mode")
def extract_youtube_id(url: str) -> str:
m = re.search(r"v=([^&]+)", url)
if m:
return m.group(1)
raise ValueError("Invalid YouTube URL")
def extract_tweet_id(url: str) -> str:
m = re.search(r"https://x\.com/([^/]+)/status/([0-9]+)", url)
if m:
user = f'user="{m.group(1)}"'
tweet_id = f'id="{m.group(2)}"'
tweet = f"{user} {tweet_id}"
return tweet
raise ValueError("Invalid tweet URL")
if __name__ == "__main__":
main()
主に使う機能として
- 画像
- Youtube
- X Post(Tweet)
- コールアウト の4つに絞っています。
画像挿入機能
使い方
python3 insert_media.py article_name picture image-1.png -l 10
挿入先の記事(article_name)と画像(image-1.png)と行番号(10)を指定します。
内部では、単純に{{{{< figure src="picture_name" title="" >}}}}'を挿入しているだけです。
Youtube動画埋め込み機能
使い方
python3 insert_media.py article_name youtube https://www.youtube.com/watch?v=w7Ft2ymGmfc -l 20
Youtubeでは、vクエリパラメータの値を参照してHugoに投げるようにしています。
こんな感じ{{{{< youtube w7Ft2ymGmfc >}}}} スクリプト内部では正規表現を使っています。
XのPost埋め込み機能
使い方
python3 insert_media.py article_name tweet https://x.com/GoHugoIO/status/877500564405444608 -l 30
Xでは、Youtubeと少し違います。
下記のように、ユーザー名とポストIDを取得しなければいけません。
{{{{< tweet user="GoHugoIO" id="877500564405444608" >}}}}
こちらも正規表現を使ってグループ化してuserとidに引き渡しています。
コールアウト機能
使い方
python3 insert_media.py article_name callout warning -l 40
こういったように埋め込まれます{{< callout type="alert" text="" >}}
コールアウトは3つの設定とカスタム設定がありますが、今回はデフォルトの3つの設定のみをほどこしました。
カスタム設定では、絵文字の変更や色、外枠の太さなどが自由に変更できるようです。
こちらはAlertです
こちらはWarningです
こちらはTipです
おまけ
挿入する画像のexifを削除して、画像名を連番にするシェルスクリプトも用意しました。
実はこちらが先に完成していました。
#!/bin/zsh
set -e
if [ -z "$1" ]; then
echo "usage: $0 image_name"
exit 1
fi
IMG_PATH="/your/path/$1"
if [[ ! -f "$IMG_PATH" ]]; then
echo "Error: File not found - $IMG_PATH"
exit 1
fi
exiftool -all= $IMG_PATH
base="image"
ext="png"
i=1
while [[ -e "${base}-${i}.${ext}" ]]; do
((i++))
done
new_name="${base}-${i}.${ext}"
bak_name="${base}-${i}.${ext}.bak"
exiftool -all= -o "$new_name" "$IMG_PATH"
mv "$IMG_PATH" "$bak_name"
echo "changed name : $1 → $new_name"
echo "created backup : $bak_name"
合わせて使うことによって、ブログの更新を気軽にしよう!という目論みです。
さいごに
作ったスクリプトをコードブロックで紹介するときに、ブロック内でshortcodeとして認識されてしまい、どうやってエスケープすればいいかで20分くらい使ってしまいました。
C言語のコメントアウトと同じようにすればいいという記事を見つけて解決に至りました。
めでたしめでたし。