Skip to content

Releases: Evil0ctal/Douyin_TikTok_Download_API

v5.1.3

Choose a tag to compare

@Evil0ctal Evil0ctal released this 02 Oct 00:45

What's new

Short links now work on the per-platform endpoints, not only on
/api/v1/parse.
GET /api/v1/douyin/video?url=https://v.douyin.com/... was
accepted and then failed with "missing required parameter(s) for
douyin.content_detail: content_id", although the endpoint's own documentation
listed v.douyin.com links as supported
(#767).
Comments, comment replies and every author endpoint failed the same way, on
both platforms. The release also carries this month's dependency updates,
among them a PyJWT update that clears thirteen advisories an image scanner
may be reporting; dtk itself never loads PyJWT.

Upgrading

cd /opt/dtk && git pull
# set DTK_IMAGE_TAG=5.1.3 in .env — no `v`, see the tag table at the bottom
export COMPOSE_ENV_FILES=.env   # without this, `image:` ignores the root .env
docker compose -p dtk -f docker/compose.yml pull api worker downloader
docker compose -p dtk -f docker/compose.yml run --rm migrate
docker compose -p dtk -f docker/compose.yml up -d

Or run the installer again and pick Upgrade:

bash install/install.sh --manage

Your data is untouched. No new migrations in this release.

Changed

  • Dependencies updated. On the server: SQLAlchemy 2.1, uvicorn 0.54,
    Alembic 1.20 and wreq 0.12.3. In the console: React 19.3, react-i18next 17
    and TanStack Query 5.104. Nothing to do: the images carry them, and a source
    checkout picks them up with uv sync and npm ci.

Fixed

  • A link in url= on the per-platform endpoints is followed when it has to
    be.
    A short link (v.douyin.com/…, vm.tiktok.com/…) sent to
    /api/v1/{platform}/video, its comments and comments/replies, or any
    author endpoint is now expanded by the worker, through the same egress as
    /api/v1/parse, and the post or author behind it is fetched. A link to the
    wrong kind of thing, such as a profile link sent to /video, now fails with
    INVALID_PARAM and details.reason set to wrong_resource, with
    details.resource and details.expected saying what the link was and what
    the endpoint wanted, instead of reporting a missing id. Nothing to do.
    Reported by @duiaic — thank you.

Security

  • PyJWT 2.13.0 → 2.15.0. Clears thirteen advisories against PyJWT, among
    them GHSA-ffc3-869f-jxw9 (critical) and five rated high. dtk is not affected
    by any of them: PyJWT arrives as a dependency of the mcp package, which uses
    it only in its OAuth client code, and neither the API, the worker nor the MCP
    server loads it. Upgrading stops image scanners from flagging it; there is
    nothing else to do.

本次更新

短链接现在在分平台接口上也能用了,不再只限于 /api/v1/parse。
GET /api/v1/douyin/video?url=https://v.douyin.com/... 会被接受,然后失败,报
"missing required parameter(s) for douyin.content_detail: content_id"——而这个接口
自己的文档写着支持 v.douyin.com 链接
(#767)。
评论、评论回复和所有作者接口在两个平台上都以同样的方式失败。这个版本还带上了本月的
依赖更新,其中 PyJWT 的更新会消除镜像扫描器可能报出的十三条安全公告;dtk 本身从不加载
PyJWT。

升级方式

cd /opt/dtk && git pull
# 在 .env 里把 DTK_IMAGE_TAG 改成 5.1.3 —— 不带 v,见文末标签表
export COMPOSE_ENV_FILES=.env   # 不加这句,image: 的插值读不到根目录的 .env
docker compose -p dtk -f docker/compose.yml pull api worker downloader
docker compose -p dtk -f docker/compose.yml run --rm migrate
docker compose -p dtk -f docker/compose.yml up -d

或者重新运行安装脚本并选择「升级」:

bash install/install.sh --manage

数据不受影响。这个版本没有新增数据库迁移。

变更

  • 依赖更新。 服务端:SQLAlchemy 2.1、uvicorn 0.54、Alembic 1.20 和 wreq 0.12.3。
    控制台:React 19.3、react-i18next 17 和 TanStack Query 5.104。不用做任何事:镜像里
    已经带上了;源码部署运行 uv sync 和 npm ci 即可。

修复

  • 分平台接口 url= 里的链接,需要跟随时会被跟随。 发给 /api/v1/{platform}/video、
    它的 comments 和 comments/replies,或任何作者接口的短链接(v.douyin.com/…、
    vm.tiktok.com/…),现在由 worker 展开,走的出口和 /api/v1/parse 相同,然后取回它
    背后的作品或作者。指向错误类型的链接,比如把主页链接发给 /video,现在返回
    INVALID_PARAM,details.reason 为 wrong_resource,并由 details.resource 和
    details.expected 写明链接是什么、接口要的是什么,而不是报缺少 id。不用做任何事。
    感谢 @duiaic 的反馈。

安全

  • PyJWT 2.13.0 → 2.15.0。 消除了针对 PyJWT 的十三条安全公告,其中
    GHSA-ffc3-869f-jxw9 为严重(critical),另有五条为高危。dtk 不受其中任何一条影响:
    PyJWT 是作为 mcp 包的依赖被装进来的,mcp 只在它的 OAuth 客户端代码里用到它,而
    API、worker 和 MCP 服务端都不会加载它。升级之后镜像扫描器就不会再报它;除此之外不用
    做任何事。

Tags / 标签

Git tag / Git 标签 v5.1.3
DTK_IMAGE_TAG — this release / 这个版本 5.1.3
DTK_IMAGE_TAG — this exact build / 钉死这次构建 sha-31d56df1462e99f24621eb2c6f060af183bf24fd

The Docker tag drops the v: docker/metadata-action strips it when publishing,
so :v5.1.3 was never pushed. One value names both published images. latest
and 5.1 move; pin sha- in production.

Docker 标签不带 v:docker/metadata-action 在发布时会把它剥掉,所以 :v5.1.3
这个标签从来没有被推送过。同一个值同时对应两个已发布镜像。latest 和 5.1 会移动,
生产环境请钉 sha-。

Images / 镜像: evil0ctal/douyin_tiktok_download_api · evil0ctal/douyin_tiktok_download_api-downloader

Full changelog / 完整提交记录: v5.1.2...v5.1.3

v5.1.2

Choose a tag to compare

@Evil0ctal Evil0ctal released this 28 Sep 18:50

What's new

A security release: a caller-supplied ?proxy= could reach the instance's own
network.
On instances where an administrator had set security.request_proxy
to public, the check that keeps a caller's proxy on public addresses only read
the hostname and never looked it up, so a name that resolves to an internal
address got through
(GHSA-q3h8-73xx-gwqx).
The proxy's name is now resolved, every address it answers with must be public,
and the request goes to the address that was checked. If you never changed
security.request_proxy from its default deny, you were not affected. The
repository also gains a NOTICE file
(#766).

Upgrading

cd /opt/dtk && git pull
# set DTK_IMAGE_TAG=5.1.2 in .env — no `v`, see the tag table at the bottom
export COMPOSE_ENV_FILES=.env   # without this, `image:` ignores the root .env
docker compose -p dtk -f docker/compose.yml pull api worker downloader
docker compose -p dtk -f docker/compose.yml run --rm migrate
docker compose -p dtk -f docker/compose.yml up -d

Or run the installer again and pick Upgrade:

bash install/install.sh --manage

Your data is untouched. No new migrations in this release. If you run with
security.request_proxy set to public and cannot upgrade right away, set it
to deny until you can.

Added

  • A NOTICE file with the project's copyright notice. The README asked
    anyone redistributing the project to keep "the copyright notice", and the
    repository did not have one. Nothing to do; if you redistribute the project,
    keep NOTICE next to LICENSE.

Changed

  • In public mode, a ?proxy= given by name is resolved and pinned. Every
    address the name resolves to must be public, and the request is sent to the
    address that was checked rather than to the name. An https proxy keeps its
    name, because its certificate is checked against it. Two new refusal reasons
    come with this: host_unresolvable for a name that does not resolve within
    five seconds, and authority_invalid for a host, port or credentials
    containing characters a URL does not allow there. If your proxy's username or
    password contains such characters, percent-encode them. any mode is
    unchanged.
  • Hostnames that spell a private address with hyphens are refused
    everywhere hosts are checked.
    A name like app-10-0-0-1.example.com now
    counts as private for task callbacks, notification URLs and the URL allowlist,
    just as 10.0.0.1 itself always did. A name spelling a public address is
    unaffected. Nothing to do unless one of your callback or notification URLs has
    such a name.

Security

  • SSRF through ?proxy= in public mode (GHSA-q3h8-73xx-gwqx). Affects
    5.0.0 through 5.1.1, only when security.request_proxy is public. Anyone
    holding an API key with read access could name a proxy whose hostname
    resolves to loopback, a private range or a cloud metadata address, and the
    worker would connect to it. The hostname is now resolved and every answer
    checked before the request is accepted, and what is passed on is the checked
    address, so it cannot be looked up again and answer differently. Upgrade; until
    then, set security.request_proxy to deny. Instances on deny (the default)
    or any are not affected by this change. Reported by
    @xiao1212998 — thank you.

本次更新

这是一个安全更新:调用方传入的 ?proxy= 可以访问到实例自己所在的网络。 在管理员把
security.request_proxy 设成 public 的实例上,用来保证调用方代理只能是公网地址的检查
只看主机名的字面、从不解析它,所以一个解析到内网地址的域名能直接通过
(GHSA-q3h8-73xx-gwqx)。
现在会先解析代理的域名,它解析出的每一个地址都必须是公网地址,请求发往的是通过检查的那个
地址。如果你从没把 security.request_proxy 从默认的 deny 改掉,你不受影响。仓库里还新增了
一个 NOTICE 文件
(#766)。

升级方式

cd /opt/dtk && git pull
# 在 .env 里把 DTK_IMAGE_TAG 改成 5.1.2 —— 不带 v,见文末标签表
export COMPOSE_ENV_FILES=.env   # 不加这句,image: 的插值读不到根目录的 .env
docker compose -p dtk -f docker/compose.yml pull api worker downloader
docker compose -p dtk -f docker/compose.yml run --rm migrate
docker compose -p dtk -f docker/compose.yml up -d

或者重新运行安装脚本并选择「升级」:

bash install/install.sh --manage

数据不受影响。这个版本没有新增数据库迁移。如果你的 security.request_proxy 是 public
而又没法马上升级,请先把它改成 deny,升级之后再改回来。

新增

  • 新增 NOTICE 文件,写明项目的版权声明。 README 要求分发本项目的人「保留版权声明」,
    而仓库里之前根本没有版权声明。不用做任何事;如果你要分发本项目,请把 NOTICE 和
    LICENSE 放在一起。

变更

  • public 模式下,以域名给出的 ?proxy= 会被解析并钉住地址。 域名解析出的每一个地址都
    必须是公网地址,请求发往的是通过检查的那个地址而不是域名。https 代理保留域名,因为它的
    证书要按域名校验。随之新增两个拒绝原因:host_unresolvable 表示域名在五秒内没有解析出
    结果,authority_invalid 表示主机、端口或凭据里有 URL 不允许出现在那里的字符。如果你代理
    的用户名或密码里有这样的字符,请对它们做百分号编码。any 模式不变。
  • 用连字符拼出私有地址的主机名,在所有检查主机的地方都会被拒绝。 像
    app-10-0-0-1.example.com 这样的名字,现在在任务回调、通知 URL 和 URL 白名单里都算作私有
    地址,和 10.0.0.1 本身一直以来的待遇一样。拼出公网地址的名字不受影响。除非你的回调或通知
    URL 用了这种名字,否则不用做任何事。

安全

  • public 模式下通过 ?proxy= 的 SSRF(GHSA-q3h8-73xx-gwqx)。 影响 5.0.0 到 5.1.1,
    且仅限 security.request_proxy 为 public 的实例。任何持有读权限 API Key 的人都可以指定
    一个主机名解析到回环、私有网段或云元数据地址的代理,worker 就会去连接它。现在请求被接受之前
    会先解析主机名并检查每一个解析结果,往下传的是检查过的地址,不会被再次解析出不同的答案。
    请升级;升级之前请把 security.request_proxy 设为 deny。使用 deny(默认)或 any 的
    实例不受这项变更影响。感谢 @xiao1212998 的报告。

Tags / 标签

Git tag / Git 标签 v5.1.2
DTK_IMAGE_TAG — this release / 这个版本 5.1.2
DTK_IMAGE_TAG — this exact build / 钉死这次构建 sha-81cabb5c6f0f9420857b85076508c2be03259e9c

The Docker tag drops the v: docker/metadata-action strips it when publishing,
so :v5.1.2 was never pushed. One value names both published images. latest
and 5.1 move; pin sha- in production.

Docker 标签不带 v:docker/metadata-action 在发布时会把它剥掉,所以 :v5.1.2
这个标签从来没有被推送过。同一个值同时对应两个已发布镜像。latest 和 5.1 会移动,
生产环境请钉 sha-。

Images / 镜像: evil0ctal/douyin_tiktok_download_api · evil0ctal/douyin_tiktok_download_api-downloader

Full changelog / 完整提交记录: v5.1.1...v5.1.2

v5.1.1

Choose a tag to compare

@Evil0ctal Evil0ctal released this 23 Sep 06:36

What's new

Each platform now has its own refill thresholds, and either one can be switched
off.
Until now the identity pool used one "mint below / top up to" pair for
both platforms. A deployment that only serves Douyin, on a server that cannot
reach TikTok, kept trying to mint TikTok identities every minute, failed every
time, and raised pool_empty for a pool it never wanted
(#763).
Each platform can now follow the global pair, set its own, or be turned off.
This release also fixes a race in first-run setup.

Upgrading

cd /opt/dtk && git pull
# set DTK_IMAGE_TAG=5.1.1 in .env — no `v`, see the tag table at the bottom
export COMPOSE_ENV_FILES=.env   # without this, `image:` ignores the root .env
docker compose -p dtk -f docker/compose.yml pull api worker downloader
docker compose -p dtk -f docker/compose.yml run --rm migrate
docker compose -p dtk -f docker/compose.yml up -d

Or run the installer again and pick Upgrade:

bash install/install.sh --manage

Your data is untouched, and nothing changes until you change it: every platform
starts out following the global pool.min_size / pool.target_size, exactly
as before. No new migrations in this release.

Added

  • Per-platform refill thresholds. Four new settings:
    pool.douyin.min_size, pool.douyin.target_size, pool.tiktok.min_size and
    pool.tiktok.target_size. They default to -1, which means "follow the global
    value". Setting a platform's min_size to 0 turns its automatic minting off,
    along with its pool_empty and pool_below_min alerts. Imported identities and
    identities you mint by hand still work. Values below -1 are refused rather
    than read as "follow". If you only use one platform, set the other one's
    min_size to 0.
  • The refill card on the Identities page has a row per platform. Each row has
    its own Auto switch and its own Mint below / Top up to numbers. A
    number that is still following the global value is marked default, and
    Restore default drops a platform's own numbers in one click.

Changed

  • A mark of 0 now also silences the pool alerts. This applies to the global
    pool.min_size too: before, setting it to 0 stopped minting but an empty pool
    still raised pool_empty every hour. If you relied on that alert with the mark
    at 0, set the mark back to 1 or more.
  • GET /api/v1/admin/identities/pool reports each platform's own marks. Every
    platform row gains min_size, target_size, min_inherited,
    target_inherited and auto. The top-level min_size / target_size are
    still there and are the global pair, so existing clients keep working.
  • The pool step in dtk diagnose compares against the highest mark of any
    platform
    rather than the global pool.min_size. It still counts the whole
    pool, not each platform separately.

Fixed

  • The saved-folder endpoints added in 5.1.0 were missing from the API
    reference
    in documents/. They are documented now. Nothing to do.

Security

  • First-run setup could create two administrators from one token. The setup
    endpoint read the token, compared it, and only then deleted it, so two requests
    sent at the same moment with the correct token could both succeed. Using the
    token is now the same step as deleting it, and only one request can win. The
    exposure was small: the token only appears in the container log, so this let
    whoever already held it create a second admin, and never let anyone else in.
    A wrong guess still costs one of five attempts and never uses up the real
    token. Nothing to do; an instance that is already set up is not affected.

本次更新

每个平台都有了自己的补充阈值,也可以单独关掉。 之前身份池的「低于多少开始铸造 /
补到多少」是两个平台共用的一对。一个只用抖音、服务器又访问不到 TikTok 的部署,会每分钟
尝试铸造 TikTok 身份、每次都失败,还会为一个根本不需要的池子发 pool_empty 告警
(#763)。
现在每个平台可以沿用全局值、单独设置,或者关闭。这个版本还修复了首次初始化里的一个竞态问题。

升级方式

cd /opt/dtk && git pull
# 在 .env 里把 DTK_IMAGE_TAG 改成 5.1.1 —— 不带 v,见文末标签表
export COMPOSE_ENV_FILES=.env   # 不加这句,image: 的插值读不到根目录的 .env
docker compose -p dtk -f docker/compose.yml pull api worker downloader
docker compose -p dtk -f docker/compose.yml run --rm migrate
docker compose -p dtk -f docker/compose.yml up -d

或者重新运行安装脚本并选择「升级」:

bash install/install.sh --manage

数据不受影响,而且在你改动之前什么都不会变:每个平台一开始都沿用全局的
pool.min_size / pool.target_size,和以前完全一样。这个版本没有新增数据库迁移。

新增

  • 按平台的补充阈值。 新增四个设置:pool.douyin.min_size、
    pool.douyin.target_size、pool.tiktok.min_size 和 pool.tiktok.target_size。
    默认值是 -1,表示「沿用全局值」。把某个平台的 min_size 设为 0 会关闭它的自动
    铸造,同时不再发出它的 pool_empty 和 pool_below_min 告警。导入的身份和手动铸造的
    身份照常可用。小于 -1 的值会被拒绝,而不是被当成「沿用」。如果你只用一个平台,
    把另一个平台的 min_size 设成 0 即可。
  • 身份页的「自动补充」卡片改成每个平台一行。 每行有自己的自动开关和自己的
    低于 / 补到两个数字。仍在沿用全局值的数字旁边标着默认,点恢复默认
    可以一次清掉这个平台单独设置的数字。

变更

  • 下限为 0 时,身份池告警也一并关闭。 这对全局的 pool.min_size 同样适用:以前把它
    设成 0 会停止铸造,但池子空了仍然每小时发一次 pool_empty。如果你在下限为 0 的情况下
    依赖这条告警,请把下限改回 1 或以上。
  • GET /api/v1/admin/identities/pool 会返回每个平台自己的阈值。 每个平台行新增
    min_size、target_size、min_inherited、target_inherited 和 auto。顶层的
    min_size / target_size 仍然保留,表示全局那一对,已有的客户端不受影响。
  • dtk diagnose 的身份池一步改为和所有平台里最高的下限比较,而不是全局的
    pool.min_size。它统计的仍然是整个池子,没有按平台分别检查。

修复

  • 5.1.0 新增的收藏夹接口没有写进 documents/ 里的接口参考。 现在已经补上。
    不用做任何事。

安全

  • 首次初始化可能用同一个 token 创建出两个管理员。 初始化接口先读取 token、比较,
    之后才删除它,所以两个同时发出、都带着正确 token 的请求可以都成功。现在使用 token 和
    删除 token 是同一步,只有一个请求能胜出。影响范围很小:token 只出现在容器日志里,所以
    这只能让已经拿到 token 的人多建一个管理员,从来不会让别人进来。猜错 token 仍然只消耗
    五次机会中的一次,不会用掉真正的 token。不用做任何事;已经初始化过的实例不受影响。

Tags / 标签

Git tag / Git 标签 v5.1.1
DTK_IMAGE_TAG — this release / 这个版本 5.1.1
DTK_IMAGE_TAG — this exact build / 钉死这次构建 sha-d21b92ec28e481795f4c8530ee6dc0f7d40da69d

The Docker tag drops the v: docker/metadata-action strips it when publishing,
so :v5.1.1 was never pushed. One value names both published images. latest
and 5.1 move; pin sha- in production.

Docker 标签不带 v:docker/metadata-action 在发布时会把它剥掉,所以 :v5.1.1
这个标签从来没有被推送过。同一个值同时对应两个已发布镜像。latest 和 5.1 会移动,
生产环境请钉 sha-。

v5.1.0

Choose a tag to compare

@Evil0ctal Evil0ctal released this 15 Sep 03:10

What's new

Saved folders, on both platforms. Douyin and TikTok both let a user file
other people's posts into named folders, and both let each folder be public or
private individually. This release reads all of it: the folders, what is inside
one, who owns it, and whether it is public — a public folder needs no login at
all. TikTok reposts are wired too, and a TikTok collection link pasted into
/parse now resolves.

Upgrading

cd /opt/dtk && git pull
# set DTK_IMAGE_TAG=5.1.0 in .env — no `v`, see the tag table at the bottom
export COMPOSE_ENV_FILES=.env   # without this, `image:` ignores the root .env
docker compose -p dtk -f docker/compose.yml pull api worker downloader
docker compose -p dtk -f docker/compose.yml run --rm migrate
docker compose -p dtk -f docker/compose.yml up -d

Or run the installer again and pick Upgrade:

bash install/install.sh --manage

Your data is untouched: the named volumes survive a rebuild, so the identity
pool, the archive, the settings and the API keys stay where they are. No new
migrations in this release.

Added

  • GET /{platform}/user/collections — the bookmark folders. Each entry now
    reports whether it is public, plus the folder's owner. On TikTok you ask about
    an author and a guest identity sees that author's public folders. On Douyin the
    endpoint carries no user id at all and answers only about the session sending
    it, so pin identity to an imported identity there; passing sec_user_id on
    Douyin is refused rather than quietly ignored, because ignoring it would return
    your own folders under somebody else's name.
  • GET /{platform}/collection/posts — what is inside one folder. Both
    platforms. The request names the folder, not the account, so a folder its owner
    made public is readable with a guest identity and no import at all. A private
    one is refused rather than returned empty.
  • GET /{platform}/collection — one folder's own details, by id. TikTok only.
    Takes just the folder id, which is all a shared link carries, and is the only
    way to find out what an unknown collection id is: name, cover, item count,
    owner, and whether it is public.
  • GET /{platform}/user/bookmarks — every post an account saved. TikTok only,
    and only ever that account's own list, so it needs an imported identity.
  • GET /{platform}/user/reposts — posts an author reposted. TikTok only. A
    public profile tab, so a guest identity reads it. Each entry carries its
    original author rather than the account that reposted it.
  • TikTok collection links are recognized. Paste
    https://www.tiktok.com/@name/collection/Title-7685… into /parse and you get
    the folder back. Douyin's /collection/ links are unchanged and still resolve
    to an author's own series, which is a different thing wearing the same word.

Changed

  • Playground endpoint names read as a set. "Mix / playlist posts" did not
    distinguish an author's own series from a viewer's bookmark folders. The four
    are now named against each other: Author's own series, Bookmark folders,
    Saved posts (every folder), Saved posts (one folder). Endpoint names,
    parameters and URLs are unchanged — this is labels only.
  • Controls that a platform cannot accept are hidden in the Playground rather
    than submitted and refused, the same way controls your role cannot use already
    were.

Fixed

  • A page size TikTok refuses is now refused here. count above 35 on a
    TikTok list came back looking like an author with no posts — an ordinary
    absence, indistinguishable from the truth, after spending a pooled identity to
    get it. Values over the platform's real ceiling are rejected before anything
    leaves the process, naming the ceiling. Douyin's 50 is unchanged. If you were
    passing count=50 to a TikTok endpoint you now get a clear INVALID_PARAM
    instead of a silent empty page; lower it to 35.
  • An author's bookmark folders were documented as private. They are not:
    visibility is per folder, and a guest identity sees the public ones. The old
    wording said a guest gets an empty page, which would have stopped anyone from
    trying. Corrected in the endpoint docs and both locales.
  • Folder covers were dropped. A collection's cover arrived as null for
    every folder because the upstream container is camelCase and the parser read
    the snake_case spelling.

Security

  • Two test samples named a real account. The session-check fixtures carried a
    live numeric account id and web id copied from a capture. They are placeholders
    now, matching how every other fixture in the repo already redacted that field.
    Nothing was exploitable — a numeric user id is public — but it identified a
    person and did not need to.

本次更新

收藏夹,两个平台都支持了。 抖音和 TikTok 都允许把别人的作品收进命名的收藏夹,
也都允许逐个把收藏夹设为公开或私密。这个版本把这些全读出来了:收藏夹列表、某个
收藏夹里的内容、归属于谁、是否公开——公开的收藏夹完全不需要登录。TikTok 的转发
列表也一并接上了,收藏夹分享链接丢进 /parse 就能解析。

升级方式

cd /opt/dtk && git pull
# 在 .env 里把 DTK_IMAGE_TAG 改成 5.1.0 —— 不带 v,见文末标签表
export COMPOSE_ENV_FILES=.env   # 不加这句,image: 的插值读不到根目录的 .env
docker compose -p dtk -f docker/compose.yml pull api worker downloader
docker compose -p dtk -f docker/compose.yml run --rm migrate
docker compose -p dtk -f docker/compose.yml up -d

或者重新运行安装脚本并选择「升级」:

bash install/install.sh --manage

数据不受影响:命名卷不随容器重建而消失,身份池、归档、设置和 API Key 都在原地。
这个版本没有新增数据库迁移。

新增

  • GET /{platform}/user/collections —— 收藏夹列表。 每一条现在都会说明自己
    是否公开,以及归属于谁。在 TikTok 上你可以问某个作者,游客身份能看到该作者公开
    的收藏夹。在抖音上这个接口里根本没有用户 id,只回答发出请求的那个会话自己的收藏
    夹,所以必须把 identity 指向导入的身份;在抖音上传 sec_user_id 会被拒绝而不是
    被悄悄忽略——忽略它的话,返回的会是你自己的收藏夹,却挂在别人的名下。
  • GET /{platform}/collection/posts —— 某个收藏夹里的内容。 两个平台都支持。
    请求指名的是收藏夹而不是账号,所以作者设为公开的收藏夹,游客身份就能读,完全不用
    导入身份。私密的会被直接拒绝,而不是返回空页。
  • GET /{platform}/collection —— 按 id 查单个收藏夹的信息。 仅 TikTok。只要
    收藏夹 id——分享链接里也就只有这个——它是唯一能查出一个陌生收藏夹 id 究竟是什么
    的途径:名称、封面、作品数、归属、是否公开。
  • GET /{platform}/user/bookmarks —— 账号收藏的全部作品。 仅 TikTok,且只能读
    账号自己的,所以需要导入身份。
  • GET /{platform}/user/reposts —— 作者转发的作品。 仅 TikTok。这是公开的主页
    tab,游客身份即可读取。每一条携带的是原作者,而不是转发它的那个账号。
  • 能识别 TikTok 收藏夹链接了。 把
    https://www.tiktok.com/@name/collection/Title-7685… 丢进 /parse 就能拿到这个
    收藏夹。抖音的 /collection/ 链接行为不变,仍然解析为作者自建的合集——那是同一个
    词底下的另一样东西。

变更

  • Playground 的接口名现在是成组可读的。 原来的「合辑 / 播放列表作品」无法区分
    作者自建的合集和使用者自己的收藏夹。现在四个名字互相对照:作者自建合集、
    收藏夹列表、收藏的作品(跨全部收藏夹)、收藏的作品(单个收藏夹)。接口名、
    参数和 URL 都没有变——这次只改了标签。
  • 平台不接受的控件会在 Playground 里隐藏,而不是提交上去再被拒绝,这和原本
    「角色权限不够就隐藏」的做法是同一套。

修复

  • TikTok 不接受的分页大小,现在这边也会拒绝。 在 TikTok 的列表接口上把 count
    设到 35 以上,返回的结果看起来就像这个作者没有任何作品——一次普通的「没有内容」,
    和真相无法区分,而且是在消耗掉一个池内身份之后才拿到的。超过平台真实上限的值现在
    在请求离开进程之前就被拒绝,并且会明确告诉你上限是多少。抖音的 50 不受影响。如果
    你原本在 TikTok 接口上传 count=50,现在会收到明确的 INVALID_PARAM 而不是一个
    静默的空页,把它降到 35 即可。
  • 作者的收藏夹被写成了「私密」。 事实并非如此:公开与否是逐个收藏夹的属性,游客
    身份能看到公开的那些。原来的文案写着游客只会拿到空页,而这种说法会让人根本不去尝试。
    接口文档和中英两份文案都已更正。
  • 收藏夹封面丢失。 每个收藏夹的封面都返回 null,因为上游的容器字段是驼峰命名,
    而解析器读的是下划线命名。

安全

  • 两份测试样本里写着一个真实账号。 会话检查的样本携带了从抓包里复制来的真实数字
    账号 id 和 web id。现在已改成占位值,与仓库里其他所有样本对这个字段的处理方式一致。
    这不构成可被利用的问题——数字 id 本来就是公开的——但它指向了一个具体的人,而这毫无
    必要。

Tags / 标签

Git tag / Git 标签 v5.1.0
DTK_IMAGE_TAG — this release / 这个版本 5.1.0
DTK_IMAGE_TAG — this exact build / 钉死这次构建 sha-8a3feca26683778d8c6c885803cb0e11374f3d5e

The Docker tag drops the v: docker/metadata-action strips it when publishing,
so :v5.1.0 was never pushed. One value names both published images. latest
and 5.1 move; pin sha- in production.

Docker 标签不带 v:docker/metadata-action 在发布时会把它剥掉,所以 :v5.1.0
这个标签从来没有被推送过。同一个值同时对应两个已发布镜像。latest 和 5.1 会移动,
生产环境请钉 sha-。

v5.0.3

Choose a tag to compare

@Evil0ctal Evil0ctal released this 11 Sep 09:27

What's new

If you are on 5.0.2, upgrade. The TikTok fix it shipped worked for about a
minute per author and then silently stopped, which is worse than the bug it
replaced.

Upgrading

cd /opt/dtk && git pull
# set DTK_IMAGE_TAG=5.0.3 in .env — no `v`, see the tag table at the bottom
export COMPOSE_ENV_FILES=.env   # without this, `image:` ignores the root .env
docker compose -p dtk -f docker/compose.yml pull api worker downloader
docker compose -p dtk -f docker/compose.yml run --rm migrate
docker compose -p dtk -f docker/compose.yml up -d

Or run the installer again and pick Upgrade:

bash install/install.sh --manage

Your data is untouched: the named volumes survive a rebuild, so the identity
pool, the archive, the settings and the API keys stay where they are. No new
migrations in this release.

Fixed

  • 5.0.2's TikTok handle lookup only worked on a cold cache. Asking for a
    TikTok author's posts by profile link resolves the @handle to the secUid
    those endpoints need. That lookup read its answer out of a parse callback —
    and fetch answers a cache hit from storage without calling one. So the first
    request for an author succeeded and every request in the next fifteen minutes
    fell back to the same INVALID_PARAM the fix was meant to remove, with
    nothing in the log to say why. It reads the returned payload now, which is
    populated on both paths.
  • A session's 403 listed scopes that could never have refused it. A console
    session is bounded by its role, never by its scopes, so printing them was
    noise — and actively misleading on the demo account, whose scope list contains
    admin. The refusal read as "I hold admin and still cannot read this".
    have_scopes now appears only for a caller that scopes actually bind.

Changed

  • Two new worker log events, worker.author_handle.resolved (with cached) and
    worker.author_handle.no_id. The branch that gave up used to say nothing at
    all, which is why the bug above was invisible from the outside.

本次更新

如果你在用 5.0.2,请升级。 它带的那个 TikTok 修复,每个作者只能用大约一分钟就会
悄悄失效——比它想修的那个问题更糟。

升级方式

cd /opt/dtk && git pull
# 在 .env 里把 DTK_IMAGE_TAG 改成 5.0.3 —— 不带 v,见文末标签表
export COMPOSE_ENV_FILES=.env   # 不加这句,image: 的插值读不到根目录的 .env
docker compose -p dtk -f docker/compose.yml pull api worker downloader
docker compose -p dtk -f docker/compose.yml run --rm migrate
docker compose -p dtk -f docker/compose.yml up -d

或者再跑一次安装脚本,选「升级」:

bash install/install.zh.sh --manage

数据不受影响:命名卷不随容器重建而消失,身份池、归档、设置和 API Key 都在原地。
本次没有新的迁移脚本。

修复

  • 5.0.2 的 TikTok handle 解析只在缓存是冷的时候有效。 用主页链接取 TikTok 作者的
    作品时,会把 @handle 解析成那些接口需要的 secUid。而这个查询是从 parse 回调里
    取结果的——fetch 命中缓存时直接从存储返回,根本不调回调。于是对一个作者的第一次
    请求成功,之后十五分钟内的每一次都退回到它本该消除的那条 INVALID_PARAM,日志里还
    什么都没有。现在改成读返回的 payload,两条路径都有值。
  • 会话的 403 列出了根本不可能拒绝它的 scope。 控制台会话只受角色约束,从不受
    scope 约束,所以列出来纯属噪音——在演示账号上还会误导,因为它的 scope 列表里含 admin,
    读起来像「我有 admin 却读不了这个」。现在 have_scopes 只在 scope 真正起作用时出现。

变更

  • 新增两条 worker 日志事件:worker.author_handle.resolved(带 cached)和
    worker.author_handle.no_id。此前放弃的那个分支一句话都不打,这正是上面那个 bug
    从外面完全看不见的原因。

Tags / 标签

Git tag / Git 标签 v5.0.3
DTK_IMAGE_TAG — this release / 这个版本 5.0.3
DTK_IMAGE_TAG — this exact build / 钉死这次构建 sha-<the commit v5.0.3points at —git rev-list -n1 v5.0.3>

The Docker tag drops the v: docker/metadata-action strips it when publishing,
so :v5.0.3 was never pushed. One value names both published images. latest
and 5.0 move with every release — pin a row above instead.

镜像标签不带 v:发布时 docker/metadata-action 会把它剥掉,:v5.0.3 从来没有被
推送过。这一个值同时决定两个已发布镜像的标签。latest 和 5.0
会随每次发布移动,要钉死请用上面两行之一。

Images / 镜像: evil0ctal/douyin_tiktok_download_api · evil0ctal/douyin_tiktok_download_api-downloader

Full changelog / 完整提交记录: v5.0.2...v5.0.3

v5.0.2

Choose a tag to compare

@Evil0ctal Evil0ctal released this 11 Sep 08:15

What's new

Four fixes, all of them things you could hit on a normal afternoon. Three
came out of using the live demo rather than reading the code.

Upgrading

cd /opt/dtk && git pull
# set DTK_IMAGE_TAG=5.0.2 in .env — no `v`, see the tag table at the bottom
export COMPOSE_ENV_FILES=.env   # without this, `image:` ignores the root .env
docker compose -p dtk -f docker/compose.yml pull api worker downloader
docker compose -p dtk -f docker/compose.yml run --rm migrate
docker compose -p dtk -f docker/compose.yml up -d

Or run the installer again and pick Upgrade:

bash install/install.sh --manage

Your data is untouched: the named volumes survive a rebuild, so the identity
pool, the archive, the settings and the API keys stay where they are. No new
migrations in this release.

Added

  • Author.sec_uid. The id this project's own author endpoints key on. On
    Douyin it is the same value as uid; on TikTok the two differ, and that
    difference is the reason the field exists. If you read an author out of any
    response and mean to pass it back, read this field.

Fixed

  • A TikTok profile link could not reach that author's posts. Two things
    were wrong at once. The parser threw TikTok's secUid away, so nothing the
    API returned could be fed back into its own author endpoints — you got a
    numeric id and an @handle, and author_posts, author_likes, followers
    and following refuse both. And a profile link carries only the handle, which
    those endpoints refused by name rather than resolving. The handle is now
    looked up for you, behind author_profile's cache, so a second page of the
    same author costs nothing. Douyin was never affected: its profile links
    contain the sec_user_id literally.
  • An author download could only ever go to Douyin. On the Downloads page the
    platform menu was disabled for the whole of author mode, on the reasoning that
    an author is named by an id rather than by digits. But a bare author id is the
    same MS4wLjABAAAA… shape on both platforms and says nothing about which one
    it came from, so the code read that menu — while it sat greyed out at its
    default. TikTok was unreachable and the one control that could have said
    otherwise would not open.

Changed

  • A 403 now says what it wants and what you sent. Eleven places raise
    FORBIDDEN_SCOPE and they disagreed: most named what the endpoint requires,
    one named only your own role, and the key was spelled required in some and
    required_role in others. details is now one shape everywhere —
    required_scopes or required_roles, plus have_role, have_scopes and
    via. Read via first: it says whether this caller is judged by its scopes
    (api_key) or by its role (session), and an administrator's key is still
    bounded by its own scopes.

    If you match on these keys, they changed. required is now
    required_scopes, and required_role is now required_roles and holds a
    list.

  • The message on that 403 stopped claiming an API key you may not have. One
    catalogue entry served all eleven refusals and read "This API key lacks the
    scope required by this endpoint" — wrong for a console session, which has no
    API key, and wrong for the role gates, which are not scopes. It is neutral and
    true now, and points at details for the part that varies.


本次更新

四个修复,都是正常用一下午就可能撞上的。其中三个是在演示站上用出来的,不是读代码读出来的。

升级方式

cd /opt/dtk && git pull
# 在 .env 里把 DTK_IMAGE_TAG 改成 5.0.2 —— 不带 v,见文末标签表
export COMPOSE_ENV_FILES=.env   # 不加这句,image: 的插值读不到根目录的 .env
docker compose -p dtk -f docker/compose.yml pull api worker downloader
docker compose -p dtk -f docker/compose.yml run --rm migrate
docker compose -p dtk -f docker/compose.yml up -d

或者再跑一次安装脚本,选「升级」:

bash install/install.zh.sh --manage

数据不受影响:命名卷不随容器重建而消失,身份池、归档、设置和 API Key 都在原地。
本次没有新的迁移脚本。

新增

  • Author.sec_uid。 本项目自己的作者接口认的那个 id。抖音上它和 uid 同值;
    TikTok 上两者不同,而这个差异正是它存在的理由。从任何响应里读出作者、还打算拿
    回去再请求的话,读这个字段。

修复

  • TikTok 的主页链接取不到该作者的作品。 两处同时出错。解析器把 TikTok 的
    secUid 丢掉了,于是这个 API 返回的任何东西都喂不回它自己的作者接口 —— 你拿到
    的是一个数字 id 和一个 @handle,而 author_posts、author_likes、followers、
    following 两个都不收。同时主页链接里只有 handle,这些接口按名字拒绝它,而不是
    去解析。现在 handle 会替你查一次,走 author_profile 自己的缓存,所以同一作者的
    第二页不花钱。抖音从来不受影响:它的主页链接字面就带着 sec_user_id。
  • 作者作品的下载只能下到抖音。 下载页的平台下拉在整个「作者」模式下都是禁用的,
    理由是「作者是用 id 指定的,不是数字」。但裸的作者 id 在两个平台上是同一个
    MS4wLjABAAAA… 形状,说不出自己来自哪边,所以代码其实要读这个菜单 —— 而它正灰着,
    停在默认值上。TikTok 根本到不了,唯一能改它的控件点不开。

变更

  • 403 现在会说清「要什么」和「你给了什么」。 有 11 处会抛 FORBIDDEN_SCOPE,
    而它们各说各的:多数说接口需要什么,有一处只说你自己是什么角色,键名还有
    required 和 required_role 两种拼法。现在 details 全站一个形状 ——
    required_scopes 或 required_roles,外加 have_role、have_scopes 和 via。
    先看 via:它说明这个调用方是按 scope 判定(api_key)还是按角色判定
    (session),而管理员的 key 同样受它自己的 scope 约束。

    如果你的代码匹配这些键,它们变了。 required 改成 required_scopes,
    required_role 改成 required_roles 且值是列表。

  • 403 的文案不再断言你有一把可能并不存在的 API Key。 一条文案服务全部 11 种拒绝,
    写的是「该 API Key 缺少调用此接口所需的权限范围」—— 对用会话 cookie 的控制台用户
    是错的(他根本没有 API Key),对角色关卡也是错的(那不是 scope)。现在它中立且真实,
    会变的部分交给 details。


Tags / 标签

Git tag / Git 标签 v5.0.2
DTK_IMAGE_TAG — this release / 这个版本 5.0.2
DTK_IMAGE_TAG — this exact build / 钉死这次构建 sha-<the commit v5.0.2points at —git rev-list -n1 v5.0.2>

The Docker tag drops the v: docker/metadata-action strips it when publishing,
so :v5.0.2 was never pushed. One value names both published images. latest
and 5.0 move with every release — pin a row above instead.

镜像标签不带 v:发布时 docker/metadata-action 会把它剥掉,:v5.0.2 从来没有被
推送过。这一个值同时决定两个已发布镜像的标签。latest 和 5.0
会随每次发布移动,要钉死请用上面两行之一。

Images / 镜像: evil0ctal/douyin_tiktok_download_api · evil0ctal/douyin_tiktok_download_api-downloader

Full changelog / 完整提交记录: v5.0.1...v5.0.2

v5.0.1

Choose a tag to compare

@Evil0ctal Evil0ctal released this 11 Sep 04:14

What's new

A maintenance release. One thing in it is worth upgrading for on its own: on a
public instance with demo mode on, the console was showing demo visitors the
whole sidebar and answering 403 on most of it.

Upgrading

cd /opt/dtk && git pull
# set DTK_IMAGE_TAG=5.0.1 in .env — no `v`, see the tag table at the bottom
export COMPOSE_ENV_FILES=.env   # without this, `image:` ignores the root .env
docker compose -p dtk -f docker/compose.yml pull api worker downloader
docker compose -p dtk -f docker/compose.yml run --rm migrate
docker compose -p dtk -f docker/compose.yml up -d

Or run the new installer again and pick Upgrade:

bash install/install.sh --manage

Your data is untouched: the named volumes survive a rebuild, so the identity
pool, the archive, the settings and the API keys stay where they are. No new
migrations in this release.

Added

  • A guided installer, in both languages. install/install.sh and
    install/install.zh.sh work out your distribution, offer the right way to
    install Docker, scale the container limits to the machine, generate .env
    with real secrets, and bring the stack up. Run it a second time and it becomes
    the operations menu instead: status, upgrade, passwords, an extra
    administrator, backups, any runtime setting, disk cleanup, stop or uninstall.
  • A live demo. https://douyin.wtf is open to everyone — sign in with the
    prefilled demo account and use the console and the API. 30 requests per 10
    seconds, then a 10-second cooldown.
  • Community files. CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md,
    issue forms in both languages, a bilingual pull-request template, CODEOWNERS
    and Dependabot. GitHub scored the repository's community profile at 42% before
    this; it is 100% now.
  • Two easter eggs, which you are invited to find rather than told about.

Changed

  • The English README is now the default. README.md is English and the
    Chinese one moved to README.zh-CN.md. Links to the old path still resolve on
    GitHub.
  • Dependencies. eslint 10, vite 8, i18next 26, mcp 2.2, redis 8.1,
    sqlalchemy 2.0.52, uvicorn 0.52, Go 1.27 for the downloader image, and eleven
    GitHub Actions. The console's React Compiler lint rules came with eslint 10 and
    found two real defects, both fixed below.
  • Documentation. Both installation pages now open with the choice between
    Docker and a by-hand install, carry a contents list, and have a section on
    mirrors for installing from mainland China.

Fixed

  • A demo visitor saw the whole sidebar. /auth/me sat behind a guard whose
    floor was viewer, so a demo session was refused its own principal and the
    console could not tell it was a demo. It rendered every page — Identities,
    Proxies, Users, Settings, Backup — each of which then answered 403. Twenty nav
    items where there should have been eleven.
  • The mobile navigation drawer covered the whole screen. A later rule with
    equal specificity overrode the drawer's width, so the menu hid the page behind
    it and left a column of dead space beside labels a few words long. It is 300px
    or 80vw now, whichever is smaller.
  • About and the sponsor were missing on phones. Not cut off — absent from the
    markup. The only route to either was typing the URL.
  • The Tools page ran 62px off the side of a phone. Five tabs in a row that
    does not wrap; the fifth was unreachable and dragged the page into a
    horizontal scroll.
  • A memo in the API keys page never memoised. It read the clock during render
    and listed the result as a dependency, so every render produced a new value.
  • A temporal dead zone in the downloads page, where two mutations called a
    helper two hundred lines before it was declared.

Security

  • explain and identity are no longer offered to a session that cannot use
    them.
    Both hand back the identity's own cookie jar and the signed upstream
    URL, so both are operator-only and always were — the playground was letting a
    demo visitor tick the box and be answered 403. The API's refusal is unchanged;
    what changed is that the control is now disabled, says which role it needs, and
    keeps its value out of the request.

本次更新

维护版本。其中一项单独就值得升级:在开了演示模式的公开实例上,控制台会把整个侧边栏
展示给演示访客,而其中大部分点进去都是 403。

升级方式

cd /opt/dtk && git pull
# 在 .env 里把 DTK_IMAGE_TAG 改成 5.0.1 —— 不带 v,见文末标签表
export COMPOSE_ENV_FILES=.env   # 不加这句,image: 的插值读不到根目录的 .env
docker compose -p dtk -f docker/compose.yml pull api worker downloader
docker compose -p dtk -f docker/compose.yml run --rm migrate
docker compose -p dtk -f docker/compose.yml up -d

或者再跑一次新的安装脚本,选「升级」:

bash install/install.zh.sh --manage

数据不受影响:命名卷不随容器重建而消失,身份池、归档、设置和 API Key 都在原地。
本次没有新的迁移脚本。

新增

  • 引导式安装脚本,中英双语。 install/install.sh 和 install/install.zh.sh
    会识别发行版、给出对应的 Docker 安装方式、按机器大小调好容器上限、生成带真实密钥的
    .env,然后把整套跑起来。再跑第二次它就变成运维菜单:状态、升级、改口令、加管理员、
    备份、改任意运行时设置、清理磁盘、停止或卸载。
  • 在线演示站。 https://douyin.wtf 对所有人开放 —— 用登录页替你填好的演示账号
    登录,即可使用控制台和接口。每 10 秒 30 次请求,超了冷却 10 秒。
  • 社区文件。 CONTRIBUTING.md、SECURITY.md、CODE_OF_CONDUCT.md、中英双语的
    issue 表单、双语 PR 模板、CODEOWNERS 和 Dependabot。此前 GitHub 给这个仓库的社区
    档案完整度打 42 分,现在是 100。
  • 两个彩蛋,请自己找,不剧透。

变更

  • 英文自述文档成为默认。 README.md 现在是英文版,中文版移到
    README.zh-CN.md。指向旧路径的链接在 GitHub 上仍然可用。
  • 依赖升级。 eslint 10、vite 8、i18next 26、mcp 2.2、redis 8.1、
    sqlalchemy 2.0.52、uvicorn 0.52、下载器镜像的 Go 1.27,以及十一个 GitHub Action。
    eslint 10 带来的 React Compiler 规则查出了两个真缺陷,都在下面修复里。
  • 文档。 两份安装文档现在开头就让你在 Docker 和手动部署之间做选择,配了全文目录,
    并新增了中国大陆用户的换源章节。

修复

  • 演示访客看到的是整个侧边栏。 /auth/me 挂在一个下限为 viewer 的守卫上,
    于是演示会话读不到自己的身份,控制台也就不知道自己是个演示。它把每个页面都渲染了
    出来 —— 身份池、代理、用户、设置、备份 —— 而这些点进去全是 403。本该 11 项的导航
    显示了 20 项。
  • 移动端导航抽屉占满整屏。 一条权重相同但位置更靠后的规则盖掉了抽屉宽度,于是菜单
    把它背后的页面完全遮住,而只有几个字的菜单项右侧留着一大片空白。现在是 300px 和
    80vw 取小。
  • 手机上看不到「关于」和赞助商。 不是被截断,是根本没渲染出来,唯一的入口是手敲网址。
  • 基础工具页在手机上横向溢出 62px。 五个标签放在一个不换行的行里,第五个点不到,
    还把整页拖进了横向滚动。
  • API Key 页有个 memo 从来没生效过。 它在渲染期读时钟并把结果列为依赖,于是每次
    渲染都得到一个新值。
  • 下载页存在暂时性死区 —— 两个操作调用了一个在两百行之后才声明的辅助函数。

安全

  • explain 和 identity 不再提供给用不了它们的会话。 这两个参数都会交回身份自己的
    cookie 和带签名的上游 URL,所以它们一直是 operator 专属 —— 而调试台此前允许演示访客
    勾上它,然后收到 403。接口侧的拒绝没有变;变的是这个控件现在是禁用的、会说明需要什么
    角色,并且不会把值放进请求。

Tags / 标签

Git tag / Git 标签 v5.0.1
DTK_IMAGE_TAG — this release / 这个版本 5.0.1
DTK_IMAGE_TAG — this exact build / 钉死这次构建 sha-1b9478a97f89a606a7147fc1acb4461b06dc3af6

The Docker tag drops the v: docker/metadata-action strips it when publishing,
so :v5.0.1 was never pushed. One value names both published images. latest
and 5.0 move with every release — pin a row above instead.

镜像标签不带 v:发布时 docker/metadata-action 会把它剥掉,:v5.0.1 从来没有被
推送过。这一个值同时决定两个已发布镜像的标签。latest 和 5.0
会随每次发布移动,要钉死请用上面两行之一。

Images / 镜像: evil0ctal/douyin_tiktok_download_api · evil0ctal/douyin_tiktok_download_api-downloader

Full changelog / 完整提交记录: v5.0.0...v5.0.1

v5.0.0

Choose a tag to compare

@Evil0ctal Evil0ctal released this 10 Sep 14:33

What's new

v5 is a rewrite. It started from an empty branch and shares no code with v4.

v4's real problem was never a shortage of features — it was that the API would
die quietly and nobody would know. A cookie expires, a signature algorithm
changes, an endpoint gets rate-limited, and you find out when someone files an
issue. v5 puts "you can see it" and "it heals itself" ahead of features: it
mints its own guest identities with a headless browser, spreads requests across
them by health, trips a breaker per endpoint, and writes down what happened to
every single request.

Upgrading

There is no upgrade path from v4, and that is deliberate. v5 is a different
schema, a different configuration model and a different container layout. Treat
it as a new install:

git clone https://github.com/Evil0ctal/Douyin_TikTok_Download_API.git
cd Douyin_TikTok_Download_API
# write .env — see .env.example, or the Quick start in the README
COMPOSE_ENV_FILES=.env docker compose -p dtk -f docker/compose.yml up -d

v4 keeps running. Its code is on the v4 branch
and its image is still on Docker Hub as evil0ctal/douyin_tiktok_download_api:V4.1.2.
If you are staying on it, pin that tag rather than latest — latest is v5 now.

Breaking changes

  • Everything. Different endpoints, different response shape, different
    configuration. config.yaml is gone; runtime settings live in the database
    and are edited from the console. Nothing that talked to a v4 instance will
    talk to a v5 one without changes.
  • Bilibili is not carried over. v4 supported it, v5 does not yet. It shares
    neither the signing nor the identity machinery with Douyin and TikTok, so the
    rewrite left it out.
  • Douyin follower and following lists are not exposed. They are only served
    to a signed-in session, so registering endpoints that always return an empty
    page would have been dishonest. Import your own cookies and the rest sees more.

Added

  • A self-maintaining identity pool. A headless browser mints guest
    identities — cookies, fingerprint, proxy — and tops the pool up when usable
    ones run low. No more copying cookies out of a browser into a config file.
  • A scheduler that spreads the load. Health tiers, quantised LRU rotation,
    one in-flight lock per identity, a token bucket per (identity, endpoint) and a
    circuit breaker per endpoint. No single identity carries the traffic.
  • A web console. Identity pool, scheduler, playground, library, downloads,
    logs, diagnostics — in English and Chinese, both written by hand.
  • Asynchronous by default. Endpoints answer 202 with a task id; add
    ?wait= to make a call synchronous, or use a callback_url webhook.
  • MCP. Point an agent at /mcp and it gets the same tools, the same
    identity pool and the same rate limits as the REST API.
  • A content archive. Everything parsed is stored, so a post deleted upstream
    is still here. Media downloads land on your own disk.
  • Signing in pure Python. a_bogus, X-Bogus, X-Gnarly and X-Dynosaur, with a
    browser fallback for when a platform changes one.
  • Demo mode. Publish a shared read-only account and API key so anyone can
    try your instance. Turn it on in Settings; it is read-only, its requests are
    not written to the request log or the archive, and turning it off ends every
    demo session immediately.
  • API keys with scopes, per-key rate limits, and four roles.

Security

  • Every stored credential — cookie jars, proxy URLs — is encrypted with the
    instance key. A database dump on its own does not carry them.
  • Nothing ships a default password or key. The process refuses to start without
    DTK_SECRET_KEY.
  • The published demo credentials are the single deliberate exception to
    "credentials are never readable", and they are readable only while demo mode
    is on.

本次更新

v5 是一次重写,从空分支起步,和 v4 不共享任何代码。

v4 最大的问题从来不是功能少,而是接口会悄悄死掉,而你不知道。Cookie 过期、
签名算法变更、某个接口被风控,通常都要等到有人来提 issue 才发现。v5 把「看得见」
和「能自愈」排在功能前面:用无头浏览器自己铸造游客身份,按健康度把请求摊开,
每个接口独立熔断,每一次请求都留下一条结构化记录。

升级方式

从 v4 没有升级路径,这是刻意的。 v5 是另一套表结构、另一套配置模型、
另一套容器编排。请当作全新安装:

git clone https://github.com/Evil0ctal/Douyin_TikTok_Download_API.git
cd Douyin_TikTok_Download_API
# 写 .env —— 见 .env.example,或自述文档的「快速开始」
COMPOSE_ENV_FILES=.env docker compose -p dtk -f docker/compose.yml up -d

v4 继续可用。代码保留在 v4 分支,
镜像也还在,是 evil0ctal/douyin_tiktok_download_api:V4.1.2。
要留在 v4 上就固定这个版本号 tag,别用 latest —— latest 现在是 v5 了。

不兼容变更

  • 全部。 接口不同、响应结构不同、配置方式不同。config.yaml 没有了,
    运行时设置存在数据库里,在控制台改。任何对接过 v4 的东西都需要改造才能对接 v5。
  • 哔哩哔哩没有移植过来。 v4 支持,v5 暂时没有。它和抖音 / TikTok 不共用
    签名和身份体系,重写时先放下了。
  • 抖音的粉丝和关注列表没有开放。 这两个接口只对已登录会话开放,
    注册一个永远返回空页的接口不诚实。导入你自己的登录 Cookie 之后,其余接口
    能看到的内容会更多。

新增

  • 会自己维护的身份池。 无头浏览器铸造游客身份(cookie + 指纹 + 代理),
    可用数不够时自己补。不用再从浏览器里抠 cookie 粘进配置文件。
  • 把请求摊开的调度器。 健康度分层、量化 LRU 轮换、每身份独占锁、
    每(身份,接口)令牌桶、接口级熔断。单个身份不会承担全部流量。
  • Web 控制台。 身份池、调度器、调试台、资料库、下载、日志、诊断,
    中英双语,两种语言都是手写的。
  • 默认异步。 接口返回 202 和一个任务 ID;加 ?wait= 可退回同步,
    也可以用 callback_url 回调。
  • MCP。 把 AI 代理指向 /mcp,它拿到的工具、身份池和限流和 REST 接口是同一套。
  • 内容归档。 解析过的内容自动入库,平台删了这里还在。媒体下载存到你自己的磁盘。
  • 纯 Python 的签名实现。 a_bogus、X-Bogus、X-Gnarly、X-Dynosaur,
    平台改算法时还有浏览器兜底。
  • 演示模式。 对外公开一个共用的只读账号和 API Key,任何人都能试用你的实例。
    在设置里开启;它是只读的,请求不写入请求日志和归档库,关闭后所有演示会话立即失效。
  • 带作用域的 API Key、每把 Key 独立限流,以及四种角色。

安全

  • 所有存下来的凭据——cookie jar、代理地址——都用实例主密钥加密。
    单独拿到一份数据库导出是解不开的。
  • 仓库里不带任何默认密码或密钥。没有 DTK_SECRET_KEY 进程直接拒绝启动。
  • 对外公开的演示凭据是「凭据永远不可读取」这条规则唯一一处刻意的例外,
    而且只在演示模式开着时可读。

Tags / 标签

Git tag / Git 标签 v5.0.0
DTK_IMAGE_TAG — this release / 这个版本 5.0.0
DTK_IMAGE_TAG — this exact build / 钉死这次构建 sha-33a02ba71d3689ae9a05ccb90d6e6a9c19942067

The Docker tag drops the v: docker/metadata-action strips it when publishing,
so :v5.0.0 was never pushed. One value names both published images. latest
and 5.0 move with every release — pin a row above instead.

镜像标签不带 v:发布时 docker/metadata-action 会把它剥掉,:v5.0.0 从来没有被
推送过。这一个值同时决定两个已发布镜像的标签。latest 和 5.0
会随每次发布移动,要钉死请用上面两行之一。

Images / 镜像: evil0ctal/douyin_tiktok_download_api · evil0ctal/douyin_tiktok_download_api-downloader

Full changelog / 完整提交记录: https://github.com/Evil0ctal/Douyin_TikTok_Download_API/commits/v5.0.0

V4.1.2

Choose a tag to compare

@Evil0ctal Evil0ctal released this 16 Mar 08:27

🔊 V4.1.2

中文

修复问题

  • 修复TikTok Web个人主页作品接口 - /api/tiktok/web/fetch_user_post
    • Fix: 重新定义了BaseRequestModel的参数

🔊 V4.1.2

English

Fixed issues

  • Fixed the TikTok Web personal homepage work interface - /api/tiktok/web/fetch_user_post
  • Fix: Redefine the parameters of BaseRequestModel

V4.1.1

Choose a tag to compare

@Evil0ctal Evil0ctal released this 01 Mar 22:17

🔊 V4.1.1

中文

优化性能

  • 优化视频下载的性能
    • Fix: 修改视频下载方法为流式下载-> #569 #570

🔊 V4.1.1

English

Optimize performance

  • Optimize video download performance
  • Fix: Change video download method to streaming download-> #569 #570

Acknowledgements/致谢

@hadwinfu - Issue/PR --> #569 #570