File size: 6,324 Bytes
2931418
 
 
 
 
 
 
 
 
 
 
 
 
 
f6a6455
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
01abd01
 
 
 
f6a6455
 
 
 
 
 
 
 
 
 
 
 
 
401a193
 
 
 
 
 
 
 
 
 
f6a6455
dc57844
 
 
 
 
 
 
 
 
 
 
29c4ada
 
 
e935ba7
 
 
 
 
 
 
29c4ada
 
 
dc57844
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f6a6455
 
 
 
 
 
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
---
title: TutorialMaker
emoji: πŸ’»
colorFrom: indigo
colorTo: purple
sdk: gradio
sdk_version: 6.19.0
python_version: '3.13'
app_file: app.py
pinned: false
license: mit
short_description: Make Tutorials from YouTube Videos
---

# YouTube β†’ Tutorial Post Generator

Give a **topic** and your **Hugging Face token**, and this Space builds a downloadable
**`.docx` tutorial** (text + AI-captioned screenshots) from the single best YouTube video
on that topic.

## Pipeline

1. **Search** β€” top 5 videos via the [`adarshajay/youtube-search`](https://huggingface.co/spaces/adarshajay/youtube-search) Space.
2. **Sentiment rank** β€” fetch top comments per video with `yt-dlp` and score them with the
   BERT classifier [`OmarMedhat7/youtube-sentiment-analysis-model`](https://huggingface.co/OmarMedhat7/youtube-sentiment-analysis-model);
   the video with the highest positive share wins.
3. **Download** the winner with `yt-dlp` and extract audio with `ffmpeg`.
4. **Transcribe** locally with `faster-whisper` (segment timestamps).
5. **Candidate frames** are extracted densely from the video, then the **video is
   auto-deleted** β€” only small JPEGs and the transcript remain.
6. **Tutorial text** β€” `deepseek-ai/DeepSeek-V3` (HF Inference Providers, billed to your
   token) turns the transcript into an **answer-engine-optimized** post: an answer-first
   paragraph, H2 step headings, an FAQ, a meta description, a URL slug, and a
   last-updated/source citation. Optional **primary/secondary keyword** placement (primary
   ~3Γ— + in title/slug/meta/intro/H2; each secondary once).
7. **Screenshot selection** β€” a weighted indicator blends each step's LLM-suggested
   timestamp with Whisper's actual speech timing.
8. **Captions** β€” a vision model (default `Qwen/Qwen2.5-VL-72B-Instruct`, billed to your
   token) captions each screenshot.
9. **Assemble** the `.docx` for download.

## Notes

- **Your HF token is used only for the LLM and vision-model calls** and is billed to your
  account. Create a fine-grained token with *"Make calls to Inference Providers"* at
  <https://huggingface.co/settings/tokens>.
- **Free CPU tier:** Whisper runs on CPU, so transcription is slow β€” keep videos short
  (default cap ~20 min).
- **YouTube usually blocks the Space's datacenter IP.** Each visitor can supply their own
  access in the **"YouTube access β€” cookies / proxy"** panel (used only for that run, then
  deleted):
  - **Cookies:** a `youtube.com` cookies.txt (Netscape format, raw or base64) exported
    from a **throwaway** Google account.
  - **Proxy:** a **residential** proxy URL. **Free *datacenter* proxies (e.g. Webshare's
    free tier) usually do *not* bypass YouTube's block** and have tight bandwidth caps.
  - An operator can set shared defaults via the `YT_COOKIES` / `YT_PROXY` Space secrets.
  - A hosted Space **cannot** open or read a visitor's browser β€” auth must be supplied
    explicitly.

## PO token guide (free, no proxy)

YouTube increasingly requires a **Proof-of-Origin (PO) token** to serve video from
datacenter IPs. A matching **PO token + visitor data** pair can get the Space past the
"Sign in to confirm you're not a bot" wall **without any proxy** β€” and it's free. It's
fiddly to extract by hand and the tokens expire (hours), so re-do this when downloads
start failing.

Paste the values into the app's **"YouTube access β€” cookies / proxy"** panel:
**PO token** field and **Visitor data** field.

### Easiest: the PO Token Grabber extension (one click)

This repo ships a tiny Chrome/Edge extension in [`extension/`](extension/) that reads the
PO token + visitor data for you. Grab it with the **⬇️ Download the PO Token Grabber
extension (.zip)** button in the app's *YouTube access* panel (or from
[`extension/`](extension/)), unzip it, and load it unpacked (`chrome://extensions` β†’
Developer mode β†’ Load unpacked). Open a YouTube video and press play, then click the
extension **while on the YouTube tab** to capture the values. Open this Space in another
tab and hit **‡ Fill the Space tab** to inject them straight into the fields below (or use
the Copy buttons and paste). See
[`extension/README.md`](extension/README.md). (It's unpublished/sideloaded and may need
updates when YouTube changes; the manual steps below always work as a fallback.)

### Get them manually (web client, ~2 min)

1. Open a fresh **Incognito** window and go to <https://www.youtube.com> (a throwaway
   logged-in account is fine; if you log in, also export that session's cookies).
2. Open **DevTools β†’ Network** (F12), then **play any video**.
3. **Visitor data:** filter Network for `v1/player`, open the request to
   `youtubei/v1/player`, and in the **Request payload** copy
   `context.client.visitorData` (a string ending in `%3D%3D`). Paste it into the
   **Visitor data** field.
4. **PO token (gvs):** filter Network for `googlevideo.com`, open a `videoplayback`
   request, and copy the value of its **`pot`** query parameter. In the **PO token**
   field enter it prefixed with the client + context:

   ```
   web.gvs+<pot-value>
   ```

   You can supply more than one, comma-separated, e.g.
   `web.gvs+<gvs-pot>, web.player+<player-pot>` (the player PO token comes from the
   `v1/player` request's response, field `...poToken`).
5. Run the Space. When a PO token is present it automatically uses the **web** player
   client so the token applies.

**Tips**
- The PO token is bound to the **visitor data** (and cookies) it was made with β€” always
  submit them together, from the same session.
- Prefer using the **same Incognito session** for cookies + visitor data + PO token.
- Tokens are short-lived; refresh when you next get blocked.

### Automated / operator option

yt-dlp can auto-generate PO tokens via the
[`bgutil-ytdlp-pot-provider`](https://github.com/Brainicism/bgutil-ytdlp-pot-provider)
plugin, but that needs a small provider server running alongside yt-dlp β€” not bundled in
this hosted Space. A self-hosting operator can run it and set shared defaults via the
`YT_POT` / `YT_VISITOR_DATA` Space secrets.

See the authoritative, up-to-date method in the
[yt-dlp PO Token Guide](https://github.com/yt-dlp/yt-dlp/wiki/PO-Token-Guide).

## Local run

```bash
pip install -r requirements.txt   # needs ffmpeg on PATH
python app.py
```