# Changelog of Douyin User Post Videos (`opspilot.cc/douyin-user-post-videos`) Actor

- **URL**: https://apify.com/opspilot.cc/douyin-user-post-videos/changelog.md
- **Full Actor documentation**: https://apify.com/opspilot.cc/douyin-user-post-videos.md

## Changelog

### \[0.4] - 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).
- Medians on an even-sized sample returned the upper-middle element, not the
  median. `arr[Math.floor(arr.length / 2)]` yields the larger of the two middle
  values, which biases the number upward — the exact bias a median exists to
  remove. It now averages the two middle values.

### \[0.3] - 2026-10-09

#### Changed

- README: added three sections before Quick start - what you can use it for, who it fits, and who it does **not** fit. The last section now names the Actor's real limits (what it cannot reach, which field is unavailable, which reading of a metric is wrong), so a buyer can tell whether it is the wrong tool before paying.

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

- KV writes and status messages can no longer hang the run. Under
  `LIMITED_PERMISSIONS` these calls do not merely fail — they can return a
  promise that never resolves. Measured on a live run of a sibling Actor:
  results were fetched successfully, then the following `setValue` never
  returned and the run sat for 49 minutes before being aborted with an empty
  dataset and a paid bill, and not one error in the log. `try/catch` cannot
  help there — it catches a rejection, not a promise that never settles. Every
  bookkeeping write now races a 15s timeout and reports the failure as
  non-fatal, so a stuck store costs one summary field instead of the whole run.

### 0.1 (2026-10-05)

- Initial release. One dataset row per Douyin post: description with a caption-less flag, digg/comment/share/collect counts, the For-You recommend count, duration converted from milliseconds, video-vs-image detection, ad-post flag, music title, publish time and a working link, with the source account attached.
- The summary reports, per account: posts collected, the account's own post total and coverage when the lookup provides them, median and total likes, median comments and recommends, video/image split, ad-post count, posts-without-caption count, the five most-liked posts, and a `viewsNote`.
- Input accepts `sec_uid`s, `douyin.com/user/<sec_uid>` profile URLs and numeric `short_id`s (resolved through the provider's profile lookup first, with the resolved nickname carried onto every row), up to 20 accounts and 200 posts each; `newest` or `popular` order.
- Measured quirks handled: all engagement lives in `statistics` (never at the top level) and `statistics.play_count` is always 0 — Douyin does not expose views, so `viewCount` is `null` with `viewsAvailable: false` rather than a false zero; `has_more` is 0/1; paging uses the millisecond `max_cursor` and stops when it does not advance; `collect_stat` sits outside `statistics`; and the short_id profile lookup returns only a minimal 7-field profile (no follower count, signature or post total), which is reported as null with `profileFieldsAvailable` naming what the lookup actually carried.
- Free tier: 5 runs/day (counter persisted in a cross-run named KV store).
