Files
91/91VideoSpider
nianzhibai 39ef2defcc feat(spider91): 流式爬取 + 完成后统一入队 teaser + 封面失败标 failed
三件相关改动,主题都是 spider91 爬虫流程。

1. 流式爬取协议(取代旧的 "Python 凑齐 15 个再交 Go" 模型)

  Python 端 (spider_91porn.py):
    - 新增 --stream-output flag。开启后每解析出一个 video 直链就把
      entry 作为一行 JSON 写到 stdout 并 flush。
    - log() 在 stream 模式下走 stderr,避免污染 stdout JSONL 协议。
    - --output FILE 仍生效,作离线归档用。

  Go 端 (crawler.go):
    - 新 startSpiderTargetNew() 异步启动 cmd,返回 stdout pipe。
    - RunOnce 用 bufio.Scanner 按行读 stdout,每行解析后立即 processOne
      (下载视频 + 封面 + UpsertVideo)。删掉旧 readSpiderOutput / 全 JSON
      文件解析路径。
    - Python stderr 转发到 backend log,前缀 [spider91:py]。

  收益:Python 翻页找下一个 viewkey 与 Go 下载当前视频在时间上重叠,
  最大化每条签名链接 e= 时间窗。今天观察到 Python 77 秒就找完 15 个
  viewkey 全部 emit;如果还像旧模型那样要等 Go 串行下完才开始下一个,
  后面几个的签名很容易过期(之前 8/15 全 EOF 的根因之一)。

2. teaser 在 crawler 完成后统一入队(取代每条入库立即 enqueue)

  - main.go attachSpider91Crawler 不再注入 OnNewVideo callback。
  - main.go runSpider91Crawl 在 Crawler.RunOnce 完成后调一次
    enqueueDriveGeneration(driveID),让所有新视频统一进 teaser worker。
  - 与 nightly Phase 2 的 "等 teaser 队列 idle" 语义自然对齐。
  - 下载阶段不和 ffmpeg 抢 CPU/IO。

3. 网站封面下载失败时显式标 thumbnail_status='failed'

  spider91 drive 的 thumb worker 按设计不处理 spider91 视频(封面应是
  网站原图直接保存)。当网站封面下载失败时,url='' + status='pending'
  会让 enqueueDriveGeneration 的 waitForThumbnailsBeforePreview 因为
  CountVideosNeedingThumbnail > 0 把 teaser 卡死等待循环。

  修复:crawler.go processOne 中 thumb 失败分支显式标 status='failed'
  (CountVideosNeedingThumbnail 条件 status != 'failed' 会排除)。

  今天观察到的现象:187 MB 视频 c2c04fc8602c5396d469 卡在
  '[preview] waiting for 1 thumbnails before teaser generation'
  循环 35 分钟。

测试:
  - crawler_test.go 重构为 buildFakeSpiderScript helper,
    生成支持 --stream-output 的伪 python(其实是 sh),逐行 echo JSON。
  - TestCrawlerRunOnceFullFlow / TestCrawlerThumbDownloadFailureMarksStatusFailed
    通过新 helper 验证流式协议 + thumb fail 闸门。

go test ./... 全绿;线上手动触发 spider91 抓取验证流式行为正确。
2026-05-27 18:48:30 +08:00
..