Skip to content

Commit a2a9539

Browse files
Funbucketclaude
andcommitted
feat(book): YouTube 임베드 추가 및 ipynb-youtube-embed 스킬 신설
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 941c790 commit a2a9539

4 files changed

Lines changed: 114 additions & 0 deletions

File tree

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
---
2+
name: ipynb-youtube-embed
3+
description: |
4+
Jupyter 노트북에 YouTube 영상을 임베드하는 스킬.
5+
Jupyter Book(GitHub Pages)에서 iframe이 올바르게 렌더링되도록
6+
출력(outputs)까지 함께 저장한다.
7+
8+
트리거: "유튜브 임베드", "YouTube 임베드", "영상 노트북에 추가", "embed video"
9+
---
10+
11+
# ipynb-youtube-embed
12+
13+
Jupyter Book으로 배포되는 `.ipynb`에 YouTube iframe을 삽입하는 워크플로우.
14+
15+
## 핵심 원칙
16+
17+
Jupyter Book은 노트북을 **실행하지 않고** 저장된 `outputs`를 그대로 렌더링한다.
18+
따라서 코드 셀을 삽입할 때 `outputs`에 HTML 출력을 **미리 저장**해야 iframe이 표시된다.
19+
`outputs`가 비어 있으면 코드만 보이고 iframe은 렌더링되지 않는다.
20+
21+
## 삽입 방법
22+
23+
### 1. YouTube에서 iframe 코드 복사
24+
25+
YouTube 영상 → 공유 → 퍼가기 → `<iframe ...>` 전체 복사
26+
27+
### 2. 노트북 JSON에 코드 셀 + 출력 삽입
28+
29+
```python
30+
import json
31+
32+
NOTEBOOK = "book/{topic}/{notebook}.ipynb"
33+
IFRAME = '<iframe width="560" height="315" src="https://www.youtube.com/embed/{VIDEO_ID}" ...></iframe>'
34+
35+
with open(NOTEBOOK) as f:
36+
nb = json.load(f)
37+
38+
cell = {
39+
"cell_type": "code",
40+
"execution_count": 1,
41+
"metadata": {"tags": ["hide-input"]},
42+
"source": [
43+
"from IPython.display import HTML\n",
44+
f"HTML('''{IFRAME}''')"
45+
],
46+
"outputs": [
47+
{
48+
"data": {
49+
"text/html": [IFRAME],
50+
"text/plain": ["<IPython.core.display.HTML object>"]
51+
},
52+
"execution_count": 1,
53+
"metadata": {},
54+
"output_type": "execute_result"
55+
}
56+
]
57+
}
58+
59+
# 제목 셀(Cell 0) 바로 아래 삽입
60+
nb['cells'].insert(1, cell)
61+
62+
with open(NOTEBOOK, 'w') as f:
63+
json.dump(nb, f, ensure_ascii=False, indent=1)
64+
```
65+
66+
## 태그 선택 기준
67+
68+
| 태그 | 효과 | 사용 시점 |
69+
|------|------|-----------|
70+
| `hide-input` | 코드 접힘, 출력(iframe)만 표시 | 기본값 — 독자에게 Python 코드가 불필요할 때 |
71+
| `remove-input` | 코드 완전 제거, 출력만 표시 | 코드 존재 자체를 숨기고 싶을 때 |
72+
| (없음) | 코드 + 출력 모두 표시 | 코드 노출이 필요할 때 |
73+
74+
## 삽입 위치 기준
75+
76+
- **제목 바로 아래(Cell 1)**: 영상이 해당 노트북 전체를 커버하는 경우 (기본값)
77+
- **특정 섹션 아래**: 영상이 특정 개념만 다루는 경우
78+
79+
## 주의사항
80+
81+
- `outputs`를 빈 배열로 두면 Jupyter Book에서 iframe이 **절대 렌더링되지 않는다**.
82+
- `text/html` 값은 반드시 **리스트 형태** (`["<iframe ...>"]`)여야 한다.
83+
- 언어별 노트북이 분리된 경우(`_ko.ipynb`, `_en.ipynb`), 각각 따로 삽입한다.

README-ko_kr.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ causal_studio/
4242
| `book-serve` | Jupyter Book 로컬 서버 실행 |
4343
| `book-publish` | 노트북을 book TOC에 추가하고 빌드 검증 |
4444
| `ipynb-to-english` | 한국어 노트북을 영어 `_en.ipynb`로 번역 |
45+
| `ipynb-youtube-embed` | 노트북에 YouTube iframe 임베드 (Jupyter Book 렌더링을 위해 outputs까지 저장) |
4546
| `manim-video-pipeline` | scene 설계 / 스크립트 / 렌더 / 오디오 / mux / 합본 전 과정 |
4647
| `manim-thumbnail` | Manim으로 3b1b 스타일 YouTube 썸네일 PNG 생성 |
4748
| `git-commit` | 변경 분석 및 커밋 |

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ Install local video assets → video-assets-setup skill
4242
| `book-serve` | Run Jupyter Book local server |
4343
| `book-publish` | Add notebook to book TOC and verify build |
4444
| `ipynb-to-english` | Translate Korean notebook to English `_en.ipynb` |
45+
| `ipynb-youtube-embed` | Embed a YouTube iframe into a notebook (with pre-stored output for Jupyter Book) |
4546
| `manim-video-pipeline` | Scene design / script / render / audio / mux / concat |
4647
| `manim-thumbnail` | Generate a 3b1b-style YouTube thumbnail PNG with Manim |
4748
| `git-commit` | Analyze changes and commit |

book/why_causal_inference/why_causal_inference_ko.ipynb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,35 @@
1010
"# 우리가 왜 인과추론에 관심을 가져야 할까요?\n"
1111
]
1212
},
13+
{
14+
"cell_type": "code",
15+
"execution_count": 1,
16+
"id": "01f15a40",
17+
"metadata": {
18+
"tags": [
19+
"hide-input"
20+
]
21+
},
22+
"outputs": [
23+
{
24+
"data": {
25+
"text/html": [
26+
"<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/IKurxtuTb3o?si=lz1U1Np592ci_u14\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen></iframe>"
27+
],
28+
"text/plain": [
29+
"<IPython.core.display.HTML object>"
30+
]
31+
},
32+
"execution_count": 1,
33+
"metadata": {},
34+
"output_type": "execute_result"
35+
}
36+
],
37+
"source": [
38+
"from IPython.display import HTML\n",
39+
"HTML('''<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/IKurxtuTb3o?si=lz1U1Np592ci_u14\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen></iframe>''')"
40+
]
41+
},
1342
{
1443
"cell_type": "markdown",
1544
"id": "643181d0",

0 commit comments

Comments
 (0)