Obtain the transcript from the link destination. Prioritize official captions and embedded transcripts, and only when they cannot be obtained, fetch the media audio and transcribe it with local Voxtral.
Principles
- Do not use Whisper unless the user explicitly specifies it.
- "Original transcript" is not translated. Preserve the language of the audio or official transcript, and do not fix Voxtral to
language. - When captions or a transcript can be obtained, prioritize the official text over ASR.
- Do not submit registration forms, logins, payments, or consent buttons on your own.
- Signed URLs tend to expire. Save them immediately after obtaining them.
- In the final deliverable, briefly leave the acquisition method, inputs used, failed routes, and ASR misrecognition risks.
Save location
Place working files in work/ . Place deliverables to hand to the user in outputs/ .
Standard deliverables:
outputs/<slug>-transcript.txtoutputs/<slug>-transcript-timestamped.txt- As needed,
outputs/<slug>-understanding.md
Workflow
1. Inspect the page and embedded data
First save the linked HTML and player config, and look for transcript, caption, subtitle, m3u8, vtt, srt, json, docx.
curl -L "<URL>" -o work/page.html
rg -n "transcript|caption|subtitle|\\.vtt|\\.srt|\\.m3u8|window\\.|uberdata" work/page.html
Do not fabricate URLs. Use only verified URLs such as HTML, network playlists, player config,yt-dlp output, etc.
2. Try captions with yt-dlp
Check the caption list for the page URL or HLS master URL.
yt-dlp --list-subs "<URL>"
If captions are visible, first save only the captions.
yt-dlp --skip-download --write-subs --sub-langs all --sub-format vtt -o "work/%(title)s.%(ext)s" "<URL>"
If it fails, look at the error body. Treat 403, AccessDenied, and Request has expired as signature expiration.
3. Handle expired signed URLs
If there is an expired S3 or CloudFront URL in an HLS subtitle playlist:
caption/*.m3u8or master playlist again.- Only if the user has the relevant Chrome tab open, reload the existing tab and then try re-fetching.
- If the signature is not updated, give up on the caption route and proceed to audio ASR.
For Chrome operations, check whether it is an existing logged-in profile. Do not enter fictitious information in registration forms.
4. Fetch the media
If captions cannot be obtained, check the format withyt-dlp -F and save a light format that contains audio.
yt-dlp -F "<URL_OR_M3U8>"
yt-dlp -f worst -o "work/<slug>.%(ext)s" "<URL_OR_M3U8>"
If the audio is in a separate format, prioritize the audio format.
5. Convert to WAV for Voxtral
Make the Voxtral fallback 16 kHz, mono, PCM16 WAV.
ffmpeg -y -i "work/<slug>.mp4" -vn -ac 1 -ar 16000 "work/<slug>-16k.wav"
ffprobe -hide_banner -show_streams "work/<slug>-16k.wav"
6. Transcribe with local Voxtral
If NiceVoice's VoxMLX server is running, use that server.
pgrep -fl "voxmlx.server"
lsof -Pan -p <PID> -iTCP -sTCP:LISTEN
ws://127.0.0.1:<PORT>/v1/realtime as the endpoint, run the bundle script.
uv run --project /Users/username/ghq/github.com/schroneko/nicevoice-app/Server python <skill-dir>/scripts/voxtral-transcribe-segmented.py "work/<slug>-16k.wav" "outputs/<slug>-transcript.txt" --timestamped-output "outputs/<slug>-transcript-timestamped.txt" --endpoint "ws://127.0.0.1:<PORT>/v1/realtime" --segment-seconds 20 --language-mode none
--language-mode none is the standard for prioritizing the original text. Only when the user explicitly specifies it, use ja、en、auto-ja-en .
7. Verify and report
Check the first few lines, the line count, and the file size.
sed -n '1,8p' "outputs/<slug>-transcript.txt"
wc -l "outputs/<slug>-transcript.txt" "outputs/<slug>-transcript-timestamped.txt"
Include the following in the report:
- Whether official subtitles were obtained, or whether it fell back to ASR
yt-dlpthe reason subtitles could not be obtained with- If Voxtral was used, whether the language was fixed
- Links to the transcript and the timestamped transcript
- Risk of misrecognition of ASR proper nouns, punctuation, and English service names