Build Swell AI

YESreplaces $17/mosaves $204/yrback to the verdict

0%0 of 18 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A local podcast production tool whose hard problems are editing without ever rewriting the source recording, hitting a published loudness target measured on the file you actually ship, and generating notes that can be traced back to the words that were said.

estimated effort multi-daythe files for this build are in the project pack

RuntimePython 3.12 with FastAPI and an HTMX interfaceAudioffmpeg for decode, filtering, loudness measurement and encodeStorageSQLite for metadata, a read-only media folder for sources, a renders folder for outputEditingAn ordered edit list applied at render, never applied to the sourceTranscriptionA local model by default, an API key optional and degraded cleanly without one

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the interface and the processing.

    Get it python.org, or your package manager. open ↗

    Verify python3 --version prints 3.12 or higher

  2. installfree

    Why Decode, filtering, loudness measurement and encoding all come from here, and the measurement and the encode agreeing matters more than either alone.

    Get it Your package manager or a static build. open ↗

    Verify ffmpeg -filters lists the loudness normalisation filter and -encoders lists your target codec

  3. have readyfree, plus the disk and the time

    Why An hour of audio per episode through a hosted service is a recurring bill and a copy of every conversation leaving the machine.

    Get it Download a speech recognition model sized for your hardware and confirm it runs on a short clip.

    Verify A one minute clip transcribes with timings

  4. API keyper minute of audio or per thousand tokens; check the current rate

    Why Only if you want higher quality than the local model gives; every phase must work without it.

    Get it Create a key with the narrowest scope your provider offers.

    Verify A short clip transcribes through the API and the local path still works when the key is removed

  5. have readyfree

    Why Loudness, chunk stitching and gating all behave differently on a quiet remote guest than on a clean studio track, and a clean fixture proves nothing.

    Get it One studio recording, one noisy remote call, and one already mastered episode.

    Verify All three import and render within the stated tolerance

  6. decidefree

    Why The figures are asserted by tests and the published guidance changes, so the version you built against needs recording.

    Get it Read the current audio requirements of the directories you publish to and write down the figures and the date. open ↗

    Verify The README names the figures, the source and the date

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, source_sha256, duration_seconds, sample_rate, channels, imported_at) · source_path is inside the read-only folder
- edits(episode_id, ordinal, kind, at_seconds, to_seconds, params_json) · kind is cut, fade, gain, gate or marker, and applying them produces a new file
- renders(id, episode_id, edit_sha256, target_lufs, measured_lufs, true_peak_db, format, bitrate, channels, path, rendered_at)
- transcripts(id, episode_id, engine, engine_version, language, created_at)
- segments(transcript_id, ordinal, start_seconds, end_seconds, speaker, text, confidence) · timings survive a manual correction
- chapters(episode_id, start_seconds, title, origin) · origin is manual or derived, and derived ones carry the segments they came from
- drafts(id, episode_id, kind, engine, body, citations_json, approved_at, approved_by) · citations_json maps each passage to segment ids

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.

VariableNeededExampleWhere the value comes from
MEDIA_DIRrequired./mediaRead-only sources. Nothing in the application writes here.
RENDERS_DIRrequired./rendersEverything the tool produces. The only folder retention can reach.
DATABASE_PATHrequired./data/podcast.dbEpisodes, edits, transcripts, renders and drafts.
TARGET_LUFSrequired-16Taken from the directory's published requirements, with the date recorded.
TRUE_PEAK_CEILING_DBrequired-1Measured on the encoded file, not on the intermediate.
RENDER_RETENTION_DAYSrequired60Applies to renders only. Sources are never deleted by any path.
MAX_RENDERS_GBrequired50A render that would exceed this is refused before it starts.
TRANSCRIPTION_API_KEYsecretoptionalsk-...redactedOptional. Without it the local model is used and the interface says so.

The build, in order

  1. Originals that cannot be damaged

    Importing a recording makes it read-only and every later operation is additive.

    1. The filesystem is a better guarantee than a code review.

      Files app/media/import.pytests/test_source_untouched.py

    2. An edit list can be undone; an overwritten file cannot.

      Files app/edits/model.py

    3. It catches the one path that got it wrong, whenever that appears.

      Files app/render/guard.py

    done when · tick each as it passes
    watch out
    • Normalising or trimming in place because it is one ffmpeg command overwrites the only copy of a two hour interview, and the failure is silent until somebody asks for the unedited version. Import into a folder nothing writes to, keep edits as data, and make the render read the source and write elsewhere.
  2. A transcript with timings you can click

    Every word has a time, so notes and chapters can point back at the audio.

    1. Fixed boundaries with no overlap duplicate or lose a sentence every time.

      Files app/transcribe/chunk.pytests/test_stitch.py

    2. The timings are what make every later feature possible.

      Files app/transcribe/edit.py

    3. Two transcripts of one episode are only comparable if you know what made them.

      Files app/transcribe/model.py

    done when · tick each as it passes
    watch out
    • Chunking an hour of audio at fixed boundaries with no overlap cuts sentences in half, and stitching by concatenation then duplicates the words either side of every boundary, which the person only notices when the show notes quote a phrase twice. Overlap the chunks, match the repeated region when stitching, and test with a recording long enough to need several.
  3. Loudness measured on the file you ship

    The exported episode meets a published target, verified after encoding rather than before.

    1. The encoded file is the only file anyone will hear.

      Files app/render/loudness.pytests/test_loudness.py

    2. A claim without a measurement is a claim.

      Files app/render/model.py

    3. The published figures change, and the README is where the next person looks.

      Files docs/audio-targets.md

    done when · tick each as it passes
    watch out
    • Measuring loudness on the normalised audio and then encoding to a lossy format ships a file whose true peak is above the ceiling, because the encoder reconstructs a waveform that overshoots the samples it was given; a single loudness pass also gets the target wrong because it cannot see the whole file. Use a two pass measurement, encode, then measure the encoded file and store what you measured.
  4. Notes that point at what was actually said

    Every generated passage can be traced to timestamps in the transcript and nothing ships unread.

    1. It is the only cheap defence against a confident invention.

      Files app/drafts/generate.pytests/test_citations.py

    2. The approval is the point at which a person takes responsibility.

      Files app/export/gate.py

    3. A tool that needs a key to be useful is a tool with a monthly bill.

      Files app/drafts/extract.py

    done when · tick each as it passes
    watch out
    • A model given only a rough transcript will confidently supply a guest's employer, a round number and a neat closing quotation, none of which were said, and the host publishes it because it reads well. Require citations per passage, show them beside the draft, refuse to export an unapproved draft, and treat anything with no citation as something to delete rather than to trust.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more podcasting.