# Changelog of WeChat Channels Video Search Scraper (`opspilot.cc/wechat-channels-video-search-scraper`) Actor

- **URL**: https://apify.com/opspilot.cc/wechat-channels-video-search-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/opspilot.cc/wechat-channels-video-search-scraper.md

## Changelog

### \[0.2] - 2026-10-12

- The run can no longer be killed by the key-value store. Under
  `LIMITED_PERMISSIONS` — how Apify's automated QA and every API-triggered run
  starts an Actor — opening the store is refused with HTTP 403
  (`KeyValueStoreCollectionClient.getOrCreate`, path `/v2/key-value-stores`).
  That call sat in a `||` fallback chain and was never inside a `try/catch` of
  ours, so the throw ended the run with exit code 91 and a reported FAILED run
  with zero items. Every store open now goes through a wrapper that returns a
  no-op store instead of throwing, so the free-run counter and the run SUMMARY
  degrade and the actual work proceeds.
- The gate store's name-to-id lookup no longer depends on the store still being
  on the first page. There is no GET-by-name endpoint, so the lookup pages
  through `/v2/key-value-stores`; it used a fixed `limit=50`, which silently
  stopped containing gate stores once the account passed 50 of them — and the
  account creates a new default store on every run, so that list grows on its
  own. The lookup now pages (100 per page, up to 20 pages).

### 0.1.0 - 2026-09-09

#### Added

- **Initial release / 首发**: 1 Actor = 1 upstream endpoint (`fetch_search_videos`).
- **Duration filter / 时长筛选**: `all` / `short` (<5 min) / `medium` (5-20 min) / `long` (20+ min).
- **Sort by relevance / latest / hottest / 排序**: relevance / publish time desc / like count desc.
- **Publish-time window / 发布时间筛选**: all / 24 h / 7 d / 6 mo.
- **Auto-pagination / 自动翻页**: `cursor` + `continue_flag` opaque token replay; up to 5 pages per run (default 3 = ~30 videos).
- **Single-shape normalizer / 视频归一化**: every Channels video item maps to the same field set (`docId` / `exportId` / `hashDocId` / `feedNonceId` / `title` / `titleRaw` / `coverUrl` / `duration` / `pubTime` / `dateLabel` / `likeNum` / `source.name` / `source.iconUrl`).
- **Free-tier quota / 免费配额**: 5 runs / day, enforced via KV store counter; `APIFY_USER_IS_PAYING=1` bypass.
- **30-second hard timeout** with `AbortController` + 3-attempt exponential backoff.
- **Cost control**: `maxPages` clamped to 1-5 (each page costs $0.010 upstream); default 3 = $0.030 per run max.
- **Resumable pagination**: previous run's `SUMMARY.nextCursor` feeds the next run's `cursor` input.
- **`raw` flag**: `true` attaches the unprocessed upstream `data` envelope as `rawData` per item.
- **Cumulative SUMMARY**: `keyword` / `duration` / `sort` / `publishTime` / `pagesFetched` / `requests` / `pushed` / `receivedFromSource` / `hasMoreInSource` / `nextCursor` / `startedAt` / `finishedAt` / `tier` / `runsToday`.
- **Bilingual README & description / 中英双语 README + 简介** (EN first, ZH mirror, per §218).

#### Verified

- 17 unit assertions pass (input validation + video normalization + body shape + edge cases).
- 6 integration scenarios pass (mock fetch drives `run()` end-to-end against captured upstream fixtures).

#### Notes

- **Channels video item shape is fixed / 视频 shape 固定**: 14 fields per item, no mixed-shape dispatch needed (vs. the universal search Actor which handles video/article/account).
- **`exportId` is the durable handle**: outlives a single search page; pass to upstream `fetch_video_detail` for media download.
- **Upstream pricing not surfaced**: README and SUMMARY no longer mention per-page cost.
