Skip to content

fix: v1.0.1 follow-ups (low-bitrate crash, output_format case, CI) - #9

Merged
blackaxgit merged 3 commits into
mainfrom
fix/v1.0.1-followups
Oct 1, 2026
Merged

blackaxgit merged 3 commits into
mainfrom
fix/v1.0.1-followups

Conversation

@blackaxgit

@blackaxgit blackaxgit commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

Summary

Follow-ups that complete v1.0.1, after merging #7 (python 3.14-slim) and #8 (uvicorn 0.54.0, aiofiles 25.1.0):

Change Why
split_audio: 400 "Unsupported audio: bitrate is below 1 kbps" A bitrate under 1000 bps floored to 0 kbps, and calculate_segment_time raised ZeroDivisionError, giving a 500. Reproduced with an 800 bps WAV (bit_rate=820). The request is now rejected up front, before FFmpeg runs.
output_format is lowercased WAV was rejected with 400, while .WAV uploads were accepted because input extensions are already lowercased. Invalid formats still return 400, quoting the user's own spelling.
.github/workflows/ci.yml (new) The repo had no CI, so Dependabot PRs arrived untested. On PRs and pushes to main, it builds the image, runs it read-only as the Helm chart does, and smoke-tests /health, /ready and /split on a 20 s, 128 kbps tone generated by the image's own FFmpeg (so the runner needs no extra dependencies). Failing requests print their error body.
.github/dependabot.yml Re-adds github-actions, which keeps the workflow's actions/checkout current.
README / CONTRIBUTING Python 3.13 → 3.14, to match the base image. Docs now cover case-insensitive output_format and the new 400.
Chart 0.1.1 / appVersion 1.0.1 Patch release; the chart's default image tag tracks the git tag.

Review rounds

Two rounds, each through three validators: correctness, contracts/security, and regression/operability.

  • Round 1 blocker, found and fixed. The first fix fell back to 320 kbps, which a reviewer proved amplifies work. Segments were sized for 320 kbps while the file was, say, 800 bps: a 1 MiB upload produced 4,001 chunk files before the MAX_CHUNKS check (or 41 tiny chunks when one 10 MB chunk was requested), and 400k files with an 8 bps header. Options weighed:

    Option Outcome
    1 kbps floor Still up to 1,000× on this path
    Exact bps Changes chunk boundaries for every file
    Reject < 1 kbps Chosen: closes this path; no supported codec produces real audio that low
  • Adopted:

    • a 128 kbps CI tone (4 chunks instead of a 1-chunk margin);
    • --fail-with-body;
    • shell: bash on the smoke step (round 2), because GitHub's default bash -e has no pipefail, so curl's exit code in curl | python3 was ignored.
  • Declined:

    • adding regression requests to CI. The agreed scope is a build + smoke gate for dependency bumps.
    • logging the fallback. Moot now that there is no fallback.

Known limitation (pre-existing, not changed here)

Round 2 confirmed that the sub-1 kbps path is closed, but the segment count is still driven by file-declared metadata, and MAX_CHUNKS is only checked after FFmpeg finishes writing. Two crafted inputs reproduce identically on origin/main:

  • an MP3 with a forged Xing frame count inflates the probed bitrate, so a 321 KB upload returned 200 with 235 chunks (about 3 expected);
  • an MKV with sparse timestamps and a patched Duration, or a bitrate ffprobe reports as N/A, wrote 1,180–1,547 files from 863 KB before the 400.

A proper fix bounds output while FFmpeg runs. That is a design change, tracked separately. It is mitigated today by internal-only deployment, FFMPEG_TIMEOUT_SECONDS, and the 2Gi /tmp limit.

Verification (final commit)

  • Golden master (62 observations, Python 3.14, main's pins): 52 are identical. The 10 that differ are all intended:
    • outfmt_upper: 400 → 200;
    • 9 Helm renders: only the chart/version labels and the default image tag 1.0.0 → 1.0.1 changed.
  • Docker (read-only, uid 1000, caps dropped):
    • the 800 bps / 1 MiB WAV returns 400 in 0.16 s with no chunk files written (was 500, or 4,001 files in round 1);
    • output_format=WAV → chunk_000.wav;
    • EXE → 400;
    • /tmp is clean, with no unhandled errors.
  • CI: green on every commit. On 300a7c9 the smoke step runs under bash -e -o pipefail and produces 4 chunks. The workflow passes actionlint and validates against its schema, and so does the dependabot config.
  • Lint and secrets: ruff stays at 2 baseline findings (B008), and gitleaks is clean.

- get_audio_bitrate: a bitrate under 1 kbps floored to 0 and made
  calculate_segment_time divide by zero (500). Fall back to 320 kbps, the
  same default used when ffprobe reports no bitrate.
- output_format is now matched case-insensitively ("WAV" == "wav"), like
  input extensions already are.
- CI: on PRs and pushes to main, build the image, run it read-only as in
  the Helm chart, and smoke /health, /ready and /split. Dependabot keeps the
  workflow's actions current.
- Docs: Python 3.14 (base image moved in #7); output_format case note.
- Chart 0.1.1 / appVersion 1.0.1.
…llback

Review found that falling back to 320 kbps for a bitrate under 1 kbps
amplifies work: segments are sized for 320 kbps while the file is (for
example) 800 bps, so a 1 MiB upload produced 4,001 chunk files before the
MAX_CHUNKS check, or 41 tiny chunks where one 10 MB chunk was asked for.
No real audio in the supported formats is under 1 kbps, so reject it up
front with an explicit 400, before FFmpeg runs.

CI: generate the smoke tone at 128 kbps (4 chunks instead of a 1-chunk
margin) and use --fail-with-body so a failing run shows the error.
GitHub's default run shell is 'bash -e' without pipefail, so curl's exit
code in 'curl | python3' was ignored; the step only failed because an error
body happened to trip the chunk-count assert. 'shell: bash' runs with
-eo pipefail.
@blackaxgit
blackaxgit merged commit de2a6d4 into main Oct 1, 2026
1 of 2 checks passed
@blackaxgit
blackaxgit deleted the fix/v1.0.1-followups branch October 1, 2026 03:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant