Monteerib taskuhäälingu heli või videot: eemaldab täitesõnad ja vaikused.
Kogu oskus — vaikeversiooni hoitakse GitHubis; sinu muudatused jäävad sinu enda klooni.
Töötle toored taskusaate-/koosolekusalvestised viimistletud taskusaate episoodideks.
ffmpeg ja ffprobe on paigaldatudOPENAI_API_KEY keskkonnamuutujates (Whisper API transkriptsiooni jaoks)resemblyzer (pip install resemblyzer) – ainult kõnelejate eristamiseks esiletõstuklippide koostamiselffprobe -v quiet -print_format json -show_format -show_streams "INPUT_FILE"
Märgi üles: kestus, diskreetimissagedus, kanalid, koodek, bitikiirus.
Jaga 5-minutilisteks tükkideks ja transkribeeri OpenAI Whisper API kaudu segmenditasemel ajatemplitega:
# Extract chunk
ffmpeg -y -i "INPUT_FILE" -ss OFFSET -t 300 -ar 16000 -ac 1 /tmp/chunk_OFFSET.mp3
# Transcribe
curl -s https://api.openai.com/v1/audio/transcriptions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-F file="@/tmp/chunk_OFFSET.mp3" \
-F model="whisper-1" \
-F response_format="verbose_json" \
-F language="LANG" \
-F 'timestamp_granularities[]=segment' > /tmp/transcript_OFFSET.json
Otsi transkriptsioonidest: - Algusmarkerid: „welcome“, „hello everyone“, „大家好“, „欢迎“, sissejuhatav muusika, esimene sisuline teemalause - Lõpumarkerid: „see you next time“, „bye“, „下期见“, „感谢收听“, millele järgneb saatejärgne jutt
Tee esialgne kärbe käskudega -ss START -to END ja -c copy (ilma ümberkodeerimiseta), et luua tööfail.
Jaga kärbitud fail 5-minutilisteks tükkideks ja transkribeeri iga tükk sõnatasemel ajatemplitega:
# Extract chunks
for i in $(seq 0 300 DURATION); do
ffmpeg -y -i "TRIMMED_FILE" -ss $i -t 300 -ar 16000 -ac 1 /tmp/wchunk_${i}.mp3
done
# Transcribe each chunk (can run in parallel)
for i in $(seq 0 300 DURATION); do
curl -s https://api.openai.com/v1/audio/transcriptions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-F file="@/tmp/wchunk_${i}.mp3" \
-F model="whisper-1" \
-F response_format="verbose_json" \
-F language="LANG" \
-F 'timestamp_granularities[]=word' \
-F 'timestamp_granularities[]=segment' > /tmp/wtranscript_${i}.json &
done
wait
Seejärel käivita selle oskusega kaasas olev täitesõnade eemaldamise skript:
python3 ./filler_removal.py \
--total-duration DURATION \
--end-at END_TIMESTAMP \
--cut START1:END1 --cut START2:END2 \
--chunk-offsets 0,300,600,900,...
Argumendid:
- --total-duration: kärbitud sisendfaili kestus sekundites (kohustuslik)
- --end-at: lõika ära kõik pärast seda ajatemplit (nt saatejärgse jutu algus)
- --cut START:END: lõika välja konkreetne vahemik. Võib korrata.
- --chunk-offsets: komadega eraldatud tükkide nihked (vaikimisi automaatselt 0,300,600,…)
Skript väljastab faili /tmp/ffmpeg_filter.txt filtriga atrim+concat.
Rakenda filtrit kahes etapis:
# Step A: Cut fillers → intermediate WAV (avoids re-encoding artifacts)
ffmpeg -y -i "TRIMMED_FILE" \
-filter_complex_script /tmp/ffmpeg_filter.txt \
-map '[out]' -c:a pcm_s16le -ar 44100 /tmp/podcast_cut.wav
# Step B: Enhance audio → final MP3
ffmpeg -y -i /tmp/podcast_cut.wav \
-af "ENHANCEMENT_CHAIN" \
-c:a libmp3lame -b:a 192k "OUTPUT_FILE"
Piirangud: Whisperi sõnatasemel ajatemplid võivad hiina keele puhul märkamata jätta täitesõnad, mis on naaberkõnega kokku sulanud. Skript tabab eraldiseisvaid täitesõnu usaldusväärselt, kuid võib märkamata jätta ~10–20% kõnesse põimitud täitesõnadest.
Vaikimisi kett (külalisesõbralik – tasakaalustab mitme kõneleja helitugevuse erinevusi). Varasemate käivituste suurim viga on mürapiiraja (agate) kasutamine, mis vaigistab vaiksema külalise täielikult. Ära kunagi lisa agate'i vaikimisi ketti tagasi.
highpass=f=80, # Remove room rumble
lowpass=f=12000, # Remove hiss (use 7500 for 16kHz sources)
afftdn=nf=-25:nr=8:nt=w, # Gentle FFT noise reduction
equalizer=f=180:t=q:w=1.5:g=-2, # Cut mud
equalizer=f=2500:t=q:w=1.2:g=3, # Boost presence
equalizer=f=4500:t=q:w=1.5:g=1.5, # Boost clarity
dynaudnorm=f=200:g=5:p=0.95:m=5:s=0, # Rolling-window normalization — lifts the quieter speaker independently
acompressor=threshold=-20dB:ratio=2:attack=5:release=200:makeup=1, # Gentle glue
loudnorm=I=-16:TP=-1.5:LRA=13 # Podcast standard loudness
Miks dynaudnorm on peategelane: see normaliseerib 200 ms libisevates akendes, nii et kui külaline räägib, tõstetakse seda akent saatejuhi valjematest akendest sõltumatult. Järjekord on oluline – käivita dynaudnorm ENNE acompressor-it, et kompressor näeks tasakaalustatud signaali.
Ära kunagi lisa vaikimisi ketti järgmist:
- agate (mürapiiraja) – lõikab ära iga kõneleja, kes on lävest vaiksem; tapab külalise.
- Tugev kompressioon (suhe >3:1, makeup >2 dB) – lamendab dünaamika ja paneb külalise kõlama pumpavalt.
- Kitsas LRA (<12) loudnorm-is – purustab loomuliku kõne dünaamika.
Kohanda lowpass-filtrit lähtefaili diskreetimissageduse järgi:
- 16 kHz allikas → lowpass=7500
- 44,1 kHz+ allikas → lowpass=12000 (või jäta vahele)
Kontrolli pärast renderdamist külalise kuuldavust: käivita ffmpeg -i OUTPUT -af "ebur128=peak=true" -f null - ja kontrolli, et I: oleks −16 LUFS lähedal ja LRA: 4–6 LU (kitsam LRA sobib, sest dynaudnorm tegi enne akende kaupa tasakaalustamise). Kui väljund kõlab nii, nagu oleks külaline välja lõigatud, kahtlusta, et mürapiiraja või agressiivne kompressor on tagasi hiilinud.
ls -lh "OUTPUT_FILE"
ffprobe -v quiet -show_entries format=duration -of csv=p=0 "OUTPUT_FILE"
Anna aru: kestus, faili suurus, mis eemaldati (täitesõnade arv, vaikuste arv, säästetud aeg).
Kui allikas on video ja kasutaja soovib tagasi lõigatud videot (nt kaassaatejuht vajab monteeritud episoodi teise keelde dubleerimiseks), käivita sammud 1–3 täpselt nagu ülal – tõmba helitükid otse mp4-st välja võtmega -vn – ja rakenda seejärel samad lõikepunktid pildile ja helile ühe läbimisega:
# 1) same chunks + transcription as Step 3, but sourced from the video
for i in $(seq 0 300 DURATION); do
ffmpeg -y -v error -ss $i -t 300 -i SOURCE.mp4 -vn -ar 16000 -ac 1 /tmp/wchunk_${i}.mp3 &
done; wait
# 2) filler_removal.py as usual -> /tmp/ffmpeg_filter.txt
# 3) turn its keep segments into a combined video+audio filter
python3 video_cut.py /tmp/video_filter.txt
# 4) one render pass (hardware encoder; ~7 min for a 60-min 1080p episode)
ffmpeg -y -i SOURCE.mp4 -filter_complex_script /tmp/video_filter.txt \
-map '[vout]' -map '[aout]' \
-c:v h264_videotoolbox -b:v 3500k -c:a aac -b:a 160k \
-movflags +faststart episode-edited.mp4
Kasuta mitte-Apple'i riistvaral h264_videotoolbox asemel -c:v libx264 -crf 21 -preset veryfast.
Märkused:
- Koosta --chunk-offsets kujul $(seq 0 300 3600 | paste -sd, -). Lõpus olev tühi väli paneb argumendiparseri kokku jooksma.
- Tulemus on hüppelõike stiilis. See on oodatav ja sobib rääkivate peade ruudustiku jaoks hästi; lühike sulandus 400+ lõikepunktis näeb halvem välja kui järsk lõige.
- video_cut.py küpsetab parendusketi samasse läbimisse, nii et erinevalt heliteest puudub kaheetapiline WAV-vaheetapp. Ära käivita ketti hiljem uuesti.
- Arvesta 60-minutilise 1080p väljundi puhul ~1,5 GB-ga – see on e-kirjaga saatmiseks liiga suur, seega laadi üles ja jaga linki.
- Salvesta säilitatavate segmentide loend episoodi kõrvale. Ilma selleta tuleb hilisema palve „sama lõige, aga videona“ korral lõiked uuesti tuletada ja need ei ühti juba üle antud heliversiooniga.
Kui kasutaja märgib avaldatud episoodis konkreetseid probleeme („mõned laused jäid sisse“, „lõika välja osa X kohta“, „lõpp salvestati uuesti“, „eemalda topelt sissejuhatus“), ÄRA käivita kogu töövoogu toorfailist uuesti. Lõika probleemsed vahemikud valmis MP3-st kirurgiliselt välja ja kodeeri üks kord uuesti:
atrim+concat filter, mis säilitab kõik peale lõigatavate vahemike; renderda WAV-iks (pcm_s16le), seejärel üks MP3-läbimine. Ära rakenda parendusketti uuesti – lõplik fail on juba parendatud/loudnorm'itud; uuesti käivitamine töötleb seda topelt.Need jäävad täitesõnade/vaikuste eemaldamisest alles, sest on päris lausetega kokku sulanud. Otsi neid transkriptsioonist sihipäraselt: - Koosoleku-/tehnikajutt: „you're muted / unmute“, „你声音太小 / 听得到吗“, „can you see my screen“, „等一下我看一下“. Lõika fraas välja, säilita ümbritsev sisu. - Ummikteemad: paneel tõstatab toote/juhtumi, mida keegi pole uurinud, ja ütleb siis „我们就跳过吧 / let's skip it“ – lõika välja kogu kõrvalepõige, mitte ainult vahelejätmise rida. - Uuesti salvestatud lõigud (eriti lõpud): segane esimene võte, seejärel marker nagu „重新开始 / 从头来 / let's restart / feels redundant“, seejärel puhas uus võte. Lõika välja ebaõnnestunud võte, säilita uuesti salvestatu. Kontrolli seda alati viimase ~3 minuti puhul. - Saate-eelsed / topelt sissejuhatused: enne ametlikku algust salvestatud enesetutvustus, mida hiljem korratakse – lõika saate-eelne koopia välja.
Paralleelsed sõnatasemel päringud tagastavad mõnikord tühja vastuse (0 baiti). Korda tühje päringuid järjestikku, päringute vahel sleep 1.
Lõika valmis episoodist lühikesi, jagatavaid tsitaate (vastuolulisi / läbinägelikke hetki) ja soovi korral pane need muusikaga kokku ~1-minutiliseks kokkuvõtteklipiks.
resemblyzer)Kui vajad konkreetse inimese klippi (või kasutaja ütleb „X-i pole kokkuvõtteklipis“), lahenda see hääle, mitte transkriptsiooni lugemise põhjal. pip install resemblyzer (sisaldab oma kodeerijat – HF tokenit pole vaja). Retsept, mis töötas 5 kõnelejaga paneelil:
1. Koosta puhtad võrdlushääled kõigi jaoks, keda saad tuvastada, lõikudest, kus nad ütlevad oma nime (sissejuhatused) või tutvustavad oma toodet (lõpureklaamid) – need on garanteeritult ühe kõnelejaga. Stabiilsuse huvides keskmista inimese kohta 2–3 akent: enc.embed_utterance(preprocess_wav(slice, source_sr=16000)), keskmista, L2-normaliseeri.
2. Ära klasterda pimesi kogu episoodi – Whisperi segmendid sisaldavad pealerääkimist, nii et AgglomerativeClustering vajub kokku üheks hiiglaslikuks klombiks + üksikuteks. Selle asemel hinda iga segmendi vektoresitust (koosinus) oma puhaste võrdlushäälte suhtes. Segmendid, mis ei sobi hästi mitte kellegagi (parim sarnasus ≲ 0,78, kui päris vasted jäävad vahemikku 0,88–0,94), on tuvastamata N-is kõneleja.
3. Kinnita, et see on üks järjepidev hääl: võta need madala vastega segmendid, kontrolli, et need on omavahel sarnased (≳0,87) ja koos selgelt erinevad igast teadaolevast võrdlushäälest. Koosta neist võrdlushääl, hinda kõik segmendid uuesti – kõrge kindlusega tabamused peaksid kõik olema see inimene.
4. Tee terve mõistuse kontroll teadaoleva põhjal: kinnitatud klipp (nt kellegi tootetutvustus) peab saama kõrgeima skoori oma võrdlushäälega (≳0,9). Kui sinu võrdlushääled ei eristu, pikenda aknaid ja keskmista rohkem.
Laadi kogu episood üks kord 16 kHz mono float-kujul ffmpeg-toru kaudu (-f f32le -) ja lõika mälus – see on palju kiirem kui üks ffmpeg-kutse segmendi kohta.
Võta iga kandidaadi jaoks aken välja ja transkribeeri see uuesti, et (a) veenduda, et sisu/kõneleja on õige, ja (b) leida puhtad lausepiirid. Whisper moonutab nimesid – ära kunagi usalda esimese transkriptsiooni kirjapilti. Kasuta sõnatasemel täpsust, et fikseerida algus, mis ei lõika ära esimest sõna, ja lõpp, mis jätab välja kokutused/kordused.
-ss / -to peavad olema SISENDI valikud (enne -i). Väljundi valikutena annavad need vaikuse või valed vahemikud. ffmpeg -ss START -to END -i in.mp3 ...for s in "${SEG[@]}" (itereeri väärtusi) – mitte kunagi ${SEG[$i]} alates i=0.# 1) extract + remove pauses (collapse gaps >0.2s; gaps sit near -25dB after dynaudnorm, so threshold ~-23dB)
ffmpeg -y -ss START -to END -i FINAL.mp3 -ar 44100 -ac 1 \
-af "silenceremove=start_periods=1:start_silence=0.04:start_threshold=-30dB:stop_periods=-1:stop_duration=0.20:stop_threshold=-23dB:detection=peak" sr.wav
# 2) speech fades (compute fade-out start from sr.wav duration)
ffmpeg -y -i sr.wav -af "afade=t=in:st=0:d=0.12,afade=t=out:st=${DUR-0.3}:d=0.3" f.wav
# 3) tune in -> speech -> tune out
ffmpeg -y -i sting_in.wav -i f.wav -i sting_out.wav \
-filter_complex "[0][1]acrossfade=d=0.18:c1=tri:c2=tri[a];[a][2]acrossfade=d=0.18:c1=tri:c2=tri[out]" \
-map "[out]" -c:a libmp3lame -b:a 192k clip.mp3
Sünteesi signatuurhelid (heliressursse pole vaja) – pehme kellakõla, madal helitugevus:
# tune-in: bright C-E-G bell
ffmpeg -y -f lavfi -i "sine=f=523.25:d=0.85" -f lavfi -i "sine=f=659.25:d=0.85" -f lavfi -i "sine=f=783.99:d=0.85" \
-filter_complex "[0][1][2]amix=inputs=3:normalize=1,afade=t=in:st=0:d=0.02,afade=t=out:st=0.2:d=0.65,lowpass=f=3800,volume=0.30[s]" -map "[s]" -ar 44100 -ac 1 sting_in.wav
# tune-out: lower, gentler G-C bell
ffmpeg -y -f lavfi -i "sine=f=392:d=0.75" -f lavfi -i "sine=f=523.25:d=0.75" \
-filter_complex "[0][1]amix=inputs=2:normalize=1,afade=t=in:st=0:d=0.06,afade=t=out:st=0.15:d=0.6,lowpass=f=3200,volume=0.22[s]" -map "[s]" -ar 44100 -ac 1 sting_out.wav
Liida pausidest puhastatud kõnelõigud (1,5 s vaikne algus/lõpp, ~0,35 s vahed) concat-demuxeri abil, seejärel miksi alla pehme süntesaatoripind ja hajuta kogu pala sisse/välja. Ära kasuta klipipõhiseid signatuurhelisid kokkuvõtteklipi sees uuesti – üks üldine hajutus on puhtam.
# music bed: warm 4-chord pad (C-G-Am-F), each chord 4s, lowpass+tremolo, concat -> pad16.wav, then loop
# (mkchord mixes 3 sines, normalize=1, lowpass=f=750, tremolo=f=4.5:d=0.25, afade in/out)
ffmpeg -y -i speech_reel.wav -stream_loop 6 -i pad16.wav \
-filter_complex "[1]atrim=0:${TOT},volume=0.075[m];[0][m]amix=inputs=2:normalize=0:duration=first[mix];[mix]afade=t=in:st=0:d=1.3,afade=t=out:st=${TOT-1.6}:d=1.6[out]" \
-map "[out]" -c:a libmp3lame -b:a 192k highlight.mp3
amix … normalize=0, et häält ei summutataks.episodes/ep{NNN}/highlights/ep{NNN}-clip{N}-{who}.mp3episodes/ep{NNN}/ep{NNN}-highlight.mp3Genereeri episoodi kaanepilt OpenAI GPT Image API-ga (gpt-image-1), mis vastab sinu saate stiilile. Anna kaasa oma stiilinäidise pilt (eelmine kaanepilt, sinu sõnamärk, sinu värvipalett) – mudel jäljendab seda.
import openai, base64
client = openai.OpenAI() # uses OPENAI_API_KEY from env
style_img = open("YOUR_STYLE_REFERENCE.png", "rb")
result = client.images.edit(
model="gpt-image-1",
image=[style_img], # add a content reference as a 2nd image if you have one
prompt="""Create an illustration in the EXACT same art style as this image
(match the line work, color palette, background, and decorative elements).
Depict: [DESCRIBE THE SCENE]. Keep [YOUR SHOW NAME / wordmark] in the same
style and position as the reference.""",
size="1024x1024",
)
with open("cover.png", "wb") as f:
f.write(base64.b64decode(result.data[0].b64_json))
Märkused: laadi OPENAI_API_KEY keskkonnast, väljasta 1024×1024 PNG ja hoia saate sõnamärk/bränding episoodide lõikes ühtsena.
Kui saatejuht koostab kakskeelseid hiina/inglise saatemärkmeid, peab hiinakeelne osa olema kirjutatud päris hiina keeles – mitte hiina grammatikaga, millesse on puistatud ingliskeelseid tegusõnu ja nimisõnu. Keelte segamine nagu „close 了一个 deal“, „build 出来的 agent“ või „PR 不是 buy 来的“ loeb nagu mustand ja on viga nr 1, mida vältida.
Tõlgi need levinud idufirma-/tehnoloogiavaldkonna ingliskeelsed laensõnad hiina keelde:
$20K, $200K või 200 美金 (arvuga koos sobib kumbki vorm)Loe hiinakeelne osa uuesti läbi hiina lugeja pilguga. Kui mõni lause tundub poolenisti tõlgituna – nt sisaldab sõnu „build“, „close“, „deal“, „view“, „stack“, „leader“ eraldiseisvate ingliskeelsete sõnadena –, kirjuta need sõnad hiina keeles ümber. Ainus inglise keel, mis peaks ülelugemise üle elama, on brändinimed ja ülaltoodud lühendid.
Whisper moonutab sageli ettevõtete, toodete ja inimeste nimesid. Enne saatemärkmete või mis tahes nimesid ja linke sisaldava väljundi genereerimist:
acme.com, acmehq.com või midagi hoopis muud. Küsi alati.See on eriti oluline tagasilinkide või sotsiaalmeedia postituste genereerimisel – valesti kirjutatud domeen on raisatud link.
Kaks eraldi jaotist – esmalt hiina, siis inglise keel (või millised keeled saade ka ei sihiks). ÄRA põimi neid vaheldumisi ega pane kõrvuti.
Pealkirjade reegel: hoia pealkirjad madalad ja järjepidevad – vali üks tase (nt H2) ja lamenda kõik alajaotised sellele. Mõned avaldamisplatvormid renderdavad ainult ühte pealkirjataset pluss paksu kirja; kui sinu oma teeb nii, järgi seda.
Ajatempli vorming: alati MM:SS eesolevate nullidega (nt 08:25, 00:00, 42:10). Mitte kunagi 0:00 ega 1:05.
EP{NNN}: {Episode title}
---
## 中文
**嘉宾:** {中文姓名 English Name}, {中文职位} {公司} (URL)
## 简介
{完整中文段落}
## 时间轴
- 00:00 — {中文描述}
- 08:25 — {中文描述}
## 核心要点
- {中文要点}
## 相关链接
- {品牌名}:{URL}
---
## English
**Guest:** {English Name}, {Title} at {Company} (URL)
## Summary
{Full English paragraph}
## Timestamps
- 00:00 — {English description}
- 08:25 — {English description}
## Key Takeaways
- {English takeaway}
## Links
- {Brand}: {URL}
Miks kaks jaotist kakskeelsete loendipunktide asemel: hiina lugejad tahavad puhast hiinakeelset proosat, inglise lugejad puhast ingliskeelset proosat. „中文 / English“ vaheldumine igas loendipunktis muudab mõlemad pooled raskemini loetavaks. Kirjuta iga jaotis nii, nagu oleks see ainus.
Hoia iga episood iseseisvalt oma kaustas. Lihtne, nullidega täidetud paigutus skaleerub puhtalt:
episodes/
├── ep001/
│ ├── ep001-final.mp3 # the finished episode
│ ├── ep001-highlight.mp3 # optional 1-min reel
│ ├── cover.png
│ └── shownotes.md
└── ep002/
└── ...
ep{NNN} (nullidega täidetud 3 numbrit)ep{NNN}-final.mp3; esiletõstuklipid asukohas ep{NNN}/highlights/ep{NNN}-clip{N}-{who}.mp3Kui kasutaja soovib lihtsalt lihtsat kärbet (nt „lõika esimesed 3 s ära“):
ffmpeg -y -i "INPUT" -ss 3 -c copy "OUTPUT"
Kasuta -c copy kohese kadudeta kärpimise jaoks, kui helitöötlust pole vaja.