Build Podsqueeze
YESreplaces $8.99/mosaves $107.88/yrback to the verdict
A local episode assistant whose hard problems are keeping every timestamp on the original file's timeline, transcribing a long recording without mangling a word at each chunk boundary, and exporting at the loudness and in the formats the platforms actually publish.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs the jobs, the transcription interface and the interface.
Get it python.org, or your package manager. open ↗
Verify
python3 --version prints 3.12 or higher - installfree
Why Decoding, silence detection, clip rendering, loudness measurement and encoding all go through it, so a cut-down build will fail halfway through the pipeline.
Get it Your package manager, or a static build for your platform. open ↗
Verify
A loudness measurement and a silence detection both run on a sample file - installfree locally, pennies per hour hosted
Why Segment-level timings are not enough: chapters, clips and captions all need word boundaries.
Get it A local speech model with word timestamps, or an API that returns them.
Verify
A one minute sample returns per-word start and end times - have readyfree
Why Chunk boundaries, drift and loudness only misbehave on a long recording with real silences, and a three minute sample proves nothing.
Get it Your own recordings, one over ninety minutes, one with two speakers.
Verify
Both import, and the long one crosses several chunk boundaries - installfree
Why The only way to know chapters and transcripts exported correctly is to open them somewhere you did not write.
Get it Any mature player that displays chapter markers and loads a caption file.
Verify
A known good file shows its chapters and captions in it - decidefree
Why These are published numbers that change, and the build asserts them, so where and when you read them has to be written down.
Get it Read the platform's current audio and chapter guidance, note the figures and the date, and record both in the repository. open ↗
Verify
The figures appear in configuration and the source and date are in the README
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- episodes(id, title, source_path, sha256, duration_s, sample_rate, channels, container, imported_at) · the source path is read and never written - jobs(id, episode_id, kind, state, attempts, started_at, finished_at, error) · transcribe, analyse, render - words(episode_id, ordinal, text, start_s, end_s, speaker, confidence) · times on the original's timeline, always, with no exceptions - segments(episode_id, ordinal, start_s, end_s, speaker, text) · derived from words and regenerable at any time - chapters(episode_id, ordinal, start_s, title, source) · source is auto or edited, and an edit is never overwritten by a regeneration - artefacts(episode_id, kind, model, prompt_version, generated_body, edited_body, generated_at) · title, description, keywords and social posts, each separately regenerable - clips(id, episode_id, start_s, end_s, reason, rendered_path, integrated_lufs, true_peak_dbtp) - loudness(episode_id, stage, integrated_lufs, true_peak_dbtp, measured_at) · stage is source, rendered or encoded, because encoding moves the peak
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
DATA_DIR | required | ./data | Database, working copies and rendered output. Never the masters. |
MEDIA_SOURCE_DIR | required | ~/Recordings | Read only. The suite hashes everything here after every command. |
TRANSCRIBE_BACKEND | required | local | Local model or API. Both return word-level times through the same interface. |
TRANSCRIBE_API_KEYsecret | optional | sk-...redacted | Only for the API backend. Audio is uploaded there, so choose deliberately. |
TARGET_LUFS | required | -16 | Copied from the platform's current published figure, with the date recorded in the README. |
TRUE_PEAK_CEILING_DBTP | required | -1 | Also published. Measured on the encoded file, not the intermediate. |
CHUNK_OVERLAP_SECONDS | required | 5 | The overlap that lets the stitcher match words instead of trusting a boundary. |
WORKDIR_RETENTION_DAYS | required | 14 | Working copies are large and regenerable, so they go first. |
The build, in order
Import that never touches the master
The original is read, hashed and left alone, and every timestamp refers to it.
The one promise about the master, tested rather than stated.
Files
src/import.pytests/test_master_untouched.pySeparate directory, separate lifetime, separate retention.
Files
src/media/decode.pyEvery downstream timestamp depends on it.
Files
src/media/timeline.py
done when · tick each as it passeswatch out- Trimming silence from the working copy before transcription looks like an obvious saving, and it shifts every word time relative to the file you will actually publish, so chapters drift progressively later through the episode and nobody notices until a listener mentions that the markers are off by a minute near the end. Keep one timeline, the original's, and if you must trim for processing then keep the mapping back.
A transcript with word times you can trust
An hour of audio transcribes without a mangled word at every chunk boundary.
Fixed boundaries land in the middle of words, every time.
Files
src/transcribe/chunk.pytests/test_boundaries.pyThe overlap exists so the join can be checked rather than trusted.
Files
src/transcribe/stitch.pyThe low confidence minute is the one that ends up quoted.
Files
src/ui/transcript.html
done when · tick each as it passeswatch out- Splitting an hour of audio into fixed ten minute chunks cuts a word in half at each boundary and produces a mangled token and a timing gap in six places, and because the transcript reads perfectly everywhere else those six places go straight into the show notes. Split on detected silence, overlap the chunks, and de-duplicate the overlap by matching words.
Chapters and notes drawn only from the transcript
Every chapter starts at a moment that actually exists in the audio.
A chapter that lands mid-word is worse than no chapter.
Files
src/chapters/propose.pytests/test_chapter_times.pyCounts, lengths and title limits are published and they change.
Files
docs/chapter-guidance.mdRegenerating the description must not eat an edited title.
Files
src/artefacts/store.py
done when · tick each as it passeswatch out- A model asked for chapters returns plausible timestamps that correspond to nothing in the audio, usually suspiciously round numbers, and a listener pressing a chapter lands mid-sentence in the middle of a different topic. Every chapter start must be an actual word start from the transcript, snapped to a silence, and a proposal the audio cannot support is dropped rather than rounded into place.
Clips that begin and end on a sentence
A rendered clip sounds like something a person cut.
Then fade, so the cut is inaudible rather than merely short.
Files
src/clips/boundaries.pytests/test_clip_edges.pyThe working copy exists for transcription, not for output.
Files
src/clips/render.pyWith the reason, so the list is worth reading.
Files
src/clips/propose.py
done when · tick each as it passeswatch out- Cutting at the exact start and end times of a quote clips the first consonant and swallows the tail of the last word, so every clip is amateurish in precisely the same way and it is never obvious why. Expand the boundaries to the silences on either side, add a short fade, and check the rendered file rather than the plan.
Export that a host will acceptproduct builder
The files leave in the formats the platforms publish and at the loudness they ask for.
Peak normalisation is why an episode sounds quiet next to everything else.
Files
src/export/loudness.pytests/test_loudness.pyThe encoder is what pushes the true peak over the ceiling.
Files
src/export/encode.pyFile metadata and a feed document, checked in a second player.
Files
src/export/chapters.pydocs/verification.md
done when · tick each as it passeswatch out- Normalising to a peak of zero rather than to an integrated loudness target leaves the episode noticeably quieter than everything else in a listener's queue, and the encoder can then push the true peak above the ceiling and clip, which is exactly why the published guidance says to precondition before encoding. Measure integrated loudness and true peak with a conforming meter, target the current published figures, and measure the encoded file rather than the intermediate.
That is the whole plan for Podsqueeze. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Remote recording with guests.
- Hosting, the feed and directory analytics.
- Licensed music and voice cloning.
- Video clips with burned-in captions.
- hosted transcription, media clipping, templates, and publishing convenience
- remote studio reliability
- licensed music libraries
- hosting distribution
- advanced mastering and support
- A watched folder that imports a new recording as it lands, which the jobs table already supports
- Speaker naming carried across episodes, once speaker labels have proved stable on two-speaker recordings
- A per-episode diff of the generated text against your edits, which is the fastest way to improve the prompts
Need the files? The project pack on the verdict page hands your agent the whole brief · more podcasting.