# content-harvester 内容采集服务。给一个来源页 URL,用真实有头浏览器逐个打开内容详情页,抓出每条内容的元数据(标题 / 详情页 URL / 封面图 / 媒体地址);若调用方下发了对象存储(MinIO/S3)配置,顺带把媒体拉下来、连同封面一起上传,把媒体地址换成对象存储上的稳定 URL(很多站点的媒体地址带 `expires` 签名、一小时左右就失效)。结果以 `resources` 列表回传。 支持两类内容:**视频**(m3u8 → yt-dlp+ffmpeg 转 mp4 上传,`source_type=video`)和 **图集**(套图详情页里成百上千张图,逐张下载上传,`source_type=image`、`content_type=article`、回传 `images[]` 数组——主站对 `images` 数组会自动跑图片 OCR 审核)。按 `content_url` 路径(`/videos/`·`/video/` ↔ 视频,`/photos/`·`/photo/` ↔ 图集)或载荷里的 `media_kind` 自动选 scraper。 主要给「启源 AI 平台」主站调用:主站「多内容数据采集」任务里来源大类选「内容网站」时,把作业 POST 给本服务,本服务返回 `resources`,主站写进资源库。 当前内置 **xchina.co** 的视频 scraper(`core/scraper.py`)和图集 scraper(`core/scraper_photos.py`);接别的来源就再加一个 scraper 模块。 > 前身是个 Tkinter GUI 视频下载器,已废弃改造成本服务。详细设计见 `PRD-minio-upload.md`,实现速查见 `CLAUDE.md`。 --- ## 1. 功能 **采集流程(一次作业)** 1. 收到 `POST /agents/{id}/invoke`(载荷来自主站),校验 token、`source_kind`、`content_url` host 白名单,从载荷取 MinIO 配置,生成 `job_id`,**立即返回** `{"task_id": job_id, "status":"running"}`,作业转后台异步跑(进程内单作业串行,防风控)。 2. 按 `content_url` 路径(或载荷 `media_kind`)选 scraper:视频(`/videos/`·`/video/`)走 `XChinaScraper`,图集(`/photos/`·`/photo/`)走 `XChinaPhotoScraper`。用有头 Chromium(容器里靠 Xvfb 提供虚拟显示)打开来源页: - 系列页 → 取页内卡片(视频:`.item.video`;图集:`.item.photo`)里的详情链接,按 `start_page..end_page` 翻页(默认只爬给定页); - 详情页 → 直接采这一条。 3. 逐个打开详情页:处理广告中转页;遇 Cloudflare JS 质询页等它自动放行(最多 ~24s);抓 **标题**、**简介/正文**(实在没有就用标题——主站资源库对空正文会拒收)、**封面图 URL**: - **视频**:简介=meta/og/jsonld 描述 + 详情面板文本 + 标签;封面=截屏 tab 图 → og:image/JSON-LD thumbnail/`