# Changelog of RedGIFs Niche and Keyword Video Fetcher (`webdesigndetail/redgifs-video-fetcher`) Actor

- **URL**: https://apify.com/webdesigndetail/redgifs-video-fetcher/changelog.md
- **Full Actor documentation**: https://apify.com/webdesigndetail/redgifs-video-fetcher.md

## Changelog

All notable changes to this project will be documented in this file.

### \[Unreleased]

#### Added

- Initial Python 3.13 / Apify SDK 4.x Actor structure.
- Authorized niche-first discovery with optional keyword-search fallback.
- Required integrated Apify Proxy configuration and explicit content-rights acknowledgement.
- Bounded, streamed MP4 downloads into deterministic `video-*` key-value records.
- Dataset, key-value store, input, and output schemas with run-summary telemetry.
- Reproducible uv dependency management and a non-root Apify Docker image.
- Restart-safe `RUN_STATE` checkpointing on the `PERSIST_STATE` and `MIGRATING` events, so a
  migrated run resumes without duplicating dataset rows or re-downloading stored media.
- Stable error categories on every summary entry, including a dedicated `target_schema_changed`
  classification that fails a run loudly instead of emitting silently wrong records.
- Sparse discovery, progress, and completion status messages.
- Deployment split into `CI and beta deployment` and a manual `Release latest Actor build`,
  with a one-item metadata smoke on the exact beta build and a manual one-video smoke before that
  same build is promoted to `latest`. Production promotion retags the tested build instead of
  rebuilding source that was never smoke-tested.
- `.actorignore`, so caches, virtualenvs, tests, and local storage are not uploaded to Apify.
- Synthetic sanitized test fixtures plus sticky-session, token-refresh, session-rotation,
  schema-change, budget, MIME-validation, cleanup, and migration-resume tests behind an
  80 percent coverage gate.

#### Changed

- Discovery now tolerates individual keyword failures: a failing keyword is recorded and skipped,
  and only a run in which every keyword fails is fatal.
- Dataset records are published as each item completes instead of after every download finishes,
  so an abort, a timeout, or a migration no longer discards completed work.
- Item processing uses a bounded worker pool rather than creating one task per discovered item.
- Platform video persistence now streams temporary files directly to the authenticated KVS record
  endpoint instead of asking the SDK to read each entire file into memory.

#### Fixed

- The deployment smoke test read `mimeType` and `sizeBytes`, which the Actor never emits; it now
  reads the `mime` and `size` fields that are actually written, so the gate can pass.

- `ruff format --check` failed on the source tree, which made the CI quality job red.

- Input rejections raised by model-level validators rendered as
  `Invalid Actor input: : Value error, ...`, leaking a stray colon and pydantic's internal
  prefix into the Actor log. They now read as plain sentences.

- The Docker build failed outright: the uv cache mount declared `uid=1000,gid=1000`, but `myuser`
  in `apify/actor-python:3.13` is uid/gid 999, so `uv sync` could not write its cache.

- The Apify build server rejected the dataset schema with `no schema with key or ref
  "https://json-schema.org/draft/2020-12/schema"`. That meta-schema is what Apify's own
  documentation shows, but their builder compiles `fields` with an AJV instance that does not
  register it. The embedded `$schema` key is now omitted so AJV falls back to whichever
  meta-schema its build provides, which works before and after they fix the skew.

- Three RedGIFs adapter defects found by the first live run:
  - the search endpoint now receives the term as `search_text` and `type=g`, matching the current
    endpoint contract used by maintained clients;
  - the niche feed and the search endpoint accept different `order` vocabularies (`hot` is
    valid only for niche feeds, `top` only for search), so the public sort option is now
    translated per endpoint instead of forwarded verbatim; and
  - there is no public niche-lookup endpoint, so the niche feed itself is now the existence
    probe. This removes a request per keyword and one dependency on an endpoint that may not
    exist.

- `target_schema_changed` errors now name the response keys that were actually present, so a
  future shape change is diagnosable from the Actor log. Keys only; never values.

- RedGIFs reports some failures as HTTP 200 with an `{"error": {"code", "message"}}` body. That
  was previously misread as a response-shape change, which is fatal and unretryable. Such a
  response is now classified: a not-found error becomes `ResourceNotFound`, so `nicheThenSearch`
  falls back to keyword search as designed, and anything else becomes a normal per-keyword
  failure carrying the source's own code and message. Only those two scalars are surfaced, both
  length bounded, so sibling fields in the body cannot reach a log.

- `NicheMoved` is now a typed condition rather than a generic fatal API error. The Actor follows one
  allowlisted replacement slug when supplied, prevents redirect cycles, falls back to search in
  `nicheThenSearch`, and gives `nicheOnly` users a canonical-slug error.

- Per-keyword warnings now include the sanitized source cause and point to `RUN_SUMMARY`; the final
  all-keywords failure also points operators to that record.

#### Security

- Removed the `X-Session-Id` request header, which disclosed the internal Apify Proxy session
  identifier to the target host on every metadata and media request.

No cloud deployment or Apify Store publication is asserted by this changelog.
