Files
flan 58b21b3c19 Install fpcalc in the image; pull cut edges inward onto silence
The Dockerfile installed ffmpeg but not libchromaprint-tools, so in any
deploy the fingerprint and discover tiers were inert rather than broken:
fpcalc_available() returned False, the command exited tidily, and a
healthy-looking image silently never matched an ad.

Cutting a real episode (rather than trusting detection metrics) showed a
fingerprint match ends where the ad stops being recognisable, not where the
break ends. One edge sat 2.3s inside the resumed narration and the cut ate
the opening of "It was 405 on the morning of Thursday, June 19, 2014".

Snapping to the nearest silence was tried first and measured worse, 2.3s to
2.88s clipped, because the closest silence was a pause inside the narration.
Direction is the fix: starts only move later, ends only move earlier, so a
span shrinks and never grows and every error leaves a sliver of ad rather
than deleting a sentence.

This only helps where silence exists. On that episode it tightened three of
five edges and left the 2.31s clip untouched, since the ad-to-narration
transition has no detectable pause. That residual is a detection-edge
problem, likely BRIDGE_FRAMES extending a run, and is not solved here.
2026-07-23 21:19:12 +00:00

57 lines
2.3 KiB
Docker

# adscrub: pipeline CLI + feed server in one image.
#
# Default command serves the cleaned feed(s) over HTTP; every pipeline stage is
# also available as a one-shot command, e.g.:
# docker compose run --rm adscrub ingest
# docker compose run --rm adscrub chapters
# docker compose run --rm adscrub transcribe
# docker compose run --rm adscrub cut
#
# Build with --build-arg GPU=1 (or `docker compose -f compose.yaml -f compose.gpu.yaml
# build`) to pull in the cuBLAS/cuDNN extra for faster-whisper's CUDA path — only
# needed on a host that actually passes a GPU through (see CLAUDE.md).
FROM python:3.13-slim
COPY --from=ghcr.io/astral-sh/uv:0.7 /uv /uvx /bin/
WORKDIR /app
ENV UV_LINK_MODE=copy UV_COMPILE_BYTECODE=1
ARG GPU=0
# dependency layer first: rebuilds only when the lockfile changes
COPY pyproject.toml uv.lock README.md ./
RUN --mount=type=cache,target=/root/.cache/uv \
if [ "$GPU" = "1" ]; then uv sync --frozen --no-dev --no-install-project --extra gpu; \
else uv sync --frozen --no-dev --no-install-project; fi
COPY src ./src
RUN --mount=type=cache,target=/root/.cache/uv \
if [ "$GPU" = "1" ]; then uv sync --frozen --no-dev --extra gpu; \
else uv sync --frozen --no-dev; fi
ENV PATH="/app/.venv/bin:$PATH" \
ADSCRUB_DB=/app/data/adscrub.db \
ADSCRUB_DATA_DIR=/app/data
# gosu drops from root to the unprivileged `adscrub` user after the entrypoint fixes
# ownership of /app/data. uid/gid 568 matches TrueNAS SCALE's standard "apps" account,
# same convention as hark/tiltmeter.
# libchromaprint-tools provides fpcalc, which the `fingerprint`/`discover` tiers shell out to.
# Without it those tiers are not broken so much as INERT: fpcalc_available() returns False, the
# command exits with a tidy message, and an image that looks healthy silently never matches an
# ad. A missing system binary is the whole difference between the cheap tiers running and not.
RUN apt-get update && apt-get install -y --no-install-recommends gosu ffmpeg libchromaprint-tools \
&& rm -rf /var/lib/apt/lists/* \
&& groupadd --gid 568 adscrub \
&& useradd --system --uid 568 --gid 568 --no-create-home adscrub
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
VOLUME ["/app/data"]
EXPOSE 8711
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["adscrub", "serve", "--bind", "0.0.0.0:8711"]