From fe37c037d45025927867ebab9aee1c1a99f01098 Mon Sep 17 00:00:00 2001 From: tcpsyn Date: Sat, 15 Aug 2026 17:05:10 -0500 Subject: [PATCH] Fix NAS stats returning zeros instead of real download numbers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two independent bugs, either of which made gather_castopod() report total_downloads: 0 on a dev machine — indistinguishable from a podcast nobody listens to. - NAS_SSH hardcoded mmgnas-10g, which is only reachable when the 10G cable is physically connected. Now probed once per process with a 2s timeout and falls back to mmgnas; NAS_HOST overrides detection entirely. Same detection added to deploy_stats_cron.sh. - _run_db_query treated any docker binary on PATH as "running on the NAS", so a machine with Docker Desktop queried its own daemon, found no castopod container, and never fell back to SSH. Only the QNAP container-station path counts now. Real figures with both fixed: 3803 downloads, 750 unique listeners. Co-Authored-By: Claude Opus 5 --- deploy_stats_cron.sh | 14 ++++++++++++- podcast_stats.py | 49 +++++++++++++++++++++++++++++++++++++------- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/deploy_stats_cron.sh b/deploy_stats_cron.sh index 5d15e49..5687577 100755 --- a/deploy_stats_cron.sh +++ b/deploy_stats_cron.sh @@ -5,9 +5,21 @@ set -e -NAS_HOST="mmgnas-10g" NAS_USER="luke" NAS_PORT="8001" + +# mmgnas-10g is only up when the 10G cable is physically connected. Prefer it +# when reachable (faster transfers), otherwise fall back to the wireless/1G +# host. Override with NAS_HOST=... to skip detection. +if [ -z "$NAS_HOST" ]; then + if ssh -p "$NAS_PORT" -o ConnectTimeout=2 -o BatchMode=yes \ + "$NAS_USER@mmgnas-10g" true 2>/dev/null; then + NAS_HOST="mmgnas-10g" + else + NAS_HOST="mmgnas" + fi + echo "NAS host: $NAS_HOST" +fi DOCKER_BIN="/share/CACHEDEV1_DATA/.qpkg/container-station/bin/docker" DEPLOY_DIR="/share/CACHEDEV1_DATA/podcast-stats" CONTAINER_NAME="podcast-stats" diff --git a/podcast_stats.py b/podcast_stats.py index 835b0c7..57e5991 100644 --- a/podcast_stats.py +++ b/podcast_stats.py @@ -29,8 +29,42 @@ YOUTUBE_PLAYLIST = "PLGq4uZyNV1yYH_rcitTTPVysPbC6-7pe-" APPLE_PODCAST_ID = "1875205848" APPLE_STOREFRONTS = ["us", "gb", "ca", "au"] SPOTIFY_SHOW_ID = "0ZrpMigG1fo0CCN7F4YmuF" -NAS_SSH = "luke@mmgnas-10g" NAS_SSH_PORT = "8001" +NAS_USER = "luke" +NAS_HOST_WIRED = "mmgnas-10g" # only reachable when the 10G cable is plugged in +NAS_HOST_DEFAULT = "mmgnas" # wireless/1G — always available +_nas_host_cache = None + + +def resolve_nas_host(): + """Prefer the 10G wired host, fall back to the always-available one. + + mmgnas-10g is only up when the cable is physically connected, so pinning it + made every NAS read fail silently on wifi — gather_castopod() returned zeros + that looked like real download numbers. Probed once per process and cached. + Set NAS_HOST to skip detection entirely. + """ + global _nas_host_cache + override = os.getenv("NAS_HOST") + if override: + return override + if _nas_host_cache: + return _nas_host_cache + try: + probe = subprocess.run( + ["ssh", "-p", NAS_SSH_PORT, "-o", "ConnectTimeout=2", "-o", "BatchMode=yes", + f"{NAS_USER}@{NAS_HOST_WIRED}", "true"], + capture_output=True, timeout=10, + ) + reachable = probe.returncode == 0 + except (subprocess.TimeoutExpired, OSError): + reachable = False + _nas_host_cache = NAS_HOST_WIRED if reachable else NAS_HOST_DEFAULT + return _nas_host_cache + + +def nas_ssh_target(): + return f"{NAS_USER}@{resolve_nas_host()}" DOCKER_BIN = "/share/CACHEDEV1_DATA/.qpkg/container-station/bin/docker" CASTOPOD_DB_CONTAINER = "castopod-mariadb-1" @@ -239,11 +273,12 @@ def gather_youtube(include_comments=False): def _run_db_query(sql): # If running on NAS (docker socket available), exec directly - docker_bin = None - for path in [DOCKER_BIN, "/usr/bin/docker", "/usr/local/bin/docker"]: - if os.path.exists(path): - docker_bin = path - break + # Only the QNAP container-station path means "we are running ON the NAS". + # Probing generic docker paths broke this on any dev machine with Docker + # Desktop installed: /usr/local/bin/docker exists, so this took the local + # branch, queried the wrong daemon, and returned zeros that looked like real + # download numbers instead of falling back to SSH. + docker_bin = DOCKER_BIN if os.path.exists(DOCKER_BIN) else None db_pass = os.getenv("CASTOPOD_DB_PASS", "") if docker_bin: @@ -253,7 +288,7 @@ def _run_db_query(sql): "mysql", "-u", "castopod", "castopod", "-N"] else: cmd = [ - "ssh", "-p", NAS_SSH_PORT, NAS_SSH, + "ssh", "-p", NAS_SSH_PORT, nas_ssh_target(), f"{DOCKER_BIN} exec -i -e MYSQL_PWD={db_pass} {CASTOPOD_DB_CONTAINER} mysql -u castopod castopod -N" ] try: