© 2026 Chiranjeev (@chiranr19) — All Rights Reserved. This project is source-available for viewing only; it is not open source. No copying, reuse, modification, deployment, or redistribution of any part of it (or its underlying ideas) without prior written permission — see LICENSE and SIGNATURE. Prospective employers and collaborators are welcome to read the code. · authorship sigil
AQY3·QG3X·BBYY·R2WN
Find the 30 seconds that hook you. An explainable, dependency-light Python package that locates the most memorable segment of a song — the chorus, the hook, the part you'd use as a preview clip — and tells you why it picked it.
Built on librosa. No cloud services, no API keys, no model downloads. Point it at an audio file and it returns a start/end timestamp plus a per-signal breakdown.
The audio above is synthetic (a generated verse/chorus signal); the detected window and the per-signal scores are hookfinder's real output on it.
from hookfinder import find_hook
hook = find_hook("song.mp3")
print(hook) # Hook(start=57.00s, end=87.00s, duration=30.00s, score=0.91)
print(hook.components) # {'repetition': 0.98, 'harmonic': 0.71, 'rhythmic': 0.66, ...}$ hookfinder song.mp3
song.mp3
best hook: 0:57 -> 1:27 (30s, score 0.912)
repetition=0.98 harmonic=0.71 rhythmic=0.66 energy=0.80 centrality=1.00The durable, reusable piece of a music app is rarely the app — it's the pipeline that answers "which slice of this track do I preview?" This package is that pipeline, extracted and made general. A Tamil-film-music app is just one consumer of it; so is a podcast teaser tool, a DJ crate-digging helper, or a dataset-builder for MIR research.
pip install hookfinderOr from source:
git clone https://github.com/chiranr19/hookfinder
cd hookfinder
pip install -e .librosa pulls in soundfile/audioread for decoding. For formats beyond WAV
(mp3, m4a, webm), having ffmpeg on your PATH is
recommended.
Every candidate window (default 30 s, stepped across the song) is scored on
five signals, each normalized to [0, 1]:
| Signal | Measures | Intuition |
|---|---|---|
| repetition | recurrence of the section elsewhere in the song | the hook is the part the song keeps returning to |
| harmonic | frame-to-frame chroma stability | settled sections beat transitional churn |
| rhythmic | percussive onset strength | the groove is present and driving |
| energy | fraction of the window above a loudness threshold | sustained, not a quiet passage |
| centrality | structural position prior | hooks rarely live in the intro or outro |
The signals are combined with configurable weights (repetition leads by
default), the score curve is smoothed so the pick is a broad plateau rather
than a one-window spike, the top window is chosen, and its start is snapped to
a nearby beat for a clean cut. Because the score is a transparent weighted sum,
every result carries the components that produced it — no black box.
Feature extraction runs once per song; scanning candidates is cheap.
Two quality modes, set with quality= (or --quality):
| Mode | Chroma | Rhythm source | Analysis time* |
|---|---|---|---|
fast (default) |
chroma_stft on the mix |
onset strength on the mix | ~1.5 s |
high |
chroma_cqt on harmonic part |
onset/beats on percussive part | ~19 s |
*Excluding decode, on a 4.5-minute track. high runs harmonic/percussive
separation (HPSS), which alone costs more than the entire fast pipeline.
Decode usually dominates total runtime, not analysis — a 4.5-minute .webm
took ~21 s to decode via librosa's audioread fallback versus ~1.5 s to
analyze. Installing ffmpeg and/or working from .wav
makes the biggest difference.
On real songs the two modes tend to surface the same set of candidate
regions but sometimes rank them differently — because songs often have
several near-equally-hooky sections whose scores sit within a few percent of
each other. When the top scores are that close, treat the pick as one
reasonable answer among several and look at find_candidates() rather than
assuming a single truth.
from hookfinder import find_hook
hook = find_hook("song.mp3", clip_duration=30)
clip = (hook.start, hook.end)from hookfinder import HookFinder, Weights
finder = HookFinder(
clip_duration=15, # shorter teaser
step=0.5, # finer search
weights=Weights(repetition=0.5, energy=0.3, rhythmic=0.2,
harmonic=0.0, centrality=0.0),
align_to_beat=True,
)
hook = finder.find("song.mp3")finder = HookFinder()
for cand in finder.find_candidates("song.mp3", top_k=3):
print(cand.start, cand.score, cand.components)from hookfinder import extract_from_file, HookFinder
feats = extract_from_file("song.mp3") # analyze audio once
short = HookFinder(clip_duration=15).find(feats)
long = HookFinder(clip_duration=45).find(feats)hookfinder song.mp3 # best hook + breakdown
hookfinder song.mp3 --top 3 # top 3 non-overlapping candidates
hookfinder song.mp3 -d 15 --json # 15s clip, JSON output
hookfinder song.mp3 --quality high # slower, cleaner per-signal sources
hookfinder song.mp3 --export teaser.wav # write the clip to disk
hookfinder song.mp3 --w-repetition 0.6 --w-energy 0.4find_hook(audio, clip_duration=30, sr=None, weights=None, align_to_beat=True, step=1.0, quality="fast") -> HookHookFinder(...).find(audio) -> HookHookFinder(...).find_candidates(audio, top_k=5) -> list[Hook]Hook:.start,.end,.duration,.score,.components,.to_dict()Weights(repetition, harmonic, rhythmic, energy, centrality)— auto-normalizedextract_from_file(path, quality="fast") -> AudioFeatures/extract_features(y, sr, quality="fast") -> AudioFeatures
audio may be a file path, a NumPy waveform (pass sr; stereo is downmixed,
integer PCM is scaled), or a precomputed AudioFeatures. Empty audio, clips
under ~3 seconds, and non-finite samples raise ValueError.
- It's a signal-based heuristic, not a trained model. It finds the salient, repeated, high-energy section — which is usually the hook, but "catchiness" is subjective and it won't always agree with you.
- The top pick is often not a clear winner. On real tracks the leading
candidates frequently score within a few percent of each other; the ranking
between them is not meaningful at that margin. Use
find_candidates()when that matters to you. - Instrumental or through-composed music (no repeating chorus) leans on the other four signals and is inherently harder.
- Beat tracking and recurrence degrade on very short or very noisy audio.
- It does not download audio. Bring your own files. (See
examples/for how a downloader would sit on top of this.)
pip install -e ".[dev]"
pytestProprietary — All Rights Reserved. Source-available for viewing only; not open source. No use, copy, or reuse without written permission. See LICENSE.
