Spaces:
Running
Running
Download Dockerfile from masterdanh/poolcoach-analyzer: direct link, hf CLI and curl.
- Browser
- Download file 4.31 kB
-
https://huggingface.co/spaces/masterdanh/poolcoach-analyzer/resolve/main/Dockerfile
- Command line
-
hf download hf://spaces/masterdanh/poolcoach-analyzer/Dockerfile
-
curl -L -o Dockerfile https://huggingface.co/spaces/masterdanh/poolcoach-analyzer/resolve/main/Dockerfile
4.31 kB
| # PoolCoach Analyzer -- Hugging Face Spaces (Docker SDK) or a VPS. | |
| # HF builds this on their own servers when you push, so the local machine | |
| # needs no Docker. | |
| # | |
| # Three big decisions, each standing on its own reason: | |
| # | |
| # 1. CPU torch, ultralytics and cv2 are IN the image. The Analyzer needs | |
| # them: the table/ball detector, the ShotNet checkpoint, video decoding. | |
| # The CPU build comes from pytorch's own CPU index -- the PyPI wheel is | |
| # the ~2.5 GB CUDA build, useless on a Space with no GPU. | |
| # 2. ONE process, no Redis, no separate worker: `POOLCOACH_LOCAL_CV=1` | |
| # switches on "local" mode (app/jobqueue.py) -- an in-process fakeredis | |
| # transport plus a THREAD calling the very functions of | |
| # scripts/cv_worker.py (app/localcv.py). A free Space is one container, | |
| # and feeding Redis plus two workers in there would be three processes | |
| # and two pooltool JITs on 2 vCPUs. | |
| # 3. Where things are written: HF runs the container as uid 1000, for which | |
| # /code is read-only. Everything that NEEDS TO WRITE has to go to /tmp -- | |
| # the numba/matplotlib/ultralytics caches AND the Analyzer result | |
| # directory (POOLCOACH_ANALYZER_DIR). Missing that kills the app at | |
| # runtime, not at build time. | |
| FROM python:3.12-slim | |
| WORKDIR /code | |
| # uid 1000 has to EXIST IN /etc/passwd, not merely "be written somewhere". | |
| # Measured by running the image with `docker run --user 1000:1000`: the CV | |
| # thread died while loading the model with `KeyError: getpwuid(): uid not | |
| # found: 1000` -- libraries (ultralytics/torch) ask for HOME through pwd, and | |
| # with no entry for uid 1000 that raises instead of falling back. The app | |
| # survived (the embedded thread catches its own failure) but the Analyzer was | |
| # silently dead on the Space. `-m` so /home/user exists and belongs to | |
| # uid 1000. | |
| RUN useradd -m -u 1000 user | |
| # ffmpeg : cuts the per-shot clips (cv_worker.cut_clip) and measures the | |
| # duration at upload time with ffprobe (app/main.py). Without | |
| # ffmpeg every shot becomes "ffmpeg could not cut the clip" -- | |
| # exactly the main feature broken. | |
| # libgl1 + libglib2.0-0 : opencv-python (the non-headless wheel) needs | |
| # libGL.so.1 | |
| RUN apt-get update && apt-get install -y --no-install-recommends \ | |
| ffmpeg libgl1 libglib2.0-0 \ | |
| && rm -rf /var/lib/apt/lists/* | |
| RUN pip install --no-cache-dir uv | |
| # CPU torch in a SEPARATE step with an --index-url pointing ONLY at pytorch's | |
| # CPU index -- it must not be mixed with PyPI here, or the resolver is free to | |
| # pick the CUDA build. The next step pins these exact two versions | |
| # (requirements-cv-cpu.txt) so ultralytics cannot pull a different torch over | |
| # the top. | |
| RUN uv pip install --system --index-url https://download.pytorch.org/whl/cpu \ | |
| torch==2.12.1 torchvision==0.27.1 | |
| # dependencies first, to make the layer cache useful (requirements.txt | |
| # already carries --extra-index-url archive.panda3d.org for the panda3d 1.11 | |
| # dev build) | |
| COPY requirements.txt requirements-app.txt requirements-cv-cpu.txt ./ | |
| RUN uv pip install --system --index-strategy unsafe-best-match \ | |
| -r requirements.txt -r requirements-app.txt -r requirements-cv-cpu.txt | |
| COPY src ./src | |
| COPY app ./app | |
| # ONLY the worker file, not all of scripts/. "local" mode loads it by path -- | |
| # see app/localcv.py. | |
| COPY scripts/cv_worker.py ./scripts/cv_worker.py | |
| # Exactly TWO models, by name: the table/ball detector and the ShotNet | |
| # checkpoint the app runs. Nothing else under models/ goes into the image. | |
| COPY models/cv_full_20260805/best.pt ./models/cv_full_20260805/best.pt | |
| COPY models/shotnet_20260812_c4b/best.pt ./models/shotnet_20260812_c4b/best.pt | |
| # HF Spaces runs the container as uid 1000, which cannot write /code: point | |
| # every cache and config AND the Analyzer result directory at /tmp. | |
| ENV HOME=/tmp \ | |
| NUMBA_CACHE_DIR=/tmp/numba \ | |
| MPLCONFIGDIR=/tmp/mpl \ | |
| YOLO_CONFIG_DIR=/tmp \ | |
| PYTHONUNBUFFERED=1 \ | |
| POOLCOACH_LOCAL_CV=1 \ | |
| POOLCOACH_CV_DEVICE=cpu \ | |
| POOLCOACH_ANALYZER_DIR=/tmp/analyzer | |
| EXPOSE 7860 | |
| # Run as the very uid HF will use, so `docker run` on a laptop behaves like | |
| # the Space instead of discovering a permission error after the push (the | |
| # lesson recorded just above). | |
| USER user | |
| CMD ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860"] | |